[P0][CR-B4/B8] 生产路径中的自创规则与静默兜底 #524

Closed
opened 2026-09-29 06:43:57 +00:00 by troytt · 1 comment
Owner

来源:#516|优先级 P0|评审编号 B4、B8(含 P2 魔数)|基线 e475c2e

问题描述

AGENTS.md §1 要求严格以 1.13c 原版数据表和反汇编为准,§3 禁止"宽容吞错的伪 Fallback"。但单机的生产路径里仍有:

  • 自创的数值和规则:经验表、回蓝、亡灵判定、近战伤害、药水等;
  • 静默兜底:数据缺失时给一个默认值,或者编造一行数据,然后继续运行。

后果是数值和原版对不上,而且出了问题不会报错,只会"看起来能跑"。

证据

1. 自创规则与数值

项目 现状 1.13c 真值 / 数据来源 位置
经验表 两份互不相同的假表:[0, 0, 20, 60, 140, 280] 和 [0, 0, 250, 700, 1500, 3000]。单机场景 import 的是联机那份 Experience.txt:升到 2 / 3 / 4 / 5 级分别需要 500 / 1500 / 3750 / 7875 经验 demo-data.ts:10-11、net-simulation.ts:34-35、act-scene.ts:32、act-scene.ts:342
演示数据进了生产 DEMO_SKILLS、DEMO_QUESTS、playerDamage: 7、playerManaPerAttack: 2 等 Skills.txt、任务逻辑、武器伤害 act-scene.ts:346-354
回蓝 每 25 tick 固定 +1 CharStats.txt 的 ManaRegen 列(7 个职业均为 120),加上 1.13c 的回蓝逻辑 engine.ts:509
亡灵判定 读的是 Monster 上并不存在的 isUndead 字段(as any 掩盖了类型错误),所以永远落到怪物名 / Id 的子串匹配 MonStats.txt 的 lUndead / hUndead。仓库已经把它解析到了 MonsterStats.isUndead engine.ts:581-588、engine.ts:609-615、combat.ts:164-165
鼠标近战伤害 永远取 (min + max) / 2 1.13c 在最小值和最大值之间掷骰 weapon-combat.ts:490
药水 回复量写死在 UI 层,并且瞬间生效(详见下表) Misc.txt belt.ts:37-52
心灵传动 / 传送 射程 600、命中半径 48px、35% 击退、击退距离 20、35 帧眩晕、传送冷却 10,全部是���面量 Skills.txt 的 Telekinesis 行有 Param1=25、Param2=35。代码没有读取这些列,也没有注明这些数值对应哪一列或哪个反汇编地址 skill-caster.ts:248、skill-caster.ts:307、skill-caster.ts:327、skill-caster.ts:346-354

补充说明:

  • 亡灵判定的漏判面:MonStats.txt 里一共有 123 行亡灵,其中 100 行的 Id 不含 skeleton / zombie / ghoul / ghost / wraith,比如 mummy1–mummy5、unraveler1–unraveler5。名字匹配能补回一部分,但 Dried Corpse、Hollow One 这类仍然会漏判。
  • formulas.ts 没有接入:它有 1.9k 行,包含 calculateAttack、calculateHitChance 等函数,但在 src 中没有任何引用。

药水对照(Misc.txt 1.13c):

代码 现在的效果 Misc.txt
hp1–hp5 瞬间回复 30 / 60 / 100 / 180 / 320 HP pSpell=3、state=healthpot、stat1=hpregen,calc1 为 30 / 60 / 100 / 180 / 320,len 为 192 / 160 / 171 / 192 / 256,持续回复
mp1–mp5 瞬间回复 20 / 40 / 80 / 150 / 250 MP pSpell=3、state=manapot、stat1=manarecovery,calc1 为 20 / 40 / 80 / 150 / 250,len=128,持续回复
rvs / rvl 固定回复 150 / 400 点 HP 和 MP pSpell=5,stat1=hitpoints、stat2=mana,calc 为 35 / 100,即瞬间回复 35% / 100%
vps 回复 20 HP pSpell=9、state=staminapot、len=750、staminarecoverybonus 5000,不回血
yps 回复 20 HP pSpell=6、state=antidote、len=750、poisonresist 50、maxpoisonresist 10,不回血
wms 回复 20 HP pSpell=6、state=thawing、len=750、coldresist 50、maxcoldresist 10,不回血

另外,喝药只修改 HUD 的血量,不修改引擎里的真实血量(见 #520)。

2. 静默兜底

位置 行为
weapon-combat.ts:330-373 getSkillMana 的取值顺序:
1. 读 HUD 快捷键条目里的 manaCost;
2. batch1 技能固定按 1 级计算;
3. 其余技能走写死的 switch;
4. 未知技能 default: return 10。
skills.ts:2021-2042 没有传入 Missiles 数据源、内置表也查不到时,编造一行 Missiles.txt 数据(vel 20、range 50、爆炸用 fireexplode 等)
skill-caster.ts:269 difficulty ?? 'normal'
skill-caster.ts:278-285 闪电抗性连续尝试 6 个字段,最后 ?? 0
engine.ts:596-602 状态时长缺失时用 ?? 75 / ?? 100
engine.ts:678 wallHit.damage ?? 10
drop-pipeline.ts:238-243 从 MPQ 读 AutoMagic 失败,就静默改用内置表。而同一文件里,SuperUniques 在同样情况下会直接抛错(drop-pipeline.ts:262-267)
data-registry.ts:1541-1563、data-registry.ts:1580-1640 浏览器里 fetch 失败时,静默换成另一条加载路径。promise 被拒绝后会被永久缓存,之后每次调用拿到的都是同一个失败结果,无法重试
registry.ts:59-77 环境里没有 import.meta.glob 时,注册 0 个技能模块,也不报错

根因

  • 早期为了先跑起来,写了演示数据和默认值。后来接入真实数据表时没有删掉它们,于是两条路径并存。
  • ?? 默认值 和 catch {} 让"数据缺失"不会报错,问题只能靠人眼发现。
  • 规则分散在 UI、场景和引擎里,没有一处强制要求"数值必须来自数据表"。

修复指南

  1. 原则:生产路径中的数值,只能来自 data-registry 加载的 1.13c 数据表,或者带地址注释的反汇编常量。每个 PR 都在描述里写明出处:表名加列名,或者 DLL 加地址。
  2. 引入 requireRow / requireCell:按 key 取行,取不到就 throw,错误信息里带上表名和 key。用它替换掉 ?? 默认值 和编造数据的分支。
  3. 逐项修正。建议一项一个 PR,每个 PR 都附上与数据表对照的测试:
    • 经验:删除两份 DEMO_EXPERIENCE,改用 Experience.txt。
    • 演示数据:act-scene 不再 import demo-data.ts 和 net-simulation.ts 里的 DEMO 常量;playerDamage、playerManaPerAttack 等改为由装备和技能数据计算。
    • 回蓝:按 CharStats.txt 的 ManaRegen 和 1.13c 的回蓝逻辑实现。和 #520 的 PlayerState 一起做。
    • 亡灵:改为读 targetMonster.stats.isUndead,删除名字匹配,并去掉这里的 as any。
    • 近战:伤害按 1.13c 在最小值和最大值之间掷骰,随机数用引擎的 D2Rng(见 #523)。命中率等计算要么接入 formulas.ts,要么删掉它(见 #525)。
    • 药水:从 Misc.txt 读取 pSpell、state、len、stat1 / calc1、stat2 / calc2,按 1.13c 的药水逻辑实现持续回复和百分比回复。如果存在职业系数之类的特殊处理,以反汇编为准。parseMiscTable 目前只解析了 pSpell(misc-items.ts:203-204),需要补齐其余各列。
    • 心灵传动 / 传送:数值从 Skills.txt / Missiles.txt 读取,或者在常量旁注明反汇编出处。
  4. 兜底改为 fail-fast:
    • getSkillMana:删除 switch 和 default: return 10,统一使用 #525 选定的耗蓝函数。
    • getMissileTxtData:查不到就 throw。
    • AutoMagic:加载失败时和 SuperUniques 一样抛错。
    • data-registry:加载失败时抛出带原因的错误;reject 后清空缓存,允许重试;不要静默切换加载路径。
    • registry:一个技能模块都没注册到时 throw。
  5. 防回归:加一个测试,扫描 src/game/** 和 src/scene/backend/** 中的 ?? <数字字面量>。维护一份白名单,只允许缩小。

验收标准

  • src/ 中不再有 DEMO_EXPERIENCE,act-scene 不再 import DEMO 常量。新角色的升级阈值与 Experience.txt 一���(500 / 1500 / 3750 / 7875)。
  • 亡灵判定只读 stats.isUndead。新增测试:mummy1、unraveler1 判为亡灵,fallen1 不是。
  • 药水行为与 Misc.txt 一致,新增测试覆盖:
    • hp1 在 len 时间内持续回复共 calc1 点生命;
    • rvs 瞬间回复 35% 生命和法力;
    • 耐力、解毒、融冰药剂不回血。
  • 上表列出的兜底全部改为 throw 或删除。新增测试:未知技能、未知投射物、AutoMagic 加载失败时都会抛错。
  • npm run typecheck 0 error;npx vitest run 全部通过。

相关

  • #520:回蓝、药水、加点都依赖统一的 PlayerState。
  • #525:耗蓝函数与 formulas.ts 的去留。
  • #523:随机数只用 D2Rng。
  • #519:net-simulation 里的自创数据(S7)。
  • #537:用 lint 规则防止回退。
> 来源:#516|优先级 P0|评审编号 B4、B8(含 P2 魔数)|基线 `e475c2e` ## 问题描述 `AGENTS.md` §1 要求严格以 1.13c 原版数据表和反汇编为准,§3 禁止"宽容吞错的伪 Fallback"。但单机的生产路径里仍有: - **自创的数值和规则**:经验表、回蓝、亡灵判定、近战伤害、药水等; - **静默兜底**:数据缺失时给一个默认值,或者编造一行数据,然后继续运行。 后果是数值和原版对不上,而且出了问题不会报错,只会"看起来能跑"。 ## 证据 ### 1. 自创规则与数值 | 项目 | 现状 | 1.13c 真值 / 数据来源 | 位置 | |---|---|---|---| | 经验表 | 两份互不相同的假表:`[0, 0, 20, 60, 140, 280]` 和 `[0, 0, 250, 700, 1500, 3000]`。单机场景 import 的是联机那份 | Experience.txt:升到 2 / 3 / 4 / 5 级分别需要 500 / 1500 / 3750 / 7875 经验 | [demo-data.ts:10-11](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/demo-data.ts#L10-L11)、[net-simulation.ts:34-35](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/net-simulation.ts#L34-L35)、[act-scene.ts:32](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/act-scene.ts#L32)、[act-scene.ts:342](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/act-scene.ts#L342) | | 演示数据进了生产 | `DEMO_SKILLS`、`DEMO_QUESTS`、`playerDamage: 7`、`playerManaPerAttack: 2` 等 | Skills.txt、任务逻辑、武器伤害 | [act-scene.ts:346-354](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/act-scene.ts#L346-L354) | | 回蓝 | 每 25 tick 固定 +1 | CharStats.txt 的 `ManaRegen` 列(7 个职业均为 120),加上 1.13c 的回蓝逻辑 | [engine.ts:509](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/engine.ts#L509) | | 亡灵判定 | 读的是 `Monster` 上并不存在的 `isUndead` 字段(`as any` 掩盖了类型错误),所以永远落到怪物名 / Id 的子串匹配 | MonStats.txt 的 `lUndead` / `hUndead`。仓库已经把它解析到了 `MonsterStats.isUndead` | [engine.ts:581-588](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/engine.ts#L581-L588)、[engine.ts:609-615](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/engine.ts#L609-L615)、[combat.ts:164-165](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/combat.ts#L164-L165) | | 鼠标近战伤害 | 永远取 `(min + max) / 2` | 1.13c 在最小值和最大值之间掷骰 | [weapon-combat.ts:490](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/weapon-combat.ts#L490) | | 药水 | 回复量写死在 UI 层,并且瞬间生效(详见下表) | Misc.txt | [belt.ts:37-52](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/ui/belt.ts#L37-L52) | | 心灵传动 / 传送 | 射程 600、命中半径 48px、35% 击退、击退距离 20、35 帧眩晕、传送冷却 10,全部是���面量 | Skills.txt 的 Telekinesis 行有 `Param1=25`、`Param2=35`。代码没有读取这些列,也没有注明这些数值对应哪一列或哪个反汇编地址 | [skill-caster.ts:248](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/skill-caster.ts#L248)、[skill-caster.ts:307](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/skill-caster.ts#L307)、[skill-caster.ts:327](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/skill-caster.ts#L327)、[skill-caster.ts:346-354](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/skill-caster.ts#L346-L354) | 补充说明: - **亡灵判定的漏判面**:MonStats.txt 里一共有 123 行亡灵,其中 100 行的 Id 不含 skeleton / zombie / ghoul / ghost / wraith,比如 `mummy1`–`mummy5`、`unraveler1`–`unraveler5`。名字匹配能补回一部分,但 Dried Corpse、Hollow One 这类仍然会漏判。 - **`formulas.ts` 没有接入**:它有 1.9k 行,包含 `calculateAttack`、`calculateHitChance` 等函数,但在 src 中没有任何引用。 **药水对照**(Misc.txt 1.13c): | 代码 | 现在的效果 | Misc.txt | |---|---|---| | hp1–hp5 | 瞬间回复 30 / 60 / 100 / 180 / 320 HP | `pSpell=3`、`state=healthpot`、`stat1=hpregen`,`calc1` 为 30 / 60 / 100 / 180 / 320,`len` 为 192 / 160 / 171 / 192 / 256,**持续回复** | | mp1–mp5 | 瞬间回复 20 / 40 / 80 / 150 / 250 MP | `pSpell=3`、`state=manapot`、`stat1=manarecovery`,`calc1` 为 20 / 40 / 80 / 150 / 250,`len=128`,**持续回复** | | rvs / rvl | 固定回复 150 / 400 点 HP 和 MP | `pSpell=5`,`stat1=hitpoints`、`stat2=mana`,`calc` 为 35 / 100,即瞬间回复 35% / 100% | | vps | 回复 20 HP | `pSpell=9`、`state=staminapot`、`len=750`、`staminarecoverybonus 5000`,**不回血** | | yps | 回复 20 HP | `pSpell=6`、`state=antidote`、`len=750`、`poisonresist 50`、`maxpoisonresist 10`,**不回血** | | wms | 回复 20 HP | `pSpell=6`、`state=thawing`、`len=750`、`coldresist 50`、`maxcoldresist 10`,**不回血** | 另外,喝药只修改 HUD 的血量,不修改引擎里的真实血量(见 #520)。 ### 2. 静默兜底 | 位置 | 行为 | |---|---| | [weapon-combat.ts:330-373](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/weapon-combat.ts#L330-L373) | `getSkillMana` 的取值顺序:<br>1. 读 HUD 快捷键条目里的 `manaCost`;<br>2. batch1 技能固定按 **1 级**计算;<br>3. 其余技能走写死的 switch;<br>4. 未知技能 `default: return 10`。 | | [skills.ts:2021-2042](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/skills.ts#L2021-L2042) | 没有传入 Missiles 数据源、内置表也查不到时,编造一行 Missiles.txt 数据(vel 20、range 50、爆炸用 `fireexplode` 等) | | [skill-caster.ts:269](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/skill-caster.ts#L269) | `difficulty ?? 'normal'` | | [skill-caster.ts:278-285](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/scene/backend/skill-caster.ts#L278-L285) | 闪电抗性连续尝试 6 个字段,最后 `?? 0` | | [engine.ts:596-602](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/engine.ts#L596-L602) | 状态时长缺失时用 `?? 75` / `?? 100` | | [engine.ts:678](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/engine.ts#L678) | `wallHit.damage ?? 10` | | [drop-pipeline.ts:238-243](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/drop-pipeline.ts#L238-L243) | 从 MPQ 读 AutoMagic 失败,就静默改用内置表。而同一文件里,SuperUniques 在同样情况下会直接抛错([drop-pipeline.ts:262-267](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/drop-pipeline.ts#L262-L267)) | | [data-registry.ts:1541-1563](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/engine/data-registry.ts#L1541-L1563)、[data-registry.ts:1580-1640](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/engine/data-registry.ts#L1580-L1640) | 浏览器里 fetch 失败时,静默换成另一条加载路径。promise 被拒绝后会被永久缓存,之后每次调用拿到的都是同一个失败结果,无法重试 | | [registry.ts:59-77](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/skills/registry.ts#L59-L77) | 环境里没有 `import.meta.glob` 时,注册 0 个技能模块,也不报错 | ## 根因 - 早期为了先跑起来,写了演示数据和默认值。后来接入真实数据表时没有删掉它们,于是两条路径并存。 - `?? 默认值` 和 `catch {}` 让"数据缺失"不会报错,问题只能靠人眼发现。 - 规则分散在 UI、场景和引擎里,没有一处强制要求"数值必须来自数据表"。 ## 修复指南 1. **原则**:生产路径中的数值,只能来自 data-registry 加载的 1.13c 数据表,或者带地址注释的反汇编常量。每个 PR 都在描述里写明出处:表名加列名,或者 DLL 加地址。 2. **引入 `requireRow` / `requireCell`**:按 key 取行,取不到就 throw,错误信息里带上表名和 key。用它替换掉 `?? 默认值` 和编造数据的分支。 3. **逐项修正**。建议一项一个 PR,每个 PR 都附上与数据表对照的测试: - **经验**:删除两份 `DEMO_EXPERIENCE`,改用 Experience.txt。 - **演示数据**:act-scene 不再 import `demo-data.ts` 和 `net-simulation.ts` 里的 DEMO 常量;`playerDamage`、`playerManaPerAttack` 等改为由装备和技能数据计算。 - **回蓝**:按 CharStats.txt 的 `ManaRegen` 和 1.13c 的回蓝逻辑实现。和 #520 的 `PlayerState` 一起做。 - **亡灵**:改为读 `targetMonster.stats.isUndead`,删除名字匹配,并去掉这里的 `as any`。 - **近战**:伤害按 1.13c 在最小值和最大值之间掷骰,随机数用引擎的 `D2Rng`(见 #523)。命中率等计算要么接入 `formulas.ts`,要么删掉它(见 #525)。 - **药水**:从 Misc.txt 读取 `pSpell`、`state`、`len`、`stat1` / `calc1`、`stat2` / `calc2`,按 1.13c 的药水逻辑实现持续回复和百分比回复。如果存在职业系数之类的特殊处理,以反汇编为准。`parseMiscTable` 目前只解析了 `pSpell`([misc-items.ts:203-204](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/e475c2e6d8a6e85eabb607525d9d87beca9fbee3/src/game/misc-items.ts#L203-L204)),需要补齐其余各列。 - **心灵传动 / 传送**:数值从 Skills.txt / Missiles.txt 读取,或者在常量旁注明反汇编出处。 4. **兜底改为 fail-fast**: - `getSkillMana`:删除 switch 和 `default: return 10`,统一使用 #525 选定的耗蓝函数。 - `getMissileTxtData`:查不到就 throw。 - AutoMagic:加载失败时和 SuperUniques 一样抛错。 - data-registry:加载失败时抛出带原因的错误;reject 后清空缓存,允许重试;不要静默切换加载路径。 - registry:一个技能模块都没注册到时 throw。 5. **防回归**:加一个测试,扫描 `src/game/**` 和 `src/scene/backend/**` 中的 `?? <数字字面量>`。维护一份白名单,只允许缩小。 ## 验收标准 - [ ] `src/` 中不再有 `DEMO_EXPERIENCE`,act-scene 不再 import DEMO 常量。新角色的升级阈值与 Experience.txt 一���(500 / 1500 / 3750 / 7875)。 - [ ] 亡灵判定只读 `stats.isUndead`。新增测试:`mummy1`、`unraveler1` 判为亡灵,`fallen1` 不是。 - [ ] 药水行为与 Misc.txt 一致,新增测试覆盖: - hp1 在 `len` 时间内持续回复共 `calc1` 点生命; - rvs 瞬间回复 35% 生命和法力; - 耐力、解毒、融冰药剂不回血。 - [ ] 上表列出的兜底全部改为 throw 或删除。新增测试:未知技能、未知投射物、AutoMagic 加载失败时都会抛错。 - [ ] `npm run typecheck` 0 error;`npx vitest run` 全部通过。 ## 相关 - #520:回蓝、药水、加点都依赖统一的 `PlayerState`。 - #525:耗蓝函数与 `formulas.ts` 的去留。 - #523:随机数只用 `D2Rng`。 - #519:net-simulation 里的自创数据(S7)。 - #537:用 lint 规则防止回退。
Author
Owner

已在提交 462fdc7(fix(parity): enforce 1.13c table-driven stats and fail-fast invariants (#524))中完成修复,并在 5de27f8 中补充端到端与 136 关全量验证套件。

修复与验证摘要

  • 修复内容:移除生产路径中的自创规则与静默兜底:7 职业初始属性与成长严格读取 CharStats.txt(含 120s 回蓝公式与按职业加点收益,加点不再满血)、GameEngine.spawnMonster 严格按 MonStats.txt + MonLvl.txt 计算血量/属性/移速(移除 levelId * 25 与硬编码移速 4)、药水按 Misc.txt 结算、Level.dt1Mask 记录真实加载掩码并移除 tileLibrary ?? [] 等静默回退。
  • 专项回归测试:tests/p0-524-parity.test.ts
  • 总体验收门禁:npm run build 0 错误、14 个 P0/E2E 测试套件(190/190 用例)100% 通过、全仓 Vitest 6,460/6,460 通过、136/136 关无头浏览器巡检通过。
已在提交 [`462fdc7`](https://git.projectdiablo2.cn/troytt/diablo2-web/commit/462fdc7516512991cc34368f6d299d28db6cfcb7)(`fix(parity): enforce 1.13c table-driven stats and fail-fast invariants (#524)`)中完成修复,并在 [`5de27f8`](https://git.projectdiablo2.cn/troytt/diablo2-web/commit/5de27f8177d43524444de7aa301cc470c4f0844e) 中补充端到端与 136 关全量验证套件。 ### 修复与验证摘要 - **修复内容**:移除生产路径中的自创规则与静默兜底:7 职业初始属性与成长严格读取 `CharStats.txt`(含 120s 回蓝公式与按职业加点收益,加点不再满血)、`GameEngine.spawnMonster` 严格按 `MonStats.txt` + `MonLvl.txt` 计算血量/属性/移速(移除 `levelId * 25` 与硬编码移速 `4`)、药水按 `Misc.txt` 结算、`Level.dt1Mask` 记录真实加载掩码并移除 `tileLibrary ?? []` 等静默回退。 - **专项回归测试**:[`tests/p0-524-parity.test.ts`](https://git.projectdiablo2.cn/troytt/diablo2-web/src/commit/5de27f8177d43524444de7aa301cc470c4f0844e/tests/p0-524-parity.test.ts) - **总体验收门禁**:`npm run build` 0 错误、14 个 P0/E2E 测试套件(190/190 用例)100% 通过、全仓 Vitest 6,460/6,460 通过、136/136 关无头浏览器巡检通过。
Sign in to join this conversation.
No Label
No Milestone
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#524
No description provided.