受击回血与 FROM TARGET 查询订正
本批延续全武器目标,在 main 工作,未创建 worktree、未提交、未 attach 原版游戏。按 godot-master 将战斗状态留在 CoreSim,Godot 只消费回复特效与 HUD。
已接入的范围
TYPE81/CAST SKILL ON STRUCK → WC_PROC_FULLHEAL 已进入真实受击事务,不再只是产生请求。原有 TYPE203 击杀回血保持工作。 源目录有 4 个受击 Fullheal 武器;新增可构造的 3 个为:
SWORDS/SWORD_U03POLEARMS/POLEARM_U06BSTAVES/LEGENDARY_STAFF04
2HMACE/ZZZ_WRAITHHAMMER 还含 TYPE231 等未完整支持路径,不能因回血可用而放行整件。 全量实际构造为 1053/1419,其中 14 个成长武器;319 个 host/NG+ 待补、47 个缺基础创建等级。构造成功不等于全部战斗行为或自然掉落已经完整。
原版受击事件顺序
证据是原版 E:/Torchlight 2/Torchlight2.exe.i64,EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。 本批重新只读获取 53D5A0、548850、53C7E0、582380、5FC670 的 C/ASM,位于 build/research_weapon_families/。旧 IDB 注释不是依据,冲突时以指令为准。
53D5A0 的顺序:
- native state 为 5/6 或 DAMAGEABLE=false(+1707/+0x6AB)直接返回。
- 最终伤害为 0 只处理相应仇恨分支,返回,不掷 TYPE81 的骰。
- notify 标志为真且受击角色
+0x7DC <= 0,在 53DAD4 派发 TYPE81。 - 53DC01 才调用 modifyHealth 扣 HP,再进入死亡处理。
所以非零致命伤也可以先创建回复效果,但这条持续回复不会立刻抵消伤害,后续死者 tick 也不能复活。 548850 普通攻击给 applyDamage 的 notify 参数为真;攻击方的 0x2000 strike-effects 禁用位不是 TYPE81 的禁用位。 HealthChangeSuppressed 是更晚的 HP 修改门,不应当提前阻止 TYPE81;DAMAGEABLE=false 则确实在更早处阻止它。 物理伤害有最少 1 点规则,测试零伤害必须使用真正吸收/格挡后的最终零值,不能假定伤害倍率 0 就得到 0。
当前事务只承诺固定 SELF 回复程序的时序等价:回复不立即改变 HP,因此可暂存伤害与效果后统一提交。尚未将任意嵌套伤害/召唤 proc 都视为可由这一回调执行。
递归保护不能混同装备冷却
CUnit+0x7DC 是角色的 float 倒计时,构造初值 0。56B8E8..56B902 仅在大于 0 时减实际 dt,保留负越界值,不钳回 0。
身上 proc 的 53C7E0 模式 0、3、4 会把它写成 1.0;模式 1、2 不写。装备路径 582380 不写这个角色字段,而是执行物品自己的技能管理器。 因此“所有受击 proc 每秒最多一次”的旧总结不成立。多个装备各自管理冷却,角色 guard 为正时则会在 TYPE81 派发之前统一挡住这次受击事件。
v27 新增保存 ProcRecursionRemaining,旧版缺字段按已确认的构造初值 0 迁移,拒绝非有限值。装备自身的 charges/冷却仍使用前批的物品状态。 当前没有开放身上任意 proc 的执行消费者,不把字段已保存当作这些入口全部完成。
FROM TARGET 公共几何规则订正
这是覆盖最多的一组剩余技能的公共依赖:源引用中 WC_STORMCLAW PROC 有 27 条,BLOODWASH/FIRESTORM 等也用目标侧施法。几何谓词已落为 core_sim/ProcSpatial.cs,完整世界选目标与这些伤害技能尚未放行。
5FC670 先用 inverse(xform) 转成局部坐标,压平 Y,求距离 d 和绝对角度 a。a 来自 atan2(x,z) 乘原版 float 常量 57.29582595825195。 设 m=minDistance,h=halfAngle,r=maxDistance:
characterPass =
d <= m
OR (((a <= h) OR (h > 0 AND d < 3 AND a < 30)) AND d <= r)
OR (h > 20 AND d <= 1.5 AND a <= 180)
objectPass =
(a <= h AND d <= r)
OR (h > 20 AND d <= 1.5 AND a <= 180)旧文将最后一项写成 h>20 且 (d>1.5 或 a>180),正好反转了原版 CL 置位路径。 ASM 5FC8EA/5FC8FB 越过近身条件时跳到 5FC919;只有满足近身条件才在 5FC915 mov cl,1。这不是一个让远处目标绕过射程的旁路。 例:d=20、a=90、h=60、r=9 必须拒绝;d=1.5、a=180、h=21、r=0 则会命中近身旁路。 字符列表有严格 d<3/a<30 的放宽;第二个物件列表没有这条,也没有字符列表的 minDistance 豁免。 FROM TARGET 的 m=0 仍可容纳严格同点,不应解释成无条件移除这个比较。
正常加载 RANGE 默认 100,Fullheal 沿用;Stormclaw 原文 RANGE=9、FINDTARGETANGLE=360,后者按该接口直接当半角使用,仍不能越过射程 9。
完整 query 还要求:
- 原版字符/物件两个列表的遍历顺序,以及目标是否存活、可选、启用、MIMICIDLE、技能 canTarget 与 alignment 门。
- 原版 inverse Matrix4 和 x87 运算精度;目前谓词接受已经求出的距离/角度,不伪装完成 Ogre 数学层。
- 字符列表调用
5F3060、物件列表调用5F4EA0;后续展开已确认后者正是前者的布尔取反,两者使用同一套遮挡。起终点 Y 都加5,不能只做 Godot 扇形重叠检测。详见后续目标过滤批次。 - FROM TARGET 的原点替换、排除 origin、synced 随机旋转以及 volatile 随机选中顺序。
验证
build/proc_struck_core.log:1068 条核心断言全部通过。- 三件原版装备逐件通过:实际怪物伤害触发、扣血不被取消、延后回血、正/负 guard、v27 重放、0x2000、致命伤不复活、DAMAGEABLE、后置 HP 抑制、全吸收不掷 proc、范围缺口整笔回滚。
build/proc_struck_python.log:41 项相关来源/公式/既有回归通过。- Godot C# 零 warning/error;coresimlint 通过。
build/struck_proc_world/result.json:原版 SWORD_U03、真实 ECHO PASS 敌对单位 C_Sturm_Ranged01_Immobile;生产怪物 tick 的 polearm1 动作,strike=0.7 秒,fixture seed=25。- 主场景先扣 6 点(100→94);0.1 秒回复后为 131.760009765625/236,与缓存 0.016 × dt 的公式一致。怪物 HIT、冷却、RNG、回复与表现去重重放通过。
- 上一批
recovery_proc主场景击杀回血也再次通过;默认存档 SHA256 仍为9840fe858b6e311c73f8c2d4ed49912d59d71365f6278392811730e012d32a7f。 - 查看了 512 短边 struck_start WebP:无屏幕级黑块、无新增 HUD 遮挡(LIGHT-PART-OVER/OCCLUDE 各 2/2);场景雨幕/人物遮挡仍影响特效辨读(READ=1/2),不宣称原版逐像素或完整动画等价。
下一阶段仍需完整目标过滤/LOS/源施法角色与武器伤害来源,才能真正开启 Stormclaw、Bloodwash 等攻击型 proc。召唤、其余武器副作用、精确空间输入以及 47 个基础等级缺口保持清单,全武器目标未完成。