diff --git a/src/data/ability.ts b/src/data/ability.ts index 950538af9f6..edc213099a9 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -4408,7 +4408,7 @@ export function initAbilities() { .attr(StatusEffectImmunityAbAttr, StatusEffect.POISON, StatusEffect.TOXIC) .ignorable(), new Ability(Abilities.FLASH_FIRE, 3) - .attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1, (pokemon: Pokemon) => !pokemon.status || pokemon.status.effect !== StatusEffect.FREEZE) + .attr(TypeImmunityAddBattlerTagAbAttr, Type.FIRE, BattlerTagType.FIRE_BOOST, 1) .ignorable(), new Ability(Abilities.SHIELD_DUST, 3) .attr(IgnoreMoveEffectsAbAttr) diff --git a/src/field/pokemon.ts b/src/field/pokemon.ts index 618b4343950..36603b33b90 100644 --- a/src/field/pokemon.ts +++ b/src/field/pokemon.ts @@ -2440,8 +2440,10 @@ export default abstract class Pokemon extends Phaser.GameObjects.Container { } for (const tag of source.summonData.tags) { - // bypass yawn, and infatuation as those can not be passed via Baton Pass - if (tag.sourceMove === Moves.YAWN || tag.tagType === BattlerTagType.INFATUATED) { + // bypass those can not be passed via Baton Pass + const excludeTagTypes = new Set([BattlerTagType.DROWSY, BattlerTagType.INFATUATED, BattlerTagType.FIRE_BOOST]); + + if (excludeTagTypes.has(tag.tagType)) { continue; } diff --git a/src/test/abilities/flash_fire.test.ts b/src/test/abilities/flash_fire.test.ts new file mode 100644 index 00000000000..a5b3677edee --- /dev/null +++ b/src/test/abilities/flash_fire.test.ts @@ -0,0 +1,131 @@ +import { Species } from "#app/enums/species.js"; +import GameManager from "#test/utils/gameManager"; +import { Abilities } from "#enums/abilities"; +import { Moves } from "#enums/moves"; +import Phaser from "phaser"; +import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; +import { SPLASH_ONLY } from "#test/utils/testUtils"; +import { MovePhase, TurnEndPhase } from "#app/phases"; +import { getMovePosition } from "#test/utils/gameManagerUtils"; +import { Status, StatusEffect } from "#app/data/status-effect.js"; +import { BattlerTagType } from "#app/enums/battler-tag-type.js"; +import { BattlerIndex } from "#app/battle.js"; + +describe("Abilities - Flash Fire", () => { + 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") + .ability(Abilities.FLASH_FIRE) + .startingLevel(20) + .enemyLevel(20) + .disableCrits(); + }); + + + it("immune to Fire-type moves", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); + await game.startBattle([Species.BLISSEY]); + + const blissey = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.phaseInterceptor.to(TurnEndPhase); + expect(blissey.hp).toBe(blissey.getMaxHp()); + }, 20000); + + it("not activate if the Pokémon is protected from the Fire-type move", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.PROTECT]); + await game.startBattle([Species.BLISSEY]); + + const blissey = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.PROTECT)); + await game.phaseInterceptor.to(TurnEndPhase); + expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); + }, 20000); + + it("activated by Will-O-Wisp", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.WILL_O_WISP)).moveset(SPLASH_ONLY); + await game.startBattle([Species.BLISSEY]); + + const blissey = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.move.forceHit(); + await game.phaseInterceptor.to(MovePhase, false); + await game.move.forceHit(); + + await game.phaseInterceptor.to(TurnEndPhase); + expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); + }, 20000); + + it("activated after being frozen", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset(SPLASH_ONLY); + await game.startBattle([Species.BLISSEY]); + + const blissey = game.scene.getPlayerPokemon()!; + + blissey!.status = new Status(StatusEffect.FREEZE); + expect(blissey.status?.effect).toBe(StatusEffect.FREEZE); + + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + + await game.phaseInterceptor.to(TurnEndPhase); + expect(blissey!.getTag(BattlerTagType.FIRE_BOOST)).toBeDefined(); + }, 20000); + + it("not passing with baton pass", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.EMBER)).moveset([Moves.BATON_PASS]); + await game.startBattle([Species.BLISSEY, Species.CHANSEY]); + + // ensure use baton pass after enemy moved + game.doAttack(getMovePosition(game.scene, 0, Moves.BATON_PASS)); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + + game.doSelectPartyPokemon(1); + + await game.phaseInterceptor.to(TurnEndPhase); + const chansey = game.scene.getPlayerPokemon()!; + expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.CHANSEY); + expect(chansey!.getTag(BattlerTagType.FIRE_BOOST)).toBeUndefined(); + }, 20000); + + it("boosts Fire-type move when the ability is activated", async() => { + game.override.enemyMoveset(Array(4).fill(Moves.FIRE_PLEDGE)).moveset([Moves.EMBER, Moves.SPLASH]); + game.override.enemyAbility(Abilities.FLASH_FIRE).ability(Abilities.NONE); + await game.startBattle([Species.BLISSEY]); + const blissey = game.scene.getPlayerPokemon()!; + const initialHP = 1000; + blissey.hp = initialHP; + + // first turn + game.doAttack(getMovePosition(game.scene, 0, Moves.EMBER)); + await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]); + await game.phaseInterceptor.to(TurnEndPhase); + const originalDmg = initialHP - blissey.hp; + + expect(blissey.hp > 0); + blissey.hp = initialHP; + + // second turn + game.doAttack(getMovePosition(game.scene, 0, Moves.SPLASH)); + await game.phaseInterceptor.to(TurnEndPhase); + const flashFireDmg = initialHP - blissey.hp; + + expect(flashFireDmg).toBeGreaterThan(originalDmg); + }, 20000); +});