diff --git a/game/core/level_runner.gd b/game/core/level_runner.gd index 9469a2f0..1e33d01d 100644 --- a/game/core/level_runner.gd +++ b/game/core/level_runner.gd @@ -503,11 +503,11 @@ func _enter_node(node: Dictionary) -> bool: "shop": # 商店房兑付:到达即开店(覆盖层归呈现层),购买出账/ # 离开归玩家意图——零自动推进;离开确认经 continue_after_node 回选路口 + var payload := {"gp": gp} # legacy 固定三件(off 对照承载——零 RNG 零池消费) if _shop.pool_mode: _shop.open_pool() # 进店抽 3(恰一 shop_draw scope;买后全重抽同签) - node_entered.emit(node, {"gp": gp, "shelf": true}) - return true - node_entered.emit(node, {"gp": gp}) # legacy 固定三件(off 对照承载——零 RNG 零池消费) + payload = {"gp": gp, "shelf": true} + node_entered.emit(node, payload) return true push_error("[关卡节点地图] 未知节点类型: %s" % str(node)) return false diff --git a/game/tests/unit/core/level_shop_economy_test.gd b/game/tests/unit/core/level_shop_economy_test.gd index a29a2bff..98e9f1b2 100644 --- a/game/tests/unit/core/level_shop_economy_test.gd +++ b/game/tests/unit/core/level_shop_economy_test.gd @@ -251,6 +251,7 @@ func _init() -> void: _smoke_shop_graph() _smoke_multi_terminal_settlement() _smoke_shop_pool() + _test_shop_node_entered_payload() _smoke_generated_coverage() quit(Kit.finish("level_shop_economy_test")) @@ -1391,6 +1392,74 @@ func _smoke_shop_pool() -> void: Kit.check("终局池余量在域 [0,配额](%s)" % str(ps), ps_ok) +# ---- 商店房 node_entered 载荷(尾波 V1——_enter_node pool/legacy 双发射路径归一后的行为保持断言)---- + +func _test_shop_node_entered_payload() -> void: + print("--- 商店房 node_entered 载荷(pool 含 shelf:true+gp 现值/legacy 无 shelf 键)---") + var autoplay = load("res://sim/level_autoplay.gd") + # pool 态:载荷字典经信号闭包当场取样(字典为引用累积器——lambda 按值捕获标量的绕行惯例) + var cap_pool := {"payload": {}, "gp": -1} + var pool_ok := false + for s in range(42, 92): + var pr = load("res://core/adventure_runner.gd").new(_db, _fresh_rng(s)) + pr.set_hero_override(900, 400) + if not pr.start_adventure(s, {"shop_mode": "pool"}): + continue + var rp = pr.runner() + rp.node_entered.connect(func(node, payload): + if String(node.get("type", "")) == "shop": + cap_pool["payload"] = payload + cap_pool["gp"] = int(rp.gp)) + if not _adv_drive_to_shop(pr, _adv_st(), autoplay): + continue + pool_ok = not (cap_pool["payload"] as Dictionary).is_empty() + if pool_ok: + break + Kit.check("pool 态进店信号可达(seed 扫描 42–91)", pool_ok) + Kit.check("pool 态 node_entered 载荷含 shelf:true〔尾波 V1〕", pool_ok and bool((cap_pool["payload"] as Dictionary).get("shelf", false))) + Kit.check("pool 态载荷 gp = 进店钱包现值(%d)〔尾波 V1〕" % int(cap_pool["gp"]), pool_ok and int((cap_pool["payload"] as Dictionary).get("gp", -1)) == int(cap_pool["gp"])) + # legacy 态:载荷无 shelf 键(两 payload 字典逐字保持的另一半——直构 LevelRunner 链) + var cap_leg := {"payload": {}, "gp": -1} + var leg_ok := false + for s in range(42, 92): + var lr = load("res://core/level_runner.gd").new(_db, _fresh_rng(s)) + lr.set_hero_override(900, 400) + lr.start_level(2, s) + lr.node_entered.connect(func(node, payload): + if String(node.get("type", "")) == "shop": + cap_leg["payload"] = payload + cap_leg["gp"] = int(lr.gp)) + var st := _battle_st("normal") + var guard := 60 + while String(lr.level_result) == "" and String(lr.current_node().get("type", "")) != "shop" and guard > 0: + guard -= 1 + if lr.awaiting_choice(): + var ids: Array = lr.next_candidate_ids() + ids.sort() + lr.pick_next_node(int(ids[0])) + continue + var t := String(lr.current_node().get("type", "")) + if t == "reward" or t == "rest": + lr.continue_after_node() + continue + var b = lr.current_battle() + if b == null: + break + var fin := {} + b.battle_finished.connect(func(p): fin["p"] = p) + var g2 := 80 + while lr.current_battle() == b and not fin.has("p") and g2 > 0: + g2 -= 1 + autoplay._autoplay_battle(lr, b, st, null) + if String(lr.level_result) == "" and String(lr.current_node().get("type", "")) == "shop": + leg_ok = not (cap_leg["payload"] as Dictionary).is_empty() + if leg_ok: + break + Kit.check("legacy 态进店信号可达(seed 扫描 42–91)", leg_ok) + Kit.check("legacy 态 node_entered 载荷无 shelf 键〔尾波 V1〕", leg_ok and not (cap_leg["payload"] as Dictionary).has("shelf")) + Kit.check("legacy 态载荷 gp = 进店钱包现值(%d)〔尾波 V1〕" % int(cap_leg["gp"]), leg_ok and int((cap_leg["payload"] as Dictionary).get("gp", -1)) == int(cap_leg["gp"])) + + # 入口→Boss 全路径枚举(DFS 迭代栈——图合法性校验保证单入口无环,路径有限)