From 87b78e6b706924d435f57924c991d9705b93972b Mon Sep 17 00:00:00 2001 From: damocleas Date: Sat, 22 Mar 2025 15:47:41 -0400 Subject: [PATCH 1/4] [Balance] [Move] Made more moves callable with Metronome, Assist, Sleep Talk, and Copycat (#5549) Update invalid-moves.ts --- src/data/moves/invalid-moves.ts | 104 -------------------------------- 1 file changed, 104 deletions(-) diff --git a/src/data/moves/invalid-moves.ts b/src/data/moves/invalid-moves.ts index 50f815b9e58..64052902d9d 100644 --- a/src/data/moves/invalid-moves.ts +++ b/src/data/moves/invalid-moves.ts @@ -3,144 +3,47 @@ import { Moves } from "#enums/moves"; /** Set of moves that cannot be called by {@linkcode Moves.METRONOME Metronome} */ export const invalidMetronomeMoves: ReadonlySet = new Set([ Moves.AFTER_YOU, - Moves.APPLE_ACID, - Moves.ARMOR_CANNON, Moves.ASSIST, - Moves.ASTRAL_BARRAGE, - Moves.AURA_WHEEL, Moves.BANEFUL_BUNKER, Moves.BEAK_BLAST, - Moves.BEHEMOTH_BASH, - Moves.BEHEMOTH_BLADE, Moves.BELCH, Moves.BESTOW, - Moves.BLAZING_TORQUE, - Moves.BODY_PRESS, - Moves.BRANCH_POKE, - Moves.BREAKING_SWIPE, - Moves.CELEBRATE, - Moves.CHATTER, - Moves.CHILLING_WATER, - Moves.CHILLY_RECEPTION, - Moves.CLANGOROUS_SOUL, - Moves.COLLISION_COURSE, - Moves.COMBAT_TORQUE, Moves.COMEUPPANCE, Moves.COPYCAT, Moves.COUNTER, - Moves.COVET, Moves.CRAFTY_SHIELD, - Moves.DECORATE, Moves.DESTINY_BOND, Moves.DETECT, - Moves.DIAMOND_STORM, - Moves.DOODLE, - Moves.DOUBLE_IRON_BASH, - Moves.DOUBLE_SHOCK, - Moves.DRAGON_ASCENT, - Moves.DRAGON_ENERGY, - Moves.DRUM_BEATING, - Moves.DYNAMAX_CANNON, - Moves.ELECTRO_DRIFT, Moves.ENDURE, - Moves.ETERNABEAM, - Moves.FALSE_SURRENDER, Moves.FEINT, - Moves.FIERY_WRATH, - Moves.FILLET_AWAY, Moves.FLEUR_CANNON, Moves.FOCUS_PUNCH, Moves.FOLLOW_ME, - Moves.FREEZE_SHOCK, - Moves.FREEZING_GLARE, - Moves.GLACIAL_LANCE, - Moves.GRAV_APPLE, Moves.HELPING_HAND, - Moves.HOLD_HANDS, - Moves.HYPER_DRILL, - Moves.HYPERSPACE_FURY, - Moves.HYPERSPACE_HOLE, - Moves.ICE_BURN, Moves.INSTRUCT, - Moves.JET_PUNCH, - Moves.JUNGLE_HEALING, Moves.KINGS_SHIELD, - Moves.LIFE_DEW, - Moves.LIGHT_OF_RUIN, - Moves.MAKE_IT_RAIN, - Moves.MAGICAL_TORQUE, Moves.MAT_BLOCK, Moves.ME_FIRST, - Moves.METEOR_ASSAULT, Moves.METRONOME, Moves.MIMIC, - Moves.MIND_BLOWN, Moves.MIRROR_COAT, Moves.MIRROR_MOVE, - Moves.MOONGEIST_BEAM, - Moves.NATURE_POWER, - Moves.NATURES_MADNESS, - Moves.NOXIOUS_TORQUE, Moves.OBSTRUCT, - Moves.ORDER_UP, - Moves.ORIGIN_PULSE, - Moves.OVERDRIVE, - Moves.PHOTON_GEYSER, - Moves.PLASMA_FISTS, - Moves.POPULATION_BOMB, - Moves.POUNCE, - Moves.POWER_SHIFT, - Moves.PRECIPICE_BLADES, Moves.PROTECT, - Moves.PYRO_BALL, Moves.QUASH, Moves.QUICK_GUARD, - Moves.RAGE_FIST, Moves.RAGE_POWDER, - Moves.RAGING_BULL, - Moves.RAGING_FURY, - Moves.RELIC_SONG, Moves.REVIVAL_BLESSING, - Moves.RUINATION, - Moves.SALT_CURE, - Moves.SECRET_SWORD, - Moves.SHED_TAIL, Moves.SHELL_TRAP, Moves.SILK_TRAP, Moves.SKETCH, Moves.SLEEP_TALK, - Moves.SNAP_TRAP, - Moves.SNARL, Moves.SNATCH, Moves.SNORE, - Moves.SNOWSCAPE, - Moves.SPECTRAL_THIEF, - Moves.SPICY_EXTRACT, Moves.SPIKY_SHIELD, - Moves.SPIRIT_BREAK, Moves.SPOTLIGHT, - Moves.STEAM_ERUPTION, - Moves.STEEL_BEAM, - Moves.STRANGE_STEAM, Moves.STRUGGLE, - Moves.SUNSTEEL_STRIKE, - Moves.SURGING_STRIKES, - Moves.SWITCHEROO, - Moves.TECHNO_BLAST, - Moves.TERA_STARSTORM, - Moves.THIEF, - Moves.THOUSAND_ARROWS, - Moves.THOUSAND_WAVES, - Moves.THUNDER_CAGE, - Moves.THUNDEROUS_KICK, - Moves.TIDY_UP, - Moves.TRAILBLAZE, Moves.TRANSFORM, - Moves.TRICK, - Moves.TWIN_BEAM, - Moves.V_CREATE, - Moves.WICKED_BLOW, - Moves.WICKED_TORQUE, Moves.WIDE_GUARD, ]); @@ -157,7 +60,6 @@ export const invalidAssistMoves: ReadonlySet = new Set([ Moves.CIRCLE_THROW, Moves.COPYCAT, Moves.COUNTER, - Moves.COVET, Moves.DESTINY_BOND, Moves.DETECT, Moves.DIG, @@ -192,7 +94,6 @@ export const invalidAssistMoves: ReadonlySet = new Set([ Moves.SPOTLIGHT, Moves.STRUGGLE, Moves.SWITCHEROO, - Moves.THIEF, Moves.TRANSFORM, Moves.TRICK, Moves.WHIRLWIND, @@ -208,7 +109,6 @@ export const invalidSleepTalkMoves: ReadonlySet = new Set([ Moves.COPYCAT, Moves.DIG, Moves.DIVE, - Moves.DYNAMAX_CANNON, Moves.FREEZE_SHOCK, Moves.FLY, Moves.FOCUS_PUNCH, @@ -238,15 +138,12 @@ export const invalidCopycatMoves: ReadonlySet = new Set([ Moves.ASSIST, Moves.BANEFUL_BUNKER, Moves.BEAK_BLAST, - Moves.BEHEMOTH_BASH, - Moves.BEHEMOTH_BLADE, Moves.BESTOW, Moves.CELEBRATE, Moves.CHATTER, Moves.CIRCLE_THROW, Moves.COPYCAT, Moves.COUNTER, - Moves.COVET, Moves.DESTINY_BOND, Moves.DETECT, Moves.DRAGON_TAIL, @@ -274,7 +171,6 @@ export const invalidCopycatMoves: ReadonlySet = new Set([ Moves.SPOTLIGHT, Moves.STRUGGLE, Moves.SWITCHEROO, - Moves.THIEF, Moves.TRANSFORM, Moves.TRICK, Moves.WHIRLWIND, From d0a9672e91de1c7fcf87d1f81eede4faa7dc6a86 Mon Sep 17 00:00:00 2001 From: damocleas Date: Sat, 22 Mar 2025 16:18:08 -0400 Subject: [PATCH 2/4] [Balance] [Move] Add Fleur Cannon as a Metronome callableove (#5551) * Update invalid-moves.ts * forgot fleur cannon --- src/data/moves/invalid-moves.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/moves/invalid-moves.ts b/src/data/moves/invalid-moves.ts index 64052902d9d..7284831528a 100644 --- a/src/data/moves/invalid-moves.ts +++ b/src/data/moves/invalid-moves.ts @@ -16,7 +16,6 @@ export const invalidMetronomeMoves: ReadonlySet = new Set([ Moves.DETECT, Moves.ENDURE, Moves.FEINT, - Moves.FLEUR_CANNON, Moves.FOCUS_PUNCH, Moves.FOLLOW_ME, Moves.HELPING_HAND, From 4b8f1df8cd4bc98b985f05931befa11183e0443d Mon Sep 17 00:00:00 2001 From: NightKev <34855794+DayKev@users.noreply.github.com> Date: Sat, 22 Mar 2025 14:46:58 -0700 Subject: [PATCH 3/4] [Bug] Add list of uncallable moves for Mirror Move (#5552) * Add list of uncallable moves for Mirror Move * Mirror Move now respects the uncallable moves list --------- Co-authored-by: damocleas --- src/data/moves/invalid-moves.ts | 66 +++++++++++++++++++++++++++++++++ src/data/moves/move.ts | 7 ++-- 2 files changed, 70 insertions(+), 3 deletions(-) diff --git a/src/data/moves/invalid-moves.ts b/src/data/moves/invalid-moves.ts index 7284831528a..5cd45de7939 100644 --- a/src/data/moves/invalid-moves.ts +++ b/src/data/moves/invalid-moves.ts @@ -174,3 +174,69 @@ export const invalidCopycatMoves: ReadonlySet = new Set([ Moves.TRICK, Moves.WHIRLWIND, ]); + +export const invalidMirrorMoveMoves: ReadonlySet = new Set([ + Moves.ACUPRESSURE, + Moves.AFTER_YOU, + Moves.AROMATIC_MIST, + Moves.BEAK_BLAST, + Moves.BELCH, + Moves.CHILLY_RECEPTION, + Moves.COACHING, + Moves.CONVERSION_2, + Moves.COUNTER, + Moves.CRAFTY_SHIELD, + Moves.CURSE, + Moves.DECORATE, + Moves.DOODLE, + Moves.DOOM_DESIRE, + Moves.DRAGON_CHEER, + Moves.ELECTRIC_TERRAIN, + Moves.FINAL_GAMBIT, + Moves.FLORAL_HEALING, + Moves.FLOWER_SHIELD, + Moves.FOCUS_PUNCH, + Moves.FUTURE_SIGHT, + Moves.GEAR_UP, + Moves.GRASSY_TERRAIN, + Moves.GRAVITY, + Moves.GUARD_SPLIT, + Moves.HAIL, + Moves.HAZE, + Moves.HEAL_PULSE, + Moves.HELPING_HAND, + Moves.HOLD_HANDS, + Moves.INSTRUCT, + Moves.ION_DELUGE, + Moves.MAGNETIC_FLUX, + Moves.MAT_BLOCK, + Moves.ME_FIRST, + Moves.MIMIC, + Moves.MIRROR_COAT, + Moves.MIRROR_MOVE, + Moves.MIST, + Moves.MISTY_TERRAIN, + Moves.MUD_SPORT, + Moves.PERISH_SONG, + Moves.POWER_SPLIT, + Moves.PSYCH_UP, + Moves.PSYCHIC_TERRAIN, + Moves.PURIFY, + Moves.QUICK_GUARD, + Moves.RAIN_DANCE, + Moves.REFLECT_TYPE, + Moves.ROLE_PLAY, + Moves.ROTOTILLER, + Moves.SANDSTORM, + Moves.SHELL_TRAP, + Moves.SKETCH, + Moves.SNOWSCAPE, + Moves.SPIT_UP, + Moves.SPOTLIGHT, + Moves.STRUGGLE, + Moves.SUNNY_DAY, + Moves.TEATIME, + Moves.TRANSFORM, + Moves.WATER_SPORT, + Moves.WIDE_GUARD, +]); diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 3d80b6b3f4c..c7b5b4aec6b 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -125,7 +125,7 @@ import { MoveTarget } from "#enums/MoveTarget"; import { MoveFlags } from "#enums/MoveFlags"; import { MoveEffectTrigger } from "#enums/MoveEffectTrigger"; import { MultiHitType } from "#enums/MultiHitType"; -import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidSleepTalkMoves } from "./invalid-moves"; +import { invalidAssistMoves, invalidCopycatMoves, invalidMetronomeMoves, invalidMirrorMoveMoves, invalidSleepTalkMoves } from "./invalid-moves"; type MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => boolean; type UserMoveConditionFunc = (user: Pokemon, move: Move) => boolean; @@ -6966,7 +6966,8 @@ export class CopyMoveAttr extends CallMoveAttr { getCondition(): MoveConditionFunc { return (user, target, move) => { if (this.mirrorMove) { - return target.getMoveHistory().length !== 0; + const lastMove = target.getLastXMoves()[0]?.move; + return !!lastMove && !this.invalidMoves.has(lastMove); } else { const lastMove = globalScene.currentBattle.lastMove; return lastMove !== undefined && !this.invalidMoves.has(lastMove); @@ -8562,7 +8563,7 @@ export function initMoves() { new SelfStatusMove(Moves.METRONOME, PokemonType.NORMAL, -1, 10, -1, 0, 1) .attr(RandomMoveAttr, invalidMetronomeMoves), new StatusMove(Moves.MIRROR_MOVE, PokemonType.FLYING, -1, 20, -1, 0, 1) - .attr(CopyMoveAttr, true), + .attr(CopyMoveAttr, true, invalidMirrorMoveMoves), new AttackMove(Moves.SELF_DESTRUCT, PokemonType.NORMAL, MoveCategory.PHYSICAL, 200, 100, 5, -1, 0, 1) .attr(SacrificialAttr) .makesContact(false) From 1e876ec595195d4094d3cccc076c8bd7d98a5099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Serrado=20Marques?= Date: Sun, 23 Mar 2025 19:07:14 +0000 Subject: [PATCH 4/4] [BUG] fixes #5472 - transform on reload (#5508) * [BUG] fixes #5472 - transform on reload * Fix the bug where transformed pokemon failed to load sprite on reload if it was not the base form * Now properly loads the transformed sprite assets during summon phase --- src/field/pokemon.ts | 2 +- src/phases/summon-phase.ts | 4 ++ src/system/pokemon-data.ts | 29 +++++++++++++- test/abilities/imposter.test.ts | 59 +++++++++++++++++++++++++++ test/moves/transform.test.ts | 70 ++++++++++++++++++++++++++++++++- 5 files changed, 160 insertions(+), 4 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 890c6bab0d6..42fe1dc1010 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -5575,7 +5575,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } this.resetBattleSummonData(); if (this.summonDataPrimer) { - for (const k of Object.keys(this.summonData)) { + for (const k of Object.keys(this.summonDataPrimer)) { if (this.summonDataPrimer[k]) { this.summonData[k] = this.summonDataPrimer[k]; } diff --git a/src/phases/summon-phase.ts b/src/phases/summon-phase.ts index 31cd2645e68..42463e7edb0 100644 --- a/src/phases/summon-phase.ts +++ b/src/phases/summon-phase.ts @@ -195,6 +195,10 @@ export class SummonPhase extends PartyMemberPokemonPhase { pokemon.cry(pokemon.getHpRatio() > 0.25 ? undefined : { rate: 0.85 }); pokemon.getSprite().clearTint(); pokemon.resetSummonData(); + // necessary to stay transformed during wild waves + if (pokemon.summonData?.speciesForm) { + pokemon.loadAssets(false); + } globalScene.time.delayedCall(1000, () => this.end()); }, }); diff --git a/src/system/pokemon-data.ts b/src/system/pokemon-data.ts index 4e694bc1189..76f5526e407 100644 --- a/src/system/pokemon-data.ts +++ b/src/system/pokemon-data.ts @@ -3,7 +3,7 @@ import { globalScene } from "#app/global-scene"; import type { Gender } from "../data/gender"; import type { Nature } from "#enums/nature"; import type { PokeballType } from "#enums/pokeball"; -import { getPokemonSpecies } from "../data/pokemon-species"; +import { getPokemonSpecies, getPokemonSpeciesForm } from "../data/pokemon-species"; import { Status } from "../data/status-effect"; import Pokemon, { EnemyPokemon, PokemonMove, PokemonSummonData } from "../field/pokemon"; import { TrainerSlot } from "../data/trainer-config"; @@ -14,6 +14,7 @@ import { Moves } from "#enums/moves"; import type { Species } from "#enums/species"; import { CustomPokemonData } from "#app/data/custom-pokemon-data"; import type { PokemonType } from "#enums/pokemon-type"; +import { getSpeciesFormChangeMessage } from "#app/data/pokemon-forms"; export default class PokemonData { public id: number; @@ -63,6 +64,7 @@ export default class PokemonData { public bossSegments?: number; public summonData: PokemonSummonData; + public summonDataSpeciesFormIndex: number; /** Data that can customize a Pokemon in non-standard ways from its Species */ public customPokemonData: CustomPokemonData; @@ -145,8 +147,9 @@ export default class PokemonData { this.moveset = sourcePokemon.moveset; if (!forHistory) { this.status = sourcePokemon.status; - if (this.player) { + if (this.player && sourcePokemon.summonData) { this.summonData = sourcePokemon.summonData; + this.summonDataSpeciesFormIndex = this.getSummonDataSpeciesFormIndex(); } } } else { @@ -170,6 +173,8 @@ export default class PokemonData { this.summonData.ability = source.summonData.ability; this.summonData.moveset = source.summonData.moveset?.map(m => PokemonMove.loadMove(m)); this.summonData.types = source.summonData.types; + this.summonData.speciesForm = source.summonData.speciesForm; + this.summonDataSpeciesFormIndex = source.summonDataSpeciesFormIndex; if (source.summonData.tags) { this.summonData.tags = source.summonData.tags?.map(t => loadBattlerTag(t)); @@ -213,8 +218,28 @@ export default class PokemonData { this, ); if (this.summonData) { + // when loading from saved session, recover summonData.speciesFrom and form index species object + // used to stay transformed on reload session + if (this.summonData.speciesForm) { + this.summonData.speciesForm = getPokemonSpeciesForm( + this.summonData.speciesForm.speciesId, + this.summonDataSpeciesFormIndex, + ); + } ret.primeSummonData(this.summonData); } return ret; } + + /** + * Method to save summon data species form index + * Necessary in case the pokemon is transformed + * to reload the correct form + */ + getSummonDataSpeciesFormIndex(): number { + if (this.summonData.speciesForm) { + return this.summonData.speciesForm.formIndex; + } + return 0; + } } diff --git a/test/abilities/imposter.test.ts b/test/abilities/imposter.test.ts index b4469cd9042..2c7302d04b7 100644 --- a/test/abilities/imposter.test.ts +++ b/test/abilities/imposter.test.ts @@ -127,4 +127,63 @@ describe("Abilities - Imposter", () => { expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); }); + + it("should persist transformed attributes across reloads", async () => { + game.override.moveset([Moves.ABSORB]); + + await game.classicMode.startBattle([Species.DITTO]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.toNextWave(); + + expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.currentBattle.waveIndex).toBe(2); + + await game.reload.reloadSession(); + + const playerReloaded = game.scene.getPlayerPokemon()!; + const playerMoveset = player.getMoveset(); + + expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(playerReloaded.getAbility()).toBe(enemy.getAbility()); + expect(playerReloaded.getGender()).toBe(enemy.getGender()); + + expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); + for (const s of EFFECTIVE_STATS) { + expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false)); + } + + expect(playerMoveset.length).toEqual(1); + expect(playerMoveset[0]?.moveId).toEqual(Moves.SPLASH); + }); + + it("should stay transformed with the correct form after reload", async () => { + game.override.moveset([Moves.ABSORB]); + game.override.enemySpecies(Species.UNOWN); + await game.classicMode.startBattle([Species.DITTO]); + + const enemy = game.scene.getEnemyPokemon()!; + + // change form + enemy.species.forms[5]; + enemy.species.formIndex = 5; + + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + await game.toNextWave(); + + expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.currentBattle.waveIndex).toBe(2); + + await game.reload.reloadSession(); + + const playerReloaded = game.scene.getPlayerPokemon()!; + + expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex); + }); }); diff --git a/test/moves/transform.test.ts b/test/moves/transform.test.ts index 5140202a383..d37decf28f4 100644 --- a/test/moves/transform.test.ts +++ b/test/moves/transform.test.ts @@ -6,6 +6,7 @@ import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; import { Stat, BATTLE_STATS, EFFECTIVE_STATS } from "#enums/stat"; import { Abilities } from "#enums/abilities"; +import { BattlerIndex } from "#app/battle"; // TODO: Add more tests once Transform is fully implemented describe("Moves - Transform", () => { @@ -58,7 +59,7 @@ describe("Moves - Transform", () => { } const playerMoveset = player.getMoveset(); - const enemyMoveset = player.getMoveset(); + const enemyMoveset = enemy.getMoveset(); expect(playerMoveset.length).toBe(enemyMoveset.length); for (let i = 0; i < playerMoveset.length && i < enemyMoveset.length; i++) { @@ -127,4 +128,71 @@ describe("Moves - Transform", () => { expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); }); + + it("should persist transformed attributes across reloads", async () => { + game.override.enemyMoveset([]).moveset([]); + + await game.classicMode.startBattle([Species.DITTO]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + game.move.changeMoveset(player, Moves.TRANSFORM); + game.move.changeMoveset(enemy, Moves.MEMENTO); + + game.move.select(Moves.TRANSFORM); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextWave(); + + expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.currentBattle.waveIndex).toBe(2); + + await game.reload.reloadSession(); + + const playerReloaded = game.scene.getPlayerPokemon()!; + const playerMoveset = player.getMoveset(); + + expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(playerReloaded.getAbility()).toBe(enemy.getAbility()); + expect(playerReloaded.getGender()).toBe(enemy.getGender()); + + expect(playerReloaded.getStat(Stat.HP, false)).not.toBe(enemy.getStat(Stat.HP)); + for (const s of EFFECTIVE_STATS) { + expect(playerReloaded.getStat(s, false)).toBe(enemy.getStat(s, false)); + } + + expect(playerMoveset.length).toEqual(1); + expect(playerMoveset[0]?.moveId).toEqual(Moves.MEMENTO); + }); + + it("should stay transformed with the correct form after reload", async () => { + game.override.enemyMoveset([]).moveset([]); + game.override.enemySpecies(Species.DARMANITAN); + + await game.classicMode.startBattle([Species.DITTO]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + // change form + enemy.species.forms[1]; + enemy.species.formIndex = 1; + + game.move.changeMoveset(player, Moves.TRANSFORM); + game.move.changeMoveset(enemy, Moves.MEMENTO); + + game.move.select(Moves.TRANSFORM); + await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); + await game.toNextWave(); + + expect(game.scene.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); + expect(game.scene.currentBattle.waveIndex).toBe(2); + + await game.reload.reloadSession(); + + const playerReloaded = game.scene.getPlayerPokemon()!; + + expect(playerReloaded.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); + expect(playerReloaded.getSpeciesForm().formIndex).toBe(enemy.getSpeciesForm().formIndex); + }); });