diff options
Diffstat (limited to 'extension')
9 files changed, 172 insertions, 225 deletions
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation -Subproject e76336cd92639f4ec71088fc4c80aea4c25528c +Subproject 444a27726695478e44e0166e75df1f354b6432d diff --git a/extension/src/openvic-extension/UIAdapter.cpp b/extension/src/openvic-extension/UIAdapter.cpp index d688754..1478a5a 100644 --- a/extension/src/openvic-extension/UIAdapter.cpp +++ b/extension/src/openvic-extension/UIAdapter.cpp @@ -105,7 +105,7 @@ bool GodotGUIBuilder::generate_icon(GUI::Element const& element, AssetManager& a } const StringName texture_file = - std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::MaskedFlag>()->get_texture_file()); + std_view_to_godot_string_name(icon.get_sprite()->cast_to<GFX::MaskedFlag>()->get_overlay_file()); const Ref<ImageTexture> texture = asset_manager.get_texture(texture_file); if (texture.is_valid()) { godot_texture_rect->set_texture(texture); @@ -189,7 +189,7 @@ bool GodotGUIBuilder::generate_button(GUI::Element const& element, AssetManager& } } else if (button.get_sprite()->is_type<GFX::MaskedFlag>()) { texture = asset_manager.get_texture(std_view_to_godot_string_name( - button.get_sprite()->cast_to<GFX::MaskedFlag>()->get_texture_file())); + button.get_sprite()->cast_to<GFX::MaskedFlag>()->get_overlay_file())); if (texture.is_null()) { UtilityFunctions::push_error("Failed to load masked flag sprite for GUI button ", button_name); ret = false; diff --git a/extension/src/openvic-extension/classes/GFXIconTexture.cpp b/extension/src/openvic-extension/classes/GFXIconTexture.cpp index 9edfb1b..57c5d50 100644 --- a/extension/src/openvic-extension/classes/GFXIconTexture.cpp +++ b/extension/src/openvic-extension/classes/GFXIconTexture.cpp @@ -10,13 +10,14 @@ using namespace godot; using namespace OpenVic; +using OpenVic::Utilities::godot_to_std_string; using OpenVic::Utilities::std_view_to_godot_string; using OpenVic::Utilities::std_view_to_godot_string_name; void GFXIconTexture::_bind_methods() { OV_BIND_METHOD(GFXIconTexture::clear); - OV_BIND_METHOD(GFXIconTexture::set_gfx_texture_sprite_name, { "gfx_texture_sprite_name" }, DEFVAL(GFX::NO_FRAMES)); + OV_BIND_METHOD(GFXIconTexture::set_gfx_texture_sprite_name, { "gfx_texture_sprite_name", "icon" }, DEFVAL(GFX::NO_FRAMES)); OV_BIND_METHOD(GFXIconTexture::get_gfx_texture_sprite_name); OV_BIND_METHOD(GFXIconTexture::set_icon_index, { "new_icon_index" }); @@ -32,7 +33,7 @@ GFXIconTexture::GFXIconTexture() Ref<GFXIconTexture> GFXIconTexture::make_gfx_icon_texture(GFX::TextureSprite const* gfx_texture_sprite, GFX::frame_t icon) { Ref<GFXIconTexture> icon_texture; icon_texture.instantiate(); - ERR_FAIL_NULL_V(icon_texture, icon_texture); + ERR_FAIL_NULL_V(icon_texture, nullptr); icon_texture->set_gfx_texture_sprite(gfx_texture_sprite, icon); return icon_texture; } @@ -53,9 +54,11 @@ Error GFXIconTexture::set_gfx_texture_sprite(GFX::TextureSprite const* new_gfx_t } AssetManager* asset_manager = AssetManager::get_singleton(); ERR_FAIL_NULL_V(asset_manager, FAILED); + const StringName texture_file = std_view_to_godot_string_name(new_gfx_texture_sprite->get_texture_file()); const Ref<ImageTexture> texture = asset_manager->get_texture(texture_file); - ERR_FAIL_NULL_V_MSG(texture, FAILED, "Failed to load texture: " + texture_file); + ERR_FAIL_NULL_V_MSG(texture, FAILED, vformat("Failed to load texture: %s", texture_file)); + gfx_texture_sprite = new_gfx_texture_sprite; set_atlas(texture); icon_index = GFX::NO_FRAMES; @@ -70,13 +73,16 @@ Error GFXIconTexture::set_gfx_texture_sprite_name(String const& gfx_texture_spri } GameSingleton* game_singleton = GameSingleton::get_singleton(); ERR_FAIL_NULL_V(game_singleton, FAILED); - GFX::Sprite const* sprite = game_singleton->get_gfx_sprite(gfx_texture_sprite_name); - ERR_FAIL_NULL_V_MSG(sprite, FAILED, "GFX sprite not found: " + gfx_texture_sprite_name); + GFX::Sprite const* sprite = game_singleton->get_game_manager().get_ui_manager().get_sprite_by_identifier( + godot_to_std_string(gfx_texture_sprite_name) + ); + ERR_FAIL_NULL_V_MSG(sprite, FAILED, vformat("GFX sprite not found: %s", gfx_texture_sprite_name)); GFX::TextureSprite const* new_texture_sprite = sprite->cast_to<GFX::TextureSprite>(); ERR_FAIL_NULL_V_MSG( - new_texture_sprite, FAILED, "Invalid type for GFX sprite " + gfx_texture_sprite_name + ": " + - std_view_to_godot_string(sprite->get_type()) + " (expected " + - std_view_to_godot_string(GFX::TextureSprite::get_type_static()) + ")" + new_texture_sprite, FAILED, vformat( + "Invalid type for GFX sprite %s: %s (expected %s)", gfx_texture_sprite_name, + std_view_to_godot_string(sprite->get_type()), std_view_to_godot_string(GFX::TextureSprite::get_type_static()) + ) ); return set_gfx_texture_sprite(new_texture_sprite, icon); } @@ -100,7 +106,7 @@ Error GFXIconTexture::set_icon_index(int32_t new_icon_index) { if (GFX::NO_FRAMES < new_icon_index && new_icon_index <= icon_count) { icon_index = new_icon_index; } else { - icon_index = icon_count; + icon_index = 1; if (new_icon_index > icon_count) { UtilityFunctions::push_warning( "Invalid icon index ", new_icon_index, " out of count ", icon_count, " - defaulting to ", icon_index diff --git a/extension/src/openvic-extension/singletons/AssetManager.cpp b/extension/src/openvic-extension/singletons/AssetManager.cpp index b50cae8..8e9eb41 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.cpp +++ b/extension/src/openvic-extension/singletons/AssetManager.cpp @@ -37,33 +37,47 @@ AssetManager::~AssetManager() { _singleton = nullptr; } -AssetManager::image_asset_map_t::iterator AssetManager::_get_image_asset(StringName path) { - const image_asset_map_t::iterator it = image_assets.find(path); - if (it != image_assets.end()) { - return it; - } +Ref<Image> AssetManager::_load_image(StringName path) { GameSingleton* game_singleton = GameSingleton::get_singleton(); - ERR_FAIL_NULL_V(game_singleton, image_assets.end()); + ERR_FAIL_NULL_V(game_singleton, nullptr); const String lookedup_path = - std_to_godot_string(game_singleton->get_dataloader().lookup_image_file_or_dds(godot_to_std_string(path)).string()); + std_to_godot_string(game_singleton->get_dataloader().lookup_image_file(godot_to_std_string(path)).string()); if (lookedup_path.is_empty()) { UtilityFunctions::push_error("Failed to look up image: ", path); - return image_assets.end(); + return nullptr; } const Ref<Image> image = Utilities::load_godot_image(lookedup_path); if (image.is_null() || image->is_empty()) { UtilityFunctions::push_error("Failed to load image: ", lookedup_path, " (looked up from ", path, ")"); - return image_assets.end(); + return nullptr; + } else { + return image; } - return image_assets.emplace(std::move(path), AssetManager::image_asset_t { image, nullptr }).first; } -Ref<Image> AssetManager::get_image(StringName path) { - const image_asset_map_t::const_iterator it = _get_image_asset(path); +AssetManager::image_asset_map_t::iterator AssetManager::_get_image_asset(StringName path) { + const image_asset_map_t::iterator it = image_assets.find(path); if (it != image_assets.end()) { - return it->second.image; + return it; + } + const Ref<Image> image = _load_image(path); + if (image.is_valid()) { + return image_assets.emplace(std::move(path), AssetManager::image_asset_t { image, nullptr }).first; } else { - return nullptr; + return image_assets.end(); + } +} + +Ref<Image> AssetManager::get_image(StringName path, bool cache) { + if (cache) { + const image_asset_map_t::const_iterator it = _get_image_asset(path); + if (it != image_assets.end()) { + return it->second.image; + } else { + return nullptr; + } + } else { + return _load_image(path); } } diff --git a/extension/src/openvic-extension/singletons/AssetManager.hpp b/extension/src/openvic-extension/singletons/AssetManager.hpp index 7cfc31b..625944d 100644 --- a/extension/src/openvic-extension/singletons/AssetManager.hpp +++ b/extension/src/openvic-extension/singletons/AssetManager.hpp @@ -23,6 +23,7 @@ namespace OpenVic { image_asset_map_t image_assets; font_map_t fonts; + static godot::Ref<godot::Image> _load_image(godot::StringName path); image_asset_map_t::iterator _get_image_asset(godot::StringName path); protected: @@ -35,8 +36,8 @@ namespace OpenVic { ~AssetManager(); /* Search for and load an image at the specified path relative to the game defines, first checking the AssetManager's - * image cache in case it has already been loaded, and returning nullptr if image loading fails. */ - godot::Ref<godot::Image> get_image(godot::StringName path); + * 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 path, bool cache = true); /* 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 diff --git a/extension/src/openvic-extension/singletons/GameSingleton.cpp b/extension/src/openvic-extension/singletons/GameSingleton.cpp index d101a86..db3dd3f 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.cpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.cpp @@ -17,6 +17,9 @@ using OpenVic::Utilities::godot_to_std_string; using OpenVic::Utilities::std_to_godot_string; 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; + void GameSingleton::_bind_methods() { OV_BIND_SMETHOD(setup_logger); @@ -58,15 +61,6 @@ void GameSingleton::_bind_methods() { ADD_SIGNAL(MethodInfo("state_updated")); ADD_SIGNAL(MethodInfo("province_selected", PropertyInfo(Variant::INT, "index"))); - - OV_BIND_SMETHOD( - draw_pie_chart, - { "image", "stopAngles", "colours", "radius", "shadow_displacement", "shadow_tightness", "shadow_radius", - "shadow_thickness", "trim_colour", "trim_size", "gradient_falloff", "gradient_base", "donut", "donut_inner_trim", - "donut_inner_radius" } - ); - - OV_BIND_SMETHOD(load_image, { "path" }); } Control* GameSingleton::generate_gui(String const& gui_file, String const& gui_element) { @@ -90,25 +84,6 @@ Control* GameSingleton::generate_gui(String const& gui_file, String const& gui_e return result; } -GFX::Sprite const* GameSingleton::get_gfx_sprite(String const& sprite_name) const { - return game_manager.get_ui_manager().get_sprite_by_identifier(godot_to_std_string(sprite_name)); -} - -void GameSingleton::draw_pie_chart( - Ref<Image> image, Array const& stopAngles, Array const& colours, float radius, Vector2 shadow_displacement, - float shadow_tightness, float shadow_radius, float shadow_thickness, Color trim_colour, float trim_size, - float gradient_falloff, float gradient_base, bool donut, bool donut_inner_trim, float donut_inner_radius -) { - Utilities::draw_pie_chart( - image, stopAngles, colours, radius, shadow_displacement, shadow_tightness, shadow_radius, shadow_thickness, - trim_colour, trim_size, gradient_falloff, gradient_base, donut, donut_inner_trim, donut_inner_radius - ); -} - -Ref<Image> GameSingleton::load_image(String const& path) { - return Utilities::load_godot_image(path); -} - GameSingleton* GameSingleton::get_singleton() { return singleton; } @@ -143,6 +118,10 @@ void GameSingleton::setup_logger() { }); } +GameManager const& GameSingleton::get_game_manager() const { + return game_manager; +} + Dataloader const& GameSingleton::get_dataloader() const { return dataloader; } @@ -156,6 +135,13 @@ Error GameSingleton::setup_game() { bool ret = game_manager.load_bookmark(&bookmark_manager.get_bookmarks().front()); // TODO - load pop history with the new history system ret &= dataloader.load_pop_history(game_manager, "history/pops/" + game_manager.get_today().to_string()); + for (Province& province : game_manager.get_map().get_provinces()) { + province.set_crime( + game_manager.get_modifier_manager().get_crime_modifier_by_index( + (province.get_index() - 1) % game_manager.get_modifier_manager().get_crime_modifier_count() + ) + ); + } return ERR(ret); } @@ -166,21 +152,32 @@ int32_t GameSingleton::get_province_index_from_uv_coords(Vector2 const& coords) } template<std::derived_from<HasIdentifierAndColour> T> -static Dictionary _distribution_to_dictionary(fixed_point_map_t<T const*> const& dist) { - static const StringName piechart_info_size_key = "size"; - static const StringName piechart_info_colour_key = "colour"; - Dictionary dict; - for (auto const& [key, val] : dist) { - if (key != nullptr) { - Dictionary sub_dict; - sub_dict[piechart_info_size_key] = val.to_float(); - sub_dict[piechart_info_colour_key] = Utilities::to_godot_color(key->get_colour()); - dict[std_view_to_godot_string(key->get_identifier())] = std::move(sub_dict); +static Array _distribution_to_pie_chart_array(fixed_point_map_t<T const*> const& dist) { + using entry_t = std::pair<T const*, fixed_point_t>; + std::vector<entry_t> sorted_dist; + sorted_dist.reserve(dist.size()); + for (entry_t const& entry : dist) { + if (entry.first != nullptr) { + sorted_dist.push_back(entry); } else { - UtilityFunctions::push_error("Null distribution key with value ", val.to_float()); + UtilityFunctions::push_error("Null distribution key with value ", entry.second.to_float()); } } - return dict; + std::sort(sorted_dist.begin(), sorted_dist.end(), [](entry_t const& lhs, entry_t const& rhs) -> bool { + return lhs.second < rhs.second; + }); + static const StringName identifier_key = "identifier"; + static const StringName colour_key = "colour"; + static const StringName weight_key = "weight"; + Array array; + for (auto const& [key, val] : sorted_dist) { + Dictionary sub_dict; + sub_dict[identifier_key] = std_view_to_godot_string(key->get_identifier()); + sub_dict[colour_key] = Utilities::to_godot_color(key->get_colour()); + sub_dict[weight_key] = val.to_float(); + array.push_back(sub_dict); + } + return array; } Dictionary GameSingleton::get_province_info_from_index(int32_t index) const { @@ -223,15 +220,15 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const { ret[province_info_total_population_key] = province->get_total_population(); fixed_point_map_t<PopType const*> const& pop_types = province->get_pop_type_distribution(); if (!pop_types.empty()) { - ret[province_info_pop_types_key] = _distribution_to_dictionary(pop_types); + ret[province_info_pop_types_key] = _distribution_to_pie_chart_array(pop_types); } fixed_point_map_t<Ideology const*> const& ideologies = province->get_ideology_distribution(); if (!ideologies.empty()) { - ret[province_info_pop_ideologies_key] = _distribution_to_dictionary(ideologies); + ret[province_info_pop_ideologies_key] = _distribution_to_pie_chart_array(ideologies); } fixed_point_map_t<Culture const*> const& cultures = province->get_culture_distribution(); if (!cultures.empty()) { - ret[province_info_pop_cultures_key] = _distribution_to_dictionary(cultures); + ret[province_info_pop_cultures_key] = _distribution_to_pie_chart_array(cultures); } static const StringName building_info_building_key = "building"; @@ -292,25 +289,34 @@ Ref<Texture> GameSingleton::get_province_colour_texture() const { } Error GameSingleton::_update_colour_image() { + Map const& map = game_manager.get_map(); + if (!map.provinces_are_locked()) { + UtilityFunctions::push_error("Cannot generate province colour image before provinces are locked!"); + return FAILED; + } + /* We reshape the list of colours into a square, as each texture dimensions cannot exceed 16384. */ + static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(Province::index_t) * 4); + static constexpr int32_t colour_image_width = PROVINCE_INDEX_SQRT * sizeof(Mapmode::base_stripe_t) / sizeof(colour_t); + /* Province count + null province, rounded up to next multiple of PROVINCE_INDEX_SQRT. + * Rearranged from: (map.get_province_count() + 1) + (PROVINCE_INDEX_SQRT - 1) */ + static const int32_t colour_image_height = (map.get_province_count() + PROVINCE_INDEX_SQRT) / PROVINCE_INDEX_SQRT; + static PackedByteArray colour_data_array; - static constexpr int64_t colour_data_array_size = - (static_cast<int64_t>(Province::MAX_INDEX) + 1) * sizeof(Mapmode::base_stripe_t); + static const int64_t colour_data_array_size = colour_image_width * colour_image_height * sizeof(colour_t); colour_data_array.resize(colour_data_array_size); Error err = OK; - if (!game_manager.get_map().generate_mapmode_colours(mapmode_index, colour_data_array.ptrw())) { + if (!map.generate_mapmode_colours(mapmode_index, colour_data_array.ptrw())) { err = FAILED; } - /* We reshape the list of colours into a square, as each texture dimensions cannot exceed 16384. */ - static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(Province::index_t) * 4); if (province_colour_image.is_null()) { province_colour_image.instantiate(); ERR_FAIL_NULL_V_EDMSG(province_colour_image, FAILED, "Failed to create province colour image"); } /* Width is doubled as each province has a (base, stripe) colour pair. */ province_colour_image->set_data( - PROVINCE_INDEX_SQRT * 2, PROVINCE_INDEX_SQRT, false, Image::FORMAT_RGBA8, colour_data_array + colour_image_width, colour_image_height, false, Image::FORMAT_RGBA8, colour_data_array ); if (province_colour_texture.is_null()) { province_colour_texture = ImageTexture::create_from_image(province_colour_image); @@ -363,39 +369,39 @@ Error GameSingleton::expand_building(int32_t province_index, String const& build } void GameSingleton::set_paused(bool paused) { - game_manager.get_clock().isPaused = paused; + game_manager.get_clock().is_paused = paused; } void GameSingleton::toggle_paused() { - game_manager.get_clock().isPaused = !game_manager.get_clock().isPaused; + game_manager.get_clock().is_paused = !game_manager.get_clock().is_paused; } bool GameSingleton::is_paused() const { - return game_manager.get_clock().isPaused; + return game_manager.get_clock().is_paused; } void GameSingleton::increase_speed() { - game_manager.get_clock().increaseSimulationSpeed(); + game_manager.get_clock().increase_simulation_speed(); } void GameSingleton::decrease_speed() { - game_manager.get_clock().decreaseSimulationSpeed(); + game_manager.get_clock().decrease_simulation_speed(); } bool GameSingleton::can_increase_speed() const { - return game_manager.get_clock().canIncreaseSimulationSpeed(); + return game_manager.get_clock().can_increase_simulation_speed(); } bool GameSingleton::can_decrease_speed() const { - return game_manager.get_clock().canDecreaseSimulationSpeed(); + return game_manager.get_clock().can_decrease_simulation_speed(); } String GameSingleton::get_longform_date() const { - return std_to_godot_string(game_manager.get_today().to_string()); + return Utilities::date_to_formatted_string(game_manager.get_today()); } void GameSingleton::try_tick() { - game_manager.get_clock().conditionallyAdvanceGame(); + game_manager.get_clock().conditionally_advance_game(); } Error GameSingleton::_load_map_images(bool flip_vertical) { @@ -411,7 +417,6 @@ Error GameSingleton::_load_map_images(bool flip_vertical) { static_cast<int32_t>(game_manager.get_map().get_height()) }; - static constexpr int32_t GPU_DIM_LIMIT = 0x3FFF; // For each dimension of the image, this finds the small number of equal subdivisions // required get the individual texture dims under GPU_DIM_LIMIT for (int i = 0; i < 2; ++i) { @@ -461,15 +466,25 @@ Error GameSingleton::_load_map_images(bool flip_vertical) { return err; } -Error GameSingleton::_load_terrain_variants_compatibility_mode(String const& terrain_texturesheet_path) { - static constexpr int32_t SHEET_DIMS = 8, SHEET_SIZE = SHEET_DIMS * SHEET_DIMS; +Error GameSingleton::_load_terrain_variants() { + if (terrain_texture.is_valid()) { + UtilityFunctions::push_error("Terrain variants have already been loaded!"); + return FAILED; + } + static const String terrain_texturesheet_path = "map/terrain/texturesheet.tga"; + + AssetManager* asset_manager = AssetManager::get_singleton(); + ERR_FAIL_NULL_V(asset_manager, FAILED); // Load the terrain texture sheet and prepare to slice it up - Ref<Image> terrain_sheet = Utilities::load_godot_image(terrain_texturesheet_path); + Ref<Image> terrain_sheet = asset_manager->get_image(terrain_texturesheet_path); if (terrain_sheet.is_null()) { UtilityFunctions::push_error("Failed to load terrain texture sheet: ", terrain_texturesheet_path); return FAILED; } + + static constexpr int32_t SHEET_DIMS = 8, SHEET_SIZE = SHEET_DIMS * SHEET_DIMS; + terrain_sheet->flip_y(); const int32_t sheet_width = terrain_sheet->get_width(), sheet_height = terrain_sheet->get_height(); if (sheet_width < 1 || sheet_width % SHEET_DIMS != 0 || sheet_width != sheet_height) { @@ -511,8 +526,6 @@ Error GameSingleton::_load_terrain_variants_compatibility_mode(String const& ter } Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& file_paths) { - static constexpr std::string_view terrain_texture_file = "map/terrain/texturesheet.tga"; - Dataloader::path_vector_t roots; for (String const& path : file_paths) { roots.push_back(godot_to_std_string(path)); @@ -528,8 +541,7 @@ Error GameSingleton::load_defines_compatibility_mode(PackedStringArray const& fi UtilityFunctions::push_error("Failed to load defines!"); err = FAILED; } - if (_load_terrain_variants_compatibility_mode(std_to_godot_string( - dataloader.lookup_file(terrain_texture_file).string())) != OK) { + if (_load_terrain_variants() != OK) { UtilityFunctions::push_error("Failed to load terrain variants!"); err = FAILED; } diff --git a/extension/src/openvic-extension/singletons/GameSingleton.hpp b/extension/src/openvic-extension/singletons/GameSingleton.hpp index 1346a5f..108fd28 100644 --- a/extension/src/openvic-extension/singletons/GameSingleton.hpp +++ b/extension/src/openvic-extension/singletons/GameSingleton.hpp @@ -14,7 +14,7 @@ namespace OpenVic { class GameSingleton : public godot::Object { GDCLASS(GameSingleton, godot::Object) - inline static GameSingleton* singleton = nullptr; + static inline GameSingleton* singleton = nullptr; GameManager game_manager; Dataloader dataloader; @@ -27,12 +27,10 @@ namespace OpenVic { godot::Ref<godot::Texture2DArray> terrain_texture; godot::Error _generate_terrain_texture_array(); - godot::Error _load_map_images(bool flip_vertical = false); + godot::Error _load_map_images(bool flip_vertical); + godot::Error _load_terrain_variants(); - godot::Error _load_terrain_variants_compatibility_mode(godot::String const& terrain_texturesheet_path); - - /* Generate the province_colour_texture from the current mapmode. - */ + /* Generate the province_colour_texture from the current mapmode. */ godot::Error _update_colour_image(); void _on_state_updated(); @@ -42,16 +40,6 @@ namespace OpenVic { public: godot::Control* generate_gui(godot::String const& gui_file, godot::String const& gui_element); - GFX::Sprite const* get_gfx_sprite(godot::String const& sprite_name) const; - - static void draw_pie_chart( - godot::Ref<godot::Image> image, godot::Array const& stopAngles, godot::Array const& colours, float radius, - godot::Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness, - godot::Color trim_colour, float trim_size, float gradient_falloff, float gradient_base, bool donut, - bool donut_inner_trim, float donut_inner_radius - ); - - static godot::Ref<godot::Image> load_image(godot::String const& path); static GameSingleton* get_singleton(); @@ -60,50 +48,47 @@ 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. - */ + * pointing to the defines folder. */ godot::Error load_defines_compatibility_mode(godot::PackedStringArray const& file_paths); static godot::String search_for_game_path(godot::String hint_path = {}); /* Post-load/restart game setup - reset the game to post-load state - * and (re)generate starting data, e.g. buildings. - */ + * and (re)generate starting data, e.g. buildings. */ godot::Error setup_game(); int32_t get_province_index_from_uv_coords(godot::Vector2 const& coords) const; /* Get info to display in Province Overview Panel, packaged in - * a Dictionary using StringName constants as keys. - */ + * a Dictionary using StringName constants as keys. */ godot::Dictionary get_province_info_from_index(int32_t index) const; int32_t get_map_width() const; int32_t get_map_height() const; float get_map_aspect_ratio() const; - /* The cosmetic terrain textures stored in a Texture2DArray. - */ + /* The cosmetic terrain textures stored in a Texture2DArray. */ godot::Ref<godot::Texture> get_terrain_texture() const; + /* The flag image corresponding to the requested country / flag_type + * combination, or nullptr if no such flag can be found. */ + godot::Ref<godot::Image> get_flag_image(Country const* country, godot::StringName const& flag_type) const; + /* Number of (vertical, horizontal) subdivisions the province shape image * was split into when making the province_shape_texture to ensure no - * piece had a dimension greater than 16383. - */ + * piece had a dimension greater than 16383. */ godot::Vector2i get_province_shape_image_subdivisions() const; /* The map, encoded in RGB8 with RG representing province index and B representing terrain texture. * To support a wider range of GPUs, the image is divided so that no piece has a dimension - * greater than 16383 and the pieces are stored in a Texture2DArray. - */ + * greater than 16383 and the pieces are stored in a Texture2DArray. */ godot::Ref<godot::Texture> get_province_shape_texture() const; - /* The colour each province should be tinted, arranged in - * index order into a 256x256 RGB8 texture. - */ + /* The base and stripe colours for each province. */ godot::Ref<godot::Texture> get_province_colour_texture() const; int32_t get_mapmode_count() const; diff --git a/extension/src/openvic-extension/utility/Utilities.cpp b/extension/src/openvic-extension/utility/Utilities.cpp index 5940373..e3bcce6 100644 --- a/extension/src/openvic-extension/utility/Utilities.cpp +++ b/extension/src/openvic-extension/utility/Utilities.cpp @@ -4,6 +4,7 @@ #include <godot_cpp/classes/file_access.hpp> #include <godot_cpp/classes/resource_loader.hpp> +#include <godot_cpp/classes/translation_server.hpp> #include <godot_cpp/variant/utility_functions.hpp> #include <gli/convert.hpp> @@ -12,6 +13,24 @@ using namespace godot; using namespace OpenVic; +/* Date formatted like this: "January 1, 1836" (with the month localised, if possible). */ +String Utilities::date_to_formatted_string(Date date) { + std::string const& month_name = date.get_month_name(); + const String day_and_year = " " + String::num_int64(date.get_day()) + ", " + String::num_int64(date.get_year()); + TranslationServer const* server = TranslationServer::get_singleton(); + if (server != nullptr) { + return server->translate(std_to_godot_string_name(month_name)) + day_and_year; + } else { + return std_to_godot_string(month_name) + day_and_year; + } +} + +Ref<Resource> Utilities::load_resource(String const& path, String const& type_hint) { + ResourceLoader* loader = ResourceLoader::get_singleton(); + ERR_FAIL_NULL_V(loader, nullptr); + return loader->load(path, type_hint); +} + static Ref<Image> load_dds_image(String const& path) { gli::texture2d texture { gli::load_dds(Utilities::godot_to_std_string(path)) }; if (texture.empty()) { @@ -42,7 +61,7 @@ static Ref<Image> load_dds_image(String const& path) { PackedByteArray pixels; pixels.resize(size); - /* Index offset used to control whether we are reading */ + /* Index offset used to control whether we are reading */ const size_t rb_idx = 2 * needs_bgr_to_rgb; uint8_t const* ptr = static_cast<uint8_t const*>(texture.data()); for (size_t i = 0; i < size; i += 4) { @@ -72,96 +91,3 @@ Ref<FontFile> Utilities::load_godot_font(String const& fnt_path, Ref<Image> cons } return font; } - -// Get the polar coordinates of a pixel relative to the center -static Vector2 getPolar(Vector2 UVin, Vector2 center) { - Vector2 relcoord = (UVin - center); - float dist = relcoord.length(); - float theta = std::numbers::pi / 2 + atan2(relcoord.y, relcoord.x); - if (theta < 0.0f) { - theta += std::numbers::pi * 2; - } - return { dist, theta }; -} - -// From thebookofshaders, returns a gradient falloff -static inline float parabola(float base, float x, float k) { - return powf(base * x * (1.0 - x), k); -} - -static inline float parabola_shadow(float base, float x) { - return base * x * x; -} - -static Color pie_chart_fragment( - Vector2 UV, float radius, Array const& stopAngles, Array const& colours, Vector2 shadow_displacement, - float shadow_tightness, float shadow_radius, float shadow_thickness, Color trim_colour, float trim_size, - float gradient_falloff, float gradient_base, bool donut, bool donut_inner_trim, float donut_inner_radius -) { - - Vector2 coords = getPolar(UV, { 0.5, 0.5 }); - float dist = coords.x; - float theta = coords.y; - - Vector2 shadow_polar = getPolar(UV, shadow_displacement); - float shadow_peak = radius + (radius - donut_inner_radius) / 2.0; - float shadow_gradient = - shadow_thickness + parabola_shadow(shadow_tightness * -10.0, shadow_polar.x + shadow_peak - shadow_radius); - - // Inner hole of the donut => make it transparent - if (donut && dist <= donut_inner_radius) { - return { 0.1, 0.1, 0.1, shadow_gradient }; - } - // Inner trim - else if (donut && donut_inner_trim && dist <= donut_inner_radius + trim_size) { - return { trim_colour, 1.0 }; - } - // Interior - else if (dist <= radius - trim_size) { - Color col { 1.0f, 0.0f, 0.0f }; - for (int i = 0; i < stopAngles.size(); i++) { - if (theta <= float(stopAngles[i])) { - col = colours[i]; - break; - } - } - float gradient = parabola(gradient_base, dist, gradient_falloff); - return { col * (1.0 - gradient), 1.0 }; - } - // Outer trim - else if (dist <= radius) { - return { trim_colour, 1.0 }; - } - // Outside the circle - else { - return { 0.1, 0.1, 0.1, shadow_gradient }; - } -} - -void Utilities::draw_pie_chart( - Ref<Image> image, Array const& stopAngles, Array const& colours, float radius, Vector2 shadow_displacement, - float shadow_tightness, float shadow_radius, float shadow_thickness, Color trim_colour, float trim_size, - float gradient_falloff, float gradient_base, bool donut, bool donut_inner_trim, float donut_inner_radius -) { - - ERR_FAIL_NULL_EDMSG(image, "Cannot draw pie chart to null image."); - const int32_t width = image->get_width(); - const int32_t height = image->get_height(); - ERR_FAIL_COND_EDMSG(width <= 0 || height <= 0, "Cannot draw pie chart to empty image."); - if (width != height) { - UtilityFunctions::push_warning("Drawing pie chart to non-square image: ", width, "x", height); - } - const int32_t size = std::min(width, height); - for (int32_t y = 0; y < size; ++y) { - for (int32_t x = 0; x < size; ++x) { - image->set_pixel( - x, y, - pie_chart_fragment( - Vector2 { static_cast<float>(x), static_cast<float>(y) } / size, radius, stopAngles, colours, - shadow_displacement, shadow_tightness, shadow_radius, shadow_thickness, trim_colour, trim_size, - gradient_falloff, gradient_base, donut, donut_inner_trim, donut_inner_radius - ) - ); - } - } -} diff --git a/extension/src/openvic-extension/utility/Utilities.hpp b/extension/src/openvic-extension/utility/Utilities.hpp index 752f495..9537dda 100644 --- a/extension/src/openvic-extension/utility/Utilities.hpp +++ b/extension/src/openvic-extension/utility/Utilities.hpp @@ -4,6 +4,7 @@ #include <godot_cpp/classes/image.hpp> #include <openvic-simulation/types/Colour.hpp> +#include <openvic-simulation/types/Date.hpp> #include <openvic-simulation/types/Vector.hpp> #define ERR(x) ((x) ? OK : FAILED) @@ -30,6 +31,8 @@ namespace OpenVic::Utilities { return std_to_godot_string_name(static_cast<std::string>(str)); } + godot::String date_to_formatted_string(Date date); + inline godot::Color to_godot_color(colour_t colour) { return { colour_byte_to_float((colour >> 16) & 0xFF), @@ -46,14 +49,14 @@ namespace OpenVic::Utilities { return { vec.x, vec.y }; } + /* Loads a Resource from a file in the Godot project directory given by a path beginning with "res://". */ + godot::Ref<godot::Resource> load_resource(godot::String const& path, godot::String const& type_hint = {}); + + /* Load an Image from anywhere on the machine, using Godot's image-loading function or, in the case of + * ".dds" image files which Godot is unable to load at runtime, GLI's DDS loading function. */ godot::Ref<godot::Image> load_godot_image(godot::String const& path); + /* Load a Font from anywhere on the machine, combining the ".fnt" file loaded from the given path with the + * already-loaded image file containing the actual characters. */ godot::Ref<godot::FontFile> load_godot_font(godot::String const& fnt_path, godot::Ref<godot::Image> const& image); - - void draw_pie_chart( - godot::Ref<godot::Image> image, godot::Array const& stopAngles, godot::Array const& colours, float radius, - godot::Vector2 shadow_displacement, float shadow_tightness, float shadow_radius, float shadow_thickness, - godot::Color trim_colour, float trim_size, float gradient_falloff, float gradient_base, bool donut, - bool donut_inner_trim, float donut_inner_radius - ); } |