## VERTICAL SLICE - NOT FOR PRODUCTION ## 组合根 UI 装配分步承接件:三屏壳实例化、视图句柄接线、覆盖层装配与模态编排注入按步拆开。 ## 本件是 main._build_ui 的逐字分步搬运(成员访问改 root 前缀)——main 侧留同名薄委托,装配序与 z 序零改动。 ## 层序唯一事实源=root.ui.LAYER_CONTRACT + root.ui.apply_layer_contract(ui_registry.gd——本件不持有层序逻辑)。 extends RefCounted const UiAssembler := preload("res://presentation/ui_assembler.gd") const RowPacer := preload("res://presentation/row_pacer.gd") const TreasureInteractController := preload("res://presentation/treasure_interact_controller.gd") const CurioInteractController := preload("res://presentation/curio_interact_controller.gd") const CampShopPresenter := preload("res://presentation/camp_shop_presenter.gd") const BoardResolversScript := preload("res://presentation/board_resolvers.gd") const DebugSessionScript := preload("res://presentation/debug_session.gd") const TuneSimController := preload("res://presentation/tune_sim_controller.gd") const MenuScreenScene := preload("res://presentation/scenes/menu_screen.tscn") const CampScreenScene := preload("res://presentation/scenes/camp_screen.tscn") const AdventureScreenScene := preload("res://presentation/scenes/adventure_screen.tscn") static func build(root: Variant) -> void: _build_screens(root) _build_battle_wiring(root) _build_overlays(root) _build_aux_panels(root) _build_curio(root) _build_menu_wiring(root) _build_debug_and_resolvers(root) _build_modal_flow(root) static func _build_screens(root: Variant) -> void: # 三屏壳实例化:冒险屏先挂、菜单屏后挂(启动态菜单在顶层=玩家可见—— # 装配序纪律:菜单先挂会被冒险屏盖死,F5 见战斗界面);最终树序+登记序以 LAYER_CONTRACT # 为唯一事实源,_apply_layer_contract 在装配尾统一落位。 # 屏内 level_screen 与 battle_root 无同时可见态(屏级互斥+模态栈遮挡),层内先后零视觉差; # 视觉共存对逐对验算:legend/battle(图例战斗中开——legend 在冒险屏壳之后=其上 ✓)、 # debug/level_screen(浮窗最后子=其上 ✓)、shop/level_screen(商店节点域弹出——模态均在屏壳上 ✓)。 root.ui.screens.adventure_screen = AdventureScreenScene.instantiate() root.add_child(root.ui.screens.adventure_screen) root.ui.screens.menu_screen = MenuScreenScene.instantiate() root.add_child(root.ui.screens.menu_screen) root.ui.screens.start_overlay = root.ui.screens.menu_screen.start_overlay root.ui.screens.battle_root = root.ui.screens.adventure_screen.battle_root root.ui.battle.stage = root.ui.screens.adventure_screen.stage root.ui.battle.log_view = root.ui.screens.adventure_screen.log_view root.ui.battle.info_panel = root.ui.screens.adventure_screen.info_panel root.ui.battle.board_zone = root.ui.screens.adventure_screen.board_zone root.ui.battle.settlement_fx = root.ui.screens.adventure_screen.settlement_fx root.ui.modals.level_screen = root.ui.screens.adventure_screen.level_screen static func _build_battle_wiring(root: Variant) -> void: # 结算行排播泵(本体在 row_pacer.gd;本类注入视图句柄与呈现刷新回调,零信号连接) root.ui.screens.row_pacer = RowPacer.new() root.ui.screens.row_pacer.log_view = root.ui.battle.log_view root.ui.screens.row_pacer.stage = root.ui.battle.stage root.ui.screens.row_pacer.board_zone = root.ui.battle.board_zone root.ui.screens.row_pacer.settlement_fx = root.ui.battle.settlement_fx root.ui.screens.row_pacer.board_view = root.ui.battle.board_zone.board_view root.ui.screens.row_pacer.refresh_request = root._refresh_presentations root.ui.screens.row_pacer.pump_ended = root.ui.modals.flow.pump_ended # 演播完边沿统一派发(挂起 modal 优先,其次锁期点击重放) root.add_child(root.ui.screens.row_pacer) # 秘宝两步交互(本体在 treasure_interact_controller.gd;settlement/runner/grant 随关卡/战斗更替, # 经现值回调注入——仿 identity_resolver 只读回调先例;grant 每战新建编排器,现值取件防悬挂) root._treasure_ui = TreasureInteractController.new({ "db": root.db, "meta": root.meta, "info_panel": root.ui.battle.info_panel, "toast": root.ui.battle.board_zone.toast, "board_view": root.ui.battle.board_zone.board_view, "settlement_get": func() -> Variant: return root.run_scope.settlement, "runner_get": func() -> Variant: return root.run_scope.runner, "grant_get": func() -> Variant: return root.run_scope.settlement.reward_grant() if root.run_scope.settlement != null else null, }) root.ui.battle.board_zone.action_requested.connect(root._on_board_action) root.ui.battle.board_zone.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(战斗屏 HUD 角标) root.ui.battle.info_panel.treasure_slot_clicked.connect(root._treasure_ui.on_slot_clicked) # 两步交互第一步 root.ui.battle.board_zone.stop_pressed.connect(root._on_stop_pressed) root.ui.battle.board_zone.refresh_confirmed.connect(root._on_refresh_confirmed) root.ui.battle.board_zone.legend_toggled.connect(root._on_legend_toggled) root.ui.battle.board_zone.hover_forbidden.connect(root._on_hover_forbidden) root.ui.battle.board_zone.chord_attempted.connect(root._on_chord_attempted) root.logger = PlaytestLogger.new() root.logger.ap_per_round = int(root.db.run.action_economy.ap_per_round) static func _build_overlays(root: Variant) -> void: # 模态覆盖层(登记序 = 树序 = z 遮挡序:图例 < 明细回看 < 节点 < 关卡画面 < 三选一 < 终局 # < 商店 < 营地 < 设置 < 状态面板(只读) < 生涯页(只读) < 确认弹窗 < 调试浮窗; # 关卡画面住冒险屏壳(树序在模态之下)——登记序仍夹在节点层与三选一层之间,遮挡语义零改动) var overlays: Dictionary[Variant, Variant] = UiAssembler.build_overlays(root, root.db) root.ui.modals.legend_overlay = overlays.legend_overlay root.ui.modals.node_overlay = overlays.node_overlay root.ui.modals.end_overlay = overlays.end_overlay root.ui.modals.shop_overlay = overlays.shop_overlay root.ui.screens.camp_screen = CampScreenScene.instantiate() root.add_child(root.ui.screens.camp_screen) root.ui.modals.camp_overlay = root.ui.screens.camp_screen.camp_overlay # 营地屏壳插位(夹商店层与设置层之间)由 LAYER_CONTRACT 营地槽统一落位——禁散落 move_child root.ui.modals.legend_overlay.closed.connect(root.ui.modals.flow.close.bind(root.ui.modals.legend_overlay)) root.ui.modals.node_overlay.continue_pressed.connect(root._on_node_continue) root.ui.modals.level_screen.route_chosen.connect(root._on_route_chosen) root.ui.modals.level_screen.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(地图屏顶栏角标) root.ui.modals.level_screen.abandon_requested.connect(root._on_abandon_requested) # 放弃入口 root.ui.modals.end_overlay.continue_pressed.connect(root._on_battle_end_continue) static func _build_aux_panels(root: Variant) -> void: root.ui.modals.settings_overlay = SettingsOverlay.new(root.settings_store) # 最小设置面(音量/全屏/减动效) root.add_child(root.ui.modals.settings_overlay) root.ui.modals.settings_overlay.closed.connect(root._on_settings_closed) # 局内状态面板·只读(登记设置层同档带 dim 遮罩,确认弹窗之前; # 数据=组合根开层实时读 MetaProgression 访问器喂入,本层零写通路) root.ui.modals.status_panel = StatusPanel.new() root.add_child(root.ui.modals.status_panel) root.ui.modals.status_panel.closed.connect(root._on_status_closed) # 生涯页·只读全屏页(登记序=营地同档,设置层旁、确认弹窗之前; # 树序同步=addChild 同位(登记序=树序=z 序纪律);数据=组合根开层实时读 SaveManager 计数 # 访问器+MetaProgression 访问器喂入,本层零写通路零跳转;入口=开始层「营地与系统」组生涯钮) root.ui.modals.career_overlay = CareerOverlay.new() root.add_child(root.ui.modals.career_overlay) root.ui.modals.career_overlay.closed.connect(root._on_career_closed) # 选档界面(营地交互重做卡③):登记序=生涯同档(树序同步=addChild 同位);三动作只发意图信号 #(激活/创建/删除+确认分派归组合根——视图零数据层依赖);入口=「进入存档」钮(卡② mn3 接线)。 root.ui.modals.save_select_overlay = SaveSelectOverlay.new() root.add_child(root.ui.modals.save_select_overlay) root.ui.modals.save_select_overlay.closed.connect(root._on_save_select_closed) root.ui.modals.save_select_overlay.new_slot_requested.connect(root._on_save_select_new) root.ui.modals.save_select_overlay.load_slot_requested.connect(root._on_save_select_load) root.ui.modals.save_select_overlay.delete_slot_requested.connect(root._on_save_select_delete) static func _build_curio(root: Variant) -> void: # 提升三选一覆盖层(z 序 = 节点层之上、终局层之下;确认后同拍推进; # 插位由 LAYER_CONTRACT curio 槽统一落位——禁散落 move_child) root.ui.modals.curio_overlay = CurioPickOverlay.new() root.add_child(root.ui.modals.curio_overlay) # 提升三选一与替换子交互(本体在 curio_interact_controller.gd;本类只接线与回调注入, # 模态栈/演播后排窗/呈现刷新/fatal 经回调注入,adventure/runner 现值回调) root._bonus = CurioInteractController.new(CurioFlowContext.new(root.db, {"curio_overlay": root.ui.modals.curio_overlay, "end_overlay": root.ui.modals.end_overlay, "board_view": root.ui.battle.board_zone.board_view, "toast": root.ui.battle.board_zone.toast}, {"meta_get": func() -> Variant: return root.meta, "save_get": func() -> Variant: return root.save_manager, "adventure_get": func() -> Variant: return root.run_scope.adventure, "runner_get": func() -> Variant: return root.run_scope.runner, "adventure_mode_get": func() -> Variant: return root.run_scope.is_adventure}, {"open_modal": root.ui.modals.flow.open, "open_modal_after_playback": root.ui.modals.flow.open_after_playback, "close_modal": root.ui.modals.flow.close, "refresh_presentations": root._refresh_presentations, "fatal_screen": root._fatal_screen, "gp_summary": _gp_summary_cb(root)})) # 构造参数收拢 context(u20 三束) root.ui.modals.curio_overlay.pick_confirmed.connect(root._bonus.on_pick_confirmed) root.ui.modals.curio_overlay.continue_pressed.connect(root._bonus.on_continue) root.ui.modals.curio_overlay.refresh_requested.connect(root._bonus.on_refresh) static func _build_menu_wiring(root: Variant) -> void: # 菜单屏接线(句柄经屏壳暴露;三入口化——进入存档/设置/退出游戏,卡② mn3: # 进入存档钮落点=卡③选档层 _open_save_select,点击链在此闭环) root.ui.screens.menu_screen.entersavebtn.pressed.connect(root._open_save_select) # 调试入口=UI 最顶层全局浮层角标(任何 UI 不遮挡——菜单/战斗/地图三枚 # 屏内角标由全局浮层承载;连接归组合根,树序/登记面见 LAYER_CONTRACT 末位) root.ui.screens.debug_badge = UiAssembler.build_debug_badge(root) root.add_child(root.ui.screens.debug_badge) root.ui.screens.debug_badge.pressed.connect(root._on_debug_badge_pressed) # 营地/商店投影(本体在 camp_shop_presenter.gd;本类只接线、探针缝保缝转发; # 购买写调用唯一经 MetaProgression/LevelRunner core 单口——组合根 lambda 现值注入) root._camp_pres = CampShopPresenter.new(CampFlowContext.new(root.db, {"camp_overlay": root.ui.modals.camp_overlay, "shop_overlay": root.ui.modals.shop_overlay, "toast": root.ui.battle.board_zone.toast, "start_overlay": root.ui.screens.start_overlay, "logger": root.logger}, {"meta_get": func() -> Variant: return root.meta, "save_get": func() -> Variant: return root.save_manager, "runner_get": func() -> Variant: return root.run_scope.runner}, {"open_modal": root.ui.modals.flow.open, "close_modal": root.ui.modals.flow.close, "refresh_presentations": root._refresh_presentations})) # 构造参数收拢 context(u21 三束) root.ui.modals.shop_overlay.buy_requested.connect(root._camp_pres.on_shop_buy) root.ui.modals.shop_overlay.refresh_requested.connect(root._camp_pres.on_shop_refresh) # MC-1 root.ui.modals.shop_overlay.leave_pressed.connect(root._camp_pres.on_shop_leave) root.ui.modals.camp_overlay.buy_requested.connect(root._camp_pres.on_camp_buy) root.ui.modals.camp_overlay.attune_toggled.connect(root._camp_pres.on_camp_attune_toggle) root.ui.modals.camp_overlay.tree_buy_requested.connect(root._camp_pres.on_camp_tree_buy) root.ui.modals.camp_overlay.tree_fork_toggled.connect(root._camp_pres.on_camp_fork_toggle) root.ui.modals.camp_overlay.forge_buy_requested.connect(root._camp_pres.on_camp_forge_buy) # 锻造三意图(写口唯一归 MetaProgression) root.ui.modals.camp_overlay.forge_unlock_requested.connect(root._camp_pres.on_camp_forge_unlock) root.ui.modals.camp_overlay.forge_select_requested.connect(root._camp_pres.on_camp_forge_select) root.ui.modals.camp_overlay.closed.connect(root._camp_pres.on_camp_closed) root.ui.screens.menu_screen.settingsbtn.pressed.connect(root._open_settings) # 最小设置面入口 root.ui.screens.menu_screen.quitbtn.pressed.connect(root._quit_game) # 退出游戏提级一级菜单 static func _build_debug_and_resolvers(root: Variant) -> void: var inter: Dictionary[Variant, Variant] = UiAssembler.build_inter_overlay(root) root.ui.screens.inter_overlay = inter.overlay root.ui.screens.inter_label = inter.label inter.next_btn.pressed.connect(root._on_inter_next_pressed) # 确认弹窗(新冒险互斥确认弹窗化):树序落在开始层/过场层之后 = 子级最末 #(纪律「登记序 = 树序 = z 序」——confirm_dialog.open 禁运行时 move_child 自提抬层; # 弹窗只在开始菜单域弹出,绘制序恒最上; # 诊断转储入口在调试浮窗) root.ui.modals.confirm_dialog = ConfirmDialog.new() root.add_child(root.ui.modals.confirm_dialog) # 消费方编排(卡③ 契约 3/sl8——禁双常驻静态接线):open 前设 _confirm_consumer, # confirmed/cancelled 后按该状态量分派;分派面可容纳后续消费方(卡④ 到位直接接入不返工) root.ui.modals.confirm_dialog.confirmed.connect(root._on_confirm_accepted) root.ui.modals.confirm_dialog.cancelled.connect(root._on_confirm_declined) # 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口; # 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块; # 诊断转储入口在本浮窗,玩家画面零常驻调试件)。 # 树序在开始层与确认弹窗之后 = 浮于菜单之上的 Dialog(不隐藏开始层——与确认弹窗同款形态); # 开窗唯一入口 = _show_debug_overlay(C6 单口——角标/探针/启动路径全收口) root.ui.modals.debug_overlay = DebugOverlay.new(root.db, int(root.db.combat.hero.atk), int(root.db.combat.hero.max_hp)) root.add_child(root.ui.modals.debug_overlay) root.ui.modals.debug_overlay.closed.connect(root.ui.modals.flow.close.bind(root.ui.modals.debug_overlay)) root.ui.modals.debug_overlay.apply_requested.connect(root._on_tune_apply) root.ui.modals.debug_overlay.simulate_requested.connect(root._on_tune_simulate) root.ui.modals.debug_overlay.debug_levels_requested.connect(root._start_debug_levels) # 棋盘解析回调族(有态绑定件——board_resolvers.gd 承接;组合根只接线;局部持有不占类成员位) var resolvers = BoardResolversScript.new(root.db, root.ui.modals.debug_overlay, func() -> Variant: return root.run_scope.settlement) # 奖励徽章数据源:地图战斗资源 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效) root.ui.battle.board_zone.board_view.set_identity_resolver(resolvers.identity_lookup) # 数字遮蔽数据源:地形变异 hide_number 解析只读回调(信息型呈现——board_view 渲染面读取) root.ui.battle.board_zone.board_view.set_variant_mask_resolver(resolvers.variant_mask_lookup) root.ui.battle.board_zone.board_view.set_variant_mark_resolver(resolvers.variant_mark_lookup) # flat_damage 未开格角标 # 调试会话(英雄生效值预览回调绑同一解析件——创建晚于浮窗、随 _build_ui 一次接线) root._debug = DebugSessionScript.new(root.db, root.get_node(root.AUTOLOAD_PATH_RNG), func() -> Dictionary: return resolvers.debug_hero_override(), root._seed_value) root._debug.battle_created.connect(root._on_debug_battle_created) root._debug.battle_ready.connect(root._on_debug_battle_ready) static func _build_modal_flow(root: Variant) -> void: # 模态登记(缺陷⑤纪律——登记序 = 树序 = z 序):层序唯一事实源=LAYER_CONTRACT, # _apply_layer_contract 统一落位(关卡画面宿住冒险屏壳、营地屏壳夹商店与设置层之间、 # 三选一对位终局层之下等特例均在合同数据内表达,禁散落 move_child) # 模态编排注入(ModalFlow 纯逻辑类:依赖仅 ModalStack+查询 Callable——实例随 ui.modals 容器构造建立) root.ui.modals.flow.start_visible_get = func() -> bool: return root.ui.screens.start_overlay.visible root.ui.modals.flow.inter_visible_get = func() -> bool: return root.ui.screens.inter_overlay.visible root.ui.modals.flow.settlement_get = func() -> Variant: return root.run_scope.settlement root.ui.modals.flow.rows_empty_get = func() -> bool: return root.ui.screens.row_pacer.pending_rows().is_empty() root.ui.modals.flow.flush_rows = root.ui.screens.row_pacer.flush root.ui.modals.flow.on_open_sync = func() -> void: root._sync_status_entry() # 状态面板入口角标可见性/置灰随开态同步 root._sync_debug_overlay() # 浮窗当局数据/按钮态随开态同步 root.ui.modals.flow.on_close_sync = func() -> void: root._sync_status_entry() # 关闭恢复角标可点态(无残留——同源口径) root._sync_debug_overlay() # 他层关闭复活浮窗时同步刷新(关后自动恢复语义配套) root.ui.modals.flow.replay_click = root._replay_locked_click # 视觉层合同装配(唯一事实源=LAYER_CONTRACT:树序+登记序同源派生——改序只改合同表) root.ui.apply_layer_contract(root) root._tune_sim = TuneSimController.new(root.db, root.ui.modals.debug_overlay, int(root.db.combat.hero.max_hp)) static func fatal_screen(root: Variant, msg: String) -> void: var l := UiKit.label(msg,UiKit.FS_SUB, true, UiKit.RED_ALERT) l.set_anchors_preset(Control.PRESET_CENTER) root.add_child(l) # GP 总结行回调(gp_summary 束键——单行 lambda 内嵌字典实参收尾歧义,提函数承载) static func _gp_summary_cb(root: Variant) -> Callable: return func() -> String: return root._gp_summary_text({"gp_dropped": root.run_scope.last_gain_amount})