Reset per-game reveal state on new game so reveal animates over cached content
Starting a new game reuses block ids (1,2,3...). The reveal clock's per-block start times (activeRevealBlockStarts in the lab) and the renderer's animation/ revealed sets are keyed by block id and were never cleared on a client reset, so a new game over already-cached content inherited the previous run's start times. beginPageReveal then computed a huge elapsed and the shader treated the reveal as already complete — showing everything at once instead of animating. resetClientPlaybackAndDisplay (run on new game and restore) now emits story:client-reset; the lab clears activeRevealBlockStarts/pending reveal state, the texture renderer clears active animations and revealed-block ids, and the timeline invalidates prepared segments. So each game starts with a clean reveal clock. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -99,6 +99,10 @@ class BookPlaybackTimelineModule extends BaseModule {
|
|||||||
});
|
});
|
||||||
this.addEventListener(document, 'webgl-book:page-count-changed', this.invalidatePreparedSegments);
|
this.addEventListener(document, 'webgl-book:page-count-changed', this.invalidatePreparedSegments);
|
||||||
this.addEventListener(document, 'story:history-restoring', this.invalidatePreparedSegments);
|
this.addEventListener(document, 'story:history-restoring', this.invalidatePreparedSegments);
|
||||||
|
this.addEventListener(document, 'story:client-reset', () => {
|
||||||
|
this.invalidatePreparedSegments();
|
||||||
|
this.activeSegment = null;
|
||||||
|
});
|
||||||
window.BookPlaybackTimeline = this;
|
window.BookPlaybackTimeline = this;
|
||||||
this.reportProgress(100, 'Book playback timeline ready');
|
this.reportProgress(100, 'Book playback timeline ready');
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ class BookTextureRendererModule extends BaseModule {
|
|||||||
});
|
});
|
||||||
this.addEventListener(document, 'story:manual-scroll', this.fastForwardAnimations);
|
this.addEventListener(document, 'story:manual-scroll', this.fastForwardAnimations);
|
||||||
this.addEventListener(document, 'story:history-restoring', this.stopAnimations);
|
this.addEventListener(document, 'story:history-restoring', this.stopAnimations);
|
||||||
|
this.addEventListener(document, 'story:client-reset', this.stopAnimations);
|
||||||
this.currentSpread = this.pagination?.getCurrentSpread?.() || { index: 0, left: [], right: [], pageMeta: { left: null, right: null } };
|
this.currentSpread = this.pagination?.getCurrentSpread?.() || { index: 0, left: [], right: [], pageMeta: { left: null, right: null } };
|
||||||
this.drawSpread(this.currentSpread);
|
this.drawSpread(this.currentSpread);
|
||||||
this.reportProgress(100, 'Book texture renderer ready');
|
this.reportProgress(100, 'Book texture renderer ready');
|
||||||
@@ -1028,6 +1029,7 @@ class BookTextureRendererModule extends BaseModule {
|
|||||||
|
|
||||||
stopAnimations() {
|
stopAnimations() {
|
||||||
this.activeAnimations.clear();
|
this.activeAnimations.clear();
|
||||||
|
this.revealedBlockIds.clear();
|
||||||
this.drawSpread(this.currentSpread || this.pagination?.getCurrentSpread?.());
|
this.drawSpread(this.currentSpread || this.pagination?.getCurrentSpread?.());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -657,8 +657,15 @@ class GameLoopModule extends BaseModule {
|
|||||||
if (inputHandler && typeof inputHandler.clearHistory === 'function') {
|
if (inputHandler && typeof inputHandler.clearHistory === 'function') {
|
||||||
inputHandler.clearHistory();
|
inputHandler.clearHistory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signal a client reset so transient, block-id-keyed reveal/animation state is
|
||||||
|
// cleared. Without this, a new game that reuses block ids over already-cached
|
||||||
|
// content keeps the previous run's reveal start times and skips the animation.
|
||||||
|
document.dispatchEvent(new CustomEvent('story:client-reset', {
|
||||||
|
detail: { reason: 'client-reset' }
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the singleton instance
|
// Create the singleton instance
|
||||||
|
|||||||
@@ -695,6 +695,15 @@ document.addEventListener('webgl-book:request-page-flip', (event) => {
|
|||||||
document.addEventListener('webgl-book:page-cache-problem', (event) => {
|
document.addEventListener('webgl-book:page-cache-problem', (event) => {
|
||||||
pageTextureStore?.recordProblem?.(event.detail || {});
|
pageTextureStore?.recordProblem?.(event.detail || {});
|
||||||
});
|
});
|
||||||
|
// New game / history restore: drop block-id-keyed reveal state so a reused block id does
|
||||||
|
// not inherit the previous run's reveal start time (which would skip the animation).
|
||||||
|
document.addEventListener('story:client-reset', () => {
|
||||||
|
activeRevealBlockStarts.clear();
|
||||||
|
pendingRevealStartBlockIds.clear();
|
||||||
|
pageRevealFreezeAt = null;
|
||||||
|
clearPageReveal('left', 'client-reset');
|
||||||
|
clearPageReveal('right', 'client-reset');
|
||||||
|
});
|
||||||
// Pagination spread updates only carry state. The playback owner decides when the
|
// Pagination spread updates only carry state. The playback owner decides when the
|
||||||
// visible spread changes (via flips). The scene jumps directly only for non-playback
|
// visible spread changes (via flips). The scene jumps directly only for non-playback
|
||||||
// commits such as history restore. See docs/webgl-3d-ui-spec.md "Single ownership".
|
// commits such as history restore. See docs/webgl-3d-ui-spec.md "Single ownership".
|
||||||
|
|||||||
Reference in New Issue
Block a user