From c32bb6ca0882f3f9682192f55850d61ad2cc09c7 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Tue, 6 Oct 2026 05:09:50 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20=E7=BB=AD=E7=8E=A9=E5=81=9C=E7=82=B9?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=20main.gd:717=20=E5=B4=A9=E5=80=BC=EF=BC=88?= =?UTF-8?q?=E7=94=A8=E6=88=B7=E5=AE=9E=E6=9C=BA=20F5=20=E5=AE=9A=E4=BD=8D?= =?UTF-8?q?=EF=BC=89=E2=80=94=E2=80=94resume=5Fadventure=20=E4=B8=89?= =?UTF-8?q?=E9=80=89=E4=B8=80=E5=81=9C=E7=82=B9=E5=88=86=E6=94=AF=E5=9C=A8?= =?UTF-8?q?=20level=5Fstarted=20=E4=B9=8B=E5=89=8D=E5=90=8C=E6=AD=A5=20cur?= =?UTF-8?q?io=5Fchoice=5Fpresented.emit=E3=80=94resume=20=E6=8F=90?= =?UTF-8?q?=E5=89=8D=20return=E3=80=95=EF=BC=8CGP=20=E6=80=BB=E7=BB=93=20l?= =?UTF-8?q?ambda=20=E6=B1=82=E5=80=BC=E6=97=B6=20runner=20=E5=B0=9A?= =?UTF-8?q?=E6=9C=AA=E8=B5=8B=E5=80=BC=EF=BC=88null.gp=20=E7=82=B8?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E4=BF=AE=3D=5Fgp=5Fsummary=5Ftext=20runner?= =?UTF-8?q?=20=E7=BC=BA=E4=BD=8D=E5=AE=88=E5=8D=AB+=E6=96=AD=E7=82=B9?= =?UTF-8?q?=E5=BF=AB=E7=85=A7=20gp=20=E5=85=9C=E5=BA=95=EF=BC=88=E5=90=88?= =?UTF-8?q?=E8=AE=A1=E8=AF=AD=E4=B9=89=3D=E6=81=A2=E5=A4=8D=E9=9D=A2?= =?UTF-8?q?=E5=90=88=E8=AE=A1=E5=8D=B3=E5=AD=98=E6=A1=A3=E5=90=88=E8=AE=A1?= =?UTF-8?q?=EF=BC=8C=E4=B8=8E=20=5Fbonus=5Fsummary=5Ftext=20=E5=90=8C?= =?UTF-8?q?=E6=AC=BE=E5=AE=88=E5=8D=AB=E5=BC=8F=EF=BC=89=EF=BC=9B=E5=9B=9E?= =?UTF-8?q?=E5=BD=92=3Dui=5Fstruct=5Fprobe=E2=91=A6resume=20=E4=B8=93?= =?UTF-8?q?=E9=A1=B9=EF=BC=88=E5=85=88=E7=BA=A2=E5=90=8E=E7=BB=BF=E5=AE=9E?= =?UTF-8?q?=E8=AF=81=EF=BC=9ANil.gp=20=E5=90=8C=E6=A0=88=E5=A4=8D=E7=8E=B0?= =?UTF-8?q?=E2=86=92=E4=BF=AE=E5=90=8E=E6=80=BB=E7=BB=93=E9=9D=A2=E5=BC=80?= =?UTF-8?q?+=E5=BF=AB=E7=85=A7=E5=90=88=E8=AE=A1=2024=20=E6=96=AD=E8=A8=80?= =?UTF-8?q?=EF=BC=89=EF=BC=9B=E8=BD=BB=E9=97=B8=E5=86=92=E7=83=9F782/0+run?= =?UTF-8?q?=5Fall=2016=E5=A5=97+probe=5Fgate=20--click=20=E5=85=A8?= =?UTF-8?q?=E7=BB=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/presentation/main.gd | 7 ++++++ game/smoke/ui_struct_probe.gd | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 96605e33..b48e27a2 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -713,7 +713,14 @@ func _on_battle_finished(payload: Dictionary) -> void: # 战利品行装配(battle.md OQ14——胜利总结结算展示 GP 总量+增量;账本语义零改动,纯呈现读数): # 增量 = battle_finished 载荷 gp_dropped(战斗域上抛);总量 = runner.gp 现值(LevelRunner 入账 # 连接先于组合根 handler——此读数即战后入账完毕的总量)。 +# runner 缺位守卫(2026-10-06 用户实机 main.gd:717 崩值定位——续玩停点恢复分支:adventure_runner +# resume_adventure 的 bonus_pending 分支在 level_started 之前同步 curio_choice_presented.emit +# 〔resume 提前 return〕,本 lambda 求值时 runner 尚未赋值;合计改读断点快照 gp 兜底——恢复源快照 +# 此刻必在档,语义=续玩恢复面的合计即存档合计;与 _bonus_summary_text 同款守卫式) func _gp_summary_text(payload: Dictionary) -> String: + if runner == null: + var bp: Dictionary = save_manager.breakpoint_snapshot() if save_manager != null else {} + return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(bp.get("gp", 0))] return "战利品 GP +%d · 合计 %d" % [int(payload.get("gp_dropped", 0)), int(runner.gp)] diff --git a/game/smoke/ui_struct_probe.gd b/game/smoke/ui_struct_probe.gd index a64f806a..743775e2 100644 --- a/game/smoke/ui_struct_probe.gd +++ b/game/smoke/ui_struct_probe.gd @@ -99,6 +99,28 @@ func _ready() -> void: _settle() await _audit("shop", [main.shop_overlay]) + # ---- ⑦resume 专项:续玩停点恢复(2026-10-06 用户实机 main.gd:717 报错回归闸)---- + # 三选一停点续玩分支在 level_started 之前同步 curio_choice_presented.emit(adventure_runner + # resume 提前 return)——GP 总结 lambda 求值时 runner 尚未赋值,修复前 717 炸 null。 + ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_resume", true) + main.save_manager.set_breakpoint(_resume_bp_fixture()) + main._refresh_resume_button() + main.runner = null # 全新启动等价态(真机=菜单直进续玩,无前置冒险——runner 从未赋值;探针此前推流会残留旧 runner) + main._resume_from_menu() + _settle(8) + if not main._end_overlay.visible: + problems.append("[resume] 续玩恢复后胜利总结面未开(三选一停点分支中断)") + else: + var found := {"ok": false} + _find_label_text(main._end_overlay, "合计 24", found) + if not found.ok: + problems.append("[resume] 胜利总结面 GP 行未见快照合计「合计 24」(runner 缺位守卫缺位——717 回归)") + var s: String = main._gp_summary_text({"gp_dropped": 0}) + if not s.contains("合计 24"): + problems.append("[resume] _gp_summary_text runner=null 守卫未取快照 gp(%s)" % s) + # 恢复现场(resume 改写了 main 的 adventure/save 态——后续无屏依赖,仅为整洁) + main._level_active = false + # ---- 判定 ---- if problems.is_empty(): print("[ui_struct_probe] PASS — 全屏结构断言零红") @@ -109,6 +131,25 @@ func _ready() -> void: get_tree().quit(1) +# ⑦resume 夹具:最小合法断点快照——bonus_pending 非空=三选一停点分支(合计 gp=24 供守卫兜底断言) +func _resume_bp_fixture() -> Dictionary: + return { + "master_seed": 42, "rng_counter": 0, "hero_hp": 20, "gp": 24, "keystone": 0, + "level_index": 1, "bonus": {}, "shop_purchased": {}, "relics": [], + "bonus_aux_carry": [], "keystone_drops": [], "bonus_taken": {}, + "bonus_pending": {"candidates": [{"kind": "heal", "tier": "common"}], "payload": {"gp_dropped": 0}}, + } + + +# 递归找含目标子串的 Label(⑦resume 断言读数面) +func _find_label_text(node: Node, needle: String, found: Dictionary) -> void: + for ch in node.get_children(): + if ch is Label and String(ch.text).contains(needle): + found.ok = true + return + _find_label_text(ch, needle, found) + + func _pick_battle_node(main: Node) -> int: # 入口/兑付停点走 level_screen.open_choices 直传(不写 _pending_candidates)—— # 候选从视图态取:_cand_ids × runner.nodes() 的 type(编号解析=node_id_of 单一出口)