244 lines
17 KiB
GDScript
244 lines
17 KiB
GDScript
## VERTICAL SLICE - NOT FOR PRODUCTION
|
||
## 组合根 UI 装配分步承接件:三屏壳实例化、视图句柄接线、覆盖层装配与模态编排注入按步拆开。
|
||
## 本件是 main._build_ui 的逐字分步搬运(成员访问改 root 前缀)——main 侧留同名薄委托,装配序与 z 序零改动。
|
||
## 层序唯一事实源仍是 main.LAYER_CONTRACT + _apply_layer_contract(本件不持有层序逻辑)。
|
||
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._adventure_screen = AdventureScreenScene.instantiate()
|
||
root.add_child(root._adventure_screen)
|
||
root._menu_screen = MenuScreenScene.instantiate()
|
||
root.add_child(root._menu_screen)
|
||
root._start_overlay = root._menu_screen.start_overlay
|
||
root._battle_root = root._adventure_screen.battle_root
|
||
root.stage = root._adventure_screen.stage
|
||
root.log_view = root._adventure_screen.log_view
|
||
root.info_panel = root._adventure_screen.info_panel
|
||
root.board_zone = root._adventure_screen.board_zone
|
||
root.board_view = root._adventure_screen.board_view # 探针兼容别名(= board_zone.board_view)
|
||
root.ap_bar = root._adventure_screen.ap_bar # 探针兼容别名(= board_zone.ap_bar)
|
||
root.settlement_fx = root._adventure_screen.settlement_fx
|
||
root.level_screen = root._adventure_screen.level_screen
|
||
|
||
static func _build_battle_wiring(root: Variant) -> void:
|
||
# 结算行排播泵(本体在 row_pacer.gd;本类注入视图句柄与呈现刷新回调,零信号连接)
|
||
root._row_pacer = RowPacer.new()
|
||
root._row_pacer.log_view = root.log_view
|
||
root._row_pacer.stage = root.stage
|
||
root._row_pacer.board_zone = root.board_zone
|
||
root._row_pacer.settlement_fx = root.settlement_fx
|
||
root._row_pacer.board_view = root.board_view
|
||
root._row_pacer.refresh_request = root._refresh_presentations
|
||
root._row_pacer.pump_ended = root._on_pump_ended # 演播完边沿统一派发(挂起 modal 优先,其次锁期点击重放)
|
||
root.add_child(root._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.info_panel, "toast": root.board_zone.toast,
|
||
"board_view": root.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.board_zone.action_requested.connect(root._on_board_action)
|
||
root.board_zone.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(战斗屏 HUD 角标)
|
||
root.info_panel.treasure_slot_clicked.connect(root._treasure_ui.on_slot_clicked) # 两步交互第一步
|
||
root.board_zone.stop_pressed.connect(root._on_stop_pressed)
|
||
root.board_zone.refresh_confirmed.connect(root._on_refresh_confirmed)
|
||
root.board_zone.legend_toggled.connect(root._on_legend_toggled)
|
||
root.board_zone.hover_forbidden.connect(root._on_hover_forbidden)
|
||
root.board_zone.chord_attempted.connect(root._on_chord_attempted)
|
||
|
||
root.logger = PlaytestLogger.new()
|
||
root.logger.ap_per_round = int(root.db.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.legend_overlay = overlays.legend_overlay
|
||
root.node_overlay = overlays.node_overlay
|
||
root._end_overlay = overlays.end_overlay
|
||
root.shop_overlay = overlays.shop_overlay
|
||
root._camp_screen = CampScreenScene.instantiate()
|
||
root.add_child(root._camp_screen)
|
||
root.camp_overlay = root._camp_screen.camp_overlay
|
||
# 营地屏壳插位(夹商店层与设置层之间)由 LAYER_CONTRACT 营地槽统一落位——禁散落 move_child
|
||
root.legend_overlay.closed.connect(root._on_modal_closed.bind(root.legend_overlay))
|
||
root.node_overlay.continue_pressed.connect(root._on_node_continue)
|
||
root.level_screen.route_chosen.connect(root._on_route_chosen)
|
||
root.level_screen.status_badge.pressed.connect(root._on_status_badge_pressed) # 状态面板入口(地图屏顶栏角标)
|
||
root.level_screen.abandon_requested.connect(root._on_abandon_requested) # 放弃入口
|
||
root._end_overlay.continue_pressed.connect(root._on_battle_end_continue)
|
||
|
||
static func _build_aux_panels(root: Variant) -> void:
|
||
root.settings_overlay = SettingsOverlay.new(root.settings_store) # 最小设置面(音量/全屏/减动效)
|
||
root.add_child(root.settings_overlay)
|
||
root.settings_overlay.closed.connect(root._on_settings_closed)
|
||
# 局内状态面板·只读(登记设置层同档带 dim 遮罩,确认弹窗之前;
|
||
# 数据=组合根开层实时读 MetaProgression 访问器喂入,本层零写通路)
|
||
root.status_panel = StatusPanel.new()
|
||
root.add_child(root.status_panel)
|
||
root.status_panel.closed.connect(root._on_status_closed)
|
||
# 生涯页·只读全屏页(登记序=营地同档,设置层旁、确认弹窗之前;
|
||
# 树序同步=addChild 同位(登记序=树序=z 序纪律);数据=组合根开层实时读 SaveManager 计数
|
||
# 访问器+MetaProgression 访问器喂入,本层零写通路零跳转;入口=开始层「营地与系统」组生涯钮)
|
||
root.career_overlay = CareerOverlay.new()
|
||
root.add_child(root.career_overlay)
|
||
root.career_overlay.closed.connect(root._on_career_closed)
|
||
# 选档界面(营地交互重做卡③):登记序=生涯同档(树序同步=addChild 同位);三动作只发意图信号
|
||
#(激活/创建/删除+确认分派归组合根——视图零数据层依赖);入口=「进入存档」钮(卡② mn3 接线)。
|
||
root.save_select_overlay = SaveSelectOverlay.new()
|
||
root.add_child(root.save_select_overlay)
|
||
root.save_select_overlay.closed.connect(root._on_save_select_closed)
|
||
root.save_select_overlay.new_slot_requested.connect(root._on_save_select_new)
|
||
root.save_select_overlay.load_slot_requested.connect(root._on_save_select_load)
|
||
root.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.curio_overlay = CurioPickOverlay.new()
|
||
root.add_child(root.curio_overlay)
|
||
# 提升三选一与替换子交互(本体在 curio_interact_controller.gd;本类只接线与回调注入,
|
||
# 模态栈/演播后排窗/呈现刷新/fatal 经回调注入,adventure/runner 现值回调)
|
||
root._bonus = CurioInteractController.new(CurioFlowContext.new(root.db, func() -> Variant: return root.meta, root.curio_overlay, root._end_overlay, root.board_view,
|
||
root.board_zone.toast, func() -> Variant: return root.save_manager, func() -> Variant: return root.run_scope.adventure,
|
||
func() -> Variant: return root.run_scope.runner, func() -> Variant: return root.run_scope.is_adventure,
|
||
root._open_modal, root._open_modal_after_playback, root._close_modal, root._refresh_presentations,
|
||
root._fatal_screen, func() -> String: return root._gp_summary_text({"gp_dropped": root.run_scope.last_gain_amount}))) # 构造参数收拢 context
|
||
root.curio_overlay.pick_confirmed.connect(root._bonus.on_pick_confirmed)
|
||
root.curio_overlay.continue_pressed.connect(root._bonus.on_continue)
|
||
root.curio_overlay.refresh_requested.connect(root._bonus.on_refresh)
|
||
|
||
static func _build_menu_wiring(root: Variant) -> void:
|
||
# 菜单屏接线(句柄经屏壳暴露;三入口化——进入存档/设置/退出游戏,卡② mn3:
|
||
# 进入存档钮落点=卡③选档层 _open_save_select,点击链在此闭环)
|
||
root._menu_screen.entersavebtn.pressed.connect(root._open_save_select)
|
||
# 调试入口=UI 最顶层全局浮层角标(任何 UI 不遮挡——菜单/战斗/地图三枚
|
||
# 屏内角标由全局浮层承载;连接归组合根,树序/登记面见 LAYER_CONTRACT 末位)
|
||
root.debug_badge = UiAssembler.build_debug_badge(root)
|
||
root.add_child(root.debug_badge)
|
||
root.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, func() -> Variant: return root.meta, func() -> Variant: return root.save_manager, root.camp_overlay, root.shop_overlay,
|
||
root.board_zone.toast, root._start_overlay, root.logger, func() -> Variant: return root.run_scope.runner,
|
||
root._open_modal, root._close_modal, root._refresh_presentations)) # 构造参数收拢 context
|
||
root.shop_overlay.buy_requested.connect(root._camp_pres.on_shop_buy)
|
||
root.shop_overlay.refresh_requested.connect(root._camp_pres.on_shop_refresh) # MC-1
|
||
root.shop_overlay.leave_pressed.connect(root._camp_pres.on_shop_leave)
|
||
root.camp_overlay.buy_requested.connect(root._camp_pres.on_camp_buy)
|
||
root.camp_overlay.attune_toggled.connect(root._camp_pres.on_camp_attune_toggle)
|
||
root.camp_overlay.tree_buy_requested.connect(root._camp_pres.on_camp_tree_buy)
|
||
root.camp_overlay.tree_fork_toggled.connect(root._camp_pres.on_camp_fork_toggle)
|
||
root.camp_overlay.forge_buy_requested.connect(root._camp_pres.on_camp_forge_buy) # 锻造三意图(写口唯一归 MetaProgression)
|
||
root.camp_overlay.forge_unlock_requested.connect(root._camp_pres.on_camp_forge_unlock)
|
||
root.camp_overlay.forge_select_requested.connect(root._camp_pres.on_camp_forge_select)
|
||
root.camp_overlay.closed.connect(root._camp_pres.on_camp_closed)
|
||
root._menu_screen.settingsbtn.pressed.connect(root._open_settings) # 最小设置面入口
|
||
root._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._inter_overlay = inter.overlay
|
||
root._inter_label = inter.label
|
||
inter.next_btn.pressed.connect(root._on_inter_next_pressed)
|
||
|
||
# 确认弹窗(新冒险互斥确认弹窗化):树序落在开始层/过场层之后 = 子级最末
|
||
#(纪律「登记序 = 树序 = z 序」——confirm_dialog.open 禁运行时 move_child 自提抬层;
|
||
# 弹窗只在开始菜单域弹出,绘制序恒最上;
|
||
# 诊断转储入口在调试浮窗)
|
||
root.confirm_dialog = ConfirmDialog.new()
|
||
root.add_child(root.confirm_dialog)
|
||
# 消费方编排(卡③ 契约 3/sl8——禁双常驻静态接线):open 前设 _confirm_consumer,
|
||
# confirmed/cancelled 后按该状态量分派;分派面可容纳后续消费方(卡④ 到位直接接入不返工)
|
||
root.confirm_dialog.confirmed.connect(root._on_confirm_accepted)
|
||
root.confirm_dialog.cancelled.connect(root._on_confirm_declined)
|
||
|
||
# 调试浮窗(主菜单三散件收一:随机种子/数值调整/批量模拟/两层调试入口;
|
||
# 全局可开浮窗:调试会话恒开=启动即开〔_ready〕+两屏角标再开+当局数据只读区块;
|
||
# 诊断转储入口在本浮窗,玩家画面零常驻调试件)。
|
||
# 树序在开始层与确认弹窗之后 = 浮于菜单之上的 Dialog(不隐藏开始层——与确认弹窗同款形态);
|
||
# 开窗唯一入口 = _show_debug_overlay(C6 单口——角标/探针/启动路径全收口)
|
||
root.debug_overlay = DebugOverlay.new(root.db, int(root.db.hero.atk), int(root.db.hero.max_hp))
|
||
root.add_child(root.debug_overlay)
|
||
root._seed_edit = root.debug_overlay.seed_edit # 种子读取面在浮窗(_seed_value 唯一消费点不变)
|
||
root.debug_overlay.closed.connect(root._on_modal_closed.bind(root.debug_overlay))
|
||
root.debug_overlay.apply_requested.connect(root._on_tune_apply)
|
||
root.debug_overlay.simulate_requested.connect(root._on_tune_simulate)
|
||
root.debug_overlay.debug_levels_requested.connect(root._start_debug_levels)
|
||
|
||
# 棋盘解析回调族(有态绑定件——board_resolvers.gd 承接;组合根只接线;局部持有不占类成员位)
|
||
var resolvers = BoardResolversScript.new(root.db, root.debug_overlay, func() -> Variant: return root.run_scope.settlement)
|
||
# 奖励徽章数据源:地图战斗资源 身份解析只读回调(已开格身份 icon——挖掘期 Active 相位内有效)
|
||
root.board_view.set_identity_resolver(resolvers.identity_lookup)
|
||
# 数字遮蔽数据源:地形变异 hide_number 解析只读回调(信息型呈现——board_view 渲染面读取)
|
||
root.board_view.set_variant_mask_resolver(resolvers.variant_mask_lookup)
|
||
root.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)
|
||
root._modal_flow = ModalFlow.new()
|
||
root._modal_flow.start_visible_get = func() -> bool: return root._start_overlay.visible
|
||
root._modal_flow.inter_visible_get = func() -> bool: return root._inter_overlay.visible
|
||
root._modal_flow.settlement_get = func() -> Variant: return root.run_scope.settlement
|
||
root._modal_flow.rows_empty_get = func() -> bool: return root._row_pacer.pending_rows().is_empty()
|
||
root._modal_flow.flush_rows = root._flush_row_queue
|
||
root._modal_flow.on_open_sync = func() -> void:
|
||
root._sync_status_entry() # 状态面板入口角标可见性/置灰随开态同步
|
||
root._sync_debug_overlay() # 浮窗当局数据/按钮态随开态同步
|
||
root._modal_flow.on_close_sync = func() -> void:
|
||
root._sync_status_entry() # 关闭恢复角标可点态(无残留——同源口径)
|
||
root._sync_debug_overlay() # 他层关闭复活浮窗时同步刷新(关后自动恢复语义配套)
|
||
root._modal_flow.replay_click = root._replay_locked_click
|
||
root._modals = root._modal_flow.stack # 探针 _list 登记序断言缝(ui_struct 登记序=z 序纪律断言)
|
||
# 视觉层合同装配(唯一事实源=LAYER_CONTRACT:树序+登记序同源派生——改序只改合同表)
|
||
root._apply_layer_contract()
|
||
root._tune_sim = TuneSimController.new(root.db, root.debug_overlay, int(root.db.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)
|