130 lines
7.5 KiB
GDScript
130 lines
7.5 KiB
GDScript
# tests/unit/battle_settlement/effect_port_test.gd — 效果端口件验收宿主(效果端口与掷格分件批)
|
||
# 断言面 = 三套(需求卡片 §9 对照表行 1/2/3 载体):
|
||
# V1 端口单实例与真实发射路由(装配链同一实例+attack_commit 经真实战斗流命中可观测)
|
||
# V2 业务类零引擎直连(源码契约锚——两业务文件全文零 EffectEngine/EffectContext token)
|
||
# V3 伤害域事实面闭集(合成行阳性/阴性对照——载荷携带事实可达、宿主链事实不可达)
|
||
# 协议照单元侧惯例:extends SceneTree+Kit.check 主通道;--script 模式无 autoload,
|
||
# ConfigDB 显式实例化构造注入(各套自建 RngService——源套掷点隔离形态)。
|
||
# 运行:godot --headless --path game --script res://tests/unit/battle_settlement/effect_port_test.gd
|
||
extends SceneTree
|
||
|
||
const Kit := preload("res://tests/unit/test_kit.gd")
|
||
const ConfigDBScript := preload("res://core/config_db.gd")
|
||
const EffectEngineScript := preload("res://core/effect_engine.gd")
|
||
|
||
const TEST_SEED := 42 # 夹具种子(基建值非玩法数值——同种子两跑对比的隔离前提)
|
||
const LAYER_COUNTER := "swap_charge_layers" # 整备军备蓄力层计数器(feats.tres charge_rotation 行)
|
||
|
||
var _db
|
||
|
||
|
||
func _init() -> void:
|
||
_db = ConfigDBScript.new()
|
||
var load_ok: bool = _db.load_tables("res://data/tables", true)
|
||
Kit.check("前置:表加载零 error", load_ok and _db.diagnostics_count("error") == 0)
|
||
if load_ok:
|
||
_smoke_effect_port_wiring()
|
||
_test_business_classes_engine_free()
|
||
_test_biz_fact_face_closed()
|
||
quit(Kit.finish("effect_port_test"))
|
||
|
||
|
||
# ---- 夹具 ----
|
||
|
||
func _enc(feats: Array) -> Dictionary:
|
||
return {"level_index": 1, "node_depth": 0, "hero_hp": int(_db.combat.hero.max_hp),
|
||
"hero_armor_class": 10, "hero_ap_bonus": 0, "attunement": [], "feats": feats, "tree_rules": [],
|
||
"battle_index": 1, "voucher_rap": 0, "voucher_time_sand": 0, "node_type": "boss", "adv_bucket": {}}
|
||
|
||
|
||
func _orch(seed_v: int):
|
||
var rng = load("res://core/rng_service.gd").new()
|
||
rng.set_master_seed(seed_v)
|
||
return load("res://core/battle_orchestrator.gd").new(_db, rng)
|
||
|
||
|
||
func _hero_attack_value(rows: Array) -> int:
|
||
for r in rows:
|
||
if String(r.get("type", "")) == "damage" and String(r.get("source_class", "")) == "hero_attack":
|
||
return int(r.get("value", 0))
|
||
return -1
|
||
|
||
|
||
# ---- V1 端口单实例与真实发射路由 ----
|
||
|
||
func _smoke_effect_port_wiring() -> void:
|
||
print("--- 效果端口:装配链单实例+真实发射路由 ---")
|
||
# 充能跑(整备军备在册——attack_commit 订阅行 attack_bonus per 蓄力层计数器)
|
||
var orch_c = _orch(TEST_SEED)
|
||
var rows_c: Array = []
|
||
orch_c.step_row.connect(func(row): rows_c.append(row))
|
||
Kit.check("V1 begin_battle 受理", orch_c.begin_battle(_enc(["charge_rotation"])))
|
||
# 端口同一性(内部字段直查=受控例外:端口同一性无公开查询面,对照表行 1 注明理由)
|
||
var port_d = orch_c.fx_host().damage._port
|
||
var port_a = orch_c.action_economy._port
|
||
Kit.check("V1 端口在座(damage/action_economy 双绑)", port_d != null and port_a != null)
|
||
Kit.check("V1 端口单实例(两业务类持同一 EffectPort)", port_d == port_a)
|
||
# 蓄力层预置 3(调试注入两层·数据面粒度——计数器账面直写,与 refresh_feats_test V8 同法)
|
||
EffectEngineScript.counter_add(orch_c.fx_host().counters_ctx, LAYER_COUNTER, 3)
|
||
# 最小回合:停手即轮末结算(主角先手攻击在战斗段同步兑付——零 await 同步链)
|
||
orch_c.minesweeper.settle_batch([{"action": "stop"}])
|
||
var v_c := _hero_attack_value(rows_c)
|
||
Kit.check("V1 attack_commit 命中批可观测:主角攻击行在流", v_c >= 0)
|
||
Kit.check("V1 命中批经端口路由全处理(attack_bonus 消费行+counter_add set-0 行——层账 3→0)",
|
||
EffectEngineScript.counter(orch_c.fx_host().counters_ctx, LAYER_COUNTER) == 0)
|
||
# 对照跑(同种子同 enc 零预置——attack_commit 订阅行 when 不通过=零命中零扰动)
|
||
var orch_0 = _orch(TEST_SEED)
|
||
var rows_0: Array = []
|
||
orch_0.step_row.connect(func(row): rows_0.append(row))
|
||
orch_0.begin_battle(_enc(["charge_rotation"]))
|
||
orch_0.minesweeper.settle_batch([{"action": "stop"}])
|
||
var v_0 := _hero_attack_value(rows_0)
|
||
Kit.check("V1 对照跑攻击行在流", v_0 >= 0)
|
||
Kit.check("V1 delta 读数非零(蓄力层加算进入主角攻击基值)", v_c - v_0 >= 1)
|
||
|
||
|
||
# ---- V2 业务类零引擎直连(源码契约锚——theme_projection_test 源码锚惯例)----
|
||
|
||
func _test_business_classes_engine_free() -> void:
|
||
print("--- 效果端口:业务类零引擎直连源码锚 ---")
|
||
for path in ["res://core/damage_controller.gd", "res://core/action_economy.gd"]:
|
||
var src := FileAccess.get_file_as_string(path)
|
||
Kit.check("V2 扫描面非空前置:%s" % path, src.length() > 0)
|
||
Kit.check("V2 全文零引擎直连 token(EffectEngine/EffectContext):%s" % path,
|
||
not src.contains("EffectEngine") and not src.contains("EffectContext"))
|
||
|
||
|
||
# ---- V3 伤害域事实面闭集(合成行两对照——damage_taken 真实单据流)----
|
||
|
||
func _test_biz_fact_face_closed() -> void:
|
||
print("--- 效果端口:业务发射事实面闭集 ---")
|
||
var orch = _orch(TEST_SEED)
|
||
Kit.check("V3 begin_battle 受理", orch.begin_battle(_enc([])))
|
||
# 合成行直改数据面(调试注入两层·数据面粒度):effect 段逐字抄现役铁壁 shield 表行(kind/value 零推表);
|
||
# rebuild 走持有 id 过滤真实表行——合成行经直改表行进入装载域(订阅面换代为合成行独占,确定性)
|
||
var pos_id := "probe_shield_pos"
|
||
var neg_id := "probe_shield_neg"
|
||
_db.crossrun.feats.rows.append({"feat_id": pos_id, "display_name": "探针盾甲甲", "rarity": "common",
|
||
"desc": "V3 阳性对照(hp_ratio 载荷携带)", "triggers": [{"on": "damage_taken",
|
||
"when": [{"fact": "hp_ratio", "op": "lte", "value": 100}], "do": {"kind": "shield", "value": 1}}]})
|
||
_db.crossrun.feats.rows.append({"feat_id": neg_id, "display_name": "探针盾甲乙", "rarity": "common",
|
||
"desc": "V3 阴性对照(battle_index 闭面外)", "triggers": [{"on": "damage_taken",
|
||
"when": [{"fact": "battle_index", "op": "eq", "value": 1}], "do": {"kind": "shield", "value": 1}}]})
|
||
var ctrl = orch.fx_host().damage
|
||
# 阳性对照(载荷携带事实——harness 能见命中):合成行独占订阅面(rebuild 持有 id 过滤——
|
||
# 只挂阳性行),board_flat_hero 真实单据流,base_value 足量避开伤害下限保底
|
||
orch.fx_host().rebuild_subscriptions([pos_id], [], [])
|
||
var hp0 := int(ctrl.units.hero_unit().get("hp", 0))
|
||
var t_a: Dictionary = ctrl.open_ticket(ctrl.KIND_BOARD_FLAT_HERO, "probe_port", "闭面探测甲",
|
||
{"channel": "board", "base_value": 10, "target_rule": "hero", "ri": 1, "origin_cell": orch.minesweeper.hero_pos()})
|
||
ctrl.flush_submit(t_a)
|
||
var hp1 := int(ctrl.units.hero_unit().get("hp", 0))
|
||
Kit.check("V3 阳性对照:hp_ratio 载荷 when 通过——承伤减量恰 1(10→9)", hp0 - hp1 == 9)
|
||
# 阴性对照(闭面外事实——宿主链答而业务面不答):订阅面换代为阴性合成行独占,同路径零减量=F10 现状形态守卫
|
||
orch.fx_host().rebuild_subscriptions([neg_id], [], [])
|
||
var t_b: Dictionary = ctrl.open_ticket(ctrl.KIND_BOARD_FLAT_HERO, "probe_port", "闭面探测乙",
|
||
{"channel": "board", "base_value": 10, "target_rule": "hero", "ri": 1, "origin_cell": orch.minesweeper.hero_pos()})
|
||
ctrl.flush_submit(t_b)
|
||
var hp2 := int(ctrl.units.hero_unit().get("hp", 0))
|
||
Kit.check("V3 阴性对照:battle_index 不答——零减量(10→10,事实面未扩)", hp1 - hp2 == 10)
|