元素 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”。

精确更新规则

  1. NEEDSUPDATE 为 false 时整次更新直接结束。
  2. Period > 0 时,从 Remaining 减 delta;结果先保存 float,但到零比较和加回周期使用仍留在 x87 的值。
  3. Remaining <= 0 时只调用一次 increment,不用 while 补齐遗漏周期。
  4. increment 做整数回绕加法和原版钳制,不产生 ADDSTAT / REDUCESTAT 事件。
  5. REFRESHES 会把 Remaining 重新设为 Period;若 TIMED 也为 true,同时把 Elapsed 清零。
  6. 即便 increment 刚把 NEEDSUPDATE 关闭,这一帧仍继续 TIMED 分支,再将 delta 加入 Elapsed。
  7. 只有 Period <= 0 时,Elapsed > MAXTIME 才进入对应停止分支;这个分支不把当前值改回 DEFAULTVALUE。
  8. 末尾检查当前值是否等于默认值,决定是否停更;不会删除 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:

  1. add 后值为 1,时钟刷新为 Period=5。
  2. 动态挂载把 Remaining 改成 2。
  3. 两个 1 秒帧之后值减 10 并钳到 0;Remaining 回到 5,Elapsed 在该帧末为 1,NEEDSUPDATE=false。
  4. 之后不继续计时,实例仍存在,且没有额外 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.cscore_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