Revert leaveField tests for other PR

This commit is contained in:
Bertie690 2025-05-18 10:59:01 -04:00
parent a42b7ed9f7
commit ebe4878a2b
11 changed files with 104 additions and 310 deletions

View File

@ -110,10 +110,14 @@ export function ForceSwitch<TBase extends SubMoveOrAbAttr>(Base: TBase) {
this.tryFleeWildPokemon(switchOutTarget);
}
// NB: `prependToPhase` is used here to ensure that the switch happens before the move ends
// and `arena.ignoreAbilities` is reset.
// This ensures ability ignore effects will persist for the duration of the switch (for hazards).
private trySwitchPlayerPokemon(switchOutTarget: PlayerPokemon): void {
// If not forced to switch, add a SwitchPhase to allow picking the next switched in Pokemon.
if (this.switchType !== SwitchType.FORCE_SWITCH) {
globalScene.appendToPhase(
globalScene.prependToPhase(
new SwitchPhase(this.switchType, switchOutTarget.getFieldIndex(), true, true),
MoveEndPhase,
);
@ -124,7 +128,7 @@ export function ForceSwitch<TBase extends SubMoveOrAbAttr>(Base: TBase) {
const reservePartyMembers = globalScene.getBackupPartyMemberIndices(true);
const switchInIndex = reservePartyMembers[switchOutTarget.randSeedInt(reservePartyMembers.length)];
globalScene.appendToPhase(
globalScene.prependToPhase(
new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), switchInIndex, false, true),
MoveEndPhase,
);
@ -144,7 +148,7 @@ export function ForceSwitch<TBase extends SubMoveOrAbAttr>(Base: TBase) {
this.switchType === SwitchType.FORCE_SWITCH
? reservePartyIndices[switchOutTarget.randSeedInt(reservePartyIndices.length)]
: (globalScene.currentBattle.trainer.getNextSummonIndex(switchOutTarget.trainerSlot) ?? 0);
globalScene.appendToPhase(
globalScene.prependToPhase(
new SwitchSummonPhase(this.switchType, switchOutTarget.getFieldIndex(), summonIndex, false, false),
MoveEndPhase,
);

View File

@ -1336,7 +1336,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container {
* @see {@linkcode SubstituteTag}
* @see {@linkcode getFieldPositionOffset}
*/
getSubstituteOffset(): [number, number] {
getSubstituteOffset(): [x: number, y: number] {
return this.isPlayer() ? [-30, 10] : [30, -10];
}

View File

@ -61,7 +61,8 @@ export class FaintPhase extends PokemonPhase {
faintPokemon.getTag(BattlerTagType.GRUDGE)?.lapse(faintPokemon, BattlerTagLapseType.CUSTOM, this.source);
}
// Check for reviver seed
faintPokemon.resetSummonData();
if (!this.preventInstantRevive) {
const instantReviveModifier = globalScene.applyModifier(
PokemonInstantReviveModifier,
@ -70,7 +71,6 @@ 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 reservePartyIndices = globalScene.getBackupPartyMemberIndices(
false,
(pokemon as EnemyPokemon).trainerSlot,
);
if (reservePartyIndices.length) {
const hasReservePartyMember = !!globalScene
.getEnemyParty()
.filter(p => p.isActive() && !p.isOnField() && p.trainerSlot === (pokemon as EnemyPokemon).trainerSlot)
.length;
if (hasReservePartyMember) {
globalScene.pushPhase(new SwitchSummonPhase(SwitchType.SWITCH, this.fieldIndex, -1, false, false));
}
}
@ -217,7 +217,6 @@ 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();
},

View File

@ -274,6 +274,8 @@ export class SummonPhase extends PartyMemberPokemonPhase {
globalScene.unshiftPhase(new ShinySparklePhase(pokemon.getBattlerIndex()));
}
pokemon.resetTurnData();
if (
!this.loaded ||
[BattleType.TRAINER, BattleType.MYSTERY_ENCOUNTER].includes(globalScene.currentBattle.battleType) ||

View File

@ -165,8 +165,13 @@ export class SwitchSummonPhase extends SummonPhase {
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);
// We don't reset temp effects here as we need to transfer them to tne new pokemon
// TODO: When should this remove the info container?
// Force switch moves did it prior
this.lastPokemon.leaveField(
![SwitchType.BATON_PASS, SwitchType.SHED_TAIL].includes(this.switchType),
this.doReturn,
);
globalScene.ui.showText(
this.player
? i18next.t("battle:playerGo", {
@ -184,14 +189,12 @@ export class SwitchSummonPhase extends SummonPhase {
* If this switch is passing a Substitute, make the switched Pokemon matches the returned Pokemon's state as it left.
* Otherwise, clear any persisting tags on the returned Pokemon.
*/
if (this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) {
const substitute = this.lastPokemon.getTag(SubstituteTag);
if (substitute) {
if ((this.switchType === SwitchType.BATON_PASS || this.switchType === SwitchType.SHED_TAIL) && substitute) {
switchedInPokemon.x += this.lastPokemon.getSubstituteOffset()[0];
switchedInPokemon.y += this.lastPokemon.getSubstituteOffset()[1];
switchedInPokemon.setAlpha(0.5);
}
}
this.summon();
};
@ -209,35 +212,35 @@ export class SwitchSummonPhase extends SummonPhase {
onEnd(): void {
super.onEnd();
const pokemon = this.getPokemon();
const activePokemon = 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;
activePokemon.resetTurnData();
activePokemon.resetSummonData();
activePokemon.tempSummonData.turnCount--;
activePokemon.tempSummonData.waveTurnCount--;
activePokemon.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);
activePokemon.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);
activePokemon.summonData.tags.push(subTag);
}
this.lastPokemon.resetTurnData();
this.lastPokemon.resetSummonData();
}
globalScene.triggerPokemonFormChange(pokemon, SpeciesFormChangeActiveTrigger, true);
globalScene.triggerPokemonFormChange(activePokemon, SpeciesFormChangeActiveTrigger, true);
// Reverts to weather-based forms when weather suppressors (Cloud Nine/Air Lock) are switched out
globalScene.arena.triggerWeatherBasedFormChanges();
}

View File

@ -1,6 +1,8 @@
import { BattlerIndex } from "#app/battle";
import { ArenaTagSide } from "#app/data/arena-tag";
import { globalScene } from "#app/global-scene";
import { Abilities } from "#enums/abilities";
import { ArenaTagType } from "#enums/arena-tag-type";
import { Moves } from "#enums/moves";
import { Species } from "#enums/species";
import GameManager from "#test/testUtils/gameManager";
@ -24,29 +26,60 @@ describe("Abilities - Mold Breaker", () => {
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset([Moves.SPLASH])
.moveset([Moves.ERUPTION, Moves.EARTHQUAKE, Moves.DRAGON_TAIL])
.ability(Abilities.MOLD_BREAKER)
.battleStyle("single")
.disableCrits()
.enemySpecies(Species.MAGIKARP)
.enemyAbility(Abilities.BALL_FETCH)
.enemyPassiveAbility(Abilities.NO_GUARD)
.enemyMoveset(Moves.SPLASH);
});
it("should turn off the ignore abilities arena variable after the user's move", async () => {
game.override
.enemyMoveset(Moves.SPLASH)
.ability(Abilities.MOLD_BREAKER)
.moveset([Moves.ERUPTION])
.startingLevel(100)
.enemyLevel(2);
it("should ignore ignorable abilities during the move's execution", async () => {
game.override.startingLevel(100).enemyLevel(2).enemyAbility(Abilities.STURDY);
await game.classicMode.startBattle([Species.MAGIKARP]);
const enemy = game.scene.getEnemyPokemon()!;
expect(enemy.isFainted()).toBe(false);
game.move.select(Moves.ERUPTION);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getEnemyPokemon()?.isFainted()).toBe(true);
});
it("should turn off ignore abilities arena variable after the user's move concludes", async () => {
game.override.startingLevel(100).enemyLevel(2);
await game.classicMode.startBattle([Species.MAGIKARP]);
expect(globalScene.arena.ignoreAbilities).toBe(false);
game.move.select(Moves.SPLASH);
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to("MoveEndPhase", true);
await game.phaseInterceptor.to("MoveEffectPhase");
expect(globalScene.arena.ignoreAbilities).toBe(true);
await game.phaseInterceptor.to("MoveEndPhase");
expect(globalScene.arena.ignoreAbilities).toBe(false);
});
it("should keep Levitate opponents grounded when using force switch moves", async () => {
game.override.enemyAbility(Abilities.LEVITATE).enemySpecies(Species.WEEZING).startingWave(8); // first rival battle; guaranteed 2 mon party
// Setup toxic spikes and stealth rock
game.scene.arena.addTag(ArenaTagType.TOXIC_SPIKES, -1, Moves.TOXIC_SPIKES, 1, ArenaTagSide.ENEMY);
game.scene.arena.addTag(ArenaTagType.SPIKES, -1, Moves.CEASELESS_EDGE, 1, ArenaTagSide.ENEMY);
await game.classicMode.startBattle([Species.MAGIKARP]);
const [weezing1, weezing2] = game.scene.getEnemyParty();
// Weezing's levitate prevented removal of Toxic Spikes, ignored Spikes damage
expect(game.scene.arena.getTagOnSide(ArenaTagType.TOXIC_SPIKES, ArenaTagSide.ENEMY)).toBeDefined();
expect(weezing1.getHpRatio()).toBe(1);
game.move.select(Moves.DRAGON_TAIL);
await game.phaseInterceptor.to("TurnEndPhase");
// Levitate was ignored during the switch, causing Toxic Spikes to be removed and Spikes to deal damage
expect(weezing1.isOnField()).toBe(false);
expect(weezing2.isOnField()).toBe(true);
expect(weezing2.getHpRatio()).toBeCloseTo(0.75);
expect(game.scene.arena.getTagOnSide(ArenaTagType.TOXIC_SPIKES, ArenaTagSide.ENEMY)).toBeUndefined();
});
});

View File

@ -349,7 +349,7 @@ describe("Abilities - Wimp Out", () => {
confirmNoSwitch();
// Turn 2: get back enough HP that substitute doesn't put us under
wimpod.hp = wimpod.getMaxHp() * 0.8;
wimpod.hp = wimpod.getMaxHp() * 0.78;
game.move.select(Moves.SUBSTITUTE);
game.doSelectPartyPokemon(1);
@ -373,7 +373,7 @@ describe("Abilities - Wimp Out", () => {
it("should disregard Shell Bell recovery while still activating it before switching", async () => {
game.override
.moveset(Moves.DOUBLE_EDGE)
.enemyMoveset([Moves.SPLASH])
.enemyMoveset(Moves.SPLASH)
.startingHeldItems([{ name: "SHELL_BELL", count: 4 }]); // heals 50% of damage dealt, more than recoil takes away
await game.classicMode.startBattle([Species.WIMPOD, Species.TYRUNT]);
@ -382,10 +382,13 @@ describe("Abilities - Wimp Out", () => {
game.move.select(Moves.DOUBLE_EDGE);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
await game.phaseInterceptor.to("MoveEffectPhase");
// Wimp out activated before shell bell healing
// Wimp out check activated from recoil before shell bell procced, but did not deny the pokemon its recovery
expect(wimpod.turnData.damageTaken).toBeGreaterThan(0);
expect(wimpod.getHpRatio()).toBeGreaterThan(0.5);
await game.phaseInterceptor.to("TurnEndPhase");
confirmSwitch();
expect(game.phaseInterceptor.log).toContain("PokemonHealPhase");
});

View File

@ -106,10 +106,9 @@ describe("Items - Reviver Seed", () => {
// Self-damage tests
it.each([
{ moveType: "Relative Recoil", move: Moves.DOUBLE_EDGE },
{ moveType: "HP% Recoil", move: Moves.CHLOROBLAST },
{ moveType: "Recoil", move: Moves.DOUBLE_EDGE },
{ moveType: "Self-KO", move: Moves.EXPLOSION },
{ moveType: "Ghost-type Curse", move: Moves.CURSE },
{ moveType: "Self-Deduction", move: Moves.CURSE },
{ moveType: "Liquid Ooze", move: Moves.GIGA_DRAIN },
])("should not activate the holder's reviver seed from $moveType", async ({ move }) => {
game.override

View File

@ -32,34 +32,25 @@ describe("Moves - U-turn", () => {
.disableCrits();
});
it("should switch the user out upon use", async () => {
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
const [raichu, shuckle] = game.scene.getPlayerParty();
expect(raichu).toBeDefined();
expect(shuckle).toBeDefined();
expect(game.scene.getPlayerPokemon()!).toBe(raichu);
game.move.select(Moves.U_TURN);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
expect(game.scene.getPlayerPokemon()!).toBe(shuckle);
});
it("triggers regenerator passive once upon switch", async () => {
it("triggers regenerator a single time when a regenerator user switches out with u-turn", async () => {
// arrange
const playerHp = 1;
game.override.ability(Abilities.REGENERATOR);
await game.classicMode.startBattle([Species.RAICHU, Species.SHUCKLE]);
game.scene.getPlayerPokemon()!.hp = 1;
game.scene.getPlayerPokemon()!.hp = playerHp;
// act
game.move.select(Moves.U_TURN);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
expect(game.scene.getPlayerParty()[1].hp).toBeGreaterThan(1);
// assert
expect(game.scene.getPlayerParty()[1].hp).toEqual(
Math.floor(game.scene.getPlayerParty()[1].getMaxHp() * 0.33 + playerHp),
);
expect(game.phaseInterceptor.log).toContain("SwitchSummonPhase");
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.SHUCKLE);
});
}, 20000);
it("triggers rough skin on the u-turn user before a new pokemon is switched in", async () => {
// arrange

View File

@ -1,240 +0,0 @@
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("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...
])(
"Mid-Battle Switch Outs - $name - ",
({ playerMove = Moves.SPLASH, playerAbility = Abilities.BALL_FETCH, enemyMove = Moves.SPLASH }) => {
let phaserGame: Phaser.Game;
let game: GameManager;
beforeAll(() => {
phaserGame = new Phaser.Game({
type: Phaser.HEADLESS,
});
});
afterEach(() => {
game.phaseInterceptor.restoreOg();
});
beforeEach(() => {
game = new GameManager(phaserGame);
game.override
.moveset(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);
});
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.move.select(playerMove);
game.doSelectPartyPokemon(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();
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");
game.move.select(playerMove);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
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());
});
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(playerMove);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to("TurnEndPhase");
expect(piloWaveSpy).toHaveBeenCalledTimes(0);
expect(piloBattleWaveSpy).toHaveBeenCalledTimes(0);
expect(mamoWaveSpy).toHaveBeenCalledTimes(0);
expect(mamoBattleWaveSpy).toHaveBeenCalledTimes(0);
});
},
);