mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-06-20 16:42:45 +02:00
Merge 053963c84d
into 4b70fab608
This commit is contained in:
commit
9359d69ea4
@ -45,7 +45,7 @@ describe("Abilities - Good As Gold", () => {
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(MoveId.SPLASH, 0);
|
||||
game.move.select(MoveId.SPLASH);
|
||||
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
@ -54,12 +54,13 @@ describe("Abilities - Good As Gold", () => {
|
||||
});
|
||||
|
||||
it("should block memento and prevent the user from fainting", async () => {
|
||||
game.override.enemyMoveset([MoveId.MEMENTO]);
|
||||
game.override.enemyAbility(AbilityId.GOOD_AS_GOLD);
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
game.move.select(MoveId.MEMENTO);
|
||||
|
||||
game.move.use(MoveId.MEMENTO);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
expect(game.scene.getPlayerPokemon()!.isFainted()).toBe(false);
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(game.field.getPlayerPokemon().isFainted()).toBe(false);
|
||||
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(0);
|
||||
});
|
||||
|
||||
it("should not block any status moves that target the field, one side, or all pokemon", async () => {
|
||||
|
@ -259,7 +259,7 @@ describe("Abilities - Ice Face", () => {
|
||||
|
||||
const eiscue = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined);
|
||||
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined();
|
||||
expect(eiscue.formIndex).toBe(icefaceForm);
|
||||
expect(eiscue.hasAbility(AbilityId.ICE_FACE)).toBe(true);
|
||||
});
|
||||
@ -269,13 +269,9 @@ describe("Abilities - Ice Face", () => {
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
|
||||
game.move.select(MoveId.SIMPLE_BEAM);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase);
|
||||
|
||||
const eiscue = game.scene.getEnemyPokemon()!;
|
||||
|
||||
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).not.toBe(undefined);
|
||||
expect(eiscue.getTag(BattlerTagType.ICE_FACE)).toBeDefined();
|
||||
expect(eiscue.formIndex).toBe(icefaceForm);
|
||||
expect(game.scene.getPlayerPokemon()!.hasAbility(AbilityId.TRACE)).toBe(true);
|
||||
});
|
||||
|
@ -75,24 +75,31 @@ describe("Abilities - Imposter", () => {
|
||||
});
|
||||
|
||||
it("should copy in-battle overridden stats", async () => {
|
||||
game.override.enemyMoveset([MoveId.POWER_SPLIT]);
|
||||
game.override.ability(AbilityId.BALL_FETCH);
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP, SpeciesId.DITTO]);
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.DITTO]);
|
||||
const [karp, ditto] = game.scene.getPlayerField();
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
game.field.mockAbility(ditto, AbilityId.IMPOSTER);
|
||||
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
// Turn 1: Use power split
|
||||
const avgAtk = Math.floor((karp.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2);
|
||||
const avgSpAtk = Math.floor((karp.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2);
|
||||
|
||||
const avgAtk = Math.floor((player.getStat(Stat.ATK, false) + enemy.getStat(Stat.ATK, false)) / 2);
|
||||
const avgSpAtk = Math.floor((player.getStat(Stat.SPATK, false) + enemy.getStat(Stat.SPATK, false)) / 2);
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.move.forceEnemyMove(MoveId.POWER_SPLIT);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
expect(player.getStat(Stat.ATK, false)).toBe(avgAtk);
|
||||
expect(enemy.getStat(Stat.ATK, false)).toBe(avgAtk);
|
||||
|
||||
expect(player.getStat(Stat.SPATK, false)).toBe(avgSpAtk);
|
||||
expect(enemy.getStat(Stat.SPATK, false)).toBe(avgSpAtk);
|
||||
|
||||
// Turn 2: Switch in ditto, should copy enemy ability
|
||||
game.doSwitchPokemon(1);
|
||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(ditto.getStat(Stat.ATK, false)).toBe(avgAtk);
|
||||
expect(ditto.getStat(Stat.SPATK, false)).toBe(avgSpAtk);
|
||||
});
|
||||
|
||||
it("should set each move's pp to a maximum of 5", async () => {
|
||||
@ -101,31 +108,22 @@ describe("Abilities - Imposter", () => {
|
||||
await game.classicMode.startBattle([SpeciesId.DITTO]);
|
||||
const player = game.scene.getPlayerPokemon()!;
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
player.getMoveset().forEach(move => {
|
||||
// Should set correct maximum PP without touching `ppUp`
|
||||
if (move) {
|
||||
if (move.moveId === MoveId.SKETCH) {
|
||||
expect(move.getMovePp()).toBe(1);
|
||||
} else {
|
||||
expect(move.getMovePp()).toBe(5);
|
||||
}
|
||||
expect(move.ppUp).toBe(0);
|
||||
if (move.moveId === MoveId.SKETCH) {
|
||||
expect(move.getMovePp()).toBe(1);
|
||||
} else {
|
||||
expect(move.getMovePp()).toBe(5);
|
||||
}
|
||||
expect(move.ppUp).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
it("should activate its ability if it copies one that activates on summon", async () => {
|
||||
game.override.enemyAbility(AbilityId.INTIMIDATE);
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.DITTO]);
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
|
||||
expect(game.scene.getEnemyPokemon()?.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(game.field.getEnemyPokemon().getStatStage(Stat.ATK)).toBe(-1);
|
||||
});
|
||||
|
||||
it("should persist transformed attributes across reloads", async () => {
|
||||
|
@ -3,7 +3,6 @@ import Phaser from "phaser";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { getMovePosition } from "#test/testUtils/gameManagerUtils";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
@ -114,7 +113,7 @@ describe("Abilities - Intimidate", () => {
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
|
||||
game.move.select(getMovePosition(game.scene, 0, MoveId.SPLASH));
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
enemyPokemon = game.scene.getEnemyPokemon()!;
|
||||
|
@ -36,10 +36,8 @@ describe("Abilities - Lightningrod", () => {
|
||||
it("should redirect electric type moves", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]);
|
||||
|
||||
const enemy1 = game.scene.getEnemyField()[0];
|
||||
const enemy2 = game.scene.getEnemyField()[1];
|
||||
|
||||
enemy2.summonData.ability = AbilityId.LIGHTNING_ROD;
|
||||
const [enemy1, enemy2] = game.scene.getEnemyField();
|
||||
game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD);
|
||||
|
||||
game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY);
|
||||
game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||
@ -52,10 +50,8 @@ describe("Abilities - Lightningrod", () => {
|
||||
game.override.moveset([MoveId.SPLASH, MoveId.AERIAL_ACE]);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]);
|
||||
|
||||
const enemy1 = game.scene.getEnemyField()[0];
|
||||
const enemy2 = game.scene.getEnemyField()[1];
|
||||
|
||||
enemy2.summonData.ability = AbilityId.LIGHTNING_ROD;
|
||||
const [enemy1, enemy2] = game.scene.getEnemyField();
|
||||
game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD);
|
||||
|
||||
game.move.select(MoveId.AERIAL_ACE, BattlerIndex.PLAYER, BattlerIndex.ENEMY);
|
||||
game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||
@ -68,8 +64,7 @@ describe("Abilities - Lightningrod", () => {
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]);
|
||||
|
||||
const enemy2 = game.scene.getEnemyField()[1];
|
||||
|
||||
enemy2.summonData.ability = AbilityId.LIGHTNING_ROD;
|
||||
game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD);
|
||||
|
||||
game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY);
|
||||
game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||
@ -81,31 +76,25 @@ describe("Abilities - Lightningrod", () => {
|
||||
|
||||
it("should not redirect moves changed from electric type via ability", async () => {
|
||||
game.override.ability(AbilityId.NORMALIZE);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||
|
||||
const enemy1 = game.scene.getEnemyField()[0];
|
||||
const enemy2 = game.scene.getEnemyField()[1];
|
||||
|
||||
enemy2.summonData.ability = AbilityId.LIGHTNING_ROD;
|
||||
const [enemy1, enemy2] = game.scene.getEnemyField();
|
||||
game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD);
|
||||
|
||||
game.move.select(MoveId.SHOCK_WAVE, BattlerIndex.PLAYER, BattlerIndex.ENEMY);
|
||||
game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy1.isFullHp()).toBe(false);
|
||||
});
|
||||
|
||||
it("should redirect moves changed to electric type via ability", async () => {
|
||||
game.override.ability(AbilityId.GALVANIZE).moveset(MoveId.TACKLE);
|
||||
game.override.ability(AbilityId.GALVANIZE);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]);
|
||||
|
||||
const enemy1 = game.scene.getEnemyField()[0];
|
||||
const enemy2 = game.scene.getEnemyField()[1];
|
||||
const [enemy1, enemy2] = game.scene.getEnemyField();
|
||||
game.field.mockAbility(enemy2, AbilityId.LIGHTNING_ROD);
|
||||
|
||||
enemy2.summonData.ability = AbilityId.LIGHTNING_ROD;
|
||||
|
||||
game.move.select(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY);
|
||||
game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||
game.move.use(MoveId.TACKLE, BattlerIndex.PLAYER, BattlerIndex.ENEMY);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy1.isFullHp()).toBe(true);
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
@ -24,29 +22,28 @@ describe("Abilities - Mold Breaker", () => {
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
.moveset([MoveId.SPLASH])
|
||||
.ability(AbilityId.MOLD_BREAKER)
|
||||
.battleStyle("single")
|
||||
.criticalHits(false)
|
||||
.enemySpecies(SpeciesId.MAGIKARP)
|
||||
.enemyAbility(AbilityId.BALL_FETCH)
|
||||
.enemyAbility(AbilityId.STURDY)
|
||||
.enemyMoveset(MoveId.SPLASH);
|
||||
});
|
||||
|
||||
it("should turn off the ignore abilities arena variable after the user's move", async () => {
|
||||
game.override
|
||||
.enemyMoveset(MoveId.SPLASH)
|
||||
.ability(AbilityId.MOLD_BREAKER)
|
||||
.moveset([MoveId.ERUPTION])
|
||||
.startingLevel(100)
|
||||
.enemyLevel(2);
|
||||
await game.classicMode.startBattle([SpeciesId.MAGIKARP]);
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
await game.classicMode.startBattle([SpeciesId.PINSIR]);
|
||||
|
||||
expect(enemy.isFainted()).toBe(false);
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
|
||||
await game.phaseInterceptor.to("MoveEndPhase", true);
|
||||
expect(globalScene.arena.ignoreAbilities).toBe(false);
|
||||
const player = game.field.getPlayerPokemon();
|
||||
const enemy = game.field.getEnemyPokemon();
|
||||
|
||||
game.move.use(MoveId.X_SCISSOR);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase");
|
||||
|
||||
expect(game.scene.arena.ignoreAbilities).toBe(true);
|
||||
expect(game.scene.arena.ignoringEffectSource).toBe(player.getBattlerIndex());
|
||||
|
||||
await game.toEndOfTurn();
|
||||
expect(game.scene.arena.ignoreAbilities).toBe(false);
|
||||
expect(enemy.isFainted()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -65,8 +65,7 @@ describe("Abilities - Moxie", () => {
|
||||
|
||||
secondPokemon.hp = 1;
|
||||
|
||||
game.move.select(moveToUse);
|
||||
game.selectTarget(BattlerIndex.PLAYER_2);
|
||||
game.move.select(moveToUse, BattlerIndex.PLAYER_2);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
|
@ -96,16 +96,15 @@ describe("Abilities - Storm Drain", () => {
|
||||
});
|
||||
|
||||
it("should redirect moves changed to water type via ability", async () => {
|
||||
game.override.ability(AbilityId.LIQUID_VOICE).moveset(MoveId.PSYCHIC_NOISE);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS, SpeciesId.MAGIKARP]);
|
||||
game.override.ability(AbilityId.LIQUID_VOICE);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||
|
||||
const enemy1 = game.scene.getEnemyField()[0];
|
||||
const enemy2 = game.scene.getEnemyField()[1];
|
||||
|
||||
enemy2.summonData.ability = AbilityId.STORM_DRAIN;
|
||||
|
||||
game.move.select(MoveId.PSYCHIC_NOISE, BattlerIndex.PLAYER, BattlerIndex.ENEMY);
|
||||
game.move.select(MoveId.SPLASH, BattlerIndex.PLAYER_2);
|
||||
game.move.use(MoveId.HYPER_VOICE, BattlerIndex.PLAYER);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(enemy1.isFullHp()).toBe(true);
|
||||
|
@ -86,7 +86,7 @@ describe("Weather - Strong Winds", () => {
|
||||
const enemy = game.scene.getEnemyPokemon()!;
|
||||
enemy.hp = 1;
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(game.scene.arena.weather?.weatherType).toBeUndefined();
|
||||
|
@ -58,8 +58,7 @@ describe("Items - Dire Hit", () => {
|
||||
|
||||
await game.classicMode.startBattle([SpeciesId.PIKACHU]);
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.doKillOpponents();
|
||||
|
||||
await game.phaseInterceptor.to(BattleEndPhase);
|
||||
|
@ -4,7 +4,6 @@ import type Move from "#app/data/moves/move";
|
||||
import { allMoves } from "#app/data/data-lists";
|
||||
import { ArenaTagType } from "#app/enums/arena-tag-type";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { TurnEndPhase } from "#app/phases/turn-end-phase";
|
||||
import { NumberHolder } from "#app/utils/common";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
@ -12,7 +11,7 @@ import { SpeciesId } from "#enums/species-id";
|
||||
import { WeatherType } from "#enums/weather-type";
|
||||
import GameManager from "#test/testUtils/gameManager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
let globalScene: BattleScene;
|
||||
|
||||
@ -52,10 +51,10 @@ describe("Moves - Aurora Veil", () => {
|
||||
|
||||
game.move.select(moveToUse);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.toEndOfTurn();
|
||||
const mockedDmg = getMockedMoveDamage(
|
||||
game.scene.getEnemyPokemon()!,
|
||||
game.scene.getPlayerPokemon()!,
|
||||
game.field.getEnemyPokemon(),
|
||||
game.field.getPlayerPokemon(),
|
||||
allMoves[moveToUse],
|
||||
);
|
||||
|
||||
@ -71,10 +70,10 @@ describe("Moves - Aurora Veil", () => {
|
||||
game.move.select(moveToUse);
|
||||
game.move.select(moveToUse, 1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.toEndOfTurn();
|
||||
const mockedDmg = getMockedMoveDamage(
|
||||
game.scene.getEnemyPokemon()!,
|
||||
game.scene.getPlayerPokemon()!,
|
||||
game.field.getEnemyPokemon(),
|
||||
game.field.getPlayerPokemon(),
|
||||
allMoves[moveToUse],
|
||||
);
|
||||
|
||||
@ -82,72 +81,48 @@ describe("Moves - Aurora Veil", () => {
|
||||
});
|
||||
|
||||
it("reduces damage of special attacks by half in a single battle", async () => {
|
||||
const moveToUse = MoveId.ABSORB;
|
||||
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
game.move.use(MoveId.ABSORB);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
const mockedDmg = getMockedMoveDamage(
|
||||
game.scene.getEnemyPokemon()!,
|
||||
game.scene.getPlayerPokemon()!,
|
||||
allMoves[moveToUse],
|
||||
game.field.getEnemyPokemon(),
|
||||
game.field.getPlayerPokemon(),
|
||||
allMoves[MoveId.ABSORB],
|
||||
);
|
||||
|
||||
expect(mockedDmg).toBe(allMoves[moveToUse].power * singleBattleMultiplier);
|
||||
expect(mockedDmg).toBe(allMoves[MoveId.ABSORB].power * singleBattleMultiplier);
|
||||
});
|
||||
|
||||
it("reduces damage of special attacks by a third in a double battle", async () => {
|
||||
game.override.battleStyle("double");
|
||||
|
||||
const moveToUse = MoveId.DAZZLING_GLEAM;
|
||||
await game.classicMode.startBattle([SpeciesId.SHUCKLE, SpeciesId.SHUCKLE]);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
game.move.select(moveToUse, 1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
const mockedDmg = getMockedMoveDamage(
|
||||
game.scene.getEnemyPokemon()!,
|
||||
game.scene.getPlayerPokemon()!,
|
||||
allMoves[moveToUse],
|
||||
);
|
||||
|
||||
expect(mockedDmg).toBe(allMoves[moveToUse].power * doubleBattleMultiplier);
|
||||
});
|
||||
|
||||
it("does not affect physical critical hits", async () => {
|
||||
game.override.moveset([MoveId.WICKED_BLOW]);
|
||||
const moveToUse = MoveId.WICKED_BLOW;
|
||||
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
|
||||
game.move.use(MoveId.ABSORB);
|
||||
await game.toEndOfTurn();
|
||||
const mockedDmg = getMockedMoveDamage(
|
||||
game.scene.getEnemyPokemon()!,
|
||||
game.scene.getPlayerPokemon()!,
|
||||
allMoves[moveToUse],
|
||||
game.field.getEnemyPokemon(),
|
||||
game.field.getPlayerPokemon(),
|
||||
allMoves[MoveId.ABSORB],
|
||||
);
|
||||
expect(mockedDmg).toBe(allMoves[moveToUse].power);
|
||||
|
||||
expect(mockedDmg).toBe(allMoves[MoveId.ABSORB].power * doubleBattleMultiplier);
|
||||
});
|
||||
|
||||
it("does not affect critical hits", async () => {
|
||||
game.override.moveset([MoveId.FROST_BREATH]);
|
||||
const moveToUse = MoveId.FROST_BREATH;
|
||||
vi.spyOn(allMoves[MoveId.FROST_BREATH], "accuracy", "get").mockReturnValue(100);
|
||||
await game.classicMode.startBattle([SpeciesId.SHUCKLE]);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
game.move.use(MoveId.WICKED_BLOW);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
const mockedDmg = getMockedMoveDamage(
|
||||
game.scene.getEnemyPokemon()!,
|
||||
game.scene.getPlayerPokemon()!,
|
||||
allMoves[moveToUse],
|
||||
game.field.getEnemyPokemon(),
|
||||
game.field.getPlayerPokemon(),
|
||||
allMoves[MoveId.WICKED_BLOW],
|
||||
);
|
||||
expect(mockedDmg).toBe(allMoves[moveToUse].power);
|
||||
expect(mockedDmg).toBe(allMoves[MoveId.WICKED_BLOW].power);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -33,7 +33,7 @@ describe("Moves - Baddy Bad", () => {
|
||||
game.override.enemyMoveset(MoveId.PROTECT);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||
|
||||
game.move.select(MoveId.BADDY_BAD);
|
||||
game.move.use(MoveId.BADDY_BAD);
|
||||
await game.phaseInterceptor.to("BerryPhase");
|
||||
|
||||
expect(game.scene.arena.tags.length).toBe(0);
|
||||
|
@ -100,12 +100,12 @@ describe("Moves - Gastro Acid", () => {
|
||||
await game.toNextTurn();
|
||||
expect(enemyPokemon.summonData.abilitySuppressed).toBe(true);
|
||||
|
||||
game.move.select(MoveId.WATER_GUN);
|
||||
game.move.use(MoveId.WATER_GUN);
|
||||
await game.toNextTurn();
|
||||
// water gun should've dealt damage due to suppressed Water Absorb
|
||||
expect(enemyPokemon.hp).toBe(enemyPokemon.getMaxHp());
|
||||
|
||||
game.move.select(MoveId.SPORE);
|
||||
game.move.use(MoveId.SPORE);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
// Comatose should block stauts effect
|
||||
|
@ -349,7 +349,7 @@ describe("Moves - Instruct", () => {
|
||||
useMode: MoveUseMode.NORMAL,
|
||||
});
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.setTurnOrder([BattlerIndex.ENEMY, BattlerIndex.PLAYER]);
|
||||
await game.toEndOfTurn();
|
||||
expect(game.field.getEnemyPokemon().getLastXMoves()[0].result).toBe(MoveResult.FAIL);
|
||||
|
@ -30,30 +30,26 @@ describe("Moves - Reflect Type", () => {
|
||||
});
|
||||
|
||||
it("will make the user Normal/Grass if targetting a typeless Pokemon affected by Forest's Curse", async () => {
|
||||
game.override
|
||||
.moveset([MoveId.FORESTS_CURSE, MoveId.REFLECT_TYPE])
|
||||
.startingLevel(60)
|
||||
.enemySpecies(SpeciesId.CHARMANDER)
|
||||
.enemyMoveset([MoveId.BURN_UP, MoveId.SPLASH]);
|
||||
game.override.startingLevel(60).enemySpecies(SpeciesId.CHARMANDER);
|
||||
await game.classicMode.startBattle([SpeciesId.FEEBAS]);
|
||||
|
||||
const playerPokemon = game.scene.getPlayerPokemon();
|
||||
const enemyPokemon = game.scene.getEnemyPokemon();
|
||||
const playerPokemon = game.field.getPlayerPokemon();
|
||||
const enemyPokemon = game.field.getEnemyPokemon();
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.move.selectEnemyMove(MoveId.BURN_UP);
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.move.forceEnemyMove(MoveId.BURN_UP);
|
||||
await game.toNextTurn();
|
||||
|
||||
game.move.select(MoveId.FORESTS_CURSE);
|
||||
await game.move.selectEnemyMove(MoveId.SPLASH);
|
||||
game.move.use(MoveId.FORESTS_CURSE);
|
||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||
await game.toNextTurn();
|
||||
expect(enemyPokemon?.getTypes().includes(PokemonType.UNKNOWN)).toBe(true);
|
||||
expect(enemyPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true);
|
||||
expect(enemyPokemon.getTypes().includes(PokemonType.UNKNOWN)).toBe(true);
|
||||
expect(enemyPokemon.getTypes().includes(PokemonType.GRASS)).toBe(true);
|
||||
|
||||
game.move.select(MoveId.REFLECT_TYPE);
|
||||
await game.move.selectEnemyMove(MoveId.SPLASH);
|
||||
game.move.use(MoveId.REFLECT_TYPE);
|
||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
expect(playerPokemon?.getTypes()[0]).toBe(PokemonType.NORMAL);
|
||||
expect(playerPokemon?.getTypes().includes(PokemonType.GRASS)).toBe(true);
|
||||
expect(playerPokemon.getTypes()[0]).toBe(PokemonType.NORMAL);
|
||||
expect(playerPokemon.getTypes().includes(PokemonType.GRASS)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
@ -82,10 +82,10 @@ describe("Moves - Spikes", () => {
|
||||
|
||||
it("should work when all targets fainted", async () => {
|
||||
game.override.enemySpecies(SpeciesId.DIGLETT).battleStyle("double").startingLevel(50);
|
||||
await game.classicMode.startBattle([SpeciesId.RAYQUAZA, SpeciesId.ROWLET]);
|
||||
await game.classicMode.startBattle([SpeciesId.RAYQUAZA]);
|
||||
|
||||
game.move.select(MoveId.EARTHQUAKE);
|
||||
game.move.select(MoveId.SPIKES, 1);
|
||||
game.move.use(MoveId.SPIKES);
|
||||
await game.doKillOpponents();
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(game.scene.arena.getTagOnSide(ArenaTrapTag, ArenaTagSide.ENEMY)).toBeDefined();
|
||||
|
@ -201,9 +201,8 @@ export default class GameManager {
|
||||
/**
|
||||
* Helper function to run to the final boss encounter as it's a bit tricky due to extra dialogue
|
||||
* Also handles Major/Minor bosses from endless modes
|
||||
* @param game - The game manager
|
||||
* @param species
|
||||
* @param mode
|
||||
* @param species - Array of {@linkcode SpeciesId}s to start the final battle with.
|
||||
* @param mode - The {@linkcode GameModes} to spawn the final boss encounter in.
|
||||
*/
|
||||
async runToFinalBossEncounter(species: SpeciesId[], mode: GameModes) {
|
||||
console.log("===to final boss encounter===");
|
||||
@ -230,9 +229,9 @@ export default class GameManager {
|
||||
|
||||
/**
|
||||
* Runs the game to a mystery encounter phase.
|
||||
* @param encounterType if specified, will expect encounter to have been spawned
|
||||
* @param species Optional array of species for party.
|
||||
* @returns A promise that resolves when the EncounterPhase ends.
|
||||
* @param encounterType - If specified, will expect encounter to be the given type.
|
||||
* @param species - Optional array of species for party to start with.
|
||||
* @returns A Promise that resolves when the EncounterPhase ends.
|
||||
*/
|
||||
async runToMysteryEncounter(encounterType?: MysteryEncounterType, species?: SpeciesId[]) {
|
||||
if (!isNullOrUndefined(encounterType)) {
|
||||
@ -277,6 +276,7 @@ export default class GameManager {
|
||||
* Will trigger during the next {@linkcode SelectTargetPhase}
|
||||
* @param targetIndex - The {@linkcode BattlerIndex} of the attack target, or `undefined` for multi-target attacks
|
||||
* @param movePosition - The 0-indexed position of the move in the pokemon's moveset array
|
||||
* @throws Immediately fails tests
|
||||
*/
|
||||
selectTarget(movePosition: number, targetIndex?: BattlerIndex) {
|
||||
this.onNextPrompt(
|
||||
@ -292,7 +292,7 @@ export default class GameManager {
|
||||
handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY);
|
||||
}
|
||||
if (move.isMultiTarget() && targetIndex !== undefined) {
|
||||
throw new Error(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`);
|
||||
expect.fail(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`);
|
||||
}
|
||||
handler.processInput(Button.ACTION);
|
||||
},
|
||||
|
@ -10,7 +10,7 @@ import { getGameMode } from "#app/game-mode";
|
||||
import { GameModes } from "#enums/game-modes";
|
||||
import type { StarterMoveset } from "#app/system/game-data";
|
||||
import type { Starter } from "#app/ui/starter-select-ui-handler";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import type { MoveId } from "#enums/move-id";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
|
||||
/** Function to convert Blob to string */
|
||||
@ -98,15 +98,6 @@ export function waitUntil(truth): Promise<unknown> {
|
||||
});
|
||||
}
|
||||
|
||||
/** Get the index of `move` from the moveset of the pokemon on the player's field at location `pokemonIndex`. */
|
||||
export function getMovePosition(scene: BattleScene, pokemonIndex: 0 | 1, move: MoveId): number {
|
||||
const playerPokemon = scene.getPlayerField()[pokemonIndex];
|
||||
const moveSet = playerPokemon.getMoveset();
|
||||
const index = moveSet.findIndex(m => m.moveId === move && m.ppUsed < m.getMovePp());
|
||||
console.log(`Move position for ${MoveId[move]} (=${move}):`, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Useful for populating party, wave index, etc. without having to spin up and run through an entire EncounterPhase
|
||||
*/
|
||||
|
@ -1,4 +1,4 @@
|
||||
import type { BattlerIndex } from "#enums/battler-index";
|
||||
import { BattlerIndex } from "#enums/battler-index";
|
||||
import { getMoveTargets } from "#app/data/moves/move-utils";
|
||||
import type Pokemon from "#app/field/pokemon";
|
||||
import { PokemonMove } from "#app/data/moves/pokemon-move";
|
||||
@ -9,14 +9,13 @@ import { MoveEffectPhase } from "#app/phases/move-effect-phase";
|
||||
import { Command } from "#enums/command";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { getMovePosition } from "#test/testUtils/gameManagerUtils";
|
||||
import { GameManagerHelper } from "#test/testUtils/helpers/gameManagerHelper";
|
||||
import { vi } from "vitest";
|
||||
import { coerceArray } from "#app/utils/common";
|
||||
import { expect, vi } from "vitest";
|
||||
import { coerceArray, toReadableString } from "#app/utils/common";
|
||||
import { MoveUseMode } from "#enums/move-use-mode";
|
||||
|
||||
/**
|
||||
* Helper to handle a Pokemon's move
|
||||
* Helper to handle using a Pokemon's moves.
|
||||
*/
|
||||
export class MoveHelper extends GameManagerHelper {
|
||||
/**
|
||||
@ -49,13 +48,31 @@ export class MoveHelper extends GameManagerHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the move to be used by the given Pokemon(-index). Triggers during the next {@linkcode CommandPhase}
|
||||
* @param move - the move to use
|
||||
* @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0)
|
||||
* @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required
|
||||
* Select a move _already in the player's moveset_ to be used during the next {@linkcode CommandPhase}.
|
||||
* @param move - The {@linkcode MoveId} to use.
|
||||
* @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified.
|
||||
* @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves.
|
||||
* If set to `null`, will forgo normal target selection entirely (useful for UI tests).
|
||||
* @remarks
|
||||
* Will fail the current test if the move being selected is not in the user's moveset.
|
||||
*/
|
||||
public select(move: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) {
|
||||
const movePosition = getMovePosition(this.game.scene, pkmIndex, move);
|
||||
public select(
|
||||
move: MoveId,
|
||||
pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER,
|
||||
targetIndex?: BattlerIndex | null,
|
||||
) {
|
||||
const movePosition = this.getMovePosition(pkmIndex, move);
|
||||
if (movePosition === -1) {
|
||||
expect.fail(
|
||||
`MoveHelper.selectWithTera called with move '${toReadableString(MoveId[move])}' not in moveset!
|
||||
Battler Index: ${toReadableString(BattlerIndex[pkmIndex])};
|
||||
Moveset: [${this.game.scene
|
||||
.getPlayerParty()
|
||||
[pkmIndex].getMoveset()
|
||||
.map(pm => toReadableString(MoveId[pm.moveId]))
|
||||
.join(", ")}]`,
|
||||
);
|
||||
}
|
||||
|
||||
this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => {
|
||||
this.game.scene.ui.setMode(
|
||||
@ -77,14 +94,30 @@ export class MoveHelper extends GameManagerHelper {
|
||||
}
|
||||
|
||||
/**
|
||||
* Select the move to be used by the given Pokemon(-index), **which will also terastallize on this turn**.
|
||||
* Triggers during the next {@linkcode CommandPhase}
|
||||
* @param move - the move to use
|
||||
* @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0)
|
||||
* @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required
|
||||
* Select a move _already in the player's moveset_ to be used during the next {@linkcode CommandPhase}, **which will also terastallize on this turn**.
|
||||
* @param move - The {@linkcode MoveId} to use.
|
||||
* @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified.
|
||||
* @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves.
|
||||
* If set to `null`, will forgo normal target selection entirely (useful for UI tests)
|
||||
*/
|
||||
public selectWithTera(move: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null) {
|
||||
const movePosition = getMovePosition(this.game.scene, pkmIndex, move);
|
||||
public selectWithTera(
|
||||
move: MoveId,
|
||||
pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER,
|
||||
targetIndex?: BattlerIndex | null,
|
||||
) {
|
||||
const movePosition = this.getMovePosition(pkmIndex, move);
|
||||
if (movePosition === -1) {
|
||||
expect.fail(
|
||||
`MoveHelper.selectWithTera called with move '${toReadableString(MoveId[move])}' not in moveset!
|
||||
Battler Index: ${toReadableString(BattlerIndex[pkmIndex])};
|
||||
Moveset: [${this.game.scene
|
||||
.getPlayerParty()
|
||||
[pkmIndex].getMoveset()
|
||||
.map(pm => toReadableString(MoveId[pm.moveId]))
|
||||
.join(", ")}]`,
|
||||
);
|
||||
}
|
||||
|
||||
this.game.scene.getPlayerParty()[pkmIndex].isTerastallized = false;
|
||||
|
||||
this.game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => {
|
||||
@ -107,6 +140,15 @@ export class MoveHelper extends GameManagerHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/** Helper function to get the index of the selected move in the selected part member's moveset. */
|
||||
private getMovePosition(pokemonIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2, move: MoveId): number {
|
||||
const playerPokemon = this.game.scene.getPlayerField()[pokemonIndex];
|
||||
const moveset = playerPokemon.getMoveset();
|
||||
const index = moveset.findIndex(m => m.moveId === move && m.ppUsed < m.getMovePp());
|
||||
console.log(`Move position for ${MoveId[move]} (=${move}):`, index);
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modifies a player pokemon's moveset to contain only the selected move and then
|
||||
* selects it to be used during the next {@linkcode CommandPhase}.
|
||||
@ -116,14 +158,19 @@ export class MoveHelper extends GameManagerHelper {
|
||||
* Note: If you need to check for changes in the player's moveset as part of the test, it may be
|
||||
* best to use {@linkcode changeMoveset} and {@linkcode select} instead.
|
||||
* @param moveId - the move to use
|
||||
* @param pkmIndex - the pokemon index. Relevant for double-battles only (defaults to 0)
|
||||
* @param targetIndex - (optional) The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves, or `null` if a manual call to `selectTarget()` is required
|
||||
* @param useTera - If `true`, the Pokemon also chooses to Terastallize. This does not require a Tera Orb. Default: `false`.
|
||||
* @param pkmIndex - The {@linkcode BattlerIndex} of the player Pokemon using the move. Relevant for double battles only and defaults to {@linkcode BattlerIndex.PLAYER} if not specified.
|
||||
* @param targetIndex - The {@linkcode BattlerIndex} of the Pokemon to target for single-target moves; should be omitted for multi-target moves.
|
||||
* @param useTera - If `true`, the Pokemon will attempt to Terastallize even without a Tera Orb; default `false`.
|
||||
*/
|
||||
public use(moveId: MoveId, pkmIndex: 0 | 1 = 0, targetIndex?: BattlerIndex | null, useTera = false): void {
|
||||
public use(
|
||||
moveId: MoveId,
|
||||
pkmIndex: BattlerIndex.PLAYER | BattlerIndex.PLAYER_2 = BattlerIndex.PLAYER,
|
||||
targetIndex?: BattlerIndex,
|
||||
useTera = false,
|
||||
): void {
|
||||
if ([Overrides.MOVESET_OVERRIDE].flat().length > 0) {
|
||||
vi.spyOn(Overrides, "MOVESET_OVERRIDE", "get").mockReturnValue([]);
|
||||
console.warn("Warning: `use` overwrites the Pokemon's moveset and disables the player moveset override!");
|
||||
console.warn("Warning: `MoveHelper.use` overwriting player pokemon moveset and disabling moveset override!");
|
||||
}
|
||||
|
||||
const pokemon = this.game.scene.getPlayerField()[pkmIndex];
|
||||
|
Loading…
Reference in New Issue
Block a user