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