extends Control ## Boot screen that handles update checking before launching the game ## On mobile: Shows update UI and handles in-game patching ## On desktop: Quick check, then proceeds (assumes launcher handles updates) @onready var status_label := %StatusLabel as Label @onready var progress_container := %ProgressContainer as VBoxContainer @onready var progress_bar := %ProgressBar as ProgressBar @onready var progress_label := %ProgressLabel as Label @onready var button_container := %ButtonContainer as HBoxContainer @onready var update_button := %UpdateButton as Button @onready var skip_button := %SkipButton as Button @onready var store_button := %StoreButton as Button @onready var version_label := $VersionLabel as Label var update_manager: Node var update_info: Dictionary = {} var main_scene_path := "res://scenes/main.tscn" # Your main game scene func _ready() -> void: # Get or create the update manager update_manager = _get_update_manager() # Connect signals update_manager.update_check_completed.connect(_on_update_check_completed) update_manager.update_check_failed.connect(_on_update_check_failed) update_manager.download_started.connect(_on_download_started) update_manager.download_progress.connect(_on_download_progress) update_manager.download_completed.connect(_on_download_completed) update_manager.download_failed.connect(_on_download_failed) update_manager.store_update_required.connect(_on_store_update_required) # Connect buttons update_button.pressed.connect(_on_update_pressed) skip_button.pressed.connect(_on_skip_pressed) store_button.pressed.connect(_on_store_pressed) # Show current version version_label.text = "v" + update_manager.current_version # Start update check after a brief delay await get_tree().create_timer(0.5).timeout _check_for_updates() func _get_update_manager() -> Node: # Try to get from autoload first if has_node("/root/GameUpdateManager"): return get_node("/root/GameUpdateManager") # Otherwise, create instance var manager_script := load("res://scripts/managers/game_update_manager.gd") var manager := manager_script.new() manager.name = "GameUpdateManager" get_tree().root.add_child(manager) return manager func _check_for_updates() -> void: status_label.text = "Checking for updates..." progress_container.visible = false button_container.visible = false # On desktop without launcher, skip update check and go straight to game if update_manager.is_desktop() and not update_manager._launcher_available(): # Quick check but don't block update_manager.check_for_updates() # Auto-proceed after short delay if on desktop await get_tree().create_timer(2.0).timeout if not update_info.get("has_update", false): _proceed_to_game() else: update_manager.check_for_updates() func _on_update_check_completed(has_update: bool, info: Dictionary) -> void: update_info = info version_label.text = "v" + info.current_version if has_update: if info.needs_store_update: status_label.text = "A required update is available.\nPlease update from the store." button_container.visible = true update_button.visible = false skip_button.visible = false store_button.visible = true elif info.can_patch: status_label.text = "Update available: v" + info.latest_version button_container.visible = true update_button.visible = true skip_button.visible = true store_button.visible = false else: # Desktop with launcher - just proceed status_label.text = "Update available via launcher" await get_tree().create_timer(1.5).timeout _proceed_to_game() else: status_label.text = "Game is up to date!" await get_tree().create_timer(1.0).timeout _proceed_to_game() func _on_update_check_failed(error: String) -> void: status_label.text = "Could not check for updates" print("[BootScreen] Update check failed: ", error) # Allow playing anyway await get_tree().create_timer(1.5).timeout _proceed_to_game() func _on_update_pressed() -> void: button_container.visible = false status_label.text = "Downloading update..." update_manager.download_update() func _on_skip_pressed() -> void: _proceed_to_game() func _on_store_pressed() -> void: update_manager.open_store_page() func _on_download_started(_total_size: int) -> void: progress_container.visible = true progress_bar.value = 0 progress_label.text = "0%" func _on_download_progress(_downloaded: int, _total: int, percentage: float) -> void: progress_bar.value = percentage progress_label.text = "%.0f%%" % percentage func _on_download_completed() -> void: status_label.text = "Update installed!" progress_container.visible = false # Reload the game to apply changes await get_tree().create_timer(1.0).timeout get_tree().reload_current_scene() func _on_download_failed(error: String) -> void: status_label.text = "Update failed: " + error progress_container.visible = false button_container.visible = true update_button.text = "Retry" func _on_store_update_required(store_url: String) -> void: status_label.text = "Please update from the store" button_container.visible = true update_button.visible = false skip_button.visible = false store_button.visible = true func _proceed_to_game() -> void: # Load any previously downloaded patches _load_existing_patches() # Change to main game scene get_tree().change_scene_to_file(main_scene_path) func _load_existing_patches() -> void: # Load any PCK files from the patches directory var patches_dir := "user://patches/" var dir := DirAccess.open(patches_dir) if not dir: return var patches: Array[String] = [] dir.list_dir_begin() var file := dir.get_next() while file != "": if file.ends_with(".pck"): patches.append(file) file = dir.get_next() dir.list_dir_end() # Sort patches by version (filename includes version) patches.sort() # Load each patch in order for patch in patches: var patch_path := patches_dir + patch if ProjectSettings.load_resource_pack(patch_path, true): print("[BootScreen] Loaded patch: ", patch) else: push_warning("[BootScreen] Failed to load patch: ", patch)