技能活动、INTERRUPTABLE 与软结束请求:v47

2026-09-20。本批补 52CE50 所依赖的技能活动与软结束规则,并加入可保存的显式观测状态。普通 NPC 实时生产者及结束事件消费者仍未接完;不能宣称怪物已经完整可打断。

发现的关键差异

原反应分派调用 6D22D0(false,false,false),不是立即删除所有技能。

  • +3C 是 CSkill 的 ACTIVATION_TYPE,不是怪物 AI 状态。
  • 激活类型 4 跳过。
  • 其他条目调用 6BE830,逐层检查 6C8690。
  • 只有所有层都允许时,写 CSkill+F8 = 0.001f。
  • 不删除技能、冷却、已有投射物或监听器,也不在这个函数里强制 AI 回到 idle。

因此将前端施法数组清空、直接设 fired=false 或销毁所有技能,并不是该调用的复刻。反应接口已改名为 RequestSkillSoftStop,防止消费者误读为立即取消。

51A5B0 技能是否仍在执行

输入是原字段,而不是前端 S_CAST / S_ATTACK 的猜测:

  1. 没有当前技能指针,返回 false。
  2. requested animation != -1,且 before-hit 为真,返回 true。
  3. 否则若 resolved animation != -1,且 5996F0 或 599560 查询为真,返回 true。
  4. 其余情况要求 elapsed < endTime 且未置 interrupted 字节。

这意味着:

  • 没有活跃动画不一定代表技能结束;
  • 活跃动画可以覆盖已到期的计时分支;
  • timer fallback 的 NaN 比较仍按原 fcomp 路径返回 false;
  • 运行时快照拒绝非有限计时输入。

NativeSkillActivitySnapshot 保存这些显式输入。生产者必须提供真实观测;不能把 AI 状态编号直接填成动画活动状态。

6C8690 层结束门

按原顺序:

  • Remaining > 0:拒绝;
  • 无 owner,或 definition animation != -1:允许;
  • owner 活动查询或虚函数活动查询为非零:拒绝;
  • 任一已登记 child 活跃:拒绝;
  • 否则允许。

6BE830 在某一层拒绝后停止检查,不写结束计时。无层时允许写入 0.001f;这不是 min(oldTimer, .001),原值为负或更小时也会被覆盖。

原字段拼写与继承

真实字段为 INTERRUPTABLE:

  • 6DC320 的 CSkillProperty+0xD1 初始化为 false;
  • 有父属性对象时以父值作为缺省;
  • 当前块显式写值才覆盖;
  • 6C0C80 构造 LEVEL1 时继承根属性,后续 LEVEL 继承前一级;
  • 6BE220 读取当前有效属性对象的 +0xD1;没有属性对象返回 false。

tools/build_skill_interrupt_flags.py 导出了 1659 个原技能的根与连续 LEVEL 标志,本次语料没有 BASEFILE 技能。若以后遇到 BASEFILE 子块合并,仍会明确标为 unverified,而不是凭平铺属性猜继承。

数据读入使用路径 / 等级,不按可能冲突的短名字静默挑选。

CoreSim 与 v47

新增 NativeActorSkillManagerSnapshot,包含:

  • 当前活动观测;
  • 原 +34 技能登记列表的稳定 ID、ActivationType、EndTimer;
  • 各层结束条件及 child 活动数组。

SetActorSkillInterruptionState 校验并复制输入;RequestActorSkillSoftStop 生成新状态;捕获与恢复复制层级数组,避免外部修改快照影响实时状态。

实际伤害判定现在可以读取显式技能活动及 Interruptable 标志;调用上下文仍可明确提供已准备的值。未配置观测的旧数值通路保留既有兼容行为,不制造一个假的活动技能对象。

Godot 桥接提供显式观测入口,并从冻结目录绑定 INTERRUPTABLE;没有自动以现有 AI enum 填造原字段。

原代码与运行时验证

独立 32 位辅助进程执行:

  • 51A5B0
  • 6BE340
  • 6C8690
  • 6BE830
  • 6D22D0(false,false,false)

模型活动、owner 活动和 child 活动是明确的输入边界。768 组返回结果及结束计时 bit pattern 全部匹配。

另外验证:

  • 请求只改变允许条目的 EndTimer,不删除登记表;
  • type 4、剩余时长和活动 child 保持正确阻止行为;
  • v47 保存 / 恢复后软结束结果与 RNG 一致;
  • 快照层数组不与实时状态别名;
  • 实际 HIT 读取活动的可打断技能时产生 interrupt request;不可打断时不凭空产生;
  • Godot 内的保存、请求及重放通过,操作不消耗 RNG。

结果:

  • 2061 条核心断言通过。
  • 443 项相关 Python 回归通过。
  • Godot 检查 40 项通过。
  • C# 构建零警告 / 零错误,192 个核心文件静态检查无硬错。
  • 已有三元素桥接和四元素 GPU 回归通过。

产物:

  • build/f2/skill_interrupt_flags.json
  • build/native_skill_interrupt_oracle/results.json
  • build/skill_interrupt_core.log
  • build/skill_interrupt_regression.log
  • build/skill_interrupt_build.log
  • build/native_named_stat_engine/result.json

仍未完成

本批没有实现完整技能更新器。写入 0.001 并不等于其后 EVENT_END、粒子、伤害区域、状态与技能移除都已执行。后续需:

  1. 将正常 NPC 的真实技能 / 层活动观测接入该状态。
  2. 将 RequestSkillSoftStop 接进反应暂存宿主,避免在命中提交前改实时状态。
  3. 接上结束计时后的真实技能层 / EVENT_END 消费。
  4. 将动作恢复、仇恨与 Charged Bolt 等后续路径组合验证。
  5. 最后逐武器开放和验收,而不是撤销保护门。

构造覆盖仍为 1099 / 1419,273 阻塞、47 基础缺口、0 构造异常。全武器目标仍未完成。

遵循 godot-master 的权威状态 / 显示分离;本批不让前端清空数组冒充原技能生命周期。main 原地、未提交、未 attach 原游戏。默认存档 SHA256 未变: 9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f