245 lines
14 KiB
GDScript
245 lines
14 KiB
GDScript
## 效果引擎宿主件(自 battle_orchestrator 收编 fx 族):事件发射面(fx_emit——18 发射点统一入口)、
|
||
## 补充事实判定(biz_fact 闭面四键)、主角 AC 三值累积与同步(本轮/本战累积器+
|
||
## 基线快照)、bonus_delta 四轴累积(战末宿主 drain 汇入账本)、揭示待兑队列与兑付(含再入闸——
|
||
## 同步信号回环防二次缓冲二次兑付)、round/battle 桶计数器持有(桶随编排器生灭语义不变)、
|
||
## round_start 发射与施加、订阅重建、桶挂引用。生命周期=随编排器(编排器 _init 同建同灭,每战新建)。
|
||
## 跨件单向接线(🔴 契约红线):行发射与五事实经构造注入的 emit_cb/facts_cb 回调宿主——协议面单点
|
||
## 留宿主,禁回调宿主私有族;揭示奖励兑付走 bind_grant 注入的兑付回调(RewardGrant 授予面——
|
||
## snapshot 查询与 grant_routed 同面);对本件零反向强引用(禁 RefCounted 双向环——两件均随每战新建)。
|
||
## 随机只经注入 rng 的 fx_reveal 封闭流(除该流外零随机;零 await——效果引擎全同步解释器)。
|
||
## 类变量封闭面(15≤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)+facts_inject(键集封闭 dig_streak/rider_tier_bonus/rider_tier_crit——
|
||
## 业务发射事实束运行态写面,combat_segment 每轮写 dig_streak/begin_battle 每战复位 rider 两键)。
|
||
## 禁实现期增删键;禁为机检文本保留无效成员(原编排器 fx_counters_round/
|
||
## battle 两零读者成员迁入即删——同物异名同字典,桶引用经 counters_ctx 承载)。
|
||
## 效果端口件组建与转注(轴戊):_init 尾段建 EffectPort(六 Callable 绑本件对应方法)后对两业务类
|
||
## bind_port——端口不落本件成员(单向接线,本件零回持);业务类发射面全经端口路由,
|
||
## 计数器账面(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 经此建效果端口件转注两业务类 bind_port;reward_economy 经 luck_bias_for 查询口取回偏运合计)
|
||
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 缓冲旁路判定,防二次缓冲二次兑付)
|
||
var facts_inject := {"dig_streak": 0, "rider_tier_bonus": 0, "rider_tier_crit": false} # 业务发射事实束(键集封闭——原 damage.inject 运行态束迁宿主:dig_streak=combat_segment 每轮写;rider 两键=begin_battle 每战复位)
|
||
|
||
|
||
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 两成员同物异名零读者,不再另立
|
||
# 效果端口件组建与转注(轴戊——组装期单点:先于任何发射,六 Callable 绑本件方法后对两业务类转注;
|
||
# 端口不落本件成员——单向接线,本件零回持;方法直引用形态同 bundle emit_cb/facts_cb 既有接线)
|
||
var port := EffectPort.new({
|
||
"emit_cb": fx_emit_biz,
|
||
"counter_read_cb": counter_read,
|
||
"counter_add_cb": counter_add,
|
||
"fact_cb": biz_fact,
|
||
"reveal_cb": enqueue_reveal,
|
||
"ac_cb": apply_ac_bonus,
|
||
})
|
||
bundle.damage.bind_port(port)
|
||
bundle.action_economy.bind_port(port)
|
||
|
||
|
||
# 揭示兑付回调挂接(宿主 _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)
|
||
|
||
|
||
# 业务类发射路由(效果端口件 emit_cb——damage/action_economy 发射面统一出口;
|
||
# 事实回调=biz_fact 闭面五键,battle_index/round_index/seals_count/nest_count 禁进业务发射面)
|
||
func fx_emit_biz(event: String, payload: Dictionary) -> Array:
|
||
return engine.dispatch(counters_ctx, event, payload, biz_fact)
|
||
|
||
|
||
# 计数器账面路由(效果端口件 counter_read_cb——value_counter 层值读;账面唯一宿主侧)
|
||
func counter_read(counter_id: String) -> int:
|
||
return EffectEngine.counter(counters_ctx, counter_id)
|
||
|
||
|
||
# 计数器账面路由(效果端口件 counter_add_cb——施加单点;账面唯一宿主侧)
|
||
func counter_add(counter_id: String, value: int, set_form: bool) -> void:
|
||
EffectEngine.counter_add(counters_ctx, counter_id, value, set_form)
|
||
|
||
|
||
# 新资源类型首见登记(授予段 dig_reveal 地质学家判定面——返回 true=首见并置位;空串短路零登记)
|
||
func mark_new_resource_type(ctype: String) -> bool:
|
||
if ctype == "":
|
||
return false
|
||
if _fx_new_resource_types.has(ctype):
|
||
return false
|
||
_fx_new_resource_types[ctype] = true
|
||
return true
|
||
|
||
|
||
# bonus_delta 轴累积(授予段血税双向施加面——axis 在册四轴才累积,轴外静默)
|
||
func add_bonus_delta(axis: String, delta: int) -> void:
|
||
if _fx_bonus_delta.has(axis):
|
||
_fx_bonus_delta[axis] += delta
|
||
|
||
|
||
# 业务发射事实面(闭面四键——三 when 事实 {dig_streak 轮内槽/hp_ratio 主角
|
||
# 血量比例算式逐字承 damage_controller 原式}∪ 两 rider 运行态读数 {facts_inject 现值};
|
||
# 其余事实一律 null——battle_index/round_index/seals_count/nest_count 禁进,扩面=行为变化)
|
||
func biz_fact(name: String) -> Variant:
|
||
match name:
|
||
"dig_streak":
|
||
return int(facts_inject["dig_streak"])
|
||
"hp_ratio":
|
||
var hu: Dictionary[Variant, Variant] = damage.units.hero_unit()
|
||
return int(round(100.0 * float(int(hu.get("hp", 0))) / float(maxi(1, int(hu.get("max_hp", 1)))))) # 百分比 int(when lte int 截断口径)
|
||
"rider_tier_bonus":
|
||
return int(facts_inject["rider_tier_bonus"])
|
||
"rider_tier_crit":
|
||
return bool(facts_inject["rider_tier_crit"])
|
||
return null
|
||
|
||
|
||
# 偏运权重查询(reward_roll 发射面——bias_rewards 词汇在本层收口,掷格域只见整数)。
|
||
# 三参形 dispatch(不带 facts:本事件订阅行 when 事实面不参与,命中集与缝拆前逐字同);
|
||
# 发射时点由掷格件在开流之后、权重装配之前调用;零命中=0=原行集零扰动。
|
||
func luck_bias_for(cell_count: int) -> int:
|
||
var total := 0
|
||
for h in engine.dispatch(counters_ctx, "reward_roll", {"polarity": "positive", "cell_count": cell_count}):
|
||
if String(h.get("kind", "")) == "bias_rewards":
|
||
total += int(h.get("value", 0))
|
||
return total
|
||
|
||
|
||
# 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 迭代中追加重入;见血开窍/
|
||
# 斩首学者〔经伤害控制器端口 reveal_effect 路由〕/地质学家/跳格采掘共用)
|
||
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: Dictionary[Variant, Variant]) -> 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 未携带的运行态读数;
|
||
# 全部转宿主 facts_cb——宿主侧单点,未知事实回落 null)
|
||
func _fx_fact(fact: String) -> Variant:
|
||
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.combat.hero.armor_class)
|
||
damage.set_hero_ac_override(int(ac.base) + int(ac.round) + int(ac.battle))
|