diff --git a/game/presentation/views/standee_body.gd b/game/presentation/views/standee_body.gd index 78306a59..983a465b 100644 --- a/game/presentation/views/standee_body.gd +++ b/game/presentation/views/standee_body.gd @@ -22,6 +22,9 @@ const CHARGE_SKILL_IDS := ["heavy"] # 护甲类型图标占位——形状+色相双通道(art-bible):轻甲=◇ 灰石中性 / 魔法护甲=◆ 小人蓝 / # 重甲=▣ 深金(避开 Boss 专属紫赤与妨害紫)。(自旧 StandeeView 平移) const ARMOR_ICONS := {"light": "◇", "ward": "◆", "heavy": "▣"} +# 名行/底座纵向预算余量(UI 设计对齐批 N2 2026-10-06——旧名行基线 = 盒底−1px「声明值刚好=容器值」 +# 同型高危写法返修;≥8px 硬余量 + 字形下探预算 ⇒ 12;取值不属色板,不进 ui_kit) +const STANDEE_FOOT_MARGIN := 12.0 var box := Vector2(150, 176) # 站桩盒尺寸(旧 Control custom_minimum_size;实际高随壳层 row 布局注入) var font: Font = null # 世界字体槽位(null = ThemeDB.fallback_font——world.world_font 注入) @@ -400,6 +403,17 @@ func _portrait_rect() -> Rect2: return Rect2(0, 44, box.x, box.y - 80.0) +# 石板地台矩形(_draw 与 content_rect 共用几何——单一来源防两处漂移) +func _pedestal_rect() -> Rect2: + return Rect2(18.0, box.y - 18.0, box.x - 36.0, 12.0) + + +# 绘制内容矩形(世界局部;ui_struct_probe ⑥standee「不出框+下余量≥8px」断言读数面—— +# 最低绘制元素 = 底座下缘;N2 纵向余量纪律 2026-10-06) +func content_rect() -> Rect2: + return Rect2(0.0, 0.0, box.x, _pedestal_rect().end.y) + + func _draw() -> void: var f := _efont() # 椭圆落影(圣经 v3.5 批 B4 2026-09-30——§6.1 地台落影压脚:方向稿椭圆 (0,0,0,70),压在地台 @@ -414,9 +428,10 @@ func _draw() -> void: draw_colored_polygon(pts, Color(0, 0, 0, 0.27)) # 石板地台(v3.5——方向稿取色:板身/描边/顶缘受光线 2px——取色单点收 UiKit.STAND_STONE 族 # (T4 裸色收口);几何沿用现行底座位〔margin 26/高 10→方向稿 18/12——按方向稿对齐〕) - draw_rect(Rect2(18.0, box.y - 18.0, box.x - 36.0, 12.0), UiKit.STAND_STONE, true) - draw_rect(Rect2(18.0, box.y - 18.0, box.x - 36.0, 12.0), UiKit.STAND_STONE_EDGE, false, 1.0) - draw_rect(Rect2(18.0, box.y - 18.0, box.x - 36.0, 2.0), UiKit.STAND_STONE_RIM, true) + var ped := _pedestal_rect() + draw_rect(ped, UiKit.STAND_STONE, true) + draw_rect(ped, UiKit.STAND_STONE_EDGE, false, 1.0) + draw_rect(Rect2(ped.position.x, ped.position.y, ped.size.x, 2.0), UiKit.STAND_STONE_RIM, true) # 血条(直角递减):敌附顺位徽标(GOLD_DEEP ①②③)——旧 top VBox 几何等价(左 8 / 右 -8) var bar_x := 8.0 @@ -480,7 +495,7 @@ func _draw() -> void: var name_w := f.get_string_size(_display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, 12).x var armor_w := f.get_string_size(armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, 12).x var total_w := armor_w + 3.0 + name_w - var baseline := Vector2((box.x - total_w) * 0.5, box.y - 1.0) + var baseline := Vector2((box.x - total_w) * 0.5, box.y - 1.0 - STANDEE_FOOT_MARGIN) # N2 余量纪律(2026-10-06——旧值 box.y-1.0 离盒底 1px) f.draw_string(get_canvas_item(), baseline, armor_txt, HORIZONTAL_ALIGNMENT_LEFT, -1, 12, col) f.draw_string(get_canvas_item(), baseline + Vector2(armor_w + 3.0, 0), _display_name, HORIZONTAL_ALIGNMENT_LEFT, -1, 12, UiKit.WARM) # v3.5 暗底双色墨制(原 INK_TITLE 深墨对暗底不可读) diff --git a/game/smoke/ui_struct_probe.gd b/game/smoke/ui_struct_probe.gd index 444bb149..301312e2 100644 --- a/game/smoke/ui_struct_probe.gd +++ b/game/smoke/ui_struct_probe.gd @@ -70,6 +70,7 @@ func _ready() -> void: main.level_screen.route_chosen.emit(battle_id) _settle(6) _audit("battle_hud", [main.board_zone, main.info_panel, main.stage]) + _standeefit_contract(main) else: problems.append("[battle_hud] 候选中无战斗节点(驱动中断——候选=%s)" % str(main.level_screen._cand_ids)) @@ -121,6 +122,30 @@ func _pick_battle_node(main: Node) -> int: return -1 +# ⑥standee 专项:B 区站桩内容矩形 ⊆ 252px 舞台矩形(内缩 8px)且不被裁切 +# (UI 设计对齐批 N2 2026-10-06——名行基线离盒底 1px 同型高危写法返修回归闸; +# 内容矩形=StandeeBody.content_rect(),最低绘制元素=底座下缘) +func _standeefit_contract(main: Node) -> void: + var st: Node = main.stage + if st == null or st.hero_standee == null: + problems.append("[standee] 舞台/主角站桩缺失(断言中断)") + return + var stage_gr: Rect2 = st.get_global_rect() + var bodies: Array = [st.hero_standee] + for c in st._cards: + bodies.append(c) + for body in bodies: + if body == null or not is_instance_valid(body): + continue + var content := Rect2(st.world_to_shell(body.position), body.content_rect().size) + if not stage_gr.encloses(content): + problems.append("[standee] %s 内容矩形 %s 越出舞台矩形 %s(N2 站桩出框回归)" + % [body.name if body.name != "" else "standee", content, stage_gr]) + elif content.end.y > stage_gr.end.y - 8.0: + problems.append("[standee] %s 内容底缘 %s 距舞台底 <8px(N2 纵向余量回归,舞台底=%s)" + % [body.name if body.name != "" else "standee", content.end.y, stage_gr.end.y]) + + func _settle(frames := 4) -> void: for i in range(frames): await get_tree().process_frame