From b5407c8794c4626d010bd0856a146095daa1042d Mon Sep 17 00:00:00 2001 From: hop311 Date: Fri, 6 Sep 2024 22:58:20 +0100 Subject: Add country ranking system + great/secondary powers --- src/headless/main.cpp | 28 ++++ src/openvic-simulation/InstanceManager.cpp | 2 +- src/openvic-simulation/country/CountryInstance.cpp | 186 ++++++++++++++++++--- src/openvic-simulation/country/CountryInstance.hpp | 53 +++++- src/openvic-simulation/misc/Define.cpp | 8 +- src/openvic-simulation/misc/Define.hpp | 3 + 6 files changed, 251 insertions(+), 29 deletions(-) diff --git a/src/headless/main.cpp b/src/headless/main.cpp index fcb5e8d..21e9f7c 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -52,6 +52,34 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests) // This triggers a gamestate update ret &= game_manager.update_clock(); + // TODO - REMOVE TEST CODE + Logger::info("===== Ranking system test... ====="); + if (game_manager.get_instance_manager()) { + const auto print_ranking_list = [](std::string_view title, std::vector const& countries) -> void { + std::string text; + for (CountryInstance const* country : countries) { + text += StringUtils::append_string_views( + "\n ", country->get_identifier(), + " - Total #", std::to_string(country->get_total_rank()), " (", country->get_total_score().to_string(1), + "), Prestige #", std::to_string(country->get_prestige_rank()), " (", country->get_prestige().to_string(1), + "), Industry #", std::to_string(country->get_industrial_rank()), " (", country->get_industrial_power().to_string(1), + "), Military #", std::to_string(country->get_military_rank()), " (", country->get_military_power().to_string(1), ")" + ); + } + Logger::info(title, ":", text); + }; + + CountryInstanceManager const& country_instance_manager = + game_manager.get_instance_manager()->get_country_instance_manager(); + + print_ranking_list("Great Powers", country_instance_manager.get_great_powers()); + print_ranking_list("Secondary Powers", country_instance_manager.get_secondary_powers()); + print_ranking_list("All countries", country_instance_manager.get_total_ranking()); + } else { + Logger::error("Instance manager not available!"); + ret = false; + } + return ret; } diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index ea30246..6f5ee4a 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -41,7 +41,7 @@ void InstanceManager::update_gamestate() { // Update gamestate... map_instance.update_gamestate(today); - country_instance_manager.update_gamestate(); + country_instance_manager.update_gamestate(today, definition_manager.get_define_manager()); gamestate_updated(); gamestate_needs_update = false; diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 3c2e7bb..2c7ff70 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( @@ -23,14 +26,19 @@ CountryInstance::CountryInstance( 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_rank { 0 }, /* Budget */ cash_stockpile { 0 }, @@ -73,16 +81,13 @@ 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_rank { 0 }, generals {}, admirals {}, armies {}, @@ -99,6 +104,26 @@ 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()); @@ -273,7 +298,9 @@ bool CountryInstance::apply_history_to_country(CountryHistoryEntry const* entry, 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()); + 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); @@ -360,7 +387,7 @@ 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 } @@ -383,6 +410,18 @@ void CountryInstance::_update_military() { } void CountryInstance::update_gamestate() { + // 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(); + + 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 +444,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()]; } @@ -485,10 +631,12 @@ bool CountryInstanceManager::apply_history_to_countries( return ret; } -void CountryInstanceManager::update_gamestate() { +void CountryInstanceManager::update_gamestate(Date today, DefineManager const& define_manager) { for (CountryInstance& country : country_instances.get_items()) { country.update_gamestate(); } + + 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..3622e29 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,21 @@ 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 PROPERTY(owned_provinces); ordered_set PROPERTY(controlled_provinces); ordered_set PROPERTY(core_provinces); ordered_set PROPERTY(states); /* Production */ + fixed_point_t PROPERTY(industrial_power); + size_t PROPERTY(industrial_rank); // TODO - total amount of each good produced /* Budget */ @@ -102,17 +126,14 @@ 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); + size_t PROPERTY(military_rank); plf::colony PROPERTY(generals); plf::colony PROPERTY(admirals); ordered_set PROPERTY(armies); @@ -140,6 +161,12 @@ namespace OpenVic { 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); @@ -194,6 +221,16 @@ namespace OpenVic { private: IdentifierRegistry IDENTIFIER_REGISTRY(country_instance); + std::vector PROPERTY(great_powers); + std::vector PROPERTY(secondary_powers); + + std::vector PROPERTY(total_ranking); + std::vector PROPERTY(prestige_ranking); + std::vector PROPERTY(industrial_power_ranking); + std::vector 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; @@ -212,7 +249,7 @@ namespace OpenVic { MapInstance& map_instance ); - void update_gamestate(); + void update_gamestate(Date today, DefineManager const& define_manager); void tick(); }; } diff --git a/src/openvic-simulation/misc/Define.cpp b/src/openvic-simulation/misc/Define.cpp index 6161842..2437954 100644 --- a/src/openvic-simulation/misc/Define.cpp +++ b/src/openvic-simulation/misc/Define.cpp @@ -150,9 +150,12 @@ 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 } // Economy @@ -231,6 +234,9 @@ 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"); // Economy diff --git a/src/openvic-simulation/misc/Define.hpp b/src/openvic-simulation/misc/Define.hpp index 7d8fbf7..4e63105 100644 --- a/src/openvic-simulation/misc/Define.hpp +++ b/src/openvic-simulation/misc/Define.hpp @@ -41,6 +41,9 @@ 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 // Economy -- cgit v1.2.3-56-ga3b1