初始仓库迁移
Some checks failed
CI / verify (push) Has been cancelled

This commit is contained in:
2026-04-04 23:57:06 +08:00
parent 80986b790d
commit c49c64896a
18446 changed files with 532435 additions and 2 deletions

View 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
View 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`。*

File diff suppressed because it is too large Load Diff

View 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、能进锻造、能进存档、还能被剧情解释的正式物品。**

View 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`
## 备注
- 当前文档的目标是“先把位置收拢清楚”,不是直接修复乱码。
- 如果你下一步要我继续,我可以基于这份清单继续做两件事之一:
- 逐文件修复中文乱码
- 先做一个“乱码修复优先级 + 替换建议”文档

View 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. 一句话总结
这个项目最重要的经验不是“多写了多少功能”,而是:
**凡是会同时影响叙事、状态、演出和工具链的需求,都要先统一边界,再落实现。**

View 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”而是
**编辑器一旦想可靠,就不能只编辑静态数据,必须逐步接管真实资源定义、真实运行时状态和真实播放逻辑。**

View File

@@ -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. 一句话收束
这次需求的关键不是“给开局补一段特判对白”,而是把:
- 首遇节奏
- 背景披露节奏
- 私聊入口
- 关系记忆
统一到同一套“好感度 + 首遇状态 + 信息解锁”的通用规则里。
只有这样,玩家遇见每一个角色时才都会有对应关系位置上的初识感,角色背景才会像一步步了解出来的,同伴私聊也才会真的有价值。

View 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 和后续内容扩展才会真正开始越做越顺。**

View 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 ...` 全部收口到统一词典。

View 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 单独收口,避免继续堆进总类型文件
## P2AI 客户端层过厚,且重复了多套请求与解析逻辑
证据:
- `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 流程,工程成本都会持续上升。

View 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` 继续向“纯战斗结算”和“播放适配层”分离
- 先稳定公开接口,再做内部拆分,避免一次性大改
## P1AI 服务迁移只完成了一半,`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
## P2TypeScript 安全基线仍然偏宽松
### 现状
当前类型拆分方向是好的,`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 门禁变硬、再顺手压缩运行时大模块。只要这一步补上,后续加剧情、加编辑器能力、加自定义世界都会轻很多。

View 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 体积
## 一句话结论
这个仓库已经从“功能堆叠期”进入“工程收尾期”了。当前最值得做的不是再加一层玩法,而是把门禁补齐、把超级模块拆开、把半迁移状态收尾;只要这一步做稳,后续继续扩展剧情、编辑器和自定义世界的成本都会明显下降。

View 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 相似度”的方式形成构筑强度,并把这份构筑强度直接接进当前伤害输出,同时让怪物掉落、宝藏、交易、拆解、合成、锻造、铭刻围绕同一套标签体系形成闭环。

View 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 越多,这类问题会越难排查。

View 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 时,建议先补独立脚本,再把运行时调用接进来,最后同步这份目录文档。

View 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` 之类术语的显示策略。

View 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` 增加显示层映射,避免原始英文值继续直接透到编辑器和运行时界面。

View File

@@ -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`,最后统一共享请求层兜底文案。

View 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`

View 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。

View 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/` 中很多英文值本身可能是内部枚举,但只要当前编辑器 / 预览没有做中文映射,就仍应视为“会暴露到用户侧”的文本问题。

View 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`

View 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 的移动端优化,本质不是把元素缩小,而是重组视觉重心、固定操作锚点、让焦点内容在一屏内自然成立。**

View 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`

View 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 规则、选择流程和编辑器体系同时存在的情况下,始终让每条链路各归其位。

View 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`

View 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 连续几轮聊天时,信息释放节奏是连续的,不会忽冷忽热

View 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. 把预览、重新生成和发布做完整
这样做的价值在于:
- 范围可控
- 路径清晰
- 能真正进入当前仓库
- 后续可以在此基础上再加技能动作、剧情演出和多供应商增强路线

View 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 原生游戏真正该有的任务结构**

View 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 里需要的**
- **像系统里可控的**

File diff suppressed because it is too large Load Diff

View 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 倍率从复杂的标签网络效应,变成可解释、可调优、可控的单标签属性适配模型。

View 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 交易、任务奖励、掉落统一接入同一套“运行时物品导演”。