iter51-卡2

This commit is contained in:
DKChan committed 2026-11-04 05:08:31 +08:00
1 parent 1cd6e2b1b0
commit 372b4fdc31
27 files changed
+1231 -356

No files matched your search

+102 -81
View File
@@ -2,24 +2,49 @@ class_name GrantSegment
extends RefCounted
# ① 授予段 Segment(P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
# 职责:结算队列(开格/封印判定/清图)→ 极性路由逐批兑付 + 封印判/军规开单 + round_end 施加 +
# 转存收口 + 揭示兑付 + 段界结账。行发射/开单单点归宿主(oc)——兑付管线归 RewardGrant、
# 效果施加归 BattleFxHost(段持三引用直调,零转发壳)——
# 转存收口 + 揭示兑付 + 段界结账。行发射经 emit_row_cb 回宿主——兑付管线归 RewardGrant、
# 效果施加归 BattleFxHost(段持注入引用直调,零转发壳)——
# 本段只搬运时序,零算法改动(🔴 零行为变更契约:同种子完整局输出逐位一致)。
# 耦合边界(5.1 封装修复换代):引用束注入——构造收编排器组装的 20 键封闭束,本段断言 12 键
# (facts/units/damage/reward_economy/fx_host/minesweeper/db/action_economy/reward_grant/
# emit_row_cb/attune_param_sum_cb/perfect_sweep_cb)解包同名成员;对编排器零引用(跨件单向接线,
# 注入 Callable 亦属接线面);变值标量(round_index)走调用参数现值交付。
# run 编排骨架+批私有族(_reveal_batch/_terrain_batch/_seal_batch/_round_end_apply,段内施加面
# 再拆 _reveal_safe_apply/_reveal_mine_apply/_seal_hit_effects 等单面函数);地形 damage_taken
# 消费随迭代 46 卡A T2 并入伤害控制器全局消费族(本段零本地减伤函数);
# kind 分派单点化(P1-1 同批):counter_add 五位三行组 / grant_material 三位兑付块
# 收 _apply_counter_hit / _apply_material_hit 两助手(分支本体逐字保留在助手内)。
var oc: BattleOrchestrator # 宿主(类型标注=段内 := 推断前提;循环类名引用 Godot 4 全局类安全)
var fx_host: BattleFxHost # 效果引擎宿主件(fx 发射/施加/揭示队列/计数器桶)
var reward_grant: RewardGrant # 奖励授予件(极性路由/行应用/提交面/清图奖励/escrow)
var facts: BattleFacts # 战斗事实件(轮缓冲/封印账/溯源)
var units: UnitContainer # 单位容器(hero_unit 读面)
var damage: DamageController # 伤害控制器(开单/承伤发射面)
var reward_economy: RewardEconomy # 地图战斗资源(grant/query_full_snapshot 消费面)
var fx_host: BattleFxHost # 效果引擎宿主件(fx 发射/施加/揭示队列/计数器桶)
var minesweeper: MinesweeperRules # 排雷玩法(板读取/变异过滤/危险格移除面)
var db: ConfigDb
var action_economy: ActionEconomy
var reward_grant: RewardGrant # 奖励授予件(极性路由/行应用/提交面/清图奖励/escrow)
var emit_row_cb: Callable # 宿主 _emit_row——行协议面单点(5 参 head 束形)
var attune_param_sum_cb: Callable # 宿主 attune_param_sum 读投影——转存率合计读数
var perfect_sweep_cb: Callable # 宿主 perfect_sweep_earned 读投影——清图奖励第二份判定
func _init(host, fx_host_ref: BattleFxHost, reward_grant_ref: RewardGrant) -> void:
oc = host
fx_host = fx_host_ref
reward_grant = reward_grant_ref
func _init(bundle: Dictionary) -> void:
for k in ["facts", "units", "damage", "reward_economy", "fx_host", "minesweeper", "db",
"action_economy", "reward_grant", "emit_row_cb", "attune_param_sum_cb", "perfect_sweep_cb"]:
assert(bundle.has(k), "[授予段] 引用束缺键: %s" % k)
facts = bundle.facts
units = bundle.units
damage = bundle.damage
reward_economy = bundle.reward_economy
fx_host = bundle.fx_host
minesweeper = bundle.minesweeper
db = bundle.db
action_economy = bundle.action_economy
reward_grant = bundle.reward_grant
emit_row_cb = bundle.emit_row_cb
attune_param_sum_cb = bundle.attune_param_sum_cb
perfect_sweep_cb = bundle.perfect_sweep_cb
func run(ri: int) -> void:
@@ -45,10 +70,10 @@ func apply_board_refreshed(ri: int) -> void:
for h in br_hits:
if String(h.get("kind", "")) == "grant_gp":
reward_grant.escrow += int(h.get("value", 0))
oc._emit_row({"type": "effect", "source_class": "fx_grant_gp", "origin_cell": Vector2i(-1, -1)}, ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow})
emit_row_cb.call({"type": "effect", "source_class": "fx_grant_gp", "origin_cell": Vector2i(-1, -1)}, ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow})
for h in br_hits:
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "blast":
var bb_t := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "fx_board_refresh", "崩壁冲击",
var bb_t := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "fx_board_refresh", "崩壁冲击",
{"channel": "board", "base_value": int(h.get("value", 0)), "target_rule": DamageController.TARGET_ALL_ALIVE,
"ri": ri, "origin_cell": Vector2i(-1, -1)})
reward_grant.defer_ticket(bb_t)
@@ -56,7 +81,7 @@ func apply_board_refreshed(ri: int) -> void:
# 开格/踩雷兑付批(轮 facts_reveals 资源事件序:safe 正向乘区兑付 / mine_hit 减半负向兑付)
func _reveal_batch(ri: int) -> void:
for evt in oc._facts.reveals:
for evt in facts.reveals:
if not bool(evt.get("resource", false)):
continue # 封锁格(initial 通道)恒无资源——不入账(战斗结算 义务②)
var snap = evt.get("reward_snapshot")
@@ -75,16 +100,14 @@ func _reveal_batch(ri: int) -> void:
# new_resource_type〔地质学家——快照 cell_type 首见判定,见后集登记〕)
func _reveal_safe(evt: Dictionary, snap: Dictionary, ri: int) -> void:
if bool(evt.get("dug_under_seal", false)):
oc._emit_row({"type": "reward_ineffective", "source_class": "safe_cell", "origin_cell": evt.cell}, ri, "奖励作废(封印格直接挖开——零惩罚零奖励)", 0, {})
emit_row_cb.call({"type": "reward_ineffective", "source_class": "safe_cell", "origin_cell": evt.cell}, ri, "奖励作废(封印格直接挖开——零惩罚零奖励)", 0, {})
return
var board_b: BoardData = oc.minesweeper.current_board()
var board_b: BoardData = minesweeper.current_board()
var ctype := String(snap.get("cell_type", ""))
var is_new_type := ctype != "" and not fx_host._fx_new_resource_types.has(ctype)
if is_new_type:
fx_host._fx_new_resource_types[ctype] = true
var is_new_type := fx_host.mark_new_resource_type(ctype)
var dr_hits := fx_host.fx_emit("dig_reveal", {"cell": evt.cell, "round": ri,
"is_first_dig_of_battle": not oc._facts.feat_first_dig_used,
"terrain_kind": oc._harmful_variants(evt.get("variant_ids", []) as Array),
"is_first_dig_of_battle": not facts.feat_first_dig_used,
"terrain_kind": minesweeper.harmful_variants(evt.get("variant_ids", []) as Array),
"is_edge_cell": evt.cell.x == 0 or evt.cell.y == 0
or evt.cell.x == board_b.width - 1 or evt.cell.y == board_b.height - 1,
"is_blank_zero": board_b.digit_of(evt.cell) == 0,
@@ -96,7 +119,7 @@ func _reveal_safe(evt: Dictionary, snap: Dictionary, ri: int) -> void:
continue
pos_m += int(h.get("mult", 0))
neg_m += int(h.get("cost_mult", 0))
reward_grant.grant_routed(oc._feat_yield_adjust(snap, true, pos_m, neg_m), "positive", {"source_class": "safe_cell", "cell": evt.cell, "ri": ri})
reward_grant.grant_routed(reward_grant.feat_yield_adjust(snap, true, pos_m, neg_m), "positive", {"source_class": "safe_cell", "cell": evt.cell, "ri": ri})
_reveal_safe_apply(dr_hits, evt, ri)
@@ -107,15 +130,15 @@ func _reveal_safe_apply(dr_hits: Array, evt: Dictionary, ri: int) -> void:
for h in dr_hits:
if String(h.get("kind", "")) == "counter_add":
_apply_counter_hit(h)
if not oc._hits_of(dr_hits, "reveal_tiles").is_empty():
if not DamageController.hits_of(dr_hits, "reveal_tiles").is_empty():
fx_host.enqueue_reveal()
for h in dr_hits:
if String(h.get("kind", "")) == "grant_material":
_apply_material_hit(h, evt.cell, ri)
var spell_hits: Array[Variant] = oc._hits_of(dr_hits, "spawn_spell")
if not spell_hits.is_empty() and not oc._facts.feat_first_dig_used:
oc._facts.feat_first_dig_used = true
var spell_t := oc._damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_first_dig_spell", "奥术矢",
var spell_hits: Array[Variant] = DamageController.hits_of(dr_hits, "spawn_spell")
if not spell_hits.is_empty() and not facts.feat_first_dig_used:
facts.feat_first_dig_used = true
var spell_t := damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_first_dig_spell", "奥术矢",
{"channel": "board", "base_value": int((spell_hits[0] as Dictionary).get("value", 0)),
"target_rule": DamageController.TARGET_FRONT_ALIVE, "ri": ri, "origin_cell": evt.cell, "attack_type": "magic"})
reward_grant.submit(spell_t, false)
@@ -128,19 +151,19 @@ func _reveal_mine(evt: Dictionary, snap: Dictionary, ri: int) -> void:
var mine_power := int(snap.get("power", 0))
var mh_hits := fx_host.fx_emit("mine_hit", {"cell": evt.cell, "round": ri,
"damage": mine_power, "dug_under_seal": bool(evt.get("dug_under_seal", false)),
"hp_ratio": int(round(100.0 * float(int(oc._enemy_system.hero_unit().get("hp", 0))) / float(maxi(1, int(oc._enemy_system.hero_unit().get("max_hp", 1))))))})
"hp_ratio": int(round(100.0 * float(int(units.hero_unit().get("hp", 0))) / float(maxi(1, int(units.hero_unit().get("max_hp", 1))))))})
var applied := _reveal_mine_apply(mh_hits, snap, evt.cell, ri)
if int(applied["mine_reduce"]) > 0:
snap["power"] = maxi(0, mine_power - int(applied["mine_reduce"])) # 减伤落快照(负向兑付按减后值——安全帽每层减 1)
for h in mh_hits:
if String(h.get("kind", "")) == "counter_add":
_apply_counter_hit(h)
if not oc._hits_of(mh_hits, "reveal_tiles").is_empty():
if not DamageController.hits_of(mh_hits, "reveal_tiles").is_empty():
fx_host.enqueue_reveal() # 见血开窍——每颗雷一次揭示(授予段末统一兑付)
reward_grant.grant_routed(oc._feat_yield_adjust(snap, false, 0, 0), "negative", {"source_class": "mine_hit", "cell": evt.cell, "ri": ri, "halve_pct": int(applied["halve_pct"])})
reward_grant.grant_routed(reward_grant.feat_yield_adjust(snap, false, 0, 0), "negative", {"source_class": "mine_hit", "cell": evt.cell, "ri": ri, "halve_pct": int(applied["halve_pct"])})
for h in mh_hits:
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "reflect":
var nct := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "attune_nest_counter", "惊巢反制·反弹",
var nct := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "attune_nest_counter", "惊巢反制·反弹",
{"channel": "board", "base_value": int(h.get("value", 0)), "target_rule": DamageController.TARGET_ALL_ALIVE,
"ri": ri, "origin_cell": evt.cell})
reward_grant.submit(nct, false)
@@ -162,15 +185,14 @@ func _reveal_mine_apply(mh_hits: Array, snap: Dictionary, cell: Vector2i, ri: in
else:
mine_reduce += int(h.get("value", 0)) # 背水——定值减免
"heal":
var hu_heal: Dictionary[Variant, Variant] = oc._enemy_system.hero_unit()
var hu_heal: Dictionary[Variant, Variant] = units.hero_unit()
hu_heal["hp"] = mini(int(hu_heal.get("hp", 0)) + int(h.get("value", 0)), maxi(1, int(hu_heal.get("max_hp", 1))))
"bonus_delta":
var axis := String(h.get("axis", ""))
if fx_host._fx_bonus_delta.has(axis):
fx_host._fx_bonus_delta[axis] += int(h.get("delta", 0))
fx_host.add_bonus_delta(axis, int(h.get("delta", 0)))
"blast_scaled":
var blast_v := maxi(int(oc.db.combat.settlement.damage_roll.damage_floor), int(float(int(snap.get("power", 0))) * float(h.get("mult", 1))))
var bs_t := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_blast_scaled", "陷阱爆发",
var blast_v := maxi(int(db.combat.settlement.damage_roll.damage_floor), int(float(int(snap.get("power", 0))) * float(h.get("mult", 1))))
var bs_t := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_blast_scaled", "陷阱爆发",
{"channel": "board", "base_value": blast_v, "target_rule": DamageController.TARGET_ALL_ALIVE,
"ri": ri, "origin_cell": cell})
reward_grant.submit(bs_t, false)
@@ -182,21 +204,21 @@ func _reveal_mine_apply(mh_hits: Array, snap: Dictionary, cell: Vector2i, ri: in
# 危险格恒无变异——布置段排除,双层叠罚不存在;封印格直挖照吃变异伤害——地形后果同 mine_hit 口径,
# 非奖励不作废;吞噬流资源兑付〔荆棘编织/地脉补偿〕中伤害形态行同样经案C闸暂存,非伤害形态照常当场)
func _terrain_batch(ri: int) -> void:
for evt in oc._facts.reveals:
for evt in facts.reveals:
var vids: Array[Variant] = evt.get("variant_ids", [])
for vid in vids:
var vrow: Dictionary[Variant, Variant] = oc.db.combat.variation.row_by_id(String(vid))
var vrow: Dictionary[Variant, Variant] = db.combat.variation.row_by_id(String(vid))
if String(vrow.get("effect", "")) != "flat_damage":
continue # 触发词表按 effect 路由:flat_damage=on_open 兑现;hide_number/on_open 外组合 = 数据行新词表未接线(显式忽略面)
# 地形变异伤害单据——无类型必中,下限保底在控制器管线内
# damage_taken 统一发射(迭代 46 卡A T2——地形路径并入全局消费族 _emit_hero_damage_taken:
# damage_taken 统一发射(迭代 46 卡A T2——地形路径并入全局消费族 emit_hero_damage_taken:
# 发射在单据开立前、减后值开单,组合消费含 shield〔全局受伤语义〕;
# hero_damage_consumed 标记入单据 context——战斗段末 _resolve_board_flat_hero 跳过重复发射;
# grant_material〔荆棘/地脉〕移到单据提交后(兑付值序一致——收益不因减伤变化))
var dt_amount := int(vrow.get("effect_value", 0))
var dt_res := oc._damage._emit_hero_damage_taken(dt_amount, "terrain", String(vid), evt.cell, ri)
var dt_res := damage.emit_hero_damage_taken(dt_amount, "terrain", String(vid), evt.cell, ri)
dt_amount = int(dt_res["total"])
var vt := oc._damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, "地形变异", String(vrow.get("display_name", vid)),
var vt := damage.open_ticket(DamageController.KIND_BOARD_FLAT_HERO, "地形变异", String(vrow.get("display_name", vid)),
{"channel": "board", "base_value": dt_amount, "target_rule": DamageController.TARGET_HERO,
"ri": ri, "origin_cell": evt.cell, "context": {"variant_id": String(vid), "hero_damage_consumed": true}})
reward_grant.submit(vt, false)
@@ -210,7 +232,7 @@ func _terrain_batch(ri: int) -> void:
# 台账披露待追认〕;完美布防=本轮封印批全中(hit>0 且 miss==0)时封印伤 ×mult——全对推理奖励)。
# 伤害单据案C 暂存战斗段末兑付。
func _seal_batch(ri: int) -> void:
for evt in oc._facts.seals:
for evt in facts.seals:
_judge_seal(evt, ri)
_seal_strike_batch(ri)
@@ -220,7 +242,7 @@ func _seal_batch(ri: int) -> void:
func _seal_strike_batch(ri: int) -> void:
var fs_hits := 0
var fs_misses := 0
for fevt in oc._facts.seals:
for fevt in facts.seals:
if bool(fevt.get("hit", false)):
fs_hits += 1
else:
@@ -230,10 +252,10 @@ func _seal_strike_batch(ri: int) -> void:
var strike := _seal_strike_value(fs_fx, fs_hits, fs_misses)
if strike.is_empty():
return
for fevt in oc._facts.seals:
for fevt in facts.seals:
if not bool(fevt.get("hit", false)):
continue
var st_t := oc._damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_seal_strike", "战封印军规",
var st_t := damage.open_ticket(DamageController.KIND_BOARD_TYPED, "feat_seal_strike", "战封印军规",
{"channel": "board", "base_value": int(strike["value"]), "target_rule": DamageController.TARGET_FRONT_ALIVE,
"ri": ri, "origin_cell": fevt.cell, "attack_type": "normal"})
reward_grant.submit(st_t, false)
@@ -272,16 +294,16 @@ func _round_end_apply(ri: int) -> void:
for h in re_fx:
if String(h.get("kind", "")) == "grant_gp":
reward_grant.escrow += int(h.get("value", 0))
oc._emit_row({"type": "effect", "source_class": "fx_grant_gp", "origin_cell": Vector2i(-1, -1)}, ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow})
emit_row_cb.call({"type": "effect", "source_class": "fx_grant_gp", "origin_cell": Vector2i(-1, -1)}, ri, "专长·GP 入账 +%d" % int(h.get("value", 0)), int(h.get("value", 0)), {"effect_id": "fx_grant_gp", "escrow": reward_grant.escrow})
elif String(h.get("kind", "")) == "grant_ap" and String(h.get("mode", "")) == "queue":
oc.action_economy.queue_ap_delta(int(h.get("value", 0)))
action_economy.queue_ap_delta(int(h.get("value", 0)))
# 转存率入参(储备罐 rap_transfer_mult magnitude=目标转存率百分比 125=1.25:1;未装配=100 与 1:1 逐位一致)
var rap_pct: int = oc.attune_param_sum("rap_transfer_mult")
oc.action_economy.finish_round_stop(rap_pct if rap_pct > 0 else 100)
if oc._facts.path == "cleared":
reward_grant.grant_clear_reward(ri, oc._facts.board_from_midround, oc.perfect_sweep_earned())
var rap_pct: int = int(attune_param_sum_cb.call("rap_transfer_mult"))
action_economy.finish_round_stop(rap_pct if rap_pct > 0 else 100)
if facts.path == "cleared":
reward_grant.grant_clear_reward(ri, facts.board_from_midround, bool(perfect_sweep_cb.call()))
fx_host.flush_pending_reveals(ri) # 揭示效果兑付(授予段末统一掷格,防轮 facts 迭代重入)
oc._damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界)
damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界)
# round_end heal value_counter 兑付(迭代 46 卡A T5——坑道小憩:层值=本轮成功刷新累计恢复点,
@@ -294,12 +316,12 @@ func _round_end_heal_value_counter(re_fx: Array, ri: int) -> void:
var layers := EffectEngine.counter(fx_host.counters_ctx, String(h.get("value_counter", "")))
if layers <= 0:
continue
var hu: Dictionary[Variant, Variant] = oc._enemy_system.hero_unit()
var hu: Dictionary[Variant, Variant] = units.hero_unit()
if int(hu.get("hp", 0)) >= int(hu.get("max_hp", 1)):
oc._emit_row({"type": "reward_ineffective", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 未生效(满血)", 0, {"batch_key": String(h.get("value_counter", ""))})
emit_row_cb.call({"type": "reward_ineffective", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 未生效(满血)", 0, {"batch_key": String(h.get("value_counter", ""))})
continue
var healed: int = oc._enemy_system.heal(hu, layers)
oc._emit_row({"type": "heal", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 回复", healed, {"target": "hero"})
var healed: int = units.heal(hu, layers)
emit_row_cb.call({"type": "heal", "source_class": "fx_board_refresh", "origin_cell": Vector2i(-1, -1)}, ri, "坑道小憩 回复", healed, {"target": "hero"})
# round_end blast 通道反打单(per-counter 积层读数×value=反打总量;source_class 用中性名,
@@ -310,10 +332,10 @@ func _round_end_blast_tickets(re_fx: Array, ri: int) -> void:
var mg_layers := EffectEngine.counter(fx_host.counters_ctx, String(h.get("per", "")))
var mg_total := int(h.get("value", 0)) * mg_layers
if mg_total > 0:
var mg_t := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "fx_blast_counter",
var mg_t := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "fx_blast_counter",
"积层反噬 ×%d" % mg_layers,
{"channel": "board", "base_value": mg_total, "target_rule": DamageController.TARGET_ALL_ALIVE,
"ri": ri, "origin_cell": oc.minesweeper.hero_pos()})
"ri": ri, "origin_cell": minesweeper.hero_pos()})
reward_grant.submit(mg_t, false)
@@ -329,12 +351,12 @@ func _judge_seal(evt: Dictionary, ri: int) -> void:
# 殉爆票据 → 施加面 → 雷区图谱额外兑付 → 基线正向兑付)
func _judge_seal_hit(evt: Dictionary, ri: int) -> void:
var cell: Vector2i = evt.cell
if oc._facts.seal_consumed.has(cell):
if facts.seal_consumed.has(cell):
return # 去重跳过;零明细行
oc._facts.seal_consumed[cell] = true
oc._facts.mark_perfect_hit()
facts.seal_consumed[cell] = true
facts.mark_perfect_hit()
var fh_hits := fx_host.fx_emit("seal_hit", {"cell": cell, "round": ri,
"terrain_kind": oc._harmful_adjacent(cell)})
"terrain_kind": minesweeper.harmful_adjacent(cell)})
_seal_burst_tickets(fh_hits, cell, ri)
_seal_hit_effects(fh_hits, cell, ri)
# 雷区图谱(封印流——标中雷额外获得一份格面资源:封印开始产出对标开格;
@@ -342,7 +364,7 @@ func _judge_seal_hit(evt: Dictionary, ri: int) -> void:
for h in fh_hits:
if String(h.get("kind", "")) == "grant_material":
_apply_material_hit(h, cell, ri, "seal_hit")
var snap = oc._reward_economy.grant(cell)
var snap = reward_economy.grant(cell)
if not (snap is Dictionary):
return
reward_grant.grant_routed(snap, "positive", {"source_class": "seal_hit", "cell": cell, "ri": ri, "highlight": "seal_hit"})
@@ -351,10 +373,10 @@ func _judge_seal_hit(evt: Dictionary, ri: int) -> void:
# 标中殉爆票据(行 triggers open_ticket channel blast value_from magnitude——每标中一面封印全体存活敌
# 吃 N 群伤,无类型走 blast 通道;封印爆裂〔专长〕同面消费)
func _seal_burst_tickets(fh_hits: Array, cell: Vector2i, ri: int) -> void:
for h in oc._hits_of(fh_hits, "open_ticket"):
for h in DamageController.hits_of(fh_hits, "open_ticket"):
if String(h.get("channel", "")) != "blast":
continue
var fbt := oc._damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_seal_burst", "封印爆裂",
var fbt := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "feat_seal_burst", "封印爆裂",
{"channel": "board", "base_value": int(h.get("value", 0)), "target_rule": DamageController.TARGET_ALL_ALIVE,
"ri": ri, "origin_cell": cell, "highlight": "highlight"})
reward_grant.submit(fbt, false)
@@ -373,37 +395,36 @@ func _seal_hit_effects(fh_hits: Array, cell: Vector2i, ri: int) -> void:
fx_host.apply_ac_bonus(h)
"bonus_delta":
var axis_f := String(h.get("axis", ""))
if fx_host._fx_bonus_delta.has(axis_f):
fx_host._fx_bonus_delta[axis_f] += int(h.get("delta", 0))
fx_host.add_bonus_delta(axis_f, int(h.get("delta", 0)))
"remove_mines":
var removed: Array[Variant] = oc.minesweeper.remove_mines_in([cell])
var removed: Array[Variant] = minesweeper.remove_mines_in([cell])
if not removed.is_empty():
oc._emit_row({"type": "extended", "source_class": "fx_seal_smith", "origin_cell": cell}, ri, "封印匠·危险格中立移除", 0, {"effect_id": "mine_neutralized"})
emit_row_cb.call({"type": "extended", "source_class": "fx_seal_smith", "origin_cell": cell}, ri, "封印匠·危险格中立移除", 0, {"effect_id": "mine_neutralized"})
"preview_area":
_seal_preview_area(cell, ri)
# 示警烟火施加面(3×3 邻格资源类型预告——类型去重集入 overlay;信息呈现零回写)
func _seal_preview_area(cell: Vector2i, ri: int) -> void:
var board_p: BoardData = oc.minesweeper.current_board()
var board_p: BoardData = minesweeper.current_board()
var types: Array[Variant] = []
for n in board_p.neighbors8(cell):
var n_snap = oc._reward_economy.query_full_snapshot(n)
var n_snap = reward_economy.query_full_snapshot(n)
if n_snap is Dictionary and not types.has(String((n_snap as Dictionary).get("cell_type", ""))):
types.append(String((n_snap as Dictionary).get("cell_type", "")))
if reward_grant.treasure_inventory != null and not types.is_empty():
reward_grant.treasure_inventory.apply_overlay("reward_preview", board_p.neighbors8(cell))
oc._emit_row({"type": "extended", "source_class": "fx_warning_beacon", "origin_cell": cell}, ri, "示警烟火·周围资源类型", 0, {"effect_id": "fx_preview_area", "types": types})
emit_row_cb.call({"type": "extended", "source_class": "fx_warning_beacon", "origin_cell": cell}, ri, "示警烟火·周围资源类型", 0, {"effect_id": "fx_preview_area", "types": types})
# 封印未中(每格实例一次去重——归因行与负向兑付同键,防逐轮惩罚轰炸;seal_missed 发射序:
# grant_ap mode:queue 下一轮兑现 / forgive_seal_miss 完美保险豁免 → 归因行 → 负向兑付)
func _judge_seal_miss(evt: Dictionary, ri: int) -> void:
var cell: Vector2i = evt.cell
oc._facts.mark_perfect_miss()
if oc._facts.seal_miss_seen.has(cell):
facts.mark_perfect_miss()
if facts.seal_miss_seen.has(cell):
return # 每格实例一次去重
oc._facts.seal_miss_seen[cell] = true
facts.seal_miss_seen[cell] = true
# 危险直觉·守挂点(seal_missed 发射:契约 12 触发主语=「当回合发生的标错
# 动作」——去重闸=新旧标错判别器〔后续轮重判旧封印被闸挡回零触发〕;双条件②「结算时封印仍在
# 场」由发射面既有过滤承载〔被挖开的封印已随挖掘擦除零兑付〕;grant_ap mode:queue 下一轮
@@ -411,17 +432,17 @@ func _judge_seal_miss(evt: Dictionary, ri: int) -> void:
# adventure 域计数器闸)
for h in fx_host.fx_emit("seal_missed", {"cell": cell, "round": ri}):
if String(h.get("kind", "")) == "grant_ap" and String(h.get("mode", "")) == "queue":
oc.action_economy.queue_ap_delta(int(h.get("value", 0)))
action_economy.queue_ap_delta(int(h.get("value", 0)))
elif String(h.get("kind", "")) == "forgive_seal_miss":
# 完美保险(订阅式豁免:本战首次标错计一次豁免,只累加自有账
# _perfect_forgiven,绝不回写 _perfect_hit_false=契约 2 计数纯净;发射时机与消费点零改动=契约 3)
oc._facts.mark_perfect_forgiven()
facts.mark_perfect_forgiven()
_apply_counter_hit(h) # 豁免同时记账(insurance_used battle 域——下次标错 when lt 1 不再命中)
oc._emit_row({"type": "extended", "source_class": "seal_miss", "origin_cell": cell}, ri, "完美保险·标错豁免", 0,
{"effect_id": "insurance_used", "forgiven": oc._facts.perfect_forgiven})
var hl := "perfect_broken" if oc._facts.perfect_hit_false - oc._facts.perfect_forgiven == 1 else ""
oc._emit_row({"type": "seal_miss", "source_class": "seal_miss", "origin_cell": cell, "highlight": hl}, ri, "封印未中", 0, {})
var snap = oc._reward_economy.grant(cell)
emit_row_cb.call({"type": "extended", "source_class": "seal_miss", "origin_cell": cell}, ri, "完美保险·标错豁免", 0,
{"effect_id": "insurance_used", "forgiven": facts.perfect_forgiven})
var hl := "perfect_broken" if facts.perfect_hit_false - facts.perfect_forgiven == 1 else ""
emit_row_cb.call({"type": "seal_miss", "source_class": "seal_miss", "origin_cell": cell, "highlight": hl}, ri, "封印未中", 0, {})
var snap = reward_economy.grant(cell)
if not (snap is Dictionary):
return
reward_grant.grant_routed(snap, "negative", {"source_class": "seal_miss", "cell": cell, "ri": ri})
@@ -435,6 +456,6 @@ func _apply_counter_hit(h: Dictionary) -> void:
# grant_material 兑付块单点(三位分支本体逐字收拢——source_class 两态 safe_cell/seal_hit 由参承载)
func _apply_material_hit(h: Dictionary, cell: Vector2i, ri: int, source_class: String = "safe_cell") -> void:
var g_snap = oc._reward_economy.grant(cell)
var g_snap = reward_economy.grant(cell)
if g_snap is Dictionary:
reward_grant.grant_routed(g_snap, "positive", {"source_class": source_class, "cell": cell, "ri": ri, "label_suffix": String(h.get("label", ""))})