[Ability] Fully implement shields down (#5205)

This commit is contained in:
Sirz Benjie 2025-01-30 13:21:48 -06:00 committed by GitHub
parent 861c2e9274
commit 6b36e4ca06
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 134 additions and 4 deletions

View File

@ -5943,12 +5943,14 @@ export function initAbilities() {
.attr(PostBattleInitFormChangeAbAttr, () => 0)
.attr(PostSummonFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
.attr(PostTurnFormChangeAbAttr, p => p.formIndex % 7 + (p.getHpRatio() <= 0.5 ? 7 : 0))
.conditionalAttr(p => p.formIndex !== 7, StatusEffectImmunityAbAttr)
.conditionalAttr(p => p.formIndex !== 7, BattlerTagImmunityAbAttr, BattlerTagType.DROWSY)
.attr(UncopiableAbilityAbAttr)
.attr(UnswappableAbilityAbAttr)
.attr(UnsuppressableAbilityAbAttr)
.attr(NoFusionAbilityAbAttr)
.bypassFaint()
.partial(), // Meteor form should protect against status effects and yawn
.attr(NoTransformAbilityAbAttr)
.bypassFaint(),
new Ability(Abilities.STAKEOUT, 7)
.attr(MovePowerBoostAbAttr, (user, target, move) => !!target?.turnData.switchedInThisTurn, 2),
new Ability(Abilities.WATER_BUBBLE, 7)

View File

@ -1,4 +1,5 @@
import { Status } from "#app/data/status-effect";
import { BattlerTagType } from "#app/enums/battler-tag-type";
import { QuietFormChangePhase } from "#app/phases/quiet-form-change-phase";
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { Abilities } from "#enums/abilities";
@ -29,7 +30,7 @@ describe("Abilities - SHIELDS DOWN", () => {
game.override.battleType("single");
game.override.ability(Abilities.SHIELDS_DOWN);
game.override.moveset([ moveToUse ]);
game.override.enemyMoveset([ Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE ]);
game.override.enemyMoveset([ Moves.TACKLE ]);
});
test(
@ -42,7 +43,7 @@ describe("Abilities - SHIELDS DOWN", () => {
[Species.MINIOR]: coreForm,
});
await game.startBattle([ Species.MAGIKARP, Species.MINIOR ]);
await game.classicMode.startBattle([ Species.MAGIKARP, Species.MINIOR ]);
const minior = game.scene.getPlayerParty().find((p) => p.species.speciesId === Species.MINIOR)!;
expect(minior).not.toBe(undefined);
@ -61,4 +62,131 @@ describe("Abilities - SHIELDS DOWN", () => {
expect(minior.formIndex).toBe(meteorForm);
},
);
test("should ignore non-volatile status moves",
async () => {
game.override.enemyMoveset([ Moves.SPORE ]);
await game.classicMode.startBattle([ Species.MINIOR ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
}
);
test("should still ignore non-volatile status moves used by a pokemon with mold breaker",
async () => {
game.override.enemyAbility(Abilities.MOLD_BREAKER);
game.override.enemyMoveset([ Moves.SPORE ]);
await game.classicMode.startBattle([ Species.MINIOR ]);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.SPORE);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
}
);
test("should ignore non-volatile secondary status effects",
async() => {
game.override.enemyMoveset([ Moves.NUZZLE ]);
await game.classicMode.startBattle([ Species.MINIOR ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
}
);
test("should ignore status moves even through mold breaker",
async () => {
game.override.enemyMoveset([ Moves.SPORE ]);
game.override.enemyAbility(Abilities.MOLD_BREAKER);
await game.classicMode.startBattle([ Species.MINIOR ]);
game.move.select(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.status).toBe(undefined);
}
);
// toxic spikes currently does not poison flying types when gravity is in effect
test.todo("should become poisoned by toxic spikes when grounded",
async () => {
game.override.enemyMoveset([ Moves.GRAVITY, Moves.TOXIC_SPIKES, Moves.SPLASH ]);
game.override.moveset([ Moves.GRAVITY, Moves.SPLASH ]);
await game.classicMode.startBattle([ Species.MAGIKARP, Species.MINIOR ]);
// turn 1
game.move.select(Moves.GRAVITY);
await game.forceEnemyMove(Moves.TOXIC_SPIKES);
await game.toNextTurn();
// turn 2
game.doSwitchPokemon(1);
await game.forceEnemyMove(Moves.SPLASH);
await game.toNextTurn();
expect(game.scene.getPlayerPokemon()!.species.speciesId).toBe(Species.MINIOR);
expect(game.scene.getPlayerPokemon()!.species.formIndex).toBe(0);
expect(game.scene.getPlayerPokemon()!.status?.effect).toBe(StatusEffect.POISON);
}
);
test("should ignore yawn",
async () => {
game.override.enemyMoveset([ Moves.YAWN ]);
await game.classicMode.startBattle([ Species.MAGIKARP, Species.MINIOR ]);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.YAWN);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.findTag( (tag ) => tag.tagType === BattlerTagType.DROWSY)).toBe(undefined);
}
);
test("should not ignore volatile status effects",
async () => {
game.override.enemyMoveset([ Moves.CONFUSE_RAY ]);
await game.classicMode.startBattle([ Species.MINIOR ]);
game.move.select(Moves.SPLASH);
await game.forceEnemyMove(Moves.CONFUSE_RAY);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getPlayerPokemon()!.findTag( (tag ) => tag.tagType === BattlerTagType.CONFUSED)).not.toBe(undefined);
}
);
// the `NoTransformAbilityAbAttr` attribute is not checked anywhere, so this test cannot pass.
test.todo("ditto should not be immune to status after transforming",
async () => {
game.override.enemySpecies(Species.DITTO);
game.override.enemyAbility(Abilities.IMPOSTER);
game.override.moveset([ Moves.SPLASH, Moves.SPORE ]);
await game.classicMode.startBattle([ Species.MINIOR ]);
game.move.select(Moves.SPORE);
await game.forceEnemyMove(Moves.SPLASH);
await game.phaseInterceptor.to(TurnEndPhase);
expect(game.scene.getEnemyPokemon()!.status?.effect).toBe(StatusEffect.SLEEP);
}
);
});