chore: initial commit

This commit is contained in:
2026-05-08 11:04:00 +08:00
commit f55d2a57c3
6278 changed files with 866081 additions and 0 deletions

View File

@@ -0,0 +1,248 @@
# 14 · 叙事系统规范
> **所属文档集** [← 返回索引](./README.md)
> **摘要**对话系统、世界标记、NPC 状态管理与三结局触发逻辑。
---
## 目录
1. [叙事设计原则](#1-叙事设计原则)
2. [对话系统](#2-对话系统)
3. [世界标记系统](#3-世界标记系统)
4. [NPC 状态管理](#4-npc-状态管理)
5. [任务系统](#5-任务系统)
6. [结局触发系统](#6-结局触发系统)
7. [叙事事件目录](#7-叙事事件目录)
---
## 1. 叙事设计原则
| 原则 | 说明 |
|------|------|
| **环境叙事优先** | 通过场景、图鉴和道具描述讲述故事,减少强制对话 |
| **非线性探索** | 玩家可按任意顺序解锁叙事内容 |
| **状态感知** | NPC 的对话随玩家进程改变(不重复同一句话)|
| **数据驱动** | 所有对话树、条件和触发均为数据,不硬编码 |
---
## 2. 对话系统
### 2.1 对话数据模型
```
DataModel DialogueData {
dialogueId : ID
speakerId : ID // NPC ID 或 "player"
──────────────────────────────────
nodes : List<DialogueNode>
entryNodeId : ID // 起始节点
}
DataModel DialogueNode {
nodeId : ID
type : DialogueNodeType // Text / Choice / Condition / Action
──────────────────────────────────
// type = Text:
text : Optional<String> // 支持本地化 Key
portrait : Optional<PortraitID>
nextNodeId : Optional<ID> // null = 对话结束
// type = Choice:
choices : Optional<List<DialogueChoice>>
// type = Condition:
condition : Optional<DialogueCondition>
trueNodeId : Optional<ID>
falseNodeId : Optional<ID>
// type = Action:
action : Optional<DialogueAction>
nextNodeId : Optional<ID>
}
DataModel DialogueChoice {
choiceText : String
nextNodeId : ID
availableCondition: Optional<DialogueCondition> // null=始终显示
}
```
### 2.2 对话条件类型
```
DataModel DialogueCondition {
conditionType : DialogueConditionType
targetId : Optional<ID>
value : Optional<Number>
}
```
| 条件类型 | 说明 |
|---------|------|
| `HasAbility` | 玩家已解锁某能力 |
| `HasItem` | 玩家持有某物品 |
| `WorldFlagTrue` | 某世界标记为 true |
| `QuestStageIs` | 某任务处于指定阶段 |
| `BossDefeated` | 某 Boss 已被击败 |
| `FirstVisit` | 玩家第一次和此 NPC 对话 |
### 2.3 对话动作类型
| 动作类型 | 说明 |
|---------|------|
| `SetWorldFlag` | 设置世界标记 |
| `GiveItem` | 给予物品 |
| `GiveGeo` | 给予 Geo |
| `UnlockAbility` | 解锁能力 |
| `AdvanceQuest` | 推进任务阶段 |
| `PlayCutscene` | 播放过场动画 |
---
## 3. 世界标记系统
### 3.1 世界标记规范
```
WorldFlags: Map<String, Boolean>
```
- 存储于 `ProgressionSaveData.worldFlags`
- Key 为字符串,建议命名格式:`区域_事件_描述`(如 `region1_boss_firstEncounter`
- 标记只能设为 true不支持撤销设计保证单向性
- 叙事系统、门控系统、AI 系统均可读取世界标记
### 3.2 设置世界标记的来源
| 来源 | 说明 |
|------|------|
| 对话动作 `SetWorldFlag` | 对话选择/到达某节点时触发 |
| 门控开启 | 某些门控开启时自动设置 |
| Boss 死亡 | OnBossDefeated 事件触发时自动设置 |
| 收集品拾取 | 特定关键收集品触发 |
| 手动触发器 | 场景中的触发区域触碰时 |
---
## 4. NPC 状态管理
### 4.1 NPC 数据模型
```
DataModel NPCData {
npcId : ID
displayName : String
──────────────────────────────────
locations : List<NPCLocationEntry> // NPC 在不同阶段的位置
dialogueVersions: List<NPCDialogueVersion> // 不同进程阶段的对话
}
DataModel NPCLocationEntry {
condition : Optional<DialogueCondition> // 满足时位于此处null=默认)
roomId : ID
position : Vector2
}
DataModel NPCDialogueVersion {
condition : Optional<DialogueCondition> // 满足时使用此对话null=默认)
dialogueId : ID
priority : Integer // 多个条件同时满足时取最高优先级
}
```
### 4.2 对话版本选取规则
```
玩家与 NPC 交互时:
枚举 NPCData.dialogueVersions按 priority 降序)
→ 找第一个 condition 满足(或 condition 为 null的版本
→ 使用该版本的 dialogueId 开始对话
若无任何版本满足 → 使用默认兜底对话("......"
```
---
## 5. 任务系统
### 5.1 任务数据模型
```
DataModel QuestData {
questId : ID
displayName : String
description : String
──────────────────────────────────
stages : List<QuestStageData>
rewardOnComplete: Optional<QuestReward>
}
DataModel QuestStageData {
stageIndex : Integer
description : String
completionCondition: DialogueCondition // 推进到下一阶段的条件
}
DataModel QuestReward {
geoAmount : Optional<Integer>
itemIds : List<ID>
abilityId : Optional<ID>
worldFlagsToSet : List<String>
}
```
---
## 6. 结局触发系统
### 6.1 结局条件回顾
(详细条件见 [09_ProgressionSystem.md](09_ProgressionSystem.md) 第 5 节)
| 结局 | 类型 | 核心条件 |
|------|------|---------|
| 结局 A | 普通 | 击败最终 Boss |
| 结局 B | 完全 | Boss 全清 + 主线剧情完整 + 关键 NPC 任务 |
| 结局 C | 隐藏 | 结局 B + 隐藏世界标记序列激活 |
### 6.2 结局判定流程
```
最终 Boss 死亡时:
检查结局 C 条件(最严格)
├─ 满足 → 触发结局 C
└─ 不满足 →
检查结局 B 条件
├─ 满足 → 触发结局 B
└─ 不满足 → 触发结局 A
触发结局 X:
→ 发出 OnEndingTriggered 事件endingType
→ 播放对应过场动画序列
→ 记录到 SaveDataendingUnlocked
→ 游戏结束流程
```
---
## 7. 叙事事件目录
| 事件 | 触发时机 | 载荷 |
|------|---------|------|
| `OnDialogueStarted` | 开始对话 | `dialogueId, npcId` |
| `OnDialogueNodeReached` | 到达对话节点 | `nodeId` |
| `OnDialogueChoiceMade` | 玩家做出选择 | `choiceIndex, nextNodeId` |
| `OnDialogueEnded` | 对话结束 | `dialogueId` |
| `OnWorldFlagSet` | 世界标记变更 | `flagKey` |
| `OnQuestStarted` | 任务开始 | `questId` |
| `OnQuestAdvanced` | 任务推进 | `questId, newStage` |
| `OnQuestCompleted` | 任务完成 | `questId` |
| `OnEndingTriggered` | 结局触发 | `endingType` |
| `OnCutsceneStarted` | 过场动画开始 | `cutsceneId` |
| `OnCutsceneEnded` | 过场动画结束 | `cutsceneId` |