diff --git a/game/data/tables/feats.tres b/game/data/tables/feats.tres index eb4cebaf..6667b737 100644 --- a/game/data/tables/feats.tres +++ b/game/data/tables/feats.tres @@ -902,5 +902,38 @@ rows = [ } } ] + }, + { + "feat_id": "wall_blast", + "display_name": "崩壁冲击", + "rarity": "excellent", + "cap": 1, + "desc": "刷后群伤:成功刷新后全体存活敌各受 2 点", + "triggers": [ + { + "on": "board_refreshed", + "do": { + "kind": "open_ticket", + "channel": "blast", + "value": 2 + } + } + ] + }, + { + "feat_id": "mine_profit", + "display_name": "矿道红利", + "rarity": "rare", + "cap": 1, + "desc": "刷后GP:每次成功刷新固定 +1 GP", + "triggers": [ + { + "on": "board_refreshed", + "do": { + "kind": "grant_gp", + "value": 1 + } + } + ] } ] diff --git a/game/tests/unit/battle_settlement/refresh_feats_test.gd b/game/tests/unit/battle_settlement/refresh_feats_test.gd index aa313d22..6aa55a0e 100644 --- a/game/tests/unit/battle_settlement/refresh_feats_test.gd +++ b/game/tests/unit/battle_settlement/refresh_feats_test.gd @@ -32,6 +32,8 @@ func _init() -> void: test_refresh_discount_gate_and_charge() test_double_base_stack() test_no_feats_baseline() + test_board_refresh_fact_only_on_success() + test_mine_profit_gp() quit(Kit.finish("refresh_feats_test")) @@ -346,3 +348,74 @@ func test_no_feats_baseline() -> void: orch.action_economy.ap == ap_before - int(_db.run.action_economy.dig_ap_cost) and orch.action_economy.phase == "Active") Kit.check("V12 每轮上限照旧:第 3 次拒(RAP 足额仍拒)〔换图系批 V12〕", orch.player_request_refresh() and not orch.player_request_refresh()) + + +# ---- V4/V10 成功刷新事实与命中施加(迭代 46 卡A T4)---- + +# 成功刷新事实探针专长安装(board_refreshed → counter_add——成功事实的可观测计数面) +func _install_br_probe() -> void: + if _db.crossrun.feats.row_by_id("probe_br_counter").is_empty(): + (_db.crossrun.feats.rows as Array).append({ + "feat_id": "probe_br_counter", "display_name": "刷新事实探针", "rarity": "common", + "desc": "测试夹具:每次成功刷新计数 +1", + "triggers": [{"on": "board_refreshed", "do": {"kind": "counter_add", "counter_id": "br_probe_hits", "value": 1}}], + }) + + +func _br_count(orch) -> int: + return EffectEngineScript.counter(orch.fx_host().counters_ctx, "br_probe_hits") + + +# V4 成功事实只在新板就位后发射:成功恰一次;非 Active/每轮上限/RAP 不足三拒面全零事实零换图 +func test_board_refresh_fact_only_on_success() -> void: + print("--- V4 成功刷新事实:拒绝零事实 ---") + _install_br_probe() + var orch = _orch(46031) + var swaps := {"n": 0} + var waiting_rejected := {"v": false} + orch.board_changed.connect(func(): swaps["n"] += 1) + # 非 Active 探针(round_flow 同款:借衰减行在结算段内同步探测一次——Waiting 相位刷新必拒) + orch.step_row.connect(func(row): + if String(row.get("type", "")) == "decay" and not bool(waiting_rejected["v"]): + waiting_rejected["v"] = not orch.player_request_refresh()) + Kit.check("V4 前置:战斗受理", orch.begin_battle(_enc(["probe_br_counter"]))) + _bank_rap(orch, 3) # RAP=3×AP;首轮结算衰减行触发 Waiting 探针 + var swaps0: int = swaps["n"] + Kit.check("V4 非 Active 拒零事实(结算段内探测拒绝)〔换图系批 V4〕", + bool(waiting_rejected["v"]) and _br_count(orch) == 0 and int(swaps["n"]) == swaps0) + # 成功 ×2:事实计数与换图信号逐次 +1 + Kit.check("V4 第 1 次成功(事实 +1 换图 +1)〔换图系批 V4〕", + orch.player_request_refresh() and _br_count(orch) == 1 and int(swaps["n"]) == swaps0 + 1) + Kit.check("V4 第 2 次成功(事实 +1 换图 +1)〔换图系批 V4〕", + orch.player_request_refresh() and _br_count(orch) == 2 and int(swaps["n"]) == swaps0 + 2) + # 每轮上限拒:used=2 满门、RAP 足额——零事实零换图 + Kit.check("V4 上限拒零事实(RAP %d 足额仍拒)〔换图系批 V4〕" % orch.action_economy.rap, + not orch.can_refresh() and not orch.player_request_refresh() and _br_count(orch) == 2 and int(swaps["n"]) == swaps0 + 2) + # RAP 不足拒:停手进新轮(used 重置、停手再入 RAP)→ 消费至 RAP 0 → 拒(零事实零换图; + # 纯 RAP 不足拒绝面已由 V3 门槛拒单测钉住,此处并拦形态) + orch.minesweeper.settle_batch([{"action": "stop"}]) + Kit.check("V4 新轮受理两次(used 重置、RAP 复入)〔换图系批 V4〕", + orch.action_economy.phase == "Active" and orch.player_request_refresh() and orch.player_request_refresh() and _br_count(orch) == 4) + Kit.check("V4 RAP 不足拒零事实(RAP %d < 价 %d)〔换图系批 V4〕" % [orch.action_economy.rap, int(_db.run.action_economy.rap_refresh_cost)], + orch.action_economy.rap < int(_db.run.action_economy.rap_refresh_cost) and not orch.can_refresh() and not orch.player_request_refresh() and _br_count(orch) == 4 and int(swaps["n"]) == swaps0 + 4) + + +# V10 矿道红利:每次成功刷新 escrow 恰 +1 且 fx_grant_gp 行在案;拒绝零变化 +func test_mine_profit_gp() -> void: + print("--- V10 矿道红利:刷新固定 GP ---") + var orch = _orch(46032) + var gp_rows := {"n": 0} + orch.step_row.connect(func(row): + if String(row.get("type", "")) == "effect" and String(row.get("effect_id", "")) == "fx_grant_gp": + gp_rows["n"] += 1) + Kit.check("V10 前置:战斗受理", orch.begin_battle(_enc(["mine_profit"]))) + _bank_rap(orch, 2) + var escrow0: int = orch.reward_grant().escrow + Kit.check("V10 第 1 次成功 escrow +1(%d→%d)〔换图系批 V10〕" % [escrow0, orch.reward_grant().escrow], + orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 1) + Kit.check("V10 fx_grant_gp 行在案(%d 行)〔换图系批 V10〕" % int(gp_rows["n"]), int(gp_rows["n"]) == 1) + Kit.check("V10 第 2 次成功 escrow +1(%d→%d)〔换图系批 V10〕" % [orch.reward_grant().escrow, orch.reward_grant().escrow + 1], + orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 2) + # 拒绝零变化:RAP 不足 + Kit.check("V10 拒绝零变化(escrow=%d)〔换图系批 V10〕" % orch.reward_grant().escrow, + not orch.player_request_refresh() and orch.reward_grant().escrow == escrow0 + 2 and int(gp_rows["n"]) == 2)