diff options
Diffstat (limited to 'extension/src')
-rw-r--r-- | extension/src/MapSingleton.cpp | 150 | ||||
-rw-r--r-- | extension/src/MapSingleton.hpp | 30 | ||||
-rw-r--r-- | extension/src/openvic2/Map.cpp | 29 | ||||
-rw-r--r-- | extension/src/openvic2/Map.hpp | 28 | ||||
-rw-r--r-- | extension/src/register_types.cpp | 8 |
5 files changed, 245 insertions, 0 deletions
diff --git a/extension/src/MapSingleton.cpp b/extension/src/MapSingleton.cpp new file mode 100644 index 0000000..10b750a --- /dev/null +++ b/extension/src/MapSingleton.cpp @@ -0,0 +1,150 @@ +#include "MapSingleton.hpp" + +#include <godot_cpp/variant/utility_functions.hpp> +#include <godot_cpp/classes/file_access.hpp> +#include <godot_cpp/classes/json.hpp> + +using namespace godot; +using namespace OpenVic2; + +MapSingleton* MapSingleton::singleton = nullptr; + +void MapSingleton::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_province_identifier_file", "file_path"), &MapSingleton::load_province_identifier_file); + ClassDB::bind_method(D_METHOD("load_province_shape_file", "file_path"), &MapSingleton::load_province_shape_file); + ClassDB::bind_method(D_METHOD("get_province_shape_image"), &MapSingleton::get_province_shape_image); +} + +MapSingleton* MapSingleton::get_singleton() { + return singleton; +} + +MapSingleton::MapSingleton() { + ERR_FAIL_COND(singleton != nullptr); + singleton = this; +} + +MapSingleton::~MapSingleton() { + ERR_FAIL_COND(singleton != this); + singleton = nullptr; +} + +Error MapSingleton::load_province_identifier_file(String const& file_path) { + UtilityFunctions::print("Loading identifier file: ", file_path); + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::ModeFlags::READ); + Error err = FileAccess::get_open_error(); + if (err != OK || file.is_null()) { + UtilityFunctions::push_error("Failed to load province identifier file: ", file_path); + return err == OK ? FAILED : err; + } + String json_string = file->get_as_text(); + Ref<JSON> json; + json.instantiate(); + err = json->parse(json_string); + if (err) { + UtilityFunctions::push_error("Failed to parse province identifier file as JSON: ", file_path, + "\nError at line ", json->get_error_line(), ": ", json->get_error_message()); + return err; + } + Variant json_var = json->get_data(); + Variant::Type type = json_var.get_type(); + if (type != Variant::DICTIONARY) { + UtilityFunctions::push_error("Invalid province identifier JSON: root has type ", + Variant::get_type_name(type), " (expected Dictionary)"); + return FAILED; + } + Dictionary prov_dict = json_var; + Array prov_identifiers = prov_dict.keys(); + for (int idx = 0; idx < prov_identifiers.size(); ++idx) { + String const& identifier = prov_identifiers[idx]; + Variant const& colour_var = prov_dict[identifier]; + if (identifier.is_empty()) { + UtilityFunctions::push_error("Empty province identifier with colour: ", colour_var); + err = FAILED; + continue; + } + static const String prov_prefix = "prov_"; + if (!identifier.begins_with(prov_prefix)) + UtilityFunctions::push_warning("Province identifier missing prefix: ", identifier); + type = colour_var.get_type(); + Province::colour_t colour = Province::NULL_COLOUR; + if (type == Variant::ARRAY) { + Array colour_array = colour_var; + if (colour_array.size() == 3) { + for (int jdx = 0; jdx < 3; ++jdx) { + Variant var = colour_array[jdx]; + if (var.get_type() != Variant::FLOAT) { + colour = Province::NULL_COLOUR; + break; + } + double colour_double = var; + if (std::trunc(colour_double) != colour_double) { + colour = Province::NULL_COLOUR; + break; + } + int64_t colour_int = static_cast<int64_t>(colour_double); + if (colour_int < 0 || colour_int > 255) { + colour = Province::NULL_COLOUR; + break; + } + colour = (colour << 8) | colour_int; + } + } + } else if (type == Variant::STRING) { + String colour_string = colour_var; + if (colour_string.is_valid_hex_number()) { + int64_t colour_int = colour_string.hex_to_int(); + if (0 <= colour_int && colour_int <= 0xFFFFFF) + colour = colour_int; + } + } + if (colour == Province::NULL_COLOUR) { + UtilityFunctions::push_error("Invalid province identifier colour for ", identifier, ": ", colour_var); + err = FAILED; + continue; + } + std::string error_message; + if (map.add_province(identifier.utf8().get_data(), colour, error_message)) { + UtilityFunctions::print(error_message.c_str()); + } else { + UtilityFunctions::push_error(error_message.c_str()); + err = FAILED; + } + } + return err; +} + +Error MapSingleton::load_province_shape_file(String const& file_path) { + if (province_shape_image.is_valid()) { + UtilityFunctions::push_error("Province shape file has already been loaded, cannot load: ", file_path); + return FAILED; + } + province_shape_image.instantiate(); + Error err = province_shape_image->load(file_path); + if (err != OK) { + UtilityFunctions::push_error("Failed to load province shape file: ", file_path); + province_shape_image.unref(); + return err; + } + int32_t width = province_shape_image->get_width(); + int32_t height = province_shape_image->get_height(); + if (width < 1 || height < 1) { + UtilityFunctions::push_error("Invalid dimensions (", width, "x", height, ") for province shape file: ", file_path); + err = FAILED; + } + static const Image::Format expected_format = Image::Format::FORMAT_RGB8; + Image::Format format = province_shape_image->get_format(); + if (format != expected_format) { + UtilityFunctions::push_error("Invalid format (", format, ", should be ", expected_format, ") for province shape file: ", file_path); + err = FAILED; + } + if (err) { + province_shape_image.unref(); + return err; + } + return err; +} + +Ref<Image> MapSingleton::get_province_shape_image() const { + return province_shape_image; +} diff --git a/extension/src/MapSingleton.hpp b/extension/src/MapSingleton.hpp new file mode 100644 index 0000000..767ae88 --- /dev/null +++ b/extension/src/MapSingleton.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include <godot_cpp/core/class_db.hpp> +#include <godot_cpp/classes/image.hpp> +#include "openvic2/Map.hpp" + +namespace OpenVic2 { + class MapSingleton : public godot::Object { + + GDCLASS(MapSingleton, godot::Object) + + static MapSingleton* singleton; + + godot::Ref<godot::Image> province_shape_image; + Map map; + + protected: + static void _bind_methods(); + + public: + static MapSingleton* get_singleton(); + + MapSingleton(); + ~MapSingleton(); + + godot::Error load_province_identifier_file(godot::String const& file_path); + godot::Error load_province_shape_file(godot::String const& file_path); + godot::Ref<godot::Image> get_province_shape_image() const; + }; +} diff --git a/extension/src/openvic2/Map.cpp b/extension/src/openvic2/Map.cpp new file mode 100644 index 0000000..c53b86d --- /dev/null +++ b/extension/src/openvic2/Map.cpp @@ -0,0 +1,29 @@ +#include "Map.hpp" + +#include <sstream> +#include <iomanip> + +using namespace OpenVic2; + +std::string Province::to_string() const { + std::ostringstream stream; + stream << "(" << identifier << ", " << std::hex << std::setfill('0') << std::setw(6) << colour << ")"; + return stream.str(); +} + +bool Map::add_province(std::string const& identifier, Province::colour_t colour, std::string& error_message) { + Province new_province = { identifier, colour }; + for (Province const& province : provinces) { + if (province.identifier == identifier) { + error_message = "Duplicate province identifiers: " + province.to_string() + " and " + new_province.to_string(); + return false; + } + if (province.colour == colour) { + error_message = "Duplicate province colours: " + province.to_string() + " and " + new_province.to_string(); + return false; + } + } + provinces.push_back(new_province); + error_message = "Added province: " + new_province.to_string(); + return true; +}
\ No newline at end of file diff --git a/extension/src/openvic2/Map.hpp b/extension/src/openvic2/Map.hpp new file mode 100644 index 0000000..3c9c6de --- /dev/null +++ b/extension/src/openvic2/Map.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include <string> +#include <cstdint> +#include <vector> + +namespace OpenVic2 { + + struct Province { + using colour_t = uint32_t; + + static const colour_t NULL_COLOUR = 0; + + std::string identifier; + colour_t colour; + + std::string to_string() const; + }; + + struct Map { + private: + std::vector<Province> provinces; + + public: + bool add_province(std::string const& identifier, Province::colour_t colour, std::string& error_message); + }; + +} diff --git a/extension/src/register_types.cpp b/extension/src/register_types.cpp index d1613a5..ef5428c 100644 --- a/extension/src/register_types.cpp +++ b/extension/src/register_types.cpp @@ -9,6 +9,7 @@ #include "Simulation.hpp" #include "Checksum.hpp" #include "LoadLocalisation.hpp" +#include "MapSingleton.hpp" using namespace godot; using namespace OpenVic2; @@ -17,6 +18,7 @@ static TestSingleton* _test_singleton; static Simulation* _simulation; static Checksum* _checksum; static LoadLocalisation* _load_localisation; +static MapSingleton* _map_singleton; void initialize_openvic2_types(ModuleInitializationLevel p_level) { @@ -40,6 +42,9 @@ void initialize_openvic2_types(ModuleInitializationLevel p_level) _load_localisation = memnew(LoadLocalisation); Engine::get_singleton()->register_singleton("LoadLocalisation", LoadLocalisation::get_singleton()); + ClassDB::register_class<MapSingleton>(); + _map_singleton = memnew(MapSingleton); + Engine::get_singleton()->register_singleton("MapSingleton", MapSingleton::get_singleton()); } void uninitialize_openvic2_types(ModuleInitializationLevel p_level) { @@ -58,6 +63,9 @@ void uninitialize_openvic2_types(ModuleInitializationLevel p_level) { Engine::get_singleton()->unregister_singleton("LoadLocalisation"); memdelete(_load_localisation); + + Engine::get_singleton()->unregister_singleton("MapSingleton"); + memdelete(_map_singleton); } extern "C" |