62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
import type { GameData, SceneNode, ChapterInfo, Choice, Condition } from '../types'
|
|
|
|
export class SceneManager {
|
|
private scenes: Record<string, SceneNode> = {}
|
|
private startScene: string = ''
|
|
chapters: ChapterInfo[] = []
|
|
|
|
load(data: GameData) {
|
|
this.scenes = data.scenes
|
|
this.startScene = data.startScene
|
|
this.chapters = data.chapters || []
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
getChapterBySceneId(sceneId: string): ChapterInfo | undefined {
|
|
return this.chapters.find((ch) => ch.startScene === sceneId)
|
|
}
|
|
|
|
getChapter(chapterId: string): ChapterInfo | undefined {
|
|
return this.chapters.find((ch) => ch.id === chapterId)
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|