Previously onTimeCallback was a single nullable function pointer, so
registerEvents()'s store.setVideoTime callback was overwritten by
goToScene()'s checkQTE callback, causing videoTime to stay at 0.
Changed to a Set to support multiple concurrent listeners.
- StateManager.dump(): console.table formatted dump of variables, flags, history
- window.__sm: exposes engine.stateManager in dev mode for console inspection
- VideoManager: A/B dual-buffered video with crossfade transitions and candidate preloading
- Engine: condition-based choice filtering, ChoiceSystem timer, resumeScene for save/load
- SceneManager: getCandidateUrls for preloading next scenes
- SaveSystem: Dexie.js IndexedDB multi-slot save/load
- ChoiceSystem: timed choices with countdown and auto-default on timeout
- GamePlayer: dual video elements with crossfade CSS
- ChoicePanel: timer progress bar and countdown text
- SaveLoadMenu: save/load UI component
- App.vue: menu trigger, dual video refs, save/load integration
- gameStore: timer state, saves list
- demo.json: conditional choice example (secret ending, requires trust >= 80)
- ROADMAP: mark P1 as completed
- Reorder onEnd callback before play() in Engine.goToScene to prevent
missed ended event if video ends synchronously
- Wait for loadedmetadata event in VideoManager.play() before seeking
to ensure currentTime reset works correctly on new video sources