四元素状态词缀:创建准备、续期与子效果身份
2026-09-20。延续四元素挂载研究,将 TYPE106–109 的后续词缀流程与原始 DAT 构造数据接起来。本批完成构造准备和续期规则,不等于四元素已能在正式战斗中端到端运行。 武器准入未放宽,仍为 1099 / 1419。
原版控制流程
来源为原始 Torchlight2.exe 的只读 IDA 数据库,EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。
566DD0:元素触发器在 elapsed 推进后处理。已过期、激活被禁止,或 native state 为 5 / 6 / 41 / 16 时暂不执行。- duration 为 -900(INSTANT)或 -1000(ALWAYS)时,不创建状态词缀,直接消耗触发器。
- 依据接收者是否属于 PLAYER(类型 28)选择普通或 PLAYER 后缀;不是依据阵营。
7AE5F0:按精确名称取第一条词缀;查找本身不先过滤过期项。- 已找到:
7A3FF0写新 duration,接着7A4050将全部子效果 elapsed 清零。不做累计时长、不取新旧最大值、不重新掷子效果数值。 - 未找到:经
513C70 → 7B00D0 → 7AFFD0创建并添加词缀。 - 得到词缀后设置子效果的 GUID2 / 图标标记;随后消耗原触发器。创建返回空也会消耗触发器。
创建参数的实际读取顺序已按汇编纠正:56848D 读 duration (+0x24),56849E 读来源单位 (+0x40),5684AD 读整数 LEVEL (+0x08)。不可沿用反编译器错误的 float 参数类型,也不可交换来源和等级。
续期的精确边界
NativeAffixLifetime 通过宿主提供的子效果视图工作,不创建另一个属性池:
- 先写父词缀 duration。
- 仅当子效果 flags 的 bit 0 未设置时,覆盖子效果 duration;这个位表示显式 DURATION,而不是“当前时长是否大于零”。
- 不论是否显式时长,所有子效果的 elapsed 都归零。
- 不会清除子效果的过期位 0x20。 重置时钟不能擅自复活已被标记的效果。
256 组独立 x86 辅助进程对照运行了原 7A3FF0 与 7A4050 机器码。涵盖空列表、1–16 个子项、显式时长、过期位、正零 / 负零、哨兵及分别调用 / 连续调用,时长和 elapsed 按原始 float 位模式比较,一致。
这 256 组不覆盖整个 566DD0、词缀构造器或效果管理器清理;生命周期控制器另以宿主边界测试验证。
八份原版数据,不合并成四个简化布尔
WeaponAffixCatalog.PrepareElementalAffix 复用现有五槽 / 曲线 / affix 等级构造路径,共绑定八个原版定义、26 个有序子效果:
| 状态词缀 | 非玩家子效果 | PLAYER 差别 |
|---|---|---|
| DAMAGEEFFECTFROZEN | 移动、攻击、施法速度各 -33;Frozen;ComboIce | 速度分别 -15 / -20 / -20;无 ComboIce |
| DAMAGEEFFECTONFIRE | 火系 DAMAGE,DAMAGE_MONSTER 曲线的 25%;Burning;ComboFire | 曲线比例 20%;无 ComboFire |
| DAMAGEEFFECTPOISONED | ALL 伤害 -33、ALL 护甲 -33;Poisoned;ComboPoison | 无 ComboPoison |
| DAMAGEEFFECTELECTRICITY | Electricity;受击施放 PROC CHARGEDBOLT;ComboElectric | 无 ComboElectric |
表中 -33 等为原效果槽值,不把它们误写成已经执行过最终移动 / 伤害公式。燃烧也不是固定 25 HP;其取值沿实际效果等级和原始曲线求值,后续减伤与周期扣血仍需真实处理器。
所有原始 child 记录、五槽、已绑定曲线、名称、主题、来源身份和 DAT 哈希均保留。构造出来的每份可变 child 独立于原型和其他实例。
时长与乘数
- 创建时正 duration 在子效果派发前传播,仍尊重显式时长位。
- 默认 EXCLUSIVE 根据原始 duration 解析;不能在把 -1000 改为有限时长后,给 BURN 的 DAMAGE 子效果凭空添加独占位。
7A4780只在 +0x0C 为未设置哨兵 9999989.0 时写入传入乘数。因此燃烧 DAT 的 SOAKSCALE=0.33 保留,不会被创建调用传入的 1 覆盖。7A47F0则无条件写 +0x10;这两个参数不是对称行为。
标记不等于名称
7A6170 写 GUID2 (+0x38 / +0x3C) 并维护 0x8000 位; 7AA2A0 写 interned icon/string id (+0x2C) 并维护 0x20000 位。 两者都不是 NAME 的 wstring (+0x50)。
因此 EFFECT_FREEZE 等只能作为预期标记保留,不能覆盖 COMBOICE 或 PROC CHARGEDBOLT 的名称。实际 GUID / 字符串注册表、主题和技能绑定仍由后续宿主实现,本批没有生成假的 id 或宣布这些运行时绑定已完成。
构造接口的事务边界
- 构造准备复用现有原型缓存,区分 cold / warm;不将原型原地改成某个角色的状态实例。
- 子效果回调在各自 setOwnerAffix 后、下一个子效果求值前执行,共享同一条暂存 MWC 流。不能先全部构造完,再声称任意含随机数的处理器后置调用仍等价。
- 回调只能改工作副本;失败时不提交调用者 RNG,也不改变传入的已加载原型列表。外部回调自己的副作用不能由此接口自动回滚。
- 不传回调只生成检查用结果,
HostDispatchPrepared=false。结果不会自动进入 CombatUnit、存档或正式战斗。 - 仅接受八种精确名称、正有限时长和该调用实际传入的 1 / 1 参数。其他上下文显式拒绝,不拿近似值顶替。
- 用真实 FROZEN 子效果串联了生命周期创建 / 再次命中续期测试:不重建、不额外推进 RNG、不覆盖 ComboIce 名称,也不抹掉过期位。
验证
- 原始 lifetime 机器码:256 组,零差异。
- 真实 DAT 构造组合:8 变体 × 5 等级 × 4 难度 = 160 组;另含数值、派发顺序、失败回滚、独立实例和续期测试。
- 核心断言:1838 条通过。
- 相关 Python 回归:409 项通过。
- Godot C# 构建:零警告、零错误;coresimlint 无硬错、无本批警告。
- 逐定义武器审计:1419 总计,1099 可构造,273 首要处理阻塞,47 基础定义 / 创建等级缺口,0 构造异常。
日志为 build/elemental_affix_data_*.log;原机器码参考为 build/native_affix_lifetime_oracle/results.json。 核心实现位于 core_sim/NativeElementalLifecycle.cs,数据构造位于 core_sim.io/F2ElementalAffixes.cs。
剩余实际接线
本批没有完成以下内容,不能将准备成功计作元素武器实战就绪:
- CombatState 中真实 canApply / apply / register 的事务性接入,以及 passive / dynamic 子效果的共同所有权和到期清理。
- 两阶段效果遍历与追加顺序。后续汇编复核纠正了此前的“同帧继续遍历”推断:566EAC 在每张表开始时保存长度,569B37 比较该快照长度;先 PASSIVE 后 DYNAMIC(569B51 比较 2)。当前阶段追加的尾项不会在该阶段立即 tick,但会进入后续缓存重算;在 PASSIVE 阶段追加到 DYNAMIC 的项可在后者开始时被纳入。完整两表接线仍待实现。
- Combo* 命名统计的定时生存期和观察者通知。
- 真正的主题 / GUID / 图标绑定、状态实例快照及恢复。
- 感电 Charged Bolt 的攻击型技能派发。
- 正式 HIT、重复命中、到期、死亡、保存重放与画面核验。
继续遵守 godot-master 的数据 / 逻辑 / 表现分层,具体数值与副作用以原版为准,不套用通用减速 / DoT / 无敌帧模板。
main 原地工作,未提交;未 attach 原游戏;未改默认存档。 warp_autosave.json SHA256 仍为 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。