diff options
author | Hop311 <Hop3114@gmail.com> | 2023-12-26 00:00:02 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-26 00:00:02 +0100 |
commit | d114ecaa5a54f1e8e20828561a3cd26a09dc10a3 (patch) | |
tree | a59c5b960a706a383b8ebd1dbcfb704067a5b51b | |
parent | d26c990d9a5596a3ef3b32ba1cb0f99950cd6d34 (diff) | |
parent | 4e9764ee29fb7b453862835d5aa3a081b0f9a269 (diff) |
Merge pull request #179 from OpenVicProject/ui-work
GUIOverlappingElementsBox + GUINode improvements
31 files changed, 942 insertions, 602 deletions
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation -Subproject 14e47d58b85f657ec1fed8abf88219f09bd3efb +Subproject bff91f78f9c5339079c10adfbf8232e5159c1a2 diff --git a/extension/src/openvic-extension/UIAdapter.hpp b/extension/src/openvic-extension/UIAdapter.hpp deleted file mode 100644 index 258b5e9..0000000 --- a/extension/src/openvic-extension/UIAdapter.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include <godot_cpp/classes/control.hpp> - -#include <openvic-simulation/interface/GUI.hpp> - -#include "openvic-extension/singletons/AssetManager.hpp" - -namespace OpenVic::GodotGUIBuilder { - - 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/GFXIconTexture.cpp b/extension/src/openvic-extension/classes/GFXIconTexture.cpp index 57c5d50..895bf6b 100644 --- a/extension/src/openvic-extension/classes/GFXIconTexture.cpp +++ b/extension/src/openvic-extension/classes/GFXIconTexture.cpp @@ -5,6 +5,7 @@ #include "openvic-extension/singletons/AssetManager.hpp" #include "openvic-extension/singletons/GameSingleton.hpp" #include "openvic-extension/utility/ClassBindings.hpp" +#include "openvic-extension/utility/UITools.hpp" #include "openvic-extension/utility/Utilities.hpp" using namespace godot; @@ -71,12 +72,8 @@ Error GFXIconTexture::set_gfx_texture_sprite_name(String const& gfx_texture_spri if (gfx_texture_sprite_name.is_empty()) { return set_gfx_texture_sprite(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_texture_sprite_name) - ); - ERR_FAIL_NULL_V_MSG(sprite, FAILED, vformat("GFX sprite not found: %s", gfx_texture_sprite_name)); + GFX::Sprite const* sprite = UITools::get_gfx_sprite(gfx_texture_sprite_name); + ERR_FAIL_NULL_V(sprite, FAILED); GFX::TextureSprite const* new_texture_sprite = sprite->cast_to<GFX::TextureSprite>(); ERR_FAIL_NULL_V_MSG( new_texture_sprite, FAILED, vformat( diff --git a/extension/src/openvic-extension/classes/GFXIconTexture.hpp b/extension/src/openvic-extension/classes/GFXIconTexture.hpp index 3ed5b3e..176d855 100644 --- a/extension/src/openvic-extension/classes/GFXIconTexture.hpp +++ b/extension/src/openvic-extension/classes/GFXIconTexture.hpp @@ -35,12 +35,12 @@ namespace OpenVic { ); /* Search for a GFX::TextureSprite with the specfied name and, - * if successful, call set_gfx_texture_sprite to set it and its icon */ + * if successful, call set_gfx_texture_sprite to set it and its icon. */ godot::Error set_gfx_texture_sprite_name( godot::String const& gfx_texture_sprite_name, GFX::frame_t icon = GFX::NO_FRAMES ); - /* Return the name of the GFX::TextureSprite, or an empty String if it's null */ + /* Return the name of the GFX::TextureSprite, or an empty String if it's null. */ godot::String get_gfx_texture_sprite_name() const; /* Set icon_index to a value between one and icon_count (inclusive), and update the AtlasTexture's region diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp index 3636855..0ed7755 100644 --- a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp +++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp @@ -1,10 +1,9 @@ #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/UITools.hpp" #include "openvic-extension/utility/Utilities.hpp" using namespace godot; @@ -66,6 +65,7 @@ void GFXMaskedFlagTexture::_bind_methods() { 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::set_flag_country_name, { "new_flag_country_name" }); OV_BIND_METHOD(GFXMaskedFlagTexture::get_flag_country_name); OV_BIND_METHOD(GFXMaskedFlagTexture::get_flag_type); } @@ -123,12 +123,8 @@ Error GFXMaskedFlagTexture::set_gfx_masked_flag_name(String const& gfx_masked_fl 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::Sprite const* sprite = UITools::get_gfx_sprite(gfx_masked_flag_name); + ERR_FAIL_NULL_V(sprite, FAILED); GFX::MaskedFlag const* new_masked_flag = sprite->cast_to<GFX::MaskedFlag>(); ERR_FAIL_NULL_V_MSG( new_masked_flag, FAILED, vformat( @@ -158,6 +154,7 @@ Error GFXMaskedFlagTexture::set_flag_country_and_type(Country const* new_flag_co flag_type = new_flag_type; flag_image = new_flag_image; } else { + // TODO - use REB flag as default/error flag flag_country = nullptr; flag_type = String {}; flag_image.unref(); @@ -178,6 +175,24 @@ Error GFXMaskedFlagTexture::set_flag_country_name_and_type(String const& new_fla return set_flag_country_and_type(new_flag_country, new_flag_type); } +Error GFXMaskedFlagTexture::set_flag_country(Country const* new_flag_country) { + // TODO - get country's current flag type from the game state + return set_flag_country_and_type( new_flag_country, {}); +} + +Error GFXMaskedFlagTexture::set_flag_country_name(String const& new_flag_country_name) { + if (new_flag_country_name.is_empty()) { + return set_flag_country(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(new_flag_country); +} + 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 index be3b15a..f71a1d7 100644 --- a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp +++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp @@ -37,7 +37,7 @@ namespace OpenVic { /* 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 */ + /* 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. */ @@ -49,7 +49,14 @@ namespace OpenVic { 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 */ + /* Look up the specified country's current flag type, then call set_flag_country_and_type + * with the country and its flag type as arguments. */ + godot::Error set_flag_country(Country const* new_flag_country); + + /* Look up the country with the specified identifier, then call set_flag_country with the country its argument. */ + godot::Error set_flag_country_name(godot::String const& new_flag_country_name); + + /* 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 index 6fe2fe0..e1bffc5 100644 --- a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp +++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp @@ -1,11 +1,9 @@ #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" +#include "openvic-extension/utility/UITools.hpp" using namespace godot; using namespace OpenVic; @@ -14,14 +12,14 @@ 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> +static constexpr float 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; - } + ERR_FAIL_COND_V_MSG( + gfx_pie_chart->get_size() <= 0, FAILED, + vformat("Invalid GFX::PieChart size for GFXPieChartTexture - %d", gfx_pie_chart->get_size()) + ); const int32_t pie_chart_size = 2 * gfx_pie_chart->get_size(); bool can_update = true; if ( @@ -75,7 +73,7 @@ Error GFXPieChartTexture::_generate_pie_chart_image() { return OK; } -Error GFXPieChartTexture::set_slices(Array const& new_slices) { +Error GFXPieChartTexture::set_slices_array(TypedArray<Dictionary> const& new_slices) { static const StringName colour_key = "colour"; static const StringName weight_key = "weight"; @@ -83,15 +81,11 @@ Error GFXPieChartTexture::set_slices(Array const& new_slices) { 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; - } + ERR_CONTINUE_MSG( + !slice_dict.has(colour_key) || !slice_dict.has(weight_key), vformat("Invalid slice keys at index %d", i) + ); 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; - } + ERR_CONTINUE_MSG(slice.second <= 0.0f, vformat("Invalid slice values at index %d", i)); total_weight += slice.second; slices.emplace_back(std::move(slice)); } @@ -104,10 +98,10 @@ void GFXPieChartTexture::_bind_methods() { 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" }); + OV_BIND_METHOD(GFXPieChartTexture::set_slices_array, { "new_slices" }); } -GFXPieChartTexture::GFXPieChartTexture() : total_weight { 0.0f } {} +GFXPieChartTexture::GFXPieChartTexture() : gfx_pie_chart { nullptr }, total_weight { 0.0f } {} Ref<GFXPieChartTexture> GFXPieChartTexture::make_gfx_pie_chart_texture(GFX::PieChart const* gfx_pie_chart) { Ref<GFXPieChartTexture> pie_chart_texture; @@ -146,12 +140,8 @@ Error GFXPieChartTexture::set_gfx_pie_chart_name(String const& gfx_pie_chart_nam 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::Sprite const* sprite = UITools::get_gfx_sprite(gfx_pie_chart_name); + ERR_FAIL_NULL_V(sprite, FAILED); GFX::PieChart const* new_pie_chart = sprite->cast_to<GFX::PieChart>(); ERR_FAIL_NULL_V_MSG( new_pie_chart, FAILED, vformat( diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp index 315b00e..ad8e751 100644 --- a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp +++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp @@ -4,6 +4,8 @@ #include <openvic-simulation/interface/GFX.hpp> +#include "openvic-extension/utility/Utilities.hpp" + namespace OpenVic { class GFXPieChartTexture : public godot::ImageTexture { GDCLASS(GFXPieChartTexture, godot::ImageTexture) @@ -23,11 +25,40 @@ namespace OpenVic { public: GFXPieChartTexture(); - /* Set slices given new_slices, an Array of Dictionaries, each with the following keys: + /* Set slices given an Array of Dictionaries, each with the following key-value entries: * - colour: Color - * - weight: float - */ - godot::Error set_slices(godot::Array const& new_slices); + * - weight: float */ + godot::Error set_slices_array(godot::TypedArray<godot::Dictionary> const& new_slices); + + /* Generate slice data from a distribution of HasIdentifierAndColour derived objects, sorted by their weight. + * The resulting Array of Dictionaries can be used as an argument for set_slices_array. */ + template<std::derived_from<HasIdentifierAndColour> T> + static godot::TypedArray<godot::Dictionary> distribution_to_slices_array(fixed_point_map_t<T const*> const& dist) { + using entry_t = std::pair<T const*, fixed_point_t>; + std::vector<entry_t> sorted_dist; + sorted_dist.reserve(dist.size()); + for (entry_t const& entry : dist) { + ERR_CONTINUE_MSG( + entry.first == nullptr, godot::vformat("Null distribution key with value %f", entry.second.to_float()) + ); + sorted_dist.push_back(entry); + } + std::sort(sorted_dist.begin(), sorted_dist.end(), [](entry_t const& lhs, entry_t const& rhs) -> bool { + return lhs.second < rhs.second; + }); + static const godot::StringName identifier_key = "identifier"; + static const godot::StringName colour_key = "colour"; + static const godot::StringName weight_key = "weight"; + godot::TypedArray<godot::Dictionary> array; + for (auto const& [key, val] : sorted_dist) { + godot::Dictionary sub_dict; + sub_dict[identifier_key] = Utilities::std_view_to_godot_string(key->get_identifier()); + sub_dict[colour_key] = Utilities::to_godot_color(key->get_colour()); + sub_dict[weight_key] = val.to_float(); + array.push_back(sub_dict); + } + return array; + } /* Create a GFXPieChartTexture using the specific GFX::PieChart. * Returns nullptr if setting gfx_pie_chart fails. */ @@ -43,7 +74,7 @@ namespace OpenVic { /* 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 */ + /* 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 index 1d55c54..043f65d 100644 --- a/extension/src/openvic-extension/classes/GUINode.cpp +++ b/extension/src/openvic-extension/classes/GUINode.cpp @@ -3,10 +3,8 @@ #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/UITools.hpp" #include "openvic-extension/utility/Utilities.hpp" using namespace godot; @@ -15,33 +13,58 @@ using namespace OpenVic; using OpenVic::Utilities::godot_to_std_string; using OpenVic::Utilities::std_view_to_godot_string; +#define APPLY_TO_CHILD_TYPES(F) \ + F(Button, button) \ + F(CheckBox, check_box) \ + F(Label, label) \ + F(Panel, panel) \ + F(TextureProgressBar, progress_bar) \ + F(TextureRect, texture_rect) \ + F(GUIOverlappingElementsBox, gui_overlapping_elements_box) + +#define APPLY_TO_TEXTURE_TYPES(F) \ + F(GFXIconTexture, gfx_icon_texture) \ + F(GFXMaskedFlagTexture, gfx_masked_flag_texture) \ + F(GFXPieChartTexture, gfx_pie_chart_texture) + void GUINode::_bind_methods() { + OV_BIND_SMETHOD(generate_gui_element, { "gui_file", "gui_element", "name" }, DEFVAL(String {})); 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" }); +#define GET_BINDINGS(type, name) \ + OV_BIND_SMETHOD(get_##name##_from_node, { "node" }); \ + OV_BIND_METHOD(GUINode::get_##name##_from_nodepath, { "path" }); + + APPLY_TO_CHILD_TYPES(GET_BINDINGS) + + OV_BIND_SMETHOD(get_texture_from_node, { "node" }); + OV_BIND_METHOD(GUINode::get_texture_from_nodepath, { "path" }); + + APPLY_TO_TEXTURE_TYPES(GET_BINDINGS) - 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" }); +#undef GET_BINDINGS 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); +GUINode::GUINode() { + set_mouse_filter(MOUSE_FILTER_IGNORE); +} + +Control* GUINode::generate_gui_element(String const& gui_file, String const& gui_element, String const& name) { + Control* result = nullptr; + if (!UITools::generate_gui_element(gui_file, gui_element, name, result)) { + UtilityFunctions::push_error("Error generating GUI element ", gui_element, " from GUI file ", gui_file); + } + return result; +} + +Error GUINode::add_gui_element(String const& gui_file, String const& gui_element, String const& name) { 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())); + if (!UITools::generate_gui_element(gui_file, gui_element, name, result)) { + UtilityFunctions::push_error("Error generating GUI element ", gui_element, " from GUI file ", gui_file); err = FAILED; } if (result != nullptr) { @@ -50,107 +73,95 @@ Error GUINode::_add_gui_element(GUI::Element const* element, String const& name) 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)); +static T* _cast_node(Node* node) { + ERR_FAIL_NULL_V(node, nullptr); 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())); + ERR_FAIL_NULL_V_MSG( + result, nullptr, + vformat("Failed to cast node %s from type %s to %s", node->get_name(), node->get_class(), 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); -} +#define CHILD_GET_FUNCTIONS(type, name) \ + type* GUINode::get_##name##_from_node(Node* node) { \ + return _cast_node<type>(node); \ + } \ + type* GUINode::get_##name##_from_nodepath(NodePath const& path) const { \ + return _cast_node<type>(get_node_internal(path)); \ + } -TextureProgressBar* GUINode::get_progress_bar_node(NodePath const& path) const { - return _get_cast_node<TextureProgressBar>(path); -} +APPLY_TO_CHILD_TYPES(CHILD_GET_FUNCTIONS) -TextureRect* GUINode::get_texture_rect_node(NodePath const& path) const { - return _get_cast_node<TextureRect>(path); -} +#undef CHILD_GET_FUNCTIONS -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)); +Ref<Texture2D> GUINode::get_texture_from_node(Node* node) { + ERR_FAIL_NULL_V(node, nullptr); 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)); + ERR_FAIL_NULL_V_MSG(texture, nullptr, vformat("Failed to get Texture2D from TextureRect %s", node->get_name())); 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)); + ERR_FAIL_NULL_V_MSG( + stylebox, nullptr, vformat("Failed to get StyleBox %s from Button %s", theme_name_normal, node->get_name()) + ); 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 + "Failed to cast StyleBox %s from Button %s to type StyleBoxTexture", theme_name_normal, node->get_name() ) ); 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) + result, nullptr, + vformat("Failed to get Texture2D from StyleBoxTexture %s from Button %s", theme_name_normal, node->get_name()) ); return result; } - ERR_FAIL_V_MSG(nullptr, vformat("Failed to cast node %s to type TextureRect or Button", path)); + ERR_FAIL_V_MSG( + nullptr, vformat("Failed to cast node %s from type %s to TextureRect or Button", node->get_name(), node->get_class()) + ); +} + +Ref<Texture2D> GUINode::get_texture_from_nodepath(NodePath const& path) const { + return get_texture_from_node(get_node_internal(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); +template<std::derived_from<Texture2D> T> +static Ref<T> _cast_texture(Ref<Texture2D> const& texture) { 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())); + ERR_FAIL_NULL_V_MSG( + result, nullptr, vformat("Failed to cast Texture2D from type %s to %s", texture->get_class(), 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); -} +#define TEXTURE_GET_FUNCTIONS(type, name) \ + Ref<type> GUINode::get_##name##_from_node(Node* node) { \ + return _cast_texture<type>(get_texture_from_node(node)); \ + } \ + Ref<type> GUINode::get_##name##_from_nodepath(NodePath const& path) const { \ + return _cast_texture<type>(get_texture_from_nodepath(path)); \ + } -Ref<GFXMaskedFlagTexture> GUINode::get_gfx_masked_flag_texture_from_node(NodePath const& path) const { - return _get_cast_texture_from_node<GFXMaskedFlagTexture>(path); -} +APPLY_TO_TEXTURE_TYPES(TEXTURE_GET_FUNCTIONS) -Ref<GFXPieChartTexture> GUINode::get_gfx_pie_chart_texture_from_node(NodePath const& path) const { - return _get_cast_texture_from_node<GFXPieChartTexture>(path); -} +#undef TEXTURE_GET_FUNCTIONS + +#undef APPLY_TO_CHILD_TYPES Error GUINode::hide_node(NodePath const& path) const { - CanvasItem* node = _get_cast_node<CanvasItem>(path); - if (node == nullptr) { - return FAILED; - } + CanvasItem* node = _cast_node<CanvasItem>(get_node_internal(path)); + ERR_FAIL_NULL_V(node, FAILED); node->hide(); return OK; } -Error GUINode::hide_nodes(Array const& paths) const { +Error GUINode::hide_nodes(TypedArray<NodePath> const& paths) const { Error ret = OK; for (int32_t i = 0; i < paths.size(); ++i) { if (hide_node(paths[i]) != OK) { diff --git a/extension/src/openvic-extension/classes/GUINode.hpp b/extension/src/openvic-extension/classes/GUINode.hpp index 1671547..e38ed1f 100644 --- a/extension/src/openvic-extension/classes/GUINode.hpp +++ b/extension/src/openvic-extension/classes/GUINode.hpp @@ -12,42 +12,54 @@ #include "openvic-extension/classes/GFXIconTexture.hpp" #include "openvic-extension/classes/GFXMaskedFlagTexture.hpp" #include "openvic-extension/classes/GFXPieChartTexture.hpp" +#include "openvic-extension/classes/GUIOverlappingElementsBox.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; + GUINode(); + + static godot::Control* generate_gui_element( + godot::String const& gui_file, godot::String const& gui_element, godot::String const& name = "" + ); - 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; + static godot::Button* get_button_from_node(godot::Node* node); + static godot::CheckBox* get_check_box_from_node(godot::Node* node); + static godot::Label* get_label_from_node(godot::Node* node); + static godot::Panel* get_panel_from_node(godot::Node* node); + static godot::TextureProgressBar* get_progress_bar_from_node(godot::Node* node); + static godot::TextureRect* get_texture_rect_from_node(godot::Node* node); + static GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_node(godot::Node* node); + + godot::Button* get_button_from_nodepath(godot::NodePath const& path) const; + godot::CheckBox* get_check_box_from_nodepath(godot::NodePath const& path) const; + godot::Label* get_label_from_nodepath(godot::NodePath const& path) const; + godot::Panel* get_panel_from_nodepath(godot::NodePath const& path) const; + godot::TextureProgressBar* get_progress_bar_from_nodepath(godot::NodePath const& path) const; + godot::TextureRect* get_texture_rect_from_nodepath(godot::NodePath const& path) const; + GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_nodepath(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; + static godot::Ref<godot::Texture2D> get_texture_from_node(godot::Node* node); + static godot::Ref<GFXIconTexture> get_gfx_icon_texture_from_node(godot::Node* node); + static godot::Ref<GFXMaskedFlagTexture> get_gfx_masked_flag_texture_from_node(godot::Node* node); + static godot::Ref<GFXPieChartTexture> get_gfx_pie_chart_texture_from_node(godot::Node* node); + + godot::Ref<godot::Texture2D> get_texture_from_nodepath(godot::NodePath const& path) const; + godot::Ref<GFXIconTexture> get_gfx_icon_texture_from_nodepath(godot::NodePath const& path) const; + godot::Ref<GFXMaskedFlagTexture> get_gfx_masked_flag_texture_from_nodepath(godot::NodePath const& path) const; + godot::Ref<GFXPieChartTexture> get_gfx_pie_chart_texture_from_nodepath(godot::NodePath const& path) const; godot::Error hide_node(godot::NodePath const& path) const; - godot::Error hide_nodes(godot::Array const& paths) const; + godot::Error hide_nodes(godot::TypedArray<godot::NodePath> const& paths) const; }; } diff --git a/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp new file mode 100644 index 0000000..ff88781 --- /dev/null +++ b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp @@ -0,0 +1,177 @@ +#include "GUIOverlappingElementsBox.hpp" + +#include <godot_cpp/variant/utility_functions.hpp> + +#include "openvic-extension/utility/ClassBindings.hpp" +#include "openvic-extension/utility/UITools.hpp" +#include "openvic-extension/utility/Utilities.hpp" + +using namespace OpenVic; +using namespace godot; + +using OpenVic::Utilities::std_view_to_godot_string; + +Error GUIOverlappingElementsBox::_update_child_positions() { + ERR_FAIL_NULL_V(gui_overlapping_elements_box, FAILED); + const int32_t child_count = get_child_count(); + if (child_count <= 0) { + return OK; + } + + const auto _get_child = [this](int32_t index) -> Control* { return Object::cast_to<Control>(get_child(index)); }; + + const float box_width = get_size().x; + const float child_width = _get_child(0)->get_size().x; + + const float max_spacing = box_width / (child_count + 1); + const float default_spacing = child_width + gui_overlapping_elements_box->get_spacing().to_float(); + + const float spacing = std::min(max_spacing, default_spacing); + + const float total_width = spacing * (child_count - 1) + child_width; + + float starting_x = 0.0f; + Error err = OK; + using enum GUI::AlignedElement::format_t; + switch (gui_overlapping_elements_box->get_format()) { + case left: + break; + case centre: + starting_x = (box_width - total_width) / 2; + break; + case right: + starting_x = box_width - total_width; + break; + default: + UtilityFunctions::push_error( + "Invalid GUIOverlappingElementsBox alignment: ", + static_cast<int32_t>(gui_overlapping_elements_box->get_format()) + ); + err = FAILED; + } + + for (int32_t index = 0; index < child_count; ++index) { + _get_child(index)->set_position({ starting_x + spacing * index, 0.0f }); + } + + return err; +} + +void GUIOverlappingElementsBox::_bind_methods() { + OV_BIND_METHOD(GUIOverlappingElementsBox::clear); + OV_BIND_METHOD(GUIOverlappingElementsBox::clear_children); + OV_BIND_METHOD(GUIOverlappingElementsBox::set_child_count, { "new_count" }); + + OV_BIND_METHOD(GUIOverlappingElementsBox::get_gui_overlapping_elements_box_name); + + OV_BIND_METHOD( + GUIOverlappingElementsBox::set_gui_child_element_name, { "gui_child_element_file", "gui_child_element_name" } + ); + OV_BIND_METHOD(GUIOverlappingElementsBox::get_gui_child_element_name); +} + +void GUIOverlappingElementsBox::_notification(int what) { + if (what == NOTIFICATION_SORT_CHILDREN) { + _update_child_positions(); + } +} + +GUIOverlappingElementsBox::GUIOverlappingElementsBox() + : gui_overlapping_elements_box { nullptr }, gui_child_element { nullptr } {} + +void GUIOverlappingElementsBox::clear() { + clear_children(); + gui_child_element = nullptr; + gui_overlapping_elements_box = nullptr; +} + +void GUIOverlappingElementsBox::clear_children() { + set_child_count(0); +} + +Error GUIOverlappingElementsBox::set_child_count(int32_t new_count) { + ERR_FAIL_COND_V_MSG(new_count < 0, FAILED, "Child count must be non-negative"); + int32_t child_count = get_child_count(); + if (child_count == new_count) { + return OK; + } else if (child_count > new_count) { + do { + remove_child(get_child(--child_count)); + } while (child_count > new_count); + return OK; + } else { + ERR_FAIL_NULL_V_MSG( + gui_child_element, FAILED, vformat( + "GUIOverlappingElementsBox child element is null (child_count = %d, new_count = %d)", child_count, new_count + ) + ); + Error err = OK; + const String gui_child_element_name = std_view_to_godot_string(gui_child_element->get_name()) + "_"; + do { + Control* child = nullptr; + const String name = gui_child_element_name + itos(child_count); + if (!UITools::generate_gui_element(gui_child_element, name, child)) { + UtilityFunctions::push_error("Error generating GUIOverlappingElementsBox child element ", name); + err = FAILED; + } + ERR_FAIL_NULL_V_MSG( + child, FAILED, vformat( + "Failed to generate GUIOverlappingElementsBox child element %s (child_count = %d, new_count = %d)", + name, child_count, new_count + ) + ); + add_child(child); + child_count++; + } while (child_count < new_count); + return err; + } +} + +Error GUIOverlappingElementsBox::set_gui_overlapping_elements_box( + GUI::OverlappingElementsBox const* new_gui_overlapping_elements_box +) { + if (gui_overlapping_elements_box == new_gui_overlapping_elements_box) { + return OK; + } + gui_overlapping_elements_box = new_gui_overlapping_elements_box; + if (gui_overlapping_elements_box == nullptr) { + return OK; + } + + set_custom_minimum_size(Utilities::to_godot_fvec2(gui_overlapping_elements_box->get_size())); + queue_sort(); + return OK; +} + +String GUIOverlappingElementsBox::get_gui_overlapping_elements_box_name() const { + return gui_overlapping_elements_box != nullptr ? std_view_to_godot_string(gui_overlapping_elements_box->get_name()) : String {}; +} + +Error GUIOverlappingElementsBox::set_gui_child_element(GUI::Element const* new_gui_child_element) { + clear_children(); + gui_child_element = new_gui_child_element; + return OK; +} + +Error GUIOverlappingElementsBox::set_gui_child_element_name( + String const& gui_child_element_file, String const& gui_child_element_name +) { + if (gui_child_element_file.is_empty() && gui_child_element_name.is_empty()) { + return set_gui_child_element(nullptr); + } + ERR_FAIL_COND_V_MSG( + gui_child_element_file.is_empty(), FAILED, + vformat("GUI child element file name is empty but element name is not: %s", gui_child_element_name) + ); + ERR_FAIL_COND_V_MSG( + gui_child_element_name.is_empty(), FAILED, + vformat("GUI child element name is empty but file name is not: %s", gui_child_element_file) + ); + GUI::Element const* const new_gui_child_element = UITools::get_gui_element(gui_child_element_file, gui_child_element_name); + ERR_FAIL_NULL_V(new_gui_child_element, FAILED); + return set_gui_child_element(new_gui_child_element); +} + +String GUIOverlappingElementsBox::get_gui_child_element_name() const { + return gui_child_element != nullptr ? std_view_to_godot_string(gui_child_element->get_name()) : String {}; +} diff --git a/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.hpp b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.hpp new file mode 100644 index 0000000..5a420f2 --- /dev/null +++ b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include <godot_cpp/classes/container.hpp> + +#include <openvic-simulation/interface/GUI.hpp> + +namespace OpenVic { + class GUIOverlappingElementsBox : public godot::Container { + GDCLASS(GUIOverlappingElementsBox, godot::Container) + + GUI::OverlappingElementsBox const* PROPERTY(gui_overlapping_elements_box); + GUI::Element const* PROPERTY(gui_child_element); + + godot::Error _update_child_positions(); + + protected: + static void _bind_methods(); + + void _notification(int what); + + public: + GUIOverlappingElementsBox(); + + /* Reset gui_overlapping_elements_box and gui_child_element to nullptr, and remove all child elements. */ + void clear(); + + /* Remove all child elements. */ + void clear_children(); + + /* Add or remove child elements to reach the specified count. */ + godot::Error set_child_count(int32_t new_count); + + /* Set the GUI::OverlappingElementsBox. This does not affect any existing child elements. */ + godot::Error set_gui_overlapping_elements_box(GUI::OverlappingElementsBox const* new_gui_overlapping_elements_box); + + /* Return the name of the GUI::OverlappingElementsBox, or an empty String if it's null. */ + godot::String get_gui_overlapping_elements_box_name() const; + + /* Set the child GUI::Element, removing all previous child elements (even if the child GUI::Element doesn't change). */ + godot::Error set_gui_child_element(GUI::Element const* new_gui_child_element); + + /* Search for a GUI::Element with the specfied name and, if successful, + * set the child element to it using set_gui_child_element. */ + godot::Error set_gui_child_element_name( + godot::String const& gui_child_element_file, godot::String const& gui_child_element_name + ); + + /* Return the name of the child GUI::Element, or an empty String if it's null. */ + godot::String get_gui_child_element_name() const; + }; +} diff --git a/extension/src/openvic-extension/register_types.cpp b/extension/src/openvic-extension/register_types.cpp index 65bdc60..278365e 100644 --- a/extension/src/openvic-extension/register_types.cpp +++ b/extension/src/openvic-extension/register_types.cpp @@ -6,6 +6,7 @@ #include "openvic-extension/classes/GFXMaskedFlagTexture.hpp" #include "openvic-extension/classes/GFXPieChartTexture.hpp" #include "openvic-extension/classes/GUINode.hpp" +#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp" #include "openvic-extension/classes/MapMesh.hpp" #include "openvic-extension/singletons/AssetManager.hpp" #include "openvic-extension/singletons/Checksum.hpp" @@ -46,6 +47,7 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) { ClassDB::register_class<GFXMaskedFlagTexture>(); ClassDB::register_class<GFXPieChartTexture>(); ClassDB::register_class<GUINode>(); + ClassDB::register_class<GUIOverlappingElementsBox>(); } void uninitialize_openvic_types(ModuleInitializationLevel p_level) { diff --git a/extension/src/openvic-extension/singletons/AssetManager.cpp b/extension/src/openvic-extension/singletons/AssetManager.cpp index 8e9eb41..581c7fa 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.cpp +++ b/extension/src/openvic-extension/singletons/AssetManager.cpp @@ -15,11 +15,6 @@ using OpenVic::Utilities::std_to_godot_string; void AssetManager::_bind_methods() { OV_BIND_METHOD(AssetManager::get_image, { "path" }); OV_BIND_METHOD(AssetManager::get_texture, { "path" }); - - OV_BIND_SMETHOD(AssetManager::make_icon, { "texture", "frame", "frame_count" }); - OV_BIND_METHOD(AssetManager::get_icon, { "texture", "frame", "frame_count" }); - - OV_BIND_METHOD(AssetManager::get_texture_or_icon, { "path", "frame", "frame_count" }); OV_BIND_METHOD(AssetManager::get_font, { "name" }); } @@ -96,46 +91,6 @@ Ref<ImageTexture> AssetManager::get_texture(StringName path) { } } -Ref<AtlasTexture> AssetManager::make_icon(Ref<Texture2D> texture, GFX::frame_t frame, GFX::frame_t frame_count) { - ERR_FAIL_NULL_V(texture, nullptr); - - if (frame_count <= GFX::NO_FRAMES) { - UtilityFunctions::push_warning("No frames!"); - frame_count = 1; - } - if (frame <= GFX::NO_FRAMES || frame > frame_count) { - UtilityFunctions::push_warning("Invalid frame index ", frame, " out of count ", frame_count); - frame = frame_count; - } - frame--; - const Vector2i size = texture->get_size(); - const Rect2i region { frame * size.x / frame_count, 0, size.x / frame_count, size.y }; - - Ref<AtlasTexture> atlas; - atlas.instantiate(); - ERR_FAIL_NULL_V(atlas, nullptr); - atlas->set_atlas(texture); - atlas->set_region(region); - return atlas; -} - -Ref<AtlasTexture> AssetManager::get_icon(StringName path, GFX::frame_t frame, GFX::frame_t frame_count) { - Ref<ImageTexture> texture = get_texture(path); - ERR_FAIL_NULL_V(texture, nullptr); - return make_icon(texture, frame, frame_count); -} - -Ref<Texture2D> AssetManager::get_texture_or_icon(StringName path, GFX::frame_t frame, GFX::frame_t frame_count) { - if (frame_count < 2) { - if (frame > frame_count) { - UtilityFunctions::push_warning("Invalid frame index ", frame, " out of count ", frame_count); - } - return get_texture(path); - } else { - return get_icon(path, frame, frame_count); - } -} - Ref<Font> AssetManager::get_font(StringName name) { const font_map_t::const_iterator it = fonts.find(name); if (it != fonts.end()) { diff --git a/extension/src/openvic-extension/singletons/AssetManager.hpp b/extension/src/openvic-extension/singletons/AssetManager.hpp index 625944d..e6a7664 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.hpp +++ b/extension/src/openvic-extension/singletons/AssetManager.hpp @@ -44,17 +44,6 @@ namespace OpenVic { * or texture creation fails. */ godot::Ref<godot::ImageTexture> get_texture(godot::StringName path); - /* Extract the specified frame of the texture, which is treated as a single row of frame_count frames. */ - static godot::Ref<godot::AtlasTexture> make_icon( - godot::Ref<godot::Texture2D> texture, GFX::frame_t frame, GFX::frame_t frame_count - ); - - /* Load a texture as with get_texture, and extract the specified frame as with make_icon. */ - godot::Ref<godot::AtlasTexture> get_icon(godot::StringName path, GFX::frame_t frame, GFX::frame_t frame_count); - - /* Load a texture as with get_texture if frame_count <= 1 otherwise as with get_icon. */ - godot::Ref<godot::Texture2D> get_texture_or_icon(godot::StringName path, GFX::frame_t frame, GFX::frame_t frame_count); - /* Search for and load a font with the specified name from the game defines' font directory, first checking the * AssetManager's font cache in case it has already been loaded, and returning nullptr if font loading fails. */ godot::Ref<godot::Font> get_font(godot::StringName name); diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp index a53ba54..0e64313 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.cpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp @@ -8,6 +8,8 @@ #include <openvic-simulation/utility/Logger.hpp> +#include "openvic-extension/classes/GFXPieChartTexture.hpp" +#include "openvic-extension/singletons/AssetManager.hpp" #include "openvic-extension/singletons/LoadLocalisation.hpp" #include "openvic-extension/utility/ClassBindings.hpp" #include "openvic-extension/utility/Utilities.hpp" @@ -48,7 +50,9 @@ void GameSingleton::_bind_methods() { OV_BIND_METHOD(GameSingleton::get_selected_province_index); OV_BIND_METHOD(GameSingleton::set_selected_province, { "index" }); - OV_BIND_METHOD(GameSingleton::expand_building, { "province_index", "building_type_identifier" }); + OV_BIND_METHOD(GameSingleton::get_province_building_count); + OV_BIND_METHOD(GameSingleton::get_province_building_identifier, { "building_index" }); + OV_BIND_METHOD(GameSingleton::expand_selected_province_building, { "building_index" }); 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); @@ -135,51 +139,23 @@ int32_t GameSingleton::get_province_index_from_uv_coords(Vector2 const& coords) return game_manager.get_map().get_province_index_at(x_mod_w, y_mod_h); } -template<std::derived_from<HasIdentifierAndColour> T> -static Array _distribution_to_pie_chart_array(fixed_point_map_t<T const*> const& dist) { - using entry_t = std::pair<T const*, fixed_point_t>; - std::vector<entry_t> sorted_dist; - sorted_dist.reserve(dist.size()); - for (entry_t const& entry : dist) { - if (entry.first != nullptr) { - sorted_dist.push_back(entry); - } else { - UtilityFunctions::push_error("Null distribution key with value ", entry.second.to_float()); - } - } - std::sort(sorted_dist.begin(), sorted_dist.end(), [](entry_t const& lhs, entry_t const& rhs) -> bool { - return lhs.second < rhs.second; - }); - static const StringName identifier_key = "identifier"; - static const StringName colour_key = "colour"; - static const StringName weight_key = "weight"; - Array array; - for (auto const& [key, val] : sorted_dist) { - Dictionary sub_dict; - sub_dict[identifier_key] = std_view_to_godot_string(key->get_identifier()); - sub_dict[colour_key] = Utilities::to_godot_color(key->get_colour()); - sub_dict[weight_key] = val.to_float(); - array.push_back(sub_dict); - } - return array; -} - 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_slave_status_key = "slave_status"; + static const StringName province_info_colony_status_key = "colony_status"; static const StringName province_info_terrain_type_key = "terrain_type"; + static const StringName province_info_life_rating_key = "life_rating"; + static const StringName province_info_controller_key = "controller"; + 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_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_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_cores_key = "cores"; static const StringName province_info_buildings_key = "buildings"; Province const* province = game_manager.get_map().get_province_by_index(index); @@ -195,6 +171,17 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const { ret[province_info_region_key] = std_view_to_godot_string(region->get_identifier()); } + ret[province_info_slave_status_key] = province->get_slave(); + + ret[province_info_colony_status_key] = static_cast<int32_t>(province->get_colony_status()); + + TerrainType const* terrain_type = province->get_terrain_type(); + if (terrain_type != nullptr) { + ret[province_info_terrain_type_key] = std_view_to_godot_string(terrain_type->get_identifier()); + } + + ret[province_info_life_rating_key] = province->get_life_rating(); + Country const* controller = province->get_controller(); if (controller != nullptr) { ret[province_info_controller_key] = std_view_to_godot_string(controller->get_identifier()); @@ -206,37 +193,39 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const { ret[province_info_rgo_icon_key] = static_cast<int32_t>(rgo->get_index()); } - ret[province_info_life_rating_key] = province->get_life_rating(); - - TerrainType const* terrain_type = province->get_terrain_type(); - if (terrain_type != nullptr) { - 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()) { - ret[province_info_pop_types_key] = _distribution_to_pie_chart_array(pop_types); + ret[province_info_pop_types_key] = GFXPieChartTexture::distribution_to_slices_array(pop_types); } + fixed_point_map_t<Ideology const*> const& ideologies = province->get_ideology_distribution(); if (!ideologies.empty()) { - ret[province_info_pop_ideologies_key] = _distribution_to_pie_chart_array(ideologies); + ret[province_info_pop_ideologies_key] = GFXPieChartTexture::distribution_to_slices_array(ideologies); } + fixed_point_map_t<Culture const*> const& cultures = province->get_culture_distribution(); if (!cultures.empty()) { - ret[province_info_pop_cultures_key] = _distribution_to_pie_chart_array(cultures); + ret[province_info_pop_cultures_key] = GFXPieChartTexture::distribution_to_slices_array(cultures); + } + + std::vector<Country const*> const& cores = province->get_cores(); + if (!cores.empty()) { + PackedStringArray cores_array; + cores_array.resize(cores.size()); + for (size_t idx = 0; idx < cores.size(); ++idx) { + cores_array[idx] = std_view_to_godot_string(cores[idx]->get_identifier()); + } + ret[province_info_cores_key] = cores_array; } - static const StringName building_info_building_key = "building"; static const StringName building_info_level_key = "level"; static const StringName building_info_expansion_state_key = "expansion_state"; static const StringName building_info_start_date_key = "start_date"; @@ -245,13 +234,14 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const { std::vector<BuildingInstance> const& buildings = province->get_buildings(); if (!buildings.empty()) { - Array buildings_array; + /* This system relies on the province buildings all being present in the right order. It will have to + * be changed if we want to support variable combinations and permutations of province buildings. */ + TypedArray<Dictionary> buildings_array; buildings_array.resize(buildings.size()); for (size_t idx = 0; idx < buildings.size(); ++idx) { BuildingInstance const& building = buildings[idx]; Dictionary building_dict; - building_dict[building_info_building_key] = std_view_to_godot_string(building.get_identifier()); building_dict[building_info_level_key] = static_cast<int32_t>(building.get_level()); building_dict[building_info_expansion_state_key] = static_cast<int32_t>(building.get_expansion_state()); building_dict[building_info_start_date_key] = std_to_godot_string(building.get_start_date().to_string()); @@ -321,14 +311,14 @@ Error GameSingleton::_update_colour_image() { return FAILED; } /* We reshape the list of colours into a square, as each texture dimensions cannot exceed 16384. */ - static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(Province::index_t) * 4); - static constexpr int32_t colour_image_width = PROVINCE_INDEX_SQRT * sizeof(Mapmode::base_stripe_t) / sizeof(colour_t); + static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(Province::index_t) * CHAR_BIT / 2); + static constexpr int32_t colour_image_width = PROVINCE_INDEX_SQRT * sizeof(Mapmode::base_stripe_t) / sizeof(colour_argb_t); /* Province count + null province, rounded up to next multiple of PROVINCE_INDEX_SQRT. * Rearranged from: (map.get_province_count() + 1) + (PROVINCE_INDEX_SQRT - 1) */ static const int32_t colour_image_height = (map.get_province_count() + PROVINCE_INDEX_SQRT) / PROVINCE_INDEX_SQRT; static PackedByteArray colour_data_array; - static const int64_t colour_data_array_size = colour_image_width * colour_image_height * sizeof(colour_t); + static const int64_t colour_data_array_size = colour_image_width * colour_image_height * sizeof(colour_argb_t); colour_data_array.resize(colour_data_array_size); Error err = OK; @@ -386,12 +376,25 @@ void GameSingleton::set_selected_province(int32_t index) { emit_signal("province_selected", index); } -Error GameSingleton::expand_building(int32_t province_index, String const& building_type_identifier) { - if (!game_manager.expand_building(province_index, godot_to_std_string(building_type_identifier))) { - UtilityFunctions::push_error("Failed to expand ", building_type_identifier, " at province index ", province_index); - return FAILED; +int32_t GameSingleton::get_province_building_count() const { + return game_manager.get_economy_manager().get_building_type_manager().get_province_building_types().size(); +} + +String GameSingleton::get_province_building_identifier(int32_t index) const { + std::vector<BuildingType const*> const& province_building_types = + game_manager.get_economy_manager().get_building_type_manager().get_province_building_types(); + ERR_FAIL_COND_V_MSG( + index < 0 || index >= province_building_types.size(), {}, vformat("Invalid province building index: %d", index) + ); + return std_view_to_godot_string(province_building_types[index]->get_identifier()); +} + +Error GameSingleton::expand_selected_province_building(int32_t building_index) { + const bool ret = game_manager.expand_selected_province_building(building_index); + if (!ret) { + UtilityFunctions::push_error("Failed to expand the currently selected province's building index ", building_index); } - return OK; + return ERR(ret); } int32_t GameSingleton::get_slave_pop_icon_index() const { @@ -493,7 +496,7 @@ Error GameSingleton::_load_map_images(bool flip_vertical) { Map::shape_pixel_t const* province_shape_data = game_manager.get_map().get_province_shape_image().data(); const Vector2i divided_dims = province_dims / image_subdivisions; - Array province_shape_images; + TypedArray<Image> province_shape_images; province_shape_images.resize(image_subdivisions.x * image_subdivisions.y); for (int32_t v = 0; v < image_subdivisions.y; ++v) { for (int32_t u = 0; u < image_subdivisions.x; ++u) { @@ -561,12 +564,14 @@ Error GameSingleton::_load_terrain_variants() { } const int32_t slice_size = sheet_width / SHEET_DIMS; - Array terrain_images; + TypedArray<Image> terrain_images; { - static constexpr colour_t TERRAIN_WATER_INDEX_COLOUR = 0xFFFFFF; - Ref<Image> water_image = Image::create(slice_size, slice_size, false, terrain_sheet->get_format()); + // TODO - load "map/terrain/water.dds" instead of using a solid colour (issue is that it's 512x512 + // while the texturesheet slices are 256x256, so they can't share a Texture2DArray) + const Ref<Image> water_image = Utilities::make_solid_colour_image( + { 0.1f, 0.1f, 0.5f }, slice_size, slice_size, terrain_sheet->get_format() + ); ERR_FAIL_NULL_V_EDMSG(water_image, FAILED, "Failed to create water terrain image"); - water_image->fill({ 0.1f, 0.1f, 0.5f }); terrain_images.append(water_image); } Error err = OK; diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp index 5559f1b..1f3905e 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.hpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp @@ -7,8 +7,6 @@ #include <openvic-simulation/GameManager.hpp> #include <openvic-simulation/dataloader/Dataloader.hpp> -#include "openvic-extension/UIAdapter.hpp" - namespace OpenVic { class GameSingleton : public godot::Object { @@ -95,7 +93,9 @@ namespace OpenVic { int32_t get_selected_province_index() const; void set_selected_province(int32_t index); - godot::Error expand_building(int32_t province_index, godot::String const& building_type_identifier); + int32_t get_province_building_count() const; + godot::String get_province_building_identifier(int32_t building_index) const; + godot::Error expand_selected_province_building(int32_t building_index); 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; diff --git a/extension/src/openvic-extension/UIAdapter.cpp b/extension/src/openvic-extension/utility/UITools.cpp index cbe898c..cba65a4 100644 --- a/extension/src/openvic-extension/UIAdapter.cpp +++ b/extension/src/openvic-extension/utility/UITools.cpp @@ -1,4 +1,4 @@ -#include "UIAdapter.hpp" +#include "UITools.hpp" #include <godot_cpp/classes/button.hpp> #include <godot_cpp/classes/check_box.hpp> @@ -14,38 +14,53 @@ #include "openvic-extension/classes/GFXIconTexture.hpp" #include "openvic-extension/classes/GFXMaskedFlagTexture.hpp" #include "openvic-extension/classes/GFXPieChartTexture.hpp" +#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp" +#include "openvic-extension/singletons/AssetManager.hpp" +#include "openvic-extension/singletons/GameSingleton.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; -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&, 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 }, - { GUI::Text::get_type_static(), &generate_text }, - { GUI::OverlappingElementsBox::get_type_static(), &generate_overlapping_elements }, - { GUI::ListBox::get_type_static(), &generate_listbox }, - { GUI::Window::get_type_static(), &generate_window } +GFX::Sprite const* UITools::get_gfx_sprite(godot::String const& gfx_sprite) { + GameSingleton* game_singleton = GameSingleton::get_singleton(); + ERR_FAIL_NULL_V(game_singleton, nullptr); + GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier( + godot_to_std_string(gfx_sprite) + ); + ERR_FAIL_NULL_V_MSG(sprite, nullptr, vformat("GFX sprite not found: %s", gfx_sprite)); + return sprite; +} + +GUI::Element const* UITools::get_gui_element(godot::String const& gui_file, godot::String const& gui_element) { + GameSingleton const* game_singleton = GameSingleton::get_singleton(); + ERR_FAIL_NULL_V(game_singleton, nullptr); + 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, nullptr, 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, nullptr, vformat("Failed to find GUI element %s in GUI file %s", gui_element, gui_file)); + return element; +} + +/* GUI::Element tree -> godot::Control tree conversion code below: */ + +namespace OpenVic { + struct generate_gui_args_t { + GUI::Element const& element; + godot::String const& name; + AssetManager& asset_manager; + godot::Control*& result; + + constexpr generate_gui_args_t( + GUI::Element const& new_element, godot::String const& new_name, AssetManager& new_asset_manager, + godot::Control*& new_result + ) : element { new_element }, name { new_name }, asset_manager { new_asset_manager }, result { new_result } {} }; - const decltype(type_map)::const_iterator it = type_map.find(element->get_type()); - if (it != type_map.end()) { - 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; - return false; - } } template<std::derived_from<Control> T> @@ -75,24 +90,23 @@ static T* new_control(GUI::Element const& element, String const& name) { std_view_to_godot_string(element.get_name())); } node->set_position(Utilities::to_godot_fvec2(element.get_position())); + node->set_h_size_flags(Control::SizeFlags::SIZE_SHRINK_BEGIN); + node->set_v_size_flags(Control::SizeFlags::SIZE_SHRINK_BEGIN); node->set_focus_mode(Control::FOCUS_NONE); return node; } -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); +static bool generate_icon(generate_gui_args_t&& args) { + GUI::Icon const& icon = static_cast<GUI::Icon const&>(args.element); - result = nullptr; const String icon_name = std_view_to_godot_string(icon.get_name()); /* Change to use sprite type to choose Godot node type! */ bool ret = true; if (icon.get_sprite() != nullptr) { if (icon.get_sprite()->is_type<GFX::TextureSprite>()) { - TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name); + TextureRect* godot_texture_rect = new_control<TextureRect>(icon, args.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>(); @@ -104,9 +118,9 @@ bool GodotGUIBuilder::generate_icon( ret = false; } - result = godot_texture_rect; + args.result = godot_texture_rect; } else if (icon.get_sprite()->is_type<GFX::MaskedFlag>()) { - TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name); + TextureRect* godot_texture_rect = new_control<TextureRect>(icon, args.name); ERR_FAIL_NULL_V_MSG(godot_texture_rect, false, vformat("Failed to create TextureRect for GUI icon %s", icon_name)); GFX::MaskedFlag const* masked_flag = icon.get_sprite()->cast_to<GFX::MaskedFlag>(); @@ -118,16 +132,16 @@ bool GodotGUIBuilder::generate_icon( ret = false; } - result = godot_texture_rect; + args.result = godot_texture_rect; } else if (icon.get_sprite()->is_type<GFX::ProgressBar>()) { - TextureProgressBar* godot_progress_bar = new_control<TextureProgressBar>(icon, name); + TextureProgressBar* godot_progress_bar = new_control<TextureProgressBar>(icon, args.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()); - const Ref<ImageTexture> back_texture = asset_manager.get_texture(back_texture_file); + const Ref<ImageTexture> back_texture = args.asset_manager.get_texture(back_texture_file); if (back_texture.is_valid()) { godot_progress_bar->set_under_texture(back_texture); } else { @@ -137,7 +151,7 @@ bool GodotGUIBuilder::generate_icon( const StringName progress_texture_file = std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::ProgressBar>()->get_progress_texture_file()); - const Ref<ImageTexture> progress_texture = asset_manager.get_texture(progress_texture_file); + const Ref<ImageTexture> progress_texture = args.asset_manager.get_texture(progress_texture_file); if (progress_texture.is_valid()) { godot_progress_bar->set_progress_texture(progress_texture); } else { @@ -147,9 +161,9 @@ bool GodotGUIBuilder::generate_icon( ret = false; } - result = godot_progress_bar; + args.result = godot_progress_bar; } else if (icon.get_sprite()->is_type<GFX::PieChart>()) { - TextureRect* godot_texture_rect = new_control<TextureRect>(icon, name); + TextureRect* godot_texture_rect = new_control<TextureRect>(icon, args.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>(); @@ -165,7 +179,7 @@ bool GodotGUIBuilder::generate_icon( ret = false; } - result = godot_texture_rect; + args.result = godot_texture_rect; } else if (icon.get_sprite()->is_type<GFX::LineChart>()) { } else { @@ -180,16 +194,13 @@ bool GodotGUIBuilder::generate_icon( return ret; } -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); +static bool generate_button(generate_gui_args_t&& args) { + GUI::Button const& button = static_cast<GUI::Button const&>(args.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, name); + Button* godot_button = new_control<Button>(button, args.name); ERR_FAIL_NULL_V_MSG(godot_button, false, vformat("Failed to create Button for GUI button %s", button_name)); if (!button.get_text().empty()) { @@ -220,7 +231,7 @@ bool GodotGUIBuilder::generate_button( } if (texture.is_valid()) { - godot_button->set_size(texture->get_size()); + godot_button->set_custom_minimum_size(texture->get_size()); Ref<StyleBoxTexture> stylebox; stylebox.instantiate(); if (stylebox.is_valid()) { @@ -239,7 +250,7 @@ bool GodotGUIBuilder::generate_button( if (button.get_font() != nullptr) { const StringName font_file = std_view_to_godot_string_name(button.get_font()->get_fontname()); - const Ref<Font> font = asset_manager.get_font(font_file); + const Ref<Font> font = args.asset_manager.get_font(font_file); if (font.is_valid()) { godot_button->add_theme_font_override("font", font); } else { @@ -250,20 +261,17 @@ bool GodotGUIBuilder::generate_button( godot_button->add_theme_color_override("font_color", colour); } - result = godot_button; + args.result = godot_button; return ret; } -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); +static bool generate_checkbox(generate_gui_args_t&& args) { + GUI::Checkbox const& checkbox = static_cast<GUI::Checkbox const&>(args.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, name); + CheckBox* godot_checkbox = new_control<CheckBox>(checkbox, args.name); ERR_FAIL_NULL_V_MSG(godot_checkbox, false, vformat("Failed to create CheckBox for GUI checkbox %s", checkbox_name)); bool ret = true; @@ -272,7 +280,7 @@ bool GodotGUIBuilder::generate_checkbox( if (texture_sprite != nullptr) { Ref<GFXIconTexture> icon_texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite, 1); if (icon_texture.is_valid()) { - godot_checkbox->set_size(icon_texture->get_size()); + godot_checkbox->set_custom_minimum_size(icon_texture->get_size()); godot_checkbox->add_theme_icon_override("unchecked", icon_texture); } else { UtilityFunctions::push_error("Failed to make unchecked GFXIconTexture for GUI checkbox ", checkbox_name); @@ -297,26 +305,22 @@ bool GodotGUIBuilder::generate_checkbox( ret = false; } - result = godot_checkbox; + args.result = godot_checkbox; return ret; } -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); +static bool generate_text(generate_gui_args_t&& args) { + GUI::Text const& text = static_cast<GUI::Text const&>(args.element); - result = nullptr; const String text_name = std_view_to_godot_string(text.get_name()); - Label* godot_label = new_control<Label>(text, name); + Label* godot_label = new_control<Label>(text, args.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())); + godot_label->set_custom_minimum_size(Utilities::to_godot_fvec2(text.get_max_size())); using enum GUI::AlignedElement::format_t; - using enum HorizontalAlignment; static const std::map<GUI::AlignedElement::format_t, HorizontalAlignment> format_map { { left, HORIZONTAL_ALIGNMENT_LEFT }, { centre, HORIZONTAL_ALIGNMENT_CENTER }, @@ -333,7 +337,7 @@ bool GodotGUIBuilder::generate_text( bool ret = true; if (text.get_font() != nullptr) { const StringName font_file = std_view_to_godot_string_name(text.get_font()->get_fontname()); - const Ref<Font> font = asset_manager.get_font(font_file); + const Ref<Font> font = args.asset_manager.get_font(font_file); if (font.is_valid()) { godot_label->add_theme_font_override("font", font); } else { @@ -344,67 +348,59 @@ bool GodotGUIBuilder::generate_text( godot_label->add_theme_color_override("font_color", colour); } - result = godot_label; + args.result = godot_label; return ret; } -bool GodotGUIBuilder::generate_overlapping_elements( - GUI::Element const& element, String const& name, AssetManager& asset_manager, Control*& result -) { - GUI::OverlappingElementsBox const& overlapping_elements = static_cast<GUI::OverlappingElementsBox const&>(element); +static bool generate_overlapping_elements(generate_gui_args_t&& args) { + GUI::OverlappingElementsBox const& overlapping_elements = static_cast<GUI::OverlappingElementsBox const&>(args.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, name); + GUIOverlappingElementsBox* box = new_control<GUIOverlappingElementsBox>(overlapping_elements, args.name); ERR_FAIL_NULL_V_MSG( - godot_rect, false, vformat("Failed to create ColorRect for GUI overlapping elements %s", overlapping_elements_name) + box, false, + vformat("Failed to create GUIOverlappingElementsBox 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 }); - - result = godot_rect; - return true; + const bool ret = box->set_gui_overlapping_elements_box(&overlapping_elements) == OK; + args.result = box; + return ret; } -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); +static bool generate_listbox(generate_gui_args_t&& args) { + GUI::ListBox const& listbox = static_cast<GUI::ListBox const&>(args.element); - result = nullptr; const String listbox_name = std_view_to_godot_string(listbox.get_name()); - ColorRect* godot_rect = new_control<ColorRect>(listbox, name); + ColorRect* godot_rect = new_control<ColorRect>(listbox, args.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_custom_minimum_size(Utilities::to_godot_fvec2(listbox.get_size())); godot_rect->set_color({ 1.0f, 0.5f, 0.0f, 0.2f }); - result = godot_rect; + args.result = godot_rect; return true; } -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); +/* Forward declaration for use in generate_window. */ +static bool generate_element(GUI::Element const* element, String const& name, AssetManager& asset_manager, Control*& result); + +static bool generate_window(generate_gui_args_t&& args) { + GUI::Window const& window = static_cast<GUI::Window const&>(args.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, name); + Panel* godot_panel = new_control<Panel>(window, args.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_custom_minimum_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_window_elements()) { Control* node = nullptr; - const bool element_ret = generate_element(element.get(), "", asset_manager, node); + const bool element_ret = generate_element(element.get(), "", args.asset_manager, node); if (node != nullptr) { godot_panel->add_child(node); } @@ -414,6 +410,41 @@ bool GodotGUIBuilder::generate_window( } } - result = godot_panel; + args.result = godot_panel; return ret; } + +static bool generate_element(GUI::Element const* element, String const& name, AssetManager& asset_manager, Control*& result) { + ERR_FAIL_NULL_V(element, false); + static const std::map<std::string_view, bool (*)(generate_gui_args_t&&)> type_map { + { GUI::Icon::get_type_static(), &generate_icon }, + { GUI::Button::get_type_static(), &generate_button }, + { GUI::Checkbox::get_type_static(), &generate_checkbox }, + { GUI::Text::get_type_static(), &generate_text }, + { GUI::OverlappingElementsBox::get_type_static(), &generate_overlapping_elements }, + { GUI::ListBox::get_type_static(), &generate_listbox }, + { GUI::Window::get_type_static(), &generate_window } + }; + const decltype(type_map)::const_iterator it = type_map.find(element->get_type()); + if (it != type_map.end()) { + return it->second({ *element, name, asset_manager, result }); + } else { + UtilityFunctions::push_error("Invalid GUI element type: ", std_view_to_godot_string(element->get_type())); + return false; + } +} + +bool UITools::generate_gui_element( + GUI::Element const* element, String const& name, Control*& result +) { + result = nullptr; + AssetManager* asset_manager = AssetManager::get_singleton(); + ERR_FAIL_NULL_V(asset_manager, false); + return generate_element(element, name, *asset_manager, result); +} + +bool UITools::generate_gui_element( + godot::String const& gui_file, godot::String const& gui_element, godot::String const& name, godot::Control*& result +) { + return generate_gui_element(get_gui_element(gui_file, gui_element), name, result); +} diff --git a/extension/src/openvic-extension/utility/UITools.hpp b/extension/src/openvic-extension/utility/UITools.hpp new file mode 100644 index 0000000..65cf17a --- /dev/null +++ b/extension/src/openvic-extension/utility/UITools.hpp @@ -0,0 +1,18 @@ +#pragma once + +#include <godot_cpp/classes/control.hpp> + +#include <openvic-simulation/interface/GFX.hpp> +#include <openvic-simulation/interface/GUI.hpp> + +namespace OpenVic::UITools { + GFX::Sprite const* get_gfx_sprite(godot::String const& gfx_sprite); + GUI::Element const* get_gui_element(godot::String const& gui_file, godot::String const& gui_element); + + bool generate_gui_element( + GUI::Element const* element, godot::String const& name, godot::Control*& result + ); + bool generate_gui_element( + godot::String const& gui_file, godot::String const& gui_element, godot::String const& name, godot::Control*& result + ); +} diff --git a/extension/src/openvic-extension/utility/Utilities.cpp b/extension/src/openvic-extension/utility/Utilities.cpp index 099b5a9..8293e70 100644 --- a/extension/src/openvic-extension/utility/Utilities.cpp +++ b/extension/src/openvic-extension/utility/Utilities.cpp @@ -122,3 +122,18 @@ Ref<FontFile> Utilities::load_godot_font(String const& fnt_path, Ref<Image> cons } return font; } + +Ref<Image> Utilities::make_solid_colour_image(Color const& colour, int32_t width, int32_t height, Image::Format format) { + const Ref<Image> result = Image::create(width, height, false, format); + ERR_FAIL_NULL_V(result, nullptr); + result->fill(colour); + return result; +} + +Ref<ImageTexture> Utilities::make_solid_colour_texture(Color const& colour, int32_t width, int32_t height, Image::Format format) { + const Ref<Image> image = make_solid_colour_image(colour, width, height, format); + ERR_FAIL_NULL_V(image, nullptr); + const Ref<ImageTexture> result = ImageTexture::create_from_image(image); + ERR_FAIL_NULL_V(result, nullptr); + return result; +} diff --git a/extension/src/openvic-extension/utility/Utilities.hpp b/extension/src/openvic-extension/utility/Utilities.hpp index 6eeb285..9bbc700 100644 --- a/extension/src/openvic-extension/utility/Utilities.hpp +++ b/extension/src/openvic-extension/utility/Utilities.hpp @@ -1,7 +1,7 @@ #pragma once #include <godot_cpp/classes/font_file.hpp> -#include <godot_cpp/classes/image.hpp> +#include <godot_cpp/classes/image_texture.hpp> #include <openvic-simulation/types/Colour.hpp> #include <openvic-simulation/types/Date.hpp> @@ -37,12 +37,8 @@ namespace OpenVic::Utilities { godot::String date_to_formatted_string(Date date); - inline godot::Color to_godot_color(colour_t colour) { - return { - colour_byte_to_float((colour >> 16) & 0xFF), - colour_byte_to_float((colour >> 8) & 0xFF), - colour_byte_to_float(colour & 0xFF) - }; + inline godot::Color to_godot_color(IsColour auto colour) { + return { colour.redf(), colour.greenf(), colour.bluef(), colour.alphaf() }; } inline godot::Vector2i to_godot_ivec2(ivec2_t vec) { @@ -63,4 +59,14 @@ namespace OpenVic::Utilities { /* Load a Font from anywhere on the machine, combining the ".fnt" file loaded from the given path with the * already-loaded image file containing the actual characters. */ godot::Ref<godot::FontFile> load_godot_font(godot::String const& fnt_path, godot::Ref<godot::Image> const& image); + + godot::Ref<godot::Image> make_solid_colour_image( + godot::Color const& colour, int32_t width, int32_t height, + godot::Image::Format format = godot::Image::Format::FORMAT_RGBA8 + ); + + godot::Ref<godot::ImageTexture> make_solid_colour_texture( + godot::Color const& colour, int32_t width, int32_t height, + godot::Image::Format format = godot::Image::Format::FORMAT_RGBA8 + ); } diff --git a/game/localisation/en_GB/menus.csv b/game/localisation/en_GB/menus.csv index f81decf..6001a9d 100644 --- a/game/localisation/en_GB/menus.csv +++ b/game/localisation/en_GB/menus.csv @@ -126,7 +126,7 @@ LIFE_RATING_TOOLTIP;Liferating: {life_rating} terrain_type_MISSING;No Terrain Type total_population_MISSING;No Population PROVINCE_POPULATION_TOOLTIP;Province population -rgo_MISSING;No RGO +rgo_name_MISSING;No RGO building_MISSING;No Building building_fort;Fort building_naval_base;Naval Base diff --git a/game/src/Game/GameSession/GameSession.gd b/game/src/Game/GameSession/GameSession.gd index 305709c..f085073 100644 --- a/game/src/Game/GameSession/GameSession.gd +++ b/game/src/Game/GameSession/GameSession.gd @@ -2,12 +2,12 @@ extends Control @export var _game_session_menu : Control -func _ready(): +func _ready() -> void: Events.Options.load_settings_from_file() if GameSingleton.setup_game(0) != OK: push_error("Failed to setup game") -func _process(_delta : float): +func _process(_delta : float) -> void: GameSingleton.try_tick() # REQUIREMENTS: diff --git a/game/src/Game/GameSession/GameSession.tscn b/game/src/Game/GameSession/GameSession.tscn index 2dd0e16..a3a5f38 100644 --- a/game/src/Game/GameSession/GameSession.tscn +++ b/game/src/Game/GameSession/GameSession.tscn @@ -31,13 +31,15 @@ anchor_right = 1.0 anchor_bottom = 1.0 grow_horizontal = 2 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 +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 script = ExtResource("4_2kbih") [node name="MapControlPanel" parent="." instance=ExtResource("3_afh6d")] diff --git a/game/src/Game/GameSession/GameSessionMenu.gd b/game/src/Game/GameSession/GameSessionMenu.gd index 23ef2ef..7db0572 100644 --- a/game/src/Game/GameSession/GameSessionMenu.gd +++ b/game/src/Game/GameSession/GameSessionMenu.gd @@ -63,7 +63,7 @@ func _on_quit_confirmed() -> void: func _on_options_button_pressed() -> void: options_button_pressed.emit() -func _on_main_menu_dialog_custom_action(action) -> void: +func _on_main_menu_dialog_custom_action(action : StringName) -> void: match action: &"save_and_main_menu": _on_main_menu_confirmed() @@ -73,8 +73,8 @@ func _on_quit_dialog_custom_action(action : StringName) -> void: &"save_and_quit": _on_quit_confirmed() -func _on_save_button_pressed(): +func _on_save_button_pressed() -> void: save_button_pressed.emit() -func _on_load_button_pressed(): +func _on_load_button_pressed() -> void: load_button_pressed.emit() diff --git a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd index 6cdbf28..eb4dd9f 100644 --- a/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd +++ b/game/src/Game/GameSession/MapControlPanel/MapControlPanel.gd @@ -24,10 +24,10 @@ func _add_mapmode_button(identifier : String) -> void: if _mapmode_button_group.get_pressed_button() == null: button.button_pressed = true -func _ready(): +func _ready() -> void: _mapmode_button_group = ButtonGroup.new() _mapmode_button_group.pressed.connect(_mapmode_pressed) - for index in GameSingleton.get_mapmode_count(): + for index : int in GameSingleton.get_mapmode_count(): _add_mapmode_button(GameSingleton.get_mapmode_identifier(index)) # REQUIREMENTS: diff --git a/game/src/Game/GameSession/MapControlPanel/Minimap.gd b/game/src/Game/GameSession/MapControlPanel/Minimap.gd index 5564821..ce27302 100644 --- a/game/src/Game/GameSession/MapControlPanel/Minimap.gd +++ b/game/src/Game/GameSession/MapControlPanel/Minimap.gd @@ -9,7 +9,7 @@ var _minimap_shader : ShaderMaterial var _viewport_points : PackedVector2Array -func _ready(): +func _ready() -> void: _minimap_texture.custom_minimum_size = Vector2(GameSingleton.get_map_aspect_ratio(), 1.0) * 150 var minimap_material := _minimap_texture.get_material() if GameLoader.ShaderManager.set_up_shader(minimap_material, false) != OK: @@ -32,7 +32,7 @@ func _draw() -> void: # REQUIREMENTS # * SS-81 # * UIFUN-127 -func _unhandled_input(event : InputEvent): +func _unhandled_input(event : InputEvent) -> void: if event is InputEventMouse and Input.is_action_pressed(_action_click): var pos_clicked := get_local_mouse_position() / size - Vector2(0.5, 0.5) if abs(pos_clicked.x) < 0.5 and abs(pos_clicked.y) < 0.5: @@ -100,6 +100,6 @@ func _on_map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_ri _add_line_looped_over_x(far_left, near_left) _add_line_looped_over_x(near_right, far_right) - for i in _viewport_points.size(): + for i : int in _viewport_points.size(): _viewport_points[i] *= size queue_redraw() diff --git a/game/src/Game/GameSession/MapView.gd b/game/src/Game/GameSession/MapView.gd index 01e5844..499f745 100644 --- a/game/src/Game/GameSession/MapView.gd +++ b/game/src/Game/GameSession/MapView.gd @@ -50,7 +50,7 @@ var _viewport_dims : Vector2 = Vector2(1, 1) # to a failed HashMap lookup. I'm not sure if this is a bug in the # editor, GDExtension, my own extension, or a combination of them. # This was an absolute pain to track down. --- hop311 -func _ready(): +func _ready() -> void: if _camera == null: push_error("MapView's _camera variable hasn't been set!") return @@ -71,14 +71,16 @@ func _ready(): return _map_mesh = _map_mesh_instance.mesh - # Set map mesh size and get bounds - const pixels_per_terrain_tile : float = 32.0 + const pixels_per_terrain_tile : float = 20.0 _map_shader_material.set_shader_parameter(GameLoader.ShaderManager.param_terrain_tile_factor, float(GameSingleton.get_map_height()) / pixels_per_terrain_tile) + const pixels_per_stripe_tile : float = 16.0 _map_shader_material.set_shader_parameter(GameLoader.ShaderManager.param_stripe_tile_factor, float(GameSingleton.get_map_height()) / pixels_per_stripe_tile) - var map_mesh_aabb := _map_mesh.get_core_aabb() * _map_mesh_instance.transform + + # Get map mesh bounds + var map_mesh_aabb : AABB = _map_mesh.get_core_aabb() * _map_mesh_instance.transform _map_mesh_corner = Vector2( min(map_mesh_aabb.position.x, map_mesh_aabb.end.x), min(map_mesh_aabb.position.z, map_mesh_aabb.end.z) @@ -90,7 +92,7 @@ func _ready(): GameSingleton.province_selected.connect(_on_province_selected) -func _notification(what : int): +func _notification(what : int) -> void: match what: NOTIFICATION_WM_MOUSE_ENTER: # Mouse inside window _on_mouse_entered_viewport() @@ -108,7 +110,7 @@ func _viewport_to_map_coords(pos_viewport : Vector2) -> Vector2: var ray_origin := _camera.project_ray_origin(pos_viewport) var ray_normal := _camera.project_ray_normal(pos_viewport) # Plane with normal (0,1,0) facing upwards, at a distance 0 from the origin - var intersection = Plane(0, 1, 0, 0).intersects_ray(ray_origin, ray_normal) + var intersection : Variant = Plane(0, 1, 0, 0).intersects_ray(ray_origin, ray_normal) if typeof(intersection) == TYPE_VECTOR3: return _world_to_map_coords(intersection as Vector3) else: @@ -133,7 +135,7 @@ func _on_province_selected(index : int) -> void: # REQUIREMENTS # * SS-31 -func _unhandled_input(event : InputEvent): +func _unhandled_input(event : InputEvent) -> void: if _mouse_over_viewport and event.is_action_pressed(_action_click): # Check if the mouse is outside of bounds if _map_mesh.is_valid_uv_coord(_mouse_pos_map): @@ -154,7 +156,7 @@ func _unhandled_input(event : InputEvent): elif event.is_action_pressed(_action_zoom_out, true): zoom_out() -func _physics_process(delta : float): +func _physics_process(delta : float) -> void: _mouse_pos_viewport = get_viewport().get_mouse_position() _viewport_dims = Vector2(Resolution.get_current_resolution()) # Process movement @@ -212,7 +214,7 @@ func _clamp_over_map() -> void: func _zoom_process(delta : float) -> void: var height := _camera.position.y var zoom := _zoom_target - height - var zoom_delta = zoom * _zoom_speed * delta + var zoom_delta := zoom * _zoom_speed * delta # Set to target if height is within _zoom_epsilon of it or has overshot past it if abs(zoom - zoom_delta) < _zoom_epsilon or sign(zoom) != sign(zoom - zoom_delta): zoom_delta = zoom @@ -235,20 +237,20 @@ func _update_mouse_map_position() -> void: if _mouse_over_viewport: _map_shader_material.set_shader_parameter(GameLoader.ShaderManager.param_hover_index, hover_index) -func _on_mouse_entered_viewport(): +func _on_mouse_entered_viewport() -> void: _mouse_over_viewport = true -func _on_mouse_exited_viewport(): +func _on_mouse_exited_viewport() -> void: _mouse_over_viewport = false _map_shader_material.set_shader_parameter(GameLoader.ShaderManager.param_hover_index, 0) -func _on_window_entered_focus(): +func _on_window_entered_focus() -> void: _window_in_focus = true -func _on_window_exited_focus(): +func _on_window_exited_focus() -> void: _window_in_focus = false -func _on_minimap_clicked(pos_clicked : Vector2): +func _on_minimap_clicked(pos_clicked : Vector2) -> void: pos_clicked *= _map_mesh_dims _camera.position.x = pos_clicked.x _camera.position.z = pos_clicked.y diff --git a/game/src/Game/GameSession/MapView.tscn b/game/src/Game/GameSession/MapView.tscn index fb4ac07..43ff120 100644 --- a/game/src/Game/GameSession/MapView.tscn +++ b/game/src/Game/GameSession/MapView.tscn @@ -10,6 +10,7 @@ shader_parameter/province_shape_subdivisions = null shader_parameter/hover_index = null shader_parameter/selected_index = null shader_parameter/terrain_tile_factor = null +shader_parameter/stripe_tile_factor = null [sub_resource type="MapMesh" id="MapMesh_3gtsd"] diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel.gd b/game/src/Game/GameSession/ProvinceOverviewPanel.gd index 6c183fb..6ffc906 100644 --- a/game/src/Game/GameSession/ProvinceOverviewPanel.gd +++ b/game/src/Game/GameSession/ProvinceOverviewPanel.gd @@ -8,58 +8,107 @@ var _colony_status_button : Button var _colony_status_button_texture : GFXIconTexture var _administrative_percentage_label : Label var _owner_percentage_label : Label +var _province_modifiers_overlapping_elements_box : GUIOverlappingElementsBox 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_percentage_texture : GFXIconTexture 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 _crime_fighting_label : Label var _total_population_label : Label 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_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" -const _building_info_level_key : StringName = &"level" -const _building_info_expansion_state_key : StringName = &"expansion_state" -const _building_info_start_date_key : StringName = &"start_date" -const _building_info_end_date_key : StringName = &"end_date" -const _building_info_expansion_progress_key : StringName = &"expansion_progress" - -const _piechart_info_size_key : StringName = &"size" -const _piechart_info_colour_key : StringName = &"colour" +var _supply_limit_label : Label +var _cores_overlapping_elements_box : GUIOverlappingElementsBox + +# Buildings +var _buildings_panel : Panel +var _building_slots : Array[BuildingSlot] + +# REQUIREMENTS: +# * UI-183, UI-185, UI-186, UI-765, UI-187, UI-188, UI-189 +# * UI-191, UI-193, UI-194, UI-766, UI-195, UI-196, UI-197 +# * UI-199, UI-201, UI-202, UI-767, UI-203, UI-204, UI-205 +class BuildingSlot: + var _slot_index : int + var _slot_node : Control + + var _building_icon : GFXIconTexture + var _expand_button : Button + var _expanding_icon : TextureRect + var _expanding_progress_bar : TextureProgressBar + var _expanding_label : Label + + func _init(new_slot_index : int, new_slot_node : Control) -> void: + if new_slot_index < 0: + push_error("Invalid building slot index: ", new_slot_index) + return + _slot_index = new_slot_index + if not new_slot_node: + push_error("Invalid building slot node: null!") + return + _slot_node = new_slot_node + + for icon_index : int in GameSingleton.get_province_building_count(): + var icon := _slot_node.get_node("build_icon%d" % icon_index) + if icon: + if icon_index == _slot_index: + _building_icon = GUINode.get_gfx_icon_texture_from_node(icon) + else: + icon.hide() + + var building_name := GUINode.get_label_from_node(_slot_node.get_node(^"./description")) + if building_name: + building_name.text = GameSingleton.get_province_building_identifier(_slot_index) + _expand_button = GUINode.get_button_from_node(_slot_node.get_node(^"./expand")) + if _expand_button: + _expand_button.pressed.connect(func() -> void: GameSingleton.expand_selected_province_building(_slot_index)) + _expanding_icon = GUINode.get_texture_rect_from_node(_slot_node.get_node(^"./underconstruction_icon")) + _expanding_progress_bar = GUINode.get_progress_bar_from_node(_slot_node.get_node(^"./building_progress")) + if _expanding_progress_bar: + _expanding_progress_bar.max_value = 1.0 + _expanding_progress_bar.step = _expanding_progress_bar.max_value / 100 + _expanding_label = GUINode.get_label_from_node(_slot_node.get_node(^"./expand_text")) + + enum ExpansionState { CannotExpand, CanExpand, Preparing, Expanding } + + func update_info(info : Dictionary) -> void: + const building_info_level_key : StringName = &"level" + const building_info_expansion_state_key : StringName = &"expansion_state" + const building_info_start_date_key : StringName = &"start_date" + const building_info_end_date_key : StringName = &"end_date" + const building_info_expansion_progress_key : StringName = &"expansion_progress" + + if _building_icon: + _building_icon.set_icon_index(info.get(building_info_level_key, 0) + 1) + + var expansion_state : int = info.get(building_info_expansion_state_key, ExpansionState.CannotExpand) + var expansion_in_progress : bool = expansion_state == ExpansionState.Preparing or expansion_state == ExpansionState.Expanding + + if _expand_button: + _expand_button.visible = not expansion_in_progress + _expand_button.disabled = expansion_state != ExpansionState.CanExpand + + if _expanding_icon: + _expanding_icon.visible = expansion_in_progress + + if _expanding_progress_bar: + _expanding_progress_bar.visible = expansion_in_progress + _expanding_progress_bar.value = info.get(building_info_expansion_progress_key, 0) + + if _expanding_label: + _expanding_label.visible = expansion_in_progress var _selected_index : int: get: return _selected_index @@ -68,69 +117,87 @@ var _selected_index : int: _update_info() var _province_info : Dictionary -func _ready(): +func _ready() -> void: GameSingleton.province_selected.connect(_on_province_selected) GameSingleton.state_updated.connect(_update_info) - add_gui_element("province_interface.gui", "province_view") + if add_gui_element("province_interface.gui", "province_view") != OK: + push_error("Failed to generate province overview panel!") + return - var close_button : Button = get_button_node(^"./province_view/close_button") + var close_button : Button = get_button_from_nodepath(^"./province_view/close_button") if close_button: close_button.pressed.connect(_on_close_button_pressed) # 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") + _province_name_label = get_label_from_nodepath(^"./province_view/province_view_header/province_name") + _region_name_label = get_label_from_nodepath(^"./province_view/province_view_header/state_name") + _slave_status_icon = get_texture_rect_from_nodepath(^"./province_view/province_view_header/slave_state_icon") + var slave_status_icon_texture : GFXIconTexture = get_gfx_icon_texture_from_nodepath(^"./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") + _colony_status_button = get_button_from_nodepath(^"./province_view/province_view_header/colony_button") + _colony_status_button_texture = get_gfx_icon_texture_from_nodepath(^"./province_view/province_view_header/colony_button") + var admin_icon_texture : GFXIconTexture = get_gfx_icon_texture_from_nodepath(^"./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") + _administrative_percentage_label = get_label_from_nodepath(^"./province_view/province_view_header/admin_efficiency") + _owner_percentage_label = get_label_from_nodepath(^"./province_view/province_view_header/owner_presence") + _province_modifiers_overlapping_elements_box = get_gui_overlapping_elements_box_from_nodepath(^"./province_view/province_view_header/province_modifiers") + if _province_modifiers_overlapping_elements_box and _province_modifiers_overlapping_elements_box.set_gui_child_element_name("province_interface.gui", "prov_state_modifier") != OK: + _province_modifiers_overlapping_elements_box = null # hide province modifiers box since we can't do anything with it + _terrain_type_texture = get_gfx_icon_texture_from_nodepath(^"./province_view/province_view_header/prov_terrain") + _life_rating_bar = get_progress_bar_from_nodepath(^"./province_view/province_view_header/liferating") + _controller_flag_texture = get_gfx_masked_flag_texture_from_nodepath(^"./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()) + _rgo_icon_texture = get_gfx_icon_texture_from_nodepath(^"./province_view/province_statistics/goods_type") + _rgo_produced_label = get_label_from_nodepath(^"./province_view/province_statistics/produced") + _rgo_income_label = get_label_from_nodepath(^"./province_view/province_statistics/income") + _rgo_employment_percentage_texture = get_gfx_icon_texture_from_nodepath(^"./province_view/province_statistics/employment_ratio") + _rgo_employment_population_label = get_label_from_nodepath(^"./province_view/province_statistics/rgo_population") + _rgo_employment_percentage_label = get_label_from_nodepath(^"./province_view/province_statistics/rgo_percent") + _crime_name_label = get_label_from_nodepath(^"./province_view/province_statistics/crime_name") + _crime_icon_texture = get_gfx_icon_texture_from_nodepath(^"./province_view/province_statistics/crime_icon") + _crime_fighting_label = get_label_from_nodepath(^"./province_view/province_statistics/crimefight_percent") + _total_population_label = get_label_from_nodepath(^"./province_view/province_statistics/total_population") + _migration_label = get_label_from_nodepath(^"./province_view/province_statistics/migration") + _population_growth_label = get_label_from_nodepath(^"./province_view/province_statistics/growth") + _pop_types_piechart = get_gfx_pie_chart_texture_from_nodepath(^"./province_view/province_statistics/workforce_chart") + _pop_ideologies_piechart = get_gfx_pie_chart_texture_from_nodepath(^"./province_view/province_statistics/ideology_chart") + _pop_cultures_piechart = get_gfx_pie_chart_texture_from_nodepath(^"./province_view/province_statistics/culture_chart") + _supply_limit_label = get_label_from_nodepath(^"./province_view/province_statistics/supply_limit_label") + _cores_overlapping_elements_box = get_gui_overlapping_elements_box_from_nodepath(^"./province_view/province_statistics/core_icons") + if _cores_overlapping_elements_box and _cores_overlapping_elements_box.set_gui_child_element_name("province_interface.gui", "province_core") != OK: + _cores_overlapping_elements_box = null # hide cores box since we can't do anything with it + + _buildings_panel = get_panel_from_nodepath(^"./province_view/province_buildings") + if _buildings_panel: + var target_slot_count : int = GameSingleton.get_province_building_count() + var slot_y : float = 0.0 + for current_slot_count : int in target_slot_count: + var slot := generate_gui_element("province_interface.gui", "building", "building_slot_%d" % current_slot_count) + if slot: + _buildings_panel.add_child(slot) + slot.set_position(Vector2(0.0, slot_y)) + slot_y += slot.get_size().y + _building_slots.push_back(BuildingSlot.new(current_slot_count, slot)) + else: + push_error("Failed to generate building slot ", current_slot_count, " / ", target_slot_count) + break # TODO - fix checkbox positions - for path in [ + for path : NodePath 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) + var rally_checkbox : CheckBox = get_check_box_from_nodepath(path) rally_checkbox.set_position(rally_checkbox.get_position() - Vector2(3, 3)) hide_nodes([ + ^"./province_view/province_view_header/state_modifiers", ^"./province_view/province_view_header/occupation_progress", ^"./province_view/province_view_header/occupation_icon", ^"./province_view/province_view_header/occupation_flag", @@ -144,86 +211,40 @@ func _ready(): _update_info() -func _notification(what : int): +func _notification(what : int) -> void: match what: NOTIFICATION_TRANSLATION_CHANGED: _update_info() -""" -enum { CANNOT_EXPAND, CAN_EXPAND, PREPARING, EXPANDING } - -func _expand_building(building_identifier : String) -> void: - if GameSingleton.expand_building(_selected_index, building_identifier) != OK: - push_error("Failed to expand ", building_identifier, " in province #", _selected_index); -# Each building row contains: -# level - Level Label -# name - Name Label -# button - Expansion Button -# progress_bar - Expansion ProgressBar -var _building_rows : Array[Dictionary] +enum ColonyStatus { STATE, PROTECTORATE, COLONY } +# This assumes _cores_overlapping_elements_box is non-null +func _set_core_flag(core_index : int, country : String) -> void: + var core_flag_texture : GFXMaskedFlagTexture = get_gfx_masked_flag_texture_from_node(_cores_overlapping_elements_box.get_child(core_index).get_node(^"./country_flag")) + if core_flag_texture: + core_flag_texture.set_flag_country_name(country) -# REQUIREMENTS: -# * UI-183, UI-185, UI-186, UI-765, UI-187, UI-188, UI-189 -# * UI-191, UI-193, UI-194, UI-766, UI-195, UI-196, UI-197 -# * UI-199, UI-201, UI-202, UI-767, UI-203, UI-204, UI-205 -func _add_building_row() -> void: - var row : Dictionary = {} - - row.level = Label.new() - row.level.text = "X" - _buildings_container.add_child(row.level) - - row.name = Label.new() - row.name.text = _building_info_building_key + _missing_suffix - _buildings_container.add_child(row.name) - - row.button = Button.new() - row.button.text = "EXPAND_PROVINCE_BUILDING" - row.button.size_flags_horizontal = Control.SIZE_EXPAND_FILL - row.button.mouse_filter = Control.MOUSE_FILTER_PASS - row.button.focus_mode = FOCUS_NONE - row.button.pressed.connect(func(): _expand_building(row.name.text)) - _buildings_container.add_child(row.button) - - row.progress_bar = ProgressBar.new() - row.progress_bar.max_value = 1 - row.progress_bar.size_flags_horizontal = Control.SIZE_EXPAND_FILL - row.progress_bar.mouse_filter = Control.MOUSE_FILTER_PASS - _buildings_container.add_child(row.progress_bar) - - _building_rows.append(row) - _set_building_row(_building_rows.size() - 1, {}) - -func _set_building_row(index : int, building : Dictionary) -> void: - if index < 0 or index > _building_rows.size(): - push_error("Invalid building row index: ", index, " (max ", _building_rows.size(), ")") - return - if index == _building_rows.size(): _add_building_row() - var row := _building_rows[index] - if building.is_empty(): - row.level.visible = false - row.name.visible = false - row.progress_bar.visible = false - row.button.visible = false - return - row.level.text = str(building.get(_building_info_level_key, 0)) - row.level.visible = true - row.name.text = building.get(_building_info_building_key, - _building_info_building_key + _missing_suffix) - row.name.visible = true - - var expansion_state : int = building.get(_building_info_expansion_state_key, - CANNOT_EXPAND) - var show_progress_bar := expansion_state == PREPARING or expansion_state == EXPANDING - row.progress_bar.value = building.get(_building_info_expansion_progress_key, 0) - row.progress_bar.visible = show_progress_bar - row.button.disabled = expansion_state != CAN_EXPAND - row.button.visible = not show_progress_bar -""" - -enum { STATE, PROTECTORATE, COLONY } func _update_info() -> void: + const _province_info_province_key : StringName = &"province" + const _province_info_region_key : StringName = &"region" + const _province_info_slave_status_key : StringName = &"slave_status" + const _province_info_colony_status_key : StringName = &"colony_status" + const _province_info_terrain_type_key : StringName = &"terrain_type" + const _province_info_life_rating_key : StringName = &"life_rating" + const _province_info_controller_key : StringName = &"controller" + const _province_info_rgo_name_key : StringName = &"rgo_name" + const _province_info_rgo_icon_key : StringName = &"rgo_icon" + 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_cores_key : StringName = &"cores" + const _province_info_buildings_key : StringName = &"buildings" + + const _missing_suffix : String = "_MISSING" + _province_info = GameSingleton.get_province_info_from_index(_selected_index) if _province_info: # Header @@ -238,9 +259,9 @@ func _update_info() -> void: 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) + var colony_status : ColonyStatus = _province_info.get(_province_info_colony_status_key, 0) if _colony_status_button: - if colony_status == STATE: + if colony_status == ColonyStatus.STATE: _colony_status_button.hide() else: if _colony_status_button_texture: @@ -253,6 +274,14 @@ func _update_info() -> void: if _owner_percentage_label: pass + if _province_modifiers_overlapping_elements_box: + # TODO - replace example icons with those from the province's list of modifier instances + _province_modifiers_overlapping_elements_box.set_child_count(8) + for i : int in _province_modifiers_overlapping_elements_box.get_child_count(): + var icon : GFXIconTexture = get_gfx_icon_texture_from_node(_province_modifiers_overlapping_elements_box.get_child(i).get_node(^"./modifier")) + if icon: + icon.set_icon_index(2 * i + (i & 1) + 1) + if _terrain_type_texture: var terrain_type : String = _province_info.get(_province_info_terrain_type_key, "") if terrain_type: @@ -264,21 +293,25 @@ func _update_info() -> void: _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, &"") + _controller_flag_texture.set_flag_country_name(_province_info.get(_province_info_controller_key, "")) # 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: + # TODO - replace name with amount produced _rgo_produced_label.text = _province_info.get(_province_info_rgo_name_key, _province_info_rgo_name_key + _missing_suffix) if _rgo_income_label: + # TODO - add £ sign and replace placeholder with actual value _rgo_income_label.text = GameSingleton.float_to_formatted_string(12.34567) - # TODO - add £ sign + + if _rgo_employment_percentage_texture: + pass if _rgo_employment_population_label: + # TODO - replace placeholder with actual value _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: @@ -290,6 +323,9 @@ func _update_info() -> void: if _crime_icon_texture: _crime_icon_texture.set_icon_index(_province_info.get(_province_info_crime_icon_key, 0) + 1) + if _crime_fighting_label: + pass + if _total_population_label: _total_population_label.text = GameSingleton.int_to_formatted_string(_province_info.get(_province_info_total_population_key, 0)) @@ -300,17 +336,32 @@ func _update_info() -> void: pass if _pop_types_piechart: - _pop_types_piechart.set_slices(_province_info.get(_province_info_pop_types_key, [])) + _pop_types_piechart.set_slices_array(_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, [])) + _pop_ideologies_piechart.set_slices_array(_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, [])) + _pop_cultures_piechart.set_slices_array(_province_info.get(_province_info_pop_cultures_key, [])) + + if _supply_limit_label: + pass - #var buildings : Array = _province_info.get(_province_info_buildings_key, []) - #for i in max(buildings.size(), _building_rows.size()): - # _set_building_row(i, buildings[i] if i < buildings.size() else {}) + if _cores_overlapping_elements_box: + var cores : PackedStringArray = _province_info.get(_province_info_cores_key, []) + _cores_overlapping_elements_box.set_child_count(cores.size()) + for core_index : int in min(cores.size(), _cores_overlapping_elements_box.get_child_count()): + _set_core_flag(core_index, cores[core_index]) + for core_index : int in range(cores.size(), _cores_overlapping_elements_box.get_child_count()): + _set_core_flag(core_index, "") + + # Buildings + if _buildings_panel: + var buildings : Array[Dictionary] = _province_info.get(_province_info_buildings_key, [] as Array[Dictionary]) + for slot_index : int in min(buildings.size(), _building_slots.size()): + _building_slots[slot_index].update_info(buildings[slot_index]) + for slot_index : int in range(buildings.size(), _building_slots.size()): + _building_slots[slot_index].update_info({}) show() else: diff --git a/game/src/Game/GameSession/Topbar.gd b/game/src/Game/GameSession/Topbar.gd index 6cc710f..4de9474 100644 --- a/game/src/Game/GameSession/Topbar.gd +++ b/game/src/Game/GameSession/Topbar.gd @@ -7,7 +7,7 @@ var _speed_indicator_texture : GFXIconTexture var _date_label : Label var _country_name_label : Label -func _ready(): +func _ready() -> void: GameSingleton.state_updated.connect(_update_info) add_gui_element("topbar.gui", "topbar") @@ -19,30 +19,30 @@ func _ready(): const player_country : String = "SLV" - var player_flag_texture : GFXMaskedFlagTexture = get_gfx_masked_flag_texture_from_node(^"./topbar/player_flag") + var player_flag_texture : GFXMaskedFlagTexture = get_gfx_masked_flag_texture_from_nodepath(^"./topbar/player_flag") if player_flag_texture: - player_flag_texture.set_flag_country_name_and_type(player_country, &"") + player_flag_texture.set_flag_country_name(player_country) - _speed_up_button = get_button_node(^"./topbar/button_speedup") + _speed_up_button = get_button_from_nodepath(^"./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") + _speed_down_button = get_button_from_nodepath(^"./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") + var pause_bg_button : Button = get_button_from_nodepath(^"./topbar/pause_bg") if pause_bg_button: pause_bg_button.pressed.connect(_on_play_pause_button_pressed) - _date_label = get_label_node(^"./topbar/DateText") + _date_label = get_label_from_nodepath(^"./topbar/DateText") - _country_name_label = get_label_node(^"./topbar/CountryName") + _country_name_label = get_label_from_nodepath(^"./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") + _speed_indicator_button = get_button_from_nodepath(^"./topbar/speed_indicator") + _speed_indicator_texture = get_gfx_icon_texture_from_nodepath(^"./topbar/speed_indicator") func _update_info() -> void: if _date_label: @@ -64,21 +64,21 @@ func _update_info() -> void: # REQUIREMENTS: # * UIFUN-71 -func _on_play_pause_button_pressed(): +func _on_play_pause_button_pressed() -> void: print("Toggling pause!") GameSingleton.toggle_paused() _update_info() # REQUIREMENTS: # * UIFUN-72 -func _on_increase_speed_button_pressed(): +func _on_increase_speed_button_pressed() -> void: print("Speed up!") GameSingleton.increase_speed() _update_info() # REQUIREMENTS: # * UIFUN-73 -func _on_decrease_speed_button_pressed(): +func _on_decrease_speed_button_pressed() -> void: print("Speed down!") GameSingleton.decrease_speed() _update_info() |