You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
145 lines
4.3 KiB
145 lines
4.3 KiB
import { BaseScene } from "./scene/BaseScene";
|
|
import type { IBaseScene } from "./scene/types";
|
|
import { SceneType } from "./enums/SceneType";
|
|
import { assetManager } from "./core/AssetManager";
|
|
import { logger } from "./core/Logger";
|
|
import { Container } from "pixi.js";
|
|
import { appRuntime } from "./kernel/AppRuntime";
|
|
import type { RuntimePlugin } from "./kernel/RuntimePlugin";
|
|
|
|
const game = appRuntime.game;
|
|
const sceneManager = appRuntime.sceneManager;
|
|
const eventBus = appRuntime.events;
|
|
|
|
const runtimeSceneEventsPlugin: RuntimePlugin = {
|
|
name: "runtime-scene-events",
|
|
setup: ({ game: runtimeGame, sceneManager: runtimeSceneManager, events }) => {
|
|
runtimeGame.setAfterFrameRenderHook(() => {
|
|
// Keep legacy event name, but only emit from frame loop.
|
|
events.emit("game:rendered");
|
|
events.emit("game:frame-rendered");
|
|
});
|
|
runtimeSceneManager.onStageChange((current, previous) => {
|
|
events.emit("scene:changed", {
|
|
current: current.name,
|
|
previous: previous?.name,
|
|
});
|
|
});
|
|
},
|
|
};
|
|
|
|
type Constructor<T> = new (...args: unknown[]) => T;
|
|
|
|
export async function initApp(): Promise<void> {
|
|
appRuntime.use(runtimeSceneEventsPlugin);
|
|
await assetManager.init();
|
|
await game.init();
|
|
|
|
const sceneModules = import.meta.glob("./stages/**/page_*.ts", { eager: true });
|
|
|
|
for (const path in sceneModules) {
|
|
try {
|
|
const mod = sceneModules[path];
|
|
const match = path.match(/page_(.*?)\.ts$/);
|
|
if (!match) continue;
|
|
const fileSceneName = match[1];
|
|
|
|
const raw = (mod as { default: unknown }).default;
|
|
if (typeof raw !== "function") {
|
|
logger.warn(`initApp: invalid scene file ${path}, expected default class export, skipping`);
|
|
continue;
|
|
}
|
|
|
|
const SceneCtor = raw as Constructor<IBaseScene & Record<string, unknown>>;
|
|
const scene = new SceneCtor();
|
|
|
|
if (!scene || typeof scene !== "object" || !("stage" in scene)) {
|
|
logger.warn(`initApp: invalid scene file ${path}, skipping`);
|
|
continue;
|
|
}
|
|
|
|
const legacy = scene as IBaseScene &
|
|
Record<string, unknown> & { name?: string; type?: SceneType };
|
|
const mutable = legacy as { name?: string; type?: SceneType; stage: Container | null };
|
|
if (!mutable.name) {
|
|
mutable.name = fileSceneName;
|
|
}
|
|
if (mutable.type === undefined) {
|
|
mutable.type = SceneType.Normal;
|
|
}
|
|
if (!mutable.stage) {
|
|
mutable.stage = new Container();
|
|
}
|
|
|
|
sceneManager.registerScene(legacy as IBaseScene);
|
|
} catch (error) {
|
|
logger.error(`initApp: failed to load scene file ${path}`, error);
|
|
}
|
|
}
|
|
|
|
game.ticker.add((ticker) => {
|
|
const dt = ticker.deltaTime;
|
|
const current = sceneManager.currentScene;
|
|
|
|
for (const scene of sceneManager.getAllScenes()) {
|
|
if (scene.type === SceneType.Resident && scene.stage.visible) {
|
|
scene.update?.(dt, scene.name, ticker);
|
|
}
|
|
}
|
|
|
|
if (current) {
|
|
current.update?.(dt, current.name, ticker);
|
|
}
|
|
|
|
game.render("frame");
|
|
|
|
for (const scene of sceneManager.getAllScenes()) {
|
|
if (scene.type === SceneType.Resident && scene.stage.visible && scene.lateUpdate) {
|
|
scene.lateUpdate(dt, scene.name, ticker);
|
|
}
|
|
}
|
|
|
|
if (current?.lateUpdate) {
|
|
current.lateUpdate(dt, current.name, ticker);
|
|
}
|
|
});
|
|
|
|
const residentScenes: BaseScene[] = [];
|
|
for (const scene of sceneManager.getAllScenes()) {
|
|
if (scene.type === SceneType.Resident) {
|
|
residentScenes.push(scene as BaseScene);
|
|
}
|
|
}
|
|
|
|
for (const scene of residentScenes) {
|
|
try {
|
|
if (!scene._assetsLoaded) {
|
|
await scene.loadBundle?.();
|
|
scene._assetsLoaded = true;
|
|
}
|
|
if (!scene._layoutDone) {
|
|
await scene.layout?.();
|
|
scene._layoutDone = true;
|
|
}
|
|
await scene.onLoad?.();
|
|
} catch (error) {
|
|
logger.error(`initApp: failed to initialize resident scene ${scene.name}`, error);
|
|
}
|
|
}
|
|
|
|
const entryScene = "init";
|
|
if (sceneManager.hasScene(entryScene)) {
|
|
await sceneManager.initScene(entryScene);
|
|
} else {
|
|
logger.error(`initApp: entry scene "${entryScene}" not found`);
|
|
}
|
|
|
|
sceneManager.onStageChange((current) => {
|
|
logger.debug("Scene changed to", current.name);
|
|
});
|
|
|
|
logger.info("App initialized");
|
|
appRuntime.markReady();
|
|
}
|
|
|
|
export { game, sceneManager, eventBus, assetManager, logger };
|
|
|