元素效果阶段长度与父词缀清理
2026-09-20。本批补齐父词缀倒计时 / 退场控制内核,并纠正先前对 active effect 遍历长度的推断。尚不代表元素词缀已经接通正式 HIT、全部子效果派发与实际析构。
纠正:每阶段固定长度,不是动态追随尾部
原 566DD0 的汇编:
- 566EA7 读取当前表 size;566EAC 把它存入 var_180。
- 569B37 让循环下标与该长度快照比较,而不是与最新 size 比较。
- 569B51 比较阶段编号 2:先 PASSIVE,后 DYNAMIC;TRANSFER 不进入这两阶段。
结果:
| 在哪个阶段追加 | 加到哪个表 | 该帧是否遍历新项 |
|---|---|---|
| PASSIVE | PASSIVE | 否 |
| PASSIVE | DYNAMIC | 是,后者开始时重新取长度 |
| DYNAMIC | PASSIVE | 否 |
| DYNAMIC | DYNAMIC | 否 |
遍历读取的是当时槽位 / 效果状态,固定的是该阶段的次数,不是把所有效果对象复制成不可变快照。同帧追加的效果仍能进入随后单独执行的属性缓存重算。
此前文档中“动态列表同帧继续遍历追加项”的表述不正确,已同步纠正到 devlog 和 wiki 的 native-elemental-lifecycle 文档。不能因此把现有固定长度循环贸然改成追随增长的 while。
新增 NativeEffectTraversal 保留每阶段长度捕获、elapsed 的 x87 运算及分派前到期标记。NativeEffectFrame 已使用这个共同遍历器;它当前仍只有已表示的动态池,完整元素状态的 PASSIVE / DYNAMIC 实例表尚需绑定。
父词缀有独立倒计时
7A3E00 操作 CAffix+0xAC:
- 负数直接保持存活;管理器在此前单独处理 -900。
- 非负时减 delta,保存 float。
- 用仍保留的 x87 结果判断 > 0;否则将字段写成正零,报告到期。
它只改父词缀剩余时间,不会改子效果 duration 或 elapsed。不能用上一批 SetDuration 来推进父时钟,否则会错误地每帧改写子效果总时长。
正常有 level 的角色更新顺序由以下地址确定:
- 56B0F5:active effect 阶段。
- 56B118 / 56B12B:属性重算和单条 effect 清理。
- 56B151:健康回复 / DoT。
- 56B8E1 → 51521F → 7B0420:父词缀倒计时和清理,随后处理主题集合。
- 56B92D:CStatManager 时钟。
无 level 的早退分支 56A46E 也调用 5150E0,但跳过上面的健康和普通更新路径。不能拿文本中第一次出现的函数名来判断完整控制流。
7B0420 的清理规则
NativeAffixCleanup 对应这一管理器控制流程:
- -900 词缀立即析构,不经过普通倒计时分支。
- 其他负时长保持不变;有限时间按父时钟到期。
- 先调用析构宿主;宿主负责解绑和销毁实际子效果。
- 再清槽并使用末项补位,下一次检查补位到当前位置的词缀。
- 最后始终重算数值属性,包括空管理器。
- 描述缓存失效是另一条规则,不能当作数值重算是否执行的条件。
描述失效还会检查后面的同名存活项:使用相同来源技能 GUID,或者当前词缀没有来源技能时的首个子效果 GUID2。同名替代只影响描述失效,不阻止旧词缀析构。
原代码的描述 dirty 是一个会被后续判断覆盖的布尔值,不是“本帧任何删除都置脏”的累积 OR。实现与对照都保留此行为。
旧符号名的陷阱
IDA 中名为 CAffix__getSourceGuid 的 7A44A0 实际读取:
- 有子效果:第一个子效果的 +0x38 / +0x3C,即 GUID2 / 显示分组身份。
- 没有子效果:-1。
它不是来源角色 GUID,也不是武器实例 GUID。本批宿主接口命名为 FirstChildDisplayGuid,避免把错误的 ID 接到缓存逻辑。
验证范围
原始 Torchlight2.exe SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。
- 独立 x86 辅助进程运行原 7B0420、7A3E00、7A44A0,并使用真实 MSVCP90 wstring 比较。
- 384 组 PC24 / PC53 / PC64 对照,活动列表、所有父时长位模式、析构顺序、数值重算 / 描述失效回调全部一致。
- 析构、全局分配器、数值重算和描述失效是明确的宿主边界。该 oracle 没有执行完整子效果析构,不能把它说成整个效果管理器已还原。
- 用真实 FROZEN DAT 构造结果验证父倒计时不污染子时长,以及续期后的新截止时间。
- 1871 条核心断言通过,原有混合 effect frame 对照继续通过。
- 417 项相关 Python 回归通过。
- Godot C# 构建零警告 / 零错误,coresimlint 无硬错。
- 独立 Godot 命名统计探针 10 项通过。
- 真实 ECHO PASS 混合回复 / DoT / 控制夹具通过:共同补位顺序保持,致死回调仅一次,XP / 击杀统计正确。日志无 ERROR / SCRIPT ERROR。这是已表示的混合池回归,不是元素武器 HIT 验收。
产物:
- core_sim/NativeAffixCleanup.cs
- core_sim/NativeEffectTraversal.cs
- build/native_affix_cleanup_oracle/results.json
- build/native_affix_cleanup_core.log
- build/native_affix_cleanup_regression.log
- build/native_affix_cleanup_world.log
下一段实际接线
继续建立真正由角色持有、可保存的 affix / child 实例关系;将 canApply / apply / register、子效果解绑、主题回收与此处的阶段 / 父清理规则组合。不能让两套数据分别持有同一个效果并各自计时,也不能用无操作 Destroy 宣称正式清理已完成。
武器构造仍为 1099 / 1419;273 阻塞、47 基础定义 / 等级缺口、0 构造异常,准入未放宽。快照仍为 v42,本批没有添加假的运行实例存档字段。
遵循 godot-master 的权威数据 / 表现分层;原地 main,未提交,未 attach 游戏。默认 warp_autosave.json 未改,SHA256: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。