Checkpoint before line-grid scrolling refactor

This commit is contained in:
2026-05-16 13:44:03 +02:00
parent 42582352d6
commit fe33e4f0ab
25 changed files with 1989 additions and 840 deletions
+55 -2
View File
@@ -31,6 +31,7 @@ class GameLoopModule extends BaseModule {
'updateUIState',
'refreshGameApiState',
'hasSaveGame',
'queueUnrenderedHistoryBlocks',
'requestStartGame',
'requestSaveGame',
'requestLoadGame',
@@ -217,9 +218,13 @@ class GameLoopModule extends BaseModule {
const response = await socketClient.saveGame(1);
if (response?.success) {
const storyHistory = this.getModule('story-history');
const audioManager = this.getModule('audio-manager');
if (storyHistory && typeof storyHistory.saveSlot === 'function') {
await storyHistory.saveSlot(1, {
inkState: response.savedState || null
inkState: response.savedState || null,
latestRenderedBlockId: storyHistory.latestRenderedBlockId || 0,
renderedLineCount: storyHistory.renderedLineCount || 0,
musicState: audioManager?.getMusicState?.() || null
});
}
this.gameState.canLoad = true;
@@ -246,20 +251,57 @@ class GameLoopModule extends BaseModule {
}
await this.resetClientPlaybackAndDisplay();
document.dispatchEvent(new CustomEvent('story:history-restoring', {
detail: { active: true, reason: 'load-game' }
}));
if (browserSave?.gameId && storyHistory?.setCurrentGame) {
storyHistory.setCurrentGame(browserSave.gameId, browserSave.latestBlockId || 0);
storyHistory.setCurrentGame(
browserSave.gameId,
browserSave.latestBlockId || 0,
browserSave.latestRenderedBlockId || 0,
browserSave.renderedLineCount || 0
);
}
const uiController = this.getModule('ui-controller');
if (browserSave && uiController?.displayHandler?.restoreFromHistory) {
await uiController.displayHandler.restoreFromHistory(browserSave);
}
const audioManager = this.getModule('audio-manager');
if (browserSave?.musicState && audioManager?.restoreMusicState) {
await audioManager.restoreMusicState(browserSave.musicState);
}
const hasUnrenderedHistory = browserSave &&
Number(browserSave.latestBlockId || 0) > Number(browserSave.latestRenderedBlockId || 0);
if (hasUnrenderedHistory) {
const sentenceQueue = this.getModule('sentence-queue');
sentenceQueue?.pauseBeforeNext?.('load-resume');
document.dispatchEvent(new CustomEvent('story:process-state', {
detail: { state: 'waiting-generating', reason: 'restoring-pending-output' }
}));
}
const response = await socketClient.loadGame(1, browserSave?.inkState || null);
if (response?.success && hasUnrenderedHistory) {
await this.queueUnrenderedHistoryBlocks(browserSave);
}
if (response?.success) {
this.gameState.started = true;
this.gameState.canSave = true;
this.gameState.canLoad = true;
this.updateUIState();
}
if (!hasUnrenderedHistory) {
document.dispatchEvent(new CustomEvent('story:history-restoring', {
detail: { active: false, reason: 'load-game-complete' }
}));
} else {
const clearRestoring = () => {
document.dispatchEvent(new CustomEvent('story:history-restoring', {
detail: { active: false, reason: 'pending-output-drained' }
}));
document.removeEventListener('tts:queue-empty', clearRestoring);
};
document.addEventListener('tts:queue-empty', clearRestoring);
}
}
async hasSaveGame(slot = 1) {
@@ -272,6 +314,17 @@ class GameLoopModule extends BaseModule {
return socketClient?.hasSaveGame ? socketClient.hasSaveGame(slot) : { success: false, result: false };
}
async queueUnrenderedHistoryBlocks(saveRecord = {}) {
const storyHistory = this.getModule('story-history');
const textBuffer = this.getModule('text-buffer');
if (!storyHistory || !textBuffer || typeof textBuffer.addBlocks !== 'function') return;
const start = Math.max(1, Number(saveRecord.latestRenderedBlockId || 0) + 1);
const end = Math.max(0, Number(saveRecord.latestBlockId || 0));
if (end < start) return;
const blocks = await storyHistory.getBlocksRange(saveRecord.gameId, start, end);
textBuffer.addBlocks(blocks);
}
async resetClientPlaybackAndDisplay() {
const playbackCoordinator = this.getModule('playback-coordinator');
if (playbackCoordinator && typeof playbackCoordinator.stop === 'function') {