diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 53d4b6c54d2..d8e22898d42 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2886,7 +2886,11 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { /** Reduces damage if this Pokemon has a relevant screen (e.g. Light Screen for special attacks) */ const screenMultiplier = new Utils.NumberHolder(1); - globalScene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, simulated, source, moveCategory, screenMultiplier); + + // Critical hits should bypass screens + if (!isCritical) { + globalScene.arena.applyTagsForSide(WeakenMoveScreenTag, defendingSide, simulated, source, moveCategory, screenMultiplier); + } /** * For each {@linkcode HitsTagAttr} the move has, doubles the damage of the move if: diff --git a/test/moves/aurora_veil.test.ts b/test/moves/aurora_veil.test.ts index c8da3e03db5..21b46c4be79 100644 --- a/test/moves/aurora_veil.test.ts +++ b/test/moves/aurora_veil.test.ts @@ -1,7 +1,7 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/move"; -import { allMoves } from "#app/data/move"; +import { allMoves, CritOnlyAttr } from "#app/data/move"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; import { TurnEndPhase } from "#app/phases/turn-end-phase"; @@ -12,7 +12,7 @@ import { Species } from "#enums/species"; import { WeatherType } from "#enums/weather-type"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; let globalScene: BattleScene; @@ -47,7 +47,7 @@ describe("Moves - Aurora Veil", () => { it("reduces damage of physical attacks by half in a single battle", async () => { const moveToUse = Moves.TACKLE; - await game.startBattle([ Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE ]); game.move.select(moveToUse); @@ -61,7 +61,7 @@ describe("Moves - Aurora Veil", () => { game.override.battleType("double"); const moveToUse = Moves.ROCK_SLIDE; - await game.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); game.move.select(moveToUse); game.move.select(moveToUse, 1); @@ -74,7 +74,7 @@ describe("Moves - Aurora Veil", () => { it("reduces damage of special attacks by half in a single battle", async () => { const moveToUse = Moves.ABSORB; - await game.startBattle([ Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE ]); game.move.select(moveToUse); @@ -89,7 +89,7 @@ describe("Moves - Aurora Veil", () => { game.override.battleType("double"); const moveToUse = Moves.DAZZLING_GLEAM; - await game.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); game.move.select(moveToUse); game.move.select(moveToUse, 1); @@ -99,6 +99,31 @@ describe("Moves - Aurora Veil", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier); }); + + it("does not affect physical critical hits", async () => { + game.override.moveset([ Moves.WICKED_BLOW ]); + const moveToUse = Moves.WICKED_BLOW; + await game.classicMode.startBattle([ Species.SHUCKLE ]); + + game.move.select(moveToUse); + await game.phaseInterceptor.to(TurnEndPhase); + + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); + expect(mockedDmg).toBe(allMoves[moveToUse].power); + }); + + it("does not affect critical hits", async () => { + game.override.moveset([ Moves.FROST_BREATH ]); + const moveToUse = Moves.FROST_BREATH; + vi.spyOn(allMoves[Moves.FROST_BREATH], "accuracy", "get").mockReturnValue(100); + await game.classicMode.startBattle([ Species.SHUCKLE ]); + + game.move.select(moveToUse); + await game.phaseInterceptor.to(TurnEndPhase); + + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); + expect(mockedDmg).toBe(allMoves[moveToUse].power); + }); }); /** @@ -115,7 +140,9 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) = const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (globalScene.arena.getTagOnSide(ArenaTagType.AURORA_VEIL, side)) { - globalScene.arena.applyTagsForSide(ArenaTagType.AURORA_VEIL, side, false, attacker, move.category, multiplierHolder); + if (move.getAttrs(CritOnlyAttr).length === 0) { + globalScene.arena.applyTagsForSide(ArenaTagType.AURORA_VEIL, side, false, attacker, move.category, multiplierHolder); + } } return move.power * multiplierHolder.value; diff --git a/test/moves/light_screen.test.ts b/test/moves/light_screen.test.ts index 8eee58c8e17..1f9e4f9b97f 100644 --- a/test/moves/light_screen.test.ts +++ b/test/moves/light_screen.test.ts @@ -1,7 +1,7 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/move"; -import { allMoves } from "#app/data/move"; +import { allMoves, CritOnlyAttr } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; @@ -11,7 +11,7 @@ import { Moves } from "#enums/moves"; import { Species } from "#enums/species"; import GameManager from "#test/testUtils/gameManager"; import Phaser from "phaser"; -import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; let globalScene: BattleScene; @@ -45,7 +45,7 @@ describe("Moves - Light Screen", () => { it("reduces damage of special attacks by half in a single battle", async () => { const moveToUse = Moves.ABSORB; - await game.startBattle([ Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE ]); game.move.select(moveToUse); @@ -60,7 +60,7 @@ describe("Moves - Light Screen", () => { game.override.battleType("double"); const moveToUse = Moves.DAZZLING_GLEAM; - await game.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); game.move.select(moveToUse); game.move.select(moveToUse, 1); @@ -73,7 +73,7 @@ describe("Moves - Light Screen", () => { it("does not affect physical attacks", async () => { const moveToUse = Moves.TACKLE; - await game.startBattle([ Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE ]); game.move.select(moveToUse); @@ -82,6 +82,19 @@ describe("Moves - Light Screen", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power); }); + + it("does not affect critical hits", async () => { + game.override.moveset([ Moves.FROST_BREATH ]); + const moveToUse = Moves.FROST_BREATH; + vi.spyOn(allMoves[Moves.FROST_BREATH], "accuracy", "get").mockReturnValue(100); + await game.classicMode.startBattle([ Species.SHUCKLE ]); + + game.move.select(moveToUse); + await game.phaseInterceptor.to(TurnEndPhase); + + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); + expect(mockedDmg).toBe(allMoves[moveToUse].power); + }); }); /** @@ -98,7 +111,9 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) = const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (globalScene.arena.getTagOnSide(ArenaTagType.LIGHT_SCREEN, side)) { - globalScene.arena.applyTagsForSide(ArenaTagType.LIGHT_SCREEN, side, false, attacker, move.category, multiplierHolder); + if (move.getAttrs(CritOnlyAttr).length === 0) { + globalScene.arena.applyTagsForSide(ArenaTagType.LIGHT_SCREEN, side, false, attacker, move.category, multiplierHolder); + } } return move.power * multiplierHolder.value; diff --git a/test/moves/reflect.test.ts b/test/moves/reflect.test.ts index edc3f1ab8aa..a0376aa24b4 100644 --- a/test/moves/reflect.test.ts +++ b/test/moves/reflect.test.ts @@ -1,7 +1,7 @@ import type BattleScene from "#app/battle-scene"; import { ArenaTagSide } from "#app/data/arena-tag"; import type Move from "#app/data/move"; -import { allMoves } from "#app/data/move"; +import { allMoves, CritOnlyAttr } from "#app/data/move"; import { Abilities } from "#app/enums/abilities"; import { ArenaTagType } from "#app/enums/arena-tag-type"; import type Pokemon from "#app/field/pokemon"; @@ -45,7 +45,7 @@ describe("Moves - Reflect", () => { it("reduces damage of physical attacks by half in a single battle", async () => { const moveToUse = Moves.TACKLE; - await game.startBattle([ Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE ]); game.move.select(moveToUse); @@ -59,7 +59,7 @@ describe("Moves - Reflect", () => { game.override.battleType("double"); const moveToUse = Moves.ROCK_SLIDE; - await game.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE, Species.SHUCKLE ]); game.move.select(moveToUse); game.move.select(moveToUse, 1); @@ -72,7 +72,7 @@ describe("Moves - Reflect", () => { it("does not affect special attacks", async () => { const moveToUse = Moves.ABSORB; - await game.startBattle([ Species.SHUCKLE ]); + await game.classicMode.startBattle([ Species.SHUCKLE ]); game.move.select(moveToUse); @@ -82,6 +82,18 @@ describe("Moves - Reflect", () => { expect(mockedDmg).toBe(allMoves[moveToUse].power); }); + + it("does not affect critical hits", async () => { + game.override.moveset([ Moves.WICKED_BLOW ]); + const moveToUse = Moves.WICKED_BLOW; + await game.classicMode.startBattle([ Species.SHUCKLE ]); + + game.move.select(moveToUse); + await game.phaseInterceptor.to(TurnEndPhase); + + const mockedDmg = getMockedMoveDamage(game.scene.getEnemyPokemon()!, game.scene.getPlayerPokemon()!, allMoves[moveToUse]); + expect(mockedDmg).toBe(allMoves[moveToUse].power); + }); }); /** @@ -98,7 +110,9 @@ const getMockedMoveDamage = (defender: Pokemon, attacker: Pokemon, move: Move) = const side = defender.isPlayer() ? ArenaTagSide.PLAYER : ArenaTagSide.ENEMY; if (globalScene.arena.getTagOnSide(ArenaTagType.REFLECT, side)) { - globalScene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, false, attacker, move.category, multiplierHolder); + if (move.getAttrs(CritOnlyAttr).length === 0) { + globalScene.arena.applyTagsForSide(ArenaTagType.REFLECT, side, false, attacker, move.category, multiplierHolder); + } } return move.power * multiplierHolder.value;