From 268131a4f6e2b7af6c9d76aeb775c6a9a27140c8 Mon Sep 17 00:00:00 2001 From: Bertie690 Date: Mon, 30 Dec 2024 18:37:46 -0500 Subject: [PATCH] Added utility functions --- src/battle-scene.ts | 4 +- src/field/pokemon.ts | 9 ++- src/overrides.ts | 5 +- src/test/phases/learn-move-phase.test.ts | 93 +++++++++++++++++++++-- src/test/utils/helpers/moveHelper.ts | 6 +- src/test/utils/helpers/overridesHelper.ts | 21 +++++ 6 files changed, 125 insertions(+), 13 deletions(-) diff --git a/src/battle-scene.ts b/src/battle-scene.ts index c430a12ae3e..90f9c073f95 100644 --- a/src/battle-scene.ts +++ b/src/battle-scene.ts @@ -1823,7 +1823,9 @@ export default class BattleScene extends SceneBase { } getMaxExpLevel(ignoreLevelCap?: boolean): integer { - if (ignoreLevelCap) { + if (Overrides.LEVEL_CAP_OVERRIDE > 0) { + return Overrides.LEVEL_CAP_OVERRIDE; + } else if (ignoreLevelCap || Overrides.LEVEL_CAP_OVERRIDE < 0) { return Number.MAX_SAFE_INTEGER; } const waveIndex = Math.ceil((this.currentBattle?.waveIndex || 1) / 10) * 10; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index fcfc2ff7536..d1ca0bbd421 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2378,8 +2378,13 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.battleInfo.toggleFlyout(visible); } - addExp(exp: integer) { - const maxExpLevel = this.scene.getMaxExpLevel(); + /** + * Adds experience to this PlayerPokemon, subject to wave based level caps. + * @param exp The amount of experience to add + * @param ignoreLevelCap Whether to ignore level caps when adding experience (defaults to false) + */ + addExp(exp: integer, ignoreLevelCap?: boolean) { + const maxExpLevel = this.scene.getMaxExpLevel(ignoreLevelCap); const initialExp = this.exp; this.exp += exp; while (this.level < maxExpLevel && this.exp >= getLevelTotalExp(this.level + 1, this.species.growthRate)) { diff --git a/src/overrides.ts b/src/overrides.ts index 85be47d95cc..6cc9873979d 100644 --- a/src/overrides.ts +++ b/src/overrides.ts @@ -62,8 +62,11 @@ class DefaultOverrides { readonly STARTING_WAVE_OVERRIDE: number = 0; readonly STARTING_BIOME_OVERRIDE: Biome = Biome.TOWN; readonly ARENA_TINT_OVERRIDE: TimeOfDay | null = null; - /** Multiplies XP gained by this value including 0. Set to null to ignore the override */ + /** Multiplies XP gained by this value including 0. Set to null to ignore the override. */ readonly XP_MULTIPLIER_OVERRIDE: number | null = null; + /** Sets the level cap to this number during experience gain calculations. Set to null/0 to disable override & use normal wave-based level caps, + or any negative number to set it to 9 quadrillion (effectively disabling it). */ + readonly LEVEL_CAP_OVERRIDE: number = 0; readonly NEVER_CRIT_OVERRIDE: boolean = false; /** default 1000 */ readonly STARTING_MONEY_OVERRIDE: number = 0; diff --git a/src/test/phases/learn-move-phase.test.ts b/src/test/phases/learn-move-phase.test.ts index c4fa0e8bf45..f17eef0376a 100644 --- a/src/test/phases/learn-move-phase.test.ts +++ b/src/test/phases/learn-move-phase.test.ts @@ -4,6 +4,8 @@ import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; import { Moves } from "#enums/moves"; import { LearnMovePhase } from "#app/phases/learn-move-phase"; +import { Mode } from "#app/ui/ui"; +import { Button } from "#app/enums/buttons"; describe("Learn Move Phase", () => { let phaserGame: Phaser.Game; @@ -26,7 +28,7 @@ describe("Learn Move Phase", () => { it("If Pokemon has less than 4 moves, its newest move will be added to the lowest empty index", async () => { game.override.moveset([ Moves.SPLASH ]); - await game.startBattle([ Species.BULBASAUR ]); + await game.classicMode.startBattle([ Species.BULBASAUR ]); const pokemon = game.scene.getPlayerPokemon()!; const newMovePos = pokemon?.getMoveset().length; game.move.select(Moves.SPLASH); @@ -36,12 +38,89 @@ describe("Learn Move Phase", () => { const levelReq = levelMove[0]; const levelMoveId = levelMove[1]; expect(pokemon.level).toBeGreaterThanOrEqual(levelReq); - expect(pokemon?.getMoveset()[newMovePos]?.moveId).toBe(levelMoveId); + expect(pokemon?.moveset[newMovePos]?.moveId).toBe(levelMoveId); + }); + + it("If a pokemon has 4 move slots filled, the chosen move will be deleted and replaced", async () => { + await game.classicMode.startBattle([ Species.GALAR_MR_MIME ]); // many level up moves + const mrMime = game.scene.getPlayerPokemon()!; + const prevMoveset = [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]; + const moveSlotNum = 3; + + game.move.changeMoveset(mrMime, prevMoveset); + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + + // queue up inputs to confirm dialog boxes + game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + game.scene.ui.processInput(Button.ACTION); + }); + game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => { + for (let x = 0; x < moveSlotNum; x++) { + game.scene.ui.processInput(Button.DOWN); + } + game.scene.ui.processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(LearnMovePhase); + + const levelMove = mrMime.getLevelMoves(5)[0]; + const levelReq = levelMove[0]; + const levelMoveId = levelMove[1]; + expect(mrMime.level).toBeGreaterThanOrEqual(levelReq); + // Check each of mr mime's moveslots to make sure the changed move (and ONLY the changed move) is different + mrMime.getMoveset().forEach((move, index) => { + const expectedMove: Moves = (index === moveSlotNum ? levelMoveId : prevMoveset[index]); + expect(move?.moveId).toBe(expectedMove); + }); + }); + + it("selecting the newly deleted move will reject it and keep old moveset", async () => { + await game.classicMode.startBattle([ Species.GALAR_MR_MIME ]); // many level up moves + const mrMime = game.scene.getPlayerPokemon()!; + const prevMoveset = [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]; + + game.move.changeMoveset(mrMime, [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]); + game.move.select(Moves.SPLASH); + await game.doKillOpponents(); + + // queue up inputs to confirm dialog boxes + game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + game.scene.ui.processInput(Button.ACTION); + }); + game.onNextPrompt("LearnMovePhase", Mode.SUMMARY, () => { + for (let x = 0; x < 4; x++) { + game.scene.ui.processInput(Button.DOWN); // moves down 4 times to the 5th move slot + } + game.scene.ui.processInput(Button.ACTION); + }); + game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + game.scene.ui.processInput(Button.ACTION); + }); + await game.phaseInterceptor.to(LearnMovePhase); + + const levelReq = mrMime.getLevelMoves(5)[0][0]; + expect(mrMime.level).toBeGreaterThanOrEqual(levelReq); + expect(mrMime.getMoveset()).toEqual(prevMoveset); + }); + + it("macro should work", async () => { + await game.classicMode.startBattle([ Species.GALAR_MR_MIME ]); // many level up moves + const mrMime = game.scene.getPlayerPokemon()!; + + game.move.changeMoveset(mrMime, [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]); + await game.move.learnMove(Moves.SACRED_FIRE, 0, 3); + expect(mrMime.getMoveset()).toEqual([ Moves.SPLASH, Moves.ABSORB, Moves.SACRED_FIRE, Moves.VINE_WHIP ]); + + }); + + it("macro should work V2", async () => { + await game.classicMode.startBattle([ Species.GALAR_MR_MIME ]); // many level up moves + const mrMime = game.scene.getPlayerPokemon()!; + + game.move.changeMoveset(mrMime, [ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]); + await game.move.learnMove(Moves.SACRED_FIRE, 0, 4); + expect(mrMime.getMoveset()).toEqual([ Moves.SPLASH, Moves.ABSORB, Moves.ACID, Moves.VINE_WHIP ]); + }); - /** - * Future Tests: - * If a Pokemon has four moves, the user can specify an old move to be forgotten and a new move will take its place. - * If a Pokemon has four moves, the user can reject the new move, keeping the moveset the same. - */ }); diff --git a/src/test/utils/helpers/moveHelper.ts b/src/test/utils/helpers/moveHelper.ts index d4236d4941a..2f10011f04f 100644 --- a/src/test/utils/helpers/moveHelper.ts +++ b/src/test/utils/helpers/moveHelper.ts @@ -119,8 +119,10 @@ export class MoveHelper extends GameManagerHelper { this.game.scene.ui.processInput(Button.DOWN); } this.game.scene.ui.processInput(Button.ACTION); - if (slot === 4) { // confirm 1 last time to give up on learning move - this.game.scene.ui.processInput(Button.ACTION); + if (slot === 4) { // hit confirm 1 last time to give up on learning move + this.game.onNextPrompt("LearnMovePhase", Mode.CONFIRM, () => { + this.game.scene.ui.processInput(Button.ACTION); + }); } }); } diff --git a/src/test/utils/helpers/overridesHelper.ts b/src/test/utils/helpers/overridesHelper.ts index 1c05f92a334..19e4c3322cc 100644 --- a/src/test/utils/helpers/overridesHelper.ts +++ b/src/test/utils/helpers/overridesHelper.ts @@ -69,6 +69,27 @@ export class OverridesHelper extends GameManagerHelper { return this; } + /** + * Override the wave level cap + * @param cap the level cap value to set; 0 uses normal level caps and negative values + * disable it completely + * @returns `this` + */ + public levelCap(cap: number): this { + vi.spyOn(Overrides, "LEVEL_CAP_OVERRIDE", "get").mockReturnValue(cap); + let capStr: string; + switch (true) { + case (cap > 0): + capStr = "Level cap set to " + cap.toString() + "!"; + case (cap === 0): + capStr = "Level cap reset to default value for wave."; + case (cap < 0): + capStr = "Level cap disabled!"; + } + this.log(capStr!); // cap is guaranteed to be either more, less or equal to 0; if not I will scream + return this; + } + /** * Override the player (pokemon) starting held items * @param items the items to hold