[Move] Fully Implement Gulp Missile (#3438)

* Fix Dive + Gulp Missile interaction

* Fix animation + remove tests for inaccurate behavior

* Fix strict-null issue in new test
This commit is contained in:
innerthunder 2024-08-08 11:23:11 -07:00 committed by GitHub
parent 15106b83fb
commit 7f5e873457
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 23 additions and 26 deletions

View File

@ -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<boolean> {
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)

View File

@ -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)

View File

@ -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);
});

View File

@ -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 () => {