aboutsummaryrefslogtreecommitdiff
path: root/extension/src
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2024-08-15 01:13:54 +0200
committer hop311 <hop3114@gmail.com>2024-08-15 01:13:54 +0200
commit7c85ab11e840c281a2499dcc6dd3219c33e7d37f (patch)
tree84460d9e4c3af8656604add874fc9a379a0adc4a /extension/src
parent82b16bcca7c74607a8885b882ec36f5202e7ef70 (diff)
Add GUITextLabel (colour code + currency icon support)
Diffstat (limited to 'extension/src')
-rw-r--r--extension/src/openvic-extension/classes/GUINode.cpp7
-rw-r--r--extension/src/openvic-extension/classes/GUINode.hpp8
-rw-r--r--extension/src/openvic-extension/classes/GUITextLabel.cpp334
-rw-r--r--extension/src/openvic-extension/classes/GUITextLabel.hpp57
-rw-r--r--extension/src/openvic-extension/register_types.cpp10
-rw-r--r--extension/src/openvic-extension/singletons/AssetManager.cpp42
-rw-r--r--extension/src/openvic-extension/singletons/AssetManager.hpp19
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.cpp8
-rw-r--r--extension/src/openvic-extension/utility/UITools.cpp58
-rw-r--r--extension/src/openvic-extension/utility/Utilities.cpp5
-rw-r--r--extension/src/openvic-extension/utility/Utilities.hpp3
11 files changed, 495 insertions, 56 deletions
diff --git a/extension/src/openvic-extension/classes/GUINode.cpp b/extension/src/openvic-extension/classes/GUINode.cpp
index bd8197b..4d7d33e 100644
--- a/extension/src/openvic-extension/classes/GUINode.cpp
+++ b/extension/src/openvic-extension/classes/GUINode.cpp
@@ -40,7 +40,7 @@ using namespace OpenVic;
#define APPLY_TO_CHILD_TYPES(F) \
F(Button, button) \
- F(Label, label) \
+ F(GUITextLabel, gui_text_label) \
F(Panel, panel) \
F(TextureProgressBar, progress_bar) \
F(TextureRect, texture_rect) \
@@ -90,6 +90,7 @@ void GUINode::_bind_methods() {
OV_BIND_SMETHOD(int_to_string_suffixed, { "val" });
OV_BIND_SMETHOD(float_to_string_suffixed, { "val" });
OV_BIND_SMETHOD(float_to_string_dp, { "val", "decimal_places" });
+ OV_BIND_SMETHOD(float_to_string_dp_dynamic, { "val" });
OV_BIND_SMETHOD(format_province_name, { "province_identifier" });
}
@@ -266,6 +267,10 @@ String GUINode::float_to_string_dp(float val, int32_t decimal_places) {
return Utilities::float_to_string_dp(val, decimal_places);
}
+String GUINode::float_to_string_dp_dynamic(float val) {
+ return Utilities::float_to_string_dp_dynamic(val);
+}
+
String GUINode::format_province_name(String const& province_identifier) {
if (!province_identifier.is_empty()) {
static const String province_prefix = "PROV";
diff --git a/extension/src/openvic-extension/classes/GUINode.hpp b/extension/src/openvic-extension/classes/GUINode.hpp
index f8eb62c..6168e7e 100644
--- a/extension/src/openvic-extension/classes/GUINode.hpp
+++ b/extension/src/openvic-extension/classes/GUINode.hpp
@@ -5,7 +5,6 @@
#include <godot_cpp/classes/control.hpp>
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/input_event.hpp>
-#include <godot_cpp/classes/label.hpp>
#include <godot_cpp/classes/line_edit.hpp>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/panel.hpp>
@@ -25,6 +24,7 @@
#include "openvic-extension/classes/GUIListBox.hpp"
#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/GUIScrollbar.hpp"
+#include "openvic-extension/classes/GUITextLabel.hpp"
namespace OpenVic {
class GUINode : public godot::Control {
@@ -52,7 +52,7 @@ namespace OpenVic {
static godot::Vector2 get_gui_position(godot::String const& gui_scene, godot::String const& gui_position);
static godot::Button* get_button_from_node(godot::Node* node);
- static godot::Label* get_label_from_node(godot::Node* node);
+ static GUITextLabel* get_gui_text_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);
@@ -62,7 +62,7 @@ namespace OpenVic {
static godot::LineEdit* get_line_edit_from_node(godot::Node* node);
godot::Button* get_button_from_nodepath(godot::NodePath const& path) const;
- godot::Label* get_label_from_nodepath(godot::NodePath const& path) const;
+ GUITextLabel* get_gui_text_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;
@@ -91,6 +91,8 @@ namespace OpenVic {
static godot::String int_to_string_suffixed(int64_t val);
static godot::String float_to_string_suffixed(float val);
static godot::String float_to_string_dp(float val, int32_t decimal_places);
+ // 3dp if abs(val) < 2 else 2dp if abs(val) < 10 else 1dp
+ static godot::String float_to_string_dp_dynamic(float val);
static godot::String format_province_name(godot::String const& province_identifier);
godot::Ref<godot::BitMap> get_click_mask() const;
diff --git a/extension/src/openvic-extension/classes/GUITextLabel.cpp b/extension/src/openvic-extension/classes/GUITextLabel.cpp
new file mode 100644
index 0000000..5baba70
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUITextLabel.cpp
@@ -0,0 +1,334 @@
+#include "GUITextLabel.hpp"
+
+#include <godot_cpp/classes/style_box_texture.hpp>
+#include <godot_cpp/variant/utility_functions.hpp>
+
+#include "openvic-extension/singletons/AssetManager.hpp"
+#include "openvic-extension/utility/ClassBindings.hpp"
+#include "openvic-extension/utility/Utilities.hpp"
+
+using namespace OpenVic;
+using namespace godot;
+using namespace OpenVic::Utilities::literals;
+
+void GUITextLabel::_bind_methods() {
+ OV_BIND_METHOD(GUITextLabel::clear);
+
+ OV_BIND_METHOD(GUITextLabel::get_text);
+ OV_BIND_METHOD(GUITextLabel::set_text, { "new_text" });
+
+ OV_BIND_METHOD(GUITextLabel::get_substitution_dict);
+ OV_BIND_METHOD(GUITextLabel::add_substitution, { "key", "value" });
+ OV_BIND_METHOD(GUITextLabel::set_substitution_dict, { "new_substitution_dict" });
+ OV_BIND_METHOD(GUITextLabel::clear_substitutions);
+
+ OV_BIND_METHOD(GUITextLabel::get_max_lines);
+ OV_BIND_METHOD(GUITextLabel::set_max_lines, { "new_max_lines" });
+
+ OV_BIND_METHOD(GUITextLabel::get_alignment);
+ OV_BIND_METHOD(GUITextLabel::get_gui_text_name);
+
+ ADD_PROPERTY(PropertyInfo(Variant::STRING, "text"), "set_text", "get_text");
+ ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "substitution_dict"), "set_substitution_dict", "get_substitution_dict");
+}
+
+void GUITextLabel::_notification(int what) {
+ switch (what) {
+ case NOTIFICATION_TRANSLATION_CHANGED: {
+ _queue_update();
+ } break;
+ }
+}
+
+GUITextLabel::GUITextLabel()
+ : gui_text { nullptr }, alignment { HORIZONTAL_ALIGNMENT_LEFT }, font_height { 0.0_real }, colour_codes { nullptr },
+ max_lines { 1 }, update_queued { false } {
+ set_scroll_active(false);
+ set_clip_contents(false);
+ set_autowrap_mode(TextServer::AUTOWRAP_ARBITRARY);
+}
+
+void GUITextLabel::clear() {
+ gui_text = nullptr;
+
+ text = String {};
+ substitution_dict.clear();
+ alignment = HORIZONTAL_ALIGNMENT_LEFT;
+ max_lines = 1;
+
+ static const StringName normal_theme = "normal";
+ remove_theme_stylebox_override(normal_theme);
+
+ _update_font();
+
+ update_queued = false;
+}
+
+Error GUITextLabel::set_gui_text(GUI::Text const* new_gui_text) {
+ if (gui_text == new_gui_text) {
+ return OK;
+ }
+
+ if (new_gui_text == nullptr) {
+ clear();
+ return OK;
+ }
+
+ gui_text = new_gui_text;
+
+ text = Utilities::std_to_godot_string(gui_text->get_text());
+
+ using enum GUI::AlignedElement::format_t;
+ static const ordered_map<GUI::AlignedElement::format_t, HorizontalAlignment> format_map {
+ { left, HORIZONTAL_ALIGNMENT_LEFT },
+ { centre, HORIZONTAL_ALIGNMENT_CENTER },
+ { right, HORIZONTAL_ALIGNMENT_RIGHT }
+ };
+ const decltype(format_map)::const_iterator it = format_map.find(gui_text->get_format());
+ alignment = it != format_map.end() ? it->second : HORIZONTAL_ALIGNMENT_LEFT;
+
+ // TODO - detect max_lines based on gui_text? E.g. from total height vs line height?
+ max_lines = 1;
+
+ static const Vector2 default_padding { 1.0_real, -1.0_real };
+ const Vector2 border_size = Utilities::to_godot_fvec2(gui_text->get_border_size()) + default_padding;
+ const Vector2 max_size = Utilities::to_godot_fvec2(gui_text->get_max_size());
+ set_position(get_position() + border_size);
+ set_custom_minimum_size(max_size - 2 * border_size);
+
+ _queue_update();
+
+ Error err = _update_font();
+
+ if (!gui_text->get_texture_file().empty()) {
+ AssetManager* asset_manager = AssetManager::get_singleton();
+ ERR_FAIL_NULL_V(asset_manager, FAILED);
+
+ const StringName texture_path = Utilities::std_to_godot_string(gui_text->get_texture_file());
+ Ref<ImageTexture> texture = asset_manager->get_texture(texture_path);
+ ERR_FAIL_NULL_V_MSG(
+ texture, FAILED, vformat("Failed to load texture \"%s\" for GUITextLabel %s", texture_path, get_name())
+ );
+
+ Ref<StyleBoxTexture> stylebox;
+ stylebox.instantiate();
+ ERR_FAIL_NULL_V(stylebox, FAILED);
+ stylebox->set_texture(texture);
+
+ stylebox->set_texture_margin(SIDE_LEFT, border_size.x);
+ stylebox->set_texture_margin(SIDE_RIGHT, border_size.x);
+ stylebox->set_texture_margin(SIDE_TOP, border_size.y);
+ stylebox->set_texture_margin(SIDE_BOTTOM, border_size.y);
+
+ static const StringName normal_theme = "normal";
+ add_theme_stylebox_override(normal_theme, stylebox);
+ }
+
+ return err;
+}
+
+String GUITextLabel::get_gui_text_name() const {
+ return gui_text != nullptr ? Utilities::std_to_godot_string(gui_text->get_name()) : String {};
+}
+
+void GUITextLabel::set_text(godot::String const& new_text) {
+ if (text != new_text) {
+ text = new_text;
+ _queue_update();
+ }
+}
+
+void GUITextLabel::add_substitution(String const& key, godot::String const& value) {
+ Variant& existing_value = substitution_dict[key];
+ if (existing_value != value) {
+ existing_value = value;
+ _queue_update();
+ }
+}
+
+void GUITextLabel::set_substitution_dict(godot::Dictionary const& new_substitution_dict) {
+ substitution_dict = new_substitution_dict;
+ _queue_update();
+}
+
+void GUITextLabel::clear_substitutions() {
+ substitution_dict.clear();
+ _queue_update();
+}
+
+void GUITextLabel::set_max_lines(int32_t new_max_lines) {
+ if (new_max_lines != max_lines) {
+ max_lines = new_max_lines;
+ _queue_update();
+ }
+}
+
+Error GUITextLabel::_update_font() {
+ static const StringName font_theme = "normal_font";
+ static const StringName font_color_theme = "default_color";
+
+ if (gui_text == nullptr || gui_text->get_font() == nullptr) {
+ remove_theme_font_override(font_theme);
+ remove_theme_color_override(font_color_theme);
+ font_height = 0.0_real;
+ colour_codes = nullptr;
+
+ return OK;
+ }
+
+ add_theme_color_override(font_color_theme, Utilities::to_godot_color(gui_text->get_font()->get_colour()));
+ colour_codes = &gui_text->get_font()->get_colour_codes();
+
+ AssetManager* asset_manager = AssetManager::get_singleton();
+ ERR_FAIL_NULL_V_MSG(asset_manager, FAILED, "Failed to get AssetManager singleton for GUITextLabel");
+
+ const StringName font_file = Utilities::std_to_godot_string(gui_text->get_font()->get_fontname());
+ const Ref<Font> font = asset_manager->get_font(font_file);
+
+ ERR_FAIL_NULL_V_MSG(font, FAILED, vformat("Failed to load font \"%s\" for GUITextLabel", font_file));
+
+ add_theme_font_override(font_theme, font);
+ font_height = font->get_height();
+
+ return OK;
+}
+
+void GUITextLabel::_queue_update() {
+ if (!update_queued) {
+ update_queued = true;
+
+ callable_mp(this, &GUITextLabel::_update_text).call_deferred();
+ }
+}
+
+void GUITextLabel::_update_text() {
+ static constexpr char SUBSTITUTION_CHAR = '$';
+ static constexpr char COLOUR_CHAR = '\xA7'; // §
+
+ String const& base_text = is_auto_translating() ? tr(text) : text;
+
+ // Remove $keys$ and insert substitutions
+ String substituted_text;
+ {
+ bool substitution_section = false;
+ int64_t section_start = 0;
+ for (int64_t idx = 0; idx < base_text.length(); ++idx) {
+ if (static_cast<char>(base_text[idx]) == SUBSTITUTION_CHAR) {
+ if (section_start < idx) {
+ String section = base_text.substr(section_start, idx - section_start);
+ if (substitution_section) {
+ section = substitution_dict.get(section, String {});
+ }
+ substituted_text += section;
+ }
+ substitution_section = !substitution_section;
+ section_start = idx + 1;
+ }
+ }
+ if (!substitution_section && section_start < base_text.length()) {
+ substituted_text += base_text.substr(section_start);
+ }
+ }
+
+ // Separate out colour codes from displayed test
+ String display_text;
+ colour_instructions_t colour_instructions;
+ {
+ int64_t section_start = 0;
+ for (int64_t idx = 0; idx < substituted_text.length(); ++idx) {
+ if (static_cast<char>(substituted_text[idx]) == COLOUR_CHAR) {
+ if (idx > section_start) {
+ display_text += substituted_text.substr(section_start, idx - section_start);
+ }
+ if (++idx < substituted_text.length() && colour_codes != nullptr) {
+ colour_instructions.emplace_back(display_text.length(), static_cast<char>(substituted_text[idx]));
+ }
+ section_start = idx + 1;
+ }
+ }
+ if (section_start < substituted_text.length()) {
+ display_text += substituted_text.substr(section_start);
+ }
+ }
+
+ _generate_text(display_text, colour_instructions);
+
+ // Trim and add ellipsis if text is too long
+ if (max_lines > 0 && max_lines < get_line_count()) {
+ int32_t visible_character_count = 0;
+ while (
+ visible_character_count < get_total_character_count() &&
+ get_character_line(visible_character_count) < max_lines
+ ) {
+ ++visible_character_count;
+ }
+ static const String ellipsis = "...";
+ if (visible_character_count > ellipsis.length()) {
+ _generate_text(
+ display_text.substr(0, visible_character_count - ellipsis.length()) + ellipsis, colour_instructions
+ );
+ }
+ }
+
+ update_queued = false;
+}
+
+void GUITextLabel::_generate_text(String const& display_text, colour_instructions_t const& colour_instructions) {
+ static constexpr char RESET_COLOUR_CHAR = '!';
+ static constexpr char CURRENCY_CHAR = '\xA4'; // ¤
+
+ AssetManager const* asset_manager = AssetManager::get_singleton();
+ Ref<GFXSpriteTexture> const& currency_texture =
+ asset_manager != nullptr ? asset_manager->get_currency_texture(font_height) : Ref<GFXSpriteTexture> {};
+
+ RichTextLabel::clear();
+
+ push_paragraph(alignment);
+
+ // Add text, applying colour codes and inserting currency symbols
+ {
+ colour_instructions_t::const_iterator colour_it = colour_instructions.begin();
+ bool has_colour = false;
+ int64_t section_start = 0;
+ for (int64_t idx = 0; idx < display_text.length(); ++idx) {
+ if (colour_it != colour_instructions.end() && idx == colour_it->first) {
+ if (section_start < idx) {
+ add_text(display_text.substr(section_start, idx - section_start));
+ section_start = idx;
+ }
+ if (colour_it->second == RESET_COLOUR_CHAR) {
+ if (has_colour) {
+ pop();
+ has_colour = false;
+ }
+ } else {
+ const GFX::Font::colour_codes_t::const_iterator it = colour_codes->find(colour_it->second);
+ if (it != colour_codes->end()) {
+ if (has_colour) {
+ pop();
+ } else {
+ has_colour = true;
+ }
+ push_color(Utilities::to_godot_color(it->second));
+ }
+ }
+ ++colour_it;
+ }
+ if (static_cast<char>(display_text[idx]) == CURRENCY_CHAR) {
+ if (section_start < idx) {
+ add_text(display_text.substr(section_start, idx - section_start));
+ }
+ if (currency_texture.is_valid()) {
+ add_image(currency_texture);
+ } else {
+ static const String currency_fallback = "£";
+ add_text(currency_fallback);
+ }
+ section_start = idx + 1;
+ }
+ }
+ if (section_start < display_text.length()) {
+ add_text(display_text.substr(section_start));
+ }
+ }
+}
diff --git a/extension/src/openvic-extension/classes/GUITextLabel.hpp b/extension/src/openvic-extension/classes/GUITextLabel.hpp
new file mode 100644
index 0000000..b29870e
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GUITextLabel.hpp
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <godot_cpp/classes/rich_text_label.hpp>
+
+#include <openvic-simulation/interface/GUI.hpp>
+
+namespace OpenVic {
+ class GUITextLabel : public godot::RichTextLabel {
+ GDCLASS(GUITextLabel, godot::RichTextLabel)
+
+ using colour_instructions_t = std::vector<std::pair<int64_t, char>>;
+
+ GUI::Text const* PROPERTY(gui_text);
+
+ godot::String PROPERTY(text);
+ godot::Dictionary PROPERTY(substitution_dict);
+ godot::HorizontalAlignment PROPERTY(alignment);
+ real_t font_height;
+ GFX::Font::colour_codes_t const* colour_codes;
+ int32_t PROPERTY(max_lines);
+
+ bool update_queued;
+
+ protected:
+ static void _bind_methods();
+
+ void _notification(int what);
+
+ public:
+ GUITextLabel();
+
+ /* Reset gui_text to nullptr and reset current text. */
+ void clear();
+
+ /* Set the GUI::Text. */
+ godot::Error set_gui_text(GUI::Text const* new_gui_text);
+
+ /* Return the name of the GUI::Text, or an empty String if it's null. */
+ godot::String get_gui_text_name() const;
+
+ void set_text(godot::String const& new_text);
+ void add_substitution(godot::String const& key, godot::String const& value);
+ void set_substitution_dict(godot::Dictionary const& new_substitution_dict);
+ void clear_substitutions();
+
+ /* Any text going over this number of lines will be trimmed and replaced with an ellipsis.
+ * Values less than 1 indicate no limit. Default value: 1. */
+ void set_max_lines(int32_t new_max_lines);
+
+ private:
+ godot::Error _update_font();
+
+ void _queue_update();
+ void _update_text();
+ void _generate_text(godot::String const& display_text, colour_instructions_t const& colour_instructions);
+ };
+}
diff --git a/extension/src/openvic-extension/register_types.cpp b/extension/src/openvic-extension/register_types.cpp
index 0b9d779..eb58cb0 100644
--- a/extension/src/openvic-extension/register_types.cpp
+++ b/extension/src/openvic-extension/register_types.cpp
@@ -10,6 +10,7 @@
#include "openvic-extension/classes/GUINode.hpp"
#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/GUIScrollbar.hpp"
+#include "openvic-extension/classes/GUITextLabel.hpp"
#include "openvic-extension/classes/MapMesh.hpp"
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/Checksum.hpp"
@@ -43,10 +44,6 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) {
_load_localisation = memnew(LoadLocalisation);
Engine::get_singleton()->register_singleton("LoadLocalisation", LoadLocalisation::get_singleton());
- ClassDB::register_class<SoundSingleton>();
- _sound_singleton = memnew(SoundSingleton);
- Engine::get_singleton()->register_singleton("SoundSingleton", SoundSingleton::get_singleton());
-
ClassDB::register_class<GameSingleton>();
_game_singleton = memnew(GameSingleton);
Engine::get_singleton()->register_singleton("GameSingleton", GameSingleton::get_singleton());
@@ -63,6 +60,10 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) {
_asset_manager_singleton = memnew(AssetManager);
Engine::get_singleton()->register_singleton("AssetManager", AssetManager::get_singleton());
+ ClassDB::register_class<SoundSingleton>();
+ _sound_singleton = memnew(SoundSingleton);
+ Engine::get_singleton()->register_singleton("SoundSingleton", SoundSingleton::get_singleton());
+
ClassDB::register_class<MapMesh>();
ClassDB::register_abstract_class<GFXCorneredTileSupportingTexture>();
@@ -79,6 +80,7 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) {
ClassDB::register_class<GUINode>();
ClassDB::register_class<GUIOverlappingElementsBox>();
ClassDB::register_class<GUIScrollbar>();
+ ClassDB::register_class<GUITextLabel>();
}
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 eec1ada..d17edd0 100644
--- a/extension/src/openvic-extension/singletons/AssetManager.cpp
+++ b/extension/src/openvic-extension/singletons/AssetManager.cpp
@@ -4,6 +4,7 @@
#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;
@@ -119,7 +120,7 @@ Ref<ImageTexture> AssetManager::get_texture(StringName const& path, LoadFlags lo
}
}
-Ref<Font> AssetManager::get_font(StringName const& name) {
+Ref<FontFile> AssetManager::get_font(StringName const& name) {
const font_map_t::const_iterator it = fonts.find(name);
if (it != fonts.end()) {
ERR_FAIL_NULL_V_MSG(it->second, nullptr, vformat("Failed to load font previously: %s", name));
@@ -152,7 +153,7 @@ Ref<Font> AssetManager::get_font(StringName const& name) {
ERR_FAIL_V_MSG(nullptr, vformat("Failed to look up font: %s", font_path));
}
- const Ref<Font> font = Utilities::load_godot_font(lookedup_font_path, image);
+ const Ref<FontFile> font = Utilities::load_godot_font(lookedup_font_path, image);
if (font.is_null()) {
fonts.emplace(name, nullptr);
@@ -165,3 +166,40 @@ Ref<Font> AssetManager::get_font(StringName const& name) {
fonts.emplace(name, font);
return font;
}
+
+Error AssetManager::preload_textures() {
+ static const String currency_sprite_big = "GFX_tooltip_money_big";
+ static const String currency_sprite_medium = "GFX_tooltip_money_small";
+ static const String currency_sprite_small = "GFX_tooltip_money";
+
+ constexpr auto load = [](String const& sprite_name, Ref<GFXSpriteTexture>& texture) -> bool {
+ GFX::Sprite const* sprite = UITools::get_gfx_sprite(sprite_name);
+ ERR_FAIL_NULL_V(sprite, false);
+
+ GFX::IconTextureSprite const* icon_sprite = sprite->cast_to<GFX::IconTextureSprite>();
+ ERR_FAIL_NULL_V(icon_sprite, false);
+
+ texture = GFXSpriteTexture::make_gfx_sprite_texture(icon_sprite);
+ ERR_FAIL_NULL_V(texture, false);
+
+ return true;
+ };
+
+ bool ret = true;
+
+ ret &= load(currency_sprite_big, currency_texture_big);
+ ret &= load(currency_sprite_medium, currency_texture_medium);
+ ret &= load(currency_sprite_small, currency_texture_small);
+
+ return ERR(ret);
+}
+
+Ref<GFXSpriteTexture> const& AssetManager::get_currency_texture(real_t height) const {
+ if (height > currency_texture_big->get_height()) {
+ return currency_texture_big;
+ } else if (height > currency_texture_medium->get_height()) {
+ return currency_texture_medium;
+ } else {
+ return currency_texture_small;
+ }
+}
diff --git a/extension/src/openvic-extension/singletons/AssetManager.hpp b/extension/src/openvic-extension/singletons/AssetManager.hpp
index 0856d05..deca309 100644
--- a/extension/src/openvic-extension/singletons/AssetManager.hpp
+++ b/extension/src/openvic-extension/singletons/AssetManager.hpp
@@ -1,12 +1,14 @@
#pragma once
#include <godot_cpp/classes/atlas_texture.hpp>
-#include <godot_cpp/classes/font.hpp>
+#include <godot_cpp/classes/font_file.hpp>
#include <godot_cpp/classes/image_texture.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <openvic-simulation/interface/GFXSprite.hpp>
+#include "openvic-extension/classes/GFXSpriteTexture.hpp"
+
namespace OpenVic {
class AssetManager : public godot::Object {
GDCLASS(AssetManager, godot::Object)
@@ -32,7 +34,7 @@ namespace OpenVic {
};
/* deque_ordered_map to avoid the need to reallocate. */
using image_asset_map_t = deque_ordered_map<godot::StringName, image_asset_t>;
- using font_map_t = deque_ordered_map<godot::StringName, godot::Ref<godot::Font>>;
+ using font_map_t = deque_ordered_map<godot::StringName, godot::Ref<godot::FontFile>>;
image_asset_map_t image_assets;
font_map_t fonts;
@@ -68,7 +70,18 @@ namespace OpenVic {
/* 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 const& name);
+ godot::Ref<godot::FontFile> get_font(godot::StringName const& name);
+
+ private:
+ godot::Ref<GFXSpriteTexture> PROPERTY(currency_texture_big); // 32x32
+ godot::Ref<GFXSpriteTexture> PROPERTY(currency_texture_medium); // 24x24
+ godot::Ref<GFXSpriteTexture> PROPERTY(currency_texture_small); // 16x16
+
+ public:
+ godot::Error preload_textures();
+
+ /* Get the largest currency texture with height less than the specified font height. */
+ godot::Ref<GFXSpriteTexture> const& get_currency_texture(real_t height) const;
};
}
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp
index 5268789..13324d0 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp
@@ -597,7 +597,7 @@ Error GameSingleton::set_compatibility_mode_roots(PackedStringArray const& file_
Error GameSingleton::load_defines_compatibility_mode() {
Error err = OK;
auto add_message = std::bind_front(&LoadLocalisation::add_message, LoadLocalisation::get_singleton());
-
+
if (!game_manager.load_definitions(add_message)) {
UtilityFunctions::push_error("Failed to load defines!");
err = FAILED;
@@ -616,6 +616,12 @@ Error GameSingleton::load_defines_compatibility_mode() {
err = FAILED;
}
+ AssetManager* asset_manager = AssetManager::get_singleton();
+ if (asset_manager == nullptr || asset_manager->preload_textures() != OK) {
+ UtilityFunctions::push_error("Failed to preload assets!");
+ err = FAILED;
+ }
+
return err;
}
diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp
index cffab22..f972681 100644
--- a/extension/src/openvic-extension/utility/UITools.cpp
+++ b/extension/src/openvic-extension/utility/UITools.cpp
@@ -2,7 +2,6 @@
#include <godot_cpp/classes/button.hpp>
#include <godot_cpp/classes/color_rect.hpp>
-#include <godot_cpp/classes/label.hpp>
#include <godot_cpp/classes/line_edit.hpp>
#include <godot_cpp/classes/panel.hpp>
#include <godot_cpp/classes/style_box_empty.hpp>
@@ -19,6 +18,7 @@
#include "openvic-extension/classes/GUIListBox.hpp"
#include "openvic-extension/classes/GUIOverlappingElementsBox.hpp"
#include "openvic-extension/classes/GUIScrollbar.hpp"
+#include "openvic-extension/classes/GUITextLabel.hpp"
#include "openvic-extension/singletons/AssetManager.hpp"
#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/utility/Utilities.hpp"
@@ -500,55 +500,22 @@ static bool generate_checkbox(generate_gui_args_t&& args) {
}
static bool generate_text(generate_gui_args_t&& args) {
- using namespace OpenVic::Utilities::literals;
-
GUI::Text const& text = static_cast<GUI::Text const&>(args.element);
const String text_name = Utilities::std_to_godot_string(text.get_name());
- Label* godot_label = nullptr;
- bool ret = new_control(godot_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(Utilities::std_to_godot_string(text.get_text()));
+ GUITextLabel* text_label = nullptr;
+ bool ret = new_control(text_label, text, args.name);
+ ERR_FAIL_NULL_V_MSG(text_label, false, vformat("Failed to create GUITextLabel for GUI text %s", text_name));
- static const Vector2 default_padding { 1.0_real, 0.0_real };
- const Vector2 border_size = Utilities::to_godot_fvec2(text.get_border_size()) + default_padding;
- const Vector2 max_size = Utilities::to_godot_fvec2(text.get_max_size());
- godot_label->set_position(godot_label->get_position() + border_size);
- godot_label->set_custom_minimum_size(max_size - 2 * border_size);
+ text_label->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- using enum GUI::AlignedElement::format_t;
- static const ordered_map<GUI::AlignedElement::format_t, HorizontalAlignment> format_map {
- { left, HORIZONTAL_ALIGNMENT_LEFT },
- { centre, HORIZONTAL_ALIGNMENT_CENTER },
- { right, HORIZONTAL_ALIGNMENT_RIGHT }
- };
-
- const decltype(format_map)::const_iterator it = format_map.find(text.get_format());
- if (it != format_map.end()) {
- godot_label->set_horizontal_alignment(it->second);
- } else {
- UtilityFunctions::push_error("Invalid text format (horizontal alignment) for GUI text ", text_name);
+ if (text_label->set_gui_text(&text) != OK) {
+ UtilityFunctions::push_error("Error initialising GUITextLabel for GUI text ", text_name);
ret = false;
}
- if (text.get_font() != nullptr) {
- const StringName font_file = Utilities::std_to_godot_string(text.get_font()->get_fontname());
- const Ref<Font> font = args.asset_manager.get_font(font_file);
- if (font.is_valid()) {
- static const StringName font_theme = "font";
- godot_label->add_theme_font_override(font_theme, font);
- } else {
- UtilityFunctions::push_error("Failed to load font \"", font_file, "\" for GUI text ", text_name);
- ret = false;
- }
- const Color colour = Utilities::to_godot_color(text.get_font()->get_colour());
- static const StringName font_color_theme = "font_color";
- godot_label->add_theme_color_override(font_color_theme, colour);
- }
-
- args.result = godot_label;
+ args.result = text_label;
return ret;
}
@@ -564,7 +531,14 @@ static bool generate_overlapping_elements(generate_gui_args_t&& args) {
vformat("Failed to create GUIOverlappingElementsBox for GUI overlapping elements %s", overlapping_elements_name)
);
box->set_mouse_filter(Control::MOUSE_FILTER_IGNORE);
- ret &= box->set_gui_overlapping_elements_box(&overlapping_elements) == OK;
+
+ if (box->set_gui_overlapping_elements_box(&overlapping_elements) != OK) {
+ UtilityFunctions::push_error(
+ "Error initialising GUIOverlappingElementsBox for GUI overlapping elements ", overlapping_elements_name
+ );
+ ret = false;
+ }
+
args.result = box;
return ret;
}
diff --git a/extension/src/openvic-extension/utility/Utilities.cpp b/extension/src/openvic-extension/utility/Utilities.cpp
index 4a774a7..1fcdea8 100644
--- a/extension/src/openvic-extension/utility/Utilities.cpp
+++ b/extension/src/openvic-extension/utility/Utilities.cpp
@@ -63,6 +63,11 @@ String Utilities::float_to_string_dp(float val, int32_t decimal_places) {
return String::num(val, decimal_places).pad_decimals(decimal_places);
}
+String Utilities::float_to_string_dp_dynamic(float val) {
+ const float abs_val = std::abs(val);
+ return float_to_string_dp(val, abs_val < 2.0f ? 3 : abs_val < 10.0f ? 2 : 1);
+}
+
/* Date formatted like this: "January 1, 1836" (with the month localised, if possible). */
String Utilities::date_to_formatted_string(Date date) {
const String month_name = Utilities::std_to_godot_string(date.get_month_name());
diff --git a/extension/src/openvic-extension/utility/Utilities.hpp b/extension/src/openvic-extension/utility/Utilities.hpp
index 49314ca..48be1e0 100644
--- a/extension/src/openvic-extension/utility/Utilities.hpp
+++ b/extension/src/openvic-extension/utility/Utilities.hpp
@@ -27,6 +27,9 @@ namespace OpenVic::Utilities {
godot::String float_to_string_dp(float val, int32_t decimal_places);
+ // 3dp if abs(val) < 2 else 2dp if abs(val) < 10 else 1dp
+ godot::String float_to_string_dp_dynamic(float val);
+
constexpr real_t to_real_t(std::floating_point auto val) {
return static_cast<real_t>(val);
}