diff --git a/src/data/ability.ts b/src/data/ability.ts index a698831a1eb0..8a7365b2c28a 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -519,6 +519,7 @@ export class FullHpResistTypeAbAttr extends PreDefendAbAttr { if (pokemon.isFullHp() && typeMultiplier.value > 0.5) { typeMultiplier.value = 0.5; + pokemon.turnData.moveEffectiveness = 0.5; return true; } return false; diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 6afc0770a7fe..22df5d3a6c39 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -1538,6 +1538,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { * @returns The type damage multiplier, indicating the effectiveness of the move */ getMoveEffectiveness(source: Pokemon, move: Move, ignoreAbility: boolean = false, simulated: boolean = true, cancelled?: Utils.BooleanHolder): TypeDamageMultiplier { + if (!Utils.isNullOrUndefined(this.turnData?.moveEffectiveness)) { + return this.turnData?.moveEffectiveness; + } + if (move.hasAttr(TypelessAttr)) { return 1; } @@ -5019,6 +5023,7 @@ export class PokemonTurnData { public order: number; public statStagesIncreased: boolean = false; public statStagesDecreased: boolean = false; + public moveEffectiveness: TypeDamageMultiplier | null = null; } export enum AiType { diff --git a/src/phases/move-effect-phase.ts b/src/phases/move-effect-phase.ts index 263a576c4f08..ca1fb87654ff 100644 --- a/src/phases/move-effect-phase.ts +++ b/src/phases/move-effect-phase.ts @@ -354,12 +354,14 @@ export class MoveEffectPhase extends PokemonPhase { } else { // Queue message for number of hits made by multi-move // If multi-hit attack only hits once, still want to render a message - const hitsTotal = user.turnData.hitCount! - Math.max(user.turnData.hitsLeft!, 0); // TODO: are those bangs correct? + const hitsTotal = user.turnData.hitCount - Math.max(user.turnData.hitsLeft, 0); if (hitsTotal > 1 || (user.turnData.hitsLeft && user.turnData.hitsLeft > 0)) { // If there are multiple hits, or if there are hits of the multi-hit move left this.scene.queueMessage(i18next.t("battle:attackHitsCount", { count: hitsTotal })); } this.scene.applyModifiers(HitHealModifier, this.player, user); + // Clear all cached move effectiveness values among targets + this.getTargets().forEach((target) => target.turnData.moveEffectiveness = null); } } diff --git a/src/test/abilities/tera_shell.test.ts b/src/test/abilities/tera_shell.test.ts index 13df49136ca7..9995f7c34c3e 100644 --- a/src/test/abilities/tera_shell.test.ts +++ b/src/test/abilities/tera_shell.test.ts @@ -1,3 +1,4 @@ +import { BattlerIndex } from "#app/battle"; import { Abilities } from "#app/enums/abilities"; import { Moves } from "#app/enums/moves"; import { Species } from "#app/enums/species"; @@ -106,4 +107,26 @@ describe("Abilities - Tera Shell", () => { expect(playerPokemon.hp).toBe(playerPokemon.getMaxHp() - 40); } ); + + it( + "should change the effectiveness of all strikes of a multi-strike move", + async () => { + game.override.enemyMoveset([Moves.DOUBLE_HIT]); + + await game.classicMode.startBattle([Species.SNORLAX]); + + const playerPokemon = game.scene.getPlayerPokemon()!; + vi.spyOn(playerPokemon, "apply"); + + game.move.select(Moves.SPLASH); + + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.move.forceHit(); + for (let i = 0; i < 2; i++) { + await game.phaseInterceptor.to("MoveEffectPhase"); + expect(playerPokemon.apply).toHaveLastReturnedWith(HitResult.NOT_VERY_EFFECTIVE); + } + expect(playerPokemon.apply).toHaveReturnedTimes(2); + } + ); });