refactor for shared states and multi chapters

This commit is contained in:
Dan 2026-01-22 11:03:05 +00:00
parent 691b809b41
commit b9bea16f78
13 changed files with 2308 additions and 1786 deletions

View file

@ -5,12 +5,17 @@ class GameEngine {
this.terminal = null;
this.adapter = null;
this.state = null;
this.sharedState = null; // For series games
this.input = null;
this.sound = null;
this.scenes = null;
this.isRunning = false;
this.originalExecuteCommand = null;
// Series support
this.seriesId = gameDefinition.seriesId || null;
this.chapterNumber = gameDefinition.chapterNumber || null;
}
// Register this game as a terminal command
@ -35,6 +40,10 @@ class GameEngine {
self._reset();
return;
}
if (args[0] === "reset-series" && self.seriesId) {
self._resetSeries();
return;
}
if (args[0] === "continue" || args[0] === "resume") {
await self.start(true);
return;
@ -51,6 +60,23 @@ class GameEngine {
return;
}
// Initialize shared state for series games (do this early for chapter check)
if (this.seriesId && window.SharedStateManager) {
this.sharedState = new SharedStateManager(this.seriesId);
this.sharedState.init(this.definition.sharedStateDefaults || {});
// Check if this chapter can be played (sequential unlock)
if (this.chapterNumber && this.chapterNumber > 1) {
if (!this.sharedState.canPlayChapter(this.chapterNumber)) {
const prevChapter = this.chapterNumber - 1;
this.terminal.printError(
`You must complete Chapter ${prevChapter} before playing Chapter ${this.chapterNumber}.`,
);
return;
}
}
}
// Update the global HTML to add the game in progress details
document.body.classList.add("game-in-progress");
console.log(document.body.classList);
@ -59,7 +85,7 @@ class GameEngine {
// Initialize components
this.adapter = new TerminalAdapter(this.terminal);
this.state = new StateManager(this.definition.id);
this.state = new StateManager(this.definition.id, this.sharedState);
this.input = new InputManager(this.adapter);
// Initialize sound manager if SoundManager is available
@ -77,8 +103,11 @@ class GameEngine {
// Initialize state
this.state.init(this.definition.initialState || {});
// Load and merge external scenes if defined
const mergedScenes = this._getMergedScenes();
// Register scenes
this.scenes.registerScenes(this.definition.scenes);
this.scenes.registerScenes(mergedScenes);
// Hook into terminal input
this._hookInput();
@ -236,6 +265,66 @@ class GameEngine {
);
}
// Reset entire series progress (for series games)
_resetSeries() {
// Reset chapter state
this._reset();
// Reset shared state
if (this.seriesId) {
const sharedState = new SharedStateManager(this.seriesId);
sharedState.reset();
this.terminal.printSuccess(
`All ${this.definition.name} series progress has been reset.`,
);
}
}
// Create context for scene factories
_createSceneContext() {
return {
chapterNumber: this.chapterNumber || 1,
seriesId: this.seriesId,
sharedState: this.sharedState,
art: this.definition.art || {},
additionalOptions: this.definition.additionalHubOptions || [],
};
}
// Load external scenes and merge with inline scenes
_getMergedScenes() {
const inlineScenes = this.definition.scenes || {};
// If no external scenes defined, just return inline scenes
if (
!this.definition.externalScenes ||
this.definition.externalScenes.length === 0
) {
return inlineScenes;
}
// Load external scenes from registered factories
const externalScenes = {};
const context = this._createSceneContext();
for (const sceneRef of this.definition.externalScenes) {
const factory = window.SceneFactories?.[sceneRef];
if (factory) {
try {
const scenes = factory(context);
Object.assign(externalScenes, scenes);
} catch (e) {
console.error(`Failed to load external scenes from ${sceneRef}:`, e);
}
} else {
console.warn(`Scene factory not found: ${sceneRef}`);
}
}
// Inline scenes override external scenes (allows chapter-specific overrides)
return { ...externalScenes, ...inlineScenes };
}
// Debug command to skip to a specific scene
async _debugGoToScene(sceneName) {
const scene = this.scenes.getScene(sceneName);