class_name Game extends Node ## Interface to the game as an application. @export_file("*.tscn") var start_scene: String var _loading_resources: Dictionary[String, Promise] = {} @onready var content: Node = %Content @onready var loading_screen: Control = %LoadingScreen ## Handy typed singleton access. static var settings: GameSettingsType: get(): return GameSettings static var runtime: GameRuntimeType: get(): return GameRuntime ## Global static access to Game singleton static var instance: Game class Promise: var _callbacks: Array[Callable] = [] var _end_callbacks: Array[Callable] = [] func then(fn: Callable) -> Promise: _callbacks.push_back(fn) return self func finally(fn: Callable) -> Promise: _end_callbacks.push_back(fn) return self func resolve(res: Variant) -> void: for fn: Callable in _callbacks + _end_callbacks: fn.call(res) class ScenePromise: extends Promise func resolve(res: Variant) -> void: @warning_ignore("unsafe_cast") var instance: Node = (res as PackedScene).instantiate() super.resolve(instance) func _ready() -> void: Game.instance = self _initial_load.call_deferred() func _initial_load() -> void: queue_scene(start_scene) ## Unload the running scene & queue up a new scene to be loaded in the background. ## ## The loading screen will be shown until the scene is loaded. func queue_scene(path: String) -> Promise: _unload_content() return ( queue_load(path, ResourceLoader.CACHE_MODE_REUSE, ScenePromise.new(), "PackedScene") . finally(_finish_scene_load) ) ## Queue a resource to be loaded in the background. ## ## Returns a `Promise` which can be used to attach callbacks ## which will be called with the resource after it is loaded. func queue_load( path: String, cache_mode: ResourceLoader.CacheMode = ResourceLoader.CACHE_MODE_REUSE, promise: Promise = null, type_hint: String = "" ) -> Promise: if not promise: promise = Promise.new() _loading_resources[path] = promise ResourceLoader.load_threaded_request(path, type_hint, false, cache_mode) return promise func _unload_content() -> void: for child: Node in content.get_children(): child.queue_free() func _finish_scene_load(scene_instance: Node) -> void: # Unpause in case the previous scene was paused. get_tree().paused = false # Reset time scale in case it's been changed. Engine.time_scale = 1.0 content.add_child(scene_instance) scene_instance.reparent(content) func _process(_delta: float) -> void: if _loading_resources: loading_screen.visible = true for key: String in _loading_resources.keys(): match ResourceLoader.load_threaded_get_status(key): ResourceLoader.THREAD_LOAD_LOADED: _loading_resources[key].resolve(ResourceLoader.load_threaded_get(key)) _loading_resources.erase(key) ResourceLoader.THREAD_LOAD_FAILED: assert(false, "Failed loading resource: " + key) ResourceLoader.THREAD_LOAD_INVALID_RESOURCE: assert(false, "Can't load invalid resource: " + key) _: # Continue loading pass if not _loading_resources: loading_screen.visible = false