diff --git a/package-lock.json b/package-lock.json index 9531244a42d..676539af79e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "pokemon-rogue-battle", - "version": "1.5.0", + "version": "1.5.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "pokemon-rogue-battle", - "version": "1.5.0", + "version": "1.5.2", "hasInstallScript": true, "dependencies": { "@material/material-color-utilities": "^0.2.7", diff --git a/package.json b/package.json index 9d8adb8c1c0..81f01e1d482 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "pokemon-rogue-battle", "private": true, - "version": "1.5.0", + "version": "1.5.2", "type": "module", "scripts": { "start": "vite", diff --git a/public/audio/bgm/title.mp3 b/public/audio/bgm/title.mp3 index 86d4be0da35..8ad451a52a9 100644 Binary files a/public/audio/bgm/title.mp3 and b/public/audio/bgm/title.mp3 differ diff --git a/public/images/events/yearofthesnakeevent-de.png b/public/images/events/yearofthesnakeevent-de.png new file mode 100644 index 00000000000..f5e684bbcd5 Binary files /dev/null and b/public/images/events/yearofthesnakeevent-de.png differ diff --git a/public/images/events/yearofthesnakeevent-en.png b/public/images/events/yearofthesnakeevent-en.png new file mode 100644 index 00000000000..ddd34023b94 Binary files /dev/null and b/public/images/events/yearofthesnakeevent-en.png differ diff --git a/public/images/events/yearofthesnakeevent-es-ES.png b/public/images/events/yearofthesnakeevent-es-ES.png new file mode 100644 index 00000000000..76474c475c5 Binary files /dev/null and b/public/images/events/yearofthesnakeevent-es-ES.png differ diff --git a/public/images/events/yearofthesnakeevent-fr.png b/public/images/events/yearofthesnakeevent-fr.png new file mode 100644 index 00000000000..88ad3e770ca Binary files /dev/null and b/public/images/events/yearofthesnakeevent-fr.png differ diff --git a/public/images/events/yearofthesnakeevent-it.png b/public/images/events/yearofthesnakeevent-it.png new file mode 100644 index 00000000000..a02e7048d2a Binary files /dev/null and b/public/images/events/yearofthesnakeevent-it.png differ diff --git a/public/images/events/yearofthesnakeevent-ja.png b/public/images/events/yearofthesnakeevent-ja.png new file mode 100644 index 00000000000..ba13b5d281f Binary files /dev/null and b/public/images/events/yearofthesnakeevent-ja.png differ diff --git a/public/images/events/yearofthesnakeevent-ko.png b/public/images/events/yearofthesnakeevent-ko.png new file mode 100644 index 00000000000..dca6bcbbd15 Binary files /dev/null and b/public/images/events/yearofthesnakeevent-ko.png differ diff --git a/public/images/events/yearofthesnakeevent-pt-BR.png b/public/images/events/yearofthesnakeevent-pt-BR.png new file mode 100644 index 00000000000..a535aec2aa5 Binary files /dev/null and b/public/images/events/yearofthesnakeevent-pt-BR.png differ diff --git a/public/images/events/yearofthesnakeevent-zh-CN.png b/public/images/events/yearofthesnakeevent-zh-CN.png new file mode 100644 index 00000000000..3ab4fc565a7 Binary files /dev/null and b/public/images/events/yearofthesnakeevent-zh-CN.png differ diff --git a/src/battle-scene.ts b/src/battle-scene.ts index f0aeb68e277..d042d2c0b12 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -869,6 +869,12 @@ export default class BattleScene extends SceneBase { return party.slice(0, Math.min(party.length, this.currentBattle?.double ? 2 : 1)); } + /** + * Returns an array of Pokemon on both sides of the battle - player first, then enemy. + * Does not actually check if the pokemon are on the field or not, and always has length 4 regardless of battle type. + * @param activeOnly Whether to consider only active pokemon + * @returns array of {@linkcode Pokemon} + */ public getField(activeOnly: boolean = false): Pokemon[] { const ret = new Array(4).fill(null); const playerField = this.getPlayerField(); @@ -2084,8 +2090,11 @@ export default class BattleScene extends SceneBase { return sound; } + /** The loop point of any given battle, mystery encounter, or title track, read as seconds and milliseconds. */ getBgmLoopPoint(bgmName: string): number { switch (bgmName) { + case "title": //Firel PokéRogue Title + return 46.500; case "battle_kanto_champion": //B2W2 Kanto Champion Battle return 13.950; case "battle_johto_champion": //B2W2 Johto Champion Battle diff --git a/src/data/balance/passives.ts b/src/data/balance/passives.ts index 3a8285b4586..60b50c7909c 100644 --- a/src/data/balance/passives.ts +++ b/src/data/balance/passives.ts @@ -2,581 +2,581 @@ import { Abilities } from "#app/enums/abilities"; import { Species } from "#app/enums/species"; export const starterPassiveAbilities = { - [Species.BULBASAUR]: Abilities.GRASSY_SURGE, - [Species.CHARMANDER]: Abilities.BEAST_BOOST, - [Species.SQUIRTLE]: Abilities.STURDY, - [Species.CATERPIE]: Abilities.MAGICIAN, - [Species.WEEDLE]: Abilities.TINTED_LENS, - [Species.PIDGEY]: Abilities.SHEER_FORCE, - [Species.RATTATA]: Abilities.STRONG_JAW, - [Species.SPEAROW]: Abilities.MOXIE, - [Species.EKANS]: Abilities.REGENERATOR, - [Species.SANDSHREW]: Abilities.TOUGH_CLAWS, - [Species.NIDORAN_F]: Abilities.FLARE_BOOST, - [Species.NIDORAN_M]: Abilities.GUTS, - [Species.VULPIX]: Abilities.FUR_COAT, - [Species.ZUBAT]: Abilities.INTIMIDATE, - [Species.ODDISH]: Abilities.TRIAGE, - [Species.PARAS]: Abilities.TRIAGE, - [Species.VENONAT]: Abilities.SIMPLE, - [Species.DIGLETT]: Abilities.STURDY, - [Species.MEOWTH]: Abilities.TOUGH_CLAWS, - [Species.PSYDUCK]: Abilities.SIMPLE, - [Species.MANKEY]: Abilities.IRON_FIST, - [Species.GROWLITHE]: Abilities.FLUFFY, - [Species.POLIWAG]: Abilities.NO_GUARD, - [Species.ABRA]: Abilities.MAGICIAN, - [Species.MACHOP]: Abilities.QUICK_FEET, - [Species.BELLSPROUT]: Abilities.FLOWER_GIFT, - [Species.TENTACOOL]: Abilities.TOXIC_CHAIN, - [Species.GEODUDE]: Abilities.DRY_SKIN, - [Species.PONYTA]: Abilities.MAGIC_GUARD, - [Species.SLOWPOKE]: Abilities.UNAWARE, - [Species.MAGNEMITE]: Abilities.LEVITATE, - [Species.FARFETCHD]: Abilities.SNIPER, - [Species.DODUO]: Abilities.PARENTAL_BOND, - [Species.SEEL]: Abilities.WATER_BUBBLE, - [Species.GRIMER]: Abilities.WATER_ABSORB, - [Species.SHELLDER]: Abilities.ICE_SCALES, - [Species.GASTLY]: Abilities.SHADOW_SHIELD, - [Species.ONIX]: Abilities.ROCKY_PAYLOAD, - [Species.DROWZEE]: Abilities.MAGICIAN, - [Species.KRABBY]: Abilities.UNBURDEN, - [Species.VOLTORB]: Abilities.TRANSISTOR, - [Species.EXEGGCUTE]: Abilities.RIPEN, - [Species.CUBONE]: Abilities.PARENTAL_BOND, - [Species.LICKITUNG]: Abilities.CHEEK_POUCH, - [Species.KOFFING]: Abilities.PARENTAL_BOND, - [Species.RHYHORN]: Abilities.FILTER, - [Species.TANGELA]: Abilities.SEED_SOWER, - [Species.KANGASKHAN]: Abilities.GUTS, - [Species.HORSEA]: Abilities.DRAGONS_MAW, - [Species.GOLDEEN]: Abilities.MULTISCALE, - [Species.STARYU]: Abilities.REGENERATOR, - [Species.SCYTHER]: Abilities.TINTED_LENS, - [Species.PINSIR]: Abilities.TINTED_LENS, - [Species.TAUROS]: Abilities.STAMINA, - [Species.MAGIKARP]: Abilities.MULTISCALE, - [Species.LAPRAS]: Abilities.LIGHTNING_ROD, - [Species.DITTO]: Abilities.ADAPTABILITY, - [Species.EEVEE]: Abilities.PICKUP, - [Species.PORYGON]: Abilities.PROTEAN, - [Species.OMANYTE]: Abilities.STURDY, - [Species.KABUTO]: Abilities.TOUGH_CLAWS, - [Species.AERODACTYL]: Abilities.ORICHALCUM_PULSE, - [Species.ARTICUNO]: Abilities.SNOW_WARNING, - [Species.ZAPDOS]: Abilities.DRIZZLE, - [Species.MOLTRES]: Abilities.DROUGHT, - [Species.DRATINI]: Abilities.AERILATE, - [Species.MEWTWO]: Abilities.NEUROFORCE, - [Species.MEW]: Abilities.PROTEAN, + [Species.BULBASAUR]: { 0: Abilities.GRASSY_SURGE }, + [Species.CHARMANDER]: { 0: Abilities.BEAST_BOOST }, + [Species.SQUIRTLE]: { 0: Abilities.STURDY }, + [Species.CATERPIE]: { 0: Abilities.MAGICIAN }, + [Species.WEEDLE]: { 0: Abilities.TINTED_LENS }, + [Species.PIDGEY]: { 0: Abilities.SHEER_FORCE }, + [Species.RATTATA]: { 0: Abilities.STRONG_JAW }, + [Species.SPEAROW]: { 0: Abilities.MOXIE }, + [Species.EKANS]: { 0: Abilities.REGENERATOR }, + [Species.SANDSHREW]: { 0: Abilities.TOUGH_CLAWS }, + [Species.NIDORAN_F]: { 0: Abilities.FLARE_BOOST }, + [Species.NIDORAN_M]: { 0: Abilities.GUTS }, + [Species.VULPIX]: { 0: Abilities.FUR_COAT }, + [Species.ZUBAT]: { 0: Abilities.INTIMIDATE }, + [Species.ODDISH]: { 0: Abilities.TRIAGE }, + [Species.PARAS]: { 0: Abilities.TRIAGE }, + [Species.VENONAT]: { 0: Abilities.SIMPLE }, + [Species.DIGLETT]: { 0: Abilities.STURDY }, + [Species.MEOWTH]: { 0: Abilities.TOUGH_CLAWS }, + [Species.PSYDUCK]: { 0: Abilities.SIMPLE }, + [Species.MANKEY]: { 0: Abilities.IRON_FIST }, + [Species.GROWLITHE]: { 0: Abilities.FLUFFY }, + [Species.POLIWAG]: { 0: Abilities.NO_GUARD }, + [Species.ABRA]: { 0: Abilities.MAGICIAN }, + [Species.MACHOP]: { 0: Abilities.QUICK_FEET }, + [Species.BELLSPROUT]: { 0: Abilities.FLOWER_GIFT }, + [Species.TENTACOOL]: { 0: Abilities.TOXIC_CHAIN }, + [Species.GEODUDE]: { 0: Abilities.DRY_SKIN }, + [Species.PONYTA]: { 0: Abilities.MAGIC_GUARD }, + [Species.SLOWPOKE]: { 0: Abilities.UNAWARE }, + [Species.MAGNEMITE]: { 0: Abilities.LEVITATE }, + [Species.FARFETCHD]: { 0: Abilities.SNIPER }, + [Species.DODUO]: { 0: Abilities.PARENTAL_BOND }, + [Species.SEEL]: { 0: Abilities.WATER_BUBBLE }, + [Species.GRIMER]: { 0: Abilities.WATER_ABSORB }, + [Species.SHELLDER]: { 0: Abilities.ICE_SCALES }, + [Species.GASTLY]: { 0: Abilities.SHADOW_SHIELD }, + [Species.ONIX]: { 0: Abilities.ROCKY_PAYLOAD }, + [Species.DROWZEE]: { 0: Abilities.MAGICIAN }, + [Species.KRABBY]: { 0: Abilities.UNBURDEN }, + [Species.VOLTORB]: { 0: Abilities.TRANSISTOR }, + [Species.EXEGGCUTE]: { 0: Abilities.RIPEN }, + [Species.CUBONE]: { 0: Abilities.PARENTAL_BOND }, + [Species.LICKITUNG]: { 0: Abilities.CHEEK_POUCH }, + [Species.KOFFING]: { 0: Abilities.PARENTAL_BOND }, + [Species.RHYHORN]: { 0: Abilities.FILTER }, + [Species.TANGELA]: { 0: Abilities.SEED_SOWER }, + [Species.KANGASKHAN]: { 0: Abilities.GUTS }, + [Species.HORSEA]: { 0: Abilities.DRAGONS_MAW }, + [Species.GOLDEEN]: { 0: Abilities.MULTISCALE }, + [Species.STARYU]: { 0: Abilities.REGENERATOR }, + [Species.SCYTHER]: { 0: Abilities.TINTED_LENS }, + [Species.PINSIR]: { 0: Abilities.TINTED_LENS }, + [Species.TAUROS]: { 0: Abilities.STAMINA }, + [Species.MAGIKARP]: { 0: Abilities.MULTISCALE }, + [Species.LAPRAS]: { 0: Abilities.LIGHTNING_ROD }, + [Species.DITTO]: { 0: Abilities.ADAPTABILITY }, + [Species.EEVEE]: { 0: Abilities.PICKUP }, + [Species.PORYGON]: { 0: Abilities.PROTEAN }, + [Species.OMANYTE]: { 0: Abilities.STURDY }, + [Species.KABUTO]: { 0: Abilities.TOUGH_CLAWS }, + [Species.AERODACTYL]: { 0: Abilities.ORICHALCUM_PULSE }, + [Species.ARTICUNO]: { 0: Abilities.SNOW_WARNING }, + [Species.ZAPDOS]: { 0: Abilities.DRIZZLE }, + [Species.MOLTRES]: { 0: Abilities.DROUGHT }, + [Species.DRATINI]: { 0: Abilities.AERILATE }, + [Species.MEWTWO]: { 0: Abilities.NEUROFORCE }, + [Species.MEW]: { 0: Abilities.PROTEAN }, - [Species.CHIKORITA]: Abilities.THICK_FAT, - [Species.CYNDAQUIL]: Abilities.DROUGHT, - [Species.TOTODILE]: Abilities.TOUGH_CLAWS, - [Species.SENTRET]: Abilities.PICKUP, - [Species.HOOTHOOT]: Abilities.AERILATE, - [Species.LEDYBA]: Abilities.PRANKSTER, - [Species.SPINARAK]: Abilities.PRANKSTER, - [Species.CHINCHOU]: Abilities.REGENERATOR, - [Species.PICHU]: Abilities.ELECTRIC_SURGE, - [Species.CLEFFA]: Abilities.ANALYTIC, - [Species.IGGLYBUFF]: Abilities.HUGE_POWER, - [Species.TOGEPI]: Abilities.PIXILATE, - [Species.NATU]: Abilities.SHEER_FORCE, - [Species.MAREEP]: Abilities.ELECTROMORPHOSIS, - [Species.HOPPIP]: Abilities.FLUFFY, - [Species.AIPOM]: Abilities.SCRAPPY, - [Species.SUNKERN]: Abilities.DROUGHT, - [Species.YANMA]: Abilities.SHEER_FORCE, - [Species.WOOPER]: Abilities.COMATOSE, - [Species.MURKROW]: Abilities.DARK_AURA, - [Species.MISDREAVUS]: Abilities.BEADS_OF_RUIN, - [Species.UNOWN]: Abilities.PICKUP, - [Species.GIRAFARIG]: Abilities.PARENTAL_BOND, - [Species.PINECO]: Abilities.IRON_BARBS, - [Species.DUNSPARCE]: Abilities.UNAWARE, - [Species.GLIGAR]: Abilities.TOXIC_BOOST, - [Species.SNUBBULL]: Abilities.PIXILATE, - [Species.QWILFISH]: Abilities.TOXIC_DEBRIS, - [Species.SHUCKLE]: Abilities.HARVEST, - [Species.HERACROSS]: Abilities.TECHNICIAN, - [Species.SNEASEL]: Abilities.TOUGH_CLAWS, - [Species.TEDDIURSA]: Abilities.THICK_FAT, - [Species.SLUGMA]: Abilities.DESOLATE_LAND, - [Species.SWINUB]: Abilities.SLUSH_RUSH, - [Species.CORSOLA]: Abilities.STORM_DRAIN, - [Species.REMORAID]: Abilities.SIMPLE, - [Species.DELIBIRD]: Abilities.HUGE_POWER, - [Species.SKARMORY]: Abilities.LIGHTNING_ROD, - [Species.HOUNDOUR]: Abilities.LIGHTNING_ROD, - [Species.PHANPY]: Abilities.SPEED_BOOST, - [Species.STANTLER]: Abilities.SPEED_BOOST, - [Species.SMEARGLE]: Abilities.PRANKSTER, - [Species.TYROGUE]: Abilities.MOXIE, - [Species.SMOOCHUM]: Abilities.PSYCHIC_SURGE, - [Species.ELEKID]: Abilities.SHEER_FORCE, - [Species.MAGBY]: Abilities.SHEER_FORCE, - [Species.MILTANK]: Abilities.STAMINA, - [Species.RAIKOU]: Abilities.BEAST_BOOST, - [Species.ENTEI]: Abilities.BEAST_BOOST, - [Species.SUICUNE]: Abilities.BEAST_BOOST, - [Species.LARVITAR]: Abilities.SAND_RUSH, - [Species.LUGIA]: Abilities.DELTA_STREAM, - [Species.HO_OH]: Abilities.MAGIC_GUARD, - [Species.CELEBI]: Abilities.PSYCHIC_SURGE, + [Species.CHIKORITA]: { 0: Abilities.THICK_FAT }, + [Species.CYNDAQUIL]: { 0: Abilities.DROUGHT }, + [Species.TOTODILE]: { 0: Abilities.TOUGH_CLAWS }, + [Species.SENTRET]: { 0: Abilities.PICKUP }, + [Species.HOOTHOOT]: { 0: Abilities.AERILATE }, + [Species.LEDYBA]: { 0: Abilities.PRANKSTER }, + [Species.SPINARAK]: { 0: Abilities.PRANKSTER }, + [Species.CHINCHOU]: { 0: Abilities.REGENERATOR }, + [Species.PICHU]: { 0: Abilities.ELECTRIC_SURGE }, + [Species.CLEFFA]: { 0: Abilities.ANALYTIC }, + [Species.IGGLYBUFF]: { 0: Abilities.HUGE_POWER }, + [Species.TOGEPI]: { 0: Abilities.PIXILATE }, + [Species.NATU]: { 0: Abilities.SHEER_FORCE }, + [Species.MAREEP]: { 0: Abilities.ELECTROMORPHOSIS }, + [Species.HOPPIP]: { 0: Abilities.FLUFFY }, + [Species.AIPOM]: { 0: Abilities.SCRAPPY }, + [Species.SUNKERN]: { 0: Abilities.DROUGHT }, + [Species.YANMA]: { 0: Abilities.SHEER_FORCE }, + [Species.WOOPER]: { 0: Abilities.COMATOSE }, + [Species.MURKROW]: { 0: Abilities.DARK_AURA }, + [Species.MISDREAVUS]: { 0: Abilities.BEADS_OF_RUIN }, + [Species.UNOWN]: { 0: Abilities.PICKUP }, + [Species.GIRAFARIG]: { 0: Abilities.PARENTAL_BOND }, + [Species.PINECO]: { 0: Abilities.IRON_BARBS }, + [Species.DUNSPARCE]: { 0: Abilities.UNAWARE }, + [Species.GLIGAR]: { 0: Abilities.TOXIC_BOOST }, + [Species.SNUBBULL]: { 0: Abilities.PIXILATE }, + [Species.QWILFISH]: { 0: Abilities.TOXIC_DEBRIS }, + [Species.SHUCKLE]: { 0: Abilities.HARVEST }, + [Species.HERACROSS]: { 0: Abilities.TECHNICIAN }, + [Species.SNEASEL]: { 0: Abilities.TOUGH_CLAWS }, + [Species.TEDDIURSA]: { 0: Abilities.THICK_FAT }, + [Species.SLUGMA]: { 0: Abilities.DESOLATE_LAND }, + [Species.SWINUB]: { 0: Abilities.SLUSH_RUSH }, + [Species.CORSOLA]: { 0: Abilities.STORM_DRAIN }, + [Species.REMORAID]: { 0: Abilities.SIMPLE }, + [Species.DELIBIRD]: { 0: Abilities.HUGE_POWER }, + [Species.SKARMORY]: { 0: Abilities.LIGHTNING_ROD }, + [Species.HOUNDOUR]: { 0: Abilities.LIGHTNING_ROD }, + [Species.PHANPY]: { 0: Abilities.SPEED_BOOST }, + [Species.STANTLER]: { 0: Abilities.SPEED_BOOST }, + [Species.SMEARGLE]: { 0: Abilities.PRANKSTER }, + [Species.TYROGUE]: { 0: Abilities.MOXIE }, + [Species.SMOOCHUM]: { 0: Abilities.PSYCHIC_SURGE }, + [Species.ELEKID]: { 0: Abilities.SHEER_FORCE }, + [Species.MAGBY]: { 0: Abilities.SHEER_FORCE }, + [Species.MILTANK]: { 0: Abilities.STAMINA }, + [Species.RAIKOU]: { 0: Abilities.BEAST_BOOST }, + [Species.ENTEI]: { 0: Abilities.BEAST_BOOST }, + [Species.SUICUNE]: { 0: Abilities.BEAST_BOOST }, + [Species.LARVITAR]: { 0: Abilities.SAND_RUSH }, + [Species.LUGIA]: { 0: Abilities.DELTA_STREAM }, + [Species.HO_OH]: { 0: Abilities.MAGIC_GUARD }, + [Species.CELEBI]: { 0: Abilities.PSYCHIC_SURGE }, - [Species.TREECKO]: Abilities.TINTED_LENS, - [Species.TORCHIC]: Abilities.DEFIANT, - [Species.MUDKIP]: Abilities.DRIZZLE, - [Species.POOCHYENA]: Abilities.TOUGH_CLAWS, - [Species.ZIGZAGOON]: Abilities.RUN_AWAY, - [Species.WURMPLE]: Abilities.SIMPLE, - [Species.LOTAD]: Abilities.DRIZZLE, - [Species.SEEDOT]: Abilities.SHARPNESS, - [Species.TAILLOW]: Abilities.AERILATE, - [Species.WINGULL]: Abilities.SWIFT_SWIM, - [Species.RALTS]: Abilities.PSYCHIC_SURGE, - [Species.SURSKIT]: Abilities.WATER_BUBBLE, - [Species.SHROOMISH]: Abilities.GUTS, - [Species.SLAKOTH]: Abilities.GUTS, - [Species.NINCADA]: Abilities.MAGIC_GUARD, - [Species.WHISMUR]: Abilities.PUNK_ROCK, - [Species.MAKUHITA]: Abilities.STAMINA, - [Species.AZURILL]: Abilities.MISTY_SURGE, - [Species.NOSEPASS]: Abilities.LEVITATE, - [Species.SKITTY]: Abilities.SCRAPPY, - [Species.SABLEYE]: Abilities.UNNERVE, - [Species.MAWILE]: Abilities.UNNERVE, - [Species.ARON]: Abilities.EARTH_EATER, - [Species.MEDITITE]: Abilities.MINDS_EYE, - [Species.ELECTRIKE]: Abilities.FLASH_FIRE, - [Species.PLUSLE]: Abilities.POWER_SPOT, - [Species.MINUN]: Abilities.POWER_SPOT, - [Species.VOLBEAT]: Abilities.HONEY_GATHER, - [Species.ILLUMISE]: Abilities.HONEY_GATHER, - [Species.GULPIN]: Abilities.EARTH_EATER, - [Species.CARVANHA]: Abilities.SHEER_FORCE, - [Species.WAILMER]: Abilities.LEVITATE, - [Species.NUMEL]: Abilities.FUR_COAT, - [Species.TORKOAL]: Abilities.ANALYTIC, - [Species.SPOINK]: Abilities.PSYCHIC_SURGE, - [Species.SPINDA]: Abilities.SIMPLE, - [Species.TRAPINCH]: Abilities.ADAPTABILITY, - [Species.CACNEA]: Abilities.SAND_RUSH, - [Species.SWABLU]: Abilities.FLUFFY, - [Species.ZANGOOSE]: Abilities.POISON_HEAL, - [Species.SEVIPER]: Abilities.MULTISCALE, - [Species.LUNATONE]: Abilities.SHADOW_SHIELD, - [Species.SOLROCK]: Abilities.DROUGHT, - [Species.BARBOACH]: Abilities.SIMPLE, - [Species.CORPHISH]: Abilities.TOUGH_CLAWS, - [Species.BALTOY]: Abilities.WELL_BAKED_BODY, - [Species.LILEEP]: Abilities.SEED_SOWER, - [Species.ANORITH]: Abilities.WATER_ABSORB, - [Species.FEEBAS]: Abilities.MAGIC_GUARD, - [Species.CASTFORM]: Abilities.ADAPTABILITY, - [Species.KECLEON]: Abilities.ADAPTABILITY, - [Species.SHUPPET]: Abilities.SHADOW_SHIELD, - [Species.DUSKULL]: Abilities.UNNERVE, - [Species.TROPIUS]: Abilities.RIPEN, - [Species.ABSOL]: Abilities.SHARPNESS, - [Species.WYNAUT]: Abilities.STURDY, - [Species.SNORUNT]: Abilities.SNOW_WARNING, - [Species.SPHEAL]: Abilities.UNAWARE, - [Species.CLAMPERL]: Abilities.ARENA_TRAP, - [Species.RELICANTH]: Abilities.PRIMORDIAL_SEA, - [Species.LUVDISC]: Abilities.MULTISCALE, - [Species.BAGON]: Abilities.MOLD_BREAKER, - [Species.BELDUM]: Abilities.LEVITATE, - [Species.REGIROCK]: Abilities.SAND_STREAM, - [Species.REGICE]: Abilities.SNOW_WARNING, - [Species.REGISTEEL]: Abilities.STEELY_SPIRIT, - [Species.LATIAS]: Abilities.PRISM_ARMOR, - [Species.LATIOS]: Abilities.TINTED_LENS, - [Species.KYOGRE]: Abilities.MOLD_BREAKER, - [Species.GROUDON]: Abilities.TURBOBLAZE, - [Species.RAYQUAZA]: Abilities.UNNERVE, - [Species.JIRACHI]: Abilities.COMATOSE, - [Species.DEOXYS]: Abilities.PROTEAN, + [Species.TREECKO]: { 0: Abilities.TINTED_LENS }, + [Species.TORCHIC]: { 0: Abilities.DEFIANT }, + [Species.MUDKIP]: { 0: Abilities.DRIZZLE }, + [Species.POOCHYENA]: { 0: Abilities.TOUGH_CLAWS }, + [Species.ZIGZAGOON]: { 0: Abilities.RUN_AWAY }, + [Species.WURMPLE]: { 0: Abilities.SIMPLE }, + [Species.LOTAD]: { 0: Abilities.DRIZZLE }, + [Species.SEEDOT]: { 0: Abilities.SHARPNESS }, + [Species.TAILLOW]: { 0: Abilities.AERILATE }, + [Species.WINGULL]: { 0: Abilities.SWIFT_SWIM }, + [Species.RALTS]: { 0: Abilities.PSYCHIC_SURGE }, + [Species.SURSKIT]: { 0: Abilities.WATER_BUBBLE }, + [Species.SHROOMISH]: { 0: Abilities.GUTS }, + [Species.SLAKOTH]: { 0: Abilities.GUTS }, + [Species.NINCADA]: { 0: Abilities.MAGIC_GUARD }, + [Species.WHISMUR]: { 0: Abilities.PUNK_ROCK }, + [Species.MAKUHITA]: { 0: Abilities.STAMINA }, + [Species.AZURILL]: { 0: Abilities.MISTY_SURGE }, + [Species.NOSEPASS]: { 0: Abilities.LEVITATE }, + [Species.SKITTY]: { 0: Abilities.SCRAPPY }, + [Species.SABLEYE]: { 0: Abilities.UNNERVE }, + [Species.MAWILE]: { 0: Abilities.UNNERVE }, + [Species.ARON]: { 0: Abilities.EARTH_EATER }, + [Species.MEDITITE]: { 0: Abilities.MINDS_EYE }, + [Species.ELECTRIKE]: { 0: Abilities.FLASH_FIRE }, + [Species.PLUSLE]: { 0: Abilities.POWER_SPOT }, + [Species.MINUN]: { 0: Abilities.POWER_SPOT }, + [Species.VOLBEAT]: { 0: Abilities.HONEY_GATHER }, + [Species.ILLUMISE]: { 0: Abilities.HONEY_GATHER }, + [Species.GULPIN]: { 0: Abilities.EARTH_EATER }, + [Species.CARVANHA]: { 0: Abilities.SHEER_FORCE }, + [Species.WAILMER]: { 0: Abilities.LEVITATE }, + [Species.NUMEL]: { 0: Abilities.FUR_COAT }, + [Species.TORKOAL]: { 0: Abilities.ANALYTIC }, + [Species.SPOINK]: { 0: Abilities.PSYCHIC_SURGE }, + [Species.SPINDA]: { 0: Abilities.SIMPLE }, + [Species.TRAPINCH]: { 0: Abilities.ADAPTABILITY }, + [Species.CACNEA]: { 0: Abilities.SAND_RUSH }, + [Species.SWABLU]: { 0: Abilities.FLUFFY }, + [Species.ZANGOOSE]: { 0: Abilities.POISON_HEAL }, + [Species.SEVIPER]: { 0: Abilities.MULTISCALE }, + [Species.LUNATONE]: { 0: Abilities.SHADOW_SHIELD }, + [Species.SOLROCK]: { 0: Abilities.DROUGHT }, + [Species.BARBOACH]: { 0: Abilities.SIMPLE }, + [Species.CORPHISH]: { 0: Abilities.TOUGH_CLAWS }, + [Species.BALTOY]: { 0: Abilities.WELL_BAKED_BODY }, + [Species.LILEEP]: { 0: Abilities.SEED_SOWER }, + [Species.ANORITH]: { 0: Abilities.WATER_ABSORB }, + [Species.FEEBAS]: { 0: Abilities.MAGIC_GUARD }, + [Species.CASTFORM]: { 0: Abilities.ADAPTABILITY }, + [Species.KECLEON]: { 0: Abilities.ADAPTABILITY }, + [Species.SHUPPET]: { 0: Abilities.SHADOW_SHIELD }, + [Species.DUSKULL]: { 0: Abilities.UNNERVE }, + [Species.TROPIUS]: { 0: Abilities.RIPEN }, + [Species.ABSOL]: { 0: Abilities.SHARPNESS }, + [Species.WYNAUT]: { 0: Abilities.STURDY }, + [Species.SNORUNT]: { 0: Abilities.SNOW_WARNING }, + [Species.SPHEAL]: { 0: Abilities.UNAWARE }, + [Species.CLAMPERL]: { 0: Abilities.ARENA_TRAP }, + [Species.RELICANTH]: { 0: Abilities.PRIMORDIAL_SEA }, + [Species.LUVDISC]: { 0: Abilities.MULTISCALE }, + [Species.BAGON]: { 0: Abilities.MOLD_BREAKER }, + [Species.BELDUM]: { 0: Abilities.LEVITATE }, + [Species.REGIROCK]: { 0: Abilities.SAND_STREAM }, + [Species.REGICE]: { 0: Abilities.SNOW_WARNING }, + [Species.REGISTEEL]: { 0: Abilities.STEELY_SPIRIT }, + [Species.LATIAS]: { 0: Abilities.PRISM_ARMOR }, + [Species.LATIOS]: { 0: Abilities.TINTED_LENS }, + [Species.KYOGRE]: { 0: Abilities.MOLD_BREAKER }, + [Species.GROUDON]: { 0: Abilities.TURBOBLAZE }, + [Species.RAYQUAZA]: { 0: Abilities.UNNERVE }, + [Species.JIRACHI]: { 0: Abilities.COMATOSE }, + [Species.DEOXYS]: { 0: Abilities.PROTEAN }, - [Species.TURTWIG]: Abilities.THICK_FAT, - [Species.CHIMCHAR]: Abilities.BEAST_BOOST, - [Species.PIPLUP]: Abilities.DRIZZLE, - [Species.STARLY]: Abilities.ROCK_HEAD, - [Species.BIDOOF]: Abilities.SAP_SIPPER, - [Species.KRICKETOT]: Abilities.SHARPNESS, - [Species.SHINX]: Abilities.SPEED_BOOST, - [Species.BUDEW]: Abilities.GRASSY_SURGE, - [Species.CRANIDOS]: Abilities.ROCK_HEAD, - [Species.SHIELDON]: Abilities.EARTH_EATER, - [Species.BURMY]: Abilities.STURDY, - [Species.COMBEE]: Abilities.INTIMIDATE, - [Species.PACHIRISU]: Abilities.HONEY_GATHER, - [Species.BUIZEL]: Abilities.MOXIE, - [Species.CHERUBI]: Abilities.ORICHALCUM_PULSE, - [Species.SHELLOS]: Abilities.REGENERATOR, - [Species.DRIFLOON]: Abilities.MAGIC_GUARD, - [Species.BUNEARY]: Abilities.ADAPTABILITY, - [Species.GLAMEOW]: Abilities.INTIMIDATE, - [Species.CHINGLING]: Abilities.PUNK_ROCK, - [Species.STUNKY]: Abilities.NEUTRALIZING_GAS, - [Species.BRONZOR]: Abilities.MIRROR_ARMOR, - [Species.BONSLY]: Abilities.SAP_SIPPER, - [Species.MIME_JR]: Abilities.PRANKSTER, - [Species.HAPPINY]: Abilities.FUR_COAT, - [Species.CHATOT]: Abilities.PUNK_ROCK, - [Species.SPIRITOMB]: Abilities.VESSEL_OF_RUIN, - [Species.GIBLE]: Abilities.SAND_STREAM, - [Species.MUNCHLAX]: Abilities.RIPEN, - [Species.RIOLU]: Abilities.MINDS_EYE, - [Species.HIPPOPOTAS]: Abilities.UNAWARE, - [Species.SKORUPI]: Abilities.SUPER_LUCK, - [Species.CROAGUNK]: Abilities.MOXIE, - [Species.CARNIVINE]: Abilities.ARENA_TRAP, - [Species.FINNEON]: Abilities.WATER_BUBBLE, - [Species.MANTYKE]: Abilities.UNAWARE, - [Species.SNOVER]: Abilities.GRASSY_SURGE, - [Species.ROTOM]: Abilities.HADRON_ENGINE, - [Species.UXIE]: Abilities.UNAWARE, - [Species.MESPRIT]: Abilities.MOODY, - [Species.AZELF]: Abilities.NEUROFORCE, - [Species.DIALGA]: Abilities.LEVITATE, - [Species.PALKIA]: Abilities.SPEED_BOOST, - [Species.HEATRAN]: Abilities.EARTH_EATER, - [Species.REGIGIGAS]: Abilities.SCRAPPY, - [Species.GIRATINA]: Abilities.SHADOW_SHIELD, - [Species.CRESSELIA]: Abilities.SHADOW_SHIELD, - [Species.PHIONE]: Abilities.SIMPLE, - [Species.MANAPHY]: Abilities.PRIMORDIAL_SEA, - [Species.DARKRAI]: Abilities.UNNERVE, - [Species.SHAYMIN]: Abilities.WIND_RIDER, - [Species.ARCEUS]: Abilities.ADAPTABILITY, + [Species.TURTWIG]: { 0: Abilities.THICK_FAT }, + [Species.CHIMCHAR]: { 0: Abilities.BEAST_BOOST }, + [Species.PIPLUP]: { 0: Abilities.DRIZZLE }, + [Species.STARLY]: { 0: Abilities.ROCK_HEAD }, + [Species.BIDOOF]: { 0: Abilities.SAP_SIPPER }, + [Species.KRICKETOT]: { 0: Abilities.SHARPNESS }, + [Species.SHINX]: { 0: Abilities.SPEED_BOOST }, + [Species.BUDEW]: { 0: Abilities.GRASSY_SURGE }, + [Species.CRANIDOS]: { 0: Abilities.ROCK_HEAD }, + [Species.SHIELDON]: { 0: Abilities.EARTH_EATER }, + [Species.BURMY]: { 0: Abilities.STURDY }, + [Species.COMBEE]: { 0: Abilities.INTIMIDATE }, + [Species.PACHIRISU]: { 0: Abilities.HONEY_GATHER }, + [Species.BUIZEL]: { 0: Abilities.MOXIE }, + [Species.CHERUBI]: { 0: Abilities.ORICHALCUM_PULSE }, + [Species.SHELLOS]: { 0: Abilities.REGENERATOR }, + [Species.DRIFLOON]: { 0: Abilities.MAGIC_GUARD }, + [Species.BUNEARY]: { 0: Abilities.ADAPTABILITY }, + [Species.GLAMEOW]: { 0: Abilities.INTIMIDATE }, + [Species.CHINGLING]: { 0: Abilities.PUNK_ROCK }, + [Species.STUNKY]: { 0: Abilities.NEUTRALIZING_GAS }, + [Species.BRONZOR]: { 0: Abilities.MIRROR_ARMOR }, + [Species.BONSLY]: { 0: Abilities.SAP_SIPPER }, + [Species.MIME_JR]: { 0: Abilities.PRANKSTER }, + [Species.HAPPINY]: { 0: Abilities.FUR_COAT }, + [Species.CHATOT]: { 0: Abilities.PUNK_ROCK }, + [Species.SPIRITOMB]: { 0: Abilities.VESSEL_OF_RUIN }, + [Species.GIBLE]: { 0: Abilities.SAND_STREAM }, + [Species.MUNCHLAX]: { 0: Abilities.RIPEN }, + [Species.RIOLU]: { 0: Abilities.MINDS_EYE }, + [Species.HIPPOPOTAS]: { 0: Abilities.UNAWARE }, + [Species.SKORUPI]: { 0: Abilities.SUPER_LUCK }, + [Species.CROAGUNK]: { 0: Abilities.MOXIE }, + [Species.CARNIVINE]: { 0: Abilities.ARENA_TRAP }, + [Species.FINNEON]: { 0: Abilities.WATER_BUBBLE }, + [Species.MANTYKE]: { 0: Abilities.UNAWARE }, + [Species.SNOVER]: { 0: Abilities.GRASSY_SURGE }, + [Species.ROTOM]: { 0: Abilities.HADRON_ENGINE }, + [Species.UXIE]: { 0: Abilities.UNAWARE }, + [Species.MESPRIT]: { 0: Abilities.MOODY }, + [Species.AZELF]: { 0: Abilities.NEUROFORCE }, + [Species.DIALGA]: { 0: Abilities.LEVITATE }, + [Species.PALKIA]: { 0: Abilities.SPEED_BOOST }, + [Species.HEATRAN]: { 0: Abilities.EARTH_EATER }, + [Species.REGIGIGAS]: { 0: Abilities.SCRAPPY }, + [Species.GIRATINA]: { 0: Abilities.SHADOW_SHIELD }, + [Species.CRESSELIA]: { 0: Abilities.SHADOW_SHIELD }, + [Species.PHIONE]: { 0: Abilities.SIMPLE }, + [Species.MANAPHY]: { 0: Abilities.PRIMORDIAL_SEA }, + [Species.DARKRAI]: { 0: Abilities.UNNERVE }, + [Species.SHAYMIN]: { 0: Abilities.WIND_RIDER }, + [Species.ARCEUS]: { 0: Abilities.ADAPTABILITY }, - [Species.VICTINI]: Abilities.SHEER_FORCE, - [Species.SNIVY]: Abilities.MULTISCALE, - [Species.TEPIG]: Abilities.ROCK_HEAD, - [Species.OSHAWOTT]: Abilities.INTREPID_SWORD, - [Species.PATRAT]: Abilities.NO_GUARD, - [Species.LILLIPUP]: Abilities.FUR_COAT, - [Species.PURRLOIN]: Abilities.PICKUP, - [Species.PANSAGE]: Abilities.WELL_BAKED_BODY, - [Species.PANSEAR]: Abilities.WATER_ABSORB, - [Species.PANPOUR]: Abilities.SAP_SIPPER, - [Species.MUNNA]: Abilities.NEUTRALIZING_GAS, - [Species.PIDOVE]: Abilities.SNIPER, - [Species.BLITZLE]: Abilities.ELECTRIC_SURGE, - [Species.ROGGENROLA]: Abilities.SOLID_ROCK, - [Species.WOOBAT]: Abilities.OPPORTUNIST, - [Species.DRILBUR]: Abilities.STURDY, - [Species.AUDINO]: Abilities.FRIEND_GUARD, - [Species.TIMBURR]: Abilities.ROCKY_PAYLOAD, - [Species.TYMPOLE]: Abilities.POISON_HEAL, - [Species.THROH]: Abilities.STAMINA, - [Species.SAWK]: Abilities.SCRAPPY, - [Species.SEWADDLE]: Abilities.SHARPNESS, - [Species.VENIPEDE]: Abilities.STAMINA, - [Species.COTTONEE]: Abilities.FLUFFY, - [Species.PETILIL]: Abilities.SIMPLE, - [Species.BASCULIN]: Abilities.SUPREME_OVERLORD, - [Species.SANDILE]: Abilities.TOUGH_CLAWS, - [Species.DARUMAKA]: Abilities.GORILLA_TACTICS, - [Species.MARACTUS]: Abilities.WELL_BAKED_BODY, - [Species.DWEBBLE]: Abilities.ROCKY_PAYLOAD, - [Species.SCRAGGY]: Abilities.PROTEAN, - [Species.SIGILYPH]: Abilities.FLARE_BOOST, - [Species.YAMASK]: Abilities.PURIFYING_SALT, - [Species.TIRTOUGA]: Abilities.WATER_ABSORB, - [Species.ARCHEN]: Abilities.MULTISCALE, - [Species.TRUBBISH]: Abilities.NEUTRALIZING_GAS, - [Species.ZORUA]: Abilities.DARK_AURA, - [Species.MINCCINO]: Abilities.FUR_COAT, - [Species.GOTHITA]: Abilities.UNNERVE, - [Species.SOLOSIS]: Abilities.PSYCHIC_SURGE, - [Species.DUCKLETT]: Abilities.DRIZZLE, - [Species.VANILLITE]: Abilities.SLUSH_RUSH, - [Species.DEERLING]: Abilities.FUR_COAT, - [Species.EMOLGA]: Abilities.SERENE_GRACE, - [Species.KARRABLAST]: Abilities.QUICK_DRAW, - [Species.FOONGUS]: Abilities.THICK_FAT, - [Species.FRILLISH]: Abilities.POISON_HEAL, - [Species.ALOMOMOLA]: Abilities.MULTISCALE, - [Species.JOLTIK]: Abilities.TRANSISTOR, - [Species.FERROSEED]: Abilities.ROUGH_SKIN, - [Species.KLINK]: Abilities.STEELY_SPIRIT, - [Species.TYNAMO]: Abilities.POISON_HEAL, - [Species.ELGYEM]: Abilities.BEADS_OF_RUIN, - [Species.LITWICK]: Abilities.SHADOW_TAG, - [Species.AXEW]: Abilities.DRAGONS_MAW, - [Species.CUBCHOO]: Abilities.FUR_COAT, - [Species.CRYOGONAL]: Abilities.SNOW_WARNING, - [Species.SHELMET]: Abilities.PROTEAN, - [Species.STUNFISK]: Abilities.STORM_DRAIN, - [Species.MIENFOO]: Abilities.NO_GUARD, - [Species.DRUDDIGON]: Abilities.INTIMIDATE, - [Species.GOLETT]: Abilities.SHADOW_SHIELD, - [Species.PAWNIARD]: Abilities.SWORD_OF_RUIN, - [Species.BOUFFALANT]: Abilities.ROCK_HEAD, - [Species.RUFFLET]: Abilities.SPEED_BOOST, - [Species.VULLABY]: Abilities.THICK_FAT, - [Species.HEATMOR]: Abilities.CONTRARY, - [Species.DURANT]: Abilities.COMPOUND_EYES, - [Species.DEINO]: Abilities.PARENTAL_BOND, - [Species.LARVESTA]: Abilities.DROUGHT, - [Species.COBALION]: Abilities.INTREPID_SWORD, - [Species.TERRAKION]: Abilities.ROCKY_PAYLOAD, - [Species.VIRIZION]: Abilities.SHARPNESS, - [Species.TORNADUS]: Abilities.DRIZZLE, - [Species.THUNDURUS]: Abilities.DRIZZLE, - [Species.RESHIRAM]: Abilities.ORICHALCUM_PULSE, - [Species.ZEKROM]: Abilities.HADRON_ENGINE, - [Species.LANDORUS]: Abilities.STORM_DRAIN, - [Species.KYUREM]: Abilities.SNOW_WARNING, - [Species.KELDEO]: Abilities.GRIM_NEIGH, - [Species.MELOETTA]: Abilities.MINDS_EYE, - [Species.GENESECT]: Abilities.PROTEAN, + [Species.VICTINI]: { 0: Abilities.SHEER_FORCE }, + [Species.SNIVY]: { 0: Abilities.MULTISCALE }, + [Species.TEPIG]: { 0: Abilities.ROCK_HEAD }, + [Species.OSHAWOTT]: { 0: Abilities.INTREPID_SWORD }, + [Species.PATRAT]: { 0: Abilities.NO_GUARD }, + [Species.LILLIPUP]: { 0: Abilities.FUR_COAT }, + [Species.PURRLOIN]: { 0: Abilities.PICKUP }, + [Species.PANSAGE]: { 0: Abilities.WELL_BAKED_BODY }, + [Species.PANSEAR]: { 0: Abilities.WATER_ABSORB }, + [Species.PANPOUR]: { 0: Abilities.SAP_SIPPER }, + [Species.MUNNA]: { 0: Abilities.NEUTRALIZING_GAS }, + [Species.PIDOVE]: { 0: Abilities.SNIPER }, + [Species.BLITZLE]: { 0: Abilities.ELECTRIC_SURGE }, + [Species.ROGGENROLA]: { 0: Abilities.SOLID_ROCK }, + [Species.WOOBAT]: { 0: Abilities.OPPORTUNIST }, + [Species.DRILBUR]: { 0: Abilities.STURDY }, + [Species.AUDINO]: { 0: Abilities.FRIEND_GUARD }, + [Species.TIMBURR]: { 0: Abilities.ROCKY_PAYLOAD }, + [Species.TYMPOLE]: { 0: Abilities.POISON_HEAL }, + [Species.THROH]: { 0: Abilities.STAMINA }, + [Species.SAWK]: { 0: Abilities.SCRAPPY }, + [Species.SEWADDLE]: { 0: Abilities.SHARPNESS }, + [Species.VENIPEDE]: { 0: Abilities.STAMINA }, + [Species.COTTONEE]: { 0: Abilities.FLUFFY }, + [Species.PETILIL]: { 0: Abilities.SIMPLE }, + [Species.BASCULIN]: { 0: Abilities.SUPREME_OVERLORD }, + [Species.SANDILE]: { 0: Abilities.TOUGH_CLAWS }, + [Species.DARUMAKA]: { 0: Abilities.GORILLA_TACTICS }, + [Species.MARACTUS]: { 0: Abilities.WELL_BAKED_BODY }, + [Species.DWEBBLE]: { 0: Abilities.ROCKY_PAYLOAD }, + [Species.SCRAGGY]: { 0: Abilities.PROTEAN }, + [Species.SIGILYPH]: { 0: Abilities.FLARE_BOOST }, + [Species.YAMASK]: { 0: Abilities.PURIFYING_SALT }, + [Species.TIRTOUGA]: { 0: Abilities.WATER_ABSORB }, + [Species.ARCHEN]: { 0: Abilities.MULTISCALE }, + [Species.TRUBBISH]: { 0: Abilities.NEUTRALIZING_GAS }, + [Species.ZORUA]: { 0: Abilities.DARK_AURA }, + [Species.MINCCINO]: { 0: Abilities.FUR_COAT }, + [Species.GOTHITA]: { 0: Abilities.UNNERVE }, + [Species.SOLOSIS]: { 0: Abilities.PSYCHIC_SURGE }, + [Species.DUCKLETT]: { 0: Abilities.DRIZZLE }, + [Species.VANILLITE]: { 0: Abilities.SLUSH_RUSH }, + [Species.DEERLING]: { 0: Abilities.FUR_COAT }, + [Species.EMOLGA]: { 0: Abilities.SERENE_GRACE }, + [Species.KARRABLAST]: { 0: Abilities.QUICK_DRAW }, + [Species.FOONGUS]: { 0: Abilities.THICK_FAT }, + [Species.FRILLISH]: { 0: Abilities.POISON_HEAL }, + [Species.ALOMOMOLA]: { 0: Abilities.MULTISCALE }, + [Species.JOLTIK]: { 0: Abilities.TRANSISTOR }, + [Species.FERROSEED]: { 0: Abilities.ROUGH_SKIN }, + [Species.KLINK]: { 0: Abilities.STEELY_SPIRIT }, + [Species.TYNAMO]: { 0: Abilities.POISON_HEAL }, + [Species.ELGYEM]: { 0: Abilities.BEADS_OF_RUIN }, + [Species.LITWICK]: { 0: Abilities.SHADOW_TAG }, + [Species.AXEW]: { 0: Abilities.DRAGONS_MAW }, + [Species.CUBCHOO]: { 0: Abilities.FUR_COAT }, + [Species.CRYOGONAL]: { 0: Abilities.SNOW_WARNING }, + [Species.SHELMET]: { 0: Abilities.PROTEAN }, + [Species.STUNFISK]: { 0: Abilities.STORM_DRAIN }, + [Species.MIENFOO]: { 0: Abilities.NO_GUARD }, + [Species.DRUDDIGON]: { 0: Abilities.INTIMIDATE }, + [Species.GOLETT]: { 0: Abilities.SHADOW_SHIELD }, + [Species.PAWNIARD]: { 0: Abilities.SWORD_OF_RUIN }, + [Species.BOUFFALANT]: { 0: Abilities.ROCK_HEAD }, + [Species.RUFFLET]: { 0: Abilities.SPEED_BOOST }, + [Species.VULLABY]: { 0: Abilities.THICK_FAT }, + [Species.HEATMOR]: { 0: Abilities.CONTRARY }, + [Species.DURANT]: { 0: Abilities.COMPOUND_EYES }, + [Species.DEINO]: { 0: Abilities.PARENTAL_BOND }, + [Species.LARVESTA]: { 0: Abilities.DROUGHT }, + [Species.COBALION]: { 0: Abilities.INTREPID_SWORD }, + [Species.TERRAKION]: { 0: Abilities.ROCKY_PAYLOAD }, + [Species.VIRIZION]: { 0: Abilities.SHARPNESS }, + [Species.TORNADUS]: { 0: Abilities.DRIZZLE }, + [Species.THUNDURUS]: { 0: Abilities.DRIZZLE }, + [Species.RESHIRAM]: { 0: Abilities.ORICHALCUM_PULSE }, + [Species.ZEKROM]: { 0: Abilities.HADRON_ENGINE }, + [Species.LANDORUS]: { 0: Abilities.STORM_DRAIN }, + [Species.KYUREM]: { 0: Abilities.SNOW_WARNING }, + [Species.KELDEO]: { 0: Abilities.GRIM_NEIGH }, + [Species.MELOETTA]: { 0: Abilities.MINDS_EYE }, + [Species.GENESECT]: { 0: Abilities.PROTEAN }, - [Species.CHESPIN]: Abilities.DAUNTLESS_SHIELD, - [Species.FENNEKIN]: Abilities.PSYCHIC_SURGE, - [Species.FROAKIE]: Abilities.STAKEOUT, - [Species.BUNNELBY]: Abilities.GUTS, - [Species.FLETCHLING]: Abilities.MAGIC_GUARD, - [Species.SCATTERBUG]: Abilities.PRANKSTER, - [Species.LITLEO]: Abilities.BEAST_BOOST, - [Species.FLABEBE]: Abilities.GRASSY_SURGE, - [Species.SKIDDO]: Abilities.SEED_SOWER, - [Species.PANCHAM]: Abilities.FUR_COAT, - [Species.FURFROU]: Abilities.FLUFFY, - [Species.ESPURR]: Abilities.FUR_COAT, - [Species.HONEDGE]: Abilities.SHARPNESS, - [Species.SPRITZEE]: Abilities.FUR_COAT, - [Species.SWIRLIX]: Abilities.RIPEN, - [Species.INKAY]: Abilities.UNNERVE, - [Species.BINACLE]: Abilities.SAP_SIPPER, - [Species.SKRELP]: Abilities.DRAGONS_MAW, - [Species.CLAUNCHER]: Abilities.PROTEAN, - [Species.HELIOPTILE]: Abilities.PROTEAN, - [Species.TYRUNT]: Abilities.RECKLESS, - [Species.AMAURA]: Abilities.ICE_SCALES, - [Species.HAWLUCHA]: Abilities.MOXIE, - [Species.DEDENNE]: Abilities.PIXILATE, - [Species.CARBINK]: Abilities.SOLID_ROCK, - [Species.GOOMY]: Abilities.REGENERATOR, - [Species.KLEFKI]: Abilities.LEVITATE, - [Species.PHANTUMP]: Abilities.SHADOW_TAG, - [Species.PUMPKABOO]: Abilities.WELL_BAKED_BODY, - [Species.BERGMITE]: Abilities.ICE_SCALES, - [Species.NOIBAT]: Abilities.PUNK_ROCK, - [Species.XERNEAS]: Abilities.HARVEST, - [Species.YVELTAL]: Abilities.SOUL_HEART, - [Species.ZYGARDE]: Abilities.ADAPTABILITY, - [Species.DIANCIE]: Abilities.PRISM_ARMOR, - [Species.HOOPA]: Abilities.OPPORTUNIST, - [Species.VOLCANION]: Abilities.FILTER, - [Species.ETERNAL_FLOETTE]: Abilities.MAGIC_GUARD, + [Species.CHESPIN]: { 0: Abilities.DAUNTLESS_SHIELD }, + [Species.FENNEKIN]: { 0: Abilities.PSYCHIC_SURGE }, + [Species.FROAKIE]: { 0: Abilities.STAKEOUT }, + [Species.BUNNELBY]: { 0: Abilities.GUTS }, + [Species.FLETCHLING]: { 0: Abilities.MAGIC_GUARD }, + [Species.SCATTERBUG]: { 0: Abilities.PRANKSTER }, + [Species.LITLEO]: { 0: Abilities.BEAST_BOOST }, + [Species.FLABEBE]: { 0: Abilities.GRASSY_SURGE }, + [Species.SKIDDO]: { 0: Abilities.SEED_SOWER }, + [Species.PANCHAM]: { 0: Abilities.FUR_COAT }, + [Species.FURFROU]: { 0: Abilities.FLUFFY }, + [Species.ESPURR]: { 0: Abilities.FUR_COAT }, + [Species.HONEDGE]: { 0: Abilities.SHARPNESS }, + [Species.SPRITZEE]: { 0: Abilities.FUR_COAT }, + [Species.SWIRLIX]: { 0: Abilities.RIPEN }, + [Species.INKAY]: { 0: Abilities.UNNERVE }, + [Species.BINACLE]: { 0: Abilities.SAP_SIPPER }, + [Species.SKRELP]: { 0: Abilities.DRAGONS_MAW }, + [Species.CLAUNCHER]: { 0: Abilities.PROTEAN }, + [Species.HELIOPTILE]: { 0: Abilities.PROTEAN }, + [Species.TYRUNT]: { 0: Abilities.RECKLESS }, + [Species.AMAURA]: { 0: Abilities.ICE_SCALES }, + [Species.HAWLUCHA]: { 0: Abilities.MOXIE }, + [Species.DEDENNE]: { 0: Abilities.PIXILATE }, + [Species.CARBINK]: { 0: Abilities.SOLID_ROCK }, + [Species.GOOMY]: { 0: Abilities.REGENERATOR }, + [Species.KLEFKI]: { 0: Abilities.LEVITATE }, + [Species.PHANTUMP]: { 0: Abilities.SHADOW_TAG }, + [Species.PUMPKABOO]: { 0: Abilities.WELL_BAKED_BODY }, + [Species.BERGMITE]: { 0: Abilities.ICE_SCALES }, + [Species.NOIBAT]: { 0: Abilities.PUNK_ROCK }, + [Species.XERNEAS]: { 0: Abilities.HARVEST }, + [Species.YVELTAL]: { 0: Abilities.SOUL_HEART }, + [Species.ZYGARDE]: { 0: Abilities.ADAPTABILITY }, + [Species.DIANCIE]: { 0: Abilities.PRISM_ARMOR }, + [Species.HOOPA]: { 0: Abilities.OPPORTUNIST }, + [Species.VOLCANION]: { 0: Abilities.FILTER }, + [Species.ETERNAL_FLOETTE]: { 0: Abilities.MAGIC_GUARD }, - [Species.ROWLET]: Abilities.SNIPER, - [Species.LITTEN]: Abilities.OPPORTUNIST, - [Species.POPPLIO]: Abilities.PUNK_ROCK, - [Species.PIKIPEK]: Abilities.TECHNICIAN, - [Species.YUNGOOS]: Abilities.TOUGH_CLAWS, - [Species.GRUBBIN]: Abilities.SPEED_BOOST, - [Species.CRABRAWLER]: Abilities.WATER_BUBBLE, - [Species.ORICORIO]: Abilities.ADAPTABILITY, - [Species.CUTIEFLY]: Abilities.TINTED_LENS, - [Species.ROCKRUFF]: Abilities.ROCKY_PAYLOAD, - [Species.WISHIWASHI]: Abilities.REGENERATOR, - [Species.MAREANIE]: Abilities.TOXIC_DEBRIS, - [Species.MUDBRAY]: Abilities.CUD_CHEW, - [Species.DEWPIDER]: Abilities.TINTED_LENS, - [Species.FOMANTIS]: Abilities.SHARPNESS, - [Species.MORELULL]: Abilities.TRIAGE, - [Species.SALANDIT]: Abilities.DRAGONS_MAW, - [Species.STUFFUL]: Abilities.SCRAPPY, - [Species.BOUNSWEET]: Abilities.MOXIE, - [Species.COMFEY]: Abilities.FRIEND_GUARD, - [Species.ORANGURU]: Abilities.POWER_SPOT, - [Species.PASSIMIAN]: Abilities.LIBERO, - [Species.WIMPOD]: Abilities.REGENERATOR, - [Species.SANDYGAST]: Abilities.SAND_SPIT, - [Species.PYUKUMUKU]: Abilities.PURIFYING_SALT, - [Species.TYPE_NULL]: Abilities.ADAPTABILITY, - [Species.MINIOR]: Abilities.STURDY, - [Species.KOMALA]: Abilities.GUTS, - [Species.TURTONATOR]: Abilities.DAUNTLESS_SHIELD, - [Species.TOGEDEMARU]: Abilities.ROUGH_SKIN, - [Species.MIMIKYU]: Abilities.TOUGH_CLAWS, - [Species.BRUXISH]: Abilities.MULTISCALE, - [Species.DRAMPA]: Abilities.THICK_FAT, - [Species.DHELMISE]: Abilities.WATER_BUBBLE, - [Species.JANGMO_O]: Abilities.DAUNTLESS_SHIELD, - [Species.TAPU_KOKO]: Abilities.DAUNTLESS_SHIELD, - [Species.TAPU_LELE]: Abilities.SHEER_FORCE, - [Species.TAPU_BULU]: Abilities.TRIAGE, - [Species.TAPU_FINI]: Abilities.FAIRY_AURA, - [Species.COSMOG]: Abilities.BEAST_BOOST, - [Species.NIHILEGO]: Abilities.LEVITATE, - [Species.BUZZWOLE]: Abilities.MOXIE, - [Species.PHEROMOSA]: Abilities.TINTED_LENS, - [Species.XURKITREE]: Abilities.TRANSISTOR, - [Species.CELESTEELA]: Abilities.HEATPROOF, - [Species.KARTANA]: Abilities.LONG_REACH, - [Species.GUZZLORD]: Abilities.POISON_HEAL, - [Species.NECROZMA]: Abilities.BEAST_BOOST, - [Species.MAGEARNA]: Abilities.STEELY_SPIRIT, - [Species.MARSHADOW]: Abilities.IRON_FIST, - [Species.POIPOLE]: Abilities.LEVITATE, - [Species.STAKATAKA]: Abilities.SOLID_ROCK, - [Species.BLACEPHALON]: Abilities.MAGIC_GUARD, - [Species.ZERAORA]: Abilities.TOUGH_CLAWS, - [Species.MELTAN]: Abilities.HEATPROOF, - [Species.ALOLA_RATTATA]: Abilities.ADAPTABILITY, - [Species.ALOLA_SANDSHREW]: Abilities.ICE_SCALES, - [Species.ALOLA_VULPIX]: Abilities.SHEER_FORCE, - [Species.ALOLA_DIGLETT]: Abilities.STURDY, - [Species.ALOLA_MEOWTH]: Abilities.DARK_AURA, - [Species.ALOLA_GEODUDE]: Abilities.DRY_SKIN, - [Species.ALOLA_GRIMER]: Abilities.TOXIC_DEBRIS, + [Species.ROWLET]: { 0: Abilities.SNIPER }, + [Species.LITTEN]: { 0: Abilities.OPPORTUNIST }, + [Species.POPPLIO]: { 0: Abilities.PUNK_ROCK }, + [Species.PIKIPEK]: { 0: Abilities.TECHNICIAN }, + [Species.YUNGOOS]: { 0: Abilities.TOUGH_CLAWS }, + [Species.GRUBBIN]: { 0: Abilities.SPEED_BOOST }, + [Species.CRABRAWLER]: { 0: Abilities.WATER_BUBBLE }, + [Species.ORICORIO]: { 0: Abilities.ADAPTABILITY }, + [Species.CUTIEFLY]: { 0: Abilities.TINTED_LENS }, + [Species.ROCKRUFF]: { 0: Abilities.ROCKY_PAYLOAD }, + [Species.WISHIWASHI]: { 0: Abilities.REGENERATOR }, + [Species.MAREANIE]: { 0: Abilities.TOXIC_DEBRIS }, + [Species.MUDBRAY]: { 0: Abilities.CUD_CHEW }, + [Species.DEWPIDER]: { 0: Abilities.TINTED_LENS }, + [Species.FOMANTIS]: { 0: Abilities.SHARPNESS }, + [Species.MORELULL]: { 0: Abilities.TRIAGE }, + [Species.SALANDIT]: { 0: Abilities.DRAGONS_MAW }, + [Species.STUFFUL]: { 0: Abilities.SCRAPPY }, + [Species.BOUNSWEET]: { 0: Abilities.MOXIE }, + [Species.COMFEY]: { 0: Abilities.FRIEND_GUARD }, + [Species.ORANGURU]: { 0: Abilities.POWER_SPOT }, + [Species.PASSIMIAN]: { 0: Abilities.LIBERO }, + [Species.WIMPOD]: { 0: Abilities.REGENERATOR }, + [Species.SANDYGAST]: { 0: Abilities.SAND_SPIT }, + [Species.PYUKUMUKU]: { 0: Abilities.PURIFYING_SALT }, + [Species.TYPE_NULL]: { 0: Abilities.ADAPTABILITY }, + [Species.MINIOR]: { 0: Abilities.STURDY }, + [Species.KOMALA]: { 0: Abilities.GUTS }, + [Species.TURTONATOR]: { 0: Abilities.DAUNTLESS_SHIELD }, + [Species.TOGEDEMARU]: { 0: Abilities.ROUGH_SKIN }, + [Species.MIMIKYU]: { 0: Abilities.TOUGH_CLAWS }, + [Species.BRUXISH]: { 0: Abilities.MULTISCALE }, + [Species.DRAMPA]: { 0: Abilities.THICK_FAT }, + [Species.DHELMISE]: { 0: Abilities.WATER_BUBBLE }, + [Species.JANGMO_O]: { 0: Abilities.DAUNTLESS_SHIELD }, + [Species.TAPU_KOKO]: { 0: Abilities.DAUNTLESS_SHIELD }, + [Species.TAPU_LELE]: { 0: Abilities.SHEER_FORCE }, + [Species.TAPU_BULU]: { 0: Abilities.TRIAGE }, + [Species.TAPU_FINI]: { 0: Abilities.FAIRY_AURA }, + [Species.COSMOG]: { 0: Abilities.BEAST_BOOST }, + [Species.NIHILEGO]: { 0: Abilities.LEVITATE }, + [Species.BUZZWOLE]: { 0: Abilities.MOXIE }, + [Species.PHEROMOSA]: { 0: Abilities.TINTED_LENS }, + [Species.XURKITREE]: { 0: Abilities.TRANSISTOR }, + [Species.CELESTEELA]: { 0: Abilities.HEATPROOF }, + [Species.KARTANA]: { 0: Abilities.LONG_REACH }, + [Species.GUZZLORD]: { 0: Abilities.POISON_HEAL }, + [Species.NECROZMA]: { 0: Abilities.BEAST_BOOST }, + [Species.MAGEARNA]: { 0: Abilities.STEELY_SPIRIT }, + [Species.MARSHADOW]: { 0: Abilities.IRON_FIST }, + [Species.POIPOLE]: { 0: Abilities.LEVITATE }, + [Species.STAKATAKA]: { 0: Abilities.SOLID_ROCK }, + [Species.BLACEPHALON]: { 0: Abilities.MAGIC_GUARD }, + [Species.ZERAORA]: { 0: Abilities.TOUGH_CLAWS }, + [Species.MELTAN]: { 0: Abilities.HEATPROOF }, + [Species.ALOLA_RATTATA]: { 0: Abilities.ADAPTABILITY }, + [Species.ALOLA_SANDSHREW]: { 0: Abilities.ICE_SCALES }, + [Species.ALOLA_VULPIX]: { 0: Abilities.SHEER_FORCE }, + [Species.ALOLA_DIGLETT]: { 0: Abilities.STURDY }, + [Species.ALOLA_MEOWTH]: { 0: Abilities.DARK_AURA }, + [Species.ALOLA_GEODUDE]: { 0: Abilities.DRY_SKIN }, + [Species.ALOLA_GRIMER]: { 0: Abilities.TOXIC_DEBRIS }, - [Species.GROOKEY]: Abilities.GRASS_PELT, - [Species.SCORBUNNY]: Abilities.NO_GUARD, - [Species.SOBBLE]: Abilities.SUPER_LUCK, - [Species.SKWOVET]: Abilities.HARVEST, - [Species.ROOKIDEE]: Abilities.IRON_BARBS, - [Species.BLIPBUG]: Abilities.PSYCHIC_SURGE, - [Species.NICKIT]: Abilities.MAGICIAN, - [Species.GOSSIFLEUR]: Abilities.GRASSY_SURGE, - [Species.WOOLOO]: Abilities.SCRAPPY, - [Species.CHEWTLE]: Abilities.ROCKY_PAYLOAD, - [Species.YAMPER]: Abilities.SHEER_FORCE, - [Species.ROLYCOLY]: Abilities.SOLID_ROCK, - [Species.APPLIN]: Abilities.DRAGONS_MAW, - [Species.SILICOBRA]: Abilities.SAND_RUSH, - [Species.CRAMORANT]: Abilities.LIGHTNING_ROD, - [Species.ARROKUDA]: Abilities.INTIMIDATE, - [Species.TOXEL]: Abilities.ELECTRIC_SURGE, - [Species.SIZZLIPEDE]: Abilities.SPEED_BOOST, - [Species.CLOBBOPUS]: Abilities.WATER_BUBBLE, - [Species.SINISTEA]: Abilities.SHADOW_SHIELD, - [Species.HATENNA]: Abilities.FAIRY_AURA, - [Species.IMPIDIMP]: Abilities.INTIMIDATE, - [Species.MILCERY]: Abilities.REGENERATOR, - [Species.FALINKS]: Abilities.PARENTAL_BOND, - [Species.PINCURCHIN]: Abilities.ELECTROMORPHOSIS, - [Species.SNOM]: Abilities.SNOW_WARNING, - [Species.STONJOURNER]: Abilities.STURDY, - [Species.EISCUE]: Abilities.ICE_SCALES, - [Species.INDEEDEE]: Abilities.FRIEND_GUARD, - [Species.MORPEKO]: Abilities.MOODY, - [Species.CUFANT]: Abilities.EARTH_EATER, - [Species.DRACOZOLT]: Abilities.NO_GUARD, - [Species.ARCTOZOLT]: Abilities.WATER_ABSORB, - [Species.DRACOVISH]: Abilities.SWIFT_SWIM, - [Species.ARCTOVISH]: Abilities.STRONG_JAW, - [Species.DURALUDON]: Abilities.STEELWORKER, - [Species.DREEPY]: Abilities.PARENTAL_BOND, - [Species.ZACIAN]: Abilities.UNNERVE, - [Species.ZAMAZENTA]: Abilities.UNNERVE, - [Species.ETERNATUS]: Abilities.NEUTRALIZING_GAS, - [Species.KUBFU]: Abilities.IRON_FIST, - [Species.ZARUDE]: Abilities.TOUGH_CLAWS, - [Species.REGIELEKI]: Abilities.ELECTRIC_SURGE, - [Species.REGIDRAGO]: Abilities.MULTISCALE, - [Species.GLASTRIER]: Abilities.FILTER, - [Species.SPECTRIER]: Abilities.SHADOW_SHIELD, - [Species.CALYREX]: Abilities.HARVEST, - [Species.ENAMORUS]: Abilities.FAIRY_AURA, - [Species.GALAR_MEOWTH]: Abilities.UNBURDEN, - [Species.GALAR_PONYTA]: Abilities.CHILLING_NEIGH, - [Species.GALAR_SLOWPOKE]: Abilities.UNAWARE, - [Species.GALAR_FARFETCHD]: Abilities.INTREPID_SWORD, - [Species.GALAR_ARTICUNO]: Abilities.SERENE_GRACE, - [Species.GALAR_ZAPDOS]: Abilities.TOUGH_CLAWS, - [Species.GALAR_MOLTRES]: Abilities.DARK_AURA, - [Species.GALAR_CORSOLA]: Abilities.SHADOW_SHIELD, - [Species.GALAR_ZIGZAGOON]: Abilities.POISON_HEAL, - [Species.GALAR_DARUMAKA]: Abilities.FLASH_FIRE, - [Species.GALAR_YAMASK]: Abilities.TABLETS_OF_RUIN, - [Species.GALAR_STUNFISK]: Abilities.ARENA_TRAP, - [Species.HISUI_GROWLITHE]: Abilities.RECKLESS, - [Species.HISUI_VOLTORB]: Abilities.TRANSISTOR, - [Species.HISUI_QWILFISH]: Abilities.MERCILESS, - [Species.HISUI_SNEASEL]: Abilities.SCRAPPY, - [Species.HISUI_ZORUA]: Abilities.ADAPTABILITY, + [Species.GROOKEY]: { 0: Abilities.GRASS_PELT }, + [Species.SCORBUNNY]: { 0: Abilities.NO_GUARD }, + [Species.SOBBLE]: { 0: Abilities.SUPER_LUCK }, + [Species.SKWOVET]: { 0: Abilities.HARVEST }, + [Species.ROOKIDEE]: { 0: Abilities.IRON_BARBS }, + [Species.BLIPBUG]: { 0: Abilities.PSYCHIC_SURGE }, + [Species.NICKIT]: { 0: Abilities.MAGICIAN }, + [Species.GOSSIFLEUR]: { 0: Abilities.GRASSY_SURGE }, + [Species.WOOLOO]: { 0: Abilities.SCRAPPY }, + [Species.CHEWTLE]: { 0: Abilities.ROCKY_PAYLOAD }, + [Species.YAMPER]: { 0: Abilities.SHEER_FORCE }, + [Species.ROLYCOLY]: { 0: Abilities.SOLID_ROCK }, + [Species.APPLIN]: { 0: Abilities.DRAGONS_MAW }, + [Species.SILICOBRA]: { 0: Abilities.SAND_RUSH }, + [Species.CRAMORANT]: { 0: Abilities.LIGHTNING_ROD }, + [Species.ARROKUDA]: { 0: Abilities.INTIMIDATE }, + [Species.TOXEL]: { 0: Abilities.ELECTRIC_SURGE }, + [Species.SIZZLIPEDE]: { 0: Abilities.SPEED_BOOST }, + [Species.CLOBBOPUS]: { 0: Abilities.WATER_BUBBLE }, + [Species.SINISTEA]: { 0: Abilities.SHADOW_SHIELD }, + [Species.HATENNA]: { 0: Abilities.FAIRY_AURA }, + [Species.IMPIDIMP]: { 0: Abilities.INTIMIDATE }, + [Species.MILCERY]: { 0: Abilities.REGENERATOR }, + [Species.FALINKS]: { 0: Abilities.PARENTAL_BOND }, + [Species.PINCURCHIN]: { 0: Abilities.ELECTROMORPHOSIS }, + [Species.SNOM]: { 0: Abilities.SNOW_WARNING }, + [Species.STONJOURNER]: { 0: Abilities.STURDY }, + [Species.EISCUE]: { 0: Abilities.ICE_SCALES }, + [Species.INDEEDEE]: { 0: Abilities.FRIEND_GUARD }, + [Species.MORPEKO]: { 0: Abilities.MOODY }, + [Species.CUFANT]: { 0: Abilities.EARTH_EATER }, + [Species.DRACOZOLT]: { 0: Abilities.NO_GUARD }, + [Species.ARCTOZOLT]: { 0: Abilities.WATER_ABSORB }, + [Species.DRACOVISH]: { 0: Abilities.SWIFT_SWIM }, + [Species.ARCTOVISH]: { 0: Abilities.STRONG_JAW }, + [Species.DURALUDON]: { 0: Abilities.STEELWORKER }, + [Species.DREEPY]: { 0: Abilities.PARENTAL_BOND }, + [Species.ZACIAN]: { 0: Abilities.UNNERVE }, + [Species.ZAMAZENTA]: { 0: Abilities.UNNERVE }, + [Species.ETERNATUS]: { 0: Abilities.NEUTRALIZING_GAS }, + [Species.KUBFU]: { 0: Abilities.IRON_FIST }, + [Species.ZARUDE]: { 0: Abilities.TOUGH_CLAWS }, + [Species.REGIELEKI]: { 0: Abilities.ELECTRIC_SURGE }, + [Species.REGIDRAGO]: { 0: Abilities.MULTISCALE }, + [Species.GLASTRIER]: { 0: Abilities.FILTER }, + [Species.SPECTRIER]: { 0: Abilities.SHADOW_SHIELD }, + [Species.CALYREX]: { 0: Abilities.HARVEST }, + [Species.ENAMORUS]: { 0: Abilities.FAIRY_AURA }, + [Species.GALAR_MEOWTH]: { 0: Abilities.UNBURDEN }, + [Species.GALAR_PONYTA]: { 0: Abilities.CHILLING_NEIGH }, + [Species.GALAR_SLOWPOKE]: { 0: Abilities.UNAWARE }, + [Species.GALAR_FARFETCHD]: { 0: Abilities.INTREPID_SWORD }, + [Species.GALAR_ARTICUNO]: { 0: Abilities.SERENE_GRACE }, + [Species.GALAR_ZAPDOS]: { 0: Abilities.TOUGH_CLAWS }, + [Species.GALAR_MOLTRES]: { 0: Abilities.DARK_AURA }, + [Species.GALAR_CORSOLA]: { 0: Abilities.SHADOW_SHIELD }, + [Species.GALAR_ZIGZAGOON]: { 0: Abilities.POISON_HEAL }, + [Species.GALAR_DARUMAKA]: { 0: Abilities.FLASH_FIRE }, + [Species.GALAR_YAMASK]: { 0: Abilities.TABLETS_OF_RUIN }, + [Species.GALAR_STUNFISK]: { 0: Abilities.ARENA_TRAP }, + [Species.HISUI_GROWLITHE]: { 0: Abilities.RECKLESS }, + [Species.HISUI_VOLTORB]: { 0: Abilities.TRANSISTOR }, + [Species.HISUI_QWILFISH]: { 0: Abilities.MERCILESS }, + [Species.HISUI_SNEASEL]: { 0: Abilities.SCRAPPY }, + [Species.HISUI_ZORUA]: { 0: Abilities.ADAPTABILITY }, - [Species.SPRIGATITO]: Abilities.MAGICIAN, - [Species.FUECOCO]: Abilities.PUNK_ROCK, - [Species.QUAXLY]: Abilities.OPPORTUNIST, - [Species.LECHONK]: Abilities.SIMPLE, - [Species.TAROUNTULA]: Abilities.HONEY_GATHER, - [Species.NYMBLE]: Abilities.GUTS, - [Species.PAWMI]: Abilities.TRANSISTOR, - [Species.TANDEMAUS]: Abilities.SCRAPPY, - [Species.FIDOUGH]: Abilities.WATER_ABSORB, - [Species.SMOLIV]: Abilities.RIPEN, - [Species.SQUAWKABILLY]: Abilities.MOXIE, - [Species.NACLI]: Abilities.SOLID_ROCK, - [Species.CHARCADET]: Abilities.PRISM_ARMOR, - [Species.TADBULB]: Abilities.STAMINA, - [Species.WATTREL]: Abilities.SHEER_FORCE, - [Species.MASCHIFF]: Abilities.STRONG_JAW, - [Species.SHROODLE]: Abilities.CORROSION, - [Species.BRAMBLIN]: Abilities.SHADOW_SHIELD, - [Species.TOEDSCOOL]: Abilities.PRANKSTER, - [Species.KLAWF]: Abilities.WATER_ABSORB, - [Species.CAPSAKID]: Abilities.PARENTAL_BOND, - [Species.RELLOR]: Abilities.PRANKSTER, - [Species.FLITTLE]: Abilities.DAZZLING, - [Species.TINKATINK]: Abilities.STEELWORKER, - [Species.WIGLETT]: Abilities.STURDY, - [Species.BOMBIRDIER]: Abilities.UNBURDEN, - [Species.FINIZEN]: Abilities.IRON_FIST, - [Species.VAROOM]: Abilities.LEVITATE, - [Species.CYCLIZAR]: Abilities.PROTEAN, - [Species.ORTHWORM]: Abilities.REGENERATOR, - [Species.GLIMMET]: Abilities.LEVITATE, - [Species.GREAVARD]: Abilities.UNAWARE, - [Species.FLAMIGO]: Abilities.MOXIE, - [Species.CETODDLE]: Abilities.REFRIGERATE, - [Species.VELUZA]: Abilities.SUPER_LUCK, - [Species.DONDOZO]: Abilities.DRAGONS_MAW, - [Species.TATSUGIRI]: Abilities.FLUFFY, - [Species.GREAT_TUSK]: Abilities.INTIMIDATE, - [Species.SCREAM_TAIL]: Abilities.UNAWARE, - [Species.BRUTE_BONNET]: Abilities.CHLOROPHYLL, - [Species.FLUTTER_MANE]: Abilities.DAZZLING, - [Species.SLITHER_WING]: Abilities.SCRAPPY, - [Species.SANDY_SHOCKS]: Abilities.ELECTRIC_SURGE, - [Species.IRON_TREADS]: Abilities.STEELY_SPIRIT, - [Species.IRON_BUNDLE]: Abilities.SNOW_WARNING, - [Species.IRON_HANDS]: Abilities.IRON_FIST, - [Species.IRON_JUGULIS]: Abilities.LIGHTNING_ROD, - [Species.IRON_MOTH]: Abilities.LEVITATE, - [Species.IRON_THORNS]: Abilities.SAND_STREAM, - [Species.FRIGIBAX]: Abilities.INTIMIDATE, - [Species.GIMMIGHOUL]: Abilities.HONEY_GATHER, - [Species.WO_CHIEN]: Abilities.VESSEL_OF_RUIN, - [Species.CHIEN_PAO]: Abilities.INTIMIDATE, - [Species.TING_LU]: Abilities.STAMINA, - [Species.CHI_YU]: Abilities.BERSERK, - [Species.ROARING_MOON]: Abilities.TOUGH_CLAWS, - [Species.IRON_VALIANT]: Abilities.NEUROFORCE, - [Species.KORAIDON]: Abilities.OPPORTUNIST, - [Species.MIRAIDON]: Abilities.OPPORTUNIST, - [Species.WALKING_WAKE]: Abilities.BEAST_BOOST, - [Species.IRON_LEAVES]: Abilities.SHARPNESS, - [Species.POLTCHAGEIST]: Abilities.TRIAGE, - [Species.OKIDOGI]: Abilities.DARK_AURA, - [Species.MUNKIDORI]: Abilities.MAGICIAN, - [Species.FEZANDIPITI]: Abilities.PIXILATE, - [Species.OGERPON]: Abilities.OPPORTUNIST, - [Species.GOUGING_FIRE]: Abilities.BEAST_BOOST, - [Species.RAGING_BOLT]: Abilities.BEAST_BOOST, - [Species.IRON_BOULDER]: Abilities.SHARPNESS, - [Species.IRON_CROWN]: Abilities.SHARPNESS, - [Species.TERAPAGOS]: Abilities.SOUL_HEART, - [Species.PECHARUNT]: Abilities.TOXIC_CHAIN, - [Species.PALDEA_TAUROS]: Abilities.ADAPTABILITY, - [Species.PALDEA_WOOPER]: Abilities.THICK_FAT, - [Species.BLOODMOON_URSALUNA]: Abilities.BERSERK + [Species.SPRIGATITO]: { 0: Abilities.MAGICIAN }, + [Species.FUECOCO]: { 0: Abilities.PUNK_ROCK }, + [Species.QUAXLY]: { 0: Abilities.OPPORTUNIST }, + [Species.LECHONK]: { 0: Abilities.SIMPLE }, + [Species.TAROUNTULA]: { 0: Abilities.HONEY_GATHER }, + [Species.NYMBLE]: { 0: Abilities.GUTS }, + [Species.PAWMI]: { 0: Abilities.TRANSISTOR }, + [Species.TANDEMAUS]: { 0: Abilities.SCRAPPY }, + [Species.FIDOUGH]: { 0: Abilities.WATER_ABSORB }, + [Species.SMOLIV]: { 0: Abilities.RIPEN }, + [Species.SQUAWKABILLY]: { 0: Abilities.MOXIE }, + [Species.NACLI]: { 0: Abilities.SOLID_ROCK }, + [Species.CHARCADET]: { 0: Abilities.PRISM_ARMOR }, + [Species.TADBULB]: { 0: Abilities.STAMINA }, + [Species.WATTREL]: { 0: Abilities.SHEER_FORCE }, + [Species.MASCHIFF]: { 0: Abilities.STRONG_JAW }, + [Species.SHROODLE]: { 0: Abilities.CORROSION }, + [Species.BRAMBLIN]: { 0: Abilities.SHADOW_SHIELD }, + [Species.TOEDSCOOL]: { 0: Abilities.PRANKSTER }, + [Species.KLAWF]: { 0: Abilities.WATER_ABSORB }, + [Species.CAPSAKID]: { 0: Abilities.PARENTAL_BOND }, + [Species.RELLOR]: { 0: Abilities.PRANKSTER }, + [Species.FLITTLE]: { 0: Abilities.DAZZLING }, + [Species.TINKATINK]: { 0: Abilities.STEELWORKER }, + [Species.WIGLETT]: { 0: Abilities.STURDY }, + [Species.BOMBIRDIER]: { 0: Abilities.UNBURDEN }, + [Species.FINIZEN]: { 0: Abilities.IRON_FIST }, + [Species.VAROOM]: { 0: Abilities.LEVITATE }, + [Species.CYCLIZAR]: { 0: Abilities.PROTEAN }, + [Species.ORTHWORM]: { 0: Abilities.REGENERATOR }, + [Species.GLIMMET]: { 0: Abilities.LEVITATE }, + [Species.GREAVARD]: { 0: Abilities.UNAWARE }, + [Species.FLAMIGO]: { 0: Abilities.MOXIE }, + [Species.CETODDLE]: { 0: Abilities.REFRIGERATE }, + [Species.VELUZA]: { 0: Abilities.SUPER_LUCK }, + [Species.DONDOZO]: { 0: Abilities.DRAGONS_MAW }, + [Species.TATSUGIRI]: { 0: Abilities.FLUFFY }, + [Species.GREAT_TUSK]: { 0: Abilities.INTIMIDATE }, + [Species.SCREAM_TAIL]: { 0: Abilities.UNAWARE }, + [Species.BRUTE_BONNET]: { 0: Abilities.CHLOROPHYLL }, + [Species.FLUTTER_MANE]: { 0: Abilities.DAZZLING }, + [Species.SLITHER_WING]: { 0: Abilities.SCRAPPY }, + [Species.SANDY_SHOCKS]: { 0: Abilities.ELECTRIC_SURGE }, + [Species.IRON_TREADS]: { 0: Abilities.STEELY_SPIRIT }, + [Species.IRON_BUNDLE]: { 0: Abilities.SNOW_WARNING }, + [Species.IRON_HANDS]: { 0: Abilities.IRON_FIST }, + [Species.IRON_JUGULIS]: { 0: Abilities.LIGHTNING_ROD }, + [Species.IRON_MOTH]: { 0: Abilities.LEVITATE }, + [Species.IRON_THORNS]: { 0: Abilities.SAND_STREAM }, + [Species.FRIGIBAX]: { 0: Abilities.INTIMIDATE }, + [Species.GIMMIGHOUL]: { 0: Abilities.HONEY_GATHER }, + [Species.WO_CHIEN]: { 0: Abilities.VESSEL_OF_RUIN }, + [Species.CHIEN_PAO]: { 0: Abilities.INTIMIDATE }, + [Species.TING_LU]: { 0: Abilities.STAMINA }, + [Species.CHI_YU]: { 0: Abilities.BERSERK }, + [Species.ROARING_MOON]: { 0: Abilities.TOUGH_CLAWS }, + [Species.IRON_VALIANT]: { 0: Abilities.NEUROFORCE }, + [Species.KORAIDON]: { 0: Abilities.OPPORTUNIST }, + [Species.MIRAIDON]: { 0: Abilities.OPPORTUNIST }, + [Species.WALKING_WAKE]: { 0: Abilities.BEAST_BOOST }, + [Species.IRON_LEAVES]: { 0: Abilities.SHARPNESS }, + [Species.POLTCHAGEIST]: { 0: Abilities.TRIAGE }, + [Species.OKIDOGI]: { 0: Abilities.DARK_AURA }, + [Species.MUNKIDORI]: { 0: Abilities.MAGICIAN }, + [Species.FEZANDIPITI]: { 0: Abilities.PIXILATE }, + [Species.OGERPON]: { 0: Abilities.OPPORTUNIST }, + [Species.GOUGING_FIRE]: { 0: Abilities.BEAST_BOOST }, + [Species.RAGING_BOLT]: { 0: Abilities.BEAST_BOOST }, + [Species.IRON_BOULDER]: { 0: Abilities.SHARPNESS }, + [Species.IRON_CROWN]: { 0: Abilities.SHARPNESS }, + [Species.TERAPAGOS]: { 0: Abilities.SOUL_HEART }, + [Species.PECHARUNT]: { 0: Abilities.TOXIC_CHAIN }, + [Species.PALDEA_TAUROS]: { 0: Abilities.ADAPTABILITY }, + [Species.PALDEA_WOOPER]: { 0: Abilities.THICK_FAT }, + [Species.BLOODMOON_URSALUNA]: { 0: Abilities.BERSERK } }; diff --git a/src/data/move.ts b/src/data/move.ts index 74fc2ac8605..bfbb2b41955 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -1380,8 +1380,10 @@ export class UserHpDamageAttr extends FixedDamageAttr { } export class TargetHalfHpDamageAttr extends FixedDamageAttr { - // the initial amount of hp the target had before the first hit - // used for multi lens + /** + * The initial amount of hp the target had before the first hit. + * Used for calculating multi lens damage. + */ private initialHp: number; constructor() { super(0); @@ -1405,12 +1407,10 @@ export class TargetHalfHpDamageAttr extends FixedDamageAttr { // multi lens added hit; use initialHp tracker to ensure correct damage (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(this.initialHp / 2); return true; - break; case lensCount + 1: // parental bond added hit; calc damage as normal (args[0] as Utils.NumberHolder).value = Utils.toDmgValue(target.hp / 2); return true; - break; } } @@ -6129,7 +6129,7 @@ export class ForceSwitchOutAttr extends MoveEffectAttr { return false; } - // Don't allow wild mons to flee with U-turn et al + // Don't allow wild mons to flee with U-turn et al. if (this.selfSwitch && !user.isPlayer() && move.category !== MoveCategory.STATUS) { return false; } @@ -7084,7 +7084,25 @@ export class RepeatMoveAttr extends MoveEffectAttr { // get the last move used (excluding status based failures) as well as the corresponding moveset slot const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE)!; const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove.move)!; - const moveTargets = lastMove.targets ?? []; + // If the last move used can hit more than one target or has variable targets, + // re-compute the targets for the attack + // (mainly for alternating double/single battle shenanigans) + // Rampaging moves (e.g. Outrage) are not included due to being incompatible with Instruct + // TODO: Fix this once dragon darts gets smart targeting + let moveTargets = movesetMove.getMove().isMultiTarget() ? getMoveTargets(target, lastMove.move).targets : lastMove.targets; + + /** In the event the instructed move's only target is a fainted opponent, redirect it to an alive ally if possible + Normally, all yet-unexecuted move phases would swap over when the enemy in question faints + (see `redirectPokemonMoves` in `battle-scene.ts`), + but since instruct adds a new move phase pre-emptively, we need to handle this interaction manually. + */ + const firstTarget = globalScene.getField()[moveTargets[0]]; + if (globalScene.currentBattle.double && moveTargets.length === 1 && firstTarget.isFainted() && firstTarget !== target.getAlly()) { + const ally = firstTarget.getAlly(); + if (ally.isActive()) { // ally exists, is not dead and can sponge the blast + moveTargets = [ ally.getBattlerIndex() ]; + } + } globalScene.queueMessage(i18next.t("moveTriggers:instructingMove", { userPokemonName: getPokemonNameWithAffix(user), @@ -7098,12 +7116,9 @@ export class RepeatMoveAttr extends MoveEffectAttr { getCondition(): MoveConditionFunc { return (user, target, move) => { - // TODO: Confirm behavior of instructing move known by target but called by another move const lastMove = target.getLastXMoves(-1).find(m => m.move !== Moves.NONE); const movesetMove = target.getMoveset().find(m => m?.moveId === lastMove?.move); - const moveTargets = lastMove?.targets ?? []; - // TODO: Add a way of adding moves to list procedurally rather than a pre-defined blacklist - const unrepeatablemoves = [ + const uninstructableMoves = [ // Locking/Continually Executed moves Moves.OUTRAGE, Moves.RAGING_FURY, @@ -7158,11 +7173,11 @@ export class RepeatMoveAttr extends MoveEffectAttr { // TODO: Add Max/G-Move blockage if or when they are implemented ]; - if (!movesetMove // called move not in target's moveset (dancer, forgetting the move, etc.) + if (!lastMove?.move // no move to instruct + || !movesetMove // called move not in target's moveset (forgetting the move, etc.) || movesetMove.ppUsed === movesetMove.getMovePp() // move out of pp - || allMoves[lastMove?.move ?? Moves.NONE].isChargingMove() // called move is a charging/recharging move - || !moveTargets.length // called move has no targets - || unrepeatablemoves.includes(lastMove?.move ?? Moves.NONE)) { // called move is explicitly in the banlist + || allMoves[lastMove.move].isChargingMove() // called move is a charging/recharging move + || uninstructableMoves.includes(lastMove.move)) { // called move is in the banlist return false; } return true; @@ -7170,7 +7185,7 @@ export class RepeatMoveAttr extends MoveEffectAttr { } getTargetBenefitScore(user: Pokemon, target: Pokemon, move: Move): integer { - // TODO: Make the AI acutally use instruct + // TODO: Make the AI actually use instruct /* Ideally, the AI would score instruct based on the scorings of the on-field pokemons' * last used moves at the time of using Instruct (by the time the instructor gets to act) * with respect to the user's side. @@ -10279,7 +10294,10 @@ export function initMoves() { new StatusMove(Moves.INSTRUCT, Type.PSYCHIC, -1, 15, -1, 0, 7) .ignoresSubstitute() .attr(RepeatMoveAttr) - .edgeCase(), // incorrect interactions with Gigaton Hammer, Blood Moon & Torment + // incorrect interactions with Gigaton Hammer, Blood Moon & Torment + // Also has incorrect interactions with Dancer due to the latter + // erroneously adding copied moves to move history. + .edgeCase(), new AttackMove(Moves.BEAK_BLAST, Type.FLYING, MoveCategory.PHYSICAL, 100, 100, 15, -1, -3, 7) .attr(BeakBlastHeaderAttr) .ballBombMove() diff --git a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts index b50ced69918..65bbab16603 100644 --- a/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts +++ b/src/data/mystery-encounters/encounters/global-trade-system-encounter.ts @@ -449,7 +449,7 @@ function getPokemonTradeOptions(): Map { }); tradeOptionsMap.set(pokemon.id, tradeOptions); } else { - const originalBst = pokemon.calculateBaseStats().reduce((a, b) => a + b, 0); + const originalBst = pokemon.getSpeciesForm().getBaseStatTotal(); const tradeOptions: PokemonSpecies[] = []; for (let i = 0; i < 3; i++) { diff --git a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts index 702d8262cb6..5e87a40d952 100644 --- a/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-expert-pokemon-breeder-encounter.ts @@ -452,7 +452,7 @@ function getSpeciesFromPool(speciesPool: (Species | BreederSpeciesEvolution)[][] } function calculateEggRewardsForPokemon(pokemon: PlayerPokemon): [number, number] { - const bst = pokemon.calculateBaseStats().reduce((a, b) => a + b, 0); + const bst = pokemon.getSpeciesForm().getBaseStatTotal(); // 1 point for every 20 points below 680 BST the pokemon is, (max 18, min 1) const pointsFromBst = Math.min(Math.max(Math.floor((680 - bst) / 20), 1), 18); diff --git a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts index 6748d133d67..9e94e87938e 100644 --- a/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts +++ b/src/data/mystery-encounters/encounters/the-strong-stuff-encounter.ts @@ -147,8 +147,8 @@ export const TheStrongStuffEncounter: MysteryEncounter = // Sort party by bst const sortedParty = globalScene.getPlayerParty().slice(0) .sort((pokemon1, pokemon2) => { - const pokemon1Bst = pokemon1.calculateBaseStats().reduce((a, b) => a + b, 0); - const pokemon2Bst = pokemon2.calculateBaseStats().reduce((a, b) => a + b, 0); + const pokemon1Bst = pokemon1.getSpeciesForm().getBaseStatTotal(); + const pokemon2Bst = pokemon2.getSpeciesForm().getBaseStatTotal(); return pokemon2Bst - pokemon1Bst; }); diff --git a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts index 392a963e639..e8a4c405d5f 100644 --- a/src/data/mystery-encounters/encounters/weird-dream-encounter.ts +++ b/src/data/mystery-encounters/encounters/weird-dream-encounter.ts @@ -360,7 +360,7 @@ function getTeamTransformations(): PokemonTransformation[] { const index = pokemonTransformations.findIndex(p => p.previousPokemon.id === removed.id); pokemonTransformations[index].heldItems = removed.getHeldItems().filter(m => !(m instanceof PokemonFormChangeItemModifier)); - const bst = removed.calculateBaseStats().reduce((a, b) => a + b, 0); + const bst = removed.getSpeciesForm().getBaseStatTotal(); let newBstRange: [number, number]; if (i < 2) { newBstRange = HIGH_BST_TRANSFORM_BASE_VALUES; diff --git a/src/data/mystery-encounters/utils/encounter-phase-utils.ts b/src/data/mystery-encounters/utils/encounter-phase-utils.ts index a144b1ac5c5..8e7c67fae84 100644 --- a/src/data/mystery-encounters/utils/encounter-phase-utils.ts +++ b/src/data/mystery-encounters/utils/encounter-phase-utils.ts @@ -890,7 +890,7 @@ export function getRandomEncounterSpecies(level: number, isBoss: boolean = false if (eventEncounters.length > 0 && randSeedInt(2) === 1) { const eventEncounter = randSeedItem(eventEncounters); - const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, !isNullOrUndefined(eventEncounter.blockEvolution), isBoss, globalScene.gameMode); + const levelSpecies = getPokemonSpecies(eventEncounter.species).getWildSpeciesForLevel(level, !eventEncounter.blockEvolution, isBoss, globalScene.gameMode); isEventEncounter = true; bossSpecies = getPokemonSpecies(levelSpecies); } else { diff --git a/src/data/pokemon-species.ts b/src/data/pokemon-species.ts index 574c2a67f65..f2ab2f6520f 100644 --- a/src/data/pokemon-species.ts +++ b/src/data/pokemon-species.ts @@ -22,6 +22,7 @@ import type { Variant, VariantSet } from "#app/data/variant"; import { variantData } from "#app/data/variant"; import { speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; import { SpeciesFormKey } from "#enums/species-form-key"; +import { starterPassiveAbilities } from "#app/data/balance/passives"; export enum Region { NORMAL, @@ -230,6 +231,31 @@ export abstract class PokemonSpeciesForm { return ret; } + /** + * Method to get the passive ability of a Pokemon species + * @param formIndex The form index to use, defaults to form for this species instance + * @returns The id of the ability + */ + getPassiveAbility(formIndex?: number): Abilities { + if (Utils.isNullOrUndefined(formIndex)) { + formIndex = this.formIndex; + } + let starterSpeciesId = this.speciesId; + while (!(starterSpeciesId in starterPassiveAbilities) || !(formIndex in starterPassiveAbilities[starterSpeciesId])) { + if (pokemonPrevolutions.hasOwnProperty(starterSpeciesId)) { + starterSpeciesId = pokemonPrevolutions[starterSpeciesId]; + } else { // If we've reached the base species and still haven't found a matching ability, use form 0 if possible + if (0 in starterPassiveAbilities[starterSpeciesId]) { + return starterPassiveAbilities[starterSpeciesId][0]; + } else { + console.log("No passive ability found for %s, using run away", this.speciesId); + return Abilities.RUN_AWAY; + } + } + } + return starterPassiveAbilities[starterSpeciesId][formIndex]; + } + getLevelMoves(): LevelMoves { if (pokemonSpeciesFormLevelMoves.hasOwnProperty(this.speciesId) && pokemonSpeciesFormLevelMoves[this.speciesId].hasOwnProperty(this.formIndex)) { return pokemonSpeciesFormLevelMoves[this.speciesId][this.formIndex].slice(0); diff --git a/src/data/status-effect.ts b/src/data/status-effect.ts index 3c085bdd099..35a956cbb93 100644 --- a/src/data/status-effect.ts +++ b/src/data/status-effect.ts @@ -162,7 +162,7 @@ export function getNonVolatileStatusEffects():Array { } /** - * Returns whether a statuss effect is non volatile. + * Returns whether a status effect is non volatile. * Non-volatile status condition is a status that remains after being switched out. * @param status The status to check */ diff --git a/src/field/arena.ts b/src/field/arena.ts index 71966b97775..deeb261a491 100644 --- a/src/field/arena.ts +++ b/src/field/arena.ts @@ -695,6 +695,7 @@ export class Arena { globalScene.loadBgm(this.bgm); } + /** The loop point of any given biome track, read as seconds and milliseconds. */ getBgmLoopPoint(): number { switch (this.biomeType) { case Biome.TOWN: diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index f31f5a8c3e7..bdd3b31dea8 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -11,7 +11,6 @@ import { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtk import type { PokemonSpeciesForm } from "#app/data/pokemon-species"; import { default as PokemonSpecies, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm } from "#app/data/pokemon-species"; import { getStarterValueFriendshipCap, speciesStarterCosts } from "#app/data/balance/starters"; -import { starterPassiveAbilities } from "#app/data/balance/passives"; import type { Constructor } from "#app/utils"; import { isNullOrUndefined, randSeedInt, type nil } from "#app/utils"; import * as Utils from "#app/utils"; @@ -1115,6 +1114,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return this.getStat(Stat.HP); } + /** Returns the amount of hp currently missing from this {@linkcode Pokemon} (max - current) */ getInverseHp(): integer { return this.getMaxHp() - this.hp; } @@ -1407,11 +1407,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { return allAbilities[this.customPokemonData.passive]; } - let starterSpeciesId = this.species.speciesId; - while (pokemonPrevolutions.hasOwnProperty(starterSpeciesId)) { - starterSpeciesId = pokemonPrevolutions[starterSpeciesId]; - } - return allAbilities[starterPassiveAbilities[starterSpeciesId]]; + return allAbilities[this.species.getPassiveAbility(this.formIndex)]; } /** diff --git a/src/loading-scene.ts b/src/loading-scene.ts index 2636fe065c5..e829440a6ea 100644 --- a/src/loading-scene.ts +++ b/src/loading-scene.ts @@ -247,9 +247,9 @@ export class LoadingScene extends SceneBase { } const availableLangs = [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ]; if (lang && availableLangs.includes(lang)) { - this.loadImage("winter_holidays2024-event-" + lang, "events"); + this.loadImage("yearofthesnakeevent-" + lang, "events"); } else { - this.loadImage("winter_holidays2024-event-en", "events"); + this.loadImage("yearofthesnakeevent-en", "events"); } this.loadAtlas("statuses", ""); diff --git a/src/overrides.ts b/src/overrides.ts index 4b1f4b280eb..06ad619a074 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -22,7 +22,7 @@ import { WeatherType } from "#enums/weather-type"; * * Any override added here will be used instead of the value in {@linkcode DefaultOverrides} * - * If an override name starts with "STARTING", it will apply when a new run begins + * If an override name starts with "STARTING", it will only apply when a new run begins. * * @example * ``` @@ -39,7 +39,7 @@ const overrides = {} satisfies Partial>; * --- * Defaults for Overrides that are used when testing different in game situations * - * If an override name starts with "STARTING", it will apply when a new run begins + * If an override name starts with "STARTING", it will only apply when a new run begins. */ class DefaultOverrides { // ----------------- diff --git a/src/phases/learn-move-phase.ts b/src/phases/learn-move-phase.ts index 6f10022d1fc..a11b797c574 100644 --- a/src/phases/learn-move-phase.ts +++ b/src/phases/learn-move-phase.ts @@ -26,7 +26,7 @@ export enum LearnMoveType { export class LearnMovePhase extends PlayerPartyMemberPokemonPhase { private moveId: Moves; private messageMode: Mode; - private learnMoveType; + private learnMoveType: LearnMoveType; private cost: number; constructor(partyMemberIndex: integer, moveId: Moves, learnMoveType: LearnMoveType = LearnMoveType.LEARN_MOVE, cost: number = -1) { diff --git a/src/phases/trainer-victory-phase.ts b/src/phases/trainer-victory-phase.ts index 9722e9c4197..e2617f598da 100644 --- a/src/phases/trainer-victory-phase.ts +++ b/src/phases/trainer-victory-phase.ts @@ -29,7 +29,7 @@ export class TrainerVictoryPhase extends BattlePhase { globalScene.unshiftPhase(new ModifierRewardPhase(modifierRewardFunc)); } - if (globalScene.eventManager.getShinyMultiplier() > 1) { //If a shiny boosting event is active + if (globalScene.eventManager.isEventActive()) { for (const rewardFunc of globalScene.currentBattle.trainer?.config.eventRewardFuncs!) { globalScene.unshiftPhase(new ModifierRewardPhase(rewardFunc)); } diff --git a/src/system/settings/settings.ts b/src/system/settings/settings.ts index ed8e49ffe37..44fc1ee4f31 100644 --- a/src/system/settings/settings.ts +++ b/src/system/settings/settings.ts @@ -166,7 +166,7 @@ export const SettingKeys = { Field_Volume: "FIELD_VOLUME", SE_Volume: "SE_VOLUME", UI_Volume: "UI_SOUND_EFFECTS", - Music_Preference: "MUSIC_PREFERENCE", + Battle_Music: "BATTLE_MUSIC", Show_BGM_Bar: "SHOW_BGM_BAR", Move_Touch_Controls: "MOVE_TOUCH_CONTROLS", Shop_Overlay_Opacity: "SHOP_OVERLAY_OPACITY" @@ -577,8 +577,8 @@ export const Setting: Array = [ label: i18next.t("settings:consistent") }, { - value: "Mixed Animated", - label: i18next.t("settings:mixedAnimated") + value: "Experimental", + label: i18next.t("settings:experimental") } ], default: 0, @@ -658,11 +658,11 @@ export const Setting: Array = [ type: SettingType.AUDIO }, { - key: SettingKeys.Music_Preference, - label: i18next.t("settings:musicPreference"), + key: SettingKeys.Battle_Music, + label: i18next.t("settings:battleMusic"), options: [ { - value: "Gen V + PMD", + value: "Gen V", label: i18next.t("settings:musicGenFive") }, { @@ -741,7 +741,7 @@ export function setSetting(setting: string, value: integer): boolean { case SettingKeys.UI_Volume: globalScene.uiVolume = value ? parseInt(Setting[index].options[value].value) * 0.01 : 0; break; - case SettingKeys.Music_Preference: + case SettingKeys.Battle_Music: globalScene.musicPreference = value; break; case SettingKeys.Damage_Numbers: diff --git a/src/test/abilities/dancer.test.ts b/src/test/abilities/dancer.test.ts index 842b2196441..3a480316c96 100644 --- a/src/test/abilities/dancer.test.ts +++ b/src/test/abilities/dancer.test.ts @@ -7,7 +7,6 @@ import GameManager from "#test/utils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; - describe("Abilities - Dancer", () => { let phaserGame: Phaser.Game; let game: GameManager; @@ -24,20 +23,21 @@ describe("Abilities - Dancer", () => { beforeEach(() => { game = new GameManager(phaserGame); - game.override - .battleType("double") - .moveset([ Moves.SWORDS_DANCE, Moves.SPLASH ]) - .enemySpecies(Species.MAGIKARP) - .enemyAbility(Abilities.DANCER) - .enemyMoveset([ Moves.VICTORY_DANCE ]); + game.override.battleType("double"); }); // Reference Link: https://bulbapedia.bulbagarden.net/wiki/Dancer_(Ability) it("triggers when dance moves are used, doesn't consume extra PP", async () => { + game.override + .enemyAbility(Abilities.DANCER) + .enemySpecies(Species.MAGIKARP) + .enemyMoveset(Moves.VICTORY_DANCE); await game.classicMode.startBattle([ Species.ORICORIO, Species.FEEBAS ]); - const [ oricorio ] = game.scene.getPlayerField(); + const [ oricorio, feebas ] = game.scene.getPlayerField(); + game.move.changeMoveset(oricorio, [ Moves.SWORDS_DANCE, Moves.VICTORY_DANCE, Moves.SPLASH ]); + game.move.changeMoveset(feebas, [ Moves.SWORDS_DANCE, Moves.SPLASH ]); game.move.select(Moves.SPLASH); game.move.select(Moves.SWORDS_DANCE, 1); @@ -59,5 +59,44 @@ describe("Abilities - Dancer", () => { // doesn't use PP if copied move is also in moveset expect(oricorio.moveset[0]?.ppUsed).toBe(0); + expect(oricorio.moveset[1]?.ppUsed).toBe(0); + }); + + // TODO: Enable after Dancer rework to not push to move history + it.todo("should not count as the last move used for mirror move/instruct", async () => { + game.override + .moveset([ Moves.FIERY_DANCE, Moves.REVELATION_DANCE ]) + .enemyMoveset([ Moves.INSTRUCT, Moves.MIRROR_MOVE, Moves.SPLASH ]) + .enemySpecies(Species.SHUCKLE) + .enemyLevel(10); + await game.classicMode.startBattle([ Species.ORICORIO, Species.FEEBAS ]); + + const [ oricorio ] = game.scene.getPlayerField(); + const [ , shuckle2 ] = game.scene.getEnemyField(); + + game.move.select(Moves.REVELATION_DANCE, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2); + game.move.select(Moves.FIERY_DANCE, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2); + await game.forceEnemyMove(Moves.INSTRUCT, BattlerIndex.PLAYER); + await game.forceEnemyMove(Moves.MIRROR_MOVE, BattlerIndex.PLAYER); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY_2, BattlerIndex.ENEMY ]); + await game.phaseInterceptor.to("MovePhase"); // Oricorio rev dance + await game.phaseInterceptor.to("MovePhase"); // Feebas fiery dance + await game.phaseInterceptor.to("MovePhase"); // Oricorio fiery dance (from dancer) + await game.phaseInterceptor.to("MoveEndPhase", false); + // dancer copied move doesn't appear in move history + expect(oricorio.getLastXMoves(-1)[0].move).toBe(Moves.REVELATION_DANCE); + + await game.phaseInterceptor.to("MovePhase"); // shuckle 2 mirror moves oricorio + await game.phaseInterceptor.to("MovePhase"); // calls instructed rev dance + let currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(shuckle2); + expect(currentPhase.move.moveId).toBe(Moves.REVELATION_DANCE); + + await game.phaseInterceptor.to("MovePhase"); // shuckle 1 instructs oricorio + await game.phaseInterceptor.to("MovePhase"); + currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(oricorio); + expect(currentPhase.move.moveId).toBe(Moves.REVELATION_DANCE); + }); }); diff --git a/src/test/moves/instruct.test.ts b/src/test/moves/instruct.test.ts index 0e227ef6a3f..13f0c83c784 100644 --- a/src/test/moves/instruct.test.ts +++ b/src/test/moves/instruct.test.ts @@ -1,6 +1,7 @@ import { BattlerIndex } from "#app/battle"; import type Pokemon from "#app/field/pokemon"; import { MoveResult } from "#app/field/pokemon"; +import type { MovePhase } from "#app/phases/move-phase"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; @@ -12,10 +13,10 @@ describe("Moves - Instruct", () => { let phaserGame: Phaser.Game; let game: GameManager; - function instructSuccess(pokemon: Pokemon, move: Moves): void { - expect(pokemon.getLastXMoves(-1)[0].move).toBe(move); - expect(pokemon.getLastXMoves(-1)[1].move).toBe(pokemon.getLastXMoves()[0].move); - expect(pokemon.getMoveset().find(m => m?.moveId === move)?.ppUsed).toBe(2); + function instructSuccess(target: Pokemon, move: Moves): void { + expect(target.getLastXMoves(-1)[0].move).toBe(move); + expect(target.getLastXMoves(-1)[1].move).toBe(target.getLastXMoves()[0].move); + expect(target.getMoveset().find(m => m?.moveId === move)?.ppUsed).toBe(2); } beforeAll(() => { @@ -36,27 +37,40 @@ describe("Moves - Instruct", () => { .enemyAbility(Abilities.NO_GUARD) .enemyLevel(100) .startingLevel(100) - .ability(Abilities.BALL_FETCH) - .moveset([ Moves.INSTRUCT, Moves.SONIC_BOOM, Moves.SPLASH, Moves.TORMENT ]) .disableCrits(); }); - it("should repeat enemy's attack move when moving last", async () => { + it("should repeat target's last used move", async () => { + game.override + .moveset(Moves.INSTRUCT) + .enemyLevel(1000); // ensures shuckle no die await game.classicMode.startBattle([ Species.AMOONGUSS ]); const enemy = game.scene.getEnemyPokemon()!; game.move.changeMoveset(enemy, Moves.SONIC_BOOM); - game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - await game.forceEnemyMove(Moves.SONIC_BOOM, BattlerIndex.PLAYER); + game.move.select(Moves.INSTRUCT); + await game.forceEnemyMove(Moves.SONIC_BOOM); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + + await game.phaseInterceptor.to("MovePhase"); // enemy attacks us + await game.phaseInterceptor.to("MovePhase", false); // instruct + let currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(game.scene.getPlayerPokemon()); + await game.phaseInterceptor.to("MoveEndPhase"); + + await game.phaseInterceptor.to("MovePhase", false); // enemy repeats move + currentPhase = game.scene.getCurrentPhase() as MovePhase; + expect(currentPhase.pokemon).toBe(enemy); + expect(currentPhase.move.moveId).toBe(Moves.SONIC_BOOM); await game.phaseInterceptor.to("TurnEndPhase", false); - expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); instructSuccess(enemy, Moves.SONIC_BOOM); + expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); }); it("should repeat enemy's move through substitute", async () => { + game.override.moveset([ Moves.INSTRUCT, Moves.SPLASH ]); await game.classicMode.startBattle([ Species.AMOONGUSS ]); const enemy = game.scene.getEnemyPokemon()!; @@ -72,75 +86,94 @@ describe("Moves - Instruct", () => { await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); await game.phaseInterceptor.to("TurnEndPhase", false); - expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); instructSuccess(game.scene.getEnemyPokemon()!, Moves.SONIC_BOOM); + expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); }); it("should repeat ally's attack on enemy", async () => { game.override .battleType("double") - .moveset([]); + .enemyMoveset(Moves.SPLASH); await game.classicMode.startBattle([ Species.AMOONGUSS, Species.SHUCKLE ]); const [ amoonguss, shuckle ] = game.scene.getPlayerField(); - game.move.changeMoveset(amoonguss, Moves.INSTRUCT); - game.move.changeMoveset(shuckle, Moves.SONIC_BOOM); + game.move.changeMoveset(amoonguss, [ Moves.INSTRUCT, Moves.SONIC_BOOM ]); + game.move.changeMoveset(shuckle, [ Moves.INSTRUCT, Moves.SONIC_BOOM ]); game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2); game.move.select(Moves.SONIC_BOOM, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); - await game.forceEnemyMove(Moves.SPLASH); - await game.forceEnemyMove(Moves.SPLASH); await game.setTurnOrder([ BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await game.phaseInterceptor.to("TurnEndPhase", false); - expect(game.scene.getEnemyField()[0].getInverseHp()).toBe(40); instructSuccess(shuckle, Moves.SONIC_BOOM); + expect(game.scene.getEnemyField()[0].getInverseHp()).toBe(40); }); - // TODO: Enable test case once gigaton hammer (and blood moon) is fixed + // TODO: Enable test case once gigaton hammer (and blood moon) are reworked it.todo("should repeat enemy's Gigaton Hammer", async () => { game.override + .moveset(Moves.INSTRUCT) .enemyLevel(5); await game.classicMode.startBattle([ Species.AMOONGUSS ]); const enemy = game.scene.getEnemyPokemon()!; - game.move.changeMoveset(enemy, Moves.GIGATON_HAMMER); + game.move.changeMoveset(enemy, [ Moves.GIGATON_HAMMER, Moves.BLOOD_MOON ]); game.move.select(Moves.INSTRUCT); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); - await game.phaseInterceptor.to("TurnEndPhase", false); + await game.phaseInterceptor.to("BerryPhase"); instructSuccess(enemy, Moves.GIGATON_HAMMER); + expect(game.scene.getPlayerPokemon()!.turnData.attacksReceived.length).toBe(2); + }); + + it("should add moves to move queue for copycat", async () => { + game.override + .battleType("double") + .moveset(Moves.INSTRUCT) + .enemyLevel(5); + await game.classicMode.startBattle([ Species.AMOONGUSS ]); + + const [ enemy1, enemy2 ] = game.scene.getEnemyField()!; + game.move.changeMoveset(enemy1, Moves.WATER_GUN); + game.move.changeMoveset(enemy2, Moves.COPYCAT); + + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("BerryPhase"); + + instructSuccess(enemy1, Moves.WATER_GUN); + // amoonguss gets hit by water gun thrice; once by original attack, once by instructed use and once by copycat + expect(game.scene.getPlayerPokemon()!.turnData.attacksReceived.length).toBe(3); }); it("should respect enemy's status condition", async () => { game.override - .moveset([ Moves.THUNDER_WAVE, Moves.INSTRUCT ]) - .enemyMoveset([ Moves.SPLASH, Moves.SONIC_BOOM ]); + .moveset([ Moves.INSTRUCT, Moves.THUNDER_WAVE ]) + .enemyMoveset(Moves.SONIC_BOOM); await game.classicMode.startBattle([ Species.AMOONGUSS ]); game.move.select(Moves.THUNDER_WAVE); - await game.forceEnemyMove(Moves.SONIC_BOOM); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); await game.toNextTurn(); game.move.select(Moves.INSTRUCT); - await game.forceEnemyMove(Moves.SPLASH); - await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); - await game.move.forceStatusActivation(true); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.phaseInterceptor.to("MovePhase"); + // force enemy's instructed move to bork and then immediately thaw out + await game.move.forceStatusActivation(true); await game.move.forceStatusActivation(false); await game.phaseInterceptor.to("TurnEndPhase", false); - const moveHistory = game.scene.getEnemyPokemon()!.getMoveHistory(); - expect(moveHistory.length).toBe(3); - expect(moveHistory[0].move).toBe(Moves.SONIC_BOOM); - expect(moveHistory[1].move).toBe(Moves.NONE); - expect(moveHistory[2].move).toBe(Moves.SONIC_BOOM); + const moveHistory = game.scene.getEnemyPokemon()?.getLastXMoves(-1)!; + expect(moveHistory.map(m => m.move)).toEqual([ Moves.SONIC_BOOM, Moves.NONE, Moves.SONIC_BOOM ]); + expect(game.scene.getPlayerPokemon()?.getInverseHp()).toBe(40); }); it("should not repeat enemy's out of pp move", async () => { - game.override.enemySpecies(Species.UNOWN); + game.override + .moveset(Moves.INSTRUCT) + .enemySpecies(Species.UNOWN); await game.classicMode.startBattle([ Species.AMOONGUSS ]); const enemyPokemon = game.scene.getEnemyPokemon()!; @@ -153,13 +186,85 @@ describe("Moves - Instruct", () => { await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); await game.phaseInterceptor.to("TurnEndPhase", false); - const playerMove = game.scene.getPlayerPokemon()!.getLastXMoves()!; - expect(playerMove[0].result).toBe(MoveResult.FAIL); + const playerMoves = game.scene.getPlayerPokemon()!.getLastXMoves(-1)!; + expect(playerMoves[0].result).toBe(MoveResult.FAIL); expect(enemyPokemon.getMoveHistory().length).toBe(1); }); + it("should redirect attacking moves if enemy faints", async () => { + game.override + .battleType("double") + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.MAGIKARP) + .enemyLevel(1); + await game.classicMode.startBattle([ Species.HISUI_ELECTRODE, Species.KOMMO_O ]); + + const [ electrode, kommo_o ] = game.scene.getPlayerField()!; + game.move.changeMoveset(electrode, Moves.CHLOROBLAST); + game.move.changeMoveset(kommo_o, Moves.INSTRUCT); + + game.move.select(Moves.CHLOROBLAST, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("BerryPhase"); + + // Chloroblast always deals 50% max HP% recoil UNLESS you whiff + // due to lack of targets or similar, + // so all we have to do is check whether electrode fainted or not. + // Naturally, both karps should also be dead as well. + expect(electrode.isFainted()).toBe(true); + const [ karp1, karp2 ] = game.scene.getEnemyField()!; + expect(karp1.isFainted()).toBe(true); + expect(karp2.isFainted()).toBe(true); + }), + + it("should allow for dancer copying of instructed dance move", async () => { + game.override + .battleType("double") + .enemyMoveset([ Moves.INSTRUCT, Moves.SPLASH ]); + await game.classicMode.startBattle([ Species.ORICORIO, Species.VOLCARONA ]); + + const [ oricorio, volcarona ] = game.scene.getPlayerField(); + game.move.changeMoveset(oricorio, Moves.SPLASH); + game.move.changeMoveset(volcarona, Moves.FIERY_DANCE); + + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER); + game.move.select(Moves.FIERY_DANCE, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); + await game.forceEnemyMove(Moves.INSTRUCT, BattlerIndex.PLAYER_2); + await game.forceEnemyMove(Moves.SPLASH); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("BerryPhase"); + + // fiery dance triggered dancer successfully for a total of 4 hits + // Volcarona fiery dance has a _small_ chance to 3HKO a shuckle in worst case, so we add the hit count of both + // foes to account for spillover + instructSuccess(volcarona, Moves.FIERY_DANCE); + expect(game.scene.getEnemyField()[0].turnData.attacksReceived.length + + game.scene.getEnemyField()[1].turnData.attacksReceived.length).toBe(4); + }); + + it("should not repeat move when switching out", async () => { + game.override + .enemyMoveset(Moves.INSTRUCT) + .enemySpecies(Species.UNOWN); + await game.classicMode.startBattle([ Species.AMOONGUSS, Species.TOXICROAK ]); + + const amoonguss = game.scene.getPlayerPokemon()!; + game.move.changeMoveset(amoonguss, Moves.SEED_BOMB); + + amoonguss.battleSummonData.moveHistory = [{ move: Moves.SEED_BOMB, targets: [ BattlerIndex.ENEMY ], result: MoveResult.SUCCESS }]; + + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase", false); + + const enemyMoves = game.scene.getEnemyPokemon()!.getLastXMoves(-1)!; + expect(enemyMoves[0].result).toBe(MoveResult.FAIL); + }); + it("should fail if no move has yet been used by target", async () => { - game.override.enemyMoveset(Moves.SPLASH); + game.override + .moveset(Moves.INSTRUCT) + .enemyMoveset(Moves.SPLASH); await game.classicMode.startBattle([ Species.AMOONGUSS ]); game.move.select(Moves.INSTRUCT); @@ -183,48 +288,46 @@ describe("Moves - Instruct", () => { game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(Moves.DISABLE, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); await game.forceEnemyMove(Moves.SONIC_BOOM, BattlerIndex.PLAYER); - await game.forceEnemyMove(Moves.SPLASH); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY_2 ]); await game.phaseInterceptor.to("TurnEndPhase", false); expect(game.scene.getPlayerField()[0].getLastXMoves()[0].result).toBe(MoveResult.SUCCESS); - const enemyMove = game.scene.getEnemyPokemon()!.getLastXMoves()[0]; + const enemyMove = game.scene.getEnemyField()[0]!.getLastXMoves()[0]; expect(enemyMove.result).toBe(MoveResult.FAIL); - expect(game.scene.getEnemyPokemon()!.getMoveset().find(m => m?.moveId === Moves.SONIC_BOOM)?.ppUsed).toBe(1); - + expect(game.scene.getEnemyField()[0].getMoveset().find(m => m?.moveId === Moves.SONIC_BOOM)?.ppUsed).toBe(1); }); it("should not repeat enemy's move through protect", async () => { + game.override.moveset([ Moves.INSTRUCT ]); await game.classicMode.startBattle([ Species.AMOONGUSS ]); - const MoveToUse = Moves.PROTECT; - const enemyPokemon = game.scene.getEnemyPokemon()!; - game.move.changeMoveset(enemyPokemon, MoveToUse); + const enemy = game.scene.getEnemyPokemon()!; + game.move.changeMoveset(enemy, Moves.PROTECT); game.move.select(Moves.INSTRUCT); - await game.forceEnemyMove(Moves.PROTECT); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); await game.phaseInterceptor.to("TurnEndPhase", false); - expect(enemyPokemon.getLastXMoves(-1)[0].move).toBe(Moves.PROTECT); - expect(enemyPokemon.getLastXMoves(-1)[1]).toBeUndefined(); // undefined because protect failed - expect(enemyPokemon.getMoveset().find(m => m?.moveId === Moves.PROTECT)?.ppUsed).toBe(1); + expect(enemy.getLastXMoves(-1)[0].move).toBe(Moves.PROTECT); + expect(enemy.getLastXMoves(-1)[1]).toBeUndefined(); // undefined because instruct failed and didn't repeat + expect(enemy.getMoveset().find(m => m?.moveId === Moves.PROTECT)?.ppUsed).toBe(1); }); it("should not repeat enemy's charging move", async () => { game.override - .enemyMoveset([ Moves.SONIC_BOOM, Moves.HYPER_BEAM ]) - .enemyLevel(5); + .moveset([ Moves.INSTRUCT ]) + .enemyMoveset([ Moves.SONIC_BOOM, Moves.HYPER_BEAM ]); await game.classicMode.startBattle([ Species.SHUCKLE ]); const player = game.scene.getPlayerPokemon()!; - const enemyPokemon = game.scene.getEnemyPokemon()!; - enemyPokemon.battleSummonData.moveHistory = [{ move: Moves.SONIC_BOOM, targets: [ BattlerIndex.PLAYER ], result: MoveResult.SUCCESS, virtual: false }]; + const enemy = game.scene.getEnemyPokemon()!; + enemy.battleSummonData.moveHistory = [{ move: Moves.SONIC_BOOM, targets: [ BattlerIndex.PLAYER ], result: MoveResult.SUCCESS, virtual: false }]; game.move.select(Moves.INSTRUCT); await game.forceEnemyMove(Moves.HYPER_BEAM); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); await game.toNextTurn(); + // instruct fails at copying last move due to charging turn (rather than instructing sonic boom) expect(player.getLastXMoves()[0].result).toBe(MoveResult.FAIL); game.move.select(Moves.INSTRUCT); @@ -234,31 +337,162 @@ describe("Moves - Instruct", () => { expect(player.getLastXMoves()[0].result).toBe(MoveResult.FAIL); }); - it("should not repeat dance move not known by target", async () => { + it("should not repeat move since forgotten by target", async () => { game.override - .battleType("double") - .moveset([ Moves.INSTRUCT, Moves.FIERY_DANCE ]) - .enemyMoveset(Moves.SPLASH) - .enemyAbility(Abilities.DANCER); - await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); + .enemyLevel(5) + .xpMultiplier(0) + .enemySpecies(Species.WURMPLE) + .enemyMoveset(Moves.INSTRUCT); + await game.classicMode.startBattle([ Species.REGIELEKI ]); - game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.select(Moves.FIERY_DANCE, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); + const regieleki = game.scene.getPlayerPokemon()!; + // fill out moveset with random moves + game.move.changeMoveset(regieleki, [ Moves.ELECTRO_DRIFT, Moves.SPLASH, Moves.ICE_BEAM, Moves.ANCIENT_POWER ]); + + game.move.select(Moves.ELECTRO_DRIFT); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.phaseInterceptor.to("FaintPhase"); + await game.move.learnMove(Moves.ELECTROWEB); + await game.toNextWave(); + + game.move.select(Moves.SPLASH); + await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); + await game.phaseInterceptor.to("TurnEndPhase", false); + expect(game.scene.getEnemyField()[0].getLastXMoves()[0].result).toBe(MoveResult.FAIL); + }); + + it("should disregard priority of instructed move on use", async () => { + game.override + .enemyMoveset([ Moves.SPLASH, Moves.WHIRLWIND ]) + .moveset(Moves.INSTRUCT); + await game.classicMode.startBattle([ Species.LUCARIO, Species.BANETTE ]); + + const enemyPokemon = game.scene.getEnemyPokemon()!; + enemyPokemon.battleSummonData.moveHistory = [{ move: Moves.WHIRLWIND, targets: [ BattlerIndex.PLAYER ], result: MoveResult.SUCCESS, virtual: false }]; + + game.move.select(Moves.INSTRUCT); await game.forceEnemyMove(Moves.SPLASH); + await game.phaseInterceptor.to("TurnEndPhase", false); + + // lucario instructed enemy whirlwind at 0 priority to switch itself out + const instructedMove = enemyPokemon.getLastXMoves(-1)[1]; + expect(instructedMove.result).toBe(MoveResult.SUCCESS); + expect(instructedMove.move).toBe(Moves.WHIRLWIND); + expect(game.scene.getPlayerPokemon()?.species.speciesId).toBe(Species.BANETTE); + }); + + it("should respect moves' original priority for psychic terrain", async () => { + game.override. + battleType("double") + .moveset([ Moves.QUICK_ATTACK, Moves.SPLASH, Moves.INSTRUCT ]) + .enemyMoveset([ Moves.SPLASH, Moves.PSYCHIC_TERRAIN ]); + await game.classicMode.startBattle([ Species.BANETTE, Species.KLEFKI ]); + + game.move.select(Moves.QUICK_ATTACK, BattlerIndex.PLAYER, BattlerIndex.ENEMY); // succeeds due to terrain no + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.PSYCHIC_TERRAIN); + await game.toNextTurn(); + + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER); await game.setTurnOrder([ BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); await game.phaseInterceptor.to("TurnEndPhase", false); - expect(game.scene.getPlayerField()[0].getLastXMoves()[0].result).toBe(MoveResult.FAIL); + // quick attack failed when instructed + const banette = game.scene.getPlayerPokemon()!; + expect(banette.getLastXMoves(-1)[1].move).toBe(Moves.QUICK_ATTACK); + expect(banette.getLastXMoves(-1)[1].result).toBe(MoveResult.FAIL); + }); + + it("should still work w/ prankster in psychic terrain", async () => { + game.override. + battleType("double") + .enemyMoveset([ Moves.SPLASH, Moves.PSYCHIC_TERRAIN ]); + await game.classicMode.startBattle([ Species.BANETTE, Species.KLEFKI ]); + + const [ banette, klefki ] = game.scene.getPlayerField()!; + game.move.changeMoveset(banette, [ Moves.VINE_WHIP, Moves.SPLASH ]); + game.move.changeMoveset(klefki, [ Moves.INSTRUCT, Moves.SPLASH ]); + + game.move.select(Moves.VINE_WHIP, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); + await game.forceEnemyMove(Moves.SPLASH); + await game.forceEnemyMove(Moves.PSYCHIC_TERRAIN); + await game.toNextTurn(); + + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER); // copies vine whip + await game.setTurnOrder([ BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("TurnEndPhase", false); + expect(banette.getLastXMoves(-1)[1].move).toBe(Moves.VINE_WHIP); + expect(banette.getLastXMoves(-1)[2].move).toBe(Moves.VINE_WHIP); + expect(banette.getMoveset().find(m => m?.moveId === Moves.VINE_WHIP )?.ppUsed).toBe(2); + }); + + it("should cause spread moves to correctly hit targets in doubles after singles", async () => { + game.override + .battleType("even-doubles") + .moveset([ Moves.BREAKING_SWIPE, Moves.INSTRUCT, Moves.SPLASH ]) + .enemyMoveset(Moves.SONIC_BOOM) + .enemySpecies(Species.AXEW) + .startingLevel(500); + await game.classicMode.startBattle([ Species.KORAIDON, Species.KLEFKI ]); + + const koraidon = game.scene.getPlayerField()[0]!; + + game.move.select(Moves.BREAKING_SWIPE); + await game.phaseInterceptor.to("TurnEndPhase", false); + expect(koraidon.getInverseHp()).toBe(0); + expect(koraidon.getLastXMoves(-1)[0].targets).toEqual([ BattlerIndex.ENEMY ]); + await game.toNextWave(); + + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER); + await game.setTurnOrder([ BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("TurnEndPhase", false); + // did not take damage since enemies died beforehand; + // last move used hit both enemies + expect(koraidon.getInverseHp()).toBe(0); + expect(koraidon.getLastXMoves(-1)[1].targets?.sort()).toEqual([ BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + }); + + it("should cause AoE moves to correctly hit everyone in doubles after singles", async () => { + game.override + .battleType("even-doubles") + .moveset([ Moves.BRUTAL_SWING, Moves.INSTRUCT, Moves.SPLASH ]) + .enemySpecies(Species.AXEW) + .enemyMoveset(Moves.SONIC_BOOM) + .startingLevel(500); + await game.classicMode.startBattle([ Species.KORAIDON, Species.KLEFKI ]); + + const koraidon = game.scene.getPlayerField()[0]!; + + game.move.select(Moves.BRUTAL_SWING); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.phaseInterceptor.to("TurnEndPhase", false); + expect(koraidon.getInverseHp()).toBe(0); + expect(koraidon.getLastXMoves(-1)[0].targets).toEqual([ BattlerIndex.ENEMY ]); + await game.toNextWave(); + + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.PLAYER); + await game.setTurnOrder([ BattlerIndex.PLAYER_2, BattlerIndex.PLAYER, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); + await game.phaseInterceptor.to("TurnEndPhase", false); + // did not take damage since enemies died beforehand; + // last move used hit everything around it + expect(koraidon.getInverseHp()).toBe(0); + expect(koraidon.getLastXMoves(-1)[1].targets?.sort()).toEqual([ BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); }); it("should cause multi-hit moves to hit the appropriate number of times in singles", async () => { game.override .enemyAbility(Abilities.SKILL_LINK) + .moveset([ Moves.SPLASH, Moves.INSTRUCT ]) .enemyMoveset(Moves.BULLET_SEED); await game.classicMode.startBattle([ Species.BULBASAUR ]); - const player = game.scene.getPlayerPokemon()!; + const bulbasaur = game.scene.getPlayerPokemon()!; game.move.select(Moves.SPLASH); await game.toNextTurn(); @@ -267,34 +501,35 @@ describe("Moves - Instruct", () => { await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); await game.phaseInterceptor.to("BerryPhase"); - expect(player.turnData.attacksReceived.length).toBe(10); + expect(bulbasaur.turnData.attacksReceived.length).toBe(10); await game.toNextTurn(); game.move.select(Moves.INSTRUCT); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.PLAYER ]); await game.phaseInterceptor.to("BerryPhase"); - expect(player.turnData.attacksReceived.length).toBe(10); + expect(bulbasaur.turnData.attacksReceived.length).toBe(10); }); it("should cause multi-hit moves to hit the appropriate number of times in doubles", async () => { game.override .battleType("double") .enemyAbility(Abilities.SKILL_LINK) + .moveset([ Moves.SPLASH, Moves.INSTRUCT ]) .enemyMoveset([ Moves.BULLET_SEED, Moves.SPLASH ]) .enemyLevel(5); await game.classicMode.startBattle([ Species.BULBASAUR, Species.IVYSAUR ]); const [ , ivysaur ] = game.scene.getPlayerField(); - game.move.select(Moves.SPLASH); - game.move.select(Moves.SPLASH, 1); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER); + game.move.select(Moves.SPLASH, BattlerIndex.PLAYER_2); await game.forceEnemyMove(Moves.BULLET_SEED, BattlerIndex.PLAYER_2); await game.forceEnemyMove(Moves.SPLASH); await game.toNextTurn(); - game.move.select(Moves.INSTRUCT, 0, BattlerIndex.ENEMY); - game.move.select(Moves.INSTRUCT, 1, BattlerIndex.ENEMY); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); await game.forceEnemyMove(Moves.BULLET_SEED, BattlerIndex.PLAYER_2); await game.forceEnemyMove(Moves.SPLASH); await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2 ]); @@ -303,8 +538,8 @@ describe("Moves - Instruct", () => { expect(ivysaur.turnData.attacksReceived.length).toBe(15); await game.toNextTurn(); - game.move.select(Moves.INSTRUCT, 0, BattlerIndex.ENEMY); - game.move.select(Moves.INSTRUCT, 1, BattlerIndex.ENEMY); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.select(Moves.INSTRUCT, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY); await game.forceEnemyMove(Moves.BULLET_SEED, BattlerIndex.PLAYER_2); await game.forceEnemyMove(Moves.SPLASH); await game.setTurnOrder([ BattlerIndex.ENEMY, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER, BattlerIndex.PLAYER_2 ]); diff --git a/src/test/ui/starter-select.test.ts b/src/test/ui/starter-select.test.ts index f80990f6dc3..15b5f643539 100644 --- a/src/test/ui/starter-select.test.ts +++ b/src/test/ui/starter-select.test.ts @@ -362,8 +362,9 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.V); - handler.processInput(Button.V); + handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); @@ -423,7 +424,8 @@ describe("UI - Starter select", () => { const handler = game.scene.ui.getHandler() as StarterSelectUiHandler; handler.processInput(Button.RIGHT); handler.processInput(Button.LEFT); - handler.processInput(Button.V); + handler.processInput(Button.CYCLE_SHINY); + handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.ACTION); game.phaseInterceptor.unlock(); }); diff --git a/src/test/utils/gameManager.ts b/src/test/utils/gameManager.ts index 9fda3afa56e..8e595212898 100644 --- a/src/test/utils/gameManager.ts +++ b/src/test/utils/gameManager.ts @@ -129,9 +129,10 @@ export default class GameManager { /** * Adds an action to be executed on the next prompt. + * This can be used to (among other things) simulate inputs or run functions mid-phase. * @param phaseTarget - The target phase. * @param mode - The mode to wait for. - * @param callback - The callback to execute. + * @param callback - The callback function to execute on next prompt. * @param expireFn - Optional function to determine if the prompt has expired. */ onNextPrompt(phaseTarget: string, mode: Mode, callback: () => void, expireFn?: () => void, awaitingActionInput: boolean = false) { @@ -400,6 +401,11 @@ export default class GameManager { return updateUserInfo(); } + /** + * Faints a player or enemy pokemon instantly by setting their HP to 0. + * @param pokemon The player/enemy pokemon being fainted + * @returns A promise that resolves once the fainted pokemon's FaintPhase finishes running. + */ async killPokemon(pokemon: PlayerPokemon | EnemyPokemon) { return new Promise(async (resolve, reject) => { pokemon.hp = 0; @@ -453,8 +459,9 @@ export default class GameManager { } /** - * Intercepts `TurnStartPhase` and mocks the getSpeedOrder's return value {@linkcode TurnStartPhase.getSpeedOrder} - * Used to modify the turn order. + * Intercepts `TurnStartPhase` and mocks {@linkcode TurnStartPhase.getSpeedOrder}'s return value. + * Used to manually modify Pokemon turn order. + * Note: This *DOES NOT* account for priority, only speed. * @param {BattlerIndex[]} order The turn order to set * @example * ```ts @@ -468,7 +475,7 @@ export default class GameManager { } /** - * Removes all held items from enemy pokemon + * Removes all held items from enemy pokemon. */ removeEnemyHeldItems(): void { this.scene.clearEnemyHeldItemModifiers(); diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index 15815c96691..822c42163b1 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -160,7 +160,7 @@ export class OverridesHelper extends GameManagerHelper { } /** - * Override the player (pokemon) {@linkcode Abilities | ability} + * Override the player (pokemon) {@linkcode Abilities | ability}. * @param ability the (pokemon) {@linkcode Abilities | ability} to set * @returns `this` */ diff --git a/src/timed-event-manager.ts b/src/timed-event-manager.ts index 8633460d98c..7a9f0e59993 100644 --- a/src/timed-event-manager.ts +++ b/src/timed-event-manager.ts @@ -102,7 +102,7 @@ const timedEvents: TimedEvent[] = [ endDate: new Date(Date.UTC(2025, 1, 3, 0)), bannerKey: "yearofthesnakeevent-", scale: 0.21, - availableLangs: [], + availableLangs: [ "en", "de", "it", "fr", "ja", "ko", "es-ES", "pt-BR", "zh-CN" ], eventEncounters: [ { species: Species.EKANS }, { species: Species.ONIX }, diff --git a/src/ui/starter-select-ui-handler.ts b/src/ui/starter-select-ui-handler.ts index dd427802083..de7b182c5c1 100644 --- a/src/ui/starter-select-ui-handler.ts +++ b/src/ui/starter-select-ui-handler.ts @@ -20,7 +20,6 @@ import { pokemonFormLevelMoves, pokemonSpeciesLevelMoves } from "#app/data/balan import type PokemonSpecies from "#app/data/pokemon-species"; import { allSpecies, getPokemonSpeciesForm, getPokerusStarters } from "#app/data/pokemon-species"; import { getStarterValueFriendshipCap, speciesStarterCosts, POKERUS_STARTER_COUNT } from "#app/data/balance/starters"; -import { starterPassiveAbilities } from "#app/data/balance/passives"; import { Type } from "#enums/type"; import { GameModes } from "#app/game-mode"; import type { DexAttrProps, DexEntry, StarterMoveset, StarterAttributes, StarterPreferences } from "#app/system/game-data"; @@ -272,14 +271,12 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private abilityIconElement: Phaser.GameObjects.Sprite; private genderIconElement: Phaser.GameObjects.Sprite; private natureIconElement: Phaser.GameObjects.Sprite; - private variantIconElement: Phaser.GameObjects.Sprite; private goFilterIconElement: Phaser.GameObjects.Sprite; private shinyLabel: Phaser.GameObjects.Text; private formLabel: Phaser.GameObjects.Text; private genderLabel: Phaser.GameObjects.Text; private abilityLabel: Phaser.GameObjects.Text; private natureLabel: Phaser.GameObjects.Text; - private variantLabel: Phaser.GameObjects.Text; private goFilterLabel: Phaser.GameObjects.Text; private starterSelectMessageBox: Phaser.GameObjects.NineSlice; @@ -315,7 +312,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { private canCycleGender: boolean; private canCycleAbility: boolean; private canCycleNature: boolean; - private canCycleVariant: boolean; private assetLoadCancelled: BooleanHolder | null; public cursorObj: Phaser.GameObjects.Image; @@ -871,13 +867,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.natureLabel = addTextObject(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleNature"), TextStyle.PARTY, { fontSize: instructionTextSize }); this.natureLabel.setName("text-nature-label"); - this.variantIconElement = new Phaser.GameObjects.Sprite(globalScene, this.instructionRowX, this.instructionRowY, "keyboard", "V.png"); - this.variantIconElement.setName("sprite-variant-icon-element"); - this.variantIconElement.setScale(0.675); - this.variantIconElement.setOrigin(0.0, 0.0); - this.variantLabel = addTextObject(this.instructionRowX + this.instructionRowTextOffset, this.instructionRowY, i18next.t("starterSelectUiHandler:cycleVariant"), TextStyle.PARTY, { fontSize: instructionTextSize }); - this.variantLabel.setName("text-variant-label"); - this.goFilterIconElement = new Phaser.GameObjects.Sprite(globalScene, this.filterInstructionRowX, this.filterInstructionRowY, "keyboard", "C.png"); this.goFilterIconElement.setName("sprite-goFilter-icon-element"); this.goFilterIconElement.setScale(0.675); @@ -1854,7 +1843,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (!(passiveAttr & PassiveAttr.UNLOCKED)) { const passiveCost = getPassiveCandyCount(speciesStarterCosts[this.lastSpecies.speciesId]); options.push({ - label: `x${passiveCost} ${i18next.t("starterSelectUiHandler:unlockPassive")} (${allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]].name})`, + label: `x${passiveCost} ${i18next.t("starterSelectUiHandler:unlockPassive")} (${allAbilities[this.lastSpecies.getPassiveAbility()].name})`, handler: () => { if (Overrides.FREE_CANDY_UPGRADE_OVERRIDE || candyCount >= passiveCost) { starterData.passiveAttr |= PassiveAttr.UNLOCKED | PassiveAttr.ENABLED; @@ -1999,53 +1988,56 @@ export default class StarterSelectUiHandler extends MessageUiHandler { switch (button) { case Button.CYCLE_SHINY: if (this.canCycleShiny) { - starterAttributes.shiny = starterAttributes.shiny !== undefined ? !starterAttributes.shiny : false; - - if (starterAttributes.shiny) { - // Change to shiny, we need to get the proper default variant + if (starterAttributes.shiny === false) { + // If not shiny, we change to shiny and get the proper default variant const newProps = globalScene.gameData.getSpeciesDexAttrProps(this.lastSpecies, this.getCurrentDexProps(this.lastSpecies.speciesId)); const newVariant = starterAttributes.variant ? starterAttributes.variant as Variant : newProps.variant; this.setSpeciesDetails(this.lastSpecies, { shiny: true, variant: newVariant }); globalScene.playSound("se/sparkle"); - // Set the variant label to the shiny tint + // Cycle tint based on current sprite tint const tint = getVariantTint(newVariant); this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant)); this.pokemonShinyIcon.setTint(tint); this.pokemonShinyIcon.setVisible(true); + + starterAttributes.shiny = true; } else { - this.setSpeciesDetails(this.lastSpecies, { shiny: false, variant: 0 }); - this.pokemonShinyIcon.setVisible(false); - success = true; - } - } - break; - case Button.V: - if (this.canCycleVariant) { - let newVariant = props.variant; - do { - newVariant = (newVariant + 1) % 3; - if (newVariant === 0) { - if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.DEFAULT_VARIANT) { // TODO: is this bang correct? - break; - } - } else if (newVariant === 1) { - if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_2) { // TODO: is this bang correct? - break; + // If shiny, we update the variant + let newVariant = props.variant; + do { + newVariant = (newVariant + 1) % 3; + if (newVariant === 0) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.DEFAULT_VARIANT) { // TODO: is this bang correct? + break; + } + } else if (newVariant === 1) { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_2) { // TODO: is this bang correct? + break; + } + } else { + if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) { // TODO: is this bang correct? + break; + } } + } while (newVariant !== props.variant); + starterAttributes.variant = newVariant; // store the selected variant + // If going to a higher variant, display that + if (newVariant > props.variant) { + this.setSpeciesDetails(this.lastSpecies, { variant: newVariant as Variant }); + // Cycle tint based on current sprite tint + const tint = getVariantTint(newVariant as Variant); + this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant)); + this.pokemonShinyIcon.setTint(tint); + success = true; + // If we have run out of variants, go back to non shiny } else { - if (this.speciesStarterDexEntry!.caughtAttr & DexAttr.VARIANT_3) { // TODO: is this bang correct? - break; - } + this.setSpeciesDetails(this.lastSpecies, { shiny: false, variant: 0 }); + this.pokemonShinyIcon.setVisible(false); + success = true; + starterAttributes.shiny = false; } - } while (newVariant !== props.variant); - starterAttributes.variant = newVariant; // store the selected variant - this.setSpeciesDetails(this.lastSpecies, { variant: newVariant as Variant }); - // Cycle tint based on current sprite tint - const tint = getVariantTint(newVariant as Variant); - this.pokemonShinyIcon.setFrame(getVariantIcon(newVariant as Variant)); - this.pokemonShinyIcon.setTint(tint); - success = true; + } } break; case Button.CYCLE_FORM: @@ -2372,9 +2364,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { case SettingKeyboard.Button_Cycle_Nature: iconPath = "N.png"; break; - case SettingKeyboard.Button_Cycle_Variant: - iconPath = "V.png"; - break; case SettingKeyboard.Button_Stats: iconPath = "C.png"; break; @@ -2455,9 +2444,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { if (this.canCycleNature) { this.updateButtonIcon(SettingKeyboard.Button_Cycle_Nature, gamepadType, this.natureIconElement, this.natureLabel); } - if (this.canCycleVariant) { - this.updateButtonIcon(SettingKeyboard.Button_Cycle_Variant, gamepadType, this.variantIconElement, this.variantLabel); - } } // if filter mode is inactivated and gamepadType is not undefined, update the button icons @@ -3262,12 +3248,8 @@ export default class StarterSelectUiHandler extends MessageUiHandler { const isNonShinyCaught = !!(caughtAttr & DexAttr.NON_SHINY); const isShinyCaught = !!(caughtAttr & DexAttr.SHINY); - const isVariant1Caught = isShinyCaught && !!(caughtAttr & DexAttr.DEFAULT_VARIANT); - const isVariant2Caught = isShinyCaught && !!(caughtAttr & DexAttr.VARIANT_2); - const isVariant3Caught = isShinyCaught && !!(caughtAttr & DexAttr.VARIANT_3); this.canCycleShiny = isNonShinyCaught && isShinyCaught; - this.canCycleVariant = !!shiny && [ isVariant1Caught, isVariant2Caught, isVariant3Caught ].filter(v => v).length > 1; const isMaleCaught = !!(caughtAttr & DexAttr.MALE); const isFemaleCaught = !!(caughtAttr & DexAttr.FEMALE); @@ -3312,7 +3294,7 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.pokemonAbilityText.setShadowColor(this.getTextColor(!isHidden ? TextStyle.SUMMARY_ALT : TextStyle.SUMMARY_GOLD, true)); const passiveAttr = globalScene.gameData.starterData[species.speciesId].passiveAttr; - const passiveAbility = allAbilities[starterPassiveAbilities[this.lastSpecies.speciesId]]; + const passiveAbility = allAbilities[this.lastSpecies.getPassiveAbility(formIndex)]; if (this.pokemonAbilityText.visible) { if (this.activeTooltip === "ABILITY") { @@ -3826,8 +3808,6 @@ export default class StarterSelectUiHandler extends MessageUiHandler { this.abilityLabel.setVisible(false); this.natureIconElement.setVisible(false); this.natureLabel.setVisible(false); - this.variantIconElement.setVisible(false); - this.variantLabel.setVisible(false); this.goFilterIconElement.setVisible(false); this.goFilterLabel.setVisible(false); } diff --git a/src/ui/ui.ts b/src/ui/ui.ts index 9e8c52b1d24..682ef836765 100644 --- a/src/ui/ui.ts +++ b/src/ui/ui.ts @@ -269,7 +269,7 @@ export default class UI extends Phaser.GameObjects.Container { return false; } - if ([ Mode.CONFIRM, Mode.COMMAND, Mode.FIGHT, Mode.MESSAGE ].includes(this.mode)) { + if ([ Mode.CONFIRM, Mode.COMMAND, Mode.FIGHT, Mode.MESSAGE, Mode.TARGET_SELECT ].includes(this.mode)) { globalScene?.processInfoButton(pressed); return true; } @@ -277,6 +277,11 @@ export default class UI extends Phaser.GameObjects.Container { return true; } + /** + * Process a player input of a button (delivering it to the current UI handler for processing) + * @param button The {@linkcode Button} being inputted + * @returns true if the input attempt succeeds + */ processInput(button: Button): boolean { if (this.overlayActive) { return false;