Fix random failure in Parental Bond tests (#4036)

Co-authored-by: flx-sta <50131232+flx-sta@users.noreply.github.com>
This commit is contained in:
innerthunder 2024-09-04 22:31:32 -07:00 committed by GitHub
parent 61ab52c295
commit 1434a3edaf
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -2,12 +2,6 @@ import { Stat } from "#enums/stat";
import { StatusEffect } from "#app/data/status-effect"; import { StatusEffect } from "#app/data/status-effect";
import { Type } from "#app/data/type"; import { Type } from "#app/data/type";
import { BattlerTagType } from "#app/enums/battler-tag-type"; import { BattlerTagType } from "#app/enums/battler-tag-type";
import { BerryPhase } from "#app/phases/berry-phase";
import { CommandPhase } from "#app/phases/command-phase";
import { DamagePhase } from "#app/phases/damage-phase";
import { MoveEffectPhase } from "#app/phases/move-effect-phase";
import { MoveEndPhase } from "#app/phases/move-end-phase";
import { TurnEndPhase } from "#app/phases/turn-end-phase";
import { toDmgValue } from "#app/utils"; import { toDmgValue } from "#app/utils";
import { Abilities } from "#enums/abilities"; import { Abilities } from "#enums/abilities";
import { Moves } from "#enums/moves"; import { Moves } from "#enums/moves";
@ -15,7 +9,7 @@ import { Species } from "#enums/species";
import GameManager from "#test/utils/gameManager"; import GameManager from "#test/utils/gameManager";
import { SPLASH_ONLY } from "#test/utils/testUtils"; import { SPLASH_ONLY } from "#test/utils/testUtils";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, test } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
const TIMEOUT = 20 * 1000; const TIMEOUT = 20 * 1000;
@ -39,36 +33,31 @@ describe("Abilities - Parental Bond", () => {
game.override.disableCrits(); game.override.disableCrits();
game.override.ability(Abilities.PARENTAL_BOND); game.override.ability(Abilities.PARENTAL_BOND);
game.override.enemySpecies(Species.SNORLAX); game.override.enemySpecies(Species.SNORLAX);
game.override.enemyAbility(Abilities.INSOMNIA); game.override.enemyAbility(Abilities.FUR_COAT);
game.override.enemyMoveset(SPLASH_ONLY); game.override.enemyMoveset(SPLASH_ONLY);
game.override.startingLevel(100); game.override.startingLevel(100);
game.override.enemyLevel(100); game.override.enemyLevel(100);
}); });
test( it(
"ability should add second strike to attack move", "should add second strike to attack move",
async () => { async () => {
game.override.moveset([Moves.TACKLE]); game.override.moveset([Moves.TACKLE]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
let enemyStartingHp = enemyPokemon.hp; let enemyStartingHp = enemyPokemon.hp;
game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("DamagePhase");
await game.phaseInterceptor.to(DamagePhase);
const firstStrikeDamage = enemyStartingHp - enemyPokemon.hp; const firstStrikeDamage = enemyStartingHp - enemyPokemon.hp;
enemyStartingHp = enemyPokemon.hp; enemyStartingHp = enemyPokemon.hp;
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp; const secondStrikeDamage = enemyStartingHp - enemyPokemon.hp;
@ -77,556 +66,460 @@ describe("Abilities - Parental Bond", () => {
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should apply secondary effects to both strikes", "should apply secondary effects to both strikes",
async () => { async () => {
game.override.moveset([Moves.POWER_UP_PUNCH]); game.override.moveset([Moves.POWER_UP_PUNCH]);
game.override.enemySpecies(Species.AMOONGUSS); game.override.enemySpecies(Species.AMOONGUSS);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.POWER_UP_PUNCH); game.move.select(Moves.POWER_UP_PUNCH);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(2);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply to Status moves", "should not apply to Status moves",
async () => { async () => {
game.override.moveset([Moves.BABY_DOLL_EYES]); game.override.moveset([Moves.BABY_DOLL_EYES]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.BABY_DOLL_EYES); game.move.select(Moves.BABY_DOLL_EYES);
await game.phaseInterceptor.to(BerryPhase, false);
await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply to multi-hit moves", "should not apply to multi-hit moves",
async () => { async () => {
game.override.moveset([Moves.DOUBLE_HIT]); game.override.moveset([Moves.DOUBLE_HIT]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.DOUBLE_HIT); game.move.select(Moves.DOUBLE_HIT);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply to self-sacrifice moves", "should not apply to self-sacrifice moves",
async () => { async () => {
game.override.moveset([Moves.SELF_DESTRUCT]); game.override.moveset([Moves.SELF_DESTRUCT]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.SELF_DESTRUCT); game.move.select(Moves.SELF_DESTRUCT);
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to("DamagePhase", false);
expect(leadPokemon.turnData.hitCount).toBe(1); expect(leadPokemon.turnData.hitCount).toBe(1);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply to Rollout", "should not apply to Rollout",
async () => { async () => {
game.override.moveset([Moves.ROLLOUT]); game.override.moveset([Moves.ROLLOUT]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.ROLLOUT); game.move.select(Moves.ROLLOUT);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to("DamagePhase", false);
expect(leadPokemon.turnData.hitCount).toBe(1); expect(leadPokemon.turnData.hitCount).toBe(1);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply multiplier to fixed-damage moves", "should not apply multiplier to fixed-damage moves",
async () => { async () => {
game.override.moveset([Moves.DRAGON_RAGE]); game.override.moveset([Moves.DRAGON_RAGE]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
const enemyStartingHp = enemyPokemon.hp;
game.move.select(Moves.DRAGON_RAGE); game.move.select(Moves.DRAGON_RAGE);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.hp).toBe(enemyStartingHp - 80); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 80);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply multiplier to counter moves", "should not apply multiplier to counter moves",
async () => { async () => {
game.override.moveset([Moves.COUNTER]); game.override.moveset([Moves.COUNTER]);
game.override.enemyMoveset([Moves.TACKLE, Moves.TACKLE, Moves.TACKLE, Moves.TACKLE]); game.override.enemyMoveset(Array(4).fill(Moves.TACKLE));
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.SHUCKLE]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
const playerStartingHp = leadPokemon.hp;
const enemyStartingHp = enemyPokemon.hp;
game.move.select(Moves.COUNTER); game.move.select(Moves.COUNTER);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
const playerDamage = playerStartingHp - leadPokemon.hp; const playerDamage = leadPokemon.getMaxHp() - leadPokemon.hp;
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.hp).toBe(enemyStartingHp - 4 * playerDamage); expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp() - 4 * playerDamage);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply to multi-target moves", "should not apply to multi-target moves",
async () => { async () => {
game.override.battleType("double"); game.override.battleType("double");
game.override.moveset([Moves.EARTHQUAKE]); game.override.moveset([Moves.EARTHQUAKE]);
game.override.passiveAbility(Abilities.LEVITATE);
await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]); await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]);
const playerPokemon = game.scene.getPlayerField(); const playerPokemon = game.scene.getPlayerField();
expect(playerPokemon.length).toBe(2);
playerPokemon.forEach(p => expect(p).not.toBe(undefined));
const enemyPokemon = game.scene.getEnemyField();
expect(enemyPokemon.length).toBe(2);
enemyPokemon.forEach(p => expect(p).not.toBe(undefined));
game.move.select(Moves.EARTHQUAKE); game.move.select(Moves.EARTHQUAKE);
await game.phaseInterceptor.to(CommandPhase);
game.move.select(Moves.EARTHQUAKE, 1); game.move.select(Moves.EARTHQUAKE, 1);
await game.phaseInterceptor.to(BerryPhase, false);
await game.phaseInterceptor.to("BerryPhase", false);
playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1)); playerPokemon.forEach(p => expect(p.turnData.hitCount).toBe(1));
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should apply to multi-target moves when hitting only one target", "should apply to multi-target moves when hitting only one target",
async () => { async () => {
game.override.moveset([Moves.EARTHQUAKE]); game.override.moveset([Moves.EARTHQUAKE]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.EARTHQUAKE); game.move.select(Moves.EARTHQUAKE);
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to("DamagePhase", false);
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should only trigger post-target move effects once", "should only trigger post-target move effects once",
async () => { async () => {
game.override.moveset([Moves.MIND_BLOWN]); game.override.moveset([Moves.MIND_BLOWN]);
await game.startBattle([Species.PIDGEOT]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.MIND_BLOWN); game.move.select(Moves.MIND_BLOWN);
await game.phaseInterceptor.to(DamagePhase, false); await game.phaseInterceptor.to("DamagePhase", false);
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
// This test will time out if the user faints // This test will time out if the user faints
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(leadPokemon.hp).toBe(toDmgValue(leadPokemon.getMaxHp() / 2)); expect(leadPokemon.hp).toBe(Math.ceil(leadPokemon.getMaxHp() / 2));
}, TIMEOUT }, TIMEOUT
); );
test( it(
"Burn Up only removes type after second strike with this ability", "Burn Up only removes type after the second strike",
async () => { async () => {
game.override.moveset([Moves.BURN_UP]); game.override.moveset([Moves.BURN_UP]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.CHARIZARD]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.BURN_UP); game.move.select(Moves.BURN_UP);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("MoveEffectPhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
expect(enemyPokemon.hp).toBeGreaterThan(0); expect(enemyPokemon.hp).toBeGreaterThan(0);
expect(leadPokemon.isOfType(Type.FIRE)).toBe(true); expect(leadPokemon.isOfType(Type.FIRE)).toBe(true);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(leadPokemon.isOfType(Type.FIRE)).toBe(false); expect(leadPokemon.isOfType(Type.FIRE)).toBe(false);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"Moves boosted by this ability and Multi-Lens should strike 4 times", "Moves boosted by this ability and Multi-Lens should strike 4 times",
async () => { async () => {
game.override.moveset([Moves.TACKLE]); game.override.moveset([Moves.TACKLE]);
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE);
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
expect(leadPokemon.turnData.hitCount).toBe(4); expect(leadPokemon.turnData.hitCount).toBe(4);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"Super Fang boosted by this ability and Multi-Lens should strike twice", "Super Fang boosted by this ability and Multi-Lens should strike twice",
async () => { async () => {
game.override.moveset([Moves.SUPER_FANG]); game.override.moveset([Moves.SUPER_FANG]);
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
const enemyStartingHp = enemyPokemon.hp;
game.move.select(Moves.SUPER_FANG); game.move.select(Moves.SUPER_FANG);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
await game.phaseInterceptor.to(MoveEndPhase, false); await game.phaseInterceptor.to("MoveEndPhase", false);
expect(enemyPokemon.hp).toBe(Math.ceil(enemyStartingHp * 0.25)); expect(enemyPokemon.hp).toBe(Math.ceil(enemyPokemon.getMaxHp() * 0.25));
}, TIMEOUT }, TIMEOUT
); );
test( it(
"Seismic Toss boosted by this ability and Multi-Lens should strike twice", "Seismic Toss boosted by this ability and Multi-Lens should strike twice",
async () => { async () => {
game.override.moveset([Moves.SEISMIC_TOSS]); game.override.moveset([Moves.SEISMIC_TOSS]);
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
const enemyStartingHp = enemyPokemon.hp; const enemyStartingHp = enemyPokemon.hp;
game.move.select(Moves.SEISMIC_TOSS); game.move.select(Moves.SEISMIC_TOSS);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
await game.phaseInterceptor.to(MoveEndPhase, false); await game.phaseInterceptor.to("MoveEndPhase", false);
expect(enemyPokemon.hp).toBe(enemyStartingHp - 200); expect(enemyPokemon.hp).toBe(enemyStartingHp - 200);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"Hyper Beam boosted by this ability should strike twice, then recharge", "Hyper Beam boosted by this ability should strike twice, then recharge",
async () => { async () => {
game.override.moveset([Moves.HYPER_BEAM]); game.override.moveset([Moves.HYPER_BEAM]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.HYPER_BEAM); game.move.select(Moves.HYPER_BEAM);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined(); expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeUndefined();
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeDefined(); expect(leadPokemon.getTag(BattlerTagType.RECHARGING)).toBeDefined();
}, TIMEOUT }, TIMEOUT
); );
/** TODO: Fix TRAPPED tag lapsing incorrectly, then run this test */ it(
test(
"Anchor Shot boosted by this ability should only trap the target after the second hit", "Anchor Shot boosted by this ability should only trap the target after the second hit",
async () => { async () => {
game.override.moveset([Moves.ANCHOR_SHOT]); game.override.moveset([Moves.ANCHOR_SHOT]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.ANCHOR_SHOT); game.move.select(Moves.ANCHOR_SHOT);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined(); // Passes expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeUndefined();
await game.phaseInterceptor.to(MoveEndPhase); await game.phaseInterceptor.to("MoveEndPhase");
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); // Passes expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined();
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined(); // Fails :( expect(enemyPokemon.getTag(BattlerTagType.TRAPPED)).toBeDefined();
}, TIMEOUT }, TIMEOUT
); );
test( it(
"Smack Down boosted by this ability should only ground the target after the second hit", "Smack Down boosted by this ability should only ground the target after the second hit",
async () => { async () => {
game.override.moveset([Moves.SMACK_DOWN]); game.override.moveset([Moves.SMACK_DOWN]);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.SMACK_DOWN); game.move.select(Moves.SMACK_DOWN);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined(); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeUndefined();
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined(); expect(enemyPokemon.getTag(BattlerTagType.IGNORE_FLYING)).toBeDefined();
}, TIMEOUT }, TIMEOUT
); );
test( it(
"U-turn boosted by this ability should strike twice before forcing a switch", "U-turn boosted by this ability should strike twice before forcing a switch",
async () => { async () => {
game.override.moveset([Moves.U_TURN]); game.override.moveset([Moves.U_TURN]);
await game.startBattle([Species.CHARIZARD, Species.BLASTOISE]); await game.classicMode.startBattle([Species.MAGIKARP, Species.BLASTOISE]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.U_TURN); game.move.select(Moves.U_TURN);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(MoveEffectPhase); await game.phaseInterceptor.to("MoveEffectPhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
// This will cause this test to time out if the switch was forced on the first hit. // This will cause this test to time out if the switch was forced on the first hit.
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"Wake-Up Slap boosted by this ability should only wake up the target after the second hit", "Wake-Up Slap boosted by this ability should only wake up the target after the second hit",
async () => { async () => {
game.override.moveset([Moves.WAKE_UP_SLAP]).enemyStatusEffect(StatusEffect.SLEEP); game.override.moveset([Moves.WAKE_UP_SLAP]).enemyStatusEffect(StatusEffect.SLEEP);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.WAKE_UP_SLAP); game.move.select(Moves.WAKE_UP_SLAP);
await game.move.forceHit(); await game.move.forceHit();
await game.phaseInterceptor.to(DamagePhase); await game.phaseInterceptor.to("DamagePhase");
expect(leadPokemon.turnData.hitCount).toBe(2); expect(leadPokemon.turnData.hitCount).toBe(2);
expect(enemyPokemon.status?.effect).toBe(StatusEffect.SLEEP); expect(enemyPokemon.status?.effect).toBe(StatusEffect.SLEEP);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.status?.effect).toBeUndefined(); expect(enemyPokemon.status?.effect).toBeUndefined();
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not cause user to hit into King's Shield more than once", "should not cause user to hit into King's Shield more than once",
async () => { async () => {
game.override.moveset([Moves.TACKLE]); game.override.moveset([Moves.TACKLE]);
game.override.enemyMoveset([Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD, Moves.KINGS_SHIELD]); game.override.enemyMoveset(Array(4).fill(Moves.KINGS_SHIELD));
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!; const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.TACKLE); game.move.select(Moves.TACKLE);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(leadPokemon.getStatStage(Stat.ATK)).toBe(-1); expect(leadPokemon.getStatStage(Stat.ATK)).toBe(-1);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not cause user to hit into Storm Drain more than once", "should not cause user to hit into Storm Drain more than once",
async () => { async () => {
game.override.moveset([Moves.WATER_GUN]); game.override.moveset([Moves.WATER_GUN]);
game.override.enemyAbility(Abilities.STORM_DRAIN); game.override.enemyAbility(Abilities.STORM_DRAIN);
await game.startBattle([Species.CHARIZARD]); await game.classicMode.startBattle([Species.MAGIKARP]);
const leadPokemon = game.scene.getPlayerPokemon()!;
expect(leadPokemon).not.toBe(undefined);
const enemyPokemon = game.scene.getEnemyPokemon()!; const enemyPokemon = game.scene.getEnemyPokemon()!;
expect(enemyPokemon).not.toBe(undefined);
game.move.select(Moves.WATER_GUN); game.move.select(Moves.WATER_GUN);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(1); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(1);
}, TIMEOUT }, TIMEOUT
); );
test( it(
"ability should not apply to multi-target moves with Multi-Lens", "should not apply to multi-target moves with Multi-Lens",
async () => { async () => {
game.override.battleType("double"); game.override.battleType("double");
game.override.moveset([Moves.EARTHQUAKE, Moves.SPLASH]); game.override.moveset([Moves.EARTHQUAKE, Moves.SPLASH]);
game.override.passiveAbility(Abilities.LEVITATE);
game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]); game.override.startingHeldItems([{ name: "MULTI_LENS", count: 1 }]);
await game.startBattle([Species.CHARIZARD, Species.PIDGEOT]); await game.classicMode.startBattle([Species.MAGIKARP, Species.FEEBAS]);
const playerPokemon = game.scene.getPlayerField();
expect(playerPokemon.length).toBe(2);
playerPokemon.forEach(p => expect(p).not.toBe(undefined));
const enemyPokemon = game.scene.getEnemyField(); const enemyPokemon = game.scene.getEnemyField();
expect(enemyPokemon.length).toBe(2);
enemyPokemon.forEach(p => expect(p).not.toBe(undefined));
const enemyStartingHp = enemyPokemon.map(p => p.hp); const enemyStartingHp = enemyPokemon.map(p => p.hp);
game.move.select(Moves.EARTHQUAKE); game.move.select(Moves.EARTHQUAKE);
await game.phaseInterceptor.to(CommandPhase);
game.move.select(Moves.SPLASH, 1); game.move.select(Moves.SPLASH, 1);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("DamagePhase");
await game.phaseInterceptor.to(DamagePhase);
const enemyFirstHitDamage = enemyStartingHp.map((hp, i) => hp - enemyPokemon[i].hp); const enemyFirstHitDamage = enemyStartingHp.map((hp, i) => hp - enemyPokemon[i].hp);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2 * enemyFirstHitDamage[i])); enemyPokemon.forEach((p, i) => expect(enemyStartingHp[i] - p.hp).toBe(2 * enemyFirstHitDamage[i]));
}, TIMEOUT }, TIMEOUT
); );
}); });