aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2023-12-07 23:45:19 +0100
committer hop311 <hop3114@gmail.com>2023-12-11 10:51:01 +0100
commitfd375bdb35d8a7b2ac9cf3dd02cdb0f197451a0b (patch)
treeb22d464dbf8e0e2569b9be5aa130e4def2e51207
parenta6952efba078e49d6555b0586230986a2cb7ed40 (diff)
Big UI commit - GUINode, MaskedFlag, PieChart, etc
m---------extension/deps/openvic-simulation0
-rw-r--r--extension/src/openvic-extension/UIAdapter.cpp171
-rw-r--r--extension/src/openvic-extension/UIAdapter.hpp27
-rw-r--r--extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp183
-rw-r--r--extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp55
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.cpp167
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.hpp49
-rw-r--r--extension/src/openvic-extension/classes/GUINode.cpp161
-rw-r--r--extension/src/openvic-extension/classes/GUINode.hpp53
-rw-r--r--extension/src/openvic-extension/register_types.cpp6
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.cpp174
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.hpp11
-rw-r--r--extension/src/openvic-extension/utility/Utilities.cpp31
-rw-r--r--extension/src/openvic-extension/utility/Utilities.hpp4
-rw-r--r--game/src/Game/Autoload/Events.gd2
-rw-r--r--game/src/Game/Autoload/Events/Loader.gd6
-rw-r--r--game/src/Game/GameSession/GameSession.gd6
-rw-r--r--game/src/Game/GameSession/GameSession.tscn15
-rw-r--r--game/src/Game/GameSession/GameSpeedPanel.gd43
-rw-r--r--game/src/Game/GameSession/GameSpeedPanel.tscn67
-rw-r--r--game/src/Game/GameSession/ProvinceOverviewPanel.gd237
-rw-r--r--game/src/Game/GameSession/Topbar.gd84
-rw-r--r--game/src/Game/LoadingScreen.gd6
23 files changed, 1240 insertions, 318 deletions
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation
-Subproject 444a27726695478e44e0166e75df1f354b6432d
+Subproject 6b9cf7f9dff1570b10a0a0f988e1b1ef418d024
diff --git a/extension/src/openvic-extension/UIAdapter.cpp b/extension/src/openvic-extension/UIAdapter.cpp
index 1478a5a..cbe898c 100644
--- a/extension/src/openvic-extension/UIAdapter.cpp
+++ b/extension/src/openvic-extension/UIAdapter.cpp
@@ -12,6 +12,8 @@
#include <godot_cpp/variant/utility_functions.hpp>
#include "openvic-extension/classes/GFXIconTexture.hpp"
+#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
+#include "openvic-extension/classes/GFXPieChartTexture.hpp"
#include "openvic-extension/utility/Utilities.hpp"
using namespace godot;
@@ -20,12 +22,14 @@ using namespace OpenVic;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;
-bool GodotGUIBuilder::generate_element(GUI::Element const* element, AssetManager& asset_manager, Control*& result) {
+bool GodotGUIBuilder::generate_element(
+ GUI::Element const* element, String const& name, AssetManager& asset_manager, Control*& result
+) {
if (element == nullptr) {
UtilityFunctions::push_error("Invalid element passed to GodotGUIBuilder - null!");
return false;
}
- static const std::map<std::string_view, bool (*)(GUI::Element const&, AssetManager&, Control*&)> type_map {
+ static const std::map<std::string_view, bool (*)(GUI::Element const&, String const&, AssetManager&, Control*&)> type_map {
{ GUI::Icon::get_type_static(), &generate_icon },
{ GUI::Button::get_type_static(), &generate_button },
{ GUI::Checkbox::get_type_static(), &generate_checkbox },
@@ -36,7 +40,7 @@ bool GodotGUIBuilder::generate_element(GUI::Element const* element, AssetManager
};
const decltype(type_map)::const_iterator it = type_map.find(element->get_type());
if (it != type_map.end()) {
- return it->second(*element, asset_manager, result);
+ return it->second(*element, name, asset_manager, result);
} else {
UtilityFunctions::push_error("Invalid GUI element type: ", std_view_to_godot_string(element->get_type()));
result = nullptr;
@@ -45,7 +49,7 @@ bool GodotGUIBuilder::generate_element(GUI::Element const* element, AssetManager
}
template<std::derived_from<Control> T>
-static T* new_control(GUI::Element const& element) {
+static T* new_control(GUI::Element const& element, String const& name) {
T* node = memnew(T);
ERR_FAIL_NULL_V(node, nullptr);
@@ -57,7 +61,12 @@ static T* new_control(GUI::Element const& element) {
{ CENTER, PRESET_CENTER }
};
- node->set_name(std_view_to_godot_string(element.get_name()));
+ if (name.is_empty()) {
+ node->set_name(std_view_to_godot_string(element.get_name()));
+ } else {
+ node->set_name(name);
+ }
+
const decltype(orientation_map)::const_iterator it = orientation_map.find(element.get_orientation());
if (it != orientation_map.end()) {
node->set_anchors_and_offsets_preset(it->second);
@@ -71,7 +80,9 @@ static T* new_control(GUI::Element const& element) {
return node;
}
-bool GodotGUIBuilder::generate_icon(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+bool GodotGUIBuilder::generate_icon(
+ GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
+) {
GUI::Icon const& icon = static_cast<GUI::Icon const&>(element);
result = nullptr;
@@ -81,11 +92,8 @@ bool GodotGUIBuilder::generate_icon(GUI::Element const& element, AssetManager& a
bool ret = true;
if (icon.get_sprite() != nullptr) {
if (icon.get_sprite()->is_type<GFX::TextureSprite>()) {
- TextureRect* godot_texture_rect = new_control<TextureRect>(icon);
- if (godot_texture_rect == nullptr) {
- UtilityFunctions::push_error("Failed to create TextureRect for GUI icon ", icon_name);
- return false;
- }
+ TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name);
+ ERR_FAIL_NULL_V_MSG(godot_texture_rect, false, vformat("Failed to create TextureRect for GUI icon %s", icon_name));
GFX::TextureSprite const* texture_sprite = icon.get_sprite()->cast_to<GFX::TextureSprite>();
Ref<GFXIconTexture> texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite, icon.get_frame());
@@ -98,29 +106,24 @@ bool GodotGUIBuilder::generate_icon(GUI::Element const& element, AssetManager& a
result = godot_texture_rect;
} else if (icon.get_sprite()->is_type<GFX::MaskedFlag>()) {
- TextureRect* godot_texture_rect = new_control<TextureRect>(icon);
- if (godot_texture_rect == nullptr) {
- UtilityFunctions::push_error("Failed to create TextureRect for GUI icon ", icon_name);
- return false;
- }
+ TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name);
+ ERR_FAIL_NULL_V_MSG(godot_texture_rect, false, vformat("Failed to create TextureRect for GUI icon %s", icon_name));
- const StringName texture_file =
- std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::MaskedFlag>()->get_overlay_file());
- const Ref<ImageTexture> texture = asset_manager.get_texture(texture_file);
+ GFX::MaskedFlag const* masked_flag = icon.get_sprite()->cast_to<GFX::MaskedFlag>();
+ Ref<GFXMaskedFlagTexture> texture = GFXMaskedFlagTexture::make_gfx_masked_flag_texture(masked_flag);
if (texture.is_valid()) {
godot_texture_rect->set_texture(texture);
} else {
- UtilityFunctions::push_error("Failed to load masked flag sprite ", texture_file, " for GUI icon ", icon_name);
+ UtilityFunctions::push_error("Failed to make GFXMaskedFlagTexture for GUI icon ", icon_name);
ret = false;
}
result = godot_texture_rect;
} else if (icon.get_sprite()->is_type<GFX::ProgressBar>()) {
- TextureProgressBar* godot_progress_bar = new_control<TextureProgressBar>(icon);
- if (godot_progress_bar == nullptr) {
- UtilityFunctions::push_error("Failed to create TextureProgressBar for GUI icon ", icon_name);
- return false;
- }
+ TextureProgressBar* godot_progress_bar = new_control<TextureProgressBar>(icon, name);
+ ERR_FAIL_NULL_V_MSG(
+ godot_progress_bar, false, vformat("Failed to create TextureProgressBar for GUI icon %s", icon_name)
+ );
const StringName back_texture_file =
std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::ProgressBar>()->get_back_texture_file());
@@ -146,7 +149,23 @@ bool GodotGUIBuilder::generate_icon(GUI::Element const& element, AssetManager& a
result = godot_progress_bar;
} else if (icon.get_sprite()->is_type<GFX::PieChart>()) {
+ TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name);
+ ERR_FAIL_NULL_V_MSG(godot_texture_rect, false, vformat("Failed to create TextureRect for GUI icon %s", icon_name));
+
+ GFX::PieChart const* pie_chart = icon.get_sprite()->cast_to<GFX::PieChart>();
+ Ref<GFXPieChartTexture> texture = GFXPieChartTexture::make_gfx_pie_chart_texture(pie_chart);
+ if (texture.is_valid()) {
+ godot_texture_rect->set_texture(texture);
+ // TODO - work out why this is needed
+ Vector2 pos = godot_texture_rect->get_position();
+ pos.x -= texture->get_width() / 2;
+ godot_texture_rect->set_position(pos);
+ } else {
+ UtilityFunctions::push_error("Failed to make GFXPieChartTexture for GUI icon ", icon_name);
+ ret = false;
+ }
+ result = godot_texture_rect;
} else if (icon.get_sprite()->is_type<GFX::LineChart>()) {
} else {
@@ -161,21 +180,21 @@ bool GodotGUIBuilder::generate_icon(GUI::Element const& element, AssetManager& a
return ret;
}
-bool GodotGUIBuilder::generate_button(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+bool GodotGUIBuilder::generate_button(
+ GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
+) {
GUI::Button const& button = static_cast<GUI::Button const&>(element);
// TODO - shortcut, sprite, text
result = nullptr;
const String button_name = std_view_to_godot_string(button.get_name());
- Button* godot_button = new_control<Button>(button);
- if (godot_button == nullptr) {
- UtilityFunctions::push_error("Failed to create Button for GUI button ", button_name);
- return false;
- }
+ Button* godot_button = new_control<Button>(button, name);
+ ERR_FAIL_NULL_V_MSG(godot_button, false, vformat("Failed to create Button for GUI button %s", button_name));
- godot_button->set_text(std_view_to_godot_string(button.get_text()));
- //godot_button->set_flat(true);
+ if (!button.get_text().empty()) {
+ godot_button->set_text(std_view_to_godot_string(button.get_text()));
+ }
bool ret = true;
if (button.get_sprite() != nullptr) {
@@ -188,10 +207,10 @@ bool GodotGUIBuilder::generate_button(GUI::Element const& element, AssetManager&
ret = false;
}
} else if (button.get_sprite()->is_type<GFX::MaskedFlag>()) {
- texture = asset_manager.get_texture(std_view_to_godot_string_name(
- button.get_sprite()->cast_to<GFX::MaskedFlag>()->get_overlay_file()));
+ GFX::MaskedFlag const* masked_flag = button.get_sprite()->cast_to<GFX::MaskedFlag>();
+ texture = GFXMaskedFlagTexture::make_gfx_masked_flag_texture(masked_flag);
if (texture.is_null()) {
- UtilityFunctions::push_error("Failed to load masked flag sprite for GUI button ", button_name);
+ UtilityFunctions::push_error("Failed to make GFXMaskedFlagTexture for GUI button ", button_name);
ret = false;
}
} else {
@@ -205,8 +224,9 @@ bool GodotGUIBuilder::generate_button(GUI::Element const& element, AssetManager&
Ref<StyleBoxTexture> stylebox;
stylebox.instantiate();
if (stylebox.is_valid()) {
+ static const StringName theme_name_normal = "normal";
stylebox->set_texture(texture);
- godot_button->add_theme_stylebox_override("normal", stylebox);
+ godot_button->add_theme_stylebox_override(theme_name_normal, stylebox);
} else {
UtilityFunctions::push_error("Failed to load instantiate texture stylebox for GUI button ", button_name);
ret = false;
@@ -234,18 +254,17 @@ bool GodotGUIBuilder::generate_button(GUI::Element const& element, AssetManager&
return ret;
}
-bool GodotGUIBuilder::generate_checkbox(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+bool GodotGUIBuilder::generate_checkbox(
+ GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
+) {
GUI::Checkbox const& checkbox = static_cast<GUI::Checkbox const&>(element);
// TODO - shortcut, sprite, text
result = nullptr;
const String checkbox_name = std_view_to_godot_string(checkbox.get_name());
- CheckBox* godot_checkbox = new_control<CheckBox>(checkbox);
- if (godot_checkbox == nullptr) {
- UtilityFunctions::push_error("Failed to create CheckBox for GUI checkbox ", checkbox_name);
- return false;
- }
+ CheckBox* godot_checkbox = new_control<CheckBox>(checkbox, name);
+ ERR_FAIL_NULL_V_MSG(godot_checkbox, false, vformat("Failed to create CheckBox for GUI checkbox %s", checkbox_name));
bool ret = true;
if (checkbox.get_sprite() != nullptr) {
@@ -267,8 +286,10 @@ bool GodotGUIBuilder::generate_checkbox(GUI::Element const& element, AssetManage
ret = false;
}
} else {
- UtilityFunctions::push_error("Invalid sprite type ", std_view_to_godot_string(checkbox.get_sprite()->get_type()),
- " for GUI checkbox ", checkbox_name);
+ UtilityFunctions::push_error(
+ "Invalid sprite type ", std_view_to_godot_string(checkbox.get_sprite()->get_type()), " for GUI checkbox ",
+ checkbox_name
+ );
ret = false;
}
} else {
@@ -280,17 +301,16 @@ bool GodotGUIBuilder::generate_checkbox(GUI::Element const& element, AssetManage
return ret;
}
-bool GodotGUIBuilder::generate_text(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+bool GodotGUIBuilder::generate_text(
+ GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
+) {
GUI::Text const& text = static_cast<GUI::Text const&>(element);
result = nullptr;
const String text_name = std_view_to_godot_string(text.get_name());
- Label* godot_label = new_control<Label>(text);
- if (godot_label == nullptr) {
- UtilityFunctions::push_error("Failed to create Label for GUI text ", text_name);
- return false;
- }
+ Label* godot_label = new_control<Label>(text, name);
+ ERR_FAIL_NULL_V_MSG(godot_label, false, vformat("Failed to create Label for GUI text %s", text_name));
godot_label->set_text(std_view_to_godot_string(text.get_text()));
godot_label->set_size(Utilities::to_godot_fvec2(text.get_max_size()));
@@ -329,19 +349,17 @@ bool GodotGUIBuilder::generate_text(GUI::Element const& element, AssetManager& a
}
bool GodotGUIBuilder::generate_overlapping_elements(
- GUI::Element const& element, AssetManager& asset_manager, Control*& result
+ GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
) {
GUI::OverlappingElementsBox const& overlapping_elements = static_cast<GUI::OverlappingElementsBox const&>(element);
result = nullptr;
const String overlapping_elements_name = std_view_to_godot_string(overlapping_elements.get_name());
- ColorRect* godot_rect = new_control<ColorRect>(overlapping_elements);
- if (godot_rect == nullptr) {
- UtilityFunctions::push_error("Failed to create ColorRect for GUI overlapping elements ",
- overlapping_elements_name);
- return false;
- }
+ ColorRect* godot_rect = new_control<ColorRect>(overlapping_elements, name);
+ ERR_FAIL_NULL_V_MSG(
+ godot_rect, false, vformat("Failed to create ColorRect for GUI overlapping elements %s", overlapping_elements_name)
+ );
godot_rect->set_size(Utilities::to_godot_fvec2(overlapping_elements.get_size()));
godot_rect->set_color({ 0.0f, 0.5f, 1.0f, 0.2f });
@@ -350,17 +368,16 @@ bool GodotGUIBuilder::generate_overlapping_elements(
return true;
}
-bool GodotGUIBuilder::generate_listbox(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+bool GodotGUIBuilder::generate_listbox(
+ GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
+) {
GUI::ListBox const& listbox = static_cast<GUI::ListBox const&>(element);
result = nullptr;
const String listbox_name = std_view_to_godot_string(listbox.get_name());
- ColorRect* godot_rect = new_control<ColorRect>(listbox);
- if (godot_rect == nullptr) {
- UtilityFunctions::push_error("Failed to create ColorRect for GUI listbox ", listbox_name);
- return false;
- }
+ ColorRect* godot_rect = new_control<ColorRect>(listbox, name);
+ ERR_FAIL_NULL_V_MSG(godot_rect, false, vformat("Failed to create ColorRect for GUI listbox %s", listbox_name));
godot_rect->set_size(Utilities::to_godot_fvec2(listbox.get_size()));
godot_rect->set_color({ 1.0f, 0.5f, 0.0f, 0.2f });
@@ -369,32 +386,30 @@ bool GodotGUIBuilder::generate_listbox(GUI::Element const& element, AssetManager
return true;
}
-bool GodotGUIBuilder::generate_window(GUI::Element const& element, AssetManager& asset_manager, Control*& result) {
+bool GodotGUIBuilder::generate_window(
+ GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result
+) {
GUI::Window const& window = static_cast<GUI::Window const&>(element);
// TODO - moveable, fullscreen, dontRender (disable visibility?)
result = nullptr;
const String window_name = std_view_to_godot_string(window.get_name());
- Panel* godot_panel = new_control<Panel>(window);
- if (godot_panel == nullptr) {
- UtilityFunctions::push_error("Failed to create Panel for GUI window ", window_name);
- return false;
- }
+ Panel* godot_panel = new_control<Panel>(window, name);
+ ERR_FAIL_NULL_V_MSG(godot_panel, false, vformat("Failed to create Panel for GUI window %s", window_name));
godot_panel->set_size(Utilities::to_godot_fvec2(window.get_size()));
godot_panel->set_self_modulate({ 1.0f, 1.0f, 1.0f, 0.0f });
bool ret = true;
- for (std::unique_ptr<GUI::Element> const& element : window.get_elements()) {
+ for (std::unique_ptr<GUI::Element> const& element : window.get_window_elements()) {
Control* node = nullptr;
- const bool element_ret = generate_element(element.get(), asset_manager, node);
- if (element_ret) {
- if (node != nullptr) {
- godot_panel->add_child(node);
- }
- } else {
- UtilityFunctions::push_error("Failed to generate GUI element ", std_view_to_godot_string(element->get_name()));
+ const bool element_ret = generate_element(element.get(), "", asset_manager, node);
+ if (node != nullptr) {
+ godot_panel->add_child(node);
+ }
+ if (!element_ret) {
+ UtilityFunctions::push_error("Errors generating GUI element ", std_view_to_godot_string(element->get_name()));
ret = false;
}
}
diff --git a/extension/src/openvic-extension/UIAdapter.hpp b/extension/src/openvic-extension/UIAdapter.hpp
index d54a6b5..258b5e9 100644
--- a/extension/src/openvic-extension/UIAdapter.hpp
+++ b/extension/src/openvic-extension/UIAdapter.hpp
@@ -7,13 +7,22 @@
#include "openvic-extension/singletons/AssetManager.hpp"
namespace OpenVic::GodotGUIBuilder {
- bool generate_element(GUI::Element const* element, AssetManager& asset_manager, godot::Control*& result);
-
- bool generate_icon(GUI::Element const& element, AssetManager& asset_manager, godot::Control*& result);
- bool generate_button(GUI::Element const& element, AssetManager& asset_manager, godot::Control*& result);
- bool generate_checkbox(GUI::Element const& element, AssetManager& asset_manager, godot::Control*& result);
- bool generate_text(GUI::Element const& element, AssetManager& asset_manager, godot::Control*& result);
- bool generate_overlapping_elements(GUI::Element const& element, AssetManager& asset_manager, godot::Control*& result);
- bool generate_listbox(GUI::Element const& element, AssetManager& asset_manager, godot::Control*& result);
- bool generate_window(GUI::Element const& element, AssetManager& asset_manager, godot::Control*& result);
+
+ bool generate_element(
+ GUI::Element const* element, godot::String const& name, AssetManager& asset_manager, godot::Control*& result
+ );
+
+#define GEN_GUI_ARGS \
+ GUI::Element const& element, godot::String const& name, AssetManager& asset_manager, godot::Control*& result
+
+ bool generate_icon(GEN_GUI_ARGS);
+ bool generate_button(GEN_GUI_ARGS);
+ bool generate_checkbox(GEN_GUI_ARGS);
+ bool generate_text(GEN_GUI_ARGS);
+ bool generate_overlapping_elements(GEN_GUI_ARGS);
+ bool generate_listbox(GEN_GUI_ARGS);
+ bool generate_window(GEN_GUI_ARGS);
+
+#undef GEN_GUI_ARGS
+
}
diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
new file mode 100644
index 0000000..3636855
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
@@ -0,0 +1,183 @@
+#include "GFXMaskedFlagTexture.hpp"
+
+#include <godot_cpp/variant/utility_functions.hpp>
+
+#include "openvic-extension/singletons/AssetManager.hpp"
+#include "openvic-extension/singletons/GameSingleton.hpp"
+#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/Utilities.hpp"
+
+using namespace godot;
+using namespace OpenVic;
+
+using OpenVic::Utilities::godot_to_std_string;
+using OpenVic::Utilities::std_view_to_godot_string;
+using OpenVic::Utilities::std_view_to_godot_string_name;
+
+Error GFXMaskedFlagTexture::_generate_combined_image() {
+ ERR_FAIL_NULL_V(overlay_image, FAILED);
+ bool can_update = true;
+ if (combined_image.is_null() || combined_image->get_size() != overlay_image->get_size()) {
+ combined_image = Image::create(
+ overlay_image->get_width(), overlay_image->get_height(), false, overlay_image->get_format()
+ );
+ ERR_FAIL_NULL_V(combined_image, FAILED);
+ can_update = false;
+ }
+
+ if (mask_image.is_valid() && flag_image.is_valid()) {
+ const Vector2i centre_translation = (mask_image->get_size() - combined_image->get_size()) / 2;
+ for (Vector2i combined_image_point { 0, 0 }; combined_image_point.y < combined_image->get_height(); ++combined_image_point.y) {
+ for (combined_image_point.x = 0; combined_image_point.x < combined_image->get_width(); ++combined_image_point.x) {
+ const Color overlay_image_colour = overlay_image->get_pixelv(combined_image_point);
+ // Translate to mask_image coordinates, keeping the centres of each image aligned.
+ const Vector2i mask_image_point = combined_image_point + centre_translation;
+ if (
+ 0 <= mask_image_point.x && mask_image_point.x < mask_image->get_width() &&
+ 0 <= mask_image_point.y && mask_image_point.y < mask_image->get_height()
+ ) {
+ const Color mask_image_colour = mask_image->get_pixelv(mask_image_point);
+ // Rescale from mask_image to flag_image coordinates.
+ const Vector2i flag_image_point = mask_image_point * flag_image->get_size() / mask_image->get_size();
+ Color flag_image_colour = flag_image->get_pixelv(flag_image_point);
+ flag_image_colour.a = mask_image_colour.a;
+ combined_image->set_pixelv(combined_image_point, flag_image_colour.blend(overlay_image_colour));
+ } else {
+ combined_image->set_pixelv(combined_image_point, overlay_image_colour);
+ }
+ }
+ }
+ } else {
+ combined_image->blit_rect(overlay_image, overlay_image->get_used_rect(), {});
+ }
+
+ if (can_update) {
+ update(combined_image);
+ } else {
+ set_image(combined_image);
+ }
+ return OK;
+}
+
+void GFXMaskedFlagTexture::_bind_methods() {
+ OV_BIND_METHOD(GFXMaskedFlagTexture::clear);
+
+ OV_BIND_METHOD(GFXMaskedFlagTexture::set_gfx_masked_flag_name, { "gfx_masked_flag_name" });
+ OV_BIND_METHOD(GFXMaskedFlagTexture::get_gfx_masked_flag_name);
+
+ OV_BIND_METHOD(GFXMaskedFlagTexture::set_flag_country_name_and_type, { "new_flag_country_name", "new_flag_type" });
+ OV_BIND_METHOD(GFXMaskedFlagTexture::get_flag_country_name);
+ OV_BIND_METHOD(GFXMaskedFlagTexture::get_flag_type);
+}
+
+GFXMaskedFlagTexture::GFXMaskedFlagTexture() : gfx_masked_flag { nullptr }, flag_country { nullptr } {}
+
+Ref<GFXMaskedFlagTexture> GFXMaskedFlagTexture::make_gfx_masked_flag_texture(GFX::MaskedFlag const* gfx_masked_flag) {
+ Ref<GFXMaskedFlagTexture> masked_flag_texture;
+ masked_flag_texture.instantiate();
+ ERR_FAIL_NULL_V(masked_flag_texture, nullptr);
+ if (masked_flag_texture->set_gfx_masked_flag(gfx_masked_flag) == OK) {
+ return masked_flag_texture;
+ } else {
+ return nullptr;
+ }
+}
+
+void GFXMaskedFlagTexture::clear() {
+ gfx_masked_flag = nullptr;
+ flag_country = nullptr;
+ flag_type = String {};
+
+ overlay_image.unref();
+ mask_image.unref();
+ flag_image.unref();
+}
+
+Error GFXMaskedFlagTexture::set_gfx_masked_flag(GFX::MaskedFlag const* new_gfx_masked_flag) {
+ if (gfx_masked_flag == new_gfx_masked_flag) {
+ return OK;
+ }
+ if (new_gfx_masked_flag == nullptr) {
+ clear();
+ return OK;
+ }
+ AssetManager* asset_manager = AssetManager::get_singleton();
+ ERR_FAIL_NULL_V(asset_manager, FAILED);
+
+ const StringName overlay_file = std_view_to_godot_string_name(new_gfx_masked_flag->get_overlay_file());
+ const Ref<Image> new_overlay_image = asset_manager->get_image(overlay_file);
+ ERR_FAIL_NULL_V_MSG(new_overlay_image, FAILED, vformat("Failed to load flag overlay image: %s", overlay_file));
+
+ const StringName mask_file = std_view_to_godot_string_name(new_gfx_masked_flag->get_mask_file());
+ const Ref<Image> new_mask_image = asset_manager->get_image(mask_file);
+ ERR_FAIL_NULL_V_MSG(new_mask_image, FAILED, vformat("Failed to load flag mask image: %s", mask_file));
+
+ gfx_masked_flag = new_gfx_masked_flag;
+ overlay_image = new_overlay_image;
+ mask_image = new_mask_image;
+
+ return _generate_combined_image();
+}
+
+Error GFXMaskedFlagTexture::set_gfx_masked_flag_name(String const& gfx_masked_flag_name) {
+ if (gfx_masked_flag_name.is_empty()) {
+ return set_gfx_masked_flag(nullptr);
+ }
+ GameSingleton* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, FAILED);
+ GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier(
+ godot_to_std_string(gfx_masked_flag_name)
+ );
+ ERR_FAIL_NULL_V_MSG(sprite, FAILED, vformat("GFX sprite not found: %s", gfx_masked_flag_name));
+ GFX::MaskedFlag const* new_masked_flag = sprite->cast_to<GFX::MaskedFlag>();
+ ERR_FAIL_NULL_V_MSG(
+ new_masked_flag, FAILED, vformat(
+ "Invalid type for GFX sprite %s: %s (expected %s)", gfx_masked_flag_name,
+ std_view_to_godot_string(sprite->get_type()), std_view_to_godot_string(GFX::MaskedFlag::get_type_static())
+ )
+ );
+ return set_gfx_masked_flag(new_masked_flag);
+}
+
+String GFXMaskedFlagTexture::get_gfx_masked_flag_name() const {
+ return gfx_masked_flag != nullptr ? std_view_to_godot_string(gfx_masked_flag->get_name()) : String {};
+}
+
+Error GFXMaskedFlagTexture::set_flag_country_and_type(Country const* new_flag_country, StringName const& new_flag_type) {
+ if (flag_country == new_flag_country && flag_type == new_flag_type) {
+ return OK;
+ }
+ if (new_flag_country != nullptr) {
+ GameSingleton* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, FAILED);
+
+ const Ref<Image> new_flag_image = game_singleton->get_flag_image(new_flag_country, new_flag_type);
+ ERR_FAIL_NULL_V(new_flag_image, FAILED);
+
+ flag_country = new_flag_country;
+ flag_type = new_flag_type;
+ flag_image = new_flag_image;
+ } else {
+ flag_country = nullptr;
+ flag_type = String {};
+ flag_image.unref();
+ }
+ return _generate_combined_image();
+}
+
+Error GFXMaskedFlagTexture::set_flag_country_name_and_type(String const& new_flag_country_name, StringName const& new_flag_type) {
+ if (new_flag_country_name.is_empty()) {
+ return set_flag_country_and_type(nullptr, {});
+ }
+ GameSingleton* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, FAILED);
+ Country const* new_flag_country = game_singleton->get_game_manager().get_country_manager().get_country_by_identifier(
+ godot_to_std_string(new_flag_country_name)
+ );
+ ERR_FAIL_NULL_V_MSG(new_flag_country, FAILED, vformat("Country not found: %s", new_flag_country_name));
+ return set_flag_country_and_type(new_flag_country, new_flag_type);
+}
+
+String GFXMaskedFlagTexture::get_flag_country_name() const {
+ return flag_country != nullptr ? std_view_to_godot_string(flag_country->get_identifier()) : String {};
+}
diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
new file mode 100644
index 0000000..be3b15a
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
@@ -0,0 +1,55 @@
+#pragma once
+
+#include <godot_cpp/classes/image_texture.hpp>
+
+#include <openvic-simulation/country/Country.hpp>
+#include <openvic-simulation/interface/GFX.hpp>
+
+namespace OpenVic {
+ class GFXMaskedFlagTexture : public godot::ImageTexture {
+ GDCLASS(GFXMaskedFlagTexture, godot::ImageTexture)
+
+ GFX::MaskedFlag const* PROPERTY(gfx_masked_flag);
+ Country const* PROPERTY(flag_country);
+ godot::StringName PROPERTY(flag_type);
+
+ godot::Ref<godot::Image> overlay_image, mask_image, flag_image, combined_image;
+
+ godot::Error _generate_combined_image();
+
+ protected:
+ static void _bind_methods();
+
+ public:
+ GFXMaskedFlagTexture();
+
+ /* Create a GFXMaskedFlagTexture using the specific GFX::MaskedFlag.
+ * Returns nullptr if setting gfx_masked_flag fails. */
+ static godot::Ref<GFXMaskedFlagTexture> make_gfx_masked_flag_texture(GFX::MaskedFlag const* gfx_masked_flag);
+
+ /* Reset gfx_masked_flag, flag_country and flag_type to nullptr/an empty string, and unreference all images.
+ * This does not affect the godot::ImageTexture, which cannot be reset to a null or empty image. */
+ void clear();
+
+ /* Set the GFX::MaskedFlag, load its overlay and mask textures, and regenerate the combined image. */
+ godot::Error set_gfx_masked_flag(GFX::MaskedFlag const* new_gfx_masked_flag);
+
+ /* Search for a GFX::MaskedFlag with the specfied name and, if successful, set it using set_gfx_masked_flag. */
+ godot::Error set_gfx_masked_flag_name(godot::String const& gfx_masked_flag_name);
+
+ /* Return the name of the GFX::MaskedFlag, or an empty String if it's null */
+ godot::String get_gfx_masked_flag_name() const;
+
+ /* Set flag_country and flag_type and update the combined image to use that flag, or no flag if it doesn't exist. */
+ godot::Error set_flag_country_and_type(Country const* new_flag_country, godot::StringName const& new_flag_type);
+
+ /* Look up the country with the specified identifier, then call set_flag_country_and_type with the country and
+ * specified flag_type as arguments. */
+ godot::Error set_flag_country_name_and_type(
+ godot::String const& new_flag_country_name, godot::StringName const& new_flag_type
+ );
+
+ /* Return the name of the selected flag's country, or an empty String if it's null */
+ godot::String get_flag_country_name() const;
+ };
+}
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
new file mode 100644
index 0000000..6fe2fe0
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
@@ -0,0 +1,167 @@
+#include "GFXPieChartTexture.hpp"
+
+#include <godot_cpp/variant/utility_functions.hpp>
+
+#include "openvic-extension/singletons/AssetManager.hpp"
+#include "openvic-extension/singletons/GameSingleton.hpp"
+#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/Utilities.hpp"
+
+using namespace godot;
+using namespace OpenVic;
+
+using OpenVic::Utilities::godot_to_std_string;
+using OpenVic::Utilities::std_view_to_godot_string;
+using OpenVic::Utilities::std_view_to_godot_string_name;
+
+#define PI std::numbers::pi_v<float>
+
+Error GFXPieChartTexture::_generate_pie_chart_image() {
+ ERR_FAIL_NULL_V(gfx_pie_chart, FAILED);
+ if (gfx_pie_chart->get_size() <= 0) {
+ UtilityFunctions::push_error("Invalid GFX::PieChart size for GFXPieChartTexture - ", gfx_pie_chart->get_size());
+ return FAILED;
+ }
+ const int32_t pie_chart_size = 2 * gfx_pie_chart->get_size();
+ bool can_update = true;
+ if (
+ pie_chart_image.is_null() || pie_chart_image->get_width() != pie_chart_size ||
+ pie_chart_image->get_height() != pie_chart_size
+ ) {
+ pie_chart_image = Image::create(pie_chart_size, pie_chart_size, false, Image::FORMAT_RGBA8);
+ ERR_FAIL_NULL_V(pie_chart_image, FAILED);
+ can_update = false;
+ }
+
+ static const Color background_colour { 0.0f, 0.0f, 0.0f, 0.0f };
+ if (!slices.empty()) {
+ const float pie_chart_radius = gfx_pie_chart->get_size();
+ const Vector2 centre_translation = Vector2 { 0.5f, 0.5f } - static_cast<Vector2>(pie_chart_image->get_size()) * 0.5f;
+ for (Vector2i point { 0, 0 }; point.y < pie_chart_image->get_height(); ++point.y) {
+ for (point.x = 0; point.x < pie_chart_image->get_width(); ++point.x) {
+ const Vector2 offset = centre_translation + point;
+ if (offset.length() <= pie_chart_radius) {
+ float theta = 0.5f * PI + atan2(offset.y, offset.x);
+ if (theta < 0.0f) {
+ theta += 2.0f * PI;
+ }
+ /* Rescale angle so that total_weight is a full rotation. */
+ theta *= total_weight / (2.0f * PI);
+ Color colour = slices.front().first;
+ /* Find the slice theta lies in. */
+ for (slice_t const& slice : slices) {
+ if (theta <= slice.second) {
+ colour = slice.first;
+ break;
+ } else {
+ theta -= slice.second;
+ }
+ }
+ pie_chart_image->set_pixelv(point, colour);
+ } else {
+ pie_chart_image->set_pixelv(point, background_colour);
+ }
+ }
+ }
+ } else {
+ pie_chart_image->fill(background_colour);
+ }
+
+ if (can_update) {
+ update(pie_chart_image);
+ } else {
+ set_image(pie_chart_image);
+ }
+ return OK;
+}
+
+Error GFXPieChartTexture::set_slices(Array const& new_slices) {
+ static const StringName colour_key = "colour";
+ static const StringName weight_key = "weight";
+
+ slices.clear();
+ total_weight = 0.0f;
+ for (int32_t i = 0; i < new_slices.size(); ++i) {
+ Dictionary const& slice_dict = new_slices[i];
+ if (!slice_dict.has(colour_key) || !slice_dict.has(weight_key)) {
+ UtilityFunctions::push_error("Invalid slice keys at index ", i, " - ", slice_dict);
+ continue;
+ }
+ const slice_t slice = std::make_pair(slice_dict[colour_key], slice_dict[weight_key]);
+ if (slice.second <= 0.0f) {
+ UtilityFunctions::push_error("Invalid slice weight at index ", i, " - ", slice.second);
+ continue;
+ }
+ total_weight += slice.second;
+ slices.emplace_back(std::move(slice));
+ }
+ return _generate_pie_chart_image();
+}
+
+void GFXPieChartTexture::_bind_methods() {
+ OV_BIND_METHOD(GFXPieChartTexture::clear);
+
+ OV_BIND_METHOD(GFXPieChartTexture::set_gfx_pie_chart_name, { "gfx_pie_chart_name" });
+ OV_BIND_METHOD(GFXPieChartTexture::get_gfx_pie_chart_name);
+
+ OV_BIND_METHOD(GFXPieChartTexture::set_slices, { "new_slices" });
+}
+
+GFXPieChartTexture::GFXPieChartTexture() : total_weight { 0.0f } {}
+
+Ref<GFXPieChartTexture> GFXPieChartTexture::make_gfx_pie_chart_texture(GFX::PieChart const* gfx_pie_chart) {
+ Ref<GFXPieChartTexture> pie_chart_texture;
+ pie_chart_texture.instantiate();
+ ERR_FAIL_NULL_V(pie_chart_texture, nullptr);
+ if (pie_chart_texture->set_gfx_pie_chart(gfx_pie_chart) == OK) {
+ return pie_chart_texture;
+ } else {
+ return nullptr;
+ }
+}
+
+void GFXPieChartTexture::clear() {
+ gfx_pie_chart = nullptr;
+ slices.clear();
+ total_weight = 0.0f;
+
+ pie_chart_image.unref();
+}
+
+Error GFXPieChartTexture::set_gfx_pie_chart(GFX::PieChart const* new_gfx_pie_chart) {
+ if (gfx_pie_chart == new_gfx_pie_chart) {
+ return OK;
+ }
+ if (new_gfx_pie_chart == nullptr) {
+ clear();
+ return OK;
+ }
+
+ gfx_pie_chart = new_gfx_pie_chart;
+
+ return _generate_pie_chart_image();
+}
+
+Error GFXPieChartTexture::set_gfx_pie_chart_name(String const& gfx_pie_chart_name) {
+ if (gfx_pie_chart_name.is_empty()) {
+ return set_gfx_pie_chart(nullptr);
+ }
+ GameSingleton* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, FAILED);
+ GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier(
+ godot_to_std_string(gfx_pie_chart_name)
+ );
+ ERR_FAIL_NULL_V_MSG(sprite, FAILED, vformat("GFX sprite not found: %s", gfx_pie_chart_name));
+ GFX::PieChart const* new_pie_chart = sprite->cast_to<GFX::PieChart>();
+ ERR_FAIL_NULL_V_MSG(
+ new_pie_chart, FAILED, vformat(
+ "Invalid type for GFX sprite %s: %s (expected %s)", gfx_pie_chart_name,
+ std_view_to_godot_string(sprite->get_type()), std_view_to_godot_string(GFX::PieChart::get_type_static())
+ )
+ );
+ return set_gfx_pie_chart(new_pie_chart);
+}
+
+String GFXPieChartTexture::get_gfx_pie_chart_name() const {
+ return gfx_pie_chart != nullptr ? std_view_to_godot_string(gfx_pie_chart->get_name()) : String {};
+}
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
new file mode 100644
index 0000000..315b00e
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
@@ -0,0 +1,49 @@
+#pragma once
+
+#include <godot_cpp/classes/image_texture.hpp>
+
+#include <openvic-simulation/interface/GFX.hpp>
+
+namespace OpenVic {
+ class GFXPieChartTexture : public godot::ImageTexture {
+ GDCLASS(GFXPieChartTexture, godot::ImageTexture)
+
+ using slice_t = std::pair<godot::Color, float>;
+
+ GFX::PieChart const* PROPERTY(gfx_pie_chart);
+ std::vector<slice_t> slices;
+ float total_weight;
+ godot::Ref<godot::Image> pie_chart_image;
+
+ godot::Error _generate_pie_chart_image();
+
+ protected:
+ static void _bind_methods();
+
+ public:
+ GFXPieChartTexture();
+
+ /* Set slices given new_slices, an Array of Dictionaries, each with the following keys:
+ * - colour: Color
+ * - weight: float
+ */
+ godot::Error set_slices(godot::Array const& new_slices);
+
+ /* Create a GFXPieChartTexture using the specific GFX::PieChart.
+ * Returns nullptr if setting gfx_pie_chart fails. */
+ static godot::Ref<GFXPieChartTexture> make_gfx_pie_chart_texture(GFX::PieChart const* gfx_pie_chart);
+
+ /* Reset gfx_pie_chart, flag_country and flag_type to nullptr/an empty string, and unreference all images.
+ * This does not affect the godot::ImageTexture, which cannot be reset to a null or empty image. */
+ void clear();
+
+ /* Set the GFX::PieChart and regenerate the pie chart image. */
+ godot::Error set_gfx_pie_chart(GFX::PieChart const* new_gfx_pie_chart);
+
+ /* Search for a GFX::PieChart with the specfied name and, if successful, set it using set_gfx_pie_chart. */
+ godot::Error set_gfx_pie_chart_name(godot::String const& gfx_pie_chart_name);
+
+ /* Return the name of the GFX::PieChart, or an empty String if it's null */
+ godot::String get_gfx_pie_chart_name() const;
+ };
+}
diff --git a/extension/src/openvic-extension/classes/GUINode.cpp b/extension/src/openvic-extension/classes/GUINode.cpp
new file mode 100644
index 0000000..1d55c54
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUINode.cpp
@@ -0,0 +1,161 @@
+#include "GUINode.hpp"
+
+#include <godot_cpp/classes/style_box_texture.hpp>
+#include <godot_cpp/variant/utility_functions.hpp>
+
+#include "openvic-extension/UIAdapter.hpp"
+#include "openvic-extension/singletons/AssetManager.hpp"
+#include "openvic-extension/singletons/GameSingleton.hpp"
+#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/Utilities.hpp"
+
+using namespace godot;
+using namespace OpenVic;
+
+using OpenVic::Utilities::godot_to_std_string;
+using OpenVic::Utilities::std_view_to_godot_string;
+
+void GUINode::_bind_methods() {
+ OV_BIND_METHOD(GUINode::add_gui_element, { "gui_file", "gui_element", "name" }, DEFVAL(String {}));
+
+ OV_BIND_METHOD(GUINode::get_button_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_check_box_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_label_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_panel_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_progress_bar_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_texture_rect_node, { "path" });
+
+ OV_BIND_METHOD(GUINode::get_texture_from_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_gfx_icon_texture_from_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_gfx_masked_flag_texture_from_node, { "path" });
+ OV_BIND_METHOD(GUINode::get_gfx_pie_chart_texture_from_node, { "path" });
+
+ OV_BIND_METHOD(GUINode::hide_node, { "path" });
+ OV_BIND_METHOD(GUINode::hide_nodes, { "paths" });
+}
+
+Error GUINode::_add_gui_element(GUI::Element const* element, String const& name) {
+ ERR_FAIL_NULL_V(element, FAILED);
+ AssetManager* asset_manager = AssetManager::get_singleton();
+ ERR_FAIL_NULL_V(asset_manager, FAILED);
+ Error err = OK;
+ Control* result = nullptr;
+ if (!GodotGUIBuilder::generate_element(element, name, *asset_manager, result)) {
+ UtilityFunctions::push_error("Failed to generate GUI element ", std_view_to_godot_string(element->get_name()));
+ err = FAILED;
+ }
+ if (result != nullptr) {
+ add_child(result);
+ }
+ return err;
+}
+
+Error GUINode::add_gui_element(String const& gui_file, String const& gui_element, String const& name) {
+ GameSingleton const* game_singleton = GameSingleton::get_singleton();
+ ERR_FAIL_NULL_V(game_singleton, FAILED);
+ GUI::Scene const* scene =
+ game_singleton->get_game_manager().get_ui_manager().get_scene_by_identifier(godot_to_std_string(gui_file));
+ ERR_FAIL_NULL_V_MSG(scene, FAILED, vformat("Failed to find GUI file %s", gui_file));
+ GUI::Element const* element = scene->get_scene_element_by_identifier(godot_to_std_string(gui_element));
+ ERR_FAIL_NULL_V_MSG(element, FAILED, vformat("Failed to find GUI element %s in GUI file %s", gui_element, gui_file));
+ return _add_gui_element(element, name);
+}
+
+template<std::derived_from<godot::Node> T>
+T* GUINode::_get_cast_node(NodePath const& path) const {
+ Node* node = get_node_or_null(path);
+ ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to find node %s", path));
+ T* result = Object::cast_to<T>(node);
+ ERR_FAIL_NULL_V_MSG(result, nullptr, vformat("Failed to cast node %s to type %s", path, T::get_class_static()));
+ return result;
+}
+
+Button* GUINode::get_button_node(NodePath const& path) const {
+ return _get_cast_node<Button>(path);
+}
+
+CheckBox* GUINode::get_check_box_node(NodePath const& path) const {
+ return _get_cast_node<CheckBox>(path);
+}
+
+Label* GUINode::get_label_node(NodePath const& path) const {
+ return _get_cast_node<Label>(path);
+}
+
+Panel* GUINode::get_panel_node(NodePath const& path) const {
+ return _get_cast_node<Panel>(path);
+}
+
+TextureProgressBar* GUINode::get_progress_bar_node(NodePath const& path) const {
+ return _get_cast_node<TextureProgressBar>(path);
+}
+
+TextureRect* GUINode::get_texture_rect_node(NodePath const& path) const {
+ return _get_cast_node<TextureRect>(path);
+}
+
+Ref<Texture2D> GUINode::get_texture_from_node(NodePath const& path) const {
+ Node* node = get_node_or_null(path);
+ ERR_FAIL_NULL_V_MSG(node, nullptr, vformat("Failed to find node %s", path));
+ if (TextureRect const* texture_rect = Object::cast_to<TextureRect>(node); texture_rect != nullptr) {
+ const Ref<Texture2D> texture = texture_rect->get_texture();
+ ERR_FAIL_NULL_V_MSG(texture, nullptr, vformat("Failed to get Texture2D from TextureRect %s", path));
+ return texture;
+ } else if (Button const* button = Object::cast_to<Button>(node); button != nullptr) {
+ static const StringName theme_name_normal = "normal";
+ const Ref<StyleBox> stylebox = button->get_theme_stylebox(theme_name_normal);
+ ERR_FAIL_NULL_V_MSG(stylebox, nullptr, vformat("Failed to get StyleBox %s from Button %s", theme_name_normal, path));
+ const Ref<StyleBoxTexture> stylebox_texture = stylebox;
+ ERR_FAIL_NULL_V_MSG(
+ stylebox_texture, nullptr, vformat(
+ "Failed to cast StyleBox %s from Button %s to type StyleBoxTexture", theme_name_normal, path
+ )
+ );
+ const Ref<Texture2D> result = stylebox_texture->get_texture();
+ ERR_FAIL_NULL_V_MSG(
+ result, nullptr, vformat("Failed to get Texture2D from StyleBoxTexture %s from Button %s", theme_name_normal, path)
+ );
+ return result;
+ }
+ ERR_FAIL_V_MSG(nullptr, vformat("Failed to cast node %s to type TextureRect or Button", path));
+}
+
+template<std::derived_from<godot::Texture2D> T>
+Ref<T> GUINode::_get_cast_texture_from_node(NodePath const& path) const {
+ const Ref<Texture2D> texture = get_texture_from_node(path);
+ ERR_FAIL_NULL_V(texture, nullptr);
+ const Ref<T> result = texture;
+ ERR_FAIL_NULL_V_MSG(result, nullptr, vformat("Failed to cast Texture2D from %s to type %s", path, T::get_class_static()));
+ return result;
+}
+
+Ref<GFXIconTexture> GUINode::get_gfx_icon_texture_from_node(NodePath const& path) const {
+ return _get_cast_texture_from_node<GFXIconTexture>(path);
+}
+
+Ref<GFXMaskedFlagTexture> GUINode::get_gfx_masked_flag_texture_from_node(NodePath const& path) const {
+ return _get_cast_texture_from_node<GFXMaskedFlagTexture>(path);
+}
+
+Ref<GFXPieChartTexture> GUINode::get_gfx_pie_chart_texture_from_node(NodePath const& path) const {
+ return _get_cast_texture_from_node<GFXPieChartTexture>(path);
+}
+
+Error GUINode::hide_node(NodePath const& path) const {
+ CanvasItem* node = _get_cast_node<CanvasItem>(path);
+ if (node == nullptr) {
+ return FAILED;
+ }
+ node->hide();
+ return OK;
+}
+
+Error GUINode::hide_nodes(Array const& paths) const {
+ Error ret = OK;
+ for (int32_t i = 0; i < paths.size(); ++i) {
+ if (hide_node(paths[i]) != OK) {
+ ret = FAILED;
+ }
+ }
+ return ret;
+}
diff --git a/extension/src/openvic-extension/classes/GUINode.hpp b/extension/src/openvic-extension/classes/GUINode.hpp
new file mode 100644
index 0000000..1671547
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUINode.hpp
@@ -0,0 +1,53 @@
+#pragma once
+
+#include <godot_cpp/classes/button.hpp>
+#include <godot_cpp/classes/check_box.hpp>
+#include <godot_cpp/classes/label.hpp>
+#include <godot_cpp/classes/panel.hpp>
+#include <godot_cpp/classes/texture_progress_bar.hpp>
+#include <godot_cpp/classes/texture_rect.hpp>
+
+#include <openvic-simulation/interface/GUI.hpp>
+
+#include "openvic-extension/classes/GFXIconTexture.hpp"
+#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
+#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+
+namespace OpenVic {
+ class GUINode : public godot::Control {
+ GDCLASS(GUINode, godot::Control)
+
+ template<std::derived_from<godot::Node> T>
+ T* _get_cast_node(godot::NodePath const& path) const;
+
+ template<std::derived_from<godot::Texture2D> T>
+ godot::Ref<T> _get_cast_texture_from_node(godot::NodePath const& path) const;
+
+ protected:
+ static void _bind_methods();
+
+ public:
+ GUINode() = default;
+
+ godot::Error _add_gui_element(GUI::Element const* element, godot::String const& name);
+ godot::Error add_gui_element(
+ godot::String const& gui_file, godot::String const& gui_element, godot::String const& name = ""
+ );
+
+ godot::Button* get_button_node(godot::NodePath const& path) const;
+ godot::CheckBox* get_check_box_node(godot::NodePath const& path) const;
+ godot::Label* get_label_node(godot::NodePath const& path) const;
+ godot::Panel* get_panel_node(godot::NodePath const& path) const;
+ godot::TextureProgressBar* get_progress_bar_node(godot::NodePath const& path) const;
+ godot::TextureRect* get_texture_rect_node(godot::NodePath const& path) const;
+
+ /* Helper functions to get textures from TextureRects and Buttons. */
+ godot::Ref<godot::Texture2D> get_texture_from_node(godot::NodePath const& path) const;
+ godot::Ref<GFXIconTexture> get_gfx_icon_texture_from_node(godot::NodePath const& path) const;
+ godot::Ref<GFXMaskedFlagTexture> get_gfx_masked_flag_texture_from_node(godot::NodePath const& path) const;
+ godot::Ref<GFXPieChartTexture> get_gfx_pie_chart_texture_from_node(godot::NodePath const& path) const;
+
+ godot::Error hide_node(godot::NodePath const& path) const;
+ godot::Error hide_nodes(godot::Array const& paths) const;
+ };
+}
diff --git a/extension/src/openvic-extension/register_types.cpp b/extension/src/openvic-extension/register_types.cpp
index 2f6f799..65bdc60 100644
--- a/extension/src/openvic-extension/register_types.cpp
+++ b/extension/src/openvic-extension/register_types.cpp
@@ -3,6 +3,9 @@
#include <godot_cpp/classes/engine.hpp>
#include "openvic-extension/classes/GFXIconTexture.hpp"
+#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
+#include "openvic-extension/classes/GFXPieChartTexture.hpp"
+#include "openvic-extension/classes/GUINode.hpp"
#include "openvic-extension/classes/MapMesh.hpp"
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/Checksum.hpp"
@@ -40,6 +43,9 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) {
ClassDB::register_class<MapMesh>();
ClassDB::register_class<GFXIconTexture>();
+ ClassDB::register_class<GFXMaskedFlagTexture>();
+ ClassDB::register_class<GFXPieChartTexture>();
+ ClassDB::register_class<GUINode>();
}
void uninitialize_openvic_types(ModuleInitializationLevel p_level) {
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp
index 4a80eb9..ac029d9 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp
@@ -17,6 +17,7 @@ using namespace OpenVic;
using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_to_godot_string;
+using OpenVic::Utilities::std_to_godot_string_name;
using OpenVic::Utilities::std_view_to_godot_string;
/* Maximum width or height a GPU texture can have. */
@@ -48,44 +49,27 @@ void GameSingleton::_bind_methods() {
OV_BIND_METHOD(GameSingleton::set_selected_province, { "index" });
OV_BIND_METHOD(GameSingleton::expand_building, { "province_index", "building_type_identifier" });
+ OV_BIND_METHOD(GameSingleton::get_slave_pop_icon_index);
+ OV_BIND_METHOD(GameSingleton::get_administrative_pop_icon_index);
+ OV_BIND_METHOD(GameSingleton::get_rgo_owner_pop_icon_index);
+ OV_BIND_SMETHOD(int_to_formatted_string, { "val" });
+ OV_BIND_SMETHOD(float_to_formatted_string, { "val" });
OV_BIND_METHOD(GameSingleton::set_paused, { "paused" });
OV_BIND_METHOD(GameSingleton::toggle_paused);
OV_BIND_METHOD(GameSingleton::is_paused);
OV_BIND_METHOD(GameSingleton::increase_speed);
OV_BIND_METHOD(GameSingleton::decrease_speed);
+ OV_BIND_METHOD(GameSingleton::get_speed);
OV_BIND_METHOD(GameSingleton::can_increase_speed);
OV_BIND_METHOD(GameSingleton::can_decrease_speed);
OV_BIND_METHOD(GameSingleton::get_longform_date);
OV_BIND_METHOD(GameSingleton::try_tick);
- OV_BIND_METHOD(GameSingleton::generate_gui, { "gui_file", "gui_element" });
-
ADD_SIGNAL(MethodInfo("state_updated"));
ADD_SIGNAL(MethodInfo("province_selected", PropertyInfo(Variant::INT, "index")));
}
-Control* GameSingleton::generate_gui(String const& gui_file, String const& gui_element) {
- GUI::Scene const* scene = game_manager.get_ui_manager().get_scene_by_identifier(godot_to_std_string(gui_file));
- if (scene == nullptr) {
- UtilityFunctions::push_error("Failed to find GUI file ", gui_file);
- return nullptr;
- }
- GUI::Element const* element = scene->get_element_by_identifier(godot_to_std_string(gui_element));
- if (element == nullptr) {
- UtilityFunctions::push_error("Failed to find GUI element ", gui_element, " in GUI file ", gui_file);
- return nullptr;
- }
-
- AssetManager* asset_manager = AssetManager::get_singleton();
- ERR_FAIL_NULL_V(asset_manager, nullptr);
- Control* result = nullptr;
- if (!GodotGUIBuilder::generate_element(element, *asset_manager, result)) {
- UtilityFunctions::push_error("Failed to generate GUI element ", gui_element, " in GUI file ", gui_file);
- }
- return result;
-}
-
GameSingleton* GameSingleton::get_singleton() {
return singleton;
}
@@ -139,8 +123,8 @@ Error GameSingleton::setup_game() {
ret &= dataloader.load_pop_history(game_manager, "history/pops/" + game_manager.get_today().to_string());
for (Province& province : game_manager.get_map().get_provinces()) {
province.set_crime(
- game_manager.get_modifier_manager().get_crime_modifier_by_index(
- (province.get_index() - 1) % game_manager.get_modifier_manager().get_crime_modifier_count()
+ game_manager.get_crime_manager().get_crime_modifier_by_index(
+ (province.get_index() - 1) % game_manager.get_crime_manager().get_crime_modifier_count()
)
);
}
@@ -185,13 +169,19 @@ static Array _distribution_to_pie_chart_array(fixed_point_map_t<T const*> const&
Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
static const StringName province_info_province_key = "province";
static const StringName province_info_region_key = "region";
+ static const StringName province_info_controller_key = "controller";
static const StringName province_info_life_rating_key = "life_rating";
static const StringName province_info_terrain_type_key = "terrain_type";
+ static const StringName province_info_crime_name_key = "crime_name";
+ static const StringName province_info_crime_icon_key = "crime_icon";
static const StringName province_info_total_population_key = "total_population";
static const StringName province_info_pop_types_key = "pop_types";
static const StringName province_info_pop_ideologies_key = "pop_ideologies";
static const StringName province_info_pop_cultures_key = "pop_cultures";
- static const StringName province_info_rgo_key = "rgo";
+ static const StringName province_info_rgo_name_key = "rgo_name";
+ static const StringName province_info_rgo_icon_key = "rgo_icon";
+ static const StringName province_info_colony_status_key = "colony_status";
+ static const StringName province_info_slave_status_key = "slave_status";
static const StringName province_info_buildings_key = "buildings";
Province const* province = game_manager.get_map().get_province_by_index(index);
@@ -207,9 +197,15 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
ret[province_info_region_key] = std_view_to_godot_string(region->get_identifier());
}
+ Country const* controller = province->get_controller();
+ if (controller != nullptr) {
+ ret[province_info_controller_key] = std_view_to_godot_string(controller->get_identifier());
+ }
+
Good const* rgo = province->get_rgo();
if (rgo != nullptr) {
- ret[province_info_rgo_key] = std_view_to_godot_string(rgo->get_identifier());
+ ret[province_info_rgo_name_key] = std_view_to_godot_string(rgo->get_identifier());
+ ret[province_info_rgo_icon_key] = static_cast<int32_t>(rgo->get_index());
}
ret[province_info_life_rating_key] = province->get_life_rating();
@@ -219,6 +215,15 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
ret[province_info_terrain_type_key] = std_view_to_godot_string(terrain_type->get_identifier());
}
+ Crime const* crime = province->get_crime();
+ if (crime != nullptr) {
+ ret[province_info_crime_name_key] = std_view_to_godot_string(crime->get_identifier());
+ ret[province_info_crime_icon_key] = static_cast<int32_t>(crime->get_icon());
+ }
+
+ ret[province_info_colony_status_key] = static_cast<int32_t>(province->get_colony_status());
+ ret[province_info_slave_status_key] = province->get_slave();
+
ret[province_info_total_population_key] = province->get_total_population();
fixed_point_map_t<PopType const*> const& pop_types = province->get_pop_type_distribution();
if (!pop_types.empty()) {
@@ -278,6 +283,27 @@ Ref<Texture> GameSingleton::get_terrain_texture() const {
return terrain_texture;
}
+Ref<Image> GameSingleton::get_flag_image(Country const* country, StringName const& flag_type) const {
+ if (country != nullptr) {
+ const typename decltype(flag_image_map)::const_iterator it = flag_image_map.find(country);
+ if (it != flag_image_map.end()) {
+ const typename decltype(it->second)::const_iterator it2 = it->second.find(flag_type);
+ if (it2 != it->second.end()) {
+ return it2->second;
+ } else {
+ UtilityFunctions::push_error(
+ "Failed to find ", flag_type, " flag for country: ", std_view_to_godot_string(country->get_identifier())
+ );
+ }
+ } else {
+ UtilityFunctions::push_error(
+ "Failed to find flags for country: ", std_view_to_godot_string(country->get_identifier())
+ );
+ }
+ }
+ return nullptr;
+}
+
Vector2i GameSingleton::get_province_shape_image_subdivisions() const {
return image_subdivisions;
}
@@ -370,6 +396,41 @@ Error GameSingleton::expand_building(int32_t province_index, String const& build
return OK;
}
+int32_t GameSingleton::get_slave_pop_icon_index() const {
+ const PopType::sprite_t sprite = game_manager.get_pop_manager().get_slave_sprite();
+ if (sprite <= 0) {
+ UtilityFunctions::push_error("Slave sprite unset!");
+ return 0;
+ }
+ return sprite;
+}
+
+int32_t GameSingleton::get_administrative_pop_icon_index() const {
+ const PopType::sprite_t sprite = game_manager.get_pop_manager().get_administrative_sprite();
+ if (sprite <= 0) {
+ UtilityFunctions::push_error("Administrative sprite unset!");
+ return 0;
+ }
+ return sprite;
+}
+
+int32_t GameSingleton::get_rgo_owner_pop_icon_index() const {
+ const PopType::sprite_t sprite = game_manager.get_economy_manager().get_production_type_manager().get_rgo_owner_sprite();
+ if (sprite <= 0) {
+ UtilityFunctions::push_error("RGO owner sprite unset!");
+ return 0;
+ }
+ return sprite;
+}
+
+String GameSingleton::int_to_formatted_string(int64_t val) {
+ return Utilities::int_to_formatted_string(val);
+}
+
+String GameSingleton::float_to_formatted_string(float val) {
+ return Utilities::float_to_formatted_string(val);
+}
+
void GameSingleton::set_paused(bool paused) {
game_manager.get_clock().is_paused = paused;
}
@@ -390,6 +451,10 @@ void GameSingleton::decrease_speed() {
game_manager.get_clock().decrease_simulation_speed();
}
+int32_t GameSingleton::get_speed() const {
+ return game_manager.get_clock().get_simulation_speed();
+}
+
bool GameSingleton::can_increase_speed() const {
return game_manager.get_clock().can_increase_simulation_speed();
}
@@ -527,6 +592,57 @@ Error GameSingleton::_load_terrain_variants() {
return err;
}
+Error GameSingleton::_load_flag_images() {
+ if (!flag_image_map.empty()) {
+ UtilityFunctions::push_error("Flag images have already been loaded!");
+ return FAILED;
+ }
+
+ GovernmentTypeManager const& government_type_manager = game_manager.get_politics_manager().get_government_type_manager();
+ if (!government_type_manager.government_types_are_locked()) {
+ UtilityFunctions::push_error("Cannot load flag images before government types are locked!");
+ return FAILED;
+ }
+ CountryManager const& country_manager = game_manager.get_country_manager();
+ if (!country_manager.countries_are_locked()) {
+ UtilityFunctions::push_error("Cannot load flag images before countries are locked!");
+ return FAILED;
+ }
+
+ AssetManager* asset_manager = AssetManager::get_singleton();
+ ERR_FAIL_NULL_V(asset_manager, FAILED);
+
+ static const String flag_directory = "gfx/flags/";
+ static const String flag_separator = "_";
+ static const String flag_extension = ".tga";
+
+ std::vector<StringName> flag_types;
+ for (std::string const& type : government_type_manager.get_flag_types()) {
+ flag_types.emplace_back(std_to_godot_string_name(type));
+ }
+
+ Error ret = OK;
+ for (Country const& country : country_manager.get_countries()) {
+ std::map<StringName, Ref<Image>>& flag_images = flag_image_map[&country];
+ const String country_name = std_view_to_godot_string(country.get_identifier());
+ for (StringName const& flag_type : flag_types) {
+ String flag_path = flag_directory + country_name;
+ if (!flag_type.is_empty()) {
+ flag_path += flag_separator + flag_type;
+ }
+ flag_path += flag_extension;
+ const Ref<Image> flag_image = asset_manager->get_image(flag_path);
+ if (flag_image.is_valid()) {
+ flag_images.emplace(flag_type, flag_image);
+ } else {
+ UtilityFunctions::push_error("Failed to load flag image: ", flag_path);
+ ret = FAILED;
+ }
+ }
+ }
+ return ret;
+}
+
Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& file_paths) {
Dataloader::path_vector_t roots;
for (String const& path : file_paths) {
@@ -547,6 +663,10 @@ Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& fi
UtilityFunctions::push_error("Failed to load terrain variants!");
err = FAILED;
}
+ if (_load_flag_images() != OK) {
+ UtilityFunctions::push_error("Failed to load flag textures!");
+ err = FAILED;
+ }
if (_load_map_images(true) != OK) {
UtilityFunctions::push_error("Failed to load map images!");
err = FAILED;
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp
index 108fd28..5895899 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.hpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp
@@ -25,10 +25,12 @@ namespace OpenVic {
godot::Ref<godot::ImageTexture> province_colour_texture;
Mapmode::index_t mapmode_index = 0;
godot::Ref<godot::Texture2DArray> terrain_texture;
+ std::map<Country const*, std::map<godot::StringName, godot::Ref<godot::Image>>> flag_image_map;
godot::Error _generate_terrain_texture_array();
godot::Error _load_map_images(bool flip_vertical);
godot::Error _load_terrain_variants();
+ godot::Error _load_flag_images();
/* Generate the province_colour_texture from the current mapmode. */
godot::Error _update_colour_image();
@@ -38,9 +40,6 @@ namespace OpenVic {
static void _bind_methods();
public:
-
- godot::Control* generate_gui(godot::String const& gui_file, godot::String const& gui_element);
-
static GameSingleton* get_singleton();
GameSingleton();
@@ -98,12 +97,18 @@ namespace OpenVic {
void set_selected_province(int32_t index);
godot::Error expand_building(int32_t province_index, godot::String const& building_type_identifier);
+ int32_t get_slave_pop_icon_index() const;
+ int32_t get_administrative_pop_icon_index() const;
+ int32_t get_rgo_owner_pop_icon_index() const;
+ static godot::String int_to_formatted_string(int64_t val);
+ static godot::String float_to_formatted_string(float val);
void set_paused(bool paused);
void toggle_paused();
bool is_paused() const;
void increase_speed();
void decrease_speed();
+ int32_t get_speed() const;
bool can_increase_speed() const;
bool can_decrease_speed() const;
godot::String get_longform_date() const;
diff --git a/extension/src/openvic-extension/utility/Utilities.cpp b/extension/src/openvic-extension/utility/Utilities.cpp
index e3bcce6..099b5a9 100644
--- a/extension/src/openvic-extension/utility/Utilities.cpp
+++ b/extension/src/openvic-extension/utility/Utilities.cpp
@@ -13,6 +13,37 @@
using namespace godot;
using namespace OpenVic;
+/* Int to 2 decimal place string in terms of the largest suffix less than or equal to it,
+ * or normal integer string if less than the smallest suffix. */
+String Utilities::int_to_formatted_string(int64_t val) {
+ static const std::vector<std::pair<int64_t, String>> suffixes {
+ { 1'000'000'000'000, "T" },
+ { 1'000'000'000, "B" },
+ { 1'000'000, "M" },
+ { 1'000, "k" }
+ };
+ static constexpr int64_t decimal_places_multiplier = 100;
+ const bool negative = val < 0;
+ if (negative) {
+ val = -val;
+ }
+ for (auto const& [suffix_val, suffix_str] : suffixes) {
+ if (val >= suffix_val) {
+ const int64_t whole = val / suffix_val;
+ const int64_t frac = (val * decimal_places_multiplier / suffix_val) % decimal_places_multiplier;
+ return (negative ? "-" : "") + String::num_int64(whole) + "." +
+ (frac < 10 ? "0" : "") + String::num_int64(frac) + suffix_str;
+ }
+ }
+ return (negative ? "-" : "") + String::num_int64(val);
+}
+
+/* Float to formatted to 4 decimal place string. */
+String Utilities::float_to_formatted_string(float val) {
+ static constexpr int64_t decimal_places = 4;
+ return String::num(val, decimal_places).pad_decimals(decimal_places);
+}
+
/* Date formatted like this: "January 1, 1836" (with the month localised, if possible). */
String Utilities::date_to_formatted_string(Date date) {
std::string const& month_name = date.get_month_name();
diff --git a/extension/src/openvic-extension/utility/Utilities.hpp b/extension/src/openvic-extension/utility/Utilities.hpp
index 9537dda..6eeb285 100644
--- a/extension/src/openvic-extension/utility/Utilities.hpp
+++ b/extension/src/openvic-extension/utility/Utilities.hpp
@@ -31,6 +31,10 @@ namespace OpenVic::Utilities {
return std_to_godot_string_name(static_cast<std::string>(str));
}
+ godot::String int_to_formatted_string(int64_t val);
+
+ godot::String float_to_formatted_string(float val);
+
godot::String date_to_formatted_string(Date date);
inline godot::Color to_godot_color(colour_t colour) {
diff --git a/game/src/Game/Autoload/Events.gd b/game/src/Game/Autoload/Events.gd
index da12bf6..da29adb 100644
--- a/game/src/Game/Autoload/Events.gd
+++ b/game/src/Game/Autoload/Events.gd
@@ -3,9 +3,7 @@
## It does such by providing a global interface of signals that are connected to and emitted by that are garunteed to exist.
extends Node
-var Loader: LoaderEventsObject
var Options: OptionsEventsObject
func _init():
- Loader = LoaderEventsObject.new()
Options = OptionsEventsObject.new()
diff --git a/game/src/Game/Autoload/Events/Loader.gd b/game/src/Game/Autoload/Events/Loader.gd
deleted file mode 100644
index c17dc6f..0000000
--- a/game/src/Game/Autoload/Events/Loader.gd
+++ /dev/null
@@ -1,6 +0,0 @@
-class_name LoaderEventsObject
-extends RefCounted
-
-signal startup_load_begun()
-signal startup_load_changed(percentage : float)
-signal startup_load_ended()
diff --git a/game/src/Game/GameSession/GameSession.gd b/game/src/Game/GameSession/GameSession.gd
index afff820..2a27e3d 100644
--- a/game/src/Game/GameSession/GameSession.gd
+++ b/game/src/Game/GameSession/GameSession.gd
@@ -7,12 +7,6 @@ func _ready():
if GameSingleton.setup_game() != OK:
push_error("Failed to setup game")
- # Temporarily here for cosmetic reasons, will be moved to its
- # own child node later, similar to ProvinceOverviewPanel
- add_child(GameSingleton.generate_gui("topbar.gui", "topbar"))
- $topbar/topbar_outlinerbutton_bg.visible = false
- $topbar/topbar_outlinerbutton.visible = false
-
func _process(_delta : float):
GameSingleton.try_tick()
diff --git a/game/src/Game/GameSession/GameSession.tscn b/game/src/Game/GameSession/GameSession.tscn
index 755139e..2dd0e16 100644
--- a/game/src/Game/GameSession/GameSession.tscn
+++ b/game/src/Game/GameSession/GameSession.tscn
@@ -4,10 +4,10 @@
[ext_resource type="PackedScene" uid="uid://cvl76duuym1wq" path="res://src/Game/MusicConductor/MusicPlayer.tscn" id="2_kt6aa"]
[ext_resource type="PackedScene" uid="uid://g524p8lr574w" path="res://src/Game/GameSession/MapControlPanel/MapControlPanel.tscn" id="3_afh6d"]
[ext_resource type="PackedScene" uid="uid://dvdynl6eir40o" path="res://src/Game/GameSession/GameSessionMenu.tscn" id="3_bvmqh"]
+[ext_resource type="Script" path="res://src/Game/GameSession/Topbar.gd" id="4_2kbih"]
[ext_resource type="PackedScene" uid="uid://dkehmdnuxih2r" path="res://src/Game/GameSession/MapView.tscn" id="4_xkg5j"]
[ext_resource type="Script" path="res://src/Game/GameSession/ProvinceOverviewPanel.gd" id="5_lfv8l"]
[ext_resource type="PackedScene" uid="uid://cnbfxjy1m6wja" path="res://src/Game/Menu/OptionMenu/OptionsMenu.tscn" id="6_p5mnx"]
-[ext_resource type="PackedScene" uid="uid://dd8k3p7r3huwc" path="res://src/Game/GameSession/GameSpeedPanel.tscn" id="7_myy4q"]
[ext_resource type="PackedScene" uid="uid://d3g6wbvwflmyk" path="res://src/Game/Menu/SaveLoadMenu/SaveLoadMenu.tscn" id="8_4g7ko"]
[node name="GameSession" type="Control" node_paths=PackedStringArray("_game_session_menu")]
@@ -24,7 +24,7 @@ _game_session_menu = NodePath("GameSessionMenu")
[node name="MapView" parent="." instance=ExtResource("4_xkg5j")]
-[node name="ProvinceOverviewPanel" type="Control" parent="."]
+[node name="ProvinceOverviewPanel" type="GUINode" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
@@ -34,6 +34,12 @@ grow_vertical = 2
mouse_filter = 1
script = ExtResource("5_lfv8l")
+[node name="Topbar" type="GUINode" parent="."]
+layout_mode = 0
+offset_right = 40.0
+offset_bottom = 40.0
+script = ExtResource("4_2kbih")
+
[node name="MapControlPanel" parent="." instance=ExtResource("3_afh6d")]
layout_mode = 1
anchors_preset = 3
@@ -44,11 +50,6 @@ anchor_bottom = 1.0
grow_horizontal = 0
grow_vertical = 0
-[node name="GameSpeedPanel" parent="." instance=ExtResource("7_myy4q")]
-layout_mode = 0
-offset_right = 302.0
-offset_bottom = 31.0
-
[node name="GameSessionMenu" parent="." instance=ExtResource("3_bvmqh")]
visible = false
layout_mode = 1
diff --git a/game/src/Game/GameSession/GameSpeedPanel.gd b/game/src/Game/GameSession/GameSpeedPanel.gd
deleted file mode 100644
index 6a4b4de..0000000
--- a/game/src/Game/GameSession/GameSpeedPanel.gd
+++ /dev/null
@@ -1,43 +0,0 @@
-extends PanelContainer
-
-# REQUIREMENTS:
-# * SS-37, SS-38, SS-39
-
-@export var _longform_date_button : Button
-@export var _play_pause_display_button : Button
-@export var _decrease_speed_button : Button
-@export var _increase_speed_button : Button
-
-func _ready():
- GameSingleton.state_updated.connect(_update_buttons)
- _update_buttons()
-
-func _update_buttons():
- _play_pause_display_button.text = "⏸ " if GameSingleton.is_paused() else "▶"
-
- _increase_speed_button.disabled = not GameSingleton.can_increase_speed()
- _decrease_speed_button.disabled = not GameSingleton.can_decrease_speed()
-
- _longform_date_button.text = GameSingleton.get_longform_date()
-
-# REQUIREMENTS:
-# * UIFUN-73
-func _on_decrease_speed_button_pressed():
- GameSingleton.decrease_speed()
- _update_buttons()
-
-# REQUIREMENTS:
-# * UIFUN-72
-func _on_increase_speed_button_pressed():
- GameSingleton.increase_speed()
- _update_buttons()
-
-# REQUIREMENTS:
-# * UIFUN-71
-func _on_play_pause_display_button_pressed():
- GameSingleton.toggle_paused()
- _update_buttons()
-
-func _on_longform_date_label_pressed():
- GameSingleton.toggle_paused()
- _update_buttons()
diff --git a/game/src/Game/GameSession/GameSpeedPanel.tscn b/game/src/Game/GameSession/GameSpeedPanel.tscn
deleted file mode 100644
index 5526427..0000000
--- a/game/src/Game/GameSession/GameSpeedPanel.tscn
+++ /dev/null
@@ -1,67 +0,0 @@
-[gd_scene load_steps=8 format=3 uid="uid://dd8k3p7r3huwc"]
-
-[ext_resource type="Script" path="res://src/Game/GameSession/GameSpeedPanel.gd" id="1_pfs8t"]
-
-[sub_resource type="InputEventAction" id="InputEventAction_3k1tl"]
-action = &"time_pause"
-
-[sub_resource type="Shortcut" id="Shortcut_cg5xm"]
-events = [SubResource("InputEventAction_3k1tl")]
-
-[sub_resource type="InputEventAction" id="InputEventAction_w2rkb"]
-action = &"time_speed_decrease"
-
-[sub_resource type="Shortcut" id="Shortcut_ocrfe"]
-events = [SubResource("InputEventAction_w2rkb")]
-
-[sub_resource type="InputEventAction" id="InputEventAction_7sdhp"]
-action = &"time_speed_increase"
-
-[sub_resource type="Shortcut" id="Shortcut_gwofc"]
-events = [SubResource("InputEventAction_7sdhp")]
-
-[node name="GameSpeedPanel" type="PanelContainer" node_paths=PackedStringArray("_longform_date_button", "_play_pause_display_button", "_decrease_speed_button", "_increase_speed_button")]
-script = ExtResource("1_pfs8t")
-_longform_date_button = NodePath("ButtonList/LongformDateButton")
-_play_pause_display_button = NodePath("ButtonList/PlayPauseDisplayButton")
-_decrease_speed_button = NodePath("ButtonList/DecreaseSpeedButton")
-_increase_speed_button = NodePath("ButtonList/IncreaseSpeedButton")
-
-[node name="ButtonList" type="HBoxContainer" parent="."]
-layout_mode = 2
-
-[node name="LongformDateButton" type="Button" parent="ButtonList"]
-editor_description = "UI-74"
-custom_minimum_size = Vector2(200, 0)
-layout_mode = 2
-focus_mode = 0
-text = "LONGFORM DATE"
-
-[node name="PlayPauseDisplayButton" type="Button" parent="ButtonList"]
-editor_description = "UI-75, UIFUN-90"
-custom_minimum_size = Vector2(30, 0)
-layout_mode = 2
-focus_mode = 0
-shortcut = SubResource("Shortcut_cg5xm")
-text = "⏸ "
-
-[node name="DecreaseSpeedButton" type="Button" parent="ButtonList"]
-editor_description = "UI-77"
-custom_minimum_size = Vector2(30, 0)
-layout_mode = 2
-focus_mode = 0
-shortcut = SubResource("Shortcut_ocrfe")
-text = "-"
-
-[node name="IncreaseSpeedButton" type="Button" parent="ButtonList"]
-editor_description = "UI-76"
-custom_minimum_size = Vector2(30, 0)
-layout_mode = 2
-focus_mode = 0
-shortcut = SubResource("Shortcut_gwofc")
-text = "+"
-
-[connection signal="pressed" from="ButtonList/LongformDateButton" to="." method="_on_longform_date_label_pressed"]
-[connection signal="pressed" from="ButtonList/PlayPauseDisplayButton" to="." method="_on_play_pause_display_button_pressed"]
-[connection signal="pressed" from="ButtonList/DecreaseSpeedButton" to="." method="_on_decrease_speed_button_pressed"]
-[connection signal="pressed" from="ButtonList/IncreaseSpeedButton" to="." method="_on_increase_speed_button_pressed"]
diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel.gd b/game/src/Game/GameSession/ProvinceOverviewPanel.gd
index 1292eb4..6c183fb 100644
--- a/game/src/Game/GameSession/ProvinceOverviewPanel.gd
+++ b/game/src/Game/GameSession/ProvinceOverviewPanel.gd
@@ -1,22 +1,54 @@
-extends Control
+extends GUINode
+# Header
var _province_name_label : Label
var _region_name_label : Label
+var _slave_status_icon : TextureRect
+var _colony_status_button : Button
+var _colony_status_button_texture : GFXIconTexture
+var _administrative_percentage_label : Label
+var _owner_percentage_label : Label
+var _terrain_type_texture : GFXIconTexture
var _life_rating_bar : TextureProgressBar
+var _controller_flag_texture : GFXMaskedFlagTexture
+# state and province modifiers
+
+# Statistics
+var _rgo_icon_texture : GFXIconTexture
+var _rgo_produced_label : Label
+var _rgo_income_label : Label
+# rgo employment progress bar (execpt it isn't a progress bar?)
+var _rgo_employment_population_label : Label
+var _rgo_employment_percentage_label : Label
+var _crime_name_label : Label
+var _crime_icon_texture : GFXIconTexture
+# crime fighting
var _total_population_label : Label
-var _rgo_icon_texture : AtlasTexture
+var _migration_label : Label
+var _population_growth_label : Label
+var _pop_types_piechart : GFXPieChartTexture
+var _pop_ideologies_piechart : GFXPieChartTexture
+var _pop_cultures_piechart : GFXPieChartTexture
+# supply_limit_label
+# cores
const _missing_suffix : String = "_MISSING"
const _province_info_province_key : StringName = &"province"
const _province_info_region_key : StringName = &"region"
+const _province_info_controller_key : StringName = &"controller"
const _province_info_life_rating_key : StringName = &"life_rating"
const _province_info_terrain_type_key : StringName = &"terrain_type"
+const _province_info_crime_name_key : StringName = &"crime_name"
+const _province_info_crime_icon_key : StringName = &"crime_icon"
const _province_info_total_population_key : StringName = &"total_population"
const _province_info_pop_types_key : StringName = &"pop_types"
const _province_info_pop_ideologies_key : StringName = &"pop_ideologies"
const _province_info_pop_cultures_key : StringName = &"pop_cultures"
-const _province_info_rgo_key : StringName = &"rgo"
+const _province_info_rgo_name_key : StringName = &"rgo_name"
+const _province_info_rgo_icon_key : StringName = &"rgo_icon"
+const _province_info_colony_status_key : StringName = &"colony_status"
+const _province_info_slave_status_key : StringName = &"slave_status"
const _province_info_buildings_key : StringName = &"buildings"
const _building_info_building_key : StringName = &"building"
@@ -36,67 +68,79 @@ var _selected_index : int:
_update_info()
var _province_info : Dictionary
-func _check_class(object : Object, klass : String, name : String) -> bool:
- if object.get_class() == klass:
- return true
- else:
- push_error("Invalid ", name, " class: ", object.get_class(), " (expected ", klass, ")")
- return false
-
-func _try_get_node(path : NodePath, klass : String) -> Node:
- var node : Node = get_node(path)
- if node != null:
- if _check_class(node, klass, path):
- return node
- else:
- push_error("Failed to get node: ", path, " (returned null)")
- return null
-
func _ready():
GameSingleton.province_selected.connect(_on_province_selected)
GameSingleton.state_updated.connect(_update_info)
- add_child(GameSingleton.generate_gui("province_interface.gui", "province_view"))
+ add_gui_element("province_interface.gui", "province_view")
- var close_button : Button = _try_get_node(^"./province_view/close_button", "Button")
- if close_button != null:
+ var close_button : Button = get_button_node(^"./province_view/close_button")
+ if close_button:
close_button.pressed.connect(_on_close_button_pressed)
- _region_name_label = _try_get_node(^"./province_view/province_view_header/state_name", "Label")
-
- _province_name_label = _try_get_node(^"./province_view/province_view_header/province_name", "Label")
-
- _life_rating_bar = _try_get_node(^"./province_view/province_view_header/liferating", "TextureProgressBar")
-
- var goods_icon : TextureRect = _try_get_node(^"./province_view/province_statistics/goods_type", "TextureRect")
- if goods_icon != null:
- var texture := goods_icon.texture
- if _check_class(texture, "GFXIconTexture", "good_texture"):
- _rgo_icon_texture = texture
-
-
- var rgo_population_label : Label = _try_get_node(^"./province_view/province_statistics/rgo_population", "Label")
- if rgo_population_label != null:
- rgo_population_label.text = "0"
-
- #^"./province_view/province_statistics/crime_icon"
-
- _total_population_label = _try_get_node(^"./province_view/province_statistics/total_population", "Label")
-
- #^"./province_view/province_statistics/growth"
- #^"./province_view/province_statistics/workforce_chart"
- #^"./province_view/province_statistics/ideology_chart"
- #^"./province_view/province_statistics/culture_chart"
-
- $province_view/province_view_header/occupation_progress.visible = false
- $province_view/province_view_header/occupation_icon.visible = false
- $province_view/province_view_header/occupation_flag.visible = false
- $province_view/province_colony.visible = false
- $province_view/province_other.visible = false
- $province_view/province_buildings/army_size.visible = false
- $province_view/province_buildings/army_text.visible = false
- $province_view/province_buildings/navy_text.visible = false
- $province_view/national_focus_window.visible = false
+ # Header
+ _province_name_label = get_label_node(^"./province_view/province_view_header/province_name")
+ _region_name_label = get_label_node(^"./province_view/province_view_header/state_name")
+ _slave_status_icon = get_texture_rect_node(^"./province_view/province_view_header/slave_state_icon")
+ var slave_status_icon_texture : GFXIconTexture = get_gfx_icon_texture_from_node(^"./province_view/province_view_header/slave_state_icon")
+ if slave_status_icon_texture:
+ slave_status_icon_texture.set_icon_index(GameSingleton.get_slave_pop_icon_index())
+ _colony_status_button = get_button_node(^"./province_view/province_view_header/colony_button")
+ _colony_status_button_texture = get_gfx_icon_texture_from_node(^"./province_view/province_view_header/colony_button")
+ var admin_icon_texture : GFXIconTexture = get_gfx_icon_texture_from_node(^"./province_view/province_view_header/admin_icon")
+ if admin_icon_texture:
+ admin_icon_texture.set_icon_index(GameSingleton.get_administrative_pop_icon_index())
+ _administrative_percentage_label = get_label_node(^"./province_view/province_view_header/admin_efficiency")
+ _owner_percentage_label = get_label_node(^"./province_view/province_view_header/owner_presence")
+ _terrain_type_texture = get_gfx_icon_texture_from_node(^"./province_view/province_view_header/prov_terrain")
+ _life_rating_bar = get_progress_bar_node(^"./province_view/province_view_header/liferating")
+ _controller_flag_texture = get_gfx_masked_flag_texture_from_node(^"./province_view/province_view_header/controller_flag")
+
+ # Statistics
+ _rgo_icon_texture = get_gfx_icon_texture_from_node(^"./province_view/province_statistics/goods_type")
+ _rgo_produced_label = get_label_node(^"./province_view/province_statistics/produced")
+ _rgo_income_label = get_label_node(^"./province_view/province_statistics/income")
+ _rgo_employment_population_label = get_label_node(^"./province_view/province_statistics/rgo_population")
+ _rgo_employment_percentage_label = get_label_node(^"./province_view/province_statistics/rgo_percent")
+ _crime_name_label = get_label_node(^"./province_view/province_statistics/crime_name")
+ _crime_icon_texture = get_gfx_icon_texture_from_node(^"./province_view/province_statistics/crime_icon")
+ _total_population_label = get_label_node(^"./province_view/province_statistics/total_population")
+ _migration_label = get_label_node(^"./province_view/province_statistics/migration")
+ _population_growth_label = get_label_node(^"./province_view/province_statistics/growth")
+ _pop_types_piechart = get_gfx_pie_chart_texture_from_node(^"./province_view/province_statistics/workforce_chart")
+ _pop_ideologies_piechart = get_gfx_pie_chart_texture_from_node(^"./province_view/province_statistics/ideology_chart")
+ _pop_cultures_piechart = get_gfx_pie_chart_texture_from_node(^"./province_view/province_statistics/culture_chart")
+
+ #^"./province_view/building"
+ #^"./province_view/province_core"
+ #^"./province_view/prov_state_modifier"
+
+ #add_gui_element("province_interface.gui", "building", "building0")
+ #var building0 : Panel = get_panel_node(^"./building0")
+ #building0.set_anchors_and_offsets_preset(Control.PRESET_BOTTOM_LEFT)
+ #building0.set_position(pop_cultures_piechart_icon.get_position())
+
+ # TODO - fix checkbox positions
+ for path in [
+ ^"./province_view/province_buildings/rallypoint_checkbox",
+ ^"./province_view/province_buildings/rallypoint_merge_checkbox",
+ ^"./province_view/province_buildings/rallypoint_checkbox_naval",
+ ^"./province_view/province_buildings/rallypoint_merge_checkbox_naval"
+ ]:
+ var rally_checkbox : CheckBox = get_check_box_node(path)
+ rally_checkbox.set_position(rally_checkbox.get_position() - Vector2(3, 3))
+
+ hide_nodes([
+ ^"./province_view/province_view_header/occupation_progress",
+ ^"./province_view/province_view_header/occupation_icon",
+ ^"./province_view/province_view_header/occupation_flag",
+ ^"./province_view/province_colony",
+ ^"./province_view/province_other",
+ ^"./province_view/province_buildings/army_size",
+ ^"./province_view/province_buildings/army_text",
+ ^"./province_view/province_buildings/navy_text",
+ ^"./province_view/national_focus_window",
+ ])
_update_info()
@@ -177,27 +221,92 @@ func _set_building_row(index : int, building : Dictionary) -> void:
row.button.disabled = expansion_state != CAN_EXPAND
row.button.visible = not show_progress_bar
"""
+
+enum { STATE, PROTECTORATE, COLONY }
func _update_info() -> void:
_province_info = GameSingleton.get_province_info_from_index(_selected_index)
if _province_info:
+ # Header
if _province_name_label:
_province_name_label.text = "PROV" + _province_info.get(_province_info_province_key,
_province_info_province_key + _missing_suffix)
+
if _region_name_label:
_region_name_label.text = _province_info.get(_province_info_region_key,
_province_info_region_key + _missing_suffix)
+
+ if _slave_status_icon:
+ _slave_status_icon.visible = _province_info.get(_province_info_slave_status_key, false)
+
+ var colony_status : int = _province_info.get(_province_info_colony_status_key, 0)
+ if _colony_status_button:
+ if colony_status == STATE:
+ _colony_status_button.hide()
+ else:
+ if _colony_status_button_texture:
+ _colony_status_button_texture.set_icon_index(colony_status)
+ _colony_status_button.show()
+
+ if _administrative_percentage_label:
+ pass
+
+ if _owner_percentage_label:
+ pass
+
+ if _terrain_type_texture:
+ var terrain_type : String = _province_info.get(_province_info_terrain_type_key, "")
+ if terrain_type:
+ const _terrain_type_prefix : String = "GFX_terrainimg_"
+ if _terrain_type_texture.set_gfx_texture_sprite_name(_terrain_type_prefix + terrain_type) != OK:
+ push_error("Failed to set terrain type texture: ", terrain_type)
+
if _life_rating_bar:
- _life_rating_bar.value = _province_info.get(_province_info_life_rating_key, 0) * 0
+ _life_rating_bar.value = _province_info.get(_province_info_life_rating_key, 0)
+
+ if _controller_flag_texture:
+ var controller : String = _province_info.get(_province_info_controller_key, "REB")
+ _controller_flag_texture.set_flag_country_name_and_type(controller, &"")
+
+ # Statistics
+ if _rgo_icon_texture:
+ _rgo_icon_texture.set_icon_index(_province_info.get(_province_info_rgo_icon_key, -1) + 2)
+
+ if _rgo_produced_label:
+ _rgo_produced_label.text = _province_info.get(_province_info_rgo_name_key, _province_info_rgo_name_key + _missing_suffix)
+
+ if _rgo_income_label:
+ _rgo_income_label.text = GameSingleton.float_to_formatted_string(12.34567)
+ # TODO - add £ sign
+
+ if _rgo_employment_population_label:
+ _rgo_employment_population_label.text = GameSingleton.int_to_formatted_string(_province_info.get(_province_info_total_population_key, 0) / 10)
+
+ if _rgo_employment_percentage_label:
+ pass
+
+ if _crime_name_label:
+ _crime_name_label.text = _province_info.get(_province_info_crime_name_key, "")
+
+ if _crime_icon_texture:
+ _crime_icon_texture.set_icon_index(_province_info.get(_province_info_crime_icon_key, 0) + 1)
if _total_population_label:
- _total_population_label.text = Localisation.tr_number(_province_info.get(_province_info_total_population_key, 0))
+ _total_population_label.text = GameSingleton.int_to_formatted_string(_province_info.get(_province_info_total_population_key, 0))
- #_pop_type_chart.set_to_distribution(_province_info.get(_province_info_pop_types_key, {}))
- #_pop_ideology_chart.set_to_distribution(_province_info.get(_province_info_pop_ideologies_key, {}))
- #_pop_culture_chart.set_to_distribution(_province_info.get(_province_info_pop_cultures_key, {}))
+ if _migration_label:
+ pass
- if _rgo_icon_texture:
- _rgo_icon_texture.set_icon_index((_selected_index % 40) + 1)
+ if _population_growth_label:
+ pass
+
+ if _pop_types_piechart:
+ _pop_types_piechart.set_slices(_province_info.get(_province_info_pop_types_key, []))
+
+ if _pop_ideologies_piechart:
+ _pop_ideologies_piechart.set_slices(_province_info.get(_province_info_pop_ideologies_key, []))
+
+ if _pop_cultures_piechart:
+ _pop_cultures_piechart.set_slices(_province_info.get(_province_info_pop_cultures_key, []))
#var buildings : Array = _province_info.get(_province_info_buildings_key, [])
#for i in max(buildings.size(), _building_rows.size()):
diff --git a/game/src/Game/GameSession/Topbar.gd b/game/src/Game/GameSession/Topbar.gd
new file mode 100644
index 0000000..6cc710f
--- /dev/null
+++ b/game/src/Game/GameSession/Topbar.gd
@@ -0,0 +1,84 @@
+extends GUINode
+
+var _speed_up_button : Button
+var _speed_down_button : Button
+var _speed_indicator_button : Button
+var _speed_indicator_texture : GFXIconTexture
+var _date_label : Label
+var _country_name_label : Label
+
+func _ready():
+ GameSingleton.state_updated.connect(_update_info)
+
+ add_gui_element("topbar.gui", "topbar")
+
+ hide_nodes([
+ ^"./topbar/topbar_outlinerbutton_bg",
+ ^"./topbar/topbar_outlinerbutton"
+ ])
+
+ const player_country : String = "SLV"
+
+ var player_flag_texture : GFXMaskedFlagTexture = get_gfx_masked_flag_texture_from_node(^"./topbar/player_flag")
+ if player_flag_texture:
+ player_flag_texture.set_flag_country_name_and_type(player_country, &"")
+
+ _speed_up_button = get_button_node(^"./topbar/button_speedup")
+ if _speed_up_button:
+ _speed_up_button.pressed.connect(_on_increase_speed_button_pressed)
+
+ _speed_down_button = get_button_node(^"./topbar/button_speeddown")
+ if _speed_down_button:
+ _speed_down_button.pressed.connect(_on_decrease_speed_button_pressed)
+
+ var pause_bg_button : Button = get_button_node(^"./topbar/pause_bg")
+ if pause_bg_button:
+ pause_bg_button.pressed.connect(_on_play_pause_button_pressed)
+
+ _date_label = get_label_node(^"./topbar/DateText")
+
+ _country_name_label = get_label_node(^"./topbar/CountryName")
+ if _country_name_label:
+ _country_name_label.text = player_country
+
+ _speed_indicator_button = get_button_node(^"./topbar/speed_indicator")
+ _speed_indicator_texture = get_gfx_icon_texture_from_node(^"./topbar/speed_indicator")
+
+func _update_info() -> void:
+ if _date_label:
+ _date_label.text = GameSingleton.get_longform_date()
+
+ # TODO - add disabled state textures so this doesn't hide the buttons
+ #if _speed_up_button:
+ # _speed_up_button.disabled = not GameSingleton.can_increase_speed()
+
+ #if _speed_down_button:
+ # _speed_down_button.disabled = not GameSingleton.can_decrease_speed()
+
+ if _speed_indicator_button and _speed_indicator_texture:
+ var index : int = 1
+ if not GameSingleton.is_paused():
+ index += GameSingleton.get_speed() + 1
+ _speed_indicator_texture.set_icon_index(index)
+ _speed_indicator_button.queue_redraw()
+
+# REQUIREMENTS:
+# * UIFUN-71
+func _on_play_pause_button_pressed():
+ print("Toggling pause!")
+ GameSingleton.toggle_paused()
+ _update_info()
+
+# REQUIREMENTS:
+# * UIFUN-72
+func _on_increase_speed_button_pressed():
+ print("Speed up!")
+ GameSingleton.increase_speed()
+ _update_info()
+
+# REQUIREMENTS:
+# * UIFUN-73
+func _on_decrease_speed_button_pressed():
+ print("Speed down!")
+ GameSingleton.decrease_speed()
+ _update_info()
diff --git a/game/src/Game/LoadingScreen.gd b/game/src/Game/LoadingScreen.gd
index c7dad9c..08cd2a8 100644
--- a/game/src/Game/LoadingScreen.gd
+++ b/game/src/Game/LoadingScreen.gd
@@ -21,7 +21,6 @@ func start_loading_screen(thread_safe_function : Callable) -> void:
thread.wait_to_finish()
thread.start(thread_safe_function)
- Events.Loader.startup_load_begun.emit()
func try_update_loading_screen(percent_complete: float, quote_should_change = false):
# forces the function to behave as if deferred
@@ -29,11 +28,6 @@ func try_update_loading_screen(percent_complete: float, quote_should_change = fa
progress_bar.value = percent_complete
if quote_should_change:
quote_label.text = quotes[randi() % quotes.size()]
- if is_equal_approx(percent_complete, 100):
- thread.wait_to_finish()
- Events.Loader.startup_load_ended.emit()
- else:
- Events.Loader.startup_load_changed.emit(percent_complete)
func _ready():
if Engine.is_editor_hint(): return