主命中与后续 strike proc 的统一事务
本批将 CombatState.Attack 的主命中与后续 strike 消费者接入同一工作副本。解决的是“后续技能失败时主伤害/护盾/随机数已提交”的接线风险。正式 Stormclaw 的原选靶、事件体与表现生产者仍未绑定,不开放 TYPE201 武器准入。 后续核实:这里曾称作“HIT 统计”的 16/17 实际是 CHARACTER/WEAPONHIT、GOTWEAPONHIT 消息,不是 CStat 自增;见后续事件取证文档。
原版顺序证据
原 EXE SHA256:186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。
直接读取原机器码确认:
0x54E091 → 0x53D5A0:先调用 applyDamage。0x54E21D → 0x53D3C0:随后分派 152;前面的0x54E216压入 152。0x54E237 → 0x53D3C0:再分派 201;0x54E230压入 201。0x54E1F5(格挡结果)与0x54E202(NOSTRIKEEFFECTS)均跳往0x54E376,越过 strike proc 段。
两段之间还存在原 CHARACTER 消息/观察者与反馈调用,不能声称“扣血后立刻随机选个敌人”就是完整原版。后续已确认这对消息不是数值统计操作,并独立绑定当前完整语料中的空观察者桶;INativeStrikeProcRuntime 继续负责真实 proc,测试消费者不代替正式实现。
同时删掉了 AttackResolver 中旧的无条件 fireProcEffects 诊断调用。它们既处于真正扣血之前,也没有遵守格挡/0x2000/武器分类门,不能拿来接真实 proc。伤害数值解算未改。
当前实现
- 检测已表示的活动 strike 类型;装备遍历不因 HIDDEN 或 StatsEnabled=false 而跳过装备槽。
- 没有相关 proc 或有 NOSTRIKEEFFECTS 时保留现有直达路径,不为普通无 proc 攻击创建整个工作副本。
- 有相关 proc 时,在工作副本中执行一次完整主命中,包括现有控制、护盾、资源变化和已建模死亡工作。
- 只有主命中成功且原 strike 门允许时调用后续消费者。格挡/未命中的不可达消费者不会被要求绑定。
- 后续攻击仍走正常
CombatState.Attack,原 Stormclaw strike 的 NOSTRIKEEFFECTS 可阻止再次进入 strike 链。 - Volatile 与 Synced 两条流共同提交;发生缺失输入或后续失败时,两者和主命中都不发布。
- 提交保留现有 CombatUnit/Item 对象引用,更新其可变状态,避免前端或攻击控制器仍握着旧对象。
- 先向独立验证副本发布,再对全部快照内容做结构比较:数组顺序、float/double 位模式、身份与新增字段都参与比较。尚不能发布的状态变化显式拒绝,不静默丢失。
- 结构比较使用纯内存数据,不把 JSON/文件 I/O 放进 CoreSim。成员访问缓存仅属于当次比较,没有静态可变状态。
- 框架服务在工作副本与读档时重绑;本批无新增持久字段,仍为 v48。
主要文件:core_sim/SimStrikeTransaction.cs、Combat.cs、AttackResolve.cs、SimState.cs、SimPeriodicDeath.cs。
按 godot-master 保持模拟权威与表现分离;这一层不创建 Godot 节点、不读取系统时间、不代替原目标/视线查询。
具体测试
核心测试使用真实原 Stormclaw 的 40% 电伤 strike 定义,但后续消费者/选靶是明确夹具,不声称完成 proc 全程序。
- 主命中后,后续回调确实观察到目标已扣血/已死亡。
- 通过普通攻击 API 对第三个单位执行二次武器伤害。
- 故意使后续失败:主 HP、二次 HP、护盾消耗、金币、两条 RNG 和存档字节全部回滚。
- 成功时二次伤害和护盾移除一并发布,角色与物品引用保持原对象。
- 存档恢复后整次攻击逐字节重放。
- 格挡/NOSTRIKEEFFECTS、stats-disabled 装备槽、不支持的额外状态写入均有回归。
- Godot 隔离测试通过实际
CombatState.Attack验证顺序、回滚和重放,不读写默认玩家存档。
最终验证:2104 条核心断言、453 项相关 Python 回归、Godot 47 项检查通过;C# 构建零警告/零错误,195 个核心文件静态检查无硬错。旧元素正式数值场景与四元素 GPU 探针重新运行通过。结果记录在 build/strike_transaction_*.log、build/native_named_stat_engine/result.json。
明确边界与后续工作
INativeStrikeProcRuntime目前没有在正式加载器绑定完整原版消费者;达到需处理的 strike 分支会明确返回native_strike_proc_runtime_unbound。- 仍需正式概率/管理器执行、目标查询与 Stormclaw 事件体,以及光束端点输出。后续核实:曾称为 source-skill 的 a7 实为当前攻击武器;原版 16 个可达 proc 的左右手要求均无约束。16/17 消息的后续确认不代表更早的 CHARACTER/HIT、GOTHIT 等观察者已经实现。
- 当前发布器不支持新增/移除单位、移动物品拓扑、未登记来源物品、无法重建的旧引用别名,或任意任务/布局状态改变。这些操作会拒绝而非只提交其中一部分;召唤/变形类 proc 继续需要扩展。
- 绑定实时对象的旧反应回调不能直接复用到工作副本,需要 work-bound factory;因此显式拒绝相应未知情况。
- 为先保证状态完整性,本路径使用两次恢复与结构验证。高频攻击型 proc 的性能预算尚待实际生产消费者接通后测量,不宣称生产性能已验收。
构造覆盖仍为 1099 / 1419,273 阻塞、47 数据/等级缺口、0 构造异常;全部武器目标未完成。
main 原地,未提交。默认存档 SHA256 保持: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。