From c9c198e3b47e84eaea998cd9d2f7a8aa2b50ce73 Mon Sep 17 00:00:00 2001 From: Joel Machens Date: Mon, 16 Oct 2023 21:43:40 -0500 Subject: Country History & Order of Battle Loading --- src/openvic-simulation/country/Country.cpp | 10 +- src/openvic-simulation/country/Country.hpp | 14 +- src/openvic-simulation/dataloader/Dataloader.cpp | 207 +++++++++----- src/openvic-simulation/dataloader/Dataloader.hpp | 25 +- src/openvic-simulation/dataloader/NodeTools.cpp | 36 +-- src/openvic-simulation/dataloader/NodeTools.hpp | 17 +- src/openvic-simulation/history/CountryHistory.cpp | 315 +++++++++++++++++++++ src/openvic-simulation/history/CountryHistory.hpp | 120 ++++++++ src/openvic-simulation/history/HistoryManager.hpp | 3 + src/openvic-simulation/military/Deployment.cpp | 130 +++++++++ src/openvic-simulation/military/Deployment.hpp | 79 ++++++ src/openvic-simulation/military/LeaderTrait.cpp | 2 +- src/openvic-simulation/military/LeaderTrait.hpp | 5 +- .../military/MilitaryManager.hpp | 6 +- src/openvic-simulation/military/Unit.hpp | 25 +- src/openvic-simulation/types/Date.cpp | 30 +- src/openvic-simulation/types/Date.hpp | 6 +- 17 files changed, 883 insertions(+), 147 deletions(-) create mode 100644 src/openvic-simulation/history/CountryHistory.cpp create mode 100644 src/openvic-simulation/history/CountryHistory.hpp create mode 100644 src/openvic-simulation/military/Deployment.cpp create mode 100644 src/openvic-simulation/military/Deployment.hpp (limited to 'src') diff --git a/src/openvic-simulation/country/Country.cpp b/src/openvic-simulation/country/Country.cpp index bcc0488..ed8c3cb 100644 --- a/src/openvic-simulation/country/Country.cpp +++ b/src/openvic-simulation/country/Country.cpp @@ -81,7 +81,11 @@ const std::vector& Country::get_unit_names() const { return unit_names; } -bool Country::is_dynamic_tag() const { +const std::map& Country::get_alternative_colours() const { + return alternative_colours; +} + +const bool Country::is_dynamic_tag() const { return dynamic_tag; } @@ -181,7 +185,7 @@ bool CountryManager::load_country_data_file(GameManager& game_manager, std::stri country_parties.push_back({ party_name, start_date, end_date, *ideology, std::move(policies) }); - return ret; // + return ret; }, "unit_names", ZERO_OR_ONE, expect_dictionary([&unit_names](std::string_view key, ast::NodeCPtr value) -> bool { std::vector names; @@ -201,4 +205,4 @@ bool CountryManager::load_country_data_file(GameManager& game_manager, std::stri ret &= add_country(name, color, *graphical_culture, std::move(country_parties), std::move(unit_names), is_dynamic, std::move(alternative_colours)); return ret; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/country/Country.hpp b/src/openvic-simulation/country/Country.hpp index ce7638f..8077698 100644 --- a/src/openvic-simulation/country/Country.hpp +++ b/src/openvic-simulation/country/Country.hpp @@ -10,10 +10,14 @@ #include +#include "openvic-simulation/dataloader/Dataloader.hpp" +#include "openvic-simulation/map/Province.hpp" #include "openvic-simulation/politics/Government.hpp" #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/politics/Issue.hpp" +#include "openvic-simulation/politics/NationalValue.hpp" #include "openvic-simulation/pop/Culture.hpp" +#include "openvic-simulation/pop/Religion.hpp" #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" @@ -30,7 +34,7 @@ namespace OpenVic { const Date start_date; const Date end_date; const Ideology& ideology; - const std::vector policies; + const std::vector policies; CountryParty( std::string_view new_name, @@ -69,7 +73,7 @@ namespace OpenVic { const GraphicalCultureType& graphical_culture; const std::vector parties; const std::vector unit_names; - bool dynamic_tag; + const bool dynamic_tag; const std::map alternative_colours; Country( @@ -78,7 +82,7 @@ namespace OpenVic { const GraphicalCultureType& new_graphical_culture, std::vector&& new_parties, std::vector&& new_unit_names, - bool new_dynamic_tag, + const bool new_dynamic_tag, std::map&& new_alternative_colours ); @@ -86,7 +90,7 @@ namespace OpenVic { const GraphicalCultureType& get_graphical_culture() const; const std::vector& get_parties() const; const std::vector& get_unit_names() const; - bool is_dynamic_tag() const; + const bool is_dynamic_tag() const; const std::map& get_alternative_colours() const; }; @@ -104,7 +108,7 @@ namespace OpenVic { std::vector&& parties, std::vector&& unit_names, bool dynamic_tag, - std::map&& new_alternative_colours + std::map&& alternative_colours ); IDENTIFIER_REGISTRY_ACCESSORS_CUSTOM_PLURAL(country, countries); diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 7132d24..9c05d28 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -22,6 +22,7 @@ #ifdef _WIN32 #include + #include "Dataloader_Windows.hpp" #endif @@ -90,8 +91,8 @@ static fs::path _search_for_game_path(fs::path hint_path = {}) { if (!registry_path.empty()) return registry_path; - #pragma warning(push) - #pragma warning(disable: 4996) +#pragma warning(push) +#pragma warning(disable : 4996) static const fs::path prog_files = std::string(std::getenv("ProgramFiles")); hint_path = prog_files / "Steam"; if (!fs::is_directory(hint_path, error_code)) { @@ -102,7 +103,7 @@ static fs::path _search_for_game_path(fs::path hint_path = {}) { return ""; } } - #pragma warning(pop) +#pragma warning(pop) // Cannot support Android // Only FreeBSD currently unofficially supports emulating Linux #elif (defined(__linux__) && !defined(__ANDROID__)) || defined(__FreeBSD__) @@ -278,7 +279,7 @@ static fs::path _search_for_game_path(fs::path hint_path = {}) { auto parser = lexy_vdf::Parser::from_file(vic2_steam_lib_directory); if (!parser.parse()) { // Could not find or load appmanifest_42960.acf, report error as warning - for(auto& error : parser.get_errors()) { + for (auto& error : parser.get_errors()) { Logger::warning(error.message); } return ""; @@ -458,30 +459,56 @@ 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, fs::path const& pop_type_directory) const { - const bool ret = apply_to_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()); - } - ); + const bool ret = apply_to_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()); + }); pop_manager.lock_pop_types(); return ret; } bool Dataloader::_load_units(UnitManager& unit_manager, GoodManager const& good_manager, fs::path const& units_directory) const { - const bool ret = apply_to_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()); - } - ); + const bool ret = apply_to_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_countries(GameManager& game_manager, fs::path const& countries_file, ast::NodeCPtr root) const { +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_countries(GameManager& game_manager) const { + static const fs::path countries_file = "common/countries.txt"; + bool is_dynamic = false; bool ret = expect_dictionary( - [this, &game_manager, &is_dynamic, &countries_file](std::string_view key, ast::NodeCPtr value) -> bool { + [this, &game_manager, &is_dynamic](std::string_view key, ast::NodeCPtr value) -> bool { if (key == "dynamic_tags") { return expect_bool(assign_variable_callback(is_dynamic))(value); } @@ -492,10 +519,28 @@ bool Dataloader::_load_countries(GameManager& game_manager, fs::path const& coun return false; } - return game_manager.get_country_manager().load_country_data_file(game_manager, key, is_dynamic, Dataloader::parse_defines(lookup_file(countries_file.parent_path() / data_path)).get_file_node()); + return game_manager.get_country_manager().load_country_data_file(game_manager, key, is_dynamic, parse_defines(lookup_file(countries_file.parent_path() / data_path)).get_file_node()); } - )(root); + )(parse_defines(lookup_file(countries_file)).get_file_node()); game_manager.get_country_manager().lock_countries(); + + return ret; +} + +bool Dataloader::_load_history(GameManager& game_manager) const { + static const fs::path country_history_directory = "history/countries"; + /* 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; + } + + return game_manager.get_history_manager().get_country_manager().load_country_history_file(game_manager, tag, game_manager.get_define_manager().get_start_date(), parse_defines(lookup_file(file)).get_file_node()); + }); + game_manager.get_history_manager().get_country_manager().lock_country_histories(); + return ret; } @@ -521,9 +566,18 @@ bool Dataloader::_load_map_dir(GameManager& game_manager, fs::path const& map_di std::vector water_province_identifiers; #define APPLY_TO_MAP_PATHS(F) \ - F(definitions) F(provinces) F(positions) F(terrain) F(rivers) \ - F(terrain_definition) F(tree_definition) F(continent) F(adjacencies) \ - F(region) F(region_sea) F(province_flag_sprite) + F(definitions) \ + F(provinces) \ + F(positions) \ + F(terrain) \ + F(rivers) \ + F(terrain_definition) \ + F(tree_definition) \ + F(continent) \ + F(adjacencies) \ + F(region) \ + F(region_sea) \ + F(province_flag_sprite) #define MAP_PATH_VAR(X) std::string_view X = default_##X; APPLY_TO_MAP_PATHS(MAP_PATH_VAR) @@ -546,13 +600,14 @@ bool Dataloader::_load_map_dir(GameManager& game_manager, fs::path const& map_di ), #define MAP_PATH_DICT_ENTRY(X) \ - #X, ONE_EXACTLY, expect_string(assign_variable_callback(X)), + #X, ONE_EXACTLY, expect_string(assign_variable_callback(X)), APPLY_TO_MAP_PATHS(MAP_PATH_DICT_ENTRY) #undef MAP_PATH_DICT_ENTRY #undef APPLY_TO_MAP_PATHS - "border_heights", ZERO_OR_ONE, success_callback, + "border_heights", + ZERO_OR_ONE, success_callback, "terrain_sheet_heights", ZERO_OR_ONE, success_callback, "tree", ZERO_OR_ONE, success_callback, "border_cutoff", ZERO_OR_ONE, success_callback @@ -563,19 +618,22 @@ bool Dataloader::_load_map_dir(GameManager& game_manager, fs::path const& map_di } if (!map.load_province_definitions( - parse_csv(lookup_file(map_directory / definitions)).get_lines())) { + parse_csv(lookup_file(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())) { + game_manager.get_economy_manager().get_building_manager(), parse_defines(lookup_file(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())) { + parse_defines(lookup_file(map_directory / region)).get_file_node() + )) { Logger::error("Failed to load region file!"); ret = false; } @@ -586,21 +644,24 @@ bool Dataloader::_load_map_dir(GameManager& game_manager, fs::path const& map_di } 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(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)) { + lookup_file(map_directory / provinces), + lookup_file(map_directory / terrain), false + )) { Logger::error("Failed to load map images!"); ret = false; } if (!map.generate_and_load_province_adjacencies( - parse_csv(lookup_file(map_directory / adjacencies)).get_lines())) { + parse_csv(lookup_file(map_directory / adjacencies)).get_lines() + )) { Logger::error("Failed to generate and load province adjacencies!"); ret = false; } @@ -626,7 +687,6 @@ bool Dataloader::load_defines(GameManager& game_manager) const { 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 fs::path countries_file = "common/countries.txt"; bool ret = true; @@ -639,70 +699,72 @@ bool Dataloader::load_defines(GameManager& game_manager) const { ret = false; } if (!game_manager.get_economy_manager().get_good_manager().load_goods_file( - parse_defines(lookup_file(goods_file)).get_file_node())) { + parse_defines(lookup_file(goods_file)).get_file_node() + )) { Logger::error("Failed to load goods!"); ret = false; } - if (!_load_units(game_manager.get_military_manager().get_unit_manager(), - game_manager.get_economy_manager().get_good_manager(), units_directory)) { + if (!_load_units(game_manager.get_military_manager().get_unit_manager(), game_manager.get_economy_manager().get_good_manager(), units_directory)) { Logger::error("Failed to load units!"); ret = false; } - if (!_load_pop_types(game_manager.get_pop_manager(), - game_manager.get_military_manager().get_unit_manager(), - game_manager.get_economy_manager().get_good_manager(), - pop_type_directory)) { + if (!_load_pop_types(game_manager.get_pop_manager(), game_manager.get_military_manager().get_unit_manager(), game_manager.get_economy_manager().get_good_manager(), pop_type_directory)) { Logger::error("Failed to load pop types!"); ret = false; } if (!game_manager.get_pop_manager().get_culture_manager().load_graphical_culture_type_file( - parse_defines(lookup_file(graphical_culture_type_file)).get_file_node())) { + parse_defines(lookup_file(graphical_culture_type_file)).get_file_node() + )) { Logger::error("Failed to load graphical culture types!"); ret = false; } if (!game_manager.get_pop_manager().get_culture_manager().load_culture_file( - parse_defines(lookup_file(culture_file)).get_file_node())) { + parse_defines(lookup_file(culture_file)).get_file_node() + )) { Logger::error("Failed to load cultures!"); ret = false; } if (!game_manager.get_pop_manager().get_religion_manager().load_religion_file( - parse_defines(lookup_file(religion_file)).get_file_node())) { + parse_defines(lookup_file(religion_file)).get_file_node() + )) { Logger::error("Failed to load religions!"); ret = false; } if (!game_manager.get_politics_manager().get_ideology_manager().load_ideology_file( - parse_defines(lookup_file(ideology_file)).get_file_node())) { + parse_defines(lookup_file(ideology_file)).get_file_node() + )) { Logger::error("Failed to load ideologies!"); ret = false; } if (!game_manager.get_politics_manager().load_government_types_file( - parse_defines(lookup_file(governments_file)).get_file_node())) { + parse_defines(lookup_file(governments_file)).get_file_node() + )) { Logger::error("Failed to load government types!"); ret = false; } if (!game_manager.get_politics_manager().get_issue_manager().load_issues_file( - parse_defines(lookup_file(issues_file)).get_file_node())) { + parse_defines(lookup_file(issues_file)).get_file_node() + )) { Logger::error("Failed to load issues!"); ret = false; } if (!game_manager.get_politics_manager().get_national_value_manager().load_national_values_file( - game_manager.get_modifier_manager(), parse_defines(lookup_file(national_values_file)).get_file_node())) { + game_manager.get_modifier_manager(), parse_defines(lookup_file(national_values_file)).get_file_node() + )) { Logger::error("Failed to load national values!"); ret = false; } - if (!_load_countries(game_manager, countries_file, parse_defines(lookup_file(countries_file)).get_file_node())) { - Logger::error("Failed to load countries!"); - ret = false; - } if (!game_manager.get_economy_manager().load_production_types_file( game_manager.get_pop_manager(), - parse_defines(lookup_file(production_types_file)).get_file_node())) { + parse_defines(lookup_file(production_types_file)).get_file_node() + )) { Logger::error("Failed to load production types!"); ret = false; } if (!game_manager.get_economy_manager().load_buildings_file( - game_manager.get_modifier_manager(), - parse_defines(lookup_file(buildings_file)).get_file_node())) { + game_manager.get_modifier_manager(), + parse_defines(lookup_file(buildings_file)).get_file_node() + )) { Logger::error("Failed to load buildings!"); ret = false; } @@ -711,7 +773,8 @@ bool Dataloader::load_defines(GameManager& game_manager) const { ret = false; } if (!game_manager.get_military_manager().get_leader_trait_manager().load_leader_traits_file( - game_manager.get_modifier_manager(), parse_defines(lookup_file(leader_traits_file)).get_file_node())) { + game_manager.get_modifier_manager(), parse_defines(lookup_file(leader_traits_file)).get_file_node() + )) { Logger::error("Failed to load leader traits!"); ret = false; } @@ -719,20 +782,30 @@ 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 (!_load_countries(game_manager)) { + Logger::error("Failed to load countries!"); + ret = false; + } + if (!_load_history(game_manager)) { + Logger::error("Failed to load history!"); + ret = false; + } 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()); - } - ); + 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()); + }); } static bool _load_localisation_file(Dataloader::localisation_callback_t callback, std::vector const& lines) { @@ -753,9 +826,7 @@ static bool _load_localisation_file(Dataloader::localisation_callback_t callback } 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()); - } - ); + return apply_to_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 7be86ad..4a8b111 100644 --- a/src/openvic-simulation/dataloader/Dataloader.hpp +++ b/src/openvic-simulation/dataloader/Dataloader.hpp @@ -26,7 +26,9 @@ namespace OpenVic { bool _load_pop_types(PopManager& pop_manager, UnitManager const& unit_manager, GoodManager const& good_manager, fs::path const& pop_type_directory) const; bool _load_units(UnitManager& unit_manager, GoodManager const& good_manager, fs::path const& units_directory) const; bool _load_map_dir(GameManager& game_manager, fs::path const& map_directory) const; - bool _load_countries(GameManager& game_manager, fs::path const& countries_file, ast::NodeCPtr root) const; + bool _load_oobs(GameManager& game_manager) const; + bool _load_countries(GameManager& game_manager) const; + bool _load_history(GameManager& game_manager) const; public: static ovdl::v2script::Parser parse_defines(fs::path const& path); @@ -62,14 +64,26 @@ namespace OpenVic { */ fs::path lookup_file(fs::path const& path) 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) const; + bool apply_to_files_in_dir(fs::path const& path, fs::path const& extension, NodeTools::callback_t callback) const; bool load_defines(GameManager& game_manager) const; 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" @@ -80,8 +94,7 @@ namespace OpenVic { bool load_localisation_files(localisation_callback_t callback, fs::path const& localisation_dir = "localisation") const; private: - struct fshash - { + struct fshash { size_t operator()(const std::filesystem::path& p) const noexcept { return std::filesystem::hash_value(p); } diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index c99a2f0..92080c4 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -123,22 +123,18 @@ node_callback_t NodeTools::expect_colour(callback_t callback) { return [callback](ast::NodeCPtr node) -> bool { colour_t col = NULL_COLOUR; uint32_t components = 0; - bool ret = expect_list_of_length(3, - expect_fixed_point( - [&col, &components](fixed_point_t val) -> bool { - components++; - col <<= 8; - if (val < 0 || val > 255) { - Logger::error("Invalid colour component: ", val); - return false; - } else { - if (val <= 1) val *= 255; - col |= val.to_int32_t(); - return true; - } - } - ) - )(node); + bool ret = expect_list_of_length(3, expect_fixed_point([&col, &components](fixed_point_t val) -> bool { + components++; + col <<= 8; + if (val < 0 || val > 255) { + Logger::error("Invalid colour component: ", val); + return false; + } else { + if (val <= 1) val *= 255; + col |= val.to_int32_t(); + return true; + } + }))(node); ret &= callback(col << 8 * (3 - components)); return ret; }; @@ -218,11 +214,9 @@ node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callb size = list.size(); ret = false; } - std::for_each(list.begin(), list.begin() + size, - [callback, &ret](ast::NodeUPtr const& sub_node) -> void { - ret &= callback(sub_node.get()); - } - ); + std::for_each(list.begin(), list.begin() + size, [callback, &ret](ast::NodeUPtr const& sub_node) -> void { + ret &= callback(sub_node.get()); + }); return ret; } ); diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 66b614a..6469100 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -26,17 +26,17 @@ namespace OpenVic { namespace NodeTools { - template - concept Functor = requires(Fn&& fn, Args&& ...args) { + template + concept Functor = requires(Fn&& fn, Args&&... args) { { std::invoke(std::forward(fn), std::forward(args)...) } -> std::same_as; }; - template - concept FunctorConvertible = requires(Fn&& fn, Args&& ...args) { + template + concept FunctorConvertible = requires(Fn&& fn, Args&&... args) { { std::invoke(std::forward(fn), std::forward(args)...) } -> std::convertible_to; }; - template + template concept Callback = Functor; template @@ -78,9 +78,7 @@ namespace OpenVic { val <= static_cast(std::numeric_limits::max())) { return callback(val); } - Logger::error("Invalid int: ", val, " (valid range: [", - static_cast(std::numeric_limits::lowest()), ", ", - static_cast(std::numeric_limits::max()), "])"); + Logger::error("Invalid int: ", val, " (valid range: [", static_cast(std::numeric_limits::lowest()), ", ", static_cast(std::numeric_limits::max()), "])"); return false; }); } @@ -91,8 +89,7 @@ namespace OpenVic { if (val <= static_cast(std::numeric_limits::max())) { return callback(val); } - Logger::error("Invalid uint: ", val, " (valid range: [0, ", - static_cast(std::numeric_limits::max()), "])"); + Logger::error("Invalid uint: ", val, " (valid range: [0, ", static_cast(std::numeric_limits::max()), "])"); return false; }); } diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp new file mode 100644 index 0000000..b590022 --- /dev/null +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -0,0 +1,315 @@ +#include "CountryHistory.hpp" + +#include "openvic-simulation/GameManager.hpp" + +using namespace OpenVic; +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, + 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 +) : primary_culture { new_primary_culture }, accepted_cultures { std::move(new_accepted_cultures) }, religion { new_religion }, ruling_party { new_ruling_party }, last_election { new_last_election }, upper_house { std::move(new_upper_house) }, capital { new_capital }, government_type { new_government_type }, plurality { new_plurality }, national_value { new_national_value }, civilised { new_civilised }, prestige { new_prestige }, reforms { std::move(new_reforms) }, inital_oob { new_inital_oob } {} + +Culture const* CountryHistory::get_primary_culture() const { + return primary_culture; +} + +const std::vector& CountryHistory::get_accepted_cultures() const { + return accepted_cultures; +} + +Religion const* CountryHistory::get_religion() const { + return religion; +} + +CountryParty const* CountryHistory::get_ruling_party() const { + return ruling_party; +} + +const Date CountryHistory::get_last_election() const { + return last_election; +} + +const std::map& CountryHistory::get_upper_house() const { + return upper_house; +} + +Province const* CountryHistory::get_capital() const { + return capital; +} + +GovernmentType const* CountryHistory::get_government_type() const { + return government_type; +} + +const fixed_point_t CountryHistory::get_plurality() const { + return plurality; +} + +NationalValue const* CountryHistory::get_national_value() const { + return national_value; +} + +const bool CountryHistory::is_civilised() const { + return civilised; +} + +const fixed_point_t CountryHistory::get_prestige() const { + return prestige; +} + +const std::vector& CountryHistory::get_reforms() const { + return reforms; +} + +Deployment const* CountryHistory::get_inital_oob() const { + return inital_oob; +} + +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, + 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 +) { + if (locked) { + Logger::error("Cannot add new history entry to country history registry: locked!"); + return false; + } + + /* combine duplicate histories, priority to current (defined later) */ + auto& country_registry = country_histories[country]; + const auto existing_entry = country_registry.find(date); + + if (existing_entry != country_registry.end()) { + if (primary_culture != nullptr) existing_entry->second.primary_culture = primary_culture; + if (updated_accepted_cultures) existing_entry->second.accepted_cultures = std::move(accepted_cultures); + if (religion != nullptr) existing_entry->second.religion = religion; + if (ruling_party != nullptr) existing_entry->second.ruling_party = ruling_party; + if (last_election != Date(0)) existing_entry->second.last_election = last_election; + if (updated_upper_house) existing_entry->second.upper_house = std::move(upper_house); + if (capital != nullptr) existing_entry->second.capital = capital; + if (government_type != nullptr) existing_entry->second.government_type = government_type; + if (plurality >= 0) existing_entry->second.plurality = plurality; + if (national_value != nullptr) existing_entry->second.national_value = national_value; + if (civilised) existing_entry->second.civilised = true; + if (prestige >= 0) existing_entry->second.prestige = prestige; + if (updated_reforms) existing_entry->second.reforms = std::move(reforms); + if (initial_oob != nullptr) 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 + }); + } + return true; +} + +void CountryHistoryManager::lock_country_histories() { + for (const auto& entry : country_histories) { + if (entry.second.size() == 0) { + Logger::error("Attempted to lock country histories - country ", entry.first->get_identifier(), " has no history entries!"); + } + } + Logger::info("Locked country history registry after registering ", country_histories.size(), " items"); + locked = true; +} + +bool CountryHistoryManager::is_locked() const { + return locked; +} + +CountryHistory const* CountryHistoryManager::get_country_history(Country const* country, Date entry) const { + Date closest_entry; + auto country_registry = country_histories.find(country); + + if (country_registry == country_histories.end()) { + Logger::error("Attempted to access history of undefined country ", country->get_identifier()); + return nullptr; + } + + for (const auto& current : country_registry->second) { + if (current.first == entry) return ¤t.second; + if (current.first > entry) continue; + if (current.first > closest_entry && current.first < entry) closest_entry = current.first; + } + + auto entry_registry = country_registry->second.find(closest_entry); + if (entry_registry != country_registry->second.end()) { + return &entry_registry->second; + } + /* warned about lack of entries earlier, return nullptr */ + return nullptr; +} + +inline CountryHistory const* CountryHistoryManager::get_country_history(Country const* country, Bookmark const* entry) const { + return get_country_history(country, entry->get_date()); +} + +inline bool CountryHistoryManager::_load_country_history_entry(GameManager& game_manager, std::string_view name, OpenVic::Date date, ast::NodeCPtr root) { + 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; + fixed_point_t plurality = -1, prestige = -1; + bool civilised = false; + Date last_election = Date(0); + Deployment const* initial_oob = nullptr; + + 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)) { + 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; + } + // 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 */ + "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", ZERO_OR_MORE, game_manager.get_pop_manager().get_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); + return true; + }), + "religion", ZERO_OR_ONE, game_manager.get_pop_manager().get_religion_manager().expect_religion_identifier(assign_variable_callback_pointer(religion)), + "government", ZERO_OR_ONE, game_manager.get_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(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_name() == 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; + }), + "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([&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 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); + } + } + + initial_oob = game_manager.get_military_manager().get_deployment_manager().get_deployment_by_identifier("NULL"); + return true; + }, + "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, 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, updated_upper_house, updated_reforms); + return ret; +} + +bool CountryHistoryManager::load_country_history_file(GameManager& game_manager, std::string_view name, Date start_date, ast::NodeCPtr root) { + if (game_manager.get_country_manager().get_country_by_identifier(name)->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, start_date, root); + + ret &= expect_dictionary( + [this, &game_manager, &name](std::string_view key, ast::NodeCPtr value) -> bool { + bool is_date = false; + Date entry = Date().from_string(key, &is_date, true); + if (!is_date) return true; + + if (entry > game_manager.get_define_manager().get_end_date()) { + Logger::error("History entry ", entry.to_string(), " of country ", name, " defined after defined end date ", game_manager.get_define_manager().get_end_date().to_string()); + return false; + } + + return _load_country_history_entry(game_manager, name, entry, value); + } + )(root); + + return ret; +} diff --git a/src/openvic-simulation/history/CountryHistory.hpp b/src/openvic-simulation/history/CountryHistory.hpp new file mode 100644 index 0000000..a59ebea --- /dev/null +++ b/src/openvic-simulation/history/CountryHistory.hpp @@ -0,0 +1,120 @@ +#pragma once + +#include +#include + +#include "openvic-simulation/country/Country.hpp" +#include "openvic-simulation/history/Bookmark.hpp" +#include "openvic-simulation/map/Province.hpp" +#include "openvic-simulation/military/Deployment.hpp" +#include "openvic-simulation/politics/Government.hpp" +#include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/politics/Issue.hpp" +#include "openvic-simulation/politics/NationalValue.hpp" +#include "openvic-simulation/pop/Culture.hpp" +#include "openvic-simulation/pop/Religion.hpp" +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/types/Date.hpp" + +namespace OpenVic { + struct CountryHistoryManager; + + struct CountryHistory { + friend struct CountryHistoryManager; + + private: + 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* inital_oob; + // TODO: technologies, tech schools, and inventions when PR#51 merged + // TODO: starting foreign investment + + 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, + 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 + ); + + public: + Culture const* get_primary_culture() const; + const std::vector& get_accepted_cultures() const; + Religion const* get_religion() const; + CountryParty const* get_ruling_party() const; + const Date get_last_election() const; + const std::map& get_upper_house() const; + Province const* get_capital() const; + GovernmentType const* get_government_type() const; + 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; + Deployment const* get_inital_oob() const; + }; + + struct CountryHistoryManager { + private: + std::map> country_histories; + bool locked = false; + + inline bool _load_country_history_entry(GameManager& game_manager, std::string_view name, OpenVic::Date date, ast::NodeCPtr root); + + public: + CountryHistoryManager() {} + + 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 + ); + + void lock_country_histories(); + bool is_locked() const; + + /* Returns history of country at date, if date doesn't have an entry returns closest entry before date. Return can be nullptr if an error occurs. */ + CountryHistory const* get_country_history(Country const* country, Date entry) const; + /* 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, Date start_date, ast::NodeCPtr root); + }; + +} // namespace OpenVic diff --git a/src/openvic-simulation/history/HistoryManager.hpp b/src/openvic-simulation/history/HistoryManager.hpp index bec5359..e0d6877 100644 --- a/src/openvic-simulation/history/HistoryManager.hpp +++ b/src/openvic-simulation/history/HistoryManager.hpp @@ -1,15 +1,18 @@ #pragma once #include "openvic-simulation/history/Bookmark.hpp" +#include "openvic-simulation/history/CountryHistory.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { struct HistoryManager { private: BookmarkManager bookmark_manager; + CountryHistoryManager country_manager; public: REF_GETTERS(bookmark_manager) + REF_GETTERS(country_manager) inline bool load_bookmark_file(ast::NodeCPtr root) { return bookmark_manager.load_bookmark_file(root); diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp new file mode 100644 index 0000000..d2637b1 --- /dev/null +++ b/src/openvic-simulation/military/Deployment.cpp @@ -0,0 +1,130 @@ +#include "Deployment.hpp" + +#include "openvic-simulation/GameManager.hpp" /* gosh don't we all just love circular inclusion :DDD */ + +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) } {} + +const std::vector& Deployment::get_armies() const { + return armies; +} + +const std::vector& Deployment::get_navies() const { + return navies; +} + +const std::vector& Deployment::get_leaders() const { + return leaders; +} + +DeploymentManager::DeploymentManager() : deployments { "deployments" } {} + +bool DeploymentManager::add_deployment(std::string_view path, std::vector&& armies, std::vector&& navies, std::vector&& leaders) { + if (path.empty()) { + 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") { + Logger::warning("Loading redundant empty order of battle at ", path); + } + + return deployments.add_item({ 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) { + 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; + LeaderTrait const* background; + fixed_point_t 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; + }), + "personality", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager().expect_leader_trait_identifier(assign_variable_callback_pointer(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)), + "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 (!background->is_background_trait()) { + Logger::error("Leader ", name, " has background ", background->get_identifier(), " which is not a background trait!"); + return false; + } + + leaders.push_back(Leader{ std::string(name), type, date, personality, background, prestige }); + return ret; }, + "army", ZERO_OR_MORE, [&armies, &game_manager](ast::NodeCPtr node) -> bool { + std::string_view name; + Province const* location; + std::vector regiments; + + bool ret = expect_dictionary_keys( + "leader", ZERO_OR_MORE, success_callback, /* another paradox gem, tested in game and they don't lead the army or even show up */ + "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), + "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), + "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)) + )(node); + regiments.push_back(regiment); + return ret; + } + )(node); + armies.push_back(Army{ std::string(name), location, std::move(regiments) }); + return ret; }, + "navy", ZERO_OR_MORE, [&navies, &game_manager](ast::NodeCPtr node) -> bool { + std::string_view name; + Province const* location; + std::vector ships; + + bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(name), false), + "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), + "type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_manager().expect_unit_identifier(assign_variable_callback_pointer(ship.type)) + )(node); + ships.push_back(ship); + return ret; + }, + "leader", ZERO_OR_MORE, success_callback + )(node); + navies.push_back(Navy{ std::string(name), location, std::move(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)); + + return ret; +} diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp new file mode 100644 index 0000000..8ec0e49 --- /dev/null +++ b/src/openvic-simulation/military/Deployment.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include +#include +#include +#include + +#include "openvic-simulation/dataloader/Dataloader.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/map/Province.hpp" +#include "openvic-simulation/military/LeaderTrait.hpp" +#include "openvic-simulation/military/Unit.hpp" +#include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" + +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; + }; + + struct Regiment { + std::string name; + Unit const* type; + Province const* home; + }; + + struct Ship { + std::string name; + Unit const* type; + }; + + struct Army { + std::string name; + Province const* location; + std::vector regiments; + }; + + struct Navy { + std::string name; + Province const* location; + std::vector ships; + }; + + struct DeploymentManager; + + struct Deployment : HasIdentifier { + friend struct DeploymentManager; + + private: + const std::vector armies; + const std::vector navies; + const std::vector leaders; + + Deployment(std::string_view new_path, std::vector&& new_armies, std::vector&& new_navies, std::vector&& new_leaders); + + public: + const std::vector& get_armies() const; + const std::vector& get_navies() const; + const std::vector& get_leaders() const; + }; + + struct DeploymentManager { + private: + IdentifierRegistry deployments; + + public: + DeploymentManager(); + + bool add_deployment(std::string_view path, std::vector&& armies, std::vector&& navies, std::vector&& leaders); + IDENTIFIER_REGISTRY_ACCESSORS(deployment); + + bool load_oob_file(GameManager& game_manager, std::string_view path, ast::NodeCPtr root); + }; +} // namespace OpenVic diff --git a/src/openvic-simulation/military/LeaderTrait.cpp b/src/openvic-simulation/military/LeaderTrait.cpp index b90a399..ed21c1f 100644 --- a/src/openvic-simulation/military/LeaderTrait.cpp +++ b/src/openvic-simulation/military/LeaderTrait.cpp @@ -22,7 +22,7 @@ ModifierValue const& LeaderTrait::get_modifiers() const { return modifiers; } -LeaderTraitManager::LeaderTraitManager() : leader_traits { "leader_traits" } {} +LeaderTraitManager::LeaderTraitManager() : leader_traits { "leader trait" } {} bool LeaderTraitManager::add_leader_trait(std::string_view identifier, LeaderTrait::trait_type_t type, ModifierValue&& modifiers) { if (identifier.empty()) { diff --git a/src/openvic-simulation/military/LeaderTrait.hpp b/src/openvic-simulation/military/LeaderTrait.hpp index e525e23..d885057 100644 --- a/src/openvic-simulation/military/LeaderTrait.hpp +++ b/src/openvic-simulation/military/LeaderTrait.hpp @@ -2,10 +2,11 @@ #include #include + +#include "openvic-simulation/Modifier.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/Modifier.hpp" namespace OpenVic { struct LeaderTraitManager; diff --git a/src/openvic-simulation/military/MilitaryManager.hpp b/src/openvic-simulation/military/MilitaryManager.hpp index fd8135d..57ba8d1 100644 --- a/src/openvic-simulation/military/MilitaryManager.hpp +++ b/src/openvic-simulation/military/MilitaryManager.hpp @@ -1,15 +1,19 @@ #pragma once -#include "openvic-simulation/military/Unit.hpp" #include "openvic-simulation/military/LeaderTrait.hpp" +#include "openvic-simulation/military/Deployment.hpp" +#include "openvic-simulation/military/Unit.hpp" namespace OpenVic { struct MilitaryManager { private: UnitManager unit_manager; LeaderTraitManager leader_trait_manager; + DeploymentManager deployment_manager; + public: REF_GETTERS(unit_manager) REF_GETTERS(leader_trait_manager) + REF_GETTERS(deployment_manager) }; } diff --git a/src/openvic-simulation/military/Unit.hpp b/src/openvic-simulation/military/Unit.hpp index 17408cf..ea28511 100644 --- a/src/openvic-simulation/military/Unit.hpp +++ b/src/openvic-simulation/military/Unit.hpp @@ -2,36 +2,37 @@ #include #include -#include "openvic-simulation/economy/Good.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" + #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/economy/Good.hpp" #include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #define UNIT_PARAMS \ Unit::icon_t icon, std::string_view sprite, bool active, std::string_view unit_type, \ - bool floating_flag, uint32_t priority, fixed_point_t max_strength, fixed_point_t default_organisation, \ - fixed_point_t maximum_speed, fixed_point_t weighted_value, std::string_view move_sound, \ - std::string_view select_sound, Timespan build_time, Good::good_map_t&& build_cost, \ - fixed_point_t supply_consumption, Good::good_map_t&& supply_cost + bool floating_flag, uint32_t priority, fixed_point_t max_strength, fixed_point_t default_organisation, \ + fixed_point_t maximum_speed, fixed_point_t weighted_value, std::string_view move_sound, \ + std::string_view select_sound, Timespan build_time, Good::good_map_t &&build_cost, \ + fixed_point_t supply_consumption, Good::good_map_t &&supply_cost #define LAND_PARAMS \ bool primary_culture, std::string_view sprite_override, std::string_view sprite_mount, \ - std::string_view sprite_mount_attach_node, fixed_point_t reconnaissance, fixed_point_t attack, fixed_point_t defence, \ - fixed_point_t discipline, fixed_point_t support, fixed_point_t maneuver, fixed_point_t siege + std::string_view sprite_mount_attach_node, fixed_point_t reconnaissance, fixed_point_t attack, fixed_point_t defence, \ + fixed_point_t discipline, fixed_point_t support, fixed_point_t maneuver, fixed_point_t siege #define NAVY_PARAMS \ Unit::icon_t naval_icon, bool sail, bool transport, bool capital, fixed_point_t colonial_points, bool build_overseas, \ - uint32_t min_port_level, int32_t limit_per_port, fixed_point_t supply_consumption_score, fixed_point_t hull, \ - fixed_point_t gun_power, fixed_point_t fire_range, fixed_point_t evasion, fixed_point_t torpedo_attack + uint32_t min_port_level, int32_t limit_per_port, fixed_point_t supply_consumption_score, fixed_point_t hull, \ + fixed_point_t gun_power, fixed_point_t fire_range, fixed_point_t evasion, fixed_point_t torpedo_attack namespace OpenVic { struct Unit : HasIdentifier { using icon_t = uint32_t; enum struct type_t { - LAND, NAVAL + LAND, + NAVAL }; private: diff --git a/src/openvic-simulation/types/Date.cpp b/src/openvic-simulation/types/Date.cpp index f167b7b..6e21dfc 100644 --- a/src/openvic-simulation/types/Date.cpp +++ b/src/openvic-simulation/types/Date.cpp @@ -179,7 +179,7 @@ std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) { } // Parsed from string of the form YYYY.MM.DD -Date Date::from_string(char const* const str, char const* const end, bool* successful) { +Date Date::from_string(char const* const str, char const* const end, bool* successful, bool quiet) { if (successful != nullptr) *successful = true; year_t year = 0; @@ -187,7 +187,7 @@ Date Date::from_string(char const* const str, char const* const end, bool* succe day_t day = 1; if (str == nullptr || end <= str) { - Logger::error("Invalid string start/end pointers: ", static_cast(str), " - ", static_cast(end)); + if (!quiet) Logger::error("Invalid string start/end pointers: ", static_cast(str), " - ", static_cast(end)); if (successful != nullptr) *successful = false; return { year, month, day }; } @@ -196,7 +196,7 @@ Date Date::from_string(char const* const str, char const* const end, bool* succe while (std::isdigit(*year_end) && ++year_end < end); if (year_end <= str) { - Logger::error("Failed to find year digits in date: ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Failed to find year digits in date: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; return { year, month, day }; } @@ -204,7 +204,7 @@ Date Date::from_string(char const* const str, char const* const end, bool* succe bool sub_successful = false; uint64_t val = StringUtils::string_to_uint64(str, year_end, &sub_successful, 10); if (!sub_successful || val > std::numeric_limits::max()) { - Logger::error("Failed to read year: ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Failed to read year: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; return { year, month, day }; } @@ -217,13 +217,13 @@ Date Date::from_string(char const* const str, char const* const end, bool* succe while (std::isdigit(*month_end) && ++month_end < end); } if (month_start >= month_end) { - Logger::error("Failed to find month digits in date: ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Failed to find month digits in date: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { sub_successful = false; val = StringUtils::string_to_uint64(month_start, month_end, &sub_successful, 10); if (!sub_successful || val < 1 || val > MONTHS_IN_YEAR) { - Logger::error("Failed to read month: ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Failed to read month: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { month = val; @@ -235,41 +235,41 @@ Date Date::from_string(char const* const str, char const* const end, bool* succe while (std::isdigit(*day_end) && ++day_end < end); } if (day_start >= day_end) { - Logger::error("Failed to find day digits in date: ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Failed to find day digits in date: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { sub_successful = false; val = StringUtils::string_to_uint64(day_start, day_end, &sub_successful); if (!sub_successful || val < 1 || val > DAYS_IN_MONTH[month - 1]) { - Logger::error("Failed to read day: ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Failed to read day: ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } else { day = val; if (day_end < end) { - Logger::error("Unexpected string \"", std::string_view { day_end, static_cast(end - day_end) }, "\" at the end of date ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Unexpected string \"", std::string_view { day_end, static_cast(end - day_end) }, "\" at the end of date ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } } } } else { - Logger::error("Unexpected character \"", *month_end, "\" in month of date ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Unexpected character \"", *month_end, "\" in month of date ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } } } } } else { - Logger::error("Unexpected character \"", *year_end, "\" in year of date ", std::string_view { str, static_cast(end - str) }); + if (!quiet) Logger::error("Unexpected character \"", *year_end, "\" in year of date ", std::string_view { str, static_cast(end - str) }); if (successful != nullptr) *successful = false; } } return { year, month, day }; }; -Date Date::from_string(char const* str, size_t length, bool* successful) { - return from_string(str, str + length, successful); +Date Date::from_string(char const* str, size_t length, bool* successful, bool quiet) { + return from_string(str, str + length, successful, quiet); } -Date Date::from_string(std::string_view str, bool* successful) { - return from_string(str.data(), str.length(), successful); +Date Date::from_string(std::string_view str, bool* successful, bool quiet) { + return from_string(str.data(), str.length(), successful, quiet); } diff --git a/src/openvic-simulation/types/Date.hpp b/src/openvic-simulation/types/Date.hpp index 718de80..0cc2587 100644 --- a/src/openvic-simulation/types/Date.hpp +++ b/src/openvic-simulation/types/Date.hpp @@ -92,9 +92,9 @@ namespace OpenVic { std::string to_string() const; explicit operator std::string() const; // Parsed from string of the form YYYY.MM.DD - static Date from_string(char const* str, char const* end, bool* successful = nullptr); - static Date from_string(char const* str, size_t length, bool* successful = nullptr); - static Date from_string(std::string_view str, bool* successful = nullptr); + static Date from_string(char const* str, char const* end, bool* successful = nullptr, bool quiet = false); + static Date from_string(char const* str, size_t length, bool* successful = nullptr, bool quiet = false); + static Date from_string(std::string_view str, bool* successful = nullptr, bool quiet = false); }; std::ostream& operator<<(std::ostream& out, Date const& date); } -- cgit v1.2.3-56-ga3b1