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/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 ++++++++++++-- 6 files changed, 350 insertions(+), 45 deletions(-) (limited to 'src/openvic-simulation/military') 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 +} -- cgit v1.2.3-56-ga3b1