# VERTICAL SLICE - NOT FOR PRODUCTION # Date: 2026-09-21(元进度实现批——营地覆盖层呈现;2026-10-12 迭代 21 T6 营地统一重排—— # 四柜台并排〔2026-10-11 锻造工坊裁决会话 A② 裁〕:树淬炼柜台=树脊柱界面形态=主界面 / # 锻造工坊柜台〔钥匙门:forge_workshop 点亮前锁定态,点亮=工作间占位面板〕/ 秘宝匣柜台 # 〔钥匙门:ks_workshop 点亮前锁定态,点亮=现工坊内容整体迁入——六行+同调装配段+Y 升级行〕/ # 奖励格图鉴柜台常开独立位;宽度不足横向滚动条。旧属性锻造柜台退役〔攻防所有权切分—— # 生命唯一入口=树脊柱、攻/AC 唯一入口=锻造工坊路线〕)。 # 占位面板硬要求(2026-10-11 用户令):锻造工坊工作间占位面板打开/关闭零副作用——零写档、 # 零报错、面板内无任何可交互购买路径(纯文案标签,视觉终稿归 HUD 营地视觉批)。 # 信号面/投影契约:buy_requested(attr)〔现仅 Y 刷新升级行消费——装配段双入口之一 Q1 裁乙〕/closed、 # attune_toggled/tree_buy_requested/tree_fork_toggled、open_camp(data)/refresh_camp(data)。 # 挂接方式:开合与开始层互斥仿设置面先例(开始覆盖层非模态且树序在后——打开本层前由组合根 # 隐藏开始层,规范 2.3 遮挡纪律)。 # 高内聚边界:纯视图 setter 面——账本快照经组合根喂入(MetaProgression 只读投影),购买/关闭 # 意图经信号外发——零核心写调用、零 MetaProgression 持有。 class_name CampOverlay extends Control signal buy_requested(attr: String) signal closed signal attune_toggled(item_id: String) # 同调装配切换意图(迭代 11 第①批 T4——写口唯一归 MetaProgression.toggle_attunement) signal tree_buy_requested(node_id: String) # 天赋树购买意图(迭代 12 T9——写口唯一归 MetaProgression.tree_buy) signal tree_fork_toggled(group: String, side: String) # 双态切换意图(一节点双态——零币切态) # 方向稿取色(柜台头带深棕/行板暖米;双色墨=亮板配深墨)——值单点收 UiKit(T4 裸色收口批) const HEADER_BAND_COL := UiKit.CAMP_HEADER_BAND const ROW_PLATE_COL := UiKit.CAMP_ROW_PLATE var _balance_label: Label var _tree_holder: VBoxContainer # 树淬炼柜台内容(主界面——脊柱两行+钥匙/socket 行板+Fork 双态钮) var _tree_buy_btns: Array[Dictionary] = [] # [{node_id, btn}]——树购买/解锁钮登记面(open/refresh 重建时刷新;探针消费) var _tree_fork_btns: Array[Dictionary] = [] # [{group, side, btn}]——Fork 双态切换钮登记面(探针消费) var _forge_lock_l: Label # 锻造工坊柜台锁定态行(钥匙未点亮) var _forge_workbench: VBoxContainer # 锻造工坊工作间占位面板(点亮后展开——零交互纯文案) var _ws_scroll: ScrollContainer # 秘宝匣柜台内容容器(钥匙点亮后可见) var _ws_lock_l: Label # 秘宝匣柜台锁定态行 var _workshop_holder: VBoxContainer # 秘宝匣柜台行板(六行+同调装配段+Y 升级行——迭代 11 第①批 T4 结构随迁) var _workshop_rows: Array[Dictionary] = [] # [{item_id, progress_l, status_l}] var _attune_header_l: Label # 同调装配段(Slots 点亮呈现) var _attune_btns: Array[Dictionary] = [] # [{item_id, btn}]——装配/卸下切换钮(满位禁选随刷) var _attune_nodes: Array[Control] = [] # 段内节点册(open_camp 重建时定向清理,不扰工坊六行) var _refresh_l: Label # Y 刷新升级行(双入口之一——树上 ks_refresh 同账 Q1 裁乙) var _refresh_btn: Button var _recent_list: Label # 最近冒险(逐行拼接 label——只读账面,随 open_camp 重建) func _init() -> void: # 底=不透明暖亮档(2026-10-06 用户实测缺陷修——原形制=0.45 半透 dim+满铺石纹遮底,T5 石纹降 0.14 # 后底下屏幕透出〔用户实机:对局中开营地见棋盘/战斗提示条〕;demo #v-camp=bg-bright 实底, # 遮蔽语义由组合根输入锁兜底) var dim := UiKit.modal_dim(self, UiKit.BG_BRIGHT) # 石纹弱质感+暗角(UI 对齐批 T5 2026-10-06——demo .tilebg 0.14/.vignette;缺位不挂即回退平色) var bd := UiKit.backdrop_tile() if bd != null: dim.add_child(bd) dim.add_child(UiKit.vignette_rect()) var root := MarginContainer.new() root.set_anchors_preset(Control.PRESET_FULL_RECT) root.add_theme_constant_override("margin_left", 16) root.add_theme_constant_override("margin_right", 16) root.add_theme_constant_override("margin_top", 12) root.add_theme_constant_override("margin_bottom", 12) dim.add_child(root) var col := VBoxContainer.new() col.add_theme_constant_override("separation", 8) root.add_child(col) # ── 顶栏:左铭牌(标题+副题)/ 右钥石徽 ── var top := HBoxContainer.new() top.add_theme_constant_override("separation", 10) col.add_child(top) var plaque := PanelContainer.new() plaque.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(14, 8, 14, 8))) top.add_child(plaque) var plaque_col := VBoxContainer.new() plaque.add_child(plaque_col) var title := UiKit.label("营地 · 天赋树",UiKit.FS_TITLE, true, UiKit.INK_TITLE) plaque_col.add_child(title) var sub := UiKit.label("营地本身即树——钥石逐节点点亮,属性/规则/工作间都在树上",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # 尾裁微批a 2026-10-08——STONE 对铭牌板 3.59:1 越带,换灰石深化 5.98:1 plaque_col.add_child(sub) var spacer := Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL top.add_child(spacer) # M3(UI 落码批):钥石徽章烛光黄底+深墨字(demo ②)。Q10 甲(2026-10-06):UiKit.keystone_badge 工厂单点 var badge := UiKit.keystone_badge("钥石 0") top.add_child(badge) _balance_label = badge.value_label # ── 四柜台并排(A② 裁——迭代 21 T6 程序占位重排,视觉终稿归 HUD 营地视觉批)── # 宽度不足横向滚动(四柜 300 min 宽 > 窄窗可用宽时横滚兜底;纵向撑满余高) var camp_scroll := ScrollContainer.new() camp_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL camp_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_AUTO camp_scroll.vertical_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED col.add_child(camp_scroll) var mid := HBoxContainer.new() mid.add_theme_constant_override("separation", 14) mid.size_flags_vertical = Control.SIZE_EXPAND_FILL camp_scroll.add_child(mid) # 柜一·树淬炼(树脊柱界面形态=主界面:一线一节点行板内升级+Fork 双态切换钮) _tree_holder = VBoxContainer.new() _tree_holder.add_theme_constant_override("separation", 6) var tree_scroll := ScrollContainer.new() tree_scroll.custom_minimum_size = Vector2(300, 336) tree_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL tree_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED tree_scroll.add_child(_tree_holder) var tree_panel := _counter_panel("天赋树 · 淬炼", "", tree_scroll, 300) tree_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL mid.add_child(tree_panel) # 柜二·锻造工坊(钥匙门——forge_workshop 点亮前锁定态;点亮=工作间占位面板,零交互) _forge_lock_l = UiKit.label("🔒 锻造工坊未开放\n\n点亮树上「锻造工坊」钥匙节点后展开工作间。\n(钥匙免费、无前置——树上直接点亮)",UiKit.FS_HINT, false, UiKit.STONE_DEEP) _forge_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _forge_lock_l.custom_minimum_size = Vector2(280, 0) _forge_workbench = VBoxContainer.new() _forge_workbench.add_theme_constant_override("separation", 6) var wb_hint := UiKit.label("工作间两区并列:武器锻造区 / 护甲锻造区",UiKit.FS_HINT, true, UiKit.INK) wb_hint.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART wb_hint.custom_minimum_size = Vector2(280, 0) _forge_workbench.add_child(wb_hint) var wb_body := UiKit.label("武器/护甲路线升级随「锻造工坊实装批」开放——本批为占位面板,无任何购买入口。",UiKit.FS_HINT, false, UiKit.STONE_DEEP) wb_body.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART wb_body.custom_minimum_size = Vector2(280, 0) _forge_workbench.add_child(wb_body) var forge_holder := VBoxContainer.new() forge_holder.add_theme_constant_override("separation", 6) forge_holder.add_child(_forge_lock_l) forge_holder.add_child(_forge_workbench) var forge_panel := _counter_panel("锻造工坊", UiKit.TEX_ICON_FORGE, forge_holder, 300) forge_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL mid.add_child(forge_panel) # 柜三·秘宝匣(钥匙门——ks_workshop 点亮前锁定态;点亮=现工坊内容整体迁入:六行+同调装配段+Y 升级行) _ws_lock_l = UiKit.label("🔒 秘宝匣未开放\n\n点亮树上「秘宝匣」钥匙节点后展开。\n(前置=脊柱生命第 1 级)",UiKit.FS_HINT, false, UiKit.STONE_DEEP) _ws_lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _ws_lock_l.custom_minimum_size = Vector2(280, 0) _ws_scroll = ScrollContainer.new() _ws_scroll.custom_minimum_size = Vector2(300, 336) _ws_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL _ws_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED _workshop_holder = VBoxContainer.new() _workshop_holder.add_theme_constant_override("separation", 6) _ws_scroll.add_child(_workshop_holder) var ws_holder := VBoxContainer.new() ws_holder.add_theme_constant_override("separation", 6) ws_holder.add_child(_ws_lock_l) ws_holder.add_child(_ws_scroll) var ws_panel := _counter_panel("秘宝匣", UiKit.TEX_ICON_WORKSHOP, ws_holder, 300) ws_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL mid.add_child(ws_panel) # 柜四·奖励格图鉴(常开独立位——结构位预留,内容排 Alpha) var atlas_holder := VBoxContainer.new() var atlas_panel := _counter_panel("奖励格图鉴", UiKit.TEX_ICON_ATLAS, atlas_holder, 300) atlas_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL mid.add_child(atlas_panel) var lock_l := UiKit.label(" —— 柜台结构位预留:内容排 Alpha(地图战斗资源差异化批)",UiKit.FS_HINT, false, UiKit.STONE) lock_l.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART lock_l.custom_minimum_size = Vector2(280, 0) atlas_holder.add_child(lock_l) # ── 底栏:关闭钮(左)/ 操作注记(右)+ 最近冒险条带 ── var close_btn := UiKit.button("关闭营地(回开始界面)") close_btn.pressed.connect(_emit_closed) var bottom := HBoxContainer.new() bottom.add_theme_constant_override("separation", 10) bottom.add_child(close_btn) var spacer2 := Control.new() spacer2.size_flags_horizontal = Control.SIZE_EXPAND_FILL bottom.add_child(spacer2) var caption := UiKit.label("树上点亮 · 工作间随钥匙展开 · 图鉴常开",UiKit.FS_HINT, false, UiKit.WARM_SOFT) bottom.add_child(caption) col.add_child(bottom) _recent_list = UiKit.label("",UiKit.FS_HINT, false, UiKit.WARM_SOFT) _recent_list.clip_text = true _recent_list.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS col.add_child(_recent_list) visible = false # 柜台列面板(方向稿同构):PanelContainer 米板身 + 头带深棕(图标+标题暖白) func _counter_panel(title_text: String, icon_path: String, holder: Control, min_w: int) -> PanelContainer: var panel := PanelContainer.new() panel.custom_minimum_size = Vector2(min_w, 0) panel.add_theme_stylebox_override("panel", UiKit.panel_flat(UiKit.PANEL_DATA, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(10, 10, 10, 10))) var vbox := VBoxContainer.new() vbox.add_theme_constant_override("separation", 8) panel.add_child(vbox) var band := PanelContainer.new() band.add_theme_stylebox_override("panel", UiKit.panel_flat(HEADER_BAND_COL, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(10, 5, 10, 5))) vbox.add_child(band) var head := HBoxContainer.new() head.add_theme_constant_override("separation", 6) band.add_child(head) if icon_path != "": var ic := UiKit.icon_rect(icon_path, 32) # P6 甲②(2026-10-06):柜台头带 64 源 0.5×;缺位 null → 纯文本回退 if ic != null: head.add_child(ic) var l := UiKit.label(title_text,UiKit.FS_BODY, true, UiKit.WARM) head.add_child(l) vbox.add_child(holder) return panel # ---- 柜台行构建(open_camp 重建;行结构仿 ShopOverlay 商品卡——亮板双色墨制)---- # 天赋树段重建(迭代 21 T6——树淬炼柜台主界面:脉分组行板:节点名/等级/价/购买钮 + Fork 双态切换钮; # 树数据 = 组合根 _camp_data().tree 投影 [{node_id, display_name, vein, kind, level, price, can_buy, # currency}...] + fork_groups [{group, node_id, active_side, owned, can_buy, price, sides:[{side, display_name, effects}]}]) func _rebuild_tree(data: Dictionary) -> void: for c in _tree_holder.get_children(): c.queue_free() _tree_buy_btns.clear() _tree_fork_btns.clear() var tree: Array = data.get("tree", []) as Array var forks: Array = data.get("fork_groups", []) as Array var vein_names := {"spine": "·脊柱淬炼·", "forge": "·锻造脉·", "keystone": "·钥石脉·", "attune": "·同调脉·"} var last_vein := "" for row in tree: var vein := String(row.get("vein", "")) if vein != last_vein: last_vein = vein var vh := UiKit.label(String(vein_names.get(vein, vein)),UiKit.FS_HINT, true, UiKit.GOLD_INK) # 尾裁微批a 2026-10-08——GOLD_DEEP 对柜台板 2.67:1 越带,换深金墨 5.21:1 _tree_holder.add_child(vh) var hb := HBoxContainer.new() hb.add_theme_constant_override("separation", 6) var lvl := int(row.get("level", 0)) var cap := int(row.get("cap", 1)) var name_l := UiKit.label("%s Lv%d/%d" % [String(row.get("display_name", "")), lvl, cap], 13, false, UiKit.INK_LABEL) name_l.custom_minimum_size = Vector2(150, 0) name_l.clip_text = true hb.add_child(name_l) if String(row.get("kind", "")) == "fork": continue # Fork 行去行板化——由 fork_groups 段承载双态切换钮 var btn := UiKit.button("购买 %d 钥石" % int(row.get("price", 0)), 0.0) # 柜台行板紧凑钮豁免宽度基准(树全钥石单币 2026-10-07 裁甲) btn.pressed.connect(func(): tree_buy_requested.emit(String(row.get("node_id", "")))) btn.disabled = not bool(row.get("can_buy", false)) hb.add_child(btn) _tree_buy_btns.append({"node_id": String(row.get("node_id", "")), "btn": btn}) _tree_holder.add_child(hb) for fg in forks: var group := String(fg.get("group", "")) var active := String(fg.get("active_side", "")) var owned := bool(fg.get("owned", false)) var fb := HBoxContainer.new() fb.add_theme_constant_override("separation", 6) var state_txt := "未解锁" if owned: state_txt = "激活:%s" % active var gl := UiKit.label("%s[%s]" % [String(fg.get("display_name", group)), state_txt], 13, false, UiKit.INK_LABEL) fb.add_child(gl) if not owned: var bb := UiKit.button("解锁 %d 钥石" % int(fg.get("price", 0)), 0.0) # 行板紧凑钮豁免 bb.pressed.connect(func(): tree_buy_requested.emit(String(fg.get("node_id", "")))) bb.disabled = not bool(fg.get("can_buy", false)) fb.add_child(bb) _tree_buy_btns.append({"node_id": String(fg.get("node_id", "")), "btn": bb}) else: for side_row in (fg.get("sides", []) as Array): var sd := String(side_row.get("side", "")) var sb := UiKit.button(String(side_row.get("display_name", sd)), 0.0) # 行板紧凑钮豁免 sb.pressed.connect(func(): tree_fork_toggled.emit(group, sd)) fb.add_child(sb) _tree_fork_btns.append({"group": group, "side": sd, "btn": sb}) _tree_holder.add_child(fb) # 工作间钥匙门(迭代 21 T6——锻造工坊/秘宝匣柜台锁定态切换:节点点亮=展开、未点亮=锁定行; # 图鉴常开无门槛。判据读树投影行 level——零核心访问,纯视图) func _refresh_key_gates(data: Dictionary) -> void: var lvl_by_id := {} for row in (data.get("tree", []) as Array): lvl_by_id[String(row.get("node_id", ""))] = int(row.get("level", 0)) var forge_open := int(lvl_by_id.get("forge_workshop", 0)) >= 1 var ws_open := int(lvl_by_id.get("ks_workshop", 0)) >= 1 _forge_lock_l.visible = not forge_open _forge_workbench.visible = forge_open _ws_lock_l.visible = not ws_open _ws_scroll.visible = ws_open # 打开营地(全量重建四柜台行 + 同调装配段 + 最近冒险;树段与钥匙门随 refresh_camp 重建—— # 数据 = 组合根 _camp_data() 投影) func open_camp(data: Dictionary) -> void: _rebuild_workshop(data) # 段尾链 _rebuild_attune(同调装配段+Y 升级行随秘宝匣柜台内容) _rebuild_recent(data) refresh_camp(data) visible = true # 状态重刷(购买后调用——余额/树行板/钥匙门/工坊状态就地重建或改文案;树行板随刷重建=按钮 # 禁用态与门槛/cap 同步,12 行重建开销可忽略) func refresh_camp(data: Dictionary) -> void: var balance := int(data.get("balance", 0)) _balance_label.text = "钥石 %d" % balance _rebuild_tree(data) _refresh_key_gates(data) var ws_by_id := {} for row in data.get("workshop", []): ws_by_id[String(row.get("item_id", ""))] = row for card in _workshop_rows: var row: Dictionary = ws_by_id.get(String(card.item_id), {}) if row.is_empty(): continue var unlocked := bool(row.get("unlocked", false)) # 秘宝匣进度列(迭代 22 T2——解锁混合制三形态:钥石购买价/条件进度拼接/已入手; # 旧「累计通关 N/M 次」单一渠道文案随工坊行族退役) var prog_text := "" var mode := String(row.get("mode", "")) if unlocked: prog_text = "已入手" elif mode == "keystone": prog_text = "钥石 %d" % int(row.get("price", 0)) elif mode == "condition": var parts: Array[String] = [] for req_v in (row.get("reqs", []) as Array): var req: Dictionary = req_v as Dictionary var lbl := String(UiKit.TREASURE_REQ_LABELS.get(String(req.get("key", "")), "条件")) parts.append("%s %d/%d" % [lbl, int(req.get("progress", 0)), int(req.get("threshold", 0))]) prog_text = "、".join(parts) card.progress_l.text = prog_text # 状态色随卡⑭ 亮板双色墨制(深墨族替代暖白族——亮板上暖白不可读);失义/退役行随新目录退役 var st_text := "未解锁" var st_col: Color = UiKit.STONE if unlocked: st_text = "已解锁" st_col = UiKit.GOLD_INK # 尾裁微批a 2026-10-08——GOLD_DEEP 对行板 3.00:1 越带,换深金墨 5.84:1 card.status_l.text = st_text card.status_l.add_theme_color_override("font_color", st_col) card.status_l.tooltip_text = st_text # 截断兜底(状态列 EXPAND_FILL 吸余宽——悬停看全文) # 同调装配段状态就地刷(迭代 11 第①批 T4——Slots 点亮/满位禁选/Y 升级行余额联动) var att: Dictionary = data.get("attune", {}) if _attune_header_l != null: var slots_n := int(att.get("slots", 0)) var eq: Array = att.get("equipped", []) _attune_header_l.text = "── 同调装配 Slots %d/%d ──" % [eq.size(), slots_n] var eq_set := {} for v in eq: eq_set[String(v)] = true for card2 in _attune_btns: var on: bool = eq_set.has(String(card2.item_id)) card2.btn.text = "卸下" if on else "装配" card2.btn.disabled = (not on) and eq.size() >= slots_n # 满位禁选 if _refresh_l != null: var rf: Dictionary = att.get("refresh", {}) _refresh_l.text = "三选一刷新 +%d 次/冒险 · Lv%d" % [int(rf.get("charges", 0)), int(rf.get("level", 0))] _refresh_btn.text = "已满级" if bool(rf.get("maxed", false)) else "升级 %d 钥石" % int(rf.get("price", 0)) _refresh_btn.disabled = bool(rf.get("maxed", false)) or balance < int(rf.get("price", 0)) func _rebuild_workshop(data: Dictionary) -> void: _workshop_rows.clear() for child in _workshop_holder.get_children(): child.queue_free() _attune_nodes.clear() _attune_btns.clear() _attune_header_l = null _refresh_l = null _refresh_btn = null for row in data.get("workshop", []): var card := PanelContainer.new() card.add_theme_stylebox_override("panel", UiKit.panel_flat(ROW_PLATE_COL, UiKit.PANEL_BORDER,UiKit.RADIUS_OBJECT, 1, Vector4(10, 4, 10, 4))) var line := HBoxContainer.new() line.add_theme_constant_override("separation", 12) card.add_child(line) var name_l := UiKit.label(String(row.get("display_name", "")),UiKit.FS_HINT, true, UiKit.INK) name_l.custom_minimum_size = Vector2(132, 0) name_l.clip_text = true name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(name_l) var progress_l := UiKit.label("",UiKit.FS_HINT, false, UiKit.STONE_DEEP) # 尾裁微批a 2026-10-08——同行价格墨同案 progress_l.custom_minimum_size = Vector2(150, 0) progress_l.clip_text = true # 营地溢出修——文字全宽不撑柜 progress_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(progress_l) # 状态色随卡⑭ 亮板双色墨制(深墨族替代暖白族)——色随刷新态条件赋值,非机械两行式(T4 收口保留) var status_l := UiKit.label("",UiKit.FS_HINT, true, UiKit.STONE) status_l.clip_text = true # 同上——「已解锁 · 能力封存中」不撑柜(余宽吸收者) status_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS status_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL line.add_child(status_l) _workshop_holder.add_child(card) _workshop_rows.append({"item_id": String(row.get("item_id", "")), "progress_l": progress_l, "status_l": status_l}) _rebuild_attune(data) # 同调装配段重建(迭代 11 第①批 T4——秘宝匣柜台尾部段〔迭代 21 T6 随柜台迁入〕;Slots 点亮+ # 九件行板+Y 升级行,程序占位面) func _rebuild_attune(data: Dictionary) -> void: var att: Dictionary = data.get("attune", {}) _attune_header_l = UiKit.label("",UiKit.FS_HINT, true, UiKit.INK) _workshop_holder.add_child(_attune_header_l) _attune_nodes.append(_attune_header_l) for row in att.get("items", []): var iid := String(row.get("item_id", "")) var line := HBoxContainer.new() line.add_theme_constant_override("separation", 6) var name_l := UiKit.label(String(row.get("display_name", "")),UiKit.FS_HINT, true, UiKit.INK) name_l.custom_minimum_size = Vector2(96, 0) name_l.clip_text = true name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(name_l) var form_l := UiKit.label("被动" if String(row.get("form", "")) == "passive" else "主动·充能",UiKit.FS_CAPTION, false, UiKit.STONE) form_l.custom_minimum_size = Vector2(58, 0) line.add_child(form_l) var btn := UiKit.button("装配", 0.0) # 行板紧凑钮豁免 btn.pressed.connect(attune_toggled.emit.bind(iid)) line.add_child(btn) _workshop_holder.add_child(line) _attune_nodes.append(line) _attune_btns.append({"item_id": iid, "btn": btn}) var refresh_line := HBoxContainer.new() refresh_line.add_theme_constant_override("separation", 6) _refresh_l = UiKit.label("",UiKit.FS_CAPTION, false, UiKit.STONE) refresh_line.add_child(_refresh_l) _refresh_btn = UiKit.button("升级", 0.0) # 行板紧凑钮豁免 _refresh_btn.pressed.connect(_emit_buy_requested.bind("attune_refresh")) refresh_line.add_child(_refresh_btn) _workshop_holder.add_child(refresh_line) _attune_nodes.append(refresh_line) func _rebuild_recent(data: Dictionary) -> void: var lines: Array[String] = [] var recent: Array = data.get("recent", []) if recent.is_empty(): lines.append(" 最近冒险:(暂无记录——完成或结束一次冒险后入账)") for s in recent: var verdict := "失败" if String(s.get("result", "")) == "completed": verdict = "完成" elif String(s.get("result", "")) == "abandoned": verdict = "放弃" lines.append("最近冒险:%s · 通关 %d 关 · 钥石 +%d · 种子 %d" % [verdict, int(s.get("levels_cleared", 0)), int(s.get("keystone_total", 0)), int(s.get("master_seed", 0))]) _recent_list.text = " | ".join(lines) func _emit_closed() -> void: closed.emit() func _emit_buy_requested(attr: String) -> void: buy_requested.emit(attr)