diff --git a/source/project.godot b/source/project.godot index cc6d037..6daf99e 100644 --- a/source/project.godot +++ b/source/project.godot @@ -62,6 +62,20 @@ folder_colors={ [input] +ui_accept={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194309,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194310,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":32,"physical_keycode":0,"key_label":0,"unicode":32,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":0,"pressure":0.0,"pressed":true,"script":null) +] +} +ui_cancel={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194305,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) +] +} move_forward={ "deadzone": 0.2, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null) diff --git a/source/src/core/interactive_loader.gd b/source/src/core/interactive_loader.gd index 2efb730..281e36a 100644 --- a/source/src/core/interactive_loader.gd +++ b/source/src/core/interactive_loader.gd @@ -1,6 +1,8 @@ class_name InteractiveLoader extends Node +@export var auto_free: bool = false + var load_progress: Array = [] @@ -26,4 +28,7 @@ func load_threaded(path: String) -> Resource: else: SPrint.print_msg("[Interactive Loader] is no longer in the scene tree. We can't wait for a process frame anymore, resulting in a freeze.", -1.0, SPrint.WARNING) + if auto_free: + queue_free() + return ResourceLoader.load_threaded_get(path) diff --git a/source/src/core/level/level_area.gd b/source/src/core/level/level_area.gd index 3620185..19e565e 100644 --- a/source/src/core/level/level_area.gd +++ b/source/src/core/level/level_area.gd @@ -10,6 +10,7 @@ func _enter_tree() -> void: for spawnpoint: PlayerSpawnPoint in loaded_if_spawnpoint: if GameGlobals.spawn_id == spawnpoint.spawn_id: var was_threaded: bool = level_loader.load_threaded + level_loader.load_threaded = false load_level() level_loader.load_threaded = was_threaded return @@ -34,14 +35,14 @@ func unload_level() -> void: if keep_loaded_duration > 0.0: var duration: float = keep_loaded_duration - while is_inside_tree() and duration > 0.0: + while duration > 0.0: if has_overlapping_bodies(): return duration -= get_process_delta_time() * float(not get_tree().paused) await get_tree().process_frame - if not is_instance_valid(get_tree()): + if not is_inside_tree() or not is_instance_valid(get_tree()): return level_loader.unload_level() diff --git a/source/src/core/level/level_loader.gd b/source/src/core/level/level_loader.gd index 7c5f9fb..ccbeb9c 100644 --- a/source/src/core/level/level_loader.gd +++ b/source/src/core/level/level_loader.gd @@ -5,22 +5,36 @@ extends Marker3D signal level_loaded signal level_unloaded +static var initial_level_reference: PackedScene + @export_file("*.tscn", "*.scn") var scene_path: String @export var load_threaded: bool = true @export_tool_button("Load Level", "Slot") var editor_load: Callable = load_level @export_tool_button("Unload Level", "Clear") var editor_unload: Callable = unload_level +@export_group("Level ID") +@export var override_level_id: bool = true +@export var level_id: StringName = &"" var level: Node +var is_loading: bool = false func load_level() -> void: + if is_loading: + return + + if not Engine.is_editor_hint() and override_level_id: + GameGlobals.set_level(level_id) + if is_instance_valid(level): - if level.scene_file_path == scene_path: + if _get_uid(level.scene_file_path) == _get_uid(scene_path): push_warning("Level already loaded") return unload_level() + is_loading = true + var scene: PackedScene if load_threaded: @@ -33,6 +47,8 @@ func load_level() -> void: level = scene.instantiate() add_child(level) + is_loading = false + initial_level_reference = null level_loaded.emit() @@ -45,11 +61,21 @@ func unload_level() -> void: if child.owner == null: child.queue_free() + initial_level_reference = null level_unloaded.emit() func _load_level_threaded() -> PackedScene: var interactive_loader := InteractiveLoader.new() + interactive_loader.auto_free = true add_child(interactive_loader) - return await interactive_loader.load_threaded(scene_path) + var resource: Resource = await interactive_loader.load_threaded(scene_path) + #interactive_loader.queue_free() + return resource + + +func _get_uid(path: String) -> int: + var uid: int = ResourceUID.text_to_id(ResourceUID.path_to_uid(path)) + #print("UID: ", uid, " Path: ", path) + return uid diff --git a/source/src/core/level/load_threshold_area.gd b/source/src/core/level/load_threshold_area.gd new file mode 100644 index 0000000..c29caa0 --- /dev/null +++ b/source/src/core/level/load_threshold_area.gd @@ -0,0 +1,31 @@ +class_name LevelThresholdArea +extends Area3D + +static var is_level_loading: bool = false + +@export var level_loaders: Array[LevelLoader] = [] + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + body_entered.connect(prevent_if_any_loading.unbind(1)) + + +func prevent_if_any_loading() -> bool: + for loader: LevelLoader in level_loaders: + if not loader.is_loading: + continue + + get_tree().paused = true + is_level_loading = true + SceneFader.set_loading_hints_visible(true) + + while loader.is_loading: + await get_tree().process_frame + + SceneFader.set_loading_hints_visible(false) + get_tree().paused = false + is_level_loading = false + return true + + return false diff --git a/source/src/core/level/load_threshold_area.gd.uid b/source/src/core/level/load_threshold_area.gd.uid new file mode 100644 index 0000000..e9bfa2e --- /dev/null +++ b/source/src/core/level/load_threshold_area.gd.uid @@ -0,0 +1 @@ +uid://dhrqjudaignuc diff --git a/source/src/game.gd b/source/src/game.gd index 5cfd3fd..ed5b534 100644 --- a/source/src/game.gd +++ b/source/src/game.gd @@ -3,6 +3,8 @@ extends Node const PLAYER: PackedScene = preload(GameGlobals.PLAYER_PATH) +@export_group("Debug", "debug_") +@export_custom(PROPERTY_HINT_GROUP_ENABLE, "checkbox_only") var debug_enabled: bool = false @export var debug_spawn_point: StringName = &"" @export_range(-1, 0, 1, "or_greater") var debug_chapter_idx: int = -1 @@ -10,14 +12,17 @@ var player_character: PlayerCharacter func _init() -> void: - if OS.has_feature("editor") and not debug_spawn_point.is_empty(): - GameGlobals.set_spawn_id(debug_spawn_point) + GameGlobals.game = self + + +func _enter_tree() -> void: + if OS.has_feature("editor") and debug_enabled: + if not debug_spawn_point.is_empty(): + GameGlobals.set_spawn_id(debug_spawn_point) if debug_chapter_idx >= 0: GameGlobals.set_chapter_index(debug_chapter_idx) - GameGlobals.game = self - # Called when the node enters the scene tree for the first time. func _ready() -> void: diff --git a/source/src/globals/autoloads/game_globals.gd b/source/src/globals/autoloads/game_globals.gd index d9a4652..197e374 100644 --- a/source/src/globals/autoloads/game_globals.gd +++ b/source/src/globals/autoloads/game_globals.gd @@ -3,15 +3,21 @@ extends Node signal player_died(death_type: DeathTypes) const MAIN_MENU_PATH: String = "uid://dwd8wf02j2epn" +const INITIAL_LEVEL_ID: StringName = &"OutsideArea" const GAME_PATH: String = "uid://s7cw6ulb7kh7" const PLAYER_PATH: String = "uid://clhy3kiceqf2o" const CHAPTER_LIST: ChapterList = preload("uid://d2mcwotliowv7") +const LEVELS: Dictionary[StringName, String] = { + &"OutsideArea": "uid://lraild3yetsh", + &"EntranceHall": "uid://cmdy6f6kesmbj", +} enum DeathTypes { SAW, } var game: Game +var level: StringName = &"" var player: PlayerCharacter var player_alive: bool = true var spawn_id: StringName = &"": set = set_spawn_id @@ -26,6 +32,7 @@ func get_player() -> PlayerCharacter: func reset_game() -> void: player_alive = true spawn_id = &"" + level = INITIAL_LEVEL_ID chapter_index = 0 in_cutscene = false @@ -36,6 +43,7 @@ func load_from_save() -> void: var data: Dictionary[StringName, Variant] = SaveManager.persistent_data spawn_id = data.get(&"spawn_id") chapter_index = data.get(&"chapter_index") + level = data.get(&"level", &"") func set_spawn_id(id: StringName) -> void: @@ -50,6 +58,12 @@ func set_chapter_index(index: int) -> void: SaveManager.persistent_data[&"chapter_index"] = chapter_index +func set_level(level_id: StringName) -> void: + level = level_id + + SaveManager.persistent_data[&"level"] = level + + func kill_player(death_type: DeathTypes) -> void: if not player_alive: return diff --git a/source/src/globals/autoloads/save_manager.gd b/source/src/globals/autoloads/save_manager.gd index 1b06d7f..e373ed8 100644 --- a/source/src/globals/autoloads/save_manager.gd +++ b/source/src/globals/autoloads/save_manager.gd @@ -8,6 +8,7 @@ var persistent_data: Dictionary[StringName, Variant] = { &"spawn_id": &"", &"sequence_index": 0, &"chapter_index": 0, + &"level": &"", &"level_data": { } diff --git a/source/src/globals/autoloads/scene_fader/scene_fader.gd b/source/src/globals/autoloads/scene_fader/scene_fader.gd index cfcad48..11c48c1 100644 --- a/source/src/globals/autoloads/scene_fader/scene_fader.gd +++ b/source/src/globals/autoloads/scene_fader/scene_fader.gd @@ -21,17 +21,24 @@ func _ready() -> void: color_rect.color.a = 0.0 -func load_to_path(path: String) -> void: +func load_to_path(path: String, initial_level: String = "") -> void: if is_loading: return is_loading = true await fade_in() - await get_tree().create_timer(1.0).timeout + #await get_tree().create_timer(1.0).timeout + + # Load the initial level into memory first, so it can be retrived by other things later. + if not initial_level.is_empty(): + LevelLoader.initial_level_reference = await interactive_loader.load_threaded(initial_level) + + print("Initial Level Reference Loaded: ", LevelLoader.initial_level_reference) var scene: PackedScene = await interactive_loader.load_threaded(path) + print("Scene Loaded: ", scene) assert(is_instance_valid(scene), "Scene is invalid.") get_tree().change_scene_to_packed(scene) diff --git a/source/src/levels/expo_combined.tscn b/source/src/levels/expo_combined.tscn index 07dfbfe..e517ce1 100644 --- a/source/src/levels/expo_combined.tscn +++ b/source/src/levels/expo_combined.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=8 format=3 uid="uid://dspysc2bld6eu"] +[gd_scene load_steps=10 format=3 uid="uid://dspysc2bld6eu"] [ext_resource type="Script" uid="uid://dfnb036hysorj" path="res://src/core/level/level_loader.gd" id="1_kntr3"] [ext_resource type="PackedScene" uid="uid://drr80goa61wrx" path="res://src/core/level/level_area.tscn" id="2_5mqkb"] [ext_resource type="Script" uid="uid://3hlvt5k34xva" path="res://src/core/player_spawn_point.gd" id="3_aoi14"] +[ext_resource type="Script" uid="uid://dhrqjudaignuc" path="res://src/core/level/load_threshold_area.gd" id="3_mcc85"] [ext_resource type="Script" uid="uid://dgsfc4i6bovwa" path="res://src/core/chapter/chapter_area.gd" id="4_sujqt"] [sub_resource type="BoxShape3D" id="BoxShape3D_aoi14"] @@ -11,6 +12,9 @@ size = Vector3(17, 9, 36) [sub_resource type="BoxShape3D" id="BoxShape3D_5mqkb"] size = Vector3(31, 19, 37) +[sub_resource type="BoxShape3D" id="BoxShape3D_mcc85"] +size = Vector3(4, 3, 0.5) + [sub_resource type="BoxShape3D" id="BoxShape3D_sujqt"] size = Vector3(4, 3, 1) @@ -21,12 +25,14 @@ size = Vector3(4, 3, 1) [node name="OutsideArea" type="Marker3D" parent="LevelLoaders"] script = ExtResource("1_kntr3") scene_path = "uid://lraild3yetsh" +level_id = &"OutsideArea" metadata/_custom_type_script = "uid://dfnb036hysorj" [node name="EntranceHall" type="Marker3D" parent="LevelLoaders"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.3, 3.5, 33) script = ExtResource("1_kntr3") scene_path = "uid://cmdy6f6kesmbj" +level_id = &"EntranceHall" metadata/_custom_type_script = "uid://dfnb036hysorj" [node name="LevelAreas" type="Node" parent="."] @@ -49,6 +55,25 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 9, 53) shape = SubResource("BoxShape3D_5mqkb") debug_color = Color(1, 1, 0, 0.41960785) +[node name="LevelLoadThresholds" type="Node" parent="."] + +[node name="LevelThresholdArea" type="Area3D" parent="LevelLoadThresholds" node_paths=PackedStringArray("level_loaders")] +collision_layer = 0 +collision_mask = 2 +script = ExtResource("3_mcc85") +level_loaders = [NodePath("../../LevelLoaders/OutsideArea"), NodePath("../../LevelLoaders/EntranceHall")] +metadata/_custom_type_script = "uid://dhrqjudaignuc" + +[node name="CollisionShape" type="CollisionShape3D" parent="LevelLoadThresholds/LevelThresholdArea"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.8, 5, 36.2) +shape = SubResource("BoxShape3D_mcc85") +debug_color = Color(1, 0.5176471, 0, 0.41960785) + +[node name="CollisionShape2" type="CollisionShape3D" parent="LevelLoadThresholds/LevelThresholdArea"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.8, 5, 33) +shape = SubResource("BoxShape3D_mcc85") +debug_color = Color(1, 0.5176471, 0, 0.41960785) + [node name="Spawnpoints" type="Node" parent="."] [node name="OutsideSpawnPoint" type="Marker3D" parent="Spawnpoints"] diff --git a/source/src/ui/menus/main_menu/main_menu.gd b/source/src/ui/menus/main_menu/main_menu.gd index e6cf407..f6c066c 100644 --- a/source/src/ui/menus/main_menu/main_menu.gd +++ b/source/src/ui/menus/main_menu/main_menu.gd @@ -12,6 +12,7 @@ const AMBIENCE_STREAM: AudioStream = preload("uid://cyxvsy5wjxhl7") func _ready() -> void: continue_button.disabled = not SaveManager.save_exists() + (continue_button if not continue_button.disabled else new_game_button).grab_focus() AudioManager.play_audio(AMBIENCE_STREAM, AudioManager.AMBIENCE) @@ -24,7 +25,7 @@ func _unhandled_input(event: InputEvent) -> void: func load_game() -> void: AudioManager.fade_audio(AMBIENCE_STREAM, -80.0, SceneFader.fade_in_duration, AudioManager.AMBIENCE, true) - SceneFader.load_to_path(GameGlobals.GAME_PATH) + SceneFader.load_to_path(GameGlobals.GAME_PATH, GameGlobals.LEVELS.get(GameGlobals.level, &"")) func _on_new_game_button_pressed() -> void: diff --git a/source/src/ui/menus/pause_menu/pause_menu.gd b/source/src/ui/menus/pause_menu/pause_menu.gd index 776d585..72115da 100644 --- a/source/src/ui/menus/pause_menu/pause_menu.gd +++ b/source/src/ui/menus/pause_menu/pause_menu.gd @@ -10,6 +10,9 @@ var can_pause: bool = true func _input(event: InputEvent) -> void: + if LevelThresholdArea.is_level_loading: + return + if event.is_action_pressed(ACTION_PAUSE): if options_menu.is_visible_in_tree(): options_menu.close_menu()