元素 Combo 命名统计:原生时钟、角色运行态与快照 v42
2026-09-20。为四元素词缀的 TYPE146 子效果补齐实际命名统计状态。本批不是新增固定 DoT,也不是把 Combo 数值塞进 239 项伤害属性缓存。
原版证据与字段
只读数据库:E:/Torchlight 2/Torchlight2.exe.i64。 原 EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。
| 原函数 | 作用 |
|---|---|
| 7B55F0 | 读取 STAT 定义、整数初值 / 默认值 / 上下限和时钟字段 |
| 7B65F0 | 整数相加,先发 STAT 事件,再钳制当前值,再更新计时状态 |
| 7B5500 | 更新 NEEDSUPDATE,按 TIMED / REFRESHES 处理时钟 |
| 7B5590 | 加 INCREMENTVALUE,钳制,再更新计时状态;不派发 STAT 事件 |
| 7B6710 | 单个 CStat 每帧更新 |
| 7BB7C0 | 遍历单位的 CStatManager |
| 518290 | 名为 setDuration,实际只写剩余倒计时 +0x8C |
| 562D1E | 动态 ADD STAT:概率 → addToStat → 正时长时 setDuration |
CStat 的重要字段:
- +0x6C:当前值;+0x70:DEFAULTVALUE;+0x74:INCREMENTVALUE。
- +0x78 / +0x7C:MAXVALUE / MINVALUE。
- +0x80:DAT 的 DURATION,作为周期长度。
- +0x84:MAXTIME。
- +0x88:ELAPSEDTIME。
- +0x8C:REMAININGTIME。
- +0x94 / +0x95 / +0x96 / +0x97:NEEDSUPDATE / TIMED / STACKABLE / REFRESHES。
不能把三个时间字段合成一个“buff duration”。
精确更新规则
- NEEDSUPDATE 为 false 时整次更新直接结束。
- Period > 0 时,从 Remaining 减 delta;结果先保存 float,但到零比较和加回周期使用仍留在 x87 的值。
- Remaining <= 0 时只调用一次 increment,不用 while 补齐遗漏周期。
- increment 做整数回绕加法和原版钳制,不产生 ADDSTAT / REDUCESTAT 事件。
- REFRESHES 会把 Remaining 重新设为 Period;若 TIMED 也为 true,同时把 Elapsed 清零。
- 即便 increment 刚把 NEEDSUPDATE 关闭,这一帧仍继续 TIMED 分支,再将 delta 加入 Elapsed。
- 只有 Period <= 0 时,Elapsed > MAXTIME 才进入对应停止分支;这个分支不把当前值改回 DEFAULTVALUE。
- 末尾检查当前值是否等于默认值,决定是否停更;不会删除 CStat 实例。
7B5500 / 7B6710 的某些相等判断对整数存储使用 FLD,按 float 位模式比较。实现保留了负整数对应 NaN、正负零等行为;没有直接替换为所有位置都做整数相等。
显式 ADD STAT 则不同:
- 原始未钳制值先写入,再通知观察者。
- 重入回调完成后钳制现在的值,不覆盖回调修改。
- 完成 add / clamp / refresh 之后,动态效果的正 duration 才覆盖 Remaining。
- 518290 接受零、忽略负数和 unordered 比较;不会改 Period,也不自动启用 NEEDSUPDATE。
原版 Combo 数据的结果
ComboIce / ComboFire / ComboPoison / ComboElectric,以及同结构 ComboBlood,原定义均为 TYPE_INT:
- Period=5,MAXTIME=30,Increment=-10。
- TIMED=true,REFRESHES=true;默认值 0、下限 0、上限 100。
- SAVES=false。
例如显式加 1,效果时长为 2:
- add 后值为 1,时钟刷新为 Period=5。
- 动态挂载把 Remaining 改成 2。
- 两个 1 秒帧之后值减 10 并钳到 0;Remaining 回到 5,Elapsed 在该帧末为 1,NEEDSUPDATE=false。
- 之后不继续计时,实例仍存在,且没有额外 STAT 事件。
加 50 后一次推进 17 秒,只衰减一次到 40,不能执行三次或更多次。
词缀续期 ≠ 再次 ADD STAT。 上一批元素生命周期的同名 affix 刷新只改子效果 duration / elapsed,不重新执行子效果挂载。因此不能擅自把已衰减的 Combo 重新加 1,或同步重置其 CStat 倒计时。
本批真实接线
NativeNamedStats实现上述原时钟及整数更新。NamedIntegerStatDefinition保留可选定时定义;NamedIntegerStatSnapshot保留独立 Clock。TriggerableExecution接入定时 ADD STAT 和计时推进,保留事件前未钳制值、嵌套修改以及 RNG 事务。CombatUnit.NamedStats是角色所有的命名统计簿,与装备成长计数和 stat[239] 不混用。SimState.ApplyActorTimedStat要求调用者提供观察者派发回调;没有默认静默忽略观察者的实现。回调只能改工作状态。AdvanceCombatEffects接入角色统计时钟。只有统计需要更新时,不额外查询 HP 缓存或要求主从回血处理已完成。- 原
56B0F5先执行 active effects,56B92D后执行 stat manager;实现将统计推进放在该角色已表示的效果 / 健康 / 死亡工作之后。 - 致死工作副本携带统计服务和状态;前面已计算但未提交的角色时钟不会在重跑时多走一次。
- Godot 启动 / 读档绑定冻结统计目录,不把文件读取塞进 CoreSim。
观察者回调的完整实际内容仍需由元素词缀运行时绑定。本批测试中的无观察者或故意抛错回调均为明确夹具,不冒充原版全局观察者已恢复。
v42 保存与精度
快照增加角色命名统计簿、Clock 的 Elapsed / Remaining / NeedsUpdate,保存和恢复均断开数组引用。旧快照没有这些字段时保留空缺,不重建假历史;已存在定时统计却缺 Clock 时,重新绑定数据会拒绝。
命名统计 GUID 超过 JavaScript/Godot JSON 浮点整数精度范围。SnapshotCodec 现在把 NamedIntegerStatSnapshot.Guid 写为十进制字符串,仍能读取旧的精确数字形式,避免 Godot parse/stringify 后变成另一个 GUID。
SimSnapshot 是完整模拟快照 / 重放状态,包含临时统计;这不宣称已经复刻原版 .SVS 按 SAVES 筛选的保存策略。SAVES=false 仍保留在冻结定义中。
验证与产物
- 独立 x86 辅助进程执行原 7B5500、7B6710、7B5590、7B54A0、518290 和 TYPE_INT 的 7B65F0:1152 组 PC24 / PC53 / PC64 对照,零差异。
- 原始加法对照使用 null manager,只验证值与时钟,不冒充执行了原全局事件总线;事件顺序另由事务测试验证。
- 1862 条核心断言通过,包括跨角色重入、失败回滚、旧快照、GUID、独立数组、死亡状态时钟,以及致死重跑只推进一次。
- 413 项相关 Python 回归通过。
- 独立 headless Godot 探针 10 项通过:实际 C# 绑定、Godot JSON 往返、倒计时、快照恢复、停更和失败回滚。
- Godot C# 构建零警告 / 零错误;coresimlint 无硬错。
- 真实 ECHO PASS 的 GREATAXE_U02C 成长回归通过:10 次原动作 HIT 击杀冠军、计数到 10、两条升级词缀、监听移除及九杀存档恢复重放均通过,结果日志为
build/native_named_stat_growth_verified.log。仅目标低 HP / 复活 / 随机种子为夹具;这是旧成长链兼容性回归,不是元素武器 HIT 验收。
该地图回归首轮因主场景 UID 缓存未登记而未进入场景。完成 headless editor 导入后,同一测试通过;没有修改主场景配置。导入仍提示 honoredpath_pb / echopass_pb 的已有重复 UID,本批未处理此地图资源警告,不能将导入日志说成完全无警告。
代码:core_sim/NativeNamedStats.cs、core_sim/SimNamedStats.cs。 原代码结果:build/native_named_stat_oracle/results.json。 Godot 结果:build/native_named_stat_engine/result.json。 相关日志:build/native_named_stat_*.log。
仍未完成的武器链路
- 元素词缀的 canApply / apply / register 完整消费者、观察者绑定和共同动态列表追加。
- 主题 / GUID2 / 图标登记,子效果到期和完整词缀实例保存。
- 感电攻击型 Charged Bolt。
- 装备所有者的定时统计更新顺序;本批只接角色时钟,相关装备准入仍显式拒绝。
- computed stats、STACKABLE 统计及原版完整存档策略。
武器审计仍为 1099 / 1419 可构造,273 首要处理阻塞、47 基础缺口、0 构造异常。这不是“所有元素武器已完成”。
遵循 godot-master 的状态 / 表现分层及版本化保存;未新增通用 buff 定时器、没有用 UI 状态代替权威数据。main 原地工作,未提交,未 attach 原游戏,默认存档未修改: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。