[P0][T11] 加载 TreasureClassEx.txt(852 行 × 33 列):纯加载与索引,不含算法 #99

Closed
opened 2026-09-18 10:50:48 +00:00 by troytt · 1 comment
Owner

问题

这是整个 M10 的核心数据。掉落树的每个节点都在这张表里。

本 issue 只做加载与索引,递归展开算法在 T-13、auto-TC 生成在 T-12。拆开是因为这张表的结构本身就有足够多的坑值得单独一个 issue。

金标准数据

852 真实行(853 原始行 − 1 行空白;注意本表没有 Expansion 行)× 33 列。

完整列头(严格文件顺序 + 索引)

[0] Treasure Class  [1] group  [2] level  [3] Picks
[4] Unique  [5] Set  [6] Rare  [7] Magic  [8] NoDrop
[9] Item1  [10] Prob1    [11] Item2  [12] Prob2    [13] Item3  [14] Prob3
[15] Item4 [16] Prob4    [17] Item5  [18] Prob5    [19] Item6  [20] Prob6
[21] Item7 [22] Prob7    [23] Item8  [24] Prob8    [25] Item9  [26] Prob9
[27] Item10 [28] Prob10
[29] SumItems  [30] TotalProb  [31] DropChance  [32] Term

Item/Prob 恰好 10 对,不多不少。

各控制列的真实取值范围

列 非空行数 取值
group 702/852 整数 1..18。组尺寸 1:44 2:36 3:36 4:36 5:15 6:45 7:42 8:42 9:42 10:42 11:42 12:42 13:43 14:42 15:42 16:15 17:6 18:90。150 行无 group
level 702/852 0..96,63 个不同值
Picks 852/852 全有值 仅 9 个值 -4, -3, -2, 1, 2, 3, 4, 5, 7;分布 -4:90 -3:48 -2:46 1:515 2:42 3:6 4:45 5:30 7:30
Unique 240/852 512, 650, 800, 850, 883, 900, 983, 993, 995, 1024
Set 231/852 654, 800, 883, 900, 983, 993, 995
Rare 231/852 800, 883, 900, 972, 983, 1024
Magic 231/852 恒为 1024(唯一取值)
NoDrop 628/852 0, 5, 15, 19, 75, 100, 125;分布 空:224 0:328 5:3 15:18 19:36 75:42 100:180 125:21
SumItems 仅 101/852 26..42
TotalProb 仅 101/852 66..2306
DropChance 仅 101/852 0.4545..0.99xx
Term 851/852 恒为 0,无语义

叶子(Item 列)的 5 种形态 —— 575 个不同取值

类别 数量 例子
A. 另一个 TC 行名(递归) 357 "Act 1 Equip A" "Act 1 Junk" "Potion 1" "Misc 0" "Ammo" "Jewelry A" "Chipped Gem" "Runes 1".."Runes 17" "Countess Rune (H)"
B. 字面 gld(金币) 1 gld
C. 基础物品 code 113 rin amu jew cm1-3 gcv..gcw skc/skf hp1-5 mp1-5 rvs rvl vps key tsc isc opl gpl aqv cqv r01-33 pk1
D. <ItemTypes.Code><qlvl> 虚拟 auto-TC(表里没有这些行!) 100 weap3..weap87(29 档) armo3..armo87(29) bow3..bow87(29) mele3..mele39(13)
E. 带参数的金币 / 直接指名暗金 4 "gld,mul=1280" "gld,mul=1536" "gld,mul=2048"、Annihilus

合计被引用但表中不存在的名字 218 个 = 100 个 auto-TC + 118 个真实物品码。

group / level 的语义

同 group 内按 level 升序构成「随区域等级升级」的链:

group=1  (Equip, 44 行): Act 1 Equip A(0) → B(5) → C(9) → Act 2 Equip A(12) → … → Act 5 (H) Equip C
group=5  (Good, 15 行):  Act 1 Good(0) → Act 2 Good(12) → … → Act 1 (N) Good(38) → …
group=7  (H2H, 42 行):   Act 1 H2H A(0) B(5) C(9) → Act 2 H2H A(12) …
group=11 Citem(42)  group=12 Cpot(42)  group=13 Champ(43)  group=14 Uitem(42)
group=18 (Super, 90 行): Act 1 Super A(0) … Act 5 (H) Super Cx(96)

150 行无 group(Cow (H)、Act N Junk、Potion N、Runes N、Chipped Gem、Boss 专属 TC),不参与升级。

运行期内存布局(反汇编确证,供 T-13 参考)

TC 记录:+0x04 条目数、+0x08 TotalProb(classic)、+0x0C TotalProb(expansion)、+0x10 Picks(int32 带符号)、+0x14 NoDrop(int32)、+0x28 条目数组指针。

条目数组 stride = 0x1C(28 字节):+0x00 累计概率(classic)、+0x04 累计概率(expansion)、+0x08 u16 物品/子TC 索引(0xFFFF = 空)、+0x0A u16 参数(unique/set id,或金币倍率)、+0x0C 标志(bit0 强制 unique、bit1 强制 set、bit2 子 TC 引用)、+0x0E..+0x18 6 个 u16 品质因子。

累计概率是载入期预计算的前缀和,运行期用二分查找(D2Game!6FC32690),不是每次累加。classic / expansion 各一套(classic 要跳过 version >= 100 的扩展物品)。

实现要求

  1. 加载表,剔除空白行,建 Treasure Class 名 → row 索引。
  2. 载入期预计算每个 TC 的累计概率前缀和与 TotalProb(classic / expansion 各一套),供 T-13 二分。
  3. 建 group → [按 level 升序的 TC] 索引,供 T-13 做 TC 升级。
  4. 解析每个 ItemN,分类成上表 5 种形态之一,标记出 218 个悬空引用(其中 100 个交给 T-12 生成,118 个应能在全局物品码表里找到)。
  5. 解析 gld,mul=N 语法(逗号后的 mul= 参数)。
  6. 6 个品质因子(Unique/Set/Rare/Magic + 两个额外)存成数组,供 T-13 沿链传播。

陷阱

[!CAUTION]
SumItems / TotalProb / DropChance 是注释列,游戏不读。 只有 101 行有值(Act N (x) Equip A/B/C 链)。已验算 Act 5 (H) Equip B:SumItems=36 = 本层 ΣProb,TotalProb=1566 = 36+1530,DropChance=0.9770 = 1530/1566 —— 就是设计者自己算着玩的备注。必须自己累加,不能读这三列。

[!CAUTION]
100 个 auto-TC(armo3 / weap3 等)在表里根本不存在,必须由 T-12 运行时生成。加载阶段要能识别出这类名字并标记为「待 T-12 填充」,不能当成错误。交叉证据:经典版 TreasureClass.txt(707 行 / 28 列,无 group/level 列)里同样不含这些名字。

[!WARNING]
Magic 品质因子在全表 231 个非空行里恒等于 1024(中性)。1.13c 没有任何 TC 修改魔法品质概率,可以常量化。

[!NOTE]
Term 列恒为 0,是行终结标记,无语义,忽略即可。

验收标准

  • 852 行全部解析并建索引
  • 累计概率前缀和预计算完成,classic / expansion 各一套
  • group → [TC by level] 索引建立,18 个 group 的尺寸与上表完全一致
  • 218 个悬空引用全部分类:100 个标记为 auto-TC 待填充,118 个能在全局物品码表里解析到,未知数为 0
  • gld,mul=1280/1536/2048 三种被正确解析出倍率
  • Annihilus 被识别为直接指名暗金
  • Magic 列的 231 个非空值全部等于 1024(断言,防止数据源被换)
  • npm run typecheck 0 error
  • npx vitest run 全绿
  • npx tsx scripts/verify-items.ts 新增断言通过

溯源

金标准(数据) 1.13c MPQ data\global\excel\ 下的 TreasureClassEx.txt(本机 /usr/local/google/home/taodao/d2-data,挂载序 d2data → d2exp → Patch_D2,Patch_D2 优先)
金标准(算法) 1.13c 反汇编:D2Game!6FC32690(objdump -d,D2Common 基址 0x6FD50000、D2Game 0x6FC10000)
本 issue 结论强度 表数据为本机 1.13c 实测确证;内存布局为反汇编确证

[!WARNING]
D2MOO 是 1.10f,不是本项目的金标准。 它只能作线索使用。任何涉及常量、列序、整数截断位置或 RNG 消耗次数的结论,都必须回到 1.13c 的表格或 DLL 复核后再落地。

## 问题 这是整个 M10 的核心数据。掉落树的每个节点都在这张表里。 本 issue **只做加载与索引**,递归展开算法在 T-13、auto-TC 生成在 T-12。拆开是因为这张表的结构本身就有足够多的坑值得单独一个 issue。 ## 金标准数据 **852 真实行**(853 原始行 − 1 行空白;**注意本表没有 `Expansion` 行**)× **33 列**。 ### 完整列头(严格文件顺序 + 索引) ``` [0] Treasure Class [1] group [2] level [3] Picks [4] Unique [5] Set [6] Rare [7] Magic [8] NoDrop [9] Item1 [10] Prob1 [11] Item2 [12] Prob2 [13] Item3 [14] Prob3 [15] Item4 [16] Prob4 [17] Item5 [18] Prob5 [19] Item6 [20] Prob6 [21] Item7 [22] Prob7 [23] Item8 [24] Prob8 [25] Item9 [26] Prob9 [27] Item10 [28] Prob10 [29] SumItems [30] TotalProb [31] DropChance [32] Term ``` **Item/Prob 恰好 10 对**,不多不少。 ### 各控制列的真实取值范围 | 列 | 非空行数 | 取值 | |---|---:|---| | `group` | 702/852 | 整数 **1..18**。组尺寸 `1:44 2:36 3:36 4:36 5:15 6:45 7:42 8:42 9:42 10:42 11:42 12:42 13:43 14:42 15:42 16:15 17:6 18:90`。150 行无 group | | `level` | 702/852 | **0..96**,63 个不同值 | | `Picks` | **852/852 全有值** | 仅 9 个值 **-4, -3, -2, 1, 2, 3, 4, 5, 7**;分布 `-4:90 -3:48 -2:46 1:515 2:42 3:6 4:45 5:30 7:30` | | `Unique` | 240/852 | 512, 650, 800, 850, 883, 900, 983, 993, 995, 1024 | | `Set` | 231/852 | 654, 800, 883, 900, 983, 993, 995 | | `Rare` | 231/852 | 800, 883, 900, 972, 983, 1024 | | `Magic` | 231/852 | **恒为 1024(唯一取值)** | | `NoDrop` | 628/852 | 0, 5, 15, 19, 75, 100, 125;分布 `空:224 0:328 5:3 15:18 19:36 75:42 100:180 125:21` | | `SumItems` | **仅 101/852** | 26..42 | | `TotalProb` | **仅 101/852** | 66..2306 | | `DropChance` | **仅 101/852** | 0.4545..0.99xx | | `Term` | 851/852 | 恒为 `0`,无语义 | ### 叶子(Item 列)的 5 种形态 —— 575 个不同取值 | 类别 | 数量 | 例子 | |---|---:|---| | **A. 另一个 TC 行名(递归)** | **357** | `"Act 1 Equip A"` `"Act 1 Junk"` `"Potion 1"` `"Misc 0"` `"Ammo"` `"Jewelry A"` `"Chipped Gem"` `"Runes 1".."Runes 17"` `"Countess Rune (H)"` | | **B. 字面 `gld`(金币)** | **1** | `gld` | | **C. 基础物品 code** | **113** | `rin` `amu` `jew` `cm1-3` `gcv..gcw` `skc/skf` `hp1-5` `mp1-5` `rvs` `rvl` `vps` `key` `tsc` `isc` `opl` `gpl` `aqv` `cqv` `r01-33` `pk1` | | **D. `<ItemTypes.Code><qlvl>` 虚拟 auto-TC(表里没有这些行!)** | **100** | `weap3..weap87`(29 档) `armo3..armo87`(29) `bow3..bow87`(29) `mele3..mele39`(13) | | **E. 带参数的金币 / 直接指名暗金** | **4** | `"gld,mul=1280"` `"gld,mul=1536"` `"gld,mul=2048"`、`Annihilus` | 合计被引用但表中不存在的名字 **218 个** = 100 个 auto-TC + 118 个真实物品码。 ### group / level 的语义 同 `group` 内按 `level` 升序构成「随区域等级升级」的链: ``` group=1 (Equip, 44 行): Act 1 Equip A(0) → B(5) → C(9) → Act 2 Equip A(12) → … → Act 5 (H) Equip C group=5 (Good, 15 行): Act 1 Good(0) → Act 2 Good(12) → … → Act 1 (N) Good(38) → … group=7 (H2H, 42 行): Act 1 H2H A(0) B(5) C(9) → Act 2 H2H A(12) … group=11 Citem(42) group=12 Cpot(42) group=13 Champ(43) group=14 Uitem(42) group=18 (Super, 90 行): Act 1 Super A(0) … Act 5 (H) Super Cx(96) ``` 150 行无 group(`Cow (H)`、`Act N Junk`、`Potion N`、`Runes N`、`Chipped Gem`、Boss 专属 TC),不参与升级。 ### 运行期内存布局(反汇编确证,供 T-13 参考) TC 记录:`+0x04` 条目数、`+0x08` **TotalProb(classic)**、`+0x0C` **TotalProb(expansion)**、`+0x10` Picks(int32 带符号)、`+0x14` NoDrop(int32)、`+0x28` 条目数组指针。 条目数组 **stride = 0x1C(28 字节)**:`+0x00` 累计概率(classic)、`+0x04` 累计概率(expansion)、`+0x08` u16 物品/子TC 索引(`0xFFFF` = 空)、`+0x0A` u16 参数(unique/set id,或金币倍率)、`+0x0C` 标志(bit0 强制 unique、bit1 强制 set、bit2 **子 TC 引用**)、`+0x0E..+0x18` **6 个 u16 品质因子**。 > **累计概率是载入期预计算的前缀和**,运行期用二分查找(`D2Game!6FC32690`),不是每次累加。classic / expansion 各一套(classic 要跳过 `version >= 100` 的扩展物品)。 ## 实现要求 1. 加载表,剔除空白行,建 `Treasure Class 名 → row` 索引。 2. **载入期预计算**每个 TC 的累计概率前缀和与 `TotalProb`(classic / expansion 各一套),供 T-13 二分。 3. 建 `group → [按 level 升序的 TC]` 索引,供 T-13 做 TC 升级。 4. 解析每个 `ItemN`,分类成上表 5 种形态之一,**标记出 218 个悬空引用**(其中 100 个交给 T-12 生成,118 个应能在全局物品码表里找到)。 5. 解析 `gld,mul=N` 语法(逗号后的 `mul=` 参数)。 6. 6 个品质因子(Unique/Set/Rare/Magic + 两个额外)存成数组,供 T-13 沿链传播。 ## 陷阱 > [!CAUTION] > **`SumItems` / `TotalProb` / `DropChance` 是注释列,游戏不读。** 只有 101 行有值(`Act N (x) Equip A/B/C` 链)。已验算 `Act 5 (H) Equip B`:`SumItems=36` = 本层 ΣProb,`TotalProb=1566 = 36+1530`,`DropChance=0.9770 = 1530/1566` —— 就是设计者自己算着玩的备注。**必须自己累加,不能读这三列。** > [!CAUTION] > **100 个 auto-TC(`armo3` / `weap3` 等)在表里根本不存在**,必须由 T-12 运行时生成。加载阶段要能识别出这类名字并标记为「待 T-12 填充」,不能当成错误。交叉证据:经典版 `TreasureClass.txt`(707 行 / 28 列,无 group/level 列)里同样不含这些名字。 > [!WARNING] > `Magic` 品质因子在全表 231 个非空行里**恒等于 1024**(中性)。1.13c 没有任何 TC 修改魔法品质概率,可以常量化。 > [!NOTE] > `Term` 列恒为 0,是行终结标记,无语义,忽略即可。 ## 验收标准 - [ ] 852 行全部解析并建索引 - [ ] 累计概率前缀和预计算完成,classic / expansion 各一套 - [ ] `group → [TC by level]` 索引建立,18 个 group 的尺寸与上表完全一致 - [ ] 218 个悬空引用**全部分类**:100 个标记为 auto-TC 待填充,118 个能在全局物品码表里解析到,**未知数为 0** - [ ] `gld,mul=1280/1536/2048` 三种被正确解析出倍率 - [ ] `Annihilus` 被识别为直接指名暗金 - [ ] `Magic` 列的 231 个非空值全部等于 1024(断言,防止数据源被换) - [ ] `npm run typecheck` 0 error - [ ] `npx vitest run` 全绿 - [ ] `npx tsx scripts/verify-items.ts` 新增断言通过 --- ### 溯源 | | | |---|---| | 金标准(数据) | 1.13c MPQ `data\global\excel\` 下的 `TreasureClassEx.txt`(本机 `/usr/local/google/home/taodao/d2-data`,挂载序 d2data → d2exp → Patch_D2,**Patch_D2 优先**) | | 金标准(算法) | 1.13c 反汇编:`D2Game!6FC32690`(`objdump -d`,D2Common 基址 `0x6FD50000`、D2Game `0x6FC10000`) | | 本 issue 结论强度 | 表数据为**本机 1.13c 实测确证**;内存布局为**反汇编确证** | > [!WARNING] > **D2MOO 是 1.10f,不是本项目的金标准。** 它只能作线索使用。任何涉及常量、列序、整数截断位置或 RNG 消耗次数的结论,都必须回到 1.13c 的表格或 DLL 复核后再落地。
troytt added this to the [M10] 真实物品系统:500 基础物品、1000 词缀、TreasureClassEx 掉落树 milestone 2026-09-18 10:50:48 +00:00
Author
Owner

已完成并通过 1.13c MPQ 真实表验证(852 行全量 TC 节点,1..18 group 升序索引,218 悬空引用分类,Magic 1024 断言,前缀和预计算),合并入 main。

已完成并通过 1.13c MPQ 真实表验证(852 行全量 TC 节点,1..18 group 升序索引,218 悬空引用分类,Magic 1024 断言,前缀和预计算),合并入 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#99
No description provided.