diff options
Diffstat (limited to 'src/openvic-simulation')
21 files changed, 717 insertions, 147 deletions
diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index ea30246..bcae82b 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -40,8 +40,10 @@ void InstanceManager::update_gamestate() { Logger::info("Update: ", today); // Update gamestate... - map_instance.update_gamestate(today); - country_instance_manager.update_gamestate(); + map_instance.update_gamestate(today, definition_manager.get_define_manager()); + country_instance_manager.update_gamestate( + today, definition_manager.get_define_manager(), definition_manager.get_military_manager().get_unit_type_manager() + ); gamestate_updated(); gamestate_needs_update = false; @@ -81,7 +83,9 @@ bool InstanceManager::setup() { definition_manager.get_research_manager().get_invention_manager().get_inventions(), definition_manager.get_politics_manager().get_ideology_manager().get_ideologies(), definition_manager.get_politics_manager().get_government_type_manager().get_government_types(), - definition_manager.get_pop_manager().get_pop_types() + definition_manager.get_pop_manager().get_pop_types(), + definition_manager.get_military_manager().get_unit_type_manager().get_regiment_types(), + definition_manager.get_military_manager().get_unit_type_manager().get_ship_types() ); game_instance_setup = true; diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 3c2e7bb..4fab010 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -3,12 +3,15 @@ #include "openvic-simulation/country/CountryDefinition.hpp" #include "openvic-simulation/history/CountryHistory.hpp" #include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/misc/Define.hpp" #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/research/Invention.hpp" #include "openvic-simulation/research/Technology.hpp" using namespace OpenVic; +using enum CountryInstance::country_status_t; + static constexpr colour_t ERROR_COLOUR = colour_t::from_integer(0xFF0000); CountryInstance::CountryInstance( @@ -17,20 +20,30 @@ CountryInstance::CountryInstance( decltype(inventions)::keys_t const& invention_keys, decltype(upper_house)::keys_t const& ideology_keys, decltype(government_flag_overrides)::keys_t const& government_type_keys, - decltype(pop_type_distribution)::keys_t const& pop_type_keys + decltype(pop_type_distribution)::keys_t const& pop_type_keys, + decltype(unlocked_regiment_types)::keys_t const& unlocked_regiment_types_keys, + decltype(unlocked_ship_types)::keys_t const& unlocked_ship_types_keys ) : /* Main attributes */ country_definition { new_country_definition }, colour { ERROR_COLOUR }, capital { nullptr }, country_flags {}, - civilised { false }, releasable_vassal { true }, + country_status { COUNTRY_STATUS_UNCIVILISED }, + lose_great_power_date {}, + total_score { 0 }, + total_rank { 0 }, owned_provinces {}, controlled_provinces {}, core_provinces {}, states {}, /* Production */ + industrial_power { 0 }, + industrial_power_from_states {}, + industrial_power_from_investments {}, + industrial_rank { 0 }, + foreign_investments {}, /* Budget */ cash_stockpile { 0 }, @@ -73,32 +86,74 @@ CountryInstance::CountryInstance( /* Trade */ /* Diplomacy */ - total_rank { 0 }, prestige { 0 }, prestige_rank { 0 }, - industrial_power { 0 }, - industrial_rank { 0 }, - military_power { 0 }, - military_rank { 0 }, diplomatic_points { 0 }, /* Military */ + military_power { 0 }, + military_power_from_land { 0 }, + military_power_from_sea { 0 }, + military_power_from_leaders { 0 }, + military_rank { 0 }, generals {}, admirals {}, armies {}, navies {}, regiment_count { 0 }, - mobilisation_regiment_potential { 0 }, + max_supported_regiment_count { 0 }, + mobilisation_potential_regiment_count { 0 }, + mobilisation_max_regiment_count { 0 }, + mobilisation_impact { 0 }, + supply_consumption { 1 }, ship_count { 0 }, total_consumed_ship_supply { 0 }, max_ship_supply { 0 }, leadership_points { 0 }, - war_exhaustion { 0 } {} + war_exhaustion { 0 }, + mobilised { false }, + disarmed { false }, + unlocked_regiment_types { &unlocked_regiment_types_keys }, + allowed_regiment_cultures { RegimentType::allowed_cultures_t::NO_CULTURES }, + unlocked_ship_types { &unlocked_ship_types_keys } { + + for (RegimentType const& regiment_type : *unlocked_regiment_types.get_keys()) { + if (regiment_type.is_active()) { + unlock_unit_type(regiment_type); + } + } + + for (ShipType const& ship_type : *unlocked_ship_types.get_keys()) { + if (ship_type.is_active()) { + unlock_unit_type(ship_type); + } + } +} std::string_view CountryInstance::get_identifier() const { return country_definition->get_identifier(); } +bool CountryInstance::exists() const { + return !owned_provinces.empty(); +} + +bool CountryInstance::is_civilised() const { + return country_status <= COUNTRY_STATUS_CIVILISED; +} + +bool CountryInstance::can_colonise() const { + return country_status <= COUNTRY_STATUS_SECONDARY_POWER; +} + +bool CountryInstance::is_great_power() const { + return country_status == COUNTRY_STATUS_GREAT_POWER; +} + +bool CountryInstance::is_secondary_power() const { + return country_status == COUNTRY_STATUS_SECONDARY_POWER; +} + bool CountryInstance::set_country_flag(std::string_view flag, bool warn) { if (flag.empty()) { Logger::error("Attempted to set empty country flag for country ", get_identifier()); @@ -245,12 +300,55 @@ template void CountryInstance::add_leader(LeaderBranched<UnitType::branch_t::NAV template bool CountryInstance::remove_leader(LeaderBranched<UnitType::branch_t::LAND> const*); template bool CountryInstance::remove_leader(LeaderBranched<UnitType::branch_t::NAVAL> const*); -bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry, MapInstance& map_instance) { - if (entry == nullptr) { - Logger::error("Trying to apply null country history to ", get_identifier()); - return false; +template<UnitType::branch_t Branch> +void CountryInstance::unlock_unit_type(UnitTypeBranched<Branch> const& unit_type) { + IndexedMap<UnitTypeBranched<Branch>, bool>& unlocked_unit_types = get_unlocked_unit_types<Branch>(); + + decltype(unlocked_regiment_types)::value_ref_t unlock_value = unlocked_unit_types[unit_type]; + + if (unlock_value) { + Logger::warning( + "Attempted to unlock already-unlocked unit type \"", unit_type.get_identifier(), + "\" for country ", get_identifier() + ); + return; } + unlock_value = true; + + if constexpr (Branch == UnitType::branch_t::LAND) { + allowed_regiment_cultures = RegimentType::allowed_cultures_get_most_permissive( + allowed_regiment_cultures, unit_type.get_allowed_cultures() + ); + } +} + +template void CountryInstance::unlock_unit_type(UnitTypeBranched<UnitType::branch_t::LAND> const&); +template void CountryInstance::unlock_unit_type(UnitTypeBranched<UnitType::branch_t::NAVAL> const&); + +bool CountryInstance::is_primary_culture(Culture const& culture) const { + return &culture == primary_culture; +} + +bool CountryInstance::is_accepted_culture(Culture const& culture) const { + return accepted_cultures.contains(&culture); +} + +bool CountryInstance::is_primary_or_accepted_culture(Culture const& culture) const { + return is_primary_culture(culture) || is_accepted_culture(culture); +} + +void CountryInstance::apply_foreign_investments( + fixed_point_map_t<CountryDefinition const*> const& investments, CountryInstanceManager const& country_instance_manager +) { + for (auto const& [country, money_invested] : investments) { + foreign_investments[&country_instance_manager.get_country_instance_from_definition(*country)] = money_invested; + } +} + +bool CountryInstance::apply_history_to_country( + CountryHistoryEntry const& entry, MapInstance& map_instance, CountryInstanceManager const& country_instance_manager +) { constexpr auto set_optional = []<typename T>(T& target, std::optional<T> const& source) { if (source) { target = *source; @@ -259,60 +357,94 @@ bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry, bool ret = true; - set_optional(primary_culture, entry->get_primary_culture()); - for (Culture const* culture : entry->get_accepted_cultures()) { + set_optional(primary_culture, entry.get_primary_culture()); + for (Culture const* culture : entry.get_accepted_cultures()) { ret &= add_accepted_culture(*culture); } - set_optional(religion, entry->get_religion()); - set_optional(ruling_party, entry->get_ruling_party()); - set_optional(last_election, entry->get_last_election()); - ret &= upper_house.copy(entry->get_upper_house()); - if (entry->get_capital()) { - capital = &map_instance.get_province_instance_from_definition(**entry->get_capital()); - } - set_optional(government_type, entry->get_government_type()); - set_optional(plurality, entry->get_plurality()); - set_optional(national_value, entry->get_national_value()); - set_optional(civilised, entry->is_civilised()); - set_optional(prestige, entry->get_prestige()); - for (Reform const* reform : entry->get_reforms()) { + set_optional(religion, entry.get_religion()); + set_optional(ruling_party, entry.get_ruling_party()); + set_optional(last_election, entry.get_last_election()); + ret &= upper_house.copy(entry.get_upper_house()); + if (entry.get_capital()) { + capital = &map_instance.get_province_instance_from_definition(**entry.get_capital()); + } + set_optional(government_type, entry.get_government_type()); + set_optional(plurality, entry.get_plurality()); + set_optional(national_value, entry.get_national_value()); + if (entry.is_civilised()) { + country_status = *entry.is_civilised() ? COUNTRY_STATUS_CIVILISED : COUNTRY_STATUS_UNCIVILISED; + } + set_optional(prestige, entry.get_prestige()); + for (Reform const* reform : entry.get_reforms()) { ret &= add_reform(reform); } - set_optional(tech_school, entry->get_tech_school()); + set_optional(tech_school, entry.get_tech_school()); constexpr auto set_bool_map_to_indexed_map = []<typename T>(IndexedMap<T, bool>& target, ordered_map<T const*, bool> source) { for (auto const& [key, value] : source) { target[*key] = value; } }; - set_bool_map_to_indexed_map(technologies, entry->get_technologies()); - set_bool_map_to_indexed_map(inventions, entry->get_inventions()); - // entry->get_foreign_investment(); + set_bool_map_to_indexed_map(technologies, entry.get_technologies()); + set_bool_map_to_indexed_map(inventions, entry.get_inventions()); + apply_foreign_investments(entry.get_foreign_investment(), country_instance_manager); // These need to be applied to pops - // entry->get_consciousness(); - // entry->get_nonstate_consciousness(); - // entry->get_literacy(); - // entry->get_nonstate_culture_literacy(); - - set_optional(releasable_vassal, entry->is_releasable_vassal()); - // entry->get_colonial_points(); - for (std::string const& flag : entry->get_country_flags()) { + // entry.get_consciousness(); + // entry.get_nonstate_consciousness(); + // entry.get_literacy(); + // entry.get_nonstate_culture_literacy(); + + set_optional(releasable_vassal, entry.is_releasable_vassal()); + // entry.get_colonial_points(); + for (std::string const& flag : entry.get_country_flags()) { ret &= set_country_flag(flag, true); } - for (std::string const& flag : entry->get_global_flags()) { + for (std::string const& flag : entry.get_global_flags()) { // TODO - set global flag } - government_flag_overrides.write_non_empty_values(entry->get_government_flag_overrides()); - for (Decision const* decision : entry->get_decisions()) { + government_flag_overrides.write_non_empty_values(entry.get_government_flag_overrides()); + for (Decision const* decision : entry.get_decisions()) { // TODO - take decision } return ret; } -void CountryInstance::_update_production() { +void CountryInstance::_update_production(DefineManager const& define_manager) { + // Calculate industrial power from states and foreign investments + industrial_power = 0; + industrial_power_from_states.clear(); + industrial_power_from_investments.clear(); + + for (State const* state : states) { + const fixed_point_t state_industrial_power = state->get_industrial_power(); + if (state_industrial_power != 0) { + industrial_power += state_industrial_power; + industrial_power_from_states.emplace_back(state, state_industrial_power); + } + } + + for (auto const& [country, money_invested] : foreign_investments) { + if (country->exists()) { + const fixed_point_t investment_industrial_power = + money_invested * define_manager.get_country_investment_industrial_score_factor() / 100; + if (investment_industrial_power != 0) { + industrial_power += investment_industrial_power; + industrial_power_from_investments.emplace_back(country, investment_industrial_power); + } + } + } + + std::sort( + industrial_power_from_states.begin(), industrial_power_from_states.end(), + [](auto const& a, auto const& b) -> bool { return a.second > b.second; } + ); + std::sort( + industrial_power_from_investments.begin(), industrial_power_from_investments.end(), + [](auto const& a, auto const& b) -> bool { return a.second > b.second; } + ); } void CountryInstance::_update_budget() { @@ -334,7 +466,7 @@ void CountryInstance::_update_population() { national_militancy = 0; pop_type_distribution.clear(); - for (auto const& state : states) { + for (State const* state : states) { total_population += state->get_total_population(); // TODO - change casting if Pop::pop_size_t changes type @@ -360,11 +492,11 @@ void CountryInstance::_update_trade() { } void CountryInstance::_update_diplomacy() { - // TODO - update prestige, industrial_power, military_power (ranks will be updated after all countries have calculated their scores) + // TODO - add prestige from modifiers // TODO - update diplomatic points and colonial power } -void CountryInstance::_update_military() { +void CountryInstance::_update_military(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager) { regiment_count = 0; for (ArmyInstance const* army : armies) { @@ -379,10 +511,91 @@ void CountryInstance::_update_military() { total_consumed_ship_supply += navy->get_total_consumed_supply(); } - // TODO - update mobilisation_regiment_potential, max_ship_supply, leadership_points, war_exhaustion + // Calculate military power from land, sea, and leaders + + size_t deployed_non_mobilised_regiments = 0; + for (ArmyInstance const* army : armies) { + for (RegimentInstance const* regiment : army->get_units()) { + if (!regiment->is_mobilised()) { + deployed_non_mobilised_regiments++; + } + } + } + + max_supported_regiment_count = 0; + for (State const* state : states) { + max_supported_regiment_count += state->get_max_supported_regiments(); + } + + // TODO - apply country/tech modifiers to supply consumption + supply_consumption = 1; + + const size_t regular_army_size = std::min(4 * deployed_non_mobilised_regiments, max_supported_regiment_count); + + fixed_point_t sum_of_regiment_type_stats = 0; + for (RegimentType const& regiment_type : unit_type_manager.get_regiment_types()) { + // TODO - apply country/tech modifiers to regiment stats + sum_of_regiment_type_stats += ( + regiment_type.get_attack() + regiment_type.get_defence() /*+ land_attack_modifier + land_defense_modifier*/ + ) * regiment_type.get_discipline(); + } + + military_power_from_land = supply_consumption * fixed_point_t::parse(regular_army_size) * sum_of_regiment_type_stats + / fixed_point_t::parse(7 * (1 + unit_type_manager.get_regiment_type_count())); + + if (disarmed) { + military_power_from_land *= define_manager.get_disarmed_penalty(); + } + + military_power_from_sea = 0; + for (NavyInstance const* navy : navies) { + for (ShipInstance const* ship : navy->get_units()) { + ShipType const& ship_type = ship->get_unit_type(); + + if (ship_type.is_capital()) { + + // TODO - include gun power and hull modifiers + naval attack and defense modifiers + + military_power_from_sea += (ship_type.get_gun_power() /*+ naval_attack_modifier*/) + * (ship_type.get_hull() /* + naval_defense_modifier*/); + } + } + } + military_power_from_sea /= 250; + + military_power_from_leaders = fixed_point_t::parse( + std::min(generals.size() + admirals.size(), deployed_non_mobilised_regiments) + ); + + military_power = military_power_from_land + military_power_from_sea + military_power_from_leaders; + + // Mobilisation calculations + mobilisation_impact = 0; // TODO - apply ruling party's war policy + + mobilisation_max_regiment_count = + ((fixed_point_t::_1() + mobilisation_impact) * fixed_point_t::parse(regiment_count)).to_int64_t(); + + mobilisation_potential_regiment_count = 0; // TODO - calculate max regiments from poor citizens + if (mobilisation_potential_regiment_count > mobilisation_max_regiment_count) { + mobilisation_potential_regiment_count = mobilisation_max_regiment_count; + } + + // TODO - update max_ship_supply, leadership_points, war_exhaustion } -void CountryInstance::update_gamestate() { +void CountryInstance::update_gamestate(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager) { + // Order of updates might need to be changed/functions split up to account for dependencies + _update_production(define_manager); + _update_budget(); + _update_technology(); + _update_politics(); + _update_population(); + _update_trade(); + _update_diplomacy(); + _update_military(define_manager, unit_type_manager); + + total_score = prestige + industrial_power + military_power; + if (country_definition != nullptr) { const CountryDefinition::government_colour_map_t::const_iterator it = country_definition->get_alternative_colours().find(government_type); @@ -405,22 +618,129 @@ void CountryInstance::update_gamestate() { } else { flag_government_type = nullptr; } - - // Order of updates might need to be changed/functions split up to account for dependencies - _update_production(); - _update_budget(); - _update_technology(); - _update_politics(); - _update_population(); - _update_trade(); - _update_diplomacy(); - _update_military(); } void CountryInstance::tick() { } +void CountryInstanceManager::update_rankings(Date today, DefineManager const& define_manager) { + total_ranking.clear(); + + for (CountryInstance& country : country_instances.get_items()) { + if (country.exists()) { + total_ranking.push_back(&country); + } + } + + prestige_ranking = total_ranking; + industrial_power_ranking = total_ranking; + military_power_ranking = total_ranking; + + std::sort( + total_ranking.begin(), total_ranking.end(), + [](CountryInstance const* a, CountryInstance const* b) -> bool { + const bool a_civilised = a->is_civilised(); + const bool b_civilised = b->is_civilised(); + return a_civilised != b_civilised ? a_civilised : a->get_total_score() > b->get_total_score(); + } + ); + std::sort( + prestige_ranking.begin(), prestige_ranking.end(), + [](CountryInstance const* a, CountryInstance const* b) -> bool { + return a->get_prestige() > b->get_prestige(); + } + ); + std::sort( + industrial_power_ranking.begin(), industrial_power_ranking.end(), + [](CountryInstance const* a, CountryInstance const* b) -> bool { + return a->get_industrial_power() > b->get_industrial_power(); + } + ); + std::sort( + military_power_ranking.begin(), military_power_ranking.end(), + [](CountryInstance const* a, CountryInstance const* b) -> bool { + return a->get_military_power() > b->get_military_power(); + } + ); + + for (size_t index = 0; index < total_ranking.size(); ++index) { + const size_t rank = index + 1; + total_ranking[index]->total_rank = rank; + prestige_ranking[index]->prestige_rank = rank; + industrial_power_ranking[index]->industrial_rank = rank; + military_power_ranking[index]->military_rank = rank; + } + + const size_t max_great_power_rank = define_manager.get_great_power_rank(); + const size_t max_secondary_power_rank = define_manager.get_secondary_power_rank(); + const Timespan lose_great_power_grace_days = define_manager.get_lose_great_power_grace_days(); + + // Demote great powers who have been below the max great power rank for longer than the demotion grace period and + // remove them from the list. We don't just demote them all and clear the list as when rebuilding we'd need to look + // ahead for countries below the max great power rank but still within the demotion grace period. + for (CountryInstance* great_power : great_powers) { + if (great_power->get_total_rank() > max_great_power_rank && great_power->get_lose_great_power_date() < today) { + great_power->country_status = COUNTRY_STATUS_CIVILISED; + } + } + std::erase_if(great_powers, [](CountryInstance const* country) -> bool { + return country->get_country_status() != COUNTRY_STATUS_GREAT_POWER; + }); + + // Demote all secondary powers and clear the list. We will rebuilt the whole list from scratch, so there's no need to + // keep countries which are still above the max secondary power rank (they might become great powers instead anyway). + for (CountryInstance* secondary_power : secondary_powers) { + secondary_power->country_status = COUNTRY_STATUS_CIVILISED; + } + secondary_powers.clear(); + + // Calculate the maximum number of countries eligible for great or secondary power status. This accounts for the + // possibility of the max secondary power rank being higher than the max great power rank or both being zero, just + // in case someone wants to experiment with only having secondary powers when some great power slots are filled by + // countries in the demotion grace period, or having no great or secondary powers at all. + const size_t max_power_index = std::clamp(max_secondary_power_rank, max_great_power_rank, total_ranking.size()); + + for (size_t index = 0; index < max_power_index; index++) { + CountryInstance* country = total_ranking[index]; + + if (!country->is_civilised()) { + // All further countries are civilised and so ineligible for great or secondary power status. + break; + } + + if (country->is_great_power()) { + // The country already has great power status and is in the great powers list. + continue; + } + + if (great_powers.size() < max_great_power_rank && country->get_total_rank() <= max_great_power_rank) { + // The country is eligible for great power status and there are still slots available, + // so it is promoted and added to the list. + country->country_status = COUNTRY_STATUS_GREAT_POWER; + great_powers.push_back(country); + } else if (country->get_total_rank() <= max_secondary_power_rank) { + // The country is eligible for secondary power status and so is promoted and added to the list. + country->country_status = COUNTRY_STATUS_SECONDARY_POWER; + secondary_powers.push_back(country); + } + } + + // Sort the great powers list by total rank, as pre-existing great powers may have changed rank order and new great + // powers will have beeen added to the end of the list regardless of rank. + std::sort(great_powers.begin(), great_powers.end(), [](CountryInstance const* a, CountryInstance const* b) -> bool { + return a->get_total_rank() < b->get_total_rank(); + }); + + // Update the lose great power date for all great powers which are above the max great power rank. + const Date new_lose_great_power_date = today + lose_great_power_grace_days; + for (CountryInstance* great_power : great_powers) { + if (great_power->get_total_rank() <= max_great_power_rank) { + great_power->lose_great_power_date = new_lose_great_power_date; + } + } +} + CountryInstance& CountryInstanceManager::get_country_instance_from_definition(CountryDefinition const& country) { return country_instances.get_items()[country.get_index()]; } @@ -435,13 +755,16 @@ bool CountryInstanceManager::generate_country_instances( decltype(CountryInstance::inventions)::keys_t const& invention_keys, decltype(CountryInstance::upper_house)::keys_t const& ideology_keys, decltype(CountryInstance::government_flag_overrides)::keys_t const& government_type_keys, - decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_keys + decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_keys, + decltype(CountryInstance::unlocked_regiment_types)::keys_t const& unlocked_regiment_types_keys, + decltype(CountryInstance::unlocked_ship_types)::keys_t const& unlocked_ship_types_keys ) { reserve_more(country_instances, country_definition_manager.get_country_definition_count()); for (CountryDefinition const& country_definition : country_definition_manager.get_country_definitions()) { country_instances.add_item({ - &country_definition, technology_keys, invention_keys, ideology_keys, government_type_keys, pop_type_keys + &country_definition, technology_keys, invention_keys, ideology_keys, government_type_keys, pop_type_keys, + unlocked_regiment_types_keys, unlocked_ship_types_keys }); } @@ -462,11 +785,16 @@ bool CountryInstanceManager::apply_history_to_countries( if (history_map != nullptr) { CountryHistoryEntry const* oob_history_entry = nullptr; - for (CountryHistoryEntry const* entry : history_map->get_entries_up_to(date)) { - ret &= country_instance.apply_history_to_country(entry, map_instance); + for (auto const& [entry_date, entry] : history_map->get_entries()) { + if (entry_date <= date) { + ret &= country_instance.apply_history_to_country(*entry, map_instance, *this); - if (entry->get_inital_oob()) { - oob_history_entry = entry; + if (entry->get_inital_oob()) { + oob_history_entry = entry.get(); + } + } else { + // All foreign investments are applied regardless of the bookmark's date + country_instance.apply_foreign_investments(entry->get_foreign_investment(), *this); } } @@ -485,10 +813,14 @@ bool CountryInstanceManager::apply_history_to_countries( return ret; } -void CountryInstanceManager::update_gamestate() { +void CountryInstanceManager::update_gamestate( + Date today, DefineManager const& define_manager, UnitTypeManager const& unit_type_manager +) { for (CountryInstance& country : country_instances.get_items()) { - country.update_gamestate(); + country.update_gamestate(define_manager, unit_type_manager); } + + update_rankings(today, define_manager); } void CountryInstanceManager::tick() { diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index f4baf2d..c43f0cd 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -13,6 +13,7 @@ #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { + struct CountryInstanceManager; struct CountryDefinition; struct ProvinceInstance; struct State; @@ -28,12 +29,30 @@ namespace OpenVic { struct Religion; struct CountryHistoryEntry; struct MapInstance; + struct DefineManager; /* Representation of a country's mutable attributes, with a CountryDefinition that is unique at any single time * but can be swapped with other CountryInstance's CountryDefinition when switching tags. */ struct CountryInstance { friend struct CountryInstanceManager; + /* + Westernisation Progress vs Status for Uncivilised Countries: + 15 - primitive + 16 - uncivilised + 50 - uncivilised + 51 - partially westernised + */ + + enum struct country_status_t : uint8_t { + COUNTRY_STATUS_GREAT_POWER, + COUNTRY_STATUS_SECONDARY_POWER, + COUNTRY_STATUS_CIVILISED, + COUNTRY_STATUS_PARTIALLY_CIVILISED, + COUNTRY_STATUS_UNCIVILISED, + COUNTRY_STATUS_PRIMITIVE + }; + private: /* Main attributes */ // We can always assume country_definition is not null, as it is initialised from a reference and only ever changed @@ -42,16 +61,24 @@ namespace OpenVic { colour_t PROPERTY(colour); // Cached to avoid searching government overrides for every province ProvinceInstance const* PROPERTY(capital); string_set_t PROPERTY(country_flags); - - bool PROPERTY(civilised); bool PROPERTY_CUSTOM_PREFIX(releasable_vassal, is); + country_status_t PROPERTY(country_status); + Date PROPERTY(lose_great_power_date); + fixed_point_t PROPERTY(total_score); + size_t PROPERTY(total_rank); + ordered_set<ProvinceInstance*> PROPERTY(owned_provinces); ordered_set<ProvinceInstance*> PROPERTY(controlled_provinces); ordered_set<ProvinceInstance*> PROPERTY(core_provinces); ordered_set<State*> PROPERTY(states); /* Production */ + fixed_point_t PROPERTY(industrial_power); + std::vector<std::pair<State const*, fixed_point_t>> PROPERTY(industrial_power_from_states); + std::vector<std::pair<CountryInstance const*, fixed_point_t>> PROPERTY(industrial_power_from_investments); + size_t PROPERTY(industrial_rank); + fixed_point_map_t<CountryInstance const*> PROPERTY(foreign_investments); // TODO - total amount of each good produced /* Budget */ @@ -102,31 +129,41 @@ namespace OpenVic { // TODO - total amount of each good exported and imported /* Diplomacy */ - size_t PROPERTY(total_rank); fixed_point_t PROPERTY(prestige); size_t PROPERTY(prestige_rank); - fixed_point_t PROPERTY(industrial_power); - size_t PROPERTY(industrial_rank); - fixed_point_t PROPERTY(military_power); - size_t PROPERTY(military_rank); fixed_point_t PROPERTY(diplomatic_points); // TODO - colonial power, current wars /* Military */ + fixed_point_t PROPERTY(military_power); + fixed_point_t PROPERTY(military_power_from_land); + fixed_point_t PROPERTY(military_power_from_sea); + fixed_point_t PROPERTY(military_power_from_leaders); + size_t PROPERTY(military_rank); plf::colony<General> PROPERTY(generals); plf::colony<Admiral> PROPERTY(admirals); ordered_set<ArmyInstance*> PROPERTY(armies); ordered_set<NavyInstance*> PROPERTY(navies); size_t PROPERTY(regiment_count); - size_t PROPERTY(mobilisation_regiment_potential); + size_t PROPERTY(max_supported_regiment_count); + size_t PROPERTY(mobilisation_potential_regiment_count); + size_t PROPERTY(mobilisation_max_regiment_count); + fixed_point_t PROPERTY(mobilisation_impact); + fixed_point_t PROPERTY(supply_consumption); size_t PROPERTY(ship_count); fixed_point_t PROPERTY(total_consumed_ship_supply); fixed_point_t PROPERTY(max_ship_supply); fixed_point_t PROPERTY(leadership_points); fixed_point_t PROPERTY(war_exhaustion); + bool PROPERTY_CUSTOM_PREFIX(mobilised, is); + bool PROPERTY_CUSTOM_PREFIX(disarmed, is); + IndexedMap<RegimentType, bool> PROPERTY(unlocked_regiment_types); + RegimentType::allowed_cultures_t PROPERTY(allowed_regiment_cultures); + IndexedMap<ShipType, bool> PROPERTY(unlocked_ship_types); UNIT_BRANCHED_GETTER(get_unit_instance_groups, armies, navies); UNIT_BRANCHED_GETTER(get_leaders, generals, admirals); + UNIT_BRANCHED_GETTER(get_unlocked_unit_types, unlocked_regiment_types, unlocked_ship_types); CountryInstance( CountryDefinition const* new_country_definition, @@ -134,12 +171,20 @@ namespace OpenVic { decltype(inventions)::keys_t const& invention_keys, decltype(upper_house)::keys_t const& ideology_keys, decltype(government_flag_overrides)::keys_t const& government_type_keys, - decltype(pop_type_distribution)::keys_t const& pop_type_keys + decltype(pop_type_distribution)::keys_t const& pop_type_keys, + decltype(unlocked_regiment_types)::keys_t const& unlocked_regiment_types_keys, + decltype(unlocked_ship_types)::keys_t const& unlocked_ship_types_keys ); public: std::string_view get_identifier() const; + bool exists() const; + bool is_civilised() const; + bool can_colonise() const; + bool is_great_power() const; + bool is_secondary_power() const; + bool set_country_flag(std::string_view flag, bool warn); bool clear_country_flag(std::string_view flag, bool warn); bool add_owned_province(ProvinceInstance& new_province); @@ -168,21 +213,37 @@ namespace OpenVic { template<UnitType::branch_t Branch> bool remove_leader(LeaderBranched<Branch> const* leader); - bool apply_history_to_country(CountryHistoryEntry const* entry, MapInstance& map_instance); + template<UnitType::branch_t Branch> + void unlock_unit_type(UnitTypeBranched<Branch> const& unit_type); + + bool is_primary_culture(Culture const& culture) const; + // This only checks the accepted cultures list, ignoring the primary culture. + bool is_accepted_culture(Culture const& culture) const; + bool is_primary_or_accepted_culture(Culture const& culture) const; + + // Sets the investment of each country in the map (rather than adding to them), leaving the rest unchanged. + void apply_foreign_investments( + fixed_point_map_t<CountryDefinition const*> const& investments, + CountryInstanceManager const& country_instance_manager + ); + + bool apply_history_to_country( + CountryHistoryEntry const& entry, MapInstance& map_instance, CountryInstanceManager const& country_instance_manager + ); private: - void _update_production(); + void _update_production(DefineManager const& define_manager); void _update_budget(); void _update_technology(); void _update_politics(); void _update_population(); void _update_trade(); void _update_diplomacy(); - void _update_military(); + void _update_military(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager); public: - void update_gamestate(); + void update_gamestate(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager); void tick(); }; @@ -194,6 +255,16 @@ namespace OpenVic { private: IdentifierRegistry<CountryInstance> IDENTIFIER_REGISTRY(country_instance); + std::vector<CountryInstance*> PROPERTY(great_powers); + std::vector<CountryInstance*> PROPERTY(secondary_powers); + + std::vector<CountryInstance*> PROPERTY(total_ranking); + std::vector<CountryInstance*> PROPERTY(prestige_ranking); + std::vector<CountryInstance*> PROPERTY(industrial_power_ranking); + std::vector<CountryInstance*> PROPERTY(military_power_ranking); + + void update_rankings(Date today, DefineManager const& define_manager); + public: CountryInstance& get_country_instance_from_definition(CountryDefinition const& country); CountryInstance const& get_country_instance_from_definition(CountryDefinition const& country) const; @@ -204,7 +275,9 @@ namespace OpenVic { decltype(CountryInstance::inventions)::keys_t const& invention_keys, decltype(CountryInstance::upper_house)::keys_t const& ideology_keys, decltype(CountryInstance::government_flag_overrides)::keys_t const& government_type_keys, - decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_keys + decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_keys, + decltype(CountryInstance::unlocked_regiment_types)::keys_t const& unlocked_regiment_types_keys, + decltype(CountryInstance::unlocked_ship_types)::keys_t const& unlocked_ship_types_keys ); bool apply_history_to_countries( @@ -212,7 +285,7 @@ namespace OpenVic { MapInstance& map_instance ); - void update_gamestate(); + void update_gamestate(Date today, DefineManager const& define_manager, UnitTypeManager const& unit_type_manager); void tick(); }; } diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp index 145d26b..04f6292 100644 --- a/src/openvic-simulation/history/CountryHistory.cpp +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -191,6 +191,10 @@ void CountryHistoryManager::reserve_more_country_histories(size_t size) { } void CountryHistoryManager::lock_country_histories() { + for (auto [country, history_map] : mutable_iterator(country_histories)) { + history_map.sort_entries(); + } + Logger::info("Locked country history registry after registering ", country_histories.size(), " items"); locked = true; } diff --git a/src/openvic-simulation/history/HistoryMap.hpp b/src/openvic-simulation/history/HistoryMap.hpp index 1d6ec03..b062b0f 100644 --- a/src/openvic-simulation/history/HistoryMap.hpp +++ b/src/openvic-simulation/history/HistoryMap.hpp @@ -104,6 +104,20 @@ namespace OpenVic { } public: + void sort_entries() { + std::vector<Date> keys; + keys.reserve(entries.size()); + for (typename decltype(entries)::value_type const& entry : entries) { + keys.push_back(entry.first); + } + std::sort(keys.begin(), keys.end()); + ordered_map<Date, std::unique_ptr<entry_type>> new_entries; + for (Date const& key : keys) { + new_entries.emplace(key, std::move(entries[key])); + } + entries = std::move(new_entries); + } + /* Returns history entry at specific date, if date doesn't have an entry returns nullptr. */ entry_type const* get_entry(Date date) const { typename decltype(entries)::const_iterator it = entries.find(date); @@ -112,18 +126,5 @@ namespace OpenVic { } return nullptr; } - /* Returns history entries up to date as an ordered list of entries. */ - std::vector<entry_type const*> get_entries_up_to(Date end) const { - std::vector<entry_type const*> ret; - for (typename decltype(entries)::value_type const& entry : entries) { - if (entry.first <= end) { - ret.push_back(entry.second.get()); - } - } - std::sort(ret.begin(), ret.end(), [](entry_type const* lhs, entry_type const* rhs) -> bool { - return lhs->get_date() < rhs->get_date(); - }); - return ret; - } }; } diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index ca0bf4e..ef8793b 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -157,8 +157,10 @@ void ProvinceHistoryManager::lock_province_histories(MapDefinition const& map_de std::vector<ProvinceDefinition> const& provinces = map_definition.get_province_definitions(); std::vector<bool> province_checklist(provinces.size()); - for (decltype(province_histories)::value_type const& entry : province_histories) { - province_checklist[entry.first->get_index() - 1] = true; + for (auto [province, history_map] : mutable_iterator(province_histories)) { + province_checklist[province->get_index() - 1] = true; + + history_map.sort_entries(); } size_t missing = 0; diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index 56b3642..0ce8cea 100644 --- a/src/openvic-simulation/map/MapInstance.cpp +++ b/src/openvic-simulation/map/MapInstance.cpp @@ -94,11 +94,15 @@ bool MapInstance::apply_history_to_provinces( if (history_map != nullptr) { ProvinceHistoryEntry const* pop_history_entry = nullptr; - for (ProvinceHistoryEntry const* entry : history_map->get_entries_up_to(date)) { - province.apply_history_to_province(entry, country_manager); + for (auto const& [entry_date, entry] : history_map->get_entries()) { + if (entry_date > date) { + break; + } + + province.apply_history_to_province(*entry, country_manager); if (!entry->get_pops().empty()) { - pop_history_entry = entry; + pop_history_entry = entry.get(); } } @@ -114,9 +118,9 @@ bool MapInstance::apply_history_to_provinces( return ret; } -void MapInstance::update_gamestate(Date today) { +void MapInstance::update_gamestate(Date today, DefineManager const& define_manager) { for (ProvinceInstance& province : province_instances.get_items()) { - province.update_gamestate(today); + province.update_gamestate(today, define_manager); } state_manager.update_gamestate(); diff --git a/src/openvic-simulation/map/MapInstance.hpp b/src/openvic-simulation/map/MapInstance.hpp index d2d9a26..99c13d3 100644 --- a/src/openvic-simulation/map/MapInstance.hpp +++ b/src/openvic-simulation/map/MapInstance.hpp @@ -52,7 +52,7 @@ namespace OpenVic { IssueManager const& issue_manager ); - void update_gamestate(Date today); + void update_gamestate(Date today, DefineManager const& define_manager); void tick(Date today); }; } diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index ee20590..06b3f1e 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -4,6 +4,7 @@ #include "openvic-simulation/history/ProvinceHistory.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/military/UnitInstanceGroup.hpp" +#include "openvic-simulation/misc/Define.hpp" #include "openvic-simulation/politics/Ideology.hpp" using namespace OpenVic; @@ -31,7 +32,8 @@ ProvinceInstance::ProvinceInstance( pop_type_distribution { &pop_type_keys }, ideology_distribution { &ideology_keys }, culture_distribution {}, - religion_distribution {} {} + religion_distribution {}, + max_supported_regiments { 0 } {} bool ProvinceInstance::set_owner(CountryInstance* new_owner) { bool ret = true; @@ -92,6 +94,10 @@ bool ProvinceInstance::remove_core(CountryInstance& core_to_remove) { } } +bool ProvinceInstance::is_owner_core() const { + return owner != nullptr && cores.contains(owner); +} + bool ProvinceInstance::expand_building(size_t building_index) { BuildingInstance* building = buildings.get_item_by_index(building_index); if (building == nullptr) { @@ -136,7 +142,7 @@ size_t ProvinceInstance::get_pop_count() const { /* REQUIREMENTS: * MAP-65, MAP-68, MAP-70, MAP-234 */ -void ProvinceInstance::_update_pops() { +void ProvinceInstance::_update_pops(DefineManager const& define_manager) { total_population = 0; average_literacy = 0; average_consciousness = 0; @@ -147,7 +153,18 @@ void ProvinceInstance::_update_pops() { culture_distribution.clear(); religion_distribution.clear(); - for (Pop const& pop : pops) { + max_supported_regiments = 0; + + using enum colony_status_t; + + const fixed_point_t pop_size_per_regiment_multiplier = + colony_status == PROTECTORATE ? define_manager.get_pop_size_per_regiment_protectorate_multiplier() + : colony_status == COLONY ? define_manager.get_pop_size_per_regiment_colony_multiplier() + : is_owner_core() ? fixed_point_t::_1() : define_manager.get_pop_size_per_regiment_non_core_multiplier(); + + for (Pop& pop : pops) { + pop.update_gamestate(define_manager, owner, pop_size_per_regiment_multiplier); + total_population += pop.get_size(); average_literacy += pop.get_literacy(); average_consciousness += pop.get_consciousness(); @@ -157,6 +174,8 @@ void ProvinceInstance::_update_pops() { ideology_distribution += pop.get_ideologies(); culture_distribution[&pop.get_culture()] += pop.get_size(); religion_distribution[&pop.get_religion()] += pop.get_size(); + + max_supported_regiments += pop.get_max_supported_regiments(); } if (total_population > 0) { @@ -166,11 +185,11 @@ void ProvinceInstance::_update_pops() { } } -void ProvinceInstance::update_gamestate(Date today) { +void ProvinceInstance::update_gamestate(Date today, DefineManager const& define_manager) { for (BuildingInstance& building : buildings.get_items()) { building.update_gamestate(today); } - _update_pops(); + _update_pops(define_manager); } void ProvinceInstance::tick(Date today) { @@ -236,12 +255,7 @@ bool ProvinceInstance::setup(BuildingTypeManager const& building_type_manager) { return ret; } -bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const* entry, CountryInstanceManager& country_manager) { - if (entry == nullptr) { - Logger::error("Trying to apply null province history to ", get_identifier()); - return false; - } - +bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const& entry, CountryInstanceManager& country_manager) { bool ret = true; constexpr auto set_optional = []<typename T>(T& target, std::optional<T> const& source) { @@ -250,25 +264,25 @@ bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const* ent } }; - if (entry->get_owner()) { - ret &= set_owner(&country_manager.get_country_instance_from_definition(**entry->get_owner())); + if (entry.get_owner()) { + ret &= set_owner(&country_manager.get_country_instance_from_definition(**entry.get_owner())); } - if (entry->get_controller()) { - ret &= set_controller(&country_manager.get_country_instance_from_definition(**entry->get_controller())); + if (entry.get_controller()) { + ret &= set_controller(&country_manager.get_country_instance_from_definition(**entry.get_controller())); } - set_optional(colony_status, entry->get_colonial()); - set_optional(slave, entry->get_slave()); - for (auto const& [country, add] : entry->get_cores()) { + set_optional(colony_status, entry.get_colonial()); + set_optional(slave, entry.get_slave()); + for (auto const& [country, add] : entry.get_cores()) { if (add) { ret &= add_core(country_manager.get_country_instance_from_definition(*country)); } else { ret &= remove_core(country_manager.get_country_instance_from_definition(*country)); } } - set_optional(rgo, entry->get_rgo()); - set_optional(life_rating, entry->get_life_rating()); - set_optional(terrain_type, entry->get_terrain_type()); - for (auto const& [building, level] : entry->get_province_buildings()) { + set_optional(rgo, entry.get_rgo()); + set_optional(life_rating, entry.get_life_rating()); + set_optional(terrain_type, entry.get_terrain_type()); + for (auto const& [building, level] : entry.get_province_buildings()) { BuildingInstance* existing_entry = buildings.get_item_by_identifier(building->get_identifier()); if (existing_entry != nullptr) { existing_entry->set_level(level); @@ -280,8 +294,8 @@ bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const* ent ret = false; } } - // TODO: load state buildings - entry->get_state_buildings() - // TODO: party loyalties for each POP when implemented on POP side - entry->get_party_loyalties() + // TODO: load state buildings - entry.get_state_buildings() + // TODO: party loyalties for each POP when implemented on POP side - entry.get_party_loyalties() return ret; } diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp index 048ac3b..fa0be98 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -88,6 +88,7 @@ namespace OpenVic { IndexedMap<Ideology, fixed_point_t> PROPERTY(ideology_distribution); fixed_point_map_t<Culture const*> PROPERTY(culture_distribution); fixed_point_map_t<Religion const*> PROPERTY(religion_distribution); + size_t PROPERTY(max_supported_regiments); ProvinceInstance( ProvinceDefinition const& new_province_definition, decltype(pop_type_distribution)::keys_t const& pop_type_keys, @@ -95,7 +96,7 @@ namespace OpenVic { ); void _add_pop(Pop&& pop); - void _update_pops(); + void _update_pops(DefineManager const& define_manager); public: ProvinceInstance(ProvinceInstance&&) = default; @@ -115,6 +116,7 @@ namespace OpenVic { bool set_controller(CountryInstance* new_controller); bool add_core(CountryInstance& new_core); bool remove_core(CountryInstance& core_to_remove); + bool is_owner_core() const; bool expand_building(size_t building_index); @@ -122,7 +124,7 @@ namespace OpenVic { bool add_pop_vec(std::vector<PopBase> const& pop_vec); size_t get_pop_count() const; - void update_gamestate(Date today); + void update_gamestate(Date today, DefineManager const& define_manager); void tick(Date today); template<UnitType::branch_t Branch> @@ -131,7 +133,7 @@ namespace OpenVic { bool remove_unit_instance_group(UnitInstanceGroup<Branch>& group); bool setup(BuildingTypeManager const& building_type_manager); - bool apply_history_to_province(ProvinceHistoryEntry const* entry, CountryInstanceManager& country_manager); + bool apply_history_to_province(ProvinceHistoryEntry const& entry, CountryInstanceManager& country_manager); void setup_pop_test_values(IssueManager const& issue_manager); }; diff --git a/src/openvic-simulation/map/State.cpp b/src/openvic-simulation/map/State.cpp index 68f2f43..020b6f1 100644 --- a/src/openvic-simulation/map/State.cpp +++ b/src/openvic-simulation/map/State.cpp @@ -10,11 +10,20 @@ using namespace OpenVic; State::State( - StateSet const& new_state_set, CountryInstance* owner, ProvinceInstance* capital, - std::vector<ProvinceInstance*>&& provinces, ProvinceInstance::colony_status_t colony_status, + StateSet const& new_state_set, + CountryInstance* new_owner, + ProvinceInstance* new_capital, + std::vector<ProvinceInstance*>&& new_provinces, + ProvinceInstance::colony_status_t new_colony_status, decltype(pop_type_distribution)::keys_t const& pop_type_keys -) : state_set { new_state_set }, owner { owner }, capital { capital }, provinces { std::move(provinces) }, - colony_status { colony_status }, pop_type_distribution { &pop_type_keys } {} +) : state_set { new_state_set }, + owner { new_owner }, + capital { new_capital }, + provinces { std::move(new_provinces) }, + colony_status { new_colony_status }, + pop_type_distribution { &pop_type_keys }, + industrial_power { 0 }, + max_supported_regiments { 0 } {} std::string State::get_identifier() const { return StringUtils::append_string_views( @@ -29,6 +38,7 @@ void State::update_gamestate() { average_consciousness = 0; average_militancy = 0; pop_type_distribution.clear(); + max_supported_regiments = 0; for (ProvinceInstance const* province : provinces) { total_population += province->get_total_population(); @@ -40,6 +50,8 @@ void State::update_gamestate() { average_militancy += province->get_average_militancy() * province_population; pop_type_distribution += province->get_pop_type_distribution(); + + max_supported_regiments += province->get_max_supported_regiments(); } if (total_population > 0) { @@ -47,6 +59,16 @@ void State::update_gamestate() { average_consciousness /= total_population; average_militancy /= total_population; } + + // TODO - use actual values when State has factory data + const int32_t total_factory_levels_in_state = 0; + const int32_t potential_workforce_in_state = 0; // sum of worker pops, regardless of employment + const int32_t potential_employment_in_state = 0; // sum of (factory level * production method base_workforce_size) + + industrial_power = total_factory_levels_in_state * std::clamp( + (fixed_point_t { potential_workforce_in_state } / 100).floor() * 400 / potential_employment_in_state, + fixed_point_t::_0_20(), fixed_point_t::_4() + ); } /* Whether two provinces in the same region should be grouped into the same state or not. diff --git a/src/openvic-simulation/map/State.hpp b/src/openvic-simulation/map/State.hpp index 44b1947..596206a 100644 --- a/src/openvic-simulation/map/State.hpp +++ b/src/openvic-simulation/map/State.hpp @@ -31,9 +31,16 @@ namespace OpenVic { fixed_point_t PROPERTY(average_militancy); IndexedMap<PopType, fixed_point_t> PROPERTY(pop_type_distribution); + fixed_point_t PROPERTY(industrial_power); + + size_t PROPERTY(max_supported_regiments); + State( - StateSet const& new_state_set, CountryInstance* owner, ProvinceInstance* capital, - std::vector<ProvinceInstance*>&& provinces, ProvinceInstance::colony_status_t colony_status, + StateSet const& new_state_set, + CountryInstance* new_owner, + ProvinceInstance* new_capital, + std::vector<ProvinceInstance*>&& new_provinces, + ProvinceInstance::colony_status_t new_colony_status, decltype(pop_type_distribution)::keys_t const& pop_type_keys ); diff --git a/src/openvic-simulation/military/UnitInstance.cpp b/src/openvic-simulation/military/UnitInstance.cpp index d9f12b9..7c6488d 100644 --- a/src/openvic-simulation/military/UnitInstance.cpp +++ b/src/openvic-simulation/military/UnitInstance.cpp @@ -19,8 +19,8 @@ template struct OpenVic::UnitInstance<UnitType::branch_t::LAND>; template struct OpenVic::UnitInstance<UnitType::branch_t::NAVAL>; 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 } {} + std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop, bool new_mobilised +) : UnitInstance { new_name, new_regiment_type }, pop { new_pop }, mobilised { new_mobilised } {} UnitInstanceBranched<UnitType::branch_t::NAVAL>::UnitInstanceBranched( std::string_view new_name, ShipType const& new_ship_type diff --git a/src/openvic-simulation/military/UnitInstance.hpp b/src/openvic-simulation/military/UnitInstance.hpp index 5ff4503..ffbd37f 100644 --- a/src/openvic-simulation/military/UnitInstance.hpp +++ b/src/openvic-simulation/military/UnitInstance.hpp @@ -41,8 +41,11 @@ namespace OpenVic { private: Pop* PROPERTY(pop); + bool PROPERTY_CUSTOM_PREFIX(mobilised, is); - UnitInstanceBranched(std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop); + UnitInstanceBranched( + std::string_view new_name, RegimentType const& new_regiment_type, Pop* new_pop, bool new_mobilised) + ; public: UnitInstanceBranched(UnitInstanceBranched&&) = default; diff --git a/src/openvic-simulation/military/UnitInstanceGroup.cpp b/src/openvic-simulation/military/UnitInstanceGroup.cpp index 65ecf5b..09206e3 100644 --- a/src/openvic-simulation/military/UnitInstanceGroup.cpp +++ b/src/openvic-simulation/military/UnitInstanceGroup.cpp @@ -172,8 +172,11 @@ bool UnitInstanceManager::generate_unit_instance( unit_instance = &*get_unit_instances<Branch>().insert( [&unit_deployment]() -> UnitInstanceBranched<Branch> { if constexpr (Branch == UnitType::branch_t::LAND) { - // TODO - get pop from Province unit_deployment.get_home() - return { unit_deployment.get_name(), unit_deployment.get_type(), nullptr }; + return { + unit_deployment.get_name(), unit_deployment.get_type(), + nullptr, // TODO - get pop from Province unit_deployment.get_home() + false // Not mobilised + }; } else if constexpr (Branch == UnitType::branch_t::NAVAL) { return { unit_deployment.get_name(), unit_deployment.get_type() }; } diff --git a/src/openvic-simulation/military/UnitType.cpp b/src/openvic-simulation/military/UnitType.cpp index 11f7221..d4ffbee 100644 --- a/src/openvic-simulation/military/UnitType.cpp +++ b/src/openvic-simulation/military/UnitType.cpp @@ -1,5 +1,6 @@ #include "UnitType.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/map/TerrainType.hpp" using namespace OpenVic; @@ -30,6 +31,29 @@ UnitType::UnitType( supply_cost { std::move(unit_args.supply_cost) }, terrain_modifiers { std::move(unit_args.terrain_modifiers) } {} +bool UnitTypeBranched<LAND>::allowed_cultures_check_culture_in_country( + allowed_cultures_t allowed_cultures, Culture const& culture, CountryInstance const& country +) { + using enum allowed_cultures_t; + + switch (allowed_cultures) { + case ALL_CULTURES: + return true; + case ACCEPTED_CULTURES: + return country.is_primary_or_accepted_culture(culture); + case PRIMARY_CULTURE: + return country.is_primary_culture(culture); + case NO_CULTURES: + return false; + default: + Logger::error( + "Unknown allowed cultures value ", static_cast<uint32_t>(allowed_cultures), " for culture ", + culture.get_identifier(), " and country ", country.get_identifier() + ); + return false; + } +} + 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 }, diff --git a/src/openvic-simulation/military/UnitType.hpp b/src/openvic-simulation/military/UnitType.hpp index 6bd7392..6bbaca7 100644 --- a/src/openvic-simulation/military/UnitType.hpp +++ b/src/openvic-simulation/military/UnitType.hpp @@ -15,6 +15,8 @@ namespace OpenVic { struct TerrainType; struct TerrainTypeManager; + struct Culture; + struct CountryInstance; struct UnitType : HasIdentifier { using icon_t = uint32_t; @@ -94,7 +96,18 @@ namespace OpenVic { struct UnitTypeBranched<UnitType::branch_t::LAND> : UnitType { friend struct UnitTypeManager; - enum struct allowed_cultures_t { ALL_CULTURES, ACCEPTED_CULTURES, PRIMARY_CULTURE }; + // Each value is a subset of its predecessor, so smaller values contain larger values + enum struct allowed_cultures_t { ALL_CULTURES, ACCEPTED_CULTURES, PRIMARY_CULTURE, NO_CULTURES }; + + constexpr static allowed_cultures_t allowed_cultures_get_most_permissive( + allowed_cultures_t lhs, allowed_cultures_t rhs + ) { + return std::min(lhs, rhs); + } + + static bool allowed_cultures_check_culture_in_country( + allowed_cultures_t allowed_cultures, Culture const& culture, CountryInstance const& country + ); struct regiment_type_args_t { allowed_cultures_t allowed_cultures = allowed_cultures_t::ALL_CULTURES; diff --git a/src/openvic-simulation/misc/Define.cpp b/src/openvic-simulation/misc/Define.cpp index 6161842..c28aab0 100644 --- a/src/openvic-simulation/misc/Define.cpp +++ b/src/openvic-simulation/misc/Define.cpp @@ -150,15 +150,25 @@ bool DefineManager::load_define_years(Timespan& value, Define::Type type, std::s DefineManager::DefineManager() : // Date start_date { 1836, 1, 1 }, - end_date { 1936, 1, 1 } + end_date { 1936, 1, 1 }, // Country + great_power_rank { 8 }, + lose_great_power_grace_days { Timespan::from_years(1) }, + secondary_power_rank { 16 }, + country_investment_industrial_score_factor { 1 }, // Economy // Military + pop_size_per_regiment { 1000 }, + min_pop_size_for_regiment { 1000 }, + pop_size_per_regiment_protectorate_multiplier { 1 }, + pop_size_per_regiment_colony_multiplier { 1 }, + pop_size_per_regiment_non_core_multiplier { 1 }, // Diplomacy + disarmed_penalty { 0 } // Pops @@ -231,12 +241,24 @@ bool DefineManager::load_defines_file(ast::NodeCPtr root) { ret &= load_define(end_date, Date, "end_date"); // Country + ret &= load_define(great_power_rank, Country, "GREAT_NATIONS_COUNT"); + ret &= load_define_days(lose_great_power_grace_days, Country, "GREATNESS_DAYS"); + ret &= load_define(secondary_power_rank, Country, "COLONIAL_RANK"); + ret &= load_define(country_investment_industrial_score_factor, Country, "INVESTMENT_SCORE_FACTOR"); // Economy // Military + ret &= load_define(pop_size_per_regiment, Military, "POP_SIZE_PER_REGIMENT"); + ret &= load_define(min_pop_size_for_regiment, Military, "POP_MIN_SIZE_FOR_REGIMENT"); + ret &= load_define( + pop_size_per_regiment_protectorate_multiplier, Military, "POP_MIN_SIZE_FOR_REGIMENT_PROTECTORATE_MULTIPLIER" + ); + ret &= load_define(pop_size_per_regiment_colony_multiplier, Military, "POP_MIN_SIZE_FOR_REGIMENT_COLONY_MULTIPLIER"); + ret &= load_define(pop_size_per_regiment_non_core_multiplier, Military, "POP_MIN_SIZE_FOR_REGIMENT_NONCORE_MULTIPLIER"); // Diplomacy + ret &= load_define(disarmed_penalty, Diplomacy, "DISARMAMENT_ARMY_HIT"); // Pops diff --git a/src/openvic-simulation/misc/Define.hpp b/src/openvic-simulation/misc/Define.hpp index 7d8fbf7..064883c 100644 --- a/src/openvic-simulation/misc/Define.hpp +++ b/src/openvic-simulation/misc/Define.hpp @@ -1,5 +1,6 @@ #pragma once +#include "openvic-simulation/pop/Pop.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -41,12 +42,23 @@ namespace OpenVic { Date PROPERTY(end_date); // end_date // Country + size_t PROPERTY(great_power_rank); // GREAT_NATIONS_COUNT + Timespan PROPERTY(lose_great_power_grace_days); // GREATNESS_DAYS + size_t PROPERTY(secondary_power_rank); // COLONIAL_RANK + fixed_point_t PROPERTY(country_investment_industrial_score_factor); // INVESTMENT_SCORE_FACTOR // Economy // Military + Pop::pop_size_t PROPERTY(pop_size_per_regiment); // POP_SIZE_PER_REGIMENT + Pop::pop_size_t PROPERTY(min_pop_size_for_regiment); // POP_MIN_SIZE_FOR_REGIMENT + // POP_MIN_SIZE_FOR_REGIMENT_PROTECTORATE_MULTIPLIER + fixed_point_t PROPERTY(pop_size_per_regiment_protectorate_multiplier); + fixed_point_t PROPERTY(pop_size_per_regiment_colony_multiplier); // POP_MIN_SIZE_FOR_REGIMENT_COLONY_MULTIPLIER + fixed_point_t PROPERTY(pop_size_per_regiment_non_core_multiplier); // POP_MIN_SIZE_FOR_REGIMENT_NONCORE_MULTIPLIER // Diplomacy + fixed_point_t PROPERTY(disarmed_penalty); // DISARMAMENT_ARMY_HIT // Pops diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index 6dd0d1f..9221485 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -4,6 +4,7 @@ #include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/military/UnitType.hpp" +#include "openvic-simulation/misc/Define.hpp" #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/politics/Issue.hpp" #include "openvic-simulation/politics/Rebel.hpp" @@ -40,7 +41,8 @@ Pop::Pop(PopBase const& pop_base, decltype(ideologies)::keys_t const& ideology_k savings { 0 }, life_needs_fulfilled { 0 }, everyday_needs_fulfilled { 0 }, - luxury_needs_fulfilled { 0 } {} + luxury_needs_fulfilled { 0 }, + max_supported_regiments { 0 } {} void Pop::setup_pop_test_values(IssueManager const& issue_manager) { /* Returns +/- range% of size. */ @@ -125,6 +127,23 @@ void Pop::set_location(ProvinceInstance const& new_location) { } } +void Pop::update_gamestate( + DefineManager const& define_manager, CountryInstance const* owner, fixed_point_t const& pop_size_per_regiment_multiplier +) { + if (type.get_can_be_recruited()) { + if ( + size < define_manager.get_min_pop_size_for_regiment() || owner == nullptr || + !RegimentType::allowed_cultures_check_culture_in_country(owner->get_allowed_regiment_cultures(), culture, *owner) + ) { + max_supported_regiments = 0; + } else { + max_supported_regiments = (fixed_point_t::parse(size) / ( + fixed_point_t::parse(define_manager.get_pop_size_per_regiment()) * pop_size_per_regiment_multiplier + )).to_int64_t() + 1; + } + } +} + Strata::Strata(std::string_view new_identifier) : HasIdentifier { new_identifier } {} PopType::PopType( diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp index c74840f..e8cab42 100644 --- a/src/openvic-simulation/pop/Pop.hpp +++ b/src/openvic-simulation/pop/Pop.hpp @@ -26,6 +26,8 @@ namespace OpenVic { struct IssueManager; struct ProvinceInstance; struct CountryParty; + struct DefineManager; + struct CountryInstance; struct PopBase { friend struct PopManager; @@ -82,6 +84,8 @@ namespace OpenVic { fixed_point_t PROPERTY(everyday_needs_fulfilled); fixed_point_t PROPERTY(luxury_needs_fulfilled); + size_t PROPERTY(max_supported_regiments); + Pop(PopBase const& pop_base, decltype(ideologies)::keys_t const& ideology_keys); public: @@ -93,6 +97,11 @@ namespace OpenVic { void setup_pop_test_values(IssueManager const& issue_manager); void set_location(ProvinceInstance const& new_location); + + void update_gamestate( + DefineManager const& define_manager, CountryInstance const* owner, + fixed_point_t const& pop_size_per_regiment_multiplier + ); }; struct Strata : HasIdentifier { |