grunk/src/game/game.gd

120 lines
3.0 KiB
GDScript

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