TYPE52 持续伤害挂载、来源与重放

后续:DoT与回复统一周期更新及v31顺序已接非致死扣血,下面保留挂载批次当时的范围记录。

本页补充持续伤害净速率。挂载不是扣血:本批已接入 CoreSim 的命中事务和 v30 保存/恢复,但统一周期 HP、致死归属及 Godot 正式调用方仍待接线。TYPE52 武器准入没有开放。

来源身份不可替换

原始链路是装备槽 → 装备管理器 → 7AF0B0 transfer → 目标 5625907AF231 从 effect manager +0x58 取宿主作为 source;562677 把 source 转为 CCharacter。

装备 source 的 CCharacter 转换为空,不能为它擅自替换成持有角色。角色来源则执行下列增伤;这是调用来源的区别,不表示所有技能效果都绕过角色增伤。

固定 EXE SHA256:186472c3057b38f4cdff4696959a943c396ae6166995f7418997b5ea853e8a5e。IDA 证据包括 513BC07B21E07B1D707AF0B07CBAA05625905243A0524190520D7051BB8051BB40

挂载后槽值公式

f32 表示单精度存储,中间计算仍按 x87 精度执行:

text
multiplier = 1
if source 可转换成 CCharacter:
    multiplier = f32(1 + source.stat(25, ALL) * 0.01f)
    if incoming.damageType != ALL:
        multiplier = f32(source.stat(25, incoming.damageType) * 0.01f + multiplier)
    if source.getRootOwner() != null:
        multiplier = f32(rootOwner.stat(91, TOTAL) * 0.01f + multiplier)
    multiplier = f32(source.magicBonusToSkillFrac() + multiplier)
    target.lastDotCharacter = source
    if source.findOwningPlayer() != null:
        target.lastDotPlayer = source.findOwningPlayer()

newSlot0 = f32(incoming.slot0 * multiplier)
newSlot1 = f32(multiplier * incoming.slot1)
if sourceCharacter.findOwningPlayer() != null and target.alignment == EVIL:
    分别对两槽调用 scalePlayerVsMonsterDamage
else if target.findOwningPlayer() != null and sourceCharacter.alignment == EVIL:
    分别对两槽调用 scaleMonsterVsPlayerDamage

attached.setSlotAndReroll(0, newSlot0)
attached.setSlotAndReroll(1, newSlot1)

520D70 的 magicBonusToSkillFrac 查询 MAGIC_BONUS_TO_SKILL,以有效专注为输入,再乘 0.01f。getRootOwner 的 master 链行为不能仅凭当前持有者推断。

两条 scale 函数是多人数量缩放,不是难度倍数:检查附近其他玩家数量,然后查询普通/CHAMP/BOSS 版本的 PLAYERVSMONSTERDAMAGEMONSTERVSPLAYERDAMAGE。无其他玩家时返回输入原值。

随机数和门控时机

  • 第一次接收门在 7AF127,拒绝时不创建临时副本、不消耗复制随机数。
  • 临时副本改为 DYNAMIC、清 SAVE,普通复制三次取值;非 EXCLUSIVE TYPE52/156 再分别缩放两个原始槽,各取值一次。
  • 562590 再执行一次接收门;MIMICIDLE 在 TYPE52 分支中检查。它跳过附加,却仍返回分派成功。已经发生的临时复制随机消耗不能回滚成零。
  • 新目标附加的复制/setSource 共三次取值;之后两次 4453D0 又各取值一次。第二次必须在第一槽已经改变后执行,参数没变化也不能省略。
  • 变量区间测试中 EXCLUSIVE 为 8 次取值/16 个 MWC advance;非 EXCLUSIVE 为 10 次取值/20 个 advance。上下界相等等情形依 MWC 规则,不能固定写成 16 或 20。

重复命中和到期

7B1D70 的 EXCLUSIVE 只看新效果。相同 NAME 与 TYPE 的所有存活旧效果被标记过期,新的仍然追加;旧效果是否 EXCLUSIVE、来自哪件装备都不影响匹配。

7A6430 使有限时长旧效果 elapsed=duration,并置 flags 0x20;不是立即把旧记录从列表删掉。没有 EXCLUSIVE 则允许叠加。有限 DAMAGE 默认 EXCLUSIVE,但原数据可显式为 false,例如 UNIQUE_TL2_DAMAGEOVERTIME2

7AF2A0 移除过期效果用末项填补当前位置并重新检查该位置。因此 [到期A, B, C] 清理后为 [C, B]。该顺序影响后续 float 累加,不能用稳定过滤替代。当前 CoreSim 已实现独立 DoT 动态列表的此规则;与其他 timed 类型混排仍需共用完整管理器顺序。

CoreSim 状态和验证

新增 NativeDotEffectSnapshot 保存实例编号、source(正单位 ID/负物品实例 ID)、来源等级与 key、NAME、伤害通道、duration/elapsed、flags、值及原始槽/曲线。v30 深复制保存/恢复,v29 及更早合法快照恢复为空 DoT,不重掷随机数。

CombatState.Attack 在明确 receiver gate/MIMIC/角色来源上下文时准备 DoT 和缓存,整个命中成功才提交。缺少上下文、来源身份或无效槽值时,HP/RNG/旧效果不部分落地。拒绝附加不会产生幽灵实例或新零缓存。

原版机器码对照:1080 组 562590 TYPE52 分派(三精度),来源转换、门控、槽值、两次 setter 和弱引用写入条件一致。属性/类型/source/复制/setter/多人 helper 为显式边界;该 oracle 没有执行整个复制 RNG、aggro 或完整生命周期

组合回归验证复制随机数、同名替换/叠加、swap-last、CoreSim 实际 Attack 方法、净速率核、v30 字节级重放及快照不别名。1484 核心断言和 62 项相关 Python 检查通过,Godot C# 构建通过。这不是主场景或原游戏实时验收。

未完成的生产链路

还需统一 DoT/回复/其他动态效果的寿命和净 HP 更新,复原自然回复与完整 modifyHealth 门,按原版扫描来源处理致死和 target 的两条弱引用回落;再接经验、掉落、任务及 UNITTHEME 表现。

旧正回复循环遇到 DoT 目前明确报 dot_periodic_runtime_pending,不悄悄只回血不扣伤害。Godot 世界还需提供原接收门和来源上下文,完成实机与存档验证之后才能解除此保护并开放武器准入。

当前构造统计仍为 1053/1419;319 个处理阻塞、47 个基础缺口,TYPE52 为其中 53 条定义的首个阻塞。