diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 6a8f8f02b92..8ceb1648257 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2020,7 +2020,7 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { this.scene.applyModifiers(TempStatStageBoosterModifier, this.isPlayer(), Stat.ACC, userAccStage); - userAccStage.value = ignoreAccStatStage.value ? 0 : userAccStage.value; + userAccStage.value = ignoreAccStatStage.value ? 0 : Math.min(userAccStage.value, 6); targetEvaStage.value = ignoreEvaStatStage.value ? 0 : targetEvaStage.value; if (target.findTag(t => t instanceof ExposedTag)) { diff --git a/src/test/abilities/moody.test.ts b/src/test/abilities/moody.test.ts index 9e936e8100a..2404e1edb14 100644 --- a/src/test/abilities/moody.test.ts +++ b/src/test/abilities/moody.test.ts @@ -1,18 +1,16 @@ -import { BattleStat } from "#app/data/battle-stat"; +import { BATTLE_STATS } from "#enums/stat"; import { Abilities } from "#enums/abilities"; import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/utils/gameManager"; import { SPLASH_ONLY } from "#test/utils/testUtils"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; describe("Abilities - Moody", () => { let phaserGame: Phaser.Game; let game: GameManager; - const battleStatsArray = [BattleStat.ATK, BattleStat.DEF, BattleStat.SPATK, BattleStat.SPDEF, BattleStat.SPD]; - beforeAll(() => { phaserGame = new Phaser.Game({ type: Phaser.HEADLESS, @@ -36,8 +34,7 @@ describe("Abilities - Moody", () => { .moveset(SPLASH_ONLY); }); - it( - "should increase one BattleStat by 2 stages and decrease a different BattleStat by 1 stage", + it("should increase one stat stage by 2 and decrease a different stat stage by 1", async () => { await game.startBattle(); @@ -46,47 +43,47 @@ describe("Abilities - Moody", () => { await game.toNextTurn(); // Find the increased and decreased stats, make sure they are different. - const statChanges = playerPokemon.summonData.battleStats; - const changedStats = battleStatsArray.filter(bs => statChanges[bs] === 2 || statChanges[bs] === -1); + const changedStats = BATTLE_STATS.filter(s => playerPokemon.getStatStage(s) === 2 || playerPokemon.getStatStage(s) === -1); expect(changedStats).toBeTruthy(); expect(changedStats.length).toBe(2); expect(changedStats[0] !== changedStats[1]).toBeTruthy(); }); - it( - "should only increase one BattleStat by 2 stages if all BattleStats are at -6", + it("should only increase one stat stage by 2 if all stat stages are at -6", async () => { await game.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; - // Set all BattleStats to -6 - battleStatsArray.forEach(bs => playerPokemon.summonData.battleStats[bs] = -6); + + // Set all stat stages to -6 + vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(-6)); game.move.select(Moves.SPLASH); await game.toNextTurn(); // Should increase one BattleStat by 2 (from -6, meaning it will be -4) - const increasedStat = battleStatsArray.filter(bs => playerPokemon.summonData.battleStats[bs] === -4); + const increasedStat = BATTLE_STATS.filter(s => playerPokemon.getStatStage(s) === -4); expect(increasedStat).toBeTruthy(); expect(increasedStat.length).toBe(1); }); - it( - "should only decrease one BattleStat by 1 stage if all BattleStats are at 6", + it("should only decrease one stat stage by 1 stage if all stat stages are at 6", async () => { await game.startBattle(); const playerPokemon = game.scene.getPlayerPokemon()!; - // Set all BattleStats to 6 - battleStatsArray.forEach(bs => playerPokemon.summonData.battleStats[bs] = 6); + + // Set all stat stages to 6 + vi.spyOn(playerPokemon.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(6)); game.move.select(Moves.SPLASH); await game.toNextTurn(); // Should decrease one BattleStat by 1 (from 6, meaning it will be 5) - const decreasedStat = battleStatsArray.filter(bs => playerPokemon.summonData.battleStats[bs] === 5); + const decreasedStat = BATTLE_STATS.filter(s => playerPokemon.getStatStage(s) === 5); + expect(decreasedStat).toBeTruthy(); expect(decreasedStat.length).toBe(1); }); diff --git a/src/test/items/temp_stat_stage_booster.test.ts b/src/test/items/temp_stat_stage_booster.test.ts index f02a6c6e741..bb976dcf828 100644 --- a/src/test/items/temp_stat_stage_booster.test.ts +++ b/src/test/items/temp_stat_stage_booster.test.ts @@ -1,4 +1,4 @@ -import { Stat } from "#enums/stat"; +import { BATTLE_STATS, Stat } from "#enums/stat"; import GameManager from "#test/utils/gameManager"; import { Species } from "#enums/species"; import Phase from "phaser"; @@ -36,14 +36,14 @@ describe("Items - Temporary Stat Stage Boosters", () => { game = new GameManager(phaserGame); game.override.battleType("single"); - game.override.enemySpecies(Species.MEW); + game.override.enemySpecies(Species.SHUCKLE); game.override.enemyMoveset(SPLASH_ONLY); game.override.enemyAbility(Abilities.BALL_FETCH); game.override.moveset([ Moves.TACKLE, Moves.SPLASH, Moves.HONE_CLAWS, Moves.BELLY_DRUM ]); game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]); }); - it("should provide a x1.3 stat stage multiplier alone", async() => { + it("should provide a x1.3 stat stage multiplier", async() => { await game.startBattle([ Species.PIKACHU ]); @@ -59,15 +59,19 @@ describe("Items - Temporary Stat Stage Boosters", () => { expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3); }, 20000); - it("should only increase existing stat stage multiplier by 0.3", async() => { + it("should increase existing ACC stat stage by 1 for X_ACCURACY only", async() => { + game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }]); + game.override.ability(Abilities.SIMPLE); + await game.startBattle([ Species.PIKACHU ]); const partyMember = game.scene.getPlayerPokemon()!; - vi.spyOn(partyMember, "getStatStageMultiplier"); + vi.spyOn(partyMember, "getAccuracyMultiplier"); + // Raise ACC by +2 stat stages game.move.select(Moves.HONE_CLAWS); await game.phaseInterceptor.to(TurnEndPhase); @@ -76,10 +80,12 @@ describe("Items - Temporary Stat Stage Boosters", () => { await game.phaseInterceptor.to(TurnEndPhase); - expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8); + // ACC at +3 stat stages yields a x2 multiplier + expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2); }, 20000); - it("should not increase past x4 maximum stat stage multiplier", async() => { + + it("should increase existing stat stage multiplier by 3/10 for the rest of the boosters", async() => { await game.startBattle([ Species.PIKACHU ]); @@ -88,7 +94,8 @@ describe("Items - Temporary Stat Stage Boosters", () => { vi.spyOn(partyMember, "getStatStageMultiplier"); - game.move.select(Moves.BELLY_DRUM); + // Raise ATK by +1 stat stage + game.move.select(Moves.HONE_CLAWS); await game.phaseInterceptor.to(TurnEndPhase); @@ -96,12 +103,37 @@ describe("Items - Temporary Stat Stage Boosters", () => { await game.phaseInterceptor.to(TurnEndPhase); + // ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK + expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8); + }, 20000); + + it("should not increase past maximum stat stage multiplier", async() => { + game.override.startingModifier([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ACC }, { name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]); + + await game.startBattle([ + Species.PIKACHU + ]); + + const partyMember = game.scene.getPlayerPokemon()!; + + vi.spyOn(partyMember, "getStatStageMultiplier"); + vi.spyOn(partyMember, "getAccuracyMultiplier"); + + // Set all stat stages to 6 + vi.spyOn(partyMember.summonData, "statStages", "get").mockReturnValue(new Array(BATTLE_STATS.length).fill(6)); + + game.move.select(Moves.TACKLE); + + await game.phaseInterceptor.to(TurnEndPhase); + + expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3); expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4); }, 20000); it("should renew how many battles are left of existing booster when picking up new booster of same type", async() => { game.override.startingLevel(200); game.override.itemRewards([{ name: "TEMP_STAT_STAGE_BOOSTER", type: Stat.ATK }]); + await game.startBattle([ Species.PIKACHU ]); @@ -115,9 +147,12 @@ describe("Items - Temporary Stat Stage Boosters", () => { const modifier = game.scene.findModifier(m => m instanceof TempStatStageBoosterModifier) as TempStatStageBoosterModifier; expect(modifier.getBattlesLeft()).toBe(4); - // Forced X Attack to spawn in the first slot with override + // Forced X_ATTACK to spawn in the first slot with override game.onNextPrompt("SelectModifierPhase", Mode.MODIFIER_SELECT, () => { const handler = game.scene.ui.getHandler() as ModifierSelectUiHandler; + // Traverse to first modifier slot + handler.processInput(Button.LEFT); + handler.processInput(Button.UP); handler.processInput(Button.ACTION); }, () => game.isCurrentPhase(CommandPhase) || game.isCurrentPhase(NewBattlePhase), true);