Files
gplan-2/game/core/battle_fx_host.gd
T
DKChan 139cef098e 卡①T5(拆件批wr6/V13): damage_controller 拆分+参数束——open_ticket 12参→(kind,source_class,display,spec)四参 spec 束(必载 channel/base_value/target_rule/ri 缺载 assert;可选 origin_cell/attack_type/rider/highlight/context 缺省同原签名;返回单据字典九栏目+requires/source_unit 逐字不变=契约8);类变量 19→15(dig_streak/charge_layers/rider_tier_bonus/rider_tier_crit→inject 束;fx_reveal_request/fx_ac_request→fx_calls 束 {reveal,ac});函数级拆分=_hero_attack_ctx→_ctx_rewrites(按序改写族四拍+armor_roll 发射+clamp_check 唯一发射点)+_ctx_clamp_delta(克制点数+订阅改写,消费同批命中禁重复发射);_hero_attack_redeem→_kill_overflow(kill 发射+贯穿弹射);_resolve_enemy_skill→_enemy_roll(掷点/折扣/克制/池乘/下限)+_enemy_shield_apply(damage_taken 施加面 {total,shield} 穿线,发射位留主函数保减伤落快照先后序)。调用点 30 处全改 spec 形(core 14=reward_grant 3+grant_segment 7+combat_segment 3+控制器内部 overflow 1;damage_controller_test 15;system_contracts_test 1)+inject/fx_calls 注入点改键(combat_segment :34/:37/:62、begin_battle :341-347)。验收:gdstyle 基线外 0 NEW(damage_controller 9 条存量键全清:branches/length/locals×3/class-vars 19→15/params 12→4)+ unit/integration 62 套件 1698 PASS 零 SCRIPT ERROR(〔dc1〕damage_controller 44 检查全绿含 A/B 模式/死亡作废/requires 门/flush_submit/AC 合流/池乘区;〔dc2〕system_contracts 九栏目契约绿;〔dc3〕round_flow/seal_polarity/level_shop 全绿)+ system_test 70 项 0 失败三闸过〔拆件批 V13〕
2026-10-27 07:52:02 +08:00

169 lines
9.9 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 效果引擎宿主件(自 battle_orchestrator 收编 fx 族):事件发射面(fx_emit——18 发射点统一入口)、
## 补充事实判定(charge_layers 自答,余五事实转宿主)、主角 AC 三值累积与同步(本轮/本战累积器+
## 基线快照)、bonus_delta 四轴累积(战末宿主 drain 汇入账本)、揭示待兑队列与兑付(含再入闸——
## 同步信号回环防二次缓冲二次兑付)、round/battle 桶计数器持有(桶随编排器生灭语义不变)、
## round_start 发射与施加、订阅重建、桶挂引用。生命周期=随编排器(编排器 _init 同建同灭,每战新建)。
## 跨件单向接线(🔴 契约红线):行发射与五事实经构造注入的 emit_cb/facts_cb 回调宿主——协议面单点
## 留宿主,禁回调宿主私有族;揭示奖励兑付走 bind_grant 注入的兑付回调(RewardGrant 授予面——
## snapshot 查询与 grant_routed 同面);对本件零反向强引用(禁 RefCounted 双向环——两件均随每战新建)。
## 随机只经注入 rng 的 fx_reveal 封闭流(除该流外零随机;零 await——效果引擎全同步解释器)。
## 类变量封闭面(14≤15):bundle 7 项(db/rng/minesweeper/action_economy/damage/emit_cb/facts_cb,
## 缺一键 assert——引用束注入防 _init max-parameters 新键)+grant_cb+engine+counters_ctx+
## ac 束(键集封闭 base/round/battle)+_fx_bonus_delta+_fx_new_resource_types+reveal 束
## (键集封闭 queue/busy)。禁实现期增删键;禁为机检文本保留无效成员(原编排器 fx_counters_round/
## battle 两零读者成员迁入即删——同物异名同字典,桶引用经 counters_ctx 承载)。
class_name BattleFxHost
extends RefCounted
var db: ConfigDb
var rng: RngServiceCore
var minesweeper: MinesweeperRules
var action_economy: ActionEconomy
var damage: DamageController
var emit_cb: Callable # 宿主 _emit_row——行协议面单点(5 参 head 束形:head/ri/display/value/extra)
var facts_cb: Callable # 宿主 _fx_host_fact——round_index/battle_index/seals_count/nest_count/hp_ratio 五事实宿主侧单点
var grant_cb: Callable = Callable() # 揭示兑付回调=RewardGrant 授予面(bind_grant 注入;构造序解法——本件对授予面零强引用)
var engine: EffectEngine # 效果引擎(原编排器 _fx——宿主 _init 经此转注 damage/action_economy/reward_economy 三 set_fx)
var counters_ctx: EffectContext # 四桶引用束(round/battle 两桶本件自有;adventure/level 桶 begin_battle mount_buckets 挂引用)
var ac := {"base": -1, "round": 0, "battle": 0} # ac 束(base=主角 AC 基线快照 enc.hero_armor_class,-1=表值兜底未解析;round=ac_bonus 本轮累积器〔封印桩,round_start 清零〕;battle=本战累积器〔石肤,cap 封顶,begin_battle 清零〕)
var _fx_bonus_delta := {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0} # 本战 bonus_delta 累积(战末宿主 bonus_delta_drain 读后清零)
var _fx_new_resource_types := {} # 本战已见资源类型集(地质学家 fact new_resource_type 判定——battle 域)
var reveal := {"queue": [], "busy": false} # reveal 束(queue=揭示效果待兑队列〔见血开窍/斩首学者/地质学家/跳格采掘共用——授予段末统一兑付,防轮 facts 迭代中追加重入〕;busy=兑付中标记——宿主 _on_cell_revealed 缓冲旁路判定,防二次缓冲二次兑付)
func _init(bundle: Dictionary) -> void:
for k in ["db", "rng", "minesweeper", "action_economy", "damage", "emit_cb", "facts_cb"]:
assert(bundle.has(k), "[效果引擎宿主] 引用束缺键: %s" % k)
db = bundle.db
rng = bundle.rng
minesweeper = bundle.minesweeper
action_economy = bundle.action_economy
damage = bundle.damage
emit_cb = bundle.emit_cb
facts_cb = bundle.facts_cb
engine = EffectEngine.new(db)
counters_ctx = EffectContext.new() # round/battle 两桶即 ctx 自带字典——编排器旧 fx_counters 两成员同物异名零读者,不再另立
# 揭示兑付回调挂接(宿主 _init 建两件后接线——构造序禁环)
func bind_grant(cb: Callable) -> void:
grant_cb = cb
# 事件发射薄面(18 发射点统一入口——订阅空=零循环零扰动;确定性同步分发零 RNG)
func fx_emit(event: String, payload: Dictionary) -> Array:
return engine.dispatch(counters_ctx, event, payload, _fx_fact)
# round_start 发射+施加(grant_ap mode:queue=当轮入队,行动经济轮切换即兑现;计数器 round_start
# 重置点在 dispatch 内生效。调用点两处:begin_battle 首轮 / 轮始段续轮。payload 携 round〔宿主侧
# 事实单点〕+ap_exhausted_last〔一滴不剩 when 面〕;发射前 ac_bonus 本轮累积器清零并同步主角 AC)
func emit_round_start() -> void:
ac.round = 0
_sync_hero_ac()
for h in fx_emit("round_start", {"round": int(facts_cb.call("round_index")),
"ap_exhausted_last": action_economy.ap_exhausted_last_round()}):
if String(h.get("kind", "")) == "grant_ap" and String(h.get("mode", "")) == "queue":
action_economy.queue_ap_delta(int(h.get("value", 0)))
# ac_bonus 施加(mode=round 本轮累积器〔封印桩,round_start 清零〕/
# mode=battle 本战累积器〔石肤,cap 封顶;缺省 battle〕;cap=0/缺省=无上限)
func apply_ac_bonus(h: Dictionary) -> void:
var mode := String(h.get("mode", "battle"))
var cap := int(h.get("cap", 0))
if mode == "round":
ac.round = int(ac.round) + int(h.get("value", 0))
else:
ac.battle = int(ac.battle) + int(h.get("value", 0)) if cap <= 0 else mini(cap, int(ac.battle) + int(h.get("value", 0)))
_sync_hero_ac()
# 揭示效果入队(fx_reveal 掷格兑付延后到授予段末——防轮 facts 迭代中追加重入;见血开窍/
# 斩首学者〔经伤害控制器 fx_calls["reveal"] 回调〕/地质学家/跳格采掘共用)
func enqueue_reveal() -> void:
reveal.queue.append({"round": int(facts_cb.call("round_index"))})
# 揭示效果兑付(授予段末统一——fx_reveal 封闭流从未揭示安全格掷 1 格全揭示;
# 信息红线=只揭真格〔危险格不入候选〕;空候选=零兑付留归因行且不取号;
# 揭示奖励经兑付回调正向路由直接入账——reveal.busy 旁路宿主 _on_cell_revealed 缓冲防二次兑付)
func flush_pending_reveals(ri: int) -> void:
if (reveal.queue as Array).is_empty():
return
var board: BoardData = minesweeper.current_board()
while not (reveal.queue as Array).is_empty():
(reveal.queue as Array).pop_back()
var cands: Array[Variant] = []
for c in board.all_cells():
if not minesweeper.is_revealed(c) and not board.is_mine(c):
cands.append(c)
if cands.is_empty():
emit_cb.call({"type": "extended", "source_class": "fx_reveal", "origin_cell": Vector2i(-1, -1)}, ri, "揭示落空(无未揭示安全格)", 0, {"effect_id": "fx_reveal"})
continue
var rg: RandomNumberGenerator = rng.open_stream("fx_reveal")
var cell: Vector2i = cands[rg.randi_range(0, cands.size() - 1)]
reveal.busy = true
var ok: bool = minesweeper.fx_reveal(cell)
reveal.busy = false
if not ok:
emit_cb.call({"type": "extended", "source_class": "fx_reveal", "origin_cell": Vector2i(-1, -1)}, ri, "揭示落空(格子不可揭)", 0, {"effect_id": "fx_reveal"})
continue
grant_cb.call(cell, ri)
emit_cb.call({"type": "extended", "source_class": "fx_reveal", "origin_cell": cell, "highlight": "fx_reveal"}, ri, "专长揭示", 0, {"effect_id": "fx_reveal"})
# 效果引擎订阅重建(每战从持有集/装备集重建,中途获得下一战生效;树规则合成订阅同面装载。
# 装载序=feats 表序→treasure 表序→树规则,写死保种子复放。计数器四桶经 counters_ctx 引用宿主容器,无回灌时点)
func rebuild_subscriptions(feats: Array, attunement: Array, tree_rules: Array) -> void:
engine.rebuild(feats, attunement, tree_rules)
# begin_battle 桶挂引用(adventure 桶=账本共享引用跨战持久=同一字典引用,非副本搬运;
# level 非 Dictionary 即不挂——LevelRunner 每关 new 下传)
func mount_buckets(adv_bucket: Dictionary, level_bucket: Variant) -> void:
counters_ctx.adventure_bucket = adv_bucket
if level_bucket is Dictionary:
counters_ctx.level_bucket = level_bucket
# begin_battle 运行态清零(ac 三值/bonus_delta 四轴/new_resource_types/揭示队列+再入闸——
# 原编排器 begin_battle 扩签运行态就位段原序)
func reset_battle_state(ac_base: int) -> void:
ac.base = ac_base
ac.round = 0
ac.battle = 0
_fx_new_resource_types = {}
reveal.queue = []
reveal.busy = false
_fx_bonus_delta = {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0}
# 战末 drain(读后清零;宿主 _battle_over 单消费——读改清三拍合一,时序等价)
func bonus_delta_drain() -> Dictionary:
var out := _fx_bonus_delta
_fx_bonus_delta = {"atk": 0, "max_hp": 0, "armor_class": 0, "ap": 0}
return out
# 揭示兑付中读投影(宿主 _on_cell_revealed 旁路判定——fx 揭示的奖励由兑付点直接入账,
# 此处缓冲会导致二次兑付)
func reveal_busy() -> bool:
return bool(reveal.busy)
# 补充事实面(fact 取值第二优先源——payload 未携带的运行态读数;charge_layers 本件自答
# 〔action_economy 直读〕,余五事实转宿主 facts_cb——宿主侧单点,未知事实回落 null)
func _fx_fact(fact: String) -> Variant:
if fact == "charge_layers":
return action_economy.charge_layers
return facts_cb.call(fact)
# ac_bonus 累积后同步主角 AC(石肤/封印桩消费面——defender_ac 经 set_hero_ac_override 单点;
# 基线 = enc 口径,-1(表值兜底态)时解析为表值后叠加,守恒口径与 begin_battle 注入一致)
func _sync_hero_ac() -> void:
if int(ac.base) < 0:
ac.base = int(db.hero.armor_class)
damage.set_hero_ac_override(int(ac.base) + int(ac.round) + int(ac.battle))