Files
gplan-2/game/presentation/views/board_cell_unit.gd
T

332 lines
19 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# VERTICAL SLICE - NOT FOR PRODUCTION
# 棋盘 Node2D 迁移批 Phase A 2026-09-23:CellUnit——BoardView SubViewport 世界内逐格渲染件(Node2D)。
# 固定叠层序(ADR-0006 Decision 4):砖面底 < 变异纹带 < 数字/徽章 < 旗 < 焦点高亮——本批全部程序化
# _draw 等价平移现行占位视觉;每层留 texture 槽位(art-bible §8.2 格面 32×32 暂定档——终稿 1:1 换入,
# 本批不实现纹理加载)。无 class_name(类名缓存面纪律——BoardView 经 preload 使用)。
# 字体:font 槽位缺省 = ThemeDB.fallback_font(与旧 Button 缺省主题字体同源);注入挂点归
# BoardView.set_world_font(SubViewport 内主题不自动继承主场景,Phase B 复用面)。
# 行为等价(缺陷①回归约束):格件常驻定长、逐格重绘由 BoardView 驱动,本类零游戏状态——渲染数据
# 全部由 BoardView 每次 paint 写入(bg_sb/glyph/badge),呼吸微光由 BoardView 就地改 bg_sb 边框。
extends Node2D
var cell := Vector2i.ZERO # 板坐标(诊断观测面)
var edge := 46.0: # 格边长(BoardView.set_cell_area 单点驱动)
set(v):
edge = v
queue_redraw()
var bg_sb: StyleBoxFlat = null # 格底 StyleBox(BoardView 每次 paint 重建;呼吸微光就地改边框)
# ── 接触带微光绘制面(2026-09-26 批:可挖域微光自"墙格整框"改"英雄格周界空间侧"——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 plate := false # 数字色牌开关(BoardView paint 按已开格 glyph 写入——原则二)
var outline_sides: Array[int] = [] # 墙格地面朝向描边码 0=N/1=S/2=W/3=E(轮廓加强批——程序侧结构件)
# ── 高墙感 D 投影档(2026-09-28 挑档落码——研究件四变体用户裁 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
queue_redraw()
var proj_band := 0.0: # 地面投影带宽(像素,0 = 不画——BoardView 按北邻墙判定写入)
set(v):
proj_band = v
queue_redraw()
var glyph := "": # 文字位——数字/雷/旗/人/?(现行视觉旗与数字互斥,同位承载)
set(v):
glyph = v
queue_redraw()
var glyph_color := UiKit.INK: # 文字色(墨族/雷朱/旗朱/小人蓝)
set(v):
glyph_color = v
queue_redraw()
var badge_glyph := "": # 奖励徽章文字(右下角金——旗之下、变异纹之上、不进数字区)
set(v):
badge_glyph = v
queue_redraw()
var dot_visible := false: # BFS 路径预览点迹(中心 6×6 小人蓝——旧 ColorRect 等价)
set(v):
dot_visible = v
queue_redraw()
var hl_visible := false: # 焦点高亮(悬停态边框覆盖层)
set(v):
hl_visible = v
queue_redraw()
var hl_color := UiKit.GOLD: # 高亮色(金目标/旗朱/禁入灰禁红)
set(v):
hl_color = v
queue_redraw()
var font: Font = null # 字体槽位(null = ThemeDB.fallback_font——BoardView.set_world_font 注入)
var bevel := 0.0: # 顶面倒角强度(0=关——立体原型批 2026-09-24;档位 0/0.35/0.6/1.0)
set(v):
bevel = v
queue_redraw()
# ── 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 冻结① 2026-09-30——地面格南邻墙→南部 30% 承接墙面:墙顶纹理原亮度
# 直贴零暗档零阴影〔用户终裁「它们是同样颜色的」〕;墙脚阴线只画地面/墙面交界;(b)-(c) 交界
# 无边框由 BoardView 侧跳过墙格北向描边承载)──
const E_FACADE_FRAC := 0.30 # 墙面承接高(格边比——用户指令 30%)
# ── 素材槽位(本批全 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_flag: Texture2D = null: # ④ 旗(T17 修复:槽位可运行时换装〔插旗〕——同族 facade/treasure_mark
set(v): # 均 setter 触发重绘;旧普通 var 赋值不重绘=旗插上不显示)
if tex_flag != v:
tex_flag = v
queue_redraw()
var tex_facade: Texture2D = null # ⑥ E 格墙面材质(v3.5——南邻墙顶 center 瓦片同源,BoardView 注入)
var facade_strip := false: # E 格开关(BoardView paint 按「已开格且南邻未开墙」写入——瓦片态专用)
set(v):
facade_strip = v
queue_redraw()
# 变异角标(迭代 6 T6——程序化,tex_mutation 纹带槽留给变异纹带六型自产批):
# "flat_damage" = 未开格伤害型变异,左下角危险红三角(开格有代价的策略纹理标记)
# 叠加层标记(迭代 7 T4——秘宝板级叠加层的格面呈现;派生信息层零回写):
# "mine_reveal" = 真视格(危险红内缩框——「这格是雷」的确定决策标记,插旗照常计费);
# "reward_preview" 格不复用本字段——预读徽章走 badge_glyph 注入(board_view refresh 注入 REWARD_BADGE 字形)
var treasure_mark := "":
set(v):
if treasure_mark != v:
treasure_mark = v
queue_redraw()
var mutation_mark := "":
set(v):
if mutation_mark != v:
mutation_mark = v
queue_redraw()
# ── 视野迷雾呈现态(迭代 11 第④批 T2/T3——纯呈现零逻辑;规格=art-bible §6.5 v3.5,正主=HUD框架「视野迷雾」节)──
# 0=正常(灯光区内)/ 1=全黑(区外未开格——正主「区外未开格=全黑不渲染」不透明档)/ 2=记忆态(区外已开格灰显可读)
var fog_state := 0:
set(v):
if fog_state != v:
fog_state = v
queue_redraw()
var removed_dim := false: # 中立移除退色层(T8/D6——BoardView paint 按排雷玩法 is_removed 写入)
set(v):
removed_dim = v
queue_redraw()
func _draw() -> void:
var rect := Rect2(Vector2.ZERO, Vector2(edge, edge))
# ① 砖面底(占位 = StyleBoxFlat:格底色 + 暗化描边 + 3px 圆角——旧 Button normal 样式等价;
# 呼吸微光/主角蓝框由 BoardView 改 bg_sb 后触发重绘)
if tex_base != null:
draw_texture_rect(tex_base, rect, false)
# 轮廓加强(2026-09-26 用户反馈「轮廓线不明显」):墙格地面朝向边补 2px 深墨线(§8.5 结构归程序——
# 素材 1px 墨线在 45/36px 非整数缩放下视觉衰减,程序线保底;墙-墙内界不画,保墙群内聚;
# 坐标推导:各边内贴 2px 矩形,相邻边矩形全长相交=周界闭合)
if not outline_sides.is_empty():
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, edge, ow)
1: or_ = Rect2(0, edge - ow, edge, ow)
2: or_ = Rect2(0, 0, ow, edge)
3: or_ = Rect2(edge - ow, 0, ow, edge)
draw_rect(or_, oc, true)
_draw_south_facade()
_draw_north_projection()
_draw_e_facade()
# 瓦片态描边覆盖层(高墙瓦片接线批 2026-09-26):tex_base 在位时 BoardView 传入的 bg_sb
# 已降级为「透明底 + 描边」载体——呼吸微光/主角蓝框画在瓦片之上(art-bible §1.2 叠层序
# 不变:底 < 纹带 < 数字 < 旗 < 焦点高亮);素材缺位回退走 elif 程序化原样,零观感变化
if bg_sb != null:
draw_style_box(bg_sb, 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 提亮)
if contact_col.a > 0.0:
_draw_north_projection() # 平色回退+微光并发的英雄格补画(瓦片路径已画——同像素重复无观感差)
_draw_e_facade() # E 格墙面补画(同上——英雄格兼 E 格时瓦片路径已画,重复无害)
var band := 4.0
var half := edge * 0.5
for b in contact_bands:
var r := Rect2()
match int(b):
0: r = Rect2(0, 0, edge, band)
1: r = Rect2(0, edge - band, edge, band)
2: r = Rect2(0, 0, band, edge)
3: r = Rect2(edge - band, 0, band, edge)
draw_rect(r, contact_col, true)
for cc in contact_corners:
var pt := Vector2.ZERO
match int(cc):
0: pt = Vector2(0, 0)
1: pt = Vector2(edge, 0)
2: pt = Vector2(0, edge)
3: pt = Vector2(edge, edge)
draw_circle(pt, 6.0, contact_col)
draw_circle(pt, 2.5, Color(1, 1, 1, minf(1.0, contact_col.a * 0.95)))
# 半条边:角点沿两条邻边各延伸 edge/2(坐标推导:横带沿 N/S 自角点向板心侧 half;
# NW/SE 半带 x=y=角点坐标,NE/SE 半带自 edge-half 收进——正交整边时重叠同色无害)
if int(cc) == 0 or int(cc) == 1:
# N 侧横带:NW 自 x=0、NE 自 x=edge-half
draw_rect(Rect2(pt.x if int(cc) == 0 else pt.x - half, 0.0, half, band), contact_col, true)
if int(cc) == 2 or int(cc) == 3:
# S 侧横带:SW 自 x=0、SE 自 x=edge-half
draw_rect(Rect2(pt.x if int(cc) == 2 else pt.x - half, edge - band, half, band), contact_col, true)
if int(cc) == 0 or int(cc) == 2:
# W 侧竖带:NW 自 y=0、SW 自 y=edge-half
draw_rect(Rect2(0.0, pt.y if int(cc) == 0 else pt.y - half, band, half), contact_col, true)
if int(cc) == 1 or int(cc) == 3:
# E 侧竖带:NE 自 y=0、SE 自 y=edge-half
draw_rect(Rect2(edge - band, pt.y if int(cc) == 1 else pt.y - half, band, half), contact_col, true)
elif bg_sb != null:
draw_style_box(bg_sb, rect)
_draw_south_facade() # 平色回退路径——D 投影档两件同样服役(结构归程序)
_draw_north_projection()
# 顶面倒角(立体原型批:光源左上——上/左内沿受光 2px、下/右内沿背光 2px;内缩 2px 让
# 呼吸微光/主角蓝边框保持最外沿可见;强度随档位)
if bevel > 0.0:
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
draw_rect(Rect2(t, t, edge - t * 2.0, t), lit, true)
draw_rect(Rect2(t, t, t, edge - t * 2.0), lit, true)
draw_rect(Rect2(t, edge - t * 2.0, edge - t * 2.0, t), dark, true)
draw_rect(Rect2(edge - t * 2.0, t, t, edge - t * 2.0), dark, true)
# ② 变异纹带(占位批次无程序化纹带——槽位先行,终稿换入即用)
if tex_mutation != null:
draw_texture_rect(tex_mutation, rect, false)
# ②' 变异角标(迭代 6 T6):左下角危险红三角(危险红=「开此格有代价」语义位,与雷族同源;
# 尺寸随格边比例——灰色可辨辅助=红对砖面明度差,禁喧宾)
# ②" 秘宝叠加层(迭代 7 T4):真视格=危险红内缩框(策略纹理——确定决策输入不喧宾)
if treasure_mark == "mine_reveal":
var inset := edge * 0.14
draw_rect(Rect2(inset, inset, edge - inset * 2.0, edge - inset * 2.0),
Color(UiKit.RED_MINE, 0.9), false, 2.0)
if mutation_mark == "flat_damage":
var mw := edge * 0.28
var mpts := PackedVector2Array([Vector2(0, edge - mw), Vector2(0, edge), Vector2(mw, edge)])
draw_colored_polygon(mpts, Color(UiKit.RED_MINE, 0.85))
# ③ 数字/徽章(数字 ≥ 格高 50% 墨族对格底 ≥4.5:1;徽章右下角金,遮蔽态 = "?")
if glyph != "":
# 数字色牌后备路径(圣经 v3.5 冻结④ 批 B2 2026-09-30:数字亮墨直画已开格底**退役必垫牌**;
# 牌=后备件——雷/「?」维持牌载保读性(牌上深墨族 12:1),复杂格底〔物件纹样/装饰变体格〕
# 判定随装饰瓦片自产批扩。牌尺寸 0.60/0.62 格边居中 = 样张 v2 冻结值;旗=未开格不垫牌)
if plate:
var pw := edge * 0.60
var ph := edge * 0.62
var pr := Rect2((edge - pw) * 0.5, (edge - ph) * 0.5, pw, ph)
draw_rect(pr, UiKit.DIGIT_PLATE, true)
draw_rect(pr, UiKit.DIGIT_PLATE_EDGE, false, 1.0)
if tex_glyph != null:
draw_texture_rect(tex_glyph, rect, false)
else:
_draw_glyph(glyph, glyph_color, maxi(18, int(ceil(edge * 0.5)))) # 字号下限 18(§7.4 冻结公式)
if badge_glyph != "":
if tex_badge != null:
draw_texture_rect(tex_badge, rect, false)
else:
_draw_badge(badge_glyph)
# ④ 旗(现行视觉旗文字在 ③ 文字位承载——独立槽位留给终稿旗图)
if tex_flag != null:
draw_texture_rect(tex_flag, rect, false)
# ⑤ 路径点迹 + 焦点高亮(悬停态覆盖层,不参与命中)
if dot_visible:
var ds := 6.0
draw_rect(Rect2(Vector2((edge - ds) * 0.5, (edge - ds) * 0.5), Vector2(ds, ds)), UiKit.HERO_BLUE, true)
if hl_visible:
# 悬停高亮减淡(2026-09-26 用户反馈:金框太明显——绘制时 55% alpha,hl_color 属性与探针断言面不动)
draw_rect(Rect2(1, 1, edge - 2.0, edge - 2.0), Color(hl_color, 0.55), false, 2.0) # 2px 内描边(旧 hl Panel 等价)
# ⑥ 视野迷雾层(迭代 11 第④批 T2/T3——最顶遮罩,§6.5 落文):
# 全黑=不透明纯黑盖全部叠层(含旗印/徽章/高亮——旗印记随格隐没,§6.5 显式推论);
# 记忆态=记忆板素色压平(纹理免疫——色牌/旗印/徽章随板隐没)+数字记忆墨重画(数字可读硬指标 ≥4.5:1)
if fog_state == 1:
draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(0, 0, 0, 1.0), true)
elif fog_state == 2:
# 记忆态换装(P10 乙 2026-10-06——demo 口径「地板纹理 + 灰罩 α.66」:原 #3A3A3A 平涂退役;
# 地板纹理原样直画 + FOG_MEMORY_PLATE α.66 灰罩压顶;素材零改动、格边长零改动。
# 记忆墨 FOG_MEMORY_INK 对合成底实测 ≈6.7:1 ≥4.5:1(§7.2 硬指标;floor avg #453020 复算在册))
if tex_base != null:
draw_texture_rect(tex_base, Rect2(Vector2.ZERO, Vector2(edge, edge)), false)
draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(UiKit.FOG_MEMORY_PLATE, 0.66), true)
if glyph != "":
_draw_glyph(glyph, UiKit.FOG_MEMORY_INK, maxi(18, int(ceil(edge * 0.5)))) # 字号下限 18(§7.4 冻结公式同源)
# ⑦ 中立移除退色层(迭代 13 T8/D6——P2 甲退色降档:暗幕压顶全层覆盖,暗淡记忆态同族呈现语义;
# 顶层于迷雾之上——移除格不再参与迷雾语义。本实现移除集=未揭示雷格(无数字无旗印))
if removed_dim:
draw_rect(Rect2(Vector2.ZERO, Vector2(edge, edge)), Color(0.12, 0.10, 0.09, 0.62), true)
# 南立面(D 档件一——墙格南缘内侧,自底边向上:双段暗带在下、口沿高光线贴其上沿):
# 坐标推导(自格底边起算):远段=[edge−far, edge],近段=[edge−far−near, edge−far],
# 高光线=[edge−far−near−hl, edge−far−near]——与研究件「墙顶面→高光→暗段→更暗段→格界」下行序一致
func _draw_south_facade() -> void:
if not facade_south:
return
var far_h := edge * FACADE_FAR_FRAC
var near_h := edge * FACADE_NEAR_FRAC
var y_far := 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, edge, FACADE_HIGHLIGHT_PX), UiKit.STAND_STONE_RIM, true) # 口沿高光(UiKit.STAND_STONE_RIM 并档——同值单点,清扫批 2026-10-02)
draw_rect(Rect2(0.0, y_near, edge, near_h), FACADE_NEAR_COL, true)
draw_rect(Rect2(0.0, y_far, edge, far_h), FACADE_FAR_COL, true)
# 地面投影(D 档件二——北邻墙格在本已开格北缘投渐隐暗带,自北缘向南三段衰减):
# 坐标推导:三等分 proj_band,α 0.28/0.16/0.06(研究件实测贴墙 α≈0.28、带末渐融入地面)
func _draw_north_projection() -> void:
if proj_band <= 0.0:
return
var seg := proj_band / 3.0
draw_rect(Rect2(0.0, 0.0, edge, seg), PROJECTION_NEAR_COL, true)
draw_rect(Rect2(0.0, seg, edge, seg), PROJECTION_MID_COL, true)
draw_rect(Rect2(0.0, seg * 2.0, edge, seg), PROJECTION_EDGE_COL, true)
# E 格高墙面(圣经 v3.5 冻结① 2026-09-30——方向稿 draw_facade 移植):本格已开且南邻为墙 →
# 南部 30% 承接墙面。材质 = 墙顶 center 瓦片顶部 30% 区带**原亮度**直贴(零暗档零阴影);
# 墙脚阴线只画地面/墙面交界(strip 顶缘 1px);墙面底缘(格界处)不画任何线——直接接南侧墙顶
# (v3.4 用户裁定:白边导致两截)。仅瓦片态服役(tex_facade 缺位 = 平色回退原语义不动)。
func _draw_e_facade() -> void:
if not facade_strip or tex_facade == null:
return
var strip_h := edge * E_FACADE_FRAC
var y0 := edge - strip_h
var ts := tex_facade.get_size()
draw_texture_rect_region(tex_facade, Rect2(0.0, y0, edge, strip_h),
Rect2(0.0, 0.0, ts.x, ts.y * E_FACADE_FRAC))
draw_rect(Rect2(0.0, y0, edge, 1.0), UiKit.E_FACADE_FOOT_COL, true)
# 文字位绘制:水平居中 + 垂直居中(基线 = (格高 − 字高)/2 + 上伸部——旧 Button 居中对齐等价)
func _draw_glyph(g: 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)
draw_string(f, Vector2(0.0, (edge - (asc + desc)) * 0.5 + asc), g,
HORIZONTAL_ALIGNMENT_CENTER, edge, fs, col)
# 徽章绘制:右下角金(旧 Label BOTTOM_RIGHT 锚点等价——文字贴角内缩 2–3px)
func _draw_badge(g: String) -> void:
var f := font if font != null else ThemeDB.fallback_font
if f == null:
return
var fs := maxi(10, int(edge * 0.28))
var sz := f.get_string_size(g, HORIZONTAL_ALIGNMENT_LEFT, -1, fs)
draw_string(f, Vector2(edge - sz.x - 2.0, edge - 3.0), g, HORIZONTAL_ALIGNMENT_LEFT, -1, fs, UiKit.GOLD)