Compare commits

...

3 Commits

Author SHA1 Message Date
Rob Kelly 01df07eb11 PromptMap includes modifiers 2024-12-08 19:44:30 -07:00
Rob Kelly ce2996a703 Added control binding elements 2024-12-08 19:32:21 -07:00
Rob Kelly 94d1ac8abf Added project settings menu 2024-12-08 16:21:05 -07:00
34 changed files with 1025 additions and 117 deletions

1
.gitignore vendored
View File

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

24
assets/text/text.csv Normal file
View File

@ -0,0 +1,24 @@
keys,en
ACTION_camera_forward,"Forward (free camera)"
ACTION_camera_back,"Backward (free camera)"
ACTION_camera_left,"Left (free camera)"
ACTION_camera_right,"Right (free camera)"
ACTION_camera_up,"Up (free camera)"
ACTION_camera_down,"Down (free camera)"
ACTION_camera_sprint,"Sprint (free camera)"
ACTION_camera_cancel,"Reset free camera"
ACTION_shot_zoom_in,"Zoom in"
ACTION_shot_zoom_out,"Zoom out"
ACTION_shot_accept,Shoot
ACTION_shot_cancel,"Cancel shot"
ACTION_shot_reset,"Reset shot"
ACTION_select_driver,"Select driver"
ACTION_select_iron,"Select iron"
ACTION_select_wedge,"Select wedge"
ACTION_select_special,"Select special"
ACTION_select_putter,"Select putter"
ACTION_club_next,"Select next club"
ACTION_club_previous,"Select previous club"
ACTION_pause,Pause
ACTION_ball_next,"Select next ball"
ACTION_ball_previous,"Select previous ball"
1 keys en
2 ACTION_camera_forward Forward (free camera)
3 ACTION_camera_back Backward (free camera)
4 ACTION_camera_left Left (free camera)
5 ACTION_camera_right Right (free camera)
6 ACTION_camera_up Up (free camera)
7 ACTION_camera_down Down (free camera)
8 ACTION_camera_sprint Sprint (free camera)
9 ACTION_camera_cancel Reset free camera
10 ACTION_shot_zoom_in Zoom in
11 ACTION_shot_zoom_out Zoom out
12 ACTION_shot_accept Shoot
13 ACTION_shot_cancel Cancel shot
14 ACTION_shot_reset Reset shot
15 ACTION_select_driver Select driver
16 ACTION_select_iron Select iron
17 ACTION_select_wedge Select wedge
18 ACTION_select_special Select special
19 ACTION_select_putter Select putter
20 ACTION_club_next Select next club
21 ACTION_club_previous Select previous club
22 ACTION_pause Pause
23 ACTION_ball_next Select next ball
24 ACTION_ball_previous Select previous ball

View File

@ -0,0 +1,17 @@
[remap]
importer="csv_translation"
type="Translation"
uid="uid://cqh1mly60nrmw"
[deps]
files=["res://assets/text/text.en.translation"]
source_file="res://assets/text/text.csv"
dest_files=["res://assets/text/text.en.translation"]
[params]
compress=true
delimiter=0

View File

@ -19,6 +19,7 @@ run/max_fps=60
[autoload] [autoload]
ClubCatalog="*res://src/equipment/clubs/club_catalog.tscn" ClubCatalog="*res://src/equipment/clubs/club_catalog.tscn"
GameSettings="*res://src/game/game_settings.gd"
[debug] [debug]
@ -197,6 +198,20 @@ ball_previous={
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":true,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
] ]
} }
club_next={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(105, 13),"global_position":Vector2(114, 59),"factor":1.0,"button_index":8,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
club_previous={
"deadzone": 0.5,
"events": [Object(InputEventMouseButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"button_mask":0,"position":Vector2(277, 2),"global_position":Vector2(286, 48),"factor":1.0,"button_index":9,"canceled":false,"pressed":true,"double_click":false,"script":null)
]
}
[internationalization]
locale/translations=PackedStringArray("res://assets/text/text.en.translation")
[layer_names] [layer_names]

View File

@ -13,6 +13,9 @@ var _loading_resources := {}
static var group := "GameGroup" # hey i'm group static var group := "GameGroup" # hey i'm group
## Typesafe accessor for GameSettings singleton
static var settings := GameSettings as GameSettingsType
class Promise: class Promise:
var _callbacks: Array[Callable] = [] 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 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 control_disabled := false
var reset_enabled := false: var reset_enabled := false:
@ -223,9 +206,15 @@ func _unhandled_input(event: InputEvent) -> void:
func camera_motion(motion: Vector2) -> void: func camera_motion(motion: Vector2) -> void:
if not control_disabled and phase == Phase.AIM: if not control_disabled and phase == Phase.AIM:
# Can only control camera while aiming # 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 = 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_MIN,
PITCH_MAX PITCH_MAX
) )
@ -493,9 +482,11 @@ func _process(delta: float) -> void:
## Visual updates ## Visual updates
# Rotation # Rotation
direction.rotation.y = lerp_angle( 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 lags behind camera control
arrow_pivot.rotation.y = lerp_angle( arrow_pivot.rotation.y = lerp_angle(

View File

@ -3,23 +3,6 @@ class_name FreeCamera extends CharacterBody3D
const SPRINT_MULT := 3.0 const SPRINT_MULT := 3.0
const PITCH_LIMIT := deg_to_rad(85.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) @onready var _target := Vector2(rotation.x, rotation.y)
static var scene := preload("res://src/ui/camera/free_camera/free_camera.tscn") 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: 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 = 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,
PITCH_LIMIT, PITCH_LIMIT,
) )
@ -44,15 +33,15 @@ func camera_motion(motion: Vector2) -> void:
func _physics_process(delta: float) -> void: func _physics_process(delta: float) -> void:
# Rotation # Rotation
rotation.y = lerp_angle(rotation.y, _target.y, delta * x_acceleration) rotation.y = lerp_angle(rotation.y, _target.y, delta * Game.settings.x_acceleration)
rotation.x = lerp_angle(rotation.x, _target.x, delta * y_acceleration) rotation.x = lerp_angle(rotation.x, _target.x, delta * Game.settings.y_acceleration)
# Handle spatial movement # Handle spatial movement
var xz_input := Input.get_vector("camera_left", "camera_right", "camera_forward", "camera_back") 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 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 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"): if Input.is_action_pressed("camera_sprint"):
speed *= SPRINT_MULT speed *= SPRINT_MULT

View File

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

View File

@ -0,0 +1,19 @@
@tool
class_name CheckerContainer extends PanelContainer
## PanelContainer that sets its theme based on its parent parity
@export var even_variation := "CheckerContainerEven"
@export var odd_variation := "CheckerContainerOdd"
func _ready() -> void:
get_parent().child_order_changed.connect(_recompute)
func _is_even_child() -> bool:
var parent := get_parent()
return parent.get_children().find(self) % 2 if parent else false
func _recompute() -> void:
theme_type_variation = even_variation if _is_even_child() else odd_variation

View File

@ -26,7 +26,7 @@ func _update() -> void:
var actions := InputMap.action_get_events(action) var actions := InputMap.action_get_events(action)
if actions: if actions:
var primary := actions[0] var primary := actions[0]
input_symbol = _get_input_symbol(primary) input_symbol = PromptMap.from_event(primary)
text = PROMPT_FORMAT.format( text = PROMPT_FORMAT.format(
[ [
@ -34,16 +34,3 @@ func _update() -> void:
label if label else UNKNOWN_LABEL_SYM label if label else UNKNOWN_LABEL_SYM
] ]
) )
func _get_input_symbol(event: InputEvent) -> String:
if event is InputEventKey:
return PromptMap.key(event as InputEventKey)
elif event is InputEventMouseButton:
return PromptMap.mouse_button(event as InputEventMouseButton)
elif event is InputEventJoypadButton:
return PromptMap.gamepad_button(event as InputEventJoypadButton)
elif event is InputEventJoypadMotion:
return PromptMap.gamepad_axis(event as InputEventJoypadMotion)
else:
return PromptMap.UNKNOWN_INPUT_SYMBOL

View File

@ -1,6 +1,6 @@
[gd_scene load_steps=2 format=3 uid="uid://b47goj32i6sdh"] [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"] [node name="InputPrompt" type="Label"]
anchors_preset = 15 anchors_preset = 15
@ -11,4 +11,3 @@ grow_vertical = 2
theme_type_variation = &"InputPrompt" theme_type_variation = &"InputPrompt"
text = "❓ - [unknown]" text = "❓ - [unknown]"
script = ExtResource("1_qq6w5") 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. ## Start playing a screen shake effect.
func screen_shake(intensity: float, duration: float = 0.2) -> void: 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 return
var tween := get_tree().create_tween() var tween := get_tree().create_tween()
@ -24,7 +24,7 @@ func screen_shake(intensity: float, duration: float = 0.2) -> void:
## Rumble the screen indefinitely. ## Rumble the screen indefinitely.
func set_rumble(intensity: float) -> void: 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 return
rumbler.intensity = intensity rumbler.intensity = intensity
@ -52,7 +52,7 @@ func hit_lag_huge() -> void:
## Stop processing for some number of frames. ## Stop processing for some number of frames.
func hit_lag(frames: int = 1) -> void: 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 return
_hit_lag_frames = frames _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=9 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://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"] [ext_resource type="FontFile" uid="uid://comihs66wounx" path="res://assets/fonts/Dokdo/Dokdo-Regular.ttf" id="1_eha6a"]
@ -6,9 +6,60 @@
[ext_resource type="FontFile" uid="uid://dyog4ex5nqfat" path="res://assets/fonts/promptfont/promptfont.otf" id="2_8kux8"] [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"] [ext_resource type="FontFile" uid="uid://s4c1kf0rk2mb" path="res://assets/fonts/Geo/Geo-Regular.ttf" id="3_cee6l"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rq1no"]
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)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
corner_detail = 5
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ja4y7"]
content_margin_left = 0.0
content_margin_top = 0.0
content_margin_right = 0.0
content_margin_bottom = 0.0
bg_color = Color(0.2, 0.2, 0.2, 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
[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] [resource]
default_font = ExtResource("3_cee6l") default_font = ExtResource("3_cee6l")
default_font_size = 18 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")
CheckerContainerEven/base_type = &"PanelContainer"
CheckerContainerEven/styles/panel = SubResource("StyleBoxFlat_rq1no")
CheckerContainerOdd/base_type = &"PanelContainer"
CheckerContainerOdd/styles/panel = SubResource("StyleBoxFlat_ja4y7")
ClubSelectLabel/base_type = &"Label" ClubSelectLabel/base_type = &"Label"
ClubSelectLabel/colors/font_color = Color(1, 0.933333, 0.894118, 1) ClubSelectLabel/colors/font_color = Color(1, 0.933333, 0.894118, 1)
ClubSelectLabel/colors/font_outline_color = Color(0, 0, 0, 1) ClubSelectLabel/colors/font_outline_color = Color(0, 0, 0, 1)
@ -24,12 +75,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/colors/font_shadow_color = Color(0.2, 0.2, 0.2, 1)
ClubSelectLabelDisabled/font_sizes/font_size = 84 ClubSelectLabelDisabled/font_sizes/font_size = 84
ClubSelectLabelDisabled/fonts/font = ExtResource("1_3rv2b") 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/base_type = &"Label"
InputPrompt/colors/font_color = Color(1, 1, 0.870588, 1) InputPrompt/colors/font_color = Color(1, 1, 0.870588, 1)
InputPrompt/constants/outline_size = 8 InputPrompt/constants/outline_size = 8
InputPrompt/font_sizes/font_size = 32 InputPrompt/font_sizes/font_size = 32
InputPrompt/fonts/font = ExtResource("2_8kux8") 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/colors/font_outline_color = Color(0, 0, 0, 1)
PauseMenuButton/constants/outline_size = 6 PauseMenuButton/constants/outline_size = 6
PauseMenuButton/font_sizes/font_size = 32 PauseMenuButton/font_sizes/font_size = 32
@ -39,9 +95,29 @@ 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/colors/font_outline_color = Color(1, 0.901961, 0.509804, 1)
QuantityLabel/constants/outline_size = 6 QuantityLabel/constants/outline_size = 6
QuantityLabel/font_sizes/font_size = 22 QuantityLabel/font_sizes/font_size = 22
SettingsInputLabel/base_type = &"Label"
SettingsInputLabel/colors/font_color = Color(1, 1, 0.870588, 1)
SettingsInputLabel/colors/font_outline_color = Color(0, 0, 0, 1)
SettingsInputLabel/constants/outline_size = 6
SettingsInputLabel/font_sizes/font_size = 36
SettingsInputLabel/fonts/font = ExtResource("2_5ty6u")
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/base_type = &"RichTextLabel"
ShotFeedback/colors/font_shadow_color = Color(0, 0, 0, 1) ShotFeedback/colors/font_shadow_color = Color(0, 0, 0, 1)
ShotFeedback/constants/shadow_offset_x = 6 ShotFeedback/constants/shadow_offset_x = 6
ShotFeedback/constants/shadow_offset_y = 4 ShotFeedback/constants/shadow_offset_y = 4
ShotFeedback/font_sizes/normal_font_size = 272 ShotFeedback/font_sizes/normal_font_size = 272
ShotFeedback/fonts/normal_font = ExtResource("1_eha6a") 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 extends Control
## Menu shown in-game when the user presses pause. ## 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 menu_list: VBoxContainer = %MenuList
@onready var quit_confirm: CenterContainer = %QuitConfirm @onready var quit_confirm: CenterContainer = %QuitConfirm
@onready var settings_container: MarginContainer = %SettingsContainer
func _ready() -> void: func _ready() -> void:
print_debug("Pause menu _ready()")
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE) Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
func _exit_tree() -> void: func _exit_tree() -> void:
print_debug("Pause menu _exit_tree()")
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED) Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
@ -30,12 +31,14 @@ func _hide() -> void:
func resume() -> void: func resume() -> void:
print_debug("Pause menu resume()")
queue_free() queue_free()
func settings() -> void: 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: func quit() -> void:

View File

@ -60,9 +60,25 @@ text = "Settings"
[node name="QuitButton" type="Button" parent="MarginContainer/MenuList"] [node name="QuitButton" type="Button" parent="MarginContainer/MenuList"]
layout_mode = 2 layout_mode = 2
theme_type_variation = &"PauseMenuButton" 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" 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="."] [node name="QuitConfirm" type="CenterContainer" parent="."]
unique_name_in_owner = true unique_name_in_owner = true
visible = false visible = false
@ -88,7 +104,9 @@ layout_mode = 2
[node name="Label" type="Label" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer"] [node name="Label" type="Label" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2 layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "Quit to desktop?" text = "Quit to desktop?"
horizontal_alignment = 1
[node name="HBoxContainer" type="HBoxContainer" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer"] [node name="HBoxContainer" type="HBoxContainer" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2 layout_mode = 2
@ -97,11 +115,13 @@ theme_override_constants/separation = 16
[node name="CancelButton" type="Button" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] [node name="CancelButton" type="Button" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_type_variation = &"CancelButton"
text = "Cancel" text = "Cancel"
[node name="ConfirmQuitButton" type="Button" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"] [node name="ConfirmQuitButton" type="Button" parent="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2 layout_mode = 2
size_flags_horizontal = 3 size_flags_horizontal = 3
theme_type_variation = &"AlertButton"
text = "Quit" text = "Quit"
[connection signal="pressed" from="MarginContainer/MenuList/ResumeButton" to="." method="resume"] [connection signal="pressed" from="MarginContainer/MenuList/ResumeButton" to="." method="resume"]

View File

@ -0,0 +1,33 @@
class_name ControlBinding extends CheckerContainer
## Input for rebinding an action.
const ACTION_KEY_FMT := "ACTION_{0}"
const SCENE := preload("res://src/ui/menus/settings_menu/control_binding/control_binding.tscn")
@export var key: StringName
@onready var action: Label = %Action
@onready var binding: Label = %Binding
func _ready() -> void:
# gdlint:ignore = private-method-call
super._ready()
# Set action label text
var loc_action := tr(ACTION_KEY_FMT.format([key]))
# Fall back to just the key if no localization exists
@warning_ignore("incompatible_ternary")
action.text = loc_action if loc_action else key
# Set the binding label
var actions := InputMap.action_get_events(key)
if actions:
var primary := actions[0]
binding.text = PromptMap.from_event(primary)
static func create(_key: StringName) -> ControlBinding:
var instance: ControlBinding = SCENE.instantiate()
instance.key = _key
return instance

View File

@ -0,0 +1,48 @@
[gd_scene load_steps=2 format=3 uid="uid://dwvpddd7id1h"]
[ext_resource type="Script" path="res://src/ui/menus/settings_menu/control_binding/control_binding.gd" id="1_7mwhu"]
[node name="ControlBinding" type="PanelContainer"]
theme_type_variation = &"CheckerContainerOdd"
script = ExtResource("1_7mwhu")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2
theme_override_constants/separation = 48
alignment = 2
[node name="Action" type="Label" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"SettingsInputLabel"
text = "Action"
[node name="Button" type="Button" parent="MarginContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(300, 42)
layout_mode = 2
[node name="Binding" type="Label" parent="MarginContainer/HBoxContainer/Button"]
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 = -13.0
offset_top = -19.5
offset_right = 13.0
offset_bottom = 19.5
grow_horizontal = 2
grow_vertical = 2
theme_type_variation = &"InputPrompt"
text = "unset"
horizontal_alignment = 1

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,9 @@
[gd_scene load_steps=2 format=3 uid="uid://dpry41u0ctikn"]
[ext_resource type="PackedScene" uid="uid://dcah6r3ku60g6" path="res://src/ui/menus/settings_menu/settings/setting/setting.tscn" id="1_km84n"]
[node name="DropdownSetting" instance=ExtResource("1_km84n")]
[node name="OptionButton" type="OptionButton" parent="PanelContainer/MarginContainer" index="0"]
layout_mode = 2
item_count = 1

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,47 @@
extends MarginContainer
## Menu allowing the user to adjust game configuration.
const SETTINGS_GROUP := "Settings"
const SETTINGS_FILE := "override.cfg"
@onready var control_binding_list: VBoxContainer = %ControlBindingList
func _ready() -> void:
populate_control_bindings()
func _get_settings_elements() -> Array[Setting]:
var elements: Array[Setting] = []
elements.assign(get_tree().get_nodes_in_group(SETTINGS_GROUP))
return elements
func populate_control_bindings() -> void:
InputMap.get
for action: StringName in InputMap.get_actions():
if not action.begins_with("ui_"):
control_binding_list.add_child(ControlBinding.create(action))
## 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,308 @@
[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 = 3
[node name="Game" type="MarginContainer" parent="TabContainer"]
visible = false
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"]
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="ControlBindingList" type="VBoxContainer" parent="TabContainer/Controls/VBoxContainer/ScrollContainer/MarginContainer"]
unique_name_in_owner = true
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="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="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://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"] [sub_resource type="Animation" id="Animation_3xds6"]
resource_name = "RESET" resource_name = "RESET"
@ -654,6 +654,33 @@ fill_from = Vector2(0, 0.4)
fill_to = Vector2(1, 0.6) fill_to = Vector2(1, 0.6)
metadata/_snap_enabled = true 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"] [sub_resource type="Animation" id="Animation_ornnh"]
resource_name = "idle" resource_name = "idle"
length = 6.0 length = 6.0
@ -683,39 +710,26 @@ tracks/1/keys = {
"values": [0.0, 2.0] "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"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_bdkm3"]
_data = { _data = {
"RESET": SubResource("Animation_3dab1"), "RESET": SubResource("Animation_3dab1"),
"idle": SubResource("Animation_ornnh") "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"] [sub_resource type="Animation" id="Animation_ll1u8"]
resource_name = "show" resource_name = "show"
length = 0.4 length = 0.4
@ -731,20 +745,6 @@ tracks/0/keys = {
"times": PackedFloat32Array(0, 0.4) "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"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_o70c6"]
_data = { _data = {
"RESET": SubResource("Animation_qjs4v"), "RESET": SubResource("Animation_qjs4v"),

View File

@ -1,6 +1,7 @@
class_name PromptMap class_name PromptMap
## Tools for mapping input events to PromptFont glyphs. ## Tools for mapping input events to PromptFont glyphs.
const COMPOSE_FMT := "{0}+{1}"
const UNKNOWN_INPUT_SYMBOL := PromptFont.ICON_QUESTION const UNKNOWN_INPUT_SYMBOL := PromptFont.ICON_QUESTION
## Keyboard key map ## Keyboard key map
@ -12,6 +13,7 @@ const KEYBOARD := {
KEY_CTRL: PromptFont.KEYBOARD_CONTROL, KEY_CTRL: PromptFont.KEYBOARD_CONTROL,
KEY_ALT: PromptFont.KEYBOARD_ALT, KEY_ALT: PromptFont.KEYBOARD_ALT,
KEY_SHIFT: PromptFont.KEYBOARD_SHIFT, KEY_SHIFT: PromptFont.KEYBOARD_SHIFT,
KEY_META: PromptFont.KEYBOARD_SUPER,
KEY_TAB: PromptFont.KEYBOARD_TAB, KEY_TAB: PromptFont.KEYBOARD_TAB,
KEY_CAPSLOCK: PromptFont.KEYBOARD_CAPS, KEY_CAPSLOCK: PromptFont.KEYBOARD_CAPS,
KEY_BACKSPACE: PromptFont.KEYBOARD_BACKSPACE, KEY_BACKSPACE: PromptFont.KEYBOARD_BACKSPACE,
@ -203,6 +205,20 @@ const NINTENDO_AXIS := {
} }
static func _compose_modifiers(event: InputEventWithModifiers, base: String) -> String:
# Control -> Alt -> Shift -> Super/Meta
var composed := base
if event.meta_pressed:
composed = COMPOSE_FMT.format([PromptFont.KEYBOARD_SUPER, composed])
if event.shift_pressed:
composed = COMPOSE_FMT.format([PromptFont.KEYBOARD_SHIFT, composed])
if event.alt_pressed:
composed = COMPOSE_FMT.format([PromptFont.KEYBOARD_ALT, composed])
if event.ctrl_pressed:
composed = COMPOSE_FMT.format([PromptFont.KEYBOARD_CONTROL, composed])
return composed
## Get the symbol representing the given keyboard input event. ## Get the symbol representing the given keyboard input event.
## ##
## If there is no such symbol available, returns the key label. ## If there is no such symbol available, returns the key label.
@ -211,12 +227,12 @@ static func key(event: InputEventKey) -> String:
return OS.get_keycode_string( return OS.get_keycode_string(
DisplayServer.keyboard_get_keycode_from_physical(event.physical_keycode) DisplayServer.keyboard_get_keycode_from_physical(event.physical_keycode)
) )
return KEYBOARD[event.physical_keycode] return _compose_modifiers(event, KEYBOARD[event.physical_keycode] as String)
## Get the symbol representing the given mouse button event. ## Get the symbol representing the given mouse button event.
static func mouse_button(event: InputEventMouseButton) -> String: static func mouse_button(event: InputEventMouseButton) -> String:
return MOUSE.get(event.button_index, UNKNOWN_INPUT_SYMBOL) return _compose_modifiers(event, MOUSE.get(event.button_index, UNKNOWN_INPUT_SYMBOL) as String)
## Get the symbol representing the given gamepad button event. ## Get the symbol representing the given gamepad button event.
@ -269,3 +285,16 @@ static func nintendo_button(event: InputEventJoypadButton) -> String:
## Symbols specific to Nintendo Switch joycons will be used where possible. ## Symbols specific to Nintendo Switch joycons will be used where possible.
static func nintendo_axis(event: InputEventJoypadMotion) -> String: static func nintendo_axis(event: InputEventJoypadMotion) -> String:
return NINTENDO_AXIS.get(event.axis, gamepad_axis(event)) return NINTENDO_AXIS.get(event.axis, gamepad_axis(event))
## Get the symbol representing the given event.
static func from_event(event: InputEvent) -> String:
if event is InputEventKey:
return key(event as InputEventKey)
if event is InputEventMouseButton:
return mouse_button(event as InputEventMouseButton)
if event is InputEventJoypadButton:
return gamepad_button(event as InputEventJoypadButton)
if event is InputEventJoypadMotion:
return gamepad_axis(event as InputEventJoypadMotion)
return UNKNOWN_INPUT_SYMBOL

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="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"] [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="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="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="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"] [sub_resource type="Resource" id="Resource_rdjhi"]
script = ExtResource("5_h6mje") 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"]] [node name="World" type="Node" groups=["WorldGroup"]]
script = ExtResource("1_ybjyx") script = ExtResource("1_ybjyx")