test(卡② T7-F2): 旧测试断言随无状态化换代迁移(smoke 裸引擎段 + 4 单测件)
- smoke_all 裸引擎段 ~53 dispatch/写口 ctx 化、twin_digs→adv_twin_digs - _smoke_tree_rules:standalone 引擎自建 ctx、sense 限次跨战持久改 level 桶共享引用(删 export/seed 链) - effect_words_t1_test ⑥⑦重写为桶语义(adventure 共享字典往返、round_ 前缀随 round_start 整桶清空、battle 无前缀存活、counter_spend 静态) - tree_rules_v11_test 合成行夹具 lvl_ 改名+删 domain;treasure_effects/settle_segments dispatch/counter ctx 化、dig_combo→geo_types 活 id 冒烟 1150/0、四单测件 EXIT=0(换代同生死,无假绿)。
This commit is contained in:
1 parent
4c5bdbc9aa
commit
b226a292df
5 files changed
+150
-142
No files matched your search
+89
-85
@@ -1422,188 +1422,188 @@ func _smoke_feats() -> void:
|
||||
for h in hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
var sv_c = h.get("set", null)
|
||||
fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)) if sv_c == null else int(sv_c), sv_c != null)
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)) if sv_c == null else int(sv_c), sv_c != null)
|
||||
# 开格系
|
||||
fx.rebuild(["twin_pick"], [], [])
|
||||
for i in 4:
|
||||
var hits_dp: Array = fx.dispatch("dig_paid", {"ap_cost": 1})
|
||||
var hits_dp: Array = fx.dispatch(orch.counters_ctx, "dig_paid", {"ap_cost": 1})
|
||||
var refund := false
|
||||
for h in hits_dp:
|
||||
if String(h.get("kind", "")) == "grant_ap":
|
||||
refund = true
|
||||
fx.counter_add("twin_digs", 1)
|
||||
EffectEngine.counter_add(orch.counters_ctx, "adv_twin_digs", 1)
|
||||
if i == 1 or i == 3:
|
||||
check(refund, "双管镐:第 %d 次付费挖格返 1 AP(每第 2 次——mod_eq 预评估口径)" % (i + 1))
|
||||
else:
|
||||
check(not refund, "双管镐反例:第 %d 次不返(未满 2 次)" % (i + 1))
|
||||
fx.rebuild(["geologist"], [], [])
|
||||
var geo_hits: Array = fx.dispatch("dig_reveal", {"new_resource_type": true})
|
||||
var geo_hits: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"new_resource_type": true})
|
||||
apply_fx.call(geo_hits)
|
||||
check(orch._hits_of(geo_hits, "counter_add").size() == 1, "地质学家:首开新类型集 1 点〔专长批 V3〕")
|
||||
fx.counter_add("geo_types", 2) # 手动基数补齐(row1 已落账 1+2=3——满 4 种的预评估现值)
|
||||
var geo_hits2: Array = fx.dispatch("dig_reveal", {"new_resource_type": true})
|
||||
EffectEngine.counter_add(orch.counters_ctx, "geo_types", 2) # 手动基数补齐(row1 已落账 1+2=3——满 4 种的预评估现值)
|
||||
var geo_hits2: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"new_resource_type": true})
|
||||
apply_fx.call(geo_hits2)
|
||||
check(orch._hits_of(geo_hits2, "reveal_tiles").size() == 1 and fx.counter("geo_types") == 0,
|
||||
check(orch._hits_of(geo_hits2, "reveal_tiles").size() == 1 and EffectEngine.counter(orch.counters_ctx, "geo_types") == 0,
|
||||
"地质学家:满 4 种(预评估现值 3+本格)→揭示 1 格·计数清零〔专长批 V3〕")
|
||||
var geo_hits3: Array = fx.dispatch("dig_reveal", {"new_resource_type": false})
|
||||
var geo_hits3: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"new_resource_type": false})
|
||||
check(orch._hits_of(geo_hits3, "counter_add").is_empty(), "地质学家反例:同类型重复首开不叠点〔专长批 V3〕")
|
||||
fx.rebuild(["hard_hat"], [], [])
|
||||
apply_fx.call(fx.dispatch("dig_reveal", {}))
|
||||
apply_fx.call(fx.dispatch("dig_reveal", {}))
|
||||
check(fx.counter("helm_layers") == 2, "安全帽:开安全格攒层 ×2〔专长批 V3〕")
|
||||
var hh_hits: Array = fx.dispatch("mine_hit", {"damage": 6})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "dig_reveal", {}))
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "dig_reveal", {}))
|
||||
check(EffectEngine.counter(orch.counters_ctx, "helm_layers") == 2, "安全帽:开安全格攒层 ×2〔专长批 V3〕")
|
||||
var hh_hits: Array = fx.dispatch(orch.counters_ctx, "mine_hit", {"damage": 6})
|
||||
var hh_reduce := 0
|
||||
for h in hh_hits:
|
||||
if String(h.get("kind", "")) == "damage_reduce":
|
||||
hh_reduce = fx.counter(String(h.get("value_counter", "")))
|
||||
hh_reduce = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(hh_reduce == 2, "安全帽:踩雷耗层减伤=层值 2(一次清零随施加面 set 行)〔专长批 V3〕")
|
||||
fx.rebuild(["skip_mining"], [], [])
|
||||
var sk_zero: Array = fx.dispatch("dig_reveal", {"is_blank_zero": true})
|
||||
var sk_zero: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": true})
|
||||
apply_fx.call(sk_zero)
|
||||
check(orch._hits_of(sk_zero, "counter_add").size() == 1, "跳格采掘:空白 0 格攒 1 点〔专长批 V3〕")
|
||||
fx.counter_add("skip_pts", 1) # 手动基数补齐(已落账 1+1=2——满 3 点的预评估现值)
|
||||
var sk_full: Array = fx.dispatch("dig_reveal", {"is_blank_zero": true})
|
||||
EffectEngine.counter_add(orch.counters_ctx, "skip_pts", 1) # 手动基数补齐(已落账 1+1=2——满 3 点的预评估现值)
|
||||
var sk_full: Array = fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": true})
|
||||
apply_fx.call(sk_full)
|
||||
check(orch._hits_of(sk_full, "reveal_tiles").size() == 1,
|
||||
"跳格采掘:满 3 点→揭示 1 格〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch("dig_reveal", {"is_blank_zero": false}), "counter_add").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_blank_zero": false}), "counter_add").is_empty(),
|
||||
"跳格采掘反例:普通开格不计点〔专长批 V3〕")
|
||||
fx.rebuild(["blast_accounting"], [], [])
|
||||
for i in 5:
|
||||
var ba_hits: Array = fx.dispatch("dig_paid", {"ap_cost": 1})
|
||||
var ba_hits: Array = fx.dispatch(orch.counters_ctx, "dig_paid", {"ap_cost": 1})
|
||||
for h in ba_hits:
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx.counter_add(String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch("round_end", {}), "grant_gp").size() == 1, "爆破会计:本轮挖格花费≥5→+1 GP〔专长批 V3〕")
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1, "爆破会计:本轮挖格花费≥5→+1 GP〔专长批 V3〕")
|
||||
fx.rebuild(["frontier_miner"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("dig_reveal", {"is_edge_cell": true}), "grant_material").size() == 1
|
||||
and orch._hits_of(fx.dispatch("dig_reveal", {"is_edge_cell": false}), "grant_material").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": true}), "grant_material").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_reveal", {"is_edge_cell": false}), "grant_material").is_empty(),
|
||||
"边陲矿工:边缘格产出 +1·中心格反例零产出〔专长批 V3〕")
|
||||
# 封印系
|
||||
fx.rebuild(["seal_echo"], [], [])
|
||||
apply_fx.call(fx.dispatch("seal_hit", {}))
|
||||
apply_fx.call(fx.dispatch("seal_hit", {}))
|
||||
var se_hits: Array = fx.dispatch("round_end", {})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "seal_hit", {}))
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "seal_hit", {}))
|
||||
var se_hits: Array = fx.dispatch(orch.counters_ctx, "round_end", {})
|
||||
check(orch._hits_of(se_hits, "grant_ap").size() == 1 and String((orch._hits_of(se_hits, "grant_ap")[0] as Dictionary).get("mode", "")) == "queue",
|
||||
"封印回响:标中计数≥2→轮末返 1 AP(下轮 queue)〔专长批 V3〕")
|
||||
fx.rebuild(["seal_smith"], [], [])
|
||||
var sm_remove := 0
|
||||
for i in 3:
|
||||
var sm_dispatch: Array = fx.dispatch("seal_hit", {})
|
||||
var sm_dispatch: Array = fx.dispatch(orch.counters_ctx, "seal_hit", {})
|
||||
apply_fx.call(sm_dispatch)
|
||||
for h in sm_dispatch:
|
||||
if String(h.get("kind", "")) == "remove_mines":
|
||||
sm_remove += 1
|
||||
check(sm_remove == 1, "封印匠:标中每第 3 次→中立移除恰一次(频率闸死)〔专长批 V3〕")
|
||||
fx.rebuild(["seal_stake"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("seal_hit", {}), "ac_bonus").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch("seal_hit", {}), "ac_bonus")[0] as Dictionary).get("mode", "")) == "round",
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "ac_bonus")[0] as Dictionary).get("mode", "")) == "round",
|
||||
"封印桩:标中本轮 AC +N(round 域——round_start 清零)〔专长批 V3〕")
|
||||
fx.rebuild(["warning_beacon"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("seal_hit", {}), "preview_area").size() == 1, "示警烟火:标中揭示 3×3 资源类型(不揭安危位置)〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "preview_area").size() == 1, "示警烟火:标中揭示 3×3 资源类型(不揭安危位置)〔专长批 V3〕")
|
||||
# 踩雷/受伤系
|
||||
fx.rebuild(["pain_memory"], [], [])
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
var pm_hits: Array = fx.dispatch("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
var pm_hits: Array = fx.dispatch(orch.counters_ctx, "attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
var pm_bonus := 0
|
||||
for h in orch._hits_of(pm_hits, "attack_bonus"):
|
||||
pm_bonus = fx.counter(String(h.get("value_counter", "")))
|
||||
pm_bonus = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(pm_bonus == 2, "疼痛记忆:攻击附加层值伤害=层值 2·用后清零(set 行随施加面)〔专长批 V3〕")
|
||||
fx.rebuild(["fuse_wire"], [], [])
|
||||
var fw_hits: Array = fx.dispatch("damage_taken", {"kind": "terrain", "amount": 6})
|
||||
var fw_hits: Array = fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 6})
|
||||
var fw_shield := 0
|
||||
for h in fw_hits:
|
||||
if String(h.get("kind", "")) == "shield":
|
||||
fw_shield += int(h.get("value", 0))
|
||||
check(orch._hits_of(fx.dispatch("damage_taken", {"kind": "terrain", "amount": 3}), "shield").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 3}), "shield").is_empty(),
|
||||
"保险丝反例:单次 <5 不攒点不触发〔专长批 V3〕")
|
||||
for h in fx.dispatch("damage_taken", {"kind": "terrain", "amount": 6}):
|
||||
for h in fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "terrain", "amount": 6}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
check(fw_shield == 0, "保险丝:首点不出盾(2 点才出——预评估口径)〔专长批 V3〕")
|
||||
fx.rebuild(["blood_insight"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {}), "reveal_tiles").size() == 1, "见血开窍:踩雷揭示 1 格〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "reveal_tiles").size() == 1, "见血开窍:踩雷揭示 1 格〔专长批 V3〕")
|
||||
fx.rebuild(["trap_instinct"], [], [])
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
var ti_hits: Array = fx.dispatch("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
var ti_hits: Array = fx.dispatch(orch.counters_ctx, "attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0})
|
||||
var ti_val := 0
|
||||
for h in orch._hits_of(ti_hits, "attack_bonus"):
|
||||
ti_val = fx.counter(String(h.get("value_counter", "")))
|
||||
ti_val = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(ti_val == 2, "陷阱本能:踩雷本轮攻击 +2(attack_bonus 通道——面板加算)〔专长批 V3〕")
|
||||
fx.rebuild(["chain_traps", "pain_memory"], [], [])
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
var ct_before: int = fx.counter("pain_layers")
|
||||
apply_fx.call(fx.dispatch("mine_hit", {}))
|
||||
check(fx.counter("pain_layers") == ct_before + 2, "连环陷阱:同轮第 2 颗起每颗额外 +1 层(疼痛记忆同账)〔专长批 V3〕")
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
var ct_before: int = EffectEngine.counter(orch.counters_ctx, "pain_layers")
|
||||
apply_fx.call(fx.dispatch(orch.counters_ctx, "mine_hit", {}))
|
||||
check(EffectEngine.counter(orch.counters_ctx, "pain_layers") == ct_before + 2, "连环陷阱:同轮第 2 颗起每颗额外 +1 层(疼痛记忆同账)〔专长批 V3〕")
|
||||
fx.rebuild(["trap_burst"], [], [])
|
||||
var tb_hits: Array = fx.dispatch("mine_hit", {"damage": 8})
|
||||
var tb_hits: Array = fx.dispatch(orch.counters_ctx, "mine_hit", {"damage": 8})
|
||||
check(orch._hits_of(tb_hits, "blast_scaled").size() == 1
|
||||
and float((orch._hits_of(tb_hits, "blast_scaled")[0] as Dictionary).get("mult", 0)) > 1.0,
|
||||
"陷阱爆发:全体敌雷伤×倍率直伤(blast_scaled——施加面值=damage×mult)〔专长批 V3〕")
|
||||
fx.rebuild(["blood_honey"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {}), "heal").size() == 1, "血蜜:踩雷回复 N 生命〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "heal").size() == 1, "血蜜:踩雷回复 N 生命〔专长批 V3〕")
|
||||
fx.rebuild(["stone_skin"], [], [])
|
||||
var ss_hits: Array = fx.dispatch("damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1})
|
||||
var ss_hits: Array = fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1})
|
||||
check(orch._hits_of(ss_hits, "ac_bonus").size() == 1
|
||||
and int((orch._hits_of(ss_hits, "ac_bonus")[0] as Dictionary).get("cap", 0)) == 3,
|
||||
"石肤:受伤 AC+1(本场域 cap 3——达上限不再涨由累积器封顶)〔专长批 V3〕")
|
||||
fx.rebuild(["last_stand"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1, "hp_ratio": 40}), "damage_reduce").size() == 1
|
||||
and orch._hits_of(fx.dispatch("damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 2, "hp_ratio": 80}), "damage_reduce").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 1, "hp_ratio": 40}), "damage_reduce").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "damage_taken", {"kind": "enemy_skill", "amount": 5, "source": "s", "round": 2, "hp_ratio": 80}), "damage_reduce").is_empty(),
|
||||
"背水:生命≤50% 减伤·过半血反例零减伤〔专长批 V3〕")
|
||||
# 跨族桥
|
||||
fx.rebuild(["breaker_strike"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {"dug_under_seal": true}), "counter_add").size() == 1
|
||||
and orch._hits_of(fx.dispatch("mine_hit", {"dug_under_seal": false}), "counter_add").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": true}), "counter_add").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {"dug_under_seal": false}), "counter_add").is_empty(),
|
||||
"破封一击:挖开带封印危险格 +1 层·无封印反例零触发〔专长批 V3〕")
|
||||
# 节奏系
|
||||
fx.rebuild(["map_exchange"], [], [])
|
||||
var me_hits: Array = fx.dispatch("dig_stop", {"ap_left": 4})
|
||||
var me_hits: Array = fx.dispatch(orch.counters_ctx, "dig_stop", {"ap_left": 4})
|
||||
apply_fx.call(me_hits)
|
||||
fx.counter_add("stop_swap", 4) # value_event="ap_left" 解析归施加面(action_economy 载荷取值)——冒烟手动落账
|
||||
EffectEngine.counter_add(orch.counters_ctx, "stop_swap", 4) # value_event="ap_left" 解析归施加面(action_economy 载荷取值)——冒烟手动落账
|
||||
check(orch._hits_of(me_hits, "counter_add").size() == 1, "弃图兑换:停手剩余 AP≥3 入账〔专长批 V3〕")
|
||||
var me_atk := 0
|
||||
for h in orch._hits_of(fx.dispatch("attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0}), "attack_bonus"):
|
||||
me_atk = fx.counter(String(h.get("value_counter", "")))
|
||||
for h in orch._hits_of(fx.dispatch(orch.counters_ctx, "attack_commit", {"atk": 10, "target": "敌", "matchup": "", "dig_streak": 0, "nest_count": 0}), "attack_bonus"):
|
||||
me_atk = EffectEngine.counter(orch.counters_ctx, String(h.get("value_counter", "")))
|
||||
check(me_atk == 4, "弃图兑换:折为攻击伤害=剩余 AP 全额·用后清零〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch("dig_stop", {"ap_left": 2}), "counter_add").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "dig_stop", {"ap_left": 2}), "counter_add").is_empty(),
|
||||
"弃图兑换反例:剩余 AP<3 不触发〔专长批 V3〕")
|
||||
fx.rebuild(["last_drop"], [], [])
|
||||
var ld_hits: Array = fx.dispatch("round_start", {"round": 2, "ap_exhausted_last": true})
|
||||
var ld_hits: Array = fx.dispatch(orch.counters_ctx, "round_start", {"round": 2, "ap_exhausted_last": true})
|
||||
check(orch._hits_of(ld_hits, "grant_ap").size() == 1, "一滴不剩:上轮 AP 耗尽→+1 AP〔专长批 V3〕")
|
||||
check(orch._hits_of(fx.dispatch("round_start", {"round": 3, "ap_exhausted_last": false}), "grant_ap").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "round_start", {"round": 3, "ap_exhausted_last": false}), "grant_ap").is_empty(),
|
||||
"一滴不剩反例:上轮有余不发〔专长批 V3〕")
|
||||
fx.rebuild(["market_close"], [], [])
|
||||
for i in 8:
|
||||
for h in fx.dispatch("dig_reveal", {}):
|
||||
for h in fx.dispatch(orch.counters_ctx, "dig_reveal", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx.counter_add(String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch("round_end", {}), "grant_gp").size() == 1, "收盘:本轮开格≥8→+1 GP〔专长批 V3〕")
|
||||
EffectEngine.counter_add(orch.counters_ctx, String(h.get("counter_id", "")), 1)
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "round_end", {}), "grant_gp").size() == 1, "收盘:本轮开格≥8→+1 GP〔专长批 V3〕")
|
||||
# 战斗系
|
||||
fx.rebuild(["execution_scholar"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("kill", {"target": "敌", "overflow": 0}), "reveal_tiles").size() == 1,
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "kill", {"target": "敌", "overflow": 0}), "reveal_tiles").size() == 1,
|
||||
"斩首学者:击杀揭示 1 格〔专长批 V3〕")
|
||||
fx.rebuild(["executioner"], [], [])
|
||||
var ex_hits: Array = fx.dispatch("enemy_hp_threshold", {"unit": "e1", "pct": 30, "is_boss": false})
|
||||
var ex_hits: Array = fx.dispatch(orch.counters_ctx, "enemy_hp_threshold", {"unit": "e1", "pct": 30, "is_boss": false})
|
||||
check(orch._hits_of(ex_hits, "target_attack_bonus").size() == 1
|
||||
and orch._hits_of(fx.dispatch("enemy_hp_threshold", {"unit": "e1", "pct": 60, "is_boss": false}), "target_attack_bonus").is_empty(),
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "enemy_hp_threshold", {"unit": "e1", "pct": 60, "is_boss": false}), "target_attack_bonus").is_empty(),
|
||||
"处刑台:敌血量≤30% 挂单位标记加伤·阈值外反例零命中〔专长批 V3〕")
|
||||
fx.rebuild(["field_dressing"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("battle_won", {"round": 2}), "heal").size() == 1,
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "battle_won", {"round": 2}), "heal").size() == 1,
|
||||
"战地包扎:战斗胜利回复 N 生命(battle_won 新挂点)〔专长批 V3〕")
|
||||
# 契约尖点
|
||||
fx.rebuild(["blood_tax"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("mine_hit", {}), "bonus_delta").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch("seal_hit", {}), "bonus_delta")[0] as Dictionary).get("axis", "")) == "atk",
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "mine_hit", {}), "bonus_delta").size() == 1
|
||||
and String((orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "bonus_delta")[0] as Dictionary).get("axis", "")) == "atk",
|
||||
"血税:双向同局账本(踩陷阱−2max_hp/封印命中+1atk——bonus_delta 战末汇入)〔专长批 V3〕")
|
||||
# 迁入 2 件
|
||||
fx.rebuild(["venom_edge"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("clamp_check", {"matchup": "magic_vs_heavy"}), "modify_ticket").size() == 1
|
||||
and orch._hits_of(fx.dispatch("clamp_check", {"matchup": "normal_vs_light"}), "modify_ticket").is_empty(),
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "magic_vs_heavy"}), "modify_ticket").size() == 1
|
||||
and orch._hits_of(fx.dispatch(orch.counters_ctx, "clamp_check", {"matchup": "normal_vs_light"}), "modify_ticket").is_empty(),
|
||||
"淬毒刃纹(迁入):克制正面(magic_vs_heavy 矩阵值>0)追加·中性面反例零触发〔专长批 V3〕")
|
||||
fx.rebuild(["seal_burst"], [], [])
|
||||
check(orch._hits_of(fx.dispatch("seal_hit", {}), "open_ticket").size() == 1,
|
||||
check(orch._hits_of(fx.dispatch(orch.counters_ctx, "seal_hit", {}), "open_ticket").size() == 1,
|
||||
"封印爆裂(迁入):标中全体敌群伤〔专长批 V3〕")
|
||||
# 零持有
|
||||
var orch0 = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(74))
|
||||
@@ -6051,36 +6051,40 @@ func _smoke_tree() -> void:
|
||||
# 引擎直构 + 编排授予段直注(_grant_seg._judge_seal=结算授予段封印判定单口))----
|
||||
func _smoke_tree_rules() -> void:
|
||||
print("--- 天赋树六键行为(V10:窗/限次/聚散/守侧)---")
|
||||
# 开局姿态组:每关首轮窗(battle_index 事实——引擎直构)
|
||||
# 开局姿态组:每关首轮窗(battle_index 事实——引擎直构 + 自建 ctx)
|
||||
var fx = load("res://core/effect_engine.gd").new(null)
|
||||
fx.rebuild([], [], ["posture_first_strike", "posture_bulwark"])
|
||||
check((fx.dispatch("round_start", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1,
|
||||
var ctx_p := EffectContext.new()
|
||||
check((fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1,
|
||||
"先手压制:每关首战首轮窗命中(battle_index=1 且 round=1 → grant_ap queue)〔天赋树批 V10〕")
|
||||
check((fx.dispatch("round_start", {"battle_index": 1, "round_index": 2}, Callable()) as Array).is_empty()
|
||||
and (fx.dispatch("round_start", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
check((fx.dispatch(ctx_p, "round_start", {"battle_index": 1, "round_index": 2}, Callable()) as Array).is_empty()
|
||||
and (fx.dispatch(ctx_p, "round_start", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
"先手压制:首轮窗外地不触发(第二轮/第二关首战首轮——「每战首轮」旧语义换代件)")
|
||||
check((fx.dispatch("damage_taken", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1
|
||||
and (fx.dispatch("damage_taken", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
check((fx.dispatch(ctx_p, "damage_taken", {"battle_index": 1, "round_index": 1}, Callable()) as Array).size() == 1
|
||||
and (fx.dispatch(ctx_p, "damage_taken", {"battle_index": 2, "round_index": 1}, Callable()) as Array).is_empty(),
|
||||
"稳健拓荒:首轮窗内受击出盾、窗外不出(shield damage_taken 同窗条件)")
|
||||
# 危险直觉·锋:限次闸(引擎直构——mine_hit 挂点现成,编排收集 halve_pct 系既有 damage_mult 路径)
|
||||
# 危险直觉·锋:限次闸(引擎直构——mine_hit 挂点现成;限次计数住 level 桶,跨战持久=共享同一字典引用)
|
||||
var fz = load("res://core/effect_engine.gd").new(null)
|
||||
fz.rebuild([], [], ["sense_sharp"])
|
||||
var lvl_shared := {} # 同一关的 level 桶(两编排器共享引用 = 跨战持久,替代旧 counters_export/seed 副本搬运)
|
||||
var ctx_fz := EffectContext.new()
|
||||
ctx_fz.level_bucket = lvl_shared
|
||||
var got_mult := false
|
||||
var got_add := false
|
||||
for h in (fz.dispatch("mine_hit", {}, Callable()) as Array):
|
||||
for h in (fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array):
|
||||
if String(h.get("kind", "")) == "damage_mult":
|
||||
got_mult = true
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
got_add = true
|
||||
check(got_mult and got_add, "锋:踩雷减半命中+限次记账(counter 0<2 闸开——damage_mult+counter_add 双行同预评估态)")
|
||||
fz.counter_add("tree_sense_sharp_used", 2) # 直调置数(V10 注入口径——置关卡域计数后走事件路径)
|
||||
check((fz.dispatch("mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)")
|
||||
var exported: Dictionary = fz.counters_export()
|
||||
EffectEngine.counter_add(ctx_fz, "lvl_tree_sense_sharp_used", 2) # 直调置数(V10 注入口径——置 level 桶计数后走事件路径)
|
||||
check((fz.dispatch(ctx_fz, "mine_hit", {}, Callable()) as Array).is_empty(), "锋:限次耗尽全额(counter 2 不<2 闸关——每关限次 provisional 2)")
|
||||
var fz2 = load("res://core/effect_engine.gd").new(null)
|
||||
fz2.rebuild([], [], ["sense_sharp"])
|
||||
fz2.counters_seed(exported)
|
||||
check((fz2.dispatch("mine_hit", {}, Callable()) as Array).is_empty(),
|
||||
"锋:限次跨战持久(adventure 域 counters_export/seed——断点保留同源,不新增存档域不升 v7)")
|
||||
var ctx_fz2 := EffectContext.new()
|
||||
ctx_fz2.level_bucket = lvl_shared # 挂同一 level 桶引用(新形态跨战持久:无 export/seed,同字典即持久)
|
||||
check((fz2.dispatch(ctx_fz2, "mine_hit", {}, Callable()) as Array).is_empty(),
|
||||
"锋:限次跨战持久(level 桶共享引用——替代旧 adventure 域 counters_export/seed 副本搬运)")
|
||||
# 守侧五场景(契约 12):编排 rig——begin_battle 携 sense_guard,授予段 _grant_seg._judge_seal 直注
|
||||
var orch = load("res://core/battle_orchestrator.gd").new(_db, _fresh_rng(777001))
|
||||
check(orch.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": 40, "hero_atk": 8, "hero_max_hp": 40,
|
||||
|
||||
@@ -26,12 +26,12 @@ func _init() -> void:
|
||||
"新效果类型在册:heal/ac_bonus/damage_reduce/bonus_delta/blast_scaled/preview_area/target_attack_bonus〔专长批 V1〕")
|
||||
Kit.check_quiet(EffectEngineScript.OPS.has("mod_eq"), "新算子在册:mod_eq〔专长批 V1〕")
|
||||
|
||||
# ② 合法新词行零违例(validate_trigger_row 正例)
|
||||
# ② 合法新词行零违例(validate_trigger_row 正例——前缀归属替代旧 domain 键)
|
||||
var errs: Array = []
|
||||
var ok_row := {"on": "dig_stop", "when": [{"fact": "ap_left", "op": "gte", "value": 3}],
|
||||
"do": {"kind": "counter_add", "counter_id": "stop_swap", "value_event": "ap_left", "domain": "adventure"}}
|
||||
"do": {"kind": "counter_add", "counter_id": "stop_swap", "value_event": "ap_left"}}
|
||||
var mine_covered: bool = EffectEngineScript.validate_trigger_row("V1正例", {}, [ok_row], errs)
|
||||
Kit.check_quiet(errs.is_empty() and not mine_covered, "合法新词行 validate 零违例(dig_stop/ap_left/counter_add+domain)〔专长批 V1〕")
|
||||
Kit.check_quiet(errs.is_empty() and not mine_covered, "合法新词行 validate 零违例(dig_stop/ap_left/counter_add——battle 桶无前缀)〔专长批 V1〕")
|
||||
errs = []
|
||||
EffectEngineScript.validate_trigger_row("V1正例2", {}, [{"on": "battle_won",
|
||||
"when": [{"fact": "hp_ratio", "op": "lte", "value": 1}],
|
||||
@@ -46,81 +46,85 @@ func _init() -> void:
|
||||
EffectEngineScript.validate_trigger_row("出域kind", {}, [{"on": "dig_stop", "do": {"kind": "nope_kind", "value": 1}}], errs)
|
||||
Kit.check_quiet(errs.size() == 4, "出域 on/fact/op/kind 各一行全部被拒(4 违例)〔专长批 V1〕")
|
||||
|
||||
# ④ 运行期命中序(合法新事件;dispatch 出域 fail-fast=debug assert〔effect_engine dispatch 首行
|
||||
# assert(EVENTS.has)——release 剥离系引擎语义,自动化覆盖走 ③ 加载期 validate 致命通道〕)
|
||||
# ④ 运行期命中序(合法新事件;dispatch 出域 fail-fast=debug assert;引擎无状态——计数器经 ctx)
|
||||
var fx = EffectEngineScript.new(null)
|
||||
var ctx_f := EffectContext.new()
|
||||
var hits: Array = []
|
||||
fx.rebuild([], [], [])
|
||||
fx._push_trigger("test", "v1_row", {"on": "dig_stop",
|
||||
"when": [{"fact": "ap_left", "op": "gte", "value": 3}],
|
||||
"do": {"kind": "counter_add", "counter_id": "stop_swap", "value_event": "ap_left"}}, {})
|
||||
hits = fx.dispatch("dig_stop", {"ap_left": 5})
|
||||
hits = fx.dispatch(ctx_f, "dig_stop", {"ap_left": 5})
|
||||
Kit.check_quiet(hits.size() == 1, "dig_stop 合法命中(ap_left 5≥3)〔专长批 V1〕")
|
||||
hits = fx.dispatch("dig_stop", {"ap_left": 1})
|
||||
hits = fx.dispatch(ctx_f, "dig_stop", {"ap_left": 1})
|
||||
Kit.check_quiet(hits.is_empty(), "dig_stop 反例不命中(ap_left 1<3)〔专长批 V1〕")
|
||||
Kit.check_quiet(not EffectEngineScript.EVENTS.has("dig_stop_x"),
|
||||
"词表封闭:出域事件词不在 EVENTS(dispatch 首行 assert 消费同一词表)〔专长批 V1〕")
|
||||
|
||||
# ⑤ mod_eq 算子(双管镐落法——预评估口径每第 2 次判定)
|
||||
# ⑤ mod_eq 算子(双管镐落法——预评估口径每第 2 次判定;battle 桶无前缀)
|
||||
var fx2 = EffectEngineScript.new(null)
|
||||
var ctx2 := EffectContext.new()
|
||||
fx2.rebuild([], [], [])
|
||||
fx2._push_trigger("test", "mod_row", {"on": "dig_paid",
|
||||
"when": [{"fact": "counter", "id": "dbl_digs", "op": "mod_eq", "value": 2, "ref": 1}],
|
||||
"do": {"kind": "grant_ap", "value": 1}}, {})
|
||||
var refunds := 0
|
||||
for i in 4:
|
||||
if not fx2.dispatch("dig_paid", {"ap_cost": 1}).is_empty():
|
||||
if not fx2.dispatch(ctx2, "dig_paid", {"ap_cost": 1}).is_empty():
|
||||
refunds += 1
|
||||
fx2.counter_add("dbl_digs", 1)
|
||||
EffectEngine.counter_add(ctx2, "dbl_digs", 1)
|
||||
Kit.check_quiet(refunds == 2, "mod_eq 预评估口径:4 次付费挖格恰返 2 次(第 2/4 次)〔专长批 V1〕")
|
||||
|
||||
# ⑥ adventure 域计数器 export/seed 往返(T6)
|
||||
# ⑥ adventure 桶共享引用跨引擎持久(T6 换代——替代旧 export/seed 副本搬运)
|
||||
var adv_bucket := {} # 同一冒险账本字典引用跨两引擎=跨战持久(无副本无搬运时点)
|
||||
var fx3 = EffectEngineScript.new(null)
|
||||
fx3._push_trigger("test", "adv_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "pain_layers", "value": 1, "domain": "adventure"}}, {})
|
||||
fx3._push_trigger("test", "adv_row2", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "round_mines", "value": 1, "reset_at": "round_start"}}, {})
|
||||
"do": {"kind": "counter_add", "counter_id": "adv_persist", "value": 1}}, {})
|
||||
var ctx3 := EffectContext.new()
|
||||
ctx3.adventure_bucket = adv_bucket
|
||||
for _i in 2:
|
||||
for h in fx3.dispatch("mine_hit", {}): # 施加面同构(编排侧 counter_add 逐条落账)
|
||||
for h in fx3.dispatch(ctx3, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx3.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
var exported: Dictionary = fx3.counters_export()
|
||||
Kit.check_quiet(exported.get("pain_layers", -1) == 2 and not exported.has("round_mines"),
|
||||
"export 只含 adventure 域(pain_layers=2、round_mines battle 域不入)〔专长批 V1〕")
|
||||
EffectEngine.counter_add(ctx3, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
Kit.check_quiet(EffectEngine.counter(ctx3, "adv_persist") == 2 and int(adv_bucket.get("adv_persist", -1)) == 2,
|
||||
"adventure 桶就地直写共享字典累积=2(adv_ 前缀归 adventure 桶——替代旧 counters_export)〔专长批 V1〕")
|
||||
var fx4 = EffectEngineScript.new(null)
|
||||
fx4._push_trigger("test", "adv_row_seed", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "pain_layers", "value": 1, "domain": "adventure"}}, {})
|
||||
fx4.counters_seed({"pain_layers": 7})
|
||||
Kit.check_quiet(fx4.counter("pain_layers") == 7, "seed 回灌:counters_seed 后 adventure 域计数器=7〔专长批 V1〕")
|
||||
fx4._push_trigger("test", "adv_row2", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "adv_persist", "value": 1}}, {})
|
||||
var ctx4 := EffectContext.new()
|
||||
ctx4.adventure_bucket = adv_bucket # 挂同一字典引用=下一战可读上一战持久值(替代旧 counters_seed 回灌)
|
||||
Kit.check_quiet(EffectEngine.counter(ctx4, "adv_persist") == 2,
|
||||
"adventure 桶共享引用跨引擎可读(无 export/seed,同字典即跨战持久)〔专长批 V1〕")
|
||||
|
||||
# ⑦ V4——攒层/计数类件跨事件计数(置计数→事件路径→清零点:round_start 重置/用后清零)
|
||||
# ⑦ V4——前缀归桶清零/消耗语义(round_ 桶随 round_start 整桶清空;battle 无前缀计数跨 round_start 存活)
|
||||
var fx5 = EffectEngineScript.new(null)
|
||||
var ctx5 := EffectContext.new()
|
||||
fx5.rebuild([], [], [])
|
||||
fx5._push_trigger("test", "round_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "round_mines", "value": 1}}, {}) # 未声明 reset_at——round_start 不重置(声明制对照面)
|
||||
fx5._push_trigger("test", "spend_row", {"on": "treasure_used",
|
||||
"do": {"kind": "counter_spend", "counter_id": "round_mines", "value": 1}}, {})
|
||||
for h in fx5.dispatch("mine_hit", {}):
|
||||
"do": {"kind": "counter_add", "counter_id": "round_mines", "value": 1}}, {})
|
||||
for h in fx5.dispatch(ctx5, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx5.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
for h in fx5.dispatch("mine_hit", {}):
|
||||
EffectEngine.counter_add(ctx5, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
for h in fx5.dispatch(ctx5, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx5.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
Kit.check_quiet(fx5.counter("round_mines") == 2, "V4:跨事件计数累积(两雷两计)〔专长批 V4〕")
|
||||
Kit.check_quiet(fx5.counter_spend("round_mines", 1) and fx5.counter("round_mines") == 1,
|
||||
"V4:counter_spend 写口消耗(用后清零语义承载面)〔专长批 V4〕")
|
||||
fx5.dispatch("round_start", {"round": 2})
|
||||
Kit.check_quiet(fx5.counter("round_mines") == 1,
|
||||
"V4:非 reset 点计数器不受 round_start 影响(reset_at 声明制——round_mines 未声明重置)〔专长批 V4〕")
|
||||
EffectEngine.counter_add(ctx5, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
Kit.check_quiet(EffectEngine.counter(ctx5, "round_mines") == 2, "V4:跨事件计数累积(两雷两计——round_ 前缀落 round 桶)〔专长批 V4〕")
|
||||
Kit.check_quiet(EffectEngine.counter_spend(ctx5, "round_mines", 1) and EffectEngine.counter(ctx5, "round_mines") == 1,
|
||||
"V4:counter_spend 静态写口消耗(用后清零语义承载面)〔专长批 V4〕")
|
||||
fx5.dispatch(ctx5, "round_start", {"round": 2})
|
||||
Kit.check_quiet(EffectEngine.counter(ctx5, "round_mines") == 0,
|
||||
"V4:round_ 前缀计数器随 round_start 整桶清空(旧 reset_at 名单声明制换代为前缀归桶)〔专长批 V4〕")
|
||||
# battle 桶对照:无前缀 helm_layers 不受 round_start 影响(跨 round_start 存活)
|
||||
var fx6 = EffectEngineScript.new(null)
|
||||
var ctx6 := EffectContext.new()
|
||||
fx6.rebuild([], [], [])
|
||||
fx6._push_trigger("test", "reset_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "helm_layers", "value": 1, "reset_at": "round_start"}}, {})
|
||||
for h in fx6.dispatch("mine_hit", {}):
|
||||
fx6._push_trigger("test", "battle_row", {"on": "mine_hit",
|
||||
"do": {"kind": "counter_add", "counter_id": "helm_layers", "value": 1}}, {})
|
||||
for h in fx6.dispatch(ctx6, "mine_hit", {}):
|
||||
if String(h.get("kind", "")) == "counter_add":
|
||||
fx6.counter_add(String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
fx6.dispatch("round_start", {"round": 2})
|
||||
Kit.check_quiet(fx6.counter("helm_layers") == 0,
|
||||
"V4:round_start 重置点清零(订阅行声明 reset_at——安全帽攒层清零语义)〔专长批 V4〕")
|
||||
EffectEngine.counter_add(ctx6, String(h.get("counter_id", "")), int(h.get("value", 0)))
|
||||
fx6.dispatch(ctx6, "round_start", {"round": 2})
|
||||
Kit.check_quiet(EffectEngine.counter(ctx6, "helm_layers") == 1,
|
||||
"V4:battle 桶(无前缀)计数器不受 round_start 清空(round 桶整桶清空仅触及 round_ 前缀键)〔专长批 V4〕")
|
||||
|
||||
quit(Kit.finish("effect_words_t1"))
|
||||
@@ -29,8 +29,8 @@ func _init() -> void:
|
||||
Kit.check_quiet(oc._combat_seg != null and oc._combat_seg.oc == oc, "V4 编排器接线·战斗段〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._decay_seg != null and oc._decay_seg.oc == oc, "V4 编排器接线·衰减段〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._verdict_seg != null and oc._verdict_seg.oc == oc, "V4 编排器接线·判定段〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._grant_seg._apply_counter_hit({"counter_id": "dig_combo", "value": 1}) == null
|
||||
and oc._fx.counter("dig_combo") == 1, "V4 counter_add 助手单点(原五位三行组收拢后行为等价)〔迭代27 T2〕")
|
||||
Kit.check_quiet(oc._grant_seg._apply_counter_hit({"counter_id": "geo_types", "value": 1}) == null
|
||||
and EffectEngine.counter(oc.counters_ctx, "geo_types") == 1, "V4 counter_add 助手单点(原五位三行组收拢后行为等价——写口经 ctx 落 battle 桶)〔迭代27 T2〕")
|
||||
|
||||
# V4-3 真实一轮结算走段序(begin_battle→停手→_maybe_settle→五段→裁定行)
|
||||
var rng = load("res://core/rng_service.gd").new()
|
||||
|
||||
@@ -31,7 +31,7 @@ func _init() -> void:
|
||||
var ap_with := int(ob1.action_economy._ap) # 测试夹具直读(呈现读数走 presentation_snapshot——本断言取账本真值)
|
||||
Kit.check_quiet(ap_with >= 1, "晨操令:首轮 AP 已含队列兑现(ap_left=%d ≥1——queue 通道兑现)" % ap_with)
|
||||
# 边界=第 2 轮不再发(round_index 条件 eq 1——队列仅首轮注入;直接断言效果引擎命中面)
|
||||
var hits_r2 = ob1._fx.dispatch("round_start", {"round_index": 2})
|
||||
var hits_r2 = ob1._fx.dispatch(ob1.counters_ctx, "round_start", {"round_index": 2})
|
||||
var r2_ap := 0
|
||||
for h in hits_r2:
|
||||
if String(h.get("kind", "")) == "grant_ap" and String(h.get("mode", "")) == "queue":
|
||||
@@ -42,7 +42,7 @@ func _init() -> void:
|
||||
var ob1b = orch_s.new(db, rng1b)
|
||||
Kit.check_quiet(ob1b.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(db.hero.max_hp)}) and ob1b.attune_param_sum("light_radius_bonus") == 0,
|
||||
"空装配基线复用:未装配件参数读数 0")
|
||||
var hits_none = ob1b._fx.dispatch("round_start", {"round_index": 1})
|
||||
var hits_none = ob1b._fx.dispatch(ob1b.counters_ctx, "round_start", {"round_index": 1})
|
||||
Kit.check_quiet(_ap_of(hits_none) == 0, "晨操令反例:未装配首轮零命中(订阅行随装配集装载)")
|
||||
|
||||
# ② 双管装填(前 2 轮各 +1 / 第 3 轮零)
|
||||
@@ -50,10 +50,10 @@ func _init() -> void:
|
||||
var ob2 = orch_s.new(db, rng2)
|
||||
Kit.check_quiet(ob2.begin_battle({"level_index": 1, "node_depth": 0, "hero_hp": int(db.hero.max_hp), "attunement": ["twin_loader"]}),
|
||||
"双管装填:装配起战")
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch("round_start", {"round_index": 1})) == 1
|
||||
and _ap_of(ob2._fx.dispatch("round_start", {"round_index": 2})) == 1,
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch(ob2.counters_ctx, "round_start", {"round_index": 1})) == 1
|
||||
and _ap_of(ob2._fx.dispatch(ob2.counters_ctx, "round_start", {"round_index": 2})) == 1,
|
||||
"双管装填:第 1/2 轮各命中 +1(lte 2 边界内)")
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch("round_start", {"round_index": 3})) == 0,
|
||||
Kit.check_quiet(_ap_of(ob2._fx.dispatch(ob2.counters_ctx, "round_start", {"round_index": 3})) == 0,
|
||||
"双管装填反例:第 3 轮零命中;战斗外零发放(round_start 事件域外无触发面)")
|
||||
|
||||
# ③ 阔视野带(参数读数=4 / 未装配=0——注入面消费在呈现桥 T4)
|
||||
|
||||
@@ -26,17 +26,17 @@ func _init() -> void:
|
||||
{"fact": "round_index", "op": "eq", "value": 1}],
|
||||
"do": {"kind": "shield", "value": 1}}],
|
||||
"sense_sharp": [{"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "damage_mult", "value": 50}},
|
||||
{"on": "mine_hit",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_sharp_used", "value": 1, "domain": "adventure"}}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_sharp_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_sharp_used", "value": 1}}],
|
||||
"sense_guard": [{"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "grant_ap", "mode": "queue", "value": 1}},
|
||||
{"on": "seal_missed",
|
||||
"when": [{"fact": "counter", "id": "tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "tree_sense_guard_used", "value": 1, "domain": "adventure"}}],
|
||||
"when": [{"fact": "counter", "id": "lvl_tree_sense_guard_used", "op": "lt", "value": 2}],
|
||||
"do": {"kind": "counter_add", "counter_id": "lvl_tree_sense_guard_used", "value": 1}}],
|
||||
}
|
||||
var six := ["posture_first_strike", "posture_bulwark", "sense_sharp", "sense_guard", "economy_hoard", "economy_spend"]
|
||||
# rebuild 实装面(db=null→feat/attune 两段空转,_triggers 只含树规则合成行)——行数对照载体
|
||||
|
||||
Reference in new issue
Block a user