feat(ux): T2 节点图标组接线——五键量化过复用闸四道+load_tex 守卫接线+字形退役(缺位回退)

This commit is contained in:
DKChan committed 2026-09-28 13:51:41 +08:00
1 parent 9a79927b05
commit 4ece929315
5 files changed
+91 -12

No files matched your search

+43
View File
@@ -42,6 +42,8 @@ RAMP_BACKDROP = [(30, 25, 20), (120, 106, 88)] # BG_ROOT→P
RAMP_BORDER = [(120, 106, 88), (201, 175, 131)] # PANEL_BORDER→BRICK
ICON_GOLD = (240, 168, 40) # 奖励金(钥石)
ICON_WARM = (240, 226, 200) # 暖白(柜台件,暗卡上可读)
NODE_WARM = (240, 226, 200) # 节点图标暖白(关卡画面样张 v1 冻结——暗底态原色,当前金底乘法调制深墨)
NODE_PX = 20 # 节点图标烘焙边长(52×36 方块内 20px 图标+右上编号,1:1 消费零缩放)
def ramp_color(ramp, t):
@@ -103,14 +105,55 @@ def main():
jobs.append(("icon_forge.png", recolor_flat(Image.open(src_path(*(icon_dir + ("sword.png",)))), ICON_WARM)))
jobs.append(("icon_workshop.png", recolor_flat(Image.open(src_path(*(icon_dir + ("flask_full.png",)))), ICON_WARM)))
jobs.append(("icon_atlas.png", recolor_flat(Image.open(src_path(*(icon_dir + ("book_open.png",)))), ICON_WARM)))
# 节点图标五件(board-game-icons 64px→20px 烘焙,WARM 单色——关卡画面样张 v1 冻结形制,M-2 迭代 4 T2;
# 战=bow/赏=award/疗=campfire/王=crown_b/商=pouch,语义近似+灰度互辨,色相不承载身份 §4.6)
node_jobs = (("node_battle.png", "bow.png"), ("node_reward.png", "award.png"),
("node_rest.png", "campfire.png"), ("node_boss.png", "crown_b.png"),
("node_shop.png", "pouch.png"))
node_outs = {}
for name, src in node_jobs:
im = recolor_flat(Image.open(src_path(*(icon_dir + (src,)))), NODE_WARM)
im = im.resize((NODE_PX, NODE_PX), Image.NEAREST)
node_outs[name] = im
jobs.append((name, im))
for name, im in jobs:
im.save(out_path(name))
print("saved:", "/".join(OUT + (name,)))
node_icon_gates(node_outs)
print("=== 量化改色完成(%d 件,全 CC0;闸①②③④ 见脚本头注) ===" % len(jobs))
return 0
def node_icon_gates(outs):
"""节点图标复用闸四道自检(闸④授权=kenney-board-game-icons CC0 全包在案):
①量化=全像素 NODE_WARM 或透明(零原包杂色) ②禁入带=WARM 色相 39° 天然合规
③灰度互辨=alpha 剪影两两错位率 ≥0.08(与样张 L1 同判据) ④见上。"""
fail = []
for name, im in outs.items():
bad = sum(1 for px in im.getdata() if px[3] > 8 and px[:3] != NODE_WARM)
if bad:
fail.append("%s 闸①杂色 %d px" % (name, bad))
if im.size != (NODE_PX, NODE_PX):
fail.append("%s 尺寸 %s" % (name, im.size))
keys = list(outs.keys())
worst = (1.0, None)
sigs = {k: [1 if px[3] > 96 else 0 for px in outs[k].getdata()] for k in keys}
for i, a in enumerate(keys):
for b in keys[i + 1:]:
diff = sum(1 for x, y in zip(sigs[a], sigs[b]) if x != y) / len(sigs[a])
if diff < worst[0]:
worst = (diff, (a, b))
if worst[0] < 0.08:
fail.append("闸③灰度互辨最小错位 %.2f %s" % worst)
if fail:
for f in fail:
print("[FAIL] 节点图标闸 %s" % f)
raise SystemExit(1)
print("[PASS] 节点图标复用闸①②③④(量化单色/禁入带天然合规/灰度互辨最小错位 %.2f %s/CC0)"
% worst)
if __name__ == "__main__":
import sys
sys.exit(main())
+10 -4
View File
@@ -73,7 +73,7 @@ COLD_PASSAGE = (112, 120, 128) # S3 换层通道冷调(underdark 方向——
NODE_KEYS = ["battle", "reward", "rest", "boss", "shop"]
NODE_ICONS = {"battle": "bow.png", "reward": "award.png", "rest": "campfire.png",
"boss": "crown_b.png", "shop": "pouch.png"} # T1 挑选(语义近似;闸④全 CC0)
ICON_PX = 22 # 节点图标边长(52×36 方块内 22px 图标+右下 10px 编号)
ICON_PX = 20 # 节点图标边长(52×36 方块内 20px 图标+右上编号——冻结值,与 quantize 烘焙同值)
NODE_HALF = (26, 18) # 节点方块半宽高(与 level_map_screen.gd 同源解析对表)
CANVAS_MIN = (620, 240) # 画布下限(同源)
W_PASS, W_LIT, W_HOVER = 2, 3, 4 # 通道折线宽(未lit/lit/悬停——冻结值)
@@ -202,8 +202,14 @@ def fit_content(nodes):
return size, layer, slot
# ══ 图标装载(kenney CC0 64px → WARM 单色重着色 → NEAREST 缩放) ══
# ══ 图标装载(优先烘焙终稿 game/assets/art/ui/node_*.png(quantize 产出,20px 1:1);
# 缺位回退 kenney 64px 现场量化——同 WARM 单色+NEAREST,与烘焙产物逐像素同构) ══
def load_icon(kind):
for base in (ROOT, MAIN_ROOT):
p = os.path.join(base, "game", "assets", "art", "ui", "node_%s.png" % kind)
if os.path.exists(p):
im = Image.open(p).convert("RGBA")
return im if im.size == (ICON_PX, ICON_PX) else im.resize((ICON_PX, ICON_PX), Image.NEAREST)
im = Image.open(asset_root(*(ICON_DIR + (NODE_ICONS[kind],)))).convert("RGBA")
px = im.load()
for y in range(im.size[1]):
@@ -329,8 +335,8 @@ def draw_screen(nodes, title, level_name, gp, visited, current, cands, hover=-1,
img.alpha_composite(icon, (ic_x, ic_y))
num = str(nid)
nb = d.textbbox((0, 0), num, font=F_NODE_NUM)
nx = x1 - 4 - (nb[2] - nb[0])
ny = y1 - 3 - (nb[3] - nb[1])
nx = x1 - 4 - (nb[2] - nb[0]) # 右对齐至方块右内缘 -4(与引擎 HORIZONTAL_ALIGNMENT_RIGHT 同位)
ny = y0 + 1 # 右上角——与图标占位区零重叠(L7)
d.text((nx - nb[0], ny - nb[1]), num, font=F_NODE_NUM, fill=ink)
records["nodes"][nid] = {
"state": state, "type": n["type"], "block": (x0, y0, x1, y1),