diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index 5058a83d11a..c02e7d15484 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -219,11 +219,14 @@ export class MovePhase extends BattlePhase { this.showMoveText(); - // TODO: Clean up implementation of two-turn moves. if (moveQueue.length > 0) { // Using .shift here clears out two turn moves once they've been used this.ignorePp = moveQueue.shift()?.ignorePP ?? false; } + if (this.pokemon.getTag(BattlerTagType.CHARGING)?.sourceMove === this.move.moveId) { + this.pokemon.lapseTag(BattlerTagType.CHARGING); + } + // "commit" to using the move, deducting PP. if (!this.ignorePp) { const ppUsed = 1 + this.getPpIncreaseFromPressure(targets); diff --git a/src/test/moves/dig.test.ts b/src/test/moves/dig.test.ts index cbb3d7fb2b9..4c6b5d3b75d 100644 --- a/src/test/moves/dig.test.ts +++ b/src/test/moves/dig.test.ts @@ -82,13 +82,14 @@ describe("Moves - Dig", () => { await game.classicMode.startBattle([ Species.MAGIKARP ]); const playerPokemon = game.scene.getPlayerPokemon()!; - const playerDig = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.DIG); game.move.select(Moves.DIG); await game.phaseInterceptor.to("TurnEndPhase"); expect(playerPokemon.getTag(BattlerTagType.UNDERGROUND)).toBeUndefined(); expect(playerPokemon.status?.effect).toBe(StatusEffect.SLEEP); + + const playerDig = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.DIG); expect(playerDig?.ppUsed).toBe(0); }); diff --git a/src/test/moves/dive.test.ts b/src/test/moves/dive.test.ts index 3840595d77b..cdb6523cbda 100644 --- a/src/test/moves/dive.test.ts +++ b/src/test/moves/dive.test.ts @@ -82,13 +82,14 @@ describe("Moves - Dive", () => { await game.classicMode.startBattle([ Species.MAGIKARP ]); const playerPokemon = game.scene.getPlayerPokemon()!; - const playerDive = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.DIVE); game.move.select(Moves.DIVE); await game.phaseInterceptor.to("TurnEndPhase"); expect(playerPokemon.getTag(BattlerTagType.UNDERWATER)).toBeUndefined(); expect(playerPokemon.status?.effect).toBe(StatusEffect.SLEEP); + + const playerDive = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.DIVE); expect(playerDive?.ppUsed).toBe(0); }); @@ -119,8 +120,6 @@ describe("Moves - Dive", () => { const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - const playerDive = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.DIVE); - game.move.select(Moves.DIVE); await game.phaseInterceptor.to("TurnEndPhase"); @@ -131,6 +130,8 @@ describe("Moves - Dive", () => { expect(playerPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); expect(playerPokemon.getTag(BattlerTagType.UNDERWATER)).toBeUndefined(); + + const playerDive = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.DIVE); expect(playerDive?.ppUsed).toBe(0); }); }); diff --git a/src/test/moves/fly.test.ts b/src/test/moves/fly.test.ts index 4d25e4dde74..6ae758fe3dc 100644 --- a/src/test/moves/fly.test.ts +++ b/src/test/moves/fly.test.ts @@ -85,13 +85,14 @@ describe("Moves - Fly", () => { await game.classicMode.startBattle([ Species.MAGIKARP ]); const playerPokemon = game.scene.getPlayerPokemon()!; - const playerFly = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.FLY); game.move.select(Moves.FLY); await game.phaseInterceptor.to("TurnEndPhase"); expect(playerPokemon.getTag(BattlerTagType.FLYING)).toBeUndefined(); expect(playerPokemon.status?.effect).toBe(StatusEffect.SLEEP); + + const playerFly = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.FLY); expect(playerFly?.ppUsed).toBe(0); }); @@ -103,8 +104,6 @@ describe("Moves - Fly", () => { const playerPokemon = game.scene.getPlayerPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!; - const playerFly = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.FLY); - game.move.select(Moves.FLY); await game.forceEnemyMove(Moves.SPLASH); @@ -116,6 +115,8 @@ describe("Moves - Fly", () => { await game.phaseInterceptor.to("TurnEndPhase"); expect(playerPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.FAIL); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); + + const playerFly = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.FLY); expect(playerFly?.ppUsed).toBe(0); }); }); diff --git a/src/test/moves/solar_beam.test.ts b/src/test/moves/solar_beam.test.ts new file mode 100644 index 00000000000..e367810879e --- /dev/null +++ b/src/test/moves/solar_beam.test.ts @@ -0,0 +1,103 @@ +import { allMoves } from "#app/data/move"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { WeatherType } from "#enums/weather-type"; +import { MoveResult } from "#app/field/pokemon"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/utils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, it, expect, vi } from "vitest"; + +describe("Moves - Solar Beam", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + game.override + .moveset(Moves.SOLAR_BEAM) + .battleType("single") + .startingLevel(100) + .enemySpecies(Species.SNORLAX) + .enemyLevel(100) + .enemyAbility(Abilities.BALL_FETCH) + .enemyMoveset(Moves.SPLASH); + }); + + it("should deal damage in two turns if no weather is active", async () => { + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SOLAR_BEAM); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(playerPokemon.getTag(BattlerTagType.CHARGING)).toBeDefined(); + expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp()); + expect(playerPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.OTHER); + // expect(playerSolarBeam?.ppUsed).toBe(0); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(playerPokemon.getTag(BattlerTagType.CHARGING)).toBeUndefined(); + expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); + expect(playerPokemon.getMoveHistory()).toHaveLength(2); + expect(playerPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS); + + const playerSolarBeam = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.SOLAR_BEAM); + expect(playerSolarBeam?.ppUsed).toBe(1); + }); + + it.each([ + { weatherType: WeatherType.SUNNY, name: "Sun" }, + { weatherType: WeatherType.HARSH_SUN, name: "Harsh Sun" } + ])("should deal damage in one turn if $name is active", async ({ weatherType }) => { + game.override.weather(weatherType); + + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + const enemyPokemon = game.scene.getEnemyPokemon()!; + + game.move.select(Moves.SOLAR_BEAM); + + await game.phaseInterceptor.to("TurnEndPhase"); + expect(playerPokemon.getTag(BattlerTagType.CHARGING)).toBeUndefined(); + expect(enemyPokemon.hp).toBeLessThan(enemyPokemon.getMaxHp()); + expect(playerPokemon.getMoveHistory()).toHaveLength(2); + expect(playerPokemon.getLastXMoves(1)[0].result).toBe(MoveResult.SUCCESS); + + const playerSolarBeam = playerPokemon.getMoveset().find(mv => mv && mv.moveId === Moves.SOLAR_BEAM); + expect(playerSolarBeam?.ppUsed).toBe(1); + }); + + it.each([ + { weatherType: WeatherType.RAIN, name: "Rain" }, + { weatherType: WeatherType.HEAVY_RAIN, name: "Heavy Rain" } + ])("should have its power halved in $name", async ({ weatherType }) => { + game.override.weather(weatherType); + + await game.classicMode.startBattle([ Species.MAGIKARP ]); + + const solarBeam = allMoves[Moves.SOLAR_BEAM]; + + vi.spyOn(solarBeam, "calculateBattlePower"); + + game.move.select(Moves.SOLAR_BEAM); + + await game.phaseInterceptor.to("TurnEndPhase"); + await game.phaseInterceptor.to("TurnEndPhase"); + expect(solarBeam.calculateBattlePower).toHaveLastReturnedWith(60); + }); +});