From 5743751e5ca93c455f5b9a0da26c1dd9f21b4fcc Mon Sep 17 00:00:00 2001 From: Wlowscha <54003515+Wlowscha@users.noreply.github.com> Date: Wed, 12 Feb 2025 00:01:54 +0100 Subject: [PATCH] [Bug][Test] Adding bypass faint to abilities that need it + fixing Perish Body (#5226) * Added tests for snad spit, seed sower and perish body; for all three, the test checking if the ability triggers after the user faints is failing. * Adding .bypassFaint() to the three abilities, tests passing * Apply suggestions from code review Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> * Removed incorrect test for perish song * Added tests for perish body when one mon already has the perish song tag, both ways * Changed ability behavior to pass tests * Removing superfluous conditional --------- Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com> --- src/data/ability.ts | 11 ++- src/test/abilities/perish_body.test.ts | 116 +++++++++++++++++++++++++ src/test/abilities/sand_spit.test.ts | 17 +++- src/test/abilities/seed_sower.test.ts | 69 +++++++++++++++ 4 files changed, 207 insertions(+), 6 deletions(-) create mode 100644 src/test/abilities/perish_body.test.ts create mode 100644 src/test/abilities/seed_sower.test.ts diff --git a/src/data/ability.ts b/src/data/ability.ts index a6d00b29fbc..347054d2447 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -1003,7 +1003,7 @@ export class PostDefendPerishSongAbAttr extends PostDefendAbAttr { override applyPostDefend(pokemon: Pokemon, _passive: boolean, simulated: boolean, attacker: Pokemon, move: Move, _hitResult: HitResult, _args: any[]): boolean { if (move.checkFlag(MoveFlags.MAKES_CONTACT, attacker, pokemon) && !move.hitsSubstitute(attacker, pokemon)) { - if (pokemon.getTag(BattlerTagType.PERISH_SONG) || attacker.getTag(BattlerTagType.PERISH_SONG)) { + if (attacker.getTag(BattlerTagType.PERISH_SONG)) { return false; } else { if (!simulated) { @@ -6141,7 +6141,8 @@ export function initAbilities() { .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.hasFlag(MoveFlags.SOUND_BASED), 0.5) .ignorable(), new Ability(Abilities.SAND_SPIT, 8) - .attr(PostDefendWeatherChangeAbAttr, WeatherType.SANDSTORM, (target, user, move) => move.category !== MoveCategory.STATUS), + .attr(PostDefendWeatherChangeAbAttr, WeatherType.SANDSTORM, (target, user, move) => move.category !== MoveCategory.STATUS) + .bypassFaint(), new Ability(Abilities.ICE_SCALES, 8) .attr(ReceivedMoveDamageMultiplierAbAttr, (target, user, move) => move.category === MoveCategory.SPECIAL, 0.5) .ignorable(), @@ -6174,7 +6175,8 @@ export function initAbilities() { new Ability(Abilities.STEELY_SPIRIT, 8) .attr(UserFieldMoveTypePowerBoostAbAttr, Type.STEEL), new Ability(Abilities.PERISH_BODY, 8) - .attr(PostDefendPerishSongAbAttr, 4), + .attr(PostDefendPerishSongAbAttr, 4) + .bypassFaint(), new Ability(Abilities.WANDERING_SPIRIT, 8) .attr(PostDefendAbilitySwapAbAttr) .bypassFaint() @@ -6232,7 +6234,8 @@ export function initAbilities() { .attr(PostDefendAbilityGiveAbAttr, Abilities.LINGERING_AROMA) .bypassFaint(), new Ability(Abilities.SEED_SOWER, 9) - .attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY), + .attr(PostDefendTerrainChangeAbAttr, TerrainType.GRASSY) + .bypassFaint(), new Ability(Abilities.THERMAL_EXCHANGE, 9) .attr(PostDefendStatStageChangeAbAttr, (target, user, move) => user.getMoveType(move) === Type.FIRE && move.category !== MoveCategory.STATUS, Stat.ATK, 1) .attr(StatusEffectImmunityAbAttr, StatusEffect.BURN) diff --git a/src/test/abilities/perish_body.test.ts b/src/test/abilities/perish_body.test.ts new file mode 100644 index 00000000000..70ba6d8d684 --- /dev/null +++ b/src/test/abilities/perish_body.test.ts @@ -0,0 +1,116 @@ +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, expect, it } from "vitest"; + + +describe("Abilities - Perish Song", () => { + 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"); + game.override.disableCrits(); + + game.override.enemySpecies(Species.MAGIKARP); + game.override.enemyAbility(Abilities.BALL_FETCH); + + game.override.starterSpecies(Species.CURSOLA); + game.override.ability(Abilities.PERISH_BODY); + game.override.moveset([ Moves.SPLASH ]); + }); + + it("should trigger when hit with damaging move", async () => { + game.override.enemyMoveset([ Moves.AQUA_JET ]); + await game.classicMode.startBattle(); + const cursola = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon(); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(cursola?.summonData.tags[0].turnCount).toBe(3); + expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + }); + + it("should trigger even when fainting", async () => { + game.override.enemyMoveset([ Moves.AQUA_JET ]) + .enemyLevel(100) + .startingLevel(1); + await game.classicMode.startBattle([ Species.CURSOLA, Species.FEEBAS ]); + const magikarp = game.scene.getEnemyPokemon(); + + game.move.select(Moves.SPLASH); + game.doSelectPartyPokemon(1); + await game.toNextTurn(); + + expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + }); + + it("should not activate if attacker already has perish song", async () => { + game.override.enemyMoveset([ Moves.PERISH_SONG, Moves.AQUA_JET, Moves.SPLASH ]); + await game.classicMode.startBattle([ Species.FEEBAS, Species.CURSOLA ]); + const feebas = game.scene.getPlayerPokemon(); + const magikarp = game.scene.getEnemyPokemon(); + + game.move.select(Moves.SPLASH); + await game.forceEnemyMove(Moves.PERISH_SONG); + await game.toNextTurn(); + + expect(feebas?.summonData.tags[0].turnCount).toBe(3); + expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + + game.doSwitchPokemon(1); + await game.forceEnemyMove(Moves.SPLASH); + await game.toNextTurn(); + + const cursola = game.scene.getPlayerPokemon(); + expect(cursola?.summonData.tags.length).toBe(0); + expect(magikarp?.summonData.tags[0].turnCount).toBe(2); + + game.move.select(Moves.SPLASH); + await game.forceEnemyMove(Moves.AQUA_JET); + await game.toNextTurn(); + + expect(cursola?.summonData.tags.length).toBe(0); + expect(magikarp?.summonData.tags[0].turnCount).toBe(1); + + }); + + it("should activate if cursola already has perish song, but not reset its counter", async () => { + game.override.enemyMoveset([ Moves.PERISH_SONG, Moves.AQUA_JET, Moves.SPLASH ]); + game.override.moveset([ Moves.WHIRLWIND, Moves.SPLASH ]); + game.override.startingWave(5); + await game.classicMode.startBattle([ Species.CURSOLA ]); + const cursola = game.scene.getPlayerPokemon(); + + game.move.select(Moves.WHIRLWIND); + await game.forceEnemyMove(Moves.PERISH_SONG); + await game.toNextTurn(); + + const magikarp = game.scene.getEnemyPokemon(); + expect(cursola?.summonData.tags[0].turnCount).toBe(3); + expect(magikarp?.summonData.tags.length).toBe(0); + + game.move.select(Moves.SPLASH); + await game.forceEnemyMove(Moves.AQUA_JET); + await game.toNextTurn(); + + expect(cursola?.summonData.tags[0].turnCount).toBe(2); + expect(magikarp?.summonData.tags.length).toBe(1); + expect(magikarp?.summonData.tags[0].turnCount).toBe(3); + }); +}); diff --git a/src/test/abilities/sand_spit.test.ts b/src/test/abilities/sand_spit.test.ts index 1c21cff3c14..092c3e66105 100644 --- a/src/test/abilities/sand_spit.test.ts +++ b/src/test/abilities/sand_spit.test.ts @@ -36,7 +36,7 @@ describe("Abilities - Sand Spit", () => { it("should trigger when hit with damaging move", async () => { game.override.enemyMoveset([ Moves.TACKLE ]); - await game.startBattle(); + await game.classicMode.startBattle(); game.move.select(Moves.SPLASH); await game.toNextTurn(); @@ -44,9 +44,22 @@ describe("Abilities - Sand Spit", () => { expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM); }, 20000); + it("should trigger even when fainting", async () => { + game.override.enemyMoveset([ Moves.TACKLE ]) + .enemyLevel(100) + .startingLevel(1); + await game.classicMode.startBattle([ Species.SILICOBRA, Species.MAGIKARP ]); + + game.move.select(Moves.SPLASH); + game.doSelectPartyPokemon(1); + await game.toNextTurn(); + + expect(game.scene.arena.weather?.weatherType).toBe(WeatherType.SANDSTORM); + }); + it("should not trigger when targetted with status moves", async () => { game.override.enemyMoveset([ Moves.GROWL ]); - await game.startBattle(); + await game.classicMode.startBattle(); game.move.select(Moves.COIL); await game.toNextTurn(); diff --git a/src/test/abilities/seed_sower.test.ts b/src/test/abilities/seed_sower.test.ts new file mode 100644 index 00000000000..71b76e24d2e --- /dev/null +++ b/src/test/abilities/seed_sower.test.ts @@ -0,0 +1,69 @@ +import { TerrainType } from "#app/data/terrain"; +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, expect, it } from "vitest"; + + +describe("Abilities - Seed Sower", () => { + 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"); + game.override.disableCrits(); + + game.override.enemySpecies(Species.MAGIKARP); + game.override.enemyAbility(Abilities.BALL_FETCH); + + game.override.starterSpecies(Species.ARBOLIVA); + game.override.ability(Abilities.SEED_SOWER); + game.override.moveset([ Moves.SPLASH ]); + }); + + it("should trigger when hit with damaging move", async () => { + game.override.enemyMoveset([ Moves.TACKLE ]); + await game.classicMode.startBattle(); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(game.scene.arena.terrain?.terrainType).toBe(TerrainType.GRASSY); + }); + + it("should trigger even when fainting", async () => { + game.override.enemyMoveset([ Moves.TACKLE ]) + .enemyLevel(100) + .startingLevel(1); + await game.classicMode.startBattle([ Species.ARBOLIVA, Species.MAGIKARP ]); + + game.move.select(Moves.SPLASH); + game.doSelectPartyPokemon(1); + await game.toNextTurn(); + + expect(game.scene.arena.terrain?.terrainType).toBe(TerrainType.GRASSY); + }); + + it("should not trigger when targetted with status moves", async () => { + game.override.enemyMoveset([ Moves.GROWL ]); + await game.classicMode.startBattle(); + + game.move.select(Moves.SPLASH); + await game.toNextTurn(); + + expect(game.scene.arena.terrain?.terrainType).not.toBe(TerrainType.GRASSY); + }); +});