From 67cbd14630c4344902d3fa1ddca178809da4293b Mon Sep 17 00:00:00 2001 From: hop311 Date: Sun, 21 Jul 2024 14:09:25 +0100 Subject: Fleshing out Country, State and Province instances + history --- src/headless/main.cpp | 9 + src/openvic-simulation/InstanceManager.cpp | 10 +- src/openvic-simulation/country/CountryInstance.cpp | 313 ++++++++++++++++++--- src/openvic-simulation/country/CountryInstance.hpp | 167 ++++++++--- src/openvic-simulation/dataloader/NodeTools.hpp | 7 +- src/openvic-simulation/history/CountryHistory.cpp | 13 +- src/openvic-simulation/history/CountryHistory.hpp | 4 +- src/openvic-simulation/history/ProvinceHistory.cpp | 31 +- src/openvic-simulation/history/ProvinceHistory.hpp | 3 +- src/openvic-simulation/map/MapInstance.cpp | 21 +- src/openvic-simulation/map/MapInstance.hpp | 7 +- src/openvic-simulation/map/Mapmode.cpp | 8 +- src/openvic-simulation/map/ProvinceInstance.cpp | 130 +++++++-- src/openvic-simulation/map/ProvinceInstance.hpp | 50 +++- src/openvic-simulation/map/State.cpp | 58 +++- src/openvic-simulation/map/State.hpp | 28 +- .../military/UnitInstanceGroup.cpp | 13 +- .../military/UnitInstanceGroup.hpp | 4 +- src/openvic-simulation/pop/Pop.cpp | 5 +- src/openvic-simulation/types/HasIdentifier.hpp | 17 ++ .../types/IdentifierRegistry.hpp | 6 +- src/openvic-simulation/types/IndexedMap.hpp | 6 +- 22 files changed, 735 insertions(+), 175 deletions(-) diff --git a/src/headless/main.cpp b/src/headless/main.cpp index 1acf581..23b5c93 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -25,6 +25,7 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) Logger::info("State updated"); }, nullptr }; + Logger::info("===== Loading definitions... ====="); ret &= game_manager.load_definitions( roots, [](std::string_view key, Dataloader::locale_t locale, std::string_view localisation) -> bool { @@ -40,6 +41,14 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) std::cout << "Testing Executed" << std::endl << std::endl; } + Logger::info("===== Setting up instance... ====="); + ret &= game_manager.setup_instance( + game_manager.get_definition_manager().get_history_manager().get_bookmark_manager().get_bookmark_by_index(0) + ); + + Logger::info("===== Starting game session... ====="); + ret &= game_manager.start_game_session(); + return ret; } diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index d314acd..cd5da59 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -41,6 +41,7 @@ void InstanceManager::update_gamestate() { // Update gamestate... map_instance.update_gamestate(today); + country_instance_manager.update_gamestate(); gamestate_updated(); gamestate_needs_update = false; @@ -74,12 +75,12 @@ bool InstanceManager::setup() { definition_manager.get_pop_manager().get_pop_types(), definition_manager.get_politics_manager().get_ideology_manager().get_ideologies() ); - ret &= map_instance.get_state_manager().generate_states(map_instance); ret &= country_instance_manager.generate_country_instances( definition_manager.get_country_definition_manager(), definition_manager.get_research_manager().get_technology_manager().get_technologies(), 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_ideology_manager().get_ideologies(), + definition_manager.get_pop_manager().get_pop_types() ); game_instance_setup = true; @@ -115,6 +116,7 @@ bool InstanceManager::load_bookmark(Bookmark const* new_bookmark) { bool ret = map_instance.apply_history_to_provinces( definition_manager.get_history_manager().get_province_manager(), today, + country_instance_manager, // TODO - the following argument is for generating test pop attributes definition_manager.get_politics_manager().get_issue_manager() ); @@ -123,6 +125,10 @@ bool InstanceManager::load_bookmark(Bookmark const* new_bookmark) { definition_manager.get_history_manager().get_country_manager(), today, unit_instance_manager, map_instance ); + ret &= map_instance.get_state_manager().generate_states( + map_instance, definition_manager.get_pop_manager().get_pop_types() + ); + return ret; } diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 8d2955b..0b27b78 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -2,59 +2,156 @@ #include "openvic-simulation/country/CountryDefinition.hpp" #include "openvic-simulation/history/CountryHistory.hpp" -#include "openvic-simulation/military/UnitInstanceGroup.hpp" +#include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/research/Invention.hpp" +#include "openvic-simulation/research/Technology.hpp" using namespace OpenVic; +static constexpr colour_t ERROR_COLOUR = colour_t::from_integer(0xFF0000); + CountryInstance::CountryInstance( CountryDefinition const* new_country_definition, decltype(technologies)::keys_t const& technology_keys, decltype(inventions)::keys_t const& invention_keys, - decltype(upper_house)::keys_t const& ideology_keys -) : country_definition { new_country_definition }, - primary_culture { nullptr }, - religion { nullptr }, - ruling_party { nullptr }, - last_election {}, + decltype(upper_house)::keys_t const& ideology_keys, + decltype(pop_type_distribution)::keys_t const& pop_type_keys +) : /* Main attributes */ + country_definition { new_country_definition }, + colour { ERROR_COLOUR }, capital { nullptr }, + country_flags {}, + civilised { false }, + releasable_vassal { true }, + owned_provinces {}, + controlled_provinces {}, + core_provinces {}, + states {}, + + /* Production */ + + /* Budget */ + cash_stockpile { 0 }, + + /* Technology */ + technologies { &technology_keys }, + inventions { &invention_keys }, + current_research { nullptr }, + invested_research_points { 0 }, + expected_completion_date {}, + research_point_stockpile { 0 }, + daily_research_points { 0 }, + national_literacy { 0 }, + tech_school { nullptr }, + + /* Politics */ + national_value { nullptr }, government_type { nullptr }, + last_election {}, + ruling_party { nullptr }, + upper_house { &ideology_keys }, + reforms {}, + suppression_points { 0 }, + infamy { 0 }, plurality { 0 }, - national_value { nullptr }, - civilised { false }, + revanchism { 0 }, + + /* Population */ + primary_culture { nullptr }, + accepted_cultures {}, + religion { nullptr }, + total_population { 0 }, + national_consciousness { 0 }, + national_militancy { 0 }, + pop_type_distribution { &pop_type_keys }, + national_focus_capacity { 0 }, + + /* Trade */ + + /* Diplomacy */ + total_rank { 0 }, prestige { 0 }, - upper_house { &ideology_keys }, - technologies { &technology_keys }, - inventions { &invention_keys } {} + prestige_rank { 0 }, + industrial_power { 0 }, + industrial_rank { 0 }, + military_power { 0 }, + military_rank { 0 }, + diplomatic_points { 0 }, + + /* Military */ + generals {}, + admirals {}, + armies {}, + navies {}, + regiment_count { 0 }, + mobilisation_regiment_potential { 0 }, + ship_count { 0 }, + total_consumed_ship_supply { 0 }, + max_ship_supply { 0 }, + leadership_points { 0 }, + war_exhaustion { 0 } {} std::string_view CountryInstance::get_identifier() const { - return country_definition != nullptr ? country_definition->get_identifier() : "NULL"; + return country_definition->get_identifier(); } -bool CountryInstance::add_accepted_culture(Culture const* new_accepted_culture) { - if (std::find(accepted_cultures.begin(), accepted_cultures.end(), new_accepted_culture) != accepted_cultures.end()) { +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()); + return false; + } + if (!country_flags.emplace(flag).second && warn) { Logger::warning( - "Attempted to add accepted culture ", new_accepted_culture->get_identifier(), " to country ", - country_definition->get_identifier(), ": already present!" + "Attempted to set country flag \"", flag, "\" for country ", get_identifier(), ": already set!" ); - return false; } - accepted_cultures.push_back(new_accepted_culture); return true; } -bool CountryInstance::remove_accepted_culture(Culture const* culture_to_remove) { - auto existing_entry = std::find(accepted_cultures.begin(), accepted_cultures.end(), culture_to_remove); - if (existing_entry == accepted_cultures.end()) { +bool CountryInstance::clear_country_flag(std::string_view flag, bool warn) { + if (flag.empty()) { + Logger::error("Attempted to clear empty country flag from country ", get_identifier()); + return false; + } + if (country_flags.erase(flag) == 0 && warn) { Logger::warning( - "Attempted to remove accepted culture ", culture_to_remove->get_identifier(), " from country ", - country_definition->get_identifier(), ": not present!" + "Attempted to clear country flag \"", flag, "\" from country ", get_identifier(), ": not set!" ); - return false; } - accepted_cultures.erase(existing_entry); return true; } +#define ADD_AND_REMOVE(item) \ + bool CountryInstance::add_##item(std::remove_pointer_t& new_item) { \ + if (!item##s.emplace(&new_item).second) { \ + Logger::error( \ + "Attempted to add " #item " \"", new_item.get_identifier(), "\" to country ", get_identifier(), \ + ": already present!" \ + ); \ + return false; \ + } \ + return true; \ + } \ + bool CountryInstance::remove_##item(std::remove_pointer_t& item_to_remove) { \ + if (item##s.erase(&item_to_remove) == 0) { \ + Logger::error( \ + "Attempted to remove " #item " \"", item_to_remove.get_identifier(), "\" from country ", get_identifier(), \ + ": not present!" \ + ); \ + return false; \ + } \ + return true; \ + } + +ADD_AND_REMOVE(owned_province) +ADD_AND_REMOVE(controlled_province) +ADD_AND_REMOVE(core_province) +ADD_AND_REMOVE(state) +ADD_AND_REMOVE(accepted_culture) + +#undef ADD_AND_REMOVE + bool CountryInstance::set_upper_house(Ideology const* ideology, fixed_point_t popularity) { if (ideology != nullptr) { upper_house[*ideology] = popularity; @@ -145,7 +242,7 @@ template void CountryInstance::add_leader(LeaderBranched const*); template bool CountryInstance::remove_leader(LeaderBranched const*); -bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry) { +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; @@ -161,13 +258,15 @@ bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry) set_optional(primary_culture, entry->get_primary_culture()); for (Culture const* culture : entry->get_accepted_cultures()) { - ret &= add_accepted_culture(culture); + 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()); - set_optional(capital, entry->get_capital()); + 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()); @@ -176,20 +275,158 @@ bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry) for (Reform const* reform : entry->get_reforms()) { ret &= add_reform(reform); } + set_optional(tech_school, entry->get_tech_school()); + constexpr auto set_bool_map_to_indexed_map = + [](IndexedMap& target, ordered_map 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(); + + // 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()) { + ret &= set_country_flag(flag, true); + } + for (std::string const& flag : entry->get_global_flags()) { + // TODO - set global flag + } + // entry->get_government_flag_overrides(); + for (Decision const* decision : entry->get_decisions()) { + // TODO - take decision + } return ret; } +void CountryInstance::_update_production() { + +} + +void CountryInstance::_update_budget() { + +} + +void CountryInstance::_update_technology() { + +} + +void CountryInstance::_update_politics() { + +} + +void CountryInstance::_update_population() { + total_population = 0; + national_literacy = 0; + national_consciousness = 0; + national_militancy = 0; + pop_type_distribution.clear(); + + for (auto const& state : states) { + total_population += state->get_total_population(); + + // TODO - change casting if Pop::pop_size_t changes type + const fixed_point_t state_population = fixed_point_t::parse(state->get_total_population()); + national_literacy += state->get_average_literacy() * state_population; + national_consciousness += state->get_average_consciousness() * state_population; + national_militancy += state->get_average_militancy() * state_population; + + pop_type_distribution += state->get_pop_type_distribution(); + } + + if (total_population > 0) { + national_literacy /= total_population; + national_consciousness /= total_population; + national_militancy /= total_population; + } + + // TODO - update national focus capacity +} + +void CountryInstance::_update_trade() { + // TODO - update total amount of each good exported and imported +} + +void CountryInstance::_update_diplomacy() { + // TODO - update prestige, industrial_power, military_power (ranks will be updated after all countries have calculated their scores) + // TODO - update diplomatic points and colonial power +} + +void CountryInstance::_update_military() { + regiment_count = 0; + + for (ArmyInstance const* army : armies) { + regiment_count += army->get_unit_count(); + } + + ship_count = 0; + total_consumed_ship_supply = 0; + + for (NavyInstance const* navy : navies) { + ship_count += navy->get_unit_count(); + total_consumed_ship_supply += navy->get_total_consumed_supply(); + } + + // TODO - update mobilisation_regiment_potential, max_ship_supply, leadership_points, war_exhaustion +} + +void CountryInstance::update_gamestate() { + if (country_definition != nullptr) { + const CountryDefinition::government_colour_map_t::const_iterator it = + country_definition->get_alternative_colours().find(government_type); + + if (it != country_definition->get_alternative_colours().end()) { + colour = it->second; + } else { + colour = country_definition->get_colour(); + } + } else { + colour = ERROR_COLOUR; + } + + // 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() { + +} + +CountryInstance& CountryInstanceManager::get_country_instance_from_definition(CountryDefinition const& country) { + return country_instances.get_items()[country.get_index()]; +} + +CountryInstance const& CountryInstanceManager::get_country_instance_from_definition(CountryDefinition const& country) const { + return country_instances.get_items()[country.get_index()]; +} + bool CountryInstanceManager::generate_country_instances( CountryDefinitionManager const& country_definition_manager, decltype(CountryInstance::technologies)::keys_t const& technology_keys, decltype(CountryInstance::inventions)::keys_t const& invention_keys, - decltype(CountryInstance::upper_house)::keys_t const& ideology_keys + decltype(CountryInstance::upper_house)::keys_t const& ideology_keys, + decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_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 }); + country_instances.add_item({ &country_definition, technology_keys, invention_keys, ideology_keys, pop_type_keys }); } return true; @@ -210,7 +447,7 @@ bool CountryInstanceManager::apply_history_to_countries( 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); + ret &= country_instance.apply_history_to_country(entry, map_instance); if (entry->get_inital_oob()) { oob_history_entry = entry; @@ -231,3 +468,15 @@ bool CountryInstanceManager::apply_history_to_countries( return ret; } + +void CountryInstanceManager::update_gamestate() { + for (CountryInstance& country : country_instances.get_items()) { + country.update_gamestate(); + } +} + +void CountryInstanceManager::tick() { + for (CountryInstance& country : country_instances.get_items()) { + country.tick(); + } +} diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index 1eaf398..5b26f7f 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -6,8 +6,7 @@ #include "openvic-simulation/military/Leader.hpp" #include "openvic-simulation/military/UnitInstanceGroup.hpp" -#include "openvic-simulation/research/Invention.hpp" -#include "openvic-simulation/research/Technology.hpp" +#include "openvic-simulation/pop/Pop.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/IndexedMap.hpp" @@ -15,61 +14,140 @@ namespace OpenVic { struct CountryDefinition; - struct Culture; - struct Religion; + struct ProvinceInstance; + struct State; + struct Technology; + struct Invention; + struct TechnologySchool; + struct NationalValue; + struct GovernmentType; struct CountryParty; struct Ideology; - struct ProvinceDefinition; - struct GovernmentType; - struct NationalValue; struct Reform; + struct Culture; + struct Religion; struct CountryHistoryEntry; + struct MapInstance; - /* Representation of an existing country that is currently in-game. */ + /* 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; private: - CountryDefinition const* PROPERTY_RW(country_definition); - Culture const* PROPERTY_RW(primary_culture); - std::vector PROPERTY(accepted_cultures); - Religion const* PROPERTY_RW(religion); - CountryParty const* PROPERTY_RW(ruling_party); - Date PROPERTY_RW(last_election); - IndexedMap PROPERTY(upper_house); - // TODO - should this be ProvinceInstance and/or non-const pointer? - // Currently ProvinceDefinition as that's what CountryHistoryEntry has (loaded prior to ProvinceInstance generation) - ProvinceDefinition const* PROPERTY_RW(capital); - GovernmentType const* PROPERTY_RW(government_type); - fixed_point_t PROPERTY_RW(plurality); - NationalValue const* PROPERTY_RW(national_value); - bool PROPERTY_RW(civilised); - fixed_point_t PROPERTY_RW(prestige); - std::vector PROPERTY(reforms); // TODO: should be map of reform groups to active reforms: must set defaults & validate applied history - + /* Main attributes */ + // We can always assume country_definition is not null, as it is initialised from a reference and only ever changed + // by swapping with another CountryInstance's country_definition. + CountryDefinition const* PROPERTY(country_definition); + 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); + + ordered_set PROPERTY(owned_provinces); + ordered_set PROPERTY(controlled_provinces); + ordered_set PROPERTY(core_provinces); + ordered_set PROPERTY(states); + + /* Production */ + // TODO - total amount of each good produced + + /* Budget */ + fixed_point_t PROPERTY(cash_stockpile); + // TODO - cash stockpile change over last 30 days + + /* Technology */ IndexedMap PROPERTY(technologies); IndexedMap PROPERTY(inventions); - - // TODO: Military units + OOBs; will probably need an extensible deployment class - + Technology const* PROPERTY(current_research); + fixed_point_t PROPERTY(invested_research_points); + Date PROPERTY(expected_completion_date); + fixed_point_t PROPERTY(research_point_stockpile); + fixed_point_t PROPERTY(daily_research_points); // TODO - breakdown by source + fixed_point_t PROPERTY(national_literacy); + TechnologySchool const* PROPERTY(tech_school); + // TODO - cached possible inventions with %age chance + + /* Politics */ + NationalValue const* PROPERTY(national_value); + GovernmentType const* PROPERTY(government_type); + Date PROPERTY(last_election); + CountryParty const* PROPERTY(ruling_party); + IndexedMap PROPERTY(upper_house); + std::vector PROPERTY(reforms); // TODO: should be map of reform groups to active reforms: must set defaults & validate applied history + // TODO - national issue support distribution (for just voters and for everyone) + fixed_point_t PROPERTY(suppression_points); + fixed_point_t PROPERTY(infamy); + fixed_point_t PROPERTY(plurality); + fixed_point_t PROPERTY(revanchism); + // TODO - rebel movements + + /* Population */ + Culture const* PROPERTY(primary_culture); + ordered_set PROPERTY(accepted_cultures); + Religion const* PROPERTY(religion); + Pop::pop_size_t PROPERTY(total_population); + // TODO - population change over last 30 days + fixed_point_t PROPERTY(national_consciousness); + fixed_point_t PROPERTY(national_militancy); + IndexedMap PROPERTY(pop_type_distribution); + size_t PROPERTY(national_focus_capacity) + // TODO - national foci + + /* Trade */ + // 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 */ plf::colony PROPERTY(generals); plf::colony PROPERTY(admirals); ordered_set PROPERTY(armies); ordered_set PROPERTY(navies); + size_t PROPERTY(regiment_count); + size_t PROPERTY(mobilisation_regiment_potential); + 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); UNIT_BRANCHED_GETTER(get_unit_instance_groups, armies, navies); UNIT_BRANCHED_GETTER(get_leaders, generals, admirals); CountryInstance( CountryDefinition const* new_country_definition, decltype(technologies)::keys_t const& technology_keys, - decltype(inventions)::keys_t const& invention_keys, decltype(upper_house)::keys_t const& ideology_keys + decltype(inventions)::keys_t const& invention_keys, decltype(upper_house)::keys_t const& ideology_keys, + decltype(pop_type_distribution)::keys_t const& pop_type_keys ); public: std::string_view get_identifier() const; - bool add_accepted_culture(Culture const* new_accepted_culture); - bool remove_accepted_culture(Culture const* culture_to_remove); + 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); + bool remove_owned_province(ProvinceInstance& province_to_remove); + bool add_controlled_province(ProvinceInstance& new_province); + bool remove_controlled_province(ProvinceInstance& province_to_remove); + bool add_core_province(ProvinceInstance& new_core); + bool remove_core_province(ProvinceInstance& core_to_remove); + bool add_state(State& new_state); + bool remove_state(State& state_to_remove); + + bool add_accepted_culture(Culture const& new_accepted_culture); + bool remove_accepted_culture(Culture const& culture_to_remove); /* Set a party's popularity in the upper house. */ bool set_upper_house(Ideology const* ideology, fixed_point_t popularity); bool add_reform(Reform const* new_reform); @@ -85,29 +163,50 @@ namespace OpenVic { template bool remove_leader(LeaderBranched const* leader); - bool apply_history_to_country(CountryHistoryEntry const* entry); + bool apply_history_to_country(CountryHistoryEntry const* entry, MapInstance& map_instance); + + private: + void _update_production(); + void _update_budget(); + void _update_technology(); + void _update_politics(); + void _update_population(); + void _update_trade(); + void _update_diplomacy(); + void _update_military(); + + public: + + void update_gamestate(); + void tick(); }; struct CountryDefinitionManager; struct CountryHistoryManager; struct UnitInstanceManager; - struct MapInstance; struct CountryInstanceManager { private: IdentifierRegistry IDENTIFIER_REGISTRY(country_instance); public: + CountryInstance& get_country_instance_from_definition(CountryDefinition const& country); + CountryInstance const& get_country_instance_from_definition(CountryDefinition const& country) const; + bool generate_country_instances( CountryDefinitionManager const& country_definition_manager, decltype(CountryInstance::technologies)::keys_t const& technology_keys, decltype(CountryInstance::inventions)::keys_t const& invention_keys, - decltype(CountryInstance::upper_house)::keys_t const& ideology_keys + decltype(CountryInstance::upper_house)::keys_t const& ideology_keys, + decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_keys ); bool apply_history_to_countries( CountryHistoryManager const& history_manager, Date date, UnitInstanceManager& unit_instance_manager, MapInstance& map_instance ); + + void update_gamestate(); + void tick(); }; } diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 18faffb..d019cce 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -12,7 +12,6 @@ #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/Date.hpp" -#include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/IndexedMap.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/types/Vector.hpp" @@ -528,7 +527,7 @@ namespace OpenVic { }; } - template T, typename... SetArgs> + template Callback auto set_callback_pointer(tsl::ordered_set& set, bool warn = false) { return [&set, warn](T const& val) -> bool { if (set.emplace(&val).second) { @@ -539,7 +538,7 @@ namespace OpenVic { }; } - template Key, typename Value, typename... MapArgs> + template Callback auto map_callback( tsl::ordered_map& map, Key const* key, bool warn = false ) { @@ -561,7 +560,7 @@ namespace OpenVic { Logger::error("Null key in map_callback"); return false; } - Value& map_value = map[*key]; + typename IndexedMap::value_ref_t map_value = map[*key]; bool ret = true; if (map_value != Value {}) { Logger::warn_or_error(warn, "Duplicate map entry with key: \"", key, "\""); diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp index 935a769..145d26b 100644 --- a/src/openvic-simulation/history/CountryHistory.cpp +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -47,14 +47,7 @@ bool CountryHistoryMap::_load_history_entry( " when it actually belongs to ", reform.get_reform_group(), " in history of ", entry.get_country() ); } - if (std::find(entry.reforms.begin(), entry.reforms.end(), &reform) != entry.reforms.end()) { - Logger::error( - "Redefinition of reform ", reform.get_identifier(), " in history of ", entry.get_country() - ); - return false; - } - entry.reforms.push_back(&reform); - return true; + return set_callback_pointer(entry.reforms)(reform); })(value); } @@ -81,9 +74,7 @@ bool CountryHistoryMap::_load_history_entry( ), "primary_culture", ZERO_OR_ONE, culture_manager.expect_culture_identifier(assign_variable_callback_pointer_opt(entry.primary_culture)), - "culture", ZERO_OR_MORE, culture_manager.expect_culture_identifier( - vector_callback_pointer(entry.accepted_cultures) - ), + "culture", ZERO_OR_MORE, culture_manager.expect_culture_identifier(set_callback_pointer(entry.accepted_cultures)), "religion", ZERO_OR_ONE, definition_manager.get_pop_manager().get_religion_manager().expect_religion_identifier( assign_variable_callback_pointer_opt(entry.religion) ), diff --git a/src/openvic-simulation/history/CountryHistory.hpp b/src/openvic-simulation/history/CountryHistory.hpp index c74841d..d6a6997 100644 --- a/src/openvic-simulation/history/CountryHistory.hpp +++ b/src/openvic-simulation/history/CountryHistory.hpp @@ -32,7 +32,7 @@ namespace OpenVic { CountryDefinition const& PROPERTY(country); std::optional PROPERTY(primary_culture); - std::vector PROPERTY(accepted_cultures); + ordered_set PROPERTY(accepted_cultures); std::optional PROPERTY(religion); std::optional PROPERTY(ruling_party); std::optional PROPERTY(last_election); @@ -43,7 +43,7 @@ namespace OpenVic { std::optional PROPERTY(national_value); std::optional PROPERTY_CUSTOM_PREFIX(civilised, is); std::optional PROPERTY(prestige); - std::vector PROPERTY(reforms); + ordered_set PROPERTY(reforms); std::optional PROPERTY(inital_oob); std::optional PROPERTY(tech_school); ordered_map PROPERTY(technologies); diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index 1fa6e90..ca0bf4e 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -30,6 +30,33 @@ bool ProvinceHistoryMap::_load_history_entry( { "0", STATE }, { "1", PROTECTORATE }, { "2", COLONY } }; + const auto set_core_instruction = [&entry](bool add) { + return [&entry, add](CountryDefinition const& country) -> bool { + const auto it = entry.cores.find(&country); + if (it == entry.cores.end()) { + // No current core instruction + entry.cores.emplace(&country, add); + return true; + } else if (it->second == add) { + // Desired core instruction already exists + Logger::warning( + "Duplicate attempt to ", add ? "add" : "remove", " core of country ", country.get_identifier(), + " ", add ? "to" : "from", " province history of ", entry.get_province() + ); + return true; + } else { + // Opposite core instruction exists + entry.cores.erase(it); + Logger::warning( + "Attempted to ", add ? "add" : "remove", " core of country ", country.get_identifier(), + " ", add ? "to" : "from", " province history of ", entry.get_province(), + " after previously ", add ? "adding" : "removing", " it" + ); + return true; + } + }; + }; + return expect_dictionary_keys_and_default( [this, &definition_manager, &building_type_manager, &entry]( std::string_view key, ast::NodeCPtr value) -> bool { @@ -60,10 +87,10 @@ bool ProvinceHistoryMap::_load_history_entry( assign_variable_callback_pointer_opt(entry.controller, true) ), "add_core", ZERO_OR_MORE, country_definition_manager.expect_country_definition_identifier( - vector_callback_pointer(entry.add_cores) + set_core_instruction(true) ), "remove_core", ZERO_OR_MORE, country_definition_manager.expect_country_definition_identifier( - vector_callback_pointer(entry.remove_cores) + set_core_instruction(false) ), "colonial", ZERO_OR_ONE, expect_identifier(expect_mapped_string(colony_status_map, assign_variable_callback(entry.colonial))), diff --git a/src/openvic-simulation/history/ProvinceHistory.hpp b/src/openvic-simulation/history/ProvinceHistory.hpp index 85853d7..99ea2af 100644 --- a/src/openvic-simulation/history/ProvinceHistory.hpp +++ b/src/openvic-simulation/history/ProvinceHistory.hpp @@ -31,8 +31,7 @@ namespace OpenVic { std::optional PROPERTY(controller); std::optional PROPERTY(colonial); std::optional PROPERTY(slave); - std::vector PROPERTY(add_cores); - std::vector PROPERTY(remove_cores); + ordered_map PROPERTY(cores); std::optional PROPERTY(rgo); std::optional PROPERTY(life_rating); std::optional PROPERTY(terrain_type); diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index 986b102..56b3642 100644 --- a/src/openvic-simulation/map/MapInstance.cpp +++ b/src/openvic-simulation/map/MapInstance.cpp @@ -10,20 +10,12 @@ MapInstance::MapInstance(MapDefinition const& new_map_definition) : map_definition { new_map_definition }, selected_province { nullptr }, highest_province_population { 0 }, total_map_population { 0 } {} -ProvinceInstance* MapInstance::get_province_instance_from_const(ProvinceDefinition const* province) { - if (province != nullptr) { - return get_province_instance_by_index(province->get_index()); - } else { - return nullptr; - } +ProvinceInstance& MapInstance::get_province_instance_from_definition(ProvinceDefinition const& province) { + return province_instances.get_items()[province.get_index() - 1]; } -ProvinceInstance const* MapInstance::get_province_instance_from_const(ProvinceDefinition const* province) const { - if (province != nullptr) { - return get_province_instance_by_index(province->get_index()); - } else { - return nullptr; - } +ProvinceInstance const& MapInstance::get_province_instance_from_definition(ProvinceDefinition const& province) const { + return province_instances.get_items()[province.get_index() - 1]; } void MapInstance::set_selected_province(ProvinceDefinition::index_t index) { @@ -89,7 +81,8 @@ bool MapInstance::setup( } bool MapInstance::apply_history_to_provinces( - ProvinceHistoryManager const& history_manager, Date date, IssueManager const& issue_manager + ProvinceHistoryManager const& history_manager, Date date, CountryInstanceManager& country_manager, + IssueManager const& issue_manager ) { bool ret = true; @@ -102,7 +95,7 @@ bool MapInstance::apply_history_to_provinces( ProvinceHistoryEntry const* pop_history_entry = nullptr; for (ProvinceHistoryEntry const* entry : history_map->get_entries_up_to(date)) { - province.apply_history_to_province(entry); + province.apply_history_to_province(entry, country_manager); if (!entry->get_pops().empty()) { pop_history_entry = entry; diff --git a/src/openvic-simulation/map/MapInstance.hpp b/src/openvic-simulation/map/MapInstance.hpp index 00bd638..d2d9a26 100644 --- a/src/openvic-simulation/map/MapInstance.hpp +++ b/src/openvic-simulation/map/MapInstance.hpp @@ -35,8 +35,8 @@ namespace OpenVic { IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_INDEX_OFFSET(province_instance, 1); - ProvinceInstance* get_province_instance_from_const(ProvinceDefinition const* province); - ProvinceInstance const* get_province_instance_from_const(ProvinceDefinition const* province) const; + ProvinceInstance& get_province_instance_from_definition(ProvinceDefinition const& province); + ProvinceInstance const& get_province_instance_from_definition(ProvinceDefinition const& province) const; void set_selected_province(ProvinceDefinition::index_t index); ProvinceInstance* get_selected_province(); @@ -48,7 +48,8 @@ namespace OpenVic { decltype(ProvinceInstance::ideology_distribution)::keys_t const& ideology_keys ); bool apply_history_to_provinces( - ProvinceHistoryManager const& history_manager, Date date, IssueManager const& issue_manager + ProvinceHistoryManager const& history_manager, Date date, CountryInstanceManager& country_manager, + IssueManager const& issue_manager ); void update_gamestate(Date today); diff --git a/src/openvic-simulation/map/Mapmode.cpp b/src/openvic-simulation/map/Mapmode.cpp index c47a07a..cab67d1 100644 --- a/src/openvic-simulation/map/Mapmode.cpp +++ b/src/openvic-simulation/map/Mapmode.cpp @@ -2,7 +2,7 @@ #include -#include "openvic-simulation/country/CountryDefinition.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/map/MapDefinition.hpp" #include "openvic-simulation/map/MapInstance.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" @@ -90,7 +90,7 @@ static constexpr colour_argb_t DEFAULT_COLOUR_WHITE = (0xFFFFFF_argb).with_alpha * national focus, RGO, population density, sphere of influence, ranking and migration. */ static constexpr colour_argb_t DEFAULT_COLOUR_GREY = (0x7F7F7F_argb).with_alpha(ALPHA_VALUE); -template T, typename P> +template requires(std::same_as || std::same_as) static constexpr auto get_colour_mapmode(T const*(P::*get_item)() const) { return [get_item](MapInstance const&, ProvinceInstance const& province) -> Mapmode::base_stripe_t { @@ -114,7 +114,7 @@ static constexpr auto get_colour_mapmode(T const*(P::*get_item)() const) { }; } -template T> +template static constexpr Mapmode::base_stripe_t shaded_mapmode(fixed_point_map_t const& map) { const std::pair, fixed_point_map_const_iterator_t> largest = get_largest_two_items(map); @@ -132,7 +132,7 @@ static constexpr Mapmode::base_stripe_t shaded_mapmode(fixed_point_map_t T> +template static constexpr auto shaded_mapmode(fixed_point_map_t const&(ProvinceInstance::*get_map)() const) { return [get_map](MapInstance const&, ProvinceInstance const& province) -> Mapmode::base_stripe_t { return shaded_mapmode((province.*get_map)()); diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index 6b20cd2..ee20590 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -1,9 +1,10 @@ #include "ProvinceInstance.hpp" -#include "openvic-simulation/country/CountryDefinition.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/history/ProvinceHistory.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/military/UnitInstanceGroup.hpp" +#include "openvic-simulation/politics/Ideology.hpp" using namespace OpenVic; @@ -32,6 +33,65 @@ ProvinceInstance::ProvinceInstance( culture_distribution {}, religion_distribution {} {} +bool ProvinceInstance::set_owner(CountryInstance* new_owner) { + bool ret = true; + + if (owner != new_owner) { + if (owner != nullptr) { + ret &= owner->remove_owned_province(*this); + } + + owner = new_owner; + + if (owner != nullptr) { + ret &= owner->add_owned_province(*this); + } + } + + return ret; +} + +bool ProvinceInstance::set_controller(CountryInstance* new_controller) { + bool ret = true; + + if (controller != new_controller) { + if (controller != nullptr) { + ret &= controller->remove_controlled_province(*this); + } + + controller = new_controller; + + if (controller != nullptr) { + ret &= controller->add_controlled_province(*this); + } + } + + return ret; +} + +bool ProvinceInstance::add_core(CountryInstance& new_core) { + if (cores.emplace(&new_core).second) { + return new_core.add_core_province(*this); + } else { + Logger::error( + "Attempted to add core \"", new_core.get_identifier(), "\" to country ", get_identifier(), ": already exists!" + ); + return false; + } +} + +bool ProvinceInstance::remove_core(CountryInstance& core_to_remove) { + if (cores.erase(&core_to_remove) > 0) { + return core_to_remove.remove_core_province(*this); + } else { + Logger::error( + "Attempted to remove core \"", core_to_remove.get_identifier(), "\" from country ", get_identifier(), + ": does not exist!" + ); + return false; + } +} + bool ProvinceInstance::expand_building(size_t building_index) { BuildingInstance* building = buildings.get_item_by_index(building_index); if (building == nullptr) { @@ -43,7 +103,7 @@ bool ProvinceInstance::expand_building(size_t building_index) { void ProvinceInstance::_add_pop(Pop&& pop) { pop.set_location(*this); - pops.push_back(std::move(pop)); + pops.insert(std::move(pop)); } bool ProvinceInstance::add_pop(Pop&& pop) { @@ -78,17 +138,32 @@ size_t ProvinceInstance::get_pop_count() const { */ void ProvinceInstance::_update_pops() { total_population = 0; + average_literacy = 0; + average_consciousness = 0; + average_militancy = 0; + pop_type_distribution.clear(); ideology_distribution.clear(); culture_distribution.clear(); religion_distribution.clear(); + for (Pop const& pop : pops) { total_population += pop.get_size(); + average_literacy += pop.get_literacy(); + average_consciousness += pop.get_consciousness(); + average_militancy += pop.get_militancy(); + pop_type_distribution[pop.get_type()] += pop.get_size(); ideology_distribution += pop.get_ideologies(); culture_distribution[&pop.get_culture()] += pop.get_size(); religion_distribution[&pop.get_religion()] += pop.get_size(); } + + if (total_population > 0) { + average_literacy /= total_population; + average_consciousness /= total_population; + average_militancy /= total_population; + } } void ProvinceInstance::update_gamestate(Date today) { @@ -161,39 +236,38 @@ bool ProvinceInstance::setup(BuildingTypeManager const& building_type_manager) { return ret; } -bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const* entry) { +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; } - if (entry->get_life_rating()) life_rating = *entry->get_life_rating(); - if (entry->get_colonial()) colony_status = *entry->get_colonial(); - if (entry->get_rgo()) rgo = *entry->get_rgo(); - if (entry->get_terrain_type()) terrain_type = *entry->get_terrain_type(); - if (entry->get_owner()) owner = *entry->get_owner(); - if (entry->get_controller()) controller = *entry->get_controller(); - if (entry->get_slave()) slave = *entry->get_slave(); - for (CountryDefinition const* core : entry->get_remove_cores()) { - const typename decltype(cores)::iterator existing_core = std::find(cores.begin(), cores.end(), core); - if (existing_core != cores.end()) { - cores.erase(existing_core); - } else { - Logger::warning( - "Trying to remove non-existent core ", core->get_identifier(), " from province ", get_identifier() - ); + + bool ret = true; + + constexpr auto set_optional = [](T& target, std::optional const& source) { + if (source) { + target = *source; } + }; + + if (entry->get_owner()) { + ret &= set_owner(&country_manager.get_country_instance_from_definition(**entry->get_owner())); } - for (CountryDefinition const* core : entry->get_add_cores()) { - const typename decltype(cores)::iterator existing_core = std::find(cores.begin(), cores.end(), core); - if (existing_core == cores.end()) { - cores.push_back(core); + 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()) { + if (add) { + ret &= add_core(country_manager.get_country_instance_from_definition(*country)); } else { - Logger::warning( - "Trying to add already-existing core ", core->get_identifier(), " to province ", get_identifier() - ); + ret &= remove_core(country_manager.get_country_instance_from_definition(*country)); } } - bool ret = true; + 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) { @@ -206,8 +280,8 @@ bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const* ent ret = false; } } - // TODO: load state buildings - // TODO: party loyalties for each POP when implemented on POP side + // 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 ec06e0c..048ac3b 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -1,5 +1,7 @@ #pragma once +#include + #include "openvic-simulation/economy/BuildingInstance.hpp" #include "openvic-simulation/military/UnitInstance.hpp" #include "openvic-simulation/military/UnitType.hpp" @@ -13,7 +15,7 @@ namespace OpenVic { struct ProvinceDefinition; struct TerrainType; struct State; - struct CountryDefinition; + struct CountryInstance; struct Crime; struct GoodDefinition; struct Ideology; @@ -22,6 +24,7 @@ namespace OpenVic { struct BuildingTypeManager; struct ProvinceHistoryEntry; struct IssueManager; + struct CountryInstanceManager; template struct UnitInstanceGroup; @@ -39,17 +42,32 @@ namespace OpenVic { enum struct colony_status_t : uint8_t { STATE, PROTECTORATE, COLONY }; + static constexpr std::string_view get_colony_status_string(colony_status_t colony_status) { + using enum colony_status_t; + switch (colony_status) { + case STATE: + return "state"; + case PROTECTORATE: + return "protectorate"; + case COLONY: + return "colony"; + default: + return "unknown colony status"; + } + } + private: ProvinceDefinition const& PROPERTY(province_definition); TerrainType const* PROPERTY(terrain_type); life_rating_t PROPERTY(life_rating); colony_status_t PROPERTY(colony_status); - State const* PROPERTY_RW(state); - CountryDefinition const* PROPERTY(owner); - CountryDefinition const* PROPERTY(controller); - // Cores being CountryDefinitions means then they won't be affected by tag switched (as desired) - std::vector PROPERTY(cores); + State* PROPERTY_RW(state); + + CountryInstance* PROPERTY(owner); + CountryInstance* PROPERTY(controller); + ordered_set PROPERTY(cores); + bool PROPERTY(slave); Crime const* PROPERTY_RW(crime); // TODO - change this into a factory-like structure @@ -60,8 +78,12 @@ namespace OpenVic { UNIT_BRANCHED_GETTER(get_unit_instance_groups, armies, navies); - std::vector PROPERTY(pops); + plf::colony PROPERTY(pops); // TODO - replace with a more easily vectorisable container? Pop::pop_size_t PROPERTY(total_population); + // TODO - population change (growth + migration), monthly totals + breakdown by source/destination + fixed_point_t PROPERTY(average_literacy); + fixed_point_t PROPERTY(average_consciousness); + fixed_point_t PROPERTY(average_militancy); IndexedMap PROPERTY(pop_type_distribution); IndexedMap PROPERTY(ideology_distribution); fixed_point_map_t PROPERTY(culture_distribution); @@ -82,6 +104,18 @@ namespace OpenVic { return province_definition; } + constexpr CountryInstance* get_owner() { + return owner; + } + constexpr CountryInstance* get_controller() { + return controller; + } + + bool set_owner(CountryInstance* new_owner); + bool set_controller(CountryInstance* new_controller); + bool add_core(CountryInstance& new_core); + bool remove_core(CountryInstance& core_to_remove); + bool expand_building(size_t building_index); bool add_pop(Pop&& pop); @@ -97,7 +131,7 @@ namespace OpenVic { bool remove_unit_instance_group(UnitInstanceGroup& group); bool setup(BuildingTypeManager const& building_type_manager); - bool apply_history_to_province(ProvinceHistoryEntry const* entry); + 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 42d84cc..68f2f43 100644 --- a/src/openvic-simulation/map/State.cpp +++ b/src/openvic-simulation/map/State.cpp @@ -1,23 +1,51 @@ #include "State.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/map/MapDefinition.hpp" #include "openvic-simulation/map/MapInstance.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/map/Region.hpp" +#include "openvic-simulation/utility/StringUtils.hpp" using namespace OpenVic; State::State( - StateSet const& new_state_set, CountryDefinition const* owner, ProvinceInstance* capital, - std::vector&& provinces, ProvinceInstance::colony_status_t colony_status + StateSet const& new_state_set, CountryInstance* owner, ProvinceInstance* capital, + std::vector&& provinces, ProvinceInstance::colony_status_t 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 } {} + colony_status { colony_status }, pop_type_distribution { &pop_type_keys } {} + +std::string State::get_identifier() const { + return StringUtils::append_string_views( + state_set.get_region().get_identifier(), "_", owner->get_identifier(), "_", + ProvinceInstance::get_colony_status_string(colony_status) + ); +} void State::update_gamestate() { total_population = 0; + average_literacy = 0; + average_consciousness = 0; + average_militancy = 0; + pop_type_distribution.clear(); for (ProvinceInstance const* province : provinces) { total_population += province->get_total_population(); + + // TODO - change casting if Pop::pop_size_t changes type + const fixed_point_t province_population = fixed_point_t::parse(province->get_total_population()); + average_literacy += province->get_average_literacy() * province_population; + average_consciousness += province->get_average_consciousness() * province_population; + average_militancy += province->get_average_militancy() * province_population; + + pop_type_distribution += province->get_pop_type_distribution(); + } + + if (total_population > 0) { + average_literacy /= total_population; + average_consciousness /= total_population; + average_militancy /= total_population; } } @@ -39,7 +67,9 @@ void StateSet::update_gamestate() { } } -bool StateManager::add_state_set(MapInstance& map_instance, Region const& region) { +bool StateManager::add_state_set( + MapInstance& map_instance, Region const& region, decltype(State::pop_type_distribution)::keys_t const& pop_type_keys +) { if (region.get_meta()) { Logger::error("Cannot use meta region \"", region.get_identifier(), "\" as state template!"); return false; @@ -54,7 +84,7 @@ bool StateManager::add_state_set(MapInstance& map_instance, Region const& region for (ProvinceDefinition const* province : region.get_provinces()) { - ProvinceInstance* province_instance = map_instance.get_province_instance_from_const(province); + ProvinceInstance* province_instance = &map_instance.get_province_instance_from_definition(*province); // add to existing state if shared owner & status... for (std::vector& provinces : temp_provinces) { @@ -83,22 +113,28 @@ bool StateManager::add_state_set(MapInstance& map_instance, Region const& region for (std::vector& provinces : temp_provinces) { ProvinceInstance* capital = provinces.front(); - state_set.states.push_back( + CountryInstance* owner = capital->get_owner(); + + State& state = *state_set.states.insert( /* TODO: capital province logic */ - { state_set, capital->get_owner(), capital, std::move(provinces), capital->get_colony_status() } + { state_set, owner, capital, std::move(provinces), capital->get_colony_status(), pop_type_keys } ); - State const& state = state_set.states.back(); - for (ProvinceInstance* province : state.get_provinces()) { province->set_state(&state); } + + if (owner != nullptr) { + owner->add_state(state); + } } return true; } -bool StateManager::generate_states(MapInstance& map_instance) { +bool StateManager::generate_states( + MapInstance& map_instance, decltype(State::pop_type_distribution)::keys_t const& pop_type_keys +) { MapDefinition const& map_definition = map_instance.get_map_definition(); state_sets.clear(); @@ -109,7 +145,7 @@ bool StateManager::generate_states(MapInstance& map_instance) { for (Region const& region : map_definition.get_regions()) { if (!region.get_meta()) { - if (add_state_set(map_instance, region)) { + if (add_state_set(map_instance, region, pop_type_keys)) { state_count += state_sets.back().get_state_count(); } else { ret = false; diff --git a/src/openvic-simulation/map/State.hpp b/src/openvic-simulation/map/State.hpp index 6111668..44b1947 100644 --- a/src/openvic-simulation/map/State.hpp +++ b/src/openvic-simulation/map/State.hpp @@ -1,7 +1,10 @@ #pragma once +#include #include +#include + #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/pop/Pop.hpp" #include "openvic-simulation/utility/Getters.hpp" @@ -9,7 +12,7 @@ namespace OpenVic { struct StateManager; struct StateSet; - struct CountryDefinition; + struct CountryInstance; struct ProvinceInstance; struct State { @@ -17,19 +20,26 @@ namespace OpenVic { private: StateSet const& PROPERTY(state_set); - CountryDefinition const* PROPERTY(owner); + CountryInstance* PROPERTY(owner); ProvinceInstance* PROPERTY(capital); std::vector PROPERTY(provinces); ProvinceInstance::colony_status_t PROPERTY(colony_status); Pop::pop_size_t PROPERTY(total_population); + fixed_point_t PROPERTY(average_literacy); + fixed_point_t PROPERTY(average_consciousness); + fixed_point_t PROPERTY(average_militancy); + IndexedMap PROPERTY(pop_type_distribution); State( - StateSet const& new_state_set, CountryDefinition const* owner, ProvinceInstance* capital, - std::vector&& provinces, ProvinceInstance::colony_status_t colony_status + StateSet const& new_state_set, CountryInstance* owner, ProvinceInstance* capital, + std::vector&& provinces, ProvinceInstance::colony_status_t colony_status, + decltype(pop_type_distribution)::keys_t const& pop_type_keys ); public: + std::string get_identifier() const; + void update_gamestate(); }; @@ -38,8 +48,7 @@ namespace OpenVic { struct StateSet { friend struct StateManager; - // TODO - use a container that supports adding and removing items without invalidating pointers - using states_t = std::vector; + using states_t = plf::colony; private: Region const& PROPERTY(region); @@ -60,13 +69,16 @@ namespace OpenVic { private: std::vector PROPERTY(state_sets); - bool add_state_set(MapInstance& map_instance, Region const& region); + bool add_state_set( + MapInstance& map_instance, Region const& region, + decltype(State::pop_type_distribution)::keys_t const& pop_type_keys + ); public: /* Creates states from current province gamestate & regions, sets province state value. * After this function, the `regions` property is unmanaged and must be carefully updated and * validated by functions that modify it. */ - bool generate_states(MapInstance& map_instance); + bool generate_states(MapInstance& map_instance, decltype(State::pop_type_distribution)::keys_t const& pop_type_keys); void reset(); diff --git a/src/openvic-simulation/military/UnitInstanceGroup.cpp b/src/openvic-simulation/military/UnitInstanceGroup.cpp index 80ca3a9..07736f3 100644 --- a/src/openvic-simulation/military/UnitInstanceGroup.cpp +++ b/src/openvic-simulation/military/UnitInstanceGroup.cpp @@ -155,6 +155,17 @@ UnitInstanceGroupBranched::UnitInstanceGroupBranched( std::vector&& new_units ) : UnitInstanceGroup { new_name, std::move(new_units) } {} + +fixed_point_t UnitInstanceGroupBranched::get_total_consumed_supply() const { + fixed_point_t total_consumed_supply = 0; + + for (ShipInstance const* ship : get_units()) { + total_consumed_supply += ship->get_unit_type().get_supply_consumption_score(); + } + + return total_consumed_supply; +} + template bool UnitInstanceManager::generate_unit_instance( UnitDeployment const& unit_deployment, UnitInstanceBranched*& unit_instance @@ -220,7 +231,7 @@ bool UnitInstanceManager::generate_unit_instance_group( }); ret &= unit_instance_group.set_position( - map_instance.get_province_instance_from_const(unit_deployment_group.get_location()) + &map_instance.get_province_instance_from_definition(*unit_deployment_group.get_location()) ); ret &= unit_instance_group.set_country(&country); diff --git a/src/openvic-simulation/military/UnitInstanceGroup.hpp b/src/openvic-simulation/military/UnitInstanceGroup.hpp index 4fe0cba..78ff289 100644 --- a/src/openvic-simulation/military/UnitInstanceGroup.hpp +++ b/src/openvic-simulation/military/UnitInstanceGroup.hpp @@ -90,7 +90,7 @@ namespace OpenVic { friend struct UnitInstanceManager; private: - std::vector PROPERTY(carried_armies); + std::vector PROPERTY(carried_armies); UnitInstanceGroupBranched( std::string_view new_name, @@ -99,6 +99,8 @@ namespace OpenVic { public: UnitInstanceGroupBranched(UnitInstanceGroupBranched&&) = default; + + fixed_point_t get_total_consumed_supply() const; }; using NavyInstance = UnitInstanceGroupBranched; diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index 0ecd937..c421de3 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -1,6 +1,7 @@ #include "Pop.hpp" #include "openvic-simulation/country/CountryDefinition.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/politics/Ideology.hpp" @@ -119,7 +120,9 @@ void Pop::set_location(ProvinceInstance const& new_location) { // TODO - update location dependent attributes - votes.set_keys(location->get_owner() != nullptr ? &location->get_owner()->get_parties() : nullptr); + votes.set_keys( + location->get_owner() != nullptr ? &location->get_owner()->get_country_definition()->get_parties() : nullptr + ); // TODO - calculate vote distribution } } diff --git a/src/openvic-simulation/types/HasIdentifier.hpp b/src/openvic-simulation/types/HasIdentifier.hpp index 925d58b..74961e3 100644 --- a/src/openvic-simulation/types/HasIdentifier.hpp +++ b/src/openvic-simulation/types/HasIdentifier.hpp @@ -2,6 +2,8 @@ #include #include +#include +#include #include #include "openvic-simulation/types/Colour.hpp" @@ -49,6 +51,11 @@ namespace OpenVic { return obj != nullptr ? stream << *obj : stream << ""; } + template + concept HasGetIdentifier = requires(T const& t) { + { t.get_identifier() } -> std::same_as; + }; + /* * Base class for objects with associated colour information. */ @@ -71,6 +78,11 @@ namespace OpenVic { using HasColour = _HasColour; using HasAlphaColour = _HasColour; + template + concept HasGetColour = requires(T const& t) { + { t.get_colour() } -> IsColour; + }; + /* * Base class for objects with a unique string identifier and associated colour information. */ @@ -107,4 +119,9 @@ namespace OpenVic { HasIndex& operator=(HasIndex const&) = delete; HasIndex& operator=(HasIndex&&) = delete; }; + + template + concept HasGetIndex = requires(T const& t) { + { t.get_index() } -> std::unsigned_integral; + }; } diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index a73ebcc..03cdbb1 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -4,6 +4,7 @@ #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" +#include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/utility/Getters.hpp" #include "openvic-simulation/utility/Logger.hpp" @@ -27,11 +28,6 @@ namespace OpenVic { return true; } - template - concept HasGetIdentifier = requires(T const& t) { - { t.get_identifier() } -> std::same_as; - }; - /* Registry Value Info - the type that is being registered, and a unique identifier string getter. */ template concept RegistryValueInfo = requires( diff --git a/src/openvic-simulation/types/IndexedMap.hpp b/src/openvic-simulation/types/IndexedMap.hpp index 68effb9..30cf5cd 100644 --- a/src/openvic-simulation/types/IndexedMap.hpp +++ b/src/openvic-simulation/types/IndexedMap.hpp @@ -14,6 +14,8 @@ namespace OpenVic { using container_t = std::vector; using key_t = Key; using value_t = Value; + using value_ref_t = container_t::reference; + using value_const_ref_t = container_t::const_reference; using keys_t = std::vector; using container_t::operator[]; @@ -91,11 +93,11 @@ namespace OpenVic { } } - constexpr value_t& operator[](key_t const& key) { + constexpr value_ref_t operator[](key_t const& key) { return container_t::operator[](get_index_from_item(key)); } - constexpr value_t const& operator[](key_t const& key) const { + constexpr value_const_ref_t operator[](key_t const& key) const { return container_t::operator[](get_index_from_item(key)); } -- cgit v1.2.3-56-ga3b1