Files
gplan-2/game/presentation/ui_assembler.gd
T
DKChan 9abf5ff448 merge: M-1 迭代 2「样张冻结与表现定稿」loop——样张 v2 机检 16/16 四冻结+框材组 8 件接线+演出密度终稿+棋盘数字终稿+M-1 出口闸检查单
五批次 a08d89c/f9b0765→2d1388b:①T1 样张 v2(机检闸形 A 全过即冻结——新增 A3 Δ≥55/F6 双明度带/F7 DEPTH_PX 同源/A6 观感密度四断言,art-bible v2.13 落四冻结+UiKit 小人蓝同步 #2E86DE+图例旗色封印黑漏网修)②T2 框材组 8 件(♻ Kenney/OGA 全 CC0 量化改色入包,UiKit 单点工厂+五视图接线,✦ 背景横幅列缺料)③T3 演出四项(死亡碎裂/胜利纹章/失败掸灰/意图三档+结算光带缘线)+fx_probe 新探针④T4 数字形制冻结值落码(三档墨色温/字号 ≥18 公式/色牌 0.60·0.62)⑤T5 M-1 出口闸检查单(design/qa/m1-exit-gate-checklist-2026-09-28.md——真人目检八项+✦ 缺料清单)+06 两已解决条目移除+roadmap 挂起清单对齐。
验证:样张机检 16/16+冒烟 557/0(基线持平)+五探针(ui_input/ui_probe/ui_click/ui_tune/fx 新增)全 PASS+doc 三闸对基线零新增(lint 8=8/term 215=215/死链 0)+熵增闸 PASS。
2026-09-28 04:58:58 +08:00

196 lines
9.7 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
# 实现 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, review_overlay, node_overlay, end_overlay, shop_overlay, tune_panel};调用方按本序 addChild。
static func build_overlays(host: Control, db, base_atk: int, base_hp: int) -> Dictionary:
var legend_overlay := LegendOverlay.new()
host.add_child(legend_overlay)
var review_overlay := SettlementReviewOverlay.new()
host.add_child(review_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——主菜单营地入口兑付呈现;开合仿 TunePanel 与开始层互斥)
var camp_overlay := CampOverlay.new()
host.add_child(camp_overlay)
var tune_panel := TunePanel.new(db, base_atk, base_hp)
tune_panel.visible = false # 显式初始化——原版依赖"被不透明开始层遮蔽"的树序巧合(缺陷⑤家族);模态锁输入以 visible 为准
host.add_child(tune_panel)
return {
"legend_overlay": legend_overlay,
"review_overlay": review_overlay,
"node_overlay": node_overlay,
"end_overlay": end_overlay,
"shop_overlay": shop_overlay,
"camp_overlay": camp_overlay,
"tune_panel": tune_panel,
}
# 开始覆盖层(返回 {overlay, box, seed_edit, lvbtn, tunebtn, dbgbtn}——按钮接线归组合根;
# 单场遭遇用户入口已删(2026-09-13 用户裁决——单场原语仅探针经 debug_session.start_single 使用))
static func build_start_overlay(host: Control, db = null) -> Dictionary:
var overlay := overlay_bg(host, UiKit.BG_OVERLAY)
# 石纹底噪底层(样张 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)
var box := overlay_box(overlay)
var title := UiKit.label("Dungeon Sweeper", 34, true)
title.add_theme_color_override("font_color", UiKit.WARM)
box.add_child(title)
var sub := UiKit.label("战斗屏战斗屏改造批 —— 扫雷的因,逐行兑现的果", 16, false)
sub.add_theme_color_override("font_color", UiKit.WARM_SOFT)
box.add_child(sub)
box.add_child(HSeparator.new())
var seed_row := HBoxContainer.new()
box.add_child(seed_row)
seed_row.add_child(UiKit.label("随机种子:", 15, false))
var seed_edit := LineEdit.new()
seed_edit.text = "42"
seed_edit.custom_minimum_size = Vector2(120, 0)
seed_row.add_child(seed_edit)
# 唯一试玩入口 = 正式「开始冒险」(三关序列);单关测试直入口(L2/L3)退役(2026-09-23 用户拍板)
var lvbtn := UiKit.button("▶ 开始冒险(三关连探:地精洞窟 → 幽暗地域 → 灰矮人矿城)")
box.add_child(lvbtn)
var tunebtn := UiKit.button("测试用 · 角色数值调整板(模拟天赋树属性提升)")
box.add_child(tunebtn)
var dbgbtn := UiKit.button("调试 · 连续关卡两层(HP 关内延续)")
box.add_child(dbgbtn)
var campbtn := UiKit.button("营地 · 永久强化(钥石换永久属性——跨局成长账本)") # 元进度实现批——主菜单营地入口(双入口框架的主菜单一侧)
box.add_child(campbtn)
# 玩法帮助文案(表值驱动——2026-09-16 AP 压速批遗留硬编码 8 随手修正;护甲等级掷点批同步"减伤"→"护甲强化")
var help_text := "玩法:每轮 %d AP——挖开(1AP)/插旗(1AP)/移动(免费)/连锁(免费)。\n每个非封锁格都藏一种格面资源(增伤/护甲强化/回血/爆裂/削弱)。开安全格、标中雷 = 正向兑付;\n开雷格、标错安全格 = 负向兑付——全部在轮末结算逐行兑现;吃掷点的基础攻击按护甲等级判定,无 miss、最低保底 1 点。\n停手:剩余 AP 转存 RAP;弃图刷新:RAP−%d 当场换图继续挖(每轮剩 %d 次)。\n敌人全灭胜、主角倒下负。悬停可看三态光标与可挖域微光。" % [
int(db.action_economy.ap_per_round), int(db.action_economy.rap_refresh_cost), int(db.action_economy.refresh_cap_per_round)]
var help := UiKit.label(help_text, 13, false)
help.add_theme_color_override("font_color", UiKit.WARM_FAINT)
box.add_child(help)
return {"overlay": overlay, "box": box, "seed_edit": seed_edit,
"lvbtn": lvbtn, "tunebtn": tunebtn, "dbgbtn": dbgbtn,
"campbtn": campbtn}
# 调试两层模式的层间过渡覆盖层(历史入口专用)。返回 {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("", 24, true)
label.add_theme_color_override("font_color", 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("诊断转储")
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