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/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 +++-- 7 files changed, 226 insertions(+), 76 deletions(-) (limited to 'src/openvic-simulation/map') 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(); -- cgit v1.2.3-56-ga3b1