aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hop311 <hop3114@gmail.com>2023-09-24 23:42:48 +0200
committer Hop311 <hop3114@gmail.com>2023-09-25 00:12:36 +0200
commitbbfa8faf5337ebdff60ef2106074417aa628eca1 (patch)
tree99604a6bc9a99f1c68232cfff84f01192991f1a9
parent3714db86f7c52674e044566096f389660a67a039 (diff)
Adding terrain image/type loading
-rw-r--r--src/openvic-simulation/GameManager.cpp8
-rw-r--r--src/openvic-simulation/GameManager.hpp3
-rw-r--r--src/openvic-simulation/Modifier.cpp41
-rw-r--r--src/openvic-simulation/Modifier.hpp7
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp19
-rw-r--r--src/openvic-simulation/economy/Good.cpp4
-rw-r--r--src/openvic-simulation/economy/ProductionType.cpp4
-rw-r--r--src/openvic-simulation/map/Map.cpp239
-rw-r--r--src/openvic-simulation/map/Map.hpp24
-rw-r--r--src/openvic-simulation/map/TerrainType.cpp190
-rw-r--r--src/openvic-simulation/map/TerrainType.hpp68
-rw-r--r--src/openvic-simulation/types/IdentifierRegistry.hpp1
-rw-r--r--src/openvic-simulation/utility/BMP.cpp93
-rw-r--r--src/openvic-simulation/utility/BMP.hpp17
14 files changed, 564 insertions, 154 deletions
diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp
index 249808d..d969132 100644
--- a/src/openvic-simulation/GameManager.cpp
+++ b/src/openvic-simulation/GameManager.cpp
@@ -70,6 +70,14 @@ UnitManager const& GameManager::get_unit_manager() const {
return unit_manager;
}
+ModifierManager& GameManager::get_modifier_manager() {
+ return modifier_manager;
+}
+
+ModifierManager const& GameManager::get_modifier_manager() const{
+ return modifier_manager;
+}
+
GameAdvancementHook& GameManager::get_clock() {
return clock;
}
diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp
index eb73331..d3f5656 100644
--- a/src/openvic-simulation/GameManager.hpp
+++ b/src/openvic-simulation/GameManager.hpp
@@ -24,6 +24,7 @@ namespace OpenVic {
IssueManager issue_manager;
ProductionTypeManager production_type_manager;
UnitManager unit_manager;
+ ModifierManager modifier_manager;
GameAdvancementHook clock;
time_t session_start; /* SS-54, as well as allowing time-tracking */
@@ -54,6 +55,8 @@ namespace OpenVic {
ProductionTypeManager const& get_production_type_manager() const;
UnitManager& get_unit_manager();
UnitManager const& get_unit_manager() const;
+ ModifierManager& get_modifier_manager();
+ ModifierManager const& get_modifier_manager() const;
GameAdvancementHook& get_clock();
GameAdvancementHook const& get_clock() const;
diff --git a/src/openvic-simulation/Modifier.cpp b/src/openvic-simulation/Modifier.cpp
index 7871692..9fa0691 100644
--- a/src/openvic-simulation/Modifier.cpp
+++ b/src/openvic-simulation/Modifier.cpp
@@ -15,6 +15,9 @@ ModifierValue::ModifierValue(effect_map_t&& new_values) : values { std::move(new
ModifierValue::ModifierValue(ModifierValue const&) = default;
ModifierValue::ModifierValue(ModifierValue&&) = default;
+ModifierValue& ModifierValue::operator=(ModifierValue const&) = default;
+ModifierValue& ModifierValue::operator=(ModifierValue&&) = default;
+
void ModifierValue::trim() {
std::erase_if(values, [](effect_map_t::value_type const& value) -> bool {
return value.second == fixed_point_t::_0();
@@ -111,19 +114,39 @@ bool ModifierManager::add_modifier(const std::string_view identifier, ModifierVa
return modifiers.add_item({ identifier, std::move(values), icon });
}
-node_callback_t ModifierManager::expect_modifier_value(callback_t<ModifierValue&&> callback) const {
- return [this, callback](ast::NodeCPtr root) -> bool {
+bool ModifierManager::setup_modifier_effects() {
+ bool ret = true;
+
+ ret &= add_modifier_effect("movement_cost", false);
+ ret &= add_modifier_effect("farm_rgo_size", true);
+ ret &= add_modifier_effect("farm_rgo_eff", true);
+ ret &= add_modifier_effect("mine_rgo_size", true);
+ ret &= add_modifier_effect("mine_rgo_eff", true);
+ ret &= add_modifier_effect("min_build_railroad", false);
+ ret &= add_modifier_effect("supply_limit", true);
+ ret &= add_modifier_effect("combat_width", false);
+ ret &= add_modifier_effect("defence", true);
+
+ modifier_effects.lock();
+ return ret;
+}
+
+node_callback_t ModifierManager::expect_modifier_value(callback_t<ModifierValue&&> callback, key_value_callback_t default_callback) const {
+ return [this, callback, default_callback](ast::NodeCPtr root) -> bool {
ModifierValue modifier;
bool ret = expect_dictionary(
- [this, &modifier](std::string_view key, ast::NodeCPtr value) -> bool {
+ [this, &modifier, default_callback](std::string_view key, ast::NodeCPtr value) -> bool {
ModifierEffect const* effect = get_modifier_effect_by_identifier(key);
if (effect != nullptr) {
- return expect_fixed_point(
- assign_variable_callback(modifier.values[effect])
- )(value);
+ if (modifier.values.find(effect) == modifier.values.end()) {
+ return expect_fixed_point(
+ assign_variable_callback(modifier.values[effect])
+ )(value);
+ }
+ Logger::error("Duplicate modifier effect: ", key);
+ return false;
}
- Logger::error("Invalid modifier effect: ", key);
- return false;
+ return default_callback(key, value);
}
)(root);
ret &= callback(std::move(modifier));
@@ -138,4 +161,4 @@ namespace OpenVic { //so the compiler shuts up
}
return stream;
}
-} \ No newline at end of file
+}
diff --git a/src/openvic-simulation/Modifier.hpp b/src/openvic-simulation/Modifier.hpp
index 6db2c0c..ea302b2 100644
--- a/src/openvic-simulation/Modifier.hpp
+++ b/src/openvic-simulation/Modifier.hpp
@@ -37,6 +37,9 @@ namespace OpenVic {
ModifierValue(ModifierValue const&);
ModifierValue(ModifierValue&&);
+ ModifierValue& operator=(ModifierValue const&);
+ ModifierValue& operator=(ModifierValue&&);
+
/* Removes effect entries with a value of zero. */
void trim();
size_t get_effect_count() const;
@@ -98,6 +101,8 @@ namespace OpenVic {
bool add_modifier(const std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon);
IDENTIFIER_REGISTRY_ACCESSORS(Modifier, modifier)
- NodeTools::node_callback_t expect_modifier_value(NodeTools::callback_t<ModifierValue&&> callback) const;
+ bool setup_modifier_effects();
+
+ NodeTools::node_callback_t expect_modifier_value(NodeTools::callback_t<ModifierValue&&> callback, NodeTools::key_value_callback_t default_callback) const;
};
}
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp
index 6396967..530aa76 100644
--- a/src/openvic-simulation/dataloader/Dataloader.cpp
+++ b/src/openvic-simulation/dataloader/Dataloader.cpp
@@ -255,6 +255,21 @@ bool Dataloader::_load_map_dir(GameManager& game_manager, fs::path const& map_di
}
map.lock_water_provinces();
+ if (!map.get_terrain_type_manager().load_terrain_types(game_manager.get_modifier_manager(), _parse_defines(lookup_file(map_directory / terrain_definition)).get_file_node())) {
+ Logger::error("Failed to load terrain types!");
+ ret = false;
+ }
+
+ if (!map.load_map_images(lookup_file(map_directory / provinces), lookup_file(map_directory / terrain), false)) {
+ Logger::error("Failed to load map images!");
+ ret = false;
+ }
+
+ if (!map.generate_and_load_province_adjacencies(_parse_csv(lookup_file(map_directory / adjacencies)).get_lines())) {
+ Logger::error("Failed to generate and load province adjacencies!");
+ ret = false;
+ }
+
return ret;
}
@@ -272,6 +287,10 @@ bool Dataloader::load_defines(GameManager& game_manager) const {
bool ret = true;
+ if (!game_manager.get_modifier_manager().setup_modifier_effects()) {
+ Logger::error("Failed to set up modifier effects!");
+ ret = false;
+ }
if (!game_manager.get_good_manager().load_goods_file(_parse_defines(lookup_file(goods_file)).get_file_node())) {
Logger::error("Failed to load goods!");
ret = false;
diff --git a/src/openvic-simulation/economy/Good.cpp b/src/openvic-simulation/economy/Good.cpp
index f1da8bd..8a04e39 100644
--- a/src/openvic-simulation/economy/Good.cpp
+++ b/src/openvic-simulation/economy/Good.cpp
@@ -112,8 +112,8 @@ bool GoodManager::load_goods_file(ast::NodeCPtr root) {
[this, good_category](std::string_view key, ast::NodeCPtr value) -> bool {
colour_t colour = NULL_COLOUR;
Good::price_t base_price;
- bool available_from_start, tradeable = true;
- bool money, overseas_penalty = false;
+ bool available_from_start = true, tradeable = true;
+ bool money = false, overseas_penalty = false;
bool ret = expect_dictionary_keys(
"color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)),
diff --git a/src/openvic-simulation/economy/ProductionType.cpp b/src/openvic-simulation/economy/ProductionType.cpp
index 4ea7967..40ac2a3 100644
--- a/src/openvic-simulation/economy/ProductionType.cpp
+++ b/src/openvic-simulation/economy/ProductionType.cpp
@@ -264,10 +264,12 @@ bool ProductionTypeManager::load_production_types_file(GoodManager& good_manager
}
}
- return ret & PARSE_NODE(node) & add_production_type(
+ ret &= PARSE_NODE(node);
+ ret &= add_production_type(
key, owner, employees, type, workforce, input_goods, output_goods, value,
bonuses, efficiency, coastal, farm, mine, good_manager
);
+ return ret;
}
)(root);
diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp
index 6cafb57..203e100 100644
--- a/src/openvic-simulation/map/Map.cpp
+++ b/src/openvic-simulation/map/Map.cpp
@@ -4,6 +4,7 @@
#include <unordered_set>
#include "openvic-simulation/economy/Good.hpp"
+#include "openvic-simulation/utility/BMP.hpp"
#include "openvic-simulation/utility/Logger.hpp"
using namespace OpenVic;
@@ -182,116 +183,6 @@ Province const* Map::get_selected_province() const {
return get_province_by_index(get_selected_province_index());
}
-static colour_t colour_at(uint8_t const* colour_data, int32_t idx) {
- idx *= 3;
- return (colour_data[idx] << 16) | (colour_data[idx + 1] << 8) | colour_data[idx + 2];
-}
-
-bool Map::generate_province_shape_image(size_t new_width, size_t new_height, uint8_t const* colour_data,
- uint8_t const* terrain_data, terrain_variant_map_t const& terrain_variant_map, bool detailed_errors) {
- if (!province_shape_image.empty()) {
- Logger::error("Province index image has already been generated!");
- return false;
- }
- if (!provinces.is_locked()) {
- Logger::error("Province index image cannot be generated until after provinces are locked!");
- return false;
- }
- if (new_width < 1 || new_height < 1) {
- Logger::error("Invalid province image dimensions: ", new_width, "x", new_height);
- return false;
- }
- if (colour_data == nullptr) {
- Logger::error("Province colour data pointer is null!");
- return false;
- }
- if (terrain_data == nullptr) {
- Logger::error("Province terrain data pointer is null!");
- return false;
- }
- width = new_width;
- height = new_height;
- province_shape_image.resize(width * height);
-
- std::vector<bool> province_checklist(provinces.size());
- bool ret = true;
- std::unordered_set<colour_t> unrecognised_province_colours, unrecognised_terrain_colours;
-
- for (int32_t y = 0; y < height; ++y) {
- for (int32_t x = 0; x < width; ++x) {
- const int32_t idx = x + y * width;
-
- const colour_t terrain_colour = colour_at(terrain_data, idx);
- const terrain_variant_map_t::const_iterator it = terrain_variant_map.find(terrain_colour);
- if (it != terrain_variant_map.end()) province_shape_image[idx].terrain = it->second;
- else {
- if (unrecognised_terrain_colours.find(terrain_colour) == unrecognised_terrain_colours.end()) {
- unrecognised_terrain_colours.insert(terrain_colour);
- if (detailed_errors) {
- Logger::warning("Unrecognised terrain colour ", colour_to_hex_string(terrain_colour),
- " at (", x, ", ", y, ")");
- }
- }
- province_shape_image[idx].terrain = 0;
- }
-
- const colour_t province_colour = colour_at(colour_data, idx);
- if (x > 0) {
- const int32_t jdx = idx - 1;
- if (colour_at(colour_data, jdx) == province_colour) {
- province_shape_image[idx].index = province_shape_image[jdx].index;
- continue;
- }
- }
- if (y > 0) {
- const int32_t jdx = idx - width;
- if (colour_at(colour_data, jdx) == province_colour) {
- province_shape_image[idx].index = province_shape_image[jdx].index;
- continue;
- }
- }
- const Province::index_t index = get_index_from_colour(province_colour);
- if (index != Province::NULL_INDEX) {
- province_checklist[index - 1] = true;
- province_shape_image[idx].index = index;
- continue;
- }
- if (unrecognised_province_colours.find(province_colour) == unrecognised_province_colours.end()) {
- unrecognised_province_colours.insert(province_colour);
- if (detailed_errors) {
- Logger::warning("Unrecognised province colour ", colour_to_hex_string(province_colour),
- " at (", x, ", ", y, ")");
- }
- }
- province_shape_image[idx].index = Province::NULL_INDEX;
- }
- }
- if (!unrecognised_province_colours.empty()) {
- Logger::warning("Province image contains ", unrecognised_province_colours.size(), " unrecognised province colours");
- }
- if (!unrecognised_terrain_colours.empty()) {
- Logger::warning("Terrain image contains ", unrecognised_terrain_colours.size(), " unrecognised terrain colours");
- }
-
- size_t missing = 0;
- for (size_t idx = 0; idx < province_checklist.size(); ++idx) {
- if (!province_checklist[idx]) {
- if (detailed_errors) {
- Logger::error("Province missing from shape image: ", provinces.get_item_by_index(idx)->to_string());
- }
- missing++;
- }
- }
- if (missing > 0) {
- Logger::error("Province image is missing ", missing, " province colours");
- ret = false;
- }
-
- ret &= _generate_province_adjacencies();
-
- return ret;
-}
-
size_t Map::get_width() const {
return width;
}
@@ -304,6 +195,14 @@ std::vector<Map::shape_pixel_t> const& Map::get_province_shape_image() const {
return province_shape_image;
}
+TerrainTypeManager& Map::get_terrain_type_manager() {
+ return terrain_type_manager;
+}
+
+TerrainTypeManager const& Map::get_terrain_type_manager() const {
+ return terrain_type_manager;
+}
+
bool Map::add_mapmode(const std::string_view identifier, Mapmode::colour_func_t colour_func) {
if (identifier.empty()) {
Logger::error("Invalid mapmode identifier - empty!");
@@ -443,7 +342,7 @@ bool Map::load_province_definitions(std::vector<LineObject> const& lines) {
[this, &ret](LineObject const& line) -> void {
const std::string_view identifier = line.get_value_for(0);
if (!identifier.empty()) {
- colour_t colour;
+ colour_t colour = NULL_COLOUR;
if (!parse_province_colour(colour,
{ line.get_value_for(1), line.get_value_for(2), line.get_value_for(3) }
)) {
@@ -504,6 +403,118 @@ bool Map::load_region_file(ast::NodeCPtr root) {
return ret;
}
+static constexpr colour_t colour_at(uint8_t const* colour_data, int32_t idx) {
+ idx *= 3;
+ return (colour_data[idx + 2] << 16) | (colour_data[idx + 1] << 8) | colour_data[idx];
+}
+
+bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain_path, bool detailed_errors) {
+ if (!provinces.is_locked()) {
+ Logger::error("Province index image cannot be generated until after provinces are locked!");
+ return false;
+ }
+ if (!terrain_type_manager.terrain_type_mappings_are_locked()) {
+ Logger::error("Province index image cannot be generated until after terrain type mappings are locked!");
+ return false;
+ }
+
+ BMP province_bmp;
+ if (!(province_bmp.open(province_path) && province_bmp.read_header() && province_bmp.read_pixel_data())) {
+ Logger::error("Failed to read BMP for compatibility mode province image: ", province_path);
+ return false;
+ }
+ static constexpr uint16_t expected_province_bpp = 24;
+ if (province_bmp.get_bits_per_pixel() != expected_province_bpp) {
+ Logger::error("Invalid province BMP bits per pixel: ", province_bmp.get_bits_per_pixel(), " (expected ", expected_province_bpp, ")");
+ return false;
+ }
+
+ BMP terrain_bmp;
+ if (!(terrain_bmp.open(terrain_path) && terrain_bmp.read_header() && terrain_bmp.read_pixel_data())) {
+ Logger::error("Failed to read BMP for compatibility mode terrain image: ", terrain_path);
+ return false;
+ }
+ static constexpr uint16_t expected_terrain_bpp = 8;
+ if (terrain_bmp.get_bits_per_pixel() != expected_terrain_bpp) {
+ Logger::error("Invalid terrain BMP bits per pixel: ", terrain_bmp.get_bits_per_pixel(), " (expected ", expected_terrain_bpp, ")");
+ return false;
+ }
+
+ if (province_bmp.get_width() != terrain_bmp.get_width() || province_bmp.get_height() != terrain_bmp.get_height()) {
+ Logger::error("Mismatched province and terrain BMP dims: ", province_bmp.get_width(), "x", province_bmp.get_height(), " vs ", terrain_bmp.get_width(), "x", terrain_bmp.get_height());
+ return false;
+ }
+
+ width = province_bmp.get_width();
+ height = province_bmp.get_height();
+ province_shape_image.resize(width * height);
+
+ uint8_t const* province_data = province_bmp.get_pixel_data().data();
+ uint8_t const* terrain_data = terrain_bmp.get_pixel_data().data();
+
+ std::vector<bool> province_checklist(provinces.size());
+ bool ret = true;
+ std::unordered_set<colour_t> unrecognised_province_colours;
+
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; ++x) {
+ const size_t idx = x + y * width;
+
+ province_shape_image[idx].terrain = terrain_data[idx] < terrain_type_manager.get_terrain_texture_limit() ? terrain_data[idx] + 1 : 0;
+
+ const colour_t province_colour = colour_at(province_data, idx);
+ if (x > 0) {
+ const size_t jdx = idx - 1;
+ if (colour_at(province_data, jdx) == province_colour) {
+ province_shape_image[idx].index = province_shape_image[jdx].index;
+ continue;
+ }
+ }
+ if (y > 0) {
+ const size_t jdx = idx - width;
+ if (colour_at(province_data, jdx) == province_colour) {
+ province_shape_image[idx].index = province_shape_image[jdx].index;
+ continue;
+ }
+ }
+ const Province::index_t index = get_index_from_colour(province_colour);
+ if (index != Province::NULL_INDEX) {
+ province_checklist[index - 1] = true;
+ province_shape_image[idx].index = index;
+ continue;
+ }
+ if (unrecognised_province_colours.find(province_colour) == unrecognised_province_colours.end()) {
+ unrecognised_province_colours.insert(province_colour);
+ if (detailed_errors) {
+ Logger::warning("Unrecognised province colour ", colour_to_hex_string(province_colour),
+ " at (", x, ", ", y, ")");
+ }
+ }
+ province_shape_image[idx].index = Province::NULL_INDEX;
+ }
+ }
+
+ if (!unrecognised_province_colours.empty()) {
+ Logger::warning("Province image contains ", unrecognised_province_colours.size(), " unrecognised province colours");
+ }
+
+ size_t missing = 0;
+ for (size_t idx = 0; idx < province_checklist.size(); ++idx) {
+ if (!province_checklist[idx]) {
+ if (detailed_errors) {
+ Logger::error("Province missing from shape image: ", provinces.get_item_by_index(idx)->to_string());
+ }
+ missing++;
+ }
+ }
+ if (missing > 0) {
+ Logger::error("Province image is missing ", missing, " province colours");
+ ret = false;
+ }
+
+ return ret;
+}
+
/* REQUIREMENTS:
* MAP-19, MAP-84
*/
@@ -531,3 +542,9 @@ bool Map::_generate_province_adjacencies() {
return changed;
}
+
+bool Map::generate_and_load_province_adjacencies(std::vector<ovdl::csv::LineObject> const& additional_adjacencies) {
+ bool ret = _generate_province_adjacencies();
+ // TODO - read additional adjacencies
+ return ret;
+}
diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp
index f81b9c1..99c0bce 100644
--- a/src/openvic-simulation/map/Map.hpp
+++ b/src/openvic-simulation/map/Map.hpp
@@ -1,12 +1,15 @@
#pragma once
+#include <filesystem>
#include <functional>
#include <openvic-dataloader/csv/LineObject.hpp>
#include "openvic-simulation/map/Region.hpp"
+#include "openvic-simulation/map/TerrainType.hpp"
namespace OpenVic {
+ namespace fs = std::filesystem;
struct Mapmode : HasIdentifier {
friend struct Map;
@@ -35,14 +38,12 @@ namespace OpenVic {
* MAP-4
*/
struct Map {
- using terrain_t = uint8_t;
- using terrain_variant_map_t = std::map<colour_t, terrain_t>;
#pragma pack(push, 1)
/* Used to represent tightly packed 3-byte integer pixel information. */
struct shape_pixel_t {
Province::index_t index;
- terrain_t terrain;
+ TerrainTypeMapping::index_t terrain;
};
#pragma pack(pop)
private:
@@ -52,13 +53,14 @@ namespace OpenVic {
IdentifierRegistry<Region> regions;
IdentifierRegistry<Mapmode> mapmodes;
ProvinceSet water_provinces;
+ TerrainTypeManager terrain_type_manager;
size_t width = 0, height = 0;
std::vector<shape_pixel_t> province_shape_image;
colour_index_map_t colour_index_map;
+
Province::index_t max_provinces = Province::MAX_INDEX;
Province::index_t selected_province = Province::NULL_INDEX;
-
Pop::pop_size_t highest_province_population, total_map_population;
Province::index_t get_index_from_colour(colour_t colour) const;
@@ -70,12 +72,10 @@ namespace OpenVic {
bool add_province(const std::string_view identifier, colour_t colour);
IDENTIFIER_REGISTRY_ACCESSORS(Province, province)
IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(Province, province)
+
bool set_water_province(const std::string_view identifier);
bool set_water_province_list(std::vector<std::string_view> const& list);
void lock_water_provinces();
- bool add_region(const std::string_view identifier, std::vector<std::string_view> const& province_identifiers);
- IDENTIFIER_REGISTRY_ACCESSORS(Region, region)
- IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(Region, region)
Province* get_province_by_index(Province::index_t index);
Province const* get_province_by_index(Province::index_t index) const;
@@ -86,11 +86,15 @@ namespace OpenVic {
Province::index_t get_selected_province_index() const;
Province const* get_selected_province() const;
- bool generate_province_shape_image(size_t new_width, size_t new_height, uint8_t const* colour_data,
- uint8_t const* terrain_data, terrain_variant_map_t const& terrain_variant_map, bool detailed_errors);
size_t get_width() const;
size_t get_height() const;
std::vector<shape_pixel_t> const& get_province_shape_image() const;
+ TerrainTypeManager& get_terrain_type_manager();
+ TerrainTypeManager const& get_terrain_type_manager() const;
+
+ bool add_region(const std::string_view identifier, std::vector<std::string_view> const& province_identifiers);
+ IDENTIFIER_REGISTRY_ACCESSORS(Region, region)
+ IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(Region, region)
bool add_mapmode(const std::string_view identifier, Mapmode::colour_func_t colour_func);
IDENTIFIER_REGISTRY_ACCESSORS(Mapmode, mapmode)
@@ -111,5 +115,7 @@ namespace OpenVic {
bool load_province_definitions(std::vector<ovdl::csv::LineObject> const& lines);
bool load_province_positions(BuildingManager const& building_manager, ast::NodeCPtr root);
bool load_region_file(ast::NodeCPtr root);
+ bool load_map_images(fs::path const& province_path, fs::path const& terrain_path, bool detailed_errors);
+ bool generate_and_load_province_adjacencies(std::vector<ovdl::csv::LineObject> const& additional_adjacencies);
};
}
diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp
new file mode 100644
index 0000000..8464421
--- /dev/null
+++ b/src/openvic-simulation/map/TerrainType.cpp
@@ -0,0 +1,190 @@
+#include "TerrainType.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::NodeTools;
+
+TerrainType::TerrainType(const std::string_view new_identifier, colour_t new_colour, ModifierValue&& new_values, bool new_is_water)
+ : HasIdentifierAndColour { new_identifier, new_colour, true, false }, ModifierValue { std::move(new_values) }, is_water { new_is_water } {}
+
+bool TerrainType::get_is_water() const {
+ return is_water;
+}
+
+TerrainTypeMapping::TerrainTypeMapping(const std::string_view new_identifier, TerrainType const& new_type,
+ std::vector<index_t>&& new_terrain_indicies, index_t new_priority, bool new_has_texture)
+ : HasIdentifier { new_identifier }, type { new_type }, terrain_indicies { std::move(new_terrain_indicies) },
+ priority { new_priority }, has_texture { new_has_texture } {}
+
+TerrainType const& TerrainTypeMapping::get_type() const {
+ return type;
+}
+
+std::vector<TerrainTypeMapping::index_t> const& TerrainTypeMapping::get_terrain_indicies() const {
+ return terrain_indicies;
+}
+
+TerrainTypeMapping::index_t TerrainTypeMapping::get_priority() const {
+ return priority;
+}
+
+bool TerrainTypeMapping::get_has_texture() const {
+ return has_texture;
+}
+
+TerrainTypeManager::TerrainTypeManager() : terrain_types { "terrain types" }, terrain_type_mappings { "terrain type mappings" } {}
+
+bool TerrainTypeManager::add_terrain_type(const std::string_view identifier, colour_t colour, ModifierValue&& values, bool is_water) {
+ if (identifier.empty()) {
+ Logger::error("Invalid terrain type identifier - empty!");
+ return false;
+ }
+ if (colour > MAX_COLOUR_RGB) {
+ Logger::error("Invalid terrain type colour for ", identifier, ": ", colour_to_hex_string(colour));
+ return false;
+ }
+ return terrain_types.add_item({ identifier, colour, std::move(values), is_water });
+}
+
+bool TerrainTypeManager::add_terrain_type_mapping(const std::string_view identifier, TerrainType const* type,
+ std::vector<TerrainTypeMapping::index_t>&& terrain_indicies, TerrainTypeMapping::index_t priority, bool has_texture) {
+ if (!terrain_types.is_locked()) {
+ Logger::error("Cannot register terrain type mappings until terrain types are locked!");
+ return false;
+ }
+ if (identifier.empty()) {
+ Logger::error("Invalid terrain type mapping identifier - empty!");
+ return false;
+ }
+ if (type == nullptr) {
+ Logger::error("Null terrain type for mapping ", identifier);
+ return false;
+ }
+ return terrain_type_mappings.add_item({ identifier, *type, std::move(terrain_indicies), priority, has_texture });
+}
+
+bool TerrainTypeManager::_load_terrain_type_categories(ModifierManager const& modifier_manager, ast::NodeCPtr root) {
+ const bool ret = expect_dictionary_reserve_length(terrain_types,
+ [this, &modifier_manager](std::string_view type_key, ast::NodeCPtr type_node) -> bool {
+ ModifierValue values;
+ colour_t colour = NULL_COLOUR;
+ bool is_water = false;
+ bool has_colour = false, has_is_water = false;
+ bool ret = modifier_manager.expect_modifier_value(move_variable_callback(values),
+ [&colour, &has_colour, &is_water, &has_is_water](std::string_view key, ast::NodeCPtr value) -> bool {
+ if (key == "color") {
+ if (!has_colour) {
+ has_colour = true;
+ return expect_colour(assign_variable_callback(colour))(value);
+ } else {
+ Logger::error("Duplicate terrain type colour key!");
+ return false;
+ }
+ } else if (key == "is_water") {
+ if (!has_is_water) {
+ has_is_water = true;
+ return expect_bool(assign_variable_callback(is_water))(value);
+ } else {
+ Logger::error("Duplicate terrain type is_water key!");
+ return false;
+ }
+ } else {
+ Logger::error("Invalid terrain type entry key: ", key);
+ return false;
+ }
+ }
+ )(type_node);
+ if (!has_colour) {
+ Logger::error("Terrain type missing color key: ", type_key);
+ ret = false;
+ }
+ ret &= add_terrain_type(type_key, colour, std::move(values), is_water);
+ return ret;
+ }
+ )(root);
+ terrain_types.lock();
+ return ret;
+}
+
+bool TerrainTypeManager::_load_terrain_type_mapping(std::string_view mapping_key, ast::NodeCPtr mapping_value) {
+ TerrainType const* type = nullptr;
+ std::vector<TerrainTypeMapping::index_t> terrain_indicies;
+ TerrainTypeMapping::index_t priority = 0;
+ bool has_texture = true;
+
+ bool ret = expect_dictionary_keys(
+ "type", ONE_EXACTLY, expect_terrain_type_identifier(assign_variable_callback_pointer(type)),
+ "color", ONE_EXACTLY, expect_list_reserve_length(terrain_indicies, expect_uint(
+ [&terrain_indicies](uint64_t val) -> bool {
+ if (val <= 1 << 8 * sizeof(TerrainTypeMapping::index_t)) {
+ TerrainTypeMapping::index_t index = val;
+ if (std::find(terrain_indicies.begin(), terrain_indicies.end(), index) == terrain_indicies.end()) {
+ terrain_indicies.push_back(val);
+ return true;
+ }
+ Logger::error("Repeat terrain type mapping index: ", val);
+ return false;
+ }
+ Logger::error("Index too big for terrain type mapping index: ", val);
+ return false;
+ }
+ )),
+ "priority", ZERO_OR_ONE, expect_uint(assign_variable_callback_uint("terrain type mapping priority", priority)),
+ "has_texture", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_texture))
+ )(mapping_value);
+ if (has_texture) {
+ if (++terrain_texture_count == terrain_texture_limit + 1) {
+ Logger::error("More terrain textures than limit!");
+ ret = false;
+ }
+ }
+ ret &= add_terrain_type_mapping(mapping_key, type, std::move(terrain_indicies), priority, has_texture);
+ return true;
+}
+
+TerrainTypeMapping::index_t TerrainTypeManager::get_terrain_texture_limit() const {
+ return terrain_texture_limit;
+}
+
+bool TerrainTypeManager::load_terrain_types(ModifierManager const& modifier_manager, ast::NodeCPtr root) {
+ bool terrain = false, categories = false;
+ bool ret = expect_dictionary_and_length(
+ [this](size_t size) -> size_t {
+ terrain_type_mappings.reserve(size - 2);
+ return size;
+ },
+ [this, &terrain, &categories, &modifier_manager](std::string_view key, ast::NodeCPtr value) -> bool {
+ if (key == "terrain") {
+ if (!terrain) {
+ terrain = true;
+ return expect_uint(assign_variable_callback_uint("terrain texture limit", terrain_texture_limit))(value);
+ } else {
+ Logger::error("Duplicate terrain key!");
+ return false;
+ }
+ } else if (key == "categories") {
+ if (!categories) {
+ categories = true;
+ return _load_terrain_type_categories(modifier_manager, value);
+ } else {
+ Logger::error("Duplicate categories key!");
+ return false;
+ }
+ } else if (terrain && categories) {
+ return _load_terrain_type_mapping(key, value);
+ } else {
+ Logger::error("Cannot define terrain type mapping before terrain and categories keys: ", key);
+ return false;
+ }
+ }
+ )(root);
+ if (!terrain) {
+ Logger::error("Missing expected key: \"terrain\"");
+ ret = false;
+ }
+ if (!categories) {
+ Logger::error("Missing expected key: \"categories\"");
+ ret = false;
+ }
+ terrain_type_mappings.lock();
+ return ret;
+}
diff --git a/src/openvic-simulation/map/TerrainType.hpp b/src/openvic-simulation/map/TerrainType.hpp
new file mode 100644
index 0000000..48b811a
--- /dev/null
+++ b/src/openvic-simulation/map/TerrainType.hpp
@@ -0,0 +1,68 @@
+#pragma once
+
+#include "openvic-simulation/Modifier.hpp"
+
+namespace OpenVic {
+ struct TerrainTypeManager;
+
+ struct TerrainType : HasIdentifierAndColour, ModifierValue {
+ friend struct TerrainTypeManager;
+
+ private:
+ const bool is_water;
+
+ TerrainType(const std::string_view new_identifier, colour_t new_colour, ModifierValue&& new_values, bool new_is_water);
+
+ public:
+ TerrainType(TerrainType&&) = default;
+
+ bool get_is_water() const;
+ };
+
+ struct TerrainTypeMapping : HasIdentifier {
+ friend struct TerrainTypeManager;
+
+ using index_t = uint8_t;
+
+ private:
+ TerrainType const& type;
+ const std::vector<index_t> terrain_indicies;
+ const index_t priority;
+ const bool has_texture;
+
+ TerrainTypeMapping(const std::string_view new_identifier, TerrainType const& new_type, std::vector<index_t>&& new_terrain_indicies, index_t new_priority, bool new_has_texture);
+
+ public:
+ TerrainTypeMapping(TerrainTypeMapping&&) = default;
+
+ TerrainType const& get_type() const;
+ std::vector<index_t> const& get_terrain_indicies() const;
+ index_t get_priority() const;
+ bool get_has_texture() const;
+ };
+
+ struct TerrainTypeManager {
+ private:
+ IdentifierRegistry<TerrainType> terrain_types;
+ IdentifierRegistry<TerrainTypeMapping> terrain_type_mappings;
+
+ TerrainTypeMapping::index_t terrain_texture_limit = 0, terrain_texture_count = 0;
+
+ bool _load_terrain_type_categories(ModifierManager const& modifier_manager, ast::NodeCPtr root);
+ bool _load_terrain_type_mapping(std::string_view key, ast::NodeCPtr value);
+
+ public:
+ TerrainTypeManager();
+
+ bool add_terrain_type(const std::string_view identifier, colour_t colour, ModifierValue&& values, bool is_water);
+ IDENTIFIER_REGISTRY_ACCESSORS(TerrainType, terrain_type)
+
+ bool add_terrain_type_mapping(const std::string_view identifier, TerrainType const* type,
+ std::vector<TerrainTypeMapping::index_t>&& terrain_indicies, TerrainTypeMapping::index_t priority, bool has_texture);
+ IDENTIFIER_REGISTRY_ACCESSORS(TerrainTypeMapping, terrain_type_mapping)
+
+ TerrainTypeMapping::index_t get_terrain_texture_limit() const;
+
+ bool load_terrain_types(ModifierManager const& modifier_manager, ast::NodeCPtr root);
+ };
+}
diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp
index 7b61cf8..41e4c6b 100644
--- a/src/openvic-simulation/types/IdentifierRegistry.hpp
+++ b/src/openvic-simulation/types/IdentifierRegistry.hpp
@@ -241,6 +241,7 @@ namespace OpenVic {
#define IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(type, singular, plural) \
void lock_##plural() { plural.lock(); } \
+ bool plural##_are_locked() const { return plural.is_locked(); } \
type const* get_##singular##_by_identifier(const std::string_view identifier) const { \
return plural.get_item_by_identifier(identifier); } \
size_t get_##singular##_count() const { \
diff --git a/src/openvic-simulation/utility/BMP.cpp b/src/openvic-simulation/utility/BMP.cpp
index 531870b..2fa9417 100644
--- a/src/openvic-simulation/utility/BMP.cpp
+++ b/src/openvic-simulation/utility/BMP.cpp
@@ -11,13 +11,12 @@ BMP::~BMP() {
close();
}
-bool BMP::open(char const* filepath) {
+bool BMP::open(fs::path const& filepath) {
reset();
- errno = 0;
- file = fopen(filepath, "rb");
- if (file == nullptr || errno != 0) {
- Logger::error("Failed to open BMP file \"", filepath, "\" (errno = ", errno, ")");
- file = nullptr;
+ file.open(filepath, std::ios::binary);
+ if (file.fail()) {
+ Logger::error("Failed to open BMP file \"", filepath, "\"");
+ close();
return false;
}
return true;
@@ -28,15 +27,17 @@ bool BMP::read_header() {
Logger::error("BMP header already validated!");
return false;
}
- if (file == nullptr) {
+ if (!file.is_open()) {
Logger::error("Cannot read BMP header before opening a file");
return false;
}
- if (fseek(file, 0, SEEK_SET) != 0) {
+ file.seekg(0, std::ios::beg);
+ if (file.fail()) {
Logger::error("Failed to move to the beginning of the BMP file!");
return false;
}
- if (fread(&header, sizeof(header), 1, file) != 1) {
+ file.read(reinterpret_cast<char*>(&header), sizeof(header));
+ if (file.fail()) {
Logger::error("Failed to read BMP header!");
return false;
}
@@ -117,7 +118,11 @@ bool BMP::read_header() {
}
bool BMP::read_palette() {
- if (file == nullptr) {
+ if (palette_read) {
+ Logger::error("BMP palette already read!");
+ return false;
+ }
+ if (!file.is_open()) {
Logger::error("Cannot read BMP palette before opening a file");
return false;
}
@@ -129,24 +134,25 @@ bool BMP::read_palette() {
Logger::error("Cannot read BMP palette - header indicates this file doesn't have one");
return false;
}
- if (fseek(file, sizeof(header), SEEK_SET) != 0) {
+ file.seekg(sizeof(header), std::ios::beg);
+ if (file.fail()) {
Logger::error("Failed to move to the palette in the BMP file!");
return false;
}
palette.resize(palette_size);
- if (fread(palette.data(), palette_size * PALETTE_COLOUR_SIZE, 1, file) != 1) {
+ file.read(reinterpret_cast<char*>(palette.data()), palette_size * PALETTE_COLOUR_SIZE);
+ if (file.fail()) {
Logger::error("Failed to read BMP header!");
palette.clear();
return false;
}
- return true;
+ palette_read = true;
+ return palette_read;
}
void BMP::close() {
- if (file != nullptr) {
- if (fclose(file) != 0)
- Logger::error("Failed to close BMP!");
- file = nullptr;
+ if (file.is_open()) {
+ file.close();
}
}
@@ -156,8 +162,61 @@ void BMP::reset() {
header_validated = false;
palette_size = 0;
palette.clear();
+ pixel_data.clear();
+}
+
+int32_t BMP::get_width() const {
+ return header.width_px;
+}
+
+int32_t BMP::get_height() const {
+ return header.height_px;
+}
+
+uint16_t BMP::get_bits_per_pixel() const {
+ return header.bits_per_pixel;
}
std::vector<colour_t> const& BMP::get_palette() const {
+ if (!palette_read) {
+ Logger::warning("Trying to get BMP palette before loading");
+ }
return palette;
}
+
+bool BMP::read_pixel_data() {
+ if (pixel_data_read) {
+ Logger::error("BMP pixel data already read!");
+ return false;
+ }
+ if (!file.is_open()) {
+ Logger::error("Cannot read BMP pixel data before opening a file");
+ return false;
+ }
+ if (!header_validated) {
+ Logger::error("Cannot read pixel data before BMP header is validated!");
+ return false;
+ }
+ file.seekg(header.offset, std::ios::beg);
+ if (file.fail()) {
+ Logger::error("Failed to move to the pixel data in the BMP file!");
+ return false;
+ }
+ const size_t pixel_data_size = get_width() * get_height() * header.bits_per_pixel / 8;
+ pixel_data.resize(pixel_data_size);
+ file.read(reinterpret_cast<char*>(pixel_data.data()), pixel_data_size);
+ if (file.fail()) {
+ Logger::error("Failed to read BMP pixel data!");
+ pixel_data.clear();
+ return false;
+ }
+ pixel_data_read = true;
+ return pixel_data_read;
+}
+
+std::vector<uint8_t> const& BMP::get_pixel_data() const {
+ if (!pixel_data_read) {
+ Logger::warning("Trying to get BMP pixel data before loading");
+ }
+ return pixel_data;
+}
diff --git a/src/openvic-simulation/utility/BMP.hpp b/src/openvic-simulation/utility/BMP.hpp
index f04b41a..c08ac99 100644
--- a/src/openvic-simulation/utility/BMP.hpp
+++ b/src/openvic-simulation/utility/BMP.hpp
@@ -1,11 +1,14 @@
#pragma once
-#include <cstdio>
+#include <filesystem>
+#include <fstream>
#include <vector>
#include "openvic-simulation/types/Colour.hpp"
namespace OpenVic {
+ namespace fs = std::filesystem;
+
class BMP {
#pragma pack(push)
#pragma pack(1)
@@ -29,10 +32,11 @@ namespace OpenVic {
} header;
#pragma pack(pop)
- FILE* file = nullptr;
- bool header_validated = false;
+ std::ifstream file;
+ bool header_validated = false, palette_read = false, pixel_data_read = false;
uint32_t palette_size = 0;
std::vector<colour_t> palette;
+ std::vector<uint8_t> pixel_data;
public:
static constexpr uint32_t PALETTE_COLOUR_SIZE = sizeof(colour_t);
@@ -40,12 +44,17 @@ namespace OpenVic {
BMP() = default;
~BMP();
- bool open(char const* filepath);
+ bool open(fs::path const& filepath);
bool read_header();
bool read_palette();
+ bool read_pixel_data();
void close();
void reset();
+ int32_t get_width() const;
+ int32_t get_height() const;
+ uint16_t get_bits_per_pixel() const;
std::vector<colour_t> const& get_palette() const;
+ std::vector<uint8_t> const& get_pixel_data() const;
};
}