feat: editor services, stores, and graph improvements
This commit is contained in:
132
editor/stores/editorStore.ts
Normal file
132
editor/stores/editorStore.ts
Normal file
@@ -0,0 +1,132 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import { shallowRef, ref, computed, triggerRef } from 'vue'
|
||||
import type { GameData, SceneNode, Choice } from '@engine/types'
|
||||
|
||||
export const useEditorStore = defineStore('editor', () => {
|
||||
const gameData = shallowRef<GameData>({ scenes: {}, startScene: '', variables: {} })
|
||||
const selectedNodeId = ref<string | null>(null)
|
||||
const startSceneId = ref('')
|
||||
const dirty = ref(false)
|
||||
|
||||
const selectedScene = computed(() => {
|
||||
if (!selectedNodeId.value) return null
|
||||
return gameData.value.scenes[selectedNodeId.value] ?? null
|
||||
})
|
||||
|
||||
function markDirty() { dirty.value = true }
|
||||
|
||||
function loadJSON(json: GameData) {
|
||||
gameData.value = JSON.parse(JSON.stringify(json))
|
||||
triggerRef(gameData)
|
||||
startSceneId.value = json.startScene || ''
|
||||
selectedNodeId.value = null
|
||||
dirty.value = false
|
||||
}
|
||||
|
||||
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 = {
|
||||
...gameData.value,
|
||||
scenes: {
|
||||
...gameData.value.scenes,
|
||||
[id]: { id, videoUrl: '', choices: [], nextScene: '', subtitleUrl: '', onEnter: [] },
|
||||
},
|
||||
}
|
||||
triggerRef(gameData)
|
||||
dirty.value = true
|
||||
return id
|
||||
}
|
||||
|
||||
function deleteScene(id: string) {
|
||||
if (startSceneId.value === id) return
|
||||
const nextScenes = { ...gameData.value.scenes }
|
||||
delete nextScenes[id]
|
||||
for (const key of Object.keys(nextScenes)) {
|
||||
const s = nextScenes[key]
|
||||
if (s.choices) nextScenes[key] = { ...s, choices: s.choices.filter((c) => c.targetScene !== id) }
|
||||
if (Array.isArray(s.nextScene)) {
|
||||
nextScenes[key] = { ...s, nextScene: s.nextScene.filter((r) => r.targetScene !== id) }
|
||||
} else if (s.nextScene === id) {
|
||||
nextScenes[key] = { ...nextScenes[key], nextScene: '' }
|
||||
}
|
||||
if (s.qte) {
|
||||
const qte = { ...s.qte }
|
||||
let changed = false
|
||||
if (qte.successScene === id) { qte.successScene = ''; changed = true }
|
||||
if (qte.failScene === id) { qte.failScene = ''; changed = true }
|
||||
if (changed) nextScenes[key] = { ...nextScenes[key], qte }
|
||||
}
|
||||
}
|
||||
gameData.value = { ...gameData.value, scenes: nextScenes }
|
||||
triggerRef(gameData)
|
||||
dirty.value = true
|
||||
if (selectedNodeId.value === id) selectedNodeId.value = null
|
||||
}
|
||||
|
||||
function updateScene(id: string, partial: Partial<SceneNode>) {
|
||||
const scene = gameData.value.scenes[id]
|
||||
if (!scene) return
|
||||
gameData.value = {
|
||||
...gameData.value,
|
||||
scenes: { ...gameData.value.scenes, [id]: { ...scene, ...partial } },
|
||||
}
|
||||
triggerRef(gameData)
|
||||
dirty.value = true
|
||||
}
|
||||
|
||||
function addChoice(sourceId: string) {
|
||||
const scene = gameData.value.scenes[sourceId]
|
||||
if (!scene) return
|
||||
gameData.value = {
|
||||
...gameData.value,
|
||||
scenes: {
|
||||
...gameData.value.scenes,
|
||||
[sourceId]: { ...scene, choices: [...(scene.choices || []), { text: '\u65b0\u9009\u9879', targetScene: '' }] },
|
||||
},
|
||||
}
|
||||
triggerRef(gameData)
|
||||
dirty.value = true
|
||||
}
|
||||
|
||||
function updateChoice(sourceId: string, index: number, partial: Partial<Choice>) {
|
||||
const scene = gameData.value.scenes[sourceId]
|
||||
if (!scene?.choices) return
|
||||
const newChoices = scene.choices.map((c, i) => (i === index ? { ...c, ...partial } : c))
|
||||
gameData.value = {
|
||||
...gameData.value,
|
||||
scenes: { ...gameData.value.scenes, [sourceId]: { ...scene, choices: newChoices } },
|
||||
}
|
||||
triggerRef(gameData)
|
||||
dirty.value = true
|
||||
}
|
||||
|
||||
function deleteChoice(sourceId: string, index: number) {
|
||||
const scene = gameData.value.scenes[sourceId]
|
||||
if (!scene?.choices) return
|
||||
gameData.value = {
|
||||
...gameData.value,
|
||||
scenes: {
|
||||
...gameData.value.scenes,
|
||||
[sourceId]: { ...scene, choices: scene.choices.filter((_, i) => i !== index) },
|
||||
},
|
||||
}
|
||||
triggerRef(gameData)
|
||||
dirty.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
gameData, selectedNodeId, selectedScene, startSceneId, dirty,
|
||||
markDirty, loadJSON, exportJSON, addScene, deleteScene,
|
||||
updateScene, addChoice, updateChoice, deleteChoice, generateId,
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user