- modal_stack.gd 新增 has_open_excluding(skip):排除式开态查询(地图屏系登记册模态成员=常态 屏态,其上角标必须可点;只读查询零副作用,登记序/开合记账语义零改动) - board_zone_view.gd 战斗屏 HUD 右下角+level_map_screen.gd 地图屏顶栏 GP 徽右——各一枚「暂停」 角标(debug 角标同形制:UiKit.button 紧凑豁免宽度基准+FS_HINT 字号降档;可见性/置灰归组合根, 信号连接归组合根 ADR-0001;非对局期显式不可见防菜单误现——P1 丁) - main.gd 接线:两角标 pressed→_on_pause_badge_pressed(守卫兜底处理器:他层开着即拒开—— 常态防线=置灰,探针 emit 绕过 disabled 时由此兜住;地图屏开态经 has_open_excluding 不计自身) +_sync_pause_entry 单一同步点(可见性=_level_active∧屏态;置灰=has_open)挂模态开合单口与 屏级互斥口/进局/离局/续玩五路径 - ui_struct_probe:pause 结构断言+只读性契约(游戏语义信号清单巡检——pressed/toggled/text/ value/item/gui_input/focus/closed 十二名,白名单外零连接,合法连接恰 2=关闭钮+closed 外发) +零动效契约(子树零 AnimationPlayer+源码零 create_tween——R5/P4 甲)【首跑 L1 修正:全集 巡检把 Godot 容器布局生命线连接(child_order_changed/item_rect_changed 等引擎自动脏标记) 误判非法——收敛为信号名清单,任务书 v1.1「巡检信号名清单落码列全」本意】 - ui_input_probe case⑯:暂停层输入锁语义(角标真点击 press+release 开层→锁期挖掘零穿透→ 「继续」关层→同格挖掘重新到达;十七断言 PASS)【首跑 L1 修正:域恰 2 枚时 flag_t 带旗+dig_t 揭开后零无旗格可取——case 挪全 case 之尾+前沿移位扩大可挖域兜底(板面扰动零波及)】 - ui_click_probe:战斗屏角标开合/守卫断言(图例开着=置灰+emit 兜底拒开)+地图屏角标一次性 case(选路前开/关,地图屏选路态零扰动);ui_contrast_probe 增钥石徽深墨/烛光黄对(12 对全绿) - smoke_all._pause_overlay_projection:暂停层投影==MetaProgression 访问器逐项(余额徽/锻造三行/ 工坊六行解锁态/天赋树概览——隔离档 smoke_meta 复用,+5 check) - 任务书回填 T0 基线注记(冒烟 783/0·run_all 17 套·交集核对零命中,HEAD=44328b1 时点) - T2 闸读数:冒烟 788/0(基线 783/0,+5 本批)· ui_input 十七断言 PASS · ui_struct 全绿 · ui_contrast 12 对 PASS · ui_click 问题=0 · ui_layout_probe 退出码 0;纯呈现零数值=零 MC
103 lines
4.9 KiB
GDScript
103 lines
4.9 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# UI 文字对比度机检(UI 设计对齐批 T2 2026-10-06——任务书 §7 新增机检段①,probe_gate 默认必跑)
|
||
# 断言面 = 文字墨对直背景 ≥4.5:1(art-bible §7.2 无区域例外;文字禁直压纹理底——垫牌后按牌色计)。
|
||
# 测点表随批维护:血数垫牌 / 意图徽标四墨(PANEL_DATA 垫牌)/ C区槽位两态墨 / RAP 标签 + 全站锚点组。
|
||
# 测点对一律取 UiKit 常量实时求值——代码侧换色即本闸红,禁只改注释不动值;α<1 墨先对背景混成再算。
|
||
# 运行:Godot_console.exe --headless --path . res://smoke/ui_contrast_probe.tscn
|
||
extends Node
|
||
|
||
const MIN_RATIO := 4.5
|
||
|
||
# R1 过渡白名单:呈现层(ui_kit.gd 之外)允许的裸 Color8( 在册处——file→恰计数。
|
||
# T4a 已把 T0 实测全量 25 处/10 文件全部原值搬运收 ui_kit(2026-10-06)——本表应为空表;
|
||
# 新增裸取色 = 本闸红(先立常量再消费)。
|
||
const BARE_COLOR8_WL := {}
|
||
|
||
|
||
# 测点表:[名称, 墨色, 直背景, 墨α](语义见行尾;新增文字位随批入表,白名单豁免须注理由)
|
||
func _pairs() -> Array:
|
||
return [
|
||
["站桩血数·垫牌", Color(1, 1, 1), UiKit.HP_BAR_BACK, 0.95], # N1-1/2 白 α.95 压 HP_BAR_BACK 素牌
|
||
["意图atk·垫牌", UiKit.RED_MINE, UiKit.PANEL_DATA, 1.0], # N1-3 ⚔ 攻击预告(零换墨)
|
||
["意图buf·垫牌", UiKit.HERO_BLUE_DEEP, UiKit.PANEL_DATA, 1.0], # N1-4 ✦ 增益(同族深档)
|
||
["意图debuff·垫牌", UiKit.DEBUFF_PURPLE, UiKit.PANEL_DATA, 1.0], # ☠ 妨害(零换墨随牌达标)
|
||
["意图charge·垫牌", UiKit.GOLD_INK, UiKit.PANEL_DATA, 1.0], # 蓄力重击(深金墨)
|
||
["C区槽·已用", UiKit.STONE_DEEP, UiKit.PANEL_DATA, 1.0], # N1-5 占位暗字(灰石深化)
|
||
["C区槽·不可用", UiKit.INK_LABEL, UiKit.PANEL_DATA, 1.0], # N1-6 置灰(标签深墨)
|
||
["RAP标签", UiKit.INK_LABEL, UiKit.PANEL_DATA, 1.0], # N1-7(T0 实测已合规,显式化)
|
||
["锚·正文墨/数据板", UiKit.INK, UiKit.PANEL_DATA, 1.0], # 全站锚点:正文墨对数据板
|
||
["锚·暖白/屏根", UiKit.WARM, UiKit.BG_ROOT, 1.0], # 全站锚点:暗底正文
|
||
["锚·暖白弱/舞台底", UiKit.WARM_FAINT, UiKit.STAGE_BG, 1.0], # 全站锚点:B区辅助墨
|
||
["钥石徽·深墨/烛光黄", UiKit.INK_ON_GOLD, UiKit.CANDLE, 1.0], # 迭代17卡③——badge 工厂件文字(营地徽+暂停层余额行同形制)
|
||
]
|
||
|
||
|
||
func _lum(c: Color) -> float:
|
||
var f := func(x: float) -> float:
|
||
return x / 12.92 if x <= 0.03928 else pow((x + 0.055) / 1.055, 2.4)
|
||
return 0.2126 * f.call(c.r) + 0.7152 * f.call(c.g) + 0.0722 * f.call(c.b)
|
||
|
||
|
||
func _ratio(a: Color, b: Color) -> float:
|
||
var la := _lum(a)
|
||
var lb := _lum(b)
|
||
var hi := la if la >= lb else lb
|
||
var lo := lb if la >= lb else la
|
||
return (hi + 0.05) / (lo + 0.05)
|
||
|
||
|
||
func _ready() -> void:
|
||
var problems: Array = []
|
||
var pairs := _pairs()
|
||
for p in pairs:
|
||
var fg: Color = p[1]
|
||
var bg: Color = p[2]
|
||
var alpha: float = p[3]
|
||
if alpha < 1.0:
|
||
fg = Color(fg.r * alpha + bg.r * (1.0 - alpha),
|
||
fg.g * alpha + bg.g * (1.0 - alpha),
|
||
fg.b * alpha + bg.b * (1.0 - alpha))
|
||
var r := _ratio(fg, bg)
|
||
if r < MIN_RATIO:
|
||
problems.append("[contrast] %s = %.2f:1 < %.1f(测点对须垫牌或换墨)" % [p[0], r, MIN_RATIO])
|
||
problems.append_array(_scan_static())
|
||
if problems.is_empty():
|
||
print("[ui_contrast_probe] PASS — 对比度 %d 对测点全绿(≥%.1f:1 无区域例外)+ 裸色白名单/preload 红线静态段绿" % [pairs.size(), MIN_RATIO])
|
||
get_tree().quit(0)
|
||
else:
|
||
for msg in problems:
|
||
print(msg)
|
||
print("[ui_contrast_probe] FAIL — %d 项越带(含静态段)" % problems.size())
|
||
get_tree().quit(1)
|
||
|
||
|
||
# ---- 机检段②③(任务书 §7):呈现层裸 Color8( 计数 vs 白名单 + 素材 preload 计数 = 0 ----
|
||
func _scan_static() -> Array:
|
||
var problems: Array = []
|
||
var root := "res://presentation"
|
||
var dirs := [root]
|
||
while not dirs.is_empty():
|
||
var d: String = dirs.pop_front()
|
||
for f in DirAccess.get_files_at(d):
|
||
if not f.ends_with(".gd"):
|
||
continue
|
||
var path := d + "/" + f
|
||
var src := FileAccess.get_file_as_string(path)
|
||
var c8 := src.count("Color8(")
|
||
var pre := 0
|
||
for line in src.split("\n"):
|
||
if line.contains("preload(") and (line.contains("res://assets/") or line.contains(".png")):
|
||
pre += 1
|
||
if path.ends_with("/ui_kit.gd"):
|
||
if pre != 0:
|
||
problems.append("[static] ui_kit.gd 素材 preload=%d(红线=0)" % pre)
|
||
continue # ui_kit=色板工厂本体,Color8 定义面合法
|
||
var wl: int = BARE_COLOR8_WL.get(path, 0)
|
||
if c8 != wl:
|
||
problems.append("[static] %s 裸 Color8=%d 白名单=%d(R1 先立常量再消费)" % [path, c8, wl])
|
||
if pre != 0:
|
||
problems.append("[static] %s 素材 preload=%d(R2 红线=0)" % [path, pre])
|
||
for sub in DirAccess.get_directories_at(d):
|
||
dirs.append(d + "/" + sub)
|
||
return problems
|