mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-14 11:29:28 +02:00
Compare commits
17 Commits
936268943b
...
13a887ed93
Author | SHA1 | Date | |
---|---|---|---|
|
13a887ed93 | ||
|
6c0253ada4 | ||
|
4fa130ad9f | ||
|
907e707b8b | ||
|
8e729d9955 | ||
|
62aea1ad8d | ||
|
99a5f0d9ba | ||
|
69fff96089 | ||
|
675139e3ba | ||
|
d10b1b0a13 | ||
|
3114c1743f | ||
|
0c03cf1df6 | ||
|
571691b19a | ||
|
6dfddea403 | ||
|
5bfcca3b53 | ||
|
5d1e13139c | ||
|
f76d12f430 |
@ -3,7 +3,7 @@ import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("{{description}}", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
@ -15,10 +15,6 @@ describe("{{description}}", () => {
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
game.phaseInterceptor.restoreOg();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
game.override
|
||||
|
@ -397,7 +397,11 @@ export class BattleScene extends SceneBase {
|
||||
});
|
||||
}
|
||||
|
||||
create() {
|
||||
/**
|
||||
* Create game objects with loaded assets.
|
||||
* Called by Phaser on new game start.
|
||||
*/
|
||||
create(): void {
|
||||
this.scene.remove(LoadingScene.KEY);
|
||||
initGameSpeed.apply(this);
|
||||
this.inputController = new InputsController();
|
||||
@ -422,6 +426,7 @@ export class BattleScene extends SceneBase {
|
||||
this.ui?.update();
|
||||
}
|
||||
|
||||
// TODO: Split this up into multiple sub-methods
|
||||
launchBattle() {
|
||||
this.arenaBg = this.add.sprite(0, 0, "plains_bg");
|
||||
this.arenaBg.setName("sprite-arena-bg");
|
||||
@ -602,6 +607,8 @@ export class BattleScene extends SceneBase {
|
||||
this.arenaNextEnemy.setVisible(false);
|
||||
|
||||
for (const a of [this.arenaPlayer, this.arenaPlayerTransition, this.arenaEnemy, this.arenaNextEnemy]) {
|
||||
// TODO: This seems questionable - we just initialized the arena sprites and then have to manually check if they're a sprite?
|
||||
// This is likely the result of either extreme laziness or confusion
|
||||
if (a instanceof Phaser.GameObjects.Sprite) {
|
||||
a.setOrigin(0, 0);
|
||||
}
|
||||
@ -1143,6 +1150,7 @@ export class BattleScene extends SceneBase {
|
||||
return this.currentBattle?.randSeedInt(range, min);
|
||||
}
|
||||
|
||||
// TODO: Break up function - this does far too much in 1 sitting
|
||||
reset(clearScene = false, clearData = false, reloadI18n = false): void {
|
||||
if (clearData) {
|
||||
this.gameData = new GameData();
|
||||
|
@ -5,10 +5,9 @@ import type { PokemonSpeciesForm } from "#data/pokemon-species";
|
||||
import { PokemonSpecies } from "#data/pokemon-species";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import { PartyMemberStrength } from "#enums/party-member-strength";
|
||||
import type { SpeciesId } from "#enums/species-id";
|
||||
import { PlayerPokemon } from "#field/pokemon";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import type { Starter } from "#ui/starter-select-ui-handler";
|
||||
import { randSeedGauss, randSeedInt, randSeedItem } from "#utils/common";
|
||||
import { isNullOrUndefined, randSeedGauss, randSeedInt, randSeedItem } from "#utils/common";
|
||||
import { getEnumValues } from "#utils/enums";
|
||||
import { getPokemonSpecies, getPokemonSpeciesForm } from "#utils/pokemon-utils";
|
||||
|
||||
@ -32,15 +31,9 @@ export function getDailyRunStarters(seed: string): Starter[] {
|
||||
() => {
|
||||
const startingLevel = globalScene.gameMode.getStartingLevel();
|
||||
|
||||
if (/\d{18}$/.test(seed)) {
|
||||
for (let s = 0; s < 3; s++) {
|
||||
const offset = 6 + s * 6;
|
||||
const starterSpeciesForm = getPokemonSpeciesForm(
|
||||
Number.parseInt(seed.slice(offset, offset + 4)) as SpeciesId,
|
||||
Number.parseInt(seed.slice(offset + 4, offset + 6)),
|
||||
);
|
||||
starters.push(getDailyRunStarter(starterSpeciesForm, startingLevel));
|
||||
}
|
||||
const eventStarters = getDailyEventSeedStarters(seed);
|
||||
if (!isNullOrUndefined(eventStarters)) {
|
||||
starters.push(...eventStarters);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -72,18 +65,7 @@ function getDailyRunStarter(starterSpeciesForm: PokemonSpeciesForm, startingLeve
|
||||
const starterSpecies =
|
||||
starterSpeciesForm instanceof PokemonSpecies ? starterSpeciesForm : getPokemonSpecies(starterSpeciesForm.speciesId);
|
||||
const formIndex = starterSpeciesForm instanceof PokemonSpecies ? undefined : starterSpeciesForm.formIndex;
|
||||
const pokemon = new PlayerPokemon(
|
||||
starterSpecies,
|
||||
startingLevel,
|
||||
undefined,
|
||||
formIndex,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
);
|
||||
const pokemon = globalScene.addPlayerPokemon(starterSpecies, startingLevel, undefined, formIndex);
|
||||
const starter: Starter = {
|
||||
species: starterSpecies,
|
||||
dexAttr: pokemon.getDexAttr(),
|
||||
@ -145,6 +127,11 @@ const dailyBiomeWeights: BiomeWeights = {
|
||||
};
|
||||
|
||||
export function getDailyStartingBiome(): BiomeId {
|
||||
const eventBiome = getDailyEventSeedBiome(globalScene.seed);
|
||||
if (!isNullOrUndefined(eventBiome)) {
|
||||
return eventBiome;
|
||||
}
|
||||
|
||||
const biomes = getEnumValues(BiomeId).filter(b => b !== BiomeId.TOWN && b !== BiomeId.END);
|
||||
|
||||
let totalWeight = 0;
|
||||
@ -169,3 +156,126 @@ export function getDailyStartingBiome(): BiomeId {
|
||||
// TODO: should this use `randSeedItem`?
|
||||
return biomes[randSeedInt(biomes.length)];
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is Daily Mode and the seed is longer than a default seed
|
||||
* then it has been modified and could contain a custom event seed. \
|
||||
* Default seeds are always exactly 24 characters.
|
||||
* @returns `true` if it is a Daily Event Seed.
|
||||
*/
|
||||
export function isDailyEventSeed(seed: string): boolean {
|
||||
return globalScene.gameMode.isDaily && seed.length > 24;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects the seed to contain `/starters\d{18}/`
|
||||
* where the digits alternate between 4 digits for the species ID and 2 digits for the form index
|
||||
* (left padded with `0`s as necessary).
|
||||
* @returns An array of {@linkcode Starter}s, or `null` if no valid match.
|
||||
*/
|
||||
export function getDailyEventSeedStarters(seed: string): Starter[] | null {
|
||||
if (!isDailyEventSeed(seed)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const starters: Starter[] = [];
|
||||
const match = /starters(\d{4})(\d{2})(\d{4})(\d{2})(\d{4})(\d{2})/g.exec(seed);
|
||||
|
||||
if (!match || match.length !== 7) {
|
||||
return null;
|
||||
}
|
||||
|
||||
for (let i = 1; i < match.length; i += 2) {
|
||||
const speciesId = Number.parseInt(match[i]) as SpeciesId;
|
||||
const formIndex = Number.parseInt(match[i + 1]);
|
||||
|
||||
if (!getEnumValues(SpeciesId).includes(speciesId)) {
|
||||
console.warn("Invalid species ID used for custom daily run seed starter:", speciesId);
|
||||
return null;
|
||||
}
|
||||
|
||||
const starterForm = getPokemonSpeciesForm(speciesId, formIndex);
|
||||
const startingLevel = globalScene.gameMode.getStartingLevel();
|
||||
const starter = getDailyRunStarter(starterForm, startingLevel);
|
||||
starters.push(starter);
|
||||
}
|
||||
|
||||
return starters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects the seed to contain `/boss\d{4}\d{2}/`
|
||||
* where the first 4 digits are the species ID and the next 2 digits are the form index
|
||||
* (left padded with `0`s as necessary).
|
||||
* @returns A {@linkcode PokemonSpeciesForm} to be used for the boss, or `null` if no valid match.
|
||||
*/
|
||||
export function getDailyEventSeedBoss(seed: string): PokemonSpeciesForm | null {
|
||||
if (!isDailyEventSeed(seed)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = /boss(\d{4})(\d{2})/g.exec(seed);
|
||||
if (!match || match.length !== 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const speciesId = Number.parseInt(match[1]) as SpeciesId;
|
||||
const formIndex = Number.parseInt(match[2]);
|
||||
|
||||
if (!getEnumValues(SpeciesId).includes(speciesId)) {
|
||||
console.warn("Invalid species ID used for custom daily run seed boss:", speciesId);
|
||||
return null;
|
||||
}
|
||||
|
||||
const starterForm = getPokemonSpeciesForm(speciesId, formIndex);
|
||||
return starterForm;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects the seed to contain `/biome\d{2}/` where the 2 digits are a biome ID (left padded with `0` if necessary).
|
||||
* @returns The biome to use or `null` if no valid match.
|
||||
*/
|
||||
export function getDailyEventSeedBiome(seed: string): BiomeId | null {
|
||||
if (!isDailyEventSeed(seed)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = /biome(\d{2})/g.exec(seed);
|
||||
if (!match || match.length !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const startingBiome = Number.parseInt(match[1]) as BiomeId;
|
||||
|
||||
if (!getEnumValues(BiomeId).includes(startingBiome)) {
|
||||
console.warn("Invalid biome ID used for custom daily run seed:", startingBiome);
|
||||
return null;
|
||||
}
|
||||
|
||||
return startingBiome;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expects the seed to contain `/luck\d{2}/` where the 2 digits are a number between `0` and `14`
|
||||
* (left padded with `0` if necessary).
|
||||
* @returns The custom luck value or `null` if no valid match.
|
||||
*/
|
||||
export function getDailyEventSeedLuck(seed: string): number | null {
|
||||
if (!isDailyEventSeed(seed)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const match = /luck(\d{2})/g.exec(seed);
|
||||
if (!match || match.length !== 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const luck = Number.parseInt(match[1]);
|
||||
|
||||
if (luck < 0 || luck > 14) {
|
||||
console.warn("Invalid luck value used for custom daily run seed:", luck);
|
||||
return null;
|
||||
}
|
||||
|
||||
return luck;
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ import {
|
||||
TrappedTag,
|
||||
TypeImmuneTag,
|
||||
} from "#data/battler-tags";
|
||||
import { getDailyEventSeedBoss } from "#data/daily-run";
|
||||
import { allAbilities, allMoves } from "#data/data-lists";
|
||||
import { getLevelTotalExp } from "#data/exp";
|
||||
import {
|
||||
@ -6256,6 +6257,11 @@ export class EnemyPokemon extends Pokemon {
|
||||
this.species.forms[Overrides.OPP_FORM_OVERRIDES[speciesId]]
|
||||
) {
|
||||
this.formIndex = Overrides.OPP_FORM_OVERRIDES[speciesId];
|
||||
} else if (globalScene.gameMode.isDaily && globalScene.gameMode.isWaveFinal(globalScene.currentBattle.waveIndex)) {
|
||||
const eventBoss = getDailyEventSeedBoss(globalScene.seed);
|
||||
if (!isNullOrUndefined(eventBoss)) {
|
||||
this.formIndex = eventBoss.formIndex;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dataSource) {
|
||||
|
@ -3,7 +3,7 @@ import { CHALLENGE_MODE_MYSTERY_ENCOUNTER_WAVES, CLASSIC_MODE_MYSTERY_ENCOUNTER_
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import Overrides from "#app/overrides";
|
||||
import { allChallenges, type Challenge, copyChallenge } from "#data/challenge";
|
||||
import { getDailyStartingBiome } from "#data/daily-run";
|
||||
import { getDailyEventSeedBoss, getDailyStartingBiome } from "#data/daily-run";
|
||||
import { allSpecies } from "#data/data-lists";
|
||||
import type { PokemonSpecies } from "#data/pokemon-species";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
@ -15,6 +15,7 @@ import type { Arena } from "#field/arena";
|
||||
import { classicFixedBattles, type FixedBattleConfigs } from "#trainers/fixed-battle-configs";
|
||||
import { applyChallenges } from "#utils/challenge-utils";
|
||||
import { BooleanHolder, isNullOrUndefined, randSeedInt, randSeedItem } from "#utils/common";
|
||||
import { getPokemonSpecies } from "#utils/pokemon-utils";
|
||||
import i18next from "i18next";
|
||||
|
||||
interface GameModeConfig {
|
||||
@ -211,6 +212,12 @@ export class GameMode implements GameModeConfig {
|
||||
|
||||
getOverrideSpecies(waveIndex: number): PokemonSpecies | null {
|
||||
if (this.isDaily && this.isWaveFinal(waveIndex)) {
|
||||
const eventBoss = getDailyEventSeedBoss(globalScene.seed);
|
||||
if (!isNullOrUndefined(eventBoss)) {
|
||||
// Cannot set form index here, it will be overriden when adding it as enemy pokemon.
|
||||
return getPokemonSpecies(eventBoss.speciesId);
|
||||
}
|
||||
|
||||
const allFinalBossSpecies = allSpecies.filter(
|
||||
s =>
|
||||
(s.subLegendary || s.legendary || s.mythical) &&
|
||||
|
@ -6,6 +6,7 @@ import Overrides from "#app/overrides";
|
||||
import { EvolutionItem, pokemonEvolutions } from "#balance/pokemon-evolutions";
|
||||
import { tmPoolTiers, tmSpecies } from "#balance/tms";
|
||||
import { getBerryEffectDescription, getBerryName } from "#data/berry";
|
||||
import { getDailyEventSeedLuck } from "#data/daily-run";
|
||||
import { allMoves, modifierTypes } from "#data/data-lists";
|
||||
import { SpeciesFormChangeItemTrigger } from "#data/form-change-triggers";
|
||||
import { getNatureName, getNatureStatMultiplier } from "#data/nature";
|
||||
@ -2921,6 +2922,12 @@ export function getPartyLuckValue(party: Pokemon[]): number {
|
||||
const DailyLuck = new NumberHolder(0);
|
||||
globalScene.executeWithSeedOffset(
|
||||
() => {
|
||||
const eventLuck = getDailyEventSeedLuck(globalScene.seed);
|
||||
if (!isNullOrUndefined(eventLuck)) {
|
||||
DailyLuck.value = eventLuck;
|
||||
return;
|
||||
}
|
||||
|
||||
DailyLuck.value = randSeedInt(15); // Random number between 0 and 14
|
||||
},
|
||||
0,
|
||||
@ -2928,6 +2935,7 @@ export function getPartyLuckValue(party: Pokemon[]): number {
|
||||
);
|
||||
return DailyLuck.value;
|
||||
}
|
||||
|
||||
const eventSpecies = timedEventManager.getEventLuckBoostedSpecies();
|
||||
const luck = Phaser.Math.Clamp(
|
||||
party
|
||||
|
@ -370,6 +370,9 @@ export class PhaseManager {
|
||||
}
|
||||
|
||||
this.currentPhase = this.phaseQueue.shift() ?? null;
|
||||
if (!this.currentPhase) {
|
||||
throw new Error("No phases in queue; aborting");
|
||||
}
|
||||
|
||||
const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
|
||||
// Check if there are any conditional phases queued
|
||||
@ -389,12 +392,26 @@ export class PhaseManager {
|
||||
}
|
||||
this.conditionalQueue.push(...unactivatedConditionalPhases);
|
||||
|
||||
if (this.currentPhase) {
|
||||
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
||||
this.currentPhase.start();
|
||||
}
|
||||
this.startCurrentPhase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to start and log the current phase.
|
||||
*
|
||||
* @remarks
|
||||
* This is disabled during tests by `phase-interceptor.ts` to allow for pausing execution at specific phases.
|
||||
* As such, **do not remove or split this method** as it will break integration tests.
|
||||
*/
|
||||
private startCurrentPhase(): void {
|
||||
if (!this.currentPhase) {
|
||||
console.warn("Trying to start null phase!");
|
||||
return;
|
||||
}
|
||||
console.log(`%cStart Phase ${this.currentPhase.phaseName}`, "color:green;");
|
||||
this.currentPhase.start();
|
||||
}
|
||||
|
||||
// TODO: Review if we can remove this
|
||||
overridePhase(phase: Phase): boolean {
|
||||
if (this.standbyPhase) {
|
||||
return false;
|
||||
@ -402,8 +419,7 @@ export class PhaseManager {
|
||||
|
||||
this.standbyPhase = this.currentPhase;
|
||||
this.currentPhase = phase;
|
||||
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
|
||||
phase.start();
|
||||
this.startCurrentPhase();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ import { globalScene } from "#app/global-scene";
|
||||
import type { PhaseMap, PhaseString } from "#types/phase-types";
|
||||
|
||||
export abstract class Phase {
|
||||
/** Start the current phase. */
|
||||
start() {}
|
||||
|
||||
/** End the current phase and start a new one. */
|
||||
end() {
|
||||
globalScene.phaseManager.shiftPhase();
|
||||
}
|
||||
|
@ -3,9 +3,6 @@ import { BattlerIndex } from "#enums/battler-index";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { TurnEndPhase } from "#phases/turn-end-phase";
|
||||
import { VictoryPhase } from "#phases/victory-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
@ -46,7 +43,7 @@ describe("Abilities - Moxie", () => {
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase);
|
||||
await game.phaseInterceptor.to("VictoryPhase");
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
});
|
||||
@ -67,7 +64,7 @@ describe("Abilities - Moxie", () => {
|
||||
|
||||
game.move.select(moveToUse, BattlerIndex.PLAYER_2);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(firstPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||
},
|
||||
|
@ -1,9 +1,7 @@
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { SelectTargetPhase } from "#phases/select-target-phase";
|
||||
import { TurnStartPhase } from "#phases/turn-start-phase";
|
||||
import type { TurnStartPhase } from "#phases/turn-start-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
@ -41,7 +39,7 @@ describe("Battle order", () => {
|
||||
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.to("TurnStartPhase", false);
|
||||
|
||||
const playerPokemonIndex = playerPokemon.getBattlerIndex();
|
||||
const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
|
||||
@ -60,7 +58,7 @@ describe("Battle order", () => {
|
||||
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
||||
await game.phaseInterceptor.to("TurnStartPhase", false);
|
||||
|
||||
const playerPokemonIndex = playerPokemon.getBattlerIndex();
|
||||
const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
|
||||
@ -84,7 +82,7 @@ describe("Battle order", () => {
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
game.move.select(MoveId.TACKLE, 1);
|
||||
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false);
|
||||
await game.phaseInterceptor.to("TurnStartPhase", false);
|
||||
|
||||
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getCommandOrder();
|
||||
@ -108,7 +106,7 @@ describe("Battle order", () => {
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
game.move.select(MoveId.TACKLE, 1);
|
||||
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false);
|
||||
await game.phaseInterceptor.to("TurnStartPhase", false);
|
||||
|
||||
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getCommandOrder();
|
||||
@ -132,7 +130,7 @@ describe("Battle order", () => {
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
game.move.select(MoveId.TACKLE, 1);
|
||||
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false);
|
||||
await game.phaseInterceptor.to("TurnStartPhase", false);
|
||||
|
||||
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
|
||||
const order = phase.getCommandOrder();
|
||||
|
@ -1,28 +1,13 @@
|
||||
import { getGameMode } from "#app/game-mode";
|
||||
import { allSpecies } from "#data/data-lists";
|
||||
import { AbilityId } from "#enums/ability-id";
|
||||
import { BiomeId } from "#enums/biome-id";
|
||||
import { GameModes } from "#enums/game-modes";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { PlayerGender } from "#enums/player-gender";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { BattleEndPhase } from "#phases/battle-end-phase";
|
||||
import { CommandPhase } from "#phases/command-phase";
|
||||
import { DamageAnimPhase } from "#phases/damage-anim-phase";
|
||||
import { EncounterPhase } from "#phases/encounter-phase";
|
||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { LoginPhase } from "#phases/login-phase";
|
||||
import { NextEncounterPhase } from "#phases/next-encounter-phase";
|
||||
import { SelectGenderPhase } from "#phases/select-gender-phase";
|
||||
import { SelectStarterPhase } from "#phases/select-starter-phase";
|
||||
import { SummonPhase } from "#phases/summon-phase";
|
||||
import { SwitchPhase } from "#phases/switch-phase";
|
||||
import { TitlePhase } from "#phases/title-phase";
|
||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import { generateStarter } from "#test/test-utils/game-manager-utils";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
@ -45,55 +30,11 @@ describe("Phase - Battle Phase", () => {
|
||||
game.scene.gameData.gender = undefined!; // just for these tests!
|
||||
});
|
||||
|
||||
it("test phase interceptor with prompt", async () => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
|
||||
game.onNextPrompt("SelectGenderPhase", UiMode.OPTION_SELECT, () => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.run(SelectGenderPhase);
|
||||
|
||||
await game.phaseInterceptor.run(TitlePhase);
|
||||
await game.waitMode(UiMode.TITLE);
|
||||
|
||||
expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE);
|
||||
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
|
||||
});
|
||||
|
||||
it("test phase interceptor with prompt with preparation for a future prompt", async () => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
|
||||
game.onNextPrompt("SelectGenderPhase", UiMode.OPTION_SELECT, () => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
});
|
||||
|
||||
game.onNextPrompt("CheckSwitchPhase", UiMode.CONFIRM, () => {
|
||||
game.setMode(UiMode.MESSAGE);
|
||||
game.endPhase();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectGenderPhase);
|
||||
|
||||
await game.phaseInterceptor.run(TitlePhase);
|
||||
await game.waitMode(UiMode.TITLE);
|
||||
|
||||
expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE);
|
||||
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
|
||||
});
|
||||
|
||||
it("newGame one-liner", async () => {
|
||||
await game.classicMode.startBattle();
|
||||
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
|
||||
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
|
||||
});
|
||||
|
||||
it("do attack wave 3 - single battle - regular - OHKO", async () => {
|
||||
game.override.enemySpecies(SpeciesId.RATTATA).startingLevel(2000).battleStyle("single").startingWave(3);
|
||||
await game.classicMode.startBattle([SpeciesId.MEWTWO]);
|
||||
game.move.use(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
await game.toNextWave();
|
||||
});
|
||||
|
||||
it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async () => {
|
||||
@ -107,7 +48,7 @@ describe("Phase - Battle Phase", () => {
|
||||
.battleStyle("single");
|
||||
await game.classicMode.startBattle([SpeciesId.MEWTWO]);
|
||||
game.move.select(MoveId.TACKLE);
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase, false);
|
||||
await game.phaseInterceptor.to("TurnInitPhase", false);
|
||||
});
|
||||
|
||||
it("load 100% data file", async () => {
|
||||
@ -135,68 +76,6 @@ describe("Phase - Battle Phase", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("wrong phase", async () => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
await game.phaseInterceptor.run(LoginPhase).catch(e => {
|
||||
expect(e).toBe("Wrong phase: this is SelectGenderPhase and not LoginPhase");
|
||||
});
|
||||
});
|
||||
|
||||
it("wrong phase but skip", async () => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
await game.phaseInterceptor.run(LoginPhase, () => game.isCurrentPhase(SelectGenderPhase));
|
||||
});
|
||||
|
||||
it("good run", async () => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
game.onNextPrompt(
|
||||
"SelectGenderPhase",
|
||||
UiMode.OPTION_SELECT,
|
||||
() => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
},
|
||||
() => game.isCurrentPhase(TitlePhase),
|
||||
);
|
||||
await game.phaseInterceptor.run(SelectGenderPhase, () => game.isCurrentPhase(TitlePhase));
|
||||
await game.phaseInterceptor.run(TitlePhase);
|
||||
});
|
||||
|
||||
it("good run from select gender to title", async () => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
game.onNextPrompt(
|
||||
"SelectGenderPhase",
|
||||
UiMode.OPTION_SELECT,
|
||||
() => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
},
|
||||
() => game.isCurrentPhase(TitlePhase),
|
||||
);
|
||||
await game.phaseInterceptor.runFrom(SelectGenderPhase).to(TitlePhase);
|
||||
});
|
||||
|
||||
it("good run to SummonPhase phase", async () => {
|
||||
await game.phaseInterceptor.run(LoginPhase);
|
||||
game.onNextPrompt(
|
||||
"SelectGenderPhase",
|
||||
UiMode.OPTION_SELECT,
|
||||
() => {
|
||||
game.scene.gameData.gender = PlayerGender.MALE;
|
||||
game.endPhase();
|
||||
},
|
||||
() => game.isCurrentPhase(TitlePhase),
|
||||
);
|
||||
game.onNextPrompt("TitlePhase", UiMode.TITLE, () => {
|
||||
game.scene.gameMode = getGameMode(GameModes.CLASSIC);
|
||||
const starters = generateStarter(game.scene);
|
||||
const selectStarterPhase = new SelectStarterPhase();
|
||||
game.scene.phaseManager.pushPhase(new EncounterPhase(false));
|
||||
selectStarterPhase.initBattle(starters);
|
||||
});
|
||||
await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase);
|
||||
});
|
||||
|
||||
it.each([
|
||||
{ name: "1v1", double: false, qty: 1 },
|
||||
{ name: "2v1", double: false, qty: 2 },
|
||||
@ -232,7 +111,7 @@ describe("Phase - Battle Phase", () => {
|
||||
await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
await game.killPokemon(game.scene.currentBattle.enemyParty[0]);
|
||||
expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true);
|
||||
await game.phaseInterceptor.to("VictoryPhase");
|
||||
@ -296,7 +175,7 @@ describe("Phase - Battle Phase", () => {
|
||||
game.field.getPlayerPokemon().hp = 1;
|
||||
game.move.select(moveToUse);
|
||||
|
||||
await game.phaseInterceptor.to(BattleEndPhase);
|
||||
await game.phaseInterceptor.to("BattleEndPhase");
|
||||
game.doRevivePokemon(0); // pretend max revive was picked
|
||||
game.doSelectModifier();
|
||||
|
||||
@ -308,6 +187,6 @@ describe("Phase - Battle Phase", () => {
|
||||
},
|
||||
() => game.isCurrentPhase(NextEncounterPhase),
|
||||
);
|
||||
await game.phaseInterceptor.to(SwitchPhase);
|
||||
await game.phaseInterceptor.to("SwitchPhase");
|
||||
});
|
||||
});
|
||||
|
@ -6,7 +6,6 @@ import { SpeciesId } from "#enums/species-id";
|
||||
import { BATTLE_STATS, Stat } from "#enums/stat";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { TempStatStageBoosterModifier } from "#modifiers/modifier";
|
||||
import { TurnEndPhase } from "#phases/turn-end-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
|
||||
import Phaser from "phaser";
|
||||
@ -47,7 +46,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.runFrom("EnemyCommandPhase").to(TurnEndPhase);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3);
|
||||
});
|
||||
@ -64,11 +63,11 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||
// Raise ACC by +2 stat stages
|
||||
game.move.select(MoveId.HONE_CLAWS);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
// ACC at +3 stat stages yields a x2 multiplier
|
||||
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2);
|
||||
@ -84,11 +83,11 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||
// Raise ATK by +1 stat stage
|
||||
game.move.select(MoveId.HONE_CLAWS);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
// ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK
|
||||
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8);
|
||||
@ -112,7 +111,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
||||
|
||||
game.move.select(MoveId.TACKLE);
|
||||
|
||||
await game.phaseInterceptor.to(TurnEndPhase);
|
||||
await game.phaseInterceptor.to("TurnEndPhase");
|
||||
|
||||
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3);
|
||||
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4);
|
||||
|
@ -4,10 +4,7 @@ import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import type { Move } from "#moves/move";
|
||||
import { DamageAnimPhase } from "#phases/damage-anim-phase";
|
||||
import { MoveEffectPhase } from "#phases/move-effect-phase";
|
||||
import { MoveEndPhase } from "#phases/move-end-phase";
|
||||
import { MovePhase } from "#phases/move-phase";
|
||||
import type { MoveEffectPhase } from "#phases/move-effect-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
@ -55,14 +52,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
// Force user party to act before enemy party
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
});
|
||||
|
||||
@ -75,14 +72,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
// Force user party to act before enemy party
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
});
|
||||
|
||||
@ -95,19 +92,19 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
// Force first enemy to act (and fail) in between party
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
|
||||
// Skip enemy move; because the enemy is at full HP, Rest should fail
|
||||
await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
});
|
||||
|
||||
@ -121,18 +118,18 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
// Force first enemy to act in between party
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEndPhase);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
// Skip enemy move
|
||||
await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase);
|
||||
await game.phaseInterceptor.to("MoveEndPhase");
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
});
|
||||
|
||||
@ -145,14 +142,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
// Force user party to act before enemy party
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
});
|
||||
|
||||
@ -189,24 +186,24 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
// Force first enemy to act in between party
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
});
|
||||
|
||||
@ -243,24 +240,24 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
||||
// Force first enemy to act in between party
|
||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
|
||||
await game.phaseInterceptor.to(MoveEffectPhase, false);
|
||||
await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
|
||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
||||
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||
});
|
||||
});
|
||||
|
@ -2,8 +2,6 @@ import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
@ -40,7 +38,7 @@ describe("Moves - Growth", () => {
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
|
||||
game.move.select(MoveId.GROWTH);
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
||||
});
|
||||
|
@ -2,10 +2,6 @@ import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { BerryPhase } from "#phases/berry-phase";
|
||||
import { FaintPhase } from "#phases/faint-phase";
|
||||
import { MessagePhase } from "#phases/message-phase";
|
||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest";
|
||||
@ -43,7 +39,7 @@ describe("Moves - Parting Shot", () => {
|
||||
|
||||
game.move.select(MoveId.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||
@ -58,7 +54,7 @@ describe("Moves - Parting Shot", () => {
|
||||
|
||||
game.move.select(MoveId.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||
@ -77,36 +73,14 @@ describe("Moves - Parting Shot", () => {
|
||||
SpeciesId.ABRA,
|
||||
]);
|
||||
|
||||
// use Memento 3 times to debuff enemy
|
||||
game.move.select(MoveId.MEMENTO);
|
||||
await game.phaseInterceptor.to(FaintPhase);
|
||||
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
||||
game.move.select(MoveId.MEMENTO);
|
||||
await game.phaseInterceptor.to(FaintPhase);
|
||||
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
|
||||
game.doSelectPartyPokemon(2);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
||||
game.move.select(MoveId.MEMENTO);
|
||||
await game.phaseInterceptor.to(FaintPhase);
|
||||
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
|
||||
game.doSelectPartyPokemon(3);
|
||||
|
||||
// set up done
|
||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
||||
const enemyPokemon = game.field.getEnemyPokemon();
|
||||
expect(enemyPokemon).toBeDefined();
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6);
|
||||
enemyPokemon.setStatStage(Stat.ATK, -6);
|
||||
enemyPokemon.setStatStage(Stat.SPATK, -6);
|
||||
|
||||
// now parting shot should fail
|
||||
game.move.select(MoveId.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||
@ -125,7 +99,7 @@ describe("Moves - Parting Shot", () => {
|
||||
|
||||
game.move.select(MoveId.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||
@ -140,11 +114,10 @@ describe("Moves - Parting Shot", () => {
|
||||
await game.classicMode.startBattle([SpeciesId.SNORLAX, SpeciesId.MEOWTH]);
|
||||
|
||||
const enemyPokemon = game.field.getEnemyPokemon();
|
||||
expect(enemyPokemon).toBeDefined();
|
||||
|
||||
game.move.select(MoveId.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||
@ -158,11 +131,10 @@ describe("Moves - Parting Shot", () => {
|
||||
await game.classicMode.startBattle([SpeciesId.MURKROW]);
|
||||
|
||||
const enemyPokemon = game.field.getEnemyPokemon();
|
||||
expect(enemyPokemon).toBeDefined();
|
||||
|
||||
game.move.select(MoveId.PARTING_SHOT);
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
await game.phaseInterceptor.to("BerryPhase", false);
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||
@ -174,22 +146,21 @@ describe("Moves - Parting Shot", () => {
|
||||
"Parting shot regularly not fail if no party available to switch - party fainted",
|
||||
async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.MURKROW, SpeciesId.MEOWTH]);
|
||||
|
||||
const meowth = game.scene.getPlayerParty()[1];
|
||||
meowth.hp = 0;
|
||||
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
// intentionally kill party pokemon, switch to second slot (now 1 party mon is fainted)
|
||||
await game.killPokemon(game.scene.getPlayerParty()[0]);
|
||||
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
game.doSelectPartyPokemon(1);
|
||||
|
||||
await game.phaseInterceptor.to(TurnInitPhase, false);
|
||||
game.move.select(MoveId.PARTING_SHOT);
|
||||
game.doSelectPartyPokemon(1);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
await game.phaseInterceptor.to(BerryPhase, false);
|
||||
const enemyPokemon = game.field.getEnemyPokemon();
|
||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MEOWTH);
|
||||
expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.MURKROW);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { TurnEndPhase } from "#phases/turn-end-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
@ -41,7 +39,7 @@ describe("Moves - Tackle", () => {
|
||||
await game.classicMode.startBattle([SpeciesId.MIGHTYENA]);
|
||||
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
||||
await game.toEndOfTurn();
|
||||
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
||||
expect(hpLost).toBe(0);
|
||||
});
|
||||
@ -55,7 +53,7 @@ describe("Moves - Tackle", () => {
|
||||
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
||||
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
||||
await game.toEndOfTurn();
|
||||
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
||||
expect(hpLost).toBeGreaterThan(0);
|
||||
expect(hpLost).toBeLessThan(4);
|
||||
|
@ -2,8 +2,6 @@ import { AbilityId } from "#enums/ability-id";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { Stat } from "#enums/stat";
|
||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
@ -43,7 +41,7 @@ describe("Moves - Tail whip", () => {
|
||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||
|
||||
game.move.select(moveToUse);
|
||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
||||
await game.toEndOfTurn();
|
||||
|
||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||
});
|
||||
|
@ -9,7 +9,6 @@ import { MessagePhase } from "#phases/message-phase";
|
||||
import {
|
||||
MysteryEncounterBattlePhase,
|
||||
MysteryEncounterOptionSelectedPhase,
|
||||
MysteryEncounterPhase,
|
||||
MysteryEncounterRewardsPhase,
|
||||
} from "#phases/mystery-encounter-phases";
|
||||
import { VictoryPhase } from "#phases/victory-phase";
|
||||
@ -89,9 +88,9 @@ export async function runMysteryEncounterToEnd(
|
||||
uiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
await game.toNextTurn();
|
||||
} else {
|
||||
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
||||
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,7 +111,7 @@ export async function runSelectMysteryEncounterOption(
|
||||
);
|
||||
|
||||
if (game.isCurrentPhase(MessagePhase)) {
|
||||
await game.phaseInterceptor.run(MessagePhase);
|
||||
await game.phaseInterceptor.to("MessagePhase");
|
||||
}
|
||||
|
||||
// dispose of intro messages
|
||||
@ -126,7 +125,7 @@ export async function runSelectMysteryEncounterOption(
|
||||
() => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase),
|
||||
);
|
||||
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, true);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", true);
|
||||
|
||||
// select the desired option
|
||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||
@ -205,7 +204,7 @@ export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManage
|
||||
game.scene.field.remove(p);
|
||||
});
|
||||
game.scene.phaseManager.pushPhase(new VictoryPhase(0));
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
game.endPhase();
|
||||
game.setMode(UiMode.MESSAGE);
|
||||
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase, runRewardsPhase);
|
||||
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase", runRewardsPhase);
|
||||
}
|
||||
|
@ -134,7 +134,7 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
|
||||
@ -147,9 +147,7 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -232,9 +230,9 @@ describe("Berries Abound - Mystery Encounter", () => {
|
||||
});
|
||||
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -366,11 +366,11 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game, false);
|
||||
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name);
|
||||
game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers
|
||||
game.promptHandler["prompts"] = []; // Clear out prompt handlers
|
||||
game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => {
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
game.endPhase();
|
||||
});
|
||||
await game.phaseInterceptor.run(MysteryEncounterRewardsPhase);
|
||||
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
|
||||
|
||||
expect(selectOptionSpy).toHaveBeenCalledTimes(1);
|
||||
const optionData = selectOptionSpy.mock.calls[0][0];
|
||||
@ -395,7 +395,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
|
||||
it("should NOT be selectable if the player doesn't have any Bug types", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [SpeciesId.ABRA]);
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
@ -417,7 +417,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -436,7 +436,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -458,7 +458,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -482,7 +482,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -530,7 +530,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
it("should NOT be selectable if the player doesn't have any Bug items", async () => {
|
||||
game.scene.modifiers = [];
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty);
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
|
||||
game.scene.modifiers = [];
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
@ -558,7 +558,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -25,7 +25,6 @@ import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
|
||||
import { CommandPhase } from "#phases/command-phase";
|
||||
import { MovePhase } from "#phases/move-phase";
|
||||
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
||||
import { NewBattlePhase } from "#phases/new-battle-phase";
|
||||
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
||||
import {
|
||||
runMysteryEncounterToEnd,
|
||||
@ -200,9 +199,9 @@ describe("Clowning Around - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability;
|
||||
|
||||
game.onNextPrompt("PostMysteryEncounterPhase", UiMode.MESSAGE, () => {
|
||||
@ -215,7 +214,7 @@ describe("Clowning Around - Mystery Encounter", () => {
|
||||
const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler;
|
||||
vi.spyOn(partyUiHandler, "show");
|
||||
game.endPhase();
|
||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
||||
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name);
|
||||
|
||||
// Wait for Yes/No confirmation to appear
|
||||
@ -228,7 +227,7 @@ describe("Clowning Around - Mystery Encounter", () => {
|
||||
// Click "Select" on Pokemon
|
||||
partyUiHandler.processInput(Button.ACTION);
|
||||
// Stop next battle before it runs
|
||||
await game.phaseInterceptor.to(NewBattlePhase, false);
|
||||
await game.phaseInterceptor.to("NewBattlePhase", false);
|
||||
|
||||
const leadPokemon = scene.getPlayerParty()[0];
|
||||
expect(leadPokemon.customPokemonData?.ability).toBe(abilityToTrain);
|
||||
|
@ -126,9 +126,9 @@ describe("Dancing Lessons - Mystery Encounter", () => {
|
||||
partyLead.calculateStats();
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -215,7 +215,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
|
||||
const partyCountBefore = scene.getPlayerParty().length;
|
||||
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
|
@ -94,7 +94,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -131,7 +131,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -171,7 +171,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 3);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -211,7 +211,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 4);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -122,9 +122,9 @@ describe("Fight or Flight - Mystery Encounter", () => {
|
||||
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -155,7 +155,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
|
||||
it("should NOT be selectable if the player doesn't have a Stealing move", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty);
|
||||
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
@ -182,9 +182,9 @@ describe("Fight or Flight - Mystery Encounter", () => {
|
||||
const item = game.scene.currentBattle.mysteryEncounter!.misc;
|
||||
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -120,7 +120,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
||||
it("should NOT be selectable if the player doesn't have enough money", async () => {
|
||||
game.scene.money = 0;
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
@ -162,7 +162,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
||||
|
||||
// Turn 3
|
||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
|
||||
// Rewards
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
@ -181,11 +181,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
||||
// Skip minigame
|
||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
|
||||
// Rewards
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -210,11 +210,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
||||
wobbuffet.hp = Math.floor(0.2 * wobbuffet.getMaxHp());
|
||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
|
||||
// Rewards
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -240,11 +240,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
||||
wobbuffet.hp = Math.floor(0.1 * wobbuffet.getMaxHp());
|
||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
|
||||
// Rewards
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -270,11 +270,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
||||
wobbuffet.hp = 1;
|
||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
|
||||
// Rewards
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -227,7 +227,7 @@ describe("Global Trade System - Mystery Encounter", () => {
|
||||
|
||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -161,9 +161,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -205,9 +205,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -262,9 +262,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -146,7 +146,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
|
||||
it("should NOT be selectable if the player doesn't have enough money", async () => {
|
||||
game.scene.money = 0;
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
@ -218,7 +218,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
|
||||
|
||||
it("should NOT be selectable if the player doesn't the right type pokemon", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.BLASTOISE]);
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
|
||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
@ -299,9 +299,9 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -13,7 +13,6 @@ import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
|
||||
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
|
||||
import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter";
|
||||
import { CommandPhase } from "#phases/command-phase";
|
||||
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
||||
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
||||
import {
|
||||
runMysteryEncounterToEnd,
|
||||
@ -176,7 +175,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
||||
|
||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const eggsAfter = scene.gameData.eggs;
|
||||
@ -187,8 +186,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
||||
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
||||
game.endPhase();
|
||||
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||
|
||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship;
|
||||
// 20 from ME + extra from winning battle (that extra is not accurate to what happens in game.
|
||||
@ -261,7 +260,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
||||
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const eggsAfter = scene.gameData.eggs;
|
||||
@ -272,8 +271,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
||||
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
||||
game.endPhase();
|
||||
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||
|
||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship;
|
||||
expect(friendshipAfter).toBe(friendshipBefore + 20 + FRIENDSHIP_GAIN_FROM_BATTLE); // 20 from ME + extra for friendship gained from winning battle
|
||||
@ -343,7 +342,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
||||
|
||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const eggsAfter = scene.gameData.eggs;
|
||||
@ -354,8 +353,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
|
||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
||||
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
||||
game.endPhase();
|
||||
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||
|
||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship;
|
||||
expect(friendshipAfter).toBe(friendshipBefore + 20 + FRIENDSHIP_GAIN_FROM_BATTLE); // 20 + extra for friendship gained from winning battle
|
||||
|
@ -229,9 +229,9 @@ describe("The Strong Stuff - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -16,7 +16,6 @@ import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
|
||||
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
|
||||
import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter";
|
||||
import { CommandPhase } from "#phases/command-phase";
|
||||
import { MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases";
|
||||
import { PartyHealPhase } from "#phases/party-heal-phase";
|
||||
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
||||
import { VictoryPhase } from "#phases/victory-phase";
|
||||
@ -295,9 +294,9 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
|
||||
|
||||
// Should have Macho Brace in the rewards
|
||||
await skipBattleToNextBattle(game, true);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -339,7 +338,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -366,11 +365,10 @@ async function skipBattleToNextBattle(game: GameManager, isFinalBattle = false)
|
||||
p.status = new Status(StatusEffect.FAINT);
|
||||
game.scene.field.remove(p);
|
||||
});
|
||||
game.phaseInterceptor["onHold"] = [];
|
||||
game.scene.phaseManager.pushPhase(new VictoryPhase(0));
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
game.endPhase();
|
||||
if (isFinalBattle) {
|
||||
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
||||
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
|
||||
} else {
|
||||
await game.toNextTurn();
|
||||
}
|
||||
|
@ -172,7 +172,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
||||
it("should give 2 Leftovers, 1 Shell Bell, and Black Sludge", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier;
|
||||
@ -242,9 +242,9 @@ describe("Trash to Treasure - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -116,7 +116,7 @@ describe("Weird Dream - Mystery Encounter", () => {
|
||||
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
|
||||
|
||||
await runMysteryEncounterToEnd(game, 1);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
|
||||
const pokemonAfter = scene.getPlayerParty();
|
||||
@ -139,9 +139,9 @@ describe("Weird Dream - Mystery Encounter", () => {
|
||||
it("should have 1 Memory Mushroom, 5 Rogue Balls, and 3 Mints in rewards", async () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 1);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -196,9 +196,9 @@ describe("Weird Dream - Mystery Encounter", () => {
|
||||
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -37,7 +37,7 @@ describe("Mystery Encounter Phases", () => {
|
||||
SpeciesId.VOLCARONA,
|
||||
]);
|
||||
|
||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||
});
|
||||
|
||||
@ -49,9 +49,9 @@ describe("Mystery Encounter Phases", () => {
|
||||
|
||||
game.onNextPrompt("MysteryEncounterPhase", UiMode.MYSTERY_ENCOUNTER, () => {
|
||||
// End phase early for test
|
||||
game.phaseInterceptor.superEndPhase();
|
||||
game.endPhase();
|
||||
});
|
||||
await game.phaseInterceptor.run(MysteryEncounterPhase);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase");
|
||||
|
||||
expect(game.scene.mysteryEncounterSaveData.encounteredEvents.length).toBeGreaterThan(0);
|
||||
expect(game.scene.mysteryEncounterSaveData.encounteredEvents[0].type).toEqual(
|
||||
@ -75,7 +75,7 @@ describe("Mystery Encounter Phases", () => {
|
||||
handler.processInput(Button.ACTION);
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.run(MysteryEncounterPhase);
|
||||
await game.phaseInterceptor.to("MysteryEncounterPhase");
|
||||
|
||||
// Select option 1 for encounter
|
||||
const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler;
|
||||
|
@ -241,7 +241,7 @@ describe("SelectModifierPhase", () => {
|
||||
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
|
||||
scene.phaseManager.unshiftPhase(selectModifierPhase);
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
@ -265,7 +265,7 @@ describe("SelectModifierPhase", () => {
|
||||
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
|
||||
scene.phaseManager.unshiftPhase(selectModifierPhase);
|
||||
game.move.select(MoveId.SPLASH);
|
||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||
const modifierSelectHandler = scene.ui.handlers.find(
|
||||
|
@ -1,49 +0,0 @@
|
||||
export class ErrorInterceptor {
|
||||
private static instance: ErrorInterceptor;
|
||||
public running;
|
||||
|
||||
constructor() {
|
||||
this.running = [];
|
||||
}
|
||||
|
||||
public static getInstance(): ErrorInterceptor {
|
||||
if (!ErrorInterceptor.instance) {
|
||||
ErrorInterceptor.instance = new ErrorInterceptor();
|
||||
}
|
||||
return ErrorInterceptor.instance;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.running = [];
|
||||
}
|
||||
|
||||
add(obj) {
|
||||
this.running.push(obj);
|
||||
}
|
||||
|
||||
remove(obj) {
|
||||
const index = this.running.indexOf(obj);
|
||||
if (index !== -1) {
|
||||
this.running.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
process.on("uncaughtException", error => {
|
||||
console.log(error);
|
||||
const toStop = ErrorInterceptor.getInstance().running;
|
||||
for (const elm of toStop) {
|
||||
elm.rejectAll(error);
|
||||
}
|
||||
global.testFailed = true;
|
||||
});
|
||||
|
||||
// Global error handler for unhandled promise rejections
|
||||
process.on("unhandledRejection", (reason, _promise) => {
|
||||
console.log(reason);
|
||||
const toStop = ErrorInterceptor.getInstance().running;
|
||||
for (const elm of toStop) {
|
||||
elm.rejectAll(reason);
|
||||
}
|
||||
global.testFailed = true;
|
||||
});
|
@ -20,17 +20,14 @@ import { ModifierTypeOption } from "#modifiers/modifier-type";
|
||||
import { CheckSwitchPhase } from "#phases/check-switch-phase";
|
||||
import { CommandPhase } from "#phases/command-phase";
|
||||
import { EncounterPhase } from "#phases/encounter-phase";
|
||||
import { LoginPhase } from "#phases/login-phase";
|
||||
import { MovePhase } from "#phases/move-phase";
|
||||
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
||||
import { NewBattlePhase } from "#phases/new-battle-phase";
|
||||
import { SelectStarterPhase } from "#phases/select-starter-phase";
|
||||
import type { SelectTargetPhase } from "#phases/select-target-phase";
|
||||
import { TitlePhase } from "#phases/title-phase";
|
||||
import { TurnEndPhase } from "#phases/turn-end-phase";
|
||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||
import { TurnStartPhase } from "#phases/turn-start-phase";
|
||||
import { ErrorInterceptor } from "#test/test-utils/error-interceptor";
|
||||
import { generateStarter } from "#test/test-utils/game-manager-utils";
|
||||
import { GameWrapper } from "#test/test-utils/game-wrapper";
|
||||
import { ChallengeModeHelper } from "#test/test-utils/helpers/challenge-mode-helper";
|
||||
@ -40,12 +37,14 @@ import { FieldHelper } from "#test/test-utils/helpers/field-helper";
|
||||
import { ModifierHelper } from "#test/test-utils/helpers/modifiers-helper";
|
||||
import { MoveHelper } from "#test/test-utils/helpers/move-helper";
|
||||
import { OverridesHelper } from "#test/test-utils/helpers/overrides-helper";
|
||||
import { PromptHandler } from "#test/test-utils/helpers/prompt-handler";
|
||||
import { ReloadHelper } from "#test/test-utils/helpers/reload-helper";
|
||||
import { SettingsHelper } from "#test/test-utils/helpers/settings-helper";
|
||||
import type { InputsHandler } from "#test/test-utils/inputs-handler";
|
||||
import { MockFetch } from "#test/test-utils/mocks/mock-fetch";
|
||||
import { PhaseInterceptor } from "#test/test-utils/phase-interceptor";
|
||||
import { TextInterceptor } from "#test/test-utils/text-interceptor";
|
||||
import type { PhaseString } from "#types/phase-types";
|
||||
import type { BallUiHandler } from "#ui/ball-ui-handler";
|
||||
import type { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
|
||||
import type { CommandUiHandler } from "#ui/command-ui-handler";
|
||||
@ -67,6 +66,7 @@ export class GameManager {
|
||||
public phaseInterceptor: PhaseInterceptor;
|
||||
public textInterceptor: TextInterceptor;
|
||||
public inputsHandler: InputsHandler;
|
||||
public readonly promptHandler: PromptHandler;
|
||||
public readonly override: OverridesHelper;
|
||||
public readonly move: MoveHelper;
|
||||
public readonly classicMode: ClassicModeHelper;
|
||||
@ -84,7 +84,6 @@ export class GameManager {
|
||||
*/
|
||||
constructor(phaserGame: Phaser.Game, bypassLogin = true) {
|
||||
localStorage.clear();
|
||||
ErrorInterceptor.getInstance().clear();
|
||||
// Simulate max rolls on RNG functions
|
||||
// TODO: Create helpers for disabling/enabling battle RNG
|
||||
BattleScene.prototype.randBattleSeedInt = (range, min = 0) => min + range - 1;
|
||||
@ -104,6 +103,7 @@ export class GameManager {
|
||||
}
|
||||
|
||||
this.textInterceptor = new TextInterceptor(this.scene);
|
||||
this.promptHandler = new PromptHandler(this);
|
||||
this.override = new OverridesHelper(this);
|
||||
this.move = new MoveHelper(this);
|
||||
this.classicMode = new ClassicModeHelper(this);
|
||||
@ -153,13 +153,14 @@ export class GameManager {
|
||||
* @param mode - The mode to wait for.
|
||||
* @returns A promise that resolves when the mode is set.
|
||||
*/
|
||||
// TODO: This is unused
|
||||
// TODO: This is unused
|
||||
async waitMode(mode: UiMode): Promise<void> {
|
||||
await vi.waitUntil(() => this.scene.ui?.getMode() === mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* End the currently running phase immediately.
|
||||
* End the current phase immediately.
|
||||
* @see {@linkcode PhaseInterceptor.shiftPhase} Function to skip the next upcoming phase
|
||||
*/
|
||||
endPhase() {
|
||||
this.scene.phaseManager.getCurrentPhase()?.end();
|
||||
@ -172,15 +173,18 @@ export class GameManager {
|
||||
* @param mode - The mode to wait for.
|
||||
* @param callback - The callback function to execute on next prompt.
|
||||
* @param expireFn - Optional function to determine if the prompt has expired.
|
||||
* @param awaitingActionInput - If true, will prevent the prompt from activating until the current {@linkcode AwaitableUiHandler}
|
||||
* is awaiting input; default `false`
|
||||
* @todo Remove in favor of {@linkcode promptHandler.addToNextPrompt}
|
||||
*/
|
||||
onNextPrompt(
|
||||
phaseTarget: string,
|
||||
phaseTarget: PhaseString,
|
||||
mode: UiMode,
|
||||
callback: () => void,
|
||||
expireFn?: () => void,
|
||||
expireFn?: () => boolean,
|
||||
awaitingActionInput = false,
|
||||
) {
|
||||
this.phaseInterceptor.addToNextPrompt(phaseTarget, mode, callback, expireFn, awaitingActionInput);
|
||||
this.promptHandler.addToNextPrompt(phaseTarget, mode, callback, expireFn, awaitingActionInput);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -188,9 +192,10 @@ export class GameManager {
|
||||
* @returns A promise that resolves when the title phase is reached.
|
||||
*/
|
||||
async runToTitle(): Promise<void> {
|
||||
await this.phaseInterceptor.whenAboutToRun(LoginPhase);
|
||||
this.phaseInterceptor.pop();
|
||||
await this.phaseInterceptor.run(TitlePhase);
|
||||
// Go to login phase and skip past it
|
||||
await this.phaseInterceptor.to("LoginPhase", false);
|
||||
this.phaseInterceptor.shiftPhase();
|
||||
await this.phaseInterceptor.to("TitlePhase");
|
||||
|
||||
this.scene.gameSpeed = 5;
|
||||
this.scene.moveAnimations = false;
|
||||
@ -270,7 +275,7 @@ export class GameManager {
|
||||
true,
|
||||
);
|
||||
|
||||
await this.phaseInterceptor.run(EncounterPhase);
|
||||
await this.phaseInterceptor.to("EncounterPhase");
|
||||
if (!isNullOrUndefined(encounterType)) {
|
||||
expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(encounterType);
|
||||
}
|
||||
@ -503,7 +508,7 @@ export class GameManager {
|
||||
* @param inPhase - Which phase to expect the selection to occur in. Defaults to `SwitchPhase`
|
||||
* (which is where the majority of non-command switch operations occur).
|
||||
*/
|
||||
doSelectPartyPokemon(slot: number, inPhase = "SwitchPhase") {
|
||||
doSelectPartyPokemon(slot: number, inPhase: PhaseString = "SwitchPhase") {
|
||||
this.onNextPrompt(inPhase, UiMode.PARTY, () => {
|
||||
const partyHandler = this.scene.ui.getHandler() as PartyUiHandler;
|
||||
|
||||
@ -542,7 +547,7 @@ export class GameManager {
|
||||
* ```
|
||||
*/
|
||||
async setTurnOrder(order: BattlerIndex[]): Promise<void> {
|
||||
await this.phaseInterceptor.to(TurnStartPhase, false);
|
||||
await this.phaseInterceptor.to("TurnStartPhase", false);
|
||||
|
||||
vi.spyOn(this.scene.phaseManager.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order);
|
||||
}
|
||||
|
@ -47,12 +47,14 @@ export class GameWrapper {
|
||||
public scene: BattleScene;
|
||||
|
||||
constructor(phaserGame: Phaser.Game, bypassLogin: boolean) {
|
||||
// TODO: Figure out how to actually set RNG states correctly
|
||||
Phaser.Math.RND.sow(["test"]);
|
||||
// vi.spyOn(Utils, "apiFetch", "get").mockReturnValue(fetch);
|
||||
if (bypassLogin) {
|
||||
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true);
|
||||
}
|
||||
this.game = phaserGame;
|
||||
// TODO: Move these mocks elsewhere
|
||||
MoveAnim.prototype.getAnim = () => ({
|
||||
frames: {},
|
||||
});
|
||||
@ -71,10 +73,16 @@ export class GameWrapper {
|
||||
PokedexMonContainer.prototype.remove = MockContainer.prototype.remove;
|
||||
}
|
||||
|
||||
setScene(scene: BattleScene) {
|
||||
/**
|
||||
* Initialize the given {@linkcode BattleScene} and override various properties to avoid crashes with headless games.
|
||||
* @param scene - The {@linkcode BattleScene} to initialize
|
||||
* @returns A Promise that resolves once the initialization process has completed.
|
||||
*/
|
||||
// TODO: is asset loading & method overriding actually needed for a headless renderer?
|
||||
async setScene(scene: BattleScene): Promise<void> {
|
||||
this.scene = scene;
|
||||
this.injectMandatory();
|
||||
this.scene.preload?.();
|
||||
this.scene.preload();
|
||||
this.scene.create();
|
||||
}
|
||||
|
||||
|
@ -49,7 +49,7 @@ export class ChallengeModeHelper extends GameManagerHelper {
|
||||
selectStarterPhase.initBattle(starters);
|
||||
});
|
||||
|
||||
await this.game.phaseInterceptor.run(EncounterPhase);
|
||||
await this.game.phaseInterceptor.to("EncounterPhase");
|
||||
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0 && this.game.override.removeEnemyStartingItems) {
|
||||
this.game.removeEnemyHeldItems();
|
||||
}
|
||||
|
159
test/test-utils/helpers/prompt-handler.ts
Normal file
159
test/test-utils/helpers/prompt-handler.ts
Normal file
@ -0,0 +1,159 @@
|
||||
import type { AwaitableUiHandler } from "#app/ui/awaitable-ui-handler";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import type { GameManager } from "#test/test-utils/game-manager";
|
||||
import { GameManagerHelper } from "#test/test-utils/helpers/game-manager-helper";
|
||||
import type { PhaseString } from "#types/phase-types";
|
||||
import chalk from "chalk";
|
||||
import { type MockInstance, vi } from "vitest";
|
||||
|
||||
interface UIPrompt {
|
||||
/** The {@linkcode PhaseString | name} of the Phase during which to execute the callback. */
|
||||
phaseTarget: PhaseString;
|
||||
/** The {@linkcode UIMode} to wait for. */
|
||||
mode: UiMode;
|
||||
/** The callback function to execute. */
|
||||
callback: () => void;
|
||||
/**
|
||||
* An optional callback function to determine if the prompt has expired and should be removed.
|
||||
* Expired prompts are removed upon the next UI mode change without executing their callback.
|
||||
*/
|
||||
expireFn?: () => boolean;
|
||||
/**
|
||||
* If `true`, restricts the prompt to only activate when the current {@linkcode AwaitableUiHandler} is waiting for input.
|
||||
* @defaultValue `false`
|
||||
*/
|
||||
awaitingActionInput: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of phases that hang whiile waiting for player input.
|
||||
* Changing UI modes during these phases will halt the phase interceptor.
|
||||
* @todo This is an extremely unintuitive solution that only works on a select few phases
|
||||
* and does not account for UI handlers not accepting input
|
||||
*/
|
||||
const endBySetMode: ReadonlyArray<PhaseString> = [
|
||||
"CommandPhase",
|
||||
"TitlePhase",
|
||||
"SelectGenderPhase",
|
||||
"SelectStarterPhase",
|
||||
"SelectModifierPhase",
|
||||
"MysteryEncounterPhase",
|
||||
"PostMysteryEncounterPhase",
|
||||
];
|
||||
|
||||
/** Helper class to handle executing prompts upon UI mode changes. */
|
||||
export class PromptHandler extends GameManagerHelper {
|
||||
/** An array of {@linkcode UIPrompt | prompts} with associated callbacks. */
|
||||
private prompts: UIPrompt[] = [];
|
||||
/** The original `setModeInternal` function, stored for use in {@linkcode setMode}. */
|
||||
private originalSetModeInternal: (typeof this.game.scene.ui)["setModeInternal"];
|
||||
|
||||
public static runInterval?: NodeJS.Timeout;
|
||||
|
||||
constructor(game: GameManager) {
|
||||
super(game);
|
||||
this.originalSetModeInternal = this.game.scene.ui["setModeInternal"];
|
||||
// `any` assertion needed as we are mocking private property
|
||||
(
|
||||
vi.spyOn(this.game.scene.ui as any, "setModeInternal") as MockInstance<
|
||||
(typeof this.game.scene.ui)["setModeInternal"]
|
||||
>
|
||||
).mockImplementation((...args) => this.setMode(args));
|
||||
|
||||
// Set an interval to repeatedly check the current prompt.
|
||||
if (PromptHandler.runInterval) {
|
||||
throw new Error("Prompt handler run interval was not properly cleared on test end!");
|
||||
}
|
||||
PromptHandler.runInterval = setInterval(() => this.doPromptCheck());
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to wrap UI mode changing.
|
||||
* @param args - Arguments being passed to the original method
|
||||
* @returns The original return value.
|
||||
*/
|
||||
private setMode(args: Parameters<typeof this.originalSetModeInternal>) {
|
||||
const mode = args[0];
|
||||
|
||||
this.doLog(`UI mode changed to ${UiMode[mode]} (=${mode})!`);
|
||||
const ret = this.originalSetModeInternal.apply(this.game.scene.ui, args) as ReturnType<
|
||||
typeof this.originalSetModeInternal
|
||||
>;
|
||||
|
||||
const currentPhase = this.game.scene.phaseManager.getCurrentPhase()?.phaseName!;
|
||||
if (endBySetMode.includes(currentPhase)) {
|
||||
this.game.phaseInterceptor.checkMode();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to perform prompt handling every so often.
|
||||
* @param uiMode - The {@linkcode UiMode} being set
|
||||
*/
|
||||
private doPromptCheck(): void {
|
||||
if (this.prompts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const prompt = this.prompts[0];
|
||||
|
||||
// remove expired prompts
|
||||
if (prompt.expireFn?.()) {
|
||||
this.prompts.shift();
|
||||
return;
|
||||
}
|
||||
|
||||
const currentPhase = this.game.scene.phaseManager.getCurrentPhase()?.phaseName;
|
||||
const currentHandler = this.game.scene.ui.getHandler();
|
||||
const mode = this.game.scene.ui.getMode();
|
||||
|
||||
// If the current mode, phase, and handler match the expected values, execute the callback and continue.
|
||||
// If not, leave it there.
|
||||
if (
|
||||
mode === prompt.mode &&
|
||||
currentPhase === prompt.phaseTarget &&
|
||||
currentHandler.active &&
|
||||
!(prompt.awaitingActionInput && !(currentHandler as AwaitableUiHandler)["awaitingActionInput"])
|
||||
) {
|
||||
prompt.callback();
|
||||
this.prompts.shift();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Queue a callback to be executed on the next UI mode change.
|
||||
* This can be used to (among other things) simulate inputs or run callbacks mid-phase.
|
||||
* @param phaseTarget - The {@linkcode PhaseString | name} of the Phase during which the callback will be executed
|
||||
* @param mode - The {@linkcode UiMode} to wait for
|
||||
* @param callback - The callback function to execute
|
||||
* @param expireFn - Optional function to determine if the prompt has expired
|
||||
* @param awaitingActionInput - If `true`, restricts the prompt to only activate when the current {@linkcode AwaitableUiHandler} is waiting for input; default `false`
|
||||
* @remarks
|
||||
* If multiple prompts are queued up in succession, each will be checked in turn **until the first prompt that neither expires nor matches**.
|
||||
* @todo Review all uses of this function to check if they can be made synchronous
|
||||
*/
|
||||
public addToNextPrompt(
|
||||
phaseTarget: PhaseString,
|
||||
mode: UiMode,
|
||||
callback: () => void,
|
||||
expireFn?: () => boolean,
|
||||
awaitingActionInput = false,
|
||||
) {
|
||||
this.prompts.push({
|
||||
phaseTarget,
|
||||
mode,
|
||||
callback,
|
||||
expireFn,
|
||||
awaitingActionInput,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper function to add green coloration to phase logs.
|
||||
* @param args - Arguments to original logging function.
|
||||
*/
|
||||
private doLog(...args: unknown[]): void {
|
||||
console.log(chalk.hex("#ffa500")(...args));
|
||||
}
|
||||
}
|
@ -57,7 +57,8 @@ export class ReloadHelper extends GameManagerHelper {
|
||||
this.game.scene.modifiers = [];
|
||||
}
|
||||
titlePhase.loadSaveSlot(-1); // Load the desired session data
|
||||
this.game.phaseInterceptor.shift(); // Loading the save slot also ended TitlePhase, clean it up
|
||||
console.log(this.game.scene.phaseManager.getCurrentPhase()?.phaseName);
|
||||
this.game.scene.phaseManager.shiftPhase(); // Loading the save slot also ended TitlePhase, clean it up
|
||||
|
||||
// Run through prompts for switching Pokemon, copied from classicModeHelper.ts
|
||||
if (this.game.scene.battleStyle === BattleStyle.SWITCH) {
|
||||
|
11
test/test-utils/mocks/mock-phase.ts
Normal file
11
test/test-utils/mocks/mock-phase.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { Phase } from "#app/phase";
|
||||
/**
|
||||
* A rudimentary mock of a phase.
|
||||
* Ends upon starting by default.
|
||||
*/
|
||||
export abstract class mockPhase extends Phase {
|
||||
public phaseName: any;
|
||||
start() {
|
||||
this.end();
|
||||
}
|
||||
}
|
@ -1,480 +1,223 @@
|
||||
import { Phase } from "#app/phase";
|
||||
import type { PhaseString } from "#app/@types/phase-types";
|
||||
import type { BattleScene } from "#app/battle-scene";
|
||||
import type { Phase } from "#app/phase";
|
||||
import type { Constructor } from "#app/utils/common";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { AttemptRunPhase } from "#phases/attempt-run-phase";
|
||||
import { BattleEndPhase } from "#phases/battle-end-phase";
|
||||
import { BerryPhase } from "#phases/berry-phase";
|
||||
import { CheckSwitchPhase } from "#phases/check-switch-phase";
|
||||
import { CommandPhase } from "#phases/command-phase";
|
||||
import { DamageAnimPhase } from "#phases/damage-anim-phase";
|
||||
import { EggLapsePhase } from "#phases/egg-lapse-phase";
|
||||
import { EncounterPhase } from "#phases/encounter-phase";
|
||||
import { EndEvolutionPhase } from "#phases/end-evolution-phase";
|
||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
||||
import { EvolutionPhase } from "#phases/evolution-phase";
|
||||
import { ExpPhase } from "#phases/exp-phase";
|
||||
import { FaintPhase } from "#phases/faint-phase";
|
||||
import { FormChangePhase } from "#phases/form-change-phase";
|
||||
import { GameOverModifierRewardPhase } from "#phases/game-over-modifier-reward-phase";
|
||||
import { GameOverPhase } from "#phases/game-over-phase";
|
||||
import { LearnMovePhase } from "#phases/learn-move-phase";
|
||||
import { LevelCapPhase } from "#phases/level-cap-phase";
|
||||
import { LoginPhase } from "#phases/login-phase";
|
||||
import { MessagePhase } from "#phases/message-phase";
|
||||
import { ModifierRewardPhase } from "#phases/modifier-reward-phase";
|
||||
import { MoveEffectPhase } from "#phases/move-effect-phase";
|
||||
import { MoveEndPhase } from "#phases/move-end-phase";
|
||||
import { MovePhase } from "#phases/move-phase";
|
||||
import {
|
||||
MysteryEncounterBattlePhase,
|
||||
MysteryEncounterOptionSelectedPhase,
|
||||
MysteryEncounterPhase,
|
||||
MysteryEncounterRewardsPhase,
|
||||
PostMysteryEncounterPhase,
|
||||
} from "#phases/mystery-encounter-phases";
|
||||
import { NewBattlePhase } from "#phases/new-battle-phase";
|
||||
import { NewBiomeEncounterPhase } from "#phases/new-biome-encounter-phase";
|
||||
import { NextEncounterPhase } from "#phases/next-encounter-phase";
|
||||
import { PartyExpPhase } from "#phases/party-exp-phase";
|
||||
import { PartyHealPhase } from "#phases/party-heal-phase";
|
||||
import { PokemonTransformPhase } from "#phases/pokemon-transform-phase";
|
||||
import { PositionalTagPhase } from "#phases/positional-tag-phase";
|
||||
import { PostGameOverPhase } from "#phases/post-game-over-phase";
|
||||
import { PostSummonPhase } from "#phases/post-summon-phase";
|
||||
import { QuietFormChangePhase } from "#phases/quiet-form-change-phase";
|
||||
import { RevivalBlessingPhase } from "#phases/revival-blessing-phase";
|
||||
import { RibbonModifierRewardPhase } from "#phases/ribbon-modifier-reward-phase";
|
||||
import { SelectBiomePhase } from "#phases/select-biome-phase";
|
||||
import { SelectGenderPhase } from "#phases/select-gender-phase";
|
||||
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
||||
import { SelectStarterPhase } from "#phases/select-starter-phase";
|
||||
import { SelectTargetPhase } from "#phases/select-target-phase";
|
||||
import { ShinySparklePhase } from "#phases/shiny-sparkle-phase";
|
||||
import { ShowAbilityPhase } from "#phases/show-ability-phase";
|
||||
import { StatStageChangePhase } from "#phases/stat-stage-change-phase";
|
||||
import { SummonPhase } from "#phases/summon-phase";
|
||||
import { SwitchPhase } from "#phases/switch-phase";
|
||||
import { SwitchSummonPhase } from "#phases/switch-summon-phase";
|
||||
import { TitlePhase } from "#phases/title-phase";
|
||||
import { ToggleDoublePositionPhase } from "#phases/toggle-double-position-phase";
|
||||
import { TurnEndPhase } from "#phases/turn-end-phase";
|
||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||
import { TurnStartPhase } from "#phases/turn-start-phase";
|
||||
import { UnavailablePhase } from "#phases/unavailable-phase";
|
||||
import { UnlockPhase } from "#phases/unlock-phase";
|
||||
import { VictoryPhase } from "#phases/victory-phase";
|
||||
import { ErrorInterceptor } from "#test/test-utils/error-interceptor";
|
||||
import type { PhaseClass, PhaseString } from "#types/phase-types";
|
||||
import { UI } from "#ui/ui";
|
||||
// biome-ignore-start lint/correctness/noUnusedImports: TSDoc imports
|
||||
import type { GameManager } from "#test/test-utils/game-manager";
|
||||
import type { PromptHandler } from "#test/test-utils/helpers/prompt-handler";
|
||||
// biome-ignore-end lint/correctness/noUnusedImports: TSDoc imports
|
||||
import { format } from "util";
|
||||
import chalk from "chalk";
|
||||
import { vi } from "vitest";
|
||||
|
||||
export interface PromptHandler {
|
||||
phaseTarget?: string;
|
||||
mode?: UiMode;
|
||||
callback?: () => void;
|
||||
expireFn?: () => void;
|
||||
awaitingActionInput?: boolean;
|
||||
}
|
||||
/**
|
||||
* A Set containing phase names that will not be shown in the console when started.
|
||||
*
|
||||
* Used to reduce console noise from very repetitive phases.
|
||||
*/
|
||||
const blacklistedPhaseNames: ReadonlySet<PhaseString> = new Set(["ActivatePriorityQueuePhase"]);
|
||||
|
||||
type PhaseInterceptorPhase = PhaseClass | PhaseString;
|
||||
/**
|
||||
* The interceptor's current state.
|
||||
* Possible values are the following:
|
||||
* - `running`: The interceptor is currently running a phase.
|
||||
* - `interrupted`: The interceptor has been interrupted by a UI prompt and is waiting for the caller to end it.
|
||||
* - `idling`: The interceptor is not currently running a phase and is ready to start a new one.
|
||||
*/
|
||||
type StateType = "running" | "interrupted" | "idling";
|
||||
|
||||
/**
|
||||
* The PhaseInterceptor is a wrapper around the `BattleScene`'s {@linkcode PhaseManager}.
|
||||
* It allows tests to exert finer control over the phase system, providing logging, manual advancing, and other helpful utilities.
|
||||
*/
|
||||
export class PhaseInterceptor {
|
||||
public scene;
|
||||
public phases = {};
|
||||
public log: string[];
|
||||
private onHold;
|
||||
private interval;
|
||||
private promptInterval;
|
||||
private intervalRun;
|
||||
private prompts: PromptHandler[];
|
||||
private phaseFrom;
|
||||
private inProgress;
|
||||
private originalSetMode;
|
||||
private originalSetOverlayMode;
|
||||
private originalSuperEnd;
|
||||
|
||||
private scene: BattleScene;
|
||||
/**
|
||||
* List of phases with their corresponding start methods.
|
||||
*
|
||||
* CAUTION: If a phase and its subclasses (if any) both appear in this list,
|
||||
* make sure that this list contains said phase AFTER all of its subclasses.
|
||||
* This way, the phase's `prototype.start` is properly preserved during
|
||||
* `initPhases()` so that its subclasses can use `super.start()` properly.
|
||||
* A log containing all phases having been executed in FIFO order. \
|
||||
* Entries are appended each time {@linkcode run} is called, and can be cleared manually with {@linkcode clearLogs}.
|
||||
*/
|
||||
private PHASES = [
|
||||
[LoginPhase, this.startPhase],
|
||||
[TitlePhase, this.startPhase],
|
||||
[SelectGenderPhase, this.startPhase],
|
||||
[NewBiomeEncounterPhase, this.startPhase],
|
||||
[SelectStarterPhase, this.startPhase],
|
||||
[PostSummonPhase, this.startPhase],
|
||||
[SummonPhase, this.startPhase],
|
||||
[ToggleDoublePositionPhase, this.startPhase],
|
||||
[CheckSwitchPhase, this.startPhase],
|
||||
[ShowAbilityPhase, this.startPhase],
|
||||
[MessagePhase, this.startPhase],
|
||||
[TurnInitPhase, this.startPhase],
|
||||
[CommandPhase, this.startPhase],
|
||||
[EnemyCommandPhase, this.startPhase],
|
||||
[TurnStartPhase, this.startPhase],
|
||||
[MovePhase, this.startPhase],
|
||||
[MoveEffectPhase, this.startPhase],
|
||||
[DamageAnimPhase, this.startPhase],
|
||||
[FaintPhase, this.startPhase],
|
||||
[BerryPhase, this.startPhase],
|
||||
[TurnEndPhase, this.startPhase],
|
||||
[BattleEndPhase, this.startPhase],
|
||||
[EggLapsePhase, this.startPhase],
|
||||
[SelectModifierPhase, this.startPhase],
|
||||
[NextEncounterPhase, this.startPhase],
|
||||
[NewBattlePhase, this.startPhase],
|
||||
[VictoryPhase, this.startPhase],
|
||||
[LearnMovePhase, this.startPhase],
|
||||
[MoveEndPhase, this.startPhase],
|
||||
[StatStageChangePhase, this.startPhase],
|
||||
[ShinySparklePhase, this.startPhase],
|
||||
[SelectTargetPhase, this.startPhase],
|
||||
[UnavailablePhase, this.startPhase],
|
||||
[QuietFormChangePhase, this.startPhase],
|
||||
[SwitchPhase, this.startPhase],
|
||||
[SwitchSummonPhase, this.startPhase],
|
||||
[PartyHealPhase, this.startPhase],
|
||||
[FormChangePhase, this.startPhase],
|
||||
[EvolutionPhase, this.startPhase],
|
||||
[EndEvolutionPhase, this.startPhase],
|
||||
[LevelCapPhase, this.startPhase],
|
||||
[AttemptRunPhase, this.startPhase],
|
||||
[SelectBiomePhase, this.startPhase],
|
||||
[PositionalTagPhase, this.startPhase],
|
||||
[PokemonTransformPhase, this.startPhase],
|
||||
[MysteryEncounterPhase, this.startPhase],
|
||||
[MysteryEncounterOptionSelectedPhase, this.startPhase],
|
||||
[MysteryEncounterBattlePhase, this.startPhase],
|
||||
[MysteryEncounterRewardsPhase, this.startPhase],
|
||||
[PostMysteryEncounterPhase, this.startPhase],
|
||||
[RibbonModifierRewardPhase, this.startPhase],
|
||||
[GameOverModifierRewardPhase, this.startPhase],
|
||||
[ModifierRewardPhase, this.startPhase],
|
||||
[PartyExpPhase, this.startPhase],
|
||||
[ExpPhase, this.startPhase],
|
||||
[EncounterPhase, this.startPhase],
|
||||
[GameOverPhase, this.startPhase],
|
||||
[UnlockPhase, this.startPhase],
|
||||
[PostGameOverPhase, this.startPhase],
|
||||
[RevivalBlessingPhase, this.startPhase],
|
||||
];
|
||||
public log: PhaseString[] = [];
|
||||
/**
|
||||
* The interceptor's current state.
|
||||
* Possible values are the following:
|
||||
* - `running`: The interceptor is currently running a phase.
|
||||
* - `interrupted`: The interceptor has been interrupted by a UI prompt and is waiting for the caller to end it.
|
||||
* - `idling`: The interceptor is not currently running a phase and is ready to start a new one.
|
||||
*/
|
||||
private state: StateType = "idling";
|
||||
|
||||
private endBySetMode = [
|
||||
TitlePhase,
|
||||
SelectGenderPhase,
|
||||
CommandPhase,
|
||||
SelectModifierPhase,
|
||||
MysteryEncounterPhase,
|
||||
PostMysteryEncounterPhase,
|
||||
];
|
||||
private target: PhaseString;
|
||||
|
||||
/**
|
||||
* Constructor to initialize the scene and properties, and to start the phase handling.
|
||||
* @param scene - The scene to be managed.
|
||||
* @param scene - The scene to be managed
|
||||
*/
|
||||
constructor(scene) {
|
||||
constructor(scene: BattleScene) {
|
||||
this.scene = scene;
|
||||
this.onHold = [];
|
||||
this.prompts = [];
|
||||
this.clearLogs();
|
||||
this.startPromptHandler();
|
||||
this.initPhases();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears phase logs
|
||||
*/
|
||||
clearLogs() {
|
||||
this.log = [];
|
||||
}
|
||||
|
||||
rejectAll(error) {
|
||||
if (this.inProgress) {
|
||||
clearInterval(this.promptInterval);
|
||||
clearInterval(this.interval);
|
||||
clearInterval(this.intervalRun);
|
||||
this.inProgress.onError(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to set the starting phase.
|
||||
* @param phaseFrom - The phase to start from.
|
||||
* @returns The instance of the PhaseInterceptor.
|
||||
*/
|
||||
runFrom(phaseFrom: PhaseInterceptorPhase): PhaseInterceptor {
|
||||
this.phaseFrom = phaseFrom;
|
||||
return this;
|
||||
// Mock the private startCurrentPhase method to toggle `isRunning` rather than actually starting anything
|
||||
vi.spyOn(this.scene.phaseManager as any, "startCurrentPhase").mockImplementation(() => {
|
||||
this.state = "idling";
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to transition to a target phase.
|
||||
* @param phaseTo - The phase to transition to.
|
||||
* @param runTarget - Whether or not to run the target phase; default `true`.
|
||||
* @returns A promise that resolves when the transition is complete.
|
||||
* @param target - The name of the {@linkcode Phase} to transition to
|
||||
* @param runTarget - Whether or not to run the target phase before resolving; default `true`
|
||||
* @returns A Promise that resolves once {@linkcode target} has been reached.
|
||||
* @todo remove `Constructor` from type signature in favor of phase strings
|
||||
* @remarks
|
||||
* This will not resolve for *any* reason until the target phase has been reached.
|
||||
* @example
|
||||
* await game.phaseInterceptor.to("MoveEffectPhase", false);
|
||||
*/
|
||||
async to(phaseTo: PhaseInterceptorPhase, runTarget = true): Promise<void> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
ErrorInterceptor.getInstance().add(this);
|
||||
if (this.phaseFrom) {
|
||||
await this.run(this.phaseFrom).catch(e => reject(e));
|
||||
this.phaseFrom = null;
|
||||
}
|
||||
const targetName = typeof phaseTo === "string" ? phaseTo : phaseTo.name;
|
||||
this.intervalRun = setInterval(async () => {
|
||||
const currentPhase = this.onHold?.length && this.onHold[0];
|
||||
if (currentPhase && currentPhase.name === targetName) {
|
||||
clearInterval(this.intervalRun);
|
||||
if (!runTarget) {
|
||||
return resolve();
|
||||
public async to(target: PhaseString | Constructor<Phase>, runTarget = true): Promise<void> {
|
||||
this.target = typeof target === "string" ? target : (target.name as PhaseString);
|
||||
|
||||
const pm = this.scene.phaseManager;
|
||||
|
||||
// TODO: remove bangs once signature is updated
|
||||
let currentPhase: Phase = pm.getCurrentPhase()!;
|
||||
|
||||
let didLog = false;
|
||||
|
||||
// NB: This has to use an interval to wait for UI prompts to activate.
|
||||
// TODO: Rework after UI rework
|
||||
await vi.waitUntil(
|
||||
async () => {
|
||||
// If we were interrupted by a UI prompt, we assume that the calling code will queue inputs to
|
||||
// end the current phase manually, so we just wait for the phase to end from the caller.
|
||||
if (this.state === "interrupted") {
|
||||
if (!didLog) {
|
||||
this.doLog("PhaseInterceptor.to: Waiting for phase to end after being interrupted!");
|
||||
didLog = true;
|
||||
}
|
||||
await this.run(currentPhase).catch(e => {
|
||||
clearInterval(this.intervalRun);
|
||||
return reject(e);
|
||||
});
|
||||
return resolve();
|
||||
return false;
|
||||
}
|
||||
if (currentPhase && currentPhase.name !== targetName) {
|
||||
await this.run(currentPhase).catch(e => {
|
||||
clearInterval(this.intervalRun);
|
||||
return reject(e);
|
||||
});
|
||||
|
||||
currentPhase = pm.getCurrentPhase()!;
|
||||
// TODO: Remove proof-of-concept error throw after signature update
|
||||
if (!currentPhase) {
|
||||
throw new Error("currentPhase is null after being started!");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (currentPhase.is(this.target)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Current phase is different; run and wait for it to finish.
|
||||
await this.run(currentPhase);
|
||||
return false;
|
||||
},
|
||||
{ interval: 0, timeout: 20_000 },
|
||||
);
|
||||
|
||||
// We hit the target; run as applicable and wrap up.
|
||||
if (!runTarget) {
|
||||
this.doLog(`PhaseInterceptor.to: Stopping before running ${this.target}`);
|
||||
return;
|
||||
}
|
||||
|
||||
await this.run(currentPhase);
|
||||
this.doLog(
|
||||
`PhaseInterceptor.to: Stopping ${this.state === "interrupted" ? `after reaching UiMode.${UiMode[this.scene.ui.getMode()]} during` : "on completion of"} ${this.target}`,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to run a phase with an optional skip function.
|
||||
* @param phaseTarget - The phase to run.
|
||||
* @param skipFn - Optional skip function.
|
||||
* @returns A promise that resolves when the phase is run.
|
||||
* Internal wrapper method to start a phase and wait until it finishes.
|
||||
* @param currentPhase - The {@linkcode Phase} to run
|
||||
* @returns A Promise that resolves when the phase has completed running.
|
||||
*/
|
||||
run(phaseTarget: PhaseInterceptorPhase, skipFn?: (className: PhaseClass) => boolean): Promise<void> {
|
||||
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
|
||||
this.scene.moveAnimations = null; // Mandatory to avoid crash
|
||||
return new Promise(async (resolve, reject) => {
|
||||
ErrorInterceptor.getInstance().add(this);
|
||||
const interval = setInterval(async () => {
|
||||
const currentPhase = this.onHold.shift();
|
||||
if (currentPhase) {
|
||||
if (currentPhase.name !== targetName) {
|
||||
clearInterval(interval);
|
||||
const skip = skipFn?.(currentPhase.name);
|
||||
if (skip) {
|
||||
this.onHold.unshift(currentPhase);
|
||||
ErrorInterceptor.getInstance().remove(this);
|
||||
return resolve();
|
||||
}
|
||||
clearInterval(interval);
|
||||
return reject(`Wrong phase: this is ${currentPhase.name} and not ${targetName}`);
|
||||
}
|
||||
clearInterval(interval);
|
||||
this.inProgress = {
|
||||
name: currentPhase.name,
|
||||
callback: () => {
|
||||
ErrorInterceptor.getInstance().remove(this);
|
||||
resolve();
|
||||
},
|
||||
onError: error => reject(error),
|
||||
};
|
||||
currentPhase.call();
|
||||
}
|
||||
});
|
||||
});
|
||||
private async run(currentPhase: Phase): Promise<void> {
|
||||
try {
|
||||
this.state = "running";
|
||||
this.logPhase(currentPhase.phaseName);
|
||||
currentPhase.start();
|
||||
await vi.waitUntil(
|
||||
() => this.state !== "running",
|
||||
{ interval: 50, timeout: 20_000 }, // TODO: Figure out an appropriate timeout for individual phases
|
||||
);
|
||||
} catch (error) {
|
||||
throw error instanceof Error
|
||||
? error
|
||||
: new Error(
|
||||
`Unknown error occurred while running phase ${currentPhase.phaseName}!\nError: ${format("%O", error)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
whenAboutToRun(phaseTarget: PhaseInterceptorPhase, _skipFn?: (className: PhaseClass) => boolean): Promise<void> {
|
||||
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
|
||||
this.scene.moveAnimations = null; // Mandatory to avoid crash
|
||||
return new Promise(async (resolve, _reject) => {
|
||||
ErrorInterceptor.getInstance().add(this);
|
||||
const interval = setInterval(async () => {
|
||||
const currentPhase = this.onHold[0];
|
||||
if (currentPhase?.name === targetName) {
|
||||
clearInterval(interval);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
/**
|
||||
* If this is at the target phase, unlock the interceptor and
|
||||
* return control back to the caller once the calling phase has finished.
|
||||
* @remarks
|
||||
* This should not be called by anything other than {@linkcode PromptHandler}.
|
||||
*/
|
||||
public checkMode(): void {
|
||||
const currentPhase = this.scene.phaseManager.getCurrentPhase()!;
|
||||
if (!currentPhase.is(this.target) || this.state === "interrupted") {
|
||||
// Wrong phase / already interrupted = do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
// Interrupt the phase and return control to the caller
|
||||
this.state = "interrupted";
|
||||
}
|
||||
|
||||
pop() {
|
||||
this.onHold.pop();
|
||||
/**
|
||||
* Skip the next upcoming phase.
|
||||
* @throws Error if currently running a phase.
|
||||
* @remarks
|
||||
* This function should be used for skipping phases _not yet started_.
|
||||
* To end ones already in the process of running, use {@linkcode GameManager.endPhase}.
|
||||
* @example
|
||||
* await game.phaseInterceptor.to("LoginPhase", false);
|
||||
* game.phaseInterceptor.shiftPhase();
|
||||
*/
|
||||
public shiftPhase(): void {
|
||||
const phaseName = this.scene.phaseManager.getCurrentPhase()!.phaseName;
|
||||
if (this.state !== "idling") {
|
||||
throw new Error(`shiftPhase attempted to skip phase ${phaseName} mid-execution!`);
|
||||
}
|
||||
this.doLog(`Skipping current phase ${phaseName}`);
|
||||
this.scene.phaseManager.shiftPhase();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the current phase from the phase interceptor.
|
||||
* Deprecated no-op function.
|
||||
*
|
||||
* Do not call this unless absolutely necessary. This function is intended
|
||||
* for cleaning up the phase interceptor when, for whatever reason, a phase
|
||||
* is manually ended without using the phase interceptor.
|
||||
*
|
||||
* @param shouldRun Whether or not the current scene should also be run.
|
||||
* This was previously used to reset timers created using `setInterval` to wait for phase end
|
||||
* and undo various method stubs upon a test ending. \
|
||||
* However, since we now use {@linkcode vi.waitUntil} and {@linkcode vi.spyOn} to perform these tasks
|
||||
* respectively, this function has become no longer needed.
|
||||
* @deprecated This is no longer needed and will be removed in a future PR
|
||||
*/
|
||||
shift(shouldRun = false): void {
|
||||
this.onHold.shift();
|
||||
if (shouldRun) {
|
||||
this.scene.phaseManager.shiftPhase();
|
||||
public restoreOg() {}
|
||||
|
||||
/**
|
||||
* Method to log the start of a phase.
|
||||
* @param phaseName - The name of the phase to log.
|
||||
*/
|
||||
private logPhase(phaseName: PhaseString) {
|
||||
if (!blacklistedPhaseNames.has(phaseName)) {
|
||||
this.doLog(`Starting Phase: ${phaseName}`);
|
||||
}
|
||||
this.log.push(phaseName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to initialize phases and their corresponding methods.
|
||||
* Clear all prior phase logs.
|
||||
*/
|
||||
initPhases() {
|
||||
this.originalSetMode = UI.prototype.setMode;
|
||||
this.originalSetOverlayMode = UI.prototype.setOverlayMode;
|
||||
this.originalSuperEnd = Phase.prototype.end;
|
||||
UI.prototype.setMode = (mode, ...args) => this.setMode.call(this, mode, ...args);
|
||||
Phase.prototype.end = () => this.superEndPhase.call(this);
|
||||
for (const [phase, methodStart] of this.PHASES) {
|
||||
const originalStart = phase.prototype.start;
|
||||
this.phases[phase.name] = {
|
||||
start: originalStart,
|
||||
endBySetMode: this.endBySetMode.some(elm => elm.name === phase.name),
|
||||
};
|
||||
phase.prototype.start = () => methodStart.call(this, phase);
|
||||
}
|
||||
public clearLogs(): void {
|
||||
this.log = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to start a phase and log it.
|
||||
* @param phase - The phase to start.
|
||||
* Wrapper function to add coral coloration to phase logs.
|
||||
* @param args - Arguments to original logging function.
|
||||
*/
|
||||
startPhase(phase: PhaseClass) {
|
||||
this.log.push(phase.name);
|
||||
const instance = this.scene.phaseManager.getCurrentPhase();
|
||||
this.onHold.push({
|
||||
name: phase.name,
|
||||
call: () => {
|
||||
this.phases[phase.name].start.apply(instance);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
unlock() {
|
||||
this.inProgress?.callback();
|
||||
this.inProgress = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to end a phase and log it.
|
||||
* @param phase - The phase to start.
|
||||
*/
|
||||
superEndPhase() {
|
||||
const instance = this.scene.phaseManager.getCurrentPhase();
|
||||
this.originalSuperEnd.apply(instance);
|
||||
this.inProgress?.callback();
|
||||
this.inProgress = undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* m2m to set mode.
|
||||
* @param mode - The {@linkcode UiMode} to set.
|
||||
* @param args - Additional arguments to pass to the original method.
|
||||
*/
|
||||
setMode(mode: UiMode, ...args: unknown[]): Promise<void> {
|
||||
const currentPhase = this.scene.phaseManager.getCurrentPhase();
|
||||
const instance = this.scene.ui;
|
||||
console.log("setMode", `${UiMode[mode]} (=${mode})`, args);
|
||||
const ret = this.originalSetMode.apply(instance, [mode, ...args]);
|
||||
if (!this.phases[currentPhase.constructor.name]) {
|
||||
throw new Error(
|
||||
`missing ${currentPhase.constructor.name} in phaseInterceptor PHASES list --- Add it to PHASES inside of /test/utils/phaseInterceptor.ts`,
|
||||
);
|
||||
}
|
||||
if (this.phases[currentPhase.constructor.name].endBySetMode) {
|
||||
this.inProgress?.callback();
|
||||
this.inProgress = undefined;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* mock to set overlay mode
|
||||
* @param mode - The {@linkcode Mode} to set.
|
||||
* @param args - Additional arguments to pass to the original method.
|
||||
*/
|
||||
setOverlayMode(mode: UiMode, ...args: unknown[]): Promise<void> {
|
||||
const instance = this.scene.ui;
|
||||
console.log("setOverlayMode", `${UiMode[mode]} (=${mode})`, args);
|
||||
const ret = this.originalSetOverlayMode.apply(instance, [mode, ...args]);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to start the prompt handler.
|
||||
*/
|
||||
startPromptHandler() {
|
||||
this.promptInterval = setInterval(() => {
|
||||
if (this.prompts.length) {
|
||||
const actionForNextPrompt = this.prompts[0];
|
||||
const expireFn = actionForNextPrompt.expireFn?.();
|
||||
const currentMode = this.scene.ui.getMode();
|
||||
const currentPhase = this.scene.phaseManager.getCurrentPhase()?.constructor.name;
|
||||
const currentHandler = this.scene.ui.getHandler();
|
||||
if (expireFn) {
|
||||
this.prompts.shift();
|
||||
} else if (
|
||||
currentMode === actionForNextPrompt.mode &&
|
||||
currentPhase === actionForNextPrompt.phaseTarget &&
|
||||
currentHandler.active &&
|
||||
(!actionForNextPrompt.awaitingActionInput ||
|
||||
(actionForNextPrompt.awaitingActionInput && currentHandler.awaitingActionInput))
|
||||
) {
|
||||
const prompt = this.prompts.shift();
|
||||
if (prompt?.callback) {
|
||||
prompt.callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method to add an action to the next prompt.
|
||||
* @param phaseTarget - The target phase for the prompt.
|
||||
* @param mode - The mode of the UI.
|
||||
* @param callback - The callback function to execute.
|
||||
* @param expireFn - The function to determine if the prompt has expired.
|
||||
* @param awaitingActionInput - ???; default `false`
|
||||
*/
|
||||
addToNextPrompt(
|
||||
phaseTarget: string,
|
||||
mode: UiMode,
|
||||
callback: () => void,
|
||||
expireFn?: () => void,
|
||||
awaitingActionInput = false,
|
||||
) {
|
||||
this.prompts.push({
|
||||
phaseTarget,
|
||||
mode,
|
||||
callback,
|
||||
expireFn,
|
||||
awaitingActionInput,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores the original state of phases and clears intervals.
|
||||
*
|
||||
* This function iterates through all phases and resets their `start` method to the original
|
||||
* function stored in `this.phases`. Additionally, it clears the `promptInterval` and `interval`.
|
||||
*/
|
||||
restoreOg() {
|
||||
for (const [phase] of this.PHASES) {
|
||||
phase.prototype.start = this.phases[phase.name].start;
|
||||
}
|
||||
UI.prototype.setMode = this.originalSetMode;
|
||||
UI.prototype.setOverlayMode = this.originalSetOverlayMode;
|
||||
Phase.prototype.end = this.originalSuperEnd;
|
||||
clearInterval(this.promptInterval);
|
||||
clearInterval(this.interval);
|
||||
clearInterval(this.intervalRun);
|
||||
private doLog(...args: unknown[]): void {
|
||||
console.log(chalk.hex("#ff7f50")(...args));
|
||||
}
|
||||
}
|
||||
|
153
test/test-utils/tests/helpers/prompt-handler.test.ts
Normal file
153
test/test-utils/tests/helpers/prompt-handler.test.ts
Normal file
@ -0,0 +1,153 @@
|
||||
import type { PhaseString } from "#app/@types/phase-types";
|
||||
import type { Phase } from "#app/phase";
|
||||
import type { AwaitableUiHandler } from "#app/ui/awaitable-ui-handler";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import type { GameManager } from "#test/test-utils/game-manager";
|
||||
import { PromptHandler } from "#test/test-utils/helpers/prompt-handler";
|
||||
import type { PhaseInterceptor } from "#test/test-utils/phase-interceptor";
|
||||
import type { UI } from "#ui/ui";
|
||||
import { beforeAll, beforeEach, describe, expect, it, type Mock, vi } from "vitest";
|
||||
|
||||
describe("Test Utils - PromptHandler", () => {
|
||||
let promptHandler: PromptHandler;
|
||||
let handler: AwaitableUiHandler;
|
||||
|
||||
let callback1: Mock;
|
||||
let callback2: Mock;
|
||||
let setModeCallback: Mock;
|
||||
let checkModeCallback: Mock;
|
||||
|
||||
beforeAll(() => {
|
||||
setModeCallback = vi.fn();
|
||||
checkModeCallback = vi.fn();
|
||||
callback1 = vi.fn(() => console.log("callback 1 called!")).mockName("callback 1");
|
||||
callback2 = vi.fn(() => console.log("callback 2 called!")).mockName("callback 2");
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
handler = {
|
||||
active: true,
|
||||
show: () => {},
|
||||
awaitingActionInput: true,
|
||||
} as unknown as AwaitableUiHandler;
|
||||
|
||||
promptHandler = new PromptHandler({
|
||||
scene: {
|
||||
ui: {
|
||||
getHandler: () => handler,
|
||||
setModeInternal: () => {
|
||||
setModeCallback();
|
||||
return Promise.resolve();
|
||||
},
|
||||
getMode: () => UiMode.TEST_DIALOGUE,
|
||||
} as unknown as UI,
|
||||
phaseManager: {
|
||||
getCurrentPhase: () =>
|
||||
({
|
||||
phaseName: "CommandPhase",
|
||||
}) as unknown as Phase,
|
||||
},
|
||||
},
|
||||
phaseInterceptor: {
|
||||
checkMode: () => {
|
||||
checkModeCallback();
|
||||
},
|
||||
} as PhaseInterceptor,
|
||||
} as GameManager);
|
||||
});
|
||||
|
||||
function onNextPrompt(
|
||||
target: string,
|
||||
mode: UiMode,
|
||||
callback: () => void,
|
||||
expireFn?: () => boolean,
|
||||
awaitingActionInput = false,
|
||||
) {
|
||||
promptHandler.addToNextPrompt(target as unknown as PhaseString, mode, callback, expireFn, awaitingActionInput);
|
||||
}
|
||||
|
||||
describe("setMode", () => {
|
||||
it("should wrap and pass along original function arguments from setModeInternal", async () => {
|
||||
const setModeSpy = vi.spyOn(promptHandler as any, "setMode");
|
||||
promptHandler["game"].scene.ui["setModeInternal"](UiMode.PARTY, false, false, false, []);
|
||||
|
||||
expect(setModeSpy).toHaveBeenCalledExactlyOnceWith([UiMode.PARTY, false, false, false, []]);
|
||||
expect(setModeCallback).toHaveBeenCalledAfter(setModeSpy);
|
||||
});
|
||||
|
||||
it("should call PhaseInterceptor.checkMode if current phase in `endBySetMode`", async () => {
|
||||
promptHandler["game"].scene.ui["setModeInternal"](UiMode.PARTY, false, false, false, []);
|
||||
|
||||
expect(checkModeCallback).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
|
||||
describe("doPromptCheck", () => {
|
||||
it("should check and remove the first prompt", async () => {
|
||||
onNextPrompt("testDialoguePhase", UiMode.TEST_DIALOGUE, () => callback1());
|
||||
onNextPrompt("testDialoguePhase", UiMode.TEST_DIALOGUE, () => callback2());
|
||||
promptHandler["doPromptCheck"]();
|
||||
|
||||
expect(callback1).toHaveBeenCalled();
|
||||
expect(callback2).not.toHaveBeenCalled();
|
||||
expect(promptHandler["prompts"]).toHaveLength(1);
|
||||
});
|
||||
|
||||
it.each<{ reason: string; callback: () => void }>([
|
||||
{
|
||||
reason: "wrong UI mode",
|
||||
callback: () => onNextPrompt("testDialoguePhase", UiMode.ACHIEVEMENTS, () => callback1()),
|
||||
},
|
||||
{
|
||||
reason: "wrong phase",
|
||||
callback: () => onNextPrompt("wrong phase", UiMode.TEST_DIALOGUE, () => callback1()),
|
||||
},
|
||||
{
|
||||
reason: "UI handler is inactive",
|
||||
callback: () => {
|
||||
handler.active = false;
|
||||
onNextPrompt("testDialoguePhase", UiMode.TEST_DIALOGUE, () => callback1());
|
||||
},
|
||||
},
|
||||
{
|
||||
reason: "UI handler is not awaiting input",
|
||||
callback: () => {
|
||||
handler["awaitingActionInput"] = false;
|
||||
onNextPrompt("testDialoguePhase", UiMode.TEST_DIALOGUE, () => callback1(), undefined, true);
|
||||
},
|
||||
},
|
||||
])("should skip callback and keep in queue if $reason", async ({ callback }) => {
|
||||
callback();
|
||||
onNextPrompt("testDialoguePhase", UiMode.TEST_DIALOGUE, () => callback2);
|
||||
promptHandler["doPromptCheck"]();
|
||||
|
||||
expect(callback1).not.toHaveBeenCalled();
|
||||
expect(callback2).not.toHaveBeenCalled();
|
||||
expect(promptHandler["prompts"]).toHaveLength(2);
|
||||
});
|
||||
|
||||
it("should remove expired prompts without blocking", async () => {
|
||||
onNextPrompt(
|
||||
"testDialoguePhase",
|
||||
UiMode.TEST_DIALOGUE,
|
||||
() => callback1(),
|
||||
() => true,
|
||||
);
|
||||
onNextPrompt(
|
||||
"testDialoguePhase",
|
||||
UiMode.TEST_DIALOGUE,
|
||||
() => callback2(),
|
||||
() => false,
|
||||
);
|
||||
promptHandler["doPromptCheck"]();
|
||||
|
||||
expect(callback1).not.toHaveBeenCalled();
|
||||
expect(callback2).not.toHaveBeenCalled();
|
||||
expect(promptHandler["prompts"]).toHaveLength(1);
|
||||
|
||||
promptHandler["doPromptCheck"]();
|
||||
expect(callback2).toHaveBeenCalledOnce();
|
||||
expect(promptHandler["prompts"]).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
});
|
62
test/test-utils/tests/phase-interceptor/integration.test.ts
Normal file
62
test/test-utils/tests/phase-interceptor/integration.test.ts
Normal file
@ -0,0 +1,62 @@
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import Phaser from "phaser";
|
||||
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
|
||||
describe("Utils - Phase Interceptor - Integration", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
});
|
||||
|
||||
it("runToTitle", async () => {
|
||||
await game.runToTitle();
|
||||
|
||||
expect(game.scene.ui.getMode()).toBe(UiMode.TITLE);
|
||||
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("TitlePhase");
|
||||
});
|
||||
|
||||
it("runToSummon", async () => {
|
||||
await game.classicMode.runToSummon([SpeciesId.ABOMASNOW]);
|
||||
|
||||
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("SummonPhase");
|
||||
});
|
||||
|
||||
it("startBattle", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.RABOOT]);
|
||||
|
||||
expect(game.scene.ui.getMode()).toBe(UiMode.COMMAND);
|
||||
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
|
||||
});
|
||||
|
||||
it("1 Full Turn", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
|
||||
|
||||
game.move.use(MoveId.SPLASH);
|
||||
await game.move.forceEnemyMove(MoveId.SPLASH);
|
||||
await game.toNextTurn();
|
||||
|
||||
expect(game.scene.ui.getMode()).toBe(UiMode.COMMAND);
|
||||
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
|
||||
});
|
||||
|
||||
it("should not break when phase ended early via prompt", async () => {
|
||||
await game.classicMode.startBattle([SpeciesId.REGIELEKI]);
|
||||
game.onNextPrompt("CommandPhase", UiMode.COMMAND, () => {
|
||||
game.endPhase();
|
||||
});
|
||||
|
||||
game.move.use(MoveId.BOUNCE);
|
||||
await game.phaseInterceptor.to("EnemyCommandPhase");
|
||||
});
|
||||
});
|
150
test/test-utils/tests/phase-interceptor/unit.test.ts
Normal file
150
test/test-utils/tests/phase-interceptor/unit.test.ts
Normal file
@ -0,0 +1,150 @@
|
||||
import type { PhaseString } from "#app/@types/phase-types";
|
||||
import { globalScene } from "#app/global-scene";
|
||||
import type { Phase } from "#app/phase";
|
||||
import type { Constructor } from "#app/utils/common";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import { mockPhase } from "#test/test-utils/mocks/mock-phase";
|
||||
import Phaser from "phaser";
|
||||
import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
// TODO: Move these to `mock-phase.ts` if/when unit tests for the phase manager are created
|
||||
class applePhase extends mockPhase {
|
||||
public readonly phaseName = "applePhase";
|
||||
}
|
||||
|
||||
class bananaPhase extends mockPhase {
|
||||
public readonly phaseName = "bananaPhase";
|
||||
}
|
||||
|
||||
class coconutPhase extends mockPhase {
|
||||
public readonly phaseName = "coconutPhase";
|
||||
}
|
||||
|
||||
class oneSecTimerPhase extends mockPhase {
|
||||
public readonly phaseName = "oneSecTimerPhase";
|
||||
start() {
|
||||
setTimeout(() => {
|
||||
console.log("1 sec passed!");
|
||||
this.end();
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
class unshifterPhase extends mockPhase {
|
||||
public readonly phaseName = "unshifterPhase";
|
||||
start() {
|
||||
globalScene.phaseManager.unshiftPhase(new applePhase() as unknown as Phase);
|
||||
globalScene.phaseManager.unshiftPhase(new bananaPhase() as unknown as Phase);
|
||||
globalScene.phaseManager.unshiftPhase(new coconutPhase() as unknown as Phase);
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
|
||||
describe("Utils - Phase Interceptor - Unit", () => {
|
||||
let phaserGame: Phaser.Game;
|
||||
let game: GameManager;
|
||||
|
||||
beforeAll(() => {
|
||||
phaserGame = new Phaser.Game({
|
||||
type: Phaser.HEADLESS,
|
||||
});
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
game = new GameManager(phaserGame);
|
||||
setPhases(applePhase, bananaPhase, coconutPhase, bananaPhase, coconutPhase);
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function to set the phase manager's phases to the specified values and start the first one.
|
||||
* @param phases - An array of constructors to {@linkcode Phase}s to set.
|
||||
* Constructors must have no arguments.
|
||||
*/
|
||||
function setPhases(phase: Constructor<mockPhase>, ...phases: Constructor<mockPhase>[]) {
|
||||
game.scene.phaseManager.clearAllPhases();
|
||||
game.scene.phaseManager.phaseQueue = [phase, ...phases].map(m => new m()) as Phase[];
|
||||
game.scene.phaseManager.shiftPhase(); // start the thing going
|
||||
}
|
||||
|
||||
function getQueuedPhases(): string[] {
|
||||
return game.scene.phaseManager["phaseQueuePrepend"]
|
||||
.concat(game.scene.phaseManager.phaseQueue)
|
||||
.map(p => p.phaseName);
|
||||
}
|
||||
|
||||
function getCurrentPhaseName(): string {
|
||||
return game.scene.phaseManager.getCurrentPhase()?.phaseName ?? "no phase";
|
||||
}
|
||||
|
||||
/** Wrapper function to make TS not complain about incompatible argument typing on `PhaseString`. */
|
||||
function to(phaseName: string, runTarget = true) {
|
||||
return game.phaseInterceptor.to(phaseName as unknown as PhaseString, runTarget);
|
||||
}
|
||||
|
||||
describe("to", () => {
|
||||
it("should start the specified phase and resolve after it ends", async () => {
|
||||
await to("applePhase");
|
||||
|
||||
expect(getCurrentPhaseName()).toBe("bananaPhase");
|
||||
expect(getQueuedPhases()).toEqual(["coconutPhase", "bananaPhase", "coconutPhase"]);
|
||||
expect(game.phaseInterceptor.log).toEqual(["applePhase"]);
|
||||
});
|
||||
|
||||
it("should run to the specified phase without starting/logging", async () => {
|
||||
await to("applePhase", false);
|
||||
|
||||
expect(getCurrentPhaseName()).toBe("applePhase");
|
||||
expect(getQueuedPhases()).toEqual(["bananaPhase", "coconutPhase", "bananaPhase", "coconutPhase"]);
|
||||
expect(game.phaseInterceptor.log).toEqual([]);
|
||||
|
||||
await to("applePhase", false);
|
||||
|
||||
// should not do anything
|
||||
expect(getCurrentPhaseName()).toBe("applePhase");
|
||||
expect(getQueuedPhases()).toEqual(["bananaPhase", "coconutPhase", "bananaPhase", "coconutPhase"]);
|
||||
expect(game.phaseInterceptor.log).toEqual([]);
|
||||
});
|
||||
|
||||
it("should run all phases between start and the first instance of target", async () => {
|
||||
await to("coconutPhase");
|
||||
|
||||
expect(getCurrentPhaseName()).toBe("bananaPhase");
|
||||
expect(getQueuedPhases()).toEqual(["coconutPhase"]);
|
||||
expect(game.phaseInterceptor.log).toEqual(["applePhase", "bananaPhase", "coconutPhase"]);
|
||||
});
|
||||
|
||||
it("should work on newly unshifted phases", async () => {
|
||||
setPhases(unshifterPhase, coconutPhase); // adds applePhase, bananaPhase and coconutPhase to queue
|
||||
await to("bananaPhase");
|
||||
|
||||
expect(getCurrentPhaseName()).toBe("coconutPhase");
|
||||
expect(getQueuedPhases()).toEqual(["coconutPhase"]);
|
||||
expect(game.phaseInterceptor.log).toEqual(["unshifterPhase", "applePhase", "bananaPhase"]);
|
||||
});
|
||||
|
||||
it("should wait for asynchronous phases to end", async () => {
|
||||
setPhases(oneSecTimerPhase, coconutPhase);
|
||||
const callback = vi.fn(() => console.log("fffffff"));
|
||||
const spy = vi.spyOn(oneSecTimerPhase.prototype, "end");
|
||||
setTimeout(() => {
|
||||
callback();
|
||||
}, 500);
|
||||
await to("coconutPhase");
|
||||
expect(callback).toHaveBeenCalled();
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe("shift", () => {
|
||||
it("should skip the next phase in line without starting it", async () => {
|
||||
const startSpy = vi.spyOn(applePhase.prototype, "start");
|
||||
|
||||
game.phaseInterceptor.shiftPhase();
|
||||
|
||||
expect(getCurrentPhaseName()).toBe("bananaPhase");
|
||||
expect(getQueuedPhases()).toEqual(["coconutPhase", "bananaPhase", "coconutPhase"]);
|
||||
expect(startSpy).not.toHaveBeenCalled();
|
||||
expect(game.phaseInterceptor.log).toEqual([]);
|
||||
});
|
||||
});
|
||||
});
|
@ -69,7 +69,7 @@ describe("UI - Pokedex", () => {
|
||||
// Open the pokedex UI.
|
||||
await game.runToTitle();
|
||||
|
||||
await game.phaseInterceptor.setOverlayMode(UiMode.POKEDEX);
|
||||
await game.scene.ui.setOverlayMode(UiMode.POKEDEX);
|
||||
|
||||
// Get the handler for the current UI.
|
||||
const handler = game.scene.ui.getHandler();
|
||||
@ -89,7 +89,7 @@ describe("UI - Pokedex", () => {
|
||||
// Open the pokedex UI.
|
||||
await game.runToTitle();
|
||||
|
||||
await game.phaseInterceptor.setOverlayMode(UiMode.POKEDEX_PAGE, species, starterAttributes);
|
||||
await game.scene.ui.setOverlayMode(UiMode.POKEDEX_PAGE, species, starterAttributes);
|
||||
|
||||
// Get the handler for the current UI.
|
||||
const handler = game.scene.ui.getHandler();
|
||||
|
@ -6,8 +6,6 @@ import { GameModes } from "#enums/game-modes";
|
||||
import { Nature } from "#enums/nature";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { EncounterPhase } from "#phases/encounter-phase";
|
||||
import { SelectStarterPhase } from "#phases/select-starter-phase";
|
||||
import type { TitlePhase } from "#phases/title-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
|
||||
@ -54,9 +52,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.LEFT);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -88,7 +85,7 @@ describe("UI - Starter select", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||
@ -115,9 +112,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.LEFT);
|
||||
handler.processInput(Button.CYCLE_GENDER);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -149,7 +145,7 @@ describe("UI - Starter select", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||
@ -179,9 +175,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.CYCLE_NATURE);
|
||||
handler.processInput(Button.CYCLE_ABILITY);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -213,7 +208,7 @@ describe("UI - Starter select", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||
@ -242,9 +237,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.LEFT);
|
||||
handler.processInput(Button.CYCLE_GENDER);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -276,7 +270,7 @@ describe("UI - Starter select", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||
@ -303,9 +297,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.LEFT);
|
||||
handler.processInput(Button.ACTION);
|
||||
handler.processInput(Button.CYCLE_SHINY);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -337,7 +330,7 @@ describe("UI - Starter select", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(false);
|
||||
@ -365,9 +358,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.CYCLE_SHINY);
|
||||
handler.processInput(Button.CYCLE_SHINY);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -399,7 +391,7 @@ describe("UI - Starter select", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||
@ -426,9 +418,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.CYCLE_SHINY);
|
||||
handler.processInput(Button.CYCLE_SHINY);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -460,7 +451,7 @@ describe("UI - Starter select", () => {
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||
@ -486,9 +477,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -527,7 +517,7 @@ describe("UI - Starter select", () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.CATERPIE);
|
||||
});
|
||||
|
||||
@ -551,9 +541,8 @@ describe("UI - Starter select", () => {
|
||||
handler.processInput(Button.RIGHT);
|
||||
handler.processInput(Button.DOWN);
|
||||
handler.processInput(Button.ACTION);
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
||||
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||
let options: OptionSelectItem[] = [];
|
||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||
await new Promise<void>(resolve => {
|
||||
@ -593,7 +582,7 @@ describe("UI - Starter select", () => {
|
||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.whenAboutToRun(EncounterPhase);
|
||||
await game.phaseInterceptor.to("EncounterPhase", false);
|
||||
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.NIDORAN_M);
|
||||
});
|
||||
});
|
||||
|
@ -72,8 +72,6 @@ describe("UI - Transfer Items", () => {
|
||||
expect(
|
||||
handler.optionsContainer.list.some(option => RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text)),
|
||||
).toBe(true);
|
||||
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
@ -93,8 +91,6 @@ describe("UI - Transfer Items", () => {
|
||||
expect(handler.optionsContainer.list.some(option => (option as BBCodeText).text?.includes("Transfer"))).toBe(
|
||||
true,
|
||||
);
|
||||
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
|
||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||
|
@ -2,7 +2,6 @@ import { Button } from "#enums/buttons";
|
||||
import { MoveId } from "#enums/move-id";
|
||||
import { SpeciesId } from "#enums/species-id";
|
||||
import { UiMode } from "#enums/ui-mode";
|
||||
import { CommandPhase } from "#phases/command-phase";
|
||||
import { GameManager } from "#test/test-utils/game-manager";
|
||||
import type { MockText } from "#test/test-utils/mocks/mocks-container/mock-text";
|
||||
import { FightUiHandler } from "#ui/fight-ui-handler";
|
||||
@ -46,7 +45,6 @@ describe("UI - Type Hints", () => {
|
||||
const { ui } = game.scene;
|
||||
const handler = ui.getHandler<FightUiHandler>();
|
||||
handler.processInput(Button.ACTION); // select "Fight"
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
|
||||
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
||||
@ -59,7 +57,7 @@ describe("UI - Type Hints", () => {
|
||||
expect.soft(dragonClawText.color).toBe("#929292");
|
||||
ui.getHandler().processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
await game.phaseInterceptor.to("CommandPhase");
|
||||
});
|
||||
|
||||
it("check status move color", async () => {
|
||||
@ -71,7 +69,6 @@ describe("UI - Type Hints", () => {
|
||||
const { ui } = game.scene;
|
||||
const handler = ui.getHandler<FightUiHandler>();
|
||||
handler.processInput(Button.ACTION); // select "Fight"
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
|
||||
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
||||
@ -84,7 +81,7 @@ describe("UI - Type Hints", () => {
|
||||
expect.soft(growlText.color).toBe(undefined);
|
||||
ui.getHandler().processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
await game.phaseInterceptor.to("CommandPhase");
|
||||
});
|
||||
|
||||
it("should show the proper hint for a move in doubles after one of the enemy pokemon flees", async () => {
|
||||
@ -107,7 +104,6 @@ describe("UI - Type Hints", () => {
|
||||
const { ui } = game.scene;
|
||||
const handler = ui.getHandler<FightUiHandler>();
|
||||
handler.processInput(Button.ACTION); // select "Fight"
|
||||
game.phaseInterceptor.unlock();
|
||||
});
|
||||
|
||||
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
||||
@ -121,6 +117,6 @@ describe("UI - Type Hints", () => {
|
||||
expect.soft(shadowBallText.color).toBe(undefined);
|
||||
ui.getHandler().processInput(Button.ACTION);
|
||||
});
|
||||
await game.phaseInterceptor.to(CommandPhase);
|
||||
await game.phaseInterceptor.to("CommandPhase");
|
||||
});
|
||||
});
|
||||
|
@ -1,6 +1,7 @@
|
||||
import "vitest-canvas-mock";
|
||||
import { PromptHandler } from "#test/test-utils/helpers/prompt-handler";
|
||||
import { initTests } from "#test/test-utils/test-file-initialization";
|
||||
import { afterAll, beforeAll, vi } from "vitest";
|
||||
import { afterAll, afterEach, beforeAll, vi } from "vitest";
|
||||
|
||||
/** Set the timezone to UTC for tests. */
|
||||
|
||||
@ -48,8 +49,6 @@ vi.mock("i18next", async importOriginal => {
|
||||
return await importOriginal();
|
||||
});
|
||||
|
||||
global.testFailed = false;
|
||||
|
||||
beforeAll(() => {
|
||||
initTests();
|
||||
});
|
||||
@ -58,3 +57,20 @@ afterAll(() => {
|
||||
global.server.close();
|
||||
console.log("Closing i18n MSW server!");
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
clearInterval(PromptHandler.runInterval);
|
||||
PromptHandler.runInterval = undefined;
|
||||
});
|
||||
|
||||
process.on("uncaughtException", err => {
|
||||
clearInterval(PromptHandler.runInterval);
|
||||
PromptHandler.runInterval = undefined;
|
||||
throw err;
|
||||
});
|
||||
|
||||
process.on("unhandledRejection", err => {
|
||||
clearInterval(PromptHandler.runInterval);
|
||||
PromptHandler.runInterval = undefined;
|
||||
throw err;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user