mirror of
https://github.com/pagefaultgames/pokerogue.git
synced 2025-08-20 22:39:31 +02:00
Compare commits
21 Commits
c6544e887b
...
7cd229b6b5
Author | SHA1 | Date | |
---|---|---|---|
|
7cd229b6b5 | ||
|
f42237d415 | ||
|
b44f0a4176 | ||
|
076ef81691 | ||
|
23271901cf | ||
|
1517e0512e | ||
|
4fa130ad9f | ||
|
907e707b8b | ||
|
8e729d9955 | ||
|
62aea1ad8d | ||
|
99a5f0d9ba | ||
|
69fff96089 | ||
|
675139e3ba | ||
|
d10b1b0a13 | ||
|
3114c1743f | ||
|
0c03cf1df6 | ||
|
571691b19a | ||
|
6dfddea403 | ||
|
5bfcca3b53 | ||
|
5d1e13139c | ||
|
f76d12f430 |
@ -90,9 +90,13 @@ If this feature requires new text, the text should be integrated into the code w
|
|||||||
- For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text.
|
- For any feature pulled from the mainline Pokémon games (e.g. a Move or Ability implementation), it's best practice to include a source link for any added text.
|
||||||
[Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice.
|
[Poké Corpus](https://abcboy101.github.io/poke-corpus/) is a great resource for finding text from the mainline games; otherwise, a video/picture showing the text being displayed should suffice.
|
||||||
- You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response.
|
- You should also [notify the current Head of Translation](#notifying-translation) to ensure a fast response.
|
||||||
3. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes).
|
3. Your locales should use the following format:
|
||||||
4. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
|
- File names should be in `kebab-case`. Example: `trainer-names.json`
|
||||||
5. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment.
|
- Key names should be in `camelCase`. Example: `aceTrainer`
|
||||||
|
- If you make use of i18next's inbuilt [context support](https://www.i18next.com/translation-function/context), you need to use `snake_case` for the context key. Example: `aceTrainer_male`
|
||||||
|
4. At this point, you may begin [testing locales integration in your main PR](#documenting-locales-changes).
|
||||||
|
5. The Translation Team will approve the locale PR (after corrections, if necessary), then merge it into `pokerogue-locales`.
|
||||||
|
6. The Dev Team will approve your main PR for your feature, then merge it into PokéRogue's beta environment.
|
||||||
|
|
||||||
[^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates).
|
[^2]: For those wondering, the reason for choosing English specifically is due to it being the master language set in Pontoon (the program used by the Translation Team to perform locale updates).
|
||||||
If a key is present in any language _except_ the master language, it won't appear anywhere else in the translation tool, rendering missing English keys quite a hassle.
|
If a key is present in any language _except_ the master language, it won't appear anywhere else in the translation tool, rendering missing English keys quite a hassle.
|
||||||
|
@ -3,7 +3,7 @@ import { MoveId } from "#enums/move-id";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { GameManager } from "#test/test-utils/game-manager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
|
|
||||||
describe("{{description}}", () => {
|
describe("{{description}}", () => {
|
||||||
let phaserGame: Phaser.Game;
|
let phaserGame: Phaser.Game;
|
||||||
@ -15,10 +15,6 @@ describe("{{description}}", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
game.phaseInterceptor.restoreOg();
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
game = new GameManager(phaserGame);
|
game = new GameManager(phaserGame);
|
||||||
game.override
|
game.override
|
||||||
|
@ -104,6 +104,7 @@ import {
|
|||||||
getLuckString,
|
getLuckString,
|
||||||
getLuckTextTint,
|
getLuckTextTint,
|
||||||
getPartyLuckValue,
|
getPartyLuckValue,
|
||||||
|
type ModifierType,
|
||||||
PokemonHeldItemModifierType,
|
PokemonHeldItemModifierType,
|
||||||
} from "#modifiers/modifier-type";
|
} from "#modifiers/modifier-type";
|
||||||
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
|
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
|
||||||
@ -397,7 +398,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);
|
this.scene.remove(LoadingScene.KEY);
|
||||||
initGameSpeed.apply(this);
|
initGameSpeed.apply(this);
|
||||||
this.inputController = new InputsController();
|
this.inputController = new InputsController();
|
||||||
@ -422,6 +427,7 @@ export class BattleScene extends SceneBase {
|
|||||||
this.ui?.update();
|
this.ui?.update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Split this up into multiple sub-methods
|
||||||
launchBattle() {
|
launchBattle() {
|
||||||
this.arenaBg = this.add.sprite(0, 0, "plains_bg");
|
this.arenaBg = this.add.sprite(0, 0, "plains_bg");
|
||||||
this.arenaBg.setName("sprite-arena-bg");
|
this.arenaBg.setName("sprite-arena-bg");
|
||||||
@ -602,6 +608,8 @@ export class BattleScene extends SceneBase {
|
|||||||
this.arenaNextEnemy.setVisible(false);
|
this.arenaNextEnemy.setVisible(false);
|
||||||
|
|
||||||
for (const a of [this.arenaPlayer, this.arenaPlayerTransition, this.arenaEnemy, this.arenaNextEnemy]) {
|
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) {
|
if (a instanceof Phaser.GameObjects.Sprite) {
|
||||||
a.setOrigin(0, 0);
|
a.setOrigin(0, 0);
|
||||||
}
|
}
|
||||||
@ -1143,6 +1151,7 @@ export class BattleScene extends SceneBase {
|
|||||||
return this.currentBattle?.randSeedInt(range, min);
|
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 {
|
reset(clearScene = false, clearData = false, reloadI18n = false): void {
|
||||||
if (clearData) {
|
if (clearData) {
|
||||||
this.gameData = new GameData();
|
this.gameData = new GameData();
|
||||||
@ -1203,7 +1212,9 @@ export class BattleScene extends SceneBase {
|
|||||||
this.updateScoreText();
|
this.updateScoreText();
|
||||||
this.scoreText.setVisible(false);
|
this.scoreText.setVisible(false);
|
||||||
|
|
||||||
[this.luckLabelText, this.luckText].map(t => t.setVisible(false));
|
[this.luckLabelText, this.luckText].forEach(t => {
|
||||||
|
t.setVisible(false);
|
||||||
|
});
|
||||||
|
|
||||||
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN);
|
this.newArena(Overrides.STARTING_BIOME_OVERRIDE || BiomeId.TOWN);
|
||||||
|
|
||||||
@ -1237,8 +1248,7 @@ export class BattleScene extends SceneBase {
|
|||||||
Object.values(mp)
|
Object.values(mp)
|
||||||
.flat()
|
.flat()
|
||||||
.map(mt => mt.modifierType)
|
.map(mt => mt.modifierType)
|
||||||
.filter(mt => "localize" in mt)
|
.filter((mt): mt is ModifierType & Localizable => "localize" in mt && typeof mt.localize === "function"),
|
||||||
.map(lpb => lpb as unknown as Localizable),
|
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
for (const item of localizable) {
|
for (const item of localizable) {
|
||||||
@ -1513,8 +1523,8 @@ export class BattleScene extends SceneBase {
|
|||||||
return this.currentBattle;
|
return this.currentBattle;
|
||||||
}
|
}
|
||||||
|
|
||||||
newArena(biome: BiomeId, playerFaints?: number): Arena {
|
newArena(biome: BiomeId, playerFaints = 0): Arena {
|
||||||
this.arena = new Arena(biome, BiomeId[biome].toLowerCase(), playerFaints);
|
this.arena = new Arena(biome, playerFaints);
|
||||||
this.eventTarget.dispatchEvent(new NewArenaEvent());
|
this.eventTarget.dispatchEvent(new NewArenaEvent());
|
||||||
|
|
||||||
this.arenaBg.pipelineData = {
|
this.arenaBg.pipelineData = {
|
||||||
@ -2711,7 +2721,9 @@ export class BattleScene extends SceneBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.party.map(p => p.updateInfo(instant));
|
this.party.forEach(p => {
|
||||||
|
p.updateInfo(instant);
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
const args = [this];
|
const args = [this];
|
||||||
if (modifier.shouldApply(...args)) {
|
if (modifier.shouldApply(...args)) {
|
||||||
|
@ -74,6 +74,7 @@ import {
|
|||||||
randSeedItem,
|
randSeedItem,
|
||||||
toDmgValue,
|
toDmgValue,
|
||||||
} from "#utils/common";
|
} from "#utils/common";
|
||||||
|
import { toCamelCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export class Ability implements Localizable {
|
export class Ability implements Localizable {
|
||||||
@ -109,13 +110,9 @@ export class Ability implements Localizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localize(): void {
|
localize(): void {
|
||||||
const i18nKey = AbilityId[this.id]
|
const i18nKey = toCamelCase(AbilityId[this.id]);
|
||||||
.split("_")
|
|
||||||
.filter(f => f)
|
|
||||||
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
|
||||||
.join("") as string;
|
|
||||||
|
|
||||||
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`) as string}${this.nameAppend}` : "";
|
this.name = this.id ? `${i18next.t(`ability:${i18nKey}.name`)}${this.nameAppend}` : "";
|
||||||
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
|
this.description = this.id ? (i18next.t(`ability:${i18nKey}.description`) as string) : "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1866,17 +1866,16 @@ interface PokemonPrevolutions {
|
|||||||
export const pokemonPrevolutions: PokemonPrevolutions = {};
|
export const pokemonPrevolutions: PokemonPrevolutions = {};
|
||||||
|
|
||||||
export function initPokemonPrevolutions(): void {
|
export function initPokemonPrevolutions(): void {
|
||||||
const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ].map(sfk => sfk as string);
|
// TODO: Why do we have empty strings in our array?
|
||||||
const prevolutionKeys = Object.keys(pokemonEvolutions);
|
const megaFormKeys = [ SpeciesFormKey.MEGA, "", SpeciesFormKey.MEGA_X, "", SpeciesFormKey.MEGA_Y ];
|
||||||
prevolutionKeys.forEach(pk => {
|
for (const [pk, evolutions] of Object.entries(pokemonEvolutions)) {
|
||||||
const evolutions = pokemonEvolutions[pk];
|
|
||||||
for (const ev of evolutions) {
|
for (const ev of evolutions) {
|
||||||
if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) {
|
if (ev.evoFormKey && megaFormKeys.indexOf(ev.evoFormKey) > -1) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
pokemonPrevolutions[ev.speciesId] = Number.parseInt(pk) as SpeciesId;
|
pokemonPrevolutions[ev.speciesId] = Number.parseInt(pk) as SpeciesId;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@ import type { ChargingMove, MoveAttrMap, MoveAttrString, MoveClassMap, MoveKindS
|
|||||||
import type { TurnMove } from "#types/turn-move";
|
import type { TurnMove } from "#types/turn-move";
|
||||||
import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
|
import { BooleanHolder, type Constructor, isNullOrUndefined, NumberHolder, randSeedFloat, randSeedInt, randSeedItem, toDmgValue } from "#utils/common";
|
||||||
import { getEnumValues } from "#utils/enums";
|
import { getEnumValues } from "#utils/enums";
|
||||||
import { toTitleCase } from "#utils/strings";
|
import { toCamelCase, toTitleCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import { applyChallenges } from "#utils/challenge-utils";
|
import { applyChallenges } from "#utils/challenge-utils";
|
||||||
|
|
||||||
@ -162,10 +162,16 @@ export abstract class Move implements Localizable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
localize(): void {
|
localize(): void {
|
||||||
const i18nKey = MoveId[this.id].split("_").filter(f => f).map((f, i) => i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()).join("") as unknown as string;
|
const i18nKey = toCamelCase(MoveId[this.id])
|
||||||
|
|
||||||
this.name = this.id ? `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}` : "";
|
if (this.id === MoveId.NONE) {
|
||||||
this.effect = this.id ? `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}` : "";
|
this.name = "";
|
||||||
|
this.effect = ""
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = `${i18next.t(`move:${i18nKey}.name`)}${this.nameAppend}`;
|
||||||
|
this.effect = `${i18next.t(`move:${i18nKey}.effect`)}${this.nameAppend}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -12,6 +12,7 @@ import { WeatherType } from "#enums/weather-type";
|
|||||||
import type { Pokemon } from "#field/pokemon";
|
import type { Pokemon } from "#field/pokemon";
|
||||||
import type { PokemonFormChangeItemModifier } from "#modifiers/modifier";
|
import type { PokemonFormChangeItemModifier } from "#modifiers/modifier";
|
||||||
import { type Constructor, coerceArray } from "#utils/common";
|
import { type Constructor, coerceArray } from "#utils/common";
|
||||||
|
import { toCamelCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
export abstract class SpeciesFormChangeTrigger {
|
export abstract class SpeciesFormChangeTrigger {
|
||||||
@ -143,11 +144,7 @@ export class SpeciesFormChangeMoveLearnedTrigger extends SpeciesFormChangeTrigge
|
|||||||
super();
|
super();
|
||||||
this.move = move;
|
this.move = move;
|
||||||
this.known = known;
|
this.known = known;
|
||||||
const moveKey = MoveId[this.move]
|
const moveKey = toCamelCase(MoveId[this.move]);
|
||||||
.split("_")
|
|
||||||
.filter(f => f)
|
|
||||||
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
|
||||||
.join("") as unknown as string;
|
|
||||||
this.description = known
|
this.description = known
|
||||||
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
? i18next.t("pokemonEvolutions:Forms.moveLearned", {
|
||||||
move: i18next.t(`move:${moveKey}.name`),
|
move: i18next.t(`move:${moveKey}.name`),
|
||||||
|
@ -38,6 +38,7 @@ export enum UiMode {
|
|||||||
UNAVAILABLE,
|
UNAVAILABLE,
|
||||||
CHALLENGE_SELECT,
|
CHALLENGE_SELECT,
|
||||||
RENAME_POKEMON,
|
RENAME_POKEMON,
|
||||||
|
RENAME_RUN,
|
||||||
RUN_HISTORY,
|
RUN_HISTORY,
|
||||||
RUN_INFO,
|
RUN_INFO,
|
||||||
TEST_DIALOGUE,
|
TEST_DIALOGUE,
|
||||||
|
@ -54,7 +54,7 @@ export class Arena {
|
|||||||
public bgm: string;
|
public bgm: string;
|
||||||
public ignoreAbilities: boolean;
|
public ignoreAbilities: boolean;
|
||||||
public ignoringEffectSource: BattlerIndex | null;
|
public ignoringEffectSource: BattlerIndex | null;
|
||||||
public playerTerasUsed: number;
|
public playerTerasUsed = 0;
|
||||||
/**
|
/**
|
||||||
* Saves the number of times a party pokemon faints during a arena encounter.
|
* Saves the number of times a party pokemon faints during a arena encounter.
|
||||||
* {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave).
|
* {@linkcode globalScene.currentBattle.enemyFaints} is the corresponding faint counter for the enemy (this resets every wave).
|
||||||
@ -68,12 +68,11 @@ export class Arena {
|
|||||||
|
|
||||||
public readonly eventTarget: EventTarget = new EventTarget();
|
public readonly eventTarget: EventTarget = new EventTarget();
|
||||||
|
|
||||||
constructor(biome: BiomeId, bgm: string, playerFaints = 0) {
|
constructor(biome: BiomeId, playerFaints = 0) {
|
||||||
this.biomeType = biome;
|
this.biomeType = biome;
|
||||||
this.bgm = bgm;
|
this.bgm = BiomeId[biome].toLowerCase();
|
||||||
this.trainerPool = biomeTrainerPools[biome];
|
this.trainerPool = biomeTrainerPools[biome];
|
||||||
this.updatePoolsForTimeOfDay();
|
this.updatePoolsForTimeOfDay();
|
||||||
this.playerTerasUsed = 0;
|
|
||||||
this.playerFaints = playerFaints;
|
this.playerFaints = playerFaints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,7 +447,9 @@ export class LoadingScene extends SceneBase {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (!mobile) {
|
if (!mobile) {
|
||||||
loadingGraphics.map(g => g.setVisible(false));
|
loadingGraphics.forEach(g => {
|
||||||
|
g.setVisible(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const intro = this.add.video(0, 0);
|
const intro = this.add.video(0, 0);
|
||||||
|
@ -121,8 +121,8 @@ export class ModifierBar extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateModifierOverflowVisibility(ignoreLimit: boolean) {
|
updateModifierOverflowVisibility(ignoreLimit: boolean) {
|
||||||
const modifierIcons = this.getAll().reverse();
|
const modifierIcons = this.getAll().reverse() as Phaser.GameObjects.Container[];
|
||||||
for (const modifier of modifierIcons.map(m => m as Phaser.GameObjects.Container).slice(iconOverflowIndex)) {
|
for (const modifier of modifierIcons.slice(iconOverflowIndex)) {
|
||||||
modifier.setVisible(ignoreLimit);
|
modifier.setVisible(ignoreLimit);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -370,6 +370,9 @@ export class PhaseManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.currentPhase = this.phaseQueue.shift() ?? null;
|
this.currentPhase = this.phaseQueue.shift() ?? null;
|
||||||
|
if (!this.currentPhase) {
|
||||||
|
throw new Error("No phases in queue; aborting");
|
||||||
|
}
|
||||||
|
|
||||||
const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
|
const unactivatedConditionalPhases: [() => boolean, Phase][] = [];
|
||||||
// Check if there are any conditional phases queued
|
// Check if there are any conditional phases queued
|
||||||
@ -389,12 +392,26 @@ export class PhaseManager {
|
|||||||
}
|
}
|
||||||
this.conditionalQueue.push(...unactivatedConditionalPhases);
|
this.conditionalQueue.push(...unactivatedConditionalPhases);
|
||||||
|
|
||||||
if (this.currentPhase) {
|
this.startCurrentPhase();
|
||||||
console.log(`%cStart Phase ${this.currentPhase.constructor.name}`, "color:green;");
|
|
||||||
this.currentPhase.start();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
overridePhase(phase: Phase): boolean {
|
||||||
if (this.standbyPhase) {
|
if (this.standbyPhase) {
|
||||||
return false;
|
return false;
|
||||||
@ -402,8 +419,7 @@ export class PhaseManager {
|
|||||||
|
|
||||||
this.standbyPhase = this.currentPhase;
|
this.standbyPhase = this.currentPhase;
|
||||||
this.currentPhase = phase;
|
this.currentPhase = phase;
|
||||||
console.log(`%cStart Phase ${phase.constructor.name}`, "color:green;");
|
this.startCurrentPhase();
|
||||||
phase.start();
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,10 @@ import { globalScene } from "#app/global-scene";
|
|||||||
import type { PhaseMap, PhaseString } from "#types/phase-types";
|
import type { PhaseMap, PhaseString } from "#types/phase-types";
|
||||||
|
|
||||||
export abstract class Phase {
|
export abstract class Phase {
|
||||||
|
/** Start the current phase. */
|
||||||
start() {}
|
start() {}
|
||||||
|
|
||||||
|
/** End the current phase and start a new one. */
|
||||||
end() {
|
end() {
|
||||||
globalScene.phaseManager.shiftPhase();
|
globalScene.phaseManager.shiftPhase();
|
||||||
}
|
}
|
||||||
|
@ -127,6 +127,7 @@ export interface SessionSaveData {
|
|||||||
battleType: BattleType;
|
battleType: BattleType;
|
||||||
trainer: TrainerData;
|
trainer: TrainerData;
|
||||||
gameVersion: string;
|
gameVersion: string;
|
||||||
|
runNameText: string;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
challenges: ChallengeData[];
|
challenges: ChallengeData[];
|
||||||
mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME,
|
mysteryEncounterType: MysteryEncounterType | -1; // Only defined when current wave is ME,
|
||||||
@ -206,10 +207,12 @@ export interface StarterData {
|
|||||||
[key: number]: StarterDataEntry;
|
[key: number]: StarterDataEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TutorialFlags {
|
// TODO: Rework into a bitmask
|
||||||
[key: string]: boolean;
|
export type TutorialFlags = {
|
||||||
}
|
[key in Tutorial]: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Rework into a bitmask
|
||||||
export interface SeenDialogues {
|
export interface SeenDialogues {
|
||||||
[key: string]: boolean;
|
[key: string]: boolean;
|
||||||
}
|
}
|
||||||
@ -822,52 +825,51 @@ export class GameData {
|
|||||||
return true; // TODO: is `true` the correct return value?
|
return true; // TODO: is `true` the correct return value?
|
||||||
}
|
}
|
||||||
|
|
||||||
private loadGamepadSettings(): boolean {
|
private loadGamepadSettings(): void {
|
||||||
Object.values(SettingGamepad)
|
Object.values(SettingGamepad).forEach(setting => {
|
||||||
.map(setting => setting as SettingGamepad)
|
setSettingGamepad(setting, settingGamepadDefaults[setting]);
|
||||||
.forEach(setting => setSettingGamepad(setting, settingGamepadDefaults[setting]));
|
});
|
||||||
|
|
||||||
if (!localStorage.hasOwnProperty("settingsGamepad")) {
|
if (!localStorage.hasOwnProperty("settingsGamepad")) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct?
|
const settingsGamepad = JSON.parse(localStorage.getItem("settingsGamepad")!); // TODO: is this bang correct?
|
||||||
|
|
||||||
for (const setting of Object.keys(settingsGamepad)) {
|
for (const setting of Object.keys(settingsGamepad)) {
|
||||||
setSettingGamepad(setting as SettingGamepad, settingsGamepad[setting]);
|
setSettingGamepad(setting as SettingGamepad, settingsGamepad[setting]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true; // TODO: is `true` the correct return value?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public saveTutorialFlag(tutorial: Tutorial, flag: boolean): boolean {
|
/**
|
||||||
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
* Save the specified tutorial as having the specified completion status.
|
||||||
let tutorials: object = {};
|
* @param tutorial - The {@linkcode Tutorial} whose completion status is being saved
|
||||||
if (localStorage.hasOwnProperty(key)) {
|
* @param status - The completion status to set
|
||||||
tutorials = JSON.parse(localStorage.getItem(key)!); // TODO: is this bang correct?
|
*/
|
||||||
}
|
public saveTutorialFlag(tutorial: Tutorial, status: boolean): void {
|
||||||
|
// Grab the prior save data tutorial
|
||||||
|
const saveDataKey = getDataTypeKey(GameDataType.TUTORIALS);
|
||||||
|
const tutorials: TutorialFlags = localStorage.hasOwnProperty(saveDataKey)
|
||||||
|
? JSON.parse(localStorage.getItem(saveDataKey)!)
|
||||||
|
: {};
|
||||||
|
|
||||||
Object.keys(Tutorial)
|
// TODO: We shouldn't be storing this like that
|
||||||
.map(t => t as Tutorial)
|
for (const key of Object.values(Tutorial)) {
|
||||||
.forEach(t => {
|
|
||||||
const key = Tutorial[t];
|
|
||||||
if (key === tutorial) {
|
if (key === tutorial) {
|
||||||
tutorials[key] = flag;
|
tutorials[key] = status;
|
||||||
} else {
|
} else {
|
||||||
tutorials[key] ??= false;
|
tutorials[key] ??= false;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
localStorage.setItem(key, JSON.stringify(tutorials));
|
localStorage.setItem(saveDataKey, JSON.stringify(tutorials));
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTutorialFlags(): TutorialFlags {
|
public getTutorialFlags(): TutorialFlags {
|
||||||
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
const key = getDataTypeKey(GameDataType.TUTORIALS);
|
||||||
const ret: TutorialFlags = {};
|
const ret: TutorialFlags = Object.values(Tutorial).reduce((acc, tutorial) => {
|
||||||
Object.values(Tutorial)
|
acc[Tutorial[tutorial]] = false;
|
||||||
.map(tutorial => tutorial as Tutorial)
|
return acc;
|
||||||
.forEach(tutorial => (ret[Tutorial[tutorial]] = false));
|
}, {} as TutorialFlags);
|
||||||
|
|
||||||
if (!localStorage.hasOwnProperty(key)) {
|
if (!localStorage.hasOwnProperty(key)) {
|
||||||
return ret;
|
return ret;
|
||||||
@ -979,6 +981,54 @@ export class GameData {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async renameSession(slotId: number, newName: string): Promise<boolean> {
|
||||||
|
return new Promise(async resolve => {
|
||||||
|
if (slotId < 0) {
|
||||||
|
return resolve(false);
|
||||||
|
}
|
||||||
|
const sessionData: SessionSaveData | null = await this.getSession(slotId);
|
||||||
|
|
||||||
|
if (!sessionData) {
|
||||||
|
return resolve(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newName === "") {
|
||||||
|
return resolve(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionData.runNameText = newName;
|
||||||
|
const updatedDataStr = JSON.stringify(sessionData);
|
||||||
|
const encrypted = encrypt(updatedDataStr, bypassLogin);
|
||||||
|
const secretId = this.secretId;
|
||||||
|
const trainerId = this.trainerId;
|
||||||
|
|
||||||
|
if (bypassLogin) {
|
||||||
|
localStorage.setItem(
|
||||||
|
`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`,
|
||||||
|
encrypt(updatedDataStr, bypassLogin),
|
||||||
|
);
|
||||||
|
resolve(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
pokerogueApi.savedata.session
|
||||||
|
.update({ slot: slotId, trainerId, secretId, clientSessionId }, encrypted)
|
||||||
|
.then(error => {
|
||||||
|
if (error) {
|
||||||
|
console.error("Failed to update session name:", error);
|
||||||
|
resolve(false);
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(`sessionData${slotId ? slotId : ""}_${loggedInUser?.username}`, encrypted);
|
||||||
|
updateUserInfo().then(success => {
|
||||||
|
if (success !== null && !success) {
|
||||||
|
return resolve(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
resolve(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> {
|
loadSession(slotId: number, sessionData?: SessionSaveData): Promise<boolean> {
|
||||||
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
|
// biome-ignore lint/suspicious/noAsyncPromiseExecutor: TODO: fix this
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
|
54
src/ui/rename-run-ui-handler.ts
Normal file
54
src/ui/rename-run-ui-handler.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import i18next from "i18next";
|
||||||
|
import type { InputFieldConfig } from "./form-modal-ui-handler";
|
||||||
|
import { FormModalUiHandler } from "./form-modal-ui-handler";
|
||||||
|
import type { ModalConfig } from "./modal-ui-handler";
|
||||||
|
|
||||||
|
export class RenameRunFormUiHandler extends FormModalUiHandler {
|
||||||
|
getModalTitle(_config?: ModalConfig): string {
|
||||||
|
return i18next.t("menu:renamerun");
|
||||||
|
}
|
||||||
|
|
||||||
|
getWidth(_config?: ModalConfig): number {
|
||||||
|
return 160;
|
||||||
|
}
|
||||||
|
|
||||||
|
getMargin(_config?: ModalConfig): [number, number, number, number] {
|
||||||
|
return [0, 0, 48, 0];
|
||||||
|
}
|
||||||
|
|
||||||
|
getButtonLabels(_config?: ModalConfig): string[] {
|
||||||
|
return [i18next.t("menu:rename"), i18next.t("menu:cancel")];
|
||||||
|
}
|
||||||
|
|
||||||
|
getReadableErrorMessage(error: string): string {
|
||||||
|
const colonIndex = error?.indexOf(":");
|
||||||
|
if (colonIndex > 0) {
|
||||||
|
error = error.slice(0, colonIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.getReadableErrorMessage(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
override getInputFieldConfigs(): InputFieldConfig[] {
|
||||||
|
return [{ label: i18next.t("menu:runName") }];
|
||||||
|
}
|
||||||
|
|
||||||
|
show(args: any[]): boolean {
|
||||||
|
if (!super.show(args)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (this.inputs?.length) {
|
||||||
|
this.inputs.forEach(input => {
|
||||||
|
input.text = "";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const config = args[0] as ModalConfig;
|
||||||
|
this.submitAction = _ => {
|
||||||
|
this.sanitizeInputs();
|
||||||
|
const sanitizedName = btoa(encodeURIComponent(this.inputs[0].text));
|
||||||
|
config.buttonActions[0](sanitizedName);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -26,6 +26,7 @@ import { addBBCodeTextObject, addTextObject, getTextColor } from "#ui/text";
|
|||||||
import { UiHandler } from "#ui/ui-handler";
|
import { UiHandler } from "#ui/ui-handler";
|
||||||
import { addWindow } from "#ui/ui-theme";
|
import { addWindow } from "#ui/ui-theme";
|
||||||
import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common";
|
import { formatFancyLargeNumber, formatLargeNumber, formatMoney, getPlayTimeString } from "#utils/common";
|
||||||
|
import { toCamelCase } from "#utils/strings";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle";
|
import RoundRectangle from "phaser3-rex-plugins/plugins/roundrectangle";
|
||||||
|
|
||||||
@ -207,6 +208,10 @@ export class RunInfoUiHandler extends UiHandler {
|
|||||||
headerText.setOrigin(0, 0);
|
headerText.setOrigin(0, 0);
|
||||||
headerText.setPositionRelative(headerBg, 8, 4);
|
headerText.setPositionRelative(headerBg, 8, 4);
|
||||||
this.runContainer.add(headerText);
|
this.runContainer.add(headerText);
|
||||||
|
const runName = addTextObject(0, 0, this.runInfo.runNameText, TextStyle.WINDOW);
|
||||||
|
runName.setOrigin(0, 0);
|
||||||
|
runName.setPositionRelative(headerBg, 60, 4);
|
||||||
|
this.runContainer.add(runName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -702,10 +707,7 @@ export class RunInfoUiHandler extends UiHandler {
|
|||||||
rules.push(i18next.t("challenges:inverseBattle.shortName"));
|
rules.push(i18next.t("challenges:inverseBattle.shortName"));
|
||||||
break;
|
break;
|
||||||
default: {
|
default: {
|
||||||
const localizationKey = Challenges[this.runInfo.challenges[i].id]
|
const localizationKey = toCamelCase(Challenges[this.runInfo.challenges[i].id]);
|
||||||
.split("_")
|
|
||||||
.map((f, i) => (i ? `${f[0]}${f.slice(1).toLowerCase()}` : f.toLowerCase()))
|
|
||||||
.join("");
|
|
||||||
rules.push(i18next.t(`challenges:${localizationKey}.name`));
|
rules.push(i18next.t(`challenges:${localizationKey}.name`));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
import { GameMode } from "#app/game-mode";
|
import { GameMode } from "#app/game-mode";
|
||||||
import { globalScene } from "#app/global-scene";
|
import { globalScene } from "#app/global-scene";
|
||||||
import { Button } from "#enums/buttons";
|
import { Button } from "#enums/buttons";
|
||||||
|
import { GameModes } from "#enums/game-modes";
|
||||||
import { TextStyle } from "#enums/text-style";
|
import { TextStyle } from "#enums/text-style";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
|
// biome-ignore lint/performance/noNamespaceImport: See `src/system/game-data.ts`
|
||||||
import * as Modifier from "#modifiers/modifier";
|
import * as Modifier from "#modifiers/modifier";
|
||||||
import type { SessionSaveData } from "#system/game-data";
|
import type { SessionSaveData } from "#system/game-data";
|
||||||
import type { PokemonData } from "#system/pokemon-data";
|
import type { PokemonData } from "#system/pokemon-data";
|
||||||
|
import type { OptionSelectConfig } from "#ui/abstract-option-select-ui-handler";
|
||||||
import { MessageUiHandler } from "#ui/message-ui-handler";
|
import { MessageUiHandler } from "#ui/message-ui-handler";
|
||||||
import { RunDisplayMode } from "#ui/run-info-ui-handler";
|
import { RunDisplayMode } from "#ui/run-info-ui-handler";
|
||||||
import { addTextObject } from "#ui/text";
|
import { addTextObject } from "#ui/text";
|
||||||
@ -15,7 +17,7 @@ import { fixedInt, formatLargeNumber, getPlayTimeString, isNullOrUndefined } fro
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
|
||||||
const SESSION_SLOTS_COUNT = 5;
|
const SESSION_SLOTS_COUNT = 5;
|
||||||
const SLOTS_ON_SCREEN = 3;
|
const SLOTS_ON_SCREEN = 2;
|
||||||
|
|
||||||
export enum SaveSlotUiMode {
|
export enum SaveSlotUiMode {
|
||||||
LOAD,
|
LOAD,
|
||||||
@ -33,6 +35,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
private uiMode: SaveSlotUiMode;
|
private uiMode: SaveSlotUiMode;
|
||||||
private saveSlotSelectCallback: SaveSlotSelectCallback | null;
|
private saveSlotSelectCallback: SaveSlotSelectCallback | null;
|
||||||
|
protected manageDataConfig: OptionSelectConfig;
|
||||||
|
|
||||||
private scrollCursor = 0;
|
private scrollCursor = 0;
|
||||||
|
|
||||||
@ -101,6 +104,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
|
|
||||||
processInput(button: Button): boolean {
|
processInput(button: Button): boolean {
|
||||||
const ui = this.getUi();
|
const ui = this.getUi();
|
||||||
|
const manageDataOptions: any[] = [];
|
||||||
|
|
||||||
let success = false;
|
let success = false;
|
||||||
let error = false;
|
let error = false;
|
||||||
@ -109,14 +113,115 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
const originalCallback = this.saveSlotSelectCallback;
|
const originalCallback = this.saveSlotSelectCallback;
|
||||||
if (button === Button.ACTION) {
|
if (button === Button.ACTION) {
|
||||||
const cursor = this.cursor + this.scrollCursor;
|
const cursor = this.cursor + this.scrollCursor;
|
||||||
if (this.uiMode === SaveSlotUiMode.LOAD && !this.sessionSlots[cursor].hasData) {
|
const sessionSlot = this.sessionSlots[cursor];
|
||||||
|
if (this.uiMode === SaveSlotUiMode.LOAD && !sessionSlot.hasData) {
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
switch (this.uiMode) {
|
switch (this.uiMode) {
|
||||||
case SaveSlotUiMode.LOAD:
|
case SaveSlotUiMode.LOAD:
|
||||||
this.saveSlotSelectCallback = null;
|
if (!sessionSlot.malformed) {
|
||||||
|
manageDataOptions.push({
|
||||||
|
label: i18next.t("menu:loadGame"),
|
||||||
|
handler: () => {
|
||||||
|
globalScene.ui.revertMode();
|
||||||
originalCallback?.(cursor);
|
originalCallback?.(cursor);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
keepOpen: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
manageDataOptions.push({
|
||||||
|
label: i18next.t("saveSlotSelectUiHandler:renameRun"),
|
||||||
|
handler: () => {
|
||||||
|
globalScene.ui.revertMode();
|
||||||
|
ui.setOverlayMode(
|
||||||
|
UiMode.RENAME_RUN,
|
||||||
|
{
|
||||||
|
buttonActions: [
|
||||||
|
(sanitizedName: string) => {
|
||||||
|
const name = decodeURIComponent(atob(sanitizedName));
|
||||||
|
globalScene.gameData.renameSession(cursor, name).then(response => {
|
||||||
|
if (response[0] === false) {
|
||||||
|
globalScene.reset(true);
|
||||||
|
} else {
|
||||||
|
this.clearSessionSlots();
|
||||||
|
this.cursorObj = null;
|
||||||
|
this.populateSessionSlots();
|
||||||
|
this.setScrollCursor(0);
|
||||||
|
this.setCursor(0);
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText("", 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
ui.revertMode();
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
);
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.manageDataConfig = {
|
||||||
|
xOffset: 0,
|
||||||
|
yOffset: 48,
|
||||||
|
options: manageDataOptions,
|
||||||
|
maxOptions: 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
manageDataOptions.push({
|
||||||
|
label: i18next.t("saveSlotSelectUiHandler:deleteRun"),
|
||||||
|
handler: () => {
|
||||||
|
globalScene.ui.revertMode();
|
||||||
|
ui.showText(i18next.t("saveSlotSelectUiHandler:deleteData"), null, () => {
|
||||||
|
ui.setOverlayMode(
|
||||||
|
UiMode.CONFIRM,
|
||||||
|
() => {
|
||||||
|
globalScene.gameData.tryClearSession(cursor).then(response => {
|
||||||
|
if (response[0] === false) {
|
||||||
|
globalScene.reset(true);
|
||||||
|
} else {
|
||||||
|
this.clearSessionSlots();
|
||||||
|
this.cursorObj = null;
|
||||||
|
this.populateSessionSlots();
|
||||||
|
this.setScrollCursor(0);
|
||||||
|
this.setCursor(0);
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText("", 0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
ui.revertMode();
|
||||||
|
ui.showText("", 0);
|
||||||
|
},
|
||||||
|
false,
|
||||||
|
0,
|
||||||
|
19,
|
||||||
|
import.meta.env.DEV ? 300 : 2000,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
keepOpen: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
manageDataOptions.push({
|
||||||
|
label: i18next.t("menuUiHandler:cancel"),
|
||||||
|
handler: () => {
|
||||||
|
globalScene.ui.revertMode();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
keepOpen: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
ui.setOverlayMode(UiMode.MENU_OPTION_SELECT, this.manageDataConfig);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SaveSlotUiMode.SAVE: {
|
case SaveSlotUiMode.SAVE: {
|
||||||
const saveAndCallback = () => {
|
const saveAndCallback = () => {
|
||||||
const originalCallback = this.saveSlotSelectCallback;
|
const originalCallback = this.saveSlotSelectCallback;
|
||||||
@ -161,6 +266,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.saveSlotSelectCallback = null;
|
this.saveSlotSelectCallback = null;
|
||||||
|
ui.showText("", 0);
|
||||||
originalCallback?.(-1);
|
originalCallback?.(-1);
|
||||||
success = true;
|
success = true;
|
||||||
}
|
}
|
||||||
@ -267,33 +373,34 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
this.cursorObj = globalScene.add.container(0, 0);
|
this.cursorObj = globalScene.add.container(0, 0);
|
||||||
const cursorBox = globalScene.add.nineslice(
|
const cursorBox = globalScene.add.nineslice(
|
||||||
0,
|
0,
|
||||||
0,
|
15,
|
||||||
"select_cursor_highlight_thick",
|
"select_cursor_highlight_thick",
|
||||||
undefined,
|
undefined,
|
||||||
296,
|
294,
|
||||||
44,
|
this.sessionSlots[prevSlotIndex ?? 0]?.saveData?.runNameText ? 50 : 60,
|
||||||
6,
|
6,
|
||||||
6,
|
6,
|
||||||
6,
|
6,
|
||||||
6,
|
6,
|
||||||
);
|
);
|
||||||
const rightArrow = globalScene.add.image(0, 0, "cursor");
|
const rightArrow = globalScene.add.image(0, 0, "cursor");
|
||||||
rightArrow.setPosition(160, 0);
|
rightArrow.setPosition(160, 15);
|
||||||
rightArrow.setName("rightArrow");
|
rightArrow.setName("rightArrow");
|
||||||
this.cursorObj.add([cursorBox, rightArrow]);
|
this.cursorObj.add([cursorBox, rightArrow]);
|
||||||
this.sessionSlotsContainer.add(this.cursorObj);
|
this.sessionSlotsContainer.add(this.cursorObj);
|
||||||
}
|
}
|
||||||
const cursorPosition = cursor + this.scrollCursor;
|
const cursorPosition = cursor + this.scrollCursor;
|
||||||
const cursorIncrement = cursorPosition * 56;
|
const cursorIncrement = cursorPosition * 76;
|
||||||
if (this.sessionSlots[cursorPosition] && this.cursorObj) {
|
if (this.sessionSlots[cursorPosition] && this.cursorObj) {
|
||||||
const hasData = this.sessionSlots[cursorPosition].hasData;
|
const session = this.sessionSlots[cursorPosition];
|
||||||
|
const hasData = session.hasData && !session.malformed;
|
||||||
// If the session slot lacks session data, it does not move from its default, central position.
|
// If the session slot lacks session data, it does not move from its default, central position.
|
||||||
// Only session slots with session data will move leftwards and have a visible arrow.
|
// Only session slots with session data will move leftwards and have a visible arrow.
|
||||||
if (!hasData) {
|
if (!hasData) {
|
||||||
this.cursorObj.setPosition(151, 26 + cursorIncrement);
|
this.cursorObj.setPosition(151, 20 + cursorIncrement);
|
||||||
this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement);
|
this.sessionSlots[cursorPosition].setPosition(0, cursorIncrement);
|
||||||
} else {
|
} else {
|
||||||
this.cursorObj.setPosition(145, 26 + cursorIncrement);
|
this.cursorObj.setPosition(145, 20 + cursorIncrement);
|
||||||
this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement);
|
this.sessionSlots[cursorPosition].setPosition(-6, cursorIncrement);
|
||||||
}
|
}
|
||||||
this.setArrowVisibility(hasData);
|
this.setArrowVisibility(hasData);
|
||||||
@ -311,7 +418,8 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
revertSessionSlot(slotIndex: number): void {
|
revertSessionSlot(slotIndex: number): void {
|
||||||
const sessionSlot = this.sessionSlots[slotIndex];
|
const sessionSlot = this.sessionSlots[slotIndex];
|
||||||
if (sessionSlot) {
|
if (sessionSlot) {
|
||||||
sessionSlot.setPosition(0, slotIndex * 56);
|
const valueHeight = 76;
|
||||||
|
sessionSlot.setPosition(0, slotIndex * valueHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -340,7 +448,7 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
this.setCursor(this.cursor, prevSlotIndex);
|
this.setCursor(this.cursor, prevSlotIndex);
|
||||||
globalScene.tweens.add({
|
globalScene.tweens.add({
|
||||||
targets: this.sessionSlotsContainer,
|
targets: this.sessionSlotsContainer,
|
||||||
y: this.sessionSlotsContainerInitialY - 56 * scrollCursor,
|
y: this.sessionSlotsContainerInitialY - 76 * scrollCursor,
|
||||||
duration: fixedInt(325),
|
duration: fixedInt(325),
|
||||||
ease: "Sine.easeInOut",
|
ease: "Sine.easeInOut",
|
||||||
});
|
});
|
||||||
@ -374,12 +482,14 @@ export class SaveSlotSelectUiHandler extends MessageUiHandler {
|
|||||||
class SessionSlot extends Phaser.GameObjects.Container {
|
class SessionSlot extends Phaser.GameObjects.Container {
|
||||||
public slotId: number;
|
public slotId: number;
|
||||||
public hasData: boolean;
|
public hasData: boolean;
|
||||||
|
/** Indicates the save slot ran into an error while being loaded */
|
||||||
|
public malformed: boolean;
|
||||||
|
private slotWindow: Phaser.GameObjects.NineSlice;
|
||||||
private loadingLabel: Phaser.GameObjects.Text;
|
private loadingLabel: Phaser.GameObjects.Text;
|
||||||
|
|
||||||
public saveData: SessionSaveData;
|
public saveData: SessionSaveData;
|
||||||
|
|
||||||
constructor(slotId: number) {
|
constructor(slotId: number) {
|
||||||
super(globalScene, 0, slotId * 56);
|
super(globalScene, 0, slotId * 76);
|
||||||
|
|
||||||
this.slotId = slotId;
|
this.slotId = slotId;
|
||||||
|
|
||||||
@ -387,32 +497,89 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setup() {
|
setup() {
|
||||||
const slotWindow = addWindow(0, 0, 304, 52);
|
this.slotWindow = addWindow(0, 0, 304, 70);
|
||||||
this.add(slotWindow);
|
this.add(this.slotWindow);
|
||||||
|
|
||||||
this.loadingLabel = addTextObject(152, 26, i18next.t("saveSlotSelectUiHandler:loading"), TextStyle.WINDOW);
|
this.loadingLabel = addTextObject(152, 33, i18next.t("saveSlotSelectUiHandler:loading"), TextStyle.WINDOW);
|
||||||
this.loadingLabel.setOrigin(0.5, 0.5);
|
this.loadingLabel.setOrigin(0.5, 0.5);
|
||||||
this.add(this.loadingLabel);
|
this.add(this.loadingLabel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a name for sessions that don't have a name yet.
|
||||||
|
* @param data - The {@linkcode SessionSaveData} being checked
|
||||||
|
* @returns The default name for the given data.
|
||||||
|
*/
|
||||||
|
decideFallback(data: SessionSaveData): string {
|
||||||
|
let fallbackName = `${GameMode.getModeName(data.gameMode)}`;
|
||||||
|
switch (data.gameMode) {
|
||||||
|
case GameModes.CLASSIC:
|
||||||
|
fallbackName += ` (${globalScene.gameData.gameStats.classicSessionsPlayed + 1})`;
|
||||||
|
break;
|
||||||
|
case GameModes.ENDLESS:
|
||||||
|
case GameModes.SPLICED_ENDLESS:
|
||||||
|
fallbackName += ` (${globalScene.gameData.gameStats.endlessSessionsPlayed + 1})`;
|
||||||
|
break;
|
||||||
|
case GameModes.DAILY: {
|
||||||
|
const runDay = new Date(data.timestamp).toLocaleDateString();
|
||||||
|
fallbackName += ` (${runDay})`;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case GameModes.CHALLENGE: {
|
||||||
|
const activeChallenges = data.challenges.filter(c => c.value !== 0);
|
||||||
|
if (activeChallenges.length === 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
fallbackName = "";
|
||||||
|
for (const challenge of activeChallenges.slice(0, 3)) {
|
||||||
|
if (fallbackName !== "") {
|
||||||
|
fallbackName += ", ";
|
||||||
|
}
|
||||||
|
fallbackName += challenge.toChallenge().getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeChallenges.length > 3) {
|
||||||
|
fallbackName += ", ...";
|
||||||
|
} else if (fallbackName === "") {
|
||||||
|
// Something went wrong when retrieving the names of the active challenges,
|
||||||
|
// so fall back to just naming the run "Challenge"
|
||||||
|
fallbackName = `${GameMode.getModeName(data.gameMode)}`;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fallbackName;
|
||||||
|
}
|
||||||
|
|
||||||
async setupWithData(data: SessionSaveData) {
|
async setupWithData(data: SessionSaveData) {
|
||||||
|
const hasName = data?.runNameText;
|
||||||
this.remove(this.loadingLabel, true);
|
this.remove(this.loadingLabel, true);
|
||||||
|
if (hasName) {
|
||||||
|
const nameLabel = addTextObject(8, 5, data.runNameText, TextStyle.WINDOW);
|
||||||
|
this.add(nameLabel);
|
||||||
|
} else {
|
||||||
|
const fallbackName = this.decideFallback(data);
|
||||||
|
await globalScene.gameData.renameSession(this.slotId, fallbackName);
|
||||||
|
const nameLabel = addTextObject(8, 5, fallbackName, TextStyle.WINDOW);
|
||||||
|
this.add(nameLabel);
|
||||||
|
}
|
||||||
|
|
||||||
const gameModeLabel = addTextObject(
|
const gameModeLabel = addTextObject(
|
||||||
8,
|
8,
|
||||||
5,
|
19,
|
||||||
`${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`,
|
`${GameMode.getModeName(data.gameMode) || i18next.t("gameMode:unknown")} - ${i18next.t("saveSlotSelectUiHandler:wave")} ${data.waveIndex}`,
|
||||||
TextStyle.WINDOW,
|
TextStyle.WINDOW,
|
||||||
);
|
);
|
||||||
this.add(gameModeLabel);
|
this.add(gameModeLabel);
|
||||||
|
|
||||||
const timestampLabel = addTextObject(8, 19, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW);
|
const timestampLabel = addTextObject(8, 33, new Date(data.timestamp).toLocaleString(), TextStyle.WINDOW);
|
||||||
this.add(timestampLabel);
|
this.add(timestampLabel);
|
||||||
|
|
||||||
const playTimeLabel = addTextObject(8, 33, getPlayTimeString(data.playTime), TextStyle.WINDOW);
|
const playTimeLabel = addTextObject(8, 47, getPlayTimeString(data.playTime), TextStyle.WINDOW);
|
||||||
this.add(playTimeLabel);
|
this.add(playTimeLabel);
|
||||||
|
|
||||||
const pokemonIconsContainer = globalScene.add.container(144, 4);
|
const pokemonIconsContainer = globalScene.add.container(144, 16);
|
||||||
data.party.forEach((p: PokemonData, i: number) => {
|
data.party.forEach((p: PokemonData, i: number) => {
|
||||||
const iconContainer = globalScene.add.container(26 * i, 0);
|
const iconContainer = globalScene.add.container(26 * i, 0);
|
||||||
iconContainer.setScale(0.75);
|
iconContainer.setScale(0.75);
|
||||||
@ -427,13 +594,9 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
TextStyle.PARTY,
|
TextStyle.PARTY,
|
||||||
{ fontSize: "54px", color: "#f8f8f8" },
|
{ fontSize: "54px", color: "#f8f8f8" },
|
||||||
);
|
);
|
||||||
text.setShadow(0, 0, undefined);
|
text.setShadow(0, 0, undefined).setStroke("#424242", 14).setOrigin(1, 0);
|
||||||
text.setStroke("#424242", 14);
|
|
||||||
text.setOrigin(1, 0);
|
|
||||||
|
|
||||||
iconContainer.add(icon);
|
|
||||||
iconContainer.add(text);
|
|
||||||
|
|
||||||
|
iconContainer.add([icon, text]);
|
||||||
pokemonIconsContainer.add(iconContainer);
|
pokemonIconsContainer.add(iconContainer);
|
||||||
|
|
||||||
pokemon.destroy();
|
pokemon.destroy();
|
||||||
@ -441,7 +604,7 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
this.add(pokemonIconsContainer);
|
this.add(pokemonIconsContainer);
|
||||||
|
|
||||||
const modifierIconsContainer = globalScene.add.container(148, 30);
|
const modifierIconsContainer = globalScene.add.container(148, 38);
|
||||||
modifierIconsContainer.setScale(0.5);
|
modifierIconsContainer.setScale(0.5);
|
||||||
let visibleModifierIndex = 0;
|
let visibleModifierIndex = 0;
|
||||||
for (const m of data.modifiers) {
|
for (const m of data.modifiers) {
|
||||||
@ -464,20 +627,31 @@ class SessionSlot extends Phaser.GameObjects.Container {
|
|||||||
|
|
||||||
load(): Promise<boolean> {
|
load(): Promise<boolean> {
|
||||||
return new Promise<boolean>(resolve => {
|
return new Promise<boolean>(resolve => {
|
||||||
globalScene.gameData.getSession(this.slotId).then(async sessionData => {
|
globalScene.gameData
|
||||||
|
.getSession(this.slotId)
|
||||||
|
.then(async sessionData => {
|
||||||
// Ignore the results if the view was exited
|
// Ignore the results if the view was exited
|
||||||
if (!this.active) {
|
if (!this.active) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
this.hasData = !!sessionData;
|
||||||
if (!sessionData) {
|
if (!sessionData) {
|
||||||
this.hasData = false;
|
|
||||||
this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty"));
|
this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty"));
|
||||||
resolve(false);
|
resolve(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.hasData = true;
|
|
||||||
this.saveData = sessionData;
|
this.saveData = sessionData;
|
||||||
await this.setupWithData(sessionData);
|
this.setupWithData(sessionData);
|
||||||
|
resolve(true);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
if (!this.active) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
console.warn(`Failed to load session slot #${this.slotId}:`, e);
|
||||||
|
this.loadingLabel.setText(i18next.t("menu:failedToLoadSession"));
|
||||||
|
this.hasData = true;
|
||||||
|
this.malformed = true;
|
||||||
resolve(true);
|
resolve(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -31,7 +31,7 @@ export class TestDialogueUiHandler extends FormModalUiHandler {
|
|||||||
// we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
|
// we check for null or undefined here as per above - the typeof is still an object but the value is null so we need to exit out of this and pass the null key
|
||||||
|
|
||||||
// Return in the format expected by i18next
|
// Return in the format expected by i18next
|
||||||
return middleKey ? `${topKey}:${middleKey.map(m => m).join(".")}.${t}` : `${topKey}:${t}`;
|
return middleKey ? `${topKey}:${middleKey.join(".")}.${t}` : `${topKey}:${t}`;
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter(t => t);
|
.filter(t => t);
|
||||||
|
@ -60,6 +60,7 @@ import { addWindow } from "#ui/ui-theme";
|
|||||||
import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler";
|
import { UnavailableModalUiHandler } from "#ui/unavailable-modal-ui-handler";
|
||||||
import { executeIf } from "#utils/common";
|
import { executeIf } from "#utils/common";
|
||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
|
import { RenameRunFormUiHandler } from "./rename-run-ui-handler";
|
||||||
|
|
||||||
const transitionModes = [
|
const transitionModes = [
|
||||||
UiMode.SAVE_SLOT,
|
UiMode.SAVE_SLOT,
|
||||||
@ -98,6 +99,7 @@ const noTransitionModes = [
|
|||||||
UiMode.SESSION_RELOAD,
|
UiMode.SESSION_RELOAD,
|
||||||
UiMode.UNAVAILABLE,
|
UiMode.UNAVAILABLE,
|
||||||
UiMode.RENAME_POKEMON,
|
UiMode.RENAME_POKEMON,
|
||||||
|
UiMode.RENAME_RUN,
|
||||||
UiMode.TEST_DIALOGUE,
|
UiMode.TEST_DIALOGUE,
|
||||||
UiMode.AUTO_COMPLETE,
|
UiMode.AUTO_COMPLETE,
|
||||||
UiMode.ADMIN,
|
UiMode.ADMIN,
|
||||||
@ -168,6 +170,7 @@ export class UI extends Phaser.GameObjects.Container {
|
|||||||
new UnavailableModalUiHandler(),
|
new UnavailableModalUiHandler(),
|
||||||
new GameChallengesUiHandler(),
|
new GameChallengesUiHandler(),
|
||||||
new RenameFormUiHandler(),
|
new RenameFormUiHandler(),
|
||||||
|
new RenameRunFormUiHandler(),
|
||||||
new RunHistoryUiHandler(),
|
new RunHistoryUiHandler(),
|
||||||
new RunInfoUiHandler(),
|
new RunInfoUiHandler(),
|
||||||
new TestDialogueUiHandler(UiMode.TEST_DIALOGUE),
|
new TestDialogueUiHandler(UiMode.TEST_DIALOGUE),
|
||||||
|
@ -3,9 +3,6 @@ import { BattlerIndex } from "#enums/battler-index";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
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 { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
@ -46,7 +43,7 @@ describe("Abilities - Moxie", () => {
|
|||||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase);
|
await game.phaseInterceptor.to("VictoryPhase");
|
||||||
|
|
||||||
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
|
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||||
});
|
});
|
||||||
@ -67,7 +64,7 @@ describe("Abilities - Moxie", () => {
|
|||||||
|
|
||||||
game.move.select(moveToUse, BattlerIndex.PLAYER_2);
|
game.move.select(moveToUse, BattlerIndex.PLAYER_2);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(firstPokemon.getStatStage(Stat.ATK)).toBe(1);
|
expect(firstPokemon.getStatStage(Stat.ATK)).toBe(1);
|
||||||
},
|
},
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
|
import type { TurnStartPhase } from "#phases/turn-start-phase";
|
||||||
import { SelectTargetPhase } from "#phases/select-target-phase";
|
|
||||||
import { TurnStartPhase } from "#phases/turn-start-phase";
|
|
||||||
import { GameManager } from "#test/test-utils/game-manager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
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
|
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150
|
||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
await game.phaseInterceptor.to("TurnStartPhase", false);
|
||||||
|
|
||||||
const playerPokemonIndex = playerPokemon.getBattlerIndex();
|
const playerPokemonIndex = playerPokemon.getBattlerIndex();
|
||||||
const enemyPokemonIndex = enemyPokemon.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
|
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50
|
||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
await game.phaseInterceptor.run(EnemyCommandPhase);
|
await game.phaseInterceptor.to("TurnStartPhase", false);
|
||||||
|
|
||||||
const playerPokemonIndex = playerPokemon.getBattlerIndex();
|
const playerPokemonIndex = playerPokemon.getBattlerIndex();
|
||||||
const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
|
const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
|
||||||
@ -84,7 +82,7 @@ describe("Battle order", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
game.move.select(MoveId.TACKLE, 1);
|
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 phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
|
||||||
const order = phase.getCommandOrder();
|
const order = phase.getCommandOrder();
|
||||||
@ -108,7 +106,7 @@ describe("Battle order", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
game.move.select(MoveId.TACKLE, 1);
|
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 phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
|
||||||
const order = phase.getCommandOrder();
|
const order = phase.getCommandOrder();
|
||||||
@ -132,7 +130,7 @@ describe("Battle order", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
game.move.select(MoveId.TACKLE, 1);
|
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 phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
|
||||||
const order = phase.getCommandOrder();
|
const order = phase.getCommandOrder();
|
||||||
|
@ -1,28 +1,13 @@
|
|||||||
import { getGameMode } from "#app/game-mode";
|
|
||||||
import { allSpecies } from "#data/data-lists";
|
import { allSpecies } from "#data/data-lists";
|
||||||
import { AbilityId } from "#enums/ability-id";
|
import { AbilityId } from "#enums/ability-id";
|
||||||
import { BiomeId } from "#enums/biome-id";
|
import { BiomeId } from "#enums/biome-id";
|
||||||
import { GameModes } from "#enums/game-modes";
|
|
||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { PlayerGender } from "#enums/player-gender";
|
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { BattleEndPhase } from "#phases/battle-end-phase";
|
|
||||||
import { CommandPhase } from "#phases/command-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 { 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 { GameManager } from "#test/test-utils/game-manager";
|
||||||
import { generateStarter } from "#test/test-utils/game-manager-utils";
|
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
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!
|
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 () => {
|
it("do attack wave 3 - single battle - regular - OHKO", async () => {
|
||||||
game.override.enemySpecies(SpeciesId.RATTATA).startingLevel(2000).battleStyle("single").startingWave(3);
|
game.override.enemySpecies(SpeciesId.RATTATA).startingLevel(2000).battleStyle("single").startingWave(3);
|
||||||
await game.classicMode.startBattle([SpeciesId.MEWTWO]);
|
await game.classicMode.startBattle([SpeciesId.MEWTWO]);
|
||||||
game.move.use(MoveId.TACKLE);
|
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 () => {
|
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");
|
.battleStyle("single");
|
||||||
await game.classicMode.startBattle([SpeciesId.MEWTWO]);
|
await game.classicMode.startBattle([SpeciesId.MEWTWO]);
|
||||||
game.move.select(MoveId.TACKLE);
|
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 () => {
|
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([
|
it.each([
|
||||||
{ name: "1v1", double: false, qty: 1 },
|
{ name: "1v1", double: false, qty: 1 },
|
||||||
{ name: "2v1", double: false, qty: 2 },
|
{ name: "2v1", double: false, qty: 2 },
|
||||||
@ -232,7 +111,7 @@ describe("Phase - Battle Phase", () => {
|
|||||||
await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]);
|
await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
await game.phaseInterceptor.to(DamageAnimPhase, false);
|
await game.phaseInterceptor.to("DamageAnimPhase", false);
|
||||||
await game.killPokemon(game.scene.currentBattle.enemyParty[0]);
|
await game.killPokemon(game.scene.currentBattle.enemyParty[0]);
|
||||||
expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true);
|
expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true);
|
||||||
await game.phaseInterceptor.to("VictoryPhase");
|
await game.phaseInterceptor.to("VictoryPhase");
|
||||||
@ -296,7 +175,7 @@ describe("Phase - Battle Phase", () => {
|
|||||||
game.field.getPlayerPokemon().hp = 1;
|
game.field.getPlayerPokemon().hp = 1;
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BattleEndPhase);
|
await game.phaseInterceptor.to("BattleEndPhase");
|
||||||
game.doRevivePokemon(0); // pretend max revive was picked
|
game.doRevivePokemon(0); // pretend max revive was picked
|
||||||
game.doSelectModifier();
|
game.doSelectModifier();
|
||||||
|
|
||||||
@ -308,6 +187,6 @@ describe("Phase - Battle Phase", () => {
|
|||||||
},
|
},
|
||||||
() => game.isCurrentPhase(NextEncounterPhase),
|
() => 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 { BATTLE_STATS, Stat } from "#enums/stat";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { TempStatStageBoosterModifier } from "#modifiers/modifier";
|
import { TempStatStageBoosterModifier } from "#modifiers/modifier";
|
||||||
import { TurnEndPhase } from "#phases/turn-end-phase";
|
|
||||||
import { GameManager } from "#test/test-utils/game-manager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
|
import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
@ -47,7 +46,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
|
|
||||||
await game.phaseInterceptor.runFrom("EnemyCommandPhase").to(TurnEndPhase);
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3);
|
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3);
|
||||||
});
|
});
|
||||||
@ -64,11 +63,11 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
|||||||
// Raise ACC by +2 stat stages
|
// Raise ACC by +2 stat stages
|
||||||
game.move.select(MoveId.HONE_CLAWS);
|
game.move.select(MoveId.HONE_CLAWS);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
// ACC at +3 stat stages yields a x2 multiplier
|
// ACC at +3 stat stages yields a x2 multiplier
|
||||||
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2);
|
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2);
|
||||||
@ -84,11 +83,11 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
|||||||
// Raise ATK by +1 stat stage
|
// Raise ATK by +1 stat stage
|
||||||
game.move.select(MoveId.HONE_CLAWS);
|
game.move.select(MoveId.HONE_CLAWS);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
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
|
// ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK
|
||||||
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8);
|
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8);
|
||||||
@ -112,7 +111,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.TACKLE);
|
game.move.select(MoveId.TACKLE);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(TurnEndPhase);
|
await game.phaseInterceptor.to("TurnEndPhase");
|
||||||
|
|
||||||
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3);
|
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3);
|
||||||
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4);
|
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4);
|
||||||
|
@ -4,10 +4,7 @@ import { MoveId } from "#enums/move-id";
|
|||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
import { Stat } from "#enums/stat";
|
||||||
import type { Move } from "#moves/move";
|
import type { Move } from "#moves/move";
|
||||||
import { DamageAnimPhase } from "#phases/damage-anim-phase";
|
import type { MoveEffectPhase } from "#phases/move-effect-phase";
|
||||||
import { MoveEffectPhase } from "#phases/move-effect-phase";
|
|
||||||
import { MoveEndPhase } from "#phases/move-end-phase";
|
|
||||||
import { MovePhase } from "#phases/move-phase";
|
|
||||||
import { GameManager } from "#test/test-utils/game-manager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
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
|
// Force user party to act before enemy party
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
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);
|
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);
|
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);
|
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);
|
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -75,14 +72,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||||||
// Force user party to act before enemy party
|
// Force user party to act before enemy party
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
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);
|
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);
|
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);
|
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);
|
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
|
// 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.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);
|
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);
|
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
|
// 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);
|
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);
|
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -121,18 +118,18 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||||||
// Force first enemy to act in between party
|
// Force first enemy to act in between party
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
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);
|
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);
|
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MoveEndPhase);
|
await game.phaseInterceptor.to("MoveEndPhase");
|
||||||
// Skip enemy move
|
// 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);
|
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);
|
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -145,14 +142,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||||||
// Force user party to act before enemy party
|
// Force user party to act before enemy party
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
|
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);
|
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);
|
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);
|
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);
|
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -189,24 +186,24 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||||||
// Force first enemy to act in between party
|
// Force first enemy to act in between party
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -243,24 +240,24 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
|
|||||||
// Force first enemy to act in between party
|
// Force first enemy to act in between party
|
||||||
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
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);
|
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,8 +2,6 @@ import { AbilityId } from "#enums/ability-id";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
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 { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
@ -40,7 +38,7 @@ describe("Moves - Growth", () => {
|
|||||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||||
|
|
||||||
game.move.select(MoveId.GROWTH);
|
game.move.select(MoveId.GROWTH);
|
||||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1);
|
||||||
});
|
});
|
||||||
|
@ -2,10 +2,6 @@ import { AbilityId } from "#enums/ability-id";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
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 { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest";
|
||||||
@ -43,7 +39,7 @@ describe("Moves - Parting Shot", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.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.ATK)).toBe(0);
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||||
@ -58,7 +54,7 @@ describe("Moves - Parting Shot", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.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.ATK)).toBe(0);
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||||
@ -77,36 +73,14 @@ describe("Moves - Parting Shot", () => {
|
|||||||
SpeciesId.ABRA,
|
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();
|
const enemyPokemon = game.field.getEnemyPokemon();
|
||||||
expect(enemyPokemon).toBeDefined();
|
enemyPokemon.setStatStage(Stat.ATK, -6);
|
||||||
|
enemyPokemon.setStatStage(Stat.SPATK, -6);
|
||||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6);
|
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6);
|
|
||||||
|
|
||||||
// now parting shot should fail
|
// now parting shot should fail
|
||||||
game.move.select(MoveId.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(-6);
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6);
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6);
|
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6);
|
||||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||||
@ -125,7 +99,7 @@ describe("Moves - Parting Shot", () => {
|
|||||||
|
|
||||||
game.move.select(MoveId.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.ATK)).toBe(0);
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
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]);
|
await game.classicMode.startBattle([SpeciesId.SNORLAX, SpeciesId.MEOWTH]);
|
||||||
|
|
||||||
const enemyPokemon = game.field.getEnemyPokemon();
|
const enemyPokemon = game.field.getEnemyPokemon();
|
||||||
expect(enemyPokemon).toBeDefined();
|
|
||||||
|
|
||||||
game.move.select(MoveId.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.ATK)).toBe(0);
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
|
||||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
||||||
@ -158,11 +131,10 @@ describe("Moves - Parting Shot", () => {
|
|||||||
await game.classicMode.startBattle([SpeciesId.MURKROW]);
|
await game.classicMode.startBattle([SpeciesId.MURKROW]);
|
||||||
|
|
||||||
const enemyPokemon = game.field.getEnemyPokemon();
|
const enemyPokemon = game.field.getEnemyPokemon();
|
||||||
expect(enemyPokemon).toBeDefined();
|
|
||||||
|
|
||||||
game.move.select(MoveId.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(-1);
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-1);
|
||||||
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
|
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",
|
"Parting shot regularly not fail if no party available to switch - party fainted",
|
||||||
async () => {
|
async () => {
|
||||||
await game.classicMode.startBattle([SpeciesId.MURKROW, SpeciesId.MEOWTH]);
|
await game.classicMode.startBattle([SpeciesId.MURKROW, SpeciesId.MEOWTH]);
|
||||||
|
|
||||||
|
const meowth = game.scene.getPlayerParty()[1];
|
||||||
|
meowth.hp = 0;
|
||||||
|
|
||||||
game.move.select(MoveId.SPLASH);
|
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.move.select(MoveId.PARTING_SHOT);
|
||||||
|
game.doSelectPartyPokemon(1);
|
||||||
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
await game.phaseInterceptor.to(BerryPhase, false);
|
|
||||||
const enemyPokemon = game.field.getEnemyPokemon();
|
const enemyPokemon = game.field.getEnemyPokemon();
|
||||||
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
|
||||||
expect(enemyPokemon.getStatStage(Stat.SPATK)).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 { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
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 { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
@ -41,7 +39,7 @@ describe("Moves - Tackle", () => {
|
|||||||
await game.classicMode.startBattle([SpeciesId.MIGHTYENA]);
|
await game.classicMode.startBattle([SpeciesId.MIGHTYENA]);
|
||||||
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
await game.toEndOfTurn();
|
||||||
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
||||||
expect(hpLost).toBe(0);
|
expect(hpLost).toBe(0);
|
||||||
});
|
});
|
||||||
@ -55,7 +53,7 @@ describe("Moves - Tackle", () => {
|
|||||||
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase);
|
await game.toEndOfTurn();
|
||||||
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
|
||||||
expect(hpLost).toBeGreaterThan(0);
|
expect(hpLost).toBeGreaterThan(0);
|
||||||
expect(hpLost).toBeLessThan(4);
|
expect(hpLost).toBeLessThan(4);
|
||||||
|
@ -2,8 +2,6 @@ import { AbilityId } from "#enums/ability-id";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { Stat } from "#enums/stat";
|
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 { GameManager } from "#test/test-utils/game-manager";
|
||||||
import Phaser from "phaser";
|
import Phaser from "phaser";
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||||
@ -43,7 +41,7 @@ describe("Moves - Tail whip", () => {
|
|||||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
|
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
|
||||||
|
|
||||||
game.move.select(moveToUse);
|
game.move.select(moveToUse);
|
||||||
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase);
|
await game.toEndOfTurn();
|
||||||
|
|
||||||
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1);
|
||||||
});
|
});
|
||||||
|
@ -9,7 +9,6 @@ import { MessagePhase } from "#phases/message-phase";
|
|||||||
import {
|
import {
|
||||||
MysteryEncounterBattlePhase,
|
MysteryEncounterBattlePhase,
|
||||||
MysteryEncounterOptionSelectedPhase,
|
MysteryEncounterOptionSelectedPhase,
|
||||||
MysteryEncounterPhase,
|
|
||||||
MysteryEncounterRewardsPhase,
|
MysteryEncounterRewardsPhase,
|
||||||
} from "#phases/mystery-encounter-phases";
|
} from "#phases/mystery-encounter-phases";
|
||||||
import { VictoryPhase } from "#phases/victory-phase";
|
import { VictoryPhase } from "#phases/victory-phase";
|
||||||
@ -89,9 +88,9 @@ export async function runMysteryEncounterToEnd(
|
|||||||
uiHandler.processInput(Button.ACTION);
|
uiHandler.processInput(Button.ACTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.phaseInterceptor.to(CommandPhase);
|
await game.toNextTurn();
|
||||||
} else {
|
} else {
|
||||||
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -112,7 +111,7 @@ export async function runSelectMysteryEncounterOption(
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (game.isCurrentPhase(MessagePhase)) {
|
if (game.isCurrentPhase(MessagePhase)) {
|
||||||
await game.phaseInterceptor.run(MessagePhase);
|
await game.phaseInterceptor.to("MessagePhase");
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispose of intro messages
|
// dispose of intro messages
|
||||||
@ -126,7 +125,7 @@ export async function runSelectMysteryEncounterOption(
|
|||||||
() => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase),
|
() => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase),
|
||||||
);
|
);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, true);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", true);
|
||||||
|
|
||||||
// select the desired option
|
// select the desired option
|
||||||
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
|
||||||
@ -205,7 +204,7 @@ export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManage
|
|||||||
game.scene.field.remove(p);
|
game.scene.field.remove(p);
|
||||||
});
|
});
|
||||||
game.scene.phaseManager.pushPhase(new VictoryPhase(0));
|
game.scene.phaseManager.pushPhase(new VictoryPhase(0));
|
||||||
game.phaseInterceptor.superEndPhase();
|
game.endPhase();
|
||||||
game.setMode(UiMode.MESSAGE);
|
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 runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
|
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 game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
|
||||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
|
||||||
|
|
||||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -232,9 +230,9 @@ describe("Berries Abound - Mystery Encounter", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2);
|
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);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
|
@ -366,11 +366,11 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
|||||||
await skipBattleRunMysteryEncounterRewardsPhase(game, false);
|
await skipBattleRunMysteryEncounterRewardsPhase(game, false);
|
||||||
|
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name);
|
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.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => {
|
||||||
game.phaseInterceptor.superEndPhase();
|
game.endPhase();
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(MysteryEncounterRewardsPhase);
|
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
|
||||||
|
|
||||||
expect(selectOptionSpy).toHaveBeenCalledTimes(1);
|
expect(selectOptionSpy).toHaveBeenCalledTimes(1);
|
||||||
const optionData = selectOptionSpy.mock.calls[0][0];
|
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 () => {
|
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.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [SpeciesId.ABRA]);
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||||
|
|
||||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
@ -417,7 +417,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
|||||||
await runMysteryEncounterToEnd(game, 2);
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -436,7 +436,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
|||||||
await runMysteryEncounterToEnd(game, 2);
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -458,7 +458,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
|||||||
await runMysteryEncounterToEnd(game, 2);
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -482,7 +482,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
|||||||
await runMysteryEncounterToEnd(game, 2);
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
|
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 () => {
|
it("should NOT be selectable if the player doesn't have any Bug items", async () => {
|
||||||
game.scene.modifiers = [];
|
game.scene.modifiers = [];
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty);
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||||
|
|
||||||
game.scene.modifiers = [];
|
game.scene.modifiers = [];
|
||||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||||
@ -558,7 +558,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
|
|||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
|
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 { CommandPhase } from "#phases/command-phase";
|
||||||
import { MovePhase } from "#phases/move-phase";
|
import { MovePhase } from "#phases/move-phase";
|
||||||
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
||||||
import { NewBattlePhase } from "#phases/new-battle-phase";
|
|
||||||
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
||||||
import {
|
import {
|
||||||
runMysteryEncounterToEnd,
|
runMysteryEncounterToEnd,
|
||||||
@ -200,9 +199,9 @@ describe("Clowning Around - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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;
|
const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability;
|
||||||
|
|
||||||
game.onNextPrompt("PostMysteryEncounterPhase", UiMode.MESSAGE, () => {
|
game.onNextPrompt("PostMysteryEncounterPhase", UiMode.MESSAGE, () => {
|
||||||
@ -215,7 +214,7 @@ describe("Clowning Around - Mystery Encounter", () => {
|
|||||||
const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler;
|
const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler;
|
||||||
vi.spyOn(partyUiHandler, "show");
|
vi.spyOn(partyUiHandler, "show");
|
||||||
game.endPhase();
|
game.endPhase();
|
||||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name);
|
||||||
|
|
||||||
// Wait for Yes/No confirmation to appear
|
// Wait for Yes/No confirmation to appear
|
||||||
@ -228,7 +227,7 @@ describe("Clowning Around - Mystery Encounter", () => {
|
|||||||
// Click "Select" on Pokemon
|
// Click "Select" on Pokemon
|
||||||
partyUiHandler.processInput(Button.ACTION);
|
partyUiHandler.processInput(Button.ACTION);
|
||||||
// Stop next battle before it runs
|
// Stop next battle before it runs
|
||||||
await game.phaseInterceptor.to(NewBattlePhase, false);
|
await game.phaseInterceptor.to("NewBattlePhase", false);
|
||||||
|
|
||||||
const leadPokemon = scene.getPlayerParty()[0];
|
const leadPokemon = scene.getPlayerParty()[0];
|
||||||
expect(leadPokemon.customPokemonData?.ability).toBe(abilityToTrain);
|
expect(leadPokemon.customPokemonData?.ability).toBe(abilityToTrain);
|
||||||
|
@ -126,9 +126,9 @@ describe("Dancing Lessons - Mystery Encounter", () => {
|
|||||||
partyLead.calculateStats();
|
partyLead.calculateStats();
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -215,7 +215,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
|
||||||
const partyCountBefore = scene.getPlayerParty().length;
|
const partyCountBefore = scene.getPlayerParty().length;
|
||||||
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||||
|
|
||||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
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 game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1);
|
await runMysteryEncounterToEnd(game, 1);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2);
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 3);
|
await runMysteryEncounterToEnd(game, 3);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 4);
|
await runMysteryEncounterToEnd(game, 4);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
|
@ -122,9 +122,9 @@ describe("Fight or Flight - Mystery Encounter", () => {
|
|||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
|
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 () => {
|
it("should NOT be selectable if the player doesn't have a Stealing move", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty);
|
||||||
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
scene.getPlayerParty().forEach(p => (p.moveset = []));
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||||
|
|
||||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
@ -182,9 +182,9 @@ describe("Fight or Flight - Mystery Encounter", () => {
|
|||||||
const item = game.scene.currentBattle.mysteryEncounter!.misc;
|
const item = game.scene.currentBattle.mysteryEncounter!.misc;
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 2);
|
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);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
|
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 () => {
|
it("should NOT be selectable if the player doesn't have enough money", async () => {
|
||||||
game.scene.money = 0;
|
game.scene.money = 0;
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
|
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();
|
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
@ -162,7 +162,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
|||||||
|
|
||||||
// Turn 3
|
// Turn 3
|
||||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
(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
|
// Rewards
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
@ -181,11 +181,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
|||||||
// Skip minigame
|
// Skip minigame
|
||||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
(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
|
// Rewards
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -210,11 +210,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
|||||||
wobbuffet.hp = Math.floor(0.2 * wobbuffet.getMaxHp());
|
wobbuffet.hp = Math.floor(0.2 * wobbuffet.getMaxHp());
|
||||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
(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
|
// Rewards
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -240,11 +240,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
|||||||
wobbuffet.hp = Math.floor(0.1 * wobbuffet.getMaxHp());
|
wobbuffet.hp = Math.floor(0.1 * wobbuffet.getMaxHp());
|
||||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
(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
|
// Rewards
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -270,11 +270,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
|
|||||||
wobbuffet.hp = 1;
|
wobbuffet.hp = 1;
|
||||||
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
|
||||||
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
|
(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
|
// Rewards
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
|
@ -227,7 +227,7 @@ describe("Global Trade System - Mystery Encounter", () => {
|
|||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
|
@ -161,9 +161,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -205,9 +205,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -262,9 +262,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 () => {
|
it("should NOT be selectable if the player doesn't have enough money", async () => {
|
||||||
game.scene.money = 0;
|
game.scene.money = 0;
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||||
|
|
||||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
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 () => {
|
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.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.BLASTOISE]);
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||||
|
|
||||||
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
const encounterPhase = scene.phaseManager.getCurrentPhase();
|
||||||
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
@ -299,9 +299,9 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
await runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
|
||||||
import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter";
|
import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter";
|
||||||
import { CommandPhase } from "#phases/command-phase";
|
import { CommandPhase } from "#phases/command-phase";
|
||||||
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
|
||||||
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
||||||
import {
|
import {
|
||||||
runMysteryEncounterToEnd,
|
runMysteryEncounterToEnd,
|
||||||
@ -176,7 +175,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
|
|||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
await runMysteryEncounterToEnd(game, 1, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
const eggsAfter = scene.gameData.eggs;
|
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.COMMON).length).toBe(commonEggs);
|
||||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
||||||
|
|
||||||
game.phaseInterceptor.superEndPhase();
|
game.endPhase();
|
||||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||||
|
|
||||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship;
|
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.
|
// 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 runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
const eggsAfter = scene.gameData.eggs;
|
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.COMMON).length).toBe(commonEggs);
|
||||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
||||||
|
|
||||||
game.phaseInterceptor.superEndPhase();
|
game.endPhase();
|
||||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||||
|
|
||||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship;
|
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
|
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 runMysteryEncounterToEnd(game, 3, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
const eggsAfter = scene.gameData.eggs;
|
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.COMMON).length).toBe(commonEggs);
|
||||||
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
|
||||||
|
|
||||||
game.phaseInterceptor.superEndPhase();
|
game.endPhase();
|
||||||
await game.phaseInterceptor.to(PostMysteryEncounterPhase);
|
await game.phaseInterceptor.to("PostMysteryEncounterPhase");
|
||||||
|
|
||||||
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship;
|
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
|
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 game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
|
||||||
import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter";
|
import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter";
|
||||||
import { CommandPhase } from "#phases/command-phase";
|
import { CommandPhase } from "#phases/command-phase";
|
||||||
import { MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases";
|
|
||||||
import { PartyHealPhase } from "#phases/party-heal-phase";
|
import { PartyHealPhase } from "#phases/party-heal-phase";
|
||||||
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
import { SelectModifierPhase } from "#phases/select-modifier-phase";
|
||||||
import { VictoryPhase } from "#phases/victory-phase";
|
import { VictoryPhase } from "#phases/victory-phase";
|
||||||
@ -295,9 +294,9 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
|
|||||||
|
|
||||||
// Should have Macho Brace in the rewards
|
// Should have Macho Brace in the rewards
|
||||||
await skipBattleToNextBattle(game, true);
|
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);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
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 game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2);
|
await runMysteryEncounterToEnd(game, 2);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -366,11 +365,10 @@ async function skipBattleToNextBattle(game: GameManager, isFinalBattle = false)
|
|||||||
p.status = new Status(StatusEffect.FAINT);
|
p.status = new Status(StatusEffect.FAINT);
|
||||||
game.scene.field.remove(p);
|
game.scene.field.remove(p);
|
||||||
});
|
});
|
||||||
game.phaseInterceptor["onHold"] = [];
|
|
||||||
game.scene.phaseManager.pushPhase(new VictoryPhase(0));
|
game.scene.phaseManager.pushPhase(new VictoryPhase(0));
|
||||||
game.phaseInterceptor.superEndPhase();
|
game.endPhase();
|
||||||
if (isFinalBattle) {
|
if (isFinalBattle) {
|
||||||
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase);
|
await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
|
||||||
} else {
|
} else {
|
||||||
await game.toNextTurn();
|
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 () => {
|
it("should give 2 Leftovers, 1 Shell Bell, and Black Sludge", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1);
|
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);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier;
|
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 game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
|
@ -112,11 +112,11 @@ describe("Weird Dream - Mystery Encounter", () => {
|
|||||||
it("should transform the new party into new species, 2 at +90/+110, the rest at +40/50 BST", async () => {
|
it("should transform the new party into new species, 2 at +90/+110, the rest at +40/50 BST", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
||||||
|
|
||||||
const pokemonPrior = scene.getPlayerParty().map(pokemon => pokemon);
|
const pokemonPrior = scene.getPlayerParty().slice();
|
||||||
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
|
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
|
||||||
|
|
||||||
await runMysteryEncounterToEnd(game, 1);
|
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);
|
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
||||||
|
|
||||||
const pokemonAfter = scene.getPlayerParty();
|
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 () => {
|
it("should have 1 Memory Mushroom, 5 Rogue Balls, and 3 Mints in rewards", async () => {
|
||||||
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 1);
|
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);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -196,9 +196,9 @@ describe("Weird Dream - Mystery Encounter", () => {
|
|||||||
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
|
||||||
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
await runMysteryEncounterToEnd(game, 2, undefined, true);
|
||||||
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
await skipBattleRunMysteryEncounterRewardsPhase(game);
|
||||||
await game.phaseInterceptor.to(SelectModifierPhase, false);
|
await game.phaseInterceptor.to("SelectModifierPhase", false);
|
||||||
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
|
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);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
|
@ -37,7 +37,7 @@ describe("Mystery Encounter Phases", () => {
|
|||||||
SpeciesId.VOLCARONA,
|
SpeciesId.VOLCARONA,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await game.phaseInterceptor.to(MysteryEncounterPhase, false);
|
await game.phaseInterceptor.to("MysteryEncounterPhase", false);
|
||||||
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
|
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -49,9 +49,9 @@ describe("Mystery Encounter Phases", () => {
|
|||||||
|
|
||||||
game.onNextPrompt("MysteryEncounterPhase", UiMode.MYSTERY_ENCOUNTER, () => {
|
game.onNextPrompt("MysteryEncounterPhase", UiMode.MYSTERY_ENCOUNTER, () => {
|
||||||
// End phase early for test
|
// 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.length).toBeGreaterThan(0);
|
||||||
expect(game.scene.mysteryEncounterSaveData.encounteredEvents[0].type).toEqual(
|
expect(game.scene.mysteryEncounterSaveData.encounteredEvents[0].type).toEqual(
|
||||||
@ -75,7 +75,7 @@ describe("Mystery Encounter Phases", () => {
|
|||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.phaseInterceptor.run(MysteryEncounterPhase);
|
await game.phaseInterceptor.to("MysteryEncounterPhase");
|
||||||
|
|
||||||
// Select option 1 for encounter
|
// Select option 1 for encounter
|
||||||
const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler;
|
const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler;
|
||||||
|
@ -241,7 +241,7 @@ describe("SelectModifierPhase", () => {
|
|||||||
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
|
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
|
||||||
scene.phaseManager.unshiftPhase(selectModifierPhase);
|
scene.phaseManager.unshiftPhase(selectModifierPhase);
|
||||||
game.move.select(MoveId.SPLASH);
|
game.move.select(MoveId.SPLASH);
|
||||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||||
|
|
||||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
@ -265,7 +265,7 @@ describe("SelectModifierPhase", () => {
|
|||||||
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
|
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
|
||||||
scene.phaseManager.unshiftPhase(selectModifierPhase);
|
scene.phaseManager.unshiftPhase(selectModifierPhase);
|
||||||
game.move.select(MoveId.SPLASH);
|
game.move.select(MoveId.SPLASH);
|
||||||
await game.phaseInterceptor.run(SelectModifierPhase);
|
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||||
|
|
||||||
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
|
||||||
const modifierSelectHandler = scene.ui.handlers.find(
|
const modifierSelectHandler = scene.ui.handlers.find(
|
||||||
|
82
test/system/rename-run.test.ts
Normal file
82
test/system/rename-run.test.ts
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import * as account from "#app/account";
|
||||||
|
import * as bypassLoginModule from "#app/global-vars/bypass-login";
|
||||||
|
import { pokerogueApi } from "#app/plugins/api/pokerogue-api";
|
||||||
|
import type { SessionSaveData } from "#app/system/game-data";
|
||||||
|
import { AbilityId } from "#enums/ability-id";
|
||||||
|
import { MoveId } from "#enums/move-id";
|
||||||
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
|
import Phaser from "phaser";
|
||||||
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
|
describe("System - Rename Run", () => {
|
||||||
|
let phaserGame: Phaser.Game;
|
||||||
|
let game: GameManager;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
phaserGame = new Phaser.Game({
|
||||||
|
type: Phaser.HEADLESS,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
game = new GameManager(phaserGame);
|
||||||
|
game.override
|
||||||
|
.moveset([MoveId.SPLASH])
|
||||||
|
.battleStyle("single")
|
||||||
|
.enemyAbility(AbilityId.BALL_FETCH)
|
||||||
|
.enemyMoveset(MoveId.SPLASH);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
game.phaseInterceptor.restoreOg();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("renameSession", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(false);
|
||||||
|
vi.spyOn(account, "updateUserInfo").mockImplementation(async () => [true, 1]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if slotId < 0", async () => {
|
||||||
|
const result = await game.scene.gameData.renameSession(-1, "Named Run");
|
||||||
|
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if getSession returns null", async () => {
|
||||||
|
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue(null as unknown as SessionSaveData);
|
||||||
|
|
||||||
|
const result = await game.scene.gameData.renameSession(-1, "Named Run");
|
||||||
|
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true if bypassLogin is true", async () => {
|
||||||
|
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true);
|
||||||
|
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
|
||||||
|
|
||||||
|
const result = await game.scene.gameData.renameSession(0, "Named Run");
|
||||||
|
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return false if api returns error", async () => {
|
||||||
|
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
|
||||||
|
vi.spyOn(pokerogueApi.savedata.session, "update").mockResolvedValue("Unknown Error!");
|
||||||
|
|
||||||
|
const result = await game.scene.gameData.renameSession(0, "Named Run");
|
||||||
|
|
||||||
|
expect(result).toEqual(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should return true if api is succesfull", async () => {
|
||||||
|
vi.spyOn(game.scene.gameData, "getSession").mockResolvedValue({} as SessionSaveData);
|
||||||
|
vi.spyOn(pokerogueApi.savedata.session, "update").mockResolvedValue("");
|
||||||
|
|
||||||
|
const result = await game.scene.gameData.renameSession(0, "Named Run");
|
||||||
|
|
||||||
|
expect(result).toEqual(true);
|
||||||
|
expect(account.updateUserInfo).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
@ -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 { CheckSwitchPhase } from "#phases/check-switch-phase";
|
||||||
import { CommandPhase } from "#phases/command-phase";
|
import { CommandPhase } from "#phases/command-phase";
|
||||||
import { EncounterPhase } from "#phases/encounter-phase";
|
import { EncounterPhase } from "#phases/encounter-phase";
|
||||||
import { LoginPhase } from "#phases/login-phase";
|
|
||||||
import { MovePhase } from "#phases/move-phase";
|
import { MovePhase } from "#phases/move-phase";
|
||||||
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
|
||||||
import { NewBattlePhase } from "#phases/new-battle-phase";
|
import { NewBattlePhase } from "#phases/new-battle-phase";
|
||||||
import { SelectStarterPhase } from "#phases/select-starter-phase";
|
import { SelectStarterPhase } from "#phases/select-starter-phase";
|
||||||
import type { SelectTargetPhase } from "#phases/select-target-phase";
|
import type { SelectTargetPhase } from "#phases/select-target-phase";
|
||||||
import { TitlePhase } from "#phases/title-phase";
|
|
||||||
import { TurnEndPhase } from "#phases/turn-end-phase";
|
import { TurnEndPhase } from "#phases/turn-end-phase";
|
||||||
import { TurnInitPhase } from "#phases/turn-init-phase";
|
import { TurnInitPhase } from "#phases/turn-init-phase";
|
||||||
import { TurnStartPhase } from "#phases/turn-start-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 { generateStarter } from "#test/test-utils/game-manager-utils";
|
||||||
import { GameWrapper } from "#test/test-utils/game-wrapper";
|
import { GameWrapper } from "#test/test-utils/game-wrapper";
|
||||||
import { ChallengeModeHelper } from "#test/test-utils/helpers/challenge-mode-helper";
|
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 { ModifierHelper } from "#test/test-utils/helpers/modifiers-helper";
|
||||||
import { MoveHelper } from "#test/test-utils/helpers/move-helper";
|
import { MoveHelper } from "#test/test-utils/helpers/move-helper";
|
||||||
import { OverridesHelper } from "#test/test-utils/helpers/overrides-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 { ReloadHelper } from "#test/test-utils/helpers/reload-helper";
|
||||||
import { SettingsHelper } from "#test/test-utils/helpers/settings-helper";
|
import { SettingsHelper } from "#test/test-utils/helpers/settings-helper";
|
||||||
import type { InputsHandler } from "#test/test-utils/inputs-handler";
|
import type { InputsHandler } from "#test/test-utils/inputs-handler";
|
||||||
import { MockFetch } from "#test/test-utils/mocks/mock-fetch";
|
import { MockFetch } from "#test/test-utils/mocks/mock-fetch";
|
||||||
import { PhaseInterceptor } from "#test/test-utils/phase-interceptor";
|
import { PhaseInterceptor } from "#test/test-utils/phase-interceptor";
|
||||||
import { TextInterceptor } from "#test/test-utils/text-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 { BallUiHandler } from "#ui/ball-ui-handler";
|
||||||
import type { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
|
import type { BattleMessageUiHandler } from "#ui/battle-message-ui-handler";
|
||||||
import type { CommandUiHandler } from "#ui/command-ui-handler";
|
import type { CommandUiHandler } from "#ui/command-ui-handler";
|
||||||
@ -67,6 +66,7 @@ export class GameManager {
|
|||||||
public phaseInterceptor: PhaseInterceptor;
|
public phaseInterceptor: PhaseInterceptor;
|
||||||
public textInterceptor: TextInterceptor;
|
public textInterceptor: TextInterceptor;
|
||||||
public inputsHandler: InputsHandler;
|
public inputsHandler: InputsHandler;
|
||||||
|
public readonly promptHandler: PromptHandler;
|
||||||
public readonly override: OverridesHelper;
|
public readonly override: OverridesHelper;
|
||||||
public readonly move: MoveHelper;
|
public readonly move: MoveHelper;
|
||||||
public readonly classicMode: ClassicModeHelper;
|
public readonly classicMode: ClassicModeHelper;
|
||||||
@ -84,7 +84,6 @@ export class GameManager {
|
|||||||
*/
|
*/
|
||||||
constructor(phaserGame: Phaser.Game, bypassLogin = true) {
|
constructor(phaserGame: Phaser.Game, bypassLogin = true) {
|
||||||
localStorage.clear();
|
localStorage.clear();
|
||||||
ErrorInterceptor.getInstance().clear();
|
|
||||||
// Simulate max rolls on RNG functions
|
// Simulate max rolls on RNG functions
|
||||||
// TODO: Create helpers for disabling/enabling battle RNG
|
// TODO: Create helpers for disabling/enabling battle RNG
|
||||||
BattleScene.prototype.randBattleSeedInt = (range, min = 0) => min + range - 1;
|
BattleScene.prototype.randBattleSeedInt = (range, min = 0) => min + range - 1;
|
||||||
@ -104,6 +103,7 @@ export class GameManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.textInterceptor = new TextInterceptor(this.scene);
|
this.textInterceptor = new TextInterceptor(this.scene);
|
||||||
|
this.promptHandler = new PromptHandler(this);
|
||||||
this.override = new OverridesHelper(this);
|
this.override = new OverridesHelper(this);
|
||||||
this.move = new MoveHelper(this);
|
this.move = new MoveHelper(this);
|
||||||
this.classicMode = new ClassicModeHelper(this);
|
this.classicMode = new ClassicModeHelper(this);
|
||||||
@ -159,7 +159,8 @@ export class GameManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End the currently running phase immediately.
|
* End the current phase immediately.
|
||||||
|
* @see {@linkcode PhaseInterceptor.shiftPhase} Function to skip the next upcoming phase
|
||||||
*/
|
*/
|
||||||
endPhase() {
|
endPhase() {
|
||||||
this.scene.phaseManager.getCurrentPhase()?.end();
|
this.scene.phaseManager.getCurrentPhase()?.end();
|
||||||
@ -172,15 +173,18 @@ export class GameManager {
|
|||||||
* @param mode - The mode to wait for.
|
* @param mode - The mode to wait for.
|
||||||
* @param callback - The callback function to execute on next prompt.
|
* @param callback - The callback function to execute on next prompt.
|
||||||
* @param expireFn - Optional function to determine if the prompt has expired.
|
* @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(
|
onNextPrompt(
|
||||||
phaseTarget: string,
|
phaseTarget: PhaseString,
|
||||||
mode: UiMode,
|
mode: UiMode,
|
||||||
callback: () => void,
|
callback: () => void,
|
||||||
expireFn?: () => void,
|
expireFn?: () => boolean,
|
||||||
awaitingActionInput = false,
|
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.
|
* @returns A promise that resolves when the title phase is reached.
|
||||||
*/
|
*/
|
||||||
async runToTitle(): Promise<void> {
|
async runToTitle(): Promise<void> {
|
||||||
await this.phaseInterceptor.whenAboutToRun(LoginPhase);
|
// Go to login phase and skip past it
|
||||||
this.phaseInterceptor.pop();
|
await this.phaseInterceptor.to("LoginPhase", false);
|
||||||
await this.phaseInterceptor.run(TitlePhase);
|
this.phaseInterceptor.shiftPhase();
|
||||||
|
await this.phaseInterceptor.to("TitlePhase");
|
||||||
|
|
||||||
this.scene.gameSpeed = 5;
|
this.scene.gameSpeed = 5;
|
||||||
this.scene.moveAnimations = false;
|
this.scene.moveAnimations = false;
|
||||||
@ -270,7 +275,7 @@ export class GameManager {
|
|||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.phaseInterceptor.run(EncounterPhase);
|
await this.phaseInterceptor.to("EncounterPhase");
|
||||||
if (!isNullOrUndefined(encounterType)) {
|
if (!isNullOrUndefined(encounterType)) {
|
||||||
expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(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`
|
* @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).
|
* (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, () => {
|
this.onNextPrompt(inPhase, UiMode.PARTY, () => {
|
||||||
const partyHandler = this.scene.ui.getHandler() as PartyUiHandler;
|
const partyHandler = this.scene.ui.getHandler() as PartyUiHandler;
|
||||||
|
|
||||||
@ -542,7 +547,7 @@ export class GameManager {
|
|||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
async setTurnOrder(order: BattlerIndex[]): Promise<void> {
|
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);
|
vi.spyOn(this.scene.phaseManager.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order);
|
||||||
}
|
}
|
||||||
|
@ -47,12 +47,14 @@ export class GameWrapper {
|
|||||||
public scene: BattleScene;
|
public scene: BattleScene;
|
||||||
|
|
||||||
constructor(phaserGame: Phaser.Game, bypassLogin: boolean) {
|
constructor(phaserGame: Phaser.Game, bypassLogin: boolean) {
|
||||||
|
// TODO: Figure out how to actually set RNG states correctly
|
||||||
Phaser.Math.RND.sow(["test"]);
|
Phaser.Math.RND.sow(["test"]);
|
||||||
// vi.spyOn(Utils, "apiFetch", "get").mockReturnValue(fetch);
|
// vi.spyOn(Utils, "apiFetch", "get").mockReturnValue(fetch);
|
||||||
if (bypassLogin) {
|
if (bypassLogin) {
|
||||||
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true);
|
vi.spyOn(bypassLoginModule, "bypassLogin", "get").mockReturnValue(true);
|
||||||
}
|
}
|
||||||
this.game = phaserGame;
|
this.game = phaserGame;
|
||||||
|
// TODO: Move these mocks elsewhere
|
||||||
MoveAnim.prototype.getAnim = () => ({
|
MoveAnim.prototype.getAnim = () => ({
|
||||||
frames: {},
|
frames: {},
|
||||||
});
|
});
|
||||||
@ -71,10 +73,16 @@ export class GameWrapper {
|
|||||||
PokedexMonContainer.prototype.remove = MockContainer.prototype.remove;
|
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.scene = scene;
|
||||||
this.injectMandatory();
|
this.injectMandatory();
|
||||||
this.scene.preload?.();
|
this.scene.preload();
|
||||||
this.scene.create();
|
this.scene.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ export class ChallengeModeHelper extends GameManagerHelper {
|
|||||||
selectStarterPhase.initBattle(starters);
|
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) {
|
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0 && this.game.override.removeEnemyStartingItems) {
|
||||||
this.game.removeEnemyHeldItems();
|
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 = [];
|
this.game.scene.modifiers = [];
|
||||||
}
|
}
|
||||||
titlePhase.loadSaveSlot(-1); // Load the desired session data
|
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
|
// Run through prompts for switching Pokemon, copied from classicModeHelper.ts
|
||||||
if (this.game.scene.battleStyle === BattleStyle.SWITCH) {
|
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 { UiMode } from "#enums/ui-mode";
|
||||||
import { AttemptRunPhase } from "#phases/attempt-run-phase";
|
// biome-ignore-start lint/correctness/noUnusedImports: TSDoc imports
|
||||||
import { BattleEndPhase } from "#phases/battle-end-phase";
|
import type { GameManager } from "#test/test-utils/game-manager";
|
||||||
import { BerryPhase } from "#phases/berry-phase";
|
import type { PromptHandler } from "#test/test-utils/helpers/prompt-handler";
|
||||||
import { CheckSwitchPhase } from "#phases/check-switch-phase";
|
// biome-ignore-end lint/correctness/noUnusedImports: TSDoc imports
|
||||||
import { CommandPhase } from "#phases/command-phase";
|
import { format } from "util";
|
||||||
import { DamageAnimPhase } from "#phases/damage-anim-phase";
|
import chalk from "chalk";
|
||||||
import { EggLapsePhase } from "#phases/egg-lapse-phase";
|
import { vi } from "vitest";
|
||||||
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";
|
|
||||||
|
|
||||||
export interface PromptHandler {
|
|
||||||
phaseTarget?: string;
|
|
||||||
mode?: UiMode;
|
|
||||||
callback?: () => void;
|
|
||||||
expireFn?: () => void;
|
|
||||||
awaitingActionInput?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
type PhaseInterceptorPhase = PhaseClass | PhaseString;
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of phases with their corresponding start methods.
|
* A Set containing phase names that will not be shown in the console when started.
|
||||||
*
|
*
|
||||||
* CAUTION: If a phase and its subclasses (if any) both appear in this list,
|
* Used to reduce console noise from very repetitive phases.
|
||||||
* 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.
|
|
||||||
*/
|
*/
|
||||||
private PHASES = [
|
const blacklistedPhaseNames: ReadonlySet<PhaseString> = new Set(["ActivatePriorityQueuePhase"]);
|
||||||
[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],
|
|
||||||
];
|
|
||||||
|
|
||||||
private endBySetMode = [
|
/**
|
||||||
TitlePhase,
|
* The interceptor's current state.
|
||||||
SelectGenderPhase,
|
* Possible values are the following:
|
||||||
CommandPhase,
|
* - `running`: The interceptor is currently running a phase.
|
||||||
SelectModifierPhase,
|
* - `interrupted`: The interceptor has been interrupted by a UI prompt and is waiting for the caller to end it.
|
||||||
MysteryEncounterPhase,
|
* - `idling`: The interceptor is not currently running a phase and is ready to start a new one.
|
||||||
PostMysteryEncounterPhase,
|
*/
|
||||||
];
|
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 {
|
||||||
|
private scene: BattleScene;
|
||||||
|
/**
|
||||||
|
* 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}.
|
||||||
|
*/
|
||||||
|
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 target: PhaseString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor to initialize the scene and properties, and to start the phase handling.
|
* 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.scene = scene;
|
||||||
this.onHold = [];
|
// Mock the private startCurrentPhase method to toggle `isRunning` rather than actually starting anything
|
||||||
this.prompts = [];
|
vi.spyOn(this.scene.phaseManager as any, "startCurrentPhase").mockImplementation(() => {
|
||||||
this.clearLogs();
|
this.state = "idling";
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to transition to a target phase.
|
* Method to transition to a target phase.
|
||||||
* @param phaseTo - The phase to transition to.
|
* @param target - The name of the {@linkcode Phase} to transition to
|
||||||
* @param runTarget - Whether or not to run the target phase; default `true`.
|
* @param runTarget - Whether or not to run the target phase before resolving; default `true`
|
||||||
* @returns A promise that resolves when the transition is complete.
|
* @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> {
|
public async to(target: PhaseString | Constructor<Phase>, runTarget = true): Promise<void> {
|
||||||
return new Promise(async (resolve, reject) => {
|
this.target = typeof target === "string" ? target : (target.name as PhaseString);
|
||||||
ErrorInterceptor.getInstance().add(this);
|
|
||||||
if (this.phaseFrom) {
|
const pm = this.scene.phaseManager;
|
||||||
await this.run(this.phaseFrom).catch(e => reject(e));
|
|
||||||
this.phaseFrom = null;
|
// 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;
|
||||||
}
|
}
|
||||||
const targetName = typeof phaseTo === "string" ? phaseTo : phaseTo.name;
|
return false;
|
||||||
this.intervalRun = setInterval(async () => {
|
}
|
||||||
const currentPhase = this.onHold?.length && this.onHold[0];
|
|
||||||
if (currentPhase && currentPhase.name === targetName) {
|
currentPhase = pm.getCurrentPhase()!;
|
||||||
clearInterval(this.intervalRun);
|
// 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) {
|
if (!runTarget) {
|
||||||
return resolve();
|
this.doLog(`PhaseInterceptor.to: Stopping before running ${this.target}`);
|
||||||
}
|
return;
|
||||||
await this.run(currentPhase).catch(e => {
|
|
||||||
clearInterval(this.intervalRun);
|
|
||||||
return reject(e);
|
|
||||||
});
|
|
||||||
return resolve();
|
|
||||||
}
|
|
||||||
if (currentPhase && currentPhase.name !== targetName) {
|
|
||||||
await this.run(currentPhase).catch(e => {
|
|
||||||
clearInterval(this.intervalRun);
|
|
||||||
return reject(e);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
await this.run(currentPhase);
|
||||||
* Method to run a phase with an optional skip function.
|
this.doLog(
|
||||||
* @param phaseTarget - The phase to run.
|
`PhaseInterceptor.to: Stopping ${this.state === "interrupted" ? `after reaching UiMode.${UiMode[this.scene.ui.getMode()]} during` : "on completion of"} ${this.target}`,
|
||||||
* @param skipFn - Optional skip function.
|
|
||||||
* @returns A promise that resolves when the phase is run.
|
|
||||||
*/
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
pop() {
|
|
||||||
this.onHold.pop();
|
|
||||||
this.scene.phaseManager.shiftPhase();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the current phase from the phase interceptor.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
shift(shouldRun = false): void {
|
|
||||||
this.onHold.shift();
|
|
||||||
if (shouldRun) {
|
|
||||||
this.scene.phaseManager.shiftPhase();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to initialize phases and their corresponding methods.
|
|
||||||
*/
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method to start a phase and log it.
|
|
||||||
* @param phase - The phase to start.
|
|
||||||
*/
|
|
||||||
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
|
* Internal wrapper method to start a phase and wait until it finishes.
|
||||||
* @param mode - The {@linkcode Mode} to set.
|
* @param currentPhase - The {@linkcode Phase} to run
|
||||||
* @param args - Additional arguments to pass to the original method.
|
* @returns A Promise that resolves when the phase has completed running.
|
||||||
*/
|
*/
|
||||||
setOverlayMode(mode: UiMode, ...args: unknown[]): Promise<void> {
|
private async run(currentPhase: Phase): Promise<void> {
|
||||||
const instance = this.scene.ui;
|
try {
|
||||||
console.log("setOverlayMode", `${UiMode[mode]} (=${mode})`, args);
|
this.state = "running";
|
||||||
const ret = this.originalSetOverlayMode.apply(instance, [mode, ...args]);
|
this.logPhase(currentPhase.phaseName);
|
||||||
return ret;
|
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)}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to start the prompt handler.
|
* 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}.
|
||||||
*/
|
*/
|
||||||
startPromptHandler() {
|
public checkMode(): void {
|
||||||
this.promptInterval = setInterval(() => {
|
const currentPhase = this.scene.phaseManager.getCurrentPhase()!;
|
||||||
if (this.prompts.length) {
|
if (!currentPhase.is(this.target) || this.state === "interrupted") {
|
||||||
const actionForNextPrompt = this.prompts[0];
|
// Wrong phase / already interrupted = do nothing
|
||||||
const expireFn = actionForNextPrompt.expireFn?.();
|
return;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
// Interrupt the phase and return control to the caller
|
||||||
});
|
this.state = "interrupted";
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method to add an action to the next prompt.
|
* Skip the next upcoming phase.
|
||||||
* @param phaseTarget - The target phase for the prompt.
|
* @throws Error if currently running a phase.
|
||||||
* @param mode - The mode of the UI.
|
* @remarks
|
||||||
* @param callback - The callback function to execute.
|
* This function should be used for skipping phases _not yet started_.
|
||||||
* @param expireFn - The function to determine if the prompt has expired.
|
* To end ones already in the process of running, use {@linkcode GameManager.endPhase}.
|
||||||
* @param awaitingActionInput - ???; default `false`
|
* @example
|
||||||
|
* await game.phaseInterceptor.to("LoginPhase", false);
|
||||||
|
* game.phaseInterceptor.shiftPhase();
|
||||||
*/
|
*/
|
||||||
addToNextPrompt(
|
public shiftPhase(): void {
|
||||||
phaseTarget: string,
|
const phaseName = this.scene.phaseManager.getCurrentPhase()!.phaseName;
|
||||||
mode: UiMode,
|
if (this.state !== "idling") {
|
||||||
callback: () => void,
|
throw new Error(`shiftPhase attempted to skip phase ${phaseName} mid-execution!`);
|
||||||
expireFn?: () => void,
|
}
|
||||||
awaitingActionInput = false,
|
this.doLog(`Skipping current phase ${phaseName}`);
|
||||||
) {
|
this.scene.phaseManager.shiftPhase();
|
||||||
this.prompts.push({
|
|
||||||
phaseTarget,
|
|
||||||
mode,
|
|
||||||
callback,
|
|
||||||
expireFn,
|
|
||||||
awaitingActionInput,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Restores the original state of phases and clears intervals.
|
* Deprecated no-op function.
|
||||||
*
|
*
|
||||||
* This function iterates through all phases and resets their `start` method to the original
|
* This was previously used to reset timers created using `setInterval` to wait for phase end
|
||||||
* function stored in `this.phases`. Additionally, it clears the `promptInterval` and `interval`.
|
* 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
|
||||||
*/
|
*/
|
||||||
restoreOg() {
|
public restoreOg() {}
|
||||||
for (const [phase] of this.PHASES) {
|
|
||||||
phase.prototype.start = this.phases[phase.name].start;
|
/**
|
||||||
|
* 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}`);
|
||||||
}
|
}
|
||||||
UI.prototype.setMode = this.originalSetMode;
|
this.log.push(phaseName);
|
||||||
UI.prototype.setOverlayMode = this.originalSetOverlayMode;
|
}
|
||||||
Phase.prototype.end = this.originalSuperEnd;
|
|
||||||
clearInterval(this.promptInterval);
|
/**
|
||||||
clearInterval(this.interval);
|
* Clear all prior phase logs.
|
||||||
clearInterval(this.intervalRun);
|
*/
|
||||||
|
public clearLogs(): void {
|
||||||
|
this.log = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper function to add coral coloration to phase logs.
|
||||||
|
* @param args - Arguments to original logging function.
|
||||||
|
*/
|
||||||
|
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.
|
// Open the pokedex UI.
|
||||||
await game.runToTitle();
|
await game.runToTitle();
|
||||||
|
|
||||||
await game.phaseInterceptor.setOverlayMode(UiMode.POKEDEX);
|
await game.scene.ui.setOverlayMode(UiMode.POKEDEX);
|
||||||
|
|
||||||
// Get the handler for the current UI.
|
// Get the handler for the current UI.
|
||||||
const handler = game.scene.ui.getHandler();
|
const handler = game.scene.ui.getHandler();
|
||||||
@ -89,7 +89,7 @@ describe("UI - Pokedex", () => {
|
|||||||
// Open the pokedex UI.
|
// Open the pokedex UI.
|
||||||
await game.runToTitle();
|
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.
|
// Get the handler for the current UI.
|
||||||
const handler = game.scene.ui.getHandler();
|
const handler = game.scene.ui.getHandler();
|
||||||
|
@ -6,8 +6,6 @@ import { GameModes } from "#enums/game-modes";
|
|||||||
import { Nature } from "#enums/nature";
|
import { Nature } from "#enums/nature";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
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 type { TitlePhase } from "#phases/title-phase";
|
||||||
import { GameManager } from "#test/test-utils/game-manager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
|
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.RIGHT);
|
||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -88,7 +85,7 @@ describe("UI - Starter select", () => {
|
|||||||
resolve();
|
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].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||||
@ -115,9 +112,8 @@ describe("UI - Starter select", () => {
|
|||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -149,7 +145,7 @@ describe("UI - Starter select", () => {
|
|||||||
resolve();
|
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].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
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_NATURE);
|
||||||
handler.processInput(Button.CYCLE_ABILITY);
|
handler.processInput(Button.CYCLE_ABILITY);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -213,7 +208,7 @@ describe("UI - Starter select", () => {
|
|||||||
resolve();
|
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].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||||
@ -242,9 +237,8 @@ describe("UI - Starter select", () => {
|
|||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.CYCLE_GENDER);
|
handler.processInput(Button.CYCLE_GENDER);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -276,7 +270,7 @@ describe("UI - Starter select", () => {
|
|||||||
resolve();
|
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].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
||||||
@ -303,9 +297,8 @@ describe("UI - Starter select", () => {
|
|||||||
handler.processInput(Button.LEFT);
|
handler.processInput(Button.LEFT);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
handler.processInput(Button.CYCLE_SHINY);
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -337,7 +330,7 @@ describe("UI - Starter select", () => {
|
|||||||
resolve();
|
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].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(false);
|
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.CYCLE_SHINY);
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -399,7 +391,7 @@ describe("UI - Starter select", () => {
|
|||||||
resolve();
|
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].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
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.CYCLE_SHINY);
|
handler.processInput(Button.CYCLE_SHINY);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -460,7 +451,7 @@ describe("UI - Starter select", () => {
|
|||||||
resolve();
|
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].species.speciesId).toBe(SpeciesId.BULBASAUR);
|
||||||
expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
|
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.RIGHT);
|
handler.processInput(Button.RIGHT);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -527,7 +517,7 @@ describe("UI - Starter select", () => {
|
|||||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
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);
|
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.RIGHT);
|
||||||
handler.processInput(Button.DOWN);
|
handler.processInput(Button.DOWN);
|
||||||
handler.processInput(Button.ACTION);
|
handler.processInput(Button.ACTION);
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.run(SelectStarterPhase);
|
await game.phaseInterceptor.to("SelectStarterPhase");
|
||||||
let options: OptionSelectItem[] = [];
|
let options: OptionSelectItem[] = [];
|
||||||
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
let optionSelectUiHandler: OptionSelectUiHandler | undefined;
|
||||||
await new Promise<void>(resolve => {
|
await new Promise<void>(resolve => {
|
||||||
@ -593,7 +582,7 @@ describe("UI - Starter select", () => {
|
|||||||
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
|
||||||
saveSlotSelectUiHandler.processInput(Button.ACTION);
|
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);
|
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.NIDORAN_M);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -72,8 +72,6 @@ describe("UI - Transfer Items", () => {
|
|||||||
expect(
|
expect(
|
||||||
handler.optionsContainer.list.some(option => RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text)),
|
handler.optionsContainer.list.some(option => RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text)),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
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(
|
expect(handler.optionsContainer.list.some(option => (option as BBCodeText).text?.includes("Transfer"))).toBe(
|
||||||
true,
|
true,
|
||||||
);
|
);
|
||||||
|
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
await game.phaseInterceptor.to("SelectModifierPhase");
|
await game.phaseInterceptor.to("SelectModifierPhase");
|
||||||
|
@ -2,7 +2,6 @@ import { Button } from "#enums/buttons";
|
|||||||
import { MoveId } from "#enums/move-id";
|
import { MoveId } from "#enums/move-id";
|
||||||
import { SpeciesId } from "#enums/species-id";
|
import { SpeciesId } from "#enums/species-id";
|
||||||
import { UiMode } from "#enums/ui-mode";
|
import { UiMode } from "#enums/ui-mode";
|
||||||
import { CommandPhase } from "#phases/command-phase";
|
|
||||||
import { GameManager } from "#test/test-utils/game-manager";
|
import { GameManager } from "#test/test-utils/game-manager";
|
||||||
import type { MockText } from "#test/test-utils/mocks/mocks-container/mock-text";
|
import type { MockText } from "#test/test-utils/mocks/mocks-container/mock-text";
|
||||||
import { FightUiHandler } from "#ui/fight-ui-handler";
|
import { FightUiHandler } from "#ui/fight-ui-handler";
|
||||||
@ -46,7 +45,6 @@ describe("UI - Type Hints", () => {
|
|||||||
const { ui } = game.scene;
|
const { ui } = game.scene;
|
||||||
const handler = ui.getHandler<FightUiHandler>();
|
const handler = ui.getHandler<FightUiHandler>();
|
||||||
handler.processInput(Button.ACTION); // select "Fight"
|
handler.processInput(Button.ACTION); // select "Fight"
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
||||||
@ -59,7 +57,7 @@ describe("UI - Type Hints", () => {
|
|||||||
expect.soft(dragonClawText.color).toBe("#929292");
|
expect.soft(dragonClawText.color).toBe("#929292");
|
||||||
ui.getHandler().processInput(Button.ACTION);
|
ui.getHandler().processInput(Button.ACTION);
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.to(CommandPhase);
|
await game.phaseInterceptor.to("CommandPhase");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("check status move color", async () => {
|
it("check status move color", async () => {
|
||||||
@ -71,7 +69,6 @@ describe("UI - Type Hints", () => {
|
|||||||
const { ui } = game.scene;
|
const { ui } = game.scene;
|
||||||
const handler = ui.getHandler<FightUiHandler>();
|
const handler = ui.getHandler<FightUiHandler>();
|
||||||
handler.processInput(Button.ACTION); // select "Fight"
|
handler.processInput(Button.ACTION); // select "Fight"
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
||||||
@ -84,7 +81,7 @@ describe("UI - Type Hints", () => {
|
|||||||
expect.soft(growlText.color).toBe(undefined);
|
expect.soft(growlText.color).toBe(undefined);
|
||||||
ui.getHandler().processInput(Button.ACTION);
|
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 () => {
|
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 { ui } = game.scene;
|
||||||
const handler = ui.getHandler<FightUiHandler>();
|
const handler = ui.getHandler<FightUiHandler>();
|
||||||
handler.processInput(Button.ACTION); // select "Fight"
|
handler.processInput(Button.ACTION); // select "Fight"
|
||||||
game.phaseInterceptor.unlock();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
|
||||||
@ -121,6 +117,6 @@ describe("UI - Type Hints", () => {
|
|||||||
expect.soft(shadowBallText.color).toBe(undefined);
|
expect.soft(shadowBallText.color).toBe(undefined);
|
||||||
ui.getHandler().processInput(Button.ACTION);
|
ui.getHandler().processInput(Button.ACTION);
|
||||||
});
|
});
|
||||||
await game.phaseInterceptor.to(CommandPhase);
|
await game.phaseInterceptor.to("CommandPhase");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import "vitest-canvas-mock";
|
import "vitest-canvas-mock";
|
||||||
|
import { PromptHandler } from "#test/test-utils/helpers/prompt-handler";
|
||||||
import { initTests } from "#test/test-utils/test-file-initialization";
|
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. */
|
/** Set the timezone to UTC for tests. */
|
||||||
|
|
||||||
@ -48,8 +49,6 @@ vi.mock("i18next", async importOriginal => {
|
|||||||
return await importOriginal();
|
return await importOriginal();
|
||||||
});
|
});
|
||||||
|
|
||||||
global.testFailed = false;
|
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
initTests();
|
initTests();
|
||||||
});
|
});
|
||||||
@ -58,3 +57,20 @@ afterAll(() => {
|
|||||||
global.server.close();
|
global.server.close();
|
||||||
console.log("Closing i18n MSW server!");
|
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