[M10] 真实物品系统:500 基础物品、1000 词缀、TreasureClassEx 掉落树 #25

Closed
opened 2026-09-15 06:36:00 +00:00 by troytt · 2 comments
Owner

里程碑 M10 · 重要程度 ★★★★☆ · 风险 中 · 依赖:M9
完整路线图见 ROADMAP.md


背景

项 当前(demo-data.ts) 真实 D2
物品基类 3(剑 / 帽 / 药水) ~500
词缀 2 ~1000

更关键的是掉落模型:rollDrop 现在的实现完全不是 D2 的模型。D2 的掉落是 TreasureClassEx.txt 定义的递归树,每个节点按权重向下展开。

改动清单

  • [MODIFY] src/game/acts.ts —— 加载 Armor.txt、Weapons.txt、Misc.txt、MagicPrefix.txt、MagicSuffix.txt、UniqueItems.txt、SetItems.txt、TreasureClassEx.txt、ItemTypes.txt、Runes.txt、Gems.txt
  • [MODIFY] src/game/items.ts —— itemBaseFromRow / affixFromRow 列映射逐列核对真实表。新增:品质等级(普通/魔法/稀有/套装/暗金/符文之语)、插槽、镶嵌、耐久、需求(力量/敏捷/等级)
  • [NEW] src/game/treasure.ts —— TreasureClassEx.txt 掉落树
  • [NEW] src/game/runeword.ts —— 符文之语

注意

物品结构的任何变动都会影响 M13(.d2s 存档互通)——.d2s 的物品段是位对齐的变长编码,
字段一旦定错,往返比对就过不去。建议在本里程碑设计数据结构时就预留 .d2s 需要的全部字段
(GUID、品质、插槽数、已镶嵌物品、耐久、个性化名称等)。

验收

  • 打死怪掉出带词缀的稀有装备,属性生效
  • 新增 scripts/verify-items.ts:全量物品表列映射断言
  • TreasureClassEx 递归树无环、各节点概率和为 1
  • 符文之语可合成且属性正确
> **里程碑 M10** · 重要程度 ★★★★☆ · 风险 中 · 依赖:M9 > 完整路线图见 [`ROADMAP.md`](https://git.projectdiablo2.cn/troytt/diablo2-web/src/branch/main/ROADMAP.md) --- ## 背景 | 项 | 当前(`demo-data.ts`) | 真实 D2 | |---|---|---| | 物品基类 | **3**(剑 / 帽 / 药水) | ~500 | | 词缀 | **2** | ~1000 | 更关键的是掉落模型:`rollDrop` 现在的实现**完全不是** D2 的模型。D2 的掉落是 `TreasureClassEx.txt` 定义的**递归树**,每个节点按权重向下展开。 ## 改动清单 - **[MODIFY] `src/game/acts.ts`** —— 加载 `Armor.txt`、`Weapons.txt`、`Misc.txt`、`MagicPrefix.txt`、`MagicSuffix.txt`、`UniqueItems.txt`、`SetItems.txt`、`TreasureClassEx.txt`、`ItemTypes.txt`、`Runes.txt`、`Gems.txt` - **[MODIFY] `src/game/items.ts`** —— `itemBaseFromRow` / `affixFromRow` 列映射逐列核对真实表。新增:品质等级(普通/魔法/稀有/套装/暗金/符文之语)、插槽、镶嵌、耐久、需求(力量/敏捷/等级) - **[NEW] `src/game/treasure.ts`** —— `TreasureClassEx.txt` 掉落树 - **[NEW] `src/game/runeword.ts`** —— 符文之语 ## 注意 物品结构的任何变动都会影响 M13(`.d2s` 存档互通)——`.d2s` 的物品段是**位对齐的变长编码**, 字段一旦定错,往返比对就过不去。建议在本里程碑设计数据结构时就预留 `.d2s` 需要的全部字段 (GUID、品质、插槽数、已镶嵌物品、耐久、个性化名称等)。 ## 验收 - [ ] 打死怪掉出带词缀的稀有装备,属性生效 - [ ] 新增 `scripts/verify-items.ts`:全量物品表列映射断言 - [ ] `TreasureClassEx` 递归树无环、各节点概率和为 1 - [ ] 符文之语可合成且属性正确
troytt added this to the [M10] 真实物品系统:500 基础物品、1000 词缀、TreasureClassEx 掉落树 milestone 2026-09-17 07:23:00 +00:00
Author
Owner

子 issue 拆分(24 个)

已按「每张表 / 每个算法一个」的粒度拆完,全部挂在本 milestone 下。金标准为 1.13c MPQ 表 + 1.13c DLL 反汇编(非 D2MOO)。

第 0 层 · 地基(阻塞其余全部)

# 标题
#87 [P0][T23] 实现 1.13c PRNG:64 位 LCG(0x6AC690C5)+ per-unit seed
#88 [P0][T17] 冻结物品数据结构:一次预留 .d2s 位对齐编码所需的全部字段

第 1 层 · 数据加载

# 标题
#89 [P0][T01] 加载 ItemTypes.txt:类型树、Equiv 继承链、TreasureClass 与 Rarity 列
#90 [P0][T02] 加载 ItemStatCost.txt + Properties.txt:属性层与属性函数
#91 [P1][T03] 加载 Armor.txt → ItemBase(202 行 × 164 列)
#92 [P1][T04] 加载 Weapons.txt → ItemBase(306 行 × 166 列)
#93 [P1][T05] 加载 Misc.txt → ItemBase(151 行 × 168 列)
#94 [P1][T06] 加载 MagicPrefix.txt / MagicSuffix.txt(637 + 741 行)
#95 [P2][T07] 加载 RarePrefix.txt / RareSuffix.txt(46 + 155 行)
#96 [P2][T08] 加载 AutoMagic.txt(36 行)
#97 [P1][T09] 加载 UniqueItems.txt(401 行,enabled=1 仅 385)
#98 [P1][T10] 加载 SetItems.txt + Sets.txt(127 + 32 行)
#99 [P0][T11] 加载 TreasureClassEx.txt(852 行 × 33 列):纯加载与索引

第 2 层 · 算法

# 标题
#100 [P0][T12] 实现 auto-TC 运行时生成:armo3-87 / weap3-87 等 100 个虚拟节点
#101 [P0][T13] 实现 TreasureClassEx 展开:64 帧显式栈 + 前缀和二分 + NoDrop 浮点缩放
#102 [P1][T14] 实现 ItemRatio 品质判定:严格复刻整数截断顺序
#103 [P1][T24] 实现 exceptional / elite 基底升级:qf[4] / qf[5] 的两个 1024 判定
#104 [P1][T15] 实现词缀抽取:alvl 计算、group 冲突排除、rare 3~6 词缀
#105 [P2][T16] 实现符文之语:Runes.txt 169 行(complete=1 仅 78 条可用)
#106 [P2][T18] 实现宝石镶嵌:Gems.txt 68 行 × 武器/盾/头盔三套属性

第 3 层 · 集成

# 标题
#107 [P0][T19] 用 TC 树替换 rollDrop,接线 MonStats.TreasureClass → engine → act-scene
#108 [P1][T20] 物品名本地化:namestr → string.tbl 三层链

第 4 层 · 工具与验收

# 标题
#109 [P1][T21] 重写 scripts/verify-items.ts:全量真实表列映射断言 + TC 树不变量
#110 [P3][T22] 新增 loot.html + src/loot.ts:掉落模拟器 / 概率表双 Tab

关键路径

#87 → #88 → #89 → #99/#91/#92 → #100 → #101 → #102 → #103 → #104 → #107 → #109

#93 / #95 / #96 / #105 / #106 / #108 可随时并行插入。


调查过程中对母 issue 估计值的修正

项 母 issue 估计 1.13c 实测
基础物品 ~500 659(Armor 202 + Weapons 306 + Misc 151)
魔法词缀 ~1000 1378(637 + 741),其中可生成 1162

三个原计划完全没覆盖、调查中发现的必做项

  1. auto-TC 运行时生成(#100) —— TreasureClassEx.txt 的 Item 列引用了 100 个表中根本不存在的名字(weap3 armo3 …),由引擎在载入期从 ItemTypes.TreasureClass=1 的 5 个种子动态生成。不做这个,Act 1 Equip A 直接断链,玩家一件装备都掉不出来。

  2. exceptional / elite 基底升级(#103) —— TC 的 6 个品质因子里 [4] [5] 各做一次 Rand & 0x3FF 判定,对应 ubercode / ultracode 三档基底升级。不做的话高难度永远掉不出精英基底。

  3. 物品名本地化(#108) —— 不做的话界面上全是四字母 code。已从 P2 提到 P1。


三个必错的陷阱(已写进对应子 issue)

[!CAUTION]
品质判定公式:社区流传的两个版本都不完全对。反汇编(D2Game!6FC2FC40)证明是 * 128 之后 * 100 / EffectiveMF,且 EffectiveMF 自身已含 +100,并有 mf <= 10 完全不衰减 的门槛。数学上等价但整数截断位置不同。

[!CAUTION]
ItemRatio 的列顺序是 Unique → Rare → Set → Magic,判定顺序却是 Unique → Set → Rare → Magic。 照列顺序写循环会静默把 Set 和 Rare 对调,两者数值接近,单测极难发现。

[!CAUTION]
Picks < 0 时 NoDrop 完全无效,且一次 RNG 都不消耗。 全表 184 行(21.6%)是负 Picks —— Champ / Unique / Super / Countess / Griswold / Smith 全在其中。


证据强度说明

区块 强度
PRNG、TC 展开、NoDrop 缩放、品质判定、.d2s 位宽 逐指令确证
genFlags 0x04/0x10 的语义、ItemRatio 选行的三个列语义 高可信推断
词缀生成(alvl 公式、magic 前后缀分布、rare 条数分布) 证据最弱 —— D2Game 侧的 roll 函数本轮未定位
符文之语合成判定 高可信推断(与表结构自洽)

#104 和 #105 的正文里已显式标注证据强度,并附了「把存疑变确证」的实测收敛方法。不要把这两个当成和 #101 / #102 同等可信。


[!NOTE]
本轮范围是只做调查与拆分,不写实现代码。CubeMain(赫拉迪克方块配方)经确认不纳入本里程碑,留给后续。

## 子 issue 拆分(24 个) 已按「每张表 / 每个算法一个」的粒度拆完,全部挂在本 milestone 下。金标准为 **1.13c MPQ 表 + 1.13c DLL 反汇编**(非 D2MOO)。 ### 第 0 层 · 地基(阻塞其余全部) | # | 标题 | |---|---| | #87 | `[P0][T23]` 实现 1.13c PRNG:64 位 LCG(`0x6AC690C5`)+ per-unit seed | | #88 | `[P0][T17]` 冻结物品数据结构:一次预留 `.d2s` 位对齐编码所需的全部字段 | ### 第 1 层 · 数据加载 | # | 标题 | |---|---| | #89 | `[P0][T01]` 加载 ItemTypes.txt:类型树、Equiv 继承链、TreasureClass 与 Rarity 列 | | #90 | `[P0][T02]` 加载 ItemStatCost.txt + Properties.txt:属性层与属性函数 | | #91 | `[P1][T03]` 加载 Armor.txt → ItemBase(202 行 × 164 列) | | #92 | `[P1][T04]` 加载 Weapons.txt → ItemBase(306 行 × 166 列) | | #93 | `[P1][T05]` 加载 Misc.txt → ItemBase(151 行 × 168 列) | | #94 | `[P1][T06]` 加载 MagicPrefix.txt / MagicSuffix.txt(637 + 741 行) | | #95 | `[P2][T07]` 加载 RarePrefix.txt / RareSuffix.txt(46 + 155 行) | | #96 | `[P2][T08]` 加载 AutoMagic.txt(36 行) | | #97 | `[P1][T09]` 加载 UniqueItems.txt(401 行,enabled=1 仅 385) | | #98 | `[P1][T10]` 加载 SetItems.txt + Sets.txt(127 + 32 行) | | #99 | `[P0][T11]` 加载 TreasureClassEx.txt(852 行 × 33 列):纯加载与索引 | ### 第 2 层 · 算法 | # | 标题 | |---|---| | #100 | `[P0][T12]` 实现 auto-TC 运行时生成:armo3-87 / weap3-87 等 100 个虚拟节点 | | #101 | `[P0][T13]` 实现 TreasureClassEx 展开:64 帧显式栈 + 前缀和二分 + NoDrop 浮点缩放 | | #102 | `[P1][T14]` 实现 ItemRatio 品质判定:严格复刻整数截断顺序 | | #103 | `[P1][T24]` 实现 exceptional / elite 基底升级:qf[4] / qf[5] 的两个 1024 判定 | | #104 | `[P1][T15]` 实现词缀抽取:alvl 计算、group 冲突排除、rare 3~6 词缀 | | #105 | `[P2][T16]` 实现符文之语:Runes.txt 169 行(complete=1 仅 78 条可用) | | #106 | `[P2][T18]` 实现宝石镶嵌:Gems.txt 68 行 × 武器/盾/头盔三套属性 | ### 第 3 层 · 集成 | # | 标题 | |---|---| | #107 | `[P0][T19]` 用 TC 树替换 `rollDrop`,接线 MonStats.TreasureClass → engine → act-scene | | #108 | `[P1][T20]` 物品名本地化:namestr → string.tbl 三层链 | ### 第 4 层 · 工具与验收 | # | 标题 | |---|---| | #109 | `[P1][T21]` 重写 `scripts/verify-items.ts`:全量真实表列映射断言 + TC 树不变量 | | #110 | `[P3][T22]` 新增 `loot.html` + `src/loot.ts`:掉落模拟器 / 概率表双 Tab | --- ## 关键路径 ``` #87 → #88 → #89 → #99/#91/#92 → #100 → #101 → #102 → #103 → #104 → #107 → #109 ``` #93 / #95 / #96 / #105 / #106 / #108 可随时并行插入。 --- ## 调查过程中对母 issue 估计值的修正 | 项 | 母 issue 估计 | 1.13c 实测 | |---|---|---| | 基础物品 | ~500 | **659**(Armor 202 + Weapons 306 + Misc 151) | | 魔法词缀 | ~1000 | **1378**(637 + 741),其中可生成 **1162** | --- ## 三个原计划完全没覆盖、调查中发现的必做项 1. **auto-TC 运行时生成(#100)** —— `TreasureClassEx.txt` 的 `Item` 列引用了 **100 个表中根本不存在的名字**(`weap3` `armo3` …),由引擎在载入期从 `ItemTypes.TreasureClass=1` 的 5 个种子动态生成。不做这个,`Act 1 Equip A` 直接断链,玩家一件装备都掉不出来。 2. **exceptional / elite 基底升级(#103)** —— TC 的 6 个品质因子里 `[4]` `[5]` 各做一次 `Rand & 0x3FF` 判定,对应 `ubercode` / `ultracode` 三档基底升级。不做的话高难度永远掉不出精英基底。 3. **物品名本地化(#108)** —— 不做的话界面上全是四字母 code。已从 P2 提到 P1。 --- ## 三个必错的陷阱(已写进对应子 issue) > [!CAUTION] > **品质判定公式**:社区流传的两个版本都不完全对。反汇编(`D2Game!6FC2FC40`)证明是 `* 128` 之后 `* 100 / EffectiveMF`,且 `EffectiveMF` 自身已含 `+100`,并有 `mf <= 10 完全不衰减` 的门槛。数学上等价但**整数截断位置不同**。 > [!CAUTION] > **ItemRatio 的列顺序是 Unique → Rare → Set → Magic,判定顺序却是 Unique → Set → Rare → Magic。** 照列顺序写循环会静默把 Set 和 Rare 对调,两者数值接近,单测极难发现。 > [!CAUTION] > **`Picks < 0` 时 NoDrop 完全无效,且一次 RNG 都不消耗。** 全表 184 行(21.6%)是负 Picks —— Champ / Unique / Super / Countess / Griswold / Smith 全在其中。 --- ## 证据强度说明 | 区块 | 强度 | |---|---| | PRNG、TC 展开、NoDrop 缩放、品质判定、`.d2s` 位宽 | **逐指令确证** | | genFlags 0x04/0x10 的语义、ItemRatio 选行的三个列语义 | 高可信推断 | | **词缀生成(alvl 公式、magic 前后缀分布、rare 条数分布)** | **证据最弱** —— D2Game 侧的 roll 函数本轮未定位 | | 符文之语合成判定 | 高可信推断(与表结构自洽) | #104 和 #105 的正文里已显式标注证据强度,并附了「把存疑变确证」的实测收敛方法。**不要把这两个当成和 #101 / #102 同等可信。** --- > [!NOTE] > 本轮范围是**只做调查与拆分,不写实现代码**。CubeMain(赫拉迪克方块配方)经确认**不纳入本里程碑**,留给后续。
Author
Owner

🎉 Milestone M10(真实物品系统)全部 24 个子任务已全部闭环完成!

交付清单

  1. 地基与底层规范(Wave 1: #87, #88, #89, #90, #95, #96):

    • 1.13c 64-bit LCG PRNG (D2Rng) 与 per-unit seed 隔离
    • 物品数据结构冻结与 .d2s 位对齐规范文档及字段预留
    • ItemTypes.txt 98 种有效类型、Equiv 继承链与 5 个 auto-TC 种子
    • ItemStatCost.txt 359 行 + Properties.txt 268 行属性解析与位宽映射
    • RarePrefix.txt (46) / RareSuffix.txt (155) 稀有名字词池
    • AutoMagic.txt (36) 职业专属装备自动词缀
  2. 装备基底与表格加载(Wave 2: #91, #92, #93, #94, #97, #98, #99):

    • Armor.txt 202 护甲基底
    • Weapons.txt 306 武器基底(空列头安全防御与 15 个 rarity=0 标记)
    • Misc.txt 151 杂项基底(含 33 枚符文索引与金币/药水识别)
    • MagicPrefix (637) / MagicSuffix (741) 共 1378 条全量魔法词缀
    • UniqueItems.txt 401 行暗金装备(385 enabled,异构参数支持)
    • SetItems.txt (127) + Sets.txt (32) 套装与多层加成
    • TreasureClassEx.txt 852 行全量 TC 节点索引、前缀和预计算与 218 个悬空引用解析
  3. 核心算法层(Wave 3: #100, #101, #102, #103, #104, #105, #106):

    • auto-TC 运行时生成(weap3..87/armo3..87/bow3..87/mele3..39 共 100 虚拟节点)
    • TreasureClassEx 展开引擎(64 层调用栈、尾调用优化、Picks<0 确定性展开与 Picks>=0 玩家数 NoDrop 缩放、品质因子继承与金币倍率解析)
    • ItemRatio.txt 6 层阶梯品质判定(Diminishing MF 惩罚曲线、class-specific 调整、fallback 保底降级与耐久衰减)
    • 基底升级(ubercode/ultracode 阶梯判定与 qlvl 继承)
    • 词缀抽取与 alvl 公式、group 互斥与稀有物品 3~6 词缀抽取
    • Runes.txt 169 行解析、78 组符文之语与 23 组天梯符文之语装配
    • Gems.txt 68 行宝石/符文镶嵌解析(武器/头胸/盾牌分流)与 ilvl 三段插槽上限
  4. 集成与本地化(Wave 4: #107, #108):

    • 物品名本地化三层链(patch -> expansion -> base),中文 UTF-8 解码与 LoD 回退 ENG
    • 用真实 TC 树替换 rollDrop,完整接线 MonStats.TreasureClass 12 列 -> TC group 升级 -> 掉落展开 -> 品质判定 -> 词缀抽取 -> GameEngine -> act-scene.ts,并彻底删除 DEMO_BASES / DEMO_AFFIXES 占位数据
  5. 终验套件与工具(Wave 5: #109, #110):

    • 重写 scripts/verify-items.ts:120 项全量真实 MPQ 断言与 TC 树不变量核验通过
    • 新增掉落模拟器与概率表网页 loot.html + src/loot.ts(蒙特卡洛模拟 + 解析概率表双 Tab)

验证结果

  • npm run typecheck: 0 error
  • npx tsx scripts/verify-items.ts: 120 checks, 0 problems (ALL 1.13c REAL MPQ INVARIANTS HOLD)
  • npx vitest run: 97 test files passed, 1565 tests passed, 0 failures
  • npm run build: 生产打包全部通过

所有分支与 worktree 均已清理完毕,代码已合入 main 并推送到远程仓库。

## 🎉 Milestone M10(真实物品系统)全部 24 个子任务已全部闭环完成! ### 交付清单 1. **地基与底层规范(Wave 1: #87, #88, #89, #90, #95, #96)**: - 1.13c 64-bit LCG PRNG (`D2Rng`) 与 per-unit seed 隔离 - 物品数据结构冻结与 `.d2s` 位对齐规范文档及字段预留 - `ItemTypes.txt` 98 种有效类型、Equiv 继承链与 5 个 auto-TC 种子 - `ItemStatCost.txt` 359 行 + `Properties.txt` 268 行属性解析与位宽映射 - `RarePrefix.txt` (46) / `RareSuffix.txt` (155) 稀有名字词池 - `AutoMagic.txt` (36) 职业专属装备自动词缀 2. **装备基底与表格加载(Wave 2: #91, #92, #93, #94, #97, #98, #99)**: - `Armor.txt` 202 护甲基底 - `Weapons.txt` 306 武器基底(空列头安全防御与 15 个 rarity=0 标记) - `Misc.txt` 151 杂项基底(含 33 枚符文索引与金币/药水识别) - `MagicPrefix` (637) / `MagicSuffix` (741) 共 1378 条全量魔法词缀 - `UniqueItems.txt` 401 行暗金装备(385 enabled,异构参数支持) - `SetItems.txt` (127) + `Sets.txt` (32) 套装与多层加成 - `TreasureClassEx.txt` 852 行全量 TC 节点索引、前缀和预计算与 218 个悬空引用解析 3. **核心算法层(Wave 3: #100, #101, #102, #103, #104, #105, #106)**: - `auto-TC` 运行时生成(weap3..87/armo3..87/bow3..87/mele3..39 共 100 虚拟节点) - `TreasureClassEx` 展开引擎(64 层调用栈、尾调用优化、Picks<0 确定性展开与 Picks>=0 玩家数 NoDrop 缩放、品质因子继承与金币倍率解析) - `ItemRatio.txt` 6 层阶梯品质判定(Diminishing MF 惩罚曲线、class-specific 调整、fallback 保底降级与耐久衰减) - 基底升级(ubercode/ultracode 阶梯判定与 qlvl 继承) - 词缀抽取与 `alvl` 公式、group 互斥与稀有物品 3~6 词缀抽取 - `Runes.txt` 169 行解析、78 组符文之语与 23 组天梯符文之语装配 - `Gems.txt` 68 行宝石/符文镶嵌解析(武器/头胸/盾牌分流)与 ilvl 三段插槽上限 4. **集成与本地化(Wave 4: #107, #108)**: - 物品名本地化三层链(patch -> expansion -> base),中文 UTF-8 解码与 LoD 回退 ENG - 用真实 TC 树替换 `rollDrop`,完整接线 `MonStats.TreasureClass` 12 列 -> TC group 升级 -> 掉落展开 -> 品质判定 -> 词缀抽取 -> `GameEngine` -> `act-scene.ts`,并**彻底删除 DEMO_BASES / DEMO_AFFIXES 占位数据** 5. **终验套件与工具(Wave 5: #109, #110)**: - 重写 `scripts/verify-items.ts`:120 项全量真实 MPQ 断言与 TC 树不变量核验通过 - 新增掉落模拟器与概率表网页 `loot.html` + `src/loot.ts`(蒙特卡洛模拟 + 解析概率表双 Tab) ### 验证结果 - `npm run typecheck`: 0 error - `npx tsx scripts/verify-items.ts`: 120 checks, 0 problems (ALL 1.13c REAL MPQ INVARIANTS HOLD) - `npx vitest run`: 97 test files passed, 1565 tests passed, 0 failures - `npm run build`: 生产打包全部通过 所有分支与 worktree 均已清理完毕,代码已合入 `main` 并推送到远程仓库。
Sign in to join this conversation.
No Label
No project
No Assignees
1 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: troytt/diablo2-web#25
No description provided.