From 38a6bf07e3955348b797890c76c363cea7d2caed Mon Sep 17 00:00:00 2001 From: geeilhan <107366005+geeilhan@users.noreply.github.com> Date: Tue, 29 Oct 2024 16:36:24 +0100 Subject: [PATCH] [Bug] Fix enemy faint causing Frenzy moves to mishandle paralysis (#4680) * [BUG] Added frenzy reset function during move phase should move be cancelled (#4227) * Revert [P2 BUG] Fix since it does not work and is faulty * [P2 BUG] Implemented correct frenzy Tag and Movequeue reset should frenzy move fail (#4277) * Cleaned up Bug Fix using frenzyMissFunc * Added automated test case for frenzy move edge case * Improvement to frenzy move reset test case speed * Fix test * Add missing import --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/phases/move-phase.ts | 6 +- src/test/phases/frenzy-move-reset.test.ts | 72 +++++++++++++++++++++++ 2 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 src/test/phases/frenzy-move-reset.test.ts diff --git a/src/phases/move-phase.ts b/src/phases/move-phase.ts index a516fd8593d..5c6c339ffa5 100644 --- a/src/phases/move-phase.ts +++ b/src/phases/move-phase.ts @@ -3,7 +3,7 @@ import BattleScene from "#app/battle-scene"; import { applyAbAttrs, applyPostMoveUsedAbAttrs, applyPreAttackAbAttrs, BlockRedirectAbAttr, IncreasePpAbAttr, PokemonTypeChangeAbAttr, PostMoveUsedAbAttr, RedirectMoveAbAttr, ReduceStatusEffectDurationAbAttr } from "#app/data/ability"; import { CommonAnim } from "#app/data/battle-anims"; import { BattlerTagLapseType, CenterOfAttentionTag } from "#app/data/battler-tags"; -import { allMoves, applyMoveAttrs, BypassRedirectAttr, BypassSleepAttr, CopyMoveAttr, HealStatusEffectAttr, MoveFlags, PreMoveMessageAttr } from "#app/data/move"; +import { allMoves, applyMoveAttrs, BypassRedirectAttr, BypassSleepAttr, CopyMoveAttr, frenzyMissFunc, HealStatusEffectAttr, MoveFlags, PreMoveMessageAttr } from "#app/data/move"; import { SpeciesFormChangePreMoveTrigger } from "#app/data/pokemon-forms"; import { getStatusEffectActivationText, getStatusEffectHealText } from "#app/data/status-effect"; import { Type } from "#app/data/type"; @@ -470,6 +470,10 @@ export class MovePhase extends BattlePhase { this.scene.eventTarget.dispatchEvent(new MoveUsedEvent(this.pokemon?.id, this.move.getMove(), ppUsed)); } + if (this.cancelled && this.pokemon.summonData?.tags?.find(t => t.tagType === BattlerTagType.FRENZY)) { + frenzyMissFunc(this.pokemon, this.move.getMove()); + } + this.pokemon.pushMoveHistory({ move: Moves.NONE, result: MoveResult.FAIL }); this.pokemon.lapseTags(BattlerTagLapseType.MOVE_EFFECT); diff --git a/src/test/phases/frenzy-move-reset.test.ts b/src/test/phases/frenzy-move-reset.test.ts new file mode 100644 index 00000000000..db9ec2bfe66 --- /dev/null +++ b/src/test/phases/frenzy-move-reset.test.ts @@ -0,0 +1,72 @@ +import { BattlerIndex } from "#app/battle"; +import { Abilities } from "#enums/abilities"; +import { BattlerTagType } from "#enums/battler-tag-type"; +import { StatusEffect } from "#enums/status-effect"; +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 } from "vitest"; + +describe("Frenzy Move Reset", () => { + 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 + .battleType("single") + .disableCrits() + .starterSpecies(Species.MAGIKARP) + .moveset(Moves.THRASH) + .statusEffect(StatusEffect.PARALYSIS) + .enemyMoveset(Moves.SPLASH) + .enemyLevel(100) + .enemySpecies(Species.SHUCKLE) + .enemyAbility(Abilities.BALL_FETCH); + }); + + /* + * Thrash (or frenzy moves in general) should not continue to run if attack fails due to paralysis + * + * This is a 3-turn Thrash test: + * 1. Thrash is selected and succeeds to hit the enemy -> Enemy Faints + * + * 2. Thrash is automatically selected but misses due to paralysis + * Note: After missing the Pokemon should stop automatically attacking + * + * 3. At the start of the 3rd turn the Player should be able to select a move/switch Pokemon/etc. + * Note: This means that BattlerTag.FRENZY is not anymore in pokemon.summonData.tags and pokemon.summonData.moveQueue is empty + * + */ + it("should cancel frenzy move if move fails turn 2", async () => { + await game.classicMode.startBattle(); + + const playerPokemon = game.scene.getPlayerPokemon()!; + + game.move.select(Moves.THRASH); + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.move.forceStatusActivation(false); + await game.toNextTurn(); + + expect(playerPokemon.summonData.moveQueue.length).toBe(2); + expect(playerPokemon.summonData.tags.some(tag => tag.tagType === BattlerTagType.FRENZY)).toBe(true); + + await game.setTurnOrder([ BattlerIndex.PLAYER, BattlerIndex.ENEMY ]); + await game.move.forceStatusActivation(true); + await game.toNextTurn(); + + expect(playerPokemon.summonData.moveQueue.length).toBe(0); + expect(playerPokemon.summonData.tags.some(tag => tag.tagType === BattlerTagType.FRENZY)).toBe(false); + }); +});