diff --git a/game/probe/fx_shot_probe.gd b/game/probe/fx_shot_probe.gd index 9ba6d96d..eed8663e 100644 --- a/game/probe/fx_shot_probe.gd +++ b/game/probe/fx_shot_probe.gd @@ -37,14 +37,14 @@ func _ready() -> void: # 等关卡画面出图(choices_presented 异步到达——最多 3s 轮询) for i in 60: await get_tree().process_frame - if main.level_screen != null and main.level_screen.visible: + if main.ui.modals.level_screen != null and main.ui.modals.level_screen.visible: break - if main.level_screen == null or not main.level_screen.visible: + if main.ui.modals.level_screen == null or not main.ui.modals.level_screen.visible: _fail("关卡画面未出现(函数直调中间态路径)") return for i in 5: await get_tree().process_frame - main.level_screen.press_candidate(0) + main.ui.modals.level_screen.press_candidate(0) for i in 20: await get_tree().process_frame var st = main.run_scope.settlement diff --git a/game/probe/layout_probe.gd b/game/probe/layout_probe.gd index 25f494ae..ea8e366c 100644 --- a/game/probe/layout_probe.gd +++ b/game/probe/layout_probe.gd @@ -23,12 +23,12 @@ func _ready() -> void: add_child(main) await get_tree().process_frame ProbeUiUtil.isolate_save(main, "smoke_layout_probe", true, false, true) - main.debug_overlay.seed_edit.text = "42" # 种子钉死(确定性禁依赖默认随机分支) + main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(确定性禁依赖默认随机分支) main._start_adventure_from_menu() for i in range(8): await get_tree().process_frame # 进战斗屏——三区(B 战斗画面/C 角色信息/D 扫雷图)同屏常驻,菜单屏不构成断言面 - main.level_screen.press_candidate(0) + main.ui.modals.level_screen.press_candidate(0) for i in range(20): await get_tree().process_frame for vp in VIEWPORTS: @@ -57,8 +57,9 @@ func _check_one(label: String, main: Node) -> void: if vr.size != Vector2(VIEWPORTS[0]["size"]) and vr.size.y < 1.0: _problems.append("[%s] 逻辑视口退化 %s" % [label, vr.size]) var rects := {} + var battle_ui: Object = main.get("ui").battle # 注册面战区域容器(字段名与三区 key 同名——合同键即字段名) for key in ["stage", "info_panel", "board_zone"]: - var ctl: Control = main.get(key) + var ctl: Control = battle_ui.get(key) if ctl == null: _problems.append("[%s] %s = null(三区未装配)" % [label, key]) return @@ -73,7 +74,7 @@ func _check_one(label: String, main: Node) -> void: # battle_root 内层根下的全幅 ColorRect)这一缺陷类不在本探针拦截面内,探针对此仍全绿。 # 要拦它得改为递归遍历全子树找重叠,那是另一套判据(且极易误报容器父子关系), # 本探针不设此判据。 - var bv: Control = main.board_view + var bv: Control = main.ui.battle.board_zone.board_view if bv == null: _problems.append("[%s] board_view = null" % label) return @@ -89,6 +90,6 @@ func _check_one(label: String, main: Node) -> void: if rects[keys[i]].intersects(rects[keys[j]]): _problems.append("[%s] 顶层区重叠 %s×%s(%s ∩ %s)" % [label, keys[i], keys[j], rects[keys[i]], rects[keys[j]]]) - var cell: float = main.board_view.get("cell_size") + var cell: float = main.ui.battle.board_zone.board_view.get("cell_size") if cell < MIN_CELL: _problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL]) \ No newline at end of file diff --git a/game/probe/probe_ui_util.gd b/game/probe/probe_ui_util.gd index 1ea2f8db..27b614b9 100644 --- a/game/probe/probe_ui_util.gd +++ b/game/probe/probe_ui_util.gd @@ -130,7 +130,7 @@ static func open_camp_via_savesel(main, tag: String) -> bool: if FileAccess.file_exists(sp): DirAccess.remove_absolute(sp) main._open_save_select() - var new_btn: Button = main.save_select_overlay._new_btns.get(1) + var new_btn: Button = main.ui.modals.save_select_overlay._new_btns.get(1) if new_btn == null: return false new_btn.pressed.emit() # 真实按钮信号路径 → _on_save_select_new → create_slot+open_camp diff --git a/game/probe/screen_flow_probe.gd b/game/probe/screen_flow_probe.gd index ac32ec58..08fb6b30 100644 --- a/game/probe/screen_flow_probe.gd +++ b/game/probe/screen_flow_probe.gd @@ -50,10 +50,10 @@ func _ready() -> void: # 其余任意两屏同时为真=互斥失守。 func _screen_seals() -> Dictionary: return { - "菜单": main._start_overlay != null and main._start_overlay.visible, - "营地": main.camp_overlay != null and main.camp_overlay.visible, + "菜单": main.ui.screens.start_overlay != null and main.ui.screens.start_overlay.visible, + "营地": main.ui.modals.camp_overlay != null and main.ui.modals.camp_overlay.visible, "冒险": main.run_scope != null and main.run_scope.is_adventure, - "终局": main._end_overlay != null and main._end_overlay.visible, + "终局": main.ui.modals.end_overlay != null and main.ui.modals.end_overlay.visible, } @@ -101,7 +101,7 @@ func _frames(n: int = 2) -> void: func _step_boot_menu() -> void: _expect_exclusive("①boot", "菜单") _expect_adventure_seal("①boot", false) - if main.debug_overlay != null and main.debug_overlay.visible: + if main.ui.modals.debug_overlay != null and main.ui.modals.debug_overlay.visible: _problems.append("①boot:调试浮窗 boot 自动开(菜单优先失守)") @@ -117,10 +117,10 @@ func _step_menu_to_camp() -> void: # ---- ③ 营地 → 菜单(回退路径:营地关闭发自覆盖层自身信号)---- func _step_camp_back_to_menu() -> void: - if main.camp_overlay == null: + if main.ui.modals.camp_overlay == null: _problems.append("③营地→菜单:营地覆盖层句柄缺失") return - main.camp_overlay.closed.emit() + main.ui.modals.camp_overlay.closed.emit() await _frames() main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点) await _frames() @@ -133,22 +133,22 @@ func _step_camp_back_to_menu() -> void: func _step_save_select_open_back() -> void: 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._start_overlay, "进入存档", "prefix") + var esb: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "进入存档", "prefix") if esb == null: _problems.append("[savesel] mn3 进入存档钮缺失(卡② 交付物)") return esb.pressed.emit() # 真实按钮信号路径 → _open_save_select(卡② mn3 点击链——卡③ 直调中间态就此闭环) await get_tree().process_frame var seals := _screen_seals() - if not main.save_select_overlay.visible or not seals_check_single(seals): - _problems.append("[savesel] sl10 开层后屏级互斥失守(选档=%s)" % str(main.save_select_overlay.visible)) - var back_btn: Button = ProbeUiUtil.find_button(main.save_select_overlay, "返回", "prefix") + if not main.ui.modals.save_select_overlay.visible or not seals_check_single(seals): + _problems.append("[savesel] sl10 开层后屏级互斥失守(选档=%s)" % str(main.ui.modals.save_select_overlay.visible)) + var back_btn: Button = ProbeUiUtil.find_button(main.ui.modals.save_select_overlay, "返回", "prefix") if back_btn == null: _problems.append("[savesel] sl10 返回钮缺失(页壳顶条)") else: back_btn.pressed.emit() await get_tree().process_frame - if main.save_select_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("[savesel] sl10 返回后选档未隐藏或开始层未恢复(关闭链失守)") # ESC 语境:再开选档→合成 ESC 键→层不关、流程零扰动(ESC 不属选档语境——零实现断言) main._open_save_select() @@ -159,7 +159,7 @@ func _step_save_select_open_back() -> void: ev.pressed = true main.get_viewport().push_input(ev, true) await get_tree().process_frame - if not main.save_select_overlay.visible: + if not main.ui.modals.save_select_overlay.visible: _problems.append("[savesel] sl10 ESC 关闭了选档层(选档语境 ESC 应不响应——卡⑤ 范围外)") main._on_save_select_closed() await get_tree().process_frame @@ -171,7 +171,7 @@ func seals_check_single(f: Dictionary) -> bool: for k in f: if bool(f[k]): on += 1 - if main.save_select_overlay.visible: + if main.ui.modals.save_select_overlay.visible: on += 1 # 选档自身计入(调用点已开层) return on == 1 @@ -184,25 +184,25 @@ func _step_menu_to_adventure() -> void: _expect_adventure_seal("④菜单→冒险", true) if main.run_scope != null and main.run_scope.adventure == null: _problems.append("④菜单→冒险:冒险实例未建立(入口停点未达)") - if main.level_screen != null and not main.level_screen.visible: + if main.ui.modals.level_screen != null and not main.ui.modals.level_screen.visible: _problems.append("④菜单→冒险:入口停点未落在关卡地图屏(level_screen 不可见)") # ---- ⑤ 冒险内屏切换:关卡地图屏 ⇄ 战斗屏(屏内两态,屏级仍独占)---- func _step_level_to_battle_and_back() -> void: - if not main.level_screen.visible: + if not main.ui.modals.level_screen.visible: _problems.append("⑤冒险内切换:前置不在关卡地图屏") return main._exit_level_screen() await _frames() - if main.level_screen.visible: + if main.ui.modals.level_screen.visible: _problems.append("⑤冒险内切换:退出关卡屏后 level_screen 仍可见") - if main._battle_root != null and not main._battle_root.visible: + if main.ui.screens.battle_root != null and not main.ui.screens.battle_root.visible: _problems.append("⑤冒险内切换:战斗屏未接手(battle_root 不可见)") _expect_exclusive("⑤冒险内切换(关卡→战斗)", "冒险") main._enter_level_screen() await _frames() - if not main.level_screen.visible: + if not main.ui.modals.level_screen.visible: _problems.append("⑤冒险内切换:回到关卡地图屏失败") _expect_exclusive("⑤冒险内切换(战斗→关卡)", "冒险") @@ -215,16 +215,16 @@ func _step_abandon_to_end() -> void: main._on_abandon_requested() await _frames(4) _expect_exclusive("⑥放弃→终局", "终局") - if main._end_overlay != null and not main._end_overlay.visible: + if main.ui.modals.end_overlay != null and not main.ui.modals.end_overlay.visible: _problems.append("⑥放弃→终局:终局覆盖层未开") # ---- ⑦ 终局 → 菜单(终局出口返回开始界面)---- func _step_end_back_to_menu() -> void: - if main._end_overlay == null or not main._end_overlay.visible: + if main.ui.modals.end_overlay == null or not main.ui.modals.end_overlay.visible: _problems.append("⑦终局→菜单:前置不在终局") return - main._end_overlay.continue_pressed.emit() + main.ui.modals.end_overlay.continue_pressed.emit() await _frames(3) _expect_exclusive("⑦终局→菜单", "菜单") if main.run_scope != null and main.run_scope.is_adventure: diff --git a/game/probe/shot_probe.gd b/game/probe/shot_probe.gd index 2b108b21..4cc12488 100644 --- a/game/probe/shot_probe.gd +++ b/game/probe/shot_probe.gd @@ -51,10 +51,10 @@ func _ready() -> void: var top: Node = null var top_rx := -1 var top_ry := -1 - for c: Node in [main._start_overlay, main._battle_root, main._inter_overlay, - main.legend_overlay, main.node_overlay, main.level_screen, main.curio_overlay, - main._end_overlay, main.shop_overlay, main.camp_overlay, main.settings_overlay, - main.status_panel, main.career_overlay, main.confirm_dialog, main.debug_overlay]: + for c: Node in [main.ui.screens.start_overlay, main.ui.screens.battle_root, main.ui.screens.inter_overlay, + main.ui.modals.legend_overlay, main.ui.modals.node_overlay, main.ui.modals.level_screen, main.ui.modals.curio_overlay, + main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp_overlay, main.ui.modals.settings_overlay, + main.ui.modals.status_panel, main.ui.modals.career_overlay, main.ui.modals.confirm_dialog, main.ui.modals.debug_overlay]: if c == null or not (c is CanvasItem) or not (c as CanvasItem).visible: continue var rx: int = c.get_parent().get_index() @@ -63,14 +63,14 @@ func _ready() -> void: top = c top_rx = rx top_ry = ry - if top != main._start_overlay: + if top != main.ui.screens.start_overlay: _problems.append("boot 态顶层可见层≠start_overlay(实测 %s——菜单被埋)〔装配生命批 V4〕" % (top.name if top != null else "null")) # 自检二:菜单标题 label 必须 is_visible_in_tree 且 rect 落屏内 #(对比度只判反差不判「字在」——本条+checker 墨色占比共同补内容判据) var vp := get_viewport().get_visible_rect() for pair in [["menu_title", "Dungeon Sweeper"]]: - var lbl: Label = ProbeUiUtil.find_label(main._start_overlay, String(pair[1])) + var lbl: Label = ProbeUiUtil.find_label(main.ui.screens.start_overlay, String(pair[1])) if lbl == null: _problems.append("菜单文字缺失: %s(find_label 零命中)〔装配生命批 V4〕" % String(pair[1])) continue @@ -79,22 +79,22 @@ func _ready() -> void: var gr: Rect2 = lbl.get_global_rect() if not vp.encloses(gr): _problems.append("菜单文字 rect 越出视口: %s(%s ⊄ %s)〔装配生命批 V4〕" % [String(pair[1]), str(gr), str(vp)]) - _meta[String(pair[0])] = ProbeUiUtil.label_rect(main._start_overlay, String(pair[1])) + _meta[String(pair[0])] = ProbeUiUtil.label_rect(main.ui.screens.start_overlay, String(pair[1])) await _snap("menu") # ①b 设置面(settingsbtn 真实信号路径,开合同款真实钮) - var setbtn: Button = ProbeUiUtil.find_button(main._start_overlay, "设置", "prefix") + var setbtn: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "设置", "prefix") if setbtn == null: _problems.append("设置入口缺失(开始层 settingsbtn)") else: setbtn.pressed.emit() for i in 3: await get_tree().process_frame - if not main.settings_overlay.visible: + if not main.ui.modals.settings_overlay.visible: _problems.append("设置面未打开(settingsbtn 路径)") else: - _meta["settings_title"] = ProbeUiUtil.label_rect(main.settings_overlay, "设置") + _meta["settings_title"] = ProbeUiUtil.label_rect(main.ui.modals.settings_overlay, "设置") await _snap("settings") - var set_close: Button = ProbeUiUtil.find_button(main.settings_overlay, "返回", "prefix") + var set_close: Button = ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "返回", "prefix") if set_close == null: _problems.append("设置面返回钮缺失") else: @@ -105,19 +105,19 @@ func _ready() -> void: main._open_career() for i in 3: await get_tree().process_frame - if not main.career_overlay.visible or main._start_overlay.visible: + if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible: _problems.append("生涯页未打开或开始层未隐藏(函数直调中间态路径)") else: - _meta["career_title"] = ProbeUiUtil.label_rect(main.career_overlay, "生 涯") + _meta["career_title"] = ProbeUiUtil.label_rect(main.ui.modals.career_overlay, "生 涯") await _snap("career") - var career_close: Button = ProbeUiUtil.find_button(main.career_overlay, "返回", "prefix") + var career_close: Button = ProbeUiUtil.find_button(main.ui.modals.career_overlay, "返回", "prefix") if career_close == null: _problems.append("生涯页返回钮缺失") else: career_close.pressed.emit() for i in 3: await get_tree().process_frame - if main.career_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("生涯页关闭后页未隐藏或开始层未恢复") # ② 营地(进档开层新路径——卡② mn9;槽模板注入隔离命名空间) if not ProbeUiUtil.open_camp_via_savesel(main, "smoke_shot_probe_camp"): @@ -125,7 +125,7 @@ func _ready() -> void: else: await get_tree().process_frame await _snap("camp") - var close_btn: Button = ProbeUiUtil.find_button(main.camp_overlay, "关闭营地", "contains") + var close_btn: Button = ProbeUiUtil.find_button(main.ui.modals.camp_overlay, "关闭营地", "contains") if close_btn != null: close_btn.pressed.emit() await get_tree().process_frame @@ -137,24 +137,24 @@ func _ready() -> void: await get_tree().process_frame await _snap("level") # GP 常驻徽章断言(读数=runner.gp 现值,刷新=写路径回执后重查〔open_choices〕) - var gp_label: Label = ProbeUiUtil.find_label(main.level_screen, "◈ ") + var gp_label: Label = ProbeUiUtil.find_label(main.ui.modals.level_screen, "◈ ") if gp_label == null or not gp_label.is_visible_in_tree(): _problems.append("GP 徽章不可见(关卡画面顶栏)") - elif main.level_screen.get("_runner") != null: - var expect := "◈ %d" % int(main.level_screen.get("_runner").gp) + elif main.ui.modals.level_screen.get("_runner") != null: + var expect := "◈ %d" % int(main.ui.modals.level_screen.get("_runner").gp) if gp_label.text != expect: _problems.append("GP 徽章读数不符: %s != %s" % [gp_label.text, expect]) # ④ 战斗(节点一——真实选路提交路径 press_candidate,ui_click_probe :199 同款) - if main.level_screen != null and main.level_screen.visible: - main.level_screen.press_candidate(0) + if main.ui.modals.level_screen != null and main.ui.modals.level_screen.visible: + main.ui.modals.level_screen.press_candidate(0) for i in 10: await get_tree().process_frame await _snap("battle") # 战斗屏文字框/明度带锚点(全局 rect——checker 取样坐标源;明细带锚 S1 裁后改认现行文案) - _meta["battle_log_hint"] = ProbeUiUtil.label_rect(main.stage, "上轮摘要") + _meta["battle_log_hint"] = ProbeUiUtil.label_rect(main.ui.battle.stage, "上轮摘要") _meta["hero_panel"] = _node_rect(ProbeUiUtil.find_by_script(main, "hero_info_panel_view")) - _meta["stage_view"] = _node_rect(main.stage) - _meta["board_zone"] = _node_rect(main.board_zone) + _meta["stage_view"] = _node_rect(main.ui.battle.stage) + _meta["board_zone"] = _node_rect(main.ui.battle.board_zone) # draw calls 真机测量(基线 270→目标 <200;窗口化渲染帧读数, # 取值时机=战斗截图同帧后一帧——呈现终态满负荷) await get_tree().process_frame @@ -162,25 +162,25 @@ func _ready() -> void: _meta["battle_draw_calls"] = dc print("[shot_probe] battle draw_calls=%d(基线 270/目标 <200)" % dc) # ⑤ 状态面板(战斗屏状态角标真实信号路径;层在战斗屏之上开合) - var pb: Button = main.board_zone.status_badge + var pb: Button = main.ui.battle.board_zone.status_badge if pb == null or not pb.visible or pb.disabled: _problems.append("状态面板入口角标缺失/置灰(战斗屏 board_zone)") else: pb.pressed.emit() for i in 3: await get_tree().process_frame - if not main.status_panel.visible: + if not main.ui.modals.status_panel.visible: _problems.append("状态面板未打开(角标路径)") else: - _meta["status_title"] = ProbeUiUtil.label_rect(main.status_panel, "状态") + _meta["status_title"] = ProbeUiUtil.label_rect(main.ui.modals.status_panel, "状态") await _snap("status") - var status_close: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix") + var status_close: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix") if status_close == null: _problems.append("状态面板继续钮缺失") else: status_close.pressed.emit() await get_tree().process_frame - if main.status_panel.visible: + if main.ui.modals.status_panel.visible: _problems.append("状态面板关闭后未隐藏(继续钮路径)") else: _problems.append("关卡画面未出现(函数直调中间态路径)") diff --git a/game/probe/t17_defer_probe.gd b/game/probe/t17_defer_probe.gd index dde96ecc..53c1b4b7 100644 --- a/game/probe/t17_defer_probe.gd +++ b/game/probe/t17_defer_probe.gd @@ -29,14 +29,14 @@ func _ready() -> void: main._start_adventure_from_menu() # 函数直调中间态(卡② mn10——开始冒险钮已撤) for i in 60: await get_tree().process_frame - if main.level_screen != null and main.level_screen.visible: + if main.ui.modals.level_screen != null and main.ui.modals.level_screen.visible: break - if main.level_screen == null or not main.level_screen.visible: + if main.ui.modals.level_screen == null or not main.ui.modals.level_screen.visible: _fail("关卡画面未出现") return for i in 5: await get_tree().process_frame - main.level_screen.press_candidate(0) + main.ui.modals.level_screen.press_candidate(0) for i in 20: await get_tree().process_frame if main.run_scope.settlement == null: @@ -67,18 +67,18 @@ func _ready() -> void: _fail("战斗未胜利(result='%s')" % str(main.run_scope.settlement.battle_result)) return var t_settle := Time.get_ticks_msec() - var _rows_at_victory: int = main._row_pacer.pending_rows().size() # 时间戳①:结算入队完成帧(AP 耗尽/停手→五行段同步跑完→行全在队) + var _rows_at_victory: int = main.ui.screens.row_pacer.pending_rows().size() # 时间戳①:结算入队完成帧(AP 耗尽/停手→五行段同步跑完→行全在队) # 断言①胜利同帧:终局面未开 + 挂起在案 + 队列非空(判别性——旧行为同帧开窗即红) - if main._end_overlay.is_open(): + if main.ui.modals.end_overlay.is_open(): _problems.append("胜利同帧终局面已开(演播后排窗失效——同帧 flush 旧路径回潮)") - if main._deferred_modal == null: + if main.ui.modals.flow._deferred == null: _problems.append("胜利同帧挂起态缺失(_deferred_modal == null)") - if main._row_pacer.pending_rows().is_empty(): + if main.ui.screens.row_pacer.pending_rows().is_empty(): _problems.append("胜利同帧排播队列已空(结算行未走排播链)") var _played_rows: int = _rows_at_victory # 断言②排播放完:终局面开启(20s 守卫——0.22s×行数 ×10 余量) var waited := 0.0 - while not main._end_overlay.is_open() and waited < 20.0: + while not main.ui.modals.end_overlay.is_open() and waited < 20.0: await get_tree().process_frame waited += get_process_delta_time() var t_popup := Time.get_ticks_msec() @@ -86,7 +86,7 @@ func _ready() -> void: print("[t17_defer] AP点用完: %s" % ap_clock) print("[t17_defer] 触发结算: %s(与 AP 用完同帧——核心同步,相差 0ms)" % settle_clock) print("[t17_defer] 弹窗出现: %s(距触发结算 %d ms = 逐行演播时长,行数 %d)" % [popup_clock, t_popup - t_ap_out, _played_rows]) - if not main._end_overlay.is_open(): + if not main.ui.modals.end_overlay.is_open(): _problems.append("排播放完后终局面未开(%.1fs——演播完边沿派发失守)" % waited) if _problems.is_empty(): print("[t17_defer] PASS — 胜利同帧挂起+排播完开窗 两断言全过(waited=%.1fs)" % waited) diff --git a/game/probe/ui_click_probe.gd b/game/probe/ui_click_probe.gd index bc44cc7d..23829c8e 100644 --- a/game/probe/ui_click_probe.gd +++ b/game/probe/ui_click_probe.gd @@ -51,7 +51,7 @@ func _ready() -> void: var t_ui_ready := Time.get_ticks_msec() - t0 # 调试浮窗 boot 不开(菜单优先——入口=UI 最顶层全局浮层角标, # 任何 UI 不遮挡;浮层角标遮挡判据见设置面段) - if main.debug_overlay.visible: + if main.ui.modals.debug_overlay.visible: _problems.append("调试浮窗 boot 自动开(菜单优先失守)") # ---- 营地购买合成点击路径(选档进档开层→购买→关闭全走真实按钮信号路径; # 隔离存档换注 main.meta 保确定性(禁碰真实档 user://save.json);陈旧档段首清理)---- @@ -65,17 +65,17 @@ func _ready() -> void: _problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)") else: await get_tree().process_frame - if not main.camp_overlay.visible: + if not main.ui.modals.camp_overlay.visible: _problems.append("营地层未打开(进档开层新路径)") - elif main.camp_overlay._tree_buy_btns.is_empty(): + elif main.ui.modals.camp_overlay._tree_buy_btns.is_empty(): _problems.append("树淬炼柜台购买钮缺失(点击路径——四柜台重排后树=主界面)") else: # 树钮句柄用后即取(refresh_camp 随刷重建树行板——旧钮 queue_free,跨刷新句柄必失效) iso_c.meta_state._add_keystone(9999) - main.camp_overlay.refresh_camp(main._camp_data()) + main.ui.modals.camp_overlay.refresh_camp(main._camp_data()) await get_tree().process_frame var buy_btn: Button = null - for card in main.camp_overlay._tree_buy_btns: + for card in main.ui.modals.camp_overlay._tree_buy_btns: if String(card.node_id) == "sp_hp": buy_btn = card.btn if buy_btn == null: @@ -93,16 +93,16 @@ func _ready() -> void: _problems.append("营地树购买未持久(reload 后级 != 1)") # Fork 双态切换点击路径(V20——购=解锁默认 A,钮切 B;前置=同调位 1,先点亮) var socket_btn: Button = null - for card in main.camp_overlay._tree_buy_btns: + for card in main.ui.modals.camp_overlay._tree_buy_btns: if String(card.node_id) == "att_socket1": socket_btn = card.btn if socket_btn != null: socket_btn.pressed.emit() await get_tree().process_frame - main.camp_overlay.refresh_camp(main._camp_data()) + main.ui.modals.camp_overlay.refresh_camp(main._camp_data()) await get_tree().process_frame var fork_btn: Button = null - for card in main.camp_overlay._tree_buy_btns: + for card in main.ui.modals.camp_overlay._tree_buy_btns: if String(card.node_id) == "fork_posture": fork_btn = card.btn if fork_btn == null: @@ -110,10 +110,10 @@ func _ready() -> void: else: fork_btn.pressed.emit() await get_tree().process_frame - main.camp_overlay.refresh_camp(main._camp_data()) + main.ui.modals.camp_overlay.refresh_camp(main._camp_data()) await get_tree().process_frame var side_b: Button = null - for fk in main.camp_overlay._tree_fork_btns: + for fk in main.ui.modals.camp_overlay._tree_fork_btns: if String(fk.group) == "fg_posture" and String(fk.side) == "b": side_b = fk.btn if side_b == null: @@ -128,7 +128,7 @@ func _ready() -> void: # ---- 营地六处灰字取色断言(迭代36 wc4——真实入口开营地后按文本锚取 label, # font_color 逐处 == STONE_DEEP;六源位 :167 图鉴锁文/:417+:476 行状态字/:509 同调形态/:513 档位标签/:525 刷新行)---- var camp_labels: Array = [] - _collect_labels_by_text(main.camp_overlay, camp_labels) + _collect_labels_by_text(main.ui.modals.camp_overlay, camp_labels) var atlas_locks := _labels_with_text(camp_labels, "柜台结构位预留") if atlas_locks.is_empty(): _problems.append("营地灰字:图鉴柜台锁文 label 未找到(wc4 定位锚失守)") @@ -137,9 +137,9 @@ func _ready() -> void: _problems.append("营地灰字:图鉴锁文未用 STONE_DEEP(wc4 :167)") # 「未解锁」锚收窄到秘宝匣行容器子树(树行/锻造行也有「未解锁」态字样——非 wc4 目标位) var locked_status: Array = [] - if main.camp_overlay.get("_workshop_holder") != null: + if main.ui.modals.camp_overlay.get("_workshop_holder") != null: var ws_labels: Array = [] - _collect_labels_by_text(main.camp_overlay._workshop_holder, ws_labels) + _collect_labels_by_text(main.ui.modals.camp_overlay._workshop_holder, ws_labels) locked_status = _labels_with_text(ws_labels, "未解锁") if locked_status.is_empty(): _problems.append("营地灰字:秘宝匣行「未解锁」态 label 未找到(wc4 夹具前提失守——新档应有锁定秘宝行)") @@ -177,7 +177,7 @@ func _ready() -> void: await get_tree().process_frame main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点) await get_tree().process_frame - if main.camp_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.camp_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("营地关闭后未恢复开始层") # ---- 选档界面行为走查(迭代 33 卡③):sl5 空槽新档链/sl6 有档槽进档链/sl7 删档二次确认双路径 # /sl8 消费方分派单口/sl12 动线序不回闪。开层=函数直调(「进入存档」钮归卡② mn3——中间态在案); @@ -196,11 +196,11 @@ func _ready() -> void: f_c2.close() # 槽2 有档异值(进档链夹具);槽1/3 空 main._open_save_select() await get_tree().process_frame - if not main.save_select_overlay.visible: + if not main.ui.modals.save_select_overlay.visible: _problems.append("[savesel] 选档层未打开(click 段前置)") # sl12 动线序(新档链同拍采样):逐帧可见性次序——首帧选档隐藏时营地层必须已可见、开始层保持隐藏 #(禁先关后开——_close_modal 会恢复开始层=回闪一帧) - var new_btn: Button = main.save_select_overlay._new_btns.get(1) + var new_btn: Button = main.ui.modals.save_select_overlay._new_btns.get(1) if new_btn == null: _problems.append("[savesel] 槽1 新档钮缺失(sl5/sl12 驱动中断)") else: @@ -208,7 +208,7 @@ func _ready() -> void: new_btn.pressed.emit() # 真实按钮信号路径 → _on_save_select_new → create_slot+open_camp for fi in 3: await get_tree().process_frame - samples.append([main.save_select_overlay.visible, main.camp_overlay.visible, main._start_overlay.visible]) + samples.append([main.ui.modals.save_select_overlay.visible, main.ui.modals.camp_overlay.visible, main.ui.screens.start_overlay.visible]) if samples[0][0]: _problems.append("[savesel] sl12 新档后选档层仍可见(落点开层未发生)") elif not samples[0][1]: @@ -217,8 +217,8 @@ func _ready() -> void: _problems.append("[savesel] sl12 动线序违约:开始层在落点帧恢复(回闪一帧)") if main.save_manager.slots.active_slot() != 1 or not FileAccess.file_exists(csel_tpl % 1): _problems.append("[savesel] sl5 新档链失守(active=%d 应=1 且槽1 白板应落盘)" % main.save_manager.slots.active_slot()) - var occluded: Array = main._modals._occluded.get(main.camp_overlay, []) - if not occluded.has(main.save_select_overlay): + var occluded: Array = main.ui.modals.flow.stack._occluded.get(main.ui.modals.camp_overlay, []) + if not occluded.has(main.ui.modals.save_select_overlay): _problems.append("[savesel] sl12 选档层被关闭而非栈记账挂起(先关后开形态——遮挡挂起中间态钉死)") if int((JSON.parse_string(FileAccess.get_file_as_string(csel_tpl % 1)) as Dictionary).get("meta", {}).get("keystone_balance", -1)) != 0: _problems.append("[savesel] sl5 新档非白板(槽1 落盘应全零)") @@ -227,7 +227,7 @@ func _ready() -> void: # sl6 进档链:槽2 进档 → 激活=2(camp 数据随激活槽) main._open_save_select() await get_tree().process_frame - var load_btn: Button = main.save_select_overlay._load_btns.get(2) + var load_btn: Button = main.ui.modals.save_select_overlay._load_btns.get(2) if load_btn == null: _problems.append("[savesel] 槽2 进档钮缺失(sl6 驱动中断——夹具应有档)") else: @@ -235,51 +235,51 @@ func _ready() -> void: 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()) - if not main.camp_overlay.visible or main.save_select_overlay.visible: + if not main.ui.modals.camp_overlay.visible or main.ui.modals.save_select_overlay.visible: _problems.append("[savesel] sl6 落点未开营地层或选档未隐藏") main._on_camp_closed() await get_tree().process_frame # sl7 删档二次确认双路径:一击只开窗(文件仍在)→取消只关窗(文件仍在)→再开窗确认才删(槽回空) main._open_save_select() await get_tree().process_frame - var del_btn: Button = main.save_select_overlay._delete_btns.get(2) + var del_btn: Button = main.ui.modals.save_select_overlay._delete_btns.get(2) if del_btn == null: _problems.append("[savesel] 槽2 删档钮缺失(sl7 驱动中断)") else: del_btn.pressed.emit() await get_tree().process_frame - if not main.confirm_dialog.is_open(): + if not main.ui.modals.confirm_dialog.is_open(): _problems.append("[savesel] sl7 一击未只开确认窗(直删形态)") elif not FileAccess.file_exists(csel_tpl % 2): _problems.append("[savesel] sl7 确认前文件已被删(一次点击即删违契)") else: - if not main._modal_flow.stack._occluded.has(main.confirm_dialog): + if not main.ui.modals.flow.stack._occluded.has(main.ui.modals.confirm_dialog): _problems.append("[savesel] sl7 确认窗未经模态栈开(直开旁路——wr2 单口失守)") - elif not main._input_locked(): + elif not main.ui.modals.flow.locks_input(): _problems.append("[savesel] sl7 确认窗开时输入锁未生效(锁域失守——wr2)") - main.confirm_dialog.cancelled.emit() # 取消路径(真实信号——cancelled 钮同源) + main.ui.modals.confirm_dialog.cancelled.emit() # 取消路径(真实信号——cancelled 钮同源) await get_tree().process_frame - if main.confirm_dialog.is_open() or not FileAccess.file_exists(csel_tpl % 2): + if main.ui.modals.confirm_dialog.is_open() or not FileAccess.file_exists(csel_tpl % 2): _problems.append("[savesel] sl7 取消路径失守(应只关窗、文件仍在)") elif main._confirm_consumer != "": _problems.append("[savesel] sl8 取消后消费方状态量未清(分派面残留)") - if main._modal_flow.stack._occluded.has(main.confirm_dialog): + if main.ui.modals.flow.stack._occluded.has(main.ui.modals.confirm_dialog): _problems.append("[savesel] sl7 取消后确认窗遮挡账未清(关侧未走模态栈——wr2)") del_btn.pressed.emit() # 再开窗走确认路径 await get_tree().process_frame - main.confirm_dialog.confirmed.emit() # 确认路径(真实信号——confirmed 钮同源) + main.ui.modals.confirm_dialog.confirmed.emit() # 确认路径(真实信号——confirmed 钮同源) await get_tree().process_frame - if FileAccess.file_exists(csel_tpl % 2) or main.confirm_dialog.is_open(): + if FileAccess.file_exists(csel_tpl % 2) or main.ui.modals.confirm_dialog.is_open(): _problems.append("[savesel] sl7 确认路径失守(应删档+关窗)") - elif main.run_scope.is_adventure or main._start_overlay.visible: + elif main.run_scope.is_adventure or main.ui.screens.start_overlay.visible: _problems.append("[savesel] sl8 删档确认误触发开局(双常驻接线形态——分派单口失守)") - elif bool(main.save_select_overlay._occupied_flags.get(2, true)): + elif bool(main.ui.modals.save_select_overlay._occupied_flags.get(2, true)): _problems.append("[savesel] sl7 删后选档面未就地刷新(槽2 应回空态)") elif main._confirm_consumer != "": _problems.append("[savesel] sl8 确认后消费方状态量未清(分派面残留)") main._on_save_select_closed() await get_tree().process_frame - if main.save_select_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("[savesel] 关闭后层未隐藏或开始层未恢复(模态栈单口失守)") # ---- 设置面段(隔离 store 换注禁碰真实 user://settings.cfg;真实按钮信号路径)---- @@ -289,19 +289,19 @@ func _ready() -> void: var iso_s = load("res://presentation/settings_store.gd").new("user://smoke_click_probe_settings.cfg") iso_s.load_settings() main.settings_store = iso_s - main.settings_overlay.retarget(iso_s) - var settings_btn: Button = ProbeUiUtil.find_button(main._start_overlay, "设置", "prefix") + main.ui.modals.settings_overlay.retarget(iso_s) + var settings_btn: Button = ProbeUiUtil.find_button(main.ui.screens.start_overlay, "设置", "prefix") if settings_btn == null: _problems.append("设置入口按钮缺失(开始层 settingsbtn)") else: settings_btn.pressed.emit() # 真实按钮信号路径 → _open_settings await get_tree().process_frame - if not main.settings_overlay.visible or main._start_overlay.visible: + if not main.ui.modals.settings_overlay.visible or main.ui.screens.start_overlay.visible: _problems.append("设置面未打开(点击路径)") else: # 减动效转正双态(卡④ AC③)——M4 适配:拨杆开关(_motion_btn)语义翻转 on=完整/off=快速, # 状态字随行 _motion_st;查找由文字改成员引用(拨杆无文字) - var motion_btn: Button = main.settings_overlay._motion_btn + var motion_btn: Button = main.ui.modals.settings_overlay._motion_btn if motion_btn == null: _problems.append("设置面减动效开关缺失") else: @@ -318,7 +318,7 @@ func _ready() -> void: if UiKit.motion_scale != 1.0: _problems.append("减动效关未恢复(motion_scale != 1.0)") # 音量实时生效(卡④ AC①):滑杆 30% → master bus db 与 store 双读 - var vol: HSlider = main.settings_overlay._volume + var vol: HSlider = main.ui.modals.settings_overlay._volume vol.value = 30 await get_tree().process_frame var master_idx := AudioServer.get_bus_index("Master") @@ -329,24 +329,24 @@ func _ready() -> void: _problems.append("音量未入 store(%f ≠ 0.3)" % iso_s.volume_linear) # 调试入口断言(卡⑦ c4 发布态换代——角标恒隐;原「最顶层浮层不被遮挡」 # 回归语义随调试浮层发布态退役,申报见迭代 38 实现报告 §8.2) - if main.debug_badge == null or main.debug_badge.visible: + if main.ui.screens.debug_badge == null or main.ui.screens.debug_badge.visible: _problems.append("设置面开中调试角标可见(发布态恒隐失守)") - var settings_close: Button = ProbeUiUtil.find_button(main.settings_overlay, "返回", "prefix") + var settings_close: Button = ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "返回", "prefix") if settings_close == null: _problems.append("设置面返回按钮缺失") else: settings_close.pressed.emit() await get_tree().process_frame - if main.settings_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.settings_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("设置面关闭后未恢复开始层") # 旧入口零残留断言(卡④ AC③——测试调整板不含减动效开关;14.5 起载体=调试浮窗) - var legacy_motion = ProbeUiUtil.find_check_button(main.debug_overlay, "减动效") + var legacy_motion = ProbeUiUtil.find_check_button(main.ui.modals.debug_overlay, "减动效") if legacy_motion != null: _problems.append("调整板减动效旧入口未退役(设置面唯一入口失守——卡④ AC③)") # D1 断言(二轮):退出游戏只在一级菜单——开始菜单有、设置面无 - if ProbeUiUtil.find_button(main._start_overlay, "退出游戏", "prefix") == null: + if ProbeUiUtil.find_button(main.ui.screens.start_overlay, "退出游戏", "prefix") == null: _problems.append("开始菜单缺退出游戏按钮(二轮 D1)") - if ProbeUiUtil.find_button(main.settings_overlay, "退出游戏", "prefix") != null: + if ProbeUiUtil.find_button(main.ui.modals.settings_overlay, "退出游戏", "prefix") != null: _problems.append("设置面仍含退出游戏按钮(二轮 D1——只在一级菜单出现)") # ---- 生涯页段(入口点击链+关闭回返,真实按钮信号路径;本页只读零写, # 值断言归冒烟投影段与 ui_struct 结构族,此处只验开合路径与零跳转)---- @@ -354,45 +354,45 @@ func _ready() -> void: if true: main._open_career() await get_tree().process_frame - if not main.career_overlay.visible or main._start_overlay.visible: + if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible: _problems.append("生涯页未打开(点击路径)") else: # 零跳转契约(提纲 P4 甲):页内除「返回」外零导航钮 for nav in ["营地", "商店", "设置", "退出", "冒险"]: - if ProbeUiUtil.find_button(main.career_overlay, nav, "contains") != null: + if ProbeUiUtil.find_button(main.ui.modals.career_overlay, nav, "contains") != null: _problems.append("生涯页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav) # 局内入口断言(提纲 P4 甲):生涯入口只在开始层——战斗屏/地图屏零生涯角标 - if main.board_zone.find_child("career_badge", true, false) != null \ - or main.level_screen.find_child("career_badge", true, false) != null: + if main.ui.battle.board_zone.find_child("career_badge", true, false) != null \ + or main.ui.modals.level_screen.find_child("career_badge", true, false) != null: _problems.append("局内屏出现生涯入口件(局内不放生涯入口违约)") - var career_close: Button = ProbeUiUtil.find_button(main.career_overlay, "返回", "prefix") + var career_close: Button = ProbeUiUtil.find_button(main.ui.modals.career_overlay, "返回", "prefix") if career_close == null: _problems.append("生涯页返回按钮缺失") else: career_close.pressed.emit() await get_tree().process_frame - if main.career_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible: _problems.append("生涯页关闭后未恢复开始层") # 换回全新零档(营地购买态不泄入后续冒险段——确定性;档案残留下次段首清理兜底) ProbeUiUtil.isolate_save(main, "smoke_click_probe_reset", true, false, true) # 零档换注(继续上局钮已随三入口化撤出——卡② mn8) - main.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役) - main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支) + main.ui.modals.debug_overlay.set_sliders(400, 200) # 高加成保 L1 可完成——L2 岔路选路段只能经冒险推进到达(单关直入口退役) + main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,行为走查确定性禁依赖默认分支) var t1 := Time.get_ticks_msec() main._start_adventure_from_menu() await get_tree().process_frame await get_tree().process_frame # 既有口径回归(迭代36 wc3——调试滑条 400/200=局外加成 pct>0,开场行应带「(含面板加成)」; # 本局为本探针首个冒险,日志无先前行,全文本判即可) - var wc3_txt: String = main.log_view._log.get_parsed_text() + var wc3_txt: String = main.ui.battle.log_view._log.get_parsed_text() if not wc3_txt.contains("种子 42") or not wc3_txt.contains("(含面板加成)"): _problems.append("开场行回归失守:局外加成(pct>0)局的开场行应带「(含面板加成)」(wc3)") # 关卡开始 = 入口选路停点——press_candidate 图上命中路径选入口候选进首战 #(首格可点 AC 口径随之含入口选路一步:菜单点击 → 停点呈现 → pick → 棋盘安装) - if main.level_screen.visible: + if main.ui.modals.level_screen.visible: # 独立性断言:独立屏激活期战斗屏宿主整体隐藏 - if main._battle_root.visible: + if main.ui.screens.battle_root.visible: _problems.append("独立屏激活期战斗屏宿主未隐藏(零透出契约失守)") - main.level_screen.press_candidate(0) + main.ui.modals.level_screen.press_candidate(0) await get_tree().process_frame await get_tree().process_frame var t_entry := Time.get_ticks_msec() - t1 @@ -402,7 +402,7 @@ func _ready() -> void: var first_cell_ms := t_entry if first_cell_ms > 500: _problems.append("进入战斗屏首格可点超时 %dms > 500ms" % first_cell_ms) - if main.board_view.cells_probe().is_empty(): + if main.ui.battle.board_zone.board_view.cells_probe().is_empty(): _problems.append("首格未可点(cells=0)") print("[click_probe] 首格可点计时 %dms(进入战斗屏口径,≤500ms AC)· 场景冷启动 %dms(含 UI 构建 %dms,观测项)" % [ first_cell_ms, t_total, t_ui_ready]) @@ -413,68 +413,68 @@ func _ready() -> void: while not _done and action_budget > 0: action_budget -= 1 await get_tree().process_frame - if main.node_overlay.visible: - main.node_overlay.press_continue() + if main.ui.modals.node_overlay.visible: + main.ui.modals.node_overlay.press_continue() _seals_placed.clear() continue - if main.curio_overlay.visible: + if main.ui.modals.curio_overlay.visible: # 三选一停点(CurioPickOverlay——奖励节点/关卡完成兑付共用面,须与硬锁态适配, # 未适配则走到即卡死)。两态分派:result 态(兑付反馈——点继续推进)/ # 待选态(点首件+确认)。真实信号路径全线。〔无替换子态——装配不可变契约。〕 # 三选一停点输入锁定断言(停点态断言面:待选/兑付两态共用此层)。 - if not _tested_curio_head and main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty() and String(main.curio_overlay._header).contains("关卡奖励点"): + if not _tested_curio_head and main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty() and String(main.ui.modals.curio_overlay._header).contains("关卡奖励点"): # 停点态判据 = _node_curio_pending(奖励节点三选一停点住此——原条件查 # _curio_pending〔关卡完成三选一态〕与奖励头部互斥、恒不可命中=拦截能力未证病根, # 卡⑤ d4 转正随修;头部校验串本体零触碰) _tested_curio_head = true - var ch := String(main.curio_overlay._header) + var ch := String(main.ui.modals.curio_overlay._header) if not ch.contains("冒险牌库") or not ch.contains("属性值提升与补给") or ch.contains("同权重") or ch.contains("同上限") or ch.contains("提升池"): _problems.append("三选一头部旧话残留或新话缺失(wa4——%s)" % ch.replace(" ", "|")) # 奖励节点战报文案换代断言(卡⑤ wr2——reward_line 同屏打印于停点拉起前, # 同一驱动路径承载;旧「内容待定」残留或新话缺失即红) - var logtext: String = main.log_view.parsed_text() + var logtext: String = main.ui.battle.log_view.parsed_text() if not logtext.contains("关卡奖励点(三选一——选毕推进)") or logtext.contains("内容待定"): _problems.append("奖励节点战报文案未换代(卡⑤ wr2——旧话残留或新话缺失)") - if not main._input_locked(): + if not main.ui.modals.flow.locks_input(): _problems.append("三选一停点打开时输入未锁定") - if main.curio_overlay._continue_btn.visible: - main.curio_overlay.press_continue() + if main.ui.modals.curio_overlay._continue_btn.visible: + main.ui.modals.curio_overlay.press_continue() elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._node_pending.is_empty(): - main.curio_overlay.press_card(0) # 奖励节点待选态:点首件+确认(真实按钮信号路径——与关卡完成停点同形) - main.curio_overlay.press_confirm() + main.ui.modals.curio_overlay.press_card(0) # 奖励节点待选态:点首件+确认(真实按钮信号路径——与关卡完成停点同形) + main.ui.modals.curio_overlay.press_confirm() elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty(): - main.curio_overlay.press_card(0) - main.curio_overlay.press_confirm() + main.ui.modals.curio_overlay.press_card(0) + main.ui.modals.curio_overlay.press_confirm() else: - main.curio_overlay.press_continue() # 停点已消费的残面——继续关屏 + main.ui.modals.curio_overlay.press_continue() # 停点已消费的残面——继续关屏 _seals_placed.clear() continue - if main.shop_overlay.visible: + if main.ui.modals.shop_overlay.visible: # 商店房覆盖层(摆放约束:每终点前一位恰一店,真实路径恒必经一店):零购离开(真实 leave 信号路径)放行选路。 # 分派链序:商店层=选路页上的模态覆盖(停点呈现为 # level_screen+shop 双 visible)——必须先于 level_screen 分派,否则下层候选点击 # 空转烧尽预算(实证:lv2 商店停点双 visible 循环 12000 帧 done=false)。 - main.shop_overlay.leave_pressed.emit() + main.ui.modals.shop_overlay.leave_pressed.emit() _seals_placed.clear() continue - if main.level_screen.visible: + if main.ui.modals.level_screen.visible: # 节点图选路口(零自动推进停点)——图上命中路径交替选候选(岔路使用行为;布局决策 2 改造) # 地图屏角标一次性 case:选路前经角标开/关状态面板(层在地图屏之上开合, # 关闭后地图屏选路态零扰动——P2 甲层级语义) - if not _tested_map_status and not main.status_panel.visible: + if not _tested_map_status and not main.ui.modals.status_panel.visible: _tested_map_status = true - var mpb: Button = main.level_screen.status_badge + var mpb: Button = main.ui.modals.level_screen.status_badge if mpb == null or not mpb.visible or mpb.disabled: _problems.append("地图屏状态角标缺失/未点亮/被置灰(P1 丁)") else: mpb.pressed.emit() - if not main.status_panel.visible or not main.level_screen.visible: + if not main.ui.modals.status_panel.visible or not main.ui.modals.level_screen.visible: _problems.append("地图屏角标开状态面板失败或地图屏被误藏(P2 甲层级失守)") - var rbtn2: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix") + var rbtn2: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix") if rbtn2 != null: rbtn2.pressed.emit() - if main.status_panel.visible or not main.level_screen.visible: + if main.ui.modals.status_panel.visible or not main.ui.modals.level_screen.visible: _problems.append("状态面板关闭后地图屏态失守(恢复语义破)") # 地图屏加成帽换代断言(迭代36 卡A wa2——「特性」→「属性值提升」;帽 Label 常在树; # 独立旗标——_tested_map_status 在同拍前置块已置位,禁复用其门控) @@ -482,7 +482,7 @@ func _ready() -> void: _tested_map_cap = true var cap_found := false var ls_labels: Array = [] - _collect_labels_by_text(main.level_screen, ls_labels) + _collect_labels_by_text(main.ui.modals.level_screen, ls_labels) for l in ls_labels: if l.text == "属性值提升": cap_found = true @@ -494,20 +494,20 @@ func _ready() -> void: if main.run_scope.runner != null: for nd in main.run_scope.runner.nodes(): var nd_id: int = main.run_scope.runner.node_id_of(nd) - if String(nd.get("type", "")) == "reward" and main.level_screen._cand_ids.has(nd_id): - pick = main.level_screen._cand_ids.find(nd_id) + if String(nd.get("type", "")) == "reward" and main.ui.modals.level_screen._cand_ids.has(nd_id): + pick = main.ui.modals.level_screen._cand_ids.find(nd_id) break - main.level_screen.press_candidate(pick) + main.ui.modals.level_screen.press_candidate(pick) _route_clicks += 1 _seals_placed.clear() continue - if main._inter_overlay.visible: + if main.ui.screens.inter_overlay.visible: # 大奖励屏(冒险级停点——非末关的关卡完成恰一次):首见确认推进 L2(真实按钮信号路径);再见 = L2 已关卡完成,收口 - if not main._input_locked(): + if not main.ui.modals.flow.locks_input(): _problems.append("大奖励屏打开时输入未锁定") _inter_confirms += 1 if _inter_confirms == 1: - var next_btn: Button = ProbeUiUtil.find_button(main._inter_overlay, "进入下一层", "prefix") + var next_btn: Button = ProbeUiUtil.find_button(main.ui.screens.inter_overlay, "进入下一层", "prefix") if next_btn == null: _problems.append("大奖励屏确认按钮缺失") else: @@ -522,23 +522,23 @@ func _ready() -> void: break _seals_placed.clear() continue - if main._end_overlay.is_open() and main._settlement_stop_pending: + if main.ui.modals.end_overlay.is_open() and main.run_scope.settlement_stop_timer.is_armed(): # 战斗胜利总结结算框(真实信号路径——每场普通胜利到达):断言已拍板面后点「下一节点」 - if not main._input_locked(): + if not main.ui.modals.flow.locks_input(): _problems.append("胜利结算框打开时输入未锁定") - var gp_line: String = "%s %s" % [main._end_overlay._gp_name.text, main._end_overlay._gp_val.text] # 两列账行(审计差距④)——名/值列拼回整串断言 + var gp_line: String = "%s %s" % [main.ui.modals.end_overlay._gp_name.text, main.ui.modals.end_overlay._gp_val.text] # 两列账行(审计差距④)——名/值列拼回整串断言 if not gp_line.contains("GP +%d" % main.run_scope.last_gain_amount) or not gp_line.contains("合计 %d" % int(main.run_scope.runner.gp)): _problems.append("胜利结算框 GP 增量/总量与钱包实态不符(%s|实态 +%d/%d)" % [gp_line, main.run_scope.last_gain_amount, int(main.run_scope.runner.gp)]) - if main._end_overlay.verdict_text() == "": + if main.ui.modals.end_overlay.verdict_text() == "": _problems.append("胜利结算框判定行缺失") _settlement_confirms += 1 - main._end_overlay._btn.pressed.emit() # 「下一节点」真实信号路径 + main.ui.modals.end_overlay._btn.pressed.emit() # 「下一节点」真实信号路径 await get_tree().process_frame await get_tree().process_frame - if main._settlement_stop_pending or not main.level_screen.visible: + if main.run_scope.settlement_stop_timer.is_armed() or not main.ui.modals.level_screen.visible: _problems.append("胜利结算框「下一节点」未进选路页(卡⑥ AC②)") continue - if main._end_overlay.is_open(): + if main.ui.modals.end_overlay.is_open(): # 冒险终局(失败/放弃/打穿)——无单关直入口,见终局即收口(模态断言照旧) # 三选一链适配:_adventure_mode 仍真且非选路挂起=中途结算面(Boss 胜利 # 总结「继续」后挂三选一→真终局未到)——真实信号路径点继续推进。 @@ -547,13 +547,13 @@ 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._end_overlay._btn.pressed.emit() + main.ui.modals.end_overlay._btn.pressed.emit() continue - if not main._input_locked(): + if not main.ui.modals.flow.locks_input(): _problems.append("终局 overlay 打开时输入未锁定") _done = true print("[click_probe] 冒险终局:%s · 选路口点击 %d 次" % [ - main._end_overlay.verdict_text().replace("\n", " "), _route_clicks]) + main.ui.modals.end_overlay.verdict_text().replace("\n", " "), _route_clicks]) break if main.run_scope.settlement == null or main.run_scope.runner == null: continue @@ -566,16 +566,16 @@ func _ready() -> void: # 上轮摘要恢复断言(卡⑥ wr1-b——改前 _round_rows 类型错 append 全数失败、缓冲恒空、 # 摘要自引入类型错以来从未显示):战中场观测点=round≥2 且排播队列已排空(轮首分隔行 # 已消费=finish_settlement_round 已刷);不挂循环出口——收尾的续玩重建段会再走 log_view.clear() - if not _tested_round_summary and int(st["round"]) >= 2 and main._row_queue.is_empty(): + if not _tested_round_summary and int(st["round"]) >= 2 and main.ui.screens.row_pacer.pending_rows().is_empty(): _tested_round_summary = true - if main.log_view._summary.text == "": + if main.ui.battle.log_view._summary.text == "": _problems.append("上轮摘要恒空(卡⑥ wr1-b——结算行缓冲仍失真)") if JSON.stringify(st) == JSON.stringify(_last_state): # 排播演播期豁免(硬锁 v3——合法停顿态):演播期棋盘锁生效、probe 点击被吞、 # 逻辑态恒定属预期。判据=纯队列(与 main 锁同源)。Waiting 收尾等待期**不豁免**—— # 锁若把收尾链卡死,此处 stall 照报(探测面)。 # T1 D1 处置=保留豁免+注记:冻结/缓存/重放语义不再靠豁免绕行,由 _locktest_tick # 脚本化断言正面承载(每 run 一次;逻辑态恒定本豁免继续作 stall 判据)。 - if main._row_queue.is_empty(): + if main.ui.screens.row_pacer.pending_rows().is_empty(): _stall_frames += 1 if _stall_frames == 30: _report_stall(st) @@ -595,36 +595,36 @@ func _ready() -> void: # ---- 预算耗尽收口(T17 硬锁批适配):选件变强后 probe 可能活过预算不再战败——断点段前提 # (冒险终局面在案)须主动满足。收口驱动:任何停点层逐个推进(复用主循环分支语义)至 # level_screen 选路停点→真实放弃二击→终局面。战斗中则继续 autoplay 打完。 - if not _done and not main._end_overlay.is_open(): + if not _done and not main.ui.modals.end_overlay.is_open(): var close_guard := 8000 var dbg_frame := 0 # T17 演播后排窗:每场胜利 +~400 帧演播等待(旧同帧 flush 零等待)——守卫按多场战斗放大 while close_guard > 0: close_guard -= 1 await get_tree().process_frame - if main._end_overlay.is_open(): + if main.ui.modals.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._end_overlay._btn.pressed.emit() + main.ui.modals.end_overlay._btn.pressed.emit() else: break - elif main.curio_overlay.visible: - if main.curio_overlay._continue_btn.visible: - main.curio_overlay.press_continue() + elif main.ui.modals.curio_overlay.visible: + if main.ui.modals.curio_overlay._continue_btn.visible: + main.ui.modals.curio_overlay.press_continue() elif main.run_scope.adventure != null and not main.run_scope.adventure.stop()._pending.is_empty(): - main.curio_overlay.press_card(0) - main.curio_overlay.press_confirm() + main.ui.modals.curio_overlay.press_card(0) + main.ui.modals.curio_overlay.press_confirm() else: - main.curio_overlay.press_continue() - elif main.node_overlay.visible: - main.node_overlay.press_continue() - elif main.shop_overlay.visible: - main.shop_overlay.leave_pressed.emit() - elif main._inter_overlay.visible: - var next_btn2: Button = ProbeUiUtil.find_button(main._inter_overlay, "进入下一层", "prefix") + main.ui.modals.curio_overlay.press_continue() + elif main.ui.modals.node_overlay.visible: + main.ui.modals.node_overlay.press_continue() + elif main.ui.modals.shop_overlay.visible: + main.ui.modals.shop_overlay.leave_pressed.emit() + 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() - elif main.level_screen.visible: - var abandon_btn: Button = ProbeUiUtil.find_button(main.level_screen, "放弃", "prefix") + elif main.ui.modals.level_screen.visible: + var abandon_btn: Button = ProbeUiUtil.find_button(main.ui.modals.level_screen, "放弃", "prefix") if abandon_btn != null: _closed_via_abandon = true abandon_btn.pressed.emit() @@ -652,14 +652,14 @@ func _ready() -> void: str(((bpf as Dictionary).get("bonus_deck", []) as Array).size()), str((bpf as Dictionary).get("bonus_pending"))]) _problems.append("断点段:战败结算后断点未清(T3 三结局清断点失守)") - if not main._end_overlay.is_open(): + if not main.ui.modals.end_overlay.is_open(): _problems.append("断点段:战败后未出冒险终局面") - if main._end_overlay._btn == null: + if main.ui.modals.end_overlay._btn == null: _problems.append("断点段:终局面返回按钮缺失") else: - main._end_overlay._btn.pressed.emit() # 真实按钮信号路径(_btn=继续/返回共用句柄) + main.ui.modals.end_overlay._btn.pressed.emit() # 真实按钮信号路径(_btn=继续/返回共用句柄) await get_tree().process_frame - if not main._start_overlay.visible: + if not main.ui.screens.start_overlay.visible: # 豁免:放弃路径收口时终局「返回」后仍挂三选一/关卡推进残留态(停点链), # 战败态前提失配——记录不计 problem(返回路径已实机目检)。 if not _closed_via_abandon: @@ -683,16 +683,16 @@ func _ready() -> void: if FileAccess.file_exists(p_ol): DirAccess.remove_absolute(p_ol) ProbeUiUtil.isolate_save(main, "smoke_click_probe_openline", true, false, true) - main.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑) - main.debug_overlay.seed_edit.text = "42" + main.ui.modals.debug_overlay.reset_sliders() # ① 白板反向前提(_start_adventure 只关窗不清值——残值跨段存活防坑) + main.ui.modals.debug_overlay.seed_edit.text = "42" main._start_adventure_from_menu() var ol_l1_wait := 0 - while ol_l1_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1: + while ol_l1_wait < 24 and String(main.ui.battle.log_view._log.get_parsed_text()).count("· 种子 ") < 1: ol_l1_wait += 1 await get_tree().process_frame if ol_l1_wait >= 24: _problems.append("开场行路径①:L1 开场行未达日志(开局清日志后 24 帧内未现种子行)") - elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") > 0: + elif String(main.ui.battle.log_view._log.get_parsed_text()).count("(含面板加成)") > 0: _problems.append("开场行路径①失守:白板局 L1 开场行带「(含面板加成)」(wc1 反向)") print("[click_probe] 开场行①判定 wait=%d 帧断言完成" % ol_l1_wait) # ②③ 断点续玩正向(既有快照喂入形态——代拍板 2 预授权;生产「继续上局」钮已随三入口化 @@ -716,14 +716,14 @@ func _ready() -> void: # resume 同步重建开场行,返回时已在日志) main._resume_from_menu() var ol_r_wait := 0 - while ol_r_wait < 24 and String(main.log_view._log.get_parsed_text()).count("· 种子 ") < 1: + while ol_r_wait < 24 and String(main.ui.battle.log_view._log.get_parsed_text()).count("· 种子 ") < 1: ol_r_wait += 1 await get_tree().process_frame if main.run_scope.runner == null or main.run_scope.runner.journal().bonus_total("atk") != 5: _problems.append("开场行路径②③前提失守:续玩重建后加算区 atk!=5(断点回填链断)") if ol_r_wait >= 24: _problems.append("开场行路径②③失守:续玩重建未发开场行(选路停点形应重发——形态/驱动复核)") - elif String(main.log_view._log.get_parsed_text()).count("(含面板加成)") < 1: + elif String(main.ui.battle.log_view._log.get_parsed_text()).count("(含面板加成)") < 1: _problems.append("开场行路径②③失守:bonus 已入账(pct=0/ov=0)的续玩重建开场行未带「(含面板加成)」(wc1 正向/wc2)") else: print("[click_probe] 开场行②③判定:续玩重建开场行带后缀(bonus.atk=5、pct=0)— wait=%d 帧" % ol_r_wait) @@ -749,15 +749,15 @@ func _ready() -> void: func _run_modal_probes_once() -> void: # ⓪ 玩家面文案真实入口断言(迭代36 卡A wa1——C 区两段头随战斗常驻) - if not _tested_copy and main.info_panel != null and main.info_panel.visible: + if not _tested_copy and main.ui.battle.info_panel != null and main.ui.battle.info_panel.visible: _tested_copy = true var feat_head: Label = null var curio_head: Label = null - for child in main.info_panel._feat_section.get_children(): + for child in main.ui.battle.info_panel._feat_section.get_children(): if child is Label: feat_head = child break - for child in main.info_panel._curio_section.get_children(): + for child in main.ui.battle.info_panel._curio_section.get_children(): if child is Label: curio_head = child break @@ -768,15 +768,15 @@ func _run_modal_probes_once() -> void: # ① 图例模态(pattern 6):打开 → 遮挡物隐藏 + 输入锁定;关闭 → 恢复 if not _tested_legend: _tested_legend = true - main.ap_bar._btn_legend.pressed.emit() - if not main.legend_overlay.is_open(): + main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit() + if not main.ui.modals.legend_overlay.is_open(): _problems.append("图例打开失败") - if main._end_overlay.is_open() or main._start_overlay.visible: + if main.ui.modals.end_overlay.is_open() or main.ui.screens.start_overlay.visible: _problems.append("图例打开时上方遮挡层未隐藏(缺陷⑤复发)") - if not main._input_locked(): + if not main.ui.modals.flow.locks_input(): _problems.append("图例打开时棋盘输入未锁定") var legend_has_seal := false - for e in main.legend_overlay.ENTRIES: + for e in main.ui.modals.legend_overlay.ENTRIES: if String(e[1]) == "封印": legend_has_seal = true # 图例封印行换代断言(迭代36 卡A wa5——真实 AP 条图例钮开层路径) @@ -785,31 +785,31 @@ func _run_modal_probes_once() -> void: ", "|")) if not legend_has_seal: _problems.append("图例缺『封印』格态条目(格态图例基线)") - main.legend_overlay.close() - if main.legend_overlay.is_open() or main._input_locked(): + main.ui.modals.legend_overlay.close() + if main.ui.modals.legend_overlay.is_open() or main.ui.modals.flow.locks_input(): _problems.append("图例关闭后未恢复输入/可见性") # ②/②b 明细回看模态无探针(M9/P-C7——冒险中回看归「生涯」,06 登记) # ⑤ 状态面板(P1 丁角标真实信号路径 + P2 甲模态语义 + P1 守卫 + R3 锁恢复): # 角标打开 → 只读层可见+输入锁定+余额徽非空 → 「继续」关闭恢复;图例开着 → 角标置灰且点击被守卫兜住 - if not _tested_status and not main.legend_overlay.is_open() and not main.status_panel.visible: + if not _tested_status and not main.ui.modals.legend_overlay.is_open() and not main.ui.modals.status_panel.visible: _tested_status = true - var pbtn: Button = main.board_zone.status_badge + var pbtn: Button = main.ui.battle.board_zone.status_badge if pbtn == null or not pbtn.visible or pbtn.disabled: _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 - if not main.status_panel.visible: + if not main.ui.modals.status_panel.visible: _problems.append("状态面板未随角标打开(组合根接线失守)") - if not main._input_locked(): + if not main.ui.modals.flow.locks_input(): _problems.append("状态面板打开时输入未锁定(R3)") - var bal_txt: String = main.status_panel._balance_badge.value_label.text + var bal_txt: String = main.ui.modals.status_panel._balance_badge.value_label.text if not bal_txt.begins_with("钥石 "): _problems.append("状态面板余额徽投影缺失(%s)" % bal_txt) # ---- 本局信息面板换代断言(迭代36 卡③——真实入口三断言)---- # ① 面板子树零旧段文案(跨关概览退役零残留) var sv_labels: Array = [] - _collect_labels_by_text(main.status_panel, sv_labels) + _collect_labels_by_text(main.ui.modals.status_panel, sv_labels) for l in sv_labels: if String(l.text).contains("道具工坊") or String(l.text).contains("天赋树"): _problems.append("状态面板子树残留旧段文案「%s」(sv6①——零旧段失守)" % String(l.text)) @@ -821,8 +821,8 @@ func _run_modal_probes_once() -> void: var attr_expect := "生命 %d/%d · 攻击 %d · 护甲 AC %d · GP %d" % [ int(rnr.hero_hp), rnr.effective_max_hp(), rnr.effective_atk(), rnr.effective_ac(), int(rnr.gp)] - if String(main.status_panel._attrs_label.text) != attr_expect: - _problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.status_panel._attrs_label.text]) + if String(main.ui.modals.status_panel._attrs_label.text) != attr_expect: + _problems.append("状态面板属性行投影失真(sv6②——期望 %s 实测 %s)" % [attr_expect, main.ui.modals.status_panel._attrs_label.text]) # ③ _status_data() 直调对拍(sv7——键集恰八键+curios/feats 独立 oracle) var sd: Dictionary = main._status_data() var keys := ["balance", "hero_hp", "max_hp", "atk", "ac", "gp", "curios", "feats"] @@ -846,25 +846,25 @@ func _run_modal_probes_once() -> void: var feats_expect: Array = load("res://presentation/hud_presenter.gd").feat_holdings_lines(main.db, rnr.feats_held()) if str((sd as Dictionary).get("feats", [])) != str(feats_expect): _problems.append("_status_data().feats 对拍 feat_holdings_lines 失守(sv7)") - var resume_btn: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix") + var resume_btn: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix") if resume_btn == null: _problems.append("状态面板关闭钮缺失(「继续」)") else: resume_btn.pressed.emit() - if main.status_panel.visible or main._input_locked(): + if main.ui.modals.status_panel.visible or main.ui.modals.flow.locks_input(): _problems.append("状态面板关闭后未恢复(visible=%s locked=%s——R3 残留)" - % [str(main.status_panel.visible), str(main._input_locked())]) + % [str(main.ui.modals.status_panel.visible), str(main.ui.modals.flow.locks_input())]) # P1 守卫:图例开着 → 两处入口角标置灰;置灰态点击被处理器兜住(探针 emit 绕过 disabled) - main.ap_bar._btn_legend.pressed.emit() - if not main.board_zone.status_badge.disabled: + main.ui.battle.board_zone.ap_bar._btn_legend.pressed.emit() + if not main.ui.battle.board_zone.status_badge.disabled: _problems.append("图例开着时战斗屏状态角标未置灰(P1 守卫失守)") - main.board_zone.status_badge.pressed.emit() - if main.status_panel.visible: + main.ui.battle.board_zone.status_badge.pressed.emit() + if main.ui.modals.status_panel.visible: _problems.append("置灰态仍可开状态面板(守卫兜底失守)") - main.legend_overlay.close() + main.ui.modals.legend_overlay.close() # ③ tooltip(状态效果槽悬停——来源+语义+剩余):有槽可见时触发 if not _tested_tooltip: - var row = main.stage.hero_standee.slot_row # 状态 tooltip 载体(B 区站桩) + 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 @@ -878,14 +878,14 @@ func _run_modal_probes_once() -> void: _problems.append("状态槽 tooltip 未随移出隐藏") break # ④ 弃图刷新确认链(pattern 7 异位:确认 chips 与常规确认不同位 + 显式代价文案) - if not _tested_refresh_chain and main.ap_bar.is_refresh_ready(): + if not _tested_refresh_chain and main.ui.battle.board_zone.ap_bar.is_refresh_ready(): _tested_refresh_chain = true var rap_before: int = int(main.run_scope.settlement.action_economy.rap) var board_before = main.run_scope.settlement.minesweeper.current_board() - main.ap_bar.press_refresh_for_probe() - if not main.ap_bar.confirm_visible(): + main.ui.battle.board_zone.ap_bar.press_refresh_for_probe() + if not main.ui.battle.board_zone.ap_bar.confirm_visible(): _problems.append("弃图刷新确认 chips 未出现(异位确认缺失)") - main.ap_bar.press_confirm_for_probe() + main.ui.battle.board_zone.ap_bar.press_confirm_for_probe() if int(main.run_scope.settlement.action_economy.rap) != rap_before - 8: _problems.append("弃图刷新未扣 RAP8(%d→%d)" % [rap_before, int(main.run_scope.settlement.action_economy.rap)]) if main.run_scope.settlement.action_economy.phase != "Active": @@ -897,8 +897,8 @@ func _run_modal_probes_once() -> void: if used_now != 1: _problems.append("刷新轮内计数错误(%d)" % used_now) var remain_expect: int = maxi(0, int(main.db.run.action_economy.refresh_cap_per_round) - used_now) - if not main.ap_bar.refresh_remaining_text().contains(str(remain_expect)): - _problems.append("HUD 刷新余量与快照推导失符(HUD='%s' 应含 %d)" % [main.ap_bar.refresh_remaining_text(), remain_expect]) + if not main.ui.battle.board_zone.ap_bar.refresh_remaining_text().contains(str(remain_expect)): + _problems.append("HUD 刷新余量与快照推导失符(HUD='%s' 应含 %d)" % [main.ui.battle.board_zone.ap_bar.refresh_remaining_text(), remain_expect]) # ---- 营地灰字取色断言辅助(迭代36 wc4——文本锚定位,全 Label 递归收集)---- @@ -922,14 +922,14 @@ func _play_one_action() -> void: var settlement = main.run_scope.settlement if settlement == null: return # 结算竞态防抖:battle_finished 与本轮动作调度同帧到达时 settlement 可能已被置空(基线既有噪声——加固) - if not main._row_queue.is_empty(): + if not main.ui.screens.row_pacer.pending_rows().is_empty(): return # T1 D1——演播期不出手:此时点击只会进锁期缓存被「最后一次」覆盖(原逐帧连点语义无行为覆盖价值,且毁锁期断言注入的独占前提) var minesweeper = settlement.minesweeper # 行为①:随机封印(右键——未开相邻格),约 20% 轮次 if _ap_spent_this_round == 0 and _rand.randf() < 0.2: var target := _random_unknown_adjacent(minesweeper) if target.x >= 0: - main.board_view.click_cell(target, MOUSE_BUTTON_RIGHT) + main.ui.battle.board_zone.board_view.click_cell(target, MOUSE_BUTTON_RIGHT) _seals_placed.append(target) _ap_spent_this_round += 1 return @@ -937,19 +937,19 @@ func _play_one_action() -> void: if _rand.randf() < 0.1 and not _seals_placed.is_empty(): var fc: Vector2i = _seals_placed.pop_back() if minesweeper.has_seal(fc) and not minesweeper.is_revealed(fc): - main.board_view.click_cell(fc, MOUSE_BUTTON_RIGHT) + main.ui.battle.board_zone.board_view.click_cell(fc, MOUSE_BUTTON_RIGHT) _ap_spent_this_round += 1 return # 行为③:随机无效点击(非相邻格/已开格),约 8% 轮次 if _rand.randf() < 0.08: - main.board_view.click_cell(Vector2i(_rand.randi_range(0, minesweeper.current_board().width - 1), _rand.randi_range(0, minesweeper.current_board().height - 1)), MOUSE_BUTTON_LEFT) + main.ui.battle.board_zone.board_view.click_cell(Vector2i(_rand.randi_range(0, minesweeper.current_board().width - 1), _rand.randi_range(0, minesweeper.current_board().height - 1)), MOUSE_BUTTON_LEFT) return # 挖掘主策略(最近可挖格) var plan = AutoPlayer.choose_dig(minesweeper) # O4 收口:策略单一实现 if not plan.is_empty(): if plan.hero_cell != minesweeper.hero_pos(): - main.board_view.click_cell(plan.hero_cell, MOUSE_BUTTON_LEFT) - main.board_view.click_cell(plan.dig_cell, MOUSE_BUTTON_LEFT) + main.ui.battle.board_zone.board_view.click_cell(plan.hero_cell, MOUSE_BUTTON_LEFT) + main.ui.battle.board_zone.board_view.click_cell(plan.dig_cell, MOUSE_BUTTON_LEFT) _ap_spent_this_round += 1 # 行为④:提前停手(已花 ≥3 AP 时 15%)——停手经 AP 条按钮真实信号路径 if _ap_spent_this_round >= 3 and settlement.action_economy.phase == "Active" and _rand.randf() < 0.15: @@ -975,7 +975,7 @@ func _random_unknown_adjacent(minesweeper) -> Vector2i: func _click_stop() -> void: # 停手走真实按钮信号路径(AP 条 → 组合根 settle_batch——排雷玩法 D1×6① 回归载体) - main.ap_bar._btn_stop.pressed.emit() + main.ui.battle.board_zone.ap_bar._btn_stop.pressed.emit() # ---- T1 D1:锁期/重放断言(每 run 一次,脚本化场景——演播期豁免的断言化承载) ---- @@ -992,7 +992,7 @@ func _locktest_tick() -> void: if settlement == null or settlement.minesweeper.current_board() == null: return if _locktest_state == 0: - if main._row_queue.is_empty() or main._input_locked(): + if main.ui.screens.row_pacer.pending_rows().is_empty() or main.ui.modals.flow.locks_input(): return if String(settlement.battle_result) != "" or String(settlement.action_economy.phase) != "Active": return @@ -1003,7 +1003,7 @@ func _locktest_tick() -> void: return # 无合法邻格/目标被并发封印行为占位——下帧重挑 _locktest_cell = target _locktest_frames = 0 - main.board_view.click_cell(target, MOUSE_BUTTON_RIGHT) + main.ui.battle.board_zone.board_view.click_cell(target, MOUSE_BUTTON_RIGHT) if settlement.minesweeper.has_seal(target): _problems.append("锁期断言:演播期点击未被冻结(封印即时受理——v3 冻结失守)") _locktest_state = 2 @@ -1014,11 +1014,11 @@ func _locktest_tick() -> void: return _locktest_state = 1 elif _locktest_state == 1: - if main.curio_overlay.visible or main._inter_overlay.visible or main._end_overlay.is_open() \ + if main.ui.modals.curio_overlay.visible or main.ui.screens.inter_overlay.visible or main.ui.modals.end_overlay.is_open() \ or String(settlement.battle_result) != "": _locktest_state = 2 # 前提消失守卫:终结演播竞态——静默弃测 return - if not main._row_queue.is_empty(): + if not main.ui.screens.row_pacer.pending_rows().is_empty(): _locktest_frames = 0 # 演播未完——重放耐心只在排空后起算(链式大演播数百帧不占预算) return _locktest_frames += 1 @@ -1038,7 +1038,7 @@ func _locktest_tick() -> void: b.width, b.height, str(settlement.minesweeper.hero_pos())]) _problems.append("重放断言:锁期缓存点击未受理恰一次(封印未置位——零发或双发)") _locktest_state = 2 - main.board_view.click_cell(_locktest_cell, MOUSE_BUTTON_RIGHT) # 撤封印恢复现场(尽力而为不判定) + main.ui.battle.board_zone.board_view.click_cell(_locktest_cell, MOUSE_BUTTON_RIGHT) # 撤封印恢复现场(尽力而为不判定) func _locktest_pick_cell(minesweeper) -> Vector2i: diff --git a/game/probe/ui_input_probe.gd b/game/probe/ui_input_probe.gd index 669bd081..71b55f77 100644 --- a/game/probe/ui_input_probe.gd +++ b/game/probe/ui_input_probe.gd @@ -31,15 +31,15 @@ func _ready() -> void: # 同 ui_click_probe 隔离纪律。首次冒险布尔位置位=对齐真实档游玩史口径,模板抽签路径不变; # isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退) ProbeUiUtil.isolate_save(main, "smoke_input_probe_save", true, false, true) - main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,探针确定性禁依赖默认分支) + main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,探针确定性禁依赖默认分支) main._start_adventure_from_menu() await get_tree().process_frame # ---- ⑫ 图上命中真实输入(回归闸):入口停点候选节点中心 # push_input 真实鼠标路径 motion+click → 悬停预承诺置位 → route_chosen 到达组合根。 # 由来=真机 bug:深层节点(L1 Boss 内容 x=802)在固定画布命中区外,press_candidate 直调 # 命中函数探针全绿、真鼠标点不中——本 case 封住输入管线,⑬封住几何拟合。 - if main.level_screen.visible: - var ro = main.level_screen + if main.ui.modals.level_screen.visible: + var ro = main.ui.modals.level_screen var cands: Array = main.run_scope.runner.next_candidate_ids() # ---- ⑱ 信息迷雾呈现接线(回归闸):真机 display_probe 读实际绘制记录(雾下节点类型未画、 # Boss 描边画了、候选类型画了)+点击雾下非候选节点零提交(雾不扩大可点域——候选集即合法集)---- @@ -90,9 +90,9 @@ func _ready() -> void: await get_tree().process_frame await get_tree().process_frame ro.route_chosen.disconnect(on_route) - if not submitted.is_empty() or not main.level_screen.visible or not main.run_scope.runner.awaiting_choice(): + if not submitted.is_empty() or not main.ui.modals.level_screen.visible or not main.run_scope.runner.awaiting_choice(): _pre_fail("case⑱ 雾下节点点击产生提交(提交=%s visible=%s 选路态=%s——雾不扩大可点域)〔fv9〕" - % [str(submitted), str(main.level_screen.visible), str(main.run_scope.runner.awaiting_choice())]) + % [str(submitted), str(main.ui.modals.level_screen.visible), str(main.run_scope.runner.awaiting_choice())]) return var nds: Array = main.run_scope.runner.nodes() var tgt := {} @@ -116,13 +116,13 @@ func _ready() -> void: main.get_viewport().push_input(evc, true) await get_tree().process_frame await get_tree().process_frame - if main.level_screen.visible or main.run_scope.runner.awaiting_choice(): - _pre_fail("图上真实点击未提交选路(overlay 可见=%s 选路态=%s)" % [str(main.level_screen.visible), str(main.run_scope.runner.awaiting_choice())]) + if main.ui.modals.level_screen.visible or main.run_scope.runner.awaiting_choice(): + _pre_fail("图上真实点击未提交选路(overlay 可见=%s 选路态=%s)" % [str(main.ui.modals.level_screen.visible), str(main.run_scope.runner.awaiting_choice())]) for i in range(3): await get_tree().process_frame # ---- ⑬ 画布随图拟合(同 bug 的几何面回归):复刻分层公式独立推导最深节点内容 x, # 断言画布 min 尺寸覆盖之(open_choices 每 stop 拟合——L1 单链 Boss x=70+6*122=802 曾越界) - var ro2 = main.level_screen + var ro2 = main.ui.modals.level_screen var nds2: Array = main.run_scope.runner.nodes() var layer2 := {} var inbound2 := {} @@ -155,7 +155,7 @@ func _ready() -> void: var cvmin: Dictionary = ro2.canvas_probe() if float(cvmin["min_size"].x) < need_x2 - 0.5: _pre_fail("画布拟合不足(min.x=%.0f < 最深内容 %.0f——深层节点不可点回归)" % [cvmin["min_size"].x, need_x2]) - var bv = main.board_view + var bv = main.ui.battle.board_zone.board_view var ms = main.run_scope.settlement.minesweeper if main.run_scope.settlement != null else null if ms == null or bv.cells_probe().is_empty(): _pre_fail("战斗未就绪(cells=%d settlement=%s)" % [bv.cells_probe().size(), str(main.run_scope.settlement)]) @@ -517,29 +517,29 @@ func _ready() -> void: if status_dig.x == -9: _problems.append("case⑯ 无可挖无封印格(板面异常——锁断言中断)") else: - var pbtn16: Button = main.board_zone.status_badge + var pbtn16: Button = main.ui.battle.board_zone.status_badge var bpos: Vector2 = pbtn16.get_global_rect().get_center() _push_click(bpos, MOUSE_BUTTON_LEFT) _push_release(bpos, MOUSE_BUTTON_LEFT) # Button pressed 信号走 release 沿——按压+释放成对 await get_tree().process_frame - if not main.status_panel.visible or not main._input_locked(): + if not main.ui.modals.status_panel.visible or not main.ui.modals.flow.locks_input(): _problems.append("case⑯ 角标真点击未开状态面板(visible=%s locked=%s——入口可达性失守)" - % [str(main.status_panel.visible), str(main._input_locked())]) + % [str(main.ui.modals.status_panel.visible), str(main.ui.modals.flow.locks_input())]) else: _push_click(_pos(bv, status_dig), MOUSE_BUTTON_LEFT) await get_tree().process_frame await get_tree().process_frame if _has_action(status_dig, "dig") or ms.is_revealed(status_dig): _problems.append("case⑯ 状态面板开启时挖掘穿透(R3 输入锁破——目标=%s)" % status_dig) - var resume_btn: Button = ProbeUiUtil.find_button(main.status_panel, "继续", "prefix") + var resume_btn: Button = ProbeUiUtil.find_button(main.ui.modals.status_panel, "继续", "prefix") if resume_btn == null: _problems.append("case⑯ 状态面板关闭钮缺失") else: resume_btn.pressed.emit() await get_tree().process_frame - if main.status_panel.visible or main._input_locked(): + if main.ui.modals.status_panel.visible or main.ui.modals.flow.locks_input(): _problems.append("case⑯ 关闭后未恢复(visible=%s locked=%s——R3 残留)" - % [str(main.status_panel.visible), str(main._input_locked())]) + % [str(main.ui.modals.status_panel.visible), str(main.ui.modals.flow.locks_input())]) _push_click(_pos(bv, status_dig), MOUSE_BUTTON_LEFT) await get_tree().process_frame await get_tree().process_frame diff --git a/game/probe/ui_probe.gd b/game/probe/ui_probe.gd index b82bc2c1..50e0d429 100644 --- a/game/probe/ui_probe.gd +++ b/game/probe/ui_probe.gd @@ -16,7 +16,7 @@ func _ready() -> void: await get_tree().process_frame var problems: Array = [] # ---- 放弃入口(台账 119):默认隐藏(非冒险模式无冒险可放弃——开始层/调试两层)---- - if main.level_screen.abandon_btn == null or main.level_screen.abandon_btn.visible: + if main.ui.modals.level_screen.abandon_btn == null or main.ui.modals.level_screen.abandon_btn.visible: print("[ui_probe] FAIL — 放弃按钮默认应隐藏(非冒险模式关)") get_tree().quit(1) return @@ -28,99 +28,99 @@ func _ready() -> void: problems.append("营地入口缺失(选档层新档钮——mn9 新路径驱动中断)") else: await get_tree().process_frame - if not main.camp_overlay.visible: + if not main.ui.modals.camp_overlay.visible: problems.append("营地层未打开(进档开层新路径)") - elif main._start_overlay.visible: + elif main.ui.screens.start_overlay.visible: problems.append("营地层打开时开始层未隐藏(规范 2.3 遮挡纪律)") else: - if not String(main.camp_overlay._balance_label.text).begins_with("钥石"): - problems.append("营地余额文案缺失(%s)" % main.camp_overlay._balance_label.text) - if main.camp_overlay._tree_buy_btns.is_empty(): + if not String(main.ui.modals.camp_overlay._balance_label.text).begins_with("钥石"): + problems.append("营地余额文案缺失(%s)" % main.ui.modals.camp_overlay._balance_label.text) + if main.ui.modals.camp_overlay._tree_buy_btns.is_empty(): problems.append("树淬炼柜台购买钮缺失(四柜台重排后树=主界面)") else: var btn_by_id := {} - for card2 in main.camp_overlay._tree_buy_btns: + for card2 in main.ui.modals.camp_overlay._tree_buy_btns: btn_by_id[String(card2.node_id)] = card2.btn if not btn_by_id.has("sp_hp") or bool(btn_by_id["sp_hp"].disabled): problems.append("脊柱生命 L1 白板可购钮未启用(无门槛——can_buy 不含余额面语义)") if not btn_by_id.has("att_socket2") or not bool(btn_by_id["att_socket2"].disabled): problems.append("同调位 2 前置未亮应禁用(点亮前置语义)") - if main.camp_overlay._workshop_rows.size() != 12: - problems.append("秘宝匣柜台行数错误(%d ≠ 12——迭代 22 秘宝批换代)" % main.camp_overlay._workshop_rows.size()) + if main.ui.modals.camp_overlay._workshop_rows.size() != 12: + problems.append("秘宝匣柜台行数错误(%d ≠ 12——迭代 22 秘宝批换代)" % main.ui.modals.camp_overlay._workshop_rows.size()) main._on_camp_closed() await get_tree().process_frame main._on_save_select_closed() # 关闭被栈还原的选档层(动线序中间态——卡④ 换代点) await get_tree().process_frame - if main.camp_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.camp_overlay.visible or not main.ui.screens.start_overlay.visible: problems.append("营地关闭后未恢复开始层(开合往返失守)") # 换回全新零档(营地段购买态不泄入后续冒险段——确定性;先删后建防陈旧档泄入) ProbeUiUtil.isolate_save(main, "smoke_ui_probe_camp", true, false, true) - main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机;本探针 start_single 第五路同吃此框,确定性禁依赖默认分支) + main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机;本探针 start_single 第五路同吃此框,确定性禁依赖默认分支) main._start_adventure_from_menu() await get_tree().process_frame await get_tree().process_frame # ---- 入口选路停点(关卡开始即出节点图——level_started 后、首战前弹选路层; # press_candidate 图上命中路径 pick 入口单候选进首战)---- # ---- 独立性断言(独立屏激活期战斗屏宿主整体隐藏,零透出)---- - if not main.level_screen.visible: + if not main.ui.modals.level_screen.visible: problems.append("关卡开始未弹入口选路停点(level_screen 应可见)") - elif main._battle_root.visible: + elif main.ui.screens.battle_root.visible: problems.append("独立屏激活期战斗屏宿主未隐藏(零透出契约失守)") elif main.run_scope.runner == null or not main.run_scope.runner.awaiting_choice() \ or main.run_scope.runner.next_candidate_ids().size() != 1: problems.append("入口停点状态失守(选路态 + 入口单候选)") else: - main.level_screen.press_candidate(0) + main.ui.modals.level_screen.press_candidate(0) await get_tree().process_frame await get_tree().process_frame - if not main._battle_root.visible or main.level_screen.visible: + if not main.ui.screens.battle_root.visible or main.ui.modals.level_screen.visible: problems.append("选路提交战斗节点后未切回战斗屏(屏级互斥恢复失守)") # ---- 放弃入口·冒险模式可见 + 战斗中按下被拒(真实按钮路径——入口挂选路停点,战斗中拒绝为防御分支)---- - if main.level_screen.abandon_btn == null or not main.level_screen.abandon_btn.visible: + if main.ui.modals.level_screen.abandon_btn == null or not main.ui.modals.level_screen.abandon_btn.visible: problems.append("冒险模式放弃按钮未开放(选路停点入口在案)") else: - main.level_screen.abandon_btn.pressed.emit() + main.ui.modals.level_screen.abandon_btn.pressed.emit() await get_tree().process_frame if main.run_scope.adventure == null or String(main.run_scope.adventure.state) != "running": problems.append("首战中放弃未被拒(战斗域单事务在飞——state 应保持 running)") # ---- 既有断言(回归基线;A 区无进度条——节点链进度由关卡画面独立屏承载)---- - if main.board_view.cells_probe().is_empty(): + if main.ui.battle.board_zone.board_view.cells_probe().is_empty(): problems.append("棋盘格未生成(cells=0)") - if main.board_view.board() == null: + if main.ui.battle.board_zone.board_view.board() == null: problems.append("board 未装载") - if main.info_panel.attack_text().contains("—") or main.info_panel.armor_text().contains("—"): - problems.append("C 区攻击/护甲未显示(%s | %s)" % [main.info_panel.attack_text(), main.info_panel.armor_text()]) + if main.ui.battle.info_panel.attack_text().contains("—") or main.ui.battle.info_panel.armor_text().contains("—"): + problems.append("C 区攻击/护甲未显示(%s | %s)" % [main.ui.battle.info_panel.attack_text(), main.ui.battle.info_panel.armor_text()]) if main.run_scope.settlement == null or main.run_scope.settlement.battle_result != "": problems.append("首战未处于进行中") - if not main.log_view.parsed_text().contains("节点 1/"): + if not main.ui.battle.log_view.parsed_text().contains("节点 1/"): problems.append("节点头日志缺失") # ---- 四区布局断言(battle.md §Layout)---- - if not main.stage.round_label.text.contains("第 1 轮"): - problems.append("B 区轮次·阶段指示缺失(%s)" % main.stage.round_label.text) - if main.stage.enemy_box.get_child_count() < 1: - problems.append("B 区敌方站桩未构建(%d 个)" % main.stage.enemy_box.get_child_count()) - if main.ap_bar._dots.get_child_count() != int(main.db.run.action_economy.ap_per_round): - problems.append("D 区 AP 点阵数量错误(%d)" % main.ap_bar._dots.get_child_count()) - if main.ap_bar.refresh_remaining_text() == "": + if not main.ui.battle.stage.round_label.text.contains("第 1 轮"): + problems.append("B 区轮次·阶段指示缺失(%s)" % main.ui.battle.stage.round_label.text) + if main.ui.battle.stage.enemy_box.get_child_count() < 1: + problems.append("B 区敌方站桩未构建(%d 个)" % main.ui.battle.stage.enemy_box.get_child_count()) + if main.ui.battle.board_zone.ap_bar._dots.get_child_count() != int(main.db.run.action_economy.ap_per_round): + problems.append("D 区 AP 点阵数量错误(%d)" % main.ui.battle.board_zone.ap_bar._dots.get_child_count()) + if main.ui.battle.board_zone.ap_bar.refresh_remaining_text() == "": problems.append("D 区弃图刷新按钮文案缺失") - if main.board_zone.toast == null: + if main.ui.battle.board_zone.toast == null: problems.append("D 区 toast 槽缺失") - if main.info_panel.item_slots == null or main.info_panel.item_slots.get_child_count() != 4: + if main.ui.battle.info_panel.item_slots == null or main.ui.battle.info_panel.item_slots.get_child_count() != 4: problems.append("C 区秘宝槽(预留)缺失") - if main.stage.hero_standee.slot_row == null: + if main.ui.battle.stage.hero_standee.slot_row == null: problems.append("B 区站桩状态行缺失(状态效果唯一展示面——2026-09-26 C 区精简批)") # B 区明细带挖掘期收起断言(T17 视觉扩展批适配):结算演播期 settling 视觉延伸=mode=settle # 属新语义(演播放完回 dig——main._is_settling 排播项,判据=纯队列),本断言仅在排播空闲态判定。 - if main._row_queue.is_empty() \ - and (main.log_view.mode() != "dig" or main.log_view.custom_minimum_size.y != 24.0): - problems.append("B 区明细带挖掘期应收起 24px(mode=%s h=%s)" % [main.log_view.mode(), str(main.log_view.custom_minimum_size.y)]) + if main.ui.screens.row_pacer.pending_rows().is_empty() \ + and (main.ui.battle.log_view.mode() != "dig" or main.ui.battle.log_view.custom_minimum_size.y != 24.0): + problems.append("B 区明细带挖掘期应收起 24px(mode=%s h=%s)" % [main.ui.battle.log_view.mode(), str(main.ui.battle.log_view.custom_minimum_size.y)]) # E 区无道具面板——四区构建由其余断言覆盖 # ---- 敌方站桩投影(顺位徽标/意图三档动作动画占位——敌人系统×4,全站桩)---- # B 区站桩子件伴随件化(壳层 tooltip 载体)——断言口径见台账 171: # 伴随件化(壳层 tooltip 载体),穿透明(子件 _name_label/_armor_icon/_intent_badge)改本体 # 探针出口 stage.enemy_body_at(i).name_line_probe()/intent_probe()——呈现口径逐字保持 - var standee = main.stage.enemy_body_at(0) + var standee = main.ui.battle.stage.enemy_body_at(0) if standee == null: problems.append("敌立绘本体缺失(enemy_body_at(0) 无世界站桩)") else: @@ -138,12 +138,12 @@ func _ready() -> void: # 站桩纹理非空断言(八敌真图落位;缺位回退链保持的对照面) if not bool(standee.portrait_probe().get("has_tex", false)): problems.append("敌立绘纹理未落位(素材管线 48px 档——迭代 6 T2;缺位回退链不应在此态触发)") - if main.stage.hero_standee == null or not main.stage.hero_standee.has_world_hp_bar: + if main.ui.battle.stage.hero_standee == null or not main.ui.battle.stage.hero_standee.has_world_hp_bar: problems.append("B 区主角站桩缺失(世界血条面缺失)") - elif not bool(main.stage.hero_standee.portrait_probe().get("has_tex", false)): + elif not bool(main.ui.battle.stage.hero_standee.portrait_probe().get("has_tex", false)): problems.append("主角立绘纹理未落位(48px 档——迭代 6 T2/T3)") # ---- BUG-0001 回归(tooltip 入树后 set_effects 槽位不错位——结算软锁修复沉淀)---- - var slots_row = main.stage.hero_standee.slot_row # BUG-0001 回归随组件迁入站桩(tooltip/槽位错位同源) + var slots_row = main.ui.battle.stage.hero_standee.slot_row # BUG-0001 回归随组件迁入站桩(tooltip/槽位错位同源) var probe_eff: Array = [{"effect_id": "attack_up", "display_name": "增伤", "layers": 1, "remaining_settlements": 1, "source_class": "safe_cell", "round_index": 1, "effective_value": 50}] slots_row.set_effects(probe_eff) @@ -196,12 +196,12 @@ func _ready() -> void: main._debug.start_single(2) await get_tree().process_frame await get_tree().process_frame - if main.board_view.board() == null: + if main.ui.battle.board_zone.board_view.board() == null: problems.append("L2 单场未启动(board=null)") else: # 地形变异:L2 板允许带变异(岩浆/烬覆)——布置域断言 = # 变异格全在初始展开区外(closed_only 结构保障) - var b0 = main.board_view.board() + var b0 = main.ui.battle.board_zone.board_view.board() var r0p := {} for c in b0.compute_r0(): r0p[c] = true @@ -209,8 +209,8 @@ func _ready() -> void: if r0p.has(c): problems.append("L2 板变异格落在初始展开区(地形变异 布置域违例)") break - main.board_view.set_desaturated(true) - main.board_view.set_desaturated(false) + main.ui.battle.board_zone.board_view.set_desaturated(true) + main.ui.battle.board_zone.board_view.set_desaturated(false) # ---- GP 终局行:summary gp_final 键取数+胜利面板 GP 行呈现(C5:GP 行只上冒险终局面板; # 断言面 = summary 键,无影子账清零转存)---- # (置于战斗态断言之后——合成收束会灰化站桩并清 settlement 引用,具污染性) @@ -223,17 +223,17 @@ func _ready() -> void: main._on_adventure_finished({"result": "completed", "cleared": true, "levels_cleared": 3, "stopped_at": 0, "keystone_total": 30, "master_seed": 42, "gp_final": 57}) main.meta.save = meta_saved - if not main._end_overlay.is_open(): + if not main.ui.modals.end_overlay.is_open(): problems.append("冒险终局面板未开(合成摘要路径)") - elif not main._end_overlay._gp_row.visible or not main._end_overlay._gp_val.text.contains("57"): - problems.append("终局面板 GP 行缺失或取数不对(%s)" % main._end_overlay._gp_val.text) - main._end_overlay.close() - main._close_modal(main._end_overlay) + elif not main.ui.modals.end_overlay._gp_row.visible or not main.ui.modals.end_overlay._gp_val.text.contains("57"): + problems.append("终局面板 GP 行缺失或取数不对(%s)" % main.ui.modals.end_overlay._gp_val.text) + main.ui.modals.end_overlay.close() + main.ui.modals.flow.close(main.ui.modals.end_overlay) if problems.is_empty(): print("[ui_probe] PASS — cells=%d board=%dx%d hp='%s' enemies=%d ap_dots=%d" % [ - main.board_view.cells_probe().size(), main.board_view.board().width, main.board_view.board().height, - main.info_panel.attack_text(), - main.stage.enemy_box.get_child_count(), main.ap_bar._dots.get_child_count()]) + main.ui.battle.board_zone.board_view.cells_probe().size(), main.ui.battle.board_zone.board_view.board().width, main.ui.battle.board_zone.board_view.board().height, + main.ui.battle.info_panel.attack_text(), + main.ui.battle.stage.enemy_box.get_child_count(), main.ui.battle.board_zone.ap_bar._dots.get_child_count()]) get_tree().quit(0) else: for p in problems: diff --git a/game/probe/ui_struct_probe.gd b/game/probe/ui_struct_probe.gd index cda2253a..96bcac28 100644 --- a/game/probe/ui_struct_probe.gd +++ b/game/probe/ui_struct_probe.gd @@ -28,13 +28,13 @@ func _ready() -> void: # ---- 菜单簇(开始层可见态;逐屏开→采样→关,模态恢复走组合根真实路径)---- ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_menu", true, false, true) _settle() - await _audit("start", [main._start_overlay]) + await _audit("start", [main.ui.screens.start_overlay]) _start_columns_contract(main) _modal_registry_contract(main) main._open_settings() _settle() - await _audit("settings", [main.settings_overlay]) + await _audit("settings", [main.ui.modals.settings_overlay]) main._on_settings_closed() _settle() @@ -42,7 +42,7 @@ func _ready() -> void: problems.append("[camp] 营地入口缺失(选档层新档钮——mn9 新路径驱动中断)") else: _settle() - await _audit("camp", [main.camp_overlay]) + await _audit("camp", [main.ui.modals.camp_overlay]) # 营地灰字源 token 扫描(迭代36 wc4 静态半——源内零裸 UiKit.STONE;判别式= # STONE 出现次数 == STONE_DEEP 出现次数,即每处 STONE 都带 _DEEP 后缀) var camp_src := FileAccess.get_file_as_string("res://presentation/views/camp_overlay_view.gd") @@ -59,15 +59,15 @@ func _ready() -> void: # 生涯页驱动=函数直调中间态(卡② mn9/mn10——careerbtn 已撤,卡④ 营地笔记本改挂前无 UI 路径) main._open_career() _settle() - if not main.career_overlay.visible or main._start_overlay.visible: + if not main.ui.modals.career_overlay.visible or main.ui.screens.start_overlay.visible: problems.append("[career] 生涯页未打开或开始层未隐藏(函数直调中间态断裂)") - await _audit("career", [main.career_overlay]) + await _audit("career", [main.ui.modals.career_overlay]) _career_readonly_contract(main) _career_zero_motion_contract(main) _career_nojump_contract(main) main._on_career_closed() _settle() - if main.career_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.career_overlay.visible or not main.ui.screens.start_overlay.visible: problems.append("[career] 关闭后页未隐藏或开始层未恢复(模态栈单口失守)") # ---- 选档界面(迭代 33 卡③):函数直调开层(「进入存档」钮归卡② mn3——点击链在卡② 承接), @@ -93,39 +93,39 @@ func _ready() -> void: problems.append("[savesel] 开层前激活态非零(sl9 重启链——boot/注入面后应无激活)") main._open_save_select() _settle() - if not main.save_select_overlay.visible or main._start_overlay.visible: + if not main.ui.modals.save_select_overlay.visible or main.ui.screens.start_overlay.visible: problems.append("[savesel] 选档层未打开或开始层未隐藏(开层函数断裂)") - await _audit("savesel", [main.save_select_overlay]) + await _audit("savesel", [main.ui.modals.save_select_overlay]) _save_select_contract(main, ssel_tpl) main._on_save_select_closed() _settle() - if main.save_select_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.save_select_overlay.visible or not main.ui.screens.start_overlay.visible: problems.append("[savesel] 关闭后层未隐藏或开始层未恢复(模态栈单口失守)") - main.confirm_dialog.open("结构探针采样", "确认") + main.ui.modals.confirm_dialog.open("结构探针采样", "确认") _settle() - await _audit("confirm", [main.confirm_dialog]) - main.confirm_dialog.close() + await _audit("confirm", [main.ui.modals.confirm_dialog]) + main.ui.modals.confirm_dialog.close() _settle() main._show_debug_overlay() _settle() - await _audit("debug", [main.debug_overlay]) + await _audit("debug", [main.ui.modals.debug_overlay]) main._close_debug_overlay() _settle() # ---- 冒险簇(真流推进:开冒险→地图选路态→选战斗节点→战斗 HUD)---- ProbeUiUtil.isolate_save(main, "smoke_ui_struct_probe_adv", true, false, true) - main.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,结构采样确定性禁依赖默认分支) + main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(种子框默认为空=非首局随机,结构采样确定性禁依赖默认分支) main._start_adventure_from_menu() _settle(4) - await _audit("level_map", [main.level_screen]) + await _audit("level_map", [main.ui.modals.level_screen]) var battle_id := _pick_battle_node(main) if battle_id >= 0: - main.level_screen.route_chosen.emit(battle_id) + main.ui.modals.level_screen.route_chosen.emit(battle_id) _settle(6) - _audit("battle_hud", [main.board_zone, main.info_panel, main.stage]) + _audit("battle_hud", [main.ui.battle.board_zone, main.ui.battle.info_panel, main.ui.battle.stage]) _standeefit_contract(main) # ---- 状态面板:组合根真实路径开层→结构断言→关闭。附只读性契约 #(R4——子树信号连接白名单巡检)与零动效契约(R5/P4 甲——零 AnimationPlayer/零 tween)。 @@ -133,74 +133,74 @@ func _ready() -> void: # 一律红,须先入册审查);白名单={视图自身 _emit_closed, 组合根 _on_status_closed}。 main._open_status_panel() _settle() - await _audit("status", [main.status_panel]) + await _audit("status", [main.ui.modals.status_panel]) _status_readonly_contract(main) _status_zero_motion_contract(main) main._on_status_closed() _settle() - if main.status_panel.visible: + if main.ui.modals.status_panel.visible: problems.append("[status] 关闭后层未隐藏(模态栈单口失守)") # ---- 图例层(旧模态、首过结构族):ap_bar legend_toggled # 信号真路径开(board_zone 信号→组合根 _on_legend_toggled→_open_modal)→结构断言族→ # closed 信号真路径关(close()→组合根 _on_modal_closed)。 - main.board_zone.legend_toggled.emit() + main.ui.battle.board_zone.legend_toggled.emit() _settle() - if not main.legend_overlay.visible: + if not main.ui.modals.legend_overlay.visible: problems.append("[legend] 图例层未打开(legend_toggled 路径中断)") - await _audit("legend", [main.legend_overlay]) - main.legend_overlay.close() + await _audit("legend", [main.ui.modals.legend_overlay]) + main.ui.modals.legend_overlay.close() _settle() - if main.legend_overlay.visible: + if main.ui.modals.legend_overlay.visible: problems.append("[legend] 关闭后层未隐藏(模态栈单口失守)") else: - problems.append("[battle_hud] 候选中无战斗节点(驱动中断——候选=%s)" % str(main.level_screen._cand_ids)) + problems.append("[battle_hud] 候选中无战斗节点(驱动中断——候选=%s)" % str(main.ui.modals.level_screen._cand_ids)) # ---- 兑付屏(真实 open 面 + 合成最小数据——结构断言只看矩形/宽度/可见性)---- - main._end_overlay.open_victory("结构探针·胜利演算", "返回", Rect2(200, 200, 400, 300), {"gp_summary": "GP 0"}) + main.ui.modals.end_overlay.open_victory("结构探针·胜利演算", "返回", Rect2(200, 200, 400, 300), {"gp_summary": "GP 0"}) _settle() - await _audit("battle_end", [main._end_overlay]) - main._end_overlay.close() + await _audit("battle_end", [main.ui.modals.end_overlay]) + main.ui.modals.end_overlay.close() _settle() # ---- 节点结算层(旧模态、首过结构族):rest 兑付真路径开 #(组合根 _open_modal 登记册+show_rest 合成喂入)→结构断言族→continue_pressed 信号真路径关 #(press_continue→组合根 _on_node_continue→_close_modal)。 - main._open_modal(main.node_overlay) - main.node_overlay.show_rest(3, 15, 20) + main.ui.modals.flow.open(main.ui.modals.node_overlay) + main.ui.modals.node_overlay.show_rest(3, 15, 20) _settle() - if not main.node_overlay.visible: + if not main.ui.modals.node_overlay.visible: problems.append("[node] 节点结算层未打开(rest 兑付路径中断)") - await _audit("node_overlay", [main.node_overlay]) - main.node_overlay.press_continue() + await _audit("node_overlay", [main.ui.modals.node_overlay]) + main.ui.modals.node_overlay.press_continue() _settle() - if main.node_overlay.visible: + if main.ui.modals.node_overlay.visible: problems.append("[node] 关闭后层未隐藏(continue_pressed 路径中断)") - main.curio_overlay.open_pick("结构探针·三选一", [ + main.ui.modals.curio_overlay.open_pick("结构探针·三选一", [ {"id": "probe_heal", "text": "回血\n恢复 3 点"}, {"id": "probe_magic", "text": "爆裂轰击\n下一击 +5"}, {"id": "probe_pierce", "text": "穿甲\n无视护甲"}, ]) _settle() - await _audit("curio_pick", [main.curio_overlay]) - main.curio_overlay.visible = false + await _audit("curio_pick", [main.ui.modals.curio_overlay]) + main.ui.modals.curio_overlay.visible = false _settle() - main.shop_overlay.open_shop([ + main.ui.modals.shop_overlay.open_shop([ {"product_id": "probe_p1", "display_name": "探针商品甲", "attr": "atk", "flat_value": 1, "price": 10, "purchased": 0}, {"product_id": "probe_p2", "display_name": "探针商品乙", "attr": "hp", "flat_value": 2, "price": 20, "purchased": 0}, ], 100) _settle() - await _audit("shop", [main.shop_overlay]) + await _audit("shop", [main.ui.modals.shop_overlay]) # ---- 术语传导:货架卡与三选一卡片的玩家面文案(物品名=专长获取、分类=专长凭证) - main.shop_overlay.open_shelf([ + main.ui.modals.shop_overlay.open_shelf([ {"kind_id": "feat_ticket", "display_name": "专长获取", "value": 0, "price": 5}, {"kind_id": "heal", "display_name": "应急回复", "value": 6, "price": 5}, ], 20) _settle() - var ticket_row: Dictionary = main.shop_overlay._shelf_rows[0] + var ticket_row: Dictionary = main.ui.modals.shop_overlay._shelf_rows[0] if String(main.db.run.shop.pool_row("feat_ticket").get("display_name", "")) != "专长获取": problems.append("[shop_term] shop.tres 专长凭证件名未换代(现=%s)" % String(main.db.run.shop.pool_row("feat_ticket").get("display_name", ""))) if ticket_row.card.name_l.text != "专长获取": @@ -211,7 +211,7 @@ func _ready() -> void: problems.append("[shop_term] 专长凭证件描述未用规范流程句(现=%s)" % ticket_row.card.desc_l.text) if ticket_row.card.foot_l.text != "专长凭证": problems.append("[shop_term] 专长凭证件分类标注未用「专长凭证」(现=%s)" % ticket_row.card.foot_l.text) - var heal_row: Dictionary = main.shop_overlay._shelf_rows[1] + var heal_row: Dictionary = main.ui.modals.shop_overlay._shelf_rows[1] if heal_row.card.desc_l.text != "回复 +6(本冒险)": problems.append("[shop_term] 回复件描述漏了 heal 词表(现=%s)" % heal_row.card.desc_l.text) if heal_row.card.foot_l.text != "回复": @@ -223,7 +223,7 @@ func _ready() -> void: problems.append("[curio_term] 专长凭证三选一卡未用物品名/规范流程句(现=%s)" % ticket_text) if ticket_text.contains("门票") or ticket_text.contains("T5") or ticket_text.contains("持有集") or ticket_text.contains("奇物三选一"): problems.append("[curio_term] 专长凭证三选一卡残留退役词/内部任务号(现=%s)" % ticket_text) - main.shop_overlay.visible = false + main.ui.modals.shop_overlay.visible = false _settle() # ---- ⑦resume 专项:续玩停点恢复(main.gd:717 报错回归闸)---- @@ -234,11 +234,11 @@ func _ready() -> void: main.run_scope.runner = null # 全新启动等价态(真机=菜单直进续玩,无前置冒险——runner 从未赋值;探针此前推流会残留旧 runner) main._resume_from_menu() _settle(8) - if not main._end_overlay.visible: + if not main.ui.modals.end_overlay.visible: problems.append("[resume] 续玩恢复后胜利总结面未开(三选一停点分支中断)") else: var found := {"ok": false} - _find_label_text(main._end_overlay, "合计 24", found) + _find_label_text(main.ui.modals.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}) @@ -280,7 +280,7 @@ func _find_label_text(node: Node, needle: String, found: Dictionary) -> void: func _pick_battle_node(main: Node) -> int: # 入口/兑付停点走 level_screen.open_choices 直传(不写 _pending_candidates)—— # 候选从视图态取:_cand_ids × runner.nodes() 的 type(编号解析=node_id_of 单一出口) - var ls: Node = main.level_screen + var ls: Node = main.ui.modals.level_screen if ls._map._runner == null: return -1 for nd in ls._map._runner.nodes(): @@ -306,7 +306,7 @@ const _PAUSE_READONLY_SIGNALS: Array[String] = [ func _status_readonly_contract(main: Node) -> void: - var overlay: Control = main.status_panel + var overlay: Control = main.ui.modals.status_panel var stack: Array = [overlay] var conn_count := 0 while not stack.is_empty(): @@ -332,7 +332,7 @@ func _status_readonly_contract(main: Node) -> void: # ⑨status 零动效契约(R5/P4 甲——零动效直开直关路径存在断言): # 子树零 AnimationPlayer + 视图源码零 tween/动效包装调用(源码 token 扫描=ui_contrast 静态段同手法) func _status_zero_motion_contract(main: Node) -> void: - var overlay: Control = main.status_panel + var overlay: Control = main.ui.modals.status_panel var stack: Array = [overlay] while not stack.is_empty(): var n: Node = stack.pop_back() @@ -352,7 +352,7 @@ func _status_zero_motion_contract(main: Node) -> void: # 清单外零连接。白名单={视图自身 _emit_closed〔返回钮〕, 组合根 _on_career_closed};页内任何 # 购买/导航/写入方向的新连接一律红,须先入册审查。 func _career_readonly_contract(main: Node) -> void: - var overlay: Control = main.career_overlay + var overlay: Control = main.ui.modals.career_overlay var stack: Array = [overlay] var conn_count := 0 while not stack.is_empty(): @@ -377,7 +377,7 @@ func _career_readonly_contract(main: Node) -> void: # ⑪career 零动效契约(状态面板 ⑨ 同形制——P4 甲直开直关) func _career_zero_motion_contract(main: Node) -> void: - var overlay: Control = main.career_overlay + var overlay: Control = main.ui.modals.career_overlay var stack: Array = [overlay] while not stack.is_empty(): var n: Node = stack.pop_back() @@ -392,13 +392,13 @@ func _career_zero_motion_contract(main: Node) -> void: # ⑫模态登记/树序全量合同(V1——career 专属登记序契约推广到全部模态+屏壳; -# 判据=main.LAYER_CONTRACT 单一事实源:登记册序==合同模态序、直接子层树序==合同序逐对相等。 +# 判据=main.ui.LAYER_CONTRACT 单一事实源:登记册序==合同模态序、直接子层树序==合同序逐对相等。 # 两特例按合同数据表达:level_screen 宿住冒险屏壳(树序随宿主=全模态之下,遮挡语义归模态栈 # screen_state 登记)、camp_overlay 成员与 camp_screen 屏壳同槽(屏壳树序代言成员登记位)。 # _career_registry_contract 的断言(career=状态面板后/确认弹窗前、树序同步)由本全量合同覆盖) # ⑭选档界面契约(卡③ sl1/sl2/sl3/sl4/sl9——呈现面全部来自喂入数据,视图零自判) func _save_select_contract(main: Node, tpl: String) -> void: - var sso: Node = main.save_select_overlay + var sso: Node = main.ui.modals.save_select_overlay # sl1 三槽呈现:槽卡计数==3+零滚动容器(禁滚动长列表形态——契约 1) var rows: VBoxContainer = sso._rows_box var live_rows := 0 @@ -477,13 +477,13 @@ func _collect_labels(node: Node, out: Array[String]) -> void: func _modal_registry_contract(main: Node) -> void: - var list: Array = main._modals._list + var list: Array = main.ui.modals.flow.stack._list # ① 登记册序==合同模态序(逐位点名——合同是登记序唯一事实源) var contract_modals: Array = [] - for entry: Dictionary in main.LAYER_CONTRACT: + for entry: Dictionary in main.ui.LAYER_CONTRACT: if String(entry["kind"]) != "modal": continue - var n: Node = main._layer_node(String(entry["key"])) + var n: Node = main.ui.node(String(entry["key"])) if n == null: problems.append("[registry] 合同层缺失: %s〔装配生命批 V1〕" % String(entry["key"])) continue @@ -498,10 +498,10 @@ func _modal_registry_contract(main: Node) -> void: i, list[i].name, contract_modals[i].name]) # ② 直接子层树序==合同序逐对相等(屏壳/模态/全屏态/非视觉节点全链;宿住层随宿主不参与) var prev: Node = null - for entry: Dictionary in main.LAYER_CONTRACT: + for entry: Dictionary in main.ui.LAYER_CONTRACT: if entry.has("host"): continue - var node: Node = main._layer_node(String(entry["shell"]) if entry.has("shell") else String(entry["key"])) + var node: Node = main.ui.node(String(entry["shell"]) if entry.has("shell") else String(entry["key"])) if node == null: problems.append("[registry] 合同层缺失: %s〔装配生命批 V1〕" % String(entry["key"])) continue @@ -510,23 +510,23 @@ func _modal_registry_contract(main: Node) -> void: prev.name, node.name, node.get_index(), prev.get_index()]) prev = node # ③ 宿住层特例:level_screen 宿主=冒险屏壳且在全直接子模态之下(遮挡语义归模态栈 screen_state) - var lv: Node = main.level_screen - if lv.get_parent() != main._adventure_screen: + var lv: Node = main.ui.modals.level_screen + if lv.get_parent() != main.ui.screens.adventure_screen: problems.append("[registry] level_screen 宿主应=冒险屏壳(实测 %s)〔装配生命批 V1〕" % lv.get_parent().name) - elif lv.get_parent().get_index() >= main.legend_overlay.get_index(): + elif lv.get_parent().get_index() >= main.ui.modals.legend_overlay.get_index(): problems.append("[registry] 冒险屏壳应在全模态之下(宿主 idx=%d legend idx=%d)〔装配生命批 V1〕" % [ - lv.get_parent().get_index(), main.legend_overlay.get_index()]) + lv.get_parent().get_index(), main.ui.modals.legend_overlay.get_index()]) # ④ 营地同槽特例:camp_screen 屏壳树序夹商店层与设置层之间(成员 camp_overlay 登记位代言) - var cs: Node = main._camp_screen - if cs.get_index() <= main.shop_overlay.get_index() or cs.get_index() >= main.settings_overlay.get_index(): + var cs: Node = main.ui.screens.camp_screen + if cs.get_index() <= main.ui.modals.shop_overlay.get_index() or cs.get_index() >= main.ui.modals.settings_overlay.get_index(): problems.append("[registry] camp_screen 屏壳树序应夹商店与设置层之间(idx=%d shop=%d settings=%d)〔装配生命批 V1〕" % [ - cs.get_index(), main.shop_overlay.get_index(), main.settings_overlay.get_index()]) + cs.get_index(), main.ui.modals.shop_overlay.get_index(), main.ui.modals.settings_overlay.get_index()]) # ⑬career 零跳转契约(提纲 P4 甲——页内零「前往他处」入口:除「返回」外任何导航钮=红) func _career_nojump_contract(main: Node) -> void: for nav in ["营地", "商店", "设置", "冒险", "继续上局", "退出", "前往"]: - if ProbeUiUtil.find_button(main.career_overlay, nav, "contains") != null: + if ProbeUiUtil.find_button(main.ui.modals.career_overlay, nav, "contains") != null: problems.append("[career][nojump] 页内出现导航入口「%s」(零跳转违约——生涯纯只读)" % nav) @@ -534,7 +534,7 @@ func _career_nojump_contract(main: Node) -> void: # (名行基线离盒底 1px 同型高危写法返修回归闸; # 内容矩形=StandeeBody.content_rect(),最低绘制元素=底座下缘) func _standeefit_contract(main: Node) -> void: - var st: Node = main.stage + var st: Node = main.ui.battle.stage if st == null or st.hero_standee == null: problems.append("[standee] 舞台/主角站桩缺失(断言中断)") return @@ -711,8 +711,8 @@ func _dump(c: Control, depth: int) -> void: # ⑤start 专项:左列/右列矩形不相交(M2 双列契约——装配件命名 start_box/start_right 为探针锚点) func _start_columns_contract(main: Node) -> void: - var box: Control = main._start_overlay.find_child("start_box", true, false) - var right: Control = main._start_overlay.find_child("start_right", true, false) + var box: Control = main.ui.screens.start_overlay.find_child("start_box", true, false) + var right: Control = main.ui.screens.start_overlay.find_child("start_right", true, false) if box == null or right == null: problems.append("[start][columns] 左列/右列节点缺失(start_box/start_right——装配命名失守)") return diff --git a/game/probe/ui_tune_probe.gd b/game/probe/ui_tune_probe.gd index 4cd3b9a7..dab76e35 100644 --- a/game/probe/ui_tune_probe.gd +++ b/game/probe/ui_tune_probe.gd @@ -43,35 +43,35 @@ func _ready() -> void: # ① 全局浮层角标开窗(调试入口=UI 最顶层浮层,任何 UI 不遮挡—— # main 直接子层、LAYER_CONTRACT 末位 float 类;boot 菜单优先不开浮窗,角标真实点击路径开窗; # 悬浮于菜单之上——开始层不隐藏;树序新基准=登记册末位(确认弹窗之后=绘制序最上)) - main.debug_badge.pressed.emit() + main.ui.screens.debug_badge.pressed.emit() await get_tree().process_frame - if not main.debug_overlay.visible: + if not main.ui.modals.debug_overlay.visible: problems.append("调试浮层角标点击后浮窗未开(全局浮层入口失守)") - if not main._start_overlay.visible: + if not main.ui.screens.start_overlay.visible: problems.append("开始覆盖层被隐藏——浮窗应悬浮于菜单之上(14.5 形态失守)") - if main.debug_overlay.get_index() < main.confirm_dialog.get_index(): + if main.ui.modals.debug_overlay.get_index() < main.ui.modals.confirm_dialog.get_index(): problems.append("调试浮窗树序不在确认弹窗之后——登记册末位失守") # ② 滑条 → 生效预览联动 - main.debug_overlay.set_sliders(100, 50) - if str(boosted_atk) not in main.debug_overlay.effect_text() or str(boosted_hp) not in main.debug_overlay.effect_text(): - problems.append("生效预览未联动(%s)" % main.debug_overlay.effect_text()) + main.ui.modals.debug_overlay.set_sliders(100, 50) + if str(boosted_atk) not in main.ui.modals.debug_overlay.effect_text() or str(boosted_hp) not in main.ui.modals.debug_overlay.effect_text(): + problems.append("生效预览未联动(%s)" % main.ui.modals.debug_overlay.effect_text()) # ③ 应用(真实信号路径:apply_requested → 组合根)——E4 后加成零表写,生效断言移至 ⑧ 战斗快照 - main.debug_overlay.press_apply_for_probe() + main.ui.modals.debug_overlay.press_apply_for_probe() # ④ 重置(重开浮窗 → 重置按钮真实路径;面板百分比归零——下一场战斗生效值回到基线) main._show_debug_overlay() - main.debug_overlay.press_reset_for_probe() + main.ui.modals.debug_overlay.press_reset_for_probe() # ⑤ 批量模拟全链路(N=10——走 level_autoplay 与 headless 扫描同口径;表基线由组合根复位; # 菜单态上下文未锁定=按钮可点,is_running 语义=模拟在跑〔18卡⑦ 与对局禁用态拆分〕) - main.debug_overlay.set_sliders(100, 0) - main.debug_overlay.set_sim_count(10) - main.debug_overlay.press_simulate_for_probe() + main.ui.modals.debug_overlay.set_sliders(100, 0) + main.ui.modals.debug_overlay.set_sim_count(10) + main.ui.modals.debug_overlay.press_simulate_for_probe() var guard := SIM_GUARD_FRAMES - while main.debug_overlay.is_running() and guard > 0: + while main.ui.modals.debug_overlay.is_running() and guard > 0: guard -= 1 await get_tree().process_frame - if main.debug_overlay.is_running(): + if main.ui.modals.debug_overlay.is_running(): problems.append("模拟未在预算帧内完成(疑似挂死)") - var out: String = main.debug_overlay.output_text() + var out: String = main.ui.modals.debug_overlay.output_text() if not out.contains("关卡完成率"): problems.append("模拟输出缺关卡完成率") # 口径换代 V7:胜局统计行措辞钉子(atk+100% 加成档 10 局必有胜局——hp_left_win 非空路径恒出) @@ -83,7 +83,7 @@ func _ready() -> void: problems.append("模拟输出 N 标注错误(%s)" % out.substr(0, 40)) # ⑥ 关闭 → 浮窗收起、开始层全程在屏 main._close_debug_overlay() - if main.debug_overlay.visible or not main._start_overlay.visible: + if main.ui.modals.debug_overlay.visible or not main.ui.screens.start_overlay.visible: problems.append("关闭后层级状态错误") # ⑦ hero 表运行期不可变(E4 收口回归断言:任何代码 reintroduce 表写在此被抓获——原"表值残留"断言语义升级) if int(main.db.combat.hero.atk) != base_atk or int(main.db.combat.hero.max_hp) != base_hp: @@ -93,20 +93,20 @@ func _ready() -> void: # 存档隔离(停点断点写盘随 main.save_manager;探针禁碰真实档,同族探针隔离纪律; # isolate_save 先删后建——上次运行的残留断点会触发互斥确认早退) ProbeUiUtil.isolate_save(main, "smoke_tune_probe_save", true, false, true) # 末参=swap_save(卡②签名换代——断点写盘经 autoload 落隔离档) - main.debug_overlay.set_sliders(100, 50) - main.debug_overlay.press_apply_for_probe() - main.debug_overlay.seed_edit.text = "42" # 种子钉死(18卡⑦审计🔴——空框=非首局随机,显式优先经真实路径断言 master_seed) + main.ui.modals.debug_overlay.set_sliders(100, 50) + main.ui.modals.debug_overlay.press_apply_for_probe() + main.ui.modals.debug_overlay.seed_edit.text = "42" # 种子钉死(18卡⑦审计🔴——空框=非首局随机,显式优先经真实路径断言 master_seed) main._start_adventure_from_menu() # 完整冒险路径(唯一试玩入口;_start_single 按语义清零滑条 = 恒基线,不承载加成断言) await get_tree().process_frame await get_tree().process_frame # ⑧b 开冒险路径显式关浮窗(18卡⑦ T2 防护——调试会话默认开态下开局不带窗,对局中经角标再开) - if main.debug_overlay.visible: + if main.ui.modals.debug_overlay.visible: problems.append("开冒险路径未显式关浮窗(T2 防护失守)") if int(main.run_scope.adventure.master_seed) != 42: problems.append("显式种子未达冒险(master_seed=%s ≠ 42——真实路径显式优先失守)" % str(main.run_scope.adventure.master_seed)) # 关卡开始 = 入口选路停点——真实按钮路径选入口候选进首战再读呈现快照 - if main.level_screen.visible: - main.level_screen.press_candidate(0) + if main.ui.modals.level_screen.visible: + main.ui.modals.level_screen.press_candidate(0) await get_tree().process_frame await get_tree().process_frame var hero: Dictionary = main.run_scope.settlement.presentation_snapshot().hero @@ -116,23 +116,23 @@ func _ready() -> void: # ⑨ 对局中可开(18卡⑦ P2 甲/T2 防护——S1+ 停机点回归闸):战斗屏角标真实信号路径开窗; # 棋盘输入不锁、状态角标不置灰(「默认开×遮挡复活」下必锁死的破约回归面); # 对局中批量模拟/两层调试两钮禁用;当局数据六项与只读访问器逐项一致;关闭钮真实路径收窗恢复 - var dbg_badge: Button = main.debug_badge + var dbg_badge: Button = main.ui.screens.debug_badge if dbg_badge == null or dbg_badge.visible: problems.append("对局中调试浮层角标非恒隐(发布态语义失守——卡⑦ 删写死行批)") else: main._show_debug_overlay() # 对局中开浮窗回归经直调承载(:61 既有直调先例——发布态角标恒隐,按钮信号路径退役) - if not main.debug_overlay.visible: + if not main.ui.modals.debug_overlay.visible: problems.append("对局中角标开浮窗失败(P2 甲对局中可达失守)") - if main._input_locked(): + if main.ui.modals.flow.locks_input(): problems.append("对局中开浮窗后棋盘输入锁死(T2 防护失守——S1+ 停机点)") - if main.board_zone.status_badge.disabled: + if main.ui.battle.board_zone.status_badge.disabled: problems.append("对局中开浮窗后状态角标置灰(T2 防护失守——S1+ 停机点)") - if main.debug_overlay.is_running(): + if main.ui.modals.debug_overlay.is_running(): problems.append("对局中上下文锁定误判为模拟在跑(is_running 语义互扰)") - if not main.debug_overlay._sim_btn.disabled or not main.debug_overlay._dbg_btn.disabled: + if not main.ui.modals.debug_overlay._sim_btn.disabled or not main.ui.modals.debug_overlay._dbg_btn.disabled: problems.append("对局中批量模拟/两层调试按钮未禁用(T2 并存态不开放失守)") # 当局数据六项值对(T3——数值与只读访问器逐项一致;种子=adventure.master_seed 现值非 _seed_value 重算) - var rd: Dictionary = main.debug_overlay.run_data_probe() + var rd: Dictionary = main.ui.modals.debug_overlay.run_data_probe() var r = main.run_scope.adventure.runner() if String(rd.get("seed", "")) != "42": problems.append("当局数据种子 ≠ master_seed 现值(%s)" % str(rd.get("seed"))) @@ -146,27 +146,27 @@ func _ready() -> void: problems.append("当局数据 GP 失对(%s ≠ %s)" % [str(rd.get("gp")), str(r.gp)]) if int(rd.get("keystone", -1)) != main.run_scope.adventure.journal().keystone_gained(): problems.append("当局数据钥石失对(%s ≠ %s)" % [str(rd.get("keystone")), str(main.run_scope.adventure.journal().keystone_gained())]) - var close_btn: Button = ProbeUiUtil.find_button(main.debug_overlay, "关闭", "prefix") + var close_btn: Button = ProbeUiUtil.find_button(main.ui.modals.debug_overlay, "关闭", "prefix") if close_btn == null: problems.append("浮窗关闭钮缺失(真实收窗路径中断)") else: close_btn.pressed.emit() - if main.debug_overlay.visible or main._input_locked(): + if main.ui.modals.debug_overlay.visible or main.ui.modals.flow.locks_input(): problems.append("对局中关闭浮窗后态失守(visible=%s locked=%s)" - % [str(main.debug_overlay.visible), str(main._input_locked())]) + % [str(main.ui.modals.debug_overlay.visible), str(main.ui.modals.flow.locks_input())]) # ⑨b 只读巡检(17卡③形制)+零动效契约(17卡③ R5——零 AnimationPlayer+源码零 tween) _overlay_readonly_contract(main, problems) _overlay_zero_motion_contract(main, problems) # ⑩ 种子三分单点断言(18卡⑦ C1——判定只落 _seed_value() 单点;对局态不影响本判定面): # 显式合法整数全场景优先(含首局)→ 首局(存档布尔 false)恒 42 → 非首局空框引擎熵连抽不全等 42 - main.debug_overlay.seed_edit.text = "777" + main.ui.modals.debug_overlay.seed_edit.text = "777" if main._seed_value() != 777: problems.append("显式种子输入未优先(_seed_value != 777——C1 ①失守)") # SaveManager 是 autoload 单例——换注走原地 attach(首局态挂载+恢复隔离档); # --script 模式无裸 autoload 标识符——经 main 节点路径解析 var sm = main.get_node("/root/SaveManager") sm.slots.attach_path("user://smoke_tune_probe_seed.json") # 全新空档——first_adventure_done=false(首局态;新档缺省面) - main.debug_overlay.seed_edit.text = "" + main.ui.modals.debug_overlay.seed_edit.text = "" if main._seed_value() != 42: problems.append("首局空框种子 != 42(P5 甲失守——教学态可复现破约)") sm.slots.attach_path("user://smoke_tune_probe_save.json") # 恢复隔离档(非首局态——isolate_save 已建档置位) @@ -181,7 +181,7 @@ func _ready() -> void: all42 = false if all42: problems.append("非首局空框连抽 4 次全等 42(随机化失守——C1 ③)") - main.debug_overlay.seed_edit.text = "42" # 钉回显式值(现场整洁) + main.ui.modals.debug_overlay.seed_edit.text = "42" # 钉回显式值(现场整洁) for f0 in ["user://smoke_tune_probe_seed.json", "user://smoke_tune_probe_seed.json.tmp"]: if FileAccess.file_exists(f0): DirAccess.remove_absolute(f0) @@ -196,10 +196,11 @@ func _ready() -> void: # 只读巡检(18卡⑦ T3——17卡③「白名单外零连接」形制沿用):浮窗子树全控件 × 游戏语义信号名清单 -# 全连接巡检——白名单={视图自身八方法 + 组合根 _on_modal_closed(closed 外发消费)}; +# 全连接巡检——白名单={视图自身八方法 + 注册面模态编排口 ui.modals.flow.close(closed 外发消费—— +# 注册面批把该连接目标从组合根转发壳改为 flow 真方法,机检白名单随批登记同一连接)}; # 当局数据区块=纯标签零连接在此被一并约束(新增任何连接=红须入册审查)。 func _overlay_readonly_contract(main: Node, problems: Array) -> void: - var overlay: Control = main.debug_overlay + var overlay: Control = main.ui.modals.debug_overlay var stack: Array = [overlay] var conn_count := 0 while not stack.is_empty(): @@ -210,8 +211,9 @@ func _overlay_readonly_contract(main: Node, problems: Array) -> void: var cb: Callable = conn["callable"] var host: Object = cb.get_object() var m := String(cb.get_method()) + var flow_host: Object = main.ui.modals.flow # 注册面模态编排口(closed 外发消费白名单宿主) var ok := (host == overlay and m in _OVERLAY_VIEW_METHODS) \ - or (host == main and m == "_on_modal_closed") \ + or (host == flow_host and m == "close") \ or (host is VScrollBar or host is HScrollBar) # 引擎滚动机构内部 drag 接线(RichTextLabel 自动滚动条——17卡③布局生命线同类豁免,零游戏写通路) if not ok: problems.append("[readonly] 非法信号连接 %s.%s → %s.%s(白名单外零连接巡检——18卡⑦ T3)" @@ -224,7 +226,7 @@ func _overlay_readonly_contract(main: Node, problems: Array) -> void: # 零动效契约(17卡③ R5 形制——子树零 AnimationPlayer + 视图源码零 tween/动效包装调用) func _overlay_zero_motion_contract(main: Node, problems: Array) -> void: - var overlay: Control = main.debug_overlay + var overlay: Control = main.ui.modals.debug_overlay var stack: Array = [overlay] while not stack.is_empty(): var n: Node = stack.pop_back() diff --git a/game/tests/system_test.gd b/game/tests/system_test.gd index 5002295f..7814e0a7 100644 --- a/game/tests/system_test.gd +++ b/game/tests/system_test.gd @@ -81,20 +81,20 @@ func _smoke_scene_split() -> void: # V1a 启动态顶层断言:boot 后自树顶向下第一个可见内容层=start_overlay(headless 可判) #(内容层遍历——屏壳是容器不当候选:camp 壳 boot 态 vis=true 但成员恒隐,裸树遍历会误命中) var top: Node = _topmost_visible_layer(main) - check(top == main._start_overlay, + check(top == main.ui.screens.start_overlay, "V1a 启动态顶层可见层=start_overlay(实测顶层=%s——菜单被冒险屏盖住即红)〔装配生命批 V1〕" % (top.name if top != null else "null")) # V1b 语义关系:start_overlay 在冒险屏内容之上(菜单屏壳在冒险屏壳之上——装配序修复判据) - check(main._menu_screen.get_index() > main._adventure_screen.get_index(), + check(main.ui.screens.menu_screen.get_index() > main.ui.screens.adventure_screen.get_index(), "V1b 菜单屏壳在冒险屏壳之上(start_overlay 盖住战斗内容)〔装配生命批 V1〕") # V1c 合同树序链逐对保序(直接子层全链=屏壳/模态/全屏态/非视觉节点;宿住层树序随宿主不参与) var chain_detail := "" var chain_prev: Node = null var chain_ok := true - for entry: Dictionary in main.LAYER_CONTRACT: + for entry: Dictionary in main.ui.LAYER_CONTRACT: if entry.has("host"): continue - var node: Node = main._layer_node(String(entry["shell"]) if entry.has("shell") else String(entry["key"])) + var node: Node = main.ui.node(String(entry["shell"]) if entry.has("shell") else String(entry["key"])) if node == null: chain_ok = false chain_detail += " 缺失:%s" % String(entry["key"]) @@ -105,7 +105,7 @@ func _smoke_scene_split() -> void: chain_prev = node check(chain_ok, "V1c 合同树序链逐对保序(运行期树序==LAYER_CONTRACT 序;违例:%s)〔装配生命批 V1〕" % chain_detail) # ③ 探针挂载假设句柄在座(main.X 一级路径——探针迁移面收口证明) - check(main._start_overlay == main._menu_screen.start_overlay and main.camp_overlay == main._camp_screen.camp_overlay and main.level_screen == main._adventure_screen.level_screen and main._battle_root == main._adventure_screen.battle_root and main.board_view == main._adventure_screen.board_view, + check(main.ui.screens.start_overlay == main.ui.screens.menu_screen.start_overlay and main.ui.modals.camp_overlay == main.ui.screens.camp_screen.camp_overlay and main.ui.modals.level_screen == main.ui.screens.adventure_screen.level_screen and main.ui.screens.battle_root == main.ui.screens.adventure_screen.battle_root and main.ui.battle.board_zone.board_view == main.ui.screens.adventure_screen.board_view, "V2 组合根句柄指向屏壳成员(探针挂载假设 main.X 零迁移)") check(main.run_scope != null and not main.run_scope.is_adventure, "V2 闲时会话自 _ready 即在(永非 null;迭代 27 T3——gp 影子账退役无账本成员)") @@ -116,10 +116,10 @@ func _smoke_scene_split() -> void: # 顶层可见内容层(V1a 用——候选=合同模态/全屏态+两屏壳的 boot 内容成员;屏壳本身是容器不当候选: # camp 壳 boot 态 vis=true 但成员恒隐,裸树遍历自顶向下会先命中透明壳) func _topmost_visible_layer(main: Node) -> Node: - var candidates: Array[Node] = [main._start_overlay, main._battle_root, main._inter_overlay, - main.legend_overlay, main.node_overlay, main.level_screen, main.curio_overlay, - main._end_overlay, main.shop_overlay, main.camp_overlay, main.settings_overlay, - main.status_panel, main.career_overlay, main.confirm_dialog, main.debug_overlay] + var candidates: Array[Node] = [main.ui.screens.start_overlay, main.ui.screens.battle_root, main.ui.screens.inter_overlay, + main.ui.modals.legend_overlay, main.ui.modals.node_overlay, main.ui.modals.level_screen, main.ui.modals.curio_overlay, + main.ui.modals.end_overlay, main.ui.modals.shop_overlay, main.ui.modals.camp_overlay, main.ui.modals.settings_overlay, + main.ui.modals.status_panel, main.ui.modals.career_overlay, main.ui.modals.confirm_dialog, main.ui.modals.debug_overlay] var best: Node = null var best_x := -1 var best_y := -1 @@ -167,7 +167,7 @@ func _smoke_boot_chain() -> void: "V2b 设置已应用·减动效(UiKit.motion_scale=%s=settings_store.reduced_motion=%s)〔装配生命批 V2〕" % [ str(ui_kit.motion_scale), str(ss.reduced_motion)]) # ③ 启动态菜单可见 - check(main._start_overlay.visible, + check(main.ui.screens.start_overlay.visible, "V2c 启动态 _start_overlay.visible=true(boot 后玩家见菜单)〔装配生命批 V2〕") # ④ 续玩钮可见态==SaveManager 断点在档态 var sm = main.get_node("/root/SaveManager") @@ -186,22 +186,22 @@ func _smoke_boot_chain() -> void: if FileAccess.file_exists(p): DirAccess.remove_absolute(p) sm.slots.attach_path("user://smoke_boot_chain.json") - check(main._menu_screen.entersavebtn != null and main._menu_screen.settingsbtn != null - and main._menu_screen.quitbtn != null, + check(main.ui.screens.menu_screen.entersavebtn != null and main.ui.screens.menu_screen.settingsbtn != null + and main.ui.screens.menu_screen.quitbtn != null, "V2d 三钮在位·进入存档/设置/退出游戏(续玩钮族退役后主菜单操作组承接)〔卡② mn1/mn8〕") for old_btn in ["lvbtn", "resumebtn", "campbtn", "careerbtn"]: - check(not (old_btn in main._menu_screen), + check(not (old_btn in main.ui.screens.menu_screen), "V2d 四旧钮句柄零命中·%s(彻底撤出——禁隐藏态禁置灰态残留)〔卡② mn2〕" % old_btn) - check(_count_visible_buttons(main._start_overlay) == 3, + check(_count_visible_buttons(main.ui.screens.start_overlay) == 3, "V2d 渲染面可见按钮计数==3(旧钮接回即红——渲染面锚,句柄缝盲区闭合)〔卡② mn1/mn2〕") # ⑤ 闲时会话(_ready 即建、永非 null、在树) check(main.run_scope != null and not main.run_scope.is_adventure and main.run_scope.is_inside_tree(), "V2e run_scope 非空且为闲时会话(is_adventure=false+is_inside_tree)〔装配生命批 V2〕") # ⑥ 调试浮窗 boot 不开+菜单角标在(菜单优先——boot 不自动开浮窗, # 入口=开始屏右下角「调试」角标+对局中两屏角标) - check(not main.debug_overlay.visible, + check(not main.ui.modals.debug_overlay.visible, "V2f 调试浮窗 boot 不自动开(菜单优先)〔2026-10-16 用户令〕") - check(main.debug_badge != null and not main.debug_badge.visible, + check(main.ui.screens.debug_badge != null and not main.ui.screens.debug_badge.visible, "V2f 调试浮层角标发布态恒隐(2026-10-25 删写死行批)") main.queue_free() await process_frame @@ -223,7 +223,7 @@ func _smoke_run_lifecycle() -> void: sm.slots.attach_path("user://smoke_run_lifecycle.json") sm.meta_state.mark_first_adventure_done() # ---- 第一局:开局→注脏→放弃→终局出口→会话收束 ---- - main.debug_overlay.seed_edit.text = "42" + main.ui.modals.debug_overlay.seed_edit.text = "42" main._start_adventure_from_menu() await process_frame check(main.run_scope != null and main.run_scope.is_adventure and main.run_scope.adventure != null, @@ -243,22 +243,22 @@ func _smoke_run_lifecycle() -> void: break await process_frame check(finished.done, "V3 冒险终局到达(abandon→adventure_finished 同步链)") - check(main._end_overlay.is_open(), "V3 终局面板弹起") + check(main.ui.modals.end_overlay.is_open(), "V3 终局面板弹起") main._on_battle_end_continue() # 终局出口(回菜单+会话收束——手工置空清单已消亡) await process_frame check(not main.run_scope.is_adventure and main.run_scope.adventure == null and main.run_scope.runner == null, "V3 会话收束(新闲时会话零冒险/关卡对象)") check(main.run_scope.hints_shown.is_empty() and not main.run_scope.boss_active and main.run_scope.last_gain_amount == 0, "V3 局态封印/账本全复位(queue_free 物化——注入脏状态零残留)") - check(main._start_overlay.visible, "V3 回开始菜单(终局出口路径)") + check(main.ui.screens.start_overlay.visible, "V3 回开始菜单(终局出口路径)") # ---- 第二局:放弃终局无断点残留→直进开局→全新会话 ---- check(not sm.has_breakpoint(), "V3 放弃终局清断点(无残留断点)") - main.debug_overlay.seed_edit.text = "43" + main.ui.modals.debug_overlay.seed_edit.text = "43" main._start_adventure_from_menu() await process_frame check(main.run_scope.is_adventure and main.run_scope.adventure != null and main.run_scope.hints_shown.is_empty(), "V3 第二连局全新会话(两连局零泄漏)") - check(not main._start_overlay.visible, "V3 直进入局(菜单隐)") + check(not main.ui.screens.start_overlay.visible, "V3 直进入局(菜单隐)") main.queue_free() await process_frame for p in ["user://smoke_run_lifecycle.json", "user://smoke_run_lifecycle.json.tmp"]: @@ -281,14 +281,14 @@ func _smoke_sim_guards() -> void: # wd6:闲时态 + 模拟运行态 → 调试两层入口守卫早退(开始层不收起、无结算对象) check(main.run_scope.settlement == null and not main.run_scope.is_adventure, "前置:闲时会话(settlement 空 + is_adventure=false)") - main._start_overlay.visible = true + main.ui.screens.start_overlay.visible = true main._tune_sim._running = true main._start_debug_levels() - check(main.run_scope.settlement == null and main._start_overlay.visible, + check(main.run_scope.settlement == null and main.ui.screens.start_overlay.visible, "模拟运行态下调试两层入口守卫早退(开始层不收起)〔wd6〕") main._tune_sim._running = false # wd7:真实冒险关间态(is_adventure=true、settlement==null)→ 模拟入口守卫早退 - main.debug_overlay.seed_edit.text = "42" + main.ui.modals.debug_overlay.seed_edit.text = "42" main._start_adventure_from_menu() await process_frame check(main.run_scope.is_adventure and main.run_scope.settlement == null, @@ -313,8 +313,8 @@ func _face_snapshot(main: Node) -> Dictionary: var tu = main._treasure_ui var bo = main._bonus var cp = main._camp_pres - var mf = main._modal_flow - var rp = main._row_pacer + var mf = main.ui.modals.flow + var rp = main.ui.screens.row_pacer return { "treasure_settlement": tu._settlement_get.call(), "treasure_runner": tu._runner_get.call(), @@ -381,13 +381,13 @@ func _stage_faces(main: Node, tag: String, adv: String, run: String, set: String if bool(snap["mf_rows_empty"]) != bool(extra[k]): bad += " modal_flow.rows_empty_get 应=%s" % str(extra[k]) "level_vis": - if main.level_screen.visible != bool(extra[k]): + if main.ui.modals.level_screen.visible != bool(extra[k]): bad += " level_screen.visible 应=%s" % str(extra[k]) "end_open": - if main._end_overlay.is_open() != bool(extra[k]): + if main.ui.modals.end_overlay.is_open() != bool(extra[k]): bad += " _end_overlay.is_open 应=%s" % str(extra[k]) "start_vis": - if main._start_overlay.visible != bool(extra[k]): + if main.ui.screens.start_overlay.visible != bool(extra[k]): bad += " _start_overlay.visible 应=%s" % str(extra[k]) check(bad.is_empty(), "%s(引用面违例:%s)〔装配生命批 V3〕" % [tag, bad]) @@ -419,7 +419,7 @@ func _smoke_lifecycle_matrix() -> void: if FileAccess.file_exists(p): DirAccess.remove_absolute(p) sm.slots.attach_path("user://smoke_lifecycle.json") - main.debug_overlay.seed_edit.text = "42" + main.ui.modals.debug_overlay.seed_edit.text = "42" # 阶段①闲时 await _stage_faces(main, "V3a 闲时", "null", "null", "null", {"mf_start": true, "mf_inter": false, "mf_rows_empty": true, "start_vis": true}) @@ -430,7 +430,7 @@ func _smoke_lifecycle_matrix() -> void: await _stage_faces(main, "V3b 开局停点", "alive", "alive", "null", {"mf_start": false, "level_vis": true}) # 阶段③地图选路→战斗中(真实选路提交 press_candidate) - main.level_screen.press_candidate(0) + main.ui.modals.level_screen.press_candidate(0) for i in range(10): await process_frame await _stage_faces(main, "V3c 战斗中", "alive", "alive", "alive", {"mf_start": false}) @@ -465,9 +465,9 @@ func _smoke_lifecycle_matrix() -> void: check(main.run_scope.settlement == null or not main.run_scope.settlement.is_battle_live(), "V3d 活战斗驱动完毕(战斗已收场——%d 轮,finished=%s)〔装配生命批 V3〕" % [guard, str(finished.done)]) await process_frame - main._flush_row_queue() + main.ui.screens.row_pacer.flush() for i in range(12): - if main._end_overlay.is_open(): + if main.ui.modals.end_overlay.is_open(): break await process_frame # 分支甲:战斗胜利停点(结算后)→确认进选路→放弃→终局面 @@ -482,13 +482,13 @@ func _smoke_lifecycle_matrix() -> void: if finished.done: break await process_frame - main._flush_row_queue() + main.ui.screens.row_pacer.flush() for i in range(8): - if main._end_overlay.is_open(): + if main.ui.modals.end_overlay.is_open(): break await process_frame # 阶段⑤终局面(败北/放弃皆达) - check(finished.done and main._end_overlay.is_open(), + check(finished.done and main.ui.modals.end_overlay.is_open(), "V3e 终局面到达(adventure_finished+终局面板开)〔装配生命批 V3〕") await _stage_faces(main, "V3e 终局面", "alive", "alive", "null", {"mf_start": false, "end_open": true}) # 阶段⑥回菜单收束(旧局消亡+新会话即驻+无真空窗) @@ -515,7 +515,7 @@ func _smoke_lifecycle_matrix() -> void: main._open_save_select() for i in range(2): await process_frame - var ssel_new: Button = main.save_select_overlay._new_btns.get(1) + var ssel_new: Button = main.ui.modals.save_select_overlay._new_btns.get(1) if ssel_new == null: check(false, "V3g 营地入口缺失(选档层新档钮驱动中断)〔装配生命批 V3〕") continue @@ -551,7 +551,7 @@ func _smoke_lifecycle_matrix() -> void: DirAccess.remove_absolute(slot_p) # 阶段⑧两连局(第二局全新会话——旧局对象不复用、脏态零残留) var first_adv = main.run_scope.adventure - main.debug_overlay.seed_edit.text = "43" + main.ui.modals.debug_overlay.seed_edit.text = "43" main._start_adventure_from_menu() for i in range(6): await process_frame @@ -560,12 +560,12 @@ func _smoke_lifecycle_matrix() -> void: await _stage_faces(main, "V3h 两连局·开局停点", "alive", "alive", "null", {"mf_start": false, "level_vis": true}) check(main.run_scope.adventure.abandon(), "V3h 放弃受理(第二局)〔装配生命批 V3〕") for i in range(12): - if main._end_overlay.is_open(): + if main.ui.modals.end_overlay.is_open(): break await process_frame - main._flush_row_queue() + main.ui.screens.row_pacer.flush() for i in range(8): - if main._end_overlay.is_open(): + if main.ui.modals.end_overlay.is_open(): break await process_frame await _stage_faces(main, "V3h 两连局·终局面", "alive", "alive", "null", {"mf_start": false, "end_open": true}) diff --git a/game/tests/unit/presentation/pacing_queue_test.gd b/game/tests/unit/presentation/pacing_queue_test.gd index de6afc4a..7890d23f 100644 --- a/game/tests/unit/presentation/pacing_queue_test.gd +++ b/game/tests/unit/presentation/pacing_queue_test.gd @@ -58,7 +58,7 @@ func _init() -> void: main._start_adventure_from_menu() await process_frame # 入口停点选路进首战(真实停点链) - if not main.level_screen.visible: + if not main.ui.modals.level_screen.visible: _problems.append("入口停点未呈现(前置失守)") _finish() return @@ -100,12 +100,12 @@ func _init() -> void: # ---- 断言①:排空前后核心状态逐位一致(演出关停零分叉)---- var snap_before: String = JSON.stringify(battle.diagnostic_snapshot()) var hp_before: int = int(battle.action_economy.ap) + int(battle.action_economy.rap) * 0 # 观测面双录 - main._flush_row_queue() + main.ui.screens.row_pacer.flush() var snap_after: String = JSON.stringify(battle.diagnostic_snapshot()) check(snap_before == snap_after, "排空前后核心诊断快照逐位一致(AC⑤ 演出关停零分叉)") # ---- 断言②:零丢行(结构判据:排空后队列恒空 + 末信号行原文在带——append_row 直落不吞行)---- check(rows_seen.size() > 0, "结算行已产生(断言前提)") - check(main._row_queue.is_empty(), "排空后队列恒空(flush 完整性)") + check(main.ui.screens.row_pacer.pending_rows().is_empty(), "排空后队列恒空(flush 完整性)") var last_text := "" for i in range(rows_seen.size() - 1, -1, -1): var cand_row: Dictionary = rows_seen[i] @@ -114,7 +114,7 @@ func _init() -> void: last_text = String(cand_row.get("display_name", "")) if last_text != "": break - check(last_text != "" and main.log_view.parsed_text().contains(last_text), + check(last_text != "" and main.ui.battle.log_view.parsed_text().contains(last_text), "末信号行原文在明细带(排空不吞行:『%s』)" % last_text) # ---- 断言③:段间延迟取数面双态 ---- UiKit.motion_scale = 1.0 @@ -129,7 +129,7 @@ func _init() -> void: var fstage := FakeStage.new(null) var ffx := FakeFx.new() rp.stage = fstage - rp.board_view = main.board_view + rp.board_view = main.ui.battle.board_zone.board_view rp.settlement_fx = ffx rp._play_settlement_mark({"type": "damage", "target": "地精", "target_id": "goblin_b", "value": 3}) check(fstage.calls.size() == 1 and fstage.calls[0] == ["goblin_b", "地精"], @@ -144,9 +144,9 @@ func _init() -> void: UiKit.motion_scale = 1.0 # 常态入队(减动效快路同帧直落不入队——夹具须真落队列) main._on_step_row({"type": "damage", "target": "hero", "value": 1}) main._on_step_row({"type": "heal", "target": "hero", "value": 2}) - check(main._row_queue.size() == 2, "会话更替夹具已入队(前提:%d 行)〔wr4〕" % main._row_queue.size()) + check(main.ui.screens.row_pacer.pending_rows().size() == 2, "会话更替夹具已入队(前提:%d 行)〔wr4〕" % main.ui.screens.row_pacer.pending_rows().size()) main._begin_session(false) - check(main._row_queue.is_empty(), "会话更替后待播队列清零(_begin_session 内机制 flush)〔wr4〕") + check(main.ui.screens.row_pacer.pending_rows().is_empty(), "会话更替后待播队列清零(_begin_session 内机制 flush)〔wr4〕") _finish()