diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index d7da1ab996c..c949fc4fe88 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -859,6 +859,11 @@ export class MoveEffectPhase extends PokemonPhase { const substitute = target.getTag(SubstituteTag); const isBlockedBySubstitute = substitute && this.move.hitsSubstitute(user, target); if (isBlockedBySubstitute) { + if (substitute.hp >= dmg) { + user.turnData.totalDamageDealt += dmg; + } else { + user.turnData.totalDamageDealt += substitute.hp; + } substitute.hp -= dmg; } else if (!target.isPlayer() && dmg >= target.hp) { globalScene.applyModifiers(EnemyEndureChanceModifier, false, target); diff --git a/test/moves/recoil-moves.test.ts b/test/moves/recoil-moves.test.ts new file mode 100644 index 00000000000..1ceba4b39f7 --- /dev/null +++ b/test/moves/recoil-moves.test.ts @@ -0,0 +1,79 @@ +import { Moves } from "#enums/moves"; +import { Species } from "#enums/species"; +import GameManager from "#test/testUtils/gameManager"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { Abilities } from "#enums/abilities"; + +describe("Moves - Recoil Moves", () => { + 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 + .battleStyle("single") + .enemySpecies(Species.PIDOVE) + .startingLevel(1) + .enemyLevel(100) + .enemyMoveset(Moves.SUBSTITUTE) + .disableCrits() + .ability(Abilities.NO_GUARD) + .enemyAbility(Abilities.BALL_FETCH); + }); + + it.each([ + { moveName: "Double Edge", moveId: Moves.DOUBLE_EDGE }, + { moveName: "Brave Bird", moveId: Moves.BRAVE_BIRD }, + { moveName: "Flare Blitz", moveId: Moves.FLARE_BLITZ }, + { moveName: "Head Charge", moveId: Moves.HEAD_CHARGE }, + { moveName: "Head Smash", moveId: Moves.HEAD_SMASH }, + { moveName: "Light of Ruin", moveId: Moves.LIGHT_OF_RUIN }, + { moveName: "Struggle", moveId: Moves.STRUGGLE }, + { moveName: "Submission", moveId: Moves.SUBMISSION }, + { moveName: "Take Down", moveId: Moves.TAKE_DOWN }, + { moveName: "Volt Tackle", moveId: Moves.VOLT_TACKLE }, + { moveName: "Wave Crash", moveId: Moves.WAVE_CRASH }, + { moveName: "Wild Charge", moveId: Moves.WILD_CHARGE }, + { moveName: "Wood Hammer", moveId: Moves.WOOD_HAMMER }, + ])("$moveName causes recoil damage when hitting a substitute", async ({ moveId }) => { + game.override.moveset([moveId]); + await game.classicMode.startBattle([Species.TOGEPI]); + + game.move.select(moveId); + await game.toNextTurn(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp()); + }); + + it("causes recoil damage when hitting a substitute in a double battle", async () => { + game.override.battleStyle("double").moveset([Moves.DOUBLE_EDGE]); + + await game.classicMode.startBattle([Species.TOGEPI, Species.TOGEPI]); + + const [playerPokemon1, playerPokemon2] = game.scene.getPlayerField(); + + game.move.select(Moves.DOUBLE_EDGE, 0); + game.move.select(Moves.DOUBLE_EDGE, 1); + + await game.phaseInterceptor.to("TurnEndPhase", false); + await game.toNextTurn(); + + console.log(playerPokemon1.hp); + console.log(playerPokemon2.hp); + + expect(playerPokemon1.hp).toBeLessThan(playerPokemon1.getMaxHp()); + expect(playerPokemon2.hp).toBeLessThan(playerPokemon2.getMaxHp()); + }); +});