diff options
author | Hop311 <Hop3114@gmail.com> | 2024-07-14 17:37:10 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-07-14 17:37:10 +0200 |
commit | e8a3b33f13ebdf3a388b4996308b4db9763dc375 (patch) | |
tree | db365e5d71df41b7b35abdcc3d4c0d76f1653619 /src/openvic-simulation | |
parent | b9b35ad9536cfdcd61f5208eeaad7ead4bd0418d (diff) | |
parent | e4701ebc08f57575a02bdc1777d9851a987c1cba (diff) |
Merge pull request #173 from OpenVicProject/unit-colonies
Unit and leader rework (branch based templates and colony containers)
Diffstat (limited to 'src/openvic-simulation')
-rw-r--r-- | src/openvic-simulation/InstanceManager.cpp | 9 | ||||
-rw-r--r-- | src/openvic-simulation/InstanceManager.hpp | 2 | ||||
-rw-r--r-- | src/openvic-simulation/country/CountryInstance.cpp | 93 | ||||
-rw-r--r-- | src/openvic-simulation/country/CountryInstance.hpp | 15 | ||||
-rw-r--r-- | src/openvic-simulation/map/ProvinceInstance.cpp | 2 | ||||
-rw-r--r-- | src/openvic-simulation/map/ProvinceInstance.hpp | 34 | ||||
-rw-r--r-- | src/openvic-simulation/military/Deployment.cpp | 10 | ||||
-rw-r--r-- | src/openvic-simulation/military/Deployment.hpp | 9 | ||||
-rw-r--r-- | src/openvic-simulation/military/Leader.cpp | 4 | ||||
-rw-r--r-- | src/openvic-simulation/military/Leader.hpp | 47 | ||||
-rw-r--r-- | src/openvic-simulation/military/UnitInstance.cpp | 197 | ||||
-rw-r--r-- | src/openvic-simulation/military/UnitInstance.hpp | 174 | ||||
-rw-r--r-- | src/openvic-simulation/military/UnitInstanceGroup.cpp | 169 | ||||
-rw-r--r-- | src/openvic-simulation/military/UnitInstanceGroup.hpp | 220 | ||||
-rw-r--r-- | src/openvic-simulation/military/UnitType.cpp | 4 | ||||
-rw-r--r-- | src/openvic-simulation/military/UnitType.hpp | 39 |
16 files changed, 643 insertions, 385 deletions
diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index 7ee5fb8..c51dec7 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -36,10 +36,15 @@ void InstanceManager::update_gamestate() { return; } currently_updating_gamestate = true; + Logger::info("Update: ", today); + + // Update gamestate... map_instance.update_gamestate(today); + gamestate_updated(); gamestate_needs_update = false; + currently_updating_gamestate = false; } @@ -48,8 +53,12 @@ void InstanceManager::update_gamestate() { */ void InstanceManager::tick() { today++; + Logger::info("Tick: ", today); + + // Tick... map_instance.tick(today); + set_gamestate_needs_update(); } diff --git a/src/openvic-simulation/InstanceManager.hpp b/src/openvic-simulation/InstanceManager.hpp index 743fd71..9cf346a 100644 --- a/src/openvic-simulation/InstanceManager.hpp +++ b/src/openvic-simulation/InstanceManager.hpp @@ -7,7 +7,7 @@ #include "openvic-simulation/economy/GoodInstance.hpp" #include "openvic-simulation/map/MapInstance.hpp" #include "openvic-simulation/map/Mapmode.hpp" -#include "openvic-simulation/military/UnitInstance.hpp" +#include "openvic-simulation/military/UnitInstanceGroup.hpp" #include "openvic-simulation/misc/SimulationClock.hpp" #include "openvic-simulation/types/Date.hpp" diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 2debd8a..a4086a7 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -2,7 +2,8 @@ #include "openvic-simulation/country/CountryDefinition.hpp" #include "openvic-simulation/history/CountryHistory.hpp" -#include "openvic-simulation/military/UnitInstance.hpp" +#include "openvic-simulation/military/Deployment.hpp" +#include "openvic-simulation/military/UnitInstanceGroup.hpp" using namespace OpenVic; @@ -73,6 +74,89 @@ bool CountryInstance::remove_reform(Reform const* reform_to_remove) { return true; } +void CountryInstance::add_general(General&& new_general) { + generals.emplace(std::move(new_general)); +} + +bool CountryInstance::remove_general(General const* general_to_remove) { + const auto it = generals.get_iterator(general_to_remove); + if (it != generals.end()) { + generals.erase(it); + return true; + } + + Logger::error( + "Trying to remove non-existent general ", general_to_remove != nullptr ? general_to_remove->get_name() : "NULL", + " from country ", get_identifier() + ); + return false; +} + +void CountryInstance::add_admiral(Admiral&& new_admiral) { + admirals.emplace(std::move(new_admiral)); +} + +bool CountryInstance::remove_admiral(Admiral const* admiral_to_remove) { + const auto it = admirals.get_iterator(admiral_to_remove); + if (it != admirals.end()) { + admirals.erase(it); + return true; + } + + Logger::error( + "Trying to remove non-existent admiral ", admiral_to_remove != nullptr ? admiral_to_remove->get_name() : "NULL", + " from country ", get_identifier() + ); + return false; +} + +bool CountryInstance::add_leader(LeaderBase const& new_leader) { + using enum UnitType::branch_t; + + switch (new_leader.get_branch()) { + case LAND: + add_general({ new_leader }); + return true; + + case NAVAL: + add_admiral({ new_leader }); + return true; + + default: + Logger::error( + "Trying to add leader ", new_leader.get_name(), " to country ", get_identifier(), " with invalid branch ", + static_cast<uint32_t>(new_leader.get_branch()) + ); + return false; + } +} + +bool CountryInstance::remove_leader(LeaderBase const* leader_to_remove) { + if (leader_to_remove == nullptr) { + Logger::error("Trying to remvoe null leader from country ", get_identifier()); + return false; + } + + using enum UnitType::branch_t; + + switch (leader_to_remove->get_branch()) { + case LAND: + remove_general(static_cast<General const*>(leader_to_remove)); + return true; + + case NAVAL: + remove_admiral(static_cast<Admiral const*>(leader_to_remove)); + return true; + + default: + Logger::error( + "Trying to add leader ", leader_to_remove->get_name(), " to country ", get_identifier(), " with invalid branch ", + static_cast<uint32_t>(leader_to_remove->get_branch()) + ); + return false; + } +} + bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry) { if (entry == nullptr) { Logger::error("Trying to apply null country history to ", get_identifier()); @@ -135,7 +219,7 @@ bool CountryInstanceManager::apply_history_to_countries( CountryHistoryEntry const* oob_history_entry = nullptr; for (CountryHistoryEntry const* entry : history_map->get_entries_up_to(date)) { - country_instance.apply_history_to_country(entry); + ret &= country_instance.apply_history_to_country(entry); if (entry->get_inital_oob()) { oob_history_entry = entry; @@ -143,10 +227,13 @@ bool CountryInstanceManager::apply_history_to_countries( } if (oob_history_entry != nullptr) { - unit_instance_manager.generate_deployment( + ret &= unit_instance_manager.generate_deployment( map_instance, country_instance, *oob_history_entry->get_inital_oob() ); } + } else { + Logger::error("Country ", country_instance.get_identifier(), " has no history!"); + ret = false; } } } diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index 45924b2..3022b6a 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -2,6 +2,10 @@ #include <vector> +#include <plf_colony.h> + +#include "openvic-simulation/military/Leader.hpp" +#include "openvic-simulation/military/UnitInstanceGroup.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" @@ -42,6 +46,9 @@ namespace OpenVic { std::vector<Reform const*> 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 + plf::colony<General> PROPERTY(generals); + plf::colony<Admiral> PROPERTY(admirals); + CountryInstance(CountryDefinition const* new_country_definition); public: @@ -55,6 +62,14 @@ namespace OpenVic { bool add_reform(Reform const* new_reform); bool remove_reform(Reform const* reform_to_remove); + void add_general(General&& new_general); + bool remove_general(General const* general_to_remove); + void add_admiral(Admiral&& new_admiral); + bool remove_admiral(Admiral const* admiral_to_remove); + + bool add_leader(LeaderBase const& new_leader); + bool remove_leader(LeaderBase const* leader_to_remove); + bool apply_history_to_country(CountryHistoryEntry const* entry); }; diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index 61b1527..8bb5345 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -3,7 +3,7 @@ #include "openvic-simulation/country/CountryDefinition.hpp" #include "openvic-simulation/history/ProvinceHistory.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" -#include "openvic-simulation/military/UnitInstance.hpp" +#include "openvic-simulation/military/UnitInstanceGroup.hpp" using namespace OpenVic; diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp index ca7f149..a4f9e98 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -1,6 +1,8 @@ #pragma once #include "openvic-simulation/economy/BuildingInstance.hpp" +#include "openvic-simulation/military/UnitInstance.hpp" +#include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/pop/Pop.hpp" #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" @@ -14,8 +16,6 @@ namespace OpenVic { struct CountryDefinition; struct Crime; struct GoodDefinition; - struct ArmyInstance; - struct NavyInstance; struct Ideology; struct Culture; struct Religion; @@ -24,6 +24,15 @@ namespace OpenVic { struct IdeologyManager; struct IssueManager; + template<UnitType::branch_t> + struct UnitInstanceGroup; + + template<UnitType::branch_t> + struct UnitInstanceGroupBranched; + + using ArmyInstance = UnitInstanceGroupBranched<UnitType::branch_t::LAND>; + using NavyInstance = UnitInstanceGroupBranched<UnitType::branch_t::NAVAL>; + struct ProvinceInstance : HasIdentifierAndColour { friend struct MapInstance; @@ -83,6 +92,27 @@ namespace OpenVic { bool add_navy(NavyInstance& navy); bool remove_navy(NavyInstance& navy); + template<UnitType::branch_t Branch> + bool add_unit_instance_group(UnitInstanceGroup<Branch>& group) { + if constexpr (Branch == UnitType::branch_t::LAND) { + return add_army(static_cast<ArmyInstance&>(group)); + } else if constexpr (Branch == UnitType::branch_t::NAVAL) { + return add_navy(static_cast<NavyInstance&>(group)); + } else { + OpenVic::utility::unreachable(); + } + } + template<UnitType::branch_t Branch> + bool remove_unit_instance_group(UnitInstanceGroup<Branch>& group) { + if constexpr (Branch == UnitType::branch_t::LAND) { + return remove_army(static_cast<ArmyInstance&>(group)); + } else if constexpr (Branch == UnitType::branch_t::NAVAL) { + return remove_navy(static_cast<NavyInstance&>(group)); + } else { + OpenVic::utility::unreachable(); + } + } + bool setup(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 66f656d..9c89690 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -22,13 +22,13 @@ NavyDeployment::NavyDeployment( Deployment::Deployment( std::string_view new_path, std::vector<ArmyDeployment>&& new_armies, std::vector<NavyDeployment>&& new_navies, - std::vector<Leader>&& new_leaders + std::vector<LeaderBase>&& new_leaders ) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) }, leaders { std::move(new_leaders) } {} bool DeploymentManager::add_deployment( std::string_view path, std::vector<ArmyDeployment>&& armies, std::vector<NavyDeployment>&& navies, - std::vector<Leader>&& leaders + std::vector<LeaderBase>&& leaders ) { if (path.empty()) { Logger::error("Attemped to load order of battle with no path! Something is very wrong!"); @@ -68,7 +68,7 @@ bool DeploymentManager::load_oob_file( std::vector<ArmyDeployment> armies; std::vector<NavyDeployment> navies; - std::vector<Leader> leaders; + std::vector<LeaderBase> leaders; bool ret = expect_dictionary_keys_and_default( key_value_success_callback, // TODO: load SOI information @@ -109,8 +109,8 @@ bool DeploymentManager::load_oob_file( ret = false; } - leaders.emplace_back( - leader_name, leader_branch, leader_date, leader_personality, leader_background, leader_prestige, picture + leaders.push_back( + { leader_name, leader_branch, leader_date, leader_personality, leader_background, leader_prestige, picture } ); return ret; diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp index 8d3f016..9a1d2ee 100644 --- a/src/openvic-simulation/military/Deployment.hpp +++ b/src/openvic-simulation/military/Deployment.hpp @@ -10,7 +10,6 @@ namespace OpenVic { struct ProvinceDefinition; - struct RegimentType; struct RegimentDeployment { friend struct DeploymentManager; @@ -26,8 +25,6 @@ namespace OpenVic { RegimentDeployment(RegimentDeployment&&) = default; }; - struct ShipType; - struct ShipDeployment { friend struct DeploymentManager; @@ -79,11 +76,11 @@ namespace OpenVic { private: std::vector<ArmyDeployment> PROPERTY(armies); std::vector<NavyDeployment> PROPERTY(navies); - std::vector<Leader> PROPERTY(leaders); + std::vector<LeaderBase> PROPERTY(leaders); Deployment( std::string_view new_path, std::vector<ArmyDeployment>&& new_armies, std::vector<NavyDeployment>&& new_navies, - std::vector<Leader>&& new_leaders + std::vector<LeaderBase>&& new_leaders ); public: @@ -101,7 +98,7 @@ namespace OpenVic { public: bool add_deployment( std::string_view path, std::vector<ArmyDeployment>&& armies, std::vector<NavyDeployment>&& navies, - std::vector<Leader>&& leaders + std::vector<LeaderBase>&& leaders ); bool load_oob_file( diff --git a/src/openvic-simulation/military/Leader.cpp b/src/openvic-simulation/military/Leader.cpp index d6be36f..a86c044 100644 --- a/src/openvic-simulation/military/Leader.cpp +++ b/src/openvic-simulation/military/Leader.cpp @@ -2,8 +2,8 @@ using namespace OpenVic; -Leader::Leader( +LeaderBase::LeaderBase( 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 ) : name { new_name }, branch { new_branch }, date { new_date }, personality { new_personality }, background { new_background }, - prestige { new_prestige }, picture { new_picture } {}
\ No newline at end of file + prestige { new_prestige }, picture { new_picture } {} diff --git a/src/openvic-simulation/military/Leader.hpp b/src/openvic-simulation/military/Leader.hpp index 5995164..3f8603e 100644 --- a/src/openvic-simulation/military/Leader.hpp +++ b/src/openvic-simulation/military/Leader.hpp @@ -1,10 +1,20 @@ #pragma once +#include <string> +#include <string_view> + #include "openvic-simulation/military/LeaderTrait.hpp" #include "openvic-simulation/military/UnitType.hpp" +#include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/utility/Utility.hpp" namespace OpenVic { - struct Leader { + struct DeploymentManager; + + struct LeaderBase { + friend struct DeploymentManager; + private: std::string PROPERTY(name); UnitType::branch_t PROPERTY(branch); /* type in defines */ @@ -14,12 +24,39 @@ namespace OpenVic { fixed_point_t PROPERTY(prestige); std::string PROPERTY(picture); - public: - Leader( + private: + LeaderBase( 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; + protected: + LeaderBase(LeaderBase const&) = default; + + public: + LeaderBase(LeaderBase&&) = default; }; -}
\ No newline at end of file + + struct CountryInstance; + + template<UnitType::branch_t> + struct UnitInstanceGroup; + + template<UnitType::branch_t> + struct UnitInstanceGroupBranched; + + template<UnitType::branch_t Branch> + struct LeaderBranched : LeaderBase { + + friend struct CountryInstance; + friend bool UnitInstanceGroup<Branch>::set_leader(LeaderBranched<Branch>* new_leader); + + private: + UnitInstanceGroupBranched<Branch>* PROPERTY(unit_instance_group); + + LeaderBranched(LeaderBase const& leader_base) : LeaderBase { leader_base }, unit_instance_group { nullptr } {} + }; + + using General = LeaderBranched<UnitType::branch_t::LAND>; + using Admiral = LeaderBranched<UnitType::branch_t::NAVAL>; +} diff --git a/src/openvic-simulation/military/UnitInstance.cpp b/src/openvic-simulation/military/UnitInstance.cpp index ac9c194..1f49205 100644 --- a/src/openvic-simulation/military/UnitInstance.cpp +++ b/src/openvic-simulation/military/UnitInstance.cpp @@ -1,196 +1,11 @@ #include "UnitInstance.hpp" -#include <vector> - -#include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/map/MapInstance.hpp" -#include "openvic-simulation/map/ProvinceInstance.hpp" -#include "openvic-simulation/military/Deployment.hpp" - using namespace OpenVic; -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) - : UnitInstance { new_name, new_ship_type } {} - -MovementInfo::MovementInfo() : path {}, movement_progress {} {} - -//TODO: pathfinding logic -MovementInfo::MovementInfo(ProvinceInstance const* starting_province, ProvinceInstance const* target_province) - : path { starting_province, target_province }, movement_progress { 0 } {} - -ArmyInstance::ArmyInstance( - std::string_view new_name, - std::vector<RegimentInstance*>&& new_units, - Leader const* new_leader, - CountryInstance* new_country -) : UnitInstanceGroup { new_name, UnitType::branch_t::LAND, std::move(new_units), new_leader, new_country } {} - -void ArmyInstance::set_position(ProvinceInstance* 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<ShipInstance*>&& new_units, - Leader const* new_leader, - CountryInstance* new_country -) : UnitInstanceGroup { new_name, UnitType::branch_t::NAVAL, std::move(new_units), new_leader, new_country } {} - -void NavyInstance::set_position(ProvinceInstance* 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( - MapInstance& map_instance, 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<RegimentInstance*> 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_instance.get_province_instance_from_const(army_deployment.get_location())); - - return ret; -} - -bool UnitInstanceManager::generate_navy( - MapInstance& map_instance, 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<ShipInstance*> 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_instance.get_province_instance_from_const(navy_deployment.get_location())); - - return ret; -} - -bool UnitInstanceManager::generate_deployment( - MapInstance& map_instance, 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_instance, country, army_deployment); - } - - for (NavyDeployment const& navy_deployment : deployment->get_navies()) { - ret &= generate_navy(map_instance, country, navy_deployment); - } +UnitInstanceBranched<UnitType::branch_t::LAND>::UnitInstanceBranched( + std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop +) : UnitInstance { new_name, new_regiment_type }, pop { new_pop } {} - return ret; -} +UnitInstanceBranched<UnitType::branch_t::NAVAL>::UnitInstanceBranched( + std::string_view new_name, ShipType const& new_ship_type +) : UnitInstance { new_name, new_ship_type } {} diff --git a/src/openvic-simulation/military/UnitInstance.hpp b/src/openvic-simulation/military/UnitInstance.hpp index a1437ef..1480591 100644 --- a/src/openvic-simulation/military/UnitInstance.hpp +++ b/src/openvic-simulation/military/UnitInstance.hpp @@ -3,26 +3,26 @@ #include <concepts> #include <string> #include <string_view> -#include <vector> -#include "openvic-simulation/military/Leader.hpp" #include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { - template<std::derived_from<UnitType> T> + template<UnitType::branch_t Branch> struct UnitInstance { + using _UnitType = UnitTypeBranched<Branch>; + private: std::string PROPERTY(unit_name); - T const& PROPERTY(unit_type); //can't change + _UnitType const& PROPERTY(unit_type); fixed_point_t PROPERTY_RW(organisation); fixed_point_t PROPERTY_RW(morale); fixed_point_t PROPERTY_RW(strength); protected: - UnitInstance(std::string_view new_unit_name, T const& new_unit_type) : + UnitInstance(std::string_view new_unit_name, _UnitType const& new_unit_type) : unit_name { new_unit_name }, unit_type { new_unit_type }, organisation { new_unit_type.get_default_organisation() }, //TODO: modifiers @@ -39,172 +39,34 @@ namespace OpenVic { struct Pop; - struct RegimentInstance : UnitInstance<RegimentType> { - friend struct UnitInstanceManager; - - private: - Pop* PROPERTY(pop); - - RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop); - - public: - RegimentInstance(RegimentInstance&&) = default; - }; + template<UnitType::branch_t> + struct UnitInstanceBranched; - struct ShipInstance : UnitInstance<ShipType> { + template<> + struct UnitInstanceBranched<UnitType::branch_t::LAND> : UnitInstance<UnitType::branch_t::LAND> { friend struct UnitInstanceManager; private: - ShipInstance(std::string_view new_name, ShipType const& new_ship_type); - - public: - ShipInstance(ShipInstance&&) = default; - }; - - struct ProvinceInstance; - - struct MovementInfo { - private: - std::vector<ProvinceInstance const*> PROPERTY(path); - fixed_point_t PROPERTY(movement_progress); - - public: - MovementInfo(); - // contains/calls pathfinding logic - MovementInfo(ProvinceInstance const* starting_province, ProvinceInstance const* target_province); - }; - - struct CountryInstance; - - template<utility::is_derived_from_specialization_of<UnitInstance> I> - struct UnitInstanceGroup { - private: - std::string PROPERTY(name); - const UnitType::branch_t PROPERTY(branch); - std::vector<I*> PROPERTY(units); - Leader const* PROPERTY(leader); - - MovementInfo PROPERTY_REF(movement_info); + Pop* PROPERTY(pop); - protected: - ProvinceInstance* PROPERTY_ACCESS(position, protected); - CountryInstance* PROPERTY_ACCESS(country, protected); - - UnitInstanceGroup( - std::string_view new_name, - UnitType::branch_t new_branch, - std::vector<I*>&& new_units, - Leader const* new_leader, - CountryInstance* new_country - ) : name { new_name }, - branch { new_branch }, - units { std::move(new_units) }, - leader { new_leader }, - position { nullptr }, - country { new_country } {} + UnitInstanceBranched(std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop); 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<UnitType const*> 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(ProvinceInstance* new_position) = 0; + UnitInstanceBranched(UnitInstanceBranched&&) = default; }; - struct ArmyInstance : UnitInstanceGroup<RegimentInstance> { - friend struct UnitInstanceManager; - - private: - ArmyInstance( - std::string_view new_name, - std::vector<RegimentInstance*>&& new_units, - Leader const* new_leader, - CountryInstance* new_country - ); + using RegimentInstance = UnitInstanceBranched<UnitType::branch_t::LAND>; - public: - ArmyInstance(ArmyInstance&&) = default; - - void set_position(ProvinceInstance* new_position) override; - }; - - struct NavyInstance : UnitInstanceGroup<ShipInstance> { + template<> + struct UnitInstanceBranched<UnitType::branch_t::NAVAL> : UnitInstance<UnitType::branch_t::NAVAL> { friend struct UnitInstanceManager; private: - std::vector<ArmyInstance const*> PROPERTY(carried_armies); - - NavyInstance( - std::string_view new_name, - std::vector<ShipInstance*>&& new_ships, - Leader const* new_leader, - CountryInstance* new_country - ); + UnitInstanceBranched(std::string_view new_name, ShipType const& new_ship_type); public: - NavyInstance(NavyInstance&&) = default; - - void set_position(ProvinceInstance* new_position) override; + UnitInstanceBranched(UnitInstanceBranched&&) = default; }; - struct RegimentDeployment; - struct ShipDeployment; - struct MapInstance; - struct ArmyDeployment; - struct NavyDeployment; - struct Deployment; - - struct UnitInstanceManager { - private: - std::deque<RegimentInstance> PROPERTY(regiments); - std::deque<ShipInstance> PROPERTY(ships); - - std::deque<ArmyInstance> PROPERTY(armies); - std::deque<NavyInstance> PROPERTY(navies); - - bool generate_regiment(RegimentDeployment const& regiment_deployment, RegimentInstance*& regiment); - bool generate_ship(ShipDeployment const& ship_deployment, ShipInstance*& ship); - bool generate_army(MapInstance& map_instance, CountryInstance& country, ArmyDeployment const& army_deployment); - bool generate_navy(MapInstance& map_instance, CountryInstance& country, NavyDeployment const& navy_deployment); - - public: - bool generate_deployment(MapInstance& map_instance, CountryInstance& country, Deployment const* deployment); - }; + using ShipInstance = UnitInstanceBranched<UnitType::branch_t::NAVAL>; } diff --git a/src/openvic-simulation/military/UnitInstanceGroup.cpp b/src/openvic-simulation/military/UnitInstanceGroup.cpp new file mode 100644 index 0000000..46fb992 --- /dev/null +++ b/src/openvic-simulation/military/UnitInstanceGroup.cpp @@ -0,0 +1,169 @@ +#include "UnitInstanceGroup.hpp" + +#include <vector> + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/military/Deployment.hpp" + +using namespace OpenVic; + +MovementInfo::MovementInfo() : path {}, movement_progress {} {} + +//TODO: pathfinding logic +MovementInfo::MovementInfo(ProvinceInstance const* starting_province, ProvinceInstance const* target_province) + : path { starting_province, target_province }, movement_progress { 0 } {} + +UnitInstanceGroupBranched<UnitType::branch_t::LAND>::UnitInstanceGroupBranched( + std::string_view new_name, + std::vector<RegimentInstance*>&& new_units, + _Leader* new_leader, + CountryInstance* new_country +) : UnitInstanceGroup { new_name, std::move(new_units), new_leader, new_country } {} + +UnitInstanceGroupBranched<UnitType::branch_t::NAVAL>::UnitInstanceGroupBranched( + std::string_view new_name, + std::vector<ShipInstance*>&& new_units, + _Leader* new_leader, + CountryInstance* new_country +) : UnitInstanceGroup { new_name, std::move(new_units), new_leader, new_country } {} + +bool UnitInstanceManager::generate_regiment(RegimentDeployment const& regiment_deployment, RegimentInstance*& regiment) { + // TODO - get pop from Province regiment_deployment.get_home() + RegimentInstance& regiment_instance = + *regiments.insert({ regiment_deployment.get_name(), regiment_deployment.get_type(), nullptr }); + + regiment = ®iment_instance; + + return true; +} + +bool UnitInstanceManager::generate_ship(ShipDeployment const& ship_deployment, ShipInstance*& ship) { + ShipInstance& ship_instance = *ships.insert({ ship_deployment.get_name(), ship_deployment.get_type() }); + + ship = &ship_instance; + + return true; +} + +bool UnitInstanceManager::generate_army( + MapInstance& map_instance, 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<RegimentInstance*> 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; + } + + ArmyInstance& army_instance = *armies.insert({ army_deployment.get_name(), std::move(army_regiments), nullptr, &country }); + + army_instance.set_position(map_instance.get_province_instance_from_const(army_deployment.get_location())); + + return ret; +} + +bool UnitInstanceManager::generate_navy( + MapInstance& map_instance, 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<ShipInstance*> 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; + } + + NavyInstance& navy_intance = *navies.insert({ navy_deployment.get_name(), std::move(navy_ships), nullptr, &country }); + + navy_intance.set_position(map_instance.get_province_instance_from_const(navy_deployment.get_location())); + + return ret; +} + +bool UnitInstanceManager::generate_deployment( + MapInstance& map_instance, CountryInstance& country, Deployment const* deployment +) { + if (deployment == nullptr) { + Logger::error("Trying to generate null deployment for ", country.get_identifier()); + return false; + } + + bool ret = true; + + for (ArmyDeployment const& army_deployment : deployment->get_armies()) { + ret &= generate_army(map_instance, country, army_deployment); + } + + for (NavyDeployment const& navy_deployment : deployment->get_navies()) { + ret &= generate_navy(map_instance, country, navy_deployment); + } + + for (LeaderBase const& leader : deployment->get_leaders()) { + ret &= country.add_leader(leader); + } + + return ret; +} diff --git a/src/openvic-simulation/military/UnitInstanceGroup.hpp b/src/openvic-simulation/military/UnitInstanceGroup.hpp new file mode 100644 index 0000000..54aac3d --- /dev/null +++ b/src/openvic-simulation/military/UnitInstanceGroup.hpp @@ -0,0 +1,220 @@ +#pragma once + +#include <string> +#include <string_view> +#include <vector> + +#include <plf_colony.h> + +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/military/UnitInstance.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/utility/Getters.hpp" +#include "openvic-simulation/utility/Utility.hpp" + +namespace OpenVic { + struct ProvinceInstance; + + struct MovementInfo { + private: + std::vector<ProvinceInstance const*> PROPERTY(path); + fixed_point_t PROPERTY(movement_progress); + + public: + MovementInfo(); + // contains/calls pathfinding logic + MovementInfo(ProvinceInstance const* starting_province, ProvinceInstance const* target_province); + }; + + template<UnitType::branch_t> + struct LeaderBranched; + + struct CountryInstance; + + template<UnitType::branch_t> + struct UnitInstanceGroupBranched; + + template<UnitType::branch_t Branch> + struct UnitInstanceGroup { + using _UnitInstance = UnitInstanceBranched<Branch>; + using _Leader = LeaderBranched<Branch>; + + private: + std::string PROPERTY(name); + std::vector<_UnitInstance*> PROPERTY(units); + _Leader* PROPERTY(leader); + + MovementInfo PROPERTY_REF(movement_info); + + protected: + ProvinceInstance* PROPERTY_ACCESS(position, protected); + CountryInstance* PROPERTY_ACCESS(country, protected); + + UnitInstanceGroup( + std::string_view new_name, + std::vector<_UnitInstance*>&& new_units, + _Leader* new_leader, + CountryInstance* new_country + ) : name { new_name }, + units { std::move(new_units) }, + leader { nullptr }, + position { nullptr }, + country { new_country } { + set_leader(new_leader); + } + + 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](_UnitInstance 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<UnitType const*> weighted_unit_types; + + for (_UnitInstance 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; + } + + void set_position(ProvinceInstance* new_position) { + if (position != new_position) { + if (position != nullptr) { + position->remove_unit_instance_group(*this); + } + + position = new_position; + + if (position != nullptr) { + position->add_unit_instance_group(*this); + } + } + } + + bool set_leader(_Leader* new_leader) { + bool ret = true; + + if (leader != new_leader) { + if (leader != nullptr) { + if (leader->unit_instance_group == this) { + leader->unit_instance_group = nullptr; + } else { + Logger::error( + "Mismatch between leader and unit instance group: group ", name, " has leader ", + leader->get_name(), " but the leader has group ", leader->get_unit_instance_group() != nullptr + ? leader->get_unit_instance_group()->get_name() : "NULL" + ); + ret = false; + } + } + + leader = new_leader; + + if (leader != nullptr) { + if (leader->unit_instance_group != nullptr) { + if (leader->unit_instance_group != this) { + ret &= leader->unit_instance_group->set_leader(nullptr); + } else { + Logger::error("Leader ", leader->get_name(), " already leads group ", name, "!"); + ret = false; + } + } + + leader->unit_instance_group = static_cast<UnitInstanceGroupBranched<Branch>*>(this); + } + } + + return ret; + } + }; + + template<> + struct UnitInstanceGroupBranched<UnitType::branch_t::LAND> : UnitInstanceGroup<UnitType::branch_t::LAND> { + friend struct UnitInstanceManager; + + private: + UnitInstanceGroupBranched( + std::string_view new_name, + std::vector<RegimentInstance*>&& new_units, + _Leader* new_leader, + CountryInstance* new_country + ); + + public: + UnitInstanceGroupBranched(UnitInstanceGroupBranched&&) = default; + }; + + using ArmyInstance = UnitInstanceGroupBranched<UnitType::branch_t::LAND>; + + template<> + struct UnitInstanceGroupBranched<UnitType::branch_t::NAVAL> : UnitInstanceGroup<UnitType::branch_t::NAVAL> { + friend struct UnitInstanceManager; + + private: + std::vector<ArmyInstance const*> PROPERTY(carried_armies); + + UnitInstanceGroupBranched( + std::string_view new_name, + std::vector<ShipInstance*>&& new_ships, + _Leader* new_leader, + CountryInstance* new_country + ); + + public: + UnitInstanceGroupBranched(UnitInstanceGroupBranched&&) = default; + }; + + using NavyInstance = UnitInstanceGroupBranched<UnitType::branch_t::NAVAL>; + + struct RegimentDeployment; + struct ShipDeployment; + struct MapInstance; + struct ArmyDeployment; + struct NavyDeployment; + struct Deployment; + + struct UnitInstanceManager { + private: + plf::colony<RegimentInstance> PROPERTY(regiments); + plf::colony<ShipInstance> PROPERTY(ships); + + plf::colony<ArmyInstance> PROPERTY(armies); + plf::colony<NavyInstance> PROPERTY(navies); + + bool generate_regiment(RegimentDeployment const& regiment_deployment, RegimentInstance*& regiment); + bool generate_ship(ShipDeployment const& ship_deployment, ShipInstance*& ship); + bool generate_army(MapInstance& map_instance, CountryInstance& country, ArmyDeployment const& army_deployment); + bool generate_navy(MapInstance& map_instance, CountryInstance& country, NavyDeployment const& navy_deployment); + + public: + bool generate_deployment(MapInstance& map_instance, CountryInstance& country, Deployment const* deployment); + }; +} diff --git a/src/openvic-simulation/military/UnitType.cpp b/src/openvic-simulation/military/UnitType.cpp index 45ce5d9..a7769d3 100644 --- a/src/openvic-simulation/military/UnitType.cpp +++ b/src/openvic-simulation/military/UnitType.cpp @@ -30,7 +30,7 @@ UnitType::UnitType( supply_cost { std::move(unit_args.supply_cost) }, terrain_modifiers { std::move(unit_args.terrain_modifiers) } {} -RegimentType::RegimentType( +UnitTypeBranched<LAND>::UnitTypeBranched( std::string_view new_identifier, unit_type_args_t& unit_args, regiment_type_args_t const& regiment_type_args ) : UnitType { new_identifier, LAND, unit_args }, allowed_cultures { regiment_type_args.allowed_cultures }, @@ -45,7 +45,7 @@ RegimentType::RegimentType( maneuver { regiment_type_args.maneuver }, siege { regiment_type_args.siege } {} -ShipType::ShipType( +UnitTypeBranched<NAVAL>::UnitTypeBranched( std::string_view new_identifier, unit_type_args_t& unit_args, ship_type_args_t const& ship_type_args ) : UnitType { new_identifier, NAVAL, unit_args }, naval_icon { ship_type_args.naval_icon }, diff --git a/src/openvic-simulation/military/UnitType.hpp b/src/openvic-simulation/military/UnitType.hpp index 9e7ecdc..095a759 100644 --- a/src/openvic-simulation/military/UnitType.hpp +++ b/src/openvic-simulation/military/UnitType.hpp @@ -72,7 +72,11 @@ namespace OpenVic { UnitType(UnitType&&) = default; }; - struct RegimentType : UnitType { + template<UnitType::branch_t> + struct UnitTypeBranched; + + template<> + struct UnitTypeBranched<UnitType::branch_t::LAND> : UnitType { friend struct UnitTypeManager; enum struct allowed_cultures_t { ALL_CULTURES, ACCEPTED_CULTURES, PRIMARY_CULTURE }; @@ -102,17 +106,22 @@ namespace OpenVic { const fixed_point_t PROPERTY(maneuver); const fixed_point_t PROPERTY(siege); - RegimentType(std::string_view new_identifier, unit_type_args_t& unit_args, regiment_type_args_t const& regiment_type_args); + UnitTypeBranched( + std::string_view new_identifier, unit_type_args_t& unit_args, regiment_type_args_t const& regiment_type_args + ); public: - RegimentType(RegimentType&&) = default; + UnitTypeBranched(UnitTypeBranched&&) = default; }; - struct ShipType : UnitType { + using RegimentType = UnitTypeBranched<UnitType::branch_t::LAND>; + + template<> + struct UnitTypeBranched<UnitType::branch_t::NAVAL> : UnitType { friend struct UnitTypeManager; struct ship_type_args_t { - UnitType::icon_t naval_icon = 0; + icon_t naval_icon = 0; bool sail = false, transport = false, capital = false, build_overseas = false; uint32_t min_port_level = 0; int32_t limit_per_port = 0; @@ -140,12 +149,14 @@ namespace OpenVic { const fixed_point_t PROPERTY(evasion); const fixed_point_t PROPERTY(torpedo_attack); - ShipType(std::string_view new_identifier, unit_type_args_t& unit_args, ship_type_args_t const& ship_type_args); + UnitTypeBranched(std::string_view new_identifier, unit_type_args_t& unit_args, ship_type_args_t const& ship_type_args); public: - ShipType(ShipType&&) = default; + UnitTypeBranched(UnitTypeBranched&&) = default; }; + using ShipType = UnitTypeBranched<UnitType::branch_t::NAVAL>; + struct UnitTypeManager { private: IdentifierPointerRegistry<UnitType> IDENTIFIER_REGISTRY(unit_type); @@ -157,15 +168,21 @@ namespace OpenVic { void lock_all_unit_types(); bool add_regiment_type( - std::string_view identifier, UnitType::unit_type_args_t& unit_args, RegimentType::regiment_type_args_t const& regiment_type_args + std::string_view identifier, UnitType::unit_type_args_t& unit_args, + RegimentType::regiment_type_args_t const& regiment_type_args ); bool add_ship_type( - std::string_view identifier, UnitType::unit_type_args_t& unit_args, ShipType::ship_type_args_t const& ship_type_args + std::string_view identifier, UnitType::unit_type_args_t& unit_args, + ShipType::ship_type_args_t const& ship_type_args ); - static NodeTools::Callback<std::string_view> auto expect_branch_str(NodeTools::Callback<UnitType::branch_t> auto callback) { + static NodeTools::Callback<std::string_view> auto expect_branch_str( + NodeTools::Callback<UnitType::branch_t> auto callback + ) { using enum UnitType::branch_t; - static const string_map_t<UnitType::branch_t> branch_map { { "land", LAND }, { "naval", NAVAL }, { "sea", NAVAL } }; + static const string_map_t<UnitType::branch_t> branch_map { + { "land", LAND }, { "naval", NAVAL }, { "sea", NAVAL } + }; return NodeTools::expect_mapped_string(branch_map, callback); } static NodeTools::NodeCallback auto expect_branch_identifier(NodeTools::Callback<UnitType::branch_t> auto callback) { |