aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md8
-rw-r--r--extension/src/Simulation.hpp52
-rw-r--r--extension/src/TestSingleton.hpp2
-rw-r--r--extension/src/register_types.cpp51
-rw-r--r--game/default_bus_layout.tres15
-rw-r--r--game/icon.svg.import2
-rw-r--r--game/project.godot24
-rw-r--r--game/src/Autoload/Resolution.gd43
-rw-r--r--game/src/GameMenu.gd13
-rw-r--r--game/src/GameMenu.tscn41
-rw-r--r--game/src/LocaleButton.gd26
-rw-r--r--game/src/LocaleButton.tscn12
-rw-r--r--game/src/MainMenu.gd13
-rw-r--r--game/src/MainMenu.tscn60
-rw-r--r--game/src/OptionMenu/MonitorDisplaySelector.gd16
-rw-r--r--game/src/OptionMenu/OptionsMenu.gd72
-rw-r--r--game/src/OptionMenu/OptionsMenu.tscn181
-rw-r--r--game/src/OptionMenu/ResolutionSelector.gd24
-rw-r--r--game/src/OptionMenu/ScreenModeSelector.gd33
-rw-r--r--game/src/OptionMenu/SettingNodes/SettingHSlider.gd20
-rw-r--r--game/src/OptionMenu/SettingNodes/SettingOptionButton.gd22
-rw-r--r--game/src/OptionMenu/VolumeGrid.gd54
-rw-r--r--game/src/OptionMenu/VolumeGrid.tscn8
-rw-r--r--game/src/OptionsMenu.gd43
-rw-r--r--game/src/OptionsMenu.tscn141
-rw-r--r--game/src/SampleGame.gd40
-rw-r--r--game/src/SampleGame.tscn64
m---------godot-cpp0
28 files changed, 833 insertions, 247 deletions
diff --git a/README.md b/README.md
index 409c75d..ecbd504 100644
--- a/README.md
+++ b/README.md
@@ -2,20 +2,20 @@
Main Repo for the OpenVic2 Project
## Required
-* [Godot 4 Beta 16](https://downloads.tuxfamily.org/godotengine/4.0/beta16/)
+* [Godot 4 RC 1](https://downloads.tuxfamily.org/godotengine/4.0/rc1/)
* [scons](https://scons.org/)
## Build/Run Instructions
-1. Install [Godot 4 Beta 16](https://downloads.tuxfamily.org/godotengine/4.0/beta16/) and [scons](https://scons.org/) for your system.
+1. Install [Godot 4 RC 1](https://downloads.tuxfamily.org/godotengine/4.0/rc1/) and [scons](https://scons.org/) for your system.
2. Run the command `git submodule update --init --recursive` to retrieve all related submodules.
3. Run `scons` in the project root, you should see a libopenvic2 file in `game/bin/openvic2`.
-4. Open with Godot 4 Beta 16, click import and navigate to the `game` directory.
+4. Open with Godot 4 RC 1, click import and navigate to the `game` directory.
5. Import and edit.
6. Once loaded, click the play button at the top right, if you see `Hello GDExtension Singleton!` in the output at the bottom then it is working.
## Project Export
1. Build the extension with `scons` or `scons target=template_debug`. (or `scons target=template_release` for release)
-2. Open `game/project.godot` with Godot 4 Beta 16.
+2. Open `game/project.godot` with Godot 4 RC 1.
3. Click `Project` at the top left, click `Export`.
4. If you do not have the templates, you must download the templates, there is highlighted white text at the bottom of the Export subwindow that opens up the template manager for you to download.
5. Click `Export All`:
diff --git a/extension/src/Simulation.hpp b/extension/src/Simulation.hpp
new file mode 100644
index 0000000..e16f34b
--- /dev/null
+++ b/extension/src/Simulation.hpp
@@ -0,0 +1,52 @@
+#pragma once
+
+#include <godot_cpp/classes/object.hpp>
+#include <godot_cpp/core/class_db.hpp>
+#include <godot_cpp/variant/utility_functions.hpp>
+#include <vector>
+
+namespace OpenVic2 {
+ class Simulation : public godot::Object {
+ GDCLASS(Simulation, godot::Object)
+ std::vector<size_t> exampleProvinces;
+
+ //BEGIN BOILERPLATE
+ static Simulation* _simulation;
+
+ protected:
+ static void _bind_methods() {
+ godot::ClassDB::bind_method(godot::D_METHOD("conductSimulationStep"), &Simulation::conductSimulationStep);
+ godot::ClassDB::bind_method(godot::D_METHOD("queryProvinceSize"), &Simulation::queryProvinceSize);
+ }
+
+ public:
+ inline static Simulation* get_singleton() { return _simulation; }
+
+ inline Simulation() {
+ ERR_FAIL_COND(_simulation != nullptr);
+ _simulation = this;
+
+ exampleProvinces.resize(10, 1);
+ }
+ inline ~Simulation() {
+ ERR_FAIL_COND(_simulation != this);
+ _simulation = nullptr;
+ }
+ //END BOILERPLATE
+
+ inline void conductSimulationStep() {
+ for (size_t x = 0; x < exampleProvinces.size(); x++) {
+ exampleProvinces[x] += (x + 1);
+ }
+ }
+
+ inline size_t queryProvinceSize(size_t provinceID) {
+ if (provinceID >= exampleProvinces.size()) {
+ return 0;
+ }
+ return exampleProvinces[provinceID];
+ }
+ };
+
+ Simulation* Simulation::_simulation = nullptr;
+} \ No newline at end of file
diff --git a/extension/src/TestSingleton.hpp b/extension/src/TestSingleton.hpp
index 0a591ac..de27589 100644
--- a/extension/src/TestSingleton.hpp
+++ b/extension/src/TestSingleton.hpp
@@ -6,7 +6,7 @@
namespace OpenVic2 {
class TestSingleton : public godot::Object
{
- GDCLASS(TestSingleton, godot::Object);
+ GDCLASS(TestSingleton, godot::Object)
static TestSingleton *singleton;
diff --git a/extension/src/register_types.cpp b/extension/src/register_types.cpp
index 775e6c3..4b049ce 100644
--- a/extension/src/register_types.cpp
+++ b/extension/src/register_types.cpp
@@ -6,46 +6,55 @@
#include <godot_cpp/classes/engine.hpp>
#include "TestSingleton.hpp"
+#include "Simulation.hpp"
using namespace godot;
using namespace OpenVic2;
-static TestSingleton *_test_singleton;
+static TestSingleton* _test_singleton;
+static Simulation* _simulation;
void initialize_openvic2_types(ModuleInitializationLevel p_level)
{
- if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
- return;
- }
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
- ClassDB::register_class<TestSingleton>();
+ ClassDB::register_class<TestSingleton>();
+ _test_singleton = memnew(TestSingleton);
+ Engine::get_singleton()->register_singleton("TestSingleton", TestSingleton::get_singleton());
+
+ ClassDB::register_class<Simulation>();
+ _simulation = memnew(Simulation);
+ Engine::get_singleton()->register_singleton("Simulation", Simulation::get_singleton());
- _test_singleton = memnew(TestSingleton);
- Engine::get_singleton()->register_singleton("TestSingleton", TestSingleton::get_singleton());
}
void uninitialize_openvic2_types(ModuleInitializationLevel p_level) {
- if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
- return;
- }
+ if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) {
+ return;
+ }
+
+ Engine::get_singleton()->unregister_singleton("TestSingleton");
+ memdelete(_test_singleton);
- Engine::get_singleton()->unregister_singleton("TestSingleton");
- memdelete(_test_singleton);
+ Engine::get_singleton()->unregister_singleton("Simulation");
+ memdelete(_test_singleton);
}
extern "C"
{
- // Initialization.
+ // Initialization.
- GDExtensionBool GDE_EXPORT openvic2_library_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
- {
- GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization);
+ GDExtensionBool GDE_EXPORT openvic2_library_init(const GDExtensionInterface *p_interface, const GDExtensionClassLibraryPtr p_library, GDExtensionInitialization *r_initialization)
+ {
+ GDExtensionBinding::InitObject init_obj(p_interface, p_library, r_initialization);
- init_obj.register_initializer(initialize_openvic2_types);
- init_obj.register_terminator(uninitialize_openvic2_types);
- init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
+ init_obj.register_initializer(initialize_openvic2_types);
+ init_obj.register_terminator(uninitialize_openvic2_types);
+ init_obj.set_minimum_library_initialization_level(MODULE_INITIALIZATION_LEVEL_SCENE);
- return init_obj.init();
- }
+ return init_obj.init();
+ }
} \ No newline at end of file
diff --git a/game/default_bus_layout.tres b/game/default_bus_layout.tres
new file mode 100644
index 0000000..87413af
--- /dev/null
+++ b/game/default_bus_layout.tres
@@ -0,0 +1,15 @@
+[gd_resource type="AudioBusLayout" format=3 uid="uid://cquqx51trot64"]
+
+[resource]
+bus/1/name = &"Music"
+bus/1/solo = false
+bus/1/mute = false
+bus/1/bypass_fx = false
+bus/1/volume_db = 0.0
+bus/1/send = &"Master"
+bus/2/name = &"SFX"
+bus/2/solo = false
+bus/2/mute = false
+bus/2/bypass_fx = false
+bus/2/volume_db = 0.0
+bus/2/send = &"Master"
diff --git a/game/icon.svg.import b/game/icon.svg.import
index b124f12..00642a8 100644
--- a/game/icon.svg.import
+++ b/game/icon.svg.import
@@ -16,9 +16,9 @@ dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.cte
[params]
compress/mode=0
+compress/high_quality=false
compress/lossy_quality=0.7
compress/hdr_compression=1
-compress/bptc_ldr=0
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
diff --git a/game/project.godot b/game/project.godot
index 653cc91..d66f91f 100644
--- a/game/project.godot
+++ b/game/project.godot
@@ -11,6 +11,28 @@ config_version=5
[application]
config/name="OpenVic2"
-run/main_scene="res://src/MainMenu.tscn"
+run/main_scene="res://src/GameMenu.tscn"
config/features=PackedStringArray("4.0", "Forward Plus")
config/icon="res://icon.svg"
+
+[autoload]
+
+Resolution="*res://src/Autoload/Resolution.gd"
+
+[display]
+
+window/size/viewport_width=1280
+window/size/viewport_height=720
+window/size/mode=3
+window/size/resizable=false
+window/stretch/mode="canvas_items"
+window/stretch/aspect="ignore"
+
+[internationalization]
+
+locale/translation_remaps={}
+locale/fallback="en_GB"
+locale/locale_filter_mode=0
+locale/country_short_name={
+"United States of America": "USA"
+}
diff --git a/game/src/Autoload/Resolution.gd b/game/src/Autoload/Resolution.gd
new file mode 100644
index 0000000..cde46f5
--- /dev/null
+++ b/game/src/Autoload/Resolution.gd
@@ -0,0 +1,43 @@
+extends Node
+
+const _resolutions := {
+ &"3840x2160": Vector2i(3840,2160),
+ &"2560x1440": Vector2i(2560,1080),
+ &"1920x1080": Vector2i(1920,1080),
+ &"1366x768": Vector2i(1366,768),
+ &"1536x864": Vector2i(1536,864),
+ &"1280x720": Vector2i(1280,720),
+ &"1440x900": Vector2i(1440,900),
+ &"1600x900": Vector2i(1600,900),
+ &"1024x600": Vector2i(1024,600),
+ &"800x600": Vector2i(800,600)
+}
+
+func has_resolution(resolution_name : StringName) -> bool:
+ return resolution_name in _resolutions
+
+func get_resolution(resolution_name : StringName, default : Vector2i = Vector2i(1920, 1080)) -> Vector2i:
+ return _resolutions.get(resolution_name, default)
+
+func get_resolution_name_list() -> Array:
+ return _resolutions.keys()
+
+func get_current_resolution() -> Vector2:
+ var window := get_viewport().get_window()
+ match window.mode:
+ Window.MODE_EXCLUSIVE_FULLSCREEN, Window.MODE_FULLSCREEN:
+ return window.content_scale_size
+ _:
+ return window.size
+
+func set_resolution(resolution : Vector2) -> void:
+ var window := get_viewport().get_window()
+ match window.mode:
+ Window.MODE_EXCLUSIVE_FULLSCREEN, Window.MODE_FULLSCREEN:
+ window.content_scale_size = resolution
+ _:
+ window.size = resolution
+ window.content_scale_size = Vector2i(0,0)
+
+func reset_resolution() -> void:
+ set_resolution(get_current_resolution())
diff --git a/game/src/GameMenu.gd b/game/src/GameMenu.gd
new file mode 100644
index 0000000..1f5a77e
--- /dev/null
+++ b/game/src/GameMenu.gd
@@ -0,0 +1,13 @@
+extends Control
+
+
+func _on_main_menu_options_button_pressed():
+ $OptionsMenu.toggle_locale_button_visibility(false)
+ $OptionsMenu.show()
+ $MainMenu.hide()
+
+
+func _on_options_menu_back_button_pressed():
+ $MainMenu.show()
+ $OptionsMenu.hide()
+ $OptionsMenu.toggle_locale_button_visibility(true)
diff --git a/game/src/GameMenu.tscn b/game/src/GameMenu.tscn
new file mode 100644
index 0000000..24f1c6c
--- /dev/null
+++ b/game/src/GameMenu.tscn
@@ -0,0 +1,41 @@
+[gd_scene load_steps=5 format=3 uid="uid://b4pg2y2ivib8f"]
+
+[ext_resource type="Script" path="res://src/GameMenu.gd" id="1_cafwe"]
+[ext_resource type="PackedScene" uid="uid://dvoin538iby54" path="res://src/MainMenu.tscn" id="1_y4m2a"]
+[ext_resource type="PackedScene" uid="uid://cnbfxjy1m6wja" path="res://src/OptionMenu/OptionsMenu.tscn" id="3_111lv"]
+[ext_resource type="PackedScene" uid="uid://b7oncobnacxmt" path="res://src/LocaleButton.tscn" id="4_jno35"]
+
+[node name="GameMenu" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_cafwe")
+
+[node name="MainMenu" parent="." instance=ExtResource("1_y4m2a")]
+layout_mode = 1
+
+[node name="OptionsMenu" parent="." instance=ExtResource("3_111lv")]
+visible = false
+layout_mode = 1
+
+[node name="HBox" type="HBoxContainer" parent="."]
+layout_mode = 1
+anchors_preset = 3
+anchor_left = 1.0
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+offset_left = -150.0
+offset_top = -20.0
+grow_horizontal = 0
+grow_vertical = 0
+alignment = 2
+
+[node name="LocaleButton" parent="HBox" instance=ExtResource("4_jno35")]
+layout_mode = 2
+
+[connection signal="options_button_pressed" from="MainMenu" to="." method="_on_main_menu_options_button_pressed"]
+[connection signal="back_button_pressed" from="OptionsMenu" to="." method="_on_options_menu_back_button_pressed"]
diff --git a/game/src/LocaleButton.gd b/game/src/LocaleButton.gd
new file mode 100644
index 0000000..eed815e
--- /dev/null
+++ b/game/src/LocaleButton.gd
@@ -0,0 +1,26 @@
+extends OptionButton
+
+var _locales_country_rename : Dictionary
+var _locales_list : Array[String]
+
+func _ready():
+ print("Loading locale button")
+
+ _locales_country_rename = ProjectSettings.get_setting("internationalization/locale/country_short_name", {})
+
+ _locales_list = [TranslationServer.get_locale()]
+ _locales_list.append_array(TranslationServer.get_loaded_locales())
+
+ for locale in _locales_list:
+ var locale_name := TranslationServer.get_locale_name(locale)
+ var locale_first_part := locale_name.get_slice(", ", 0)
+ var locale_second_part := locale_name.substr(locale_first_part.length() + 2)
+ if locale_second_part in _locales_country_rename:
+ locale_second_part = _locales_country_rename[locale_second_part]
+
+ add_item("%s, %s" % [locale_first_part, locale_second_part])
+
+
+func _on_item_selected(index):
+ print("Selected locale " + _locales_list[index])
+ TranslationServer.set_locale(_locales_list[index])
diff --git a/game/src/LocaleButton.tscn b/game/src/LocaleButton.tscn
new file mode 100644
index 0000000..55f1c29
--- /dev/null
+++ b/game/src/LocaleButton.tscn
@@ -0,0 +1,12 @@
+[gd_scene load_steps=2 format=3 uid="uid://b7oncobnacxmt"]
+
+[ext_resource type="Script" path="res://src/LocaleButton.gd" id="1_ganev"]
+
+[node name="LocaleButton" type="OptionButton"]
+custom_minimum_size = Vector2(150, 0)
+alignment = 2
+text_overrun_behavior = 2
+fit_to_longest_item = false
+script = ExtResource("1_ganev")
+
+[connection signal="item_selected" from="." to="." method="_on_item_selected"]
diff --git a/game/src/MainMenu.gd b/game/src/MainMenu.gd
index a9cdf10..de49ec4 100644
--- a/game/src/MainMenu.gd
+++ b/game/src/MainMenu.gd
@@ -1,21 +1,16 @@
extends Control
+signal options_button_pressed
-# Called when the node enters the scene tree for the first time.
func _ready():
print("From GDScript")
TestSingleton.hello_singleton()
- $CenterContainer/VBoxContainer/NewGameButton.grab_focus()
- pass # Replace with function body.
-
-
-## Called every frame. 'delta' is the elapsed time since the previous frame.
-#func _process(delta):
-# pass
+ $VBox/Center/VBox/NewGameButton.grab_focus()
func _on_new_game_button_pressed():
print("Start a new game!")
+ get_tree().change_scene_to_file("res://src/SampleGame.tscn")
func _on_continue_button_pressed():
@@ -28,7 +23,7 @@ func _on_multi_player_button_pressed():
func _on_options_button_pressed():
print("Check out some options!")
- get_tree().change_scene_to_file("res://src/OptionsMenu.tscn")
+ options_button_pressed.emit()
func _on_exit_button_pressed():
diff --git a/game/src/MainMenu.tscn b/game/src/MainMenu.tscn
index 5a2cb8a..587eb01 100644
--- a/game/src/MainMenu.tscn
+++ b/game/src/MainMenu.tscn
@@ -1,58 +1,66 @@
-[gd_scene load_steps=2 format=3 uid="uid://b4pg2y2ivib8f"]
+[gd_scene load_steps=2 format=3 uid="uid://dvoin538iby54"]
[ext_resource type="Script" path="res://src/MainMenu.gd" id="1_6akq8"]
[node name="MainMenu" type="Control"]
layout_mode = 3
-anchors_preset = 0
-offset_right = 1152.0
-offset_bottom = 648.0
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
script = ExtResource("1_6akq8")
-[node name="Label" type="Label" parent="."]
-layout_mode = 0
-offset_top = 137.0
-offset_right = 1152.0
-offset_bottom = 264.0
+[node name="VBox" type="VBoxContainer" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+
+[node name="TitleLabel" type="Label" parent="VBox"]
+layout_mode = 2
+size_flags_vertical = 6
+size_flags_stretch_ratio = 1.5
theme_override_font_sizes/font_size = 90
text = "OpenVic2"
horizontal_alignment = 1
+vertical_alignment = 1
-[node name="CenterContainer" type="CenterContainer" parent="."]
-layout_mode = 0
-offset_top = 268.0
-offset_right = 1152.0
-offset_bottom = 648.0
+[node name="Center" type="CenterContainer" parent="VBox"]
+layout_mode = 2
+size_flags_vertical = 2
-[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer"]
+[node name="VBox" type="VBoxContainer" parent="VBox/Center"]
layout_mode = 2
-[node name="NewGameButton" type="Button" parent="CenterContainer/VBoxContainer" node_paths=PackedStringArray("shortcut_context")]
+[node name="NewGameButton" type="Button" parent="VBox/Center/VBox" node_paths=PackedStringArray("shortcut_context")]
layout_mode = 2
focus_neighbor_top = NodePath("../ExitButton")
shortcut_context = NodePath("")
text = "New Game"
-[node name="ContinueButton" type="Button" parent="CenterContainer/VBoxContainer"]
+[node name="ContinueButton" type="Button" parent="VBox/Center/VBox"]
layout_mode = 2
disabled = true
text = "Continue"
-[node name="MultiPlayerButton" type="Button" parent="CenterContainer/VBoxContainer"]
+[node name="MultiplayerButton" type="Button" parent="VBox/Center/VBox"]
layout_mode = 2
-text = "Multi-Player"
+text = "Multipayer"
-[node name="OptionsButton" type="Button" parent="CenterContainer/VBoxContainer"]
+[node name="OptionsButton" type="Button" parent="VBox/Center/VBox"]
layout_mode = 2
text = "Options"
-[node name="ExitButton" type="Button" parent="CenterContainer/VBoxContainer"]
+[node name="ExitButton" type="Button" parent="VBox/Center/VBox"]
layout_mode = 2
focus_neighbor_bottom = NodePath("../NewGameButton")
text = "Exit"
-[connection signal="pressed" from="CenterContainer/VBoxContainer/NewGameButton" to="." method="_on_new_game_button_pressed"]
-[connection signal="pressed" from="CenterContainer/VBoxContainer/ContinueButton" to="." method="_on_continue_button_pressed"]
-[connection signal="pressed" from="CenterContainer/VBoxContainer/MultiPlayerButton" to="." method="_on_multi_player_button_pressed"]
-[connection signal="pressed" from="CenterContainer/VBoxContainer/OptionsButton" to="." method="_on_options_button_pressed"]
-[connection signal="pressed" from="CenterContainer/VBoxContainer/ExitButton" to="." method="_on_exit_button_pressed"]
+[connection signal="pressed" from="VBox/Center/VBox/NewGameButton" to="." method="_on_new_game_button_pressed"]
+[connection signal="pressed" from="VBox/Center/VBox/ContinueButton" to="." method="_on_continue_button_pressed"]
+[connection signal="pressed" from="VBox/Center/VBox/MultiplayerButton" to="." method="_on_multi_player_button_pressed"]
+[connection signal="pressed" from="VBox/Center/VBox/OptionsButton" to="." method="_on_options_button_pressed"]
+[connection signal="pressed" from="VBox/Center/VBox/ExitButton" to="." method="_on_exit_button_pressed"]
diff --git a/game/src/OptionMenu/MonitorDisplaySelector.gd b/game/src/OptionMenu/MonitorDisplaySelector.gd
new file mode 100644
index 0000000..600b296
--- /dev/null
+++ b/game/src/OptionMenu/MonitorDisplaySelector.gd
@@ -0,0 +1,16 @@
+extends SettingOptionButton
+
+
+func _ready():
+ clear()
+ for screen_index in range(DisplayServer.get_screen_count()):
+ add_item("Monitor %d" % (screen_index + 1))
+
+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
new file mode 100644
index 0000000..e3c8433
--- /dev/null
+++ b/game/src/OptionMenu/OptionsMenu.gd
@@ -0,0 +1,72 @@
+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
+ # These buttons can be accessed regardless of the tab
+ var button_list := HBoxContainer.new()
+ button_list.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
+ button_list.alignment = BoxContainer.ALIGNMENT_END
+ tab_bar.add_child(button_list)
+
+ var reset_button := Button.new()
+ reset_button.text = "R"
+ reset_button.pressed.connect(func(): reset_settings.emit())
+ button_list.add_child(reset_button)
+
+ var back_button := Button.new()
+ back_button.text = "X"
+ back_button.pressed.connect(_on_back_button_pressed)
+ button_list.add_child(back_button)
+
+ get_viewport().get_window().close_requested.connect(_on_window_close_requested)
+
+func _notification(what):
+ match what:
+ NOTIFICATION_CRASH:
+ _on_window_close_requested()
+
+# 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)
+ 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()
diff --git a/game/src/OptionMenu/OptionsMenu.tscn b/game/src/OptionMenu/OptionsMenu.tscn
new file mode 100644
index 0000000..0ed531f
--- /dev/null
+++ b/game/src/OptionMenu/OptionsMenu.tscn
@@ -0,0 +1,181 @@
+[gd_scene load_steps=7 format=3 uid="uid://cnbfxjy1m6wja"]
+
+[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"]
+
+[node name="OptionsMenu" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_tlein")
+
+[node name="Margin" type="MarginContainer" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+theme_override_constants/margin_left = 250
+theme_override_constants/margin_top = 100
+theme_override_constants/margin_right = 250
+theme_override_constants/margin_bottom = 200
+
+[node name="Tab" type="TabContainer" parent="Margin"]
+layout_mode = 2
+size_flags_vertical = 3
+tab_alignment = 1
+use_hidden_tabs_for_min_size = true
+
+[node name="Video" type="HBoxContainer" parent="Margin/Tab"]
+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"]
+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"]
+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"]
+layout_mode = 2
+
+[node name="LocaleVBox" type="VBoxContainer" parent="."]
+layout_mode = 1
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+mouse_filter = 2
+alignment = 2
+
+[node name="LocaleHBox" type="HBoxContainer" parent="LocaleVBox"]
+layout_mode = 2
+mouse_filter = 2
+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/ResolutionSelector.gd b/game/src/OptionMenu/ResolutionSelector.gd
new file mode 100644
index 0000000..ef1a0ff
--- /dev/null
+++ b/game/src/OptionMenu/ResolutionSelector.gd
@@ -0,0 +1,24 @@
+extends SettingOptionButton
+
+func _ready():
+ print("Resolution selector ready")
+
+ clear()
+ var resolution_index := 0
+ for resolution in Resolution.get_resolution_name_list():
+ add_item(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)
+
+ resolution_index += 1
+
+
+func _on_item_selected(index):
+ print("Selected index: %d" % index)
+
+ var resolution_size : Vector2i = Resolution.get_resolution(get_item_text(index))
+ Resolution.set_resolution(resolution_size)
diff --git a/game/src/OptionMenu/ScreenModeSelector.gd b/game/src/OptionMenu/ScreenModeSelector.gd
new file mode 100644
index 0000000..fae0771
--- /dev/null
+++ b/game/src/OptionMenu/ScreenModeSelector.gd
@@ -0,0 +1,33 @@
+extends SettingOptionButton
+
+enum ScreenMode { Unknown = -1, Fullscreen, Borderless, Windowed }
+
+func get_screen_mode_from_window_mode(window_mode : int) -> ScreenMode:
+ match window_mode:
+ Window.MODE_EXCLUSIVE_FULLSCREEN:
+ return ScreenMode.Fullscreen
+ Window.MODE_FULLSCREEN:
+ return ScreenMode.Borderless
+ Window.MODE_WINDOWED:
+ return ScreenMode.Windowed
+ _:
+ return ScreenMode.Unknown
+
+func get_window_mode_from_screen_mode(screen_mode : int) -> Window.Mode:
+ match screen_mode:
+ ScreenMode.Fullscreen:
+ return Window.MODE_EXCLUSIVE_FULLSCREEN
+ ScreenMode.Borderless:
+ return Window.MODE_FULLSCREEN
+ ScreenMode.Windowed:
+ return Window.MODE_WINDOWED
+ _:
+ return Window.MODE_EXCLUSIVE_FULLSCREEN
+
+func _on_item_selected(index : int):
+ print("Selected index: %d" % index)
+
+ var window := get_viewport().get_window()
+ var current_resolution := Resolution.get_current_resolution()
+ window.mode = get_window_mode_from_screen_mode(index)
+ Resolution.set_resolution(current_resolution)
diff --git a/game/src/OptionMenu/SettingNodes/SettingHSlider.gd b/game/src/OptionMenu/SettingNodes/SettingHSlider.gd
new file mode 100644
index 0000000..da9348f
--- /dev/null
+++ b/game/src/OptionMenu/SettingNodes/SettingHSlider.gd
@@ -0,0 +1,20 @@
+extends HSlider
+class_name SettingHSlider
+
+@export
+var section_name : String = "Setting"
+
+@export
+var setting_name : String = "SettingHSlider"
+
+@export
+var default_value : float = 0
+
+func load_setting(file : ConfigFile):
+ value = file.get_value(section_name, setting_name, default_value)
+
+func save_setting(file : ConfigFile):
+ file.set_value(section_name, setting_name, value)
+
+func reset_setting():
+ value = default_value
diff --git a/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd b/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd
new file mode 100644
index 0000000..46fc825
--- /dev/null
+++ b/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd
@@ -0,0 +1,22 @@
+extends OptionButton
+class_name SettingOptionButton
+
+@export
+var section_name : String = "Setting"
+
+@export
+var setting_name : String = "SettingOptionMenu"
+
+@export
+var default_value : int = 0
+
+func load_setting(file : ConfigFile):
+ selected = file.get_value(section_name, setting_name, default_value)
+ item_selected.emit(selected)
+
+func save_setting(file : ConfigFile):
+ file.set_value(section_name, setting_name, selected)
+
+func reset_setting():
+ selected = default_value
+ item_selected.emit(selected)
diff --git a/game/src/OptionMenu/VolumeGrid.gd b/game/src/OptionMenu/VolumeGrid.gd
new file mode 100644
index 0000000..fae5ff6
--- /dev/null
+++ b/game/src/OptionMenu/VolumeGrid.gd
@@ -0,0 +1,54 @@
+extends GridContainer
+
+const RATIO_FOR_LINEAR = 100
+
+var _slider_dictionary := {}
+
+func get_db_as_volume_value(db : float) -> float:
+ # db_to_linear produces a float between 0 and 1 from a db value
+ return db_to_linear(db) * RATIO_FOR_LINEAR
+
+func get_volume_value_as_db(value : float) -> float:
+ # linear_to_db consumes a float between 0 and 1 to produce the db value
+ return linear_to_db(value / RATIO_FOR_LINEAR)
+
+func add_volume_column(bus_name : StringName, bus_index : int) -> HSlider:
+ var volume_label := Label.new()
+ volume_label.text = bus_name + " Volume"
+ add_child(volume_label)
+
+ var volume_slider := SettingHSlider.new()
+ volume_slider.section_name = "Audio"
+ volume_slider.setting_name = volume_label.text
+ volume_slider.custom_minimum_size = Vector2(290, 0)
+ volume_slider.size_flags_vertical = Control.SIZE_FILL
+ volume_slider.min_value = 0
+ volume_slider.default_value = 100
+ volume_slider.max_value = 120 # 120 so volume can be boosted somewhat
+ volume_slider.value_changed.connect(_on_slider_value_changed.bind(bus_index))
+ add_child(volume_slider)
+
+ _slider_dictionary[volume_label.text] = volume_slider
+ return volume_slider
+
+func _ready():
+ for bus_index in AudioServer.bus_count:
+ add_volume_column(AudioServer.get_bus_name(bus_index), bus_index)
+
+func _on_slider_value_changed(value : float, bus_index : int) -> void:
+ AudioServer.set_bus_volume_db(bus_index, get_volume_value_as_db(value))
+
+
+func _on_options_menu_load_settings(load_file : ConfigFile):
+ for volume_label_text in _slider_dictionary:
+ _slider_dictionary[volume_label_text].load_setting(load_file)
+
+
+func _on_options_menu_save_settings(save_file : ConfigFile):
+ for volume_label_text in _slider_dictionary:
+ _slider_dictionary[volume_label_text].save_setting(save_file)
+
+
+func _on_options_menu_reset_settings():
+ for volume_label_text in _slider_dictionary:
+ _slider_dictionary[volume_label_text].reset_setting()
diff --git a/game/src/OptionMenu/VolumeGrid.tscn b/game/src/OptionMenu/VolumeGrid.tscn
new file mode 100644
index 0000000..6d4de3c
--- /dev/null
+++ b/game/src/OptionMenu/VolumeGrid.tscn
@@ -0,0 +1,8 @@
+[gd_scene load_steps=2 format=3 uid="uid://dy4si8comamnv"]
+
+[ext_resource type="Script" path="res://src/OptionMenu/VolumeGrid.gd" id="1_wb64h"]
+
+[node name="VolumeGrid" type="GridContainer"]
+size_flags_vertical = 0
+columns = 2
+script = ExtResource("1_wb64h")
diff --git a/game/src/OptionsMenu.gd b/game/src/OptionsMenu.gd
deleted file mode 100644
index adcd3b5..0000000
--- a/game/src/OptionsMenu.gd
+++ /dev/null
@@ -1,43 +0,0 @@
-extends Control
-
-
-# Called when the node enters the scene tree for the first time.
-func _ready():
- print("TODO: Load user settings!")
- pass # Replace with function body.
-
-## Called every frame. 'delta' is the elapsed time since the previous frame.
-#func _process(delta):
-# pass
-
-func _on_resolution_selector_item_selected(index):
- print("Selected index: %d" % index)
-
-func _on_screen_mode_selector_item_selected(index):
- print("Selected index: %d" % index)
-
-func _on_monitor_display_selector_item_selected(index):
- print("Selected index: %d" % index)
-
-func _on_music_volume_value_changed(value):
- print("Music: %f" % value)
-
-
-func _on_sfx_volume_value_changed(value):
- print("SFX: %f" % value)
-
-
-func _on_ear_exploder_toggled(button_pressed):
- print("KABOOM!!!" if button_pressed else "DEFUSED!!!")
-
-
-func _on_save_settings_button_pressed():
- print("TODO: save current settings!")
-
-
-func _on_back_button_pressed():
- get_tree().change_scene_to_file("res://src/MainMenu.tscn")
-
-
-func _on_spin_box_value_changed(value):
- print("Spinbox: %d" % value)
diff --git a/game/src/OptionsMenu.tscn b/game/src/OptionsMenu.tscn
deleted file mode 100644
index 495d72c..0000000
--- a/game/src/OptionsMenu.tscn
+++ /dev/null
@@ -1,141 +0,0 @@
-[gd_scene load_steps=2 format=3 uid="uid://ch03lp7d7fvw3"]
-
-[ext_resource type="Script" path="res://src/OptionsMenu.gd" id="1_2tajv"]
-
-[node name="OptionsMenu" type="Control"]
-layout_mode = 3
-anchors_preset = 15
-anchor_right = 1.0
-anchor_bottom = 1.0
-grow_horizontal = 2
-grow_vertical = 2
-script = ExtResource("1_2tajv")
-
-[node name="TabContainer" type="TabContainer" parent="."]
-layout_mode = 0
-offset_right = 1152.0
-offset_bottom = 648.0
-
-[node name="VideoTab" type="TabBar" parent="TabContainer"]
-layout_mode = 2
-tooltip_text = "This is my cool and very nice tooltip"
-
-[node name="GridContainer" type="GridContainer" parent="TabContainer/VideoTab"]
-layout_mode = 0
-offset_right = 40.0
-offset_bottom = 40.0
-columns = 2
-
-[node name="Label2" type="Label" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-text = "Resolution:"
-
-[node name="ResolutionSelector" type="OptionButton" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-item_count = 3
-selected = 0
-popup/item_0/text = "1920x1080"
-popup/item_0/id = 0
-popup/item_1/text = "1366x768"
-popup/item_1/id = 1
-popup/item_2/text = "1280x1024"
-popup/item_2/id = 2
-
-[node name="Label3" type="Label" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-text = "Screen Mode:"
-
-[node name="ScreenModeSelector" type="OptionButton" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-item_count = 3
-selected = 0
-popup/item_0/text = "Borderless"
-popup/item_0/id = 0
-popup/item_1/text = "Windowed"
-popup/item_1/id = 1
-popup/item_2/text = "Fullscreen"
-popup/item_2/id = 2
-
-[node name="Label" type="Label" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-text = "Monitor Selection:"
-
-[node name="MonitorDisplaySelector" type="OptionButton" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-item_count = 1
-selected = 0
-popup/item_0/text = "Monitor 1"
-popup/item_0/id = 0
-
-[node name="BackButton" type="Button" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-text = "Back to Main Menu"
-
-[node name="SaveSettingsButton" type="Button" parent="TabContainer/VideoTab/GridContainer"]
-layout_mode = 2
-text = "Save Me!"
-
-[node name="SoundTab" type="TabBar" parent="TabContainer"]
-visible = false
-layout_mode = 2
-
-[node name="GridContainer" type="GridContainer" parent="TabContainer/SoundTab"]
-layout_mode = 0
-offset_top = 33.0
-offset_right = 1152.0
-offset_bottom = 648.0
-columns = 2
-
-[node name="Label" type="Label" parent="TabContainer/SoundTab/GridContainer"]
-layout_mode = 2
-text = "Music Volume"
-horizontal_alignment = 2
-
-[node name="MusicVolume" type="HSlider" parent="TabContainer/SoundTab/GridContainer"]
-layout_mode = 2
-value = 100.0
-
-[node name="Label3" type="Label" parent="TabContainer/SoundTab/GridContainer"]
-layout_mode = 2
-text = "SFX Volume"
-horizontal_alignment = 2
-
-[node name="SfxVolume" type="HSlider" parent="TabContainer/SoundTab/GridContainer"]
-layout_mode = 2
-value = 100.0
-
-[node name="Label2" type="Label" parent="TabContainer/SoundTab/GridContainer"]
-layout_mode = 2
-horizontal_alignment = 2
-
-[node name="EarExploder" type="CheckButton" parent="TabContainer/SoundTab/GridContainer"]
-layout_mode = 2
-text = "Explode Eardrums on Startup?"
-
-[node name="OtherTab" type="TabBar" parent="TabContainer"]
-visible = false
-layout_mode = 2
-
-[node name="HBoxContainer" type="HBoxContainer" parent="TabContainer/OtherTab"]
-layout_mode = 0
-offset_right = 40.0
-offset_bottom = 40.0
-
-[node name="Label" type="Label" parent="TabContainer/OtherTab/HBoxContainer"]
-layout_mode = 2
-text = "Spinbox Example :)"
-
-[node name="SpinBox" type="SpinBox" parent="TabContainer/OtherTab/HBoxContainer"]
-layout_mode = 2
-
-[connection signal="item_selected" from="TabContainer/VideoTab/GridContainer/ResolutionSelector" to="." method="_on_resolution_selector_item_selected"]
-[connection signal="item_selected" from="TabContainer/VideoTab/GridContainer/ScreenModeSelector" to="." method="_on_screen_mode_selector_item_selected"]
-[connection signal="item_selected" from="TabContainer/VideoTab/GridContainer/MonitorDisplaySelector" to="." method="_on_monitor_display_selector_item_selected"]
-[connection signal="pressed" from="TabContainer/VideoTab/GridContainer/BackButton" to="." method="_on_back_button_pressed"]
-[connection signal="pressed" from="TabContainer/VideoTab/GridContainer/SaveSettingsButton" to="." method="_on_save_settings_button_pressed"]
-[connection signal="changed" from="TabContainer/SoundTab/GridContainer/MusicVolume" to="." method="_on_music_volume_changed"]
-[connection signal="drag_ended" from="TabContainer/SoundTab/GridContainer/MusicVolume" to="." method="_on_music_volume_drag_ended"]
-[connection signal="value_changed" from="TabContainer/SoundTab/GridContainer/MusicVolume" to="." method="_on_music_volume_value_changed"]
-[connection signal="value_changed" from="TabContainer/SoundTab/GridContainer/SfxVolume" to="." method="_on_sfx_volume_value_changed"]
-[connection signal="toggled" from="TabContainer/SoundTab/GridContainer/EarExploder" to="." method="_on_ear_exploder_toggled"]
-[connection signal="value_changed" from="TabContainer/OtherTab/HBoxContainer/SpinBox" to="." method="_on_spin_box_value_changed"]
diff --git a/game/src/SampleGame.gd b/game/src/SampleGame.gd
new file mode 100644
index 0000000..0e3a61d
--- /dev/null
+++ b/game/src/SampleGame.gd
@@ -0,0 +1,40 @@
+extends Control
+
+var selectedId = 0
+
+# Called when the node enters the scene tree for the first time.
+func _ready():
+ updateVisibleInfo()
+ pass # Replace with function body.
+
+
+# Called every frame. 'delta' is the elapsed time since the previous frame.
+#func _process(delta):
+# pass
+
+
+func updateVisibleInfo():
+ $CenterContainer/VBoxContainer2/GridContainer/ProvinceNumDisplay.text = str(selectedId)
+ $CenterContainer/VBoxContainer2/GridContainer/ProvinceSizeDisplay.text = str(Simulation.queryProvinceSize(selectedId))
+
+
+func _on_pass_time_button_pressed():
+ Simulation.conductSimulationStep()
+ updateVisibleInfo()
+
+
+func _on_next_prov_button_pressed():
+ selectedId = (selectedId + 1) % 10
+ updateVisibleInfo()
+
+
+func _on_prev_prov_button_pressed():
+ if selectedId == 0:
+ selectedId = 9
+ else:
+ selectedId -= 1
+ updateVisibleInfo()
+
+
+func _on_to_main_menu_pressed():
+ get_tree().change_scene_to_file("res://src/MainMenu.tscn")
diff --git a/game/src/SampleGame.tscn b/game/src/SampleGame.tscn
new file mode 100644
index 0000000..8221857
--- /dev/null
+++ b/game/src/SampleGame.tscn
@@ -0,0 +1,64 @@
+[gd_scene load_steps=2 format=3 uid="uid://bgnupcshe1m7r"]
+
+[ext_resource type="Script" path="res://src/SampleGame.gd" id="1_eklvp"]
+
+[node name="SampleGame" type="Control"]
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+script = ExtResource("1_eklvp")
+
+[node name="CenterContainer" type="CenterContainer" parent="."]
+layout_mode = 0
+offset_right = 1152.0
+offset_bottom = 648.0
+
+[node name="VBoxContainer2" type="VBoxContainer" parent="CenterContainer"]
+layout_mode = 2
+
+[node name="GridContainer" type="GridContainer" parent="CenterContainer/VBoxContainer2"]
+layout_mode = 2
+columns = 2
+
+[node name="ProvenceLabel" type="Label" parent="CenterContainer/VBoxContainer2/GridContainer"]
+layout_mode = 2
+text = "Viewing Province #:"
+horizontal_alignment = 2
+
+[node name="ProvinceNumDisplay" type="Label" parent="CenterContainer/VBoxContainer2/GridContainer"]
+layout_mode = 2
+
+[node name="ProvinceSizeLabel" type="Label" parent="CenterContainer/VBoxContainer2/GridContainer"]
+layout_mode = 2
+text = "Province Size:"
+horizontal_alignment = 2
+
+[node name="ProvinceSizeDisplay" type="Label" parent="CenterContainer/VBoxContainer2/GridContainer"]
+layout_mode = 2
+
+[node name="VBoxContainer" type="VBoxContainer" parent="CenterContainer/VBoxContainer2"]
+layout_mode = 2
+
+[node name="PassTimeButton" type="Button" parent="CenterContainer/VBoxContainer2/VBoxContainer"]
+layout_mode = 2
+text = "Pass Time"
+
+[node name="NextProvButton" type="Button" parent="CenterContainer/VBoxContainer2/VBoxContainer"]
+layout_mode = 2
+text = "View Next Province"
+
+[node name="PrevProvButton" type="Button" parent="CenterContainer/VBoxContainer2/VBoxContainer"]
+layout_mode = 2
+text = "View Previous Province"
+
+[node name="ToMainMenu" type="Button" parent="CenterContainer/VBoxContainer2/VBoxContainer"]
+layout_mode = 2
+text = "Exit to Main Menu"
+
+[connection signal="pressed" from="CenterContainer/VBoxContainer2/VBoxContainer/PassTimeButton" to="." method="_on_pass_time_button_pressed"]
+[connection signal="pressed" from="CenterContainer/VBoxContainer2/VBoxContainer/NextProvButton" to="." method="_on_next_prov_button_pressed"]
+[connection signal="pressed" from="CenterContainer/VBoxContainer2/VBoxContainer/PrevProvButton" to="." method="_on_prev_prov_button_pressed"]
+[connection signal="pressed" from="CenterContainer/VBoxContainer2/VBoxContainer/ToMainMenu" to="." method="_on_to_main_menu_pressed"]
diff --git a/godot-cpp b/godot-cpp
-Subproject a8d848506074b1bb7eae319b06e9de60395eee1
+Subproject 516fad14e45d341211121832bb3daa172aebd6e