♻ 复用件审阅+量化改色(tools/quantize_ui_skin.py,全 CC0,闸①色板 token 渐变带重映射/闸②token 天然合规/闸③图标四形互异/闸④授权干净):panel_stone/button_stone 两态/backdrop_stone/border_stone/营地图标四件(钥石金+柜台暖白)九件入 game/assets/art/ui/(PNG 不入 git,缺位 load_tex null 回退)。 接线=UiKit 单点:panel_skinned(B 区舞台 tint PANEL_STAGE/C 区主面板/图例面板+墨字随行改色 §7.2)+_apply_button_skin(全站按钮两态皮+墨字,UiKit.button 消费点零改)+frame_skinned(营地仪式石框容器)+icon_rect(营地钥石行/三柜台头图标,缺位纯文本回退)+开始界面石纹底噪底层(ui_assembler,z 序 dim 之上内容之下)。数据框/暗卡/结算回看保持素面或暗底(§7.1 数据素面纪律)。 ✦ B 区背景横幅不生图列缺料清单(归 T5 交付)。8 件=面板九宫格/按钮常态/按钮按压/石纹底噪/仪式石框/钥石图标/柜台图标组/B 区横幅(缺料)。轻闸=冒烟 557/0+四探针 PASS(带素材模式)+编辑器导入 9 件零报错。
188 lines
9.2 KiB
GDScript
188 lines
9.2 KiB
GDScript
# 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)
|
||
|
||
# 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
|