# 剧情编辑器

基于 Vue Flow 的可视化交互式电影剧情编辑工具，通过拖拽节点和连线来编排游戏场景和分支。

## 启动

```bash
npm install
npm run dev
```

浏览器打开 `http://localhost:5173/editor/`

## 界面布局

```
┌──────────────────────────────────────────────────┐
│  工具栏: 新建 / 导入导出 / 加载示例 / 起始场景      │
├────────────────────┬──────────────┬───────────────┤
│                    │              │               │
│   场景节点图        │  属性编辑面板  │   视频预览     │
│   (Vue Flow)       │              │               │
│                    │              │               │
└────────────────────┴──────────────┴───────────────┘
```

## 基本操作

### 场景节点图（左侧）

- **点击节点** → 右侧面板打开该场景的属性编辑器
- **拖拽节点端口到另一个节点** → 创建"选项"连线（自动生成选项文本）
- **缩放/平移**：鼠标滚轮缩放，拖拽画布平移

### 新建场景

点击工具栏 `+ 新场景`，生成一个新节点并自动选中。

### 编辑场景（右侧面板选中节点后）

| 字段 | 说明 |
|------|------|
| 视频路径 | 该场景对应的视频文件，如 `/videos/scene_1.mp4` |
| 字幕路径 | 可选 WebVTT 字幕文件，如 `/subtitles/scene_1.vtt` |
| 默认下一场景 | 没有选项或选项超时后的自动跳转目标 |
| QTE | 快速反应事件：触发时间、提示文字、按键、限时、成功/失败跳转 |
| 选项列表 | 玩家可见的分支选项，包含文字、目标场景、限时 |

### 选项管理

- 点击 `+ 添加选项` 增加新选项
- 填写选项文字和目标场景
- 可设置限时（秒），超时自动选第一项
- 点 × 删除选项

### 删除场景

编辑器面板标题栏点击 🗑 按钮，会同时清理其他场景中指向该场景的引用。

## 数据管理

### 加载示例

点击 `加载示例` 载入 `public/scenes/demo.json`，了解数据格式。

### 导入 / 导出 JSON

- **导出**：将当前编辑的剧情导出为 JSON 文件
- **导入**：从 JSON 文件加载剧情数据

JSON 格式与游戏引擎的数据结构一致：

```json
{
  "startScene": "scene_1",
  "variables": {},
  "scenes": {
    "scene_1": {
      "id": "scene_1",
      "videoUrl": "/videos/scene_1.mp4",
      "choices": [
        { "text": "进入房间", "targetScene": "scene_2" }
      ],
      "nextScene": "",
      "subtitleUrl": "",
      "onEnter": []
    }
  }
}
```

导出的 JSON 可直接放入 `public/scenes/` 目录供游戏引擎加载。

## 场景数据结构

```ts
interface SceneNode {
  id: string              // 唯一标识
  videoUrl: string        // 视频文件路径
  subtitleUrl?: string    // 字幕文件路径（可选）
  choices?: Choice[]      // 选项列表
  qte?: QTEDefinition     // QTE 快速反应事件（可选）
  nextScene?: string      // 默认跳转场景
  onEnter?: Effect[]      // 进入场景时触发的效果
}

interface Choice {
  text: string            // 选项文字
  targetScene: string     // 目标场景 ID
  conditions?: Condition[] // 显示条件（可选）
  effects?: Effect[]      // 触发效果（可选）
  timeLimit?: number      // 限时秒数，0=不限
}
```

## 图布局

场景节点图使用 [dagre](https://github.com/dagrejs/dagre) 进行层级化自动布局。

### 布局策略演进

| 版本 | 方式 | 问题 |
|------|------|------|
| v1 | 网格排列 `(i % 4) * 220` | 不反映拓扑关系 |
| v2 | BFS 层级分配 | 多父同子时边交叉严重 |
| v3 (当前) | Dagre (Sugiyama 算法) | — |

Dagre 自动处理：

- **层内排序** — Barycenter 启发式最小化边交叉
- **多父同子** — 插入虚拟节点，边不交织
- **回边处理** — 循环引用自动反向路由
- **间距优化** — 根据节点度自适应分布

### 配置

布局参数在 `editor/composables/useLayout.ts`：

| 参数 | 值 | 说明 |
|------|-----|------|
| `rankdir` | `'LR'` | 左→右方向 |
| `nodesep` | `50` | 同层节点垂直间距（px） |
| `ranksep` | `240` | 层级间水平间距（px） |
| `marginx` / `marginy` | `60` | 画布四周边距（px） |
| `NODE_W` / `NODE_H` | `180 × 60` | 节点尺寸，用于居中偏移 |

### 连线样式

- 边类型：`SmoothStepEdge`（正交线，拐角带圆角），在 `SceneGraph.vue` 通过 `:default-edge-type="SmoothStepEdge"` 全局设置
- 动画：已禁用
- 回边：Dagre 自动路由，Vue Flow 渲染为从右往左的回线

### 调参指南

| 现象 | 调整 |
|------|------|
| 节点堆叠拥挤 | 增大 `nodesep` / `ranksep` |
| 节点间距过大 | 减小 `ranksep` |
| 边碰到画布边缘 | 增大 `marginx` / `marginy` |
| 节点定位偏左/上 | 检查 `NODE_W` / `NODE_H` 是否匹配实际节点尺寸 |
