From 1cf075adc9e10ce65e1975c4a5c28305863f0834 Mon Sep 17 00:00:00 2001 From: "Amani H." <109637146+xsn34kzx@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:31:25 -0400 Subject: [PATCH] [Bug] Fix Guard Swap and Power Swap swapping all stats (#4213) * [Bug] Fix `SwapStatStagesAttr` Oversight * Remove SPLASH_ONLY Leftover --- src/data/move.ts | 2 +- src/test/moves/guard_swap.test.ts | 40 +++++++++++-------- src/test/moves/heart_swap.test.ts | 64 +++++++++++++++++++++++++++++++ src/test/moves/power_swap.test.ts | 41 ++++++++++++-------- 4 files changed, 112 insertions(+), 35 deletions(-) create mode 100644 src/test/moves/heart_swap.test.ts diff --git a/src/data/move.ts b/src/data/move.ts index 650725b311b..473e2e51f41 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -2836,7 +2836,7 @@ export class SwapStatStagesAttr extends MoveEffectAttr { */ apply(user: Pokemon, target: Pokemon, move: Move, args: any []): boolean { if (super.apply(user, target, move, args)) { - for (const s of BATTLE_STATS) { + for (const s of this.stats) { const temp = user.getStatStage(s); user.setStatStage(s, target.getStatStage(s)); target.setStatStage(s, temp); diff --git a/src/test/moves/guard_swap.test.ts b/src/test/moves/guard_swap.test.ts index a27afaaa7ba..0c24f69c32c 100644 --- a/src/test/moves/guard_swap.test.ts +++ b/src/test/moves/guard_swap.test.ts @@ -1,16 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; import GameManager from "#app/test/utils/gameManager"; import { Species } from "#enums/species"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { Stat } from "#enums/stat"; +import { Stat, BATTLE_STATS } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { MoveEndPhase } from "#app/phases/move-end-phase"; describe("Moves - Guard Swap", () => { let phaserGame: Phaser.Game; let game: GameManager; + const TIMEOUT = 20 * 1000; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -27,37 +28,42 @@ describe("Moves - Guard Swap", () => { game.override .battleType("single") .enemyAbility(Abilities.BALL_FETCH) - .enemyMoveset([Moves.SHELL_SMASH]) - .enemySpecies(Species.MEW) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.INDEEDEE) .enemyLevel(200) .moveset([ Moves.GUARD_SWAP ]) .ability(Abilities.NONE); }); - it("should swap the user's DEF AND SPDEF stat stages with the target's", async () => { - await game.startBattle([ + it("should swap the user's DEF and SPDEF stat stages with the target's", async () => { + await game.classicMode.startBattle([ Species.INDEEDEE ]); - // Should start with no stat stages const player = game.scene.getPlayerPokemon()!; - // After Shell Smash, should have +2 in ATK and SPATK, -1 in DEF and SPDEF const enemy = game.scene.getEnemyPokemon()!; + vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); + game.move.select(Moves.GUARD_SWAP); await game.phaseInterceptor.to(MoveEndPhase); - expect(player.getStatStage(Stat.DEF)).toBe(0); - expect(player.getStatStage(Stat.SPDEF)).toBe(0); - expect(enemy.getStatStage(Stat.DEF)).toBe(-1); - expect(enemy.getStatStage(Stat.SPDEF)).toBe(-1); + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } await game.phaseInterceptor.to(TurnEndPhase); - expect(player.getStatStage(Stat.DEF)).toBe(-1); - expect(player.getStatStage(Stat.SPDEF)).toBe(-1); - expect(enemy.getStatStage(Stat.DEF)).toBe(0); - expect(enemy.getStatStage(Stat.SPDEF)).toBe(0); - }, 20000); + for (const s of BATTLE_STATS) { + if (s === Stat.DEF || s === Stat.SPDEF) { + expect(player.getStatStage(s)).toBe(1); + expect(enemy.getStatStage(s)).toBe(0); + } else { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } + } + }, TIMEOUT); }); diff --git a/src/test/moves/heart_swap.test.ts b/src/test/moves/heart_swap.test.ts new file mode 100644 index 00000000000..f658641d46f --- /dev/null +++ b/src/test/moves/heart_swap.test.ts @@ -0,0 +1,64 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import { Species } from "#enums/species"; +import { TurnEndPhase } from "#app/phases/turn-end-phase"; +import { Moves } from "#enums/moves"; +import { BATTLE_STATS } from "#enums/stat"; +import { Abilities } from "#enums/abilities"; +import { MoveEndPhase } from "#app/phases/move-end-phase"; + +describe("Moves - Heart Swap", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + const TIMEOUT = 20 * 1000; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .battleType("single") + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.INDEEDEE) + .enemyLevel(200) + .moveset([ Moves.HEART_SWAP ]) + .ability(Abilities.NONE); + }); + + it("should swap all of the user's stat stages with the target's", async () => { + await game.classicMode.startBattle([ + Species.MANAPHY + ]); + + const player = game.scene.getPlayerPokemon()!; + const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); + + game.move.select(Moves.HEART_SWAP); + + await game.phaseInterceptor.to(MoveEndPhase); + + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } + + await game.phaseInterceptor.to(TurnEndPhase); + + for (const s of BATTLE_STATS) { + expect(enemy.getStatStage(s)).toBe(0); + expect(player.getStatStage(s)).toBe(1); + } + }, TIMEOUT); +}); diff --git a/src/test/moves/power_swap.test.ts b/src/test/moves/power_swap.test.ts index a3d4bfca19a..92cd786c050 100644 --- a/src/test/moves/power_swap.test.ts +++ b/src/test/moves/power_swap.test.ts @@ -1,16 +1,17 @@ -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import Phaser from "phaser"; import GameManager from "#app/test/utils/gameManager"; import { Species } from "#enums/species"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { Moves } from "#enums/moves"; -import { Stat } from "#enums/stat"; +import { Stat, BATTLE_STATS } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { MoveEndPhase } from "#app/phases/move-end-phase"; describe("Moves - Power Swap", () => { let phaserGame: Phaser.Game; let game: GameManager; + const TIMEOUT = 20 * 1000; beforeAll(() => { phaserGame = new Phaser.Game({ @@ -27,36 +28,42 @@ describe("Moves - Power Swap", () => { game.override .battleType("single") .enemyAbility(Abilities.BALL_FETCH) - .enemyMoveset([Moves.SHELL_SMASH]) - .enemySpecies(Species.MEW) + .enemyMoveset(Moves.SPLASH) + .enemySpecies(Species.INDEEDEE) .enemyLevel(200) .moveset([ Moves.POWER_SWAP ]) .ability(Abilities.NONE); }); - it("should swap the user's ATK AND SPATK stat stages with the target's", async () => { - await game.startBattle([ + it("should swap the user's ATK and SPATK stat stages with the target's", async () => { + await game.classicMode.startBattle([ Species.INDEEDEE ]); - // Should start with no stat stages const player = game.scene.getPlayerPokemon()!; - // After Shell Smash, should have +2 in ATK and SPATK, -1 in DEF and SPDEF const enemy = game.scene.getEnemyPokemon()!; + + vi.spyOn(enemy.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(1)); + game.move.select(Moves.POWER_SWAP); await game.phaseInterceptor.to(MoveEndPhase); - expect(player.getStatStage(Stat.ATK)).toBe(0); - expect(player.getStatStage(Stat.SPATK)).toBe(0); - expect(enemy.getStatStage(Stat.ATK)).toBe(2); - expect(enemy.getStatStage(Stat.SPATK)).toBe(2); + for (const s of BATTLE_STATS) { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } await game.phaseInterceptor.to(TurnEndPhase); - expect(player.getStatStage(Stat.ATK)).toBe(2); - expect(player.getStatStage(Stat.SPATK)).toBe(2); - expect(enemy.getStatStage(Stat.ATK)).toBe(0); - expect(enemy.getStatStage(Stat.SPATK)).toBe(0); - }, 20000); + for (const s of BATTLE_STATS) { + if (s === Stat.ATK || s === Stat.SPATK) { + expect(player.getStatStage(s)).toBe(1); + expect(enemy.getStatStage(s)).toBe(0); + } else { + expect(player.getStatStage(s)).toBe(0); + expect(enemy.getStatStage(s)).toBe(1); + } + } + }, TIMEOUT); });