2025-03-07 19:26:12 -07:00
|
|
|
class_name Game extends Node
|
|
|
|
## Interface to the game as an application.
|
|
|
|
|
2025-04-21 18:07:11 -06:00
|
|
|
@export_file("*.tscn") var start_scene: String
|
|
|
|
|
|
|
|
var _loading_resources: Dictionary[String, Promise] = {}
|
|
|
|
|
|
|
|
@onready var content: Node = %Content
|
|
|
|
@onready var loading_screen: Control = %LoadingScreen
|
2025-04-19 16:18:12 -06:00
|
|
|
|
2025-03-07 19:26:12 -07:00
|
|
|
## Handy typed singleton access.
|
2025-03-22 19:16:52 -06:00
|
|
|
static var settings: GameSettingsType:
|
|
|
|
get():
|
|
|
|
return GameSettings
|
|
|
|
static var runtime: GameRuntimeType:
|
|
|
|
get():
|
|
|
|
return GameRuntime
|
2025-03-20 22:45:19 -06:00
|
|
|
|
2025-03-22 17:16:17 -06:00
|
|
|
## Global static access to Game singleton
|
|
|
|
static var instance: Game
|
|
|
|
|
|
|
|
|
2025-04-21 18:07:11 -06:00
|
|
|
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)
|
|
|
|
|
|
|
|
|
2025-03-22 17:16:17 -06:00
|
|
|
func _ready() -> void:
|
|
|
|
Game.instance = self
|
2025-04-21 18:07:11 -06:00
|
|
|
_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:
|
2025-04-22 22:38:06 -06:00
|
|
|
_unload_content()
|
2025-04-23 12:03:38 -06:00
|
|
|
return (
|
|
|
|
queue_load(path, ResourceLoader.CACHE_MODE_REUSE, ScenePromise.new(), "PackedScene")
|
|
|
|
. finally(_finish_scene_load)
|
|
|
|
)
|
2025-04-21 18:07:11 -06:00
|
|
|
|
|
|
|
|
|
|
|
## 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.
|
2025-04-23 12:03:38 -06:00
|
|
|
func queue_load(
|
|
|
|
path: String,
|
|
|
|
cache_mode: ResourceLoader.CacheMode = ResourceLoader.CACHE_MODE_REUSE,
|
|
|
|
promise: Promise = null,
|
|
|
|
type_hint: String = ""
|
|
|
|
) -> Promise:
|
2025-04-21 18:07:11 -06:00
|
|
|
if not promise:
|
|
|
|
promise = Promise.new()
|
|
|
|
_loading_resources[path] = promise
|
2025-04-23 12:03:38 -06:00
|
|
|
ResourceLoader.load_threaded_request(path, type_hint, false, cache_mode)
|
2025-04-21 18:07:11 -06:00
|
|
|
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
|