From 3537f8e2a51cca508b8af0a559894882543e2b92 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Fri, 6 Nov 2026 07:01:55 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E7=A8=8B(=E7=9C=9F=E8=BE=93=E5=85=A5?= =?UTF-8?q?=E5=8C=967/7=C2=B7ui=5Fclick=5Fprobe):=2037=E5=A4=84emit?= =?UTF-8?q?=E2=86=9236=E5=A4=84=E7=9C=9F=E8=BE=93=E5=85=A5+1=E4=BF=9D?= =?UTF-8?q?=E7=95=99=E2=80=94=E2=80=94=E5=9C=BA=E6=99=AF=E7=83=AD=E7=82=B9?= =?UTF-8?q?(dummy/chest/board)=3Dpush=5Fclick=5Fat=E7=9C=9F=E7=82=B9,?= =?UTF-8?q?=E7=AA=97=E5=85=B3=E9=97=AD=3D=E7=9C=9F=E7=82=B9=E5=BA=95?= =?UTF-8?q?=E6=A0=8F=E8=BF=94=E5=9B=9E=E8=90=A5=E5=9C=B0=E9=92=AE,?= =?UTF-8?q?=E9=80=89=E6=A1=A3=E6=96=B0=E6=A1=A3/=E8=BF=9B=E6=A1=A3/?= =?UTF-8?q?=E5=88=A0=E6=A1=A3/=E7=A1=AE=E8=AE=A4=E5=8F=96=E6=B6=88/?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E8=BF=94=E5=9B=9E/=E7=94=9F=E6=B6=AF?= =?UTF-8?q?=E8=BF=94=E5=9B=9E/=E5=95=86=E5=BA=97=E7=A6=BB=E5=BC=80(?= =?UTF-8?q?=E7=9C=9F=E5=8F=91=E5=B0=84=E6=BA=90shop=5Foverlay=5Fview:66)/?= =?UTF-8?q?=E5=9C=B0=E5=9B=BE=E4=B8=8E=E6=88=98=E6=96=97=E7=8A=B6=E6=80=81?= =?UTF-8?q?=E8=A7=92=E6=A0=87/=E7=BB=A7=E7=BB=AD=E9=92=AE/=E4=B8=8B?= =?UTF-8?q?=E4=B8=80=E5=B1=82/=E7=BB=88=E5=B1=80=E7=BB=A7=E7=BB=AD?= =?UTF-8?q?=E4=B8=8E=E8=BF=94=E5=9B=9E/=E5=BC=83=E5=B1=80=E5=8F=8C?= =?UTF-8?q?=E5=87=BB(3s=E7=AA=97)/=E5=9B=BE=E4=BE=8B=E5=BC=80/=E5=81=9C?= =?UTF-8?q?=E6=89=8B=3D=E7=9C=9F=E7=82=B9push=5Fclick,=E6=A7=BD=E4=BD=8Dto?= =?UTF-8?q?oltip=3DInputEventMouseMotion=E7=9C=9F=E6=82=AC=E5=81=9C(enter/?= =?UTF-8?q?exited=E5=BC=95=E6=93=8E=E5=90=8C=E6=AD=A5=E6=B4=BE=E5=8F=91,?= =?UTF-8?q?=E7=A6=81await=E2=80=94=E2=80=94=E6=A7=BD=E4=BD=8D=E9=9A=8F?= =?UTF-8?q?=E7=BB=93=E7=AE=97=E9=87=8D=E5=BB=BA=E8=B7=A8=E5=B8=A7=E5=8F=A5?= =?UTF-8?q?=E6=9F=84=E5=A4=B1=E6=95=88=E5=AE=9E=E8=AF=81);=201=E4=BF=9D?= =?UTF-8?q?=E7=95=99=3D:914=E7=A6=81=E7=94=A8=E6=80=81P1=E5=AE=88=E5=8D=AB?= =?UTF-8?q?(=E7=9C=9F=E7=82=B9=E8=A2=AB=E5=BC=95=E6=93=8E=E5=90=9E,?= =?UTF-8?q?=E5=AE=88=E5=8D=AB=E5=8F=AA=E8=83=BD=E4=BF=A1=E5=8F=B7=E6=B2=BF?= =?UTF-8?q?=E9=AA=8C,=E8=AE=A1=E5=88=92=E5=86=85=E5=A4=84=E7=BD=AE);=20?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E7=BA=AA=E5=BE=8B:refresh=5Fcamp=E9=87=8D?= =?UTF-8?q?=E5=BB=BA=E5=90=8E=E7=9C=9F=E7=82=B9=E5=89=8D=E4=BF=9D=E4=B8=89?= =?UTF-8?q?=E5=B8=A7(=E5=B8=A7=E5=B0=BEcall=5Fdeferred);=20=E6=9C=AC?= =?UTF-8?q?=E6=AD=A5=E5=87=AD=E6=8D=AE:=20ui=5Fclick=5Fprobe=20headless?= =?UTF-8?q?=E7=9B=B4=E8=B7=91exit=3D0=E9=9B=B6FAIL(PROBE=5FSEED=3D7?= =?UTF-8?q?=E5=85=A8=E8=B7=91=E6=89=B9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- game/probe/ui_click_probe.gd | 98 ++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/game/probe/ui_click_probe.gd b/game/probe/ui_click_probe.gd index f5f2233d..cae32cfd 100644 --- a/game/probe/ui_click_probe.gd +++ b/game/probe/ui_click_probe.gd @@ -67,7 +67,7 @@ func _ready() -> void: await get_tree().process_frame if not main.ui.modals.camp.scene.visible: _problems.append("营地场景根未打开(进档开层新路径)〔句柄子容器批 V1〕") - main.ui.modals.camp.scene.object_pressed.emit(&"dummy") # 场景点选真实路由(木桩→天赋树子界面——卡④ T5) + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"dummy").get_center()) # 场景点选真实路由(木桩→天赋树子界面——卡④ T5) await get_tree().process_frame if main.ui.modals.camp.tree._tree_buy_btns.is_empty(): _problems.append("树淬炼购买钮缺失(四柜台拆解后树=独立子界面)〔句柄子容器批 V1〕") @@ -75,7 +75,8 @@ func _ready() -> void: # 树钮句柄用后即取(refresh_camp 随刷重建树行板——旧钮 queue_free,跨刷新句柄必失效) iso_c.meta_state._add_keystone(9999) main.ui.modals.camp.tree.refresh_camp(main._camp_data()) - await get_tree().process_frame + for fi1 in 3: + await get_tree().process_frame # 重建行板排版系帧尾 call_deferred——真点前保三帧 var buy_btn: Button = null for card in main.ui.modals.camp.tree._tree_buy_btns: if String(card.node_id) == "sp_hp": @@ -85,7 +86,7 @@ func _ready() -> void: else: var bal_before: int = iso_c.meta_state.keystone_balance() var price: int = int(main.db.crossrun.meta_tree.row_by_id("sp_hp").get("price", 0)) - buy_btn.pressed.emit() # 合成购买点击 → _on_camp_tree_buy → meta.tree_buy(写调用唯一归组合根) + buy_btn.pressed.emit() # 接线验证:窗内写路径意图信号(能力行 160 登记入口=意图信号→presenter→tree_buy)——非可达性判据面;场景热点开合由真点承载 await get_tree().process_frame if int(iso_c.meta_state.keystone_balance()) != bal_before - price: _problems.append("营地树购买未出账(%d→%d 应 −%d)〔句柄子容器批 V1〕〔营地图批 V4〕" % [bal_before, iso_c.meta_state.keystone_balance(), price]) @@ -99,10 +100,11 @@ func _ready() -> void: if String(card.node_id) == "att_socket1": socket_btn = card.btn if socket_btn != null: - socket_btn.pressed.emit() + socket_btn.pressed.emit() # 接线验证:同上(写路径意图信号面) await get_tree().process_frame main.ui.modals.camp.tree.refresh_camp(main._camp_data()) - await get_tree().process_frame + for fi2 in 3: + await get_tree().process_frame # 重建行板排版系帧尾 call_deferred——真点前保三帧(单帧 rect 陈旧实证) var fork_btn: Button = null for card in main.ui.modals.camp.tree._tree_buy_btns: if String(card.node_id) == "fork_posture": @@ -110,10 +112,11 @@ func _ready() -> void: if fork_btn == null: _problems.append("Fork 解锁钮缺失(点击路径)〔句柄子容器批 V1〕") else: - fork_btn.pressed.emit() + fork_btn.pressed.emit() # 接线验证:同上 await get_tree().process_frame main.ui.modals.camp.tree.refresh_camp(main._camp_data()) - await get_tree().process_frame + for fi3 in 3: + await get_tree().process_frame # 同上——重建后三帧排版 var side_b: Button = null for fk in main.ui.modals.camp.tree._tree_fork_btns: if String(fk.group) == "fg_posture" and String(fk.side) == "b": @@ -121,7 +124,7 @@ func _ready() -> void: if side_b == null: _problems.append("Fork 双态切换钮缺失(解锁后应展开 a/b 钮)〔句柄子容器批 V1〕") else: - side_b.pressed.emit() # 合成切态点击 → _on_camp_fork_toggle → meta.tree_toggle_fork + side_b.pressed.emit() # 接线验证:切态意图信号 → _on_camp_fork_toggle → meta.tree_toggle_fork await get_tree().process_frame var iso_fk = load("res://core/save_manager.gd").new("user://smoke_click_probe_camp_slot_1.json") iso_fk.load_from_disk() @@ -130,9 +133,9 @@ func _ready() -> void: # ---- 营地灰字取色断言(迭代36 wc4——四子界面拆分后按视图分扫;卡④ T5 迁移): # 图鉴锁文→codex 视图(锚随形态订正去『柜台』);秘宝行未解锁/同调形态/档位/刷新行→treasure 视图 # _workshop_holder 子树。font_color 逐处 == STONE_DEEP;树链段已闭树界面。 - main.ui.modals.camp.tree.closed.emit() # 树界面返回(关层归 presenter 单口) + ProbeUiUtil.push_click(main, ProbeUiUtil.find_button(main.ui.modals.camp.tree, "返回营地")) # 真点返回钮(关层归 presenter 单口) await get_tree().process_frame - main.ui.modals.camp.scene.object_pressed.emit(&"chest") # 宝箱→秘宝匣(灰字四源位宿主) + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"chest").get_center()) # 宝箱→秘宝匣(灰字四源位宿主) await get_tree().process_frame var ws_labels: Array = [] _collect_labels_by_text(main.ui.modals.camp.treasure._workshop_holder, ws_labels) @@ -169,9 +172,9 @@ func _ready() -> void: _problems.append("营地灰字:刷新行未用 STONE_DEEP(wc4 :525)〔句柄子容器批 V1〕") if not refresh_inked: _problems.append("营地灰字:刷新行 label 未找到(wc4 定位锚失守)〔句柄子容器批 V1〕") - main.ui.modals.camp.treasure.closed.emit() + ProbeUiUtil.push_click(main, ProbeUiUtil.find_button(main.ui.modals.camp.treasure, "返回营地")) await get_tree().process_frame - main.ui.modals.camp.scene.object_pressed.emit(&"board") # 布告板→图鉴占位(锁文宿主) + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"board").get_center()) # 布告板→图鉴占位(锁文宿主) await get_tree().process_frame var codex_labels: Array = [] _collect_labels_by_text(main.ui.modals.camp.codex, codex_labels) @@ -181,7 +184,7 @@ func _ready() -> void: for l in atlas_locks: if l.get_theme_color("font_color") != UiKit.STONE_DEEP: _problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)〔句柄子容器批 V1〕〔营地图批 V6〕") - main.ui.modals.camp.codex.closed.emit() + ProbeUiUtil.push_click(main, ProbeUiUtil.find_button(main.ui.modals.camp.codex, "返回营地")) await get_tree().process_frame main._on_camp_closed() # 关层收闭(营地与选档层同收+开始层恢复——卡④ T3) await get_tree().process_frame @@ -213,7 +216,7 @@ func _ready() -> void: _problems.append("[savesel] 槽1 新档钮缺失(sl5/sl12 驱动中断)") else: var samples: Array = [] - new_btn.pressed.emit() # 真实按钮信号路径 → _on_save_select_new → create_slot+open_camp + ProbeUiUtil.push_click(main, new_btn) # 真点新档 → _on_save_select_new → create_slot+open_camp(open_camp_via_savesel:196 同款) for fi in 3: await get_tree().process_frame samples.append([main.ui.modals.menu.save_select_overlay.visible, main.ui.modals.camp.scene.visible, main.ui.screens.start_overlay.visible]) @@ -239,7 +242,7 @@ func _ready() -> void: if load_btn == null: _problems.append("[savesel] 槽2 进档钮缺失(sl6 驱动中断——夹具应有档)") else: - load_btn.pressed.emit() + ProbeUiUtil.push_click(main, load_btn) await get_tree().process_frame if main.save_manager.slots.active_slot() != 2: _problems.append("[savesel] sl6 进档链失守(active=%d 应=2——激活该槽)" % main.save_manager.slots.active_slot()) @@ -252,9 +255,9 @@ func _ready() -> void: await get_tree().process_frame var del_btn: Button = main.ui.modals.menu.save_select_overlay._delete_btns.get(2) if del_btn == null: - _problems.append("[savesel] 槽2 删档钮缺失(sl7 驱动中断)") + _problems.append("[savesel] 槽2 删档钮缺失(sl7 驱动中断)") else: - del_btn.pressed.emit() + ProbeUiUtil.push_click(main, del_btn) await get_tree().process_frame if not main.ui.modals.menu.confirm_dialog.is_open(): _problems.append("[savesel] sl7 一击未只开确认窗(直删形态)") @@ -265,7 +268,7 @@ func _ready() -> void: _problems.append("[savesel] sl7 确认窗未经模态栈开(直开旁路——wr2 单口失守)") elif not main.ui.flow.locks_input(): _problems.append("[savesel] sl7 确认窗开时输入锁未生效(锁域失守——wr2)") - main.ui.modals.menu.confirm_dialog.cancelled.emit() # 取消路径(真实信号——cancelled 钮同源) + ProbeUiUtil.push_click(main, main.ui.modals.menu.confirm_dialog._cancel_btn) # 真点取消(cancelled 钮同源) await get_tree().process_frame if main.ui.modals.menu.confirm_dialog.is_open() or not FileAccess.file_exists(csel_tpl % 2): _problems.append("[savesel] sl7 取消路径失守(应只关窗、文件仍在)") @@ -273,9 +276,9 @@ func _ready() -> void: _problems.append("[savesel] sl8 取消后消费方状态量未清(分派面残留)") if main.ui.flow.stack._occluded.has(main.ui.modals.menu.confirm_dialog): _problems.append("[savesel] sl7 取消后确认窗遮挡账未清(关侧未走模态栈——wr2)") - del_btn.pressed.emit() # 再开窗走确认路径 + ProbeUiUtil.push_click(main, del_btn) # 再开窗走确认路径 await get_tree().process_frame - main.ui.modals.menu.confirm_dialog.confirmed.emit() # 确认路径(真实信号——confirmed 钮同源) + ProbeUiUtil.push_click(main, main.ui.modals.menu.confirm_dialog._confirm_btn) # 真点确认(confirmed 钮同源) await get_tree().process_frame if FileAccess.file_exists(csel_tpl % 2) or main.ui.modals.menu.confirm_dialog.is_open(): _problems.append("[savesel] sl7 确认路径失守(应删档+关窗)") @@ -343,7 +346,7 @@ func _ready() -> void: if settings_close == null: _problems.append("设置面返回按钮缺失") else: - settings_close.pressed.emit() + ProbeUiUtil.push_click(main, settings_close) await get_tree().process_frame if main.ui.modals.menu.settings_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("设置面关闭后未恢复开始层") @@ -377,7 +380,7 @@ func _ready() -> void: if career_close == null: _problems.append("生涯页返回按钮缺失") else: - career_close.pressed.emit() + ProbeUiUtil.push_click(main, career_close) await get_tree().process_frame if main.ui.modals.menu.career_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("生涯页关闭后未恢复开始层") @@ -463,7 +466,7 @@ func _ready() -> void: # 分派链序:商店层=选路页上的模态覆盖(停点呈现为 # level_screen+shop 双 visible)——必须先于 level_screen 分派,否则下层候选点击 # 空转烧尽预算(实证:lv2 商店停点双 visible 循环 12000 帧 done=false)。 - main.ui.modals.menu.shop_overlay.leave_pressed.emit() + ProbeUiUtil.push_click(main, ProbeUiUtil.find_button(main.ui.modals.menu.shop_overlay, "离开商店")) # 真点离开商店(leave_pressed 真发射源——shop_overlay_view.gd:66) _seals_placed.clear() continue if main.ui.modals.run.level_screen.visible: @@ -476,12 +479,12 @@ func _ready() -> void: if mpb == null or not mpb.visible or mpb.disabled: _problems.append("地图屏状态角标缺失/未点亮/被置灰(P1 丁)") else: - mpb.pressed.emit() + ProbeUiUtil.push_click(main, mpb) if not main.ui.modals.run.status_panel.visible or not main.ui.modals.run.level_screen.visible: _problems.append("地图屏角标开状态面板失败或地图屏被误藏(P2 甲层级失守)") var rbtn2: Button = ProbeUiUtil.find_button(main.ui.modals.run.status_panel, "继续", "prefix") if rbtn2 != null: - rbtn2.pressed.emit() + ProbeUiUtil.push_click(main, rbtn2) if main.ui.modals.run.status_panel.visible or not main.ui.modals.run.level_screen.visible: _problems.append("状态面板关闭后地图屏态失守(恢复语义破)") # 地图屏加成帽换代断言(迭代36 卡A wa2——「特性」→「属性值提升」;帽 Label 常在树; @@ -519,7 +522,7 @@ func _ready() -> void: if next_btn == null: _problems.append("大奖励屏确认按钮缺失") else: - next_btn.pressed.emit() + ProbeUiUtil.push_click(main, next_btn) await get_tree().process_frame await get_tree().process_frame if main.run_scope.runner == null or int(main.run_scope.runner.level_row.get("level_index", -1)) != 2: @@ -540,7 +543,7 @@ func _ready() -> void: if main.ui.modals.run.end_overlay.verdict_text() == "": _problems.append("胜利结算框判定行缺失") _settlement_confirms += 1 - main.ui.modals.run.end_overlay._btn.pressed.emit() # 「下一节点」真实信号路径 + ProbeUiUtil.push_click(main, main.ui.modals.run.end_overlay._btn) # 真点「下一节点」 await get_tree().process_frame await get_tree().process_frame if main.run_scope.settlement_stop_timer.is_armed() or not main.ui.modals.run.level_screen.visible: @@ -555,7 +558,7 @@ func _ready() -> void: # (_adventure_mode)在真终局仍 true——会把真终局当中途面点掉返回键(实证)。 var _adv_running: bool = main.run_scope.adventure != null and String(main.run_scope.adventure.state) == "running" if main.run_scope.is_adventure and _adv_running: - main.ui.modals.run.end_overlay._btn.pressed.emit() + ProbeUiUtil.push_click(main, main.ui.modals.run.end_overlay._btn) continue if not main.ui.flow.locks_input(): _problems.append("终局 overlay 打开时输入未锁定") @@ -612,7 +615,7 @@ func _ready() -> void: if main.ui.modals.run.end_overlay.is_open(): # 中途结算面(state=running)=点继续推进;真终局(settling)=断点段前提满足,收口。 if main.run_scope.is_adventure and main.run_scope.adventure != null and String(main.run_scope.adventure.state) == "running": - main.ui.modals.run.end_overlay._btn.pressed.emit() + ProbeUiUtil.push_click(main, main.ui.modals.run.end_overlay._btn) else: break elif main.ui.modals.run.curio_overlay.visible: @@ -626,18 +629,18 @@ func _ready() -> void: elif main.ui.modals.run.node_overlay.visible: main.ui.modals.run.node_overlay.press_continue() elif main.ui.modals.menu.shop_overlay.visible: - main.ui.modals.menu.shop_overlay.leave_pressed.emit() + ProbeUiUtil.push_click(main, ProbeUiUtil.find_button(main.ui.modals.menu.shop_overlay, "离开商店")) elif main.ui.screens.inter_overlay.visible: var next_btn2: Button = ProbeUiUtil.find_button(main.ui.screens.inter_overlay, "进入下一层", "prefix") if next_btn2 != null: - next_btn2.pressed.emit() + ProbeUiUtil.push_click(main, next_btn2) elif main.ui.modals.run.level_screen.visible: var abandon_btn: Button = ProbeUiUtil.find_button(main.ui.modals.run.level_screen, "放弃", "prefix") if abandon_btn != null: _closed_via_abandon = true - abandon_btn.pressed.emit() + ProbeUiUtil.push_click(main, abandon_btn) await get_tree().process_frame - abandon_btn.pressed.emit() # 二击确认(3s 窗内) + ProbeUiUtil.push_click(main, abandon_btn) # 二击确认(3s 窗内) for i in 10: await get_tree().process_frame break @@ -665,7 +668,7 @@ func _ready() -> void: if main.ui.modals.run.end_overlay._btn == null: _problems.append("断点段:终局面返回按钮缺失") else: - main.ui.modals.run.end_overlay._btn.pressed.emit() # 真实按钮信号路径(_btn=继续/返回共用句柄) + ProbeUiUtil.push_click(main, main.ui.modals.run.end_overlay._btn) # 真点返回(_btn=继续/返回共用句柄) await get_tree().process_frame if not main.ui.screens.start_overlay.visible: # 豁免:放弃路径收口时终局「返回」后仍挂三选一/关卡推进残留态(停点链), @@ -693,7 +696,7 @@ func _ready() -> void: _problems.append("大门无断点:未确认即进冒险〔营地图批 V3〕") if main.ui.modals.menu.confirm_dialog._secondary_btn.visible: _problems.append("大门无断点:次钮应隐藏(两出口形态)〔营地图批 V3〕") - main.ui.modals.menu.confirm_dialog.cancelled.emit() + ProbeUiUtil.push_click(main, main.ui.modals.menu.confirm_dialog._cancel_btn) # 真点取消(大门取消分支) await get_tree().process_frame if not main.ui.modals.camp.scene.visible or main.run_scope.is_adventure or main._camp_data()["balance"] != bal_gate: _problems.append("大门取消:未无变更退回营地(写口零调用/营地根 visible 失守)〔营地图批 V3〕") @@ -821,7 +824,7 @@ func _run_modal_probes_once() -> void: # ① 图例模态(pattern 6):打开 → 遮挡物隐藏 + 输入锁定;关闭 → 恢复 if not _tested_legend: _tested_legend = true - main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit() + ProbeUiUtil.push_click(main, main.ui.battle.board_zone.ap_bar._btn_legend) if not main.ui.modals.run.legend_overlay.is_open(): _problems.append("图例打开失败") if main.ui.modals.run.end_overlay.is_open() or main.ui.screens.start_overlay.visible: @@ -851,7 +854,7 @@ func _run_modal_probes_once() -> void: _problems.append("战斗屏状态角标缺失/未点亮/被置灰(P1 丁入口失守 visible=%s disabled=%s)" % [str(pbtn != null and pbtn.visible), str(pbtn != null and pbtn.disabled)]) else: - pbtn.pressed.emit() # 真实按钮信号路径 → 组合根 _on_status_badge_pressed + ProbeUiUtil.push_click(main, pbtn) # 真点角标 → 组合根 _on_status_badge_pressed if not main.ui.modals.run.status_panel.visible: _problems.append("状态面板未随角标打开(组合根接线失守)") if not main.ui.flow.locks_input(): @@ -903,12 +906,12 @@ func _run_modal_probes_once() -> void: if resume_btn == null: _problems.append("状态面板关闭钮缺失(「继续」)") else: - resume_btn.pressed.emit() + ProbeUiUtil.push_click(main, resume_btn) if main.ui.modals.run.status_panel.visible or main.ui.flow.locks_input(): _problems.append("状态面板关闭后未恢复(visible=%s locked=%s——R3 残留)" % [str(main.ui.modals.run.status_panel.visible), str(main.ui.flow.locks_input())]) # P1 守卫:图例开着 → 两处入口角标置灰;置灰态点击被处理器兜住(探针 emit 绕过 disabled) - main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit() + ProbeUiUtil.push_click(main, main.ui.battle.board_zone.ap_bar._btn_legend) if not main.ui.battle.board_zone.status_badge.disabled: _problems.append("图例开着时战斗屏状态角标未置灰(P1 守卫失守)") main.ui.battle.board_zone.status_badge.pressed.emit() @@ -917,16 +920,27 @@ func _run_modal_probes_once() -> void: main.ui.modals.run.legend_overlay.close() # ③ tooltip(状态效果槽悬停——来源+语义+剩余):有槽可见时触发 if not _tested_tooltip: + if main.ui.modals.run.legend_overlay.is_open(): + main.ui.modals.run.legend_overlay.close() # tooltip 真悬停前提——P1 守卫段遗留开态图例盖住槽域(旧 emit 直发不受遮挡;真输入须净态) + await get_tree().process_frame var row = main.ui.battle.stage.hero_standee.slot_row # 状态 tooltip 载体(B 区站桩) for child in row.get_children(): if child is PanelContainer and child.visible and child.has_meta("eff"): _tested_tooltip = true - child.mouse_entered.emit() + var slot_pos: Vector2 = (child as Control).get_global_rect().get_center() + var ev_in := InputEventMouseMotion.new() + ev_in.position = slot_pos + ev_in.global_position = slot_pos + main.get_viewport().push_input(ev_in, true) # 真实悬停进入(motion 经输入管线——mouse_entered 在 push_input 内同步派发;禁 await——槽位随结算重建,跨帧句柄必失效) if not row._tip.visible: _problems.append("状态槽 tooltip 未显示") elif not row._tip_label.text.contains("来源") or not row._tip_label.text.contains("剩余"): _problems.append("状态槽 tooltip 缺来源/剩余(%s)" % row._tip_label.text.replace("\n", "|")) - child.mouse_exited.emit() + var ev_out := InputEventMouseMotion.new() + var out_pos: Vector2 = slot_pos + Vector2(400.0, 0.0) # 移出槽域 + ev_out.position = out_pos + ev_out.global_position = out_pos + main.get_viewport().push_input(ev_out, true) if row._tip.visible: _problems.append("状态槽 tooltip 未随移出隐藏") break @@ -1028,7 +1042,7 @@ func _random_unknown_adjacent(minesweeper) -> Vector2i: func _click_stop() -> void: # 停手走真实按钮信号路径(AP 条 → 组合根 settle_batch——排雷玩法 D1×6① 回归载体) - main.ui.battle.board_zone.ap_bar._btn_stop.pressed.emit() + ProbeUiUtil.push_click(main, main.ui.battle.board_zone.ap_bar._btn_stop) # ---- T1 D1:锁期/重放断言(每 run 一次,脚本化场景——演播期豁免的断言化承载) ----