Compare commits

...

18 Commits

Author SHA1 Message Date
Bertie690
5707a0c4a1
Merge b4dd435d3e into 076ef81691 2025-08-14 00:28:25 -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
NightKev
b4dd435d3e
Re-add ! and add TODO comment instead 2025-08-11 05:57:07 -07:00
Bertie690
817654341a
Update phase-interceptor.ts
Co-authored-by: NightKev <34855794+DayKev@users.noreply.github.com>
2025-08-11 08:47:55 -04:00
NightKev
d969d9f67b
Merge branch 'beta' into deprecated-func 2025-08-11 05:36:37 -07:00
Wlowscha
a9040f0048
Merge branch 'beta' into deprecated-func 2025-08-02 22:07:59 +02:00
Bertie690
5d7a3d28ff dddddd 2025-08-01 21:39:09 -04:00
Bertie690
040eaf8632 fixed another dumb error bc me big dumb bozo 2025-08-01 21:30:35 -04:00
Bertie690
c318a0cc59 Fixed tests 2025-08-01 21:25:12 -04:00
Bertie690
20582b43c0 maybe fixed? 2025-08-01 21:18:05 -04:00
Bertie690
c737a9206f Perhaps fixed things? 2025-08-01 20:52:05 -04:00
Bertie690
152f54ee7a somehow fixed reload bug by making things actively worse 2025-08-01 20:42:25 -04:00
Bertie690
1819712201 Fixed issues and syntax errors 2025-08-01 20:10:57 -04:00
Bertie690
9a19eac9ee Added selectStarterPhase to the end by set mode collection 2025-08-01 19:03:11 -04:00
Bertie690
bca9560f55 Added minor docs to the phase manager + renamed shift to shiftPhase 2025-08-01 18:57:31 -04:00
Bertie690
a1a3526c17 Removed deprecated functions from phase interceptor 2025-08-01 18:38:05 -04:00
44 changed files with 752 additions and 599 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

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

@ -2,8 +2,10 @@ import { globalScene } from "#app/global-scene";
import type { PhaseMap, PhaseString } from "#types/phase-types"; import type { PhaseMap, PhaseString } from "#types/phase-types";
export abstract class Phase { export abstract class Phase {
/** Start the current phase. */
start() {} start() {}
/** End the current phase and start a new one. */
end() { end() {
globalScene.phaseManager.shiftPhase(); globalScene.phaseManager.shiftPhase();
} }

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,
@ -979,6 +980,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

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

@ -207,6 +207,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);
} }
/** /**

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

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

@ -3,9 +3,6 @@ import { BattlerIndex } from "#enums/battler-index";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
import { TurnEndPhase } from "#phases/turn-end-phase";
import { VictoryPhase } from "#phases/victory-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -46,7 +43,7 @@ describe("Abilities - Moxie", () => {
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(0);
game.move.select(moveToUse); game.move.select(moveToUse);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(VictoryPhase); await game.phaseInterceptor.to("VictoryPhase");
expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1); expect(playerPokemon.getStatStage(Stat.ATK)).toBe(1);
}); });
@ -67,7 +64,7 @@ describe("Abilities - Moxie", () => {
game.move.select(moveToUse, BattlerIndex.PLAYER_2); game.move.select(moveToUse, BattlerIndex.PLAYER_2);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
expect(firstPokemon.getStatStage(Stat.ATK)).toBe(1); expect(firstPokemon.getStatStage(Stat.ATK)).toBe(1);
}, },

View File

@ -1,9 +1,7 @@
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { EnemyCommandPhase } from "#phases/enemy-command-phase"; import type { TurnStartPhase } from "#phases/turn-start-phase";
import { SelectTargetPhase } from "#phases/select-target-phase";
import { TurnStartPhase } from "#phases/turn-start-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -41,7 +39,7 @@ describe("Battle order", () => {
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150 vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 150]); // set enemyPokemon's speed to 150
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.run(EnemyCommandPhase); await game.phaseInterceptor.to("TurnStartPhase", false);
const playerPokemonIndex = playerPokemon.getBattlerIndex(); const playerPokemonIndex = playerPokemon.getBattlerIndex();
const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
@ -60,7 +58,7 @@ describe("Battle order", () => {
vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50 vi.spyOn(enemyPokemon, "stats", "get").mockReturnValue([20, 20, 20, 20, 20, 50]); // set enemyPokemon's speed to 50
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.run(EnemyCommandPhase); await game.phaseInterceptor.to("TurnStartPhase", false);
const playerPokemonIndex = playerPokemon.getBattlerIndex(); const playerPokemonIndex = playerPokemon.getBattlerIndex();
const enemyPokemonIndex = enemyPokemon.getBattlerIndex(); const enemyPokemonIndex = enemyPokemon.getBattlerIndex();
@ -84,7 +82,7 @@ describe("Battle order", () => {
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
game.move.select(MoveId.TACKLE, 1); game.move.select(MoveId.TACKLE, 1);
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); await game.phaseInterceptor.to("TurnStartPhase", false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder(); const order = phase.getCommandOrder();
@ -108,7 +106,7 @@ describe("Battle order", () => {
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
game.move.select(MoveId.TACKLE, 1); game.move.select(MoveId.TACKLE, 1);
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); await game.phaseInterceptor.to("TurnStartPhase", false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder(); const order = phase.getCommandOrder();
@ -132,7 +130,7 @@ describe("Battle order", () => {
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
game.move.select(MoveId.TACKLE, 1); game.move.select(MoveId.TACKLE, 1);
await game.phaseInterceptor.runFrom(SelectTargetPhase).to(TurnStartPhase, false); await game.phaseInterceptor.to("TurnStartPhase", false);
const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase; const phase = game.scene.phaseManager.getCurrentPhase() as TurnStartPhase;
const order = phase.getCommandOrder(); const order = phase.getCommandOrder();

View File

@ -1,28 +1,13 @@
import { getGameMode } from "#app/game-mode";
import { allSpecies } from "#data/data-lists"; import { allSpecies } from "#data/data-lists";
import { AbilityId } from "#enums/ability-id"; import { AbilityId } from "#enums/ability-id";
import { BiomeId } from "#enums/biome-id"; import { BiomeId } from "#enums/biome-id";
import { GameModes } from "#enums/game-modes";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { PlayerGender } from "#enums/player-gender";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { BattleEndPhase } from "#phases/battle-end-phase";
import { CommandPhase } from "#phases/command-phase"; import { CommandPhase } from "#phases/command-phase";
import { DamageAnimPhase } from "#phases/damage-anim-phase";
import { EncounterPhase } from "#phases/encounter-phase";
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
import { LoginPhase } from "#phases/login-phase";
import { NextEncounterPhase } from "#phases/next-encounter-phase"; import { NextEncounterPhase } from "#phases/next-encounter-phase";
import { SelectGenderPhase } from "#phases/select-gender-phase";
import { SelectStarterPhase } from "#phases/select-starter-phase";
import { SummonPhase } from "#phases/summon-phase";
import { SwitchPhase } from "#phases/switch-phase";
import { TitlePhase } from "#phases/title-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import { generateStarter } from "#test/test-utils/game-manager-utils";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -45,55 +30,11 @@ describe("Phase - Battle Phase", () => {
game.scene.gameData.gender = undefined!; // just for these tests! game.scene.gameData.gender = undefined!; // just for these tests!
}); });
it("test phase interceptor with prompt", async () => {
await game.phaseInterceptor.run(LoginPhase);
game.onNextPrompt("SelectGenderPhase", UiMode.OPTION_SELECT, () => {
game.scene.gameData.gender = PlayerGender.MALE;
game.endPhase();
});
await game.phaseInterceptor.run(SelectGenderPhase);
await game.phaseInterceptor.run(TitlePhase);
await game.waitMode(UiMode.TITLE);
expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE);
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
});
it("test phase interceptor with prompt with preparation for a future prompt", async () => {
await game.phaseInterceptor.run(LoginPhase);
game.onNextPrompt("SelectGenderPhase", UiMode.OPTION_SELECT, () => {
game.scene.gameData.gender = PlayerGender.MALE;
game.endPhase();
});
game.onNextPrompt("CheckSwitchPhase", UiMode.CONFIRM, () => {
game.setMode(UiMode.MESSAGE);
game.endPhase();
});
await game.phaseInterceptor.run(SelectGenderPhase);
await game.phaseInterceptor.run(TitlePhase);
await game.waitMode(UiMode.TITLE);
expect(game.scene.ui?.getMode()).toBe(UiMode.TITLE);
expect(game.scene.gameData.gender).toBe(PlayerGender.MALE);
});
it("newGame one-liner", async () => {
await game.classicMode.startBattle();
expect(game.scene.ui?.getMode()).toBe(UiMode.COMMAND);
expect(game.scene.phaseManager.getCurrentPhase()?.phaseName).toBe("CommandPhase");
});
it("do attack wave 3 - single battle - regular - OHKO", async () => { it("do attack wave 3 - single battle - regular - OHKO", async () => {
game.override.enemySpecies(SpeciesId.RATTATA).startingLevel(2000).battleStyle("single").startingWave(3); game.override.enemySpecies(SpeciesId.RATTATA).startingLevel(2000).battleStyle("single").startingWave(3);
await game.classicMode.startBattle([SpeciesId.MEWTWO]); await game.classicMode.startBattle([SpeciesId.MEWTWO]);
game.move.use(MoveId.TACKLE); game.move.use(MoveId.TACKLE);
await game.phaseInterceptor.to("SelectModifierPhase"); await game.toNextWave();
}); });
it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async () => { it("do attack wave 3 - single battle - regular - NO OHKO with opponent using non damage attack", async () => {
@ -107,7 +48,7 @@ describe("Phase - Battle Phase", () => {
.battleStyle("single"); .battleStyle("single");
await game.classicMode.startBattle([SpeciesId.MEWTWO]); await game.classicMode.startBattle([SpeciesId.MEWTWO]);
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase, false); await game.phaseInterceptor.to("TurnInitPhase", false);
}); });
it("load 100% data file", async () => { it("load 100% data file", async () => {
@ -135,68 +76,6 @@ describe("Phase - Battle Phase", () => {
} }
}); });
it("wrong phase", async () => {
await game.phaseInterceptor.run(LoginPhase);
await game.phaseInterceptor.run(LoginPhase).catch(e => {
expect(e).toBe("Wrong phase: this is SelectGenderPhase and not LoginPhase");
});
});
it("wrong phase but skip", async () => {
await game.phaseInterceptor.run(LoginPhase);
await game.phaseInterceptor.run(LoginPhase, () => game.isCurrentPhase(SelectGenderPhase));
});
it("good run", async () => {
await game.phaseInterceptor.run(LoginPhase);
game.onNextPrompt(
"SelectGenderPhase",
UiMode.OPTION_SELECT,
() => {
game.scene.gameData.gender = PlayerGender.MALE;
game.endPhase();
},
() => game.isCurrentPhase(TitlePhase),
);
await game.phaseInterceptor.run(SelectGenderPhase, () => game.isCurrentPhase(TitlePhase));
await game.phaseInterceptor.run(TitlePhase);
});
it("good run from select gender to title", async () => {
await game.phaseInterceptor.run(LoginPhase);
game.onNextPrompt(
"SelectGenderPhase",
UiMode.OPTION_SELECT,
() => {
game.scene.gameData.gender = PlayerGender.MALE;
game.endPhase();
},
() => game.isCurrentPhase(TitlePhase),
);
await game.phaseInterceptor.runFrom(SelectGenderPhase).to(TitlePhase);
});
it("good run to SummonPhase phase", async () => {
await game.phaseInterceptor.run(LoginPhase);
game.onNextPrompt(
"SelectGenderPhase",
UiMode.OPTION_SELECT,
() => {
game.scene.gameData.gender = PlayerGender.MALE;
game.endPhase();
},
() => game.isCurrentPhase(TitlePhase),
);
game.onNextPrompt("TitlePhase", UiMode.TITLE, () => {
game.scene.gameMode = getGameMode(GameModes.CLASSIC);
const starters = generateStarter(game.scene);
const selectStarterPhase = new SelectStarterPhase();
game.scene.phaseManager.pushPhase(new EncounterPhase(false));
selectStarterPhase.initBattle(starters);
});
await game.phaseInterceptor.runFrom(SelectGenderPhase).to(SummonPhase);
});
it.each([ it.each([
{ name: "1v1", double: false, qty: 1 }, { name: "1v1", double: false, qty: 1 },
{ name: "2v1", double: false, qty: 2 }, { name: "2v1", double: false, qty: 2 },
@ -232,7 +111,7 @@ describe("Phase - Battle Phase", () => {
await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]); await game.classicMode.startBattle([SpeciesId.DARMANITAN, SpeciesId.CHARIZARD]);
game.move.select(moveToUse); game.move.select(moveToUse);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
await game.killPokemon(game.scene.currentBattle.enemyParty[0]); await game.killPokemon(game.scene.currentBattle.enemyParty[0]);
expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true); expect(game.scene.currentBattle.enemyParty[0].isFainted()).toBe(true);
await game.phaseInterceptor.to("VictoryPhase"); await game.phaseInterceptor.to("VictoryPhase");
@ -296,7 +175,7 @@ describe("Phase - Battle Phase", () => {
game.field.getPlayerPokemon().hp = 1; game.field.getPlayerPokemon().hp = 1;
game.move.select(moveToUse); game.move.select(moveToUse);
await game.phaseInterceptor.to(BattleEndPhase); await game.phaseInterceptor.to("BattleEndPhase");
game.doRevivePokemon(0); // pretend max revive was picked game.doRevivePokemon(0); // pretend max revive was picked
game.doSelectModifier(); game.doSelectModifier();
@ -308,6 +187,6 @@ describe("Phase - Battle Phase", () => {
}, },
() => game.isCurrentPhase(NextEncounterPhase), () => game.isCurrentPhase(NextEncounterPhase),
); );
await game.phaseInterceptor.to(SwitchPhase); await game.phaseInterceptor.to("SwitchPhase");
}); });
}); });

View File

@ -6,7 +6,6 @@ import { SpeciesId } from "#enums/species-id";
import { BATTLE_STATS, Stat } from "#enums/stat"; import { BATTLE_STATS, Stat } from "#enums/stat";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { TempStatStageBoosterModifier } from "#modifiers/modifier"; import { TempStatStageBoosterModifier } from "#modifiers/modifier";
import { TurnEndPhase } from "#phases/turn-end-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler"; import type { ModifierSelectUiHandler } from "#ui/modifier-select-ui-handler";
import Phaser from "phaser"; import Phaser from "phaser";
@ -47,7 +46,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.runFrom("EnemyCommandPhase").to(TurnEndPhase); await game.toEndOfTurn();
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3); expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.3);
}); });
@ -64,11 +63,11 @@ describe("Items - Temporary Stat Stage Boosters", () => {
// Raise ACC by +2 stat stages // Raise ACC by +2 stat stages
game.move.select(MoveId.HONE_CLAWS); game.move.select(MoveId.HONE_CLAWS);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
// ACC at +3 stat stages yields a x2 multiplier // ACC at +3 stat stages yields a x2 multiplier
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2); expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(2);
@ -84,11 +83,11 @@ describe("Items - Temporary Stat Stage Boosters", () => {
// Raise ATK by +1 stat stage // Raise ATK by +1 stat stage
game.move.select(MoveId.HONE_CLAWS); game.move.select(MoveId.HONE_CLAWS);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
// ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK // ATK at +1 stat stage yields a x1.5 multiplier, add 0.3 from X_ATTACK
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8); expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(1.8);
@ -112,7 +111,7 @@ describe("Items - Temporary Stat Stage Boosters", () => {
game.move.select(MoveId.TACKLE); game.move.select(MoveId.TACKLE);
await game.phaseInterceptor.to(TurnEndPhase); await game.phaseInterceptor.to("TurnEndPhase");
expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3); expect(partyMember.getAccuracyMultiplier).toHaveReturnedWith(3);
expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4); expect(partyMember.getStatStageMultiplier).toHaveReturnedWith(4);

View File

@ -4,10 +4,7 @@ import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import type { Move } from "#moves/move"; import type { Move } from "#moves/move";
import { DamageAnimPhase } from "#phases/damage-anim-phase"; import type { MoveEffectPhase } from "#phases/move-effect-phase";
import { MoveEffectPhase } from "#phases/move-effect-phase";
import { MoveEndPhase } from "#phases/move-end-phase";
import { MovePhase } from "#phases/move-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
@ -55,14 +52,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
// Force user party to act before enemy party // Force user party to act before enemy party
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
}); });
@ -75,14 +72,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
// Force user party to act before enemy party // Force user party to act before enemy party
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}); });
@ -95,19 +92,19 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
// Force first enemy to act (and fail) in between party // Force first enemy to act (and fail) in between party
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
await game.phaseInterceptor.to(MoveEndPhase); await game.phaseInterceptor.to("MoveEndPhase");
// Skip enemy move; because the enemy is at full HP, Rest should fail // Skip enemy move; because the enemy is at full HP, Rest should fail
await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase); await game.phaseInterceptor.to("MoveEndPhase");
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
}); });
@ -121,18 +118,18 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
// Force first enemy to act in between party // Force first enemy to act in between party
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(100);
await game.phaseInterceptor.to(MoveEndPhase); await game.phaseInterceptor.to("MoveEndPhase");
// Skip enemy move // Skip enemy move
await game.phaseInterceptor.runFrom(MovePhase).to(MoveEndPhase); await game.phaseInterceptor.to("MoveEndPhase");
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
}); });
@ -145,14 +142,14 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
// Force user party to act before enemy party // Force user party to act before enemy party
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY, BattlerIndex.ENEMY_2]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}); });
@ -189,24 +186,24 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
// Force first enemy to act in between party // Force first enemy to act in between party
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}); });
@ -243,24 +240,24 @@ describe("Moves - Fusion Flare and Fusion Bolt", () => {
// Force first enemy to act in between party // Force first enemy to act in between party
await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]); await game.setTurnOrder([BattlerIndex.PLAYER, BattlerIndex.ENEMY_2, BattlerIndex.PLAYER_2, BattlerIndex.ENEMY]);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(100);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionBolt.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionBolt.calculateBattlePower).toHaveLastReturnedWith(200);
await game.phaseInterceptor.to(MoveEffectPhase, false); await game.phaseInterceptor.to("MoveEffectPhase", false);
expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id); expect((game.scene.phaseManager.getCurrentPhase() as MoveEffectPhase).move.id).toBe(fusionFlare.id);
await game.phaseInterceptor.to(DamageAnimPhase, false); await game.phaseInterceptor.to("DamageAnimPhase", false);
expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200); expect(fusionFlare.calculateBattlePower).toHaveLastReturnedWith(200);
}); });
}); });

View File

@ -2,8 +2,6 @@ import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -40,7 +38,7 @@ describe("Moves - Growth", () => {
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(0);
game.move.select(MoveId.GROWTH); game.move.select(MoveId.GROWTH);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); await game.toEndOfTurn();
expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1); expect(playerPokemon.getStatStage(Stat.SPATK)).toBe(1);
}); });

View File

@ -2,10 +2,6 @@ import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { BerryPhase } from "#phases/berry-phase";
import { FaintPhase } from "#phases/faint-phase";
import { MessagePhase } from "#phases/message-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it, test } from "vitest";
@ -43,7 +39,7 @@ describe("Moves - Parting Shot", () => {
game.move.select(MoveId.PARTING_SHOT); game.move.select(MoveId.PARTING_SHOT);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
@ -58,7 +54,7 @@ describe("Moves - Parting Shot", () => {
game.move.select(MoveId.PARTING_SHOT); game.move.select(MoveId.PARTING_SHOT);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
@ -79,24 +75,24 @@ describe("Moves - Parting Shot", () => {
// use Memento 3 times to debuff enemy // use Memento 3 times to debuff enemy
game.move.select(MoveId.MEMENTO); game.move.select(MoveId.MEMENTO);
await game.phaseInterceptor.to(FaintPhase); await game.phaseInterceptor.to("FaintPhase");
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true); expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
game.doSelectPartyPokemon(1); game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to(TurnInitPhase, false); await game.phaseInterceptor.to("TurnInitPhase", false);
game.move.select(MoveId.MEMENTO); game.move.select(MoveId.MEMENTO);
await game.phaseInterceptor.to(FaintPhase); await game.phaseInterceptor.to("FaintPhase");
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true); expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
game.doSelectPartyPokemon(2); game.doSelectPartyPokemon(2);
await game.phaseInterceptor.to(TurnInitPhase, false); await game.phaseInterceptor.to("TurnInitPhase", false);
game.move.select(MoveId.MEMENTO); game.move.select(MoveId.MEMENTO);
await game.phaseInterceptor.to(FaintPhase); await game.phaseInterceptor.to("FaintPhase");
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true); expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
game.doSelectPartyPokemon(3); game.doSelectPartyPokemon(3);
// set up done // set up done
await game.phaseInterceptor.to(TurnInitPhase, false); await game.phaseInterceptor.to("TurnInitPhase", false);
const enemyPokemon = game.field.getEnemyPokemon(); const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon).toBeDefined(); expect(enemyPokemon).toBeDefined();
@ -106,7 +102,7 @@ describe("Moves - Parting Shot", () => {
// now parting shot should fail // now parting shot should fail
game.move.select(MoveId.PARTING_SHOT); game.move.select(MoveId.PARTING_SHOT);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-6);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-6);
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
@ -125,7 +121,7 @@ describe("Moves - Parting Shot", () => {
game.move.select(MoveId.PARTING_SHOT); game.move.select(MoveId.PARTING_SHOT);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
@ -144,7 +140,7 @@ describe("Moves - Parting Shot", () => {
game.move.select(MoveId.PARTING_SHOT); game.move.select(MoveId.PARTING_SHOT);
await game.phaseInterceptor.to(BerryPhase, false); await game.phaseInterceptor.to("BerryPhase", false);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW); expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
@ -153,43 +149,24 @@ describe("Moves - Parting Shot", () => {
it.todo( it.todo(
// TODO: fix this bug to pass the test! // TODO: fix this bug to pass the test!
"Parting shot should de-buff and not fail if no party available to switch - party size 1", "should lower stats without failing if no alive party members available to switch",
async () => {
await game.classicMode.startBattle([SpeciesId.MURKROW]);
const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon).toBeDefined();
game.move.select(MoveId.PARTING_SHOT);
await game.phaseInterceptor.to(BerryPhase, false);
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(-1);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(-1);
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MURKROW);
},
);
it.todo(
// TODO: fix this bug to pass the test!
"Parting shot regularly not fail if no party available to switch - party fainted",
async () => { async () => {
await game.classicMode.startBattle([SpeciesId.MURKROW, SpeciesId.MEOWTH]); await game.classicMode.startBattle([SpeciesId.MURKROW, SpeciesId.MEOWTH]);
const meowth = game.scene.getPlayerParty()[1];
meowth.hp = 0;
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
await game.toNextTurn();
// intentionally kill party pokemon, switch to second slot (now 1 party mon is fainted)
await game.killPokemon(game.scene.getPlayerParty()[0]);
expect(game.scene.getPlayerParty()[0].isFainted()).toBe(true);
await game.phaseInterceptor.run(MessagePhase);
game.doSelectPartyPokemon(1);
await game.phaseInterceptor.to(TurnInitPhase, false);
game.move.select(MoveId.PARTING_SHOT); game.move.select(MoveId.PARTING_SHOT);
game.doSelectPartyPokemon(1);
await game.toEndOfTurn();
await game.phaseInterceptor.to(BerryPhase, false);
const enemyPokemon = game.field.getEnemyPokemon(); const enemyPokemon = game.field.getEnemyPokemon();
expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.ATK)).toBe(0);
expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0); expect(enemyPokemon.getStatStage(Stat.SPATK)).toBe(0);
expect(game.scene.getPlayerField()[0].species.speciesId).toBe(SpeciesId.MEOWTH); expect(game.field.getPlayerPokemon().species.speciesId).toBe(SpeciesId.MURKROW);
}, },
); );
}); });

View File

@ -1,8 +1,6 @@
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
import { TurnEndPhase } from "#phases/turn-end-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -41,7 +39,7 @@ describe("Moves - Tackle", () => {
await game.classicMode.startBattle([SpeciesId.MIGHTYENA]); await game.classicMode.startBattle([SpeciesId.MIGHTYENA]);
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp; const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
game.move.select(moveToUse); game.move.select(moveToUse);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); await game.toEndOfTurn();
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp; const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
expect(hpLost).toBe(0); expect(hpLost).toBe(0);
}); });
@ -55,7 +53,7 @@ describe("Moves - Tackle", () => {
const hpOpponent = game.scene.currentBattle.enemyParty[0].hp; const hpOpponent = game.scene.currentBattle.enemyParty[0].hp;
game.move.select(moveToUse); game.move.select(moveToUse);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnEndPhase); await game.toEndOfTurn();
const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp; const hpLost = hpOpponent - game.scene.currentBattle.enemyParty[0].hp;
expect(hpLost).toBeGreaterThan(0); expect(hpLost).toBeGreaterThan(0);
expect(hpLost).toBeLessThan(4); expect(hpLost).toBeLessThan(4);

View File

@ -2,8 +2,6 @@ import { AbilityId } from "#enums/ability-id";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { Stat } from "#enums/stat"; import { Stat } from "#enums/stat";
import { EnemyCommandPhase } from "#phases/enemy-command-phase";
import { TurnInitPhase } from "#phases/turn-init-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import Phaser from "phaser"; import Phaser from "phaser";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest"; import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
@ -43,7 +41,7 @@ describe("Moves - Tail whip", () => {
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0); expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(0);
game.move.select(moveToUse); game.move.select(moveToUse);
await game.phaseInterceptor.runFrom(EnemyCommandPhase).to(TurnInitPhase); await game.toEndOfTurn();
expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1); expect(enemyPokemon.getStatStage(Stat.DEF)).toBe(-1);
}); });

View File

@ -9,7 +9,6 @@ import { MessagePhase } from "#phases/message-phase";
import { import {
MysteryEncounterBattlePhase, MysteryEncounterBattlePhase,
MysteryEncounterOptionSelectedPhase, MysteryEncounterOptionSelectedPhase,
MysteryEncounterPhase,
MysteryEncounterRewardsPhase, MysteryEncounterRewardsPhase,
} from "#phases/mystery-encounter-phases"; } from "#phases/mystery-encounter-phases";
import { VictoryPhase } from "#phases/victory-phase"; import { VictoryPhase } from "#phases/victory-phase";
@ -89,9 +88,9 @@ export async function runMysteryEncounterToEnd(
uiHandler.processInput(Button.ACTION); uiHandler.processInput(Button.ACTION);
}); });
await game.phaseInterceptor.to(CommandPhase); await game.toNextTurn();
} else { } else {
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
} }
} }
@ -112,7 +111,7 @@ export async function runSelectMysteryEncounterOption(
); );
if (game.isCurrentPhase(MessagePhase)) { if (game.isCurrentPhase(MessagePhase)) {
await game.phaseInterceptor.run(MessagePhase); await game.phaseInterceptor.to("MessagePhase");
} }
// dispose of intro messages // dispose of intro messages
@ -126,7 +125,7 @@ export async function runSelectMysteryEncounterOption(
() => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase), () => game.isCurrentPhase(MysteryEncounterOptionSelectedPhase),
); );
await game.phaseInterceptor.to(MysteryEncounterPhase, true); await game.phaseInterceptor.to("MysteryEncounterPhase", true);
// select the desired option // select the desired option
const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>(); const uiHandler = game.scene.ui.getHandler<MysteryEncounterUiHandler>();
@ -205,7 +204,7 @@ export async function skipBattleRunMysteryEncounterRewardsPhase(game: GameManage
game.scene.field.remove(p); game.scene.field.remove(p);
}); });
game.scene.phaseManager.pushPhase(new VictoryPhase(0)); game.scene.phaseManager.pushPhase(new VictoryPhase(0));
game.phaseInterceptor.superEndPhase(); game.endPhase();
game.setMode(UiMode.MESSAGE); game.setMode(UiMode.MESSAGE);
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase, runRewardsPhase); await game.phaseInterceptor.to("MysteryEncounterRewardsPhase", runRewardsPhase);
} }

View File

@ -134,7 +134,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[]; const berriesAfter = scene.findModifiers(m => m instanceof BerryModifier) as BerryModifier[];
@ -147,9 +147,7 @@ describe("Berries Abound - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.BERRIES_ABOUND, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase);
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -232,9 +230,9 @@ describe("Berries Abound - Mystery Encounter", () => {
}); });
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -368,9 +368,9 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterRewardsPhase.name);
game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers game.phaseInterceptor["prompts"] = []; // Clear out prompt handlers
game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => { game.onNextPrompt("MysteryEncounterRewardsPhase", UiMode.OPTION_SELECT, () => {
game.phaseInterceptor.superEndPhase(); game.endPhase();
}); });
await game.phaseInterceptor.run(MysteryEncounterRewardsPhase); await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
expect(selectOptionSpy).toHaveBeenCalledTimes(1); expect(selectOptionSpy).toHaveBeenCalledTimes(1);
const optionData = selectOptionSpy.mock.calls[0][0]; const optionData = selectOptionSpy.mock.calls[0][0];
@ -395,7 +395,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
it("should NOT be selectable if the player doesn't have any Bug types", async () => { it("should NOT be selectable if the player doesn't have any Bug types", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [SpeciesId.ABRA]); await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, [SpeciesId.ABRA]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
@ -417,7 +417,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -436,7 +436,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -458,7 +458,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -482,7 +482,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -530,7 +530,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
it("should NOT be selectable if the player doesn't have any Bug items", async () => { it("should NOT be selectable if the player doesn't have any Bug items", async () => {
game.scene.modifiers = []; game.scene.modifiers = [];
await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.BUG_TYPE_SUPERFAN, defaultParty);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
game.scene.modifiers = []; game.scene.modifiers = [];
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
@ -558,7 +558,7 @@ describe("Bug-Type Superfan - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -25,7 +25,6 @@ import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { CommandPhase } from "#phases/command-phase"; import { CommandPhase } from "#phases/command-phase";
import { MovePhase } from "#phases/move-phase"; import { MovePhase } from "#phases/move-phase";
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { NewBattlePhase } from "#phases/new-battle-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
runMysteryEncounterToEnd, runMysteryEncounterToEnd,
@ -200,9 +199,9 @@ describe("Clowning Around - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.CLOWNING_AROUND, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability; const abilityToTrain = scene.currentBattle.mysteryEncounter?.misc.ability;
game.onNextPrompt("PostMysteryEncounterPhase", UiMode.MESSAGE, () => { game.onNextPrompt("PostMysteryEncounterPhase", UiMode.MESSAGE, () => {
@ -215,7 +214,7 @@ describe("Clowning Around - Mystery Encounter", () => {
const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler; const partyUiHandler = game.scene.ui.handlers[UiMode.PARTY] as PartyUiHandler;
vi.spyOn(partyUiHandler, "show"); vi.spyOn(partyUiHandler, "show");
game.endPhase(); game.endPhase();
await game.phaseInterceptor.to(PostMysteryEncounterPhase); await game.phaseInterceptor.to("PostMysteryEncounterPhase");
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(PostMysteryEncounterPhase.name);
// Wait for Yes/No confirmation to appear // Wait for Yes/No confirmation to appear
@ -228,7 +227,7 @@ describe("Clowning Around - Mystery Encounter", () => {
// Click "Select" on Pokemon // Click "Select" on Pokemon
partyUiHandler.processInput(Button.ACTION); partyUiHandler.processInput(Button.ACTION);
// Stop next battle before it runs // Stop next battle before it runs
await game.phaseInterceptor.to(NewBattlePhase, false); await game.phaseInterceptor.to("NewBattlePhase", false);
const leadPokemon = scene.getPlayerParty()[0]; const leadPokemon = scene.getPlayerParty()[0];
expect(leadPokemon.customPokemonData?.ability).toBe(abilityToTrain); expect(leadPokemon.customPokemonData?.ability).toBe(abilityToTrain);

View File

@ -126,9 +126,9 @@ describe("Dancing Lessons - Mystery Encounter", () => {
partyLead.calculateStats(); partyLead.calculateStats();
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -215,7 +215,7 @@ describe("Dancing Lessons - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DANCING_LESSONS, defaultParty);
const partyCountBefore = scene.getPlayerParty().length; const partyCountBefore = scene.getPlayerParty().length;
scene.getPlayerParty().forEach(p => (p.moveset = [])); scene.getPlayerParty().forEach(p => (p.moveset = []));
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);

View File

@ -94,7 +94,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 1); await runMysteryEncounterToEnd(game, 1);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -131,7 +131,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -171,7 +171,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 3); await runMysteryEncounterToEnd(game, 3);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -211,7 +211,7 @@ describe("Department Store Sale - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.DEPARTMENT_STORE_SALE, defaultParty);
await runMysteryEncounterToEnd(game, 4); await runMysteryEncounterToEnd(game, 4);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -122,9 +122,9 @@ describe("Fight or Flight - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -155,7 +155,7 @@ describe("Fight or Flight - Mystery Encounter", () => {
it("should NOT be selectable if the player doesn't have a Stealing move", async () => { it("should NOT be selectable if the player doesn't have a Stealing move", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FIGHT_OR_FLIGHT, defaultParty);
scene.getPlayerParty().forEach(p => (p.moveset = [])); scene.getPlayerParty().forEach(p => (p.moveset = []));
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
@ -182,9 +182,9 @@ describe("Fight or Flight - Mystery Encounter", () => {
const item = game.scene.currentBattle.mysteryEncounter!.misc; const item = game.scene.currentBattle.mysteryEncounter!.misc;
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -120,7 +120,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
it("should NOT be selectable if the player doesn't have enough money", async () => { it("should NOT be selectable if the player doesn't have enough money", async () => {
game.scene.money = 0; game.scene.money = 0;
await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.FUN_AND_GAMES, defaultParty);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
@ -162,7 +162,7 @@ describe("Fun And Games! - Mystery Encounter", () => {
// Turn 3 // Turn 3
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
@ -181,11 +181,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
// Skip minigame // Skip minigame
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -210,11 +210,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
wobbuffet.hp = Math.floor(0.2 * wobbuffet.getMaxHp()); wobbuffet.hp = Math.floor(0.2 * wobbuffet.getMaxHp());
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -240,11 +240,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
wobbuffet.hp = Math.floor(0.1 * wobbuffet.getMaxHp()); wobbuffet.hp = Math.floor(0.1 * wobbuffet.getMaxHp());
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -270,11 +270,11 @@ describe("Fun And Games! - Mystery Encounter", () => {
wobbuffet.hp = 1; wobbuffet.hp = 1;
scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0; scene.currentBattle.mysteryEncounter!.misc.turnsRemaining = 0;
(game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL); (game.scene.phaseManager.getCurrentPhase() as CommandPhase).handleCommand(Command.FIGHT, 0, MoveUseMode.NORMAL);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
// Rewards // Rewards
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -227,7 +227,7 @@ describe("Global Trade System - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 }); await runMysteryEncounterToEnd(game, 3, { pokemonNo: 1, optionNo: 1 });
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -161,9 +161,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -205,9 +205,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -262,9 +262,9 @@ describe("Mysterious Challengers - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.MYSTERIOUS_CHALLENGERS, defaultParty);
await runMysteryEncounterToEnd(game, 3, undefined, true); await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -146,7 +146,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
it("should NOT be selectable if the player doesn't have enough money", async () => { it("should NOT be selectable if the player doesn't have enough money", async () => {
game.scene.money = 0; game.scene.money = 0;
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
@ -218,7 +218,7 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
it("should NOT be selectable if the player doesn't the right type pokemon", async () => { it("should NOT be selectable if the player doesn't the right type pokemon", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.BLASTOISE]); await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, [SpeciesId.BLASTOISE]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
const encounterPhase = scene.phaseManager.getCurrentPhase(); const encounterPhase = scene.phaseManager.getCurrentPhase();
expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name); expect(encounterPhase?.constructor.name).toBe(MysteryEncounterPhase.name);
@ -299,9 +299,9 @@ describe("Teleporting Hijinks - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.TELEPORTING_HIJINKS, defaultParty);
await runMysteryEncounterToEnd(game, 3, undefined, true); await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -13,7 +13,6 @@ import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter"; import { TheExpertPokemonBreederEncounter } from "#mystery-encounters/the-expert-pokemon-breeder-encounter";
import { CommandPhase } from "#phases/command-phase"; import { CommandPhase } from "#phases/command-phase";
import { PostMysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { import {
runMysteryEncounterToEnd, runMysteryEncounterToEnd,
@ -176,7 +175,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 1, undefined, true); await runMysteryEncounterToEnd(game, 1, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
@ -187,8 +186,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
game.phaseInterceptor.superEndPhase(); game.endPhase();
await game.phaseInterceptor.to(PostMysteryEncounterPhase); await game.phaseInterceptor.to("PostMysteryEncounterPhase");
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship; const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon1.friendship;
// 20 from ME + extra from winning battle (that extra is not accurate to what happens in game. // 20 from ME + extra from winning battle (that extra is not accurate to what happens in game.
@ -261,7 +260,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
@ -272,8 +271,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
game.phaseInterceptor.superEndPhase(); game.endPhase();
await game.phaseInterceptor.to(PostMysteryEncounterPhase); await game.phaseInterceptor.to("PostMysteryEncounterPhase");
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship; const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon2.friendship;
expect(friendshipAfter).toBe(friendshipBefore + 20 + FRIENDSHIP_GAIN_FROM_BATTLE); // 20 from ME + extra for friendship gained from winning battle expect(friendshipAfter).toBe(friendshipBefore + 20 + FRIENDSHIP_GAIN_FROM_BATTLE); // 20 from ME + extra for friendship gained from winning battle
@ -343,7 +342,7 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
await runMysteryEncounterToEnd(game, 3, undefined, true); await runMysteryEncounterToEnd(game, 3, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const eggsAfter = scene.gameData.eggs; const eggsAfter = scene.gameData.eggs;
@ -354,8 +353,8 @@ describe("The Expert Pokémon Breeder - Mystery Encounter", () => {
expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs); expect(eggsAfter.filter(egg => egg.tier === EggTier.COMMON).length).toBe(commonEggs);
expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs); expect(eggsAfter.filter(egg => egg.tier === EggTier.RARE).length).toBe(rareEggs);
game.phaseInterceptor.superEndPhase(); game.endPhase();
await game.phaseInterceptor.to(PostMysteryEncounterPhase); await game.phaseInterceptor.to("PostMysteryEncounterPhase");
const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship; const friendshipAfter = scene.currentBattle.mysteryEncounter!.misc.pokemon3.friendship;
expect(friendshipAfter).toBe(friendshipBefore + 20 + FRIENDSHIP_GAIN_FROM_BATTLE); // 20 + extra for friendship gained from winning battle expect(friendshipAfter).toBe(friendshipBefore + 20 + FRIENDSHIP_GAIN_FROM_BATTLE); // 20 + extra for friendship gained from winning battle

View File

@ -229,9 +229,9 @@ describe("The Strong Stuff - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.THE_STRONG_STUFF, defaultParty);
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -16,7 +16,6 @@ import * as MysteryEncounters from "#mystery-encounters/mystery-encounters";
import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters"; import { HUMAN_TRANSITABLE_BIOMES } from "#mystery-encounters/mystery-encounters";
import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter"; import { TheWinstrateChallengeEncounter } from "#mystery-encounters/the-winstrate-challenge-encounter";
import { CommandPhase } from "#phases/command-phase"; import { CommandPhase } from "#phases/command-phase";
import { MysteryEncounterRewardsPhase } from "#phases/mystery-encounter-phases";
import { PartyHealPhase } from "#phases/party-heal-phase"; import { PartyHealPhase } from "#phases/party-heal-phase";
import { SelectModifierPhase } from "#phases/select-modifier-phase"; import { SelectModifierPhase } from "#phases/select-modifier-phase";
import { VictoryPhase } from "#phases/victory-phase"; import { VictoryPhase } from "#phases/victory-phase";
@ -295,9 +294,9 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
// Should have Macho Brace in the rewards // Should have Macho Brace in the rewards
await skipBattleToNextBattle(game, true); await skipBattleToNextBattle(game, true);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -339,7 +338,7 @@ describe("The Winstrate Challenge - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.THE_WINSTRATE_CHALLENGE, defaultParty);
await runMysteryEncounterToEnd(game, 2); await runMysteryEncounterToEnd(game, 2);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -366,11 +365,10 @@ async function skipBattleToNextBattle(game: GameManager, isFinalBattle = false)
p.status = new Status(StatusEffect.FAINT); p.status = new Status(StatusEffect.FAINT);
game.scene.field.remove(p); game.scene.field.remove(p);
}); });
game.phaseInterceptor["onHold"] = [];
game.scene.phaseManager.pushPhase(new VictoryPhase(0)); game.scene.phaseManager.pushPhase(new VictoryPhase(0));
game.phaseInterceptor.superEndPhase(); game.endPhase();
if (isFinalBattle) { if (isFinalBattle) {
await game.phaseInterceptor.to(MysteryEncounterRewardsPhase); await game.phaseInterceptor.to("MysteryEncounterRewardsPhase");
} else { } else {
await game.toNextTurn(); await game.toNextTurn();
} }

View File

@ -172,7 +172,7 @@ describe("Trash to Treasure - Mystery Encounter", () => {
it("should give 2 Leftovers, 1 Shell Bell, and Black Sludge", async () => { it("should give 2 Leftovers, 1 Shell Bell, and Black Sludge", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
await runMysteryEncounterToEnd(game, 1); await runMysteryEncounterToEnd(game, 1);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier; const leftovers = scene.findModifier(m => m instanceof TurnHealModifier) as TurnHealModifier;
@ -242,9 +242,9 @@ describe("Trash to Treasure - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.TRASH_TO_TREASURE, defaultParty);
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -116,7 +116,7 @@ describe("Weird Dream - Mystery Encounter", () => {
const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal()); const bstsPrior = pokemonPrior.map(species => species.getSpeciesForm().getBaseStatTotal());
await runMysteryEncounterToEnd(game, 1); await runMysteryEncounterToEnd(game, 1);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
const pokemonAfter = scene.getPlayerParty(); const pokemonAfter = scene.getPlayerParty();
@ -139,9 +139,9 @@ describe("Weird Dream - Mystery Encounter", () => {
it("should have 1 Memory Mushroom, 5 Rogue Balls, and 3 Mints in rewards", async () => { it("should have 1 Memory Mushroom, 5 Rogue Balls, and 3 Mints in rewards", async () => {
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
await runMysteryEncounterToEnd(game, 1); await runMysteryEncounterToEnd(game, 1);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -196,9 +196,9 @@ describe("Weird Dream - Mystery Encounter", () => {
await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty); await game.runToMysteryEncounter(MysteryEncounterType.WEIRD_DREAM, defaultParty);
await runMysteryEncounterToEnd(game, 2, undefined, true); await runMysteryEncounterToEnd(game, 2, undefined, true);
await skipBattleRunMysteryEncounterRewardsPhase(game); await skipBattleRunMysteryEncounterRewardsPhase(game);
await game.phaseInterceptor.to(SelectModifierPhase, false); await game.phaseInterceptor.to("SelectModifierPhase", false);
expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name); expect(scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(SelectModifierPhase.name);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

View File

@ -37,7 +37,7 @@ describe("Mystery Encounter Phases", () => {
SpeciesId.VOLCARONA, SpeciesId.VOLCARONA,
]); ]);
await game.phaseInterceptor.to(MysteryEncounterPhase, false); await game.phaseInterceptor.to("MysteryEncounterPhase", false);
expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name); expect(game.scene.phaseManager.getCurrentPhase()?.constructor.name).toBe(MysteryEncounterPhase.name);
}); });
@ -49,9 +49,9 @@ describe("Mystery Encounter Phases", () => {
game.onNextPrompt("MysteryEncounterPhase", UiMode.MYSTERY_ENCOUNTER, () => { game.onNextPrompt("MysteryEncounterPhase", UiMode.MYSTERY_ENCOUNTER, () => {
// End phase early for test // End phase early for test
game.phaseInterceptor.superEndPhase(); game.endPhase();
}); });
await game.phaseInterceptor.run(MysteryEncounterPhase); await game.phaseInterceptor.to("MysteryEncounterPhase");
expect(game.scene.mysteryEncounterSaveData.encounteredEvents.length).toBeGreaterThan(0); expect(game.scene.mysteryEncounterSaveData.encounteredEvents.length).toBeGreaterThan(0);
expect(game.scene.mysteryEncounterSaveData.encounteredEvents[0].type).toEqual( expect(game.scene.mysteryEncounterSaveData.encounteredEvents[0].type).toEqual(
@ -75,7 +75,7 @@ describe("Mystery Encounter Phases", () => {
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
}); });
await game.phaseInterceptor.run(MysteryEncounterPhase); await game.phaseInterceptor.to("MysteryEncounterPhase");
// Select option 1 for encounter // Select option 1 for encounter
const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler; const handler = game.scene.ui.getHandler() as MysteryEncounterUiHandler;

View File

@ -241,7 +241,7 @@ describe("SelectModifierPhase", () => {
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers); const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
scene.phaseManager.unshiftPhase(selectModifierPhase); scene.phaseManager.unshiftPhase(selectModifierPhase);
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(
@ -265,7 +265,7 @@ describe("SelectModifierPhase", () => {
const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers); const selectModifierPhase = new SelectModifierPhase(0, undefined, customModifiers);
scene.phaseManager.unshiftPhase(selectModifierPhase); scene.phaseManager.unshiftPhase(selectModifierPhase);
game.move.select(MoveId.SPLASH); game.move.select(MoveId.SPLASH);
await game.phaseInterceptor.run(SelectModifierPhase); await game.phaseInterceptor.to("SelectModifierPhase");
expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT); expect(scene.ui.getMode()).to.equal(UiMode.MODIFIER_SELECT);
const modifierSelectHandler = scene.ui.handlers.find( const modifierSelectHandler = scene.ui.handlers.find(

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

@ -20,13 +20,11 @@ import { ModifierTypeOption } from "#modifiers/modifier-type";
import { CheckSwitchPhase } from "#phases/check-switch-phase"; import { CheckSwitchPhase } from "#phases/check-switch-phase";
import { CommandPhase } from "#phases/command-phase"; import { CommandPhase } from "#phases/command-phase";
import { EncounterPhase } from "#phases/encounter-phase"; import { EncounterPhase } from "#phases/encounter-phase";
import { LoginPhase } from "#phases/login-phase";
import { MovePhase } from "#phases/move-phase"; import { MovePhase } from "#phases/move-phase";
import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases"; import { MysteryEncounterPhase } from "#phases/mystery-encounter-phases";
import { NewBattlePhase } from "#phases/new-battle-phase"; import { NewBattlePhase } from "#phases/new-battle-phase";
import { SelectStarterPhase } from "#phases/select-starter-phase"; import { SelectStarterPhase } from "#phases/select-starter-phase";
import type { SelectTargetPhase } from "#phases/select-target-phase"; import type { SelectTargetPhase } from "#phases/select-target-phase";
import { TitlePhase } from "#phases/title-phase";
import { TurnEndPhase } from "#phases/turn-end-phase"; import { TurnEndPhase } from "#phases/turn-end-phase";
import { TurnInitPhase } from "#phases/turn-init-phase"; import { TurnInitPhase } from "#phases/turn-init-phase";
import { TurnStartPhase } from "#phases/turn-start-phase"; import { TurnStartPhase } from "#phases/turn-start-phase";
@ -188,10 +186,12 @@ export class GameManager {
* @returns A promise that resolves when the title phase is reached. * @returns A promise that resolves when the title phase is reached.
*/ */
async runToTitle(): Promise<void> { async runToTitle(): Promise<void> {
await this.phaseInterceptor.whenAboutToRun(LoginPhase); // Go to login phase and skip past it
this.phaseInterceptor.pop(); await this.phaseInterceptor.to("LoginPhase", false);
await this.phaseInterceptor.run(TitlePhase); this.phaseInterceptor.shiftPhase(true);
await this.phaseInterceptor.to("TitlePhase");
// TODO: This should be moved to a separate initialization method
this.scene.gameSpeed = 5; this.scene.gameSpeed = 5;
this.scene.moveAnimations = false; this.scene.moveAnimations = false;
this.scene.showLevelUpStats = false; this.scene.showLevelUpStats = false;
@ -270,7 +270,7 @@ export class GameManager {
true, true,
); );
await this.phaseInterceptor.run(EncounterPhase); await this.phaseInterceptor.to("EncounterPhase");
if (!isNullOrUndefined(encounterType)) { if (!isNullOrUndefined(encounterType)) {
expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(encounterType); expect(this.scene.currentBattle?.mysteryEncounter?.encounterType).toBe(encounterType);
} }
@ -542,7 +542,7 @@ export class GameManager {
* ``` * ```
*/ */
async setTurnOrder(order: BattlerIndex[]): Promise<void> { async setTurnOrder(order: BattlerIndex[]): Promise<void> {
await this.phaseInterceptor.to(TurnStartPhase, false); await this.phaseInterceptor.to("TurnStartPhase", false);
vi.spyOn(this.scene.phaseManager.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order); vi.spyOn(this.scene.phaseManager.getCurrentPhase() as TurnStartPhase, "getSpeedOrder").mockReturnValue(order);
} }

View File

@ -49,7 +49,7 @@ export class ChallengeModeHelper extends GameManagerHelper {
selectStarterPhase.initBattle(starters); selectStarterPhase.initBattle(starters);
}); });
await this.game.phaseInterceptor.run(EncounterPhase); await this.game.phaseInterceptor.to("EncounterPhase");
if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0 && this.game.override.removeEnemyStartingItems) { if (overrides.OPP_HELD_ITEMS_OVERRIDE.length === 0 && this.game.override.removeEnemyStartingItems) {
this.game.removeEnemyHeldItems(); this.game.removeEnemyHeldItems();
} }

View File

@ -57,7 +57,7 @@ export class ReloadHelper extends GameManagerHelper {
this.game.scene.modifiers = []; this.game.scene.modifiers = [];
} }
titlePhase.loadSaveSlot(-1); // Load the desired session data titlePhase.loadSaveSlot(-1); // Load the desired session data
this.game.phaseInterceptor.shift(); // Loading the save slot also ended TitlePhase, clean it up this.game.phaseInterceptor.shiftPhase(); // Loading the save slot also ended TitlePhase, clean it up
// Run through prompts for switching Pokemon, copied from classicModeHelper.ts // Run through prompts for switching Pokemon, copied from classicModeHelper.ts
if (this.game.scene.battleStyle === BattleStyle.SWITCH) { if (this.game.scene.battleStyle === BattleStyle.SWITCH) {

View File

@ -1,3 +1,4 @@
import type { BattleScene } from "#app/battle-scene";
import { Phase } from "#app/phase"; import { Phase } from "#app/phase";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { AttemptRunPhase } from "#phases/attempt-run-phase"; import { AttemptRunPhase } from "#phases/attempt-run-phase";
@ -64,6 +65,7 @@ import { UnlockPhase } from "#phases/unlock-phase";
import { VictoryPhase } from "#phases/victory-phase"; import { VictoryPhase } from "#phases/victory-phase";
import { ErrorInterceptor } from "#test/test-utils/error-interceptor"; import { ErrorInterceptor } from "#test/test-utils/error-interceptor";
import type { PhaseClass, PhaseString } from "#types/phase-types"; import type { PhaseClass, PhaseString } from "#types/phase-types";
import type { AwaitableUiHandler } from "#ui/awaitable-ui-handler";
import { UI } from "#ui/ui"; import { UI } from "#ui/ui";
export interface PromptHandler { export interface PromptHandler {
@ -76,20 +78,39 @@ export interface PromptHandler {
type PhaseInterceptorPhase = PhaseClass | PhaseString; type PhaseInterceptorPhase = PhaseClass | PhaseString;
interface PhaseStub {
start(): void;
endBySetMode: boolean;
}
interface InProgressStub {
name: string;
callback(): void;
onError(error: any): void;
}
interface onHoldStub {
name: string;
call(): void;
}
export class PhaseInterceptor { export class PhaseInterceptor {
public scene; public scene: BattleScene;
public phases = {}; // @ts-expect-error: initialized in `initPhases`
public log: string[]; public phases: Record<PhaseString, PhaseStub> = {};
private onHold; public log: PhaseString[];
private interval; /**
private promptInterval; * TODO: This should not be an array;
private intervalRun; * Our linear phase system means only 1 phase is ever started at once (if any)
*/
private onHold: onHoldStub[];
private interval: NodeJS.Timeout;
private promptInterval: NodeJS.Timeout;
private intervalRun: NodeJS.Timeout;
private prompts: PromptHandler[]; private prompts: PromptHandler[];
private phaseFrom; private inProgress?: InProgressStub;
private inProgress; private originalSetMode: UI["setMode"];
private originalSetMode; private originalSuperEnd: Phase["end"];
private originalSetOverlayMode;
private originalSuperEnd;
/** /**
* List of phases with their corresponding start methods. * List of phases with their corresponding start methods.
@ -100,72 +121,73 @@ export class PhaseInterceptor {
* `initPhases()` so that its subclasses can use `super.start()` properly. * `initPhases()` so that its subclasses can use `super.start()` properly.
*/ */
private PHASES = [ private PHASES = [
[LoginPhase, this.startPhase], LoginPhase,
[TitlePhase, this.startPhase], TitlePhase,
[SelectGenderPhase, this.startPhase], SelectGenderPhase,
[NewBiomeEncounterPhase, this.startPhase], NewBiomeEncounterPhase,
[SelectStarterPhase, this.startPhase], SelectStarterPhase,
[PostSummonPhase, this.startPhase], PostSummonPhase,
[SummonPhase, this.startPhase], SummonPhase,
[ToggleDoublePositionPhase, this.startPhase], ToggleDoublePositionPhase,
[CheckSwitchPhase, this.startPhase], CheckSwitchPhase,
[ShowAbilityPhase, this.startPhase], ShowAbilityPhase,
[MessagePhase, this.startPhase], MessagePhase,
[TurnInitPhase, this.startPhase], TurnInitPhase,
[CommandPhase, this.startPhase], CommandPhase,
[EnemyCommandPhase, this.startPhase], EnemyCommandPhase,
[TurnStartPhase, this.startPhase], TurnStartPhase,
[MovePhase, this.startPhase], MovePhase,
[MoveEffectPhase, this.startPhase], MoveEffectPhase,
[DamageAnimPhase, this.startPhase], DamageAnimPhase,
[FaintPhase, this.startPhase], FaintPhase,
[BerryPhase, this.startPhase], BerryPhase,
[TurnEndPhase, this.startPhase], TurnEndPhase,
[BattleEndPhase, this.startPhase], BattleEndPhase,
[EggLapsePhase, this.startPhase], EggLapsePhase,
[SelectModifierPhase, this.startPhase], SelectModifierPhase,
[NextEncounterPhase, this.startPhase], NextEncounterPhase,
[NewBattlePhase, this.startPhase], NewBattlePhase,
[VictoryPhase, this.startPhase], VictoryPhase,
[LearnMovePhase, this.startPhase], LearnMovePhase,
[MoveEndPhase, this.startPhase], MoveEndPhase,
[StatStageChangePhase, this.startPhase], StatStageChangePhase,
[ShinySparklePhase, this.startPhase], ShinySparklePhase,
[SelectTargetPhase, this.startPhase], SelectTargetPhase,
[UnavailablePhase, this.startPhase], UnavailablePhase,
[QuietFormChangePhase, this.startPhase], QuietFormChangePhase,
[SwitchPhase, this.startPhase], SwitchPhase,
[SwitchSummonPhase, this.startPhase], SwitchSummonPhase,
[PartyHealPhase, this.startPhase], PartyHealPhase,
[FormChangePhase, this.startPhase], FormChangePhase,
[EvolutionPhase, this.startPhase], EvolutionPhase,
[EndEvolutionPhase, this.startPhase], EndEvolutionPhase,
[LevelCapPhase, this.startPhase], LevelCapPhase,
[AttemptRunPhase, this.startPhase], AttemptRunPhase,
[SelectBiomePhase, this.startPhase], SelectBiomePhase,
[PositionalTagPhase, this.startPhase], PositionalTagPhase,
[PokemonTransformPhase, this.startPhase], PokemonTransformPhase,
[MysteryEncounterPhase, this.startPhase], MysteryEncounterPhase,
[MysteryEncounterOptionSelectedPhase, this.startPhase], MysteryEncounterOptionSelectedPhase,
[MysteryEncounterBattlePhase, this.startPhase], MysteryEncounterBattlePhase,
[MysteryEncounterRewardsPhase, this.startPhase], MysteryEncounterRewardsPhase,
[PostMysteryEncounterPhase, this.startPhase], PostMysteryEncounterPhase,
[RibbonModifierRewardPhase, this.startPhase], RibbonModifierRewardPhase,
[GameOverModifierRewardPhase, this.startPhase], GameOverModifierRewardPhase,
[ModifierRewardPhase, this.startPhase], ModifierRewardPhase,
[PartyExpPhase, this.startPhase], PartyExpPhase,
[ExpPhase, this.startPhase], ExpPhase,
[EncounterPhase, this.startPhase], EncounterPhase,
[GameOverPhase, this.startPhase], GameOverPhase,
[UnlockPhase, this.startPhase], UnlockPhase,
[PostGameOverPhase, this.startPhase], PostGameOverPhase,
[RevivalBlessingPhase, this.startPhase], RevivalBlessingPhase,
]; ];
private endBySetMode = [ private endBySetMode = [
TitlePhase, TitlePhase,
SelectGenderPhase, SelectGenderPhase,
CommandPhase, CommandPhase,
SelectStarterPhase,
SelectModifierPhase, SelectModifierPhase,
MysteryEncounterPhase, MysteryEncounterPhase,
PostMysteryEncounterPhase, PostMysteryEncounterPhase,
@ -175,7 +197,7 @@ export class PhaseInterceptor {
* Constructor to initialize the scene and properties, and to start the phase handling. * Constructor to initialize the scene and properties, and to start the phase handling.
* @param scene - The scene to be managed. * @param scene - The scene to be managed.
*/ */
constructor(scene) { constructor(scene: BattleScene) {
this.scene = scene; this.scene = scene;
this.onHold = []; this.onHold = [];
this.prompts = []; this.prompts = [];
@ -200,16 +222,6 @@ export class PhaseInterceptor {
} }
} }
/**
* Method to set the starting phase.
* @param phaseFrom - The phase to start from.
* @returns The instance of the PhaseInterceptor.
*/
runFrom(phaseFrom: PhaseInterceptorPhase): PhaseInterceptor {
this.phaseFrom = phaseFrom;
return this;
}
/** /**
* Method to transition to a target phase. * Method to transition to a target phase.
* @param phaseTo - The phase to transition to. * @param phaseTo - The phase to transition to.
@ -219,59 +231,50 @@ export class PhaseInterceptor {
async to(phaseTo: PhaseInterceptorPhase, runTarget = true): Promise<void> { async to(phaseTo: PhaseInterceptorPhase, runTarget = true): Promise<void> {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
ErrorInterceptor.getInstance().add(this); ErrorInterceptor.getInstance().add(this);
if (this.phaseFrom) {
await this.run(this.phaseFrom).catch(e => reject(e));
this.phaseFrom = null;
}
const targetName = typeof phaseTo === "string" ? phaseTo : phaseTo.name; const targetName = typeof phaseTo === "string" ? phaseTo : phaseTo.name;
this.intervalRun = setInterval(async () => { this.intervalRun = setInterval(async () => {
const currentPhase = this.onHold?.length && this.onHold[0]; const currentPhase = this.onHold?.length && this.onHold[0];
if (currentPhase && currentPhase.name === targetName) { if (!currentPhase) {
clearInterval(this.intervalRun); // No current phase means the manager either hasn't started yet
if (!runTarget) { // or we were interrupted by prompt; wait for phase to finish
return resolve(); return;
} }
await this.run(currentPhase).catch(e => {
// If current phase is different, run it and wait for it to finish.
if (currentPhase.name !== targetName) {
await this.run().catch(e => {
clearInterval(this.intervalRun); clearInterval(this.intervalRun);
return reject(e); return reject(e);
}); });
return;
}
// Hit target phase; run it and resolve
clearInterval(this.intervalRun);
if (!runTarget) {
return resolve(); return resolve();
} }
if (currentPhase && currentPhase.name !== targetName) { await this.run().catch(e => {
await this.run(currentPhase).catch(e => { clearInterval(this.intervalRun);
clearInterval(this.intervalRun); return reject(e);
return reject(e); });
}); return resolve();
}
}); });
}); });
} }
/** /**
* Method to run a phase with an optional skip function. * Method to run the current phase with an optional skip function.
* @param phaseTarget - The phase to run.
* @param skipFn - Optional skip function.
* @returns A promise that resolves when the phase is run. * @returns A promise that resolves when the phase is run.
*/ */
run(phaseTarget: PhaseInterceptorPhase, skipFn?: (className: PhaseClass) => boolean): Promise<void> { private run(): Promise<void> {
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name; // @ts-expect-error: This is apparently mandatory to avoid a crash; review if this is needed
this.scene.moveAnimations = null; // Mandatory to avoid crash this.scene.moveAnimations = null;
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
ErrorInterceptor.getInstance().add(this); ErrorInterceptor.getInstance().add(this);
const interval = setInterval(async () => { const interval = setInterval(async () => {
const currentPhase = this.onHold.shift(); const currentPhase = this.onHold.shift();
if (currentPhase) { if (currentPhase) {
if (currentPhase.name !== targetName) {
clearInterval(interval);
const skip = skipFn?.(currentPhase.name);
if (skip) {
this.onHold.unshift(currentPhase);
ErrorInterceptor.getInstance().remove(this);
return resolve();
}
clearInterval(interval);
return reject(`Wrong phase: this is ${currentPhase.name} and not ${targetName}`);
}
clearInterval(interval); clearInterval(interval);
this.inProgress = { this.inProgress = {
name: currentPhase.name, name: currentPhase.name,
@ -287,26 +290,6 @@ export class PhaseInterceptor {
}); });
} }
whenAboutToRun(phaseTarget: PhaseInterceptorPhase, _skipFn?: (className: PhaseClass) => boolean): Promise<void> {
const targetName = typeof phaseTarget === "string" ? phaseTarget : phaseTarget.name;
this.scene.moveAnimations = null; // Mandatory to avoid crash
return new Promise(async (resolve, _reject) => {
ErrorInterceptor.getInstance().add(this);
const interval = setInterval(async () => {
const currentPhase = this.onHold[0];
if (currentPhase?.name === targetName) {
clearInterval(interval);
resolve();
}
});
});
}
pop() {
this.onHold.pop();
this.scene.phaseManager.shiftPhase();
}
/** /**
* Remove the current phase from the phase interceptor. * Remove the current phase from the phase interceptor.
* *
@ -316,7 +299,7 @@ export class PhaseInterceptor {
* *
* @param shouldRun Whether or not the current scene should also be run. * @param shouldRun Whether or not the current scene should also be run.
*/ */
shift(shouldRun = false): void { shiftPhase(shouldRun = false): void {
this.onHold.shift(); this.onHold.shift();
if (shouldRun) { if (shouldRun) {
this.scene.phaseManager.shiftPhase(); this.scene.phaseManager.shiftPhase();
@ -328,17 +311,16 @@ export class PhaseInterceptor {
*/ */
initPhases() { initPhases() {
this.originalSetMode = UI.prototype.setMode; this.originalSetMode = UI.prototype.setMode;
this.originalSetOverlayMode = UI.prototype.setOverlayMode;
this.originalSuperEnd = Phase.prototype.end; this.originalSuperEnd = Phase.prototype.end;
UI.prototype.setMode = (mode, ...args) => this.setMode.call(this, mode, ...args); UI.prototype.setMode = (mode, ...args) => this.setMode.call(this, mode, ...args);
Phase.prototype.end = () => this.superEndPhase.call(this); Phase.prototype.end = () => this.superEndPhase.call(this);
for (const [phase, methodStart] of this.PHASES) { for (const phase of this.PHASES) {
const originalStart = phase.prototype.start; const originalStart = phase.prototype.start;
this.phases[phase.name] = { this.phases[phase.name] = {
start: originalStart, start: originalStart,
endBySetMode: this.endBySetMode.some(elm => elm.name === phase.name), endBySetMode: this.endBySetMode.some(elm => elm.name === phase.name),
}; };
phase.prototype.start = () => methodStart.call(this, phase); phase.prototype.start = () => this.startPhase.call(this, phase);
} }
} }
@ -347,7 +329,7 @@ export class PhaseInterceptor {
* @param phase - The phase to start. * @param phase - The phase to start.
*/ */
startPhase(phase: PhaseClass) { startPhase(phase: PhaseClass) {
this.log.push(phase.name); this.log.push(phase.name as PhaseString);
const instance = this.scene.phaseManager.getCurrentPhase(); const instance = this.scene.phaseManager.getCurrentPhase();
this.onHold.push({ this.onHold.push({
name: phase.name, name: phase.name,
@ -357,16 +339,11 @@ export class PhaseInterceptor {
}); });
} }
unlock() {
this.inProgress?.callback();
this.inProgress = undefined;
}
/** /**
* Method to end a phase and log it. * Method to end a phase and log it.
* @param phase - The phase to start. * @param phase - The phase to start.
*/ */
superEndPhase() { private superEndPhase() {
const instance = this.scene.phaseManager.getCurrentPhase(); const instance = this.scene.phaseManager.getCurrentPhase();
this.originalSuperEnd.apply(instance); this.originalSuperEnd.apply(instance);
this.inProgress?.callback(); this.inProgress?.callback();
@ -379,7 +356,8 @@ export class PhaseInterceptor {
* @param args - Additional arguments to pass to the original method. * @param args - Additional arguments to pass to the original method.
*/ */
setMode(mode: UiMode, ...args: unknown[]): Promise<void> { setMode(mode: UiMode, ...args: unknown[]): Promise<void> {
const currentPhase = this.scene.phaseManager.getCurrentPhase(); // TODO: remove the `!` in PR 6243 / after PR 6243 is merged
const currentPhase = this.scene.phaseManager.getCurrentPhase()!;
const instance = this.scene.ui; const instance = this.scene.ui;
console.log("setMode", `${UiMode[mode]} (=${mode})`, args); console.log("setMode", `${UiMode[mode]} (=${mode})`, args);
const ret = this.originalSetMode.apply(instance, [mode, ...args]); const ret = this.originalSetMode.apply(instance, [mode, ...args]);
@ -395,18 +373,6 @@ export class PhaseInterceptor {
return ret; return ret;
} }
/**
* mock to set overlay mode
* @param mode - The {@linkcode Mode} to set.
* @param args - Additional arguments to pass to the original method.
*/
setOverlayMode(mode: UiMode, ...args: unknown[]): Promise<void> {
const instance = this.scene.ui;
console.log("setOverlayMode", `${UiMode[mode]} (=${mode})`, args);
const ret = this.originalSetOverlayMode.apply(instance, [mode, ...args]);
return ret;
}
/** /**
* Method to start the prompt handler. * Method to start the prompt handler.
*/ */
@ -425,7 +391,7 @@ export class PhaseInterceptor {
currentPhase === actionForNextPrompt.phaseTarget && currentPhase === actionForNextPrompt.phaseTarget &&
currentHandler.active && currentHandler.active &&
(!actionForNextPrompt.awaitingActionInput || (!actionForNextPrompt.awaitingActionInput ||
(actionForNextPrompt.awaitingActionInput && currentHandler.awaitingActionInput)) (actionForNextPrompt.awaitingActionInput && (currentHandler as AwaitableUiHandler)["awaitingActionInput"]))
) { ) {
const prompt = this.prompts.shift(); const prompt = this.prompts.shift();
if (prompt?.callback) { if (prompt?.callback) {
@ -467,11 +433,10 @@ export class PhaseInterceptor {
* function stored in `this.phases`. Additionally, it clears the `promptInterval` and `interval`. * function stored in `this.phases`. Additionally, it clears the `promptInterval` and `interval`.
*/ */
restoreOg() { restoreOg() {
for (const [phase] of this.PHASES) { for (const phase of this.PHASES) {
phase.prototype.start = this.phases[phase.name].start; phase.prototype.start = this.phases[phase.name].start;
} }
UI.prototype.setMode = this.originalSetMode; UI.prototype.setMode = this.originalSetMode;
UI.prototype.setOverlayMode = this.originalSetOverlayMode;
Phase.prototype.end = this.originalSuperEnd; Phase.prototype.end = this.originalSuperEnd;
clearInterval(this.promptInterval); clearInterval(this.promptInterval);
clearInterval(this.interval); clearInterval(this.interval);

View File

@ -69,7 +69,7 @@ describe("UI - Pokedex", () => {
// Open the pokedex UI. // Open the pokedex UI.
await game.runToTitle(); await game.runToTitle();
await game.phaseInterceptor.setOverlayMode(UiMode.POKEDEX); await game.scene.ui.setOverlayMode(UiMode.POKEDEX);
// Get the handler for the current UI. // Get the handler for the current UI.
const handler = game.scene.ui.getHandler(); const handler = game.scene.ui.getHandler();
@ -89,7 +89,7 @@ describe("UI - Pokedex", () => {
// Open the pokedex UI. // Open the pokedex UI.
await game.runToTitle(); await game.runToTitle();
await game.phaseInterceptor.setOverlayMode(UiMode.POKEDEX_PAGE, species, starterAttributes); await game.scene.ui.setOverlayMode(UiMode.POKEDEX_PAGE, species, starterAttributes);
// Get the handler for the current UI. // Get the handler for the current UI.
const handler = game.scene.ui.getHandler(); const handler = game.scene.ui.getHandler();

View File

@ -6,8 +6,6 @@ import { GameModes } from "#enums/game-modes";
import { Nature } from "#enums/nature"; import { Nature } from "#enums/nature";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { EncounterPhase } from "#phases/encounter-phase";
import { SelectStarterPhase } from "#phases/select-starter-phase";
import type { TitlePhase } from "#phases/title-phase"; import type { TitlePhase } from "#phases/title-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler"; import type { OptionSelectItem } from "#ui/abstract-option-select-ui-handler";
@ -54,9 +52,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.RIGHT); handler.processInput(Button.RIGHT);
handler.processInput(Button.LEFT); handler.processInput(Button.LEFT);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -88,7 +85,7 @@ describe("UI - Starter select", () => {
resolve(); resolve();
}); });
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
@ -115,9 +112,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.LEFT); handler.processInput(Button.LEFT);
handler.processInput(Button.CYCLE_GENDER); handler.processInput(Button.CYCLE_GENDER);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -149,7 +145,7 @@ describe("UI - Starter select", () => {
resolve(); resolve();
}); });
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
@ -179,9 +175,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.CYCLE_NATURE); handler.processInput(Button.CYCLE_NATURE);
handler.processInput(Button.CYCLE_ABILITY); handler.processInput(Button.CYCLE_ABILITY);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -213,7 +208,7 @@ describe("UI - Starter select", () => {
resolve(); resolve();
}); });
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
@ -242,9 +237,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.LEFT); handler.processInput(Button.LEFT);
handler.processInput(Button.CYCLE_GENDER); handler.processInput(Button.CYCLE_GENDER);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -276,7 +270,7 @@ describe("UI - Starter select", () => {
resolve(); resolve();
}); });
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
@ -303,9 +297,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.LEFT); handler.processInput(Button.LEFT);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.CYCLE_SHINY);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -337,7 +330,7 @@ describe("UI - Starter select", () => {
resolve(); resolve();
}); });
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(false); expect(game.scene.getPlayerParty()[0].shiny).toBe(false);
@ -365,9 +358,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.CYCLE_SHINY);
handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.CYCLE_SHINY);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -399,7 +391,7 @@ describe("UI - Starter select", () => {
resolve(); resolve();
}); });
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
@ -426,9 +418,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.CYCLE_SHINY);
handler.processInput(Button.CYCLE_SHINY); handler.processInput(Button.CYCLE_SHINY);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -460,7 +451,7 @@ describe("UI - Starter select", () => {
resolve(); resolve();
}); });
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.BULBASAUR);
expect(game.scene.getPlayerParty()[0].shiny).toBe(true); expect(game.scene.getPlayerParty()[0].shiny).toBe(true);
@ -486,9 +477,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.RIGHT); handler.processInput(Button.RIGHT);
handler.processInput(Button.RIGHT); handler.processInput(Button.RIGHT);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -527,7 +517,7 @@ describe("UI - Starter select", () => {
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
saveSlotSelectUiHandler.processInput(Button.ACTION); saveSlotSelectUiHandler.processInput(Button.ACTION);
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.CATERPIE); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.CATERPIE);
}); });
@ -551,9 +541,8 @@ describe("UI - Starter select", () => {
handler.processInput(Button.RIGHT); handler.processInput(Button.RIGHT);
handler.processInput(Button.DOWN); handler.processInput(Button.DOWN);
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.run(SelectStarterPhase); await game.phaseInterceptor.to("SelectStarterPhase");
let options: OptionSelectItem[] = []; let options: OptionSelectItem[] = [];
let optionSelectUiHandler: OptionSelectUiHandler | undefined; let optionSelectUiHandler: OptionSelectUiHandler | undefined;
await new Promise<void>(resolve => { await new Promise<void>(resolve => {
@ -593,7 +582,7 @@ describe("UI - Starter select", () => {
const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler; const saveSlotSelectUiHandler = game.scene.ui.getHandler() as SaveSlotSelectUiHandler;
saveSlotSelectUiHandler.processInput(Button.ACTION); saveSlotSelectUiHandler.processInput(Button.ACTION);
}); });
await game.phaseInterceptor.whenAboutToRun(EncounterPhase); await game.phaseInterceptor.to("EncounterPhase", false);
expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.NIDORAN_M); expect(game.scene.getPlayerParty()[0].species.speciesId).toBe(SpeciesId.NIDORAN_M);
}); });
}); });

View File

@ -72,8 +72,6 @@ describe("UI - Transfer Items", () => {
expect( expect(
handler.optionsContainer.list.some(option => RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text)), handler.optionsContainer.list.some(option => RegExp(/Lum Berry\[color.*(2)/).exec((option as BBCodeText).text)),
).toBe(true); ).toBe(true);
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.to("SelectModifierPhase"); await game.phaseInterceptor.to("SelectModifierPhase");
@ -93,8 +91,6 @@ describe("UI - Transfer Items", () => {
expect(handler.optionsContainer.list.some(option => (option as BBCodeText).text?.includes("Transfer"))).toBe( expect(handler.optionsContainer.list.some(option => (option as BBCodeText).text?.includes("Transfer"))).toBe(
true, true,
); );
game.phaseInterceptor.unlock();
}); });
await game.phaseInterceptor.to("SelectModifierPhase"); await game.phaseInterceptor.to("SelectModifierPhase");

View File

@ -2,7 +2,6 @@ import { Button } from "#enums/buttons";
import { MoveId } from "#enums/move-id"; import { MoveId } from "#enums/move-id";
import { SpeciesId } from "#enums/species-id"; import { SpeciesId } from "#enums/species-id";
import { UiMode } from "#enums/ui-mode"; import { UiMode } from "#enums/ui-mode";
import { CommandPhase } from "#phases/command-phase";
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import type { MockText } from "#test/test-utils/mocks/mocks-container/mock-text"; import type { MockText } from "#test/test-utils/mocks/mocks-container/mock-text";
import { FightUiHandler } from "#ui/fight-ui-handler"; import { FightUiHandler } from "#ui/fight-ui-handler";
@ -46,7 +45,6 @@ describe("UI - Type Hints", () => {
const { ui } = game.scene; const { ui } = game.scene;
const handler = ui.getHandler<FightUiHandler>(); const handler = ui.getHandler<FightUiHandler>();
handler.processInput(Button.ACTION); // select "Fight" handler.processInput(Button.ACTION); // select "Fight"
game.phaseInterceptor.unlock();
}); });
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
@ -59,7 +57,7 @@ describe("UI - Type Hints", () => {
expect.soft(dragonClawText.color).toBe("#929292"); expect.soft(dragonClawText.color).toBe("#929292");
ui.getHandler().processInput(Button.ACTION); ui.getHandler().processInput(Button.ACTION);
}); });
await game.phaseInterceptor.to(CommandPhase); await game.phaseInterceptor.to("CommandPhase");
}); });
it("check status move color", async () => { it("check status move color", async () => {
@ -71,7 +69,6 @@ describe("UI - Type Hints", () => {
const { ui } = game.scene; const { ui } = game.scene;
const handler = ui.getHandler<FightUiHandler>(); const handler = ui.getHandler<FightUiHandler>();
handler.processInput(Button.ACTION); // select "Fight" handler.processInput(Button.ACTION); // select "Fight"
game.phaseInterceptor.unlock();
}); });
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
@ -84,7 +81,7 @@ describe("UI - Type Hints", () => {
expect.soft(growlText.color).toBe(undefined); expect.soft(growlText.color).toBe(undefined);
ui.getHandler().processInput(Button.ACTION); ui.getHandler().processInput(Button.ACTION);
}); });
await game.phaseInterceptor.to(CommandPhase); await game.phaseInterceptor.to("CommandPhase");
}); });
it("should show the proper hint for a move in doubles after one of the enemy pokemon flees", async () => { it("should show the proper hint for a move in doubles after one of the enemy pokemon flees", async () => {
@ -107,7 +104,6 @@ describe("UI - Type Hints", () => {
const { ui } = game.scene; const { ui } = game.scene;
const handler = ui.getHandler<FightUiHandler>(); const handler = ui.getHandler<FightUiHandler>();
handler.processInput(Button.ACTION); // select "Fight" handler.processInput(Button.ACTION); // select "Fight"
game.phaseInterceptor.unlock();
}); });
game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => { game.onNextPrompt("CommandPhase", UiMode.FIGHT, () => {
@ -121,6 +117,6 @@ describe("UI - Type Hints", () => {
expect.soft(shadowBallText.color).toBe(undefined); expect.soft(shadowBallText.color).toBe(undefined);
ui.getHandler().processInput(Button.ACTION); ui.getHandler().processInput(Button.ACTION);
}); });
await game.phaseInterceptor.to(CommandPhase); await game.phaseInterceptor.to("CommandPhase");
}); });
}); });