135 lines
7.9 KiB
GDScript
135 lines
7.9 KiB
GDScript
# 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:
|
||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:264 (战斗屏三区几何与棋盘格边长——分辨率档位组)
|
||
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.menu.debug_overlay.seed_edit.text = "42" # 种子钉死(确定性禁依赖默认随机分支)
|
||
main._start_adventure_from_menu()
|
||
for i in range(8):
|
||
await get_tree().process_frame
|
||
# 进战斗屏——三区(B 战斗画面/C 角色信息/D 扫雷图)同屏常驻,菜单屏不构成断言面
|
||
ProbeUiUtil.push_click_at(main, main.ui.modals.run.level_screen.candidate_rect(0).get_center())
|
||
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:
|
||
# oracle: design/gdd/systems/表现与支撑/HUD框架.md:264 (单档几何断言组)
|
||
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),
|
||
}
|
||
# 舞台背景与地面层(真实选路入口进战斗屏后的装载断言——迭代 48 卡③ V2/V4/V6)
|
||
var stage: Object = battle_ui.get("stage")
|
||
var bg_tex: TextureRect = stage.get("bg_texture")
|
||
if bg_tex == null or bg_tex.texture == null:
|
||
_problems.append("[%s] 舞台背景纹理未装载(bg_texture 缺位或空纹理)〔舞台背景 V2〕" % label)
|
||
elif not String(bg_tex.texture.resource_path).ends_with("_dungeon.png"):
|
||
_problems.append("[%s] 舞台背景路径非 dungeon 变体:%s〔舞台背景 V2/V6〕" % [label, String(bg_tex.texture.resource_path)])
|
||
var ground: TextureRect = stage.get("ground_layer")
|
||
var layout = main.db.presentation.battle_layout
|
||
if ground == null or not ground.visible:
|
||
_problems.append("[%s] 地面层缺位或不可见〔舞台背景 V4〕" % label)
|
||
elif absf(ground.size.y - float(layout.ground_h)) > 0.51:
|
||
_problems.append("[%s] 地面层高 %s ≠ 布局表 ground_h %d〔舞台背景 V4〕" % [label, str(ground.size.y), int(layout.ground_h)]) |