From 77d25d18f963568bc015a8cc4bb98ff6f9b9533c Mon Sep 17 00:00:00 2001 From: Jacob Hatchett <152253273+newmattock@users.noreply.github.com> Date: Sat, 22 Jun 2024 08:37:46 -0700 Subject: [PATCH] [Ability] Implemented Aura Break (#2252) * re-adjust according to new changes * added unit tests * add test fixes * add documentation * more fixes --- src/data/ability.ts | 3 +- src/test/abilities/aura_break.test.ts | 95 +++++++++++++++++++++++++++ 2 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 src/test/abilities/aura_break.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index 56e41087d66..a9d6ef582c7 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4592,7 +4592,8 @@ export function initAbilities() { .attr(FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 4 / 3), new Ability(Abilities.AURA_BREAK, 6) .ignorable() - .unimplemented(), + .conditionalAttr(target => target.hasAbility(Abilities.DARK_AURA), FieldMoveTypePowerBoostAbAttr, Type.DARK, 9 / 16) + .conditionalAttr(target => target.hasAbility(Abilities.FAIRY_AURA), FieldMoveTypePowerBoostAbAttr, Type.FAIRY, 9 / 16), new Ability(Abilities.PRIMORDIAL_SEA, 6) .attr(PostSummonWeatherChangeAbAttr, WeatherType.HEAVY_RAIN) .attr(PostBiomeChangeWeatherChangeAbAttr, WeatherType.HEAVY_RAIN) diff --git a/src/test/abilities/aura_break.test.ts b/src/test/abilities/aura_break.test.ts new file mode 100644 index 00000000000..158aef57593 --- /dev/null +++ b/src/test/abilities/aura_break.test.ts @@ -0,0 +1,95 @@ +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; +import Phaser from "phaser"; +import GameManager from "#app/test/utils/gameManager"; +import * as overrides from "#app/overrides"; +import { Species } from "#enums/species"; +import { MoveEffectPhase } from "#app/phases"; +import { Moves } from "#enums/moves"; +import { getMovePosition } from "#app/test/utils/gameManagerUtils"; +import { Abilities } from "#enums/abilities"; +import Move, { allMoves } from "#app/data/move.js"; +import Pokemon from "#app/field/pokemon.js"; +import { FieldMoveTypePowerBoostAbAttr } from "#app/data/ability.js"; +import { NumberHolder } from "#app/utils.js"; + +describe("Abilities - Aura Break", () => { + let phaserGame: Phaser.Game; + let game: GameManager; + + beforeAll(() => { + phaserGame = new Phaser.Game({ + type: Phaser.HEADLESS, + }); + }); + + afterEach(() => { + game.phaseInterceptor.restoreOg(); + }); + + beforeEach(() => { + game = new GameManager(phaserGame); + vi.spyOn(overrides, "SINGLE_BATTLE_OVERRIDE", "get").mockReturnValue(true); + vi.spyOn(overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([Moves.MOONBLAST, Moves.DARK_PULSE, Moves.MOONBLAST, Moves.DARK_PULSE]); + vi.spyOn(overrides, "OPP_MOVESET_OVERRIDE", "get").mockReturnValue([Moves.SPLASH, Moves.SPLASH, Moves.SPLASH, Moves.SPLASH]); + vi.spyOn(overrides, "OPP_ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.AURA_BREAK); + }); + + it("reverses the effect of fairy aura", async () => { + vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.FAIRY_AURA); + const basePower = allMoves[Moves.MOONBLAST].power; + const multiplier = 9 / 16; + await game.startBattle([Species.MAGIKARP]); + + game.doAttack(getMovePosition(game.scene, 0, Moves.MOONBLAST)); + + const appliedPower = getMockedMovePower(game.scene.getEnemyField()[0], game.scene.getPlayerField()[0], allMoves[Moves.MOONBLAST]); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(appliedPower).not.toBe(undefined); + expect(appliedPower).not.toBe(basePower); + expect(appliedPower).toBe(basePower * multiplier); + + }); + it("reverses the effect of dark aura", async () => { + vi.spyOn(overrides, "ABILITY_OVERRIDE", "get").mockReturnValue(Abilities.DARK_AURA); + const basePower = allMoves[Moves.DARK_PULSE].power; + const multiplier = 9 / 16; + await game.startBattle([Species.MAGIKARP]); + + game.doAttack(getMovePosition(game.scene, 0, Moves.DARK_PULSE)); + + const appliedPower = getMockedMovePower(game.scene.getEnemyField()[0], game.scene.getPlayerField()[0], allMoves[Moves.DARK_PULSE]); + + await game.phaseInterceptor.to(MoveEffectPhase); + + expect(appliedPower).not.toBe(undefined); + expect(appliedPower).not.toBe(basePower); + expect(appliedPower).toBe(basePower * multiplier); + }); +}); + +/** + * Calculates the mocked power of a move in a Pokémon battle, taking into account certain abilities. + * + * @param defender - The defending Pokémon. + * @param attacker - The attacking Pokémon. + * @param move - The move being used in the attack. + * @returns The calculated power of the move after applying any relevant ability effects. + * + * @remarks + * This function creates a NumberHolder with the initial power of the move. + * It then checks if the defender has an ability with the FieldMoveTypePowerBoostAbAttr. + * If so, it applies a power modification of 9/16 using an instance of FieldMoveTypePowerBoostAbAttr. + * The final calculated power is then returned. + */ +const getMockedMovePower = (defender: Pokemon, attacker: Pokemon, move: Move): number => { + const powerHolder = new NumberHolder(move.power); + + if (defender.hasAbilityWithAttr(FieldMoveTypePowerBoostAbAttr)) { + const auraBreakInstance = new FieldMoveTypePowerBoostAbAttr(move.type, 9 / 16); + auraBreakInstance.applyPreAttack(attacker, false, defender, move, [powerHolder]); + } + + return powerHolder.value; +};