iter45-卡①T1: 单格两绘制域件建件与宿主瘦身——_CellPaintState 27成员沿绘制层域轴拆 cell_base_layer(12成员+九函数+九常量,draw_base入口)/cell_mark_overlay(13成员+六函数段+draw_centered_text单点化,四绘制入口),宿主退六步时序器类变量33→10(edge/cell/glow_group上收+glyph/glyph_color/plate/fog_state/_glow_layer冻结面原位+base/face两实例),board_view写入面49处改址(u.st.X→u.base./u.face./u.edge/u.glow_group,u.st.清零)〔状态件 V1/V2〕;逐字搬运+三处机械改写(st.X→裸成员/draw_X→canvas.draw_X/st.edge→rect.size.x),draw_glyph_layer增plate第5参(设计文档§2.1'plate绘制侧零消费'系413124b0恢复守卫前的过时实查——现行if plate:守卫在位,迁出即随行,保golden 0.00%零漂移);孤立验证:render_gate golden三张0.00%+run_all四套件全绿+run_smoke 15项0失败

This commit is contained in:
DKChan committed 2026-10-30 08:17:58 +08:00
1 parent 14054d9dfb
commit 2b26cae9e4
4 files changed
+441 -416

No files matched your search

+44 -368
View File
@@ -1,128 +1,31 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## CellUnit——BoardView SubViewport 世界内逐格渲染件(Node2D),全部程序化 _draw 占位视觉(纹理槽位已留未装载)。
## CellUnit——BoardView SubViewport 世界内逐格渲染件(Node2D),被动绘制壳:_draw 六步时序器
## +视野迷雾域+微光子件生命周期+格件几何;基座层与标识叠加两绘制域件 _init 自建持有
## (cell_base_layer/cell_mark_overlay——单一变化原因=迷雾规格与格件节点生命周期)。
## 无 class_name(类名缓存面纪律)——使用方经 preload 引用。
## 内部状态件(u3 子域拆件——契约 9 职责语句「单格绘制输入集」):本件为被动绘制壳,
## 状态件 _CellPaintState 承载全部绘制入参(单一变化原因=格面呈现),setter 触发宿主重绘语义
## 经 _owner 回引保持逐字;探针直读冻结成员(glyph/fog_state/plate/glyph_color)与 _glow_layer 住宿主。
## 探针直读冻结成员(glyph/fog_state/plate/glyph_color)与 _glow_layer 住宿主;
## hl_color/hl_visible/dot_visible/mutation_mark 探针读 u.face.*。
extends Node2D
const _GLOW_LAYER := preload("res://presentation/views/cell_type_glow_layer.gd")
const _BASE_LAYER := preload("res://presentation/views/cell_base_layer.gd")
const _MARK_OVERLAY := preload("res://presentation/views/cell_mark_overlay.gd")
# ── D 投影档绘制常量(研究件实测取色——立面高光/双段暗带/地面投影;黑罩 alpha 叠加实测等效)──
const FACADE_HIGHLIGHT_PX := 2.0 # 口沿高光线厚(像素)
const FACADE_NEAR_FRAC := 0.16 # 立面近段高(格边比——研究件 10/64)
const FACADE_FAR_FRAC := 0.25 # 立面远段高(格边比——研究件 16/64)
const FACADE_NEAR_COL := Color(0, 0, 0, 0.60) # 立面近段黑罩(墙顶 132→52 实测 α≈0.61)
const FACADE_FAR_COL := Color(0, 0, 0, 0.82) # 立面远段黑罩(墙顶 132→24 实测 α≈0.82)
const PROJECTION_NEAR_COL := Color(0, 0, 0, 0.28) # 投影贴墙段(地面 66→46 实测 α≈0.28)
const PROJECTION_MID_COL := Color(0, 0, 0, 0.16) # 投影中段(渐隐)
const PROJECTION_EDGE_COL := Color(0, 0, 0, 0.06) # 投影远段(渐隐收边)
# ── E 格高墙面(圣经 v3.5 冻结①——地面格南邻墙→南部 30% 承接墙面:墙顶纹理原亮度
# 直贴零暗档零阴影;墙脚阴线只画地面/墙面交界;(b)-(c) 交界
# 无边框由 BoardView 侧跳过墙格北向描边承载)──
const E_FACADE_FRAC := 0.30 # 墙面承接高(格边比 30%)
## 单格绘制输入集(被动绘制壳的全部绘制入参——BoardView 直写面;setter 触发宿主重绘语义逐字)
class _CellPaintState extends RefCounted:
var _owner: Node2D = null
var cell := Vector2i.ZERO # 板坐标(诊断观测面)
var edge := 46.0: # 格边长(BoardView.set_cell_area 单点驱动)
set(v):
edge = v
if _owner._glow_layer != null and is_instance_valid(_owner._glow_layer):
_owner._glow_layer.setup(edge, glow_group)
_owner.queue_redraw()
var bg_sb: StyleBoxFlat = null # 格底 StyleBox(BoardView 每次 paint 重建;呼吸微光就地改边框)
# ── 接触带微光绘制面(可挖域微光画在英雄格周界空间侧——art-bible §3.2 v2.8;
# 仅英雄格单元承载,BoardView.refresh 按可挖域写边/角码、_process 逐拍更新呼吸色)──
var contact_bands: Array[int] = [] # 正交接触边码 0=N/1=S/2=W/3=E(该侧整条边)
var contact_corners: Array[int] = [] # 斜向共享角码 0=NW/1=NE/2=SW/3=SE(角点+两侧各半条边)
var contact_col := Color(0, 0, 0, 0) # 当前呼吸色(alpha 0 = 不画)
var outline_sides: Array[int] = [] # 墙格地面朝向描边码 0=N/1=S/2=W/3=E(程序侧结构件)
# ── 高墙感 D 投影档(研究件四变体定 D=C+地面投影,ITB 同款高度线索)──
# 南立面 = 墙格南缘内侧「口沿高光线 + 双段暗带」(邻地面格才画,墙群内聚);地面投影 = 北邻墙格
# 在本已开格北缘投渐隐暗带。参数按研究件 64px 拼版实测比例换算(立面双段 0.16/0.25 格边、投影带
# 0.22 格边;坐标推导留档 commit)。素材缺位程序化回退同样服役(结构归程序——art-bible §8.5)。
var facade_south := false: # 南立面开关(BoardView paint 按「南邻已开格」写入)
set(v):
facade_south = v
_owner.queue_redraw()
var proj_band := 0.0: # 地面投影带宽(像素,0 = 不画——BoardView 按北邻墙判定写入)
set(v):
proj_band = v
_owner.queue_redraw()
var badge_glyph := "": # 奖励徽章文字(右下角金——封印之下、变异纹之上、不进数字区)
set(v):
badge_glyph = v
if _owner._glow_layer != null and is_instance_valid(_owner._glow_layer):
_owner._glow_layer.set_badge_mask(v != "")
_owner.queue_redraw()
var dot_visible := false: # BFS 路径预览点迹(中心 6×6 小人蓝)
set(v):
dot_visible = v
_owner.queue_redraw()
var hl_visible := false: # 焦点高亮(悬停态边框覆盖层)
set(v):
hl_visible = v
_owner.queue_redraw()
var hl_color := UiKit.GOLD: # 高亮色(金目标/封印朱/禁入灰禁红)
set(v):
hl_color = v
_owner.queue_redraw()
var font: Font = null # 字体槽位(null = ThemeDB.fallback_font——BoardView.set_world_font 注入)
var bevel := 0.0: # 顶面倒角强度(0=关;档位 0/0.35/0.6/1.0)
set(v):
bevel = v
_owner.queue_redraw()
# ── 素材槽位(现全 null = 程序化占位服役;art-bible §8.2 格面 32×32 暂定档)──
var tex_base: Texture2D = null # ① 砖面底
var tex_mutation: Texture2D = null # ② 变异纹带
var tex_glyph: Texture2D = null # ③ 数字
var tex_badge: Texture2D = null # ③' 徽章
var tex_seal: Texture2D = null: # ④ 封印(槽位可运行时换装〔封印〕——同族 facade/treasure_mark
set(v): # 均 setter 触发重绘(普通 var 赋值不重绘=封印插上不显示))
if tex_seal != v:
tex_seal = v
_owner.queue_redraw()
var tex_facade: Texture2D = null # ⑥ E 格墙面材质(v3.5——南邻墙顶 center 瓦片同源,BoardView 注入)
var facade_strip := false: # E 格开关(BoardView paint 按「已开格且南邻未开墙」写入——瓦片态专用)
set(v):
facade_strip = v
_owner.queue_redraw()
# 变异角标(程序化,tex_mutation 纹带槽暂留):
# "flat_damage" = 未开格伤害型变异,左下角危险红三角(开格有代价的策略纹理标记)
# 叠加层标记(秘宝整图叠加层的格面呈现;派生信息层零回写):
# "mine_reveal" = 真视格(危险红内缩框——「这格是雷」的确定决策标记,封印照常计费);
# "reward_preview" 格不复用本字段——预读徽章走 badge_glyph 注入(board_view refresh 注入 REWARD_BADGE 字形)
var treasure_mark := "":
set(v):
if treasure_mark != v:
treasure_mark = v
_owner.queue_redraw()
var mutation_mark := "":
set(v):
if mutation_mark != v:
mutation_mark = v
_owner.queue_redraw()
# ── 未开格类型微光(英雄 8 向相邻未开格的组粒度类型提示)
# 作用集 = 可挖域 ∩ 身份可查格,由 BoardView 侧派生下发;本件只承接绘制与子件生命周期。
# 静态:不呼吸、不入动效预算;相位门控(结算/道具窗口态隐藏)在 BoardView 侧。
var glow_group := 0:
set(v):
if glow_group != v:
glow_group = v
_owner._sync_glow_layer()
var removed_dim := false: # 中立移除退色层(BoardView paint 按排雷玩法 is_removed 写入)
set(v):
removed_dim = v
_owner.queue_redraw()
var st := _CellPaintState.new()
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 cell := Vector2i.ZERO # 板坐标(诊断观测面)
# ── 未开格类型微光(英雄 8 向相邻未开格的组粒度类型提示)
# 作用集 = 可挖域 ∩ 身份可查格,由 BoardView 侧派生下发;本件只承接绘制与子件生命周期。
# 静态:不呼吸、不入动效预算;相位门控(结算/道具窗口态隐藏)在 BoardView 侧。
var glow_group := 0:
set(v):
if glow_group != v:
glow_group = v
_sync_glow_layer()
var glyph := "": # 文字位——数字/雷/封印/人/?(现行视觉封印与数字互斥,同位承载;探针直读冻结)
set(v):
glyph = v
@@ -141,268 +44,51 @@ var fog_state := 0: # 探针直读冻结
queue_redraw()
var _glow_layer: Node2D = null
var base = null # 格面基座层绘制域件(_init 自建)
var face = null # 格面标识与叠加层绘制域件(_init 自建)
func _init() -> void:
st._owner = self
base = _BASE_LAYER.new(self)
face = _MARK_OVERLAY.new(self)
func _draw() -> void:
var rect := Rect2(Vector2.ZERO, Vector2(st.edge, st.edge))
_draw_base_layer(rect)
_draw_mutation_marks()
_draw_glyph_layer(rect)
_draw_overlay_layers(rect)
## ① 砖面底层域(占位 = StyleBoxFlat:格底色 + 暗化描边 + 3px 圆角;瓦片态描边覆盖层;
## 呼吸微光/主角蓝框由 BoardView 改 bg_sb 后触发重绘。素材缺位回退走 elif 程序化原样,零观感变化)
func _draw_base_layer(rect: Rect2) -> void:
if st.tex_base != null:
draw_texture_rect(st.tex_base, rect, false)
# 轮廓加强(轮廓线不明显):墙格地面朝向边补 2px 深墨线(§8.5 结构归程序——
# 素材 1px 墨线在 45/36px 非整数缩放下视觉衰减,程序线保底;墙-墙内界不画,保墙群内聚;
# 坐标推导:各边内贴 2px 矩形,相邻边矩形全长相交=周界闭合)
if not st.outline_sides.is_empty():
_draw_outline_sides()
_draw_south_facade()
_draw_north_projection()
_draw_e_facade()
# 瓦片态描边覆盖层:tex_base 在位时 BoardView 传入的 bg_sb
# 已降级为「透明底 + 描边」载体——呼吸微光/主角蓝框画在瓦片之上(art-bible §1.2 叠层序
# 不变:底 < 纹带 < 数字 < 封印 < 焦点高亮);素材缺位回退走 elif 程序化原样,零观感变化
if st.bg_sb != null:
draw_style_box(st.bg_sb, rect)
_draw_contact_or_fallback(rect)
## 接触带微光或平色回退域(英雄格周界空间侧——正交邻墙=整条边;斜向=共享角点+两侧各半条边;光不进墙格):
## 带宽 4px 贴格边内侧(坐标推导:N=[0,0,edge,4] S=[0,edge-4,edge,4] W=[0,0,4,edge] E=[edge-4,0,4,edge];
## 半条边=沿邻边自角点起 edge/2;角点圆 r=6 贴角点圆心,近白芯 r=2.5 提亮)
func _draw_contact_or_fallback(rect: Rect2) -> void:
if st.contact_col.a > 0.0:
_draw_north_projection() # 平色回退+微光并发的英雄格补画(瓦片路径已画——同像素重复无观感差)
_draw_e_facade() # E 格墙面补画(同上——英雄格兼 E 格时瓦片路径已画,重复无害)
_draw_contact_bands()
elif st.bg_sb != null:
draw_style_box(st.bg_sb, rect)
_draw_south_facade() # 平色回退路径——D 投影档两件同样服役(结构归程序)
_draw_north_projection()
_draw_bevel()
## ②③④ 中层域:变异纹带与标记/数字色牌与字形/徽章/封印
func _draw_glyph_layer(rect: Rect2) -> void:
# ② 变异纹带(无程序化纹带——槽位先行,贴图落位即用)
if st.tex_mutation != null:
draw_texture_rect(st.tex_mutation, rect, false)
# ③ 数字/徽章(数字 ≥ 格高 50% 墨族对格底 ≥4.5:1;徽章右下角金,遮蔽态 = "?")
if glyph != "":
if plate:
_draw_digit_plate()
if st.tex_glyph != null:
draw_texture_rect(st.tex_glyph, rect, false)
else:
_draw_glyph(glyph, glyph_color, maxi(18, int(ceil(st.edge * 0.5)))) # 字号下限 18(§7.4 冻结公式)
if st.badge_glyph != "":
if st.tex_badge != null:
draw_texture_rect(st.tex_badge, rect, false)
else:
_draw_badge(st.badge_glyph)
# ④ 封印(现行视觉封印文字在 ③ 文字位承载——独立槽位留给封印图)
if st.tex_seal != null:
draw_texture_rect(st.tex_seal, rect, false)
## 数字色牌后备域(圣经 v3.5 冻结④:数字亮墨直画已开格底须垫牌;
## 牌=后备件——雷/「?」维持牌载保读性(牌上深墨族 12:1);
## 牌尺寸 0.60/0.62 格边居中;封印=未开格不垫牌)
func _draw_digit_plate() -> void:
var pw := st.edge * 0.60
var ph := st.edge * 0.62
var pr := Rect2((st.edge - pw) * 0.5, (st.edge - ph) * 0.5, pw, ph)
draw_rect(pr, UiKit.DIGIT_PLATE, true)
draw_rect(pr, UiKit.DIGIT_PLATE_EDGE, false, 1.0)
## ⑤⑥⑦ 顶层域:路径点迹 + 焦点高亮 + 视野迷雾 + 中立移除退色
func _draw_overlay_layers(rect: Rect2) -> void:
# ⑤ 路径点迹 + 焦点高亮(悬停态覆盖层,不参与命中)
if st.dot_visible:
var ds := 6.0
draw_rect(Rect2(Vector2((st.edge - ds) * 0.5, (st.edge - ds) * 0.5), Vector2(ds, ds)), UiKit.HERO_BLUE, true)
if st.hl_visible:
# 悬停高亮减淡(金框太明显——绘制时 55% alpha,hl_color 属性与探针断言面不动)
draw_rect(Rect2(1, 1, st.edge - 2.0, st.edge - 2.0), Color(st.hl_color, 0.55), false, 2.0) # 2px 内描边
_draw_fog_layer()
# ⑦ 中立移除退色层(退色降档:暗幕压顶全层覆盖,暗淡记忆态同族呈现语义;
# 顶层于迷雾之上——移除格不再参与迷雾语义。本实现移除集=未揭示危险格(无数字无封印印))
if st.removed_dim:
draw_rect(Rect2(Vector2.ZERO, Vector2(st.edge, st.edge)), Color(0.12, 0.10, 0.09, 0.62), true)
var rect := Rect2(Vector2.ZERO, Vector2(edge, edge))
base.draw_base(self, rect) # 基座层整树(描边/立面/投影/接触带/倒角与其补画序)
face.draw_marks(self, rect) # 变异标记
face.draw_glyph_layer(self, rect, glyph, glyph_color, plate) # 字形/色牌/徽章/封印
face.draw_overlay_under(self, rect) # 点迹+高亮(雾层由宿主夹在其后)
_draw_fog_layer(rect) # 宿主迷雾域(原位——雾夹在 hl 与 removed 之间)
face.draw_removed_dim(self, rect) # 退色层
## ⑥ 视野迷雾层(最顶遮罩,§6.5 落文):
## 全黑=不透明纯黑盖全部叠层(含封印印/徽章/高亮——封印印记随格隐没,§6.5 显式推论);
## 记忆态=记忆板素色压平(纹理免疫——色牌/封印印/徽章随板隐没)+数字记忆墨重画(数字可读硬指标 ≥4.5:1)
func _draw_fog_layer() -> void:
func _draw_fog_layer(rect: Rect2) -> void:
if fog_state == 1:
draw_rect(Rect2(Vector2.ZERO, Vector2(st.edge, st.edge)), Color(0, 0, 0, 1.0), true)
draw_rect(rect, Color(0, 0, 0, 1.0), true)
elif fog_state == 2:
_draw_fog_memory()
## 墙格轮廓域:地面朝向边补 2px 深墨线(各边内贴 2px 矩形,相邻边矩形全长相交=周界闭合)
func _draw_outline_sides() -> void:
var ow := 2.0
var oc := UiKit.FOG_OUTLINE
for b in st.outline_sides:
var or_ := Rect2()
match int(b):
0: or_ = Rect2(0, 0, st.edge, ow)
1: or_ = Rect2(0, st.edge - ow, st.edge, ow)
2: or_ = Rect2(0, 0, ow, st.edge)
3: or_ = Rect2(st.edge - ow, 0, ow, st.edge)
draw_rect(or_, oc, true)
## 接触带微光域:正交整条边 + 斜向共享角点与两侧半条边
func _draw_contact_bands() -> void:
var band := 4.0
var half := st.edge * 0.5
for b in st.contact_bands:
var r := Rect2()
match int(b):
0: r = Rect2(0, 0, st.edge, band)
1: r = Rect2(0, st.edge - band, st.edge, band)
2: r = Rect2(0, 0, band, st.edge)
3: r = Rect2(st.edge - band, 0, band, st.edge)
draw_rect(r, st.contact_col, true)
for cc in st.contact_corners:
_draw_contact_corner(int(cc), band, half)
## 斜向角点域:角点圆 r=6 贴角点圆心 + 近白芯 r=2.5 提亮 + 半条边(角点沿两条邻边各延伸 edge/2——
## 坐标推导:横带沿 N/S 自角点向板心侧 half;NW/SE 半带 x=y=角点坐标,NE/SE 半带自 edge-half 收进——
## 正交整边时重叠同色无害)
func _draw_contact_corner(cc: int, band: float, half: float) -> void:
var pt := Vector2.ZERO
match cc:
0: pt = Vector2(0, 0)
1: pt = Vector2(st.edge, 0)
2: pt = Vector2(0, st.edge)
3: pt = Vector2(st.edge, st.edge)
draw_circle(pt, 6.0, st.contact_col)
draw_circle(pt, 2.5, Color(1, 1, 1, minf(1.0, st.contact_col.a * 0.95)))
if cc == 0 or cc == 1:
# N 侧横带:NW 自 x=0、NE 自 x=edge-half
draw_rect(Rect2(pt.x if cc == 0 else pt.x - half, 0.0, half, band), st.contact_col, true)
if cc == 2 or cc == 3:
# S 侧横带:SW 自 x=0、SE 自 x=edge-half
draw_rect(Rect2(pt.x if cc == 2 else pt.x - half, st.edge - band, half, band), st.contact_col, true)
if cc == 0 or cc == 2:
# W 侧竖带:NW 自 y=0、SW 自 y=edge-half
draw_rect(Rect2(0.0, pt.y if cc == 0 else pt.y - half, band, half), st.contact_col, true)
if cc == 1 or cc == 3:
# E 侧竖带:NE 自 y=0、SE 自 y=edge-half
draw_rect(Rect2(st.edge - band, pt.y if cc == 1 else pt.y - half, band, half), st.contact_col, true)
## 顶面倒角域(光源左上——上/左内沿受光 2px、下/右内沿背光 2px;内缩 2px 让
## 呼吸微光/主角蓝边框保持最外沿可见;强度随档位)
func _draw_bevel() -> void:
if st.bevel <= 0.0:
return
var base: Color = st.bg_sb.bg_color
var lit: Color = base.lightened(0.22 * st.bevel)
var dark: Color = base.darkened(0.22 * st.bevel)
var t := 2.0
draw_rect(Rect2(t, t, st.edge - t * 2.0, t), lit, true)
draw_rect(Rect2(t, t, t, st.edge - t * 2.0), lit, true)
draw_rect(Rect2(t, st.edge - t * 2.0, st.edge - t * 2.0, t), dark, true)
draw_rect(Rect2(st.edge - t * 2.0, t, t, st.edge - t * 2.0), dark, true)
## 变异标记域:真视格=危险红内缩框/未开格伤害型变异=左下角危险红三角
func _draw_mutation_marks() -> void:
if st.treasure_mark == "mine_reveal":
var inset := st.edge * 0.14
draw_rect(Rect2(inset, inset, st.edge - inset * 2.0, st.edge - inset * 2.0),
Color(UiKit.RED_MINE, 0.9), false, 2.0)
if st.mutation_mark == "flat_damage":
var mw := st.edge * 0.28
var mpts := PackedVector2Array([Vector2(0, st.edge - mw), Vector2(0, st.edge), Vector2(mw, st.edge)])
draw_colored_polygon(mpts, Color(UiKit.RED_MINE, 0.85))
_draw_fog_memory(rect)
## 记忆态换装域(demo 口径「地板纹理 + 灰罩 α.66」:
## 地板纹理原样直画 + FOG_MEMORY_PLATE α.66 灰罩压顶;素材零改动、格边长零改动。
## 记忆墨 FOG_MEMORY_INK 对合成底归一后(真实渲染面)三关 5.71–5.93:1 ≥4.5:1(§7.2 硬指标;
## 测点行值与素材随动,见 ui_contrast_probe 迷雾段))
func _draw_fog_memory() -> void:
if st.tex_base != null:
draw_texture_rect(st.tex_base, Rect2(Vector2.ZERO, Vector2(st.edge, st.edge)), false)
draw_rect(Rect2(Vector2.ZERO, Vector2(st.edge, st.edge)), Color(UiKit.FOG_MEMORY_PLATE, 0.66), true)
func _draw_fog_memory(rect: Rect2) -> void:
if base.tex_base != null:
draw_texture_rect(base.tex_base, rect, false)
draw_rect(rect, Color(UiKit.FOG_MEMORY_PLATE, 0.66), true)
if glyph != "":
_draw_glyph(glyph, UiKit.FOG_MEMORY_INK, maxi(18, int(ceil(st.edge * 0.5)))) # 字号下限 18(§7.4 冻结公式同源)
# 南立面(D 档件一——墙格南缘内侧,自底边向上:双段暗带在下、口沿高光线贴其上沿):
# 坐标推导(自格底边起算):远段=[edge−far, edge],近段=[edge−far−near, edge−far],
# 高光线=[edge−far−near−hl, edge−far−near]——与研究件「墙顶面→高光→暗段→更暗段→格界」下行序一致
func _draw_south_facade() -> void:
if not st.facade_south:
return
var far_h := st.edge * FACADE_FAR_FRAC
var near_h := st.edge * FACADE_NEAR_FRAC
var y_far := st.edge - far_h
var y_near := y_far - near_h
var y_hl := y_near - FACADE_HIGHLIGHT_PX
draw_rect(Rect2(0.0, y_hl, st.edge, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点)
draw_rect(Rect2(0.0, y_near, st.edge, near_h), FACADE_NEAR_COL, true)
draw_rect(Rect2(0.0, y_far, st.edge, far_h), FACADE_FAR_COL, true)
# 地面投影(D 档件二——北邻墙格在本已开格北缘投渐隐暗带,自北缘向南三段衰减):
# 坐标推导:三等分 proj_band,α 0.28/0.16/0.06(研究件实测贴墙 α≈0.28、带末渐融入地面)
func _draw_north_projection() -> void:
if st.proj_band <= 0.0:
return
var seg := st.proj_band / 3.0
draw_rect(Rect2(0.0, 0.0, st.edge, seg), PROJECTION_NEAR_COL, true)
draw_rect(Rect2(0.0, seg, st.edge, seg), PROJECTION_MID_COL, true)
draw_rect(Rect2(0.0, seg * 2.0, st.edge, seg), PROJECTION_EDGE_COL, true)
# E 格高墙面(圣经 v3.5 冻结①——方向稿 draw_facade 移植):本格已开且南邻为墙 →
# 南部 30% 承接墙面。材质 = 墙顶 center 瓦片顶部 30% 区带**原亮度**直贴(零暗档零阴影);
# 墙脚阴线只画地面/墙面交界(strip 顶缘 1px);墙面底缘(格界处)不画任何线——直接接南侧墙顶
# (白边导致两截)。仅瓦片态服役(tex_facade 缺位 = 平色回退原语义不动)。
func _draw_e_facade() -> void:
if not st.facade_strip or st.tex_facade == null:
return
var strip_h := st.edge * E_FACADE_FRAC
var y0 := st.edge - strip_h
var ts := st.tex_facade.get_size()
draw_texture_rect_region(st.tex_facade, Rect2(0.0, y0, st.edge, strip_h),
Rect2(0.0, 0.0, ts.x, ts.y * E_FACADE_FRAC))
draw_rect(Rect2(0.0, y0, st.edge, 1.0), UiKit.E_FACADE_FOOT_COL, true)
# 文字位绘制:水平居中 + 垂直居中(基线 = (格高 − 字高)/2 + 上伸部)
func _draw_glyph(g: String, col: Color, fs: int) -> void:
var f := st.font if st.font != null else ThemeDB.fallback_font
if f == null:
return
var asc := f.get_ascent(fs)
var desc := f.get_descent(fs)
draw_string(f, Vector2(0.0, (st.edge - (asc + desc)) * 0.5 + asc), g,
HORIZONTAL_ALIGNMENT_CENTER, st.edge, fs, col)
face.draw_centered_text(self, rect, glyph, UiKit.FOG_MEMORY_INK, maxi(18, int(ceil(edge * 0.5)))) # 字号下限 18(§7.4 冻结公式同源)
# 微光子件生命周期:有组才建、组变即换、无组即撤——作用集 ≤8 格常驻,
# 其余格零节点零绘制成本。与父件 _draw() 分离的三条理由见本件头注(命令序列/重绘预算/材质挂点)。
func _sync_glow_layer() -> void:
if st.glow_group == 0:
if glow_group == 0:
if _glow_layer != null and is_instance_valid(_glow_layer):
_glow_layer.queue_free()
_glow_layer = null
@@ -410,15 +96,5 @@ func _sync_glow_layer() -> void:
if _glow_layer == null or not is_instance_valid(_glow_layer):
_glow_layer = _GLOW_LAYER.new()
add_child(_glow_layer)
_glow_layer.setup(st.edge, st.glow_group)
_glow_layer.set_badge_mask(st.badge_glyph != "")
# 徽章绘制:右下角金(文字贴角内缩 2–3px)
func _draw_badge(g: String) -> void:
var f := st.font if st.font != null else ThemeDB.fallback_font
if f == null:
return
var fs := maxi(10, int(st.edge * 0.28))
var sz := f.get_string_size(g, HORIZONTAL_ALIGNMENT_LEFT, -1, fs)
draw_string(f, Vector2(st.edge - sz.x - 2.0, st.edge - 3.0), g, HORIZONTAL_ALIGNMENT_LEFT, -1, fs, UiKit.GOLD)
_glow_layer.setup(edge, glow_group)
_glow_layer.set_badge_mask(face.badge_glyph != "")
+48 -48
View File
@@ -182,7 +182,7 @@ func set_treasure_overlays(reveal_cells: Array, preview_cells: Array) -> void:
func set_world_font(f: Font) -> void:
_world.board_font = f
for c in _cells:
_cells[c].st.font = f
_cells[c].face.font = f
_cells[c].queue_redraw()
@@ -201,7 +201,7 @@ func set_cell_area(avail: Vector2) -> void:
var stride := _cell + float(SEP)
for c in _cells:
var u: Node2D = _cells[c]
u.st.edge = _cell
u.edge = _cell
u.position = Vector2(c) * stride
if _world.hero != null and _hero_sprite.last.x >= 0:
_place_hero(_hero_sprite.last, _hero_sprite.last, true) # 格边长变更(窗口缩放)——英雄即时随格重置(杀位移 tween 防飞陈旧落点)
@@ -255,17 +255,17 @@ func rebuild(board: BoardData, minesweeper: MinesweeperRules) -> void:
# 逐格构建器(rebuild 建格循环唯一出函面):.new() 与属性装配逐字迁入,循环体只调构建器
func _build_cell_unit(c: Vector2i, stride: float) -> Node2D:
var u: Node2D = _CELL_UNIT.new()
u.st.cell = c
u.st.edge = _cell
u.cell = c
u.edge = _cell
u.position = Vector2(c) * stride
u.st.bevel = DEPTH_BEVEL # 新建格单元继承
u.base.bevel = DEPTH_BEVEL # 新建格单元继承
u.use_parent_material = true # 去饱和材质挂 BoardWorld——单元随世界材质灰化
if _palette.tiles_ok:
# NEAREST 采样(32×32 源 × 45/36px 格边非整数缩放接受档,
# 与英雄小人 1.5× 豁免同级(圣经 §8.2);只在瓦片
# 模式启用——程序化回退路径渲染零扰动(含格面文字采样不回退不变化)
u.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
u.st.font = _world.board_font
u.face.font = _world.board_font
_world.add_child(u)
return u
@@ -514,21 +514,21 @@ func _apply_hover_visuals(c: Vector2i) -> void:
var seal: bool = _minesweeper.has_seal(c)
var adjacent: bool = _diggable.has(c)
if revealed:
u.st.hl_visible = true
u.st.hl_color = COL_HL_TARGET # 移动目标格边框(金)
u.face.hl_visible = true
u.face.hl_color = COL_HL_TARGET # 移动目标格边框(金)
_set_cursor_shape(c, CURSOR_MOVE_SEM)
_show_path_preview(c)
elif adjacent:
u.st.hl_visible = true
u.st.hl_color = COL_HL_TARGET # 挖掘目标格边框(相邻封印格同此——左键直接挖)
u.face.hl_visible = true
u.face.hl_color = COL_HL_TARGET # 挖掘目标格边框(相邻封印格同此——左键直接挖)
_set_cursor_shape(c, CURSOR_DIG)
elif seal:
u.st.hl_visible = true
u.st.hl_color = COL_SEAL # 已承诺封印标记(封印黑;无交互可供)
u.face.hl_visible = true
u.face.hl_color = COL_SEAL # 已承诺封印标记(封印黑;无交互可供)
_set_cursor_shape(c, Input.CURSOR_FORBIDDEN)
else:
u.st.hl_visible = true
u.st.hl_color = UiKit.RED_FORBID # 不可及域灰禁框(禁止光标+首遇 toast 双通道)
u.face.hl_visible = true
u.face.hl_color = UiKit.RED_FORBID # 不可及域灰禁框(禁止光标+首遇 toast 双通道)
_set_cursor_shape(c, Input.CURSOR_FORBIDDEN)
hover_forbidden.emit(c)
@@ -536,7 +536,7 @@ func _apply_hover_visuals(c: Vector2i) -> void:
func _clear_hover_visuals(c: Vector2i) -> void:
if not _cells.has(c):
return
_cells[c].st.hl_visible = false
_cells[c].face.hl_visible = false
_hide_path_preview()
@@ -551,12 +551,12 @@ func _show_path_preview(c: Vector2i) -> void:
var path: Array[Variant] = _minesweeper.compute_move_path(c)
for pc in path:
if _cells.has(pc):
_cells[pc].st.dot_visible = true
_cells[pc].face.dot_visible = true
func _hide_path_preview() -> void:
for c in _cells:
_cells[c].st.dot_visible = false
_cells[c].face.dot_visible = false
# ---- 奖励徽章(已开格身份 icon——封印之下、纹带之上、不进数字区) ----
@@ -613,19 +613,19 @@ func _paint_cell(c: Vector2i, hero: Vector2i) -> void:
var is_hero: bool = c == hero
# 封印槽统一维护:封印格=封印印记纹理;其余格清 null(封印格挖开后残留在 tex_seal
# 会让 _draw 无条件把封印印画在已开格上——tex_seal 绘制无 revealed 条件)。缺盘走字形回退。
u.st.tex_seal = _palette.seal_tex if (seal and not revealed and _palette.seal_tex != null) else null
u.face.tex_seal = _palette.seal_tex if (seal and not revealed and _palette.seal_tex != null) else null
if removed:
revealed = false # 移除格固定未开呈现基座(退色暗幕层承载;危险格本无数字,周边数字冻结)
seal = false
u.st.tex_seal = null
u.face.tex_seal = null
# 绘制层五段(字形与色牌→基底瓦片→立面投影→秘宝/变异叠加→迷雾)——段间交换经单元状态面
#(u.st/u.glyph/u.glyph_color),唯一例外=基底色 bg 经 ink 段返回值交 base 段(无单元场域承载)
#(u.base/u.face 域件与 u.glyph/u.glyph_color),唯一例外=基底色 bg 经 ink 段返回值交 base 段(无单元场域承载)
var bg: Color = _paint_cell_ink(c, u, revealed, seal)
_paint_cell_base(c, u, revealed, is_hero, bg)
_paint_cell_facade(c, u, revealed)
_paint_cell_overlays(c, u, revealed)
_paint_cell_fog(c, u, revealed, hero)
u.st.removed_dim = removed
u.face.removed_dim = removed
u.queue_redraw()
@@ -696,17 +696,17 @@ func _paint_cell_base(c: Vector2i, u: Node2D, revealed: bool, is_hero: bool, bg:
# 贴图缺位回退字形「人」(项目纹理槽惯例——贴图在位=格内字形退役,单位本体由独立精灵承载)
u.glyph = "人"
u.glyph_color = COL_HERO
u.st.tex_base = tile
u.st.bg_sb = sb
u.base.tex_base = tile
u.base.bg_sb = sb
# 立面投影段:未开墙格南缘立面/轮廓描边码;已开格北缘投影暗带/E 格高墙面——段内重置与赋值同序
func _paint_cell_facade(c: Vector2i, u: Node2D, revealed: bool) -> void:
u.st.outline_sides.assign([]) # typed Array[int](assign 保持引用语义)
u.st.facade_south = false
u.st.proj_band = 0.0
u.st.tex_facade = null
u.st.facade_strip = false
u.base.outline_sides.assign([]) # typed Array[int](assign 保持引用语义)
u.base.facade_south = false
u.base.proj_band = 0.0
u.base.tex_facade = null
u.base.facade_strip = false
# 封印槽不在此重置:尾部重置会与此上方条件赋值构成双写踩踏(把封印纹理恒清 null=素材在位时封印不可见);
# 上方 else 分支已是全路径清残留,尾部重置系冗余且有害
var south_n: Vector2i = c + Vector2i(0, 1)
@@ -716,26 +716,26 @@ func _paint_cell_facade(c: Vector2i, u: Node2D, revealed: bool) -> void:
if not revealed:
# 高墙感 D 投影档:南邻地面格 → 墙格南缘立「口沿高光+双段暗带」立面
#(邻地面才画——墙-墙内界保墙群内聚);板外=墙不画
u.st.facade_south = south_in and _minesweeper.is_revealed(south_n)
u.base.facade_south = south_in and _minesweeper.is_revealed(south_n)
# 轮廓加强:墙格四邻中已开(地面)格朝向记描边码(板外=墙不描——保墙群外轮廓只在墙/地交界)
for d in [Vector2i(0, -1), Vector2i(0, 1), Vector2i(-1, 0), Vector2i(1, 0)]:
var n: Vector2i = c + d
if n.x >= 0 and n.y >= 0 and n.x < _board.width and n.y < _board.height and _minesweeper.is_revealed(n):
var code := 0 if d.y < 0 else (1 if d.y > 0 else (2 if d.x < 0 else 3))
if code == 1 and u.st.facade_south:
if code == 1 and u.base.facade_south:
continue # 南缘由立面件承载(高光+双段暗带)——2px 描边让位防三重叠黑
if code == 0 and _palette.tiles_ok:
continue # v3.5 冻结① (b)-(c) 交界无边框:墙面直贴接墙顶(E 格墙面件在邻格承载)——北向描边不画(平色回退态保留描边)
u.st.outline_sides.append(code)
u.base.outline_sides.append(code)
else:
# 地面投影:北邻墙格在本已开格北缘投渐隐暗带(ITB 同款高度线索——带宽按格边比例常量)
if north_in and not _minesweeper.is_revealed(north_n):
u.st.proj_band = u.st.edge * WALL_PROJECTION_FRAC
u.base.proj_band = u.edge * WALL_PROJECTION_FRAC
# E 格高墙面(圣经 v3.5 冻结①——方向稿 is_facade_cell 口径):已开格南邻未开墙 → 南部 30%
# 承接墙面(材质=同板墙顶 center 瓦片,原亮度直贴);仅瓦片态启用——平色回退保持原语义
if south_in and not _minesweeper.is_revealed(south_n) and _palette.tiles_ok:
u.st.facade_strip = true
u.st.tex_facade = _palette.tiles.get("wall_center")
u.base.facade_strip = true
u.base.tex_facade = _palette.tiles.get("wall_center")
# 秘宝/变异叠加段:真视红框/预读徽章/变异角标/奖励徽章/类型微光组号——glyph/digit_col 局部镜像
@@ -746,21 +746,21 @@ func _paint_cell_overlays(c: Vector2i, u: Node2D, revealed: bool) -> void:
var glyph: String = u.glyph
var digit_col: Color = u.glyph_color
if not revealed:
u.st.treasure_mark = "mine_reveal" if _treasure_reveal.has(c) else ""
u.face.treasure_mark = "mine_reveal" if _treasure_reveal.has(c) else ""
if _treasure_preview.has(c) and glyph == "":
var pid = _identity_cb.call(c) if _identity_cb.is_valid() else null
if pid != null and REWARD_BADGE.has(String(pid.get("cell_type", ""))):
glyph = String(REWARD_BADGE[String(pid.get("cell_type", ""))])
digit_col = COL_GOLD
u.st.badge_glyph = glyph
u.face.badge_glyph = glyph
# ②' 变异角标:未开格带伤害型变异 → 危险红角标(已开=威胁已兑现标记退位);
# 回调缺位 = 无标记(程序化角标,tex_mutation 纹带槽暂留)
if not revealed and _vmark_cb.is_valid():
u.st.mutation_mark = String(_vmark_cb.call(c))
u.face.mutation_mark = String(_vmark_cb.call(c))
else:
u.st.mutation_mark = ""
u.st.badge_glyph = _update_badge(c)
u.st.glow_group = int(_glow.get(c, UiKit.GLOW_NONE))
u.face.mutation_mark = ""
u.face.badge_glyph = _update_badge(c)
u.glow_group = int(_glow.get(c, UiKit.GLOW_NONE))
# 迷雾段:灯光区外未开格全黑/已开格记忆态灰显——纯呈现零逻辑(🔴 契约 1)
@@ -803,7 +803,7 @@ func _sync_glow_to_units() -> void:
for c in touched:
var u: Node2D = _cells.get(c)
if u != null:
u.st.glow_group = int(_glow.get(c, UiKit.GLOW_NONE))
u.glow_group = int(_glow.get(c, UiKit.GLOW_NONE))
func _process(delta: float) -> void:
@@ -815,14 +815,14 @@ func _process(delta: float) -> void:
if UiKit.motion_scale < 1.0:
return # 减动效:呼吸停格(refresh/enable 时已施静态色——静态可辨、无持续运动)
var k := 0.5 + 0.5 * sin(_breath_t * 3.0) # 3 rad/s≈0.48Hz——无 >3 次/秒频闪(AC 基线)
_contact_unit.st.contact_col = COL_CONTACT.lerp(UiKit.CONTACT_GLOW_DEEP, 0.55 - 0.35 * k)
_contact_unit.base.contact_col = COL_CONTACT.lerp(UiKit.CONTACT_GLOW_DEEP, 0.55 - 0.35 * k)
_contact_unit.queue_redraw()
# 带几何写入(refresh / set_dig_glow_enabled(true) 挂点):清旧承载件→按英雄格可挖域写边/角码→施色
func _apply_contact_bands() -> void:
if _contact_unit != null and is_instance_valid(_contact_unit) and (_cells.get(_minesweeper.hero_pos()) != _contact_unit if _minesweeper != null else true):
_contact_unit.st.contact_col = Color(0, 0, 0, 0)
_contact_unit.base.contact_col = Color(0, 0, 0, 0)
_contact_unit.queue_redraw()
_contact_unit = null
if _board == null or _minesweeper == null or not _dig_glow:
@@ -839,19 +839,19 @@ func _apply_contact_bands() -> void:
for d in [Vector2i(-1, -1), Vector2i(1, -1), Vector2i(-1, 1), Vector2i(1, 1)]:
if _diggable.has(_minesweeper.hero_pos() + d):
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.st.contact_bands = bands
hu.st.contact_corners = corners
hu.base.contact_bands = bands
hu.base.contact_corners = corners
# 减动效:呼吸停格——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.st.contact_col = COL_CONTACT.lerp(UiKit.CONTACT_GLOW_DEEP, 0.55 - 0.35 * k)
hu.base.contact_col = COL_CONTACT.lerp(UiKit.CONTACT_GLOW_DEEP, 0.55 - 0.35 * k)
hu.queue_redraw()
func _reset_breath() -> void:
if _contact_unit != null and is_instance_valid(_contact_unit):
_contact_unit.st.contact_col = Color(0, 0, 0, 0)
_contact_unit.base.contact_col = Color(0, 0, 0, 0)
_contact_unit.queue_redraw()
@@ -926,5 +926,5 @@ func cell_rect_probe(c: Vector2i) -> Rect2:
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.st.edge, u.st.edge))
var local := Rect2(u.global_position, Vector2(u.edge, u.edge))
return Rect2(host.position + local.position * k, local.size * k)
+204
View File
@@ -0,0 +1,204 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 格面基座层绘制域件(CellUnit 拆件——单一变化原因=基座层视觉规格):
## 底面/描边/高墙立面/地面投影/接触带微光/顶面倒角的全部绘制入参与绘制命令。
## 无 class_name(类名缓存面纪律)——经宿主 board_cell_unit preload 引用;宿主 _draw 按层序调
## draw_base(canvas, rect) 把绘制命令落到宿主画布;setter 经 _owner 回引触发宿主重绘语义逐字。
extends RefCounted
# ── D 投影档绘制常量(研究件实测取色——立面高光/双段暗带/地面投影;黑罩 alpha 叠加实测等效)──
const FACADE_HIGHLIGHT_PX := 2.0 # 口沿高光线厚(像素)
const FACADE_NEAR_FRAC := 0.16 # 立面近段高(格边比——研究件 10/64)
const FACADE_FAR_FRAC := 0.25 # 立面远段高(格边比——研究件 16/64)
const FACADE_NEAR_COL := Color(0, 0, 0, 0.60) # 立面近段黑罩(墙顶 132→52 实测 α≈0.61)
const FACADE_FAR_COL := Color(0, 0, 0, 0.82) # 立面远段黑罩(墙顶 132→24 实测 α≈0.82)
const PROJECTION_NEAR_COL := Color(0, 0, 0, 0.28) # 投影贴墙段(地面 66→46 实测 α≈0.28)
const PROJECTION_MID_COL := Color(0, 0, 0, 0.16) # 投影中段(渐隐)
const PROJECTION_EDGE_COL := Color(0, 0, 0, 0.06) # 投影远段(渐隐收边)
# ── E 格高墙面(圣经 v3.5 冻结①——地面格南邻墙→南部 30% 承接墙面:墙顶纹理原亮度
# 直贴零暗档零阴影;墙脚阴线只画地面/墙面交界;(b)-(c) 交界
# 无边框由 BoardView 侧跳过墙格北向描边承载)──
const E_FACADE_FRAC := 0.30 # 墙面承接高(格边比 30%)
var _owner: Node2D = null
var bg_sb: StyleBoxFlat = null # 格底 StyleBox(BoardView 每次 paint 重建;呼吸微光就地改边框)
# ── 接触带微光绘制面(可挖域微光画在英雄格周界空间侧——art-bible §3.2 v2.8;
# 仅英雄格单元承载,BoardView.refresh 按可挖域写边/角码、_process 逐拍更新呼吸色)──
var contact_bands: Array[int] = [] # 正交接触边码 0=N/1=S/2=W/3=E(该侧整条边)
var contact_corners: Array[int] = [] # 斜向共享角码 0=NW/1=NE/2=SW/3=SE(角点+两侧各半条边)
var contact_col := Color(0, 0, 0, 0) # 当前呼吸色(alpha 0 = 不画)
var outline_sides: Array[int] = [] # 墙格地面朝向描边码 0=N/1=S/2=W/3=E(程序侧结构件)
# ── 高墙感 D 投影档(研究件四变体定 D=C+地面投影,ITB 同款高度线索)──
# 南立面 = 墙格南缘内侧「口沿高光线 + 双段暗带」(邻地面格才画,墙群内聚);地面投影 = 北邻墙格
# 在本已开格北缘投渐隐暗带。参数按研究件 64px 拼版实测比例换算(立面双段 0.16/0.25 格边、投影带
# 0.22 格边;坐标推导留档 commit)。素材缺位程序化回退同样服役(结构归程序——art-bible §8.5)。
var facade_south := false: # 南立面开关(BoardView paint 按「南邻已开格」写入)
set(v):
facade_south = v
_owner.queue_redraw()
var proj_band := 0.0: # 地面投影带宽(像素,0 = 不画——BoardView 按北邻墙判定写入)
set(v):
proj_band = v
_owner.queue_redraw()
var bevel := 0.0: # 顶面倒角强度(0=关;档位 0/0.35/0.6/1.0)
set(v):
bevel = v
_owner.queue_redraw()
var tex_base: Texture2D = null # ① 砖面底(宿主迷雾记忆态公开单向读)
var tex_facade: Texture2D = null # ⑥ E 格墙面材质(v3.5——南邻墙顶 center 瓦片同源,BoardView 注入)
var facade_strip := false: # E 格开关(BoardView paint 按「已开格且南邻未开墙」写入——瓦片态专用)
set(v):
facade_strip = v
_owner.queue_redraw()
func _init(owner: Node2D) -> void:
_owner = owner
## ① 砖面底层域(占位 = StyleBoxFlat:格底色 + 暗化描边 + 3px 圆角;瓦片态描边覆盖层;
## 呼吸微光/主角蓝框由 BoardView 改 bg_sb 后触发重绘。素材缺位回退走 elif 程序化原样,零观感变化)
func draw_base(canvas: CanvasItem, rect: Rect2) -> void:
if tex_base != null:
canvas.draw_texture_rect(tex_base, rect, false)
# 轮廓加强(轮廓线不明显):墙格地面朝向边补 2px 深墨线(§8.5 结构归程序——
# 素材 1px 墨线在 45/36px 非整数缩放下视觉衰减,程序线保底;墙-墙内界不画,保墙群内聚;
# 坐标推导:各边内贴 2px 矩形,相邻边矩形全长相交=周界闭合)
if not outline_sides.is_empty():
_draw_outline_sides(canvas, rect)
_draw_south_facade(canvas, rect)
_draw_north_projection(canvas, rect)
_draw_e_facade(canvas, rect)
# 瓦片态描边覆盖层:tex_base 在位时 BoardView 传入的 bg_sb
# 已降级为「透明底 + 描边」载体——呼吸微光/主角蓝框画在瓦片之上(art-bible §1.2 叠层序
# 不变:底 < 纹带 < 数字 < 封印 < 焦点高亮);素材缺位回退走 elif 程序化原样,零观感变化
if bg_sb != null:
canvas.draw_style_box(bg_sb, rect)
_draw_contact_or_fallback(canvas, rect)
## 接触带微光或平色回退域(英雄格周界空间侧——正交邻墙=整条边;斜向=共享角点+两侧各半条边;光不进墙格):
## 带宽 4px 贴格边内侧(坐标推导:N=[0,0,edge,4] S=[0,edge-4,edge,4] W=[0,0,4,edge] E=[edge-4,0,4,edge];
## 半条边=沿邻边自角点起 edge/2;角点圆 r=6 贴角点圆心,近白芯 r=2.5 提亮)
func _draw_contact_or_fallback(canvas: CanvasItem, rect: Rect2) -> void:
if contact_col.a > 0.0:
_draw_north_projection(canvas, rect) # 平色回退+微光并发的英雄格补画(瓦片路径已画——同像素重复无观感差)
_draw_e_facade(canvas, rect) # E 格墙面补画(同上——英雄格兼 E 格时瓦片路径已画,重复无害)
_draw_contact_bands(canvas, rect)
elif bg_sb != null:
canvas.draw_style_box(bg_sb, rect)
_draw_south_facade(canvas, rect) # 平色回退路径——D 投影档两件同样服役(结构归程序)
_draw_north_projection(canvas, rect)
_draw_bevel(canvas, rect)
## 墙格轮廓域:地面朝向边补 2px 深墨线(各边内贴 2px 矩形,相邻边矩形全长相交=周界闭合)
func _draw_outline_sides(canvas: CanvasItem, rect: Rect2) -> void:
var ow := 2.0
var oc := UiKit.FOG_OUTLINE
for b in outline_sides:
var or_ := Rect2()
match int(b):
0: or_ = Rect2(0, 0, rect.size.x, ow)
1: or_ = Rect2(0, rect.size.x - ow, rect.size.x, ow)
2: or_ = Rect2(0, 0, ow, rect.size.x)
3: or_ = Rect2(rect.size.x - ow, 0, ow, rect.size.x)
canvas.draw_rect(or_, oc, true)
## 接触带微光域:正交整条边 + 斜向共享角点与两侧半条边
func _draw_contact_bands(canvas: CanvasItem, rect: Rect2) -> void:
var band := 4.0
var half := rect.size.x * 0.5
for b in contact_bands:
var r := Rect2()
match int(b):
0: r = Rect2(0, 0, rect.size.x, band)
1: r = Rect2(0, rect.size.x - band, rect.size.x, band)
2: r = Rect2(0, 0, band, rect.size.x)
3: r = Rect2(rect.size.x - band, 0, band, rect.size.x)
canvas.draw_rect(r, contact_col, true)
for cc in contact_corners:
_draw_contact_corner(canvas, rect, int(cc), band, half)
## 斜向角点域:角点圆 r=6 贴角点圆心 + 近白芯 r=2.5 提亮 + 半条边(角点沿两条邻边各延伸 edge/2——
## 坐标推导:横带沿 N/S 自角点向板心侧 half;NW/SE 半带 x=y=角点坐标,NE/SE 半带自 edge-half 收进——
## 正交整边时重叠同色无害)
func _draw_contact_corner(canvas: CanvasItem, rect: Rect2, cc: int, band: float, half: float) -> void:
var pt := Vector2.ZERO
match cc:
0: pt = Vector2(0, 0)
1: pt = Vector2(rect.size.x, 0)
2: pt = Vector2(0, rect.size.x)
3: pt = Vector2(rect.size.x, rect.size.x)
canvas.draw_circle(pt, 6.0, contact_col)
canvas.draw_circle(pt, 2.5, Color(1, 1, 1, minf(1.0, contact_col.a * 0.95)))
if cc == 0 or cc == 1:
# N 侧横带:NW 自 x=0、NE 自 x=edge-half
canvas.draw_rect(Rect2(pt.x if cc == 0 else pt.x - half, 0.0, half, band), contact_col, true)
if cc == 2 or cc == 3:
# S 侧横带:SW 自 x=0、SE 自 x=edge-half
canvas.draw_rect(Rect2(pt.x if cc == 2 else pt.x - half, rect.size.x - band, half, band), contact_col, true)
if cc == 0 or cc == 2:
# W 侧竖带:NW 自 y=0、SW 自 y=edge-half
canvas.draw_rect(Rect2(0.0, pt.y if cc == 0 else pt.y - half, band, half), contact_col, true)
if cc == 1 or cc == 3:
# E 侧竖带:NE 自 y=0、SE 自 y=edge-half
canvas.draw_rect(Rect2(rect.size.x - band, pt.y if cc == 1 else pt.y - half, band, half), contact_col, true)
## 顶面倒角域(光源左上——上/左内沿受光 2px、下/右内沿背光 2px;内缩 2px 让
## 呼吸微光/主角蓝边框保持最外沿可见;强度随档位)
func _draw_bevel(canvas: CanvasItem, rect: Rect2) -> void:
if bevel <= 0.0:
return
var base: Color = bg_sb.bg_color
var lit: Color = base.lightened(0.22 * bevel)
var dark: Color = base.darkened(0.22 * bevel)
var t := 2.0
canvas.draw_rect(Rect2(t, t, rect.size.x - t * 2.0, t), lit, true)
canvas.draw_rect(Rect2(t, t, t, rect.size.x - t * 2.0), lit, true)
canvas.draw_rect(Rect2(t, rect.size.x - t * 2.0, rect.size.x - t * 2.0, t), dark, true)
canvas.draw_rect(Rect2(rect.size.x - t * 2.0, t, t, rect.size.x - t * 2.0), dark, true)
# 南立面(D 档件一——墙格南缘内侧,自底边向上:双段暗带在下、口沿高光线贴其上沿):
# 坐标推导(自格底边起算):远段=[edge−far, edge],近段=[edge−far−near, edge−far],
# 高光线=[edge−far−near−hl, edge−far−near]——与研究件「墙顶面→高光→暗段→更暗段→格界」下行序一致
func _draw_south_facade(canvas: CanvasItem, rect: Rect2) -> void:
if not facade_south:
return
var far_h := rect.size.x * FACADE_FAR_FRAC
var near_h := rect.size.x * FACADE_NEAR_FRAC
var y_far := rect.size.x - far_h
var y_near := y_far - near_h
var y_hl := y_near - FACADE_HIGHLIGHT_PX
canvas.draw_rect(Rect2(0.0, y_hl, rect.size.x, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点)
canvas.draw_rect(Rect2(0.0, y_near, rect.size.x, near_h), FACADE_NEAR_COL, true)
canvas.draw_rect(Rect2(0.0, y_far, rect.size.x, far_h), FACADE_FAR_COL, true)
# 地面投影(D 档件二——北邻墙格在本已开格北缘投渐隐暗带,自北缘向南三段衰减):
# 坐标推导:三等分 proj_band,α 0.28/0.16/0.06(研究件实测贴墙 α≈0.28、带末渐融入地面)
func _draw_north_projection(canvas: CanvasItem, rect: Rect2) -> void:
if proj_band <= 0.0:
return
var seg := proj_band / 3.0
canvas.draw_rect(Rect2(0.0, 0.0, rect.size.x, seg), PROJECTION_NEAR_COL, true)
canvas.draw_rect(Rect2(0.0, seg, rect.size.x, seg), PROJECTION_MID_COL, true)
canvas.draw_rect(Rect2(0.0, seg * 2.0, rect.size.x, seg), PROJECTION_EDGE_COL, true)
# E 格高墙面(圣经 v3.5 冻结①——方向稿 draw_facade 移植):本格已开且南邻为墙 →
# 南部 30% 承接墙面。材质 = 墙顶 center 瓦片顶部 30% 区带**原亮度**直贴(零暗档零阴影);
# 墙脚阴线只画地面/墙面交界(strip 顶缘 1px);墙面底缘(格界处)不画任何线——直接接南侧墙顶
# (白边导致两截)。仅瓦片态服役(tex_facade 缺位 = 平色回退原语义不动)。
func _draw_e_facade(canvas: CanvasItem, rect: Rect2) -> void:
if not facade_strip or tex_facade == null:
return
var strip_h := rect.size.x * E_FACADE_FRAC
var y0 := rect.size.x - strip_h
var ts := tex_facade.get_size()
canvas.draw_texture_rect_region(tex_facade, Rect2(0.0, y0, rect.size.x, strip_h),
Rect2(0.0, 0.0, ts.x, ts.y * E_FACADE_FRAC))
canvas.draw_rect(Rect2(0.0, y0, rect.size.x, 1.0), UiKit.E_FACADE_FOOT_COL, true)
@@ -0,0 +1,145 @@
## VERTICAL SLICE - NOT FOR PRODUCTION
## 格面标识与顶层叠加绘制域件(CellUnit 拆件——单一变化原因=标识与叠加层视觉规格):
## 变异标记/数字色牌与字形/奖励徽章/封印/路径点迹/焦点高亮/中立移除退色的全部绘制入参与绘制命令。
## 无 class_name(类名缓存面纪律)——经宿主 board_cell_unit preload 引用;宿主 _draw 按层序调
## draw_marks/draw_glyph_layer/draw_overlay_under/draw_removed_dim(雾层由宿主夹在 overlay_under 与 removed_dim 之间);
## setter 经 _owner 回引触发宿主重绘语义逐字(badge_glyph 的 glow mask 回引 _owner._glow_layer 原样)。
extends RefCounted
var _owner: Node2D = null
var badge_glyph := "": # 奖励徽章文字(右下角金——封印之下、变异纹之上、不进数字区)
set(v):
badge_glyph = v
if _owner._glow_layer != null and is_instance_valid(_owner._glow_layer):
_owner._glow_layer.set_badge_mask(v != "")
_owner.queue_redraw()
var dot_visible := false: # BFS 路径预览点迹(中心 6×6 小人蓝)
set(v):
dot_visible = v
_owner.queue_redraw()
var hl_visible := false: # 焦点高亮(悬停态边框覆盖层)
set(v):
hl_visible = v
_owner.queue_redraw()
var hl_color := UiKit.GOLD: # 高亮色(金目标/封印朱/禁入灰禁红)
set(v):
hl_color = v
_owner.queue_redraw()
var font: Font = null # 字体槽位(null = ThemeDB.fallback_font——BoardView.set_world_font 注入)
# ── 素材槽位(现全 null = 程序化占位服役;art-bible §8.2 格面 32×32 暂定档)──
var tex_mutation: Texture2D = null # ② 变异纹带
var tex_glyph: Texture2D = null # ③ 数字
var tex_badge: Texture2D = null # ③' 徽章
var tex_seal: Texture2D = null: # ④ 封印(槽位可运行时换装〔封印〕——同族 facade/treasure_mark
set(v): # 均 setter 触发重绘(普通 var 赋值不重绘=封印插上不显示))
if tex_seal != v:
tex_seal = v
_owner.queue_redraw()
# 变异角标(程序化,tex_mutation 纹带槽暂留):
# "flat_damage" = 未开格伤害型变异,左下角危险红三角(开格有代价的策略纹理标记)
# 叠加层标记(秘宝整图叠加层的格面呈现;派生信息层零回写):
# "mine_reveal" = 真视格(危险红内缩框——「这格是雷」的确定决策标记,封印照常计费);
# "reward_preview" 格不复用本字段——预读徽章走 badge_glyph 注入(board_view refresh 注入 REWARD_BADGE 字形)
var treasure_mark := "":
set(v):
if treasure_mark != v:
treasure_mark = v
_owner.queue_redraw()
var mutation_mark := "":
set(v):
if mutation_mark != v:
mutation_mark = v
_owner.queue_redraw()
var removed_dim := false: # 中立移除退色层(BoardView paint 按排雷玩法 is_removed 写入)
set(v):
removed_dim = v
_owner.queue_redraw()
func _init(owner: Node2D) -> void:
_owner = owner
## 变异标记域:真视格=危险红内缩框/未开格伤害型变异=左下角危险红三角
func draw_marks(canvas: CanvasItem, rect: Rect2) -> void:
if treasure_mark == "mine_reveal":
var inset := rect.size.x * 0.14
canvas.draw_rect(Rect2(inset, inset, rect.size.x - inset * 2.0, rect.size.x - inset * 2.0),
Color(UiKit.RED_MINE, 0.9), false, 2.0)
if mutation_mark == "flat_damage":
var mw := rect.size.x * 0.28
var mpts := PackedVector2Array([Vector2(0, rect.size.x - mw), Vector2(0, rect.size.x), Vector2(mw, rect.size.x)])
canvas.draw_colored_polygon(mpts, Color(UiKit.RED_MINE, 0.85))
## ②③④ 中层域:变异纹带与标记/数字色牌与字形/徽章/封印
##(glyph/glyph_color/plate 三者住宿主冻结面——探针直读成员,作参数传入)
func draw_glyph_layer(canvas: CanvasItem, rect: Rect2, glyph: String, glyph_color: Color, plate: bool) -> void:
# ② 变异纹带(无程序化纹带——槽位先行,贴图落位即用)
if tex_mutation != null:
canvas.draw_texture_rect(tex_mutation, rect, false)
# ③ 数字/徽章(数字 ≥ 格高 50% 墨族对格底 ≥4.5:1;徽章右下角金,遮蔽态 = "?")
if glyph != "":
if plate:
_draw_digit_plate(canvas, rect)
if tex_glyph != null:
canvas.draw_texture_rect(tex_glyph, rect, false)
else:
draw_centered_text(canvas, rect, glyph, glyph_color, maxi(18, int(ceil(rect.size.x * 0.5)))) # 字号下限 18(§7.4 冻结公式)
if badge_glyph != "":
if tex_badge != null:
canvas.draw_texture_rect(tex_badge, rect, false)
else:
_draw_badge(canvas, rect, badge_glyph)
# ④ 封印(现行视觉封印文字在 ③ 文字位承载——独立槽位留给封印图)
if tex_seal != null:
canvas.draw_texture_rect(tex_seal, rect, false)
## 数字色牌后备域(圣经 v3.5 冻结④:数字亮墨直画已开格底须垫牌;
## 牌=后备件——雷/「?」维持牌载保读性(牌上深墨族 12:1);
## 牌尺寸 0.60/0.62 格边居中;封印=未开格不垫牌)
func _draw_digit_plate(canvas: CanvasItem, rect: Rect2) -> void:
var pw := rect.size.x * 0.60
var ph := rect.size.x * 0.62
var pr := Rect2((rect.size.x - pw) * 0.5, (rect.size.x - ph) * 0.5, pw, ph)
canvas.draw_rect(pr, UiKit.DIGIT_PLATE, true)
canvas.draw_rect(pr, UiKit.DIGIT_PLATE_EDGE, false, 1.0)
## ⑤ 路径点迹 + 焦点高亮(悬停态覆盖层,不参与命中)——宿主时序器在其后插雾层
func draw_overlay_under(canvas: CanvasItem, rect: Rect2) -> void:
if dot_visible:
var ds := 6.0
canvas.draw_rect(Rect2(Vector2((rect.size.x - ds) * 0.5, (rect.size.x - ds) * 0.5), Vector2(ds, ds)), UiKit.HERO_BLUE, true)
if hl_visible:
# 悬停高亮减淡(金框太明显——绘制时 55% alpha,hl_color 属性与探针断言面不动)
canvas.draw_rect(Rect2(1, 1, rect.size.x - 2.0, rect.size.x - 2.0), Color(hl_color, 0.55), false, 2.0) # 2px 内描边
## ⑦ 中立移除退色层(退色降档:暗幕压顶全层覆盖,暗淡记忆态同族呈现语义;
## 顶层于迷雾之上——移除格不再参与迷雾语义。本实现移除集=未揭示危险格(无数字无封印印))
func draw_removed_dim(canvas: CanvasItem, rect: Rect2) -> void:
if removed_dim:
canvas.draw_rect(rect, Color(0.12, 0.10, 0.09, 0.62), true)
## 文字位绘制单点(宿主迷雾记忆态复用——同公式同字号;水平居中 + 垂直居中,基线 = (格高 − 字高)/2 + 上伸部)
func draw_centered_text(canvas: CanvasItem, rect: Rect2, text: String, col: Color, fs: int) -> void:
var f := font if font != null else ThemeDB.fallback_font
if f == null:
return
var asc := f.get_ascent(fs)
var desc := f.get_descent(fs)
canvas.draw_string(f, Vector2(0.0, (rect.size.x - (asc + desc)) * 0.5 + asc), text,
HORIZONTAL_ALIGNMENT_CENTER, rect.size.x, fs, col)
# 徽章绘制:右下角金(文字贴角内缩 2–3px)
func _draw_badge(canvas: CanvasItem, rect: Rect2, g: String) -> void:
var f := font if font != null else ThemeDB.fallback_font
if f == null:
return
var fs := maxi(10, int(rect.size.x * 0.28))
var sz := f.get_string_size(g, HORIZONTAL_ALIGNMENT_LEFT, -1, fs)
canvas.draw_string(f, Vector2(rect.size.x - sz.x - 2.0, rect.size.x - 3.0), g, HORIZONTAL_ALIGNMENT_LEFT, -1, fs, UiKit.GOLD)