diff --git a/src/data/moves/move.ts b/src/data/moves/move.ts index 925ef0700cd..c442d40fa07 100644 --- a/src/data/moves/move.ts +++ b/src/data/moves/move.ts @@ -1533,6 +1533,7 @@ export class CritOnlyAttr extends MoveAttr { } } +// TODO: Fix subclasses to actually extend from `getDamage` export class FixedDamageAttr extends MoveAttr { private damage: number; @@ -5935,8 +5936,8 @@ export class ProtectAttr extends AddBattlerTagAttr { for (const turnMove of user.getLastXMoves(-1).slice()) { if ( // Quick & Wide guard increment the Protect counter without using it for fail chance - !(allMoves[turnMove.move].hasAttr("ProtectAttr") || - [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || + !(allMoves[turnMove.move].hasAttr("ProtectAttr") || + [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || turnMove.result !== MoveResult.SUCCESS ) { break; diff --git a/test/abilities/shields-down.test.ts b/test/abilities/shields-down.test.ts index 98a1cfffa8e..2eb7359edd1 100644 --- a/test/abilities/shields-down.test.ts +++ b/test/abilities/shields-down.test.ts @@ -1,5 +1,6 @@ import { Status } from "#data/status-effect"; import { AbilityId } from "#enums/ability-id"; +import { ArenaTagType } from "#enums/arena-tag-type"; import { BattlerTagType } from "#enums/battler-tag-type"; import { MoveId } from "#enums/move-id"; import { SpeciesId } from "#enums/species-id"; @@ -104,27 +105,26 @@ describe("Abilities - SHIELDS DOWN", () => { expect(game.field.getPlayerPokemon().status).toBe(undefined); }); - // toxic spikes currently does not poison flying types when gravity is in effect - test.todo("should become poisoned by toxic spikes when grounded", async () => { - game.override - .enemyMoveset([MoveId.GRAVITY, MoveId.TOXIC_SPIKES, MoveId.SPLASH]) - .moveset([MoveId.GRAVITY, MoveId.SPLASH]); - + it("should be poisoned by toxic spikes when Gravity is active before changing forms", async () => { await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.MINIOR]); - // turn 1 - game.move.select(MoveId.GRAVITY); - await game.move.selectEnemyMove(MoveId.TOXIC_SPIKES); + // Change minior to core form in a state where it would revert on switch + const minior = game.scene.getPlayerParty()[1]; + minior.formIndex = redCoreForm; + + game.move.use(MoveId.GRAVITY); + await game.move.forceEnemyMove(MoveId.TOXIC_SPIKES); await game.toNextTurn(); - // turn 2 + expect(game).toHaveArenaTag(ArenaTagType.GRAVITY); + game.doSwitchPokemon(1); - await game.move.selectEnemyMove(MoveId.SPLASH); await game.toNextTurn(); - expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.MINIOR); - expect(game.field.getPlayerPokemon().species.formIndex).toBe(0); - expect(game.field.getPlayerPokemon().status?.effect).toBe(StatusEffect.POISON); + expect(minior.species.speciesId).toBe(SpeciesId.MINIOR); + expect(minior.formIndex).toBe(0); + expect(minior.isGrounded()).toBe(true); + expect(minior).toHaveStatusEffect(StatusEffect.POISON); }); test("should ignore yawn", async () => { diff --git a/test/moves/fly-bounce.test.ts b/test/moves/fly-bounce.test.ts index 42d88c13345..e792938ba88 100644 --- a/test/moves/fly-bounce.test.ts +++ b/test/moves/fly-bounce.test.ts @@ -48,14 +48,14 @@ describe("Moves - Fly and Bounce", () => { const player = game.field.getPlayerPokemon(); const enemy = game.field.getEnemyPokemon(); - expect(player.getTag(BattlerTagType.FLYING)).toBeDefined(); + expect(player).toHaveBattlerTag(BattlerTagType.FLYING); expect(enemy.getLastXMoves(1)[0].result).toBe(MoveResult.MISS); expect(player.hp).toBe(player.getMaxHp()); expect(enemy.hp).toBe(enemy.getMaxHp()); expect(player.getMoveQueue()[0].move).toBe(MoveId.FLY); await game.toEndOfTurn(); - expect(player.getTag(BattlerTagType.FLYING)).toBeUndefined(); + expect(player).not.toHaveBattlerTag(BattlerTagType.FLYING); expect(enemy.hp).toBeLessThan(enemy.getMaxHp()); expect(player.getMoveHistory()).toHaveLength(2); @@ -63,6 +63,7 @@ describe("Moves - Fly and Bounce", () => { expect(playerFly?.ppUsed).toBe(1); }); + // TODO: Move to a No Guard test file it("should not allow the user to evade attacks from Pokemon with No Guard", async () => { game.override.enemyAbility(AbilityId.NO_GUARD); @@ -71,7 +72,7 @@ describe("Moves - Fly and Bounce", () => { const playerPokemon = game.field.getPlayerPokemon(); const enemyPokemon = game.field.getEnemyPokemon(); - game.move.select(MoveId.FLY); + game.move.use(MoveId.FLY); await game.toEndOfTurn(); expect(playerPokemon.hp).toBeLessThan(playerPokemon.getMaxHp()); @@ -85,10 +86,10 @@ describe("Moves - Fly and Bounce", () => { const playerPokemon = game.field.getPlayerPokemon(); - game.move.select(MoveId.FLY); + game.move.use(MoveId.FLY); await game.toEndOfTurn(); - expect(playerPokemon.getTag(BattlerTagType.FLYING)).toBeUndefined(); + expect(playerPokemon).not.toHaveBattlerTag(BattlerTagType.FLYING); expect(playerPokemon.status?.effect).toBe(StatusEffect.SLEEP); const playerFly = playerPokemon.getMoveset().find(mv => mv && mv.moveId === MoveId.FLY); @@ -110,13 +111,13 @@ describe("Moves - Fly and Bounce", () => { // Bounce should've worked until hit const azurill = game.field.getPlayerPokemon(); - expect(azurill.getTag(BattlerTagType.FLYING)).toBeDefined(); - expect(azurill.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined(); + expect(azurill).toHaveBattlerTag(BattlerTagType.FLYING); + expect(azurill).not.toHaveBattlerTag(BattlerTagType.IGNORE_FLYING); await game.phaseInterceptor.to("MoveEndPhase"); - expect(azurill.getTag(BattlerTagType.FLYING)).toBeUndefined(); - expect(azurill.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); + expect(azurill).not.toHaveBattlerTag(BattlerTagType.FLYING); + expect(azurill).toHaveBattlerTag(BattlerTagType.IGNORE_FLYING); expect(azurill.getMoveQueue()).toHaveLength(0); expect(azurill.visible).toBe(true); if (move !== MoveId.GRAVITY) { diff --git a/test/moves/smack-down-thousand-arrows.test.ts b/test/moves/smack-down-thousand-arrows.test.ts index a90c242c3e1..c394b889dfd 100644 --- a/test/moves/smack-down-thousand-arrows.test.ts +++ b/test/moves/smack-down-thousand-arrows.test.ts @@ -46,10 +46,9 @@ describe("Moves - Smack Down and Thousand Arrows", () => { expect(enemy.isGrounded()).toBe(false); game.move.use(move); - await game.phaseInterceptor.to("MoveEffectPhase", false); await game.toEndOfTurn(); - expect(enemy.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); + expect(enemy).toHaveBattlerTag(BattlerTagType.IGNORE_FLYING); expect(enemy.isGrounded()).toBe(true); }); @@ -61,45 +60,33 @@ describe("Moves - Smack Down and Thousand Arrows", () => { expect(eelektross.isGrounded()).toBe(false); game.move.use(MoveId.THOUSAND_ARROWS); - await game.phaseInterceptor.to("MoveEffectPhase", false); await game.toEndOfTurn(); - expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); + expect(eelektross).toHaveBattlerTag(BattlerTagType.IGNORE_FLYING); expect(eelektross.hp).toBeLessThan(eelektross.getMaxHp()); expect(eelektross.isGrounded()).toBe(true); }); it.each([ - { name: "Telekinesis", move: MoveId.TELEKINESIS, tags: [BattlerTagType.TELEKINESIS, BattlerTagType.FLOATING] }, - { name: "Magnet Rise", move: MoveId.MAGNET_RISE, tags: [BattlerTagType.FLOATING] }, - ])("should cancel the ungrounding effects of $name", async ({ move, tags }) => { + { name: "TELEKINESIS", tag: BattlerTagType.TELEKINESIS }, + { name: "FLOATING", tag: BattlerTagType.FLOATING }, + ])("should cancel the effects of BattlerTagType.$name", async ({ tag }) => { await game.classicMode.startBattle([SpeciesId.ILLUMISE]); - game.move.use(MoveId.SMACK_DOWN); - await game.move.forceEnemyMove(move); - await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); - await game.phaseInterceptor.to("MoveEndPhase"); - - // ensure move suceeeded before getting knocked down const eelektross = game.field.getEnemyPokemon(); - tags.forEach(t => { - expect(eelektross.getTag(t)).toBeDefined(); - }); - expect(eelektross.isGrounded()).toBe(false); + eelektross.addTag(tag); + game.move.use(MoveId.SMACK_DOWN); await game.toEndOfTurn(); - tags.forEach(t => { - expect(eelektross.getTag(t)).toBeUndefined(); - }); - expect(eelektross.hp).toBeLessThan(eelektross.getMaxHp()); - expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); - expect(eelektross.isGrounded()).toBe(false); + expect(eelektross).not.toHaveBattlerTag(tag); + expect(eelektross).toHaveBattlerTag(BattlerTagType.IGNORE_FLYING); }); // NB: This test might sound useless, but semi-invulnerable pokemon are technically considered "ungrounded" // by most things - it("should not ground semi-invulnerable targets unless already ungrounded", async () => { + it("should not ground semi-invulnerable targets hit via No Guard unless already ungrounded", async () => { + game.override.ability(AbilityId.NO_GUARD); await game.classicMode.startBattle([SpeciesId.ILLUMISE]); game.move.use(MoveId.THOUSAND_ARROWS); @@ -110,7 +97,7 @@ describe("Moves - Smack Down and Thousand Arrows", () => { // Eelektross took damage but was not forcibly grounded const eelektross = game.field.getEnemyPokemon(); expect(eelektross.isGrounded()).toBe(true); - expect(eelektross.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined(); + expect(eelektross).not.toHaveBattlerTag(BattlerTagType.IGNORE_FLYING); expect(eelektross.hp).toBeLessThan(eelektross.getMaxHp()); }); @@ -129,7 +116,7 @@ describe("Moves - Smack Down and Thousand Arrows", () => { await game.toEndOfTurn(); expect(hitSpy).toHaveReturnedWith([expect.anything(), 1]); - expect(archeops.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); + expect(archeops).toHaveBattlerTag(BattlerTagType.IGNORE_FLYING); expect(archeops.isGrounded()).toBe(true); expect(archeops.hp).toBeLessThan(archeops.getMaxHp()); });