专长迁移完成:27 行 triggers 订阅形态全量生效+旧 23 挂点枚举消亡+消费点拆除+断言面随迁(T3——内容数据驱动扩展批)

接手前任中断的 T3 半成品(脏文件三件:battle_orchestrator/effects engine/feats.tres),方向核实无误后续做收口;
承接修正四处随本提交留痕:①perfect_defense 的 when 用了 fact 封闭 9 项外的 flags_hit_all(装载校验必红)——
改 flags_count gte 1 保守判定,全中闸=flag_settle 发射点施加面承载(载荷字段仅供观测);②has 算子语义与行
不匹配(数组包含对 String 恒假)——落非空在场判定(value 缺省)+数组包含(value 给定),查询面落法=代拍板
白名单;③前任漏写 _harmful_adjacent 助手(flag_hit 发射点调用必炸);④counter_add set 键语义 bool() 吞 0
(清零行 set:0 被当 false 走加零通道)——改 has(set) 置值模式,四处施加点同修。

本提交主干(专长族全量迁移——任务书 §六 T3):
- damage_controller:feat_flags 旗面整体退役,五事件施加面接线——attack_commit 命中随单据 context 携带
  (发射点=开单入口,任务书 §3.1 定稿位;主角攻击单恒段内首单,开单现场=结账现场),rider/改写/加值/
  段数/状态五族按迁移前算序施加(重锤两拍/专注/spike 恒全额/猎弱移档/魔网湛蓝猎手克制直调/整备每层
  +1/厚积满层倍乘/奥术+猎巢+执拗 rider 合计后档位暴击);armor_roll 免掷恒折扣、clamp_check 双面
  (hero+board_typed)delta 窗口、kill 溢出弹射(landed×value%)、damage_taken shield 加法族合计。
  _feat_clamp_bonus/13 处旗消费点拆除。
- battle_orchestrator:_combat_segment 换代(dig_streak/charge_layers 注伤害控制器运行态;连珠矢段数
  施加位紧随主角攻击单开立=迁移前开单位,flush 兑现序逐位一致);round_start 发射位前移至行动经济轮
  切换前(树规则 grant_ap queue 当轮入队当轮兑现——先手压制首轮语义锚);深挖快照滚动拆除(计数器
  reset_at 声明承载);熔火/连击旧计数变量退役。
- action_economy:feat_ap_carry/feat_dig_extra/deep_paid_count 旧注入旗拆除;dig_paid 施加面落地
  (counter_spend 对价:AP 富余付全价积计数、不足 fallback 只付基础 1——2026-10-04 降级口径逐位保留)。
- feat_table schema:HOOKS 23 挂点枚举/MINE_HOOKS/EFFECT_KEYS/hook+effects 双形态校验全删——triggers
  订阅形态唯一合法行形(行键白名单收口 feat_id/display_name/rarity/archetype/triggers/desc/note)。
- feats_engine:hooks_for 挂点投影退役(订阅装载序=feats 表序,由效果引擎 rebuild 承载)。
- battle_narrator:专长卡片文案表值化(summaries 硬编码映射拆除,读行 desc——27 件措辞逐字迁移)。
- smoke 断言面随迁(任务书 v1.1 补列项):构造表 5 行换代 triggers 形态;挂点投影断言换代订阅装载面;
  ④-⑧ 求值断言(_feat_flags_for 直呼+旗读数)全量改写为 rebuild+dispatch 命中面(27 件逐事件覆盖);
  深挖钻头付费开掘回归闸改 counter_spend 计数器面;特性卡 desc 直读断言新增。

映射表对拍注记(行为等价优先,.tres 尾注同步留痕):猎巢人/矿工的执拗映射表标 modify_ticket/attack_bonus,
现行为必中附加通道(hero_rider 后置于乘区与下限)——落 add_ticket_rider(per 取数载荷);整备军备现行为
双效果(轮末转蓄力+每层首段攻 +1)——两行订阅;深挖钻头 per-round 计数器对(paid/given)复刻旧 v2
跨轮不对称语义(given 不随轮重置)。

验证:冒烟 800 项 0 失败(三闸过)· 单元 17 套全绿 · S1 魔法流对拍预检=MC formed/off 两态与 T0 pre 锚
(83000 种子段)同种子逐位一致(2×5 局,三档只测不判)——14 件迁移行实战斗承载零分岔。

设计权威:design/research/研究-内容数据驱动扩展架构-2026-10-06.md(案乙)。
任务书:design/research/任务书-内容数据驱动扩展批-2026-10-06.md §六 T3、§五映射表、§七契约 2/4。
This commit is contained in:
DKChan committed 2026-10-06 23:20:05 +08:00
1 parent 225765f3f7
commit e28a6cb3c4
9 files changed
+982 -676

No files matched your search

+166 -126
View File
@@ -60,13 +60,16 @@ var _outcomes := {} # ticket_id → "redeemed"/"zero"/"voided"(
var _seq := 0 # 流水号
var _halted := false # 主角倒下终止旗(死亡事件置位——flush 余单作废)
var _hero_ac_override := -1 # 主角护甲等级合流入参(begin_battle 显式设定——-1 = 表值兜底,口径同战斗编排器)
# 效果引擎(内容数据驱动扩展批 T1——attack_commit/clamp_check/armor_roll/kill/damage_taken 发射面;
# 双轨过渡期仅观测命中,施加面仍走 feat_flags 旧消费点)
# 效果引擎(内容数据驱动扩展批 T1/T3——attack_commit/clamp_check/armor_roll/kill/damage_taken
# 发射+施加面:dispatch 返回命中,本类按组合规则施加——T3 起旧 feat_flags 旗消费点全部拆除)
var _fx: EffectEngine = null
# 特性求值旗(迭代 12 T2——begin_battle 编排器经 FeatsEngine 求值后注入;空字典=零扰动。
# rider_flat=英雄侧必中附加值〔奥术余烬族〕/rider_crit=档二 rider 可暴击/first_dig_spell=每战首次
# 开安全格追加魔法矢〔消费在编排器授予段〕/clamp_magic_light·clamp_magic_heavy=克制点直调)
var feat_flags: Dictionary = {}
# 战斗段运行态事实(编排器每轮刷新——fact 取数面:attack_commit 载荷 dig_streak /
# charge_layers facts〔猎巢人 per/整备 attack_bonus per/厚积薄发 when/铁壁 shield per〕;
# 流派档位两运行态〔magic 档一 rider+1 / 档二 rider 可暴击〕)
var dig_streak := 0
var charge_layers := 0
var rider_tier_bonus := 0
var rider_tier_crit := false
func _init(db_ref, rng_ref, units_ref, effects_ref) -> void:
@@ -87,10 +90,38 @@ func set_fx(fx: EffectEngine) -> void:
_fx = fx
# 事件发射薄面(发射点统一入口——订阅空=零循环零扰动)
# 事件发射薄面(发射+命中收集统一入口——订阅空=零循环零扰动;命中由各施加面按组合规则施加)
func _fx_hits(event: String, payload: Dictionary) -> Array:
if _fx == null:
return []
return _fx.dispatch(event, payload, Callable(self, "_fx_fact"))
# 事件发射薄面(免施加观测位——enemy_hp_threshold 等现无订阅者的事件保留零扰动面)
func _fx_dispatch(event: String, payload: Dictionary) -> void:
if _fx != null:
_fx.dispatch(event, payload)
_fx_hits(event, payload)
# 补充事实面(fact 取值第二优先源——payload 未携带的运行态读数:charge_layers 编排器每轮注入)
func _fx_fact(fact: String):
match fact:
"charge_layers":
return charge_layers
"dig_streak":
return dig_streak
return null
# per 取数(per 字段解析——载荷同名字段优先,回落补充事实面;add_ticket_rider/attack_bonus/
# shield/open_ticket 的倍数承载:贡献值 = value × per 读数,cap 对乘积封顶)
func _per_value(name: String, payload: Dictionary) -> int:
if name == "":
return 1
if payload.has(name):
return int(payload[name])
if _fx_fact(name) != null:
return int(_fx_fact(name))
return 0
# 巢穴控制数(fact nest_count 取数面——敌方 weaken 实例数,猎巢人订阅面)
@@ -104,12 +135,28 @@ func _nest_count() -> int:
# ---- 开单面(流程调用——编排器六调用点的唯一形态)----
# 开单:A 模式当场结账;B 模式入队待 flush。返回流水号(outcome() 查询键)。
func submit(t: Dictionary) -> int:
_seq += 1
t["ticket_id"] = _seq
_outcomes[_seq] = "pending"
if policy == POLICY_IMMEDIATE:
_resolve(t)
else:
_queue.append(t)
return _seq
# 单据构造(缺省填齐——调用点只写差异栏;context = kind 专用:enemy_skill 带 skill_id/baseline,
# board_flat_hero 的键并入行载荷,board_blast/board_typed 带 batch_key 供未生效行归因)
# board_flat_hero 的键并入行载荷,board_blast/board_typed 带 batch_key 供未生效行归因)。
# attack_commit 发射点(内容数据驱动扩展批 T3——我方伤害单据开立入口,任务书 §3.1 定稿位;
# 命中挂单据 context 随行——施加面在 _resolve_hero_attack 管线算术前逐族施加,开单时点=主角攻击
# 单恒为段内首单(结账现场与开单现场一致)。载荷 atk/target/matchup + 挖掘 streak 事实〔连珠矢/
# 执拗订阅面〕+巢穴控制数〔猎巢人〕)
func open_ticket(kind: String, source_class: String, display: String, channel: String, base_value: int,
target_rule: String, ri: int, origin_cell: Vector2i = Vector2i(-1, -1), attack_type: String = "",
rider: int = 0, highlight: String = "", context: Dictionary = {}) -> Dictionary:
return {
var t := {
"kind": kind,
"source_class": source_class,
"origin_cell": origin_cell,
@@ -125,18 +172,16 @@ func open_ticket(kind: String, source_class: String, display: String, channel: S
"requires": -1, # 前置单据 id(-1 = 无依赖)
"source_unit": {}, # enemy_skill 专用——来源敌句柄(作废判定 + commit)
}
# 开单:A 模式当场结账;B 模式入队待 flush。返回流水号(outcome() 查询键)。
func submit(t: Dictionary) -> int:
_seq += 1
t["ticket_id"] = _seq
_outcomes[_seq] = "pending"
if policy == POLICY_IMMEDIATE:
_resolve(t)
else:
_queue.append(t)
return _seq
if kind == KIND_HERO_ATTACK:
var tgt = _target_for(target_rule)
var armor_b: String = String(tgt.get("armor_type", "light")) if tgt != null else "light"
var matchup_o: String = (attack_type + "_vs_" + armor_b) if attack_type != "" else ""
var ac_payload := {"atk": int(base_value),
"target": String(tgt.get("display_name", "")) if tgt != null else "",
"matchup": matchup_o, "dig_streak": dig_streak, "nest_count": _nest_count()}
t.context["fx_attack_hits"] = _fx_hits("attack_commit", ac_payload)
t.context["fx_attack_payload"] = ac_payload
return t
# 同步开单(清图批专用——完美第二份与高光份数的流程依赖需要同步读数,开单权归流程 用户裁决③):
@@ -212,6 +257,8 @@ func _resolve(t: Dictionary) -> void:
# 主角先手攻击:掷点 → 折扣/暴击 → 克制点数 → hero_offense×enemy_defense 池乘区 → 下限 → 交付
# (内容数据驱动扩展批 T3——attack_commit/clamp_check/armor_roll 订阅施加面:命中=开单入口随单
# context 携带,本函数按迁移前算序逐族施加——命中序=数据表声明序,确定性)
func _resolve_hero_attack(t: Dictionary) -> void:
var target = _target_for(String(t.target_rule))
if target == null:
@@ -220,56 +267,70 @@ func _resolve_hero_attack(t: Dictionary) -> void:
var armor: String = String(target.get("armor_type", "light"))
var atk_type: String = String(t.attack_type)
var matchup := (atk_type + "_vs_" + armor) if atk_type != "" else ""
# attack_commit 发射点(内容数据驱动扩展批 T1——我方伤害单据开立、管线算术前;载荷 atk/target/
# matchup + 挖掘 streak 事实〔连珠矢/执拗订阅面〕+巢穴控制数〔猎巢人〕)
_fx_dispatch("attack_commit", {"atk": int(t.base_value), "target": String(target.display_name),
"matchup": matchup, "dig_streak": int(feat_flags.get("dig_streak", 0)), "nest_count": _nest_count()})
var ac_hits: Array = (t.context.get("fx_attack_hits", []) as Array)
var ac_payload: Dictionary = (t.context.get("fx_attack_payload", {}) as Dictionary)
var roll_read: Dictionary = _armor_roll()
var threshold: int = defender_ac(target) + int(db.settlement.attack_bonus)
# armor_roll 发射点(内容数据驱动扩展批 T1——敌护甲掷骰判定;免掷恒折扣订阅面)
_fx_dispatch("armor_roll", {"target_ac": threshold, "roll": int(roll_read.roll)})
# 矿工重锤·命中交换(迭代 12 T4 普攻流——正负双修原生形态:命中判定减成 trade_hit 点
# 〔更易命中〕换攻击增伤 trade_atk%——高 AC 负期望/低 AC 正期望)
var trade_hit := int(feat_flags.get("hit_trade_hit", 0))
var ar_hits := _fx_hits("armor_roll", {"target_ac": threshold, "roll": int(roll_read.roll)})
# 按序改写族第一拍:矿工重锤命中交换(正负双修原生形态:命中判定减成 trade_hit 点〔更易命中〕
# 换攻击增伤 trade_atk%——高 AC 负期望/低 AC 正期望;改写位先于掷点折扣读数,与迁移前一致)
var trade_hit := 0
for h in ac_hits:
if String(h.get("kind", "")) == "modify_ticket" and int(h.get("trade_hit", 0)) > 0:
trade_hit += int(h.trade_hit)
if trade_hit > 0:
threshold = maxi(1, threshold - trade_hit)
var crit: bool = bool(roll_read.crit)
var discount: int = _roll_discount(int(roll_read.roll), threshold)
# 专注(迭代 12 T4 普攻流——免掷骰恒定折扣:方差买断成期望——稳定器人格;roll 记 0 归因)
var rewrite := int(feat_flags.get("armor_roll_rewrite", 0))
if rewrite > 0:
discount = rewrite
crit = false
roll_read["roll"] = 0
# 厚积薄发满层必中(迭代 12 T5 蓄力流——spike ready 时 discount 恒全额、暴击面走倍乘不加掷)
if int(feat_flags.get("spike_mult", 0)) > 0 and int(feat_flags.get("charge_layers", 0)) >= 3:
discount = 100
# 猎弱本能(迭代 12 T4 穿刺流——对轻甲敌折扣档改善 value 档:对冲 pierce 列 L1 −1 开局罚;
# 按序改写族第二拍:专注免掷骰恒定折扣(方差买断成期望——稳定器人格;roll 记 0 归因)
for h in ar_hits:
if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "armor_rewrite":
discount = int(h.get("value", 0))
crit = false
roll_read["roll"] = 0
# 按序改写族第三拍:厚积薄发满层必中(spike ready 时 discount 恒全额、暴击面走倍乘不加掷;
# when charge_layers≥3 在 dispatch 判定——满层持续状态口径)
var spike_ready := false
for h in ac_hits:
if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "spike":
spike_ready = true
discount = 100
# 按序改写族第四拍:猎弱本能对轻甲敌折扣档改善 value 档(对冲 pierce 列 L1 −1 开局罚;
# 改善 = 折扣表向全额方向移档,nat 最大面/已全额不重复)
var shift := int(feat_flags.get("discount_shift", 0))
var shift := 0
var clamp_hits := _fx_hits("clamp_check", {"matchup": matchup})
for h in clamp_hits:
if String(h.get("kind", "")) != "modify_ticket":
continue
if String(h.get("mode", "")) == "discount_shift":
shift += int(h.get("value", 0))
if shift > 0 and armor == "light" and discount < 100:
discount = _discount_shift_up(discount, shift)
# clamp_check 发射点(内容数据驱动扩展批 T1——克制关系判定修正点;魔网/湛蓝/猎手订阅面)
if atk_type != "":
_fx_dispatch("clamp_check", {"matchup": matchup})
var cdelta: int = _counter_delta(atk_type, armor) + _feat_clamp_bonus(atk_type, armor)
# 克制点数读值+订阅改写(魔网织者/湛蓝誓约/猎手刺剑 clamp_check modify delta——克制矩阵查表值
# 后的直调;hero_attack 与 board_typed 两消费面同享)
var cdelta: int = _counter_delta(atk_type, armor)
for h in clamp_hits:
if String(h.get("kind", "")) == "modify_ticket" and h.has("delta"):
cdelta += int(h.delta)
# 淬毒刃纹(迭代 11 同调批 T5——底稿 §3:对被克制敌人的穿刺/魔法直伤 +N;条件 = 克制成立 ×
# 攻击类型限定,主角攻击面专属——棋盘克制直伤走 _resolve_board_typed 不吃本加成)
if attune_venom > 0 and (atk_type == "pierce" or atk_type == "magic") and cdelta > 0:
cdelta += attune_venom
var base_value := float(t.base_value)
var trade_atk := int(feat_flags.get("hit_trade_atk", 0))
if trade_atk > 0:
base_value *= 1.0 + float(trade_atk) / 100.0 # 重锤增伤面(交换的正向侧)
# 蓄力流(迭代 12 T5):整备军备「每层下轮首段 +1」(层数×value 加算)+厚积薄发满层(≥3)
# spike 倍乘+强制必中(discount 100/crit 关闭掷骰面——方差买断的爆发出口)
var charge_layers := int(feat_flags.get("charge_layers", 0))
if charge_layers > 0:
base_value += float(charge_layers * int(feat_flags.get("ap_carry_value", 0)))
var spike_mult := int(feat_flags.get("spike_mult", 0))
var spike_ready := spike_mult > 0 and charge_layers >= 3
# 按序改写族第五拍:矿工重锤增伤面(交换的正向侧)
for h in ac_hits:
if String(h.get("kind", "")) == "modify_ticket" and int(h.get("trade_atk", 0)) > 0:
base_value *= 1.0 + float(int(h.trade_atk)) / 100.0
# 加法族:整备军备「每层下轮首段 +1」(层数×value 加算)——attack_bonus per charge_layers
for h in ac_hits:
if String(h.get("kind", "")) == "attack_bonus":
base_value += float(int(h.get("value", 0)) * _per_value(String(h.get("per", "")), ac_payload))
# 任意真族:厚积薄发满层 spike 倍乘(≥3 层——强制必中已在折扣拍承载)
if spike_ready:
base_value *= float(spike_mult)
for h in ac_hits:
if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "spike":
base_value *= float(int(h.get("value", 1)))
var rolled: int = DamageMath.rolled_component(base_value, discount, crit, int(db.settlement.crit_multiplier), cdelta)
var payload: Dictionary = {"target": String(target.display_name)}
payload[atk_type] = rolled # 分量键 = 实际攻击类型(归因字段,单分量模型)
@@ -280,56 +341,49 @@ func _resolve_hero_attack(t: Dictionary) -> void:
payload["crit"] = true
var pooled: int = DamageMath.pool_damage(rolled, clamped_pool("hero_offense"), clamped_pool("enemy_defense"))
var landed: int = maxi(int(db.settlement.damage_floor), pooled)
# 英雄侧 rider(迭代 12 T2 魔法流——enemy_skill rider 对称位补齐):后置必中附加,独立于掷点
# 与乘区(奥术余烬「普攻命中后恒定结算」语义);档二(magic 4 件)rider 可暴击=当次 crit 时
# rider 值乘暴击倍率。rider 不改变下限语义(landed 含 rider 后不再过 floor)。
var hero_rider := int(feat_flags.get("rider_flat", 0))
# 猎巢人(迭代 12 T3 标旗流——巢穴控制数转伤害光环):控制数 = 敌方 weaken(削弱)实例数
# 〔八型批 side-wide 状态行——标中旗控制巢穴的既有承载;原设计「对其中敌伤害」简化为全体
# 先手攻击附加值——台账披露待追认〕,每控制 1 巢 rider +Z。
var nest_aura := int(feat_flags.get("nest_aura_value", 0))
if nest_aura > 0:
for eff in effects.query_active("enemy"):
if String(eff.effect_id) == "weaken":
hero_rider += nest_aura
# 矿工的执拗(迭代 12 T4 普攻流——「排雷排得多好,打得就有多疼」):本轮安全开格 streak
# 〔编排器授予段实时写入 feat_flags.dig_streak;踩雷清零〕×value,上限 cap。
var rdig := int(feat_flags.get("round_dig_value", 0))
if rdig > 0:
hero_rider += mini(int(feat_flags.get("round_dig_cap", 5)), int(feat_flags.get("dig_streak", 0)) * rdig)
# 加法族:英雄侧 rider(enemy_skill rider 对称位补齐口径)——后置必中附加,独立于掷点与乘区
# (奥术余烬「普攻命中后恒定结算」语义);猎巢人巢控光环(per nest_count)/矿工的执拗开格加成
# (per dig_streak、cap 封顶)同通道合计;档一质变 rider+1 / 档二(crit 时)rider 乘暴击倍率。
# rider 不改变下限语义(landed 含 rider 后不再过 floor)。
var hero_rider := rider_tier_bonus
for h in ac_hits:
if String(h.get("kind", "")) == "add_ticket_rider":
var contrib := int(h.get("value", 0)) * _per_value(String(h.get("per", "")), ac_payload)
if h.has("cap"):
contrib = mini(int(h.cap), contrib)
hero_rider += contrib
if hero_rider > 0:
if crit and bool(feat_flags.get("rider_crit", false)):
if crit and rider_tier_crit:
hero_rider = int(round(float(hero_rider) * float(db.settlement.crit_multiplier)))
landed += hero_rider
payload["hero_rider"] = hero_rider
units.apply_damage(target, landed)
payload["killed"] = not target.alive # 击杀标记在扣血后取(原语义不变)
# kill 发射点(内容数据驱动扩展批 T1——敌人死亡确认;贯穿射击 overflow 订阅面)
# kill 发射+施加(内容数据驱动扩展批 T3——敌人死亡确认;贯穿射击溢出弹射订阅面:对下一存活敌
# 以穿刺类型弹射本体 value%〔无次敌=弹射落空〕。B 模式入队段尾 flush 兑现;A 模式递归 resolve)
if bool(payload.get("killed", false)):
_fx_dispatch("kill", {"target": String(target.display_name), "overflow": landed})
# 碎甲震击(迭代 12 T4 普攻流——命中重甲附加「护甲裂解」):复用 off_guard(enemy percent
# +25 受伤加深——零新状态行);apply_effect 域闭/重复授予由状态容器自行守卫。
var crack := int(feat_flags.get("crack_value", 0))
if crack > 0 and armor == "heavy":
effects.apply_effect("off_guard", {"source_class": "feat_armor_crack"}, -1)
payload["cracked"] = true
for h in _fx_hits("kill", {"target": String(target.display_name), "overflow": landed}):
if String(h.get("kind", "")) != "overflow":
continue
var next_target = units.default_target()
if next_target != null:
var of_t := open_ticket(DamageController.KIND_BOARD_TYPED, "feat_pierce_shot",
"贯穿射击", "board", maxi(int(db.settlement.damage_floor), int(landed * int(h.get("value", 50)) / 100)),
DamageController.TARGET_FRONT_ALIVE, int(t.round_index), t.origin_cell, "pierce")
if policy == POLICY_SEGMENT:
_queue.append(of_t)
else:
_resolve(of_t)
# 即时执行族:碎甲震击打重甲附加「护甲裂解」(复用 off_guard——零新状态行;apply_effect 域闭/
# 重复授予由状态容器自行守卫)
for h in ac_hits:
if String(h.get("kind", "")) == "apply_status":
effects.apply_effect(String(h.get("status_id", "")), {"source_class": "feat_armor_crack"}, -1)
payload["cracked"] = true
var label := _attack_label(String(t.display), crit, discount)
if hero_rider > 0:
label += "+奥术%d(必中)" % hero_rider
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), label, landed, payload, String(t.highlight))
# 贯穿射击(迭代 12 T4 穿刺流——击杀溢出弹射):击杀时对下一存活敌以穿刺类型弹射
# 本体半额〔原设计「溢出伤害」追踪在单分量模型不可行——半额弹射 provisional 裁定披露待追认;
# 无次敌=弹射落空〕。B 模式入队段尾 flush 兑现;A 模式递归 resolve(目标存活守卫内建)。
if bool(payload.get("killed", false)) and int(feat_flags.get("kill_overflow", 0)) > 0:
var next_target = units.default_target()
if next_target != null:
var of_t := open_ticket(DamageController.KIND_BOARD_TYPED, "feat_pierce_shot",
"贯穿射击", "board", maxi(int(db.settlement.damage_floor), int(landed / 2)),
DamageController.TARGET_FRONT_ALIVE, int(t.round_index), t.origin_cell, "pierce")
if policy == POLICY_SEGMENT:
_queue.append(of_t)
else:
_resolve(of_t)
_outcomes[int(t.ticket_id)] = "redeemed"
@@ -344,14 +398,14 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
var rolled: int = DamageMath.rolled_component(float(t.base_value), discount, crit, int(db.settlement.crit_multiplier), 0)
var pooled: int = DamageMath.pool_damage(rolled, clamped_pool("hero_defense"), clamped_pool("enemy_output"))
var total: int = maxi(int(db.settlement.damage_floor), pooled) + int(t.rider) # rider 后置于伤害下限——独立必中加项,不进乘区
# damage_taken 发射点(内容数据驱动扩展批 T1——kind=enemy_skill;铁壁减伤订阅面)
_fx_dispatch("damage_taken", {"kind": "enemy_skill", "amount": total,
"source": String(t.context.get("skill_id", "")), "round": int(t.round_index)})
# 铁壁整备(迭代 12 T5 蓄力流——每层下轮开场 S 点减伤;减后过下限保底)
var shield := int(feat_flags.get("shield_per_layer", 0)) * int(feat_flags.get("charge_layers", 0))
# 稳健拓荒(迭代 12 T10——Fork posture B 侧 rule 键:受击减 1;两侧对称弱值=「不同强非强弱」)
if int(feat_flags.get("bulwark", 0)) > 0:
shield += 1
# damage_taken 发射+施加(内容数据驱动扩展批 T3——kind=enemy_skill;铁壁整备减伤订阅面:
# shield 加法族合计 value×per(charge_layers),稳健拓荒树规则同面(rebuild 合成订阅);
# 减后过下限保底)
var shield := 0
for h in _fx_hits("damage_taken", {"kind": "enemy_skill", "amount": total,
"source": String(t.context.get("skill_id", "")), "round": int(t.round_index)}):
if String(h.get("kind", "")) == "shield":
shield += int(h.get("value", 0)) * _per_value(String(h.get("per", "")), {})
if shield > 0:
total = maxi(int(db.settlement.damage_floor), total - shield)
units.apply_damage(hero, total)
@@ -378,14 +432,17 @@ func _resolve_board_typed(t: Dictionary) -> void:
return
var armor_b: String = String(target.get("armor_type", "light"))
var atk_b: String = String(t.attack_type)
# clamp_check 发射点(内容数据驱动扩展批 T1——克制判定修正点棋盘面;与 hero_attack 同表同点)
# clamp_check 发射+施加(内容数据驱动扩展批 T3——克制判定修正点棋盘面;与 hero_attack 同表同点:
# modify delta 订阅改写合计进克制点数;discount_shift 档位面属主角攻击专享,本面不施加)
var cdelta: int = _counter_delta(atk_b, armor_b)
if atk_b != "":
_fx_dispatch("clamp_check", {"matchup": atk_b + "_vs_" + armor_b})
var delta: int = _counter_delta(atk_b, armor_b) + _feat_clamp_bonus(atk_b, armor_b)
var dealt: int = DamageMath.counter_component(int(t.base_value), delta, int(db.settlement.damage_floor))
for h in _fx_hits("clamp_check", {"matchup": atk_b + "_vs_" + armor_b}):
if String(h.get("kind", "")) == "modify_ticket" and h.has("delta"):
cdelta += int(h.delta)
var dealt: int = DamageMath.counter_component(int(t.base_value), cdelta, int(db.settlement.damage_floor))
units.apply_damage(target, dealt)
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → %s" % [String(t.display), String(target.display_name)], dealt,
{"target": String(target.display_name), "attack_type": String(t.attack_type), "effectiveness": delta}, String(t.highlight))
{"target": String(target.display_name), "attack_type": String(t.attack_type), "effectiveness": cdelta}, String(t.highlight))
_outcomes[int(t.ticket_id)] = "redeemed"
@@ -462,23 +519,6 @@ func _counter_delta(attack_type: String, armor: String) -> int:
return int(row.get(armor, 0))
# 克制点直调(迭代 12 T2 魔法流/T4 穿刺流 clamp_modifier 件——克制矩阵查表值后的特性调整;
# 空旗=0 零扰动):魔网织者=magic vs magic(矩阵罚 −1 → 0/正);湛蓝誓约=magic vs heavy(+1 → +2);
# 猎手刺剑=pierce vs light(+1——对轻甲敌穿刺提升,对重甲无增益=正负双修)。hero_attack 与
# board_typed 两消费面同享。
func _feat_clamp_bonus(attack_type: String, armor: String) -> int:
var bonus := 0
if attack_type == "magic":
if armor == "magic":
bonus += int(feat_flags.get("clamp_magic_magic", 0))
elif armor == "heavy":
bonus += int(feat_flags.get("clamp_magic_heavy", 0))
elif attack_type == "pierce":
if armor == "light":
bonus += int(feat_flags.get("clamp_pierce_light", 0))
return bonus
# 折扣档改善(迭代 12 T4 猎弱本能——折扣表向全额方向移 value 档;查不到当前档 = 不动防御口径)
func _discount_shift_up(discount: int, shift: int) -> int:
var tbl: Array = db.settlement.roll_tier_discounts