[Misc] Move asset initialization from preload to launchBattle

https://github.com/pagefaultgames/pokerogue/pull/6109

* Move asset initialization into `preload` instead of `launchBattle`

* Fix init problems; remove unused `waitUntil` util function

* Fixed missing `clearAllPhases` call
This commit is contained in:
Bertie690 2025-08-02 17:29:35 -04:00 committed by GitHub
parent 1f5e089818
commit 957a3e5c24
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 60 additions and 89 deletions

View File

@ -380,9 +380,21 @@ export class BattleScene extends SceneBase {
}; };
} }
populateAnims(); /**
* These moves serve as fallback animations for other moves without loaded animations, and
* must be loaded prior to game start.
*/
const defaultMoves = [MoveId.TACKLE, MoveId.TAIL_WHIP, MoveId.FOCUS_ENERGY, MoveId.STRUGGLE];
await this.initVariantData(); await Promise.all([
populateAnims(),
this.initVariantData(),
initCommonAnims().then(() => loadCommonAnimAssets(true)),
Promise.all(defaultMoves.map(m => initMoveAnim(m))).then(() => loadMoveAnimAssets(defaultMoves, true)),
this.initStarterColors(),
]).catch(reason => {
throw new Error(`Unexpected error during BattleScene preLoad!\nReason: ${reason}`);
});
} }
create() { create() {
@ -584,8 +596,6 @@ export class BattleScene extends SceneBase {
this.party = []; this.party = [];
const loadPokemonAssets = [];
this.arenaPlayer = new ArenaBase(true); this.arenaPlayer = new ArenaBase(true);
this.arenaPlayer.setName("arena-player"); this.arenaPlayer.setName("arena-player");
this.arenaPlayerTransition = new ArenaBase(true); this.arenaPlayerTransition = new ArenaBase(true);
@ -640,26 +650,14 @@ export class BattleScene extends SceneBase {
this.reset(false, false, true); this.reset(false, false, true);
// Initialize UI-related aspects and then start the login phase.
const ui = new UI(); const ui = new UI();
this.uiContainer.add(ui); this.uiContainer.add(ui);
this.ui = ui; this.ui = ui;
ui.setup(); ui.setup();
const defaultMoves = [MoveId.TACKLE, MoveId.TAIL_WHIP, MoveId.FOCUS_ENERGY, MoveId.STRUGGLE]; this.phaseManager.toTitleScreen(true);
this.phaseManager.shiftPhase();
Promise.all([
Promise.all(loadPokemonAssets),
initCommonAnims().then(() => loadCommonAnimAssets(true)),
Promise.all(
[MoveId.TACKLE, MoveId.TAIL_WHIP, MoveId.FOCUS_ENERGY, MoveId.STRUGGLE].map(m => initMoveAnim(m)),
).then(() => loadMoveAnimAssets(defaultMoves, true)),
this.initStarterColors(),
]).then(() => {
this.phaseManager.toTitleScreen(true);
this.phaseManager.shiftPhase();
});
} }
initSession(): void { initSession(): void {

View File

@ -1,5 +1,4 @@
import { GameManager } from "#test/test-utils/game-manager"; import { GameManager } from "#test/test-utils/game-manager";
import { waitUntil } 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";
@ -36,19 +35,6 @@ describe("Test misc", () => {
expect(spy).toHaveBeenCalled(); expect(spy).toHaveBeenCalled();
}); });
// it.skip("test apifetch mock async", async () => {
// const spy = vi.fn();
// await apiFetch("https://localhost:8080/account/info").then(response => {
// expect(response.status).toBe(200);
// expect(response.ok).toBe(true);
// return response.json();
// }).then(data => {
// spy(); // Call the spy function
// expect(data).toEqual({ "username": "greenlamp", "lastSessionSlot": 0 });
// });
// expect(spy).toHaveBeenCalled();
// });
it("test fetch mock sync", async () => { it("test fetch mock sync", async () => {
const response = await fetch("https://localhost:8080/account/info"); const response = await fetch("https://localhost:8080/account/info");
const data = await response.json(); const data = await response.json();
@ -62,19 +48,4 @@ describe("Test misc", () => {
const data = await game.scene.cachedFetch("./battle-anims/splishy-splash.json"); const data = await game.scene.cachedFetch("./battle-anims/splishy-splash.json");
expect(data).toBeDefined(); expect(data).toBeDefined();
}); });
it("testing wait phase queue", async () => {
const fakeScene = {
phaseQueue: [1, 2, 3], // Initially not empty
};
setTimeout(() => {
fakeScene.phaseQueue = [];
}, 500);
const spy = vi.fn();
await waitUntil(() => fakeScene.phaseQueue.length === 0).then(result => {
expect(result).toBe(true);
spy(); // Call the spy function
});
expect(spy).toHaveBeenCalled();
});
}); });

View File

@ -87,17 +87,6 @@ function getTestRunStarters(seed: string, species?: SpeciesId[]): Starter[] {
return starters; return starters;
} }
export function waitUntil(truth): Promise<unknown> {
return new Promise(resolve => {
const interval = setInterval(() => {
if (truth()) {
clearInterval(interval);
resolve(true);
}
}, 1000);
});
}
/** /**
* Useful for populating party, wave index, etc. without having to spin up and run through an entire EncounterPhase * Useful for populating party, wave index, etc. without having to spin up and run through an entire EncounterPhase
*/ */

View File

@ -31,7 +31,7 @@ import { TurnEndPhase } from "#phases/turn-end-phase";
import { TurnInitPhase } from "#phases/turn-init-phase"; import { TurnInitPhase } from "#phases/turn-init-phase";
import { TurnStartPhase } from "#phases/turn-start-phase"; import { TurnStartPhase } from "#phases/turn-start-phase";
import { ErrorInterceptor } from "#test/test-utils/error-interceptor"; import { ErrorInterceptor } from "#test/test-utils/error-interceptor";
import { generateStarter, waitUntil } from "#test/test-utils/game-manager-utils"; import { generateStarter } from "#test/test-utils/game-manager-utils";
import { GameWrapper } from "#test/test-utils/game-wrapper"; import { GameWrapper } from "#test/test-utils/game-wrapper";
import { ChallengeModeHelper } from "#test/test-utils/helpers/challenge-mode-helper"; import { ChallengeModeHelper } from "#test/test-utils/helpers/challenge-mode-helper";
import { ClassicModeHelper } from "#test/test-utils/helpers/classic-mode-helper"; import { ClassicModeHelper } from "#test/test-utils/helpers/classic-mode-helper";
@ -85,30 +85,22 @@ export class GameManager {
constructor(phaserGame: Phaser.Game, bypassLogin = true) { constructor(phaserGame: Phaser.Game, bypassLogin = true) {
localStorage.clear(); localStorage.clear();
ErrorInterceptor.getInstance().clear(); ErrorInterceptor.getInstance().clear();
BattleScene.prototype.randBattleSeedInt = (range, min = 0) => min + range - 1; // This simulates a max roll // Simulate max rolls on RNG functions
// TODO: Create helpers for disabling/enabling battle RNG
BattleScene.prototype.randBattleSeedInt = (range, min = 0) => min + range - 1;
this.gameWrapper = new GameWrapper(phaserGame, bypassLogin); this.gameWrapper = new GameWrapper(phaserGame, bypassLogin);
let firstTimeScene = false; // TODO: Figure out a way to optimize and re-use the same game manager for each test
// Re-use an existing `globalScene` if present, or else create a new scene from scratch.
if (globalScene) { if (globalScene) {
this.scene = globalScene; this.scene = globalScene;
this.phaseInterceptor = new PhaseInterceptor(this.scene);
this.resetScene();
} else { } else {
this.scene = new BattleScene(); this.scene = new BattleScene();
this.phaseInterceptor = new PhaseInterceptor(this.scene);
this.gameWrapper.setScene(this.scene); this.gameWrapper.setScene(this.scene);
firstTimeScene = true;
}
this.phaseInterceptor = new PhaseInterceptor(this.scene);
if (!firstTimeScene) {
this.scene.reset(false, true);
(this.scene.ui.handlers[UiMode.STARTER_SELECT] as StarterSelectUiHandler).clearStarterPreferences();
// Must be run after phase interceptor has been initialized.
this.scene.phaseManager.toTitleScreen(true);
this.scene.phaseManager.shiftPhase();
this.gameWrapper.scene = this.scene;
} }
this.textInterceptor = new TextInterceptor(this.scene); this.textInterceptor = new TextInterceptor(this.scene);
@ -122,10 +114,30 @@ export class GameManager {
this.modifiers = new ModifierHelper(this); this.modifiers = new ModifierHelper(this);
this.field = new FieldHelper(this); this.field = new FieldHelper(this);
this.initDefaultOverrides();
// TODO: remove `any` assertion
global.fetch = vi.fn(MockFetch) as any;
}
/** Reset a prior `BattleScene` instance to the proper initial state. */
private resetScene(): void {
this.scene.reset(false, true);
(this.scene.ui.handlers[UiMode.STARTER_SELECT] as StarterSelectUiHandler).clearStarterPreferences();
this.gameWrapper.scene = this.scene;
this.scene.phaseManager.toTitleScreen(true);
this.scene.phaseManager.shiftPhase();
}
/**
* Initialize various default overrides for starting tests, typically to alleviate randomness.
*/
// TODO: This should not be here
private initDefaultOverrides(): void {
// Disables Mystery Encounters on all tests (can be overridden at test level) // Disables Mystery Encounters on all tests (can be overridden at test level)
this.override.mysteryEncounterChance(0); this.override.mysteryEncounterChance(0);
global.fetch = vi.fn(MockFetch) as any;
} }
/** /**
@ -141,15 +153,13 @@ export class GameManager {
* @param mode - The mode to wait for. * @param mode - The mode to wait for.
* @returns A promise that resolves when the mode is set. * @returns A promise that resolves when the mode is set.
*/ */
waitMode(mode: UiMode): Promise<void> { // TODO: This is unused
return new Promise(async resolve => { async waitMode(mode: UiMode): Promise<void> {
await waitUntil(() => this.scene.ui?.getMode() === mode); await vi.waitUntil(() => this.scene.ui?.getMode() === mode);
return resolve();
});
} }
/** /**
* Ends the current phase. * End the currently running phase immediately.
*/ */
endPhase() { endPhase() {
this.scene.phaseManager.getCurrentPhase()?.end(); this.scene.phaseManager.getCurrentPhase()?.end();
@ -283,11 +293,14 @@ export class GameManager {
.getPokemon() .getPokemon()
.getMoveset() .getMoveset()
[movePosition].getMove(); [movePosition].getMove();
if (!move.isMultiTarget()) {
handler.setCursor(targetIndex !== undefined ? targetIndex : BattlerIndex.ENEMY); // Multi target attacks do not select a target
} if (move.isMultiTarget()) {
if (move.isMultiTarget() && targetIndex !== undefined) { if (targetIndex !== undefined) {
expect.fail(`targetIndex was passed to selectMove() but move ("${move.name}") is not targetted`); expect.fail(`targetIndex was passed to selectMove() but move ("${move.name}") is not targeted`);
}
} else {
handler.setCursor(targetIndex ?? BattlerIndex.ENEMY);
} }
handler.processInput(Button.ACTION); handler.processInput(Button.ACTION);
}, },