aboutsummaryrefslogtreecommitdiff
path: root/game/src/OptionMenu
diff options
context:
space:
mode:
author George L. Albany <Megacake1234@gmail.com>2023-02-21 22:30:12 +0100
committer GitHub <noreply@github.com>2023-02-21 22:30:12 +0100
commitf1c23555878ee4b0e40c6af5f89f05b666012309 (patch)
tree978dfdfd1ac6940414af5e19128060419076de76 /game/src/OptionMenu
parentfb9e316a18139ea6b6ffe3b237796b42d7114738 (diff)
Add Keychain plugin for Controls tab (#15)
* Add modified Keychain plugin for future Controls tab See https://github.com/Orama-Interactive/Keychain/tree/4.x Added Events autoload singleton for global eventing namespace Added Events.Options for global options functionality * Add Controls tab via Keychain plugin Use Events.Options for save, load, and reset of settings Separate OptionMenu tabs into scene files Add locale saving and loading Refactor SettingNodes scripts for more generalized use Remove random prints Remove useless spinbox signal connection Make Resolution consistently use Vector2i * Implement Godot project overrides for resolution and window mode Overrides are necessary as Godot does not load resolution or window mode on startup, so an override is necessary to ensure this happens. Add null checks to SettingHSlider and SettingOptionButton * Fix incorrect resolution value in ResolutionSelector * Correct project settings override behavior in editor Godot normally tries to overwrite the project settings in the editor, a template feature tag must be used to prevent the editor from overwriting the project.godot settings. * Fix Orama-Interactive/Keychain#8
Diffstat (limited to 'game/src/OptionMenu')
-rw-r--r--game/src/OptionMenu/ControlsTab.tscn14
-rw-r--r--game/src/OptionMenu/MonitorDisplaySelector.gd6
-rw-r--r--game/src/OptionMenu/OptionsMenu.gd48
-rw-r--r--game/src/OptionMenu/OptionsMenu.tscn130
-rw-r--r--game/src/OptionMenu/OtherTab.tscn18
-rw-r--r--game/src/OptionMenu/ResolutionSelector.gd62
-rw-r--r--game/src/OptionMenu/ScreenModeSelector.gd6
-rw-r--r--game/src/OptionMenu/SettingNodes/SettingHSlider.gd7
-rw-r--r--game/src/OptionMenu/SettingNodes/SettingOptionButton.gd51
-rw-r--r--game/src/OptionMenu/SoundTab.gd4
-rw-r--r--game/src/OptionMenu/SoundTab.tscn34
-rw-r--r--game/src/OptionMenu/VideoTab.tscn73
12 files changed, 273 insertions, 180 deletions
diff --git a/game/src/OptionMenu/ControlsTab.tscn b/game/src/OptionMenu/ControlsTab.tscn
new file mode 100644
index 0000000..b84dc85
--- /dev/null
+++ b/game/src/OptionMenu/ControlsTab.tscn
@@ -0,0 +1,14 @@
+[gd_scene load_steps=2 format=3 uid="uid://cdwymd51i4b2f"]
+
+[ext_resource type="PackedScene" uid="uid://by4gggse2nsdx" path="res://addons/keychain/ShortcutEdit.tscn" id="1_fv8sh"]
+
+[node name="Controls" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="ShortcutEdit" parent="." instance=ExtResource("1_fv8sh")]
+layout_mode = 1
diff --git a/game/src/OptionMenu/MonitorDisplaySelector.gd b/game/src/OptionMenu/MonitorDisplaySelector.gd
index 600b296..f2f0dc8 100644
--- a/game/src/OptionMenu/MonitorDisplaySelector.gd
+++ b/game/src/OptionMenu/MonitorDisplaySelector.gd
@@ -1,16 +1,14 @@
extends SettingOptionButton
-
-func _ready():
+func _setup_button():
clear()
for screen_index in range(DisplayServer.get_screen_count()):
add_item("Monitor %d" % (screen_index + 1))
+ default_selected = get_viewport().get_window().current_screen
func _on_item_selected(index):
- print("Selected index: %d" % index)
var window := get_viewport().get_window()
var mode := window.mode
window.mode = Window.MODE_WINDOWED
get_viewport().get_window().set_current_screen(index)
window.mode = mode
- print(get_viewport().get_window().current_screen)
diff --git a/game/src/OptionMenu/OptionsMenu.gd b/game/src/OptionMenu/OptionsMenu.gd
index e3c8433..5aba7f2 100644
--- a/game/src/OptionMenu/OptionsMenu.gd
+++ b/game/src/OptionMenu/OptionsMenu.gd
@@ -1,27 +1,9 @@
extends Control
-@export
-var user_settings_file_path : String = "settings.cfg"
-
signal back_button_pressed
-signal save_settings(save_file: ConfigFile)
-signal load_settings(load_file: ConfigFile)
-signal reset_settings()
-
-@onready
-var _settings_file_path := "user://" + user_settings_file_path
-var _settings_file := ConfigFile.new()
-
func _ready():
# Prepare options menu before loading user settings
-
- print("TODO: Load user settings!")
-
- if FileAccess.file_exists(_settings_file_path):
- _settings_file.load(_settings_file_path)
- load_settings.emit(_settings_file)
-
var tab_bar : TabBar = $Margin/Tab.get_child(0, true)
# This ends up easier to manage then trying to manually recreate the TabContainer's behavior
@@ -33,7 +15,7 @@ func _ready():
var reset_button := Button.new()
reset_button.text = "R"
- reset_button.pressed.connect(func(): reset_settings.emit())
+ reset_button.pressed.connect(Events.Options.try_reset_settings)
button_list.add_child(reset_button)
var back_button := Button.new()
@@ -42,6 +24,8 @@ func _ready():
button_list.add_child(back_button)
get_viewport().get_window().close_requested.connect(_on_window_close_requested)
+ _save_overrides.call_deferred()
+ Events.Options.save_settings.connect(func(_f): self._save_overrides.call_deferred())
func _notification(what):
match what:
@@ -51,22 +35,24 @@ func _notification(what):
# Could pass the LocaleButton between the MainMenu and OptionsMenu
# but that seems a bit excessive
func toggle_locale_button_visibility(locale_visible : bool):
- print("Toggling locale button: %s" % locale_visible)
$LocaleVBox/LocaleHBox/LocaleButton.visible = locale_visible
-func _on_ear_exploder_toggled(button_pressed):
- print("KABOOM!!!" if button_pressed else "DEFUSED!!!")
-
-
func _on_back_button_pressed():
- save_settings.emit(_settings_file)
- _settings_file.save(_settings_file_path)
+ Events.Options.save_settings_from_file()
back_button_pressed.emit()
-
-func _on_spin_box_value_changed(value):
- print("Spinbox: %d" % value)
-
func _on_window_close_requested() -> void:
if visible:
- _on_back_button_pressed()
+ Events.Options.save_settings_from_file()
+
+func _save_overrides() -> void:
+ var override_path := ProjectSettings.get_setting("application/config/project_settings_override") as String
+ if override_path == null or override_path.is_empty():
+ override_path = ProjectSettings.get_setting("openvic2/settings/settings_file_path") as String
+ var file := ConfigFile.new()
+ file.load(override_path)
+ file.set_value("display", "window/size/mode", get_viewport().get_window().mode)
+ var resolution : Vector2i = Resolution.get_current_resolution()
+ file.set_value("display", "window/size/viewport_width", resolution.x)
+ file.set_value("display", "window/size/viewport_height", resolution.y)
+ file.save(override_path)
diff --git a/game/src/OptionMenu/OptionsMenu.tscn b/game/src/OptionMenu/OptionsMenu.tscn
index 0ed531f..eafe37f 100644
--- a/game/src/OptionMenu/OptionsMenu.tscn
+++ b/game/src/OptionMenu/OptionsMenu.tscn
@@ -2,10 +2,10 @@
[ext_resource type="Script" path="res://src/OptionMenu/OptionsMenu.gd" id="1_tlein"]
[ext_resource type="PackedScene" uid="uid://b7oncobnacxmt" path="res://src/LocaleButton.tscn" id="2_d7wvq"]
-[ext_resource type="Script" path="res://src/OptionMenu/ResolutionSelector.gd" id="2_jk1ey"]
-[ext_resource type="Script" path="res://src/OptionMenu/ScreenModeSelector.gd" id="3_hsicf"]
-[ext_resource type="Script" path="res://src/OptionMenu/MonitorDisplaySelector.gd" id="3_q1cm3"]
-[ext_resource type="PackedScene" uid="uid://dy4si8comamnv" path="res://src/OptionMenu/VolumeGrid.tscn" id="4_n4oqr"]
+[ext_resource type="PackedScene" uid="uid://bq3awxxjn1tuw" path="res://src/OptionMenu/VideoTab.tscn" id="2_ji8xr"]
+[ext_resource type="PackedScene" uid="uid://cbtgwpx2wxi33" path="res://src/OptionMenu/SoundTab.tscn" id="3_4w35t"]
+[ext_resource type="PackedScene" uid="uid://bq7ibhm0txl5p" path="res://addons/keychain/ShortcutEdit.tscn" id="4_vdhjp"]
+[ext_resource type="PackedScene" uid="uid://dp2grvybtecqu" path="res://src/OptionMenu/OtherTab.tscn" id="5_ahefp"]
[node name="OptionsMenu" type="Control"]
layout_mode = 3
@@ -34,114 +34,18 @@ size_flags_vertical = 3
tab_alignment = 1
use_hidden_tabs_for_min_size = true
-[node name="Video" type="HBoxContainer" parent="Margin/Tab"]
+[node name="Video" parent="Margin/Tab" instance=ExtResource("2_ji8xr")]
layout_mode = 2
-tooltip_text = "This is my cool and very nice tooltip"
-alignment = 1
-[node name="VBoxContainer" type="VBoxContainer" parent="Margin/Tab/Video"]
-layout_mode = 2
-
-[node name="Control" type="Control" parent="Margin/Tab/Video/VBoxContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-size_flags_stretch_ratio = 0.1
-
-[node name="GridContainer" type="GridContainer" parent="Margin/Tab/Video/VBoxContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-columns = 2
-
-[node name="ResolutionLabel" type="Label" parent="Margin/Tab/Video/VBoxContainer/GridContainer"]
-layout_mode = 2
-text = "Resolution"
-
-[node name="ResolutionSelector" type="OptionButton" parent="Margin/Tab/Video/VBoxContainer/GridContainer"]
-layout_mode = 2
-item_count = 1
-selected = 0
-popup/item_0/text = "MISSING"
-popup/item_0/id = 0
-script = ExtResource("2_jk1ey")
-section_name = "Video"
-setting_name = "Resolution"
-default_value = -1
-
-[node name="ScreenModeLabel" type="Label" parent="Margin/Tab/Video/VBoxContainer/GridContainer"]
-layout_mode = 2
-text = "Screen Mode"
-
-[node name="ScreenModeSelector" type="OptionButton" parent="Margin/Tab/Video/VBoxContainer/GridContainer"]
-layout_mode = 2
-item_count = 3
-selected = 0
-popup/item_0/text = "Fullscreen"
-popup/item_0/id = 0
-popup/item_1/text = "Borderless"
-popup/item_1/id = 1
-popup/item_2/text = "Windowed"
-popup/item_2/id = 2
-script = ExtResource("3_hsicf")
-section_name = "Video"
-setting_name = "Mode Selected"
-
-[node name="MonitorSelectionLabel" type="Label" parent="Margin/Tab/Video/VBoxContainer/GridContainer"]
-layout_mode = 2
-text = "Monitor Selection"
-
-[node name="MonitorDisplaySelector" type="OptionButton" parent="Margin/Tab/Video/VBoxContainer/GridContainer"]
-layout_mode = 2
-item_count = 1
-selected = 0
-popup/item_0/text = "MISSING"
-popup/item_0/id = 0
-script = ExtResource("3_q1cm3")
-section_name = "Video"
-setting_name = "Current Screen"
-
-[node name="Sound" type="HBoxContainer" parent="Margin/Tab"]
+[node name="Sound" parent="Margin/Tab" instance=ExtResource("3_4w35t")]
visible = false
layout_mode = 2
-alignment = 1
-
-[node name="VBoxContainer" type="VBoxContainer" parent="Margin/Tab/Sound"]
-layout_mode = 2
-
-[node name="Control" type="Control" parent="Margin/Tab/Sound/VBoxContainer"]
-layout_mode = 2
-size_flags_vertical = 3
-size_flags_stretch_ratio = 0.1
-
-[node name="VolumeGrid" parent="Margin/Tab/Sound/VBoxContainer" instance=ExtResource("4_n4oqr")]
-layout_mode = 2
-
-[node name="ButtonGrid" type="GridContainer" parent="Margin/Tab/Sound/VBoxContainer"]
-layout_mode = 2
-size_flags_vertical = 2
-columns = 2
-
-[node name="Spacer" type="Control" parent="Margin/Tab/Sound/VBoxContainer/ButtonGrid"]
-layout_mode = 2
-size_flags_horizontal = 3
-
-[node name="EarExploder" type="CheckButton" parent="Margin/Tab/Sound/VBoxContainer/ButtonGrid"]
-layout_mode = 2
-text = "Explode Eardrums on Startup?"
-[node name="Other" type="Control" parent="Margin/Tab"]
+[node name="Controls" parent="Margin/Tab" instance=ExtResource("4_vdhjp")]
visible = false
layout_mode = 2
-[node name="HBoxContainer" type="HBoxContainer" parent="Margin/Tab/Other"]
-layout_mode = 0
-offset_right = 40.0
-offset_bottom = 40.0
-
-[node name="Label" type="Label" parent="Margin/Tab/Other/HBoxContainer"]
-layout_mode = 2
-text = "Spinbox Example :)"
-
-[node name="SpinBox" type="SpinBox" parent="Margin/Tab/Other/HBoxContainer"]
+[node name="Other" parent="Margin/Tab" instance=ExtResource("5_ahefp")]
layout_mode = 2
[node name="LocaleVBox" type="VBoxContainer" parent="."]
@@ -161,21 +65,3 @@ alignment = 2
[node name="LocaleButton" parent="LocaleVBox/LocaleHBox" instance=ExtResource("2_d7wvq")]
layout_mode = 2
-
-[connection signal="load_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/ResolutionSelector" method="load_setting"]
-[connection signal="load_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/ScreenModeSelector" method="load_setting"]
-[connection signal="load_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/MonitorDisplaySelector" method="load_setting"]
-[connection signal="load_settings" from="." to="Margin/Tab/Sound/VBoxContainer/VolumeGrid" method="_on_options_menu_load_settings"]
-[connection signal="reset_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/ResolutionSelector" method="reset_setting"]
-[connection signal="reset_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/ScreenModeSelector" method="reset_setting"]
-[connection signal="reset_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/MonitorDisplaySelector" method="reset_setting"]
-[connection signal="reset_settings" from="." to="Margin/Tab/Sound/VBoxContainer/VolumeGrid" method="_on_options_menu_reset_settings"]
-[connection signal="save_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/ResolutionSelector" method="save_setting"]
-[connection signal="save_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/ScreenModeSelector" method="save_setting"]
-[connection signal="save_settings" from="." to="Margin/Tab/Video/VBoxContainer/GridContainer/MonitorDisplaySelector" method="save_setting"]
-[connection signal="save_settings" from="." to="Margin/Tab/Sound/VBoxContainer/VolumeGrid" method="_on_options_menu_save_settings"]
-[connection signal="item_selected" from="Margin/Tab/Video/VBoxContainer/GridContainer/ResolutionSelector" to="Margin/Tab/Video/VBoxContainer/GridContainer/ResolutionSelector" method="_on_item_selected"]
-[connection signal="item_selected" from="Margin/Tab/Video/VBoxContainer/GridContainer/ScreenModeSelector" to="Margin/Tab/Video/VBoxContainer/GridContainer/ScreenModeSelector" method="_on_item_selected"]
-[connection signal="item_selected" from="Margin/Tab/Video/VBoxContainer/GridContainer/MonitorDisplaySelector" to="Margin/Tab/Video/VBoxContainer/GridContainer/MonitorDisplaySelector" method="_on_item_selected"]
-[connection signal="toggled" from="Margin/Tab/Sound/VBoxContainer/ButtonGrid/EarExploder" to="." method="_on_ear_exploder_toggled"]
-[connection signal="value_changed" from="Margin/Tab/Other/HBoxContainer/SpinBox" to="." method="_on_spin_box_value_changed"]
diff --git a/game/src/OptionMenu/OtherTab.tscn b/game/src/OptionMenu/OtherTab.tscn
new file mode 100644
index 0000000..0ffc92d
--- /dev/null
+++ b/game/src/OptionMenu/OtherTab.tscn
@@ -0,0 +1,18 @@
+[gd_scene format=3 uid="uid://dp2grvybtecqu"]
+
+[node name="Other" type="Control"]
+visible = false
+layout_mode = 3
+anchors_preset = 0
+
+[node name="HBoxContainer" type="HBoxContainer" parent="."]
+layout_mode = 0
+offset_right = 40.0
+offset_bottom = 40.0
+
+[node name="Label" type="Label" parent="HBoxContainer"]
+layout_mode = 2
+text = "Spinbox Example :)"
+
+[node name="SpinBox" type="SpinBox" parent="HBoxContainer"]
+layout_mode = 2
diff --git a/game/src/OptionMenu/ResolutionSelector.gd b/game/src/OptionMenu/ResolutionSelector.gd
index ef1a0ff..e602bab 100644
--- a/game/src/OptionMenu/ResolutionSelector.gd
+++ b/game/src/OptionMenu/ResolutionSelector.gd
@@ -1,24 +1,58 @@
extends SettingOptionButton
-func _ready():
- print("Resolution selector ready")
+@export
+var default_value : Vector2i = Vector2i(-1, -1)
+
+func add_resolution(value : Vector2i, selection_name : String = "") -> void:
+ if selection_name.is_empty():
+ selection_name = "%sx%s" % [value.x, value.y]
+ add_item(selection_name)
+ set_item_metadata(item_count - 1, value)
+
+func find_resolution_value(value : Vector2i) -> int:
+ for item_index in range(item_count):
+ if get_item_metadata(item_index) == value:
+ return item_index
+ return -1
+
+func _setup_button():
+ if default_value.x < 0:
+ default_value.x = ProjectSettings.get_setting("display/window/size/viewport_width")
+
+ if default_value.y < 0:
+ default_value.y = ProjectSettings.get_setting("display/window/size/viewport_height")
clear()
- var resolution_index := 0
+ default_selected = -1
+ selected = -1
for resolution in Resolution.get_resolution_name_list():
- add_item(resolution)
+ var resolution_value := Resolution.get_resolution(resolution)
+ add_resolution(resolution_value, resolution)
- if Vector2(Resolution.get_resolution(resolution)) == Resolution.get_current_resolution():
- if default_value == -1:
- default_value = resolution_index
- _select_int(resolution_index)
- print(resolution)
+ if resolution_value == default_value:
+ default_selected = item_count - 1
- resolution_index += 1
+ if resolution_value == Resolution.get_current_resolution():
+ selected = item_count - 1
+ if default_selected == -1:
+ add_resolution(default_value)
+ default_selected = item_count - 1
-func _on_item_selected(index):
- print("Selected index: %d" % index)
+ if selected == -1:
+ selected = default_selected
+
+func _get_value_for_file(select_value : int):
+ return get_item_metadata(select_value)
- var resolution_size : Vector2i = Resolution.get_resolution(get_item_text(index))
- Resolution.set_resolution(resolution_size)
+func _set_value_from_file(load_value):
+ var resolution_value := load_value as Vector2i
+ selected = find_resolution_value(resolution_value)
+ if selected == -1:
+ if add_nonstandard_value:
+ add_resolution(resolution_value)
+ selected = item_count - 1
+ else: push_error("Setting value '%s' invalid for setting [%s] %s" % [load_value, section_name, setting_name])
+
+func _on_item_selected(index):
+ Resolution.set_resolution(get_item_metadata(index))
diff --git a/game/src/OptionMenu/ScreenModeSelector.gd b/game/src/OptionMenu/ScreenModeSelector.gd
index fae0771..92c5d60 100644
--- a/game/src/OptionMenu/ScreenModeSelector.gd
+++ b/game/src/OptionMenu/ScreenModeSelector.gd
@@ -24,9 +24,11 @@ func get_window_mode_from_screen_mode(screen_mode : int) -> Window.Mode:
_:
return Window.MODE_EXCLUSIVE_FULLSCREEN
-func _on_item_selected(index : int):
- print("Selected index: %d" % index)
+func _setup_button():
+ default_selected = get_screen_mode_from_window_mode(get_viewport().get_window().mode)
+ selected = default_selected
+func _on_item_selected(index : int):
var window := get_viewport().get_window()
var current_resolution := Resolution.get_current_resolution()
window.mode = get_window_mode_from_screen_mode(index)
diff --git a/game/src/OptionMenu/SettingNodes/SettingHSlider.gd b/game/src/OptionMenu/SettingNodes/SettingHSlider.gd
index da9348f..cf2adf4 100644
--- a/game/src/OptionMenu/SettingNodes/SettingHSlider.gd
+++ b/game/src/OptionMenu/SettingNodes/SettingHSlider.gd
@@ -10,10 +10,17 @@ var setting_name : String = "SettingHSlider"
@export
var default_value : float = 0
+func _ready():
+ Events.Options.load_settings.connect(load_setting)
+ Events.Options.save_settings.connect(save_setting)
+ Events.Options.reset_settings.connect(reset_setting)
+
func load_setting(file : ConfigFile):
+ if file == null: return
value = file.get_value(section_name, setting_name, default_value)
func save_setting(file : ConfigFile):
+ if file == null: return
file.set_value(section_name, setting_name, value)
func reset_setting():
diff --git a/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd b/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd
index 46fc825..3a5c979 100644
--- a/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd
+++ b/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd
@@ -8,15 +8,52 @@ var section_name : String = "Setting"
var setting_name : String = "SettingOptionMenu"
@export
-var default_value : int = 0
+var default_selected : int = -1:
+ get: return default_selected
+ set(v):
+ if v == -1:
+ default_selected = -1
+ return
+ default_selected = v % item_count
-func load_setting(file : ConfigFile):
- selected = file.get_value(section_name, setting_name, default_value)
+@export
+var add_nonstandard_value := false
+
+func _get_value_for_file(select_value : int):
+ if select_value > -1:
+ return get_item_text(select_value)
+ else:
+ return null
+
+func _set_value_from_file(load_value) -> void:
+ selected = -1
+ for item_index in range(item_count):
+ if load_value == get_item_text(item_index):
+ selected = item_index
+ if selected == -1:
+ if add_nonstandard_value:
+ add_item(load_value)
+ selected = item_count - 1
+ else: push_error("Setting value '%s' invalid for setting [%s] \"%s\"" % [load_value, section_name, setting_name])
+
+func _setup_button() -> void:
+ pass
+
+func _ready():
+ Events.Options.load_settings.connect(load_setting)
+ Events.Options.save_settings.connect(save_setting)
+ Events.Options.reset_settings.connect(reset_setting)
+ _setup_button()
+
+func load_setting(file : ConfigFile) -> void:
+ if file == null: return
+ _set_value_from_file(file.get_value(section_name, setting_name, _get_value_for_file(default_selected)))
item_selected.emit(selected)
-func save_setting(file : ConfigFile):
- file.set_value(section_name, setting_name, selected)
+func save_setting(file : ConfigFile) -> void:
+ if file == null: return
+ file.set_value(section_name, setting_name, _get_value_for_file(selected))
-func reset_setting():
- selected = default_value
+func reset_setting() -> void:
+ selected = default_selected
item_selected.emit(selected)
diff --git a/game/src/OptionMenu/SoundTab.gd b/game/src/OptionMenu/SoundTab.gd
new file mode 100644
index 0000000..c707605
--- /dev/null
+++ b/game/src/OptionMenu/SoundTab.gd
@@ -0,0 +1,4 @@
+extends HBoxContainer
+
+func _on_ear_exploder_toggled(button_pressed):
+ print("KABOOM!!!" if button_pressed else "DEFUSED!!!")
diff --git a/game/src/OptionMenu/SoundTab.tscn b/game/src/OptionMenu/SoundTab.tscn
new file mode 100644
index 0000000..10d7f10
--- /dev/null
+++ b/game/src/OptionMenu/SoundTab.tscn
@@ -0,0 +1,34 @@
+[gd_scene load_steps=3 format=3 uid="uid://cbtgwpx2wxi33"]
+
+[ext_resource type="Script" path="res://src/OptionMenu/SoundTab.gd" id="1_a7k0s"]
+[ext_resource type="PackedScene" uid="uid://dy4si8comamnv" path="res://src/OptionMenu/VolumeGrid.tscn" id="1_okpft"]
+
+[node name="Sound" type="HBoxContainer"]
+alignment = 1
+script = ExtResource("1_a7k0s")
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="Control" type="Control" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+size_flags_stretch_ratio = 0.1
+
+[node name="VolumeGrid" parent="VBoxContainer" instance=ExtResource("1_okpft")]
+layout_mode = 2
+
+[node name="ButtonGrid" type="GridContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 2
+columns = 2
+
+[node name="Spacer" type="Control" parent="VBoxContainer/ButtonGrid"]
+layout_mode = 2
+size_flags_horizontal = 3
+
+[node name="EarExploder" type="CheckButton" parent="VBoxContainer/ButtonGrid"]
+layout_mode = 2
+text = "Explode Eardrums on Startup?"
+
+[connection signal="toggled" from="VBoxContainer/ButtonGrid/EarExploder" to="." method="_on_ear_exploder_toggled"]
diff --git a/game/src/OptionMenu/VideoTab.tscn b/game/src/OptionMenu/VideoTab.tscn
new file mode 100644
index 0000000..d46f056
--- /dev/null
+++ b/game/src/OptionMenu/VideoTab.tscn
@@ -0,0 +1,73 @@
+[gd_scene load_steps=4 format=3 uid="uid://bq3awxxjn1tuw"]
+
+[ext_resource type="Script" path="res://src/OptionMenu/ResolutionSelector.gd" id="1_i8nro"]
+[ext_resource type="Script" path="res://src/OptionMenu/ScreenModeSelector.gd" id="2_wa7vw"]
+[ext_resource type="Script" path="res://src/OptionMenu/MonitorDisplaySelector.gd" id="3_y6lyb"]
+
+[node name="Video" type="HBoxContainer"]
+tooltip_text = "This is my cool and very nice tooltip"
+alignment = 1
+
+[node name="VBoxContainer" type="VBoxContainer" parent="."]
+layout_mode = 2
+
+[node name="Control" type="Control" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+size_flags_stretch_ratio = 0.1
+
+[node name="GridContainer" type="GridContainer" parent="VBoxContainer"]
+layout_mode = 2
+size_flags_vertical = 3
+columns = 2
+
+[node name="ResolutionLabel" type="Label" parent="VBoxContainer/GridContainer"]
+layout_mode = 2
+text = "Resolution"
+
+[node name="ResolutionSelector" type="OptionButton" parent="VBoxContainer/GridContainer"]
+layout_mode = 2
+item_count = 1
+selected = 0
+popup/item_0/text = "MISSING"
+popup/item_0/id = 0
+script = ExtResource("1_i8nro")
+section_name = "Video"
+setting_name = "Resolution"
+add_nonstandard_value = true
+
+[node name="ScreenModeLabel" type="Label" parent="VBoxContainer/GridContainer"]
+layout_mode = 2
+text = "Screen Mode"
+
+[node name="ScreenModeSelector" type="OptionButton" parent="VBoxContainer/GridContainer"]
+layout_mode = 2
+item_count = 3
+selected = 0
+popup/item_0/text = "Fullscreen"
+popup/item_0/id = 0
+popup/item_1/text = "Borderless"
+popup/item_1/id = 1
+popup/item_2/text = "Windowed"
+popup/item_2/id = 2
+script = ExtResource("2_wa7vw")
+section_name = "Video"
+setting_name = "Mode Selected"
+
+[node name="MonitorSelectionLabel" type="Label" parent="VBoxContainer/GridContainer"]
+layout_mode = 2
+text = "Monitor Selection"
+
+[node name="MonitorDisplaySelector" type="OptionButton" parent="VBoxContainer/GridContainer"]
+layout_mode = 2
+item_count = 1
+selected = 0
+popup/item_0/text = "MISSING"
+popup/item_0/id = 0
+script = ExtResource("3_y6lyb")
+section_name = "Video"
+setting_name = "Current Screen"
+
+[connection signal="item_selected" from="VBoxContainer/GridContainer/ResolutionSelector" to="VBoxContainer/GridContainer/ResolutionSelector" method="_on_item_selected"]
+[connection signal="item_selected" from="VBoxContainer/GridContainer/ScreenModeSelector" to="VBoxContainer/GridContainer/ScreenModeSelector" method="_on_item_selected"]
+[connection signal="item_selected" from="VBoxContainer/GridContainer/MonitorDisplaySelector" to="VBoxContainer/GridContainer/MonitorDisplaySelector" method="_on_item_selected"]