Compare commits

...

10 Commits

Author SHA1 Message Date
Bertie690
965665daeb
Merge 552e002110 into f42237d415 2025-08-14 13:28:36 -04:00
Bertie690
f42237d415
[Refactor] Removed map(x => x) (#6256)
* Enforced a few usages of `toCamelCase`

* Removed `map(x => x)`

* Removed more maps and sufff

* Update test/mystery-encounter/encounters/weird-dream-encounter.test.ts

* Update game-data.ts types to work
2025-08-14 10:25:44 -07:00
fabske0
b44f0a4176
[Refactor] Remove bgm param from arena constructor (#6254) 2025-08-14 16:52:56 +00:00
NightKev
552e002110
Merge branch 'beta' into get-current-phase 2025-08-14 08:43:46 -07:00
Bertie690
22f36e7c18
Update phase-manager.ts
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-14 11:41:53 -04:00
Sirz Benjie
076ef81691
[Bug] [UI/UX] [Beta] Fix icons not showing in save slot selection (#6262)
Fix icons not showing in save slot selection
2025-08-13 20:49:46 -05:00
fabske0
23271901cf
[Docs] Add locale key naming info to localization.md (#6260) 2025-08-14 01:12:00 +00:00
Inês Simões
1517e0512e
[UI/UX] [Feature] Save Management Tool (Rename/Delete Saves) (#5978)
* Implement Name Run Feat
Modified load session ui component, adding a submenu when selecting a 3
slot. This menu has 4 options:
Load Game -> Behaves as before, allowing the player to continue
progress from the last saved state in the slot.

Rename Run -> Overlays a rename form, allowing the player to type a
name for the run, checking for string validity, with the option to
cancel or confirm (Rename).

Delete Run -> Prompts user confirmation to delete save data, removing
the current save slot from the users save data.

Cancel -> Hides menu overlay.

Modified game data to implement a function to accept and store
runNameText to the users data.

Modified run info ui component, to display the chosen name when
viewing run information.

Example: When loading the game, the user can choose the Load Game
menu option, then select a save slot, prompting the menu, then choose
"Rename Run" and type the name "Monotype Water Run" then confirm,
thus being able to better organize their save files.

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Implement Rename Input Design and Tests for Name Run Feat
Created a test to verify Name Run Feature behaviour in the
backend (rename_run.test.ts), checking possible errors and
 expected behaviours.

Created a UiHandler RenameRunFormUiHandler
(rename-run-ui-handler.ts), creating a frontend input
overlay for the Name Run Feature.

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Fixed formating and best practices issues:
Rewrote renameSession to be more inline with other
API call funtions, removed debugging comments and
whitespaces.

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Minor Sanitization for aesthetics
Deleting the input when closing the overlay for
aesthetics purpose

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Fixed minor rebase alterations.

Signed-off-by: Matheus Alves matheus.r.noya.alves@tecnico.ulisboa.pt
Co-authored-by: Inês Simões ines.p.simoes@tecnico.ulisboa.pt

* Implemented Default Name Logic
Altered logic in save-slot-select-ui-handler.ts to
support default naming of runs based on the run
game mode with decideFallback function.

In game-data.ts, to prevent inconsistent naming,
added check for unfilled input, ignoring empty
rename requests.

Signed-off-by: Matheus Alves matheus.r.noya.alves@tecnico.ulisboa.pt
Co-authored-by: Inês Simões ines.p.simoes@tecnico.ulisboa.pt

* Replace fallback name logic: use first active challenge instead
of game mode

Previously used game mode as the fallback name, updated to use the
first active challenge instead (e.g. Monogen or Mono Type), which
better reflects the run's theme.
Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Rebasing and conflict resolution

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Lint fix

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Inês Simões <ines.p.simoes@tecnico.ulisboa.pt>

* Minor compile fix

* Dependency resolved

* Format name respected

* Add all active challenges to default challenge session name if possible

If more than 3 challenges are active, only the first 3 are added
to the name (to prevent the text going off-screen)
and then "..." is appended to the end to indicate
there were more challenges active than the ones listed

* Allow deleting malformed sessions

---------

Signed-off-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Signed-off-by: Matheus Alves matheus.r.noya.alves@tecnico.ulisboa.pt
Co-authored-by: Matheus Alves <matheus.r.noya.alves@tecnico.ulisboa.pt>
Co-authored-by: Wlowscha <54003515+Wlowscha@users.noreply.github.com>
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
Co-authored-by: Sirz Benjie <142067137+SirzBenjie@users.noreply.github.com>
2025-08-13 20:08:12 -05:00
Bertie690
04734bd9bf Removed bangs from various calls to phase manager 2025-08-09 11:18:31 -04:00
Bertie690
e97ef78497 Added toBeAtPhase + removed null from phase manager current phase signature 2025-08-09 10:33:21 -04:00
63 changed files with 724 additions and 296 deletions

View File

@ -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.

View File

@ -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";
@ -1203,7 +1204,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 +1240,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 +1515,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 = {
@ -1569,9 +1571,9 @@ export class BattleScene extends SceneBase {
return 0; return 0;
} }
const isEggPhase: boolean = ["EggLapsePhase", "EggHatchPhase"].includes( const isEggPhase =
this.phaseManager.getCurrentPhase()?.phaseName ?? "", this.phaseManager.getCurrentPhase().is("EggLapsePhase") ||
); this.phaseManager.getCurrentPhase().is("EggHatchPhase");
if ( if (
// Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros. // Give trainers with specialty types an appropriately-typed form for Wormadam, Rotom, Arceus, Oricorio, Silvally, or Paldean Tauros.
@ -2711,7 +2713,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)) {

View File

@ -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) : "";
} }

View File

@ -469,7 +469,7 @@ const QuickGuardConditionFunc: ProtectConditionFunc = (_arena, moveId) => {
const move = allMoves[moveId]; const move = allMoves[moveId];
const effectPhase = globalScene.phaseManager.getCurrentPhase(); const effectPhase = globalScene.phaseManager.getCurrentPhase();
if (effectPhase?.is("MoveEffectPhase")) { if (effectPhase.is("MoveEffectPhase")) {
const attacker = effectPhase.getUserPokemon(); const attacker = effectPhase.getUserPokemon();
if (attacker) { if (attacker) {
return move.getPriority(attacker) > 0; return move.getPriority(attacker) > 0;

View File

@ -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;
} }
}); }
} }

View File

@ -227,26 +227,27 @@ interface GenericSerializableBattlerTag<T extends BattlerTagType> extends Serial
* Descendants can override {@linkcode isMoveRestricted} to restrict moves that * Descendants can override {@linkcode isMoveRestricted} to restrict moves that
* match a condition. A restricted move gets cancelled before it is used. * match a condition. A restricted move gets cancelled before it is used.
* Players and enemies should not be allowed to select restricted moves. * Players and enemies should not be allowed to select restricted moves.
* @todo Require descendant subclasses to inherit a `PRE_MOVE` lapse type
*/ */
export abstract class MoveRestrictionBattlerTag extends SerializableBattlerTag { export abstract class MoveRestrictionBattlerTag extends SerializableBattlerTag {
public declare readonly tagType: MoveRestrictionBattlerTagType; public declare readonly tagType: MoveRestrictionBattlerTagType;
override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean { override lapse(pokemon: Pokemon, lapseType: BattlerTagLapseType): boolean {
if (lapseType === BattlerTagLapseType.PRE_MOVE) { if (lapseType !== BattlerTagLapseType.PRE_MOVE) {
// Cancel the affected pokemon's selected move return super.lapse(pokemon, lapseType);
const phase = globalScene.phaseManager.getCurrentPhase() as MovePhase;
const move = phase.move;
if (this.isMoveRestricted(move.moveId, pokemon)) {
if (this.interruptedText(pokemon, move.moveId)) {
globalScene.phaseManager.queueMessage(this.interruptedText(pokemon, move.moveId));
}
phase.cancel();
}
return true;
} }
return super.lapse(pokemon, lapseType); // Cancel the affected pokemon's selected move
const phase = globalScene.phaseManager.getCurrentPhase() as MovePhase;
const move = phase.move;
if (this.isMoveRestricted(move.moveId, pokemon)) {
if (this.interruptedText(pokemon, move.moveId)) {
globalScene.phaseManager.queueMessage(this.interruptedText(pokemon, move.moveId));
}
phase.cancel();
}
return true;
} }
/** /**

View File

@ -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}`;
} }
/** /**
@ -5926,8 +5932,8 @@ export class ProtectAttr extends AddBattlerTagAttr {
for (const turnMove of user.getLastXMoves(-1).slice()) { for (const turnMove of user.getLastXMoves(-1).slice()) {
if ( if (
// Quick & Wide guard increment the Protect counter without using it for fail chance // Quick & Wide guard increment the Protect counter without using it for fail chance
!(allMoves[turnMove.move].hasAttr("ProtectAttr") || !(allMoves[turnMove.move].hasAttr("ProtectAttr") ||
[MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) || [MoveId.QUICK_GUARD, MoveId.WIDE_GUARD].includes(turnMove.move)) ||
turnMove.result !== MoveResult.SUCCESS turnMove.result !== MoveResult.SUCCESS
) { ) {
break; break;

View File

@ -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`),

View File

@ -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,

View File

@ -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;
} }

View File

@ -1245,7 +1245,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
} }
// During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus" // During the Pokemon's MoveEffect phase, the offset is removed to put the Pokemon "in focus"
const currentPhase = globalScene.phaseManager.getCurrentPhase(); const currentPhase = globalScene.phaseManager.getCurrentPhase();
return !(currentPhase?.is("MoveEffectPhase") && currentPhase.getPokemon() === this); return !(currentPhase.is("MoveEffectPhase") && currentPhase.getPokemon() === this);
} }
/** If this Pokemon has a Substitute on the field, removes its sprite from the field. */ /** If this Pokemon has a Substitute on the field, removes its sprite from the field. */
@ -4929,7 +4929,7 @@ export abstract class Pokemon extends Phaser.GameObjects.Container {
*/ */
if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) { if (effect === StatusEffect.SLEEP || effect === StatusEffect.FREEZE) {
const currentPhase = globalScene.phaseManager.getCurrentPhase(); const currentPhase = globalScene.phaseManager.getCurrentPhase();
if (currentPhase?.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) { if (currentPhase.is("MoveEffectPhase") && currentPhase.getUserPokemon() === this) {
this.turnData.hitCount = 1; this.turnData.hitCount = 1;
this.turnData.hitsLeft = 1; this.turnData.hitsLeft = 1;
} }

View File

@ -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);

View File

@ -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);
} }
} }

View File

@ -236,7 +236,7 @@ export class PhaseManager {
/** Parallel array to {@linkcode dynamicPhaseQueues} - matches phase types to their queues */ /** Parallel array to {@linkcode dynamicPhaseQueues} - matches phase types to their queues */
private dynamicPhaseTypes: Constructor<Phase>[]; private dynamicPhaseTypes: Constructor<Phase>[];
private currentPhase: Phase | null = null; private currentPhase: Phase;
private standbyPhase: Phase | null = null; private standbyPhase: Phase | null = null;
constructor() { constructor() {
@ -260,7 +260,12 @@ export class PhaseManager {
} }
/* Phase Functions */ /* Phase Functions */
getCurrentPhase(): Phase | null {
/**
* Return the currently running {@linkcode Phase}.
* @returns The Phase currently in the process of running.
*/
getCurrentPhase(): Phase {
return this.currentPhase; return this.currentPhase;
} }
@ -363,13 +368,16 @@ export class PhaseManager {
} }
} }
} }
// If no phases are left to run, add phases to start a new turn.
if (!this.phaseQueue.length) { if (!this.phaseQueue.length) {
this.populatePhaseQueue(); this.populatePhaseQueue();
// Clear the conditionalQueue if there are no phases left in the phaseQueue // Clear the conditionalQueue if there are no phases left in the phaseQueue
this.conditionalQueue = []; this.conditionalQueue = [];
} }
this.currentPhase = this.phaseQueue.shift() ?? null; // Bang is justified as `populatePhaseQueue` ensures we always have _something_ in the queue at all times
this.currentPhase = this.phaseQueue.shift()!;
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,10 +397,15 @@ 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.
*/
private startCurrentPhase(): void {
console.log(`%cStart Phase ${this.currentPhase.phaseName}`, "color:green;");
this.currentPhase.start();
} }
overridePhase(phase: Phase): boolean { overridePhase(phase: Phase): boolean {
@ -402,8 +415,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;
} }

View File

@ -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)!)
: {};
// TODO: We shouldn't be storing this like that
for (const key of Object.values(Tutorial)) {
if (key === tutorial) {
tutorials[key] = status;
} else {
tutorials[key] ??= false;
}
} }
Object.keys(Tutorial) localStorage.setItem(saveDataKey, JSON.stringify(tutorials));
.map(t => t as Tutorial)
.forEach(t => {
const key = Tutorial[t];
if (key === tutorial) {
tutorials[key] = flag;
} else {
tutorials[key] ??= false;
}
});
localStorage.setItem(key, 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) => {

View File

@ -383,14 +383,14 @@ export class GameChallengesUiHandler extends UiHandler {
this.updateChallengeArrows(this.startCursor.visible); this.updateChallengeArrows(this.startCursor.visible);
} else { } else {
globalScene.phaseManager.toTitleScreen(); globalScene.phaseManager.toTitleScreen();
globalScene.phaseManager.getCurrentPhase()?.end(); globalScene.phaseManager.getCurrentPhase().end();
} }
success = true; success = true;
} else if (button === Button.SUBMIT || button === Button.ACTION) { } else if (button === Button.SUBMIT || button === Button.ACTION) {
if (this.hasSelectedChallenge) { if (this.hasSelectedChallenge) {
if (this.startCursor.visible) { if (this.startCursor.visible) {
globalScene.phaseManager.unshiftNew("SelectStarterPhase"); globalScene.phaseManager.unshiftNew("SelectStarterPhase");
globalScene.phaseManager.getCurrentPhase()?.end(); globalScene.phaseManager.getCurrentPhase().end();
} else { } else {
this.startCursor.setVisible(true); this.startCursor.setVisible(true);
this.cursorObj?.setVisible(false); this.cursorObj?.setVisible(false);

View File

@ -45,7 +45,7 @@ export class EggHatchSceneHandler extends UiHandler {
processInput(button: Button): boolean { processInput(button: Button): boolean {
if (button === Button.ACTION || button === Button.CANCEL) { if (button === Button.ACTION || button === Button.CANCEL) {
const phase = globalScene.phaseManager.getCurrentPhase(); const phase = globalScene.phaseManager.getCurrentPhase();
if (phase?.is("EggHatchPhase") && phase.trySkip()) { if (phase.is("EggHatchPhase") && phase.trySkip()) {
return true; return true;
} }
} }

View File

@ -222,7 +222,7 @@ export class EggSummaryUiHandler extends MessageUiHandler {
if (button === Button.CANCEL) { if (button === Button.CANCEL) {
if (!this.blockExit) { if (!this.blockExit) {
const phase = globalScene.phaseManager.getCurrentPhase(); const phase = globalScene.phaseManager.getCurrentPhase();
if (phase?.is("EggSummaryPhase")) { if (phase.is("EggSummaryPhase")) {
phase.end(); phase.end();
} }
success = true; success = true;

View File

@ -125,7 +125,7 @@ export class MenuUiHandler extends MessageUiHandler {
const ui = this.getUi(); const ui = this.getUi();
this.excludedMenus = () => [ this.excludedMenus = () => [
{ {
condition: !!globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase"), condition: globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase"),
options: [MenuOptions.EGG_GACHA], options: [MenuOptions.EGG_GACHA],
}, },
{ condition: bypassLogin, options: [MenuOptions.LOG_OUT] }, { condition: bypassLogin, options: [MenuOptions.LOG_OUT] },

View File

@ -816,7 +816,7 @@ export class PartyUiHandler extends MessageUiHandler {
// TODO: This risks hitting the other options (.MOVE_i and ALL) so does it? Do we need an extra check? // TODO: This risks hitting the other options (.MOVE_i and ALL) so does it? Do we need an extra check?
if ( if (
option >= PartyOption.FORM_CHANGE_ITEM && option >= PartyOption.FORM_CHANGE_ITEM &&
globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase") && globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase") &&
this.partyUiMode === PartyUiMode.CHECK this.partyUiMode === PartyUiMode.CHECK
) { ) {
const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon);
@ -1504,7 +1504,7 @@ export class PartyUiHandler extends MessageUiHandler {
break; break;
case PartyUiMode.CHECK: case PartyUiMode.CHECK:
this.addCommonOptions(pokemon); this.addCommonOptions(pokemon);
if (globalScene.phaseManager.getCurrentPhase()?.is("SelectModifierPhase")) { if (globalScene.phaseManager.getCurrentPhase().is("SelectModifierPhase")) {
const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon); const formChangeItemModifiers = this.getFormChangeItemsModifiers(pokemon);
for (let i = 0; i < formChangeItemModifiers.length; i++) { for (let i = 0; i < formChangeItemModifiers.length; i++) {
this.options.push(PartyOption.FORM_CHANGE_ITEM + i); this.options.push(PartyOption.FORM_CHANGE_ITEM + i);

View File

@ -705,7 +705,7 @@ export class PokedexPageUiHandler extends MessageUiHandler {
show(args: any[]): boolean { show(args: any[]): boolean {
// Allow the use of candies if we are in one of the whitelisted phases // Allow the use of candies if we are in one of the whitelisted phases
this.canUseCandies = ["TitlePhase", "SelectStarterPhase", "CommandPhase"].includes( this.canUseCandies = ["TitlePhase", "SelectStarterPhase", "CommandPhase"].includes(
globalScene.phaseManager.getCurrentPhase()?.phaseName ?? "", globalScene.phaseManager.getCurrentPhase().phaseName,
); );
if (args.length >= 1 && args[0] === "refresh") { if (args.length >= 1 && args[0] === "refresh") {

View 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;
}
}

View File

@ -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;
} }

View File

@ -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) {
originalCallback?.(cursor); manageDataOptions.push({
label: i18next.t("menu:loadGame"),
handler: () => {
globalScene.ui.revertMode();
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,22 +627,33 @@ 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
// Ignore the results if the view was exited .getSession(this.slotId)
if (!this.active) { .then(async sessionData => {
return; // Ignore the results if the view was exited
} if (!this.active) {
if (!sessionData) { return;
this.hasData = false; }
this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty")); this.hasData = !!sessionData;
resolve(false); if (!sessionData) {
return; this.loadingLabel.setText(i18next.t("saveSlotSelectUiHandler:empty"));
} resolve(false);
this.hasData = true; return;
this.saveData = sessionData; }
await this.setupWithData(sessionData); this.saveData = sessionData;
resolve(true); 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);
});
}); });
} }
} }

View File

@ -336,7 +336,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
private teraLabel: Phaser.GameObjects.Text; private teraLabel: Phaser.GameObjects.Text;
private goFilterLabel: Phaser.GameObjects.Text; private goFilterLabel: Phaser.GameObjects.Text;
/** Group holding the UI elements appearing in the instructionsContainer */ /** Group holding the UI elements appearing in the instructionsContainer */
/* TODO: Uncomment this once our testing infra supports mocks of `Phaser.GameObject.Group` /* TODO: Uncomment this once our testing infra supports mocks of `Phaser.GameObject.Group`
private instructionElemGroup: Phaser.GameObjects.Group; private instructionElemGroup: Phaser.GameObjects.Group;
*/ */
@ -4271,7 +4271,7 @@ export class StarterSelectUiHandler extends MessageUiHandler {
globalScene.phaseManager.pushNew("EncounterPhase"); globalScene.phaseManager.pushNew("EncounterPhase");
} }
this.clearText(); this.clearText();
globalScene.phaseManager.getCurrentPhase()?.end(); globalScene.phaseManager.getCurrentPhase().end();
}, },
cancel, cancel,
null, null,

View File

@ -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);

View File

@ -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),

View File

@ -15,6 +15,8 @@ import type { AtLeastOne } from "#types/type-helpers";
import type { expect } from "vitest"; import type { expect } from "vitest";
import type Overrides from "#app/overrides"; import type Overrides from "#app/overrides";
import type { PokemonMove } from "#moves/pokemon-move"; import type { PokemonMove } from "#moves/pokemon-move";
import type { PhaseString } from "#types/phase-types";
import type { Phase } from "#app/phase";
declare module "vitest" { declare module "vitest" {
interface Assertion { interface Assertion {
@ -29,6 +31,12 @@ declare module "vitest" {
*/ */
toEqualArrayUnsorted<E>(expected: E[]): void; toEqualArrayUnsorted<E>(expected: E[]): void;
/**
* Check if the currently running {@linkcode Phase} is of the given type.
* @param expectedPhase - The expected {@linkcode PhaseString}
*/
toBeAtPhase(expectedPhase: PhaseString): void;
/** /**
* Check whether a {@linkcode Pokemon}'s current typing includes the given types. * Check whether a {@linkcode Pokemon}'s current typing includes the given types.
* *

View File

@ -196,7 +196,7 @@ describe("Abilities - Disguise", () => {
game.move.select(MoveId.SHADOW_SNEAK); game.move.select(MoveId.SHADOW_SNEAK);
await game.toNextWave(); await game.toNextWave();
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2); expect(game.scene.currentBattle.waveIndex).toBe(2);
}); });

View File

@ -86,7 +86,7 @@ describe("Phase - Battle Phase", () => {
it("newGame one-liner", async () => { it("newGame one-liner", async () => {
await game.classicMode.startBattle(); await game.classicMode.startBattle();
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("do attack wave 3 - single battle - regular - OHKO", async () => { it("do attack wave 3 - single battle - regular - OHKO", async () => {

View File

@ -36,62 +36,62 @@ describe("Test Battle Phase", () => {
game.override.battleStyle("single").startingWave(10); game.override.battleStyle("single").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 2vs2 boss", async () => { it("startBattle 2vs2 boss", async () => {
game.override.battleStyle("double").startingWave(10); game.override.battleStyle("double").startingWave(10);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 2vs2 trainer", async () => { it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5); game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 2vs1 trainer", async () => { it("startBattle 2vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5); game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 2vs1 rival", async () => { it("startBattle 2vs1 rival", async () => {
game.override.battleStyle("single").startingWave(8); game.override.battleStyle("single").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 2vs2 rival", async () => { it("startBattle 2vs2 rival", async () => {
game.override.battleStyle("double").startingWave(8); game.override.battleStyle("double").startingWave(8);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 1vs1 trainer", async () => { it("startBattle 1vs1 trainer", async () => {
game.override.battleStyle("single").startingWave(5); game.override.battleStyle("single").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE]); await game.classicMode.startBattle([SpeciesId.BLASTOISE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 2vs2 trainer", async () => { it("startBattle 2vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5); game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
it("startBattle 4vs2 trainer", async () => { it("startBattle 4vs2 trainer", async () => {
game.override.battleStyle("double").startingWave(5); game.override.battleStyle("double").startingWave(5);
await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]); await game.classicMode.startBattle([SpeciesId.BLASTOISE, SpeciesId.CHARIZARD, SpeciesId.DARKRAI, SpeciesId.GABITE]);
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND); expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
}); });
}); });

View File

@ -1,3 +1,4 @@
import { toBeAtPhase } from "#test/test-utils/matchers/to-be-at-phase";
import { toEqualArrayUnsorted } from "#test/test-utils/matchers/to-equal-array-unsorted"; import { toEqualArrayUnsorted } from "#test/test-utils/matchers/to-equal-array-unsorted";
import { toHaveAbilityApplied } from "#test/test-utils/matchers/to-have-ability-applied"; import { toHaveAbilityApplied } from "#test/test-utils/matchers/to-have-ability-applied";
import { toHaveBattlerTag } from "#test/test-utils/matchers/to-have-battler-tag"; import { toHaveBattlerTag } from "#test/test-utils/matchers/to-have-battler-tag";
@ -22,6 +23,7 @@ import { expect } from "vitest";
expect.extend({ expect.extend({
toEqualArrayUnsorted, toEqualArrayUnsorted,
toBeAtPhase,
toHaveTypes, toHaveTypes,
toHaveUsedMove, toHaveUsedMove,
toHaveEffectiveStat, toHaveEffectiveStat,

View File

@ -212,7 +212,7 @@ describe("Transforming Effects", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave(); await game.toNextWave();
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2); expect(game.scene.currentBattle.waveIndex).toBe(2);
await game.reload.reloadSession(); await game.reload.reloadSession();
@ -242,7 +242,7 @@ describe("Transforming Effects", () => {
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY]);
await game.toNextWave(); await game.toNextWave();
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase"); expect(game).toBeAtPhase("CommandPhase");
expect(game.scene.currentBattle.waveIndex).toBe(2); expect(game.scene.currentBattle.waveIndex).toBe(2);
expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId); expect(player.getSpeciesForm().speciesId).toBe(enemy.getSpeciesForm().speciesId);

View File

@ -9,7 +9,6 @@ import { ATrainersTestEncounter } from "#mystery-encounters/a-trainers-test-enco
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; 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 { CommandPhase } from "#phases/command-phase";
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 { import {
@ -106,7 +105,7 @@ describe("A Trainer's Test - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect( expect(
@ -131,7 +130,7 @@ describe("A Trainer's Test - 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(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined(); expect(eggsAfter).toBeDefined();
@ -179,7 +178,7 @@ describe("A Trainer's Test - 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(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
expect(eggsAfter).toBeDefined(); expect(eggsAfter).toBeDefined();

View File

@ -10,7 +10,6 @@ import { BerryModifier, PokemonHeldItemModifier } from "#modifiers/modifier";
import { AbsoluteAvariceEncounter } from "#mystery-encounters/absolute-avarice-encounter"; import { AbsoluteAvariceEncounter } from "#mystery-encounters/absolute-avarice-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase"; import { MovePhase } from "#phases/move-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -132,7 +131,7 @@ describe("Absolute Avarice - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.GREEDENT); expect(enemyField[0].species.speciesId).toBe(SpeciesId.GREEDENT);
const moveset = enemyField[0].moveset.map(m => m.moveId); const moveset = enemyField[0].moveset.map(m => m.moveId);
@ -148,7 +147,7 @@ describe("Absolute Avarice - 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(game).toBeAtPhase("SelectModifierPhase");
for (const partyPokemon of scene.getPlayerParty()) { for (const partyPokemon of scene.getPlayerParty()) {
const pokemonId = partyPokemon.id; const pokemonId = partyPokemon.id;

View File

@ -11,7 +11,6 @@ import { BerriesAboundEncounter } from "#mystery-encounters/berries-abound-encou
import * as EncounterDialogueUtils from "#mystery-encounters/encounter-dialogue-utils"; import * as EncounterDialogueUtils from "#mystery-encounters/encounter-dialogue-utils";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
runMysteryEncounterToEnd, runMysteryEncounterToEnd,
@ -114,7 +113,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
}); });
@ -135,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(game).toBeAtPhase("SelectModifierPhase");
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
const berriesAfterCount = berriesAfter.reduce((a, b) => a + b.stackCount, 0); const berriesAfterCount = berriesAfter.reduce((a, b) => a + b.stackCount, 0);
@ -148,7 +147,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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -188,7 +187,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -212,7 +211,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -233,7 +232,7 @@ 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -12,7 +12,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import { BugTypeSuperfanEncounter } from "#mystery-encounters/bug-type-superfan-encounter"; import { BugTypeSuperfanEncounter } from "#mystery-encounters/bug-type-superfan-encounter";
import * as encounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as encounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases"; import { MysteryEncounterPhase, MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -231,7 +230,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(2); expect(enemyParty.length).toBe(2);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -244,7 +243,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(3); expect(enemyParty.length).toBe(3);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -258,7 +257,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(4); expect(enemyParty.length).toBe(4);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -273,7 +272,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5); expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -289,7 +288,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5); expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -307,7 +306,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5); expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -325,7 +324,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5); expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -343,7 +342,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyParty = scene.getEnemyParty(); const enemyParty = scene.getEnemyParty();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyParty.length).toBe(5); expect(enemyParty.length).toBe(5);
expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN); expect(scene.currentBattle.trainer?.config.trainerType).toBe(TrainerType.BUG_TYPE_SUPERFAN);
expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL); expect(enemyParty[0].species.speciesId).toBe(SpeciesId.BEEDRILL);
@ -365,7 +364,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game, false); await skipBattleRunMysteryEncounterRewardsPhase(game, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name); expect(game).toBeAtPhase("MysteryEncounterRewardsPhase");
game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers
game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => { game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => {
game.phaseInterceptor.superEndPhase(); game.phaseInterceptor.superEndPhase();
@ -406,7 +405,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2); await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -416,7 +415,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty);
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -435,7 +434,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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -457,7 +456,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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -481,7 +480,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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -542,7 +541,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3); await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -557,7 +556,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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -22,7 +22,6 @@ import { ClowningAroundEncounter } from "#mystery-encounters/clowning-around-enc
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
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 { NewBattlePhase } from "#phases/new-battle-phase";
@ -172,7 +171,7 @@ describe("Clowning Around - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(2); expect(enemyField.length).toBe(2);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.MR_MIME); expect(enemyField[0].species.speciesId).toBe(SpeciesId.MR_MIME);
expect(enemyField[0].moveset).toEqual([ expect(enemyField[0].moveset).toEqual([
@ -201,7 +200,7 @@ describe("Clowning Around - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability; const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability;
@ -216,7 +215,7 @@ describe("Clowning Around - Mystery Encounter", () => {
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(game).toBeAtPhase("PostMysteryEncounterPhase");
// Wait for Yes/No confirmation to appear // Wait for Yes/No confirmation to appear
await vi.waitFor(() => expect(optionSelectUiHandler.show).toHaveBeenCalled()); await vi.waitFor(() => expect(optionSelectUiHandler.show).toHaveBeenCalled());

View File

@ -10,7 +10,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter"; import { DancingLessonsEncounter } from "#mystery-encounters/dancing-lessons-encounter";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { LearnMovePhase } from "#phases/learn-move-phase"; import { LearnMovePhase } from "#phases/learn-move-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";
@ -106,7 +105,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.ORICORIO); expect(enemyField[0].species.speciesId).toBe(SpeciesId.ORICORIO);
expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 0, 0, 0]); expect(enemyField[0].summonData.statStages).toEqual([1, 1, 1, 1, 0, 0, 0]);
@ -127,7 +126,7 @@ describe("Dancing Lessons - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -227,7 +226,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3); await runSelectMysteryEncounterOption(game, 3);
const partyCountAfter = scene.getPlayerParty().length; const partyCountAfter = scene.getPlayerParty().length;
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -161,7 +161,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1); await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -316,7 +316,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2); await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -449,7 +449,7 @@ describe("Delibird-y - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3); await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -93,7 +93,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only TMs", async () => { it("should have shop with only TMs", async () => {
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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -130,7 +130,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only Vitamins", async () => { it("should have shop with only Vitamins", async () => {
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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -170,7 +170,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only X Items", async () => { it("should have shop with only X Items", async () => {
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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -210,7 +210,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
it("should have shop with only Pokeballs", async () => { it("should have shop with only Pokeballs", async () => {
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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -16,7 +16,6 @@ import { AttackTypeBoosterModifier, PokemonHeldItemModifier } from "#modifiers/m
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { FieryFalloutEncounter } from "#mystery-encounters/fiery-fallout-encounter"; import { FieryFalloutEncounter } from "#mystery-encounters/fiery-fallout-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-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 { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
@ -161,7 +160,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(2); expect(enemyField.length).toBe(2);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.VOLCARONA); expect(enemyField[0].species.speciesId).toBe(SpeciesId.VOLCARONA);
expect(enemyField[1].species.speciesId).toBe(SpeciesId.VOLCARONA); expect(enemyField[1].species.speciesId).toBe(SpeciesId.VOLCARONA);
@ -177,7 +176,7 @@ describe("Fiery Fallout - 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(game).toBeAtPhase("SelectModifierPhase");
const leadPokemonId = scene.getPlayerParty()?.[0].id; const leadPokemonId = scene.getPlayerParty()?.[0].id;
const leadPokemonItems = scene.findModifiers( const leadPokemonItems = scene.findModifiers(
@ -266,7 +265,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FIERY_FALLOUT, defaultParty);
await runMysteryEncounterToEnd(game, 3); await runMysteryEncounterToEnd(game, 3);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to(SelectModifierPhase, false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(game).toBeAtPhase("SelectModifierPhase");
const leadPokemonItems = scene.getPlayerParty()[0].getHeldItems() as PokemonHeldItemModifier[]; const leadPokemonItems = scene.getPlayerParty()[0].getHeldItems() as PokemonHeldItemModifier[];
const item = leadPokemonItems.find(i => i instanceof AttackTypeBoosterModifier); const item = leadPokemonItems.find(i => i instanceof AttackTypeBoosterModifier);
@ -292,7 +291,7 @@ describe("Fiery Fallout - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3); await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(continueEncounterSpy).not.toHaveBeenCalled(); expect(continueEncounterSpy).not.toHaveBeenCalled();
}); });
}); });

View File

@ -10,7 +10,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter"; import { FightOrFlightEncounter } from "#mystery-encounters/fight-or-flight-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -110,7 +109,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
}); });
@ -123,7 +122,7 @@ 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -166,7 +165,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2); await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -183,7 +182,7 @@ describe("Fight or Flight - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -14,7 +14,7 @@ import { FunAndGamesEncounter } from "#mystery-encounters/fun-and-games-encounte
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; 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 { CommandPhase } from "#phases/command-phase"; import type { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -131,7 +131,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1); await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -143,7 +143,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.WOBBUFFET); expect(game.field.getEnemyPokemon().species.speciesId).toBe(SpeciesId.WOBBUFFET);
expect(game.field.getEnemyPokemon().ivs).toEqual([0, 0, 0, 0, 0, 0]); expect(game.field.getEnemyPokemon().ivs).toEqual([0, 0, 0, 0, 0, 0]);
expect(game.field.getEnemyPokemon().nature).toBe(Nature.MILD); expect(game.field.getEnemyPokemon().nature).toBe(Nature.MILD);
@ -165,7 +165,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(game).toBeAtPhase("SelectModifierPhase");
}); });
it("should have no items in rewards if Wubboffet doesn't take enough damage", async () => { it("should have no items in rewards if Wubboffet doesn't take enough damage", async () => {
@ -173,7 +173,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase(); game.endPhase();
}); });
@ -184,7 +184,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -200,7 +200,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase(); game.endPhase();
}); });
@ -213,7 +213,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -230,7 +230,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase(); game.endPhase();
}); });
@ -243,7 +243,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -260,7 +260,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true); await runMysteryEncounterToEnd(game, 1, { pokemonNo: 1 }, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => { game.onNextPrompt("MessagePhase", UiMode.MESSAGE, () => {
game.endPhase(); game.endPhase();
}); });
@ -273,7 +273,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to(SelectModifierPhase, false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -226,7 +226,7 @@ describe("Global Trade System - Mystery Encounter", () => {
await scene.updateModifiers(true); await scene.updateModifiers(true);
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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -147,7 +147,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1); await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -212,7 +212,7 @@ describe("Lost at Sea - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2); await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -12,7 +12,6 @@ import { MysteriousChallengersEncounter } from "#mystery-encounters/mysterious-c
import { MysteryEncounter } from "#mystery-encounters/mystery-encounter"; import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; 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 { CommandPhase } from "#phases/command-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
runMysteryEncounterToEnd, runMysteryEncounterToEnd,
@ -152,7 +151,7 @@ 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);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
}); });
@ -162,7 +161,7 @@ describe("Mysterious Challengers - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -196,7 +195,7 @@ 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);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
}); });
@ -206,7 +205,7 @@ describe("Mysterious Challengers - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -253,7 +252,7 @@ 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);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
}); });
@ -263,7 +262,7 @@ describe("Mysterious Challengers - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -245,7 +245,7 @@ describe("Part-Timer - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3); await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -119,7 +119,7 @@ describe("Safari Zone - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1); await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -8,7 +8,6 @@ import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TeleportingHijinksEncounter } from "#mystery-encounters/teleporting-hijinks-encounter"; import { TeleportingHijinksEncounter } from "#mystery-encounters/teleporting-hijinks-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -157,7 +156,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1); await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -167,7 +166,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
}); });
it("should transport to a new area", async () => { it("should transport to a new area", async () => {
@ -229,7 +228,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2); await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -239,7 +238,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.METAGROSS]); await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.METAGROSS]);
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
}); });
it("should transport to a new area", async () => { it("should transport to a new area", async () => {
@ -300,7 +299,7 @@ describe("Teleporting Hijinks - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -12,7 +12,6 @@ import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; 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 { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -158,7 +157,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true); expect(successfullyLoaded).toBe(true);
// Check usual battle stuff // Check usual battle stuff
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1); expect(scene.getPlayerParty().length).toBe(1);
@ -177,7 +176,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(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1CommonEggs; const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon1CommonEggs;
@ -243,7 +242,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true); expect(successfullyLoaded).toBe(true);
// Check usual battle stuff // Check usual battle stuff
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1); expect(scene.getPlayerParty().length).toBe(1);
@ -262,7 +261,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(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2CommonEggs; const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon2CommonEggs;
@ -325,7 +324,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(successfullyLoaded).toBe(true); expect(successfullyLoaded).toBe(true);
// Check usual battle stuff // Check usual battle stuff
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE); expect(scene.currentBattle.mysteryEncounter?.encounterMode).toBe(MysteryEncounterMode.TRAINER_BATTLE);
expect(scene.getPlayerParty().length).toBe(1); expect(scene.getPlayerParty().length).toBe(1);
@ -344,7 +343,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(game).toBeAtPhase("SelectModifierPhase");
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3CommonEggs; const commonEggs = scene.currentBattle.mysteryEncounter!.misc.pokemon3CommonEggs;

View File

@ -182,7 +182,7 @@ describe("The Pokemon Salesman - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 1); await runSelectMysteryEncounterOption(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -17,7 +17,6 @@ import { PokemonMove } from "#moves/pokemon-move";
import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"; import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TheStrongStuffEncounter } from "#mystery-encounters/the-strong-stuff-encounter"; import { TheStrongStuffEncounter } from "#mystery-encounters/the-strong-stuff-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase"; import { MovePhase } from "#phases/move-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -192,7 +191,7 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.SHUCKLE); expect(enemyField[0].species.speciesId).toBe(SpeciesId.SHUCKLE);
expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 0, 0, 0]); expect(enemyField[0].summonData.statStages).toEqual([0, 1, 0, 1, 0, 0, 0]);
@ -230,7 +229,7 @@ describe("The Strong Stuff - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -15,7 +15,6 @@ import { MysteryEncounter } from "#mystery-encounters/mystery-encounter";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; 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 { MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases"; 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";
@ -263,7 +262,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, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(scene.currentBattle.trainer).toBeDefined(); expect(scene.currentBattle.trainer).toBeDefined();
expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICTOR); expect(scene.currentBattle.trainer!.config.trainerType).toBe(TrainerType.VICTOR);
expect(scene.currentBattle.mysteryEncounter?.enemyPartyConfigs.length).toBe(4); expect(scene.currentBattle.mysteryEncounter?.enemyPartyConfigs.length).toBe(4);
@ -296,7 +295,7 @@ 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -338,7 +337,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
it("should have a Rarer Candy in the rewards", async () => { it("should have a Rarer Candy in the rewards", async () => {
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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -20,7 +20,6 @@ import {
} from "#mystery-encounters/encounter-phase-utils"; } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { TrashToTreasureEncounter } from "#mystery-encounters/trash-to-treasure-encounter"; import { TrashToTreasureEncounter } from "#mystery-encounters/trash-to-treasure-encounter";
import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase"; import { MovePhase } from "#phases/move-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
@ -173,7 +172,7 @@ 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, 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(game).toBeAtPhase("SelectModifierPhase");
const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier; const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier;
expect(leftovers).toBeDefined(); expect(leftovers).toBeDefined();
@ -221,7 +220,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(SpeciesId.GARBODOR); expect(enemyField[0].species.speciesId).toBe(SpeciesId.GARBODOR);
expect(enemyField[0].moveset).toEqual([ expect(enemyField[0].moveset).toEqual([
@ -243,7 +242,7 @@ describe("Trash to Treasure - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -16,7 +16,6 @@ import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"
import { generateModifierType } from "#mystery-encounters/encounter-phase-utils"; import { generateModifierType } from "#mystery-encounters/encounter-phase-utils";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { UncommonBreedEncounter } from "#mystery-encounters/uncommon-breed-encounter"; import { UncommonBreedEncounter } from "#mystery-encounters/uncommon-breed-encounter";
import { CommandPhase } from "#phases/command-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 { StatStageChangePhase } from "#phases/stat-stage-change-phase"; import { StatStageChangePhase } from "#phases/stat-stage-change-phase";
@ -121,7 +120,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -148,7 +147,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(enemyField[0].species.speciesId).toBe(speciesToSpawn); expect(enemyField[0].species.speciesId).toBe(speciesToSpawn);
@ -200,7 +199,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 2); await runSelectMysteryEncounterOption(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();
@ -260,7 +259,7 @@ describe("Uncommon Breed - Mystery Encounter", () => {
await runSelectMysteryEncounterOption(game, 3); await runSelectMysteryEncounterOption(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled expect(scene.ui.playError).not.toHaveBeenCalled(); // No error sfx, option is disabled
expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.handleOptionSelect).not.toHaveBeenCalled();
expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled(); expect(mysteryEncounterPhase.continueEncounter).not.toHaveBeenCalled();

View File

@ -10,7 +10,6 @@ import * as EncounterPhaseUtils from "#mystery-encounters/encounter-phase-utils"
import * as EncounterTransformationSequence from "#mystery-encounters/encounter-transformation-sequence"; import * as EncounterTransformationSequence from "#mystery-encounters/encounter-transformation-sequence";
import * as MysteryEncounters from "#mystery-encounters/mystery-encounters"; import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { WeirdDreamEncounter } from "#mystery-encounters/weird-dream-encounter"; import { WeirdDreamEncounter } from "#mystery-encounters/weird-dream-encounter";
import { CommandPhase } from "#phases/command-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
runMysteryEncounterToEnd, runMysteryEncounterToEnd,
@ -112,12 +111,12 @@ 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(game).toBeAtPhase("SelectModifierPhase");
const pokemonAfter = scene.getPlayerParty(); const pokemonAfter = scene.getPlayerParty();
const bstsAfter = pokemonAfter.map(pokemon => pokemon.getSpeciesForm().getBaseStatTotal()); const bstsAfter = pokemonAfter.map(pokemon => pokemon.getSpeciesForm().getBaseStatTotal());
@ -140,7 +139,7 @@ describe("Weird Dream - Mystery Encounter", () => {
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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
@ -187,7 +186,7 @@ describe("Weird Dream - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
const enemyField = scene.getEnemyField(); const enemyField = scene.getEnemyField();
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(CommandPhase.name); expect(game).toBeAtPhase("CommandPhase");
expect(enemyField.length).toBe(1); expect(enemyField.length).toBe(1);
expect(scene.getEnemyParty().length).toBe(scene.getPlayerParty().length); expect(scene.getEnemyParty().length).toBe(scene.getPlayerParty().length);
}); });
@ -197,7 +196,7 @@ describe("Weird Dream - 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(game).toBeAtPhase("SelectModifierPhase");
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);

View File

@ -34,7 +34,7 @@ describe("Mystery Encounters", () => {
]); ]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to(MysteryEncounterPhase, false);
expect(game.scene.phaseManager.getCurrentPhase()!.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
}); });
it("Encounters should not run on X1 waves", async () => { it("Encounters should not run on X1 waves", async () => {

View File

@ -38,7 +38,7 @@ describe("Mystery Encounter Phases", () => {
]); ]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to(MysteryEncounterPhase, false);
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game).toBeAtPhase("MysteryEncounterPhase");
}); });
it("Runs MysteryEncounterPhase", async () => { it("Runs MysteryEncounterPhase", async () => {

View 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();
});
});
});

View File

@ -46,6 +46,7 @@ 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 { PhaseClass, 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";
@ -162,7 +163,7 @@ export class GameManager {
* End the currently running phase immediately. * End the currently running phase immediately.
*/ */
endPhase() { endPhase() {
this.scene.phaseManager.getCurrentPhase()?.end(); this.scene.phaseManager.getCurrentPhase().end();
} }
/** /**
@ -412,10 +413,11 @@ export class GameManager {
* Checks if the current phase matches the target phase. * Checks if the current phase matches the target phase.
* @param phaseTarget - The target phase. * @param phaseTarget - The target phase.
* @returns Whether the current phase matches the target phase * @returns Whether the current phase matches the target phase
* @todo Remove `phaseClass` from signature
*/ */
isCurrentPhase(phaseTarget) { isCurrentPhase(phaseTarget: PhaseClass | PhaseString) {
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
return this.scene.phaseManager.getCurrentPhase()?.constructor.name === targetName; return this.scene.phaseManager.getCurrentPhase().phaseName === targetName;
} }
/** /**

View File

@ -0,0 +1,45 @@
/** biome-ignore-start lint/correctness/noUnusedImports: TSDoc imports */
import type { Phase } from "#app/phase";
import type { GameManager } from "#test/test-utils/game-manager";
// biome-ignore-end lint/correctness/noUnusedImports: TSDoc
import { isGameManagerInstance, receivedStr } from "#test/test-utils/test-utils";
import type { PhaseString } from "#types/phase-types";
import type { MatcherState, SyncExpectationResult } from "@vitest/expect";
/**
* Matcher that checks if the current {@linkcode Phase} is of the given type.
* @param received - The object to check. Should be the current {@linkcode GameManager}
* @param expectedPhase - The expected {@linkcode PhaseString}
* @returns The result of the matching
*/
export function toBeAtPhase(this: MatcherState, received: unknown, expectedPhase: PhaseString): SyncExpectationResult {
if (!isGameManagerInstance(received)) {
return {
pass: this.isNot,
message: () => `Expected to receive a GameManager, but got ${receivedStr(received)}!`,
};
}
if (!received.scene?.phaseManager) {
return {
pass: this.isNot,
message: () => `Expected GameManager.${received.scene ? "scene.phaseManager" : "scene"} to be defined!`,
};
}
const currPhase = received.scene.phaseManager.getCurrentPhase();
const pass = currPhase.is(expectedPhase);
const actual = currPhase.phaseName;
return {
pass,
message: () =>
pass
? `Expected the current phase to NOT be ${expectedPhase}, but it was!`
: `Expected the current phase to be ${expectedPhase}, but got ${actual} instead!`,
expected: expectedPhase,
actual,
};
}

View File

@ -416,7 +416,7 @@ export class PhaseInterceptor {
const actionForNextPrompt = this.prompts[0]; const actionForNextPrompt = this.prompts[0];
const expireFn = actionForNextPrompt.expireFn?.(); const expireFn = actionForNextPrompt.expireFn?.();
const currentMode = this.scene.ui.getMode(); const currentMode = this.scene.ui.getMode();
const currentPhase = this.scene.phaseManager.getCurrentPhase()?.constructor.name; const currentPhase = this.scene.phaseManager.getCurrentPhase().phaseName;
const currentHandler = this.scene.ui.getHandler(); const currentHandler = this.scene.ui.getHandler();
if (expireFn) { if (expireFn) {
this.prompts.shift(); this.prompts.shift();