证据为原版 E:/Torchlight 2/Torchlight2.exe.i64,EXE SHA256: 186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e

原版数据覆盖

全量233个TRIGGERABLEACTION、150个命名STAT定义已扫描。75个武器通过literal AFFIXES执行156次TYPE165安装,目标去重后54个,均能解析。 这54个是27个CHARACTER/DIE计数器和27个STAT/ADDSTAT升级监听器,完整保留奖励AFFIXES、STATWATCHER和TRIGGERABLESREMOVE,不能只抄计数器。

安装不是普通查找

583080 TYPE165先掷rand(1,100),要求 CHANCE > roll。CHANCE100仍可能在roll100失败;失败概率分支会走原效果挂载fallback,空名/缺定义分支则不会。 7C21F0按名字惰性加载模板。旧名 CUnit__findTriggerableByDef(515900)实为“查重后创建”:已有同指针/同名时返回null,新建时复制308字节对象并加入列表。

6DF4A0绑定owner,6DEB90注册监听并清零冷却锚点;它即使对已注册实例调用,也先清零锚点。 安装仅在模板LEVEL为0时使用源效果等级。执行另受USEOWNERLEVEL(默认true)和CASTFROMBEARER影响,6DEC70/6DEE20可取物品或持有者等级。原模板LEVEL1并不等于成长奖励永远按1级算。

装备、持有者与事件过滤

57DDC0让非TRIGGERONBEAREREVENTS的监听器直接注册;bearer类按TRIGGERWHILEININVENTORY或TRIGGERWHILEEQUIPPED决定是否注册。 因此典型击杀计数器只在装备时工作,监听物品自身计数的升级触发器却可继续注册。

6DF080先检查事件creator是否符合WATCH_UNITTYPES等限制。FILTERSELF要求owner/bearer等于事件creator或另一单位(击杀者);ENEMY再检查敌对关系。 部分关系不匹配的路径仍返回true,所以外层仍可开始冷却,不可将“没有执行动作”等同于“回调失败”。

6DF230对ADDSTAT先要求事件值≥FLOATCONDITION,再检查owner/bearer。奖励块内STATWATCHER另外检查特定计数,例如ZombieSlayer>49;不能把任何同值STAT事件当成目标计数。

冷却与整数STAT的时序

6DF560检查 nativeClock >= LastTrigger + Cooldown 与重入标志。5C2530每帧将 trunc(float(dt)*1000)加到本地玩家+2412:60次float(1/60)更新累积960ms,不是1000ms。不是系统墙钟,也不是累计秒数后再统一取整。

命名STAT不在effect stat[239]里。7BBED0按GUID找实例/定义;TYPE_INT由 7B65F0先trunc增量并相加,发送包含新总值的事件,随后 7B54A0才钳制MIN/MAX。 例如当前9999、增加2、MAX10000:事件值为10001,最终存储10000。嵌套watcher与递归事件必须保持这个先后顺序。

6DEC70根据动作包的成功返回值或REMOVEAFFIXESONLYIFAFFIXESAPPLIED决定是否删除指定触发器。这里的“成功”不是每个底层效果都成功:7BFD10在找到affix并调用attach后设置成功,不读取attach返回值,难度拒绝仍可能走删除。 7BFD10的触发奖励AFFIX路径含额外setSource/setLevel,随机调用不能直接套literal装备初始构造。

递归派发与删除顺序(2026-09-13补核)

7BF860从事件桶删除监听器时逐项左移、保留次序;513580从owner触发器数组删除时却用末项补位。7BF8D0遍历live事件桶并无条件递增索引,因此回调中删除当前项可跳过后继项,回调中新追加的监听器则可能被本次事件执行。二者不能统一成swap-back,也不能用预先拍平的匹配列表。

7BFD10遇到不满足的STATWATCHER立即return0,后面的奖励不再执行;此前已经执行的奖励不撤销,但成功返回值被丢弃,会影响删除门。watcher应在每个奖励执行前重新查询,不能预先一次性筛选。

5831BE确认TYPE146 ADD STAT每次仍要rand(0,100),条件为CHANCE >= roll。即使100%也消耗骰;0%可在roll0成功。它不同于TYPE165的rand(1,100)与严格大于。被接收的TYPE146即使概率失败仍返回1。

7B55F0读取命名整数STAT时:VALUE初值默认0,DEFAULTVALUE复位值默认0,MINVALUE默认0,MAXVALUE默认100,SAVES默认false。成长计数涉及21个定义,显式最大值999或10000、SAVES=true。TYPE_INT的输入小数先截断再相加,溢出整数和仍按32位处理。

1B91390的枚举初始化确认ADDSTAT=0、CLEARSTAT=1、REDUCESTAT=2。7B65F0用截断后的增量判断事件:正值发ADDSTAT,零/负值发REDUCESTAT。

7B65F0回调之后再钳制当前字段,不是把事件前算好的最终值写回;回调中的递归加计数必须保留。7B54C8在整数值位于范围内时还会落入float位重解释比较,负整数特殊位型不能简单用Math.Clamp替代。超出32位的浮点转整数另受84A7F0的SSE/旧x87分支影响,当前移植明确不支持该越界输入。

触发奖励数值构造的完整顺序是createAffix → setSource → setLevel → attachAffix。测试范围图效果warm literal消耗14步,而trigger bundle消耗18步;难度拒绝也分别消耗8步、12步。递归STAT生成奖励时应沿正在构造的affix的暂存RNG继续,不能推进被挂起的父随机流。

当前移植状态

2026-09-13后续已接入真实装备与死亡事务,生产快照升级为v25。成长实例/命名计数属于物品,时钟/难度/订阅次序属于会话;致命伤害、计数、升级词缀与属性重算一起提交,失败不消耗HP/RNG或提前删除监听器。

验证为940条核心断言、32项相关Python回归;主场景用ECHO PASS真实冠军怪与GREATAXE_U02C经动画HIT到10杀,装备获得两条原始奖励词缀,9杀存档恢复后第10杀逐值/RNG一致。低HP复活/位置/种子是隔离测试输入,没有声称原版exe动态对拍。

目录同时补入105个扩展UNITTYPE,原版武器清单为1419;目前1042个可构造,其中10个可创建完整已支持的成长链,330个host/NG+及47个基础等级缺口仍在。其余含未实现proc/DOT等奖励的链仍整体pending;75个成长武器没有全部完成。升级提示/动态目标tooltip、特殊归属击杀等仍未齐全。扩展类型详情见 UNITTYPE的HIE与DAT两层加载

两个接线修复:同InstanceId的已拾取地面记录与背包物品恢复后重新关联,避免升级后快照分叉;同一装备获得新词缀只更新攻击的数值stamp,不重选动画/清除命中记录,换成另一把同属性成长武器则仍使旧攻击失效。

相关:CAffix效果管理器Triggerable动作派发证据