# VERTICAL SLICE - NOT FOR PRODUCTION # 实现 GDD:design/ux/battle.md(四区装配器——组合根的 UI 构建拆分载体)。 # Date: 2026-09-12(code-review 修复批:main.gd 回涨 761 行触规范 1.1 审视线——构建拆出) # 高内聚边界(规范 1.1/1.3):视图构建装配器——只做 new / add_child / 布局, # 零信号连接、零核心写调用;返回句柄供组合根接线(ADR-0001:接线拓扑唯一归组合根)。 # 静态无状态。z 序契约:调用方必须按 build 返回序 addChild(登记序 = 模态遮挡序——缺陷⑤纪律)。 extends RefCounted # 战斗屏三区(B 战斗画面 / C·D 两列——A 区关卡进度条顶栏随 停点前移批 2026-09-23 退役:节点链 # 进度由关卡画面本体(选路覆盖层整图)承载,28px 纵向空间拨给 C/D 底行 520→548;E 区道具面板 # 已随可选类删除下线,2026-09-15 格面资源批)。 # + 结算演出覆盖层(A–D 之上、模态之下——跨区弹道/光带载体,结算演出链批 2026-09-20)。 # 返回句柄字典:{stage, log_view, info_panel, board_zone, board_view, ap_bar, settlement_fx} # (gp_label/keystone_label 随 2026-09-26 信息归属拍板退役——GP/钥石迁出战斗屏,句柄不再透出) static func build_battle_ui(root: Control, db) -> Dictionary: # set_anchors_and_offsets_preset(非 set_anchors_preset):后者会把控件重置回最小内容尺寸 # (实测 927×613——缩顶、棋盘 45px 格 ×10 行超出 D 区 333px 高被裁,2026-09-13 用户报告回归) root.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT) root.add_theme_constant_override("separation", 0) # B 区:战斗画面(全宽 × 252px——常驻双舞台,布局决策 D1) var stage := BattleStageView.new(db) root.add_child(stage) # C/D 两列(548px——停点前移批 2026-09-23:原 A 区 28px 顶栏纵向空间并入) var bottom := HBoxContainer.new() bottom.size_flags_vertical = Control.SIZE_EXPAND_FILL bottom.add_theme_constant_override("separation", 4) root.add_child(bottom) var info_panel := HeroInfoPanelView.new() bottom.add_child(info_panel) var board_zone := BoardZoneView.new() bottom.add_child(board_zone) board_zone.ap_bar.set_refresh_cost(int(db.action_economy.rap_refresh_cost)) # RAP 读数表值驱动 # 结算演出覆盖层(A–D 之上——z 序 = addChild 序,模态覆盖层组在 build_overlays 中更晚挂入) var settlement_fx := SettlementFxView.new() root.add_child(settlement_fx) # CJK 字体随包(2026-09-27 用户拍板 Fusion Pixel OFL):Control 侧默认主题字体走 project.godot # gui/theme/custom_font;SubViewport 两世界不继承 Control 主题——显式注入挂点接线(C6 挂点链)。 # 缺位守卫(load_font 返回 null = 引擎回退字体,与旧 Button 渲染同源,不炸)。 var ui_font: Font = UiKit.load_font(UiKit.FONT_UI) if ui_font != null: stage.set_world_font(ui_font) board_zone.board_view.set_world_font(ui_font) # GP/钥石余额位已随 2026-09-26 信息归属拍板退役(中/大循环资源迁出战斗屏—— # GP 去向 = 关卡画面+战斗结算画面、钥石去向 = 结算画面+营地,呈现归对应 UX spec 批次) return { "stage": stage, "log_view": stage.log_view, "info_panel": info_panel, "board_zone": board_zone, "board_view": board_zone.board_view, "ap_bar": board_zone.ap_bar, "settlement_fx": settlement_fx, } # 模态覆盖层组(z 序 = 登记序:图例 < 节点 < 终局 < 商店)。 # 返回 {legend_overlay, node_overlay, end_overlay, shop_overlay, camp_overlay};调用方按本序 addChild。 # (调整板已随迭代 14.5 调试整合批退役——调试浮窗由组合根直构挂树序最末,见 main._build_ui; # 明细回看 overlay 已随 M9/P-C7 退役 2026-10-04——冒险中回看归「生涯」06 登记) static func build_overlays(host: Control, db) -> Dictionary: var legend_overlay := LegendOverlay.new(int(db.action_economy.rap_refresh_cost)) # 打磨包 2026-09-29——RAP 行表值注入 host.add_child(legend_overlay) var node_overlay := NodeOverlay.new() host.add_child(node_overlay) var end_overlay := BattleEndOverlay.new() host.add_child(end_overlay) # 商店覆盖层(GP+商店房实施批 2026-09-21——商店房节点兑付呈现;按钮接线归组合根,装配器零信号连接) var shop_overlay := ShopOverlay.new() host.add_child(shop_overlay) # 营地覆盖层(元进度实现批 2026-09-21——主菜单营地入口兑付呈现;开合与开始层互斥仿设置面先例) var camp_overlay := CampOverlay.new() host.add_child(camp_overlay) return { "legend_overlay": legend_overlay, "node_overlay": node_overlay, "end_overlay": end_overlay, "shop_overlay": shop_overlay, "camp_overlay": camp_overlay, } # 开始覆盖层(返回 {overlay, box, lvbtn, resumebtn, campbtn, settingsbtn, quitbtn, dbgbtn}——按钮接线归组合根; # 迭代 14.5 调试整合批:随机种子/数值调整板/两层调试三散件撤出主按钮组,收进调试浮窗—— # 本层只留右下角「调试」角标入口(DS_DEBUG=1 门控归组合根),dbgbtn 即该角标。 # UI 落码批 M2(2026-10-04):单列 VBox→双列(左操作 56% + 右舞台/帮助牌);按钮动词化+悬停解释 #(解释全迁 tooltip,G5);lvbtn 升主操作钮(烛光黄 CANDLE);右列=立绘看台+玩法速览牌(帮助全文保留, # 表值驱动不变);root 挂 UiKit.tooltip_theme() 全树生效。返回键集不变,组合根接线零改) static func build_start_overlay(host: Control, db = null) -> Dictionary: var overlay := overlay_bg(host, UiKit.BG_BRIGHT) overlay.theme = UiKit.tooltip_theme() # 石纹底噪底层(样张 v2 冻结批 T2——z 序=dim 之上/内容之下;缺位不挂即回退平色) var bd_tex := UiKit.load_tex(UiKit.TEX_BACKDROP_STONE) if bd_tex != null: var bd := TextureRect.new() bd.texture = bd_tex bd.stretch_mode = TextureRect.STRETCH_TILE bd.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST bd.set_anchors_preset(Control.PRESET_FULL_RECT) bd.mouse_filter = Control.MOUSE_FILTER_IGNORE overlay.add_child(bd) # 双列骨架(M2):左=操作列 56% / 右=舞台+速览 var cols := HBoxContainer.new() cols.set_anchors_preset(Control.PRESET_FULL_RECT) cols.add_theme_constant_override("separation", 32) var pad := MarginContainer.new() pad.set_anchors_preset(Control.PRESET_FULL_RECT) for side in ["margin_left", "margin_right", "margin_top", "margin_bottom"]: pad.add_theme_constant_override(side, 56) cols.add_child(pad) overlay.add_child(cols) var box := VBoxContainer.new() box.name = "start_box" box.size_flags_horizontal = Control.SIZE_EXPAND_FILL box.size_flags_stretch_ratio = 1.4 box.add_theme_constant_override("separation", 10) pad.add_child(box) var title := UiKit.label("Dungeon Sweeper",UiKit.FS_H1, true, UiKit.INK_ON_DEEP) box.add_child(title) var sub := UiKit.label("扫雷的因 · 逐行兑现的果",UiKit.FS_BODY, false, UiKit.WARM_SOFT) box.add_child(sub) box.add_child(HSeparator.new()) # 组帽·冒险(M2 分组) box.add_child(UiKit.label("── 冒 险 ──",UiKit.FS_CAPTION, false, UiKit.WARM_FAINT)) # 唯一试玩入口 = 正式「开始冒险」(三关序列);单关测试直入口(L2/L3)退役(2026-09-23 用户拍板) var resumebtn := UiKit.button("▶ 继续上局") # 迭代 8 卡②——有断点才亮(可见性与关数文案归组合根刷新) resumebtn.tooltip_text = "自最近停点恢复" resumebtn.visible = false box.add_child(resumebtn) var lvbtn := UiKit.primary_button("▶ 开始冒险", "三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城") box.add_child(lvbtn) # 组帽·营地与系统 box.add_child(UiKit.label("── 营地与系统 ──",UiKit.FS_CAPTION, false, UiKit.WARM_FAINT)) var campbtn := UiKit.verb_button("营地", UiKit.TEX_ICON_KEYSTONE, "永久强化:钥石换永久属性——跨局成长账本") # 元进度实现批——主菜单营地入口 box.add_child(campbtn) var settingsbtn := UiKit.button("设置") # 迭代 8 卡④——最小设置面入口 settingsbtn.tooltip_text = "音量 / 全屏 / 减动效" box.add_child(settingsbtn) # 二轮修改批 D1(2026-09-29 用户目检裁决):退出游戏与设置同层并行——只在一级菜单出现 var quitbtn := UiKit.button("退出游戏") box.add_child(quitbtn) # 迭代 14.5 调试整合批——调试入口收右下角标(与主按钮组非平级;DS_DEBUG=1 点亮,可见性归组合根)。 # margin 16px 角距;字号降档与主按钮拉开层级 var dbgbtn := UiKit.button("调试", 0.0) # 角标豁免宽度基准(0=不设 min/flags——角落小件) dbgbtn.add_theme_font_size_override("font_size", UiKit.FS_HINT) dbgbtn.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_RIGHT, Control.PRESET_MODE_MINSIZE, 16) overlay.add_child(dbgbtn) # 右列:立绘看台 + 玩法速览牌(M2——帮助全文保留表值驱动,仅容器化;内容含地形变异段不删) var right := VBoxContainer.new() right.name = "start_right" right.size_flags_horizontal = Control.SIZE_EXPAND_FILL right.add_theme_constant_override("separation", 18) right.alignment = BoxContainer.ALIGNMENT_CENTER cols.add_child(right) # ui-struct-probe 批 T2:右列挂 HBox 第二列(原误挂 pad 与左列同矩形叠放——M2 双列失效根因) var stage := VBoxContainer.new() stage.alignment = BoxContainer.ALIGNMENT_CENTER var foe_row := HBoxContainer.new() foe_row.alignment = BoxContainer.ALIGNMENT_CENTER foe_row.add_theme_constant_override("separation", 36) for cfg in [[UiKit.TEX_ENEMY_GOBLIN, 46, 96], [UiKit.TEX_HERO_PORTRAIT, 138, 144], [UiKit.TEX_ENEMY_OGRE_CHIEF, 80, 96]]: var tr := TextureRect.new() var tex := UiKit.load_tex(cfg[0]) if tex != null: tr.texture = tex tr.custom_minimum_size = Vector2(cfg[1], cfg[2]) tr.expand_mode = TextureRect.EXPAND_IGNORE_SIZE tr.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED tr.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST if cfg[0] != UiKit.TEX_HERO_PORTRAIT: tr.modulate = Color(0.7, 0.7, 0.7, 0.4) # 敌影剪影退后(§6.1 立绘>背景) foe_row.add_child(tr) stage.add_child(foe_row) var pedestal := Panel.new() var ped_sb := UiKit.panel_flat(UiKit.TITLE_PEDESTAL, UiKit.BG_ROOT, 17, 2) pedestal.add_theme_stylebox_override("panel", ped_sb) pedestal.custom_minimum_size = Vector2(240, 22) pedestal.size_flags_horizontal = Control.SIZE_SHRINK_CENTER stage.add_child(pedestal) right.add_child(stage) # 玩法帮助文案(表值驱动——2026-09-16 AP 压速批遗留硬编码 8 随手修正;护甲等级掷点批同步"减伤"→"护甲强化"; # M2 容器化=石板牌承载〔文字禁直接压纹理/深底——§7.2〕,内容逐字保留含地形变异段) var help_text := "玩法:每轮 %d AP——挖开(1AP)/插旗(1AP)/移动(免费)/连锁(免费)。\n每个非封锁格都藏一种格面资源(增伤/护甲强化/回血/爆裂/削弱)。开安全格、标中雷 = 正向兑付;\n开雷格、标错安全格 = 负向兑付——全部在轮末结算逐行兑现;吃掷点的基础攻击按护甲等级判定,无 miss、最低保底 1 点。\n停手:剩余 AP 转存 RAP;弃图刷新:RAP−%d 当场换图继续挖(每轮剩 %d 次)。\n敌人全灭胜、主角倒下负。悬停可看三态光标与可挖域微光。\n地形变异:第二关起 Boss 图带整图变异(贫矿脉——回复与护甲资源减少、攻击资源增多;开战横幅与 HUD 角标常驻提示)。" % [ int(db.action_economy.ap_per_round), int(db.action_economy.rap_refresh_cost), int(db.action_economy.refresh_cap_per_round)] var help_plate := PanelContainer.new() help_plate.add_theme_stylebox_override("panel", UiKit.panel_skinned(Vector4(14, 10, 14, 10))) help_plate.size_flags_horizontal = Control.SIZE_SHRINK_CENTER var help := UiKit.label(help_text,UiKit.FS_BODY, false, UiKit.INK) help_plate.add_child(help) right.add_child(help_plate) return {"overlay": overlay, "box": box, "lvbtn": lvbtn, "resumebtn": resumebtn, "campbtn": campbtn, "settingsbtn": settingsbtn, "quitbtn": quitbtn, "dbgbtn": dbgbtn} # 调试两层模式的层间过渡覆盖层(历史入口专用)。返回 {overlay, label, next_btn}——接线归组合根 static func build_inter_overlay(host: Control) -> Dictionary: var overlay := overlay_bg(host, Color(0, 0, 0, 0.6)) var box := overlay_box(overlay) var label := UiKit.label("",UiKit.FS_TITLE, true, UiKit.WARM) box.add_child(label) var next_btn := UiKit.button("进入下一层") box.add_child(next_btn) overlay.visible = false return {"overlay": overlay, "label": label, "next_btn": next_btn} # Playtest 诊断按钮(调试变体——battle.md §States 允许;卡死转储) static func build_diag_button(host: Control) -> Button: var diag_btn := UiKit.button("诊断转储", 0.0) # 角落小件豁免宽度基准(固定 offsets 112px 宽) diag_btn.set_anchors_preset(Control.PRESET_BOTTOM_LEFT) diag_btn.offset_left = 6.0 diag_btn.offset_top = -30.0 diag_btn.offset_right = 118.0 diag_btn.offset_bottom = -6.0 host.add_child(diag_btn) return diag_btn static func overlay_bg(host: Control, color: Color) -> ColorRect: var dim := ColorRect.new() dim.color = color dim.set_anchors_preset(Control.PRESET_FULL_RECT) host.add_child(dim) return dim static func overlay_box(overlay: Control) -> VBoxContainer: var center := CenterContainer.new() center.set_anchors_preset(Control.PRESET_FULL_RECT) overlay.add_child(center) var box := VBoxContainer.new() box.add_theme_constant_override("separation", 12) center.add_child(box) return box