aboutsummaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
author hop311 <hop3114@gmail.com>2023-12-30 15:59:20 +0100
committer hop311 <hop3114@gmail.com>2023-12-30 15:59:20 +0100
commitc0cc6e202c33fb3889d0025b1b04148ae66545f2 (patch)
tree65ef49b0d12cf307269defeb0960f25ea442f777 /extension
parentf2f42d1b65605876a6ec12626be55183b4681b88 (diff)
Added button state textures + block colour progress bars
Diffstat (limited to 'extension')
m---------extension/deps/openvic-simulation0
-rw-r--r--extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp106
-rw-r--r--extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp46
-rw-r--r--extension/src/openvic-extension/classes/GFXIconTexture.cpp33
-rw-r--r--extension/src/openvic-extension/classes/GFXIconTexture.hpp12
-rw-r--r--extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp31
-rw-r--r--extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp13
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.cpp31
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.hpp23
-rw-r--r--extension/src/openvic-extension/classes/GUINode.cpp3
-rw-r--r--extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp9
-rw-r--r--extension/src/openvic-extension/register_types.cpp2
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.cpp52
-rw-r--r--extension/src/openvic-extension/singletons/GameSingleton.hpp7
-rw-r--r--extension/src/openvic-extension/singletons/LoadLocalisation.cpp2
-rw-r--r--extension/src/openvic-extension/singletons/LoadLocalisation.hpp2
-rw-r--r--extension/src/openvic-extension/utility/UITools.cpp118
17 files changed, 421 insertions, 69 deletions
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation
-Subproject bff91f78f9c5339079c10adfbf8232e5159c1a2
+Subproject 0a425fbe05d6138b753c0e4a7c06f06695bde8a
diff --git a/extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp b/extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp
new file mode 100644
index 0000000..e6dff1f
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GFXButtonStateTexture.cpp
@@ -0,0 +1,106 @@
+#include "GFXButtonStateTexture.hpp"
+
+#include "openvic-extension/utility/ClassBindings.hpp"
+
+using namespace OpenVic;
+using namespace godot;
+
+void GFXButtonStateTexture::_bind_methods() {
+ OV_BIND_METHOD(GFXButtonStateTexture::set_button_state, { "new_button_state" });
+ OV_BIND_METHOD(GFXButtonStateTexture::get_button_state);
+
+ OV_BIND_SMETHOD(get_generate_state_image_func_name);
+
+ OV_BIND_SMETHOD(button_state_to_theme_name, { "button_state" });
+ OV_BIND_METHOD(GFXButtonStateTexture::get_button_state_theme);
+
+ OV_BIND_METHOD(GFXButtonStateTexture::generate_state_image, { "source_image" });
+
+ BIND_ENUM_CONSTANT(HOVER);
+ BIND_ENUM_CONSTANT(PRESSED);
+ BIND_ENUM_CONSTANT(DISABLED);
+}
+
+GFXButtonStateTexture::GFXButtonStateTexture() : button_state { HOVER } {}
+
+Ref<GFXButtonStateTexture> GFXButtonStateTexture::make_gfx_button_state_texture(
+ ButtonState button_state, Ref<Image> const& source_image
+) {
+ Ref<GFXButtonStateTexture> button_state_texture;
+ button_state_texture.instantiate();
+ ERR_FAIL_NULL_V(button_state_texture, nullptr);
+ button_state_texture->set_button_state(button_state);
+ if (source_image.is_valid()) {
+ ERR_FAIL_COND_V(button_state_texture->generate_state_image(source_image) != OK, nullptr);
+ }
+ return button_state_texture;
+}
+
+void GFXButtonStateTexture::set_button_state(ButtonState new_button_state) {
+ ERR_FAIL_COND(new_button_state != HOVER && new_button_state != PRESSED && new_button_state != DISABLED);
+ button_state = new_button_state;
+}
+
+Error GFXButtonStateTexture::generate_state_image(Ref<Image> const& source_image) {
+ ERR_FAIL_COND_V(source_image.is_null() || source_image->is_empty(), FAILED);
+ /* Whether we've already set the ImageTexture to an image of the right dimensions and format,
+ * and so can update it without creating and setting a new image, or not. */
+ const bool can_update = state_image.is_valid() && state_image->get_size() == source_image->get_size()
+ && state_image->get_format() == source_image->get_format();
+ if (!can_update) {
+ state_image = Image::create(source_image->get_width(), source_image->get_height(), false, source_image->get_format());
+ ERR_FAIL_NULL_V(state_image, FAILED);
+ }
+
+ static constexpr auto hover_colour = [](Color const& colour) -> Color {
+ return { std::min(colour.r + 0.1f, 1.0f), std::min(colour.g + 0.1f, 1.0f), std::min(colour.b + 0.1f, 1.0f), colour.a };
+ };
+ static constexpr auto pressed_colour = [](Color const& colour) -> Color {
+ return { std::max(colour.r - 0.1f, 0.0f), std::max(colour.g - 0.1f, 0.0f), std::max(colour.b - 0.1f, 0.0f), colour.a };
+ };
+ static constexpr auto disabled_colour = [](Color const& colour) -> Color {
+ const float luma = colour.get_luminance();
+ return { luma, luma, luma, colour.a };
+ };
+
+ const auto colour_func = button_state == HOVER ? hover_colour : button_state == PRESSED ? pressed_colour : disabled_colour;
+
+ for (Vector2i point { 0, 0 }; point.y < state_image->get_height(); ++point.y) {
+ for (point.x = 0; point.x < state_image->get_width(); ++point.x) {
+ state_image->set_pixelv(point, colour_func(source_image->get_pixelv(point)));
+ }
+ }
+
+ if (can_update) {
+ update(state_image);
+ } else {
+ set_image(state_image);
+ }
+ return OK;
+}
+
+StringName const& GFXButtonStateTexture::get_generate_state_image_func_name() {
+ static const StringName generate_state_image_func_name = "generate_state_image";
+ return generate_state_image_func_name;
+}
+
+StringName const& GFXButtonStateTexture::button_state_to_theme_name(ButtonState button_state) {
+ static const StringName theme_name_hover = "hover";
+ static const StringName theme_name_pressed = "pressed";
+ static const StringName theme_name_disabled = "disabled";
+ static const StringName theme_name_error = "";
+ switch (button_state) {
+ case HOVER:
+ return theme_name_hover;
+ case PRESSED:
+ return theme_name_pressed;
+ case DISABLED:
+ return theme_name_disabled;
+ default:
+ return theme_name_error;
+ }
+}
+
+StringName const& GFXButtonStateTexture::get_button_state_theme() const {
+ return button_state_to_theme_name(button_state);
+}
diff --git a/extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp b/extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp
new file mode 100644
index 0000000..32f4087
--- /dev/null
+++ b/extension/src/openvic-extension/classes/GFXButtonStateTexture.hpp
@@ -0,0 +1,46 @@
+#pragma once
+
+#include <godot_cpp/classes/image_texture.hpp>
+
+#include <openvic-simulation/utility/Getters.hpp>
+
+namespace OpenVic {
+ class GFXButtonStateTexture : public godot::ImageTexture {
+ GDCLASS(GFXButtonStateTexture, godot::ImageTexture)
+
+ public:
+ enum ButtonState {
+ HOVER,
+ PRESSED,
+ DISABLED
+ };
+
+ private:
+ ButtonState PROPERTY(button_state);
+ godot::Ref<godot::Image> state_image;
+
+ protected:
+ static void _bind_methods();
+
+ public:
+ GFXButtonStateTexture();
+
+ /* Create a GFXButtonStateTexture using the specified godot::Image. Returns nullptr if generate_state_image fails. */
+ static godot::Ref<GFXButtonStateTexture> make_gfx_button_state_texture(
+ ButtonState button_state, godot::Ref<godot::Image> const& source_image = nullptr
+ );
+
+ /* Set the ButtonState to be generated by this class (calling this does not trigger state image generation). */
+ void set_button_state(ButtonState new_button_state);
+
+ /* Generate a modified version of source_image and update the underlying godot::ImageTexture to use it. */
+ godot::Error generate_state_image(godot::Ref<godot::Image> const& source_image);
+
+ static godot::StringName const& get_generate_state_image_func_name();
+
+ static godot::StringName const& button_state_to_theme_name(ButtonState button_state);
+ godot::StringName const& get_button_state_theme() const;
+ };
+}
+
+VARIANT_ENUM_CAST(OpenVic::GFXButtonStateTexture::ButtonState);
diff --git a/extension/src/openvic-extension/classes/GFXIconTexture.cpp b/extension/src/openvic-extension/classes/GFXIconTexture.cpp
index 895bf6b..5d29c07 100644
--- a/extension/src/openvic-extension/classes/GFXIconTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXIconTexture.cpp
@@ -15,6 +15,11 @@ using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;
+StringName const& GFXIconTexture::_signal_image_updated() {
+ static const StringName signal_image_updated = "image_updated";
+ return signal_image_updated;
+}
+
void GFXIconTexture::_bind_methods() {
OV_BIND_METHOD(GFXIconTexture::clear);
@@ -26,16 +31,32 @@ void GFXIconTexture::_bind_methods() {
OV_BIND_METHOD(GFXIconTexture::get_icon_count);
ADD_PROPERTY(PropertyInfo(Variant::INT, "icon_index"), "set_icon_index", "get_icon_index");
+
+ ADD_SIGNAL(
+ MethodInfo(_signal_image_updated(), PropertyInfo(Variant::OBJECT, "source_image", PROPERTY_HINT_RESOURCE_TYPE, "Image"))
+ );
}
GFXIconTexture::GFXIconTexture()
: gfx_texture_sprite { nullptr }, icon_index { GFX::NO_FRAMES }, icon_count { GFX::NO_FRAMES } {}
-Ref<GFXIconTexture> GFXIconTexture::make_gfx_icon_texture(GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon) {
+Ref<GFXIconTexture> GFXIconTexture::make_gfx_icon_texture(
+ GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon,
+ std::vector<Ref<GFXButtonStateTexture>> const& button_state_textures
+) {
Ref<GFXIconTexture> icon_texture;
icon_texture.instantiate();
ERR_FAIL_NULL_V(icon_texture, nullptr);
- icon_texture->set_gfx_texture_sprite(gfx_texture_sprite, icon);
+
+ for (Ref<GFXButtonStateTexture> const& button_state_texture : button_state_textures) {
+ icon_texture->connect(
+ _signal_image_updated(),
+ Callable { *button_state_texture, GFXButtonStateTexture::get_generate_state_image_func_name() },
+ CONNECT_PERSIST
+ );
+ }
+
+ ERR_FAIL_COND_V(icon_texture->set_gfx_texture_sprite(gfx_texture_sprite, icon) != OK, nullptr);
return icon_texture;
}
@@ -57,9 +78,15 @@ Error GFXIconTexture::set_gfx_texture_sprite(GFX::TextureSprite const* new_gfx_t
ERR_FAIL_NULL_V(asset_manager, FAILED);
const StringName texture_file = std_view_to_godot_string_name(new_gfx_texture_sprite->get_texture_file());
+
+ /* Needed for GFXButtonStateTexture, AssetManager::get_texture will re-use this image from its internal cache. */
+ const Ref<Image> image = asset_manager->get_image(texture_file);
+ ERR_FAIL_NULL_V_MSG(image, FAILED, vformat("Failed to load image: %s", texture_file));
+
const Ref<ImageTexture> texture = asset_manager->get_texture(texture_file);
ERR_FAIL_NULL_V_MSG(texture, FAILED, vformat("Failed to load texture: %s", texture_file));
+ sprite_image = image;
gfx_texture_sprite = new_gfx_texture_sprite;
set_atlas(texture);
icon_index = GFX::NO_FRAMES;
@@ -98,6 +125,7 @@ Error GFXIconTexture::set_icon_index(int32_t new_icon_index) {
}
icon_index = GFX::NO_FRAMES;
set_region({ {}, size });
+ emit_signal(_signal_image_updated(), sprite_image);
return OK;
}
if (GFX::NO_FRAMES < new_icon_index && new_icon_index <= icon_count) {
@@ -111,5 +139,6 @@ Error GFXIconTexture::set_icon_index(int32_t new_icon_index) {
}
}
set_region({ (icon_index - 1) * size.x / icon_count, 0, size.x / icon_count, size.y });
+ emit_signal(_signal_image_updated(), sprite_image->get_region(get_region()));
return OK;
}
diff --git a/extension/src/openvic-extension/classes/GFXIconTexture.hpp b/extension/src/openvic-extension/classes/GFXIconTexture.hpp
index 176d855..06dac34 100644
--- a/extension/src/openvic-extension/classes/GFXIconTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXIconTexture.hpp
@@ -4,6 +4,8 @@
#include <openvic-simulation/interface/GFX.hpp>
+#include "openvic-extension/classes/GFXButtonStateTexture.hpp"
+
namespace OpenVic {
class GFXIconTexture : public godot::AtlasTexture {
GDCLASS(GFXIconTexture, godot::AtlasTexture)
@@ -16,14 +18,22 @@ namespace OpenVic {
GFX::frame_t PROPERTY(icon_index);
GFX::frame_t PROPERTY(icon_count);
+ godot::Ref<godot::Image> sprite_image;
+
+ static godot::StringName const& _signal_image_updated();
+
protected:
static void _bind_methods();
public:
GFXIconTexture();
+ /* Create a GFXIconTexture using the specified GFX::TextureSprite and icon index. Returns nullptr if
+ * set_gfx_texture_sprite fails. Connects the provided GFXButtonStateTextures (if any) to the
+ * GFXIconTexture's image_updated signal. */
static godot::Ref<GFXIconTexture> make_gfx_icon_texture(
- GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon = GFX::NO_FRAMES
+ GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon = GFX::NO_FRAMES,
+ std::vector<godot::Ref<GFXButtonStateTexture>> const& button_state_textures = {}
);
/* Discard the GFX::TextureSprite, atlas texture and icon index. */
diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
index 424be33..0a44e56 100644
--- a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.cpp
@@ -13,15 +13,22 @@ using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;
+StringName const& GFXMaskedFlagTexture::_signal_image_updated() {
+ static const StringName signal_image_updated = "image_updated";
+ return signal_image_updated;
+}
+
Error GFXMaskedFlagTexture::_generate_combined_image() {
ERR_FAIL_NULL_V(overlay_image, FAILED);
- bool can_update = true;
- if (combined_image.is_null() || combined_image->get_size() != overlay_image->get_size()) {
+ /* Whether we've already set the ImageTexture to an image of the right dimensions and format,
+ * and so can update it without creating and setting a new image, or not. */
+ const bool can_update = combined_image.is_valid() && combined_image->get_size() == overlay_image->get_size()
+ && combined_image->get_format() == overlay_image->get_format();
+ if (!can_update) {
combined_image = Image::create(
overlay_image->get_width(), overlay_image->get_height(), false, overlay_image->get_format()
);
ERR_FAIL_NULL_V(combined_image, FAILED);
- can_update = false;
}
if (mask_image.is_valid() && flag_image.is_valid()) {
@@ -55,6 +62,7 @@ Error GFXMaskedFlagTexture::_generate_combined_image() {
} else {
set_image(combined_image);
}
+ emit_signal(_signal_image_updated(), combined_image);
return OK;
}
@@ -68,14 +76,29 @@ void GFXMaskedFlagTexture::_bind_methods() {
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);
+
+ ADD_SIGNAL(
+ MethodInfo(_signal_image_updated(), PropertyInfo(Variant::OBJECT, "source_image", PROPERTY_HINT_RESOURCE_TYPE, "Image"))
+ );
}
GFXMaskedFlagTexture::GFXMaskedFlagTexture() : gfx_masked_flag { nullptr }, flag_country { nullptr } {}
-Ref<GFXMaskedFlagTexture> GFXMaskedFlagTexture::make_gfx_masked_flag_texture(GFX::MaskedFlag const* gfx_masked_flag) {
+Ref<GFXMaskedFlagTexture> GFXMaskedFlagTexture::make_gfx_masked_flag_texture(
+ GFX::MaskedFlag const* gfx_masked_flag, std::vector<Ref<GFXButtonStateTexture>> const& button_state_textures
+) {
Ref<GFXMaskedFlagTexture> masked_flag_texture;
masked_flag_texture.instantiate();
ERR_FAIL_NULL_V(masked_flag_texture, nullptr);
+
+ for (Ref<GFXButtonStateTexture> const& button_state_texture : button_state_textures) {
+ masked_flag_texture->connect(
+ _signal_image_updated(),
+ Callable { *button_state_texture, GFXButtonStateTexture::get_generate_state_image_func_name() },
+ CONNECT_PERSIST
+ );
+ }
+
ERR_FAIL_COND_V(masked_flag_texture->set_gfx_masked_flag(gfx_masked_flag) != OK, nullptr);
return masked_flag_texture;
}
diff --git a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
index f71a1d7..294b842 100644
--- a/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXMaskedFlagTexture.hpp
@@ -5,6 +5,8 @@
#include <openvic-simulation/country/Country.hpp>
#include <openvic-simulation/interface/GFX.hpp>
+#include "openvic-extension/classes/GFXButtonStateTexture.hpp"
+
namespace OpenVic {
class GFXMaskedFlagTexture : public godot::ImageTexture {
GDCLASS(GFXMaskedFlagTexture, godot::ImageTexture)
@@ -15,6 +17,8 @@ namespace OpenVic {
godot::Ref<godot::Image> overlay_image, mask_image, flag_image, combined_image;
+ static godot::StringName const& _signal_image_updated();
+
godot::Error _generate_combined_image();
protected:
@@ -23,9 +27,12 @@ namespace OpenVic {
public:
GFXMaskedFlagTexture();
- /* Create a GFXMaskedFlagTexture using the specific GFX::MaskedFlag.
- * Returns nullptr if setting gfx_masked_flag fails. */
- static godot::Ref<GFXMaskedFlagTexture> make_gfx_masked_flag_texture(GFX::MaskedFlag const* gfx_masked_flag);
+ /* Create a GFXMaskedFlagTexture using the specified GFX::MaskedFlag. Returns nullptr if gfx_masked_flag fails.
+ * Connects the provided GFXButtonStateTextures (if any) to the GFXMaskedFlagTexture's image_updated signal. */
+ static godot::Ref<GFXMaskedFlagTexture> make_gfx_masked_flag_texture(
+ GFX::MaskedFlag const* gfx_masked_flag,
+ std::vector<godot::Ref<GFXButtonStateTexture>> const& button_state_textures = {}
+ );
/* Reset gfx_masked_flag, flag_country and flag_type to nullptr/an empty string, and unreference all images.
* This does not affect the godot::ImageTexture, which cannot be reset to a null or empty image. */
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
index 63deeda..c9a2a72 100644
--- a/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.cpp
@@ -12,6 +12,19 @@ using OpenVic::Utilities::godot_to_std_string;
using OpenVic::Utilities::std_view_to_godot_string;
using OpenVic::Utilities::std_view_to_godot_string_name;
+StringName const& GFXPieChartTexture::_slice_identifier_key() {
+ static StringName const slice_identifier_key = "identifier";
+ return slice_identifier_key;
+}
+StringName const& GFXPieChartTexture::_slice_colour_key() {
+ static StringName const slice_colour_key = "colour";
+ return slice_colour_key;
+}
+StringName const& GFXPieChartTexture::_slice_weight_key() {
+ static StringName const slice_weight_key = "weight";
+ return slice_weight_key;
+}
+
static constexpr float PI = std::numbers::pi_v<float>;
Error GFXPieChartTexture::_generate_pie_chart_image() {
@@ -21,14 +34,13 @@ Error GFXPieChartTexture::_generate_pie_chart_image() {
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 (
- pie_chart_image.is_null() || pie_chart_image->get_width() != pie_chart_size ||
- pie_chart_image->get_height() != pie_chart_size
- ) {
+ /* Whether we've already set the ImageTexture to an image of the right dimensions,
+ * and so can update it without creating and setting a new image, or not. */
+ const bool can_update = pie_chart_image.is_valid() && pie_chart_image->get_width() == pie_chart_size
+ && pie_chart_image->get_height() == pie_chart_size;
+ if (!can_update) {
pie_chart_image = Image::create(pie_chart_size, pie_chart_size, false, Image::FORMAT_RGBA8);
ERR_FAIL_NULL_V(pie_chart_image, FAILED);
- can_update = false;
}
static const Color background_colour { 0.0f, 0.0f, 0.0f, 0.0f };
@@ -74,17 +86,14 @@ Error GFXPieChartTexture::_generate_pie_chart_image() {
}
Error GFXPieChartTexture::set_slices_array(TypedArray<Dictionary> const& new_slices) {
- static const StringName colour_key = "colour";
- static const StringName weight_key = "weight";
-
slices.clear();
total_weight = 0.0f;
for (int32_t i = 0; i < new_slices.size(); ++i) {
Dictionary const& slice_dict = new_slices[i];
ERR_CONTINUE_MSG(
- !slice_dict.has(colour_key) || !slice_dict.has(weight_key), vformat("Invalid slice keys at index %d", i)
+ !slice_dict.has(_slice_colour_key()) || !slice_dict.has(_slice_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]);
+ slice_t slice = std::make_pair(slice_dict[_slice_colour_key()], slice_dict[_slice_weight_key()]);
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));
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
index ad8e751..f8279e4 100644
--- a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
@@ -17,6 +17,10 @@ namespace OpenVic {
float total_weight;
godot::Ref<godot::Image> pie_chart_image;
+ static godot::StringName const& _slice_identifier_key();
+ static godot::StringName const& _slice_colour_key();
+ static godot::StringName const& _slice_weight_key();
+
godot::Error _generate_pie_chart_image();
protected:
@@ -34,34 +38,31 @@ namespace OpenVic {
* 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 namespace godot;
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())
+ entry.first == nullptr, 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;
+ TypedArray<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();
+ Dictionary sub_dict;
+ sub_dict[_slice_identifier_key()] = Utilities::std_view_to_godot_string(key->get_identifier());
+ sub_dict[_slice_colour_key()] = Utilities::to_godot_color(key->get_colour());
+ sub_dict[_slice_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. */
+ /* Create a GFXPieChartTexture using the specified GFX::PieChart. Returns nullptr if gfx_pie_chart fails. */
static godot::Ref<GFXPieChartTexture> make_gfx_pie_chart_texture(GFX::PieChart const* gfx_pie_chart);
/* Reset gfx_pie_chart, flag_country and flag_type to nullptr/an empty string, and unreference all images.
diff --git a/extension/src/openvic-extension/classes/GUINode.cpp b/extension/src/openvic-extension/classes/GUINode.cpp
index 043f65d..89701e0 100644
--- a/extension/src/openvic-extension/classes/GUINode.cpp
+++ b/extension/src/openvic-extension/classes/GUINode.cpp
@@ -49,6 +49,9 @@ void GUINode::_bind_methods() {
}
GUINode::GUINode() {
+ set_anchors_and_offsets_preset(PRESET_FULL_RECT);
+ set_h_grow_direction(GROW_DIRECTION_BOTH);
+ set_v_grow_direction(GROW_DIRECTION_BOTH);
set_mouse_filter(MOUSE_FILTER_IGNORE);
}
diff --git a/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp
index ff88781..921f633 100644
--- a/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp
+++ b/extension/src/openvic-extension/classes/GUIOverlappingElementsBox.cpp
@@ -120,6 +120,15 @@ Error GUIOverlappingElementsBox::set_child_count(int32_t new_count) {
name, child_count, new_count
)
);
+
+ static const StringName set_z_index_func_name = "set_z_index";
+ static const StringName mouse_entered_signal_name = "mouse_entered";
+ static const StringName mouse_exited_signal_name = "mouse_exited";
+
+ /* Move the child element in front of its neighbours when moused-over. */
+ child->connect(mouse_entered_signal_name, Callable { child, set_z_index_func_name }.bind(1), CONNECT_PERSIST);
+ child->connect(mouse_exited_signal_name, Callable { child, set_z_index_func_name }.bind(0), CONNECT_PERSIST);
+
add_child(child);
child_count++;
} while (child_count < new_count);
diff --git a/extension/src/openvic-extension/register_types.cpp b/extension/src/openvic-extension/register_types.cpp
index 278365e..046bc60 100644
--- a/extension/src/openvic-extension/register_types.cpp
+++ b/extension/src/openvic-extension/register_types.cpp
@@ -2,6 +2,7 @@
#include <godot_cpp/classes/engine.hpp>
+#include "openvic-extension/classes/GFXButtonStateTexture.hpp"
#include "openvic-extension/classes/GFXIconTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
@@ -43,6 +44,7 @@ void initialize_openvic_types(ModuleInitializationLevel p_level) {
Engine::get_singleton()->register_singleton("AssetManager", AssetManager::get_singleton());
ClassDB::register_class<MapMesh>();
+ ClassDB::register_class<GFXButtonStateTexture>();
ClassDB::register_class<GFXIconTexture>();
ClassDB::register_class<GFXMaskedFlagTexture>();
ClassDB::register_class<GFXPieChartTexture>();
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp
index 7ad0db0..e10efb3 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp
@@ -25,6 +25,21 @@ using OpenVic::Utilities::std_view_to_godot_string;
/* Maximum width or height a GPU texture can have. */
static constexpr int32_t GPU_DIM_LIMIT = 0x3FFF;
+/* StringNames cannot be constructed until Godot has called StringName::setup(),
+ * so we must use these wrapper functions to delay their initialisation. */
+StringName const& GameSingleton::_signal_gamestate_updated() {
+ static const StringName signal_gamestate_updated = "gamestate_updated";
+ return signal_gamestate_updated;
+}
+StringName const& GameSingleton::_signal_province_selected() {
+ static const StringName signal_province_selected = "province_selected";
+ return signal_province_selected;
+}
+StringName const& GameSingleton::_signal_clock_state_changed() {
+ static const StringName signal_clock_state_changed = "clock_state_changed";
+ return signal_clock_state_changed;
+}
+
void GameSingleton::_bind_methods() {
OV_BIND_SMETHOD(setup_logger);
@@ -70,24 +85,31 @@ void GameSingleton::_bind_methods() {
OV_BIND_METHOD(GameSingleton::get_longform_date);
OV_BIND_METHOD(GameSingleton::try_tick);
- ADD_SIGNAL(MethodInfo("state_updated"));
- ADD_SIGNAL(MethodInfo("province_selected", PropertyInfo(Variant::INT, "index")));
+ ADD_SIGNAL(MethodInfo(_signal_gamestate_updated()));
+ ADD_SIGNAL(MethodInfo(_signal_province_selected(), PropertyInfo(Variant::INT, "index")));
+ ADD_SIGNAL(MethodInfo(_signal_clock_state_changed()));
}
GameSingleton* GameSingleton::get_singleton() {
return singleton;
}
-void GameSingleton::_on_state_updated() {
+void GameSingleton::_on_gamestate_updated() {
_update_colour_image();
- emit_signal("state_updated");
+ emit_signal(_signal_gamestate_updated());
+}
+
+void GameSingleton::_on_clock_state_changed() {
+ emit_signal(_signal_clock_state_changed());
}
/* REQUIREMENTS:
* MAP-21, MAP-23, MAP-25, MAP-32, MAP-33, MAP-34
*/
GameSingleton::GameSingleton()
- : game_manager { std::bind(&GameSingleton::_on_state_updated, this) } {
+ : game_manager {
+ std::bind(&GameSingleton::_on_gamestate_updated, this), std::bind(&GameSingleton::_on_clock_state_changed, this)
+ } {
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
}
@@ -360,7 +382,7 @@ int32_t GameSingleton::get_selected_province_index() const {
void GameSingleton::set_selected_province(int32_t index) {
game_manager.get_map().set_selected_province(index);
_update_colour_image();
- emit_signal("province_selected", index);
+ emit_signal(_signal_province_selected(), index);
}
int32_t GameSingleton::get_province_building_count() const {
@@ -411,35 +433,35 @@ String GameSingleton::float_to_formatted_string(float val) {
}
void GameSingleton::set_paused(bool paused) {
- game_manager.get_clock().is_paused = paused;
+ game_manager.get_simulation_clock().set_paused(paused);
}
void GameSingleton::toggle_paused() {
- game_manager.get_clock().is_paused = !game_manager.get_clock().is_paused;
+ game_manager.get_simulation_clock().toggle_paused();
}
bool GameSingleton::is_paused() const {
- return game_manager.get_clock().is_paused;
+ return game_manager.get_simulation_clock().is_paused();
}
void GameSingleton::increase_speed() {
- game_manager.get_clock().increase_simulation_speed();
+ game_manager.get_simulation_clock().increase_simulation_speed();
}
void GameSingleton::decrease_speed() {
- game_manager.get_clock().decrease_simulation_speed();
+ game_manager.get_simulation_clock().decrease_simulation_speed();
}
int32_t GameSingleton::get_speed() const {
- return game_manager.get_clock().get_simulation_speed();
+ return game_manager.get_simulation_clock().get_simulation_speed();
}
bool GameSingleton::can_increase_speed() const {
- return game_manager.get_clock().can_increase_simulation_speed();
+ return game_manager.get_simulation_clock().can_increase_simulation_speed();
}
bool GameSingleton::can_decrease_speed() const {
- return game_manager.get_clock().can_decrease_simulation_speed();
+ return game_manager.get_simulation_clock().can_decrease_simulation_speed();
}
String GameSingleton::get_longform_date() const {
@@ -447,7 +469,7 @@ String GameSingleton::get_longform_date() const {
}
void GameSingleton::try_tick() {
- game_manager.get_clock().conditionally_advance_game();
+ game_manager.get_simulation_clock().conditionally_advance_game();
}
Error GameSingleton::_load_map_images(bool flip_vertical) {
diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp
index 56f3c25..5622688 100644
--- a/extension/src/openvic-extension/singletons/GameSingleton.hpp
+++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp
@@ -25,6 +25,10 @@ namespace OpenVic {
godot::Ref<godot::Texture2DArray> terrain_texture;
std::map<Country const*, std::map<godot::StringName, godot::Ref<godot::Image>>> flag_image_map;
+ static godot::StringName const& _signal_gamestate_updated();
+ 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(bool flip_vertical);
godot::Error _load_terrain_variants();
@@ -32,7 +36,8 @@ namespace OpenVic {
/* Generate the province_colour_texture from the current mapmode. */
godot::Error _update_colour_image();
- void _on_state_updated();
+ void _on_gamestate_updated();
+ void _on_clock_state_changed();
protected:
static void _bind_methods();
diff --git a/extension/src/openvic-extension/singletons/LoadLocalisation.cpp b/extension/src/openvic-extension/singletons/LoadLocalisation.cpp
index 6469820..8860105 100644
--- a/extension/src/openvic-extension/singletons/LoadLocalisation.cpp
+++ b/extension/src/openvic-extension/singletons/LoadLocalisation.cpp
@@ -30,7 +30,7 @@ LoadLocalisation::~LoadLocalisation() {
_singleton = nullptr;
}
-Error LoadLocalisation::_load_file(String const& file_path, Ref<Translation> translation) const {
+Error LoadLocalisation::_load_file(String const& file_path, Ref<Translation> const& translation) const {
const Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::ModeFlags::READ);
Error err = FileAccess::get_open_error();
ERR_FAIL_COND_V_MSG(
diff --git a/extension/src/openvic-extension/singletons/LoadLocalisation.hpp b/extension/src/openvic-extension/singletons/LoadLocalisation.hpp
index aeee076..9e39746 100644
--- a/extension/src/openvic-extension/singletons/LoadLocalisation.hpp
+++ b/extension/src/openvic-extension/singletons/LoadLocalisation.hpp
@@ -12,7 +12,7 @@ namespace OpenVic {
godot::Ref<godot::Translation> translations[Dataloader::_LocaleCount];
- godot::Error _load_file(godot::String const& file_path, godot::Ref<godot::Translation> translation) const;
+ godot::Error _load_file(godot::String const& file_path, godot::Ref<godot::Translation> const& translation) const;
godot::Ref<godot::Translation> _get_translation(godot::String const& locale) const;
protected:
diff --git a/extension/src/openvic-extension/utility/UITools.cpp b/extension/src/openvic-extension/utility/UITools.cpp
index 67ea8f5..409a3ba 100644
--- a/extension/src/openvic-extension/utility/UITools.cpp
+++ b/extension/src/openvic-extension/utility/UITools.cpp
@@ -11,6 +11,7 @@
#include <godot_cpp/classes/theme.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
+#include "openvic-extension/classes/GFXButtonStateTexture.hpp"
#include "openvic-extension/classes/GFXIconTexture.hpp"
#include "openvic-extension/classes/GFXMaskedFlagTexture.hpp"
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
@@ -138,28 +139,75 @@ static bool generate_icon(generate_gui_args_t&& args) {
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 = args.asset_manager.get_texture(back_texture_file);
+ 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);
+ if (back_texture.is_null()) {
+ UtilityFunctions::push_error(
+ "Failed to load progress bar sprite back texture ", back_texture_file, " for GUI icon ", icon_name
+ );
+ ret = false;
+ }
+ }
+ if (back_texture.is_null()) {
+ const Color back_colour = Utilities::to_godot_color(progress_bar->get_back_colour());
+ back_texture = Utilities::make_solid_colour_texture(
+ back_colour, progress_bar->get_size().x, progress_bar->get_size().y
+ );
+ if (back_texture.is_null()) {
+ UtilityFunctions::push_error(
+ "Failed to generate progress bar sprite ", back_colour, " back texture for GUI icon ", icon_name
+ );
+ ret = false;
+ }
+ }
if (back_texture.is_valid()) {
godot_progress_bar->set_under_texture(back_texture);
} else {
- UtilityFunctions::push_error("Failed to load progress bar base sprite ", back_texture_file, " for GUI icon ", icon_name);
+ UtilityFunctions::push_error(
+ "Failed to create and set progress bar sprite back texture for GUI icon ", icon_name
+ );
ret = false;
}
- 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 = args.asset_manager.get_texture(progress_texture_file);
+ 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);
+ if (progress_texture.is_null()) {
+ UtilityFunctions::push_error(
+ "Failed to load progress bar sprite progress texture ", progress_texture_file, " for GUI icon ", icon_name
+ );
+ ret = false;
+ }
+ }
+ if (progress_texture.is_null()) {
+ const Color progress_colour = Utilities::to_godot_color(progress_bar->get_progress_colour());
+ progress_texture = Utilities::make_solid_colour_texture(
+ progress_colour, progress_bar->get_size().x, progress_bar->get_size().y
+ );
+ if (progress_texture.is_null()) {
+ UtilityFunctions::push_error(
+ "Failed to generate progress bar sprite ", progress_colour, " progress texture for GUI icon ", icon_name
+ );
+ ret = false;
+ }
+ }
if (progress_texture.is_valid()) {
godot_progress_bar->set_progress_texture(progress_texture);
} else {
UtilityFunctions::push_error(
- "Failed to load progress bar base sprite ", progress_texture_file, " for GUI icon ", icon_name
+ "Failed to create and set progress bar sprite progress texture for GUI icon ", icon_name
);
ret = false;
}
+ // TODO - work out why progress bar is missing bottom border pixel (e.g. province building expansion bar)
+ godot_progress_bar->set_custom_minimum_size(Utilities::to_godot_fvec2(static_cast<fvec2_t>(progress_bar->get_size())));
+
args.result = godot_progress_bar;
} else if (icon.get_sprite()->is_type<GFX::PieChart>()) {
TextureRect* godot_texture_rect = new_control<TextureRect>(icon, args.name);
@@ -202,23 +250,43 @@ static bool generate_button(generate_gui_args_t&& args) {
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));
+ godot_button->set_mouse_filter(Control::MOUSE_FILTER_PASS);
+
if (!button.get_text().empty()) {
godot_button->set_text(std_view_to_godot_string(button.get_text()));
}
bool ret = true;
+
+ using enum GFXButtonStateTexture::ButtonState;
+ static constexpr std::array<GFXButtonStateTexture::ButtonState, 3> button_states { HOVER, PRESSED, DISABLED };
+
+ std::vector<Ref<GFXButtonStateTexture>> button_state_textures;
+ for (GFXButtonStateTexture::ButtonState button_state : button_states) {
+ Ref<GFXButtonStateTexture> button_state_texture = GFXButtonStateTexture::make_gfx_button_state_texture(button_state);
+ if (button_state_texture.is_valid()) {
+ button_state_textures.push_back(button_state_texture);
+ } else {
+ UtilityFunctions::push_error(
+ "Failed to make ", GFXButtonStateTexture::button_state_to_theme_name(button_state),
+ " GFXButtonStateTexture for GUI button ", button_name
+ );
+ ret = false;
+ }
+ }
+
if (button.get_sprite() != nullptr) {
Ref<Texture2D> texture;
if (button.get_sprite()->is_type<GFX::TextureSprite>()) {
GFX::TextureSprite const* texture_sprite = button.get_sprite()->cast_to<GFX::TextureSprite>();
- texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite);
+ texture = GFXIconTexture::make_gfx_icon_texture(texture_sprite, 0, button_state_textures);
if (texture.is_null()) {
UtilityFunctions::push_error("Failed to make GFXIconTexture for GUI button ", button_name);
ret = false;
}
} else if (button.get_sprite()->is_type<GFX::MaskedFlag>()) {
GFX::MaskedFlag const* masked_flag = button.get_sprite()->cast_to<GFX::MaskedFlag>();
- texture = GFXMaskedFlagTexture::make_gfx_masked_flag_texture(masked_flag);
+ texture = GFXMaskedFlagTexture::make_gfx_masked_flag_texture(masked_flag, button_state_textures);
if (texture.is_null()) {
UtilityFunctions::push_error("Failed to make GFXMaskedFlagTexture for GUI button ", button_name);
ret = false;
@@ -231,15 +299,21 @@ static bool generate_button(generate_gui_args_t&& args) {
if (texture.is_valid()) {
godot_button->set_custom_minimum_size(texture->get_size());
- Ref<StyleBoxTexture> stylebox;
- stylebox.instantiate();
- if (stylebox.is_valid()) {
- static const StringName theme_name_normal = "normal";
+
+ const auto add_stylebox = [godot_button, &button_name](StringName const& theme_name, Ref<Texture2D> const& texture) -> bool {
+ Ref<StyleBoxTexture> stylebox;
+ stylebox.instantiate();
+ ERR_FAIL_NULL_V(stylebox, false);
stylebox->set_texture(texture);
- godot_button->add_theme_stylebox_override(theme_name_normal, stylebox);
- } else {
- UtilityFunctions::push_error("Failed to load instantiate texture stylebox for GUI button ", button_name);
- ret = false;
+ godot_button->add_theme_stylebox_override(theme_name, stylebox);
+ return true;
+ };
+
+ static const StringName theme_name_normal = "normal";
+ ret &= add_stylebox(theme_name_normal, texture);
+
+ for (Ref<GFXButtonStateTexture> const& button_state_texture : button_state_textures) {
+ ret &= add_stylebox(button_state_texture->get_button_state_theme(), button_state_texture);
}
}
} else {
@@ -256,8 +330,14 @@ static bool generate_button(generate_gui_args_t&& args) {
UtilityFunctions::push_error("Failed to load font for GUI button ", button_name);
ret = false;
}
+
+ static const std::vector<StringName> button_font_themes {
+ "font_color", "font_hover_color", "font_hover_pressed_color", "font_pressed_color", "font_disabled_color"
+ };
const Color colour = Utilities::to_godot_color(button.get_font()->get_colour());
- godot_button->add_theme_color_override("font_color", colour);
+ for (StringName const& theme_name : button_font_themes) {
+ godot_button->add_theme_color_override(theme_name, colour);
+ }
}
args.result = godot_button;