From 9c9e7fbbb94da1b76c518e723b0e5e5253dbad42 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 18:12:03 -0400 Subject: [PATCH 01/12] Made `game.phaseInterceptor` fail if move not in moveset also added a few assorted doc fixes --- test/abilities/intimidate.test.ts | 3 +- test/abilities/moxie.test.ts | 3 +- test/testUtils/gameManager.ts | 14 ++--- test/testUtils/gameManagerUtils.ts | 11 +--- test/testUtils/helpers/moveHelper.ts | 81 ++++++++++++++++++++-------- 5 files changed, 68 insertions(+), 44 deletions(-) diff --git a/test/abilities/intimidate.test.ts b/test/abilities/intimidate.test.ts index 3dcd9bcd129..6790e2b98d3 100644 --- a/test/abilities/intimidate.test.ts +++ b/test/abilities/intimidate.test.ts @@ -3,7 +3,6 @@ import Phaser from "phaser"; import GameManager from "#test/testUtils/gameManager"; import { UiMode } from "#enums/ui-mode"; import { Stat } from "#enums/stat"; -import { getMovePosition } from "#test/testUtils/gameManagerUtils"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; @@ -114,7 +113,7 @@ describe("Abilities - Intimidate", () => { expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1); - game.move.select(getMovePosition(game.scene, 0, MoveId.SPLASH)); + game.move.select(MoveId.SPLASH); await game.toNextTurn(); enemyPokemon = game.scene.getEnemyPokemon()!; diff --git a/test/abilities/moxie.test.ts b/test/abilities/moxie.test.ts index a85ed081448..1c4acf139e4 100644 --- a/test/abilities/moxie.test.ts +++ b/test/abilities/moxie.test.ts @@ -65,8 +65,7 @@ describe("Abilities - Moxie", () => { secondPokemon.hp = 1; - game.move.select(moveToUse); - game.selectTarget(BattlerIndex.PLAYER_2); + game.move.select(moveToUse, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to(TurnEndPhase); diff --git a/test/testUtils/gameManager.ts b/test/testUtils/gameManager.ts index b9f499d4e0c..f4ba31499d4 100644 --- a/test/testUtils/gameManager.ts +++ b/test/testUtils/gameManager.ts @@ -201,9 +201,8 @@ export default class GameManager { /** * Helper function to run to the final boss encounter as it's a bit tricky due to extra dialogue * Also handles Major/Minor bosses from endless modes - * @param game - The game manager - * @param species - * @param mode + * @param species - Array of {@linkcode SpeciesId}s to start the final battle with. + * @param mode - The {@linkcode GameModes} to spawn the final boss encounter in. */ async runToFinalBossEncounter(species: SpeciesId[], mode: GameModes) { console.log("===to final boss encounter==="); @@ -230,9 +229,9 @@ export default class GameManager { /** * Runs the game to a mystery encounter phase. - * @param encounterType if specified, will expect encounter to have been spawned - * @param species Optional array of species for party. - * @returns A promise that resolves when the EncounterPhase ends. + * @param encounterType - If specified, will expect encounter to be the given type. + * @param species - Optional array of species for party to start with. + * @returns A Promise that resolves when the EncounterPhase ends. */ async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: SpeciesId[]) { if (!isNullOrUndefined(encounterType)) { @@ -277,6 +276,7 @@ export default class GameManager { * Will trigger during the next {@linkcode SelectTargetPhase} * @param targetIndex - The {@linkcode BattlerIndex} of the attack target, or `undefined` for multi-target attacks * @param movePosition - The 0-indexed position of the move in the pokemon's moveset array + * @throws Immediately fails tests */ selectTarget(movePosition: number, targetIndex?: BattlerIndex) { this.onNextPrompt( @@ -292,7 +292,7 @@ export default class GameManager { handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY); } if (move.isMultiTarget() && targetIndex !== undefined) { - throw new Error(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`); + expect.fail(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`); } handler.processInput(Button.ACTION); }, diff --git a/test/testUtils/gameManagerUtils.ts b/test/testUtils/gameManagerUtils.ts index 57fd9b91d26..2e1714c90ad 100644 --- a/test/testUtils/gameManagerUtils.ts +++ b/test/testUtils/gameManagerUtils.ts @@ -10,7 +10,7 @@ import { getGameMode } from "#app/game-mode"; import { GameModes } from "#enums/game-modes"; import type { StarterMoveset } from "#app/system/game-data"; import type { Starter } from "#app/ui/starter-select-ui-handler"; -import { MoveId } from "#enums/move-id"; +import type { MoveId } from "#enums/move-id"; import type { SpeciesId } from "#enums/species-id"; /** Function to convert Blob to string */ @@ -98,15 +98,6 @@ export function waitUntil(truth): Promise { }); } -/** Get the index of `move` from the moveset of the pokemon on the player's field at location `pokemonIndex`. */ -export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: MoveId): number { - const playerPokemon = scene.getPlayerField()[pokemonIndex]; - const moveSet = playerPokemon.getMoveset(); - const index = moveSet.findIndex(m => m.moveId === move && m.ppUsed < m.getMovePp()); - console.log(`Move position for ${MoveId[move]} (=${move}):`, index); - return index; -} - /** * Useful for populating party, wave index, etc. without having to spin up and run through an entire EncounterPhase */ diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index ed1441a6a2f..0c233ddba65 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -1,4 +1,4 @@ -import type { BattlerIndex } from "#enums/battler-index"; +import { BattlerIndex } from "#enums/battler-index"; import { getMoveTargets } from "#app/data/moves/move-utils"; import type Pokemon from "#app/field/pokemon"; import { PokemonMove } from "#app/data/moves/pokemon-move"; @@ -9,14 +9,13 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase"; import { Command } from "#enums/command"; import { MoveId } from "#enums/move-id"; import { UiMode } from "#enums/ui-mode"; -import { getMovePosition } from "#test/testUtils/gameManagerUtils"; import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper"; -import { vi } from "vitest"; -import { coerceArray } from "#app/utils/common"; +import { expect, vi } from "vitest"; +import { coerceArray, toReadableString } from "#app/utils/common"; import { MoveUseMode } from "#enums/move-use-mode"; /** - * Helper to handle a Pokemon's move + * Helper to handle using a Pokemon's moves. */ export class MoveHelper extends GameManagerHelper { /** @@ -49,13 +48,25 @@ export class MoveHelper extends GameManagerHelper { } /** - * Select the move to be used by the given Pokemon(-index). Triggers during the next {@linkcode CommandPhase} - * @param move - the move to use - * @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0) - * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required + * Select a move _already in the player's moveset_ to be used during the next {@linkcode CommandPhase}. + * @param move - The {@linkcode MoveId} to use. + * @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified. + * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves. + * If set to `null`, will forgo normal target selection entirely (useful for UI tests). + * @remarks + * Will fail the current test if the move being selected is not in the user's moveset. */ - public select(move: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { - const movePosition = getMovePosition(this.game.scene, pkmIndex, move); + public select( + move: MoveId, + pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER, + targetIndex?: BattlerIndex | null, + ) { + const movePosition = this.getMovePosition(pkmIndex, move); + if (movePosition === -1) { + expect.fail( + `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset; Battler Index: ${BattlerIndex[pkmIndex]}`, + ); + } this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { this.game.scene.ui.setMode( @@ -77,14 +88,24 @@ export class MoveHelper extends GameManagerHelper { } /** - * Select the move to be used by the given Pokemon(-index), **which will also terastallize on this turn**. - * Triggers during the next {@linkcode CommandPhase} - * @param move - the move to use - * @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0) - * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required + * Select a move _already in the player's moveset_ to be used during the next {@linkcode CommandPhase}, **which will also terastallize on this turn**. + * @param move - The {@linkcode MoveId} to use. + * @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified. + * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves. + * If set to `null`, will forgo normal target selection entirely (useful for UI tests) */ - public selectWithTera(move: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) { - const movePosition = getMovePosition(this.game.scene, pkmIndex, move); + public selectWithTera( + move: MoveId, + pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER, + targetIndex?: BattlerIndex | null, + ) { + const movePosition = this.getMovePosition(pkmIndex, move); + if (movePosition === -1) { + expect.fail( + `MoveHelper.selectWithTera called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene.getField()[pkmIndex].getMoveset()}`, + ); + } + this.game.scene.getPlayerParty()[pkmIndex].isTerastallized = false; this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => { @@ -107,6 +128,15 @@ export class MoveHelper extends GameManagerHelper { } } + /** Helper function to get the index of the selected move in the selected part member's moveset. */ + private getMovePosition(pokemonIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2, move: MoveId): number { + const playerPokemon = this.game.scene.getPlayerField()[pokemonIndex]; + const moveset = playerPokemon.getMoveset(); + const index = moveset.findIndex(m => m.moveId === move && m.ppUsed < m.getMovePp()); + console.log(`Move position for ${MoveId[move]} (=${move}):`, index); + return index; + } + /** * Modifies a player pokemon's moveset to contain only the selected move and then * selects it to be used during the next {@linkcode CommandPhase}. @@ -116,14 +146,19 @@ export class MoveHelper extends GameManagerHelper { * Note: If you need to check for changes in the player's moveset as part of the test, it may be * best to use {@linkcode changeMoveset} and {@linkcode select} instead. * @param moveId - the move to use - * @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0) - * @param targetIndex - (optional) The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required - * @param useTera - If `true`, the Pokemon also chooses to Terastallize. This does not require a Tera Orb. Default: `false`. + * @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified. + * @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves. + * @param useTera - If `true`, the Pokemon will attempt to Terastallize even without a Tera Orb; default `false`. */ - public use(moveId: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null, useTera = false): void { + public use( + moveId: MoveId, + pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER, + targetIndex?: BattlerIndex, + useTera = false, + ): void { if ([Overrides.MOVESET_OVERRIDE].flat().length > 0) { vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([]); - console.warn("Warning: `use` overwrites the Pokemon's moveset and disables the player moveset override!"); + console.warn("Warning: `MoveHelper.use` overwriting player pokemon moveset and disabling moveset override!"); } const pokemon = this.game.scene.getPlayerField()[pkmIndex]; From b9ffd03165dd35950985df17dd3e1dad49636b42 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 18:15:12 -0400 Subject: [PATCH 02/12] Fixed fail comment --- test/testUtils/helpers/moveHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 0c233ddba65..1e0e87b3dcb 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -64,7 +64,7 @@ export class MoveHelper extends GameManagerHelper { const movePosition = this.getMovePosition(pkmIndex, move); if (movePosition === -1) { expect.fail( - `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset; Battler Index: ${BattlerIndex[pkmIndex]}`, + `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene.getField()[pkmIndex].getMoveset()}`, ); } From 599a2487bf5cf9a6f43724e5e22205131b4a3eca Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 18:34:25 -0400 Subject: [PATCH 03/12] added `map` statement --- test/testUtils/helpers/moveHelper.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 1e0e87b3dcb..43e12bb8158 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -64,7 +64,10 @@ export class MoveHelper extends GameManagerHelper { const movePosition = this.getMovePosition(pkmIndex, move); if (movePosition === -1) { expect.fail( - `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene.getField()[pkmIndex].getMoveset()}`, + `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene + .getField() + [pkmIndex].getMoveset() + .map(pm => pm.moveId)}`, ); } @@ -102,7 +105,10 @@ export class MoveHelper extends GameManagerHelper { const movePosition = this.getMovePosition(pkmIndex, move); if (movePosition === -1) { expect.fail( - `MoveHelper.selectWithTera called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene.getField()[pkmIndex].getMoveset()}`, + `MoveHelper.selectWithTera called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene + .getField() + [pkmIndex].getMoveset() + .map(pm => pm.moveId)}`, ); } From 11fa4354fabd04d144d8535dc6deb3ae2bbf2c7c Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 18:39:09 -0400 Subject: [PATCH 04/12] Fixed tests --- test/abilities/lightningrod.test.ts | 2 +- test/arena/weather_strong_winds.test.ts | 2 +- test/moves/baddy_bad.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/abilities/lightningrod.test.ts b/test/abilities/lightningrod.test.ts index 2dc29500454..d343cddedb7 100644 --- a/test/abilities/lightningrod.test.ts +++ b/test/abilities/lightningrod.test.ts @@ -89,7 +89,7 @@ describe("Abilities - Lightningrod", () => { enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(false); diff --git a/test/arena/weather_strong_winds.test.ts b/test/arena/weather_strong_winds.test.ts index d0d256816eb..d98ba96fd85 100644 --- a/test/arena/weather_strong_winds.test.ts +++ b/test/arena/weather_strong_winds.test.ts @@ -86,7 +86,7 @@ describe("Weather - Strong Winds", () => { const enemy = game.scene.getEnemyPokemon()!; enemy.hp = 1; - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); expect(game.scene.arena.weather?.weatherType).toBeUndefined(); diff --git a/test/moves/baddy_bad.test.ts b/test/moves/baddy_bad.test.ts index ffdf9f0309c..8709b6d3eac 100644 --- a/test/moves/baddy_bad.test.ts +++ b/test/moves/baddy_bad.test.ts @@ -33,7 +33,7 @@ describe("Moves - Baddy Bad", () => { game.override.enemyMoveset(MoveId.PROTECT); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - game.move.select(MoveId.BADDY_BAD); + game.move.use(MoveId.BADDY_BAD); await game.phaseInterceptor.to("BerryPhase"); expect(game.scene.arena.tags.length).toBe(0); From ef8f17b026fd1e4ef946c91c11b2ff7ce52bb0e7 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 18:43:02 -0400 Subject: [PATCH 05/12] Fixed test --- test/abilities/lightningrod.test.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test/abilities/lightningrod.test.ts b/test/abilities/lightningrod.test.ts index d343cddedb7..d533838430c 100644 --- a/test/abilities/lightningrod.test.ts +++ b/test/abilities/lightningrod.test.ts @@ -81,7 +81,7 @@ describe("Abilities - Lightningrod", () => { it("should not redirect moves changed from electric type via ability", async () => { game.override.ability(AbilityId.NORMALIZE); - await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); const enemy1 = game.scene.getEnemyField()[0]; const enemy2 = game.scene.getEnemyField()[1]; @@ -89,14 +89,13 @@ describe("Abilities - Lightningrod", () => { enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.use(MoveId.SPLASH, BattlerIndex.PLAYER_2); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(false); }); it("should redirect moves changed to electric type via ability", async () => { - game.override.ability(AbilityId.GALVANIZE).moveset(MoveId.TACKLE); + game.override.ability(AbilityId.GALVANIZE); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); const enemy1 = game.scene.getEnemyField()[0]; @@ -104,8 +103,7 @@ describe("Abilities - Lightningrod", () => { enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; - game.move.select(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + game.move.use(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(true); From 01c1f0e1e37fd71b6d6f9c5ab8fc58043d1e08a6 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 18:55:10 -0400 Subject: [PATCH 06/12] Fixed test and comment --- test/abilities/ice_face.test.ts | 8 ++------ test/testUtils/helpers/moveHelper.ts | 4 ++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/test/abilities/ice_face.test.ts b/test/abilities/ice_face.test.ts index c42713d7e6c..6e261eb00e2 100644 --- a/test/abilities/ice_face.test.ts +++ b/test/abilities/ice_face.test.ts @@ -259,7 +259,7 @@ describe("Abilities - Ice Face", () => { const eiscue = game.scene.getEnemyPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); expect(eiscue.formIndex).toBe(icefaceForm); expect(eiscue.hasAbility(AbilityId.ICE_FACE)).toBe(true); }); @@ -269,13 +269,9 @@ describe("Abilities - Ice Face", () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.move.select(MoveId.SIMPLE_BEAM); - - await game.phaseInterceptor.to(TurnInitPhase); - const eiscue = game.scene.getEnemyPokemon()!; - expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined); + expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined(); expect(eiscue.formIndex).toBe(icefaceForm); expect(game.scene.getPlayerPokemon()!.hasAbility(AbilityId.TRACE)).toBe(true); }); diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 43e12bb8158..af572674e6e 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -67,7 +67,7 @@ export class MoveHelper extends GameManagerHelper { `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene .getField() [pkmIndex].getMoveset() - .map(pm => pm.moveId)}`, + .map(pm => MoveId[pm.moveId])}`, ); } @@ -108,7 +108,7 @@ export class MoveHelper extends GameManagerHelper { `MoveHelper.selectWithTera called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene .getField() [pkmIndex].getMoveset() - .map(pm => pm.moveId)}`, + .map(pm => MoveId[pm.moveId])}`, ); } From f399e0a8c6cc4e35c1b091ededa291607fd621bb Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 19:00:07 -0400 Subject: [PATCH 07/12] Fixed tests --- test/moves/reflect_type.test.ts | 30 +++++++++++++----------------- test/moves/spikes.test.ts | 6 +++--- 2 files changed, 16 insertions(+), 20 deletions(-) diff --git a/test/moves/reflect_type.test.ts b/test/moves/reflect_type.test.ts index 0915069764c..e07c7b43ca4 100644 --- a/test/moves/reflect_type.test.ts +++ b/test/moves/reflect_type.test.ts @@ -30,30 +30,26 @@ describe("Moves - Reflect Type", () => { }); it("will make the user Normal/Grass if targetting a typeless Pokemon affected by Forest's Curse", async () => { - game.override - .moveset([MoveId.FORESTS_CURSE, MoveId.REFLECT_TYPE]) - .startingLevel(60) - .enemySpecies(SpeciesId.CHARMANDER) - .enemyMoveset([MoveId.BURN_UP, MoveId.SPLASH]); + game.override.startingLevel(60).enemySpecies(SpeciesId.CHARMANDER); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const playerPokemon = game.scene.getPlayerPokemon(); - const enemyPokemon = game.scene.getEnemyPokemon(); + const playerPokemon = game.field.getPlayerPokemon(); + const enemyPokemon = game.field.getEnemyPokemon(); - game.move.select(MoveId.SPLASH); - await game.move.selectEnemyMove(MoveId.BURN_UP); + game.move.use(MoveId.SPLASH); + await game.move.forceEnemyMove(MoveId.BURN_UP); await game.toNextTurn(); - game.move.select(MoveId.FORESTS_CURSE); - await game.move.selectEnemyMove(MoveId.SPLASH); + game.move.use(MoveId.FORESTS_CURSE); + await game.move.forceEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - expect(enemyPokemon?.getTypes().includes(PokemonType.UNKNOWN)).toBe(true); - expect(enemyPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true); + expect(enemyPokemon.getTypes().includes(PokemonType.UNKNOWN)).toBe(true); + expect(enemyPokemon.getTypes().includes(PokemonType.GRASS)).toBe(true); - game.move.select(MoveId.REFLECT_TYPE); - await game.move.selectEnemyMove(MoveId.SPLASH); + game.move.use(MoveId.REFLECT_TYPE); + await game.move.forceEnemyMove(MoveId.SPLASH); await game.phaseInterceptor.to("TurnEndPhase"); - expect(playerPokemon?.getTypes()[0]).toBe(PokemonType.NORMAL); - expect(playerPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true); + expect(playerPokemon.getTypes()[0]).toBe(PokemonType.NORMAL); + expect(playerPokemon.getTypes().includes(PokemonType.GRASS)).toBe(true); }); }); diff --git a/test/moves/spikes.test.ts b/test/moves/spikes.test.ts index 49492701d18..f6f603ef2c4 100644 --- a/test/moves/spikes.test.ts +++ b/test/moves/spikes.test.ts @@ -82,10 +82,10 @@ describe("Moves - Spikes", () => { it("should work when all targets fainted", async () => { game.override.enemySpecies(SpeciesId.DIGLETT).battleStyle("double").startingLevel(50); - await game.classicMode.startBattle([SpeciesId.RAYQUAZA, SpeciesId.ROWLET]); + await game.classicMode.startBattle([SpeciesId.RAYQUAZA]); - game.move.select(MoveId.EARTHQUAKE); - game.move.select(MoveId.SPIKES, 1); + game.move.use(MoveId.SPIKES); + await game.doKillOpponents(); await game.phaseInterceptor.to("TurnEndPhase"); expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeDefined(); From f7989a8d3640183f0b4fc22310df09070a5434ff Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 19:02:24 -0400 Subject: [PATCH 08/12] Fixed test v2 --- test/moves/gastro_acid.test.ts | 2 +- test/moves/instruct.test.ts | 2 +- test/testUtils/helpers/moveHelper.ts | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/test/moves/gastro_acid.test.ts b/test/moves/gastro_acid.test.ts index dbaa53dcb4f..677b95df2ff 100644 --- a/test/moves/gastro_acid.test.ts +++ b/test/moves/gastro_acid.test.ts @@ -96,7 +96,7 @@ describe("Moves - Gastro Acid", () => { await game.toNextTurn(); expect(enemyPokemon?.getHpRatio()).toBeLessThan(1); - game.move.select(MoveId.SPORE); + game.move.use(MoveId.SPORE); await game.phaseInterceptor.to("BerryPhase"); expect(enemyPokemon?.status?.effect).toBeFalsy(); diff --git a/test/moves/instruct.test.ts b/test/moves/instruct.test.ts index d12859301b6..19b7a941de7 100644 --- a/test/moves/instruct.test.ts +++ b/test/moves/instruct.test.ts @@ -349,7 +349,7 @@ describe("Moves - Instruct", () => { useMode: MoveUseMode.NORMAL, }); - game.move.select(MoveId.SPLASH); + game.move.use(MoveId.SPLASH); await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); await game.toEndOfTurn(); expect(game.field.getEnemyPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL); diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index af572674e6e..206f214216b 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -67,7 +67,8 @@ export class MoveHelper extends GameManagerHelper { `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene .getField() [pkmIndex].getMoveset() - .map(pm => MoveId[pm.moveId])}`, + .map(pm => MoveId[pm.moveId]) + .join(", ")}`, ); } @@ -108,7 +109,8 @@ export class MoveHelper extends GameManagerHelper { `MoveHelper.selectWithTera called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene .getField() [pkmIndex].getMoveset() - .map(pm => MoveId[pm.moveId])}`, + .map(pm => MoveId[pm.moveId]) + .join(", ")}`, ); } From 17894af7c45333c45c4de4ff757c8be52c742d16 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 16 Jun 2025 19:24:04 -0400 Subject: [PATCH 09/12] Fixed various tests --- test/abilities/good_as_gold.test.ts | 11 +++-- test/abilities/imposter.test.ts | 26 +++------- test/abilities/storm_drain.test.ts | 7 ++- test/moves/aurora_veil.test.ts | 77 ++++++++++------------------- 4 files changed, 43 insertions(+), 78 deletions(-) diff --git a/test/abilities/good_as_gold.test.ts b/test/abilities/good_as_gold.test.ts index 89e354b1f34..2490cd43fea 100644 --- a/test/abilities/good_as_gold.test.ts +++ b/test/abilities/good_as_gold.test.ts @@ -45,7 +45,7 @@ describe("Abilities - Good As Gold", () => { const player = game.scene.getPlayerPokemon()!; - game.move.select(MoveId.SPLASH, 0); + game.move.select(MoveId.SPLASH); await game.phaseInterceptor.to("BerryPhase"); @@ -54,12 +54,13 @@ describe("Abilities - Good As Gold", () => { }); it("should block memento and prevent the user from fainting", async () => { - game.override.enemyMoveset([MoveId.MEMENTO]); + game.override.enemyAbility(AbilityId.GOOD_AS_GOLD); await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - game.move.select(MoveId.MEMENTO); + + game.move.use(MoveId.MEMENTO); await game.phaseInterceptor.to("BerryPhase"); - expect(game.scene.getPlayerPokemon()!.isFainted()).toBe(false); - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(0); + expect(game.field.getPlayerPokemon().isFainted()).toBe(false); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(0); }); it("should not block any status moves that target the field, one side, or all pokemon", async () => { diff --git a/test/abilities/imposter.test.ts b/test/abilities/imposter.test.ts index 30491139877..0023dd594da 100644 --- a/test/abilities/imposter.test.ts +++ b/test/abilities/imposter.test.ts @@ -75,8 +75,6 @@ describe("Abilities - Imposter", () => { }); it("should copy in-battle overridden stats", async () => { - game.override.enemyMoveset([MoveId.POWER_SPLIT]); - await game.classicMode.startBattle([SpeciesId.DITTO]); const player = game.scene.getPlayerPokemon()!; @@ -85,7 +83,8 @@ describe("Abilities - Imposter", () => { const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); - game.move.select(MoveId.TACKLE); + game.move.use(MoveId.TACKLE); + await game.move.forceEnemyMove(MoveId.POWER_SPLIT); await game.phaseInterceptor.to(TurnEndPhase); expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); @@ -101,31 +100,22 @@ describe("Abilities - Imposter", () => { await game.classicMode.startBattle([SpeciesId.DITTO]); const player = game.scene.getPlayerPokemon()!; - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to(TurnEndPhase); - player.getMoveset().forEach(move => { // Should set correct maximum PP without touching `ppUp` - if (move) { - if (move.moveId === MoveId.SKETCH) { - expect(move.getMovePp()).toBe(1); - } else { - expect(move.getMovePp()).toBe(5); - } - expect(move.ppUp).toBe(0); + if (move.moveId === MoveId.SKETCH) { + expect(move.getMovePp()).toBe(1); + } else { + expect(move.getMovePp()).toBe(5); } + expect(move.ppUp).toBe(0); }); }); it("should activate its ability if it copies one that activates on summon", async () => { game.override.enemyAbility(AbilityId.INTIMIDATE); - await game.classicMode.startBattle([SpeciesId.DITTO]); - game.move.select(MoveId.TACKLE); - await game.phaseInterceptor.to("MoveEndPhase"); - - expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1); + expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1); }); it("should persist transformed attributes across reloads", async () => { diff --git a/test/abilities/storm_drain.test.ts b/test/abilities/storm_drain.test.ts index 8eedf0c7ce8..28085138d75 100644 --- a/test/abilities/storm_drain.test.ts +++ b/test/abilities/storm_drain.test.ts @@ -96,16 +96,15 @@ describe("Abilities - Storm Drain", () => { }); it("should redirect moves changed to water type via ability", async () => { - game.override.ability(AbilityId.LIQUID_VOICE).moveset(MoveId.PSYCHIC_NOISE); - await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); + game.override.ability(AbilityId.LIQUID_VOICE); + await game.classicMode.startBattle([SpeciesId.FEEBAS]); const enemy1 = game.scene.getEnemyField()[0]; const enemy2 = game.scene.getEnemyField()[1]; enemy2.summonData.ability = AbilityId.STORM_DRAIN; - game.move.select(MoveId.PSYCHIC_NOISE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); - game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); + game.move.use(MoveId.HYPER_VOICE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(true); diff --git a/test/moves/aurora_veil.test.ts b/test/moves/aurora_veil.test.ts index b9ae79e4155..8faf3654401 100644 --- a/test/moves/aurora_veil.test.ts +++ b/test/moves/aurora_veil.test.ts @@ -4,7 +4,6 @@ import type Move from "#app/data/moves/move"; import { allMoves } from "#app/data/data-lists"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; -import { TurnEndPhase } from "#app/phases/turn-end-phase"; import { NumberHolder } from "#app/utils/common"; import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; @@ -12,7 +11,7 @@ import { SpeciesId } from "#enums/species-id"; import { WeatherType } from "#enums/weather-type"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; let globalScene: BattleScene; @@ -52,10 +51,10 @@ describe("Moves - Aurora Veil", () => { game.move.select(moveToUse); - await game.phaseInterceptor.to(TurnEndPhase); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -71,10 +70,10 @@ describe("Moves - Aurora Veil", () => { game.move.select(moveToUse); game.move.select(moveToUse, 1); - await game.phaseInterceptor.to(TurnEndPhase); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), allMoves[moveToUse], ); @@ -82,72 +81,48 @@ describe("Moves - Aurora Veil", () => { }); it("reduces damage of special attacks by half in a single battle", async () => { - const moveToUse = MoveId.ABSORB; await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - game.move.select(moveToUse); + game.move.use(MoveId.ABSORB); - await game.phaseInterceptor.to(TurnEndPhase); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), + allMoves[MoveId.ABSORB], ); - expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier); + expect(mockedDmg).toBe(allMoves[MoveId.ABSORB].power * singleBattleMultiplier); }); it("reduces damage of special attacks by a third in a double battle", async () => { game.override.battleStyle("double"); - - const moveToUse = MoveId.DAZZLING_GLEAM; - await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE]); - - game.move.select(moveToUse); - game.move.select(moveToUse, 1); - - await game.phaseInterceptor.to(TurnEndPhase); - const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], - ); - - expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); - }); - - it("does not affect physical critical hits", async () => { - game.override.moveset([MoveId.WICKED_BLOW]); - const moveToUse = MoveId.WICKED_BLOW; await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - game.move.select(moveToUse); - await game.phaseInterceptor.to(TurnEndPhase); - + game.move.use(MoveId.ABSORB); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), + allMoves[MoveId.ABSORB], ); - expect(mockedDmg).toBe(allMoves[moveToUse].power); + + expect(mockedDmg).toBe(allMoves[MoveId.ABSORB].power * doubleBattleMultiplier); }); it("does not affect critical hits", async () => { - game.override.moveset([MoveId.FROST_BREATH]); - const moveToUse = MoveId.FROST_BREATH; - vi.spyOn(allMoves[MoveId.FROST_BREATH], "accuracy", "get").mockReturnValue(100); await game.classicMode.startBattle([SpeciesId.SHUCKLE]); - game.move.select(moveToUse); - await game.phaseInterceptor.to(TurnEndPhase); + game.move.use(MoveId.WICKED_BLOW); + await game.toEndOfTurn(); const mockedDmg = getMockedMoveDamage( - game.scene.getEnemyPokemon()!, - game.scene.getPlayerPokemon()!, - allMoves[moveToUse], + game.field.getEnemyPokemon(), + game.field.getPlayerPokemon(), + allMoves[MoveId.WICKED_BLOW], ); - expect(mockedDmg).toBe(allMoves[moveToUse].power); + expect(mockedDmg).toBe(allMoves[MoveId.WICKED_BLOW].power); }); }); From 3d0c050a5ed1894656229998af44bc2d2d50fff6 Mon Sep 17 00:00:00 2001 From: Bertie690 <136088738+Bertie690@users.noreply.github.com> Date: Tue, 17 Jun 2025 07:57:02 -0400 Subject: [PATCH 10/12] Update error msg to not use fullcaps --- test/testUtils/helpers/moveHelper.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index 206f214216b..dcef00a9a46 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -64,10 +64,12 @@ export class MoveHelper extends GameManagerHelper { const movePosition = this.getMovePosition(pkmIndex, move); if (movePosition === -1) { expect.fail( - `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene + `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset! + Battler Index: ${toReadableString(BattlerIndex[pkmIndex])}; + Moveset: ${this.game.scene .getField() [pkmIndex].getMoveset() - .map(pm => MoveId[pm.moveId]) + .map(pm => toReadableString(MoveId[pm.moveId])) .join(", ")}`, ); } @@ -106,10 +108,12 @@ export class MoveHelper extends GameManagerHelper { const movePosition = this.getMovePosition(pkmIndex, move); if (movePosition === -1) { expect.fail( - `MoveHelper.selectWithTera called with move ${toReadableString(MoveId[move])} not in moveset;\nBattler Index: ${BattlerIndex[pkmIndex]};\nMoveset: ${this.game.scene + `MoveHelper.selectWithTera called with move '${toReadableString(MoveId[move])}'not in moveset! + Battler Index: ${toReadableString(BattlerIndex[pkmIndex])}; + Moveset: ${this.game.scene .getField() [pkmIndex].getMoveset() - .map(pm => MoveId[pm.moveId]) + .map(pm => toReadableString(MoveId[pm.moveId])) .join(", ")}`, ); } From 79121a8d01c626bfcf35b607648ef82b30b90a21 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Tue, 17 Jun 2025 08:34:00 -0400 Subject: [PATCH 11/12] Fixed remaining tests --- test/abilities/mold_breaker.test.ts | 33 +++++++++++++--------------- test/items/dire_hit.test.ts | 3 +-- test/testUtils/helpers/moveHelper.ts | 20 ++++++++--------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/test/abilities/mold_breaker.test.ts b/test/abilities/mold_breaker.test.ts index 28a077e8908..3fac3448f4d 100644 --- a/test/abilities/mold_breaker.test.ts +++ b/test/abilities/mold_breaker.test.ts @@ -1,8 +1,6 @@ -import { BattlerIndex } from "#enums/battler-index"; -import { globalScene } from "#app/global-scene"; -import { AbilityId } from "#enums/ability-id"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; +import { AbilityId } from "#enums/ability-id"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; @@ -24,29 +22,28 @@ describe("Abilities - Mold Breaker", () => { beforeEach(() => { game = new GameManager(phaserGame); game.override - .moveset([MoveId.SPLASH]) .ability(AbilityId.MOLD_BREAKER) .battleStyle("single") .criticalHits(false) .enemySpecies(SpeciesId.MAGIKARP) - .enemyAbility(AbilityId.BALL_FETCH) + .enemyAbility(AbilityId.STURDY) .enemyMoveset(MoveId.SPLASH); }); it("should turn off the ignore abilities arena variable after the user's move", async () => { - game.override - .enemyMoveset(MoveId.SPLASH) - .ability(AbilityId.MOLD_BREAKER) - .moveset([MoveId.ERUPTION]) - .startingLevel(100) - .enemyLevel(2); - await game.classicMode.startBattle([SpeciesId.MAGIKARP]); - const enemy = game.scene.getEnemyPokemon()!; + await game.classicMode.startBattle([SpeciesId.PINSIR]); - expect(enemy.isFainted()).toBe(false); - game.move.select(MoveId.SPLASH); - await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); - await game.phaseInterceptor.to("MoveEndPhase", true); - expect(globalScene.arena.ignoreAbilities).toBe(false); + const player = game.field.getPlayerPokemon(); + const enemy = game.field.getEnemyPokemon(); + + game.move.use(MoveId.X_SCISSOR); + await game.phaseInterceptor.to("MoveEffectPhase"); + + expect(game.scene.arena.ignoreAbilities).toBe(true); + expect(game.scene.arena.ignoringEffectSource).toBe(player.getBattlerIndex()); + + await game.toEndOfTurn(); + expect(game.scene.arena.ignoreAbilities).toBe(false); + expect(enemy.isFainted()).toBe(true); }); }); diff --git a/test/items/dire_hit.test.ts b/test/items/dire_hit.test.ts index 25fe9c8b876..a484a0ad302 100644 --- a/test/items/dire_hit.test.ts +++ b/test/items/dire_hit.test.ts @@ -58,8 +58,7 @@ describe("Items - Dire Hit", () => { await game.classicMode.startBattle([SpeciesId.PIKACHU]); - game.move.select(MoveId.SPLASH); - + game.move.use(MoveId.SPLASH); await game.doKillOpponents(); await game.phaseInterceptor.to(BattleEndPhase); diff --git a/test/testUtils/helpers/moveHelper.ts b/test/testUtils/helpers/moveHelper.ts index dcef00a9a46..b7b0fefa4db 100644 --- a/test/testUtils/helpers/moveHelper.ts +++ b/test/testUtils/helpers/moveHelper.ts @@ -64,13 +64,13 @@ export class MoveHelper extends GameManagerHelper { const movePosition = this.getMovePosition(pkmIndex, move); if (movePosition === -1) { expect.fail( - `MoveHelper.select called with move ${toReadableString(MoveId[move])} not in moveset! - Battler Index: ${toReadableString(BattlerIndex[pkmIndex])}; - Moveset: ${this.game.scene - .getField() + `MoveHelper.selectWithTera called with move '${toReadableString(MoveId[move])}' not in moveset! +Battler Index: ${toReadableString(BattlerIndex[pkmIndex])}; +Moveset: [${this.game.scene + .getPlayerParty() [pkmIndex].getMoveset() .map(pm => toReadableString(MoveId[pm.moveId])) - .join(", ")}`, + .join(", ")}]`, ); } @@ -108,13 +108,13 @@ export class MoveHelper extends GameManagerHelper { const movePosition = this.getMovePosition(pkmIndex, move); if (movePosition === -1) { expect.fail( - `MoveHelper.selectWithTera called with move '${toReadableString(MoveId[move])}'not in moveset! - Battler Index: ${toReadableString(BattlerIndex[pkmIndex])}; - Moveset: ${this.game.scene - .getField() + `MoveHelper.selectWithTera called with move '${toReadableString(MoveId[move])}' not in moveset! +Battler Index: ${toReadableString(BattlerIndex[pkmIndex])}; +Moveset: [${this.game.scene + .getPlayerParty() [pkmIndex].getMoveset() .map(pm => toReadableString(MoveId[pm.moveId])) - .join(", ")}`, + .join(", ")}]`, ); } From dc9d7a3e172fc8f4befaeb0f5ff073183f0630a6 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Tue, 17 Jun 2025 16:55:55 -0400 Subject: [PATCH 12/12] Fixed test 0.5 --- test/abilities/imposter.test.ts | 28 ++++++++++++++++++---------- test/abilities/lightningrod.test.ts | 27 +++++++++------------------ test/abilities/storm_drain.test.ts | 2 +- 3 files changed, 28 insertions(+), 29 deletions(-) diff --git a/test/abilities/imposter.test.ts b/test/abilities/imposter.test.ts index 0023dd594da..976bc339dde 100644 --- a/test/abilities/imposter.test.ts +++ b/test/abilities/imposter.test.ts @@ -75,23 +75,31 @@ describe("Abilities - Imposter", () => { }); it("should copy in-battle overridden stats", async () => { - await game.classicMode.startBattle([SpeciesId.DITTO]); + game.override.ability(AbilityId.BALL_FETCH); + await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.DITTO]); - const player = game.scene.getPlayerPokemon()!; - const enemy = game.scene.getEnemyPokemon()!; + const [karp, ditto] = game.scene.getPlayerField(); + const enemy = game.field.getEnemyPokemon(); + game.field.mockAbility(ditto, AbilityId.IMPOSTER); - const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); - const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); + // Turn 1: Use power split + const avgAtk = Math.floor((karp.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2); + const avgSpAtk = Math.floor((karp.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2); - game.move.use(MoveId.TACKLE); + game.move.use(MoveId.SPLASH); await game.move.forceEnemyMove(MoveId.POWER_SPLIT); - await game.phaseInterceptor.to(TurnEndPhase); + await game.toNextTurn(); - expect(player.getStat(Stat.ATK, false)).toBe(avgAtk); expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk); - - expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk); expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk); + + // Turn 2: Switch in ditto, should copy enemy ability + game.doSwitchPokemon(1); + await game.move.forceEnemyMove(MoveId.SPLASH); + await game.toNextTurn(); + + expect(ditto.getStat(Stat.ATK, false)).toBe(avgAtk); + expect(ditto.getStat(Stat.SPATK, false)).toBe(avgSpAtk); }); it("should set each move's pp to a maximum of 5", async () => { diff --git a/test/abilities/lightningrod.test.ts b/test/abilities/lightningrod.test.ts index d533838430c..028258a9d56 100644 --- a/test/abilities/lightningrod.test.ts +++ b/test/abilities/lightningrod.test.ts @@ -36,10 +36,8 @@ describe("Abilities - Lightningrod", () => { it("should redirect electric type moves", async () => { await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -52,10 +50,8 @@ describe("Abilities - Lightningrod", () => { game.override.moveset([MoveId.SPLASH, MoveId.AERIAL_ACE]); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.AERIAL_ACE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -68,8 +64,7 @@ describe("Abilities - Lightningrod", () => { await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2); @@ -83,10 +78,8 @@ describe("Abilities - Lightningrod", () => { game.override.ability(AbilityId.NORMALIZE); await game.classicMode.startBattle([SpeciesId.FEEBAS]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); await game.phaseInterceptor.to("BerryPhase"); @@ -98,10 +91,8 @@ describe("Abilities - Lightningrod", () => { game.override.ability(AbilityId.GALVANIZE); await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]); - const enemy1 = game.scene.getEnemyField()[0]; - const enemy2 = game.scene.getEnemyField()[1]; - - enemy2.summonData.ability = AbilityId.LIGHTNING_ROD; + const [enemy1, enemy2] = game.scene.getEnemyField(); + game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD); game.move.use(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); await game.phaseInterceptor.to("BerryPhase"); diff --git a/test/abilities/storm_drain.test.ts b/test/abilities/storm_drain.test.ts index 28085138d75..6e56bf44fa7 100644 --- a/test/abilities/storm_drain.test.ts +++ b/test/abilities/storm_drain.test.ts @@ -104,7 +104,7 @@ describe("Abilities - Storm Drain", () => { enemy2.summonData.ability = AbilityId.STORM_DRAIN; - game.move.use(MoveId.HYPER_VOICE, BattlerIndex.PLAYER, BattlerIndex.ENEMY); + game.move.use(MoveId.HYPER_VOICE, BattlerIndex.PLAYER); await game.phaseInterceptor.to("BerryPhase"); expect(enemy1.isFullHp()).toBe(true);