import { ref, computed } from 'vue' import type { GameData, SceneNode, Choice } from '@engine/types' export interface EditorNode { id: string label: string videoUrl: string subtitleUrl: string choices: Choice[] nextScene: string onEnter: any[] qte: any | null } export function useGraphEditor() { const gameData = ref({ scenes: {}, startScene: '', variables: {} }) const selectedNodeId = ref(null) const startSceneId = ref('') const selectedNode = computed(() => { if (!selectedNodeId.value) return null return gameData.value.scenes[selectedNodeId.value] ?? null }) const sceneList = computed(() => { return Object.values(gameData.value.scenes).map((s) => ({ id: s.id, label: s.id, })) }) function loadJSON(json: GameData) { gameData.value = JSON.parse(JSON.stringify(json)) startSceneId.value = json.startScene } function exportJSON(): GameData { return JSON.parse(JSON.stringify({ ...gameData.value, startScene: startSceneId.value })) } function generateId(): string { let i = Object.keys(gameData.value.scenes).length + 1 while (gameData.value.scenes[`scene_${i}`]) i++ return `scene_${i}` } function addScene(): string { const id = generateId() gameData.value.scenes[id] = { id, videoUrl: '', choices: [], nextScene: '', subtitleUrl: '', onEnter: [], } return id } function deleteScene(id: string) { if (startSceneId.value === id) return delete gameData.value.scenes[id] for (const s of Object.values(gameData.value.scenes)) { s.choices = (s.choices || []).filter((c) => c.targetScene !== id) if (s.nextScene === id) s.nextScene = '' } if (selectedNodeId.value === id) selectedNodeId.value = null } function updateScene(id: string, partial: Partial) { const scene = gameData.value.scenes[id] if (!scene) return Object.assign(scene, partial) gameData.value.scenes = { ...gameData.value.scenes } } function addChoice(sourceId: string) { const scene = gameData.value.scenes[sourceId] if (!scene) return if (!scene.choices) scene.choices = [] scene.choices.push({ text: '新选项', targetScene: '', }) gameData.value.scenes = { ...gameData.value.scenes } } function updateChoice(sourceId: string, index: number, partial: Partial) { const scene = gameData.value.scenes[sourceId] if (!scene?.choices) return Object.assign(scene.choices[index], partial) gameData.value.scenes = { ...gameData.value.scenes } } function deleteChoice(sourceId: string, index: number) { const scene = gameData.value.scenes[sourceId] if (!scene?.choices) return scene.choices.splice(index, 1) gameData.value.scenes = { ...gameData.value.scenes } } function newSceneData(): EditorNode { return { id: '', label: '', videoUrl: '', subtitleUrl: '', choices: [], nextScene: '', onEnter: [], qte: null, } } return { gameData, selectedNodeId, selectedNode, sceneList, startSceneId, loadJSON, exportJSON, addScene, deleteScene, updateScene, addChoice, updateChoice, deleteChoice, newSceneData, generateId, } }