Implemented a settings menu

This commit is contained in:
SchimmelSpreu83 2026-03-23 20:35:38 +01:00
parent 71d967d988
commit 4df3d03d07
32 changed files with 1322 additions and 80 deletions

View File

@ -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"

View File

@ -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"

View File

@ -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"

View File

@ -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...",,

Can't render this file because it has a wrong number of fields in line 3.

View File

@ -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]

View File

@ -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)

View File

@ -0,0 +1 @@
uid://dj1t6rc6fpiti

View File

@ -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:

View File

@ -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

View File

@ -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()

View File

@ -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()

View File

@ -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]

View File

@ -5,16 +5,25 @@ 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"):
if settings_menu.is_visible_in_tree():
settings_menu.close()
else:
toggle_pause()
@ -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()

View File

@ -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)

View File

@ -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)

View File

@ -0,0 +1 @@
uid://cll4odq5p7iuv

View File

@ -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

View File

@ -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))

View File

@ -0,0 +1 @@
uid://bfpr421kg4s

View File

@ -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

View File

@ -0,0 +1 @@
uid://cs2yn6o8vsp4a

View File

@ -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

Binary file not shown.

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
uid://bvs4uvpewolwc

View File

@ -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")

View File

@ -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("../..")