diff --git a/assets/text/text.csv b/assets/text/text.csv new file mode 100644 index 0000000..455bc02 --- /dev/null +++ b/assets/text/text.csv @@ -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" diff --git a/assets/text/text.csv.import b/assets/text/text.csv.import new file mode 100644 index 0000000..da6ce06 --- /dev/null +++ b/assets/text/text.csv.import @@ -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 diff --git a/project.godot b/project.godot index 7621a78..ccf16b9 100644 --- a/project.godot +++ b/project.godot @@ -198,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) ] } +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] diff --git a/src/ui/elements/checker_container.gd b/src/ui/elements/checker_container.gd new file mode 100644 index 0000000..663f3b7 --- /dev/null +++ b/src/ui/elements/checker_container.gd @@ -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 diff --git a/src/ui/elements/input_prompt/input_prompt.gd b/src/ui/elements/input_prompt/input_prompt.gd index 8b45eeb..562089b 100644 --- a/src/ui/elements/input_prompt/input_prompt.gd +++ b/src/ui/elements/input_prompt/input_prompt.gd @@ -26,7 +26,7 @@ func _update() -> void: var actions := InputMap.action_get_events(action) if actions: var primary := actions[0] - input_symbol = _get_input_symbol(primary) + input_symbol = PromptMap.from_event(primary) text = PROMPT_FORMAT.format( [ @@ -34,16 +34,3 @@ func _update() -> void: 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 diff --git a/src/ui/main_theme.tres b/src/ui/main_theme.tres index ecce430..10b3413 100644 --- a/src/ui/main_theme.tres +++ b/src/ui/main_theme.tres @@ -1,4 +1,4 @@ -[gd_resource type="Theme" load_steps=7 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://comihs66wounx" path="res://assets/fonts/Dokdo/Dokdo-Regular.ttf" id="1_eha6a"] @@ -6,6 +6,30 @@ [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_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 @@ -32,6 +56,10 @@ 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/colors/font_color = Color(1, 0.933333, 0.894118, 1) ClubSelectLabel/colors/font_outline_color = Color(0, 0, 0, 1) @@ -67,6 +95,12 @@ 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 +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" diff --git a/src/ui/menus/settings_menu/control_binding/control_binding.gd b/src/ui/menus/settings_menu/control_binding/control_binding.gd new file mode 100644 index 0000000..d9e7b6b --- /dev/null +++ b/src/ui/menus/settings_menu/control_binding/control_binding.gd @@ -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 diff --git a/src/ui/menus/settings_menu/control_binding/control_binding.tscn b/src/ui/menus/settings_menu/control_binding/control_binding.tscn new file mode 100644 index 0000000..f7f1ab0 --- /dev/null +++ b/src/ui/menus/settings_menu/control_binding/control_binding.tscn @@ -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 diff --git a/src/ui/menus/settings_menu/settings/dropdown_setting/dropdown_setting.tscn b/src/ui/menus/settings_menu/settings/dropdown_setting/dropdown_setting.tscn new file mode 100644 index 0000000..61efceb --- /dev/null +++ b/src/ui/menus/settings_menu/settings/dropdown_setting/dropdown_setting.tscn @@ -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 diff --git a/src/ui/menus/settings_menu/settings_menu.gd b/src/ui/menus/settings_menu/settings_menu.gd index 130b33f..7c21c27 100644 --- a/src/ui/menus/settings_menu/settings_menu.gd +++ b/src/ui/menus/settings_menu/settings_menu.gd @@ -4,6 +4,12 @@ extends MarginContainer 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] = [] @@ -11,6 +17,13 @@ func _get_settings_elements() -> Array[Setting]: 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() diff --git a/src/ui/menus/settings_menu/settings_menu.tscn b/src/ui/menus/settings_menu/settings_menu.tscn index 0be9045..1cefd14 100644 --- a/src/ui/menus/settings_menu/settings_menu.tscn +++ b/src/ui/menus/settings_menu/settings_menu.tscn @@ -14,9 +14,10 @@ script = ExtResource("1_lbcn7") [node name="TabContainer" type="TabContainer" parent="."] layout_mode = 2 -current_tab = 0 +current_tab = 3 [node name="Game" type="MarginContainer" parent="TabContainer"] +visible = false layout_mode = 2 theme_type_variation = &"SettingsPageContainer" metadata/_tab_index = 0 @@ -232,7 +233,6 @@ theme_type_variation = &"SettingsListMargin" layout_mode = 2 [node name="Controls" type="MarginContainer" parent="TabContainer"] -visible = false layout_mode = 2 theme_type_variation = &"SettingsPageContainer" metadata/_tab_index = 3 @@ -265,7 +265,8 @@ size_flags_horizontal = 3 size_flags_vertical = 3 theme_type_variation = &"SettingsListMargin" -[node name="SettingsList" type="VBoxContainer" parent="TabContainer/Controls/VBoxContainer/ScrollContainer/MarginContainer"] +[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="."] diff --git a/src/ui/elements/input_prompt/prompt_map.gd b/src/util/prompt_map.gd similarity index 95% rename from src/ui/elements/input_prompt/prompt_map.gd rename to src/util/prompt_map.gd index 1458c2d..edf3024 100644 --- a/src/ui/elements/input_prompt/prompt_map.gd +++ b/src/util/prompt_map.gd @@ -269,3 +269,16 @@ static func nintendo_button(event: InputEventJoypadButton) -> String: ## Symbols specific to Nintendo Switch joycons will be used where possible. static func nintendo_axis(event: InputEventJoypadMotion) -> String: 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