Files
gplan-2/game/probe/layout_probe.gd
T

120 lines
6.7 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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://probe/probe_ui_util.gd") # 隔离样板单点
const BoardViewScript := preload("res://presentation/views/board_view.gd") # 算式单点静态调用载体(迭代 46 卡C)
# 分辨率档位表(逻辑视口尺寸;数据侧——增删档位只改这里,断言代码零改动)
const VIEWPORTS := [
{"label": "基座 1280x800", "size": Vector2i(1280, 800)},
{"label": "16:9 全屏 1920x1080", "size": Vector2i(1920, 1080)},
]
var _problems: PackedStringArray = []
var _cell_reads: Dictionary = {} # 逐档格边长实读留痕(label→cell_size;PASS 行引基座值)
var _est_reads: Dictionary = {} # 逐档 13/14 板推定读数留痕(label→{13,14,avail};PASS 后打印对拍)
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.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.ui.modals.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 档几何不变量全过+格边长等价成立(基座实读 %.0fpx)〔格边长 V1〕"
% [VIEWPORTS.size(), float(_cell_reads.get("基座 1280x800", -1.0))])
for label in _est_reads:
var e: Dictionary = _est_reads[label]
print("[layout_probe] [%s] 13 板推定 %dpx/14 板推定 %dpx(实板可用面积 %s)〔格边长 V4〕"
% [label, int(e["13"]), int(e["14"]), str(e["avail"])])
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 := {}
var battle_ui: Object = main.get("ui").battle # 注册面战区域容器(字段名与三区 key 同名——合同键即字段名)
for key in ["stage", "info_panel", "board_zone"]:
var ctl: Control = battle_ui.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])
# 判据覆盖边界(如实记录,勿当已拦截):本探针按名点名三区做几何判定,
# 不遍历战斗屏宿主的全部后代——「未登记兄弟控件」(如向装配器塞一个挂在
# battle_root 内层根下的全幅 ColorRect)这一缺陷类不在本探针拦截面内,探针对此仍全绿。
# 要拦它得改为递归遍历全子树找重叠,那是另一套判据(且极易误报容器父子关系),
# 本探针不设此判据。
var bv: Control = main.ui.battle.board_zone.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.ui.battle.board_zone.board_view.get("cell_size")
_cell_reads[label] = cell
# 格边长接线守卫——判据=等价(生产写入值与单点算式严格相等,生产接线断开即红;
# 全仓禁任何人造格边长可读阈值)。
# 板型 N 无公开出口→`bv.get("_board")` 内部字段直查=受控例外(改动—验收对照表行 1 注明理由)。
var b = bv.get("_board")
if b == null:
_problems.append("[%s] _board 为 null(板型 N 无从取值,等价断言无依据)〔格边长 V1〕" % label)
elif cell != float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)):
_problems.append("[%s] 格边长等价破——生产写入 %.1fpx ≠ _cell_for(n=%d, avail=%s) 返回 %.1fpx(实板可用面积 %s)〔格边长 V1〕"
% [label, cell, maxi(int(b.width), int(b.height)), str(bv.size),
float(BoardViewScript._cell_for(maxi(int(b.width), int(b.height)), bv.size)), str(bv.size)])
# 最大板推定留痕(迭代 46 卡C T2——实板可用面积代入 13/14 板型求算式单点,读数打印不作判红:
# 锚点=用户验收的现行渲染值;对拍归实现报告,对拍不一致=卡 §9 L3 #2 呈报,禁实现侧改锚;
# 零尺寸前置=可用面积不可信时推定无依据)
if bv.size.x <= 0.0 or bv.size.y <= 0.0:
_problems.append("[%s] board_view 零尺寸(可用面积不可信,格边长推定无依据)〔格边长 V4〕" % label)
else:
_est_reads[label] = {
"13": BoardViewScript._cell_for(13, bv.size),
"14": BoardViewScript._cell_for(14, bv.size),
"avail": str(bv.size),
}