@@ -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/**`
|
||||
|
||||
建议新增:
|
||||
|
||||
|
||||
@@ -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 页面保持清爽
|
||||
|
||||
|
||||
@@ -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` 轮明确收束到“开战前摇”
|
||||
|
||||
可以在下一版实现文档中单独收紧,不影响本稿主结构。
|
||||
@@ -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
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -110,6 +110,14 @@
|
||||
- 最后游玩时间
|
||||
- 游戏信息
|
||||
|
||||
### 3.3.1 移动端卡片布局约束
|
||||
|
||||
- 移动端列表卡片中的封面只能作为独立缩略图或弱化背景层使用,不能直接占满整张卡片并压在正文信息下方。
|
||||
- 标题、时间、摘要所在的信息区必须保持 `min-width: 0` 的可收缩布局,长标题不能把正文挤出屏幕外。
|
||||
- 世界名称最多展示 2 行,游戏信息最多展示 3 行,超出后截断,不允许横向溢出。
|
||||
- 时间标签、状态标签在窄屏下必须允许换行或独立成行,不能为了保持单行导致卡片内容错位。
|
||||
- 列表卡片缩略图区域比例固定,文本区与缩略图区在移动端需要保持稳定对齐,避免出现上下参差和视觉歪斜。
|
||||
|
||||
其中“游戏信息”优先级如下:
|
||||
|
||||
1. `continueGameDigest`
|
||||
|
||||
Reference in New Issue
Block a user