iter49-卡3:营地五界面真窗口化批(窗壳形态换装):WINDOW_SIZE 720×640单点+modal_shell窗壳骨架+career同壳+screen_flow_probe V1-V8窗壳形态段
This commit is contained in:
1 parent
240f1a1769
commit
041d509b4d
19 files changed
+524
-132
No files matched your search
@@ -45,7 +45,6 @@ STATUS_VALUES = ("已覆盖", "部分覆盖", "未覆盖")
|
||||
|
||||
# 验收标准节豁免:无可写验收面的在册件(逐件附理由;新豁免须经用户裁)
|
||||
ACCEPT_EXEMPT = {
|
||||
"锻造工坊.md": "工程化系统,验收面由天赋树与元进度两篇承接,本批迁移实现事实后复核",
|
||||
"生涯.md": "P6 后置未实装,无现成可观测面;实装批补验收标准节",
|
||||
}
|
||||
|
||||
|
||||
+24
-19
@@ -2,7 +2,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
r"""extract_checks.py — 断言静态提取器(验证集清单生成与出口对账)
|
||||
|
||||
机器提取宿主脚本的全部静态 check( 调用点(每个调用点记录:所在文件、所在函数、
|
||||
机器提取宿主脚本的全部静态断言调用点(每个调用点记录:所在文件、所在函数、
|
||||
行号、标签字符串字面量原文、cond 表达式原文),提供两种模式:
|
||||
|
||||
用法:
|
||||
@@ -19,10 +19,11 @@ r"""extract_checks.py — 断言静态提取器(验证集清单生成与出口
|
||||
断言,对账时以「新增」出现(非迁移面),出口判定以显式模式为准。
|
||||
|
||||
提取口径:
|
||||
- 静态 check( 调用点,两种通道都认:smoke/系统宿主的 check(cond, label)(cond 在前)
|
||||
与单元/集成宿主的 Kit.check("label", cond)(点前缀=标签在前);`_check(` 等同名
|
||||
前缀调用非标准通道,不取。
|
||||
- 调用点可跨行:从 `check(` 起括号配平归集完整实参文本(() [] {} 三种括号都计深度);
|
||||
- 静态断言调用点,三通道都认:smoke/系统宿主的 check(cond, label)(cond 在前)、
|
||||
单元/集成宿主的 Kit.check("label", cond)(点前缀=标签在前)、静默收集通道
|
||||
Kit.check_quiet(cond, label)(cond 在前,条目 quiet 列记 channel:check_quiet);
|
||||
`_check(` 等同名前缀调用非标准通道,不取。
|
||||
- 调用点可跨行:从调用名起括号配平归集完整实参文本(() [] {} 三种括号都计深度);
|
||||
字符串字面量内的括号/逗号/井号、行尾注释、转义序列均不参与结构判定(码位掩码单趟预扫)。
|
||||
- cond 摘要=cond 原文跨行折叠为单行(连续空白并一空格);标签原文=标签实参里的字符串
|
||||
字面量逐字保真(含引号与转义序列原样,禁改写);标签实参若带格式化/拼接后缀
|
||||
@@ -64,8 +65,8 @@ def default_sources():
|
||||
files.extend(sorted(p.rglob("*.gd")))
|
||||
return sorted(files)
|
||||
|
||||
CALL_RE = re.compile(r"check\(")
|
||||
DEF_RE = re.compile(r"^\s*(static\s+)?func\s+check\s*\(")
|
||||
CALL_RE = re.compile(r"check(?:_quiet)?\(")
|
||||
DEF_RE = re.compile(r"^\s*(static\s+)?func\s+check(?:_quiet)?\s*\(")
|
||||
FUNC_RE = re.compile(r"^(static\s+)?func\s+([A-Za-z_][A-Za-z0-9_]*)")
|
||||
LITERAL_RE = re.compile(r'"((?:[^"\\]|\\.)*)"')
|
||||
OPENERS = "([{"
|
||||
@@ -188,9 +189,9 @@ def split_args(body):
|
||||
return args
|
||||
|
||||
|
||||
def collect_call(joined, start):
|
||||
"""从 check( 的 start 起括号配平归集调用。返回 (body, end_pos);不配平返回 None。"""
|
||||
i = start + len("check(")
|
||||
def collect_call(joined, start, call_len):
|
||||
"""从调用名起点起括号配平归集调用。返回 (body, end_pos);不配平返回 None。"""
|
||||
i = start + call_len
|
||||
depth = 1
|
||||
in_str = False
|
||||
parts = []
|
||||
@@ -228,7 +229,7 @@ def collect_call(joined, start):
|
||||
|
||||
|
||||
def extract(src_path: Path):
|
||||
"""提取单文件全部静态 check( 调用点。返回(条目列表, 函数表),条目保持源码顺序。"""
|
||||
"""提取单文件全部静态断言调用点(check/check_quiet)。返回(条目列表, 函数表),条目保持源码顺序。"""
|
||||
src = src_path.read_text(encoding="utf-8")
|
||||
lines = src.split("\n")
|
||||
funcs = func_table(lines)
|
||||
@@ -247,28 +248,32 @@ def extract(src_path: Path):
|
||||
entries = []
|
||||
for m in CALL_RE.finditer(joined):
|
||||
if not mask[m.start()]:
|
||||
continue # 字符串字面量内 / 注释里的 check( 字样
|
||||
continue # 字符串字面量内 / 注释里的调用名字样
|
||||
ln = line_of(m.start())
|
||||
if DEF_RE.match(lines[ln - 1]):
|
||||
continue # func check 定义行
|
||||
continue # func check / func check_quiet 定义行
|
||||
prev = joined[m.start() - 1] if m.start() > 0 else ""
|
||||
if prev and (prev.isalnum() or prev == "_"):
|
||||
continue # `_check(` 等同名前缀——非标准通道
|
||||
label_first = prev == "." # Kit.check("label", cond)——点前缀=标签在前
|
||||
body, end_pos = collect_call(joined, m.start())
|
||||
is_quiet = m.group(0).startswith("check_quiet")
|
||||
if is_quiet:
|
||||
label_first = False # Kit.check_quiet(cond, label)——cond 在前
|
||||
else:
|
||||
label_first = prev == "." # Kit.check("label", cond)——点前缀=标签在前
|
||||
body, end_pos = collect_call(joined, m.start(), len(m.group(0)))
|
||||
if body is None:
|
||||
raise SystemExit("[extract_checks] FAIL:%s 行 %d 起的 check( 括号不配平到文件尾——先查解析缺口"
|
||||
raise SystemExit("[extract_checks] FAIL:%s 行 %d 起的断言调用括号不配平到文件尾——先查解析缺口"
|
||||
% (src_path.name, ln))
|
||||
args = split_args(body)
|
||||
if len(args) not in (2, 3):
|
||||
raise SystemExit(
|
||||
"[extract_checks] FAIL:%s 行 %d 的 check( 拆出 %d 个实参(预期 2/3)——解析缺口,先查再跑"
|
||||
"[extract_checks] FAIL:%s 行 %d 的断言调用拆出 %d 个实参(预期 2/3)——解析缺口,先查再跑"
|
||||
% (src_path.name, ln, len(args)))
|
||||
if label_first:
|
||||
label_expr, cond = args[0], args[1]
|
||||
else:
|
||||
cond, label_expr = args[0], args[1]
|
||||
quiet = args[2] if len(args) == 3 else ""
|
||||
quiet = "channel:check_quiet" if is_quiet else (args[2] if len(args) == 3 else "")
|
||||
lm = LITERAL_RE.search(label_expr)
|
||||
if lm is None:
|
||||
continue # 标签实参无字面量(wrapper 直传变量——既有套件先例 reward_overlay_test _req)
|
||||
@@ -336,7 +341,7 @@ def render_list(entries, funcs_by_src, src_names, carry=None) -> str:
|
||||
out.append("# 验证集清单(断言快照·冻结)")
|
||||
out.append("")
|
||||
out.append("> 生成日期:%s · 基点 commit:`%s` · 源:%s" % (date.today().isoformat(), base_commit(), "、".join(src_names)))
|
||||
out.append("> 提取口径:静态 `check(` 调用点,括号配平归集(可跨行);缺省源=三宿主遍历")
|
||||
out.append("> 提取口径:静态 `check(`/`check_quiet(` 调用点,括号配平归集(可跨行);缺省源=三宿主遍历")
|
||||
out.append("> (unit/**+integration/**+system_test.gd)。本基点实测 %d 个静态调用点;行级" % len(entries))
|
||||
out.append("> `grep -c \"check(\"` 是另一口径(含 `func check(` 定义行与注释行),两口径各自浮动,")
|
||||
out.append("> 禁互相换算(tests/README「断言计数双口径」)。运行时计数大于静态计数=循环展开条目。")
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""色板基线生成器——palette_check 双门②基线的建档与刷新件。
|
||||
|
||||
产出两张 1×N 条带 PNG 落 game/tests/golden/palette_baseline/(图像白名单在册目录):
|
||||
ui_tokens.png = ui_kit.gd 全量 Color8 常量唯一色值(token 板现役值快照)
|
||||
art_snapshot.png = 现役素材(game/assets/art 递归)全量唯一色快照(像素板+各屏环境子板)
|
||||
|
||||
palette_check --baseline 取目录下全部 PNG 的唯一色并集为「既有色板」;基线缺一张
|
||||
即漏判——基线刷新必须重生成全套。维护纪律:素材批收尾跑一次本器刷新并随批提交
|
||||
(新色过门②后须入基线,否则下批扫描把已过门的新色再报新增)。
|
||||
|
||||
用法:
|
||||
python tools/gen_palette_baseline.py [--repo <仓库根>]
|
||||
退出码: 0 成功 / 2 前置缺位(ui_kit.gd 或素材目录缺 / 素材零 PNG)
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
from PIL import Image
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from palette_check import palette_of # 排除全透明的唯一色提取——与检查器同口径
|
||||
|
||||
|
||||
def strip_comments(text):
|
||||
"""剔 GDScript 行注释与字符串字面量,防注释里举的色值被误收进 token 快照。"""
|
||||
out = []
|
||||
for line in text.split("\n"):
|
||||
i = line.find("#")
|
||||
if i != -1:
|
||||
line = line[:i]
|
||||
out.append(line)
|
||||
return "\n".join(out)
|
||||
|
||||
|
||||
def token_cols(ui_kit_path):
|
||||
"""ui_kit.gd 全量 Color8 唯一色值集合。"""
|
||||
text = strip_comments(open(ui_kit_path, encoding="utf-8").read())
|
||||
vals = re.findall(r"Color8\((\d+),\s*(\d+),\s*(\d+)\)", text)
|
||||
return sorted(set((int(r), int(g), int(b)) for r, g, b in vals))
|
||||
|
||||
|
||||
def scan_dirs(roots):
|
||||
"""递归扫 PNG,唯一色并集(含像素位置首见序不保——并集无序)。"""
|
||||
cols = set()
|
||||
count = 0
|
||||
for root in roots:
|
||||
for dirpath, _, names in os.walk(root):
|
||||
for fn in sorted(names):
|
||||
if fn.lower().endswith(".png"):
|
||||
c, _n = palette_of(os.path.join(dirpath, fn))
|
||||
cols |= c
|
||||
count += 1
|
||||
return cols, count
|
||||
|
||||
|
||||
def write_strip(cols, out_path):
|
||||
"""1×N 条带 PNG(一色一像素列)。"""
|
||||
cols = sorted(cols)
|
||||
im = Image.new("RGB", (len(cols), 1))
|
||||
for i, c in enumerate(cols):
|
||||
im.putpixel((i, 0), c)
|
||||
im.save(out_path)
|
||||
return len(cols)
|
||||
|
||||
|
||||
def main():
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
ap = argparse.ArgumentParser(description="色板基线生成器(token∪素材快照)")
|
||||
ap.add_argument("--repo", default=".", help="仓库根(含 game/ 与 tools/)")
|
||||
args = ap.parse_args()
|
||||
|
||||
ui_kit = os.path.join(args.repo, "game", "presentation", "views", "ui_kit.gd")
|
||||
artdir = os.path.join(args.repo, "game", "assets", "art")
|
||||
outdir = os.path.join(args.repo, "game", "tests", "golden", "palette_baseline")
|
||||
if not os.path.isfile(ui_kit):
|
||||
print("[gen_palette_baseline] FAIL:ui_kit.gd 缺:%s" % ui_kit)
|
||||
return 2
|
||||
if not os.path.isdir(artdir):
|
||||
print("[gen_palette_baseline] FAIL:素材目录缺:%s" % artdir)
|
||||
return 2
|
||||
os.makedirs(outdir, exist_ok=True)
|
||||
|
||||
tcols = token_cols(ui_kit)
|
||||
n_t = write_strip(tcols, os.path.join(outdir, "ui_tokens.png"))
|
||||
|
||||
acols, npng = scan_dirs([artdir])
|
||||
if npng == 0:
|
||||
print("[gen_palette_baseline] FAIL:素材目录零 PNG(素材未同步)——拒绝生成空快照")
|
||||
return 2
|
||||
n_a = write_strip(acols, os.path.join(outdir, "art_snapshot.png"))
|
||||
|
||||
union = set(tcols) | set(acols)
|
||||
print("[gen_palette_baseline] 已生成:ui_tokens.png %d 色 / art_snapshot.png %d 色"
|
||||
"(素材 %d 张 PNG);基线并集 %d 色 → %s"
|
||||
% (n_t, n_a, npng, len(union), outdir))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
+11
-2
@@ -17,12 +17,21 @@
|
||||
|
||||
另:必须排除全透明像素 (0,0,0,0)(同standee_asset_check 的坑)。
|
||||
|
||||
基线(双门②「新增色 vs 既有色板」的既有色板机检承载物):
|
||||
目录 = game/tests/golden/palette_baseline/,两张条带 PNG(1×N,一色一像素列):
|
||||
ui_tokens.png = ui_kit.gd 全量 Color8 token 快照(token 板现役值)
|
||||
art_snapshot.png = 现役素材全量唯一色快照(像素板+各屏环境子板)
|
||||
两张并集=「既有色板」。缺一张即漏判——基线刷新必须重生成全套。
|
||||
门①语义检查属设计面人工核对(§4.2 色相语义判域),不在本器执法面。
|
||||
|
||||
用法:
|
||||
# 主用法:扫目录,新增色相对 --baseline 判定
|
||||
# 主用法(闸机 05b 色板闸承载):扫目录,新增色相对 --baseline 判定
|
||||
python tools/palette_check.py <目录> --baseline <基线目录>
|
||||
# 无基线:只出读数(单张色数/ 近色对计数),不判红
|
||||
python tools/palette_check.py <目录>
|
||||
退出码: 0 通过 / 1 有红灯(仅新增色违规或超单张上限时)
|
||||
# 基线建档与刷新:重扫现役素材∪token 重生成基线条带(素材批收尾动作,随批提交)
|
||||
python tools/gen_palette_baseline.py
|
||||
退出码: 0 通过 / 1 有红灯(仅新增色近色不过门②时)
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""B3 路径核对器(圣经 §8.3② 挂点实现 · 立绘定档批 2026-10-07)。
|
||||
"""B3 路径核对器(圣经 §8.3② 挂点实现)。
|
||||
|
||||
职责:扫描 asset-path-map.md 声明的全部路径 + 代码侧素材常量,
|
||||
报「悬空(声明存在但盘上无)/ 退役未清(仍引用已退役命名)」。
|
||||
|
||||
@@ -1209,6 +1209,18 @@ def run_screen_checks(scr, zones, board_origin, frames_strip, frames_meta, s6_im
|
||||
weakest = min(ratios, key=lambda r: r[1])
|
||||
check("S1", "整屏①全部文字对直接背景 ≥4.5:1(WCAG AA)", all(r[1] >= 4.5 for r in ratios),
|
||||
"最弱 %s=%.2f;全部 %s" % (weakest[0], weakest[1], ratios))
|
||||
# S1b 意图徽标四色墨对 PANEL_DATA 垫牌 ≥4.5:1(色值对表——镜像实装同值;垫牌=ui_kit PANEL_DATA #E9D9B8,
|
||||
# 与 draw_standee 徽标垫矩形 fill 同源;四墨=意图色表 damage/buff/debuff/charge 四值)
|
||||
panel_data = (233, 217, 184)
|
||||
intent_inks = [("攻击墨", (150, 44, 36)), ("增益墨", (30, 90, 150)),
|
||||
("减益墨", (122, 66, 148)), ("蓄力墨", (116, 80, 11))]
|
||||
iratios = [(n, round((max(wcag_lum(a), wcag_lum(panel_data)) + 0.05)
|
||||
/ (min(wcag_lum(a), wcag_lum(panel_data)) + 0.05), 2))
|
||||
for n, a in intent_inks]
|
||||
iweak = min(iratios, key=lambda r: r[1])
|
||||
check("S1b", "意图徽标四色墨对 PANEL_DATA 垫牌 ≥4.5:1(WCAG AA·色值对表)",
|
||||
all(r[1] >= 4.5 for r in iratios),
|
||||
"最弱 %s=%.2f;全部 %s" % (iweak[0], iweak[1], iratios))
|
||||
# S2 整屏②明度三档带 + 世界底层两子带(§2;舞台=实测区域读数,格底=实测板面;WCAG 0–1 域)
|
||||
stage_l = _region_wcag(scr, (300, 190, 600, 220))
|
||||
bx, by = board_origin
|
||||
|
||||
@@ -413,6 +413,14 @@ if [ $QUICK -eq 1 ] || [ $NONBEHAVIOR -eq 1 ]; then
|
||||
else
|
||||
gate "05 渲染闸" "render.log" bash "$REPO/tools/render_gate.sh" "$GAME"
|
||||
fi
|
||||
# ── 5b. 色板闸(§4.1 新色双门②:素材新增色 vs 基线〔token∪素材快照〕近色判红;
|
||||
# 门①语义检查属设计面人工核对不入机检;基线刷新=tools/gen_palette_baseline.py,素材批收尾动作)──
|
||||
if [ $QUICK -eq 1 ] || [ $NONBEHAVIOR -eq 1 ]; then
|
||||
NAMES+=("05b 色板闸"); EXITS+=("SKIP"); LOGS+=("")
|
||||
echo ""; echo "[gates] 05b 色板闸 → SKIP(--quick 或 影响面选闸:素材色板面不随测量面改动开闸)"
|
||||
else
|
||||
gate "05b 色板闸" "palette.log" "$PY" "$REPO/tools/palette_check.py" "$GAME/assets/art" --baseline "$REPO/game/tests/golden/palette_baseline"
|
||||
fi
|
||||
fi
|
||||
# ══ 文档域(6–8e)══
|
||||
if in_domain docs; then
|
||||
|
||||
Reference in new issue
Block a user