From a42b7ed9f78bc084711e879340507299df7bb1a2 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Sat, 17 May 2025 21:45:15 -0400 Subject: [PATCH] Hopefully fixed extraneous reset calls and call timing --- src/data/mixins/force-switch.ts | 2 +- src/data/moves/move.ts | 5 +- src/field/pokemon.ts | 9 +- src/phases/faint-phase.ts | 15 +- src/phases/switch-summon-phase.ts | 33 ++-- test/phases/switch-phases.test.ts | 315 ++++++++++++++++++------------ 6 files changed, 231 insertions(+), 148 deletions(-) diff --git a/src/data/mixins/force-switch.ts b/src/data/mixins/force-switch.ts index 5381b35c8bb..479bf6725a2 100644 --- a/src/data/mixins/force-switch.ts +++ b/src/data/mixins/force-switch.ts @@ -86,7 +86,7 @@ export function ForceSwitch(Base: TBase) { /** * Wrapper function to handle the actual "switching out" of Pokemon. - * @param switchOutTarget - The {@linkcode Pokemon} (player or enemy) attempting to switch out. + * @param switchOutTarget - The {@linkcode Pokemon} (player or enemy) to be switched switch out. */ protected doSwitch(switchOutTarget: Pokemon): void { if (switchOutTarget instanceof PlayerPokemon) { diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 8e642da07d8..3a654006f1c 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -7771,8 +7771,9 @@ const targetSleptOrComatoseCondition: MoveConditionFunc = (user: Pokemon, target const failIfLastCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => globalScene.phaseQueue.find(phase => phase instanceof MovePhase) !== undefined; const failIfLastInPartyCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => { - const party: Pokemon[] = user.isPlayer() ? globalScene.getPlayerParty() : globalScene.getEnemyParty(); - return party.some(pokemon => pokemon.isActive() && !pokemon.isOnField()); + const player = user.isPlayer(); + const otherPartyIndices = globalScene.getBackupPartyMemberIndices(player, !player ? (user as EnemyPokemon).trainerSlot : undefined) + return otherPartyIndices.length > 0; }; const failIfGhostTypeCondition: MoveConditionFunc = (user: Pokemon, target: Pokemon, move: Move) => !target.isOfType(PokemonType.GHOST); diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index d1aa938f3ee..5b10cd00d04 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -5034,6 +5034,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } for (const tag of source.summonData.tags) { + // Skip non-Baton Passable tags (or telekinesis for mega gengar; cf. https://bulbapedia.bulbagarden.net/wiki/Telekinesis_(move)) if ( !tag.isBatonPassable || (tag.tagType === BattlerTagType.TELEKINESIS && @@ -6314,20 +6315,20 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @param destroy - Whether to destroy this Pokemon once it leaves the field; default `false` * @remarks * This **SHOULD NOT** be called when a `SummonPhase` or `SwitchSummonPhase` is already being added, - * which can lead to premature resetting of {@klinkcode turnData} and {@linkcode summonData}. + * which can lead to premature resetting of {@linkcode turnData} and {@linkcode summonData}. */ leaveField(clearEffects = true, hideInfo = true, destroy = false) { console.log(`leaveField called on Pokemon ${this.name}`) this.resetSprite(); - this.resetTurnData(); globalScene - .getField(true) - .filter(p => p !== this) + .getField(true) + .filter(p => p !== this) .forEach(p => p.removeTagsBySourceId(this.id)); if (clearEffects) { this.destroySubstitute(); this.resetSummonData(); + this.resetTurnData(); } if (hideInfo) { this.hideInfo(); diff --git a/src/phases/faint-phase.ts b/src/phases/faint-phase.ts index 1aa24d59fa0..04ba6fefacf 100644 --- a/src/phases/faint-phase.ts +++ b/src/phases/faint-phase.ts @@ -61,8 +61,7 @@ export class FaintPhase extends PokemonPhase { faintPokemon.getTag(BattlerTagType.GRUDGE)?.lapse(faintPokemon, BattlerTagLapseType.CUSTOM, this.source); } - faintPokemon.resetSummonData(); - + // Check for reviver seed if (!this.preventInstantRevive) { const instantReviveModifier = globalScene.applyModifier( PokemonInstantReviveModifier, @@ -71,6 +70,7 @@ export class FaintPhase extends PokemonPhase { ) as PokemonInstantReviveModifier; if (instantReviveModifier) { + faintPokemon.resetSummonData(); faintPokemon.loseHeldItem(instantReviveModifier); globalScene.updateModifiers(this.player); return this.end(); @@ -179,11 +179,11 @@ export class FaintPhase extends PokemonPhase { } else { globalScene.unshiftPhase(new VictoryPhase(this.battlerIndex)); if ([BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType)) { - const hasReservePartyMember = !!globalScene - .getEnemyParty() - .filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot) - .length; - if (hasReservePartyMember) { + const reservePartyIndices = globalScene.getBackupPartyMemberIndices( + false, + (pokemon as EnemyPokemon).trainerSlot, + ); + if (reservePartyIndices.length) { globalScene.pushPhase(new SwitchSummonPhase(SwitchType.SWITCH, this.fieldIndex, -1, false, false)); } } @@ -217,6 +217,7 @@ export class FaintPhase extends PokemonPhase { globalScene.addFaintedEnemyScore(pokemon as EnemyPokemon); globalScene.currentBattle.addPostBattleLoot(pokemon as EnemyPokemon); } + // TODO: Do we need to leave the field here & now as opposed to during `switchSummonPhase`? pokemon.leaveField(); this.end(); }, diff --git a/src/phases/switch-summon-phase.ts b/src/phases/switch-summon-phase.ts index c3041c5824f..e918f0b129b 100644 --- a/src/phases/switch-summon-phase.ts +++ b/src/phases/switch-summon-phase.ts @@ -111,7 +111,6 @@ export class SwitchSummonPhase extends SummonPhase { scale: 0.5, onComplete: () => { globalScene.time.delayedCall(750, () => this.switchAndSummon()); - lastPokemon.leaveField(this.switchType === SwitchType.SWITCH, false); }, }); } @@ -162,9 +161,12 @@ export class SwitchSummonPhase extends SummonPhase { } } + // Swap around the 2 pokemon's party positions and play an animation to send in the new pokemon. party[this.slotIndex] = this.lastPokemon; party[this.fieldIndex] = switchedInPokemon; const showTextAndSummon = () => { + // TODO: Should this remove the info container? + this.lastPokemon.leaveField(![SwitchType.BATON_PASS, SwitchType.SHED_TAIL].includes(this.switchType), false); globalScene.ui.showText( this.player ? i18next.t("battle:playerGo", { @@ -209,25 +211,30 @@ export class SwitchSummonPhase extends SummonPhase { const pokemon = this.getPokemon(); + // If not switching at start of battle, reset turn counts and temp data on the newly sent in Pokemon + // Needed as we increment turn counters in `TurnEndPhase`. + if (this.switchType !== SwitchType.INITIAL_SWITCH) { + // No need to reset turn/summon data for initial switch + // (since both get initialized to an empty object on object creation) + this.lastPokemon.resetTurnData(); + this.lastPokemon.resetSummonData(); + pokemon.tempSummonData.turnCount--; + pokemon.tempSummonData.waveTurnCount--; + pokemon.turnData.switchedInThisTurn = true; + } + + // Baton Pass over any eligible effects or substitutes before resetting the last pokemon's temporary data. if (this.switchType === SwitchType.BATON_PASS) { pokemon.transferSummon(this.lastPokemon); + this.lastPokemon.resetTurnData(); + this.lastPokemon.resetSummonData(); } else if (this.switchType === SwitchType.SHED_TAIL) { const subTag = this.lastPokemon.getTag(SubstituteTag); if (subTag) { pokemon.summonData.tags.push(subTag); } - } - - // If not switching at start of battle, reset turn counts and temp data. - // Needed as we increment turn counters in `TurnEndPhase`. - if (this.switchType !== SwitchType.INITIAL_SWITCH) { - pokemon.tempSummonData.turnCount--; - pokemon.tempSummonData.waveTurnCount--; - pokemon.turnData.switchedInThisTurn = true; - // No need to reset turn/summon data for initial switch - //(since both get initialized to an empty object on object creation) - pokemon.resetTurnData(); - pokemon.resetSummonData(); + this.lastPokemon.resetTurnData(); + this.lastPokemon.resetSummonData(); } globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true); diff --git a/test/phases/switch-phases.test.ts b/test/phases/switch-phases.test.ts index 4f250acf579..5019856daba 100644 --- a/test/phases/switch-phases.test.ts +++ b/test/phases/switch-phases.test.ts @@ -1,21 +1,157 @@ -import Trainer from "#app/field/trainer"; +import { PokemonSummonData, PokemonTurnData } from "#app/field/pokemon"; import { Abilities } from "#enums/abilities"; import { BattleType } from "#enums/battle-type"; +import { BattlerTagType } from "#enums/battler-tag-type"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; -describe.each<{ name: string; selfMove?: Moves; selfAbility?: Abilities; oppMove?: Moves }>([ - { name: "Self Switch Attack Moves", selfMove: Moves.U_TURN }, - { name: "Target Switch Attack Moves", oppMove: Moves.DRAGON_TAIL }, - { name: "Self Switch Status Moves", selfMove: Moves.TELEPORT }, - { name: "Target Switch Status Moves", oppMove: Moves.WHIRLWIND }, - { name: "Self Switch Abilities", selfAbility: Abilities.EMERGENCY_EXIT }, +describe("Manual Switching -", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleStyle("single") + .disableCrits() + .enemySpecies(Species.MAGIKARP) + .moveset(Moves.SPLASH) + .enemyMoveset(Moves.SPLASH) + .battleType(BattleType.TRAINER) + .enemyAbility(Abilities.BALL_FETCH); + }); + + describe("Player", () => { + it("should only call leaveField once on the switched out pokemon", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + + const [piloswine, mamoswine] = game.scene.getPlayerParty(); + const piloLeaveSpy = vi.spyOn(piloswine, "leaveField"); + const mamoLeaveSpy = vi.spyOn(mamoswine, "leaveField"); + + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(piloLeaveSpy).toHaveBeenCalledTimes(1); + expect(mamoLeaveSpy).toHaveBeenCalledTimes(0); + }); + + it("should only reset summonData/turnData once per switch", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + + const [piloswine, mamoswine] = game.scene.getPlayerParty(); + const piloSummonSpy = vi.spyOn(piloswine, "resetSummonData"); + const piloTurnSpy = vi.spyOn(piloswine, "resetTurnData"); + const mamoSummonSpy = vi.spyOn(mamoswine, "resetSummonData"); + const mamoTurnSpy = vi.spyOn(mamoswine, "resetTurnData"); + + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(piloSummonSpy).toHaveBeenCalledTimes(1); + expect(piloTurnSpy).toHaveBeenCalledTimes(1); + expect(mamoSummonSpy).toHaveBeenCalledTimes(1); + expect(mamoTurnSpy).toHaveBeenCalledTimes(2); // once from switching, once at turn start + }); + + it("should not reset battleData/waveData upon switching", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + + const [piloswine, mamoswine] = game.scene.getPlayerParty(); + const piloWaveSpy = vi.spyOn(piloswine, "resetWaveData"); + const piloBattleWaveSpy = vi.spyOn(piloswine, "resetBattleAndWaveData"); + const mamoWaveSpy = vi.spyOn(mamoswine, "resetWaveData"); + const mamoBattleWaveSpy = vi.spyOn(mamoswine, "resetBattleAndWaveData"); + + game.doSwitchPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(piloWaveSpy).toHaveBeenCalledTimes(0); + expect(piloBattleWaveSpy).toHaveBeenCalledTimes(0); + expect(mamoWaveSpy).toHaveBeenCalledTimes(0); + expect(mamoBattleWaveSpy).toHaveBeenCalledTimes(0); + }); + }); + + describe("Enemy", () => { + it("should only call leaveField once on the switched out pokemon", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + + const [enemy1, enemy2] = game.scene.getEnemyParty(); + const enemy1LeaveSpy = vi.spyOn(enemy1, "leaveField"); + const enemy2LeaveSpy = vi.spyOn(enemy2, "leaveField"); + + game.move.select(Moves.SPLASH); + game.forceEnemyToSwitch(); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(enemy1LeaveSpy).toHaveBeenCalledTimes(1); + expect(enemy2LeaveSpy).toHaveBeenCalledTimes(0); + }); + + it("should only reset summonData/turnData once per switch", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + + const [enemy1, enemy2] = game.scene.getEnemyParty(); + const enemy1SummonSpy = vi.spyOn(enemy1, "resetSummonData"); + const enemy1TurnSpy = vi.spyOn(enemy1, "resetTurnData"); + const enemy2SummonSpy = vi.spyOn(enemy2, "resetSummonData"); + const enemy2TurnSpy = vi.spyOn(enemy2, "resetTurnData"); + + game.move.select(Moves.SPLASH); + game.forceEnemyToSwitch(); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(enemy1SummonSpy).toHaveBeenCalledTimes(1); + expect(enemy1TurnSpy).toHaveBeenCalledTimes(1); + expect(enemy2SummonSpy).toHaveBeenCalledTimes(1); + expect(enemy2TurnSpy).toHaveBeenCalledTimes(2); // once from switching, once at turn start + }); + + it("should not reset battleData/waveData upon switching", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + + const [enemy1, enemy2] = game.scene.getEnemyParty(); + const enemy1WaveSpy = vi.spyOn(enemy1, "resetWaveData"); + const enemy1BattleWaveSpy = vi.spyOn(enemy1, "resetBattleAndWaveData"); + const enemy2WaveSpy = vi.spyOn(enemy2, "resetWaveData"); + const enemy2BattleWaveSpy = vi.spyOn(enemy2, "resetBattleAndWaveData"); + + game.move.select(Moves.SPLASH); + game.forceEnemyToSwitch(); + await game.phaseInterceptor.to("TurnEndPhase"); + + expect(enemy1WaveSpy).toHaveBeenCalledTimes(0); + expect(enemy1BattleWaveSpy).toHaveBeenCalledTimes(0); + expect(enemy2WaveSpy).toHaveBeenCalledTimes(0); + expect(enemy2BattleWaveSpy).toHaveBeenCalledTimes(0); + }); + }); +}); + +describe.each<{ name: string; playerMove?: Moves; playerAbility?: Abilities; enemyMove?: Moves }>([ + { name: "Self Switch Attack Moves", playerMove: Moves.U_TURN }, + { name: "Target Switch Attack Moves", enemyMove: Moves.DRAGON_TAIL }, + { name: "Self Switch Status Moves", playerMove: Moves.TELEPORT }, + { name: "Target Switch Status Moves", enemyMove: Moves.WHIRLWIND }, + { name: "Self Switch Abilities", playerAbility: Abilities.EMERGENCY_EXIT, enemyMove: Moves.BRAVE_BIRD }, + /* { name: "Fainting", playerMove: Moves.EXPLOSION }, */ // TODO: This calls it twice... ])( - "Switch Outs - $name - ", - ({ selfMove = Moves.SPLASH, selfAbility = Abilities.BALL_FETCH, oppMove = Moves.SPLASH }) => { + "Mid-Battle Switch Outs - $name - ", + ({ playerMove = Moves.SPLASH, playerAbility = Abilities.BALL_FETCH, enemyMove = Moves.SPLASH }) => { let phaserGame: Phaser.Game; let game: GameManager; @@ -32,136 +168,73 @@ describe.each<{ name: string; selfMove?: Moves; selfAbility?: Abilities; oppMove beforeEach(() => { game = new GameManager(phaserGame); game.override + .moveset(playerMove) + .ability(playerAbility) .battleStyle("single") .disableCrits() + .enemyLevel(100) + .battleType(BattleType.TRAINER) + .passiveAbility(Abilities.STURDY) .enemySpecies(Species.MAGIKARP) + .enemyMoveset(enemyMove) + .enemyAbility(Abilities.BALL_FETCH) .enemyPassiveAbility(Abilities.NO_GUARD); }); - describe("Player -", () => { - beforeEach(() => { - game.override.moveset(oppMove).ability(selfAbility).enemyMoveset(selfMove).enemyAbility(Abilities.BALL_FETCH); - }); + it("should only call leaveField once on the switched out pokemon", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); - it("should only call leaveField once on the switched out pokemon", async () => { - await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + const [piloswine, mamoswine] = game.scene.getPlayerParty(); + const piloLeaveSpy = vi.spyOn(piloswine, "leaveField"); + const mamoLeaveSpy = vi.spyOn(mamoswine, "leaveField"); - const [piloswine, mamoswine] = game.scene.getPlayerParty(); - const piloLeaveSpy = vi.spyOn(piloswine, "leaveField"); - const mamoLeaveSpy = vi.spyOn(mamoswine, "leaveField"); + game.move.select(playerMove); + game.doSelectPartyPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); - game.move.select(selfMove); - game.doSelectPartyPokemon(1); - await game.phaseInterceptor.to("BerryPhase", false); - - expect(piloLeaveSpy).toHaveBeenCalledTimes(1); - expect(mamoLeaveSpy).toHaveBeenCalledTimes(0); - }); - - it("should only reset summonData/turnData once per switch", async () => { - await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); - - const [piloswine, mamoswine] = game.scene.getPlayerParty(); - const piloSummonSpy = vi.spyOn(piloswine, "resetSummonData"); - const piloTurnSpy = vi.spyOn(piloswine, "resetTurnData"); - const mamoSummonSpy = vi.spyOn(mamoswine, "resetSummonData"); - const mamoTurnSpy = vi.spyOn(mamoswine, "resetTurnData"); - - game.move.select(selfMove); - game.doSelectPartyPokemon(1); - await game.phaseInterceptor.to("BerryPhase", false); - - expect(piloSummonSpy).toHaveBeenCalledTimes(1); - expect(piloTurnSpy).toHaveBeenCalledTimes(1); - expect(mamoSummonSpy).toHaveBeenCalledTimes(1); - expect(mamoTurnSpy).toHaveBeenCalledTimes(1); - }); - - it("should not reset battleData/waveData upon switching", async () => { - await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); - - const [piloswine, mamoswine] = game.scene.getPlayerParty(); - const piloWaveSpy = vi.spyOn(piloswine, "resetWaveData"); - const piloBattleWaveSpy = vi.spyOn(piloswine, "resetBattleAndWaveData"); - const mamoWaveSpy = vi.spyOn(mamoswine, "resetWaveData"); - const mamoBattleWaveSpy = vi.spyOn(mamoswine, "resetBattleAndWaveData"); - - game.move.select(selfMove); - game.doSelectPartyPokemon(1); - await game.phaseInterceptor.to("TurnEndPhase"); - - expect(piloWaveSpy).toHaveBeenCalledTimes(0); - expect(piloBattleWaveSpy).toHaveBeenCalledTimes(0); - expect(mamoWaveSpy).toHaveBeenCalledTimes(0); - expect(mamoBattleWaveSpy).toHaveBeenCalledTimes(0); - }); + expect(piloLeaveSpy).toHaveBeenCalledTimes(1); + expect(mamoLeaveSpy).toHaveBeenCalledTimes(0); }); - describe("Enemy - ", () => { - beforeEach(() => { - game.override - .enemyMoveset(oppMove) - .enemyAbility(selfAbility) - .moveset(selfMove) - .ability(Abilities.BALL_FETCH) - .battleType(BattleType.TRAINER); + it("should only reset summonData/turnData once per switch", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); - // prevent natural trainer switches - vi.spyOn(Trainer.prototype, "getPartyMemberMatchupScores").mockReturnValue([ - [100, 1], - [100, 1], - ]); - }); + const [piloswine, mamoswine] = game.scene.getPlayerParty(); + piloswine.addTag(BattlerTagType.AQUA_RING, 999); // give piloswine a tag to ensure we know if summonData got reset + const piloSummonSpy = vi.spyOn(piloswine, "resetSummonData"); + const piloTurnSpy = vi.spyOn(piloswine, "resetTurnData"); + const mamoSummonSpy = vi.spyOn(mamoswine, "resetSummonData"); + const mamoTurnSpy = vi.spyOn(mamoswine, "resetTurnData"); - it("should only call leaveField once on the switched out pokemon", async () => { - await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + game.move.select(playerMove); + game.doSelectPartyPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); - const [enemy1, enemy2] = game.scene.getEnemyParty(); - const enemy1LeaveSpy = vi.spyOn(enemy1, "leaveField"); - const enemy2LeaveSpy = vi.spyOn(enemy2, "leaveField"); + expect(piloSummonSpy).toHaveBeenCalledTimes(1); + expect(piloTurnSpy).toHaveBeenCalledTimes(1); + expect(mamoSummonSpy).toHaveBeenCalledTimes(1); + expect(mamoTurnSpy).toHaveBeenCalledTimes(1); + expect(piloswine.summonData).toEqual(new PokemonSummonData()); + expect(piloswine.turnData).toEqual(new PokemonTurnData()); + }); - game.move.select(selfMove); - await game.phaseInterceptor.to("BerryPhase", false); + it("should not reset battleData/waveData upon switching", async () => { + await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); - expect(enemy1LeaveSpy).toHaveBeenCalledTimes(1); - expect(enemy2LeaveSpy).toHaveBeenCalledTimes(0); - }); + const [piloswine, mamoswine] = game.scene.getPlayerParty(); + const piloWaveSpy = vi.spyOn(piloswine, "resetWaveData"); + const piloBattleWaveSpy = vi.spyOn(piloswine, "resetBattleAndWaveData"); + const mamoWaveSpy = vi.spyOn(mamoswine, "resetWaveData"); + const mamoBattleWaveSpy = vi.spyOn(mamoswine, "resetBattleAndWaveData"); - it("should only reset summonData/turnData once per switch", async () => { - await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); + game.move.select(playerMove); + game.doSelectPartyPokemon(1); + await game.phaseInterceptor.to("TurnEndPhase"); - const [enemy1, enemy2] = game.scene.getEnemyParty(); - const enemy1SummonSpy = vi.spyOn(enemy1, "resetSummonData"); - const enemy1TurnSpy = vi.spyOn(enemy1, "resetTurnData"); - const enemy2SummonSpy = vi.spyOn(enemy2, "resetSummonData"); - const enemy2TurnSpy = vi.spyOn(enemy2, "resetTurnData"); - - game.move.select(selfMove); - await game.phaseInterceptor.to("BerryPhase", false); - - expect(enemy1SummonSpy).toHaveBeenCalledTimes(1); - expect(enemy1TurnSpy).toHaveBeenCalledTimes(1); - expect(enemy2SummonSpy).toHaveBeenCalledTimes(1); - expect(enemy2TurnSpy).toHaveBeenCalledTimes(1); - }); - - it("should not reset battleData/waveData upon switching", async () => { - await game.classicMode.startBattle([Species.PILOSWINE, Species.MAMOSWINE]); - - const [enemy1, enemy2] = game.scene.getEnemyParty(); - const enemy1WaveSpy = vi.spyOn(enemy1, "resetWaveData"); - const enemy1BattleWaveSpy = vi.spyOn(enemy1, "resetBattleAndWaveData"); - const enemy2WaveSpy = vi.spyOn(enemy2, "resetWaveData"); - const enemy2BattleWaveSpy = vi.spyOn(enemy2, "resetBattleAndWaveData"); - - game.move.select(selfMove); - await game.phaseInterceptor.to("TurnEndPhase"); - - expect(enemy1WaveSpy).toHaveBeenCalledTimes(0); - expect(enemy1BattleWaveSpy).toHaveBeenCalledTimes(0); - expect(enemy2WaveSpy).toHaveBeenCalledTimes(0); - expect(enemy2BattleWaveSpy).toHaveBeenCalledTimes(0); - }); + expect(piloWaveSpy).toHaveBeenCalledTimes(0); + expect(piloBattleWaveSpy).toHaveBeenCalledTimes(0); + expect(mamoWaveSpy).toHaveBeenCalledTimes(0); + expect(mamoBattleWaveSpy).toHaveBeenCalledTimes(0); }); }, );