五批次 a08d89c/f9b0765→2d1388b:①T1 样张 v2(机检闸形 A 全过即冻结——新增 A3 Δ≥55/F6 双明度带/F7 DEPTH_PX 同源/A6 观感密度四断言,art-bible v2.13 落四冻结+UiKit 小人蓝同步 #2E86DE+图例旗色封印黑漏网修)②T2 框材组 8 件(♻ Kenney/OGA 全 CC0 量化改色入包,UiKit 单点工厂+五视图接线,✦ 背景横幅列缺料)③T3 演出四项(死亡碎裂/胜利纹章/失败掸灰/意图三档+结算光带缘线)+fx_probe 新探针④T4 数字形制冻结值落码(三档墨色温/字号 ≥18 公式/色牌 0.60·0.62)⑤T5 M-1 出口闸检查单(design/qa/m1-exit-gate-checklist-2026-09-28.md——真人目检八项+✦ 缺料清单)+06 两已解决条目移除+roadmap 挂起清单对齐。 验证:样张机检 16/16+冒烟 557/0(基线持平)+五探针(ui_input/ui_probe/ui_click/ui_tune/fx 新增)全 PASS+doc 三闸对基线零新增(lint 8=8/term 215=215/死链 0)+熵增闸 PASS。
235 lines
13 KiB
GDScript
235 lines
13 KiB
GDScript
# VERTICAL SLICE - NOT FOR PRODUCTION
|
||
# Date: 2026-09-11(2026-09-20 样式收敛批:全站色板与 StyleBox 工厂单点化)
|
||
# 呈现层共享小部件工厂 + 全站取色/描边唯一出口——各视图禁再内联 Color8/StyleBoxFlat
|
||
# 字面量;美术批换皮只改本文件(值 = 收敛时各视图现值快照,本批零视觉变化)。
|
||
class_name UiKit
|
||
extends RefCounted
|
||
|
||
# ── 色板(语义族;值 = 2026-09-20 收敛时现值) ──
|
||
const INK := Color8(51, 38, 27) # 墨族——正文/数字(对格底 ≥4.5:1)
|
||
const INK_COLD := Color8(19, 46, 54) # 青墨 1-3 #132E36(数字墨三档·冷=稀疏——样张 v2 定稿 2026-09-28)
|
||
const INK_OCHRE := Color8(60, 37, 12) # 赭墨 4-5 #3C250C(数字墨三档·中)
|
||
const INK_RUST := Color8(74, 30, 20) # 锈墨 6-8 #4A1E14(数字墨三档·暖=密集)
|
||
const INK_LABEL := Color8(46, 34, 24) # 标签深墨
|
||
const INK_TITLE := Color8(30, 26, 22) # 亮底标题墨
|
||
const INK_ROUTE := Color8(24, 20, 16) # 节点图最深墨
|
||
const INK_ON_GOLD := Color8(60, 44, 20) # 金底上的深墨
|
||
|
||
const PANEL_HERO := Color8(232, 220, 196) # 面板底·主角侧(英雄卡/角色信息框)
|
||
const PANEL_ENEMY := Color8(226, 212, 190) # 面板底·敌方卡
|
||
const PANEL_DATA := Color8(226, 210, 182) # 面板底·数据框(AP 条/状态槽)
|
||
const PANEL_STAGE := Color8(216, 200, 172) # 面板底·战斗画面区
|
||
const PANEL_BORDER := Color8(120, 106, 88) # 面板细线框;兼熄灭点阵(DOT_OFF 别名)
|
||
|
||
const GOLD := Color8(240, 168, 40) # 奖励金
|
||
const GOLD_DEEP := Color8(176, 122, 20) # 深金(金框描边/达意金)
|
||
const GOLD_LIT := Color8(230, 190, 90) # 亮金(候选高亮)
|
||
const GOLD_DIM := Color8(140, 116, 60) # 暗金(已过节点)
|
||
|
||
const SEAL_BLACK := Color8(0, 0, 0) # 封印黑 #000000——封印(插旗)宣告色(2026-09-28
|
||
# 用户拍板旗朱→封印紫→封印黑:紫色与微光蓝族色相冲突,黑色无色相天然避开禁入带
|
||
# 妨害紫 281° 各留 12-14° 间距;候选值样张批冻结;旗朱 #C63D22 退役)
|
||
const RED_DEEP := Color8(150, 60, 48) # 深朱(敌卡框/敌像)
|
||
const RED_MINE := Color8(150, 44, 36) # 引爆雷字朱(已开雷格"雷"字)
|
||
const RED_FORBID := Color8(120, 60, 50) # 禁止框朱
|
||
const RED_ALERT := Color8(200, 60, 48) # 报错朱
|
||
|
||
const HERO_BLUE := Color8(46, 134, 222) # 小人蓝 #2E86DE(样张 v2 定稿 2026-09-28——圣经 §4.1 候选值冻结;旧 #2E6E8E 收敛期快照退役)
|
||
const BOSS_PURPLE := Color8(143, 47, 106) # 紫赤(Boss 专属)
|
||
const DEBUFF_PURPLE := Color8(122, 66, 148) # 妨害紫(意图 ☠)
|
||
|
||
const DUSK := Color8(90, 108, 140) # 暮蓝(全作唯一暮蓝档——失败终局)
|
||
|
||
const WARM := Color8(240, 226, 200) # 暖白(暗底正文)
|
||
const WARM_SOFT := Color8(200, 188, 166) # 暖白次级(说明文)
|
||
const WARM_FAINT := Color8(178, 166, 146) # 暖白弱化(帮助文)
|
||
const WARM_ROUTE_SUB := Color8(210, 196, 170) # 节点图编号暖白
|
||
|
||
const STONE := Color8(120, 110, 98) # 灰石(通道/箭头/失效)
|
||
const STONE_FAINT := Color8(140, 124, 100) # 灰石弱化(占位提示文)
|
||
const DARK_UPCOMING := Color8(64, 58, 52) # 未达暗字(底/文同值)
|
||
const DARK_TEXT_FAINT := Color8(160, 150, 138)# 未达节点文字
|
||
|
||
const BRICK := Color8(201, 175, 131) # 棋盘·未开掩尘地砖 #C9AF83
|
||
const FLOOR := Color8(179, 154, 120) # 棋盘·已开裸土 #B39A78
|
||
const CONTACT_GLOW := Color8(214, 236, 255) # 可挖域接触带微光(蓝族浅一档·近白蓝——art-bible §3.2 v2.8 定稿;
|
||
# 旧绿相 DIG_EDGE 随画法切换退役 2026-09-26 批)
|
||
const CONTACT_GLOW_DEEP := Color8(150, 190, 230) # 接触带呼吸暗端(lerp 目标)
|
||
const DIGIT_PLATE := Color8(245, 234, 211) # 数字色牌(素色暖纸白直角牌——art-bible §4.1;数字一律垫牌,
|
||
# 可读不依赖格底——原则二;2026-09-26 呈现修复批落码)
|
||
const DIGIT_PLATE_EDGE := Color8(210, 198, 172) # 色牌牌缘线
|
||
const SLOT_BACKDROP := Color8(52, 44, 36) # B 区状态图标槽实底(深墨棕——白图标对比承载,2026-09-26 反馈④)
|
||
|
||
const BG_ROOT := Color8(30, 25, 20) # 屏幕根底
|
||
const BG_OVERLAY := Color8(28, 22, 18) # 模态遮罩底
|
||
const DIM := Color(0.08, 0.06, 0.05) # 遮罩基色(用时乘 alpha)
|
||
const TOAST_BG := Color(0.94, 0.91, 0.84, 0.95) # toast 面底
|
||
const TIP_BG := Color(0.16, 0.12, 0.09, 0.96) # tooltip 深底
|
||
|
||
# BBCode 十六进制(RichTextLabel 用;与上同值)
|
||
const HEX_GOLD := "f0a828"
|
||
const HEX_INK := "33261b"
|
||
const HEX_ON_GOLD := "3a2a08"
|
||
|
||
# 语义别名(同值异名——语义站点用别名,值仍单点)
|
||
const DOT_OFF := PANEL_BORDER
|
||
|
||
|
||
# ── 减动效(battle.md §Transitions 内建承诺:开启时时长减半;结算逐行兑现为信息载体降速不跳过)──
|
||
# 切片期入口 = TunePanel 开关(设置独立于存档两分制——正式设置层载体随设置批落位)
|
||
static var motion_scale := 1.0 # 1.0 常态 / 0.5 减动效
|
||
|
||
# 全部呈现层 tween 时长经本函数包装——新增动效一律 UiKit.motion(秒),禁再写裸时长字面量
|
||
static func motion(dur: float) -> float:
|
||
return dur * motion_scale
|
||
|
||
|
||
# ── 单位/特效纹理路径单点(棋盘小人贴图批 2026-09-25)──
|
||
# 素材 PNG 不入 git(用户裁定)——装载必须经 load_tex(exists 守卫),缺位返回 null 走各消费点
|
||
# 的字形/无演出回退路径;禁 preload(素材缺位树上直接断言炸脚本,冒烟安全红线)
|
||
const TEX_HERO_IDLE := "res://assets/art/character/character_hero_idle.png"
|
||
const TEX_HERO_WALK := "res://assets/art/character/character_hero_walk.png"
|
||
const TEX_HERO_PORTRAIT := "res://assets/art/character/character_hero_portrait.png" # B 区站桩立绘 48×48(源档 candidates/hero-idle/hero_idle_01.png,2026-09-26 用户点名换装)
|
||
# 敌种立绘(B 区站桩 2026-09-26 槽接线批)——文件键 = 数据表主键 enemy_id(圣经 §8.3 命名规范),
|
||
# 键集 = art-bible §4.6.4 八敌种;素材批入库前全部缺位(load_tex→null→字形回退),入库即显示零代码换装
|
||
const TEX_ENEMY_GOBLIN := "res://assets/art/character/character_enemy_goblin.png" # 地精
|
||
const TEX_ENEMY_BUGBEAR := "res://assets/art/character/character_enemy_bugbear.png" # 熊地精
|
||
const TEX_ENEMY_OGRE_CHIEF := "res://assets/art/character/character_enemy_ogre_chief.png" # 食人魔酋长
|
||
const TEX_ENEMY_GIANT_SPIDER := "res://assets/art/character/character_enemy_giant_spider.png" # 巨蜘蛛
|
||
const TEX_ENEMY_DRIDER := "res://assets/art/character/character_enemy_drider.png" # 蛛化卓尔精灵
|
||
const TEX_ENEMY_DUERGAR := "res://assets/art/character/character_enemy_duergar.png" # 灰矮人
|
||
const TEX_ENEMY_HOOK_HORROR := "res://assets/art/character/character_enemy_hook_horror.png" # 钩爪恐魔
|
||
const TEX_ENEMY_DUERGAR_KING := "res://assets/art/character/character_enemy_duergar_king.png" # 灰矮人石君王
|
||
|
||
# 框材组纹理槽(样张 v2 冻结批 T2 2026-09-28——♻ 复用件量化改色入包,产物 = tools/quantize_ui_skin.py,
|
||
# 全 CC0;缺位 = load_tex null → 程序化回退原路径,冒烟安全)
|
||
const TEX_PANEL_STONE := "res://assets/art/ui/panel_stone.png" # 石板亮面板九宫格 48×48
|
||
const TEX_BUTTON_STONE := "res://assets/art/ui/button_stone.png" # 按钮九宫格·常态(可按的砖)
|
||
const TEX_BUTTON_STONE_PRESSED := "res://assets/art/ui/button_stone_pressed.png" # 按钮九宫格·按压(下沉一档)
|
||
const TEX_BACKDROP_STONE := "res://assets/art/ui/backdrop_stone.png" # 石纹底噪 256×256 无缝
|
||
const TEX_BORDER_STONE := "res://assets/art/ui/border_stone.png" # 仪式石框(空心角括框)
|
||
const TEX_ICON_KEYSTONE := "res://assets/art/ui/icon_keystone.png" # 钥石图标(奖励金)
|
||
const TEX_ICON_FORGE := "res://assets/art/ui/icon_forge.png" # 柜台一·属性锻造
|
||
const TEX_ICON_WORKSHOP := "res://assets/art/ui/icon_workshop.png" # 柜台二·道具工坊
|
||
const TEX_ICON_ATLAS := "res://assets/art/ui/icon_atlas.png" # 柜台三·奖励格图鉴
|
||
|
||
|
||
# 素材纹理守卫装载(exists 检查避开缺位树上 ResourceLoader 报错刷屏——冒烟/无素材检出静默回退)
|
||
static func load_tex(path: String) -> Texture2D:
|
||
if ResourceLoader.exists(path):
|
||
return load(path)
|
||
return null
|
||
|
||
|
||
# CJK 随包字体(2026-09-27 用户拍板 Fusion Pixel OFL——CJK 覆盖+授权干净;命名遵 art-bible §8.3
|
||
# 全小写 snake_case 域前缀)。守卫装载同 load_tex 纪律:缺位返回 null(引擎回退字体接手,不炸)。
|
||
const FONT_UI := "res://assets/fonts/ui_font_fusion_pixel_zh_hans.ttf"
|
||
|
||
static func load_font(path: String) -> Font:
|
||
if ResourceLoader.exists(path):
|
||
var res: Resource = load(path)
|
||
return res as Font
|
||
return null
|
||
|
||
|
||
# ── StyleBox 工厂 ──
|
||
# margin = (左, 上, 右, 下);border_w = 0 即无边框(根底/遮罩用)
|
||
static func panel_flat(bg: Color, border: Color, corner: int = 0, border_w: int = 1, margin: Vector4 = Vector4.ZERO) -> StyleBoxFlat:
|
||
var sb := StyleBoxFlat.new()
|
||
sb.bg_color = bg
|
||
sb.set_corner_radius_all(corner)
|
||
sb.set_border_width_all(border_w)
|
||
sb.border_color = border
|
||
sb.content_margin_left = margin.x
|
||
sb.content_margin_top = margin.y
|
||
sb.content_margin_right = margin.z
|
||
sb.content_margin_bottom = margin.w
|
||
return sb
|
||
|
||
|
||
static func label(text: String, size: int, bold: bool) -> Label:
|
||
var l := Label.new()
|
||
l.text = text
|
||
l.add_theme_font_size_override("font_size", size)
|
||
if bold:
|
||
l.add_theme_color_override("font_color", INK_LABEL)
|
||
return l
|
||
|
||
|
||
static func button(text: String) -> Button:
|
||
var b := Button.new()
|
||
b.text = text
|
||
_apply_button_skin(b)
|
||
return b
|
||
|
||
|
||
# ── 框材纹理工厂(样张 v2 冻结批 T2——§7.1 容器穿戏服、数据素面;缺位回退 flat/主题默认) ──
|
||
# 九宫格 StyleBoxTexture(texture_margin 8px——quantize 脚本同值);tint 供消费点微调色调
|
||
static func _tex_sb(tex: Texture2D, margin: float, tint := Color.WHITE) -> StyleBoxTexture:
|
||
var sb := StyleBoxTexture.new()
|
||
sb.texture = tex
|
||
sb.texture_margin_left = margin
|
||
sb.texture_margin_top = margin
|
||
sb.texture_margin_right = margin
|
||
sb.texture_margin_bottom = margin
|
||
sb.modulate_color = tint
|
||
return sb
|
||
|
||
|
||
# 石板亮面板(B 区舞台/C 区主面板/图例——容器穿戏服;数据框仍 panel_flat 素面 §7.1)
|
||
static func panel_skinned(margin: Vector4 = Vector4.ZERO, tint := Color.WHITE) -> StyleBox:
|
||
var tex := load_tex(TEX_PANEL_STONE)
|
||
if tex == null:
|
||
return panel_flat(PANEL_DATA, PANEL_BORDER, 4, 1, margin)
|
||
var sb := _tex_sb(tex, 8.0, tint)
|
||
sb.content_margin_left = margin.x
|
||
sb.content_margin_top = margin.y
|
||
sb.content_margin_right = margin.z
|
||
sb.content_margin_bottom = margin.w
|
||
return sb
|
||
|
||
|
||
# 仪式石框(空心角括框——营地容器框;缺位回退暗面板)
|
||
static func frame_skinned(margin: Vector4 = Vector4.ZERO) -> StyleBox:
|
||
var tex := load_tex(TEX_BORDER_STONE)
|
||
if tex == null:
|
||
return panel_flat(BG_OVERLAY, PANEL_BORDER, 6, 1, margin)
|
||
var sb := _tex_sb(tex, 12.0)
|
||
sb.content_margin_left = margin.x
|
||
sb.content_margin_top = margin.y
|
||
sb.content_margin_right = margin.z
|
||
sb.content_margin_bottom = margin.w
|
||
return sb
|
||
|
||
|
||
# 图标槽(营地钥石/柜台件——缺位返回 null,消费点不挂 TextureRect 即回退纯文本)
|
||
static func icon_rect(tex_path: String, px: float) -> TextureRect:
|
||
var t := load_tex(tex_path)
|
||
if t == null:
|
||
return null
|
||
var tr := TextureRect.new()
|
||
tr.texture = t
|
||
tr.custom_minimum_size = Vector2(px, px)
|
||
tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
tr.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||
return tr
|
||
|
||
|
||
# 按钮皮(常态/按压两态+墨字——缺位 = 引擎默认主题,素材批前逐位一致)
|
||
static func _apply_button_skin(b: Button) -> void:
|
||
var tex_n := load_tex(TEX_BUTTON_STONE)
|
||
if tex_n == null:
|
||
return
|
||
var tex_p := load_tex(TEX_BUTTON_STONE_PRESSED)
|
||
b.add_theme_stylebox_override("normal", _tex_sb(tex_n, 8.0))
|
||
b.add_theme_stylebox_override("hover", _tex_sb(tex_n, 8.0))
|
||
if tex_p != null:
|
||
b.add_theme_stylebox_override("pressed", _tex_sb(tex_p, 8.0))
|
||
b.add_theme_stylebox_override("disabled", _tex_sb(tex_p, 8.0))
|
||
b.add_theme_color_override("font_color", INK)
|
||
b.add_theme_color_override("font_hover_color", INK)
|
||
b.add_theme_color_override("font_pressed_color", INK)
|
||
b.add_theme_color_override("font_focus_color", INK)
|
||
b.add_theme_color_override("font_disabled_color", Color(INK, 0.45))
|