效果引擎框架落地:事件发布订阅解释器+16 发射点埋点(T1——内容数据驱动扩展批)

把「内容的效果行为」从结算代码硬编码分支搬进数据行的第一步:通用解释器 EffectEngine 落地。
本提交=框架与埋点,双轨过渡期——发射点仅观测命中、施加面仍走旧消费点,行为与迁移前逐位一致
(冒烟 783 项 0 失败实证、单元 17 套全绿)。

新增 game/core/effect_engine.gd:
- 事件词汇 16 个(任务书 §3.1 定稿)+扫雷侧事件集(流派扫雷触发铁律新形态)
- 效果类型词汇 24 型(任务书 §3.3 定稿 19 行;标题计 23 系笔误按行内容全收,代拍板留痕)
- fact 条件词汇 9 项+when 算子词汇封闭(mod_zero/advantageous/gte_ref/lt_ref/ends_with
  系现状条件装进 9 项 fact 的查询面落法——代拍板白名单覆盖:掘进蓄能取模、淬毒克制正面、
  深挖配额计数器比较、对某甲族泛条件)
- 订阅装载 rebuild(每战 begin_battle 重建——中途获得下一战生效,同战内不热载;
  装载序=feats 表序→treasure 表序→树规则合成行,写死保种子复放)
- 计数器账本(counter_add/counter_spend/reset_at=round_start 重置点声明)
- 组合规则六族(加法/乘法/任意真/按序改写/按序消耗/即时执行——发射点施加面引用)

16 发射点埋点(订阅空=零循环零扰动):
- 战斗编排器:dig_reveal/mine_hit/flag_hit/flag_settle/round_end/enemy_hp_threshold/
  treasure_used/damage_taken(kind=terrain)/round_start×2
- 伤害控制器:attack_commit(hero_attack 单算术前)/clamp_check×2(主角攻击+棋盘克制面)/
  armor_roll/kill/damage_taken(kind=enemy_skill)
- 行动经济:dig_paid(对价闸前)/charge_full(转蓄力满层沿——R6 框架先行,现无订阅者)
- 地图战斗资源:reward_roll(摇号前权重合成层)

行为等价细节(本批预演、T3 落地):
- 停手转存改记账制:request_stop 记剩余 AP 后清零,转存决策移至 round_end 发射点
  finish_round_stop(grant_ap(mode:transfer) 订阅定夺;双轨期旧旗并行,缺省 RAP 转存不变)
- attack_commit 发射点=管线算术前(载荷含 matchup/dig_streak/nest_count——连珠矢/执拗/
  猎巢人/淬毒订阅事实源)

设计权威:design/research/研究-内容数据驱动扩展架构-2026-10-06.md(案乙)。
任务书:design/research/任务书-内容数据驱动扩展批-2026-10-06.md §六 T1。
This commit is contained in:
DKChan committed 2026-10-06 21:22:33 +08:00
1 parent fc4dbde4f6
commit 7d157b3c4c
6 files changed
+442 -6

No files matched your search

+45 -1
View File
@@ -60,6 +60,9 @@ var _outcomes := {} # ticket_id → "redeemed"/"zero"/"voided"(
var _seq := 0 # 流水号
var _halted := false # 主角倒下终止旗(死亡事件置位——flush 余单作废)
var _hero_ac_override := -1 # 主角护甲等级合流入参(begin_battle 显式设定——-1 = 表值兜底,口径同战斗编排器)
# 效果引擎(内容数据驱动扩展批 T1——attack_commit/clamp_check/armor_roll/kill/damage_taken 发射面;
# 双轨过渡期仅观测命中,施加面仍走 feat_flags 旧消费点)
var _fx: EffectEngine = null
# 特性求值旗(迭代 12 T2——begin_battle 编排器经 FeatsEngine 求值后注入;空字典=零扰动。
# rider_flat=英雄侧必中附加值〔奥术余烬族〕/rider_crit=档二 rider 可暴击/first_dig_spell=每战首次
# 开安全格追加魔法矢〔消费在编排器授予段〕/clamp_magic_light·clamp_magic_heavy=克制点直调)
@@ -79,6 +82,26 @@ func set_hero_ac_override(v: int) -> void:
_hero_ac_override = v
# 效果引擎注入(战斗编排器 _init——事件发射面接线)
func set_fx(fx: EffectEngine) -> void:
_fx = fx
# 事件发射薄面(发射点统一入口——订阅空=零循环零扰动)
func _fx_dispatch(event: String, payload: Dictionary) -> void:
if _fx != null:
_fx.dispatch(event, payload)
# 巢穴控制数(fact nest_count 取数面——敌方 weaken 实例数,猎巢人订阅面)
func _nest_count() -> int:
var n := 0
for eff in effects.query_active("enemy"):
if String(eff.effect_id) == "weaken":
n += 1
return n
# ---- 开单面(流程调用——编排器六调用点的唯一形态)----
# 单据构造(缺省填齐——调用点只写差异栏;context = kind 专用:enemy_skill 带 skill_id/baseline,
@@ -196,8 +219,15 @@ func _resolve_hero_attack(t: Dictionary) -> void:
return
var armor: String = String(target.get("armor_type", "light"))
var atk_type: String = String(t.attack_type)
var matchup := (atk_type + "_vs_" + armor) if atk_type != "" else ""
# attack_commit 发射点(内容数据驱动扩展批 T1——我方伤害单据开立、管线算术前;载荷 atk/target/
# matchup + 挖掘 streak 事实〔连珠矢/执拗订阅面〕+巢穴控制数〔猎巢人〕)
_fx_dispatch("attack_commit", {"atk": int(t.base_value), "target": String(target.display_name),
"matchup": matchup, "dig_streak": int(feat_flags.get("dig_streak", 0)), "nest_count": _nest_count()})
var roll_read: Dictionary = _armor_roll()
var threshold: int = defender_ac(target) + int(db.settlement.attack_bonus)
# armor_roll 发射点(内容数据驱动扩展批 T1——敌护甲掷骰判定;免掷恒折扣订阅面)
_fx_dispatch("armor_roll", {"target_ac": threshold, "roll": int(roll_read.roll)})
# 矿工重锤·命中交换(迭代 12 T4 普攻流——正负双修原生形态:命中判定减成 trade_hit 点
# 〔更易命中〕换攻击增伤 trade_atk%——高 AC 负期望/低 AC 正期望)
var trade_hit := int(feat_flags.get("hit_trade_hit", 0))
@@ -219,6 +249,9 @@ func _resolve_hero_attack(t: Dictionary) -> void:
var shift := int(feat_flags.get("discount_shift", 0))
if shift > 0 and armor == "light" and discount < 100:
discount = _discount_shift_up(discount, shift)
# clamp_check 发射点(内容数据驱动扩展批 T1——克制关系判定修正点;魔网/湛蓝/猎手订阅面)
if atk_type != "":
_fx_dispatch("clamp_check", {"matchup": matchup})
var cdelta: int = _counter_delta(atk_type, armor) + _feat_clamp_bonus(atk_type, armor)
# 淬毒刃纹(迭代 11 同调批 T5——底稿 §3:对被克制敌人的穿刺/魔法直伤 +N;条件 = 克制成立 ×
# 攻击类型限定,主角攻击面专属——棋盘克制直伤走 _resolve_board_typed 不吃本加成)
@@ -271,6 +304,9 @@ func _resolve_hero_attack(t: Dictionary) -> void:
payload["hero_rider"] = hero_rider
units.apply_damage(target, landed)
payload["killed"] = not target.alive # 击杀标记在扣血后取(原语义不变)
# kill 发射点(内容数据驱动扩展批 T1——敌人死亡确认;贯穿射击 overflow 订阅面)
if bool(payload.get("killed", false)):
_fx_dispatch("kill", {"target": String(target.display_name), "overflow": landed})
# 碎甲震击(迭代 12 T4 普攻流——命中重甲附加「护甲裂解」):复用 off_guard(enemy percent
# +25 受伤加深——零新状态行);apply_effect 域闭/重复授予由状态容器自行守卫。
var crack := int(feat_flags.get("crack_value", 0))
@@ -308,6 +344,9 @@ func _resolve_enemy_skill(t: Dictionary) -> void:
var rolled: int = DamageMath.rolled_component(float(t.base_value), discount, crit, int(db.settlement.crit_multiplier), 0)
var pooled: int = DamageMath.pool_damage(rolled, clamped_pool("hero_defense"), clamped_pool("enemy_output"))
var total: int = maxi(int(db.settlement.damage_floor), pooled) + int(t.rider) # rider 后置于伤害下限——独立必中加项,不进乘区
# damage_taken 发射点(内容数据驱动扩展批 T1——kind=enemy_skill;铁壁减伤订阅面)
_fx_dispatch("damage_taken", {"kind": "enemy_skill", "amount": total,
"source": String(t.context.get("skill_id", "")), "round": int(t.round_index)})
# 铁壁整备(迭代 12 T5 蓄力流——每层下轮开场 S 点减伤;减后过下限保底)
var shield := int(feat_flags.get("shield_per_layer", 0)) * int(feat_flags.get("charge_layers", 0))
# 稳健拓荒(迭代 12 T10——Fork posture B 侧 rule 键:受击减 1;两侧对称弱值=「不同强非强弱」)
@@ -337,7 +376,12 @@ func _resolve_board_typed(t: Dictionary) -> void:
_ineffective_row(t, "无存活敌人")
_outcomes[int(t.ticket_id)] = "zero"
return
var delta: int = _counter_delta(String(t.attack_type), String(target.get("armor_type", "light"))) + _feat_clamp_bonus(String(t.attack_type), String(target.get("armor_type", "light")))
var armor_b: String = String(target.get("armor_type", "light"))
var atk_b: String = String(t.attack_type)
# clamp_check 发射点(内容数据驱动扩展批 T1——克制判定修正点棋盘面;与 hero_attack 同表同点)
if atk_b != "":
_fx_dispatch("clamp_check", {"matchup": atk_b + "_vs_" + armor_b})
var delta: int = _counter_delta(atk_b, armor_b) + _feat_clamp_bonus(atk_b, armor_b)
var dealt: int = DamageMath.counter_component(int(t.base_value), delta, int(db.settlement.damage_floor))
units.apply_damage(target, dealt)
emit_row_cb.call("damage", String(t.source_class), t.origin_cell, int(t.round_index), "%s → %s" % [String(t.display), String(target.display_name)], dealt,