563 lines
33 KiB
GDScript
563 lines
33 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(数字墨三档·暖=密集;亮瓦自适应深墨族档兼色牌后备路径)
|
||
# ── 亮墨三档(圣经 v3.5 冻结④ 2026-09-30——数字直画已开格底:色温承袭密度语义,冷亮/暖白/金亮;
|
||
# 实测对比 6.0–6.3:1(A3a);金亮低饱和避奖励金语义;色牌后备组用深墨族上牌〔12:1〕。
|
||
# 2026-10-01 修订:亮墨族限暗纹理板(dungeon),亮筋纹板按 _digit_ink_mode 自适应切深墨三档)──
|
||
const INK_L_COLD := Color8(216, 236, 240) # 冷亮 1-3 #D8ECF0
|
||
const INK_L_WARM := Color8(240, 226, 200) # 暖白 4-5 #F0E2C8(关卡屏 WARM 同族)
|
||
const INK_L_GOLD := Color8(248, 227, 168) # 金亮 6-8 #F8E3A8
|
||
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_DATA := Color8(233, 217, 184) # 面板底·数据框(AP 条/状态槽)#E9D9B8(P7 乙 2026-10-06——demo 唯一来源对齐,Δ≤6/255)
|
||
const PANEL_BORDER := Color8(120, 110, 88) # 面板细线框;兼熄灭点阵(DOT_OFF 别名)#786E58(P7 乙同上)
|
||
|
||
const GOLD := Color8(240, 168, 40) # 奖励金
|
||
const GOLD_DEEP := Color8(176, 122, 20) # 深金(金框描边/达意金)
|
||
const GOLD_INK := Color8(116, 80, 11) # 深金墨(意图蓄力徽标墨——PANEL_DATA 垫牌上 4.9:1,UI 设计对齐批 N1 2026-10-06)
|
||
const GOLD_LIT := Color8(230, 190, 90) # 亮金(候选高亮)
|
||
const GOLD_DIM := Color8(140, 116, 60) # 暗金(已过节点)
|
||
const GOLD_DONE := Color8(104, 86, 44) # 已过节点暗金·关卡画面呈现态(关卡画面样张 v1 冻结新值 #68562C——旧 GOLD_DIM #8C743C 对双色墨均 <4.5:1 对比硬指标逼守;GOLD_DIM 他用不动)
|
||
|
||
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 HERO_BLUE_DEEP := Color8(30, 90, 150) # 小人蓝同族深档(意图增益徽标墨——PANEL_DATA 垫牌上 4.8:1;命名同 CONTACT_GLOW_DEEP 规则,UI 设计对齐批 N1 2026-10-06)
|
||
const BOSS_PURPLE := Color8(143, 47, 106) # 紫赤(Boss 专属)
|
||
const DEBUFF_PURPLE := Color8(122, 66, 148) # 妨害紫(意图 ☠)
|
||
|
||
# ── 类型色五族(UI 设计对齐批 N4c 2026-10-06——值 = demo --tg-* 冻结抄录〔任务书 §2.2 抄录表〕;
|
||
# 前四族 = 实机现值搬运、第五族群伤 demo 值补族——实机原完全缺位,消费面 = tg_band() 同源判定)──
|
||
const TG_BUFF := Color8(194, 178, 100) # 通用增益/默认带
|
||
const TG_HEAL := Color8(102, 164, 87) # 回复
|
||
const TG_MAGIC := Color8(138, 114, 196) # 魔法
|
||
const TG_PIERCE := Color8(87, 160, 160) # 穿甲
|
||
const TG_AOE := Color8(226, 86, 118) # 群伤/AOE #E25676(demo --tg-aoe)
|
||
|
||
# 类型色带分族判定(N4c/Q4 甲——三选一与商店货架同源同判:id/kind 小写子串匹配,默认=通用增益带;
|
||
# blast=爆裂群伤归 TG_AOE(reward.tres 现成 clear_blast/spark_aoe 消费面);max_hp 含 hp 归回复族)
|
||
static func tg_band(id_lower: String) -> Color:
|
||
if id_lower.contains("heal") or id_lower.contains("hp"):
|
||
return TG_HEAL
|
||
if id_lower.contains("aoe") or id_lower.contains("blast"):
|
||
return TG_AOE
|
||
if id_lower.contains("magic"):
|
||
return TG_MAGIC
|
||
if id_lower.contains("pierce"):
|
||
return TG_PIERCE
|
||
return TG_BUFF
|
||
|
||
# ── 呈现散件裸色搬运(UI 设计对齐批 N4a 2026-10-06——原值搬运零观感;本节立后 ui_kit 之外
|
||
# 呈现层裸 Color8 = 0,守卫 = ui_contrast_probe 静态段)──
|
||
const INK_HINT := Color8(110, 69, 52) # 提示/警示暖棕墨(设置警示文/地图底栏组标)
|
||
const INK_ON_DEEP := Color8(246, 238, 218) # 深底标题亮墨(主菜单标题/营地柜台等级章字)
|
||
const TITLE_PEDESTAL := Color8(90, 74, 56) # 标题屏底座石台面(椭圆 17 底座;描边=BG_ROOT)
|
||
const MAP_BAR_PLATE := Color8(240, 228, 200) # 关卡地图底栏亮板
|
||
const MAP_VDIV := Color8(201, 184, 142) # 关卡地图底栏竖分隔线(1px)
|
||
const HINT_STRIP_EDGE := Color8(74, 62, 48) # 棋盘底提示条描边(半透明罩层板边)
|
||
const RAP_DOT_CORE_WARM := Color8(248, 226, 168) # AP 点阵亮心(烛光黄族)
|
||
const RAP_DOT_LIT_BLUE := Color8(105, 161, 232) # RAP 点阵亮面(小人蓝族)
|
||
const RAP_DOT_CORE_BLUE := Color8(156, 194, 240) # RAP 点阵亮心(小人蓝族)
|
||
const E_FACADE_FOOT_COL := Color8(52, 42, 32) # 墙脚阴线(方向稿同色——原 board_cell_unit 局部)
|
||
const FOG_MEMORY_PLATE := Color8(58, 58, 58) # 记忆板 #3A3A3A(§6.5——原 board_cell_unit 局部)
|
||
const FOG_MEMORY_INK := Color8(196, 196, 196) # 记忆墨 #C4C4C4(对记忆板 ≈6.3:1——原局部)
|
||
const FOG_OUTLINE := Color8(30, 24, 18) # 雾面轮廓墨(原 board_cell_unit _draw 局部 oc)
|
||
const ICON_TINT_ON_GOLD := Color(24.0 / 240.0, 20.0 / 226.0, 16.0 / 200.0) # 当前金底图标墨 = WARM 分量乘法调制(双色墨制——原 level_map_screen 局部)
|
||
|
||
const DUSK := Color8(90, 108, 140) # 暮蓝(全作唯一暮蓝档——失败终局)
|
||
|
||
const WARM := Color8(240, 226, 200) # 暖白(暗底正文)
|
||
const WARM_SOFT := Color8(232, 213, 168) # 暖白次级(说明文)#E8D5A8(M0 换代——灰米 #C8BCA6 退役,配色总纲「米色去灰」)
|
||
const WARM_FAINT := Color8(205, 184, 140) # 暖白弱化(帮助文)#CDB88C(M0 换代,同上)
|
||
|
||
const STONE := Color8(120, 110, 98) # 灰石(通道/箭头/失效)
|
||
const STONE_FAINT := Color8(140, 124, 100) # 灰石弱化(占位提示文)
|
||
const STONE_DEEP := Color8(85, 77, 65) # 灰石深化(「已用」槽位墨——PANEL_DATA 槽牌 5.6:1 / 石皮 5.0:1,UI 设计对齐批 N1 2026-10-06;旧 STONE_FAINT 对亮底双向 <4.5:1)
|
||
const DARK_UPCOMING := Color8(64, 58, 52) # 未达暗字(底/文同值)
|
||
|
||
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(62, 54, 44) # B 区状态图标槽实底(深墨棕——白图标对比承载)#3E362C(P7 乙 2026-10-06 对齐)
|
||
|
||
const BG_ROOT := Color8(58, 46, 34) # 屏幕根底 #3A2E22(UI 落码批 M0 2026-10-04——配色总纲「提暖提亮禁近黑」,旧近黑 #1E1914 退役)
|
||
const BG_OVERLAY := Color8(54, 43, 32) # 模态遮罩底 #362B20(同批换代,旧 #1C1612 退役)
|
||
const BG_BRIGHT := Color8(69, 55, 35) # 暖亮档底 #453723(M0 新增——标题/营地/商店等暖亮屏根底)
|
||
const CANDLE := Color8(246, 198, 103) # 烛光黄 #F6C667(M0 新增——主操作色/候选节点/钥石徽章底;
|
||
# 与 §4.1 奖励金 #F0A828 分轨——圣经 §7.2 琥珀双岗注记)
|
||
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 深底
|
||
|
||
# 呈现杂色档(T4 裸色收口批 2026-10-02——各视图裸 Color8 字面量单点化;值 = 收口时各来源行现值快照)
|
||
const STAGE_BG := Color8(62, 52, 42) # B 区舞台暗底(方向稿 render_battle_screen——battle_stage_view 面板底)
|
||
const BEAM_LIGHT := Color8(255, 236, 200) # B 区顶光柱暖光(用时乘 alpha 渐隐——battle_stage_view 光柱层)
|
||
const STAND_STONE := Color8(150, 130, 100) # 站桩石板地台·板身(standee_body 方向稿取色)
|
||
const STAND_STONE_EDGE := Color8(90, 76, 58) # 站桩石板地台·描边
|
||
const STAND_STONE_RIM := Color8(196, 172, 132) # 站桩石板地台·顶缘受光线(board_cell_unit 口沿高光已并档至此——清扫批 2026-10-02)
|
||
const CAMP_HEADER_BAND := Color8(110, 69, 38) # 营地柜台头带深棕 #6E4526(4-camp.png 方向稿取色——P7 乙 2026-10-06 对齐 demo --wood-deep)
|
||
const CAMP_ROW_PLATE := Color8(240, 230, 206) # 营地行板暖米(同上)
|
||
const CAMP_BADGE_PANEL := Color8(40, 32, 24) # 营地钥石徽暗底(同上)
|
||
const HP_BAR_BACK := Color8(40, 34, 32) # 迷你血条深底 #282220(关卡画面样张 v1 冻结——P7 乙 2026-10-06 对齐;兼 N1 血数垫牌)
|
||
|
||
# BBCode 十六进制(RichTextLabel 用;与上同值)
|
||
const HEX_GOLD := "f0a828"
|
||
const HEX_INK := "33261b"
|
||
const HEX_ON_GOLD := "3a2a08"
|
||
# 结算行/叙事行语义档(T4 收口批——battle_log_view 行格式化与 narrator 横幅行的裸 hex 单点化;
|
||
# 名随呈现语义、值 = 2026-09 起各行现值快照,色值逐字节不变)
|
||
const HEX_LOG_ROUND := "9a8a72" # 轮次前缀/零值行暖灰石
|
||
const HEX_LOG_GAIN := "e0b860" # 收益金(GP 袋/商店行/败判定/节点横幅)
|
||
const HEX_LOG_LOSS := "c47c7c" # 损失红(GP 流失/负效果)
|
||
const HEX_LOG_HEAL := "7cc47c" # 治疗绿(效果正值/治疗行/恢复点行)
|
||
const HEX_LOG_DMG_NUM := "d9402a" # 伤害数字朱(放大加色——数字即信息主角)
|
||
const HEX_LOG_DMG_KILL := "e07050" # 击杀行正文橙红
|
||
const HEX_LOG_DMG_BODY := "e0a050" # 伤害行正文橙
|
||
const HEX_LOG_VOIDED := "b08060" # 未生效行暗橙(⚠ 划线)
|
||
const HEX_LOG_CAST := "b09a72" # 施放行暖石(✦ 具名技能)
|
||
const HEX_LOG_DECAY := "8a9ab0" # 状态衰减蓝灰
|
||
const HEX_LOG_WIN := "80d080" # 胜利判定绿(模拟报告通关率同档)
|
||
const HEX_ROUTE_NOTE := "d0c090" # 奖励点预告暗金(narrator 行)
|
||
const HEX_SEED_NOTE := "c8a860" # 种子/开场行暗金(narrator 行)
|
||
|
||
# 语义别名(同值异名——语义站点用别名,值仍单点)
|
||
const DOT_OFF := PANEL_BORDER
|
||
|
||
|
||
# ── 减动效(battle.md §Transitions 内建承诺:开启时时长减半;结算逐行兑现为信息载体降速不跳过)──
|
||
# 唯一入口 = 设置面开关(迭代 8 卡④ 转正;切片期调整板开关已随其退役,调整板本体 14.5 收进调试浮窗)
|
||
static var motion_scale := 1.0 # 1.0 常态 / 0.5 减动效
|
||
static var victory_fx_style := "crest" # 胜利演出风格(P-C5 两套并存——"crest"碎砖拼纹章[默认冻结件]/"dust"尘金粒子;切换=调试浮窗,会话级)
|
||
|
||
# 全部呈现层 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 区站桩立绘·normal 态(48px 档,迭代 6 T3 换装=new-normal 源)
|
||
const TEX_HERO_ATTACK := "res://assets/art/character/character_hero_attack.png" # B 区站桩立绘·attack 态(迭代 6 T3 三态切换)
|
||
const TEX_HERO_HIT := "res://assets/art/character/character_hero_hit.png" # B 区站桩立绘·hit 态(迭代 6 T3 三态切换)
|
||
# 敌种立绘(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" # 柜台三·奖励格图鉴
|
||
# 节点图标五键(关卡画面样张 v1 冻结批 M-2 迭代 4 T2——20px 烘焙 1:1 消费,WARM 单色;
|
||
# 对象键 = node_type 文件侧延伸 §8.3;缺位 = load_tex null → 字形回退)
|
||
const TEX_NODE_BATTLE := "res://assets/art/ui/node_battle.png" # 节点图标·战斗房
|
||
const TEX_NODE_REWARD := "res://assets/art/ui/node_reward.png" # 节点图标·奖励房
|
||
const TEX_NODE_REST := "res://assets/art/ui/node_rest.png" # 节点图标·恢复房
|
||
const TEX_NODE_BOSS := "res://assets/art/ui/node_boss.png" # 节点图标·Boss 房
|
||
const TEX_NODE_SHOP := "res://assets/art/ui/node_shop.png" # 节点图标·商店房
|
||
# 手绘功能图标十键(UI 落码批 M1 2026-10-04——20×20 像素 1:1/整数倍消费〔§8.2 甲类纪律〕;
|
||
# 源=demo 原型 src 盘上件搬运;缺位 = load_tex null → 字形回退不炸)
|
||
const TEX_ICON_SWORD := "res://assets/art/ui/icon_sword.png" # 图标·攻击系(锻造行/攻击强化卡)
|
||
const TEX_ICON_SHIELD := "res://assets/art/ui/icon_shield.png" # 图标·护甲系
|
||
const TEX_ICON_HEART := "res://assets/art/ui/icon_heart.png" # 图标·生命系
|
||
const TEX_ICON_SHOVEL := "res://assets/art/ui/icon_shovel.png" # 图标·铲(挖掘系商品/锻造)
|
||
const TEX_ICON_MEDKIT := "res://assets/art/ui/icon_medkit.png" # 图标·医疗包
|
||
const TEX_ICON_POUCH := "res://assets/art/ui/icon_pouch.png" # 图标·弹药袋/行动点
|
||
const TEX_ICON_BLAST := "res://assets/art/ui/icon_blast.png" # 图标·工兵爆破(秘宝)
|
||
const TEX_ICON_COINBAG := "res://assets/art/ui/icon_coinbag.png" # 图标·金币袋
|
||
const TEX_ICON_KEYSHARD := "res://assets/art/ui/icon_keyshard.png" # 图标·钥石残片
|
||
const TEX_ICON_REFRESH := "res://assets/art/ui/icon_refresh.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 即无边框(根底/遮罩用)
|
||
# 圆角率单点(UI 设计对齐批 P5 甲 2026-10-06——容器/按钮全站唯一圆角值;数据框/数字牌/AP 点阵/准线/血条
|
||
# =0 直角不变;底座椭圆 17/结算金印 200/状态脉冲环 100 圆形 = §7.1 明示例外保留。取值=代拍板 5,可推翻)
|
||
const RADIUS_OBJECT := 5
|
||
# 九宫格切片(P4 乙 2026-10-06——panel 石皮 8→16 与 demo .plate9 slice16 对齐;button 维持 8、仪式框维持 12)
|
||
const PANEL_SLICE := 16.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
|
||
|
||
|
||
# color 缺省哨兵 = alpha<0(真实用色不取负 alpha)——传色即一次到位(旧「label() 后跟
|
||
# add_theme_color_override」两行式收单行);省略时保持旧默认:bold=INK_LABEL / 非 bold=主题缺省
|
||
static func label(text: String, size: int, bold: bool, color: Color = Color(-1, -1, -1, -1)) -> Label:
|
||
var l := Label.new()
|
||
l.text = text
|
||
l.add_theme_font_size_override("font_size", size)
|
||
if color.a >= 0.0:
|
||
l.add_theme_color_override("font_color", color)
|
||
elif bold:
|
||
l.add_theme_color_override("font_color", INK_LABEL)
|
||
return l
|
||
|
||
|
||
# ── 模态壳工厂(T4 收口批 2026-10-02——各 overlay 手搓 dim+Center+VBox/Panel 骨架单点化)──
|
||
# dim = 全屏半透明遮罩(全屏锚定纪律同既有各 overlay);center 全屏锚定居中;with_panel = 内容包
|
||
# PanelContainer(panel_sb 缺省 = 主题默认板——settlement_review 同款);box = 内容列
|
||
#(min_size/separation 参数化,min_size ZERO = 不设)。返回 Dictionary{dim, center, panel, box}
|
||
#——探针直读的私有件由调用方按名摘引,结构同名同层级不变。
|
||
static func modal_dim(parent: Control, dim_color: Color) -> ColorRect:
|
||
var dim := ColorRect.new()
|
||
dim.color = dim_color
|
||
dim.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||
parent.add_child(dim)
|
||
return dim
|
||
|
||
|
||
static func modal_shell(parent: Control, dim_color: Color, min_size := Vector2.ZERO, separation := 12,
|
||
with_panel := false, panel_sb: StyleBox = null) -> Dictionary:
|
||
var dim := modal_dim(parent, dim_color)
|
||
var center := CenterContainer.new()
|
||
center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||
dim.add_child(center)
|
||
var panel: PanelContainer = null
|
||
var host: Control = center
|
||
if with_panel:
|
||
panel = PanelContainer.new()
|
||
if panel_sb != null:
|
||
panel.add_theme_stylebox_override("panel", panel_sb)
|
||
center.add_child(panel)
|
||
host = panel
|
||
var box := VBoxContainer.new()
|
||
if min_size != Vector2.ZERO:
|
||
box.custom_minimum_size = min_size
|
||
box.add_theme_constant_override("separation", separation)
|
||
host.add_child(box)
|
||
return {"dim": dim, "center": center, "panel": panel, "box": box}
|
||
|
||
|
||
# 属性百分比合成(main._debug_hero_override 与调试浮窗生效值预览共用单点——两处同式实现收口)
|
||
static func scaled(base: int, pct: int) -> int:
|
||
return maxi(1, int(round(float(base) * (100.0 + float(pct)) / 100.0)))
|
||
|
||
|
||
# 宽度纪律(ui-struct-probe 批 T3——全站按钮最小宽度基准 216+SIZE_FILL:VBox 列内拉满列宽等宽,
|
||
# HBox 行内不抢余宽(EXPAND_FILL 会把行撑爆——营地底条 1616px 实证);角落小件/紧凑位传 0 豁免)
|
||
static func button(text: String, min_w := 216.0) -> Button:
|
||
var b := Button.new()
|
||
b.text = text
|
||
if min_w > 0.0:
|
||
b.custom_minimum_size = Vector2(min_w, 0)
|
||
b.size_flags_horizontal = Control.SIZE_FILL
|
||
_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)
|
||
# Q1 丙(2026-10-06):content margin 低于切片的分量自动抬到切片——切片加大到 16 后石框纹理不吃内容
|
||
# (T0 实查 9 消费面 7 处 margin<16;工厂单点 clamp,调用点零改)
|
||
static func panel_skinned(margin: Vector4 = Vector4.ZERO, tint := Color.WHITE) -> StyleBox:
|
||
var m := Vector4(maxf(margin.x, PANEL_SLICE), maxf(margin.y, PANEL_SLICE),
|
||
maxf(margin.z, PANEL_SLICE), maxf(margin.w, PANEL_SLICE))
|
||
var tex := load_tex(TEX_PANEL_STONE)
|
||
if tex == null:
|
||
return panel_flat(PANEL_DATA, PANEL_BORDER, RADIUS_OBJECT, 1, m)
|
||
var sb := _tex_sb(tex, PANEL_SLICE, tint)
|
||
sb.content_margin_left = m.x
|
||
sb.content_margin_top = m.y
|
||
sb.content_margin_right = m.z
|
||
sb.content_margin_bottom = m.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,RADIUS_OBJECT, 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))
|
||
|
||
|
||
# ── UI 落码批 M10 公共件(2026-10-04——bcard 竖卡工厂/拨杆开关/样式滑杆/动词按钮/tooltip 主题;
|
||
# 消费方 = M2 标题/M3 营地/M4 设置/M6 商店/M7 三选一。单点纪律同 §7.1:新件只加不改旧件)──
|
||
|
||
# 动词按钮(图标+动词+悬停说明三件套——M2 标题屏按钮组/通用;icon 缺位=纯文字不炸)
|
||
static func verb_button(text: String, icon_path: String, tip := "") -> Button:
|
||
var b := button(text)
|
||
var tex := load_tex(icon_path)
|
||
if tex != null:
|
||
b.icon = tex
|
||
b.add_theme_constant_override("icon_max_width", 20)
|
||
b.expand_icon = false
|
||
if tip != "":
|
||
b.tooltip_text = tip
|
||
return b
|
||
|
||
|
||
# 主操作钮(烛光黄平涂+木描边——非奖励金;配色总纲「主按钮禁亮金」;缺 panel_stone 依赖=纯色可靠)
|
||
static func primary_button(text: String, tip := "") -> Button:
|
||
var b := button(text)
|
||
var sb := panel_flat(CANDLE, Color8(110, 69, 52),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2))
|
||
b.add_theme_stylebox_override("normal", sb)
|
||
b.add_theme_stylebox_override("hover", panel_flat(CANDLE.lightened(0.06), Color8(110, 69, 52),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
|
||
b.add_theme_stylebox_override("pressed", panel_flat(Color8(214, 172, 88), Color8(110, 69, 52),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
|
||
b.add_theme_color_override("font_color", Color8(58, 42, 8))
|
||
b.add_theme_color_override("font_hover_color", Color8(58, 42, 8))
|
||
b.add_theme_color_override("font_pressed_color", Color8(58, 42, 8))
|
||
b.add_theme_color_override("font_focus_color", Color8(58, 42, 8))
|
||
if tip != "":
|
||
b.tooltip_text = tip
|
||
return b
|
||
|
||
|
||
# 危险钮(深红平涂——放弃/删除类;破坏性确认位置纪律=不得与常规确认同位)
|
||
static func danger_button(text: String, tip := "") -> Button:
|
||
var b := button(text)
|
||
var sb := panel_flat(Color8(180, 78, 54), Color8(110, 42, 28),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2))
|
||
b.add_theme_stylebox_override("normal", sb)
|
||
b.add_theme_stylebox_override("hover", panel_flat(Color8(196, 90, 62), Color8(110, 42, 28),RADIUS_OBJECT, 2, Vector4(10, 2, 10, 2)))
|
||
b.add_theme_color_override("font_color", Color8(255, 244, 224))
|
||
b.add_theme_color_override("font_hover_color", Color8(255, 244, 224))
|
||
b.add_theme_color_override("font_pressed_color", Color8(255, 244, 224))
|
||
if tip != "":
|
||
b.tooltip_text = tip
|
||
return b
|
||
|
||
|
||
# bcard 竖卡工厂(商店/三选一同构卡——M6/M7 共用;M7.1 差异仅=价签 vs ✓选择章)
|
||
# 返回 {panel, mark, band, icon, name_l, desc_l, price_l, foot_l};set_selected 经 mark/样式换装
|
||
class BCard:
|
||
extends PanelContainer
|
||
var mark: Label
|
||
var icon_tr: TextureRect
|
||
var name_l: Label
|
||
var desc_l: Label
|
||
var price_l: Label
|
||
var foot_l: Label
|
||
var body: VBoxContainer # 内容列(消费方经 add_footer 插件到底注前——M6 购买钮挂位)
|
||
var _sb_normal: StyleBox
|
||
var _sb_sel: StyleBox
|
||
|
||
func _init(title: String, desc: String, band: Color, icon_path: String, price := "", footnote := "") -> void:
|
||
var tex := UiKit.load_tex(UiKit.TEX_PANEL_STONE)
|
||
_sb_normal = UiKit.panel_skinned(Vector4(12, 10, 12, 10)) if tex != null else UiKit.panel_flat(PANEL_DATA, PANEL_BORDER,RADIUS_OBJECT, 1, Vector4(12, 10, 12, 10)) # 回退=数据板单点(P7 乙后同值,字面量退役)
|
||
_sb_sel = UiKit.panel_flat(PANEL_DATA, Color8(138, 90, 52), RADIUS_OBJECT, 2, Vector4(12, 10, 12, 10)) # 选中底=数据板单点(P7 乙后同值)
|
||
add_theme_stylebox_override("panel", _sb_normal)
|
||
custom_minimum_size = Vector2(216, 300)
|
||
var box := VBoxContainer.new()
|
||
box.add_theme_constant_override("separation", 8)
|
||
add_child(box)
|
||
body = box
|
||
var band_r := ColorRect.new()
|
||
band_r.color = band
|
||
band_r.custom_minimum_size = Vector2(0, 6)
|
||
box.add_child(band_r)
|
||
mark = UiKit.label("✓", 16, true, Color8(58, 42, 8))
|
||
mark.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||
mark.visible = false
|
||
box.add_child(mark)
|
||
icon_tr = TextureRect.new()
|
||
var itex := UiKit.load_tex(icon_path)
|
||
if itex != null:
|
||
icon_tr.texture = itex
|
||
icon_tr.custom_minimum_size = Vector2(56, 56)
|
||
icon_tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
icon_tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||
icon_tr.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||
icon_tr.size_flags_horizontal = Control.SIZE_SHRINK_CENTER
|
||
box.add_child(icon_tr)
|
||
name_l = UiKit.label(title, 18, true, INK_TITLE)
|
||
name_l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
box.add_child(name_l)
|
||
desc_l = UiKit.label(desc, 14, false, Color8(74, 58, 40))
|
||
desc_l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
desc_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
box.add_child(desc_l)
|
||
var spacer := Control.new()
|
||
spacer.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||
box.add_child(spacer)
|
||
price_l = UiKit.label(price, 21, true, GOLD)
|
||
price_l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
price_l.visible = price != ""
|
||
box.add_child(price_l)
|
||
foot_l = UiKit.label(footnote, 11, false, Color8(110, 69, 52))
|
||
foot_l.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||
foot_l.visible = footnote != ""
|
||
box.add_child(foot_l)
|
||
|
||
func set_selected(v: bool) -> void:
|
||
add_theme_stylebox_override("panel", _sb_sel if v else _sb_normal)
|
||
mark.visible = v
|
||
|
||
func add_footer(c: Control) -> void:
|
||
body.add_child(c)
|
||
body.move_child(c, body.get_child_index(foot_l))
|
||
|
||
|
||
static func bcard(title: String, desc: String, band: Color, icon_path: String, price := "", footnote := "") -> BCard:
|
||
return BCard.new(title, desc, band, icon_path, price, footnote)
|
||
|
||
|
||
# 拨杆开关(设置屏——46×22 滑柄形;状态字随行由消费方摆;会话级/持久由消费方接 SettingsStore)
|
||
class SwitchToggle:
|
||
extends Button
|
||
var _knob: ColorRect
|
||
|
||
func _init(initial: bool) -> void:
|
||
toggle_mode = true
|
||
button_pressed = initial
|
||
custom_minimum_size = Vector2(46, 22)
|
||
var track := UiKit.panel_flat(Color8(184, 165, 130), Color8(120, 110, 88),RADIUS_OBJECT, 2)
|
||
add_theme_stylebox_override("normal", track)
|
||
add_theme_stylebox_override("hover", track)
|
||
add_theme_stylebox_override("pressed", track)
|
||
_knob = ColorRect.new()
|
||
_knob.color = Color8(58, 42, 8)
|
||
_knob.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||
add_child(_knob)
|
||
toggled.connect(func(_p: bool) -> void: _place())
|
||
_place()
|
||
|
||
func _place() -> void:
|
||
_knob.position = Vector2(25, 3) if button_pressed else Vector2(3, 3)
|
||
_knob.size = Vector2(18, 16)
|
||
|
||
|
||
static func switch_toggle(initial: bool) -> SwitchToggle:
|
||
return SwitchToggle.new(initial)
|
||
|
||
|
||
# 样式滑杆(设置屏音量——轨道暖石+默认柄;范围/读数由消费方接)
|
||
static func styled_slider(value: float, minv := 0.0, maxv := 100.0) -> HSlider:
|
||
var s := HSlider.new()
|
||
s.min_value = minv
|
||
s.max_value = maxv
|
||
s.value = value
|
||
s.custom_minimum_size = Vector2(200, 20)
|
||
s.add_theme_stylebox_override("slider", panel_flat(Color8(184, 165, 130), PANEL_BORDER, 0, 1))
|
||
s.add_theme_stylebox_override("grabber_area", panel_flat(Color8(122, 82, 50), Color8(0, 0, 0, 0), 0, 0))
|
||
return s
|
||
|
||
|
||
# tooltip 全站主题(深底多行素牌——§7.2 文字可读硬指标;消费方=任一祖先 Control.theme 挂一次全树生效)
|
||
static var _tip_theme: Theme = null
|
||
|
||
static func tooltip_theme() -> Theme:
|
||
if _tip_theme != null:
|
||
return _tip_theme
|
||
var th := Theme.new()
|
||
th.set_stylebox("panel", "TooltipPanel",
|
||
panel_flat(Color(0.09, 0.07, 0.05, 0.96), Color8(90, 80, 68), 0, 1, Vector4(8, 6, 8, 6)))
|
||
th.set_color("font_color", "TooltipLabel", WARM)
|
||
th.set_font_size("font_size", "TooltipLabel", 14)
|
||
_tip_theme = th
|
||
return _tip_theme
|