diff --git a/test/moves/swallow.test.ts b/test/moves/swallow.test.ts index d4ce11bbc2a..bb95c2c593d 100644 --- a/test/moves/swallow.test.ts +++ b/test/moves/swallow.test.ts @@ -3,12 +3,14 @@ import { StockpilingTag } from "#app/data/battler-tags"; import { BattlerTagType } from "#app/enums/battler-tag-type"; import type { TurnMove } from "#app/field/pokemon"; import { MoveResult } from "#enums/move-result"; +import { MovePhase } from "#app/phases/move-phase"; +import { TurnInitPhase } from "#app/phases/turn-init-phase"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Moves - Swallow", () => { let phaserGame: Phaser.Game; @@ -29,41 +31,99 @@ describe("Moves - Swallow", () => { .battleStyle("single") .enemySpecies(SpeciesId.RATTATA) .enemyMoveset(MoveId.SPLASH) - .enemyAbility(AbilityId.BALL_FETCH) - .enemyLevel(1000) - .startingLevel(1000) - .ability(AbilityId.BALL_FETCH); + .enemyAbility(AbilityId.NONE) + .enemyLevel(2000) + .moveset(MoveId.SWALLOW) + .ability(AbilityId.NONE); }); - it.each<{ stackCount: number; healPercent: number }>([ - { stackCount: 1, healPercent: 25 }, - { stackCount: 2, healPercent: 50 }, - { stackCount: 3, healPercent: 100 }, - ])( - "should heal the user by $healPercent% max HP when consuming $count stockpile stacks", - async ({ stackCount, healPercent }) => { - await game.classicMode.startBattle([SpeciesId.SWALOT]); + describe("consumes all stockpile stacks to heal (scaling with stacks)", () => { + it("1 stack -> 25% heal", async () => { + const stacksToSetup = 1; + const expectedHeal = 25; - const swalot = game.field.getPlayerPokemon(); - swalot.hp = 1; + await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); - for (let i = 0; i < stackCount; i++) { - swalot.addTag(BattlerTagType.STOCKPILING); - } + const pokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); + pokemon["hp"] = 1; - const stockpilingTag = swalot.getTag(StockpilingTag)!; + pokemon.addTag(BattlerTagType.STOCKPILING); + + const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); - expect(stockpilingTag.stockpiledCount).toBe(stackCount); + expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); - game.move.use(MoveId.SWALLOW); - await game.toEndOfTurn(); + vi.spyOn(pokemon, "heal"); - expect(swalot.getHpRatio()).toBeCloseTo(healPercent / 100, 1); - expect(swalot.getTag(StockpilingTag)).toBeUndefined(); - }, - ); + game.move.select(MoveId.SWALLOW); + await game.phaseInterceptor.to(TurnInitPhase); - it("should fail without stacks", async () => { + expect(pokemon.heal).toHaveBeenCalledOnce(); + expect(pokemon.heal).toHaveReturnedWith(expectedHeal); + + expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); + }); + + it("2 stacks -> 50% heal", async () => { + const stacksToSetup = 2; + const expectedHeal = 50; + + await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); + + const pokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); + pokemon["hp"] = 1; + + pokemon.addTag(BattlerTagType.STOCKPILING); + pokemon.addTag(BattlerTagType.STOCKPILING); + + const stockpilingTag = pokemon.getTag(StockpilingTag)!; + expect(stockpilingTag).toBeDefined(); + expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); + + vi.spyOn(pokemon, "heal"); + + game.move.select(MoveId.SWALLOW); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(pokemon.heal).toHaveBeenCalledOnce(); + expect(pokemon.heal).toHaveReturnedWith(expectedHeal); + + expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); + }); + + it("3 stacks -> 100% heal", async () => { + const stacksToSetup = 3; + const expectedHeal = 100; + + await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); + + const pokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(pokemon, "getMaxHp").mockReturnValue(100); + pokemon["hp"] = 0.0001; + + pokemon.addTag(BattlerTagType.STOCKPILING); + pokemon.addTag(BattlerTagType.STOCKPILING); + pokemon.addTag(BattlerTagType.STOCKPILING); + + const stockpilingTag = pokemon.getTag(StockpilingTag)!; + expect(stockpilingTag).toBeDefined(); + expect(stockpilingTag.stockpiledCount).toBe(stacksToSetup); + + vi.spyOn(pokemon, "heal"); + + game.move.select(MoveId.SWALLOW); + await game.phaseInterceptor.to(TurnInitPhase); + + expect(pokemon.heal).toHaveBeenCalledOnce(); + expect(pokemon.heal).toHaveReturnedWith(expect.closeTo(expectedHeal)); + + expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); + }); + }); + + it("fails without stacks", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); const pokemon = game.scene.getPlayerPokemon()!; @@ -71,17 +131,17 @@ describe("Moves - Swallow", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeUndefined(); - game.move.use(MoveId.SWALLOW); - await game.toEndOfTurn(); + game.move.select(MoveId.SWALLOW); + await game.phaseInterceptor.to(TurnInitPhase); - expect(pokemon.getLastXMoves()[0]).toMatchObject({ + expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: MoveId.SWALLOW, result: MoveResult.FAIL, targets: [pokemon.getBattlerIndex()], }); }); - describe("should reset stat stage boosts granted by stacks", () => { + describe("restores stat stage boosts granted by stacks", () => { it("decreases stats based on stored values (both boosts equal)", async () => { await game.classicMode.startBattle([SpeciesId.ABOMASNOW]); @@ -90,13 +150,16 @@ describe("Moves - Swallow", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); + + game.move.select(MoveId.SWALLOW); + await game.phaseInterceptor.to(MovePhase); + expect(pokemon.getStatStage(Stat.DEF)).toBe(1); expect(pokemon.getStatStage(Stat.SPDEF)).toBe(1); - game.move.use(MoveId.SWALLOW); - await game.toEndOfTurn(); + await game.phaseInterceptor.to(TurnInitPhase); - expect(pokemon.getLastXMoves()[0]).toMatchObject({ + expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: MoveId.SWALLOW, result: MoveResult.SUCCESS, targets: [pokemon.getBattlerIndex()], @@ -104,6 +167,8 @@ describe("Moves - Swallow", () => { expect(pokemon.getStatStage(Stat.DEF)).toBe(0); expect(pokemon.getStatStage(Stat.SPDEF)).toBe(0); + + expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); it("lower stat stages based on stored values (different boosts)", async () => { @@ -114,16 +179,18 @@ describe("Moves - Swallow", () => { const stockpilingTag = pokemon.getTag(StockpilingTag)!; expect(stockpilingTag).toBeDefined(); + // for the sake of simplicity (and because other tests cover the setup), set boost amounts directly stockpilingTag.statChangeCounts = { [Stat.DEF]: -1, [Stat.SPDEF]: 2, }; - game.move.use(MoveId.SWALLOW); - await game.toEndOfTurn(); + game.move.select(MoveId.SWALLOW); - expect(pokemon.getLastXMoves()[0]).toMatchObject({ + await game.phaseInterceptor.to(TurnInitPhase); + + expect(pokemon.getMoveHistory().at(-1)).toMatchObject({ move: MoveId.SWALLOW, result: MoveResult.SUCCESS, targets: [pokemon.getBattlerIndex()], @@ -131,6 +198,8 @@ describe("Moves - Swallow", () => { expect(pokemon.getStatStage(Stat.DEF)).toBe(1); expect(pokemon.getStatStage(Stat.SPDEF)).toBe(-2); + + expect(pokemon.getTag(StockpilingTag)).toBeUndefined(); }); }); });