## VERTICAL SLICE - NOT FOR PRODUCTION ## 组合根覆盖层流程承接件:设置/状态面板/生涯页/选档界面/图例/钱包事件的开合与数据投喂。 ## 本件是 main.gd 同名函数的逐字搬运(成员访问改 root 前缀)——main 侧留同名薄委托,探针 main.X 路径零改动。 ## 全部开合走组合根模态编排口(root.ui.flow.open/close);本件零成员态。 extends RefCounted static func open_settings(root: Variant) -> void: root.ui.screens.start_overlay.visible = false root.ui.flow.open(root.ui.modals.menu.settings_overlay) # 模态栈单口 static func on_settings_closed(root: Variant) -> void: root.ui.flow.close(root.ui.modals.menu.settings_overlay) # 模态栈单口 # 语境分支(卡⑤ 契约 3——谓词=camp_esc_overlay.visible,谁开的设置谁收): # 营地浮窗路径关设置回浮窗不回主菜单(栈语义:浮窗登记在下、由树序遮挡非属性隐藏); # 菜单路径保留开始层恢复硬副作用。 if not root.ui.modals.camp.esc_overlay.visible: root.ui.screens.start_overlay.visible = true # ---- 局内状态面板·只读(入口角标在两屏装配段;开合与数据喂入归组合根)---- # 状态面板快照投影(数据只读同源:每次开层实时读访问器现值拼装—— # 快照仅服务单次开层生命周期(open_status 重建各行),跨开层零持有;零写调用。 # 八键换代(迭代 36 卡③):跨关解锁概览退役(营地/生涯承载)——只留本局信息; # ledger 直取下标(键缺失=致命,禁 .get 缺省留门——fail-fast 红线);禁 null 守卫软回退 # (两入口开层时 runner 恒在役——状态面板卡 F3 证据链) static func status_data(root: Variant) -> Dictionary: return HudPresenter.status_data(root.db, root.meta, root.run_scope.runner, root.treasure_ui()) static func open_status_panel(root: Variant) -> void: root.ui.flow.open(root.ui.modals.run.status_panel) # 模态栈单口(开层前排空排播队列——既有层同款行为,目检确认项) root.ui.modals.run.status_panel.open_status(status_data(root)) static func on_status_closed(root: Variant) -> void: root.ui.flow.close(root.ui.modals.run.status_panel) # 模态栈单口 static func open_career(root: Variant) -> void: root.ui.screens.start_overlay.visible = false root.ui.flow.open(root.ui.modals.menu.career_overlay) # 模态栈单口 root.ui.modals.menu.career_overlay.open_career(HudPresenter.career_data(root.db, root.save_manager, root.meta)) static func on_career_closed(root: Variant) -> void: root.ui.flow.close(root.ui.modals.menu.career_overlay) # 模态栈单口 if not root.ui.modals.camp.scene.visible: # 营地语境关生涯不回开始层(笔记本入口——卡④ T3;栈已复现场景根) root.ui.screens.start_overlay.visible = true # ---- 营地场景点选路由(卡④ T3/T4——场景根 object_pressed 组合根分派)---- # 大门两分支(裁决 5,Q5 甲):点大门永远先出确认不直接进冒险——有断点=三出口弹窗 #(继续上一段冒险/开始新冒险/取消),无断点=是否开始新冒险(是/否);cancelled 恒=无变更退回营地。 # 篝火回主菜单必须二次确认(camp.md v2.2 裁定);确认后营地层与选档层同收+开始层恢复(关层收闭)。 static func on_camp_object_pressed(root: Variant, key: StringName) -> void: match key: &"forge": root.camp_pres().open_camp_forge() &"chest": root.camp_pres().open_camp_treasure() &"dummy": root.camp_pres().open_camp_tree() &"board": root.camp_pres().open_camp_codex() &"notebook": open_career(root) # 生涯页入口改挂帐篷旁笔记本(CareerOverlay 本体零改——卡④ T3) &"gate": _open_gate_confirm(root) &"campfire": root.set_confirm_consumer("campfire_leave") root.ui.modals.menu.confirm_dialog.open("确定离开营地回到主菜单?", "离开", "留下") static func _open_gate_confirm(root: Variant) -> void: if root.save_manager.has_breakpoint(): root.set_confirm_consumer("gate") root.ui.modals.menu.confirm_dialog.open_three("有未完成的冒险——继续上一段冒险,还是开始新冒险?", "继续上一段冒险", "开始新冒险", "取消") return root.set_confirm_consumer("gate_new") root.ui.modals.menu.confirm_dialog.open("是否开始新冒险?", "是", "否") # 生涯页快照投影 —— 承接体见 HudPresenter.career_data(开层调用点直接改引) # ==================== 选档界面(营地交互重做卡③)==================== # 开层(模态栈单口;与开始层互斥=career 同款先例)。「进入存档」钮归卡② mn3——本函数为 # 该钮的唯一落点(卡③ 批内探针函数直调=中间态,实现报告「已知中间态」登记)。 static func open_save_select(root: Variant) -> void: root.ui.screens.start_overlay.visible = false root.ui.flow.open(root.ui.modals.menu.save_select_overlay) # 模态栈单口 root.ui.modals.menu.save_select_overlay.open_save_select(HudPresenter.save_select_data(root.save_manager)) static func on_save_select_closed(root: Variant) -> void: root.ui.flow.close(root.ui.modals.menu.save_select_overlay) # 模态栈单口 root.ui.screens.start_overlay.visible = true # 槽数据快照 —— 承接体见 HudPresenter.save_select_data(开层调用点直接改引) # 空槽新档链(卡④ T3 换代:落点=营地场景根——CampOverlay 随四柜台拆解退役):卡① 创建=激活=白板落盘, # 落点开层。动线序(禁先关后开):先落点开层(开始层保持隐藏),选档层被模态栈遮挡隐藏、不关闭。 static func on_save_select_new(root: Variant, slot: int) -> void: root.save_manager.slots.create_slot(slot) root.camp_pres().open_camp() # 有档槽进档链(契约 8 进档即激活):卡① 激活+读盘(兜底/拒读链按槽成立)→落点=营地场景根(卡④ T3)。 static func on_save_select_load(root: Variant, slot: int) -> void: root.save_manager.slots.activate_slot(slot) root.camp_pres().open_camp() # 删档链(契约 3):第一次点击只出确认窗(消费方编排——open 前设消费方=delete_slot), # 确认才删、取消不删;禁长按删/一次点击即删/撤销式软删。 static func on_save_select_delete(root: Variant, slot: int) -> void: root.set_confirm_consumer("delete_slot") root.set_confirm_consumer_slot(slot) root.ui.flow.open(root.ui.modals.menu.confirm_dialog) # 模态栈单口(显示编排/挂起兜底/遮挡记账——与其余覆盖层同口) root.ui.modals.menu.confirm_dialog.open("删除存档位 %d?删除后该档不可恢复。" % slot, "删除", "取消") # 入口角标点击(两屏同款处理器):P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰, # 探针 pressed.emit 绕过 disabled 时由此兜住;地图屏自身的登记开态不计——has_open_excluding) static func on_status_badge_pressed(root: Variant) -> void: var map_on: bool = root.ui.modals.run.level_screen != null and root.ui.modals.run.level_screen.visible # P1 守卫兜底——其他模态开着即拒开(常态防线=角标置灰,探针 pressed.emit 绕过 disabled 时由此兜住; # 地图屏自身的登记开态不计;判据排除调试浮窗:对局中浮窗开着时状态角标仍可点) if (map_on and root.ui.flow.badge_greyed_map()) or (not map_on and root.ui.flow.badge_greyed_battle()): return open_status_panel(root) # 入口角标可见性/置灰同步(P1 丁+守卫——单一同步点):战斗屏角标=对局内且地图屏未激活; # 地图屏角标=随屏;置灰=登记册上除地图屏自身外任一层开着(防「看得见点不着」态)。 # 调用面=模态开合单口与屏级互斥口(凡影响开态/屏态的路径必经其一)。 static func sync_status_entry(root: Variant) -> void: if root.ui.modals.run.status_panel == null or root.ui.flow == null or root.ui.battle.board_zone == null or root.ui.modals.run.level_screen == null: return var map_on: bool = root.ui.modals.run.level_screen.visible root.ui.battle.board_zone.status_badge.visible = root.run_scope.level_active and not map_on root.ui.modals.run.level_screen.status_badge.visible = map_on # 置灰判据为排除式(调试浮窗开态不计入:对局中浮窗开着时角标仍可点) root.ui.battle.board_zone.status_badge.disabled = root.ui.flow.badge_greyed_battle() # 互斥域谓词 root.ui.modals.run.level_screen.status_badge.disabled = root.ui.flow.badge_greyed_map() # 调试浮层角标(全局浮层任何 UI 不遮挡——可见性不随场景变,只抬 offset # 避让:地图屏底栏 85px 常驻,角标抬至底栏上方 16px;其余场景右下角 16px 贴底) root.ui.screens.debug_badge.offset_bottom = -101.0 if map_on else -16.0 static func on_camp_closed(root: Variant) -> void: root.camp_pres().on_camp_closed() static func camp_data(root: Variant) -> Dictionary: return HudPresenter.camp_data(root.camp_pres()) static func on_wallet_changed(root: Variant, event: String, gp: int, extra: Dictionary) -> void: # wallet 行族转发(到账/购买/清零——试玩日志验收取数面)+ 常驻余额与在开商店覆盖层重刷 root.logger.log_wallet(event, gp, extra) if String(event) == "gain": root.run_scope.last_gain_amount = int(extra.get("amount", 0)) # GP 增量取数(每战恰一笔 gain) if root.ui.modals.menu.shop_overlay.visible and root.run_scope.runner != null: if root.run_scope.runner.shop.is_shop_pool(): root.ui.modals.menu.shop_overlay.refresh_shelf_state(int(root.run_scope.runner.gp)) # pool 面余额联动(货架内容仅买后重抽变) else: root.ui.modals.menu.shop_overlay.refresh_state(root.run_scope.runner.shop.shop_products(), int(root.run_scope.runner.gp)) root.refresh_presentations() static func on_legend_toggled(root: Variant) -> void: if root.ui.modals.run.legend_overlay.is_open(): root.ui.flow.close(root.ui.modals.run.legend_overlay) else: root.ui.flow.open(root.ui.modals.run.legend_overlay)