diff --git a/game/probe/screen_flow_probe.gd b/game/probe/screen_flow_probe.gd index d02f7097..17a26a6a 100644 --- a/game/probe/screen_flow_probe.gd +++ b/game/probe/screen_flow_probe.gd @@ -69,12 +69,14 @@ func _seals_text(f: Dictionary) -> String: # ---- 流程状态位(与可见性是两回事,逐步单独判)---- func _expect_adventure_seal(step: String, expect: bool) -> void: + # oracle: design/engineering/architecture/architecture.md:168 三屏壳与呈现装配——屏级互斥与流程状态位判据宿主面(状态位判据组) var got: bool = main.run_scope != null and main.run_scope.is_adventure if got != expect: _problems.append("%s:流程状态位 is_adventure 应为 %s,实测 %s" % [step, str(expect), str(got)]) func _expect_exclusive(step: String, expect: String) -> void: + # oracle: design/engineering/architecture/architecture.md:168 屏壳切换与呈现装配(屏级互斥组) var f: Dictionary = _screen_seals() var pairs := [["菜单", "营地"], ["菜单", "冒险"], ["营地", "冒险"], ["终局", "营地"], ["终局", "菜单"]] for pair in pairs: @@ -101,6 +103,7 @@ func _frames(n: int = 2) -> void: # ---- ① boot:菜单独占 ---- func _step_boot_menu() -> void: + # oracle: design/engineering/test-conventions.md:49 调试面发布态恒隐、无 UI 开启口(boot 组) _expect_exclusive("①boot", "菜单") _expect_adventure_seal("①boot", false) if main.ui.modals.menu.debug_overlay != null and main.ui.modals.menu.debug_overlay.visible: @@ -109,6 +112,7 @@ func _step_boot_menu() -> void: # ---- ② 菜单 → 营地(真实按钮信号路径)---- func _step_menu_to_camp() -> void: + # oracle: design/gdd/ux/camp.md:10 营地=存档内局外枢纽,存档语境可达(菜单→营地组) # 营地经进档开层(卡② mn9 新路径——campbtn 已撤;槽模板注入隔离命名空间) if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_camp"): _problems.append("②菜单→营地:选档层新档钮缺失(新路径驱动中断)") @@ -119,14 +123,15 @@ func _step_menu_to_camp() -> void: # ---- ③ 营地 → 菜单(回退路径:篝火二次确认动线——卡④ 营地唯一回菜单出口;选档层随关层收闭同收)---- func _step_camp_back_to_menu() -> void: + # oracle: design/gdd/ux/camp.md:22 篝火=离开营地回主菜单、必须二次确认(营地→菜单组) if main.ui.modals.camp.scene == null: _problems.append("③营地→菜单:营地场景根句柄缺失〔句柄子容器批 V1〕") return - main.ui.modals.camp.scene.object_pressed.emit(&"campfire") + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"campfire").get_center()) await _frames() if not main.ui.modals.menu.confirm_dialog.visible: _problems.append("③篝火:二次确认弹窗未现(camp.md v2.2 必须先确认)〔营地图批 V7〕") - main.ui.modals.menu.confirm_dialog.confirmed.emit() # 二次确认离场(confirmed 先于关层——卡④ T4) + ProbeUiUtil.push_click(main, main.ui.modals.menu.confirm_dialog._confirm_btn) # 二次确认离场(confirmed 先于关层——卡④ T4) await _frames() _expect_exclusive("③营地→菜单(回退)", "菜单") @@ -135,6 +140,7 @@ func _step_camp_back_to_menu() -> void: # ---- 选档界面开合(卡③ sl10):函数直调开层(「进入存档」钮归卡② mn3)→屏级互斥采样→ # 返回钮真实信号路径关闭→ESC 语境不响应(卡⑤ 契约 2 范围外——零实现即断言)。---- func _step_save_select_open_back() -> void: + # oracle: design/gdd/systems/表现与支撑/存档序列化.md:61 激活态由选档界面的「新档」或「进档」建立(选档开合组) ProbeUiUtil.isolate_save(main, "smoke_screen_flow_savesel", false, false, true) main.save_manager.slots.attach_slot_template("user://smoke_screen_flow_savesel_slot_%d.json") var esb: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "进入存档", "prefix") @@ -150,7 +156,7 @@ func _step_save_select_open_back() -> void: if back_btn == null: _problems.append("[savesel] sl10 返回钮缺失(页壳顶条)") else: - back_btn.pressed.emit() + ProbeUiUtil.push_click(main, back_btn) await get_tree().process_frame if main.ui.modals.menu.save_select_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("[savesel] sl10 返回后选档未隐藏或开始层未恢复(关闭链失守)") @@ -181,6 +187,7 @@ func seals_check_single(f: Dictionary) -> bool: func _step_menu_to_adventure() -> void: + # oracle: design/gdd/ux/level-map.md:11 入口停点候选数=入口个数(菜单→冒险组) # 开始冒险钮已随三入口化撤出——函数直调中间态(卡② mn10;卡④ 大门两分支换代) main._start_adventure_from_menu() await _frames(3) @@ -194,6 +201,7 @@ func _step_menu_to_adventure() -> void: # ---- ⑤ 冒险内屏切换:关卡地图屏 ⇄ 战斗屏(屏内两态,屏级仍独占)---- func _step_level_to_battle_and_back() -> void: + # oracle: design/gdd/ux/battle.md:57 Entry & Exit Points——进出战斗屏(冒险内切换组) if not main.ui.modals.run.level_screen.visible: _problems.append("⑤冒险内切换:前置不在关卡地图屏") return @@ -213,6 +221,7 @@ func _step_level_to_battle_and_back() -> void: # ---- ⑥ 放弃 → 终局(放弃路径=回退路径的非正常出口)---- func _step_abandon_to_end() -> void: + # oracle: design/gdd/ux/level-map.md:42 放弃冒险(任意选路停点)→ 冒险落幕(放弃→终局组) if main.run_scope == null or main.run_scope.adventure == null: _problems.append("⑥放弃→终局:前置无冒险实例") return @@ -225,10 +234,11 @@ func _step_abandon_to_end() -> void: # ---- ⑦ 终局 → 菜单(终局出口返回开始界面)---- func _step_end_back_to_menu() -> void: + # oracle: design/gdd/ux/settlement-summary.md:12 冒险总结结算=冒险落幕时终局覆盖层(终局→菜单组) if main.ui.modals.run.end_overlay == null or not main.ui.modals.run.end_overlay.visible: _problems.append("⑦终局→菜单:前置不在终局") return - main.ui.modals.run.end_overlay.continue_pressed.emit() + ProbeUiUtil.push_click(main, main.ui.modals.run.end_overlay._btn) # 真点继续钮(_btn 唯一钮——ui_click_probe:543 同源) await _frames(3) _expect_exclusive("⑦终局→菜单", "菜单") if main.run_scope != null and main.run_scope.is_adventure: @@ -237,6 +247,7 @@ func _step_end_back_to_menu() -> void: # ---- ③' 营地 Esc 浮窗+HUD(卡⑤——真实键盘合成路径 push_input;四态路由+三出口+语境分支+余额单口)---- func _step_camp_esc_and_hud() -> void: + # oracle: design/gdd/ux/camp.md:45 ESC 优先序:有功能窗口先关最上层窗,无窗才进 ESC 浮窗(Esc 段组) ProbeUiUtil.isolate_save(main, "smoke_screen_flow_esc", false, false, true) if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_esc"): _problems.append("[esc] 营地入口缺失(选档层新档钮驱动中断)〔ESC批 V1〕") @@ -254,7 +265,7 @@ func _step_camp_esc_and_hud() -> void: await _frames() if main.ui.modals.camp.esc_overlay.visible: _problems.append("[esc] echo 事件唤起浮窗(触发沿 pressed 且非 echo 失守)〔ESC批 V1〕") - main.ui.modals.camp.scene.object_pressed.emit(&"dummy") # 子界面开态 + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"dummy").get_center()) # 子界面开态 await _frames() _push_esc() await _frames() @@ -285,7 +296,7 @@ func _step_camp_esc_and_hud() -> void: if not main.ui.modals.camp.esc_overlay.visible or main.ui.screens.start_overlay.visible: _problems.append("[esc] 关设置后未回浮窗或回弹主菜单(契约 3 语境分支失守)〔ESC批 V4〕〔ESC批 B3〕") # 返回主菜单动线(契约 4——浮窗自收+营地隐+选档收+菜单显) - main.ui.modals.camp.esc_overlay.return_requested.emit() + ProbeUiUtil.push_click(main, main.ui.modals.camp.esc_overlay._return_btn) # 真点返回主菜单钮(camp_esc_overlay 底栏钮) await _frames() if main.ui.modals.camp.esc_overlay.visible or main.ui.modals.camp.scene.visible or main.ui.modals.menu.save_select_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("[esc] 返回主菜单动线失守(浮窗自收/营地隐/选档收/菜单显)〔ESC批 V3〕〔ESC批 B3〕") @@ -300,6 +311,7 @@ func _step_camp_esc_and_hud() -> void: # 判的是呈现形态换壳(近全屏面板→居中定尺寸窗壳 720×640):窗几何/场景根留底/遮罩 0.6/关闭钮/ # 互斥单开/ESC 优先序/窗内滚动/生涯同壳——行为面(开合路由)零改,只钉窗壳可观测提交面。 func _step_camp_window_shell() -> void: + # oracle: design/gdd/ux/camp.md:45 五功能窗统一窗壳、五窗互斥单开、各窗自带关闭钮(窗壳 V1–V8 组) ProbeUiUtil.isolate_save(main, "smoke_screen_flow_winshell", false, false, true) if not await ProbeUiUtil.open_camp_via_savesel(main, "smoke_screen_flow_winshell"): _problems.append("[winshell] 营地入口缺失(选档层新档钮驱动中断)〔窗口化批 V1〕") @@ -323,11 +335,11 @@ func _step_camp_window_shell() -> void: if prev_back == null: _problems.append("[winshell] %s 底栏「返回营地」钮缺失〔窗口化批 V4〕" % String(opened.back())) else: - prev_back.pressed.emit() + ProbeUiUtil.push_click(main, prev_back) await _frames() if prev.visible or not main.ui.modals.camp.scene.visible: _problems.append("[winshell] %s 关闭钮按下未隐窗或场景根失守〔窗口化批 V4〕" % String(opened.back())) - main.ui.modals.camp.scene.object_pressed.emit(k) + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(k).get_center()) await _frames() opened.append(k) var view: Control = views[k] @@ -362,7 +374,7 @@ func _step_camp_window_shell() -> void: if vis_n != 1: _problems.append("[winshell] %s 开态五窗 visible 计数=%d 应=1(互斥单开失守)〔窗口化批 V5〕" % [String(k), vis_n]) # 树窗点选遮挡(V5b——树窗开态向锻炉命中区推真实鼠标点:被遮罩挡=零新窗零响应) - main.ui.modals.camp.scene.object_pressed.emit(&"dummy") + main.ui.modals.camp.scene.object_pressed.emit(&"dummy") # 前提注入(保留 emit):V5b 被测面=窗开态遮罩挡场景点选——真点击会被遮罩吞掉,树窗开态只能信号注入建立 await _frames() var tree_view: Control = views[&"dummy"] if not tree_view.visible: @@ -383,7 +395,7 @@ func _step_camp_window_shell() -> void: # 随栈 close 恢复语义逐层清账,V6/V8 从干净单窗态起步) await _close_all_windows(views) # V6 ESC 先关窗(生涯窗开态 Esc→关生涯窗、浮窗不唤起) - main.ui.modals.camp.scene.object_pressed.emit(&"notebook") + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"notebook").get_center()) await _frames() if not career.visible: _problems.append("[winshell] 生涯窗未开(V6 前置中断)〔窗口化批 V6〕") @@ -395,7 +407,7 @@ func _step_camp_window_shell() -> void: if main.ui.modals.camp.esc_overlay.visible: _problems.append("[winshell] 关窗同拍唤起浮窗(应无窗才进浮窗)〔窗口化批 V6〕") # V7 内容超高窗内滚动(树窗+真实投影喂加长行册) - main.ui.modals.camp.scene.object_pressed.emit(&"dummy") + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"dummy").get_center()) await _frames() if not tree_view.visible: _problems.append("[winshell] 树窗未开(V7 前置中断)〔窗口化批 V7〕") @@ -420,7 +432,7 @@ func _step_camp_window_shell() -> void: # 清窗经真实钮(同 V6 前置——干净单窗态起步) await _close_all_windows(views) # V8 生涯同壳(窗板同几何+返回钮在位+内容列滚动面在) - main.ui.modals.camp.scene.object_pressed.emit(&"notebook") + ProbeUiUtil.push_click_at(main, main.ui.modals.camp.scene.get_hotspot_rect(&"notebook").get_center()) await _frames() if not career.visible: _problems.append("[winshell] 生涯窗未开(V8 前置中断)〔窗口化批 V8〕") @@ -445,7 +457,7 @@ func _step_camp_window_shell() -> void: # 段尾收闭:生涯窗经真实「返回」钮关闭(on_camp_closed 关闭名单不含生涯窗——防残留泄段) var career_back: Button = ProbeUiUtil.find_button(career, "返回", "prefix") if career_back != null: - career_back.pressed.emit() + ProbeUiUtil.push_click(main, career_back) await _frames() if career.visible: _problems.append("[winshell] 生涯「返回」钮未隐窗〔窗口化批 V4〕") @@ -455,6 +467,7 @@ func _step_camp_window_shell() -> void: # 清窗经真实钮(逐窗巡检 visible→钮 pressed——栈 close 恢复账随层清空) func _close_all_windows(views: Dictionary) -> void: + # oracle: design/gdd/ux/camp.md:45 各窗自带关闭钮(清窗组) for k: StringName in [&"forge", &"chest", &"dummy", &"board"]: var w: Control = views[k] if not w.visible: @@ -463,7 +476,7 @@ func _close_all_windows(views: Dictionary) -> void: if back == null: _problems.append("[winshell] %s 底栏「返回营地」钮缺失〔窗口化批 V4〕" % String(k)) continue - back.pressed.emit() + ProbeUiUtil.push_click(main, back) await _frames()