Added project settings menu

This commit is contained in:
Rob Kelly 2024-12-08 16:21:05 -07:00
parent bfe2ca46d4
commit 94d1ac8abf
28 changed files with 781 additions and 101 deletions

1
.gitignore vendored
View File

@ -3,6 +3,7 @@
# Godot-specific ignores
.import/
override.cfg
export.cfg
export_presets.cfg

View File

@ -19,6 +19,7 @@ run/max_fps=60
[autoload]
ClubCatalog="*res://src/equipment/clubs/club_catalog.tscn"
GameSettings="*res://src/game/game_settings.gd"
[debug]

View File

@ -13,6 +13,9 @@ var _loading_resources := {}
static var group := "GameGroup" # hey i'm group
## Typesafe accessor for GameSettings singleton
static var settings := GameSettings as GameSettingsType
class Promise:
var _callbacks: Array[Callable] = []

29
src/game/game_settings.gd Normal file
View File

@ -0,0 +1,29 @@
class_name GameSettingsType extends Node
## Container for project settings, for quick runtime access.
var free_camera_speed: float
var x_sensitivity: float
var y_sensitivity: float
var x_acceleration: float
var y_acceleration: float
var invert_pitch: bool
var enable_screen_shake: bool
var enable_hit_lag: bool
func _init() -> void:
ProjectSettings.settings_changed.connect(_read_settings)
func _read_settings() -> void:
free_camera_speed = ProjectSettings.get_setting("game/config/controls/camera/free_camera_speed")
x_sensitivity = ProjectSettings.get_setting("game/config/controls/camera/x_axis_sensitivity")
y_sensitivity = ProjectSettings.get_setting("game/config/controls/camera/y_axis_sensitivity")
x_acceleration = ProjectSettings.get_setting("game/config/controls/camera/x_axis_acceleration")
y_acceleration = ProjectSettings.get_setting("game/config/controls/camera/y_axis_acceleration")
invert_pitch = ProjectSettings.get_setting("game/config/controls/camera/invert_pitch")
enable_screen_shake = ProjectSettings.get_setting(
"game/config/accessibility/enable_screen_shake"
)
enable_hit_lag = ProjectSettings.get_setting("game/config/accessibility/enable_hit_lag")

View File

@ -69,23 +69,6 @@ const EXPLOSIVE_FORCE_FACTOR := 0.12
var player: WorldPlayer
var base_speed: float = ProjectSettings.get_setting("game/config/controls/camera/free_camera_speed")
var x_sensitivity: float = ProjectSettings.get_setting(
"game/config/controls/camera/x_axis_sensitivity"
)
var x_acceleration: float = ProjectSettings.get_setting(
"game/config/controls/camera/x_axis_acceleration"
)
var y_sensitivity: float = ProjectSettings.get_setting(
"game/config/controls/camera/y_axis_sensitivity"
)
var y_acceleration: float = ProjectSettings.get_setting(
"game/config/controls/camera/y_axis_acceleration"
)
var invert_pitch: bool = ProjectSettings.get_setting("game/config/controls/camera/invert_pitch")
var control_disabled := false
var reset_enabled := false:
@ -223,9 +206,15 @@ func _unhandled_input(event: InputEvent) -> void:
func camera_motion(motion: Vector2) -> void:
if not control_disabled and phase == Phase.AIM:
# Can only control camera while aiming
_target_rotation.y = _target_rotation.y - deg_to_rad(motion.x * x_sensitivity)
_target_rotation.y = _target_rotation.y - deg_to_rad(motion.x * Game.settings.x_sensitivity)
_target_rotation.x = clampf(
_target_rotation.x - deg_to_rad(motion.y * y_sensitivity) * (-1 if invert_pitch else 1),
(
_target_rotation.x
- (
deg_to_rad(motion.y * Game.settings.y_sensitivity)
* (-1 if Game.settings.invert_pitch else 1)
)
),
PITCH_MIN,
PITCH_MAX
)
@ -493,9 +482,11 @@ func _process(delta: float) -> void:
## Visual updates
# Rotation
direction.rotation.y = lerp_angle(
direction.rotation.y, _target_rotation.y, delta * x_acceleration
direction.rotation.y, _target_rotation.y, delta * Game.settings.x_acceleration
)
pitch.rotation.x = lerp_angle(
pitch.rotation.x, _target_rotation.x, delta * Game.settings.y_acceleration
)
pitch.rotation.x = lerp_angle(pitch.rotation.x, _target_rotation.x, delta * y_acceleration)
# Arrow lags behind camera control
arrow_pivot.rotation.y = lerp_angle(

View File

@ -3,23 +3,6 @@ class_name FreeCamera extends CharacterBody3D
const SPRINT_MULT := 3.0
const PITCH_LIMIT := deg_to_rad(85.0)
var base_speed: float = ProjectSettings.get_setting("game/config/controls/camera/free_camera_speed")
var x_sensitivity: float = ProjectSettings.get_setting(
"game/config/controls/camera/x_axis_sensitivity"
)
var x_acceleration: float = ProjectSettings.get_setting(
"game/config/controls/camera/x_axis_acceleration"
)
var y_sensitivity: float = ProjectSettings.get_setting(
"game/config/controls/camera/y_axis_sensitivity"
)
var y_acceleration: float = ProjectSettings.get_setting(
"game/config/controls/camera/y_axis_acceleration"
)
var invert_pitch: bool = ProjectSettings.get_setting("game/config/controls/camera/invert_pitch")
@onready var _target := Vector2(rotation.x, rotation.y)
static var scene := preload("res://src/ui/camera/free_camera/free_camera.tscn")
@ -34,9 +17,15 @@ func _unhandled_input(event: InputEvent) -> void:
func camera_motion(motion: Vector2) -> void:
_target.y = _target.y - deg_to_rad(motion.x * x_sensitivity)
_target.y = _target.y - deg_to_rad(motion.x * Game.settings.x_sensitivity)
_target.x = clampf(
_target.x - deg_to_rad(motion.y * y_sensitivity) * (-1 if invert_pitch else 1),
(
_target.x
- (
deg_to_rad(motion.y * Game.settings.y_sensitivity)
* (-1 if Game.settings.invert_pitch else 1)
)
),
-PITCH_LIMIT,
PITCH_LIMIT,
)
@ -44,15 +33,15 @@ func camera_motion(motion: Vector2) -> void:
func _physics_process(delta: float) -> void:
# Rotation
rotation.y = lerp_angle(rotation.y, _target.y, delta * x_acceleration)
rotation.x = lerp_angle(rotation.x, _target.x, delta * y_acceleration)
rotation.y = lerp_angle(rotation.y, _target.y, delta * Game.settings.x_acceleration)
rotation.x = lerp_angle(rotation.x, _target.x, delta * Game.settings.y_acceleration)
# Handle spatial movement
var xz_input := Input.get_vector("camera_left", "camera_right", "camera_forward", "camera_back")
var y_input := Input.get_vector("camera_down", "camera_up", "camera_down", "camera_up")
var direction := (transform.basis * Vector3(xz_input.x, y_input.y, xz_input.y)).normalized()
var speed := base_speed
var speed := Game.settings.free_camera_speed
if Input.is_action_pressed("camera_sprint"):
speed *= SPRINT_MULT

View File

@ -2,7 +2,7 @@ class_name OrbitalCamera extends Node3D
const POSITION_ACCELERATION := 4.0
const BASIS_ACCELERATION := 4.0
const TARGETER_ACCELERATION := 7.0
const TARGETER_ACCELERATION := 4.0
@export var target: Node3D
@export var offset := Vector3(0, 1, 0)

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://b47goj32i6sdh"]
[ext_resource type="Script" path="res://src/ui/input_prompt/input_prompt.gd" id="1_qq6w5"]
[ext_resource type="Script" path="res://src/ui/elements/input_prompt/input_prompt.gd" id="1_qq6w5"]
[node name="InputPrompt" type="Label"]
anchors_preset = 15
@ -11,4 +11,3 @@ grow_vertical = 2
theme_type_variation = &"InputPrompt"
text = "❓ - [unknown]"
script = ExtResource("1_qq6w5")
action = ""

View File

@ -0,0 +1,97 @@
@tool
class_name NumericSlider extends HBoxContainer
## HSlider with an attached SpinBox
signal value_changed(value: float)
@export_category("Shared Range Properties")
@export var min_value := 0.0:
set(v):
_set_shared("min_value", v)
min_value = v
@export var max_value := 100.0:
set(v):
_set_shared("max_value", v)
max_value = v
@export var step := 1.0:
set(v):
_set_shared("step", v)
step = v
@export var page := 0:
set(v):
_set_shared("page", v)
page = v
@export var value: float:
get:
return _value
set(v):
_value = v
value_changed.emit(v)
@export var exp_edit := false:
set(v):
_set_shared("exp_edit", v)
exp_edit = v
@export var rounded := false:
set(v):
_set_shared("rounded", v)
rounded = v
@export var allow_greater := false:
set(v):
_set_shared("allow_greater", v)
allow_greater = v
@export var allow_lesser := false:
set(v):
_set_shared("allow_lesser", v)
allow_lesser = v
var _value := 0.0:
set(v):
if slider:
slider.set_value_no_signal(v)
if spin_box:
spin_box.set_value_no_signal(v)
_value = v
@onready var slider: HSlider = $HSlider
@onready var spin_box: SpinBox = $SpinBox
func _ready() -> void:
# Force propagation of properties to shared range elements
min_value = min_value
max_value = max_value
step = step
page = page
_value = _value
exp_edit = exp_edit
rounded = rounded
allow_greater = allow_greater
allow_lesser = allow_lesser
func set_value_no_signal(new_value: float) -> void:
_value = new_value
func _set_shared(property: String, new_value: Variant) -> void:
if slider:
slider.set(property, new_value)
if spin_box:
spin_box.set(property, new_value)
func _on_spin_box_value_changed(new_value: float) -> void:
value = new_value
func _on_slider_value_changed(new_value: float) -> void:
value = new_value

View File

@ -0,0 +1,27 @@
[gd_scene load_steps=2 format=3 uid="uid://dqqcyi26d3bpg"]
[ext_resource type="Script" path="res://src/ui/elements/numeric_slider/numeric_slider.gd" id="1_kcr4o"]
[node name="NumericSlider" type="HBoxContainer"]
anchors_preset = 14
anchor_top = 0.5
anchor_right = 1.0
anchor_bottom = 0.5
offset_top = -8.0
offset_bottom = 10.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_kcr4o")
[node name="HSlider" type="HSlider" parent="."]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 4
[node name="SpinBox" type="SpinBox" parent="."]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
alignment = 2
[connection signal="value_changed" from="HSlider" to="." method="_on_slider_value_changed"]
[connection signal="value_changed" from="SpinBox" to="." method="_on_spin_box_value_changed"]

View File

@ -0,0 +1,29 @@
@tool
extends CheckBox
## Checkbox which updates its label based on check state.
@export var true_text := "On":
set(value):
true_text = value
_refresh()
@export var false_text := "Off":
set(value):
false_text = value
_refresh()
func _ready() -> void:
_refresh()
func _set_text(value: bool) -> void:
text = true_text if value else false_text
func _refresh() -> void:
_set_text(button_pressed)
func _on_toggled(toggled_on: bool) -> void:
_set_text(toggled_on)

View File

@ -0,0 +1,10 @@
[gd_scene load_steps=2 format=3 uid="uid://b7ce38k7rx466"]
[ext_resource type="Script" path="res://src/ui/elements/text_checkbox/text_checkbox.gd" id="1_6mma0"]
[node name="TextCheckbox" type="CheckBox"]
size_flags_horizontal = 0
text = "Off"
script = ExtResource("1_6mma0")
[connection signal="toggled" from="." to="." method="_on_toggled"]

View File

@ -13,7 +13,7 @@ var _hit_lag_frames := -1
## Start playing a screen shake effect.
func screen_shake(intensity: float, duration: float = 0.2) -> void:
if not ProjectSettings.get_setting("game/config/accessibility/enable_screen_shake"):
if not Game.settings.enable_screen_shake:
return
var tween := get_tree().create_tween()
@ -24,7 +24,7 @@ func screen_shake(intensity: float, duration: float = 0.2) -> void:
## Rumble the screen indefinitely.
func set_rumble(intensity: float) -> void:
if not ProjectSettings.get_setting("game/config/accessibility/enable_screen_shake"):
if not Game.settings.enable_screen_shake:
return
rumbler.intensity = intensity
@ -52,7 +52,7 @@ func hit_lag_huge() -> void:
## Stop processing for some number of frames.
func hit_lag(frames: int = 1) -> void:
if not ProjectSettings.get_setting("game/config/accessibility/enable_hit_lag"):
if not Game.settings.enable_hit_lag:
return
_hit_lag_frames = frames

View File

@ -1,4 +1,4 @@
[gd_resource type="Theme" load_steps=6 format=3 uid="uid://diodjft5u2cck"]
[gd_resource type="Theme" load_steps=7 format=3 uid="uid://diodjft5u2cck"]
[ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="1_3rv2b"]
[ext_resource type="FontFile" uid="uid://comihs66wounx" path="res://assets/fonts/Dokdo/Dokdo-Regular.ttf" id="1_eha6a"]
@ -6,9 +6,32 @@
[ext_resource type="FontFile" uid="uid://dyog4ex5nqfat" path="res://assets/fonts/promptfont/promptfont.otf" id="2_8kux8"]
[ext_resource type="FontFile" uid="uid://s4c1kf0rk2mb" path="res://assets/fonts/Geo/Geo-Regular.ttf" id="3_cee6l"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ynsl8"]
content_margin_left = 36.0
content_margin_top = 8.0
content_margin_right = 36.0
content_margin_bottom = 8.0
bg_color = Color(0.1, 0.1, 0.1, 0.6)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[resource]
default_font = ExtResource("3_cee6l")
default_font_size = 18
AlertButton/base_type = &"UIButton"
AlertButton/colors/font_color = Color(0.819608, 0.196078, 0.196078, 1)
AlertButton/colors/font_outline_color = Color(0, 0, 0, 1)
AlertButton/constants/outline_size = 6
AlertButton/font_sizes/font_size = 24
AlertButton/fonts/font = ExtResource("2_5ty6u")
CancelButton/base_type = &"UIButton"
CancelButton/colors/font_outline_color = Color(0, 0, 0, 1)
CancelButton/constants/outline_size = 6
CancelButton/font_sizes/font_size = 24
CancelButton/fonts/font = ExtResource("2_5ty6u")
ClubSelectLabel/base_type = &"Label"
ClubSelectLabel/colors/font_color = Color(1, 0.933333, 0.894118, 1)
ClubSelectLabel/colors/font_outline_color = Color(0, 0, 0, 1)
@ -24,12 +47,17 @@ ClubSelectLabelDisabled/colors/font_outline_color = Color(0.2, 0.2, 0.2, 1)
ClubSelectLabelDisabled/colors/font_shadow_color = Color(0.2, 0.2, 0.2, 1)
ClubSelectLabelDisabled/font_sizes/font_size = 84
ClubSelectLabelDisabled/fonts/font = ExtResource("1_3rv2b")
HeaderLarge/font_sizes/font_size = 28
HeaderMedium/font_sizes/font_size = 24
HeaderSmall/font_sizes/font_size = 20
HeaderXLarge/base_type = &"Label"
HeaderXLarge/font_sizes/font_size = 36
InputPrompt/base_type = &"Label"
InputPrompt/colors/font_color = Color(1, 1, 0.870588, 1)
InputPrompt/constants/outline_size = 8
InputPrompt/font_sizes/font_size = 32
InputPrompt/fonts/font = ExtResource("2_8kux8")
PauseMenuButton/base_type = &"Button"
PauseMenuButton/base_type = &"UIButton"
PauseMenuButton/colors/font_outline_color = Color(0, 0, 0, 1)
PauseMenuButton/constants/outline_size = 6
PauseMenuButton/font_sizes/font_size = 32
@ -39,9 +67,23 @@ QuantityLabel/colors/font_color = Color(0.819608, 0.196078, 0.196078, 1)
QuantityLabel/colors/font_outline_color = Color(1, 0.901961, 0.509804, 1)
QuantityLabel/constants/outline_size = 6
QuantityLabel/font_sizes/font_size = 22
SettingsList/base_type = &"VBoxContainer"
SettingsList/constants/separation = 4
SettingsListMargin/base_type = &"MarginContainer"
SettingsListMargin/constants/margin_bottom = 8
SettingsListMargin/constants/margin_left = 8
SettingsListMargin/constants/margin_right = 8
SettingsListMargin/constants/margin_top = 8
SettingsPageContainer/base_type = &"MarginContainer"
SettingsPageContainer/constants/margin_bottom = 72
SettingsPageContainer/constants/margin_left = 72
SettingsPageContainer/constants/margin_right = 72
SettingsPageContainer/constants/margin_top = 72
ShotFeedback/base_type = &"RichTextLabel"
ShotFeedback/colors/font_shadow_color = Color(0, 0, 0, 1)
ShotFeedback/constants/shadow_offset_x = 6
ShotFeedback/constants/shadow_offset_y = 4
ShotFeedback/font_sizes/normal_font_size = 272
ShotFeedback/fonts/normal_font = ExtResource("1_eha6a")
UIButton/base_type = &"Button"
UIButton/styles/normal = SubResource("StyleBoxFlat_ynsl8")

View File

@ -1,17 +1,18 @@
extends Control
## Menu shown in-game when the user presses pause.
const SETTINGS_SCENE := preload("res://src/ui/menus/settings_menu/settings_menu.tscn")
@onready var menu_list: VBoxContainer = %MenuList
@onready var quit_confirm: CenterContainer = %QuitConfirm
@onready var settings_container: MarginContainer = %SettingsContainer
func _ready() -> void:
print_debug("Pause menu _ready()")
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
func _exit_tree() -> void:
print_debug("Pause menu _exit_tree()")
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
@ -30,12 +31,14 @@ func _hide() -> void:
func resume() -> void:
print_debug("Pause menu resume()")
queue_free()
func settings() -> void:
pass # TODO
var instance: Control = SETTINGS_SCENE.instantiate()
settings_container.add_child(instance)
instance.tree_exited.connect(_unhide)
_hide()
func quit() -> void:

View File

@ -60,9 +60,25 @@ text = "Settings"
[node name="QuitButton" type="Button" parent="MarginContainer/MenuList"]
layout_mode = 2
theme_type_variation = &"PauseMenuButton"
theme_override_colors/font_color = Color(1, 0.36, 0.36, 1)
theme_override_colors/font_color = Color(0.819608, 0.196078, 0.196078, 1)
text = "Quit"
[node name="SettingsContainer" type="MarginContainer" parent="."]
unique_name_in_owner = true
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -640.0
offset_top = -360.0
offset_right = 640.0
offset_bottom = 360.0
grow_horizontal = 2
grow_vertical = 2
mouse_filter = 2
[node name="QuitConfirm" type="CenterContainer" parent="."]
unique_name_in_owner = true
visible = false
@ -88,7 +104,9 @@ layout_mode = 2
[node name="Label" type="Label" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Quit to desktop?"
horizontal_alignment = 1
[node name="HBoxContainer" type="HBoxContainer" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
@ -97,11 +115,13 @@ theme_override_constants/separation = 16
[node name="CancelButton" type="Button" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"CancelButton"
text = "Cancel"
[node name="ConfirmQuitButton" type="Button" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"AlertButton"
text = "Quit"
[connection signal="pressed" from="MarginContainer/MenuList/ResumeButton" to="." method="resume"]

View File

@ -0,0 +1,12 @@
extends Setting
## Setting with checkbox representing a boolean value.
@onready var checkbox: CheckBox = %TextCheckbox
func initialize_value(value: Variant) -> void:
checkbox.button_pressed = value as bool
func get_value() -> Variant:
return checkbox.button_pressed

View File

@ -0,0 +1,12 @@
[gd_scene load_steps=4 format=3 uid="uid://bpmpj4n6xp17l"]
[ext_resource type="PackedScene" uid="uid://dcah6r3ku60g6" path="res://src/ui/menus/settings_menu/settings/setting/setting.tscn" id="1_5t42f"]
[ext_resource type="Script" path="res://src/ui/menus/settings_menu/settings/checkbox_setting/checkbox_setting.gd" id="2_mwq55"]
[ext_resource type="PackedScene" uid="uid://b7ce38k7rx466" path="res://src/ui/elements/text_checkbox/text_checkbox.tscn" id="3_fosy2"]
[node name="CheckboxSetting" instance=ExtResource("1_5t42f")]
script = ExtResource("2_mwq55")
[node name="TextCheckbox" parent="PanelContainer/MarginContainer" index="0" instance=ExtResource("3_fosy2")]
unique_name_in_owner = true
layout_mode = 2

View File

@ -0,0 +1,12 @@
extends Setting
## Setting with numeric slider representing a floating-point value
@onready var slider: NumericSlider = %NumericSlider
func initialize_value(value: Variant) -> void:
slider.value = value as float
func get_value() -> Variant:
return slider.value

View File

@ -0,0 +1,12 @@
[gd_scene load_steps=4 format=3 uid="uid://dut1lj8ju37sq"]
[ext_resource type="PackedScene" uid="uid://dcah6r3ku60g6" path="res://src/ui/menus/settings_menu/settings/setting/setting.tscn" id="1_t2sut"]
[ext_resource type="Script" path="res://src/ui/menus/settings_menu/settings/numeric_setting/numeric_setting.gd" id="2_3nkup"]
[ext_resource type="PackedScene" uid="uid://dqqcyi26d3bpg" path="res://src/ui/elements/numeric_slider/numeric_slider.tscn" id="2_piwkl"]
[node name="NumericSetting" instance=ExtResource("1_t2sut")]
script = ExtResource("2_3nkup")
[node name="NumericSlider" parent="PanelContainer/MarginContainer" index="0" instance=ExtResource("2_piwkl")]
unique_name_in_owner = true
layout_mode = 2

View File

@ -0,0 +1,25 @@
class_name Setting extends HBoxContainer
## Base class for settings menu inputs
@export var key: StringName
func _ready() -> void:
if not Engine.is_editor_hint():
initialize_value(ProjectSettings.get_setting(key))
## Initialize the value of this setting from project settings.
func initialize_value(_value: Variant) -> void:
pass # Implemented in derived type
## Apply the set value of this setting to project settings.
func apply() -> void:
ProjectSettings.set_setting(key, get_value())
## Get the raw value of this setting.
func get_value() -> Variant:
# Implemented in derived type
return null

View File

@ -0,0 +1,26 @@
[gd_scene load_steps=2 format=3 uid="uid://dcah6r3ku60g6"]
[ext_resource type="Script" path="res://src/ui/menus/settings_menu/settings/setting/setting.gd" id="1_rrash"]
[node name="Setting" type="HBoxContainer"]
script = ExtResource("1_rrash")
[node name="Spacer" type="HSeparator" parent="."]
custom_minimum_size = Vector2(32, 0)
layout_mode = 2
[node name="SettingLabel" type="Label" parent="."]
layout_mode = 2
size_flags_horizontal = 3
text = "Label"
[node name="PanelContainer" type="PanelContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 3
[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"]
layout_mode = 2
theme_override_constants/margin_left = 4
theme_override_constants/margin_top = 2
theme_override_constants/margin_right = 4
theme_override_constants/margin_bottom = 2

View File

@ -0,0 +1,34 @@
extends MarginContainer
## Menu allowing the user to adjust game configuration.
const SETTINGS_GROUP := "Settings"
const SETTINGS_FILE := "override.cfg"
func _get_settings_elements() -> Array[Setting]:
var elements: Array[Setting] = []
elements.assign(get_tree().get_nodes_in_group(SETTINGS_GROUP))
return elements
## Close menu without applying settings.
func cancel() -> void:
queue_free()
## Apply all settings.
func apply() -> void:
for setting: Setting in _get_settings_elements():
setting.apply()
## Write all applied settings to disk.
func save_settings() -> void:
ProjectSettings.save_custom(SETTINGS_FILE)
## Apply settings and close menu.
func accept() -> void:
apply()
save_settings()
queue_free()

View File

@ -0,0 +1,307 @@
[gd_scene load_steps=4 format=3 uid="uid://d3eaqw2rdurct"]
[ext_resource type="Script" path="res://src/ui/menus/settings_menu/settings_menu.gd" id="1_lbcn7"]
[ext_resource type="PackedScene" uid="uid://bpmpj4n6xp17l" path="res://src/ui/menus/settings_menu/settings/checkbox_setting/checkbox_setting.tscn" id="2_f274v"]
[ext_resource type="PackedScene" uid="uid://dut1lj8ju37sq" path="res://src/ui/menus/settings_menu/settings/numeric_setting/numeric_setting.tscn" id="3_jox8e"]
[node name="SettingsMenu" type="MarginContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_lbcn7")
[node name="TabContainer" type="TabContainer" parent="."]
layout_mode = 2
current_tab = 0
[node name="Game" type="MarginContainer" parent="TabContainer"]
layout_mode = 2
theme_type_variation = &"SettingsPageContainer"
metadata/_tab_index = 0
[node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/Game"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Game/VBoxContainer"]
layout_mode = 2
[node name="Icon" type="Label" parent="TabContainer/Game/VBoxContainer/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"InputPrompt"
text = "⚙"
[node name="Label" type="Label" parent="TabContainer/Game/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_vertical = 8
theme_type_variation = &"HeaderXLarge"
text = "Game Configuration"
[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Game/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="TabContainer/Game/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_type_variation = &"SettingsListMargin"
[node name="SettingsList" type="VBoxContainer" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="AccessibilityHeading" type="HBoxContainer" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList"]
layout_mode = 2
[node name="Label" type="Label" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccessibilityHeading"]
layout_mode = 2
theme_type_variation = &"HeaderMedium"
text = "Accessibility"
[node name="HSeparator" type="HSeparator" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccessibilityHeading"]
layout_mode = 2
size_flags_horizontal = 3
[node name="ScreenShake" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("2_f274v")]
layout_mode = 2
key = &"game/config/accessibility/enable_screen_shake"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/ScreenShake" index="1"]
text = "Enable Screen Shake"
[node name="HitLag" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("2_f274v")]
layout_mode = 2
key = &"game/config/accessibility/enable_hit_lag"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/HitLag" index="1"]
text = "Enable Hit Lag Effect"
[node name="CameraHeading" type="HBoxContainer" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList"]
layout_mode = 2
[node name="Label" type="Label" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/CameraHeading"]
layout_mode = 2
theme_type_variation = &"HeaderMedium"
text = "Camera"
[node name="HSeparator" type="HSeparator" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/CameraHeading"]
layout_mode = 2
size_flags_horizontal = 3
[node name="FreeCameraSpeed" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("3_jox8e")]
layout_mode = 2
key = &"game/config/controls/camera/free_camera_speed"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/FreeCameraSpeed" index="1"]
text = "Free Camera Speed"
[node name="NumericSlider" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/FreeCameraSpeed/PanelContainer/MarginContainer" index="0"]
step = 0.1
[node name="HSlider" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/FreeCameraSpeed/PanelContainer/MarginContainer/NumericSlider" index="0"]
step = 0.1
[node name="SpinBox" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/FreeCameraSpeed/PanelContainer/MarginContainer/NumericSlider" index="1"]
step = 0.1
suffix = "m/s"
[node name="SensitivityX" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("3_jox8e")]
layout_mode = 2
key = &"game/config/controls/camera/x_axis_sensitivity"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/SensitivityX" index="1"]
text = "Sensitivity, Horizontal"
[node name="NumericSlider" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/SensitivityX/PanelContainer/MarginContainer" index="0"]
max_value = 4.0
step = 0.01
[node name="SensitivityY" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("3_jox8e")]
layout_mode = 2
key = &"game/config/controls/camera/y_axis_sensitivity"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/SensitivityY" index="1"]
text = "Sensitivity, Vertical"
[node name="NumericSlider" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/SensitivityY/PanelContainer/MarginContainer" index="0"]
max_value = 4.0
step = 0.01
[node name="AccelerationX" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("3_jox8e")]
layout_mode = 2
key = &"game/config/controls/camera/x_axis_acceleration"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccelerationX" index="1"]
text = "Acceleration, Horizontal"
[node name="NumericSlider" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccelerationX/PanelContainer/MarginContainer" index="0"]
step = 0.1
[node name="AccelerationY" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("3_jox8e")]
layout_mode = 2
key = &"game/config/controls/camera/y_axis_acceleration"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccelerationY" index="1"]
text = "Acceleration, Vertical"
[node name="NumericSlider" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccelerationY/PanelContainer/MarginContainer" index="0"]
step = 0.1
[node name="InvertPitch" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList" groups=["Settings"] instance=ExtResource("2_f274v")]
layout_mode = 2
key = &"game/config/controls/camera/invert_pitch"
[node name="SettingLabel" parent="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/InvertPitch" index="1"]
text = "Invert Pitch"
[node name="Graphics" type="MarginContainer" parent="TabContainer"]
visible = false
layout_mode = 2
theme_type_variation = &"SettingsPageContainer"
metadata/_tab_index = 1
[node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/Graphics"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Graphics/VBoxContainer"]
layout_mode = 2
[node name="Icon" type="Label" parent="TabContainer/Graphics/VBoxContainer/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"InputPrompt"
text = "🖵
"
[node name="Label" type="Label" parent="TabContainer/Graphics/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_vertical = 8
theme_type_variation = &"HeaderXLarge"
text = "Graphics & Display"
[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Graphics/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="TabContainer/Graphics/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_type_variation = &"SettingsListMargin"
[node name="SettingsList" type="VBoxContainer" parent="TabContainer/Graphics/VBoxContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="Audio" type="MarginContainer" parent="TabContainer"]
visible = false
layout_mode = 2
theme_type_variation = &"SettingsPageContainer"
metadata/_tab_index = 2
[node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/Audio"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Audio/VBoxContainer"]
layout_mode = 2
[node name="Icon" type="Label" parent="TabContainer/Audio/VBoxContainer/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"InputPrompt"
text = "🕬"
[node name="Label" type="Label" parent="TabContainer/Audio/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_vertical = 8
theme_type_variation = &"HeaderXLarge"
text = "Audio Settings"
[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Audio/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="TabContainer/Audio/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_type_variation = &"SettingsListMargin"
[node name="SettingsList" type="VBoxContainer" parent="TabContainer/Audio/VBoxContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="Controls" type="MarginContainer" parent="TabContainer"]
visible = false
layout_mode = 2
theme_type_variation = &"SettingsPageContainer"
metadata/_tab_index = 3
[node name="VBoxContainer" type="VBoxContainer" parent="TabContainer/Controls"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/Controls/VBoxContainer"]
layout_mode = 2
[node name="Icon" type="Label" parent="TabContainer/Controls/VBoxContainer/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"InputPrompt"
text = "␼"
[node name="Label" type="Label" parent="TabContainer/Controls/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_vertical = 8
theme_type_variation = &"HeaderXLarge"
text = "Control Bindings"
[node name="ScrollContainer" type="ScrollContainer" parent="TabContainer/Controls/VBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="TabContainer/Controls/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_type_variation = &"SettingsListMargin"
[node name="SettingsList" type="VBoxContainer" parent="TabContainer/Controls/VBoxContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="SouthEast" type="MarginContainer" parent="."]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 8
theme_override_constants/margin_left = 16
theme_override_constants/margin_top = 16
theme_override_constants/margin_right = 16
theme_override_constants/margin_bottom = 16
[node name="HBoxContainer" type="HBoxContainer" parent="SouthEast"]
layout_mode = 2
theme_override_constants/separation = 16
[node name="CancelButton" type="Button" parent="SouthEast/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"CancelButton"
text = "Cancel
"
[node name="AcceptButton" type="Button" parent="SouthEast/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"AlertButton"
text = "Accept
"
[connection signal="pressed" from="SouthEast/HBoxContainer/CancelButton" to="." method="cancel"]
[connection signal="pressed" from="SouthEast/HBoxContainer/AcceptButton" to="." method="accept"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/ScreenShake"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/HitLag"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/FreeCameraSpeed"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/FreeCameraSpeed/PanelContainer/MarginContainer/NumericSlider"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/SensitivityX"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/SensitivityY"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccelerationX"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/AccelerationY"]
[editable path="TabContainer/Game/VBoxContainer/ScrollContainer/MarginContainer/SettingsList/InvertPitch"]

View File

@ -13,7 +13,7 @@
[ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="8_bejx4"]
[ext_resource type="Texture2D" uid="uid://tancoet1lih5" path="res://assets/ui/ball_icons/basic_icon.png" id="8_tt8i3"]
[ext_resource type="PackedScene" uid="uid://dmciuk3pbjsae" path="res://src/ui/shot_hud/life_bar/life_bar.tscn" id="9_w1fiw"]
[ext_resource type="PackedScene" uid="uid://b47goj32i6sdh" path="res://src/ui/input_prompt/input_prompt.tscn" id="14_ik4gg"]
[ext_resource type="PackedScene" uid="uid://b47goj32i6sdh" path="res://src/ui/elements/input_prompt/input_prompt.tscn" id="14_ik4gg"]
[sub_resource type="Animation" id="Animation_3xds6"]
resource_name = "RESET"
@ -654,6 +654,33 @@ fill_from = Vector2(0, 0.4)
fill_to = Vector2(1, 0.6)
metadata/_snap_enabled = true
[sub_resource type="Animation" id="Animation_3dab1"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Glint:anchor_left")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [-1.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Glint:anchor_right")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
[sub_resource type="Animation" id="Animation_ornnh"]
resource_name = "idle"
length = 6.0
@ -683,39 +710,26 @@ tracks/1/keys = {
"values": [0.0, 2.0]
}
[sub_resource type="Animation" id="Animation_3dab1"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Glint:anchor_left")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [-1.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Glint:anchor_right")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_bdkm3"]
_data = {
"RESET": SubResource("Animation_3dab1"),
"idle": SubResource("Animation_ornnh")
}
[sub_resource type="Animation" id="Animation_qjs4v"]
length = 0.001
tracks/0/type = "bezier"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position:y")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(-55, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
[sub_resource type="Animation" id="Animation_ll1u8"]
resource_name = "show"
length = 0.4
@ -731,20 +745,6 @@ tracks/0/keys = {
"times": PackedFloat32Array(0, 0.4)
}
[sub_resource type="Animation" id="Animation_qjs4v"]
length = 0.001
tracks/0/type = "bezier"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath(".:position:y")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"handle_modes": PackedInt32Array(0),
"points": PackedFloat32Array(-55, -0.25, 0, 0.25, 0),
"times": PackedFloat32Array(0)
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_o70c6"]
_data = {
"RESET": SubResource("Animation_qjs4v"),

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=9 format=3 uid="uid://cwnwcd8kushl3"]
[gd_scene load_steps=8 format=3 uid="uid://cwnwcd8kushl3"]
[ext_resource type="Script" path="res://src/world/world.gd" id="1_ybjyx"]
[ext_resource type="PackedScene" uid="uid://bm2o3mex10v11" path="res://levels/debug_level/debug_level.tscn" id="2_0xu5a"]
@ -6,11 +6,10 @@
[ext_resource type="Script" path="res://src/ui/world_ui.gd" id="2_imewa"]
[ext_resource type="Resource" uid="uid://crock3revdn73" path="res://src/player/debug_player.tres" id="3_pyw81"]
[ext_resource type="Script" path="res://src/world/play_manager/round_robin_manager.gd" id="5_h6mje"]
[ext_resource type="Resource" uid="uid://b5j0lmo4jayk4" path="res://src/player/debug_orange.tres" id="5_ym50e"]
[sub_resource type="Resource" id="Resource_rdjhi"]
script = ExtResource("5_h6mje")
players = Array[ExtResource("2_e743i")]([ExtResource("3_pyw81"), ExtResource("5_ym50e")])
players = Array[ExtResource("2_e743i")]([ExtResource("3_pyw81")])
[node name="World" type="Node" groups=["WorldGroup"]]
script = ExtResource("1_ybjyx")