From 023860d53a90774dcdba84ad7c0dca3c944c9a49 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Mon, 12 Feb 2024 12:15:04 +0100 Subject: feat: unit instance type definition, renamed existing structs for consistency --- src/openvic-simulation/military/Deployment.cpp | 44 ++- src/openvic-simulation/military/Deployment.hpp | 70 ++--- src/openvic-simulation/military/Leader.cpp | 9 + src/openvic-simulation/military/Leader.hpp | 23 ++ .../military/MilitaryManager.hpp | 4 +- src/openvic-simulation/military/Unit.cpp | 343 --------------------- src/openvic-simulation/military/Unit.hpp | 181 ----------- src/openvic-simulation/military/UnitInstance.cpp | 31 ++ src/openvic-simulation/military/UnitInstance.hpp | 110 +++++++ src/openvic-simulation/military/UnitType.cpp | 343 +++++++++++++++++++++ src/openvic-simulation/military/UnitType.hpp | 181 +++++++++++ 11 files changed, 743 insertions(+), 596 deletions(-) create mode 100644 src/openvic-simulation/military/Leader.cpp create mode 100644 src/openvic-simulation/military/Leader.hpp delete mode 100644 src/openvic-simulation/military/Unit.cpp delete mode 100644 src/openvic-simulation/military/Unit.hpp create mode 100644 src/openvic-simulation/military/UnitInstance.cpp create mode 100644 src/openvic-simulation/military/UnitInstance.hpp create mode 100644 src/openvic-simulation/military/UnitType.cpp create mode 100644 src/openvic-simulation/military/UnitType.hpp (limited to 'src/openvic-simulation/military') diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index 4e2693d..903df3a 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -5,31 +5,25 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -Leader::Leader( - std::string_view new_name, Unit::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 } {} - -Regiment::Regiment(std::string_view new_name, LandUnit 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 } {} -Ship::Ship(std::string_view new_name, NavalUnit 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 } {} -Army::Army(std::string_view new_name, Province const* new_location, std::vector&& 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) } {} -Navy::Navy(std::string_view new_name, Province const* new_location, std::vector&& 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, + std::string_view new_path, std::vector&& new_armies, std::vector&& new_navies, std::vector&& new_leaders ) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) }, leaders { std::move(new_leaders) } {} 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!"); @@ -64,14 +58,14 @@ bool DeploymentManager::load_oob_file( return true; } } - std::vector armies; - std::vector navies; + std::vector armies; + std::vector navies; std::vector leaders; bool ret = expect_dictionary_keys_and_default( key_value_success_callback, // TODO: load SOI information "leader", ZERO_OR_MORE, [&leaders, &game_manager](ast::NodeCPtr node) -> bool { std::string_view leader_name {}; - Unit::branch_t leader_branch = Unit::branch_t::INVALID_BRANCH; + UnitType::branch_t leader_branch = UnitType::branch_t::INVALID_BRANCH; Date leader_date {}; LeaderTrait const* leader_personality = nullptr; LeaderTrait const* leader_background = nullptr; @@ -81,7 +75,7 @@ bool DeploymentManager::load_oob_file( bool ret = expect_dictionary_keys( "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(leader_name)), "date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(leader_date)), - "type", ONE_EXACTLY, UnitManager::expect_branch_identifier(assign_variable_callback(leader_branch)), + "type", ONE_EXACTLY, UnitTypeManager::expect_branch_identifier(assign_variable_callback(leader_branch)), "personality", ONE_EXACTLY, game_manager.get_military_manager().get_leader_trait_manager().expect_leader_trait_identifier_or_string( assign_variable_callback_pointer(leader_personality) @@ -116,7 +110,7 @@ bool DeploymentManager::load_oob_file( "army", ZERO_OR_MORE, [&armies, &game_manager](ast::NodeCPtr node) -> bool { std::string_view army_name {}; Province const* army_location = nullptr; - std::vector army_regiments {}; + std::vector army_regiments {}; const bool ret = expect_dictionary_keys( "name", ONE_EXACTLY, expect_string(assign_variable_callback(army_name)), @@ -124,17 +118,17 @@ bool DeploymentManager::load_oob_file( game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(army_location)), "regiment", ONE_OR_MORE, [&game_manager, &army_regiments](ast::NodeCPtr node) -> bool { std::string_view regiment_name {}; - LandUnit const* regiment_type = nullptr; + 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_manager() - .expect_land_unit_identifier(assign_variable_callback_pointer(regiment_type)), + "type", ONE_EXACTLY, game_manager.get_military_manager().get_unit_type_manager() + .expect_regiment_type_identifier(assign_variable_callback_pointer(regiment_type)), "home", ZERO_OR_ONE, game_manager.get_map() .expect_province_identifier(assign_variable_callback_pointer(regiment_home)) )(node); if (regiment_home == nullptr) { - Logger::warning("Regiment ", regiment_name, " has no home province!"); + Logger::warning("RegimentDeployment ", regiment_name, " has no home province!"); } army_regiments.emplace_back(regiment_name, regiment_type, regiment_home); return ret; @@ -148,7 +142,7 @@ bool DeploymentManager::load_oob_file( "navy", ZERO_OR_MORE, [&navies, &game_manager](ast::NodeCPtr node) -> bool { std::string_view navy_name {}; Province const* navy_location = nullptr; - std::vector navy_ships {}; + std::vector navy_ships {}; const bool ret = expect_dictionary_keys( "name", ONE_EXACTLY, expect_string(assign_variable_callback(navy_name)), @@ -156,11 +150,11 @@ bool DeploymentManager::load_oob_file( game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(navy_location)), "ship", ONE_OR_MORE, [&game_manager, &navy_ships](ast::NodeCPtr node) -> bool { std::string_view ship_name {}; - NavalUnit const* ship_type = nullptr; + 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_manager() - .expect_naval_unit_identifier(assign_variable_callback_pointer(ship_type)) + "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); return ret; diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp index e774108..742914d 100644 --- a/src/openvic-simulation/military/Deployment.hpp +++ b/src/openvic-simulation/military/Deployment.hpp @@ -5,82 +5,62 @@ #include #include "openvic-simulation/dataloader/Dataloader.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/map/Province.hpp" -#include "openvic-simulation/military/LeaderTrait.hpp" -#include "openvic-simulation/military/Unit.hpp" -#include "openvic-simulation/types/Date.hpp" -#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/military/Leader.hpp" +#include "openvic-simulation/military/UnitType.hpp" namespace OpenVic { - struct Leader { + struct RegimentDeployment { private: - std::string PROPERTY(name); - Unit::branch_t PROPERTY(branch); /* type in defines */ - Date PROPERTY(date); - LeaderTrait const* PROPERTY(personality); - LeaderTrait const* PROPERTY(background); - fixed_point_t PROPERTY(prestige); - std::string PROPERTY(picture); - - public: - Leader( - std::string_view new_name, Unit::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 - ); - }; - - struct Regiment { - private: - std::string PROPERTY(name); - LandUnit const* PROPERTY(type); + std::string PROPERTY(name); + RegimentType const* PROPERTY(type); Province const* PROPERTY(home); public: - Regiment(std::string_view new_name, LandUnit const* new_type, Province const* new_home); + RegimentDeployment(std::string_view new_name, RegimentType const* new_type, Province const* new_home); }; - struct Ship { + struct ShipDeployment { private: - std::string PROPERTY(name); - NavalUnit const* PROPERTY(type); + std::string PROPERTY(name); + ShipType const* PROPERTY(type); public: - Ship(std::string_view new_name, NavalUnit const* new_type); + ShipDeployment(std::string_view new_name, ShipType const* new_type); }; - struct Army { + struct ArmyDeployment { private: - std::string PROPERTY(name); - Province const* PROPERTY(location); - std::vector PROPERTY(regiments); + std::string PROPERTY(name); + Province const* PROPERTY(location); + std::vector PROPERTY(regiments); public: - Army(std::string_view new_name, Province const* new_location, std::vector&& new_regiments); + ArmyDeployment(std::string_view new_name, Province const* new_location, std::vector&& new_regiments); }; - struct Navy { + struct NavyDeployment { private: - std::string PROPERTY(name); - Province const* PROPERTY(location); - std::vector PROPERTY(ships); + std::string PROPERTY(name); + Province const* PROPERTY(location); + std::vector PROPERTY(ships); public: - Navy(std::string_view new_name, Province const* new_location, std::vector&& new_ships); + NavyDeployment(std::string_view new_name, Province const* new_location, std::vector&& new_ships); }; struct Deployment : HasIdentifier { friend std::unique_ptr std::make_unique( - std::string_view&&, std::vector&&, std::vector&&, std::vector&& + std::string_view&&, std::vector&&, std::vector&&, std::vector&& ); private: - std::vector PROPERTY(armies); - std::vector PROPERTY(navies); + std::vector PROPERTY(armies); + std::vector PROPERTY(navies); std::vector PROPERTY(leaders); Deployment( - std::string_view new_path, std::vector&& new_armies, std::vector&& new_navies, + std::string_view new_path, std::vector&& new_armies, std::vector&& new_navies, std::vector&& new_leaders ); @@ -95,7 +75,7 @@ namespace OpenVic { 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( diff --git a/src/openvic-simulation/military/Leader.cpp b/src/openvic-simulation/military/Leader.cpp new file mode 100644 index 0000000..d6be36f --- /dev/null +++ b/src/openvic-simulation/military/Leader.cpp @@ -0,0 +1,9 @@ +#include "Leader.hpp" + +using namespace OpenVic; + +Leader::Leader( + 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 diff --git a/src/openvic-simulation/military/Leader.hpp b/src/openvic-simulation/military/Leader.hpp new file mode 100644 index 0000000..180fd39 --- /dev/null +++ b/src/openvic-simulation/military/Leader.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "openvic-simulation/military/LeaderTrait.hpp" +#include "openvic-simulation/military/UnitType.hpp" + +namespace OpenVic { + struct Leader { + private: + std::string PROPERTY(name); + UnitType::branch_t PROPERTY(branch); /* type in defines */ + Date PROPERTY(date); + LeaderTrait const* PROPERTY(personality); + LeaderTrait const* PROPERTY(background); + fixed_point_t PROPERTY(prestige); + std::string PROPERTY(picture); + + public: + Leader( + 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 + ); + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/military/MilitaryManager.hpp b/src/openvic-simulation/military/MilitaryManager.hpp index 2efa3ea..343d789 100644 --- a/src/openvic-simulation/military/MilitaryManager.hpp +++ b/src/openvic-simulation/military/MilitaryManager.hpp @@ -2,13 +2,13 @@ #include "openvic-simulation/military/Deployment.hpp" #include "openvic-simulation/military/LeaderTrait.hpp" -#include "openvic-simulation/military/Unit.hpp" +#include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/military/Wargoal.hpp" namespace OpenVic { struct MilitaryManager { private: - UnitManager PROPERTY_REF(unit_manager); + UnitTypeManager PROPERTY_REF(unit_type_manager); LeaderTraitManager PROPERTY_REF(leader_trait_manager); DeploymentManager PROPERTY_REF(deployment_manager); WargoalTypeManager PROPERTY_REF(wargoal_type_manager); diff --git a/src/openvic-simulation/military/Unit.cpp b/src/openvic-simulation/military/Unit.cpp deleted file mode 100644 index 54fc763..0000000 --- a/src/openvic-simulation/military/Unit.cpp +++ /dev/null @@ -1,343 +0,0 @@ -#include "Unit.hpp" - -#include "openvic-simulation/map/TerrainType.hpp" - -using namespace OpenVic; -using namespace OpenVic::NodeTools; - -using enum Unit::branch_t; -using enum Unit::unit_type_t; - -Unit::Unit( - std::string_view new_identifier, branch_t new_branch, unit_args_t& unit_args -) : HasIdentifier { new_identifier }, - branch { new_branch }, - icon { unit_args.icon }, - sprite { unit_args.sprite }, - active { unit_args.active }, - unit_type { unit_args.unit_type }, - floating_flag { unit_args.floating_flag }, - priority { unit_args.priority }, - max_strength { unit_args.max_strength }, - default_organisation { unit_args.default_organisation }, - maximum_speed { unit_args.maximum_speed }, - weighted_value { unit_args.weighted_value }, - move_sound { unit_args.move_sound }, - select_sound { unit_args.select_sound }, - build_time { unit_args.build_time }, - build_cost { std::move(unit_args.build_cost) }, - supply_consumption { unit_args.supply_consumption }, - supply_cost { std::move(unit_args.supply_cost) }, - terrain_modifiers { std::move(unit_args.terrain_modifiers) } {} - -LandUnit::LandUnit( - std::string_view new_identifier, unit_args_t& unit_args, land_unit_args_t const& land_unit_args -) : Unit { new_identifier, LAND, unit_args }, - allowed_cultures { land_unit_args.allowed_cultures }, - sprite_override { land_unit_args.sprite_override }, - sprite_mount { land_unit_args.sprite_mount }, - sprite_mount_attach_node { land_unit_args.sprite_mount_attach_node }, - reconnaissance { land_unit_args.reconnaissance }, - attack { land_unit_args.attack }, - defence { land_unit_args.defence }, - discipline { land_unit_args.discipline }, - support { land_unit_args.support }, - maneuver { land_unit_args.maneuver }, - siege { land_unit_args.siege } {} - -NavalUnit::NavalUnit( - std::string_view new_identifier, unit_args_t& unit_args, naval_unit_args_t const& naval_unit_args -) : Unit { new_identifier, NAVAL, unit_args }, - naval_icon { naval_unit_args.naval_icon }, - sail { naval_unit_args.sail }, - transport { naval_unit_args.transport }, - capital { naval_unit_args.capital }, - colonial_points { naval_unit_args.colonial_points }, - build_overseas { naval_unit_args.build_overseas }, - min_port_level { naval_unit_args.min_port_level }, - limit_per_port { naval_unit_args.limit_per_port }, - supply_consumption_score { naval_unit_args.supply_consumption_score }, - hull { naval_unit_args.hull }, - gun_power { naval_unit_args.gun_power }, - fire_range { naval_unit_args.fire_range }, - evasion { naval_unit_args.evasion }, - torpedo_attack { naval_unit_args.torpedo_attack } {} - -void UnitManager::reserve_all_units(size_t size) { - reserve_more_units(size); - reserve_more_land_units(size); - reserve_more_naval_units(size); -} - -void UnitManager::lock_all_units() { - units.lock(); - land_units.lock(); - naval_units.lock(); -} - -static bool _check_shared_parameters(std::string_view identifier, Unit::unit_args_t const& unit_args) { - if (identifier.empty()) { - Logger::error("Invalid unit identifier - empty!"); - return false; - } - - if (unit_args.icon <= 0) { - Logger::error("Invalid icon for unit ", identifier, " - ", unit_args.icon, " (must be positive)"); - return false; - } - - if (unit_args.unit_type == INVALID_UNIT_TYPE) { - Logger::error("Invalid unit type for unit ", identifier, "!"); - return false; - } - - // TODO check that sprite, move_sound and select_sound exist - - return true; -} - -bool UnitManager::add_land_unit( - std::string_view identifier, Unit::unit_args_t& unit_args, LandUnit::land_unit_args_t const& land_unit_args -) { - if (!_check_shared_parameters(identifier, unit_args)) { - return false; - } - - // TODO check that sprite_override, sprite_mount, and sprite_mount_attach_node exist - - if (naval_units.has_identifier(identifier)) { - Logger::error("Land unit ", identifier, " already exists as a naval unit!"); - return false; - } - - bool ret = land_units.add_item({ identifier, unit_args, std::move(land_unit_args) }); - if (ret) { - ret &= units.add_item(&land_units.get_items().back()); - } - return ret; -} - -bool UnitManager::add_naval_unit( - std::string_view identifier, Unit::unit_args_t& unit_args, NavalUnit::naval_unit_args_t const& naval_unit_args -) { - if (!_check_shared_parameters(identifier, unit_args)) { - return false; - } - - if (naval_unit_args.naval_icon <= 0) { - Logger::error("Invalid naval icon identifier - ", naval_unit_args.naval_icon, " (must be positive)"); - return false; - } - - if (naval_unit_args.supply_consumption_score <= 0) { - Logger::warning("Supply consumption score for ", identifier, " is not positive!"); - } - - if (land_units.has_identifier(identifier)) { - Logger::error("Naval unit ", identifier, " already exists as a land unit!"); - return false; - } - - bool ret = naval_units.add_item({ identifier, unit_args, naval_unit_args }); - if (ret) { - ret &= units.add_item(&naval_units.get_items().back()); - } - return ret; -} - -bool UnitManager::load_unit_file( - GoodManager const& good_manager, TerrainTypeManager const& terrain_type_manager, ModifierManager const& modifier_manager, - ast::NodeCPtr root -) { - return expect_dictionary([this, &good_manager, &terrain_type_manager, &modifier_manager]( - std::string_view key, ast::NodeCPtr value - ) -> bool { - - Unit::branch_t branch = INVALID_BRANCH; - - bool ret = expect_key("type", expect_branch_identifier(assign_variable_callback(branch)))(value); - - /* We shouldn't just check ret as it can be false even if branch was successfully parsed, - * but more than one instance of the key was found. */ - if (branch != LAND && branch != NAVAL) { - Logger::error("Failed to read branch for unit: ", key); - return false; - } - - Unit::unit_args_t unit_args {}; - - static const string_map_t unit_type_map { - { "infantry", INFANTRY }, - { "cavalry", CAVALRY }, - { "support", SUPPORT }, - { "special", SPECIAL }, - { "big_ship", BIG_SHIP }, - { "light_ship", LIGHT_SHIP }, - { "transport", TRANSPORT } - }; - - key_map_t key_map {}; - /* Shared dictionary entries */ - ret &= add_key_map_entries(key_map, - "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.icon)), - "type", ONE_EXACTLY, success_callback, /* Already loaded above using expect_key */ - "sprite", ONE_EXACTLY, expect_identifier(assign_variable_callback(unit_args.sprite)), - "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(unit_args.active)), - "unit_type", ONE_EXACTLY, - expect_identifier(expect_mapped_string(unit_type_map, assign_variable_callback(unit_args.unit_type))), - "floating_flag", ONE_EXACTLY, expect_bool(assign_variable_callback(unit_args.floating_flag)), - "priority", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.priority)), - "max_strength", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.max_strength)), - "default_organisation", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.default_organisation)), - "maximum_speed", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.maximum_speed)), - "weighted_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.weighted_value)), - "move_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.move_sound)), - "select_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.select_sound)), - "build_time", ONE_EXACTLY, expect_days(assign_variable_callback(unit_args.build_time)), - "build_cost", ONE_EXACTLY, good_manager.expect_good_decimal_map(move_variable_callback(unit_args.build_cost)), - "supply_consumption", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.supply_consumption)), - "supply_cost", ONE_EXACTLY, good_manager.expect_good_decimal_map(move_variable_callback(unit_args.supply_cost)) - ); - - const auto add_terrain_modifier = [&unit_args, &terrain_type_manager, &modifier_manager]( - std::string_view default_key, ast::NodeCPtr default_value - ) -> bool { - TerrainType const* terrain_type = terrain_type_manager.get_terrain_type_by_identifier(default_key); - if (terrain_type != nullptr) { - // TODO - restrict what modifier effects can be used here - return modifier_manager.expect_modifier_value( - map_callback(unit_args.terrain_modifiers, terrain_type) - )(default_value); - } - return key_value_invalid_callback(default_key, default_value); - }; - - switch (branch) { - case LAND: { - LandUnit::land_unit_args_t land_unit_args {}; - bool is_restricted_to_primary_culture = false; - bool is_restricted_to_accepted_cultures = false; - - ret &= add_key_map_entries(key_map, - "primary_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_primary_culture)), - "accepted_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_accepted_cultures)), - "sprite_override", ZERO_OR_ONE, expect_identifier(assign_variable_callback(land_unit_args.sprite_override)), - "sprite_mount", ZERO_OR_ONE, expect_identifier(assign_variable_callback(land_unit_args.sprite_mount)), - "sprite_mount_attach_node", ZERO_OR_ONE, - expect_identifier(assign_variable_callback(land_unit_args.sprite_mount_attach_node)), - "reconnaissance", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(land_unit_args.reconnaissance)), - "attack", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(land_unit_args.attack)), - "defence", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(land_unit_args.defence)), - "discipline", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(land_unit_args.discipline)), - "support", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(land_unit_args.support)), - "maneuver", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(land_unit_args.maneuver)), - "siege", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(land_unit_args.siege)) - ); - - if (is_restricted_to_accepted_cultures) { - land_unit_args.allowed_cultures = LandUnit::allowed_cultures_t::ACCEPTED_CULTURES; - } else if (is_restricted_to_primary_culture) { - land_unit_args.allowed_cultures = LandUnit::allowed_cultures_t::PRIMARY_CULTURE; - } else { - land_unit_args.allowed_cultures = LandUnit::allowed_cultures_t::ALL_CULTURES; - } - - ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier)(value); - - ret &= add_land_unit(key, unit_args, land_unit_args); - - return ret; - } - case NAVAL: { - NavalUnit::naval_unit_args_t naval_unit_args {}; - - ret &= add_key_map_entries(key_map, - "naval_icon", ONE_EXACTLY, expect_uint(assign_variable_callback(naval_unit_args.naval_icon)), - "sail", ZERO_OR_ONE, expect_bool(assign_variable_callback(naval_unit_args.sail)), - "transport", ZERO_OR_ONE, expect_bool(assign_variable_callback(naval_unit_args.transport)), - "capital", ZERO_OR_ONE, expect_bool(assign_variable_callback(naval_unit_args.capital)), - "colonial_points", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(naval_unit_args.colonial_points)), - "can_build_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(naval_unit_args.build_overseas)), - "min_port_level", ONE_EXACTLY, expect_uint(assign_variable_callback(naval_unit_args.min_port_level)), - "limit_per_port", ONE_EXACTLY, expect_int(assign_variable_callback(naval_unit_args.limit_per_port)), - "supply_consumption_score", ZERO_OR_ONE, - expect_fixed_point(assign_variable_callback(naval_unit_args.supply_consumption_score)), - "hull", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(naval_unit_args.hull)), - "gun_power", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(naval_unit_args.gun_power)), - "fire_range", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(naval_unit_args.fire_range)), - "evasion", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(naval_unit_args.evasion)), - "torpedo_attack", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(naval_unit_args.torpedo_attack)) - ); - - ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier)(value); - - ret &= add_naval_unit(key, unit_args, naval_unit_args); - - return ret; - } - default: - /* Unreachable - an earlier check terminates the function if branch isn't LAND or NAVAL. */ - Logger::error("Unknown branch for unit ", key, ": ", static_cast(branch)); - return false; - } - })(root); -} - -bool UnitManager::generate_modifiers(ModifierManager& modifier_manager) const { - bool ret = true; - - const auto generate_stat_modifiers = [&modifier_manager, &ret](std::string_view identifier, Unit::branch_t branch) -> void { - const auto stat_modifier = [&modifier_manager, &ret, &identifier]( - std::string_view suffix, bool is_positive_good, ModifierEffect::format_t format - ) -> void { - ret &= modifier_manager.add_modifier_effect( - ModifierManager::get_flat_identifier(identifier, suffix), is_positive_good, format - ); - }; - - using enum ModifierEffect::format_t; - - ret &= modifier_manager.register_complex_modifier(identifier); - - stat_modifier("attack", true, RAW_DECIMAL); - stat_modifier("defence", true, RAW_DECIMAL); - stat_modifier("default_organisation", true, RAW_DECIMAL); - stat_modifier("maximum_speed", true, RAW_DECIMAL); - stat_modifier("build_time", false, INT); - stat_modifier("supply_consumption", false, PROPORTION_DECIMAL); - - switch (branch) { - case LAND: - stat_modifier("reconnaissance", true, RAW_DECIMAL); - stat_modifier("discipline", true, PROPORTION_DECIMAL); - stat_modifier("support", true, PROPORTION_DECIMAL); - stat_modifier("maneuver", true, INT); - stat_modifier("siege", true, RAW_DECIMAL); - break; - case NAVAL: - stat_modifier("colonial_points", true, INT); - stat_modifier("supply_consumption_score", false, INT); - stat_modifier("hull", true, RAW_DECIMAL); - stat_modifier("gun_power", true, RAW_DECIMAL); - stat_modifier("fire_range", true, RAW_DECIMAL); - stat_modifier("evasion", true, PROPORTION_DECIMAL); - stat_modifier("torpedo_attack", true, RAW_DECIMAL); - break; - default: - /* Unreachable - units are only added via add_land_unit or add_naval_unit which set branch to LAND or NAVAL. */ - Logger::error("Invalid branch for unit ", identifier, ": ", static_cast(branch)); - } - }; - - generate_stat_modifiers("army_base", LAND); - for (LandUnit const& land_unit : get_land_units()) { - generate_stat_modifiers(land_unit.get_identifier(), LAND); - } - - generate_stat_modifiers("navy_base", NAVAL); - for (NavalUnit const& naval_unit : get_naval_units()) { - generate_stat_modifiers(naval_unit.get_identifier(), NAVAL); - } - - return ret; -} diff --git a/src/openvic-simulation/military/Unit.hpp b/src/openvic-simulation/military/Unit.hpp deleted file mode 100644 index d1ee3e3..0000000 --- a/src/openvic-simulation/military/Unit.hpp +++ /dev/null @@ -1,181 +0,0 @@ -#pragma once - -#include -#include - -#include "openvic-simulation/misc/Modifier.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/economy/Good.hpp" -#include "openvic-simulation/types/Date.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" - -namespace OpenVic { - struct TerrainType; - struct TerrainTypeManager; - - struct Unit : HasIdentifier { - using icon_t = uint32_t; - using terrain_modifiers_t = ordered_map; - - enum struct branch_t : uint8_t { INVALID_BRANCH, LAND, NAVAL }; - enum struct unit_type_t : uint8_t { - INVALID_UNIT_TYPE, INFANTRY, CAVALRY, SUPPORT, SPECIAL, BIG_SHIP, LIGHT_SHIP, TRANSPORT - }; - - struct unit_args_t { - icon_t icon = 0; - unit_type_t unit_type = unit_type_t::INVALID_UNIT_TYPE; - // TODO defaults for move_sound and select_sound - std::string_view sprite, move_sound, select_sound; - bool active = true, floating_flag = false; - uint32_t priority = 0; - fixed_point_t max_strength = 0, default_organisation = 0, maximum_speed = 0, weighted_value = 0, - supply_consumption = 0; - Timespan build_time; - Good::good_map_t build_cost, supply_cost; - terrain_modifiers_t terrain_modifiers; - - unit_args_t() = default; - unit_args_t(unit_args_t&&) = default; - }; - - private: - const branch_t PROPERTY(branch); /* type in defines */ - const icon_t PROPERTY(icon); - std::string PROPERTY(sprite); - const bool PROPERTY_CUSTOM_PREFIX(active, is); - unit_type_t PROPERTY(unit_type); - const bool PROPERTY_CUSTOM_PREFIX(floating_flag, has); - - const uint32_t PROPERTY(priority); - const fixed_point_t PROPERTY(max_strength); - const fixed_point_t PROPERTY(default_organisation); - const fixed_point_t PROPERTY(maximum_speed); - const fixed_point_t PROPERTY(weighted_value); - - std::string PROPERTY(move_sound); - std::string PROPERTY(select_sound); - - const Timespan PROPERTY(build_time); - Good::good_map_t PROPERTY(build_cost); - const fixed_point_t PROPERTY(supply_consumption); - Good::good_map_t PROPERTY(supply_cost); - - terrain_modifiers_t PROPERTY(terrain_modifiers); - - protected: - /* Non-const reference unit_args so variables can be moved from it. */ - Unit(std::string_view new_identifier, branch_t new_branch, unit_args_t& unit_args); - - public: - Unit(Unit&&) = default; - }; - - struct LandUnit : Unit { - friend struct UnitManager; - - enum struct allowed_cultures_t { ALL_CULTURES, ACCEPTED_CULTURES, PRIMARY_CULTURE }; - - struct land_unit_args_t { - allowed_cultures_t allowed_cultures = allowed_cultures_t::ALL_CULTURES; - std::string_view sprite_override, sprite_mount, sprite_mount_attach_node; - // TODO - represent these as modifier effects, so that they can be combined with tech, inventions, - // leader bonuses, etc. and applied to unit instances all in one go (same for NavalUnits below) - fixed_point_t reconnaissance = 0, attack = 0, defence = 0, discipline = 0, support = 0, maneuver = 0, - siege = 0; - - land_unit_args_t() = default; - land_unit_args_t(land_unit_args_t&&) = default; - }; - - private: - const allowed_cultures_t PROPERTY(allowed_cultures); - std::string PROPERTY(sprite_override); - std::string PROPERTY(sprite_mount); - std::string PROPERTY(sprite_mount_attach_node); - const fixed_point_t PROPERTY(reconnaissance); - const fixed_point_t PROPERTY(attack); - const fixed_point_t PROPERTY(defence); - const fixed_point_t PROPERTY(discipline); - const fixed_point_t PROPERTY(support); - const fixed_point_t PROPERTY(maneuver); - const fixed_point_t PROPERTY(siege); - - LandUnit(std::string_view new_identifier, unit_args_t& unit_args, land_unit_args_t const& land_unit_args); - - public: - LandUnit(LandUnit&&) = default; - }; - - struct NavalUnit : Unit { - friend struct UnitManager; - - struct naval_unit_args_t { - Unit::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; - fixed_point_t colonial_points = 0, supply_consumption_score = 0, hull = 0, gun_power = 0, fire_range = 0, - evasion = 0, torpedo_attack = 0; - - naval_unit_args_t() = default; - naval_unit_args_t(naval_unit_args_t&&) = default; - }; - - private: - const icon_t PROPERTY(naval_icon); - const bool PROPERTY_CUSTOM_PREFIX(sail, can); - const bool PROPERTY_CUSTOM_PREFIX(transport, is); - const bool PROPERTY_CUSTOM_PREFIX(capital, is); - const fixed_point_t PROPERTY(colonial_points); - const bool PROPERTY_CUSTOM_PREFIX(build_overseas, can); - const uint32_t PROPERTY(min_port_level); - const int32_t PROPERTY(limit_per_port); - const fixed_point_t PROPERTY(supply_consumption_score); - - const fixed_point_t PROPERTY(hull); - const fixed_point_t PROPERTY(gun_power); - const fixed_point_t PROPERTY(fire_range); - const fixed_point_t PROPERTY(evasion); - const fixed_point_t PROPERTY(torpedo_attack); - - NavalUnit(std::string_view new_identifier, unit_args_t& unit_args, naval_unit_args_t const& naval_unit_args); - - public: - NavalUnit(NavalUnit&&) = default; - }; - - struct UnitManager { - private: - IdentifierPointerRegistry IDENTIFIER_REGISTRY(unit); - IdentifierRegistry IDENTIFIER_REGISTRY(land_unit); - IdentifierRegistry IDENTIFIER_REGISTRY(naval_unit); - - public: - void reserve_all_units(size_t size); - void lock_all_units(); - - bool add_land_unit( - std::string_view identifier, Unit::unit_args_t& unit_args, LandUnit::land_unit_args_t const& land_unit_args - ); - bool add_naval_unit( - std::string_view identifier, Unit::unit_args_t& unit_args, NavalUnit::naval_unit_args_t const& naval_unit_args - ); - - static NodeTools::Callback auto expect_branch_str(NodeTools::Callback auto callback) { - using enum Unit::branch_t; - static const string_map_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 auto callback) { - return NodeTools::expect_identifier(expect_branch_str(callback)); - } - - bool load_unit_file( - GoodManager const& good_manager, TerrainTypeManager const& terrain_type_manager, - ModifierManager const& modifier_manager, ast::NodeCPtr root - ); - bool generate_modifiers(ModifierManager& modifier_manager) const; - }; -} diff --git a/src/openvic-simulation/military/UnitInstance.cpp b/src/openvic-simulation/military/UnitInstance.cpp new file mode 100644 index 0000000..7927b0f --- /dev/null +++ b/src/openvic-simulation/military/UnitInstance.cpp @@ -0,0 +1,31 @@ +#include "UnitInstance.hpp" +#include +#include "openvic-simulation/military/UnitType.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(Province const* starting_province, Province const* target_province) + : path { std::vector { 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 } {} + +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 diff --git a/src/openvic-simulation/military/UnitInstance.hpp b/src/openvic-simulation/military/UnitInstance.hpp new file mode 100644 index 0000000..dcca18a --- /dev/null +++ b/src/openvic-simulation/military/UnitInstance.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include +#include "openvic-simulation/map/Province.hpp" +#include "openvic-simulation/military/Leader.hpp" +#include "openvic-simulation/military/UnitType.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" + +namespace OpenVic { + template T> + struct UnitInstance { + protected: + std::string PROPERTY(unit_name); + T const& PROPERTY(unit_type); //can't change + + 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) : + unit_name { new_unit_name }, + unit_type { new_unit_type }, + organisation { new_unit_type.get_default_organisation() }, //TODO: modifiers + morale { 0 }, //TODO: modifiers + strength { new_unit_type.get_max_strength() } {} + public: + void set_unit_name(std::string_view new_unit_name) { + unit_name = new_unit_name; + } + }; + + struct RegimentInstance : UnitInstance { + private: + Pop& PROPERTY(pop); + + public: + RegimentInstance(std::string_view new_name, RegimentType const& new_regiment_type, Pop& new_pop); + }; + + struct ShipInstance : UnitInstance { + public: + ShipInstance(std::string_view new_name, ShipType const& new_ship_type); + }; + + struct MovementInfo { + private: + std::vector PROPERTY(path); + fixed_point_t PROPERTY(movement_progress); + + public: + MovementInfo(); + MovementInfo(Province const* starting_province, Province const* target_province); // contains/calls pathfinding logic + }; + + 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); + + MovementInfo PROPERTY_REF(movement_info); + + protected: + UnitInstanceGroup( + std::string_view new_name, + UnitType::branch_t new_branch, + std::vector&& new_units, + Leader const* new_leader, + Province const* new_position + ) : name { new_name }, + branch { new_branch }, + units { std::move(new_units) }, + leader { new_leader }, + position { new_position } {} + + public: + void set_name(std::string_view new_name) { + name = new_name; + } + }; + + struct ArmyInstance : UnitInstanceGroup { + public: + ArmyInstance( + std::string_view new_name, + std::vector&& new_units, + Leader const* new_leader, + Province const* new_position + ); + }; + + struct NavyInstance : UnitInstanceGroup { + 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 + ); + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/military/UnitType.cpp b/src/openvic-simulation/military/UnitType.cpp new file mode 100644 index 0000000..1fafe35 --- /dev/null +++ b/src/openvic-simulation/military/UnitType.cpp @@ -0,0 +1,343 @@ +#include "UnitType.hpp" + +#include "openvic-simulation/map/TerrainType.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +using enum UnitType::branch_t; +using enum UnitType::unit_category_t; + +UnitType::UnitType( + std::string_view new_identifier, branch_t new_branch, unit_type_args_t& unit_args +) : HasIdentifier { new_identifier }, + branch { new_branch }, + icon { unit_args.icon }, + sprite { unit_args.sprite }, + active { unit_args.active }, + unit_category { unit_args.unit_category }, + floating_flag { unit_args.floating_flag }, + priority { unit_args.priority }, + max_strength { unit_args.max_strength }, + default_organisation { unit_args.default_organisation }, + maximum_speed { unit_args.maximum_speed }, + weighted_value { unit_args.weighted_value }, + move_sound { unit_args.move_sound }, + select_sound { unit_args.select_sound }, + build_time { unit_args.build_time }, + build_cost { std::move(unit_args.build_cost) }, + supply_consumption { unit_args.supply_consumption }, + supply_cost { std::move(unit_args.supply_cost) }, + terrain_modifiers { std::move(unit_args.terrain_modifiers) } {} + +RegimentType::RegimentType( + 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 }, + sprite_override { regiment_type_args.sprite_override }, + sprite_mount { regiment_type_args.sprite_mount }, + sprite_mount_attach_node { regiment_type_args.sprite_mount_attach_node }, + reconnaissance { regiment_type_args.reconnaissance }, + attack { regiment_type_args.attack }, + defence { regiment_type_args.defence }, + discipline { regiment_type_args.discipline }, + support { regiment_type_args.support }, + maneuver { regiment_type_args.maneuver }, + siege { regiment_type_args.siege } {} + +ShipType::ShipType( + 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 }, + sail { ship_type_args.sail }, + transport { ship_type_args.transport }, + capital { ship_type_args.capital }, + colonial_points { ship_type_args.colonial_points }, + build_overseas { ship_type_args.build_overseas }, + min_port_level { ship_type_args.min_port_level }, + limit_per_port { ship_type_args.limit_per_port }, + supply_consumption_score { ship_type_args.supply_consumption_score }, + hull { ship_type_args.hull }, + gun_power { ship_type_args.gun_power }, + fire_range { ship_type_args.fire_range }, + evasion { ship_type_args.evasion }, + torpedo_attack { ship_type_args.torpedo_attack } {} + +void UnitTypeManager::reserve_all_unit_types(size_t size) { + reserve_more_unit_types(size); + reserve_more_regiment_types(size); + reserve_more_ship_types(size); +} + +void UnitTypeManager::lock_all_unit_types() { + unit_types.lock(); + regiment_types.lock(); + ship_types.lock(); +} + +static bool _check_shared_parameters(std::string_view identifier, UnitType::unit_type_args_t const& unit_args) { + if (identifier.empty()) { + Logger::error("Invalid unit identifier - empty!"); + return false; + } + + if (unit_args.icon <= 0) { + Logger::error("Invalid icon for unit ", identifier, " - ", unit_args.icon, " (must be positive)"); + return false; + } + + if (unit_args.unit_category == INVALID_UNIT_CATEGORY) { + Logger::error("Invalid unit type for unit ", identifier, "!"); + return false; + } + + // TODO check that sprite, move_sound and select_sound exist + + return true; +} + +bool UnitTypeManager::add_regiment_type( + std::string_view identifier, UnitType::unit_type_args_t& unit_args, RegimentType::regiment_type_args_t const& regiment_type_args +) { + if (!_check_shared_parameters(identifier, unit_args)) { + return false; + } + + // TODO check that sprite_override, sprite_mount, and sprite_mount_attach_node exist + + if (ship_types.has_identifier(identifier)) { + Logger::error("Land unit ", identifier, " already exists as a naval unit!"); + return false; + } + + bool ret = regiment_types.add_item({ identifier, unit_args, std::move(regiment_type_args) }); + if (ret) { + ret &= unit_types.add_item(®iment_types.get_items().back()); + } + return ret; +} + +bool UnitTypeManager::add_ship_type( + std::string_view identifier, UnitType::unit_type_args_t& unit_args, ShipType::ship_type_args_t const& ship_type_args +) { + if (!_check_shared_parameters(identifier, unit_args)) { + return false; + } + + if (ship_type_args.naval_icon <= 0) { + Logger::error("Invalid naval icon identifier - ", ship_type_args.naval_icon, " (must be positive)"); + return false; + } + + if (ship_type_args.supply_consumption_score <= 0) { + Logger::warning("Supply consumption score for ", identifier, " is not positive!"); + } + + if (regiment_types.has_identifier(identifier)) { + Logger::error("Naval unit ", identifier, " already exists as a land unit!"); + return false; + } + + bool ret = ship_types.add_item({ identifier, unit_args, ship_type_args }); + if (ret) { + ret &= unit_types.add_item(&ship_types.get_items().back()); + } + return ret; +} + +bool UnitTypeManager::load_unit_type_file( + GoodManager const& good_manager, TerrainTypeManager const& terrain_type_manager, ModifierManager const& modifier_manager, + ast::NodeCPtr root +) { + return expect_dictionary([this, &good_manager, &terrain_type_manager, &modifier_manager]( + std::string_view key, ast::NodeCPtr value + ) -> bool { + + UnitType::branch_t branch = INVALID_BRANCH; + + bool ret = expect_key("type", expect_branch_identifier(assign_variable_callback(branch)))(value); + + /* We shouldn't just check ret as it can be false even if branch was successfully parsed, + * but more than one instance of the key was found. */ + if (branch != LAND && branch != NAVAL) { + Logger::error("Failed to read branch for unit: ", key); + return false; + } + + UnitType::unit_type_args_t unit_args {}; + + static const string_map_t unit_type_map { + { "infantry", INFANTRY }, + { "cavalry", CAVALRY }, + { "support", SUPPORT }, + { "special", SPECIAL }, + { "big_ship", BIG_SHIP }, + { "light_ship", LIGHT_SHIP }, + { "transport", TRANSPORT } + }; + + key_map_t key_map {}; + /* Shared dictionary entries */ + ret &= add_key_map_entries(key_map, + "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.icon)), + "type", ONE_EXACTLY, success_callback, /* Already loaded above using expect_key */ + "sprite", ONE_EXACTLY, expect_identifier(assign_variable_callback(unit_args.sprite)), + "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(unit_args.active)), + "unit_type", ONE_EXACTLY, + expect_identifier(expect_mapped_string(unit_type_map, assign_variable_callback(unit_args.unit_category))), + "floating_flag", ONE_EXACTLY, expect_bool(assign_variable_callback(unit_args.floating_flag)), + "priority", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.priority)), + "max_strength", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.max_strength)), + "default_organisation", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.default_organisation)), + "maximum_speed", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.maximum_speed)), + "weighted_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.weighted_value)), + "move_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.move_sound)), + "select_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.select_sound)), + "build_time", ONE_EXACTLY, expect_days(assign_variable_callback(unit_args.build_time)), + "build_cost", ONE_EXACTLY, good_manager.expect_good_decimal_map(move_variable_callback(unit_args.build_cost)), + "supply_consumption", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.supply_consumption)), + "supply_cost", ONE_EXACTLY, good_manager.expect_good_decimal_map(move_variable_callback(unit_args.supply_cost)) + ); + + const auto add_terrain_modifier = [&unit_args, &terrain_type_manager, &modifier_manager]( + std::string_view default_key, ast::NodeCPtr default_value + ) -> bool { + TerrainType const* terrain_type = terrain_type_manager.get_terrain_type_by_identifier(default_key); + if (terrain_type != nullptr) { + // TODO - restrict what modifier effects can be used here + return modifier_manager.expect_modifier_value( + map_callback(unit_args.terrain_modifiers, terrain_type) + )(default_value); + } + return key_value_invalid_callback(default_key, default_value); + }; + + switch (branch) { + case LAND: { + RegimentType::regiment_type_args_t regiment_type_args {}; + bool is_restricted_to_primary_culture = false; + bool is_restricted_to_accepted_cultures = false; + + ret &= add_key_map_entries(key_map, + "primary_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_primary_culture)), + "accepted_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_accepted_cultures)), + "sprite_override", ZERO_OR_ONE, expect_identifier(assign_variable_callback(regiment_type_args.sprite_override)), + "sprite_mount", ZERO_OR_ONE, expect_identifier(assign_variable_callback(regiment_type_args.sprite_mount)), + "sprite_mount_attach_node", ZERO_OR_ONE, + expect_identifier(assign_variable_callback(regiment_type_args.sprite_mount_attach_node)), + "reconnaissance", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.reconnaissance)), + "attack", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.attack)), + "defence", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.defence)), + "discipline", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.discipline)), + "support", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.support)), + "maneuver", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.maneuver)), + "siege", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(regiment_type_args.siege)) + ); + + if (is_restricted_to_accepted_cultures) { + regiment_type_args.allowed_cultures = RegimentType::allowed_cultures_t::ACCEPTED_CULTURES; + } else if (is_restricted_to_primary_culture) { + regiment_type_args.allowed_cultures = RegimentType::allowed_cultures_t::PRIMARY_CULTURE; + } else { + regiment_type_args.allowed_cultures = RegimentType::allowed_cultures_t::ALL_CULTURES; + } + + ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier)(value); + + ret &= add_regiment_type(key, unit_args, regiment_type_args); + + return ret; + } + case NAVAL: { + ShipType::ship_type_args_t ship_type_args {}; + + ret &= add_key_map_entries(key_map, + "naval_icon", ONE_EXACTLY, expect_uint(assign_variable_callback(ship_type_args.naval_icon)), + "sail", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.sail)), + "transport", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.transport)), + "capital", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.capital)), + "colonial_points", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(ship_type_args.colonial_points)), + "can_build_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.build_overseas)), + "min_port_level", ONE_EXACTLY, expect_uint(assign_variable_callback(ship_type_args.min_port_level)), + "limit_per_port", ONE_EXACTLY, expect_int(assign_variable_callback(ship_type_args.limit_per_port)), + "supply_consumption_score", ZERO_OR_ONE, + expect_fixed_point(assign_variable_callback(ship_type_args.supply_consumption_score)), + "hull", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.hull)), + "gun_power", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.gun_power)), + "fire_range", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.fire_range)), + "evasion", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.evasion)), + "torpedo_attack", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(ship_type_args.torpedo_attack)) + ); + + ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier)(value); + + ret &= add_ship_type(key, unit_args, ship_type_args); + + return ret; + } + default: + /* Unreachable - an earlier check terminates the function if branch isn't LAND or NAVAL. */ + Logger::error("Unknown branch for unit ", key, ": ", static_cast(branch)); + return false; + } + })(root); +} + +bool UnitTypeManager::generate_modifiers(ModifierManager& modifier_manager) const { + bool ret = true; + + const auto generate_stat_modifiers = [&modifier_manager, &ret](std::string_view identifier, UnitType::branch_t branch) -> void { + const auto stat_modifier = [&modifier_manager, &ret, &identifier]( + std::string_view suffix, bool is_positive_good, ModifierEffect::format_t format + ) -> void { + ret &= modifier_manager.add_modifier_effect( + ModifierManager::get_flat_identifier(identifier, suffix), is_positive_good, format + ); + }; + + using enum ModifierEffect::format_t; + + ret &= modifier_manager.register_complex_modifier(identifier); + + stat_modifier("attack", true, RAW_DECIMAL); + stat_modifier("defence", true, RAW_DECIMAL); + stat_modifier("default_organisation", true, RAW_DECIMAL); + stat_modifier("maximum_speed", true, RAW_DECIMAL); + stat_modifier("build_time", false, INT); + stat_modifier("supply_consumption", false, PROPORTION_DECIMAL); + + switch (branch) { + case LAND: + stat_modifier("reconnaissance", true, RAW_DECIMAL); + stat_modifier("discipline", true, PROPORTION_DECIMAL); + stat_modifier("support", true, PROPORTION_DECIMAL); + stat_modifier("maneuver", true, INT); + stat_modifier("siege", true, RAW_DECIMAL); + break; + case NAVAL: + stat_modifier("colonial_points", true, INT); + stat_modifier("supply_consumption_score", false, INT); + stat_modifier("hull", true, RAW_DECIMAL); + stat_modifier("gun_power", true, RAW_DECIMAL); + stat_modifier("fire_range", true, RAW_DECIMAL); + stat_modifier("evasion", true, PROPORTION_DECIMAL); + stat_modifier("torpedo_attack", true, RAW_DECIMAL); + break; + default: + /* Unreachable - unit types are only added via add_regiment_type or add_ship_type which set branch to LAND or NAVAL. */ + Logger::error("Invalid branch for unit ", identifier, ": ", static_cast(branch)); + } + }; + + generate_stat_modifiers("army_base", LAND); + for (RegimentType const& regiment_type : get_regiment_types()) { + generate_stat_modifiers(regiment_type.get_identifier(), LAND); + } + + generate_stat_modifiers("navy_base", NAVAL); + for (ShipType const& ship_type : get_ship_types()) { + generate_stat_modifiers(ship_type.get_identifier(), NAVAL); + } + + return ret; +} diff --git a/src/openvic-simulation/military/UnitType.hpp b/src/openvic-simulation/military/UnitType.hpp new file mode 100644 index 0000000..201708e --- /dev/null +++ b/src/openvic-simulation/military/UnitType.hpp @@ -0,0 +1,181 @@ +#pragma once + +#include +#include + +#include "openvic-simulation/misc/Modifier.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/Good.hpp" +#include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" + +namespace OpenVic { + struct TerrainType; + struct TerrainTypeManager; + + struct UnitType : HasIdentifier { + using icon_t = uint32_t; + using terrain_modifiers_t = ordered_map; + + enum struct branch_t : uint8_t { INVALID_BRANCH, LAND, NAVAL }; + enum struct unit_category_t : uint8_t { + INVALID_UNIT_CATEGORY, INFANTRY, CAVALRY, SUPPORT, SPECIAL, BIG_SHIP, LIGHT_SHIP, TRANSPORT + }; + + struct unit_type_args_t { + icon_t icon = 0; + unit_category_t unit_category = unit_category_t::INVALID_UNIT_CATEGORY; + // TODO defaults for move_sound and select_sound + std::string_view sprite, move_sound, select_sound; + bool active = true, floating_flag = false; + uint32_t priority = 0; + fixed_point_t max_strength = 0, default_organisation = 0, maximum_speed = 0, weighted_value = 0, + supply_consumption = 0; + Timespan build_time; + Good::good_map_t build_cost, supply_cost; + terrain_modifiers_t terrain_modifiers; + + unit_type_args_t() = default; + unit_type_args_t(unit_type_args_t&&) = default; + }; + + private: + const branch_t PROPERTY(branch); /* type in defines */ + const icon_t PROPERTY(icon); + std::string PROPERTY(sprite); + const bool PROPERTY_CUSTOM_PREFIX(active, is); + unit_category_t PROPERTY(unit_category); + const bool PROPERTY_CUSTOM_PREFIX(floating_flag, has); + + const uint32_t PROPERTY(priority); + const fixed_point_t PROPERTY(max_strength); + const fixed_point_t PROPERTY(default_organisation); + const fixed_point_t PROPERTY(maximum_speed); + const fixed_point_t PROPERTY(weighted_value); + + std::string PROPERTY(move_sound); + std::string PROPERTY(select_sound); + + const Timespan PROPERTY(build_time); + Good::good_map_t PROPERTY(build_cost); + const fixed_point_t PROPERTY(supply_consumption); + Good::good_map_t PROPERTY(supply_cost); + + terrain_modifiers_t PROPERTY(terrain_modifiers); + + protected: + /* Non-const reference unit_args so variables can be moved from it. */ + UnitType(std::string_view new_identifier, branch_t new_branch, unit_type_args_t& unit_args); + + public: + UnitType(UnitType&&) = default; + }; + + struct RegimentType : UnitType { + friend struct UnitTypeManager; + + enum struct allowed_cultures_t { ALL_CULTURES, ACCEPTED_CULTURES, PRIMARY_CULTURE }; + + struct regiment_type_args_t { + allowed_cultures_t allowed_cultures = allowed_cultures_t::ALL_CULTURES; + std::string_view sprite_override, sprite_mount, sprite_mount_attach_node; + // TODO - represent these as modifier effects, so that they can be combined with tech, inventions, + // leader bonuses, etc. and applied to unit instances all in one go (same for ShipTypes below) + fixed_point_t reconnaissance = 0, attack = 0, defence = 0, discipline = 0, support = 0, maneuver = 0, + siege = 0; + + regiment_type_args_t() = default; + regiment_type_args_t(regiment_type_args_t&&) = default; + }; + + private: + const allowed_cultures_t PROPERTY(allowed_cultures); + std::string PROPERTY(sprite_override); + std::string PROPERTY(sprite_mount); + std::string PROPERTY(sprite_mount_attach_node); + const fixed_point_t PROPERTY(reconnaissance); + const fixed_point_t PROPERTY(attack); + const fixed_point_t PROPERTY(defence); + const fixed_point_t PROPERTY(discipline); + const fixed_point_t PROPERTY(support); + 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); + + public: + RegimentType(RegimentType&&) = default; + }; + + struct ShipType : UnitType { + friend struct UnitTypeManager; + + struct ship_type_args_t { + UnitType::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; + fixed_point_t colonial_points = 0, supply_consumption_score = 0, hull = 0, gun_power = 0, fire_range = 0, + evasion = 0, torpedo_attack = 0; + + ship_type_args_t() = default; + ship_type_args_t(ship_type_args_t&&) = default; + }; + + private: + const icon_t PROPERTY(naval_icon); + const bool PROPERTY_CUSTOM_PREFIX(sail, can); + const bool PROPERTY_CUSTOM_PREFIX(transport, is); + const bool PROPERTY_CUSTOM_PREFIX(capital, is); + const fixed_point_t PROPERTY(colonial_points); + const bool PROPERTY_CUSTOM_PREFIX(build_overseas, can); + const uint32_t PROPERTY(min_port_level); + const int32_t PROPERTY(limit_per_port); + const fixed_point_t PROPERTY(supply_consumption_score); + + const fixed_point_t PROPERTY(hull); + const fixed_point_t PROPERTY(gun_power); + const fixed_point_t PROPERTY(fire_range); + 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); + + public: + ShipType(ShipType&&) = default; + }; + + struct UnitTypeManager { + private: + IdentifierPointerRegistry IDENTIFIER_REGISTRY(unit_type); + IdentifierRegistry IDENTIFIER_REGISTRY(regiment_type); + IdentifierRegistry IDENTIFIER_REGISTRY(ship_type); + + public: + void reserve_all_unit_types(size_t size); + 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 + ); + bool add_ship_type( + std::string_view identifier, UnitType::unit_type_args_t& unit_args, ShipType::ship_type_args_t const& ship_type_args + ); + + static NodeTools::Callback auto expect_branch_str(NodeTools::Callback auto callback) { + using enum UnitType::branch_t; + static const string_map_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 auto callback) { + return NodeTools::expect_identifier(expect_branch_str(callback)); + } + + bool load_unit_type_file( + GoodManager const& good_manager, TerrainTypeManager const& terrain_type_manager, + ModifierManager const& modifier_manager, ast::NodeCPtr root + ); + bool generate_modifiers(ModifierManager& modifier_manager) const; + }; +} -- cgit v1.2.3-56-ga3b1