简体中文
物品装配系统
装配系统把多个模块写进同一件物品的内容, 合并成你最终看到的名称和 Lore
简单说, 装配系统就是把各模块写进物品的状态, 统一合并成最终展示. 你的装备能一路成长, 靠的就是它
为什么需要装配系统
一件 RPG 装备上, 往往同时带着好几个模块写进去的内容:
- EmakiItem 写入的基础名称, 基础 Lore, 套装, 技能
- EmakiForge 写入的品质, 锻造材料, 额外属性
- EmakiStrengthen 写入的星级, 强化加成, 失败标记
- EmakiGem 写入的插槽, 宝石, 宝石等级
- EmakiAttribute 写入或读取的属性 payload
如果每个模块都直接改最终的 Lore, 就会互相覆盖, 出现重复, 顺序混乱, 还会留下残留. 装配系统的做法是: 各模块往自己那一层 (layer) 写结构化数据, CoreLib 再统一渲染出最终展示
核心概念
| 概念 | 说明 |
|---|---|
| namespace layer | 每个模块在物品上占一层数据, 这一层里装着该模块写进去的所有状态 |
| order | 层的排序权重, 决定最终 Lore 里各模块的展示谁先谁后 |
| snapshot | 某一层数据的快照, 用来编解码和保存 |
| contribution | 这一层对最终展示的贡献, 包括 Lore 区块, 名称修饰和属性统计 |
| renderer | 渲染器, 负责把所有层的贡献合并成最终的 ItemMeta |
namespace layer
| Namespace | Order | 说明 |
|---|---|---|
forge | 100 | 锻造层, 装着品质, 材料贡献和锻造配方信息 |
strengthen | 200 | 强化层, 装着星级, 锻印, 保护状态和强化属性 |
gem | 300 | 宝石层, 装着插槽, 宝石, 等级和宝石属性 |
cooking | 10000 | 烹饪的展示层, 或用于特殊展示 |
order 数字越小越靠前, 你可以用它控制最终 Lore 里各模块谁先谁后
装配流程
text
1. 业务模块完成操作 (如强化成功, 宝石镶嵌)
↓
2. 模块把真实状态写入 PDC (layer snapshot)
↓
3. 模块通过 ItemOperationLedger 执行 name_actions/lore_actions
↓
4. Ledger 记录操作到 PDC (emaki:item_operations), 支持后续精确回退
↓
5. 返回更新后的 ItemStack需要撤销操作时 (如宝石提取, 强化降级), 走的是这样三步:
text
1. 模块调用 Ledger.revert (operationId) 或 Ledger.revertAll (sourceNamespace)
↓
2. Reverter 根据账本记录精确撤销对应的名称/Lore 修改
↓
3. 不影响其他模块的操作结果关键服务
| 服务 | 说明 |
|---|---|
EmakiItemAssemblyService | 物品装配的入口, 接收重建请求并协调各个 layer |
ItemOperationLedger | 统一的名称/Lore 操作账本, 支持精确回退 |
ItemOperationExecutor | 执行 name_actions/lore_actions, 并把变更记下来 |
ItemOperationReverter | 按账本记录精确回退操作 |
EmakiNamespaceRegistry | 负责 namespace 的注册和顺序管理, 记着所有已注册 layer 的 order |
EmakiItemLayerCodecRegistry | 负责 layer 数据的编码, 解码和兼容, 处理版本迁移 |
Lore 操作账本 (ItemOperationLedger)
模块每执行一次 name_actions 或 lore_actions, Ledger 都会生成一条带 operationId 的记录, 存进物品的 PDC (物品自带的附加数据存储) (emaki:item_operations)
每条记录包含:
| 字段 | 说明 |
|---|---|
operationId | 这次操作的唯一标识 |
sourceNamespace | 这条操作来自哪个模块 (如 gem, strengthen, forge) |
timestamp | 操作发生的时间 |
nameRecords | 名称的修改记录 (用来回退) |
loreRecords | Lore 的修改记录 (用来回退) |
核心 API:
| 方法 | 说明 |
|---|---|
apply(itemStack, operationId, sourceNamespace, nameActions, loreActions, variables) | 执行操作, 并记进 PDC |
revert(itemStack, operationId) | 精确撤销指定的这次操作 |
revertAll(itemStack, sourceNamespace) | 清空某个命名空间下的所有操作 |
名称修饰 (Name Contribution)
除了 Lore, layer 还可以给物品名称加修饰. 例如:
- 强化层在名称前添加
+10 - 锻造层在名称后添加品质标签
[史诗] - 宝石层不修改名称
名称修饰可以指定加在名称的哪个位置:
| 位置 | 说明 |
|---|---|
PREFIX | 加在名称前面 |
POSTFIX | 加在名称后面 |
多个 layer 的名称修饰会按 order 排序, 再依次拼起来
NOTE
位置枚举值是 PREFIX 和 POSTFIX, 不是 SUFFIX
配置者能做什么
你不会直接操作装配服务, 但你的配置会直接影响最终展示:
调整 Lore 模板
每个模块的 Lore 模板就写在它自己的配置里:
yaml
# EmakiStrengthen 配置示例
lore:
template:
- '<gray>强化等级:<yellow>+%star%'
- '<gray>强化加成:<green>+%bonus_attack% 攻击'调整属性展示格式
属性怎么显示, 定义在 EmakiAttribute 的 lore_formats/ 里:
yaml
# lore_formats/default_flat.yml
format: '<gray>%display_name%:<white>+%value%'控制展示开关
有些模块还能配置要不要显示某些状态:
yaml
display:
show_quality: true
show_material_info: false
show_star_level: true
show_gem_slots: true使用模块命令刷新装备
配置改完后, 已有装备的展示不会自动更新, 用模块提供的刷新命令即可:
- 手动刷新单件装备
- 批量刷新在线玩家装备
- 玩家登录时自动刷新 (如果模块支持)
设计装备展示的建议
- 把"真实数值"放进 PDC / 属性 payload, 别只写 Lore. Lore 是给玩家看的, PDC 才是存数据的地方
- Lore 里只放玩家需要看到的信息. 内部调试数据不要摆进去
- 每个模块的 Lore 区块界限要清楚. 锻造区, 强化区, 宝石区各管各的, 不要交叉
- 别让两个模块写同一行一样的属性. 攻击力只在一个地方展示, 玩家才不会看花眼
- 升级旧装备前, 先在测试服抽样刷新. 确认新模板不会弄坏旧数据的展示
- 名称修饰保持简洁.
+10 烈焰剑 [史诗]比[+10][史诗][锻造][宝石x3] 烈焰剑好读得多
装配触发时机
下面这些操作都会触发物品重建:
| 操作 | 触发模块 |
|---|---|
| 强化成功/失败 | EmakiStrengthen |
| 锻造完成 | EmakiForge |
| 宝石镶嵌/提取/升级 | EmakiGem |
| 物品自动更新 | EmakiItem |
| 手动刷新命令 | 各模块 |
| 属性同步 | EmakiAttribute |
Lore 操作里 anchor 的匹配规则
用 lore_actions 里的 insert_below 或 insert_above 时要注意: anchor 留空, 和 anchor 没匹配到任何行, 结果并不一样:
| 操作 | anchor 为空 | anchor 未匹配到任何行 |
|---|---|---|
insert_below | 插入到 Lore 末尾 | 插入到 Lore 末尾 |
insert_above | 插入到 Lore 开头 | 插入到 Lore 末尾 |
anchor 用的是包含匹配 (contains), 命中第一行就停; insert_below 插在该行之后, insert_above 插在该行之前
Namespace ID 规范化
注册 namespace 时, ID 会自动按下面的规则整理:
- 去除首尾空格
- 转为小写
- 空格替换为下划线
- 空字符串变为
unknown