Merge branch 'beta' into new-team

This commit is contained in:
Madmadness65 2024-09-15 01:01:44 -05:00 committed by GitHub
commit 2792ba83c9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
40 changed files with 5449 additions and 4796 deletions

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 694 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 B

View File

@ -105,6 +105,7 @@ import HeldModifierConfig from "#app/interfaces/held-modifier-config";
import { ExpPhase } from "#app/phases/exp-phase";
import { ShowPartyExpBarPhase } from "#app/phases/show-party-exp-bar-phase";
import { MysteryEncounterMode } from "#enums/mystery-encounter-mode";
import { ExpGainsSpeed } from "./enums/exp-gains-speed";
export const bypassLogin = import.meta.env.VITE_BYPASS_LOGIN === "1";
@ -180,7 +181,7 @@ export default class BattleScene extends SceneBase {
public experimentalSprites: boolean = false;
public musicPreference: integer = 0;
public moveAnimations: boolean = true;
public expGainsSpeed: integer = 0;
public expGainsSpeed: ExpGainsSpeed = ExpGainsSpeed.DEFAULT;
public skipSeenDialogues: boolean = false;
/**
* Determines if the egg hatching animation should be skipped

View File

@ -1086,7 +1086,7 @@ export class PostDefendMoveDisableAbAttr extends PostDefendAbAttr {
applyPostDefend(pokemon: Pokemon, passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean {
if (attacker.getTag(BattlerTagType.DISABLED) === null) {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance) && !attacker.isMax()) {
if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && (this.chance === -1 || pokemon.randSeedInt(100) < this.chance)) {
if (simulated) {
return true;
}

View File

@ -524,10 +524,6 @@ export class FlinchedTag extends BattlerTag {
applyAbAttrs(FlinchEffectAbAttr, pokemon, null);
}
canAdd(pokemon: Pokemon): boolean {
return !pokemon.isMax();
}
/**
* Cancels the Pokemon's next Move on the turn this tag is applied
* @param pokemon The {@linkcode Pokemon} with this tag
@ -878,10 +874,6 @@ export class EncoreTag extends BattlerTag {
}
canAdd(pokemon: Pokemon): boolean {
if (pokemon.isMax()) {
return false;
}
const lastMoves = pokemon.getLastXMoves(1);
if (!lastMoves.length) {
return false;
@ -1060,19 +1052,11 @@ export class MinimizeTag extends BattlerTag {
super(BattlerTagType.MINIMIZED, BattlerTagLapseType.TURN_END, 1, Moves.MINIMIZE);
}
canAdd(pokemon: Pokemon): boolean {
return !pokemon.isMax();
}
onAdd(pokemon: Pokemon): void {
super.onAdd(pokemon);
}
lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
//If a pokemon dynamaxes they lose minimized status
if (pokemon.isMax()) {
return false;
}
return lapseType !== BattlerTagLapseType.CUSTOM || super.lapse(pokemon, lapseType);
}

View File

@ -3977,7 +3977,7 @@ export class StatusCategoryOnAllyAttr extends VariableMoveCategoryAttr {
export class ShellSideArmCategoryAttr extends VariableMoveCategoryAttr {
apply(user: Pokemon, target: Pokemon, move: Move, args: any[]): boolean {
const category = (args[0] as Utils.IntegerHolder);
const category = (args[0] as Utils.NumberHolder);
const atkRatio = user.getEffectiveStat(Stat.ATK, target, move) / target.getEffectiveStat(Stat.DEF, user, move);
const specialRatio = user.getEffectiveStat(Stat.SPATK, target, move) / target.getEffectiveStat(Stat.SPDEF, user, move);
@ -5248,7 +5248,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr {
return false;
}
if (!this.user && move.category === MoveCategory.STATUS && (target.hasAbilityWithAttr(ForceSwitchOutImmunityAbAttr) || target.isMax())) {
if (!this.user && move.category === MoveCategory.STATUS && (target.hasAbilityWithAttr(ForceSwitchOutImmunityAbAttr))) {
return false;
}
@ -6458,8 +6458,6 @@ const failOnGravityCondition: MoveConditionFunc = (user, target, move) => !user.
const failOnBossCondition: MoveConditionFunc = (user, target, move) => !target.isBossImmune();
const failOnMaxCondition: MoveConditionFunc = (user, target, move) => !target.isMax();
const failIfSingleBattle: MoveConditionFunc = (user, target, move) => user.scene.currentBattle.double;
const failIfDampCondition: MoveConditionFunc = (user, target, move) => {
@ -6855,8 +6853,7 @@ export function initMoves() {
new StatusMove(Moves.DISABLE, Type.NORMAL, 100, 20, -1, 0, 1)
.attr(AddBattlerTagAttr, BattlerTagType.DISABLED, false, true)
.condition((user, target, move) => target.getMoveHistory().reverse().find(m => m.move !== Moves.NONE && m.move !== Moves.STRUGGLE && !m.virtual) !== undefined)
.ignoresSubstitute()
.condition(failOnMaxCondition),
.ignoresSubstitute(),
new AttackMove(Moves.ACID, Type.POISON, MoveCategory.SPECIAL, 40, 100, 30, 10, 0, 1)
.attr(StatStageChangeAttr, [ Stat.SPDEF ], -1)
.target(MoveTarget.ALL_NEAR_ENEMIES),
@ -6894,8 +6891,7 @@ export function initMoves() {
.attr(RecoilAttr)
.recklessMove(),
new AttackMove(Moves.LOW_KICK, Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, 0, 1)
.attr(WeightPowerAttr)
.condition(failOnMaxCondition),
.attr(WeightPowerAttr),
new AttackMove(Moves.COUNTER, Type.FIGHTING, MoveCategory.PHYSICAL, -1, 100, 20, -1, -5, 1)
.attr(CounterDamageAttr, (move: Move) => move.category === MoveCategory.PHYSICAL, 2)
.target(MoveTarget.ATTACKER),
@ -8006,8 +8002,7 @@ export function initMoves() {
.target(MoveTarget.ENEMY_SIDE),
new AttackMove(Moves.GRASS_KNOT, Type.GRASS, MoveCategory.SPECIAL, -1, 100, 20, -1, 0, 4)
.attr(WeightPowerAttr)
.makesContact()
.condition(failOnMaxCondition),
.makesContact(),
new AttackMove(Moves.CHATTER, Type.FLYING, MoveCategory.SPECIAL, 65, 100, 20, 100, 0, 4)
.attr(ConfuseAttr)
.soundBased(),
@ -8109,8 +8104,7 @@ export function initMoves() {
new AttackMove(Moves.HEAVY_SLAM, Type.STEEL, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5)
.attr(MinimizeAccuracyAttr)
.attr(CompareWeightPowerAttr)
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
.condition(failOnMaxCondition),
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true),
new AttackMove(Moves.SYNCHRONOISE, Type.PSYCHIC, MoveCategory.SPECIAL, 120, 100, 10, -1, 0, 5)
.target(MoveTarget.ALL_NEAR_OTHERS)
.condition(unknownTypeCondition)
@ -8262,8 +8256,7 @@ export function initMoves() {
new AttackMove(Moves.HEAT_CRASH, Type.FIRE, MoveCategory.PHYSICAL, -1, 100, 10, -1, 0, 5)
.attr(MinimizeAccuracyAttr)
.attr(CompareWeightPowerAttr)
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true)
.condition(failOnMaxCondition),
.attr(HitsTagAttr, BattlerTagType.MINIMIZED, true),
new AttackMove(Moves.LEAF_TORNADO, Type.GRASS, MoveCategory.SPECIAL, 65, 90, 10, 50, 0, 5)
.attr(StatStageChangeAttr, [ Stat.ACC ], -1),
new AttackMove(Moves.STEAMROLLER, Type.BUG, MoveCategory.PHYSICAL, 65, 100, 20, 30, 0, 5)

View File

@ -10,6 +10,7 @@ import { applyDamageToPokemon } from "#app/data/mystery-encounters/utils/encount
import { MysteryEncounterTier } from "#enums/mystery-encounter-tier";
import { MysteryEncounterOptionMode } from "#enums/mystery-encounter-option-mode";
import { CLASSIC_MODE_MYSTERY_ENCOUNTER_WAVES } from "#app/game-mode";
import {PokemonMove} from "#app/field/pokemon";
const OPTION_1_REQUIRED_MOVE = Moves.SURF;
const OPTION_2_REQUIRED_MOVE = Moves.FLY;
@ -44,8 +45,8 @@ export const LostAtSeaEncounter: MysteryEncounter = MysteryEncounterBuilder.with
const encounter = scene.currentBattle.mysteryEncounter!;
encounter.setDialogueToken("damagePercentage", String(DAMAGE_PERCENTAGE));
encounter.setDialogueToken("option1RequiredMove", Moves[OPTION_1_REQUIRED_MOVE]);
encounter.setDialogueToken("option2RequiredMove", Moves[OPTION_2_REQUIRED_MOVE]);
encounter.setDialogueToken("option1RequiredMove", new PokemonMove(OPTION_1_REQUIRED_MOVE).getName());
encounter.setDialogueToken("option2RequiredMove", new PokemonMove(OPTION_2_REQUIRED_MOVE).getName());
return true;
})

View File

@ -87,6 +87,7 @@ export const MysteriousChallengersEncounter: MysteryEncounter =
);
const e4Template = trainerPartyTemplates.ELITE_FOUR;
const brutalConfig = trainerConfigs[brutalTrainerType].clone();
brutalConfig.title = trainerConfigs[brutalTrainerType].title;
brutalConfig.setPartyTemplates(e4Template);
// @ts-ignore
brutalConfig.partyTemplateFunc = null; // Overrides gym leader party template func

View File

@ -17,7 +17,8 @@ export enum SpeciesWildEvolutionDelay {
SHORT,
MEDIUM,
LONG,
VERY_LONG
VERY_LONG,
NEVER
}
export enum EvolutionItem {
@ -39,19 +40,34 @@ export enum EvolutionItem {
TART_APPLE,
STRAWBERRY_SWEET,
UNREMARKABLE_TEACUP,
CHIPPED_POT = 51,
BLACK_AUGURITE,
UPGRADE,
DUBIOUS_DISC,
DRAGON_SCALE,
PRISM_SCALE,
RAZOR_CLAW,
RAZOR_FANG,
REAPER_CLOTH,
ELECTIRIZER,
MAGMARIZER,
PROTECTOR,
SACHET,
WHIPPED_DREAM,
SYRUPY_APPLE,
CHIPPED_POT,
GALARICA_CUFF,
GALARICA_WREATH,
PEAT_BLOCK,
AUSPICIOUS_ARMOR,
MALICIOUS_ARMOR,
MASTERPIECE_TEACUP,
SUN_FLUTE,
MOON_FLUTE,
BLACK_AUGURITE = 51,
PEAT_BLOCK,
METAL_ALLOY,
SCROLL_OF_DARKNESS,
SCROLL_OF_WATERS,
SYRUPY_APPLE
LEADERS_CREST
}
/**
@ -222,7 +238,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
],
[Species.SLOWPOKE]: [
new SpeciesEvolution(Species.SLOWBRO, 37, null, null),
new SpeciesEvolution(Species.SLOWKING, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* King's Rock */), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.SLOWKING, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.MAGNEMITE]: [
new SpeciesEvolution(Species.MAGNETON, 30, null, null)
@ -249,8 +265,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ELECTRODE, 30, null, null)
],
[Species.CUBONE]: [
new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.MEDIUM),
new SpeciesEvolution(Species.MAROWAK, 28, null, null)
new SpeciesEvolution(Species.ALOLA_MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.MAROWAK, 28, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.TYROGUE]: [
new SpeciesEvolution(Species.HITMONLEE, 20, null, new SpeciesEvolutionCondition(p => p.stats[Stat.ATK] > p.stats[Stat.DEF])),
@ -258,8 +274,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.HITMONTOP, 20, null, new SpeciesEvolutionCondition(p => p.stats[Stat.ATK] === p.stats[Stat.DEF]))
],
[Species.KOFFING]: [
new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.METROPOLIS || p.scene.arena.biomeType === Biome.SLUM), SpeciesWildEvolutionDelay.MEDIUM),
new SpeciesEvolution(Species.WEEZING, 35, null, null)
new SpeciesEvolution(Species.GALAR_WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.WEEZING, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.RHYHORN]: [
new SpeciesEvolution(Species.RHYDON, 42, null, null)
@ -304,7 +320,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.QUILAVA, 14, null, null)
],
[Species.QUILAVA]: [
new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.HISUI_TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.TYPHLOSION, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.TOTODILE]: [
@ -652,7 +668,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DEWOTT, 17, null, null)
],
[Species.DEWOTT]: [
new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.HISUI_SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.SAMUROTT, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.PATRAT]: [
@ -800,10 +816,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.BISHARP, 52, null, null)
],
[Species.BISHARP]: [
new SpeciesEvolution(Species.KINGAMBIT, 64, null, null)
new SpeciesEvolution(Species.KINGAMBIT, 1, EvolutionItem.LEADERS_CREST, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.RUFFLET]: [
new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.HISUI_BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.BRAVIARY, 54, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.VULLABY]: [
@ -883,20 +899,20 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CLAWITZER, 37, null, null)
],
[Species.TYRUNT]: [
new SpeciesEvolution(Species.TYRANTRUM, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
new SpeciesEvolution(Species.TYRANTRUM, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.AMAURA]: [
new SpeciesEvolution(Species.AURORUS, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
new SpeciesEvolution(Species.AURORUS, 39, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
],
[Species.GOOMY]: [
new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.HISUI_SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.SLIGGOO, 40, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.SLIGGOO]: [
new SpeciesEvolution(Species.GOODRA, 50, null, new SpeciesEvolutionCondition(p => [ WeatherType.RAIN, WeatherType.FOG, WeatherType.HEAVY_RAIN ].indexOf(p.scene.arena.weather?.weatherType || WeatherType.NONE) > -1), SpeciesWildEvolutionDelay.LONG)
],
[Species.BERGMITE]: [
new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.HISUI_AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.AVALUGG, 37, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.NOIBAT]: [
@ -906,7 +922,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DARTRIX, 17, null, null)
],
[Species.DARTRIX]: [
new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.HISUI_DECIDUEYE, 36, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)),
new SpeciesEvolution(Species.DECIDUEYE, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.LITTEN]: [
@ -928,7 +944,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.TOUCANNON, 28, null, null)
],
[Species.YUNGOOS]: [
new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
new SpeciesEvolution(Species.GUMSHOOS, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.GRUBBIN]: [
new SpeciesEvolution(Species.CHARJABUG, 20, null, null)
@ -946,7 +962,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ARAQUANID, 22, null, null)
],
[Species.FOMANTIS]: [
new SpeciesEvolution(Species.LURANTIS, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
new SpeciesEvolution(Species.LURANTIS, 34, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY))
],
[Species.MORELULL]: [
new SpeciesEvolution(Species.SHIINOTIC, 24, null, null)
@ -973,17 +989,17 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.KOMMO_O, 45, null, null)
],
[Species.COSMOG]: [
new SpeciesEvolution(Species.COSMOEM, 43, null, null)
new SpeciesEvolution(Species.COSMOEM, 23, null, null)
],
[Species.COSMOEM]: [
new SpeciesEvolution(Species.SOLGALEO, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)),
new SpeciesEvolution(Species.LUNALA, 53, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
new SpeciesEvolution(Species.SOLGALEO, 53, EvolutionItem.SUN_FLUTE, null, SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesEvolution(Species.LUNALA, 53, EvolutionItem.MOON_FLUTE, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.MELTAN]: [
new SpeciesEvolution(Species.MELMETAL, 48, null, null)
],
[Species.ALOLA_RATTATA]: [
new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
new SpeciesEvolution(Species.ALOLA_RATICATE, 20, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
],
[Species.ALOLA_DIGLETT]: [
new SpeciesEvolution(Species.ALOLA_DUGTRIO, 26, null, null)
@ -1090,7 +1106,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GALAR_RAPIDASH, 40, null, null)
],
[Species.GALAR_FARFETCHD]: [
new SpeciesEvolution(Species.SIRFETCHD, 30, null, null)
new SpeciesEvolution(Species.SIRFETCHD, 30, null, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.GALAR_SLOWPOKE]: [
new SpeciesEvolution(Species.GALAR_SLOWBRO, 1, EvolutionItem.GALARICA_CUFF, null, SpeciesWildEvolutionDelay.VERY_LONG),
@ -1106,7 +1122,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GALAR_LINOONE, 20, null, null)
],
[Species.GALAR_LINOONE]: [
new SpeciesEvolution(Species.OBSTAGOON, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
new SpeciesEvolution(Species.OBSTAGOON, 35, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
],
[Species.GALAR_YAMASK]: [
new SpeciesEvolution(Species.RUNERIGUS, 34, null, null)
@ -1214,7 +1230,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.GLIMMORA, 35, null, null)
],
[Species.GREAVARD]: [
new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
new SpeciesEvolution(Species.HOUNDSTONE, 30, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT))
],
[Species.FRIGIBAX]: [
new SpeciesEvolution(Species.ARCTIBAX, 35, null, null)
@ -1226,8 +1242,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CLODSIRE, 20, null, null)
],
[Species.PIKACHU]: [
new SpeciesFormEvolution(Species.ALOLA_RAICHU, "", "", 1, EvolutionItem.THUNDER_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALOLA_RAICHU, "partner", "", 1, EvolutionItem.THUNDER_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALOLA_RAICHU, "", "", 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ALOLA_RAICHU, "partner", "", 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.RAICHU, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.RAICHU, "partner", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG)
],
@ -1255,7 +1271,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
],
[Species.POLIWHIRL]: [
new SpeciesEvolution(Species.POLIWRATH, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.POLITOED, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* King's Rock */), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.POLITOED, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.WEEPINBELL]: [
new SpeciesEvolution(Species.VICTREEBEL, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1267,7 +1283,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CLOYSTER, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.EXEGGCUTE]: [
new SpeciesEvolution(Species.ALOLA_EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.biomeType === Biome.ISLAND || p.scene.arena.biomeType === Biome.BEACH), SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.ALOLA_EXEGGUTOR, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.EXEGGUTOR, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.TANGELA]: [
@ -1280,12 +1296,12 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.STARMIE, 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.EEVEE]: [
new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(70, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.SYLVEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.SYLVEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => !!p.getMoveset().find(m => m?.getMove().type === Type.FAIRY)), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.ESPEON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.UMBREON, "partner", "", 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.VAPOREON, "", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.VAPOREON, "partner", "", 1, EvolutionItem.WATER_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesFormEvolution(Species.JOLTEON, "", "", 1, EvolutionItem.THUNDER_STONE, null, SpeciesWildEvolutionDelay.LONG),
@ -1329,10 +1345,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.DUDUNSPARCE, 32, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.HYPER_DRILL).length > 0), SpeciesWildEvolutionDelay.LONG)
],
[Species.GLIGAR]: [
new SpeciesEvolution(Species.GLISCOR, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.GLISCOR, 1, EvolutionItem.RAZOR_FANG, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor fang at night*/), SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.SNEASEL]: [
new SpeciesEvolution(Species.WEAVILE, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.WEAVILE, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT /* Razor claw at night*/), SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.URSARING]: [
new SpeciesEvolution(Species.URSALUNA, 1, EvolutionItem.PEAT_BLOCK, null, SpeciesWildEvolutionDelay.VERY_LONG) //Ursaring does not evolve into Bloodmoon Ursaluna
@ -1362,8 +1378,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.SUDOWOODO, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM)
],
[Species.MIME_JR]: [
new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.biomeType === Biome.ICE_CAVE || p.scene.arena.biomeType === Biome.SNOWY_FOREST)), SpeciesWildEvolutionDelay.MEDIUM),
new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0), SpeciesWildEvolutionDelay.MEDIUM)
new SpeciesEvolution(Species.GALAR_MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT)), SpeciesWildEvolutionDelay.MEDIUM),
new SpeciesEvolution(Species.MR_MIME, 1, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.MIMIC).length > 0 && (p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY)), SpeciesWildEvolutionDelay.MEDIUM)
],
[Species.PANSAGE]: [
new SpeciesEvolution(Species.SIMISAGE, 1, EvolutionItem.LEAF_STONE, null, SpeciesWildEvolutionDelay.LONG)
@ -1381,8 +1397,8 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.WHIMSICOTT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.PETILIL]: [
new SpeciesEvolution(Species.HISUI_LILLIGANT, 1, EvolutionItem.SUN_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesEvolution(Species.LILLIGANT, 1, EvolutionItem.SUN_STONE, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.HISUI_LILLIGANT, 1, EvolutionItem.SHINY_STONE, null, SpeciesWildEvolutionDelay.LONG),
new SpeciesEvolution(Species.LILLIGANT, 1, EvolutionItem.SUN_STONE, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.BASCULIN]: [
new SpeciesFormEvolution(Species.BASCULEGION, "white-striped", "female", 40, null, new SpeciesEvolutionCondition(p => p.gender === Gender.FEMALE, p => p.gender = Gender.FEMALE), SpeciesWildEvolutionDelay.VERY_LONG),
@ -1435,7 +1451,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.APPLETUN, 1, EvolutionItem.SWEET_APPLE, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.CLOBBOPUS]: [
new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TAUNT).length > 0), SpeciesWildEvolutionDelay.MEDIUM)
new SpeciesEvolution(Species.GRAPPLOCT, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.TAUNT).length > 0)/*Once Taunt is implemented, change evo level to 1 and delay to LONG*/)
],
[Species.SINISTEA]: [
new SpeciesFormEvolution(Species.POLTEAGEIST, "phony", "phony", 1, EvolutionItem.CRACKED_POT, null, SpeciesWildEvolutionDelay.LONG),
@ -1472,7 +1488,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.OVERQWIL, 28, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.BARB_BARRAGE).length > 0), SpeciesWildEvolutionDelay.LONG)
],
[Species.HISUI_SNEASEL]: [
new SpeciesEvolution(Species.SNEASLER, 1, null, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.SNEASLER, 1, EvolutionItem.RAZOR_CLAW, new SpeciesEvolutionCondition(p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY /* Razor claw at day*/), SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.CHARCADET]: [
new SpeciesEvolution(Species.ARMAROUGE, 1, EvolutionItem.AUSPICIOUS_ARMOR, null, SpeciesWildEvolutionDelay.LONG),
@ -1512,10 +1528,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.RHYDON]: [
new SpeciesEvolution(Species.RHYPERIOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Protector */), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.RHYPERIOR, 1, EvolutionItem.PROTECTOR, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.SEADRA]: [
new SpeciesEvolution(Species.KINGDRA, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Dragon scale*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.KINGDRA, 1, EvolutionItem.DRAGON_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.SCYTHER]: [
new SpeciesEvolution(Species.SCIZOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(
@ -1524,22 +1540,22 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.KLEAVOR, 1, EvolutionItem.BLACK_AUGURITE, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.ELECTABUZZ]: [
new SpeciesEvolution(Species.ELECTIVIRE, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Electirizer*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.ELECTIVIRE, 1, EvolutionItem.ELECTIRIZER, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.MAGMAR]: [
new SpeciesEvolution(Species.MAGMORTAR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Magmarizer*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.MAGMORTAR, 1, EvolutionItem.MAGMARIZER, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.PORYGON]: [
new SpeciesEvolution(Species.PORYGON2, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /*Upgrade*/), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.PORYGON2, 1, EvolutionItem.UPGRADE, null, SpeciesWildEvolutionDelay.LONG)
],
[Species.PORYGON2]: [
new SpeciesEvolution(Species.PORYGON_Z, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Dubious disc*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.PORYGON_Z, 1, EvolutionItem.DUBIOUS_DISC, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.FEEBAS]: [
new SpeciesEvolution(Species.MILOTIC, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Prism scale*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.MILOTIC, 1, EvolutionItem.PRISM_SCALE, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.DUSCLOPS]: [
new SpeciesEvolution(Species.DUSKNOIR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /* Reaper cloth*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.DUSKNOIR, 1, EvolutionItem.REAPER_CLOTH, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.CLAMPERL]: [
new SpeciesEvolution(Species.HUNTAIL, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => p.gender === Gender.MALE, p => p.gender = Gender.MALE /* Deep Sea Tooth */), SpeciesWildEvolutionDelay.VERY_LONG),
@ -1558,10 +1574,10 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ACCELGOR, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => !!p.scene.gameData.dexData[Species.KARRABLAST].caughtAttr), SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.SPRITZEE]: [
new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /*Sachet*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.AROMATISSE, 1, EvolutionItem.SACHET, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.SWIRLIX]: [
new SpeciesEvolution(Species.SLURPUFF, 1, EvolutionItem.LINKING_CORD, new SpeciesEvolutionCondition(p => true /*Whipped Dream*/), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.SLURPUFF, 1, EvolutionItem.WHIPPED_DREAM, null, SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.PHANTUMP]: [
new SpeciesEvolution(Species.TREVENANT, 1, EvolutionItem.LINKING_CORD, null, SpeciesWildEvolutionDelay.VERY_LONG)
@ -1576,7 +1592,7 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.ANNIHILAPE, 35, null, new SpeciesEvolutionCondition(p => p.moveset.filter(m => m?.moveId === Moves.RAGE_FIST).length > 0), SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.GOLBAT]: [
new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesEvolution(Species.CROBAT, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.VERY_LONG)
],
[Species.CHANSEY]: [
new SpeciesEvolution(Species.BLISSEY, 1, null, new SpeciesFriendshipEvolutionCondition(200), SpeciesWildEvolutionDelay.LONG)
@ -1610,29 +1626,29 @@ export const pokemonEvolutions: PokemonEvolutions = {
new SpeciesEvolution(Species.CHANSEY, 1, null, new SpeciesFriendshipEvolutionCondition(160), SpeciesWildEvolutionDelay.SHORT)
],
[Species.MUNCHLAX]: [
new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.SNORLAX, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
],
[Species.RIOLU]: [
new SpeciesEvolution(Species.LUCARIO, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.LUCARIO, 1, null, new SpeciesFriendshipEvolutionCondition(120, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DAWN || p.scene.arena.getTimeOfDay() === TimeOfDay.DAY), SpeciesWildEvolutionDelay.LONG)
],
[Species.WOOBAT]: [
new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.MEDIUM)
new SpeciesEvolution(Species.SWOOBAT, 1, null, new SpeciesFriendshipEvolutionCondition(90), SpeciesWildEvolutionDelay.MEDIUM)
],
[Species.SWADLOON]: [
new SpeciesEvolution(Species.LEAVANNY, 1, null, new SpeciesFriendshipEvolutionCondition(110), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.LEAVANNY, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
],
[Species.TYPE_NULL]: [
new SpeciesEvolution(Species.SILVALLY, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.SILVALLY, 1, null, new SpeciesFriendshipEvolutionCondition(100), SpeciesWildEvolutionDelay.LONG)
],
[Species.ALOLA_MEOWTH]: [
new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.LONG)
new SpeciesEvolution(Species.ALOLA_PERSIAN, 1, null, new SpeciesFriendshipEvolutionCondition(120), SpeciesWildEvolutionDelay.LONG)
],
[Species.SNOM]: [
new SpeciesEvolution(Species.FROSMOTH, 1, null, new SpeciesFriendshipEvolutionCondition(90, p => p.scene.arena.getTimeOfDay() === TimeOfDay.DUSK || p.scene.arena.getTimeOfDay() === TimeOfDay.NIGHT), SpeciesWildEvolutionDelay.MEDIUM)
],
[Species.GIMMIGHOUL]: [
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesFriendshipEvolutionCondition(70), SpeciesWildEvolutionDelay.VERY_LONG)
new SpeciesFormEvolution(Species.GHOLDENGO, "chest", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG),
new SpeciesFormEvolution(Species.GHOLDENGO, "roaming", "", 1, null, new SpeciesEvolutionCondition( p => p.evoCounter > 9 ), SpeciesWildEvolutionDelay.VERY_LONG)
]
};

View File

@ -1609,6 +1609,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 12, Moves.DRAGON_BREATH ],
[ 16, Moves.CURSE ],
[ 20, Moves.ROCK_SLIDE ],
[ 22, Moves.GYRO_BALL ], //Custom, from USUM
[ 24, Moves.SCREECH ],
[ 28, Moves.SAND_TOMB ],
[ 32, Moves.STEALTH_ROCK ],
@ -2121,7 +2122,7 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 20, Moves.DOUBLE_HIT ],
[ 24, Moves.SLASH ],
[ 28, Moves.FOCUS_ENERGY ],
[ 30, Moves.STEEL_WING ],
[ 30, Moves.STEEL_WING ], //Custom
[ 32, Moves.AGILITY ],
[ 36, Moves.AIR_SLASH ],
[ 40, Moves.X_SCISSOR ],
@ -7549,14 +7550,15 @@ export const pokemonSpeciesLevelMoves: PokemonSpeciesLevelMoves = {
[ 1, Moves.POUND ],
[ 1, Moves.COPYCAT ],
[ 1, Moves.BARRIER ],
[ 1, Moves.TICKLE ], //USUM
[ 4, Moves.BATON_PASS ],
[ 8, Moves.ENCORE ],
[ 12, Moves.CONFUSION ],
[ 16, Moves.ROLE_PLAY ],
[ 16, Moves.MIMIC ], //Custom, swapped with Role Play to be closer to USUM
[ 20, Moves.PROTECT ],
[ 24, Moves.RECYCLE ],
[ 28, Moves.PSYBEAM ],
[ 32, Moves.MIMIC ],
[ 32, Moves.ROLE_PLAY ], //Custom, swapped with Mimic
[ 36, Moves.LIGHT_SCREEN ],
[ 36, Moves.REFLECT ],
[ 36, Moves.SAFEGUARD ],

View File

@ -944,7 +944,7 @@ export function initSpecies() {
new PokemonSpecies(Species.VENUSAUR, 1, false, false, false, "Seed Pokémon", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, GrowthRate.MEDIUM_SLOW, 87.5, true, true,
new PokemonForm("Normal", "", Type.GRASS, Type.POISON, 2, 100, Abilities.OVERGROW, Abilities.NONE, Abilities.CHLOROPHYLL, 525, 80, 82, 83, 100, 100, 80, 45, 50, 263, true, null, true),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GRASS, Type.POISON, 2.4, 155.5, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 625, 80, 100, 123, 122, 120, 80, 45, 50, 263, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 100, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.POISON, 24, 999.9, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, Abilities.CHLOROPHYLL, 625, 120, 82, 98, 130, 115, 80, 45, 50, 263, true),
),
new PokemonSpecies(Species.CHARMANDER, 1, false, false, false, "Lizard Pokémon", Type.FIRE, null, 0.6, 8.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 309, 39, 52, 43, 60, 50, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.CHARMELEON, 1, false, false, false, "Flame Pokémon", Type.FIRE, null, 1.1, 19, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 405, 58, 64, 58, 80, 65, 80, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false),
@ -952,20 +952,20 @@ export function initSpecies() {
new PokemonForm("Normal", "", Type.FIRE, Type.FLYING, 1.7, 90.5, Abilities.BLAZE, Abilities.NONE, Abilities.SOLAR_POWER, 534, 78, 84, 78, 109, 85, 100, 45, 50, 267, false, null, true),
new PokemonForm("Mega X", SpeciesFormKey.MEGA_X, Type.FIRE, Type.DRAGON, 1.7, 110.5, Abilities.TOUGH_CLAWS, Abilities.NONE, Abilities.TOUGH_CLAWS, 634, 78, 130, 111, 130, 85, 100, 45, 50, 267),
new PokemonForm("Mega Y", SpeciesFormKey.MEGA_Y, Type.FIRE, Type.FLYING, 1.7, 100.5, Abilities.DROUGHT, Abilities.NONE, Abilities.DROUGHT, 634, 78, 104, 78, 159, 115, 100, 45, 50, 267),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 90.5, Abilities.SOLAR_POWER, Abilities.SOLAR_POWER, Abilities.SOLAR_POWER, 634, 118, 84, 93, 139, 110, 100, 45, 50, 267),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.FLYING, 28, 999.9, Abilities.BERSERK, Abilities.BERSERK, Abilities.BERSERK, 634, 118, 84, 93, 139, 110, 100, 45, 50, 267),
),
new PokemonSpecies(Species.SQUIRTLE, 1, false, false, false, "Tiny Turtle Pokémon", Type.WATER, null, 0.5, 9, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 314, 44, 48, 65, 50, 64, 43, 45, 50, 63, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.WARTORTLE, 1, false, false, false, "Turtle Pokémon", Type.WATER, null, 1, 22.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 405, 59, 63, 80, 65, 80, 58, 45, 50, 142, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.BLASTOISE, 1, false, false, false, "Shellfish Pokémon", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
new PokemonForm("Normal", "", Type.WATER, null, 1.6, 85.5, Abilities.TORRENT, Abilities.NONE, Abilities.RAIN_DISH, 530, 79, 83, 100, 85, 105, 78, 45, 50, 265, false, null, true),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.WATER, null, 1.6, 101.1, Abilities.MEGA_LAUNCHER, Abilities.NONE, Abilities.MEGA_LAUNCHER, 630, 79, 103, 120, 135, 115, 78, 45, 50, 265),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 85.5, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 630, 119, 83, 130, 115, 115, 68, 45, 50, 265),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.STEEL, 25, 999.9, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 630, 119, 83, 130, 115, 115, 68, 45, 50, 265),
),
new PokemonSpecies(Species.CATERPIE, 1, false, false, false, "Worm Pokémon", Type.BUG, null, 0.3, 2.9, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 45, 30, 35, 20, 20, 45, 255, 50, 39, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.METAPOD, 1, false, false, false, "Cocoon Pokémon", Type.BUG, null, 0.7, 9.9, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 50, 20, 55, 25, 25, 30, 120, 50, 72, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.BUTTERFREE, 1, false, false, false, "Butterfly Pokémon", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, GrowthRate.MEDIUM_FAST, 50, true, true,
new PokemonForm("Normal", "", Type.BUG, Type.FLYING, 1.1, 32, Abilities.COMPOUND_EYES, Abilities.NONE, Abilities.TINTED_LENS, 395, 60, 45, 50, 90, 80, 70, 45, 50, 198, true, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 32, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.FLYING, 17, 999.9, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, Abilities.COMPOUND_EYES, 495, 85, 35, 80, 120, 90, 85, 45, 50, 198, true),
),
new PokemonSpecies(Species.WEEDLE, 1, false, false, false, "Hairy Bug Pokémon", Type.BUG, Type.POISON, 0.3, 3.2, Abilities.SHIELD_DUST, Abilities.NONE, Abilities.RUN_AWAY, 195, 40, 35, 30, 20, 20, 50, 255, 70, 39, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.KAKUNA, 1, false, false, false, "Cocoon Pokémon", Type.BUG, Type.POISON, 0.6, 10, Abilities.SHED_SKIN, Abilities.NONE, Abilities.SHED_SKIN, 205, 45, 25, 50, 25, 25, 35, 120, 70, 72, GrowthRate.MEDIUM_FAST, 50, false),
@ -994,7 +994,7 @@ export function initSpecies() {
new PokemonForm("Cute Cosplay", "cute-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
new PokemonForm("Smart Cosplay", "smart-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
new PokemonForm("Tough Cosplay", "tough-cosplay", Type.ELECTRIC, null, 0.4, 6, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 430, 45, 80, 50, 75, 60, 120, 190, 50, 112, true, null, true), //Custom
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 6, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, null, 21, 999.9, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 530, 125, 95, 60, 90, 70, 90, 190, 50, 112), //+100 BST from Partner Form
),
new PokemonSpecies(Species.RAICHU, 1, false, false, false, "Mouse Pokémon", Type.ELECTRIC, null, 0.8, 30, Abilities.STATIC, Abilities.NONE, Abilities.LIGHTNING_ROD, 485, 60, 90, 55, 90, 80, 110, 75, 50, 243, GrowthRate.MEDIUM_FAST, 50, true),
new PokemonSpecies(Species.SANDSHREW, 1, false, false, false, "Mouse Pokémon", Type.GROUND, null, 0.6, 12, Abilities.SAND_VEIL, Abilities.NONE, Abilities.SAND_RUSH, 300, 50, 75, 85, 20, 30, 40, 255, 50, 60, GrowthRate.MEDIUM_FAST, 50, false),
@ -1024,7 +1024,7 @@ export function initSpecies() {
new PokemonSpecies(Species.DUGTRIO, 1, false, false, false, "Mole Pokémon", Type.GROUND, null, 0.7, 33.3, Abilities.SAND_VEIL, Abilities.ARENA_TRAP, Abilities.SAND_FORCE, 425, 35, 100, 50, 50, 70, 120, 50, 50, 149, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.MEOWTH, 1, false, false, false, "Scratch Cat Pokémon", Type.NORMAL, null, 0.4, 4.2, Abilities.PICKUP, Abilities.TECHNICIAN, Abilities.UNNERVE, 290, 40, 45, 35, 40, 40, 90, 255, 50, 58, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.NORMAL, null, 0.4, 4.2, Abilities.PICKUP, Abilities.TECHNICIAN, Abilities.UNNERVE, 290, 40, 45, 35, 40, 40, 90, 255, 50, 58, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 33, 4.2, Abilities.TECHNICIAN, Abilities.TECHNICIAN, Abilities.TECHNICIAN, 540, 115, 110, 65, 65, 70, 115, 255, 50, 58), //+100 BST from Persian
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 33, 999.9, Abilities.TECHNICIAN, Abilities.TECHNICIAN, Abilities.TECHNICIAN, 540, 115, 110, 65, 65, 70, 115, 255, 50, 58), //+100 BST from Persian
),
new PokemonSpecies(Species.PERSIAN, 1, false, false, false, "Classy Cat Pokémon", Type.NORMAL, null, 1, 32, Abilities.LIMBER, Abilities.TECHNICIAN, Abilities.UNNERVE, 440, 65, 70, 60, 65, 65, 115, 90, 50, 154, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.PSYDUCK, 1, false, false, false, "Duck Pokémon", Type.WATER, null, 0.8, 19.6, Abilities.DAMP, Abilities.CLOUD_NINE, Abilities.SWIFT_SWIM, 320, 50, 52, 48, 65, 50, 55, 190, 50, 64, GrowthRate.MEDIUM_FAST, 50, false),
@ -1046,7 +1046,7 @@ export function initSpecies() {
new PokemonSpecies(Species.MACHOKE, 1, false, false, false, "Superpower Pokémon", Type.FIGHTING, null, 1.5, 70.5, Abilities.GUTS, Abilities.NO_GUARD, Abilities.STEADFAST, 405, 80, 100, 70, 50, 60, 45, 90, 50, 142, GrowthRate.MEDIUM_SLOW, 75, false),
new PokemonSpecies(Species.MACHAMP, 1, false, false, false, "Superpower Pokémon", Type.FIGHTING, null, 1.6, 130, Abilities.GUTS, Abilities.NO_GUARD, Abilities.STEADFAST, 505, 90, 130, 80, 65, 85, 55, 45, 50, 253, GrowthRate.MEDIUM_SLOW, 75, false, true,
new PokemonForm("Normal", "", Type.FIGHTING, null, 1.6, 130, Abilities.GUTS, Abilities.NO_GUARD, Abilities.STEADFAST, 505, 90, 130, 80, 65, 85, 55, 45, 50, 253, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIGHTING, null, 25, 130, Abilities.GUTS, Abilities.GUTS, Abilities.GUTS, 605, 115, 170, 95, 65, 95, 65, 45, 50, 253),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIGHTING, null, 25, 999.9, Abilities.GUTS, Abilities.GUTS, Abilities.GUTS, 605, 115, 170, 95, 65, 95, 65, 45, 50, 253),
),
new PokemonSpecies(Species.BELLSPROUT, 1, false, false, false, "Flower Pokémon", Type.GRASS, Type.POISON, 0.7, 4, Abilities.CHLOROPHYLL, Abilities.NONE, Abilities.GLUTTONY, 300, 50, 75, 35, 70, 30, 40, 255, 70, 60, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(Species.WEEPINBELL, 1, false, false, false, "Flycatcher Pokémon", Type.GRASS, Type.POISON, 1, 6.4, Abilities.CHLOROPHYLL, Abilities.NONE, Abilities.GLUTTONY, 390, 65, 90, 50, 85, 45, 55, 120, 70, 137, GrowthRate.MEDIUM_SLOW, 50, false),
@ -1079,7 +1079,7 @@ export function initSpecies() {
new PokemonSpecies(Species.GENGAR, 1, false, false, false, "Shadow Pokémon", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, GrowthRate.MEDIUM_SLOW, 50, false, true,
new PokemonForm("Normal", "", Type.GHOST, Type.POISON, 1.5, 40.5, Abilities.CURSED_BODY, Abilities.NONE, Abilities.NONE, 500, 60, 65, 60, 130, 75, 110, 45, 50, 250, false, null, true),
new PokemonForm("Mega", SpeciesFormKey.MEGA, Type.GHOST, Type.POISON, 1.4, 40.5, Abilities.SHADOW_TAG, Abilities.NONE, Abilities.NONE, 600, 60, 65, 80, 170, 95, 130, 45, 50, 250),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 40.5, Abilities.CURSED_BODY, Abilities.CURSED_BODY, Abilities.CURSED_BODY, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GHOST, Type.POISON, 20, 999.9, Abilities.CURSED_BODY, Abilities.CURSED_BODY, Abilities.CURSED_BODY, 600, 140, 65, 70, 140, 85, 100, 45, 50, 250),
),
new PokemonSpecies(Species.ONIX, 1, false, false, false, "Rock Snake Pokémon", Type.ROCK, Type.GROUND, 8.8, 210, Abilities.ROCK_HEAD, Abilities.STURDY, Abilities.WEAK_ARMOR, 385, 35, 45, 160, 30, 45, 70, 45, 50, 77, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.DROWZEE, 1, false, false, false, "Hypnosis Pokémon", Type.PSYCHIC, null, 1, 32.4, Abilities.INSOMNIA, Abilities.FOREWARN, Abilities.INNER_FOCUS, 328, 60, 48, 45, 43, 90, 42, 190, 70, 66, GrowthRate.MEDIUM_FAST, 50, false),
@ -1087,7 +1087,7 @@ export function initSpecies() {
new PokemonSpecies(Species.KRABBY, 1, false, false, false, "River Crab Pokémon", Type.WATER, null, 0.4, 6.5, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 325, 30, 105, 90, 25, 25, 50, 225, 50, 65, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.KINGLER, 1, false, false, false, "Pincer Pokémon", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.WATER, null, 1.3, 60, Abilities.HYPER_CUTTER, Abilities.SHELL_ARMOR, Abilities.SHEER_FORCE, 475, 55, 130, 115, 50, 50, 75, 60, 50, 166, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 60, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 80, 70, 60, 50, 166),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 19, 999.9, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, Abilities.TOUGH_CLAWS, 575, 90, 155, 140, 50, 80, 70, 60, 50, 166),
),
new PokemonSpecies(Species.VOLTORB, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 0.5, 10.4, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 330, 40, 30, 50, 55, 55, 100, 190, 70, 66, GrowthRate.MEDIUM_FAST, null, false),
new PokemonSpecies(Species.ELECTRODE, 1, false, false, false, "Ball Pokémon", Type.ELECTRIC, null, 1.2, 66.6, Abilities.SOUNDPROOF, Abilities.STATIC, Abilities.AFTERMATH, 490, 60, 50, 70, 80, 80, 150, 60, 70, 172, GrowthRate.MEDIUM_FAST, null, false),
@ -1131,13 +1131,13 @@ export function initSpecies() {
),
new PokemonSpecies(Species.LAPRAS, 1, false, false, false, "Transport Pokémon", Type.WATER, Type.ICE, 2.5, 220, Abilities.WATER_ABSORB, Abilities.SHELL_ARMOR, Abilities.HYDRATION, 535, 130, 85, 80, 85, 95, 60, 45, 50, 187, GrowthRate.SLOW, 50, false, true,
new PokemonForm("Normal", "", Type.WATER, Type.ICE, 2.5, 220, Abilities.WATER_ABSORB, Abilities.SHELL_ARMOR, Abilities.HYDRATION, 535, 130, 85, 80, 85, 95, 60, 45, 50, 187, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ICE, 24, 220, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 635, 170, 85, 95, 115, 110, 60, 45, 50, 187),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ICE, 24, 999.9, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, Abilities.SHELL_ARMOR, 635, 170, 85, 95, 115, 110, 60, 45, 50, 187),
),
new PokemonSpecies(Species.DITTO, 1, false, false, false, "Transform Pokémon", Type.NORMAL, null, 0.3, 4, Abilities.LIMBER, Abilities.NONE, Abilities.IMPOSTER, 288, 48, 48, 48, 48, 48, 48, 35, 50, 101, GrowthRate.MEDIUM_FAST, null, false),
new PokemonSpecies(Species.EEVEE, 1, false, false, false, "Evolution Pokémon", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 325, 55, 55, 50, 45, 65, 55, 45, 50, 65, GrowthRate.MEDIUM_FAST, 87.5, false, true,
new PokemonForm("Normal", "", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 325, 55, 55, 50, 45, 65, 55, 45, 50, 65, false, null, true),
new PokemonForm("Partner", "partner", Type.NORMAL, null, 0.3, 6.5, Abilities.RUN_AWAY, Abilities.ADAPTABILITY, Abilities.ANTICIPATION, 435, 65, 75, 70, 65, 85, 75, 45, 50, 65, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 18, 6.5, Abilities.PROTEAN, Abilities.PROTEAN, Abilities.PROTEAN, 535, 105, 95, 70, 95, 85, 85, 45, 50, 65), //+100 BST from Partner Form
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 18, 999.9, Abilities.PROTEAN, Abilities.PROTEAN, Abilities.PROTEAN, 535, 110, 90, 70, 95, 85, 85, 45, 50, 65), //+100 BST from Partner Form
),
new PokemonSpecies(Species.VAPOREON, 1, false, false, false, "Bubble Jet Pokémon", Type.WATER, null, 1, 29, Abilities.WATER_ABSORB, Abilities.NONE, Abilities.HYDRATION, 525, 130, 65, 60, 110, 95, 65, 45, 50, 184, GrowthRate.MEDIUM_FAST, 87.5, false),
new PokemonSpecies(Species.JOLTEON, 1, false, false, false, "Lightning Pokémon", Type.ELECTRIC, null, 0.8, 24.5, Abilities.VOLT_ABSORB, Abilities.NONE, Abilities.QUICK_FEET, 525, 65, 65, 60, 110, 95, 130, 45, 50, 184, GrowthRate.MEDIUM_FAST, 87.5, false),
@ -1153,7 +1153,7 @@ export function initSpecies() {
),
new PokemonSpecies(Species.SNORLAX, 1, false, false, false, "Sleeping Pokémon", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, GrowthRate.SLOW, 87.5, false, true,
new PokemonForm("Normal", "", Type.NORMAL, null, 2.1, 460, Abilities.IMMUNITY, Abilities.THICK_FAT, Abilities.GLUTTONY, 540, 160, 110, 65, 65, 110, 30, 25, 50, 189, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 35, 460, Abilities.HARVEST, Abilities.HARVEST, Abilities.HARVEST, 640, 200, 135, 80, 80, 125, 20, 25, 50, 189),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.NORMAL, null, 35, 999.9, Abilities.HARVEST, Abilities.HARVEST, Abilities.HARVEST, 640, 200, 135, 80, 80, 125, 20, 25, 50, 189),
),
new PokemonSpecies(Species.ARTICUNO, 1, true, false, false, "Freeze Pokémon", Type.ICE, Type.FLYING, 1.7, 55.4, Abilities.PRESSURE, Abilities.NONE, Abilities.SNOW_CLOAK, 580, 90, 85, 100, 95, 125, 85, 3, 35, 290, GrowthRate.SLOW, null, false),
new PokemonSpecies(Species.ZAPDOS, 1, true, false, false, "Electric Pokémon", Type.ELECTRIC, Type.FLYING, 1.6, 52.6, Abilities.PRESSURE, Abilities.NONE, Abilities.STATIC, 580, 90, 90, 85, 125, 90, 100, 3, 35, 290, GrowthRate.SLOW, null, false),
@ -1793,7 +1793,7 @@ export function initSpecies() {
new PokemonSpecies(Species.TRUBBISH, 5, false, false, false, "Trash Bag Pokémon", Type.POISON, null, 0.6, 31, Abilities.STENCH, Abilities.STICKY_HOLD, Abilities.AFTERMATH, 329, 50, 50, 62, 40, 62, 65, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.GARBODOR, 5, false, false, false, "Trash Heap Pokémon", Type.POISON, null, 1.9, 107.3, Abilities.STENCH, Abilities.WEAK_ARMOR, Abilities.AFTERMATH, 474, 80, 95, 82, 60, 82, 75, 60, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.POISON, null, 1.9, 107.3, Abilities.STENCH, Abilities.WEAK_ARMOR, Abilities.AFTERMATH, 474, 80, 95, 82, 60, 82, 75, 60, 50, 166, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.POISON, Type.STEEL, 21, 107.3, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, 574, 135, 125, 102, 57, 102, 53, 60, 50, 166),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.POISON, Type.STEEL, 21, 999.9, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, Abilities.TOXIC_DEBRIS, 574, 135, 125, 102, 57, 102, 53, 60, 50, 166),
),
new PokemonSpecies(Species.ZORUA, 5, false, false, false, "Tricky Fox Pokémon", Type.DARK, null, 0.7, 12.5, Abilities.ILLUSION, Abilities.NONE, Abilities.NONE, 330, 40, 65, 40, 80, 40, 65, 75, 50, 66, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.ZOROARK, 5, false, false, false, "Illusion Fox Pokémon", Type.DARK, null, 1.6, 81.1, Abilities.ILLUSION, Abilities.NONE, Abilities.NONE, 510, 60, 105, 60, 120, 60, 105, 45, 50, 179, GrowthRate.MEDIUM_SLOW, 87.5, false),
@ -2267,25 +2267,25 @@ export function initSpecies() {
new PokemonSpecies(Species.MELTAN, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 0.2, 8, Abilities.MAGNET_PULL, Abilities.NONE, Abilities.NONE, 300, 46, 65, 65, 55, 35, 34, 3, 0, 150, GrowthRate.SLOW, null, false),
new PokemonSpecies(Species.MELMETAL, 7, false, false, true, "Hex Nut Pokémon", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, GrowthRate.SLOW, null, false, true,
new PokemonForm("Normal", "", Type.STEEL, null, 2.5, 800, Abilities.IRON_FIST, Abilities.NONE, Abilities.NONE, 600, 135, 143, 143, 80, 65, 34, 3, 0, 300, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 800, Abilities.IRON_FIST, Abilities.IRON_FIST, Abilities.IRON_FIST, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, null, 25, 999.9, Abilities.IRON_FIST, Abilities.IRON_FIST, Abilities.IRON_FIST, 700, 175, 165, 155, 85, 75, 45, 3, 0, 300),
),
new PokemonSpecies(Species.GROOKEY, 8, false, false, false, "Chimp Pokémon", Type.GRASS, null, 0.3, 5, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 310, 50, 65, 50, 40, 40, 65, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.THWACKEY, 8, false, false, false, "Beat Pokémon", Type.GRASS, null, 0.7, 14, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 420, 70, 85, 70, 55, 60, 80, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.RILLABOOM, 8, false, false, false, "Drummer Pokémon", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
new PokemonForm("Normal", "", Type.GRASS, null, 2.1, 90, Abilities.OVERGROW, Abilities.NONE, Abilities.GRASSY_SURGE, 530, 100, 125, 90, 60, 70, 85, 45, 50, 265, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 90, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, null, 28, 999.9, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, Abilities.GRASSY_SURGE, 630, 125, 150, 105, 85, 85, 80, 45, 50, 265),
),
new PokemonSpecies(Species.SCORBUNNY, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.3, 4.5, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 310, 50, 71, 40, 40, 40, 69, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.RABOOT, 8, false, false, false, "Rabbit Pokémon", Type.FIRE, null, 0.6, 9, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 420, 65, 86, 60, 55, 60, 94, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.CINDERACE, 8, false, false, false, "Striker Pokémon", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
new PokemonForm("Normal", "", Type.FIRE, null, 1.4, 33, Abilities.BLAZE, Abilities.NONE, Abilities.LIBERO, 530, 80, 116, 75, 65, 75, 119, 45, 50, 265, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 33, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, null, 27, 999.9, Abilities.LIBERO, Abilities.LIBERO, Abilities.LIBERO, 630, 100, 146, 80, 90, 80, 134, 45, 50, 265),
),
new PokemonSpecies(Species.SOBBLE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.3, 4, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 310, 50, 40, 40, 70, 40, 70, 45, 50, 62, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.DRIZZILE, 8, false, false, false, "Water Lizard Pokémon", Type.WATER, null, 0.7, 11.5, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 420, 65, 60, 55, 95, 55, 90, 45, 50, 147, GrowthRate.MEDIUM_SLOW, 87.5, false),
new PokemonSpecies(Species.INTELEON, 8, false, false, false, "Secret Agent Pokémon", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, GrowthRate.MEDIUM_SLOW, 87.5, false, true,
new PokemonForm("Normal", "", Type.WATER, null, 1.9, 45.2, Abilities.TORRENT, Abilities.NONE, Abilities.SNIPER, 530, 70, 85, 65, 125, 65, 120, 45, 50, 265, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 45.2, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, null, 40, 999.9, Abilities.SNIPER, Abilities.SNIPER, Abilities.SNIPER, 630, 95, 97, 77, 147, 77, 137, 45, 50, 265),
),
new PokemonSpecies(Species.SKWOVET, 8, false, false, false, "Cheeky Pokémon", Type.NORMAL, null, 0.3, 2.5, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 275, 70, 55, 55, 35, 35, 25, 255, 50, 55, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.GREEDENT, 8, false, false, false, "Greedy Pokémon", Type.NORMAL, null, 0.6, 6, Abilities.CHEEK_POUCH, Abilities.NONE, Abilities.GLUTTONY, 460, 120, 95, 95, 55, 75, 20, 90, 50, 161, GrowthRate.MEDIUM_FAST, 50, false),
@ -2293,13 +2293,13 @@ export function initSpecies() {
new PokemonSpecies(Species.CORVISQUIRE, 8, false, false, false, "Raven Pokémon", Type.FLYING, null, 0.8, 16, Abilities.KEEN_EYE, Abilities.UNNERVE, Abilities.BIG_PECKS, 365, 68, 67, 55, 43, 55, 77, 120, 50, 128, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(Species.CORVIKNIGHT, 8, false, false, false, "Raven Pokémon", Type.FLYING, Type.STEEL, 2.2, 75, Abilities.PRESSURE, Abilities.UNNERVE, Abilities.MIRROR_ARMOR, 495, 98, 87, 105, 53, 85, 67, 45, 50, 248, GrowthRate.MEDIUM_SLOW, 50, false, true,
new PokemonForm("Normal", "", Type.FLYING, Type.STEEL, 2.2, 75, Abilities.PRESSURE, Abilities.UNNERVE, Abilities.MIRROR_ARMOR, 495, 98, 87, 105, 53, 85, 67, 45, 50, 248, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FLYING, Type.STEEL, 14, 75, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, 595, 128, 102, 140, 53, 95, 77, 45, 50, 248),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FLYING, Type.STEEL, 14, 999.9, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, Abilities.MIRROR_ARMOR, 595, 128, 102, 140, 53, 95, 77, 45, 50, 248),
),
new PokemonSpecies(Species.BLIPBUG, 8, false, false, false, "Larva Pokémon", Type.BUG, null, 0.4, 8, Abilities.SWARM, Abilities.COMPOUND_EYES, Abilities.TELEPATHY, 180, 25, 20, 20, 25, 45, 45, 255, 50, 36, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.DOTTLER, 8, false, false, false, "Radome Pokémon", Type.BUG, Type.PSYCHIC, 0.4, 19.5, Abilities.SWARM, Abilities.COMPOUND_EYES, Abilities.TELEPATHY, 335, 50, 35, 80, 50, 90, 30, 120, 50, 117, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.ORBEETLE, 8, false, false, false, "Seven Spot Pokémon", Type.BUG, Type.PSYCHIC, 0.4, 40.8, Abilities.SWARM, Abilities.FRISK, Abilities.TELEPATHY, 505, 60, 45, 110, 80, 120, 90, 45, 50, 253, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.BUG, Type.PSYCHIC, 0.4, 40.8, Abilities.SWARM, Abilities.FRISK, Abilities.TELEPATHY, 505, 60, 45, 110, 80, 120, 90, 45, 50, 253, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.PSYCHIC, 14, 40.8, Abilities.TRACE, Abilities.TRACE, Abilities.TRACE, 605, 90, 45, 130, 110, 140, 90, 45, 50, 253),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.BUG, Type.PSYCHIC, 14, 999.9, Abilities.TRACE, Abilities.TRACE, Abilities.TRACE, 605, 90, 45, 130, 110, 140, 90, 45, 50, 253),
),
new PokemonSpecies(Species.NICKIT, 8, false, false, false, "Fox Pokémon", Type.DARK, null, 0.6, 8.9, Abilities.RUN_AWAY, Abilities.UNBURDEN, Abilities.STAKEOUT, 245, 40, 28, 28, 47, 52, 50, 255, 50, 49, GrowthRate.FAST, 50, false),
new PokemonSpecies(Species.THIEVUL, 8, false, false, false, "Fox Pokémon", Type.DARK, null, 1.2, 19.9, Abilities.RUN_AWAY, Abilities.UNBURDEN, Abilities.STAKEOUT, 455, 70, 58, 58, 87, 92, 90, 127, 50, 159, GrowthRate.FAST, 50, false),
@ -2310,7 +2310,7 @@ export function initSpecies() {
new PokemonSpecies(Species.CHEWTLE, 8, false, false, false, "Snapping Pokémon", Type.WATER, null, 0.3, 8.5, Abilities.STRONG_JAW, Abilities.SHELL_ARMOR, Abilities.SWIFT_SWIM, 284, 50, 64, 50, 38, 38, 44, 255, 50, 57, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.DREDNAW, 8, false, false, false, "Bite Pokémon", Type.WATER, Type.ROCK, 1, 115.5, Abilities.STRONG_JAW, Abilities.SHELL_ARMOR, Abilities.SWIFT_SWIM, 485, 90, 115, 90, 48, 68, 74, 75, 50, 170, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.WATER, Type.ROCK, 1, 115.5, Abilities.STRONG_JAW, Abilities.SHELL_ARMOR, Abilities.SWIFT_SWIM, 485, 90, 115, 90, 48, 68, 74, 75, 50, 170, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ROCK, 24, 115.5, Abilities.STRONG_JAW, Abilities.STRONG_JAW, Abilities.STRONG_JAW, 585, 115, 145, 115, 43, 83, 84, 75, 50, 170),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.WATER, Type.ROCK, 24, 999.9, Abilities.STRONG_JAW, Abilities.STRONG_JAW, Abilities.STRONG_JAW, 585, 115, 145, 115, 43, 83, 84, 75, 50, 170),
),
new PokemonSpecies(Species.YAMPER, 8, false, false, false, "Puppy Pokémon", Type.ELECTRIC, null, 0.3, 13.5, Abilities.BALL_FETCH, Abilities.NONE, Abilities.RATTLED, 270, 59, 45, 50, 40, 50, 26, 255, 50, 54, GrowthRate.FAST, 50, false),
new PokemonSpecies(Species.BOLTUND, 8, false, false, false, "Dog Pokémon", Type.ELECTRIC, null, 1, 34, Abilities.STRONG_JAW, Abilities.NONE, Abilities.COMPETITIVE, 490, 69, 90, 60, 90, 60, 121, 45, 50, 172, GrowthRate.FAST, 50, false),
@ -2318,21 +2318,21 @@ export function initSpecies() {
new PokemonSpecies(Species.CARKOL, 8, false, false, false, "Coal Pokémon", Type.ROCK, Type.FIRE, 1.1, 78, Abilities.STEAM_ENGINE, Abilities.FLAME_BODY, Abilities.FLASH_FIRE, 410, 80, 60, 90, 60, 70, 50, 120, 50, 144, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(Species.COALOSSAL, 8, false, false, false, "Coal Pokémon", Type.ROCK, Type.FIRE, 2.8, 310.5, Abilities.STEAM_ENGINE, Abilities.FLAME_BODY, Abilities.FLASH_FIRE, 510, 110, 80, 120, 80, 90, 30, 45, 50, 255, GrowthRate.MEDIUM_SLOW, 50, false, true,
new PokemonForm("Normal", "", Type.ROCK, Type.FIRE, 2.8, 310.5, Abilities.STEAM_ENGINE, Abilities.FLAME_BODY, Abilities.FLASH_FIRE, 510, 110, 80, 120, 80, 90, 30, 45, 50, 255, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ROCK, Type.FIRE, 42, 310.5, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, 610, 140, 95, 130, 95, 110, 40, 45, 50, 255),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ROCK, Type.FIRE, 42, 999.9, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, Abilities.STEAM_ENGINE, 610, 140, 95, 130, 95, 110, 40, 45, 50, 255),
),
new PokemonSpecies(Species.APPLIN, 8, false, false, false, "Apple Core Pokémon", Type.GRASS, Type.DRAGON, 0.2, 0.5, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.BULLETPROOF, 260, 40, 40, 80, 40, 40, 20, 255, 50, 52, GrowthRate.ERRATIC, 50, false),
new PokemonSpecies(Species.FLAPPLE, 8, false, false, false, "Apple Wing Pokémon", Type.GRASS, Type.DRAGON, 0.3, 1, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.HUSTLE, 485, 70, 110, 80, 95, 60, 70, 45, 50, 170, GrowthRate.ERRATIC, 50, false, true,
new PokemonForm("Normal", "", Type.GRASS, Type.DRAGON, 0.3, 1, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.HUSTLE, 485, 70, 110, 80, 95, 60, 70, 45, 50, 170, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 1, Abilities.HUSTLE, Abilities.HUSTLE, Abilities.HUSTLE, 585, 90, 130, 100, 85, 80, 100, 45, 50, 170),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 999.9, Abilities.HUSTLE, Abilities.HUSTLE, Abilities.HUSTLE, 585, 90, 130, 100, 85, 80, 100, 45, 50, 170),
),
new PokemonSpecies(Species.APPLETUN, 8, false, false, false, "Apple Nectar Pokémon", Type.GRASS, Type.DRAGON, 0.4, 13, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.THICK_FAT, 485, 110, 85, 80, 100, 80, 30, 45, 50, 170, GrowthRate.ERRATIC, 50, false, true,
new PokemonForm("Normal", "", Type.GRASS, Type.DRAGON, 0.4, 13, Abilities.RIPEN, Abilities.GLUTTONY, Abilities.THICK_FAT, 485, 110, 85, 80, 100, 80, 30, 45, 50, 170, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 13, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 585, 130, 75, 115, 125, 115, 25, 45, 50, 170),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GRASS, Type.DRAGON, 24, 999.9, Abilities.THICK_FAT, Abilities.THICK_FAT, Abilities.THICK_FAT, 585, 130, 75, 115, 125, 115, 25, 45, 50, 170),
),
new PokemonSpecies(Species.SILICOBRA, 8, false, false, false, "Sand Snake Pokémon", Type.GROUND, null, 2.2, 7.6, Abilities.SAND_SPIT, Abilities.SHED_SKIN, Abilities.SAND_VEIL, 315, 52, 57, 75, 35, 50, 46, 255, 50, 63, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.SANDACONDA, 8, false, false, false, "Sand Snake Pokémon", Type.GROUND, null, 3.8, 65.5, Abilities.SAND_SPIT, Abilities.SHED_SKIN, Abilities.SAND_VEIL, 510, 72, 107, 125, 65, 70, 71, 120, 50, 179, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.GROUND, null, 3.8, 65.5, Abilities.SAND_SPIT, Abilities.SHED_SKIN, Abilities.SAND_VEIL, 510, 72, 107, 125, 65, 70, 71, 120, 50, 179, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GROUND, null, 22, 65.5, Abilities.SAND_SPIT, Abilities.SAND_SPIT, Abilities.SAND_SPIT, 610, 117, 137, 140, 55, 80, 81, 120, 50, 179),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.GROUND, null, 22, 999.9, Abilities.SAND_SPIT, Abilities.SAND_SPIT, Abilities.SAND_SPIT, 610, 117, 137, 140, 55, 80, 81, 120, 50, 179),
),
new PokemonSpecies(Species.CRAMORANT, 8, false, false, false, "Gulp Pokémon", Type.FLYING, Type.WATER, 0.8, 18, Abilities.GULP_MISSILE, Abilities.NONE, Abilities.NONE, 475, 70, 85, 55, 85, 95, 85, 45, 50, 166, GrowthRate.MEDIUM_FAST, 50, false, false,
new PokemonForm("Normal", "", Type.FLYING, Type.WATER, 0.8, 18, Abilities.GULP_MISSILE, Abilities.NONE, Abilities.NONE, 475, 70, 85, 55, 85, 95, 85, 45, 50, 166, false, null, true),
@ -2345,12 +2345,12 @@ export function initSpecies() {
new PokemonSpecies(Species.TOXTRICITY, 8, false, false, false, "Punk Pokémon", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.PLUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, GrowthRate.MEDIUM_SLOW, 50, false, true,
new PokemonForm("Amped Form", "amped", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.PLUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, false, "", true),
new PokemonForm("Low-Key Form", "lowkey", Type.ELECTRIC, Type.POISON, 1.6, 40, Abilities.PUNK_ROCK, Abilities.MINUS, Abilities.TECHNICIAN, 502, 75, 98, 70, 114, 70, 75, 45, 50, 176, false, "lowkey", true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, Type.POISON, 24, 40, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, 602, 114, 98, 82, 144, 82, 82, 45, 50, 176),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.ELECTRIC, Type.POISON, 24, 999.9, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, Abilities.PUNK_ROCK, 602, 114, 98, 82, 144, 82, 82, 45, 50, 176),
),
new PokemonSpecies(Species.SIZZLIPEDE, 8, false, false, false, "Radiator Pokémon", Type.FIRE, Type.BUG, 0.7, 1, Abilities.FLASH_FIRE, Abilities.WHITE_SMOKE, Abilities.FLAME_BODY, 305, 50, 65, 45, 50, 50, 45, 190, 50, 61, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.CENTISKORCH, 8, false, false, false, "Radiator Pokémon", Type.FIRE, Type.BUG, 3, 120, Abilities.FLASH_FIRE, Abilities.WHITE_SMOKE, Abilities.FLAME_BODY, 525, 100, 115, 65, 90, 90, 65, 75, 50, 184, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.FIRE, Type.BUG, 3, 120, Abilities.FLASH_FIRE, Abilities.WHITE_SMOKE, Abilities.FLAME_BODY, 525, 100, 115, 65, 90, 90, 65, 75, 50, 184, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.BUG, 75, 120, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, 625, 140, 145, 75, 90, 100, 75, 75, 50, 184),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FIRE, Type.BUG, 75, 999.9, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, Abilities.FLASH_FIRE, 625, 140, 145, 75, 90, 100, 75, 75, 50, 184),
),
new PokemonSpecies(Species.CLOBBOPUS, 8, false, false, false, "Tantrum Pokémon", Type.FIGHTING, null, 0.6, 4, Abilities.LIMBER, Abilities.NONE, Abilities.TECHNICIAN, 310, 50, 68, 60, 50, 50, 32, 180, 50, 62, GrowthRate.MEDIUM_SLOW, 50, false),
new PokemonSpecies(Species.GRAPPLOCT, 8, false, false, false, "Jujitsu Pokémon", Type.FIGHTING, null, 1.6, 39, Abilities.LIMBER, Abilities.NONE, Abilities.TECHNICIAN, 480, 80, 118, 90, 70, 80, 42, 45, 50, 168, GrowthRate.MEDIUM_SLOW, 50, false),
@ -2366,13 +2366,13 @@ export function initSpecies() {
new PokemonSpecies(Species.HATTREM, 8, false, false, false, "Serene Pokémon", Type.PSYCHIC, null, 0.6, 4.8, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 370, 57, 40, 65, 86, 73, 49, 120, 50, 130, GrowthRate.SLOW, 0, false),
new PokemonSpecies(Species.HATTERENE, 8, false, false, false, "Silent Pokémon", Type.PSYCHIC, Type.FAIRY, 2.1, 5.1, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 510, 57, 90, 95, 136, 103, 29, 45, 50, 255, GrowthRate.SLOW, 0, false, true,
new PokemonForm("Normal", "", Type.PSYCHIC, Type.FAIRY, 2.1, 5.1, Abilities.HEALER, Abilities.ANTICIPATION, Abilities.MAGIC_BOUNCE, 510, 57, 90, 95, 136, 103, 29, 45, 50, 255, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.PSYCHIC, Type.FAIRY, 26, 5.1, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, 610, 97, 90, 105, 146, 122, 50, 45, 50, 255),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.PSYCHIC, Type.FAIRY, 26, 999.9, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, Abilities.MAGIC_BOUNCE, 610, 97, 90, 105, 146, 122, 50, 45, 50, 255),
),
new PokemonSpecies(Species.IMPIDIMP, 8, false, false, false, "Wily Pokémon", Type.DARK, Type.FAIRY, 0.4, 5.5, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 265, 45, 45, 30, 55, 40, 50, 255, 50, 53, GrowthRate.MEDIUM_FAST, 100, false),
new PokemonSpecies(Species.MORGREM, 8, false, false, false, "Devious Pokémon", Type.DARK, Type.FAIRY, 0.8, 12.5, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 370, 65, 60, 45, 75, 55, 70, 120, 50, 130, GrowthRate.MEDIUM_FAST, 100, false),
new PokemonSpecies(Species.GRIMMSNARL, 8, false, false, false, "Bulk Up Pokémon", Type.DARK, Type.FAIRY, 1.5, 61, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 510, 95, 120, 65, 95, 75, 60, 45, 50, 255, GrowthRate.MEDIUM_FAST, 100, false, true,
new PokemonForm("Normal", "", Type.DARK, Type.FAIRY, 1.5, 61, Abilities.PRANKSTER, Abilities.FRISK, Abilities.PICKPOCKET, 510, 95, 120, 65, 95, 75, 60, 45, 50, 255, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.DARK, Type.FAIRY, 32, 61, Abilities.PRANKSTER, Abilities.PRANKSTER, Abilities.PRANKSTER, 610, 135, 138, 77, 110, 85, 65, 45, 50, 255),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.DARK, Type.FAIRY, 32, 999.9, Abilities.PRANKSTER, Abilities.PRANKSTER, Abilities.PRANKSTER, 610, 135, 138, 77, 110, 85, 65, 45, 50, 255),
),
new PokemonSpecies(Species.OBSTAGOON, 8, false, false, false, "Blocking Pokémon", Type.DARK, Type.NORMAL, 1.6, 46, Abilities.RECKLESS, Abilities.GUTS, Abilities.DEFIANT, 520, 93, 90, 101, 60, 81, 95, 45, 50, 260, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.PERRSERKER, 8, false, false, false, "Viking Pokémon", Type.STEEL, null, 0.8, 28, Abilities.BATTLE_ARMOR, Abilities.TOUGH_CLAWS, Abilities.STEELY_SPIRIT, 440, 70, 110, 100, 50, 60, 50, 90, 50, 154, GrowthRate.MEDIUM_FAST, 50, false),
@ -2391,7 +2391,7 @@ export function initSpecies() {
new PokemonForm("Ruby Swirl", "ruby-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true),
new PokemonForm("Caramel Swirl", "caramel-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true),
new PokemonForm("Rainbow Swirl", "rainbow-swirl", Type.FAIRY, null, 0.3, 0.5, Abilities.SWEET_VEIL, Abilities.NONE, Abilities.AROMA_VEIL, 495, 65, 60, 75, 110, 121, 64, 100, 50, 173, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 0.5, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.FAIRY, null, 30, 999.9, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, Abilities.MISTY_SURGE, 595, 135, 60, 75, 130, 131, 64, 100, 50, 173),
),
new PokemonSpecies(Species.FALINKS, 8, false, false, false, "Formation Pokémon", Type.FIGHTING, null, 3, 62, Abilities.BATTLE_ARMOR, Abilities.NONE, Abilities.DEFIANT, 470, 65, 100, 100, 70, 60, 75, 45, 50, 165, GrowthRate.MEDIUM_FAST, null, false),
new PokemonSpecies(Species.PINCURCHIN, 8, false, false, false, "Sea Urchin Pokémon", Type.ELECTRIC, null, 0.3, 1, Abilities.LIGHTNING_ROD, Abilities.NONE, Abilities.ELECTRIC_SURGE, 435, 48, 101, 95, 91, 85, 15, 75, 50, 152, GrowthRate.MEDIUM_FAST, 50, false),
@ -2413,7 +2413,7 @@ export function initSpecies() {
new PokemonSpecies(Species.CUFANT, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 1.2, 100, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 330, 72, 80, 49, 40, 49, 40, 190, 50, 66, GrowthRate.MEDIUM_FAST, 50, false),
new PokemonSpecies(Species.COPPERAJAH, 8, false, false, false, "Copperderm Pokémon", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.STEEL, null, 3, 650, Abilities.SHEER_FORCE, Abilities.NONE, Abilities.HEAVY_METAL, 500, 122, 130, 69, 80, 69, 30, 90, 50, 175, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 650, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.GROUND, 23, 999.9, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, Abilities.MOLD_BREAKER, 600, 167, 155, 89, 80, 89, 20, 90, 50, 175),
),
new PokemonSpecies(Species.DRACOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.DRAGON, 1.8, 190, Abilities.VOLT_ABSORB, Abilities.HUSTLE, Abilities.SAND_RUSH, 505, 90, 100, 90, 80, 70, 75, 45, 50, 177, GrowthRate.SLOW, null, false),
new PokemonSpecies(Species.ARCTOZOLT, 8, false, false, false, "Fossil Pokémon", Type.ELECTRIC, Type.ICE, 2.3, 150, Abilities.VOLT_ABSORB, Abilities.STATIC, Abilities.SLUSH_RUSH, 505, 90, 100, 90, 90, 80, 55, 45, 50, 177, GrowthRate.SLOW, null, false),
@ -2421,7 +2421,7 @@ export function initSpecies() {
new PokemonSpecies(Species.ARCTOVISH, 8, false, false, false, "Fossil Pokémon", Type.WATER, Type.ICE, 2, 175, Abilities.WATER_ABSORB, Abilities.ICE_BODY, Abilities.SLUSH_RUSH, 505, 90, 90, 100, 80, 90, 55, 45, 50, 177, GrowthRate.SLOW, null, false),
new PokemonSpecies(Species.DURALUDON, 8, false, false, false, "Alloy Pokémon", Type.STEEL, Type.DRAGON, 1.8, 40, Abilities.LIGHT_METAL, Abilities.HEAVY_METAL, Abilities.STALWART, 535, 70, 95, 115, 120, 50, 85, 45, 50, 187, GrowthRate.MEDIUM_FAST, 50, false, true,
new PokemonForm("Normal", "", Type.STEEL, Type.DRAGON, 1.8, 40, Abilities.LIGHT_METAL, Abilities.HEAVY_METAL, Abilities.STALWART, 535, 70, 95, 115, 120, 50, 85, 45, 50, 187, false, null, true),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.DRAGON, 43, 40, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 635, 100, 105, 119, 166, 57, 88, 45, 50, 187),
new PokemonForm("G-Max", SpeciesFormKey.GIGANTAMAX, Type.STEEL, Type.DRAGON, 43, 999.9, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, Abilities.LIGHTNING_ROD, 635, 100, 110, 120, 175, 60, 70, 45, 50, 187),
),
new PokemonSpecies(Species.DREEPY, 8, false, false, false, "Lingering Pokémon", Type.DRAGON, Type.GHOST, 0.5, 2, Abilities.CLEAR_BODY, Abilities.INFILTRATOR, Abilities.CURSED_BODY, 270, 28, 60, 30, 40, 30, 82, 45, 50, 54, GrowthRate.SLOW, 50, false),
new PokemonSpecies(Species.DRAKLOAK, 8, false, false, false, "Caretaker Pokémon", Type.DRAGON, Type.GHOST, 1.4, 11, Abilities.CLEAR_BODY, Abilities.INFILTRATOR, Abilities.CURSED_BODY, 410, 68, 80, 50, 60, 50, 102, 45, 50, 144, GrowthRate.SLOW, 50, false),
@ -2436,14 +2436,14 @@ export function initSpecies() {
),
new PokemonSpecies(Species.ETERNATUS, 8, false, true, false, "Gigantic Pokémon", Type.POISON, Type.DRAGON, 20, 950, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 690, 140, 85, 95, 145, 95, 130, 255, 0, 345, GrowthRate.SLOW, null, false, true,
new PokemonForm("Normal", "", Type.POISON, Type.DRAGON, 20, 950, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 690, 140, 85, 95, 145, 95, 130, 255, 0, 345, false, null, true),
new PokemonForm("E-Max", "eternamax", Type.POISON, Type.DRAGON, 100, 0, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 1125, 255, 115, 250, 125, 250, 130, 255, 0, 345),
new PokemonForm("E-Max", "eternamax", Type.POISON, Type.DRAGON, 100, 999.9, Abilities.PRESSURE, Abilities.NONE, Abilities.NONE, 1125, 255, 115, 250, 125, 250, 130, 255, 0, 345),
),
new PokemonSpecies(Species.KUBFU, 8, true, false, false, "Wushu Pokémon", Type.FIGHTING, null, 0.6, 12, Abilities.INNER_FOCUS, Abilities.NONE, Abilities.NONE, 385, 60, 90, 60, 53, 50, 72, 3, 50, 77, GrowthRate.SLOW, 87.5, false),
new PokemonSpecies(Species.URSHIFU, 8, true, false, false, "Wushu Pokémon", Type.FIGHTING, Type.DARK, 1.9, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 550, 100, 130, 100, 63, 60, 97, 3, 50, 275, GrowthRate.SLOW, 87.5, false, true,
new PokemonForm("Single Strike Style", "single-strike", Type.FIGHTING, Type.DARK, 1.9, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 550, 100, 130, 100, 63, 60, 97, 3, 50, 275, false, "", true),
new PokemonForm("Rapid Strike Style", "rapid-strike", Type.FIGHTING, Type.WATER, 1.9, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 550, 100, 130, 100, 63, 60, 97, 3, 50, 275, false, null, true),
new PokemonForm("G-Max Single Strike Style", SpeciesFormKey.GIGANTAMAX_SINGLE, Type.FIGHTING, Type.DARK, 29, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275),
new PokemonForm("G-Max Rapid Strike Style", SpeciesFormKey.GIGANTAMAX_RAPID, Type.FIGHTING, Type.WATER, 26, 105, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275),
new PokemonForm("G-Max Single Strike Style", SpeciesFormKey.GIGANTAMAX_SINGLE, Type.FIGHTING, Type.DARK, 29, 999.9, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275),
new PokemonForm("G-Max Rapid Strike Style", SpeciesFormKey.GIGANTAMAX_RAPID, Type.FIGHTING, Type.WATER, 26, 999.9, Abilities.UNSEEN_FIST, Abilities.NONE, Abilities.NONE, 650, 125, 150, 115, 73, 70, 117, 3, 50, 275),
),
new PokemonSpecies(Species.ZARUDE, 8, false, false, true, "Rogue Monkey Pokémon", Type.DARK, Type.GRASS, 1.8, 70, Abilities.LEAF_GUARD, Abilities.NONE, Abilities.NONE, 600, 105, 120, 105, 70, 95, 105, 3, 0, 300, GrowthRate.SLOW, null, false, false,
new PokemonForm("Normal", "", Type.DARK, Type.GRASS, 1.8, 70, Abilities.LEAF_GUARD, Abilities.NONE, Abilities.NONE, 600, 105, 120, 105, 70, 95, 105, 3, 0, 300, false, null, true),

View File

@ -255,7 +255,9 @@ export class TrainerConfig {
name = i18next.t("trainerNames:rival");
}
}
this.name = name;
return this;
}
@ -937,6 +939,20 @@ export class TrainerConfig {
return this;
}
/**
* Sets a localized name for the trainer. This should only be used for trainers that dont use a "initFor" function and are considered "named" trainers
* @param name - The name of the trainer.
* @returns {TrainerConfig} The updated TrainerConfig instance.
*/
setLocalizedName(name: string): TrainerConfig {
// Check if the internationalization (i18n) system is initialized.
if (!getIsInitialized()) {
initI18n();
}
this.name = i18next.t(`trainerNames:${name.toLowerCase()}`);
return this;
}
/**
* Retrieves the title for the trainer based on the provided trainer slot and variant.
* @param {TrainerSlot} trainerSlot - The slot to determine which title to use. Defaults to TrainerSlot.NONE.
@ -2370,21 +2386,22 @@ export const trainerConfigs: TrainerConfigs = {
}
p.pokeball = PokeballType.MASTER_BALL;
})),
[TrainerType.VICTOR]: new TrainerConfig(++t).setName("Victor").setTitle("The Winstrates")
[TrainerType.VICTOR]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Victor")
.setMoneyMultiplier(1) // The Winstrate trainers have total money multiplier of 6
.setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG),
[TrainerType.VICTORIA]: new TrainerConfig(++t).setName("Victoria").setTitle("The Winstrates")
[TrainerType.VICTORIA]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Victoria")
.setMoneyMultiplier(1)
.setPartyTemplates(trainerPartyTemplates.ONE_AVG_ONE_STRONG),
[TrainerType.VIVI]: new TrainerConfig(++t).setName("Vivi").setTitle("The Winstrates")
[TrainerType.VIVI]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vivi")
.setMoneyMultiplier(1)
.setPartyTemplates(trainerPartyTemplates.TWO_AVG_ONE_STRONG),
[TrainerType.VICKY]: new TrainerConfig(++t).setName("Vicky").setTitle("The Winstrates")
[TrainerType.VICKY]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vicky")
.setMoneyMultiplier(1)
.setPartyTemplates(trainerPartyTemplates.ONE_AVG),
[TrainerType.VITO]: new TrainerConfig(++t).setName("Vito").setTitle("The Winstrates")
[TrainerType.VITO]: new TrainerConfig(++t).setTitle("The Winstrates").setLocalizedName("Vito")
.setMoneyMultiplier(2)
.setPartyTemplates(new TrainerPartyCompoundTemplate(new TrainerPartyTemplate(3, PartyMemberStrength.AVERAGE), new TrainerPartyTemplate(2, PartyMemberStrength.STRONG))),
[TrainerType.BUG_TYPE_SUPERFAN]: new TrainerConfig(++t).setMoneyMultiplier(2.25).setEncounterBgm(TrainerType.ACE_TRAINER)
.setPartyTemplates(new TrainerPartyTemplate(2, PartyMemberStrength.AVERAGE))
};

View File

@ -0,0 +1,22 @@
/**
* Defines the speed of gaining experience.
*
* @remarks
* The `expGainSpeed` can have several modes:
* - `0` - Default: The normal speed.
* - `1` - Fast: Fast speed.
* - `2` - Faster: Faster speed.
* - `3` - Skip: Skip gaining exp animation.
*
* @default 0 - Uses the default normal speed.
*/
export enum ExpGainsSpeed {
/** The normal speed. */
DEFAULT,
/** Fast speed. */
FAST,
/** Faster speed. */
FASTER,
/** Skip gaining exp animation. */
SKIP
}

View File

@ -3,14 +3,14 @@ import BattleScene, { AnySound } from "../battle-scene";
import { Variant, VariantSet, variantColorCache } from "#app/data/variant";
import { variantData } from "#app/data/variant";
import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr } from "../data/move";
import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatStagesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatStageChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, OneHitKOAccuracyAttr, RespectAttackTypeImmunityAttr, MoveTarget } from "../data/move";
import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species";
import { Constructor, isNullOrUndefined, randSeedInt } from "#app/utils";
import * as Utils from "../utils";
import { Type, TypeDamageMultiplier, getTypeDamageMultiplier, getTypeRgb } from "../data/type";
import { getLevelTotalExp } from "../data/exp";
import { Stat, type PermanentStat, type BattleStat, type EffectiveStat, PERMANENT_STATS, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat";
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier } from "../modifier/modifier";
import { DamageMoneyRewardModifier, EnemyDamageBoosterModifier, EnemyDamageReducerModifier, EnemyEndureChanceModifier, EnemyFusionChanceModifier, HiddenAbilityRateBoosterModifier, BaseStatModifier, PokemonFriendshipBoosterModifier, PokemonHeldItemModifier, PokemonNatureWeightModifier, ShinyRateBoosterModifier, SurviveDamageModifier, TempStatStageBoosterModifier, TempCritBoosterModifier, StatBoosterModifier, CritBoosterModifier, TerastallizeModifier, PokemonBaseStatFlatModifier, PokemonBaseStatTotalModifier, PokemonIncrementingStatModifier, EvoTrackerModifier } from "../modifier/modifier";
import { PokeballType } from "../data/pokeball";
import { Gender } from "../data/gender";
import { initMoveAnim, loadMoveAnimAssets } from "../data/battle-anims";
@ -99,6 +99,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
public pauseEvolutions: boolean;
public pokerus: boolean;
public wildFlee: boolean;
public evoCounter: integer;
public fusionSpecies: PokemonSpecies | null;
public fusionFormIndex: integer;
@ -195,6 +196,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
this.metSpecies = dataSource.metSpecies ?? (this.metBiome !== -1 ? this.species.speciesId : this.species.getRootSpeciesId(true));
this.pauseEvolutions = dataSource.pauseEvolutions;
this.pokerus = !!dataSource.pokerus;
this.evoCounter = dataSource.evoCounter ?? 0;
this.fusionSpecies = dataSource.fusionSpecies instanceof PokemonSpecies ? dataSource.fusionSpecies : dataSource.fusionSpecies ? getPokemonSpecies(dataSource.fusionSpecies) : null;
this.fusionFormIndex = dataSource.fusionFormIndex;
this.fusionAbilityIndex = dataSource.fusionAbilityIndex;
@ -592,8 +594,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
// Resetting properties should not be shown on the field
this.setVisible(false);
// Reset field position
this.setFieldPosition(FieldPosition.CENTER);
// Remove the offset from having a Substitute active
if (this.isOffsetBySubstitute()) {
this.x -= this.getSubstituteOffset()[0];
this.y -= this.getSubstituteOffset()[1];
@ -870,22 +871,29 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param stat the desired {@linkcode EffectiveStat}
* @param opponent the target {@linkcode Pokemon}
* @param move the {@linkcode Move} being used
* @param ignoreAbility determines whether this Pokemon's abilities should be ignored during the stat calculation
* @param ignoreOppAbility during an attack, determines whether the opposing Pokemon's abilities should be ignored during the stat calculation.
* @param isCritical determines whether a critical hit has occurred or not (`false` by default)
* @param simulated if `true`, nullifies any effects that produce any changes to game state from triggering
* @returns the final in-battle value of a stat
*/
getEffectiveStat(stat: EffectiveStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): integer {
getEffectiveStat(stat: EffectiveStat, opponent?: Pokemon, move?: Move, ignoreAbility: boolean = false, ignoreOppAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): integer {
const statValue = new Utils.NumberHolder(this.getStat(stat, false));
this.scene.applyModifiers(StatBoosterModifier, this.isPlayer(), this, stat, statValue);
// The Ruin abilities here are never ignored, but they reveal themselves on summon anyway
const fieldApplied = new Utils.BooleanHolder(false);
for (const pokemon of this.scene.getField(true)) {
applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied);
applyFieldStatMultiplierAbAttrs(FieldMultiplyStatAbAttr, pokemon, stat, statValue, this, fieldApplied, simulated);
if (fieldApplied.value) {
break;
}
}
applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, stat, statValue);
let ret = statValue.value * this.getStatStageMultiplier(stat, opponent, move, isCritical);
if (!ignoreAbility) {
applyStatMultiplierAbAttrs(StatMultiplierAbAttr, this, stat, statValue, simulated);
}
let ret = statValue.value * this.getStatStageMultiplier(stat, opponent, move, ignoreOppAbility, isCritical, simulated);
switch (stat) {
case Stat.ATK:
@ -2223,10 +2231,12 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @param stat the desired {@linkcode EffectiveStat}
* @param opponent the target {@linkcode Pokemon}
* @param move the {@linkcode Move} being used
* @param ignoreOppAbility determines whether the effects of the opponent's abilities (i.e. Unaware) should be ignored (`false` by default)
* @param isCritical determines whether a critical hit has occurred or not (`false` by default)
* @param simulated determines whether effects are applied without altering game state (`true` by default)
* @return the stat stage multiplier to be used for effective stat calculation
*/
getStatStageMultiplier(stat: EffectiveStat, opponent?: Pokemon, move?: Move, isCritical: boolean = false): number {
getStatStageMultiplier(stat: EffectiveStat, opponent?: Pokemon, move?: Move, ignoreOppAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): number {
const statStage = new Utils.IntegerHolder(this.getStatStage(stat));
const ignoreStatStage = new Utils.BooleanHolder(false);
@ -2243,7 +2253,9 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
break;
}
}
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, false, stat, ignoreStatStage);
if (!ignoreOppAbility) {
applyAbAttrs(IgnoreOpponentStatStagesAbAttr, opponent, null, simulated, stat, ignoreStatStage);
}
if (move) {
applyMoveAttrs(IgnoreOpponentStatStagesAttr, this, opponent, move, ignoreStatStage);
}
@ -2308,13 +2320,18 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
}
/**
* Apply the results of a move to this pokemon
* @param {Pokemon} source The pokemon using the move
* @param {PokemonMove} battlerMove The move being used
* @returns {HitResult} The result of the attack
*/
apply(source: Pokemon, move: Move): HitResult {
let result: HitResult;
* Calculates the damage of an attack made by another Pokemon against this Pokemon
* @param source {@linkcode Pokemon} the attacking Pokemon
* @param move {@linkcode Pokemon} the move used in the attack
* @param ignoreAbility If `true`, ignores this Pokemon's defensive ability effects
* @param isCritical If `true`, calculates damage for a critical hit.
* @param simulated If `true`, suppresses changes to game state during the calculation.
* @returns a {@linkcode DamageCalculationResult} object with three fields:
* - `cancelled`: `true` if the move was cancelled by another effect.
* - `result`: {@linkcode HitResult} indicates the attack's type effectiveness.
* - `damage`: `number` the attack's final damage output.
*/
getAttackDamage(source: Pokemon, move: Move, ignoreAbility: boolean = false, ignoreSourceAbility: boolean = false, isCritical: boolean = false, simulated: boolean = true): DamageCalculationResult {
const damage = new Utils.NumberHolder(0);
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
@ -2332,291 +2349,354 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* The effectiveness of the move being used. Along with type matchups, this
* accounts for changes in effectiveness from the move's attributes and the
* abilities of both the source and this Pokemon.
*
* Note that the source's abilities are not ignored here
*/
const typeMultiplier = this.getMoveEffectiveness(source, move, false, false, cancelled);
const typeMultiplier = this.getMoveEffectiveness(source, move, ignoreAbility, simulated, cancelled);
switch (moveCategory) {
case MoveCategory.PHYSICAL:
case MoveCategory.SPECIAL:
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
const sourceTeraType = source.getTeraType();
const isPhysical = moveCategory === MoveCategory.PHYSICAL;
const power = move.calculateBattlePower(source, this);
/** Combined damage multiplier from field effects such as weather, terrain, etc. */
const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(moveType, source.isGrounded()));
applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier);
if (cancelled.value) {
// Cancelled moves fail silently
source.stopMultiHit(this);
return HitResult.NO_EFFECT;
} else {
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === moveType) as TypeBoostTag;
if (typeBoost?.oneUse) {
source.removeTag(typeBoost.tagType);
const isTypeImmune = (typeMultiplier * arenaAttackTypeMultiplier.value) === 0;
if (cancelled.value || isTypeImmune) {
return {
cancelled: cancelled.value,
result: move.id === Moves.SHEER_COLD ? HitResult.IMMUNE : HitResult.NO_EFFECT,
damage: 0
};
}
// If the attack deals fixed damaged, return a result with that much damage
const fixedDamage = new Utils.IntegerHolder(0);
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
if (fixedDamage.value) {
return {
cancelled: false,
result: HitResult.EFFECTIVE,
damage: fixedDamage.value
};
}
// If the attack is a one-hit KO move, return a result with damage equal to this Pokemon's HP
const isOneHitKo = new Utils.BooleanHolder(false);
applyMoveAttrs(OneHitKOAttr, source, this, move, isOneHitKo);
if (isOneHitKo.value) {
return {
cancelled: false,
result: HitResult.ONE_HIT_KO,
damage: this.hp
};
}
// ----- BEGIN BASE DAMAGE MULTIPLIERS -----
/** A base damage multiplier based on the source's level */
const levelMultiplier = (2 * source.level / 5 + 2);
/** The power of the move after power boosts from abilities, etc. have applied */
const power = move.calculateBattlePower(source, this, simulated);
/**
* The attacker's offensive stat for the given move's category.
* Critical hits ignore negative stat stages.
*/
const sourceAtk = new Utils.NumberHolder(source.getEffectiveStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, ignoreSourceAbility, ignoreAbility, isCritical, simulated));
applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk);
/**
* This Pokemon's defensive stat for the given move's category.
* Critical hits ignore positive stat stages.
*/
const targetDef = new Utils.NumberHolder(this.getEffectiveStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, ignoreAbility, ignoreSourceAbility, isCritical, simulated));
applyMoveAttrs(VariableDefAttr, source, this, move, targetDef);
/**
* The attack's base damage, as determined by the source's level, move power
* and Attack stat as well as this Pokemon's Defense stat
*/
const baseDamage = ((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2;
// ------ END BASE DAMAGE MULTIPLIERS ------
/** 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities) */
const { targets, multiple } = getMoveTargets(source, move.id);
const numTargets = multiple ? targets.length : 1;
const targetMultiplier = (numTargets > 1) ? 0.75 : 1;
/** 0.25x multiplier if this is an added strike from the attacker's Parental Bond */
const parentalBondMultiplier = new Utils.NumberHolder(1);
if (!ignoreSourceAbility) {
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, simulated, numTargets, new Utils.IntegerHolder(0), parentalBondMultiplier);
}
/** Doubles damage if this Pokemon's last move was Glaive Rush */
const glaiveRushMultiplier = new Utils.IntegerHolder(1);
if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) {
glaiveRushMultiplier.value = 2;
}
/** The damage multiplier when the given move critically hits */
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
applyAbAttrs(MultCritAbAttr, source, null, simulated, criticalMultiplier);
/**
* A multiplier for random damage spread in the range [0.85, 1]
* This is always 1 for simulated calls.
*/
const randomMultiplier = simulated ? 1 : ((this.randSeedIntRange(85, 100)) / 100);
const sourceTypes = source.getTypes();
const sourceTeraType = source.getTeraType();
const matchesSourceType = sourceTypes.includes(moveType);
/** A damage multiplier for when the attack is of the attacker's type and/or Tera type. */
const stabMultiplier = new Utils.NumberHolder(1);
if (matchesSourceType) {
stabMultiplier.value += 0.5;
}
if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) {
stabMultiplier.value += 0.5;
}
if (!ignoreSourceAbility) {
applyAbAttrs(StabBoostAbAttr, source, null, simulated, stabMultiplier);
}
stabMultiplier.value = Math.min(stabMultiplier.value, 2.25);
/** Halves damage if the attacker is using a physical attack while burned */
const burnMultiplier = new Utils.NumberHolder(1);
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
if (!move.hasAttr(BypassBurnDamageReductionAttr)) {
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
if (!ignoreSourceAbility) {
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, simulated);
}
/** Combined damage multiplier from field effects such as weather, terrain, etc. */
const arenaAttackTypeMultiplier = new Utils.NumberHolder(this.scene.arena.getAttackTypeMultiplier(moveType, source.isGrounded()));
applyMoveAttrs(IgnoreWeatherTypeDebuffAttr, source, this, move, arenaAttackTypeMultiplier);
/**
* Whether or not this Pokemon is immune to the incoming move.
* Note that this isn't fully resolved in `getMoveEffectiveness` because
* of possible type-suppressing field effects (e.g. Desolate Land's effect on Water-type attacks).
*/
const isTypeImmune = (typeMultiplier * arenaAttackTypeMultiplier.value) === 0;
if (isTypeImmune) {
// Moves with no effect that were not cancelled queue a "no effect" message before failing
source.stopMultiHit(this);
result = (move.id === Moves.SHEER_COLD)
? HitResult.IMMUNE
: HitResult.NO_EFFECT;
if (result === HitResult.IMMUNE) {
this.scene.queueMessage(i18next.t("battle:hitResultImmune", { pokemonName: this.name }));
} else {
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) }));
}
return result;
}
const glaiveRushModifier = new Utils.IntegerHolder(1);
if (this.getTag(BattlerTagType.RECEIVE_DOUBLE_DAMAGE)) {
glaiveRushModifier.value = 2;
}
let isCritical: boolean;
const critOnly = new Utils.BooleanHolder(false);
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move);
if (critOnly.value || critAlways) {
isCritical = true;
} else {
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance);
if (Overrides.NEVER_CRIT_OVERRIDE) {
isCritical = false;
}
}
if (isCritical) {
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit);
if (noCritTag || blockCrit.value) {
isCritical = false;
}
}
const sourceAtk = new Utils.IntegerHolder(source.getEffectiveStat(isPhysical ? Stat.ATK : Stat.SPATK, this, undefined, isCritical));
const targetDef = new Utils.IntegerHolder(this.getEffectiveStat(isPhysical ? Stat.DEF : Stat.SPDEF, source, move, isCritical));
const criticalMultiplier = new Utils.NumberHolder(isCritical ? 1.5 : 1);
applyAbAttrs(MultCritAbAttr, source, null, false, criticalMultiplier);
const screenMultiplier = new Utils.NumberHolder(1);
if (!isCritical) {
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier);
}
const sourceTypes = source.getTypes();
const matchesSourceType = sourceTypes[0] === moveType || (sourceTypes.length > 1 && sourceTypes[1] === moveType);
const stabMultiplier = new Utils.NumberHolder(1);
if (sourceTeraType === Type.UNKNOWN && matchesSourceType) {
stabMultiplier.value += 0.5;
} else if (sourceTeraType !== Type.UNKNOWN && sourceTeraType === moveType) {
stabMultiplier.value += 0.5;
}
applyAbAttrs(StabBoostAbAttr, source, null, false, stabMultiplier);
if (sourceTeraType !== Type.UNKNOWN && matchesSourceType) {
stabMultiplier.value = Math.min(stabMultiplier.value + 0.5, 2.25);
}
// 25% damage debuff on moves hitting more than one non-fainted target (regardless of immunities)
const { targets, multiple } = getMoveTargets(source, move.id);
const targetMultiplier = (multiple && targets.length > 1) ? 0.75 : 1;
applyMoveAttrs(VariableAtkAttr, source, this, move, sourceAtk);
applyMoveAttrs(VariableDefAttr, source, this, move, targetDef);
const effectPhase = this.scene.getCurrentPhase();
let numTargets = 1;
if (effectPhase instanceof MoveEffectPhase) {
numTargets = effectPhase.getTargets().length;
}
const twoStrikeMultiplier = new Utils.NumberHolder(1);
applyPreAttackAbAttrs(AddSecondStrikeAbAttr, source, this, move, false, numTargets, new Utils.IntegerHolder(0), twoStrikeMultiplier);
if (!isTypeImmune) {
const levelMultiplier = (2 * source.level / 5 + 2);
const randomMultiplier = (this.randSeedIntRange(85, 100) / 100);
damage.value = Utils.toDmgValue((((levelMultiplier * power * sourceAtk.value / targetDef.value) / 50) + 2)
* stabMultiplier.value
* typeMultiplier
* arenaAttackTypeMultiplier.value
* screenMultiplier.value
* twoStrikeMultiplier.value
* targetMultiplier
* criticalMultiplier.value
* glaiveRushModifier.value
* randomMultiplier);
if (isPhysical && source.status && source.status.effect === StatusEffect.BURN) {
if (!move.hasAttr(BypassBurnDamageReductionAttr)) {
const burnDamageReductionCancelled = new Utils.BooleanHolder(false);
applyAbAttrs(BypassBurnDamageReductionAbAttr, source, burnDamageReductionCancelled, false);
if (!burnDamageReductionCancelled.value) {
damage.value = Utils.toDmgValue(damage.value / 2);
}
}
}
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, false, damage);
/**
* For each {@link HitsTagAttr} the move has, doubles the damage of the move if:
* The target has a {@link BattlerTagType} that this move interacts with
* AND
* The move doubles damage when used against that tag
*/
move.getAttrs(HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
if (this.getTag(hta.tagType)) {
damage.value *= 2;
}
});
}
if (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && moveType === Type.DRAGON) {
damage.value = Utils.toDmgValue(damage.value / 2);
}
const fixedDamage = new Utils.IntegerHolder(0);
applyMoveAttrs(FixedDamageAttr, source, this, move, fixedDamage);
if (!isTypeImmune && fixedDamage.value) {
damage.value = fixedDamage.value;
isCritical = false;
result = HitResult.EFFECTIVE;
}
result = result!; // telling TS compiler that result is defined!
if (!result) {
const isOneHitKo = new Utils.BooleanHolder(false);
applyMoveAttrs(OneHitKOAttr, source, this, move, isOneHitKo);
if (isOneHitKo.value) {
result = HitResult.ONE_HIT_KO;
isCritical = false;
damage.value = this.hp;
} else if (typeMultiplier >= 2) {
result = HitResult.SUPER_EFFECTIVE;
} else if (typeMultiplier >= 1) {
result = HitResult.EFFECTIVE;
} else {
result = HitResult.NOT_VERY_EFFECTIVE;
}
}
const isOneHitKo = result === HitResult.ONE_HIT_KO;
if (!fixedDamage.value && !isOneHitKo) {
if (!source.isPlayer()) {
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage);
}
if (!this.isPlayer()) {
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
}
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, false, damage);
}
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
console.log("damage", damage.value, move.name, power, sourceAtk, targetDef);
// In case of fatal damage, this tag would have gotten cleared before we could lapse it.
const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND);
if (damage.value) {
this.lapseTags(BattlerTagLapseType.HIT);
const substitute = this.getTag(SubstituteTag);
if (substitute && move.hitsSubstitute(source, this)) {
substitute.hp -= damage.value;
damage.value = 0;
}
if (this.isFullHp()) {
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
} else if (!this.isPlayer() && damage.value >= this.hp) {
this.scene.applyModifiers(EnemyEndureChanceModifier, false, this);
}
/**
* We explicitly require to ignore the faint phase here, as we want to show the messages
* about the critical hit and the super effective/not very effective messages before the faint phase.
*/
damage.value = this.damageAndUpdate(damage.value, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true);
this.turnData.damageTaken += damage.value;
if (isCritical) {
this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
}
if (source.isPlayer()) {
this.scene.validateAchvs(DamageAchv, damage);
if (damage.value > this.scene.gameData.gameStats.highestDamage) {
this.scene.gameData.gameStats.highestDamage = damage.value;
}
}
if (damage.value > 0) {
source.turnData.damageDealt += damage.value;
source.turnData.currDamageDealt = damage.value;
this.battleData.hitCount++;
const attackResult = { move: move.id, result: result as DamageResult, damage: damage.value, critical: isCritical, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() };
this.turnData.attacksReceived.unshift(attackResult);
if (source.isPlayer() && !this.isPlayer()) {
this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, damage);
}
}
}
// want to include is.Fainted() in case multi hit move ends early, still want to render message
if (source.turnData.hitsLeft === 1 || this.isFainted()) {
switch (result) {
case HitResult.SUPER_EFFECTIVE:
this.scene.queueMessage(i18next.t("battle:hitResultSuperEffective"));
break;
case HitResult.NOT_VERY_EFFECTIVE:
this.scene.queueMessage(i18next.t("battle:hitResultNotVeryEffective"));
break;
case HitResult.ONE_HIT_KO:
this.scene.queueMessage(i18next.t("battle:hitResultOneHitKO"));
break;
case HitResult.IMMUNE:
case HitResult.NO_EFFECT:
console.error("Unhandled move immunity!");
break;
}
}
if (this.isFainted()) {
// set splice index here, so future scene queues happen before FaintedPhase
this.scene.setPhaseQueueSplice();
this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), isOneHitKo));
this.destroySubstitute();
this.resetSummonData();
}
if (damage) {
destinyTag?.lapse(source, BattlerTagLapseType.CUSTOM);
if (!burnDamageReductionCancelled.value) {
burnMultiplier.value = 0.5;
}
}
break;
case MoveCategory.STATUS:
}
/** Reduces damage if this Pokemon has a relevant screen (e.g. Light Screen for special attacks) */
const screenMultiplier = new Utils.NumberHolder(1);
this.scene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, move.category, this.scene.currentBattle.double, screenMultiplier);
/**
* For each {@linkcode HitsTagAttr} the move has, doubles the damage of the move if:
* The target has a {@linkcode BattlerTagType} that this move interacts with
* AND
* The move doubles damage when used against that tag
*/
const hitsTagMultiplier = new Utils.NumberHolder(1);
move.getAttrs(HitsTagAttr).filter(hta => hta.doubleDamage).forEach(hta => {
if (this.getTag(hta.tagType)) {
hitsTagMultiplier.value *= 2;
}
});
/** Halves damage if this Pokemon is grounded in Misty Terrain against a Dragon-type attack */
const mistyTerrainMultiplier = (this.scene.arena.terrain?.terrainType === TerrainType.MISTY && this.isGrounded() && moveType === Type.DRAGON)
? 0.5
: 1;
damage.value = Utils.toDmgValue(
baseDamage
* targetMultiplier
* parentalBondMultiplier.value
* arenaAttackTypeMultiplier.value
* glaiveRushMultiplier.value
* criticalMultiplier.value
* randomMultiplier
* stabMultiplier.value
* typeMultiplier
* burnMultiplier.value
* screenMultiplier.value
* hitsTagMultiplier.value
* mistyTerrainMultiplier
);
/** Doubles damage if the attacker has Tinted Lens and is using a resisted move */
if (!ignoreSourceAbility) {
applyPreAttackAbAttrs(DamageBoostAbAttr, source, this, move, simulated, damage);
}
/** Apply the enemy's Damage and Resistance tokens */
if (!source.isPlayer()) {
this.scene.applyModifiers(EnemyDamageBoosterModifier, false, damage);
}
if (!this.isPlayer()) {
this.scene.applyModifiers(EnemyDamageReducerModifier, false, damage);
}
/** Apply this Pokemon's post-calc defensive modifiers (e.g. Fur Coat) */
if (!ignoreAbility) {
applyPreDefendAbAttrs(ReceivedMoveDamageMultiplierAbAttr, this, source, move, cancelled, simulated, damage);
}
// This attribute may modify damage arbitrarily, so be careful about changing its order of application.
applyMoveAttrs(ModifiedDamageAttr, source, this, move, damage);
if (this.isFullHp() && !ignoreAbility) {
applyPreDefendAbAttrs(PreDefendFullHpEndureAbAttr, this, source, move, cancelled, false, damage);
}
// debug message for when damage is applied (i.e. not simulated)
if (!simulated) {
console.log("damage", damage.value, move.name, power, sourceAtk, targetDef);
}
let hitResult: HitResult;
if (typeMultiplier < 1) {
hitResult = HitResult.NOT_VERY_EFFECTIVE;
} else if (typeMultiplier > 1) {
hitResult = HitResult.SUPER_EFFECTIVE;
} else {
hitResult = HitResult.EFFECTIVE;
}
return {
cancelled: cancelled.value,
result: hitResult,
damage: damage.value
};
}
/**
* Applies the results of a move to this pokemon
* @param source The {@linkcode Pokemon} using the move
* @param move The {@linkcode Move} being used
* @returns The {@linkcode HitResult} of the attack
*/
apply(source: Pokemon, move: Move): HitResult {
const defendingSide = this.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
if (move.category === MoveCategory.STATUS) {
const cancelled = new Utils.BooleanHolder(false);
const typeMultiplier = this.getMoveEffectiveness(source, move, false, false, cancelled);
if (!cancelled.value && typeMultiplier === 0) {
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) }));
}
result = (cancelled.value || typeMultiplier === 0) ? HitResult.NO_EFFECT : HitResult.STATUS;
break;
}
return (typeMultiplier === 0) ? HitResult.NO_EFFECT : HitResult.STATUS;
} else {
/** Determines whether the attack critically hits */
let isCritical: boolean;
const critOnly = new Utils.BooleanHolder(false);
const critAlways = source.getTag(BattlerTagType.ALWAYS_CRIT);
applyMoveAttrs(CritOnlyAttr, source, this, move, critOnly);
applyAbAttrs(ConditionalCritAbAttr, source, null, false, critOnly, this, move);
if (critOnly.value || critAlways) {
isCritical = true;
} else {
const critChance = [24, 8, 2, 1][Math.max(0, Math.min(this.getCritStage(source, move), 3))];
isCritical = critChance === 1 || !this.scene.randBattleSeedInt(critChance);
}
return result;
const noCritTag = this.scene.arena.getTagOnSide(NoCritTag, defendingSide);
const blockCrit = new Utils.BooleanHolder(false);
applyAbAttrs(BlockCritAbAttr, this, null, false, blockCrit);
if (noCritTag || blockCrit.value || Overrides.NEVER_CRIT_OVERRIDE) {
isCritical = false;
}
const { cancelled, result, damage: dmg } = this.getAttackDamage(source, move, false, false, isCritical, false);
const typeBoost = source.findTag(t => t instanceof TypeBoostTag && t.boostedType === source.getMoveType(move)) as TypeBoostTag;
if (typeBoost?.oneUse) {
source.removeTag(typeBoost.tagType);
}
if (cancelled || result === HitResult.IMMUNE || result === HitResult.NO_EFFECT) {
source.stopMultiHit(this);
if (!cancelled) {
if (result === HitResult.IMMUNE) {
this.scene.queueMessage(i18next.t("battle:hitResultImmune", { pokemonName: getPokemonNameWithAffix(this) }));
} else {
this.scene.queueMessage(i18next.t("battle:hitResultNoEffect", { pokemonName: getPokemonNameWithAffix(this) }));
}
}
return result;
}
// In case of fatal damage, this tag would have gotten cleared before we could lapse it.
const destinyTag = this.getTag(BattlerTagType.DESTINY_BOND);
const isOneHitKo = result === HitResult.ONE_HIT_KO;
if (dmg) {
this.lapseTags(BattlerTagLapseType.HIT);
const substitute = this.getTag(SubstituteTag);
const isBlockedBySubstitute = !!substitute && move.hitsSubstitute(source, this);
if (isBlockedBySubstitute) {
substitute.hp -= dmg;
}
if (!this.isPlayer() && dmg >= this.hp) {
this.scene.applyModifiers(EnemyEndureChanceModifier, false, this);
}
/**
* We explicitly require to ignore the faint phase here, as we want to show the messages
* about the critical hit and the super effective/not very effective messages before the faint phase.
*/
const damage = this.damageAndUpdate(isBlockedBySubstitute ? 0 : dmg, result as DamageResult, isCritical, isOneHitKo, isOneHitKo, true);
if (damage > 0) {
if (source.isPlayer()) {
this.scene.validateAchvs(DamageAchv, damage);
if (damage > this.scene.gameData.gameStats.highestDamage) {
this.scene.gameData.gameStats.highestDamage = damage;
}
}
source.turnData.damageDealt += damage;
source.turnData.currDamageDealt = damage;
this.turnData.damageTaken += damage;
this.battleData.hitCount++;
const attackResult = { move: move.id, result: result as DamageResult, damage: damage, critical: isCritical, sourceId: source.id, sourceBattlerIndex: source.getBattlerIndex() };
this.turnData.attacksReceived.unshift(attackResult);
if (source.isPlayer() && !this.isPlayer()) {
this.scene.applyModifiers(DamageMoneyRewardModifier, true, source, new Utils.NumberHolder(damage));
}
}
}
if (isCritical) {
this.scene.queueMessage(i18next.t("battle:hitResultCriticalHit"));
}
// want to include is.Fainted() in case multi hit move ends early, still want to render message
if (source.turnData.hitsLeft === 1 || this.isFainted()) {
switch (result) {
case HitResult.SUPER_EFFECTIVE:
this.scene.queueMessage(i18next.t("battle:hitResultSuperEffective"));
break;
case HitResult.NOT_VERY_EFFECTIVE:
this.scene.queueMessage(i18next.t("battle:hitResultNotVeryEffective"));
break;
case HitResult.ONE_HIT_KO:
this.scene.queueMessage(i18next.t("battle:hitResultOneHitKO"));
break;
}
}
if (this.isFainted()) {
// set splice index here, so future scene queues happen before FaintedPhase
this.scene.setPhaseQueueSplice();
this.scene.unshiftPhase(new FaintPhase(this.scene, this.getBattlerIndex(), isOneHitKo));
this.destroySubstitute();
this.resetSummonData();
}
if (dmg) {
destinyTag?.lapse(source, BattlerTagLapseType.CUSTOM);
}
return result;
}
}
/**
@ -3968,6 +4048,12 @@ export class PlayerPokemon extends Pokemon {
this.updateInfo(true).then(() => resolve());
});
};
if (preEvolution.speciesId === Species.GIMMIGHOUL) {
const evotracker = this.getHeldItems().filter(m => m instanceof EvoTrackerModifier)[0] ?? null;
if (evotracker) {
this.scene.removeModifier(evotracker);
}
}
if (!this.scene.gameMode.isDaily || this.metBiome > -1) {
this.scene.gameData.updateSpeciesDexIvs(this.species.speciesId, this.ivs);
this.scene.gameData.setPokemonSeen(this, false);
@ -4287,7 +4373,7 @@ export class EnemyPokemon extends Pokemon {
}
// Filter out any moves this Pokemon cannot use
const movePool = this.getMoveset().filter(m => m?.isUsable(this));
let movePool = this.getMoveset().filter(m => m?.isUsable(this));
// If no moves are left, use Struggle. Otherwise, continue with move selection
if (movePool.length) {
// If there's only 1 move in the move pool, use it.
@ -4308,6 +4394,39 @@ export class EnemyPokemon extends Pokemon {
return { move: moveId, targets: this.getNextTargets(moveId) };
case AiType.SMART_RANDOM:
case AiType.SMART:
/**
* Search this Pokemon's move pool for moves that will KO an opposing target.
* If there are any moves that can KO an opponent (i.e. a player Pokemon),
* those moves are the only ones considered for selection on this turn.
*/
const koMoves = movePool.filter(pkmnMove => {
if (!pkmnMove) {
return false;
}
const move = pkmnMove.getMove()!;
if (move.moveTarget === MoveTarget.ATTACKER) {
return false;
}
const fieldPokemon = this.scene.getField();
const moveTargets = getMoveTargets(this, move.id).targets
.map(ind => fieldPokemon[ind])
.filter(p => this.isPlayer() !== p.isPlayer());
// Only considers critical hits for crit-only moves or when this Pokemon is under the effect of Laser Focus
const isCritical = move.hasAttr(CritOnlyAttr) || !!this.getTag(BattlerTagType.ALWAYS_CRIT);
return move.category !== MoveCategory.STATUS
&& moveTargets.some(p => {
const doesNotFail = move.applyConditions(this, p, move) || [Moves.SUCKER_PUNCH, Moves.UPPER_HAND, Moves.THUNDERCLAP].includes(move.id);
return doesNotFail && p.getAttackDamage(this, move, !p.battleData.abilityRevealed, false, isCritical).damage >= p.hp;
});
}, this);
if (koMoves.length > 0) {
movePool = koMoves;
}
/**
* Move selection is based on the move's calculated "benefit score" against the
* best possible target(s) (as determined by {@linkcode getNextTargets}).
@ -4779,6 +4898,16 @@ export enum HitResult {
export type DamageResult = HitResult.EFFECTIVE | HitResult.SUPER_EFFECTIVE | HitResult.NOT_VERY_EFFECTIVE | HitResult.ONE_HIT_KO | HitResult.OTHER;
/** Interface containing the results of a damage calculation for a given move */
export interface DamageCalculationResult {
/** `true` if the move was cancelled (thus suppressing "No Effect" messages) */
cancelled: boolean;
/** The effectiveness of the move */
result: HitResult;
/** The damage dealt by the move */
damage: number;
}
/**
* Wrapper class for the {@linkcode Move} class for Pokemon to interact with.
* These are the moves assigned to a {@linkcode Pokemon} object.

View File

@ -240,6 +240,8 @@
"TOXIC_ORB": { "name": "Toxic Orb", "description": "It's a bizarre orb that exudes toxins when touched and will badly poison the holder during battle." },
"FLAME_ORB": { "name": "Flame Orb", "description": "It's a bizarre orb that gives off heat when touched and will affect the holder with a burn during battle." },
"EVOLUTION_TRACKER_GIMMIGHOUL": { "name": "Treasures", "description": "This Pokémon loves treasure! Keep collecting treasure and something might happen!"},
"BATON": { "name": "Baton", "description": "Allows passing along effects when switching Pokémon, which also bypasses traps." },
"SHINY_CHARM": { "name": "Shiny Charm", "description": "Dramatically increases the chance of a wild Pokémon being Shiny." },
@ -330,6 +332,21 @@
"TART_APPLE": "Tart Apple",
"STRAWBERRY_SWEET": "Strawberry Sweet",
"UNREMARKABLE_TEACUP": "Unremarkable Teacup",
"UPGRADE": "Upgrade",
"DUBIOUS_DISC": "Dubious Disc",
"DRAGON_SCALE": "Dragon Scale",
"PRISM_SCALE": "Prism Scale",
"RAZOR_CLAW": "Razor Claw",
"RAZOR_FANG": "Razor Fang",
"REAPER_CLOTH": "Reaper Cloth",
"ELECTIRIZER": "Electirizer",
"MAGMARIZER": "Magmarizer",
"PROTECTOR": "Protector",
"SACHET": "Sachet",
"WHIPPED_DREAM": "Whipped Dream",
"LEADERS_CREST": "Leader's Crest",
"SUN_FLUTE": "Sun Flute",
"MOON_FLUTE": "Moon Flute",
"CHIPPED_POT": "Chipped Pot",
"BLACK_AUGURITE": "Black Augurite",

View File

@ -13,6 +13,7 @@
"ALL": "All",
"PASS_BATON": "Pass Baton",
"UNPAUSE_EVOLUTION": "Unpause Evolution",
"PAUSE_EVOLUTION": "Pause Evolution",
"REVIVE": "Revive",
"RENAME": "Rename",
"SELECT": "Select",
@ -24,6 +25,7 @@
"tooManyItems": "{{pokemonName}} has too many\nof this item!",
"anyEffect": "It won't have any effect.",
"unpausedEvolutions": "Evolutions have been unpaused for {{pokemonName}}.",
"pausedEvolutions": "Evolutions have been paused for {{pokemonName}}.",
"unspliceConfirmation": "Do you really want to unsplice {{fusionName}}\nfrom {{pokemonName}}? {{fusionName}} will be lost.",
"wasReverted": "{{fusionName}} was reverted to {{pokemonName}}.",
"releaseConfirmation": "Do you really want to release {{pokemonName}}?",

View File

@ -51,7 +51,7 @@
"renamePokemon": "Renombrar Pokémon.",
"rename": "Renombrar",
"nickname": "Apodo",
"errorServerDown": "¡Ups! Ha habido un problema al contactar con el servidor.\n\nPuedes mantener esta ventana abierta, el juego se reconectará automáticamente.",
"errorServerDown": "¡Ups! Ha habido un problema al contactar con el servidor.\n\nPuedes mantener esta ventana abierta,\nel juego se reconectará automáticamente.",
"noSaves": "No tienes ninguna partida guardada registrada!",
"tooManySaves": "¡Tienes demasiadas partidas guardadas registradas!"
}

View File

@ -364,8 +364,8 @@
"effect": "Le lanceur creuse au premier tour et frappe au second."
},
"toxic": {
"name": "Fil Toxique",
"effect": "Tisse un fil imprégné de venin. Empoisonne la cible et baisse sa Vitesse."
"name": "Toxik",
"effect": "Le lanceur empoisonne gravement la cible. Les dégâts dus au poison augmentent à chaque tour."
},
"confusion": {
"name": "Choc Mental",

View File

@ -11,6 +11,10 @@
"expGainsSpeed": "Vit. barre dExp",
"expPartyDisplay": "Afficher Exp équipe",
"skipSeenDialogues": "Passer dialogues connus",
"eggSkip": "Animation déclosion",
"never": "Jamais",
"always": "Toujours",
"ask": "Demander",
"battleStyle": "Style de combat",
"enableRetries": "Activer les réessais",
"hideIvs": "Masquer Scanner dIV",

View File

@ -1109,11 +1109,11 @@ class EvolutionItemModifierTypeGenerator extends ModifierTypeGenerator {
}
const evolutionItemPool = [
party.filter(p => pokemonEvolutions.hasOwnProperty(p.species.speciesId)).map(p => {
party.filter(p => pokemonEvolutions.hasOwnProperty(p.species.speciesId) && (!p.pauseEvolutions || p.species.speciesId === Species.SLOWPOKE || p.species.speciesId === Species.EEVEE)).map(p => {
const evolutions = pokemonEvolutions[p.species.speciesId];
return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFormKey()) && (!e.condition || e.condition.predicate(p)));
}).flat(),
party.filter(p => p.isFusion() && p.fusionSpecies && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId)).map(p => {
party.filter(p => p.isFusion() && p.fusionSpecies && pokemonEvolutions.hasOwnProperty(p.fusionSpecies.speciesId) && (!p.pauseEvolutions || p.fusionSpecies.speciesId === Species.SLOWPOKE || p.fusionSpecies.speciesId === Species.EEVEE)).map(p => {
const evolutions = pokemonEvolutions[p.fusionSpecies!.speciesId];
return evolutions.filter(e => e.item !== EvolutionItem.NONE && (e.evoFormKey === null || (e.preFormKey || "") === p.getFusionFormKey()) && (!e.condition || e.condition.predicate(p)));
}).flat()
@ -1372,6 +1372,8 @@ export const modifierTypes = {
FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(false),
RARE_FORM_CHANGE_ITEM: () => new FormChangeItemModifierTypeGenerator(true),
EVOLUTION_TRACKER_GIMMIGHOUL: () => new PokemonHeldItemModifierType("modifierType:ModifierType.EVOLUTION_TRACKER_GIMMIGHOUL", "relic_gold", (type, _args) => new Modifiers.EvoTrackerModifier(type, (_args[0] as Pokemon).id, Species.GIMMIGHOUL, 10)),
MEGA_BRACELET: () => new ModifierType("modifierType:ModifierType.MEGA_BRACELET", "mega_bracelet", (type, _args) => new Modifiers.MegaEvolutionAccessModifier(type)),
DYNAMAX_BAND: () => new ModifierType("modifierType:ModifierType.DYNAMAX_BAND", "dynamax_band", (type, _args) => new Modifiers.GigantamaxAccessModifier(type)),
TERA_ORB: () => new ModifierType("modifierType:ModifierType.TERA_ORB", "tera_orb", (type, _args) => new Modifiers.TerastallizeAccessModifier(type)),
@ -1613,7 +1615,6 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.TEMP_STAT_STAGE_BOOSTER, 4),
new WeightedModifierType(modifierTypes.BERRY, 2),
new WeightedModifierType(modifierTypes.TM_COMMON, 2),
new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0, 1),
].map(m => {
m.setTier(ModifierTier.COMMON); return m;
}),
@ -1684,7 +1685,7 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.BASE_STAT_BOOSTER, 3),
new WeightedModifierType(modifierTypes.TERA_SHARD, 1),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 4 : 0),
new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount * 3, 0) : 0, 3),
new WeightedModifierType(modifierTypes.VOUCHER, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(1 - rerollCount, 0) : 0, 1),
].map(m => {
m.setTier(ModifierTier.GREAT); return m;
}),
@ -1765,7 +1766,7 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.RARE_FORM_CHANGE_ITEM, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 6, 24),
new WeightedModifierType(modifierTypes.MEGA_BRACELET, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36),
new WeightedModifierType(modifierTypes.DYNAMAX_BAND, (party: Pokemon[]) => Math.min(Math.ceil(party[0].scene.currentBattle.waveIndex / 50), 4) * 9, 36),
new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(9 - rerollCount * 3, 0) : 0, 9),
new WeightedModifierType(modifierTypes.VOUCHER_PLUS, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily ? Math.max(3 - rerollCount * 1, 0) : 0, 3),
].map(m => {
m.setTier(ModifierTier.ROGUE); return m;
}),
@ -1774,7 +1775,7 @@ const modifierPool: ModifierPool = {
new WeightedModifierType(modifierTypes.SHINY_CHARM, 14),
new WeightedModifierType(modifierTypes.HEALING_CHARM, 18),
new WeightedModifierType(modifierTypes.MULTI_LENS, 18),
new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(15 - rerollCount * 5, 0) : 0, 15),
new WeightedModifierType(modifierTypes.VOUCHER_PREMIUM, (party: Pokemon[], rerollCount: integer) => !party[0].scene.gameMode.isDaily && !party[0].scene.gameMode.isEndless && !party[0].scene.gameMode.isSplicedOnly ? Math.max(5 - rerollCount * 2, 0) : 0, 5),
new WeightedModifierType(modifierTypes.DNA_SPLICERS, (party: Pokemon[]) => !party[0].scene.gameMode.isSplicedOnly && party.filter(p => !p.fusionSpecies).length > 1 ? 24 : 0, 24),
new WeightedModifierType(modifierTypes.MINI_BLACK_HOLE, (party: Pokemon[]) => (!party[0].scene.gameMode.isFreshStartChallenge() && party[0].scene.gameData.unlocks[Unlockables.MINI_BLACK_HOLE]) ? 1 : 0, 1),
].map(m => {

View File

@ -29,7 +29,6 @@ import { Abilities } from "#app/enums/abilities";
import { LearnMovePhase } from "#app/phases/learn-move-phase";
import { LevelUpPhase } from "#app/phases/level-up-phase";
import { PokemonHealPhase } from "#app/phases/pokemon-heal-phase";
import { SpeciesFormKey } from "#app/data/pokemon-species";
export type ModifierPredicate = (modifier: Modifier) => boolean;
@ -841,6 +840,41 @@ export class BaseStatModifier extends PokemonHeldItemModifier {
}
}
export class EvoTrackerModifier extends PokemonHeldItemModifier {
protected species: Species;
protected required: integer;
readonly isTransferrable: boolean = false;
constructor(type: ModifierType, pokemonId: integer, species: Species, required: integer, stackCount?: integer) {
super(type, pokemonId, stackCount);
this.species = species;
this.required = required;
}
matchType(modifier: Modifier): boolean {
if (modifier instanceof EvoTrackerModifier) {
return (modifier as EvoTrackerModifier).species === this.species;
}
return false;
}
clone(): PersistentModifier {
return new EvoTrackerModifier(this.type, this.pokemonId, this.species, this.stackCount);
}
getArgs(): any[] {
return super.getArgs().concat(this.species);
}
apply(args: any[]): boolean {
return true;
}
getMaxHeldItemCount(_pokemon: Pokemon): integer {
return this.required;
}
}
/**
* Currently used by Shuckle Juice item
*/
@ -1086,7 +1120,7 @@ export class EvolutionStatBoosterModifier extends StatBoosterModifier {
* @returns true if the stat boosts can be applied, false otherwise
*/
shouldApply(args: any[]): boolean {
return super.shouldApply(args) && ((args[0] as Pokemon).getFormKey() !== SpeciesFormKey.GIGANTAMAX);
return super.shouldApply(args) && !(args[0] as Pokemon).isMax();
}
/**
@ -1273,7 +1307,7 @@ export class SpeciesCritBoosterModifier extends CritBoosterModifier {
* Applies Specific Type item boosts (e.g., Magnet)
*/
export class AttackTypeBoosterModifier extends PokemonHeldItemModifier {
private moveType: Type;
public moveType: Type;
private boostMultiplier: number;
constructor(type: ModifierType, pokemonId: integer, moveType: Type, boostPercent: number, stackCount?: integer) {
@ -2373,6 +2407,15 @@ export class MoneyRewardModifier extends ConsumableModifier {
scene.addMoney(moneyAmount.value);
scene.getParty().map(p => {
if (p.species?.speciesId === Species.GIMMIGHOUL || p.fusionSpecies?.speciesId === Species.GIMMIGHOUL) {
p.evoCounter++;
const modifierType: ModifierType = modifierTypes.EVOLUTION_TRACKER_GIMMIGHOUL();
const modifier = modifierType!.newModifier(p);
scene.addModifier(modifier);
}
});
return true;
}
}

View File

@ -102,7 +102,7 @@ export class MoveEffectPhase extends PokemonPhase {
* (and not random target) and failed the hit check against its target (MISS), log the move
* as FAILed or MISSed (depending on the conditions above) and end this phase.
*/
if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]])) {
if (!hasActiveTargets || (!move.hasAttr(VariableTargetAttr) && !move.isMultiTarget() && !targetHitChecks[this.targets[0]] && !targets[0].getTag(ProtectedTag))) {
this.stopMultiHit();
if (hasActiveTargets) {
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: this.getTarget()? getPokemonNameWithAffix(this.getTarget()!) : "" }));
@ -125,20 +125,6 @@ export class MoveEffectPhase extends PokemonPhase {
/** Has the move successfully hit a target (for damage) yet? */
let hasHit: boolean = false;
for (const target of targets) {
/**
* If the move missed a target, stop all future hits against that target
* and move on to the next target (if there is one).
*/
if (!targetHitChecks[target.getBattlerIndex()]) {
this.stopMultiHit(target);
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }));
if (moveHistoryEntry.result === MoveResult.PENDING) {
moveHistoryEntry.result = MoveResult.MISS;
}
user.pushMoveHistory(moveHistoryEntry);
applyMoveAttrs(MissEffectAttr, user, null, move);
continue;
}
/** The {@linkcode ArenaTagSide} to which the target belongs */
const targetSide = target.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY;
@ -156,6 +142,21 @@ export class MoveEffectPhase extends PokemonPhase {
&& (hasConditionalProtectApplied.value || (!target.findTags(t => t instanceof DamageProtectedTag).length && target.findTags(t => t instanceof ProtectedTag).find(t => target.lapseTag(t.tagType)))
|| (this.move.getMove().category !== MoveCategory.STATUS && target.findTags(t => t instanceof DamageProtectedTag).find(t => target.lapseTag(t.tagType))));
/**
* If the move missed a target, stop all future hits against that target
* and move on to the next target (if there is one).
*/
if (!isProtected && !targetHitChecks[target.getBattlerIndex()]) {
this.stopMultiHit(target);
this.scene.queueMessage(i18next.t("battle:attackMissed", { pokemonNameWithAffix: getPokemonNameWithAffix(target) }));
if (moveHistoryEntry.result === MoveResult.PENDING) {
moveHistoryEntry.result = MoveResult.MISS;
}
user.pushMoveHistory(moveHistoryEntry);
applyMoveAttrs(MissEffectAttr, user, null, move);
continue;
}
/** Does this phase represent the invoked move's first strike? */
const firstHit = (user.turnData.hitsLeft === user.turnData.hitCount);

View File

@ -1,4 +1,5 @@
import BattleScene from "#app/battle-scene";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
import { ExpNotification } from "#app/enums/exp-notification";
import { ExpBoosterModifier } from "#app/modifier/modifier";
import * as Utils from "#app/utils";
@ -44,7 +45,7 @@ export class ShowPartyExpBarPhase extends PlayerPartyMemberPokemonPhase {
} else {
this.end();
}
} else if (this.scene.expGainsSpeed < 3) {
} else if (this.scene.expGainsSpeed < ExpGainsSpeed.SKIP) {
this.scene.partyExpBar.showPokemonExp(pokemon, exp.value, false, newLevel).then(() => {
setTimeout(() => this.end(), 500 / Math.pow(2, this.scene.expGainsSpeed));
});

View File

@ -30,7 +30,7 @@ export class TrainerVictoryPhase extends BattlePhase {
const trainerType = this.scene.currentBattle.trainer?.config.trainerType!; // TODO: is this bang correct?
if (vouchers.hasOwnProperty(TrainerType[trainerType])) {
if (!this.scene.validateVoucher(vouchers[TrainerType[trainerType]]) && this.scene.currentBattle.trainer?.config.isBoss) {
this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType]));
this.scene.unshiftPhase(new ModifierRewardPhase(this.scene, [modifierTypes.VOUCHER, modifierTypes.VOUCHER, modifierTypes.VOUCHER_PLUS, modifierTypes.VOUCHER_PREMIUM][vouchers[TrainerType[trainerType]].voucherType]));
}
}

View File

@ -44,6 +44,7 @@ export default class PokemonData {
public pauseEvolutions: boolean;
public pokerus: boolean;
public usedTMs: Moves[];
public evoCounter: integer;
public fusionSpecies: Species;
public fusionFormIndex: integer;
@ -95,6 +96,8 @@ export default class PokemonData {
}
this.pokerus = !!source.pokerus;
this.evoCounter = source.evoCounter ?? 0;
this.fusionSpecies = sourcePokemon ? sourcePokemon.fusionSpecies?.speciesId : source.fusionSpecies;
this.fusionFormIndex = source.fusionFormIndex;
this.fusionAbilityIndex = source.fusionAbilityIndex;

View File

@ -1,14 +1,13 @@
import { DamagePhase } from "#app/phases/damage-phase";
import { toDmgValue } from "#app/utils";
import { allMoves } from "#app/data/move";
import { Abilities } from "#enums/abilities";
import { ArenaTagType } from "#enums/arena-tag-type";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
describe("Round Down and Minimun 1 test in Damage Calculation", () => {
describe("Battle Mechanics - Damage Calculation", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
@ -24,24 +23,86 @@ describe("Round Down and Minimun 1 test in Damage Calculation", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleType("single");
game.override.startingLevel(10);
game.override
.battleType("single")
.enemySpecies(Species.SNORLAX)
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH)
.startingLevel(100)
.enemyLevel(100)
.disableCrits()
.moveset([Moves.TACKLE, Moves.DRAGON_RAGE, Moves.FISSURE, Moves.JUMP_KICK]);
});
it("Tackle deals expected base damage", async () => {
await game.classicMode.startBattle([Species.CHARIZARD]);
const playerPokemon = game.scene.getPlayerPokemon()!;
vi.spyOn(playerPokemon, "getEffectiveStat").mockReturnValue(80);
const enemyPokemon = game.scene.getEnemyPokemon()!;
vi.spyOn(enemyPokemon, "getEffectiveStat").mockReturnValue(90);
// expected base damage = [(2*level/5 + 2) * power * playerATK / enemyDEF / 50] + 2
// = 31.8666...
expect(enemyPokemon.getAttackDamage(playerPokemon, allMoves[Moves.TACKLE]).damage).toBeCloseTo(31);
});
it("Attacks deal 1 damage at minimum", async () => {
game.override
.startingLevel(1)
.enemySpecies(Species.AGGRON);
await game.classicMode.startBattle([Species.MAGIKARP]);
const aggron = game.scene.getEnemyPokemon()!;
game.move.select(Moves.TACKLE);
await game.phaseInterceptor.to("BerryPhase", false);
// Lvl 1 0 Atk Magikarp Tackle vs. 0 HP / 0 Def Aggron: 1-1 (0.3 - 0.3%) -- possibly the worst move ever
expect(aggron.hp).toBe(aggron.getMaxHp() - 1);
});
it("Fixed-damage moves ignore damage multipliers", async () => {
game.override
.enemySpecies(Species.DRAGONITE)
.enemyAbility(Abilities.MULTISCALE);
await game.classicMode.startBattle([Species.MAGIKARP]);
const magikarp = game.scene.getPlayerPokemon()!;
const dragonite = game.scene.getEnemyPokemon()!;
expect(dragonite.getAttackDamage(magikarp, allMoves[Moves.DRAGON_RAGE]).damage).toBe(40);
});
it("One-hit KO moves ignore damage multipliers", async () => {
game.override
.enemySpecies(Species.AGGRON)
.enemyAbility(Abilities.MULTISCALE);
await game.classicMode.startBattle([Species.MAGIKARP]);
const magikarp = game.scene.getPlayerPokemon()!;
const aggron = game.scene.getEnemyPokemon()!;
expect(aggron.getAttackDamage(magikarp, allMoves[Moves.FISSURE]).damage).toBe(aggron.hp);
});
it("When the user fails to use Jump Kick with Wonder Guard ability, the damage should be 1.", async () => {
game.override.enemySpecies(Species.GASTLY);
game.override.enemyMoveset(Moves.SPLASH);
game.override.starterSpecies(Species.SHEDINJA);
game.override.moveset([Moves.JUMP_KICK]);
game.override.ability(Abilities.WONDER_GUARD);
game.override
.enemySpecies(Species.GASTLY)
.ability(Abilities.WONDER_GUARD);
await game.startBattle();
await game.classicMode.startBattle([Species.SHEDINJA]);
const shedinja = game.scene.getPlayerPokemon()!;
game.move.select(Moves.JUMP_KICK);
await game.phaseInterceptor.to(DamagePhase);
await game.phaseInterceptor.to("DamagePhase");
expect(shedinja.hp).toBe(shedinja.getMaxHp() - 1);
});
@ -49,21 +110,19 @@ describe("Round Down and Minimun 1 test in Damage Calculation", () => {
it("Charizard with odd HP survives Stealth Rock damage twice", async () => {
game.scene.arena.addTag(ArenaTagType.STEALTH_ROCK, 1, Moves.STEALTH_ROCK, 0);
game.override.seed("Charizard Stealth Rock test");
game.override.enemySpecies(Species.CHARIZARD);
game.override.enemyAbility(Abilities.BLAZE);
game.override.starterSpecies(Species.PIKACHU);
game.override.enemyLevel(100);
game.override
.seed("Charizard Stealth Rock test")
.enemySpecies(Species.CHARIZARD)
.enemyAbility(Abilities.BLAZE);
await game.startBattle();
await game.classicMode.startBattle([Species.PIKACHU]);
const charizard = game.scene.getEnemyPokemon()!;
const maxHp = charizard.getMaxHp();
const damage_prediction = toDmgValue(charizard.getMaxHp() / 2);
const currentHp = charizard.hp;
const expectedHP = maxHp - damage_prediction;
expect(currentHp).toBe(expectedHP);
if (charizard.getMaxHp() % 2 === 1) {
expect(charizard.hp).toBeGreaterThan(charizard.getMaxHp() / 2);
} else {
expect(charizard.hp).toBe(charizard.getMaxHp() / 2);
}
});
});

View File

@ -0,0 +1,106 @@
import { allMoves, MoveCategory } from "#app/data/move";
import { Abilities } from "#app/enums/abilities";
import { Moves } from "#app/enums/moves";
import { Species } from "#app/enums/species";
import { AiType, EnemyPokemon } from "#app/field/pokemon";
import { randSeedInt } from "#app/utils";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
const TIMEOUT = 20 * 1000;
const NUM_TRIALS = 300;
type MoveChoiceSet = { [key: number]: number };
function getEnemyMoveChoices(pokemon: EnemyPokemon, moveChoices: MoveChoiceSet): void {
// Use an unseeded random number generator in place of the mocked-out randBattleSeedInt
vi.spyOn(pokemon.scene, "randBattleSeedInt").mockImplementation((range, min?) => {
return randSeedInt(range, min);
});
for (let i = 0; i < NUM_TRIALS; i++) {
const queuedMove = pokemon.getNextMove();
moveChoices[queuedMove.move]++;
}
for (const [moveId, count] of Object.entries(moveChoices)) {
console.log(`Move: ${allMoves[moveId].name} Count: ${count} (${count / NUM_TRIALS * 100}%)`);
}
}
describe("Enemy Commands - Move Selection", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.ability(Abilities.BALL_FETCH)
.enemyAbility(Abilities.BALL_FETCH);
});
it(
"should never use Status moves if an attack can KO",
async () => {
game.override
.enemySpecies(Species.ETERNATUS)
.enemyMoveset([Moves.ETERNABEAM, Moves.SLUDGE_BOMB, Moves.DRAGON_DANCE, Moves.COSMIC_POWER])
.startingLevel(1)
.enemyLevel(100);
await game.classicMode.startBattle([Species.MAGIKARP]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
enemyPokemon.aiType = AiType.SMART_RANDOM;
const moveChoices: MoveChoiceSet = {};
const enemyMoveset = enemyPokemon.getMoveset();
enemyMoveset.forEach(mv => moveChoices[mv!.moveId] = 0);
getEnemyMoveChoices(enemyPokemon, moveChoices);
enemyMoveset.forEach(mv => {
if (mv?.getMove().category === MoveCategory.STATUS) {
expect(moveChoices[mv.moveId]).toBe(0);
}
});
}, TIMEOUT
);
it(
"should not select Last Resort if it would fail, even if the move KOs otherwise",
async () => {
game.override
.enemySpecies(Species.KANGASKHAN)
.enemyMoveset([Moves.LAST_RESORT, Moves.GIGA_IMPACT, Moves.SPLASH, Moves.SWORDS_DANCE])
.startingLevel(1)
.enemyLevel(100);
await game.classicMode.startBattle([Species.MAGIKARP]);
const enemyPokemon = game.scene.getEnemyPokemon()!;
enemyPokemon.aiType = AiType.SMART_RANDOM;
const moveChoices: MoveChoiceSet = {};
const enemyMoveset = enemyPokemon.getMoveset();
enemyMoveset.forEach(mv => moveChoices[mv!.moveId] = 0);
getEnemyMoveChoices(enemyPokemon, moveChoices);
enemyMoveset.forEach(mv => {
if (mv?.getMove().category === MoveCategory.STATUS || mv?.moveId === Moves.LAST_RESORT) {
expect(moveChoices[mv.moveId]).toBe(0);
}
});
}, TIMEOUT
);
});

View File

@ -0,0 +1,93 @@
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest";
import GameManager from "../utils/gameManager";
import { Species } from "#enums/species";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import { BattlerIndex } from "#app/battle";
import { StatusEffect } from "#app/enums/status-effect";
const TIMEOUT = 20 * 1000;
describe("Moves - Baneful Bunker", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override.battleType("single");
game.override.moveset(Moves.SLASH);
game.override.enemySpecies(Species.SNORLAX);
game.override.enemyAbility(Abilities.INSOMNIA);
game.override.enemyMoveset(Moves.BANEFUL_BUNKER);
game.override.startingLevel(100);
game.override.enemyLevel(100);
});
test(
"should protect the user and poison attackers that make contact",
async () => {
await game.classicMode.startBattle([Species.CHARIZARD]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.SLASH);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeTruthy();
}, TIMEOUT
);
test(
"should protect the user and poison attackers that make contact, regardless of accuracy checks",
async () => {
await game.classicMode.startBattle([Species.CHARIZARD]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.SLASH);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("MoveEffectPhase");
await game.move.forceMiss();
await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeTruthy();
}, TIMEOUT
);
test(
"should not poison attackers that don't make contact",
async () => {
game.override.moveset(Moves.FLASH_CANNON);
await game.classicMode.startBattle([Species.CHARIZARD]);
const leadPokemon = game.scene.getPlayerPokemon()!;
const enemyPokemon = game.scene.getEnemyPokemon()!;
game.move.select(Moves.FLASH_CANNON);
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
await game.phaseInterceptor.to("MoveEffectPhase");
await game.move.forceMiss();
await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
expect(leadPokemon.status?.effect === StatusEffect.POISON).toBeFalsy();
}, TIMEOUT
);
});

View File

@ -43,6 +43,23 @@ describe("Moves - Obstruct", () => {
expect(enemy.getStatStage(Stat.DEF)).toBe(-2);
}, TIMEOUT);
it("bypasses accuracy checks when applying protection and defense reduction", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.ICE_PUNCH));
await game.classicMode.startBattle();
game.move.select(Moves.OBSTRUCT);
await game.phaseInterceptor.to("MoveEffectPhase");
await game.move.forceMiss();
const player = game.scene.getPlayerPokemon()!;
const enemy = game.scene.getEnemyPokemon()!;
await game.phaseInterceptor.to("TurnEndPhase");
expect(player.isFullHp()).toBe(true);
expect(enemy.getStatStage(Stat.DEF)).toBe(-2);
}, TIMEOUT
);
it("protects from non-contact damaging moves and doesn't lower the opponent's defense by 2 stages", async () => {
game.override.enemyMoveset(Array(4).fill(Moves.WATER_GUN));
await game.classicMode.startBattle();

View File

@ -3,7 +3,6 @@ import * as MysteryEncounters from "#app/data/mystery-encounters/mystery-encount
import * as EncounterPhaseUtils from "#app/data/mystery-encounters/utils/encounter-phase-utils";
import { getPokemonSpecies } from "#app/data/pokemon-species";
import { Biome } from "#app/enums/biome";
import { Moves } from "#app/enums/moves";
import { MysteryEncounterType } from "#app/enums/mystery-encounter-type";
import { Species } from "#app/enums/species";
import GameManager from "#app/test/utils/gameManager";
@ -16,6 +15,7 @@ import BattleScene from "#app/battle-scene";
import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
import { PartyExpPhase } from "#app/phases/party-exp-phase";
const namespace = "mysteryEncounter:lostAtSea";
/** Blastoise for surf. Pidgeot for fly. Abra for none. */
const defaultParty = [Species.BLASTOISE, Species.PIDGEOT, Species.ABRA];
@ -102,8 +102,8 @@ describe("Lost at Sea - Mystery Encounter", () => {
const onInitResult = onInit!(scene);
expect(LostAtSeaEncounter.dialogueTokens?.damagePercentage).toBe("25");
expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe(Moves[Moves.SURF]);
expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe(Moves[Moves.FLY]);
expect(LostAtSeaEncounter.dialogueTokens?.option1RequiredMove).toBe("Surf");
expect(LostAtSeaEncounter.dialogueTokens?.option2RequiredMove).toBe("Fly");
expect(onInitResult).toBe(true);
});

View File

@ -107,7 +107,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
expect(onInitResult).toBe(true);
});
describe("Option 1 - Fight", () => {
describe.skip("Option 1 - Fight", () => {
it("should have the correct properties", () => {
const option = UncommonBreedEncounter.options[0];
expect(option.optionMode).toBe(MysteryEncounterOptionMode.DEFAULT);

View File

@ -0,0 +1,55 @@
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
import { Species } from "#app/enums/species";
import { ExpPhase } from "#app/phases/exp-phase";
import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves";
import GameManager from "#test/utils/gameManager";
import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
vi.mock("../data/exp", ({}) => {
return {
getLevelRelExp: vi.fn(() => 1), //consistent levelRelExp
};
});
describe("UI - Battle Info", () => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.GUILLOTINE, Moves.SPLASH])
.battleType("single")
.enemyAbility(Abilities.BALL_FETCH)
.enemyMoveset(Moves.SPLASH)
.enemySpecies(Species.CATERPIE);
});
it.each([ExpGainsSpeed.FAST, ExpGainsSpeed.FASTER, ExpGainsSpeed.SKIP])(
"should increase exp gains animation by 2^%i",
async (expGainsSpeed) => {
game.settings.expGainsSpeed(expGainsSpeed);
vi.spyOn(Math, "pow");
await game.classicMode.startBattle([Species.CHARIZARD]);
game.move.select(Moves.SPLASH);
await game.doKillOpponents();
await game.phaseInterceptor.to(ExpPhase, true);
expect(Math.pow).not.toHaveBeenCalledWith(2, expGainsSpeed);
}
);
});

View File

@ -54,6 +54,7 @@ import { MysteryEncounterPhase } from "#app/phases/mystery-encounter-phases";
import { expect } from "vitest";
import { MysteryEncounterType } from "#enums/mystery-encounter-type";
import { isNullOrUndefined } from "#app/utils";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
/**
* Class to manage the game state and transitions between phases.
@ -148,7 +149,7 @@ export default class GameManager {
this.scene.gameSpeed = 5;
this.scene.moveAnimations = false;
this.scene.showLevelUpStats = false;
this.scene.expGainsSpeed = 3;
this.scene.expGainsSpeed = ExpGainsSpeed.SKIP;
this.scene.expParty = ExpNotification.SKIP;
this.scene.hpBarSpeed = 3;
this.scene.enableTutorials = false;

View File

@ -1,6 +1,7 @@
import { PlayerGender } from "#app/enums/player-gender";
import { BattleStyle } from "#app/enums/battle-style";
import { GameManagerHelper } from "./gameManagerHelper";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
/**
* Helper to handle settings for tests
@ -38,6 +39,15 @@ export class SettingsHelper extends GameManagerHelper {
this.log(`Gender set to: ${PlayerGender[gender]} (=${gender})` );
}
/**
* Change the exp gains speed
* @param speed the {@linkcode ExpGainsSpeed} to set
*/
expGainsSpeed(speed: ExpGainsSpeed) {
this.game.scene.expGainsSpeed = speed;
this.log(`Exp Gains Speed set to: ${ExpGainsSpeed[speed]} (=${speed})` );
}
private log(...params: any[]) {
console.log("Settings:", ...params);
}

View File

@ -60,6 +60,7 @@ export interface PromptHandler {
expireFn?: () => void;
awaitingActionInput?: boolean;
}
import { ExpPhase } from "#app/phases/exp-phase";
export default class PhaseInterceptor {
public scene;
@ -127,7 +128,8 @@ export default class PhaseInterceptor {
[MysteryEncounterRewardsPhase, this.startPhase],
[PostMysteryEncounterPhase, this.startPhase],
[ModifierRewardPhase, this.startPhase],
[PartyExpPhase, this.startPhase]
[PartyExpPhase, this.startPhase],
[ExpPhase, this.startPhase],
];
private endBySetMode = [

View File

@ -11,8 +11,11 @@ import { Stat } from "#enums/stat";
import BattleFlyout from "./battle-flyout";
import { WindowVariant, addWindow } from "./ui-theme";
import i18next from "i18next";
import { ExpGainsSpeed } from "#app/enums/exp-gains-speed";
export default class BattleInfo extends Phaser.GameObjects.Container {
public static readonly EXP_GAINS_DURATION_BASE = 1650;
private baseY: number;
private player: boolean;
@ -702,7 +705,11 @@ export default class BattleInfo extends Phaser.GameObjects.Container {
instant = true;
}
const durationMultiplier = Phaser.Tweens.Builders.GetEaseFunction("Sine.easeIn")(1 - (Math.max(this.lastLevel - 100, 0) / 150));
const duration = this.visible && !instant ? (((levelExp - this.lastLevelExp) / relLevelExp) * 1650) * durationMultiplier * levelDurationMultiplier : 0;
let duration = this.visible && !instant ? (((levelExp - this.lastLevelExp) / relLevelExp) * BattleInfo.EXP_GAINS_DURATION_BASE) * durationMultiplier * levelDurationMultiplier : 0;
const speed = (this.scene as BattleScene).expGainsSpeed;
if (speed && speed >= ExpGainsSpeed.DEFAULT) {
duration = speed >= ExpGainsSpeed.SKIP ? ExpGainsSpeed.DEFAULT : duration / Math.pow(2, speed);
}
if (ratio === 1) {
this.lastLevelExp = 0;
this.lastLevel++;

View File

@ -467,8 +467,8 @@ export default class PartyUiHandler extends MessageUiHandler {
} else if (option === PartyOption.UNPAUSE_EVOLUTION) {
this.clearOptions();
ui.playSelect();
pokemon.pauseEvolutions = false;
this.showText(i18next.t("partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon) }), undefined, () => this.showText("", 0), null, true);
pokemon.pauseEvolutions = !pokemon.pauseEvolutions;
this.showText(i18next.t(pokemon.pauseEvolutions? "partyUiHandler:pausedEvolutions" : "partyUiHandler:unpausedEvolutions", { pokemonName: getPokemonNameWithAffix(pokemon) }), undefined, () => this.showText("", 0), null, true);
} else if (option === PartyOption.UNSPLICE) {
this.clearOptions();
ui.playSelect();
@ -889,7 +889,7 @@ export default class PartyUiHandler extends MessageUiHandler {
this.options.push(PartyOption.SUMMARY);
this.options.push(PartyOption.RENAME);
if (pokemon.pauseEvolutions && (pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) || (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId)))) {
if ((pokemonEvolutions.hasOwnProperty(pokemon.species.speciesId) || (pokemon.isFusion() && pokemon.fusionSpecies && pokemonEvolutions.hasOwnProperty(pokemon.fusionSpecies.speciesId)))) {
this.options.push(PartyOption.UNPAUSE_EVOLUTION);
}
@ -976,6 +976,8 @@ export default class PartyUiHandler extends MessageUiHandler {
if (formChangeItemModifiers && option >= PartyOption.FORM_CHANGE_ITEM) {
const modifier = formChangeItemModifiers[option - PartyOption.FORM_CHANGE_ITEM];
optionName = `${modifier.active ? i18next.t("partyUiHandler:DEACTIVATE") : i18next.t("partyUiHandler:ACTIVATE")} ${modifier.type.name}`;
} else if (option === PartyOption.UNPAUSE_EVOLUTION) {
optionName = `${pokemon.pauseEvolutions ? i18next.t("partyUiHandler:UNPAUSE_EVOLUTION") : i18next.t("partyUiHandler:PAUSE_EVOLUTION")}`;
} else {
if (this.localizedOptions.includes(option)) {
optionName = i18next.t(`partyUiHandler:${PartyOption[option]}`);