feat: chapter select system, multi-chapter support, scene manager refactor, and docs update
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { SceneNode, Choice, EngineEvent, Hotspot } from '../types'
|
||||
import type { SceneNode, Choice, EngineEvent, Hotspot, ChapterInfo } from '../types'
|
||||
import { SceneManager } from './SceneManager'
|
||||
import { VideoManager } from './VideoManager'
|
||||
import { StateManager } from './StateManager'
|
||||
@@ -24,6 +24,11 @@ export class Engine {
|
||||
private qteResolved = false
|
||||
private justCameFromImage = false
|
||||
private loopActive = false
|
||||
private onUnlockChapter: ((chapterId: string) => void) | null = null
|
||||
|
||||
setChapterUnlockHandler(handler: (chapterId: string) => void) {
|
||||
this.onUnlockChapter = handler
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.sceneManager = new SceneManager()
|
||||
@@ -62,6 +67,12 @@ export class Engine {
|
||||
this.qteResolved = false
|
||||
this.loopActive = false
|
||||
|
||||
const chapter = this.sceneManager.getChapterBySceneId(scene.id)
|
||||
if (chapter) {
|
||||
this.onUnlockChapter?.(chapter.id)
|
||||
this.emit('chapterUnlock', chapter)
|
||||
}
|
||||
|
||||
if (scene.onEnter) {
|
||||
this.stateManager.apply(scene.onEnter)
|
||||
}
|
||||
@@ -333,6 +344,29 @@ export class Engine {
|
||||
this.emit('gameEnd')
|
||||
}
|
||||
|
||||
startChapter(chapterId: string) {
|
||||
const chapter = this.sceneManager.getChapter(chapterId)
|
||||
if (!chapter) return
|
||||
|
||||
const scene = this.sceneManager.getScene(chapter.startScene)
|
||||
if (!scene) return
|
||||
|
||||
const defaultVars = chapter.defaultVariables
|
||||
if (defaultVars) {
|
||||
this.stateManager.variables = { ...defaultVars }
|
||||
} else {
|
||||
this.stateManager.init(this.sceneManager.chapters.length > 0
|
||||
? {} // from chapters, use the chapter's defaultVariables or empty
|
||||
: {})
|
||||
}
|
||||
this.stateManager.flags = new Set()
|
||||
this.stateManager.history = []
|
||||
|
||||
this.ended = false
|
||||
this.isInitialScene = false
|
||||
this.goToScene(scene)
|
||||
}
|
||||
|
||||
resumeScene(sceneId: string, savedState: { variables: Record<string, number>; flags: string[]; history: any[] }) {
|
||||
this.stateManager.variables = { ...savedState.variables }
|
||||
this.stateManager.flags = new Set(savedState.flags)
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import type { GameData, SceneNode, Choice, Condition } from '../types'
|
||||
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 {
|
||||
@@ -23,6 +25,14 @@ export class SceneManager {
|
||||
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[] = []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user