diff options
Diffstat (limited to 'extension')
13 files changed, 406 insertions, 57 deletions
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation -Subproject 164e76e367ff7dc5914f0d7105b5914fd3fba90 +Subproject 2c892c99a6647be15ef23cabf6cc40f08769283 diff --git a/extension/src/openvic-extension/classes/GUIListBox.cpp b/extension/src/openvic-extension/classes/GUIListBox.cpp new file mode 100644 index 0000000..f7b8d88 --- /dev/null +++ b/extension/src/openvic-extension/classes/GUIListBox.cpp @@ -0,0 +1,226 @@ +#include "GUIListBox.hpp" + +#include <godot_cpp/classes/input_event_mouse_button.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 GUIListBox::_calculate_child_arrangement() { + ERR_FAIL_NULL_V(gui_listbox, FAILED); + + const int32_t child_count = get_child_count(); + const real_t max_height = get_size().height; + + real_t height = 0.0f, height_under_max_scroll_index = 0.0f; + + children_data.clear(); + max_scroll_index = 0; + + for (int32_t index = 0; index < child_count; ++index) { + Control* child = Object::cast_to<Control>(get_child(index)); + if (child != nullptr && child != scrollbar && child->is_visible()) { + const real_t child_height = child->get_size().height; /* Spacing is ignored */ + children_data.push_back({ child, height, child_height }); + + height += child_height; + height_under_max_scroll_index += child_height; + + while (height_under_max_scroll_index > max_height && max_scroll_index + 1 < children_data.size()) { + height_under_max_scroll_index -= children_data[max_scroll_index++].height; + } + } + } + + ERR_FAIL_NULL_V(scrollbar, FAILED); + + scrollbar->set_limits(0, max_scroll_index, false); + scrollbar->emit_value_changed(); + scrollbar->set_visible(max_scroll_index > 0); + + return OK; +} + +Error GUIListBox::_update_child_positions() { + ERR_FAIL_NULL_V(gui_listbox, FAILED); + + if (children_data.empty()) { + return OK; + } + + const real_t max_height = get_size().height; + const real_t scroll_pos = children_data[scroll_index].start_pos; + + for (int32_t index = 0; index < children_data.size(); ++index) { + child_data_t const& data = children_data[index]; + if (index < scroll_index || data.start_pos + data.height > scroll_pos + max_height) { + data.child->set_position({ 0.0f, max_height + 10.0f }); + } else { + data.child->set_position({ 0.0f, data.start_pos - scroll_pos }); + } + } + + return OK; +} + +void GUIListBox::_bind_methods() { + OV_BIND_METHOD(GUIListBox::clear); + OV_BIND_METHOD(GUIListBox::clear_children); + + OV_BIND_METHOD(GUIListBox::get_scroll_index); + OV_BIND_METHOD(GUIListBox::set_scroll_index, { "new_scroll_index" }); + OV_BIND_METHOD(GUIListBox::get_max_scroll_index); + + OV_BIND_METHOD(GUIListBox::get_gui_listbox_name); + OV_BIND_METHOD(GUIListBox::get_scrollbar); +} + +void GUIListBox::_notification(int what) { + switch (what) { + case NOTIFICATION_SORT_CHILDREN: { + _calculate_child_arrangement(); + } break; + } +} + +GUIListBox::GUIListBox() + : gui_listbox { nullptr }, scrollbar { nullptr }, children_data {}, scroll_index { 0 }, max_scroll_index { 0 } { + set_clip_contents(true); +} + +Vector2 GUIListBox::_get_minimum_size() const { + if (gui_listbox != nullptr) { + Size2 size = Utilities::to_godot_fvec2(gui_listbox->get_size()); + + if (scrollbar != nullptr) { + size.width += scrollbar->get_minimum_size().width; + } + + return size; + } else { + return {}; + } +} + +void GUIListBox::_gui_input(godot::Ref<godot::InputEvent> const& event) { + ERR_FAIL_NULL(event); + + Ref<InputEventMouseButton> mb = event; + + if (mb.is_valid()) { + if (mb->is_pressed()) { + if (mb->get_button_index() == MouseButton::MOUSE_BUTTON_WHEEL_UP) { + set_scroll_index(scroll_index - 1); + } else if (mb->get_button_index() == MouseButton::MOUSE_BUTTON_WHEEL_DOWN) { + set_scroll_index(scroll_index + 1); + } else { + return; + } + accept_event(); + } + } +} + +void GUIListBox::clear() { + gui_listbox = nullptr; + children_data.clear(); + scroll_index = 0; + max_scroll_index = 0; + clear_children(); + if (scrollbar != nullptr) { + remove_child(scrollbar); + scrollbar = nullptr; + } +} + +void GUIListBox::clear_children() { + int32_t child_count = get_child_count(); + while (child_count > 0) { + Node* child = get_child(--child_count); + if (child != scrollbar) { + remove_child(child); + } + } + if (scrollbar != nullptr) { + scrollbar->set_value(0); + } +} + +void GUIListBox::set_scroll_index(int32_t new_scroll_index) { + scroll_index = std::clamp(new_scroll_index, 0, max_scroll_index); + if (scrollbar != nullptr && scrollbar->get_value() != scroll_index) { + scrollbar->set_value(scroll_index, false); + } + _update_child_positions(); +} + +Error GUIListBox::set_gui_listbox(GUI::ListBox const* new_gui_listbox) { + if (gui_listbox == new_gui_listbox) { + return OK; + } + + if (new_gui_listbox == nullptr) { + clear(); + return OK; + } + + gui_listbox = new_gui_listbox; + + const String scrollbar_name = std_view_to_godot_string(gui_listbox->get_scrollbar_name()); + + Error err = OK; + + if (scrollbar_name.is_empty()) { + UtilityFunctions::push_error("GUIListBox ", get_name(), " has no scrollbar name!"); + err = FAILED; + scrollbar = nullptr; + } else { + static const String scrollbar_scene = "core"; + + Control* scrollbar_control = nullptr; + if (!UITools::generate_gui_element(scrollbar_scene, scrollbar_name, "", scrollbar_control)) { + UtilityFunctions::push_error("Error generating scrollbar ", scrollbar_name, " for GUIListBox ", get_name()); + err = FAILED; + } + + if (scrollbar_control != nullptr) { + scrollbar = Object::cast_to<GUIScrollbar>(scrollbar_control); + if (scrollbar != nullptr) { + add_child(scrollbar); + + const Size2 size = Utilities::to_godot_fvec2(gui_listbox->get_size()); + scrollbar->set_position({ size.width, 0.0f }); + scrollbar->set_length_override(size.height); + + static const StringName set_scroll_index_func_name = "set_scroll_index"; + + scrollbar->connect( + GUIScrollbar::signal_value_changed(), Callable { this, set_scroll_index_func_name }, CONNECT_PERSIST + ); + } else { + UtilityFunctions::push_error( + "Element ", scrollbar_name, " for GUIListBox ", get_name(), " is not a GUIScrollbar" + ); + err = FAILED; + } + } else { + scrollbar = nullptr; + } + } + + return err; +} + +String GUIListBox::get_gui_listbox_name() const { + return gui_listbox != nullptr ? std_view_to_godot_string(gui_listbox->get_name()) : String {}; +} + +GUIScrollbar* GUIListBox::get_scrollbar() const { + return scrollbar; +} diff --git a/extension/src/openvic-extension/classes/GUIListBox.hpp b/extension/src/openvic-extension/classes/GUIListBox.hpp new file mode 100644 index 0000000..9d49840 --- /dev/null +++ b/extension/src/openvic-extension/classes/GUIListBox.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include <godot_cpp/classes/container.hpp> + +#include <openvic-simulation/interface/GUI.hpp> + +#include "openvic-extension/classes/GUIScrollbar.hpp" + +namespace OpenVic { + class GUIListBox : public godot::Container { + GDCLASS(GUIListBox, godot::Container) + + GUI::ListBox const* PROPERTY(gui_listbox); + + GUIScrollbar* scrollbar; + + struct child_data_t { + Control* child; + real_t start_pos, height; + }; + + std::vector<child_data_t> children_data; + + /* The children_data index of the topmost visible child element. */ + int32_t PROPERTY(scroll_index); + int32_t PROPERTY(max_scroll_index); + + godot::Error _calculate_child_arrangement(); + godot::Error _update_child_positions(); + + protected: + static void _bind_methods(); + + void _notification(int what); + + public: + GUIListBox(); + + godot::Vector2 _get_minimum_size() const override; + void _gui_input(godot::Ref<godot::InputEvent> const& event) override; + + /* Reset gui_listbox to nullptr, and remove all child elements. */ + void clear(); + + /* Remove all child elements except for the scrollbar. */ + void clear_children(); + + void set_scroll_index(int32_t new_scroll_index); + + /* Set the GUI::ListBox. This does not affect any existing child elements. */ + godot::Error set_gui_listbox(GUI::ListBox const* new_gui_listbox); + + /* Return the name of the GUI::ListBox, or an empty String if it's null. */ + godot::String get_gui_listbox_name() const; + + GUIScrollbar* get_scrollbar() const; + }; +} diff --git a/extension/src/openvic-extension/classes/GUINode.cpp b/extension/src/openvic-extension/classes/GUINode.cpp index 5296ad7..73ebb0c 100644 --- a/extension/src/openvic-extension/classes/GUINode.cpp +++ b/extension/src/openvic-extension/classes/GUINode.cpp @@ -18,7 +18,8 @@ using namespace OpenVic; F(TextureProgressBar, progress_bar) \ F(TextureRect, texture_rect) \ F(GUIOverlappingElementsBox, gui_overlapping_elements_box) \ - F(GUIScrollbar, gui_scrollbar) + F(GUIScrollbar, gui_scrollbar) \ + F(GUIListBox, gui_listbox) #define APPLY_TO_TEXTURE_TYPES(F) \ F(GFXSpriteTexture, gfx_sprite_texture) \ @@ -48,6 +49,7 @@ void GUINode::_bind_methods() { OV_BIND_SMETHOD(int_to_formatted_string, { "val" }); OV_BIND_SMETHOD(float_to_formatted_string, { "val", "decimal_places" }); + OV_BIND_SMETHOD(format_province_name, { "province_identifier" }); } GUINode::GUINode() { @@ -189,3 +191,8 @@ String GUINode::int_to_formatted_string(int64_t val) { String GUINode::float_to_formatted_string(float val, int32_t decimal_places) { return Utilities::float_to_formatted_string(val, decimal_places); } + +String GUINode::format_province_name(String const& province_identifier) { + static const String province_prefix = "PROV"; + return province_prefix + province_identifier; +} diff --git a/extension/src/openvic-extension/classes/GUINode.hpp b/extension/src/openvic-extension/classes/GUINode.hpp index f935683..3dbe403 100644 --- a/extension/src/openvic-extension/classes/GUINode.hpp +++ b/extension/src/openvic-extension/classes/GUINode.hpp @@ -12,6 +12,7 @@ #include "openvic-extension/classes/GFXSpriteTexture.hpp" #include "openvic-extension/classes/GFXMaskedFlagTexture.hpp" #include "openvic-extension/classes/GFXPieChartTexture.hpp" +#include "openvic-extension/classes/GUIListBox.hpp" #include "openvic-extension/classes/GUIOverlappingElementsBox.hpp" #include "openvic-extension/classes/GUIScrollbar.hpp" @@ -43,6 +44,7 @@ namespace OpenVic { static godot::TextureRect* get_texture_rect_from_node(godot::Node* node); static GUIOverlappingElementsBox* get_gui_overlapping_elements_box_from_node(godot::Node* node); static GUIScrollbar* get_gui_scrollbar_from_node(godot::Node* node); + static GUIListBox* get_gui_listbox_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; @@ -52,6 +54,7 @@ namespace OpenVic { 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; GUIScrollbar* get_gui_scrollbar_from_nodepath(godot::NodePath const& path) const; + GUIListBox* get_gui_listbox_from_nodepath(godot::NodePath const& path) const; /* Helper functions to get textures from TextureRects and Buttons. */ static godot::Ref<godot::Texture2D> get_texture_from_node(godot::Node* node); @@ -69,5 +72,6 @@ namespace OpenVic { static godot::String int_to_formatted_string(int64_t val); static godot::String float_to_formatted_string(float val, int32_t decimal_places); + static godot::String format_province_name(godot::String const& province_identifier); }; } diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.cpp b/extension/src/openvic-extension/classes/GUIScrollbar.cpp index 0f3cde1..93eb00b 100644 --- a/extension/src/openvic-extension/classes/GUIScrollbar.cpp +++ b/extension/src/openvic-extension/classes/GUIScrollbar.cpp @@ -16,7 +16,7 @@ using OpenVic::Utilities::std_view_to_godot_string; /* StringNames cannot be constructed until Godot has called StringName::setup(), * so we must use wrapper functions to delay their initialisation. */ -StringName const& GUIScrollbar::_signal_value_changed() { +StringName const& GUIScrollbar::signal_value_changed() { static const StringName signal_value_changed = "value_changed"; return signal_value_changed; } @@ -44,7 +44,10 @@ void GUIScrollbar::_bind_methods() { OV_BIND_METHOD(GUIScrollbar::set_range_limits, { "new_range_limit_min", "new_range_limit_max", "signal" }, DEFVAL(true)); OV_BIND_METHOD(GUIScrollbar::set_limits, { "new_min_value", "new_max_value", "signal" }, DEFVAL(true)); - ADD_SIGNAL(MethodInfo(_signal_value_changed(), PropertyInfo(Variant::INT, "value"))); + OV_BIND_METHOD(GUIScrollbar::get_length_override); + OV_BIND_METHOD(GUIScrollbar::set_length_override, { "new_length_override" }); + + ADD_SIGNAL(MethodInfo(signal_value_changed(), PropertyInfo(Variant::INT, "value"))); } GUIScrollbar::GUIScrollbar() { @@ -220,24 +223,30 @@ void GUIScrollbar::_constrain_value() { /* _constrain_value() should be called sometime after this. */ Error GUIScrollbar::_constrain_range_limits() { - range_limit_min = std::clamp(range_limit_min, min_value, max_value); - range_limit_max = std::clamp(range_limit_max, min_value, max_value); - - Error err = OK; - if (range_limit_min > range_limit_max) { - UtilityFunctions::push_error( - "GUIScrollbar range max ", range_limit_max, " is less than range min ", range_limit_min, " - swapping values." - ); - std::swap(range_limit_min, range_limit_max); - err = FAILED; - } + if (range_limited) { + range_limit_min = std::clamp(range_limit_min, min_value, max_value); + range_limit_max = std::clamp(range_limit_max, min_value, max_value); + + Error err = OK; + if (range_limit_min > range_limit_max) { + UtilityFunctions::push_error( + "GUIScrollbar range max ", range_limit_max, " is less than range min ", range_limit_min, " - swapping values." + ); + std::swap(range_limit_min, range_limit_max); + err = FAILED; + } - const int axis = orientation == HORIZONTAL ? 0 : 1; - range_limit_min_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_min); - range_limit_max_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_max) - + slider_rect.size[axis] / 2.0f; + const int axis = orientation == HORIZONTAL ? 0 : 1; + range_limit_min_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_min); + range_limit_max_rect.position[axis] = slider_start + slider_distance * _value_to_ratio(range_limit_max) + + slider_rect.size[axis] / 2.0f; - return err; + return err; + } else { + range_limit_min = min_value; + range_limit_max = max_value; + return OK; + } } /* _constrain_range_limits() should be called sometime after this. */ @@ -265,6 +274,10 @@ Vector2 GUIScrollbar::_get_minimum_size() const { size[axis] = std::max(size[axis], more_texture->get_size()[axis]); } + if (length_override > 0.0f) { + size[1 - axis] = length_override; + } + return size; } else { return {}; @@ -272,7 +285,7 @@ Vector2 GUIScrollbar::_get_minimum_size() const { } void GUIScrollbar::emit_value_changed() { - emit_signal(_signal_value_changed(), value); + emit_signal(signal_value_changed(), value); } Error GUIScrollbar::reset() { @@ -291,7 +304,7 @@ Error GUIScrollbar::reset() { pressed_less = false; pressed_more = false; - value = (max_value - min_value) / 2; + value = min_value; range_limit_min = min_value; range_limit_max = max_value; @@ -321,6 +334,7 @@ void GUIScrollbar::clear() { range_limit_max_rect = {}; orientation = HORIZONTAL; + length_override = 0.0f; min_value = 0; max_value = 100; range_limited = false; @@ -347,6 +361,7 @@ Error GUIScrollbar::set_gui_scrollbar(GUI::Scrollbar const* new_gui_scrollbar) { const String gui_scrollbar_name = std_view_to_godot_string(gui_scrollbar->get_name()); orientation = gui_scrollbar->is_horizontal() ? HORIZONTAL : VERTICAL; + length_override = 0.0f; range_limited = gui_scrollbar->is_range_limited(); /* _Element is either GUI::Button or GUI::Icon, both of which have their own @@ -477,6 +492,19 @@ Error GUIScrollbar::set_limits(int32_t new_min_value, int32_t new_max_value, boo return ERR(ret); } +void GUIScrollbar::set_length_override(real_t new_length_override) { + ERR_FAIL_COND_MSG( + length_override < 0, vformat("Invalid GUIScrollbar length override: %f - cannot be negative!", length_override) + ); + + length_override = new_length_override; + + _calculate_rects(); + _constrain_limits(); + _constrain_range_limits(); + _constrain_value(); +} + void GUIScrollbar::_gui_input(Ref<InputEvent> const& event) { ERR_FAIL_NULL(event); diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.hpp b/extension/src/openvic-extension/classes/GUIScrollbar.hpp index 7af7b57..c5b476a 100644 --- a/extension/src/openvic-extension/classes/GUIScrollbar.hpp +++ b/extension/src/openvic-extension/classes/GUIScrollbar.hpp @@ -11,8 +11,6 @@ namespace OpenVic { class GUIScrollbar : public godot::Control { GDCLASS(GUIScrollbar, godot::Control) - static godot::StringName const& _signal_value_changed(); - GUI::Scrollbar const* PROPERTY(gui_scrollbar); godot::Ref<GFXSpriteTexture> slider_texture; @@ -33,6 +31,7 @@ namespace OpenVic { godot::Rect2 range_limit_max_rect; godot::Orientation PROPERTY(orientation); + real_t PROPERTY(length_override); int32_t PROPERTY(value); int32_t PROPERTY(min_value); @@ -78,6 +77,8 @@ namespace OpenVic { void _notification(int what); public: + static godot::StringName const& signal_value_changed(); + GUIScrollbar(); godot::Vector2 _get_minimum_size() const override; @@ -98,5 +99,8 @@ namespace OpenVic { godot::Error set_range_limits(int32_t new_range_limit_min, int32_t new_range_limit_max, bool signal = true); godot::Error set_limits(int32_t new_min_value, int32_t new_max_value, bool signal = true); + + /* Override the main dimension of gui_scollbar's size with the specified length. */ + void set_length_override(real_t new_length_override); }; } diff --git a/extension/src/openvic-extension/register_types.cpp b/extension/src/openvic-extension/register_types.cpp index 1d503d7..750cc53 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/GFXSpriteTexture.hpp" #include "openvic-extension/classes/GFXMaskedFlagTexture.hpp" #include "openvic-extension/classes/GFXPieChartTexture.hpp" +#include "openvic-extension/classes/GUIListBox.hpp" #include "openvic-extension/classes/GUINode.hpp" #include "openvic-extension/classes/GUIOverlappingElementsBox.hpp" #include "openvic-extension/classes/GUIScrollbar.hpp" @@ -56,6 +57,7 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) { ClassDB::register_class<GFXMaskedFlagTexture>(); ClassDB::register_class<GFXPieChartTexture>(); + ClassDB::register_class<GUIListBox>(); ClassDB::register_class<GUINode>(); ClassDB::register_class<GUIOverlappingElementsBox>(); ClassDB::register_class<GUIScrollbar>(); diff --git a/extension/src/openvic-extension/singletons/AssetManager.cpp b/extension/src/openvic-extension/singletons/AssetManager.cpp index 546dc9d..083d934 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.cpp +++ b/extension/src/openvic-extension/singletons/AssetManager.cpp @@ -13,8 +13,8 @@ using OpenVic::Utilities::godot_to_std_string; 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_METHOD(AssetManager::get_image, { "path", "cache", "flip_y" }, DEFVAL(true), DEFVAL(false)); + OV_BIND_METHOD(AssetManager::get_texture, { "path", "flip_y" }, DEFVAL(false)); OV_BIND_METHOD(AssetManager::get_font, { "name" }); } @@ -45,19 +45,22 @@ Ref<Image> AssetManager::_load_image(StringName const& path) { return image; } -AssetManager::image_asset_t* AssetManager::_get_image_asset(StringName const& path) { +AssetManager::image_asset_t* AssetManager::_get_image_asset(StringName const& path, bool flip_y) { image_asset_map_t::iterator it = image_assets.find(path); if (it != image_assets.end()) { return &it.value(); } const Ref<Image> image = _load_image(path); ERR_FAIL_NULL_V(image, nullptr); + if (flip_y) { + image->flip_y(); + } return &image_assets.emplace(std::move(path), AssetManager::image_asset_t { image, nullptr }).first.value(); } -Ref<Image> AssetManager::get_image(StringName const& path, bool cache) { +Ref<Image> AssetManager::get_image(StringName const& path, bool cache, bool flip_y) { if (cache) { - image_asset_t const* asset = _get_image_asset(path); + image_asset_t const* asset = _get_image_asset(path, flip_y); ERR_FAIL_NULL_V(asset, nullptr); return asset->image; } else { @@ -65,8 +68,8 @@ Ref<Image> AssetManager::get_image(StringName const& path, bool cache) { } } -Ref<ImageTexture> AssetManager::get_texture(StringName const& path) { - image_asset_t* asset = _get_image_asset(path); +Ref<ImageTexture> AssetManager::get_texture(StringName const& path, bool flip_y) { + image_asset_t* asset = _get_image_asset(path, flip_y); ERR_FAIL_NULL_V(asset, nullptr); if (asset->texture.is_null()) { asset->texture = ImageTexture::create_from_image(asset->image); diff --git a/extension/src/openvic-extension/singletons/AssetManager.hpp b/extension/src/openvic-extension/singletons/AssetManager.hpp index 7f73e4c..0416e5b 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.hpp +++ b/extension/src/openvic-extension/singletons/AssetManager.hpp @@ -25,7 +25,7 @@ namespace OpenVic { font_map_t fonts; static godot::Ref<godot::Image> _load_image(godot::StringName const& path); - image_asset_t* _get_image_asset(godot::StringName const& path); + image_asset_t* _get_image_asset(godot::StringName const& path, bool flip_y); protected: static void _bind_methods(); @@ -38,12 +38,12 @@ namespace OpenVic { /* Search for and load an image at the specified path relative to the game defines, first checking the AssetManager's * image cache (if cache is true) in case it has already been loaded, and returning nullptr if image loading fails. */ - godot::Ref<godot::Image> get_image(godot::StringName const& path, bool cache = true); + godot::Ref<godot::Image> get_image(godot::StringName const& path, bool cache = true, bool flip_y = false); /* Create a texture from an image found at the specified path relative to the game defines, fist checking * AssetManager's texture cache in case it has already been loaded, and returning nullptr if image loading * or texture creation fails. */ - godot::Ref<godot::ImageTexture> get_texture(godot::StringName const& path); + godot::Ref<godot::ImageTexture> get_texture(godot::StringName const& path, bool flip_y = false); /* 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. */ diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp index 9b8abce..8893f75 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.cpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp @@ -129,14 +129,6 @@ void GameSingleton::setup_logger() { }); } -GameManager const& GameSingleton::get_game_manager() const { - return game_manager; -} - -Dataloader const& GameSingleton::get_dataloader() const { - return dataloader; -} - Error GameSingleton::setup_game(int32_t bookmark_index) { Bookmark const* bookmark = game_manager.get_history_manager().get_bookmark_manager().get_bookmark_by_index(bookmark_index); ERR_FAIL_NULL_V_MSG(bookmark, FAILED, vformat("Failed to get bookmark with index: %d", bookmark_index)); diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp index 35f7437..a2b15cd 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.hpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp @@ -1,6 +1,5 @@ #pragma once -#include <godot_cpp/classes/control.hpp> #include <godot_cpp/classes/image_texture.hpp> #include <godot_cpp/classes/texture2d_array.hpp> @@ -14,8 +13,8 @@ namespace OpenVic { static inline GameSingleton* singleton = nullptr; - GameManager game_manager; - Dataloader dataloader; + GameManager PROPERTY(game_manager); + Dataloader PROPERTY(dataloader); godot::Vector2i image_subdivisions; godot::Ref<godot::Texture2DArray> province_shape_texture; @@ -29,7 +28,6 @@ namespace OpenVic { static godot::StringName const& _signal_province_selected(); static godot::StringName const& _signal_clock_state_changed(); - godot::Error _generate_terrain_texture_array(); godot::Error _load_map_images(); godot::Error _load_terrain_variants(); godot::Error _load_flag_images(); @@ -50,9 +48,6 @@ namespace OpenVic { static void setup_logger(); - GameManager const& get_game_manager() const; - Dataloader const& get_dataloader() const; - /* Load the game's defines in compatiblity mode from the filepath * pointing to the defines folder. */ godot::Error load_defines_compatibility_mode(godot::PackedStringArray const& file_paths); diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp index 9cd5db0..c00b64d 100644 --- a/extension/src/openvic-extension/utility/UITools.cpp +++ b/extension/src/openvic-extension/utility/UITools.cpp @@ -15,6 +15,7 @@ #include "openvic-extension/classes/GFXSpriteTexture.hpp" #include "openvic-extension/classes/GFXMaskedFlagTexture.hpp" #include "openvic-extension/classes/GFXPieChartTexture.hpp" +#include "openvic-extension/classes/GUIListBox.hpp" #include "openvic-extension/classes/GUIOverlappingElementsBox.hpp" #include "openvic-extension/classes/GUIScrollbar.hpp" #include "openvic-extension/singletons/AssetManager.hpp" @@ -117,6 +118,11 @@ static bool add_theme_stylebox(Control* control, StringName const& theme_name, R stylebox.instantiate(); ERR_FAIL_NULL_V(stylebox, false); stylebox->set_texture(texture); + + static const StringName changed_signal = "changed"; + static const StringName emit_changed_func = "emit_changed"; + texture->connect(changed_signal, Callable { *stylebox, emit_changed_func }, Object::CONNECT_PERSIST); + control->add_theme_stylebox_override(theme_name, stylebox); return true; }; @@ -145,6 +151,9 @@ static bool generate_icon(generate_gui_args_t&& args) { ret = false; } + const float scale = icon.get_scale(); + godot_texture_rect->set_scale({ scale, scale }); + args.result = godot_texture_rect; } else if (icon.get_sprite()->is_type<GFX::MaskedFlag>()) { TextureRect* godot_texture_rect = nullptr; @@ -168,12 +177,15 @@ static bool generate_icon(generate_gui_args_t&& args) { godot_progress_bar, false, vformat("Failed to create TextureProgressBar for GUI icon %s", icon_name) ); + godot_progress_bar->set_nine_patch_stretch(true); + godot_progress_bar->set_max(1.0); + GFX::ProgressBar const* progress_bar = icon.get_sprite()->cast_to<GFX::ProgressBar>(); Ref<ImageTexture> back_texture; if (!progress_bar->get_back_texture_file().empty()) { const StringName back_texture_file = std_view_to_godot_string_name(progress_bar->get_back_texture_file()); - back_texture = args.asset_manager.get_texture(back_texture_file); + back_texture = args.asset_manager.get_texture(back_texture_file, true); if (back_texture.is_null()) { UtilityFunctions::push_error( "Failed to load progress bar sprite back texture ", back_texture_file, " for GUI icon ", icon_name @@ -205,7 +217,7 @@ static bool generate_icon(generate_gui_args_t&& args) { Ref<ImageTexture> progress_texture; if (!progress_bar->get_progress_texture_file().empty()) { const StringName progress_texture_file = std_view_to_godot_string_name(progress_bar->get_progress_texture_file()); - progress_texture = args.asset_manager.get_texture(progress_texture_file); + progress_texture = args.asset_manager.get_texture(progress_texture_file, true); if (progress_texture.is_null()) { UtilityFunctions::push_error( "Failed to load progress bar sprite progress texture ", progress_texture_file, " for GUI icon ", icon_name @@ -266,9 +278,13 @@ static bool generate_icon(generate_gui_args_t&& args) { } if (args.result != nullptr) { - const float scale = icon.get_scale(); - args.result->set_scale({ scale, scale }); - // TODO - rotation (may have to translate as godot rotates around the top left corner) + const float rotation = icon.get_rotation(); + if (rotation != 0.0f) { + args.result->set_position( + args.result->get_position() - args.result->get_custom_minimum_size().height * Vector2 { sin(rotation), cos(rotation) - 1.0f } + ); + args.result->set_rotation(-rotation); + } } } else { UtilityFunctions::push_error("Null sprite for GUI icon ", icon_name); @@ -470,6 +486,24 @@ static bool generate_overlapping_elements(generate_gui_args_t&& args) { return ret; } +static bool generate_listbox(generate_gui_args_t&& args) { + GUI::ListBox const& listbox = static_cast<GUI::ListBox const&>(args.element); + + const String listbox_name = std_view_to_godot_string(listbox.get_name()); + + GUIListBox* gui_listbox = nullptr; + bool ret = new_control(gui_listbox, listbox, args.name); + ERR_FAIL_NULL_V_MSG(gui_listbox, false, vformat("Failed to create GUIListBox for GUI listbox %s", listbox_name)); + + if (gui_listbox->set_gui_listbox(&listbox) != OK) { + UtilityFunctions::push_error("Error initialising GUIListBox for GUI listbox ", listbox_name); + ret = false; + } + + args.result = gui_listbox; + return ret; +} + template<std::derived_from<GUI::Element> T> requires requires(T const& element) { { element.get_size() } -> std::same_as<fvec2_t>; @@ -498,10 +532,6 @@ static bool generate_placeholder(generate_gui_args_t&& args, Color colour) { return ret; } -static bool generate_listbox(generate_gui_args_t&& args) { - return generate_placeholder<GUI::ListBox>(std::move(args), { 0.0f, 0.0f, 1.0f, 0.3f }); -} - static bool generate_texteditbox(generate_gui_args_t&& args) { return generate_placeholder<GUI::TextEditBox>(std::move(args), { 0.0f, 1.0f, 0.0f, 0.3f }); } |