Files
tianshu-engine/engine/core/SceneManager.ts
cocos02 937e45c203 feat: P1 core - seamless video switching, conditional branches, save/load
- 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
2026-06-07 16:48:52 +08:00

52 lines
1.4 KiB
TypeScript

import type { GameData, SceneNode, Choice, Condition } from '../types'
export class SceneManager {
private scenes: Record<string, SceneNode> = {}
private startScene: string = ''
load(data: GameData) {
this.scenes = data.scenes
this.startScene = data.startScene
}
getScene(id: string): SceneNode | undefined {
return this.scenes[id]
}
getStartScene(): SceneNode {
const scene = this.scenes[this.startScene]
if (!scene) throw new Error(`Start scene "${this.startScene}" not found`)
return scene
}
getAllSceneIds(): string[] {
return Object.keys(this.scenes)
}
getCandidateTargetIds(scene: SceneNode, evaluateCondition: (conds?: Condition[]) => boolean): string[] {
const targets: string[] = []
if (scene.choices) {
for (const choice of scene.choices) {
if (!choice.conditions || evaluateCondition(choice.conditions)) {
if (!targets.includes(choice.targetScene)) {
targets.push(choice.targetScene)
}
}
}
}
if (scene.nextScene && !targets.includes(scene.nextScene)) {
targets.push(scene.nextScene)
}
return targets
}
getCandidateUrls(scene: SceneNode, evaluateCondition: (conds?: Condition[]) => boolean): string[] {
return this.getCandidateTargetIds(scene, evaluateCondition)
.map(id => this.scenes[id]?.videoUrl)
.filter((url): url is string => !!url)
}
}