462 lines
28 KiB
GDScript
462 lines
28 KiB
GDScript
class_name GrantSegment
|
||
extends RefCounted
|
||
# ① 授予段 Segment(P1-1 结算巨函数拆段;段边界=审计五段〔裁点③甲〕)。
|
||
# 职责:结算队列(开格/封印判定/清图)→ 极性路由逐批兑付 + 封印判/军规开单 + round_end 施加 +
|
||
# 转存收口 + 揭示兑付 + 段界结账。行发射经 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 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(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:
|
||
# ① 授予段:结算队列(开格/封印判定/清图)→ 极性路由逐批兑付(伤害形态单据暂存——见 submit 案C 闸)。
|
||
# 判定基线 = 轮末在场封印(发射面已过滤——被挖开的封印已随挖掘擦除零兑付;
|
||
# 开格事实不可撤)。
|
||
reward_grant.defer["on"] = true # 案C:授予段伤害单据只暂存不结账(兑付时点=战斗段末)
|
||
_reveal_batch(ri)
|
||
_terrain_batch(ri)
|
||
_seal_batch(ri)
|
||
_round_end_apply(ri)
|
||
|
||
|
||
# 成功刷新事实发射+施加(迭代 46 卡A T4——BattleRefreshFlow 装新板后单点调用,每成功刷新恰一次;
|
||
# 拒绝刷新零事实〔C2〕。命中消费三面:counter_add→既有单点落账〔蓄力周转/石层缓冲/坑道小憩 计数〕;
|
||
# grant_gp→战斗内缓冲 escrow 入账+fx_grant_gp 行〔矿道红利——不绑清图奖励,C7〕;
|
||
# open_ticket channel=blast→崩壁冲击群伤单延迟入队〔案C 同队列,战斗段末按存活目标结账〕)
|
||
func apply_board_refreshed(ri: int) -> void:
|
||
var br_hits := fx_host.fx_emit("board_refreshed", {"round": ri})
|
||
for h in br_hits:
|
||
if String(h.get("kind", "")) == "counter_add":
|
||
_apply_counter_hit(h)
|
||
for h in br_hits:
|
||
if String(h.get("kind", "")) == "grant_gp":
|
||
reward_grant.escrow += int(h.get("value", 0))
|
||
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 := 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)
|
||
|
||
|
||
# 开格/踩雷兑付批(轮 facts_reveals 资源事件序:safe 正向乘区兑付 / mine_hit 减半负向兑付)
|
||
func _reveal_batch(ri: int) -> void:
|
||
for evt in facts.reveals:
|
||
if not bool(evt.get("resource", false)):
|
||
continue # 封锁格(initial 通道)恒无资源——不入账(战斗结算 义务②)
|
||
var snap = evt.get("reward_snapshot")
|
||
if not (snap is Dictionary):
|
||
continue
|
||
if evt.kind == "safe":
|
||
_reveal_safe(evt, snap, ri)
|
||
elif evt.kind == "mine":
|
||
_reveal_mine(evt, snap, ri)
|
||
|
||
|
||
# safe 事件兑付(封印不可逆四情况④:插安全格再挖开 = 零惩罚零奖励——正向兑付作废〔归因行在案〕;
|
||
# 标空判定已随封印擦除自然缺席——被挖的封印不在轮末在场基线。dig_reveal 发射+施加序:
|
||
# 乘区收集 → 正向兑付 → 施加面〔计数器/揭示入队/追加产出/奥术矢〕。
|
||
# payload 携 is_edge_cell〔边陲矿工〕/is_blank_zero〔跳格采掘〕/
|
||
# new_resource_type〔地质学家——快照 cell_type 首见判定,见后集登记〕)
|
||
func _reveal_safe(evt: Dictionary, snap: Dictionary, ri: int) -> void:
|
||
if bool(evt.get("dug_under_seal", false)):
|
||
emit_row_cb.call({"type": "reward_ineffective", "source_class": "safe_cell", "origin_cell": evt.cell}, ri, "奖励作废(封印格直接挖开——零惩罚零奖励)", 0, {})
|
||
return
|
||
var board_b: BoardData = minesweeper.current_board()
|
||
var ctype := String(snap.get("cell_type", ""))
|
||
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 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,
|
||
"new_resource_type": is_new_type})
|
||
var pos_m := 0
|
||
var neg_m := 0
|
||
for h in dr_hits:
|
||
if String(h.get("kind", "")) != "yield_mult":
|
||
continue
|
||
pos_m += int(h.get("mult", 0))
|
||
neg_m += int(h.get("cost_mult", 0))
|
||
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)
|
||
|
||
|
||
# dig_reveal 施加面(序=计数器〔连击计数——乘区收集后执行=本格兑付用旧值〕→ 揭示入队〔地质学家/
|
||
# 跳格采掘 reveal_tiles 订阅面,授予段末统一掷格兑付〕→ 追加产出〔双份制:grant_material
|
||
# 命中=配额内追加第二份〕→ 奥术矢(每战首次开安全格追加魔法矢;任意真族单次生效))
|
||
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 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] = 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)
|
||
|
||
|
||
# mine 事件兑付(封印不可逆四情况②:插危险格再挖开 = 只受挖雷一层惩罚——mine_hit 照常兑付惊动巢穴,
|
||
# 标中不发〔封印已随挖掘擦除、不入轮末判定基线〕。mine_hit 发射+施加序:施加面收集 → 减伤落快照 →
|
||
# 计数器 → 揭示入队 → 负向兑付〔带减半系数〕→ 反打开单)
|
||
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(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 DamageController.hits_of(mh_hits, "reveal_tiles").is_empty():
|
||
fx_host.enqueue_reveal() # 见血开窍——每颗雷一次揭示(授予段末统一兑付)
|
||
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 := damage.open_ticket(DamageController.KIND_BOARD_BLAST, "attune_nest_halve", "反弹伤害",
|
||
{"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)
|
||
|
||
|
||
# mine_hit 施加面(damage_reduce〔安全帽耗层减伤——value_counter 层值一次清零/背水——定值减免〕/
|
||
# heal〔血蜜〕/ bonus_delta〔血税——fx 件四轴累积〕/ blast_scaled〔陷阱爆发——雷伤×倍率全体敌,案C 暂存〕/
|
||
# damage_mult 减半系数收集;返回 {halve_pct, mine_reduce} 交兑付与快照减伤消费)
|
||
func _reveal_mine_apply(mh_hits: Array, snap: Dictionary, cell: Vector2i, ri: int) -> Dictionary:
|
||
var halve_pct := 0
|
||
var mine_reduce := 0
|
||
for h in mh_hits:
|
||
match String(h.get("kind", "")):
|
||
"damage_mult":
|
||
halve_pct += int(h.get("value", 0))
|
||
"damage_reduce":
|
||
if h.has("value_counter") and String(h.get("value_counter", "")) != "":
|
||
mine_reduce += EffectEngine.counter(fx_host.counters_ctx, String(h.get("value_counter", ""))) # 安全帽——层值减免
|
||
else:
|
||
mine_reduce += int(h.get("value", 0)) # 背水——定值减免
|
||
"heal":
|
||
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", ""))
|
||
fx_host.add_bonus_delta(axis, int(h.get("delta", 0)))
|
||
"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)
|
||
return {"halve_pct": halve_pct, "mine_reduce": mine_reduce}
|
||
|
||
|
||
# 地形变异 兑付批(授予段尾——触发枚举 on_open:挖开即伤、chain 通道同口径;伤害单据案C 暂存到
|
||
# 战斗段末兑付〔案C〕;信息型 hide_number 不进结算——呈现层读取,见 board_view 数字遮蔽;
|
||
# 危险格恒无变异——布置段排除,双层叠罚不存在;封印格直挖照吃变异伤害——地形后果同 mine_hit 口径,
|
||
# 非奖励不作废;吞噬流资源兑付〔荆棘编织/地脉补偿〕中伤害形态行同样经案C闸暂存,非伤害形态照常当场)
|
||
func _terrain_batch(ri: int) -> void:
|
||
for evt in facts.reveals:
|
||
var vids: Array[Variant] = evt.get("variant_ids", [])
|
||
for vid in vids:
|
||
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:
|
||
# 发射在单据开立前、减后值开单,组合消费含 shield〔全局受伤语义〕;
|
||
# hero_damage_consumed 标记入单据 context——战斗段末 _resolve_board_flat_hero 跳过重复发射;
|
||
# grant_material〔荆棘/地脉〕移到单据提交后(兑付值序一致——收益不因减伤变化))
|
||
var dt_amount := int(vrow.get("effect_value", 0))
|
||
var dt_res := damage.emit_hero_damage_taken(dt_amount, "terrain", String(vid), evt.cell, ri)
|
||
dt_amount = int(dt_res["total"])
|
||
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)
|
||
for h in (dt_res["hits"] as Array):
|
||
if String(h.get("kind", "")) == "grant_material":
|
||
_apply_material_hit(h, evt.cell, ri)
|
||
|
||
|
||
# 封印判定批+战封印军规开单段(封印流:seal_judged 批后统一开单:轮末每面封印命中对最前存活敌
|
||
# 结算 board_typed 必中伤害〔复用零新配方;「封印所在巢穴敌」简化为 default_target 最前存活敌——
|
||
# 台账披露待追认〕;封印全中倍率=本轮 hit>0 且 miss==0 时封印伤 ×mult——全对推理奖励)。
|
||
# 伤害单据案C 暂存战斗段末兑付。
|
||
func _seal_batch(ri: int) -> void:
|
||
for evt in facts.seals:
|
||
_judge_seal(evt, ri)
|
||
_seal_strike_batch(ri)
|
||
|
||
|
||
# seal_settle 发射+施加(战封印 open_ticket + 封印全中倍率 modify_ticket 改写窗口:封印伤基值×完美倍率后
|
||
# 逐封印开单)
|
||
func _seal_strike_batch(ri: int) -> void:
|
||
var fs_hits := 0
|
||
var fs_misses := 0
|
||
for fevt in facts.seals:
|
||
if bool(fevt.get("hit", false)):
|
||
fs_hits += 1
|
||
else:
|
||
fs_misses += 1
|
||
var fs_fx := fx_host.fx_emit("seal_settle", {"seals_hit_all": fs_hits > 0 and fs_misses == 0,
|
||
"seals_count": fs_hits, "round": ri})
|
||
var strike := _seal_strike_value(fs_fx, fs_hits, fs_misses)
|
||
if strike.is_empty():
|
||
return
|
||
for fevt in facts.seals:
|
||
if not bool(fevt.get("hit", false)):
|
||
continue
|
||
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)
|
||
|
||
|
||
# 战封印军规基值决议(attack 通道 open_ticket 首命中取基值;seal_mult modify_ticket 连乘;
|
||
# 封印全中倍率=miss==0 且 hit>0 时基值×倍率。空旗集=空字典〔不开单合法态〕)
|
||
func _seal_strike_value(fs_fx: Array, fs_hits: int, fs_misses: int) -> Dictionary:
|
||
var banner_hits: Array[Variant] = []
|
||
for h in fs_fx:
|
||
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "attack":
|
||
banner_hits.append(h)
|
||
if banner_hits.is_empty():
|
||
return {}
|
||
var strike_value := int((banner_hits[0] as Dictionary).get("value", 0))
|
||
var seal_mult := 1
|
||
for h in fs_fx:
|
||
if String(h.get("kind", "")) == "modify_ticket" and String(h.get("mode", "")) == "seal_mult":
|
||
seal_mult *= maxi(1, int(h.get("value", 1)))
|
||
if fs_misses == 0 and fs_hits > 0 and seal_mult > 1:
|
||
strike_value *= seal_mult # 封印全中:同轮全中封印伤倍增(全对推理奖励)
|
||
return {"value": strike_value}
|
||
|
||
|
||
# round_end 发射+施加(每轮结算收口:轮末反打单开立〔open_ticket 即时执行〕+积层清零〔counter_add
|
||
# set〕;停手侧 AP 已在 request_stop 记账,finish_round_stop 按转存率转存 RAP)+施加面:grant_gp〔爆破会计/收盘——escrow 战斗域
|
||
# 缓冲,随 gp_dropped 上抛〕/grant_ap mode:queue〔封印回响——返 1 AP 下轮,queue_ap_delta 通道与轮始
|
||
# 加成同面〕+段界收口(转存率入参、清图奖励、揭示兑付、段界结账)
|
||
func _round_end_apply(ri: int) -> void:
|
||
var re_fx := fx_host.fx_emit("round_end", {"round": ri})
|
||
_round_end_blast_tickets(re_fx, ri)
|
||
_round_end_heal_value_counter(re_fx, ri) # 迭代 46 卡A T5——坑道小憩恢复兑付(先于清零行消费层值,声明序=heal 行在 counter set 行前)
|
||
for h in re_fx:
|
||
if String(h.get("kind", "")) == "counter_add":
|
||
_apply_counter_hit(h)
|
||
for h in re_fx:
|
||
if String(h.get("kind", "")) == "grant_gp":
|
||
reward_grant.escrow += int(h.get("value", 0))
|
||
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":
|
||
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 = 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 迭代重入)
|
||
damage.flush() # 授予段界结账(案C 后本段伤害单据全数暂存,此处对空队 no-op——保留作防御性段界)
|
||
|
||
|
||
# round_end heal value_counter 兑付(迭代 46 卡A T5——坑道小憩:层值=本轮成功刷新累计恢复点,
|
||
# units.heal 封顶、满血发未生效行;层清零由同事件 counter_add set 行承载〔声明序在后〕——本面
|
||
# 先于 counter_add 遍历消费层值即保序。行源=成功刷新事实族(无棋盘格——fx_board_refresh 豁免类))
|
||
func _round_end_heal_value_counter(re_fx: Array, ri: int) -> void:
|
||
for h in re_fx:
|
||
if String(h.get("kind", "")) != "heal" or String(h.get("value_counter", "")) == "":
|
||
continue
|
||
var layers := EffectEngine.counter(fx_host.counters_ctx, String(h.get("value_counter", "")))
|
||
if layers <= 0:
|
||
continue
|
||
var hu: Dictionary[Variant, Variant] = units.hero_unit()
|
||
if int(hu.get("hp", 0)) >= int(hu.get("max_hp", 1)):
|
||
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 = 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 用中性名,
|
||
# per-counter 反打通道保留泛用面;案C 暂存战斗段末兑付)
|
||
func _round_end_blast_tickets(re_fx: Array, ri: int) -> void:
|
||
for h in re_fx:
|
||
if String(h.get("kind", "")) == "open_ticket" and String(h.get("channel", "")) == "blast":
|
||
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 := 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": minesweeper.hero_pos()})
|
||
reward_grant.submit(mg_t, false)
|
||
|
||
|
||
# 封印判定(封印流本体;唯一调用面=本段 _seal_batch 的 facts_seals 循环——命中/未中两分)
|
||
func _judge_seal(evt: Dictionary, ri: int) -> void:
|
||
if evt.hit:
|
||
_judge_seal_hit(evt, ri)
|
||
else:
|
||
_judge_seal_miss(evt, ri)
|
||
|
||
|
||
# 封印命中(资格以危险格实例为键去重——排雷玩法 诚实重复上报的对侧;seal_hit 发射序:
|
||
# 标中雷反打票据 → 施加面 → 标中雷额外兑付 → 基线正向兑付)
|
||
func _judge_seal_hit(evt: Dictionary, ri: int) -> void:
|
||
var cell: Vector2i = evt.cell
|
||
if facts.seal_consumed.has(cell):
|
||
return # 去重跳过;零明细行
|
||
facts.seal_consumed[cell] = true
|
||
facts.mark_perfect_hit()
|
||
var fh_hits := fx_host.fx_emit("seal_hit", {"cell": cell, "round": ri,
|
||
"terrain_kind": minesweeper.harmful_adjacent(cell)})
|
||
_seal_burst_tickets(fh_hits, cell, ri)
|
||
_seal_hit_effects(fh_hits, cell, ri)
|
||
# 标中雷追加(封印流——标中雷额外获得一份格面资源:封印开始产出对标开格;
|
||
# 复用 grant 单点,额外兑付走同一 grant_routed 正向路由)
|
||
for h in fh_hits:
|
||
if String(h.get("kind", "")) == "grant_material":
|
||
_apply_material_hit(h, cell, ri, "seal_hit")
|
||
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"})
|
||
|
||
|
||
# 标中雷反打票据(行 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 DamageController.hits_of(fh_hits, "open_ticket"):
|
||
if String(h.get("channel", "")) != "blast":
|
||
continue
|
||
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)
|
||
|
||
|
||
# 标中施加面:counter_add〔血税 标中+1 攻——bonus_delta atk 轴〕/
|
||
# ac_bonus〔封印桩 本轮 AC+N〕/ bonus_delta〔血税——fx 件四轴累积〕/
|
||
# remove_mines〔封印匠 每第 3 次该危险格中立移除——mod_eq 闸在行 when〕/
|
||
# preview_area〔示警烟火 3×3 资源类型预告〕
|
||
func _seal_hit_effects(fh_hits: Array, cell: Vector2i, ri: int) -> void:
|
||
for h in fh_hits:
|
||
match String(h.get("kind", "")):
|
||
"counter_add":
|
||
_apply_counter_hit(h)
|
||
"ac_bonus":
|
||
fx_host.apply_ac_bonus(h)
|
||
"bonus_delta":
|
||
var axis_f := String(h.get("axis", ""))
|
||
fx_host.add_bonus_delta(axis_f, int(h.get("delta", 0)))
|
||
"remove_mines":
|
||
var removed: Array[Variant] = minesweeper.remove_mines_in([cell])
|
||
if not removed.is_empty():
|
||
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 = minesweeper.current_board()
|
||
var types: Array[Variant] = []
|
||
for n in board_p.neighbors8(cell):
|
||
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))
|
||
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
|
||
facts.mark_perfect_miss()
|
||
if facts.seal_miss_seen.has(cell):
|
||
return # 每格实例一次去重
|
||
facts.seal_miss_seen[cell] = true
|
||
# 危险直觉·守挂点(seal_missed 发射:契约 12 触发主语=「当回合发生的标错
|
||
# 动作」——去重闸=新旧标错判别器〔后续轮重判旧封印被闸挡回零触发〕;双条件②「结算时封印仍在
|
||
# 场」由发射面既有过滤承载〔被挖开的封印已随挖掘擦除零兑付〕;grant_ap mode:queue 下一轮
|
||
# 开轮兑现——末轮判出无下一轮=合法作废自动成立〔编排器每战新建,队列不跨战〕;限次=合成行
|
||
# 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":
|
||
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)
|
||
facts.mark_perfect_forgiven()
|
||
_apply_counter_hit(h) # 豁免同时记账(insurance_used battle 域——下次标错 when lt 1 不再命中)
|
||
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})
|
||
|
||
|
||
# counter_add 三行组单点(五位分支本体逐字收拢——kind 分派随段归位 P1-1)
|
||
func _apply_counter_hit(h: Dictionary) -> void:
|
||
var set_v = h.get("set", null)
|
||
EffectEngine.counter_add(fx_host.counters_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
|
||
|
||
|
||
# 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 = 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", ""))})
|