diff --git a/project.godot b/project.godot index 5f48923..ac04dd8 100644 --- a/project.godot +++ b/project.godot @@ -12,7 +12,7 @@ config_version=5 config/name="GFOLF 2" config/description="GFOLF: Combat Golf Action" -run/main_scene="res://src/world/world.tscn" +run/main_scene="res://src/game/game.tscn" config/features=PackedStringArray("4.3", "Forward Plus") run/max_fps=60 @@ -59,6 +59,7 @@ config/controls/camera/invert_pitch=false [global_group] WorldGroup="Singleton group for the active world instance, if any." +GameGroup="Singleton group for the active game instance, if any." [gui] @@ -163,6 +164,3 @@ jolt_3d/limits/max_temporary_memory=64 [rendering] textures/canvas_textures/default_texture_filter=0 -anti_aliasing/quality/msaa_3d=3 -anti_aliasing/quality/screen_space_aa=1 -anti_aliasing/quality/use_debanding=true diff --git a/src/game/game.gd b/src/game/game.gd new file mode 100644 index 0000000..c514d0b --- /dev/null +++ b/src/game/game.gd @@ -0,0 +1,98 @@ +class_name Game extends Node +## Wrapper for the game application + +@export var start_scene: String = "res://src/world/world.tscn" + +var _loading_resources := {} + +@onready var content: Node = %Content +@onready var loader_transition: AnimationPlayer = %LoaderTransition +@onready var loading_screen: Control = %LoadingScreen + +static var group := "GameGroup" # hey i'm group + + +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: + var instance: Node = (res as PackedScene).instantiate() + super.resolve(instance) + + +func _initial_load() -> void: + queue_scene(start_scene) + + +func _ready() -> void: + call_deferred("_initial_load") + + +## 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: + if not loading_screen.visible: + loading_screen.show() + loading_screen.modulate = Color.WHITE + + for child: Node in content.get_children(): + child.queue_free() + + return queue_load(path, ScenePromise.new()).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, promise: Promise = null) -> Promise: + if not promise: + promise = Promise.new() + _loading_resources[path] = promise + ResourceLoader.load_threaded_request(path) + return promise + + +func _finish_scene_load(instance: Node) -> void: + content.add_child(instance) + instance.reparent(content) + + +func _process(_delta: float) -> void: + if _loading_resources and not loading_screen.visible: + loader_transition.play("fade_in") + + for key: String in _loading_resources.keys(): + match ResourceLoader.load_threaded_get_status(key): + ResourceLoader.THREAD_LOAD_LOADED: + @warning_ignore("unsafe_cast") + (_loading_resources[key] as Promise).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 and loading_screen.visible: + loader_transition.play("fade_out") diff --git a/src/game/game.tscn b/src/game/game.tscn new file mode 100644 index 0000000..b2f8aac --- /dev/null +++ b/src/game/game.tscn @@ -0,0 +1,178 @@ +[gd_scene load_steps=7 format=3 uid="uid://cefit4bc8akbb"] + +[ext_resource type="Script" path="res://src/game/game.gd" id="1_4qa87"] +[ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="2_y3adf"] + +[sub_resource type="Animation" id="Animation_2c1ud"] +resource_name = "fade_in" +length = 0.4 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 2 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.4), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} + +[sub_resource type="Animation" id="Animation_c3dlb"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} + +[sub_resource type="Animation" id="Animation_xbqy2"] +resource_name = "fade_out" +length = 0.4 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.4), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [true, false] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 2 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.4), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_1s0w0"] +_data = { +"RESET": SubResource("Animation_c3dlb"), +"fade_in": SubResource("Animation_2c1ud"), +"fade_out": SubResource("Animation_xbqy2") +} + +[node name="Game" type="Node" groups=["GameGroup"]] +script = ExtResource("1_4qa87") + +[node name="Control" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="LoadingScreen" type="Control" parent="Control"] +unique_name_in_owner = true +z_index = 128 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 + +[node name="ColorRect" type="ColorRect" parent="Control/LoadingScreen"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0, 0, 0, 1) + +[node name="MarginContainer" type="MarginContainer" parent="Control/LoadingScreen"] +layout_mode = 1 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -1.0 +offset_top = -42.0 +grow_horizontal = 0 +grow_vertical = 0 +theme_override_constants/margin_right = 64 +theme_override_constants/margin_bottom = 32 + +[node name="RichTextLabel" type="RichTextLabel" parent="Control/LoadingScreen/MarginContainer"] +custom_minimum_size = Vector2(160, 0) +layout_mode = 2 +mouse_filter = 2 +theme_override_colors/default_color = Color(1, 0.75, 0.75, 1) +theme_override_fonts/normal_font = ExtResource("2_y3adf") +theme_override_font_sizes/normal_font_size = 32 +bbcode_enabled = true +text = "[wave]LOADING...[/wave]" +fit_content = true + +[node name="LoaderTransition" type="AnimationPlayer" parent="Control/LoadingScreen"] +unique_name_in_owner = true +libraries = { +"": SubResource("AnimationLibrary_1s0w0") +} + +[node name="ViewportContainer" type="SubViewportContainer" parent="Control"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +stretch = true + +[node name="Viewport" type="SubViewport" parent="Control/ViewportContainer"] +handle_input_locally = false +msaa_2d = 3 +msaa_3d = 3 +screen_space_aa = 1 +use_taa = true +size = Vector2i(1280, 720) +render_target_update_mode = 4 + +[node name="Content" type="Node" parent="Control/ViewportContainer/Viewport"] +unique_name_in_owner = true