aboutsummaryrefslogtreecommitdiff
path: root/extension/src/GameSingleton.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'extension/src/GameSingleton.cpp')
-rw-r--r--extension/src/GameSingleton.cpp268
1 files changed, 185 insertions, 83 deletions
diff --git a/extension/src/GameSingleton.cpp b/extension/src/GameSingleton.cpp
index 3811dea..3476ac5 100644
--- a/extension/src/GameSingleton.cpp
+++ b/extension/src/GameSingleton.cpp
@@ -17,15 +17,19 @@ void GameSingleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_province_identifier_file", "file_path"), &GameSingleton::load_province_identifier_file);
ClassDB::bind_method(D_METHOD("load_water_province_file", "file_path"), &GameSingleton::load_water_province_file);
ClassDB::bind_method(D_METHOD("load_region_file", "file_path"), &GameSingleton::load_region_file);
- ClassDB::bind_method(D_METHOD("load_province_shape_file", "file_path"), &GameSingleton::load_province_shape_file);
+ ClassDB::bind_method(D_METHOD("load_terrain_file", "file_path"), &GameSingleton::load_terrain_file);
+ ClassDB::bind_method(D_METHOD("load_map_images", "province_image_path", "terrain_image_path"), &GameSingleton::load_map_images);
ClassDB::bind_method(D_METHOD("setup"), &GameSingleton::setup);
ClassDB::bind_method(D_METHOD("get_province_index_from_uv_coords", "coords"), &GameSingleton::get_province_index_from_uv_coords);
ClassDB::bind_method(D_METHOD("get_province_info_from_index", "index"), &GameSingleton::get_province_info_from_index);
ClassDB::bind_method(D_METHOD("get_width"), &GameSingleton::get_width);
ClassDB::bind_method(D_METHOD("get_height"), &GameSingleton::get_height);
- ClassDB::bind_method(D_METHOD("get_province_index_images"), &GameSingleton::get_province_index_images);
- ClassDB::bind_method(D_METHOD("get_province_colour_image"), &GameSingleton::get_province_colour_image);
+ ClassDB::bind_method(D_METHOD("get_aspect_ratio"), &GameSingleton::get_aspect_ratio);
+ ClassDB::bind_method(D_METHOD("get_terrain_texture"), &GameSingleton::get_terrain_texture);
+ ClassDB::bind_method(D_METHOD("get_province_shape_image_subdivisions"), &GameSingleton::get_province_shape_image_subdivisions);
+ 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);
@@ -54,27 +58,31 @@ GameSingleton* GameSingleton::get_singleton() {
/* REQUIREMENTS:
* MAP-21, MAP-25
*/
-GameSingleton::GameSingleton() : game_manager{ [this]() { emit_signal("state_updated"); } } {
+GameSingleton::GameSingleton() : game_manager{ [this]() { emit_signal("state_updated"); } }, terrain_variants{ "terrain variants" } {
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()); });
+ static constexpr colour_t HIGH_ALPHA_VALUE = to_alpha_value(0.5f);
+ static constexpr colour_t LOW_ALPHA_VALUE = to_alpha_value(0.2f);
using mapmode_t = std::pair<std::string, Mapmode::colour_func_t>;
const std::vector<mapmode_t> mapmodes = {
- { "mapmode_province", [](Map const&, Province const& province) -> Province::colour_t { return province.get_colour(); } },
- { "mapmode_region", [](Map const&, Province const& province) -> Province::colour_t {
- Region const* region = province.get_region();
- if (region != nullptr) return region->get_colour();
- return province.get_colour();
+ { "mapmode_terrain", [](Map const&, Province const& province) -> colour_t {
+ return LOW_ALPHA_VALUE | (province.is_water() ? 0x4287F5 : 0x0D7017);
+ } },
+ { "mapmode_province", [](Map const&, Province const& province) -> colour_t {
+ return HIGH_ALPHA_VALUE | province.get_colour();
} },
- { "mapmode_terrain", [](Map const&, Province const& province) -> Province::colour_t {
- return province.is_water() ? 0x4287F5 : 0x0D7017;
+ { "mapmode_region", [](Map const&, Province const& province) -> colour_t {
+ Region const* region = province.get_region();
+ if (region != nullptr) return (0xCC << 24) | region->get_colour();
+ return NULL_COLOUR;
} },
- { "mapmode_index", [](Map const& map, Province const& province) -> Province::colour_t {
+ { "mapmode_index", [](Map const& map, Province const& province) -> colour_t {
const uint8_t f = static_cast<float>(province.get_index()) / static_cast<float>(map.get_province_count()) * 255.0f;
- return (f << 16) | (f << 8) | f;
+ return HIGH_ALPHA_VALUE | (f << 16) | (f << 8) | f;
} }
};
for (mapmode_t const& mapmode : mapmodes)
@@ -96,7 +104,7 @@ GameSingleton::~GameSingleton() {
singleton = nullptr;
}
-static Error load_json_file(String const& file_description, String const& file_path, Variant& result) {
+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);
@@ -119,10 +127,10 @@ static Error load_json_file(String const& file_description, String const& file_p
using parse_json_entry_func_t = std::function<godot::Error (godot::String const&, godot::Variant const&)>;
-static Error parse_json_dictionary_file(String const& file_description, String const& file_path,
+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);
+ 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) {
@@ -147,40 +155,38 @@ static Error parse_json_dictionary_file(String const& file_description, String c
return err;
}
-Error GameSingleton::_parse_province_identifier_entry(String const& identifier, Variant const& entry) {
- const Variant::Type type = entry.get_type();
- Province::colour_t colour = Province::NULL_COLOUR;
+static colour_t _parse_colour(Variant const& var) {
+ const Variant::Type type = var.get_type();
if (type == Variant::ARRAY) {
- Array const& colour_array = entry;
+ 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) {
- colour = Province::NULL_COLOUR;
- break;
- }
+ if (var.get_type() != Variant::FLOAT) return NULL_COLOUR;
const double colour_double = var;
- if (std::trunc(colour_double) != colour_double) {
- colour = Province::NULL_COLOUR;
- break;
- }
+ 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) {
- colour = Province::NULL_COLOUR;
- break;
- }
+ 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 = entry;
+ }
+ 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 (0 <= colour_int && colour_int <= 0xFFFFFF)
- colour = colour_int;
+ if (colour_int != NULL_COLOUR && colour_int <= MAX_COLOUR_RGB)
+ return colour_int;
}
}
- if (colour == Province::NULL_COLOUR) {
+ 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) {
UtilityFunctions::push_error("Invalid colour for province identifier \"", identifier, "\": ", entry);
return FAILED;
}
@@ -188,7 +194,7 @@ Error GameSingleton::_parse_province_identifier_entry(String const& identifier,
}
Error GameSingleton::load_province_identifier_file(String const& file_path) {
- const Error err = parse_json_dictionary_file("province identifier", file_path, "prov_",
+ 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);
});
@@ -208,7 +214,8 @@ Error GameSingleton::_parse_region_entry(String const& identifier, Variant const
if (type == Variant::STRING) {
String const& province_string = province_var;
province_identifiers.push_back(province_string.utf8().get_data());
- } else {
+ }
+ else {
UtilityFunctions::push_error("Invalid province identifier for region \"", identifier, "\": ", entry);
err = FAILED;
}
@@ -222,7 +229,7 @@ Error GameSingleton::_parse_region_entry(String const& identifier, Variant const
}
Error GameSingleton::load_region_file(String const& file_path) {
- const Error err = parse_json_dictionary_file("region", file_path, "region_",
+ 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);
});
@@ -230,53 +237,134 @@ Error GameSingleton::load_region_file(String const& file_path) {
return err;
}
-Error GameSingleton::load_province_shape_file(String const& file_path) {
- if (province_index_image[0].is_valid()) {
- UtilityFunctions::push_error("Province shape file has already been loaded, cannot load: ", file_path);
+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) {
+ UtilityFunctions::push_error("Invalid colour for terrain texture \"", identifier, "\": ", entry);
return FAILED;
}
- Ref<Image> province_shape_image;
- province_shape_image.instantiate();
- Error err = province_shape_image->load(file_path);
+ 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 province shape file: ", file_path);
+ UtilityFunctions::push_error("Failed to load terrain image: ", terrain_path);
return err;
}
- const int32_t width = province_shape_image->get_width();
- const 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);
+ return ERR(terrain_variants.add_item({ identifier.utf8().get_data(), colour, terrain_image }));
+}
+
+Error GameSingleton::load_terrain_file(String const& file_path) {
+ Error parse_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 (terrain_variants.get_item_count() == 0) parse_err = 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();
+ const Error texturearray_err = terrain_texture->create_from_images(terrain_images);
+ if (texturearray_err != OK) {
+ UtilityFunctions::push_error("Failed to create terrain texture array!");
+ return texturearray_err;
+ }
+ return parse_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 (width % image_width_divide != 0) {
- UtilityFunctions::push_error("Invalid width ", width, " (must be divisible by ", image_width_divide, ") for province shape file: ", file_path);
+ 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 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);
+ 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;
- err = ERR(game_manager.map.generate_province_index_image(width, height, province_shape_image->get_data().ptr()));
-
- std::vector<Province::index_t> const& province_index_data = game_manager.map.get_province_index_image();
- const int32_t divided_width = width / image_width_divide;
- for (int32_t i = 0; i < image_width_divide; ++i) {
- PackedByteArray index_data_array;
- index_data_array.resize(divided_width * height * sizeof(Province::index_t));
- for (int32_t y = 0; y < height; ++y)
- memcpy(index_data_array.ptrw() + y * divided_width * sizeof(Province::index_t),
- province_index_data.data() + y * width + i * divided_width,
- divided_width * sizeof(Province::index_t));
- province_index_image[i] = Image::create_from_data(divided_width, height, false, Image::FORMAT_RG8, index_data_array);
- if (province_index_image[i].is_null()) {
- UtilityFunctions::push_error("Failed to create province ID image #", i);
- err = FAILED;
+
+ // 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("");
+ err = FAILED;
+ }
+
if (update_colour_image() != OK) err = FAILED;
return err;
@@ -288,14 +376,15 @@ godot::Error GameSingleton::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);
+ 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 {
+ }
+ else {
Array const& array = json_var;
for (int64_t idx = 0; idx < array.size(); ++idx) {
Variant const& entry = array[idx];
@@ -340,7 +429,7 @@ Dictionary GameSingleton::get_province_info_from_index(int32_t index) const {
buildings_array.resize(buildings.size());
for (size_t idx = 0; idx < buildings.size(); ++idx) {
KEY(building) KEY(level) KEY(expansion_state) KEY(start_date) KEY(end_date) KEY(expansion_progress)
-
+
Dictionary building_dict;
Building const& building = buildings[idx];
building_dict[building_key] = building.get_identifier().c_str();
@@ -366,27 +455,36 @@ int32_t GameSingleton::get_height() const {
return game_manager.map.get_height();
}
-Array GameSingleton::get_province_index_images() const {
- Array ret;
- for (int i = 0; i < image_width_divide; ++i)
- ret.append(province_index_image[i]);
- return ret;
+float GameSingleton::get_aspect_ratio() const {
+ return static_cast<float>(get_width()) / static_cast<float>(get_height());
+}
+
+Ref<Texture> GameSingleton::get_terrain_texture() const {
+ return terrain_texture;
+}
+
+Vector2i GameSingleton::get_province_shape_image_subdivisions() const {
+ return image_subdivisions;
+}
+
+Ref<Texture> GameSingleton::get_province_shape_texture() const {
+ return province_shape_texture;
}
-Ref<Image> GameSingleton::get_province_colour_image() const {
- return province_colour_image;
+Ref<Texture> GameSingleton::get_province_colour_texture() const {
+ return province_colour_texture;
}
Error GameSingleton::update_colour_image() {
static PackedByteArray colour_data_array;
- static constexpr int64_t colour_data_array_size = (Province::MAX_INDEX + 1) * 4;
+ static constexpr int64_t colour_data_array_size = (MAX_INDEX + 1) * Map::MAPMODE_COLOUR_SIZE;
colour_data_array.resize(colour_data_array_size);
Error err = OK;
if (game_manager.map.generate_mapmode_colours(mapmode_index, colour_data_array.ptrw()) != SUCCESS)
err = FAILED;
- static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(Province::index_t) * 4);
+ static constexpr int32_t PROVINCE_INDEX_SQRT = 1 << (sizeof(index_t) * 4);
if (province_colour_image.is_null())
province_colour_image.instantiate();
province_colour_image->set_data(PROVINCE_INDEX_SQRT, PROVINCE_INDEX_SQRT,
@@ -395,6 +493,10 @@ Error GameSingleton::update_colour_image() {
UtilityFunctions::push_error("Failed to update province colour image");
return FAILED;
}
+ if (province_colour_texture.is_null())
+ province_colour_texture = ImageTexture::create_from_image(province_colour_image);
+ else
+ province_colour_texture->update(province_colour_image);
return err;
}