From 928a3ae977996bc4f49fc8770359fdd638e0808a Mon Sep 17 00:00:00 2001 From: Corrade <49605314+Corrade@users.noreply.github.com> Date: Thu, 25 Jul 2024 12:34:02 +1200 Subject: [PATCH] [Bug] Fixed OHKO moves being affected by accuracy and evasion battle stats (#3117) * Fixed OHKO moves being affected by accuracy and evasion battle stats * Added related tests for Fissure, unskipped related test for Hustle * Tweaked fissure accuracy and evasion tests to use spyOn() for getAccuracyMultiplier() as per feedback * Fixed accuracy test for Fissure --- src/field/pokemon.ts | 7 ++++++- src/test/abilities/hustle.test.ts | 6 +++--- src/test/moves/fissure.test.ts | 35 +++++++++++++++++++++++++++---- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index a0200e7c455..f6f71384825 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -3,7 +3,7 @@ import BattleScene, { AnySound } from "../battle-scene"; import { Variant, VariantSet, variantColorCache } from "#app/data/variant"; import { variantData } from "#app/data/variant"; import BattleInfo, { PlayerBattleInfo, EnemyBattleInfo } from "../ui/battle-info"; -import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, StatusMoveTypeImmunityAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, MoveFlags, NeutralDamageAgainstFlyingTypeMultiplierAttr } from "../data/move"; +import Move, { HighCritAttr, HitsTagAttr, applyMoveAttrs, FixedDamageAttr, VariableAtkAttr, allMoves, MoveCategory, TypelessAttr, CritOnlyAttr, getMoveTargets, OneHitKOAttr, VariableMoveTypeAttr, StatusMoveTypeImmunityAttr, VariableDefAttr, AttackMove, ModifiedDamageAttr, VariableMoveTypeMultiplierAttr, IgnoreOpponentStatChangesAttr, SacrificialAttr, VariableMoveCategoryAttr, CounterDamageAttr, StatChangeAttr, RechargeAttr, ChargeAttr, IgnoreWeatherTypeDebuffAttr, BypassBurnDamageReductionAttr, SacrificialAttrOnHit, MoveFlags, NeutralDamageAgainstFlyingTypeMultiplierAttr, OneHitKOAccuracyAttr } from "../data/move"; import { default as PokemonSpecies, PokemonSpeciesForm, SpeciesFormKey, getFusedSpeciesName, getPokemonSpecies, getPokemonSpeciesForm, getStarterValueFriendshipCap, speciesStarters, starterPassiveAbilities } from "../data/pokemon-species"; import { Constructor } from "#app/utils"; import * as Utils from "../utils"; @@ -1792,6 +1792,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns The calculated accuracy multiplier. */ getAccuracyMultiplier(target: Pokemon, sourceMove: Move): number { + const isOhko = sourceMove.hasAttr(OneHitKOAccuracyAttr); + if (isOhko) { + return 1; + } + const userAccuracyLevel = new Utils.IntegerHolder(this.summonData.battleStats[BattleStat.ACC]); const targetEvasionLevel = new Utils.IntegerHolder(target.summonData.battleStats[BattleStat.EVA]); diff --git a/src/test/abilities/hustle.test.ts b/src/test/abilities/hustle.test.ts index 9e6012a11a4..853dd9bc904 100644 --- a/src/test/abilities/hustle.test.ts +++ b/src/test/abilities/hustle.test.ts @@ -78,13 +78,13 @@ describe("Abilities - Hustle", () => { expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1); }); - // Skip until OHKO moves are fixed - it should not be affected by accuracy and evasion stats it("does not affect OHKO moves", async () => { vi.spyOn(overrides, "STARTING_LEVEL_OVERRIDE", "get").mockReturnValue(100); vi.spyOn(overrides, "OPP_LEVEL_OVERRIDE", "get").mockReturnValue(30); await game.startBattle([Species.PIKACHU]); const pikachu = game.scene.getPlayerPokemon(); + const enemyPokemon = game.scene.getEnemyPokemon(); vi.spyOn(pikachu, "getAccuracyMultiplier"); vi.spyOn(allMoves[Moves.FISSURE], "calculateBattleAccuracy"); @@ -92,8 +92,8 @@ describe("Abilities - Hustle", () => { game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); await game.phaseInterceptor.to(DamagePhase); - expect(game.scene.getEnemyPokemon().turnData.damageTaken).toBe(game.scene.getEnemyPokemon().hp); + expect(enemyPokemon.turnData.damageTaken).toBe(enemyPokemon.getMaxHp()); expect(pikachu.getAccuracyMultiplier).toHaveReturnedWith(1); expect(allMoves[Moves.FISSURE].calculateBattleAccuracy).toHaveReturnedWith(100); - }, { skip: true }); + }); }); diff --git a/src/test/moves/fissure.test.ts b/src/test/moves/fissure.test.ts index 4809a3b4dee..f91c01ebbe2 100644 --- a/src/test/moves/fissure.test.ts +++ b/src/test/moves/fissure.test.ts @@ -2,17 +2,18 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite import Phaser from "phaser"; import GameManager from "#app/test/utils/gameManager"; import Overrides from "#app/overrides"; -import { DamagePhase } from "#app/phases"; +import { DamagePhase, TurnEndPhase } from "#app/phases"; import { Moves } from "#enums/moves"; import { getMovePosition } from "#app/test/utils/gameManagerUtils"; import { Abilities } from "#enums/abilities"; import { Species } from "#app/enums/species.js"; -import { EnemyPokemon } from "#app/field/pokemon"; +import { EnemyPokemon, PlayerPokemon } from "#app/field/pokemon"; +import { BattleStat } from "#app/data/battle-stat"; describe("Moves - Fissure", () => { let phaserGame: Phaser.Game; let game: GameManager; - //let partyPokemon: PlayerPokemon; + let partyPokemon: PlayerPokemon; let enemyPokemon: EnemyPokemon; beforeAll(() => { @@ -43,7 +44,7 @@ describe("Moves - Fissure", () => { await game.startBattle(); - //partyPokemon = game.scene.getParty()[0]; + partyPokemon = game.scene.getParty()[0]; enemyPokemon = game.scene.getEnemyPokemon(); // remove berries @@ -60,4 +61,30 @@ describe("Moves - Fissure", () => { expect(enemyPokemon.isFainted()).toBe(true); }); + + it("ignores accuracy stat", async () => { + vi.spyOn(partyPokemon, "getAccuracyMultiplier"); + + enemyPokemon.summonData.battleStats[BattleStat.ACC] = -6; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + + // wait for TurnEndPhase instead of DamagePhase as fissure might not actually inflict damage + await game.phaseInterceptor.to(TurnEndPhase); + + expect(partyPokemon.getAccuracyMultiplier).toHaveReturnedWith(1); + }); + + it("ignores evasion stat", async () => { + vi.spyOn(partyPokemon, "getAccuracyMultiplier"); + + enemyPokemon.summonData.battleStats[BattleStat.EVA] = 6; + + game.doAttack(getMovePosition(game.scene, 0, Moves.FISSURE)); + + // wait for TurnEndPhase instead of DamagePhase as fissure might not actually inflict damage + await game.phaseInterceptor.to(TurnEndPhase); + + expect(partyPokemon.getAccuracyMultiplier).toHaveReturnedWith(1); + }); });