aboutsummaryrefslogtreecommitdiff
path: root/extension
diff options
context:
space:
mode:
author Hop311 <hop3114@gmail.com>2023-05-22 14:57:00 +0200
committer Hop311 <hop3114@gmail.com>2023-05-22 14:57:00 +0200
commitbd9b0206bcfc8848c865055a933d1e207521ac8e (patch)
tree3636f015f1fa3fe397778c240293d6c27ca41562 /extension
parent30fa7d74ddcfd4e64a5526995a392f7b51feb869 (diff)
GD/std string cleanup + separate data loading cpp
Diffstat (limited to 'extension')
-rw-r--r--extension/src/GameSingleton.cpp520
-rw-r--r--extension/src/GameSingleton.hpp24
-rw-r--r--extension/src/LoadGameOpenVic.cpp450
-rw-r--r--extension/src/LoadLocalisation.cpp61
-rw-r--r--extension/src/Utilities.hpp22
-rw-r--r--extension/src/register_types.h2
6 files changed, 562 insertions, 517 deletions
diff --git a/extension/src/GameSingleton.cpp b/extension/src/GameSingleton.cpp
index e067517..6b3456a 100644
--- a/extension/src/GameSingleton.cpp
+++ b/extension/src/GameSingleton.cpp
@@ -1,16 +1,14 @@
#include "GameSingleton.hpp"
-#include <godot_cpp/classes/file_access.hpp>
-#include <godot_cpp/classes/json.hpp>
#include <godot_cpp/variant/utility_functions.hpp>
#include "openvic/Logger.hpp"
+#include "Utilities.hpp"
+
using namespace godot;
using namespace OpenVic;
-#define ERR(x) ((x) == SUCCESS ? OK : FAILED)
-
GameSingleton* GameSingleton::singleton = nullptr;
void GameSingleton::_bind_methods() {
@@ -27,7 +25,6 @@ void GameSingleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("get_province_shape_texture"), &GameSingleton::get_province_shape_texture);
ClassDB::bind_method(D_METHOD("get_province_colour_texture"), &GameSingleton::get_province_colour_texture);
- ClassDB::bind_method(D_METHOD("update_colour_image"), &GameSingleton::update_colour_image);
ClassDB::bind_method(D_METHOD("get_mapmode_count"), &GameSingleton::get_mapmode_count);
ClassDB::bind_method(D_METHOD("get_mapmode_identifier", "index"), &GameSingleton::get_mapmode_identifier);
ClassDB::bind_method(D_METHOD("set_mapmode", "identifier"), &GameSingleton::set_mapmode);
@@ -54,6 +51,7 @@ void GameSingleton::_bind_methods() {
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_water_province_file_key"), &GameSingleton::get_water_province_file_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_region_file_key"), &GameSingleton::get_region_file_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_terrain_variant_file_key"), &GameSingleton::get_terrain_variant_file_key);
+ ClassDB::bind_static_method("GameSingleton", D_METHOD("get_terrain_texture_dir_key"), &GameSingleton::get_terrain_texture_dir_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_province_image_file_key"), &GameSingleton::get_province_image_file_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_terrain_image_file_key"), &GameSingleton::get_terrain_image_file_key);
ClassDB::bind_static_method("GameSingleton", D_METHOD("get_goods_file_key"), &GameSingleton::get_goods_file_key);
@@ -77,9 +75,8 @@ GameSingleton* GameSingleton::get_singleton() {
return singleton;
}
-
void GameSingleton::_on_state_updated() {
- update_colour_image();
+ _update_colour_image();
emit_signal("state_updated");
}
@@ -91,13 +88,17 @@ GameSingleton::GameSingleton() : game_manager { [this]() { _on_state_updated();
ERR_FAIL_COND(singleton != nullptr);
singleton = this;
- Logger::set_info_func([](std::string&& str) { UtilityFunctions::print(str.c_str()); });
- Logger::set_error_func([](std::string&& str) { UtilityFunctions::push_error(str.c_str()); });
+ Logger::set_info_func([](std::string&& str) { UtilityFunctions::print(std_to_godot_string(str)); });
+ Logger::set_error_func([](std::string&& str) { UtilityFunctions::push_error(std_to_godot_string(str)); });
+}
+
+Error GameSingleton::_load_hardcoded_defines() {
+ Error err = OK;
static constexpr colour_t LOW_ALPHA_VALUE = float_to_alpha_value(0.4f);
static constexpr colour_t HIGH_ALPHA_VALUE = float_to_alpha_value(0.7f);
using mapmode_t = std::pair<std::string, Mapmode::colour_func_t>;
- const std::vector<mapmode_t> mapmodes = {
+ const std::vector<mapmode_t> mapmodes {
{ "mapmode_terrain",
[](Map const&, Province const& province) -> colour_t {
return LOW_ALPHA_VALUE | (province.is_water() ? 0x4287F5 : 0x0D7017);
@@ -129,9 +130,9 @@ GameSingleton::GameSingleton() : game_manager { [this]() { _on_state_updated();
if (railroad != nullptr) {
colour_t val = fraction_to_colour_byte(railroad->get_level(), railroad->get_type().get_max_level(), 0.5f, 1.0f);
switch (railroad->get_expansion_state()) {
- case Building::ExpansionState::CannotExpand: val <<= 16; break;
- case Building::ExpansionState::CanExpand: break;
- default: val <<= 8; break;
+ case Building::ExpansionState::CannotExpand: val <<= 16; break;
+ case Building::ExpansionState::CanExpand: break;
+ default: val <<= 8; break;
}
return HIGH_ALPHA_VALUE | val;
}
@@ -139,16 +140,20 @@ GameSingleton::GameSingleton() : game_manager { [this]() { _on_state_updated();
} }
};
for (mapmode_t const& mapmode : mapmodes)
- game_manager.map.add_mapmode(mapmode.first, mapmode.second);
+ if (game_manager.map.add_mapmode(mapmode.first, mapmode.second) != SUCCESS)
+ err = FAILED;
game_manager.map.lock_mapmodes();
using building_type_t = std::tuple<std::string, Building::level_t, Timespan>;
- const std::vector<building_type_t> building_types = {
+ const std::vector<building_type_t> building_types {
{ "building_fort", 4, 8 }, { "building_naval_base", 6, 15 }, { "building_railroad", 5, 10 }
};
for (building_type_t const& type : building_types)
- game_manager.building_manager.add_building_type(std::get<0>(type), std::get<1>(type), std::get<2>(type));
+ if (game_manager.building_manager.add_building_type(std::get<0>(type), std::get<1>(type), std::get<2>(type)) != SUCCESS)
+ err = FAILED;
game_manager.building_manager.lock_building_types();
+
+ return err;
}
GameSingleton::~GameSingleton() {
@@ -156,451 +161,10 @@ GameSingleton::~GameSingleton() {
singleton = nullptr;
}
-static Error _load_json_file(String const& file_description, String const& file_path, Variant& result) {
- result.clear();
- UtilityFunctions::print("Loading ", file_description, " file: ", file_path);
- const 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 ", file_description, " file: ", file_path);
- return err == OK ? FAILED : err;
- }
- const String json_string = file->get_as_text();
- JSON json;
- err = json.parse(json_string);
- if (err != OK) {
- UtilityFunctions::push_error("Failed to parse ", file_description, " file as JSON: ", file_path,
- "\nError at line ", json.get_error_line(), ": ", json.get_error_message());
- return err;
- }
- result = json.get_data();
- return err;
-}
-
-using parse_json_entry_func_t = std::function<Error(String const&, Variant const&)>;
-
-static Error _parse_json_dictionary_file(String const& file_description, String const& file_path,
- String const& identifier_prefix, parse_json_entry_func_t parse_entry) {
- Variant json_var;
- Error err = _load_json_file(file_description, file_path, json_var);
- if (err != OK) return err;
- const Variant::Type type = json_var.get_type();
- if (type != Variant::DICTIONARY) {
- UtilityFunctions::push_error("Invalid ", file_description, " JSON: root has type ",
- Variant::get_type_name(type), " (expected Dictionary)");
- return FAILED;
- }
- Dictionary const& dict = json_var;
- const Array identifiers = dict.keys();
- for (int64_t idx = 0; idx < identifiers.size(); ++idx) {
- String const& identifier = identifiers[idx];
- Variant const& entry = dict[identifier];
- if (identifier.is_empty()) {
- UtilityFunctions::push_error("Empty identifier in ", file_description, " file with entry: ", entry);
- err = FAILED;
- continue;
- }
- if (!identifier.begins_with(identifier_prefix))
- UtilityFunctions::push_warning("Identifier in ", file_description, " file missing \"", identifier_prefix, "\" prefix: ", identifier);
- if (parse_entry(identifier, entry) != OK) err = FAILED;
- }
- return err;
-}
-
-static colour_t _parse_colour(Variant const& var) {
- const Variant::Type type = var.get_type();
- if (type == Variant::ARRAY) {
- Array const& colour_array = var;
- if (colour_array.size() == 3) {
- colour_t colour = NULL_COLOUR;
- for (int jdx = 0; jdx < 3; ++jdx) {
- Variant const& var = colour_array[jdx];
- if (var.get_type() != Variant::FLOAT) return NULL_COLOUR;
- const double colour_double = var;
- if (std::trunc(colour_double) != colour_double) return NULL_COLOUR;
- const int64_t colour_int = static_cast<int64_t>(colour_double);
- if (colour_int < 0 || colour_int > 255) return NULL_COLOUR;
- colour = (colour << 8) | colour_int;
- }
- return colour;
- }
- } else if (type == Variant::STRING) {
- String const& colour_string = var;
- if (colour_string.is_valid_hex_number()) {
- const int64_t colour_int = colour_string.hex_to_int();
- if (colour_int != NULL_COLOUR && colour_int <= MAX_COLOUR_RGB)
- return colour_int;
- }
- }
- return NULL_COLOUR;
-}
-
-Error GameSingleton::_parse_province_identifier_entry(String const& identifier, Variant const& entry) {
- const colour_t colour = _parse_colour(entry);
- if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) {
- UtilityFunctions::push_error("Invalid colour for province identifier \"", identifier, "\": ", entry);
- return FAILED;
- }
- return ERR(game_manager.map.add_province(identifier.utf8().get_data(), colour));
-}
-
-Error GameSingleton::load_province_identifier_file(String const& file_path) {
- const Error err = _parse_json_dictionary_file("province identifier", file_path, "prov_",
- [this](String const& identifier, Variant const& entry) -> Error {
- return _parse_province_identifier_entry(identifier, entry);
- });
- game_manager.map.lock_provinces();
- return err;
-}
-
-Error GameSingleton::_parse_region_entry(String const& identifier, Variant const& entry) {
- Error err = OK;
- Variant::Type type = entry.get_type();
- std::vector<std::string> province_identifiers;
- if (type == Variant::ARRAY) {
- Array const& province_array = entry;
- for (int64_t idx = 0; idx < province_array.size(); ++idx) {
- Variant const& province_var = province_array[idx];
- type = province_var.get_type();
- if (type == Variant::STRING) {
- String const& province_string = province_var;
- province_identifiers.push_back(province_string.utf8().get_data());
- } else {
- UtilityFunctions::push_error("Invalid province identifier for region \"", identifier, "\": ", entry);
- err = FAILED;
- }
- }
- }
- if (province_identifiers.empty()) {
- UtilityFunctions::push_error("Invalid province list for region \"", identifier, "\": ", entry);
- return FAILED;
- }
- return ERR(game_manager.map.add_region(identifier.utf8().get_data(), province_identifiers));
-}
-
-Error GameSingleton::load_region_file(String const& file_path) {
- const Error err = _parse_json_dictionary_file("region", file_path, "region_",
- [this](String const& identifier, Variant const& entry) -> Error {
- return _parse_region_entry(identifier, entry);
- });
- game_manager.map.lock_regions();
- return err;
-}
-
-TerrainVariant::TerrainVariant(std::string const& new_identfier, colour_t new_colour, Ref<Image> const& new_image)
- : HasIdentifier(new_identfier),
- HasColour(new_colour),
- image(new_image) {}
-
-Ref<Image> TerrainVariant::get_image() const { return image; }
-
-Error GameSingleton::_parse_terrain_entry(String const& identifier, Variant const& entry) {
- const colour_t colour = _parse_colour(entry);
- if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) {
- UtilityFunctions::push_error("Invalid colour for terrain texture \"", identifier, "\": ", entry);
- return FAILED;
- }
- static const String terrain_folder = "res://art/terrain/";
- const String terrain_path = terrain_folder + identifier;
- Ref<Image> terrain_image;
- terrain_image.instantiate();
- const Error err = terrain_image->load(terrain_path);
- if (err != OK) {
- UtilityFunctions::push_error("Failed to load terrain image: ", terrain_path);
- return err;
- }
- return ERR(terrain_variants.add_item({ identifier.utf8().get_data(), colour, terrain_image }));
-}
-
-Error GameSingleton::load_terrain_variant_file(String const& file_path) {
- const Error err = _parse_json_dictionary_file("terrain variants", file_path, "",
- [this](String const& identifier, Variant const& entry) -> Error {
- return _parse_terrain_entry(identifier, entry);
- });
- terrain_variants.lock();
- if (err != OK || terrain_variants.get_item_count() == 0) {
- UtilityFunctions::push_error("Failed to load terrain textures!");
- return FAILED;
- }
-
- Array terrain_images;
- for (TerrainVariant const& var : terrain_variants.get_items()) {
- terrain_variant_map[var.get_colour()] = terrain_images.size();
- terrain_images.append(var.get_image());
- }
-
- terrain_texture.instantiate();
- if (terrain_texture->create_from_images(terrain_images) != OK) {
- UtilityFunctions::push_error("Failed to create terrain texture array!");
- return FAILED;
- }
- return OK;
-}
-
-Error GameSingleton::load_map_images(String const& province_image_path, String const& terrain_image_path) {
- if (province_shape_texture.is_valid()) {
- UtilityFunctions::push_error("Map images have already been loaded, cannot load: ", province_image_path, " and ", terrain_image_path);
- return FAILED;
- }
-
- // Load images
- Ref<Image> province_image, terrain_image;
- province_image.instantiate();
- terrain_image.instantiate();
- Error err = province_image->load(province_image_path);
- if (err != OK) {
- UtilityFunctions::push_error("Failed to load province image: ", province_image_path);
- return err;
- }
- err = terrain_image->load(terrain_image_path);
- if (err != OK) {
- UtilityFunctions::push_error("Failed to load terrain image: ", terrain_image_path);
- return err;
- }
-
- // Validate dimensions and format
- const Vector2i province_dims = province_image->get_size(), terrain_dims = terrain_image->get_size();
- if (province_dims.x < 1 || province_dims.y < 1) {
- UtilityFunctions::push_error("Invalid dimensions (", province_dims.x, "x", province_dims.y, ") for province image: ", province_image_path);
- err = FAILED;
- }
- if (province_dims != terrain_dims) {
- UtilityFunctions::push_error("Invalid dimensions (", terrain_dims.x, "x", terrain_dims.y, ") for terrain image: ",
- terrain_image_path, " (must match province image: (", province_dims.x, "x", province_dims.x, "))");
- err = FAILED;
- }
- static constexpr Image::Format expected_format = Image::FORMAT_RGB8;
- const Image::Format province_format = province_image->get_format(), terrain_format = terrain_image->get_format();
- if (province_format != expected_format) {
- UtilityFunctions::push_error("Invalid format (", province_format, ", should be ", expected_format, ") for province image: ", province_image_path);
- err = FAILED;
- }
- if (terrain_format != expected_format) {
- UtilityFunctions::push_error("Invalid format (", terrain_format, ", should be ", expected_format, ") for terrain image: ", terrain_image_path);
- err = FAILED;
- }
- if (err != OK) return err;
-
- // Generate interleaved province and terrain ID image
- if (game_manager.map.generate_province_shape_image(province_dims.x, province_dims.y, province_image->get_data().ptr(),
- terrain_image->get_data().ptr(), terrain_variant_map) != SUCCESS) return FAILED;
-
- 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)
- for (image_subdivisions[i] = 1;
- province_dims[i] / image_subdivisions[i] > GPU_DIM_LIMIT || province_dims[i] % image_subdivisions[i] != 0; ++image_subdivisions[i]);
-
- Map::shape_pixel_t const* province_shape_data = game_manager.map.get_province_shape_image().data();
- const Vector2i divided_dims = province_dims / image_subdivisions;
- Array province_shape_images;
- province_shape_images.resize(image_subdivisions.x * image_subdivisions.y);
- for (int32_t v = 0; v < image_subdivisions.y; ++v) {
- for (int32_t u = 0; u < image_subdivisions.x; ++u) {
- PackedByteArray index_data_array;
- index_data_array.resize(divided_dims.x * divided_dims.y * sizeof(Map::shape_pixel_t));
-
- for (int32_t y = 0; y < divided_dims.y; ++y)
- memcpy(index_data_array.ptrw() + y * divided_dims.x * sizeof(Map::shape_pixel_t),
- province_shape_data + (v * divided_dims.y + y) * province_dims.x + u * divided_dims.x,
- divided_dims.x * sizeof(Map::shape_pixel_t));
-
- const Ref<Image> province_shape_subimage = Image::create_from_data(divided_dims.x, divided_dims.y, false, Image::FORMAT_RGB8, index_data_array);
- if (province_shape_subimage.is_null()) {
- UtilityFunctions::push_error("Failed to create province shape image (", u, ", ", v, ")");
- err = FAILED;
- }
- province_shape_images[u + v * image_subdivisions.x] = province_shape_subimage;
- }
- }
-
- province_shape_texture.instantiate();
- if (province_shape_texture->create_from_images(province_shape_images) != OK) {
- UtilityFunctions::push_error("Failed to create terrain texture array!");
- err = FAILED;
- }
-
- if (update_colour_image() != OK) err = FAILED;
-
- return err;
-}
-
-Error GameSingleton::_parse_good_entry(String const& identifier, Variant const& entry) {
- if (entry.get_type() != Variant::DICTIONARY) {
- UtilityFunctions::push_error("Invalid good entry for ", identifier, ": ", entry);
- return FAILED;
- }
- Dictionary const& dict = entry;
-
- static const String key_category = "category";
- Variant const& var_category = dict.get(key_category, "");
- String category;
- if (var_category.get_type() == Variant::STRING) category = var_category;
- else UtilityFunctions::push_error("Invalid good category for ", identifier, ": ", var_category);
-
- static const String key_base_price = "base_price";
- Variant const& var_base_price = dict.get(key_base_price, NULL_PRICE);
- price_t base_price = NULL_PRICE;
- if (var_base_price.get_type() == Variant::FLOAT) base_price = var_base_price;
- else UtilityFunctions::push_error("Invalid good base price for ", identifier, ": ", var_base_price);
-
- static const String key_colour = "colour";
- Variant const& var_colour = dict.get(key_colour, "");
- const colour_t colour = _parse_colour(var_colour);
- if (colour > MAX_COLOUR_RGB) {
- UtilityFunctions::push_error("Invalid good colour for ", identifier, ": ", var_colour);
- return FAILED;
- }
-
- static const String key_default_available = "default_available";
- Variant const& var_default_available = dict.get(key_default_available, true);
- bool default_available = false;
- if (var_default_available.get_type() == Variant::BOOL) default_available = var_default_available;
- else UtilityFunctions::push_error("Invalid good available default bool value for ", identifier, ": ", var_default_available);
-
- static const String key_tradeable = "tradeable";
- Variant const& var_tradeable = dict.get(key_tradeable, true);
- bool tradeable = false;
- if (var_tradeable.get_type() == Variant::BOOL) tradeable = var_tradeable;
- else UtilityFunctions::push_error("Invalid good tradeable bool value for ", identifier, ": ", var_tradeable);
-
- static const String key_currency = "currency";
- Variant const& var_currency = dict.get(key_currency, true);
- bool currency = false;
- if (var_currency.get_type() == Variant::BOOL) currency = var_currency;
- else UtilityFunctions::push_error("Invalid good currency bool value for ", identifier, ": ", var_currency);
-
- static const String key_overseas_maintenance = "overseas_maintenance";
- Variant const& var_overseas_maintenance = dict.get(key_overseas_maintenance, true);
- bool overseas_maintenance = false;
- if (var_overseas_maintenance.get_type() == Variant::BOOL) overseas_maintenance = var_overseas_maintenance;
- else UtilityFunctions::push_error("Invalid good overseas maintenance bool value for ", identifier, ": ", var_overseas_maintenance);
-
- return ERR(game_manager.good_manager.add_good(identifier.utf8().get_data(), category.utf8().get_data(),
- colour, base_price, default_available, tradeable, currency, overseas_maintenance));
-}
-
-Error GameSingleton::load_goods(String const& defines_path, String const& icons_dir_path) {
- Error err = _parse_json_dictionary_file("good", defines_path, "good_",
- [this](String const& identifier, Variant const& entry) -> Error {
- return _parse_good_entry(identifier, entry);
- });
- game_manager.good_manager.lock_goods();
- for (Good const& good : game_manager.good_manager.get_goods()) {
- Ref<Image> image;
- image.instantiate();
- const String path = icons_dir_path + String{ "/" } + good.get_identifier().c_str() + ".png";
- const Error good_err = image->load(path);
- if (good_err || image.is_null()) {
- UtilityFunctions::push_error("Failed to load good icon image: ", path);
- err = FAILED;
- continue;
- }
- Ref<Texture> tex = ImageTexture::create_from_image(image);
- if (tex.is_null()) {
- UtilityFunctions::push_error("Failed to generate good icon texture: ", path);
- err = FAILED;
- continue;
- }
- good_icons[good.get_identifier().c_str()] = tex;
- }
- return err;
-}
-
-StringName const& GameSingleton::get_province_identifier_file_key() {
- static const StringName key = "province_identifiers";
- return key;
-}
-StringName const& GameSingleton::get_water_province_file_key() {
- static const StringName key = "water_provinces";
- return key;
-}
-StringName const& GameSingleton::get_region_file_key() {
- static const StringName key = "regions";
- return key;
-}
-StringName const& GameSingleton::get_terrain_variant_file_key() {
- static const StringName key = "terrain_variants";
- return key;
-}
-StringName const& GameSingleton::get_province_image_file_key() {
- static const StringName key = "province_image";
- return key;
-}
-StringName const& GameSingleton::get_terrain_image_file_key() {
- static const StringName key = "terrain_image";
- return key;
-}
-StringName const& GameSingleton::get_goods_file_key() {
- static const StringName key = "goods";
- return key;
-}
-StringName const& GameSingleton::get_good_icons_dir_key() {
- static const StringName key = "good_icons";
- return key;
-}
-
-Error GameSingleton::load_defines(Dictionary const& file_dict) {
- Error err = OK;
- if (load_province_identifier_file(file_dict.get(get_province_identifier_file_key(), "")) != OK) {
- UtilityFunctions::push_error("Failed to load province identifiers!");
- err = FAILED;
- }
- if (load_water_province_file(file_dict.get(get_water_province_file_key(), "")) != OK) {
- UtilityFunctions::push_error("Failed to load water provinces!");
- err = FAILED;
- }
- if (load_region_file(file_dict.get(get_region_file_key(), "")) != OK) {
- UtilityFunctions::push_error("Failed to load regions!");
- err = FAILED;
- }
- if (load_terrain_variant_file(file_dict.get(get_terrain_variant_file_key(), "")) != OK) {
- UtilityFunctions::push_error("Failed to load terrain variants!");
- err = FAILED;
- }
- if (load_map_images(file_dict.get(get_province_image_file_key(), ""), file_dict.get(get_terrain_image_file_key(), "")) != OK) {
- UtilityFunctions::push_error("Failed to load map images!");
- err = FAILED;
- }
- if (load_goods(file_dict.get(get_goods_file_key(), ""), file_dict.get(get_good_icons_dir_key(), "")) != OK) {
- UtilityFunctions::push_error("Failed to load goods!");
- err = FAILED;
- }
- return err;
-}
-
Error GameSingleton::setup() {
return ERR(game_manager.setup());
}
-Error GameSingleton::load_water_province_file(String const& file_path) {
- Variant json_var;
- Error err = _load_json_file("water province", file_path, json_var);
- if (err != OK) return err;
- Variant::Type type = json_var.get_type();
- if (type != Variant::ARRAY) {
- UtilityFunctions::push_error("Invalid water province JSON: root has type ",
- Variant::get_type_name(type), " (expected Array)");
- err = FAILED;
- } else {
- Array const& array = json_var;
- for (int64_t idx = 0; idx < array.size(); ++idx) {
- Variant const& entry = array[idx];
- type = entry.get_type();
- if (type != Variant::STRING) {
- UtilityFunctions::push_error("Invalid water province identifier: ", entry);
- err = FAILED;
- continue;
- }
- String const& identifier = entry;
- if (game_manager.map.set_water_province(identifier.utf8().get_data()) != SUCCESS)
- err = FAILED;
- }
- }
- game_manager.map.lock_water_provinces();
- return err;
-}
-
int32_t GameSingleton::get_province_index_from_uv_coords(Vector2 const& coords) const {
const size_t x_mod_w = UtilityFunctions::fposmod(coords.x, 1.0f) * get_width();
const size_t y_mod_h = UtilityFunctions::fposmod(coords.y, 1.0f) * get_height();
@@ -658,13 +222,13 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
if (province == nullptr) return {};
Dictionary ret;
- ret[get_province_info_province_key()] = province->get_identifier().c_str();
+ ret[get_province_info_province_key()] = std_to_godot_string(province->get_identifier());
Region const* region = province->get_region();
- if (region != nullptr) ret[get_province_info_region_key()] = region->get_identifier().c_str();
+ if (region != nullptr) ret[get_province_info_region_key()] = std_to_godot_string(region->get_identifier());
Good const* rgo = province->get_rgo();
- if (rgo != nullptr) ret[get_province_info_rgo_key()] = rgo->get_identifier().c_str();
+ if (rgo != nullptr) ret[get_province_info_rgo_key()] = std_to_godot_string(rgo->get_identifier());
ret[get_province_info_life_rating_key()] = province->get_life_rating();
@@ -676,11 +240,11 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
Building const& building = buildings[idx];
Dictionary building_dict;
- building_dict[get_building_info_building_key()] = building.get_identifier().c_str();
+ building_dict[get_building_info_building_key()] = std_to_godot_string(building.get_identifier());
building_dict[get_building_info_level_key()] = static_cast<int32_t>(building.get_level());
building_dict[get_building_info_expansion_state_key()] = static_cast<int32_t>(building.get_expansion_state());
- building_dict[get_building_info_start_date_key()] = static_cast<std::string>(building.get_start_date()).c_str();
- building_dict[get_building_info_end_date_key()] = static_cast<std::string>(building.get_end_date()).c_str();
+ building_dict[get_building_info_start_date_key()] = std_to_godot_string(static_cast<std::string>(building.get_start_date()));
+ building_dict[get_building_info_end_date_key()] = std_to_godot_string(static_cast<std::string>(building.get_end_date()));
building_dict[get_building_info_expansion_progress_key()] = building.get_expansion_progress();
buildings_array[idx] = building_dict;
@@ -718,7 +282,7 @@ Ref<Texture> GameSingleton::get_province_colour_texture() const {
return province_colour_texture;
}
-Error GameSingleton::update_colour_image() {
+Error GameSingleton::_update_colour_image() {
static PackedByteArray colour_data_array;
static constexpr int64_t colour_data_array_size = (MAX_INDEX + 1) * Map::MAPMODE_COLOUR_SIZE;
colour_data_array.resize(colour_data_array_size);
@@ -728,18 +292,18 @@ Error GameSingleton::update_colour_image() {
err = FAILED;
static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(index_t) * 4);
- if (province_colour_image.is_null())
+ 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");
+ }
province_colour_image->set_data(PROVINCE_INDEX_SQRT, PROVINCE_INDEX_SQRT,
false, Image::FORMAT_RGBA8, colour_data_array);
- if (province_colour_image.is_null()) {
- UtilityFunctions::push_error("Failed to update province colour image");
- return FAILED;
- }
- if (province_colour_texture.is_null())
+ if (province_colour_texture.is_null()) {
province_colour_texture = ImageTexture::create_from_image(province_colour_image);
- else
- province_colour_texture->update(province_colour_image);
+ ERR_FAIL_NULL_V_EDMSG(province_colour_texture, FAILED,
+ "Failed to create province colour texture");
+ } else province_colour_texture->update(province_colour_image);
return err;
}
@@ -749,18 +313,18 @@ int32_t GameSingleton::get_mapmode_count() const {
String GameSingleton::get_mapmode_identifier(int32_t index) const {
Mapmode const* mapmode = game_manager.map.get_mapmode_by_index(index);
- if (mapmode != nullptr) return mapmode->get_identifier().c_str();
+ if (mapmode != nullptr) return std_to_godot_string(mapmode->get_identifier());
return String {};
}
Error GameSingleton::set_mapmode(String const& identifier) {
- Mapmode const* mapmode = game_manager.map.get_mapmode_by_identifier(identifier.utf8().get_data());
+ Mapmode const* mapmode = game_manager.map.get_mapmode_by_identifier(godot_to_std_string(identifier));
if (mapmode == nullptr) {
UtilityFunctions::push_error("Failed to set mapmode to: ", identifier);
return FAILED;
}
mapmode_index = mapmode->get_index();
- update_colour_image();
+ _update_colour_image();
return OK;
}
@@ -770,12 +334,12 @@ int32_t GameSingleton::get_selected_province_index() const {
void GameSingleton::set_selected_province(int32_t index) {
game_manager.map.set_selected_province(index);
- update_colour_image();
+ _update_colour_image();
emit_signal("province_selected", index);
}
Error GameSingleton::expand_building(int32_t province_index, String const& building_type_identifier) {
- if (game_manager.expand_building(province_index, building_type_identifier.utf8().get_data()) != SUCCESS) {
+ if (game_manager.expand_building(province_index, godot_to_std_string(building_type_identifier)) != SUCCESS) {
UtilityFunctions::push_error("Failed to expand ", building_type_identifier, " at province index ", province_index);
return FAILED;
}
@@ -815,7 +379,7 @@ bool GameSingleton::can_decrease_speed() const {
}
String GameSingleton::get_longform_date() const {
- return static_cast<std::string>(game_manager.get_today()).c_str();
+ return std_to_godot_string(static_cast<std::string>(game_manager.get_today()));
}
void GameSingleton::try_tick() {
diff --git a/extension/src/GameSingleton.hpp b/extension/src/GameSingleton.hpp
index 99fe7bb..4c809af 100644
--- a/extension/src/GameSingleton.hpp
+++ b/extension/src/GameSingleton.hpp
@@ -12,10 +12,13 @@ namespace OpenVic {
public:
TerrainVariant(std::string const& new_identfier, colour_t new_colour,
- godot::Ref<godot::Image> const& new_image);
+ godot::Ref<godot::Image> const& new_image)
+ : HasIdentifier { new_identfier },
+ HasColour { new_colour },
+ image { new_image } {}
TerrainVariant(TerrainVariant&&) = default;
- godot::Ref<godot::Image> get_image() const;
+ godot::Ref<godot::Image> get_image() const { return image; }
};
class GameSingleton : public godot::Object {
GDCLASS(GameSingleton, godot::Object)
@@ -36,16 +39,24 @@ namespace OpenVic {
godot::Error _parse_province_identifier_entry(godot::String const& identifier, godot::Variant const& entry);
godot::Error _parse_region_entry(godot::String const& identifier, godot::Variant const& entry);
- godot::Error _parse_terrain_entry(godot::String const& identifier, godot::Variant const& entry);
+ godot::Error _parse_terrain_entry(godot::String const& identifier, godot::Variant const& entry, godot::String const& terrain_texture_dir_path);
godot::Error _parse_good_entry(godot::String const& identifier, godot::Variant const& entry);
godot::Error load_province_identifier_file(godot::String const& file_path);
godot::Error load_water_province_file(godot::String const& file_path);
godot::Error load_region_file(godot::String const& file_path);
- godot::Error load_terrain_variant_file(godot::String const& file_path);
+ godot::Error load_terrain_variants(godot::String const& terrain_identifiers_path, godot::String const& terrain_texture_dir_path);
godot::Error load_map_images(godot::String const& province_image_path, godot::String const& terrain_image_path);
godot::Error load_goods(godot::String const& defines_path, godot::String const& icons_dir_path);
+ /* Hardcoded data for defining things for which parsing from files has
+ * not been implemented, currently mapmodes and building types.
+ */
+ godot::Error _load_hardcoded_defines();
+
+ /* Generate the province_colour_texture from the current mapmode.
+ */
+ godot::Error _update_colour_image();
void _on_state_updated();
protected:
@@ -61,6 +72,7 @@ namespace OpenVic {
static godot::StringName const& get_water_province_file_key();
static godot::StringName const& get_region_file_key();
static godot::StringName const& get_terrain_variant_file_key();
+ static godot::StringName const& get_terrain_texture_dir_key();
static godot::StringName const& get_province_image_file_key();
static godot::StringName const& get_terrain_image_file_key();
static godot::StringName const& get_goods_file_key();
@@ -121,10 +133,6 @@ namespace OpenVic {
*/
godot::Ref<godot::Texture> get_province_colour_texture() const;
- /* Generate the province_colour_texture from the current mapmode.
- */
- godot::Error update_colour_image();
-
int32_t get_mapmode_count() const;
godot::String get_mapmode_identifier(int32_t index) const;
godot::Error set_mapmode(godot::String const& identifier);
diff --git a/extension/src/LoadGameOpenVic.cpp b/extension/src/LoadGameOpenVic.cpp
new file mode 100644
index 0000000..7f33bf5
--- /dev/null
+++ b/extension/src/LoadGameOpenVic.cpp
@@ -0,0 +1,450 @@
+#include "GameSingleton.hpp"
+
+#include <godot_cpp/classes/file_access.hpp>
+#include <godot_cpp/classes/json.hpp>
+#include <godot_cpp/variant/utility_functions.hpp>
+
+#include "Utilities.hpp"
+
+using namespace godot;
+using namespace OpenVic;
+
+static Error _load_json_file(String const& file_description, String const& file_path, Variant& result) {
+ result.clear();
+ UtilityFunctions::print("Loading ", file_description, " file: ", file_path);
+ const 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 ", file_description, " file: ", file_path);
+ return err == OK ? FAILED : err;
+ }
+ const String json_string = file->get_as_text();
+ JSON json;
+ err = json.parse(json_string);
+ if (err != OK) {
+ UtilityFunctions::push_error("Failed to parse ", file_description, " file as JSON: ", file_path,
+ "\nError at line ", json.get_error_line(), ": ", json.get_error_message());
+ return err;
+ }
+ result = json.get_data();
+ return err;
+}
+
+using parse_json_entry_func_t = std::function<Error(String const&, Variant const&)>;
+
+static Error _parse_json_dictionary_file(String const& file_description, String const& file_path,
+ String const& identifier_prefix, parse_json_entry_func_t parse_entry) {
+ Variant json_var;
+ Error err = _load_json_file(file_description, file_path, json_var);
+ if (err != OK) return err;
+ const Variant::Type type = json_var.get_type();
+ if (type != Variant::DICTIONARY) {
+ UtilityFunctions::push_error("Invalid ", file_description, " JSON: root has type ",
+ Variant::get_type_name(type), " (expected Dictionary)");
+ return FAILED;
+ }
+ Dictionary const& dict = json_var;
+ const Array identifiers = dict.keys();
+ for (int64_t idx = 0; idx < identifiers.size(); ++idx) {
+ String const& identifier = identifiers[idx];
+ Variant const& entry = dict[identifier];
+ if (identifier.is_empty()) {
+ UtilityFunctions::push_error("Empty identifier in ", file_description, " file with entry: ", entry);
+ err = FAILED;
+ continue;
+ }
+ if (!identifier.begins_with(identifier_prefix))
+ UtilityFunctions::push_warning("Identifier in ", file_description, " file missing \"", identifier_prefix, "\" prefix: ", identifier);
+ if (parse_entry(identifier, entry) != OK) err = FAILED;
+ }
+ return err;
+}
+
+static colour_t _parse_colour(Variant const& var) {
+ const Variant::Type type = var.get_type();
+ if (type == Variant::ARRAY) {
+ Array const& colour_array = var;
+ if (colour_array.size() == 3) {
+ colour_t colour = NULL_COLOUR;
+ for (int jdx = 0; jdx < 3; ++jdx) {
+ Variant const& var = colour_array[jdx];
+ if (var.get_type() != Variant::FLOAT) return NULL_COLOUR;
+ const double colour_double = var;
+ if (std::trunc(colour_double) != colour_double) return NULL_COLOUR;
+ const int64_t colour_int = static_cast<int64_t>(colour_double);
+ if (colour_int < 0 || colour_int > 255) return NULL_COLOUR;
+ colour = (colour << 8) | colour_int;
+ }
+ return colour;
+ }
+ } else if (type == Variant::STRING) {
+ String const& colour_string = var;
+ if (colour_string.is_valid_hex_number()) {
+ const int64_t colour_int = colour_string.hex_to_int();
+ if (colour_int != NULL_COLOUR && colour_int <= MAX_COLOUR_RGB)
+ return colour_int;
+ }
+ }
+ return NULL_COLOUR;
+}
+
+Error GameSingleton::_parse_province_identifier_entry(String const& identifier, Variant const& entry) {
+ const colour_t colour = _parse_colour(entry);
+ if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) {
+ UtilityFunctions::push_error("Invalid colour for province identifier \"", identifier, "\": ", entry);
+ return FAILED;
+ }
+ return ERR(game_manager.map.add_province(godot_to_std_string(identifier), colour));
+}
+
+Error GameSingleton::load_province_identifier_file(String const& file_path) {
+ const Error err = _parse_json_dictionary_file("province identifier", file_path, "prov_",
+ [this](String const& identifier, Variant const& entry) -> Error {
+ return _parse_province_identifier_entry(identifier, entry);
+ });
+ game_manager.map.lock_provinces();
+ return err;
+}
+
+Error GameSingleton::load_water_province_file(String const& file_path) {
+ Variant json_var;
+ Error err = _load_json_file("water province", file_path, json_var);
+ if (err != OK) return err;
+ Variant::Type type = json_var.get_type();
+ if (type != Variant::ARRAY) {
+ UtilityFunctions::push_error("Invalid water province JSON: root has type ",
+ Variant::get_type_name(type), " (expected Array)");
+ err = FAILED;
+ } else {
+ Array const& array = json_var;
+ for (int64_t idx = 0; idx < array.size(); ++idx) {
+ Variant const& entry = array[idx];
+ type = entry.get_type();
+ if (type != Variant::STRING) {
+ UtilityFunctions::push_error("Invalid water province identifier: ", entry);
+ err = FAILED;
+ continue;
+ }
+ String const& identifier = entry;
+ if (game_manager.map.set_water_province(godot_to_std_string(identifier)) != SUCCESS)
+ err = FAILED;
+ }
+ }
+ game_manager.map.lock_water_provinces();
+ return err;
+}
+
+Error GameSingleton::_parse_region_entry(String const& identifier, Variant const& entry) {
+ Error err = OK;
+ Variant::Type type = entry.get_type();
+ std::vector<std::string> province_identifiers;
+ if (type == Variant::ARRAY) {
+ Array const& province_array = entry;
+ for (int64_t idx = 0; idx < province_array.size(); ++idx) {
+ Variant const& province_var = province_array[idx];
+ type = province_var.get_type();
+ if (type == Variant::STRING) {
+ String const& province_string = province_var;
+ province_identifiers.push_back(godot_to_std_string(province_string));
+ } else {
+ UtilityFunctions::push_error("Invalid province identifier for region \"", identifier, "\": ", entry);
+ err = FAILED;
+ }
+ }
+ }
+ if (province_identifiers.empty()) {
+ UtilityFunctions::push_error("Invalid province list for region \"", identifier, "\": ", entry);
+ return FAILED;
+ }
+ return ERR(game_manager.map.add_region(godot_to_std_string(identifier), province_identifiers));
+}
+
+Error GameSingleton::load_region_file(String const& file_path) {
+ const Error err = _parse_json_dictionary_file("region", file_path, "region_",
+ [this](String const& identifier, Variant const& entry) -> Error {
+ return _parse_region_entry(identifier, entry);
+ });
+ game_manager.map.lock_regions();
+ return err;
+}
+
+Error GameSingleton::_parse_terrain_entry(String const& identifier, Variant const& entry, String const& terrain_texture_dir_path) {
+ const colour_t colour = _parse_colour(entry);
+ if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) {
+ UtilityFunctions::push_error("Invalid colour for terrain texture \"", identifier, "\": ", entry);
+ return FAILED;
+ }
+ const String terrain_path = terrain_texture_dir_path + identifier;
+ Ref<Image> terrain_image;
+ terrain_image.instantiate();
+ const Error err = terrain_image->load(terrain_path);
+ if (err != OK) {
+ UtilityFunctions::push_error("Failed to load terrain image: ", terrain_path);
+ return err;
+ }
+ return ERR(terrain_variants.add_item({ godot_to_std_string(identifier), colour, terrain_image }));
+}
+
+Error GameSingleton::load_terrain_variants(String const& terrain_identifiers_path, String const& terrain_texture_dir_path) {
+ const Error err = _parse_json_dictionary_file("terrain variants", terrain_identifiers_path, "",
+ [this, terrain_texture_dir_path](String const& identifier, Variant const& entry) -> Error {
+ return _parse_terrain_entry(identifier, entry, terrain_texture_dir_path + String { "/" });
+ });
+ terrain_variants.lock();
+ if (terrain_variants.get_item_count() == 0) {
+ UtilityFunctions::push_error("Failed to load terrain textures!");
+ return FAILED;
+ }
+
+ Array terrain_images;
+ for (TerrainVariant const& var : terrain_variants.get_items()) {
+ terrain_variant_map[var.get_colour()] = terrain_images.size();
+ terrain_images.append(var.get_image());
+ }
+
+ terrain_texture.instantiate();
+ if (terrain_texture->create_from_images(terrain_images) != OK) {
+ UtilityFunctions::push_error("Failed to create terrain texture array!");
+ return FAILED;
+ }
+ return err;
+}
+
+Error GameSingleton::load_map_images(String const& province_image_path, String const& terrain_image_path) {
+ if (province_shape_texture.is_valid()) {
+ UtilityFunctions::push_error("Map images have already been loaded, cannot load: ", province_image_path, " and ", terrain_image_path);
+ return FAILED;
+ }
+
+ // Load images
+ Ref<Image> province_image, terrain_image;
+ province_image.instantiate();
+ terrain_image.instantiate();
+ Error err = province_image->load(province_image_path);
+ if (err != OK) {
+ UtilityFunctions::push_error("Failed to load province image: ", province_image_path);
+ return err;
+ }
+ err = terrain_image->load(terrain_image_path);
+ if (err != OK) {
+ UtilityFunctions::push_error("Failed to load terrain image: ", terrain_image_path);
+ return err;
+ }
+
+ // Validate dimensions and format
+ const Vector2i province_dims = province_image->get_size(), terrain_dims = terrain_image->get_size();
+ if (province_dims.x < 1 || province_dims.y < 1) {
+ UtilityFunctions::push_error("Invalid dimensions (", province_dims.x, "x", province_dims.y, ") for province image: ", province_image_path);
+ err = FAILED;
+ }
+ if (province_dims != terrain_dims) {
+ UtilityFunctions::push_error("Invalid dimensions (", terrain_dims.x, "x", terrain_dims.y, ") for terrain image: ",
+ terrain_image_path, " (must match province image: (", province_dims.x, "x", province_dims.x, "))");
+ err = FAILED;
+ }
+ static constexpr Image::Format expected_format = Image::FORMAT_RGB8;
+ const Image::Format province_format = province_image->get_format(), terrain_format = terrain_image->get_format();
+ if (province_format != expected_format) {
+ UtilityFunctions::push_error("Invalid format (", province_format, ", should be ", expected_format, ") for province image: ", province_image_path);
+ err = FAILED;
+ }
+ if (terrain_format != expected_format) {
+ UtilityFunctions::push_error("Invalid format (", terrain_format, ", should be ", expected_format, ") for terrain image: ", terrain_image_path);
+ err = FAILED;
+ }
+ if (err != OK) return err;
+
+ // Generate interleaved province and terrain ID image
+ if (game_manager.map.generate_province_shape_image(province_dims.x, province_dims.y, province_image->get_data().ptr(),
+ terrain_image->get_data().ptr(), terrain_variant_map) != SUCCESS) err = FAILED;
+
+ 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)
+ for (image_subdivisions[i] = 1; province_dims[i] / image_subdivisions[i] > GPU_DIM_LIMIT ||
+ province_dims[i] % image_subdivisions[i] != 0; ++image_subdivisions[i]);
+
+ Map::shape_pixel_t const* province_shape_data = game_manager.map.get_province_shape_image().data();
+ const Vector2i divided_dims = province_dims / image_subdivisions;
+ Array province_shape_images;
+ province_shape_images.resize(image_subdivisions.x * image_subdivisions.y);
+ for (int32_t v = 0; v < image_subdivisions.y; ++v) {
+ for (int32_t u = 0; u < image_subdivisions.x; ++u) {
+ PackedByteArray index_data_array;
+ index_data_array.resize(divided_dims.x * divided_dims.y * sizeof(Map::shape_pixel_t));
+
+ for (int32_t y = 0; y < divided_dims.y; ++y)
+ memcpy(index_data_array.ptrw() + y * divided_dims.x * sizeof(Map::shape_pixel_t),
+ province_shape_data + (v * divided_dims.y + y) * province_dims.x + u * divided_dims.x,
+ divided_dims.x * sizeof(Map::shape_pixel_t));
+
+ const Ref<Image> province_shape_subimage = Image::create_from_data(divided_dims.x, divided_dims.y, false, Image::FORMAT_RGB8, index_data_array);
+ if (province_shape_subimage.is_null()) {
+ UtilityFunctions::push_error("Failed to create province shape image (", u, ", ", v, ")");
+ err = FAILED;
+ }
+ province_shape_images[u + v * image_subdivisions.x] = province_shape_subimage;
+ }
+ }
+
+ province_shape_texture.instantiate();
+ if (province_shape_texture->create_from_images(province_shape_images) != OK) {
+ UtilityFunctions::push_error("Failed to create terrain texture array!");
+ err = FAILED;
+ }
+
+ return err;
+}
+
+Error GameSingleton::_parse_good_entry(String const& identifier, Variant const& entry) {
+ if (entry.get_type() != Variant::DICTIONARY) {
+ UtilityFunctions::push_error("Invalid good entry for ", identifier, ": ", entry);
+ return FAILED;
+ }
+ Dictionary const& dict = entry;
+
+ static const String key_category = "category";
+ Variant const& var_category = dict.get(key_category, "");
+ String category;
+ if (var_category.get_type() == Variant::STRING) category = var_category;
+ else UtilityFunctions::push_error("Invalid good category for ", identifier, ": ", var_category);
+
+ static const String key_base_price = "base_price";
+ Variant const& var_base_price = dict.get(key_base_price, NULL_PRICE);
+ price_t base_price = NULL_PRICE;
+ if (var_base_price.get_type() == Variant::FLOAT) base_price = var_base_price;
+ else UtilityFunctions::push_error("Invalid good base price for ", identifier, ": ", var_base_price);
+
+ static const String key_colour = "colour";
+ Variant const& var_colour = dict.get(key_colour, "");
+ const colour_t colour = _parse_colour(var_colour);
+ if (colour > MAX_COLOUR_RGB) {
+ UtilityFunctions::push_error("Invalid good colour for ", identifier, ": ", var_colour);
+ return FAILED;
+ }
+
+ static const String key_default_available = "default_available";
+ Variant const& var_default_available = dict.get(key_default_available, true);
+ bool default_available = false;
+ if (var_default_available.get_type() == Variant::BOOL) default_available = var_default_available;
+ else UtilityFunctions::push_error("Invalid good available default bool value for ", identifier, ": ", var_default_available);
+
+ static const String key_tradeable = "tradeable";
+ Variant const& var_tradeable = dict.get(key_tradeable, true);
+ bool tradeable = false;
+ if (var_tradeable.get_type() == Variant::BOOL) tradeable = var_tradeable;
+ else UtilityFunctions::push_error("Invalid good tradeable bool value for ", identifier, ": ", var_tradeable);
+
+ static const String key_currency = "currency";
+ Variant const& var_currency = dict.get(key_currency, true);
+ bool currency = false;
+ if (var_currency.get_type() == Variant::BOOL) currency = var_currency;
+ else UtilityFunctions::push_error("Invalid good currency bool value for ", identifier, ": ", var_currency);
+
+ static const String key_overseas_maintenance = "overseas_maintenance";
+ Variant const& var_overseas_maintenance = dict.get(key_overseas_maintenance, true);
+ bool overseas_maintenance = false;
+ if (var_overseas_maintenance.get_type() == Variant::BOOL) overseas_maintenance = var_overseas_maintenance;
+ else UtilityFunctions::push_error("Invalid good overseas maintenance bool value for ", identifier, ": ", var_overseas_maintenance);
+
+ return ERR(game_manager.good_manager.add_good(godot_to_std_string(identifier), godot_to_std_string(category),
+ colour, base_price, default_available, tradeable, currency, overseas_maintenance));
+}
+
+Error GameSingleton::load_goods(String const& defines_path, String const& icons_dir_path) {
+ Error err = _parse_json_dictionary_file("good", defines_path, "good_",
+ [this](String const& identifier, Variant const& entry) -> Error {
+ return _parse_good_entry(identifier, entry);
+ });
+ game_manager.good_manager.lock_goods();
+ for (Good const& good : game_manager.good_manager.get_goods()) {
+ Ref<Image> image;
+ image.instantiate();
+ const String path = icons_dir_path + String { "/" } + std_to_godot_string(good.get_identifier()) + ".png";
+ const Error good_err = image->load(path);
+ if (good_err || image.is_null()) {
+ UtilityFunctions::push_error("Failed to load good icon image: ", path);
+ err = FAILED;
+ continue;
+ }
+ Ref<Texture> tex = ImageTexture::create_from_image(image);
+ if (tex.is_null()) {
+ UtilityFunctions::push_error("Failed to generate good icon texture: ", path);
+ err = FAILED;
+ continue;
+ }
+ good_icons[std_to_godot_string(good.get_identifier())] = tex;
+ }
+ return err;
+}
+
+StringName const& GameSingleton::get_province_identifier_file_key() {
+ static const StringName key = "province_identifiers";
+ return key;
+}
+StringName const& GameSingleton::get_water_province_file_key() {
+ static const StringName key = "water_provinces";
+ return key;
+}
+StringName const& GameSingleton::get_region_file_key() {
+ static const StringName key = "regions";
+ return key;
+}
+StringName const& GameSingleton::get_terrain_variant_file_key() {
+ static const StringName key = "terrain_variants";
+ return key;
+}
+StringName const& GameSingleton::get_terrain_texture_dir_key() {
+ static const StringName key = "terrain_textures";
+ return key;
+}
+StringName const& GameSingleton::get_province_image_file_key() {
+ static const StringName key = "province_image";
+ return key;
+}
+StringName const& GameSingleton::get_terrain_image_file_key() {
+ static const StringName key = "terrain_image";
+ return key;
+}
+StringName const& GameSingleton::get_goods_file_key() {
+ static const StringName key = "goods";
+ return key;
+}
+StringName const& GameSingleton::get_good_icons_dir_key() {
+ static const StringName key = "good_icons";
+ return key;
+}
+
+Error GameSingleton::load_defines(Dictionary const& file_dict) {
+ Error err = OK;
+ if (load_province_identifier_file(file_dict.get(get_province_identifier_file_key(), "")) != OK) {
+ UtilityFunctions::push_error("Failed to load province identifiers!");
+ err = FAILED;
+ }
+ if (load_water_province_file(file_dict.get(get_water_province_file_key(), "")) != OK) {
+ UtilityFunctions::push_error("Failed to load water provinces!");
+ err = FAILED;
+ }
+ if (load_region_file(file_dict.get(get_region_file_key(), "")) != OK) {
+ UtilityFunctions::push_error("Failed to load regions!");
+ err = FAILED;
+ }
+ if (load_terrain_variants(file_dict.get(get_terrain_variant_file_key(), ""),
+ file_dict.get(get_terrain_texture_dir_key(), "")) != OK) {
+ UtilityFunctions::push_error("Failed to load terrain variants!");
+ err = FAILED;
+ }
+ if (load_map_images(file_dict.get(get_province_image_file_key(), ""), file_dict.get(get_terrain_image_file_key(), "")) != OK) {
+ UtilityFunctions::push_error("Failed to load map images!");
+ err = FAILED;
+ }
+ if (load_goods(file_dict.get(get_goods_file_key(), ""), file_dict.get(get_good_icons_dir_key(), "")) != OK) {
+ UtilityFunctions::push_error("Failed to load goods!");
+ err = FAILED;
+ }
+ if (_load_hardcoded_defines() != OK) {
+ UtilityFunctions::push_error("Failed to hardcoded defines!");
+ err = FAILED;
+ }
+ return err;
+}
diff --git a/extension/src/LoadLocalisation.cpp b/extension/src/LoadLocalisation.cpp
index e80d996..b660435 100644
--- a/extension/src/LoadLocalisation.cpp
+++ b/extension/src/LoadLocalisation.cpp
@@ -31,7 +31,7 @@ LoadLocalisation::~LoadLocalisation() {
}
Error LoadLocalisation::_load_file_into_translation(String const& file_path, Ref<Translation> translation) {
- Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::ModeFlags::READ);
+ const 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 localisation file: ", file_path);
@@ -42,15 +42,18 @@ Error LoadLocalisation::_load_file_into_translation(String const& file_path, Ref
PackedStringArray line = file->get_csv_line();
line_number++;
if (line.size() < 2 || line[0].is_empty() || line[1].is_empty()) {
- if (!line[0].is_empty())
+ if (!line[0].is_empty()) {
UtilityFunctions::push_warning("Key \"", line[0], "\" missing value on line ", line_number, " in file: ", file_path);
- else if (line.size() >= 2 && !line[1].is_empty())
+ err = FAILED;
+ } else if (line.size() >= 2 && !line[1].is_empty()) {
UtilityFunctions::push_warning("Value \"", line[1], "\" missing key on line ", line_number, " in file: ", file_path);
+ err = FAILED;
+ }
continue;
}
translation->add_message(line[0], line[1].c_unescape());
}
- return OK;
+ return err;
}
Ref<Translation> LoadLocalisation::_get_translation(String const& locale) {
@@ -72,40 +75,38 @@ Error LoadLocalisation::load_file(String const& file_path, String const& locale)
* FS-18, FS-24, FS-25
*/
Error LoadLocalisation::load_locale_dir(String const& dir_path, String const& locale) {
+ if (!DirAccess::dir_exists_absolute(dir_path)) {
+ UtilityFunctions::push_error("Locale directory does not exist: ", dir_path);
+ return FAILED;
+ }
Ref<Translation> translation = _get_translation(locale);
- if (DirAccess::dir_exists_absolute(dir_path)) {
- Error err = OK;
- for (String const& file_name : DirAccess::get_files_at(dir_path)) {
- if (file_name.get_extension().to_lower() == "csv") {
- String file_path = dir_path.path_join(file_name);
- if (_load_file_into_translation(file_path, translation) != OK)
- err = FAILED;
- }
+ Error err = OK;
+ for (String const& file_name : DirAccess::get_files_at(dir_path)) {
+ if (file_name.get_extension().to_lower() == "csv") {
+ String file_path = dir_path.path_join(file_name);
+ if (_load_file_into_translation(file_path, translation) != OK)
+ err = FAILED;
}
- return err;
}
- UtilityFunctions::push_error("Locale directory does not exist: ", dir_path);
- return FAILED;
+ return err;
}
/* REQUIREMENTS
* FS-23
*/
Error LoadLocalisation::load_localisation_dir(String const& dir_path) {
- if (DirAccess::dir_exists_absolute(dir_path)) {
- TranslationServer* server = TranslationServer::get_singleton();
- Error err = OK;
- for (String const& locale_name : DirAccess::get_directories_at(dir_path)) {
- if (locale_name == server->standardize_locale(locale_name)) {
- if (load_locale_dir(dir_path.path_join(locale_name), locale_name) != OK)
- err = FAILED;
- } else {
- err = FAILED;
- UtilityFunctions::push_error("Invalid locale directory name: ", locale_name);
- }
- }
- return err;
+ if(!DirAccess::dir_exists_absolute(dir_path)) {
+ UtilityFunctions::push_error("Localisation directory does not exist: ", dir_path);
+ return FAILED;
+ }
+ TranslationServer* server = TranslationServer::get_singleton();
+ Error err = OK;
+ for (String const& locale_name : DirAccess::get_directories_at(dir_path)) {
+ if (locale_name != server->standardize_locale(locale_name))
+ UtilityFunctions::push_error("Invalid locale directory name: ", locale_name);
+ else if (load_locale_dir(dir_path.path_join(locale_name), locale_name) == OK)
+ continue;
+ err = FAILED;
}
- UtilityFunctions::push_error("Localisation directory does not exist: ", dir_path);
- return FAILED;
+ return err;
}
diff --git a/extension/src/Utilities.hpp b/extension/src/Utilities.hpp
new file mode 100644
index 0000000..16193e8
--- /dev/null
+++ b/extension/src/Utilities.hpp
@@ -0,0 +1,22 @@
+#pragma once
+
+#include <godot_cpp/variant/string.hpp>
+
+#include "openvic/Types.hpp"
+
+#define ERR(x) ((x) == SUCCESS ? OK : FAILED)
+
+namespace OpenVic {
+
+ inline char const* godot_to_c_string(godot::String const& str) {
+ return str.ascii().get_data();
+ }
+
+ inline std::string godot_to_std_string(godot::String const& str) {
+ return godot_to_c_string(str);
+ }
+
+ inline godot::String std_to_godot_string(std::string const& str) {
+ return str.c_str();
+ }
+}
diff --git a/extension/src/register_types.h b/extension/src/register_types.h
index cd20e8d..dd24689 100644
--- a/extension/src/register_types.h
+++ b/extension/src/register_types.h
@@ -3,4 +3,4 @@
#include <godot_cpp/godot.hpp>
void initialize_openvic_types(godot::ModuleInitializationLevel);
-void uninitialize_openvic_types(godot::ModuleInitializationLevel); \ No newline at end of file
+void uninitialize_openvic_types(godot::ModuleInitializationLevel);