Files
tianshu-engine/engine/core/Engine.ts
2026-06-07 13:50:05 +08:00

104 lines
2.4 KiB
TypeScript

import type { SceneNode, Choice, EngineEvent } from '../types'
import { SceneManager } from './SceneManager'
import { VideoManager } from './VideoManager'
import { StateManager } from './StateManager'
type EventHandler = (...args: any[]) => void
export class Engine {
sceneManager: SceneManager
videoManager: VideoManager
stateManager: StateManager
private currentScene: SceneNode | null = null
private events: Map<EngineEvent, Set<EventHandler>> = new Map()
private ended: boolean = false
constructor() {
this.sceneManager = new SceneManager()
this.videoManager = new VideoManager()
this.stateManager = new StateManager()
}
on(event: EngineEvent, handler: EventHandler) {
if (!this.events.has(event)) this.events.set(event, new Set())
this.events.get(event)!.add(handler)
}
off(event: EngineEvent, handler: EventHandler) {
this.events.get(event)?.delete(handler)
}
private emit(event: EngineEvent, ...args: any[]) {
this.events.get(event)?.forEach((h) => h(...args))
}
start() {
this.ended = false
const startScene = this.sceneManager.getStartScene()
this.goToScene(startScene)
}
private goToScene(scene: SceneNode) {
this.currentScene = scene
if (scene.onEnter) {
this.stateManager.apply(scene.onEnter)
}
this.videoManager.play(scene.videoUrl)
this.emit('sceneChange', scene)
this.videoManager.onEnd(() => {
this.emit('videoEnd', scene)
this.onVideoEnd(scene)
})
}
private onVideoEnd(scene: SceneNode) {
if (scene.choices && scene.choices.length > 0) {
this.emit('choiceRequest', scene.choices)
} else if (scene.nextScene) {
const next = this.sceneManager.getScene(scene.nextScene)
if (next) {
this.goToScene(next)
} else {
this.endGame()
}
} else {
this.endGame()
}
}
makeChoice(choice: Choice) {
if (!this.currentScene) return
if (choice.effects) {
this.stateManager.apply(choice.effects)
}
this.stateManager.recordChoice({
sceneId: this.currentScene.id,
choiceIndex: this.currentScene.choices?.indexOf(choice) ?? -1,
choiceText: choice.text,
})
const next = this.sceneManager.getScene(choice.targetScene)
if (next) {
this.goToScene(next)
} else {
this.endGame()
}
}
endGame() {
this.ended = true
this.emit('gameEnd')
}
destroy() {
this.videoManager.detach()
this.events.clear()
}
}