1
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-20 09:54:17 +08:00
parent 67c584b4df
commit 50759f3c1e
159 changed files with 16938 additions and 16925 deletions

View File

@@ -498,7 +498,7 @@ type GeneratedCharacterAnimationAsset = {
- `src/components/CharacterAnimator.tsx`
- `src/types/characters.ts`
- `src/data/characterOverrides.json`
- `scripts/dev-server/localApiPlugins.ts`
- `server-node/src/modules/assets/**`
建议新增:

View File

@@ -1,6 +1,6 @@
# AI 原生自定义世界创作页面 PRD
更新时间:`2026-04-13`
更新时间:`2026-04-20`
## 0. 文档目的
@@ -314,9 +314,11 @@ UI 主标题建议:
按优先级取:
1. `draftProfile.camp.imageSrc`
2. `draftProfile` 中可解析的营地
3. 角色主图或默认创作占位图
1. `draftProfile.cover.imageSrc`,当 `sourceType``uploaded / generated`
2. `draftProfile.camp.imageSrc` 作为默认封面底
3. 默认封面底图上叠加 `draftProfile.cover.characterRoleIds` 对应的角色主形象
4. 若未显式指定角色,则按 `playableNpcs` 顺序取前 `3` 个有主图的角色
5. 若开局场景图为空,则回退到第一张场景图;再不行才回退到首个角色主图或默认占位图
### 草稿卡片主操作
@@ -358,9 +360,78 @@ UI 主标题建议:
按优先级取:
1. 营地图
2. 第一可扮演角色立绘
3. 默认已发布作品占位图
1. `CustomWorldProfile.cover.imageSrc`,当 `sourceType``uploaded / generated`
2. 开局场景图作为默认封面底图
3. 默认封面底图上叠加 `cover.characterRoleIds` 指定的角色主形象
4. 若未显式指定角色,则按 `playableNpcs` 顺序取前 `3` 个有主图的角色
5. 若默认底图不可用,再回退到第一可扮演角色立绘或默认占位图
## 7.3 作品封面属性
作品必须新增显式封面属性,作为作者可编辑的作品资产,而不再只靠“卡片展示时临时猜一张图”。
建议字段:
```ts
type CustomWorldCoverSourceType = 'default' | 'uploaded' | 'generated';
interface CustomWorldCoverProfile {
sourceType: CustomWorldCoverSourceType;
imageSrc?: string | null;
characterRoleIds?: string[];
}
```
字段含义:
1. `sourceType = default`
- 表示继续使用系统默认封面布局
- `imageSrc` 不作为最终封面图使用
- 底图固定取“开局场景图”
- 前景角色取 `characterRoleIds`
2. `sourceType = uploaded`
- 表示作者直接上传了一张最终封面
- 卡片与详情页直接显示 `imageSrc`
- 不再叠加默认角色前景
3. `sourceType = generated`
- 表示作者通过 AI 生成了一张最终封面
- 卡片与详情页直接显示 `imageSrc`
- 不再叠加默认角色前景
## 7.4 默认封面布局
默认封面布局不是单纯“取开局场景图”,而是:
```text
开局场景图
+ 前景主角色主形象 2~3 个
+ 用于列表卡片和作品详情的统一封面预览
```
明确规则:
1. 默认封面底图固定优先取 `camp.imageSrc`
2. 默认前景角色固定从 `playableNpcs` 中取前 `3` 个有主图的角色
3. 若作者在 `cover.characterRoleIds` 中显式指定角色,则优先按指定顺序展示
4. 前端只负责把后端给出的“底图 + 角色主图列表”渲染成封面,不在前端做封面规则推理
5. 已上传或已生成的最终封面,直接作为成品图显示,不再做默认布局叠加
## 7.5 作者操作
作者在作品编辑态至少支持 4 个动作:
1. `使用默认封面`
2. `上传封面`
3. `AI 生成封面`
4. `重置为默认`
约束:
1. 上传和 AI 生成都必须把最终图片落到后端资产目录,前端不能长期持有 Data URL 作为作品封面
2. 重置为默认后,`sourceType` 回到 `default`
3. 草稿与已发布作品都读取同一份封面属性,不允许出现“草稿页是一个封面、发布后又自动换另一张”的漂移
### 已发布卡片主操作
@@ -395,6 +466,8 @@ interface CustomWorldWorkSummary {
subtitle: string;
summary: string;
coverImageSrc?: string | null;
coverRenderMode?: 'image' | 'scene_with_roles';
coverCharacterImageSrcs?: string[];
updatedAt: string;
publishedAt?: string | null;
stage?: string | null;
@@ -447,6 +520,25 @@ interface CustomWorldWorkSummary {
仅已发布作品为 `true`
### `coverRenderMode / coverCharacterImageSrcs`
用于支撑默认封面布局。
规则:
1. 当作品封面为上传或 AI 生成成图时:
- `coverRenderMode = image`
- `coverCharacterImageSrcs = []`
2. 当作品封面为默认布局时:
- `coverRenderMode = scene_with_roles`
- `coverImageSrc = 开局场景图`
- `coverCharacterImageSrcs = 需要叠加的角色主图列表`
一句话:
**后端负责告诉前端“这张封面该怎么画”,前端只负责把它画出来。**
---
## 9. 后端接口设计
@@ -700,8 +792,9 @@ type SelectionStage =
1. 新建作品区位于首屏
2. tabs 横向可滚
3. 作品卡优先单列
3. 平台“创作”页中的“我的创作”列表在移动端至少双列展示,不能继续沿用横向滚动卡片的固定宽度
4. 不使用桌面化大表格
5. 双列卡片必须采用紧凑栅格布局,标题、状态、时间允许换行或截断,但不能横向溢出或出现参差错位
## 12.2 页面保持清爽

View File

@@ -0,0 +1,728 @@
# AI 原生场景多幕配置与 NPC 相遇聊天流程 PRD
更新时间:`2026-04-20`
## 0. 文档目的
这份 PRD 用于把下面几条已经存在但还没真正接成一条产品主链的设计,收束成一次可直接编码的迭代:
- `docs/prd/AI_NATIVE_AGENT_FIRST_CUSTOM_WORLD_CREATOR_PRD_2026-04-12.md`
- `docs/prd/AI_NATIVE_SCENE_CHAPTER_GAMEPLAY_PRD_AND_EXECUTION_PLAN_2026-04-08.md`
- `docs/prd/AI_NATIVE_NPC_CHAT_SINGLE_TURN_SESSION_PRD_2026-04-18.md`
- `docs/design/NPC_HIGH_AFFINITY_CHAT_QUEST_OFFER_FLOW_2026-04-19.md`
- `docs/design/SCENE_CHAPTER_LOOP_AND_FIRST_ENTRY_CHAPTER_QUEST_DESIGN_2026-04-08.md`
本次要解决的不是再新建一套场景系统或聊天系统,而是把现有:
1. 创作工作区
2. 场景章节闭环
3. NPC 多轮聊天
4. 场景背景资产
5. 好感度关系流
接成一条新的稳定流程:
**每个场景由创作者在工具中配置为 `2~5` 幕;每一幕都绑定独立背景图和相遇 NPC 顺序;每一幕的第一个 NPC 视为主角色;运行时按幕切换背景和可遇对象,并根据主角色当前好感度裁决聊天轮数与第 5 轮收束方式。**
这份文档必须能直接指导后续创作工具和游戏流程改造,避免需求落地漂移。
---
## 1. 一句话定义
把当前“一个场景只有一层平铺内容”的创作与运行方式,升级成“一个场景内有多幕推进、每幕有独立视觉和主角色相遇规则”的章节内流程。
---
## 2. 本次目标
本次迭代必须同时满足以下目标:
1. 创作者可以在现有创作页面中为每个场景章节配置多幕内容。
2. 每一幕都必须绑定一张正式背景图。
3. 每一幕都可以配置玩家会遇到哪些 NPC并且保留顺序。
4. 每一幕配置的第一个 NPC 必须被系统认定为该幕主角色。
5. 运行时进入某一幕时,背景图和可遇 NPC 必须随幕切换。
6. 当前幕主角色的聊天轮数必须按好感度裁决,而不是继续完全沿用统一规则。
7. 好感度大于 `0` 的主角色,在相遇后进入无限轮聊天态,直到玩家主动退出。
8. 好感度小于 `0` 的主角色,在相遇后最多只允许聊天 `5` 轮,第 `5` 轮必须输出一段为后续剧情开展铺垫的收束回应。
9. 前端继续只负责展示,幕切换、聊天限制、幕进度与数据裁决全部由 Express 后端负责。
10. 默认复用现有创作页面、草稿抽屉、详情弹层、场景章节和聊天流程,不新开独立系统或新页面。
---
## 3. 明确不做
本次明确不做下面这些事:
1. 不新建独立的“场景编辑器”页面。
2. 不把幕推进逻辑放到前端本地计算。
3. 不让创作者直接编辑底层运行时 `ChapterState` 或聊天状态对象。
4. 不做多 NPC 并行聊天。
5. 不做每一幕的复杂分支树可视化编辑器。
6. 不把“规则说明文案”默认堆到创作页或游戏 UI 面板里。
7. 不把“点击配置”实现成在当前卡片下面继续展开大段内容。
8. 不重写现有高好感委托链路,只在本次规则下明确它什么时候还能触发。
---
## 4. 现状判断
## 4.1 创作工具侧现状
当前仓库已经具备下面这些基础:
1. `packages/shared/src/contracts/customWorldAgent.ts`
- 已存在 `scene_chapter` 草稿卡 kind。
2. `server-node/src/services/customWorldAgentDraftCompiler.ts`
- 已经能编译世界、第一幕、线程、势力、角色、地点等草稿卡。
3. `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx`
- 已有草稿抽屉,但还没有把 `scene_chapter` 正式纳入抽屉分组。
4. 现有场景背景图生成与发布链已存在。
但当前仍有 4 个缺口:
1. 场景章节没有“幕”这一层结构化对象。
2. 背景图是场景级资产,不是幕级资产。
3. NPC 与场景的关系主要还是地点级归属,不是幕级相遇编排。
4. 创作者无法在创作页里明确控制“这一幕谁先出场、谁是主角色”。
## 4.2 游戏运行侧现状
当前运行时已经具备下面这些基础:
1. `src/data/questFlow.ts`
- 已有 `scene chapter quest``buildSceneChapterId(...)`
2. `src/services/storyEngine/chapterDirector.ts`
- 已能按场景章节输出 `ChapterState`
3. `src/hooks/story/npcEncounterActions.ts`
- 已有 `npc_chat` 多轮聊天、`turnCount``pendingQuestOffer` 等状态。
4. `packages/shared/src/contracts/story.ts`
- 已有 `NpcChatTurnRequest` / `NpcChatTurnResult` 契约。
但当前仍有 5 个问题:
1. 场景内部仍偏单层推进,缺少“第几幕”的明确状态。
2. 场景背景不会随幕切换。
3. 场景可遇 NPC 不会随幕切换。
4. 主角色没有从配置顺序直接编译成运行时规则。
5. 负好感主角色聊天仍没有“最多 5 轮且第 5 轮收束铺垫”的规则。
一句话总结:
**现在我们有场景章节也有聊天系统但还没有“场景多幕蓝图”这一层把创作配置、背景资产、NPC 相遇顺序和聊天规则真正串起来。**
---
## 5. 核心决策
## 5.1 场景章节与场景幕的关系
本次新增一个明确约束:
- `场景章节` 仍然是场景级闭环容器
- `场景幕` 是场景章节内部的有序分段
关系定义如下:
| 层级 | 作用 |
| --- | --- |
| `scene chapter` | 表示这一整个场景在剧情上的一章 |
| `scene act` | 表示这章内部的第几幕、当前视觉和当前相遇主体 |
每个场景章节必须至少有 `2` 幕,最多 `5` 幕。
## 5.2 多幕数量与章节阶段映射
为了不引入第二套完全独立的运行时章节体系,本次规定场景幕按数量映射到现有 `ChapterState.stage`
| 幕数 | 编译规则 |
| --- | --- |
| `2` 幕 | 幕 1=`opening + expansion`,幕 2=`turning_point + climax + aftermath` |
| `3` 幕 | 幕 1=`opening`,幕 2=`expansion + turning_point`,幕 3=`climax + aftermath` |
| `4` 幕 | 幕 1=`opening`,幕 2=`expansion`,幕 3=`turning_point`,幕 4=`climax + aftermath` |
| `5` 幕 | 与 `opening / expansion / turning_point / climax / aftermath` 一一对应 |
这意味着:
1. 创作者在工具里编辑的是“第几幕”。
2. 运行时仍然只认现有章节阶段枚举。
3. `chapterDirector` 可以继续复用,只是数据来源从“纯 quest 推导”升级成“quest + 幕蓝图联合推导”。
## 5.3 主角色定义
每一幕配置的 `encounterNpcIds` 必须是有序数组。
规则固定为:
1. `encounterNpcIds[0]` 就是当前幕主角色。
2. 运行时会把它编译成 `primaryNpcId`
3. 主角色承担该幕默认的首次相遇、聊天轮数裁决和幕推进优先级。
4. 其余 NPC 视为辅助相遇角色,不直接承担本次“好感度聊天轮数规则”。
---
## 6. 数据结构要求
## 6.1 创作草稿层新增结构
建议在现有 `CustomWorldFoundationDraftProfile` 之上新增下面两层:
```ts
type SceneActAdvanceRule =
| 'after_primary_contact'
| 'after_active_step_complete'
| 'after_chapter_resolution';
interface CustomWorldFoundationDraftSceneAct {
id: string;
title: string;
summary: string;
stageCoverage: Array<
| 'opening'
| 'expansion'
| 'turning_point'
| 'climax'
| 'aftermath'
>;
backgroundImageSrc?: string | null;
backgroundAssetId?: string | null;
encounterNpcIds: string[];
primaryNpcId: string;
linkedThreadIds: string[];
actGoal: string;
transitionHook: string;
advanceRule: SceneActAdvanceRule;
}
interface CustomWorldFoundationDraftSceneChapter {
id: string;
sceneId: string;
sceneName: string;
title: string;
summary: string;
linkedThreadIds: string[];
linkedLandmarkIds: string[];
acts: CustomWorldFoundationDraftSceneAct[];
}
```
硬要求:
1. `primaryNpcId` 必须等于 `encounterNpcIds[0]`,不允许单独填写成别的角色。
2. 每幕必须至少有 `1` 个 NPC。
3. 每幕必须有 `backgroundImageSrc``backgroundAssetId`
4. `advanceRule` 由系统按幕位置默认编译,第一版不要求创作者手改。
## 6.2 发布到运行时的蓝图结构
创作草稿在发布时必须进一步编译成运行时蓝图:
```ts
interface SceneActBlueprint {
id: string;
sceneId: string;
title: string;
stageCoverage: Array<
| 'opening'
| 'expansion'
| 'turning_point'
| 'climax'
| 'aftermath'
>;
backgroundImageSrc?: string | null;
encounterNpcIds: string[];
primaryNpcId: string;
advanceRule:
| 'after_primary_contact'
| 'after_active_step_complete'
| 'after_chapter_resolution';
actGoal: string;
transitionHook: string;
}
interface SceneChapterBlueprint {
id: string;
sceneId: string;
title: string;
summary: string;
acts: SceneActBlueprint[];
}
```
建议把它挂入 `CustomWorldProfile` 的新字段中:
```ts
sceneChapterBlueprints?: SceneChapterBlueprint[] | null;
```
原因:
1. 现有 `landmarks` 只足够表达地点,不足够表达幕顺序。
2. 现有 `ChapterState` 是运行时状态,不适合直接兼做创作者蓝图。
3. 独立蓝图层更适合后端编译和发布校验。
## 6.3 聊天状态扩展
建议在现有 `StoryNpcChatState` 上新增有限聊天需要的状态:
```ts
interface StoryNpcChatState {
npcId: string;
npcName: string;
turnCount: number;
customInputPlaceholder?: string;
pendingQuestOffer?: {
quest: QuestLogEntry;
} | null;
sceneActId?: string | null;
turnLimit?: number | null;
remainingTurns?: number | null;
limitReason?: 'negative_affinity' | null;
forceExitAfterTurn?: boolean;
}
```
要求:
1. 正常无限聊天时,`turnLimit``remainingTurns``null`
2. 负好感主角色聊天时,`turnLimit=5`
3.`5` 轮结束后,`forceExitAfterTurn=true`,由后端明确告知前端结束当前聊天态。
## 6.4 NPC 聊天返回契约扩展
建议扩展 `NpcChatTurnResult`
```ts
type NpcChatTurnResult = {
npcReply: string;
affinityDelta: number;
affinityText: string;
suggestions: string[];
chatDirective?: {
turnLimit?: number | null;
remainingTurns?: number | null;
forceExit?: boolean;
closingMode?: 'free' | 'foreshadow_close';
};
};
```
这部分必须由后端给出,不允许前端自己猜。
---
## 7. 创作工具需求
## 7.1 入口与承载方式
本次必须继续复用现有:
1. `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx`
2. `src/components/custom-world-agent/CustomWorldDraftCardDetailModal.tsx`
3. `src/components/custom-world-agent/CustomWorldDraftEditPanel.tsx`
不新建独立页面。
新增规则:
1. 草稿抽屉必须正式支持 `scene_chapter` 分组。
2. `scene_chapter` 分组应位于 `chapter` 后、`thread` 前。
3. 点开 `scene_chapter` 草稿卡后,进入现有详情弹层和编辑面板体系。
4. 创作页面卡片摘要后续可增加 `sceneChapterCount`,但第一版不是阻塞项。
## 7.2 场景章节卡展示要求
每张 `scene_chapter` 草稿卡至少展示:
1. 场景名称
2. 章节标题
3. 幕数量
4. 已就绪背景图数量
5. 关联 NPC 数量
6. 关联线程数量
7. 当前风险数
详情页必须至少展示:
1. 场景摘要
2. 幕结构总览
3. 每幕的背景缩略图
4. 每幕的主角色
5. 每幕的辅助 NPC
6. 每幕目标
7. 每幕过渡钩子
## 7.3 幕编辑交互
每个场景章节卡的编辑区必须支持下面这些操作:
1. 新增幕
2. 删除幕
3. 调整幕顺序
4. 编辑幕标题
5. 编辑幕摘要
6. 绑定幕背景图
7. 配置幕相遇 NPC 顺序
8. 编辑幕目标
9. 编辑幕过渡钩子
交互要求:
1. 幕列表在桌面端纵向堆叠,在移动端同样保持纵向,不做复杂双列。
2. 每幕是独立卡片,不把所有字段一次性铺满。
3. 点击“配置背景图”时必须打开独立面板或独立弹层,不允许在当前卡片下方内联展开。
4. 点击“配置相遇 NPC”时必须打开独立面板或独立弹层不允许在当前卡片下方内联展开。
5. 默认不展示大段规则说明文字。
## 7.4 幕背景图配置
背景图配置必须复用现有场景图资产链,而不是另造上传体系。
要求如下:
1. 一幕只绑定一张正式背景图。
2. 可从已生成场景图中选择,也可调用现有场景图生成链生成。
3. 幕背景图和场景总背景图不是同一个概念,允许不同幕使用不同图。
4. 发布前如果存在未绑定背景图的幕,必须阻止发布。
5. 幕切换时运行时优先使用幕背景图,而不是地点默认图。
## 7.5 幕相遇 NPC 配置
NPC 配置面板必须支持:
1. 从当前世界的 `playableNpcs + storyNpcs` 中选择角色
2. 只展示与当前场景相关的优先推荐角色
3. 支持排序
4. 第一位角色明确标记为“主角色”
5. 允许同一角色出现在多个不同幕
硬约束:
1. 每幕至少 `1` 名 NPC。
2. 第一位 NPC 不能为空。
3. 不允许把不存在于当前世界角色池中的 id 写入幕配置。
4. 若主角色未与当前场景或线程建立任何关联,给出发布警告。
## 7.6 创作校验
`CustomWorldQualityFinding` 至少新增下面这些检查项:
1. `scene_chapter_missing_act`
2. `scene_act_missing_background`
3. `scene_act_missing_primary_npc`
4. `scene_act_missing_encounter_npc`
5. `scene_act_primary_npc_not_first`
6. `scene_act_unlinked_thread`
7. `scene_act_unpublished_background`
发布阻断项:
1. 幕数小于 `2`
2. 任意一幕没有背景图
3. 任意一幕没有 NPC
4. 任意一幕的第一 NPC 为空
---
## 8. 游戏流程需求
## 8.1 幕运行时状态
运行时必须为每个场景章节维护独立幕进度:
```ts
interface SceneActRuntimeState {
sceneId: string;
chapterId: string;
currentActId: string;
currentActIndex: number;
completedActIds: string[];
visitedActIds: string[];
}
```
建议挂入当前 story engine memory 中,和现有 `openedSceneChapterIds` 并存。
## 8.2 进入场景时的流程
当玩家进入一个有 `SceneChapterBlueprint` 的场景时:
1. 后端定位当前场景对应的 `scene chapter blueprint`
2. 如果该场景首次进入,则激活第 `1`
3. 如果该场景未完成且已有幕进度,则恢复到当前未完成幕
4. 把当前幕的背景图写入前端展示模型
5. 把当前幕的 `encounterNpcIds` 作为本幕优先相遇池
6. 把当前幕的 `stageCoverage` 交给 `chapterDirector` 参与裁决,并结合 quest 进度输出单一 `ChapterState.stage`
## 8.3 幕推进规则
第一版不要求创作者手填推进条件,而是由系统按幕位置默认编译:
1.`1` 幕默认 `after_primary_contact`
- 玩家与主角色发生首次有效接触后可进入下一幕判定
2. 中间幕默认 `after_active_step_complete`
- 当前场景章节任务 active step 完成后进入下一幕判定
3. 最后一幕默认 `after_chapter_resolution`
- 当前场景章节任务完成或进入可收束状态后结束本场景章节
要求:
1. 幕推进由后端统一裁决。
2. 前端只接收“幕已切换”的结果,不自行判断。
3. 幕切换后必须触发背景切换与相遇池更新。
## 8.4 幕切换表现
游戏前台在幕切换时必须至少做到:
1. 显示当前幕标题
2. 更新背景图
3. 更新当前可遇 NPC
4. 给出一条轻量系统提示,说明进入了新一幕
注意:
1. 不新建独立页面。
2. 不弹全屏说明面板。
3. 移动端优先保证幕标题与背景切换不遮挡底部操作区。
---
## 9. NPC 相遇与聊天规则
## 9.1 规则适用范围
本次新增的“按好感度控制聊天轮数”规则,只对**当前幕主角色**生效。
也就是说:
1. 当前幕 `primaryNpcId` 命中的角色,使用本次新规则。
2. 当前幕其他辅助 NPC第一版继续沿用现有 `npc_chat` 通用流程。
3. 辅助 NPC 的聊天不直接推进幕进度,除非后端另有章节 step 裁决。
## 9.2 主角色好感度大于 0
当当前幕主角色对玩家的当前好感度 `> 0` 时:
1. 玩家与其相遇后可以进入聊天态。
2. 聊天轮数无限制。
3. 继续沿用现有:
- `3` 个续聊建议项
- `1` 个自定义输入框
- 主动退出聊天
4. 只要满足 `docs/design/NPC_HIGH_AFFINITY_CHAT_QUEST_OFFER_FLOW_2026-04-19.md` 中的条件,仍然允许在聊天内抛出委托。
## 9.3 主角色好感度等于 0
为了避免编码边界歧义,本 PRD 先明确:
1. `affinity = 0` 视为中立档,不归入负好感限制分支。
2. 中立档允许进入正常多轮聊天。
3. 中立档不自动享受“高好感委托时机”。
这意味着:
- `> 0`:无限聊,且可进入高好感委托逻辑
- `= 0`:无限聊,但不进入高好感委托逻辑
- `< 0`:最多 `5`
## 9.4 主角色好感度小于 0
当当前幕主角色对玩家的当前好感度 `< 0` 时,必须进入 `limited hostile chat mode`
1. 允许进入聊天,但最多 `5` 轮。
2. 聊天状态中必须显示剩余轮数。
3.`1~4` 轮仍然走正常“玩家一句 -> NPC 一句 -> 建议项刷新”的基本结构。
4.`5` 轮不是普通续聊,而是强制收束轮。
5.`5` 轮必须输出一段带方向的收束回应,为后续剧情开展铺垫。
6.`5` 轮结束后:
- 自定义输入框隐藏
- 当前聊天态结束
- 恢复普通冒险态或进入后续 action 选择
## 9.5 第 5 轮的“铺垫”定义
“为开展铺垫”在本次 PRD 中必须被明确解释为:
**NPC 在第 5 轮必须抛出一个明确的后续方向,不能只用一句敌意台词把对话硬截断。**
可接受的铺垫结果包括:
1. 抛出新的威胁或最后通牒
2. 指向某个地点、人物或线索
3. 把矛盾推向对峙、交易、追踪或战斗
4. 暗示自己下一步行动去向
5. 给玩家一个必须接住的悬念或条件
不可接受的结果:
1. 纯重复敌意表达
2. 没有任何新方向的信息
3. 第 5 轮结束后界面直接空掉,没有后续承接
## 9.6 对当前负好感拦截逻辑的调整
当前若主角色属于本幕 `primaryNpcId`,则需要覆盖现有“负好感直接不给持续聊天”的逻辑。
新规则如下:
1. 如果它是当前幕主角色,即使当前好感度 `< 0`,也允许进入有限聊天态。
2. 只有在完成第 `5` 轮铺垫收束后,才切回普通探索/对峙流程。
3. 如果该 NPC 不是当前幕主角色,仍可沿用现有负好感拦截逻辑。
---
## 10. 前端表现要求
## 10.1 创作页
创作页必须保持清爽,不默认塞规则说明。
必须做到:
1. `scene_chapter` 卡片可见
2. 幕列表可编辑
3. 背景图选择和 NPC 选择都走独立面板
4. 移动端仍能完成幕排序、背景选择、NPC 排序
## 10.2 游戏主面板
Adventure 主面板在本次迭代中至少增加下面这些表现:
1. 当前幕标题或幕序号标签
2. 当前幕背景图切换
3. 主角色负好感聊天时的“剩余轮数”轻量提示
4. 第 5 轮结束后的过渡系统消息
禁止:
1. 默认展示大段规则介绍
2. 把幕配置说明直接写进玩家面板
3. 为了展示幕切换而新建独立剧情页面
---
## 11. 前后端职责边界
## 11.1 前端职责
前端只负责:
1. 渲染 `scene_chapter` 草稿卡与幕编辑 UI
2. 发起背景图配置和 NPC 配置请求
3. 渲染当前幕背景和幕标题
4. 渲染负好感聊天剩余轮数
5. 根据后端返回切换幕、退出聊天、展示后续 options
前端不负责:
1. 计算主角色是谁
2. 计算好感度轮数限制
3. 判定什么时候切幕
4. 决定第 5 轮要输出什么铺垫
5. 本地拼接下一幕 encounter 池
## 11.2 后端职责
后端必须负责:
1. 把创作页幕配置编译成运行时蓝图
2. 校验每幕背景与 NPC 配置完整性
3. 维护 `SceneActRuntimeState`
4. 进入场景时确定当前幕
5. 输出当前幕背景与 encounter 池
6. 裁决主角色聊天轮数限制
7. 在第 `5` 轮生成铺垫式收束回应
8. 在满足条件时推进到下一幕
---
## 12. 影响模块
本 PRD 落地时,至少会影响下面这些模块:
1. `packages/shared/src/contracts/customWorldAgent.ts`
- 新增场景多幕草稿结构
2. `src/types/customWorld.ts`
- 新增发布态 `sceneChapterBlueprints`
3. `server-node/src/services/customWorldAgentDraftCompiler.ts`
- 编译 `scene_chapter` 草稿卡
4. `server-node/src/services/customWorldAgentDraftEditService.ts`
- 支持场景幕的增删改排序
5. `server-node/src/services/customWorldAgentQualityService.ts`
- 增加幕背景和幕 NPC 校验
6. `src/components/custom-world-agent/CustomWorldAgentDraftDrawer.tsx`
- 展示 `scene_chapter` 分组
7. `src/components/custom-world-agent/CustomWorldDraftCardDetailModal.tsx`
- 展示幕详情
8. `src/components/custom-world-agent/CustomWorldDraftEditPanel.tsx`
- 新增幕编辑 UI
9. `src/data/questFlow.ts`
- 让 scene chapter quest 感知当前幕
10. `src/services/storyEngine/chapterDirector.ts`
- 用当前幕映射章节阶段和摘要
11. `src/hooks/story/npcEncounterActions.ts`
- 新增主角色有限聊天与第 5 轮收束逻辑
12. `packages/shared/src/contracts/story.ts`
- 扩展 `NpcChatTurnResult`
13. `src/services/aiService.ts`
- 透传有限聊天新字段
14. `server-node/src/modules/ai/chatOrchestrator.ts`
- 生成第 `5` 轮铺垫式收束结果
---
## 13. 验收标准
当下面这些结果都成立时,视为本次 PRD 已被正确落地:
1. 创作者可以在现有创作工作区中创建并编辑 `scene_chapter`
2. 每个场景章节都可以配置 `2~5` 幕。
3. 每一幕都可以绑定独立背景图。
4. 每一幕都可以配置有序 NPC 列表,第一位自动成为主角色。
5. 发布时缺少幕背景或幕 NPC 会被明确拦截。
6. 玩家进入场景后,当前幕背景图能正确显示。
7. 当前幕可遇 NPC 会按幕配置切换。
8. 当前幕主角色好感度 `> 0` 时可以无限续聊。
9. 当前幕主角色好感度 `< 0` 时最多只聊 `5` 轮。
10.`5` 轮结束后一定会出现为后续剧情开展铺垫的收束结果,而不是直接硬断。
11. 高好感委托链仍只在正好感聊天中触发。
12. 桌面端和移动端都能完成幕配置与幕切换使用。
---
## 14. 本稿默认假定
为了避免下一步编码时再出现语义歧义,这份 PRD 先明确采用下面两条默认假定:
1. `affinity = 0` 先按中立档处理:允许无限聊,但不进入高好感委托分支。
2. “第 5 轮为开展铺垫”先解释为“为后续剧情推进、对峙、追踪、交易或战斗制造明确下一跳”,而不是限定为必须开战。
如果后续你希望把:
- `affinity = 0` 改成也只聊 `5`
-`5` 轮明确收束到“开战前摇”
可以在下一版实现文档中单独收紧,不影响本稿主结构。

View File

@@ -1,6 +1,6 @@
# “我的”Tab 设置与账号安全 PRD
更新时间:`2026-04-16`
更新时间:`2026-04-19`
## 0. 目标
@@ -64,6 +64,13 @@
4. 更换手机号
5. 账号操作记录
交互层级要求补充为:
1. 设置首页只展示分区入口与危险操作,不在首页内联展开具体详情
2. 点击任一分区入口后,必须进入独立二级面板
3. 二级面板负责单一任务,不允许把详情继续堆在入口列表下面
4. 更换手机号属于独立操作面板,不允许在账号概况面板内直接展开表单
底部保留两个危险操作按钮:
1. 退出登录
@@ -84,6 +91,12 @@
这里只看信息,不做大编辑动作。
标题约束:
- 设置首页标题固定表达“设置”或“设置与账号安全”
- 设置首页标题区域不展示手机号,也不允许把手机号当作主标题替代昵称
- 手机号只允许出现在账号概况信息项中,以脱敏值展示
## 4.2 当前安全状态
展示当前账号命中的风控保护:
@@ -188,8 +201,11 @@
1. 设置继续采用当前账号弹窗基础形态即可
2. 移动端优先底部弹层,桌面端可居中弹窗
3. 更换手机号区域默认折叠
4. 危险操作按钮与普通按钮必须明显区分
3. 设置首页只保留分区入口,不直接承载分区详情内容
4. 分区详情必须通过独立子面板承载,移动端优先使用全宽底部子弹层,桌面端使用覆盖在设置首页之上的居中子面板
5. 更换手机号必须通过独立操作面板完成,不再使用当前面板内联展开表单
6. 危险操作按钮与普通按钮必须明显区分
7. 设置首页标题处禁止展示手机号、脱敏手机号或手机号形态的 displayName
---

View File

@@ -110,6 +110,14 @@
- 最后游玩时间
- 游戏信息
### 3.3.1 移动端卡片布局约束
- 移动端列表卡片中的封面只能作为独立缩略图或弱化背景层使用,不能直接占满整张卡片并压在正文信息下方。
- 标题、时间、摘要所在的信息区必须保持 `min-width: 0` 的可收缩布局,长标题不能把正文挤出屏幕外。
- 世界名称最多展示 2 行,游戏信息最多展示 3 行,超出后截断,不允许横向溢出。
- 时间标签、状态标签在窄屏下必须允许换行或独立成行,不能为了保持单行导致卡片内容错位。
- 列表卡片缩略图区域比例固定,文本区与缩略图区在移动端需要保持稳定对齐,避免出现上下参差和视觉歪斜。
其中“游戏信息”优先级如下:
1. `continueGameDigest`