diff options
Diffstat (limited to 'src/openvic-simulation/map')
-rw-r--r-- | src/openvic-simulation/map/Map.cpp | 156 | ||||
-rw-r--r-- | src/openvic-simulation/map/Map.hpp | 17 | ||||
-rw-r--r-- | src/openvic-simulation/map/Province.cpp | 4 | ||||
-rw-r--r-- | src/openvic-simulation/map/Province.hpp | 8 | ||||
-rw-r--r-- | src/openvic-simulation/map/Region.cpp | 43 | ||||
-rw-r--r-- | src/openvic-simulation/map/Region.hpp | 20 | ||||
-rw-r--r-- | src/openvic-simulation/map/State.cpp | 60 | ||||
-rw-r--r-- | src/openvic-simulation/map/State.hpp | 12 | ||||
-rw-r--r-- | src/openvic-simulation/map/TerrainType.hpp | 2 |
9 files changed, 206 insertions, 116 deletions
diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp index fde3b3a..67e91cd 100644 --- a/src/openvic-simulation/map/Map.cpp +++ b/src/openvic-simulation/map/Map.cpp @@ -1,7 +1,9 @@ #include "Map.hpp" #include <cassert> +#include <cstddef> #include <unordered_set> +#include <vector> #include "openvic-simulation/economy/Good.hpp" #include "openvic-simulation/history/ProvinceHistory.hpp" @@ -108,42 +110,30 @@ void Map::lock_water_provinces() { Logger::info("Locked water provinces after registering ", water_provinces.size()); } -bool Map::add_region(std::string_view identifier, std::vector<std::string_view> const& province_identifiers) { +bool Map::add_region(std::string_view identifier, Region::provinces_t const& provinces) { if (identifier.empty()) { Logger::error("Invalid region identifier - empty!"); return false; } - Region::provinces_t provinces; - provinces.reserve(province_identifiers.size()); - bool meta = false, ret = true; - for (const std::string_view province_identifier : province_identifiers) { - Province* province = get_province_by_identifier(province_identifier); - if (province != nullptr) { - if (std::find(provinces.begin(), provinces.end(), province) != provinces.end()) { - Logger::error("Duplicate province identifier ", province_identifier, " in region ", identifier); - ret = false; - } else { - if (province->get_has_region()) { - meta = true; - } - provinces.push_back(province); - } - } else { - Logger::error("Invalid province identifier ", province_identifier, " for region ", identifier); - ret = false; - } - } if (provinces.empty()) { Logger::warning("No valid provinces in list for ", identifier); - return ret; + return true; } - if (!meta) { - for (Province* province : provinces) { - province->has_region = true; + const bool meta = std::any_of(provinces.begin(), provinces.end(), std::bind_front(&Province::get_has_region)); + + Region region { identifier, provinces.front()->get_colour(), meta }; + bool ret = region.add_provinces(provinces); + region.lock(); + if (regions.add_item(std::move(region))) { + if (!meta) { + for (Province const* province : provinces) { + remove_province_const(province)->has_region = true; + } } + } else { + ret = false; } - ret &= regions.add_item({ identifier, std::move(provinces), meta }); return ret; } @@ -387,36 +377,22 @@ bool Map::load_province_positions(BuildingTypeManager const& building_type_manag bool Map::load_region_file(ast::NodeCPtr root) { const bool ret = expect_dictionary_reserve_length(regions, [this](std::string_view region_identifier, ast::NodeCPtr region_node) -> bool { - std::vector<std::string_view> province_identifiers; + Region::provinces_t provinces; bool ret = expect_list_reserve_length( - province_identifiers, expect_identifier(vector_callback(province_identifiers)) + provinces, expect_province_identifier(vector_callback_pointer(provinces)) )(region_node); - ret &= add_region(region_identifier, province_identifiers); + ret &= add_region(region_identifier, provinces); return ret; } )(root); lock_regions(); - for (Region& region : regions.get_items()) { + for (Region const& region : regions.get_items()) { if (!region.meta) { - for (Province* province : region.get_provinces()) { - if (!province->get_has_region()) { - Logger::error("Province in non-meta region without has_region set: ", province->get_identifier()); - province->has_region = true; - } - province->region = ®ion; + for (Province const* province : region.get_provinces()) { + remove_province_const(province)->region = ®ion; } } } - for (Province& province : provinces.get_items()) { - const bool region_null = province.get_region() == nullptr; - if (province.get_has_region() == region_null) { - Logger::error( - "Province has_region / region mismatch: has_region = ", province.get_has_region(), - ", region = ", province.get_region() - ); - province.has_region = !region_null; - } - } return ret; } @@ -670,3 +646,91 @@ bool Map::generate_and_load_province_adjacencies(std::vector<LineObject> const& ); return ret; } + +bool Map::load_climate_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) { + bool ret = expect_dictionary_reserve_length(climates, [this, &modifier_manager](std::string_view identifier, ast::NodeCPtr node) -> bool { + if (identifier.empty()) { + Logger::error("Invalid climate identifier - empty!"); + return false; + } + + bool ret = true; + Climate* cur_climate = climates.get_item_by_identifier(identifier); + if (cur_climate == nullptr) { + ModifierValue values; + ret &= modifier_manager.expect_modifier_value(move_variable_callback(values))(node); + ret &= climates.add_item({ identifier, std::move(values) }); + } else { + ret &= expect_list_reserve_length(*cur_climate, expect_province_identifier( + [cur_climate, &identifier](Province& province) { + if (province.climate != cur_climate) { + cur_climate->add_province(&province); + if (province.climate != nullptr) { + Climate* old_climate = const_cast<Climate*>(province.climate); + old_climate->remove_province(&province); + Logger::warning( + "Province with id ", province.get_identifier(), + " found in multiple climates: ", identifier, + " and ", old_climate->get_identifier() + ); + } + province.climate = cur_climate; + } else { + Logger::warning( + "Province with id ", province.get_identifier(), + "defined twice in climate ", identifier + ); + } + return true; + } + ))(node); + cur_climate->lock(); + } + return ret; + })(root); + + lock_climates(); + + return ret; +} + +bool Map::load_continent_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) { + bool ret = expect_dictionary_reserve_length(continents, [this, &modifier_manager](std::string_view identifier, ast::NodeCPtr node) -> bool { + if (identifier.empty()) { + Logger::error("Invalid continent identifier - empty!"); + return false; + } + + ModifierValue values; + ProvinceSetModifier::provinces_t prov_list; + bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(values), + "provinces", ONE_EXACTLY, expect_list_reserve_length(prov_list, expect_province_identifier( + [&prov_list](Province const& province) -> bool { + if (province.continent == nullptr) { + prov_list.emplace_back(&province); + } + return true; + } + )) + )(node); + + Continent continent = { identifier, std::move(values) }; + continent.add_provinces(prov_list); + continent.lock(); + + if (continents.add_item(std::move(continent))) { + Continent const& moved_continent = continents.get_items().back(); + for (Province const* prov : moved_continent.get_provinces()) { + remove_province_const(prov)->continent = &moved_continent; + } + } else { + ret = false; + } + + return ret; + })(root); + + lock_continents(); + + return ret; +} diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp index e477b38..2575324 100644 --- a/src/openvic-simulation/map/Map.hpp +++ b/src/openvic-simulation/map/Map.hpp @@ -49,7 +49,6 @@ namespace OpenVic { * MAP-4 */ struct Map { - #pragma pack(push, 1) /* Used to represent tightly packed 3-byte integer pixel information. */ struct shape_pixel_t { @@ -63,6 +62,8 @@ namespace OpenVic { IdentifierRegistry<Province> IDENTIFIER_REGISTRY_CUSTOM_INDEX_OFFSET(province, 1); IdentifierRegistry<Region> IDENTIFIER_REGISTRY(region); IdentifierRegistry<Mapmode> IDENTIFIER_REGISTRY(mapmode); + IdentifierRegistry<Climate> IDENTIFIER_REGISTRY(climate); + IdentifierRegistry<Continent> IDENTIFIER_REGISTRY(continent); ProvinceSet water_provinces; TerrainTypeManager PROPERTY_REF(terrain_type_manager); @@ -87,6 +88,15 @@ namespace OpenVic { bool add_province(std::string_view identifier, colour_t colour); IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_INDEX_OFFSET(province, 1); + /* This provides a safe way to remove the const qualifier of a Province const*, via a non-const Map. + * It uses a const_cast (the fastest/simplest solution), but this could also be done without it by looking up the + * Province* using the Province const*'s index. Requiring a non-const Map ensures that this function can only be + * used where the Province* could already be accessed by other means, such as the index method, preventing + * misleading code, or in the worst case undefined behaviour. */ + constexpr Province* remove_province_const(Province const* province) { + return const_cast<Province*>(province); + } + bool set_water_province(std::string_view identifier); bool set_water_province_list(std::vector<std::string_view> const& list); void lock_water_provinces(); @@ -97,8 +107,7 @@ namespace OpenVic { Province* get_selected_province(); Province::index_t get_selected_province_index() const; - bool add_region(std::string_view identifier, std::vector<std::string_view> const& province_identifiers); - IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS(region) + bool add_region(std::string_view identifier, Region::provinces_t const& provinces); bool add_mapmode(std::string_view identifier, Mapmode::colour_func_t colour_func); @@ -124,5 +133,7 @@ namespace OpenVic { 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); + bool load_climate_file(ModifierManager const& modifier_manager, ast::NodeCPtr root); + bool load_continent_file(ModifierManager const& modifier_manager, ast::NodeCPtr root); }; } diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp index cb9095b..2d301e8 100644 --- a/src/openvic-simulation/map/Province.cpp +++ b/src/openvic-simulation/map/Province.cpp @@ -8,8 +8,8 @@ using namespace OpenVic::NodeTools; Province::Province( std::string_view new_identifier, colour_t new_colour, index_t new_index ) : HasIdentifierAndColour { new_identifier, new_colour, true }, index { new_index }, region { nullptr }, - on_map { false }, has_region { false }, water { false }, coastal { false }, port { false }, - default_terrain_type { nullptr }, positions {}, terrain_type { nullptr }, life_rating { 0 }, + climate { nullptr }, continent { nullptr }, on_map { false }, has_region { false }, water { false }, coastal { false }, + port { false }, default_terrain_type { nullptr }, positions {}, terrain_type { nullptr }, life_rating { 0 }, colony_status { colony_status_t::STATE }, state { nullptr }, owner { nullptr }, controller { nullptr }, slave { false }, crime { nullptr }, rgo { nullptr }, buildings { "buildings", false }, total_population { 0 } { assert(index != NULL_INDEX); diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp index 8a9f9e9..431b4c2 100644 --- a/src/openvic-simulation/map/Province.hpp +++ b/src/openvic-simulation/map/Province.hpp @@ -16,6 +16,10 @@ namespace OpenVic { struct TerrainType; struct TerrainTypeMapping; struct ProvinceHistoryEntry; + struct ProvinceSetModifier; + using Climate = ProvinceSetModifier; + using Continent = ProvinceSetModifier; + /* REQUIREMENTS: * MAP-5, MAP-7, MAP-8, MAP-43, MAP-47 @@ -87,7 +91,9 @@ namespace OpenVic { private: /* Immutable attributes (unchanged after initial game load) */ const index_t PROPERTY(index); - Region* PROPERTY(region); + Region const* PROPERTY(region); + Climate const* PROPERTY(climate); + Continent const* PROPERTY(continent); bool PROPERTY(on_map); bool PROPERTY(has_region); bool PROPERTY_CUSTOM_PREFIX(water, is); diff --git a/src/openvic-simulation/map/Region.cpp b/src/openvic-simulation/map/Region.cpp index 18a47a9..6f40338 100644 --- a/src/openvic-simulation/map/Region.cpp +++ b/src/openvic-simulation/map/Region.cpp @@ -4,9 +4,7 @@ using namespace OpenVic; -ProvinceSet::ProvinceSet(provinces_t&& new_provinces) : provinces { std::move(new_provinces) } {} - -bool ProvinceSet::add_province(Province* province) { +bool ProvinceSet::add_province(Province const* province) { if (locked) { Logger::error("Cannot add province to province set - locked!"); return false; @@ -16,13 +14,39 @@ bool ProvinceSet::add_province(Province* province) { return false; } if (contains_province(province)) { - Logger::error("Cannot add province ", province->get_identifier(), " to province set - already in the set!"); + Logger::warning("Cannot add province ", province->get_identifier(), " to province set - already in the set!"); return false; } provinces.push_back(province); return true; } +bool ProvinceSet::add_provinces(provinces_t const& new_provinces) { + bool ret = true; + for (Province const* province : new_provinces) { + ret &= add_province(province); + } + return ret; +} + +bool ProvinceSet::remove_province(Province const* province) { + if (locked) { + Logger::error("Cannot remove province from province set - locked!"); + return false; + } + if (province == nullptr) { + Logger::error("Cannot remove province from province set - null province!"); + return false; + } + const provinces_t::const_iterator it = std::find(provinces.begin(), provinces.end(), province); + if (it == provinces.end()) { + Logger::warning("Cannot remove province ", province->get_identifier(), " from province set - already not in the set!"); + return false; + } + provinces.erase(it); + return true; +} + void ProvinceSet::lock(bool log) { if (locked) { Logger::error("Failed to lock province set - already locked!"); @@ -67,14 +91,11 @@ ProvinceSet::provinces_t const& ProvinceSet::get_provinces() const { return provinces; } -static constexpr colour_t ERROR_REGION_COLOUR { colour_t::max_value, 0, 0 }; +Region::Region(std::string_view new_identifier, colour_t new_colour, bool new_meta) + : HasIdentifierAndColour { new_identifier, new_colour, false }, meta { new_meta } {} -Region::Region(std::string_view new_identifier, provinces_t&& new_provinces, bool new_meta) - : HasIdentifierAndColour { - new_identifier, new_provinces.size() > 0 ? new_provinces.front()->get_colour() : ERROR_REGION_COLOUR, false - }, ProvinceSet { std::move(new_provinces) }, meta { new_meta } { - lock(); -} +ProvinceSetModifier::ProvinceSetModifier(std::string_view new_identifier, ModifierValue&& new_values) + : Modifier { new_identifier, std::move(new_values), 0 } {} bool Region::get_meta() const { return meta; diff --git a/src/openvic-simulation/map/Region.hpp b/src/openvic-simulation/map/Region.hpp index 9119b93..d8948b7 100644 --- a/src/openvic-simulation/map/Region.hpp +++ b/src/openvic-simulation/map/Region.hpp @@ -5,16 +5,18 @@ namespace OpenVic { struct ProvinceSet { - using provinces_t = std::vector<Province*>; + using provinces_t = std::vector<Province const*>; private: provinces_t provinces; bool locked = false; public: - ProvinceSet(provinces_t&& new_provinces = {}); - - bool add_province(Province* province); + /* Returns true if the province is successfully added, false if not (including if it's already in the set). */ + bool add_province(Province const* province); + bool add_provinces(provinces_t const& new_provinces); + /* Returns true if the province is successfully removed, false if not (including if it's not in the set). */ + bool remove_province(Province const* province); void lock(bool log = false); bool is_locked() const; void reset(); @@ -25,6 +27,14 @@ namespace OpenVic { provinces_t const& get_provinces() const; }; + struct ProvinceSetModifier : Modifier, ProvinceSet { + friend struct Map; + private: + ProvinceSetModifier(std::string_view new_identifier, ModifierValue&& new_values); + public: + ProvinceSetModifier(ProvinceSetModifier&&) = default; + }; + /* REQUIREMENTS: * MAP-6, MAP-44, MAP-48 */ @@ -38,7 +48,7 @@ namespace OpenVic { */ const bool meta; - Region(std::string_view new_identifier, provinces_t&& new_provinces, bool new_meta); + Region(std::string_view new_identifier, colour_t new_colour, bool new_meta); public: Region(Region&&) = default; diff --git a/src/openvic-simulation/map/State.cpp b/src/openvic-simulation/map/State.cpp index 896008e..faf8d1b 100644 --- a/src/openvic-simulation/map/State.cpp +++ b/src/openvic-simulation/map/State.cpp @@ -8,76 +8,60 @@ State::State( Country const* owner, Province const* capital, Region::provinces_t&& provinces, Province::colony_status_t colony_status ) : owner { owner }, capital { capital }, provinces { std::move(provinces) }, colony_status { colony_status } {} -StateSet::StateSet(Region const& new_region) : region { new_region } { +/* Whether two provinces in the same region should be grouped into the same state or not. + * (Assumes both provinces non-null.) */ +static bool provinces_belong_in_same_state(Province const* lhs, Province const* rhs) { + return lhs->get_owner() == rhs->get_owner() && lhs->get_colony_status() == rhs->get_colony_status(); +} + +StateSet::StateSet(Map& map, Region const& new_region) : region { new_region } { if (region.get_meta()) { Logger::error("Cannot use meta region as state template!"); } std::vector<Region::provinces_t> temp_provinces; - bool in_state = false; - for (Province* province : region.get_provinces()) { + for (Province const* province : region.get_provinces()) { // add to existing state if shared owner & status... for (Region::provinces_t& provinces : temp_provinces) { - if (provinces[0] == province) { + if (provinces_belong_in_same_state(provinces[0], province)) { provinces.push_back(province); - in_state = true; - break; + // jump to the end of the outer loop, skipping the new state code + goto loop_end; } } - if (in_state) { - in_state = false; - } else { - // ...otherwise start a new state - temp_provinces.push_back({ province }); - } + // ...otherwise start a new state + temp_provinces.push_back({ province }); + loop_end:; + /* Either the province was added to an existing state and the program jumped to here, + * or it was used to create a new state and the program arrived here normally. */ } for (Region::provinces_t& provinces : temp_provinces) { - states.push_back({ + states.emplace_back( /* TODO: capital province logic */ provinces[0]->get_owner(), provinces[0], std::move(provinces), provinces[0]->get_colony_status() - }); + ); } // Go back and assign each new state to its provinces. for (State const& state : states) { - for (Province* province : state.get_provinces()) { - province->set_state(&state); + for (Province const* province : state.get_provinces()) { + map.remove_province_const(province)->set_state(&state); } } } -bool StateSet::add_state(State&& state) { - const auto existing = std::find(states.begin(), states.end(), state); - if (existing != states.end()) { - Logger::error("Attempted to add existing state!"); - return false; - } - states.push_back(std::move(state)); - return true; -} - -bool StateSet::remove_state(State const* state) { - const auto existing = std::find(states.begin(), states.end(), *state); - if (existing == states.end()) { - Logger::error("Attempted to remove non-existant state!"); - return false; - } - states.erase(existing); - return true; -} - StateSet::states_t& StateSet::get_states() { return states; } -void StateManager::generate_states(Map const& map) { +void StateManager::generate_states(Map& map) { regions.clear(); regions.reserve(map.get_region_count()); for(Region const& region : map.get_regions()) { if (!region.get_meta()) { - regions.push_back(StateSet(region)); + regions.emplace_back(map, region); } } Logger::info("Generated states."); diff --git a/src/openvic-simulation/map/State.hpp b/src/openvic-simulation/map/State.hpp index bb23b2d..bae83f7 100644 --- a/src/openvic-simulation/map/State.hpp +++ b/src/openvic-simulation/map/State.hpp @@ -21,22 +21,16 @@ namespace OpenVic { ); }; - inline bool operator==(const State& lhs, const State& rhs) { - return (lhs.get_owner() == rhs.get_owner() && lhs.get_colony_status() == rhs.get_colony_status()); - } - struct StateSet { using states_t = std::deque<State>; private: Region const& PROPERTY(region); - states_t states; + states_t PROPERTY(states); public: - StateSet(Region const& new_region); + StateSet(Map& map, Region const& new_region); - bool add_state(State&& state); - bool remove_state(State const* state); states_t& get_states(); }; @@ -49,6 +43,6 @@ namespace OpenVic { /* Creates states from current province gamestate & regions, sets province state value. * After this function, the `regions` property is unmanaged and must be carefully updated and * validated by functions that modify it. */ - void generate_states(Map const& map); + void generate_states(Map& map); }; } // namespace OpenVic diff --git a/src/openvic-simulation/map/TerrainType.hpp b/src/openvic-simulation/map/TerrainType.hpp index 1a5b09c..dc9bc56 100644 --- a/src/openvic-simulation/map/TerrainType.hpp +++ b/src/openvic-simulation/map/TerrainType.hpp @@ -5,7 +5,7 @@ namespace OpenVic { struct TerrainTypeManager; - struct TerrainType : HasIdentifierAndColour, ModifierValue { + struct TerrainType : HasIdentifierAndColour { friend struct TerrainTypeManager; private: |