255
docs/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md
Normal file
255
docs/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md
Normal file
@@ -0,0 +1,255 @@
|
||||
# 冒险运行时开发经验沉淀
|
||||
|
||||
日期:2026-03-24
|
||||
|
||||
## 1. 这轮工作主要覆盖了什么
|
||||
|
||||
这轮迭代不是单点 UI 修改,而是同时改了 4 条链路:
|
||||
|
||||
1. 战斗演绎链路:近战突进、远程施法、投射物/特效、受击结算时机。
|
||||
2. 探索链路:向前探索、呼喊、NPC 离队、切换场景时的新 encounter 生成与入场动画。
|
||||
3. NPC function 链路:聊天、招募、观察征兆、交易、离队、上下文注入边界。
|
||||
4. 面板链路:队伍页、角色详情、背包页、移动端空间分配、入口 icon 语义。
|
||||
|
||||
这类项目最重要的判断是:
|
||||
很多需求表面看是“改个界面”或“改句文案”,实际同时影响状态机、动画时序、AI 提示词、实体生成规则和移动端布局。
|
||||
|
||||
## 2. 这轮最值得沉淀的结论
|
||||
|
||||
### 2.1 function 的职责边界必须先定死
|
||||
|
||||
后面事实证明,只有少数 function 应该负责生成新 encounter:
|
||||
|
||||
- `idle_explore_forward`
|
||||
- `idle_call_out`
|
||||
- `npc_leave`
|
||||
- 进入新场景
|
||||
|
||||
其他 function 如果也偷偷新增 NPC、怪物、宝箱,后面一定会出现这些问题:
|
||||
|
||||
- 剧情上下文越来越乱
|
||||
- 场景上实体数量失控
|
||||
- 玩家以为“聊天/招募/观察”也会强行推进世界
|
||||
- AI 生成内容和本地规则互相打架
|
||||
|
||||
经验:
|
||||
先定义“谁能创造世界实体”,再写文案和动画。
|
||||
|
||||
### 2.2 动画完成点必须和数值结算点绑定
|
||||
|
||||
这轮一个关键修正是:
|
||||
攻击特效播放完后,受击目标才扣血。
|
||||
|
||||
如果不这样做,玩家看到的就是:
|
||||
|
||||
- 特效还没打到,血条先掉了
|
||||
- 投射物还在飞,目标已经结算
|
||||
- dash 还没到位,攻击已经命中
|
||||
|
||||
经验:
|
||||
战斗系统里不能只写“播放动画”,必须写清楚:
|
||||
|
||||
1. 起手阶段是什么
|
||||
2. 位移阶段是什么
|
||||
3. 命中阶段是什么
|
||||
4. 哪个阶段结束后才改数值
|
||||
|
||||
### 2.3 视觉素材选择要按“语义 + 小尺寸可读性”来做
|
||||
|
||||
这轮做过两类视觉判断:
|
||||
|
||||
- 战斗特效素材该怎么挂到角色/技能上
|
||||
- 冒险页入口 icon 应该换成什么
|
||||
|
||||
经验不是“素材越花越好”,而是:
|
||||
|
||||
- 先看素材本身表达的动作语义
|
||||
- 再看缩小到按钮尺寸后是否还能一眼看懂
|
||||
- 最后才考虑风格统一
|
||||
|
||||
例如:
|
||||
|
||||
- 队伍入口用“盔甲”不如用“盾牌”更像角色/编队
|
||||
- 背包入口用真正的包袋图,比木板式 HUD 图更直观
|
||||
- 远程施法不移动时,特效就必须承担“动作已经发生”的视觉职责
|
||||
|
||||
### 2.4 角色型 NPC 和普通 encounter 不能混着处理
|
||||
|
||||
这轮踩过的坑说明:
|
||||
|
||||
- 可扮演但未入队的 NPC
|
||||
- 已入队同伴
|
||||
- 普通场景 NPC
|
||||
|
||||
这三者虽然都可能显示为 NPC,但数据完整度、立绘来源、详情页能力并不一样。
|
||||
|
||||
经验:
|
||||
|
||||
- 角色型 NPC 要按角色数据链路渲染
|
||||
- 普通 NPC 要按 encounter/NPC state 渲染
|
||||
- 详情弹窗必须对缺失字段做空值保护
|
||||
|
||||
否则就会出现点击小人直接报 `map` 相关错误这类问题。
|
||||
|
||||
### 2.5 角色和同伴的朝向/位移必须彼此独立
|
||||
|
||||
`idle_observe_signs` 的修正说明了一件事:
|
||||
如果主角和同伴在 transform、朝向、随机停顿上仍然存在隐含父子级关系,最后视觉表现一定不自然。
|
||||
|
||||
经验:
|
||||
|
||||
- 主角随机转向和同伴随机转向要分别计算
|
||||
- 位置、朝向、停留时长都要独立采样
|
||||
- 不要让父容器顺手把子角色的朝向也带过去
|
||||
|
||||
这条经验同样适用于:
|
||||
|
||||
- 招募入队时的新同伴 dash
|
||||
- 多角色待机观察
|
||||
- 场景内多实体同步演绎
|
||||
|
||||
### 2.6 大模型负责“推理与叙述”,本地规则负责“世界变更”
|
||||
|
||||
这轮对 `npc_chat`、`npc_recruit`、`idle_observe_signs` 的调整,本质上都指向同一个原则:
|
||||
|
||||
- 大模型可以生成观察结果、聊天内容、氛围描述
|
||||
- 大模型不应该偷偷新增实体、替玩家做决定、绕开本地规则结算
|
||||
|
||||
经验:
|
||||
|
||||
1. 聊天生成要明确禁止提及其他 function 行为。
|
||||
2. 招募对话可以引导到成功入队,但最终入队仍应由本地流程触发。
|
||||
3. `idle_observe_signs` 可以走大模型推理,但写入剧情上下文的内容要可控。
|
||||
4. `npc_recruit` 这种流程不要顺手给下次推理塞一个“新 NPC”。
|
||||
|
||||
一句话总结:
|
||||
AI 可以解释世界,但不能私自改世界。
|
||||
|
||||
### 2.7 移动端面板要优先保信息密度,不要保装饰
|
||||
|
||||
这轮背包和队伍页的调整很能说明问题:
|
||||
|
||||
- 队伍页只保留成员列表
|
||||
- 成员详情放到弹窗
|
||||
- 背包页去掉多余标题、提示文案、厚重背景框
|
||||
- 装备信息移到角色详情,不和背包抢空间
|
||||
|
||||
经验:
|
||||
|
||||
- 主面板只放“高频扫读信息”
|
||||
- 低频详情放二级弹窗
|
||||
- 任何重复信息都要尽量去重
|
||||
|
||||
在小屏上,空间不是拿来“显得完整”的,而是拿来“保证可操作”的。
|
||||
|
||||
## 3. 这轮最典型的踩坑记录
|
||||
|
||||
### 3.1 encounter 生成距离只改一处是不够的
|
||||
|
||||
一开始只是把某个函数里的怪物生成位置往后挪,但后来发现:
|
||||
|
||||
- 其他会生成 encounter 的 function 仍然太近
|
||||
- 新场景进入时也可能直接出现在屏幕里
|
||||
|
||||
经验:
|
||||
“从屏幕外进场、要走 4 秒距离”必须是统一约束,不是某个函数里的特判。
|
||||
|
||||
### 3.2 详情文案改源头不一定等于改到了最终显示层
|
||||
|
||||
这轮“选项小字太长”的问题最后证明:
|
||||
真正该改的是渲染层显示的 `detailText`,而不是只改某个上游数据源。
|
||||
|
||||
经验:
|
||||
用户说“没有生效”时,要优先检查最终渲染层,而不是只检查中间数据。
|
||||
|
||||
### 3.3 新同伴入队时,尺寸问题本质是缩放基准不统一
|
||||
|
||||
招募流程里位置正确但大小不对,说明问题不在路径,而在:
|
||||
|
||||
- 新同伴使用的缩放基准
|
||||
- 主角扮演角色使用的缩放基准
|
||||
|
||||
不一致。
|
||||
|
||||
经验:
|
||||
只要角色加入到主队镜头体系里,尺寸基准必须复用主角/同伴那套规则,不能临时另开一套。
|
||||
|
||||
### 3.4 背包和角色详情职责不清,会持续挤压移动端布局
|
||||
|
||||
这轮背包页一度同时承担:
|
||||
|
||||
- 物品网格
|
||||
- 装备总览
|
||||
- 装备卸下
|
||||
- 详情查看
|
||||
|
||||
最后结果就是移动端空间不够、信息层级混乱。
|
||||
|
||||
经验:
|
||||
|
||||
- 背包负责“物品”
|
||||
- 角色详情负责“装备”
|
||||
|
||||
职责一旦分清,后面很多 UI 冲突会自然消失。
|
||||
|
||||
## 4. 可复用的开发方法
|
||||
|
||||
### 4.1 新增一个 function 前,先问 6 个问题
|
||||
|
||||
1. 它会不会生成新 encounter?
|
||||
2. 它会不会写入后续推理上下文?
|
||||
3. 它的动画分几段?
|
||||
4. 哪一刻才算真正生效?
|
||||
5. 哪些内容由 AI 生成,哪些必须本地决定?
|
||||
6. 它是否需要从屏幕外入场?
|
||||
|
||||
这 6 个问题先答清,后面返工会少很多。
|
||||
|
||||
### 4.2 配战斗动画时,至少明确 4 个时点
|
||||
|
||||
1. 起手
|
||||
2. 位移/施法
|
||||
3. 特效命中
|
||||
4. 扣血/击退/结算
|
||||
|
||||
特别是:
|
||||
|
||||
- 近战攻击前的 dash
|
||||
- 远程不移动但要挂特效
|
||||
- 受击延后到命中特效之后
|
||||
|
||||
都属于这套时点设计。
|
||||
|
||||
### 4.3 做素材替换时,不要只看资源名
|
||||
|
||||
正确顺序更接近:
|
||||
|
||||
1. 打开素材看实际形状
|
||||
2. 判断它在当前语义里是不是“最像这个功能”
|
||||
3. 缩到真实 UI 尺寸再看一遍
|
||||
4. 再决定 active/inactive 怎么处理
|
||||
|
||||
### 4.4 做聊天流式界面时,反馈一定要插到正在发生的流里
|
||||
|
||||
这轮加“好感度 +x”提示后,更明确了一件事:
|
||||
|
||||
- 系统反馈不应该只藏在最终结果
|
||||
- 要插进聊天进行中的体验里
|
||||
- 文本流超出剧情框时要自动滚动
|
||||
|
||||
否则用户会错过真正重要的状态变化。
|
||||
|
||||
## 5. 以后继续做这类需求时,建议坚持的原则
|
||||
|
||||
- 先收拢 function 边界,再改剧情文案。
|
||||
- 先确定动画结算时序,再接特效素材。
|
||||
- 先做本地规则兜底,再让大模型生成文本。
|
||||
- 先保证移动端可扫读,再考虑装饰性面板。
|
||||
- 先复用已有角色/场景坐标体系,再做个别修正。
|
||||
- 先看最终显示层,再判断“改动是否生效”。
|
||||
|
||||
## 6. 这轮最重要的一句话总结
|
||||
|
||||
这类 AI 冒险 RPG 的开发,最难的不是“把功能做出来”,而是:
|
||||
|
||||
**让 function 边界、世界状态、视觉演绎、移动端面板和大模型文本在同一套规则下稳定协作。**
|
||||
116
docs/AGENT_UI_CHANGELOG.md
Normal file
116
docs/AGENT_UI_CHANGELOG.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# UI 改动记录(供后续 Agent 阅读)
|
||||
|
||||
本文档汇总 **像素 RPG UI 皮肤化** 相关实现与决策,便于新会话快速接手。更细的命名与规范见同目录上一级的 **`UI_CODING_STANDARD.md`**。
|
||||
|
||||
---
|
||||
|
||||
## 1. 相关文件一览
|
||||
|
||||
| 路径 | 作用 |
|
||||
|------|------|
|
||||
| `UI_CODING_STANDARD.md` | 资源目录约定、9-slice 规则、图标语义、`Icons`/`UI` 命名解读、已知问题(含世界按钮切片) |
|
||||
| `src/uiAssets.ts` | **唯一推荐** 的 UI 资源映射:`UI_CHROME`(9-slice 配置)、`TAB_ICONS`、`WORLD_SELECT_ICONS`、`getNineSliceStyle()` |
|
||||
| `src/components/PixelIcon.tsx` | 小图标 `<img>`,`image-rendering: pixelated` |
|
||||
| `src/index.css` | `.pixel-nine-slice`、`.pixel-root-shell` / `.pixel-app-shell`、tab/按钮布局类、`--ui-scale` |
|
||||
| `src/App.tsx` | 世界选择、角色卡、底部 tab、剧情/背包面板、地图弹窗、`MudMapRoom` |
|
||||
| `src/components/GameCanvas.tsx` | 场景名按钮(9-slice `Title_frame_m`) |
|
||||
| `vite.config.ts` | `root` / `envDir` 指向 `__dirname`,保证 `.env.local` 从项目根加载 |
|
||||
| `public/UI/`、`public/Icons/` | 静态资源(路径以 `/UI/...`、`/Icons/...` 引用) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 架构要点
|
||||
|
||||
### 2.1 9-slice(禁止整图 `background-size: 100% 100%` 拉框体)
|
||||
|
||||
- 使用 CSS **`border-image`** + **`fill`**,通过自定义属性注入切片参数。
|
||||
- 类名:**`pixel-nine-slice`**(定义在 `src/index.css`)。
|
||||
- 行内样式由 **`getNineSliceStyle(texture, overrides?)`** 生成(`src/uiAssets.ts`),设置 `--frame-src`、`--slice-*`、`--frame-pad-*`、`--frame-repeat`、可选 `--frame-base`(一般仅在图源中心确实透明时使用)。
|
||||
|
||||
### 2.2 响应与缩放
|
||||
|
||||
- `:root` 上 **`--ui-scale`**:`clamp`(桌面)+ 小屏媒体查询内固定约 `0.8`。
|
||||
- 边框宽度 = 切片像素 × `--ui-scale`,与 padding 同逻辑,避免移动端边框比例失控。
|
||||
|
||||
### 2.3 交互
|
||||
|
||||
- **`pixel-pressable`**:hover 用 `translateY` + `brightness`,**避免**对像素框体做 `scale()` 以免糊边。
|
||||
|
||||
### 2.4 全局背景
|
||||
|
||||
- **根布局**(`App.tsx` 最外层):`pixel-root-shell` + `Background_fill.png` 平铺 + 深色半透明渐变(替代纯 `#050505` 死底)。
|
||||
- **下半屏内容区** `pixel-app-shell`:同一纹理 + **更轻**的遮罩,让纹理更明显。
|
||||
- 开局标题区已去掉单独 `bg-zinc-950`,与根背景一致。
|
||||
|
||||
---
|
||||
|
||||
## 3. `UI_CHROME` 当前用途(速查)
|
||||
|
||||
以下为 `src/uiAssets.ts` 中主要键与界面位置的对应关系(切片数值以文件内为准):
|
||||
|
||||
| Key | 资源(示例) | 用途 |
|
||||
|-----|----------------|------|
|
||||
| `appBackground` | `Background_fill.png` | 根壳 + 下半屏平铺底 |
|
||||
| `worldButtonWuxia` / `worldButtonXianxia` | `1_orange_button` / `1_violet_button` | 开局武侠/仙侠(**条高 28px**,切片见下文) |
|
||||
| `characterCardFrame` | `pick_hero_frame` | 选角卡片 |
|
||||
| `tabActive` / `tabInactive` | `Shop_tab_picked` / `Shop_tab` | 底部「角色 / 冒险 / 背包」 |
|
||||
| `panel` | `Frame_bg_big_2` | 装备区等通用面板 |
|
||||
| `storyPanel` | `Dialogue_frame` | 剧情正文区 |
|
||||
| `inventoryPanel` | `Inventory_bg` | 背包条目 |
|
||||
| `statsPanel` | `Stats_bar` | 角色数值面板 |
|
||||
| `choiceButton` | `Options_bar` | 剧情选项按钮 |
|
||||
| `modalPanel` | `Popup_window` | 地图弹窗外壳 |
|
||||
| `infoPanel` | `Dialogue_frame` | 地图弹窗内「当前地点 / 可前往」信息块 |
|
||||
| `sceneTitle` | `Title_frame_m` | 战斗画布顶部场景名按钮 |
|
||||
| `mapRoomCell` | `Map_frame` | 地图节点卡片(`MudMapRoom`) |
|
||||
| `mapDiagramPanel` | `Frame_bg_big_2` | 地图关系图整体衬底 |
|
||||
|
||||
图标路径:`TAB_ICONS`、`WORLD_SELECT_ICONS`、`CHROME_ICONS`;装备槽与背包分类见 `getEquipmentSlotIcon` / `getInventoryCategoryIcon`。
|
||||
|
||||
---
|
||||
|
||||
## 4. 已知坑(必读)
|
||||
|
||||
### 4.1 世界选择按钮「中间发空」
|
||||
|
||||
- 图源 **`1_orange_button.png` / `1_violet_button.png` 为 125×28**。
|
||||
- 若 **`slice.top + slice.bottom >= 28`**,中间横带高度为 **0**,`border-image` 的 `fill` 异常,看起来像透明/没画上。
|
||||
- **正确做法**:减小上下切片(当前为 **9+9**),并配合 `repeat: 'stretch'` 竖向拉伸条形成品。
|
||||
- **不推荐**:用大色块 `baseColor` 糊底(与素材内渐变/内框不一致)。详见 `UI_CODING_STANDARD.md`「已知问题」。
|
||||
|
||||
### 4.2 新增 9-slice 前
|
||||
|
||||
先读 PNG **宽高**,保证 **`top+bottom < height`** 且 **`left+right < width`**。
|
||||
|
||||
---
|
||||
|
||||
## 5. 环境与运行(与 UI 无关但易踩坑)
|
||||
|
||||
- LLM:`VITE_LLM_*` 写在项目根 **`.env.local`**;因 `vite.config.ts` 固定 `envDir: __dirname`,从任意 cwd 启动也应能读到。
|
||||
- 开发:`npm run dev`(默认端口见 `package.json`)。
|
||||
|
||||
---
|
||||
|
||||
## 6. 尚未统一成 UI 图、仍用 Tailwind 小块面的区域(可选后续)
|
||||
|
||||
若产品要求「全盘无纯色板」,可继续替换,当前仍可能为 `bg-black/20`、`rounded-lg border` 等:
|
||||
|
||||
- 角色页装备行、属性格子
|
||||
- 地图弹窗顶栏分隔、关闭按钮外圈
|
||||
- `GameCanvas` 内血条、名字条等 HUD
|
||||
|
||||
---
|
||||
|
||||
## 7. 修改清单摘要(按主题)
|
||||
|
||||
1. **资源规范**:`UI_CODING_STANDARD.md` + 集中映射 `uiAssets.ts`。
|
||||
2. **9-slice 系统**:`index.css` + `getNineSliceStyle`。
|
||||
3. **开局 / 主界面**:世界按钮、选角卡、tab、面板、选项、背包、根与内容区背景纹理。
|
||||
4. **地图弹窗**:外壳、信息板、节点 `Map_frame`、关系图 `Frame_bg_big_2`。
|
||||
5. **画布**:场景名 `Title_frame_m`。
|
||||
6. **世界按钮**:按真实像素尺寸修正切片,去掉错误 `baseColor` 方案。
|
||||
7. **Vite**:`root`/`envDir` 保证环境变量加载稳定。
|
||||
|
||||
---
|
||||
|
||||
*文档目的:交接给下一个 Agent 时,优先读本文件 + `UI_CODING_STANDARD.md`,再改 `uiAssets.ts` / `App.tsx` / `index.css`。*
|
||||
1194
docs/AI_CHARACTER_ANIMATION_TECHNICAL_SOLUTION_2026-04-04.md
Normal file
1194
docs/AI_CHARACTER_ANIMATION_TECHNICAL_SOLUTION_2026-04-04.md
Normal file
File diff suppressed because it is too large
Load Diff
650
docs/AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md
Normal file
650
docs/AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md
Normal file
@@ -0,0 +1,650 @@
|
||||
# AI 原生运行时物品生成系统重设计
|
||||
|
||||
更新时间:`2026-04-02`
|
||||
|
||||
## 0. 这次重设计要解决什么
|
||||
|
||||
基于当前仓库已经存在的系统,这次不再把“AI 原生物品生成”设计成一套独立玩法,而是把它重做成:
|
||||
|
||||
**挂在现有背包、build、宝藏、NPC、任务、自定义世界之上的统一运行时物品导演系统。**
|
||||
|
||||
它要解决的核心问题有 4 个:
|
||||
|
||||
1. 当前物品入口很多,但缺少统一导演层。
|
||||
2. 当前奖励能发物品,但和场景背景、相关 NPC、最近事件的贴合度不够高。
|
||||
3. 当前 build 标签体系已经存在,但运行时奖励还没有围绕“永久标签 / 限时标签 / 小数值补充”形成稳定规则。
|
||||
4. AI 目前只负责叙事包装,没有真正进入运行时物品生成链路。
|
||||
|
||||
这次重设计的目标不是“让 AI 直接生成 `InventoryItem`”,而是:
|
||||
|
||||
- 让 AI 负责物品的**叙事意图、关系语义、世界贴合**
|
||||
- 让本地规则负责物品的**标签、数值、稀有度、存档、平衡**
|
||||
|
||||
## 1. 设计结论先说
|
||||
|
||||
新的 AI 原生物品系统建议采用:
|
||||
|
||||
**上下文采样器 -> AI 物品意图层 -> 本地编译器 -> 渠道分发器 -> 叙事回写器**
|
||||
|
||||
其中:
|
||||
|
||||
- `InventoryItem` 继续作为唯一成品结构
|
||||
- `ItemBuildProfile` 继续承载永久 build 标签
|
||||
- `ItemUseProfile.buildBuffs` 继续承载限时 build 标签
|
||||
- `ItemStatProfile` 继续承载小数值加成
|
||||
- `buildTags.ts / buildDamage.ts` 继续承接战斗结算
|
||||
- `treasureInteractions.ts / npcInteractions.ts / forgeSystem.ts / customWorldRuntime.ts` 改为调用统一导演层
|
||||
|
||||
一句话:
|
||||
|
||||
**不要再按“宝藏怎么发、NPC 怎么卖、任务怎么奖”各写一套;而是让所有入口共用同一个运行时物品生成管线。**
|
||||
|
||||
## 2. 这套系统必须遵守的边界
|
||||
|
||||
## 2.1 AI 负责语义,不负责数值
|
||||
|
||||
AI 可以决定:
|
||||
|
||||
- 这件物品像什么
|
||||
- 它为什么在这里出现
|
||||
- 它和哪个 NPC / 势力 / 地标 / 怪物有关
|
||||
- 它更偏向什么 build 风格
|
||||
- 它应该是永久物还是限时物
|
||||
|
||||
AI 不应该直接决定:
|
||||
|
||||
- 最终 `outgoingDamageBonus` 是多少
|
||||
- 能带几个标签
|
||||
- 允许不允许进入装备槽
|
||||
- 价值多少
|
||||
- 能不能掉落
|
||||
- 能不能持久化
|
||||
|
||||
## 2.2 所有成品都必须编译回当前系统
|
||||
|
||||
新的系统不是新物品结构,而是新生成过程。
|
||||
|
||||
最终成品仍然必须落到:
|
||||
|
||||
- `InventoryItem`
|
||||
- `ItemStatProfile`
|
||||
- `ItemUseProfile`
|
||||
- `ItemBuildProfile`
|
||||
|
||||
否则现有:
|
||||
|
||||
- 背包
|
||||
- 装备
|
||||
- build 计算
|
||||
- 锻造
|
||||
- 存档
|
||||
- 交易
|
||||
|
||||
都接不上。
|
||||
|
||||
## 2.3 运行时物品的主收益必须是“构筑意义”
|
||||
|
||||
重新设计后,运行时物品的收益优先级建议固定为:
|
||||
|
||||
1. `build 标签`
|
||||
2. `功能性节奏收益`
|
||||
3. `小数值补充`
|
||||
|
||||
不建议反过来做成:
|
||||
|
||||
1. 大数值
|
||||
2. build 标签只是点缀
|
||||
|
||||
因为当前项目最强的系统基础已经是 build 标签和叙事关系,而不是传统数值刷装。
|
||||
|
||||
## 3. 新系统的整体架构
|
||||
|
||||
## 3.1 模块拆分
|
||||
|
||||
建议新增 5 个模块:
|
||||
|
||||
- `src/types/runtimeItem.ts`
|
||||
- `src/data/runtimeItemContext.ts`
|
||||
- `src/data/runtimeItemDirector.ts`
|
||||
- `src/data/runtimeItemCompiler.ts`
|
||||
- `src/data/runtimeItemNarrative.ts`
|
||||
|
||||
职责如下。
|
||||
|
||||
### A. `runtimeItemContext.ts`
|
||||
|
||||
负责统一采样生成上下文,不直接生成物品。
|
||||
|
||||
输入来自:
|
||||
|
||||
- `GameState`
|
||||
- `currentEncounter`
|
||||
- `currentScenePreset`
|
||||
- `npcStates`
|
||||
- `storyHistory`
|
||||
- `playerEquipment`
|
||||
- `activeBuildBuffs`
|
||||
|
||||
输出统一上下文对象:
|
||||
|
||||
```ts
|
||||
type RuntimeItemGenerationContext = {
|
||||
worldType: WorldType | null;
|
||||
customWorldProfile: CustomWorldProfile | null;
|
||||
sceneId: string | null;
|
||||
sceneName: string | null;
|
||||
sceneDescription: string | null;
|
||||
sceneTags: string[];
|
||||
treasureHints: string[];
|
||||
encounter: Encounter | null;
|
||||
encounterNpcId: string | null;
|
||||
encounterNpcName: string | null;
|
||||
encounterContextText: string | null;
|
||||
relatedNpcState: NpcPersistentState | null;
|
||||
recentStorySummary: string;
|
||||
recentActions: string[];
|
||||
playerCharacterId: string;
|
||||
playerBuildTags: string[];
|
||||
playerBuildGaps: string[];
|
||||
playerEquipmentTags: string[];
|
||||
generationChannel: RuntimeItemGenerationChannel;
|
||||
};
|
||||
```
|
||||
|
||||
### B. `runtimeItemDirector.ts`
|
||||
|
||||
负责根据上下文决定:
|
||||
|
||||
- 这次该不该生成上下文化物品
|
||||
- 生成几件
|
||||
- 主奖励 / 副奖励是什么
|
||||
- 是装备、消耗品、材料还是稀有物
|
||||
- 偏永久 build、限时 build,还是纯功能补给
|
||||
|
||||
它的输出不是成品,而是“导演结果”:
|
||||
|
||||
```ts
|
||||
type RuntimeItemPlan = {
|
||||
slot: "primary" | "secondary" | "support";
|
||||
itemKind: "equipment" | "consumable" | "material" | "relic" | "quest";
|
||||
permanence: "permanent" | "timed" | "resource";
|
||||
narrativeWeight: "light" | "medium" | "heavy";
|
||||
targetBuildDirection: string[];
|
||||
relationAnchor: RuntimeRelationAnchor;
|
||||
};
|
||||
```
|
||||
|
||||
### C. `runtimeItemCompiler.ts`
|
||||
|
||||
负责把导演计划 + AI 意图编译成正式 `InventoryItem`。
|
||||
|
||||
编译职责包括:
|
||||
|
||||
- build 标签规范化
|
||||
- 稀有度预算分配
|
||||
- 永久标签上限
|
||||
- 限时标签时长
|
||||
- 数值预算
|
||||
- 装备槽判定
|
||||
- 价值计算
|
||||
- metadata 生成
|
||||
|
||||
### D. `runtimeItemNarrative.ts`
|
||||
|
||||
负责把已经生成好的物品回写成叙事文本:
|
||||
|
||||
- 物品命名
|
||||
- 物品描述
|
||||
- 物品来源说明
|
||||
- 宝藏/NPC/任务文案嵌入
|
||||
|
||||
### E. `runtimeItem.ts`
|
||||
|
||||
负责声明:
|
||||
|
||||
- 渠道类型
|
||||
- 关系锚点类型
|
||||
- 运行时物品 metadata
|
||||
- AI 意图结构
|
||||
- 编译预算结构
|
||||
|
||||
## 3.2 AI 在新架构里的输入输出
|
||||
|
||||
建议 AI 只接触两种结构:
|
||||
|
||||
### 输入:压缩过的生成上下文
|
||||
|
||||
```ts
|
||||
type RuntimeItemAiPromptInput = {
|
||||
worldSummary: string;
|
||||
sceneSummary: string;
|
||||
encounterSummary: string;
|
||||
relatedNpcSummary: string;
|
||||
recentStorySummary: string;
|
||||
generationChannel: RuntimeItemGenerationChannel;
|
||||
playerBuildDirection: string[];
|
||||
playerBuildGaps: string[];
|
||||
desiredItemKind: string;
|
||||
permanence: string;
|
||||
};
|
||||
```
|
||||
|
||||
### 输出:轻量意图
|
||||
|
||||
```ts
|
||||
type RuntimeItemAiIntent = {
|
||||
shortNameSeed: string;
|
||||
sourcePhrase: string;
|
||||
reasonToAppear: string;
|
||||
relationHooks: string[];
|
||||
desiredBuildTags: string[];
|
||||
desiredFunctionalBias: Array<"heal" | "mana" | "cooldown" | "guard" | "damage">;
|
||||
tone: "grim" | "mysterious" | "martial" | "ritual" | "survival";
|
||||
};
|
||||
```
|
||||
|
||||
AI 输出长度必须短,目的明确,不允许直接产出成品 JSON 大对象。
|
||||
|
||||
## 4. 新系统里的物品收益模型
|
||||
|
||||
## 4.1 三种收益层
|
||||
|
||||
新系统建议把运行时物品分成三类收益层。
|
||||
|
||||
### 第一层:永久 build 标签
|
||||
|
||||
适用于:
|
||||
|
||||
- 武器
|
||||
- 护甲
|
||||
- 饰品
|
||||
- 稀有遗物
|
||||
|
||||
载体:
|
||||
|
||||
- `buildProfile.role`
|
||||
- `buildProfile.tags`
|
||||
- `buildProfile.setId / setName`
|
||||
|
||||
建议限制:
|
||||
|
||||
- 普通运行时装备:`1` 个主标签
|
||||
- 稀有以上:`1` 主标签 + `1` 协同标签
|
||||
- 传说或剧情物:允许带 `2` 标签 + 关系锚点
|
||||
|
||||
### 第二层:限时 build 标签
|
||||
|
||||
适用于:
|
||||
|
||||
- 药剂
|
||||
- 符箓
|
||||
- 战场工具
|
||||
- 一次性应急物
|
||||
|
||||
载体:
|
||||
|
||||
- `useProfile.buildBuffs`
|
||||
|
||||
建议限制:
|
||||
|
||||
- `1~2` 个标签
|
||||
- `1~3` 回合
|
||||
- 默认刷新持续时间,不建议无限叠层
|
||||
|
||||
### 第三层:少量直接数值
|
||||
|
||||
适用于:
|
||||
|
||||
- 补足 build 短板
|
||||
- 强化渠道差异
|
||||
- 给物品明确手感
|
||||
|
||||
载体:
|
||||
|
||||
- `statProfile`
|
||||
- `useProfile`
|
||||
|
||||
建议数值定位:
|
||||
|
||||
- 永远是“辅助收益”
|
||||
- 不和 build 标签争主导权
|
||||
|
||||
## 4.2 玩家 build 缺口驱动
|
||||
|
||||
建议新系统在生成物品时先判断当前玩家缺口,而不是先随机抽品类。
|
||||
|
||||
建议至少识别这些缺口:
|
||||
|
||||
- `survival_gap`
|
||||
- 当前缺 `守御 / 护体 / 回复 / 续战`
|
||||
- `mana_gap`
|
||||
- 当前缺 `法力 / 冷却 / 节奏恢复`
|
||||
- `finisher_gap`
|
||||
- 当前缺 `爆发 / 重击 / 追击`
|
||||
- `mobility_gap`
|
||||
- 当前缺 `突进 / 快袭 / 风行 / 游击`
|
||||
- `control_gap`
|
||||
- 当前缺 `控场 / 符阵 / 镇邪`
|
||||
|
||||
运行时物品应优先:
|
||||
|
||||
- 补当前明显缺口
|
||||
- 或强化当前已经成型的主方向
|
||||
|
||||
## 5. 如何让物品和背景 / NPC / 场景高度贴合
|
||||
|
||||
## 5.1 必须引入“关系锚点”
|
||||
|
||||
建议每个 AI 原生运行时物品都必须绑定至少一个 `relationAnchor`:
|
||||
|
||||
```ts
|
||||
type RuntimeRelationAnchor =
|
||||
| { type: "npc"; npcId?: string; npcName: string; roleText?: string }
|
||||
| { type: "scene"; sceneId?: string; sceneName: string }
|
||||
| { type: "landmark"; landmarkName: string }
|
||||
| { type: "monster"; monsterId?: string; monsterName: string }
|
||||
| { type: "faction"; factionName: string }
|
||||
| { type: "quest"; questId?: string; questName: string };
|
||||
```
|
||||
|
||||
如果没有锚点,物品最多只能作为普通补给,不进入“AI 原生重点物品”。
|
||||
|
||||
## 5.2 不同渠道对应不同贴合逻辑
|
||||
|
||||
### 宝藏
|
||||
|
||||
物品必须同时贴合:
|
||||
|
||||
- 当前场景
|
||||
- `treasureHints`
|
||||
- 最近故事动作
|
||||
|
||||
例如:
|
||||
|
||||
- 在矿道拿到的不是泛用剑,而是“矿脉巡火短铳”
|
||||
- 在旧祭坛拿到的不是泛用药,而是“断誓回神香”
|
||||
|
||||
### NPC 交易
|
||||
|
||||
物品必须贴合:
|
||||
|
||||
- NPC 身份
|
||||
- NPC 库存风格
|
||||
- NPC 和玩家关系
|
||||
|
||||
例如:
|
||||
|
||||
- 黑市牙人卖的是“快袭/情报/潜行”方向的东西
|
||||
- 医修给的是“回复/续战/净化”方向的东西
|
||||
|
||||
### 怪物掉落
|
||||
|
||||
物品必须贴合:
|
||||
|
||||
- 怪物战斗风格
|
||||
- 怪物生态来源
|
||||
- 所在场景
|
||||
|
||||
例如:
|
||||
|
||||
- 重甲守卫掉 `守御精粹`
|
||||
- 雾林伏击者掉 `风行羽囊`
|
||||
|
||||
### 任务奖励
|
||||
|
||||
物品必须贴合:
|
||||
|
||||
- 发布人
|
||||
- 任务目标
|
||||
- 完成方式
|
||||
- 当前线索推进
|
||||
|
||||
它最适合发“永久关系物”。
|
||||
|
||||
## 5.3 命名改成“锚点命名法”
|
||||
|
||||
建议运行时重点物品命名遵循:
|
||||
|
||||
```text
|
||||
来源词 + 关系词 + 功能词
|
||||
```
|
||||
|
||||
而不是:
|
||||
|
||||
```text
|
||||
稀有前缀 + 通用品类名
|
||||
```
|
||||
|
||||
例子:
|
||||
|
||||
- `锁风渡缉索短刃`
|
||||
- `裂界巡守压纹符`
|
||||
- `药谷回岚灵露`
|
||||
- `断碑旧誓护心佩`
|
||||
|
||||
其中:
|
||||
|
||||
- 来源词来自场景/地标/势力
|
||||
- 关系词来自 NPC / 怪物 / 事件
|
||||
- 功能词来自 build 或物品品类
|
||||
|
||||
## 6. 新系统里的预算规则
|
||||
|
||||
## 6.1 稀有度预算
|
||||
|
||||
建议按稀有度控制:
|
||||
|
||||
- 能有几个 build 标签
|
||||
- 能不能带关系锚点
|
||||
- 能不能有 set 倾向
|
||||
- 数值范围上限
|
||||
|
||||
建议预算如下:
|
||||
|
||||
| 稀有度 | build 标签 | 限时 buff | 数值强度 | 叙事强度 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| common | 0~1 | 1 | 很小 | 轻 |
|
||||
| uncommon | 1 | 1~2 | 小 | 轻 |
|
||||
| rare | 1~2 | 2 | 小到中 | 中 |
|
||||
| epic | 2 | 2 | 中 | 中到高 |
|
||||
| legendary | 2 + 关系锚点 | 2~3 | 中 | 高 |
|
||||
|
||||
## 6.2 渠道预算
|
||||
|
||||
不同渠道不该发同样强度的物品。
|
||||
|
||||
建议:
|
||||
|
||||
- `treasure`
|
||||
- 偏单件强语义物
|
||||
- `npc_trade`
|
||||
- 偏稳定、可预期、可补短板
|
||||
- `npc_reward`
|
||||
- 偏关系定制与限时支援物
|
||||
- `monster_drop`
|
||||
- 偏材料 / 精粹 / 生态锚点物
|
||||
- `quest_reward`
|
||||
- 偏永久 build 锚点物
|
||||
- `discovery`
|
||||
- 偏线索物 / 过渡工具物
|
||||
|
||||
## 6.3 build 方向切换限制
|
||||
|
||||
新系统建议引入一条硬规则:
|
||||
|
||||
**普通运行时物品默认只能强化当前 build 或其邻近 build,不应该高频强制转流派。**
|
||||
|
||||
也就是:
|
||||
|
||||
- 当前偏 `快剑/突进`,更容易给 `追击/风行`
|
||||
- 当前偏 `守御/护体`,更容易给 `续战/回复`
|
||||
- 当前偏 `法修/雷法`,更容易给 `过载/冷却`
|
||||
|
||||
真正能强制开新流派的物品,应只出现在:
|
||||
|
||||
- 高价值任务奖励
|
||||
- 关键宝藏
|
||||
- 重要 NPC 关系突破
|
||||
|
||||
## 7. 建议新增的数据结构
|
||||
|
||||
## 7.1 运行时 metadata
|
||||
|
||||
建议在 `InventoryItem` 上新增:
|
||||
|
||||
```ts
|
||||
interface RuntimeItemMetadata {
|
||||
origin: "catalog" | "procedural" | "ai_compiled";
|
||||
generationChannel: RuntimeItemGenerationChannel;
|
||||
seedKey: string;
|
||||
relationAnchor?: RuntimeRelationAnchor;
|
||||
sourceReason: string;
|
||||
recentEventHook?: string;
|
||||
}
|
||||
```
|
||||
|
||||
然后在 `InventoryItem` 上挂:
|
||||
|
||||
```ts
|
||||
interface InventoryItem {
|
||||
runtimeMetadata?: RuntimeItemMetadata;
|
||||
}
|
||||
```
|
||||
|
||||
这样后面:
|
||||
|
||||
- UI 可展示“来源”
|
||||
- 日志可回放“为什么拿到”
|
||||
- 剧情可引用“这是谁给你的”
|
||||
|
||||
## 7.2 运行时导演结果
|
||||
|
||||
```ts
|
||||
type DirectedRuntimeReward = {
|
||||
primaryItem?: InventoryItem | null;
|
||||
supportItems: InventoryItem[];
|
||||
hp?: number;
|
||||
mana?: number;
|
||||
currency?: number;
|
||||
storyHint?: string;
|
||||
};
|
||||
```
|
||||
|
||||
这样可以统一替代宝藏、帮助奖励、任务奖励等零散结构。
|
||||
|
||||
## 8. 与现有模块的接入方式
|
||||
|
||||
## 8.1 `treasureInteractions.ts`
|
||||
|
||||
重构方式:
|
||||
|
||||
- 现在:内部直接从世界池挑物品
|
||||
- 以后:调用 `runtimeItemDirector`
|
||||
|
||||
建议流程:
|
||||
|
||||
1. 从 `GameState + Encounter + ScenePreset` 组 context
|
||||
2. 指定 `generationChannel = "treasure"`
|
||||
3. director 产出 `DirectedRuntimeReward`
|
||||
4. 再由 `buildTreasureResultText` 读 reward 回写文本
|
||||
|
||||
这是最适合作为第一落点的入口。
|
||||
|
||||
## 8.2 `npcInteractions.ts`
|
||||
|
||||
接入方向:
|
||||
|
||||
- 商店货物补货
|
||||
- NPC 帮助奖励
|
||||
- 高好感赠与
|
||||
- 特殊 NPC 线索物
|
||||
|
||||
这里最适合体现“关系锚点”。
|
||||
|
||||
## 8.3 `forgeSystem.ts`
|
||||
|
||||
锻造系统不需要完全 AI 化,但应该读取 AI 原生物品遗留的 metadata:
|
||||
|
||||
- 拆解时保留关系信息
|
||||
- 产出对应方向的精粹
|
||||
- 重铸时优先在邻近 build 内滚动
|
||||
|
||||
这样 AI 原生物品与锻造闭环才是连通的。
|
||||
|
||||
## 8.4 `customWorldRuntime.ts`
|
||||
|
||||
这个模块不要废弃,而是改成:
|
||||
|
||||
- 继续负责“主题物品池”
|
||||
- director 在需要 fallback 或大批量补货时调用它
|
||||
|
||||
也就是说:
|
||||
|
||||
- `customWorldRuntime.ts` 保留“池”
|
||||
- `runtimeItemDirector.ts` 新增“导演”
|
||||
|
||||
## 9. 推荐实施顺序
|
||||
|
||||
## 阶段 A:先加类型和 metadata
|
||||
|
||||
先做:
|
||||
|
||||
- `runtimeItem.ts`
|
||||
- `InventoryItem.runtimeMetadata`
|
||||
- `RuntimeRelationAnchor`
|
||||
- `RuntimeItemGenerationContext`
|
||||
|
||||
目的:
|
||||
|
||||
- 不改玩法,只把类型基础搭好
|
||||
|
||||
## 阶段 B:先做 director + compiler
|
||||
|
||||
先不接所有入口,只把:
|
||||
|
||||
- context 采样
|
||||
- AI 意图结构
|
||||
- 本地编译器
|
||||
|
||||
跑通。
|
||||
|
||||
## 阶段 C:先接宝藏
|
||||
|
||||
原因:
|
||||
|
||||
- 独立
|
||||
- 风险低
|
||||
- 最容易观察“背景贴合”提升
|
||||
|
||||
## 阶段 D:再接 NPC 奖励与交易
|
||||
|
||||
这一步会明显提升:
|
||||
|
||||
- 关系系统
|
||||
- 世界贴脸感
|
||||
- 物品来源可解释性
|
||||
|
||||
## 阶段 E:最后接任务奖励与怪物语义掉落
|
||||
|
||||
因为这两类和现有逻辑耦合更深,适合后置。
|
||||
|
||||
## 10. 和旧版方案相比,这次重设计改了什么
|
||||
|
||||
相比之前偏概念化的 AI 原生物品方案,这次重设计更明确了 5 点:
|
||||
|
||||
1. **不新起物品系统**
|
||||
- 直接复用 `InventoryItem` 与现有 build 结构。
|
||||
2. **不让 AI 直接出成品**
|
||||
- 只让 AI 出意图,交给本地编译器落地。
|
||||
3. **所有渠道走同一导演层**
|
||||
- 不再把宝藏、NPC、任务各写一套。
|
||||
4. **把“关系锚点”正式数据化**
|
||||
- 不是只写在文案里。
|
||||
5. **先从宝藏接入,再逐步扩展**
|
||||
- 不是一次推全仓库。
|
||||
|
||||
## 11. 一句话总结
|
||||
|
||||
新的 AI 原生运行时物品生成系统,不应该是“让 AI 随机写几个看起来很酷的装备”,而应该是:
|
||||
|
||||
**让 AI 根据当前世界、场景、NPC、事件和玩家 build 给出物品意图,再由本地规则把这个意图编译成能进背包、能进 build、能进锻造、能进存档、还能被剧情解释的正式物品。**
|
||||
91
docs/CHINESE_MOJIBAKE_INVENTORY.md
Normal file
91
docs/CHINESE_MOJIBAKE_INVENTORY.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 中文乱码位置清单
|
||||
|
||||
更新时间:`2026-03-24`
|
||||
|
||||
## 说明
|
||||
|
||||
- 本文档用于记录仓库内已确认或高置信度疑似存在中文乱码的位置。
|
||||
- 当前这份文档是重建版本;原有的 [`docs/CHINESE_MOJIBAKE_INVENTORY.md`](/E:/Repos/ai-native-visual-rpg/docs/CHINESE_MOJIBAKE_INVENTORY.md) 本身也已经乱码,因此已整体替换。
|
||||
- 本次整理依据:
|
||||
- 仓库内旧清单中的完整文件/行号信息
|
||||
- 本轮人工复核时再次直接看到的明显乱码位置
|
||||
- 由于仓库内同时存在“文件内容已写坏”和“终端/工具显示失真”两类情况,下面清单优先保留高置信位置,便于后续逐项修复。
|
||||
|
||||
## 扫描范围
|
||||
|
||||
- 已纳入:`src/`、`docs/`、根目录文档与元数据文件
|
||||
- 已排除:`.git/`、`node_modules/`、`dist/`、纯图片资源目录
|
||||
|
||||
## 高置信位置
|
||||
|
||||
### 文档与元数据
|
||||
|
||||
- [`docs/AGENT_UI_CHANGELOG.md`](/E:/Repos/ai-native-visual-rpg/docs/AGENT_UI_CHANGELOG.md):1, 3, 7, 9, 11-18, 24, 26-28, 32-33, 37, 39, 41-43, 47, 49, 51, 53-66, 68, 72, 74, 77-79, 83, 87, 89-90, 94, 96, 98-100, 104, 106-112, 116
|
||||
- [`UI_CODING_STANDARD.md`](/E:/Repos/ai-native-visual-rpg/UI_CODING_STANDARD.md):3, 91, 104, 108, 112, 156, 158, 160-166
|
||||
- [`metadata.json`](/E:/Repos/ai-native-visual-rpg/metadata.json):2-3
|
||||
|
||||
### 组件层
|
||||
|
||||
- [`src/components/AdventurePanel.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/AdventurePanel.tsx):57, 65
|
||||
- [`src/components/CharacterPanel.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/CharacterPanel.tsx):37, 65-66, 91-95, 102-103
|
||||
- [`src/components/GameCanvas.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/GameCanvas.tsx):240, 462
|
||||
- [`src/components/GameShell.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/GameShell.tsx):108, 116, 124, 138, 171, 181
|
||||
- [`src/components/InventoryPanel.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/InventoryPanel.tsx):55, 58, 82-83, 181-184, 189, 191
|
||||
- [`src/components/MapModal.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/MapModal.tsx):105, 108, 136
|
||||
- [`src/components/MedievalNpcAnimator.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/MedievalNpcAnimator.tsx):124
|
||||
- [`src/components/NpcVisualEditor.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/NpcVisualEditor.tsx):65, 69-71, 403, 440, 444, 446, 464, 467, 470, 482, 569, 571, 585, 610, 628, 662, 690, 694-695, 697, 722, 751, 759, 775, 777, 781, 824
|
||||
- [`src/components/PresetEditor.tsx`](/E:/Repos/ai-native-visual-rpg/src/components/PresetEditor.tsx):34-37, 43-44, 94, 96, 349, 470, 472, 480, 482, 512, 516, 519, 525, 568, 612, 618, 637, 639, 643, 645, 652, 661, 677, 740, 769, 771, 779, 781, 806, 809, 820, 831, 835, 837, 840, 848, 871, 894, 916, 918, 930, 932, 950, 953, 956, 960, 962, 990, 1004, 1006, 1012, 1018, 1024, 1030, 1036, 1064, 1120, 1122, 1130, 1132, 1150, 1153, 1156, 1172-1175, 1180, 1182, 1186, 1188, 1199, 1203-1204, 1208, 1240, 1242
|
||||
|
||||
### 数据层
|
||||
|
||||
- [`src/data/characterPresets.ts`](/E:/Repos/ai-native-visual-rpg/src/data/characterPresets.ts):97, 102, 104, 107, 129, 132-133, 142, 144, 170, 276, 302, 470, 496, 531, 540, 566, 699-700, 729, 972
|
||||
- [`src/data/medievalNpcVisuals.ts`](/E:/Repos/ai-native-visual-rpg/src/data/medievalNpcVisuals.ts):103, 115, 117, 119, 136, 154, 156, 161, 167, 174, 177, 189, 226, 235-236, 241, 244-245, 249-254, 256-257, 260, 262, 274, 278, 288, 451-453, 565, 568, 577, 592
|
||||
- [`src/data/monsterPresets.ts`](/E:/Repos/ai-native-visual-rpg/src/data/monsterPresets.ts):41-42, 54, 60-61, 79-80, 92, 98-99, 117-118, 136-137, 155-156, 171-173, 185, 191-192, 204, 210-211, 229-230, 242, 248-249, 261, 267-268, 280, 286-287, 304-305, 323-324, 335
|
||||
- [`src/data/monsters.ts`](/E:/Repos/ai-native-visual-rpg/src/data/monsters.ts):112
|
||||
- [`src/data/npcInteractions.ts`](/E:/Repos/ai-native-visual-rpg/src/data/npcInteractions.ts):68-71, 80, 82-83, 161, 165, 173, 182, 188-190, 196, 198, 205, 231, 241, 245, 255-260, 272, 296, 319-320, 372, 444-445, 449, 451, 453, 507, 569-570, 578-579, 587-588, 597, 605-606, 615, 617-618, 626-627, 634, 641-643, 652, 661, 665, 670, 672, 676
|
||||
- [`src/data/scenePresets.ts`](/E:/Repos/ai-native-visual-rpg/src/data/scenePresets.ts):115, 120, 122, 128, 133, 135, 141, 146, 148, 154, 159, 161, 167, 172, 174, 180, 185, 187, 192-193, 198, 200, 205-206, 211, 213, 219, 224, 226, 232, 237, 239, 245, 250, 252, 258, 263, 265, 274, 279, 281, 287, 292, 294, 299-300, 305, 307, 313, 318, 320, 326, 331, 333, 339, 344, 346, 352, 357, 359, 364-365, 370, 372, 377-378, 383, 385, 390-391, 396, 398, 404, 409, 411, 417, 422, 424, 509, 523, 525
|
||||
- [`src/data/stateFunctions.ts`](/E:/Repos/ai-native-visual-rpg/src/data/stateFunctions.ts):72-73, 80, 95-96, 103, 117-118, 125, 139-140, 147, 161-162, 169, 186-187, 194, 209-210, 217, 237-238, 255-256, 273-274, 294, 311-312, 329-330, 420, 430-431, 433-435, 437-438, 440-442, 444-445, 447, 449, 451-452, 454-456, 458, 460-461, 464, 466, 468, 484-485, 487, 489, 491, 493, 601, 618
|
||||
|
||||
### Hooks 与服务层
|
||||
|
||||
- [`src/hooks/useCombatFlow.ts`](/E:/Repos/ai-native-visual-rpg/src/hooks/useCombatFlow.ts):54, 56-58, 566
|
||||
- [`src/services/ai.ts`](/E:/Repos/ai-native-visual-rpg/src/services/ai.ts):200-201, 209-210, 234-235, 269-270, 309, 311, 317, 338, 341, 358, 382
|
||||
- [`src/services/prompt.ts`](/E:/Repos/ai-native-visual-rpg/src/services/prompt.ts):7-8, 10, 13-15, 19-20, 25-40, 43, 55, 61-62, 64, 66, 74-76, 78-79, 83-84, 87-90, 96, 103-104, 112, 115, 157, 159, 161-162, 164-165, 167-168, 170, 172-173
|
||||
|
||||
### 其他源码
|
||||
|
||||
- [`src/uiAssets.ts`](/E:/Repos/ai-native-visual-rpg/src/uiAssets.ts):54, 115, 122, 129, 142, 173, 180
|
||||
|
||||
## 本轮人工复核补充
|
||||
|
||||
以下位置是在本轮实现过程中直接再次看到的明显乱码文本,建议优先复查:
|
||||
|
||||
- [`src/hooks/useCombatFlow.ts`](/E:/Repos/ai-native-visual-rpg/src/hooks/useCombatFlow.ts):1094, 1554, 1556-1557
|
||||
|
||||
## 处理优先级建议
|
||||
|
||||
### 第一批
|
||||
|
||||
- `src/components/GameShell.tsx`
|
||||
- `src/components/InventoryPanel.tsx`
|
||||
- `src/components/CharacterPanel.tsx`
|
||||
- `src/data/characterPresets.ts`
|
||||
- `src/data/npcInteractions.ts`
|
||||
- `src/data/scenePresets.ts`
|
||||
- `src/services/prompt.ts`
|
||||
|
||||
### 第二批
|
||||
|
||||
- `src/components/PresetEditor.tsx`
|
||||
- `src/components/NpcVisualEditor.tsx`
|
||||
- `src/data/monsterPresets.ts`
|
||||
- `src/data/stateFunctions.ts`
|
||||
- `docs/AGENT_UI_CHANGELOG.md`
|
||||
- `UI_CODING_STANDARD.md`
|
||||
|
||||
## 备注
|
||||
|
||||
- 当前文档的目标是“先把位置收拢清楚”,不是直接修复乱码。
|
||||
- 如果你下一步要我继续,我可以基于这份清单继续做两件事之一:
|
||||
- 逐文件修复中文乱码
|
||||
- 先做一个“乱码修复优先级 + 替换建议”文档
|
||||
262
docs/CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md
Normal file
262
docs/CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md
Normal file
@@ -0,0 +1,262 @@
|
||||
# Codex 实战经验沉淀
|
||||
|
||||
日期:`2026-03-24`
|
||||
|
||||
## 1. 先判断需求属于哪条链路
|
||||
|
||||
这个项目几乎所有需求都不是单点 UI 改动,通常会同时影响几条链路:
|
||||
|
||||
- 叙事链路:AI 剧情、提示词、选项文案
|
||||
- 状态链路:`GameState`、NPC 状态、背包、队伍、委托
|
||||
- 演出链路:屏幕外进场、战斗播放、实体站位、动画与特效
|
||||
- 工具链路:编辑器、校验脚本、存档兼容、smoke
|
||||
|
||||
经验:
|
||||
|
||||
- 每次动手前先判断“这次主要影响哪几条链路”,不要把需求误判成单纯的 UI 需求。
|
||||
- 只改表现、不改状态,最终一定会返工。
|
||||
- 只改状态、不补校验,后面也一定会返工。
|
||||
|
||||
## 2. 先补状态模型,再补交互
|
||||
|
||||
这类项目里,最稳定的顺序永远是:
|
||||
|
||||
1. 先补类型与状态字段
|
||||
2. 再补规则函数
|
||||
3. 再补 hook 流程
|
||||
4. 最后接 UI
|
||||
|
||||
已经反复验证有效的例子:
|
||||
|
||||
- `quests`
|
||||
- `playerEquipment`
|
||||
- `playerCurrency`
|
||||
- `roster`
|
||||
- `companions`
|
||||
- `currentNpcBattleMode`
|
||||
- `sparReturnEncounter`
|
||||
|
||||
经验:
|
||||
|
||||
- 如果一个功能需要“长期留存”,就不要只存在于局部组件 state。
|
||||
- 先建模后接按钮,比先堆按钮后补状态稳定得多。
|
||||
|
||||
## 3. AI 负责叙事,本地负责规则
|
||||
|
||||
项目里最稳的边界是:
|
||||
|
||||
- AI 负责:
|
||||
- `storyText`
|
||||
- 对话文本
|
||||
- 选项的自然语言表达
|
||||
- 本地规则负责:
|
||||
- 交易是否合法
|
||||
- 招募是否成立
|
||||
- 装备是否生效
|
||||
- 委托进度是否完成
|
||||
- 掉落、货币、好感、队伍编成
|
||||
|
||||
经验:
|
||||
|
||||
- 只要涉及数值、资源、状态迁移,就尽量不要让大模型即兴决定。
|
||||
- 给模型的提示词应该描述“局面”和“边界”,不要让它代替规则系统。
|
||||
|
||||
## 4. 固定选项提示不要写得太死
|
||||
|
||||
一个重要经验是:
|
||||
|
||||
- 模型需要知道每个 `functionId` 的核心含义
|
||||
- 但不需要看到“当前默认文案 / 补充信息 / 实际行为”这种过于细碎、强绑定的结构
|
||||
|
||||
更好的方式是:
|
||||
|
||||
- 保留 `functionId`、数量、顺序
|
||||
- 只提供“这个 function 的核心语义参考”
|
||||
- 让模型重写更自然、连续的 `actionText`
|
||||
|
||||
经验:
|
||||
|
||||
- 提示词越像表单,模型越容易产出僵硬选项。
|
||||
- 提示词越像“约束 + 语义边界”,剧情越自然。
|
||||
|
||||
## 5. 面前实体的提示词必须和主角对称
|
||||
|
||||
如果主角有:
|
||||
|
||||
- 描述
|
||||
- 性格
|
||||
- 状态
|
||||
- 属性
|
||||
|
||||
那么“当前面前实体”也应该尽量有同样结构。
|
||||
|
||||
经验:
|
||||
|
||||
- 只给一句“某 NPC 在这里活动”太粗,会削弱模型对局面的把握。
|
||||
- 面前实体和主角描述层级一致后,模型更容易写出有来回感、对称感的叙事。
|
||||
- 对生命/灵力这类状态,离散分档文本比裸数字更利于模型理解。
|
||||
|
||||
## 6. 屏幕外进场一定要抽成统一工具
|
||||
|
||||
这类项目很容易在多个地方各写一套“从屏幕外进入”的逻辑,结果出现:
|
||||
|
||||
- 同一实体重复进场
|
||||
- 先进入屏幕,又被拉回屏幕外再进一次
|
||||
- 多怪时只动第一个,其他直接跳终点
|
||||
|
||||
这次稳定下来的方法是:
|
||||
|
||||
- 抽统一的过渡工具
|
||||
- `buildEncounterEntryState`
|
||||
- `buildEncounterTransitionState`
|
||||
- `interpolateEncounterTransitionState`
|
||||
- 所有进场逻辑都复用这一套
|
||||
- 区分两种场景:
|
||||
- 真正从屏幕外冲入
|
||||
- 已经在屏内预览,只是收敛到正式位置
|
||||
|
||||
经验:
|
||||
|
||||
- “屏幕外进入”本质上不是动画问题,而是状态过渡问题。
|
||||
- 一旦同时存在 preview / call_out / resolve / replay,就必须统一插值逻辑。
|
||||
|
||||
## 7. 多实体系统里,不要默认“只处理第一个”
|
||||
|
||||
这个坑非常常见:
|
||||
|
||||
- 场景配置里有多个怪
|
||||
- 运行时逻辑却只用 `monsterIds[0]`
|
||||
- 或者动画只插值 `sceneMonsters[0]`
|
||||
|
||||
经验:
|
||||
|
||||
- 只要数据结构已经允许数组,就尽量按“整组”处理。
|
||||
- 即便最后 UI 只重点展示一个,也不要在底层逻辑里偷偷退化成单体。
|
||||
|
||||
## 8. 招募系统不要只做“当前队伍”
|
||||
|
||||
如果只有 `companions` 而没有 `roster`,后面一定会遇到这些问题:
|
||||
|
||||
- 队伍满员时必须强制覆盖旧同伴
|
||||
- 已招募角色很难转成待命
|
||||
- 营地/编队功能没法闭环
|
||||
|
||||
这次稳定下来的模型是:
|
||||
|
||||
- `companions`:当前上阵
|
||||
- `roster`:已招募但待命
|
||||
|
||||
经验:
|
||||
|
||||
- 只要项目里有招募,迟早就要有 roster。
|
||||
- roster 不只是 UI 功能,而是状态层能力。
|
||||
|
||||
## 9. 装备系统不要只做展示
|
||||
|
||||
装备真正形成闭环,至少要同时做到:
|
||||
|
||||
- 背包里可装备 / 卸下
|
||||
- 装备改变真实属性
|
||||
- 战斗行为读取装备加成
|
||||
- 存档兼容旧存档
|
||||
|
||||
经验:
|
||||
|
||||
- “装备栏能显示”不算完成。
|
||||
- 只有真正影响 `maxHp / maxMana / damage / incomingDamage`,它才是玩法系统。
|
||||
|
||||
## 10. 交易系统最好统一成货币价值模型
|
||||
|
||||
一开始按“品质交换”虽然简单,但很快会遇到问题:
|
||||
|
||||
- 不同类别物品难比较
|
||||
- 直接购买不好接
|
||||
- 后面加入售卖、任务赏金、宝藏货币时会冲突
|
||||
|
||||
更稳定的做法是:
|
||||
|
||||
- 所有物品都有统一价值
|
||||
- NPC 商品有购买价
|
||||
- 玩家物品有回收价
|
||||
- 货币作为通用交换媒介
|
||||
|
||||
经验:
|
||||
|
||||
- 一旦出现货币,就尽量让交易系统全面切成“价值模型”。
|
||||
- 不要同时长期维护“品质交换”和“货币购买”两套完全不同的判定逻辑。
|
||||
|
||||
## 11. 编辑器要先做保存前校验
|
||||
|
||||
编辑器进入“可持续生产内容”阶段后,最优先的不是视觉,而是:
|
||||
|
||||
- 保存前校验
|
||||
- 非法引用提示
|
||||
- 数值边界检查
|
||||
|
||||
经验:
|
||||
|
||||
- 编辑器最怕的不是“不够漂亮”,而是“保存成功但运行时报错”。
|
||||
- 只要内容开始增多,校验脚本和保存前校验就必须尽早接入。
|
||||
|
||||
## 12. 每次大改后都要补 smoke
|
||||
|
||||
对这个项目来说,`lint + build` 不够。
|
||||
|
||||
至少要补 smoke 的场景包括:
|
||||
|
||||
- 委托接取 -> 推进 -> 交付
|
||||
- 多怪遭遇
|
||||
- 装备加成
|
||||
- 队伍编成
|
||||
- 交易价值与直接购买
|
||||
- 进场插值
|
||||
|
||||
经验:
|
||||
|
||||
- 只靠人工点点看,很容易漏掉状态分支。
|
||||
- smoke 不一定要重,但要覆盖关键闭环。
|
||||
|
||||
## 13. 兼容旧存档要同步做
|
||||
|
||||
每次给 `GameState` 新增字段时,都要同步考虑:
|
||||
|
||||
- 默认值
|
||||
- 存档读取兼容
|
||||
- 旧字段缺失时如何补全
|
||||
|
||||
经验:
|
||||
|
||||
- 旧存档兼容不是“最后再说”的工作。
|
||||
- 新字段一旦进 `GameState`,就应当同一轮把持久化兼容补上。
|
||||
|
||||
## 14. 不要在坏文件上无限缝补
|
||||
|
||||
这次实际踩到过一个很明显的坑:
|
||||
|
||||
- 某些文件本身已经混入大量乱码或结构损坏
|
||||
- 在原地做小 patch 会越来越难维护
|
||||
|
||||
更稳的做法是:
|
||||
|
||||
- 保留接口
|
||||
- 整文件重写成干净版本
|
||||
- 再接回现有调用
|
||||
|
||||
经验:
|
||||
|
||||
- 当一个文件已经进入“局部 patch 很难保证正确”的状态时,重写往往比继续缝补更省时间。
|
||||
|
||||
## 15. 后续继续迭代时的建议顺序
|
||||
|
||||
如果继续推进这个项目,建议优先顺序:
|
||||
|
||||
1. 先清理中文乱码高频文件
|
||||
2. 再继续精英/Boss 层
|
||||
3. 再补营地关系事件
|
||||
4. 再做编辑器差异预览 / 导入导出
|
||||
|
||||
## 16. 一句话总结
|
||||
|
||||
这个项目最重要的经验不是“多写了多少功能”,而是:
|
||||
|
||||
**凡是会同时影响叙事、状态、演出和工具链的需求,都要先统一边界,再落实现。**
|
||||
277
docs/CODEX_PAST_WORK_EXPERIENCE_SUMMARY.md
Normal file
277
docs/CODEX_PAST_WORK_EXPERIENCE_SUMMARY.md
Normal file
@@ -0,0 +1,277 @@
|
||||
# Codex Past Work Experience Summary
|
||||
|
||||
日期:`2026-03-23`
|
||||
|
||||
## 1. 工作范围概览
|
||||
|
||||
这几轮工作主要集中在 4 个方向:
|
||||
|
||||
1. 编辑器体系整理
|
||||
2. NPC 视觉编辑与素材接入
|
||||
3. 玩家角色与怪物动画资源纠偏
|
||||
4. 选项行为编辑器与预览链路升级
|
||||
|
||||
这些改动不是孤立的 UI 修补,而是横跨了:
|
||||
|
||||
- 资源定义层
|
||||
- 编辑器字段层
|
||||
- 运行时预览层
|
||||
- 游戏真实播放逻辑层
|
||||
|
||||
## 2. 已完成的核心工作
|
||||
|
||||
### 2.1 编辑器入口与页签整理
|
||||
|
||||
- 保留 `/preset-editor`、`/npc-editor`、`/function-editor`
|
||||
- 新增 `/behavior-editor` 作为“选项行为”编辑页别名
|
||||
- 将原先单独的 `NPC 视觉` 标签并回 `NPC` 编辑页
|
||||
- 将 `Function` 页签改名为 `选项行为`
|
||||
|
||||
结论:
|
||||
- 路由层要尽量兼容旧入口,避免历史链接失效
|
||||
- 页签命名要贴近创作者语言,而不是内部实现命名
|
||||
|
||||
### 2.2 NPC 视觉模块并入 NPC 编辑
|
||||
|
||||
完成内容:
|
||||
|
||||
- 将 [NpcVisualEditor](/E:/Repos/ai-native-visual-rpg/src/components/NpcVisualEditor.tsx) 嵌入 [PresetEditor](/E:/Repos/ai-native-visual-rpg/src/components/PresetEditor.tsx) 的 NPC 编辑页
|
||||
- 让 NPC 文本字段与视觉字段围绕同一个当前选中 NPC 联动
|
||||
- 保留视觉覆盖保存与全局布局保存能力
|
||||
|
||||
经验:
|
||||
|
||||
- NPC 文本编辑和 NPC 视觉编辑不应分裂成两个互不关联的工作流
|
||||
- “当前选中的 NPC” 必须是两个模块共享的单一数据源
|
||||
|
||||
### 2.3 Medieval NPC 素材定义重建
|
||||
|
||||
完成内容:
|
||||
|
||||
- 在 [medievalNpcVisuals.ts](/E:/Repos/ai-native-visual-rpg/src/data/medievalNpcVisuals.ts) 中重建了 Medieval NPC 的资产定义
|
||||
- 补齐了 cloth / leather / metal / melee / magic / ranged 六大类真实素材
|
||||
- 为素材增加了:
|
||||
- 语义化名称
|
||||
- 图块尺寸
|
||||
- 列数
|
||||
- 帧数
|
||||
- 姿态选项
|
||||
- 让编辑器不再直接使用文件名、序号名作为展示项
|
||||
|
||||
经验:
|
||||
|
||||
- 编辑器下拉项如果来自手写数组,迟早会和真实素材目录脱节
|
||||
- 素材定义最好具备“资产元数据”,而不是只有路径字符串
|
||||
- 一旦资产存在大图块,就不能再默认所有图块都是 `32x32`
|
||||
|
||||
### 2.4 NPC 动画器支持大图块武器
|
||||
|
||||
完成内容:
|
||||
|
||||
- 在 [MedievalNpcAnimator.tsx](/E:/Repos/ai-native-visual-rpg/src/components/MedievalNpcAnimator.tsx) 中为 `AtlasSprite` 增加:
|
||||
- `tileWidth`
|
||||
- `tileHeight`
|
||||
- 对齐偏移支持
|
||||
|
||||
效果:
|
||||
|
||||
- 长柄武器
|
||||
- 巨剑
|
||||
- 64x32 远程武器
|
||||
- 64x64 投石索类武器
|
||||
|
||||
都能正确显示,不再被按 `32x32` 裁坏。
|
||||
|
||||
经验:
|
||||
|
||||
- 视觉编辑器一旦涉及装备 atlas,就必须把“资源尺寸”从数据层带到渲染层
|
||||
|
||||
### 2.5 玩家角色动画映射纠偏
|
||||
|
||||
完成内容:
|
||||
|
||||
- 在 [characterPresets.ts](/E:/Repos/ai-native-visual-rpg/src/data/characterPresets.ts) 中重新核对 5 个玩家角色的 Hero 动画目录
|
||||
- 修正了错误帧数、错误前缀、遗漏动作
|
||||
- 补齐了真实存在但之前未接入的动作:
|
||||
- `acquire`
|
||||
- `climb`
|
||||
- `dash`
|
||||
- `die`
|
||||
- `double jump`
|
||||
- `hurt`
|
||||
- `jump`
|
||||
- `jump attack`
|
||||
- `wall slide`
|
||||
- 给角色动画配置增加了 `file` 字段,支持单文件动画
|
||||
|
||||
相关文件:
|
||||
|
||||
- [types.ts](/E:/Repos/ai-native-visual-rpg/src/types.ts)
|
||||
- [CharacterAnimator.tsx](/E:/Repos/ai-native-visual-rpg/src/components/CharacterAnimator.tsx)
|
||||
- [characterCombat.ts](/E:/Repos/ai-native-visual-rpg/src/data/characterCombat.ts)
|
||||
|
||||
经验:
|
||||
|
||||
- 只要编辑器允许用户切“预览动作”,就不能让未映射动作静默 fallback 到 `idle`
|
||||
- 正确做法是:
|
||||
1. 先补齐真实动作映射
|
||||
2. 再让预览下拉只显示当前角色实际可用动作
|
||||
|
||||
### 2.6 怪物动画空白帧修复
|
||||
|
||||
完成内容:
|
||||
|
||||
- 在 [monsterPresets.ts](/E:/Repos/ai-native-visual-rpg/src/data/monsterPresets.ts) 中把怪物动画从“连续帧猜测”改成“按图集行起点取帧”
|
||||
- 补上缺失的 `die` 配置
|
||||
- 清除了所有落进空白格的动画段
|
||||
|
||||
经验:
|
||||
|
||||
- 像素怪物图集不一定按一个连续区段排完整套动作
|
||||
- 如果动作配置只写 `start + frames`,但没结合图集行结构,就非常容易踩进空白帧
|
||||
|
||||
### 2.7 选项行为编辑器重构
|
||||
|
||||
完成内容:
|
||||
|
||||
- 将原 “Function 编辑器” 改造成 “选项行为编辑器”
|
||||
- 页面文案和入口统一为“选项行为”
|
||||
- 移除“基础场景 / 结果场景”双窗格预览
|
||||
- 保留并强化:
|
||||
- 行为列表
|
||||
- 覆盖保存
|
||||
- 快速模板套用
|
||||
|
||||
经验:
|
||||
|
||||
- 创作者并不关心 “function” 这个技术词,更关心“这个选项会发生什么”
|
||||
- 同类编辑器如果只给字段表单而没有模板起稿能力,复用效率会很低
|
||||
|
||||
### 2.8 选项行为预览升级到实机回放
|
||||
|
||||
完成内容:
|
||||
|
||||
- 在 [StateFunctionEditor.tsx](/E:/Repos/ai-native-visual-rpg/src/components/StateFunctionEditor.tsx) 中新增 `BehaviorExecutionPreview`
|
||||
- 预览不再是静态推测,而是:
|
||||
1. 构造本地 `GameState`
|
||||
2. 调用真实 `resolveFunctionOption`
|
||||
3. 再调用 [useCombatFlow.ts](/E:/Repos/ai-native-visual-rpg/src/hooks/useCombatFlow.ts) 的
|
||||
- `buildResolvedChoiceState`
|
||||
- `playResolvedChoice`
|
||||
- 从而直接复用游戏真实逻辑
|
||||
|
||||
覆盖能力包括:
|
||||
|
||||
- 战斗行为
|
||||
- 恢复行为
|
||||
- 脱离行为
|
||||
- 探索前探
|
||||
- 切场行为
|
||||
|
||||
经验:
|
||||
|
||||
- 编辑器预览只要和运行时逻辑写成两套,就一定会越来越不一致
|
||||
- 预览层最稳的做法是“调用真实业务逻辑”,而不是“模拟真实业务逻辑”
|
||||
|
||||
## 3. 关键踩坑记录
|
||||
|
||||
### 3.1 图标组件名覆盖原生 `Map`
|
||||
|
||||
问题:
|
||||
|
||||
- `lucide-react` 的 `Map` 图标直接命名为 `Map`
|
||||
- 在 NPC 页签里 `new Map()` 实际调用到了图标组件
|
||||
- 导致页签内容直接渲染为空
|
||||
|
||||
经验:
|
||||
|
||||
- 图标组件命名尽量使用 `MapIcon`、`UserIcon` 这类后缀
|
||||
- 避免覆盖 JS/TS 原生对象名
|
||||
|
||||
### 3.2 预览 effect 依赖不稳定导致回放反复重启
|
||||
|
||||
问题:
|
||||
|
||||
- `BehaviorExecutionPreview` 里使用了 `useCombatFlow()`
|
||||
- 但 effect 依赖了返回对象本身
|
||||
- 每次 `gameState` 更新,effect 都会被视为变更
|
||||
- 导致预览回放速度异常、重复重启、动画像加速
|
||||
|
||||
经验:
|
||||
|
||||
- 只要预览组件内部要“异步播放状态变化”,就要高度警惕 effect 依赖环
|
||||
- 解决方式是:
|
||||
- 用 `ref` 保存稳定方法引用
|
||||
- 让 effect 只依赖真正的输入配置,不依赖内部播放状态
|
||||
|
||||
### 3.3 实时面板与回放阶段不同步
|
||||
|
||||
问题:
|
||||
|
||||
- `LIVE PLAYER` 用的是实时 `gameState`
|
||||
- `BATTLE SNAPSHOT` 用的是预计算首回合快照
|
||||
- 两者不是同一时间点的数据
|
||||
- 导致面板看起来“都对,但互相对不上”
|
||||
|
||||
经验:
|
||||
|
||||
- 预览面板要么都显示“实时状态”
|
||||
- 要么都显示“同一个阶段的快照”
|
||||
- 混用实时值和预测值会让创作者误判
|
||||
|
||||
## 4. 这类项目里沉淀下来的方法论
|
||||
|
||||
### 4.1 先校验资源,再改编辑器
|
||||
|
||||
顺序建议:
|
||||
|
||||
1. 先扫真实目录
|
||||
2. 再建资产定义
|
||||
3. 再修编辑器字段
|
||||
4. 最后修预览
|
||||
|
||||
### 4.2 预览必须尽量复用游戏真实链路
|
||||
|
||||
优先级:
|
||||
|
||||
1. 复用真实函数
|
||||
2. 复用真实状态结构
|
||||
3. 复用真实渲染组件
|
||||
4. 最后才是补充编辑器专用的辅助信息
|
||||
|
||||
### 4.3 编辑器要区分“可编辑字段”和“会生效字段”
|
||||
|
||||
经验:
|
||||
|
||||
- 不是所有字段都应该在所有行为类型下开放
|
||||
- 如果某类行为最终不会直接读取某个字段,就应该禁用或弱化它
|
||||
- 否则创作者会错误地以为改动无效是 bug
|
||||
|
||||
### 4.4 模板比空白表单更重要
|
||||
|
||||
经验:
|
||||
|
||||
- 当系统里已经有多种成熟行为时,最快的创作路径不是“从零填写”
|
||||
- 而是:
|
||||
- 选一个最像的
|
||||
- 套结构
|
||||
- 微调文案和数值
|
||||
|
||||
## 5. 推荐的后续方向
|
||||
|
||||
如果继续打磨这套编辑器,建议下一步做:
|
||||
|
||||
1. 为选项行为预览增加“时间轴 / 阶段日志”
|
||||
2. 为选项行为编辑器增加“新建行为向导”
|
||||
3. 把更多系统状态引入预览上下文
|
||||
- 同伴
|
||||
- NPC 状态
|
||||
- 背包
|
||||
- 当前场景实体池
|
||||
4. 把“可编辑字段”和“只读推导字段”视觉上再分开
|
||||
|
||||
## 6. 一句话总结
|
||||
|
||||
过去这几轮最重要的经验不是“写了多少编辑器 UI”,而是:
|
||||
|
||||
**编辑器一旦想可靠,就不能只编辑静态数据,必须逐步接管真实资源定义、真实运行时状态和真实播放逻辑。**
|
||||
@@ -0,0 +1,787 @@
|
||||
# 角色首遇感、背景故事分层解锁与同伴私聊功能设计
|
||||
|
||||
更新时间:`2026-04-04`
|
||||
|
||||
## 0. 目标
|
||||
|
||||
这份方案针对当前仓库,解决 3 个连在一起的问题:
|
||||
|
||||
1. 玩家遇见每一个角色时,在该角色当前好感度对应的关系位置上,都应该有“第一次真正接触”的感觉,而不是一上来就像已经聊过很多轮。
|
||||
2. 角色背景故事目前没有按好感度分层解锁,导致尚未建立关系时,模型和界面都可能提前拿到过深信息。
|
||||
3. 队伍中的高好感同伴虽然已经有聊天弹窗底子,但没有被“好感度 + 面板入口 + 上下文边界”真正串成完整功能。
|
||||
|
||||
这次设计不另起一套独立系统,而是基于当前仓库已有的:
|
||||
|
||||
- `useStoryGeneration` 的角色遭遇与对话流
|
||||
- `npcInteractions.ts` 的好感度与对话阶段规则
|
||||
- `prompt.ts` 的上下文注入机制
|
||||
- `useCharacterChatFlow` / `CharacterChatModal` 的私聊能力
|
||||
|
||||
继续往前补齐。
|
||||
|
||||
## 1. 当前现状与问题定位
|
||||
|
||||
## 1.1 当前“首遇感”不足的根因
|
||||
|
||||
当前问题不是只出在开局同伴,而是整套角色遭遇系统缺少“第一次真正接触”的通用状态。
|
||||
|
||||
主要有 4 个原因:
|
||||
|
||||
1. `src/data/npcInteractions.ts`
|
||||
- `buildInitialNpcState(...)` 会给角色型 NPC 一个初始好感,但当前系统直接把这个好感映射成普通对话阶段。
|
||||
- 也就是说,系统把“当前好感是多少”和“是否已经不是第一次接触”混成了一件事。
|
||||
|
||||
2. `src/data/npcInteractions.ts`
|
||||
- `getNpcChatTopics(...)` 目前只看 `guarded / partial / honest / deep`,不看“这是不是第一次真正交流”。
|
||||
- 结果是,角色只要初始好感不低,就会直接拿到像“追一点表层理由”“开始碰真正目标”这种更像熟人后续轮的聊天切口。
|
||||
|
||||
3. `src/services/prompt.ts`
|
||||
- `describePlayerState(...)` 直接把 `character.backstory` 注入为“主角背景”。
|
||||
- `describeFrontEntity(...)` 直接把 `encounterCharacter.backstory` 注入为“背景”。
|
||||
- 这样模型即使被要求“像初见”,也已经在系统层知道太多,容易写成互背设定卡。
|
||||
|
||||
4. 当前仓库虽然有 `initial_companion / camp_companion` 的开场流,但它只覆盖开局这一种遭遇。
|
||||
- 如果只靠开场特判修正,其他角色第一次见面时仍然会缺乏首遇感。
|
||||
|
||||
一句话总结:
|
||||
|
||||
**当前缺的不是“开场对白模板”,而是“所有角色共用的首遇状态机”。**
|
||||
|
||||
## 1.2 背景故事过早暴露的根因
|
||||
|
||||
当前角色数据只有一个平铺的 `character.backstory`,而没有“公开层 / 解锁层 / 核心秘密层”的结构。
|
||||
|
||||
结果是:
|
||||
|
||||
- 界面层很容易直接把完整背景展示出来。
|
||||
- prompt 层也容易直接把完整背景塞给模型。
|
||||
- 好感度虽然已经控制了 `guarded / partial / honest / deep`,但它只约束“说话方式”,没有约束“哪些背景事实可以进入上下文”。
|
||||
|
||||
这会带来两个后果:
|
||||
|
||||
1. 模型写出来的对白容易像彼此已经认识很久。
|
||||
2. 玩家还没建立关系,就已经在系统层“知道了太多”。
|
||||
|
||||
## 1.3 私聊功能现状
|
||||
|
||||
当前仓库其实已经有私聊底座:
|
||||
|
||||
- `src/hooks/story/characterChat.ts`
|
||||
- 已经有 `useCharacterChatFlow(...)`
|
||||
- 已经能生成建议、流式回复、聊天总结,并写回 `gameState.characterChats`
|
||||
|
||||
- `src/components/CharacterChatModal.tsx`
|
||||
- 已经有完整聊天弹窗
|
||||
|
||||
- `src/components/GameShell.tsx`
|
||||
- 已经挂载了 `CharacterChatModal`
|
||||
- 也把 `onOpenCharacterChat` 传给了 `CharacterPanel`
|
||||
|
||||
但缺的 3 个点还没有补上:
|
||||
|
||||
1. `CharacterPanel` 当前没有真正渲染聊天按钮。
|
||||
2. 游戏内实际查看同伴详情时打开的是 `AdventureEntityModal`,它也没有聊天入口。
|
||||
3. 私聊没有与“队伍成员 + 好感度阈值 + 已解锁背景”绑定。
|
||||
|
||||
所以现在属于“能力有了,但功能还没真正成立”。
|
||||
|
||||
## 2. 总体设计结论
|
||||
|
||||
建议把这次需求拆成 3 条链,同时落地:
|
||||
|
||||
1. **首遇感链**
|
||||
- 所有 `encounter.characterId` 的角色型 NPC,第一次真正接触时都先走通用首遇规则。
|
||||
- 首遇时的亲疏、开放程度和选项排序由“当前好感度 + 是否首遇”共同决定。
|
||||
- `initial_companion / camp_companion` 只保留场景与演出意义,不再承担特殊对话规则本体。
|
||||
|
||||
2. **背景解锁链**
|
||||
- 角色背景改成按好感度分章节解锁。
|
||||
- 未解锁章节既不能在 UI 明文显示,也不能进入 prompt 上下文。
|
||||
|
||||
3. **私聊链**
|
||||
- 队伍中的高好感同伴才能解锁私聊。
|
||||
- 在同伴详情面板中点击按钮打开现有聊天弹窗。
|
||||
- 私聊只吃“公开信息 + 已解锁背景 + 最近共同经历”,不能越权看到锁住的背景章节。
|
||||
|
||||
## 3. 所有角色通用的“首遇感”方案
|
||||
|
||||
## 3.1 核心原则
|
||||
|
||||
必须把下面两件事拆开:
|
||||
|
||||
1. 当前好感度高低
|
||||
2. 这是不是第一次真正接触
|
||||
|
||||
因为:
|
||||
|
||||
- `好感低` 不等于 `第一次见面`
|
||||
- `好感不低` 也不等于 `已经聊过很多轮`
|
||||
|
||||
正确做法应该是“双轴控制”:
|
||||
|
||||
- 好感决定:
|
||||
- 语气亲疏
|
||||
- 允许透露的信息深度
|
||||
- 哪些功能当前有机会成立
|
||||
|
||||
- 首遇状态决定:
|
||||
- 对话必须先像“第一次对上人”
|
||||
- 优先说现场判断、态度试探、短钩子
|
||||
- 不能一上来就跳到“已经熟悉彼此”的后续轮节奏
|
||||
|
||||
## 3.2 状态设计
|
||||
|
||||
建议给 `NpcPersistentState` 增加一个通用字段:
|
||||
|
||||
```ts
|
||||
interface NpcPersistentState {
|
||||
affinity: number;
|
||||
relationState?: RoleRelationState;
|
||||
helpUsed: boolean;
|
||||
chattedCount: number;
|
||||
giftsGiven: number;
|
||||
inventory: InventoryItem[];
|
||||
recruited: boolean;
|
||||
revealedFacts?: string[];
|
||||
knownAttributeRumors?: string[];
|
||||
firstMeaningfulContactResolved?: boolean;
|
||||
seenBackstoryChapterIds?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `firstMeaningfulContactResolved`
|
||||
- 适用于所有角色型 NPC
|
||||
- 表示“玩家是否已经和这个角色完成过第一轮真正接触”
|
||||
- 不等于 `chattedCount > 0`
|
||||
- 不等于 `recruited === true`
|
||||
- 不等于 `affinity` 已经较高
|
||||
|
||||
- `seenBackstoryChapterIds`
|
||||
- 用于背景章节首次解锁时的提示去重
|
||||
|
||||
建议补这组 helper:
|
||||
|
||||
```ts
|
||||
function isNpcFirstMeaningfulContact(
|
||||
encounter: Encounter,
|
||||
npcState: NpcPersistentState,
|
||||
): boolean
|
||||
|
||||
function markNpcFirstMeaningfulContactResolved(
|
||||
npcState: NpcPersistentState,
|
||||
): NpcPersistentState
|
||||
```
|
||||
|
||||
判断建议:
|
||||
|
||||
- 仅对 `encounter.characterId` 的角色型 NPC 启用
|
||||
- `firstMeaningfulContactResolved !== true` 时,进入首遇模式
|
||||
|
||||
## 3.3 首遇时的好感度矩阵
|
||||
|
||||
首遇不是永远冷冰冰,而是要落在“当前好感度对应的位置”上。
|
||||
|
||||
建议把首遇风格做成这张矩阵:
|
||||
|
||||
| 当前关系站位 | 好感区间参考 | 首遇时的感觉 | 信息上限 | 选项重心 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `guarded` | `<= 14` | 明显戒备、先观察你值不值得回应 | 只谈眼前局势、模糊钩子 | 观察、试探、判断 |
|
||||
| `neutral` | `15 - 34` | 可以正常交流,但还不是熟人 | 可给表层理由,不给深层秘密 | 互探来意、确认立场 |
|
||||
| `cooperative` | `35 - 59` | 带基础善意或认可,但仍是第一次正式接触 | 能谈轮廓,不谈全部底牌 | 确认合作、交换判断 |
|
||||
| `bonded` | `>= 60` | 明显信任,但仍应像“第一次真正见到本人/第一次正面对接” | 可谈较深动机,但不一次讲完所有旧事 | 确认并肩关系、推进共同目标 |
|
||||
|
||||
重点不是绝对数值,而是这句话:
|
||||
|
||||
**第一次见面时,角色可以按当前好感更冷或更暖,但不能直接写成“已经是后续轮”。**
|
||||
|
||||
## 3.4 首遇选项改法
|
||||
|
||||
当前 `getNpcChatTopics(...)` 和 `buildNpcEncounterStoryMoment(...)` 更像“已进入常规互动层”后的选项生成。
|
||||
|
||||
建议新增一层:
|
||||
|
||||
```ts
|
||||
function getNpcFirstContactTopics(
|
||||
encounter: Encounter,
|
||||
npcState: NpcPersistentState,
|
||||
): StoryOption[]
|
||||
|
||||
function buildNpcFirstContactOptionCatalog(
|
||||
encounter: Encounter,
|
||||
npcState: NpcPersistentState,
|
||||
baseOptions: StoryOption[],
|
||||
): StoryOption[]
|
||||
```
|
||||
|
||||
规则建议:
|
||||
|
||||
1. 只要是 `firstMeaningfulContactResolved !== true`,前 2 个选项必须来自首遇话题池。
|
||||
|
||||
2. 首遇话题池只做这几类:
|
||||
- 现场判断
|
||||
- 你为什么在这里
|
||||
- 你刚才在观察什么
|
||||
- 你对我的态度和判断
|
||||
- 前面那件事到底哪里不对
|
||||
|
||||
3. 现有其他合法功能可以继续保留,但排序必须后置:
|
||||
- `npc_trade`
|
||||
- `npc_help`
|
||||
- `npc_gift`
|
||||
- `npc_quest_accept`
|
||||
- `npc_recruit`
|
||||
|
||||
4. 首遇状态下,不允许前两项直接变成:
|
||||
- 深背景追问
|
||||
- 直接招募
|
||||
- 直接交易
|
||||
- 直接切磋
|
||||
- 直接熟人式寒暄
|
||||
|
||||
这里的关键不是“把功能全部禁掉”,而是:
|
||||
|
||||
**先保证玩家看到的是首遇节奏,再决定这一轮后面还要不要承接更深功能。**
|
||||
|
||||
## 3.5 首遇期功能排序规则
|
||||
|
||||
为了兼容“不同角色当前好感不同”的需求,建议不要把首遇期做成一刀切的硬封锁,而是做成排序和表达规则:
|
||||
|
||||
1. `npc_chat`
|
||||
- 首遇期必须优先出现
|
||||
- 至少两个切口都与“眼前”和“第一判断”有关
|
||||
|
||||
2. `npc_recruit`
|
||||
- 如果按当前好感已经合法,可以出现
|
||||
- 但不能排在前两项
|
||||
- 文案要像“确认是否并肩同行”,不能像已经熟到直接收编
|
||||
|
||||
3. `npc_trade / npc_help / npc_quest_accept`
|
||||
- 角色职业允许时可以出现
|
||||
- 但文案必须像“先试着谈这件事”,不是默认彼此早就熟悉流程
|
||||
|
||||
4. `npc_fight / npc_spar`
|
||||
- 如果剧情确实需要,可以保留
|
||||
- 但 storyText 里仍要先体现“第一次正面对上”的张力
|
||||
|
||||
这样做的好处是:
|
||||
|
||||
- 既满足“每个角色都按当前好感落点来写”
|
||||
- 又不会把首遇感做成只能用于开局同伴的特殊模板
|
||||
|
||||
## 3.6 首遇状态何时结束
|
||||
|
||||
建议当玩家与该角色完成一次真正交互结算后,就把 `firstMeaningfulContactResolved` 设为 `true`。
|
||||
|
||||
推荐计入“真正交互”的动作:
|
||||
|
||||
- `npc_preview_talk` 后进入并完成一次正式对白
|
||||
- `npc_chat`
|
||||
- `npc_help`
|
||||
- `npc_trade`
|
||||
- `npc_gift`
|
||||
- `npc_recruit`
|
||||
- `npc_quest_accept`
|
||||
- `npc_spar`
|
||||
- `npc_fight`
|
||||
|
||||
不建议计入:
|
||||
|
||||
- 仅看了一眼就离开
|
||||
- 只打开 UI 没有完成结算
|
||||
|
||||
这样可以保证:
|
||||
|
||||
- 第一次接触是独立阶段
|
||||
- 第二次开始才进入当前系统已有的常规关系推进节奏
|
||||
|
||||
## 3.7 prompt 与 fallback 约束
|
||||
|
||||
建议新增一个通用的首遇指令,而不是继续把首遇逻辑绑定在开场专用 helper 上。
|
||||
|
||||
### 1. prompt 上下文字段
|
||||
|
||||
建议在 `StoryGenerationContext` 增加:
|
||||
|
||||
```ts
|
||||
isFirstMeaningfulContact?: boolean;
|
||||
firstContactRelationStance?: 'guarded' | 'neutral' | 'cooperative' | 'bonded' | null;
|
||||
```
|
||||
|
||||
### 2. prompt 约束
|
||||
|
||||
只要 `isFirstMeaningfulContact === true`:
|
||||
|
||||
- 不再把 `character.backstory` 作为“主角背景”直接注入
|
||||
- 不再把 `encounterCharacter.backstory` 作为“背景”直接注入
|
||||
- 只允许注入:
|
||||
- `publicSummary`
|
||||
- `surfaceHook`
|
||||
- `immediateConcern`
|
||||
- 已公开的角色描述
|
||||
- 现场状态
|
||||
- 当前关系站位
|
||||
|
||||
### 3. fallback 约束
|
||||
|
||||
如果保留现有 `buildInitialCompanionDialogueText(...)` 一类 helper:
|
||||
|
||||
- 它们只能作为“某个具体场景下调用通用首遇规则”的薄包装
|
||||
- 不应继续承担独立的开场规则系统
|
||||
|
||||
也就是说:
|
||||
|
||||
**开场营地只是首遇规则的一个调用场景,不是首遇规则本体。**
|
||||
|
||||
## 4. 背景故事分层解锁设计
|
||||
|
||||
## 4.1 核心原则
|
||||
|
||||
背景故事要拆成“能公开的层”和“需要建立关系后才能知道的层”。
|
||||
|
||||
约束是:
|
||||
|
||||
1. 未达到对应好感度前,该章节不能进入 prompt 上下文。
|
||||
2. 未达到对应好感度前,该章节不能在角色详情中完整展示。
|
||||
3. 即使模型之前已经从别的地方生成过相关话头,未解锁章节也不能被总结层写成稳定关系记忆。
|
||||
|
||||
## 4.2 数据结构
|
||||
|
||||
建议在 `Character` 上增加背景解锁配置:
|
||||
|
||||
```ts
|
||||
interface CharacterBackstoryChapter {
|
||||
id: string;
|
||||
title: string;
|
||||
affinityRequired: number;
|
||||
teaser: string;
|
||||
content: string;
|
||||
contextSnippet: string;
|
||||
}
|
||||
|
||||
interface CharacterBackstoryRevealConfig {
|
||||
publicSummary: string;
|
||||
chapters: CharacterBackstoryChapter[];
|
||||
privateChatUnlockAffinity?: number;
|
||||
}
|
||||
|
||||
interface Character {
|
||||
id: string;
|
||||
name: string;
|
||||
title: string;
|
||||
description: string;
|
||||
backstory: string;
|
||||
backstoryReveal?: CharacterBackstoryRevealConfig;
|
||||
}
|
||||
```
|
||||
|
||||
字段职责:
|
||||
|
||||
- `backstory`
|
||||
- 保留为原始完整设定文本
|
||||
- 不再默认直接进入运行时 prompt
|
||||
|
||||
- `publicSummary`
|
||||
- 永远允许展示和注入
|
||||
- 只用于“第一印象”和公开层信息
|
||||
|
||||
- `teaser`
|
||||
- 章节未解锁时给 UI 的短提示
|
||||
|
||||
- `content`
|
||||
- 玩家真正看到的章节正文
|
||||
|
||||
- `contextSnippet`
|
||||
- 允许注入模型上下文的精简版
|
||||
- 明确只包含“系统允许此阶段知道的事实”
|
||||
|
||||
## 4.3 推荐阈值
|
||||
|
||||
建议不要直接复用 `guarded / partial / honest / deep` 作为背景章节解锁阈值,因为当前对话阶段对“已招募”有额外放宽。
|
||||
|
||||
尤其是:
|
||||
|
||||
- `getNpcDisclosureStage(...)`
|
||||
- `getNpcWarmthStage(...)`
|
||||
|
||||
目前都会在 `recruited === true` 时直接给更高阶段。
|
||||
|
||||
这适合控制说话语气,但不适合控制“背景章节是否已解锁”。
|
||||
|
||||
因此建议新增独立阈值:
|
||||
|
||||
| 层级 | 建议阈值 | 含义 |
|
||||
| --- | --- | --- |
|
||||
| 公开层 | `0` | 只展示公开印象,不算真正解锁 |
|
||||
| 第一章 | `20` | 来路表层、最近为何会出现在这里 |
|
||||
| 第二章 | `40` | 旧事伤痕、过去某段关键经历 |
|
||||
| 第三章 | `65` | 真正目标、必须完成的事 |
|
||||
| 第四章 | `85` | 最深层秘密、最不愿轻易说出的事实 |
|
||||
|
||||
这样做的好处是:
|
||||
|
||||
- 首遇阶段仍然有“先认识再深入”的节奏
|
||||
- 招募后也不会立刻把全部背景放开
|
||||
- 高好感的成长曲线能真正体现在“知道了多少”
|
||||
|
||||
## 4.4 运行时规则
|
||||
|
||||
建议增加这组 helper:
|
||||
|
||||
```ts
|
||||
function getUnlockedBackstoryChapters(
|
||||
character: Character,
|
||||
affinity: number,
|
||||
): CharacterBackstoryChapter[]
|
||||
|
||||
function getNextLockedBackstoryChapter(
|
||||
character: Character,
|
||||
affinity: number,
|
||||
): CharacterBackstoryChapter | null
|
||||
|
||||
function buildBackstoryPromptContext(
|
||||
character: Character,
|
||||
affinity: number,
|
||||
): string[]
|
||||
```
|
||||
|
||||
使用规则:
|
||||
|
||||
- UI 展示:
|
||||
- `publicSummary` 永远可见
|
||||
- 已解锁章节显示全文
|
||||
- 未解锁章节显示 `title + teaser + 所需好感`
|
||||
|
||||
- prompt 注入:
|
||||
- 只能使用 `publicSummary + unlocked.contextSnippet`
|
||||
- 禁止直接把 `backstory` 全文塞给模型
|
||||
|
||||
- 总结沉淀:
|
||||
- 关系总结、私聊总结只允许总结已解锁章节
|
||||
- 未解锁章节即使被模型“猜中”,也不写入稳定状态
|
||||
|
||||
## 4.5 解锁提示
|
||||
|
||||
建议当好感跨越阈值时,在运行时给一次轻提示:
|
||||
|
||||
- 文案示例:
|
||||
- `你对 宁霜 的过去有了更多了解:旧军密图`
|
||||
- `你察觉到 萧烬 愿意谈及更深的一层旧事了`
|
||||
|
||||
实现上不需要新增复杂通知系统,可以先:
|
||||
|
||||
- 在好感变化后检查本次跨越了哪些章节阈值
|
||||
- 若有新章节且 `seenBackstoryChapterIds` 未记录,则插入一条轻量 UI 提示或一条短故事结果文本
|
||||
|
||||
## 5. “未解锁背景不能加入上下文”的具体边界
|
||||
|
||||
这个需求要同时卡住 4 个入口:
|
||||
|
||||
## 5.1 主线剧情 prompt
|
||||
|
||||
`buildStoryContextFromState(...)` 与 `prompt.ts` 中:
|
||||
|
||||
- 角色背景不能再直接读取 `character.backstory`
|
||||
- 应改成读取 `buildBackstoryPromptContext(...)`
|
||||
|
||||
## 5.2 NPC 聊天 prompt
|
||||
|
||||
`buildStrictNpcChatDialoguePrompt(...)` 中:
|
||||
|
||||
- 只能拿 `publicSummary + 已解锁章节 + 最近共同经历`
|
||||
- 未解锁章节不能出现在“当前面前实体”描述里
|
||||
- 若当前还是首遇模式,还要进一步收紧到“公开层 + 当前场景判断”
|
||||
|
||||
## 5.3 私聊 prompt
|
||||
|
||||
`useCharacterChatFlow(...)` 中:
|
||||
|
||||
- `streamCharacterPanelChatReply(...)`
|
||||
- `generateCharacterPanelChatSuggestions(...)`
|
||||
- `generateCharacterPanelChatSummary(...)`
|
||||
|
||||
都只能吃已解锁背景。
|
||||
|
||||
## 5.4 关系摘要回写
|
||||
|
||||
`gameState.characterChats[characterId].summary` 是会反向进入主线上下文的。
|
||||
|
||||
所以必须再加一层约束:
|
||||
|
||||
- 如果某段私聊总结提到了未解锁章节内容,则不允许写回稳定 summary
|
||||
- 或者更稳妥地说,summary 生成 prompt 本身就只提供已解锁背景
|
||||
|
||||
推荐做法是后者,因为它更简单也更可控。
|
||||
|
||||
## 6. 高好感同伴私聊设计
|
||||
|
||||
## 6.1 解锁条件
|
||||
|
||||
建议私聊不是“所有招募同伴自动可用”,而是满足以下条件后才解锁:
|
||||
|
||||
1. 角色已在队伍体系中
|
||||
- `gameState.companions`
|
||||
- 或 `gameState.roster`
|
||||
|
||||
2. 对应 `npcState.recruited === true`
|
||||
|
||||
3. 当前好感度达到私聊阈值
|
||||
- 默认建议 `70`
|
||||
- 允许角色级配置覆盖:`backstoryReveal.privateChatUnlockAffinity`
|
||||
|
||||
之所以不用“已招募”直接等于“可私聊”,原因很简单:
|
||||
|
||||
- 当前系统里已招募会把对话阶段直接推高
|
||||
- 但用户要求的是“高好感同伴解锁私聊”
|
||||
- 所以私聊需要独立阈值,不应绑定到 `warm / deep`
|
||||
|
||||
## 6.2 入口位置
|
||||
|
||||
建议把入口放在两个地方,但以“同伴角色信息面板”为主:
|
||||
|
||||
### 1. `AdventureEntityModal`
|
||||
|
||||
这是游戏内点击同伴详情时实际打开的面板,优先级最高。
|
||||
|
||||
在 `selection.kind === 'companion'` 时增加:
|
||||
|
||||
- 已解锁:`聊天`
|
||||
- 未解锁:置灰按钮 + 提示 `好感达到 70 后解锁,当前 58`
|
||||
|
||||
点击逻辑:
|
||||
|
||||
1. 关闭 `AdventureEntityModal`
|
||||
2. 调 `characterChatUi.openChat(target)`
|
||||
3. 打开现有 `CharacterChatModal`
|
||||
|
||||
### 2. `CharacterPanel`
|
||||
|
||||
如果以后存在不走 `AdventureEntityModal` 的详情流,也应在成员详情面板里显示同样按钮,保持一致。
|
||||
|
||||
## 6.3 聊天目标结构
|
||||
|
||||
建议扩展 `CharacterChatTarget`:
|
||||
|
||||
```ts
|
||||
type CharacterChatTarget = {
|
||||
character: Character;
|
||||
npcId: string | null;
|
||||
roleLabel: string;
|
||||
hp: number;
|
||||
maxHp: number;
|
||||
mana: number;
|
||||
maxMana: number;
|
||||
affinity?: number;
|
||||
unlockedBackstoryChapterIds?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
这样聊天弹窗里可以直接显示:
|
||||
|
||||
- 当前关系热度
|
||||
- 已解锁背景章节数
|
||||
- 下一段背景解锁阈值
|
||||
|
||||
## 6.4 私聊的上下文来源
|
||||
|
||||
私聊上下文建议固定由这几部分组成:
|
||||
|
||||
1. 角色公开信息
|
||||
- `name / title / description / personality`
|
||||
|
||||
2. 当前关系信息
|
||||
- 当前好感
|
||||
- 是否出战 / 是否营地待命
|
||||
- 最近共同经历
|
||||
|
||||
3. 已解锁背景章节
|
||||
- 只传 `contextSnippet`
|
||||
|
||||
4. 最近私聊记录
|
||||
- `characterChats[characterId].history`
|
||||
- `characterChats[characterId].summary`
|
||||
|
||||
明确不传:
|
||||
|
||||
- 未解锁背景章节
|
||||
- 角色完整 `backstory`
|
||||
|
||||
## 6.5 私聊对数值的影响
|
||||
|
||||
建议本期私聊先不直接改好感数值,理由是:
|
||||
|
||||
- 避免玩家通过无限聊天刷关系
|
||||
- 先把“关系表达层”和“规则数值层”分开
|
||||
- 当前已有送礼、聊天、切磋、求助、任务等好感入口,足够支撑成长
|
||||
|
||||
私聊本期的价值应放在:
|
||||
|
||||
- 沉淀角色关系摘要
|
||||
- 解锁更深层的文本风格和背景信息
|
||||
- 让队伍中的同伴真正像“会私下说话的人”
|
||||
|
||||
如果后续要给私聊轻量数值收益,建议再单独加冷却或每日首次奖励,不要在本期一起做。
|
||||
|
||||
## 7. UI 设计建议
|
||||
|
||||
## 7.1 同伴详情面板
|
||||
|
||||
建议增加一个“关系 / 档案”区块:
|
||||
|
||||
- 当前好感:`58`
|
||||
- 关系阶段:`谨慎` / `熟络` / `深信`
|
||||
- 首遇状态:
|
||||
- `初次接触未完成`
|
||||
- 或 `已完成第一次正式对接`
|
||||
- 私聊状态:
|
||||
- `未解锁(70)`
|
||||
- 或 `已解锁`
|
||||
|
||||
下面接“背景档案”:
|
||||
|
||||
- 公开印象
|
||||
- 章节卡片列表
|
||||
|
||||
章节卡片状态:
|
||||
|
||||
- 已解锁
|
||||
- 标题
|
||||
- 正文
|
||||
|
||||
- 未解锁
|
||||
- 标题
|
||||
- `需要好感 40`
|
||||
- `teaser`
|
||||
- 遮罩态
|
||||
|
||||
## 7.2 私聊按钮状态
|
||||
|
||||
按钮文案建议:
|
||||
|
||||
- 已解锁:`聊天`
|
||||
- 未解锁:`聊天(70 解锁)`
|
||||
|
||||
按钮旁边可以补一行细字:
|
||||
|
||||
- `仅高好感同伴可进行私聊`
|
||||
|
||||
## 7.3 解锁反馈
|
||||
|
||||
建议当玩家在详情面板里刚好看到某一章解锁时:
|
||||
|
||||
- 章节卡片做一次轻量高亮
|
||||
- 私聊按钮从置灰切到可点击时也做一次轻量强调
|
||||
|
||||
这样能把“关系成长”明确反馈给玩家,而不只是静态数字变化。
|
||||
|
||||
## 8. 落地文件建议
|
||||
|
||||
## 8.1 类型与数据
|
||||
|
||||
- `src/types/characters.ts`
|
||||
- 增加 `CharacterBackstoryChapter`
|
||||
- 增加 `CharacterBackstoryRevealConfig`
|
||||
|
||||
- `src/types/scene.ts`
|
||||
- 给 `NpcPersistentState` 增加
|
||||
- `firstMeaningfulContactResolved`
|
||||
- `seenBackstoryChapterIds`
|
||||
|
||||
- `src/data/characterPresets.ts`
|
||||
- 为可招募角色补 `backstoryReveal`
|
||||
- 配置分章节阈值和 `privateChatUnlockAffinity`
|
||||
|
||||
## 8.2 关系与规则
|
||||
|
||||
- `src/data/npcInteractions.ts`
|
||||
- 增加首遇状态 helper
|
||||
- 增加首遇话题 helper
|
||||
- 增加背景章节解锁 helper
|
||||
- 增加私聊解锁 helper
|
||||
- 调整首遇期的功能排序规则
|
||||
|
||||
## 8.3 剧情与 prompt
|
||||
|
||||
- `src/hooks/useStoryGeneration.ts`
|
||||
- 新增通用首遇状态流转
|
||||
- 给首遇角色注入统一的 first-contact context
|
||||
- 处理好感跨章节时的解锁通知
|
||||
|
||||
- `src/services/aiTypes.ts`
|
||||
- 给 `StoryGenerationContext` 增加
|
||||
- `isFirstMeaningfulContact`
|
||||
- `firstContactRelationStance`
|
||||
- 已解锁背景片段字段
|
||||
|
||||
- `src/services/prompt.ts`
|
||||
- 移除对完整 `backstory` 的直接注入
|
||||
- 改为按已解锁章节构造角色背景上下文
|
||||
- 首遇模式下额外收紧信息披露
|
||||
|
||||
## 8.4 UI 与交互
|
||||
|
||||
- `src/components/AdventureEntityModal.tsx`
|
||||
- 显示同伴关系/档案区
|
||||
- 增加聊天按钮
|
||||
|
||||
- `src/components/CharacterPanel.tsx`
|
||||
- 补齐聊天按钮接线
|
||||
- 若保留本地详情弹窗,也同步显示档案区
|
||||
|
||||
- `src/components/GameShell.tsx`
|
||||
- 把 `characterChatUi.openChat` 透传给 `AdventureEntityModal`
|
||||
|
||||
- `src/hooks/story/characterChat.ts`
|
||||
- 私聊 prompt 只使用已解锁章节
|
||||
|
||||
## 9. 推荐开发顺序
|
||||
|
||||
建议按这 4 步做,不要反过来:
|
||||
|
||||
1. 先补数据建模
|
||||
- `backstoryReveal`
|
||||
- `firstMeaningfulContactResolved`
|
||||
- 解锁 helper
|
||||
|
||||
2. 再补规则
|
||||
- 首遇状态判断
|
||||
- 首遇选项排序
|
||||
- 首遇完成后的状态流转
|
||||
- 私聊解锁条件
|
||||
|
||||
3. 再补 prompt 上下文边界
|
||||
- 禁止未解锁背景进入模型上下文
|
||||
- 禁止首遇期被写成后续轮
|
||||
|
||||
4. 最后补 UI
|
||||
- 详情面板档案区
|
||||
- 聊天按钮
|
||||
- 解锁提示
|
||||
|
||||
## 10. 验收标准
|
||||
|
||||
做到以下几点,才算这次需求真正完成:
|
||||
|
||||
1. 玩家第一次遇见任一角色型 NPC 时,对话会像第一次真正接触,而不是像已经聊过很多轮。
|
||||
2. 同一个角色第一次见面时的冷暖程度,会落在该角色当前好感对应的位置上,而不是被强行写成统一冷场模板。
|
||||
3. 首遇阶段的前两项选项会优先围绕“眼前局势、互相判断、来意试探”,而不是直接跳进深聊、招募或熟人功能。
|
||||
4. 未达到阈值的背景章节在 UI 中不可完整查看,在 prompt 中也不可被注入。
|
||||
5. 好感提高后,角色详情面板中的背景章节会逐步解锁。
|
||||
6. 队伍中的高好感同伴会在详情面板中出现“聊天”按钮。
|
||||
7. 点击“聊天”后,能直接弹出现有私聊弹窗。
|
||||
8. 私聊内容、建议、总结都不会越权使用未解锁背景。
|
||||
9. 旧存档读取后不会崩,缺省字段能平稳兜底。
|
||||
|
||||
## 11. 一句话收束
|
||||
|
||||
这次需求的关键不是“给开局补一段特判对白”,而是把:
|
||||
|
||||
- 首遇节奏
|
||||
- 背景披露节奏
|
||||
- 私聊入口
|
||||
- 关系记忆
|
||||
|
||||
统一到同一套“好感度 + 首遇状态 + 信息解锁”的通用规则里。
|
||||
|
||||
只有这样,玩家遇见每一个角色时才都会有对应关系位置上的初识感,角色背景才会像一步步了解出来的,同伴私聊也才会真的有价值。
|
||||
283
docs/CURRENT_GAME_ITERATION_PRIORITIES_2026-04-03.md
Normal file
283
docs/CURRENT_GAME_ITERATION_PRIORITIES_2026-04-03.md
Normal file
@@ -0,0 +1,283 @@
|
||||
# 当前游戏优先迭代清单(2026-04-03)
|
||||
|
||||
## 结论先说
|
||||
|
||||
当前阶段最不该做的,是继续零散加玩法、加场景、加文案,却让主链路、规则底座和工程门禁继续处在半完成状态。
|
||||
|
||||
按现有文档和代码状态看,建议优先级顺序如下:
|
||||
|
||||
1. `P0`:先恢复工程绿色基线,并把运行时主链路继续拆开
|
||||
2. `P1`:再落统一角色属性底座,作为战斗 / 对话 / 招募 / Build / 掉落 / 任务的共同语义基础
|
||||
3. `P1`:在统一属性底座之上,重做 Build、运行时物品奖励、任务系统三条核心玩法链
|
||||
4. `P2`:最后收尾编辑器共享层、本地 API 分层、移动端体验与运行时包体优化
|
||||
|
||||
一句话判断:
|
||||
|
||||
**现在的优先级不是“继续扩玩法宽度”,而是“先把底层规则、主流程边界和工程可维护性补齐,再扩玩法深度”。**
|
||||
|
||||
---
|
||||
|
||||
## 优先级清单
|
||||
|
||||
## P0-1:恢复绿色基线,收紧质量门禁
|
||||
|
||||
### 为什么必须排第一
|
||||
|
||||
- `docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md` 已明确指出:当前最值得优先优化的不是继续加功能,而是把“半完成的工程化”补齐。
|
||||
- 文档中提到过 `lint` 失败、`build` warning、核心热区文件被 ESLint ignore、部分测试未进入默认套件,这意味着当前代码库还不在真正稳定的绿色基线。
|
||||
- 在这种状态下继续叠加新玩法,只会把问题扩散到更多运行时链路和编辑器链路。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 修复现有 `lint` / `build warning` / 明确可见的门禁破口
|
||||
- 缩小高风险核心文件的 ignore 范围
|
||||
- 让 `lint + typecheck + test + build + check:content` 成为可信的统一门禁
|
||||
- 对 warning 建立“尽快清零”策略,而不是长期带病开发
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- 主开发分支长期保持 `npm run check` 可稳定通过
|
||||
- 核心运行时文件不再依赖长期 ignore 才能过门禁
|
||||
- 构建 warning 收敛到零或有非常明确的短期处理计划
|
||||
|
||||
### 为什么它比新功能更优先
|
||||
|
||||
- 没有绿色基线,后续所有大改都缺少可靠回归保护
|
||||
- 这一步是后面统一属性、任务重构、物品系统重构的前置条件
|
||||
|
||||
---
|
||||
|
||||
## P0-2:继续拆运行时主链路,防止核心 hook 和壳层继续膨胀
|
||||
|
||||
### 为什么必须紧跟在 P0-1 后面
|
||||
|
||||
- `docs/PROJECT_DEVELOPMENT_EXPERIENCE.md`、`docs/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`、`docs/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md` 都反复强调:这个项目是叙事、状态、演出、界面四条链路耦合的复合项目,不能靠大文件硬扛。
|
||||
- `docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md` 和 `docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md` 一致指出,`useStoryGeneration`、`useCombatFlow`、`GameShell` 仍然是当前最大的复杂度集中点。
|
||||
- 如果不先拆主链,后面的统一属性系统、任务系统、物品导演层都会继续堆进现有巨型流程控制器,技术债只会翻倍。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 把 `useStoryGeneration` 收敛为 orchestration 层,不再直接吞下 NPC、任务、背包、锻造、聊天、奖励等全部细节
|
||||
- 把 `useCombatFlow` 进一步拆成“战斗结算”与“播放/演出同步”
|
||||
- 让 `GameShell` 回到流程壳层职责,把 selection flow、overlay、scene transition 继续下沉
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- 新功能接入时,不需要再跨 `story + combat + panel + modal` 四五层一起改
|
||||
- 核心流程可以按领域补测试,而不是只能做人工回归
|
||||
- 后续玩法扩展能优先加领域模块,而不是继续往大 hook 里塞逻辑
|
||||
|
||||
### 这一项的实际意义
|
||||
|
||||
- 这是“后续还能继续做大”的结构前提
|
||||
- 不做这一步,任何系统升级都会越来越难落地
|
||||
|
||||
---
|
||||
|
||||
## P1-1:落地统一角色属性系统,作为全玩法共同底座
|
||||
|
||||
### 为什么它是最优先的玩法底座
|
||||
|
||||
- `docs/prd/AI_NATIVE_UNIFIED_ROLE_ATTRIBUTE_SYSTEM_PRD_2026-04-02.md` 已经把问题说得很清楚:当前玩家、NPC、怪物、Build、对话、掉落还没有共享同一套解释坐标。
|
||||
- 当前项目已经有 NPC 关系、怪物标签、Build 语义、自定义世界生成能力,但这些系统之间还缺一套统一的世界级属性 schema。
|
||||
- 如果先做任务、物品、Build 深化,而不先统一属性,后面很容易再次出现“每个系统各自解释角色”的分裂。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 为预设世界固化世界级属性 schema
|
||||
- 为玩家角色、怪物、关键 NPC 补 `attributeProfile`
|
||||
- 建立统一的属性解析与校验层
|
||||
- 先让对话 / 招募 / 送礼 / 详情面板开始读取这套新属性解释
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- 玩家、NPC、怪物都能落到同一套属性语义里
|
||||
- 聊天、送礼、招募至少有一条链可以直接解释到属性层
|
||||
- 自定义世界也能生成并持久化自己的属性 schema
|
||||
|
||||
### 为什么这项优先于“多做内容”
|
||||
|
||||
- 这是后面 Build、物品、任务三条系统统一升级的共同前提
|
||||
- 没有这层底座,玩法会继续“能跑,但彼此不共语义”
|
||||
|
||||
---
|
||||
|
||||
## P1-2:把 Build 系统从“标签互相影响”改成“标签匹配角色属性”
|
||||
|
||||
### 为什么这里要尽快做
|
||||
|
||||
- `docs/prd/BUILD_SYSTEM_ATTRIBUTE_SIMILARITY_PRD_2026-04-02.md` 指出:当前 Build 更像标签网络效应,解释成本高、平衡成本高、角色差异感不够强。
|
||||
- 一旦统一角色属性系统先落地,Build 就是最适合第二个接入的玩法层,因为它最直接影响战斗反馈和角色成长感。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 为 Build 标签补属性亲和度向量
|
||||
- 改写 `buildDamage` 逻辑,让每个标签独立匹配当前角色属性画像
|
||||
- 调整 Build 面板文案,从“标签协同”转成“属性适配度”
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- 玩家能理解“为什么这个标签适合当前角色”
|
||||
- 新增标签只影响自身贡献,不再扰动整张标签网络
|
||||
- Build 面板能解释收益来自哪些属性
|
||||
|
||||
### 实际收益
|
||||
|
||||
- 提高可解释性
|
||||
- 降低平衡难度
|
||||
- 让角色差异感真正进入 Build 体验
|
||||
|
||||
---
|
||||
|
||||
## P1-3:重做运行时物品奖励,让奖励真正贴合场景、NPC、最近事件和 Build 缺口
|
||||
|
||||
### 为什么它值得排在任务系统前面
|
||||
|
||||
- `docs/AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md` 明确指出:当前宝藏、NPC、任务、锻造等入口都有物品,但缺少统一导演层,奖励与场景/NPC/事件的贴合度不够高。
|
||||
- 相比任务系统,运行时物品奖励能更快提升“世界贴脸感”和“当下反馈质量”,且可以先从宝藏入口低风险落地。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 增加运行时物品上下文采样、导演层、编译器和叙事回写层
|
||||
- 统一宝藏、NPC 奖励、怪物掉落、任务奖励的物品生成入口
|
||||
- 让奖励优先围绕 Build 标签、限时 Build Buff、少量数值补足来设计
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- 至少宝藏和 NPC 奖励接入统一导演层
|
||||
- 物品能解释“为什么在这里出现、和谁有关、补的是什么方向”
|
||||
- 物品来源可以进入背包、剧情、锻造与存档的同一套结构
|
||||
|
||||
### 实际收益
|
||||
|
||||
- 奖励不再像泛用掉落池
|
||||
- 世界、人物、最近剧情与成长反馈终于真正连起来
|
||||
|
||||
---
|
||||
|
||||
## P1-4:把任务系统从“单目标单阶段”升级成“意图 -> 合约 -> 信号推进”
|
||||
|
||||
### 为什么它仍然是高优先级
|
||||
|
||||
- `docs/prd/AI_NATIVE_QUEST_SYSTEM_PRD_2026-04-02.md` 已经指出:当前任务闭环是成立的,但任务来源偏静态、结构偏扁平、状态过粗、奖励和关系变化也不够贴语境。
|
||||
- 当前项目已经具备任务 UI、任务奖励、NPC 交互、剧情推进链,这说明任务系统适合做“升级”,而不是推倒重来。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 新增任务生成上下文、AI 任务意图层、本地任务编译层
|
||||
- 把任务推进改成统一 signal 驱动
|
||||
- 支持多 step、阶段揭示、完成后回报、后续钩子
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- NPC 接任务不再只是静态模板,而是能根据当前局面生成任务意图
|
||||
- 运行时能用统一 signal 推进任务步骤
|
||||
- 奖励除了货币/道具,还能自然进入关系、情报、后续机会
|
||||
|
||||
### 为什么它排在物品系统之后
|
||||
|
||||
- 任务系统耦合更深,适合作为统一属性和统一奖励导演层之后的升级项
|
||||
- 先把属性和物品奖励理顺,任务系统落地时会更稳
|
||||
|
||||
---
|
||||
|
||||
## P2-1:收尾编辑器共享层与本地 API 分层,让内容扩张不再继续拖慢主项目
|
||||
|
||||
### 为什么它不是最前面,但也不能拖太久
|
||||
|
||||
- 最近几份工程审查都指出:编辑器共享层、本地 JSON 写入接口、LLM 代理、Vite 插件职责仍然处于迁移中间态。
|
||||
- 当前项目已经进入“内容工具很多、正式运行时也很重”的阶段,若不收尾这部分,后续每次扩内容都会重复踩基础设施问题。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 继续拆 editor shared 层
|
||||
- 清理迁移残留和死分支
|
||||
- 把本地 API 至少按 `llm proxy / json editor api / asset catalog` 分职责拆开
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- 编辑器保存、共享组件、共享 client 不再重复实现
|
||||
- 本地 API 分工清晰,dev / preview 边界清楚
|
||||
- 编辑器扩展不再继续依赖大聚合组件
|
||||
|
||||
---
|
||||
|
||||
## P2-2:继续优化移动端冒险体验、首屏信息密度与运行时包体
|
||||
|
||||
### 为什么它放在 P2
|
||||
|
||||
- `docs/PROJECT_DEVELOPMENT_EXPERIENCE.md` 和 `docs/MOBILE_UI_DEV_EXPERIENCE.md` 都强调:冒险页必须优先保证上方演出、一屏选项和文本区自适应。
|
||||
- 但从当前文档判断,移动端体验和包体问题更像“持续治理项”,不是当前阶段最核心的系统阻塞点。
|
||||
|
||||
### 本阶段要做什么
|
||||
|
||||
- 继续优化冒险页一屏布局与文本滚动策略
|
||||
- 拆 `GameCanvas`、`AdventurePanel` 等高热区大模块
|
||||
- 按真实交互热区继续做 chunk 拆分
|
||||
|
||||
### 做到什么算完成
|
||||
|
||||
- 手机首屏稳定容纳画布、文本和关键选项
|
||||
- 核心页面热区模块更容易维护和测试
|
||||
- 构建产物中的主 chunk 有持续下降趋势
|
||||
|
||||
---
|
||||
|
||||
## 不建议当前优先做的事
|
||||
|
||||
以下内容不是不能做,而是不建议排在当前这轮前面:
|
||||
|
||||
- 大量新增世界、场景、角色 preset
|
||||
- 继续横向扩 NPC 交互种类,但不补统一规则底座
|
||||
- 继续堆宝藏、掉落、锻造分支,但不先做统一物品导演层
|
||||
- 继续增加任务模板数量,但不升级任务 contract
|
||||
- 继续往 `useStoryGeneration` / `useCombatFlow` / `GameShell` 里直接塞新逻辑
|
||||
|
||||
原因很简单:
|
||||
|
||||
**这些工作会让表面内容变多,但不会让项目变得更稳,反而会放大当前已经存在的结构问题。**
|
||||
|
||||
---
|
||||
|
||||
## 推荐迭代顺序
|
||||
|
||||
### 第一阶段:先稳住工程与主流程
|
||||
|
||||
1. 绿色基线与门禁收紧
|
||||
2. 运行时主链拆分
|
||||
|
||||
### 第二阶段:先补统一语义底座
|
||||
|
||||
1. 统一角色属性系统
|
||||
2. Build 改为属性适配
|
||||
|
||||
### 第三阶段:再深化 AI 原生玩法闭环
|
||||
|
||||
1. 运行时物品导演层
|
||||
2. 任务意图与 contract 系统
|
||||
|
||||
### 第四阶段:最后做工具与体验收尾
|
||||
|
||||
1. 编辑器共享层 / 本地 API 分层
|
||||
2. 移动端体验与包体优化
|
||||
|
||||
---
|
||||
|
||||
## 本清单的主要依据
|
||||
|
||||
- `docs/PROJECT_DEVELOPMENT_EXPERIENCE.md`
|
||||
- `docs/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
|
||||
- `docs/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md`
|
||||
- `docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md`
|
||||
- `docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md`
|
||||
- `docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md`
|
||||
- `docs/AI_NATIVE_RUNTIME_ITEM_SYSTEM_REDESIGN_2026-04-02.md`
|
||||
- `docs/prd/AI_NATIVE_UNIFIED_ROLE_ATTRIBUTE_SYSTEM_PRD_2026-04-02.md`
|
||||
- `docs/prd/BUILD_SYSTEM_ATTRIBUTE_SIMILARITY_PRD_2026-04-02.md`
|
||||
- `docs/prd/AI_NATIVE_QUEST_SYSTEM_PRD_2026-04-02.md`
|
||||
|
||||
## 最后结论
|
||||
|
||||
如果只保留一句话,那就是:
|
||||
|
||||
**当前最优先的迭代方向,不是继续堆新内容,而是先把工程基线、主流程边界和统一规则底座补齐;只有这样,AI 原生任务、物品、Build 和后续内容扩展才会真正开始越做越顺。**
|
||||
228
docs/EDITOR_GAME_PRESET_TEXT_AUDIT_2026-03-25.md
Normal file
228
docs/EDITOR_GAME_PRESET_TEXT_AUDIT_2026-03-25.md
Normal file
@@ -0,0 +1,228 @@
|
||||
# 编辑器 UI / 游戏 UI / 预设内容英文与乱码审计
|
||||
|
||||
更新时间:`2026-03-25`
|
||||
|
||||
## 范围与方法
|
||||
|
||||
- 范围只覆盖当前源码里会直接进入编辑器 UI、游戏运行时 UI、预设内容预览的文案。
|
||||
- 本轮直接按 `utf-8` 读取 `src/components` 与 `src/data` 复核,不把旧审计文档当最终事实来源。
|
||||
- 不统计 `import`、类型名、变量名、接口字段名、资源路径等纯开发层英文。
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 当前目标范围内,确认到 **1 处直接写进源码的中文乱码**:
|
||||
- `src/components/PresetEditor.tsx:72` 的 `鐗╁搧`
|
||||
- 当前更大的问题已经不是“大片中文乱码”,而是 **编辑器与部分游戏界面还残留成组英文 UI 文案**。
|
||||
- 预设内容层面的英文主要集中在 **原始枚举值 / 构筑字段**,例如 `common / rare / legendary`、`neutral`、`buildProfile.role`、`idle / move / attack / die`、`steady / burst / mobility / finisher / projectile`。这些值本身在数据层可以保留英文,但当前有一部分被界面直接原样显示出来了。
|
||||
|
||||
## 编辑器 UI
|
||||
|
||||
### 英文残留
|
||||
|
||||
- `src/components/PresetEditor.tsx:84-89`
|
||||
- 编辑器标签仍是 `Characters / NPCs / Scenes / Monsters / Items / Functions`
|
||||
- `src/components/PresetEditor.tsx:97-104`
|
||||
- 预设编辑里仍直接使用 `idle / move / attack / die` 与 `steady / burst / mobility / finisher / projectile`
|
||||
- `src/components/StateFunctionEditor.tsx:333-367`
|
||||
- 预览实体与效果摘要仍是英文:
|
||||
- `Preview NPC`
|
||||
- `Fallback NPC preview for ...`
|
||||
- `Preview Treasure`
|
||||
- `Treasure preview for ...`
|
||||
- `Damage x / Incoming x / Heal + / Mana + / Cooldown + / Turn x`
|
||||
- `src/components/StateFunctionEditor.tsx:496-607`
|
||||
- 预览阶段与提示说明仍有整段英文:
|
||||
- `Player Turn Preview`
|
||||
- `Escape Preview`
|
||||
- `Travel Result Preview`
|
||||
- `Explore Preview`
|
||||
- `Call-Out Preview`
|
||||
- `Idle Behavior Preview`
|
||||
- `Predicted skill: ...`
|
||||
- `Monster counter template uses ...`
|
||||
- `Battle behaviors are driven by skill weights ...`
|
||||
- `Escape behaviors always use the chase flow ...`
|
||||
- `src/components/StateFunctionEditor.tsx:855-939`
|
||||
- 预览面板头部与信息卡仍是英文:
|
||||
- `Option`
|
||||
- `Mode`
|
||||
- `Replay Preview`
|
||||
- `Preview Playing`
|
||||
- `Preview Ready`
|
||||
- `Live Player`
|
||||
- `Live Scene`
|
||||
- `No scene`
|
||||
- `Resolved Plan`
|
||||
- `Option kind`
|
||||
- `Target scene`
|
||||
- `Cooldowns`
|
||||
- `Battle Snapshot`
|
||||
- `Animation`
|
||||
- `Delivery`
|
||||
- `Damage`
|
||||
- `Predicted kill`
|
||||
- `Target survives`
|
||||
- `Snapshot based on live playback`
|
||||
- `src/components/ItemCatalogEditor.tsx:25`
|
||||
- 稀有度选项仍是 `common / uncommon / rare / epic / legendary`
|
||||
- `src/components/ItemCatalogEditor.tsx:486-560`
|
||||
- 物品预览区仍直接显示英文键和值:
|
||||
- `rarity`
|
||||
- `value`
|
||||
- `usable`
|
||||
- `yes / no`
|
||||
- `equip`
|
||||
- `world`
|
||||
- `neutral`
|
||||
- `HP / MP / Damage / Guard`
|
||||
- `HP Restore / MP Restore / CD Reduce`
|
||||
- `Build / 套装`
|
||||
- `Role / Set / Piece`
|
||||
- `none / standalone`
|
||||
- `src/components/NpcVisualEditor.tsx`
|
||||
- 目前大部分文案已中文化,但 `NPC` 缩写仍在标题、字段、保存提示中大量保留,属于低优先级统一项,不是乱码问题。
|
||||
|
||||
### 确认的中文乱码
|
||||
|
||||
| 文件 | 位置 | 当前文本 | 判断 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/PresetEditor.tsx` | `72` | `鐗╁搧` | 明确乱码,语义应为“物品” |
|
||||
|
||||
### 编辑器 UI 小结
|
||||
|
||||
- **最高优先级乱码修复点**:`PresetEditor.tsx:72`
|
||||
- **最高优先级英文清理点**:`StateFunctionEditor.tsx`、`ItemCatalogEditor.tsx`
|
||||
|
||||
## 游戏各界面 UI
|
||||
|
||||
### 英文残留
|
||||
|
||||
- `src/components/GameShell.tsx:414`
|
||||
- 标题副标仍是 `TAVERNREALMS`
|
||||
- `src/components/GameShell.tsx:1083-1094`
|
||||
- 团队弹窗里仍保留 `TavernRealms`
|
||||
- `src/components/CharacterChatModal.tsx:52,73,76`
|
||||
- `CHARACTER CHAT`
|
||||
- `HP`
|
||||
- `MP`
|
||||
- `src/components/CharacterDetailModal.tsx:24-40`
|
||||
- 属性与技能风格映射仍为英文:
|
||||
- `Strength / Agility / Intelligence / Spirit`
|
||||
- `Burst / Steady / Mobility / Finisher / Projectile`
|
||||
- `Female / Male / Unknown`
|
||||
- `src/components/CharacterDetailModal.tsx:149,186,194-271`
|
||||
- 详情弹窗区块标题与标签仍有整段英文:
|
||||
- `INITIAL COMPANION`
|
||||
- `Close character details`
|
||||
- `Profile`
|
||||
- `Candidate`
|
||||
- `Gender`
|
||||
- `Stats`
|
||||
- `Max HP / Max MP`
|
||||
- `Journey`
|
||||
- `Reason / Goal`
|
||||
- `Skills / Loadout / Pack / Backstory / Personality`
|
||||
- `src/components/InventoryPanel.tsx:39-50`
|
||||
- 稀有度标签仍是 `Legendary / Epic / Rare / Uncommon / Common`
|
||||
- `src/components/InventoryPanel.tsx:179-196,215`
|
||||
- 物品详情仍是英文:
|
||||
- `Quantity`
|
||||
- `Owner`
|
||||
- `Usable`
|
||||
- `Yes / No`
|
||||
- `Equipable`
|
||||
- `Value`
|
||||
- `Type`
|
||||
- `Tags`
|
||||
- `no-tags`
|
||||
- `src/components/AdventureEntityModal.tsx:206-217`
|
||||
- 自动生成的物品描述仍是英文整句:
|
||||
- `helps restore HP`
|
||||
- `supports MP recovery`
|
||||
- `fits offensive loadouts`
|
||||
- `supports defensive gearing`
|
||||
- `works as a rare trinket-grade pickup`
|
||||
- `can be saved for crafting or trading`
|
||||
- `${item.name} can be kept for trading, gifting, or future build planning.`
|
||||
- `${item.name} is a ${item.category} item that ...`
|
||||
- `src/components/AdventureEntityModal.tsx:226-235`
|
||||
- 物品属性摘要仍是英文:
|
||||
- `HP`
|
||||
- `MP`
|
||||
- `Damage`
|
||||
- `Guard x...`
|
||||
- `src/components/AdventureEntityModal.tsx:1271-1332`
|
||||
- NPC 物品详情弹窗仍有一整块英文:
|
||||
- `ITEM DETAIL`
|
||||
- `NPC inventory`
|
||||
- `Quantity`
|
||||
- `Value`
|
||||
- `Equip Slot`
|
||||
- `Not equippable`
|
||||
- `Usable item`
|
||||
- `Story, trade, or gift resource`
|
||||
- `Type`
|
||||
- `Rarity`
|
||||
- `Tags`
|
||||
- `No tags`
|
||||
- `src/components/CompanionCampModal.tsx:152,176-177,213,216`
|
||||
- `Active`
|
||||
- `HP`
|
||||
- `MP`
|
||||
- `待命 roster`
|
||||
- `Reserve`
|
||||
- `src/components/NpcModals.tsx:507`
|
||||
- 招募替换提示里仍混入 `roster`
|
||||
|
||||
### 当前未确认到的乱码
|
||||
|
||||
- 本轮没有在游戏运行时 UI 组件里复核到新的、直接写死在源码中的中文乱码。
|
||||
- 当前游戏 UI 的主要问题已经转为“英文标签 / 英文句子未汉化”,不是大面积中文乱码。
|
||||
|
||||
## 预设内容
|
||||
|
||||
### 直接会透到 UI 的英文源
|
||||
|
||||
- `src/data/itemDesign.ts:52-72`
|
||||
- 材质主题里直接保存了英文原始值:
|
||||
- `worldAffinity: "neutral"`
|
||||
- `role: "fieldcraft" / "breaker" ...`
|
||||
- `rarity: "common"`
|
||||
- `tags: ["scout", "craft"]`
|
||||
- `src/data/itemCatalog.ts:260-270`
|
||||
- `designed.rarity / designed.worldAffinity / designed.buildProfile` 会原样流入物品目录数据
|
||||
- `src/components/ItemCatalogEditor.tsx:486-560`
|
||||
- 上述原始字段当前会在编辑器预览里被直接显示,因此形成了可见英文泄漏
|
||||
- `src/data/questFlow.ts:24-30`
|
||||
- 任务奖励物品稀有度仍是 `rare / uncommon`
|
||||
- `src/data/npcInteractions.ts:71-82,102-103,166`
|
||||
- 稀有度与标签推断仍使用 `common / uncommon / rare / epic / legendary`
|
||||
- 物品标签仍使用 `weapon / armor`
|
||||
- `src/components/PresetEditor.tsx:97-104`
|
||||
- 预设编辑直接使用 `idle / move / attack / die`
|
||||
- 技能风格直接使用 `steady / burst / mobility / finisher / projectile`
|
||||
- `src/components/StateFunctionEditor.tsx:85-98`
|
||||
- 行为编辑直接使用 `battle / idle`
|
||||
- 朝向直接使用 `left / right`
|
||||
- 怪物动画直接使用 `idle / move / attack`
|
||||
- 风格直接使用 `steady / burst / mobility / finisher / projectile`
|
||||
|
||||
### 当前未确认到的乱码
|
||||
|
||||
- 本轮没有在 `src/data/*.ts` 的预设正文里复核到新的、直接写死的中文乱码。
|
||||
- 当前预设内容层面的问题,主要是“英文字段值没有在显示层做 label 映射”,不是正文汉字被写坏。
|
||||
|
||||
## 建议处理顺序
|
||||
|
||||
1. 先修 `src/components/PresetEditor.tsx:72` 的 `鐗╁搧`。
|
||||
2. 再集中处理 `src/components/StateFunctionEditor.tsx` 的整组英文预览与提示文案。
|
||||
3. 然后处理 `src/components/ItemCatalogEditor.tsx` 的物品预览英文键名与英文值。
|
||||
4. 之后清理游戏运行时最明显的英文块:
|
||||
- `src/components/CharacterDetailModal.tsx`
|
||||
- `src/components/InventoryPanel.tsx`
|
||||
- `src/components/AdventureEntityModal.tsx`
|
||||
- `src/components/CompanionCampModal.tsx`
|
||||
- `src/components/CharacterChatModal.tsx`
|
||||
- `src/components/GameShell.tsx`
|
||||
5. 最后为预设源字段补统一显示映射,把 `common / rare / legendary / neutral / idle / left / right ...` 全部收口到统一词典。
|
||||
|
||||
278
docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md
Normal file
278
docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md
Normal file
@@ -0,0 +1,278 @@
|
||||
# 工程优化审查报告(2026-03-29)
|
||||
|
||||
## 说明
|
||||
|
||||
- 扫描范围:`src/`、`scripts/`、`docs/`、`package.json`、`vite.config.ts`、`tsconfig.json`
|
||||
- 已执行校验:`npm run lint`、`npm run build`、`npm run check:content`
|
||||
- 本报告只从工程角度讨论结构、边界、质量门禁、可维护性与可扩展性
|
||||
- 按仓库说明,暂不讨论中文乱码本身
|
||||
|
||||
## 当前结论
|
||||
|
||||
项目当前**可构建、可运行、内容校验可通过**,说明基础功能链路是通的;但从工程视角看,已经出现明显的“单点过重、边界混杂、质量门禁偏弱、编辑器与运行时耦合”问题。继续叠需求会越来越依赖人工记忆和局部经验,回归风险会持续上升。
|
||||
|
||||
当前最值得优先处理的不是单个 UI 细节,而是以下四个工程主题:
|
||||
|
||||
1. 运行时主链路的职责拆分还不够,核心 hook / 组件已经过载
|
||||
2. 缺少真正的工程质量门禁,`lint` 目前本质上只是 `tsc`
|
||||
3. 编辑器、运行时、类后端能力都混在同一个 Vite 配置里
|
||||
4. 持久化、AI 调用、编辑器保存等基础设施仍然是“分散手写”
|
||||
|
||||
## 运行状态快照
|
||||
|
||||
- `npm run lint` 通过
|
||||
- `npm run build` 通过
|
||||
- `npm run check:content` 通过
|
||||
- 应用代码下未发现测试文件:`src/`、`scripts/`、`docs/` 内没有 `*.test.*` / `*.spec.*`
|
||||
- 构建产物已出现较大 chunk
|
||||
- `dist/assets/App-*.js` 约 `407 KB`
|
||||
- `dist/assets/itemCatalog-*.js` 约 `414 KB`
|
||||
- `dist/assets/PresetEditor-*.js` 约 `109 KB`
|
||||
|
||||
## 代码体征
|
||||
|
||||
下列文件已经明显进入“超大模块”区间:
|
||||
|
||||
| 文件 | 行数 | 观察 |
|
||||
| --- | ---: | --- |
|
||||
| `src/hooks/useStoryGeneration.ts` | 3304 | 同时管理剧情、NPC 交互、交易、送礼、招募、任务、角色聊天、道具/锻造接入 |
|
||||
| `src/components/PresetEditor.tsx` | 2244 | 多编辑器入口聚合在一个巨型组件中 |
|
||||
| `src/hooks/useCombatFlow.ts` | 1791 | 同时承担战斗推演、动画时序、逃跑演出、状态落地 |
|
||||
| `src/components/GameShell.tsx` | 1592 | 入口 UI、选角、世界选择、自定义世界、场景切换、浮层控制全部集中 |
|
||||
| `src/types.ts` | 663 | 运行时、AI、编辑器、自定义世界、背包、任务类型集中在一个总文件 |
|
||||
|
||||
补充信号:
|
||||
|
||||
- `src/components/GameShell.tsx` 内有 16 个 `useState`、10 个 `useEffect`、13 个 `useMemo`
|
||||
- `src/hooks/useStoryGeneration.ts` 虽然只有少量 React state,但内部累计 40+ 个函数,已经是“巨型流程控制器”
|
||||
- `src/hooks/useCombatFlow.ts` 内有大量时间常量、动画常量、`sleep + setGameState` 过程式循环,测试成本很高
|
||||
|
||||
## 优先级问题
|
||||
|
||||
## P0:运行时主链路职责过度集中
|
||||
|
||||
证据:
|
||||
|
||||
- `src/hooks/useStoryGeneration.ts:868-930` 进入 hook 后立即开始定义交易、送礼、招募、角色聊天等子流程
|
||||
- `src/hooks/useStoryGeneration.ts:3191-3303` 返回对象同时暴露剧情、任务、NPC UI、角色聊天 UI、背包/锻造 UI
|
||||
- `src/components/GameShell.tsx:293-360` 组件 props 很多,内部 state 也很多,承担“壳层 + 流程 + 浮层 + 自定义世界生成 + 场景切换”
|
||||
- `src/hooks/useCombatFlow.ts:559-1787` 将战斗计算和战斗演出揉在同一层里
|
||||
|
||||
影响:
|
||||
|
||||
- 任何一个新需求都容易同时碰到剧情、UI、战斗、背包、NPC 关系四五条链路
|
||||
- 代码 review 很难聚焦,改动一处时往往需要脑内跟完整条大流程
|
||||
- 单元测试难写,因为逻辑不是纯函数,而是大量闭包 + 过程式状态推进
|
||||
- 长期会形成“只有熟悉历史上下文的人才能安全修改”的隐性门槛
|
||||
|
||||
建议:
|
||||
|
||||
- 将 `useStoryGeneration` 拆为“剧情推进”“NPC 交互”“角色聊天”“任务结算”“模态框控制”几个子域
|
||||
- 将 `useCombatFlow` 拆成“纯战斗结算引擎”和“战斗播放适配层”
|
||||
- 让 `GameShell` 回到壳层职责,只负责路由态、页面态、模态挂载与 props 编排
|
||||
- 以“领域职责”拆分,而不是按“文件太长了随便切一刀”拆分
|
||||
|
||||
## P0:缺少真正的工程质量门禁
|
||||
|
||||
证据:
|
||||
|
||||
- `package.json:11` 的 `lint` 实际只有 `tsc --noEmit`
|
||||
- `package.json` 中没有 `test`、`format`、`lint:fix` 等基础脚本
|
||||
- 根目录未发现 `.eslintrc*`、`.prettierrc*`、`.editorconfig`
|
||||
- 代码目录下没有测试文件
|
||||
|
||||
影响:
|
||||
|
||||
- 当前项目的“能过 lint”只代表类型没炸,不代表风格一致、依赖正确、Hooks 规则正确、死代码已清理
|
||||
- 大型 hook / 大型组件的重构几乎没有自动回归保护
|
||||
- 运行时行为、编辑器行为、AI fallback 行为主要依赖人工回归
|
||||
|
||||
建议:
|
||||
|
||||
- 补齐 ESLint、Prettier、EditorConfig,至少覆盖 React Hooks、import、unused code、复杂度基线
|
||||
- 引入 Vitest,先覆盖纯数据层与纯规则层
|
||||
- 为 `useCombatFlow`、`stateFunctions`、`npcInteractions`、`questFlow` 增加单元测试
|
||||
- 为“开局 -> 选世界 -> 选角色 -> 进入剧情 -> 战斗 -> 存档恢复”补最小 E2E smoke
|
||||
- CI 中至少串联:类型检查 + 单测 + build + 内容校验
|
||||
|
||||
## P1:编辑器、运行时、类后端能力全部耦合在 Vite 配置里
|
||||
|
||||
证据:
|
||||
|
||||
- `vite.config.ts:151-203` 在 Vite 插件里实现了 LLM 代理
|
||||
- `vite.config.ts:206-269` 在 Vite 插件里实现了通用 JSON 文件读写 API
|
||||
- `vite.config.ts:253` 直接写回 `src/data/*.json`
|
||||
- `vite.config.ts:265-266` 和 `vite.config.ts:400-401` 在 `preview` 阶段也挂了这些接口
|
||||
- `vite.config.ts:425-434` 启动时默认把这些“编辑器后端能力”全部注册进去
|
||||
|
||||
影响:
|
||||
|
||||
- 本地编辑器能力与运行时能力没有清晰边界
|
||||
- `preview` 环境仍可写源码文件,发布边界不清晰
|
||||
- 未来如果要做独立部署、多人协作、远程编辑、权限控制,会非常难迁移
|
||||
- Vite 配置同时扮演构建配置、代理层、文件服务层、编辑器后端,职责失衡
|
||||
|
||||
建议:
|
||||
|
||||
- 将编辑器读写 API 从 `vite.config.ts` 抽到独立的本地工具服务或独立脚本
|
||||
- 至少区分 `dev-only write api` 与 `preview/prod read-only api`
|
||||
- 对编辑器保存接口建立统一客户端 SDK,避免组件直接散落 `fetch('/api/...')`
|
||||
- LLM 代理也建议独立成 `server/` 或 `scripts/dev-server/`,不要继续长在构建配置里
|
||||
|
||||
## P1:持久化策略分散,且直接序列化大状态对象
|
||||
|
||||
证据:
|
||||
|
||||
- `src/hooks/useGamePersistence.ts:152-167` 会在状态变化时自动把完整快照写入 `localStorage`
|
||||
- `src/hooks/useGamePersistence.ts:157-163` 快照包含 `gameState + bottomTab + currentStory`
|
||||
- `src/hooks/useGamePersistence.ts:68-116` 恢复逻辑已经开始承担大量 schema 纠偏职责
|
||||
- `src/data/customWorldLibrary.ts:1-282` 自定义世界库单独维护一套 `localStorage` 读写与 normalize
|
||||
- `src/hooks/useGameSettings.ts` 也单独维护一套本地设置持久化
|
||||
|
||||
影响:
|
||||
|
||||
- 状态结构一旦继续膨胀,快照写入频率和反序列化成本都会增加
|
||||
- schema 迁移会越来越依赖手工 normalize 补丁
|
||||
- 不同持久化入口各写一套 parser / normalizer,风格和鲁棒性难统一
|
||||
- 当前保存的是“运行中大对象”,而不是“稳定领域快照”,长期会放大兼容成本
|
||||
|
||||
建议:
|
||||
|
||||
- 建立统一的 persistence 层,集中管理 key、version、migration、节流、序列化策略
|
||||
- 对 `GameState` 做“可持久化切片”和“运行时临时切片”分层
|
||||
- 自动保存增加节流/去抖,避免每次状态波动都全量落盘
|
||||
- 如果继续扩展角色聊天、自定义世界、编辑器草稿,建议评估 IndexedDB 替代 `localStorage`
|
||||
|
||||
## P1:运行时与编辑器仍在同一个前端入口体系中,包体继续膨胀
|
||||
|
||||
证据:
|
||||
|
||||
- `src/main.tsx:21-34` 通过 `window.location.pathname` 手写分发页面
|
||||
- `src/main.tsx:60` 只有“游戏”和“PresetEditor”两个大入口
|
||||
- `PresetEditor`、`ItemCatalogEditor`、`StateFunctionEditor` 都属于重型模块
|
||||
- 构建产物已经出现 `App` 约 `407 KB`、`itemCatalog` 约 `414 KB` 的 chunk
|
||||
|
||||
影响:
|
||||
|
||||
- 游戏端与编辑器端的演进节奏被绑定在一个 SPA 入口上
|
||||
- 编辑器相关数据和静态资源容易继续抬高构建体积
|
||||
- 未来增加更多编辑器页、更多世界模板、更多资源目录后,冷启动成本会更明显
|
||||
|
||||
建议:
|
||||
|
||||
- 将编辑器拆成独立入口,至少做成独立 route module,而不是单个 `PresetEditor`
|
||||
- 继续下钻按 tab 做懒加载,尤其是 `items/functions/npcs`
|
||||
- 将静态大数据、资源目录索引、编辑器专用预览逻辑做更细的 chunk 拆分
|
||||
- 如果项目后续会长期保留编辑器,建议直接分成 game app / editor app 两个 entry
|
||||
|
||||
## P2:编辑器基础设施重复实现较多
|
||||
|
||||
证据:
|
||||
|
||||
- `src/components/PresetEditor.tsx:111-181` 自己实现 `cloneValue`、`saveJsonObject`
|
||||
- `src/components/StateFunctionEditor.tsx:113-130` 再次实现 `cloneValue`、`SectionCard`
|
||||
- `src/components/ItemCatalogEditor.tsx:94` 再次实现保存请求
|
||||
- `src/hooks/useInventoryFlow.ts:8`、`src/hooks/useEquipmentFlow.ts:10`、`src/hooks/useForgeFlow.ts:12`、`src/hooks/useTreasureFlow.ts:10` 重复声明 `CommitGeneratedState`
|
||||
|
||||
影响:
|
||||
|
||||
- 修改保存行为、错误处理、深拷贝策略时需要多处同步
|
||||
- 编辑器 UI 风格与交互行为容易逐步漂移
|
||||
- 公共契约没有收拢到共享层,维护成本会逐步抬高
|
||||
|
||||
建议:
|
||||
|
||||
- 抽 `editor/shared/` 层,集中放保存 SDK、表单字段、卡片容器、克隆工具、错误处理
|
||||
- 抽通用的 `CommitGeneratedState` 类型定义
|
||||
- 将编辑器请求和覆盖保存逻辑统一走一个 client
|
||||
|
||||
## P2:类型系统已经出现“总文件过载”
|
||||
|
||||
证据:
|
||||
|
||||
- `src/types.ts` 共 663 行
|
||||
- `src/types.ts:1-260` 同时包含世界、动画、技能、对话、自定义世界、物品等类型
|
||||
- `src/types.ts:536-663` 又继续承接剧情、聊天、任务、`GameState`、AI 响应
|
||||
|
||||
影响:
|
||||
|
||||
- 任一领域类型变化都会增加总文件冲突概率
|
||||
- 新人理解类型边界成本高
|
||||
- 编辑器类型、运行时类型、AI 传输类型被放在一起,不利于演化
|
||||
|
||||
建议:
|
||||
|
||||
- 按领域拆分:`types/combat.ts`、`types/story.ts`、`types/item.ts`、`types/customWorld.ts`、`types/persistence.ts`
|
||||
- `GameState` 相关类型与 editor override 类型分开
|
||||
- AI request/response contract 单独收口,避免继续堆进总类型文件
|
||||
|
||||
## P2:AI 客户端层过厚,且重复了多套请求与解析逻辑
|
||||
|
||||
证据:
|
||||
|
||||
- `src/services/ai.ts` 共 1153 行
|
||||
- `src/services/ai.ts:540-605`、`608-678`、`745-790` 分别手写了 JSON completion、纯文本 completion、流式 completion
|
||||
- `src/services/ai.ts:680-697` 手写了多段 JSON 解析兜底
|
||||
- `src/services/ai.ts:76-78`、`591-594`、`662-666` 主要依赖 `console.*` 打日志
|
||||
|
||||
影响:
|
||||
|
||||
- LLM 行为扩展时容易继续复制请求模板、错误处理、超时逻辑
|
||||
- 错误分类不够稳定,观测主要停留在 console 层
|
||||
- prompt、transport、fallback、parse 被放在一起,后续测试和替换模型都不够轻
|
||||
|
||||
建议:
|
||||
|
||||
- 抽 `llmClient`,统一 transport、timeout、stream、error taxonomy
|
||||
- 抽 `llmParsers`,将 JSON parse / plain text parse / suggestion parse 独立
|
||||
- 为关键 prompt 输出建立 fixture 测试,至少覆盖 fallback 与异常响应
|
||||
- 如果后续要接多个模型,尽早把 provider 层和 prompt 层解耦
|
||||
|
||||
## P2:手写路由与死代码开始累积
|
||||
|
||||
证据:
|
||||
|
||||
- `src/main.tsx:21-34` 采用手写 `pathname.startsWith(...)`
|
||||
- `src/components/GameShell.tsx:1511` 存在 `false && showTeamModal`
|
||||
|
||||
影响:
|
||||
|
||||
- 路由能力不具备可扩展性,也不利于后续加 404、重定向、权限判断、嵌套路由
|
||||
- 死代码继续堆积后,会误导维护者对真实入口和真实 UI 状态的判断
|
||||
|
||||
建议:
|
||||
|
||||
- 引入正式路由层,哪怕只做轻量路由也比手写分发更清晰
|
||||
- 清理已经废弃的 UI 分支和不可达逻辑
|
||||
- 对“临时下线的功能”改为 feature flag 或明确注释,不要用 `false &&`
|
||||
|
||||
## 建议落地顺序
|
||||
|
||||
### 第一阶段:先补工程底座
|
||||
|
||||
- 增加 ESLint / Prettier / EditorConfig
|
||||
- 增加 `test` 脚本与 Vitest
|
||||
- 把 CI 最小闭环搭起来:类型检查、单测、build、内容校验
|
||||
|
||||
### 第二阶段:先拆边界,再拆大文件
|
||||
|
||||
- 先把 Vite 中的编辑器写文件接口、LLM 代理抽走
|
||||
- 再把 `GameShell`、`useStoryGeneration`、`useCombatFlow` 按职责拆域
|
||||
- 拆分时优先保持外部接口稳定,避免一次性全仓大改
|
||||
|
||||
### 第三阶段:收敛基础设施
|
||||
|
||||
- 统一 persistence 层
|
||||
- 统一 editor shared 层
|
||||
- 统一 AI client 层
|
||||
- 拆分 `types.ts`
|
||||
|
||||
### 第四阶段:降低发布成本
|
||||
|
||||
- 将 editor 与 game 做更明确的入口拆分
|
||||
- 优化 chunk 边界
|
||||
- 评估是否把编辑器做成独立 app
|
||||
|
||||
## 一句话结论
|
||||
|
||||
这个仓库当前最需要优化的不是“再补几个功能”,而是**把已经验证有效的玩法与工具链,从“靠大文件和经验串起来”升级为“靠清晰边界、统一基础设施和自动化门禁支撑起来”**。只要这一步不做,后续每次加内容、加编辑器能力、加 AI 流程,工程成本都会持续上升。
|
||||
290
docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md
Normal file
290
docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-30.md
Normal file
@@ -0,0 +1,290 @@
|
||||
# 工程优化审查报告(2026-03-30)
|
||||
|
||||
## 审查范围
|
||||
|
||||
- 扫描范围:`src/`、`scripts/`、`docs/`、`.github/`、`package.json`、`tsconfig.json`、`vite.config.ts`
|
||||
- 实际执行:`npm run lint`、`npm run test`、`npm run build`、`npm run check:content`
|
||||
- 说明:按仓库要求,本报告不讨论中文乱码问题,只讨论工程结构、边界、质量门禁、可维护性和后续扩展成本
|
||||
|
||||
## 先说结论
|
||||
|
||||
这轮代码库相较 `docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-03-29.md` 已经有明显进展,项目不再是“所有能力都糊在一个入口文件里”的状态了,但整体仍然处于“重构过渡期”。
|
||||
|
||||
已经落地的积极变化:
|
||||
|
||||
- 入口路由已经从手写 `pathname` 分发,收敛到 `src/main.tsx` + `src/routing/appRoutes.tsx`
|
||||
- 持久化能力已经抽到 `src/persistence/`
|
||||
- 编辑器公共能力已经出现 `src/editor/shared/`
|
||||
- `CI + ESLint + Prettier + Vitest` 已经接入
|
||||
- 本地 API 插件已经从 `vite.config.ts` 抽走,落到 `scripts/dev-server/localApiPlugins.ts`
|
||||
- `preview` 环境里的 JSON 写入接口已经改成只读,这一点比上轮更安全
|
||||
|
||||
但当前仍然存在 5 个值得优先处理的工程问题:
|
||||
|
||||
1. 运行时主链仍然过于集中,`story/combat` 的真实边界还没有彻底拆开
|
||||
2. `src/services/ai.ts` 仍处于迁移中间态,存在重复实现和旧逻辑残留
|
||||
3. 编辑器主入口仍是大型聚合组件,迁移残留没有清干净
|
||||
4. 质量门禁已经有框架,但还不够“硬”,warning 和测试覆盖缺口仍然明显
|
||||
5. 运行时渲染层和构建体积仍偏重,重 UI 模块还没拆到合适粒度
|
||||
|
||||
## 当前运行状态
|
||||
|
||||
- `npm run test` 通过,6 个测试文件共 18 个测试全部通过
|
||||
- `npm run build` 通过
|
||||
- `npm run check:content` 通过
|
||||
- `npm run lint` 通过,但仍有 76 条 warning
|
||||
|
||||
当前构建产物里仍然存在较重 chunk:
|
||||
|
||||
- `dist/assets/GameCanvas-*.js` 约 `346.58 kB`
|
||||
- `dist/assets/App-*.js` 约 `326.89 kB`
|
||||
- `dist/assets/index-*.js` 约 `197.80 kB`
|
||||
- `dist/assets/index-*.css` 约 `117.37 kB`
|
||||
|
||||
## P0:运行时主链仍然过于集中,Story/Combat 边界还没有拆透
|
||||
|
||||
### 现状
|
||||
|
||||
虽然 `App.tsx` 已经明显瘦身,`GameShell` 也比之前更像壳层,但真正决定游戏推进的主逻辑仍然高度集中在两个大 hook 里:
|
||||
|
||||
- `src/hooks/useStoryGeneration.ts:824`
|
||||
- `src/hooks/useCombatFlow.ts:382`
|
||||
|
||||
### 证据
|
||||
|
||||
`useStoryGeneration` 仍然同时编排了多个本应继续拆开的子领域:
|
||||
|
||||
- `src/hooks/useStoryGeneration.ts:852` 接入 `useCharacterChatFlow`
|
||||
- `src/hooks/useStoryGeneration.ts:1583` 接入 `useTreasureFlow`
|
||||
- `src/hooks/useStoryGeneration.ts:1588` 接入 `useInventoryFlow`
|
||||
- `src/hooks/useStoryGeneration.ts:1593` 接入 `useEquipmentFlow`
|
||||
- `src/hooks/useStoryGeneration.ts:1597` 接入 `useForgeFlow`
|
||||
- 文件总长仍有约 `3240` 行
|
||||
- 结尾返回对象同时暴露剧情推进、地图旅行、NPC 交易/送礼/招募、角色聊天、背包与锻造 UI 能力,典型位置在 `src/hooks/useStoryGeneration.ts:3171-3219`
|
||||
|
||||
`useCombatFlow` 也不是纯计算层,它仍然同时承担:
|
||||
|
||||
- 战斗前后状态推导
|
||||
- 动画播放与时间推进
|
||||
- `setGameState` 驱动的可视化编排
|
||||
- 逃跑流程与 story 响应同步
|
||||
|
||||
关键位置:
|
||||
|
||||
- `src/hooks/useCombatFlow.ts:382` `useCombatFlow`
|
||||
- `src/hooks/useCombatFlow.ts:1195` `playEscapeSequenceWithStorySync`
|
||||
|
||||
### 影响
|
||||
|
||||
- 任何一个“剧情选项新增”都很容易同时碰到 battle、npc、quest、inventory、chat 五条链路
|
||||
- review 成本高,回归范围判断依赖人脑上下文
|
||||
- 单测很难往 hook 级别补,因为副作用、异步节奏和 UI 状态混在一起
|
||||
- 后续想继续做 camp、custom world、更多 companion 玩法时,改动会继续集中到这两个入口
|
||||
|
||||
### 建议
|
||||
|
||||
- 把 `useStoryGeneration` 继续下钻成“剧情推进 orchestrator + 领域 action service”
|
||||
- `useStoryGeneration` 自己只保留编排,不再直接维护 trade/gift/recruit/chat/inventory/forge 的全部细节
|
||||
- `useCombatFlow` 继续向“纯战斗结算”和“播放适配层”分离
|
||||
- 先稳定公开接口,再做内部拆分,避免一次性大改
|
||||
|
||||
## P1:AI 服务迁移只完成了一半,`src/services/ai.ts` 仍然存在双轨实现
|
||||
|
||||
### 现状
|
||||
|
||||
仓库已经新增了:
|
||||
|
||||
- `src/services/llmClient.ts`
|
||||
- `src/services/llmParsers.ts`
|
||||
- `src/services/aiFallbacks.ts`
|
||||
- `src/services/aiTypes.ts`
|
||||
|
||||
这说明拆层方向是对的。但 `src/services/ai.ts` 还没有真正变成“纯 orchestration 层”,里面仍然保留着一整套旧 transport / parse / fallback 逻辑。
|
||||
|
||||
### 证据
|
||||
|
||||
- `src/services/ai.ts:64-66` 已经开始导入 `llmClient`
|
||||
- `src/services/ai.ts:89-95` 仍然保留本地 `resolveTimeoutMs` 和超时常量
|
||||
- `src/services/ai.ts:647` 仍然保留 `_requestPlainTextCompletion`
|
||||
- `src/services/ai.ts:719` 仍然保留 `_parseJsonResponseText`
|
||||
- `src/services/ai.ts:739` 仍然保留 `_parseLineListContent`
|
||||
- `src/services/ai.ts:784` 仍然保留 `_streamPlainTextCompletion`
|
||||
- `src/services/ai.ts:885-904` 仍然保留一批旧的 `_buildOffline...` helper
|
||||
|
||||
与之对应,新的实现已经在下面这些文件里存在:
|
||||
|
||||
- `src/services/llmClient.ts`
|
||||
- `src/services/llmParsers.ts`
|
||||
|
||||
### 影响
|
||||
|
||||
- 同一类能力现在有两套真相源,后续改错误分类、超时策略、SSE 行为时容易漏改
|
||||
- 新同学读代码时很难判断应该继续改 `ai.ts`,还是应该去改 `llmClient.ts`
|
||||
- 迁移残留会拉高维护成本,也会让测试边界变得模糊
|
||||
|
||||
### 建议
|
||||
|
||||
- 把 `src/services/ai.ts` 收敛成“业务 prompt 编排 + fallback 选择”层
|
||||
- 彻底删掉未再需要的 `_requestPlainTextCompletion`、`_streamPlainTextCompletion`、`_parse*` 等旧 helper
|
||||
- 所有 transport / timeout / connectivity error / SSE 解析都只保留在 `llmClient.ts` 和 `llmParsers.ts`
|
||||
- 迁移完成后,给 `ai.ts` 增加一组 orchestration 级测试,防止 fallback 分支回归
|
||||
|
||||
## P1:编辑器主入口仍然太重,而且过渡态残留还在
|
||||
|
||||
### 现状
|
||||
|
||||
编辑器公共能力已经开始沉淀到 `src/editor/shared/`,这是好事;但主编辑器入口仍然比较重,且部分文件还保留着迁移过程里的死代码和注释块。
|
||||
|
||||
### 证据
|
||||
|
||||
`PresetEditor` 仍然是一个大型聚合组件:
|
||||
|
||||
- `src/components/PresetEditor.tsx:402` `CharacterPresetPanel`
|
||||
- `src/components/PresetEditor.tsx:1174` `SceneNpcPresetPanel`
|
||||
- `src/components/PresetEditor.tsx:1547` `ScenePresetPanel`
|
||||
- `src/components/PresetEditor.tsx:1852` `MonsterPresetPanel`
|
||||
- `src/components/PresetEditor.tsx:2218` `PresetEditor`
|
||||
- 文件总长仍有约 `2279` 行
|
||||
|
||||
同时,文件里还留着明显的过渡态残留:
|
||||
|
||||
- `src/components/PresetEditor.tsx:227` 仍然保留未使用的 `_SectionCard`
|
||||
- `src/components/NpcVisualEditor.tsx:684` 保留 `if (false)` 的旧保存路径
|
||||
- `src/components/NpcVisualEditor.tsx:685` 明确写着 “Deprecated inline save path kept only until the shared client migration is cleaned up.”
|
||||
- `src/components/NpcVisualEditor.tsx:724` 还有第二处 `if (false)` 残留
|
||||
|
||||
### 影响
|
||||
|
||||
- 编辑器后续继续扩展时,容易重新长回“大一统文件”
|
||||
- 过渡代码会误导维护者,以为旧保存链路仍然有效
|
||||
- 公共层虽然建起来了,但如果不清理旧代码,长期会形成“共享层 + 本地特例”并存
|
||||
|
||||
### 建议
|
||||
|
||||
- 以“一个 tab 一个容器”的方式,把 `PresetEditor` 再拆一层
|
||||
- 清理 `NpcVisualEditor` 里的废弃代码块,不要再保留 `if (false)` 分支
|
||||
- 对编辑器共享层设定明确规则:保存请求、克隆、Section 容器、错误提示都必须走 shared
|
||||
- 对编辑器做一次“小型迁移完成清扫”,优先删掉已经废弃但还挂在文件里的旧路径
|
||||
|
||||
## P1:质量门禁已经接上,但还不够硬
|
||||
|
||||
### 现状
|
||||
|
||||
基础设施已经比上轮完整很多,但当前门禁仍然偏“有检查,不够严格”。
|
||||
|
||||
### 证据
|
||||
|
||||
当前 lint 结果:
|
||||
|
||||
- 本次 `npm run lint` 实际输出 `76` 条 warning,虽然命令返回成功
|
||||
|
||||
脚本和规则层面的原因也很明确:
|
||||
|
||||
- `package.json:12` 的 `lint` 仍然是 `eslint . ... && tsc --noEmit`,没有 `--max-warnings 0`
|
||||
- `package.json:11` 的 `lint:guardrails` 虽然加了 `--max-warnings 0`,但它只覆盖一组显式 allowlist 文件
|
||||
- `package.json:18` 的 `check` 会先跑 `lint:guardrails`,再跑宽松版 `lint`
|
||||
- `.eslintrc.cjs:45-61` 里大量规则仍然是 `warn`
|
||||
- `.github/workflows/ci.yml:28-40` 已经把 `lint / guardrails / test / build / check:content` 都接到 CI,但 warning 仍能稳定进主干
|
||||
|
||||
测试覆盖也还是偏薄:
|
||||
|
||||
- `src/` 当前共有 `126` 个文件
|
||||
- 其中测试文件只有 `6` 个
|
||||
- 现有测试主要覆盖 `routing`、`persistence`、`jsonClient`、`llmParsers`、`battlePlan`
|
||||
- 关键主链如 `useStoryGeneration`、`useCombatFlow` 播放层、`GameShell` 集成链路、编辑器保存流程仍然没有直接测试
|
||||
|
||||
### 影响
|
||||
|
||||
- 代码库会持续积累“已知 warning,但先不处理”的债务
|
||||
- 工程信号会逐渐失真,lint 通过不代表代码足够干净
|
||||
- 大 hook 和大组件的重构依然主要依赖人工回归
|
||||
|
||||
### 建议
|
||||
|
||||
- 先把 warning 收敛到一个可控范围,再把全仓 `lint` 切成 `--max-warnings 0`
|
||||
- `lint:guardrails` 不要长期靠 allowlist,应该逐步扩大到全仓
|
||||
- 优先补三类测试:
|
||||
- `useStoryGeneration` 的状态推进和 modal 决策
|
||||
- `useCombatFlow` 播放层的关键分支
|
||||
- 编辑器保存链路和覆盖数据回写
|
||||
|
||||
## P2:运行时渲染层仍然偏重,chunk 也还没有拆到理想粒度
|
||||
|
||||
### 现状
|
||||
|
||||
入口已经有了 route lazy load,模态框也做了一部分懒加载,但核心运行时渲染层仍然比较重。
|
||||
|
||||
### 证据
|
||||
|
||||
- `src/components/AdventurePanel.tsx:470` 导出主组件,文件总长约 `1538` 行
|
||||
- `src/components/GameCanvas.tsx:472` 导出主组件,文件总长约 `1131` 行
|
||||
- `src/components/GameCanvas.tsx:768` 仍然存在 `false && companions.map(...)` 的死分支
|
||||
- 本次构建里 `GameCanvas` 和 `App` 仍然是最大 chunk 之一
|
||||
|
||||
### 影响
|
||||
|
||||
- 运行时页面的首屏与热区模块仍然偏重
|
||||
- 渲染逻辑、场景动画逻辑、实体选中逻辑继续堆在同一层,review 和测试都偏吃力
|
||||
- 清理死分支前,维护者对“哪些渲染路径是真实生效的”判断成本更高
|
||||
|
||||
### 建议
|
||||
|
||||
- `GameCanvas` 继续拆成 scene layer、entity layer、effect layer、overlay layer
|
||||
- `AdventurePanel` 继续下沉 quest/stats/settings/reward 子面板
|
||||
- 清理 `false &&` 与未使用辅助组件,避免假分支继续留在主路径文件中
|
||||
- 结合真实 chunk 数据做一次 route 内部分包,而不是只靠入口级 lazy
|
||||
|
||||
## P2:TypeScript 安全基线仍然偏宽松
|
||||
|
||||
### 现状
|
||||
|
||||
当前类型拆分方向是好的,`src/types.ts` 已经退化成 barrel,真实类型开始向 `src/types/` 下沉。但 TypeScript 编译配置还比较保守,类型系统还没有真正变成强约束。
|
||||
|
||||
### 证据
|
||||
|
||||
- `tsconfig.json:12` `skipLibCheck: true`
|
||||
- `tsconfig.json:16` `allowJs: true`
|
||||
- 当前没有启用 `strict`
|
||||
- 当前没有启用 `noUncheckedIndexedAccess`
|
||||
|
||||
### 影响
|
||||
|
||||
- 对大对象和字典访问的保护仍然偏弱
|
||||
- 新模块迁移到更细类型后,收益会被宽松编译选项部分抵消
|
||||
- “代码能过类型检查”并不等于边界足够安全
|
||||
|
||||
### 建议
|
||||
|
||||
- 不建议一次性全仓开严格模式
|
||||
- 可以先从 `src/services/`、`src/persistence/`、`src/hooks/combat/` 这些相对纯的目录启更严格约束
|
||||
- 至少先评估开启 `noUncheckedIndexedAccess` 和减少 `allowJs` 的必要性
|
||||
|
||||
## 建议的落地顺序
|
||||
|
||||
### 第一阶段:先把过渡态清干净
|
||||
|
||||
- 清理 `ai.ts` 的旧 transport / parser / fallback 实现
|
||||
- 清理 `NpcVisualEditor`、`GameCanvas`、`PresetEditor` 等文件里的 `if (false)`、未使用 helper、废弃注释块
|
||||
- 把 lint warning 数量先打下来
|
||||
|
||||
### 第二阶段:拆主链,不再让大 hook 继续膨胀
|
||||
|
||||
- 继续拆 `useStoryGeneration`
|
||||
- 继续拆 `useCombatFlow`
|
||||
- 优先把“领域动作”和“播放/展示编排”分开
|
||||
|
||||
### 第三阶段:补门禁
|
||||
|
||||
- 给主链补单测和少量集成 smoke
|
||||
- 让全仓 lint 朝 `--max-warnings 0` 收敛
|
||||
- 把 warning 从“长期存在”变成“短周期清零”
|
||||
|
||||
### 第四阶段:优化运行时体积
|
||||
|
||||
- 细化 `GameCanvas` 和 `AdventurePanel` 的模块边界
|
||||
- 按实际交互热区做 chunk 继续拆分
|
||||
- 用真实构建产物持续追踪是否降重
|
||||
|
||||
## 一句话结论
|
||||
|
||||
这轮仓库已经从“完全依赖大文件硬扛”进步到“基础设施开始成形”,但当前最需要做的已经不是继续加功能,而是把这轮重构收尾做完整:继续拆主链、删掉迁移残留、把 lint/test 门禁变硬、再顺手压缩运行时大模块。只要这一步补上,后续加剧情、加编辑器能力、加自定义世界都会轻很多。
|
||||
200
docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md
Normal file
200
docs/ENGINEERING_OPTIMIZATION_REVIEW_2026-04-01.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 工程优化审查报告(2026-04-01)
|
||||
|
||||
## 审查范围
|
||||
|
||||
- 扫描范围:`src/`、`scripts/`、`docs/`、`.github/`、`package.json`、`tsconfig*.json`、`vite.config.ts`、`vitest.config.ts`
|
||||
- 审查方式:阅读当前工作区代码结构,抽查核心运行时、编辑器、服务层与开发脚本,并执行工程命令验证现状
|
||||
- 当前快照说明:仓库存在大量未提交改动,本报告基于当前工作区状态,不假定这些改动都已经合入主分支
|
||||
- 说明:按仓库要求,不把中文乱码本身当成本次审查重点;只讨论工程结构、门禁、可维护性、可测试性和扩展成本
|
||||
|
||||
## 已执行检查
|
||||
|
||||
- `npm run lint:eslint`
|
||||
结果:失败。`src/components/ItemCatalogEditor.tsx:167` 存在未使用的 `isSearchPending` 和 `startTransition`
|
||||
- `npm run typecheck`
|
||||
结果:通过
|
||||
- `npm run test`
|
||||
结果:通过,默认套件实际执行 10 个测试文件、28 个测试
|
||||
- `npm run build`
|
||||
结果:通过,但 `src/services/customWorldPresentation.ts:163-169` 出现 duplicate key 警告
|
||||
- `npm run check:content`
|
||||
结果:通过
|
||||
|
||||
## 当前结论
|
||||
|
||||
这轮代码库已经明显比前几版更有工程骨架了,至少有这些积极变化:
|
||||
|
||||
- `src/main.tsx` + `src/routing/appRoutes.tsx` 已经承担了入口路由分发
|
||||
- `src/App.tsx` 已经比过去瘦很多,主流程开始交给 hook 和壳组件
|
||||
- `src/components/PresetEditor.tsx` 已经成为较薄的 lazy shell,而不是继续堆成巨型入口
|
||||
- `src/editor/shared/jsonClient.ts`、`src/persistence/`、`src/hooks/combat/`、`src/hooks/story/` 这些目录说明仓库已经开始做分层
|
||||
- CI、Vitest、ESLint、内容校验脚本都已经接上,不再是完全裸奔状态
|
||||
|
||||
但从工程角度看,当前最值得优先优化的,不是继续加功能,而是把“半完成的工程化”补齐。核心问题集中在 6 个方面。
|
||||
|
||||
## P0:质量门禁和真实风险点仍然脱节
|
||||
|
||||
### 现状
|
||||
|
||||
仓库已经引入了 lint、typecheck、test、build 和 content checks,但关键热区并没有真正纳入统一门禁。
|
||||
|
||||
### 证据
|
||||
|
||||
- `.eslintrc.cjs:47-63` 的 `ignorePatterns` 直接跳过了多个高复杂度核心文件:
|
||||
`src/components/AdventurePanel.tsx`、`src/components/NpcVisualEditor.tsx`、`src/components/preset-editor/PresetEditorPanels.tsx`、`src/hooks/useStoryGeneration.ts`、`src/services/customWorldPresentation.ts`
|
||||
- `tsconfig.typecheck-guardrails.json:6-15` 只对非常有限的一小组文件开启严格类型检查,远没有覆盖主运行时链路
|
||||
- `vitest.config.ts:8-10` 把 `customWorldPresentation` 映射到 stub,`vitest.config.ts:20` 还排除了真实存在的 `src/services/ai.test.ts`
|
||||
- 当前 `src/` 下共有 161 个文件,测试文件共有 11 个,但默认套件只执行其中 10 个
|
||||
- `npm run build` 已经能暴露 `src/services/customWorldPresentation.ts:163-169` 的 duplicate key 警告,但这块文件同时被 ESLint ignore、被 Vitest stub 掉,说明真实风险没有被完整看见
|
||||
|
||||
### 影响
|
||||
|
||||
- 工程信号不一致:`test` 绿、`build` 过,不代表关键模块真的健康
|
||||
- 复杂模块越是难测,越容易被长期豁免,最后演变成“最关键的地方最不受控”
|
||||
- 后续重构会缺乏可靠的回归保护,review 只能更多依赖人工记忆
|
||||
|
||||
### 建议
|
||||
|
||||
- 先缩小 `.eslintrc.cjs` 的 ignore 范围,优先把 `useStoryGeneration.ts`、`customWorldPresentation.ts`、`PresetEditorPanels.tsx` 拉回 lint
|
||||
- 把 `src/services/ai.test.ts` 重新纳入默认测试套件,除非有明确且短期的阻塞原因
|
||||
- 不要长期依赖 `tsconfig.typecheck-guardrails.json` 的 allowlist,至少把 `src/hooks/`、`src/services/`、`src/components/game-shell/` 逐步纳入 strict 范围
|
||||
- 对 build warning 建立明确策略:要么在 CI 中失败,要么把 warning 收敛到零
|
||||
|
||||
## P0:当前工作区不在真正的绿色基线
|
||||
|
||||
### 现状
|
||||
|
||||
当前代码不是“纯优化空间”问题,而是已经存在直接可见的门禁破口。
|
||||
|
||||
### 证据
|
||||
|
||||
- `package.json:11-15` 把 `lint:eslint` 和 `typecheck` 定义成正式脚本,说明它们本来就属于项目基线
|
||||
- 实际执行 `npm run lint:eslint` 时,`src/components/ItemCatalogEditor.tsx:167` 报出未使用变量错误
|
||||
- `src/components/ItemCatalogEditor.tsx:167` 引入了 `useTransition()` 返回值,但当前组件没有消费它
|
||||
- `npm run build` 虽然成功,但 `src/services/customWorldPresentation.ts:163-169` 仍然有重复 object key 警告
|
||||
|
||||
### 影响
|
||||
|
||||
- 团队会越来越难区分“可接受的技术债”和“已经破坏基线的问题”
|
||||
- 继续叠加功能会把问题扩散到更多文件,后面补起来成本更高
|
||||
|
||||
### 建议
|
||||
|
||||
- 先恢复工作区绿色基线,再继续推进大功能
|
||||
- 把“lint 零错误、build 零 warning”作为下一轮工程整理的硬目标
|
||||
|
||||
## P1:运行时主链路仍然被少数超级模块吸住
|
||||
|
||||
### 现状
|
||||
|
||||
入口已经变薄,但主复杂度仍集中在少数大文件里,尤其是故事推进、战斗同步和界面编排三层。
|
||||
|
||||
### 证据
|
||||
|
||||
- `src/hooks/useStoryGeneration.ts` 当前约 2210 行
|
||||
- `src/hooks/useStoryGeneration.ts:694` 导出主 hook,`src/hooks/useStoryGeneration.ts:1416` 接入 `useTreasureFlow`,后面还继续承接 NPC 互动、库存、打字机、AI、历史推进和故事回写
|
||||
- `src/hooks/useCombatFlow.ts:134` 是主战斗 hook,`src/hooks/useCombatFlow.ts:796-832` 仍然负责逃跑流程与 story sync 的耦合
|
||||
- `src/components/GameShell.tsx` 当前约 791 行,`src/components/GameShell.tsx:260-269` 管理一组本地 UI 状态,`src/components/GameShell.tsx:482` 继续处理场景切换时的选择编排
|
||||
- 构建产物里 `dist/assets/App-*.js` 约 389 kB,`dist/assets/index-*.js` 约 198 kB,说明主运行时 chunk 仍然偏重
|
||||
|
||||
### 影响
|
||||
|
||||
- 任何一个功能变化都容易跨 story、combat、transition、panel 几条链一起改
|
||||
- hook 单测越来越难写,因为副作用、异步和 UI 编排仍然混在一起
|
||||
- App 主 chunk 偏重,会继续拖累首屏和回归速度
|
||||
|
||||
### 建议
|
||||
|
||||
- 继续把 `useStoryGeneration` 收敛成 orchestration 层,把 treasure、NPC、inventory、chat、typewriter、AI 回写拆成更纯的领域 action
|
||||
- 让 `useCombatFlow` 更明确地区分“战斗结算”和“播放同步”
|
||||
- 把 `GameShell` 进一步下沉为 scene transition、selection flow、overlay panel 三类 view-model
|
||||
|
||||
## P1:编辑器共享层只迁移了一半,重复基础设施还在
|
||||
|
||||
### 现状
|
||||
|
||||
编辑器入口已经做了 shell 化,但真正的复杂度仍然堆在大型面板组件里,而且共享层没有吃干净。
|
||||
|
||||
### 证据
|
||||
|
||||
- `src/components/PresetEditor.tsx:41` 的入口已经很薄,说明方向是对的
|
||||
- 但 `src/components/preset-editor/PresetEditorPanels.tsx` 仍然约 2163 行
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:55` 仍然自带 `cloneValue`
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:117` 仍然自带 `saveJsonObject`
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:189` 仍然自带 `SectionCard`
|
||||
- 与之对应,`src/editor/shared/jsonClient.ts:29-40` 已经提供了共享版 `fetchJson` / `saveJsonObject`
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:364`、`:1128`、`:1500`、`:1806` 仍然把四个大型 panel 放在同一个文件里
|
||||
|
||||
### 影响
|
||||
|
||||
- 编辑器的保存、错误处理、基础 UI 容器会继续多处复制,后续很难统一行为
|
||||
- 迁移看起来开始了,但没有真正收尾,维护者仍然需要在“大文件 + 共享层”之间来回切换
|
||||
|
||||
### 建议
|
||||
|
||||
- 继续把 `PresetEditorPanels.tsx` 拆成按 tab 或按领域分文件
|
||||
- 统一复用 `src/editor/shared/` 下的保存客户端、基础容器、表单片段
|
||||
- 对编辑器做一次“小型迁移收尾”,目标是消灭重复的基础 helper
|
||||
|
||||
## P1:本地开发 API 层与构建工具耦合过深
|
||||
|
||||
### 现状
|
||||
|
||||
本地 API 插件已经把很多临时逻辑吸收进项目内部,这是好事;但它现在承担的职责太多,且全部挂在 Vite 插件层。
|
||||
|
||||
### 证据
|
||||
|
||||
- `vite.config.ts:7-18` 直接把 `createLocalApiPlugins(__dirname, env)` 注入到 Vite config
|
||||
- `scripts/dev-server/localApiPlugins.ts` 当前约 394 行
|
||||
- `scripts/dev-server/localApiPlugins.ts:150` 定义 LLM proxy 插件
|
||||
- `scripts/dev-server/localApiPlugins.ts:216` 定义通用 JSON 文件编辑插件
|
||||
- `scripts/dev-server/localApiPlugins.ts:265` 直接把编辑器保存结果写回 `src/data/*.json`
|
||||
- `scripts/dev-server/localApiPlugins.ts:429` 再统一把所有插件拼到一起
|
||||
|
||||
### 影响
|
||||
|
||||
- dev server、preview server、编辑器持久化和 LLM 代理被绑在一个文件里,测试与替换成本都偏高
|
||||
- 随着编辑器继续扩张,这个文件会继续演化成“隐形后端”
|
||||
- 生产与开发环境的边界容易模糊,问题排查也更依赖熟悉 Vite 插件机制的人
|
||||
|
||||
### 建议
|
||||
|
||||
- 至少先按职责把 `localApiPlugins.ts` 拆成 `llm-proxy`、`json-editor-api`、`asset-catalog` 三块
|
||||
- 下一阶段可以考虑把编辑器 API 抽成独立本地服务层,而不是继续塞在 Vite 插件里
|
||||
- 给 JSON 写入接口补 schema 校验,而不只是“是 object 就写入”
|
||||
|
||||
## P2:构建体积仍有继续优化空间
|
||||
|
||||
### 现状
|
||||
|
||||
路由 lazy load 和部分 modal lazy load 已经做了,但主游戏运行时包仍然偏大。
|
||||
|
||||
### 证据
|
||||
|
||||
- `dist/assets/App-*.js` 约 389 kB
|
||||
- `dist/assets/index-*.js` 约 198 kB
|
||||
- `dist/assets/index-*.css` 约 117 kB
|
||||
- `src/components/GameShell.tsx`、`src/hooks/useStoryGeneration.ts`、`src/services/prompt.ts` 仍然是较大的主链路文件
|
||||
|
||||
### 影响
|
||||
|
||||
- 新人本地启动、构建和回归阅读成本仍然偏高
|
||||
- 主运行时模块越重,越不利于后续继续做场景扩展和编辑器共存
|
||||
|
||||
### 建议
|
||||
|
||||
- 优先沿着“运行时 orchestration 拆分”去减主 chunk,而不是单纯追求更多 lazy import
|
||||
- 对 `prompt`、自定义世界、编辑器预览等非首屏关键代码继续做边界拆分
|
||||
- 每轮重构后用真实构建产物复测,而不是只凭代码体感判断
|
||||
|
||||
## 建议的落地顺序
|
||||
|
||||
1. 先恢复绿色基线:修掉 `ItemCatalogEditor` lint 错误,处理 `customWorldPresentation` 的 duplicate key warning
|
||||
2. 再补齐门禁:缩小 ESLint ignore、把 `ai.test.ts` 拉回默认测试、扩大 strict typecheck 覆盖
|
||||
3. 然后拆主链:优先处理 `useStoryGeneration`、`useCombatFlow`、`GameShell`
|
||||
4. 再做编辑器迁移收尾:拆 `PresetEditorPanels.tsx`,统一共享层
|
||||
5. 最后处理 dev API 分层和 bundle 体积
|
||||
|
||||
## 一句话结论
|
||||
|
||||
这个仓库已经从“功能堆叠期”进入“工程收尾期”了。当前最值得做的不是再加一层玩法,而是把门禁补齐、把超级模块拆开、把半迁移状态收尾;只要这一步做稳,后续继续扩展剧情、编辑器和自定义世界的成本都会明显下降。
|
||||
882
docs/EQUIPMENT_BUILD_AND_FORGE_LOOP_SYSTEM_DESIGN.md
Normal file
882
docs/EQUIPMENT_BUILD_AND_FORGE_LOOP_SYSTEM_DESIGN.md
Normal file
@@ -0,0 +1,882 @@
|
||||
# 配装构筑 + 合成/锻造闭环详细设计
|
||||
|
||||
更新时间:`2026-03-25`
|
||||
|
||||
## 0. 设计前提
|
||||
|
||||
这份方案基于当前仓库已经存在的运行时结构来设计,不另起一套独立系统。
|
||||
|
||||
- 现有物品结构已经有 `InventoryItem.tags`、`statProfile`、`useProfile`、`buildProfile`。
|
||||
- 现有装备位只有 `weapon / armor / relic` 三槽,因此本期套装与 build 成型必须围绕 2 件和 3 件完成。
|
||||
- 现有战斗结算已经有 `functionEffect.damageMultiplier / incomingDamageMultiplier`,但 `equipmentEffects.ts` 中的装备数值聚合仍然基本为空壳。
|
||||
- 现有素材库中已经出现 `setId`、`setName`、`pieceName`、`synergy` 等 build 元数据,但尚未进入真实伤害结算。
|
||||
- 现有角色、怪物、消耗品、掉落、宝藏、NPC 交易都已经形成了资源入口,适合继续补成“获取 -> 拆解 -> 锻造 -> 配装 -> 战斗 -> 再获取”的闭环。
|
||||
|
||||
因此,本方案的目标不是“再做一层 UI”,而是补齐以下 4 层:
|
||||
|
||||
1. 标签规范化层:把当前中英混用、结构标签与语义标签混用的问题拆开。
|
||||
2. 语义相似度层:用 embedding 相似度把“相近标签”自动组织为 build 与套装簇。
|
||||
3. 伤害修正层:把标签结果稳定接入当前伤害公式。
|
||||
4. 合成/锻造闭环:让掉落、材料、装备、buff、交易真正循环起来。
|
||||
|
||||
## 1. 结合当前系统的落地判断
|
||||
|
||||
### 1.1 现有可复用基础
|
||||
|
||||
- `src/types.ts`
|
||||
- 已有 `ItemStatProfile`
|
||||
- 已有 `ItemUseProfile`
|
||||
- 已有 `ItemBuildProfile`
|
||||
- 已有 `EquipmentLoadout`
|
||||
- 已有 `GameState.playerEquipment`
|
||||
- `src/data/itemDesign.ts`
|
||||
- 已经能为装备自动生成 `buildProfile`
|
||||
- 已经有 `setId / setName / pieceName / synergy`
|
||||
- 已经有一批 role/tag 原型,例如 `assassin / caster / ward / fate`
|
||||
- `src/data/monsterPresets.ts`
|
||||
- 已经有 `habitatTags`
|
||||
- 已经有较完整的 `lootTable`
|
||||
- `src/data/treasureInteractions.ts`
|
||||
- 已经有材料、消耗品、稀有品产出入口
|
||||
- `src/hooks/useCombatFlow.ts`
|
||||
- 玩家、同伴、怪物三条伤害结算路径已经齐备
|
||||
- 只差把 build 结果统一注入 `damage`
|
||||
|
||||
### 1.2 当前缺口
|
||||
|
||||
- 当前 `InventoryItem.tags` 同时承担了“分类标签”和“战斗语义标签”,例如 `weapon / armor / relic / material / mana / healing` 混在一起。
|
||||
- 当前角色正式数据结构里没有稳定的 `combatTags`,角色标签主要还停留在 UI 展示常量。
|
||||
- 当前怪物只有 `habitatTags`,更适合掉落/生态,不适合直接进入伤害 build。
|
||||
- 当前技能/物品可以恢复数值,但还不能稳定施加“限回合 build buff 标签”。
|
||||
- 当前 `getEquipmentBonuses()` 没有真正读取 `statProfile + buildProfile`,导致 build 无法进入伤害。
|
||||
|
||||
结论:
|
||||
|
||||
- 现有系统已经具备 build 系统的数据骨架。
|
||||
- 真正需要补的是“标签语义层”和“统一伤害入口”。
|
||||
|
||||
## 2. Build 标签设计原则
|
||||
|
||||
### 2.1 标签必须贴近实体语义
|
||||
|
||||
参与 build 的标签应尽量是玩家能直觉理解的“风格词”,而不是纯系统词。
|
||||
|
||||
推荐使用:
|
||||
|
||||
- 行为风格:`快剑`、`突进`、`追击`、`反击`、`蓄力`、`控场`
|
||||
- 输出方式:`重击`、`连段`、`远射`、`雷法`、`符阵`、`毒雾`
|
||||
- 生存节奏:`护体`、`守御`、`回复`、`续战`、`压血`
|
||||
- 材料/流派气质:`寒铁`、`星砂`、`灵木`、`骨纹`、`镇邪`
|
||||
- 阵营/身份风格:`先锋`、`游击`、`法修`、`命纹`、`统御`
|
||||
|
||||
不推荐直接把以下内容当作伤害 build 标签:
|
||||
|
||||
- `weapon`
|
||||
- `armor`
|
||||
- `relic`
|
||||
- `material`
|
||||
- `piece:weapon`
|
||||
- `world:xianxia`
|
||||
|
||||
这些更适合保留为筛选、配方、掉落、编辑器过滤用的结构标签。
|
||||
|
||||
### 2.2 标签分层
|
||||
|
||||
建议把标签拆成三层:
|
||||
|
||||
| 层级 | 用途 | 示例 |
|
||||
| --- | --- | --- |
|
||||
| 结构标签 | 分类、筛选、配方、存档兼容 | `weapon`、`armor`、`material`、`set:steel` |
|
||||
| 战斗语义标签 | 进入 build 相似度和伤害修正 | `快剑`、`突进`、`护体`、`雷法` |
|
||||
| 生态/素材标签 | 掉落、锻造、配方倾向 | `矿道`、`雾林`、`寒铁`、`星砂` |
|
||||
|
||||
推荐约束:
|
||||
|
||||
- `InventoryItem.tags` 继续保留结构标签与通用筛选标签。
|
||||
- `ItemBuildProfile.tags` 只保留会进入 build 计算的语义标签。
|
||||
- `MonsterPreset.habitatTags` 保留生态用途,不直接参与伤害。
|
||||
- 新增 `combatTags` 给角色/怪物,用于战斗 build。
|
||||
|
||||
### 2.3 中英混用兼容
|
||||
|
||||
当前素材生成中已有较多英文 role/tag,角色 UI 和自定义世界中又偏中文标签,因此需要加一层标签规范化。
|
||||
|
||||
建议建立 `buildTagRegistry`:
|
||||
|
||||
```ts
|
||||
type BuildTagDefinition = {
|
||||
id: string; // ASCII 稳定 id,例如 "quickblade"
|
||||
label: string; // 展示名,例如 "快剑"
|
||||
category: "role" | "style" | "resource" | "element" | "defense" | "craft";
|
||||
aliases: string[]; // 兼容 assassin / duelist / 快剑流 等历史写法
|
||||
description: string; // 供 embedding 使用
|
||||
};
|
||||
```
|
||||
|
||||
示例映射:
|
||||
|
||||
| 现有值 | 规范标签 |
|
||||
| --- | --- |
|
||||
| `assassin` | `快袭` / `切后` / `突进` |
|
||||
| `duelist` | `快剑` / `连段` / `对拼` |
|
||||
| `vanguard` | `先锋` / `稳压` / `护体` |
|
||||
| `ward` | `守御` / `镇邪` / `护体` |
|
||||
| `berserker` | `压血` / `重击` / `爆发` |
|
||||
| `caster` | `法修` / `法力` / `远程` |
|
||||
| `support` | `护持` / `回复` / `增益` |
|
||||
| `fortress` | `重甲` / `格挡` / `反击` |
|
||||
| `fate` | `命纹` / `机缘` / `冷却` |
|
||||
| `commander` | `统御` / `均衡` / `队伍增益` |
|
||||
|
||||
## 3. 标签来源设计
|
||||
|
||||
### 3.1 装备标签
|
||||
|
||||
装备是 build 的主来源,但要区分“结构标签”和“build 标签”。
|
||||
|
||||
建议:
|
||||
|
||||
- 武器、护甲、饰品各自最多提供 `2` 个核心 build 标签。
|
||||
- 若装备存在 `setId`,运行时根据套装件数额外生成“合成标签”。
|
||||
- 同一标签在多个装备上重复出现时,只记一次,不按件数无限叠加。
|
||||
|
||||
装备运行时推荐结构:
|
||||
|
||||
```ts
|
||||
type RuntimeEquipmentBuildSource = {
|
||||
itemId: string;
|
||||
slot: "weapon" | "armor" | "relic";
|
||||
coreTags: string[];
|
||||
setId?: string;
|
||||
setName?: string;
|
||||
forgeRank?: number;
|
||||
};
|
||||
```
|
||||
|
||||
### 3.2 角色/怪物标签
|
||||
|
||||
角色和怪物都应该有自己的 `combatTags`。
|
||||
|
||||
建议:
|
||||
|
||||
- 角色固定提供 `2~3` 个核心战斗语义标签。
|
||||
- 怪物固定提供 `2~3` 个核心战斗语义标签。
|
||||
- `habitatTags` 不直接参与 build 伤害,而是决定掉落材料簇、锻造路线和部分弱点设计。
|
||||
|
||||
例子:
|
||||
|
||||
- 剑系角色:`快剑`、`突进`、`压制`
|
||||
- 重甲怪物:`重甲`、`震击`、`守势`
|
||||
- 雾林怪物生态标签:`雾林`、`湿毒`、`潜伏`
|
||||
- 其中 `湿毒`、`潜伏` 可提升为 `combatTags`
|
||||
- `雾林` 保留为生态/掉落标签
|
||||
|
||||
### 3.3 Buff 标签
|
||||
|
||||
buff 标签是 build 的短时放大器,也是技能与物品进入构筑闭环的关键。
|
||||
|
||||
buff 标签来源:
|
||||
|
||||
- 技能施加的限回合标签
|
||||
- 消耗品施加的限回合标签
|
||||
- 锻造出的铭刻/附魔效果施加的限回合标签
|
||||
|
||||
建议新增:
|
||||
|
||||
```ts
|
||||
type TimedBuildBuff = {
|
||||
id: string;
|
||||
sourceType: "skill" | "item" | "forge";
|
||||
sourceId: string;
|
||||
name: string;
|
||||
tags: string[];
|
||||
durationTurns: number;
|
||||
maxStacks?: number;
|
||||
};
|
||||
```
|
||||
|
||||
推荐时长:
|
||||
|
||||
- 强爆发 buff:`1~2` 回合
|
||||
- 节奏/机动 buff:`2~3` 回合
|
||||
- 防御/续航 buff:`2~4` 回合
|
||||
|
||||
重复规则:
|
||||
|
||||
- 同名 buff 默认刷新持续时间,不新增一套完全重复标签。
|
||||
- 不同来源但规范化后相同的标签,只保留一个 build 标签实例。
|
||||
- 重复来源只提高优先级或刷新,不增加额外 `+1` 基础值。
|
||||
|
||||
## 4. 语义 embedding 与套装 build 形成方式
|
||||
|
||||
### 4.1 为什么要引入 embedding
|
||||
|
||||
如果只靠显式 `setId`,build 会很死板。
|
||||
|
||||
引入 embedding 后,可以让这些组合自然成型:
|
||||
|
||||
- `快剑` + `突进` + `追击` + `风行`
|
||||
- `重甲` + `护体` + `守御` + `反击`
|
||||
- `雷法` + `法器` + `过载` + `法力`
|
||||
|
||||
也就是说:
|
||||
|
||||
- 显式套装仍然存在
|
||||
- 隐式语义套装也能成立
|
||||
- 两者都走同一套 build 标签计算,不必再写第二套特殊规则
|
||||
|
||||
### 4.2 embedding 计算对象
|
||||
|
||||
不要对每个物品实例现算 embedding,而是只对“规范标签定义”计算。
|
||||
|
||||
推荐流程:
|
||||
|
||||
1. 为 `buildTagRegistry` 中每个规范标签生成一条 embedding 文本。
|
||||
2. 文本内容由 `label + aliases + description` 组成。
|
||||
3. 预计算标签相似度矩阵并缓存到本地数据文件。
|
||||
4. 运行时只读相似度,不现算模型。
|
||||
|
||||
示例 embedding 文本:
|
||||
|
||||
```text
|
||||
快剑:以高速轻兵器、连续出手、贴身追击为核心的近战风格;别名 duelist, swift blade, 快袭。
|
||||
```
|
||||
|
||||
### 4.3 相似度函数
|
||||
|
||||
建议使用余弦相似度,并且只保留正向相似。
|
||||
|
||||
```ts
|
||||
similarity(a, b) = max(0, cosine(embedding(a), embedding(b)))
|
||||
```
|
||||
|
||||
建议阈值:
|
||||
|
||||
- `< 0.35`:视为无关,按 `0`
|
||||
- `0.35 ~ 0.65`:弱协同
|
||||
- `0.65 ~ 0.82`:强协同
|
||||
- `> 0.82`:视为同 build 簇强关联
|
||||
|
||||
### 4.4 语义套装簇形成
|
||||
|
||||
推荐同时保留两种 build 成型路径:
|
||||
|
||||
#### A. 显式套装
|
||||
|
||||
- 依据 `setId`
|
||||
- 2 件时生成一个合成标签:`套装:<setName>`
|
||||
- 3 件时再生成一个进阶标签:`宗匠:<setName>`
|
||||
|
||||
由于当前 runtime 只有三槽,所以 2 件和 3 件阈值最合适。
|
||||
|
||||
#### B. 隐式语义套装
|
||||
|
||||
当激活标签里存在 `3` 个及以上标签两两相似度超过阈值时,形成“语义 build 簇”。
|
||||
|
||||
例如:
|
||||
|
||||
- `快剑`
|
||||
- `突进`
|
||||
- `追击`
|
||||
- `风行`
|
||||
|
||||
它们无需拥有相同 `setId`,也能形成一组高相似 build。
|
||||
|
||||
## 5. 标签修正值与伤害公式
|
||||
|
||||
### 5.1 核心规则
|
||||
|
||||
用户给出的核心要求是:
|
||||
|
||||
- 每个标签修正值都是 `1`
|
||||
- 每多一个标签修正值加一
|
||||
- 同时所有标签的 `1` 需要额外乘上与新标签的相似度
|
||||
- 修正结果作用于输出伤害
|
||||
|
||||
将这条规则做成稳定、顺序无关的公式,可以写成:
|
||||
|
||||
```text
|
||||
rawBuildScore(T) = |T| + Σ similarity(t_i, t_j)
|
||||
(i < j)
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
- `|T|` 是激活标签数量,每个标签天然贡献 `1`
|
||||
- 每对标签之间再贡献一段相似度分
|
||||
|
||||
这个公式与“新增一个标签时,额外 +1,并让旧标签的 1 再乘上与新标签的相似度”完全等价。
|
||||
|
||||
对应的增量写法:
|
||||
|
||||
```text
|
||||
score_1 = 1
|
||||
score_k = score_(k-1) + 1 + Σ similarity(t_i, t_k)
|
||||
(1 <= i < k)
|
||||
```
|
||||
|
||||
### 5.2 激活标签集的选取
|
||||
|
||||
为了防止标签爆炸,建议运行时只取 `MAX_ACTIVE_BUILD_TAGS = 8`。
|
||||
|
||||
推荐优先级:
|
||||
|
||||
1. 限回合 buff 标签
|
||||
2. 角色/怪物核心标签
|
||||
3. 武器 build 标签
|
||||
4. 饰品 build 标签
|
||||
5. 护甲 build 标签
|
||||
6. 2 件/3 件套装合成标签
|
||||
|
||||
重复标签去重后再参与计算。
|
||||
|
||||
### 5.3 从 raw score 到伤害倍率
|
||||
|
||||
`rawBuildScore` 不直接等于伤害倍率,否则会过大。建议再做一层线性缩放与封顶。
|
||||
|
||||
```text
|
||||
buildDamageBonus = clamp((rawBuildScore - 1) * 0.03, 0, 0.45)
|
||||
buildDamageMultiplier = 1 + buildDamageBonus
|
||||
```
|
||||
|
||||
推荐解释:
|
||||
|
||||
- 单标签时没有 build 成型,不给明显收益
|
||||
- 2~4 标签形成初步风格,获得 5%~18% 左右伤害增益
|
||||
- 5~8 标签形成成熟 build,伤害增益逐步逼近 45% 上限
|
||||
|
||||
### 5.4 与当前战斗公式的接法
|
||||
|
||||
当前战斗中,伤害基本来自:
|
||||
|
||||
- 技能基础伤害
|
||||
- `functionEffect.damageMultiplier`
|
||||
- 装备 stat 值
|
||||
|
||||
建议统一改成:
|
||||
|
||||
```text
|
||||
finalOutgoingDamage =
|
||||
round(
|
||||
baseSkillDamage
|
||||
* functionDamageMultiplier
|
||||
* equipmentStatMultiplier
|
||||
* buildDamageMultiplier
|
||||
)
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- `equipmentStatMultiplier` 来自武器/护甲/饰品的 `statProfile`
|
||||
- `buildDamageMultiplier` 来自标签相似度 build
|
||||
- 先乘完再 `round`
|
||||
|
||||
本期先只影响“输出伤害”。
|
||||
|
||||
也就是:
|
||||
|
||||
- 玩家攻击怪物时用玩家侧标签
|
||||
- 同伴攻击怪物时用同伴侧标签
|
||||
- 怪物攻击玩家/同伴时用怪物侧标签
|
||||
|
||||
防御端 build 抵抗可以放到下一期,不必一开始就加复杂。
|
||||
|
||||
### 5.5 示例
|
||||
|
||||
某角色当前激活标签为:
|
||||
|
||||
- `快剑`
|
||||
- `突进`
|
||||
- `追击`
|
||||
- `风行`
|
||||
- `套装:百炼争锋`
|
||||
|
||||
假设相似度如下:
|
||||
|
||||
- `快剑-突进 = 0.82`
|
||||
- `快剑-追击 = 0.78`
|
||||
- `快剑-风行 = 0.65`
|
||||
- `突进-追击 = 0.80`
|
||||
- `突进-风行 = 0.72`
|
||||
- `追击-风行 = 0.70`
|
||||
- 套装标签与前四者平均相似度 `0.76`
|
||||
|
||||
则:
|
||||
|
||||
```text
|
||||
rawBuildScore
|
||||
= 5
|
||||
+ (0.82 + 0.78 + 0.65 + 0.80 + 0.72 + 0.70)
|
||||
+ (0.76 * 4)
|
||||
= 11.51
|
||||
|
||||
buildDamageBonus
|
||||
= clamp((11.51 - 1) * 0.03, 0, 0.45)
|
||||
= 0.3153
|
||||
|
||||
buildDamageMultiplier = 1.3153
|
||||
```
|
||||
|
||||
若本次技能基础伤害为 `28`,动作倍率为 `1.2`,装备数值倍率为 `1.14`,则:
|
||||
|
||||
```text
|
||||
finalDamage = round(28 * 1.2 * 1.14 * 1.3153) = 50
|
||||
```
|
||||
|
||||
## 6. 合成 / 锻造 / 回收闭环设计
|
||||
|
||||
### 6.1 闭环目标
|
||||
|
||||
让当前已有入口真正连起来:
|
||||
|
||||
- 怪物掉落
|
||||
- 宝藏奖励
|
||||
- NPC 交易
|
||||
- 背包积累
|
||||
- 装备更替
|
||||
- 消耗品 buff
|
||||
- 锻造与回收
|
||||
|
||||
形成闭环后,物品系统就不再只是“剧情资源池”,而是成长系统。
|
||||
|
||||
### 6.2 资源分层
|
||||
|
||||
建议把资源拆成 4 类:
|
||||
|
||||
| 类型 | 作用 | 当前可复用入口 |
|
||||
| --- | --- | --- |
|
||||
| 基础材料 | 进入配方、升级、重铸 | 怪物掉落、宝藏、NPC 交易 |
|
||||
| 标签精粹 | 决定 build 方向 | 拆解装备、精炼材料 |
|
||||
| 套装蓝图 | 决定显式 setId 结果 | 宝藏、精英怪、任务 |
|
||||
| 催化剂 | 提升稀有度、锁词条、转流派 | 稀有品、商店、Boss 掉落 |
|
||||
|
||||
### 6.3 推荐闭环
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["遭遇 / 战斗 / 宝藏 / 交易"] --> B["获得装备 / 材料 / 消耗品 / 蓝图"]
|
||||
B --> C["直接装备"]
|
||||
B --> D["拆解回收"]
|
||||
D --> E["基础材料"]
|
||||
D --> F["标签精粹"]
|
||||
D --> G["蓝图碎片"]
|
||||
E --> H["合成精炼材料"]
|
||||
F --> H
|
||||
G --> I["完整蓝图"]
|
||||
H --> J["锻造新装备"]
|
||||
I --> J
|
||||
J --> K["重铸 / 淬炼 / 铭刻"]
|
||||
K --> C
|
||||
C --> L["形成 build 与伤害提升"]
|
||||
L --> A
|
||||
```
|
||||
|
||||
### 6.4 关键环节
|
||||
|
||||
#### A. 拆解
|
||||
|
||||
目标:
|
||||
|
||||
- 回收无用装备
|
||||
- 提取 build 倾向
|
||||
- 让玩家可以主动转流派
|
||||
|
||||
建议产出:
|
||||
|
||||
- 基础材料:按装备部位与稀有度产出
|
||||
- 标签精粹:按 `buildProfile.tags` 产出对应流派精粹
|
||||
- 套装碎片:带 `setId` 的装备有概率掉落
|
||||
|
||||
推荐规则:
|
||||
|
||||
- 普通/优秀:返还 `1~2` 基础材料 + `1` 标签精粹
|
||||
- 稀有/史诗:返还 `2~4` 基础材料 + `1~2` 标签精粹
|
||||
- 传说:返还 `4+` 基础材料 + `2~3` 标签精粹 + 套装碎片
|
||||
|
||||
#### B. 合成
|
||||
|
||||
目标:
|
||||
|
||||
- 把零散材料往更高层资源转化
|
||||
|
||||
推荐配方:
|
||||
|
||||
- `3` 个同系基础材料 -> `1` 个精炼材料
|
||||
- `2` 个相近标签精粹 -> `1` 个簇催化剂
|
||||
- `3` 个蓝图碎片 -> `1` 个完整蓝图
|
||||
|
||||
#### C. 锻造
|
||||
|
||||
目标:
|
||||
|
||||
- 制造三槽位装备
|
||||
- 明确把材料生态和 build 流派联系起来
|
||||
|
||||
建议锻造公式:
|
||||
|
||||
```text
|
||||
成品 = 槽位模板 + 基础材料 + 标签精粹 + 蓝图/催化剂
|
||||
```
|
||||
|
||||
推荐规则:
|
||||
|
||||
- 武器:决定主要输出 build 标签
|
||||
- 护甲:决定生存/反击/续战方向
|
||||
- 饰品:决定资源、冷却、机动、控场补短板
|
||||
|
||||
#### D. 重铸
|
||||
|
||||
目标:
|
||||
|
||||
- 保留 build 主方向
|
||||
- 调整副标签
|
||||
|
||||
推荐规则:
|
||||
|
||||
- 保留主标签与 `setId`
|
||||
- 只在同一语义簇内重掷副标签
|
||||
- 花费标签精粹 + 货币
|
||||
|
||||
这样玩家不会因为一次重铸直接从 `快剑` 跳成 `重甲`。
|
||||
|
||||
#### E. 淬炼
|
||||
|
||||
目标:
|
||||
|
||||
- 提升已有装备而不是频繁换装
|
||||
|
||||
推荐效果:
|
||||
|
||||
- 提升 `forgeRank`
|
||||
- 增加 `statProfile`
|
||||
- 提高套装合成标签的优先级
|
||||
|
||||
#### F. 铭刻 / 附魔
|
||||
|
||||
目标:
|
||||
|
||||
- 让消耗品和锻造形成直接关系
|
||||
|
||||
推荐效果:
|
||||
|
||||
- 给装备附加可触发 buff 标签
|
||||
- 或直接生产“一次性战斗铭符”
|
||||
|
||||
例子:
|
||||
|
||||
- `疾风符`:`2` 回合获得 `风行`、`突进`
|
||||
- `镇岳印`:`2` 回合获得 `护体`、`守御`
|
||||
- `雷纹油`:`1` 回合获得 `雷法`、`过载`
|
||||
|
||||
## 7. 当前三槽系统下的 build 形态
|
||||
|
||||
由于当前只有 `weapon / armor / relic` 三槽,建议本期 build 以“2 件成型、3 件毕业”为主。
|
||||
|
||||
### 7.1 套装成型方式
|
||||
|
||||
- 1 件:只提供本件核心标签
|
||||
- 2 件:生成 `套装:<setName>` 合成标签
|
||||
- 3 件:再生成 `宗匠:<setName>` 进阶标签
|
||||
|
||||
这两个“合成标签”本质上也是普通 build 标签,因此会自动进入 embedding 相似度和伤害公式。
|
||||
|
||||
### 7.2 推荐 build archetype
|
||||
|
||||
| build | 角色/怪物核心标签 | 装备标签方向 | buff 标签方向 | 锻造材料倾向 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 快剑追袭 | `快剑`、`突进`、`追击` | 武器补 `连段`,饰品补 `风行` | `疾风`、`破绽` | `百炼钢`、`风羽`、`轻皮` |
|
||||
| 重甲反击 | `守御`、`重甲`、`反击` | 护甲补 `护体`,饰品补 `镇势` | `立壁`、`嘲压` | `寒铁`、`壳片`、`岩核` |
|
||||
| 雷法过载 | `法修`、`雷法`、`法力` | 武器补 `过载`,饰品补 `聚灵` | `引雷`、`蓄放` | `星砂`、`雷纹石`、`灵晶` |
|
||||
| 命纹机缘 | `命纹`、`冷却`、`机缘` | 饰品补 `连锁`,护甲补 `续战` | `转运`、`回转` | `命纹骨片`、`旧卷`、`秘印` |
|
||||
| 医理续战 | `回复`、`护持`、`续战` | 护甲补 `守御`,饰品补 `凝神` | `回气`、`清心` | `灵木`、`药囊`、`泉露` |
|
||||
|
||||
## 8. 与当前掉落和地图生态的关系
|
||||
|
||||
### 8.1 怪物生态标签不直接进伤害
|
||||
|
||||
当前怪物已有 `habitatTags`,更适合驱动:
|
||||
|
||||
- 掉落材料倾向
|
||||
- 可锻造流派倾向
|
||||
- 宝藏/地图资源分布
|
||||
|
||||
例如:
|
||||
|
||||
- `矿道 / 铸坊 / 废城` -> `寒铁`、`锻火`、`重甲`
|
||||
- `雾林 / 沼泽` -> `毒囊`、`湿毒`、`潜伏`
|
||||
- `祭坛 / 遗迹 / 古迹` -> `残卷`、`纹石`、`镇邪`
|
||||
- `月湖 / 灵泉 / 天河` -> `水灵`、`回气`、`法力`
|
||||
|
||||
### 8.2 当前掉落表可直接扩展
|
||||
|
||||
当前怪物掉落里已经有很多适合闭环的原型:
|
||||
|
||||
- `armor + material`
|
||||
- `weapon + material`
|
||||
- `relic + mana`
|
||||
- `consumable + material`
|
||||
|
||||
下一步不必推翻,只要再补:
|
||||
|
||||
1. 掉落物的 `craftTags`
|
||||
2. 掉落物的 `buildProfile`
|
||||
3. 拆解产物表
|
||||
|
||||
就能把这些现有掉落自然接进锻造循环。
|
||||
|
||||
## 9. 数据结构建议
|
||||
|
||||
### 9.1 类型扩展
|
||||
|
||||
建议在现有结构上最小扩展:
|
||||
|
||||
```ts
|
||||
type BuildTagCategory =
|
||||
| "role"
|
||||
| "style"
|
||||
| "resource"
|
||||
| "defense"
|
||||
| "element"
|
||||
| "craft";
|
||||
|
||||
interface BuildTagDefinition {
|
||||
id: string;
|
||||
label: string;
|
||||
category: BuildTagCategory;
|
||||
aliases: string[];
|
||||
description: string;
|
||||
}
|
||||
|
||||
interface ItemBuildProfile {
|
||||
role: string;
|
||||
tags: string[]; // 只放战斗语义标签
|
||||
setId?: string;
|
||||
setName?: string;
|
||||
pieceName?: string;
|
||||
synergy?: string[];
|
||||
craftTags?: string[]; // 新增:配方/材料倾向
|
||||
forgeRank?: number; // 新增:淬炼等级
|
||||
}
|
||||
|
||||
interface CombatTaggedActor {
|
||||
combatTags: string[];
|
||||
}
|
||||
|
||||
interface TimedBuildBuff {
|
||||
id: string;
|
||||
sourceType: "skill" | "item" | "forge";
|
||||
sourceId: string;
|
||||
name: string;
|
||||
tags: string[];
|
||||
durationTurns: number;
|
||||
}
|
||||
```
|
||||
|
||||
### 9.2 GameState 扩展
|
||||
|
||||
```ts
|
||||
interface GameState {
|
||||
activeBuildBuffs: TimedBuildBuff[];
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3 技能与物品扩展
|
||||
|
||||
```ts
|
||||
interface ItemUseProfile {
|
||||
hpRestore?: number;
|
||||
manaRestore?: number;
|
||||
cooldownReduction?: number;
|
||||
buildBuffs?: TimedBuildBuff[];
|
||||
}
|
||||
|
||||
interface FunctionEffectConfig {
|
||||
damageMultiplier?: number;
|
||||
incomingDamageMultiplier?: number;
|
||||
healAmount?: number;
|
||||
manaRestore?: number;
|
||||
cooldownTickBonus?: number;
|
||||
grantedBuildBuffs?: TimedBuildBuff[];
|
||||
}
|
||||
```
|
||||
|
||||
## 10. 运行时接入点
|
||||
|
||||
### 10.1 必须新增的规则层
|
||||
|
||||
建议新增 3 个数据/规则文件:
|
||||
|
||||
- `src/data/buildTags.ts`
|
||||
- 标签规范化
|
||||
- alias 映射
|
||||
- 相似度矩阵
|
||||
- `src/data/buildDamage.ts`
|
||||
- 聚合激活标签
|
||||
- 计算 `rawBuildScore`
|
||||
- 计算 `buildDamageMultiplier`
|
||||
- `src/data/forgeRecipes.ts`
|
||||
- 合成、锻造、拆解、重铸配方
|
||||
|
||||
### 10.2 当前代码里的关键接点
|
||||
|
||||
#### A. `src/data/equipmentEffects.ts`
|
||||
|
||||
需要从“空壳”升级为真正读取:
|
||||
|
||||
- `statProfile`
|
||||
- `buildProfile`
|
||||
- 套装件数
|
||||
|
||||
建议这里做:
|
||||
|
||||
- 读取三槽装备 stat 汇总
|
||||
- 计算显式套装件数
|
||||
- 产出装备侧 build 标签源
|
||||
|
||||
#### B. `src/hooks/useCombatFlow.ts`
|
||||
|
||||
这里是 build 进伤害的核心接点。
|
||||
|
||||
当前玩家、同伴、怪物都有单独 `damage = ...` 的结算片段,建议统一收敛成:
|
||||
|
||||
```ts
|
||||
resolveOutgoingDamage({
|
||||
actor,
|
||||
target,
|
||||
skill,
|
||||
functionEffect,
|
||||
gameState,
|
||||
})
|
||||
```
|
||||
|
||||
统一处理:
|
||||
|
||||
- actor 的 `combatTags`
|
||||
- actor 装备 build 标签
|
||||
- actor 当前限回合 buff 标签
|
||||
- 显式套装合成标签
|
||||
- embedding build 伤害修正
|
||||
|
||||
#### C. `src/data/characterPresets.ts`
|
||||
|
||||
角色需要正式持有 `combatTags`,不要再只放在 UI 常量里。
|
||||
|
||||
#### D. `src/data/monsterPresets.ts`
|
||||
|
||||
怪物新增:
|
||||
|
||||
- `combatTags`
|
||||
- `craftTags`
|
||||
|
||||
保留:
|
||||
|
||||
- `habitatTags`
|
||||
|
||||
#### E. `src/hooks/useGamePersistence.ts`
|
||||
|
||||
存档兼容必须同步补:
|
||||
|
||||
- `activeBuildBuffs`
|
||||
- 新字段默认值
|
||||
- 旧存档自动补空数组
|
||||
|
||||
## 11. 编辑器与策划工作流
|
||||
|
||||
结合当前项目“先补类型和规则,再补 UI”的经验,编辑器建议按下面顺序补。
|
||||
|
||||
### 11.1 标签注册表
|
||||
|
||||
先做一个统一标签注册表,再让各编辑器引用它。
|
||||
|
||||
编辑器最少应支持:
|
||||
|
||||
- 选择规范标签
|
||||
- 查看别名
|
||||
- 查看所属 build 簇
|
||||
- 查看与其他标签的相似度
|
||||
|
||||
### 11.2 Item Catalog Editor
|
||||
|
||||
当前它已经能展示 `buildProfile`,下一步建议补:
|
||||
|
||||
- 规范标签选择器
|
||||
- 当前装备可形成的 build 预览
|
||||
- 2 件/3 件套装合成标签预览
|
||||
- 拆解产物预览
|
||||
|
||||
### 11.3 角色/怪物编辑
|
||||
|
||||
建议把角色和怪物的 `combatTags` 录入正式数据,不再只放展示文案。
|
||||
|
||||
### 11.4 相似度预计算脚本
|
||||
|
||||
建议加一个脚本,例如:
|
||||
|
||||
```text
|
||||
scripts/build-tag-similarity.mjs
|
||||
```
|
||||
|
||||
负责:
|
||||
|
||||
- 读取标签注册表
|
||||
- 生成 embedding
|
||||
- 输出相似度矩阵 JSON
|
||||
|
||||
这样运行时就不需要联网或现算。
|
||||
|
||||
## 12. 数值与反滥用约束
|
||||
|
||||
为了让系统长期可控,建议一开始就加以下约束:
|
||||
|
||||
1. 同规范标签去重,不允许同词条多件装备无限叠加基础 `+1`。
|
||||
2. 激活标签上限 `8`,避免后期词条爆炸。
|
||||
3. 伤害 build 增益封顶 `45%`,防止纯标签乘爆。
|
||||
4. 2 件/3 件套只生成合成标签,不再额外套一层独立乘区,避免双重膨胀。
|
||||
5. `habitatTags` 不直接进伤害,避免出现“矿道标签提高输出”这种语义跑偏。
|
||||
6. buff 标签以刷新时长为主,不以无限叠层为主。
|
||||
7. 重铸只在语义近邻内滚动,避免 build 完全变异。
|
||||
8. 拆解返还率不要超过完整锻造成本的 `60%~70%`,避免无限刷循环。
|
||||
|
||||
## 13. 分阶段实施建议
|
||||
|
||||
按照当前项目文档里已经验证过的开发经验,推荐顺序是“类型 -> 规则 -> hook -> UI”,不要反过来。
|
||||
|
||||
### 阶段 A:先补数据骨架
|
||||
|
||||
- 新增 `buildTagRegistry`
|
||||
- 为角色/怪物补 `combatTags`
|
||||
- 为物品补 `craftTags / forgeRank`
|
||||
- 为 `GameState` 补 `activeBuildBuffs`
|
||||
|
||||
### 阶段 B:再补规则
|
||||
|
||||
- 实现标签规范化
|
||||
- 实现 embedding 相似度矩阵
|
||||
- 实现 `rawBuildScore`
|
||||
- 实现三槽位显式套装合成标签
|
||||
- 实现 buff 标签衰减
|
||||
|
||||
### 阶段 C:接入战斗
|
||||
|
||||
- `useCombatFlow.ts` 改为统一伤害入口
|
||||
- 玩家 / 同伴 / 怪物统一读取 build
|
||||
- `equipmentEffects.ts` 正式生效
|
||||
|
||||
### 阶段 D:补合成/锻造闭环
|
||||
|
||||
- 拆解
|
||||
- 合成
|
||||
- 锻造
|
||||
- 重铸
|
||||
- 铭刻
|
||||
|
||||
### 阶段 E:最后补编辑器与 UI
|
||||
|
||||
- 物品 build 预览
|
||||
- 套装预览
|
||||
- 锻造页
|
||||
- 材料来源与标签簇提示
|
||||
|
||||
## 14. 一句话总结
|
||||
|
||||
本方案的核心不是单独增加“套装数值”,而是把装备、角色/怪物、限回合 buff 都转成统一的语义 build 标签,再用“每标签基础值 1 + 标签间 embedding 相似度”的方式形成构筑强度,并把这份构筑强度直接接进当前伤害输出,同时让怪物掉落、宝藏、交易、拆解、合成、锻造、铭刻围绕同一套标签体系形成闭环。
|
||||
437
docs/FUNCTION_DESIGN_AUDIT_2026-04-03.md
Normal file
437
docs/FUNCTION_DESIGN_AUDIT_2026-04-03.md
Normal file
@@ -0,0 +1,437 @@
|
||||
# 当前 Function 设计审计(2026-04-03)
|
||||
|
||||
## 审计范围
|
||||
|
||||
本次审计重点阅读并对照了这些位置:
|
||||
|
||||
- `docs/ADVENTURE_RUNTIME_DEV_EXPERIENCE.md`
|
||||
- `docs/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md`
|
||||
- `docs/PROJECT_DEVELOPMENT_EXPERIENCE.md`
|
||||
- `docs/CURRENT_GAME_ITERATION_PRIORITIES_2026-04-03.md`
|
||||
- `src/data/stateFunctions.ts`
|
||||
- `src/data/npcInteractions.ts`
|
||||
- `src/data/treasureInteractions.ts`
|
||||
- `src/hooks/useStoryGeneration.ts`
|
||||
- `src/hooks/story/npcEncounterActions.ts`
|
||||
- `src/hooks/story/npcInteraction.ts`
|
||||
- `src/hooks/story/progressionActions.ts`
|
||||
- `src/hooks/story/storyGenerationState.ts`
|
||||
- `src/services/ai.ts`
|
||||
- `src/services/prompt.ts`
|
||||
- `src/components/AdventurePanel.tsx`
|
||||
- `src/components/StateFunctionEditor.tsx`
|
||||
|
||||
## 先说结论
|
||||
|
||||
当前运行时的“function”不是单一体系,而是至少分成了 4 层:
|
||||
|
||||
1. `stateFunctions.ts` 里的基础战斗 / 空闲 function。
|
||||
2. `npcInteractions.ts` 里的 NPC 交互 function。
|
||||
3. `treasureInteractions.ts` 里的宝藏交互 function。
|
||||
4. `useStoryGeneration.ts` / `npcInteraction.ts` / 背包装备锻造里额外加出来的“流程控制型 functionId”。
|
||||
|
||||
“选完选项触发 function 后没有触发剧情推理”这类现象,当前主要有 3 种来源:
|
||||
|
||||
1. **按设计先走本地分流,不会在第一次点击时立刻推理。**
|
||||
2. **剧情推理其实已经完成,但被“继续冒险”这道 UI 中转门挡住了。**
|
||||
3. **真正的可见性 bug:`story_continue_adventure` 的文案常量已经乱码,导致 UI 提示失效,用户很容易误判为没继续推理。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 当前 function 体系分层
|
||||
|
||||
### 1.1 基础状态 function:`src/data/stateFunctions.ts`
|
||||
|
||||
这一层才是严格意义上的“状态 function 注册表”,由 `resolveFunctionOption` 统一解析。
|
||||
|
||||
当前运行时实际启用 12 个:
|
||||
|
||||
- 战斗类 7 个:
|
||||
- `battle_all_in_crush`
|
||||
- `battle_guard_break`
|
||||
- `battle_probe_pressure`
|
||||
- `battle_feint_step`
|
||||
- `battle_recover_breath`
|
||||
- `battle_finisher_window`
|
||||
- `battle_escape_breakout`
|
||||
- 空闲类 5 个:
|
||||
- `idle_explore_forward`
|
||||
- `idle_travel_next_scene`
|
||||
- `idle_rest_focus`
|
||||
- `idle_observe_signs`
|
||||
- `idle_call_out`
|
||||
|
||||
关键设计点:
|
||||
|
||||
- 定义源头是 `BATTLE_FUNCTIONS` / `IDLE_FUNCTIONS`。
|
||||
- 最终运行时集合由 `buildStateFunctionDefinitions` 产出。
|
||||
- 可执行过滤走 `getExecutableFunctions`。
|
||||
- 文案和视觉包装走 `resolveFunctionOption`。
|
||||
- 默认选项池走 `getDefaultFunctionIdsForContext`。
|
||||
|
||||
注意:
|
||||
|
||||
- `idle_follow_clue` 仍然留在源码和 prompt 描述里,但在 `applyRuntimeFunctionAdjustments` 中被直接过滤掉,不会进入运行时 function 集合。
|
||||
- `src/components/game-shell/useSceneTransitionModel.ts` 里仍然保留了 `idle_follow_clue` 的转场映射,这说明当前 function 清单并不完全一致。
|
||||
|
||||
### 1.2 NPC 交互 function:`src/data/npcInteractions.ts`
|
||||
|
||||
这一层不是通过 `resolveFunctionOption` 生成的,而是 `buildNpcEncounterStoryMoment` 直接拼出 `StoryOption`,并挂上 `interaction.kind = 'npc'`。
|
||||
|
||||
按功能类型看,当前会出现这些 `functionId`:
|
||||
|
||||
- `npc_preview_talk`
|
||||
- `npc_trade`
|
||||
- `npc_fight`
|
||||
- `npc_spar`
|
||||
- `npc_help`
|
||||
- `npc_chat`(可重复出现 2 个以上,代表不同聊天话题)
|
||||
- `npc_gift`
|
||||
- `npc_recruit`
|
||||
- `npc_quest_accept`
|
||||
- `npc_quest_turn_in`
|
||||
- `npc_leave`
|
||||
|
||||
关键设计点:
|
||||
|
||||
- 这一层的 function 是“眼前 NPC 交互目录”,不是基础状态机目录。
|
||||
- 真正执行分流不在 `stateFunctions.ts`,而在 `handleNpcInteraction` / `resolveNpcInteractionDecision`。
|
||||
- prompt 层通过 `availableOptions` 把这些 function 当作“固定可选项列表”交给模型,要求模型保留数量和 `functionId`。
|
||||
|
||||
### 1.3 宝藏交互 function:`src/data/treasureInteractions.ts`
|
||||
|
||||
这一层同样不是 `resolveFunctionOption` 体系,而是直接构造带 `interaction.kind = 'treasure'` 的选项。
|
||||
|
||||
当前有 3 个:
|
||||
|
||||
- `treasure_secure`
|
||||
- `treasure_inspect`
|
||||
- `treasure_leave`
|
||||
|
||||
执行时由 `useTreasureFlow.ts` 接管,最终再回到 `commitGeneratedState` 继续剧情推理。
|
||||
|
||||
### 1.4 流程控制 / 面板动作 functionId
|
||||
|
||||
这类 `functionId` 会进入 `lastFunctionId` 或 `commitGeneratedState`,但不在 `stateFunctions.ts` 注册表里:
|
||||
|
||||
- 流程控制:
|
||||
- `story_continue_adventure`
|
||||
- `camp_travel_home_scene`
|
||||
- `story_opening_camp_dialogue`
|
||||
- 面板动作:
|
||||
- `inventory_use`
|
||||
- `equipment_equip`
|
||||
- `equipment_unequip`
|
||||
- `forge_craft`
|
||||
- `forge_dismantle`
|
||||
- `forge_reforge`
|
||||
|
||||
这说明当前项目里“functionId”已经同时承担了 3 个角色:
|
||||
|
||||
- 运行时基础状态动作 ID
|
||||
- NPC / 宝藏交互动作 ID
|
||||
- 流程 / 面板事件 ID
|
||||
|
||||
这能跑,但可追踪性已经开始分裂。
|
||||
|
||||
---
|
||||
|
||||
## 2. 当前剧情推理触发链路
|
||||
|
||||
### 2.1 会立刻触发剧情推理的主链
|
||||
|
||||
这类点击后会直接走 AI 续推:
|
||||
|
||||
- 普通战斗 / 空闲 function
|
||||
- `handleChoice`
|
||||
- `buildResolvedChoiceState`
|
||||
- `playResolvedChoice`
|
||||
- `generateNextStep`
|
||||
- 这些 NPC 交互
|
||||
- `npc_preview_talk`
|
||||
- `npc_help`
|
||||
- `npc_chat`
|
||||
- `npc_fight`
|
||||
- `npc_spar`
|
||||
- `npc_quest_accept`
|
||||
- `npc_quest_turn_in`
|
||||
- `npc_leave`
|
||||
- 宝藏交互确认后
|
||||
- `treasure_secure`
|
||||
- `treasure_inspect`
|
||||
- `treasure_leave`
|
||||
- 面板动作确认后
|
||||
- `inventory_use`
|
||||
- `equipment_equip`
|
||||
- `equipment_unequip`
|
||||
- `forge_*`
|
||||
|
||||
共性是:
|
||||
|
||||
- 要么直接在 `handleChoice` 里调用 `generateNextStep`。
|
||||
- 要么先走 `commitGeneratedState` / `commitGeneratedStateWithEncounterEntry`,再统一调用 `generateStoryForState`。
|
||||
|
||||
### 2.2 第一次点击不会立刻触发剧情推理的分流
|
||||
|
||||
这类最容易被误判成“function 触发了,但剧情没继续”:
|
||||
|
||||
- `npc_trade`
|
||||
- `npc_gift`
|
||||
- `npc_recruit`(队伍满时必进 modal;队伍未满时会先进招募对话流)
|
||||
|
||||
原因不是漏调,而是当前设计明确先走:
|
||||
|
||||
- `resolveNpcInteractionDecision`
|
||||
- `trade_modal`
|
||||
- `gift_modal`
|
||||
- `recruit_modal`
|
||||
- `recruit_immediate`
|
||||
|
||||
也就是说:
|
||||
|
||||
- 第一次点击只是**打开模态框 / 进入招募流程**。
|
||||
- 真正推进剧情推理,要等到:
|
||||
- `confirmTrade`
|
||||
- `confirmGift`
|
||||
- `confirmRecruit`
|
||||
- `executeRecruitment`
|
||||
|
||||
如果产品预期是“用户每点一次选项就必须立即看到剧情继续”,这一层现在不满足。
|
||||
|
||||
### 2.3 `npc_chat` 是“先推理,再延迟展示选项”
|
||||
|
||||
`npc_chat` 不是普通的“生成下一幕 + 立刻给选项”,而是单独走这条链:
|
||||
|
||||
1. `commitNpcChatState` 先流式生成聊天正文。
|
||||
2. 聊天结束后,再调用一次 `generateNextStep`。
|
||||
3. 新一轮冒险选项不直接显示,而是塞进 `deferredOptions`。
|
||||
4. 当前界面只先显示一个 `story_continue_adventure`。
|
||||
5. 用户再点一次,才把 `deferredOptions` 放出来。
|
||||
|
||||
所以这里常见的误判是:
|
||||
|
||||
- **剧情推理其实已经做完了。**
|
||||
- 只是 UI 先让用户“继续冒险”一次,才把新选项展示出来。
|
||||
|
||||
---
|
||||
|
||||
## 3. 本次排查发现的重点问题
|
||||
|
||||
### 3.1 真正最像“没触发剧情推理”的地方:模态框型 NPC function
|
||||
|
||||
定位:
|
||||
|
||||
- `src/hooks/story/npcEncounterActions.ts:427-449`
|
||||
- `src/hooks/story/storyGenerationState.ts:41-80`
|
||||
- `src/hooks/story/npcInteraction.ts:427-585`
|
||||
|
||||
现象:
|
||||
|
||||
- 点击 `npc_trade` / `npc_gift` / `npc_recruit` 后,故事文本区通常不会立刻变化。
|
||||
- 当前故事也不会马上追加一段新的 `storyText`。
|
||||
|
||||
原因:
|
||||
|
||||
- 这些 function 的第一次点击只做本地 UI 分流。
|
||||
- 直到用户在 modal 里确认,才会真正调用 `commitGeneratedState` 继续推理。
|
||||
|
||||
判断:
|
||||
|
||||
- **这不是单纯 bug,而是当前设计本身如此。**
|
||||
- 但如果玩家从“选项即剧情推进”的心智出发,会非常像“点了没反应”。
|
||||
|
||||
建议优先级:高。
|
||||
|
||||
### 3.2 最关键的实际 bug:`story_continue_adventure` 文案乱码,导致“已推理但未显式提示”
|
||||
|
||||
定位:
|
||||
|
||||
- `src/hooks/useStoryGeneration.ts:92-96`
|
||||
- `src/hooks/story/npcEncounterActions.ts:281-400`
|
||||
- `src/components/AdventurePanel.tsx:534`
|
||||
- `src/components/AdventurePanel.tsx:860`
|
||||
- `src/components/AdventurePanel.tsx:879`
|
||||
|
||||
现象:
|
||||
|
||||
- `npc_chat` 完成后,系统本应展示一个“继续冒险”按钮,提示“剧情推理完成,继续后显示新的冒险选项”。
|
||||
- 但当前 `CONTINUE_ADVENTURE_ACTION_TEXT` 常量已经写成了乱码:`缁х画鍐掗櫓`。
|
||||
- `AdventurePanel` 又是靠 `option.actionText === '继续冒险'` 来识别这个特殊按钮。
|
||||
|
||||
直接后果:
|
||||
|
||||
- 特殊提示 UI 不会出现。
|
||||
- 玩家只会看到一个普通且乱码的单选项。
|
||||
- 实际上 `deferredOptions` 已经算好了,但用户极容易误会为“剧情没有继续推理”。
|
||||
|
||||
判断:
|
||||
|
||||
- **这是本次排查里最明确的实现级 bug。**
|
||||
- 它不会阻止底层推理发生,但会严重破坏“推理已完成”的可见性。
|
||||
|
||||
建议优先级:最高。
|
||||
|
||||
### 3.3 `StateFunctionEditor` 覆盖不到真正最容易出问题的 function
|
||||
|
||||
定位:
|
||||
|
||||
- `src/components/StateFunctionEditor.tsx:13-21`
|
||||
- `src/components/StateFunctionEditor.tsx:488`
|
||||
- `src/components/StateFunctionEditor.tsx:772-838`
|
||||
- `src/components/StateFunctionEditor.tsx:992-1211`
|
||||
|
||||
现象:
|
||||
|
||||
- 编辑器预览只接了 `buildStateFunctionDefinitions` / `getAllStateFunctionDefinitions` / `resolveFunctionOption`。
|
||||
- 也就是它只能预览 `stateFunctions.ts` 那 12 个基础 function。
|
||||
|
||||
覆盖不到的关键分支:
|
||||
|
||||
- `npc_*`
|
||||
- `treasure_*`
|
||||
- `npc_preview_talk`
|
||||
- `story_continue_adventure`
|
||||
- modal 分流
|
||||
- `npc_chat` 的 `deferredOptions`
|
||||
|
||||
判断:
|
||||
|
||||
- 这不是运行时 bug,但它解释了为什么这类问题很难在编辑器里提前暴露。
|
||||
- 当前“Function 编辑器”并没有覆盖到最复杂、最容易让用户感知为异常的 function 链路。
|
||||
|
||||
建议优先级:高。
|
||||
|
||||
### 3.4 function 清单存在“源码有、运行时无、别处还在引用”的分裂
|
||||
|
||||
定位:
|
||||
|
||||
- `src/data/stateFunctions.ts:350`
|
||||
- `src/data/stateFunctions.ts:429-441`
|
||||
- `src/components/game-shell/useSceneTransitionModel.ts:19-25`
|
||||
|
||||
现象:
|
||||
|
||||
- `idle_follow_clue` 仍在定义表、提示词描述、若干 `switch` 分支中存在。
|
||||
- 但最终在 `applyRuntimeFunctionAdjustments` 被过滤掉,不会进入运行时 function 集合。
|
||||
- `useSceneTransitionModel` 里却还保留着它的转场模式映射。
|
||||
|
||||
判断:
|
||||
|
||||
- 这不是“没触发剧情推理”的直接原因。
|
||||
- 但会让维护者误判当前真实 function 清单,也会增加后续继续扩 function 时的混乱。
|
||||
|
||||
建议优先级:中。
|
||||
|
||||
### 3.5 自动化测试几乎没有覆盖“最容易让人误会没推理”的链路
|
||||
|
||||
定位:
|
||||
|
||||
- 当前已有测试主要是 `src/hooks/story/storyGenerationState.test.ts`
|
||||
|
||||
已覆盖:
|
||||
|
||||
- `trade_modal`
|
||||
- `recruit_modal`
|
||||
- 地图切场景
|
||||
|
||||
未覆盖:
|
||||
|
||||
- `npc_chat` 的 `deferredOptions -> story_continue_adventure -> 真正显示新选项`
|
||||
- `npc_trade` / `npc_gift` / `npc_recruit` 确认后是否一定调用 `commitGeneratedState`
|
||||
- `story_continue_adventure` 文案与 UI 特判是否一致
|
||||
- `npc_preview_talk -> enterNpcInteraction -> generateStoryForState`
|
||||
- 宝藏分支确认后是否稳定续推
|
||||
|
||||
判断:
|
||||
|
||||
- 这类问题之所以能长期存在,一个核心原因就是**最关键的续推分支没有测试兜底**。
|
||||
|
||||
建议优先级:高。
|
||||
|
||||
---
|
||||
|
||||
## 4. 按“首次点击是否立即触发剧情推理”整理
|
||||
|
||||
### 4.1 首次点击就会继续推理
|
||||
|
||||
- `battle_*`
|
||||
- `idle_*`
|
||||
- `npc_preview_talk`
|
||||
- `npc_help`
|
||||
- `npc_chat`
|
||||
- `npc_fight`
|
||||
- `npc_spar`
|
||||
- `npc_quest_accept`
|
||||
- `npc_quest_turn_in`
|
||||
- `npc_leave`
|
||||
- `treasure_*`
|
||||
- `inventory_use`
|
||||
- `equipment_*`
|
||||
- `forge_*`
|
||||
|
||||
### 4.2 首次点击不会立刻继续推理
|
||||
|
||||
- `npc_trade`
|
||||
- `npc_gift`
|
||||
- `npc_recruit`(至少会先进入确认 / 对话流)
|
||||
- `story_continue_adventure`
|
||||
|
||||
这里要特别分清:
|
||||
|
||||
- `npc_trade` / `npc_gift` / `npc_recruit` 是**先分流,后确认,确认后再推理**。
|
||||
- `story_continue_adventure` 是**推理已经完成,只是先把结果选项延后展示**。
|
||||
|
||||
---
|
||||
|
||||
## 5. 建议的修正顺序
|
||||
|
||||
### P0
|
||||
|
||||
- 修掉 `CONTINUE_ADVENTURE_ACTION_TEXT` 的乱码。
|
||||
- `AdventurePanel` 不要再靠 `actionText === '继续冒险'` 判定特殊按钮,改成按 `functionId === 'story_continue_adventure'` 判定。
|
||||
|
||||
### P1
|
||||
|
||||
- 明确产品规则:
|
||||
- `npc_trade` / `npc_gift` / `npc_recruit` 第一次点击是否就应该写入一条“进入交易 / 送礼 / 招募确认”的剧情反馈。
|
||||
- 如果答案是“应该”,那这些 modal 型 function 需要补一层轻量 story feedback,而不是只弹框。
|
||||
|
||||
### P1
|
||||
|
||||
- 给 `npc_chat` 补自动化测试:
|
||||
- 聊天后必须出现 `story_continue_adventure`
|
||||
- 第二次点击后必须能展示 `deferredOptions`
|
||||
|
||||
### P1
|
||||
|
||||
- 给 `npc_trade` / `npc_gift` / `npc_recruit` 补自动化测试:
|
||||
- 第一次点击只分流
|
||||
- 确认后一定触发 `commitGeneratedState`
|
||||
- 后续一定能拿到新的 `StoryMoment`
|
||||
|
||||
### P2
|
||||
|
||||
- 扩展 `StateFunctionEditor` 或补新的“交互 function 预览器”,把 `npc_*` / `treasure_*` / `story_continue_adventure` 也纳入可预演范围。
|
||||
|
||||
### P2
|
||||
|
||||
- 清理 `idle_follow_clue` 这类“半退场”的 function,保证:
|
||||
- 运行时集合
|
||||
- prompt 描述
|
||||
- 编辑器
|
||||
- 转场映射
|
||||
- 测试
|
||||
|
||||
保持一致。
|
||||
|
||||
---
|
||||
|
||||
## 最后结论
|
||||
|
||||
当前最值得优先处理的,不是再继续加新的 function,而是先把这 3 个点收拢:
|
||||
|
||||
1. **把“首次点击只分流、不推理”的 function 明确标出来。**
|
||||
2. **把 `npc_chat -> story_continue_adventure -> deferredOptions` 这条延迟展示链做清楚。**
|
||||
3. **先修掉 `story_continue_adventure` 的乱码和基于文案的 UI 判断。**
|
||||
|
||||
如果不先处理这几个点,用户会持续把“按设计延迟”与“真实漏调剧情推理”混在一起感知,后面 function 越多,这类问题会越难排查。
|
||||
191
docs/FUNCTION_SCRIPT_CATALOG_2026-04-04.md
Normal file
191
docs/FUNCTION_SCRIPT_CATALOG_2026-04-04.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# Function 独立脚本目录(2026-04-04)
|
||||
|
||||
## 目标
|
||||
|
||||
这次整理把运行时所有核心 `functionId` 统一收敛到 `src/data/functionCatalog/` 目录下:
|
||||
|
||||
- `state/`:基础状态 function,每个 function 一个独立脚本,`src/data/stateFunctions.ts` 只负责聚合、覆盖和运行时过滤。
|
||||
- `npc/`:NPC 交互 function 的独立说明脚本。
|
||||
- `treasure/`:宝藏交互 function 的独立说明脚本。
|
||||
- `flow/`:流程控制型 function 的独立说明脚本。
|
||||
- `panel/`:背包 / 装备 / 锻造等面板动作 function 的独立说明脚本。
|
||||
|
||||
每个脚本都包含:
|
||||
|
||||
- 脚本顶部的中文注释,说明定位、触发时机和执行意图。
|
||||
- 导出的 function 元数据,统一记录 `id`、标题、详细描述、执行方式和结果。
|
||||
- 对基础 state function 额外导出正式运行时 definition,供 `stateFunctions.ts` 聚合。
|
||||
- 对流程控制 / NPC 分流类 function,额外导出运行时 helper,例如:
|
||||
- `buildContinueAdventureOption`
|
||||
- `buildCampTravelHomeOption`
|
||||
- `buildNpcPreviewTalkOption`
|
||||
- `buildNpcTradeModalState`
|
||||
- `buildNpcGiftModalState`
|
||||
- `buildNpcRecruitModalState`
|
||||
这些 helper 已经在实际运行时被调用,不再只是文档描述。
|
||||
|
||||
## 目录入口
|
||||
|
||||
- 统一索引:`src/data/functionCatalog/index.ts`
|
||||
- 基础状态聚合:`src/data/stateFunctions.ts`
|
||||
- 文档类型定义:`src/data/functionCatalog/types.ts`
|
||||
|
||||
## 基础状态 Function
|
||||
|
||||
- `battle_all_in_crush`
|
||||
脚本:`src/data/functionCatalog/state/battleAllInCrush.ts`
|
||||
说明:战斗中的正面强压动作,只在 `battle` 状态且有存活敌人时进入候选池。它会提高伤害与终结/爆发技能权重,同时抬高承伤,适合收头、压血和赌一波换血抢节奏。
|
||||
|
||||
- `battle_guard_break`
|
||||
脚本:`src/data/functionCatalog/state/battleGuardBreak.ts`
|
||||
说明:围绕破架和打断敌方节奏的重击 function。它强调“砸开敌人架势”,在保持较高伤害的同时略降反击压力,适合对付正在招架或露出结构性破绽的敌人。
|
||||
|
||||
- `battle_probe_pressure`
|
||||
脚本:`src/data/functionCatalog/state/battleProbePressure.ts`
|
||||
说明:稳扎稳打的试探压制动作。它把回合塑造成持续施压与信息试探,偏向 `steady` 技能,适合低蓝、观望或不想冒进的战斗阶段。
|
||||
|
||||
- `battle_feint_step`
|
||||
脚本:`src/data/functionCatalog/state/battleFeintStep.ts`
|
||||
说明:通过虚晃、变线和抢身位制造切入窗口的机动 function。它提高机动技能权重并降低承伤,适合塑造灵巧、安全的近身切入节奏。
|
||||
|
||||
- `battle_recover_breath`
|
||||
脚本:`src/data/functionCatalog/state/battleRecoverBreath.ts`
|
||||
说明:战斗中的保命和回气动作。它负责回血、回蓝和推进冷却轮转,优先在低血低蓝时提权,用来把高压局面拉回可控节奏。
|
||||
|
||||
- `battle_finisher_window`
|
||||
脚本:`src/data/functionCatalog/state/battleFinisherWindow.ts`
|
||||
说明:针对敌方破绽和残局血线的终结 function。它大幅提高终结和爆发倾向,适合在敌人残血、明显失衡或已经露出空档时直接收口。
|
||||
|
||||
- `battle_escape_breakout`
|
||||
脚本:`src/data/functionCatalog/state/battleEscapeBreakout.ts`
|
||||
说明:从当前战斗中脱身的逃跑 function。它不追求输出,而是驱动逃跑时长、拉开距离和追兵滞后参数,适合保命、撤离和切掉当前战斗。
|
||||
|
||||
- `idle_explore_forward`
|
||||
脚本:`src/data/functionCatalog/state/idleExploreForward.ts`
|
||||
说明:空闲状态下最核心的推进 function。它表示继续深入当前场景,允许下一步真正生成角色、怪物、宝藏或危险,是默认的主探索入口。
|
||||
|
||||
- `idle_travel_next_scene`
|
||||
脚本:`src/data/functionCatalog/state/idleTravelNextScene.ts`
|
||||
说明:从当前地点切换到相邻场景的地图流转 function。它通过 `sceneShift: 1` 把探索重心从“继续挖当前场景”改成“前往下一处地点重新开始遭遇”。
|
||||
|
||||
- `idle_rest_focus`
|
||||
脚本:`src/data/functionCatalog/state/idleRestFocus.ts`
|
||||
说明:非战斗状态下的原地调息 function。它不推进遭遇,只负责给玩家一个稳定的回血回蓝缓冲回合,适合战后休整或资源回收。
|
||||
|
||||
- `idle_observe_signs`
|
||||
脚本:`src/data/functionCatalog/state/idleObserveSigns.ts`
|
||||
说明:围绕侦察和判断的空闲 function。它要求本轮先输出可延续的观察结果,而不是立刻推进遭遇,适合摸清附近异动和前方风险。
|
||||
|
||||
- `idle_follow_clue`
|
||||
脚本:`src/data/functionCatalog/state/idleFollowClue.ts`
|
||||
说明:循着脚印、声音或痕迹继续逼近目标的保留 function。它目前仍保留在源码定义层,但运行时会在聚合阶段被过滤,属于停用中但仍需记录的 function。
|
||||
|
||||
- `idle_call_out`
|
||||
脚本:`src/data/functionCatalog/state/idleCallOut.ts`
|
||||
说明:主动出声试探的空闲 function。它把探索从被动观察切成主动打破寂静,适合把暗处角色、怪物或潜伏动静直接逼到台前。
|
||||
|
||||
## NPC 交互 Function
|
||||
|
||||
- `npc_preview_talk`
|
||||
脚本:`src/data/functionCatalog/npc/npcPreviewTalk.ts`
|
||||
说明:把前探预览切到正式 NPC 互动层的入口 function。第一次点击主要完成遭遇切换,不是立刻结算完整聊天。
|
||||
|
||||
- `npc_trade`
|
||||
脚本:`src/data/functionCatalog/npc/npcTrade.ts`
|
||||
说明:与 NPC 交易的入口 function。点击后通常先打开交易 modal,确认买卖后才真正把结果写回主故事。
|
||||
|
||||
- `npc_fight`
|
||||
脚本:`src/data/functionCatalog/npc/npcFight.ts`
|
||||
说明:与眼前 NPC 正面开战的 function。它会把交互态切到 NPC 战斗模式,并在战后结算掉落、关系和任务推进。
|
||||
|
||||
- `npc_spar`
|
||||
脚本:`src/data/functionCatalog/npc/npcSpar.ts`
|
||||
说明:与眼前 NPC 进行点到为止切磋的 function。它复用战斗骨架,但目标是关系推进,不是击杀或掠夺。
|
||||
|
||||
- `npc_help`
|
||||
脚本:`src/data/functionCatalog/npc/npcHelp.ts`
|
||||
说明:向 NPC 寻求补给、回复或支援的 function。奖励由本地规则稳定计算,避免帮助收益被模型临场漂移。
|
||||
|
||||
- `npc_chat`
|
||||
脚本:`src/data/functionCatalog/npc/npcChat.ts`
|
||||
说明:围绕当前话题与 NPC 继续交谈的 function。它会先生成对话正文,再把真正的新选项延迟到 `story_continue_adventure` 之后展示。
|
||||
|
||||
- `npc_gift`
|
||||
脚本:`src/data/functionCatalog/npc/npcGift.ts`
|
||||
说明:向 NPC 送礼的入口 function。第一次点击通常只打开礼物面板,确认礼物后才结算好感变化并继续剧情。
|
||||
|
||||
- `npc_recruit`
|
||||
脚本:`src/data/functionCatalog/npc/npcRecruit.ts`
|
||||
说明:邀请 NPC 入队的招募 function。若队伍已满会先进入替换确认流程;若满足条件且队伍未满,可直接进入招募对话和入队结算。
|
||||
|
||||
- `npc_quest_accept`
|
||||
脚本:`src/data/functionCatalog/npc/npcQuestAccept.ts`
|
||||
说明:正式接下 NPC 委托的 function。它把本地生成的任务写入 quest log,并让剧情承接“玩家已经答应处理这件事”。
|
||||
|
||||
- `npc_quest_turn_in`
|
||||
脚本:`src/data/functionCatalog/npc/npcQuestTurnIn.ts`
|
||||
说明:向 NPC 交付已完成委托的 function。它负责领奖、任务状态收尾与交付剧情反馈。
|
||||
|
||||
- `npc_leave`
|
||||
脚本:`src/data/functionCatalog/npc/npcLeave.ts`
|
||||
说明:结束当前 NPC 互动并回到探索态的 function。它是正常离场出口,避免玩家必须通过战斗或继续聊天才能离开当前 encounter。
|
||||
|
||||
## 宝藏交互 Function
|
||||
|
||||
- `treasure_secure`
|
||||
脚本:`src/data/functionCatalog/treasure/treasureSecure.ts`
|
||||
说明:直接收取宝藏的 function。它优先把主要战利品拿到手,适合玩家不想再花回合排查机关时使用。
|
||||
|
||||
- `treasure_inspect`
|
||||
脚本:`src/data/functionCatalog/treasure/treasureInspect.ts`
|
||||
说明:先检查机关和线索再收取宝藏的 function。它通常会带来更完整的收益、恢复或额外线索,但叙事上更谨慎、更耗一步。
|
||||
|
||||
- `treasure_leave`
|
||||
脚本:`src/data/functionCatalog/treasure/treasureLeave.ts`
|
||||
说明:暂时不碰宝藏、只记住位置和异常的 function。适合资源不足、风险过高或暂时不想打断当前节奏时使用。
|
||||
|
||||
## 流程控制 Function
|
||||
|
||||
- `story_continue_adventure`
|
||||
脚本:`src/data/functionCatalog/flow/storyContinueAdventure.ts`
|
||||
说明:用于承接延迟展示的 `deferredOptions`。它通常出现在 `npc_chat` 之后,点击时更多是本地恢复后续选项,而不是再次请求新剧情。
|
||||
|
||||
- `camp_travel_home_scene`
|
||||
脚本:`src/data/functionCatalog/flow/campTravelHomeScene.ts`
|
||||
说明:营地开场或同伴交流结束后,正式前往角色主场景的流程项。它负责定制化场景迁移和状态清理,不属于普通 state function。
|
||||
|
||||
- `story_opening_camp_dialogue`
|
||||
脚本:`src/data/functionCatalog/flow/storyOpeningCampDialogue.ts`
|
||||
说明:驱动营地开场 4 到 6 行对白的特殊流程项。它会让 prompt 进入开场对白模式,而不是走普通探索推进模板。
|
||||
|
||||
## 面板动作 Function
|
||||
|
||||
- `inventory_use`
|
||||
脚本:`src/data/functionCatalog/panel/inventoryUse.ts`
|
||||
说明:在背包面板里使用药品、灵力物或 build buff 物品的 function。它先由本地规则结算资源变化,再把结果记入故事历史。
|
||||
|
||||
- `equipment_equip`
|
||||
脚本:`src/data/functionCatalog/panel/equipmentEquip.ts`
|
||||
说明:在装备面板中把背包物品穿戴到对应槽位的 function。它会同时处理替换旧装备、背包回收和属性重算。
|
||||
|
||||
- `equipment_unequip`
|
||||
脚本:`src/data/functionCatalog/panel/equipmentUnequip.ts`
|
||||
说明:从装备槽位卸下物品的 function。它确保卸装结果由本地规则严格处理,不会破坏背包数量和 loadout 一致性。
|
||||
|
||||
- `forge_craft`
|
||||
脚本:`src/data/functionCatalog/panel/forgeCraft.ts`
|
||||
说明:在锻造面板中执行配方制作的 function。它负责扣除材料和货币、产出新物品,并把制作结果写入故事历史。
|
||||
|
||||
- `forge_dismantle`
|
||||
脚本:`src/data/functionCatalog/panel/forgeDismantle.ts`
|
||||
说明:在锻造面板中拆解物品回收材料的 function。拆解产出由本地锻造规则控制,避免与物品设计脱节。
|
||||
|
||||
- `forge_reforge`
|
||||
脚本:`src/data/functionCatalog/panel/forgeReforge.ts`
|
||||
说明:在锻造面板中重铸现有物品的 function。它负责货币消耗、生成新结果与历史记录,适合承接装备再随机化或再塑形。
|
||||
|
||||
## 当前实现约定
|
||||
|
||||
- `src/data/stateFunctions.ts` 现在只负责基础 state function 的聚合、override 合并、运行时过滤和 option 解析。
|
||||
- 非 state function 目前仍由各自原有流程模块执行,但它们的 `id`、标题和详细说明已经统一收口到 `functionCatalog/`。
|
||||
- 后续新增 function 时,建议先补独立脚本,再把运行时调用接进来,最后同步这份目录文档。
|
||||
91
docs/GAME_EDITOR_PRESET_TEXT_AUDIT_2026-03-29.md
Normal file
91
docs/GAME_EDITOR_PRESET_TEXT_AUDIT_2026-03-29.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# 游戏 UI / 预设 / 编辑器 UI 英文与乱码复查
|
||||
|
||||
日期:`2026-03-29`
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 当前分支里,**确认存在源码级中文乱码的文件只有 1 个**:`src/components/CustomWorldEntityEditorModal.tsx`。
|
||||
- 历史上已经出现过的两处高风险乱码,本次**未复现**:
|
||||
- `src/components/GameShell.tsx` 角色选择页返回按钮
|
||||
- `src/data/npcInteractions.ts` 切磋敌对动作文案
|
||||
- 目前更主要的问题已经从“大面积乱码”转成了三类:
|
||||
- 运行时 UI 里的英文缩写或英文句子
|
||||
- 编辑器 UI 里的英文术语、原始枚举值和英文缩写
|
||||
- 预设数据中的英文名称、分类、标签、世界倾向、构筑角色等原始值直接透到 UI
|
||||
|
||||
## 复查口径
|
||||
|
||||
- 只统计会直接进入游戏 UI、编辑器 UI、预设预览或运行时详情的文本。
|
||||
- 不把纯内部实现名算进问题范围,比如接口路径、变量名、导入路径、素材文件夹名。
|
||||
- 终端输出已切换到 UTF-8 后复查,避免把“控制台显示乱码”误判成“源码真实乱码”。
|
||||
|
||||
## 一、已确认的源码级乱码
|
||||
|
||||
### 1. 编辑器 UI
|
||||
|
||||
| 文件 | 行号 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `360`, `381` | `褰撳墠浼氱洿鎺...`、`棰勮 #...` | 场景预设选择弹窗的说明文案和预设编号标签已写坏 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `551`, `559`, `569`, `572`, `578`, `581`, `584`, `587` | `褰撳墠澶栬妯℃澘`、`褰㈣薄妯℃澘`、`鍚嶇О`、`绉板彿 / 韬唤`、`鑳屾櫙`、`鎬ф牸`、`鎴樻枟椋庢牸`、`鏍囩` | 可扮演角色编辑表单的标题与字段标签存在真实乱码 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `663`, `666`, `669`, `672` | `鍚嶇О`、`韬唤 / 鑱岃兘`、`鎻忚堪`、`鍔ㄦ満` | 普通 NPC 编辑表单字段标签存在真实乱码,即“创建自定义世界 -> NPC 编辑页”这一段 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `721`, `732`, `736`, `739` | `鍦烘櫙`、`棰勮鍥句腑鐨勫垏纾...`、`鍚嶇О`、`鎻忚堪` | 场景编辑器默认回退文案、说明文案和字段标签存在真实乱码 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `771`, `791` | ``瑙掕壊-${...}``、`['绾跨储', '浜掑姩']` | 默认新建数据本身带乱码,会继续流入编辑器与结果页 |
|
||||
|
||||
## 二、游戏 UI 中的英文残留
|
||||
|
||||
| 文件 | 行号 | 当前文本/值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/AdventurePanel.tsx` | `179-187` | `restores HP during an adventure run`、`fits offensive loadouts` 等整句英文 | 奖励物品自动描述仍是英文句子,会直接进入运行时奖励详情 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `815-816`, `988`, `1124-1126`, `1497` | `HP`、`MP` | 玩家、怪物、NPC 状态条与效果预览仍使用英文缩写 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `1071`, `1109`, `1426` | `NPC 信息`、`敌对NPC` / `NPC`、`NPC 背包` | NPC 相关标题和标签仍是中英混排 |
|
||||
| `src/components/CompanionCampModal.tsx` | `176-177`, `232-233`, `254` | `HP`、`MP`、`NPC` | 同行编队卡片和空态提示仍保留英文缩写 |
|
||||
| `src/components/NpcModals.tsx` | `251`, `355`, `407` | `NPC 商品列表`、`NPC 商品`、`HP` / `MP` | 商店弹窗标题和物品效果预览仍是中英混排 |
|
||||
| `src/components/CharacterDetailModal.tsx` | `117` | `数量 x{item.quantity}` | 数量前缀里仍保留英文乘号写法 |
|
||||
|
||||
## 三、编辑器 UI 中的英文残留
|
||||
|
||||
| 文件 | 行号 | 当前文本/值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/ItemCatalogEditor.tsx` | `601`, `617-623` | `neutral / wuxia / xianxia`、`tag` 原始值 | 世界倾向和标签直接显示原始英文值 |
|
||||
| `src/components/ItemCatalogEditor.tsx` | `637-651`, `689` | `HP`、`MP`、`Build Buff`、`CD` | 属性预览、使用效果和背包卡片预览仍有英文缩写/术语 |
|
||||
| `src/components/ItemCatalogEditor.tsx` | `662-665` | `buildProfile.role`、`synergy` 原始值 | 构筑角色和协同标签直接暴露英文角色定位值 |
|
||||
| `src/components/ItemCatalogEditor.tsx` | `712`, `824`, `841` | `物品 ID`、`使用 Build Buff`、`套装 ID` | 字段标签里仍有 `ID` / `Build Buff` |
|
||||
| `src/components/StateFunctionEditor.tsx` | `843-846`, `910` | `HP`、`No visible target` | 预览摘要和实时状态里仍有英文缩写与整句英文 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1089-1091` | `Option behavior overrides saved.`、`Failed to save option behavior overrides` | 保存反馈文案仍是英文 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1133`, `1212`, `1218` | `definition.state`、`AnimationState`、`idle/move/attack` | 原始状态值和动画值仍直接显示 |
|
||||
| `src/components/PresetEditor.tsx` | `86-88`, `2212` | `NPC`、`敌对 NPC` | 页签和说明文案仍有 `NPC` 英文缩写 |
|
||||
| `src/components/PresetEditor.tsx` | `893-910`, `1997-2000` | `AnimationState`、`steady/burst/...`、`idle/move/attack/die` | 技能动作、技能风格、怪物预览动作直接显示原始英文枚举值 |
|
||||
| `src/components/PresetEditor.tsx` | `942`, `1015`, `1142`, `1456`, `1477`, `1483`, `1752`, `1788`, `1796`, `2037`, `2137`, `2174` | `Build Buff`、`ID`、`FPS` | 多个编辑字段和动画配置项仍保留英文术语 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `568-571`, `581`, `817` | `NPC 视觉编辑器`、`当前 NPC`、`x / y` | 标题、字段名和坐标显示仍有英文缩写 |
|
||||
|
||||
## 四、预设 / 数据层中的英文残留
|
||||
|
||||
这些内容虽然不一定直接在当前文件里渲染,但会进入运行时详情、掉落展示、物品编辑器、预设编辑器或交易界面。
|
||||
|
||||
| 文件 | 行号 | 当前文本/值 | 透出路径 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/data/monsterPresets.ts` | `438-456`, `479-499`, `535-540`, `647-650` | `Material`、`Relic`、`Armor`、`Consumable`、`Stone Shell Shard`、`Blood Lens`、英文描述句子、`rare/uncommon`、`material/relic/mana` | 会进入怪物掉落、物品详情、交易弹窗和编辑器预览 |
|
||||
| `src/data/itemDesign.ts` | `56-72`, `123-149`, `602-604`, `761-762`, `832-834` | `worldAffinity` 的 `neutral/wuxia/xianxia`,`role` 的 `fieldcraft/breaker/caster/berserker/assassin`,`tags` 的 `breaker/burst/mana`,`pieceName` 的 `dust/crystal/gem`,以及 `build` 混排短语 | 会直接透到 `ItemCatalogEditor` 的世界、标签、构筑角色、协同标签和套装信息 |
|
||||
| `src/data/characterPresets.ts` | `368-379`, `384-386`, `525-543`, `839-857`, `1024-1045` | `Double Jump`、`jump attack`、`Wall Slide`、`blunt/dry/direct`、`wary/fragmented` 等原始动作名和对话风格值 | 会被 `PresetEditor` 的动作/风格选择器直接显示 |
|
||||
|
||||
## 五、未复现的问题
|
||||
|
||||
- `src/components/GameShell.tsx` 角色选择页返回按钮旧乱码已修复,当前为“返回”。
|
||||
- `src/data/npcInteractions.ts` 旧的切磋动作乱码已修复,当前为“敌对/切磋前蓄力,点击后转为原地闪避”。
|
||||
- 本次扫描 `src/components`、`src/data` 未发现 `<EFBFBD>`(replacement character)类型的编码损坏。
|
||||
- 除 `src/components/CustomWorldEntityEditorModal.tsx` 外,本次未再确认到新的源码级中文乱码文件。
|
||||
- 自定义世界的 NPC 视觉编辑组件 `src/components/CustomWorldNpcVisualEditor.tsx` 本次未发现新的乱码。
|
||||
|
||||
## 六、建议处理顺序
|
||||
|
||||
1. 先修 `src/components/CustomWorldEntityEditorModal.tsx` 的真实乱码。
|
||||
2. 再清理 `src/components/AdventurePanel.tsx` 和 `src/data/monsterPresets.ts` 的整句英文,因为它们最容易直接破坏玩家观感。
|
||||
3. 为高频缩写和枚举值补统一映射层:
|
||||
- `NPC`
|
||||
- `HP` / `MP` / `CD`
|
||||
- `worldAffinity`
|
||||
- `role`
|
||||
- `tags`
|
||||
- `AnimationState`
|
||||
- 技能风格 `steady/burst/mobility/finisher/projectile`
|
||||
4. 最后统一编辑器里所有 `ID`、`FPS`、`Build Buff` 之类术语的显示策略。
|
||||
87
docs/GAME_EDITOR_PRESET_TEXT_AUDIT_2026-03-30.md
Normal file
87
docs/GAME_EDITOR_PRESET_TEXT_AUDIT_2026-03-30.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# 游戏 UI / 预设 / 编辑器 UI 英文与乱码复核
|
||||
|
||||
日期:`2026-03-30`
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 当前分支里,确认仍在真实渲染的源码级乱码主要集中在 2 个文件:
|
||||
- `src/components/GameShell.tsx`
|
||||
- `src/components/CustomWorldEntityEditorModal.tsx`
|
||||
- `src/components/NpcVisualEditor.tsx` 中确实还留有旧乱码字符串,但位于 `/* ... */` 注释块里,本次不计入“当前 UI 问题”。
|
||||
- 英文残留仍然较多,主要分为三类:
|
||||
- 游戏运行时界面的英文标题、空态文案和缩写
|
||||
- 编辑器界面的英文术语、英文保存反馈和原始枚举值
|
||||
- 预设 / 数据层中的英文名称、标签、角色定位、动画目录和 build 相关原值直接透到 UI
|
||||
|
||||
## 复核口径
|
||||
|
||||
- 显式按 UTF-8 读取文件,避免把终端编码问题误判成源码乱码。
|
||||
- 只统计会进入游戏 UI、编辑器 UI、预设预览或结果页的文本。
|
||||
- 注释块、变量名、导入路径、接口路径等内部实现名不计入本次问题清单。
|
||||
- 英文残留部分以下表中的“当前确实会显示或透传”的高优先级项为主。
|
||||
|
||||
## 一、已确认的真实乱码
|
||||
|
||||
| 范围 | 文件 | 行号 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 游戏 UI | `src/components/GameShell.tsx` | `565` | `瑙掕壊` | 主界面底部“角色”页签已写坏 |
|
||||
| 游戏 UI | `src/components/GameShell.tsx` | `578` | `鍐掗櫓` | 主界面底部“冒险”页签已写坏 |
|
||||
| 游戏 UI | `src/components/GameShell.tsx` | `591` | `鑳屽寘` | 主界面底部“背包”页签已写坏 |
|
||||
| 游戏 UI | `src/components/GameShell.tsx` | `710` | `闃熶紞` / `鑳屽寘` | 浮层标题根据面板切换时会显示乱码 |
|
||||
| 编辑器 UI | `src/components/CustomWorldEntityEditorModal.tsx` | `386` | `宸查€?` | 场景预设选择弹窗中的“已选中”状态标签已写坏 |
|
||||
| 编辑器 UI | `src/components/CustomWorldEntityEditorModal.tsx` | `432` | `鍙栨秷` | 统一保存栏的取消按钮已写坏 |
|
||||
| 编辑器 UI | `src/components/CustomWorldEntityEditorModal.tsx` | `436` | `淇濆瓨淇敼` | 统一保存栏的主按钮文案已写坏 |
|
||||
|
||||
## 二、游戏 UI 中的英文残留
|
||||
|
||||
| 文件 | 行号 | 当前文本 / 值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/AdventurePanel.tsx` | `363` | `Currency` | 任务奖励卡的货币标题仍是英文 |
|
||||
| `src/components/AdventurePanel.tsx` | `371` | `No item bounty attached to this quest.` | 任务奖励空态文案仍是英文 |
|
||||
| `src/components/AdventurePanel.tsx` | `1424-1428` | `LOOT CACHE`、`Tap an item icon to inspect its details.`、`No usable loot dropped this time, but the battle is still settled.` | 战利品弹层标题和说明仍是整句英文 |
|
||||
| `src/components/AdventurePanel.tsx` | `1442` | `No loot dropped this time.` | 战利品列表空态文案仍是英文 |
|
||||
| `src/components/AdventurePanel.tsx` | `1352`, `1524` | `x{item.quantity}`、`HP` / `MP` | 数量展示与效果预览仍保留英文缩写 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `892-899` | `label="HP"`、`label="MP"` | 同行状态估计卡仍使用英文缩写 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `1073`, `1111`, `1428` | `NPC 信息`、`敌对NPC` / `NPC`、`NPC 背包` | NPC 详情区仍是中英混排 |
|
||||
| `src/components/CompanionCampModal.tsx` | `177-178`, `233-234`, `255` | `HP`、`MP`、`NPC` | 营地卡片和空态提示仍保留英文缩写 |
|
||||
| `src/components/NpcModals.tsx` | `79`, `252`, `273`, `356`, `408` | `x{item.quantity}`、`NPC 商品列表`、`这个 NPC 当前没有可售商品。`、`NPC 商品`、`HP` / `MP` | 交易弹窗、详情弹窗和数量角标存在中英混排 |
|
||||
| `src/components/CharacterDetailModal.tsx` | `117` | `数量 x{item.quantity}` | 角色详情中的数量前缀仍保留英文 `x` |
|
||||
|
||||
## 三、编辑器 UI 中的英文残留
|
||||
|
||||
| 文件 | 行号 | 当前文本 / 值 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/ItemCatalogEditor.tsx` | `576-581`, `621-624` | `fieldcraft`、`breaker`、`mana`、`boots`、`dust`、`crystal`、`gem` 等原值 | 物品标签、构筑角色、部件名和协同信息会直接显示英文原值 |
|
||||
| `src/components/ItemCatalogEditor.tsx` | `648`, `671`, `729-783`, `800` | `HP` / `MP` / `CD`、`物品 ID`、`使用 Build Buff`、`套装 ID` | 物品编辑器预览与字段标签仍有英文缩写 / 术语 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `818-821`, `885`, `915` | `HP`、`No visible target`、`n/a` | 选项行为预览面板仍有英文缩写和英文空态 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1060-1064` | `Failed to save option behavior overrides`、`Option behavior overrides saved.` | 保存反馈仍是英文 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1106`, `1185`, `1191` | `battle/idle`、`AnimationState` 的原始动作值、`idle/move/attack` | 状态和动作枚举值直接显示为英文 |
|
||||
| `src/components/PresetEditor.tsx` | `88`, `90`, `1474-1501`, `1806-1814` | `NPC`、`敌对 NPC`、`NPC ID`、`关联角色 ID`、`敌对资源 ID`、`连接场景 ID` | 多个标签和页签仍保留英文缩写 / `ID` |
|
||||
| `src/components/PresetEditor.tsx` | `101-106`, `896-913`, `2008-2018` | `idle/move/attack/die`、`steady/burst/mobility/finisher/projectile` | 角色技能和敌对资源预览会直接显示英文枚举值 |
|
||||
| `src/components/PresetEditor.tsx` | `945`, `2155`, `2192` | `Build Buff`、`FPS` | 技能编辑和动作图集配置仍有英文术语 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `416-461` | `Failed to load NPC visual overrides`、`Failed to load NPC layout config`、`using bundled defaults` | NPC 视觉编辑器的加载失败提示仍是英文 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `678`, `718` | `Saved NPC visual overrides to ...`、`Saved shared NPC layout config.` | 保存成功反馈仍是英文 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `903`, `906`, `919`, `1226` | `NPC 视觉编辑器`、`当前 NPC`、`x ... / y ...` | 标题、字段标签与坐标信息仍存在中英混排 |
|
||||
|
||||
## 四、预设 / 数据层中会透到 UI 的英文值
|
||||
|
||||
| 文件 | 行号 | 当前文本 / 值 | 透出路径 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/data/monsterPresets.ts` | `494-512`, `522-540`, `647-652`, `718-723` | `Armor`、`Relic`、`Material`、`Consumable`、`Carapace Plate`、`Guard Core`、`Spore Pouch`、`Burst Cap`、`Ashfire Feather`、英文描述句子、`rare/uncommon` | 会进入怪物掉落、战利品详情、交易弹窗和物品预览 |
|
||||
| `src/data/itemDesign.ts` | `56-57`, `67-68`, `123-149` | `worldAffinity: neutral/wuxia/xianxia`、`role: fieldcraft/breaker/caster/berserker/assassin`、`tags` 中的 `caster/mana/burst/assassin` | 会透到 `ItemCatalogEditor` 的世界、角色定位、标签和协同信息 |
|
||||
| `src/data/itemDesign.ts` | `213-219`, `538-545`, `589-604`, `731-762`, `820-834`, `906-918` | `pieceName: boots/chest/gloves/...`、`build`、`setId`、`role`、`dust/crystal/gem` 等 | 会透到物品编辑器、套装信息和部件信息展示 |
|
||||
| `src/data/characterPresets.ts` | `54-69` | `blunt/wary/dry/direct/fragmented` | 对话风格与性格归类原值会被编辑器直接显示 |
|
||||
| `src/data/characterPresets.ts` | `368-379`, `525-526`, `839-850`, `1024-1025` | `Double Jump`、`jump attack`、`Wall Slide` | 角色动作目录 / 前缀原值会被 `PresetEditor` 直接显示 |
|
||||
| `src/data/characterPresets.ts` | `384-386`, `541-543`, `855-857`, `1045` | `guardStyle` / `warmStyle` / `truthStyle` 对应的英文原值 | 角色预设风格字段在编辑器中仍会显示英文 |
|
||||
|
||||
## 五、未计入项
|
||||
|
||||
- `src/components/NpcVisualEditor.tsx:681-683`、`721-722` 的乱码字符串位于块注释内,不会进入当前界面,因此未计入本次“活跃问题”。
|
||||
- `docs/*.md` 里的历史审计文档和旧清单不在本次范围内,本次只统计游戏 UI、预设和编辑器 UI。
|
||||
|
||||
## 六、建议处理顺序
|
||||
|
||||
1. 先修 `src/components/GameShell.tsx` 和 `src/components/CustomWorldEntityEditorModal.tsx` 的真实乱码,因为它们已经直接出现在主流程界面。
|
||||
2. 再清理 `src/components/AdventurePanel.tsx` 的英文空态、战利品标题和 `Currency`,这是玩家最容易直接看到的一批英文。
|
||||
3. 然后统一编辑器术语映射,优先处理 `HP` / `MP` / `NPC` / `ID` / `FPS` / `Build Buff` / `AnimationState`。
|
||||
4. 最后为 `src/data/itemDesign.ts`、`src/data/monsterPresets.ts`、`src/data/characterPresets.ts` 增加显示层映射,避免原始英文值继续直接透到编辑器和运行时界面。
|
||||
@@ -0,0 +1,276 @@
|
||||
# 游戏 UI / 预设 / 编辑器 / npcInteraction / prompt 文本深度审计
|
||||
|
||||
日期:`2026-04-02`
|
||||
|
||||
## 说明
|
||||
|
||||
- 本文档基于当前仓库源码再次深搜,不直接沿用旧审计结论。
|
||||
- 审计目标:找出“可能出现在游戏 UI、预设、编辑器 UI、生成链路中的文本”里,仍然存在的:
|
||||
- `中文乱码`
|
||||
- `英文直出`
|
||||
- `中英混用`
|
||||
- 本轮重点加查:
|
||||
- `src/data/npcInteractions.ts`
|
||||
- `src/services/prompt.ts`
|
||||
- `src/services/characterChatPrompt.ts`
|
||||
- `src/services/questPrompt.ts`
|
||||
- 说明口径:
|
||||
- “会显示给玩家/编辑器使用者”的文本,按高优先级记录。
|
||||
- “内部英文枚举/键名,但可能泄露到 prompt、编辑器或预览”的内容,也单独记录。
|
||||
- 已经转成中文且当前复查无明显问题的区域,会标记为“复查通过”。
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 当前最严重的文本污染源不是单一 UI,而是两条链路同时存在问题:
|
||||
- 运行时弹窗 / 实体详情 / NPC 交易招募弹窗
|
||||
- `prompt` 生成链路
|
||||
- `src/services/prompt.ts` 是当前最重灾区,既有大面积中文乱码,也混有英文结构词,会直接影响 AI 生成质量。
|
||||
- `src/data/npcInteractions.ts` 里“话题 actionText / detailText”大体已是正常中文,但商人来源匹配、库存种子物品类别/名称仍有明显乱码,而且阶段枚举仍是英文值。
|
||||
- 预设编辑器仍然是英文和乱码高密度区,尤其是:
|
||||
- `CharacterPresetPanel.tsx`
|
||||
- `MonsterPresetPanel.tsx`
|
||||
- `SceneNpcPresetPanel.tsx`
|
||||
- `ScenePresetPanel.tsx`
|
||||
- `StateFunctionEditor.tsx`
|
||||
- 自定义世界 NPC/场景编辑器、NPC 视觉编辑器大体已转中文,但还残留 `NPC`、`AI`、`Shift` 等中英混用。
|
||||
|
||||
## 一、游戏主流程 UI 与运行时面板
|
||||
|
||||
### 1.1 复查通过
|
||||
|
||||
| 文件 | 行号 | 当前状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `47-49` | 通过 | 开场联系方式已改成 `QQ群`、`微信` |
|
||||
| `src/components/game-shell/GameShellRuntime.tsx` | `147` | 通过 | 场景名兜底已改成 `当前区域` |
|
||||
| `src/components/adventure-panel/AdventurePanelOverlays.tsx` | `137-158` | 通过 | 任务目标眉标、宝藏踪迹、切磋会话等主文案已是中文 |
|
||||
|
||||
### 1.2 仍有问题
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `81`、`89` | 中英混用 | `正在生成核心NPC...` | `NPC` 仍在中文进度文案里直出 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `151` | 中文乱码 | `闃熶紞` / `鑳屽寘` | 队伍/背包弹层标题已写坏 |
|
||||
| `src/components/game-shell/GameShellRuntime.tsx` | `200-201` | 英文直出 | `GENARRATIVE` | 运行时头部品牌字样仍为英文 |
|
||||
| `src/components/adventure-panel/AdventurePanelOverlays.tsx` | `140` | 中英混用 | `未知敌对 NPC` | 已非乱码,但仍混入 `NPC` |
|
||||
|
||||
## 二、实体详情、NPC 交互弹窗、交易/招募 UI
|
||||
|
||||
### 2.1 `AdventureEntityModal.tsx`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/AdventureEntityModal.tsx` | `111` | 中文乱码 | `鏁屽 NPC` | hostile badge 已坏,同时混入 `NPC` |
|
||||
| `src/components/AdventureEntityModal.tsx` | `113`、`137-146` | 英文直出 | `NPC`、`Player`、`Companion` | 标题/副标题兜底值仍为英文 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `221`、`264`、`272`、`283`、`296`、`306` | 英文直出 | `Portrait`、`Status`、`Relation`、`Attributes`、`Encounter`、`Inventory` | 六个主区块标题都未本地化 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `266-267` | 英文直出 | `HP`、`MP` | 状态条标签未本地化 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `274-275` | 英文直出 | `Affinity`、`Recruited: Yes/No` | 关系区块仍是英文 |
|
||||
| `src/components/AdventureEntityModal.tsx` | `298-301` | 英文直出 | `Name`、`Context`、`Type`、`Battle mode` | 遭遇信息区块仍是英文 |
|
||||
|
||||
### 2.2 `NpcModals.tsx`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/NpcModals.tsx` | `251-252` | 英文直出 | `NPC inventory` / `Your inventory` / `items` | 交易栏标题和数量单位仍是英文 |
|
||||
| `src/components/NpcModals.tsx` | `272` | 英文直出 | `This NPC has nothing to sell right now.` / `You have nothing to sell right now.` | 空状态未本地化 |
|
||||
| `src/components/NpcModals.tsx` | `290` | 英文直出 | `Purchase total` / `Sale total` | 交易总价标题未本地化 |
|
||||
| `src/components/NpcModals.tsx` | `297` | 中文乱码 | 大段价格不足提示 | 购买资金不足提示整段已坏 |
|
||||
| `src/components/NpcModals.tsx` | `303` | 中文乱码 | 大段默认说明 | 未选中物品时的说明区整段已坏 |
|
||||
| `src/components/NpcModals.tsx` | `350-399` | 英文直出 | `Item details`、`NPC item`、`Stock`、`Value`、`Purchase price`、`Buyback price`、`Slot`、`Not equippable`、`Usable immediately`、`Tags`、`None` | 物品详情弹窗几乎整屏英文 |
|
||||
| `src/components/NpcModals.tsx` | `404` | 中文乱码 + 中英混用 | 含 `MP` 的恢复说明 | 物品使用效果提示已坏 |
|
||||
| `src/components/NpcModals.tsx` | `465` | 中文乱码 | 好感变动提示 | 交易结果反馈文案已坏 |
|
||||
| `src/components/NpcModals.tsx` | `500-501` | 英文直出 | `Manage companion slot`、`Select a current companion to rotate out before recruiting this NPC.` | 招募替换同伴弹窗未本地化 |
|
||||
|
||||
## 三、预设编辑器与编辑器 UI
|
||||
|
||||
### 3.1 复查通过或基本通过
|
||||
|
||||
| 文件 | 行号 | 当前状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `242` | 通过 | 图片路径占位文案已中文化,不再暴露 `URL` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `463` | 基本通过 | 空状态是中文,但仍混入 `NPC` |
|
||||
| `src/components/CustomWorldNpcVisualEditor.tsx` | `88-91`、`552-765` | 基本通过 | 主体编辑项、装备类型、素材、姿态等基本都为中文 |
|
||||
|
||||
### 3.2 标签、枚举、面板标题问题
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/shared.ts` | `43` | 英文直出 | `NPC` | 预设编辑器顶层页签仍用英文 |
|
||||
| `src/components/preset-editor/shared.ts` | `62-67` | 英文枚举外露 | `idle`、`move`、`attack`、`die` | 怪物动画候选仍是英文值 |
|
||||
| `src/components/preset-editor/shared.ts` | `69-74` | 英文枚举外露 | `steady`、`burst`、`mobility`、`finisher`、`projectile` | 技能风格候选仍是英文值 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `702-708` | 中英混用 | `NPC 视觉编辑器` | 中文标题中混入 `NPC` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `718` | 中英混用 | `当前 NPC` | 选择器标签混入 `NPC` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `976-977` | 中英混用 | `拖动标记微调 NPC 的预览布局。移动时按住 Shift...` | 混入 `NPC`、`Shift` |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `482-483` | 中英混用 | `AI生成NPC形象`、`NPC 形象 AI 生成功能仍在开发中。` | 模态标题与副标题仍大量混用英文术语 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `635-636` | 中英混用 | `新增 NPC`、`编辑 NPC` | 主标题和副标题混入 `NPC` |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `734`、`762-763` | 中英混用 | `AI生成`、`AI生成场景`、`场景图片 AI 生成功能仍在开发中。` | 场景生成入口仍混入 `AI` |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `792-793` | 中英混用 | `npc-...`、`自定义NPC...` | 新建 NPC 默认 ID/名称混有英文前缀与 `NPC` |
|
||||
|
||||
### 3.3 `CharacterPresetPanel.tsx`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `262`、`264`、`277` | 中文乱码 | `瑙掕壊`、`淇濆瓨瑙掕壊瑕嗙洊` | 选择卡标题、下拉标签、保存按钮均已坏 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `320`、`326` | 中文乱码 | `鍔ㄧ敾`、`闁煎啿...` | 动画/世界标签已坏 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `418`、`427` | 中文乱码 | `涓栫晫`、`棰勮鎬墿` | 技能预览区字段标签已坏 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `313-314`、`402-403` | 通过 | `角色详情`、`技能预览` | 主体段落标题已是正常中文 |
|
||||
|
||||
### 3.4 `MonsterPresetPanel.tsx`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `49-50` | 英文直出 | `Saved monster overrides...`、`Failed to save monster overrides.` | 保存反馈未本地化 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `58-59` | 中文乱码 | 空状态整段提示 | 无怪物时的空态文案已坏 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `124-126` | 英文直出 | `Section`、`Editor section.`、`Field` | 左侧选择卡是占位英文 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `135` | 中文乱码 | `闂?` | 世界与名字之间的分隔符已坏 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `139` | 英文直出 | `Save Monster Overrides` | 保存按钮未本地化 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `158-159` | 英文直出 | `Monster Override Preview`、`Editor section.` | 右侧预览卡标题/说明未本地化 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `165`、`218`、`223`、`228`、`240`、`248`、`256` | 英文直出 | `Field`、`Name`、`Intro Action` | 多个字段标签仍是英文或占位文案 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `335` | 中文乱码 | 一段动画配置标签乱码 | 帧数相关字段标题已坏 |
|
||||
|
||||
### 3.5 `SceneNpcPresetPanel.tsx`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `159` | 英文直出 | `No NPC presets are available.` | 空状态未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `177-193` | 英文直出 | `NPC Library`、`Browse and select an NPC preset.`、`NPC ID`、`Save NPC Overrides` | 选择区整块未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `221-238` | 英文直出 | `Skill Preview`、`Skill`、`World` | 技能预览区未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `258-259` | 中文乱码 | 空状态/说明整段乱码 | 预览区已有损坏文本 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `264-268` | 英文直出 | `Visual Preview`、`Hostile NPCs use monster presets...`、`Narrative NPCs can preview...` | 视觉预览说明未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `310-357` | 英文直出 | `NPC Details`、`NPC ID`、`Name`、`Role`、`Avatar`、`Linked Character ID`、`Monster Preset ID`、`Initial Affinity`、`Description` | 详情字段整体未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `363` | 中文乱码 | 预览说明大段乱码 | NPC 预览描述区已坏 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `371-375` | 英文直出 | `Visual Editor`、`Hostile NPCs cannot use the visual editor...` | 可视编辑器说明未本地化 |
|
||||
|
||||
### 3.6 `ScenePresetPanel.tsx`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `105-108` | 英文直出 | `Treasure Ahead`、`Treasure` | 宝藏预览实体名/头像仍是英文 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `132-133` | 英文直出 | `Scene Library`、`Browse and select a scene preset.` | 左侧选择区未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `162` | 英文直出 | `Save` | 保存按钮未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `170-181` | 英文直出 | `Scene Preview`、`Preview Mode`、`Monster Preview`、`NPC Preview`、`Treasure Preview`、`Empty` | 预览模式整组未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `207-226` | 英文直出 | `Hostile NPCs`、`NPCs`、`Treasure Hint`、`None` | 场景摘要卡未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `233-299` | 英文直出 | `Scene Details`、`Scene ID`、`World`、`Name`、`Description`、`Image Source`、`Forward Scene`、`Unset`、`Connected Scene IDs`、`Monster IDs`、`Treasure Hints`、`NPCs In Scene` | 详情编辑区整块未本地化 |
|
||||
|
||||
### 3.7 `StateFunctionEditor.tsx` 与共享请求层
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1060-1064` | 英文直出 | `Failed to save option behavior overrides`、`Option behavior overrides saved.` | 保存结果提示未本地化 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1138` | 中英混用 | `GameState` | 预览说明里仍混入英文类型名 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1143` | 中英混用 | `敌对NPC资源` | `NPC` 混入字段标签 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1185` | 英文枚举外露 | `AnimationState` 原始值 | 玩家动作下拉会直接显示英文枚举值 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1190` | 英文占位符外露 | `{monster}` | 占位提示面向编辑器用户直接可见 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1191` | 英文枚举外露 | `idle`、`move`、`attack` | 怪物动画下拉仍使用英文值 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1213-1217` | 基本通过 | `稳扎稳打`、`爆发`、`机动` 等 | 技能权重显示标签已是中文,但底层 key 仍是英文 |
|
||||
| `src/editor/shared/jsonClient.ts` | `29`、`43` | 英文直出 | `Request failed`、`Save failed` | 通用网络错误兜底未本地化,所有编辑器接口都可能透出 |
|
||||
|
||||
## 四、`npcInteractions.ts` 重点复查
|
||||
|
||||
### 4.1 复查通过
|
||||
|
||||
| 文件 | 行号 | 当前状态 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/data/npcInteractions.ts` | `507-657` | 通过 | `actionText` / `detailText` 话题库主体已是中文,适合作为后续清理基线 |
|
||||
|
||||
### 4.2 仍有问题
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/data/npcInteractions.ts` | `299-343` | 中文乱码 | `绋€鏈夊搧`、`鏉愭枡`、`琛屽晢鎶ょ`、`绮剧偧閾?`、`闃叉按琛屽泭`、`娌抽浘缃楃洏`、`鍏界毊`、`鐚庨拱缇藉潬`、`鎷撴湰鏂囧唽`、`娈嬬己鍦板浘`、`姝﹀櫒`、`鍒跺紡浣╁垁`、`鎶ょ敳`、`鎶よ噦`、`鏃у竷鍗?`、`闅忚韩鏃х墿` | 商人来源匹配、商品分类、商品名存在明显乱码,会直接流入交易 UI |
|
||||
| `src/data/npcInteractions.ts` | `401-424` | 英文枚举外露 | `deep`、`honest`、`partial`、`guarded`、`warm`、`cooperative`、`neutral`、`distant`、`candid`、`true_but_incomplete`、`half_truth`、`situational_only` | 阶段/回答模式仍使用英文值,虽不一定直接给玩家看,但已进入 prompt 上下文 |
|
||||
| `src/data/npcInteractions.ts` | `428-500` | 基本通过 | `已经愿意逐步谈到真实来历...` 等 | 描述层已是中文,可作为未来替换英文枚举时的文案来源 |
|
||||
| `src/data/npcInteractions.ts` | `1199`、`1209`、`1243` | 中英混用 | `向该NPC送礼`、`邀请该NPC加入队伍`、`离开当前 NPC,重新回到探索状态。` | 选项和说明里仍混入 `NPC` |
|
||||
| `src/data/npcInteractions.ts` | `1250` | 中英混用 | `当前好感为 ...` 同句包含 `NPC` | 遭遇总结文本仍有术语混入 |
|
||||
|
||||
## 五、`prompt` 链路重点复查
|
||||
|
||||
### 5.1 `src/services/prompt.ts`
|
||||
|
||||
这是当前最需要优先清理的文件。问题不是一两处,而是“结构性污染”:
|
||||
|
||||
- 前段中文描述已混入乱码。
|
||||
- 中段人物/遭遇/场景/状态描述大面积乱码。
|
||||
- 后段选项约束、战斗/观察/营地对话指令大面积乱码。
|
||||
- 同时混有 `functionId`、`actionText`、`npc|treasure|none` 等英文结构词。
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/services/prompt.ts` | `145`、`151` | 中文乱码 | `锛堣嚜瀹氫箟...`、`鑷畾涔変笘鐣?...` | 自定义世界描述区已坏 |
|
||||
| `src/services/prompt.ts` | `170-171`、`198-216` | 中文乱码 | 冒险开场理由、表层钩子、眼前顾虑、目标等整段乱码 | 角色开场信息会直接污染生成上下文 |
|
||||
| `src/services/prompt.ts` | `226-233` | 中文乱码 + 英文枚举外露 | `NPC`、`deep`、`warm`、`answerMode` 等 | NPC 会话阶段控制段同时存在乱码和英文枚举 |
|
||||
| `src/services/prompt.ts` | `378-451` | 中文乱码 + 中英混用 | 遭遇实体、敌对 `NPC`、玩家状态、场景说明等整段乱码 | 玩家、NPC、怪物、场景等核心提示都已受污染 |
|
||||
| `src/services/prompt.ts` | `547-568` | 中文乱码 + 英文结构词外露 | `functionId`、`actionText`、`function` | 选项约束与动作重写规则段落已坏 |
|
||||
| `src/services/prompt.ts` | `859-910` | 中文乱码 + 中英混用 | 空闲/遭遇函数说明、观察线索、开场营地跟进等整段乱码 | 后半段生成规则几乎不可维护 |
|
||||
|
||||
### 5.2 `src/services/characterChatPrompt.ts`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/services/characterChatPrompt.ts` | `42-54` | 英文直出 | `You are a companion character...`、`Generate exactly 3 player reply suggestions.`、`Summarize the evolving relationship...` | 系统 prompt 整段为英文 |
|
||||
| `src/services/characterChatPrompt.ts` | `57-59` | 英文直出 | `Wuxia`、`Xianxia`、`Custom World` | 世界描述仍为英文 |
|
||||
| `src/services/characterChatPrompt.ts` | `64`、`69-71`、`74-75` | 英文直出 | `Custom world reference`、`female`、`male`、`unknown`、`left`、`right` | 性别、朝向、扩展说明均为英文 |
|
||||
| `src/services/characterChatPrompt.ts` | `99-112` | 英文直出 | `Recent story: none.`、`Earlier story summary`、`Most recent 3 story rounds` | 历史摘要模板为英文 |
|
||||
| `src/services/characterChatPrompt.ts` | `123-155` | 英文直出 | `damage`、`mana`、`cooldown`、`arrival reason`、`current goal`、`world schema`、`top attributes` | 角色信息拼接模板整体为英文 |
|
||||
|
||||
### 5.3 `src/services/questPrompt.ts`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/services/questPrompt.ts` | `28-31` | 英文直出 | `issued by`、`No live quests` | 当前任务摘要模板为英文 |
|
||||
| `src/services/questPrompt.ts` | `35-37` | 英文直出 | `Active companions`、`Roster companions` | 同伴摘要模板为英文 |
|
||||
| `src/services/questPrompt.ts` | `41-52` | 英文直出 | `Player`、`HP`、`Mana`、`Inventory snapshot` | 玩家状态模板为英文 |
|
||||
| `src/services/questPrompt.ts` | `67-95` | 英文直出 | `You are the quest director...` 等整段 | 任务意图系统 prompt 全英文 |
|
||||
| `src/services/questPrompt.ts` | `107-123` | 英文直出 | `World`、`Issuer NPC`、`Encounter kind`、`Recent story moments` 等 | 最终拼接 prompt 仍是英文框架 |
|
||||
|
||||
### 5.4 `src/services/aiFallbacks.ts`
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/services/aiFallbacks.ts` | `5-17` | 英文直出 | `Player: ...`、`That question is not casual...`、`I accept...` | 离线 NPC 对话与招募兜底仍是英文 |
|
||||
| `src/services/aiFallbacks.ts` | `28-35` | 英文直出 | `I will answer in my own way`、`I still remember...` | 离线角色私聊回复兜底为英文 |
|
||||
| `src/services/aiFallbacks.ts` | `40-42` | 英文直出 | 三条候选回复 | 私聊建议兜底为英文 |
|
||||
| `src/services/aiFallbacks.ts` | `52-57` | 英文直出 | `Player`、`Recent exchange`、`warmer toward the player` | 私聊摘要兜底为英文 |
|
||||
|
||||
## 六、其他会外溢到 UI / 预览 / 生成链路的文本源
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/hooks/useStoryGeneration.ts` | `578-580` | 通过 | `前往...`、`离开营地...` | 旅行选项已中文化 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `599`、`602` | 英文直出 | `approaches from a short distance away...`、`steps into view...` | 初始同伴结果文本仍是英文 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `814` | 英文直出 | `Continue the camp exchange and organize the most natural next actions.` | 营地跟进生成指令仍是英文 |
|
||||
| `src/data/scenePresets.ts` | `195`、`297` | 英文枚举外露 | `trade`、`fight`、`spar`、`help`、`chat`、`recruit`、`gift` | 场景函数列表仍是英文 ID |
|
||||
| `src/data/stateFunctions.ts` | `33` | 英文枚举外露 | `idle|move|attack` | 怪物动画枚举仍是英文 |
|
||||
| `src/data/stateFunctions.ts` | `130`、`152`、`174`、`196`、`221`、`244` | 英文枚举外露 | `steady`、`burst`、`mobility`、`finisher`、`projectile` | 技能权重 key 仍为英文 |
|
||||
| `src/data/characterPresets.ts` | `60-76` | 英文枚举外露 | `blunt`、`wary`、`evasive`、`measured`、`gentle`、`teasing`、`dry`、`steady`、`direct`、`fragmented`、`deflecting` | 对话风格推断值仍为英文,已进入 `npcInteractions` / `prompt` 描述链路 |
|
||||
|
||||
## 七、优先级建议
|
||||
|
||||
### P0:先修,否则会持续污染生成结果或直接破坏主界面
|
||||
|
||||
1. `src/services/prompt.ts`
|
||||
2. `src/components/NpcModals.tsx`
|
||||
3. `src/components/AdventureEntityModal.tsx`
|
||||
4. `src/data/npcInteractions.ts` 的商店库存种子乱码段
|
||||
5. `src/components/game-shell/GameShellOverlays.tsx` 的标题乱码
|
||||
|
||||
### P1:紧接着修,编辑器体验当前已经明显受损
|
||||
|
||||
1. `src/components/preset-editor/MonsterPresetPanel.tsx`
|
||||
2. `src/components/preset-editor/SceneNpcPresetPanel.tsx`
|
||||
3. `src/components/preset-editor/ScenePresetPanel.tsx`
|
||||
4. `src/components/preset-editor/CharacterPresetPanel.tsx`
|
||||
5. `src/components/StateFunctionEditor.tsx`
|
||||
6. `src/editor/shared/jsonClient.ts`
|
||||
|
||||
### P2:统一术语与风格,减少中英混用
|
||||
|
||||
1. 全局统一 `NPC`、`HP`、`MP`、`AI`、`Shift` 是否保留英文缩写
|
||||
2. 将 `npcInteractions.ts` / `characterPresets.ts` / `stateFunctions.ts` 的英文枚举与键名整理为“内部值 + 中文展示层”
|
||||
3. 补齐 `aiFallbacks.ts`、`characterChatPrompt.ts`、`questPrompt.ts` 的中文 prompt / fallback 版本
|
||||
|
||||
## 八、建议的修复顺序
|
||||
|
||||
1. 先修 `prompt` 和 `npcInteractions`,因为这两处会同时污染 AI 输出和运行时文本。
|
||||
2. 再修 `NpcModals`、`AdventureEntityModal`、`GameShellOverlays`,优先恢复玩家可见界面。
|
||||
3. 再批量处理四个预设编辑器面板和 `StateFunctionEditor`,最后统一共享请求层兜底文案。
|
||||
|
||||
280
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-03-30_CONTINUED.md
Normal file
280
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-03-30_CONTINUED.md
Normal file
@@ -0,0 +1,280 @@
|
||||
# 游戏 UI / 预设实体 / 编辑器 UI 英文与乱码复核(续)
|
||||
|
||||
日期:`2026-03-30`
|
||||
|
||||
## 说明
|
||||
|
||||
- 这份文档是对当前分支的重新复核,不直接沿用旧审计文档的正文,因为旧文档本身已经存在较明显乱码。
|
||||
- 本轮重点覆盖三类范围:
|
||||
- 游戏运行时 UI:`src/components/` 下实际会进入主流程的界面,以及 `src/components/game-shell/`
|
||||
- 编辑器 UI:`src/components/*Editor*.tsx`、`src/components/preset-editor/`、`src/editor/shared/`
|
||||
- 预设实体 / 数据层:`src/data/` 中会被编辑器、预览面板或游戏详情页直接透出的文本
|
||||
- 复核方式:
|
||||
- 直接按 UTF-8 读取源码,避免把终端显示问题误判成源码乱码
|
||||
- 只记录会显示在玩家或编辑器使用者面前的文本
|
||||
- `import`、类型名、变量名、接口字段名、纯内部注释默认不计入
|
||||
- 但保存 / 加载提示这类虽然来自 helper 文件、最终会显示到 UI 的字符串,仍计入
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 当前分支里,真正“源码里已经写坏”的中文乱码,主要集中在 4 个位置:
|
||||
- `src/components/GameShell.tsx`
|
||||
- `src/components/preset-editor/shared.ts`
|
||||
- `src/components/CustomWorldEntityEditorModal.tsx`
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx`
|
||||
- 其中最严重的是 `src/components/preset-editor/PresetEditorPanels.tsx`:
|
||||
- 角色/NPC/场景/敌对 NPC 资源四个子面板里都有残缺字符串
|
||||
- 同时混有 `NPC`、`ID`、`FPS`、`Build Buff`、`Medieval NPC` 等英文术语
|
||||
- 数据层 `src/data/` 本轮没有再扫到新的中文乱码;问题更多是英文预设值直接透到编辑器 / 预览 UI。
|
||||
- 游戏运行时 UI 侧已经比旧清单干净很多,但仍有几块明显英文残留:
|
||||
- `AdventurePanel`
|
||||
- `AdventureEntityModal`
|
||||
- `CompanionCampModal`
|
||||
- `NpcModals`
|
||||
- `game-shell/CharacterSelectionFlow`
|
||||
|
||||
## 一、已确认的真乱码
|
||||
|
||||
| 范围 | 文件 | 行号 | 当前文本示例 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 游戏 UI | `src/components/GameShell.tsx` | `565`, `578`, `591`, `710` | `瑙掕壊`、`鍐掗櫓`、`鑳屽寘`、`闃熶紞` | 主界面底部 tab 和浮层标题已写坏 |
|
||||
| 编辑器 UI | `src/components/preset-editor/shared.ts` | `42-55` | `瑙掕壊`、`鍦烘櫙`、`鐗╁搧`、`鏁屽 NPC`、`姝︿緺`、`浠欎緺`、`鑷畾涔変笘鐣?` | 新版预设编辑器 tab 与世界标签已写坏 |
|
||||
| 编辑器 UI | `src/components/CustomWorldEntityEditorModal.tsx` | `383`, `429`, `433` | `宸查€?`、`鍙栨秷`、`淇濆瓨淇敼` | 自定义世界实体编辑弹窗里的已选中/取消/保存文案乱码 |
|
||||
| 编辑器 UI | `src/components/preset-editor/PresetEditorPanels.tsx` | `251`, `530`, `1383`, `1468`, `1478`, `1830` 等多处 | `鏂版妧鑳?`、`鏂板鎶€鑳?`、`绾満鏅?`、`鑳屾櫙鍥捐矾寰?`、`涓嶈缃?`、`... FPS銆?` | 新版预设编辑器存在大面积残缺字符串,部分已经带 `?` 结尾 |
|
||||
|
||||
### `PresetEditorPanels.tsx` 乱码分布
|
||||
|
||||
- 角色预设区:
|
||||
- `251`, `310`, `323`, `379`, `467-688`, `719-802`
|
||||
- 示例:`新技<E696B0>?`、`预览技<E8A788>?`、`法力消<E58A9B>?`、`属性面<E680A7>?`、`主场<E4B8BB>?`
|
||||
- NPC 预设区:
|
||||
- `1000-1208`
|
||||
- 示例:`这里汇总了场景里的所<E79A84>?NPC 角色预设<E9A284>?`、`如果<E5A682>?NPC 绑定了角色技能...<2E>?`、`敌对 NPC 会沿用战斗资源预设展示...<2E>?`
|
||||
- 场景预设区:
|
||||
- `1244-1478`
|
||||
- 示例:`没有可编辑的场景预设<E9A284>?`、`敌<>?NPC`、`纯场<E7BAAF>?`、`背景图路<E59BBE>?`、`不设<E4B88D>?`
|
||||
- 敌对 NPC 资源区:
|
||||
- `1551-1851`
|
||||
- 示例:`没有可编辑的敌对 NPC 资源<E8B584>?`、`基础数<E7A180>?`、`最大生<E5A4A7>?`、`... 和 FPS<50>?`、`起始<E8B5B7>?`
|
||||
|
||||
## 二、游戏 UI 中仍会显示的英文
|
||||
|
||||
### 1. 主冒险面板
|
||||
|
||||
- `src/components/AdventurePanel.tsx:363`
|
||||
- `Currency`
|
||||
- `src/components/AdventurePanel.tsx:371`
|
||||
- `No item bounty attached to this quest.`
|
||||
- `src/components/AdventurePanel.tsx:1424`
|
||||
- `LOOT CACHE`
|
||||
- `src/components/AdventurePanel.tsx:1427-1428`
|
||||
- `Tap an item icon to inspect its details.`
|
||||
- `No usable loot dropped this time, but the battle is still settled.`
|
||||
- `src/components/AdventurePanel.tsx:1442`
|
||||
- `No loot dropped this time.`
|
||||
- `src/components/AdventurePanel.tsx:1524`
|
||||
- `HP` / `MP`
|
||||
|
||||
### 2. 实体详情与交互弹窗
|
||||
|
||||
- `src/components/AdventureEntityModal.tsx:1163-1165`
|
||||
- `x{item.quantity}`
|
||||
- `Inspect`
|
||||
- `src/components/AdventureEntityModal.tsx:1428`
|
||||
- `NPC 背包`
|
||||
- `src/components/CompanionCampModal.tsx:177-178`, `233-234`, `255`
|
||||
- `HP`
|
||||
- `MP`
|
||||
- `NPC`
|
||||
- `src/components/NpcModals.tsx:252`, `273`, `356`, `408`
|
||||
- `NPC 商品列表`
|
||||
- `这个 NPC 当前没有可售商品。`
|
||||
- `NPC 商品`
|
||||
- `HP` / `MP`
|
||||
|
||||
### 3. 开场选角流
|
||||
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx:28-32`
|
||||
- `Sword Princess`
|
||||
- `Royal Blade`
|
||||
- `Vanguard`
|
||||
- `Twin Blade Rogue`
|
||||
- `Assassin`
|
||||
- `Armored Spear`
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx:35-39`
|
||||
- `STR`
|
||||
- `AGI`
|
||||
- `INT`
|
||||
- `SPI`
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx:329-333`
|
||||
- `Character Stats`
|
||||
- `Gender:`
|
||||
|
||||
## 三、编辑器 UI 中仍会显示的英文
|
||||
|
||||
### 1. 旧预设编辑入口
|
||||
|
||||
- `src/components/PresetEditor.tsx:61-69`
|
||||
- `Preset Workshop`
|
||||
- `Unified Preset Preview And Editor`
|
||||
- `Manage character, NPC, scene, monster, item, and behavior presets from one editor shell. Each tab now loads its own container so the entry component stays small and focused.`
|
||||
|
||||
### 2. 新预设编辑器共享配置
|
||||
|
||||
- `src/components/preset-editor/shared.ts:60-72`
|
||||
- `idle`
|
||||
- `move`
|
||||
- `attack`
|
||||
- `die`
|
||||
- `steady`
|
||||
- `burst`
|
||||
- `mobility`
|
||||
- `finisher`
|
||||
- `projectile`
|
||||
|
||||
### 3. 新预设编辑器主面板
|
||||
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:620`
|
||||
- `Build Buff`
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:966`
|
||||
- `No NPC presets available.`
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:1100-1202`
|
||||
- `NPC`
|
||||
- `NPC ID`
|
||||
- `Medieval NPC`
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:1830`, `1867`
|
||||
- `FPS`
|
||||
|
||||
### 4. 物品编辑器
|
||||
|
||||
- `src/components/ItemCatalogEditor.tsx:648`
|
||||
- `HP`
|
||||
- `MP`
|
||||
- `CD`
|
||||
- `src/components/ItemCatalogEditor.tsx:783`
|
||||
- `Build Buff`
|
||||
- `src/components/ItemCatalogEditor.tsx:800`
|
||||
- `套装 ID`
|
||||
- `src/components/ItemCatalogEditor.tsx:576-585`, `793-800`
|
||||
- `selectedItem.tags`、`buildProfile.role`、`setId` 等原始英文值会直接显示在预览或输入框里
|
||||
|
||||
### 5. 选项行为编辑器
|
||||
|
||||
- `src/components/StateFunctionEditor.tsx:818`, `821`
|
||||
- `HP`
|
||||
- `No visible target`
|
||||
- `src/components/StateFunctionEditor.tsx:885`, `915`
|
||||
- `HP`
|
||||
- `n/a`
|
||||
- `src/components/StateFunctionEditor.tsx:1060-1064`
|
||||
- `Failed to save option behavior overrides`
|
||||
- `Option behavior overrides saved.`
|
||||
- `src/components/StateFunctionEditor.tsx:1185`
|
||||
- `AnimationState` 枚举值直接作为 label 显示
|
||||
- `src/components/StateFunctionEditor.tsx:1191`
|
||||
- `idle` / `move` / `attack`
|
||||
- `src/components/StateFunctionEditor.tsx:1217`
|
||||
- `steady` / `burst` / `mobility` / `finisher` / `projectile`
|
||||
|
||||
### 6. NPC 视觉编辑器与自定义世界编辑器
|
||||
|
||||
- `src/components/npcVisualEditorPersistence.ts:27-32`, `46-51`
|
||||
- `Failed to save NPC visual overrides`
|
||||
- `Saved NPC visual overrides to src/data/npcVisualOverrides.json.`
|
||||
- `Failed to save NPC layout config`
|
||||
- `Saved shared NPC layout config.`
|
||||
- `src/components/CustomWorldEntityCatalog.tsx:345`
|
||||
- `MedievalFantasyCharacters`
|
||||
- `src/components/CustomWorldEntityEditorModal.tsx:457`
|
||||
- `MedievalFantasyCharacters`
|
||||
|
||||
## 四、预设实体 / 数据层中会透到 UI 的英文值
|
||||
|
||||
### 1. 物品预设
|
||||
|
||||
- `src/data/itemDesign.ts:52-58`, `67-69`, `123-149`
|
||||
- `worldAffinity: "neutral" / "wuxia" / "xianxia"`
|
||||
- `role: "fieldcraft" / "breaker" / "caster" / "berserker" / "assassin"`
|
||||
- `rarity: "common" / "rare" / "epic"`
|
||||
- `tags: ["caster", "mana"]` 等
|
||||
- `src/data/itemDesign.ts:213-219`
|
||||
- `pieceName: "boots" / "chest" / "gloves" / "helm" / "leggings" / "shield" / "weapon"`
|
||||
- `src/data/itemDesign.ts:538-545`, `588-606`, `730-766`, `818-836`, `904-919`
|
||||
- 描述和 profile 中直接拼入 `build`、`role`、`dust`、`crystal`、`gem` 等英文值
|
||||
- 这些字段会在 `ItemCatalogEditor` 预览和构筑信息里直出
|
||||
|
||||
### 2. 敌对资源 / 掉落预设
|
||||
|
||||
- `src/data/monsterPresets.ts:494-540`, `647-723`
|
||||
- 掉落类别:`Armor`、`Relic`、`Material`、`Consumable`
|
||||
- 掉落名称:`Carapace Plate`、`Guard Core`、`Spore Pouch`、`Burst Cap`、`Ashfire Feather`、`Serpent Eye`、`Tide Ink`、`Lake Pearl`、`Thorn Nectar`
|
||||
- 掉落描述整句仍是英文
|
||||
- 这些条目会直接进入掉落预览、NPC 交易与物品详情
|
||||
|
||||
### 3. 角色预设
|
||||
|
||||
- `src/data/characterPresets.ts:53-70`
|
||||
- 对话风格值:`blunt`、`wary`、`evasive`、`measured`、`gentle`、`teasing`、`dry`、`steady`、`direct`、`fragmented`、`deflecting`
|
||||
- `src/data/characterPresets.ts:368-379`, `525-536`, `839-850`, `1024-1038`
|
||||
- 动画资源名:`Double Jump`、`jump attack`、`Wall Slide`、`skill1 bullet FX` 等
|
||||
- `src/data/characterPresets.ts:384-386`, `541-543`, `855-857`, `1043-1045`
|
||||
- `guardStyle` / `warmStyle` / `truthStyle` 的英文原值
|
||||
- 这些值会在角色预设编辑器与动作 / 风格下拉中透出
|
||||
|
||||
### 4. Build / 标签词典
|
||||
|
||||
- `src/data/buildTags.ts:42`, `56`, `91`, `126-147`, `309-316`
|
||||
- `assassin`
|
||||
- `fieldcraft`
|
||||
- `breaker`
|
||||
- `caster`
|
||||
- `armor`
|
||||
- `relic`
|
||||
- `material`
|
||||
- `consumable`
|
||||
- `rare`
|
||||
- `wuxia`
|
||||
- `xianxia`
|
||||
- `neutral`
|
||||
- 这些原始 tag 会通过物品标签、build profile 和编辑器预览进入显示层
|
||||
|
||||
## 五、本轮复核中未发现新增中文乱码的范围
|
||||
|
||||
### 游戏 UI
|
||||
|
||||
- `src/components/CharacterChatModal.tsx`
|
||||
- `src/components/CharacterDetailModal.tsx`
|
||||
- `src/components/CharacterPanel.tsx`
|
||||
- `src/components/MapModal.tsx`
|
||||
|
||||
说明:
|
||||
- 上述文件大体已中文化。
|
||||
- 仍可能存在少量英文缩写、内部 ID 或技术词,但本轮没有再发现新的明显中文乱码。
|
||||
|
||||
### 数据层
|
||||
|
||||
- `src/data/scenePresets.ts`
|
||||
- `src/data/npcInteractions.ts`
|
||||
- `src/data/treasureInteractions.ts`
|
||||
- `src/data/customWorldLibrary.ts`
|
||||
- `src/data/customWorldRuntime.ts`
|
||||
|
||||
说明:
|
||||
- 本轮在 `src/data/` 中没有扫到新的中文乱码。
|
||||
- 当前数据层问题主要是英文 tag、role、rarity、pieceName 等原始值会被上层编辑器直接显示。
|
||||
|
||||
## 六、建议优先级
|
||||
|
||||
1. 先修 `src/components/preset-editor/PresetEditorPanels.tsx`
|
||||
- 当前最集中的真乱码源
|
||||
- 已经影响角色 / NPC / 场景 / 敌对资源四个主编辑子页
|
||||
2. 再修 `src/components/preset-editor/shared.ts` 与 `src/components/GameShell.tsx`
|
||||
- 一个影响预设编辑入口 tab 与世界标签
|
||||
- 一个影响玩家主界面底部导航
|
||||
3. 然后处理 `src/components/CustomWorldEntityEditorModal.tsx`
|
||||
- 量不大,但按钮文案已经坏到影响操作判断
|
||||
4. 最后统一清英文术语
|
||||
- 游戏 UI:`AdventurePanel`、`AdventureEntityModal`、`CompanionCampModal`、`NpcModals`、`CharacterSelectionFlow`
|
||||
- 编辑器 UI:`PresetEditor.tsx`、`ItemCatalogEditor.tsx`、`StateFunctionEditor.tsx`、`npcVisualEditorPersistence.ts`
|
||||
- 数据层:`itemDesign.ts`、`monsterPresets.ts`、`characterPresets.ts`、`buildTags.ts`
|
||||
|
||||
194
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-03-31.md
Normal file
194
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-03-31.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# 游戏 UI / 预设 / 编辑器 UI 文案排查
|
||||
|
||||
日期:`2026-03-31`
|
||||
|
||||
## 说明
|
||||
|
||||
- 本文档基于当前分支源码重新复核,直接按 UTF-8 读取,不沿用旧审计文档中的乱码文本。
|
||||
- 只记录会出现在游戏 UI、预设编辑器 UI、结果页预览或保存反馈中的文本。
|
||||
- `import`、变量名、注释、仅内部使用的路径名,不计入本次问题清单。
|
||||
- 位图图片里的内嵌文本未做 OCR,本次只看源码层可见文案。
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 当前问题可以分成 3 类:
|
||||
- 真实中文乱码或截断。
|
||||
- 英文或英文缩写直接暴露在中文界面。
|
||||
- 预设数据中的英文原始值直接透出到编辑器或预览。
|
||||
- 乱码最集中的文件:
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx`
|
||||
- `src/components/NpcVisualEditor.tsx`
|
||||
- `src/components/CustomWorldEntityEditorModal.tsx`
|
||||
- `src/components/GameShell.tsx`
|
||||
- `src/editor/shared/FormFields.tsx`
|
||||
- 英文最集中的文件:
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx`
|
||||
- `src/components/game-shell/PreGameSelectionFlow.tsx`
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx`
|
||||
- `src/components/PresetEditor.tsx`
|
||||
- `src/components/ItemCatalogEditor.tsx`
|
||||
- `src/components/StateFunctionEditor.tsx`
|
||||
- 预设数据层仍有一批英文原始值会直接透出到 UI:
|
||||
- `src/data/itemDesign.ts`
|
||||
- `src/data/monsterPresets.ts`
|
||||
- `src/data/characterPresets.ts`
|
||||
- `src/data/buildTags.ts`
|
||||
|
||||
## 一、已确认的中文乱码 / 截断
|
||||
|
||||
| 范围 | 文件 | 行号 | 当前文本示例 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 游戏 UI | `src/components/GameShell.tsx` | `598`, `611`, `624` | `瑙掕壊` / `鍐掗櫓` / `鑳屽寘` | 主流程底部三个 tab 标签已写坏 |
|
||||
| 游戏 UI | `src/components/AdventurePanel.tsx` | `569-571` | `已完<E5B7B2>?` / `已交<E5B7B2>?` / `进行<E8BF9B>?` | 任务状态标签出现截断乱码 |
|
||||
| 游戏 UI | `src/components/CharacterDetailModal.tsx` | `223` | `属<>?` | 角色详情分区标题截断 |
|
||||
| 编辑器 UI | `src/components/CustomWorldEntityEditorModal.tsx` | `242`, `384`, `430`, `434` | `鏀寔...URL` / `宸查€?` / `鍙栨秷` / `淇濆瓨淇敼` | 自定义世界实体编辑弹窗的占位、选中态、取消和保存按钮已写坏 |
|
||||
| 编辑器 UI | `src/components/preset-editor/shared.ts` | `42-55` | `瑙掕壊` / `鍦烘櫙` / `鏁屽 NPC` / `姝︿緺` / `浠欎緺` / `鑷畾涔変笘鐣?` | 预设编辑器主 tab 和世界标签存在乱码 |
|
||||
| 编辑器 UI | `src/components/preset-editor/PresetEditorPanels.tsx` | `1269`, `1364`, `1371-1372`, `1467`, `1477-1486`, `1521`, `1654-1661`, `1689`, `1707` | 多处整句乱码 | 主编辑面板说明文案、预览模式、帮助文本、提示段落大面积损坏 |
|
||||
| 编辑器 UI | `src/components/NpcVisualEditor.tsx` | `463`, `521`, `550`, `701-705`, `719`, `786-833` | 多处整句乱码 | NPC 视觉编辑器的空态、失败提示、回滚提示、页头说明和多组选项已写坏 |
|
||||
| 编辑器 UI | `src/editor/shared/FormFields.tsx` | `156` | `淇濆瓨涓?..` | 通用保存按钮的“保存中...”状态显示乱码 |
|
||||
|
||||
## 二、游戏 UI 中的英文残留
|
||||
|
||||
### 1. 冒险主界面与奖励弹层
|
||||
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx:114-125`
|
||||
- 奖励物品描述 fallback 仍是整句英文,如 `restores HP during the run`、`works as a rare relic reward`。
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx:136-157`
|
||||
- 任务目标展示里仍有 `BOUNTY TARGET`、`CACHE TRACE`、`SPAR SESSION`、`Inspect the hidden reward site`。
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx:262-291`
|
||||
- 任务奖励卡里仍有 `REWARD CACHE`、`Tap an item icon to inspect its details.`、`Affinity`、`Currency`、`No item bounty attached to this quest.`。
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx:351-358`
|
||||
- 目标详情卡仍有 `Objective`、`Area`。
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx:490`, `525`, `668`
|
||||
- 统计说明、保存禁用提示、空任务提示仍是英文,如 `Inspect play time, kills, quests, and travel history.`、`Saving is temporarily disabled...`、`No active quests yet.`。
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx:749`, `781-785`, `831`, `887-908`, `925-1016`
|
||||
- 完成奖励与战斗奖励弹层仍有 `Claim reward`、`QUEST COMPLETE`、`Reward ready`、`Quest reward claimed`、`Battle reward`、`LOOT CACHE`、`No loot dropped this time.`、`Rarity`、`Quantity`、`Slot`、`Not equippable`、`Usable directly`、`Effect preview: HP + ... / MP + ...`。
|
||||
|
||||
### 2. 实体详情与 NPC 交互
|
||||
|
||||
- `src/components/AdventureEntityModal.tsx:1073`, `1111`, `1163-1165`, `1252`, `1428`
|
||||
- 仍有 `NPC 信息`、`NPC`、`x{item.quantity}`、`Inspect`、`Character`、`NPC 背包`。
|
||||
- `src/components/AdventureEntityModal.tsx:892`, `898`
|
||||
- 同伴状态标签仍直接显示 `HP` / `MP`。
|
||||
- `src/components/CompanionCampModal.tsx:177-178`, `233-234`, `255`
|
||||
- 同伴卡片和空态句子里仍有 `HP` / `MP` / `NPC`。
|
||||
- `src/components/NpcModals.tsx:79`, `252`, `273`, `356`, `408`
|
||||
- 交易弹窗与详情弹窗里仍有 `x{item.quantity}`、`NPC 商品列表`、`这个 NPC 当前没有可售商品。`、`NPC 商品`、`效果预览:HP + ... / MP + ...`。
|
||||
|
||||
### 3. 开场流程与角色选择
|
||||
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx:28-44`
|
||||
- 角色名、称号、定位、标签全部是英文,如 `Sword Princess`、`Royal Blade`、`Vanguard`、`STR`、`AGI`、`Female`、`Male`。
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx:329-391`
|
||||
- 面板标题和按钮仍有 `Character Stats`、`Gender:`、`Backstory`、`Customize`、`Details`、`Enter Camp`、`Go`。
|
||||
- `src/components/game-shell/PreGameSelectionFlow.tsx:63-75`
|
||||
- 自定义世界生成进度仍全是英文,如 `Finalizing world archive...`、`Generating core NPCs...`、`Parsing world setup...`。
|
||||
- `src/components/game-shell/PreGameSelectionFlow.tsx:252-308`
|
||||
- 开场按钮和入口仍有 `New Game`、`Start Game`、`Developer Team`、`Go`、`CONTACTS`、`WORLD SELECT`、`Back`。
|
||||
- `src/components/game-shell/PreGameSelectionFlow.tsx:344-421`
|
||||
- 世界卡片与自定义世界入口仍有 `Online`、`Featured`、`Saved`、`Playable`、`Landmarks`、`Custom`、`Create Custom World`、`Enter a world setup...`。
|
||||
- `src/components/GameShell.tsx:630`, `651`, `695`
|
||||
- Suspense fallback 仍显示 `Loading party panel`、`Loading adventure panel`、`Loading inventory panel`。
|
||||
|
||||
### 4. 其他游戏 UI
|
||||
|
||||
- `src/components/CharacterDetailModal.tsx:112`
|
||||
- `数量 x{item.quantity}` 中的 `x` 仍保留英文数量前缀。
|
||||
|
||||
## 三、编辑器 UI 中的英文残留
|
||||
|
||||
### 1. 编辑器入口与共享配置
|
||||
|
||||
- `src/components/PresetEditor.tsx:65-73`
|
||||
- 页头完整为英文:`Preset Workshop`、`Unified Preset Preview And Editor` 及其说明段。
|
||||
- `src/components/preset-editor/shared.ts:43`, `60-72`
|
||||
- 主 tab 仍有 `NPC`;动画和技能风格选项仍直接使用 `idle`、`move`、`attack`、`die`、`steady`、`burst`、`mobility`、`finisher`、`projectile`。
|
||||
|
||||
### 2. 预设编辑器主面板
|
||||
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:1267`, `1594`
|
||||
- 保存反馈仍是 `Saved.`。
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:1277-1279`, `1327-1328`, `1414-1415`, `1608-1609`, `1647-1648`
|
||||
- 多个分区标题和描述仍是占位英文 `Section` / `Editor section.`。
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:1283`, `1442`, `1448`
|
||||
- 表单标签出现错误拼接,如 `Field"NPC"`、`Field"ID"`。
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:1320`, `1640`
|
||||
- 保存按钮文字仍是 `Save`。
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx:1421`, `1658-1661`, `1692`, `1698`, `1701`, `1710`, `2149`
|
||||
- 仍有 `NPC ID`、`Monster Encounter`、`NPC Encounter`、`Empty Scene`、`None`、`NPC`、`FPS` 等英文或英文缩写。
|
||||
|
||||
### 3. 物品 / 行为 / NPC 视觉编辑器
|
||||
|
||||
- `src/components/ItemCatalogEditor.tsx:648`, `729`, `736`, `760`, `767`, `783`, `800`
|
||||
- 仍有 `HP`、`MP`、`CD`、`Build Buff`、`ID`。
|
||||
- `src/components/ItemCatalogEditor.tsx:793-817`
|
||||
- `buildProfile.role`、`setId`、`pieceName` 等原始英文值直接显示在输入框。
|
||||
- `src/components/StateFunctionEditor.tsx:818-821`, `885`, `915`
|
||||
- 预览信息里仍有 `HP`、`No visible target`、`n/a`。
|
||||
- `src/components/StateFunctionEditor.tsx:1060-1064`
|
||||
- 保存失败/成功提示仍是英文:`Failed to save option behavior overrides`、`Option behavior overrides saved.`。
|
||||
- `src/components/StateFunctionEditor.tsx:1106`, `1185`, `1191`, `1217`
|
||||
- 仍直接展示 `battle` / `idle`、`AnimationState` 原值、`idle` / `move` / `attack`,以及 `steady` / `burst` / `mobility` / `finisher` / `projectile`。
|
||||
- `src/components/NpcVisualEditor.tsx:538`, `714`, `781`, `798`
|
||||
- 仍有 `Save failed`、`Current NPC`、`Custom Hair Color`、`Hide Facial Hair`。
|
||||
- `src/components/npcVisualEditorPersistence.ts:26`, `31`, `45`, `50`
|
||||
- 保存提示仍为 `Failed to save NPC visual overrides`、`Saved NPC visual overrides to src/data/npcVisualOverrides.json.`、`Failed to save NPC layout config`、`Saved shared NPC layout config.`。
|
||||
|
||||
### 4. 自定义世界结果页 / 编辑弹窗
|
||||
|
||||
- `src/components/CustomWorldEntityCatalog.tsx:346`
|
||||
- 说明文案里直接暴露资产名 `MedievalFantasyCharacters`。
|
||||
- `src/components/CustomWorldEntityEditorModal.tsx:242`, `458`
|
||||
- 图片路径占位里仍保留 `URL`;NPC 形象编辑说明里直接出现 `MedievalFantasyCharacters`。
|
||||
|
||||
## 四、预设 / 数据层中会透出 UI 的英文原始值
|
||||
|
||||
### 1. 物品预设
|
||||
|
||||
- `src/data/itemDesign.ts:56-58`, `67-69`, `123-149`
|
||||
- `worldAffinity`、`role`、`rarity`、`tags` 中仍有 `neutral`、`wuxia`、`xianxia`、`fieldcraft`、`breaker`、`caster`、`berserker`、`assassin`、`common`、`rare`、`epic` 等原始值。
|
||||
- `src/data/itemDesign.ts:213-219`
|
||||
- `pieceName` 仍为 `boots`、`chest`、`gloves`、`helm`、`leggings`、`shield`、`weapon`。
|
||||
- `src/data/itemDesign.ts:538-545`, `581-598`, `731-748`, `906-913`
|
||||
- 描述拼接和构筑信息里仍直接出现 `build`、`role`、`dust`、`crystal`、`gem` 等英文原始词。
|
||||
- 这些值会直接透出到 `ItemCatalogEditor` 的标签、构筑字段和预览信息。
|
||||
|
||||
### 2. 怪物掉落预设
|
||||
|
||||
- `src/data/monsterPresets.ts:494-536`, `647-721`
|
||||
- 掉落类别仍有 `Armor`、`Relic`、`Material`、`Consumable`。
|
||||
- 掉落名称仍有 `Carapace Plate`、`Guard Core`、`Spore Pouch`、`Burst Cap`、`Ashfire Feather`、`Serpent Eye`、`Tide Ink`、`Lake Pearl`、`Thorn Nectar`。
|
||||
- 掉落描述仍有整句英文,如 `A toxin sac prized by alchemists and assassins alike.`。
|
||||
- 这些值会进入战斗奖励、物品详情和交易 UI。
|
||||
|
||||
### 3. 角色预设
|
||||
|
||||
- `src/data/characterPresets.ts:54-70`
|
||||
- 会话风格原始值仍为 `blunt`、`wary`、`evasive`、`measured`、`gentle`、`teasing`、`dry`、`steady`、`direct`、`fragmented`、`deflecting`。
|
||||
- `src/data/characterPresets.ts:368-386`, `525-543`, `839-857`, `1024-1045`
|
||||
- 动画文件夹 / 前缀与风格原始值仍有 `Double Jump`、`jump attack`、`Wall Slide`、`guardStyle`、`warmStyle`、`truthStyle`。
|
||||
- 这些值会透出到角色预设编辑器、技能预览和部分选择器。
|
||||
|
||||
### 4. Build / 标签词典
|
||||
|
||||
- `src/data/buildTags.ts:42`, `56`, `91`, `126-147`, `308-316`
|
||||
- 仍有 `assassin`、`fieldcraft`、`breaker`、`caster`、`weapon`、`armor`、`relic`、`material`、`consumable`、`rare`、`wuxia`、`xianxia`、`neutral` 等原始标签。
|
||||
- 这些值会在物品编辑器标签、构筑画像和相似度映射结果中直接显示。
|
||||
|
||||
## 五、优先级建议
|
||||
|
||||
1. 先修 `src/components/preset-editor/PresetEditorPanels.tsx` 和 `src/components/NpcVisualEditor.tsx`
|
||||
- 这两处是当前编辑器侧最严重的问题源,既有大面积乱码,也有大量英文占位词。
|
||||
2. 再修游戏首屏与奖励相关 UI
|
||||
- 优先处理 `src/components/adventure-panel/AdventurePanelOverlays.tsx`
|
||||
- 优先处理 `src/components/game-shell/PreGameSelectionFlow.tsx`
|
||||
- 优先处理 `src/components/game-shell/CharacterSelectionFlow.tsx`
|
||||
3. 然后修直接影响主流程判断的乱码
|
||||
- `src/components/GameShell.tsx`
|
||||
- `src/components/AdventurePanel.tsx`
|
||||
- `src/components/CharacterDetailModal.tsx`
|
||||
- `src/components/CustomWorldEntityEditorModal.tsx`
|
||||
- `src/editor/shared/FormFields.tsx`
|
||||
4. 最后补“显示层映射”
|
||||
- 为 `itemDesign.ts`、`monsterPresets.ts`、`characterPresets.ts`、`buildTags.ts` 这类预设原始值统一增加中文显示映射,避免继续把内部英文值直接透给编辑器和游戏 UI。
|
||||
|
||||
325
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-04-01.md
Normal file
325
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-04-01.md
Normal file
@@ -0,0 +1,325 @@
|
||||
# 游戏 UI / 预设 / 编辑器文本审计
|
||||
|
||||
日期:`2026-04-01`
|
||||
|
||||
## 范围
|
||||
|
||||
- 扫描范围:`src/components/`、`src/editor/`、`src/routing/`、`src/hooks/`、`src/services/`、`src/data/`
|
||||
- 聚焦对象:
|
||||
- 游戏内实际可见 UI 文本
|
||||
- 预设编辑器与自定义世界编辑器中的可见文本
|
||||
- 会直接透出到游戏 UI / 编辑器 UI 的预设原始值
|
||||
- 未覆盖:
|
||||
- 图片资源内嵌文字的 OCR
|
||||
- `docs/` 历史文档本身
|
||||
- 单纯内部实现用的 import path、className、asset path、纯 id 常量
|
||||
|
||||
## 方法
|
||||
|
||||
- 先做一轮源码级 AST 扫描,抽取 JSX 可见文本、按钮文案、占位文案、标签文案和常见说明文案。
|
||||
- 再做一轮“反向解码”复核:
|
||||
- `瑙掕壊 -> 角色`
|
||||
- `鍦烘櫙 -> 场景`
|
||||
- `姝︿緺 -> 武侠`
|
||||
- `鏈煡 AI 閿欒 -> 未知 AI 错误`
|
||||
- 结论只保留当前源码里仍然存在的问题,不直接沿用旧审计文档。
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 当前仍然有 3 类问题:
|
||||
1. 真实乱码:主要在 `appRoutes.tsx`、`AdventurePanel.tsx`、`CharacterDetailModal.tsx`、`useStoryGeneration.ts`、`preset-editor/shared.ts` 和 4 个拆分后的预设面板文件中。
|
||||
2. 游戏 / 编辑器英文残留:主要在 `AdventurePanelOverlays.tsx`、`AdventureEntityModal.tsx`、`PreGameSelectionFlow.tsx`、`NpcVisualEditor.tsx`、`ItemCatalogEditor.tsx`、`StateFunctionEditor.tsx`、自定义世界编辑器几处。
|
||||
3. 预设原始值直接透出:主要在 `characterPresets.ts`、`itemDesign.ts`、`monsterPresets.ts`、`buildTags.ts`、`scenePresets.ts`、`stateFunctions.ts`。
|
||||
- 编辑器侧当前最明显的重灾区不是旧的 `PresetEditorPanels.tsx` 大文件,而是已经拆分出的:
|
||||
- `src/components/preset-editor/shared.ts`
|
||||
- `src/components/preset-editor/CharacterPresetPanel.tsx`
|
||||
- `src/components/preset-editor/SceneNpcPresetPanel.tsx`
|
||||
- `src/components/preset-editor/ScenePresetPanel.tsx`
|
||||
- `src/components/preset-editor/MonsterPresetPanel.tsx`
|
||||
- 游戏主流程里影响最直观的点:
|
||||
- 路由加载页文本乱码
|
||||
- 冒险面板里的任务状态 / 对话状态 / NPC 交互短描述乱码
|
||||
- AI 错误兜底文案乱码
|
||||
|
||||
## 一、游戏 UI:已确认乱码
|
||||
|
||||
| 文件 | 行号 | 当前文本 / 范围 | 说明 |
|
||||
| --- | --- | --- | --- |
|
||||
| `src/routing/appRoutes.tsx` | `103-115` | `LOADING EDITOR`、`LOADING GAME`;`姝e湪杞藉叆缂栬緫鍣?..`;`姝e湪杞藉叆鍐掗櫓...` | 路由级加载屏文案。后两段是真乱码;结合反向解码可确定原意分别接近“正在载入编辑器...”和“正在载入冒险...”。 |
|
||||
| `src/components/AdventurePanel.tsx` | `99`、`101`、`103`、`109`、`111`、`113` | `查看库存与价<E4B88E>?`、`聊聊并试探口<E68EA2>?`、`看看能得到什么帮<E4B988>?`、`离开并继续探<E7BBAD>?`、`战斗决胜<E586B3>?`、`切磋几招看身<E79C8B>?` | NPC 交互短描述里有多处截断 / 乱码。 |
|
||||
| `src/components/AdventurePanel.tsx` | `200`、`203` | `可作为制作材<E4BD9C>?`、`任务奖励物品,可用于后续路线、交易或构筑规划<E8A784>?` | 任务奖励物品说明文本被截断。 |
|
||||
| `src/components/AdventurePanel.tsx` | `569-571` | `已完<E5B7B2>?`、`已交<E5B7B2>?`、`进行<E8BF9B>?` | 任务状态标签乱码。 |
|
||||
| `src/components/AdventurePanel.tsx` | `771` | `<60>?` | 对话气泡里的屏幕阅读器标签损坏。 |
|
||||
| `src/components/AdventurePanel.tsx` | `833`、`837`、`870` | `剧情推演<E68EA8>?..`、`对话进行<E8BF9B>?`、`剧情推理完成,继续后显示新的冒险选项<E98089>?` | 加载态 / 流式对话态 / 继续冒险提示都有截断。 |
|
||||
| `src/components/CharacterDetailModal.tsx` | `35-36`、`223` | `女<>?`、`男<>?`、`属<>?` | 性别标签与“属性”标题乱码。 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `1214`、`1266`、`1409`、`1549`、`1978`、`2325` | `鏈煡 AI 閿欒` | 游戏故事流里 AI 失败时的统一兜底提示乱码;可反解为“未知 AI 错误”。 |
|
||||
|
||||
## 二、游戏 UI:英文残留
|
||||
|
||||
### 1. 冒险面板和奖励弹层
|
||||
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx`
|
||||
- `554-570`:`Adventure stats`、`Current area:`、`ADVENTURE SUMMARY`、`enemies defeated`、`items in inventory`、`scene transitions so far`
|
||||
- `622-668`:`Quest log`、`Total quests:`、`No active quests yet.`
|
||||
- `711-798`:`QUEST BRIEF`、`Claim reward`、`QUEST COMPLETE`、`Reward ready`、`Reward pickup is now available in the quest log.`、`Open quest log`
|
||||
- `887-1016`:`Battle reward`、`Defeated enemies:`、`BATTLE END`、`LOOT CACHE`、`Tap an item icon to inspect its details.`、`No usable loot dropped this time.`、`No loot dropped this time.`、`Rarity:`、`Quantity:`、`Slot:`、`Not equippable`、`Usable directly`、`Passive / non-immediate item`、`Effect preview: HP +`、`MP +`、`Cooldown -`、`Tags:`、`none`
|
||||
- `src/components/AdventurePanel.tsx`
|
||||
- `359-388`:`REWARD CACHE`、`Tap an item icon to inspect its details.`、`Affinity`、`Currency`、`No item bounty attached to this quest.`
|
||||
- `636-638`:`Current area`
|
||||
- `803`、`824`:两个按钮都显示 `Refresh`
|
||||
|
||||
### 2. 实体详情、同伴、交易
|
||||
|
||||
- `src/components/AdventureEntityModal.tsx`
|
||||
- `892`、`898`:`HP`、`MP`
|
||||
- `1073`:`NPC 信息`
|
||||
- `1111`:`敌对NPC`、`NPC`
|
||||
- `1163`:数量前缀 `x{item.quantity}`
|
||||
- `1165`:`Inspect`
|
||||
- `1428`:`NPC 背包`
|
||||
- `src/components/CompanionCampModal.tsx`
|
||||
- `177-178`、`233-234`:`HP`、`MP`
|
||||
- `255`:`NPC`
|
||||
- `src/components/NpcModals.tsx`
|
||||
- `252`、`273`、`356`:`NPC 商品列表`、`这个 NPC 当前没有可售商品。`、`NPC 商品`
|
||||
- `408`:`效果预览:HP +... / MP +... / 冷却 -...`
|
||||
- `src/components/CharacterDetailModal.tsx`
|
||||
- `112`:`数量 x{item.quantity}`
|
||||
|
||||
### 3. 开场流程与加载态
|
||||
|
||||
- `src/components/game-shell/PreGameSelectionFlow.tsx`
|
||||
- `48-49`:`QQ Group`、`WeChat`
|
||||
- `81`、`89`:`核心NPC`
|
||||
- `471-473`:`Wuxia Base`
|
||||
- `519-527`:`Custom`、`Create Custom World`、`Enter a world setup and let the system generate playable characters, NPCs, items, and landmarks.`
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx`
|
||||
- `401`:`Character Details`
|
||||
- `406`:`Current Character`
|
||||
- `src/components/GameCanvas.tsx`
|
||||
- `32`:`Loading scene`
|
||||
- `src/components/GameShell.tsx`
|
||||
- `859`:`正在加载 NPC 交互...`
|
||||
|
||||
### 4. 运行时文案源头
|
||||
|
||||
- `src/data/sceneObservation.ts`
|
||||
- `9-36` 整段观察结果仍是英文:
|
||||
- `You pause to listen...`
|
||||
- `Possible NPCs: ...`
|
||||
- `Possible hostile NPCs: ...`
|
||||
- `Possible treasure clues: ...`
|
||||
- `Boss clue: ...`
|
||||
- `src/hooks/useStoryGeneration.ts`
|
||||
- `216`、`219`:最近战斗 / 最近协作提示仍是英文
|
||||
- `639-646`:营地聊天结果文本混用了英文句子
|
||||
- `662-667`:预览对话选项里仍有 `Speak with ...` 与 `Focus on the person in front of you first...`
|
||||
|
||||
## 三、编辑器 UI:已确认乱码
|
||||
|
||||
### 1. 共享标签与世界名
|
||||
|
||||
- `src/components/preset-editor/shared.ts`
|
||||
- `42`:`瑙掕壊`,可反解为 `角色`
|
||||
- `44`:`鍦烘櫙`,可反解为 `场景`
|
||||
- `45`:`鏁屽 NPC`,可反解为 `敌对 NPC`
|
||||
- `46`:`鐗╁搧`,可反解为 `物品`
|
||||
- `47`:`鍔熻兘`,可反解为 `功能`
|
||||
- `53`:`姝︿緺`,可反解为 `武侠`
|
||||
- `54`:`浠欎緺`,可反解为 `仙侠`
|
||||
- `55`:`鑷畾涔変笘鐣?`,基本可判定原意是“自定义世界”,但当前字符串已经不完整
|
||||
|
||||
### 2. 角色预设面板
|
||||
|
||||
- `src/components/preset-editor/CharacterPresetPanel.tsx`
|
||||
- `79`:空状态 / 顶部说明整段乱码
|
||||
- `372-373`:装备区标题乱码
|
||||
- `395-397`:背包区标题乱码
|
||||
- `446-447`:技能预览相关标签乱码
|
||||
- `475-477`:技能区提示乱码
|
||||
- `590-592`:底部说明大段乱码
|
||||
|
||||
### 3. 场景 NPC 预设面板
|
||||
|
||||
- `src/components/preset-editor/SceneNpcPresetPanel.tsx`
|
||||
- `87`:空状态整段乱码
|
||||
- `267`:技能预览空态说明乱码
|
||||
- `346`:角色 ID 标签乱码
|
||||
- `352`:怪物预设 ID 标签乱码
|
||||
- `389`:视觉编辑器说明整段乱码
|
||||
|
||||
### 4. 场景预设面板
|
||||
|
||||
- `src/components/preset-editor/ScenePresetPanel.tsx`
|
||||
- `52`:空状态整段乱码
|
||||
- `220-221`:敌对 NPC 分区标题乱码
|
||||
- `254-255`:场景 ID 标签乱码
|
||||
- `298-299`:怪物 ID 列表标签乱码
|
||||
- `316-317`:关联 NPC 分区标题乱码
|
||||
|
||||
### 5. 怪物预设面板
|
||||
|
||||
- `src/components/preset-editor/MonsterPresetPanel.tsx`
|
||||
- `53`:顶部说明整段乱码
|
||||
|
||||
## 四、编辑器 UI:英文残留
|
||||
|
||||
### 1. 预设编辑器主面板
|
||||
|
||||
- `src/components/preset-editor/CharacterPresetPanel.tsx`
|
||||
- `278-279`:`Character List`、`Choose a player character, preview it live, and edit the preset fields.`
|
||||
- `325`:`Save Character Overrides`
|
||||
- 多处通用占位仍是 `Section`、`Editor section.`、`Field`
|
||||
- `347`:`Inventory World`
|
||||
- `468-484`:`Skill Loadout`、`Add Skill`
|
||||
- `510`:`Skill ID`
|
||||
- `651`:`Character ID`
|
||||
- `682`:`Asset Variant`
|
||||
- `698`:`Personality`
|
||||
- `713`:`Attributes`、`Adjust the four core character attributes.`
|
||||
- `772`:`Unset`
|
||||
- `798`:`scene-id-1 / scene-id-2`
|
||||
- `src/components/preset-editor/SceneNpcPresetPanel.tsx`
|
||||
- `181-182`:`NPC Library`、`Browse and select an NPC preset.`
|
||||
- `186`:`NPC ID`
|
||||
- `223`:`Save NPC Overrides`
|
||||
- `230-246`:`Skill Preview`、`Preview ranged skills from the linked character.`、`Skill`、`World`
|
||||
- `275-276`:`Hostile NPCs use monster presets...`、`Narrative NPCs can preview linked visuals...`
|
||||
- `318-382`:`NPC Details`、`Role`、`Avatar`、`Initial Affinity`、`Description`、`Visual Editor`
|
||||
- `src/components/preset-editor/ScenePresetPanel.tsx`
|
||||
- `145`:`Scene`
|
||||
- `172`:`Save`
|
||||
- `179-193`:`Scene Preview`、`Preview Mode`、`Monster Preview`、`NPC Preview`、`Treasure Preview`、`Empty`
|
||||
- `223`、`230`、`233`、`242`:`None`、`NPC`
|
||||
- `248-272`:`Scene Details`、`World`、`Name`、`Description`
|
||||
- `288`:`Unset`
|
||||
- `src/components/preset-editor/MonsterPresetPanel.tsx`
|
||||
- `172`:`Save Monster Overrides`
|
||||
- `179-180`:`Monster Override Preview`、`Editor section.`
|
||||
- `213-222`:`Attack Range:`、`Speed:`、`HP:`、`Max HP:`
|
||||
- `236`:`Monster ID`
|
||||
- `242`:`Name`
|
||||
- `258`:`Intro Action`
|
||||
- `373`:`FPS`
|
||||
|
||||
### 2. 其他编辑器 / 自定义世界
|
||||
|
||||
- `src/components/ItemCatalogEditor.tsx`
|
||||
- `654`:`HP`、`MP`、`CD`
|
||||
- `677`、`806`:`ID`
|
||||
- `789`:`Build Buff`
|
||||
- `458`、`863`:`public/Icons`、`itemOverrides.json`
|
||||
- `src/components/NpcVisualEditor.tsx`
|
||||
- `463`、`702`、`708`、`718`:`NPC`
|
||||
- `977`:`Shift`
|
||||
- `1028-1052`:`Current loadout:`、`Unknown headgear`、`No headgear`、`Unknown main hand`、`No main hand`、`Unknown off hand`、`No off hand`
|
||||
- `src/components/CustomWorldEntityCatalog.tsx`
|
||||
- `139`、`268`、`276`、`349`:`NPC`
|
||||
- `224`:`WORLD DOSSIER`
|
||||
- `346`:`MedievalFantasyCharacters`
|
||||
- `src/components/CustomWorldEntityEditorModal.tsx`
|
||||
- `242`:`URL`
|
||||
- `460`:`MedievalFantasyCharacters`
|
||||
- `478-479`、`730`、`758-759`:`AI`、`AI生成NPC形象`、`AI生成场景`
|
||||
- `631-653`:`NPC`
|
||||
- `src/components/PresetEditor.tsx`
|
||||
- `71`:介绍文案里仍然直接显示 `NPC`
|
||||
- `src/components/StateFunctionEditor.tsx`
|
||||
- `803`:`Failed to play preview`
|
||||
- `818-821`:`HP`、`No visible target`
|
||||
- `915`:`n/a`
|
||||
- `1060-1064`:`Failed to save option behavior overrides`、`Option behavior overrides saved.`
|
||||
- `1106`:直接显示原始 `state`
|
||||
- `1185`:直接把 `AnimationState` 值作为 label
|
||||
- `1191`:敌对 NPC 反应动画里仍直接显示 `idle` / `move` / `attack`
|
||||
- `1217`:技能风格仍直接依赖 `steady` / `burst` / `mobility` / `finisher` / `projectile`
|
||||
|
||||
## 五、预设 / 数据层:会直接透出 UI 的英文原始值
|
||||
|
||||
这一部分不是“源码内部英文就算问题”,而是“当前编辑器或预览没有做显示映射,导致原始英文值直接露给用户”。
|
||||
|
||||
### 1. 角色预设
|
||||
|
||||
- `src/data/characterPresets.ts`
|
||||
- 动作文件夹 / 前缀仍是英文:
|
||||
- `363-379`
|
||||
- `520-536`
|
||||
- `739-755`
|
||||
- `834-850`
|
||||
- `1019-1038`
|
||||
- 对话风格原始值仍是英文:
|
||||
- `384-386`:`blunt` / `dry` / `direct`
|
||||
- `541-543`:`wary` / `dry` / `fragmented`
|
||||
- `760-762`:`blunt` / `teasing` / `deflecting`
|
||||
- `855-857`:`blunt` / `steady` / `direct`
|
||||
- `1043-1045`:`measured` / `steady` / `fragmented`
|
||||
- 技能风格 / 投射方式仍是英文:
|
||||
- `407-408`、`445`、`473-474`
|
||||
- `572-573`、`604-605`、`636-637`、`668-669`、`700-701`
|
||||
- `791-792`、`815-818`
|
||||
- `886`、`906`、`926-927`、`958-959`、`990-991`
|
||||
- `1066-1077`、`1109-1110`、`1133-1146`、`1178-1179`
|
||||
- 这些值当前会在角色预设编辑器、技能预览和部分行为预览里直接露出。
|
||||
|
||||
### 2. 物品设计 / Build 标签
|
||||
|
||||
- `src/data/itemDesign.ts`
|
||||
- `56-201`:`worldAffinity` / `role` / `rarity` 原始值仍是英文,如 `neutral`、`wuxia`、`xianxia`、`fieldcraft`、`breaker`、`berserker`、`legendary`
|
||||
- `213-219`:`pieceName` 仍是 `boots`、`chest`、`gloves`、`helm`、`leggings`、`shield`、`weapon`
|
||||
- `820`:说明文本里仍混入 `build`
|
||||
- `src/data/buildTags.ts`
|
||||
- `11-291`:整套 build tag id 都是英文,如 `quickblade`、`combo`、`dash`、`ranged`、`burst`、`caster`、`vanguard`、`paladin`、`starter`
|
||||
- 这些值会进入物品编辑器、构筑标签和相关预览。
|
||||
|
||||
### 3. 怪物与掉落
|
||||
|
||||
- `src/data/monsterPresets.ts`
|
||||
- `490-736`:掉落 id、稀有度、tag 原始值大量是英文,如 `rare`、`uncommon`、`armor`、`material`、`relic`、`healing`
|
||||
- `718-736`:有两条掉落本身是完整英文可见值:
|
||||
- `Consumable` / `Thorn Nectar` / `Sticky sap that can be refined into emergency recovery tonic.`
|
||||
- `Relic` / `Devour Bloom` / `A predatory blossom that stores concentrated life force.`
|
||||
|
||||
### 4. 场景 / 行为 / 锻造 / NPC 交互
|
||||
|
||||
- `src/data/scenePresets.ts`
|
||||
- `349-651`:场景 id 全部是英文连字符格式,如 `wuxia-bamboo-road`、`xianxia-cloud-gate`
|
||||
- 当前在编辑器 ID 字段中会直接显示。
|
||||
- `src/data/stateFunctions.ts`
|
||||
- `113-372`:`category` 原始值仍是 `battle` / `recovery` / `escape` / `idle`
|
||||
- 编辑器预览还会直接显示动画 / delivery 原始值。
|
||||
- `src/data/forgeSystem.ts`
|
||||
- `264`:描述里混入 `build`
|
||||
- `274-281`:`relic`、`epic`、`setId`、`pieceName` 等原始值会进入物品编辑器链路
|
||||
- `src/data/npcInteractions.ts`
|
||||
- `207-209`:兜底对话风格仍是 `measured` / `steady` / `fragmented`
|
||||
|
||||
## 六、建议修复顺序
|
||||
|
||||
1. 先修最影响主流程观感的真实乱码。
|
||||
- `src/routing/appRoutes.tsx`
|
||||
- `src/components/AdventurePanel.tsx`
|
||||
- `src/components/CharacterDetailModal.tsx`
|
||||
- `src/hooks/useStoryGeneration.ts`
|
||||
2. 再修预设编辑器的共享标签和 4 个拆分面板。
|
||||
- `src/components/preset-editor/shared.ts`
|
||||
- `src/components/preset-editor/CharacterPresetPanel.tsx`
|
||||
- `src/components/preset-editor/SceneNpcPresetPanel.tsx`
|
||||
- `src/components/preset-editor/ScenePresetPanel.tsx`
|
||||
- `src/components/preset-editor/MonsterPresetPanel.tsx`
|
||||
3. 再统一清理英文残留。
|
||||
- 游戏端优先:`AdventurePanelOverlays.tsx`、`AdventureEntityModal.tsx`、`PreGameSelectionFlow.tsx`
|
||||
- 编辑器端优先:`ItemCatalogEditor.tsx`、`NpcVisualEditor.tsx`、`StateFunctionEditor.tsx`、自定义世界编辑器
|
||||
4. 最后做“显示层映射”,避免预设原始英文继续漏到 UI。
|
||||
- `characterPresets.ts`
|
||||
- `itemDesign.ts`
|
||||
- `buildTags.ts`
|
||||
- `monsterPresets.ts`
|
||||
- `scenePresets.ts`
|
||||
- `stateFunctions.ts`
|
||||
|
||||
## 七、备注
|
||||
|
||||
- 本次结论以当前源码为准,和旧审计文档相比,已有一部分旧问题已经被修掉。
|
||||
- `src/components/preset-editor/PresetEditorPanels.tsx` 现在只是 re-export 壳文件,真正的问题已经分散到拆分后的 panel 文件里。
|
||||
- `src/components/preset-editor/shared.ts` 里的几处乱码已经可以明确反解,适合优先直接修正。
|
||||
- `src/data/` 中很多英文值本身可能是内部枚举,但只要当前编辑器 / 预览没有做中文映射,就仍应视为“会暴露到用户侧”的文本问题。
|
||||
273
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-04-02.md
Normal file
273
docs/GAME_UI_PRESET_EDITOR_TEXT_AUDIT_2026-04-02.md
Normal file
@@ -0,0 +1,273 @@
|
||||
# 游戏 UI / 预设 / 编辑器文本二次审计(扩展重查版)
|
||||
|
||||
日期:`2026-04-02`
|
||||
|
||||
说明:
|
||||
|
||||
- 本文档用于替换同名上一版审计。上一版确实漏掉了不少内容,尤其是:
|
||||
- `AdventurePanel` 里的任务概览与奖励文案
|
||||
- `npcInteractions.ts` 里的 `actionText` / `detailText`
|
||||
- 自定义世界编辑器、NPC 视觉编辑器里的英文兜底和混合术语
|
||||
- 预设编辑器里多组仍未本地化的标题、字段名、保存反馈
|
||||
- 本次以当前仓库实际内容为准,不沿用旧结论;已经修掉的内容不再重复计入。
|
||||
|
||||
## 审计范围
|
||||
|
||||
- 扫描目录:
|
||||
- `src/components/`
|
||||
- `src/data/`
|
||||
- `src/hooks/`
|
||||
- `src/services/`
|
||||
- `src/routing/`
|
||||
- `src/editor/`
|
||||
- 关注对象:
|
||||
- 玩家在主流程、冒险面板、弹窗里会看到的文本
|
||||
- 预设编辑器、自定义世界编辑器、NPC 视觉编辑器中会直接显示的文本
|
||||
- 会透传到 UI、弹窗、编辑器预览中的数据层文本源
|
||||
- 标记类型:
|
||||
- `英文直出`:面向玩家 / 编辑器用户的英文文本仍直接显示
|
||||
- `中英混用`:中文 UI 中混入 `NPC`、`HP`、`MP`、`AI`、`URL`、`Shift` 等术语
|
||||
- `异常显示`:明显乱码、截断、异常问号替代、分隔符损坏
|
||||
|
||||
## 结论摘要
|
||||
|
||||
- 游戏主流程里仍有明显英文残留:`QQ Group`、`WeChat`、`GENARRATIVE`、`Current Area`。
|
||||
- 游戏内任务 / 冒险面板仍有一批英文任务眉标和按钮辅助文案:`BOUNTY TARGET`、`CACHE TRACE`、`SPAR SESSION`、`Inspect reward item ...`、`Unknown monster`。
|
||||
- `GameShellOverlays.tsx` 仍有整组 loading fallback 出现异常编码。
|
||||
- 预设编辑器目前仍是问题最密集区域之一,`CharacterPresetPanel`、`MonsterPresetPanel`、`SceneNpcPresetPanel`、`ScenePresetPanel`、`StateFunctionEditor` 里都有明显英文直出或半成品占位。
|
||||
- 数据层里仍有大量会透出到 UI / 编辑器的英文值,重点在:
|
||||
- `npcInteractions.ts`
|
||||
- `useStoryGeneration.ts`
|
||||
- `storyGenerationState.ts`
|
||||
- `npcEncounterActions.ts`
|
||||
- `sceneObservation.ts`
|
||||
- `characterPresets.ts`
|
||||
- `stateFunctions.ts`
|
||||
|
||||
## 一、游戏主流程 UI
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `48-49` | 英文直出 | `QQ Group`、`WeChat` | 开场页联系方式标签仍是英文 |
|
||||
| `src/components/game-shell/PreGameSelectionFlow.tsx` | `351` | 中英混用 | `contact.label === 'QQ Group' ? 'QQ群' : '微信'` | 逻辑分支仍依赖英文标签 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `123` | 异常显示 | `姝e湪鍔犺浇鍐掗櫓璇︽儏...` | 冒险详情 loading fallback 乱码 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `162` | 异常显示 | `姝e湪鍔犺浇闃熶紞闈㈡澘` | 队伍面板 loading fallback 乱码 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `187` | 异常显示 | `姝e湪鍔犺浇鑳屽寘闈㈡澘` | 背包面板 loading fallback 乱码 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `214` | 异常显示 | `姝e湪鍔犺浇闃熶紞钀ュ湴...` | 营地弹窗 loading fallback 乱码 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `229` | 异常显示 | `姝e湪鍔犺浇鍦板浘...` | 地图 loading fallback 乱码 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `248` | 异常显示 | `姝e湪鍔犺浇瑙掕壊鑱婂ぉ...` | 角色聊天 loading fallback 乱码 |
|
||||
| `src/components/game-shell/GameShellOverlays.tsx` | `261` | 异常显示 | `姝e湪鍔犺浇 NPC 浜や簰...` | NPC 交互 loading fallback 同时混入 `NPC` |
|
||||
| `src/components/game-shell/GameShellRuntime.tsx` | `146` | 英文直出 | `Current Area` | 当前场景名缺失时的兜底文案仍是英文 |
|
||||
| `src/components/game-shell/GameShellRuntime.tsx` | `200` | 英文直出 | `GENARRATIVE` | 顶部 logo 文案仍是英文 |
|
||||
| `src/components/GameShell.tsx` | `311` | 英文直出 | `Current Area` | 旧壳组件里同样保留英文兜底 |
|
||||
| `src/components/GameShell.tsx` | `365` | 英文直出 | `GENARRATIVE` | 旧壳组件里同样保留英文 logo |
|
||||
| `src/components/DeveloperTeamModal.tsx` | `44` | 英文直出 | `aria-label="Close developer team modal"` | 开发团队弹窗关闭按钮辅助文案未本地化 |
|
||||
|
||||
## 二、游戏内面板 / 弹窗
|
||||
|
||||
### 2.1 冒险与任务面板
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/AdventurePanel.tsx` | `197-198` | 异常显示 | `适合进攻型构`、`适合防御型构` | 描述文本疑似被截断,正常语义应是“构筑” |
|
||||
| `src/components/AdventurePanel.tsx` | `206` | 异常显示 | `item.name + ' 奖励物品<E789A9>?';` | 奖励物品描述兜底尾部异常 |
|
||||
| `src/components/AdventurePanel.tsx` | `216` | 异常显示 | ``${hours}小时 ...<2E>?...秒`` | 时长格式字符串出现异常字符 |
|
||||
| `src/components/AdventurePanel.tsx` | `219` | 异常显示 | ``${minutes}<7D>?...秒`` | 分钟格式字符串同样异常 |
|
||||
| `src/components/AdventurePanel.tsx` | `248` | 英文直出 | ``aria-label={`Inspect reward item ${item.name}`}`` | 奖励物品按钮辅助文案是英文 |
|
||||
| `src/components/AdventurePanel.tsx` | `279` | 英文直出 | `BOUNTY TARGET` | 任务眉标未本地化 |
|
||||
| `src/components/AdventurePanel.tsx` | `283` | 英文直出 | `Unknown monster` | 目标怪物兜底文案是英文 |
|
||||
| `src/components/AdventurePanel.tsx` | `290` | 英文直出 | `CACHE TRACE` | 宝藏任务眉标未本地化 |
|
||||
| `src/components/AdventurePanel.tsx` | `295` | 英文直出 | `Inspect the hidden reward site` | 宝藏任务副文案未本地化 |
|
||||
| `src/components/AdventurePanel.tsx` | `302` | 英文直出 | `SPAR SESSION` | 切磋任务眉标未本地化 |
|
||||
|
||||
### 2.2 NPC / 实体 / 交易相关弹窗
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/AdventureEntityModal.tsx` | `895` | 中英混用 | `label="HP"` | 实体状态估计面板直接显示 `HP` |
|
||||
| `src/components/AdventureEntityModal.tsx` | `901` | 中英混用 | `label="MP"` | 实体状态估计面板直接显示 `MP` |
|
||||
| `src/components/NpcModals.tsx` | `252` | 中英混用 | `NPC 商品列表` / `你的背包列表` | 交易列表标题混入 `NPC` |
|
||||
| `src/components/NpcModals.tsx` | `273` | 中英混用 | `这个 NPC 当前没有可售商品。` | 空状态文案混入 `NPC` |
|
||||
| `src/components/NpcModals.tsx` | `356` | 中英混用 | `NPC 商品` | 详情弹窗标题混入 `NPC` |
|
||||
| `src/components/NpcModals.tsx` | `408` | 中英混用 | `效果预览:HP +... / MP +... / 冷却 -...` | 数值预览里直接保留 `HP`、`MP` |
|
||||
|
||||
### 2.3 自定义世界结果页
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/CustomWorldResultView.tsx` | `58` | 中英混用 | `新增 NPC` | 结果页新增操作标签混入 `NPC` |
|
||||
|
||||
## 三、自定义世界 / NPC 视觉编辑
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `242` | 英文直出 | `URL` | 图片地址输入提示词未本地化 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `464` | 英文直出 | `MedievalFantasyCharacters` | 形象编辑副标题里直接暴露素材包英文名 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `482-483` | 中英混用 | `AI生成NPC形象`、`NPC 形象 AI 生成功能仍在开发中。` | 同时混入 `AI`、`NPC` |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `635-636` | 中英混用 | `新增 NPC`、`编辑 NPC:...` | NPC 档案编辑标题混入英文缩写 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `663` | 中英混用 | `修改形象` | 本行本身无问题,但上下文仍指向 `NPC` 视觉编辑入口 |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `734` | 中英混用 | `AI生成` | 按钮文案混入 `AI` |
|
||||
| `src/components/CustomWorldEntityEditorModal.tsx` | `762-763` | 中英混用 | `AI生成场景`、`场景图片 AI 生成功能仍在开发中。` | 场景生成弹窗混入 `AI` |
|
||||
| `src/components/CustomWorldNpcVisualEditor.tsx` | `393` | 中英混用 | `AI生成` | 自定义 NPC 视觉编辑器按钮混入 `AI` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `263-267` | 英文直出 | `Failed to load NPC visual overrides`、`Failed to load NPC layout config` | 编辑器首屏可见的加载失败兜底为英文 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `284-308` | 英文直出 | `response was invalid, using bundled defaults` 等 | 多组降级提示未本地化 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `463` | 中英混用 | `请先选择一个 NPC 进行编辑` | 空态文案混入 `NPC` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `539` | 英文直出 | `Save failed` | 保存失败提示仍是英文 |
|
||||
| `src/components/NpcVisualEditor.tsx` | `702-708` | 中英混用 | `NPC 视觉编辑器`、`选择并编辑 NPC 的外观...` | 页面标题与简介多次混入 `NPC` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `718` | 中英混用 | `当前 NPC` | 当前对象字段名混入 `NPC` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `976-977` | 中英混用 | `拖动标记微调 NPC 的预览布局。移动时按住 Shift...` | 帮助提示同时混入 `NPC` 与 `Shift` |
|
||||
| `src/components/NpcVisualEditor.tsx` | `1033-1052` | 英文直出 | `Unknown headgear`、`No headgear`、`Unknown main hand`、`No main hand`、`Unknown off hand`、`No off hand` | 预览状态说明仍是英文 |
|
||||
|
||||
## 四、预设编辑器
|
||||
|
||||
### 4.1 共享配置与选项枚举
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/shared.ts` | `43` | 中英混用 | `label: 'NPC'` | 预设编辑器顶部 tab 仍直接显示 `NPC` |
|
||||
| `src/components/preset-editor/shared.ts` | `63-66` | 英文直出 | `idle`、`move`、`attack`、`die` | 怪物动画可选项是原始英文值 |
|
||||
| `src/components/preset-editor/shared.ts` | `70-74` | 英文直出 | `steady`、`burst`、`mobility`、`finisher`、`projectile` | 技能风格可选项是原始英文值 |
|
||||
|
||||
### 4.2 角色预设面板
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `91-92` | 英文直出 | `Saved character preset overrides...`、`Failed to save character preset overrides.` | 保存反馈未本地化 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `264-279` | 英文直出 | `Characters`、`Browse the character roster...`、`Field`、`Save Character Overrides` | 左侧选择卡与保存栏均为英文 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `315-322` | 英文直出 | `Character Details`、`Field` | 详情卡标题和字段名未本地化 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `404-430` | 英文直出 | `Skill Preview`、`Preview ranged skills...`、`Preview Monster` | 技能预览区英文直出 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `450-467` | 英文直出 | `Skill Setup`、`Add Skill` | 技能配置区仍是英文 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `498-558` | 英文直出 | 大量 `label="Field"` | 多数字段仍显示占位词 `Field` |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `605` | 英文直出 | `Start Frame` | 动画字段未本地化 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `629`、`729` | 英文直出 | `Section`、`Editor section.` | 两个分区仍是半成品英文占位 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `697-698` | 英文直出 | `Attributes`、`Adjust the core character attributes.` | 属性面板未本地化 |
|
||||
| `src/components/preset-editor/CharacterPresetPanel.tsx` | `755` | 英文直出 | `Unset` | 场景绑定下拉空值项为英文 |
|
||||
|
||||
### 4.3 怪物预设面板
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `49-50` | 英文直出 | `Saved monster overrides...`、`Failed to save monster overrides.` | 保存反馈未本地化 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `124-139` | 英文直出 | `Section`、`Editor section.`、`Field`、`Save Monster Overrides` | 左侧选择区和保存栏未本地化 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `135` | 异常显示 | ``${WORLD_LABELS[monster.worldType]} 闂?${optionMonster.name}`` | 选择列表分隔符损坏 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `158-159` | 英文直出 | `Monster Override Preview`、`Editor section.` | 预览区标题仍是英文 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `192-201` | 英文直出 | `Attack Range`、`Speed`、`HP`、`Max HP` | 预览摘要未本地化 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `212-234` | 英文直出 | `Monster ID`、`Name`、`Intro Action` | 核心字段名仍是英文 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `343` | 英文直出 | `FPS` | 动画字段未本地化 |
|
||||
| `src/components/preset-editor/MonsterPresetPanel.tsx` | `207`、`275`、`307` | 英文直出 | `Section`、`Editor section.` | 多个分区标题仍为占位英文 |
|
||||
|
||||
### 4.4 场景 NPC 预设面板
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `127-128` | 英文直出 | `Saved NPC overrides.`、`Failed to save NPC overrides.` | 保存反馈未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `159` | 英文直出 | `No NPC presets are available.` | 空态文案未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `177-193` | 英文直出 | `NPC Library`、`Browse and select an NPC preset.`、`NPC ID`、`Save NPC Overrides` | 左侧选择区英文直出 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `221-237` | 英文直出 | `Skill Preview`、`Preview ranged skills from the linked character.`、`Skill`、`World` | 技能预览区未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `258` | 异常显示 | `闂?NPC` | 空态或提示文案已损坏 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `264-268` | 英文直出 | `Visual Preview`、`Hostile NPCs use monster presets...` | 视觉预览区标题与说明均未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `310-371` | 英文直出 | `NPC Details`、`Name`、`Role`、`Avatar`、`Linked Character ID`、`Monster Preset ID`、`Initial Affinity`、`Description`、`Visual Editor` | 详情区字段名大面积英文直出 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `374-375` | 英文直出 | `Hostile NPCs cannot use the visual editor...`、`Narrative NPC visual overrides can be previewed here.` | 视觉编辑区说明未本地化 |
|
||||
| `src/components/preset-editor/SceneNpcPresetPanel.tsx` | `382-384` | 异常显示 | 三整段乱码说明 + `NPC` | 视觉编辑区空态说明已严重损坏 |
|
||||
|
||||
### 4.5 场景预设面板
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `52-53` | 英文直出 | `Saved scene overrides.`、`Failed to save scene overrides.` | 保存反馈未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `62` | 英文直出 | `No scene presets are available.` | 空态文案未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `105-107` | 英文直出 | `Treasure Ahead`、`Treasure` | 宝藏预览实体名仍是英文 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `132-171` | 英文直出 | `Scene Library`、`Browse and select a scene preset.`、`Save`、`Scene Preview`、`Preview monsters, NPCs, and treasure...` | 场景面板主框架仍多处英文 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `175-181` | 英文直出 | `Preview Mode`、`Monster Preview`、`NPC Preview`、`Treasure Preview` | 预览模式切换未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `207-226` | 英文直出 | `Hostile NPCs`、`NPCs`、`None` | 预览摘要区未本地化 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `233-290` | 英文直出 | `Scene Details`、`Edit the selected scene preset.`、`Scene ID`、`World`、`Name`、`Description`、`Image Source`、`Forward Scene`、`Connected Scene IDs`、`Monster IDs`、`Treasure Hints` | 详情编辑区字段名几乎全部英文 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `271` | 英文直出 | `Unset` | 下拉空值项为英文 |
|
||||
| `src/components/preset-editor/ScenePresetPanel.tsx` | `299` | 英文直出 | `NPCs In Scene` | 分区标题未本地化 |
|
||||
|
||||
### 4.6 状态函数编辑器
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/components/StateFunctionEditor.tsx` | `310-325` | 英文直出 | `Preview`、`Treasure` | 预览 NPC / 宝藏 context 里保留英文 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1062-1064` | 英文直出 | `Option behavior overrides saved.`、`Failed to save option behavior overrides` | 保存反馈未本地化 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1128` | 英文直出 | `无模板` 之外的模板选择逻辑仍有英文结构 | 该区主体中文,但下游模板 ID / 类型依旧偏英文 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1185` | 英文直出 | `AnimationState` 原始值直接作为选项标签 | 动画值会直接显示英文枚举 |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1190` | 英文直出 | `placeholder="可使用 {monster} 占位符"` | 占位符本身暴露英文 key |
|
||||
| `src/components/StateFunctionEditor.tsx` | `1191` | 英文直出 | `idle`、`move`、`attack` | 怪物动画下拉仍使用英文值 |
|
||||
|
||||
## 五、数据层 / 运行时文本源
|
||||
|
||||
### 5.1 直接驱动交互按钮、详情文案、结果文本的源头
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/data/npcInteractions.ts` | `444-580` | 重点补录 | 大量 `actionText` / `detailText` | 这是本轮补录重点,属于 NPC 交互选项直接展示源,需要逐条审校 |
|
||||
| `src/data/npcInteractions.ts` | `327-349` | 英文直出 | `deep`、`honest`、`partial`、`guarded`、`warm`、`cooperative`、`neutral`、`distant`、`candid`、`true_but_incomplete`、`half_truth`、`situational_only` | 这些关系 / 说话风格值虽然是逻辑枚举,但后续很容易在编辑器、调试面板、覆盖配置中直接暴露 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `576-578` | 英文直出 | `Travel to ...`、`Leave camp and head toward ...` | 旅行选项和详情文案未本地化 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `654-659` | 英文直出 | `Speak with ...`、`Focus on the person in front of you first...` | 开场对话选项未本地化 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `827`、`1297` | 英文直出 | `Exchange an opening judgment with ... at camp` | 营地开场交互 actionText 未本地化 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `1097` | 英文直出 | `Begin the adventure` | 初始 actionText 未本地化 |
|
||||
| `src/hooks/useStoryGeneration.ts` | `1374`、`1794` | 英文直出 | `Unknown AI error` | AI 兜底错误提示仍是英文 |
|
||||
| `src/hooks/story/storyGenerationState.ts` | `137-141` | 英文直出 | `You leave ...`、`Travel to ...` | 旅行结果与 actionText 未本地化 |
|
||||
| `src/hooks/story/npcEncounterActions.ts` | `277` | 英文直出 | `Victory reward: ${lootText}.` | 战斗胜利奖励结算文本未本地化 |
|
||||
| `src/hooks/story/npcEncounterActions.ts` | `389` | 英文直出 | `NPC dialogue AI is unavailable.` | NPC 对话 AI 失败兜底是英文 |
|
||||
| `src/hooks/story/characterChat.ts` | `68` | 英文直出 | `Player` | 聊天摘要拼接里保留英文说话人名 |
|
||||
| `src/hooks/story/characterChat.ts` | `84` | 英文直出 | `Tell me more clearly what you mean.` | 建议起句未本地化 |
|
||||
| `src/hooks/story/characterChat.ts` | `283` | 英文直出 | `Unknown AI error` | 私聊错误兜底未本地化 |
|
||||
| `src/data/sceneObservation.ts` | `9-34` | 英文直出 | `You pause to listen...`、`Possible NPCs...`、`Possible hostile NPCs...`、`Possible treasure clues...`、`Boss clue...` | 观察环境的整组文本源仍是英文 |
|
||||
|
||||
### 5.2 会透出到编辑器 / 预览 / 覆盖系统的预设值
|
||||
|
||||
| 文件 | 行号 | 类型 | 当前文本 / 字段 | 说明 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `src/data/scenePresets.ts` | `141` | 中英混用 | `role: '敌对NPC'` | 场景 NPC 角色名混入 `NPC` |
|
||||
| `src/data/scenePresets.ts` | `194`、`294` | 英文直出 | `['trade', 'fight', 'spar', 'help', 'chat', 'recruit', 'gift']` | 场景 NPC 功能数组是英文原始值,编辑器容易直出 |
|
||||
| `src/data/stateFunctions.ts` | `33` | 英文直出 | `monsterAnimation?: 'idle' | 'move' | 'attack'` | 动画值原始英文会进入状态函数编辑器 |
|
||||
| `src/data/stateFunctions.ts` | `130`、`152`、`174`、`196`、`221`、`244` | 英文直出 | `steady`、`burst`、`mobility`、`finisher`、`projectile` | 技能权重 key 为英文原始值 |
|
||||
| `src/data/stateFunctions.ts` | `372`、`445`、`569`、`575-576` | 中英混用 | 描述里多次直接写 `NPC` | 虽然主体中文,但会透出到说明文字 |
|
||||
| `src/data/characterPresets.ts` | `54-70` | 英文直出 | `blunt`、`wary`、`evasive`、`measured`、`gentle`、`teasing`、`dry`、`steady`、`direct`、`fragmented`、`deflecting` | 对话风格推断返回值全为英文 |
|
||||
| `src/data/characterPresets.ts` | `362-382`、`519-539`、`738-758`、`833-853`、`1018-1041` | 英文直出 | `assetFolder`、`folder`、`prefix` 中大量英文,如 `Sword Princess`、`idle`、`Double Jump`、`Wall Slide`、`Attack` | 这些值会被预设编辑器动画区直接展示或编辑 |
|
||||
| `src/data/characterPresets.ts` | `387-389`、`544-546`、`763-765`、`858-860`、`1046-1048` | 英文直出 | `guardStyle`、`warmStyle`、`truthStyle` 使用英文值 | 会透出到预设编辑器 / 调试视图 |
|
||||
| `src/data/characterPresets.ts` | `410-501`、`575-720`、`794-1000`、`1069-1197` | 英文直出 | `style`、`delivery`、`phase`、`anchor`、`motion` 使用 `steady`、`mobility`、`ranged`、`travel`、`target`、`projectile` 等英文值 | 角色技能配置层大面积保留英文元数据 |
|
||||
| `src/data/monsterPresets.ts` | `384-817` | 英文直出 | 多处 `common`、`uncommon`、`rare`、`epic` | 怪物稀有度原始值为英文,若编辑器未做映射会直接暴露 |
|
||||
| `src/editor/shared/jsonClient.ts` | `29-43` | 英文直出 | `Request failed`、`Save failed` | 编辑器通用 JSON 客户端默认错误文案未本地化 |
|
||||
|
||||
## 六、本轮已复查、暂未记录明确问题的文件
|
||||
|
||||
以下文件本轮重新检查过,但当前内容里没有继续记录“英文直出 / 异常显示”的明确条目,暂可视为本轮通过:
|
||||
|
||||
- `src/routing/appRoutes.tsx`
|
||||
- `src/components/game-shell/CharacterSelectionFlow.tsx`
|
||||
- `src/components/game-shell/GameShellStoryPanels.tsx`
|
||||
- `src/components/CharacterPanel.tsx`
|
||||
- `src/components/InventoryPanel.tsx`
|
||||
- `src/components/MapModal.tsx`
|
||||
- `src/components/CharacterChatModal.tsx`
|
||||
- `src/components/SelectionCustomizationModals.tsx`
|
||||
- `src/components/adventure-panel/AdventurePanelOverlays.tsx`
|
||||
- `src/data/sceneEncounterPreviews.ts`
|
||||
- `src/data/customWorldVisuals.ts`
|
||||
- `src/services/customWorldBuilder.ts`
|
||||
|
||||
## 建议修复顺序
|
||||
|
||||
1. 先修主流程直接暴露给玩家的文本:
|
||||
- `PreGameSelectionFlow.tsx`
|
||||
- `GameShellOverlays.tsx`
|
||||
- `GameShellRuntime.tsx`
|
||||
- `GameShell.tsx`
|
||||
- `AdventurePanel.tsx`
|
||||
2. 再修编辑器里最容易误导内容生产的面板:
|
||||
- `CharacterPresetPanel.tsx`
|
||||
- `MonsterPresetPanel.tsx`
|
||||
- `SceneNpcPresetPanel.tsx`
|
||||
- `ScenePresetPanel.tsx`
|
||||
- `StateFunctionEditor.tsx`
|
||||
3. 最后统一清理数据层文本源和枚举映射:
|
||||
- `npcInteractions.ts`
|
||||
- `useStoryGeneration.ts`
|
||||
- `storyGenerationState.ts`
|
||||
- `npcEncounterActions.ts`
|
||||
- `sceneObservation.ts`
|
||||
- `characterPresets.ts`
|
||||
- `stateFunctions.ts`
|
||||
191
docs/MOBILE_UI_DEV_EXPERIENCE.md
Normal file
191
docs/MOBILE_UI_DEV_EXPERIENCE.md
Normal file
@@ -0,0 +1,191 @@
|
||||
# 奇幻酒馆 UI 开发经验沉淀
|
||||
|
||||
## 1. 总体原则
|
||||
|
||||
### 1.1 先保证移动端成立,再兼容网页端
|
||||
- 入口页、世界选择、角色选择、冒险页、弹窗面板,都要先按手机竖屏去定义信息密度。
|
||||
- 网页端只做“放宽容器、增加留白、补充 hover”,不要反过来让桌面布局压垮手机体验。
|
||||
- 任何区域如果在移动端需要滚动,必须明确谁滚动,不能让整页和局部同时争夺滚动。
|
||||
|
||||
### 1.2 游戏 UI 要优先“状态清晰”,而不是“文案很多”
|
||||
- 开始页应该像主菜单,不像产品介绍页。
|
||||
- 角色选择页应该像角色选择器,不像角色说明书。
|
||||
- 冒险页应该像战斗/剧情操作台,不像文档阅读器。
|
||||
|
||||
### 1.3 一屏内的层级顺序要稳定
|
||||
- 上半部分:画面演出。
|
||||
- 中间部分:剧情或核心信息。
|
||||
- 底部:操作按钮。
|
||||
- 玩家必须能快速判断“我在看什么、下一步点哪里”。
|
||||
|
||||
## 2. 入口页经验
|
||||
|
||||
### 2.1 开始页
|
||||
- 极简化是对的。
|
||||
- 当前实践证明:开始页只保留“开始游戏”和“开发团队”两个主按钮,体验明显更像游戏。
|
||||
- 游戏名和英文副标题可以保留,但不要再加长段描述。
|
||||
|
||||
### 2.2 世界选择
|
||||
- 纵向焦点轮播比普通列表更适合移动端。
|
||||
- 当前居中的卡片应该最大、最清晰、最亮;上下卡片缩放和透明度随滚动连续变化。
|
||||
- 世界确认动作应该绑定当前聚焦卡,而不是每张卡都塞太多按钮。
|
||||
|
||||
### 2.3 角色选择
|
||||
- 横向轮播是正确方向。
|
||||
- 中间卡片应承担“主视觉 + 主要信息”。
|
||||
- 左右卡片只做预览,不需要承载完整信息。
|
||||
- 左右卡片倾斜方向一定要符合透视直觉:
|
||||
左侧卡片向左外倾,右侧卡片向右外倾。
|
||||
- 中间卡片不要出现任何模糊,否则会破坏“当前选中”的确认感。
|
||||
|
||||
## 3. 角色选择页经验
|
||||
|
||||
### 3.1 聚焦卡的表现
|
||||
- 中间聚焦卡用角色 `run` 动画是有效的,能明显提升“角色活着”的感觉。
|
||||
- 但动画资源和静态立绘的锚点通常不一致,必须单独做位置与缩放修正。
|
||||
- 结论:
|
||||
动画版角色不要直接复用静态图样式,要单独给 `transform`、`height`、`transform-origin`。
|
||||
|
||||
### 3.2 信息区要紧凑
|
||||
- 角色数值和角色背景应该放在轮播下方,但必须压缩高度。
|
||||
- 如果下方面板过高,会直接破坏上半部分轮播体验,尤其在手机上。
|
||||
- 删除背景面板里的额外动画模块是正确的,因为它与主轮播重复抢视觉焦点。
|
||||
|
||||
### 3.3 文案层级
|
||||
- 页面标题只保留一句:
|
||||
“选择你扮演的角色”
|
||||
- 不要再强调 `CHARACTER SELECT` 之类的开发向分类标签。
|
||||
- 聚焦卡下方直接显示角色名,比把名字只放在卡片底部更直观。
|
||||
|
||||
### 3.4 按钮位置
|
||||
- “确认选择”必须放在角色轮播和信息区之后,作为页面最后的主动作。
|
||||
- 按钮不宜过宽过高,否则会抢轮播的视觉重心。
|
||||
|
||||
## 4. 冒险页经验
|
||||
|
||||
### 4.1 剧情区必须自适应
|
||||
- 剧情文本区不应该写死高度。
|
||||
- 正确做法是:
|
||||
让它自动填满“上方画面下缘”和“底部按钮区上缘”之间的剩余空间。
|
||||
- 这样才能同时保证:
|
||||
1. 上方画面一屏可见
|
||||
2. 下方三个选项一屏可见
|
||||
3. 中间剧情尽量大
|
||||
|
||||
### 4.2 底部操作区必须锚定到底
|
||||
- 队伍、背包、换一换、选项列表,都应该属于底部控制区。
|
||||
- 用户会天然在屏幕底部寻找交互入口,尤其是手机。
|
||||
- 因此:
|
||||
不要把这些按钮漂在中间,不要让剧情区把它们挤走。
|
||||
|
||||
### 4.3 队伍与背包不应打断主流程
|
||||
- 在冒险页内,队伍和背包更适合“弹出面板”而不是“切换整页”。
|
||||
- 原因:
|
||||
1. 不会打断当前剧情阅读
|
||||
2. 用户返回成本更低
|
||||
3. 操作像手游副面板,更符合预期
|
||||
|
||||
### 4.4 图标优于文字按钮
|
||||
- 在底部工具区,队伍/背包改成 icon 后更紧凑。
|
||||
- 但必须保留 `aria-label`,保证语义清晰、后续也方便测试。
|
||||
|
||||
## 5. 队伍面板经验
|
||||
|
||||
### 5.1 移动端成员列表不能太“卡片化”
|
||||
- 如果每个成员都再套一层大边框卡片,手机上会显得很挤。
|
||||
- 更好的方式是直接在主面板里陈列成员,弱化分隔、强化内容。
|
||||
|
||||
### 5.2 队伍列表展示什么
|
||||
- 头像
|
||||
- 姓名
|
||||
- 称号
|
||||
- 身份标记(领队/同行)
|
||||
- HP / MP
|
||||
|
||||
### 5.3 队伍详情弹窗
|
||||
- 弹窗必须可滚动。
|
||||
- 弹窗宽度不宜太大,手机优先单列或偏单列。
|
||||
- 技能、装备、属性三个区块保持稳定结构,便于扫读。
|
||||
|
||||
## 6. 背包页经验
|
||||
|
||||
### 6.1 格子数要优先适配手机
|
||||
- 手机端格子间距要小一号。
|
||||
- 图标尺寸和分类角标要跟着缩小。
|
||||
- 目标不是“每个格子信息很多”,而是“单屏能看更多格子”。
|
||||
|
||||
### 6.2 背包详情弹窗
|
||||
- 手机端改成单列/窄宽度布局更合理。
|
||||
- 详情窗里的信息顺序建议固定:
|
||||
1. 名称与类别
|
||||
2. 品质与数量
|
||||
3. 大图标
|
||||
4. 说明
|
||||
5. 标签
|
||||
|
||||
## 7. 样式与动画经验
|
||||
|
||||
### 7.1 轮播动画要连续,不要离散
|
||||
- 滚动时应根据“当前位置与当前卡片中心的距离”实时计算:
|
||||
- `scale`
|
||||
- `opacity`
|
||||
- `rotate`
|
||||
- `translate`
|
||||
- 不要做成“翻页后才跳变”的效果。
|
||||
|
||||
### 7.2 焦点卡与非焦点卡的职责要不同
|
||||
- 焦点卡负责可读性与确认感。
|
||||
- 非焦点卡负责预告与空间深度。
|
||||
- 所以焦点卡不该模糊,非焦点卡可以轻微降透明度、缩放、偏移。
|
||||
|
||||
### 7.3 资源锚点要单独校准
|
||||
- 静态立绘和动画帧经常不是同一锚点。
|
||||
- 只要切换成动画,就需要重新调:
|
||||
- 高度
|
||||
- Y 偏移
|
||||
- 缩放
|
||||
- `transform-origin`
|
||||
|
||||
## 8. 工程经验
|
||||
|
||||
### 8.1 组件要继续拆
|
||||
- 本轮重构证明,把入口页、冒险页、队伍页、背包页拆成独立组件是正确的。
|
||||
- 后续新增 UI 需求时,应优先落在:
|
||||
- `GameShell.tsx`
|
||||
- `AdventurePanel.tsx`
|
||||
- `CharacterPanel.tsx`
|
||||
- `InventoryPanel.tsx`
|
||||
|
||||
### 8.2 不要相信“服务已经在跑”
|
||||
- 这轮出现过“端口上有进程,但浏览器仍然拿到旧模块”的问题。
|
||||
- 实际经验:
|
||||
1. 要检查 `src` 文件内容
|
||||
2. 要检查 dev server 实际返回的模块内容
|
||||
3. 要确认启动脚本是否是当前项目真正使用的脚本
|
||||
|
||||
### 8.3 本项目当前开发服务入口
|
||||
- 本地正确启动脚本是:
|
||||
`node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0`
|
||||
- 历史脚本或旧进程会导致“代码已改但 UI 看起来没变”的假象。
|
||||
|
||||
## 9. 后续建议
|
||||
|
||||
### 9.1 可以继续统一中英混杂文案
|
||||
- 当前部分新旧文本仍有历史遗留字符问题。
|
||||
- 如果后面继续做 UI 精修,建议单独做一轮文案清洗。
|
||||
|
||||
### 9.2 可以把轮播抽成通用组件
|
||||
- 世界纵向焦点轮播
|
||||
- 角色横向倾斜轮播
|
||||
- 这两套逻辑已经足够稳定,适合后续抽成通用 hook 或通用组件。
|
||||
|
||||
### 9.3 可以补移动端安全区适配
|
||||
- 当前已经偏移动端优先,但还可以继续加:
|
||||
- `safe-area-inset-bottom`
|
||||
- `safe-area-inset-top`
|
||||
- 更细的竖屏断点处理
|
||||
|
||||
## 10. 一句话结论
|
||||
|
||||
这一轮最关键的经验是:
|
||||
**游戏 UI 的移动端优化,本质不是把元素缩小,而是重组视觉重心、固定操作锚点、让焦点内容在一屏内自然成立。**
|
||||
744
docs/PIXELMOTION_TECHNICAL_BREAKDOWN_2026-04-04.md
Normal file
744
docs/PIXELMOTION_TECHNICAL_BREAKDOWN_2026-04-04.md
Normal file
@@ -0,0 +1,744 @@
|
||||
# PixelMotion 技术方案拆解(2026-04-04)
|
||||
|
||||
## 1. 文档目的
|
||||
|
||||
拆解 [PixelMotion](https://www.pixelmotion.art/#features) 当前线上版本的产品形态与技术实现思路,重点回答下面几个问题:
|
||||
|
||||
- 它到底是不是“AI 一键做像素动画”
|
||||
- 前端、后端、数据层分别怎么分工
|
||||
- 它为什么能比普通图生图更稳定地产出可用 Sprite Sheet
|
||||
- 如果我们在自己的项目里复刻类似能力,应该保留哪些关键设计
|
||||
|
||||
本次拆解时间截至:`2026-04-04`
|
||||
|
||||
---
|
||||
|
||||
## 2. 调研范围与证据等级
|
||||
|
||||
本次只基于**线上公开可见信息**进行拆解,未拿到 PixelMotion 的私有后端代码。
|
||||
|
||||
### 2.1 已实际核对的公开对象
|
||||
|
||||
- 首页 HTML
|
||||
- `manifest.json`
|
||||
- `sw.js`
|
||||
- 前端打包产物 `assets/index-BHdWGq2s.js`
|
||||
- 前端样式产物 `assets/index-DFENcMpn.css`
|
||||
- 公开接口:
|
||||
- `/api/utils?type=stats`
|
||||
- `/api/utils?type=gifs`
|
||||
- `/api/showcase?limit=3`
|
||||
- 线上响应头
|
||||
|
||||
### 2.2 本文的标记规则
|
||||
|
||||
- `【已确认】`:可直接从公开页面、脚本、接口返回或响应头确认
|
||||
- `【高概率推断】`:无法看到后端源码,但从前端契约和数据形态可以较高把握推断
|
||||
- `【推测】`:仅作为方案猜测,不能当成已确认事实
|
||||
|
||||
---
|
||||
|
||||
## 3. 先说结论
|
||||
|
||||
PixelMotion 本质上不是“运行时实时动画引擎”,而是一个**面向社交传播和轻量游戏资产生产的 AI Sprite Sheet 工坊**。
|
||||
|
||||
它的核心不是:
|
||||
|
||||
- 生成视频后再让前端临时播放
|
||||
- 在浏览器里做复杂骨骼动画
|
||||
- 给任意图片自由发挥动作
|
||||
|
||||
它真正的关键设计是:
|
||||
|
||||
1. 把输出格式强约束成 **4x4、16 帧的 Sprite Sheet PNG**
|
||||
2. 把动作空间强约束成 **模板动作 + 极强提示词规则**
|
||||
3. 把生成后的可用性问题交给 **前端二次编辑** 解决:
|
||||
- 隐藏坏帧
|
||||
- 调整帧序
|
||||
- 调整 FPS
|
||||
- 导出 PNG / GIF / Set
|
||||
4. 把账号、存储、积分、分享、审核全部接进同一套 BaaS 与 API 网关
|
||||
|
||||
一句话概括:
|
||||
|
||||
**它不是在做“无限自由的 AI 动画”,而是在做“高约束、可编辑、可分享的像素精灵表生成流水线”。**
|
||||
|
||||
---
|
||||
|
||||
## 4. 产品形态拆解
|
||||
|
||||
## 4.1 用户主流程
|
||||
|
||||
【已确认】从前端路由、文案和接口可以还原出主流程:
|
||||
|
||||
1. 上传角色图
|
||||
2. 选择动作
|
||||
3. 可选开启 AI 角色分析,或直接用图片参考
|
||||
4. 提交生成
|
||||
5. 得到一张 16 帧 Sprite Sheet
|
||||
6. 在编辑页中调帧序、隐藏帧、改 FPS
|
||||
7. 导出 PNG / GIF / 多角色 Set
|
||||
8. 保存到个人资产库
|
||||
9. 可投稿到 Showcase 社区
|
||||
|
||||
## 4.2 它卖的不是视频,而是精灵表
|
||||
|
||||
【已确认】前端内部把结果当成一个 `4 x 4` 的 sprite sheet 来处理:
|
||||
|
||||
- 默认按 `rows=4`、`cols=4`
|
||||
- 一共 `16` 帧
|
||||
- GIF 导出是从单张图里切格子,不是播放视频
|
||||
- PNG 导出也是从同一张大图里重组
|
||||
|
||||
这说明 PixelMotion 的核心资产格式是:
|
||||
|
||||
- 生成结果主文件:`Sprite Sheet PNG`
|
||||
- 预览导出:浏览器侧再转 `GIF`
|
||||
|
||||
所以它更像:
|
||||
|
||||
- 游戏资产小工坊
|
||||
- 社交素材制作器
|
||||
|
||||
而不是:
|
||||
|
||||
- 视频生成器
|
||||
- 骨骼动画编辑器
|
||||
|
||||
---
|
||||
|
||||
## 5. 前端技术栈
|
||||
|
||||
## 5.1 应用框架
|
||||
|
||||
【已确认】
|
||||
|
||||
- 前端是 **React SPA**
|
||||
- 打包方式很像 **Vite**
|
||||
- 路由是 **React Router**
|
||||
- 国际化使用 **i18next**
|
||||
- 样式是 **Tailwind 风格的 utility CSS**
|
||||
|
||||
确认依据:
|
||||
|
||||
- HTML 使用 `type="module"` 加载单入口 bundle
|
||||
- bundle 中可见 React 生产构建标识
|
||||
- 存在 `/`, `/home`, `/edit`, `/terms`, `/privacy`, `/admin/review` 等前端路由
|
||||
- CSS 中存在大量 `--tw-*` 变量与 utility class
|
||||
|
||||
## 5.2 UI 结构
|
||||
|
||||
【已确认】它不是纯 landing page,而是两层产品:
|
||||
|
||||
- 落地页:首页、功能说明、社区展示、定价入口
|
||||
- 工具页:`/edit`
|
||||
|
||||
这种拆法的好处是:
|
||||
|
||||
- SEO 与转化页保持轻量
|
||||
- 真正复杂的工具状态集中在编辑页
|
||||
|
||||
## 5.3 PWA 与缓存
|
||||
|
||||
【已确认】
|
||||
|
||||
- 有 `manifest.json`
|
||||
- 注册了 `serviceWorker`
|
||||
- `sw.js` 会缓存核心静态资源
|
||||
- 运行时采用近似 **network first + cache fallback**
|
||||
|
||||
这说明它把产品定位成:
|
||||
|
||||
- 可被收藏到手机桌面
|
||||
- 网络不稳时仍能较快打开外壳
|
||||
|
||||
---
|
||||
|
||||
## 6. 部署与托管
|
||||
|
||||
## 6.1 静态站托管
|
||||
|
||||
【已确认】
|
||||
|
||||
- 响应头里有 `x-vercel-id`
|
||||
- 也有 Cloudflare 相关头
|
||||
|
||||
这说明它大概率是:
|
||||
|
||||
- **Vercel 托管应用**
|
||||
- **Cloudflare 在前层做缓存 / 统计 / 防护**
|
||||
|
||||
## 6.2 API 组织方式
|
||||
|
||||
【已确认】所有业务接口都挂在同域 `/api/*` 下,例如:
|
||||
|
||||
- `/api/analyze-proxy`
|
||||
- `/api/transform-action`
|
||||
- `/api/generate-proxy`
|
||||
- `/api/generate-beta-actions`
|
||||
- `/api/remove-background`
|
||||
- `/api/payment/create`
|
||||
- `/api/payment/creem-create`
|
||||
- `/api/showcase`
|
||||
- `/api/user-actions`
|
||||
- `/api/utils?type=geo`
|
||||
- `/api/utils?type=stats`
|
||||
|
||||
【高概率推断】这类路径风格非常像:
|
||||
|
||||
- Vercel Serverless Functions
|
||||
- 或同类 Node/Edge API 层
|
||||
|
||||
其目的非常明确:
|
||||
|
||||
- 把模型供应商密钥藏到服务端
|
||||
- 把支付和鉴权放到服务端
|
||||
- 把不同模型调用统一封装成同域 API
|
||||
|
||||
---
|
||||
|
||||
## 7. 数据层与 BaaS 方案
|
||||
|
||||
## 7.1 Supabase 接入
|
||||
|
||||
【已确认】前端 bundle 中直接包含 Supabase 项目地址,并创建了 Supabase client。
|
||||
|
||||
可确认能力包括:
|
||||
|
||||
- `auth`
|
||||
- `storage`
|
||||
- `database`
|
||||
- `rpc`
|
||||
|
||||
## 7.2 已暴露出的主要表与存储桶
|
||||
|
||||
【已确认】
|
||||
|
||||
存储桶:
|
||||
|
||||
- `pixel-assets`
|
||||
- `showcase`
|
||||
|
||||
数据表:
|
||||
|
||||
- `user_assets`
|
||||
- `user_actions`
|
||||
- `user_credits`
|
||||
|
||||
RPC:
|
||||
|
||||
- `redeem_credits`
|
||||
|
||||
## 7.3 数据职责推断
|
||||
|
||||
【高概率推断】这些表/桶的职责大致如下:
|
||||
|
||||
### `user_assets`
|
||||
|
||||
- 保存用户生成结果
|
||||
- 主文件是 sprite sheet 的存储路径
|
||||
- 还会附带 `action_metadata`
|
||||
- `action_metadata` 里至少保存:
|
||||
- action 信息
|
||||
- `activeFrames`
|
||||
- `frameOrder`
|
||||
- `fps`
|
||||
|
||||
### `user_actions`
|
||||
|
||||
- 保存动作收藏 / 用户动作偏好
|
||||
- 也可能承载“每日刷新动作配额”相关信息
|
||||
|
||||
### `user_credits`
|
||||
|
||||
- 保存积分余额
|
||||
|
||||
### `showcase`
|
||||
|
||||
- 保存社区投稿所需的上下文图和结果 GIF
|
||||
- 可见字段包括:
|
||||
- `title`
|
||||
- `content`
|
||||
- `author`
|
||||
- `context_images`
|
||||
- `result_gifs`
|
||||
- `is_private`
|
||||
- `private_flags`
|
||||
|
||||
---
|
||||
|
||||
## 8. 生成链路拆解
|
||||
|
||||
## 8.1 不是单模型一步到位
|
||||
|
||||
【已确认】从接口命名和编辑页流程看,PixelMotion 至少拆成了 4 类能力:
|
||||
|
||||
1. 角色理解:`/api/analyze-proxy`
|
||||
2. 动作文本结构化:`/api/transform-action`
|
||||
3. 精灵表生成:`/api/generate-proxy`
|
||||
4. 背景去除:`/api/remove-background`
|
||||
|
||||
【高概率推断】这意味着它后端不是一个模型完成全部事情,而是多步骤流水线。
|
||||
|
||||
## 8.2 两种输入模式
|
||||
|
||||
【已确认】编辑器存在两种模式:
|
||||
|
||||
- `AI Character Analysis`:更慢、更精确
|
||||
- `Direct image reference`:更快
|
||||
|
||||
【高概率推断】对应两条链路:
|
||||
|
||||
### 模式 A:分析后生成
|
||||
|
||||
1. 上传图片
|
||||
2. 调 `analyze-proxy`
|
||||
3. 把角色描述文本 + 动作模板一起送去生成
|
||||
|
||||
优点:
|
||||
|
||||
- 更容易抽出稳定的角色语义
|
||||
- 对脏图、照片、复杂背景更友好
|
||||
|
||||
缺点:
|
||||
|
||||
- 多一次模型调用
|
||||
- 时延更高
|
||||
|
||||
### 模式 B:直接图参考
|
||||
|
||||
1. 上传图片
|
||||
2. 跳过分析
|
||||
3. 直接把参考图 base64 传给 `generate-proxy`
|
||||
|
||||
优点:
|
||||
|
||||
- 更快
|
||||
|
||||
缺点:
|
||||
|
||||
- 对输入质量更敏感
|
||||
|
||||
## 8.3 自定义动作不是直接裸喂一句话
|
||||
|
||||
【已确认】自定义动作会先走 `/api/transform-action`。
|
||||
|
||||
【高概率推断】这一步的作用是:
|
||||
|
||||
- 把用户自由文本改写成更结构化的动作描述
|
||||
- 降低“自定义动作太抽象、太文学化”导致的失控
|
||||
|
||||
这其实非常关键,因为 PixelMotion 并不是让用户把任何想法直接丢给绘图模型,而是先做一层“动作编排翻译”。
|
||||
|
||||
## 8.4 最终输出契约
|
||||
|
||||
【已确认】`generate-proxy` 返回的是单个 `imageBase64`,前端将其当成一张 4x4 精灵表处理。
|
||||
|
||||
【高概率推断】服务端最终对前端暴露的契约就是:
|
||||
|
||||
- 输入:prompt + 可选参考图
|
||||
- 输出:一张最终可切片的 Sprite Sheet
|
||||
|
||||
这有两个可能实现:
|
||||
|
||||
### 方案 1:直接让模型产出 4x4 精灵表
|
||||
|
||||
优点:
|
||||
|
||||
- 前端最简单
|
||||
- 一次请求得到最终结果
|
||||
|
||||
风险:
|
||||
|
||||
- 帧间一致性很难控
|
||||
- 容易出现局部漂移
|
||||
|
||||
### 方案 2:后端逐帧生成后再拼表
|
||||
|
||||
优点:
|
||||
|
||||
- 更可做重试和筛帧
|
||||
|
||||
风险:
|
||||
|
||||
- 成本更高
|
||||
- 延迟更大
|
||||
|
||||
【我的判断】结合它前端动作模板的写法,PixelMotion 当前更像是**强约束 prompt 直接产出整张精灵表**,或者至少对模型暴露的是“直接出 16 帧表”的任务,而不是先生成视频再切帧。
|
||||
|
||||
---
|
||||
|
||||
## 9. 为什么它比普通图生图更稳
|
||||
|
||||
PixelMotion 真正有价值的不是“模型多强”,而是它把自由度砍掉了。
|
||||
|
||||
## 9.1 固定 16 帧、固定网格
|
||||
|
||||
【已确认】
|
||||
|
||||
- 固定 `4 x 4`
|
||||
- 固定 `16` 帧
|
||||
|
||||
好处:
|
||||
|
||||
- 模型目标明确
|
||||
- 导出逻辑简单
|
||||
- 编辑器也更容易做
|
||||
|
||||
## 9.2 动作模板非常强约束
|
||||
|
||||
【已确认】游戏动作模板不是只写“run”“attack”这种词,而是把动作分成明确阶段:
|
||||
|
||||
- 起势
|
||||
- 主动作
|
||||
- 收势 / 回正
|
||||
|
||||
并且强制约束:
|
||||
|
||||
- 始终朝同一方向
|
||||
- 不允许左右镜像翻转
|
||||
- 身体水平位置固定
|
||||
- 武器不换手
|
||||
- 循环动作必须首尾闭合
|
||||
|
||||
【高概率推断】这类 prompt 工程是 PixelMotion 稳定性的核心来源之一。
|
||||
|
||||
它本质上是在把“动画导演规则”提前写进 prompt,而不是把结果完全交给模型即兴发挥。
|
||||
|
||||
## 9.3 后编辑能力兜底
|
||||
|
||||
【已确认】它允许:
|
||||
|
||||
- 拖拽调整帧序
|
||||
- 双击隐藏坏帧
|
||||
- 调整 FPS
|
||||
- 保存布局
|
||||
|
||||
这一步非常重要,因为 AI 结果不可能永远 16 帧都完美。
|
||||
|
||||
PixelMotion 的思路不是追求“模型一次全对”,而是:
|
||||
|
||||
- 先把结果做出来
|
||||
- 再让用户低成本修整
|
||||
|
||||
这比试图用一次模型调用直接得到完美动画更现实。
|
||||
|
||||
## 9.4 导出链路放到浏览器
|
||||
|
||||
【已确认】
|
||||
|
||||
- PNG 导出使用 canvas 重排
|
||||
- GIF 导出使用 `gif.js`
|
||||
- 透明背景导出时按需调用 `/api/remove-background`
|
||||
|
||||
这意味着:
|
||||
|
||||
- 昂贵的 AI 只负责生成主资产
|
||||
- 廉价的导出和重排交给前端本地完成
|
||||
|
||||
这个分工非常合理。
|
||||
|
||||
---
|
||||
|
||||
## 10. 编辑器能力拆解
|
||||
|
||||
## 10.1 编辑页不是“看结果页”,而是轻量资产编辑器
|
||||
|
||||
【已确认】编辑页至少具备这些能力:
|
||||
|
||||
- 查看单张 sprite sheet
|
||||
- 播放动画预览
|
||||
- 调 FPS
|
||||
- 隐藏帧
|
||||
- 调整帧顺序
|
||||
- 保存布局
|
||||
- 导出 PNG
|
||||
- 导出 GIF
|
||||
- 多选导出 Set
|
||||
|
||||
## 10.2 布局持久化
|
||||
|
||||
【已确认】保存布局时会把状态写回 `user_assets.action_metadata`。
|
||||
|
||||
这说明它没有把“AI 结果”和“人工修整结果”分离成两套系统,而是把二次编辑直接沉淀到资产元数据里。
|
||||
|
||||
这个做法很实用,因为:
|
||||
|
||||
- 同一资产可反复继续编辑
|
||||
- 资产库里保存的是“可用版本”,不是原始毛坯
|
||||
|
||||
---
|
||||
|
||||
## 11. 社区与增长设计
|
||||
|
||||
## 11.1 Showcase
|
||||
|
||||
【已确认】首页有公开社区展示,数据来自 `/api/showcase?limit=20`。
|
||||
|
||||
公开返回里能看到:
|
||||
|
||||
- 原始上下文图
|
||||
- 结果 GIF
|
||||
- 作者名
|
||||
- 标题和故事文案
|
||||
|
||||
这说明 PixelMotion 不是只强调“工具效率”,还在做:
|
||||
|
||||
- UGC 内容传播
|
||||
- 首页案例社证
|
||||
- 情绪价值展示
|
||||
|
||||
## 11.2 审核后台
|
||||
|
||||
【已确认】前端存在 `/admin/review` 页面,审核通过后可发布 Showcase。
|
||||
|
||||
说明社区能力不是完全开放式自动发布,而是有人工审核流。
|
||||
|
||||
## 11.3 隐私模式
|
||||
|
||||
【已确认】Showcase 数据里存在:
|
||||
|
||||
- `is_private`
|
||||
- `private_flags`
|
||||
|
||||
【高概率推断】它支持对原始参考图做局部隐私隐藏或模糊,只公开结果,不完全公开全部上下文。
|
||||
|
||||
---
|
||||
|
||||
## 12. 账号、积分与支付
|
||||
|
||||
## 12.1 账号体系
|
||||
|
||||
【已确认】
|
||||
|
||||
- 使用 Supabase Auth
|
||||
- 支持邮箱注册登录
|
||||
- 支持找回密码
|
||||
- 支持 OAuth 登录入口
|
||||
|
||||
## 12.2 积分体系
|
||||
|
||||
【已确认】
|
||||
|
||||
- 生成主流程消耗 credits
|
||||
- 刷新动作推荐也可能消耗 credits
|
||||
- 前端存在 `NO_CREDITS`、`freeCreditsExhausted` 等状态
|
||||
- 可通过 `redeem_credits` 兑换码充积分
|
||||
|
||||
## 12.3 定价方案
|
||||
|
||||
【已确认】前端内置了三档套餐:
|
||||
|
||||
| 套餐 | 人民币 | 美元 | 基础积分 | 赠送 |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| `basic` | `9.9` | `4.99` | `5` | `0` |
|
||||
| `pro` | `49.5` | `24.9` | `25` | `3` |
|
||||
| `studio` | `99` | `46.9` | `50` | `10` |
|
||||
|
||||
## 12.4 区域支付分流
|
||||
|
||||
【已确认】
|
||||
|
||||
- 中国区:`/api/payment/create`
|
||||
- 国际区:`/api/payment/creem-create`
|
||||
|
||||
结合文案还可确认:
|
||||
|
||||
- 中国区支持微信 / 支付宝语境
|
||||
- 国际区有信用卡语境
|
||||
|
||||
这说明它做了明显的地域分流支付设计。
|
||||
|
||||
---
|
||||
|
||||
## 13. 一个更完整的技术架构图
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["首页 / 编辑页<br/>React SPA"] --> B["同域 API 层<br/>/api/*"]
|
||||
A --> C["Supabase Auth"]
|
||||
A --> D["Supabase Storage / DB"]
|
||||
B --> E["角色分析服务<br/>analyze-proxy"]
|
||||
B --> F["动作文本结构化<br/>transform-action"]
|
||||
B --> G["精灵表生成服务<br/>generate-proxy"]
|
||||
B --> H["去背景服务<br/>remove-background"]
|
||||
B --> I["支付服务<br/>China / Intl"]
|
||||
G --> J["返回 4x4 Sprite Sheet PNG"]
|
||||
J --> A
|
||||
A --> K["本地导出层<br/>Canvas + gif.js"]
|
||||
A --> L["Showcase 投稿"]
|
||||
L --> D
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 14. 如果我们复刻,最该学什么
|
||||
|
||||
如果要学 PixelMotion,我认为最值得学的不是 UI,而是下面 6 个工程判断。
|
||||
|
||||
## 14.1 先把资产格式定死
|
||||
|
||||
不要一开始就追求:
|
||||
|
||||
- 任意帧数
|
||||
- 任意布局
|
||||
- 任意动作长度
|
||||
|
||||
先定死一个可生产、可编辑、可导出的资产标准,例如:
|
||||
|
||||
- 16 帧
|
||||
- 4x4
|
||||
- PNG sprite sheet
|
||||
|
||||
## 14.2 模型前面一定要有动作模板层
|
||||
|
||||
用户说“跑步”“攻击”“比心”,不应该原样送给模型。
|
||||
|
||||
应该先转换成:
|
||||
|
||||
- 朝向约束
|
||||
- 位移约束
|
||||
- 起承转合
|
||||
- 武器规则
|
||||
- 循环规则
|
||||
|
||||
## 14.3 允许生成后修帧
|
||||
|
||||
不要试图只靠模型一次完成。
|
||||
|
||||
至少要有:
|
||||
|
||||
- 隐藏帧
|
||||
- 排序
|
||||
- FPS
|
||||
|
||||
否则大量“差一点能用”的结果会直接报废。
|
||||
|
||||
## 14.4 导出放前端,本地完成
|
||||
|
||||
像 PNG 重排、GIF 预览、透明导出这类低成本操作,完全没必要都放到后端。
|
||||
|
||||
## 14.5 资产库和社区是增长飞轮
|
||||
|
||||
PixelMotion 不只是一个生成按钮,它还做了:
|
||||
|
||||
- 我的资产库
|
||||
- 作品展示
|
||||
- 投稿审核
|
||||
- 分享内容化
|
||||
|
||||
这能把一次性生成工具,变成可留存产品。
|
||||
|
||||
## 14.6 支付、积分、地域适配要早做
|
||||
|
||||
它不是等产品成熟后才补商业化,而是从一开始就把:
|
||||
|
||||
- credits
|
||||
- 兑换码
|
||||
- 地域支付
|
||||
- 国际支付
|
||||
|
||||
串进主流程里了。
|
||||
|
||||
---
|
||||
|
||||
## 15. 对 PixelMotion 后端方案的推断版复原
|
||||
|
||||
下面给一个我认为比较接近它真实实现的“后端内部分层草图”。
|
||||
|
||||
## 15.1 推荐复原结构
|
||||
|
||||
### API Gateway 层
|
||||
|
||||
- 鉴权
|
||||
- 积分扣减
|
||||
- 配额判断
|
||||
- 路由到不同 AI 服务
|
||||
|
||||
### Prompt Builder 层
|
||||
|
||||
- 读取动作模板
|
||||
- 合成角色描述
|
||||
- 合成单方向、单中心点、16 帧规则
|
||||
|
||||
### Generation Worker 层
|
||||
|
||||
- 调视觉模型生成 sprite sheet
|
||||
- 失败时重试
|
||||
- 返回 base64 PNG
|
||||
|
||||
### Asset Service 层
|
||||
|
||||
- 存储到 `pixel-assets`
|
||||
- 写 `user_assets`
|
||||
- 保存布局元数据
|
||||
|
||||
### Export Layer
|
||||
|
||||
- 前端本地切帧
|
||||
- 前端本地生成 GIF
|
||||
|
||||
### Community Layer
|
||||
|
||||
- 投稿
|
||||
- 审核
|
||||
- 公开展示
|
||||
|
||||
---
|
||||
|
||||
## 16. 风险与局限
|
||||
|
||||
因为没有后端源码,本拆解仍有边界。
|
||||
|
||||
## 16.1 不能确认的部分
|
||||
|
||||
- 实际调用了哪一家模型供应商
|
||||
- 是单次整图生成,还是逐帧生成后拼图
|
||||
- 支付后端的真实实现细节
|
||||
- 服务端有没有额外的安全审核与图像后处理
|
||||
|
||||
## 16.2 但可以较确定的部分
|
||||
|
||||
- 前端是 React + Vite 风格 SPA
|
||||
- 数据层是 Supabase
|
||||
- 输出主资产是 16 帧 sprite sheet
|
||||
- 编辑器支持帧编辑和本地导出
|
||||
- 产品采用积分制
|
||||
- 社区、审核、展示是正式能力,不是临时 demo
|
||||
|
||||
---
|
||||
|
||||
## 17. 最终判断
|
||||
|
||||
PixelMotion 的成功点,不在于它“偷偷用了某个神秘模型”,而在于它把一个本来极不稳定的问题,拆成了一个高度约束的资产生产流程:
|
||||
|
||||
- 输入被约束
|
||||
- 动作被模板化
|
||||
- 输出被标准化
|
||||
- 错误被编辑器兜底
|
||||
- 结果被资产库与社区承接
|
||||
|
||||
如果我们要借鉴它,最应该借鉴的是这套**产品约束 + 资产契约 + 编辑兜底**的整体设计,而不是只学“上传一张图点生成”这层表面交互。
|
||||
|
||||
---
|
||||
|
||||
## 18. 资料来源
|
||||
|
||||
本次拆解实际使用了以下公开来源:
|
||||
|
||||
- 官网首页:
|
||||
[https://www.pixelmotion.art/](https://www.pixelmotion.art/)
|
||||
- PWA Manifest:
|
||||
[https://www.pixelmotion.art/manifest.json](https://www.pixelmotion.art/manifest.json)
|
||||
- Service Worker:
|
||||
[https://www.pixelmotion.art/sw.js](https://www.pixelmotion.art/sw.js)
|
||||
- 公开统计接口:
|
||||
[https://www.pixelmotion.art/api/utils?type=stats](https://www.pixelmotion.art/api/utils?type=stats)
|
||||
- 公开 GIF 列表接口:
|
||||
[https://www.pixelmotion.art/api/utils?type=gifs](https://www.pixelmotion.art/api/utils?type=gifs)
|
||||
- 公开 Showcase 接口示例:
|
||||
[https://www.pixelmotion.art/api/showcase?limit=3](https://www.pixelmotion.art/api/showcase?limit=3)
|
||||
- 首页前端 bundle 与样式产物:
|
||||
- `https://www.pixelmotion.art/assets/index-BHdWGq2s.js`
|
||||
- `https://www.pixelmotion.art/assets/index-DFENcMpn.css`
|
||||
|
||||
243
docs/PROJECT_DEVELOPMENT_EXPERIENCE.md
Normal file
243
docs/PROJECT_DEVELOPMENT_EXPERIENCE.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# AI Native Visual RPG 开发经验沉淀
|
||||
|
||||
## 1. 项目特点判断
|
||||
|
||||
这个项目不是单纯的“像素 UI 项目”,而是 4 条链路同时耦合的项目:
|
||||
|
||||
1. 叙事链路:AI 生成剧情文本与选项
|
||||
2. 状态链路:玩家、怪物、NPC、背包、好感、同伴、场景流转
|
||||
3. 演出链路:战斗计划、动画播放、投射物、特效、镜头位移
|
||||
4. 界面链路:选择世界、选择角色、冒险页、背包页、地图弹窗、编辑器页
|
||||
|
||||
经验:
|
||||
|
||||
- 做功能前先判断它主要影响哪几条链路。
|
||||
- 如果一个需求同时影响“状态 + 演出 + UI”,不要只改一个点。
|
||||
- 像“初始同伴”这种功能,本质上不是 UI 需求,而是“选角流程 + 初始 encounter + NPC 好感 + 招募状态”的组合需求。
|
||||
|
||||
## 2. 先做数据建模,再做 UI
|
||||
|
||||
这一类项目最容易犯的错误,是先加按钮、再补状态。
|
||||
|
||||
实践下来更稳的顺序是:
|
||||
|
||||
1. 先把状态字段补齐
|
||||
2. 再补工具函数
|
||||
3. 再接交互入口
|
||||
4. 最后补展示层
|
||||
|
||||
已经验证有效的状态字段包括:
|
||||
|
||||
- `playerInventory`
|
||||
- `npcStates`
|
||||
- `companions`
|
||||
- `currentBattleNpcId`
|
||||
- `currentEncounter`
|
||||
- `playerActionMode`
|
||||
- `activeCombatEffects`
|
||||
|
||||
经验:
|
||||
|
||||
- “能否交易 / 能否招募 / 送礼涨多少好感”都应该由状态和规则函数决定,不能写死在按钮文本里。
|
||||
- NPC 交互尽量走本地规则,不要依赖 AI 即时决定关键数值。
|
||||
|
||||
## 3. 复杂页面一定要拆流程层
|
||||
|
||||
`App.tsx` 一旦同时承载:
|
||||
|
||||
- 游戏主状态
|
||||
- 剧情生成
|
||||
- 战斗播放
|
||||
- NPC 交互
|
||||
- 地图
|
||||
- 选角
|
||||
|
||||
就会迅速失控。
|
||||
|
||||
当前更合理的分层思路是:
|
||||
|
||||
- `useGameFlow`
|
||||
负责基础游戏状态、世界选择、角色选择、初始进入逻辑
|
||||
- `useCombatFlow`
|
||||
负责战斗计划与播放
|
||||
- `useStoryGeneration`
|
||||
负责剧情生成、NPC 本地交互分流、选项池管理
|
||||
- `useNpcInteractionFlow`
|
||||
负责同伴/NPC 展示态
|
||||
- `GameShell`
|
||||
负责主容器与选择流程
|
||||
- `AdventurePanel`
|
||||
负责冒险页文本和选项
|
||||
- `NpcModals`
|
||||
负责交易 / 送礼 / 放生招募等弹窗
|
||||
|
||||
经验:
|
||||
|
||||
- 流程层优先按“职责”拆,不按“文件长度”拆。
|
||||
- 状态修改逻辑尽量集中到 hook 内,不要散落在多个组件按钮回调里。
|
||||
|
||||
## 4. AI 只适合生成叙事,不适合决定关键规则
|
||||
|
||||
实践中最稳定的策略是:
|
||||
|
||||
- AI 负责:
|
||||
- `storyText`
|
||||
- 非 NPC 关键规则的普通探索选项文案
|
||||
- 本地规则负责:
|
||||
- NPC 交互选项
|
||||
- 交易合法性
|
||||
- 礼物好感值
|
||||
- 招募阈值
|
||||
- 战斗掉落
|
||||
- 帮助奖励
|
||||
|
||||
经验:
|
||||
|
||||
- 凡是会影响数值平衡、背包物品、好感、队伍成员的部分,都不要交给 AI 即时决定。
|
||||
- AI 生成内容要能被本地规则兜底,否则体验会不稳定。
|
||||
|
||||
## 5. NPC 系统要“角色型 NPC”和“普通 NPC”分开处理
|
||||
|
||||
项目里 NPC 实际上有两种:
|
||||
|
||||
1. 普通场景 NPC:可用通用 Medieval NPC 渲染
|
||||
2. 角色型 NPC:应该复用玩家角色对应的立绘和动画
|
||||
|
||||
经验:
|
||||
|
||||
- 只看 `encounter.kind === 'npc'` 不够,还要看 `encounter.characterId`。
|
||||
- 有 `characterId` 的 NPC,应该优先走 `CharacterAnimator`。
|
||||
- 否则就会出现:
|
||||
- 选了某个同伴,但开场看到的是另一套通用 NPC 外观
|
||||
- 战斗里角色型 NPC 看起来像普通路人
|
||||
|
||||
## 6. 位置与朝向必须统一到一套坐标规则
|
||||
|
||||
这是这类项目里最容易反复返工的点。
|
||||
|
||||
实践中踩过的坑:
|
||||
|
||||
- `sceneMonsters` 用一套坐标逻辑
|
||||
- `currentEncounter` 用另一套坐标逻辑
|
||||
- 结果开场 NPC 和遇怪站位不一致
|
||||
- 角色型 NPC 立绘比怪物更容易出现“脚没落地”“太小”“翻转方向错”
|
||||
|
||||
最终经验:
|
||||
|
||||
- 对面实体的横向定位必须统一到“怪物那套 world-space 逻辑”
|
||||
- 也就是:
|
||||
- 位置统一用怪物侧的 anchor
|
||||
- 相机平移时统一跟随同一套计算
|
||||
- 角色型 NPC 的垂直位置不能偷懒固定
|
||||
- 应该结合角色自身 `groundOffsetY`
|
||||
- 朝向规则要统一使用 `getFacingTowardPlayer`
|
||||
|
||||
一句话总结:
|
||||
|
||||
- 角色型 NPC 不应该单独发明一套站位系统,而应该尽量复用怪物对位系统。
|
||||
|
||||
## 7. “新增流程”不要破坏原有选择 UI
|
||||
|
||||
这次初始同伴功能就是一个典型经验:
|
||||
|
||||
- 用户原本对“选择扮演角色”的视觉和交互已经形成预期
|
||||
- 如果为了加“初始同伴选择”直接把角色选择页改成另一种样式,会造成明显割裂
|
||||
|
||||
经验:
|
||||
|
||||
- 新流程优先插在旧流程后面,而不是重写旧流程
|
||||
- “确认角色 -> 选择初始同伴 -> 进入冒险”比“把原选角页改成全新样式”风险小很多
|
||||
- 如果必须改 UI,也要尽量保留旧页面的视觉结构和交互节奏
|
||||
|
||||
## 8. 冒险页布局要优先保证画面和选项完整可见
|
||||
|
||||
冒险页真正的优先级是:
|
||||
|
||||
1. 上方画布要在一屏内正常显示
|
||||
2. 下方 3 个选项要在一屏内正常显示
|
||||
3. 剧情文本框剩余空间自适应
|
||||
|
||||
经验:
|
||||
|
||||
- 文本框不能无限长撑开
|
||||
- 正确做法是:
|
||||
- 文本框高度自适应剩余空间
|
||||
- 文本超长时内部滚动
|
||||
- 不能让 `storyText` 把战斗画面和选项挤出首屏
|
||||
|
||||
## 9. 编辑器页和玩家页要明确隔离
|
||||
|
||||
当前项目里存在编辑器页面:
|
||||
|
||||
- `PresetEditor`
|
||||
- `NpcVisualEditor`
|
||||
- `StateFunctionEditor`
|
||||
|
||||
玩家页和编辑器页的需求完全不同。
|
||||
|
||||
经验:
|
||||
|
||||
- 像字体切换、视觉统一这种全局改动,不要直接打到整个站点
|
||||
- 应该只挂在非编辑器根容器上
|
||||
- 比如 `fusion-pixel-app` 这种类,只挂在正式游玩界面,不挂在编辑器根节点
|
||||
|
||||
## 10. 构建环境问题要项目内消化
|
||||
|
||||
实际踩到的构建问题:
|
||||
|
||||
- Node 16 环境下,Vite 构建会因为 `crypto.getRandomValues` 缺失报错
|
||||
|
||||
沉淀出的解决方式:
|
||||
|
||||
- 不强依赖开发机立刻升级 Node
|
||||
- 在项目内增加 Vite 启动包装脚本
|
||||
- 统一让 `dev / build / preview` 都走这层 shim
|
||||
|
||||
经验:
|
||||
|
||||
- 环境兼容问题如果能在项目内吸收,就尽量不要把负担转移给每个协作者
|
||||
- 文档里要明确记录“为什么这样做”
|
||||
|
||||
## 11. 比较稳的开发顺序
|
||||
|
||||
后续继续扩展功能时,建议遵守这个顺序:
|
||||
|
||||
1. 写状态字段
|
||||
2. 写规则工具函数
|
||||
3. 写流程 hook
|
||||
4. 接 UI
|
||||
5. 跑 `npm run lint`
|
||||
6. 跑 `npm run build`
|
||||
7. 再做视觉微调
|
||||
|
||||
不要反过来做:
|
||||
|
||||
- 先做 UI
|
||||
- 再补状态
|
||||
- 最后硬修流程
|
||||
|
||||
这种顺序在状态复杂的项目里会越改越乱。
|
||||
|
||||
## 12. 当前最值得继续坚持的原则
|
||||
|
||||
- 保持 AI 生成和本地规则分工清晰
|
||||
- 保持角色型 NPC 与普通 NPC 的渲染分流
|
||||
- 保持“怪物 / encounter / 战斗 NPC”统一坐标系
|
||||
- 保持新增功能不破坏既有核心 UI 体验
|
||||
- 保持编辑器页与玩家页隔离
|
||||
- 每次大改后都用 `lint + build` 双重验证
|
||||
|
||||
## 13. 后续建议
|
||||
|
||||
下一阶段最值得继续沉淀的方向:
|
||||
|
||||
1. 把 NPC 交互逻辑继续从 `useStoryGeneration` 中独立成更纯粹的 `useNpcInteractionFlow`
|
||||
2. 把角色型 NPC 的位置、缩放、贴地参数做成可配置规则,而不是继续散落在画布里微调
|
||||
3. 针对初始同伴流程补一份单独的状态图 / 时序图
|
||||
4. 对大 chunk 警告做代码分包
|
||||
|
||||
## 14. 一句话总结
|
||||
|
||||
这个项目真正的开发经验不是“怎么多写一个按钮”,而是:
|
||||
|
||||
- 在 AI 叙事、像素演出、战斗状态、NPC 规则、选择流程和编辑器体系同时存在的情况下,始终让每条链路各归其位。
|
||||
266
docs/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md
Normal file
266
docs/PROJECT_WORK_EXPERIENCE_PLAYBOOK.md
Normal file
@@ -0,0 +1,266 @@
|
||||
# 奇幻酒馆项目开发经验手册
|
||||
|
||||
日期:`2026-03-24`
|
||||
|
||||
## 1. 项目本质判断
|
||||
|
||||
这个项目不是单纯的前端页面项目,也不是单纯的大模型接入项目,而是 4 条链路同时存在的复合型项目:
|
||||
|
||||
1. 叙事链路:剧情文本、选项文本、角色对话、聊天上下文。
|
||||
2. 状态链路:角色、怪物、NPC、场景、背包、装备、战斗状态、CD、蓝耗、死亡、掉落。
|
||||
3. 演出链路:角色进场、近战贴身、受击、死亡、逃跑、镜头跟随、场景切换、前探预览。
|
||||
4. 工具链路:NPC 形象编辑器、行为编辑器、预设数据、校验脚本、本地调试环境。
|
||||
|
||||
经验结论:
|
||||
|
||||
- 任何需求只要影响两条以上链路,就不能只改 UI。
|
||||
- 先判断需求落在哪些链路,再决定改哪些文件。
|
||||
- 这类项目最怕“看起来改好了”,但状态、动画、提示词和运行逻辑其实没对齐。
|
||||
|
||||
## 2. 架构拆分经验
|
||||
|
||||
已经验证更稳的结构是:
|
||||
|
||||
- `App.tsx` 保持尽量薄,只做外层挂载。
|
||||
- `GameShell.tsx` 负责主流程壳层:开始页、世界选择、角色选择、游戏内主界面切换。
|
||||
- 各类状态和流程尽量放进 hooks:
|
||||
- `useGameFlow`
|
||||
- `useCombatFlow`
|
||||
- `useStoryGeneration`
|
||||
- `useNpcInteractionFlow`
|
||||
- `useInventoryFlow`
|
||||
- `useEquipmentFlow`
|
||||
- 各类独立 UI 面板拆成独立组件:
|
||||
- `AdventurePanel`
|
||||
- `CharacterPanel`
|
||||
- `InventoryPanel`
|
||||
- `MapModal`
|
||||
- `AdventureEntityModal`
|
||||
|
||||
经验结论:
|
||||
|
||||
- 按职责拆,不按文件长度拆。
|
||||
- 状态修改尽量集中在 hook 内,不要散落在多个按钮回调里。
|
||||
- 预览系统、编辑器系统、正式游戏流程要尽量复用同一套业务逻辑,而不是各写一套。
|
||||
|
||||
## 3. AI 与本地规则的边界
|
||||
|
||||
目前最稳定的边界是:
|
||||
|
||||
- AI 负责生成:
|
||||
- `storyText`
|
||||
- `actionText`
|
||||
- 对话文本
|
||||
- 基于上下文的语气、叙事张力、选项措辞
|
||||
- 本地规则负责决定:
|
||||
- 哪些 function 当前可执行
|
||||
- 技能是否在 CD
|
||||
- 蓝量是否足够
|
||||
- 伤害、回复、掉血、死亡、掉落
|
||||
- 场景切换是否合法
|
||||
- 背包、装备、属性、队伍变化
|
||||
|
||||
经验结论:
|
||||
|
||||
- 涉及数值、资源、状态迁移的部分,不要交给模型临场决定。
|
||||
- 模型应被限制在“叙事表达层”,不要替代规则系统。
|
||||
- 用户每次选择后只做一次模型推理,因此提示词必须一次性包含足够边界,不能再依赖后处理改写和过滤。
|
||||
|
||||
## 4. Function 驱动设计经验
|
||||
|
||||
当前更合理的设计方向是:
|
||||
|
||||
- 所有可选行为都落到 function。
|
||||
- 每个 function 只有这些核心字段:
|
||||
- `id`
|
||||
- `text`
|
||||
- `description`
|
||||
- `category`
|
||||
- `功能代码`
|
||||
- 运行时先汇总当前场景的 function,再结合角色、状态、敌人、场景实体、资源状态做合法性过滤。
|
||||
- 模型只在“当前合法 function 集合”内生成选项文本和剧情文本。
|
||||
|
||||
经验结论:
|
||||
|
||||
- 选项不是自由散文,而是 function 的叙事包装。
|
||||
- function 必须和角色、状态、场景、实体绑定,不能做成完全漂浮的公共动作池。
|
||||
- “每次选择只能并且必须命中一个 function” 是保持状态稳定的关键。
|
||||
|
||||
## 5. 战斗与动画的经验
|
||||
|
||||
战斗部分已经沉淀出的几个重要规则:
|
||||
|
||||
### 5.1 结算时机
|
||||
|
||||
- 用户点击选项后,后台状态应立即推进到“动画完成后的结果状态”,并把这个新状态放入后续推理上下文。
|
||||
- 但扣血、死亡表现、消失等视觉结果要在动画播完后再体现在画面上。
|
||||
|
||||
这样做的好处:
|
||||
|
||||
- 模型推理可以立即开始,不被动画阻塞。
|
||||
- 画面依然保留正确的演出节奏。
|
||||
- 状态、上下文、下一轮推理不会落后于动画。
|
||||
|
||||
### 5.2 近战位移
|
||||
|
||||
- 近战动画不能按固定像素移动。
|
||||
- 必须按左右锚点、舞台宽度和目标位置计算真实接近距离。
|
||||
- 宽屏下如果只“往前走一点点”,基本就是位移基准用了屏幕百分比或固定偏移,而不是双方真实锚点差值。
|
||||
|
||||
### 5.3 死亡与退出战斗
|
||||
|
||||
- 怪物血量归零时先播死亡动画。
|
||||
- 死亡动画完成后怪物再消失。
|
||||
- 若敌方死亡,战斗状态应退出,切回空闲状态,并重新切换可用 function 集合。
|
||||
|
||||
### 5.4 逃跑与压迫感
|
||||
|
||||
- 逃跑镜头要跟着玩家,不是跟怪物。
|
||||
- 玩家向前跑,怪物被越甩越远,才能形成“甩开追兵”的感觉。
|
||||
- 相反,如果镜头锚在怪物侧,会让玩家像没动一样。
|
||||
|
||||
## 6. 遭遇、场景与地图经验
|
||||
|
||||
这部分已经验证有效的设计原则:
|
||||
|
||||
- 场景和背景图一一绑定,不切场景就不换背景。
|
||||
- 场景切换必须由选项命中的 function 驱动,而不是每回合自动变化。
|
||||
- 每个场景预设自己的怪物、NPC、宝藏池。
|
||||
- 玩家“继续向前探路”时,不是立刻随机弹结果,而是先预生成前方实体,再通过移动和镜头演出把它带到正式交互位。
|
||||
|
||||
经验结论:
|
||||
|
||||
- “探索”也应该是一种可演出的 function,而不只是一次文字刷新。
|
||||
- 同一时刻只应遇到一个主实体,避免一回合同时出现多只怪物造成状态混乱。
|
||||
- 地图系统适合做成场景连接图,由具体 function 触发前往某个场景。
|
||||
|
||||
## 7. 移动端优先的 UI 经验
|
||||
|
||||
已经验证有效的 UI 方向:
|
||||
|
||||
- 先保证移动端一屏成立,再兼容网页宽屏。
|
||||
- 开始页、世界选择、角色选择都要像游戏流程,而不是产品介绍页。
|
||||
- 世界选择适合纵向焦点滑动。
|
||||
- 角色选择适合横向翻卡和中心焦点展示。
|
||||
- 冒险页中:
|
||||
- 上方是画面演出
|
||||
- 中间是自适应剧情文本
|
||||
- 底部是固定的选项与功能入口
|
||||
|
||||
经验结论:
|
||||
|
||||
- 剧情区域不能写死高度,应填满画面下方和底部操作条上方之间的剩余空间。
|
||||
- 队伍、背包更适合作为弹出面板,不应该切走主流程。
|
||||
- 地图弹窗、队伍面板、背包详情都必须按手机窄屏重新组织,不要沿用桌面弹窗思路。
|
||||
|
||||
## 8. 选择页与游戏内界面经验
|
||||
|
||||
这轮迭代里比较明确的 UI 结论有:
|
||||
|
||||
- 开始页只保留核心按钮,视觉简洁更像正式游戏。
|
||||
- 世界选择页要突出当前聚焦卡,其他卡随滑动连续改变透明度和大小。
|
||||
- 角色选择页中:
|
||||
- 中间卡片不能模糊
|
||||
- 左右卡片要做正确方向的倾斜
|
||||
- 中间角色可播放 `run` 动画
|
||||
- 角色名称显示在动画下方
|
||||
- 属性和背景信息要紧凑,不能压缩掉上方卡片滑动空间
|
||||
- 游戏内按钮应尽量图标化,把更多空间留给剧情和选项。
|
||||
|
||||
## 9. NPC 形象编辑器经验
|
||||
|
||||
NPC 编辑器这部分已经沉淀出一些通用原则:
|
||||
|
||||
- 这套 Medieval Fantasy Characters 素材不是传统逐帧全身序列帧,发型、脸型、胡子、武器等部件不能按“每帧都变化”的思路处理。
|
||||
- 主手武器、副手武器、手、身体、头部、头饰需要明确图层关系。
|
||||
- 主手武器必须真正握在手里,不能只靠大概位置“看起来差不多”。
|
||||
- 因为素材高度重叠,编辑器里必须支持:
|
||||
- 底部组件模块点击选择
|
||||
- 键盘上下左右微调
|
||||
- 拖拽微调
|
||||
- 回滚按钮
|
||||
|
||||
经验结论:
|
||||
|
||||
- 视觉编辑器如果没有“可控选择 + 微调 + 回滚”,用户会很难调重叠素材。
|
||||
- 选项命名不能直接暴露英文源文件名,最好转成更贴近视觉理解的中文名称。
|
||||
- 编辑器产出的相对位置数据必须能直接落回项目运行时,不然就只是一个孤立工具。
|
||||
|
||||
## 10. 大模型接入与本地调试经验
|
||||
|
||||
这部分踩坑非常集中,结论也比较清晰:
|
||||
|
||||
### 10.1 前端不能直连目标模型接口
|
||||
|
||||
- 浏览器直连时会遇到 CORS。
|
||||
- 即使接口本身可用,浏览器环境也可能被跨域限制。
|
||||
- 更稳的方案是在开发服务器侧做代理,再由前端请求本地 `/api/llm/...`。
|
||||
|
||||
### 10.2 需要日志,但日志要聚焦
|
||||
|
||||
已经证明有价值的日志包括:
|
||||
|
||||
- 单次推理耗时
|
||||
- 原始提示词文本
|
||||
- 原始返回中的解析后文本
|
||||
- 失败时的模型名、状态码和错误正文
|
||||
|
||||
### 10.3 开发服务器要统一入口
|
||||
|
||||
本项目本地正确启动方式应统一走:
|
||||
|
||||
```bash
|
||||
node scripts/vite-cli.mjs --port=3000 --host=0.0.0.0
|
||||
```
|
||||
|
||||
经验结论:
|
||||
|
||||
- 只要本地存在旧进程、旧脚本或错误端口映射,就很容易出现“代码改了但界面还是旧的”假象。
|
||||
- 遇到这类问题,优先检查实际启动脚本、端口占用和返回模块内容,而不是先怀疑 UI 代码没生效。
|
||||
|
||||
## 11. 编辑器与正式运行时的关系
|
||||
|
||||
已经验证最稳的做法是:
|
||||
|
||||
- 编辑器预览直接复用正式运行时函数。
|
||||
- 预览不自己模拟一套战斗和 function 执行逻辑。
|
||||
- 运行时怎么结算,编辑器就怎么调用。
|
||||
|
||||
经验结论:
|
||||
|
||||
- 只要编辑器预览和正式逻辑分成两套,后面一定会越来越不一致。
|
||||
- 预览系统的价值不是“看起来像”,而是“执行路径就是正式路径”。
|
||||
|
||||
## 12. 后续继续开发时建议遵循的顺序
|
||||
|
||||
推荐流程:
|
||||
|
||||
1. 先补数据结构和类型。
|
||||
2. 再补 function 规则和过滤条件。
|
||||
3. 再补 hook 流程与状态迁移。
|
||||
4. 再补动画和演出。
|
||||
5. 最后再做 UI 细修。
|
||||
6. 每轮改动后至少做一次类型检查和本地启动验证。
|
||||
|
||||
不推荐的流程:
|
||||
|
||||
1. 先堆 UI。
|
||||
2. 再临时塞状态。
|
||||
3. 最后补运行逻辑。
|
||||
|
||||
这类项目里,后者几乎一定导致返工。
|
||||
|
||||
## 13. 一句话总结
|
||||
|
||||
这个项目最重要的经验不是“做了多少页面和功能”,而是:
|
||||
|
||||
**必须把 AI 文本生成、本地规则、动画演出、场景状态、编辑器工具这几套系统严格分层,再通过 function 和统一状态流把它们重新接起来。**
|
||||
|
||||
## 14. 相关文档
|
||||
|
||||
如需继续细看已有沉淀,可结合以下文档一起阅读:
|
||||
|
||||
- `docs/PROJECT_DEVELOPMENT_EXPERIENCE.md`
|
||||
- `docs/MOBILE_UI_DEV_EXPERIENCE.md`
|
||||
- `docs/CODEX_IMPLEMENTATION_EXPERIENCE_2026-03-24.md`
|
||||
- `docs/AGENT_UI_CHANGELOG.md`
|
||||
192
docs/npc-conversation-situation-draft.md
Normal file
192
docs/npc-conversation-situation-draft.md
Normal file
@@ -0,0 +1,192 @@
|
||||
# NPC 对话阶段与情景注入草案
|
||||
|
||||
## 目标
|
||||
|
||||
让 NPC 对话同时受三类因素控制,而不是只靠一大段 prompt 生硬约束:
|
||||
|
||||
1. 好感与信任阶段
|
||||
2. 角色三维表述风格
|
||||
3. 当前情景与刚刚共同经历
|
||||
|
||||
最终目标不是“少说设定”,而是“像真人一样按场合和关系逐步说”。
|
||||
|
||||
## 当前已经落地的控制层
|
||||
|
||||
### 1. 好感阶段
|
||||
|
||||
- `guarded`
|
||||
- 低好感
|
||||
- 只谈眼前局势、态度和试探
|
||||
- `partial`
|
||||
- 开始松口
|
||||
- 给出表层理由或半真半假的说明
|
||||
- `honest`
|
||||
- 逐步触及真实动机的轮廓
|
||||
- `deep`
|
||||
- 可以谈更深层的来历、目标和旧事
|
||||
|
||||
### 2. 三维表述风格
|
||||
|
||||
- `guardStyle`
|
||||
- `blunt` 直硬
|
||||
- `wary` 谨慎
|
||||
- `evasive` 回避
|
||||
- `measured` 克制
|
||||
- `warmStyle`
|
||||
- `dry` 冷淡
|
||||
- `steady` 平稳
|
||||
- `gentle` 温和
|
||||
- `teasing` 带点松弛感
|
||||
- `truthStyle`
|
||||
- `direct` 说真话时直给
|
||||
- `fragmented` 碎片式透露
|
||||
- `deflecting` 先绕一下再说
|
||||
|
||||
### 3. 情景注入
|
||||
|
||||
当前新增的情景标签:
|
||||
|
||||
- `first_contact_cautious`
|
||||
- 初见试探
|
||||
- `camp_first_contact`
|
||||
- 营地第一轮正式对接
|
||||
- `camp_followup`
|
||||
- 营地里承接上轮旧话头
|
||||
- `post_battle_breath`
|
||||
- 刚打完一轮冲突后的短暂松动
|
||||
- `shared_danger_coordination`
|
||||
- 危险未解除,优先短句对接
|
||||
- `private_followup`
|
||||
- 已经聊过一轮,不再是模板式初见
|
||||
|
||||
配套压力标签:
|
||||
|
||||
- `high`
|
||||
- `medium`
|
||||
- `low`
|
||||
|
||||
并补充:
|
||||
|
||||
- `recentSharedEvent`
|
||||
- 刚刚共同经历了什么
|
||||
- `talkPriority`
|
||||
- 这轮优先先说什么
|
||||
|
||||
## 设计原则
|
||||
|
||||
### 1. 把“知道什么”和“愿意说什么”拆开
|
||||
|
||||
角色完整设定始终存在,但 prompt 不应长期直接暴露:
|
||||
|
||||
- `reason`
|
||||
- `goal`
|
||||
- 完整背景
|
||||
- 旧事全貌
|
||||
|
||||
而是按阶段只注入:
|
||||
|
||||
- `surfaceHook`
|
||||
- `immediateConcern`
|
||||
- `guardedMotive`
|
||||
- `reason`
|
||||
- `goal`
|
||||
|
||||
### 2. 初见先谈现场,不先谈人生
|
||||
|
||||
无论玩家还是 NPC,初次见面都优先:
|
||||
|
||||
- 眼前危险
|
||||
- 当前判断
|
||||
- 彼此态度
|
||||
- 一点没说透的钩子
|
||||
|
||||
不优先:
|
||||
|
||||
- 完整来意
|
||||
- 长篇背景
|
||||
- “我们的目标一致”
|
||||
- 正式自我介绍
|
||||
|
||||
### 3. 刚打完怪时优先短句
|
||||
|
||||
`post_battle_breath` 和 `shared_danger_coordination` 两类情景下,对话应该:
|
||||
|
||||
- 先接刚才发生的事
|
||||
- 先评价判断或身手
|
||||
- 句子更短
|
||||
- 少做完整背景说明
|
||||
|
||||
## 当前实现路径
|
||||
|
||||
### 上下文字段
|
||||
|
||||
`StoryGenerationContext` 目前已经承载:
|
||||
|
||||
- 对话阶段控制
|
||||
- 三维风格
|
||||
- 情景标签
|
||||
- 压力级别
|
||||
- 最近共同经历
|
||||
- 本轮说话重点
|
||||
|
||||
### prompt 注入
|
||||
|
||||
当前 prompt 会显式加入:
|
||||
|
||||
- 当前 NPC 对话阶段控制
|
||||
- 当前对话情景控制
|
||||
|
||||
目的:
|
||||
|
||||
- 不让模型自己从一堆底层状态里猜场合
|
||||
- 先让系统做好裁决,再让模型负责“怎么说”
|
||||
|
||||
## 下一步建议
|
||||
|
||||
### 1. 把 `surfaceHook` 改成更口语、更像现场句
|
||||
|
||||
当前最大风险不是结构不够,而是字段文案还可能像“作者说明”。
|
||||
|
||||
应优先改成:
|
||||
|
||||
- 能直接对人说
|
||||
- 不像自我介绍
|
||||
- 不像任务摘要
|
||||
- 更像“站在现场会脱口而出的话”
|
||||
|
||||
### 2. 引入“问题命中”判断
|
||||
|
||||
不只看好感,也看玩家这次是不是问到了点上。
|
||||
|
||||
建议:
|
||||
|
||||
- 好感够,但问题没命中 -> 仍保留
|
||||
- 好感够,问题命中 -> 松口一层
|
||||
|
||||
### 3. 使用 `revealedFacts`
|
||||
|
||||
后续可把已公开的信息记下来,避免:
|
||||
|
||||
- 重复卖同一个关子
|
||||
- 前后口径反复横跳
|
||||
|
||||
### 4. 把开场第一段从通用剧情生成中进一步拆出
|
||||
|
||||
现在开场仍部分受通用剧情引擎影响。
|
||||
|
||||
更理想的方向:
|
||||
|
||||
- 开场先走纯对白生成
|
||||
- 对话定稿后再推导后续选项
|
||||
|
||||
这样语言会比“剧情导演 + JSON + 选项合法性”混合生成更自然。
|
||||
|
||||
## 验收重点
|
||||
|
||||
改完后重点看这几类表现是否成立:
|
||||
|
||||
1. 初见不再像互背设定卡
|
||||
2. 刚打完怪时,对话明显更短、更贴眼前
|
||||
3. 同一阶段下,不同性格角色表达方式确实不同
|
||||
4. 玩家和 NPC 都不会在第一轮自曝完整动机
|
||||
5. 同一个 NPC 连续几轮聊天时,信息释放节奏是连续的,不会忽冷忽热
|
||||
637
docs/prd/AI_CHARACTER_VISUAL_ANIMATION_MVP_PRD_2026-04-04.md
Normal file
637
docs/prd/AI_CHARACTER_VISUAL_ANIMATION_MVP_PRD_2026-04-04.md
Normal file
@@ -0,0 +1,637 @@
|
||||
# AI 角色形象与角色动画 MVP PRD
|
||||
|
||||
更新时间:`2026-04-04`
|
||||
|
||||
## 0. 一句话结论
|
||||
|
||||
本次 MVP 要做的不是一个泛化的“AI 动画平台”,而是一个能直接服务当前项目角色资产生产的最小闭环:
|
||||
|
||||
1. 用户先输入角色形象设定并上传参考图,或直接上传现成角色素材
|
||||
2. 系统生成或规范化出一张**符合当前游戏侧视角色素材视角**的主形象图
|
||||
3. 用户确认主形象后,再为该角色生成与当前项目可扮演角色动作槽位匹配的基础动作集
|
||||
4. 用户可预览、重新生成,并最终发布为当前仓库可直接读取的角色资产
|
||||
|
||||
一句话说:
|
||||
|
||||
**MVP 先解决“怎么稳定产出能进游戏的角色主形象和基础动作”,而不是先追求所有技能动作和复杂演出。**
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前仓库已经有:
|
||||
|
||||
- 可扮演角色与角色型 NPC
|
||||
- `CharacterAnimator` 运行时播放链路
|
||||
- 角色预设与 override 机制
|
||||
- 本地 API 插件机制
|
||||
- NPC 静态形象编辑器
|
||||
|
||||
但当前缺少一条可用的角色资产生产链:
|
||||
|
||||
1. 角色主形象仍依赖手工素材整理
|
||||
2. 动作资产缺少“从角色设定到可播放动作集”的编辑器闭环
|
||||
3. 没有把“AI 生成结果”稳定沉淀为当前项目的标准角色资源
|
||||
|
||||
因此本期 MVP 的目标不是重做运行时战斗系统,而是补齐:
|
||||
|
||||
- 主形象生产
|
||||
- 基础动作生产
|
||||
- 预览与重新生成
|
||||
- 发布到现有角色资源体系
|
||||
|
||||
---
|
||||
|
||||
## 2. MVP 要解决的问题
|
||||
|
||||
## 2.1 用户侧问题
|
||||
|
||||
当前如果想给项目新增一个角色,成本很高:
|
||||
|
||||
- 要先准备适合当前游戏视角的主形象
|
||||
- 再补动作资源
|
||||
- 还要手工整理资源目录和配置
|
||||
|
||||
MVP 要把这件事改成一个编辑器工作流。
|
||||
|
||||
## 2.2 项目侧问题
|
||||
|
||||
当前项目并不适合把 AI 直接放进运行时实时生成动作,因为:
|
||||
|
||||
- 运行时需要稳定、可回放、可测试的资源
|
||||
- 战斗和剧情都依赖固定动作槽位
|
||||
- 生成结果如果不资产化,会让维护成本失控
|
||||
|
||||
所以 MVP 的正确方向是:
|
||||
|
||||
**把 AI 用在编辑器里的资产生产链,而不是运行时即时生成。**
|
||||
|
||||
---
|
||||
|
||||
## 3. MVP 目标
|
||||
|
||||
## 3.1 产品目标
|
||||
|
||||
提供一个最小可用的“角色资产工坊”,满足以下闭环:
|
||||
|
||||
1. 生成或上传角色主形象
|
||||
2. 预览并重选角色主形象
|
||||
3. 基于主形象生成基础动作
|
||||
4. 预览并重选动作结果
|
||||
5. 发布为当前项目可直接使用的角色资源
|
||||
|
||||
## 3.2 技术目标
|
||||
|
||||
MVP 必须满足:
|
||||
|
||||
1. 只依赖国内可用模型与服务
|
||||
2. 只生成当前项目能消费的标准资产
|
||||
3. 不改动现有战斗规则与剧情规则
|
||||
4. 生成结果必须走本地后处理与发布流程
|
||||
5. 运行时只读本地标准化资产
|
||||
|
||||
## 3.3 成功标准
|
||||
|
||||
当以下条件同时成立时,视为 MVP 成功:
|
||||
|
||||
1. 编辑器里可以完成“主形象 -> 基础动作 -> 发布”闭环
|
||||
2. 发布后的角色能被当前 `CharacterAnimator` 播放
|
||||
3. 基础动作槽位不存在空映射
|
||||
4. 用户可以对主形象和动作分别进行重新生成
|
||||
|
||||
---
|
||||
|
||||
## 4. 非目标
|
||||
|
||||
本期不做:
|
||||
|
||||
1. 不做运行时实时动画生成
|
||||
2. 不做怪物动画生成链路
|
||||
3. 不做通用 NPC 群像流水线
|
||||
4. 不做技能动作全自动补齐
|
||||
5. 不做批量生产多个角色的自动流水线
|
||||
6. 不做多供应商路由调度
|
||||
|
||||
说明:
|
||||
|
||||
- 本期只先做好**单角色、单次编辑器操作**的最小闭环
|
||||
- 技能动作可后续补,但**基础动作槽位不能为空**
|
||||
|
||||
---
|
||||
|
||||
## 5. 目标用户与使用场景
|
||||
|
||||
## 5.1 目标用户
|
||||
|
||||
- 项目策划
|
||||
- 项目美术 / 技术美术
|
||||
- 负责角色内容生产的开发者
|
||||
|
||||
## 5.2 核心使用场景
|
||||
|
||||
### 场景 A:新建角色
|
||||
|
||||
用户输入角色设定词、参考图,生成当前项目风格可用的主形象和基础动作。
|
||||
|
||||
### 场景 B:已有角色素材导入
|
||||
|
||||
用户已有一张角色图,希望快速裁切、规范化,并继续生成基础动作。
|
||||
|
||||
### 场景 C:已有角色动作质量不满意
|
||||
|
||||
用户不改主形象,只重新生成单个或多个动作槽位。
|
||||
|
||||
---
|
||||
|
||||
## 6. MVP 用户流程
|
||||
|
||||
## 6.1 阶段 A:主形象生成 / 导入
|
||||
|
||||
1. 用户选择角色
|
||||
2. 输入角色形象设定文本
|
||||
3. 上传角色参考图,或直接上传现成角色素材
|
||||
4. 系统做尺寸、裁剪、构图校验
|
||||
5. 提交生成或规范化任务
|
||||
6. 返回多个候选预览图
|
||||
7. 用户可:
|
||||
- 预览
|
||||
- 重新生成
|
||||
- 设为当前主形象
|
||||
|
||||
## 6.2 阶段 B:基础动作生成
|
||||
|
||||
1. 用户基于已确认主形象进入动作页
|
||||
2. 系统展示当前基础动作槽位状态
|
||||
3. 用户选择:
|
||||
- 直接使用动作模板
|
||||
- 上传参考动作视频
|
||||
4. 提交动作生成任务
|
||||
5. 生成完成后进入动作预览
|
||||
6. 用户可:
|
||||
- 保留结果
|
||||
- 重新生成单动作
|
||||
- 替换当前动作
|
||||
|
||||
## 6.3 阶段 C:发布
|
||||
|
||||
只有在基础动作槽位全部有有效资源时,才允许发布。
|
||||
|
||||
发布后:
|
||||
|
||||
1. 写入主形象资源
|
||||
2. 写入动画资源
|
||||
3. 生成 manifest
|
||||
4. 更新角色 override / 映射关系
|
||||
|
||||
---
|
||||
|
||||
## 7. 主形象阶段需求
|
||||
|
||||
## 7.1 输入方式
|
||||
|
||||
MVP 支持三种主形象输入方式:
|
||||
|
||||
1. 文生图
|
||||
- 用户输入形象设定
|
||||
2. 图生图
|
||||
- 用户输入形象设定并上传参考图
|
||||
3. 直接上传素材
|
||||
- 用户上传已有角色图,不强制重新生成
|
||||
|
||||
## 7.2 视角要求
|
||||
|
||||
主形象必须贴近当前项目现有角色素材体系:
|
||||
|
||||
- 2D 侧视动作素材视角
|
||||
- 单人全身
|
||||
- 人物朝向右侧
|
||||
- 脚底完整可见
|
||||
- 武器和关键轮廓完整
|
||||
- 不做正面立绘
|
||||
- 不做强透视镜头
|
||||
|
||||
这是硬约束,不是美术建议。
|
||||
|
||||
原因:
|
||||
|
||||
- 当前项目运行时通过镜像处理左右朝向
|
||||
- 如果主形象不是侧视动作素材视角,后续动作生成和运行时挂接都会失真
|
||||
|
||||
## 7.3 尺寸与裁剪要求
|
||||
|
||||
推荐规格:
|
||||
|
||||
- 推荐输入:`1024x1536`
|
||||
- 可接受比例:`2:3` 或 `3:4`
|
||||
- 最低建议:短边 `>= 768`
|
||||
- 统一输出标准图:`1024x1536`
|
||||
|
||||
构图要求:
|
||||
|
||||
- 角色主体占画面高度约 `70% ~ 85%`
|
||||
- 头顶保留少量留白
|
||||
- 脚底必须完整露出
|
||||
- 背景尽量简单
|
||||
- 只允许单角色
|
||||
|
||||
## 7.4 主形象阶段交互要求
|
||||
|
||||
用户必须可以:
|
||||
|
||||
- 查看候选结果
|
||||
- 放大预览
|
||||
- 丢弃某个候选
|
||||
- 基于同样输入重新生成
|
||||
- 选择一个候选作为主形象
|
||||
|
||||
用户不能直接跳过主形象确认进入动作阶段,除非已经锁定主形象。
|
||||
|
||||
---
|
||||
|
||||
## 8. 动作阶段需求
|
||||
|
||||
## 8.1 基础动作槽位要求
|
||||
|
||||
MVP 必须与当前项目可扮演角色动作槽位对齐。
|
||||
|
||||
第一版要求以下基础动作槽位不能为空:
|
||||
|
||||
| 动作槽位 | 是否必填 | 备注 |
|
||||
| --- | --- | --- |
|
||||
| `idle` | 必填 | 循环动作 |
|
||||
| `acquire` | 必填 | 可由短变体衍生 |
|
||||
| `attack` | 必填 | 一次性动作 |
|
||||
| `run` | 必填 | 循环动作 |
|
||||
| `jump` | 必填 | 一次性动作 |
|
||||
| `double_jump` | 必填 | 可由跳跃二次变体生成 |
|
||||
| `jump_attack` | 必填 | 一次性动作 |
|
||||
| `dash` | 必填 | 一次性动作 |
|
||||
| `hurt` | 必填 | 一次性动作 |
|
||||
| `die` | 必填 | 一次性动作 |
|
||||
| `climb` | 必填 | 可由模板生成 |
|
||||
| `wall_slide` | 必填 | 可由攀爬停帧变体生成 |
|
||||
|
||||
这里“不能为空”指的是:
|
||||
|
||||
- 每个槽位必须最终指向一套可播放的资源
|
||||
- 允许少量槽位由近似动作衍生
|
||||
- 但不允许在运行时读到空动画映射
|
||||
|
||||
## 8.2 技能动作要求
|
||||
|
||||
本期不要求自动补齐:
|
||||
|
||||
- `skill1`
|
||||
- `skill1_jump`
|
||||
- `skill1_bullet`
|
||||
- `skill1_bullet_fx`
|
||||
- `skill2`
|
||||
- `skill2_jump`
|
||||
- `skill3`
|
||||
- `skill3_jump`
|
||||
- `skill3_bullet`
|
||||
- `skill3_bullet_fx`
|
||||
- `skill4`
|
||||
|
||||
结论:
|
||||
|
||||
- 技能动作本期可选
|
||||
- 基础动作本期必做
|
||||
|
||||
## 8.3 动作生成方式
|
||||
|
||||
MVP 支持两种方式:
|
||||
|
||||
1. 模板动作生成
|
||||
- 用户选择动作模板
|
||||
2. 参考视频驱动
|
||||
- 用户上传参考动作视频
|
||||
|
||||
优先级:
|
||||
|
||||
- 基础动作优先走模板
|
||||
- 个性化动作再用参考视频驱动
|
||||
|
||||
## 8.4 动作阶段交互要求
|
||||
|
||||
用户必须可以:
|
||||
|
||||
- 查看每个动作槽位当前状态
|
||||
- 单独预览某个动作
|
||||
- 单独重新生成某个动作
|
||||
- 保留某些已满意动作,只重生其余动作
|
||||
- 在所有基础动作齐全后再发布
|
||||
|
||||
---
|
||||
|
||||
## 9. 生成策略约束
|
||||
|
||||
## 9.1 角色一致性优先
|
||||
|
||||
动作生成阶段只能使用已锁定的主形象,不允许每个动作重新随机生成角色外观。
|
||||
|
||||
## 9.2 首尾帧控制
|
||||
|
||||
参考用户提供的视频方向,MVP 固化以下策略:
|
||||
|
||||
### 循环动作
|
||||
|
||||
- `idle`
|
||||
- `run`
|
||||
|
||||
要求首尾姿态尽量接近,便于循环。
|
||||
|
||||
### 一次性动作
|
||||
|
||||
- `attack`
|
||||
- `jump_attack`
|
||||
- `hurt`
|
||||
- `die`
|
||||
|
||||
要求末帧清晰,不与下一动作切换冲突。
|
||||
|
||||
## 9.3 高分辨率生成,低分辨率落地
|
||||
|
||||
MVP 不要求模型直接输出最终像素逐帧。
|
||||
|
||||
正确流程是:
|
||||
|
||||
1. 生成较稳定的高分辨率动作视频
|
||||
2. 本地解帧、对齐、清理
|
||||
3. 再转成当前项目可用的像素化资产
|
||||
|
||||
---
|
||||
|
||||
## 10. 技术方案边界
|
||||
|
||||
## 10.1 模型选择
|
||||
|
||||
MVP 统一采用国内可用的阿里云百炼方案:
|
||||
|
||||
- 主形象生成:`wan2.7-image-pro` / `wan2.7-image`
|
||||
- 动作生成:`wan2.2-animate-move`
|
||||
- 高质量换角动作:`wan2.2-animate-mix`
|
||||
|
||||
选择理由:
|
||||
|
||||
1. 国内可用
|
||||
2. 图像和动作链路在同一平台
|
||||
3. 便于 MVP 先收敛到单平台实现
|
||||
|
||||
## 10.2 本地后处理
|
||||
|
||||
MVP 必须包含本地后处理:
|
||||
|
||||
1. 解帧
|
||||
2. 主体裁切
|
||||
3. 背景清理
|
||||
4. 稳帧
|
||||
5. 像素化
|
||||
6. 打包 Sprite Sheet
|
||||
7. 输出 manifest
|
||||
|
||||
## 10.3 运行时边界
|
||||
|
||||
运行时不直接请求第三方模型接口。
|
||||
|
||||
运行时只读取:
|
||||
|
||||
- 主形象标准资源
|
||||
- 动画标准资源
|
||||
- override / manifest 配置
|
||||
|
||||
---
|
||||
|
||||
## 11. MVP 数据与接口
|
||||
|
||||
## 11.1 角色主形象资源
|
||||
|
||||
建议最小结构:
|
||||
|
||||
```ts
|
||||
type GeneratedCharacterVisualAsset = {
|
||||
id: string;
|
||||
characterId: string;
|
||||
sourceMode: 'text-to-image' | 'image-to-image' | 'upload';
|
||||
masterImagePath: string;
|
||||
previewImagePaths: string[];
|
||||
width: number;
|
||||
height: number;
|
||||
facing: 'right';
|
||||
locked: boolean;
|
||||
};
|
||||
```
|
||||
|
||||
## 11.2 角色动画资源
|
||||
|
||||
```ts
|
||||
type GeneratedCharacterAnimationAsset = {
|
||||
id: string;
|
||||
characterId: string;
|
||||
visualAssetId: string;
|
||||
action: string;
|
||||
frameCount: number;
|
||||
fps: number;
|
||||
loop: boolean;
|
||||
spriteSheetPath: string;
|
||||
framePaths: string[];
|
||||
previewVideoPath: string;
|
||||
};
|
||||
```
|
||||
|
||||
## 11.3 最小接口
|
||||
|
||||
建议新增:
|
||||
|
||||
- `POST /api/character-visual/jobs`
|
||||
- `GET /api/character-visual/jobs/:id`
|
||||
- `POST /api/character-visual/publish`
|
||||
- `POST /api/animation/jobs`
|
||||
- `GET /api/animation/jobs/:id`
|
||||
- `GET /api/animation/templates`
|
||||
- `POST /api/animation/publish`
|
||||
|
||||
职责:
|
||||
|
||||
### `POST /api/character-visual/jobs`
|
||||
|
||||
- 创建主形象生成或规范化任务
|
||||
|
||||
### `GET /api/character-visual/jobs/:id`
|
||||
|
||||
- 查询主形象任务状态与结果
|
||||
|
||||
### `POST /api/character-visual/publish`
|
||||
|
||||
- 锁定主形象并写入主形象 manifest
|
||||
|
||||
### `POST /api/animation/jobs`
|
||||
|
||||
- 创建动作生成任务
|
||||
|
||||
### `GET /api/animation/jobs/:id`
|
||||
|
||||
- 查询动作任务状态与结果
|
||||
|
||||
### `GET /api/animation/templates`
|
||||
|
||||
- 返回动作模板列表
|
||||
|
||||
### `POST /api/animation/publish`
|
||||
|
||||
- 发布动作资源并更新动画映射
|
||||
|
||||
---
|
||||
|
||||
## 12. 与当前仓库的接入点
|
||||
|
||||
第一批建议接入:
|
||||
|
||||
- `src/components/CharacterAnimator.tsx`
|
||||
- `src/types/characters.ts`
|
||||
- `src/data/characterOverrides.json`
|
||||
- `scripts/dev-server/localApiPlugins.ts`
|
||||
|
||||
建议新增:
|
||||
|
||||
- `src/components/CharacterAssetStudio.tsx`
|
||||
- `src/data/characterAnimationOverrides.json`
|
||||
- 角色主形象 manifest 读取逻辑
|
||||
- 动画 manifest 读取逻辑
|
||||
|
||||
运行时优先级建议:
|
||||
|
||||
1. 角色 AI 生成动画 override
|
||||
2. 角色原始 `animationMap`
|
||||
3. 默认回退动画
|
||||
|
||||
---
|
||||
|
||||
## 13. 验收标准
|
||||
|
||||
## 13.1 主形象验收
|
||||
|
||||
1. 用户可以通过文生图 / 图生图 / 直接上传素材三种方式得到主形象
|
||||
2. 主形象视角符合当前项目侧视角色素材要求
|
||||
3. 主形象可预览、可重新生成、可锁定
|
||||
|
||||
## 13.2 动作验收
|
||||
|
||||
1. 所有基础动作槽位均有有效资源
|
||||
2. 用户可以单独预览和重新生成某个动作
|
||||
3. 发布后动作能被当前 `CharacterAnimator` 播放
|
||||
|
||||
## 13.3 资产验收
|
||||
|
||||
1. 发布后可生成主形象资源目录
|
||||
2. 发布后可生成动画资源目录
|
||||
3. 可生成对应 manifest / override 映射
|
||||
|
||||
## 13.4 体验验收
|
||||
|
||||
1. 用户不需要手工整理最终资源目录
|
||||
2. 用户不需要每次都从头重做全部动作
|
||||
3. 主形象和动作两阶段都可回看和重生
|
||||
|
||||
---
|
||||
|
||||
## 14. 风险与对策
|
||||
|
||||
## 14.1 风险:主形象视角不稳定
|
||||
|
||||
对策:
|
||||
|
||||
- 把“侧视、朝右、全身、脚底完整”作为硬校验
|
||||
- 不合格结果不允许直接进入动作阶段
|
||||
|
||||
## 14.2 风险:动作循环不自然
|
||||
|
||||
对策:
|
||||
|
||||
- `idle`、`run` 强制使用循环模板优先
|
||||
- 引入首尾帧接近度校验
|
||||
|
||||
## 14.3 风险:基础动作槽位过多,MVP 开发压力大
|
||||
|
||||
对策:
|
||||
|
||||
- 允许 `acquire`、`double_jump`、`wall_slide` 等少数槽位由近似动作衍生
|
||||
- 但运行时最终槽位仍必须非空
|
||||
|
||||
## 14.4 风险:生成成本过高
|
||||
|
||||
对策:
|
||||
|
||||
- 先只支持单角色编辑
|
||||
- 基础动作优先模板化
|
||||
- 仅对不满意动作单独重生
|
||||
|
||||
---
|
||||
|
||||
## 15. MVP 开发顺序
|
||||
|
||||
## 阶段 1:主形象闭环
|
||||
|
||||
目标:
|
||||
|
||||
- 跑通主形象生成 / 上传 / 预览 / 锁定
|
||||
|
||||
产出:
|
||||
|
||||
- `CharacterAssetStudio` 的主形象页
|
||||
- 主形象任务 API
|
||||
- 主形象 manifest
|
||||
|
||||
## 阶段 2:基础动作闭环
|
||||
|
||||
目标:
|
||||
|
||||
- 跑通单动作生成、预览与替换
|
||||
|
||||
产出:
|
||||
|
||||
- 动作任务 API
|
||||
- 动作模板列表
|
||||
- 预览与重新生成流程
|
||||
|
||||
## 阶段 3:基础动作补齐与发布
|
||||
|
||||
目标:
|
||||
|
||||
- 让基础动作槽位全部非空,并可一键发布
|
||||
|
||||
产出:
|
||||
|
||||
- 动作状态面板
|
||||
- 发布逻辑
|
||||
- override / manifest 写入
|
||||
|
||||
## 阶段 4:运行时接入
|
||||
|
||||
目标:
|
||||
|
||||
- 让发布后的角色直接在现有运行时播放
|
||||
|
||||
产出:
|
||||
|
||||
- `CharacterAnimator` 读取生成动画能力
|
||||
- 角色 override 映射接入
|
||||
|
||||
---
|
||||
|
||||
## 16. 最终结论
|
||||
|
||||
对当前仓库来说,最可落地的 MVP 不是“先做一个很强的 AI 动画系统”,而是:
|
||||
|
||||
1. 先固定角色主形象生产流程
|
||||
2. 再固定基础动作生产流程
|
||||
3. 只补能进当前项目运行时的最小动作集
|
||||
4. 把预览、重新生成和发布做完整
|
||||
|
||||
这样做的价值在于:
|
||||
|
||||
- 范围可控
|
||||
- 路径清晰
|
||||
- 能真正进入当前仓库
|
||||
- 后续可以在此基础上再加技能动作、剧情演出和多供应商增强路线
|
||||
|
||||
789
docs/prd/AI_NATIVE_QUEST_SYSTEM_PRD_2026-04-02.md
Normal file
789
docs/prd/AI_NATIVE_QUEST_SYSTEM_PRD_2026-04-02.md
Normal file
@@ -0,0 +1,789 @@
|
||||
# AI 原生游戏任务系统 PRD
|
||||
|
||||
更新时间:`2026-04-02`
|
||||
|
||||
## 0. 设计目标
|
||||
|
||||
这份方案针对当前仓库,回答的是:
|
||||
|
||||
**如果要把现有“接受任务 -> 推进 -> 完成 -> 交付”的轻量任务链路,升级成真正的 AI 原生任务系统,应该怎样设计,才能既让任务更像从当前剧情里长出来,又不把规则、奖励和平衡交给模型失控决定?**
|
||||
|
||||
这里的“AI 原生”不是指:
|
||||
|
||||
- 让模型随时随地自由生成一个任务标题和奖励
|
||||
- 让模型直接修改任务状态、数值或掉落
|
||||
- 让任务系统变成纯文案系统
|
||||
|
||||
这里的“AI 原生”指的是:
|
||||
|
||||
- AI 负责理解当前故事局面,生成任务语义、动机、话术、阶段转折和关系变化
|
||||
- 本地规则负责验证任务是否合法、如何推进、何时完成、奖励如何结算、存档如何兼容
|
||||
- 任务系统不再只是“静态模板挂在 NPC 身上”,而是“从当前局面推导出来的可控任务合约”
|
||||
|
||||
一句话总结:
|
||||
|
||||
**AI 决定这份任务在叙事上是什么,本地规则决定它在系统上如何成立。**
|
||||
|
||||
## 1. 当前系统现状
|
||||
|
||||
从当前仓库实现看,任务系统已经有一个可工作的最小闭环:
|
||||
|
||||
- `src/data/questFlow.ts`
|
||||
- 负责构造任务、接受任务、推进任务、完成任务、交付任务
|
||||
- `src/hooks/story/npcEncounterActions.ts`
|
||||
- 负责 NPC 交互里的 `quest_accept` / `quest_turn_in`
|
||||
- `src/hooks/useStoryGeneration.ts`
|
||||
- 负责在战斗、宝藏、NPC 交互后更新 quest progress
|
||||
- `src/types/story.ts`
|
||||
- 当前 `QuestLogEntry` 是运行时任务主结构
|
||||
- `src/components/AdventurePanel.tsx`
|
||||
- 负责任务 UI 展示与奖励领取
|
||||
|
||||
当前系统已经支持:
|
||||
|
||||
1. NPC 提供任务
|
||||
2. 玩家接受任务
|
||||
3. 击败怪物 / 调查宝藏 / 与 NPC 切磋 三类目标推进
|
||||
4. 任务完成后领取奖励
|
||||
5. 奖励影响货币、背包和 NPC affinity
|
||||
|
||||
这说明基础闭环是成立的,问题不在“有没有任务系统”,而在于:
|
||||
|
||||
- 当前任务更像“局部规则脚手架”,还不是“AI 原生任务系统”
|
||||
- AI 目前主要参与任务文案氛围和选项重写,没有真正参与任务语义生成
|
||||
- 任务结构太扁平,无法承载阶段变化、关系转折、后续分支和任务记忆
|
||||
|
||||
## 2. 当前系统的核心限制
|
||||
|
||||
## 2.1 任务来源仍然偏静态
|
||||
|
||||
当前 `buildQuestForEncounter(...)` 的生成逻辑,本质还是:
|
||||
|
||||
- 看当前 scene 有没有 hostile npc
|
||||
- 没有就看 monsterIds
|
||||
- 再没有就看 treasureHints
|
||||
- 最后 fallback 到 spar
|
||||
|
||||
这套逻辑稳定,但仍然是:
|
||||
|
||||
**本地 if/else 在选任务类型,AI 只是在外围写故事。**
|
||||
|
||||
结果是:
|
||||
|
||||
- 任务叙事贴合度有限
|
||||
- 任务动机比较像通用模板
|
||||
- 很难让任务明显体现“为什么偏偏是这个 NPC、这个时刻、这个场景、这个玩家”
|
||||
|
||||
## 2.2 任务结构只有“单目标单阶段”
|
||||
|
||||
当前 `QuestLogEntry.objective` 只有:
|
||||
|
||||
- `kind`
|
||||
- `targetMonsterId`
|
||||
- `targetNpcId`
|
||||
- `targetSceneId`
|
||||
- `requiredCount`
|
||||
|
||||
它适合最小任务闭环,但不适合承载:
|
||||
|
||||
- 多阶段任务
|
||||
- 前置调查 -> 中段确认 -> 最终结算
|
||||
- NPC 关系驱动的任务升级
|
||||
- 同一任务在不同世界/场景下的变体
|
||||
- 任务完成方式差异带来的后续影响
|
||||
|
||||
## 2.3 任务状态过粗
|
||||
|
||||
当前状态只有:
|
||||
|
||||
- `active`
|
||||
- `completed`
|
||||
- `turned_in`
|
||||
|
||||
这会导致任务系统无法表达:
|
||||
|
||||
- 已发现但未正式接取
|
||||
- 已接受但未激活下一阶段
|
||||
- 已满足隐藏条件但未显式揭示
|
||||
- 已失败 / 已过期 / 已被替代
|
||||
- 已分支到不同结局
|
||||
|
||||
## 2.4 奖励是固定规则,不随任务语义演化
|
||||
|
||||
当前奖励主要由 `buildQuestReward(worldType, roleText)` 生成。
|
||||
|
||||
优点:
|
||||
|
||||
- 稳定
|
||||
- 易控
|
||||
- 好测试
|
||||
|
||||
问题:
|
||||
|
||||
- 奖励和任务语义绑定弱
|
||||
- 很难体现“这个 NPC 因为什么给你这份奖励”
|
||||
- 不能承载 AI 原生任务中更重要的“关系、线索、世界信息、后续机会”
|
||||
|
||||
## 2.5 AI 没有任务层专属 contract
|
||||
|
||||
当前 `StoryGenerationContext` 很强,但任务相关信息还没有独立建模。于是:
|
||||
|
||||
- prompt 里只能把任务作为周边信息描述
|
||||
- AI 无法明确返回“任务意图”
|
||||
- 本地也无法验证 AI 给出的任务建议是否合法
|
||||
|
||||
这意味着任务系统还没有形成:
|
||||
|
||||
**专门面向任务设计的 AI <-> Rule contract**
|
||||
|
||||
## 3. 设计原则
|
||||
|
||||
## 3.1 AI 负责叙事,本地负责规则
|
||||
|
||||
这是本项目已经验证有效的核心边界,这次任务系统继续沿用。
|
||||
|
||||
AI 负责:
|
||||
|
||||
- 为什么现在出现这份任务
|
||||
- 任务在关系和剧情上的意义
|
||||
- 任务目标的话术表达
|
||||
- 阶段推进时的语义转折
|
||||
- 奖励在叙事上的来源解释
|
||||
|
||||
本地负责:
|
||||
|
||||
- 任务是否能生成
|
||||
- 任务目标类型是否合法
|
||||
- 可推进事件有哪些
|
||||
- 任务进度如何累计
|
||||
- 奖励数值、掉落、affinity、货币如何结算
|
||||
- 存档结构与兼容
|
||||
|
||||
## 3.2 任务必须是“当前局面的产物”
|
||||
|
||||
AI 原生任务不应是抽象模板,而应明确绑定:
|
||||
|
||||
1. 当前 scene / landmark / threat
|
||||
2. 当前 NPC 的身份、态度、关系阶段
|
||||
3. 最近几条 story history
|
||||
4. 玩家当前状态、build 缺口、队伍关系
|
||||
5. 当前世界观与 custom world profile
|
||||
|
||||
如果一份任务脱离这些上下文也成立,那它就还不是“AI 原生任务”。
|
||||
|
||||
## 3.3 任务应优先表达“意图”,再编译成“合约”
|
||||
|
||||
不要让 AI 直接返回最终 `QuestLogEntry`。
|
||||
|
||||
更稳的方式是:
|
||||
|
||||
1. AI 先返回任务意图 `QuestIntent`
|
||||
2. 本地把它编译成可执行的 `QuestContract`
|
||||
3. 再由运行时把 `QuestContract` 映射成 UI 所需的 `QuestLogEntry`
|
||||
|
||||
这样能保证:
|
||||
|
||||
- AI 足够自由地表达语义
|
||||
- 本地仍然保留最终裁决权
|
||||
- 系统容易测试、回放和做 fallback
|
||||
|
||||
## 3.4 任务系统必须兼容“AI 不可用”
|
||||
|
||||
这个项目所有关键玩法都应该支持 fallback。
|
||||
|
||||
所以 AI 原生任务系统必须允许:
|
||||
|
||||
- AI 在线时,任务更贴剧情、更像当前局面的自然产物
|
||||
- AI 离线时,任务仍可退回 deterministic builder
|
||||
|
||||
也就是说:
|
||||
|
||||
**AI 提升任务质量,但不成为任务闭环的单点故障。**
|
||||
|
||||
## 4. 建议的系统分层
|
||||
|
||||
建议把新任务系统拆成 5 层。
|
||||
|
||||
## 4.1 任务上下文采样层
|
||||
|
||||
先从当前运行时抽取一个统一的任务上下文:
|
||||
|
||||
```ts
|
||||
type QuestGenerationContext = {
|
||||
worldType: WorldType | null;
|
||||
customWorldProfile?: CustomWorldProfile | null;
|
||||
currentSceneId?: string | null;
|
||||
currentSceneName?: string | null;
|
||||
currentSceneDescription?: string | null;
|
||||
issuerNpcId?: string | null;
|
||||
issuerNpcName?: string | null;
|
||||
issuerNpcContext?: string | null;
|
||||
issuerAffinity?: number | null;
|
||||
issuerDisclosureStage?: NpcDisclosureStage | null;
|
||||
issuerWarmthStage?: NpcWarmthStage | null;
|
||||
encounterKind?: "npc" | "monster" | "treasure" | "none";
|
||||
recentStoryMoments: StoryMoment[];
|
||||
playerCharacter: Character;
|
||||
playerHp: number;
|
||||
playerMaxHp: number;
|
||||
playerMana: number;
|
||||
playerMaxMana: number;
|
||||
playerInventory: InventoryItem[];
|
||||
playerEquipment: EquipmentLoadout;
|
||||
activeCompanions: CompanionState[];
|
||||
rosterCompanions: CompanionState[];
|
||||
currentQuestSummary: Array<{
|
||||
id: string;
|
||||
title: string;
|
||||
status: QuestStatus;
|
||||
issuerNpcId: string;
|
||||
}>;
|
||||
};
|
||||
```
|
||||
|
||||
这一层的目标不是直接生成任务,而是先回答:
|
||||
|
||||
**这次任务生成到底发生在什么局面里?**
|
||||
|
||||
## 4.2 AI 任务意图层
|
||||
|
||||
让 AI 只返回“任务意图”,而不是最终任务实例:
|
||||
|
||||
```ts
|
||||
type QuestIntent = {
|
||||
narrativeType: "bounty" | "escort" | "investigation" | "retrieval" | "relationship" | "trial";
|
||||
dramaticNeed: string;
|
||||
issuerGoal: string;
|
||||
playerHook: string;
|
||||
worldReason: string;
|
||||
recommendedObjectiveKinds: Array<
|
||||
"defeat_monster" | "inspect_treasure" | "spar_with_npc" | "deliver_item" | "reach_scene" | "talk_to_npc"
|
||||
>;
|
||||
urgency: "low" | "medium" | "high";
|
||||
intimacy: "transactional" | "cooperative" | "trust_based";
|
||||
rewardTheme: "currency" | "resource" | "relationship" | "intel" | "rare_item";
|
||||
followupHooks: string[];
|
||||
};
|
||||
```
|
||||
|
||||
重点是:
|
||||
|
||||
- AI 告诉系统这份任务“像什么”
|
||||
- 不直接决定最终字段值
|
||||
- 不直接操作运行时状态
|
||||
|
||||
## 4.3 本地任务编译层
|
||||
|
||||
本地根据 `QuestIntent + QuestGenerationContext` 编译出真正可执行的任务合约:
|
||||
|
||||
```ts
|
||||
type QuestContract = {
|
||||
id: string;
|
||||
issuerNpcId: string;
|
||||
questArchetype: QuestIntent["narrativeType"];
|
||||
title: string;
|
||||
description: string;
|
||||
summary: string;
|
||||
steps: QuestStep[];
|
||||
rewards: QuestRewardPackage;
|
||||
narrativeBindings: QuestNarrativeBinding;
|
||||
failPolicy: "never" | "leave_scene" | "issuer_hostile" | "time_window";
|
||||
};
|
||||
|
||||
type QuestStep = {
|
||||
id: string;
|
||||
kind:
|
||||
| "defeat_monster"
|
||||
| "inspect_treasure"
|
||||
| "spar_with_npc"
|
||||
| "deliver_item"
|
||||
| "reach_scene"
|
||||
| "talk_to_npc";
|
||||
targetSceneId?: string;
|
||||
targetNpcId?: string;
|
||||
targetMonsterId?: string;
|
||||
targetItemId?: string;
|
||||
requiredCount: number;
|
||||
progress: number;
|
||||
revealText: string;
|
||||
completeText: string;
|
||||
};
|
||||
```
|
||||
|
||||
这层的职责是:
|
||||
|
||||
- 将 AI 的语义建议裁剪到系统允许的任务范式内
|
||||
- 确保每一个 step 都能被现有事件流推进
|
||||
- 给每一个任务生成稳定 id、稳定状态和可存档结构
|
||||
|
||||
## 4.4 任务运行时推进层
|
||||
|
||||
运行时只认本地任务合约和事件。
|
||||
|
||||
建议新增统一的任务推进事件:
|
||||
|
||||
```ts
|
||||
type QuestProgressSignal =
|
||||
| { kind: "monster_defeated"; sceneId?: string | null; monsterId: string }
|
||||
| { kind: "treasure_inspected"; sceneId?: string | null }
|
||||
| { kind: "npc_spar_completed"; npcId: string }
|
||||
| { kind: "npc_talk_completed"; npcId: string }
|
||||
| { kind: "scene_reached"; sceneId: string }
|
||||
| { kind: "item_delivered"; npcId: string; itemId: string; quantity: number };
|
||||
```
|
||||
|
||||
然后统一走:
|
||||
|
||||
- `applyQuestProgressSignal(contract, signal)`
|
||||
|
||||
而不是继续把推进逻辑分散到多处手写 helper。
|
||||
|
||||
这样做的价值是:
|
||||
|
||||
- 后续加新任务种类时,只需扩展 signal 与 compiler
|
||||
- `useStoryGeneration` 只负责发 signal,不负责理解具体任务细节
|
||||
- 测试可以直接覆盖“某信号输入 -> 某任务状态输出”
|
||||
|
||||
## 4.5 任务叙事回写层
|
||||
|
||||
AI 原生任务真正有价值的一层,不只是“生成任务”,而是“在任务推进时改写故事”。
|
||||
|
||||
建议在以下节点允许 AI 回写叙事:
|
||||
|
||||
1. 任务生成时
|
||||
2. 新阶段解锁时
|
||||
3. 任务完成但未交付时
|
||||
4. 交付奖励时
|
||||
5. 完成方式影响关系时
|
||||
|
||||
这层应该只接收:
|
||||
|
||||
- 已经确定的任务 contract
|
||||
- 已经发生的规则结果
|
||||
|
||||
而不是允许 AI 反向篡改规则结果。
|
||||
|
||||
## 5. 建议的数据结构升级
|
||||
|
||||
建议不要直接推翻当前 `QuestLogEntry`,而是在现有结构上新增一层 metadata。
|
||||
|
||||
## 5.1 新增任务元数据
|
||||
|
||||
```ts
|
||||
type QuestNarrativeBinding = {
|
||||
origin: "fallback_builder" | "ai_compiled";
|
||||
narrativeType: "bounty" | "escort" | "investigation" | "retrieval" | "relationship" | "trial";
|
||||
dramaticNeed: string;
|
||||
issuerGoal: string;
|
||||
playerHook: string;
|
||||
worldReason: string;
|
||||
followupHooks: string[];
|
||||
};
|
||||
|
||||
type QuestRewardPackage = {
|
||||
currency: number;
|
||||
affinityBonus: number;
|
||||
items: InventoryItem[];
|
||||
intel?: {
|
||||
codexEntry?: string;
|
||||
rumorText?: string;
|
||||
unlockedSceneId?: string;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## 5.2 扩展现有 QuestLogEntry
|
||||
|
||||
```ts
|
||||
interface QuestLogEntry {
|
||||
id: string;
|
||||
issuerNpcId: string;
|
||||
issuerNpcName: string;
|
||||
sceneId: string | null;
|
||||
title: string;
|
||||
description: string;
|
||||
summary: string;
|
||||
objective: LegacyQuestObjective;
|
||||
progress: number;
|
||||
status: QuestStatus;
|
||||
completionNotified?: boolean;
|
||||
reward: QuestReward;
|
||||
rewardText: string;
|
||||
|
||||
narrativeBinding?: QuestNarrativeBinding;
|
||||
steps?: QuestStep[];
|
||||
activeStepId?: string | null;
|
||||
visibleStage?: number;
|
||||
hiddenFlags?: string[];
|
||||
}
|
||||
```
|
||||
|
||||
这样做的原因是:
|
||||
|
||||
- UI 可以先继续读旧字段
|
||||
- 新逻辑逐步切到 `steps`
|
||||
- 旧存档仍然能兼容
|
||||
|
||||
## 5.3 扩展任务状态
|
||||
|
||||
建议未来把任务状态扩展为:
|
||||
|
||||
```ts
|
||||
type QuestStatus =
|
||||
| "discovered"
|
||||
| "active"
|
||||
| "ready_to_turn_in"
|
||||
| "completed"
|
||||
| "turned_in"
|
||||
| "failed"
|
||||
| "expired";
|
||||
```
|
||||
|
||||
MVP 阶段不一定要一次性全上,但至少建议引入:
|
||||
|
||||
- `discovered`
|
||||
- `ready_to_turn_in`
|
||||
|
||||
因为 AI 原生任务里,“被感知到”与“正式接取”往往不是同一刻。
|
||||
|
||||
## 6. AI 任务 contract 设计
|
||||
|
||||
## 6.1 新增专用 prompt contract
|
||||
|
||||
建议新增专用任务生成接口,而不是继续把任务生成混在通用 story completion 里。
|
||||
|
||||
建议新增:
|
||||
|
||||
- `src/services/questDirector.ts`
|
||||
- `src/services/questPrompt.ts`
|
||||
- `src/services/questTypes.ts`
|
||||
|
||||
AI 返回格式建议如下:
|
||||
|
||||
```json
|
||||
{
|
||||
"intent": {
|
||||
"narrativeType": "investigation",
|
||||
"dramaticNeed": "NPC 怀疑附近遗迹并不安全,但不敢直接深入",
|
||||
"issuerGoal": "确认遗迹是否值得继续接近",
|
||||
"playerHook": "玩家当前正好在此地,并且具备应对未知风险的能力",
|
||||
"worldReason": "最近场景和对话都指向此处存在被掩盖的旧痕迹",
|
||||
"recommendedObjectiveKinds": ["inspect_treasure", "talk_to_npc"],
|
||||
"urgency": "medium",
|
||||
"intimacy": "cooperative",
|
||||
"rewardTheme": "intel",
|
||||
"followupHooks": ["遗迹背后的旧势力", "NPC 曾经来过这里"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
重点规则:
|
||||
|
||||
- 只允许输出意图,不允许直接输出奖励数值
|
||||
- 只允许使用本地支持的 objective kinds
|
||||
- 只描述建议,不越权改状态
|
||||
|
||||
## 6.2 AI 负责的字段
|
||||
|
||||
AI 可以负责:
|
||||
|
||||
- `title` 的语义方向
|
||||
- `description` 的叙事质感
|
||||
- `summary` 的自然语言表达
|
||||
- `dramaticNeed`
|
||||
- `issuerGoal`
|
||||
- `playerHook`
|
||||
- `worldReason`
|
||||
- `followupHooks`
|
||||
|
||||
AI 不负责:
|
||||
|
||||
- `id`
|
||||
- `requiredCount`
|
||||
- `status`
|
||||
- `progress`
|
||||
- `currency`
|
||||
- `affinityBonus`
|
||||
- 直接生成超出规则支持范围的 objective
|
||||
|
||||
## 6.3 本地编译时的裁决逻辑
|
||||
|
||||
本地应当做如下校验:
|
||||
|
||||
1. objective kind 是否在 allowlist 内
|
||||
2. 当前 scene / npc / monster / treasure 是否真的存在
|
||||
3. 当前 NPC 是否已存在未完成任务
|
||||
4. 当前任务是否会与现有任务重复或冲突
|
||||
5. 奖励是否符合 rarity / economy / relationship budget
|
||||
|
||||
如果 AI 返回不合法:
|
||||
|
||||
- 降级到 deterministic builder
|
||||
- 但仍可保留 AI 提供的部分叙事字段作为文案参考
|
||||
|
||||
## 7. 任务生成时机设计
|
||||
|
||||
建议不是每次点 NPC 都无条件生成任务,而是让系统先判断“是否值得生成任务机会”。
|
||||
|
||||
## 7.1 建议的触发点
|
||||
|
||||
适合生成任务机会的节点:
|
||||
|
||||
1. 初次与某 NPC 深入互动
|
||||
2. 某 NPC affinity 达到新阶段
|
||||
3. 某场景首次发现异常实体或藏宝线索
|
||||
4. 某条最近剧情暗示了未解决的局面
|
||||
5. 玩家在当前区域停留较久且缺少明确目标
|
||||
|
||||
## 7.2 不建议触发的节点
|
||||
|
||||
以下节点不建议频繁生成任务:
|
||||
|
||||
- 每次普通聊天都生成任务
|
||||
- 每次旅行都自动冒出新任务
|
||||
- 当前已有多个未完成任务时继续塞新任务
|
||||
- AI 仅凭最近一句话就突然变出高强度委托
|
||||
|
||||
## 7.3 任务机会判断器
|
||||
|
||||
建议先做本地 `questOpportunityEvaluator`:
|
||||
|
||||
```ts
|
||||
type QuestOpportunity = {
|
||||
shouldOffer: boolean;
|
||||
reason: string;
|
||||
suggestedIssuerNpcId?: string;
|
||||
suggestedThreatType?: "monster" | "treasure" | "relationship" | "travel";
|
||||
};
|
||||
```
|
||||
|
||||
这层先判断“该不该生成”,再决定“交给 AI 生成什么”。
|
||||
|
||||
这样能避免模型过度产出任务。
|
||||
|
||||
## 8. 任务推进设计
|
||||
|
||||
## 8.1 从“单字段进度”升级到“步骤机”
|
||||
|
||||
当前任务推进是:
|
||||
|
||||
- 一个 objective
|
||||
- 一个 progress
|
||||
|
||||
建议升级为:
|
||||
|
||||
- 多 step
|
||||
- 每个 step 自己推进
|
||||
- active step 决定当前 UI 展示
|
||||
|
||||
例如:
|
||||
|
||||
```ts
|
||||
[
|
||||
{
|
||||
id: "step_investigate_ruins",
|
||||
kind: "inspect_treasure",
|
||||
targetSceneId: "ruins_gate",
|
||||
requiredCount: 1
|
||||
},
|
||||
{
|
||||
id: "step_report_back",
|
||||
kind: "talk_to_npc",
|
||||
targetNpcId: "npc_scholar_lin",
|
||||
requiredCount: 1
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
这样任务就能表达:
|
||||
|
||||
- 先调查
|
||||
- 再回报
|
||||
- 再领取奖励
|
||||
|
||||
## 8.2 允许“隐式推进,显式揭示”
|
||||
|
||||
AI 原生任务最有意思的一点,是任务可以先发生推进,再在叙事上被揭示。
|
||||
|
||||
例如:
|
||||
|
||||
- 玩家在探索时先完成了调查
|
||||
- 回去交谈时 NPC 才明确说出“你已经替我确认了那里的情况”
|
||||
|
||||
这意味着系统上应允许:
|
||||
|
||||
- step 已完成
|
||||
- 但 `visibleStage` 仍未切换到下一步,直到触发 reveal
|
||||
|
||||
这样任务会更像故事,而不是机械 checklist。
|
||||
|
||||
## 9. 奖励设计
|
||||
|
||||
## 9.1 奖励不只是一包货币和道具
|
||||
|
||||
AI 原生任务里,奖励至少应拆成 4 类:
|
||||
|
||||
1. 经济奖励
|
||||
- currency
|
||||
- item bundles
|
||||
2. 关系奖励
|
||||
- affinity
|
||||
- disclosure stage / warmth stage 解锁
|
||||
3. 信息奖励
|
||||
- rumor
|
||||
- codex entry
|
||||
- 场景/势力线索
|
||||
4. 机会奖励
|
||||
- 解锁新 scene
|
||||
- 解锁新 NPC 交互
|
||||
- 解锁后续任务机会
|
||||
|
||||
## 9.2 AI 负责解释奖励来源,不负责定奖励数值
|
||||
|
||||
建议奖励设计继续遵守:
|
||||
|
||||
- AI 解释“为什么这个 NPC 给这些”
|
||||
- 本地决定“究竟给多少”
|
||||
|
||||
例如:
|
||||
|
||||
- AI 可说:这是 NPC 私藏的旧护符、只愿交给值得信任的人
|
||||
- 本地则决定:这是 `rare relic + affinityBonus 12 + currency 72`
|
||||
|
||||
## 10. UI 表达建议
|
||||
|
||||
当前 `AdventurePanel` 已经有不错的任务面板基础,AI 原生任务系统建议补的不是“更多卡片”,而是“更多阶段感”。
|
||||
|
||||
建议新增以下 UI 信息:
|
||||
|
||||
1. 任务来源语义
|
||||
- 谁委托
|
||||
- 为什么此刻委托
|
||||
2. 当前阶段标题
|
||||
- 当前不是只看进度条,还要看“现在在做哪一步”
|
||||
3. 任务关系状态
|
||||
- 交易型 / 协作型 / 信任型
|
||||
4. 任务后续钩子
|
||||
- 这件事可能会牵出什么
|
||||
|
||||
UI 上不建议直接暴露给玩家的内部字段:
|
||||
|
||||
- `dramaticNeed`
|
||||
- `followupHooks` 原始数组
|
||||
- 所有 hidden flags
|
||||
|
||||
这些字段应该被整理成更自然的面板文案。
|
||||
|
||||
## 11. 与当前仓库的接入点建议
|
||||
|
||||
## 11.1 第一批建议改动的文件
|
||||
|
||||
建议先从以下文件接入:
|
||||
|
||||
- `src/types/story.ts`
|
||||
- 扩展 `QuestLogEntry`
|
||||
- `src/services/aiTypes.ts`
|
||||
- 增加任务生成上下文
|
||||
- `src/data/questFlow.ts`
|
||||
- 增加 contract compiler 和 step progression
|
||||
- `src/hooks/story/npcEncounterActions.ts`
|
||||
- 将 `quest_accept` 改为“机会判断 -> AI 意图 -> 本地编译”
|
||||
- `src/services/prompt.ts`
|
||||
- 不直接负责任务生成,拆出 `questPrompt.ts`
|
||||
|
||||
## 11.2 第一批不建议碰的区域
|
||||
|
||||
这轮不建议一开始就深入改:
|
||||
|
||||
- `AdventurePanel` 的整体结构
|
||||
- `useStoryGeneration` 的全部 orchestrator
|
||||
- 所有现存 AI story prompt
|
||||
|
||||
原因是任务系统已经是主链路的一部分,第一步应该先把 contract 立住,而不是把整个 story 系统一起重写。
|
||||
|
||||
## 12. MVP 落地顺序
|
||||
|
||||
## 阶段 A:先做任务 contract,不改全部表现
|
||||
|
||||
新增:
|
||||
|
||||
- `src/services/questTypes.ts`
|
||||
- `src/services/questDirector.ts`
|
||||
- `src/services/questPrompt.ts`
|
||||
|
||||
目标:
|
||||
|
||||
- AI 能返回 `QuestIntent`
|
||||
- 本地能编译成 `QuestContract`
|
||||
- 旧 UI 仍然照常显示
|
||||
|
||||
## 阶段 B:把 NPC 接任务改成 AI 原生生成
|
||||
|
||||
先只改:
|
||||
|
||||
- `quest_accept`
|
||||
|
||||
此时:
|
||||
|
||||
- 仍沿用现有三种基础 objective kind
|
||||
- 但 title / description / rewardText / followupHooks 开始变成上下文生成
|
||||
|
||||
这是最稳的切入点,因为:
|
||||
|
||||
- 影响面小
|
||||
- 可回退
|
||||
- 容易观察质量提升
|
||||
|
||||
## 阶段 C:把任务推进改成 step + signal
|
||||
|
||||
重点改:
|
||||
|
||||
- `applyQuestProgressFromMonsterVictory`
|
||||
- `applyQuestProgressFromTreasure`
|
||||
- `applyQuestProgressFromSpar`
|
||||
|
||||
把它们收口到统一 signal reducer。
|
||||
|
||||
## 阶段 D:把交付奖励与后续机会联动起来
|
||||
|
||||
让 `quest_turn_in` 不再只是:
|
||||
|
||||
- 发货币
|
||||
- 发道具
|
||||
- 加 affinity
|
||||
|
||||
而是还能:
|
||||
|
||||
- 写入 rumor / intel
|
||||
- 解锁后续任务机会
|
||||
- 改变 NPC 对话阶段
|
||||
|
||||
## 13. 为什么这套方案适合当前仓库
|
||||
|
||||
这套方案不是重做一套新玩法,而是顺着仓库已经验证过的边界继续深化:
|
||||
|
||||
1. 仓库已经验证“AI 负责叙事,本地负责规则”是正确方向
|
||||
2. 仓库已经有现成的 quest UI、quest status、quest reward、npc affinity 链路
|
||||
3. 当前 `useStoryGeneration` 已经在汇总 story/combat/treasure/npc 事件,天然适合继续发 quest signal
|
||||
4. 当前 `StoryGenerationContext` 已经足够强,只是还缺一个任务专用 contract
|
||||
|
||||
所以这次真正缺的不是“再加一个任务面板”,而是:
|
||||
|
||||
**把任务从‘规则附属物’升级成‘叙事与规则之间的正式中介层’。**
|
||||
|
||||
## 14. 最后结论
|
||||
|
||||
对这个项目来说,理想的 AI 原生任务系统,不应该做成“AI 随机发委托”,而应该做成:
|
||||
|
||||
1. 系统先判断当前局面是否值得生成任务机会
|
||||
2. AI 根据世界、场景、NPC、最近剧情和玩家状态,生成任务意图
|
||||
3. 本地将任务意图编译成稳定、可测试、可持久化的任务合约
|
||||
4. 运行时通过统一 signal 推进任务步骤
|
||||
5. AI 在任务生成、阶段切换、完成交付时回写叙事质感
|
||||
6. 奖励既包括资源,也包括关系、情报和后续机会
|
||||
|
||||
这样生成出来的任务,才会同时满足三件事:
|
||||
|
||||
- **像从当前剧情里自然长出来的**
|
||||
- **像系统里可验证、可推进、可存档的**
|
||||
- **像 AI 原生游戏真正该有的任务结构**
|
||||
526
docs/prd/AI_NATIVE_RUNTIME_ITEM_GENERATION_DESIGN.md
Normal file
526
docs/prd/AI_NATIVE_RUNTIME_ITEM_GENERATION_DESIGN.md
Normal file
@@ -0,0 +1,526 @@
|
||||
# AI 原生游戏的运行时物品生成设计建议
|
||||
|
||||
更新时间:`2026-04-02`
|
||||
|
||||
## 0. 设计目标
|
||||
|
||||
这份方案针对当前项目,回答的是:
|
||||
|
||||
**如果要把运行时物品生成做成“AI 原生”,应该怎样设计,才能既贴合背景 / NPC / 当前事件,又不会把数值系统做崩。**
|
||||
|
||||
这里的“AI 原生”不是指“让模型直接胡乱造装备”,而是指:
|
||||
|
||||
- 物品的**语义、出处、命名、关系、叙事理由**由 AI 根据上下文生成
|
||||
- 物品的**稀有度、build 标签、数值预算、持久化结果**由本地规则编译和裁剪
|
||||
|
||||
一句话:
|
||||
|
||||
**AI 决定这是什么,规则决定它能做什么。**
|
||||
|
||||
## 1. 设计原则
|
||||
|
||||
## 1.1 物品必须是“当前故事的一部分”
|
||||
|
||||
运行时物品不该只是“随机掉了个稀有剑”。
|
||||
|
||||
每个重要物品都至少回答 4 个问题:
|
||||
|
||||
1. 它为什么会出现在这里?
|
||||
2. 它和哪个场景背景有关?
|
||||
3. 它和哪个 NPC / 势力 / 怪物有关?
|
||||
4. 它对当前玩家 build 为什么有意义?
|
||||
|
||||
## 1.2 物品收益以“标签倾向”优先,纯数值为辅
|
||||
|
||||
建议把 AI 原生物品的主要收益做成两大类:
|
||||
|
||||
1. **获得 build 标签**
|
||||
- 永久标签:主要由装备 / 稀有饰品 / 关键遗物提供
|
||||
- 限时标签:主要由消耗品 / 临时护符 / 战场符箓提供
|
||||
2. **少量直接数值**
|
||||
- 只做补充,不做主菜
|
||||
- 避免数值碾压 build 语义
|
||||
|
||||
也就是说,运行时物品的第一身份应该是:
|
||||
|
||||
**“把玩家的构筑推向某种风格”**
|
||||
|
||||
而不是:
|
||||
|
||||
**“给一个更大的攻击数字”**
|
||||
|
||||
## 1.3 物品生成必须显式读取上下文
|
||||
|
||||
建议运行时物品生成必须至少读取以下上下文:
|
||||
|
||||
- 世界背景
|
||||
- 当前场景 / 地标 / 氛围
|
||||
- 当前 encounter / 相关 NPC / 相关怪物
|
||||
- 最近 1~3 个关键剧情行为
|
||||
- 玩家当前装备与激活 build 标签
|
||||
- 玩家当前缺口
|
||||
- 续航不够
|
||||
- 控场不足
|
||||
- 缺切后
|
||||
- 缺护体
|
||||
- 当前奖励渠道
|
||||
- 宝藏
|
||||
- 交易
|
||||
- 委托
|
||||
- 掉落
|
||||
- 观察所得
|
||||
|
||||
## 1.4 AI 只产出“意图”,本地编译成成品
|
||||
|
||||
不要让 AI 直接返回最终 `InventoryItem`。
|
||||
|
||||
建议让 AI 先返回的是“物品意图”:
|
||||
|
||||
- 物品主题
|
||||
- 物品来源
|
||||
- 关联对象
|
||||
- 倾向标签
|
||||
- 永久 / 限时
|
||||
- 用途类型
|
||||
|
||||
然后本地再把它编译成:
|
||||
|
||||
- `category`
|
||||
- `rarity`
|
||||
- `tags`
|
||||
- `statProfile`
|
||||
- `useProfile`
|
||||
- `buildProfile`
|
||||
- `value`
|
||||
|
||||
这样才能稳定、可测试、可平衡。
|
||||
|
||||
## 2. 建议的系统结构
|
||||
|
||||
建议把新系统拆成 4 层。
|
||||
|
||||
## 2.1 上下文采样层
|
||||
|
||||
输入一份统一的 `RuntimeItemContext`:
|
||||
|
||||
```ts
|
||||
type RuntimeItemContext = {
|
||||
worldType: WorldType | null;
|
||||
customWorldProfile?: CustomWorldProfile | null;
|
||||
sceneId?: string | null;
|
||||
sceneName?: string | null;
|
||||
sceneDescription?: string | null;
|
||||
landmarkHints?: string[];
|
||||
encounter?: Encounter | null;
|
||||
relatedNpcState?: NpcPersistentState | null;
|
||||
recentStoryMoments: StoryMoment[];
|
||||
playerCharacter: Character;
|
||||
playerEquipment: EquipmentLoadout;
|
||||
activeBuildBuffs: TimedBuildBuff[];
|
||||
activeBuildTags: string[];
|
||||
generationChannel: "treasure" | "npc_trade" | "npc_reward" | "monster_drop" | "quest_reward" | "discovery";
|
||||
};
|
||||
```
|
||||
|
||||
这一层的目标不是做物品,而是把“这次生成到底是在什么局面下发生”说清楚。
|
||||
|
||||
## 2.2 AI 意图层
|
||||
|
||||
让模型只生成一个轻量蓝图:
|
||||
|
||||
```ts
|
||||
type RuntimeItemIntent = {
|
||||
narrativeTheme: string;
|
||||
sourceKind: "npc" | "scene" | "monster" | "faction" | "quest" | "ancient";
|
||||
sourceName: string;
|
||||
reasonToAppear: string;
|
||||
itemArchetype: "equipment" | "consumable" | "material" | "relic" | "quest";
|
||||
permanence: "permanent" | "timed";
|
||||
desiredBuildTags: string[];
|
||||
desiredStatBias: Array<"hp" | "mana" | "outgoing" | "mitigation" | "cooldown">;
|
||||
relationHooks: string[];
|
||||
};
|
||||
```
|
||||
|
||||
重点是:
|
||||
|
||||
- AI 负责“它像什么”
|
||||
- 不负责“最终数值是多少”
|
||||
|
||||
## 2.3 本地编译层
|
||||
|
||||
本地把 `RuntimeItemIntent` 编译成正式物品:
|
||||
|
||||
- 先做标签规范化
|
||||
- 走 `normalizeBuildTags`
|
||||
- 再判定品类
|
||||
- 装备 / 消耗品 / 材料 / 稀有品 / 剧情物
|
||||
- 再套预算
|
||||
- rarity budget
|
||||
- stat budget
|
||||
- tag budget
|
||||
- duration budget
|
||||
- 最后写入:
|
||||
- `buildProfile`
|
||||
- `statProfile`
|
||||
- `useProfile`
|
||||
- `value`
|
||||
|
||||
## 2.4 叙事回写层
|
||||
|
||||
物品成品生成后,再把这些信息回写到叙事文本:
|
||||
|
||||
- 它看起来像什么
|
||||
- 它为什么来自这个 NPC / 地点
|
||||
- 它和哪条线索或关系有关
|
||||
- 玩家为什么会觉得“这东西确实该在这里出现”
|
||||
|
||||
这一步仍然可以交给 AI,但只能读“已经生成好的成品”。
|
||||
|
||||
## 3. 建议的物品类型设计
|
||||
|
||||
## 3.1 永久 build 标签物品
|
||||
|
||||
适合:
|
||||
|
||||
- 武器
|
||||
- 护甲
|
||||
- 饰品
|
||||
- 关键遗物
|
||||
|
||||
建议规则:
|
||||
|
||||
- 常规装备最多给 `1` 个核心永久标签
|
||||
- 稀有或史诗装备可给 `1` 个核心标签 + `1` 个协同标签
|
||||
- 传说物品可以额外充当 set / faction / NPC build 锚点
|
||||
|
||||
示例:
|
||||
|
||||
- “渡口缉索短刃”
|
||||
- 永久标签:`快袭`
|
||||
- 协同标签:`追击`
|
||||
- 小量数值:`outgoingDamageBonus`
|
||||
|
||||
- “镇河旧誓铜符”
|
||||
- 永久标签:`守御`
|
||||
- 协同标签:`护体`
|
||||
- 小量数值:`maxManaBonus`
|
||||
|
||||
## 3.2 限时 build 标签物品
|
||||
|
||||
适合:
|
||||
|
||||
- 药剂
|
||||
- 符箓
|
||||
- 临时护符
|
||||
- 战场应急工具
|
||||
|
||||
建议规则:
|
||||
|
||||
- 只通过 `useProfile.buildBuffs` 生效
|
||||
- 持续 `1~3` 回合
|
||||
- 一次最多提供 `1~2` 个标签
|
||||
- 可叠加刷新时长,但不建议无限堆层
|
||||
|
||||
示例:
|
||||
|
||||
- “雾沼息行符”
|
||||
- `2` 回合获得:`风行`、`游击`
|
||||
- “火工催压油”
|
||||
- `1` 回合获得:`爆发`
|
||||
- 同时 `cooldownReduction: 1`
|
||||
|
||||
## 3.3 少量直接数值物品
|
||||
|
||||
适合做补充,而不是 build 主轴。
|
||||
|
||||
建议预算:
|
||||
|
||||
- 武器:
|
||||
- `outgoingDamageBonus` 小幅增加
|
||||
- 护甲:
|
||||
- `maxHpBonus`
|
||||
- 或少量 `incomingDamageMultiplier`
|
||||
- 饰品:
|
||||
- `maxManaBonus`
|
||||
- 或轻量 `outgoingDamageBonus`
|
||||
- 消耗品:
|
||||
- `hpRestore`
|
||||
- `manaRestore`
|
||||
- `cooldownReduction`
|
||||
|
||||
建议约束:
|
||||
|
||||
- 常规运行时随机物品,数值权重应低于标签权重
|
||||
- 不要让一个完全无标签的高数值物品压过语义鲜明的 build 物品
|
||||
|
||||
## 4. 如何保证“高度贴合背景和 NPC”
|
||||
|
||||
## 4.1 生成必须绑定三个锚点
|
||||
|
||||
建议每个 AI 原生物品至少有三个锚点:
|
||||
|
||||
1. **场景锚点**
|
||||
- 例如矿道、渡口、祭坛、雾林、裂界前线
|
||||
2. **关系锚点**
|
||||
- 某个 NPC、某个势力、某类怪物、某段旧事
|
||||
3. **玩法锚点**
|
||||
- 玩家当前 build 缺口或当前风险
|
||||
|
||||
如果一个候选物品说不清这三个锚点,就不要发。
|
||||
|
||||
## 4.2 命名要使用世界词汇表,不要只按品类随机拼接
|
||||
|
||||
建议名字由三部分组成:
|
||||
|
||||
```text
|
||||
来源词 + 关系词 + 品类词
|
||||
```
|
||||
|
||||
例如:
|
||||
|
||||
- “锁风渡缉索短刃”
|
||||
- “药谷回岚灵露”
|
||||
- “断碑旧誓护心佩”
|
||||
- “裂界巡守压纹符”
|
||||
|
||||
来源词来自:
|
||||
|
||||
- 场景 / 地标
|
||||
- NPC 身份
|
||||
- 势力 / 流派
|
||||
- 当前任务线
|
||||
|
||||
而不是仅仅:
|
||||
|
||||
- 稀有前缀 + 武器名
|
||||
|
||||
## 4.3 描述文案要回答“为什么现在拿到它”
|
||||
|
||||
建议每个重要物品描述都带一句运行时来源理由:
|
||||
|
||||
- 是谁留下的
|
||||
- 是从谁身上掉的
|
||||
- 为什么这时能交易给你
|
||||
- 为什么这次观察能发现它
|
||||
|
||||
这样玩家会感觉它是叙事结果,而不是系统掉表。
|
||||
|
||||
## 5. 建议的平衡规则
|
||||
|
||||
## 5.1 稀有度决定预算,不直接决定强度飞跃
|
||||
|
||||
建议把稀有度主要用于控制:
|
||||
|
||||
- 可带多少标签
|
||||
- 是否允许 set / faction 锚点
|
||||
- 叙事权重和经济价值
|
||||
|
||||
而不是简单理解为:
|
||||
|
||||
- 稀有度越高,数值就暴涨
|
||||
|
||||
建议预算参考:
|
||||
|
||||
| 稀有度 | 永久标签 | 限时标签 | 数值预算 |
|
||||
| --- | --- | --- | --- |
|
||||
| common | 0~1 | 1 | 很小 |
|
||||
| uncommon | 1 | 1~2 | 小 |
|
||||
| rare | 1+1协同 | 2 | 中等偏小 |
|
||||
| epic | 2 或 set 锚点 | 2 | 中等 |
|
||||
| legendary | 2 + 关系锚点 | 2~3 | 中等,但仍受上限约束 |
|
||||
|
||||
## 5.2 当前阶段优先支持“补短板”和“定方向”
|
||||
|
||||
建议运行时物品生成时,优先做这两类决策:
|
||||
|
||||
1. **补短板**
|
||||
- 玩家 build 缺续航,就更容易拿到 `回复 / 续战 / 护体`
|
||||
- 玩家 build 缺切后,就更容易拿到 `快袭 / 突进 / 风行`
|
||||
2. **定方向**
|
||||
- 当玩家已经有明显 build 倾向时,再投放协同物品,把这条路做深
|
||||
|
||||
不建议一味“追高强度”,否则玩家会频繁被迫换流派。
|
||||
|
||||
## 5.3 运行时物品要避免频繁彻底改流派
|
||||
|
||||
建议把随机物品对 build 的影响控制为:
|
||||
|
||||
- 大多数时候:
|
||||
- 强化现有方向
|
||||
- 或提供邻近方向
|
||||
- 少数特殊奖励:
|
||||
- 才允许提供“转流派锚点”
|
||||
|
||||
这样玩家会觉得构筑在成长,而不是被系统不断推翻。
|
||||
|
||||
## 6. 建议的接入点
|
||||
|
||||
## 6.1 宝藏奖励
|
||||
|
||||
当前最适合先改的是 `resolveTreasureReward`。
|
||||
|
||||
建议把它升级为:
|
||||
|
||||
- 先收集:
|
||||
- scene preset
|
||||
- treasure hint
|
||||
- 最近事件
|
||||
- 当前遭遇 NPC / 怪物痕迹
|
||||
- 再生成 1 个上下文物品
|
||||
- 剩余奖励继续走稳定资源:
|
||||
- 货币
|
||||
- 基础材料
|
||||
- 常规补给
|
||||
|
||||
这样宝藏会从“奖励池”变成“叙事发现”。
|
||||
|
||||
## 6.2 NPC 交易 / 赠礼 / 帮助
|
||||
|
||||
建议给 NPC 增加“关系定制物品”出口:
|
||||
|
||||
- 商人:
|
||||
- 卖与你当前 build 有关、但带其身份烙印的物品
|
||||
- 帮助奖励:
|
||||
- 给限时道具或一次性符箓
|
||||
- 高好感 NPC:
|
||||
- 才会给带永久 build 标签的私人遗物/信物
|
||||
|
||||
这样交易和关系系统就会真正联动。
|
||||
|
||||
## 6.3 怪物掉落
|
||||
|
||||
建议掉落分两层:
|
||||
|
||||
1. 基础掉落
|
||||
- 继续走 preset / material / loot table
|
||||
2. 语义掉落
|
||||
- 从怪物生态、战斗风格、所在场景生成一个 build 倾向物品或精粹
|
||||
|
||||
例如:
|
||||
|
||||
- 重甲守卫掉:
|
||||
- `守御精粹`
|
||||
- `护体甲片`
|
||||
- 雾林刺客掉:
|
||||
- `快袭羽饰`
|
||||
- `风行药囊`
|
||||
|
||||
## 6.4 委托奖励
|
||||
|
||||
任务奖励最适合给:
|
||||
|
||||
- 带关系来源的永久物品
|
||||
- 剧情关键 rare / relic
|
||||
- 能把玩家 build 往下一个阶段推一把的奖励
|
||||
|
||||
这类物品最适合绑定:
|
||||
|
||||
- 委托发布人
|
||||
- 委托目标
|
||||
- 地标
|
||||
- 任务线真相
|
||||
|
||||
## 7. 建议新增的数据结构
|
||||
|
||||
建议在不推翻现有 `InventoryItem` 的前提下,新增一层来源元数据。
|
||||
|
||||
```ts
|
||||
type RuntimeItemNarrativeBinding = {
|
||||
generationChannel: "treasure" | "npc_trade" | "npc_reward" | "monster_drop" | "quest_reward" | "discovery";
|
||||
sceneId?: string;
|
||||
sceneName?: string;
|
||||
relatedNpcId?: string;
|
||||
relatedNpcName?: string;
|
||||
relatedMonsterId?: string;
|
||||
relatedFaction?: string;
|
||||
storyReason: string;
|
||||
eventHook?: string;
|
||||
};
|
||||
|
||||
type RuntimeItemMetadata = {
|
||||
origin: "catalog" | "procedural" | "ai_compiled";
|
||||
seedKey?: string;
|
||||
narrativeBinding?: RuntimeItemNarrativeBinding;
|
||||
};
|
||||
```
|
||||
|
||||
然后在 `InventoryItem` 上增加可选字段:
|
||||
|
||||
```ts
|
||||
interface InventoryItem {
|
||||
runtimeMetadata?: RuntimeItemMetadata;
|
||||
}
|
||||
```
|
||||
|
||||
这样后面 UI、日志、剧情回放都能解释:
|
||||
|
||||
**这个物品到底从哪来。**
|
||||
|
||||
## 8. 推荐的最小落地顺序
|
||||
|
||||
建议不要一步把所有入口都改成 AI 原生,而是分三阶段。
|
||||
|
||||
## 阶段 A:先做“上下文编译器”,不改所有入口
|
||||
|
||||
新增:
|
||||
|
||||
- `src/data/runtimeItemDirector.ts`
|
||||
- `src/data/runtimeItemCompiler.ts`
|
||||
- `src/types/runtimeItem.ts`
|
||||
|
||||
先只负责:
|
||||
|
||||
- 接收上下文
|
||||
- 生成意图
|
||||
- 编译成 `InventoryItem`
|
||||
|
||||
## 阶段 B:先接一个入口验证
|
||||
|
||||
优先接:
|
||||
|
||||
- `treasureInteractions.ts`
|
||||
|
||||
因为它最独立,风险最小,最容易观察“贴合背景”的提升。
|
||||
|
||||
## 阶段 C:再接 NPC 与任务
|
||||
|
||||
等宝藏验证稳定后,再接:
|
||||
|
||||
- NPC 交易
|
||||
- NPC 帮助奖励
|
||||
- 任务奖励
|
||||
|
||||
最后再考虑怪物掉落的 AI 原生语义层。
|
||||
|
||||
## 9. 这套方案和当前仓库为什么契合
|
||||
|
||||
这套方案不是另起炉灶,而是直接复用当前仓库已经有的能力:
|
||||
|
||||
- 复用 `InventoryItem`
|
||||
- 复用 `ItemStatProfile`
|
||||
- 复用 `ItemUseProfile`
|
||||
- 复用 `ItemBuildProfile`
|
||||
- 复用 `normalizeBuildTags`
|
||||
- 复用 `TimedBuildBuff`
|
||||
- 复用装备、背包、锻造、build 结算
|
||||
- 复用“AI 叙事,本地规则结算”的总边界
|
||||
|
||||
所以它不是推翻当前系统,而是补上当前系统最缺的那一层:
|
||||
|
||||
**上下文感知的运行时物品导演。**
|
||||
|
||||
## 10. 最后结论
|
||||
|
||||
对这个项目来说,理想的 AI 原生运行时物品生成,不应该做成“AI 随机喷装备”,而应该做成:
|
||||
|
||||
1. AI 根据场景、背景、NPC、最近事件和玩家 build,先生成物品语义意图。
|
||||
2. 本地规则把这个意图编译成带永久或限时 build 标签、并附带少量数值加成的正式物品。
|
||||
3. 物品必须能解释“为什么它会在这里,由谁带来,对当前玩家为什么有意义”。
|
||||
4. 宝藏、交易、任务、掉落都逐步接入同一套导演层。
|
||||
|
||||
这样生成出来的物品才会同时满足三件事:
|
||||
|
||||
- **像故事里长出来的**
|
||||
- **像 build 里需要的**
|
||||
- **像系统里可控的**
|
||||
1084
docs/prd/AI_NATIVE_UNIFIED_ROLE_ATTRIBUTE_SYSTEM_PRD_2026-04-02.md
Normal file
1084
docs/prd/AI_NATIVE_UNIFIED_ROLE_ATTRIBUTE_SYSTEM_PRD_2026-04-02.md
Normal file
File diff suppressed because it is too large
Load Diff
414
docs/prd/BUILD_SYSTEM_ATTRIBUTE_SIMILARITY_PRD_2026-04-02.md
Normal file
414
docs/prd/BUILD_SYSTEM_ATTRIBUTE_SIMILARITY_PRD_2026-04-02.md
Normal file
@@ -0,0 +1,414 @@
|
||||
# Build 系统重构 PRD:标签-属性相似度模型
|
||||
|
||||
更新时间:`2026-04-02`
|
||||
|
||||
## 1. 背景
|
||||
|
||||
当前 Build 系统的核心实现位于:
|
||||
|
||||
- `src/data/buildDamage.ts`
|
||||
- `src/data/buildTags.ts`
|
||||
- `src/data/buildTagSimilarity.generated.ts`
|
||||
|
||||
现状不是“标签各自独立生效”,而是:
|
||||
|
||||
1. 先收集角色标签、装备标签、套装标签、Buff 标签。
|
||||
2. 再计算“每个标签与其他所有标签”的相似度。
|
||||
3. 用标签之间的整体相互作用,得到最终 `buildDamageMultiplier`。
|
||||
|
||||
这套机制已经能跑,但存在 4 个明显问题:
|
||||
|
||||
1. 解释成本高。玩家很难理解“为什么我多带一个标签,所有旧标签的贡献都变了”。
|
||||
2. 平衡难度高。任意新增一个标签,都会对整个标签集合产生连锁影响。
|
||||
3. 角色感不够强。当前倍率更像“标签团簇强度”,而不是“这个标签是否适合当前角色属性”。
|
||||
4. 扩展不稳定。策划继续扩标签、套装、Buff 时,组合爆炸会越来越明显。
|
||||
|
||||
因此,本次重构目标是把 Build 系统从“标签互相影响”改为“标签分别匹配玩家扮演角色的属性画像”,让每个标签的收益来源更直观、更可控。
|
||||
|
||||
## 2. 目标
|
||||
|
||||
### 2.1 产品目标
|
||||
|
||||
建立一套新的 Build 计算模型:
|
||||
|
||||
- 不再计算标签与标签之间的互相影响。
|
||||
- 改为计算“每个标签”和“角色每个属性”的相似度。
|
||||
- 再根据角色属性分布,决定该标签的修正加成倍数。
|
||||
- 所有标签的总收益由“单标签贡献求和”得到,而不是由“标签网络效应”得到。
|
||||
|
||||
### 2.2 设计目标
|
||||
|
||||
新系统需要满足:
|
||||
|
||||
1. 可解释:每个标签为什么强、强在哪个属性上,都能拆出来看。
|
||||
2. 可控:新增一个标签,只影响它自己的贡献,不扰动全局。
|
||||
3. 贴角色:敏捷型角色更容易吃满“突进/快剑/追击”类标签,智力/精神型角色更容易吃满“法修/法力/符阵”类标签。
|
||||
4. 可扩展:装备、Buff、套装仍然可以继续产出标签,但标签结算方式统一且稳定。
|
||||
|
||||
### 2.3 非目标
|
||||
|
||||
本期不做:
|
||||
|
||||
1. 不重做掉落、锻造、拆解循环。
|
||||
2. 不引入防御端 Build 抗性系统。
|
||||
3. 不重新设计角色基础四维属性。
|
||||
4. 不依赖运行时在线 embedding 服务。
|
||||
|
||||
## 3. 核心方案
|
||||
|
||||
## 3.1 基本思想
|
||||
|
||||
每个 Build 标签不再和其他标签比较,而是改为和以下 4 个角色属性做比较:
|
||||
|
||||
- `strength`
|
||||
- `agility`
|
||||
- `intelligence`
|
||||
- `spirit`
|
||||
|
||||
每个标签都会拥有一个“属性亲和度向量”,表示它分别贴近哪类属性。
|
||||
|
||||
示例:
|
||||
|
||||
- `快剑`:更偏 `agility`,次偏 `strength`
|
||||
- `重击`:更偏 `strength`
|
||||
- `法修`:更偏 `intelligence`
|
||||
- `护体`:偏 `spirit + strength`
|
||||
- `符阵`:偏 `intelligence + spirit`
|
||||
|
||||
角色自身也有属性分布。系统将角色属性归一化后,与标签属性亲和度做匹配,得到该标签在当前角色身上的“适配度”。
|
||||
|
||||
最终伤害加成不再来自“标签互相增幅”,而来自“每个标签在当前角色上的适配度贡献”。
|
||||
|
||||
## 3.2 新公式
|
||||
|
||||
### 角色属性权重
|
||||
|
||||
```ts
|
||||
roleAttributeWeight[attr] = character.attributes[attr] / totalAttributes
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
```ts
|
||||
totalAttributes =
|
||||
strength + agility + intelligence + spirit
|
||||
```
|
||||
|
||||
### 标签属性亲和度
|
||||
|
||||
每个标签维护一个四维向量:
|
||||
|
||||
```ts
|
||||
tagAttributeAffinity = {
|
||||
strength: 0~1,
|
||||
agility: 0~1,
|
||||
intelligence: 0~1,
|
||||
spirit: 0~1,
|
||||
}
|
||||
```
|
||||
|
||||
### 单标签适配度
|
||||
|
||||
```ts
|
||||
tagFitScore(tag, character) =
|
||||
sum(attr in [str, agi, int, spr])(
|
||||
roleAttributeWeight[attr] * tagAttributeAffinity[attr]
|
||||
)
|
||||
```
|
||||
|
||||
`tagFitScore` 结果区间固定在 `0 ~ 1`。
|
||||
|
||||
### 单标签加成倍数
|
||||
|
||||
```ts
|
||||
tagBonusMultiplier =
|
||||
1 + baseTagBonus * tagFitScore * sourceCoefficient
|
||||
```
|
||||
|
||||
建议首版参数:
|
||||
|
||||
- `baseTagBonus = 0.12`
|
||||
- `sourceCoefficient`
|
||||
- Buff 标签:`1.0`
|
||||
- 角色固有标签:`0.9`
|
||||
- 武器标签:`0.85`
|
||||
- 防具标签:`0.75`
|
||||
- 饰品标签:`0.8`
|
||||
- 套装合成标签:`0.9`
|
||||
|
||||
### 最终 Build 总倍率
|
||||
|
||||
为了避免标签过多时指数膨胀,首版采用“加法累计,再转倍率”的方式:
|
||||
|
||||
```ts
|
||||
buildBonus =
|
||||
clamp(sum(eachTagBonusDelta), 0, 0.6)
|
||||
|
||||
buildDamageMultiplier = 1 + buildBonus
|
||||
```
|
||||
|
||||
其中:
|
||||
|
||||
```ts
|
||||
eachTagBonusDelta = baseTagBonus * tagFitScore * sourceCoefficient
|
||||
```
|
||||
|
||||
这意味着:
|
||||
|
||||
- 标签越多,总收益越高。
|
||||
- 但每个标签只看自己和角色属性是否匹配。
|
||||
- 新增一个标签,不会反向修改旧标签贡献。
|
||||
|
||||
## 3.3 示例
|
||||
|
||||
角色属性:
|
||||
|
||||
- `strength = 8`
|
||||
- `agility = 10`
|
||||
- `intelligence = 4`
|
||||
- `spirit = 3`
|
||||
|
||||
归一化后:
|
||||
|
||||
- `strength = 0.32`
|
||||
- `agility = 0.40`
|
||||
- `intelligence = 0.16`
|
||||
- `spirit = 0.12`
|
||||
|
||||
标签亲和度假设:
|
||||
|
||||
```ts
|
||||
快剑: { strength: 0.35, agility: 1.0, intelligence: 0.1, spirit: 0.05 }
|
||||
突进: { strength: 0.45, agility: 0.95, intelligence: 0.0, spirit: 0.0 }
|
||||
法修: { strength: 0.0, agility: 0.1, intelligence: 1.0, spirit: 0.6 }
|
||||
```
|
||||
|
||||
则:
|
||||
|
||||
```ts
|
||||
快剑 fit = 0.32*0.35 + 0.40*1.0 + 0.16*0.1 + 0.12*0.05 = 0.534
|
||||
突进 fit = 0.32*0.45 + 0.40*0.95 = 0.524
|
||||
法修 fit = 0.40*0.1 + 0.16*1.0 + 0.12*0.6 = 0.272
|
||||
```
|
||||
|
||||
可以直观看到:
|
||||
|
||||
- 同样是标签,`快剑/突进` 对敏捷角色收益高。
|
||||
- `法修` 在这名角色身上的收益明显偏低。
|
||||
- 原因不再是“它和其他标签不合群”,而是“它和当前角色属性画像不匹配”。
|
||||
|
||||
## 4. 数据结构改造
|
||||
|
||||
## 4.1 `BuildTagDefinition` 扩展
|
||||
|
||||
当前 `src/types/build.ts` 中的 `BuildTagDefinition` 需要新增:
|
||||
|
||||
```ts
|
||||
attributeAffinity: {
|
||||
strength: number;
|
||||
agility: number;
|
||||
intelligence: number;
|
||||
spirit: number;
|
||||
};
|
||||
```
|
||||
|
||||
完整建议:
|
||||
|
||||
```ts
|
||||
interface BuildTagDefinition {
|
||||
id: string;
|
||||
label: string;
|
||||
category: BuildTagCategory;
|
||||
aliases: string[];
|
||||
description: string;
|
||||
attributeAffinity: {
|
||||
strength: number;
|
||||
agility: number;
|
||||
intelligence: number;
|
||||
spirit: number;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## 4.2 运行时明细结构
|
||||
|
||||
`src/data/buildDamage.ts` 需要将当前的 `BuildDamageBreakdown` 从“标签两两贡献表”改成“标签-属性贡献表”:
|
||||
|
||||
```ts
|
||||
type BuildDamageBreakdown = {
|
||||
tags: string[];
|
||||
buildDamageBonus: number;
|
||||
buildDamageMultiplier: number;
|
||||
rows: Array<{
|
||||
label: string;
|
||||
source: 'buff' | 'character' | 'equipment' | 'set' | 'monster';
|
||||
fitScore: number;
|
||||
sourceCoefficient: number;
|
||||
bonusDelta: number;
|
||||
attributeContributions: {
|
||||
strength: number;
|
||||
agility: number;
|
||||
intelligence: number;
|
||||
spirit: number;
|
||||
};
|
||||
}>;
|
||||
};
|
||||
```
|
||||
|
||||
这样 UI 或调试日志能直接回答:
|
||||
|
||||
- 这个标签吃的是哪条属性
|
||||
- 吃了多少
|
||||
- 为什么它比另一个标签强
|
||||
|
||||
## 4.3 相似度来源
|
||||
|
||||
当前仓库已有 `src/data/buildTagSimilarity.generated.ts`,但新方案不再以“标签-标签相似度矩阵”为主数据源。
|
||||
|
||||
建议改为新增:
|
||||
|
||||
- `src/data/buildTagAttributeAffinity.ts`
|
||||
|
||||
用于存放标签到四维属性的静态向量。
|
||||
|
||||
首版推荐手工维护,原因:
|
||||
|
||||
1. 标签总量不大,人工校准更稳定。
|
||||
2. 当前目标是产品可控性,不是自动发现语义簇。
|
||||
3. 四维属性向量远比标签两两矩阵更容易审表和平衡。
|
||||
|
||||
后续如果要半自动化,可再增加脚本从标签描述中生成建议值,但运行时仍只读取本地静态表。
|
||||
|
||||
## 5. 标签来源规则
|
||||
|
||||
标签来源不变,计算方式变化。
|
||||
|
||||
### 保留来源
|
||||
|
||||
1. 角色固有 `combatTags`
|
||||
2. 装备 `buildProfile.tags`
|
||||
3. 套装标签
|
||||
4. Buff 标签
|
||||
|
||||
### 新规则
|
||||
|
||||
1. 所有标签统一做规范化和去重。
|
||||
2. 每个标签独立计算与角色属性的适配度。
|
||||
3. 不再计算标签与标签之间的 pair / product / cluster。
|
||||
4. 套装标签本质上仍是标签,只是 `sourceCoefficient` 更高。
|
||||
|
||||
## 6. 伤害接入方式
|
||||
|
||||
当前 `calculateOutgoingDamage()` 的接法可以保留:
|
||||
|
||||
```ts
|
||||
finalDamage =
|
||||
round(
|
||||
baseDamage
|
||||
* functionMultiplier
|
||||
* equipmentMultiplier
|
||||
* buildDamageMultiplier
|
||||
)
|
||||
```
|
||||
|
||||
本次只替换 `buildDamageMultiplier` 的来源,不改整体伤害主链路。
|
||||
|
||||
## 7. 与旧系统的关键差异
|
||||
|
||||
| 维度 | 旧系统 | 新系统 |
|
||||
| --- | --- | --- |
|
||||
| 核心逻辑 | 标签之间互相影响 | 标签分别匹配角色属性 |
|
||||
| 新增标签的影响范围 | 会扰动整个标签集合 | 只影响自身贡献 |
|
||||
| 可解释性 | 低 | 高 |
|
||||
| 平衡成本 | 高 | 低 |
|
||||
| 角色属性参与感 | 弱 | 强 |
|
||||
| 套装/装备/Buff 接入 | 已支持 | 继续支持 |
|
||||
|
||||
## 8. 实施方案
|
||||
|
||||
### 阶段 A:数据层
|
||||
|
||||
1. 在 `src/types/build.ts` 扩展 `BuildTagDefinition.attributeAffinity`
|
||||
2. 在 `src/data/buildTags.ts` 为所有规范标签补齐四维亲和度
|
||||
3. 新增 `src/data/buildTagAttributeAffinity.ts` 或直接内联到标签注册表
|
||||
|
||||
### 阶段 B:规则层
|
||||
|
||||
1. 重写 `src/data/buildDamage.ts`
|
||||
2. 删除或下线标签两两 `contributionRows` 计算
|
||||
3. 新增 `tagFitScore`、`bonusDelta`、`attributeContributions` 计算
|
||||
|
||||
### 阶段 C:展示层
|
||||
|
||||
1. 调整 Build 面板展示文案
|
||||
2. 从“标签协同”改成“标签适配度”
|
||||
3. 为每个标签展示主属性来源,例如:
|
||||
- `快剑:敏捷主导,少量力量修正`
|
||||
- `法修:智力主导,精神辅助`
|
||||
|
||||
### 阶段 D:平衡层
|
||||
|
||||
1. 补一份全标签四维向量审表
|
||||
2. 选 5 个预设角色跑样例验证
|
||||
3. 校准 `baseTagBonus` 与 `sourceCoefficient`
|
||||
|
||||
## 9. 验收标准
|
||||
|
||||
### 功能验收
|
||||
|
||||
1. 任意标签的贡献都可以拆解到四个属性。
|
||||
2. 删除一个标签时,只减少它自己的收益,不应重算其他标签的贡献值。
|
||||
3. 同一套装备给不同属性角色使用时,Build 倍率应体现明显差异。
|
||||
4. 套装标签、Buff 标签仍能正常进入最终 Build 倍率。
|
||||
|
||||
### 数值验收
|
||||
|
||||
1. 单标签弱匹配时收益接近 0。
|
||||
2. 单标签强匹配时收益稳定且可预期。
|
||||
3. 3 到 6 个高匹配标签可形成清晰 build 成型感。
|
||||
4. 8 标签上限下,总 Build 加成不超过设计封顶值。
|
||||
|
||||
### 体验验收
|
||||
|
||||
1. 玩家能理解“为什么这个标签适合我当前角色”。
|
||||
2. 策划能直接通过改四维向量调数,不需要反复查标签图谱。
|
||||
3. 调试日志能一眼看出收益来源,而不是只能看到复杂的 pair 乘积。
|
||||
|
||||
## 10. 风险与对策
|
||||
|
||||
### 风险 1:标签语义被压平
|
||||
|
||||
问题:
|
||||
去掉标签-标签协同后,Build 可能变得过于线性。
|
||||
|
||||
对策:
|
||||
|
||||
1. 保留套装标签和 Buff 标签作为高价值来源。
|
||||
2. 用 `sourceCoefficient` 区分来源权重。
|
||||
3. 后续如需要,可增加“少量同流派奖励”,但必须是弱规则,不能回到全图互相影响。
|
||||
|
||||
### 风险 2:四维向量定义主观
|
||||
|
||||
问题:
|
||||
不同策划对“快剑更像敏捷还是力量”可能判断不同。
|
||||
|
||||
对策:
|
||||
|
||||
1. 首版先建立审表规范。
|
||||
2. 每个标签必须附一句属性说明。
|
||||
3. 先让预设角色覆盖主要 archetype,再扩充长尾标签。
|
||||
|
||||
### 风险 3:旧数据迁移成本
|
||||
|
||||
问题:
|
||||
现有 `buildTagSimilarity.generated.ts` 将弱化甚至失去主要用途。
|
||||
|
||||
对策:
|
||||
|
||||
1. 本期不强制删除旧文件。
|
||||
2. 新逻辑只依赖新 affinity 表。
|
||||
3. 等新系统稳定后,再清理旧相似度矩阵和旧展示逻辑。
|
||||
|
||||
## 11. 一句话结论
|
||||
|
||||
本次 Build 系统重构的核心,不是再优化“标签之间怎么互相放大”,而是把判断标准改成“这个标签和当前玩家角色的属性画像有多匹配”,从而让 Build 倍率从复杂的标签网络效应,变成可解释、可调优、可控的单标签属性适配模型。
|
||||
486
docs/prd/RUNTIME_ITEM_GENERATION_CURRENT_SYSTEM_DESIGN.md
Normal file
486
docs/prd/RUNTIME_ITEM_GENERATION_CURRENT_SYSTEM_DESIGN.md
Normal file
@@ -0,0 +1,486 @@
|
||||
# 当前运行时物品生成系统设计整理
|
||||
|
||||
更新时间:`2026-04-02`
|
||||
|
||||
## 0. 目标
|
||||
|
||||
这份文档只回答一个问题:
|
||||
|
||||
**当前仓库里,运行时物品是怎么被“定义、生成、发放、使用、转化、进入 build 结算”的。**
|
||||
|
||||
它不是未来方案,而是对现状的结构化整理,方便后续继续扩展 AI 原生玩法。
|
||||
|
||||
## 1. 当前系统的总体判断
|
||||
|
||||
当前仓库里的运行时物品系统,不是“纯 AI 生成”,而是一个 **本地规则驱动 + 局部程序化生成 + AI 负责叙事文本** 的体系。
|
||||
|
||||
它大体由 5 层组成:
|
||||
|
||||
1. **物品骨架层**
|
||||
- 用 `InventoryItem`、`ItemStatProfile`、`ItemUseProfile`、`ItemBuildProfile` 承载运行时能力。
|
||||
2. **基础生成层**
|
||||
- 用 `src/data/itemCatalog.ts` + `src/data/itemDesign.ts` 从素材路径推导出物品目录与设计元数据。
|
||||
3. **运行时发放层**
|
||||
- 在初始背包、NPC 库存、宝藏奖励、怪物掉落、拆解/锻造/重铸、自定义世界运行时生成等入口,把物品真正放进 `GameState`。
|
||||
4. **战斗/背包生效层**
|
||||
- 装备通过 `equipmentEffects.ts` 生效,消耗品通过 `inventoryEffects.ts` 生效,build 标签通过 `buildTags.ts` + `buildDamage.ts` 生效。
|
||||
5. **叙事包装层**
|
||||
- AI 负责把这些本地规则已经确定的结果写成剧情、聊天、宝藏描述、选项文案,但 **AI 当前并不直接产出结构化运行时物品**。
|
||||
|
||||
一句话概括:
|
||||
|
||||
**当前系统已经有“运行时物品玩法骨架”,但还没有“与具体背景 / 当前 NPC / 当前事件强绑定的 AI 原生物品导演层”。**
|
||||
|
||||
## 2. 当前核心数据结构
|
||||
|
||||
### 2.1 `InventoryItem`
|
||||
|
||||
运行时所有物品最终都落到 `InventoryItem`:
|
||||
|
||||
- 基础身份:`id`、`category`、`name`、`quantity`、`rarity`
|
||||
- 通用标签:`tags`
|
||||
- 世界/表现:`iconSrc`、`description`、`worldAffinity`、`worldProfiles`
|
||||
- 装备信息:`equipmentSlotId`
|
||||
- 数值能力:`statProfile`
|
||||
- 使用能力:`useProfile`
|
||||
- build 能力:`buildProfile`
|
||||
- 经济能力:`value`
|
||||
|
||||
这意味着当前系统已经允许同一个物品同时具备:
|
||||
|
||||
- 装备加成
|
||||
- 消耗品效果
|
||||
- build 标签
|
||||
- 世界观文案
|
||||
- 经济价值
|
||||
|
||||
### 2.2 `ItemStatProfile`
|
||||
|
||||
当前可直接进入数值结算的字段主要是:
|
||||
|
||||
- `maxHpBonus`
|
||||
- `maxManaBonus`
|
||||
- `outgoingDamageBonus`
|
||||
- `incomingDamageMultiplier`
|
||||
|
||||
这套结构已经足够支撑“少量直接数值提升”的物品设计。
|
||||
|
||||
### 2.3 `ItemUseProfile`
|
||||
|
||||
当前消耗品/可使用物品可提供:
|
||||
|
||||
- `hpRestore`
|
||||
- `manaRestore`
|
||||
- `cooldownReduction`
|
||||
- `buildBuffs`
|
||||
|
||||
这意味着当前系统已经具备“限时 build 标签道具”的基础承载能力。
|
||||
|
||||
### 2.4 `ItemBuildProfile`
|
||||
|
||||
当前 build 相关物品数据包括:
|
||||
|
||||
- `role`
|
||||
- `tags`
|
||||
- `setId`
|
||||
- `setName`
|
||||
- `pieceName`
|
||||
- `synergy`
|
||||
- `craftTags`
|
||||
- `forgeRank`
|
||||
|
||||
这说明当前物品系统已经不仅仅是“数值装备”,而是已经向 **build / 套装 / 锻造** 方向扩展。
|
||||
|
||||
## 3. 当前物品是如何被生成出来的
|
||||
|
||||
## 3.1 素材目录 -> 物品目录
|
||||
|
||||
`src/data/itemCatalog.ts` 负责把素材路径转成 `ItemCatalogEntry`:
|
||||
|
||||
- 先从素材路径推导:
|
||||
- `category`
|
||||
- `rarity`
|
||||
- `tags`
|
||||
- `name`
|
||||
- 再调用 `src/data/itemDesign.ts` 补充更完整的设计:
|
||||
- `statProfile`
|
||||
- `useProfile`
|
||||
- `buildProfile`
|
||||
- `value`
|
||||
- 世界观名称/描述
|
||||
|
||||
也就是说,当前项目里相当一部分物品,不是手工逐个写死的,而是 **“素材驱动 + 规则推导”**。
|
||||
|
||||
## 3.2 `itemDesign.ts` 的设计角色
|
||||
|
||||
`src/data/itemDesign.ts` 是当前最重要的“物品规则设计器”。
|
||||
|
||||
它会按素材族系推导出不同风格物品:
|
||||
|
||||
- `Armory` 系列
|
||||
- 生成武器/护甲
|
||||
- 自动带 `buildProfile`
|
||||
- 自动挂 `setId / setName / pieceName / synergy`
|
||||
- `Jewelry` 系列
|
||||
- 生成饰品
|
||||
- 偏向 `relic` 与 build 补位
|
||||
- `Potions` 系列
|
||||
- 生成消耗品
|
||||
- 可恢复 HP / Mana / 冷却
|
||||
- 部分药剂会生成 `buildBuffs`
|
||||
- `Gems` / `Skills` / `Librarium` 系列
|
||||
- 生成稀有品、法器、技能相关物品
|
||||
- 部分带 build 倾向
|
||||
|
||||
这层已经是“半程序化设计”,不是纯静态表。
|
||||
|
||||
## 3.3 自定义世界运行时程序化生成
|
||||
|
||||
`src/data/customWorldRuntime.ts` 提供了另一条明显不同的生成链:
|
||||
|
||||
- 输入:
|
||||
- `CustomWorldProfile`
|
||||
- `seedKey`
|
||||
- 查询条件:`categories / preferredTags / keywords / rarityFloor / count`
|
||||
- 输出:
|
||||
- 运行时即时程序化物品
|
||||
|
||||
它的特点是:
|
||||
|
||||
- 基于世界档案与种子稳定生成
|
||||
- 可以按角色、用途、标签、关键词筛选
|
||||
- 能生成:
|
||||
- 武器
|
||||
- 护甲
|
||||
- 饰品
|
||||
- 消耗品
|
||||
- 材料
|
||||
- 稀有品
|
||||
- 专属物品
|
||||
|
||||
当前它已经被用于:
|
||||
|
||||
- 自定义世界角色初始装备
|
||||
- 自定义世界角色初始背包
|
||||
- 自定义世界 NPC 角色库存
|
||||
|
||||
但这条链目前更像 **“主题化程序物品池”**,还不是“事件级、NPC级、场景级实时定制物品”。
|
||||
|
||||
## 4. 当前运行时有哪些物品发放入口
|
||||
|
||||
## 4.1 玩家初始物品
|
||||
|
||||
玩家开局物品主要来自两条链:
|
||||
|
||||
1. `buildInitialPlayerInventory`
|
||||
- 来自 `npcInteractions.ts`
|
||||
- 普通世界走角色预设背包
|
||||
- 自定义世界走 `buildCustomWorldStarterInventoryItems`
|
||||
2. `buildInitialEquipmentLoadout`
|
||||
- 来自 `equipmentEffects.ts`
|
||||
- 根据角色预设初始装备,转成可装备的 `InventoryItem`
|
||||
|
||||
这说明玩家初始 build 其实已经部分依赖物品体系,而不是只靠角色裸属性。
|
||||
|
||||
## 4.2 NPC 库存
|
||||
|
||||
`buildInitialNpcState` 会给每个 NPC 建立运行时库存:
|
||||
|
||||
- 角色型 NPC:
|
||||
- 从角色装备 + 角色背包推导
|
||||
- 怪物型 encounter:
|
||||
- 从怪物 preset 的 `lootTable` 推导
|
||||
- 普通场景 NPC:
|
||||
- 从职业/身份模板库存推导
|
||||
- 自定义世界 NPC:
|
||||
- 可走 `buildRuntimeCustomWorldInventoryItems`
|
||||
|
||||
因此当前 NPC 交易、赠礼、击败掉落,已经都能围绕同一套 `InventoryItem[]` 进行。
|
||||
|
||||
## 4.3 宝藏奖励
|
||||
|
||||
`src/data/treasureInteractions.ts` 负责宝藏奖励:
|
||||
|
||||
- 根据世界类型选择奖励池
|
||||
- 根据 encounter 信息和 action 做 seed
|
||||
- 产出:
|
||||
- 稀有品
|
||||
- 消耗品
|
||||
- 材料
|
||||
- HP / Mana 恢复
|
||||
- 货币
|
||||
|
||||
这条链已经是运行时生成,但目前仍然是 **世界模板池级别**,不是“根据当前场景背景 / 当前 NPC / 最近事件”深度定制。
|
||||
|
||||
## 4.4 怪物掉落 / NPC 击败掉落
|
||||
|
||||
当前掉落主要有两种:
|
||||
|
||||
1. 怪物 preset 自带 `lootTable`
|
||||
2. NPC 击败后,从 `npcState.inventory` 里按玩家收益排序取高价值物品
|
||||
|
||||
这里的核心是:
|
||||
|
||||
**当前系统已经把“掉落”统一成库存消费问题,而不是单独的一套特殊奖励系统。**
|
||||
|
||||
## 4.5 拆解 / 合成 / 锻造 / 重铸
|
||||
|
||||
`src/data/forgeSystem.ts` 已经形成第二类运行时物品生成入口:
|
||||
|
||||
- `executeDismantleItem`
|
||||
- 把装备拆成基础材料 + 标签精粹
|
||||
- `executeForgeRecipe`
|
||||
- 消耗材料生成成品装备
|
||||
- `executeReforgeItem`
|
||||
- 保留原物品基础上重投 build 标签并增强数值
|
||||
|
||||
这说明运行时物品系统已经不只是“掉落 -> 使用”,而是进入了:
|
||||
|
||||
**掉落 -> 持有 -> 拆解 -> 再生产 -> build 迭代**
|
||||
|
||||
的循环。
|
||||
|
||||
## 5. 当前物品如何进入玩法结算
|
||||
|
||||
## 5.1 装备生效
|
||||
|
||||
`src/data/equipmentEffects.ts` 会把 `EquipmentLoadout` 汇总为:
|
||||
|
||||
- `maxHpBonus`
|
||||
- `maxManaBonus`
|
||||
- `outgoingDamageMultiplier`
|
||||
- `incomingDamageMultiplier`
|
||||
|
||||
当前实现已经会优先读取 `statProfile`,没有时才走 rarity fallback。
|
||||
|
||||
这意味着:
|
||||
|
||||
- 装备的直接数值提升已经真实生效
|
||||
- 不是只在 UI 显示
|
||||
|
||||
## 5.2 build 标签生效
|
||||
|
||||
`src/data/buildTags.ts` + `src/data/buildDamage.ts` 已经形成完整 build 入口:
|
||||
|
||||
- 角色 `combatTags`
|
||||
- 装备 `buildProfile.tags`
|
||||
- 套装标签
|
||||
- 限时 `TimedBuildBuff`
|
||||
|
||||
会被统一整理为激活标签集,再计算 build 伤害乘区。
|
||||
|
||||
当前 build 标签来源已经包括:
|
||||
|
||||
- 角色
|
||||
- 怪物
|
||||
- 装备
|
||||
- 套装
|
||||
- 消耗品/技能 buff
|
||||
|
||||
## 5.3 消耗品生效
|
||||
|
||||
`src/data/inventoryEffects.ts` + `src/hooks/useInventoryFlow.ts` 会在使用物品时:
|
||||
|
||||
- 恢复 HP
|
||||
- 恢复 Mana
|
||||
- 推进技能冷却
|
||||
- 追加 `activeBuildBuffs`
|
||||
|
||||
这意味着当前系统已经存在:
|
||||
|
||||
- **直接恢复型物品**
|
||||
- **节奏调整型物品**
|
||||
- **限时 build 强化型物品**
|
||||
|
||||
三种雏形。
|
||||
|
||||
## 6. 当前 AI 在物品系统里的角色
|
||||
|
||||
当前 AI 与物品系统的关系主要有两种:
|
||||
|
||||
## 6.1 自定义世界“离线式”生成
|
||||
|
||||
`src/services/customWorld.ts` + `src/services/customWorldBuilder.ts` 会让 AI 生成:
|
||||
|
||||
- 世界名
|
||||
- playable NPC
|
||||
- story NPC
|
||||
- 关键物品样本
|
||||
- 地标
|
||||
|
||||
然后本地再把这些骨架扩展成更大的世界档案。
|
||||
|
||||
这时 AI 的角色是:
|
||||
|
||||
**提供世界观语义骨架,而不是直接在单次 encounter 里临场造出一个可结算物品。**
|
||||
|
||||
## 6.2 运行时叙事包装
|
||||
|
||||
`prompt.ts` 和相关 story flow 里,AI 主要负责:
|
||||
|
||||
- 剧情文本
|
||||
- 对话
|
||||
- 选项文案
|
||||
- 氛围描述
|
||||
|
||||
但当前 AI 不负责:
|
||||
|
||||
- 直接返回结构化物品
|
||||
- 决定物品数值是否合法
|
||||
- 决定 build 标签是否合法
|
||||
- 决定库存如何变更
|
||||
|
||||
这与仓库已有开发经验文档里的原则一致:
|
||||
|
||||
**AI 负责解释世界,本地规则负责改动世界。**
|
||||
|
||||
## 7. 当前系统的优点
|
||||
|
||||
## 7.1 已有统一物品骨架
|
||||
|
||||
`InventoryItem` 已经足够承载:
|
||||
|
||||
- 永久装备
|
||||
- 消耗品
|
||||
- build 标签
|
||||
- 世界观描述
|
||||
- 经济系统
|
||||
|
||||
后续不需要另起一套物品结构。
|
||||
|
||||
## 7.2 已有 build 玩法骨架
|
||||
|
||||
当前系统已经支持:
|
||||
|
||||
- 装备 build 标签
|
||||
- 套装 build 标签
|
||||
- 消耗品限时 build buff
|
||||
- 角色/怪物 combatTags
|
||||
|
||||
这让“物品影响 build”不再只是概念。
|
||||
|
||||
## 7.3 已有多个运行时入口
|
||||
|
||||
当前物品已经能从这些入口进入游戏:
|
||||
|
||||
- 初始背包
|
||||
- 初始装备
|
||||
- NPC 库存
|
||||
- 宝藏
|
||||
- 怪物掉落
|
||||
- 击败 NPC
|
||||
- 合成
|
||||
- 拆解
|
||||
- 锻造
|
||||
- 重铸
|
||||
- 自定义世界程序化生成
|
||||
|
||||
系统入口已经比较完整。
|
||||
|
||||
## 7.4 本地规则边界明确
|
||||
|
||||
当前实现最大的稳定性来源,是物品最终由本地规则落库和结算:
|
||||
|
||||
- 可测试
|
||||
- 可复现
|
||||
- 可存档兼容
|
||||
- 不依赖模型临场稳定性
|
||||
|
||||
这非常适合继续向 AI 原生玩法演化。
|
||||
|
||||
## 8. 当前系统的主要缺口
|
||||
|
||||
## 8.1 非自定义世界的运行时发放仍偏模板池
|
||||
|
||||
当前普通世界的宝藏、NPC 库存、基础奖励,更多还是:
|
||||
|
||||
- 世界模板
|
||||
- 身份模板
|
||||
- 怪物 preset
|
||||
|
||||
而不是根据以下上下文动态拼装:
|
||||
|
||||
- 当前场景背景
|
||||
- 当前 NPC 动机
|
||||
- 当前事件阶段
|
||||
- 最近剧情行为
|
||||
- 玩家当前 build 缺口
|
||||
|
||||
## 8.2 宝藏与物品的叙事绑定不够深
|
||||
|
||||
当前宝藏奖励会根据世界类型变化,但还没有强绑定:
|
||||
|
||||
- 地标
|
||||
- scene preset
|
||||
- treasure hint
|
||||
- 相关 NPC
|
||||
- 最近发生的事件
|
||||
|
||||
所以“玩法可用”,但“故事贴脸感”还不够强。
|
||||
|
||||
## 8.3 自定义世界运行时物品偏“主题生成”,还不是“关系生成”
|
||||
|
||||
`customWorldRuntime.ts` 已经能做:
|
||||
|
||||
- 世界主题相关
|
||||
- 标签/关键词相关
|
||||
- 角色用途相关
|
||||
|
||||
但还没有直接把以下关系结构编进物品:
|
||||
|
||||
- 这个物品和哪个 NPC 有关系
|
||||
- 为什么在这个时刻出现
|
||||
- 它与哪个任务/线索绑定
|
||||
- 它是否属于某个 faction / 场景 / 地标
|
||||
|
||||
## 8.4 一些运行时奖励没有补足完整 build 元数据
|
||||
|
||||
例如部分宝藏奖励和 NPC 模板奖励,当前只是简单 `InventoryItem`:
|
||||
|
||||
- 有 `category / rarity / tags`
|
||||
- 但未必有完整 `statProfile / useProfile / buildProfile`
|
||||
|
||||
这会导致它们更像资源或商品,而不是强 build 物品。
|
||||
|
||||
## 8.5 缺少“物品生成导演层”
|
||||
|
||||
当前系统缺的不是单个函数,而是一层明确的运行时物品导演:
|
||||
|
||||
- 输入当前上下文
|
||||
- 决定掉落/奖励/商店/赠礼候选
|
||||
- 决定叙事来源
|
||||
- 决定 build 倾向
|
||||
- 决定是永久收益还是限时收益
|
||||
- 决定数值预算
|
||||
|
||||
这层目前分散在:
|
||||
|
||||
- `treasureInteractions.ts`
|
||||
- `npcInteractions.ts`
|
||||
- `customWorldRuntime.ts`
|
||||
- `forgeSystem.ts`
|
||||
|
||||
之间,还没有统一起来。
|
||||
|
||||
## 9. 对当前系统的结论
|
||||
|
||||
当前仓库里的“运行时物品生成系统”已经不是空白,反而已经具备了很好的扩展前提:
|
||||
|
||||
- 有统一物品数据结构
|
||||
- 有 build 标签体系
|
||||
- 有限时 buff 体系
|
||||
- 有装备/消耗品/材料/稀有品的分类
|
||||
- 有宝藏、NPC、掉落、锻造等运行时入口
|
||||
- 有自定义世界程序化物品能力
|
||||
- 有明确的“AI 叙事、本地规则结算”边界
|
||||
|
||||
但它现在更像:
|
||||
|
||||
**“规则系统已经就位,AI 原生上下文驱动物品导演还没补上。”**
|
||||
|
||||
后续如果要继续往 AI 原生游戏推进,最值得补的不是推翻现有系统,而是:
|
||||
|
||||
1. 在现有 `InventoryItem` 体系上增加上下文生成层。
|
||||
2. 让物品生成同时读取场景、NPC、剧情、build 缺口。
|
||||
3. 让 AI 负责物品语义与叙事来源,本地规则负责编译成可结算物品。
|
||||
4. 把宝藏、NPC 交易、任务奖励、掉落统一接入同一套“运行时物品导演”。
|
||||
Reference in New Issue
Block a user