From c47a032dcbf8a2b33bbb3235ace07994b3a7696c Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Thu, 29 Oct 2026 07:21:04 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=AD=E4=BB=A342-=E5=8D=A11-T2:=20level=5Fr?= =?UTF-8?q?unner=20=5Fenter=5Fnode=20shop=E5=88=86=E6=94=AFpool/legacy?= =?UTF-8?q?=E5=8F=8Creturn=E5=90=88=E5=B9=B6=E4=B8=BApayload=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E5=8D=95return=E2=80=94=E2=80=94=E9=94=AE9=E5=BD=92?= =?UTF-8?q?=E7=BB=BF(max-returns|=5Fenter=5Fnode=207=E2=86=926=E9=94=AE?= =?UTF-8?q?=E6=B6=88=E5=A4=B1),=E4=B8=A4payload=E5=AD=97=E5=85=B8=E9=80=90?= =?UTF-8?q?=E5=AD=97=E4=BF=9D=E6=8C=81(pool=20{gp,shelf:true}/legacy=20{gp?= =?UTF-8?q?});=E6=96=B0=E5=A2=9E=E6=96=AD=E8=A8=80level=5Fshop=5Feconomy?= =?UTF-8?q?=5Ftest:=5Ftest=5Fshop=5Fnode=5Fentered=5Fpayload=E3=80=94?= =?UTF-8?q?=E5=B0=BE=E6=B3=A2=20V1=E3=80=95=E5=9B=9B=E6=9D=A1(pool?= =?UTF-8?q?=E6=80=81=E8=BD=BD=E8=8D=B7=E5=90=ABshelf:true=E4=B8=94gp=3D?= =?UTF-8?q?=E8=BF=9B=E5=BA=97=E9=92=B1=E5=8C=85=E7=8E=B0=E5=80=BC27/legacy?= =?UTF-8?q?=E6=80=81=E8=BD=BD=E8=8D=B7=E6=97=A0shelf=E9=94=AE=E4=B8=94gp?= =?UTF-8?q?=3D=E7=8E=B0=E5=80=BC30,=E5=AD=97=E5=85=B8=E5=BC=95=E7=94=A8?= =?UTF-8?q?=E7=B4=AF=E7=A7=AF=E5=99=A8=E5=BD=A2=E6=80=81=E7=BB=95lambda?= =?UTF-8?q?=E6=8C=89=E5=80=BC=E6=8D=95=E8=8E=B7),=E6=B3=A8=E5=86=8C?= =?UTF-8?q?=E8=BF=9B=5Finit=E6=89=A7=E8=A1=8C=E5=BA=8F=5Fsmoke=5Fshop=5Fpo?= =?UTF-8?q?ol=E4=B9=8B=E5=90=8E;=E5=AD=A4=E7=AB=8B=E9=AA=8C=E8=AF=81--only?= =?UTF-8?q?=3Dlevel=5Fshop=5Feconomy=E5=85=A8=E7=BB=BF192=20checks=20exit?= =?UTF-8?q?=3D0(=E6=97=A5=E5=BF=97=E5=9B=9E=E6=BA=AF=E8=A1=8C=E5=B1=9Epush?= =?UTF-8?q?=5Fwarning=E6=A3=8B=E7=9B=98=E7=94=9F=E6=88=90=E9=87=8D?= =?UTF-8?q?=E8=AF=95=E5=91=8A=E8=AD=A6=E9=9D=9E=E9=94=99=E8=AF=AF)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/core/level_runner.gd | 6 +- .../unit/core/level_shop_economy_test.gd | 69 +++++++++++++++++++ 2 files changed, 72 insertions(+), 3 deletions(-) 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 迭代栈——图合法性校验保证单入口无环,路径有限)