From 5799836bee29024ce8a2d0fc45e06664c0110751 Mon Sep 17 00:00:00 2001 From: hop311 Date: Sun, 14 Apr 2024 23:29:28 +0100 Subject: Generate starting unit instances --- src/openvic-simulation/GameManager.cpp | 14 +- src/openvic-simulation/GameManager.hpp | 5 +- src/openvic-simulation/country/CountryInstance.cpp | 104 ++++++++++--- src/openvic-simulation/country/CountryInstance.hpp | 21 ++- src/openvic-simulation/dataloader/Dataloader.cpp | 7 +- src/openvic-simulation/map/Province.cpp | 37 +++++ src/openvic-simulation/map/Province.hpp | 10 +- src/openvic-simulation/military/Deployment.cpp | 63 ++++++-- src/openvic-simulation/military/Deployment.hpp | 40 +++-- src/openvic-simulation/military/Leader.hpp | 2 + .../military/MilitaryManager.hpp | 4 + src/openvic-simulation/military/UnitInstance.cpp | 170 ++++++++++++++++++++- src/openvic-simulation/military/UnitInstance.hpp | 116 ++++++++++++-- .../types/fixed_point/FixedPointMap.hpp | 13 ++ 14 files changed, 528 insertions(+), 78 deletions(-) diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp index bb807c9..94d9c62 100644 --- a/src/openvic-simulation/GameManager.cpp +++ b/src/openvic-simulation/GameManager.cpp @@ -54,21 +54,33 @@ bool GameManager::reset() { bool GameManager::load_bookmark(Bookmark const* new_bookmark) { bool ret = reset(); + bookmark = new_bookmark; if (bookmark == nullptr) { + Logger::error("Cannot load bookmark - null!"); return ret; } + Logger::info("Loading bookmark ", bookmark->get_name(), " with start date ", bookmark->get_date()); + if (!define_manager.in_game_period(bookmark->get_date())) { Logger::warning("Bookmark date ", bookmark->get_date(), " is not in the game's time period!"); } + today = bookmark->get_date(); + ret &= map.apply_history_to_provinces( history_manager.get_province_manager(), today, politics_manager.get_ideology_manager(), politics_manager.get_issue_manager(), *country_manager.get_country_by_identifier("ENG") ); + map.get_state_manager().generate_states(map); - // TODO - apply country history + + ret &= country_instance_manager.generate_country_instances(country_manager); + ret &= country_instance_manager.apply_history_to_countries( + history_manager.get_country_manager(), today, military_manager.get_unit_instance_manager(), map + ); + return ret; } diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp index 66b7e57..8e0bd00 100644 --- a/src/openvic-simulation/GameManager.hpp +++ b/src/openvic-simulation/GameManager.hpp @@ -23,7 +23,6 @@ namespace OpenVic { using gamestate_updated_func_t = std::function; private: - Map PROPERTY_REF(map); DefineManager PROPERTY_REF(define_manager); EconomyManager PROPERTY_REF(economy_manager); MilitaryManager PROPERTY_REF(military_manager); @@ -33,11 +32,15 @@ namespace OpenVic { ResearchManager PROPERTY_REF(research_manager); PopManager PROPERTY_REF(pop_manager); CountryManager PROPERTY_REF(country_manager); + CountryInstanceManager PROPERTY_REF(country_instance_manager); CrimeManager PROPERTY_REF(crime_manager); EventManager PROPERTY_REF(event_manager); DecisionManager PROPERTY_REF(decision_manager); UIManager PROPERTY_REF(ui_manager); DiplomacyManager PROPERTY_REF(diplomacy_manager); + /* Near the end so it is freed after other managers that may depend on it, + * e.g. if we want to remove military units from the province they're in when they're destructed. */ + Map PROPERTY_REF(map); ScriptManager PROPERTY_REF(script_manager); SimulationClock PROPERTY_REF(simulation_clock); diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 11b1e90..b7b818e 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -1,7 +1,18 @@ #include "CountryInstance.hpp" +#include "openvic-simulation/military/UnitInstance.hpp" + using namespace OpenVic; +CountryInstance::CountryInstance(Country const* new_base_country) + : base_country { new_base_country }, primary_culture { nullptr }, religion { nullptr }, ruling_party { nullptr }, + last_election {}, capital { nullptr }, government_type { nullptr }, plurality { 0 }, national_value { nullptr }, + civilised { false }, prestige { 0 } {} + +std::string_view CountryInstance::get_identifier() const { + return base_country != nullptr ? base_country->get_identifier() : "NULL"; +} + bool CountryInstance::add_accepted_culture(Culture const* new_accepted_culture) { if (std::find(accepted_cultures.begin(), accepted_cultures.end(), new_accepted_culture) != accepted_cultures.end()) { Logger::warning("Attempted to add accepted culture ", new_accepted_culture->get_identifier(), " to country ", base_country->get_identifier(), ": already present!"); @@ -48,32 +59,79 @@ bool CountryInstance::remove_reform(Reform const* reform_to_remove) { return true; } -bool CountryInstance::apply_history_to_country(CountryHistoryMap const& history, Date date) { - accepted_cultures.clear(); - upper_house.clear(); - reforms.clear(); +bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry) { + if (entry == nullptr) { + Logger::error("Trying to apply null country history to ", get_identifier()); + return false; + } - bool ret = true; - for (CountryHistoryEntry const* entry : history.get_entries_up_to(date)) { - if (entry->get_primary_culture()) primary_culture = *entry->get_primary_culture(); - for (Culture const* culture : entry->get_accepted_cultures()) { - ret &= add_accepted_culture(culture); - } - if (entry->get_religion()) religion = *entry->get_religion(); - if (entry->get_ruling_party()) ruling_party = *entry->get_ruling_party(); - if (entry->get_last_election()) last_election = *entry->get_last_election(); - for (auto const& [ideology, popularity] : entry->get_upper_house()) { - add_to_upper_house(ideology, popularity); + constexpr auto set_optional = [](T& target, std::optional const& source) { + if (source) { + target = *source; } - if (entry->get_capital()) capital = *entry->get_capital(); - if (entry->get_government_type()) government_type = *entry->get_government_type(); - if (entry->get_plurality()) plurality = *entry->get_plurality(); - if (entry->get_national_value()) national_value = *entry->get_national_value(); - if (entry->is_civilised()) civilised = *entry->is_civilised(); - if (entry->get_prestige()) prestige = *entry->get_prestige(); - for (Reform const* reform : entry->get_reforms()) { - ret &= add_reform(reform); + }; + + bool ret = true; + + set_optional(primary_culture, entry->get_primary_culture()); + for (Culture const* culture : entry->get_accepted_cultures()) { + ret &= add_accepted_culture(culture); + } + set_optional(religion, entry->get_religion()); + set_optional(ruling_party, entry->get_ruling_party()); + set_optional(last_election, entry->get_last_election()); + for (auto const& [ideology, popularity] : entry->get_upper_house()) { + add_to_upper_house(ideology, popularity); + } + set_optional(capital, entry->get_capital()); + set_optional(government_type, entry->get_government_type()); + set_optional(plurality, entry->get_plurality()); + set_optional(national_value, entry->get_national_value()); + set_optional(civilised, entry->is_civilised()); + set_optional(prestige, entry->get_prestige()); + for (Reform const* reform : entry->get_reforms()) { + ret &= add_reform(reform); + } + + return ret; +} + +bool CountryInstanceManager::generate_country_instances(CountryManager const& country_manager) { + reserve_more(country_instances, country_manager.get_country_count()); + + for (Country const& country : country_manager.get_countries()) { + country_instances.push_back({ &country }); + } + + return true; +} + +bool CountryInstanceManager::apply_history_to_countries( + CountryHistoryManager const& history_manager, Date date, UnitInstanceManager& unit_instance_manager, Map& map +) { + bool ret = true; + + for (CountryInstance& country_instance : country_instances) { + if (!country_instance.get_base_country()->is_dynamic_tag()) { + CountryHistoryMap const* history_map = history_manager.get_country_history(country_instance.get_base_country()); + + if (history_map != nullptr) { + CountryHistoryEntry const* oob_history_entry = nullptr; + + for (CountryHistoryEntry const* entry : history_map->get_entries_up_to(date)) { + country_instance.apply_history_to_country(entry); + + if (entry->get_inital_oob()) { + oob_history_entry = entry; + } + } + + if (oob_history_entry != nullptr) { + unit_instance_manager.generate_deployment(map, country_instance, *oob_history_entry->get_inital_oob()); + } + } } } + return ret; } diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index 98c6e90..7efd930 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -6,8 +6,11 @@ #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { + struct UnitInstanceManager; + /* Representation of an existing country that is currently in-game. */ struct CountryInstance { + friend struct CountryInstanceManager; private: Country const* PROPERTY_RW(base_country); Culture const* PROPERTY_RW(primary_culture); @@ -25,7 +28,11 @@ namespace OpenVic { std::vector PROPERTY(reforms); // TODO: should be map of reform groups to active reforms: must set defaults & validate applied history // TODO: Military units + OOBs; will probably need an extensible deployment class + CountryInstance(Country const* new_base_country); + public: + std::string_view get_identifier() const; + bool add_accepted_culture(Culture const* new_accepted_culture); bool remove_accepted_culture(Culture const* culture_to_remove); /* Add or modify a party in the upper house. */ @@ -34,6 +41,18 @@ namespace OpenVic { bool add_reform(Reform const* new_reform); bool remove_reform(Reform const* reform_to_remove); - bool apply_history_to_country(CountryHistoryMap const& history, Date date); + bool apply_history_to_country(CountryHistoryEntry const* entry); + }; + + struct CountryInstanceManager { + private: + std::vector PROPERTY(country_instances); + + public: + bool generate_country_instances(CountryManager const& country_manager); + + bool apply_history_to_countries( + CountryHistoryManager const& history_manager, Date date, UnitInstanceManager& unit_instance_manager, Map& map + ); }; } // namespace OpenVic diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index d01f6ff..e13bc23 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -513,12 +513,14 @@ bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_fi { /* Country History */ CountryHistoryManager& country_history_manager = game_manager.get_history_manager().get_country_manager(); + DeploymentManager& deployment_manager = game_manager.get_military_manager().get_deployment_manager(); static constexpr std::string_view country_history_directory = "history/countries"; const path_vector_t country_history_files = lookup_basic_indentifier_prefixed_files_in_dir(country_history_directory, ".txt"); country_history_manager.reserve_more_country_histories(country_history_files.size()); + deployment_manager.reserve_more_deployments(country_history_files.size()); ret &= apply_to_files( country_history_files, @@ -541,11 +543,6 @@ bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_fi ); country_history_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) { diff --git a/src/openvic-simulation/map/Province.cpp b/src/openvic-simulation/map/Province.cpp index d1183f5..eb23759 100644 --- a/src/openvic-simulation/map/Province.cpp +++ b/src/openvic-simulation/map/Province.cpp @@ -1,6 +1,7 @@ #include "Province.hpp" #include "openvic-simulation/history/ProvinceHistory.hpp" +#include "openvic-simulation/military/UnitInstance.hpp" using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -181,6 +182,42 @@ fvec2_t Province::get_unit_position() const { return positions.unit.value_or(positions.centre); } +bool Province::add_army(ArmyInstance& army) { + if (armies.emplace(&army).second) { + return true; + } else { + Logger::error("Trying to add already-existing army ", army.get_name(), " to province ", get_identifier()); + return false; + } +} + +bool Province::remove_army(ArmyInstance& army) { + if (armies.erase(&army) > 0) { + return true; + } else { + Logger::error("Trying to remove non-existent army ", army.get_name(), " from province ", get_identifier()); + return false; + } +} + +bool Province::add_navy(NavyInstance& navy) { + if (navies.emplace(&navy).second) { + return true; + } else { + Logger::error("Trying to add already-existing navy ", navy.get_name(), " to province ", get_identifier()); + return false; + } +} + +bool Province::remove_navy(NavyInstance& navy) { + if (navies.erase(&navy) > 0) { + return true; + } else { + Logger::error("Trying to remove non-existent navy ", navy.get_name(), " from province ", get_identifier()); + return false; + } +} + bool Province::reset(BuildingTypeManager const& building_type_manager) { terrain_type = default_terrain_type; life_rating = 0; diff --git a/src/openvic-simulation/map/Province.hpp b/src/openvic-simulation/map/Province.hpp index 476ecc9..c4785dd 100644 --- a/src/openvic-simulation/map/Province.hpp +++ b/src/openvic-simulation/map/Province.hpp @@ -1,7 +1,5 @@ #pragma once -#include - #include "openvic-simulation/country/Country.hpp" #include "openvic-simulation/economy/BuildingInstance.hpp" #include "openvic-simulation/politics/Ideology.hpp" @@ -20,6 +18,8 @@ namespace OpenVic { struct ProvinceSetModifier; using Climate = ProvinceSetModifier; using Continent = ProvinceSetModifier; + struct ArmyInstance; + struct NavyInstance; /* REQUIREMENTS: * MAP-5, MAP-7, MAP-8, MAP-43, MAP-47 @@ -118,6 +118,8 @@ namespace OpenVic { // TODO - change this into a factory-like structure Good const* PROPERTY(rgo); IdentifierRegistry IDENTIFIER_REGISTRY(building); + ordered_set PROPERTY(armies); + ordered_set PROPERTY(navies); std::vector PROPERTY(pops); Pop::pop_size_t PROPERTY(total_population); @@ -154,6 +156,10 @@ namespace OpenVic { bool has_adjacency_going_through(Province const* province) const; fvec2_t get_unit_position() const; + bool add_army(ArmyInstance& army); + bool remove_army(ArmyInstance& army); + bool add_navy(NavyInstance& navy); + bool remove_navy(NavyInstance& navy); bool reset(BuildingTypeManager const& building_type_manager); bool apply_history_to_province(ProvinceHistoryEntry const* entry); diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index 903df3a..1f8b820 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -5,16 +5,19 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -RegimentDeployment::RegimentDeployment(std::string_view new_name, RegimentType const* new_type, Province const* new_home) +RegimentDeployment::RegimentDeployment(std::string_view new_name, RegimentType const& new_type, Province const* new_home) : name { new_name }, type { new_type }, home { new_home } {} -ShipDeployment::ShipDeployment(std::string_view new_name, ShipType const* new_type) : name { new_name }, type { new_type } {} +ShipDeployment::ShipDeployment(std::string_view new_name, ShipType const& new_type) + : name { new_name }, type { new_type } {} -ArmyDeployment::ArmyDeployment(std::string_view new_name, Province const* new_location, std::vector&& new_regiments) - : name { new_name }, location { new_location }, regiments { std::move(new_regiments) } {} +ArmyDeployment::ArmyDeployment( + std::string_view new_name, Province const* new_location, std::vector&& new_regiments +) : name { new_name }, location { new_location }, regiments { std::move(new_regiments) } {} -NavyDeployment::NavyDeployment(std::string_view new_name, Province const* new_location, std::vector&& new_ships) - : name { new_name }, location { new_location }, ships { std::move(new_ships) } {} +NavyDeployment::NavyDeployment( + 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, @@ -23,16 +26,15 @@ Deployment::Deployment( leaders { std::move(new_leaders) } {} bool DeploymentManager::add_deployment( - std::string_view path, std::vector&& armies, std::vector&& navies, std::vector&& leaders + 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; } - return deployments.add_item( - std::make_unique(std::move(path), std::move(armies), std::move(navies), std::move(leaders)) - ); + return deployments.add_item({ path, std::move(armies), std::move(navies), std::move(leaders) }); } bool DeploymentManager::load_oob_file( @@ -43,12 +45,16 @@ bool DeploymentManager::load_oob_file( if (deployment != nullptr) { return true; } + if (missing_oob_files.contains(history_path)) { return !fail_on_missing; } + 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) { @@ -58,9 +64,11 @@ bool DeploymentManager::load_oob_file( 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 { @@ -95,6 +103,7 @@ bool DeploymentManager::load_oob_file( ); ret = false; } + if (leader_background != nullptr && !leader_background->is_background_trait()) { Logger::error( "Leader ", leader_name, " has background ", leader_background->get_identifier(), @@ -102,9 +111,11 @@ bool DeploymentManager::load_oob_file( ); ret = false; } + leaders.emplace_back( leader_name, leader_branch, leader_date, leader_personality, leader_background, leader_prestige, picture ); + return ret; }, "army", ZERO_OR_MORE, [&armies, &game_manager](ast::NodeCPtr node) -> bool { @@ -120,6 +131,7 @@ bool DeploymentManager::load_oob_file( std::string_view regiment_name {}; RegimentType 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_type_manager() @@ -127,16 +139,26 @@ bool DeploymentManager::load_oob_file( "home", ZERO_OR_ONE, game_manager.get_map() .expect_province_identifier(assign_variable_callback_pointer(regiment_home)) )(node); + if (regiment_home == nullptr) { Logger::warning("RegimentDeployment ", regiment_name, " has no home province!"); } - army_regiments.emplace_back(regiment_name, regiment_type, regiment_home); + + if (regiment_type == nullptr) { + Logger::error("RegimentDeployment ", regiment_name, " has no type!"); + return false; + } + + army_regiments.push_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.emplace_back(army_name, army_location, std::move(army_regiments)); + + armies.push_back({ army_name, army_location, std::move(army_regiments) }); + return ret; }, "navy", ZERO_OR_MORE, [&navies, &game_manager](ast::NodeCPtr node) -> bool { @@ -151,26 +173,39 @@ bool DeploymentManager::load_oob_file( "ship", ONE_OR_MORE, [&game_manager, &navy_ships](ast::NodeCPtr node) -> bool { std::string_view ship_name {}; ShipType 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_type_manager() .expect_ship_type_identifier(assign_variable_callback_pointer(ship_type)) )(node); - navy_ships.emplace_back(ship_name, ship_type); + + if (ship_type == nullptr) { + Logger::error("ShipDeployment ", ship_name, " has no type!"); + return false; + } + + navy_ships.push_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.emplace_back(navy_name, navy_location, std::move(navy_ships)); + + navies.push_back({ navy_name, navy_location, std::move(navy_ships) }); + return ret; } )(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; } diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp index 742914d..8966397 100644 --- a/src/openvic-simulation/military/Deployment.hpp +++ b/src/openvic-simulation/military/Deployment.hpp @@ -11,48 +11,64 @@ namespace OpenVic { struct RegimentDeployment { + friend struct DeploymentManager; + private: std::string PROPERTY(name); - RegimentType const* PROPERTY(type); + RegimentType const& PROPERTY(type); Province const* PROPERTY(home); + RegimentDeployment(std::string_view new_name, RegimentType const& new_type, Province const* new_home); + public: - RegimentDeployment(std::string_view new_name, RegimentType const* new_type, Province const* new_home); + RegimentDeployment(RegimentDeployment&&) = default; }; struct ShipDeployment { + friend struct DeploymentManager; + private: std::string PROPERTY(name); - ShipType const* PROPERTY(type); + ShipType const& PROPERTY(type); + + ShipDeployment(std::string_view new_name, ShipType const& new_type); public: - ShipDeployment(std::string_view new_name, ShipType const* new_type); + ShipDeployment(ShipDeployment&&) = default; }; struct ArmyDeployment { + friend struct DeploymentManager; + private: std::string PROPERTY(name); Province const* PROPERTY(location); std::vector PROPERTY(regiments); + ArmyDeployment( + std::string_view new_name, Province const* new_location, std::vector&& new_regiments + ); + public: - ArmyDeployment(std::string_view new_name, Province const* new_location, std::vector&& new_regiments); + ArmyDeployment(ArmyDeployment&&) = default; }; struct NavyDeployment { + friend struct DeploymentManager; + private: std::string PROPERTY(name); Province const* PROPERTY(location); std::vector PROPERTY(ships); - public: NavyDeployment(std::string_view new_name, Province const* new_location, std::vector&& new_ships); + + public: + NavyDeployment(NavyDeployment&&) = default; }; struct Deployment : HasIdentifier { - friend std::unique_ptr std::make_unique( - std::string_view&&, std::vector&&, std::vector&&, std::vector&& - ); + friend struct DeploymentManager; private: std::vector PROPERTY(armies); @@ -70,18 +86,20 @@ namespace OpenVic { struct DeploymentManager { private: - IdentifierInstanceRegistry IDENTIFIER_REGISTRY(deployment); + IdentifierRegistry IDENTIFIER_REGISTRY(deployment); string_set_t missing_oob_files; public: bool add_deployment( - std::string_view path, std::vector&& armies, std::vector&& navies, std::vector&& leaders + std::string_view path, std::vector&& armies, std::vector&& navies, + std::vector&& leaders ); 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/Leader.hpp b/src/openvic-simulation/military/Leader.hpp index 180fd39..5995164 100644 --- a/src/openvic-simulation/military/Leader.hpp +++ b/src/openvic-simulation/military/Leader.hpp @@ -19,5 +19,7 @@ namespace OpenVic { std::string_view new_name, UnitType::branch_t new_branch, Date new_date, LeaderTrait const* new_personality, LeaderTrait const* new_background, fixed_point_t new_prestige, std::string_view new_picture ); + + Leader(Leader&&) = default; }; } \ No newline at end of file diff --git a/src/openvic-simulation/military/MilitaryManager.hpp b/src/openvic-simulation/military/MilitaryManager.hpp index 343d789..c2fd058 100644 --- a/src/openvic-simulation/military/MilitaryManager.hpp +++ b/src/openvic-simulation/military/MilitaryManager.hpp @@ -2,6 +2,7 @@ #include "openvic-simulation/military/Deployment.hpp" #include "openvic-simulation/military/LeaderTrait.hpp" +#include "openvic-simulation/military/UnitInstance.hpp" #include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/military/Wargoal.hpp" @@ -12,5 +13,8 @@ namespace OpenVic { LeaderTraitManager PROPERTY_REF(leader_trait_manager); DeploymentManager PROPERTY_REF(deployment_manager); WargoalTypeManager PROPERTY_REF(wargoal_type_manager); + + // TODO - separate this mutable game data manager from const defines data managers. + UnitInstanceManager PROPERTY_REF(unit_instance_manager); }; } diff --git a/src/openvic-simulation/military/UnitInstance.cpp b/src/openvic-simulation/military/UnitInstance.cpp index 7927b0f..fcb9e3b 100644 --- a/src/openvic-simulation/military/UnitInstance.cpp +++ b/src/openvic-simulation/military/UnitInstance.cpp @@ -1,10 +1,14 @@ #include "UnitInstance.hpp" + #include + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/map/Map.hpp" #include "openvic-simulation/military/UnitType.hpp" using namespace OpenVic; -RegimentInstance::RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop& new_pop) +RegimentInstance::RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop) : UnitInstance { new_name, new_regiment_type }, pop { new_pop } {} ShipInstance::ShipInstance(std::string_view new_name, ShipType const& new_ship_type) @@ -14,18 +18,172 @@ MovementInfo::MovementInfo() : path {}, movement_progress {} {} //TODO: pathfinding logic MovementInfo::MovementInfo(Province const* starting_province, Province const* target_province) - : path { std::vector { starting_province, target_province } }, movement_progress { 0 } {} + : path { starting_province, target_province }, movement_progress { 0 } {} ArmyInstance::ArmyInstance( std::string_view new_name, std::vector&& new_units, Leader const* new_leader, - Province const* new_position -) : UnitInstanceGroup { new_name, UnitType::branch_t::LAND, std::move(new_units), new_leader, new_position } {} + CountryInstance* new_country +) : UnitInstanceGroup { new_name, UnitType::branch_t::LAND, std::move(new_units), new_leader, new_country } {} + +void ArmyInstance::set_position(Province* new_position) { + if (position != new_position) { + if (position != nullptr) { + position->remove_army(*this); + } + position = new_position; + if (position != nullptr) { + position->add_army(*this); + } + } +} NavyInstance::NavyInstance( std::string_view new_name, std::vector&& new_units, Leader const* new_leader, - Province const* new_position -) : UnitInstanceGroup { new_name, UnitType::branch_t::NAVAL, std::move(new_units), new_leader, new_position } {} \ No newline at end of file + CountryInstance* new_country +) : UnitInstanceGroup { new_name, UnitType::branch_t::NAVAL, std::move(new_units), new_leader, new_country } {} + +void NavyInstance::set_position(Province* new_position) { + if (position != new_position) { + if (position != nullptr) { + position->remove_navy(*this); + } + position = new_position; + if (position != nullptr) { + position->add_navy(*this); + } + } +} + +bool UnitInstanceManager::generate_regiment(RegimentDeployment const& regiment_deployment, RegimentInstance*& regiment) { + // TODO - get pop from Province regiment_deployment.get_home() + regiments.push_back({ regiment_deployment.get_name(), regiment_deployment.get_type(), nullptr }); + + regiment = ®iments.back(); + + return true; +} + +bool UnitInstanceManager::generate_ship(ShipDeployment const& ship_deployment, ShipInstance*& ship) { + ships.push_back({ ship_deployment.get_name(), ship_deployment.get_type() }); + + ship = &ships.back(); + + return true; +} + +bool UnitInstanceManager::generate_army(Map& map, CountryInstance& country, ArmyDeployment const& army_deployment) { + if (army_deployment.get_regiments().empty()) { + Logger::error( + "Trying to generate army \"", army_deployment.get_name(), "\" with no regiments for country \"", + country.get_identifier(), "\"" + ); + return false; + } + + if (army_deployment.get_location() == nullptr) { + Logger::error( + "Trying to generate army \"", army_deployment.get_name(), "\" with no location for country \"", + country.get_identifier(), "\"" + ); + return false; + } + + bool ret = true; + + std::vector army_regiments; + + for (RegimentDeployment const& regiment_deployment : army_deployment.get_regiments()) { + RegimentInstance* regiment = nullptr; + + ret &= generate_regiment(regiment_deployment, regiment); + + if (regiment != nullptr) { + army_regiments.push_back(regiment); + } + } + + if (army_regiments.empty()) { + Logger::error( + "Failed to generate any regiments for army \"", army_deployment.get_name(), "\" for country \"", + country.get_identifier(), "\"" + ); + return false; + } + + armies.push_back({ army_deployment.get_name(), std::move(army_regiments), nullptr, &country }); + + armies.back().set_position(map.remove_province_const(army_deployment.get_location())); + + return ret; +} + +bool UnitInstanceManager::generate_navy(Map& map, CountryInstance& country, NavyDeployment const& navy_deployment) { + if (navy_deployment.get_ships().empty()) { + Logger::error( + "Trying to generate navy \"", navy_deployment.get_name(), "\" with no ships for country \"", + country.get_identifier(), "\"" + ); + return false; + } + + if (navy_deployment.get_location() == nullptr) { + Logger::error( + "Trying to generate navy \"", navy_deployment.get_name(), "\" with no location for country \"", + country.get_identifier(), "\"" + ); + return false; + } + + bool ret = true; + + std::vector navy_ships; + + for (ShipDeployment const& ship_deployment : navy_deployment.get_ships()) { + ShipInstance* ship = nullptr; + + ret &= generate_ship(ship_deployment, ship); + + if (ship != nullptr) { + navy_ships.push_back(ship); + } + } + + if (navy_ships.empty()) { + Logger::error( + "Failed to generate any ships for navy \"", navy_deployment.get_name(), "\" for country \"", + country.get_identifier(), "\"" + ); + return false; + } + + navies.push_back({ navy_deployment.get_name(), std::move(navy_ships), nullptr, &country }); + + navies.back().set_position(map.remove_province_const(navy_deployment.get_location())); + + return ret; +} + +bool UnitInstanceManager::generate_deployment(Map& map, CountryInstance& country, Deployment const* deployment) { + if (deployment == nullptr) { + Logger::error("Trying to generate null deployment for ", country.get_identifier()); + return false; + } + + // TODO - Leaders (could be stored in CountryInstance?) + + bool ret = true; + + for (ArmyDeployment const& army_deployment : deployment->get_armies()) { + ret &= generate_army(map, country, army_deployment); + } + + for (NavyDeployment const& navy_deployment : deployment->get_navies()) { + ret &= generate_navy(map, country, navy_deployment); + } + + return ret; +} diff --git a/src/openvic-simulation/military/UnitInstance.hpp b/src/openvic-simulation/military/UnitInstance.hpp index dcca18a..e3d541a 100644 --- a/src/openvic-simulation/military/UnitInstance.hpp +++ b/src/openvic-simulation/military/UnitInstance.hpp @@ -3,7 +3,9 @@ #include #include #include + #include "openvic-simulation/map/Province.hpp" +#include "openvic-simulation/military/Deployment.hpp" #include "openvic-simulation/military/Leader.hpp" #include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -11,7 +13,7 @@ namespace OpenVic { template T> struct UnitInstance { - protected: + private: std::string PROPERTY(unit_name); T const& PROPERTY(unit_type); //can't change @@ -26,23 +28,35 @@ namespace OpenVic { organisation { new_unit_type.get_default_organisation() }, //TODO: modifiers morale { 0 }, //TODO: modifiers strength { new_unit_type.get_max_strength() } {} + public: + UnitInstance(UnitInstance&&) = default; + void set_unit_name(std::string_view new_unit_name) { unit_name = new_unit_name; } }; struct RegimentInstance : UnitInstance { + friend struct UnitInstanceManager; + private: - Pop& PROPERTY(pop); + Pop* PROPERTY(pop); + + RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop); public: - RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop& new_pop); + RegimentInstance(RegimentInstance&&) = default; }; struct ShipInstance : UnitInstance { - public: + friend struct UnitInstanceManager; + + private: ShipInstance(std::string_view new_name, ShipType const& new_ship_type); + + public: + ShipInstance(ShipInstance&&) = default; }; struct MovementInfo { @@ -55,56 +69,130 @@ namespace OpenVic { MovementInfo(Province const* starting_province, Province const* target_province); // contains/calls pathfinding logic }; + struct CountryInstance; + template I> struct UnitInstanceGroup { private: std::string PROPERTY(name); const UnitType::branch_t PROPERTY(branch); std::vector PROPERTY(units); - Leader const* PROPERTY_RW(leader); - Province const* PROPERTY_RW(position); + Leader const* PROPERTY(leader); MovementInfo PROPERTY_REF(movement_info); protected: + Province* PROPERTY_ACCESS(position, protected); + CountryInstance* PROPERTY_ACCESS(country, protected); + UnitInstanceGroup( std::string_view new_name, UnitType::branch_t new_branch, std::vector&& new_units, Leader const* new_leader, - Province const* new_position + CountryInstance* new_country ) : name { new_name }, branch { new_branch }, units { std::move(new_units) }, leader { new_leader }, - position { new_position } {} - + position { nullptr }, + country { new_country } {} + public: + UnitInstanceGroup(UnitInstanceGroup&&) = default; + UnitInstanceGroup(UnitInstanceGroup const&) = delete; + void set_name(std::string_view new_name) { name = new_name; } + + size_t get_unit_count() const { + return units.size(); + } + + bool empty() const { + return units.empty(); + } + + size_t get_unit_category_count(UnitType::unit_category_t unit_category) const { + return std::count_if(units.begin(), units.end(), [unit_category](I const* unit) { + return unit->unit_type.get_unit_category() == unit_category; + }); + } + + UnitType const* get_display_unit_type() const { + if (units.empty()) { + return nullptr; + } + + fixed_point_map_t weighted_unit_types; + + for (I const* unit : units) { + UnitType const& unit_type = unit->get_unit_type(); + weighted_unit_types[&unit_type] += unit_type.get_weighted_value(); + } + + return get_largest_item_tie_break( + weighted_unit_types, + [](UnitType const* lhs, UnitType const* rhs) -> bool { + return lhs->get_weighted_value() < rhs->get_weighted_value(); + } + )->first; + } + + virtual void set_position(Province* new_position) = 0; }; struct ArmyInstance : UnitInstanceGroup { - public: + friend struct UnitInstanceManager; + + private: ArmyInstance( std::string_view new_name, std::vector&& new_units, Leader const* new_leader, - Province const* new_position + CountryInstance* new_country ); + + public: + ArmyInstance(ArmyInstance&&) = default; + + void set_position(Province* new_position) override; }; struct NavyInstance : UnitInstanceGroup { + friend struct UnitInstanceManager; + private: std::vector PROPERTY(carried_armies); - public: NavyInstance( std::string_view new_name, std::vector&& new_ships, Leader const* new_leader, - Province const* new_position + CountryInstance* new_country ); + + public: + NavyInstance(NavyInstance&&) = default; + + void set_position(Province* new_position) override; + }; + + struct UnitInstanceManager { + private: + std::deque PROPERTY(regiments); + std::deque PROPERTY(ships); + + std::deque PROPERTY(armies); + std::deque PROPERTY(navies); + + bool generate_regiment(RegimentDeployment const& regiment_deployment, RegimentInstance*& regiment); + bool generate_ship(ShipDeployment const& ship_deployment, ShipInstance*& ship); + bool generate_army(Map& map, CountryInstance& country, ArmyDeployment const& army_deployment); + bool generate_navy(Map& map, CountryInstance& country, NavyDeployment const& navy_deployment); + + public: + bool generate_deployment(Map& map, CountryInstance& country, Deployment const* deployment); }; -} \ No newline at end of file +} diff --git a/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp b/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp index 5df537a..bf07ac2 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPointMap.hpp @@ -151,6 +151,19 @@ namespace OpenVic { return std::max_element(map.begin(), map.end(), pred); } + /* This function includes a key comparator to choose between entries with equal values. */ + template + constexpr fixed_point_map_const_iterator_t get_largest_item_tie_break( + fixed_point_map_t const& map, const auto key_pred + ) { + constexpr auto pred = + [key_pred](fixed_point_map_value_t const& lhs, fixed_point_map_value_t const& rhs) -> bool { + return lhs.second < rhs.second || (lhs.second == rhs.second && key_pred(lhs.first, rhs.first)); + }; + + return std::max_element(map.begin(), map.end(), pred); + } + template constexpr std::pair, fixed_point_map_const_iterator_t> get_largest_two_items( fixed_point_map_t const& map -- cgit v1.2.3-56-ga3b1