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> = 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.onEnd(() => { this.emit('videoEnd', scene) this.onVideoEnd(scene) }) this.videoManager.play(scene.videoUrl) this.emit('sceneChange', 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() } }