[P2][T5] 修复 Kurast / Mesa / Desert / Barricade 的瓦片引用缺失 #83

Closed
opened 2026-09-18 09:47:28 +00:00 by troytt · 2 comments
Owner

问题

npx tsx scripts/verify-generators.ts 实测,10 个关卡存在瓦片引用缺失
(地图引用了 DT1 库里不存在的 tile,渲染时会出现空洞):

关卡 缺失率
79 Act 3 - Kurast 1 6.65%
81 Act 3 - Kurast 3 6.43%
80 Act 3 - Kurast 2 6.32%
104 Act 4 - Mesa 1 4.85%
106 Act 4 - Mesa 3 4.84%
105 Act 4 - Mesa 2 3.21%
43 Act 2 - Desert 3 2.05%
117 Act 5 - Barricade Snow 1.11%
112 Act 5 - Barricade 2 1.09%
110 Act 5 - Siege 1 0.83%

与子系统覆盖率的吻合

这份名单与审计报告 T6 的「低命名可追溯子系统」排名高度一致:

关卡族 对应原版子系统 命名可追溯率
Kurast DRLGOUTJUNG_ 0/5 (0%)
Mesa / Barricade / Siege DRLGOUTSIEGE_ 2/7 (28.6%)
Desert DRLGOUTDESR_ 1/11 (9.1%)

[!WARNING]
0% 命名可追溯不等于未实现。src/game/wilderness-jungles.ts(708 行)显然实现了丛林生成,
只是用了本地命名。正确解读是「这些子系统当初不是逐行对照原版写的」。
请不要把这张表当成「这些函数缺失」的证据去照搬实现。

排查路径

  1. 先把缺失的具体 tile 列出来。修改或另写脚本,对 levelId 79 输出所有缺失引用的
    (style, sequence, type) 三元组及出现次数,按频次排序。
    6.65% 通常是少数几种 tile 大量缺失,而不是均匀分散。
  2. 判断是「打包漏了」还是「引用错了」:
    • 若该 tile 确实存在于 MPQ 的某个 DT1 里但没被打进资源包 ⇒ 是 scripts/pack-act-assets.ts
      的 Dt1Mask / File1..File6 处理问题;
    • 若 MPQ 里根本没有该 tile ⇒ 是生成器算出了错误的 (style, sequence) 组合。
  3. 优先查 Dt1Mask。LvlPrest.txt 有 Dt1Mask 列,LvlTypes.txt 有 File1..File32。
    Kurast 这类关卡会混用多个 DT1 库,掩码算错就会整批引用到未加载的库。
  4. Kurast 三关(79/80/81)缺失率几乎相同(6.65 / 6.32 / 6.43),
    说明是共享的结构性问题,一处修复大概率三关同时好转。建议从 79 入手。

修改方案

根因确定前不预设。但请注意:

  • 不要用「静默跳过缺失 tile」来降低指标——这正是 M15 里清理过的那类静默降级;
  • 若确认是打包漏项,修 pack-act-assets.ts 并重新产出资源包,
    同时确认 verify-packs 的 1671 项基线如何受影响;
  • 若确认是生成器算错组合,修生成器,不要去改资源包迁就错误的引用。

验收标准

  • 上述 10 个关卡缺失率降至 0.00%(或给出无法为 0 的充分理由并量化到具体 tile)
  • 在 issue/PR 中贴出缺失 tile 的 (style, sequence, type) 清单与根因结论
  • verify-generators 不引入新的失败项
  • typecheck 0 error / vitest 全绿
  • verify-packs 基线变化(若有)已逐项说明

溯源

审计报告 113c-divergence-audit.md(条目编号与本 issue 标题中的 T 编号对应)
黄金标准 1.13c D2Common.dll,PE 时间戳 0x4B95C439 = 2010-03-09,ImageBase 0x6FD50000
可读代理 D2MOO 源码为 1.10f,非 1.13c。已通过「6/6 常量表命中 + 两个函数反汇编控制流比对」验证其 DRLG 部分与 1.13c 一致,详见报告开头「代理有效性验证」

[!WARNING]
引用 D2MOO 行号时请注意它是 1.10f。涉及 RNG 消耗次数或精确常量的改动,必须回到 1.13c 二进制复核
(工具:scratch/probe_113c_drlg.py、scratch/xref.py、scratch/findbytes.py + objdump)。

## 问题 `npx tsx scripts/verify-generators.ts` 实测,10 个关卡存在瓦片引用缺失 (地图引用了 DT1 库里不存在的 tile,渲染时会出现空洞): | 关卡 | 缺失率 | |---|---| | 79 Act 3 - Kurast 1 | **6.65%** | | 81 Act 3 - Kurast 3 | 6.43% | | 80 Act 3 - Kurast 2 | 6.32% | | 104 Act 4 - Mesa 1 | 4.85% | | 106 Act 4 - Mesa 3 | 4.84% | | 105 Act 4 - Mesa 2 | 3.21% | | 43 Act 2 - Desert 3 | 2.05% | | 117 Act 5 - Barricade Snow | 1.11% | | 112 Act 5 - Barricade 2 | 1.09% | | 110 Act 5 - Siege 1 | 0.83% | ## 与子系统覆盖率的吻合 这份名单与审计报告 T6 的「低命名可追溯子系统」排名高度一致: | 关卡族 | 对应原版子系统 | 命名可追溯率 | |---|---|---| | Kurast | `DRLGOUTJUNG_` | **0/5 (0%)** | | Mesa / Barricade / Siege | `DRLGOUTSIEGE_` | 2/7 (28.6%) | | Desert | `DRLGOUTDESR_` | 1/11 (9.1%) | > [!WARNING] > 0% 命名可追溯**不等于**未实现。`src/game/wilderness-jungles.ts`(708 行)显然实现了丛林生成, > 只是用了本地命名。正确解读是「这些子系统当初不是逐行对照原版写的」。 > 请不要把这张表当成「这些函数缺失」的证据去照搬实现。 ## 排查路径 1. **先把缺失的具体 tile 列出来**。修改或另写脚本,对 levelId 79 输出所有缺失引用的 `(style, sequence, type)` 三元组及出现次数,按频次排序。 6.65% 通常是少数几种 tile 大量缺失,而不是均匀分散。 2. **判断是「打包漏了」还是「引用错了」**: - 若该 tile 确实存在于 MPQ 的某个 DT1 里但没被打进资源包 ⇒ 是 `scripts/pack-act-assets.ts` 的 `Dt1Mask` / `File1..File6` 处理问题; - 若 MPQ 里根本没有该 tile ⇒ 是生成器算出了错误的 `(style, sequence)` 组合。 3. **优先查 `Dt1Mask`**。`LvlPrest.txt` 有 `Dt1Mask` 列,`LvlTypes.txt` 有 `File1..File32`。 Kurast 这类关卡会混用多个 DT1 库,掩码算错就会整批引用到未加载的库。 4. Kurast 三关(79/80/81)缺失率几乎相同(6.65 / 6.32 / 6.43), 说明是**共享的结构性问题**,一处修复大概率三关同时好转。建议从 79 入手。 ## 修改方案 根因确定前不预设。但请注意: - **不要**用「静默跳过缺失 tile」来降低指标——这正是 M15 里清理过的那类静默降级; - 若确认是打包漏项,修 `pack-act-assets.ts` 并重新产出资源包, 同时确认 `verify-packs` 的 1671 项基线如何受影响; - 若确认是生成器算错组合,修生成器,**不要**去改资源包迁就错误的引用。 ## 验收标准 - [ ] 上述 10 个关卡缺失率降至 0.00%(或给出无法为 0 的充分理由并量化到具体 tile) - [ ] 在 issue/PR 中贴出缺失 tile 的 `(style, sequence, type)` 清单与根因结论 - [ ] `verify-generators` 不引入新的失败项 - [ ] typecheck 0 error / vitest 全绿 - [ ] `verify-packs` 基线变化(若有)已逐项说明 --- ### 溯源 | | | |---|---| | 审计报告 | `113c-divergence-audit.md`(条目编号与本 issue 标题中的 T 编号对应) | | 黄金标准 | 1.13c `D2Common.dll`,PE 时间戳 `0x4B95C439` = 2010-03-09,ImageBase `0x6FD50000` | | 可读代理 | D2MOO 源码为 **1.10f**,非 1.13c。已通过「6/6 常量表命中 + 两个函数反汇编控制流比对」验证其 DRLG 部分与 1.13c 一致,详见报告开头「代理有效性验证」 | > [!WARNING] > 引用 D2MOO 行号时请注意它是 1.10f。涉及 RNG 消耗次数或精确常量的改动,**必须**回到 1.13c 二进制复核 > (工具:`scratch/probe_113c_drlg.py`、`scratch/xref.py`、`scratch/findbytes.py` + `objdump`)。
troytt added this to the [M17] 核心地下城与碰撞层原版一致性 (1.13c) milestone 2026-09-18 09:47:28 +00:00
Author
Owner

与 #86 的关系

本 issue 的 4 个关卡族与 #86(子系统精读审计)的前两个目标完全重叠:

本 issue 的关卡 对应子系统 #86 中的优先级
79/80/81 Kurast DRLGOUTJUNG_(0% 可追溯) 第 2 位
43 Desert 3 DRLGOUTDESR_(9.1%) 第 1 位
104/105/106 Mesa、110/112/117 Siege/Barricade DRLGOUTSIEGE_(28.6%) —

建议:若 #86 已经在做 DRLGOUTDESR_ / DRLGOUTJUNG_ 的精读,
本 issue 的根因定位可以直接复用其结论,不要重复调查。

反过来,若本 issue 先动手,请把「缺失 tile 的 (style, sequence, type) 清单」
同步到 #86,那是判断子系统是否忠实还原的一手证据。

相关:#86

### 与 #86 的关系 本 issue 的 4 个关卡族与 #86(子系统精读审计)的前两个目标完全重叠: | 本 issue 的关卡 | 对应子系统 | #86 中的优先级 | |---|---|---| | 79/80/81 Kurast | `DRLGOUTJUNG_`(0% 可追溯) | 第 2 位 | | 43 Desert 3 | `DRLGOUTDESR_`(9.1%) | 第 1 位 | | 104/105/106 Mesa、110/112/117 Siege/Barricade | `DRLGOUTSIEGE_`(28.6%) | — | **建议**:若 #86 已经在做 `DRLGOUTDESR_` / `DRLGOUTJUNG_` 的精读, 本 issue 的根因定位可以直接复用其结论,不要重复调查。 反过来,若本 issue 先动手,请把「缺失 tile 的 `(style, sequence, type)` 清单」 同步到 #86,那是判断子系统是否忠实还原的一手证据。 相关:#86
Author
Owner

修复总结 (Issue #83: 修复库拉斯特/梅萨/沙漠/关卡路障的缺失图块引用)

已完成跨幕荒野关卡真实 DT1 瓦片引用的对齐与缺失瓦片修复,全量 31/31 荒野关卡缺失瓦片比例已 100% 降至 0.00%。

1. 根因分析与代码实现

  1. 沙漠 (Desert 43) 与路障 (Barricade 112, 117) 跨幕瓦片污染:
    • 根因: src/game/wilderness.ts 中的 applyGroundTransitions 包含 Act 1 专用的泥路 (DIRT_PATH_TILE_LUT) 与水岸 (WATER_EDGE_TILE_LUT) 查表逻辑。当在非 Act 1 荒野关卡执行时,误将沙漠与雪地地砖覆盖为 Act 1 的水边瓦片 (style: 2) 与土路过渡瓦片 (style: 0)。
    • 修复: 在 applyGroundTransitions 中添加 levelTypeName 守卫,限制仅在 Act 1 - Wilderness 执行,其他关卡直接返回 0,消除跨幕误放瓦片。
  2. 库拉斯特市区 (Kurast 79, 80, 81) 大街与桥梁样式对齐:
    • 根因: src/game/wilderness-acts.ts 中的 initAct3OutdoorLevel 与 stampAct3KurastCauseway 原先硬编码了 style: 5(地砖)与 style: 1(矮墙)。而 Kurast 的 DT1 库(Floors.dt1 与 Walls.dt1)中并不存在 style 5 / 1。
    • 修复: 查询 Kurast 真实 DT1 库,石板大街与桥梁的标准地砖为 Floors.dt1 中的 style: 20 seq: 0 prop1: 194;石运河左/右堤岸矮墙为 Walls.dt1 中的 style: 20 seq: 0 type: 1 / 2 prop1: 129。
  3. 梅萨高原 (Mesa 104, 105, 106) 峡谷地表区分:
    • 根因: initAct4OutdoorLevel 原先将峡谷地表无条件设为 makeFloor(20, 3, 2)(熔岩 boiling lava,仅存在于 Act 4 - Lava 的 Floor.dt1)。Mesa 关卡最高 style 仅到 18,导致每关产生 180~270 个缺失瓦片。
    • 修复: 根据 request.levelTypeName === 'Act 4 - Lava' 动态区分:Lava 保持 style 20 seq 3,Mesa 则使用 Mesa Floor.dt1 实际存在的 style 10 seq 0 坚石地表。
  4. 攻城战场 (Siege 110) 木桩栅栏与营地残垣:
    • 根因: src/game/wilderness-siege.ts 使用了 fence.dt1 不存在的 sequence 与 style。
    • 修复: 将木栅栏规范为 style: 2 seq: 10 type: 1,破损木桩规范为 style: 2 seq: 2 type: 7,投石台矮墙设为 style: 2 seq: 10 prop1: 2 type: 1,废墟地砖与残垣更新为 camp.dt1 的 style: 18。

2. 验证门禁结果

  • scripts/verify-generators.ts(全量 101 关卡):
    • 荒野关卡 31/31 全部通过,包含 43, 79, 80, 81, 104, 105, 106, 110, 112, 117 在内的所有关卡瓦片缺失率全部清零 (0.00%)。
  • npm run typecheck: 0 错误。
  • npx vitest run: 84 passed, 1 skipped (85 文件), 1345 passed, 2 skipped。
  • npx tsx scripts/verify-packs.ts: 1671/1671 项断言通过。

3. 关联提交

  • ef93808 fix(tiles): 修复 Kurast / Mesa / Desert / Barricade 的瓦片引用缺失 (Issue #83)
  • e3a8eb6 test(tiles): 同步 Kurast/Mesa/Desert/Barricade 真实 DT1 瓦片断言 (Issue #83)
### 修复总结 (Issue #83: 修复库拉斯特/梅萨/沙漠/关卡路障的缺失图块引用) 已完成跨幕荒野关卡真实 DT1 瓦片引用的对齐与缺失瓦片修复,全量 31/31 荒野关卡缺失瓦片比例已 100% 降至 **0.00%**。 #### 1. 根因分析与代码实现 1. **沙漠 (Desert 43) 与路障 (Barricade 112, 117) 跨幕瓦片污染:** - **根因:** `src/game/wilderness.ts` 中的 `applyGroundTransitions` 包含 Act 1 专用的泥路 (`DIRT_PATH_TILE_LUT`) 与水岸 (`WATER_EDGE_TILE_LUT`) 查表逻辑。当在非 Act 1 荒野关卡执行时,误将沙漠与雪地地砖覆盖为 Act 1 的水边瓦片 (`style: 2`) 与土路过渡瓦片 (`style: 0`)。 - **修复:** 在 `applyGroundTransitions` 中添加 levelTypeName 守卫,限制仅在 `Act 1 - Wilderness` 执行,其他关卡直接返回 0,消除跨幕误放瓦片。 2. **库拉斯特市区 (Kurast 79, 80, 81) 大街与桥梁样式对齐:** - **根因:** `src/game/wilderness-acts.ts` 中的 `initAct3OutdoorLevel` 与 `stampAct3KurastCauseway` 原先硬编码了 `style: 5`(地砖)与 `style: 1`(矮墙)。而 Kurast 的 DT1 库(`Floors.dt1` 与 `Walls.dt1`)中并不存在 style 5 / 1。 - **修复:** 查询 Kurast 真实 DT1 库,石板大街与桥梁的标准地砖为 `Floors.dt1` 中的 `style: 20 seq: 0 prop1: 194`;石运河左/右堤岸矮墙为 `Walls.dt1` 中的 `style: 20 seq: 0 type: 1 / 2 prop1: 129`。 3. **梅萨高原 (Mesa 104, 105, 106) 峡谷地表区分:** - **根因:** `initAct4OutdoorLevel` 原先将峡谷地表无条件设为 `makeFloor(20, 3, 2)`(熔岩 boiling lava,仅存在于 `Act 4 - Lava` 的 `Floor.dt1`)。Mesa 关卡最高 style 仅到 18,导致每关产生 180~270 个缺失瓦片。 - **修复:** 根据 `request.levelTypeName === 'Act 4 - Lava'` 动态区分:Lava 保持 style 20 seq 3,Mesa 则使用 Mesa `Floor.dt1` 实际存在的 style 10 seq 0 坚石地表。 4. **攻城战场 (Siege 110) 木桩栅栏与营地残垣:** - **根因:** `src/game/wilderness-siege.ts` 使用了 `fence.dt1` 不存在的 sequence 与 style。 - **修复:** 将木栅栏规范为 `style: 2 seq: 10 type: 1`,破损木桩规范为 `style: 2 seq: 2 type: 7`,投石台矮墙设为 `style: 2 seq: 10 prop1: 2 type: 1`,废墟地砖与残垣更新为 `camp.dt1` 的 `style: 18`。 #### 2. 验证门禁结果 - **`scripts/verify-generators.ts`(全量 101 关卡):** - **荒野关卡 31/31 全部通过**,包含 43, 79, 80, 81, 104, 105, 106, 110, 112, 117 在内的所有关卡瓦片缺失率全部清零 (0.00%)。 - **`npm run typecheck`:** 0 错误。 - **`npx vitest run`:** 84 passed, 1 skipped (85 文件), 1345 passed, 2 skipped。 - **`npx tsx scripts/verify-packs.ts`:** 1671/1671 项断言通过。 #### 3. 关联提交 - `ef93808` `fix(tiles): 修复 Kurast / Mesa / Desert / Barricade 的瓦片引用缺失 (Issue #83)` - `e3a8eb6` `test(tiles): 同步 Kurast/Mesa/Desert/Barricade 真实 DT1 瓦片断言 (Issue #83)`
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#83
No description provided.