📄 文书档案 · 装备系统设计经验

装备系统设计规则(今日经验沉淀)

今天一整天的调试,最终让我们看清了一个核心结论:定义域与值域必须彻底分离。 以下是把今天所有混乱和修复整理成的一套规则,可以作为新项目的“装备系统设计文档”直接使用。

一、定义域(静态数据)——永远不会变的东西

这些数据在游戏运行期间不会发生变化,它们只存在于 JSON 文件中,作为“模板”。

| 文件 | 内容 | 字段示例 |

|------|------|----------|

| `items.json` | 装备模板 | `id`, `name`, `type`, `stats`, `price`, `quality`(仅限模板默认品质) |

| `effects.json` | 效果模板 | `id`, `name`, `description`, `template`, `stacking`, `maxStack` |

| `loot.json` | 掉落策略 | `chance`, `range`, `quality`分布, `rareEffects`池, `jumpPoint`配置 |

关键原则

二、值域(运行时数据)——会变化的东西

这些数据在游戏运行期间会变化、会被生成、会被玩家操作,存储在 `gameState` 中。

| 数据结构 | 存储位置 | 字段示例 |

|----------|----------|----------|

| 装备实例 | `gameState.inventory[]` | `instanceId`, `templateId`, `quality`, `effects`, `bonusStats` |

| 装备槽 | `gameState.player.equipment` | `weapon: instanceId`, `armor: instanceId` |

| 玩家最终属性 | `gameState.player.finalStats` | `attackMin`, `attackMax`, `defensePhysical`, `defenseMagical`, `hp` |

关键原则

- 每件装备都是独立实例(`instanceId` 唯一),不合并、不覆盖

- 装备槽只存实例 ID,不存模板 ID,不存完整对象

- 最终属性由 模板 + 实例 合并计算得出,战斗系统只读取 `finalStats`

三、装备实例的生成规则

当一件装备被创建时(掉落、购买、奖励),系统执行以下流程:

1. 从定义域读取模板(items.json)
2. 从掉落策略读取品质分布,roll 出一个品质
3. 如果模板有固定品质,覆盖品质(如:布衣→普通,炼狱→特殊)
4. 如果品质为 rare,从 effects.json 中抽取 1~2 个效果,附加到实例
5. 如果品质为 special 或 rare,执行跳点判定(连续尝试,一次失败即停止)
6. 生成独立 instanceId,存入 inventory

跳点规则(今日已确认可用)

四、属性计算引擎

recalculateCombatStats()
    ├── 重置 finalStats 为 0
    ├── 遍历装备槽(weapon, armor, helmet, necklace, bracelet, ring)
    │   ├── 通过 instanceId 从 inventory 找到装备实例
    │   ├── 通过 templateId 从 items.json 找到装备模板
    │   ├── 基础属性 = 模板.stats
    │   ├── 跳点加成 = 实例.bonusStats
    │   ├── 效果 = 实例.effects(用于 combatStats.modifiers)
    │   └── 最终属性 = 基础属性 + 跳点加成
    ├── 写入 player.finalStats
    └── 战斗系统只读取 player.finalStats

关键原则

- 属性计算是纯函数,无副作用

- 计算结果缓存在 `finalStats` 中,不实时计算

- 装备变化时重新计算,不是每次战斗都计算

五、效果系统

效果是数据驱动的,不硬编码:

六、品质与颜色

| 品质 | 颜色 | 显示 | 效果 |

|------|------|------|------|

| normal | 灰色 | "普通" | 无跳点,无额外效果 |

| special | 蓝色 | "特殊" | 可触发跳点,不自动附加效果 |

| rare | 金色 | "稀有" | 可触发跳点,自动附加 1~2 个效果 |

关键原则

- 品质是运行时属性,不是定义域属性(但模板可以指定默认品质)

- 品质决定了颜色显示机制行为(是否跳点、是否附加效果)

七、今天踩过的坑(新项目要避开)

| 坑 | 后果 | 正确做法 |

|----|------|----------|

| 装备槽存 `itemId` 字符串 | 丢失实例信息,无法区分同名装备 | 存 `instanceId` |

| 背包中同名装备合并 | 属性丢失,品质丢失 | 每件装备独立实例,不合并 |

| 战斗函数直接读取 `items.json` | 跳点、品质、效果全部失效 | 战斗函数只读取 `player.finalStats` |

| 效果用 `if` 枚举 | 每个新效果都要改战斗代码 | 数据驱动 + `modifiers` 对象 |

| 属性计算散落在各处 | 逻辑不一致,难以追踪 | 统一在 `recalculateCombatStats()` 中完成 |

| 跳点作为临时补丁 | 代码混乱,难以维护 | 跳点是装备实例的属性,是定义域的一部分 |

八、新项目中的装备系统结构

data/definitions/
├── items.json        # 装备模板(不变)
├── effects.json      # 效果模板(不变)
├── loot.json         # 掉落策略(不变)
└── monsters.json     # 怪物模板(不变)

data/runtime/
├── inventory.json    # 装备实例列表(可变)
├── equipment.json    # 装备槽引用(可变)
└── player.json       # 最终属性 + 状态(可变)

计算层(纯函数):
└── recalculateCombatStats()  # 模板 + 实例 → 最终属性

战斗层(只读):
└── 只读取 player.finalStats

九、关于三个阶段的思考

你提到的三个阶段——单人 RPG、组队协作、组织运营——对装备系统的要求会逐级变化:

第一阶段(单人 RPG)

第二阶段(组队协作)

第三阶段(组织运营)

今天完善的装备系统,已经为第一阶段做好了准备。第二阶段和第三阶段,是在这个基础上做扩展,而不是推翻重来。

---

明天要讨论的问题

—— 人与AI协作记录 ——