From e91ce707b2c0e80591b9fd1b6a5215e6e6989df8 Mon Sep 17 00:00:00 2001 From: hop311 Date: Sun, 29 Oct 2023 23:06:10 +0000 Subject: Stop passing Dates by reference --- src/openvic-simulation/map/Province.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/openvic-simulation/map/Province.cpp') diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp index 225d4c0..7b88ebf 100644 --- a/src/openvic-simulation/map/Province.cpp +++ b/src/openvic-simulation/map/Province.cpp @@ -155,14 +155,14 @@ void Province::update_pops() { } } -void Province::update_state(Date const& today) { +void Province::update_state(Date today) { for (BuildingInstance& building : buildings.get_items()) { building.update_state(today); } update_pops(); } -void Province::tick(Date const& today) { +void Province::tick(Date today) { for (BuildingInstance& building : buildings.get_items()) { building.tick(today); } -- cgit v1.2.3-56-ga3b1 From c1b7cab254ac14a173477661047ad2492930ff8b Mon Sep 17 00:00:00 2001 From: hop311 Date: Tue, 31 Oct 2023 01:11:47 +0000 Subject: History loading changes + PROPERTY macro --- src/openvic-simulation/Modifier.cpp | 10 +- src/openvic-simulation/Modifier.hpp | 4 +- src/openvic-simulation/country/Country.cpp | 31 ++- src/openvic-simulation/country/Country.hpp | 14 +- src/openvic-simulation/dataloader/Dataloader.cpp | 127 ++++++------ src/openvic-simulation/dataloader/Dataloader.hpp | 27 +-- src/openvic-simulation/dataloader/NodeTools.cpp | 9 +- src/openvic-simulation/dataloader/NodeTools.hpp | 13 +- src/openvic-simulation/economy/Good.cpp | 2 +- src/openvic-simulation/history/Bookmark.hpp | 5 +- src/openvic-simulation/history/CountryHistory.cpp | 187 ++++++++---------- src/openvic-simulation/history/CountryHistory.hpp | 34 ++-- src/openvic-simulation/history/ProvinceHistory.cpp | 217 ++++++++++----------- src/openvic-simulation/history/ProvinceHistory.hpp | 41 ++-- src/openvic-simulation/map/Map.cpp | 2 +- src/openvic-simulation/map/Province.cpp | 6 +- src/openvic-simulation/map/Province.hpp | 4 + src/openvic-simulation/map/TerrainType.cpp | 2 +- src/openvic-simulation/military/Deployment.cpp | 205 +++++++++++-------- src/openvic-simulation/military/Deployment.hpp | 84 +++++--- src/openvic-simulation/military/Unit.cpp | 16 +- src/openvic-simulation/military/Unit.hpp | 2 + src/openvic-simulation/misc/Define.hpp | 6 +- src/openvic-simulation/politics/Government.cpp | 2 +- src/openvic-simulation/politics/Ideology.cpp | 2 +- src/openvic-simulation/pop/Culture.cpp | 4 +- src/openvic-simulation/pop/Pop.cpp | 2 +- src/openvic-simulation/pop/Religion.cpp | 2 +- src/openvic-simulation/types/Date.hpp | 6 +- .../types/IdentifierRegistry.cpp | 16 +- .../types/IdentifierRegistry.hpp | 49 +---- .../types/fixed_point/FixedPoint.hpp | 3 +- src/openvic-simulation/utility/Getters.hpp | 66 +++++++ 33 files changed, 638 insertions(+), 562 deletions(-) create mode 100644 src/openvic-simulation/utility/Getters.hpp (limited to 'src/openvic-simulation/map/Province.cpp') diff --git a/src/openvic-simulation/Modifier.cpp b/src/openvic-simulation/Modifier.cpp index 0d79833..c3b7f6a 100644 --- a/src/openvic-simulation/Modifier.cpp +++ b/src/openvic-simulation/Modifier.cpp @@ -47,7 +47,7 @@ fixed_point_t ModifierValue::get_effect(ModifierEffect const* effect, bool* succ } bool ModifierValue::has_effect(ModifierEffect const* effect) const { - return values.find(effect) != values.end(); + return values.contains(effect); } ModifierValue& ModifierValue::operator+=(ModifierValue const& right) { @@ -107,9 +107,9 @@ bool ModifierManager::add_modifier_effect(std::string_view identifier, bool posi Logger::error("Invalid modifier effect identifier - empty!"); return false; } - return modifier_effects.add_item(std::unique_ptr { - new ModifierEffect { identifier, positive_good, format } - }); + return modifier_effects.add_item( + std::make_unique(std::move(identifier), std::move(positive_good), std::move(format)) + ); } bool ModifierManager::add_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) { @@ -260,7 +260,7 @@ key_value_callback_t ModifierManager::_modifier_effect_callback( ModifierEffect const* effect = get_modifier_effect_by_identifier(key); if (effect != nullptr) { if (effect_validator(*effect)) { - if (modifier.values.find(effect) == modifier.values.end()) { + if (!modifier.values.contains(effect)) { return expect_fixed_point(assign_variable_callback(modifier.values[effect]))(value); } else { Logger::error("Duplicate modifier effect: ", key); diff --git a/src/openvic-simulation/Modifier.hpp b/src/openvic-simulation/Modifier.hpp index 78f8228..fc37655 100644 --- a/src/openvic-simulation/Modifier.hpp +++ b/src/openvic-simulation/Modifier.hpp @@ -6,8 +6,6 @@ namespace OpenVic { struct ModifierManager; struct ModifierEffect : HasIdentifier { - friend struct ModifierManager; - enum class format_t { PROPORTION_DECIMAL, /* An unscaled fraction/ratio, with 1 being "full"/"whole" */ PERCENTAGE_DECIMAL, /* A fraction/ratio scaled so that 100 is "full"/"whole" */ @@ -15,6 +13,8 @@ namespace OpenVic { INT /* A discrete quantity, e.g. building count limit */ }; + friend std::unique_ptr std::make_unique(std::string_view&&, bool&&, format_t&&); + private: /* If true, positive values will be green and negative values will be red. * If false, the colours will be switced. diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp index b2c4a71..9e244c1 100644 --- a/src/openvic-simulation/country/Country.cpp +++ b/src/openvic-simulation/country/Country.cpp @@ -45,9 +45,9 @@ CountryParty::policy_map_t const& CountryParty::get_policies() const { Country::Country( std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture, - std::vector&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, + IdentifierRegistry&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, government_colour_map_t&& new_alternative_colours -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, graphical_culture { new_graphical_culture }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, graphical_culture { new_graphical_culture }, parties { std::move(new_parties) }, unit_names { std::move(new_unit_names) }, dynamic_tag { new_dynamic_tag }, alternative_colours { std::move(new_alternative_colours) } {} @@ -55,10 +55,6 @@ GraphicalCultureType const& Country::get_graphical_culture() const { return graphical_culture; } -std::vector const& Country::get_parties() const { - return parties; -} - Country::unit_names_map_t const& Country::get_unit_names() const { return unit_names; } @@ -75,7 +71,7 @@ CountryManager::CountryManager() : countries { "countries" } {} bool CountryManager::add_country( std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, - std::vector&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, + IdentifierRegistry&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, Country::government_colour_map_t&& alternative_colours ) { if (identifier.empty()) { @@ -122,7 +118,7 @@ bool CountryManager::load_countries( [this, &game_manager, is_dynamic, &dataloader, &countries_dir, &key](std::string_view filepath) -> bool { if (load_country_data_file( game_manager, key, is_dynamic, - Dataloader::parse_defines(dataloader.lookup_file(countries_dir / filepath)).get_file_node() + Dataloader::parse_defines(dataloader.lookup_file_case_insensitive(countries_dir / filepath)).get_file_node() )) { return true; } @@ -141,7 +137,7 @@ bool CountryManager::load_countries( } node_callback_t CountryManager::load_country_party( - PoliticsManager const& politics_manager, std::vector& country_parties + PoliticsManager const& politics_manager, IdentifierRegistry& country_parties ) const { return [&politics_manager, &country_parties](ast::NodeCPtr value) -> bool { std::string_view party_name; @@ -149,7 +145,7 @@ node_callback_t CountryManager::load_country_party( Ideology const* ideology; CountryParty::policy_map_t policies; - const bool ret = expect_dictionary_keys_and_default( + bool ret = expect_dictionary_keys_and_default( [&politics_manager, &policies, &party_name](std::string_view key, ast::NodeCPtr value) -> bool { return politics_manager.get_issue_manager().expect_issue_group_str( [&politics_manager, &policies, value, &party_name](IssueGroup const& group) -> bool { @@ -163,9 +159,10 @@ node_callback_t CountryManager::load_country_party( policies.emplace(&group, &issue); return true; } - Logger::error("Invalid policy ", issue.get_identifier(), ", group is ", + // TODO - change this back to error/false once TGC no longer has this issue + Logger::warning("Invalid policy ", issue.get_identifier(), ", group is ", issue.get_group().get_identifier(), " when ", group.get_identifier(), " was expected"); - return false; + return true; } )(value); } @@ -178,7 +175,9 @@ node_callback_t CountryManager::load_country_party( politics_manager.get_ideology_manager().expect_ideology_identifier(assign_variable_callback_pointer(ideology)) )(value); - country_parties.emplace_back(CountryParty { party_name, start_date, end_date, *ideology, std::move(policies) }); + ret &= country_parties.add_item( + { party_name, start_date, end_date, *ideology, std::move(policies) }, duplicate_warning_callback + ); return ret; }; @@ -189,7 +188,7 @@ bool CountryManager::load_country_data_file( ) { colour_t colour; GraphicalCultureType const* graphical_culture; - std::vector country_parties; + IdentifierRegistry parties { "country parties" }; Country::unit_names_map_t unit_names; Country::government_colour_map_t alternative_colours; bool ret = expect_dictionary_keys_and_default( @@ -214,7 +213,7 @@ bool CountryManager::load_country_data_file( game_manager.get_pop_manager().get_culture_manager().expect_graphical_culture_type_identifier( assign_variable_callback_pointer(graphical_culture) ), - "party", ZERO_OR_MORE, load_country_party(game_manager.get_politics_manager(), country_parties), + "party", ZERO_OR_MORE, load_country_party(game_manager.get_politics_manager(), parties), "unit_names", ZERO_OR_ONE, game_manager.get_military_manager().get_unit_manager().expect_unit_dictionary( [&unit_names, &name](Unit const& unit, ast::NodeCPtr value) -> bool { @@ -230,7 +229,7 @@ bool CountryManager::load_country_data_file( )(root); ret &= add_country( - name, colour, graphical_culture, std::move(country_parties), std::move(unit_names), is_dynamic, + name, colour, graphical_culture, std::move(parties), std::move(unit_names), is_dynamic, std::move(alternative_colours) ); return ret; diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp index 1ab0e7e..7754a0b 100644 --- a/src/openvic-simulation/country/Country.hpp +++ b/src/openvic-simulation/country/Country.hpp @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -64,22 +63,23 @@ namespace OpenVic { /* Not const to allow elements to be moved, otherwise a copy is forced * which causes a compile error as the copy constructor has been deleted. */ - std::vector parties; + IdentifierRegistry parties; const unit_names_map_t unit_names; const bool dynamic_tag; const government_colour_map_t alternative_colours; Country( std::string_view new_identifier, colour_t new_colour, GraphicalCultureType const& new_graphical_culture, - std::vector&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, + IdentifierRegistry&& new_parties, unit_names_map_t&& new_unit_names, bool new_dynamic_tag, government_colour_map_t&& new_alternative_colours ); public: Country(Country&&) = default; + IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(party, parties) + GraphicalCultureType const& get_graphical_culture() const; - std::vector const& get_parties() const; unit_names_map_t const& get_unit_names() const; bool is_dynamic_tag() const; government_colour_map_t const& get_alternative_colours() const; @@ -90,7 +90,7 @@ namespace OpenVic { IdentifierRegistry countries; NodeTools::node_callback_t load_country_party( - PoliticsManager const& politics_manager, std::vector& country_parties + PoliticsManager const& politics_manager, IdentifierRegistry& country_parties ) const; public: @@ -98,10 +98,10 @@ namespace OpenVic { bool add_country( std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, - std::vector&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, + IdentifierRegistry&& parties, Country::unit_names_map_t&& unit_names, bool dynamic_tag, Country::government_colour_map_t&& alternative_colours ); - IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries); + IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries) bool load_countries( GameManager const& game_manager, Dataloader const& dataloader, fs::path const& countries_dir, ast::NodeCPtr root diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 0094253..e98e63b 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -34,13 +34,18 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; using namespace ovdl; -// Windows and Mac by default act like case insensitive filesystems -static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) { -#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) + +static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::string_view rhs) { constexpr auto ichar_equals = [](unsigned char l, unsigned char r) { return std::tolower(l) == std::tolower(r); }; return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end(), ichar_equals); +} + +// Windows and Mac by default act like case insensitive filesystems +static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) { +#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) + return path_equals_case_insensitive(lhs, rhs); #else return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); #endif @@ -359,14 +364,36 @@ bool Dataloader::set_roots(path_vector_t const& new_roots) { return ret; } -fs::path Dataloader::lookup_file(fs::path const& path) const { +fs::path Dataloader::lookup_file(fs::path const& path, bool print_error) const { for (fs::path const& root : roots) { const fs::path composed = root / path; if (fs::is_regular_file(composed)) { return composed; } } - Logger::error("Lookup for ", path, " failed!"); + if (print_error) { + Logger::error("Lookup for ", path, " failed!"); + } + return {}; +} + +fs::path Dataloader::lookup_file_case_insensitive(fs::path const& path, bool print_error) const { + const std::string filename = path.filename().string(); + for (fs::path const& root : roots) { + const fs::path composed = root / path; + std::error_code ec; + for (fs::directory_entry const& entry : fs::directory_iterator { composed.parent_path(), ec }) { + if (entry.is_regular_file()) { + const fs::path file = entry; + if (path_equals_case_insensitive(file.filename().string(), filename)) { + return file; + } + } + } + } + if (print_error) { + Logger::error("Lookup for ", path, " failed!"); + } return {}; } @@ -398,8 +425,9 @@ Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, return ret; } -bool Dataloader::apply_to_files_in_dir(fs::path const& path, fs::path const& extension, callback_t callback) - const { +bool Dataloader::apply_to_files_in_dir( + fs::path const& path, fs::path const& extension, callback_t callback +) const { bool ret = true; for (fs::path const& file : lookup_files_in_dir(path, extension)) { if (!callback(file)) { @@ -498,61 +526,37 @@ bool Dataloader::_load_units(UnitManager& unit_manager, GoodManager const& good_ return ret; } -bool Dataloader::_load_oobs(GameManager& game_manager) const { - static const fs::path oob_directory = "history/units"; - - /* used for countries with no defined initial OOB */ - game_manager.get_military_manager().get_deployment_manager().add_deployment( - "NULL", std::vector(), std::vector(), std::vector() - ); - - bool ret = apply_to_files_in_dir(oob_directory, ".txt", [&game_manager](fs::path const& file) -> bool { - if (file.filename() == "v2dd2.txt") { - return true; /* dev diary just stuck in there for no reason whatsoever */ - } - return game_manager.get_military_manager().get_deployment_manager().load_oob_file( - game_manager, file.filename().string(), parse_defines(file).get_file_node() - ); - }); - - /* we also load OOBs in top level subdirectories, for other start dates etc */ - for (auto root : roots) { - const fs::path path = root / oob_directory; - std::error_code ec; - for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) { - if (entry.is_directory()) { - ret &= apply_to_files_in_dir(entry, ".txt", [&entry, &game_manager](fs::path const& file) -> bool { - return game_manager.get_military_manager().get_deployment_manager().load_oob_file( - game_manager, (entry.path().filename() / file.filename()).string(), parse_defines(file).get_file_node() - ); - }); - } - } - } - - game_manager.get_military_manager().get_deployment_manager().lock_deployments(); - return ret; -} - bool Dataloader::_load_history(GameManager& game_manager) const { static const fs::path country_history_directory = "history/countries"; static const fs::path province_history_directory = "history/provinces"; /* Country History */ bool ret = apply_to_files_in_dir(country_history_directory, ".txt", [this, &game_manager](fs::path const& file) -> bool { - std::string tag = file.filename().string().substr(0, 3); - - if (!game_manager.get_country_manager().has_country_identifier(tag)) { - Logger::error("Error loading history for country ", tag, ": tag not defined!"); - return false; + const std::string filename = file.stem().string(); + // TODO - standardise rules on country idenifiers characters (probably letters + underscore) and enforce them + const size_t len = std::min(std::min(filename.find(" "), filename.find("-")), filename.length()); + const std::string_view country_id { filename.data(), len }; + + Country const* country = game_manager.get_country_manager().get_country_by_identifier(country_id); + if (country == nullptr) { + Logger::warning("Found history file for non-existent country: ", country_id); + return true; } return game_manager.get_history_manager().get_country_manager().load_country_history_file( - game_manager, tag, parse_defines(lookup_file(file)).get_file_node() + game_manager, *this, *country, parse_defines(lookup_file(file)).get_file_node() ); }); game_manager.get_history_manager().get_country_manager().lock_country_histories(); + { + DeploymentManager& deployment_manager = game_manager.get_military_manager().get_deployment_manager(); + deployment_manager.lock_deployments(); + if (deployment_manager.get_missing_oob_file_count() > 0) { + Logger::warning(deployment_manager.get_missing_oob_file_count(), " missing OOB files!"); + } + } + /* Province History */ for (auto root : roots) { const fs::path path = root / province_history_directory; @@ -560,16 +564,18 @@ bool Dataloader::_load_history(GameManager& game_manager) const { for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) { if (entry.is_directory()) { bool ret = apply_to_files_in_dir(entry, ".txt", [this, &game_manager](fs::path const& file) -> bool { - std::string province_id = file.filename().string(); - province_id = province_id.substr(0, province_id.find(" ")); + const std::string filename = file.stem().string(); + const size_t len = std::min(filename.find(" "), filename.length()); + const std::string_view province_id { filename.data(), len }; - if (!game_manager.get_map().has_province_identifier(province_id)) { - Logger::error("Error loading history for province ", province_id, ": province not defined!"); - return false; + Province const* province = game_manager.get_map().get_province_by_identifier(province_id); + if (province == nullptr) { + Logger::warning("Found history file for non-existent province: ", province_id); + return true; } return game_manager.get_history_manager().get_province_manager().load_province_history_file( - game_manager, province_id, parse_defines(lookup_file(file)).get_file_node() + game_manager, *province, parse_defines(lookup_file(file)).get_file_node() ); }); } @@ -808,10 +814,6 @@ bool Dataloader::load_defines(GameManager& game_manager) const { Logger::error("Failed to load bookmarks!"); ret = false; } - if (!_load_oobs(game_manager)) { - Logger::error("Failed to load orders of battle!"); - ret = false; - } if (!game_manager.get_country_manager().load_countries( game_manager, *this, countries_file.parent_path(), parse_defines(lookup_file(countries_file)).get_file_node() )) { @@ -828,10 +830,11 @@ bool Dataloader::load_defines(GameManager& game_manager) const { bool Dataloader::load_pop_history(GameManager& game_manager, fs::path const& path) const { return apply_to_files_in_dir(path, ".txt", [&game_manager](fs::path const& file) -> bool { - return game_manager.get_map() - .expect_province_dictionary([&game_manager](Province& province, ast::NodeCPtr value) -> bool { + return game_manager.get_map().expect_province_dictionary( + [&game_manager](Province& province, ast::NodeCPtr value) -> bool { return province.load_pop_list(game_manager.get_pop_manager(), value); - })(parse_defines(file).get_file_node()); + } + )(parse_defines(file).get_file_node()); }); } diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp index 508bf13..a8b8bb1 100644 --- a/src/openvic-simulation/dataloader/Dataloader.hpp +++ b/src/openvic-simulation/dataloader/Dataloader.hpp @@ -26,7 +26,6 @@ namespace OpenVic { bool _load_pop_types(PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager) const; bool _load_units(UnitManager& unit_manager, GoodManager const& good_manager) const; bool _load_map_dir(GameManager& game_manager) const; - bool _load_oobs(GameManager& game_manager) const; bool _load_history(GameManager& game_manager) const; public: @@ -64,7 +63,8 @@ namespace OpenVic { /* REQUIREMENTS: * DAT-24 */ - fs::path lookup_file(fs::path const& path) const; + fs::path lookup_file(fs::path const& path, bool print_error = true) const; + fs::path lookup_file_case_insensitive(fs::path const& path, bool print_error = true) const; path_vector_t lookup_files_in_dir(fs::path const& path, fs::path const& extension) const; bool apply_to_files_in_dir( fs::path const& path, fs::path const& extension, NodeTools::callback_t callback @@ -74,24 +74,13 @@ namespace OpenVic { bool load_pop_history(GameManager& game_manager, fs::path const& path) const; enum locale_t : size_t { - English, - French, - German, - Polish, - Spanish, - Italian, - Swedish, - Czech, - Hungarian, - Dutch, - Portugese, - Russian, - Finnish, - _LocaleCount + English, French, German, Polish, Spanish, Italian, Swedish, + Czech, Hungarian, Dutch, Portugese, Russian, Finnish, _LocaleCount + }; + static constexpr char const* locale_names[_LocaleCount] = { + "en_GB", "fr_FR", "de_DE", "pl_PL", "es_ES", "it_IT", "sv_SE", + "cs_CZ", "hu_HU", "nl_NL", "pt_PT", "ru_RU", "fi_FI" }; - static constexpr char const* locale_names[_LocaleCount] = { "en_GB", "fr_FR", "de_DE", "pl_PL", "es_ES", - "it_IT", "sv_SE", "cs_CZ", "hu_HU", "nl_NL", - "pt_PT", "ru_RU", "fi_FI" }; /* Args: key, locale, localisation */ using localisation_callback_t = NodeTools::callback_t; diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index 2f93bc2..27d0f95 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -289,7 +289,7 @@ node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) { bool NodeTools::add_key_map_entry( key_map_t& key_map, std::string_view key, dictionary_entry_t::expected_count_t expected_count, node_callback_t callback ) { - if (key_map.find(key) == key_map.end()) { + if (!key_map.contains(key)) { key_map.emplace(key, dictionary_entry_t { expected_count, callback }); return true; } @@ -318,7 +318,12 @@ key_value_callback_t NodeTools::dictionary_keys_callback(key_map_t& key_map, key Logger::error("Invalid repeat of dictionary key: ", key); return false; } - return entry.callback(value); + if (entry.callback(value)) { + return true; + } else { + Logger::error("Callback failed for dictionary key: ", key); + return false; + } }; } diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 798976f..4d56488 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -66,7 +66,7 @@ namespace OpenVic { } node_callback_t expect_identifier(callback_t callback); - node_callback_t expect_string(callback_t callback, bool allow_empty = true); + node_callback_t expect_string(callback_t callback, bool allow_empty = false); node_callback_t expect_identifier_or_string(callback_t callback, bool allow_empty = false); node_callback_t expect_bool(callback_t callback); @@ -261,15 +261,8 @@ namespace OpenVic { }; } - template - callback_t assign_variable_callback_cast(auto& var) { - return [&var](T val) -> bool { - var = val; - return true; - }; - } - - template + template + requires std::is_integral_v || std::is_enum_v callback_t assign_variable_callback_cast(auto& var) { return [&var](T val) -> bool { var = val; diff --git a/src/openvic-simulation/economy/Good.cpp b/src/openvic-simulation/economy/Good.cpp index 11230d2..2b1d694 100644 --- a/src/openvic-simulation/economy/Good.cpp +++ b/src/openvic-simulation/economy/Good.cpp @@ -10,7 +10,7 @@ GoodCategory::GoodCategory(std::string_view new_identifier) : HasIdentifier { ne Good::Good( std::string_view new_identifier, colour_t new_colour, GoodCategory const& new_category, price_t new_base_price, bool new_available_from_start, bool new_tradeable, bool new_money, bool new_overseas_penalty -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, category { new_category }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, category { new_category }, base_price { new_base_price }, available_from_start { new_available_from_start }, tradeable { new_tradeable }, money { new_money }, overseas_penalty { new_overseas_penalty } { assert(base_price > NULL_PRICE); diff --git a/src/openvic-simulation/history/Bookmark.hpp b/src/openvic-simulation/history/Bookmark.hpp index e93718f..d5253fe 100644 --- a/src/openvic-simulation/history/Bookmark.hpp +++ b/src/openvic-simulation/history/Bookmark.hpp @@ -42,9 +42,10 @@ namespace OpenVic { BookmarkManager(); bool add_bookmark( - std::string_view name, std::string_view description, Date date, uint32_t initial_camera_x, uint32_t initial_camera_y + std::string_view name, std::string_view description, Date date, uint32_t initial_camera_x, + uint32_t initial_camera_y ); - IDENTIFIER_REGISTRY_ACCESSORS(bookmark); + IDENTIFIER_REGISTRY_ACCESSORS(bookmark) bool load_bookmark_file(ast::NodeCPtr root); }; diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp index ed72f52..cd682bd 100644 --- a/src/openvic-simulation/history/CountryHistory.cpp +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -7,7 +7,7 @@ using namespace OpenVic::NodeTools; CountryHistory::CountryHistory( Culture const* new_primary_culture, std::vector&& new_accepted_cultures, Religion const* new_religion, - CountryParty const* new_ruling_party, Date new_last_election, std::map&& new_upper_house, + CountryParty const* new_ruling_party, Date new_last_election, decimal_map_t&& new_upper_house, Province const* new_capital, GovernmentType const* new_government_type, fixed_point_t new_plurality, NationalValue const* new_national_value, bool new_civilised, fixed_point_t new_prestige, std::vector&& new_reforms, Deployment const* new_inital_oob @@ -21,7 +21,7 @@ Culture const* CountryHistory::get_primary_culture() const { return primary_culture; } -const std::vector& CountryHistory::get_accepted_cultures() const { +std::vector const& CountryHistory::get_accepted_cultures() const { return accepted_cultures; } @@ -37,7 +37,7 @@ Date CountryHistory::get_last_election() const { return last_election; } -const std::map& CountryHistory::get_upper_house() const { +decimal_map_t const& CountryHistory::get_upper_house() const { return upper_house; } @@ -49,7 +49,7 @@ GovernmentType const* CountryHistory::get_government_type() const { return government_type; } -const fixed_point_t CountryHistory::get_plurality() const { +fixed_point_t CountryHistory::get_plurality() const { return plurality; } @@ -57,15 +57,15 @@ NationalValue const* CountryHistory::get_national_value() const { return national_value; } -const bool CountryHistory::is_civilised() const { +bool CountryHistory::is_civilised() const { return civilised; } -const fixed_point_t CountryHistory::get_prestige() const { +fixed_point_t CountryHistory::get_prestige() const { return prestige; } -const std::vector& CountryHistory::get_reforms() const { +std::vector const& CountryHistory::get_reforms() const { return reforms; } @@ -76,9 +76,9 @@ Deployment const* CountryHistory::get_inital_oob() const { bool CountryHistoryManager::add_country_history_entry( Country const* country, Date date, Culture const* primary_culture, std::vector&& accepted_cultures, Religion const* religion, CountryParty const* ruling_party, Date last_election, - std::map&& upper_house, Province const* capital, GovernmentType const* government_type, + decimal_map_t&& upper_house, Province const* capital, GovernmentType const* government_type, fixed_point_t plurality, NationalValue const* national_value, bool civilised, fixed_point_t prestige, - std::vector&& reforms, Deployment const* initial_oob, bool updated_accepted_cultures, + std::vector&& reforms, std::optional initial_oob, bool updated_accepted_cultures, bool updated_upper_house, bool updated_reforms ) { if (locked) { @@ -87,8 +87,8 @@ bool CountryHistoryManager::add_country_history_entry( } /* combine duplicate histories, priority to current (defined later) */ - auto& country_registry = country_histories[country]; - const auto existing_entry = country_registry.find(date); + country_history_map_t& country_registry = country_histories[country]; + const country_history_map_t::iterator existing_entry = country_registry.find(date); if (existing_entry != country_registry.end()) { if (primary_culture != nullptr) { @@ -130,15 +130,15 @@ bool CountryHistoryManager::add_country_history_entry( if (updated_reforms) { existing_entry->second.reforms = std::move(reforms); } - if (initial_oob != nullptr) { - existing_entry->second.inital_oob = initial_oob; + if (initial_oob) { + existing_entry->second.inital_oob = *initial_oob; } } else { country_registry.emplace( date, CountryHistory { primary_culture, std::move(accepted_cultures), religion, ruling_party, last_election, std::move(upper_house), capital, government_type, plurality, national_value, civilised, - prestige, std::move(reforms), initial_oob + prestige, std::move(reforms), std::move(*initial_oob) } ); } @@ -195,57 +195,68 @@ inline CountryHistory const* CountryHistoryManager::get_country_history(Country } inline bool CountryHistoryManager::_load_country_history_entry( - GameManager& game_manager, std::string_view name, Date date, ast::NodeCPtr root + GameManager& game_manager, Dataloader const& dataloader, Country const& country, Date date, ast::NodeCPtr root ) { + PoliticsManager const& politics_manager = game_manager.get_politics_manager(); + IssueManager const& issue_manager = politics_manager.get_issue_manager(); + CultureManager const& culture_manager = game_manager.get_pop_manager().get_culture_manager(); + Province const* capital = nullptr; Culture const* primary_culture = nullptr; Religion const* religion = nullptr; GovernmentType const* government_type = nullptr; NationalValue const* national_value = nullptr; CountryParty const* ruling_party = nullptr; - std::vector accepted_cultures; - std::vector reforms; - std::map upper_house; + std::vector accepted_cultures {}; + std::vector reforms {}; + decimal_map_t upper_house {}; fixed_point_t plurality = -1, prestige = -1; bool civilised = false; Date last_election {}; - Deployment const* initial_oob = nullptr; + std::optional initial_oob; bool updated_accepted_cultures = false, updated_upper_house = false, updated_reforms = false; bool ret = expect_dictionary_keys_and_default( - [this, &game_manager, &reforms, &updated_reforms, &name](std::string_view key, ast::NodeCPtr value) -> bool { - if (game_manager.get_politics_manager().get_issue_manager().has_reform_group_identifier(key)) { + [this, &issue_manager, &reforms, &updated_reforms, &country](std::string_view key, ast::NodeCPtr value) -> bool { + ReformGroup const* reform_group = issue_manager.get_reform_group_by_identifier(key); + if (reform_group != nullptr) { updated_reforms = true; - - Reform const* reform; - - bool ret = game_manager.get_politics_manager().get_issue_manager() - .expect_reform_identifier(assign_variable_callback_pointer(reform))(value); - if (std::find(reforms.begin(), reforms.end(), reform) != reforms.end()) { - Logger::error("Redefinition of reform ", reform->get_identifier(), " in history of ", name); - return false; - } - - reforms.push_back(reform); - return ret; + return issue_manager.expect_reform_identifier( + [reform_group, &reforms, &country](Reform const& reform) -> bool { + if (&reform.get_reform_group() != reform_group) { + Logger::warning( + "Listing ", reform.get_identifier(), " as belonging to the reform group ", + reform_group->get_identifier(), " when it actually belongs to ", + reform.get_reform_group().get_identifier() + ); + } + if (std::find(reforms.begin(), reforms.end(), &reform) != reforms.end()) { + Logger::error( + "Redefinition of reform ", reform.get_identifier(), " in history of ", country.get_identifier() + ); + return false; + } + reforms.push_back(&reform); + return true; + } + )(value); } // TODO: technologies & inventions return true; }, /* we have to use a lambda, assign_variable_callback_pointer * apparently doesn't play nice with const & non-const accessors */ + // TODO - fix this issue (cause by provinces having non-const accessors) "capital", ZERO_OR_ONE, game_manager.get_map().expect_province_identifier([&capital](Province const& province) -> bool { capital = &province; return true; }), "primary_culture", ZERO_OR_ONE, - game_manager.get_pop_manager().get_culture_manager().expect_culture_identifier( - assign_variable_callback_pointer(primary_culture) - ), + culture_manager.expect_culture_identifier(assign_variable_callback_pointer(primary_culture)), "culture", ZERO_OR_MORE, - game_manager.get_pop_manager().get_culture_manager().expect_culture_identifier( + culture_manager.expect_culture_identifier( [&game_manager, &accepted_cultures, &updated_accepted_cultures](Culture const& culture) -> bool { updated_accepted_cultures = true; accepted_cultures.push_back(&culture); @@ -257,102 +268,62 @@ inline bool CountryHistoryManager::_load_country_history_entry( assign_variable_callback_pointer(religion) ), "government", ZERO_OR_ONE, - game_manager.get_politics_manager().get_government_type_manager().expect_government_type_identifier( + politics_manager.get_government_type_manager().expect_government_type_identifier( assign_variable_callback_pointer(government_type) ), "plurality", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(plurality)), "nationalvalue", ZERO_OR_ONE, - game_manager.get_politics_manager().get_national_value_manager().expect_national_value_identifier( + politics_manager.get_national_value_manager().expect_national_value_identifier( assign_variable_callback_pointer(national_value) ), "civilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(civilised)), "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(prestige)), - "ruling_party", ZERO_OR_ONE, - expect_identifier([this, &game_manager, &ruling_party, &name, &date](std::string_view identifier) -> bool { - const std::vector* parties = - &game_manager.get_country_manager().get_country_by_identifier(name)->get_parties(); - for (auto& party : *parties) { - if (party.get_identifier() == identifier) { - if (party.get_start_date() <= date && date <= party.get_end_date()) { - ruling_party = &party; - return true; - } else { - if (party.get_start_date() > date) { - Logger::warning( - "Ruling party ", identifier, " of country ", name, " has invalid start date ", - party.get_start_date(), " for bookmark: ", date.to_string() - ); - } - if (party.get_end_date() < date) { - Logger::warning( - "Ruling party ", identifier, " of country ", name, " has invalid end date ", - party.get_end_date(), " for bookmark: ", date.to_string() - ); - } - ruling_party = &party; - return true; - } - } - } - Logger::error("Ruling party ", identifier, " of country ", name, " is not defined!"); - return false; - }), + "ruling_party", ZERO_OR_ONE, country.expect_party_identifier(assign_variable_callback_pointer(ruling_party)), "last_election", ZERO_OR_ONE, expect_date(assign_variable_callback(last_election)), "upper_house", ZERO_OR_ONE, - game_manager.get_politics_manager().get_ideology_manager().expect_ideology_dictionary( + politics_manager.get_ideology_manager().expect_ideology_dictionary( [&upper_house, &updated_upper_house](Ideology const& ideology, ast::NodeCPtr value) -> bool { - fixed_point_t popularity; - - updated_upper_house = true; - bool ret = expect_fixed_point(assign_variable_callback(popularity))(value); - upper_house.emplace(&ideology, popularity); - return ret; - } - ), - "oob", ZERO_OR_ONE, - [&game_manager, &initial_oob](ast::NodeCPtr node) -> bool { - std::string_view string; - expect_string(assign_variable_callback(string))(node); - - if (string.starts_with('/')) { - if (game_manager.get_military_manager().get_deployment_manager() - .has_deployment_identifier(string.substr(1))) { - initial_oob = game_manager.get_military_manager().get_deployment_manager() - .get_deployment_by_identifier(string.substr(1)); + return expect_fixed_point([&upper_house, &updated_upper_house, &ideology](fixed_point_t val) -> bool { + if (val != 0) { + upper_house[&ideology] += val; + updated_upper_house = true; + } return true; - } - } else { - if (game_manager.get_military_manager().get_deployment_manager().has_deployment_identifier(string)) { - initial_oob = - game_manager.get_military_manager().get_deployment_manager().get_deployment_by_identifier(string); - } + })(value); } - - initial_oob = - game_manager.get_military_manager().get_deployment_manager().get_deployment_by_identifier("NULL"); - return true; - }, + ), + "oob", ZERO_OR_ONE, expect_identifier_or_string([&game_manager, &dataloader, &initial_oob](std::string_view path) -> bool { + if(!initial_oob.has_value()) + initial_oob = decltype(initial_oob)::value_type {}; + return game_manager.get_military_manager().get_deployment_manager().load_oob_file( + game_manager, dataloader, path, *initial_oob, false + ); + }), "schools", ZERO_OR_ONE, success_callback, // TODO: technology school "foreign_investment", ZERO_OR_ONE, success_callback // TODO: foreign investment )(root); ret &= add_country_history_entry( - game_manager.get_country_manager().get_country_by_identifier(name), date, primary_culture, + &country, date, primary_culture, std::move(accepted_cultures), religion, ruling_party, last_election, std::move(upper_house), capital, government_type, - plurality, national_value, civilised, prestige, std::move(reforms), initial_oob, updated_accepted_cultures, + plurality, national_value, civilised, prestige, std::move(reforms), std::move(initial_oob), updated_accepted_cultures, updated_upper_house, updated_reforms ); return ret; } -bool CountryHistoryManager::load_country_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root) { - if (game_manager.get_country_manager().get_country_by_identifier(name)->is_dynamic_tag()) { +bool CountryHistoryManager::load_country_history_file( + GameManager& game_manager, Dataloader const& dataloader, Country const& country, ast::NodeCPtr root +) { + if (country.is_dynamic_tag()) { return true; /* as far as I can tell dynamic countries are hardcoded, broken, and unused */ } - bool ret = _load_country_history_entry(game_manager, name, game_manager.get_define_manager().get_start_date(), root); + bool ret = _load_country_history_entry( + game_manager, dataloader, country, game_manager.get_define_manager().get_start_date(), root + ); - ret &= expect_dictionary([this, &game_manager, &name](std::string_view key, ast::NodeCPtr value) -> bool { + ret &= expect_dictionary([this, &game_manager, &dataloader, &country](std::string_view key, ast::NodeCPtr value) -> bool { bool is_date = false; Date entry = Date::from_string(key, &is_date, true); if (!is_date) { @@ -362,13 +333,13 @@ bool CountryHistoryManager::load_country_history_file(GameManager& game_manager, Date end_date = game_manager.get_define_manager().get_end_date(); if (entry > end_date) { Logger::error( - "History entry ", entry.to_string(), " of country ", name, " defined after defined end date ", - end_date.to_string() + "History entry ", entry.to_string(), " of country ", country.get_identifier(), + " defined after defined end date ", end_date.to_string() ); return false; } - return _load_country_history_entry(game_manager, name, entry, value); + return _load_country_history_entry(game_manager, dataloader, country, entry, value); })(root); return ret; diff --git a/src/openvic-simulation/history/CountryHistory.hpp b/src/openvic-simulation/history/CountryHistory.hpp index 0401ec4..3109d4f 100644 --- a/src/openvic-simulation/history/CountryHistory.hpp +++ b/src/openvic-simulation/history/CountryHistory.hpp @@ -28,7 +28,7 @@ namespace OpenVic { Religion const* religion; CountryParty const* ruling_party; Date last_election; - std::map upper_house; + decimal_map_t upper_house; Province const* capital; GovernmentType const* government_type; fixed_point_t plurality; @@ -43,7 +43,7 @@ namespace OpenVic { CountryHistory( Culture const* new_primary_culture, std::vector&& new_accepted_cultures, Religion const* new_religion, CountryParty const* new_ruling_party, Date new_last_election, - std::map&& new_upper_house, Province const* new_capital, + decimal_map_t&& new_upper_house, Province const* new_capital, GovernmentType const* new_government_type, fixed_point_t new_plurality, NationalValue const* new_national_value, bool new_civilised, fixed_point_t new_prestige, std::vector&& new_reforms, Deployment const* new_inital_oob @@ -51,28 +51,30 @@ namespace OpenVic { public: Culture const* get_primary_culture() const; - const std::vector& get_accepted_cultures() const; + std::vector const& get_accepted_cultures() const; Religion const* get_religion() const; CountryParty const* get_ruling_party() const; Date get_last_election() const; - const std::map& get_upper_house() const; + decimal_map_t const& get_upper_house() const; Province const* get_capital() const; GovernmentType const* get_government_type() const; - const fixed_point_t get_plurality() const; + fixed_point_t get_plurality() const; NationalValue const* get_national_value() const; - const bool is_civilised() const; - const fixed_point_t get_prestige() const; - const std::vector& get_reforms() const; + bool is_civilised() const; + fixed_point_t get_prestige() const; + std::vector const& get_reforms() const; Deployment const* get_inital_oob() const; }; struct CountryHistoryManager { private: - std::map> country_histories; + using country_history_map_t = std::map; + std::map country_histories; bool locked = false; inline bool _load_country_history_entry( - GameManager& game_manager, std::string_view name, Date date, ast::NodeCPtr root + GameManager& game_manager, Dataloader const& dataloader, Country const& country, Date date, + ast::NodeCPtr root ); public: @@ -81,10 +83,10 @@ namespace OpenVic { bool add_country_history_entry( Country const* country, Date date, Culture const* primary_culture, std::vector&& accepted_cultures, Religion const* religion, CountryParty const* ruling_party, Date last_election, - std::map&& upper_house, Province const* capital, - GovernmentType const* government_type, fixed_point_t plurality, NationalValue const* national_value, bool civilised, - fixed_point_t prestige, std::vector&& reforms, Deployment const* initial_oob, - bool updated_accepted_cultures, bool updated_upper_house, bool updated_reforms + decimal_map_t&& upper_house, Province const* capital, GovernmentType const* government_type, + fixed_point_t plurality, NationalValue const* national_value, bool civilised, fixed_point_t prestige, + std::vector&& reforms, std::optional initial_oob, bool updated_accepted_cultures, + bool updated_upper_house, bool updated_reforms ); void lock_country_histories(); @@ -96,7 +98,9 @@ namespace OpenVic { /* Returns history of country at bookmark date. Return can be nullptr if an error occurs. */ inline CountryHistory const* get_country_history(Country const* country, Bookmark const* entry) const; - bool load_country_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root); + bool load_country_history_file( + GameManager& game_manager, Dataloader const& dataloader, Country const& country, ast::NodeCPtr root + ); }; } // namespace OpenVic diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index 6dbf6a4..65e37e1 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -7,9 +7,10 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; ProvinceHistory::ProvinceHistory( - Country const* new_owner, Country const* new_controller, uint8_t new_colonial, bool new_slave, - std::vector&& new_cores, Good const* new_rgo, uint8_t new_life_rating, TerrainType const* new_terrain_type, - std::map&& new_buildings, std::map&& new_party_loyalties + Country const* new_owner, Country const* new_controller, Province::colony_status_t new_colonial, bool new_slave, + std::vector&& new_cores, Good const* new_rgo, Province::life_rating_t new_life_rating, + TerrainType const* new_terrain_type, building_level_map_t&& new_buildings, + decimal_map_t&& new_party_loyalties ) : owner { new_owner }, controller { new_controller }, colonial { new_colonial }, slave { new_slave }, cores { std::move(new_cores) }, rgo { new_rgo }, life_rating { new_life_rating }, terrain_type { new_terrain_type }, buildings { std::move(new_buildings) }, party_loyalties { std::move(new_party_loyalties) } {} @@ -22,7 +23,7 @@ Country const* ProvinceHistory::get_controller() const { return controller; } -uint8_t ProvinceHistory::get_colony_status() const { +Province::colony_status_t ProvinceHistory::get_colony_status() const { return colonial; } @@ -42,7 +43,7 @@ Good const* ProvinceHistory::get_rgo() const { return rgo; } -uint8_t ProvinceHistory::get_life_rating() const { +Province::life_rating_t ProvinceHistory::get_life_rating() const { return life_rating; } @@ -50,20 +51,20 @@ TerrainType const* ProvinceHistory::get_terrain_type() const { return terrain_type; } -std::map const& ProvinceHistory::get_buildings() const { +ProvinceHistory::building_level_map_t const& ProvinceHistory::get_buildings() const { return buildings; } -std::map const& ProvinceHistory::get_party_loyalties() const { +decimal_map_t const& ProvinceHistory::get_party_loyalties() const { return party_loyalties; } bool ProvinceHistoryManager::add_province_history_entry( - Province const* province, Date date, Country const* owner, Country const* controller, std::optional&& colonial, - std::optional&& slave, std::vector&& cores, std::vector&& remove_cores, - Good const* rgo, std::optional&& life_rating, TerrainType const* terrain_type, - std::optional>&& buildings, - std::optional>&& party_loyalties + Province const* province, Date date, Country const* owner, Country const* controller, + std::optional&& colonial, std::optional&& slave, std::vector&& cores, + std::vector&& remove_cores, Good const* rgo, std::optional&& life_rating, + TerrainType const* terrain_type, std::optional&& buildings, + std::optional>&& party_loyalties ) { if (locked) { Logger::error("Cannot add new history entry to province history registry: locked!"); @@ -104,7 +105,7 @@ bool ProvinceHistoryManager::add_province_history_entry( } // province history cores are additive existing_entry->second.cores.insert(existing_entry->second.cores.end(), cores.begin(), cores.end()); - for (const auto which : remove_cores) { + for (Country const* which : remove_cores) { const auto core = std::find(cores.begin(), cores.end(), which); if (core == cores.end()) { Logger::error( @@ -176,114 +177,109 @@ inline ProvinceHistory const* ProvinceHistoryManager::get_province_history( } inline bool ProvinceHistoryManager::_load_province_history_entry( - GameManager& game_manager, std::string_view province, Date date, ast::NodeCPtr root + GameManager const& game_manager, Province const& province, Date date, ast::NodeCPtr root, + bool is_base_entry ) { + BuildingManager const& building_manager = game_manager.get_economy_manager().get_building_manager(); + CountryManager const& country_manager = game_manager.get_country_manager(); + GoodManager const& good_manager = game_manager.get_economy_manager().get_good_manager(); + IdeologyManager const& ideology_manager = game_manager.get_politics_manager().get_ideology_manager(); + TerrainTypeManager const& terrain_type_manager = game_manager.get_map().get_terrain_type_manager(); + Country const* owner = nullptr; Country const* controller = nullptr; - std::vector cores; - std::vector remove_cores; - Good const* rgo; - std::optional life_rating, colonial; + std::vector cores {}; + std::vector remove_cores {}; + Good const* rgo = nullptr; + std::optional colonial; + std::optional life_rating; std::optional slave; - TerrainType const* terrain_type; - std::optional> buildings; - std::optional> party_loyalties; + TerrainType const* terrain_type = nullptr; + std::optional buildings; + std::optional> party_loyalties; + + using enum Province::colony_status_t; + static const string_map_t colony_status_map { + { "0", STATE }, { "1", PROTECTORATE }, { "2", COLONY } + }; bool ret = expect_dictionary_keys_and_default( - [&game_manager, &buildings](std::string_view key, ast::NodeCPtr value) -> bool { + [&building_manager, &buildings, date, is_base_entry](std::string_view key, ast::NodeCPtr value) -> bool { // used for province buildings like forts or railroads - if (game_manager.get_economy_manager().get_building_manager().has_building_identifier(key)) { - Building const* building; - uint8_t level; - - bool ret = game_manager.get_economy_manager().get_building_manager() - .expect_building_str(assign_variable_callback_pointer(building))(key); - ret &= expect_uint(assign_variable_callback(level))(value); - - if(!buildings.has_value()) - buildings = decltype(buildings)::value_type {}; - buildings->emplace(building, level); - return ret; + Building const* building = building_manager.get_building_by_identifier(key); + if (building != nullptr) { + return expect_uint([&buildings, building](Building::level_t level) -> bool { + if(!buildings.has_value()) + buildings = decltype(buildings)::value_type {}; + buildings->emplace(building, level); + return true; + })(value); } - bool is_date; - Date::from_string(key, &is_date, true); + /* Date blocks are skipped here (they get their own invocation of _load_province_history_entry) */ + bool is_date = false; + const Date sub_date { Date::from_string(key, &is_date, true) }; if (is_date) { - return true; + if (is_base_entry) { + return true; + } else { + Logger::error( + "Province history nested multiple levels deep! ", sub_date, " is inside ", date, + " (Any date blocks within a date block are ignored)" + ); + return false; + } } return key_value_invalid_callback(key, value); }, - "owner", ZERO_OR_ONE, game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(owner)), - "controller", ZERO_OR_ONE, game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(controller)), - "add_core", ZERO_OR_MORE, [&game_manager, &cores](ast::NodeCPtr node) -> bool { - Country const* core; - - bool ret = game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(core))(node); - cores.push_back(core); - return ret; - }, - "remove_core", ZERO_OR_MORE, [&game_manager, &remove_cores](ast::NodeCPtr node) -> bool { - Country const* remove; - - bool ret = game_manager.get_country_manager() - .expect_country_identifier(assign_variable_callback_pointer(remove))(node); - remove_cores.push_back(remove); - return ret; - }, - "colonial", ZERO_OR_ONE, expect_uint([&colonial](uint8_t colony_level) -> bool { - colonial = colony_level; - - return true; - }), - "colony", ZERO_OR_ONE, expect_uint([&colonial](uint8_t colony_level) -> bool { - colonial = colony_level; - - return true; - }), - "is_slave", ZERO_OR_ONE, expect_bool([&slave](bool is_slave) -> bool { - if (is_slave) { - slave = true; - } else { - slave = false; + "owner", ZERO_OR_ONE, country_manager.expect_country_identifier(assign_variable_callback_pointer(owner)), + "controller", ZERO_OR_ONE, country_manager.expect_country_identifier(assign_variable_callback_pointer(controller)), + "add_core", ZERO_OR_MORE, country_manager.expect_country_identifier( + [&cores](Country const& core) -> bool { + cores.push_back(&core); + return true; } - - return true; - }), - "trade_goods", ZERO_OR_ONE, game_manager.get_economy_manager().get_good_manager() - .expect_good_identifier(assign_variable_callback_pointer(rgo)), - "life_rating", ZERO_OR_ONE, expect_uint([&life_rating](uint8_t rating) -> bool { - life_rating = rating; - - return true; - }), - "terrain", ZERO_OR_ONE, game_manager.get_map().get_terrain_type_manager() - .expect_terrain_type_identifier(assign_variable_callback_pointer(terrain_type)), - "party_loyalty", ZERO_OR_MORE, [&game_manager, &party_loyalties](ast::NodeCPtr node) -> bool { - Ideology const* ideology; - uint8_t amount; // percent I do believe - - bool ret = expect_dictionary_keys( - "ideology", ONE_EXACTLY, game_manager.get_politics_manager().get_ideology_manager() - .expect_ideology_identifier(assign_variable_callback_pointer(ideology)), - "loyalty_value", ONE_EXACTLY, expect_uint(assign_variable_callback(amount)) + ), + "remove_core", ZERO_OR_MORE, country_manager.expect_country_identifier( + [&remove_cores](Country const& core) -> bool { + remove_cores.push_back(&core); + return true; + } + ), + "colonial", ZERO_OR_ONE, + expect_identifier(expect_mapped_string(colony_status_map, assign_variable_callback(colonial))), + "colony", ZERO_OR_ONE, expect_identifier(expect_mapped_string(colony_status_map, assign_variable_callback(colonial))), + "is_slave", ZERO_OR_ONE, expect_bool(assign_variable_callback(slave)), + "trade_goods", ZERO_OR_ONE, good_manager.expect_good_identifier(assign_variable_callback_pointer(rgo)), + "life_rating", ZERO_OR_ONE, expect_uint(assign_variable_callback(life_rating)), + "terrain", ZERO_OR_ONE, terrain_type_manager.expect_terrain_type_identifier( + assign_variable_callback_pointer(terrain_type) + ), + "party_loyalty", ZERO_OR_MORE, [&ideology_manager, &party_loyalties](ast::NodeCPtr node) -> bool { + Ideology const* ideology = nullptr; + fixed_point_t amount = 0; // percent I do believe + + const bool ret = expect_dictionary_keys( + "ideology", ONE_EXACTLY, ideology_manager.expect_ideology_identifier( + assign_variable_callback_pointer(ideology) + ), + "loyalty_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(amount)) )(node); if(!party_loyalties.has_value()) party_loyalties = decltype(party_loyalties)::value_type {}; party_loyalties->emplace(ideology, amount); return ret; }, - "state_building", ZERO_OR_MORE, [&game_manager, &buildings](ast::NodeCPtr node) -> bool { - Building const* building; - uint8_t level; + "state_building", ZERO_OR_MORE, [&building_manager, &buildings](ast::NodeCPtr node) -> bool { + Building const* building = nullptr; + uint8_t level = 0; - bool ret = expect_dictionary_keys( + const bool ret = expect_dictionary_keys( "level", ONE_EXACTLY, expect_uint(assign_variable_callback(level)), - "building", ONE_EXACTLY, game_manager.get_economy_manager().get_building_manager() - .expect_building_identifier(assign_variable_callback_pointer(building)), + "building", ONE_EXACTLY, building_manager.expect_building_identifier( + assign_variable_callback_pointer(building) + ), "upgrade", ZERO_OR_ONE, success_callback // doesn't appear to have an effect )(node); if(!buildings.has_value()) @@ -294,34 +290,37 @@ inline bool ProvinceHistoryManager::_load_province_history_entry( )(root); ret &= add_province_history_entry( - game_manager.get_map().get_province_by_identifier(province), date, owner, controller, std::move(colonial), std::move(slave), - std::move(cores), std::move(remove_cores), rgo, std::move(life_rating), terrain_type, std::move(buildings), - std::move(party_loyalties) + &province, date, owner, controller, std::move(colonial), std::move(slave), std::move(cores), std::move(remove_cores), + rgo, std::move(life_rating), terrain_type, std::move(buildings), std::move(party_loyalties) ); return ret; } -bool ProvinceHistoryManager::load_province_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root) { - bool ret = _load_province_history_entry(game_manager, name, game_manager.get_define_manager().get_start_date(), root); +bool ProvinceHistoryManager::load_province_history_file( + GameManager const& game_manager, Province const& province, ast::NodeCPtr root +) { + bool ret = _load_province_history_entry( + game_manager, province, game_manager.get_define_manager().get_start_date(), root, true + ); ret &= expect_dictionary( - [this, &game_manager, &name](std::string_view key, ast::NodeCPtr value) -> bool { + [this, &game_manager, &province, end_date = game_manager.get_define_manager().get_end_date()]( + std::string_view key, ast::NodeCPtr value) -> bool { bool is_date = false; - Date entry = Date::from_string(key, &is_date, true); + const Date entry = Date::from_string(key, &is_date, true); if (!is_date) { return true; } - Date end_date = game_manager.get_define_manager().get_end_date(); if (entry > end_date) { Logger::error( - "History entry ", entry.to_string(), " of province ", name, " defined after defined end date ", - end_date.to_string() + "History entry ", entry, " of province ", province.get_identifier(), + " defined after defined end date ", end_date ); return false; } - return _load_province_history_entry(game_manager, name, entry, value); + return _load_province_history_entry(game_manager, province, entry, value, false); } )(root); diff --git a/src/openvic-simulation/history/ProvinceHistory.hpp b/src/openvic-simulation/history/ProvinceHistory.hpp index 90c87e2..fb90cc4 100644 --- a/src/openvic-simulation/history/ProvinceHistory.hpp +++ b/src/openvic-simulation/history/ProvinceHistory.hpp @@ -17,37 +17,39 @@ namespace OpenVic { struct ProvinceHistory { friend struct ProvinceHistoryManager; + using building_level_map_t = std::map; + private: Country const* owner; Country const* controller; - uint8_t colonial; + Province::colony_status_t colonial; bool slave; std::vector cores; // non-standard, maintains cores between entries Good const* rgo; - uint8_t life_rating; + Province::life_rating_t life_rating; TerrainType const* terrain_type; - std::map buildings; - std::map party_loyalties; + building_level_map_t buildings; + decimal_map_t party_loyalties; ProvinceHistory( - Country const* new_owner, Country const* new_controller, uint8_t new_colonial, bool new_slave, - std::vector&& new_cores, Good const* new_rgo, uint8_t new_life_rating, - TerrainType const* new_terrain_type, std::map&& new_buildings, - std::map&& new_party_loyalties + Country const* new_owner, Country const* new_controller, Province::colony_status_t new_colonial, bool new_slave, + std::vector&& new_cores, Good const* new_rgo, Province::life_rating_t new_life_rating, + TerrainType const* new_terrain_type, building_level_map_t&& new_buildings, + decimal_map_t&& new_party_loyalties ); public: Country const* get_owner() const; Country const* get_controller() const; - uint8_t get_colony_status() const; // 0 = state, 1 = protectorate, 2 = colony + Province::colony_status_t get_colony_status() const; // 0 = state, 1 = protectorate, 2 = colony bool is_slave() const; std::vector const& get_cores() const; bool is_core_of(Country const* country) const; Good const* get_rgo() const; - uint8_t get_life_rating() const; + Province::life_rating_t get_life_rating() const; TerrainType const* get_terrain_type() const; - std::map const& get_buildings() const; - std::map const& get_party_loyalties() const; + building_level_map_t const& get_buildings() const; + decimal_map_t const& get_party_loyalties() const; }; struct ProvinceHistoryManager { @@ -56,7 +58,8 @@ namespace OpenVic { bool locked = false; inline bool _load_province_history_entry( - GameManager& game_manager, std::string_view province, Date date, ast::NodeCPtr root + GameManager const& game_manager, Province const& province, Date date, ast::NodeCPtr root, + bool is_base_entry ); public: @@ -64,12 +67,12 @@ namespace OpenVic { bool add_province_history_entry( Province const* province, Date date, Country const* owner, Country const* controller, - std::optional&& colonial, std::optional&& slave, + std::optional&& colonial, std::optional&& slave, std::vector&& cores, // additive to existing entries std::vector&& remove_cores, // existing cores that need to be removed - Good const* rgo, std::optional&& life_rating, TerrainType const* terrain_type, - std::optional>&& buildings, - std::optional>&& party_loyalties + Good const* rgo, std::optional&& life_rating, TerrainType const* terrain_type, + std::optional&& buildings, + std::optional>&& party_loyalties ); void lock_province_histories(); @@ -81,6 +84,8 @@ namespace OpenVic { /* Returns history of province at bookmark date. Return can be nullptr if an error occurs. */ inline ProvinceHistory const* get_province_history(Province const* province, Bookmark const* bookmark) const; - bool load_province_history_file(GameManager& game_manager, std::string_view name, ast::NodeCPtr root); + bool load_province_history_file( + GameManager const& game_manager, Province const& province, ast::NodeCPtr root + ); }; } // namespace OpenVic diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp index 4790853..b1aea2c 100644 --- a/src/openvic-simulation/map/Map.cpp +++ b/src/openvic-simulation/map/Map.cpp @@ -514,7 +514,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain goto set_terrain; } } - if (unrecognised_province_colours.find(province_colour) == unrecognised_province_colours.end()) { + if (!unrecognised_province_colours.contains(province_colour)) { unrecognised_province_colours.insert(province_colour); if (detailed_errors) { Logger::warning( diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp index 7b88ebf..66e4daf 100644 --- a/src/openvic-simulation/map/Province.cpp +++ b/src/openvic-simulation/map/Province.cpp @@ -5,7 +5,7 @@ using namespace OpenVic::NodeTools; Province::Province( std::string_view new_identifier, colour_t new_colour, index_t new_index -) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, index { new_index }, +) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, index { new_index }, buildings { "buildings", false } { assert(index != NULL_INDEX); } @@ -38,6 +38,10 @@ Province::life_rating_t Province::get_life_rating() const { return life_rating; } +Province::colony_status_t Province::get_colony_status() const { + return colony_status; +} + bool Province::load_positions(BuildingManager const& building_manager, ast::NodeCPtr root) { return expect_dictionary_keys( "text_position", ZERO_OR_ONE, expect_fvec2(assign_variable_callback(positions.text)), diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp index 10f3a9a..6c022b7 100644 --- a/src/openvic-simulation/map/Province.hpp +++ b/src/openvic-simulation/map/Province.hpp @@ -24,6 +24,8 @@ namespace OpenVic { using distance_t = uint16_t; using flags_t = uint16_t; + enum struct colony_status_t : int8_t { STATE, PROTECTORATE, COLONY }; + struct adjacency_t { friend struct Province; @@ -63,6 +65,7 @@ namespace OpenVic { Region* region = nullptr; bool on_map = false, has_region = false, water = false; life_rating_t life_rating = 0; + colony_status_t colony_status = colony_status_t::STATE; IdentifierRegistry buildings; // TODO - change this into a factory-like structure Good const* rgo = nullptr; @@ -90,6 +93,7 @@ namespace OpenVic { bool get_water() const; TerrainType const* get_terrain_type() const; life_rating_t get_life_rating() const; + colony_status_t get_colony_status() const; bool load_positions(BuildingManager const& building_manager, ast::NodeCPtr root); bool add_building(BuildingInstance&& building_instance); diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp index 1753246..796089e 100644 --- a/src/openvic-simulation/map/TerrainType.cpp +++ b/src/openvic-simulation/map/TerrainType.cpp @@ -7,7 +7,7 @@ using namespace OpenVic::NodeTools; TerrainType::TerrainType( std::string_view new_identifier, colour_t new_colour, ModifierValue&& new_modifier, bool new_is_water -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, modifier { std::move(new_modifier) }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, modifier { std::move(new_modifier) }, is_water { new_is_water } {} ModifierValue const& TerrainType::get_modifier() const { diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index 5b3aa77..b5d335a 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -5,22 +5,28 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -Deployment::Deployment( - std::string_view new_path, std::vector&& new_armies, std::vector&& new_navies, std::vector&& new_leaders -) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) }, - leaders { std::move(new_leaders) } {} +Leader::Leader( + std::string_view new_name, Unit::type_t new_type, Date new_date, LeaderTrait const* new_personality, + LeaderTrait const* new_background, fixed_point_t new_prestige +) : name { new_name }, type { new_type }, date { new_date }, personality { new_personality }, background { new_background }, + prestige { new_prestige } {} -const std::vector& Deployment::get_armies() const { - return armies; -} +Regiment::Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home) + : name { new_name }, type { new_type }, home { new_home } {} -const std::vector& Deployment::get_navies() const { - return navies; -} +Ship::Ship(std::string_view new_name, Unit const* new_type) : name { new_name }, type { new_type } {} -const std::vector& Deployment::get_leaders() const { - return leaders; -} +Army::Army(std::string_view new_name, Province const* new_location, std::vector&& new_regiments) + : name { new_name }, location { new_location }, regiments { std::move(new_regiments) } {} + +Navy::Navy(std::string_view new_name, Province const* new_location, std::vector&& new_ships) + : name { new_name }, location { new_location }, ships { std::move(new_ships) } {} + +Deployment::Deployment( + std::string_view new_path, std::vector&& new_armies, std::vector&& new_navies, + std::vector&& new_leaders +) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) }, + leaders { std::move(new_leaders) } {} DeploymentManager::DeploymentManager() : deployments { "deployments" } {} @@ -31,117 +37,150 @@ bool DeploymentManager::add_deployment( Logger::error("Attemped to load order of battle with no path! Something is very wrong!"); return false; } - if (armies.empty() && navies.empty() && leaders.empty() && path != "NULL") { + if (armies.empty() && navies.empty() && leaders.empty()) { Logger::warning("Loading redundant empty order of battle at ", path); } - return deployments.add_item({ path, std::move(armies), std::move(navies), std::move(leaders) }); + return deployments.add_item( + std::make_unique(std::move(path), std::move(armies), std::move(navies), std::move(leaders)) + ); } -bool DeploymentManager::load_oob_file(GameManager& game_manager, std::string_view path, ast::NodeCPtr root) { +bool DeploymentManager::load_oob_file( + GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path, Deployment const*& deployment, + bool fail_on_missing +) { + deployment = get_deployment_by_identifier(history_path); + if (deployment != nullptr) { + return true; + } + if (missing_oob_files.contains(history_path)) { + return !fail_on_missing; + } + static const fs::path oob_directory = "history/units/"; + fs::path full_path = oob_directory; + full_path += history_path; + const fs::path lookedup_path = dataloader.lookup_file(full_path, false); + if (lookedup_path.empty()) { + missing_oob_files.emplace(history_path); + if (fail_on_missing) { + Logger::warning("Could not find OOB file ", full_path, "!"); + return false; + } else { + return true; + } + } std::vector armies; std::vector navies; std::vector leaders; - bool ret = expect_dictionary_keys_and_default( key_value_success_callback, // TODO: load SOI information "leader", ZERO_OR_MORE, [&leaders, &game_manager](ast::NodeCPtr node) -> bool { - std::string_view name; - Unit::type_t type; - Date date; - LeaderTrait const* personality = nullptr; - LeaderTrait const* background = nullptr; - fixed_point_t prestige = 0; + std::string_view leader_name {}; + Unit::type_t leader_type = Unit::type_t::LAND; + Date leader_date {}; + LeaderTrait const* leader_personality = nullptr; + LeaderTrait const* leader_background = nullptr; + fixed_point_t leader_prestige = 0; bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), - "date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(date))), - "type", ONE_EXACTLY, expect_identifier([&type](std::string_view leader_type) -> bool { - if (leader_type == "land") { - type = Unit::type_t::LAND; - } else { - type = Unit::type_t::NAVAL; - } - return true; - }), + "name", ONE_EXACTLY, expect_string(assign_variable_callback(leader_name)), + "date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(leader_date))), + "type", ONE_EXACTLY, expect_identifier(UnitManager::expect_type_str(assign_variable_callback(leader_type))), "personality", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager() - .expect_leader_trait_identifier(assign_variable_callback_pointer(personality)), + .expect_leader_trait_identifier(assign_variable_callback_pointer(leader_personality)), "background", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager() - .expect_leader_trait_identifier(assign_variable_callback_pointer(background)), - "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(prestige)), + .expect_leader_trait_identifier(assign_variable_callback_pointer(leader_background)), + "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(leader_prestige)), "picture", ZERO_OR_ONE, success_callback )(node); - if (!personality->is_personality_trait()) { - Logger::error("Leader ", name, " has personality ", personality->get_identifier(), - " which is not a personality trait!"); - return false; + if (!leader_personality->is_personality_trait()) { + Logger::error( + "Leader ", leader_name, " has personality ", leader_personality->get_identifier(), + " which is not a personality trait!" + ); + ret = false; } - if (!background->is_background_trait()) { - Logger::error("Leader ", name, " has background ", background->get_identifier(), - " which is not a background trait!"); - return false; + if (!leader_background->is_background_trait()) { + Logger::error( + "Leader ", leader_name, " has background ", leader_background->get_identifier(), + " which is not a background trait!" + ); + ret = false; } - - leaders.push_back(Leader{ std::string(name), type, date, personality, background, prestige }); + leaders.emplace_back( + leader_name, leader_type, leader_date, leader_personality, leader_background, leader_prestige + ); return ret; }, "army", ZERO_OR_MORE, [&armies, &game_manager](ast::NodeCPtr node) -> bool { - std::string_view name; - Province const* location = nullptr; - std::vector regiments; + std::string_view army_name {}; + Province const* army_location = nullptr; + std::vector army_regiments {}; - bool ret = expect_dictionary_keys( - /* another paradox gem, tested in game and they don't lead the army or even show up */ - "leader", ZERO_OR_MORE, success_callback, - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(army_name)), "location", ONE_EXACTLY, - game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(location)), - "regiment", ONE_OR_MORE, [&game_manager, ®iments](ast::NodeCPtr node) -> bool { - Regiment regiment; - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(regiment.name), false), + game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(army_location)), + "regiment", ONE_OR_MORE, [&game_manager, &army_regiments](ast::NodeCPtr node) -> bool { + std::string_view regiment_name {}; + Unit const* regiment_type = nullptr; + Province const* regiment_home = nullptr; + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(regiment_name)), "type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_manager() - .expect_unit_identifier(assign_variable_callback_pointer(regiment.type)), - "home", ONE_EXACTLY, game_manager.get_map() - .expect_province_identifier(assign_variable_callback_pointer(regiment.home)) + .expect_unit_identifier(assign_variable_callback_pointer(regiment_type)), + "home", ZERO_OR_ONE, game_manager.get_map() + .expect_province_identifier(assign_variable_callback_pointer(regiment_home)) )(node); - regiments.push_back(regiment); + if (regiment_home == nullptr) { + Logger::warning("Regiment ", regiment_name, " has no home province!"); + } + army_regiments.emplace_back(regiment_name, regiment_type, regiment_home); return ret; - } + }, + /* another paradox gem, tested in game and they don't lead the army or even show up */ + "leader", ZERO_OR_MORE, success_callback )(node); - armies.push_back(Army{ std::string(name), location, std::move(regiments) }); + armies.emplace_back(army_name, army_location, std::move(army_regiments)); return ret; }, "navy", ZERO_OR_MORE, [&navies, &game_manager](ast::NodeCPtr node) -> bool { - std::string_view name; - Province const* location = nullptr; - std::vector ships; + std::string_view navy_name {}; + Province const* navy_location = nullptr; + std::vector navy_ships {}; - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(navy_name)), "location", ONE_EXACTLY, - game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(location)), - "ship", ONE_OR_MORE, [&game_manager, &ships](ast::NodeCPtr node) -> bool { - Ship ship; - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback_string(ship.name), false), + game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(navy_location)), + "ship", ONE_OR_MORE, [&game_manager, &navy_ships](ast::NodeCPtr node) -> bool { + std::string_view ship_name {}; + Unit const* ship_type = nullptr; + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(ship_name)), "type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_manager() - .expect_unit_identifier(assign_variable_callback_pointer(ship.type)) + .expect_unit_identifier(assign_variable_callback_pointer(ship_type)) )(node); - ships.push_back(ship); + navy_ships.emplace_back(ship_name, ship_type); return ret; }, + /* another paradox gem, tested in game and they don't lead the army or even show up */ "leader", ZERO_OR_MORE, success_callback )(node); - navies.push_back(Navy{ std::string(name), location, std::move(ships) }); + navies.emplace_back(navy_name, navy_location, std::move(navy_ships)); return ret; } - )(root); - /* need to do this for platform compatibility of identifiers */ - std::string identifier = std::string { path }; - std::replace(identifier.begin(), identifier.end(), '\\', '/'); - ret &= add_deployment(identifier, std::move(armies), std::move(navies), std::move(leaders)); - + )(Dataloader::parse_defines(lookedup_path).get_file_node()); + ret &= add_deployment(history_path, std::move(armies), std::move(navies), std::move(leaders)); + deployment = get_deployment_by_identifier(history_path); + if (deployment == nullptr) { + ret = false; + } return ret; } + +size_t DeploymentManager::get_missing_oob_file_count() const { + return missing_oob_files.size(); +} diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp index 34cf82a..ead324e 100644 --- a/src/openvic-simulation/military/Deployment.hpp +++ b/src/openvic-simulation/military/Deployment.hpp @@ -15,46 +15,69 @@ namespace OpenVic { struct Leader { - std::string name; - const Unit::type_t type; - const Date date; - LeaderTrait const* personality; - LeaderTrait const* background; - fixed_point_t prestige; + private: + std::string PROPERTY(name); + Unit::type_t PROPERTY(type); + Date PROPERTY(date); + LeaderTrait const* PROPERTY(personality); + LeaderTrait const* PROPERTY(background); + fixed_point_t PROPERTY(prestige); + + public: + Leader( + std::string_view new_name, Unit::type_t new_type, Date new_date, LeaderTrait const* new_personality, + LeaderTrait const* new_background, fixed_point_t new_prestige + ); }; struct Regiment { - std::string name; - Unit const* type; - Province const* home; + private: + std::string PROPERTY(name); + Unit const* PROPERTY(type); + Province const* PROPERTY(home); + + public: + Regiment(std::string_view new_name, Unit const* new_type, Province const* new_home); }; struct Ship { - std::string name; - Unit const* type; + private: + std::string PROPERTY(name); + Unit const* PROPERTY(type); + + public: + Ship(std::string_view new_name, Unit const* new_type); }; struct Army { - std::string name; - Province const* location; - std::vector regiments; + private: + std::string PROPERTY(name); + Province const* PROPERTY(location); + std::vector PROPERTY(regiments); + + public: + Army(std::string_view new_name, Province const* new_location, std::vector&& new_regiments); }; struct Navy { - std::string name; - Province const* location; - std::vector ships; - }; + private: + std::string PROPERTY(name); + Province const* PROPERTY(location); + std::vector PROPERTY(ships); - struct DeploymentManager; + public: + Navy(std::string_view new_name, Province const* new_location, std::vector&& new_ships); + }; struct Deployment : HasIdentifier { - friend struct DeploymentManager; + friend std::unique_ptr std::make_unique( + std::string_view&&, std::vector&&, std::vector&&, std::vector&& + ); private: - const std::vector armies; - const std::vector navies; - const std::vector leaders; + std::vector PROPERTY(armies); + std::vector PROPERTY(navies); + std::vector PROPERTY(leaders); Deployment( std::string_view new_path, std::vector&& new_armies, std::vector&& new_navies, @@ -62,14 +85,13 @@ namespace OpenVic { ); public: - const std::vector& get_armies() const; - const std::vector& get_navies() const; - const std::vector& get_leaders() const; + Deployment(Deployment&&) = default; }; struct DeploymentManager { private: - IdentifierRegistry deployments; + IdentifierInstanceRegistry deployments; + string_set_t missing_oob_files; public: DeploymentManager(); @@ -77,8 +99,12 @@ namespace OpenVic { bool add_deployment( std::string_view path, std::vector&& armies, std::vector&& navies, std::vector&& leaders ); - IDENTIFIER_REGISTRY_ACCESSORS(deployment); + IDENTIFIER_REGISTRY_ACCESSORS(deployment) - bool load_oob_file(GameManager& game_manager, std::string_view path, ast::NodeCPtr root); + bool load_oob_file( + GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path, + Deployment const*& deployment, bool fail_on_missing + ); + size_t get_missing_oob_file_count() const; }; } // namespace OpenVic diff --git a/src/openvic-simulation/military/Unit.cpp b/src/openvic-simulation/military/Unit.cpp index 1b5f2d2..5322a88 100644 --- a/src/openvic-simulation/military/Unit.cpp +++ b/src/openvic-simulation/military/Unit.cpp @@ -244,6 +244,12 @@ bool UnitManager::add_naval_unit(std::string_view identifier, UNIT_PARAMS, NAVY_ return units.add_item(NavalUnit { identifier, UNIT_ARGS, NAVY_ARGS }); } +callback_t UnitManager::expect_type_str(Callback auto callback) { + using enum Unit::type_t; + static const string_map_t type_map = { { "land", LAND }, { "naval", NAVAL }, { "sea", NAVAL } }; + return expect_mapped_string(type_map, callback); +} + bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr root) { return expect_dictionary([this, &good_manager](std::string_view key, ast::NodeCPtr value) -> bool { Unit::type_t type; @@ -256,9 +262,7 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr fixed_point_t weighted_value = 0, supply_consumption = 0; Good::good_map_t build_cost, supply_cost; - using enum Unit::type_t; - static const string_map_t type_map = { { "land", LAND }, { "naval", NAVAL } }; - bool ret = expect_key("type", expect_identifier(expect_mapped_string(type_map, assign_variable_callback(type))))(value); + bool ret = expect_key("type", expect_identifier(expect_type_str(assign_variable_callback(type))))(value); if (!ret) { Logger::error("Failed to read type for unit: ", key); @@ -288,9 +292,9 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr ); switch (type) { - case LAND: { + case Unit::type_t::LAND: { bool primary_culture = false; - std::string_view sprite_override, sprite_mount, sprite_mount_attach_node; + std::string_view sprite_override {}, sprite_mount {}, sprite_mount_attach_node {}; fixed_point_t reconnaissance = 0, attack = 0, defence = 0, discipline = 0, support = 0, maneuver = 0, siege = 0; ret &= add_key_map_entries(key_map, @@ -313,7 +317,7 @@ bool UnitManager::load_unit_file(GoodManager const& good_manager, ast::NodeCPtr return ret; } - case NAVAL: { + case Unit::type_t::NAVAL: { Unit::icon_t naval_icon = 0; bool sail = false, transport = false, capital = false, build_overseas = false; uint32_t min_port_level = 0; diff --git a/src/openvic-simulation/military/Unit.hpp b/src/openvic-simulation/military/Unit.hpp index acfc8b8..ab371e8 100644 --- a/src/openvic-simulation/military/Unit.hpp +++ b/src/openvic-simulation/military/Unit.hpp @@ -169,6 +169,8 @@ namespace OpenVic { bool add_naval_unit(std::string_view identifier, UNIT_PARAMS, NAVY_PARAMS); IDENTIFIER_REGISTRY_ACCESSORS(unit) + static NodeTools::callback_t expect_type_str(NodeTools::Callback auto callback); + bool load_unit_file(GoodManager const& good_manager, ast::NodeCPtr root); }; } diff --git a/src/openvic-simulation/misc/Define.hpp b/src/openvic-simulation/misc/Define.hpp index 56ce0b0..be71f9d 100644 --- a/src/openvic-simulation/misc/Define.hpp +++ b/src/openvic-simulation/misc/Define.hpp @@ -15,8 +15,8 @@ namespace OpenVic { enum class Type : unsigned char { None, Country, Economy, Military, Diplomacy, Pops, Ai, Graphics }; private: - std::string HASID_PROPERTY(value); - Type HASID_PROPERTY(type); + const std::string PROPERTY(value); + const Type PROPERTY(type); Define(std::string_view new_identifier, std::string&& new_value, Type new_type); @@ -40,7 +40,7 @@ namespace OpenVic { bool add_define(std::string_view name, std::string&& value, Define::Type type); bool add_date_define(std::string_view name, Date date); - IDENTIFIER_REGISTRY_ACCESSORS(define); + IDENTIFIER_REGISTRY_ACCESSORS(define) Date get_start_date() const; Date get_end_date() const; diff --git a/src/openvic-simulation/politics/Government.cpp b/src/openvic-simulation/politics/Government.cpp index 823284a..3b0f28d 100644 --- a/src/openvic-simulation/politics/Government.cpp +++ b/src/openvic-simulation/politics/Government.cpp @@ -85,7 +85,7 @@ bool GovernmentTypeManager::load_government_types_file(IdeologyManager const& id [this, &ideology_manager, &ideologies, government_type_identifier]( std::string_view key, ast::NodeCPtr value) -> bool { static const string_set_t reserved_keys = { "election", "duration", "appoint_ruling_party", "flagType" }; - if (reserved_keys.find(key) != reserved_keys.end()) { + if (reserved_keys.contains(key)) { return true; } Ideology const* ideology = ideology_manager.get_ideology_by_identifier(key); diff --git a/src/openvic-simulation/politics/Ideology.cpp b/src/openvic-simulation/politics/Ideology.cpp index 5721a57..148d6ac 100644 --- a/src/openvic-simulation/politics/Ideology.cpp +++ b/src/openvic-simulation/politics/Ideology.cpp @@ -8,7 +8,7 @@ IdeologyGroup::IdeologyGroup(std::string_view new_identifier) : HasIdentifier { Ideology::Ideology( std::string_view new_identifier, colour_t new_colour, IdeologyGroup const& new_group, bool new_uncivilised, bool new_can_reduce_militancy, Date new_spawn_date -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, uncivilised { new_uncivilised }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, uncivilised { new_uncivilised }, can_reduce_militancy { new_can_reduce_militancy }, spawn_date { new_spawn_date } {} IdeologyGroup const& Ideology::get_group() const { diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp index e386930..9159fe1 100644 --- a/src/openvic-simulation/pop/Culture.cpp +++ b/src/openvic-simulation/pop/Culture.cpp @@ -28,7 +28,7 @@ bool CultureGroup::get_is_overseas() const { Culture::Culture( std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, std::vector&& new_first_names, std::vector&& new_last_names -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) } {} CultureGroup const& Culture::get_group() const { @@ -195,7 +195,7 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) { CultureGroup const* culture_group = get_culture_group_by_identifier(culture_group_key); return expect_dictionary([this, culture_group](std::string_view key, ast::NodeCPtr value) -> bool { static const string_set_t reserved_keys = { "leader", "unit", "union", "is_overseas" }; - if (reserved_keys.find(key) != reserved_keys.end()) { + if (reserved_keys.contains(key)) { return true; } return _load_culture(culture_group, key, value); diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index f63a704..0b61096 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -52,7 +52,7 @@ PopType::PopType( Good::good_map_t&& new_life_needs, Good::good_map_t&& new_everyday_needs, Good::good_map_t&& new_luxury_needs, rebel_units_t&& new_rebel_units, Pop::pop_size_t new_max_size, Pop::pop_size_t new_merge_max_size, bool new_state_capital_only, bool new_demote_migrant, bool new_is_artisan, bool new_is_slave -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, strata { new_strata }, sprite { new_sprite }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, strata { new_strata }, sprite { new_sprite }, life_needs { std::move(new_life_needs) }, everyday_needs { std::move(new_everyday_needs) }, luxury_needs { std::move(new_luxury_needs) }, rebel_units { std::move(new_rebel_units) }, max_size { new_max_size }, merge_max_size { new_merge_max_size }, state_capital_only { new_state_capital_only }, diff --git a/src/openvic-simulation/pop/Religion.cpp b/src/openvic-simulation/pop/Religion.cpp index 489984d..99915c3 100644 --- a/src/openvic-simulation/pop/Religion.cpp +++ b/src/openvic-simulation/pop/Religion.cpp @@ -9,7 +9,7 @@ ReligionGroup::ReligionGroup(std::string_view new_identifier) : HasIdentifier { Religion::Religion( std::string_view new_identifier, colour_t new_colour, ReligionGroup const& new_group, icon_t new_icon, bool new_pagan -) : HasIdentifierAndColour { new_identifier, new_colour, true, false }, group { new_group }, icon { new_icon }, +) : HasIdentifierAndColour { new_identifier, new_colour, false, false }, group { new_group }, icon { new_icon }, pagan { new_pagan } { assert(icon > 0); } diff --git a/src/openvic-simulation/types/Date.hpp b/src/openvic-simulation/types/Date.hpp index c3fee15..b6e693c 100644 --- a/src/openvic-simulation/types/Date.hpp +++ b/src/openvic-simulation/types/Date.hpp @@ -4,9 +4,11 @@ #include #include +#include "openvic-simulation/utility/Getters.hpp" + namespace OpenVic { // A relative period between points in time, measured in days - struct Timespan { + struct Timespan : ReturnByValueProperty { using day_t = int64_t; private: @@ -44,7 +46,7 @@ namespace OpenVic { // Represents an in-game date // Note: Current implementation does not account for leap-years, or dates before Year 0 - struct Date { + struct Date : ReturnByValueProperty { using year_t = uint16_t; using month_t = uint8_t; using day_t = uint8_t; diff --git a/src/openvic-simulation/types/IdentifierRegistry.cpp b/src/openvic-simulation/types/IdentifierRegistry.cpp index faddd75..3964b12 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.cpp +++ b/src/openvic-simulation/types/IdentifierRegistry.cpp @@ -8,10 +8,6 @@ HasIdentifier::HasIdentifier(std::string_view new_identifier) : identifier { new assert(!identifier.empty()); } -std::string_view HasIdentifier::get_identifier() const { - return identifier; -} - std::ostream& OpenVic::operator<<(std::ostream& stream, HasIdentifier const& obj) { return stream << obj.get_identifier(); } @@ -20,12 +16,8 @@ std::ostream& OpenVic::operator<<(std::ostream& stream, HasIdentifier const* obj return obj != nullptr ? stream << *obj : stream << ""; } -HasColour::HasColour(colour_t const new_colour, bool can_be_null, bool can_have_alpha) : colour(new_colour) { - assert((can_be_null || colour != NULL_COLOUR) && colour <= (!can_have_alpha ? MAX_COLOUR_RGB : MAX_COLOUR_ARGB)); -} - -colour_t HasColour::get_colour() const { - return colour; +HasColour::HasColour(colour_t new_colour, bool cannot_be_null, bool can_have_alpha) : colour(new_colour) { + assert((!cannot_be_null || colour != NULL_COLOUR) && colour <= (!can_have_alpha ? MAX_COLOUR_RGB : MAX_COLOUR_ARGB)); } std::string HasColour::colour_to_hex_string() const { @@ -33,5 +25,5 @@ std::string HasColour::colour_to_hex_string() const { } HasIdentifierAndColour::HasIdentifierAndColour( - std::string_view new_identifier, const colour_t new_colour, bool can_be_null, bool can_have_alpha -) : HasIdentifier { new_identifier }, HasColour { new_colour, can_be_null, can_have_alpha } {} + std::string_view new_identifier, colour_t new_colour, bool cannot_be_null, bool can_have_alpha +) : HasIdentifier { new_identifier }, HasColour { new_colour, cannot_be_null, can_have_alpha } {} diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index ebdedce..e68f2a4 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -6,24 +6,16 @@ #include #include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Logger.hpp" -#define REF_GETTERS(var) \ - constexpr decltype(var)& get_##var() { \ - return var; \ - } \ - constexpr decltype(var) const& get_##var() const { \ - return var; \ - } - namespace OpenVic { /* - * Base class for objects with a non-empty string identifier, - * uniquely named instances of which can be entered into an - * IdentifierRegistry instance. + * Base class for objects with a non-empty string identifier. Uniquely named instances of a type derived from this class + * can be entered into an IdentifierRegistry instance. */ class HasIdentifier { - const std::string identifier; + const std::string PROPERTY(identifier); protected: HasIdentifier(std::string_view new_identifier); @@ -33,29 +25,6 @@ namespace OpenVic { HasIdentifier(HasIdentifier&&) = default; HasIdentifier& operator=(HasIdentifier const&) = delete; HasIdentifier& operator=(HasIdentifier&&) = delete; - - std::string_view get_identifier() const; - - template - inline constexpr static decltype(auto) get_property(const T& property) { - if constexpr (std::same_as) { - return std::string_view(property); - } else if constexpr (sizeof(T) <= sizeof(void*)) { - return T(property); - } else { - return property; - } - } - -#define HASID_PROPERTY(NAME) \ - const NAME; \ -\ -public: \ - auto get_##NAME() const -> decltype(get_property(NAME)) { \ - return get_property(NAME); \ - } \ -\ -private: }; std::ostream& operator<<(std::ostream& stream, HasIdentifier const& obj); @@ -65,10 +34,10 @@ private: * Base class for objects with associated colour information. */ class HasColour { - const colour_t colour; + const colour_t PROPERTY(colour); protected: - HasColour(const colour_t new_colour, bool can_be_null, bool can_have_alpha); + HasColour(colour_t new_colour, bool cannot_be_null, bool can_have_alpha); public: HasColour(HasColour const&) = delete; @@ -76,18 +45,16 @@ private: HasColour& operator=(HasColour const&) = delete; HasColour& operator=(HasColour&&) = delete; - colour_t get_colour() const; std::string colour_to_hex_string() const; }; /* - * Base class for objects with a unique string identifier - * and associated colour information. + * Base class for objects with a unique string identifier and associated colour information. */ class HasIdentifierAndColour : public HasIdentifier, public HasColour { protected: HasIdentifierAndColour( - std::string_view new_identifier, const colour_t new_colour, bool can_be_null, bool can_have_alpha + std::string_view new_identifier, colour_t new_colour, bool cannot_be_null, bool can_have_alpha ); public: diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp index 4bf5716..ba6790f 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp @@ -8,6 +8,7 @@ #include #include +#include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/NumberUtils.hpp" #include "openvic-simulation/utility/StringUtils.hpp" @@ -15,7 +16,7 @@ #include "FixedPointLUT.hpp" namespace OpenVic { - struct fixed_point_t { + struct fixed_point_t : ReturnByValueProperty { static constexpr size_t SIZE = 8; static constexpr int32_t PRECISION = FPLUT::SIN_LUT_PRECISION; diff --git a/src/openvic-simulation/utility/Getters.hpp b/src/openvic-simulation/utility/Getters.hpp new file mode 100644 index 0000000..c8f2193 --- /dev/null +++ b/src/openvic-simulation/utility/Getters.hpp @@ -0,0 +1,66 @@ +#pragma once + +#include +#include +#include + +#define REF_GETTERS(var) \ + constexpr decltype(var)& get_##var() { \ + return var; \ + } \ + constexpr decltype(var) const& get_##var() const { \ + return var; \ + } + +namespace OpenVic { + struct ReturnByValueProperty {}; + + /* + * Template function used to choose the return type and provide the implementation for the + * for variable getters created using the PROPERTY macro. + */ + template + inline constexpr decltype(auto) _get_property(const T& property) { + if constexpr(std::is_reference_v) { + /* Return const reference */ + return property; + } else if constexpr (std::same_as) { + /* Return std::string_view looking at std::string */ + return std::string_view { property }; + } else if constexpr ( + std::integral || std::floating_point || std::is_enum::value || std::derived_from + ) { + /* Return value */ + return T { property }; + } else if constexpr(std::is_pointer::value) { + /* Return const pointer */ + return static_cast>>>(property); + } else { + /* Return const reference */ + return property; + } + } + +/* + * Use this on a variable delcaration to generate a getter function. It assumes the variable is private and so + * sets the accessibility modifier state back to private after declaring the getter as public. + * Examples: + * int PROPERTY(x); // int x; int get_x() const; + * const std::string PROPERTY(name); // const std::string name; std::string_view get_name() const; + * std::vector PROPERTY(sizes); // std::vector sizes; std::vector const& get_sizes() const; + * uint8_t const* PROPERTY(data); // uint8_t const* data; uint8_t const* get_data() const; + * colour_t* PROPERTY(pixels); // colour_t* pixels; colour_t const* get_pixels() const; + * CultureGroup const& PROPERTY(group);// CultureGroup const& group; CultureGroup const& get_group() const; + * Province& PROPERTY(province); // Province& province; Province const& get_province() const; + */ +#define PROPERTY(NAME) \ + NAME; \ +\ +public: \ + auto get_##NAME() const -> decltype(OpenVic::_get_property(NAME)) { \ + return OpenVic::_get_property(NAME); \ + } \ +\ +private: + +} -- cgit v1.2.3-56-ga3b1 From b5bbeb47febc823517a5baba4eca66f32fb3609c Mon Sep 17 00:00:00 2001 From: hop311 Date: Sat, 4 Nov 2023 18:44:38 +0000 Subject: Cross-platform file lookup (case and separator) --- src/headless/main.cpp | 2 +- src/openvic-simulation/country/Country.cpp | 19 +- src/openvic-simulation/country/Country.hpp | 4 +- src/openvic-simulation/dataloader/Dataloader.cpp | 339 +++++++++++++-------- src/openvic-simulation/dataloader/Dataloader.hpp | 26 +- src/openvic-simulation/map/Map.cpp | 8 +- src/openvic-simulation/map/Province.cpp | 26 +- src/openvic-simulation/map/Province.hpp | 9 +- src/openvic-simulation/military/Deployment.cpp | 16 +- src/openvic-simulation/military/LeaderTrait.hpp | 2 +- src/openvic-simulation/pop/Culture.cpp | 2 +- .../types/IdentifierRegistry.hpp | 42 ++- src/openvic-simulation/utility/Logger.cpp | 34 --- src/openvic-simulation/utility/Logger.hpp | 21 +- src/openvic-simulation/utility/StringUtils.hpp | 68 ++++- 15 files changed, 377 insertions(+), 241 deletions(-) delete mode 100644 src/openvic-simulation/utility/Logger.cpp (limited to 'src/openvic-simulation/map/Province.cpp') diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 36a1329..0400302 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -74,7 +74,7 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) int main(int argc, char const* argv[]) { Logger::set_logger_funcs(); - char const* program_name = Logger::get_filename(argc > 0 ? argv[0] : nullptr, ""); + char const* program_name = StringUtils::get_filename(argc > 0 ? argv[0] : nullptr, ""); fs::path root; bool run_tests = false; int argn = 0; diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp index 9e244c1..6ad13ee 100644 --- a/src/openvic-simulation/country/Country.cpp +++ b/src/openvic-simulation/country/Country.cpp @@ -78,6 +78,12 @@ bool CountryManager::add_country( Logger::error("Invalid country identifier - empty!"); return false; } + if (!valid_basic_identifier(identifier)) { + Logger::error( + "Invalid country identifier: ", identifier, " (can only contain alphanumeric characters and underscores)" + ); + return false; + } if (colour > MAX_COLOUR_RGB) { Logger::error("Invalid country colour for ", identifier, ": ", colour_to_hex_string(colour)); return false; @@ -93,14 +99,13 @@ bool CountryManager::add_country( }); } -bool CountryManager::load_countries( - GameManager const& game_manager, Dataloader const& dataloader, fs::path const& countries_dir, ast::NodeCPtr root -) { +bool CountryManager::load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root) { + static constexpr std::string_view common_dir = "common/"; bool is_dynamic = false; const bool ret = expect_dictionary_reserve_length( countries, - [this, &game_manager, &is_dynamic, &dataloader, &countries_dir](std::string_view key, ast::NodeCPtr value) -> bool { + [this, &game_manager, &is_dynamic, &dataloader](std::string_view key, ast::NodeCPtr value) -> bool { if (key == "dynamic_tags") { return expect_bool([&is_dynamic](bool val) -> bool { if (val == is_dynamic) { @@ -115,10 +120,12 @@ bool CountryManager::load_countries( })(value); } if (expect_string( - [this, &game_manager, is_dynamic, &dataloader, &countries_dir, &key](std::string_view filepath) -> bool { + [this, &game_manager, is_dynamic, &dataloader, &key](std::string_view filepath) -> bool { if (load_country_data_file( game_manager, key, is_dynamic, - Dataloader::parse_defines(dataloader.lookup_file_case_insensitive(countries_dir / filepath)).get_file_node() + Dataloader::parse_defines( + dataloader.lookup_file(StringUtils::append_string_views(common_dir, filepath)) + ).get_file_node() )) { return true; } diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp index 7754a0b..50cca04 100644 --- a/src/openvic-simulation/country/Country.hpp +++ b/src/openvic-simulation/country/Country.hpp @@ -103,9 +103,7 @@ namespace OpenVic { ); IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries) - bool load_countries( - GameManager const& game_manager, Dataloader const& dataloader, fs::path const& countries_dir, ast::NodeCPtr root - ); + bool load_countries(GameManager const& game_manager, Dataloader const& dataloader, ast::NodeCPtr root); bool load_country_data_file( GameManager const& game_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root ); diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index e98e63b..0174eb3 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -34,6 +34,10 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; using namespace ovdl; +using StringUtils::append_string_views; + +#define FILESYSTEM_CASE_INSENSITIVE (defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))) +#define FILESYSTEM_NEEDS_FORWARD_SLASHES (!defined(_WIN32)) static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::string_view rhs) { constexpr auto ichar_equals = [](unsigned char l, unsigned char r) { @@ -44,7 +48,7 @@ static constexpr bool path_equals_case_insensitive(std::string_view lhs, std::st // Windows and Mac by default act like case insensitive filesystems static constexpr bool path_equals(std::string_view lhs, std::string_view rhs) { -#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__)) +#if FILESYSTEM_CASE_INSENSITIVE return path_equals_case_insensitive(lhs, rhs); #else return std::equal(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); @@ -74,13 +78,13 @@ static bool filename_equals(const is_filename auto& lhs, const is_filename auto& static fs::path _search_for_game_path(fs::path hint_path = {}) { // Apparently max amount of steam libraries is 8, if incorrect please correct it to the correct max amount - constexpr int max_amount_of_steam_libraries = 8; - constexpr std::string_view Victoria_2_folder = "Victoria 2"; - constexpr std::string_view v2_game_exe = "v2game.exe"; - constexpr std::string_view steamapps = "steamapps"; - constexpr std::string_view libraryfolders = "libraryfolders.vdf"; - constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf"; - constexpr std::string_view common_folder = "common"; + static constexpr int max_amount_of_steam_libraries = 8; + static constexpr std::string_view Victoria_2_folder = "Victoria 2"; + static constexpr std::string_view v2_game_exe = "v2game.exe"; + static constexpr std::string_view steamapps = "steamapps"; + static constexpr std::string_view libraryfolders = "libraryfolders.vdf"; + static constexpr std::string_view vic2_appmanifest = "appmanifest_42960.acf"; + static constexpr std::string_view common_folder = "common"; std::error_code error_code; @@ -364,23 +368,33 @@ bool Dataloader::set_roots(path_vector_t const& new_roots) { return ret; } -fs::path Dataloader::lookup_file(fs::path const& path, bool print_error) const { +fs::path Dataloader::lookup_file(std::string_view path, bool print_error) const { +#if FILESYSTEM_NEEDS_FORWARD_SLASHES + /* Back-slashes need to be converted into forward-slashes */ + const std::string forward_slash_path { + StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) + }; + path = forward_slash_path; +#endif + + const fs::path filepath { path }; + +#if FILESYSTEM_CASE_INSENSITIVE + /* Case-insensitive filesystem */ for (fs::path const& root : roots) { - const fs::path composed = root / path; + const fs::path composed = root / filepath; if (fs::is_regular_file(composed)) { return composed; } } - if (print_error) { - Logger::error("Lookup for ", path, " failed!"); - } - return {}; -} - -fs::path Dataloader::lookup_file_case_insensitive(fs::path const& path, bool print_error) const { - const std::string filename = path.filename().string(); +#else + /* Case-sensitive filesystem */ + const std::string_view filename = StringUtils::get_filename(path); for (fs::path const& root : roots) { - const fs::path composed = root / path; + const fs::path composed = root / filepath; + if (fs::is_regular_file(composed)) { + return composed; + } std::error_code ec; for (fs::directory_entry const& entry : fs::directory_iterator { composed.parent_path(), ec }) { if (entry.is_regular_file()) { @@ -391,32 +405,44 @@ fs::path Dataloader::lookup_file_case_insensitive(fs::path const& path, bool pri } } } +#endif + if (print_error) { - Logger::error("Lookup for ", path, " failed!"); + Logger::error("Lookup for \"", path, "\" failed!"); } return {}; } -static bool contains_file_with_name(Dataloader::path_vector_t const& paths, fs::path const& name) { - for (fs::path const& path : paths) { - if (path.filename() == name) { - return true; - } - } - return false; -} - -Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, fs::path const& extension) const { +template _Equiv> +Dataloader::path_vector_t Dataloader::_lookup_files_in_dir(std::string_view path, fs::path const& extension) const { +#if FILESYSTEM_NEEDS_FORWARD_SLASHES + /* Back-slashes need to be converted into forward-slashes */ + const std::string forward_slash_path { + StringUtils::make_forward_slash_path(StringUtils::remove_leading_slashes(path)) + }; + path = forward_slash_path; +#endif + const fs::path filepath { path }; + static constexpr _Equiv Equiv {}; path_vector_t ret; + size_t start_of_current_root_entries; for (fs::path const& root : roots) { - const fs::path composed = root / path; + start_of_current_root_entries = ret.size(); + const fs::path composed = root / filepath; std::error_code ec; - for (fs::directory_entry const& entry : fs::directory_iterator { composed, ec }) { + for (fs::directory_entry const& entry : _DirIterator { composed, ec }) { if (entry.is_regular_file()) { const fs::path file = entry; - if (extension.empty() || file.extension() == extension) { - if (!contains_file_with_name(ret, file.filename())) { + if ((extension.empty() || file.extension() == extension) && !Equiv(file, {})) { + size_t index = 0; + for (; index < ret.size() && !Equiv(file, ret[index]); ++index) {} + if (index >= ret.size()) { ret.push_back(file); + } else if (start_of_current_root_entries <= index) { + Logger::warning( + "Files in the same directory with conflicting names: ", ret[index], " (accepted) and ", + file, " (rejected)" + ); } } } @@ -425,11 +451,43 @@ Dataloader::path_vector_t Dataloader::lookup_files_in_dir(fs::path const& path, return ret; } -bool Dataloader::apply_to_files_in_dir( - fs::path const& path, fs::path const& extension, callback_t callback +struct EquivFilename { + bool operator()(fs::path const& lhs, fs::path const& rhs) const { + return lhs.filename() == rhs.filename(); + } +}; + +Dataloader::path_vector_t Dataloader::lookup_files_in_dir(std::string_view path, fs::path const& extension) const { + return _lookup_files_in_dir(path, extension); +} + +Dataloader::path_vector_t Dataloader::lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const { + return _lookup_files_in_dir(path, extension); +} + +struct EquivBasicIdentifierPrefix { + bool operator()(fs::path const& lhs, fs::path const& rhs) const { + const std::string lhs_str = lhs.stem().string(); + const std::string rhs_str = rhs.stem().string(); + return extract_basic_identifier_prefix(lhs_str) == extract_basic_identifier_prefix(rhs_str); + } +}; + +Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir( + std::string_view path, fs::path const& extension ) const { + return _lookup_files_in_dir(path, extension); +} + +Dataloader::path_vector_t Dataloader::lookup_basic_indentifier_prefixed_files_in_dir_recursive( + std::string_view path, fs::path const& extension +) const { + return _lookup_files_in_dir(path, extension); +} + +bool Dataloader::apply_to_files(path_vector_t const& files, callback_t callback) const { bool ret = true; - for (fs::path const& file : lookup_files_in_dir(path, extension)) { + for (fs::path const& file : files) { if (!callback(file)) { Logger::error("Callback failed for file: ", file); ret = false; @@ -504,8 +562,9 @@ csv::Windows1252Parser Dataloader::parse_csv(fs::path const& path) { bool Dataloader::_load_pop_types( PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager ) const { - static const fs::path pop_type_directory = "poptypes"; - const bool ret = apply_to_files_in_dir(pop_type_directory, ".txt", + static constexpr std::string_view pop_type_directory = "poptypes"; + const bool ret = apply_to_files( + lookup_files_in_dir(pop_type_directory, ".txt"), [&pop_manager, &unit_manager, &good_manager](fs::path const& file) -> bool { return pop_manager.load_pop_type_file( file.stem().string(), unit_manager, good_manager, parse_defines(file).get_file_node() @@ -517,36 +576,40 @@ bool Dataloader::_load_pop_types( } bool Dataloader::_load_units(UnitManager& unit_manager, GoodManager const& good_manager) const { - static const fs::path units_directory = "units"; - const bool ret = apply_to_files_in_dir(units_directory, ".txt", + static constexpr std::string_view units_directory = "units"; + const bool ret = apply_to_files( + lookup_files_in_dir(units_directory, ".txt"), [&unit_manager, &good_manager](fs::path const& file) -> bool { return unit_manager.load_unit_file(good_manager, parse_defines(file).get_file_node()); - }); + } + ); unit_manager.lock_units(); return ret; } -bool Dataloader::_load_history(GameManager& game_manager) const { - static const fs::path country_history_directory = "history/countries"; - static const fs::path province_history_directory = "history/provinces"; +bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_file_warnings) const { /* Country History */ - bool ret = apply_to_files_in_dir(country_history_directory, ".txt", [this, &game_manager](fs::path const& file) -> bool { - const std::string filename = file.stem().string(); - // TODO - standardise rules on country idenifiers characters (probably letters + underscore) and enforce them - const size_t len = std::min(std::min(filename.find(" "), filename.find("-")), filename.length()); - const std::string_view country_id { filename.data(), len }; - - Country const* country = game_manager.get_country_manager().get_country_by_identifier(country_id); - if (country == nullptr) { - Logger::warning("Found history file for non-existent country: ", country_id); - return true; - } + static constexpr std::string_view country_history_directory = "history/countries"; + bool ret = apply_to_files( + lookup_basic_indentifier_prefixed_files_in_dir(country_history_directory, ".txt"), + [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool { + const std::string filename = file.stem().string(); + const std::string_view country_id = extract_basic_identifier_prefix(filename); + + Country const* country = game_manager.get_country_manager().get_country_by_identifier(country_id); + if (country == nullptr) { + if (unused_history_file_warnings) { + Logger::warning("Found history file for non-existent country: ", country_id); + } + return true; + } - return game_manager.get_history_manager().get_country_manager().load_country_history_file( - game_manager, *this, *country, parse_defines(lookup_file(file)).get_file_node() - ); - }); + return game_manager.get_history_manager().get_country_manager().load_country_history_file( + game_manager, *this, *country, parse_defines(file).get_file_node() + ); + } + ); game_manager.get_history_manager().get_country_manager().lock_country_histories(); { @@ -558,53 +621,51 @@ bool Dataloader::_load_history(GameManager& game_manager) const { } /* Province History */ - for (auto root : roots) { - const fs::path path = root / province_history_directory; - std::error_code ec; - for (fs::directory_entry const& entry : fs::directory_iterator { path, ec }) { - if (entry.is_directory()) { - bool ret = apply_to_files_in_dir(entry, ".txt", [this, &game_manager](fs::path const& file) -> bool { - const std::string filename = file.stem().string(); - const size_t len = std::min(filename.find(" "), filename.length()); - const std::string_view province_id { filename.data(), len }; - - Province const* province = game_manager.get_map().get_province_by_identifier(province_id); - if (province == nullptr) { - Logger::warning("Found history file for non-existent province: ", province_id); - return true; - } - - return game_manager.get_history_manager().get_province_manager().load_province_history_file( - game_manager, *province, parse_defines(lookup_file(file)).get_file_node() - ); - }); + static constexpr std::string_view province_history_directory = "history/provinces"; + ret &= apply_to_files( + lookup_basic_indentifier_prefixed_files_in_dir_recursive(province_history_directory, ".txt"), + [this, &game_manager, unused_history_file_warnings](fs::path const& file) -> bool { + const std::string filename = file.stem().string(); + const std::string_view province_id = extract_basic_identifier_prefix(filename); + + Province const* province = game_manager.get_map().get_province_by_identifier(province_id); + if (province == nullptr) { + if (unused_history_file_warnings) { + Logger::warning("Found history file for non-existent province: ", province_id); + } + return true; } + + return game_manager.get_history_manager().get_province_manager().load_province_history_file( + game_manager, *province, parse_defines(file).get_file_node() + ); } - } + ); game_manager.get_history_manager().get_province_manager().lock_province_histories(); return ret; } bool Dataloader::_load_map_dir(GameManager& game_manager) const { - static const fs::path map_directory = "map"; + static constexpr std::string_view map_directory = "map/"; Map& map = game_manager.get_map(); - static const fs::path defaults_filename = "default.map"; - static const std::string default_definitions = "definition.csv"; - static const std::string default_provinces = "provinces.bmp"; - static const std::string default_positions = "positions.txt"; - static const std::string default_terrain = "terrain.bmp"; - static const std::string default_rivers = "rivers.bmp"; - static const std::string default_terrain_definition = "terrain.txt"; - static const std::string default_tree_definition = "trees.txt"; - static const std::string default_continent = "continent.txt"; - static const std::string default_adjacencies = "adjacencies.csv"; - static const std::string default_region = "region.txt"; - static const std::string default_region_sea = "region_sea.txt"; - static const std::string default_province_flag_sprite = "province_flag_sprites"; - - const v2script::Parser parser = parse_defines(lookup_file(map_directory / defaults_filename)); + static constexpr std::string_view defaults_filename = "default.map"; + static constexpr std::string_view default_definitions = "definition.csv"; + static constexpr std::string_view default_provinces = "provinces.bmp"; + static constexpr std::string_view default_positions = "positions.txt"; + static constexpr std::string_view default_terrain = "terrain.bmp"; + static constexpr std::string_view default_rivers = "rivers.bmp"; + static constexpr std::string_view default_terrain_definition = "terrain.txt"; + static constexpr std::string_view default_tree_definition = "trees.txt"; + static constexpr std::string_view default_continent = "continent.txt"; + static constexpr std::string_view default_adjacencies = "adjacencies.csv"; + static constexpr std::string_view default_region = "region.txt"; + static constexpr std::string_view default_region_sea = "region_sea.txt"; + static constexpr std::string_view default_province_flag_sprite = "province_flag_sprites"; + + const v2script::Parser parser = + parse_defines(lookup_file(append_string_views(map_directory, defaults_filename))); std::vector water_province_identifiers; @@ -658,20 +719,24 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { Logger::error("Failed to load map default file!"); } - if (!map.load_province_definitions(parse_csv(lookup_file(map_directory / definitions)).get_lines())) { + if (!map.load_province_definitions( + parse_csv(lookup_file(append_string_views(map_directory, definitions))).get_lines() + )) { Logger::error("Failed to load province definitions file!"); ret = false; } if (!map.load_province_positions( game_manager.get_economy_manager().get_building_manager(), - parse_defines(lookup_file(map_directory / positions)).get_file_node() + parse_defines(lookup_file(append_string_views(map_directory, positions))).get_file_node() )) { Logger::error("Failed to load province positions file!"); ret = false; } - if (!map.load_region_file(parse_defines(lookup_file(map_directory / region)).get_file_node())) { + if (!map.load_region_file( + parse_defines(lookup_file(append_string_views(map_directory, region))).get_file_node() + )) { Logger::error("Failed to load region file!"); ret = false; } @@ -682,18 +747,24 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { } 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() + game_manager.get_modifier_manager(), + parse_defines(lookup_file(append_string_views(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)) { + if (!map.load_map_images( + lookup_file(append_string_views(map_directory, provinces)), + lookup_file(append_string_views(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())) { + if (!map.generate_and_load_province_adjacencies( + parse_csv(lookup_file(append_string_views(map_directory, adjacencies))).get_lines() + )) { Logger::error("Failed to generate and load province adjacencies!"); ret = false; } @@ -702,20 +773,20 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { } bool Dataloader::load_defines(GameManager& game_manager) const { - static const fs::path defines_file = "common/defines.lua"; - static const fs::path buildings_file = "common/buildings.txt"; - static const fs::path bookmark_file = "common/bookmarks.txt"; - static const fs::path countries_file = "common/countries.txt"; - static const fs::path culture_file = "common/cultures.txt"; - static const fs::path goods_file = "common/goods.txt"; - static const fs::path governments_file = "common/governments.txt"; - static const fs::path graphical_culture_type_file = "common/graphicalculturetype.txt"; - static const fs::path ideology_file = "common/ideologies.txt"; - static const fs::path issues_file = "common/issues.txt"; - static const fs::path national_values_file = "common/nationalvalues.txt"; - static const fs::path production_types_file = "common/production_types.txt"; - static const fs::path religion_file = "common/religion.txt"; - static const fs::path leader_traits_file = "common/traits.txt"; + static const std::string defines_file = "common/defines.lua"; + static const std::string buildings_file = "common/buildings.txt"; + static const std::string bookmark_file = "common/bookmarks.txt"; + static const std::string countries_file = "common/countries.txt"; + static const std::string culture_file = "common/cultures.txt"; + static const std::string goods_file = "common/goods.txt"; + static const std::string governments_file = "common/governments.txt"; + static const std::string graphical_culture_type_file = "common/graphicalculturetype.txt"; + static const std::string ideology_file = "common/ideologies.txt"; + static const std::string issues_file = "common/issues.txt"; + static const std::string national_values_file = "common/nationalvalues.txt"; + static const std::string production_types_file = "common/production_types.txt"; + static const std::string religion_file = "common/religion.txt"; + static const std::string leader_traits_file = "common/traits.txt"; bool ret = true; @@ -815,12 +886,12 @@ bool Dataloader::load_defines(GameManager& game_manager) const { ret = false; } if (!game_manager.get_country_manager().load_countries( - game_manager, *this, countries_file.parent_path(), parse_defines(lookup_file(countries_file)).get_file_node() + game_manager, *this, parse_defines(lookup_file(countries_file)).get_file_node() )) { Logger::error("Failed to load countries!"); ret = false; } - if (!_load_history(game_manager)) { + if (!_load_history(game_manager, false)) { Logger::error("Failed to load history!"); ret = false; } @@ -828,14 +899,17 @@ bool Dataloader::load_defines(GameManager& game_manager) const { return ret; } -bool Dataloader::load_pop_history(GameManager& game_manager, fs::path const& path) const { - return apply_to_files_in_dir(path, ".txt", [&game_manager](fs::path const& file) -> bool { - return game_manager.get_map().expect_province_dictionary( - [&game_manager](Province& province, ast::NodeCPtr value) -> bool { - return province.load_pop_list(game_manager.get_pop_manager(), value); - } - )(parse_defines(file).get_file_node()); - }); +bool Dataloader::load_pop_history(GameManager& game_manager, std::string_view path) const { + return apply_to_files( + lookup_files_in_dir(path, ".txt"), + [&game_manager](fs::path const& file) -> bool { + return game_manager.get_map().expect_province_dictionary( + [&game_manager](Province& province, ast::NodeCPtr value) -> bool { + return province.load_pop_list(game_manager.get_pop_manager(), value); + } + )(parse_defines(file).get_file_node()); + } + ); } static bool _load_localisation_file(Dataloader::localisation_callback_t callback, std::vector const& lines) { @@ -855,8 +929,11 @@ static bool _load_localisation_file(Dataloader::localisation_callback_t callback return ret; } -bool Dataloader::load_localisation_files(localisation_callback_t callback, fs::path const& localisation_dir) const { - return apply_to_files_in_dir(localisation_dir, ".csv", [callback](fs::path path) -> bool { - return _load_localisation_file(callback, parse_csv(path).get_lines()); - }); +bool Dataloader::load_localisation_files(localisation_callback_t callback, std::string_view localisation_dir) const { + return apply_to_files( + lookup_files_in_dir(localisation_dir, ".csv"), + [callback](fs::path path) -> bool { + return _load_localisation_file(callback, parse_csv(path).get_lines()); + } + ); } diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp index a8b8bb1..2123469 100644 --- a/src/openvic-simulation/dataloader/Dataloader.hpp +++ b/src/openvic-simulation/dataloader/Dataloader.hpp @@ -26,7 +26,13 @@ namespace OpenVic { bool _load_pop_types(PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager) const; bool _load_units(UnitManager& unit_manager, GoodManager const& good_manager) const; bool _load_map_dir(GameManager& game_manager) const; - bool _load_history(GameManager& game_manager) const; + bool _load_history(GameManager& game_manager, bool unused_history_file_warnings) const; + + /* _DirIterator is fs::directory_iterator or fs::recursive_directory_iterator. + * _Equiv is an equivalence relation with respect to which every found file shall be unique. + * If a file is equivalent to the empty path then it is not included. */ + template _Equiv> + path_vector_t _lookup_files_in_dir(std::string_view path, fs::path const& extension) const; public: static ovdl::v2script::Parser parse_defines(fs::path const& path); @@ -63,15 +69,17 @@ namespace OpenVic { /* REQUIREMENTS: * DAT-24 */ - fs::path lookup_file(fs::path const& path, bool print_error = true) const; - fs::path lookup_file_case_insensitive(fs::path const& path, bool print_error = true) const; - path_vector_t lookup_files_in_dir(fs::path const& path, fs::path const& extension) const; - bool apply_to_files_in_dir( - fs::path const& path, fs::path const& extension, NodeTools::callback_t callback + fs::path lookup_file(std::string_view path, bool print_error = true) const; + path_vector_t lookup_files_in_dir(std::string_view path, fs::path const& extension) const; + path_vector_t lookup_files_in_dir_recursive(std::string_view path, fs::path const& extension) const; + path_vector_t lookup_basic_indentifier_prefixed_files_in_dir(std::string_view path, fs::path const& extension) const; + path_vector_t lookup_basic_indentifier_prefixed_files_in_dir_recursive( + std::string_view path, fs::path const& extension ) const; + bool apply_to_files(path_vector_t const& files, NodeTools::callback_t callback) const; bool load_defines(GameManager& game_manager) const; - bool load_pop_history(GameManager& game_manager, fs::path const& path) const; + bool load_pop_history(GameManager& game_manager, std::string_view path) const; enum locale_t : size_t { English, French, German, Polish, Spanish, Italian, Swedish, @@ -85,7 +93,7 @@ namespace OpenVic { /* Args: key, locale, localisation */ using localisation_callback_t = NodeTools::callback_t; bool load_localisation_files( - localisation_callback_t callback, fs::path const& localisation_dir = "localisation" + localisation_callback_t callback, std::string_view localisation_dir = "localisation" ) const; private: @@ -97,6 +105,6 @@ namespace OpenVic { using hint_path_t = fs::path; using game_path_t = fs::path; - inline static std::unordered_map _cached_paths; + static inline std::unordered_map _cached_paths; }; } diff --git a/src/openvic-simulation/map/Map.cpp b/src/openvic-simulation/map/Map.cpp index b1aea2c..7e2213e 100644 --- a/src/openvic-simulation/map/Map.cpp +++ b/src/openvic-simulation/map/Map.cpp @@ -42,6 +42,12 @@ bool Map::add_province(std::string_view identifier, colour_t colour) { Logger::error("Invalid province identifier - empty!"); return false; } + if (!valid_basic_identifier(identifier)) { + Logger::error( + "Invalid province identifier: ", identifier, " (can only contain alphanumeric characters and underscores)" + ); + return false; + } if (colour == NULL_COLOUR || colour > MAX_COLOUR_RGB) { Logger::error("Invalid province colour for ", identifier, ": ", colour_to_hex_string(colour)); return false; @@ -483,7 +489,7 @@ bool Map::load_map_images(fs::path const& province_path, fs::path const& terrain uint8_t const* terrain_data = terrain_bmp.get_pixel_data().data(); std::vector province_checklist(provinces.size()); - std::vector terrain_type_pixels_list(provinces.size()); + std::vector> terrain_type_pixels_list(provinces.size()); bool ret = true; std::unordered_set unrecognised_province_colours; diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp index 66e4daf..77bf33f 100644 --- a/src/openvic-simulation/map/Province.cpp +++ b/src/openvic-simulation/map/Province.cpp @@ -131,31 +131,21 @@ Pop::pop_size_t Province::get_total_population() const { return total_population; } -distribution_t const& Province::get_pop_type_distribution() const { - return pop_types; -} - -distribution_t const& Province::get_culture_distribution() const { - return cultures; -} - -distribution_t const& Province::get_religion_distribution() const { - return religions; -} - /* REQUIREMENTS: * MAP-65, MAP-68, MAP-70, MAP-234 */ void Province::update_pops() { total_population = 0; - pop_types.clear(); - cultures.clear(); - religions.clear(); + pop_type_distribution.clear(); + ideology_distribution.clear(); + culture_distribution.clear(); + religion_distribution.clear(); for (Pop const& pop : pops) { total_population += pop.get_size(); - pop_types[&pop.get_type()] += pop.get_size(); - cultures[&pop.get_culture()] += pop.get_size(); - religions[&pop.get_religion()] += pop.get_size(); + pop_type_distribution[&pop.get_type()] += pop.get_size(); + //ideology_distribution[&pop.get_???()] += pop.get_size(); + culture_distribution[&pop.get_culture()] += pop.get_size(); + religion_distribution[&pop.get_religion()] += pop.get_size(); } } diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp index 6c022b7..2fd15c9 100644 --- a/src/openvic-simulation/map/Province.hpp +++ b/src/openvic-simulation/map/Province.hpp @@ -3,6 +3,7 @@ #include #include "openvic-simulation/economy/Building.hpp" +#include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/pop/Pop.hpp" namespace OpenVic { @@ -72,7 +73,10 @@ namespace OpenVic { std::vector pops; Pop::pop_size_t total_population; - distribution_t pop_types, cultures, religions; + decimal_map_t PROPERTY(pop_type_distribution); + decimal_map_t PROPERTY(ideology_distribution); + decimal_map_t PROPERTY(culture_distribution); + decimal_map_t PROPERTY(religion_distribution); std::vector adjacencies; province_positions_t positions; @@ -109,9 +113,6 @@ namespace OpenVic { size_t get_pop_count() const; std::vector const& get_pops() const; Pop::pop_size_t get_total_population() const; - distribution_t const& get_pop_type_distribution() const; - distribution_t const& get_culture_distribution() const; - distribution_t const& get_religion_distribution() const; void update_pops(); void update_state(Date today); diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index b5d335a..0530986 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -37,9 +37,6 @@ bool DeploymentManager::add_deployment( Logger::error("Attemped to load order of battle with no path! Something is very wrong!"); return false; } - if (armies.empty() && navies.empty() && leaders.empty()) { - Logger::warning("Loading redundant empty order of battle at ", path); - } return deployments.add_item( std::make_unique(std::move(path), std::move(armies), std::move(navies), std::move(leaders)) @@ -47,8 +44,8 @@ bool DeploymentManager::add_deployment( } bool DeploymentManager::load_oob_file( - GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path, Deployment const*& deployment, - bool fail_on_missing + GameManager const& game_manager, Dataloader const& dataloader, std::string_view history_path, + Deployment const*& deployment, bool fail_on_missing ) { deployment = get_deployment_by_identifier(history_path); if (deployment != nullptr) { @@ -57,14 +54,13 @@ bool DeploymentManager::load_oob_file( if (missing_oob_files.contains(history_path)) { return !fail_on_missing; } - static const fs::path oob_directory = "history/units/"; - fs::path full_path = oob_directory; - full_path += history_path; - const fs::path lookedup_path = dataloader.lookup_file(full_path, false); + static constexpr std::string_view oob_directory = "history/units/"; + const fs::path lookedup_path = + dataloader.lookup_file(StringUtils::append_string_views(oob_directory, history_path), false); if (lookedup_path.empty()) { missing_oob_files.emplace(history_path); if (fail_on_missing) { - Logger::warning("Could not find OOB file ", full_path, "!"); + Logger::warning("Could not find OOB file ", history_path, "!"); return false; } else { return true; diff --git a/src/openvic-simulation/military/LeaderTrait.hpp b/src/openvic-simulation/military/LeaderTrait.hpp index e61a1fc..16b4201 100644 --- a/src/openvic-simulation/military/LeaderTrait.hpp +++ b/src/openvic-simulation/military/LeaderTrait.hpp @@ -46,7 +46,7 @@ namespace OpenVic { struct LeaderTraitManager { private: IdentifierRegistry leader_traits; - inline static const string_set_t allowed_modifiers = { + static inline const string_set_t allowed_modifiers = { "attack", "defence", "morale", "organisation", "reconnaissance", "speed", "attrition", "experience", "reliability" }; diff --git a/src/openvic-simulation/pop/Culture.cpp b/src/openvic-simulation/pop/Culture.cpp index 9159fe1..47501e0 100644 --- a/src/openvic-simulation/pop/Culture.cpp +++ b/src/openvic-simulation/pop/Culture.cpp @@ -174,7 +174,7 @@ bool CultureManager::load_culture_file(ast::NodeCPtr root) { return false; } - static const std::string default_unit_graphical_culture_type_identifier = "Generic"; + static constexpr std::string_view default_unit_graphical_culture_type_identifier = "Generic"; GraphicalCultureType const* const default_unit_graphical_culture_type = get_graphical_culture_type_by_identifier(default_unit_graphical_culture_type_identifier); if (default_unit_graphical_culture_type == nullptr) { diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index e68f2a4..734421d 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -10,6 +10,21 @@ #include "openvic-simulation/utility/Logger.hpp" namespace OpenVic { + + constexpr bool valid_basic_identifier_char(char c) { + return ('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') || c == '_'; + } + constexpr bool valid_basic_identifier(std::string_view identifier) { + return std::all_of(identifier.begin(), identifier.end(), valid_basic_identifier_char); + } + constexpr std::string_view extract_basic_identifier_prefix(std::string_view identifier) { + size_t len = 0; + while (len < identifier.size() && valid_basic_identifier_char(identifier[len])) { + ++len; + } + return { identifier.data(), len }; + } + /* * Base class for objects with a non-empty string identifier. Uniquely named instances of a type derived from this class * can be entered into an IdentifierRegistry instance. @@ -80,8 +95,6 @@ namespace OpenVic { } } - using distribution_t = decimal_map_t; - /* Callbacks for trying to add duplicate keys via UniqueKeyRegistry::add_item */ static bool duplicate_fail_callback(std::string_view registry_name, std::string_view duplicate_identifier) { Logger::error( @@ -192,20 +205,20 @@ namespace OpenVic { } } -#define GETTERS \ - value_type _const* get_item_by_identifier(std::string_view identifier) _const { \ +#define GETTERS(CONST) \ + value_type CONST* get_item_by_identifier(std::string_view identifier) CONST { \ const typename decltype(identifier_index_map)::const_iterator it = identifier_index_map.find(identifier); \ if (it != identifier_index_map.end()) { \ return GetPointer(items[it->second]); \ } \ return nullptr; \ } \ - value_type _const* get_item_by_index(size_t index) _const { \ + value_type CONST* get_item_by_index(size_t index) CONST { \ return index < items.size() ? &items[index] : nullptr; \ } \ - NodeTools::callback_t expect_item_str(NodeTools::callback_t callback) _const { \ + NodeTools::callback_t expect_item_str(NodeTools::callback_t callback) CONST { \ return [this, callback](std::string_view identifier) -> bool { \ - value_type _const* item = get_item_by_identifier(identifier); \ + value_type CONST* item = get_item_by_identifier(identifier); \ if (item != nullptr) { \ return callback(*item); \ } \ @@ -213,22 +226,19 @@ namespace OpenVic { return false; \ }; \ } \ - NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t callback) _const { \ + NodeTools::node_callback_t expect_item_identifier(NodeTools::callback_t callback) CONST { \ return NodeTools::expect_identifier(expect_item_str(callback)); \ } \ - NodeTools::node_callback_t expect_item_dictionary(NodeTools::callback_t callback) \ - _const { \ + NodeTools::node_callback_t expect_item_dictionary( \ + NodeTools::callback_t callback \ + ) CONST { \ return NodeTools::expect_dictionary([this, callback](std::string_view key, ast::NodeCPtr value) -> bool { \ return expect_item_str(std::bind(callback, std::placeholders::_1, value))(key); \ }); \ } -#define _const - GETTERS -#undef _const -#define _const const - GETTERS -#undef _const + GETTERS() + GETTERS(const) #undef GETTERS diff --git a/src/openvic-simulation/utility/Logger.cpp b/src/openvic-simulation/utility/Logger.cpp deleted file mode 100644 index 63dfd6c..0000000 --- a/src/openvic-simulation/utility/Logger.cpp +++ /dev/null @@ -1,34 +0,0 @@ -#include "Logger.hpp" - -#include - -using namespace OpenVic; - -void Logger::set_logger_funcs() { - Logger::set_info_func([](std::string&& str) { - std::cout << "[INFO] " << str; - }); - Logger::set_warning_func([](std::string&& str) { - std::cerr << "[WARNING] " << str; - }); - Logger::set_error_func([](std::string&& str) { - std::cerr << "[ERROR] " << str; - }); -} - -char const* Logger::get_filename(char const* filepath, char const* default_path) { - if (filepath == nullptr) { - return default_path; - } - char const* last_slash = filepath; - while (*filepath != '\0') { - if (*filepath == '\\' || *filepath == '/') { - last_slash = filepath + 1; - } - filepath++; - } - if (*last_slash == '\0') { - return default_path; - } - return last_slash; -} diff --git a/src/openvic-simulation/utility/Logger.hpp b/src/openvic-simulation/utility/Logger.hpp index 04306f7..20c7fdd 100644 --- a/src/openvic-simulation/utility/Logger.hpp +++ b/src/openvic-simulation/utility/Logger.hpp @@ -1,12 +1,16 @@ #pragma once #include +#include #include #include + #ifdef __cpp_lib_source_location #include #endif +#include "openvic-simulation/utility/StringUtils.hpp" + namespace OpenVic { #ifndef __cpp_lib_source_location @@ -52,8 +56,17 @@ namespace OpenVic { #endif public: - static void set_logger_funcs(); - static char const* get_filename(char const* filepath, char const* default_path = nullptr); + static void set_logger_funcs() { + set_info_func([](std::string&& str) { + std::cout << "[INFO] " << str; + }); + set_warning_func([](std::string&& str) { + std::cerr << "[WARNING] " << str; + }); + set_error_func([](std::string&& str) { + std::cerr << "[ERROR] " << str; + }); + } private: struct log_channel_t { @@ -65,13 +78,13 @@ namespace OpenVic { struct log { log(log_channel_t& log_channel, Ts&&... ts, source_location const& location) { std::stringstream stream; - stream << get_filename(location.file_name()) << "(" + stream << StringUtils::get_filename(location.file_name()) << "(" /* Function name removed to reduce clutter. It is already included * in Godot's print functions, so this was repeating it. */ //<< location.line() << ") `" << location.function_name() << "`: "; << location.line() << "): "; ((stream << std::forward(ts)), ...); - stream << "\n" << std::endl; + stream << std::endl; log_channel.queue.push(stream.str()); if (log_channel.func) { do { diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp index d968bf6..ede1d6b 100644 --- a/src/openvic-simulation/utility/StringUtils.hpp +++ b/src/openvic-simulation/utility/StringUtils.hpp @@ -1,4 +1,7 @@ +#pragma once + #include +#include #include #include @@ -90,7 +93,7 @@ namespace OpenVic::StringUtils { return result; } - constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + inline constexpr uint64_t string_to_uint64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { return string_to_uint64(str, str + length, successful, base); } @@ -135,11 +138,72 @@ namespace OpenVic::StringUtils { } } - constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { + inline constexpr int64_t string_to_int64(char const* str, size_t length, bool* successful = nullptr, int base = 10) { return string_to_int64(str, str + length, successful, base); } inline int64_t string_to_int64(std::string_view str, bool* successful = nullptr, int base = 10) { return string_to_int64(str.data(), str.length(), successful, base); } + + inline constexpr std::string_view get_filename(std::string_view path) { + size_t pos = path.size(); + while (pos > 0 && path[pos - 1] != '/' && path[pos - 1] != '\\') { + --pos; + } + path.remove_prefix(pos); + return path; + } + + inline constexpr char const* get_filename(char const* filepath, char const* default_path = nullptr) { + const std::string_view filename { get_filename(std::string_view { filepath }) }; + if (!filename.empty()) { + return filename.data(); + } + return default_path; + } + + inline std::string make_forward_slash_path(std::string_view path) { + std::string ret { path }; + std::replace(ret.begin(), ret.end(), '\\', '/'); + for (char& c : ret) { + if (c == '\\') { + c = '/'; + } + } + return ret; + } + + inline constexpr std::string_view remove_leading_slashes(std::string_view path) { + size_t count = 0; + while (count < path.size() && (path[count] == '/' || path[count] == '\\')) { + ++count; + } + path.remove_prefix(count); + return path; + } + + template + requires (std::is_same_v && ...) + inline std::string _append_string_views(Args... args) { + std::string ret; + ret.reserve((args.size() + ...)); + (ret.append(args), ...); + return ret; + } + + template + requires (std::is_convertible_v && ...) + inline std::string append_string_views(Args... args) { + return _append_string_views(std::string_view { args }...); + } + + inline constexpr std::string_view remove_extension(std::string_view path) { + size_t pos = path.size(); + while (pos > 0 && path[--pos] != '.') {} + if (path[pos] == '.') { + path.remove_suffix(path.size() - pos); + } + return path; + } } -- cgit v1.2.3-56-ga3b1