Implement scene-based chapter quest progression
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-08 11:58:47 +08:00
parent 9d2fc9e4b8
commit bd9fdcbe31
170 changed files with 18259 additions and 1049 deletions

View File

@@ -8,6 +8,7 @@
- [AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md](./AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md):运行时物品生成系统重设计。
- [EQUIPMENT_BUILD_AND_FORGE_LOOP_SYSTEM_DESIGN.md](./EQUIPMENT_BUILD_AND_FORGE_LOOP_SYSTEM_DESIGN.md):配装构筑与合成/锻造闭环设计。
- [COMPANION_FIRST_CONTACT_RELATIONSHIP_AND_PRIVATE_CHAT_DESIGN_2026-04-04.md](./COMPANION_FIRST_CONTACT_RELATIONSHIP_AND_PRIVATE_CHAT_DESIGN_2026-04-04.md):角色首遇感、关系分层解锁、私聊系统设计。
- [SCENE_CHAPTER_LOOP_AND_FIRST_ENTRY_CHAPTER_QUEST_DESIGN_2026-04-08.md](./SCENE_CHAPTER_LOOP_AND_FIRST_ENTRY_CHAPTER_QUEST_DESIGN_2026-04-08.md):把每个场景收束成章节单元,并在首进场景时开启章节任务的设计稿。
- [npc-conversation-situation-draft.md](./npc-conversation-situation-draft.md)NPC 对话阶段和情景注入草案。
## 推荐阅读
@@ -15,4 +16,5 @@
- 做物品、Build、锻造相关需求时先看前两份。
- 做自定义世界创作工作台、创作者输入边界、AI 分工设计时,先看第一份。
- 做角色关系、同伴互动、对话表现时,先看后两份。
- 做剧情引擎章节化、场景闭环、章节任务接入时,优先看新增的场景章节设计稿。
- 如果要判断是否符合目标,再和 `docs/prd/` 中对应 PRD 对照阅读。

View File

@@ -0,0 +1,715 @@
# 场景章节闭环与首进章节任务设计
更新时间:`2026-04-08`
## 0. 目标
这份设计稿要把当前仓库里已经存在的:
- `章节状态`
- `任务 contract / step`
- `场景残痕`
- `NPC 首遇与关系`
- `Goal Stack`
进一步收束成一条更明确的结构:
**每个场景都是一个剧情章节单元;每个章节在当前剧情引擎里都要形成“起承转合”的完整闭环;并且在玩家首次进入该场景时,自动开启一个章节任务。**
这里的重点不是再造一套全新系统,而是把现有能力重新组织成:
1. 场景不再只是地图节点,而是章节容器。
2. 章节不再只是背景摘要,而是有明确动作面的闭环。
3. 任务不再只是零散委托,而是章节在前台的执行外壳。
4. NPC 不再只是“场景里有什么人”,而是按章节节拍承担起、承、转、合的叙事职责。
---
## 1. 基于当前仓库的判断
结合当前文档与代码,现状已经具备一半骨架,但关键一半还没接上。
### 1.1 已经具备的基础
1. `src/services/storyEngine/chapterDirector.ts`
- 已经有 `ChapterState`,但当前主要根据 `signalCount / chronicleCount / activeThreadCount` 推章节阶段,更像“抽象章节热度”,还不是“具体场景章节实例”。
2. `src/data/questFlow.ts`
- 已经有 `QuestIntent -> QuestContract -> QuestStep -> QuestProgressSignal` 的完整任务闭环。
- `QuestLogEntry` 也已经有 `actId / threadId / contractId / steps / activeStepId / visibleStage` 这些可扩展入口。
3. `src/services/storyEngine/goalDirector.ts`
- 已经能把 `chapter + quest + journeyBeat + setpiece` 编译成玩家前台目标感。
- 说明章节任务一旦成型,前台目标层基本不用重做,只要接正确数据。
4. `src/data/scenePresets.ts`
- 已经有场景描述、敌对实体、额外 NPC、宝藏线索、narrative residues。
- 这些字段已经够支持“场景章节蓝图”的第一版自动编译。
### 1.2 当前缺口
当前最核心的缺口有 4 个:
1. 没有“场景首次进入”对应的持久状态。
- 现在能做场景切换,也能累计 `scenesTraveled`,但没有 `openedSceneChapterIds` 或等价结构。
2. 没有“场景章节实例”。
- `ChapterState` 已有,但没有一个明确指向 `sceneId`、可追踪起承转合进度的运行时对象。
3. 没有“章节任务自动开章”的规则。
- 现在任务更多还是由 NPC 委托机会触发,不是“首进场景即开章”。
4. 没有按章节节拍组织 NPC。
- 现在场景里有 NPC、有敌人、有残痕但还没有明确规定谁负责起、谁负责承、谁负责转、谁负责合。
一句话总结:
**当前仓库已经有章节系统、任务系统和场景叙事素材,但还没有“场景章节实例 + 首进自动开章任务 + 起承转合 NPC 编排”这条真正把它们串起来的中介层。**
---
## 2. 核心决策
## 2.1 场景 = 章节单元
从这次开始,默认把每个可到达场景都视为一个章节单元。
这意味着:
1. 玩家进入一个新场景,不只是“换地图”,而是“开启一个新章节”。
2. 该场景内必须具备一个可完成的最小剧情闭环。
3. 即使大世界主线跨多个场景延续,每个场景也要有自己的局部收束。
## 2.2 章节仍然是叙事语义,任务是前台动作面
这点要继续保持和当前项目既有方向一致:
- `章节` 负责表达“这一段故事在追什么”。
- `章节任务` 负责表达“玩家现在要做什么”。
也就是说:
**章节不是单独再做一个和任务平级的前台入口,而是通过“章节任务”落到玩家动作层。**
## 2.3 保留现有五阶段结构,起承转合作为体验要求理解
当前 `ChapterState.stage` 已经在用:
- `opening`
- `expansion`
- `turning_point`
- `climax`
- `aftermath`
这次不再新增新的叙事阶段枚举,也不再额外引入一套“四拍语义”运行时字段。
“起承转合”保留为章节闭环的体验要求,但在系统里直接压到现有五阶段上理解:
| 当前字段 | 体验语义 |
| --- | --- |
| `opening` | 起:开章立题 |
| `expansion` | 承:压力展开 |
| `turning_point` | 转:理解改判 |
| `climax` | 合:正面收束 |
| `aftermath` | 合后的余波与交接 |
这意味着:
1. 玩家体感上仍然能得到“起承转合”的完整闭环。
2. 系统运行时只认现有 `stage`,不新增第二套章节阶段系统。
3. `goalDirector / journeyBeatPlanner / setpieceDirector / UI` 都可以继续沿用现有结构。
---
## 3. 场景章节的标准闭环
每个场景章节都必须至少回答这 4 个问题:
1. 玩家刚进来时,这里有什么事正在发生?
2. 玩家在这一章里到底要处理什么压力?
3. 这一章中途会出现什么反转或改判?
4. 玩家离开前,这一章给出了什么局部收束?
对应到现有系统里,就是当前五阶段的完整跑通。
## 3.1 `opening`:开章立题
触发时机:
- 玩家首次进入某场景
必须完成的事:
1. 建立场景章节标题与主题。
2. 让一个 NPC、残痕或现场异常把问题抛出来。
3. 自动开启本章章节任务。
4. 给玩家一个明确的第一步。
适合使用的现有信号:
- `scene_reached`
- 首次 NPC 对话
- 首次观察残痕 / 宝藏线索
适合落地的任务 step
- `reach_scene`
- `talk_to_npc`
- `inspect_treasure`
## 3.2 `expansion`:压力展开
触发时机:
- 玩家已经接住本章 lead开始深入该场景
必须完成的事:
1. 让玩家确认“这一章不是空壳,确实有事要处理”。
2. 把当前场景的主压力推到前台。
3. 让场景内的敌对 NPC / 障碍 NPC / 关键残痕承担中段推进。
适合落地的任务 step
- `inspect_treasure`
- `defeat_hostile_npc`
- `spar_with_npc`
- `talk_to_npc`
## 3.3 `turning_point`:改判与揭示
触发时机:
- 玩家完成了承段主动作,系统需要让当前理解发生偏转
必须完成的事:
1. 出现一条新事实、矛盾证词或旧痕反证。
2. 让至少一个 NPC 的定位发生变化。
3. 把任务从“处理中”切到“确认真相 / 回去对话 / 做最后一跳”。
适合落地的任务 step
- `talk_to_npc`
- `inspect_treasure`
- `reach_scene`
- `item_delivered`
## 3.4 `climax`:本章收束
触发时机:
- 玩家已经拿到足够信息,或者已经处理完这一章的核心冲突
必须完成的事:
1. 当前场景的局部问题得到收束。
2. 章节任务进入可交付或已交付状态。
3. 章节回写 `chronicle`
4. 给出下一场景 handoff。
适合落地的任务 step
- `talk_to_npc`
- `item_delivered`
注意:
**`climax` 不等于世界真相彻底结束,而是这一个场景章节的核心矛盾必须在这里得到局部收束。**
也就是说,大主线可以继续,但当前场景不能只留下“半段没收”的悬空状态。
## 3.5 `aftermath`:余波与交接
触发时机:
- 章节任务已经 `ready_to_turn_in``turned_in`
- 本章主要冲突已经完成,系统需要把结果沉淀并交给下一段
必须完成的事:
1. 把本章结果写进 `chronicle` 或最近摘要。
2. 把当前场景的局部余波写回场景状态、NPC 态度或任务说明。
3. 给出下一场景或下一条线程的 handoff。
这里的重点不是再加一轮复杂任务,而是把已有结果接住。
一句话总结:
**起承转合仍然保留为设计目标,但系统实现上直接用 `opening -> expansion -> turning_point -> climax -> aftermath` 跑完整闭环。**
---
## 4. NPC 编排规则
这次的 NPC 设计重点不是“一个场景塞多少人”,而是:
**谁在这一章里负责什么。**
## 4.1 不新增独立的 `npcCasting` 系统,先按现有阶段组织职责
这轮不建议为了章节化单独新增一套 NPC casting 数据结构。
更稳的做法是直接基于现有场景数据组织阶段职责:
1. `opening`
- 优先由场景里的首遇 NPC、第一条残痕或第一层异动承担立题职责。
2. `expansion`
- 优先由敌对单位、阻拦型 NPC、关键残痕承担压力职责。
3. `turning_point`
- 优先由第二条线索、矛盾证词、返回对话的 NPC 承担改判职责。
4. `climax / aftermath`
- 优先由最初开章 NPC 或新的接棒 NPC 承担收束和 handoff。
## 4.2 允许一人多阶段承担职责,但阶段职责不能空
考虑到当前有些场景 NPC 数量不多,因此允许:
- 同一个 NPC 同时承担 `opening + aftermath`
- 同一个 NPC 同时承担 `expansion + turning_point`
如果友方 NPC 不够,可以由这些现有对象补位:
1. 场景残痕
2. 宝藏线索
3. 文书 / 道具 / 遗物
4. 敌对单位
也就是说,本章的“转”不一定靠新 NPC也可以靠现有证据或现场变化来完成。
## 4.3 NPC 的章节职责应该是动态解释,不是静态标签
不要把 NPC 只写成:
- 商人
- 侍女
- 守门人
- 怪物
更应该补的是:
- 他在这一章里为什么是开章人
- 他卡住玩家的是什么压力
- 他掌握的转折信息是什么
- 他能否承接本章结算
这部分优先通过现有 `npc context / dialogue / rewardText / quest step 文案` 去表达,不急着为每个 NPC 新增专门类型。
---
## 5. 数据结构建议
## 5.1 扩展 `ChapterState`
建议扩展为:
```ts
export interface ChapterState {
id: string;
title: string;
theme: string;
primaryThreadIds: string[];
stage: 'opening' | 'expansion' | 'turning_point' | 'climax' | 'aftermath';
chapterSummary: string;
sceneId?: string | null;
chapterQuestId?: string | null;
}
```
这样当前系统读取 `stage` 的地方仍然可用,同时章节状态也能明确绑定:
- 这是哪个场景章节
- 当前绑定的是哪一个章节任务
建议:
- 对“场景章节”使用稳定 id例如 `chapter:scene:${sceneId}`
- 不再额外发明新的章节实例类型
## 5.2 扩展 `StoryEngineMemoryState`
建议新增:
```ts
export interface StoryEngineMemoryState {
openedSceneChapterIds?: string[];
}
```
这是这次最关键的数据层补丁,因为当前仓库现在没有“首进场景已开章”的持久状态。
这里刻意不新增 `sceneChapterStates` 这类新的章节运行时容器,优先复用现有:
- `currentChapter`
- `quests`
- `storyHistory`
- `chronicle`
## 5.3 尽量复用 `QuestLogEntry`
建议补充:
```ts
export interface QuestLogEntry {
chapterId?: string | null;
}
```
其他字段尽量直接复用现有结构:
- `sceneId`
- `steps`
- `activeStepId`
- `visibleStage`
- `status`
目的不是让 UI 暴露更多字段,而是让系统能知道:
- 这是某个场景章节自动生成的主任务
- 该任务与当前 `ChapterState` 是否一一对应
一句话原则:
**这轮只补“老系统里真正缺的最小字段”,不再额外发明一整套章节蓝图 / 章节运行时数据模型。**
---
## 6. 章节任务设计
## 6.1 首次进入场景时自动开任务
根据这次需求,章节任务默认不再依赖玩家额外点一次“接受委托”。
建议规则:
1. 玩家首次进入某场景时,直接创建该场景的章节任务。
2. 章节任务默认进入 `active`
3. 同一场景后续再次进入时,不重复开同一任务。
也就是说:
**首进场景 = 开章节 = 章节任务自动入列。**
## 6.2 章节任务不强制另起一套五步或四步系统,优先复用现有 step + status
这次不建议为了章节化再发明一套新的任务阶段结构。
更稳的做法是:
1. 章节层继续使用现有五阶段 `stage`
2. 任务层继续使用现有 `steps + activeStepId + status`
3. 通过任务进度去驱动章节阶段,而不是反过来再创建一套章节 step
建议映射关系:
| 章节阶段 | 现有任务侧表现 |
| --- | --- |
| `opening` | 章节任务刚创建,首个 `step` 为接 lead |
| `expansion` | 中段调查 / 战斗 / 接触 step 在推进 |
| `turning_point` | `activeStep` 切换到改判或回报前置 step |
| `climax` | 最后一个核心 step 正在执行,或任务刚进入 `ready_to_turn_in` |
| `aftermath` | 任务 `turned_in`,或已经完成本章结算并进入 handoff |
推荐的 step 仍然复用当前支持的类型:
| 当前阶段 | 常见 step kind |
| --- | --- |
| `opening` | `reach_scene` / `talk_to_npc` / `inspect_treasure` |
| `expansion` | `inspect_treasure` / `defeat_hostile_npc` / `spar_with_npc` |
| `turning_point` | `talk_to_npc` / `inspect_treasure` / `reach_scene` |
| `climax` | `talk_to_npc` / `item_delivered` / 关键收束 step |
| `aftermath` | 优先使用 `ready_to_turn_in / turned_in` 和 handoff不再强塞新 step |
## 6.3 章节任务应该服务于 Goal Stack
当前仓库已经有 `GoalStack`,因此章节任务一旦建立,应默认成为:
1. `active_contract`
2. `immediate_step`
`ChapterState` 继续承担:
1. 章节主题
2. 章节承诺
3. 章节背景总结
也就是说前台玩家看到的是:
- 当前章节任务标题
- 下一步去哪 / 找谁 / 做什么
后台章节层则继续给叙事和 prompt 提供语义。
---
## 7. 运行时流程接入建议
## 7.1 调整 `chapterDirector.ts`
当前 `chapterDirector` 更像“热度评分器”。
这次建议直接在它内部补两类能力:
1. 场景绑定
- 当前场景存在且符合开章条件时,直接生成 `sceneId` 绑定的 `ChapterState`
2. 阶段推导
- 优先从当前场景绑定的章节任务进度推导 `opening -> expansion -> turning_point -> climax -> aftermath`
- 只有在场景章节信息不足时,才回退到现有 `signalCount / chronicleCount / activeThreadCount` 逻辑
建议新增的只是 `chapterDirector.ts` 内部 helper而不是新的模块例如
```ts
resolveSceneBoundChapterState(params)
deriveChapterStageFromQuest(params)
```
## 7.2 调整 `questFlow.ts`
建议新增:
```ts
buildChapterQuestForScene(params)
findActiveChapterQuestForScene(params)
```
这里依然放在现有 `questFlow.ts` 内部处理,不单独拆新系统。
章节任务 builder 的原则是:
1. 输入继续使用当前能拿到的场景信息
- `scenePreset`
- `currentSceneId`
- `activeThreadIds`
- `scene npcs / hostile npc / treasureHints`
2. 输出继续是现有 `QuestLogEntry`
- 只是多补 `chapterId`
- 并尽量让 `sceneId = 当前场景`
## 7.3 调整 `useStoryGeneration.ts` / `sessionActions.ts`
推荐接入点:
1. 场景切换完成后
2. `scene_reached` 信号写入时
3. 地图跳转 `travelToSceneFromMap(...)` 成功后
处理顺序建议为:
1. 先判断是否首进场景
2. 若首进:
-`sceneId` 写入 `openedSceneChapterIds`
- 检查当前场景是否已有未结清的章节任务
- 若没有,则在 `questFlow.ts` 内生成章节任务
- 写入 `chapterState(stage = opening)`
- 触发章节 pulse
3. 再刷新 `goalStack / chapterState / journeyBeat`
## 7.4 调整 `goalDirector.ts`
当前 `goalDirector.ts` 已经能编译:
- `chapter`
- `quest`
- `journeyBeat`
- `setpiece`
因此这里只需要一个优先级调整:
1. 当前场景若存在匹配 `chapterId` 的章节任务
2. 且它还未 `turned_in`
3. 则优先把它当作当前场景的 `active_contract` / `immediate_step`
这样 Goal Stack 继续复用,不需要再加新层。
## 7.5 调整 `storyChronicle.ts`
建议章节至少写 3 次 chronicle
1. 开章
2. 转折发生
3. 本章收束
这样章节不会只存在于当前一屏,而能进入长期回顾。
---
## 8. 首进场景的标准触发流程
建议标准流程如下:
```text
玩家进入场景
-> 触发 scene_reached
-> 检查 openedSceneChapterIds 是否已包含当前 sceneId
-> 若否:
-> 将当前 sceneId 写入 openedSceneChapterIds
-> 生成 chapterId = chapter:scene:${sceneId}
-> 生成章节任务并直接设为 active
-> 写入当前 ChapterState(stage = opening, sceneId, chapterQuestId)
-> 触发 Goal Pulse: 新章节开启
-> 写入 chronicle 开章记录
-> 若是:
-> 只同步当前章节状态,不重复开任务
-> 随着任务 step 与 signal 推进:
-> ChapterState.stage 依次推进到 expansion / turning_point / climax
-> 当任务 ready_to_turn_in 或 turned_in
-> ChapterState.stage = aftermath
-> 写入余波 chronicle 与下一跳 handoff
```
这一步是整个方案能不能真正成立的关键,因为用户这次要的就是:
**“首次进入某个场景”这一刻,就要像进入新章节一样被系统接住。**
---
## 9. 当前仓库可直接套用的样章示例
下面用当前仓库已经存在的 `宫苑内庭` 说明这套设计怎么落。
当前场景素材来自 `src/data/scenePresets.ts`
- 场景:`宫苑内庭`
- 场景描述:`回廊深处静得过分,花木修得齐整,却处处像埋着王庭旧案。`
- 场景友方 NPC`旧宫侍女`
- 场景线索:`回廊暗格里的香囊``花圃石座下的旧金牌`
- 相邻场景:`铸坊工场``雨夜长街``地宫通道`
可直接编成如下章节:
## 9.1 章节标题
`宫苑内庭·旧痕回廊`
## 9.2 `opening`
- 玩家首次进入 `宫苑内庭`
- `旧宫侍女` 作为开章角色
- 她不给完整解释,只提醒“最近不该过去的回廊”
- 系统自动开启章节任务:`查明内庭异动`
## 9.3 `expansion`
- 玩家需要先调查 `回廊暗格里的香囊` 或处理场景里的敌对压力
- 任务 step 进入“确认这条旧痕到底指向谁”
## 9.4 `turning_point`
- 玩家在第二条线索 `花圃石座下的旧金牌` 中得到反证
- 旧宫侍女此前的说法出现缺口
- 当前理解从“单纯禁区提醒”转成“她知道旧案,但在刻意压着不说”
## 9.5 `climax`
- 玩家返回与 `旧宫侍女` 对话
- 她承认这里只是旧案的一层外壳,并把下一跳 handoff 到:
- `雨夜长街`
-`铸坊工场`
- 当前章节任务进入可交付或已交付
## 9.6 `aftermath`
- `chronicle` 写入本章收束
- 当前章节状态进入余波
- Goal Stack 把下一步交接到新场景或下一段线索
这个例子里的玩家体感仍然是完整的“起承转合”,但系统实现上始终只在跑当前已有的五阶段。
这个例子说明:
**即使大主线还没结束,`宫苑内庭` 这个单独场景也已经能形成一章完整体验。**
---
## 10. MVP 落地顺序
## 阶段 A先补数据层和首进判定
先做:
1. `openedSceneChapterIds`
2. 场景首次进入 hook
3. `chapter:scene:${sceneId}` 的章节 id 规则
验收标准:
- 同一场景只在第一次进入时开章节任务
## 阶段 B把章节任务接到现有 questFlow
先做:
1. `buildChapterQuestForScene(...)`
2. `chapterId`
3. 场景 lead 与当前 quest step 的默认映射
验收标准:
- 章节任务能在现有 `steps + status` 下正常推进
## 阶段 C让 chapterDirector 真正按场景章节输出
先做:
1. `ChapterState.sceneId`
2. `ChapterState.chapterQuestId`
3. `chapterDirector` 优先从当前章节任务推导 `stage`
验收标准:
- 当前章节标题与当前场景一致
- 章节五阶段能和任务推进基本同步
## 阶段 D补 NPC 章节职责与 handoff
先做:
1. 为每个场景补默认开章 NPC / 转折线索 / 收束对话
2. 为每个场景补 handoff 规则
3. 回写 `chronicle`
验收标准:
- 每个场景都能给出明确的阶段承担者与下一跳
---
## 11. 验收标准
做到以下几点,才算真正满足这次需求:
1. 玩家首次进入任一可达场景时,系统会自动开启该场景的章节任务。
2. 每个场景章节都能在当前系统里跑出 `opening -> expansion -> turning_point -> climax -> aftermath` 的完整闭环,玩家体感上形成完整的起承转合。
3. 每个场景至少能找到开章、承压、转折、收束这些阶段承担者,允许一人多阶段承担,但阶段职责不能缺。
4. 章节任务不是孤立任务,而是当前章节在前台的动作面。
5. 同一场景重复进入时,不会重复开章,但会继承已存在的章节状态或余波状态。
6. 本章收束后,系统能明确交接下一场景或下一段主线程 lead。
7. 这轮实现主要落在现有 `chapterDirector / questFlow / useStoryGeneration / goalDirector / storyChronicle` 上,不再另起一套章节运行时系统。
---
## 12. 最后结论
如果我们接受“每个场景都是一个章节单元”这条方向,那么当前仓库最该补的不是一套新系统,而是对现有系统的三处收紧:
1. 补上 `openedSceneChapterIds`
2.`ChapterState` 显式绑定 `sceneId + chapterQuestId`
3. 让现有章节任务与现有五阶段直接挂钩
这样之后,现有系统会形成更简洁的收束关系:
- `scenePresets` 提供场景素材
- `questFlow` 直接把场景 lead 落成章节任务
- `chapterDirector` 用现有五阶段输出章节状态
- `useStoryGeneration / sessionActions` 处理首进开章
- `goalDirector` 把章节任务继续编译成玩家当前目标
最终玩家感受到的就不再是“我只是进了一个新场景”,而会更接近:
**我进入了这一章,接住了这一章的任务,见到了这一章该见的人,也在这一章里把一段局势真正走完了。**