diff --git a/src/data/ability.ts b/src/data/ability.ts index f4701d3a4e1..950538af9f6 100644 --- a/src/data/ability.ts +++ b/src/data/ability.ts @@ -6,7 +6,7 @@ import { BattleStat, getBattleStatName } from "./battle-stat"; import { MovePhase, PokemonHealPhase, ShowAbilityPhase, StatChangePhase } from "../phases"; import { getPokemonNameWithAffix } from "../messages"; import { Weather, WeatherType } from "./weather"; -import { BattlerTag, GroundedTag, GulpMissileTag } from "./battler-tags"; +import { BattlerTag, GroundedTag, GulpMissileTag, SemiInvulnerableTag } from "./battler-tags"; import { StatusEffect, getNonVolatileStatusEffects, getStatusEffectDescriptor, getStatusEffectHealText } from "./status-effect"; import { Gender } from "./gender"; import Move, { AttackMove, MoveCategory, MoveFlags, MoveTarget, FlinchAttr, OneHitKOAttr, HitHealAttr, allMoves, StatusMove, SelfStatusMove, VariablePowerAttr, applyMoveAttrs, IncrementMovePriorityAttr, VariableMoveTypeAttr, RandomMovesetMoveAttr, RandomMoveAttr, NaturePowerAttr, CopyMoveAttr, MoveAttr, MultiHitAttr, ChargeAttr, SacrificialAttr, SacrificialAttrOnHit } from "./move"; @@ -517,7 +517,7 @@ export class PostDefendGulpMissileAbAttr extends PostDefendAbAttr { */ applyPostDefend(pokemon: Pokemon, passive: boolean, attacker: Pokemon, move: Move, hitResult: HitResult, args: any[]): boolean | Promise { const battlerTag = pokemon.getTag(GulpMissileTag); - if (!battlerTag || move.category === MoveCategory.STATUS) { + if (!battlerTag || move.category === MoveCategory.STATUS || pokemon.getTag(SemiInvulnerableTag)) { return false; } @@ -5138,9 +5138,7 @@ export function initAbilities() { .attr(NoFusionAbilityAbAttr) .attr(UncopiableAbilityAbAttr) .attr(UnswappableAbilityAbAttr) - .attr(PostDefendGulpMissileAbAttr) - // Does not transform when Surf/Dive misses/is protected - .partial(), + .attr(PostDefendGulpMissileAbAttr), new Ability(Abilities.STALWART, 8) .attr(BlockRedirectAbAttr), new Ability(Abilities.STEAM_ENGINE, 8) diff --git a/src/data/move.ts b/src/data/move.ts index c43150992bc..2d0ea11c3a0 100644 --- a/src/data/move.ts +++ b/src/data/move.ts @@ -4356,7 +4356,7 @@ export class AddBattlerTagAttr extends MoveEffectAttr { */ export class GulpMissileTagAttr extends MoveEffectAttr { constructor() { - super(true, MoveEffectTrigger.POST_APPLY); + super(true); } /** @@ -6954,7 +6954,7 @@ export function initMoves() { .makesContact(false) .partial(), new AttackMove(Moves.DIVE, Type.WATER, MoveCategory.PHYSICAL, 80, 100, 10, -1, 0, 3) - .attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", {pokemonName: "{USER}"}), BattlerTagType.UNDERWATER) + .attr(ChargeAttr, ChargeAnim.DIVE_CHARGING, i18next.t("moveTriggers:hidUnderwater", {pokemonName: "{USER}"}), BattlerTagType.UNDERWATER, true) .attr(GulpMissileTagAttr) .ignoresVirtual(), new AttackMove(Moves.ARM_THRUST, Type.FIGHTING, MoveCategory.PHYSICAL, 15, 100, 20, -1, 0, 3) diff --git a/src/form-change-phase.ts b/src/form-change-phase.ts index 55bf655081b..5acbc4fb77c 100644 --- a/src/form-change-phase.ts +++ b/src/form-change-phase.ts @@ -11,6 +11,7 @@ import { BattleSpec } from "#enums/battle-spec"; import { BattlePhase, MovePhase, PokemonHealPhase } from "./phases"; import { getTypeRgb } from "./data/type"; import { getPokemonNameWithAffix } from "./messages"; +import { SemiInvulnerableTag } from "./data/battler-tags"; export class FormChangePhase extends EvolutionPhase { private formChange: SpeciesFormChange; @@ -194,7 +195,7 @@ export class QuietFormChangePhase extends BattlePhase { const preName = getPokemonNameWithAffix(this.pokemon); - if (!this.pokemon.isOnField()) { + if (!this.pokemon.isOnField() || this.pokemon.getTag(SemiInvulnerableTag)) { this.pokemon.changeForm(this.formChange).then(() => { this.scene.ui.showText(getSpeciesFormChangeMessage(this.pokemon, this.formChange, preName), null, () => this.end(), 1500); }); diff --git a/src/test/abilities/gulp_missile.test.ts b/src/test/abilities/gulp_missile.test.ts index b64cbe2ef10..2647f765f6e 100644 --- a/src/test/abilities/gulp_missile.test.ts +++ b/src/test/abilities/gulp_missile.test.ts @@ -1,5 +1,6 @@ import { BattlerTagType } from "#app/enums/battler-tag-type.js"; import { + BerryPhase, MoveEndPhase, TurnEndPhase, TurnStartPhase, @@ -14,7 +15,6 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite import { SPLASH_ONLY } from "../utils/testUtils"; import { BattleStat } from "#app/data/battle-stat.js"; import { StatusEffect } from "#app/enums/status-effect.js"; -import { GulpMissileTag } from "#app/data/battler-tags.js"; import Pokemon from "#app/field/pokemon.js"; describe("Abilities - Gulp Missile", () => { @@ -84,6 +84,17 @@ describe("Abilities - Gulp Missile", () => { expect(cramorant.formIndex).toBe(GORGING_FORM); }); + it("changes form during Dive's charge turn", async () => { + await game.startBattle([Species.CRAMORANT]); + const cramorant = game.scene.getPlayerPokemon()!; + + game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); + await game.phaseInterceptor.to(MoveEndPhase); + + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); + }); + it("deals ΒΌ of the attacker's maximum HP when hit by a damaging attack", async () => { game.override.enemyMoveset(Array(4).fill(Moves.TACKLE)); await game.startBattle([Species.CRAMORANT]); @@ -165,29 +176,16 @@ describe("Abilities - Gulp Missile", () => { }); it("does not activate the ability when underwater", async () => { - game.override - .enemyMoveset(Array(4).fill(Moves.SURF)) - .enemySpecies(Species.REGIELEKI) - .enemyAbility(Abilities.BALL_FETCH) - .enemyLevel(5); + game.override.enemyMoveset(Array(4).fill(Moves.SURF)); await game.startBattle([Species.CRAMORANT]); const cramorant = game.scene.getPlayerPokemon()!; game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); - await game.toNextTurn(); + await game.phaseInterceptor.to(BerryPhase, false); - // Turn 2 underwater, enemy moves first - game.doAttack(getMovePosition(game.scene, 0, Moves.DIVE)); - await game.phaseInterceptor.to(MoveEndPhase); - - expect(cramorant.formIndex).toBe(NORMAL_FORM); - expect(cramorant.getTag(GulpMissileTag)).toBeUndefined(); - - // Turn 2 Cramorant comes out and changes form - await game.phaseInterceptor.to(TurnEndPhase); - expect(cramorant.formIndex).not.toBe(NORMAL_FORM); - expect(cramorant.getTag(GulpMissileTag)).toBeDefined(); + expect(cramorant.getTag(BattlerTagType.GULP_MISSILE_ARROKUDA)).toBeDefined(); + expect(cramorant.formIndex).toBe(GULPING_FORM); }); it("prevents effect damage but inflicts secondary effect on attacker with Magic Guard", async () => {