# VERTICAL SLICE - NOT FOR PRODUCTION # 局内暂停层·只读(迭代 17 卡③ 暂停层只读入口批 2026-10-06——任务书 v1.1;事实源=06-待办清单 # 「解锁界面与钥石呈现的交互形态(残余:暂停层只读入口)」)。 # 裁决落形:P1 丁=入口为战斗屏 HUD / 关卡地图屏常驻角标(入口装配在 ui_assembler/两屏,本类不含入口); # P2 甲=登记 modal_stack 设置层同档(确认弹窗之前)带 dim 遮罩;P3 甲=展示钥石余额+三柜台进度 # (属性锻造三行已购等级 / 道具工坊六件解锁态 / 天赋树已购概览——层内行序与分组复用营地屏同序); # P4 甲=进出零动效直开直关(无 tween 无 AnimationPlayer——R5)。 # 只读契约:R4 数据只读同源——账面快照由组合根 open_pause 喂入(每次开层实时读 MetaProgression # 访问器重建,快照仅用单次开层生命周期,跨开层零持有);本类零核心访问、零写通路——唯一信号 # closed(关闭意图,组合根经模态栈单口收口)。R1 公共件单点:全部构成件经 UiKit 工厂产出 #(modal_shell/panel_skinned/label/button/keystone_badge),零裸色值零自建样式。 class_name PauseOverlay extends Control signal closed var _balance_badge: UiKit.KeystoneBadge # 钥石余额徽(badge 工厂件——Q10 甲) var _forge_holder: VBoxContainer var _workshop_holder: VBoxContainer var _tree_label: Label func _init() -> void: var shell: Dictionary = UiKit.modal_shell(self, Color(UiKit.DIM, 0.55), Vector2(560, 0), 10, true, UiKit.panel_skinned(Vector4(20, 16, 20, 16))) # 石板皮(缺位回退数据板——同设置面先例) var box: VBoxContainer = shell.box var title := UiKit.label("暂停",UiKit.FS_TITLE, true, UiKit.INK_TITLE) title.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER box.add_child(title) var hint := UiKit.label("查看本局钥石与永久强化进度(只读——购买请回营地)",UiKit.FS_HINT, false, UiKit.INK_HINT) hint.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER box.add_child(hint) # 钥石余额行(badge 工厂件——与营地顶栏钥石徽同形制) var bal_row := HBoxContainer.new() bal_row.alignment = BoxContainer.ALIGNMENT_CENTER box.add_child(bal_row) _balance_badge = UiKit.keystone_badge("钥石 0") bal_row.add_child(_balance_badge) box.add_child(HSeparator.new()) _section_head(box, "属性锻造 · 已购等级") _forge_holder = _section_holder(box) _section_head(box, "道具工坊 · 解锁态") _workshop_holder = _section_holder(box) _section_head(box, "天赋树 · 已购概览") _tree_label = UiKit.label("",UiKit.FS_HINT, false, UiKit.INK_LABEL) _tree_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART box.add_child(_tree_label) box.add_child(HSeparator.new()) var close_row := HBoxContainer.new() close_row.alignment = BoxContainer.ALIGNMENT_CENTER box.add_child(close_row) var close_btn := UiKit.button("继续", 90.0) # 紧凑返回钮(设置面先例形制;文案=代拍板白名单「继续」级措辞) close_btn.tooltip_text = "关闭暂停层,继续当前冒险(本层只读,不发生任何购买或修改)" close_btn.pressed.connect(_emit_closed) close_row.add_child(close_btn) visible = false # 显式初始化——不靠树序巧合(缺陷⑤家族纪律) # 分组小节头(营地屏分组同构——fs 副标题档深墨) func _section_head(box: VBoxContainer, text: String) -> void: var head := UiKit.label(text,UiKit.FS_BODY, true, UiKit.INK_LABEL) box.add_child(head) func _section_holder(box: VBoxContainer) -> VBoxContainer: var holder := VBoxContainer.new() holder.add_theme_constant_override("separation", 4) box.add_child(holder) return holder # ---- 开层(组合根唯一调用口——快照随开随建,R4 单次开层生命周期口径)---- func open_pause(data: Dictionary) -> void: _balance_badge.set_text("钥石 %d" % int(data.get("balance", 0))) _rebuild_forge(data) _rebuild_workshop(data) _rebuild_tree(data) visible = true # 属性锻造三行(营地屏同序同文案族——名称+每级 flat / 已购等级;只读零价格零按钮) func _rebuild_forge(data: Dictionary) -> void: for c in _forge_holder.get_children(): c.queue_free() for row in (data.get("forge", []) as Array): var line := HBoxContainer.new() line.add_theme_constant_override("separation", 8) var name_l := UiKit.label("%s %+d/级" % [String(row.get("display_name", "")), int(row.get("flat", 0))],UiKit.FS_HINT, true, UiKit.INK) name_l.custom_minimum_size = Vector2(150, 0) name_l.clip_text = true name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(name_l) var spacer := Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL line.add_child(spacer) var lvl_l := UiKit.label("Lv %d" % int(row.get("level", 0)),UiKit.FS_HINT, true, UiKit.INK_TITLE) line.add_child(lvl_l) _forge_holder.add_child(line) # 道具工坊六行(解锁态文案与营地柜台同口径——退役/封存/已解锁/未解锁;进度=解锁判定量读数) func _rebuild_workshop(data: Dictionary) -> void: for c in _workshop_holder.get_children(): c.queue_free() for row in (data.get("workshop", []) as Array): var line := HBoxContainer.new() line.add_theme_constant_override("separation", 8) var name_l := UiKit.label(String(row.get("display_name", "")),UiKit.FS_HINT, true, UiKit.INK) name_l.custom_minimum_size = Vector2(120, 0) name_l.clip_text = true name_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(name_l) var prog_l := UiKit.label("累计通关 %d/%d 次" % [int(row.get("progress", 0)), int(row.get("threshold", 0))],UiKit.FS_HINT, false, UiKit.INK_HINT) prog_l.custom_minimum_size = Vector2(150, 0) prog_l.clip_text = true prog_l.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS line.add_child(prog_l) var st_l := UiKit.label(_workshop_state_text(row),UiKit.FS_HINT, true, UiKit.INK_LABEL) st_l.size_flags_horizontal = Control.SIZE_EXPAND_FILL line.add_child(st_l) _workshop_holder.add_child(line) # 解锁态文案(camp_overlay_view.refresh_camp 同款判据——退役 > 封存 > 已解锁 > 未解锁) func _workshop_state_text(row: Dictionary) -> String: if bool(row.get("retired", false)): return "已退役" if bool(row.get("deferred", false)) and bool(row.get("unlocked", false)): return "已解锁 · 能力封存中" if bool(row.get("unlocked", false)): return "已解锁" return "未解锁" # 天赋树已购概览(单条聚合文案——已购节点等级 + Fork 组激活侧;零已购=空态句) func _rebuild_tree(data: Dictionary) -> void: var owned: Array[String] = [] for row in (data.get("tree", []) as Array): var lvl := int(row.get("level", 0)) if lvl > 0: owned.append("%s Lv%d/%d" % [String(row.get("display_name", "")), lvl, int(row.get("cap", 1))]) for fg in (data.get("fork_groups", []) as Array): var active := String(fg.get("active_side", "")) if active == "": continue var side_name := active for sd in (fg.get("sides", []) as Array): if String(sd.get("side", "")) == active: side_name = String(sd.get("display_name", active)) break owned.append("%s·激活 %s" % [String(fg.get("display_name", "")), side_name]) if owned.is_empty(): _tree_label.text = "尚未购买任何节点(天赋树在营地柜台三)" else: _tree_label.text = "已购 %d 项:%s" % [owned.size(), " · ".join(owned)] func _emit_closed() -> void: closed.emit()