From c08a901d2de96c0cc6aa1ac3c853ab57020a1790 Mon Sep 17 00:00:00 2001 From: DKChan <524912269@qq.com> Date: Tue, 6 Oct 2026 21:41:14 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9A=82=E5=81=9C=E5=B1=82=E7=BB=84A=EF=BC=9A?= =?UTF-8?q?=E5=8F=AA=E8=AF=BB=E8=A6=86=E7=9B=96=E5=B1=82=E6=9C=AC=E4=BD=93?= =?UTF-8?q?+badge=E5=B7=A5=E5=8E=82=E4=BB=B6=E6=94=B6=E7=BC=96+=E8=90=A5?= =?UTF-8?q?=E5=9C=B0=E9=92=A5=E7=9F=B3=E5=BE=BD=E8=BF=81=E7=A7=BB=EF=BC=88?= =?UTF-8?q?=E8=BF=AD=E4=BB=A317=E5=8D=A1=E2=91=A2T1=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ui_kit.gd 新增 KeystoneBadge 徽章工厂件(Q10 甲裁决收编):钥石徽形制参数化 (烛光黄底 CANDLE+木描边 CAMP_HEADER_BAND+icon_rect 守卫装载缺位纯文本回退+INK_ON_GOLD 深墨字,value_label/set_text 面),消费方=营地覆盖层+暂停层——零新素材零新色值,值全取现行 token - camp_overlay_view.gd 钥石徽本地拼装(原 :79-92 panel_flat+icon_rect+label 三段)迁 UiKit.keystone_badge 工厂调用,形制参数逐项同原装配=观感零变化,消单点重复 - 新增 presentation/views/pause_overlay.gd:局内暂停层·只读——标题区+钥石余额行(badge 工厂件) +属性锻造三行已购等级/道具工坊六行解锁态(文案判据同营地柜台:退役>封存>已解锁>未解锁) /天赋树已购概览(已购节点等级+Fork 激活侧聚合文案,空态句)+「继续」关闭钮(代拍板:紧凑 90 返回钮形制沿用设置面先例);全部经 UiKit 工厂拼装(modal_shell/panel_skinned/label/button/ keystone_badge)零裸色值零自建样式(R1);零 tween 零 AnimationPlayer=零动效直开直关(R5/P4 甲) - main.gd 接线:PauseOverlay 实例化+登记 modal_stack 登记册设置层同档(确认弹窗之前——P2 甲, 登记序=树序=z 序纪律段同步换代)+closed 接 _on_pause_closed(模态栈单口收口)+ _pause_data() 快照投影(R4:实时读 MetaProgression 访问器 keystone_balance/forge_level/ workshop_status/tree_camp_rows/tree_fork_groups,行序复用营地屏同序〔MetaTable.FORGE_ATTRS〕, 快照仅单次开层生命周期、每次开层重建)+_open_pause_layer() 开层口(_open_modal 单口) - 纯呈现零数值改动=零 MC;核心层零改动(meta_progression/save_manager 未触碰) - 轻闸:冒烟 783/0 + ui_struct_probe PASS(基线对拍 T0:783/0 零漂移) --- game/presentation/main.gd | 45 ++++- game/presentation/views/camp_overlay_view.gd | 18 +- game/presentation/views/pause_overlay.gd | 166 +++++++++++++++++++ game/presentation/views/ui_kit.gd | 28 ++++ 4 files changed, 241 insertions(+), 16 deletions(-) create mode 100644 game/presentation/views/pause_overlay.gd diff --git a/game/presentation/main.gd b/game/presentation/main.gd index 30b47ccb..9b077747 100644 --- a/game/presentation/main.gd +++ b/game/presentation/main.gd @@ -117,6 +117,7 @@ var shop_overlay: ShopOverlay # 商店覆盖层(GP+商店房实施 var camp_overlay: CampOverlay # 营地覆盖层(元进度实现批——主菜单营地入口兑付呈现) var settings_store # 系统级设置独立存储(迭代 8 卡④——音量/全屏/减动效) var settings_overlay: SettingsOverlay # 设置覆盖层(迭代 8 卡④——最小设置面) +var pause_overlay: PauseOverlay # 局内暂停层·只读(迭代 17 卡③——钥石余额+营地三柜台进度查看;登记设置层同档) var debug_overlay: DebugOverlay # 调试浮窗(迭代 14.5 调试整合批——种子/数值调整/批量模拟/两层调试入口收一) var _modals = null # 模态层级状态机(modal_stack.gd——缺陷⑤纪律载体) @@ -240,7 +241,7 @@ func _build_ui() -> void: logger.ap_per_round = int(db.action_economy.ap_per_round) # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局 - # < 商店 < 营地 < 设置 < 确认弹窗 < 调试浮窗) + # < 商店 < 营地 < 设置 < 暂停(只读) < 确认弹窗 < 调试浮窗) var overlays: Dictionary = UiAssembler.build_overlays(self, db) legend_overlay = overlays.legend_overlay node_overlay = overlays.node_overlay @@ -260,6 +261,11 @@ func _build_ui() -> void: settings_overlay = SettingsOverlay.new(settings_store) # 迭代 8 卡④——最小设置面(音量/全屏/减动效) add_child(settings_overlay) settings_overlay.closed.connect(_on_settings_closed) + # 局内暂停层·只读(迭代 17 卡③——P2 甲:登记设置层同档带 dim 遮罩,确认弹窗之前; + # 数据=组合根开层实时读 MetaProgression 访问器喂入(R4),本层零写通路) + pause_overlay = PauseOverlay.new() + add_child(pause_overlay) + pause_overlay.closed.connect(_on_pause_closed) # 提升三选一覆盖层(迭代 10——z 序 = 节点层之上、终局层之下;确认后同拍推进) curio_overlay = CurioPickOverlay.new() add_child(curio_overlay) @@ -327,9 +333,10 @@ func _build_ui() -> void: debug_overlay.simulate_requested.connect(_on_tune_simulate) debug_overlay.debug_levels_requested.connect(_start_debug_levels) - # 模态登记(缺陷⑤纪律——登记序 = 树序:含 move_child 调位后的关卡画面/三选一终位) + # 模态登记(缺陷⑤纪律——登记序 = 树序:含 move_child 调位后的关卡画面/三选一终位; + # 暂停层设置层同档、确认弹窗之前——P2 甲) _modals = ModalStack.new() - for m: Control in [legend_overlay, node_overlay, level_screen, curio_overlay, _end_overlay, shop_overlay, camp_overlay, settings_overlay, confirm_dialog, debug_overlay]: + for m: Control in [legend_overlay, node_overlay, level_screen, curio_overlay, _end_overlay, shop_overlay, camp_overlay, settings_overlay, pause_overlay, confirm_dialog, debug_overlay]: _register_modal(m) _tune_sim = TuneSimController.new(db, debug_overlay, int(db.hero.max_hp)) @@ -924,6 +931,38 @@ func _on_settings_closed() -> void: _refresh_resume_button() +# ---- 局内暂停层·只读(迭代 17 卡③——入口角标在两屏装配段〔组 B〕;开合与数据喂入归组合根)---- + +# 暂停层快照投影(R4 数据只读同源:每次开层实时读 MetaProgression 访问器现值拼装—— +# 快照仅服务单次开层生命周期(open_pause 重建各行),跨开层零持有;零写调用) +func _pause_data() -> Dictionary: + var forge_rows: Array[Dictionary] = [] + for attr in MetaTable.FORGE_ATTRS: + var row: Dictionary = db.meta_forge_row(attr) + forge_rows.append({ + "attr": attr, + "display_name": String(row.get("display_name", "")), + "level": meta.forge_level(attr), + "flat": int(row.get("flat_value", 0)), + }) + return { + "balance": meta.keystone_balance(), + "forge": forge_rows, + "workshop": meta.workshop_status(), + "tree": meta.tree_camp_rows(), + "fork_groups": meta.tree_fork_groups(), + } + + +func _open_pause_layer() -> void: + _open_modal(pause_overlay) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项) + pause_overlay.open_pause(_pause_data()) + + +func _on_pause_closed() -> void: + _close_modal(pause_overlay) # 模态栈单口 + + # ---- 探针缝保缝(T4/D4 拆件——click/ui_probe 营地段直呼组合根面;实现归 camp_shop_presenter.gd)---- func _on_camp_closed() -> void: diff --git a/game/presentation/views/camp_overlay_view.gd b/game/presentation/views/camp_overlay_view.gd index c042df7f..8a910815 100644 --- a/game/presentation/views/camp_overlay_view.gd +++ b/game/presentation/views/camp_overlay_view.gd @@ -76,20 +76,12 @@ func _init() -> void: var spacer := Control.new() spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL top.add_child(spacer) - var badge := PanelContainer.new() - # M3(UI 落码批 2026-10-04):钥石徽章烛光黄底+深墨字(demo ②——旧暗底 CAMP_BADGE_PANEL 退役; - # BADGE_PANEL_COL 常量保留他用不动) - badge.add_theme_stylebox_override("panel", - UiKit.panel_flat(UiKit.CANDLE, UiKit.CAMP_HEADER_BAND,UiKit.RADIUS_OBJECT, 1, Vector4(14, 8, 14, 8))) + # M3(UI 落码批):钥石徽章烛光黄底+深墨字(demo ②——旧暗底 CAMP_BADGE_PANEL 退役; + # BADGE_PANEL_COL 常量保留他用不动)。Q10 甲(2026-10-06):本地拼装迁 UiKit.keystone_badge + # 工厂调用(badge 单点化——暂停层同款消费);形制参数逐项同原装配,观感零变化 + var badge := UiKit.keystone_badge("钥石 0") top.add_child(badge) - var bal_row := HBoxContainer.new() - bal_row.add_theme_constant_override("separation", 6) - badge.add_child(bal_row) - var keystone_icon := UiKit.icon_rect(UiKit.TEX_ICON_KEYSTONE, 32) # P6 甲②(2026-10-06):64 源 0.5×(原 20=0.3125× 违 §8.2);缺位 null → 纯文本回退 - if keystone_icon != null: - bal_row.add_child(keystone_icon) - _balance_label = UiKit.label("钥石 0",UiKit.FS_SUB, true, UiKit.INK_ON_GOLD) - bal_row.add_child(_balance_label) + _balance_label = badge.value_label # ── 三柜台列(方向稿同构:头带深棕+米色板身)── # 三柜等宽分配(2026-10-06 用户实测缺陷修——原定宽 356+行标签按文字全宽算 min,进度行文案 diff --git a/game/presentation/views/pause_overlay.gd b/game/presentation/views/pause_overlay.gd new file mode 100644 index 00000000..feaa36b2 --- /dev/null +++ b/game/presentation/views/pause_overlay.gd @@ -0,0 +1,166 @@ +# 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() diff --git a/game/presentation/views/ui_kit.gd b/game/presentation/views/ui_kit.gd index be6846d1..96d22fd0 100644 --- a/game/presentation/views/ui_kit.gd +++ b/game/presentation/views/ui_kit.gd @@ -503,6 +503,34 @@ static func icon_rect(tex_path: String, px: float) -> TextureRect: return tr +# 钥石徽(badge 工厂件——Q10 甲收编 2026-10-06:原营地顶栏钥石徽本地拼装〔camp_overlay_view〕 +# 参数化收编,消费方 = 营地覆盖层 + 暂停层钥石余额行(迭代 17 卡③)。形制 = 烛光黄底+木色描边 +# +钥石图标(icon_rect 守卫装载,缺位=纯文本回退)+深墨字;零新素材零新色值——值全取现行 token) +class KeystoneBadge: + extends PanelContainer + var value_label: Label + + func _init(text: String, icon_path: String, icon_px: float) -> void: + add_theme_stylebox_override("panel", + UiKit.panel_flat(UiKit.CANDLE, UiKit.CAMP_HEADER_BAND, UiKit.RADIUS_OBJECT, 1, Vector4(14, 8, 14, 8))) + var row := HBoxContainer.new() + row.add_theme_constant_override("separation", 6) + add_child(row) + if icon_path != "": + var ic := UiKit.icon_rect(icon_path, icon_px) + if ic != null: + row.add_child(ic) + value_label = UiKit.label(text, UiKit.FS_SUB, true, UiKit.INK_ON_GOLD) + row.add_child(value_label) + + func set_text(t: String) -> void: + value_label.text = t + + +static func keystone_badge(text: String, icon_path := TEX_ICON_KEYSTONE, icon_px := 32.0) -> KeystoneBadge: + return KeystoneBadge.new(text, icon_path, icon_px) + + # 按钮皮(常态/按压两态+墨字——缺位 = 引擎默认主题,素材批前逐位一致) static func _apply_button_skin(b: Button) -> void: var tex_n := load_tex(TEX_BUTTON_STONE)