diff --git a/game/presentation/views/board_cell_unit.gd b/game/presentation/views/board_cell_unit.gd index fb60509c..d4b2e011 100644 --- a/game/presentation/views/board_cell_unit.gd +++ b/game/presentation/views/board_cell_unit.gd @@ -3,10 +3,14 @@ ## 无 class_name(类名缓存面纪律)——使用方经 preload 引用。 extends Node2D +const _GLOW_LAYER := preload("res://presentation/views/cell_type_glow_layer.gd") + var cell := Vector2i.ZERO # 板坐标(诊断观测面) var edge := 46.0: # 格边长(BoardView.set_cell_area 单点驱动) set(v): edge = v + if _glow_layer != null and is_instance_valid(_glow_layer): + _glow_layer.setup(edge, glow_group) queue_redraw() var bg_sb: StyleBoxFlat = null # 格底 StyleBox(BoardView 每次 paint 重建;呼吸微光就地改边框) # ── 接触带微光绘制面(2026-09-26 批:可挖域微光自"墙格整框"改"英雄格周界空间侧"——art-bible §3.2 v2.8; @@ -39,6 +43,8 @@ var glyph_color := UiKit.INK: # 文字色(墨族/雷朱/旗朱/小人蓝 var badge_glyph := "": # 奖励徽章文字(右下角金——旗之下、变异纹之上、不进数字区) set(v): badge_glyph = v + if _glow_layer != null and is_instance_valid(_glow_layer): + _glow_layer.set_badge_mask(v != "") queue_redraw() var dot_visible := false: # BFS 路径预览点迹(中心 6×6 小人蓝——旧 ColorRect 等价) set(v): @@ -113,6 +119,17 @@ var removed_dim := false: # 中立移除退色层(T8/D6——Board set(v): removed_dim = v queue_redraw() +# ── 未开格类型微光(迭代 30 卡①·Q1 丙案——英雄 8 向相邻未开格的组粒度类型提示) +# 作用集 = 可挖域 ∩ 身份可查格,由 BoardView 侧派生下发;本件只承接绘制与子件生命周期。 +# 静态(Q5 甲案):不呼吸、不入动效预算;相位门控(结算/道具窗口态隐藏)在 BoardView 侧。 +var glow_group := 0: + set(v): + if glow_group != v: + glow_group = v + _sync_glow_layer() + + +var _glow_layer: Node2D = null func _draw() -> void: @@ -315,6 +332,21 @@ func _draw_glyph(g: String, col: Color, fs: int) -> void: HORIZONTAL_ALIGNMENT_CENTER, edge, fs, col) +# 微光子件生命周期(迭代 30 卡①):有组才建、组变即换、无组即撤——作用集 ≤8 格常驻, +# 其余格零节点零绘制成本。与父件 _draw() 分离的三条理由见本件头注(命令序列/重绘预算/材质挂点)。 +func _sync_glow_layer() -> void: + if glow_group == 0: + if _glow_layer != null and is_instance_valid(_glow_layer): + _glow_layer.queue_free() + _glow_layer = null + return + if _glow_layer == null or not is_instance_valid(_glow_layer): + _glow_layer = _GLOW_LAYER.new() + add_child(_glow_layer) + _glow_layer.setup(edge, glow_group) + _glow_layer.set_badge_mask(badge_glyph != "") + + # 徽章绘制:右下角金(旧 Label BOTTOM_RIGHT 锚点等价——文字贴角内缩 2–3px) func _draw_badge(g: String) -> void: var f := font if font != null else ThemeDB.fallback_font diff --git a/game/presentation/views/board_view.gd b/game/presentation/views/board_view.gd index aa555326..255970b3 100644 --- a/game/presentation/views/board_view.gd +++ b/game/presentation/views/board_view.gd @@ -83,6 +83,9 @@ var _vmark_cb := Callable() # 地形变异 角标回调(迭代 6 T6 只 var _treasure_reveal := {} # 秘宝 真视集(迭代 7 T4——Vector2i → true;组合根 set_treasure_overlays 注入投影) var _treasure_preview := {} # 秘宝 预读集(同上——未开格徽章呈现挂点) var _diggable := {} # 可挖域(8 向相邻未开格)——呼吸微光作用集 +var _glow: Dictionary[Vector2i, int] = {} # 未开格类型微光作用集(Vector2i → 组号;=可挖域∩身份可查) +var _type_glow := true # 呈现态布尔:生产默认开;探针/夹具/调试浮窗可显式 off +var _glow_errors: Array[String] = [] # 清单外 cell_type 登记(禁静默空光) var _breath_t := 0.0 var _contact_unit: Node2D = null # 接触带承载单元(=英雄格 CellUnit;rebuild 随清场复位) var _install_tw: Tween # 入场淡入 tween(rebuild 重入先杀) @@ -264,6 +267,8 @@ func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void: child.queue_free() _world.hero = null # 英雄精灵随清场退役——下拍 refresh 经 ensure_hero 重建(首建即时落位) _cells.clear() + _glow.clear() + _glow_errors.clear() _contact_unit = null _hero_last = Vector2i(-99, -99) _r0.clear() @@ -307,6 +312,7 @@ func refresh() -> void: for n in _board.neighbors8(hero): if not _minesweeper.is_revealed(n) and not _minesweeper.is_removed(n): _diggable[n] = true + _recompute_glow(hero) for y in _board.height: for x in _board.width: _paint_cell(Vector2i(x, y), hero) @@ -315,6 +321,45 @@ func refresh() -> void: _update_hero(hero) # 英雄精灵 diff 驱动(棋盘小人贴图批 2026-09-25) +# 未开格类型微光作用集(迭代 30 卡①):= 可挖域 ∩ 身份可查格 ∩ 灯光区内。 +# 组号 = cell_type 经 UiKit.glow_group_for_cell_type 九值封闭判定得出;清单外值 = 数据错, +# 登记进 _glow_errors 由断言面显式暴露,禁静默空光(图标键错误处理同款纪律)。 +# 纯派生:只读 query_identity 回调,零玩法数据写入(🔴 契约 1)。 +# 相位门控与接触带同源(_dig_glow):结算/道具窗口态不显示——art-bible §2.1 挖掘期纪律面。 +func _recompute_glow(hero: Vector2i) -> void: + _glow.clear() + if not _type_glow or not _dig_glow: + return + for n in _diggable: + if _r0.has(n): + continue # 出生展开区不入摇号清单 ⇒ query 恒 null,显式跳过(省回调、零诊断噪音) + var fdist: int = absi(n.x - hero.x) + absi(n.y - hero.y) + if fdist > FOG_RADIUS + _fog_lantern: + continue # 灯光区外未开格全黑不渲染(§6.5)——微光亦不画 + var info: Variant = _identity_cb.call(n) if _identity_cb.is_valid() else null + if info == null: + continue + var ctype := String((info as Dictionary).get("cell_type", "")) + var g: int = UiKit.glow_group_for_cell_type(ctype) + if g == UiKit.GLOW_NONE: + # 登记去重:cell_type 随板摇定不可变,同一数据错在相位切换多次重算中只记一次 + # (否则 set_dig_glow_enabled 每切一次 _sync_glow_to_units 就追加一条,数组无限增长) + if not _glow_errors.has(ctype): + _glow_errors.append(ctype) + continue + _glow[n] = g + + +# 未开格类型微光开关(迭代 30 卡① · Q4 丙案):呈现态布尔,生产写入面 = 组合根单点。 +# 不新增设置项——工程有闸、界面无感;off 态为基线保护面(结算逐位一致断言承接,契约 1/T2)。 +func set_type_glow_enabled(on: bool) -> void: + if _type_glow == on: + return + _type_glow = on + if _board != null and _minesweeper != null: + refresh() + + # ---- 入场演出(battle.md Transitions「install 淡入 200ms + 初始 R0 展开涟漪」——2026-09-26 补实装)---- # 全板 modulate 淡入 200ms;R0 格按离板心距离逐格延迟显形(涟漪);纯视觉 alpha,零输入影响; # 减动效时长减半(motion);重入(弃图换图 rebuild)先杀旧 tween 防叠加 @@ -794,6 +839,7 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void: else: u.mutation_mark = "" u.badge_glyph = _update_badge(c) + u.glow_group = int(_glow.get(c, UiKit.GLOW_NONE)) # 视野迷雾呈态(迭代 11 第④批 T2/T3):曼哈顿距离 > 半径 → 区外——未开格全黑(正主 :163 照抄)/ # 已开格记忆态(灰显可读);以英雄格为心逐格重算,refresh()/notify_hero_move() 既有重绘驱动=移动实时跟随。 # 纯呈现零逻辑——格态/数字真值/清图判定零触碰(🔴 契约 1);结算期去饱和叠加互斥由 CellUnit 记忆态终态档承载。 @@ -816,6 +862,25 @@ func set_dig_glow_enabled(on: bool) -> void: _apply_contact_bands() # 相位重入(结算→挖掘):refresh 未跑也重挂带几何 else: _reset_breath() + _sync_glow_to_units() # 相位切换 → 类型微光作用集随之增减(只重绘变动格) + + +# 类型微光作用集与格件同步(迭代 30 卡①):只重绘「上一拍有组/这一拍有组」的并集格, +# 其余格零触碰;与 refresh() 全板重绘互为补充(refresh 走全板,本函数走增量)。 +func _sync_glow_to_units() -> void: + if _board == null or _minesweeper == null: + return + var prev: Dictionary[Vector2i, int] = _glow.duplicate() + _recompute_glow(_minesweeper.hero_pos()) + var touched := {} + for c in prev: + touched[c] = true + for c in _glow: + touched[c] = true + for c in touched: + var u: Node2D = _cells.get(c) + if u != null: + u.glow_group = int(_glow.get(c, UiKit.GLOW_NONE)) func _process(delta: float) -> void: @@ -853,7 +918,10 @@ func _apply_contact_bands() -> void: corners.append(0 if (d.x < 0 and d.y < 0) else (1 if d.x > 0 and d.y < 0 else (2 if d.x < 0 else 3))) hu.contact_bands = bands hu.contact_corners = corners - var k := 0.5 + 0.5 * sin(_breath_t * 3.0) + # 减动效:呼吸停格——refresh/enable 重放时施**静态**色(本件头注「已施静态色」的落実); + # 0.5 = 呼吸周期中值(正常态 k 的均值)。否则减动效下每次 refresh 都会按当前相位重施色, + # 带色在 refresh 间跳变=持续运动假象,且令 on/off 双帧取证无法隔离单一变量。 + var k := 0.5 if UiKit.motion_scale < 1.0 else 0.5 + 0.5 * sin(_breath_t * 3.0) hu.contact_col = COL_CONTACT.lerp(UiKit.CONTACT_GLOW_DEEP, 0.55 - 0.35 * k) hu.queue_redraw() @@ -1135,3 +1203,25 @@ func hover_probe() -> Vector2i: # 迷雾参数只读快照(case⑮ 参数漂移断言消费面——_fog_lantern/_fog_enabled 私有域直入退役,D2 清单外同类项随批收口) func fog_params_probe() -> Dictionary: return {"lantern": _fog_lantern, "enabled": _fog_enabled} + + +# 类型微光只读快照(迭代 30 卡① 断言消费面——开关态/作用集与组号/清单外 cell_type 登记) +func type_glow_probe() -> Dictionary: + return {"enabled": _type_glow, "cells": _glow.duplicate(), "errors": _glow_errors.duplicate()} + + +# 格件全局矩形只读出口(迭代 30 卡① 像素侧取样坐标源——禁测试侧硬编码像素;私有 _cells 直入退役) +# 坐标系:格件居于 SubViewport 内(_world 于原点),global_position 是**子视口画布**坐标; +# 取证帧截的是窗口画面 ⇒ 必须经 SubViewportContainer 的全局矩形换算回窗口坐标 +# (stretch 恒 1:1,仍按 host/vp 比例换算保稳健——容器中心化由 CenterContainer 驱动)。 +func cell_rect_probe(c: Vector2i) -> Rect2: + var u: Node2D = _cells.get(c) + if u == null or not is_instance_valid(u): + return Rect2() + var host: Rect2 = _svp_container.get_global_rect() + var vp_size: Vector2 = _viewport.size + if host.size.x <= 0.0 or host.size.y <= 0.0 or vp_size.x <= 0.0 or vp_size.y <= 0.0: + return Rect2() + var k: Vector2 = host.size / vp_size + var local := Rect2(u.global_position, Vector2(u.edge, u.edge)) + return Rect2(host.position + local.position * k, local.size * k) diff --git a/game/presentation/views/cell_type_glow.gdshader b/game/presentation/views/cell_type_glow.gdshader new file mode 100644 index 00000000..c71ea326 --- /dev/null +++ b/game/presentation/views/cell_type_glow.gdshader @@ -0,0 +1,53 @@ +// VERTICAL SLICE - NOT FOR PRODUCTION +// 未开格类型微光(迭代 30 卡① · Q1 丙案主路径)——格面内缘带状柔光,canvas_item 距离场衰减。 +// 事实源:HUD框架「未开格类型微光」节(生效范围/组粒度/生命周期/信息禁令); +// art-bible §4.2 五组色相(样张 v2 冻结、既裁项)+ §4.3 呈现约束+ §1.2 叠层序。 +// 纪律:① 内缘带状,禁整格均匀染色;② 砖心(数字字形占位区)零微光;③ 只承载组粒度类型, +// 不表极性/雷安/收益档位;④ 静态(Q5 甲案),相位门控由 BoardView 侧承载。 +// 参数全部由 ui_kit 常量注入(色板单点纪律,守卫 = ui_kit 之外裸 Color8 = 0)。 +// 坐标口径:VERTEX 为格件本件局部像素坐标(0..cell_px),不依赖 UV(本件无纹理绑定,UV 不可靠)。 +shader_type canvas_item; +render_mode blend_mix; + +uniform vec4 glow_col : source_color = vec4(1.0, 1.0, 1.0, 1.0); +uniform float glow_alpha : hint_range(0.0, 1.0) = 0.667; // art-bible §4.2 v2 呈现参数 α170 +uniform float band_frac : hint_range(0.02, 0.40) = 0.125; // 带宽/格边(cell÷8) +uniform float soft : hint_range(0.0, 0.95) = 0.55; // 带内软边起点(占带宽比) +uniform float corner : hint_range(0.0, 16.0) = 2.0; // 圆角掩罩半径(px) +uniform float cell_px : hint_range(8.0, 128.0) = 46.0; // 当前格边长(跟随窗口) +uniform vec4 mask_rect = vec4(0.0); // 豁免区(归一化格面坐标 x0,y0,x1,y1;全零=无豁免) + +varying vec2 v_local; + + +void vertex() { + v_local = VERTEX; +} + + +void fragment() { + vec2 p = v_local; + vec2 h = vec2(cell_px * 0.5); + float band = max(1.0, cell_px * band_frac); + float r = min(corner, cell_px * 0.25); + // 圆角矩形 SDF:负=内在,0=边界,正=外在 + vec2 q = abs(p - h) - (h - vec2(r)); + float outside = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - r; + // 到最近格缘的距离(px):0=缘,≥band=出带(即砖心) + float d = min(min(p.x, p.y), min(cell_px - p.x, cell_px - p.y)); + float a = 0.0; + if (d < band) { + float t = clamp(d / band, 0.0, 1.0); + // 贴缘段维持峰值,向内平滑归零(禁硬边条) + a = 1.0 - smoothstep(soft, 1.0, t); + } + float mask = 1.0 - smoothstep(-1.0, 0.5, outside); + if (mask_rect.z > mask_rect.x && mask_rect.w > mask_rect.y) { + vec2 nrm = p / max(cell_px, 1.0); + if (nrm.x >= mask_rect.x && nrm.x <= mask_rect.z + && nrm.y >= mask_rect.y && nrm.y <= mask_rect.w) { + mask = 0.0; // 豁免区(预读徽章位——既有元素,微光不染色其上) + } + } + COLOR = vec4(glow_col.rgb, glow_col.a * a * mask * glow_alpha); +} diff --git a/game/presentation/views/cell_type_glow.gdshader.uid b/game/presentation/views/cell_type_glow.gdshader.uid new file mode 100644 index 00000000..6d88fad5 --- /dev/null +++ b/game/presentation/views/cell_type_glow.gdshader.uid @@ -0,0 +1 @@ +uid://d0q7huhx5rla diff --git a/game/presentation/views/cell_type_glow_layer.gd b/game/presentation/views/cell_type_glow_layer.gd new file mode 100644 index 00000000..45bdce61 --- /dev/null +++ b/game/presentation/views/cell_type_glow_layer.gd @@ -0,0 +1,48 @@ +## VERTICAL SLICE - NOT FOR PRODUCTION +## CellTypeGlowLayer——未开格类型微光的独占承载件(挂在 CellUnit 之下、1:1 贴格、不入遮挡栈)。 +## 为什么单独一件而不画进 CellUnit._draw(): +## ① 父件 _draw() 命令序列由瓦片/数字/旗/焦点高亮多方共用,插进该序列会随叠层口径错位; +## ② 独立子件只在「有组」的格上建(作用集 ≤8),静止态零逐帧成本,且不占父件重绘预算; +## ③ 材质归子件独占 ⇒ 不抢世界根去饱和材质挂点(结算相位本层隐藏,无叠加冲突)。 +## 路线:Q1 丙案主路径 = 每格 canvas_item shader 距离场柔光,兜底 = 程序化渐变底图; +## 纪律:色与参数全取 ui_kit 常量,本件禁内联色值(守卫 = ui_kit 之外裸 Color8 = 0)。 +extends Node2D + +const _GLOW_SHADER := preload("res://presentation/views/cell_type_glow.gdshader") + +var _mat: ShaderMaterial = null +var _edge := 0.0 + + +# 作用集侧唯一入口(组号 = UiKit.GLOW_*;0 = 无微光) +func setup(edge: float, group: int) -> void: + _edge = edge + if _mat == null: + _mat = ShaderMaterial.new() + _mat.shader = _GLOW_SHADER + material = _mat + _mat.set_shader_parameter("glow_col", UiKit.glow_color_for_group(group)) + _mat.set_shader_parameter("cell_px", edge) + _mat.set_shader_parameter("band_frac", UiKit.TYPE_GLOW_BAND_FRAC) + _mat.set_shader_parameter("soft", UiKit.TYPE_GLOW_SOFT) + _mat.set_shader_parameter("corner", UiKit.TYPE_GLOW_CORNER) + _mat.set_shader_parameter("glow_alpha", UiKit.TYPE_GLOW_ALPHA) + queue_redraw() + + +# 预读徽章位豁免(既有元素——微光不染色其上;叠层序 §1.2:徽章在数字之后、旗之前) +func set_badge_mask(enabled: bool) -> void: + if _mat == null: + return + var m: Vector4 = Vector4(0.0, 0.0, 0.0, 0.0) + if enabled: + m = Vector4(0.68, 0.66, 0.98, 0.96) + _mat.set_shader_parameter("mask_rect", m) + + +func _draw() -> void: + if _edge <= 0.0: + return + # 一格全幅:片元着色器按距离场产出内缘带状柔光。 + # 输入 alpha 取 1/255(非零)——全透明几何在部分批次路径下会被整条跳过,着色器就跑不到。 + draw_rect(Rect2(Vector2.ZERO, Vector2(_edge, _edge)), Color(1, 1, 1, 1.0 / 255.0)) diff --git a/game/presentation/views/cell_type_glow_layer.gd.uid b/game/presentation/views/cell_type_glow_layer.gd.uid new file mode 100644 index 00000000..4a449ee4 --- /dev/null +++ b/game/presentation/views/cell_type_glow_layer.gd.uid @@ -0,0 +1 @@ +uid://dv6o8eixdwvdp diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index 38f50d32..8005f9e4 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -66,6 +66,75 @@ static func tg_band(id_lower: String) -> Color: return TG_PIERCE return TG_BUFF +# ── 未开格类型微光五组(迭代 30 卡①——HUD框架「未开格类型微光」节实装面;Q3 甲案「不并入」卡顶带 +# TG_* 两套色带两套归组并存,禁互为引用)── +# 色相 = art-bible §4.2 样张 v2 冻结五组(既裁项:本批无重裁资格,只允许按明度档做亮度缩放)。 +# 🔴 冻结面稽核(2026-10-17 契约 5 停机呈报后用户裁定):逐组 HSV 对照冻结值——BUFF/HEAL/PIERCE/ +# MAGIC 四组 ΔH ≤0.13°(8bit 量化噪声)、ΔS ≤0.002,属纯 V 缩放;群伤组初版 #FF1A31 曾 ΔH 5.40° +# (朝纯红移动、侵蚀冻结绯对危险红的刻意错峰),用户裁「退回色相保持域」→ 改 #A61D37(冻结 +# #FF2D55 的 V=0.65 缩放,H=348.57°/S=0.824 逐位不动)。五组现均满足「只动明度、不动色相」。 +# 明度档 = 本批 Q2 甲案 provisional 值——补齐 §4.2 从未定义的「各组一档明度互辨」数值 +# (色弱灰度互辨通道;复核转正归战斗一屏样张批)。 +# 🔴 生成口径(2026-10-17 原型帧实测订正):阶梯必须在**渲染合成空间**(sRGB/伽马空间)里排—— +# 引擎 canvas 混合按帧缓冲编码(伽马)做线性插值,色弱玩家屏幕上看到的是合成值而非色值本身。 +# 首版按 CIE L*(线性)排档曾把群伤/魔法排反(绯红线性亮于紫罗兰,但伽马空间绯红 G+B 通道低, +# 合成后反而更暗——实测最弱对 CR 仅 1.05)。本版五档按合成空间单调阶梯重排:以「带内均值 ≈ +# 0.49×色值 + 0.51×瓦片」合成模型反解,相邻组模型 CR = 1.245/1.234/1.239/1.270(判据线 +# 1.20;模型域与实测的差 = shader 距离场权重非扁平混合,故互辨判据的终审在渲染取证探针实测, +# L1 棋盘探针逐对读数见 reports/iter30-实现报告读数表)。 +# 明度档只许在 provisional 域内自调(L3 预授权③一轮),翻动色相 = 触 art-bible §4.2 既裁冻结面 +# = L3 停机呈报(2026-10-17 群伤组一案即此路径:停机呈报 → 用户裁 → 批内执行)。 +const TG_GLOW_BUFF := Color8(247, 223, 67) # 通用增益·芥末黄(attack/defense/weaken/action/gp) +const TG_GLOW_HEAL := Color8(104, 208, 66) # 治疗·黄绿(heal) +const TG_GLOW_PIERCE := Color8(48, 166, 166) # 穿刺·青(pierce) +const TG_GLOW_MAGIC := Color8(123, 91, 214) # 魔法·紫罗兰(magic) +const TG_GLOW_AOE := Color8(166, 29, 55) # 群伤·绯红(blast)——冻结 #FF2D55 的 V=0.65 档(色相零移动) +const TYPE_GLOW_ALPHA := 170.0 / 255.0 # 泛光强度(art-bible §4.2 v2 呈现参数 α170) +const TYPE_GLOW_BAND_FRAC := 0.125 # 带宽/格边(cell÷8——同上 v2 冻结参数) +const TYPE_GLOW_SOFT := 0.55 # 帯内软边起点(占带宽比;0=边缘突变,越大衰减越长) +const TYPE_GLOW_CORNER := 2.0 # 圆角掩罩半径(px——微光不溢出格面圆角) +const GLOW_NONE := 0 +const GLOW_BUFF := 1 +const GLOW_HEAL := 2 +const GLOW_PIERCE := 3 +const GLOW_AOE := 4 +const GLOW_MAGIC := 5 + +# 九 cell_type → 五组封闭判定(成员集合逐字照抄 art-bible §4.2;清单外值 = 数据错,返回 GLOW_NONE +# 由调用方显式报错登记,禁静默空光——图标键错误处理同款纪律。 +# 禁子串匹配、禁复用 tg_band():那是卡顶色带的 demo 冻结值且 blast 归 TG_MAGIC,与本案五组归属相反) +static func glow_group_for_cell_type(cell_type: String) -> int: + match cell_type: + "attack", "defense", "weaken", "action", "gp": + return GLOW_BUFF + "heal": + return GLOW_HEAL + "pierce": + return GLOW_PIERCE + "blast": + return GLOW_AOE + "magic": + return GLOW_MAGIC + _: + return GLOW_NONE + + +static func glow_color_for_group(g: int) -> Color: + match g: + GLOW_BUFF: + return TG_GLOW_BUFF + GLOW_HEAL: + return TG_GLOW_HEAL + GLOW_PIERCE: + return TG_GLOW_PIERCE + GLOW_AOE: + return TG_GLOW_AOE + GLOW_MAGIC: + return TG_GLOW_MAGIC + _: + return Color(0, 0, 0, 0) + + # ── 呈现散件裸色搬运(UI 设计对齐批 N4a 2026-10-06——原值搬运零观感;本节立后 ui_kit 之外 # 呈现层裸 Color8 = 0,守卫 = ui_contrast_probe 静态段)── const INK_HINT := Color8(110, 69, 52) # 提示/警示暖棕墨(设置警示文/地图底栏组标)