1. 引擎原生机制(2026-09-13 反编译核过)

CUnit::consumeAbsorbBuffers(0x51FEB0)在 bCommit=1 且伤害 ≥ 盾池时:池写 0 → CEffect::expire(0x7A6430:flags|=0x20elapsed=duration)→ 若该效果 LINK_NAME 非空,CEffect::notifyRemoved(0x7AE660)把管理器三张表里所有 NAME == 该 LINK_NAME 的效果逐个 expire,每个被 expire 的效果又沿自己的 LINK_NAME 继续 —— 一条链。真正从表里摘除在下一帧 CEffectMgr_expireEffects_perframe(0x7AF2A0)。

要点:

  • 时长到期走同一条路(expireEffects_perframe → notifyRemoved),所以连锁在「被打碎」和「自然到期」时都会发生。想只在被打碎时连锁,把盾的 DURATION 设为 ALWAYS,时长由 affix 的 DURATION 控制(affix 到期时整组一起走,连锁与否已无区别)。
  • 链必须无环。 notifyRemoved 没有访问标记,A→B→A 或 A→A 会无限递归到栈溢出——这是引擎缺陷,任何到期路径都触发。三处 MOD 数据 422 个盾扫过无环;写新链时自己保证。
  • 盾所属的 affix 若再无其它存活效果,sub_7AE480 把该 affix duration=0,affix 整体结束。

1.2 主题门(UNITTHEME / NOTTHEME)

效果带 UNITTHEME:xxx 时单位带该主题;效果到期主题消失。技能的 <STRING>NOTTHEME:xxx 表示带该主题时不可施放(REACTIVESHIELD.DATNOTTHEME:SHIELDED 挡住重复开盾)。把主题挂在与盾 LINK 的伴生效果上,就得到「盾碎之后才解锁的技能」。

1.3 没有的东西

TRIGGERABLE 的游戏事件全表(0x213DF48 起的字面量表):LEVEL / STAT(ADDSTATREDUCESTATCLEARSTAT)/ CHARACTER(HIT GOTHIT WEAPONHIT GOTWEAPONHIT KILL DIE CRIT GOTCRIT BLOCK GOTBLOCKED DODGE MISS FUMBLE FUMBLETARGET EXECUTE LEVELUP GAINEDCHARGE GAINEDCHARGEFROMZERO USEDCHARGE MAXEDCHARGE UNMAXEDCHARGE DEPLETEDCHARGE)。没有「护盾碎裂」「效果移除」事件。 ADD STAT(效果 146)只有 INSTANT 语义(不在逐帧分派器 0x566DD0 的比较表里),挂成 ALWAYS 也不会持续贡献,所以「盾碎 → 某 stat 归零 → STATWATCHER 触发」这条也走不通。

结论:「碎盾 → 失去 / 塌陷 / 解锁」纯数据可做;「碎盾 → 获得 / 施放」要运行时补一个挂钩。

2. 样例(纯数据,照原版 CHAMP_BARRIER_* / EMBERSHIELD 的写法)

2.1 琉璃结界:盾在时减伤,盾碎整组消失,并解锁「碎晶反击」

[AFFIX]
	<STRING>NAME:MK_GLASS_WARD
	<INTEGER>RANK:0
	<INTEGER>MIN_SPAWN_RANGE:0
	<INTEGER>MAX_SPAWN_RANGE:999
	<FLOAT>DURATION:20
	<INTEGER>WEIGHT:0
	<INTEGER>SLOTS_OCCUPY:0
	[UNITTYPES]
		<STRING>UNITTYPE:ANY
	[/UNITTYPES]
	[EFFECT]
		<STRING>NAME:MK_GLASS_WARD_BUFFER
		<STRING>ACTIVATION:DYNAMIC
		<STRING>DURATION:ALWAYS
		<STRING>TYPE:SHIELD BUFFER
		<STRING>DAMAGE_TYPE:ALL
		<BOOL>USEOWNERLEVEL:true
		<STRING>UNITTHEME:engineershield
		<BOOL>EXCLUSIVE:true
		<STRING>LINK_NAME:MK_GLASS_WARD_GUARD
		<FLOAT>MIN:300
		<FLOAT>MAX:300
	[/EFFECT]
	[EFFECT]
		<STRING>NAME:MK_GLASS_WARD_GUARD
		<STRING>ACTIVATION:DYNAMIC
		<STRING>DURATION:ALWAYS
		<STRING>TYPE:PERCENT DAMAGE TAKEN
		<STRING>DAMAGE_TYPE:ALL
		<STRING>UNITTHEME:MK_WARDED
		<BOOL>EXCLUSIVE:true
		<STRING>LINK_NAME:MK_GLASS_WARD_STEADY
		<FLOAT>MIN:-30
		<FLOAT>MAX:-30
	[/EFFECT]
	[EFFECT]
		<STRING>NAME:MK_GLASS_WARD_STEADY
		<STRING>ACTIVATION:DYNAMIC
		<STRING>DURATION:ALWAYS
		<STRING>TYPE:PERCENT KNOCK BACK RESISTANCE
		<BOOL>EXCLUSIVE:true
		<FLOAT>RESISTANCE:100
	[/EFFECT]
[/AFFIX]

链:BUFFER → GUARD → STEADY,线性、无环。盾被打空(或 affix 20 s 到期)→ 减伤 30% 与击退免疫同帧标记到期、下一帧摘除 → 主题 MK_WARDED 消失。

「碎晶反击」技能只需一行门:

[SKILL]
	<STRING>NAME:MK_SHATTER_RIPOSTE
	...
	<STRING>NOTTHEME:MK_WARDED
	...

结界在身时灰掉,碎了才亮。「碎后 N 秒内可用」的限时窗口纯数据做不到(到期不会新建任何东西),要靠 §3。

2.2 反向:弱点小盾当引信(原版 CHAMP_BARRIER_FIRE 的结构)

	[EFFECT]  NAME:MK_BARRIER_FUSE     TYPE:SHIELD BUFFER  DAMAGE_TYPE:ICE  MIN/MAX:20   LINK_NAME:MK_BARRIER_ARMOR
	[EFFECT]  NAME:MK_BARRIER_ARMOR    TYPE:PERCENT DAMAGE TAKEN  DAMAGE_TYPE:ALL  MIN/MAX:-60  LINK_NAME:MK_BARRIER_STRENGTH
	[EFFECT]  NAME:MK_BARRIER_STRENGTH TYPE:PERCENT DAMAGE TAKEN  DAMAGE_TYPE:FIRE MIN/MAX:-70

20 点冰系小盾是引信:只有冰伤能碎它,碎了整套 -60% / -70% 塌陷。原版用它做「用弱点元素打破靖魔者结界」。写法见 MEDIA/AFFIXES/CHAMPIONAFFIXES/CHAMP_BARRIER_FIRE.DAT

注:dotShield 修正之后,DoT 也会真扣盾(按通道匹配),所以冰系 DoT 同样能烧断这根引信;PHYSICAL 盾不再吸元素 DoT,与直伤一致。

3. 运行时扩展:碎盾 → 挂 affix(→ 施放任意技能)

引擎侧缺的只是「碎裂那一刻的回调」。我们已经站在 consumeAbsorbBuffers 的 detour 里(dotShield),补一个约束很小的机制:

  • 约定:SHIELD BUFFER 效果的 NAMEMK_ONBREAK_ 开头,后缀即碎盾时要挂到持有者身上的 affix 名,例如 NAME:MK_ONBREAK_MK_GLASS_NOVA
  • 检测:所有 bCommit=1 的调用(直伤走 applyArmorSoakAndAbsorb,DoT 走我们的换算)返回后,若发生过吸收,扫一遍该单位 DYNAMIC 表里 type==122 && 池==0 && flags&0x20 && 名字前缀匹配 的效果 —— 这正是本次被打空的盾。
  • 执行点:不能在吸收函数的循环里挂 affix(那正是在遍历的向量)。入队(单位 64 位实例 GUID + affix 名),下一帧 EndScene 主线程按掉落过滤同一套身份校验后调 CUnit::attachAffixByName(0x513C70,__thiscall(unit, const wstring& name, int level, CUnit* source, float, float, float);原版 procElementalStatusFromDamage 调它挂 DAMAGEEFFECTONFIRE 时传 level=[src+0x110]source、三个 1.0)。
  • effect 侧:被挂的 affix 里放 TYPE:CAST SKILL(效果 74,DURATION:INSTANT)—— executeInstantEffect 的 case 74 当场施放,爆炸 / 治疗 / 冲击波 / 加限时 buff 都是一条技能的事;也可以只放一个带 UNITTHEME 的 3 秒效果,配 §2.1 的 NOTTHEME 门就是「碎后 3 秒窗口」。原版有现成粒子 MEDIA/PARTICLES/SHIELDBREAK.LAYOUT
  • 联机:与 dotShield 同,只在 isLocallyAuthoritative(unit) 的一端做;客户端等同步。
  • 规模约 80 行,复用掉落过滤的延迟队列与身份闸。

4. 复核依据

  • docs/dotshield-audit-2026-09-13-evidence/(runtime 仓库):ida-0x51feb0.cida-0x7a6430.cida-0x7ae660.cida-0x7af2a0.cida-0x7ae480.c;活体 IDA 复核一致。
  • 游戏事件字面量表:活体 IDA 0x213DF48–0x213E184。
  • ADD STAT=146 未出现在 0x566DD0 的 cmp [reg+1Ch], imm 表中;EFFECTSLIST.DAT 第 146 行。
  • 原版数据:CHAMP_BARRIER_FIRE.DATCHAMP_SHIELD_FIRE.DATEMBERSHIELD12.DATRAILMAN_FORCEFIELD15.DATSKILLS/CHAMPIONS/SHIELDS/REACTIVESHIELD.DAT;STATS/CHAMPSHIELD.DAT