diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp index 9f18f084..b490a92a 100644 --- a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp +++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp @@ -220,7 +220,8 @@ Error GFXMaskedFlagTexture::set_flag_country(CountryInstance const* new_flag_cou GovernmentType const* government_type = new_flag_country->get_flag_government_type(); const StringName new_flag_type = government_type != nullptr - ? Utilities::std_to_godot_string(government_type->get_flag_type()) : String {}; + ? StringName { Utilities::std_to_godot_string(government_type->get_flag_type()) } + : StringName {}; return set_flag_country_and_type(new_flag_country->get_country_definition(), new_flag_type); } diff --git a/extension/src/openvic-extension/classes/GUILabel.cpp b/extension/src/openvic-extension/classes/GUILabel.cpp index adcfcce6..a7eacf75 100644 --- a/extension/src/openvic-extension/classes/GUILabel.cpp +++ b/extension/src/openvic-extension/classes/GUILabel.cpp @@ -5,6 +5,7 @@ #include #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-simulation/types/TextFormat.hpp" @@ -13,7 +14,15 @@ using namespace OpenVic; using namespace godot; using namespace OpenVic::Utilities::literals; +GUILabel::string_segment_t::string_segment_t(String&& new_text, Color const& new_colour, real_t new_width) + : text { std::move(new_text) }, colour { new_colour }, width { new_width } {} + +GUILabel::string_segment_t::string_segment_t(String const& new_text, Color const& new_colour, real_t new_width) + : text { new_text }, colour { new_colour }, width { new_width } {} + static constexpr int32_t DEFAULT_FONT_SIZE = 16; +static const Vector2 FLAG_DRAW_DIMS { 23.0_real, 11.0_real }; +static const real_t FLAG_SEGMENT_WIDTH = FLAG_DRAW_DIMS.width + 1.0_real; GUI_TOOLTIP_IMPLEMENTATIONS(GUILabel) @@ -136,6 +145,8 @@ void GUILabel::_notification(int what) { const Vector2 offset = base_offset + adjusted_rect.position + border_size; Vector2 position = offset; + const float ascent = font->get_ascent(font_size), descent = font->get_descent(font_size); + for (line_t const& line : lines) { position.x = offset.x; switch (horizontal_alignment) { @@ -150,30 +161,34 @@ void GUILabel::_notification(int what) { break; } - position.y += font->get_ascent(font_size); + position.y += ascent; for (segment_t const& segment : line.segments) { - string_segment_t const* string_segment = std::get_if(&segment); - - if (string_segment == nullptr) { - if (currency_texture.is_valid()) { - currency_texture->draw( - ci, position - Vector2 { - 1.0_real, static_cast(currency_texture->get_height()) * 0.75_real - } - ); - position.x += currency_texture->get_width(); - } - } else { + if (string_segment_t const* string_segment = std::get_if(&segment)) { font->draw_string( ci, position, string_segment->text, HORIZONTAL_ALIGNMENT_LEFT, -1, font_size, string_segment->colour ); + position.x += string_segment->width; + } else if (flag_segment_t const* flag_segment = std::get_if(&segment)) { + (*flag_segment)->draw_rect(ci, Rect2 { + position - Vector2 { 1.0_real, ascent - 4.0_real }, FLAG_DRAW_DIMS + }, false); + + position.x += FLAG_SEGMENT_WIDTH; + } else if (currency_texture.is_valid()) { + currency_texture->draw( + ci, position - Vector2 { + 1.0_real, static_cast(currency_texture->get_height()) * 0.75_real + } + ); + + position.x += currency_texture->get_width(); } } - position.y += font->get_descent(font_size); + position.y += descent; } } break; @@ -248,34 +263,30 @@ Error GUILabel::set_gui_text(GUI::Text const* new_gui_text, GFX::Font::colour_co Error err = OK; - AssetManager* asset_manager = AssetManager::get_singleton(); - if (asset_manager != nullptr) { - const StringName font_filepath = Utilities::std_to_godot_string(gui_text->get_font()->get_fontname()); - Ref font_file = asset_manager->get_font(font_filepath); - if (font_file.is_valid()) { - if (set_font_file(font_file) != OK) { - err = FAILED; - } - } else { - UtilityFunctions::push_error("Failed to load font \"", font_filepath, "\" for GUILabel"); - err = FAILED; - } + const StringName font_filepath = Utilities::std_to_godot_string(gui_text->get_font()->get_fontname()); - if (!gui_text->get_texture_file().empty()) { - const StringName texture_path = Utilities::std_to_godot_string(gui_text->get_texture_file()); - Ref texture = asset_manager->get_texture(texture_path); - if (texture.is_valid()) { - set_background_texture(texture); - } else { - UtilityFunctions::push_error("Failed to load texture \"", texture_path, "\" for GUILabel ", get_name()); - err = FAILED; - } + AssetManager& asset_manager = *AssetManager::get_singleton(); + Ref font_file = asset_manager.get_font(font_filepath); + if (font_file.is_valid()) { + if (set_font_file(font_file) != OK) { + err = FAILED; } } else { - UtilityFunctions::push_error("Failed to get AssetManager singleton for GUILabel"); + UtilityFunctions::push_error("Failed to load font \"", font_filepath, "\" for GUILabel"); err = FAILED; } + if (!gui_text->get_texture_file().empty()) { + const StringName texture_path = Utilities::std_to_godot_string(gui_text->get_texture_file()); + Ref texture = asset_manager.get_texture(texture_path); + if (texture.is_valid()) { + set_background_texture(texture); + } else { + UtilityFunctions::push_error("Failed to load texture \"", texture_path, "\" for GUILabel ", get_name()); + err = FAILED; + } + } + _queue_line_update(); return err; @@ -382,10 +393,8 @@ Error GUILabel::set_font_size(int32_t new_font_size) { _queue_line_update(); - AssetManager* asset_manager = AssetManager::get_singleton(); - ERR_FAIL_NULL_V_MSG(asset_manager, FAILED, "Failed to get AssetManager singleton for GUILabel"); - - currency_texture = asset_manager->get_currency_texture(font_size); + AssetManager& asset_manager = *AssetManager::get_singleton(); + currency_texture = asset_manager.get_currency_texture(font_size); ERR_FAIL_NULL_V(currency_texture, FAILED); return OK; @@ -443,6 +452,8 @@ real_t GUILabel::get_string_width(String const& string) const { real_t GUILabel::get_segment_width(segment_t const& segment) const { if (string_segment_t const* string_segment = std::get_if(&segment)) { return string_segment->width; + } else if (std::get_if(&segment) != nullptr) { + return FLAG_SEGMENT_WIDTH; } else if (currency_texture.is_valid()) { return currency_texture->get_width(); } else { @@ -628,6 +639,87 @@ void GUILabel::separate_lines( void GUILabel::separate_currency_segments( String const& string, Color const& colour, line_t& line +) const { + int64_t start_pos = 0; + int64_t marker_pos; + + const real_t currency_width = currency_texture.is_valid() ? currency_texture->get_width() : 0.0_real; + + while ((marker_pos = string.find(get_currency_marker(), start_pos)) != -1) { + if (start_pos < marker_pos) { + separate_flag_segments(string.substr(start_pos, marker_pos - start_pos), colour, line); + } + + line.segments.push_back(currency_segment_t {}); + line.width += currency_width; + + start_pos = marker_pos + get_currency_marker().length(); + } + + if (start_pos < string.length()) { + separate_flag_segments(string.substr(start_pos), colour, line); + } +} + +GUILabel::flag_segment_t GUILabel::make_flag_segment(String const& identifier) { + GameSingleton const& game_singleton = *GameSingleton::get_singleton(); + + int32_t country_index = -1; + StringName flag_type; + + InstanceManager const* instance_manager = game_singleton.get_instance_manager(); + + if (instance_manager != nullptr) { + CountryInstance const* country_instance = + instance_manager->get_country_instance_manager().get_country_instance_by_identifier( + Utilities::godot_to_std_string(identifier) + ); + + if (country_instance != nullptr) { + country_index = country_instance->get_country_definition()->get_index(); + + GovernmentType const* government_type = country_instance->get_flag_government_type(); + if (government_type == nullptr) { + government_type = country_instance->get_government_type(); + } + if (government_type != nullptr) { + flag_type = Utilities::std_to_godot_string(government_type->get_flag_type()); + } + } + } else { + CountryDefinition const* country_definition = + game_singleton.get_definition_manager().get_country_definition_manager().get_country_definition_by_identifier( + Utilities::godot_to_std_string(identifier) + ); + + if (country_definition != nullptr) { + country_index = country_definition->get_index(); + } + } + + // If no country with the given identifier can be found, fallback to country index 0 (usually REB) and empty flag type + if (country_index < 0) { + UtilityFunctions::push_warning( + "Failed to find country with identifier \"", identifier, "\" for GUILabel flag segment, falling back to index 0" + ); + country_index = 0; + } + + const Rect2 flag_image_rect = game_singleton.get_flag_sheet_rect(country_index, flag_type); + ERR_FAIL_COND_V(!flag_image_rect.has_area(), {}); + + flag_segment_t flag_segment; + flag_segment.instantiate(); + ERR_FAIL_NULL_V(flag_segment, {}); + + flag_segment->set_region(flag_image_rect); + flag_segment->set_atlas(game_singleton.get_flag_sheet_texture()); + + return flag_segment; +} + +void GUILabel::separate_flag_segments( + String const& string, Color const& colour, line_t& line ) const { const auto push_string_segment = [this, &string, &colour, &line](int64_t start, int64_t end) -> void { String substring = string.substr(start, end - start); @@ -639,17 +731,31 @@ void GUILabel::separate_currency_segments( int64_t start_pos = 0; int64_t marker_pos; - const real_t currency_width = currency_texture.is_valid() ? currency_texture->get_width() : 0.0_real; + static const int64_t FLAG_IDENTIFIER_LENGTH = 3; - while ((marker_pos = string.find(get_currency_marker(), start_pos)) != -1) { + while ((marker_pos = string.find(get_flag_marker(), start_pos)) != -1) { if (start_pos < marker_pos) { push_string_segment(start_pos, marker_pos); } - line.segments.push_back(currency_segment_t {}); - line.width += currency_width; + marker_pos += get_flag_marker().length(); - start_pos = marker_pos + get_currency_marker().length(); + start_pos = marker_pos + FLAG_IDENTIFIER_LENGTH; + + if (start_pos >= string.length()) { + UtilityFunctions::push_warning( + "Incomplete flag identifier \"", string.substr(marker_pos), "\" at end of line \"", string, + "\" in GUILabel text - discarding!" + ); + return; + } + + const flag_segment_t flag_segment = make_flag_segment(string.substr(marker_pos, FLAG_IDENTIFIER_LENGTH)); + + if (flag_segment.is_valid()) { + line.segments.push_back(flag_segment); + line.width += FLAG_SEGMENT_WIDTH; + } } if (start_pos < string.length()) { diff --git a/extension/src/openvic-extension/classes/GUILabel.hpp b/extension/src/openvic-extension/classes/GUILabel.hpp index 4583087a..94422913 100644 --- a/extension/src/openvic-extension/classes/GUILabel.hpp +++ b/extension/src/openvic-extension/classes/GUILabel.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -40,9 +41,14 @@ namespace OpenVic { godot::String text; godot::Color colour; real_t width; + + string_segment_t(godot::String&& new_text, godot::Color const& new_colour, real_t new_width); + string_segment_t(godot::String const& new_text, godot::Color const& new_colour, real_t new_width); + string_segment_t(string_segment_t&&) = default; }; using currency_segment_t = std::monostate; - using segment_t = std::variant; + using flag_segment_t = godot::Ref; + using segment_t = std::variant; struct line_t { std::vector segments; real_t width {}; @@ -121,6 +127,10 @@ namespace OpenVic { void separate_currency_segments( godot::String const& string, godot::Color const& colour, line_t& line ) const; + static flag_segment_t make_flag_segment(godot::String const& identifier); + void separate_flag_segments( + godot::String const& string, godot::Color const& colour, line_t& line + ) const; std::vector wrap_lines(std::vector& unwrapped_lines) const; void adjust_to_content_size(); }; diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.cpp b/extension/src/openvic-extension/singletons/MenuSingleton.cpp index 6ae94508..51654771 100644 --- a/extension/src/openvic-extension/singletons/MenuSingleton.cpp +++ b/extension/src/openvic-extension/singletons/MenuSingleton.cpp @@ -705,7 +705,7 @@ Dictionary MenuSingleton::get_topbar_info() const { } ); for (auto const& [country_identifier, country_name, power] : industrial_power_from_investments) { - industrial_power_tooltip += "\n" + GUILabel::get_flag_marker() + country_identifier + country_name + ": " + industrial_power_tooltip += "\n" + GUILabel::get_flag_marker() + country_identifier + " " + country_name + ": " + GUILabel::get_colour_marker() + "Y" + GUINode::float_to_string_dp(power, 3) + GUILabel::get_colour_marker() + "!"; }