iter49-卡1:效果端口与掷格分件批(轴戊两件建件):EffectPort六Callable路由+RewardRoller掷格面迁出+两业务类零引擎直连+effect_port_test V1-V3

This commit is contained in:
DKChan committed 2026-11-02 07:30:33 +08:00
1 parent 926b05a61c
commit 11beb4fc59
31 files changed
+697 -586

No files matched your search

+26 -51
View File
@@ -41,19 +41,9 @@ var _outcomes := {} # ticket_id → "redeemed"/"zero"/"voided"(
var _seq := 0 # 流水号
var _halted := false # 主角倒下终止封印(死亡事件置位——flush 余单作废)
var _hero_ac_override := -1 # 主角护甲等级合流入参(begin_battle 显式设定——-1 = 表值兜底,口径同战斗编排器)
# 效果引擎(attack_commit/clamp_check/armor_roll/kill/damage_taken
# 发射+施加面:dispatch 返回命中,本类按组合规则施加)
var _fx: EffectEngine = null
var _ctx: EffectContext = null
# 战斗段运行态事实束(编排器每轮/每战注入——键集封闭,缺键读缺省即零扰动):
# dig_streak/charge_layers——facts 取数面(attack_commit 载荷 dig_streak;猎巢人 per/整备
# attack_bonus per/厚积薄发 when/铁壁 shield per 同读 charge_layers)——combat_segment 每轮写;
# rider_tier_bonus/rider_tier_crit——流派档位两运行态(magic 档一 rider+1/档二 rider 可暴击)
# ——begin_battle 每战写
var inject := {"dig_streak": 0, "charge_layers": 0, "rider_tier_bonus": 0, "rider_tier_crit": false}
# 效果引擎入站回调束(编排器 begin_battle 同点注入):reveal=揭示效果回调(kill 发射点 reveal_tiles
# 订阅面,入队授予段末兑付)/ac=ac_bonus 回调(石肤 AC 累积单点同步)
var fx_calls := {"reveal": Callable(), "ac": Callable()}
# 效果端口件(组装期单点注入——host _init bind_port):发射/计数器/事实/揭示/AC 全经端口路由,
# 缺省 null=未绑(与原 _fx==null 零扰动守卫同语义——单测直构不接端口的夹具零扰动照跑)
var _port: EffectPort = null
func _init(db_ref, rng_ref, units_ref, effects_ref) -> void:
@@ -69,44 +59,33 @@ func set_hero_ac_override(v: int) -> void:
_hero_ac_override = v
# 效果引擎注入(战斗编排器 _init——事件发射面接线)
func set_fx(fx: EffectEngine, ctx: EffectContext) -> void:
_fx = fx
_ctx = ctx
# 效果端口件注入(host _init 组装期单点——先于 begin_battle 任何发射)
func bind_port(port: EffectPort) -> void:
_port = port
# 事件发射薄面(发射+命中收集统一入口——订阅空=零循环零扰动;命中由各施加面按组合规则施加)
# 事件发射薄面(发射+命中收集统一入口——订阅空=零循环零扰动;命中由各施加面按组合规则施加;
# 路由经效果端口件,事实回调=biz_fact 闭面五键宿主侧)
func _fx_hits(event: String, payload: Dictionary) -> Array:
if _fx == null:
if _port == null:
return []
return _fx.dispatch(_ctx, event, payload, _fx_fact)
return _port.emit(event, payload)
# 事件发射薄面(免施加观测位——enemy_hp_threshold 等现无订阅者的事件保留零扰动面)
# 补充事实面(fact 取值第二优先源——payload 未携带的运行态读数:charge_layers 编排器每轮注入。
# hp_ratio 补入〔背水 when 面,敌技 damage_taken 载荷不带血比例时回落〕;
# fx_calls.reveal=揭示效果回调〔斩首学者 kill 订阅面——编排器注入,入队授予段末兑付〕)
func _fx_fact(fact: String) -> Variant:
match fact:
"charge_layers":
return int(inject["charge_layers"])
"dig_streak":
return int(inject["dig_streak"])
"hp_ratio":
var hu: Dictionary[Variant, Variant] = 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 截断口径)
return null
# 未绑事实回落(缺省 null——int()/bool() 包裹后与原 inject 缺省值逐位同:int(null)=0/bool(null)=false)
func _fact_or(name: String) -> Variant:
return null if _port == null else _port.fact(name)
# per 取数(per 字段解析——载荷同名字段优先,回落补充事实面;add_ticket_rider/attack_bonus/
# 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))
if _fact_or(name) != null:
return int(_fact_or(name))
return 0
@@ -169,7 +148,7 @@ func open_ticket(kind: String, source_class: String, display: String, spec: Dict
var ac_payload := {"atk": int(t.base_value),
"target": String(tgt.get("display_name", "")) if tgt != null else "",
"target_id": String(tgt.get("enemy_id", "")) if tgt != null else "",
"matchup": matchup_o, "dig_streak": int(inject["dig_streak"]), "nest_count": _nest_count()}
"matchup": matchup_o, "dig_streak": int(_fact_or("dig_streak") or 0), "nest_count": _nest_count()}
t.context["fx_attack_hits"] = _fx_hits("attack_commit", ac_payload)
t.context["fx_attack_payload"] = ac_payload
return t
@@ -361,13 +340,13 @@ func _hero_attack_base_value(t: Dictionary, ctx: Dictionary) -> float:
if String(h.get("kind", "")) == "attack_bonus":
var contrib := int(h.get("value", 0)) * _per_value(String(h.get("per", "")), ac_payload)
if h.has("value_counter") and String(h.get("value_counter", "")) != "":
contrib = EffectEngine.counter(_ctx, String(h.get("value_counter", "")))
contrib = _port.counter_read(String(h.get("value_counter", "")))
base_value += float(contrib)
# 计数器施加(attack_commit 命中行内 counter_add〔用后清零行〕,命中序=声明序)
# 计数器施加(attack_commit 命中行内 counter_add〔用后清零行〕,命中序=声明序;端口路由账面)
for h in ac_hits:
if String(h.get("kind", "")) == "counter_add":
var set_v = h.get("set", null)
EffectEngine.counter_add(_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
_port.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
# 任意真族:厚积薄发满层 spike 倍乘(≥3 层——强制必中已在折扣拍承载)
if ctx["spike_ready"]:
for h in ac_hits:
@@ -397,7 +376,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D
# (奥术余烬「普攻命中后恒定结算」语义);猎巢人巢控光环(per nest_count)/矿工的执拗开格加成
# (per dig_streak、cap 封顶)同通道合计;档一质变 rider+1 / 档二(crit 时)rider 乘暴击倍率。
# rider 不改变下限语义(landed 含 rider 后不再过 floor)。
var hero_rider := int(inject["rider_tier_bonus"])
var hero_rider := int(_fact_or("rider_tier_bonus") or 0) # or 0=未绑缺省与 int(null) 缺省同值(int() 构造拒 null——端口缺省 null 面的空值合并)
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", "")), ctx["ac_payload"])
@@ -405,7 +384,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D
contrib = mini(int(h.cap), contrib)
hero_rider += contrib
if hero_rider > 0:
if bool(ctx["crit"]) and bool(inject["rider_tier_crit"]):
if bool(ctx["crit"]) and bool(_fact_or("rider_tier_crit") or false):
hero_rider = int(round(float(hero_rider) * float(db.combat.settlement.damage_roll.crit_multiplier)))
landed += hero_rider
payload["hero_rider"] = hero_rider
@@ -421,9 +400,7 @@ func _hero_attack_redeem(t: Dictionary, ctx: Dictionary, base_value: float) -> D
func _kill_overflow(t: Dictionary, target: Dictionary, landed: int) -> void:
for h in _fx_hits("kill", {"target": String(target.display_name), "target_id": String(target.get("enemy_id", "")), "overflow": landed}):
if String(h.get("kind", "")) == "reveal_tiles":
var reveal_cb: Callable = fx_calls["reveal"]
if reveal_cb.is_valid():
reveal_cb.call() # 斩首学者——击杀揭示 1 格(编排器入队,授予段末兑付)
_port.reveal_effect() # 斩首学者——击杀揭示 1 格(端口路由宿主入队,授予段末兑付)
continue
if String(h.get("kind", "")) != "overflow":
continue
@@ -468,7 +445,7 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
var total: int = int(roll["total"])
# damage_taken 发射(kind=enemy_skill;铁壁整备减伤订阅面:shield 加法族合计 value×per(charge_layers),
# 稳健拓荒树规则同面(rebuild 合成订阅);减后过下限保底。damage_reduce〔背水 hp_ratio when〕/
# counter_add〔保险丝/石肤〕/ac_bonus〔石肤〕同面施加;hp_ratio fact 由 _fx_fact 补充事实面回落提供)
# counter_add〔保险丝/石肤〕/ac_bonus〔石肤〕同面施加;hp_ratio fact 由 damage_taken 载荷携带)
var dt_res := _emit_hero_damage_taken(total, "enemy_skill", String(t.context.get("skill_id", "")), t.origin_cell, int(t.round_index))
total = int(dt_res["total"])
units.apply_damage(hero, total)
@@ -525,12 +502,10 @@ func _apply_hero_damage_effects(hits: Array, total: int) -> Dictionary:
"damage_reduce":
total = maxi(0, total - int(h.get("value", 0)))
"ac_bonus":
var ac_cb: Callable = fx_calls["ac"]
if ac_cb.is_valid():
ac_cb.call(h)
_port.apply_ac(h)
"counter_add":
var set_v = h.get("set", null)
EffectEngine.counter_add(_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
_port.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if set_v == null else int(set_v), set_v != null)
if shield > 0:
total = maxi(int(db.combat.settlement.damage_roll.damage_floor), total - shield)
return {"total": total, "shield": shield}