class_name GameSceneRuntimeAssembler extends RefCounted const UiMotionScript := preload("res://scripts/ui/common/ui_motion.gd") const FarmViewportControllerScript := preload("res://scripts/ui/farm/farm_viewport_controller.gd") const ModalLayerControllerScript := preload("res://scripts/ui/common/modal_layer_controller.gd") const MainMenuControllerScript := preload("res://scripts/ui/menus/main_menu_controller.gd") const TopBarViewScript := preload("res://scripts/ui/hud/top_bar_view.gd") const LandGridViewScript := preload("res://scripts/ui/farm/land_grid_view.gd") const LandActionMenuScript := preload("res://scripts/ui/farm/land_action_menu.gd") const PlantTipViewScript := preload("res://scripts/ui/farm/plant_tip_view.gd") const SeedBagViewScript := preload("res://scripts/ui/farm/seed_bag_view.gd") const FarmInteractionCoordinatorScript := preload("res://scripts/ui/farm/farm_interaction_coordinator.gd") const SceneHitRouterScript := preload("res://scripts/ui/scene_hit_router.gd") const GameTitleFont := preload("res://assets/fonts/google/LilitaOne-Regular.ttf") const LandLevel0Texture := preload("res://assets/egret/land/land0.png") const LandLevel1Texture := preload("res://assets/egret/land/land1.png") const LandLevel2Texture := preload("res://assets/egret/assets/icon/land/land2.png") const LandLevel3Texture := preload("res://assets/egret/assets/icon/land/land3.png") const LockedLandTexture := preload("res://assets/egret/land/land-1.png") const LandLightTexture := preload("res://assets/egret/common/land_light_png.tres") const BagBgTexture := preload("res://assets/egret/common/bag_bg_png.tres") const BagLineTexture := preload("res://assets/egret/common/bag_line_png.tres") const LogItemBgTexture := preload("res://assets/egret/common/log_itembg_png.tres") const PhaseClockTexture := preload("res://assets/egret/common/m_phase_clock_png.tres") const PhaseBarTexture := preload("res://assets/egret/common/m_phase_pro_bar_png.tres") const PhaseBarBgTexture := preload("res://assets/egret/common/m_phase_pro_bg_png.tres") const MenuPlantTexture := preload("res://assets/egret/menu/menu_p_plant.png") const MenuGainTexture := preload("res://assets/egret/menu/menu_p_gain.png") const MenuUprootTexture := preload("res://assets/egret/menu/menu_p_uproot.png") const MenuManureTexture := preload("res://assets/egret/menu/menu_p_manure.png") const MenuWaterTexture := preload("res://assets/egret/assets/game/menu/menu_p_put_water.png") const MenuDieBugTexture := preload("res://assets/egret/menu/menu_p_die_bug.png") const MenuDieGrassTexture := preload("res://assets/egret/menu/menu_p_die_grass.png") const MenuStealTexture := preload("res://assets/egret/assets/game/menu/menu_p_steal.png") const MenuPutBugTexture := preload("res://assets/egret/assets/game/menu/menu_p_put_bug.png") const MenuPutGrassTexture := preload("res://assets/egret/assets/game/menu/menu_p_put_grass.png") const PlantStateGrassTexture := preload("res://assets/egret/menu/plant_state1.png") const PlantStateBugTexture := preload("res://assets/egret/menu/plant_state2.png") const PlantStateDryTexture := preload("res://assets/egret/menu/plant_state3.png") const MainMenuHomeTexture := preload("res://assets/egret/menu2/menu2_m_home_png.tres") const MainMenuShopTexture := preload("res://assets/egret/menu2/menu2_m_shop_png.tres") const MainMenuWarehouseTexture := preload("res://assets/egret/menu2/menu2_m_warehouse_png.tres") const MainMenuFriendTexture := preload("res://assets/egret/menu2/menu2_m_rank_png.tres") const MarketIconTexture := preload("res://assets/replacement/menu2/market.png") var _owner: Control var _texture_provider var _callbacks: Dictionary = {} var _nodes: Dictionary = {} var _constants: Dictionary = {} var _modal_controller var _farm_viewport_controller var _farm_scene_root: Control var _pet_display: Control func setup(options: Dictionary) -> Dictionary: _owner = options.get("owner") as Control _texture_provider = options.get("texture_provider") _callbacks = options.get("callbacks", {}) _nodes = options.get("nodes", {}) _constants = options.get("constants", {}) var top_bar_view = _setup_top_bar_view() _setup_market_button() _setup_menu_title_labels() var main_menu_controller = _setup_main_menu() _modal_controller = _setup_modal_layer() var pet_modules := _setup_pet_display() var viewport_controller = _setup_farm_scene_root() var farm_modules := _setup_farm_interaction() var modules := { "top_bar_view": top_bar_view, "main_menu_controller": main_menu_controller, "modal_controller": _modal_controller, "modal_layer": _modal_controller.layer() if _modal_controller != null else null, "toast_label": _modal_controller.toast_label() if _modal_controller != null else null, "farm_viewport_controller": viewport_controller, "farm_scene_root": _farm_scene_root, } modules.merge(pet_modules, true) modules.merge(farm_modules, true) return modules func _setup_top_bar_view(): var top_bar_view = TopBarViewScript.new() top_bar_view.setup( { "owner": _owner, "top_layer": _node("top_layer"), "top_button_group": _node("top_button_group"), "top_hide": _node("top_hide"), "label_name": _node("label_name"), "label_level": _node("label_level"), "label_gold": _node("label_gold"), "label_diamond": _node("label_diamond"), "label_score": _node("label_score"), "label_exp": _node("label_exp"), "exp_fill": _node("exp_fill"), "avatar": _node("avatar"), "game_config": _constants.get("game_config"), }, _callback("style_label"), Callable(self, "_style_menu_title") ) return top_bar_view func _setup_main_menu(): var controller = MainMenuControllerScript.new() controller.setup( _owner, _node("main_menu"), _node("home_button_art"), MainMenuHomeTexture, { "shop": MainMenuShopTexture, "warehouse": MainMenuWarehouseTexture, "friend": MainMenuFriendTexture, }, Callable(self, "_style_menu_title"), _callback("hide_selection_ui"), Callable(self, "_on_main_menu_action_pressed") ) return controller func _setup_modal_layer(): var controller = ModalLayerControllerScript.new() controller.setup(_owner, _screen_size(), _callback("style_label")) return controller func _setup_pet_display() -> Dictionary: var pet_layer := Control.new() pet_layer.name = "PetLayer" pet_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE pet_layer.set_anchors_preset(Control.PRESET_FULL_RECT) _owner.add_child(pet_layer) _pet_display = Control.new() _pet_display.name = "BattlePetDisplay" _pet_display.position = Vector2(430.0, 482.0) _pet_display.size = Vector2(118.0, 128.0) _pet_display.pivot_offset = _pet_display.size * 0.5 _pet_display.visible = false pet_layer.add_child(_pet_display) var pet_panel_view = _callbacks.get("pet_panel_view", Callable()) if pet_panel_view is Callable and pet_panel_view.is_valid(): var panel_view = pet_panel_view.call() if panel_view != null: panel_view.set_display(_pet_display) return { "pet_layer": pet_layer, "pet_display": _pet_display, } func _setup_farm_scene_root(): _farm_viewport_controller = FarmViewportControllerScript.new() _farm_viewport_controller.setup(_owner, _screen_size(), float(_constants.get("scene_zoom_min", 0.6667)), float(_constants.get("scene_zoom_max", 1.3))) _farm_scene_root = _farm_viewport_controller.scene_root() return _farm_viewport_controller func _setup_farm_interaction() -> Dictionary: var game_view := _node("game_view") as Control var land_grid_view = LandGridViewScript.new() land_grid_view.setup({ "owner": _owner, "game_view": game_view, "game_config": _constants.get("game_config"), "crop_phase_rules": _constants.get("crop_phase_rules"), "get_land_node": _callback("get_land_node"), "render_add_land_indicator": _callback("render_add_land_indicator"), "land_count": int(_constants.get("land_count", 12)), "land_texture_size": _constants.get("land_texture_size", Vector2(172.0, 102.0)), "textures": _land_grid_textures(), }) var hit_layer := Control.new() hit_layer.name = "LandHitLayer" hit_layer.mouse_filter = Control.MOUSE_FILTER_IGNORE hit_layer.set_anchors_preset(Control.PRESET_FULL_RECT) game_view.add_child(hit_layer) var land_light := TextureRect.new() land_light.name = "LandLight" land_light.texture = LandLightTexture land_light.size = _constants.get("land_texture_size", Vector2(172.0, 102.0)) land_light.expand_mode = TextureRect.EXPAND_IGNORE_SIZE land_light.stretch_mode = TextureRect.STRETCH_SCALE land_light.mouse_filter = Control.MOUSE_FILTER_IGNORE land_light.visible = false game_view.add_child(land_light) var land_action_menu = LandActionMenuScript.new() land_action_menu.setup({ "owner": _owner, "get_land_node": _callback("get_land_node"), "on_action": _callback("cool_menu_pressed"), "on_dismiss": _callback("menu_dismiss_pressed"), "textures": _land_action_menu_textures(), "button_size": _constants.get("menu_button_size", Vector2(92.0, 88.0)), }) var long_press_timer := Timer.new() long_press_timer.name = "LandLongPressTimer" long_press_timer.one_shot = true long_press_timer.wait_time = 0.35 _owner.add_child(long_press_timer) var seed_bag_view = SeedBagViewScript.new() seed_bag_view.setup({ "owner": _owner, "style_label": _callback("style_label"), "get_item_icon_texture": _callback("get_item_icon_texture"), "seed_icon_id": _callback("seed_icon_id"), "on_item_pressed": _callback("bag_item_pressed"), "textures": _seed_bag_textures(), }) var bag_panel = seed_bag_view.panel() var bag_list: VBoxContainer = null if bag_panel != null: bag_list = bag_panel.get_node_or_null("SeedScroll/SeedList") as VBoxContainer var plant_tip_view = PlantTipViewScript.new() plant_tip_view.setup({ "owner": _owner, "game_config": _constants.get("game_config"), "crop_phase_rules": _constants.get("crop_phase_rules"), "get_land_node": _callback("get_land_node"), "style_label": _callback("style_label"), "format_duration": _callback("format_duration"), "phase_names": _constants.get("phase_names", []), "textures": _plant_tip_textures(), }) var plant_phase_timer := Timer.new() plant_phase_timer.name = "PlantPhaseRefreshTimer" plant_phase_timer.wait_time = float(_constants.get("plant_phase_refresh_seconds", 1.0)) plant_phase_timer.timeout.connect(_callback("plant_phase_timer_timeout")) _owner.add_child(plant_phase_timer) plant_phase_timer.start() var farm_interaction = FarmInteractionCoordinatorScript.new() farm_interaction.setup({ "owner": _owner, "hit_layer": hit_layer, "land_light": land_light, "land_action_menu": land_action_menu, "seed_bag_view": seed_bag_view, "plant_tip_view": plant_tip_view, "long_press_timer": long_press_timer, "constants": _farm_interaction_constants(), "callbacks": _farm_interaction_callbacks(), }) farm_interaction.setup_land_hit_areas() if _farm_viewport_controller != null and _farm_viewport_controller.has_method("set_pan_started_callback"): _farm_viewport_controller.set_pan_started_callback(Callable(farm_interaction, "on_scene_pan_started")) if _farm_viewport_controller != null and _farm_viewport_controller.has_method("set_pan_finished_callback"): _farm_viewport_controller.set_pan_finished_callback(Callable(farm_interaction, "on_scene_pan_finished")) var scene_hit_router = SceneHitRouterScript.new() scene_hit_router.setup({ "owner": _owner, "farm_scene_root": _farm_scene_root, "pet_display": _pet_display, "farm_targets": { "house_art": _node("house_art"), "dog_house_art": _node("dog_house_art"), "add_land_art": _node("add_land_art"), }, "callbacks": _scene_hit_callbacks(), }) return { "land_grid_view": land_grid_view, "plant_layer": land_grid_view.plant_layer(), "plant_state_layer": land_grid_view.plant_state_layer(), "hit_layer": hit_layer, "land_light": land_light, "land_action_menu": land_action_menu, "cool_menu_layer": land_action_menu.layer(), "long_press_timer": long_press_timer, "seed_bag_view": seed_bag_view, "bag_panel": bag_panel, "bag_list": bag_list, "plant_tip_view": plant_tip_view, "plant_tip": plant_tip_view.node(), "plant_phase_timer": plant_phase_timer, "farm_interaction_coordinator": farm_interaction, "scene_hit_router": scene_hit_router, "scene_hit_layer": scene_hit_router.scene_hit_layer(), "farm_hit_layer": scene_hit_router.farm_hit_layer(), "add_land_hit": scene_hit_router.add_land_hit(), } func _setup_menu_title_labels() -> void: _add_menu_title(_node("market_button_art"), "Market", Vector2(-15.0, 96.0), Vector2(148.0, 34.0), 24) _add_menu_title(_node("home_button_art"), "Home", Vector2(-5.0, 112.0), Vector2(165.0, 34.0), 25) _add_menu_title(_node_path("LeftMiddleMenu/Notice"), "News", Vector2(-8.0, 53.0), Vector2(102.0, 27.0), 21) _add_menu_title(_node_path("LeftMiddleMenu/Sign"), "Check-In", Vector2(-10.0, 52.0), Vector2(106.0, 28.0), 18) _add_menu_title(_node_path("LeftMiddleMenu/RewardGift"), "Gift", Vector2(-8.0, 51.0), Vector2(101.0, 28.0), 21) _add_menu_title(_node("exchange_gold_art"), "Exchange", Vector2(-10.0, 56.0), Vector2(115.0, 30.0), 18) _add_menu_title(_node_path("TopLayer/TopButtonGroup/TopLog"), "Log", Vector2(-8.0, 48.0), Vector2(90.0, 28.0), 21) _add_menu_title(_node_path("TopLayer/TopButtonGroup/TopLandup"), "Land Up", Vector2(-10.0, 53.0), Vector2(116.0, 30.0), 18) _add_menu_title(_node_path("TopLayer/TopButtonGroup/TopPay"), "Top Up", Vector2(-18.0, 53.0), Vector2(92.0, 30.0), 18) _add_menu_title(_node_path("TopLayer/TopButtonGroup/TopPayGift"), "Pack", Vector2(-8.0, 53.0), Vector2(101.0, 30.0), 21) _add_menu_title(_node_path("TopLayer/TopButtonGroup/TopLottery"), "Wheel", Vector2(-10.0, 54.0), Vector2(108.0, 30.0), 20) func _setup_market_button() -> void: var market_art := _node("market_button_art") as TextureRect if market_art == null: return market_art.texture = MarketIconTexture market_art.position = Vector2(30.0, 34.0) market_art.size = Vector2(118.0, 118.0) market_art.scale = Vector2.ONE market_art.pivot_offset = market_art.size * 0.5 market_art.expand_mode = TextureRect.EXPAND_FIT_WIDTH_PROPORTIONAL market_art.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED market_art.mouse_filter = Control.MOUSE_FILTER_IGNORE func _add_menu_title(target: Control, text: String, offset: Vector2, size: Vector2, font_size: int) -> void: if target == null or target.get_parent() == null: return if not target.visible: return var title_name := "%sTitle" % target.name var existing := target.get_parent().get_node_or_null(title_name) if existing != null: existing.queue_free() var label := Label.new() label.name = title_name label.text = text label.position = target.position + offset label.size = size label.mouse_filter = Control.MOUSE_FILTER_IGNORE label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER label.clip_text = false _style_menu_title(label, font_size) target.get_parent().add_child(label) func _style_menu_title(label: Label, size: int) -> void: if label == null: return label.add_theme_font_override("font", GameTitleFont) label.add_theme_font_size_override("font_size", size) label.add_theme_color_override("font_color", Color.WHITE) label.add_theme_constant_override("outline_size", 4) label.add_theme_color_override("font_outline_color", Color.html("#7a3a12")) label.add_theme_color_override("font_shadow_color", Color.html("#2e1708")) label.add_theme_constant_override("shadow_offset_x", 1) label.add_theme_constant_override("shadow_offset_y", 2) func _land_grid_textures() -> Dictionary: return { "level_0": LandLevel0Texture, "level_1": LandLevel1Texture, "level_2": LandLevel2Texture, "level_3": LandLevel3Texture, "locked": LockedLandTexture, "state_grass": PlantStateGrassTexture, "state_bug": PlantStateBugTexture, "state_dry": PlantStateDryTexture, } func _land_action_menu_textures() -> Dictionary: var textures := {} textures[str(_constants.get("action_plant", "plant"))] = MenuPlantTexture textures[str(_constants.get("action_gain", "gain"))] = MenuGainTexture textures[str(_constants.get("action_uproot", "uproot"))] = MenuUprootTexture textures[str(_constants.get("action_manure", "manure"))] = MenuManureTexture textures[str(_constants.get("action_water", "water"))] = MenuWaterTexture textures[str(_constants.get("action_die_bug", "die_bug"))] = MenuDieBugTexture textures[str(_constants.get("action_die_grass", "die_grass"))] = MenuDieGrassTexture textures[str(_constants.get("action_steal", "steal"))] = MenuStealTexture textures[str(_constants.get("action_put_bug", "put_bug"))] = MenuPutBugTexture textures[str(_constants.get("action_put_grass", "put_grass"))] = MenuPutGrassTexture return textures func _seed_bag_textures() -> Dictionary: return { "bg": BagBgTexture, "line": BagLineTexture, } func _plant_tip_textures() -> Dictionary: return { "bg": LogItemBgTexture, "clock": PhaseClockTexture, "bar_bg": PhaseBarBgTexture, "bar": PhaseBarTexture, } func _farm_interaction_constants() -> Dictionary: return { "land_count": int(_constants.get("land_count", 12)), "bag_mode_seed": str(_constants.get("bag_mode_seed", "seed")), "bag_mode_fertilizer": str(_constants.get("bag_mode_fertilizer", "fertilizer")), "action_plant": str(_constants.get("action_plant", "plant")), "action_gain": str(_constants.get("action_gain", "gain")), "action_uproot": str(_constants.get("action_uproot", "uproot")), "action_manure": str(_constants.get("action_manure", "manure")), "action_water": str(_constants.get("action_water", "water")), "action_die_bug": str(_constants.get("action_die_bug", "die_bug")), "action_die_grass": str(_constants.get("action_die_grass", "die_grass")), "action_steal": str(_constants.get("action_steal", "steal")), "action_put_bug": str(_constants.get("action_put_bug", "put_bug")), "action_put_grass": str(_constants.get("action_put_grass", "put_grass")), "disease_type_water": int(_constants.get("disease_type_water", 0)), "disease_type_bug": int(_constants.get("disease_type_bug", 1)), "disease_type_grass": int(_constants.get("disease_type_grass", 2)), } func _farm_interaction_callbacks() -> Dictionary: return { "get_land_node": _callback("get_land_node"), "land_data": _callback("land_data"), "land_actions": _callback("land_actions"), "is_visiting_friend": _callback("is_visiting_friend"), "is_operation_in_progress": _callback("is_operation_in_progress"), "open_land_extend_panel": _callback("open_land_extend_panel"), "apply_empty_button_style": Callable(UiMotionScript, "apply_empty_button_style"), "seed_items": _callback("seed_items"), "fertilizer_items": _callback("fertilizer_items"), "refresh_store_house": _callback("refresh_store_house"), "sow_seed": _callback("sow_seed"), "use_fertilizer": _callback("use_fertilizer"), "gather_crop": _callback("gather_crop"), "clear_land": _callback("clear_land"), "kick_disease": _callback("kick_disease"), "interact_friend_land": _callback("interact_friend_land"), } func _scene_hit_callbacks() -> Dictionary: return { "apply_empty_button_style": Callable(UiMotionScript, "apply_empty_button_style"), "keep_modal_layers_on_top": _callback("keep_modal_layers_on_top"), "open_log_panel": _callback("open_log_panel"), "open_land_upgrade_panel": _callback("open_land_upgrade_panel"), "open_pay_panel": _callback("open_pay_panel"), "open_level_gift_panel": _callback("open_level_gift_panel"), "open_lottery_panel": _callback("open_lottery_panel"), "toggle_top_button_group": _callback("toggle_top_button_group"), "open_market_panel": _callback("open_market_panel"), "open_notice_panel": _callback("open_notice_panel"), "open_sign_panel": _callback("open_sign_panel"), "open_online_gift_panel": _callback("open_online_gift_panel"), "open_gold_exchange_panel": _callback("open_gold_exchange_panel"), "open_house_upgrade_panel": _callback("open_house_upgrade_panel"), "open_land_extend_panel": _callback("open_land_extend_panel"), "open_pet_panel": _callback("open_pet_panel"), "open_player_info_panel": _callback("open_player_info_panel"), "back_to_own_farm": _callback("back_to_own_farm"), } func _on_main_menu_action_pressed(action: String) -> void: match action: "shop": _call("open_shop_panel") "warehouse": _call("open_warehouse_panel") "friend": _call("open_friend_panel") func _play_press_scale(target: Control) -> void: UiMotionScript.play_press_scale(_owner, target) func _play_release_scale(target: Control) -> void: UiMotionScript.play_release_scale(_owner, target) func _node(name: String) -> Variant: return _nodes.get(name) func _node_path(path: NodePath) -> Control: if _owner == null: return null return _owner.get_node_or_null(path) as Control func _screen_size() -> Vector2: return _constants.get("panel_screen_size", Vector2(640.0, 960.0)) func _callback(name: String) -> Callable: var callback: Callable = _callbacks.get(name, Callable()) return callback func _call(name: String, args: Array = []) -> Variant: var callback := _callback(name) if not callback.is_valid(): return null return callback.callv(args)