# VERTICAL SLICE - NOT FOR PRODUCTION # 布局几何断言探针(正主=tools/probe_gate.sh 默认段;呈现层三区布局的分辨率维度验收) # 用途:battle.md §验收「1280×800 与 16:9 全屏下三区无遮挡/无重叠/无裁切」的可执行化 # GDD 锚:design/gdd/ux/battle.md §Layout Zones(B/C/D 三区几何) # 硬约束:档位表是数据、断言是一份函数据——加档只改表不动断言(禁每档建一个探针) extends Node const ProbeUiUtil := preload("res://smoke/probe_ui_util.gd") # 隔离样板单点 # 分辨率档位表(逻辑视口尺寸;数据侧——增删档位只改这里,断言代码零改动) const VIEWPORTS := [ {"label": "基座 1280x800", "size": Vector2i(1280, 800)}, {"label": "16:9 全屏 1920x1080", "size": Vector2i(1920, 1080)}, ] const MIN_CELL := 36.0 # 格子可读门槛(battle.md §棋盘格尺寸公式 1280 档 12×12 → 36px) var _problems: PackedStringArray = [] func _ready() -> void: await get_tree().process_frame var main: Node = load("res://presentation/main.tscn").instantiate() 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._start_adventure_from_menu() for i in range(8): await get_tree().process_frame # 进战斗屏——三区(B 战斗画面/C 角色信息/D 扫雷图)同屏常驻,菜单屏不构成断言面 main.level_screen.press_candidate(0) for i in range(20): await get_tree().process_frame for vp in VIEWPORTS: # content_scale_size 是headless 下改逻辑视口的唯一有效入口(get_window().size # 在 headless 下不改变视口——用旧 API 会得到永远为真的假断言) get_tree().root.content_scale_size = vp["size"] for i in range(5): await get_tree().process_frame _check_one(vp["label"], main) # 复原本项目基座(探针不该把进程留在末档尺寸) get_tree().root.content_scale_size = Vector2i(1280, 800) if _problems.is_empty(): print("[layout_probe] PASS — %d 档几何不变量全过(三区非零/不越视口/互不重叠/view 含于 D 区/格子 ≥%.0fpx)" % [VIEWPORTS.size(), MIN_CELL]) get_tree().quit(0) else: for p in _problems: print("[layout_probe] FAIL — " + p) print("[layout_probe] FAIL — %d 项断言未过" % _problems.size()) get_tree().quit(1) # 一份不变量断言,逐档代入——代码量与档数无关 func _check_one(label: String, main: Node) -> void: var vr: Rect2 = get_viewport().get_visible_rect() if vr.size != Vector2(VIEWPORTS[0]["size"]) and vr.size.y < 1.0: _problems.append("[%s] 逻辑视口退化 %s" % [label, vr.size]) var rects := {} for key in ["stage", "info_panel", "board_zone"]: var ctl: Control = main.get(key) if ctl == null: _problems.append("[%s] %s = null(三区未装配)" % [label, key]) return var r := Rect2(ctl.global_position, ctl.size) rects[key] = r if r.size.x <= 0.0 or r.size.y <= 0.0: _problems.append("[%s] %s 零尺寸 %s" % [label, key, r.size]) elif not vr.encloses(r): _problems.append("[%s] %s 越视口 %s ⊄ %s" % [label, key, r, vr]) # 判据覆盖边界(如实记录,勿当已拦截):本探针按名点名三区做几何判定, # 不遍历战斗屏宿主的全部后代。变异验证第二发证——向装配器注入一个全幅 ColorRect # (挂在 battle_root 的内层根下)时探针仍全绿:「未登记兄弟控件」这一缺陷类不在本探针 # 拦截面内。要拦它得改为递归遍历全子树找重叠,那是另一套判据(且极易误报容器父子关系), # 归后续批按需立,不在本批范围。 var bv: Control = main.board_view if bv == null: _problems.append("[%s] board_view = null" % label) return var br := Rect2(bv.global_position, bv.size) if not vr.encloses(br): _problems.append("[%s] board_view 越视口 %s ⊄ %s" % [label, br, vr]) if rects.has("board_zone") and not rects["board_zone"].encloses(br): _problems.append("[%s] board_view 越出 board_zone(%s ⊄ %s)" % [label, br, rects["board_zone"]]) var keys := rects.keys() for i in range(keys.size()): for j in range(i + 1, keys.size()): 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") if cell < MIN_CELL: _problems.append("[%s] 格子边长 %.1fpx < 可读门槛 %.0fpx" % [label, cell, MIN_CELL])