diff --git a/game/assets/action_hints/ui_accept.tres b/game/assets/action_hints/ui_accept.tres new file mode 100644 index 0000000..90b4476 --- /dev/null +++ b/game/assets/action_hints/ui_accept.tres @@ -0,0 +1,10 @@ +[gd_resource type="Texture2D" script_class="ControllerIconTexture" format=3 uid="uid://bi8jxa3gcbfdn"] + +[ext_resource type="Script" uid="uid://ch00l1e1rteyw" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="1_lwov1"] + +[resource] +resource_local_to_scene = false +resource_name = "" +script = ExtResource("1_lwov1") +path = "ui_accept" +metadata/_custom_type_script = "uid://ch00l1e1rteyw" diff --git a/game/assets/action_hints/ui_cancel.tres b/game/assets/action_hints/ui_cancel.tres new file mode 100644 index 0000000..8165049 --- /dev/null +++ b/game/assets/action_hints/ui_cancel.tres @@ -0,0 +1,10 @@ +[gd_resource type="Texture2D" script_class="ControllerIconTexture" format=3 uid="uid://cjtlrpqb75elt"] + +[ext_resource type="Script" uid="uid://ch00l1e1rteyw" path="res://addons/controller_icons/objects/ControllerIconTexture.gd" id="1_11853"] + +[resource] +resource_local_to_scene = false +resource_name = "" +script = ExtResource("1_11853") +path = "ui_cancel" +metadata/_custom_type_script = "uid://ch00l1e1rteyw" diff --git a/game/default_bus_layout.tres b/game/default_bus_layout.tres new file mode 100644 index 0000000..22c7ef6 --- /dev/null +++ b/game/default_bus_layout.tres @@ -0,0 +1,22 @@ +[gd_resource type="AudioBusLayout" format=3 uid="uid://jka2h4facmsp"] + +[resource] +bus/0/volume_db = -4.002995 +bus/1/name = &"SFX" +bus/1/solo = false +bus/1/mute = false +bus/1/bypass_fx = false +bus/1/volume_db = 0.0 +bus/1/send = &"Master" +bus/2/name = &"Music" +bus/2/solo = false +bus/2/mute = false +bus/2/bypass_fx = false +bus/2/volume_db = 0.0 +bus/2/send = &"Master" +bus/3/name = &"Ambient" +bus/3/solo = false +bus/3/mute = false +bus/3/bypass_fx = false +bus/3/volume_db = 0.0 +bus/3/send = &"Master" diff --git a/game/localization/localization.csv b/game/localization/localization.csv index 459d470..614cc89 100644 --- a/game/localization/localization.csv +++ b/game/localization/localization.csv @@ -1,27 +1,40 @@ -KEYS,en,de,es,ja +KEYS,?plural,en,de,es,ja # UI -CONTINUE,Continue,Weiterspielen,Continue,Continue -LOAD,Load,Laden,Load,Load -NEW_GAME,New Game,Neues Spiel,New Game,New Game -SETTINGS,Settings,Einstellungen,Settings,Settings -QUIT,Quit,Beenden,Quit,Quit -LOADING,[loading]Loading...[/loading],[loading]Laden...[/loading],[loading]Loading...[/loading],[loading]Loading...[/loading] +CONTINUE,,Continue,Weiterspielen,Continue,Continue +LOAD,,Load,Laden,Load,Load +NEW_GAME,,New Game,Neues Spiel,New Game,New Game +SETTINGS,,Settings,Einstellungen,Settings,Settings +QUIT,,Quit,Beenden,Quit,Quit +LOADING,,[loading]Loading...[/loading],[loading]Laden...[/loading],[loading]Loading...[/loading],[loading]Loading...[/loading] +BACK,,Back,Zurück,Back,Back # Options/Settings -AUDIO,Audio,Audio,Audio,Audio -Master,Master,Master,Master,Master -SFX,SFX,SFX,SFX,SFX -MUSIC,Music,Musik,Music,Music -AMBIENT,Ambient,Ambiente,Ambient,Ambient -GRAPHICS,Graphics,Grafik,Graphics,Graphics -FULLSCREEN,Fullscreen,Vollbild,Fullscreen,Fullscreen -Misc,Misc,Misc,Misc,Misc -HEADBOBBING,Headbobbing,Kopfwackeln,Headbobbing,Headbobbing -HEADBOBBING_MULTIPLIER,Headbobbing Multiplier,Wackel-Multiplikator,Headbobbing Multiplier, Headbobbing Multiplier +AUDIO,,Audio,Audio,Audio,Audio +MASTER,,Master,Master,Master,Master +SFX,,SFX,SFX,SFX,SFX +MUSIC,,Music,Musik,Music,Music +AMBIENT,,Ambience,Ambiente,Ambience,Ambience +GRAPHICS,,Graphics,Grafik,Graphics,Graphics +FULLSCREEN,,Fullscreen,Vollbild,Fullscreen,Fullscreen +MISC,,Misc,Misc,Misc,Misc +HEADBOBBING,,Headbobbing,Kopfwackeln,Headbobbing,Headbobbing +HEADBOBBING_MULTIPLIER,,Headbobbing Multiplier,Wackel-Multiplikator,Headbobbing Multiplier,Headbobbing Multiplier + +WINDOW_FULLSCREEN,,Fullscreen,Vollbild,Fullscreen,Fullscreen +WINDOW_EXCLUSIVE_FULLSCREEN,,Exclusive Fullscreen,Exklusiv-Vollbild,Exclusive Fullscreen,Exclusive Fullscreen +WINDOW_WINDOWED,,Windowed,Fenstermodus,Windowed,Windowed + +SETTINGS_CONFIRMATION_TITLE,,Apply Changes?,Änderungen Anwenden?,Apply Changes?,Apply Changes?,Apply Changes? +SETTINGS_APPLY,,Apply,Anwenden,Apply,Apply,Apply +SETTINGS_REVERT,,Revert,Zurücksetzen,Revert,Revert,Revert + +?pluralrule,,nplurals=2; plural=(n != 1);,nplurals=2; plural=(n != 1);,nplurals=2; plural=(n != 1);,nplurals=2; plural=(n != 1); +SETTINGS_REVERT_TIME_LEFT,.,Reverting in %s second...,Wird in %s sekunde zurückgesetzt...,Reverting in %s second...,Reverting in %s second... +,,Reverting in %s seconds...,Wird in %s sekunden zurückgesetzt...,Reverting in %s seconds...,Reverting in %s seconds... # Death Types -DEATH_LABEL_SAW,Killed by a saw,Von einer Säge getötet,Killed by a saw,Killed by a saw -DEATH_MSG_SAW,Better watch your step next time...,,, -DEATH_LABEL_DOOM_FALL,Doomed to fall,Zum Fallen verdammt,, -DEATH_MSG_DOOM_FALL,"Yes, you can fall off the map now...","Ja, du kannst jetzt von der map fallen...",, +DEATH_LABEL_SAW,,Killed by a saw,Von einer Säge getötet,Killed by a saw,Killed by a saw +DEATH_MSG_SAW,,Better watch your step next time...,,, +DEATH_LABEL_DOOM_FALL,,Doomed to fall,Zum Fallen verdammt,, +DEATH_MSG_DOOM_FALL,,"Yes, you can fall off the map now...","Ja, du kannst jetzt von der map fallen...",, diff --git a/game/localization/localization.de.translation b/game/localization/localization.de.translation index 68dace7..8b9ce7d 100644 Binary files a/game/localization/localization.de.translation and b/game/localization/localization.de.translation differ diff --git a/game/localization/localization.en.translation b/game/localization/localization.en.translation index e660622..bc0e4f5 100644 Binary files a/game/localization/localization.en.translation and b/game/localization/localization.en.translation differ diff --git a/game/localization/localization.es.translation b/game/localization/localization.es.translation index e7d47b6..0066fc1 100644 Binary files a/game/localization/localization.es.translation and b/game/localization/localization.es.translation differ diff --git a/game/localization/localization.ja.translation b/game/localization/localization.ja.translation index cd45e8a..49b8783 100644 Binary files a/game/localization/localization.ja.translation and b/game/localization/localization.ja.translation differ diff --git a/game/project.godot b/game/project.godot index f07873e..0de89a2 100644 --- a/game/project.godot +++ b/game/project.godot @@ -16,6 +16,10 @@ run/main_scene="uid://ga0d5817hbc8" config/features=PackedStringArray("4.6", "Forward Plus") config/icon="res://godot_icon.svg" +[audio] + +buses/default_bus_layout="uid://jka2h4facmsp" + [autoload] SPrint="*uid://cpfunq8fyixco" @@ -24,6 +28,7 @@ InputManager="*uid://cocp0vmvgd4ln" ControllerIcons="*uid://bdosbfkp568je" ShaderGlobals="*uid://d2lr860r1ysrm" LimboConsole="*uid://dyxornv8vwibg" +SettingsHandler="*uid://dj1t6rc6fpiti" [debug] diff --git a/game/src/core/autoloads/settings_handler.gd b/game/src/core/autoloads/settings_handler.gd new file mode 100644 index 0000000..5b57b99 --- /dev/null +++ b/game/src/core/autoloads/settings_handler.gd @@ -0,0 +1,101 @@ +extends Node + +signal settings_changed + +const CONFIG_FILE_PATH: String = "user://settings.ini" + +var settings: Dictionary[String, Dictionary] = { + "audio": { + "user_offset_ms": 0.0, + "Master": 0.85, + "Music": 1.0, + "SFX": 1.0, + "Ambient": 1.0, + }, + "video": { + "window_mode": DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN, + }, + "gameplay": { + "headbobbing": true, + "headbobbing_multiplier": 1.0, + }, + "localization": { + "language": "en_US", + }, + "controls": { + "controller_vibration_min_range": 0.0, + }, +} + +var _previous_settings: Dictionary[String, Dictionary] = settings + + +# Called when the node enters the scene tree for the first time. +func _ready() -> void: + load_from_file() + commit_settings() + apply_settings() + save_settings() + + +func load_from_file() -> void: + var config := ConfigFile.new() + if config.load(CONFIG_FILE_PATH) != OK: + return + + for section: String in config.get_sections(): + if not settings.has(section): + continue + + for key: String in config.get_section_keys(section): + var default: Variant = get_setting(section, key) + set_setting(section, key, config.get_value(section, key, default), false) + + settings_changed.emit() + + +func apply_settings() -> void: + # Audio + RhythmPlayer.user_offset_ms = get_setting("audio", "user_offset_ms", 0.0) + AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"Master"), get_setting("audio", "Master", 0.85)) + AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"SFX"), get_setting("audio", "SFX", 1.0)) + AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"Music"), get_setting("audio", "Music", 1.0)) + AudioServer.set_bus_volume_linear(AudioServer.get_bus_index(&"Ambient"), get_setting("audio", "Ambient", 1.0)) + # Localization + TranslationServer.set_locale(get_setting("localization", "language", "en_US")) + # Video + if not Engine.is_embedded_in_editor(): + DisplayServer.window_set_mode(get_setting("video", "window_mode", DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN)) + + +func commit_settings() -> void: + _previous_settings = settings.duplicate(true) + + +func save_settings() -> void: + var config: ConfigFile = Utils.load_config(CONFIG_FILE_PATH) + + for section: String in settings.keys(): + for key: String in settings.get(section, {}).keys(): + config.set_value(section, key, settings[section][key]) + + config.save(CONFIG_FILE_PATH) + + +func revert_settings() -> void: + settings = _previous_settings.duplicate(true) + apply_settings() + + +func set_setting(section: String, key: String, value: Variant, do_save: bool = true) -> void: + settings[section][key] = value + + if do_save: + apply_settings() + commit_settings() + save_settings() + settings_changed.emit() + + +func get_setting(section: String, key: String, default: Variant = null) -> Variant: + return settings.get(section, {}).get(key, default) diff --git a/game/src/core/autoloads/settings_handler.gd.uid b/game/src/core/autoloads/settings_handler.gd.uid new file mode 100644 index 0000000..66771ec --- /dev/null +++ b/game/src/core/autoloads/settings_handler.gd.uid @@ -0,0 +1 @@ +uid://dj1t6rc6fpiti diff --git a/game/src/core/camera/effects/headbobbing_effect.gd b/game/src/core/camera/effects/headbobbing_effect.gd index f63000d..a732645 100644 --- a/game/src/core/camera/effects/headbobbing_effect.gd +++ b/game/src/core/camera/effects/headbobbing_effect.gd @@ -12,14 +12,13 @@ var time: float = 0.0 var headbobbing_multiplier: float = 1.0 # User setting. -#func _ready() -> void: - #var _update_settings: Callable = func() -> void: - #effect_enabled = SettingsManager.get_setting(&"misc", &"headbobbing") - #headbobbing_multiplier = SettingsManager.get_setting(&"misc", &"headbobbing_multiplier") -# - #SettingsManager.settings_changed.connect(_update_settings) - #ProjectSettings.settings_changed.connect(_update_settings) - #_update_settings.call() +func _ready() -> void: + var _update_settings: Callable = func() -> void: + effect_enabled = SettingsHandler.get_setting("gameplay", "headbobbing", true) + headbobbing_multiplier = SettingsHandler.get_setting("gameplay", "headbobbing_multiplier", 1.0) + + SettingsHandler.settings_changed.connect(_update_settings) + _update_settings.call() func _process_effect(delta: float) -> void: diff --git a/game/src/core/utils.gd b/game/src/core/utils.gd index af2bbaa..4e20b8f 100644 --- a/game/src/core/utils.gd +++ b/game/src/core/utils.gd @@ -141,3 +141,8 @@ static func create_box_mesh_from_aabb(aabb: AABB) -> BoxMesh: mesh.size = aabb.size return mesh + +static func load_config(config_path: String) -> ConfigFile: + var config := ConfigFile.new() + config.load(config_path) + return config diff --git a/game/src/core/vibration_component.gd b/game/src/core/vibration_component.gd index ae490f3..7ded753 100644 --- a/game/src/core/vibration_component.gd +++ b/game/src/core/vibration_component.gd @@ -10,6 +10,9 @@ extends Node #static var total_weak_vibration: float = 0.0 #static var total_strong_vibration: float = 0.0 #static var _vibration_handled: bool = false +static var min_weak_magnitude_threshold: float = 0.0 +static var min_strong_magnitude_threshold: float = 0.0 +static var min_duration_threshold: float = 0.0 ## If [code]false[/code], calling [method vibrate] will not start a controller vibration. @export var enabled: bool = true: @@ -31,7 +34,6 @@ extends Node #region Editor tooling @warning_ignore_start("unused_private_class_variable") -@export var _editor_weak_vibration: bool = false @export_tool_button("Test Vibration", "InputEventJoypadMotion") var _editor_test_vibration: Callable = vibrate @export_tool_button("Stop Vibration", "MissingNode") @@ -55,25 +57,12 @@ var _editor_stop_test_vibration: Callable = stop_vibration get = get_magnitude_multiplier, set = set_magnitude_multiplier -@export_group("Weak Controller Overrides", "weak_controller_") -@export var weak_controller_magnitude_multiplier: float = 2.0 -@export var weak_controller_duration_multiplier: float = 1.0 - var animated_weak_magnitude: float = 0.0: set(value): animated_weak_magnitude = clampf(value, 0.0, 1.0) var animated_strong_magnitude: float = 0.0: set(value): animated_strong_magnitude = clampf(value, 0.0, 1.0) -var _ed_was_weak_vibration: bool = false - - -func _notification(what: int) -> void: - if what == NOTIFICATION_EDITOR_PRE_SAVE: - _ed_was_weak_vibration = _editor_weak_vibration - _editor_weak_vibration = false - elif what == NOTIFICATION_EDITOR_POST_SAVE: - _editor_weak_vibration = _ed_was_weak_vibration func _process(delta: float) -> void: @@ -81,9 +70,13 @@ func _process(delta: float) -> void: can_vibrate() and not is_zero_approx(animated_weak_magnitude + animated_strong_magnitude) ): - Input.start_joy_vibration.call_deferred( - device, animated_weak_magnitude, animated_strong_magnitude, delta - ) + var _weak_magnitude: float = animated_weak_magnitude + var _strong_magnitude: float = animated_strong_magnitude + + _weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0) + _strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0) + + Input.start_joy_vibration.call_deferred(device, _weak_magnitude, _strong_magnitude, delta) animated_weak_magnitude = 0.0 animated_strong_magnitude = 0.0 @@ -94,13 +87,6 @@ func vibrate() -> void: if not can_vibrate(): return - var is_weak_controller: bool - - #if Engine.is_editor_hint(): - #is_weak_controller = _editor_weak_vibration - #else: - #is_weak_controller = SettingsManager.get_setting(&"controls", &"using_weak_controller") - if delay > 0.0: await get_tree().create_timer(delay).timeout @@ -110,16 +96,14 @@ func vibrate() -> void: #SPrint.print_msg("Audio Delay: %s" % (last_mix_time + output_latency)) await get_tree().create_timer(last_mix_time + output_latency).timeout - var _multiplier: float = magnitude_multiplier * ( - weak_controller_magnitude_multiplier if is_weak_controller else 1.0 - ) + var _weak_magnitude: float = clampf(weak_magnitude * magnitude_multiplier, 0.0, 1.0) + var _strong_magnitude: float = clampf(strong_magnitude * magnitude_multiplier, 0.0, 1.0) - Input.start_joy_vibration( - device, - clampf(weak_magnitude * _multiplier, 0.0, 1.0), - clampf(strong_magnitude * _multiplier, 0.0, 1.0), - duration * (weak_controller_duration_multiplier if is_weak_controller else 1.0) - ) + _weak_magnitude = remap(_weak_magnitude, 0.0, 1.0, min_weak_magnitude_threshold * float(_weak_magnitude > 0.0), 1.0) + _strong_magnitude = remap(_strong_magnitude, 0.0, 1.0, min_strong_magnitude_threshold * float(_strong_magnitude > 0.0), 1.0) + duration = maxf(duration, min_duration_threshold) + + Input.start_joy_vibration(device, _weak_magnitude, _strong_magnitude, duration) #started_vibration.emit() diff --git a/game/src/ui/main_menu/main_menu.gd b/game/src/ui/main_menu/main_menu.gd index e13a647..cc4fea0 100644 --- a/game/src/ui/main_menu/main_menu.gd +++ b/game/src/ui/main_menu/main_menu.gd @@ -11,10 +11,12 @@ const SAVE_DATA_PATH: String = SAVES_DIR + "%s/save_data.tres" @onready var continue_button: Button = %ContinueButton @onready var load_button: Button = %LoadButton @onready var new_game_button: Button = %NewGameButton +@onready var options_button: Button = %OptionsButton @onready var quit_button: Button = %QuitButton @onready var saves_scroll_container: ScrollContainer = %SavesScrollContainer @onready var saves_container: VBoxContainer = %SavesContainer +@onready var settings_menu: SettingsMenu = %SettingsMenu static func get_save_slots() -> PackedInt32Array: @@ -52,15 +54,17 @@ func _ready() -> void: continue_button.pressed.connect(_on_continue_pressed) load_button.pressed.connect(_on_load_pressed) new_game_button.pressed.connect(_on_new_game_pressed) + options_button.pressed.connect(_on_options_pressed) quit_button.pressed.connect(_on_quit_pressed) + settings_menu.close_request.connect(_on_settings_close_request) + continue_button.visible = has_save() func has_save() -> bool: - var config := ConfigFile.new() - config.load(USER_SETTINGS_PATH) - return config.has_section_key("game", "last_slot") + var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) + return config.has_section_key("save", "last_slot") func populate_save_entries() -> void: @@ -74,9 +78,8 @@ func populate_save_entries() -> void: func _on_continue_pressed() -> void: - var config := ConfigFile.new() - config.load(USER_SETTINGS_PATH) - var slot: int = config.get_value("game", "last_slot", 0) + var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) + var slot: int = config.get_value("save", "last_slot", 0) var path: String = get_save_path(slot) if not FileAccess.file_exists(path): @@ -86,7 +89,7 @@ func _on_continue_pressed() -> void: _on_new_game_pressed() return - config.set_value("game", "last_slot", slot) + config.set_value("save", "last_slot", slot) config.save(USER_SETTINGS_PATH) var save_data: SaveData = load(path) @@ -103,20 +106,22 @@ func _on_new_game_pressed() -> void: var slot_index: int = get_unique_save_slot_index() load_game_request.emit(save_data, slot_index) - var config := ConfigFile.new() - config.load(USER_SETTINGS_PATH) - config.set_value("game", "last_slot", slot_index) + var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) + config.set_value("save", "last_slot", slot_index) config.save(USER_SETTINGS_PATH) +func _on_options_pressed() -> void: + settings_menu.show() + + func _on_quit_pressed() -> void: quit_request.emit() func _on_save_entry_pressed(save_slot: int) -> void: - var config := ConfigFile.new() - config.load(USER_SETTINGS_PATH) - config.set_value("game", "last_slot", save_slot) + var config: ConfigFile = Utils.load_config(USER_SETTINGS_PATH) + config.set_value("save", "last_slot", save_slot) config.save(USER_SETTINGS_PATH) var save_path: String = get_save_path(save_slot) @@ -127,3 +132,7 @@ func _on_save_entry_pressed(save_slot: int) -> void: var save_data: SaveData = load(save_path) load_game_request.emit(save_data, save_slot) + + +func _on_settings_close_request() -> void: + settings_menu.hide() diff --git a/game/src/ui/main_menu/main_menu.tscn b/game/src/ui/main_menu/main_menu.tscn index 217cb5c..6cc6999 100644 --- a/game/src/ui/main_menu/main_menu.tscn +++ b/game/src/ui/main_menu/main_menu.tscn @@ -2,6 +2,7 @@ [ext_resource type="Script" uid="uid://ckaouf136x7rh" path="res://src/ui/main_menu/main_menu.gd" id="1_2hyyg"] [ext_resource type="Material" uid="uid://c38215ysnknyk" path="res://assets/dev/dark/dark_01.tres" id="2_4ux21"] +[ext_resource type="PackedScene" uid="uid://cxm2uc8fw031f" path="res://src/ui/settings_menu/settings_menu.tscn" id="2_osdni"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_4ux21"] sky_horizon_color = Color(0.66224277, 0.6717428, 0.6867428, 1) @@ -67,6 +68,11 @@ unique_name_in_owner = true layout_mode = 2 text = "NEW_GAME" +[node name="OptionsButton" type="Button" parent="MainButtons" unique_id=666018363] +unique_name_in_owner = true +layout_mode = 2 +text = "SETTINGS" + [node name="QuitButton" type="Button" parent="MainButtons" unique_id=851953121] unique_name_in_owner = true layout_mode = 2 @@ -111,6 +117,22 @@ text = "Save 1" layout_mode = 2 text = "Save 1" +[node name="SettingsMenu" parent="." unique_id=8078639 instance=ExtResource("2_osdni")] +unique_name_in_owner = true +visible = false +layout_mode = 1 + +[node name="ColorRect" type="ColorRect" parent="SettingsMenu" unique_id=1002221363] +show_behind_parent = true +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, 0.6901961) + [node name="Background" type="Node3D" parent="." unique_id=877986421] [node name="WorldEnvironment" type="WorldEnvironment" parent="Background" unique_id=3797920] diff --git a/game/src/ui/pause_menu/pause_menu.gd b/game/src/ui/pause_menu/pause_menu.gd index 94b25f6..f6396c8 100644 --- a/game/src/ui/pause_menu/pause_menu.gd +++ b/game/src/ui/pause_menu/pause_menu.gd @@ -5,17 +5,26 @@ extends Control @export var world: World @onready var continue_button: Button = %ContinueButton +@onready var settings_button: Button = %SettingsButton @onready var main_menu_button: Button = %MainMenuButton +@onready var settings_menu: SettingsMenu = %SettingsMenu + func _ready() -> void: continue_button.pressed.connect(set_paused.bind(false)) + settings_button.pressed.connect(_on_settings_button_pressed) main_menu_button.pressed.connect(_on_main_menu_button_pressed) + settings_menu.close_request.connect(_on_settings_menu_close_requested) + func _input(event: InputEvent) -> void: if event.is_action_pressed(&"pause"): - toggle_pause() + if settings_menu.is_visible_in_tree(): + settings_menu.close() + else: + toggle_pause() func set_paused(value: bool) -> void: @@ -31,5 +40,13 @@ func toggle_pause() -> void: set_paused(not is_paused()) +func _on_settings_button_pressed() -> void: + settings_menu.show() + + +func _on_settings_menu_close_requested() -> void: + settings_menu.hide() + + func _on_main_menu_button_pressed() -> void: world.request_world_unload() diff --git a/game/src/ui/pause_menu/pause_menu.tscn b/game/src/ui/pause_menu/pause_menu.tscn index b1c25d7..3bb027f 100644 --- a/game/src/ui/pause_menu/pause_menu.tscn +++ b/game/src/ui/pause_menu/pause_menu.tscn @@ -1,6 +1,7 @@ [gd_scene format=3 uid="uid://yuthv3c7rx8"] [ext_resource type="Script" uid="uid://brd2041bv6vdk" path="res://src/ui/pause_menu/pause_menu.gd" id="1_fsbbv"] +[ext_resource type="PackedScene" uid="uid://cxm2uc8fw031f" path="res://src/ui/settings_menu/settings_menu.tscn" id="2_u4p1g"] [node name="PauseMenu" type="Control" unique_id=90120455] process_mode = 3 @@ -13,6 +14,15 @@ grow_vertical = 2 script = ExtResource("1_fsbbv") metadata/_custom_type_script = "uid://brd2041bv6vdk" +[node name="ColorRect" type="ColorRect" parent="." unique_id=1353751197] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.6901961) + [node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=905531263] layout_mode = 1 anchors_preset = 8 @@ -32,7 +42,28 @@ unique_name_in_owner = true layout_mode = 2 text = "Continue" +[node name="SettingsButton" type="Button" parent="VBoxContainer" unique_id=1154942305] +unique_name_in_owner = true +layout_mode = 2 +text = "Options" + [node name="MainMenuButton" type="Button" parent="VBoxContainer" unique_id=2079918606] unique_name_in_owner = true layout_mode = 2 text = "Main Menu" + +[node name="SettingsMenu" parent="." unique_id=8078639 instance=ExtResource("2_u4p1g")] +unique_name_in_owner = true +visible = false +layout_mode = 1 + +[node name="ColorRect" type="ColorRect" parent="SettingsMenu" unique_id=660173424] +show_behind_parent = true +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, 0.6901961) diff --git a/game/src/ui/settings_menu/settings_menu.gd b/game/src/ui/settings_menu/settings_menu.gd new file mode 100644 index 0000000..3f2bc7f --- /dev/null +++ b/game/src/ui/settings_menu/settings_menu.gd @@ -0,0 +1,190 @@ +class_name SettingsMenu +extends Control + +signal close_request + +var _previously_focused_control: Control + +@onready var back_button: Button = %BackButton +# Confirmation +@onready var revert_timer: Timer = %RevertTimer +@onready var commit_settings: Button = %CommitSettings +@onready var revert_settings: Button = %RevertSettings +@onready var confirmation_dialog: Control = %ConfirmationDialog +@onready var time_left_label: Label = %TimeLeftLabel +# Gameplay +@onready var headbobbing: CheckButton = %Headbobbing +@onready var headbobbing_slider: HSlider = %HeadbobbingSlider +# Localization +@onready var language: OptionButton = %Language +# Video Settings +@onready var window_mode: OptionButton = %WindowMode +# Audio Settings +@onready var rhythm_delay: Button = %RhythmDelay +@onready var master_slider: HSlider = %MasterSlider +@onready var sfx_slider: HSlider = %SFXSlider +@onready var music_slider: HSlider = %MusicSlider +@onready var ambience_slider: HSlider = %AmbienceSlider + +@onready var rhythm_setup_menu: RhythmSetupMenu = $RhythmSetupMenu + + +func _ready() -> void: + update_controls_from_settings() + + back_button.pressed.connect(close_request.emit) + # Confirmation + revert_timer.timeout.connect(_on_revert_timer_timeout) + commit_settings.pressed.connect(_on_commit_settings_pressed) + revert_settings.pressed.connect(_on_revert_settings_pressed) + # Gameplay + headbobbing.toggled.connect(_on_headbobbing_toggled) + headbobbing_slider.value_changed.connect(_on_headbobbing_intensity_value_changed) + # Localization + language.item_selected.connect(_on_language_selected) + # Video settings + window_mode.item_selected.connect(_on_window_mode_item_selected) + # Audio Settings + rhythm_delay.pressed.connect(_on_configure_rhythm_delay_pressed) + rhythm_setup_menu.confirmed.connect(_on_configure_rhythm_confirmed) + master_slider.value_changed.connect(_on_volume_slider_changed.bind(&"Master")) + sfx_slider.value_changed.connect(_on_volume_slider_changed.bind(&"SFX")) + music_slider.value_changed.connect(_on_volume_slider_changed.bind(&"Music")) + ambience_slider.value_changed.connect(_on_volume_slider_changed.bind(&"Ambient")) + + visibility_changed.connect(func() -> void: if is_visible_in_tree(): update_controls_from_settings()) + + set_process(false) + + +func _process(_delta: float) -> void: + if not revert_timer.is_stopped(): + var second: int = ceili(revert_timer.time_left) + time_left_label.text = tr_n("SETTINGS_REVERT_TIME_LEFT", "", second) % second + + +func close() -> void: + if rhythm_setup_menu.is_visible_in_tree(): + rhythm_setup_menu.close() + else: + close_request.emit() + + +func update_controls_from_settings() -> void: + # Gameplay + headbobbing.set_pressed_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing", true)) + headbobbing_slider.set_value_no_signal(SettingsHandler.get_setting("gameplay", "headbobbing_multiplier", 1.0)) + + # Localization + match SettingsHandler.get_setting("localization", "language", "en_US"): + "en_US": + language.select(0) + "de_DE": + language.select(1) + "es_ES": + language.select(2) + "ja_JA": + language.select(3) + + # Video + match SettingsHandler.get_setting("video", "window_mode", 3): + 3: + window_mode.select(0) + 4: + window_mode.select(1) + 0: + window_mode.select(2) + + # Audio + master_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "Master", 0.85)) + sfx_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "SFX", 1.0)) + music_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "Music", 1.0)) + ambience_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "Ambient", 1.0)) + + +func ask_for_confirmation() -> void: + _previously_focused_control = get_viewport().gui_get_focus_owner() + confirmation_dialog.show() + revert_settings.grab_focus() + revert_timer.start() + set_process(true) + + +func _on_commit_settings_pressed() -> void: + SettingsHandler.commit_settings() + SettingsHandler.save_settings() + revert_timer.stop() + confirmation_dialog.hide() + set_process(false) + + if is_instance_valid(_previously_focused_control): + _previously_focused_control.grab_focus() + + +func _on_revert_settings_pressed() -> void: + revert_timer.stop() + _on_revert_timer_timeout() + + +func _on_revert_timer_timeout() -> void: + SettingsHandler.revert_settings() + confirmation_dialog.hide() + update_controls_from_settings() + set_process(false) + + if is_instance_valid(_previously_focused_control): + _previously_focused_control.grab_focus() + + +func _on_headbobbing_toggled(toggled_on: bool) -> void: + SettingsHandler.set_setting("gameplay", "headbobbing", toggled_on) + + +func _on_headbobbing_intensity_value_changed(value: float) -> void: + SettingsHandler.set_setting("gameplay", "headbobbing_multiplier", value) + + +func _on_language_selected(index: int) -> void: + var language_string: String = "en_US" + match index: + 0: + language_string = "en_US" + 1: + language_string = "de_DE" + 2: + language_string = "es_ES" + 3: + language_string = "ja_JA" + + SettingsHandler.set_setting("localization", "language", language_string, false) + SettingsHandler.apply_settings() + ask_for_confirmation() + + +func _on_window_mode_item_selected(index: int) -> void: + var mode: DisplayServer.WindowMode + match index: + 0: + mode = DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN + 1: + mode = DisplayServer.WindowMode.WINDOW_MODE_EXCLUSIVE_FULLSCREEN + 2: + mode = DisplayServer.WindowMode.WINDOW_MODE_WINDOWED + _: + mode = DisplayServer.WindowMode.WINDOW_MODE_FULLSCREEN + + SettingsHandler.set_setting("video", "window_mode", mode, false) + SettingsHandler.apply_settings() + ask_for_confirmation() + + +func _on_configure_rhythm_delay_pressed() -> void: + rhythm_setup_menu.show() + + +func _on_configure_rhythm_confirmed() -> void: + rhythm_setup_menu.hide() + + +func _on_volume_slider_changed(value: float, bus: StringName) -> void: + SettingsHandler.set_setting("audio", bus, value) diff --git a/game/src/ui/settings_menu/settings_menu.gd.uid b/game/src/ui/settings_menu/settings_menu.gd.uid new file mode 100644 index 0000000..a25776d --- /dev/null +++ b/game/src/ui/settings_menu/settings_menu.gd.uid @@ -0,0 +1 @@ +uid://cll4odq5p7iuv diff --git a/game/src/ui/settings_menu/settings_menu.tscn b/game/src/ui/settings_menu/settings_menu.tscn new file mode 100644 index 0000000..d66720d --- /dev/null +++ b/game/src/ui/settings_menu/settings_menu.tscn @@ -0,0 +1,414 @@ +[gd_scene format=3 uid="uid://cxm2uc8fw031f"] + +[ext_resource type="Script" uid="uid://cll4odq5p7iuv" path="res://src/ui/settings_menu/settings_menu.gd" id="1_3iv5y"] +[ext_resource type="Script" uid="uid://bfpr421kg4s" path="res://src/ui/settings_menu/slider_label.gd" id="2_axtgf"] +[ext_resource type="PackedScene" uid="uid://b41bjano4bw6s" path="res://src/ui/setup/rhythm/rhythm_setup_menu.tscn" id="3_kktd5"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_axtgf"] +content_margin_left = 0.0 +content_margin_top = 0.0 +content_margin_right = 0.0 +content_margin_bottom = 0.0 +bg_color = Color(0.1, 0.1, 0.1, 0.6) +border_width_left = 4 +border_width_top = 4 +border_width_right = 4 +border_width_bottom = 4 +corner_radius_top_left = 3 +corner_radius_top_right = 3 +corner_radius_bottom_right = 3 +corner_radius_bottom_left = 3 +corner_detail = 5 + +[node name="SettingsMenu" type="Control" unique_id=8078639] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_3iv5y") +metadata/_custom_type_script = "uid://cll4odq5p7iuv" + +[node name="PanelContainer" type="PanelContainer" parent="." unique_id=747218901] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 0.05 +anchor_right = 0.5 +anchor_bottom = 0.95 +offset_left = -200.0 +offset_right = 200.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer" unique_id=481159029] +layout_mode = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="PanelContainer/VBoxContainer" unique_id=1719442009] +layout_mode = 2 +size_flags_vertical = 3 +follow_focus = true + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer/VBoxContainer/ScrollContainer" unique_id=1482670452] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 12 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 12 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer" unique_id=1149761963] +layout_mode = 2 + +[node name="GameplaySettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=2072631132] +layout_mode = 2 + +[node name="GameplayLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1726505815] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Gameplay" +horizontal_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=136767090] +layout_mode = 2 + +[node name="Headbobbing" type="CheckButton" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1121847591] +unique_name_in_owner = true +layout_mode = 2 +button_pressed = true +text = "Headbobbing" + +[node name="HeadbobbingLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1435927865] +layout_mode = 2 +text = "Headbobbing-Intensity" + +[node name="HeadbobbingMultiplier" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=883105611] +layout_mode = 2 + +[node name="HeadbobbingSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HeadbobbingMultiplier" unique_id=1792577413] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +value = 1.0 +tick_count = 11 +ticks_on_borders = true + +[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/HeadbobbingMultiplier" unique_id=790300279 node_paths=PackedStringArray("slider")] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +text = "1.00" +horizontal_alignment = 2 +script = ExtResource("2_axtgf") +slider = NodePath("../HeadbobbingSlider") + +[node name="LocalizationSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1387881590] +layout_mode = 2 + +[node name="LocalizationLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1225964665] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Localization" +horizontal_alignment = 1 + +[node name="HSeparator2" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=282328287] +layout_mode = 2 + +[node name="Language" type="OptionButton" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=210620308] +unique_name_in_owner = true +layout_mode = 2 +flat = true +selected = 0 +item_count = 4 +popup/item_0/text = "English" +popup/item_0/id = 0 +popup/item_1/text = "German" +popup/item_1/id = 1 +popup/item_2/text = "Spanish" +popup/item_2/id = 2 +popup/item_3/text = "Japanese" +popup/item_3/id = 3 + +[node name="VideoSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=421589929] +layout_mode = 2 + +[node name="VideoLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=80166823] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Video" +horizontal_alignment = 1 + +[node name="HSeparator3" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=188845275] +layout_mode = 2 + +[node name="WindowMode" type="OptionButton" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=512024521] +unique_name_in_owner = true +layout_mode = 2 +selected = 0 +item_count = 3 +popup/item_0/text = "WINDOW_FULLSCREEN" +popup/item_0/id = 1 +popup/item_1/text = "WINDOW_EXCLUSIVE_FULLSCREEN" +popup/item_1/id = 2 +popup/item_2/text = "WINDOW_WINDOWED" +popup/item_2/id = 0 + +[node name="AudioSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1454411793] +layout_mode = 2 + +[node name="AudioLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1475246011] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Audio" +horizontal_alignment = 1 + +[node name="HSeparator4" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1708002596] +layout_mode = 2 + +[node name="RhythmDelay" type="Button" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1954746002] +unique_name_in_owner = true +layout_mode = 2 +text = "Configure Rhythm Delay" + +[node name="AudioSliders" type="VBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1541515152] +layout_mode = 2 + +[node name="MasterLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1666127429] +layout_mode = 2 +text = "MASTER" + +[node name="MasterContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=63007160] +layout_mode = 2 + +[node name="MasterSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MasterContainer" unique_id=43103749] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +value = 0.85 +tick_count = 11 +ticks_on_borders = true + +[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MasterContainer" unique_id=1539756970 node_paths=PackedStringArray("slider")] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +text = "85" +horizontal_alignment = 2 +script = ExtResource("2_axtgf") +slider = NodePath("../MasterSlider") +pad_decimals = 0 +do_remap = true +remap_output_max = 100.0 + +[node name="SFXLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=96391289] +layout_mode = 2 +text = "SFX" + +[node name="SFXContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1321350084] +layout_mode = 2 + +[node name="SFXSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/SFXContainer" unique_id=954640158] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +value = 1.0 +tick_count = 11 +ticks_on_borders = true + +[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/SFXContainer" unique_id=1852166469 node_paths=PackedStringArray("slider")] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +text = "100" +horizontal_alignment = 2 +script = ExtResource("2_axtgf") +slider = NodePath("../SFXSlider") +pad_decimals = 0 +do_remap = true +remap_output_max = 100.0 + +[node name="MusicLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1699726056] +layout_mode = 2 +text = "MUSIC" + +[node name="MusicContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=1571401865] +layout_mode = 2 + +[node name="MusicSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MusicContainer" unique_id=1554055762] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +value = 1.0 +tick_count = 11 +ticks_on_borders = true + +[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/MusicContainer" unique_id=722315379 node_paths=PackedStringArray("slider")] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +text = "100" +horizontal_alignment = 2 +script = ExtResource("2_axtgf") +slider = NodePath("../MusicSlider") +pad_decimals = 0 +do_remap = true +remap_output_max = 100.0 + +[node name="AmbienceLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=679820709] +layout_mode = 2 +text = "AMBIENT" + +[node name="AmbienceContainer" type="HBoxContainer" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders" unique_id=852171003] +layout_mode = 2 + +[node name="AmbienceSlider" type="HSlider" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/AmbienceContainer" unique_id=1743356533] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +max_value = 1.0 +step = 0.01 +value = 1.0 +tick_count = 11 +ticks_on_borders = true + +[node name="SliderLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer/AudioSliders/AmbienceContainer" unique_id=1842144008 node_paths=PackedStringArray("slider")] +custom_minimum_size = Vector2(32, 0) +layout_mode = 2 +text = "100" +horizontal_alignment = 2 +script = ExtResource("2_axtgf") +slider = NodePath("../AmbienceSlider") +pad_decimals = 0 +do_remap = true +remap_output_max = 100.0 + +[node name="ControlsSettings" type="Control" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=738451135] +layout_mode = 2 + +[node name="ControlsLabel" type="Label" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=997989979] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "Controls" +horizontal_alignment = 1 + +[node name="HSeparator5" type="HSeparator" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=1211938154] +layout_mode = 2 + +[node name="VibrationConfig" type="Button" parent="PanelContainer/VBoxContainer/ScrollContainer/MarginContainer/VBoxContainer" unique_id=253824648] +unique_name_in_owner = true +layout_mode = 2 +text = "Configure Vibrations" + +[node name="BackButton" type="Button" parent="PanelContainer/VBoxContainer" unique_id=1438707398] +unique_name_in_owner = true +layout_mode = 2 +text = "BACK" + +[node name="ConfirmationDialog" type="Control" parent="." unique_id=1084371767] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="RevertTimer" type="Timer" parent="ConfirmationDialog" unique_id=494220922] +unique_name_in_owner = true +wait_time = 5.0 +one_shot = true + +[node name="ColorRect" type="ColorRect" parent="ConfirmationDialog" unique_id=87130800] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.7254902) + +[node name="ConfirmationPanel" type="PanelContainer" parent="ConfirmationDialog" unique_id=1058124629] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -132.5 +offset_top = -85.5 +offset_right = 132.5 +offset_bottom = 85.5 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_axtgf") + +[node name="MarginContainer" type="MarginContainer" parent="ConfirmationDialog/ConfirmationPanel" unique_id=1476949599] +layout_mode = 2 +theme_override_constants/margin_left = 12 +theme_override_constants/margin_top = 12 +theme_override_constants/margin_right = 12 +theme_override_constants/margin_bottom = 12 + +[node name="VBoxContainer" type="VBoxContainer" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer" unique_id=1736269226] +layout_mode = 2 +theme_override_constants/separation = 8 + +[node name="Label" type="Label" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=458855790] +layout_mode = 2 +theme_override_font_sizes/font_size = 32 +text = "SETTINGS_CONFIRMATION_TITLE" +horizontal_alignment = 1 + +[node name="HSeparator" type="HSeparator" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=1567845501] +layout_mode = 2 + +[node name="TimeLeftLabel" type="Label" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=130409981] +unique_name_in_owner = true +layout_mode = 2 +text = "SETTINGS_REVERT_TIME_LEFT" +horizontal_alignment = 1 + +[node name="HSeparator2" type="HSeparator" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=11885534] +layout_mode = 2 + +[node name="Control" type="Control" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=1620955474] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer" unique_id=1766652365] +layout_mode = 2 +alignment = 1 + +[node name="CommitSettings" type="Button" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1797911803] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "SETTINGS_APPLY" + +[node name="RevertSettings" type="Button" parent="ConfirmationDialog/ConfirmationPanel/MarginContainer/VBoxContainer/HBoxContainer" unique_id=1089460477] +unique_name_in_owner = true +layout_mode = 2 +size_flags_horizontal = 3 +text = "SETTINGS_REVERT" + +[node name="RhythmSetupMenu" parent="." unique_id=552044082 instance=ExtResource("3_kktd5")] +visible = false +layout_mode = 1 +mouse_filter = 0 + +[node name="ColorRect" type="ColorRect" parent="RhythmSetupMenu" unique_id=539350747] +modulate = Color(0, 0, 0, 1) +show_behind_parent = true +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 diff --git a/game/src/ui/settings_menu/slider_label.gd b/game/src/ui/settings_menu/slider_label.gd new file mode 100644 index 0000000..eb13a85 --- /dev/null +++ b/game/src/ui/settings_menu/slider_label.gd @@ -0,0 +1,47 @@ +class_name SliderLabel +extends Label + + +@export var slider: Slider: set = set_slider +@export var pad_zeros: int = 1 +@export var pad_decimals: int = 2 +@export var prefix: String = "" +@export var positive_prefix: String = "" +@export var negative_prefix: String = "" +@export var suffix: String = "" +@export_group("Remap Value") +@export var do_remap: bool = false +@export var remap_initial_min: float = 0.0 +@export var remap_initial_max: float = 1.0 +@export var remap_output_min: float = 0.0 +@export var remap_output_max: float = 1.0 + + +func _ready() -> void: + await get_tree().process_frame + _on_value_changed.call_deferred(slider.value) + + +func set_slider(new_slider: Slider) -> void: + if new_slider == slider: + return + + if is_instance_valid(slider): + slider.value_changed.disconnect(_on_value_changed) + + if is_instance_valid(new_slider): + new_slider.value_changed.connect(_on_value_changed) + if not new_slider.is_node_ready(): + await new_slider.ready + _on_value_changed(new_slider.get_value()) # Apply the value of the new slider. + + slider = new_slider + + +func _on_value_changed(new_value: float) -> void: + var value: float = new_value + if do_remap: + value = remap(value, remap_initial_min, remap_initial_max, remap_output_min, remap_output_max) + + var new_text: String = str(value).pad_zeros(pad_zeros).pad_decimals(pad_decimals) + set_text(str(prefix, positive_prefix if value >= 0.0 else negative_prefix, new_text, suffix)) diff --git a/game/src/ui/settings_menu/slider_label.gd.uid b/game/src/ui/settings_menu/slider_label.gd.uid new file mode 100644 index 0000000..44c3800 --- /dev/null +++ b/game/src/ui/settings_menu/slider_label.gd.uid @@ -0,0 +1 @@ +uid://bfpr421kg4s diff --git a/game/src/ui/setup/controller/controller_setup_menu.gd b/game/src/ui/setup/controller/controller_setup_menu.gd new file mode 100644 index 0000000..510cf84 --- /dev/null +++ b/game/src/ui/setup/controller/controller_setup_menu.gd @@ -0,0 +1,48 @@ +class_name ControllerSetupMenu +extends Control + +const MIN_MAGNITUDE: float = 0.05 + +var is_testing_weak: bool = false +var is_testing_strong: bool = false + +@onready var vibration_component: VibrationComponent = %VibrationComponent +@onready var repeat_vibration_timer: Timer = %RepeatVibrationTimer + +@onready var min_duration_slider: HSlider = %MinDurationSlider +@onready var min_weak_magnitude: HSlider = %MinWeakMagnitude +@onready var min_strong_magnitude: HSlider = %MinStrongMagnitude + + +func _ready() -> void: + repeat_vibration_timer.timeout.connect(_test_vibration) + + min_weak_magnitude.focus_entered.connect(_on_weak_slider_focused) + min_strong_magnitude.focus_entered.connect(_on_strong_slider_focused) + + min_weak_magnitude.value_changed.connect(_on_min_weak_magnitude_value_changed) + min_strong_magnitude.value_changed.connect(_on_min_strong_magnitude_value_changed) + + +func _test_vibration() -> void: + vibration_component.weak_magnitude = MIN_MAGNITUDE * float(is_testing_weak) + vibration_component.strong_magnitude = MIN_MAGNITUDE * float(is_testing_strong) + vibration_component.vibrate() + + +func _on_min_weak_magnitude_value_changed(value: float) -> void: + VibrationComponent.min_weak_magnitude_threshold = value + + +func _on_min_strong_magnitude_value_changed(value: float) -> void: + VibrationComponent.min_strong_magnitude_threshold = value + + +func _on_weak_slider_focused() -> void: + is_testing_weak = true + is_testing_strong = false + + +func _on_strong_slider_focused() -> void: + is_testing_strong = true + is_testing_weak = false diff --git a/game/src/ui/setup/controller/controller_setup_menu.gd.uid b/game/src/ui/setup/controller/controller_setup_menu.gd.uid new file mode 100644 index 0000000..7d09a81 --- /dev/null +++ b/game/src/ui/setup/controller/controller_setup_menu.gd.uid @@ -0,0 +1 @@ +uid://cs2yn6o8vsp4a diff --git a/game/src/ui/setup/controller/controller_setup_menu.tscn b/game/src/ui/setup/controller/controller_setup_menu.tscn new file mode 100644 index 0000000..e2d8c7e --- /dev/null +++ b/game/src/ui/setup/controller/controller_setup_menu.tscn @@ -0,0 +1,81 @@ +[gd_scene format=3 uid="uid://dak6f2xw3mgjj"] + +[ext_resource type="Script" uid="uid://cs2yn6o8vsp4a" path="res://src/ui/setup/controller/controller_setup_menu.gd" id="1_ee61x"] +[ext_resource type="Script" uid="uid://bbwtct3hoxwws" path="res://src/core/vibration_component.gd" id="2_6beo4"] + +[node name="ControllerSetupMenu" type="Control" unique_id=1563422179] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_ee61x") +metadata/_custom_type_script = "uid://cs2yn6o8vsp4a" + +[node name="VibrationComponent" type="Node" parent="." unique_id=193091150] +unique_name_in_owner = true +script = ExtResource("2_6beo4") +weak_magnitude = 0.05 +strong_magnitude = 0.05 +metadata/_custom_type_script = "uid://bbwtct3hoxwws" + +[node name="RepeatVibrationTimer" type="Timer" parent="." unique_id=1579451638] +unique_name_in_owner = true +wait_time = 0.5 +autostart = true + +[node name="Label" type="Label" parent="." unique_id=684594410] +layout_mode = 0 +offset_right = 384.0 +offset_bottom = 49.0 +text = "Adjust the sliders, until you can bearly feel the vibrations. + +Adjust the first slider for the weak magnitude +Adjust the second slider for the strong magnitude" + +[node name="RichTextLabel" type="RichTextLabel" parent="." unique_id=1446444531] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +bbcode_enabled = true +text = "Accept [img=32]uid://bi8jxa3gcbfdn[/img] +Back [img=32]uid://cjtlrpqb75elt[/img]" +horizontal_alignment = 2 +vertical_alignment = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="." unique_id=239130047] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.35416666 +anchor_top = 0.48765433 +anchor_right = 0.6458333 +anchor_bottom = 0.5123457 +offset_left = -20.0 +offset_top = -20.0 +offset_right = 20.0 +offset_bottom = 20.0 +grow_horizontal = 2 +grow_vertical = 2 +metadata/_edit_use_anchors_ = true + +[node name="MinDurationSlider" type="HSlider" parent="VBoxContainer" unique_id=339190501] +unique_name_in_owner = true +visible = false +layout_mode = 2 + +[node name="MinWeakMagnitude" type="HSlider" parent="VBoxContainer" unique_id=1247262766] +unique_name_in_owner = true +layout_mode = 2 +max_value = 1.0 +step = 0.01 + +[node name="MinStrongMagnitude" type="HSlider" parent="VBoxContainer" unique_id=1752017132] +unique_name_in_owner = true +layout_mode = 2 +max_value = 1.0 +step = 0.01 diff --git a/game/src/ui/setup/rhythm/120bpm_beat.ogg b/game/src/ui/setup/rhythm/120bpm_beat.ogg new file mode 100644 index 0000000..2b8f9ce Binary files /dev/null and b/game/src/ui/setup/rhythm/120bpm_beat.ogg differ diff --git a/game/src/ui/setup/rhythm/120bpm_beat.ogg.import b/game/src/ui/setup/rhythm/120bpm_beat.ogg.import new file mode 100644 index 0000000..057db5d --- /dev/null +++ b/game/src/ui/setup/rhythm/120bpm_beat.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://cwpx80o5yaauf" +path="res://.godot/imported/120bpm_beat.ogg-614fb8516ee6fbb46342f9f7e2c140b1.oggvorbisstr" + +[deps] + +source_file="res://src/ui/setup/rhythm/120bpm_beat.ogg" +dest_files=["res://.godot/imported/120bpm_beat.ogg-614fb8516ee6fbb46342f9f7e2c140b1.oggvorbisstr"] + +[params] + +loop=true +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/game/src/ui/setup/rhythm/rhythm_setup_menu.gd b/game/src/ui/setup/rhythm/rhythm_setup_menu.gd new file mode 100644 index 0000000..aeacaaf --- /dev/null +++ b/game/src/ui/setup/rhythm/rhythm_setup_menu.gd @@ -0,0 +1,82 @@ +class_name RhythmSetupMenu +extends Control + +signal confirmed + +var _previous_song_info: SongInfo +var _previous_paused: bool = true +var _previous_bpm: float = 120.0 +var _previous_beats_per_bar: int = 4 + +var _previous_song_time: float = 0.0 +var _previous_total_song_time: float = 0.0 +var _previous_beat: float = 0.0 +var _previous_beat_phase: float = 0.0 +var _previous_bar: float = 0.0 +var _previous_bar_phase: float = 0.0 + +@onready var user_offset_slider: HSlider = %UserOffsetSlider +@onready var rhythm_player: RhythmPlayer = $RhythmPlayer +@onready var rhythm_beat: AudioStreamPlayer = %RhythmBeat +@onready var confirm_button: Button = %Confirm + + +func _ready() -> void: + user_offset_slider.value_changed.connect(_on_user_offset_slider_value_changed) + visibility_changed.connect(_on_visibility_changed) + confirm_button.pressed.connect(close) + + +func close() -> void: + confirmed.emit() + + +func _on_user_offset_slider_value_changed(value: float) -> void: + SettingsHandler.set_setting("audio", "user_offset_ms", value, false) + SettingsHandler.commit_settings() + SettingsHandler.save_settings() + RhythmPlayer.set_user_offset_ms(value) + + +func _on_visibility_changed() -> void: + if not is_visible_in_tree(): + rhythm_player.stop() + _restore_song_playback_data() + return + + if not rhythm_beat.playing: + _store_song_playback_data() + + user_offset_slider.set_value_no_signal(SettingsHandler.get_setting("audio", "user_offset_ms", 0.0)) + rhythm_player.play() + + +func _store_song_playback_data() -> void: + _previous_song_info = RhythmPlayer.current_song_info + _previous_paused = RhythmPlayer.paused + _previous_bpm = RhythmPlayer.bpm + _previous_beats_per_bar = RhythmPlayer.beats_per_bar + + _previous_song_time = RhythmPlayer.song_time + _previous_total_song_time = ShaderGlobals.total_song_time + _previous_beat = RhythmPlayer.beat + _previous_beat_phase = RhythmPlayer.beat_phase + _previous_bar = RhythmPlayer.bar + _previous_bar_phase = RhythmPlayer.bar_phase + + +func _restore_song_playback_data() -> void: + RhythmPlayer.current_song_info = _previous_song_info + RhythmPlayer.paused = _previous_paused + RhythmPlayer.bpm = _previous_bpm + RhythmPlayer.beats_per_bar = _previous_beats_per_bar + + RhythmPlayer.song_time = _previous_song_time + ShaderGlobals.set_song_time(_previous_song_time) + ShaderGlobals.set_total_song_time(_previous_total_song_time) + RhythmPlayer.beat = _previous_beat + ShaderGlobals.set_beat(RhythmPlayer.beat) + RhythmPlayer.beat_phase = _previous_beat_phase + RhythmPlayer.bar = _previous_bar + ShaderGlobals.set_beat(RhythmPlayer.bar) + RhythmPlayer.bar_phase = _previous_bar_phase diff --git a/game/src/ui/setup/rhythm/rhythm_setup_menu.gd.uid b/game/src/ui/setup/rhythm/rhythm_setup_menu.gd.uid new file mode 100644 index 0000000..8d420d0 --- /dev/null +++ b/game/src/ui/setup/rhythm/rhythm_setup_menu.gd.uid @@ -0,0 +1 @@ +uid://bvs4uvpewolwc diff --git a/game/src/ui/setup/rhythm/rhythm_setup_menu.tscn b/game/src/ui/setup/rhythm/rhythm_setup_menu.tscn new file mode 100644 index 0000000..40cdb0e --- /dev/null +++ b/game/src/ui/setup/rhythm/rhythm_setup_menu.tscn @@ -0,0 +1,126 @@ +[gd_scene format=3 uid="uid://b41bjano4bw6s"] + +[ext_resource type="Script" uid="uid://bvs4uvpewolwc" path="res://src/ui/setup/rhythm/rhythm_setup_menu.gd" id="1_la56f"] +[ext_resource type="Script" uid="uid://bfpr421kg4s" path="res://src/ui/settings_menu/slider_label.gd" id="2_0yond"] +[ext_resource type="Script" uid="uid://bdi06itcm6wfp" path="res://src/core/rhythm/rhythm_player.gd" id="2_jsobq"] +[ext_resource type="AudioStream" uid="uid://cwpx80o5yaauf" path="res://src/ui/setup/rhythm/120bpm_beat.ogg" id="2_uupbh"] +[ext_resource type="Script" uid="uid://c5mqtmsvgt4e8" path="res://src/core/rhythm/song_info.gd" id="4_7a0hf"] +[ext_resource type="Script" uid="uid://cx1nws4t8hs2u" path="res://src/core/rhythm/tempo_section_info.gd" id="5_cribi"] +[ext_resource type="Texture2D" uid="uid://t6ydwif1bo1c" path="res://godot_icon.svg" id="6_cribi"] +[ext_resource type="Script" uid="uid://bvmfdeypbeqsn" path="res://src/core/rhythm/rhythm_property_setter.gd" id="7_0yond"] + +[sub_resource type="Resource" id="Resource_0yond"] +script = ExtResource("4_7a0hf") +audio_stream = ExtResource("2_uupbh") +metadata/_custom_type_script = "uid://c5mqtmsvgt4e8" + +[node name="RhythmSetupMenu" type="Control" unique_id=552044082] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +script = ExtResource("1_la56f") +metadata/_custom_type_script = "uid://bvs4uvpewolwc" + +[node name="UserOffsetSlider" type="HSlider" parent="." unique_id=1070556103] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.35 +anchor_top = 0.5 +anchor_right = 0.65 +anchor_bottom = 0.5 +offset_left = -4.0 +offset_top = -8.0 +offset_right = 4.0 +offset_bottom = 8.0 +grow_horizontal = 2 +grow_vertical = 2 +min_value = -1000.0 +max_value = 1000.0 +step = 50.0 +tick_count = 5 + +[node name="SliderLabel" type="Label" parent="." unique_id=1415099418 node_paths=PackedStringArray("slider")] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 0.545 +anchor_right = 0.5 +anchor_bottom = 0.545 +offset_left = -20.0 +offset_top = -11.5 +offset_right = 20.0 +offset_bottom = 11.5 +grow_horizontal = 2 +grow_vertical = 2 +text = "+0.0 ms" +horizontal_alignment = 1 +script = ExtResource("2_0yond") +slider = NodePath("../UserOffsetSlider") +pad_decimals = 0 +positive_prefix = "+" +suffix = " ms" +metadata/_custom_type_script = "uid://bfpr421kg4s" + +[node name="Confirm" type="Button" parent="." unique_id=1584505137] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 0.75 +anchor_right = 0.5 +anchor_bottom = 0.75 +offset_left = -35.5 +offset_top = -31.0 +offset_right = 35.5 +grow_horizontal = 2 +grow_vertical = 0 +text = "Confirm" + +[node name="RhythmPlayer" type="Node" parent="." unique_id=326589779] +script = ExtResource("2_jsobq") +song_info = SubResource("Resource_0yond") +audio_player = NodePath("RhythmBeat") +metadata/_custom_type_script = "uid://bdi06itcm6wfp" + +[node name="RhythmBeat" type="AudioStreamPlayer" parent="RhythmPlayer" unique_id=763640763] +unique_name_in_owner = true +stream = ExtResource("2_uupbh") + +[node name="HBoxContainer" type="HBoxContainer" parent="." unique_id=1857329940] +layout_mode = 1 +anchors_preset = -1 +anchor_left = 0.5 +anchor_top = 0.15 +anchor_right = 0.5 +anchor_bottom = 0.15 +offset_left = -130.0 +offset_right = 130.0 +offset_bottom = 128.0 +grow_horizontal = 2 + +[node name="RhythmPropertySetter" type="Node" parent="HBoxContainer" unique_id=402296027 node_paths=PackedStringArray("beat_nodes", "bar_nodes")] +script = ExtResource("7_0yond") +beat_nodes = [NodePath("../BeatTexture")] +bar_nodes = [NodePath("../BarTexture")] +beat_property = &"modulate:a" +bar_property = &"modulate:a" +beat_range_min = 1.0 +beat_range_max = 0.0 +beat_exponent = 3.0 +bar_range_min = 1.0 +bar_range_max = 0.0 +bar_exponent = 1.5 +metadata/_custom_type_script = "uid://bvmfdeypbeqsn" + +[node name="BeatTexture" type="TextureRect" parent="HBoxContainer" unique_id=778284471] +layout_mode = 2 +texture = ExtResource("6_cribi") + +[node name="BarTexture" type="TextureRect" parent="HBoxContainer" unique_id=1873889480] +layout_mode = 2 +texture = ExtResource("6_cribi") diff --git a/game/src/worlds/exposition/exposition.tscn b/game/src/worlds/exposition/exposition.tscn index 373cf0c..8207cc0 100644 --- a/game/src/worlds/exposition/exposition.tscn +++ b/game/src/worlds/exposition/exposition.tscn @@ -380,7 +380,7 @@ material = ExtResource("3_en67y") script = ExtResource("3_maf5p") level_id = &"entrance_hall" scene_path = "uid://cksacjkic7wl6" -load_aabbs = Array[AABB]([AABB(-22, -2, -20, 41, 23, 40), AABB(-18, 0, -36, 41, 19, 16)]) +load_aabbs = Array[AABB]([AABB(-22, -2, -20, 43, 23, 40), AABB(-18, 0, -36, 41, 19, 16)]) metadata/_custom_type_script = "uid://bgau5aa5a00w3" [node name="SecondEntranceHall" type="Node3D" parent="." index="7" unique_id=282414961] @@ -510,7 +510,9 @@ shape = SubResource("BoxShape3D_dy1qy") [node name="ChaseTest" parent="." index="10" unique_id=1255206756 instance=ExtResource("14_264wa")] transform = Transform3D(-4.371139e-08, 0, 1, 0, 1, 0, -1, 0, -4.371139e-08, -20.5, 0, 8.9608346e-07) -[node name="PauseMenu" parent="." index="11" unique_id=90120455 node_paths=PackedStringArray("world") instance=ExtResource("2_yht7d")] -visible = false -world = NodePath("..") +[node name="CanvasLayer" type="CanvasLayer" parent="." index="11" unique_id=1094429456] +layer = 0 +[node name="PauseMenu" parent="CanvasLayer" index="0" unique_id=90120455 node_paths=PackedStringArray("world") instance=ExtResource("2_yht7d")] +visible = false +world = NodePath("../..")