import { onUnmounted } from 'vue' import { Engine } from '@engine/core/Engine' import { SaveSystem } from '@engine/systems/SaveSystem' import type { GameData } from '@engine/types' import { useGameStore } from '@/stores/gameStore' export function useGameEngine(videoEls: () => [HTMLVideoElement | null, HTMLVideoElement | null]) { const engine = new Engine() const saveSystem = new SaveSystem() const store = useGameStore() let lastThumbnail: string | undefined if (import.meta.env.DEV) { ;(window as any).__sm = engine.stateManager ;(window as any).__store = store } async function loadGame(dataUrl: string) { const resp = await fetch(dataUrl) const data: GameData = await resp.json() engine.sceneManager.load(data) engine.stateManager.init(data.variables) store.setChapters(data.chapters || []) const unlocked = await saveSystem.getUnlockedChapters() store.setUnlockedChapters(unlocked) } function registerEvents() { engine.setChapterUnlockHandler(async (chapterId) => { await saveSystem.unlockChapter(chapterId) store.addUnlockedChapter(chapterId) }) engine.on('sceneChange', (scene) => { store.setScene(scene) store.clearChoices() store.clearTimer() store.clearHotspots() store.setIsImageScene(scene.type === 'image') saveGame(0) }) engine.on('choiceRequest', (choiceList) => { store.setChoices(choiceList) }) engine.on('choiceTimer', (timerState) => { store.setTimer(timerState.total, timerState.remaining) }) engine.on('choiceTimeout', () => { store.clearChoices() store.clearTimer() }) engine.on('hotspotRequest', (list) => { store.setHotspots(list) }) engine.on('hotspotUpdate', (list) => { store.setHotspots(list) }) engine.on('videoEnd', () => { try { const video = engine.videoManager.getActiveVideoElement() if (video && video.readyState >= 2) { const canvas = document.createElement('canvas') canvas.width = 320 canvas.height = 180 const ctx = canvas.getContext('2d') if (ctx) { ctx.drawImage(video, 0, 0, 320, 180) lastThumbnail = canvas.toDataURL('image/jpeg', 0.6) } } } catch { /* ignore */ } }) engine.on('gameEnd', () => { store.setGameEnded(true) engine.choiceSystem.stop() }) engine.on('qteTrigger', (qte) => { store.showQTE(qte) }) engine.on('qteTimer', ({ remaining }) => { store.updateQTE(remaining) }) engine.on('qteResult', ({ success }) => { store.resolveQTE(success) }) engine.videoManager.onTimeUpdate((t: number) => { store.setVideoTime(t) }) } function start() { const [elA, elB] = videoEls() if (elA && elB) engine.videoManager.attach(elA, elB) registerEvents() engine.start() } async function resumeAutoSave(): Promise { const [elA, elB] = videoEls() if (elA && elB) engine.videoManager.attach(elA, elB) registerEvents() return await loadGameFromSlot(0) } function makeChoice(index: number) { const scene = store.currentScene if (!scene?.choices) return engine.choiceSystem.stop() store.clearTimer() engine.makeChoice(scene.choices[index]) } function clickHotspot(hotspotId: string) { const scene = store.currentScene if (!scene?.hotspots) return const hs = scene.hotspots.find((h) => h.id === hotspotId) if (hs) { engine.clickHotspot(hs) } } function startChapter(chapterId: string) { const [elA, elB] = videoEls() if (elA && elB) engine.videoManager.attach(elA, elB) store.setGameEnded(false) engine.startChapter(chapterId) } async function saveGame(slot: number) { const state = engine.stateManager const currentScene = store.currentScene await saveSystem.save(slot, { timestamp: Date.now(), currentScene: currentScene?.id ?? '', variables: state.variables, flags: [...state.flags], history: state.history, thumbnail: lastThumbnail, }) await refreshSaves() } async function loadGameFromSlot(slot: number): Promise { const data = await saveSystem.load(slot) if (!data) return false store.setGameEnded(false) engine.resumeScene(data.currentScene, { variables: data.variables, flags: data.flags, history: data.history, }) return true } async function refreshSaves() { const list = await saveSystem.listSlots() store.setSaves(list) } function destroy() { engine.destroy() } onUnmounted(() => { destroy() }) return { loadGame, start, resumeAutoSave, makeChoice, clickHotspot, startChapter, saveGame, loadGameFromSlot, refreshSaves, engine, saveSystem } }