aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Nemrav <>2024-09-17 18:44:23 +0200
committer Nemrav <>2024-09-17 18:44:23 +0200
commitc232250cc9819ae8bf165a5707a926db928dcb2b (patch)
tree1067808ef3d362ddae62b8d5994138434c5b7178
parentf090f8f33a41d8707795c8963fa67073ba90f554 (diff)
parentd8c04cbe53188d4717f8c49f918e01657dbf3440 (diff)
Merge branch 'master' into gfxobject
-rw-r--r--src/headless/main.cpp31
-rw-r--r--src/openvic-simulation/GameManager.cpp9
-rw-r--r--src/openvic-simulation/GameManager.hpp2
-rw-r--r--src/openvic-simulation/InstanceManager.cpp24
-rw-r--r--src/openvic-simulation/InstanceManager.hpp1
-rw-r--r--src/openvic-simulation/country/CountryDefinition.cpp19
-rw-r--r--src/openvic-simulation/country/CountryDefinition.hpp3
-rw-r--r--src/openvic-simulation/country/CountryInstance.cpp886
-rw-r--r--src/openvic-simulation/country/CountryInstance.hpp183
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.hpp18
-rw-r--r--src/openvic-simulation/dataloader/Vic2PathSearch.cpp6
-rw-r--r--src/openvic-simulation/economy/BuildingType.cpp11
-rw-r--r--src/openvic-simulation/economy/BuildingType.hpp5
-rw-r--r--src/openvic-simulation/economy/GoodDefinition.cpp32
-rw-r--r--src/openvic-simulation/economy/GoodDefinition.hpp9
-rw-r--r--src/openvic-simulation/economy/GoodInstance.hpp2
-rw-r--r--src/openvic-simulation/history/CountryHistory.cpp16
-rw-r--r--src/openvic-simulation/history/CountryHistory.hpp3
-rw-r--r--src/openvic-simulation/history/HistoryMap.hpp27
-rw-r--r--src/openvic-simulation/history/ProvinceHistory.cpp6
-rw-r--r--src/openvic-simulation/interface/UI.cpp12
-rw-r--r--src/openvic-simulation/interface/UI.hpp1
-rw-r--r--src/openvic-simulation/map/Crime.hpp2
-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/Mapmode.cpp12
-rw-r--r--src/openvic-simulation/map/ProvinceDefinition.cpp24
-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.cpp261
-rw-r--r--src/openvic-simulation/misc/Define.hpp67
-rw-r--r--src/openvic-simulation/misc/Modifier.cpp9
-rw-r--r--src/openvic-simulation/misc/Modifier.hpp4
-rw-r--r--src/openvic-simulation/politics/Issue.cpp32
-rw-r--r--src/openvic-simulation/politics/Issue.hpp10
-rw-r--r--src/openvic-simulation/politics/Rule.cpp14
-rw-r--r--src/openvic-simulation/politics/Rule.hpp4
-rw-r--r--src/openvic-simulation/pop/Pop.cpp29
-rw-r--r--src/openvic-simulation/pop/Pop.hpp9
-rw-r--r--src/openvic-simulation/pop/Religion.cpp15
-rw-r--r--src/openvic-simulation/research/Technology.cpp28
-rw-r--r--src/openvic-simulation/research/Technology.hpp17
-rw-r--r--src/openvic-simulation/types/IndexedMap.hpp10
-rw-r--r--src/openvic-simulation/types/Vector.hpp2
51 files changed, 1688 insertions, 349 deletions
diff --git a/src/headless/main.cpp b/src/headless/main.cpp
index ecce333..21e9f7c 100644
--- a/src/headless/main.cpp
+++ b/src/headless/main.cpp
@@ -49,6 +49,37 @@ static bool run_headless(Dataloader::path_vector_t const& roots, bool run_tests)
Logger::info("===== Starting game session... =====");
ret &= game_manager.start_game_session();
+ // 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/GameManager.cpp b/src/openvic-simulation/GameManager.cpp
index 2577b54..2980dbd 100644
--- a/src/openvic-simulation/GameManager.cpp
+++ b/src/openvic-simulation/GameManager.cpp
@@ -74,3 +74,12 @@ bool GameManager::start_game_session() {
return instance_manager->start_game_session();
}
+
+bool GameManager::update_clock() {
+ if (!instance_manager) {
+ Logger::error("Cannot update clock - instance manager uninitialised!");
+ return false;
+ }
+
+ return instance_manager->update_clock();
+}
diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp
index fdbcb6a..91de279 100644
--- a/src/openvic-simulation/GameManager.hpp
+++ b/src/openvic-simulation/GameManager.hpp
@@ -38,5 +38,7 @@ namespace OpenVic {
bool setup_instance(Bookmark const* bookmark);
bool start_game_session();
+
+ bool update_clock();
};
}
diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp
index cd5da59..c9ce24a 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;
@@ -77,10 +79,16 @@ bool InstanceManager::setup() {
);
ret &= country_instance_manager.generate_country_instances(
definition_manager.get_country_definition_manager(),
+ definition_manager.get_economy_manager().get_building_type_manager().get_building_types(),
definition_manager.get_research_manager().get_technology_manager().get_technologies(),
definition_manager.get_research_manager().get_invention_manager().get_inventions(),
definition_manager.get_politics_manager().get_ideology_manager().get_ideologies(),
- definition_manager.get_pop_manager().get_pop_types()
+ definition_manager.get_politics_manager().get_issue_manager().get_reform_groups(),
+ definition_manager.get_politics_manager().get_government_type_manager().get_government_types(),
+ definition_manager.get_crime_manager().get_crime_modifiers(),
+ 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;
@@ -147,6 +155,16 @@ bool InstanceManager::start_game_session() {
return true;
}
+bool InstanceManager::update_clock() {
+ if (!is_game_session_started()) {
+ Logger::error("Cannot update clock - game session not started!");
+ return false;
+ }
+
+ simulation_clock.conditionally_advance_game();
+ return true;
+}
+
bool InstanceManager::expand_selected_province_building(size_t building_index) {
set_gamestate_needs_update();
ProvinceInstance* province = map_instance.get_selected_province();
diff --git a/src/openvic-simulation/InstanceManager.hpp b/src/openvic-simulation/InstanceManager.hpp
index 9cf346a..cfb5447 100644
--- a/src/openvic-simulation/InstanceManager.hpp
+++ b/src/openvic-simulation/InstanceManager.hpp
@@ -58,6 +58,7 @@ namespace OpenVic {
bool setup();
bool load_bookmark(Bookmark const* new_bookmark);
bool start_game_session();
+ bool update_clock();
bool expand_selected_province_building(size_t building_index);
};
diff --git a/src/openvic-simulation/country/CountryDefinition.cpp b/src/openvic-simulation/country/CountryDefinition.cpp
index a9e7487..12a7a22 100644
--- a/src/openvic-simulation/country/CountryDefinition.cpp
+++ b/src/openvic-simulation/country/CountryDefinition.cpp
@@ -138,24 +138,31 @@ node_callback_t CountryDefinitionManager::load_country_party(
std::string_view party_name;
Date start_date, end_date;
Ideology const* ideology;
- CountryParty::policy_map_t policies;
+ CountryParty::policy_map_t policies { &politics_manager.get_issue_manager().get_issue_groups() };
bool ret = expect_dictionary_keys_and_default(
[&politics_manager, &policies, &party_name](std::string_view key, ast::NodeCPtr value) -> bool {
return politics_manager.get_issue_manager().expect_issue_group_str(
[&politics_manager, &policies, value, &party_name](IssueGroup const& group) -> bool {
- if (policies.contains(&group)) {
+ CountryParty::policy_map_t::value_ref_t policy = policies[group];
+
+ if (policy != nullptr) {
Logger::error("Country party ", party_name, " has duplicate entry for ", group.get_identifier());
return false;
}
+
return politics_manager.get_issue_manager().expect_issue_identifier(
- [&policies, &group](Issue const& issue) -> bool {
+ [&group, &policy](Issue const& issue) -> bool {
if (&issue.get_group() == &group) {
- return map_callback(policies, &group)(&issue);
+ policy = &issue;
+ return true;
}
+
// TODO - change this back to error/false once TGC no longer has this issue
- Logger::warning("Invalid policy ", issue.get_identifier(), ", group is ",
- issue.get_group().get_identifier(), " when ", group.get_identifier(), " was expected");
+ Logger::warning(
+ "Invalid policy ", issue.get_identifier(), ", group is ",
+ issue.get_group().get_identifier(), " when ", group.get_identifier(), " was expected."
+ );
return true;
}
)(value);
diff --git a/src/openvic-simulation/country/CountryDefinition.hpp b/src/openvic-simulation/country/CountryDefinition.hpp
index f04796a..6462be1 100644
--- a/src/openvic-simulation/country/CountryDefinition.hpp
+++ b/src/openvic-simulation/country/CountryDefinition.hpp
@@ -14,6 +14,7 @@
#include "openvic-simulation/types/Colour.hpp"
#include "openvic-simulation/types/Date.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"
+#include "openvic-simulation/types/IndexedMap.hpp"
#include "openvic-simulation/types/OrderedContainers.hpp"
namespace OpenVic {
@@ -23,7 +24,7 @@ namespace OpenVic {
struct CountryParty : HasIdentifierAndColour {
friend struct CountryDefinitionManager;
- using policy_map_t = ordered_map<IssueGroup const*, Issue const*>;
+ using policy_map_t = IndexedMap<IssueGroup, Issue const*>;
private:
const Date PROPERTY(start_date);
diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp
index 0b27b78..183b0c8 100644
--- a/src/openvic-simulation/country/CountryInstance.cpp
+++ b/src/openvic-simulation/country/CountryInstance.cpp
@@ -2,41 +2,60 @@
#include "openvic-simulation/country/CountryDefinition.hpp"
#include "openvic-simulation/history/CountryHistory.hpp"
+#include "openvic-simulation/map/Crime.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(
CountryDefinition const* new_country_definition,
- decltype(technologies)::keys_t const& technology_keys,
- decltype(inventions)::keys_t const& invention_keys,
+ decltype(unlocked_building_types)::keys_t const& building_type_keys,
+ decltype(unlocked_technologies)::keys_t const& technology_keys,
+ decltype(unlocked_inventions)::keys_t const& invention_keys,
decltype(upper_house)::keys_t const& ideology_keys,
- decltype(pop_type_distribution)::keys_t const& pop_type_keys
+ decltype(reforms)::keys_t const& reform_keys,
+ decltype(government_flag_overrides)::keys_t const& government_type_keys,
+ decltype(unlocked_crimes)::keys_t const& crime_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 {},
+ unlocked_building_types { &building_type_keys },
/* Budget */
cash_stockpile { 0 },
/* Technology */
- technologies { &technology_keys },
- inventions { &invention_keys },
+ unlocked_technologies { &technology_keys },
+ unlocked_inventions { &invention_keys },
current_research { nullptr },
invested_research_points { 0 },
expected_completion_date {},
@@ -51,11 +70,16 @@ CountryInstance::CountryInstance(
last_election {},
ruling_party { nullptr },
upper_house { &ideology_keys },
- reforms {},
+ reforms { &reform_keys },
+ total_administrative_multiplier { 0 },
+ rule_set {},
+ government_flag_overrides { &government_type_keys },
+ flag_government_type { nullptr },
suppression_points { 0 },
infamy { 0 },
plurality { 0 },
revanchism { 0 },
+ unlocked_crimes { &crime_keys },
/* Population */
primary_culture { nullptr },
@@ -70,32 +94,89 @@ 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 },
+ gas_attack_unlock_level { 0 },
+ gas_defence_unlock_level { 0 },
+ unit_variant_unlock_levels {} {
+
+ for (BuildingType const& building_type : *unlocked_building_types.get_keys()) {
+ if (building_type.is_default_enabled()) {
+ unlock_building_type(building_type);
+ }
+ }
+
+ for (Crime const& crime : *unlocked_crimes.get_keys()) {
+ if (crime.is_default_active()) {
+ unlock_crime(crime);
+ }
+ }
+
+ 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());
@@ -162,27 +243,37 @@ bool CountryInstance::set_upper_house(Ideology const* ideology, fixed_point_t po
}
}
-bool CountryInstance::add_reform(Reform const* new_reform) {
- if (std::find(reforms.begin(), reforms.end(), new_reform) != reforms.end()) {
- Logger::warning(
- "Attempted to add reform \"", new_reform, "\" to country ", get_identifier(), ": already present!"
- );
- return false;
+bool CountryInstance::set_ruling_party(CountryParty const& new_ruling_party) {
+ if (ruling_party != &new_ruling_party) {
+ ruling_party = &new_ruling_party;
+
+ return update_rule_set();
+ } else {
+ return true;
}
- reforms.push_back(new_reform);
- return true;
}
-bool CountryInstance::remove_reform(Reform const* reform_to_remove) {
- auto existing_entry = std::find(reforms.begin(), reforms.end(), reform_to_remove);
- if (existing_entry == reforms.end()) {
- Logger::warning(
- "Attempted to remove reform \"", reform_to_remove, "\" from country ", get_identifier(), ": not present!"
- );
- return false;
+bool CountryInstance::add_reform(Reform const& new_reform) {
+ ReformGroup const& reform_group = new_reform.get_reform_group();
+ decltype(reforms)::value_ref_t reform = reforms[reform_group];
+
+ if (reform != &new_reform) {
+ if (reform_group.is_administrative()) {
+ if (reform != nullptr) {
+ total_administrative_multiplier -= reform->get_administrative_multiplier();
+ }
+ total_administrative_multiplier += new_reform.get_administrative_multiplier();
+ }
+
+ reform = &new_reform;
+
+ // TODO - if new_reform.get_reform_group().get_type().is_uncivilised() ?
+ // TODO - new_reform.get_on_execute_trigger() / new_reform.get_on_execute_effect() ?
+
+ return update_rule_set();
+ } else {
+ return true;
}
- reforms.erase(existing_entry);
- return true;
}
template<UnitType::branch_t Branch>
@@ -242,12 +333,336 @@ 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());
+template<UnitType::branch_t Branch>
+bool CountryInstance::modify_unit_type_unlock(UnitTypeBranched<Branch> const& unit_type, unlock_level_t unlock_level_change) {
+ IndexedMap<UnitTypeBranched<Branch>, unlock_level_t>& unlocked_unit_types = get_unlocked_unit_types<Branch>();
+
+ typename IndexedMap<UnitTypeBranched<Branch>, unlock_level_t>::value_ref_t unlock_level = unlocked_unit_types[unit_type];
+
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for unit type ", unit_type.get_identifier(), " in country ",
+ get_identifier(), " to invalid value: current level = ", static_cast<int64_t>(unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(unlock_level + unlock_level_change)
+ );
return false;
}
+ unlock_level += unlock_level_change;
+
+ return true;
+}
+
+template bool CountryInstance::modify_unit_type_unlock(UnitTypeBranched<UnitType::branch_t::LAND> const&, unlock_level_t);
+template bool CountryInstance::modify_unit_type_unlock(UnitTypeBranched<UnitType::branch_t::NAVAL> const&, unlock_level_t);
+
+bool CountryInstance::modify_unit_type_unlock(UnitType const& unit_type, unlock_level_t unlock_level_change) {
+ using enum UnitType::branch_t;
+
+ switch (unit_type.get_branch()) {
+ case LAND:
+ return modify_unit_type_unlock(static_cast<UnitTypeBranched<LAND> const&>(unit_type), unlock_level_change);
+ case NAVAL:
+ return modify_unit_type_unlock(static_cast<UnitTypeBranched<NAVAL> const&>(unit_type), unlock_level_change);
+ default:
+ Logger::error(
+ "Attempted to change unlock level for unit type \"", unit_type.get_identifier(), "\" with invalid branch ",
+ static_cast<uint32_t>(unit_type.get_branch()), " is unlocked for country ", get_identifier()
+ );
+ return false;
+ }
+}
+
+bool CountryInstance::unlock_unit_type(UnitType const& unit_type) {
+ return modify_unit_type_unlock(unit_type, 1);
+}
+
+bool CountryInstance::is_unit_type_unlocked(UnitType const& unit_type) const {
+ using enum UnitType::branch_t;
+
+ switch (unit_type.get_branch()) {
+ case LAND:
+ return unlocked_regiment_types[static_cast<UnitTypeBranched<LAND> const&>(unit_type)] > 0;
+ case NAVAL:
+ return unlocked_ship_types[static_cast<UnitTypeBranched<NAVAL> const&>(unit_type)] > 0;
+ default:
+ Logger::error(
+ "Attempted to check if unit type \"", unit_type.get_identifier(), "\" with invalid branch ",
+ static_cast<uint32_t>(unit_type.get_branch()), " is unlocked for country ", get_identifier()
+ );
+ return false;
+ }
+}
+
+bool CountryInstance::modify_building_type_unlock(BuildingType const& building_type, unlock_level_t unlock_level_change) {
+ decltype(unlocked_building_types)::value_ref_t unlock_level = unlocked_building_types[building_type];
+
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for building type ", building_type.get_identifier(), " in country ",
+ get_identifier(), " to invalid value: current level = ", static_cast<int64_t>(unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(unlock_level + unlock_level_change)
+ );
+ return false;
+ }
+
+ unlock_level += unlock_level_change;
+
+ return true;
+}
+
+bool CountryInstance::unlock_building_type(BuildingType const& building_type) {
+ return modify_building_type_unlock(building_type, 1);
+}
+
+bool CountryInstance::is_building_type_unlocked(BuildingType const& building_type) const {
+ return unlocked_building_types[building_type] > 0;
+}
+
+bool CountryInstance::modify_crime_unlock(Crime const& crime, unlock_level_t unlock_level_change) {
+ decltype(unlocked_crimes)::value_ref_t unlock_level = unlocked_crimes[crime];
+
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for crime ", crime.get_identifier(), " in country ",
+ get_identifier(), " to invalid value: current level = ", static_cast<int64_t>(unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(unlock_level + unlock_level_change)
+ );
+ return false;
+ }
+
+ unlock_level += unlock_level_change;
+
+ return true;
+}
+
+bool CountryInstance::unlock_crime(Crime const& crime) {
+ return modify_crime_unlock(crime, 1);
+}
+
+bool CountryInstance::is_crime_unlocked(Crime const& crime) const {
+ return unlocked_crimes[crime] > 0;
+}
+
+bool CountryInstance::modify_gas_attack_unlock(unlock_level_t unlock_level_change) {
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (gas_attack_unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for gas attack in country ", get_identifier(),
+ " to invalid value: current level = ", static_cast<int64_t>(gas_attack_unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(gas_attack_unlock_level + unlock_level_change)
+ );
+ return false;
+ }
+
+ gas_attack_unlock_level += unlock_level_change;
+
+ return true;
+}
+
+bool CountryInstance::unlock_gas_attack() {
+ return modify_gas_attack_unlock(1);
+}
+
+bool CountryInstance::is_gas_attack_unlocked() const {
+ return gas_attack_unlock_level > 0;
+}
+
+bool CountryInstance::modify_gas_defence_unlock(unlock_level_t unlock_level_change) {
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (gas_defence_unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for gas defence in country ", get_identifier(),
+ " to invalid value: current level = ", static_cast<int64_t>(gas_defence_unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(gas_defence_unlock_level + unlock_level_change)
+ );
+ return false;
+ }
+
+ gas_defence_unlock_level += unlock_level_change;
+
+ return true;
+}
+
+bool CountryInstance::unlock_gas_defence() {
+ return modify_gas_defence_unlock(1);
+}
+
+bool CountryInstance::is_gas_defence_unlocked() const {
+ return gas_defence_unlock_level > 0;
+}
+
+bool CountryInstance::modify_unit_variant_unlock(unit_variant_t unit_variant, unlock_level_t unlock_level_change) {
+ if (unit_variant < 1) {
+ Logger::error("Trying to modify unlock level for default unit variant 0");
+ return false;
+ }
+
+ if (unit_variant_unlock_levels.size() < unit_variant) {
+ unit_variant_unlock_levels.resize(unit_variant);
+ }
+
+ unlock_level_t& unlock_level = unit_variant_unlock_levels[unit_variant - 1];
+
+ bool ret = true;
+
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for unit variant ", static_cast<uint64_t>(unit_variant), " in country ",
+ get_identifier(), " to invalid value: current level = ", static_cast<int64_t>(unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(unlock_level + unlock_level_change)
+ );
+ ret = false;
+ } else {
+ unlock_level += unlock_level_change;
+ }
+
+ while (!unit_variant_unlock_levels.empty() && unit_variant_unlock_levels.back() < 1) {
+ unit_variant_unlock_levels.pop_back();
+ }
+
+ return ret;
+}
+
+bool CountryInstance::unlock_unit_variant(unit_variant_t unit_variant) {
+ return modify_unit_variant_unlock(unit_variant, 1);
+}
+
+CountryInstance::unit_variant_t CountryInstance::get_max_unlocked_unit_variant() const {
+ return unit_variant_unlock_levels.size();
+}
+
+bool CountryInstance::modify_technology_unlock(Technology const& technology, unlock_level_t unlock_level_change) {
+ decltype(unlocked_technologies)::value_ref_t unlock_level = unlocked_technologies[technology];
+
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for technology ", technology.get_identifier(), " in country ",
+ get_identifier(), " to invalid value: current level = ", static_cast<int64_t>(unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(unlock_level + unlock_level_change)
+ );
+ return false;
+ }
+
+ unlock_level += unlock_level_change;
+
+ bool ret = true;
+
+ // TODO - bool unciv_military ?
+
+ if (technology.get_unit_variant().has_value()) {
+ ret &= modify_unit_variant_unlock(*technology.get_unit_variant(), unlock_level_change);
+ }
+ for (UnitType const* unit : technology.get_activated_units()) {
+ ret &= modify_unit_type_unlock(*unit, unlock_level_change);
+ }
+ for (BuildingType const* building : technology.get_activated_buildings()) {
+ ret &= modify_building_type_unlock(*building, unlock_level_change);
+ }
+
+ return ret;
+}
+
+bool CountryInstance::set_technology_unlock_level(Technology const& technology, unlock_level_t unlock_level) {
+ const unlock_level_t unlock_level_change = unlock_level - unlocked_technologies[technology];
+ return unlock_level_change != 0 ? modify_technology_unlock(technology, unlock_level_change) : true;
+}
+
+bool CountryInstance::unlock_technology(Technology const& technology) {
+ return modify_technology_unlock(technology, 1);
+}
+
+bool CountryInstance::is_technology_unlocked(Technology const& technology) const {
+ return unlocked_technologies[technology] > 0;
+}
+
+bool CountryInstance::modify_invention_unlock(Invention const& invention, unlock_level_t unlock_level_change) {
+ decltype(unlocked_inventions)::value_ref_t unlock_level = unlocked_inventions[invention];
+
+ // This catches subtracting below 0 or adding above the int types maximum value
+ if (unlock_level + unlock_level_change < 0) {
+ Logger::error(
+ "Attempted to change unlock level for invention ", invention.get_identifier(), " in country ",
+ get_identifier(), " to invalid value: current level = ", static_cast<int64_t>(unlock_level), ", change = ",
+ static_cast<int64_t>(unlock_level_change), ", invalid new value = ",
+ static_cast<int64_t>(unlock_level + unlock_level_change)
+ );
+ return false;
+ }
+
+ unlock_level += unlock_level_change;
+
+ bool ret = true;
+
+ // TODO - handle invention.is_news()
+
+ for (UnitType const* unit : invention.get_activated_units()) {
+ ret &= modify_unit_type_unlock(*unit, unlock_level_change);
+ }
+ for (BuildingType const* building : invention.get_activated_buildings()) {
+ ret &= modify_building_type_unlock(*building, unlock_level_change);
+ }
+ for (Crime const* crime : invention.get_enabled_crimes()) {
+ ret &= modify_crime_unlock(*crime, unlock_level_change);
+ }
+ if (invention.will_unlock_gas_attack()) {
+ ret &= modify_gas_attack_unlock(unlock_level_change);
+ }
+ if (invention.will_unlock_gas_defence()) {
+ ret &= modify_gas_defence_unlock(unlock_level_change);
+ }
+
+ return ret;
+}
+
+bool CountryInstance::set_invention_unlock_level(Invention const& invention, unlock_level_t unlock_level) {
+ const unlock_level_t unlock_level_change = unlock_level - unlocked_inventions[invention];
+ return unlock_level_change != 0 ? modify_invention_unlock(invention, unlock_level_change) : true;
+}
+
+bool CountryInstance::unlock_invention(Invention const& invention) {
+ return modify_invention_unlock(invention, 1);
+}
+
+bool CountryInstance::is_invention_unlocked(Invention const& invention) const {
+ return unlocked_inventions[invention] > 0;
+}
+
+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;
@@ -256,60 +671,100 @@ 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()) {
- ret &= add_reform(reform);
- }
- set_optional(tech_school, entry->get_tech_school());
+ set_optional(religion, entry.get_religion());
+ if (entry.get_ruling_party()) {
+ ret &= set_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());
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();
+ for (auto const& [technology, level] : entry.get_technologies()) {
+ ret &= set_technology_unlock_level(*technology, level);
+ }
+ for (auto const& [invention, activated] : entry.get_inventions()) {
+ ret &= set_invention_unlock_level(*invention, activated ? 1 : 0);
+ }
+ 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
}
- // 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() {
@@ -331,7 +786,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
@@ -357,11 +812,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) {
@@ -376,10 +831,111 @@ 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
+}
+
+bool CountryInstance::update_rule_set() {
+ rule_set.clear();
+
+ if (ruling_party != nullptr) {
+ for (Issue const* issue : ruling_party->get_policies()) {
+ if (issue != nullptr) {
+ rule_set |= issue->get_rules();
+ }
+ }
+ }
+
+ for (Reform const* reform : reforms) {
+ if (reform != nullptr) {
+ rule_set |= reform->get_rules();
+ }
+ }
+
+ return rule_set.trim_and_resolve_conflicts(true);
}
-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);
@@ -393,21 +949,138 @@ void CountryInstance::update_gamestate() {
colour = ERROR_COLOUR;
}
- // Order of updates might need to be changed/functions split up to account for dependencies
- _update_production();
- _update_budget();
- _update_technology();
- _update_politics();
- _update_population();
- _update_trade();
- _update_diplomacy();
- _update_military();
+ if (government_type != nullptr) {
+ flag_government_type = government_flag_overrides[*government_type];
+
+ if (flag_government_type == nullptr) {
+ flag_government_type = government_type;
+ }
+ } else {
+ flag_government_type = nullptr;
+ }
}
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()];
}
@@ -418,18 +1091,38 @@ CountryInstance const& CountryInstanceManager::get_country_instance_from_definit
bool CountryInstanceManager::generate_country_instances(
CountryDefinitionManager const& country_definition_manager,
- decltype(CountryInstance::technologies)::keys_t const& technology_keys,
- decltype(CountryInstance::inventions)::keys_t const& invention_keys,
+ decltype(CountryInstance::unlocked_building_types)::keys_t const& building_type_keys,
+ decltype(CountryInstance::unlocked_technologies)::keys_t const& technology_keys,
+ decltype(CountryInstance::unlocked_inventions)::keys_t const& invention_keys,
decltype(CountryInstance::upper_house)::keys_t const& ideology_keys,
- decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_keys
+ decltype(CountryInstance::reforms)::keys_t const& reform_keys,
+ decltype(CountryInstance::government_flag_overrides)::keys_t const& government_type_keys,
+ decltype(CountryInstance::unlocked_crimes)::keys_t const& crime_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());
+ bool ret = true;
+
for (CountryDefinition const& country_definition : country_definition_manager.get_country_definitions()) {
- country_instances.add_item({ &country_definition, technology_keys, invention_keys, ideology_keys, pop_type_keys });
+ ret &= country_instances.add_item({
+ &country_definition,
+ building_type_keys,
+ technology_keys,
+ invention_keys,
+ ideology_keys,
+ reform_keys,
+ government_type_keys,
+ crime_keys,
+ pop_type_keys,
+ unlocked_regiment_types_keys,
+ unlocked_ship_types_keys
+ });
}
- return true;
+ return ret;
}
bool CountryInstanceManager::apply_history_to_countries(
@@ -446,11 +1139,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);
}
}
@@ -469,10 +1167,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 5b26f7f..a7128aa 100644
--- a/src/openvic-simulation/country/CountryInstance.hpp
+++ b/src/openvic-simulation/country/CountryInstance.hpp
@@ -6,6 +6,7 @@
#include "openvic-simulation/military/Leader.hpp"
#include "openvic-simulation/military/UnitInstanceGroup.hpp"
+#include "openvic-simulation/politics/Rule.hpp"
#include "openvic-simulation/pop/Pop.hpp"
#include "openvic-simulation/types/Date.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"
@@ -13,6 +14,7 @@
#include "openvic-simulation/utility/Getters.hpp"
namespace OpenVic {
+ struct CountryInstanceManager;
struct CountryDefinition;
struct ProvinceInstance;
struct State;
@@ -23,17 +25,41 @@ namespace OpenVic {
struct GovernmentType;
struct CountryParty;
struct Ideology;
+ struct ReformGroup;
struct Reform;
+ struct Crime;
struct Culture;
struct Religion;
+ struct BuildingType;
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
+ };
+
+ using unlock_level_t = int8_t;
+ using unit_variant_t = uint8_t;
+
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 +68,25 @@ 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);
+ IndexedMap<BuildingType, unlock_level_t> PROPERTY(unlocked_building_types);
// TODO - total amount of each good produced
/* Budget */
@@ -59,8 +94,8 @@ namespace OpenVic {
// TODO - cash stockpile change over last 30 days
/* Technology */
- IndexedMap<Technology, bool> PROPERTY(technologies);
- IndexedMap<Invention, bool> PROPERTY(inventions);
+ IndexedMap<Technology, unlock_level_t> PROPERTY(unlocked_technologies);
+ IndexedMap<Invention, unlock_level_t> PROPERTY(unlocked_inventions);
Technology const* PROPERTY(current_research);
fixed_point_t PROPERTY(invested_research_points);
Date PROPERTY(expected_completion_date);
@@ -76,12 +111,17 @@ namespace OpenVic {
Date PROPERTY(last_election);
CountryParty const* PROPERTY(ruling_party);
IndexedMap<Ideology, fixed_point_t> PROPERTY(upper_house);
- std::vector<Reform const*> PROPERTY(reforms); // TODO: should be map of reform groups to active reforms: must set defaults & validate applied history
+ IndexedMap<ReformGroup, Reform const*> PROPERTY(reforms);
+ fixed_point_t PROPERTY(total_administrative_multiplier);
+ RuleSet PROPERTY(rule_set);
// TODO - national issue support distribution (for just voters and for everyone)
+ IndexedMap<GovernmentType, GovernmentType const*> PROPERTY(government_flag_overrides);
+ GovernmentType const* PROPERTY(flag_government_type);
fixed_point_t PROPERTY(suppression_points);
fixed_point_t PROPERTY(infamy);
fixed_point_t PROPERTY(plurality);
fixed_point_t PROPERTY(revanchism);
+ IndexedMap<Crime, unlock_level_t> PROPERTY(unlocked_crimes);
// TODO - rebel movements
/* Population */
@@ -100,41 +140,68 @@ 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, unlock_level_t> PROPERTY(unlocked_regiment_types);
+ RegimentType::allowed_cultures_t PROPERTY(allowed_regiment_cultures);
+ IndexedMap<ShipType, unlock_level_t> PROPERTY(unlocked_ship_types);
+ unlock_level_t PROPERTY(gas_attack_unlock_level);
+ unlock_level_t PROPERTY(gas_defence_unlock_level);
+ std::vector<unlock_level_t> PROPERTY(unit_variant_unlock_levels);
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, decltype(technologies)::keys_t const& technology_keys,
- decltype(inventions)::keys_t const& invention_keys, decltype(upper_house)::keys_t const& ideology_keys,
- decltype(pop_type_distribution)::keys_t const& pop_type_keys
+ CountryDefinition const* new_country_definition,
+ decltype(unlocked_building_types)::keys_t const& building_type_keys,
+ decltype(unlocked_technologies)::keys_t const& technology_keys,
+ decltype(unlocked_inventions)::keys_t const& invention_keys,
+ decltype(upper_house)::keys_t const& ideology_keys,
+ decltype(reforms)::keys_t const& reform_keys,
+ decltype(government_flag_overrides)::keys_t const& government_type_keys,
+ decltype(unlocked_crimes)::keys_t const& crime_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);
@@ -150,8 +217,8 @@ namespace OpenVic {
bool remove_accepted_culture(Culture const& culture_to_remove);
/* Set a party's popularity in the upper house. */
bool set_upper_house(Ideology const* ideology, fixed_point_t popularity);
- bool add_reform(Reform const* new_reform);
- bool remove_reform(Reform const* reform_to_remove);
+ bool set_ruling_party(CountryParty const& new_ruling_party);
+ bool add_reform(Reform const& new_reform);
template<UnitType::branch_t Branch>
bool add_unit_instance_group(UnitInstanceGroup<Branch>& group);
@@ -163,21 +230,73 @@ 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>
+ bool modify_unit_type_unlock(UnitTypeBranched<Branch> const& unit_type, unlock_level_t unlock_level_change);
+
+ bool modify_unit_type_unlock(UnitType const& unit_type, unlock_level_t unlock_level_change);
+ bool unlock_unit_type(UnitType const& unit_type);
+ bool is_unit_type_unlocked(UnitType const& unit_type) const;
+
+ bool modify_building_type_unlock(BuildingType const& building_type, unlock_level_t unlock_level_change);
+ bool unlock_building_type(BuildingType const& building_type);
+ bool is_building_type_unlocked(BuildingType const& building_type) const;
+
+ bool modify_crime_unlock(Crime const& crime, unlock_level_t unlock_level_change);
+ bool unlock_crime(Crime const& crime);
+ bool is_crime_unlocked(Crime const& crime) const;
+
+ bool modify_gas_attack_unlock(unlock_level_t unlock_level_change);
+ bool unlock_gas_attack();
+ bool is_gas_attack_unlocked() const;
+
+ bool modify_gas_defence_unlock(unlock_level_t unlock_level_change);
+ bool unlock_gas_defence();
+ bool is_gas_defence_unlocked() const;
+
+ bool modify_unit_variant_unlock(unit_variant_t unit_variant, unlock_level_t unlock_level_change);
+ bool unlock_unit_variant(unit_variant_t unit_variant);
+ unit_variant_t get_max_unlocked_unit_variant() const;
+
+ bool modify_technology_unlock(Technology const& technology, unlock_level_t unlock_level_change);
+ bool set_technology_unlock_level(Technology const& technology, unlock_level_t unlock_level);
+ bool unlock_technology(Technology const& technology);
+ bool is_technology_unlocked(Technology const& technology) const;
+
+ bool modify_invention_unlock(Invention const& invention, unlock_level_t unlock_level_change);
+ bool set_invention_unlock_level(Invention const& invention, unlock_level_t unlock_level);
+ bool unlock_invention(Invention const& invention);
+ bool is_invention_unlocked(Invention const& invention) const;
+
+ 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);
+
+ bool update_rule_set();
public:
- void update_gamestate();
+ void update_gamestate(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager);
void tick();
};
@@ -189,16 +308,32 @@ 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;
bool generate_country_instances(
CountryDefinitionManager const& country_definition_manager,
- decltype(CountryInstance::technologies)::keys_t const& technology_keys,
- decltype(CountryInstance::inventions)::keys_t const& invention_keys,
+ decltype(CountryInstance::unlocked_building_types)::keys_t const& building_type_keys,
+ decltype(CountryInstance::unlocked_technologies)::keys_t const& technology_keys,
+ decltype(CountryInstance::unlocked_inventions)::keys_t const& invention_keys,
decltype(CountryInstance::upper_house)::keys_t const& ideology_keys,
- decltype(CountryInstance::pop_type_distribution)::keys_t const& pop_type_keys
+ decltype(CountryInstance::reforms)::keys_t const& reform_keys,
+ decltype(CountryInstance::government_flag_overrides)::keys_t const& government_type_keys,
+ decltype(CountryInstance::unlocked_crimes)::keys_t const& crime_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(
@@ -206,7 +341,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/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp
index 8265c94..dd0d022 100644
--- a/src/openvic-simulation/dataloader/NodeTools.hpp
+++ b/src/openvic-simulation/dataloader/NodeTools.hpp
@@ -448,6 +448,22 @@ namespace OpenVic {
return assign_variable_callback_cast<T, T>(var);
}
+ /* By default this will only allow an optional to be set once. Set allow_overwrite
+ * to true to allow multiple assignments, with the last taking precedence. */
+ template<typename T>
+ Callback<T> auto assign_variable_callback_opt(
+ std::optional<T>& var, bool allow_overwrite = false
+ ) {
+ return [&var, allow_overwrite](T const& val) -> bool {
+ if (!allow_overwrite && var.has_value()) {
+ Logger::error("Cannot assign value to already-initialised optional!");
+ return false;
+ }
+ var = val;
+ return true;
+ };
+ }
+
callback_t<std::string_view> assign_variable_callback_string(std::string& var);
template<typename T>
@@ -492,7 +508,7 @@ namespace OpenVic {
) {
return [&var, allow_overwrite](T const& val) -> bool {
if (!allow_overwrite && var.has_value()) {
- Logger::error("Canoot assign pointer value to already-initialised optional!");
+ Logger::error("Cannot assign pointer value to already-initialised optional!");
return false;
}
var = &val;
diff --git a/src/openvic-simulation/dataloader/Vic2PathSearch.cpp b/src/openvic-simulation/dataloader/Vic2PathSearch.cpp
index ccd55d1..a23d0ce 100644
--- a/src/openvic-simulation/dataloader/Vic2PathSearch.cpp
+++ b/src/openvic-simulation/dataloader/Vic2PathSearch.cpp
@@ -1,3 +1,5 @@
+#include <filesystem>
+
#include <openvic-dataloader/detail/CallbackOStream.hpp>
#include <lexy-vdf/KeyValues.hpp>
@@ -159,7 +161,7 @@ static fs::path _search_for_game_path(fs::path hint_path = {}) {
// Steam Library's directory that will contain Victoria 2
fs::path vic2_steam_lib_directory;
- fs::path current_path = hint_path;
+ fs::path current_path = std::filesystem::weakly_canonical(hint_path, error_code);
// If hinted path is directory that contains steamapps
bool is_steamapps = false;
@@ -288,7 +290,7 @@ static fs::path _search_for_game_path(fs::path hint_path = {}) {
// If we could not confirm Victoria 2 was installed via the default Steam installation
bool is_common_folder = false;
- if (!vic2_install_confirmed) {
+ if (!vic2_install_confirmed && !vic2_steam_lib_directory.empty()) {
auto parser = lexy_vdf::Parser::from_file(vic2_steam_lib_directory);
if (!parser.parse()) {
// Could not find or load appmanifest_42960.acf, report error as warning
diff --git a/src/openvic-simulation/economy/BuildingType.cpp b/src/openvic-simulation/economy/BuildingType.cpp
index d096166..f0fc8a8 100644
--- a/src/openvic-simulation/economy/BuildingType.cpp
+++ b/src/openvic-simulation/economy/BuildingType.cpp
@@ -5,9 +5,8 @@ using namespace OpenVic::NodeTools;
BuildingType::BuildingType(
std::string_view identifier, building_type_args_t& building_type_args
-) : HasIdentifier { identifier },
+) : Modifier { identifier, std::move(building_type_args.modifier) },
type { building_type_args.type },
- modifier { std::move(building_type_args.modifier) },
on_completion { building_type_args.on_completion },
completion_size { building_type_args.completion_size },
max_level { building_type_args.max_level },
@@ -133,13 +132,15 @@ bool BuildingTypeManager::load_buildings_file(
port_building_type = &building_type;
} else {
Logger::error(
- "Building type ", building_type, " is marked as a port, but we are already using ", port_building_type,
- " as the port building type!"
+ "Building type ", building_type.get_identifier(), " is marked as a port, but we are already using ",
+ port_building_type->get_identifier(), " as the port building type!"
);
ret = false;
}
} else {
- Logger::error("Building type ", building_type, " is marked as a port, but is not a province building!");
+ Logger::error(
+ "Building type ", building_type.get_identifier(), " is marked as a port, but is not a province building!"
+ );
ret = false;
}
}
diff --git a/src/openvic-simulation/economy/BuildingType.hpp b/src/openvic-simulation/economy/BuildingType.hpp
index 977d6ec..ab999cd 100644
--- a/src/openvic-simulation/economy/BuildingType.hpp
+++ b/src/openvic-simulation/economy/BuildingType.hpp
@@ -16,7 +16,7 @@ namespace OpenVic {
* MAP-12, MAP-75, MAP-76
* MAP-13, MAP-78, MAP-79
*/
- struct BuildingType : HasIdentifier {
+ struct BuildingType : Modifier {
friend struct BuildingTypeManager;
using level_t = int16_t;
@@ -42,7 +42,6 @@ namespace OpenVic {
private:
std::string PROPERTY(type);
- ModifierValue PROPERTY(modifier);
std::string PROPERTY(on_completion); // probably sound played on completion
fixed_point_t PROPERTY(completion_size);
level_t PROPERTY(max_level);
@@ -51,7 +50,7 @@ namespace OpenVic {
Timespan PROPERTY(build_time); // time
bool PROPERTY(on_map); // onmap
- bool PROPERTY(default_enabled);
+ bool PROPERTY_CUSTOM_PREFIX(default_enabled, is);
ProductionType const* PROPERTY(production_type);
bool PROPERTY(pop_build_factory);
bool PROPERTY(strategic_factory);
diff --git a/src/openvic-simulation/economy/GoodDefinition.cpp b/src/openvic-simulation/economy/GoodDefinition.cpp
index 104764b..f21ea18 100644
--- a/src/openvic-simulation/economy/GoodDefinition.cpp
+++ b/src/openvic-simulation/economy/GoodDefinition.cpp
@@ -1,20 +1,28 @@
#include "GoodDefinition.hpp"
-#include <cassert>
-
using namespace OpenVic;
using namespace OpenVic::NodeTools;
GoodCategory::GoodCategory(std::string_view new_identifier) : HasIdentifier { new_identifier } {}
GoodDefinition::GoodDefinition(
- std::string_view new_identifier, colour_t new_colour, index_t new_index, GoodCategory const& new_category,
- price_t new_base_price, bool new_available_from_start, bool new_tradeable, bool new_money, bool new_overseas_penalty
-) : HasIdentifierAndColour { new_identifier, new_colour, false }, HasIndex { new_index }, category { new_category },
- base_price { new_base_price }, available_from_start { new_available_from_start }, tradeable { new_tradeable },
- money { new_money }, overseas_penalty { new_overseas_penalty } {
- assert(base_price > NULL_PRICE);
-}
+ std::string_view new_identifier,
+ colour_t new_colour,
+ index_t new_index,
+ GoodCategory const& new_category,
+ fixed_point_t new_base_price,
+ bool new_available_from_start,
+ bool new_tradeable,
+ bool new_money,
+ bool new_overseas_penalty
+) : HasIdentifierAndColour { new_identifier, new_colour, false },
+ HasIndex { new_index },
+ category { new_category },
+ base_price { new_base_price },
+ available_from_start { new_available_from_start },
+ tradeable { new_tradeable },
+ money { new_money },
+ overseas_penalty { new_overseas_penalty } {}
bool GoodDefinitionManager::add_good_category(std::string_view identifier) {
if (identifier.empty()) {
@@ -25,14 +33,14 @@ bool GoodDefinitionManager::add_good_category(std::string_view identifier) {
}
bool GoodDefinitionManager::add_good_definition(
- std::string_view identifier, colour_t colour, GoodCategory const& category, GoodDefinition::price_t base_price,
+ std::string_view identifier, colour_t colour, GoodCategory const& category, fixed_point_t base_price,
bool available_from_start, bool tradeable, bool money, bool overseas_penalty
) {
if (identifier.empty()) {
Logger::error("Invalid good identifier - empty!");
return false;
}
- if (base_price <= GoodDefinition::NULL_PRICE) {
+ if (base_price <= 0) {
Logger::error("Invalid base price for ", identifier, ": ", base_price);
return false;
}
@@ -57,7 +65,7 @@ bool GoodDefinitionManager::load_goods_file(ast::NodeCPtr root) {
ret &= expect_good_category_dictionary([this](GoodCategory const& good_category, ast::NodeCPtr good_category_value) -> bool {
return expect_dictionary([this, &good_category](std::string_view key, ast::NodeCPtr value) -> bool {
colour_t colour = colour_t::null();
- GoodDefinition::price_t base_price;
+ fixed_point_t base_price;
bool available_from_start = true, tradeable = true;
bool money = false, overseas_penalty = false;
diff --git a/src/openvic-simulation/economy/GoodDefinition.hpp b/src/openvic-simulation/economy/GoodDefinition.hpp
index 3050243..15eb4e8 100644
--- a/src/openvic-simulation/economy/GoodDefinition.hpp
+++ b/src/openvic-simulation/economy/GoodDefinition.hpp
@@ -31,14 +31,11 @@ namespace OpenVic {
struct GoodDefinition : HasIdentifierAndColour, HasIndex<> {
friend struct GoodDefinitionManager;
- using price_t = fixed_point_t;
- static constexpr price_t NULL_PRICE = fixed_point_t::_0();
-
using good_definition_map_t = fixed_point_map_t<GoodDefinition const*>;
private:
GoodCategory const& PROPERTY(category);
- const price_t PROPERTY(base_price);
+ const fixed_point_t PROPERTY(base_price);
const bool PROPERTY_CUSTOM_PREFIX(available_from_start, is);
const bool PROPERTY_CUSTOM_PREFIX(tradeable, is);
const bool PROPERTY(money);
@@ -46,7 +43,7 @@ namespace OpenVic {
GoodDefinition(
std::string_view new_identifier, colour_t new_colour, index_t new_index, GoodCategory const& new_category,
- price_t new_base_price, bool new_available_from_start, bool new_tradeable, bool new_money,
+ fixed_point_t new_base_price, bool new_available_from_start, bool new_tradeable, bool new_money,
bool new_overseas_penalty
);
@@ -63,7 +60,7 @@ namespace OpenVic {
bool add_good_category(std::string_view identifier);
bool add_good_definition(
- std::string_view identifier, colour_t colour, GoodCategory const& category, GoodDefinition::price_t base_price,
+ std::string_view identifier, colour_t colour, GoodCategory const& category, fixed_point_t base_price,
bool available_from_start, bool tradeable, bool money, bool overseas_penalty
);
diff --git a/src/openvic-simulation/economy/GoodInstance.hpp b/src/openvic-simulation/economy/GoodInstance.hpp
index 555ef87..20370ef 100644
--- a/src/openvic-simulation/economy/GoodInstance.hpp
+++ b/src/openvic-simulation/economy/GoodInstance.hpp
@@ -13,7 +13,7 @@ namespace OpenVic {
private:
GoodDefinition const& PROPERTY(good_definition);
- GoodDefinition::price_t PROPERTY(price);
+ fixed_point_t PROPERTY(price);
bool PROPERTY(available);
// TODO - supply, demand, actual bought
diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp
index 145d26b..cd51e19 100644
--- a/src/openvic-simulation/history/CountryHistory.cpp
+++ b/src/openvic-simulation/history/CountryHistory.cpp
@@ -54,7 +54,17 @@ bool CountryHistoryMap::_load_history_entry(
{
Technology const* technology = technology_manager.get_technology_by_identifier(key);
if (technology != nullptr) {
- return expect_int_bool(map_callback(entry.technologies, technology))(value);
+ return expect_uint<decltype(entry.technologies)::mapped_type>(
+ [&entry, technology](decltype(entry.technologies)::mapped_type value) -> bool {
+ if (value > 1) {
+ Logger::warning(
+ "Technology ", technology->get_identifier(),
+ " is applied multiple times in history of country ", entry.get_country().get_identifier()
+ );
+ }
+ return map_callback(entry.technologies, technology)(value);
+ }
+ )(value);
}
}
@@ -191,6 +201,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/CountryHistory.hpp b/src/openvic-simulation/history/CountryHistory.hpp
index d6a6997..04de653 100644
--- a/src/openvic-simulation/history/CountryHistory.hpp
+++ b/src/openvic-simulation/history/CountryHistory.hpp
@@ -2,6 +2,7 @@
#include <optional>
+#include "openvic-simulation/country/CountryInstance.hpp"
#include "openvic-simulation/history/HistoryMap.hpp"
#include "openvic-simulation/types/Date.hpp"
#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp"
@@ -46,7 +47,7 @@ namespace OpenVic {
ordered_set<Reform const*> PROPERTY(reforms);
std::optional<Deployment const*> PROPERTY(inital_oob);
std::optional<TechnologySchool const*> PROPERTY(tech_school);
- ordered_map<Technology const*, bool> PROPERTY(technologies);
+ ordered_map<Technology const*, CountryInstance::unlock_level_t> PROPERTY(technologies);
ordered_map<Invention const*, bool> PROPERTY(inventions);
fixed_point_map_t<CountryDefinition const*> PROPERTY(foreign_investment);
std::optional<fixed_point_t> PROPERTY(consciousness);
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/interface/UI.cpp b/src/openvic-simulation/interface/UI.cpp
index db7e78a..03166b1 100644
--- a/src/openvic-simulation/interface/UI.cpp
+++ b/src/openvic-simulation/interface/UI.cpp
@@ -23,10 +23,20 @@ bool UIManager::add_font(
Logger::error("Invalid fontname for font ", identifier, " - empty!");
return false;
}
- return fonts.add_item(
+ const bool ret = fonts.add_item(
{ identifier, colour, fontname, charset, height, std::move(colour_codes) },
duplicate_warning_callback
);
+
+ if (universal_colour_codes.empty() && ret) {
+ GFX::Font::colour_codes_t const& loaded_colour_codes = get_fonts().back().get_colour_codes();
+ if (!loaded_colour_codes.empty()) {
+ universal_colour_codes = loaded_colour_codes;
+ Logger::info("Loaded universal colour codes from font: \"", identifier, "\"");
+ }
+ }
+
+ return ret;
}
bool UIManager::_load_font(ast::NodeCPtr node) {
diff --git a/src/openvic-simulation/interface/UI.hpp b/src/openvic-simulation/interface/UI.hpp
index 32dba9c..8ba7745 100644
--- a/src/openvic-simulation/interface/UI.hpp
+++ b/src/openvic-simulation/interface/UI.hpp
@@ -8,6 +8,7 @@ namespace OpenVic {
class UIManager {
NamedInstanceRegistry<GFX::Sprite> IDENTIFIER_REGISTRY(sprite);
IdentifierRegistry<GFX::Font> IDENTIFIER_REGISTRY(font);
+ GFX::Font::colour_codes_t PROPERTY(universal_colour_codes);
NamedInstanceRegistry<GFX::Object> IDENTIFIER_REGISTRY(object);
NamedInstanceRegistry<GUI::Scene, UIManager const&> IDENTIFIER_REGISTRY(scene);
diff --git a/src/openvic-simulation/map/Crime.hpp b/src/openvic-simulation/map/Crime.hpp
index cb539f3..6aaf12c 100644
--- a/src/openvic-simulation/map/Crime.hpp
+++ b/src/openvic-simulation/map/Crime.hpp
@@ -7,7 +7,7 @@ namespace OpenVic {
friend struct CrimeManager;
private:
- const bool PROPERTY(default_active);
+ const bool PROPERTY_CUSTOM_PREFIX(default_active, is);
Crime(
std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, ConditionScript&& new_trigger,
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/Mapmode.cpp b/src/openvic-simulation/map/Mapmode.cpp
index cab67d1..f951a05 100644
--- a/src/openvic-simulation/map/Mapmode.cpp
+++ b/src/openvic-simulation/map/Mapmode.cpp
@@ -1,7 +1,5 @@
#include "Mapmode.hpp"
-#include <cassert>
-
#include "openvic-simulation/country/CountryInstance.hpp"
#include "openvic-simulation/map/MapDefinition.hpp"
#include "openvic-simulation/map/MapInstance.hpp"
@@ -13,10 +11,12 @@ using namespace OpenVic;
using namespace OpenVic::colour_literals;
Mapmode::Mapmode(
- std::string_view new_identifier, index_t new_index, colour_func_t new_colour_func
-) : HasIdentifier { new_identifier }, HasIndex { new_index }, colour_func { new_colour_func } {
- assert(colour_func != nullptr);
-}
+ std::string_view new_identifier,
+ index_t new_index,
+ colour_func_t new_colour_func
+) : HasIdentifier { new_identifier },
+ HasIndex { new_index },
+ colour_func { new_colour_func } {}
const Mapmode Mapmode::ERROR_MAPMODE {
"mapmode_error", 0, [](MapInstance const&, ProvinceInstance const& province) -> base_stripe_t {
diff --git a/src/openvic-simulation/map/ProvinceDefinition.cpp b/src/openvic-simulation/map/ProvinceDefinition.cpp
index 4f34c1e..14828e8 100644
--- a/src/openvic-simulation/map/ProvinceDefinition.cpp
+++ b/src/openvic-simulation/map/ProvinceDefinition.cpp
@@ -8,13 +8,23 @@ using namespace OpenVic;
using namespace OpenVic::NodeTools;
ProvinceDefinition::ProvinceDefinition(
- std::string_view new_identifier, colour_t new_colour, index_t new_index
-) : HasIdentifierAndColour { new_identifier, new_colour, true }, HasIndex { new_index }, region { nullptr },
- climate { nullptr }, continent { nullptr }, on_map { false }, water { false }, coastal { false },
- port { false }, port_adjacent_province { nullptr }, default_terrain_type { nullptr }, adjacencies {}, centre {},
- positions {} {
- assert(index != NULL_INDEX);
-}
+ std::string_view new_identifier,
+ colour_t new_colour,
+ index_t new_index
+) : HasIdentifierAndColour { new_identifier, new_colour, true },
+ HasIndex { new_index },
+ region { nullptr },
+ climate { nullptr },
+ continent { nullptr },
+ on_map { false },
+ water { false },
+ coastal { false },
+ port { false },
+ port_adjacent_province { nullptr },
+ default_terrain_type { nullptr },
+ adjacencies {},
+ centre {},
+ positions {} {}
bool ProvinceDefinition::operator==(ProvinceDefinition const& other) const {
return this == &other;
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 9ec5d49..c28aab0 100644
--- a/src/openvic-simulation/misc/Define.cpp
+++ b/src/openvic-simulation/misc/Define.cpp
@@ -1,109 +1,270 @@
#include "Define.hpp"
-#include <cassert>
-#include <cstdlib>
-#include <memory>
-
#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>
#include "openvic-simulation/dataloader/NodeTools.hpp"
#include "openvic-simulation/types/Date.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"
#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
+#include "openvic-simulation/utility/StringUtils.hpp"
using namespace OpenVic;
using namespace OpenVic::NodeTools;
-Define::Define(std::string_view new_identifier, std::string&& new_value, Type new_type)
- : HasIdentifier { new_identifier }, value { std::move(new_value) }, type { new_type } {}
+std::string_view Define::type_to_string(Type type) {
+ using enum Type;
+
+ switch (type) {
+ case Date: return "date";
+ case Country: return "country";
+ case Economy: return "economy";
+ case Military: return "military";
+ case Diplomacy: return "diplomacy";
+ case Pops: return "pops";
+ case Ai: return "ai";
+ case Graphics: return "graphics";
+ default: return "unknown";
+ }
+}
+
+Define::Type Define::string_to_type(std::string_view str) {
+ using enum Type;
+
+ static const string_map_t<Define::Type> type_map {
+ { "country", Country },
+ { "economy", Economy },
+ { "military", Military },
+ { "diplomacy", Diplomacy },
+ { "pops", Pops },
+ { "ai", Ai },
+ { "graphics", Graphics },
+ };
-fixed_point_t Define::get_value_as_fp() const {
- return fixed_point_t::parse(value);
+ const string_map_t<Define::Type>::const_iterator type_it = type_map.find(str);
+
+ if (type_it != type_map.end()) {
+ return type_it->second;
+ } else {
+ return Unknown;
+ }
}
-int64_t Define::get_value_as_int() const {
- return std::strtoll(value.data(), nullptr, 10);
+Define::Define(std::string_view new_identifier, std::string_view new_value, Type new_type)
+ : HasIdentifier { new_identifier }, value { new_value }, type { new_type } {}
+
+Date Define::get_value_as_date(bool* successful) const {
+ return Date::from_string(value, successful);
}
-uint64_t Define::get_value_as_uint() const {
- return std::strtoull(value.data(), nullptr, 10);
+fixed_point_t Define::get_value_as_fp(bool* successful) const {
+ return fixed_point_t::parse(value, successful);
}
-bool DefineManager::add_define(std::string_view name, std::string&& value, Define::Type type) {
- if (name.empty()) {
- Logger::error("Invalid define identifier - empty!");
- return false;
- }
- return defines.add_item({ name, std::move(value), type }, duplicate_warning_callback);
+int64_t Define::get_value_as_int(bool* successful) const {
+ return StringUtils::string_to_int64(value, successful);
}
-Date DefineManager::get_start_date() const {
- return start_date ? *start_date : Date {};
+uint64_t Define::get_value_as_uint(bool* successful) const {
+ return StringUtils::string_to_uint64(value, successful);
}
-Date DefineManager::get_end_date() const {
- return end_date ? *end_date : Date {};
+std::ostream& OpenVic::operator<<(std::ostream& os, Define::Type type) {
+ return os << Define::type_to_string(type);
}
-bool DefineManager::in_game_period(Date date) const {
- if (start_date && end_date) {
- return date.in_range(*start_date, *end_date);
+template<typename T>
+bool DefineManager::load_define(T& value, Define::Type type, std::string_view name) const {
+ static_assert(
+ std::same_as<T, OpenVic::Date> || std::same_as<T, fixed_point_t> || std::integral<T>
+ );
+
+ Define const* define = defines.get_item_by_identifier(name);
+
+ if (define != nullptr) {
+ if (define->type != type) {
+ Logger::warning("Mismatched define type for \"", name, "\" - expected ", type, ", got ", define->type);
+ }
+
+ const auto parse =
+ [define, &value, &name]<typename U, U (Define::*Func)(bool*) const>(std::string_view type_name) -> bool {
+ bool success = false;
+ const U result = (define->*Func)(&success);
+ if (success) {
+ value = static_cast<T>(result);
+ return true;
+ } else {
+ Logger::error("Failed to parse ", type_name, " \"", define->get_value(), "\" for define \"", name, "\"");
+ return false;
+ }
+ };
+
+ if constexpr (std::same_as<T, OpenVic::Date>) {
+ return parse.template operator()<Date, &Define::get_value_as_date>("date");
+ } else if constexpr (std::same_as<T, fixed_point_t>) {
+ return parse.template operator()<fixed_point_t, &Define::get_value_as_fp>("fixed point");
+ } else if constexpr (std::signed_integral<T>) {
+ return parse.template operator()<int64_t, &Define::get_value_as_int>("signed int");
+ } else if constexpr (std::unsigned_integral<T>) {
+ return parse.template operator()<uint64_t, &Define::get_value_as_uint>("unsigned int");
+ }
} else {
+ Logger::error("Missing define \"", name, "\"");
return false;
}
}
-bool DefineManager::add_date_define(std::string_view name, Date date) {
- if (name == "start_date") {
- start_date = date;
- } else if (name == "end_date") {
- end_date = date;
+template<Timespan (*Func)(Timespan::day_t)>
+bool DefineManager::_load_define_timespan(Timespan& value, Define::Type type, std::string_view name) const {
+ Define const* define = defines.get_item_by_identifier(name);
+ if (define != nullptr) {
+ if (define->type != type) {
+ Logger::warning("Mismatched define type for \"", name, "\" - expected ", type, ", got ", define->type);
+ }
+ bool success = false;
+ const int64_t result = define->get_value_as_int(&success);
+ if (success) {
+ value = Func(result);
+ return true;
+ } else {
+ Logger::error("Failed to parse days \"", define->get_value(), "\" for define \"", name, "\"");
+ return false;
+ }
} else {
- Logger::error("Invalid date define identifier - \"", name, "\" (must be start_date or end_date)");
+ Logger::error("Missing define \"", name, "\"");
+ return false;
+ }
+}
+
+bool DefineManager::load_define_days(Timespan& value, Define::Type type, std::string_view name) const {
+ return _load_define_timespan<Timespan::from_days>(value, type, name);
+}
+
+bool DefineManager::load_define_months(Timespan& value, Define::Type type, std::string_view name) const {
+ return _load_define_timespan<Timespan::from_months>(value, type, name);
+}
+
+bool DefineManager::load_define_years(Timespan& value, Define::Type type, std::string_view name) const {
+ return _load_define_timespan<Timespan::from_years>(value, type, name);
+}
+
+DefineManager::DefineManager()
+ : // Date
+ start_date { 1836, 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
+
+ // Ai
+
+ // Graphics
+
+ {}
+
+bool DefineManager::add_define(std::string_view name, std::string_view value, Define::Type type) {
+ if (name.empty()) {
+ Logger::error("Invalid define identifier - empty!");
+ return false;
+ }
+
+ if (value.empty()) {
+ Logger::error("Invalid define value for \"", name, "\" - empty!");
return false;
}
- return defines.add_item({ name, date.to_string(), Define::Type::Date });
+
+ return defines.add_item({ name, value, type }, duplicate_warning_callback);
+}
+
+bool DefineManager::in_game_period(Date date) const {
+ return date.in_range(start_date, end_date);
}
bool DefineManager::load_defines_file(ast::NodeCPtr root) {
+ using enum Define::Type;
+
bool ret = expect_dictionary_keys(
"defines", ONE_EXACTLY, expect_dictionary([this](std::string_view key, ast::NodeCPtr value) -> bool {
- using enum Define::Type;
- static const string_map_t<Define::Type> type_map {
- { "country", Country },
- { "economy", Economy },
- { "military", Military },
- { "diplomacy", Diplomacy },
- { "pops", Pops },
- { "ai", Ai },
- { "graphics", Graphics },
- };
- const string_map_t<Define::Type>::const_iterator type_it = type_map.find(key);
+ const Define::Type type = Define::string_to_type(key);
- if (type_it != type_map.end()) {
+ if (type != Unknown) {
return expect_dictionary_reserve_length(
defines,
- [this, &key, type = type_it->second](std::string_view inner_key, ast::NodeCPtr value) -> bool {
- std::string str_val;
- bool ret = expect_identifier_or_string(assign_variable_callback_string(str_val))(value);
- ret &= add_define(inner_key, std::move(str_val), type);
- return ret;
+ [this, type](std::string_view inner_key, ast::NodeCPtr value) -> bool {
+ return expect_identifier_or_string(
+ [this, &inner_key, type](std::string_view value) -> bool {
+ return add_define(inner_key, value, type);
+ }
+ )(value);
}
)(value);
} else if (key == "start_date" || key == "end_date") {
- return expect_date_identifier_or_string(std::bind_front(&DefineManager::add_date_define, this, key))(value);
+ return expect_identifier_or_string(
+ [this, &key](std::string_view value) -> bool {
+ return add_define(key, value, Date);
+ }
+ )(value);
} else {
+
+ Logger::error("Invalid define type - \"", key, "\"");
return false;
+
}
})
)(root);
lock_defines();
+ // Date
+ ret &= load_define(start_date, Date, "start_date");
+ 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
+
+ // Ai
+
+ // Graphics
+
return ret;
}
diff --git a/src/openvic-simulation/misc/Define.hpp b/src/openvic-simulation/misc/Define.hpp
index 3f7b3dc..064883c 100644
--- a/src/openvic-simulation/misc/Define.hpp
+++ b/src/openvic-simulation/misc/Define.hpp
@@ -1,7 +1,6 @@
#pragma once
-#include <optional>
-
+#include "openvic-simulation/pop/Pop.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"
#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
@@ -11,35 +10,77 @@ namespace OpenVic {
struct Define : HasIdentifier {
friend struct DefineManager;
- enum class Type : unsigned char { Date, Country, Economy, Military, Diplomacy, Pops, Ai, Graphics };
+ enum class Type : unsigned char { Unknown, Date, Country, Economy, Military, Diplomacy, Pops, Ai, Graphics };
+
+ static std::string_view type_to_string(Type type);
+ // This only accepts type names found in defines.lua, so it will never return Type::Date
+ static Type string_to_type(std::string_view str);
private:
std::string PROPERTY(value);
const Type PROPERTY(type);
- Define(std::string_view new_identifier, std::string&& new_value, Type new_type);
+ Define(std::string_view new_identifier, std::string_view new_value, Type new_type);
public:
Define(Define&&) = default;
- fixed_point_t get_value_as_fp() const;
- int64_t get_value_as_int() const;
- uint64_t get_value_as_uint() const;
+ Date get_value_as_date(bool* successful = nullptr) const;
+ fixed_point_t get_value_as_fp(bool* successful = nullptr) const;
+ int64_t get_value_as_int(bool* successful = nullptr) const;
+ uint64_t get_value_as_uint(bool* successful = nullptr) const;
};
+ std::ostream& operator<<(std::ostream& os, Define::Type type);
+
struct DefineManager {
private:
IdentifierRegistry<Define> IDENTIFIER_REGISTRY(define);
- std::optional<Date> start_date;
- std::optional<Date> end_date;
+ // Date
+ Date PROPERTY(start_date); // start_date
+ 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
+
+ // Ai
+
+ // Graphics
+
+ template<typename T>
+ bool load_define(T& value, Define::Type type, std::string_view name) const;
+
+ template<Timespan (*Func)(Timespan::day_t)>
+ bool _load_define_timespan(Timespan& value, Define::Type type, std::string_view name) const;
+
+ bool load_define_days(Timespan& value, Define::Type type, std::string_view name) const;
+ bool load_define_months(Timespan& value, Define::Type type, std::string_view name) const;
+ bool load_define_years(Timespan& value, Define::Type type, std::string_view name) const;
public:
- bool add_define(std::string_view name, std::string&& value, Define::Type type);
- bool add_date_define(std::string_view name, Date date);
+ DefineManager();
+
+ bool add_define(std::string_view name, std::string_view value, Define::Type type);
- Date get_start_date() const;
- Date get_end_date() const;
bool in_game_period(Date date) const;
bool load_defines_file(ast::NodeCPtr root);
diff --git a/src/openvic-simulation/misc/Modifier.cpp b/src/openvic-simulation/misc/Modifier.cpp
index 4d6abb7..9163af5 100644
--- a/src/openvic-simulation/misc/Modifier.cpp
+++ b/src/openvic-simulation/misc/Modifier.cpp
@@ -33,6 +33,14 @@ size_t ModifierValue::get_effect_count() const {
return values.size();
}
+void ModifierValue::clear() {
+ values.clear();
+}
+
+bool ModifierValue::empty() const {
+ return values.empty();
+}
+
fixed_point_t ModifierValue::get_effect(ModifierEffect const* effect, bool* successful) {
const effect_map_t::const_iterator it = values.find(effect);
if (it != values.end()) {
@@ -123,7 +131,6 @@ bool ModifierManager::setup_modifier_effects() {
/* Country Modifier Effects */
ret &= add_modifier_effect("administrative_efficiency", true);
ret &= add_modifier_effect("administrative_efficiency_modifier", true);
- ret &= add_modifier_effect("administrative_multiplier", true);
ret &= add_modifier_effect("artisan_input", false);
ret &= add_modifier_effect("artisan_output", true);
ret &= add_modifier_effect("artisan_throughput", true);
diff --git a/src/openvic-simulation/misc/Modifier.hpp b/src/openvic-simulation/misc/Modifier.hpp
index 58b335d..bd72a1e 100644
--- a/src/openvic-simulation/misc/Modifier.hpp
+++ b/src/openvic-simulation/misc/Modifier.hpp
@@ -37,7 +37,7 @@ namespace OpenVic {
using effect_map_t = fixed_point_map_t<ModifierEffect const*>;
private:
- effect_map_t values;
+ effect_map_t PROPERTY(values);
public:
ModifierValue();
@@ -51,6 +51,8 @@ namespace OpenVic {
/* Removes effect entries with a value of zero. */
void trim();
size_t get_effect_count() const;
+ void clear();
+ bool empty() const;
fixed_point_t get_effect(ModifierEffect const* effect, bool* successful = nullptr);
bool has_effect(ModifierEffect const* effect) const;
diff --git a/src/openvic-simulation/politics/Issue.cpp b/src/openvic-simulation/politics/Issue.cpp
index a03ffa3..1dca176 100644
--- a/src/openvic-simulation/politics/Issue.cpp
+++ b/src/openvic-simulation/politics/Issue.cpp
@@ -19,10 +19,11 @@ ReformGroup::ReformGroup(std::string_view new_identifier, ReformType const& new_
Reform::Reform(
std::string_view new_identifier, colour_t new_colour, ModifierValue&& new_values, ReformGroup const& new_group,
- size_t new_ordinal, RuleSet&& new_rules, tech_cost_t new_technology_cost, ConditionScript&& new_allow,
- ConditionScript&& new_on_execute_trigger, EffectScript&& new_on_execute_effect
-) : Issue { new_identifier, new_colour, std::move(new_values), new_group, std::move(new_rules), false }, ordinal { new_ordinal },
- reform_group { new_group }, technology_cost { new_technology_cost }, allow { std::move(new_allow) },
+ size_t new_ordinal, fixed_point_t new_administrative_multiplier, RuleSet&& new_rules, tech_cost_t new_technology_cost,
+ ConditionScript&& new_allow, ConditionScript&& new_on_execute_trigger, EffectScript&& new_on_execute_effect
+) : Issue { new_identifier, new_colour, std::move(new_values), new_group, std::move(new_rules), false },
+ reform_group { new_group }, ordinal { new_ordinal }, administrative_multiplier { new_administrative_multiplier },
+ technology_cost { new_technology_cost }, allow { std::move(new_allow) },
on_execute_trigger { std::move(new_on_execute_trigger) }, on_execute_effect { std::move(new_on_execute_effect) } {}
bool Reform::parse_scripts(DefinitionManager const& definition_manager) {
@@ -84,8 +85,8 @@ bool IssueManager::add_reform_group(std::string_view identifier, ReformType cons
bool IssueManager::add_reform(
std::string_view identifier, colour_t new_colour, ModifierValue&& values, ReformGroup const* group, size_t ordinal,
- RuleSet&& rules, Reform::tech_cost_t technology_cost, ConditionScript&& allow, ConditionScript&& on_execute_trigger,
- EffectScript&& on_execute_effect
+ fixed_point_t administrative_multiplier, RuleSet&& rules, Reform::tech_cost_t technology_cost, ConditionScript&& allow,
+ ConditionScript&& on_execute_trigger, EffectScript&& on_execute_effect
) {
if (identifier.empty()) {
Logger::error("Invalid issue identifier - empty!");
@@ -116,9 +117,16 @@ bool IssueManager::add_reform(
Logger::warning("Non-zero technology cost ", technology_cost, " found in civilised reform ", identifier, "!");
}
+ if (administrative_multiplier != 0 && !group->is_administrative()) {
+ Logger::warning(
+ "Non-zero administrative multiplier ", administrative_multiplier, " found in reform ", identifier,
+ " belonging to non-administrative group ", group->get_identifier(), "!"
+ );
+ }
+
return reforms.add_item({
- identifier, new_colour, std::move(values), *group, ordinal, std::move(rules), technology_cost, std::move(allow),
- std::move(on_execute_trigger), std::move(on_execute_effect)
+ identifier, new_colour, std::move(values), *group, ordinal, administrative_multiplier, std::move(rules),
+ technology_cost, std::move(allow), std::move(on_execute_trigger), std::move(on_execute_effect)
});
}
@@ -198,12 +206,15 @@ bool IssueManager::_load_reform(
) {
ModifierValue values;
RuleSet rules;
+ fixed_point_t administrative_multiplier = 0;
Reform::tech_cost_t technology_cost = 0;
ConditionScript allow { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE };
ConditionScript on_execute_trigger { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE };
EffectScript on_execute_effect;
- bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(values),
+ bool ret = modifier_manager.expect_modifier_value_and_keys(
+ move_variable_callback(values),
+ "administrative_multiplier", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(administrative_multiplier)),
"technology_cost", ZERO_OR_ONE, expect_uint(assign_variable_callback(technology_cost)),
"allow", ZERO_OR_ONE, allow.expect_script(),
"rules", ZERO_OR_ONE, rule_manager.expect_rule_set(move_variable_callback(rules)),
@@ -214,7 +225,8 @@ bool IssueManager::_load_reform(
)(node);
ret &= add_reform(
identifier, create_issue_reform_colour(get_issue_count() + get_reform_count()), std::move(values), group, ordinal,
- std::move(rules), technology_cost, std::move(allow), std::move(on_execute_trigger), std::move(on_execute_effect)
+ administrative_multiplier, std::move(rules), technology_cost, std::move(allow), std::move(on_execute_trigger),
+ std::move(on_execute_effect)
);
return ret;
}
diff --git a/src/openvic-simulation/politics/Issue.hpp b/src/openvic-simulation/politics/Issue.hpp
index 70f082d..d49f897 100644
--- a/src/openvic-simulation/politics/Issue.hpp
+++ b/src/openvic-simulation/politics/Issue.hpp
@@ -76,6 +76,7 @@ namespace OpenVic {
private:
ReformGroup const& PROPERTY(reform_group); // stores an already casted reference
const size_t PROPERTY(ordinal); // assigned by the parser to allow policy sorting
+ const fixed_point_t PROPERTY(administrative_multiplier);
const tech_cost_t PROPERTY(technology_cost);
ConditionScript PROPERTY(allow);
ConditionScript PROPERTY(on_execute_trigger);
@@ -83,8 +84,9 @@ namespace OpenVic {
Reform(
std::string_view new_identifier, colour_t new_colour, ModifierValue&& new_values, ReformGroup const& new_group,
- size_t new_ordinal, RuleSet&& new_rules, tech_cost_t new_technology_cost, ConditionScript&& new_allow,
- ConditionScript&& new_on_execute_trigger, EffectScript&& new_on_execute_effect
+ size_t new_ordinal, fixed_point_t new_administrative_multiplier, RuleSet&& new_rules,
+ tech_cost_t new_technology_cost, ConditionScript&& new_allow, ConditionScript&& new_on_execute_trigger,
+ EffectScript&& new_on_execute_effect
);
bool parse_scripts(DefinitionManager const& definition_manager);
@@ -125,8 +127,8 @@ namespace OpenVic {
bool add_reform_group(std::string_view identifier, ReformType const* type, bool ordered, bool administrative);
bool add_reform(
std::string_view identifier, colour_t new_colour, ModifierValue&& values, ReformGroup const* group, size_t ordinal,
- RuleSet&& rules, Reform::tech_cost_t technology_cost, ConditionScript&& allow, ConditionScript&& on_execute_trigger,
- EffectScript&& on_execute_effect
+ fixed_point_t administrative_multiplier, RuleSet&& rules, Reform::tech_cost_t technology_cost,
+ ConditionScript&& allow, ConditionScript&& on_execute_trigger, EffectScript&& on_execute_effect
);
bool load_issues_file(ModifierManager const& modifier_manager, RuleManager const& rule_manager, ast::NodeCPtr root);
diff --git a/src/openvic-simulation/politics/Rule.cpp b/src/openvic-simulation/politics/Rule.cpp
index 37aa22a..1d61652 100644
--- a/src/openvic-simulation/politics/Rule.cpp
+++ b/src/openvic-simulation/politics/Rule.cpp
@@ -62,6 +62,20 @@ size_t RuleSet::get_rule_count() const {
return ret;
}
+void RuleSet::clear() {
+ rule_groups.clear();
+}
+
+bool RuleSet::empty() const {
+ for (auto const& [group, rule_map] : rule_groups) {
+ if (!rule_map.empty()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
RuleSet::rule_map_t const& RuleSet::get_rule_group(Rule::rule_group_t group, bool* successful) const {
const rule_group_map_t::const_iterator it = rule_groups.find(group);
if (it != rule_groups.end()) {
diff --git a/src/openvic-simulation/politics/Rule.hpp b/src/openvic-simulation/politics/Rule.hpp
index 518c555..db0c926 100644
--- a/src/openvic-simulation/politics/Rule.hpp
+++ b/src/openvic-simulation/politics/Rule.hpp
@@ -42,7 +42,7 @@ namespace OpenVic {
using rule_group_map_t = ordered_map<Rule::rule_group_t, rule_map_t>;
private:
- rule_group_map_t rule_groups;
+ rule_group_map_t PROPERTY(rule_groups);
public:
RuleSet() = default;
@@ -59,6 +59,8 @@ namespace OpenVic {
bool trim_and_resolve_conflicts(bool log);
size_t get_rule_group_count() const;
size_t get_rule_count() const;
+ void clear();
+ bool empty() const;
rule_map_t const& get_rule_group(Rule::rule_group_t group, bool* successful = nullptr) const;
bool get_rule(Rule const* rule, bool* successful = nullptr) const;
diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp
index c421de3..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,9 +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 } {
- assert(size > 0);
-}
+ luxury_needs_fulfilled { 0 },
+ max_supported_regiments { 0 } {}
void Pop::setup_pop_test_values(IssueManager const& issue_manager) {
/* Returns +/- range% of size. */
@@ -127,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(
@@ -198,11 +215,7 @@ PopType::PopType(
migration_target { std::move(new_migration_target) },
promote_to { std::move(new_promote_to) },
ideologies { std::move(new_ideologies) },
- issues { std::move(new_issues) } {
- assert(sprite > 0);
- assert(max_size >= 0);
- assert(merge_max_size >= 0);
-}
+ issues { std::move(new_issues) } {}
bool PopType::parse_scripts(DefinitionManager const& definition_manager) {
bool ret = true;
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 {
diff --git a/src/openvic-simulation/pop/Religion.cpp b/src/openvic-simulation/pop/Religion.cpp
index 3fa81bf..2283821 100644
--- a/src/openvic-simulation/pop/Religion.cpp
+++ b/src/openvic-simulation/pop/Religion.cpp
@@ -1,7 +1,5 @@
#include "Religion.hpp"
-#include <cassert>
-
#include "openvic-simulation/types/Colour.hpp"
using namespace OpenVic;
@@ -10,10 +8,15 @@ using namespace OpenVic::NodeTools;
ReligionGroup::ReligionGroup(std::string_view new_identifier) : HasIdentifier { new_identifier } {}
Religion::Religion(
- std::string_view new_identifier, colour_t new_colour, ReligionGroup const& new_group, icon_t new_icon, bool new_pagan
-) : HasIdentifierAndColour { new_identifier, new_colour, false }, group { new_group }, icon { new_icon }, pagan { new_pagan } {
- assert(icon > 0);
-}
+ std::string_view new_identifier,
+ colour_t new_colour,
+ ReligionGroup const& new_group,
+ icon_t new_icon,
+ bool new_pagan
+) : HasIdentifierAndColour { new_identifier, new_colour, false },
+ group { new_group },
+ icon { new_icon },
+ pagan { new_pagan } {}
bool ReligionManager::add_religion_group(std::string_view identifier) {
if (identifier.empty()) {
diff --git a/src/openvic-simulation/research/Technology.cpp b/src/openvic-simulation/research/Technology.cpp
index 737650b..89c4a71 100644
--- a/src/openvic-simulation/research/Technology.cpp
+++ b/src/openvic-simulation/research/Technology.cpp
@@ -14,7 +14,7 @@ Technology::Technology(
Date::year_t new_year,
fixed_point_t new_cost,
bool new_unciv_military,
- uint8_t new_unit,
+ std::optional<CountryInstance::unit_variant_t>&& new_unit_variant,
unit_set_t&& new_activated_units,
building_set_t&& new_activated_buildings,
ModifierValue&& new_values,
@@ -24,9 +24,9 @@ Technology::Technology(
year { new_year },
cost { new_cost },
unciv_military { new_unciv_military },
- unit { new_unit },
- activated_buildings { std::move(new_activated_units) },
- activated_units { std::move(new_activated_buildings) },
+ unit_variant { std::move(new_unit_variant) },
+ activated_units { std::move(new_activated_units) },
+ activated_buildings { std::move(new_activated_buildings) },
ai_chance { std::move(new_ai_chance) } {}
bool Technology::parse_scripts(DefinitionManager const& definition_manager) {
@@ -61,8 +61,8 @@ bool TechnologyManager::add_technology_area(std::string_view identifier, Technol
bool TechnologyManager::add_technology(
std::string_view identifier, TechnologyArea const* area, Date::year_t year, fixed_point_t cost, bool unciv_military,
- uint8_t unit, Technology::unit_set_t&& activated_units, Technology::building_set_t&& activated_buildings,
- ModifierValue&& values, ConditionalWeight&& ai_chance
+ std::optional<CountryInstance::unit_variant_t>&& unit_variant, Technology::unit_set_t&& activated_units,
+ Technology::building_set_t&& activated_buildings, ModifierValue&& values, ConditionalWeight&& ai_chance
) {
if (identifier.empty()) {
Logger::error("Invalid technology identifier - empty!");
@@ -75,8 +75,8 @@ bool TechnologyManager::add_technology(
}
return technologies.add_item({
- identifier, *area, year, cost, unciv_military, unit, std::move(activated_units), std::move(activated_buildings),
- std::move(values), std::move(ai_chance)
+ identifier, *area, year, cost, unciv_military, std::move(unit_variant), std::move(activated_units),
+ std::move(activated_buildings), std::move(values), std::move(ai_chance)
});
}
@@ -157,8 +157,8 @@ bool TechnologyManager::load_technology_file_schools(
}
bool TechnologyManager::load_technologies_file(
- ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, BuildingTypeManager const& building_type_manager,
- ast::NodeCPtr root
+ ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager,
+ BuildingTypeManager const& building_type_manager, ast::NodeCPtr root
) {
return expect_dictionary_reserve_length(technologies, [this, &modifier_manager, &unit_type_manager, &building_type_manager](
std::string_view tech_key, ast::NodeCPtr tech_value
@@ -168,7 +168,7 @@ bool TechnologyManager::load_technologies_file(
Date::year_t year = 0;
fixed_point_t cost = 0;
bool unciv_military = false;
- uint8_t unit = 0;
+ std::optional<CountryInstance::unit_variant_t> unit_variant;
Technology::unit_set_t activated_units;
Technology::building_set_t activated_buildings;
ConditionalWeight ai_chance { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE };
@@ -179,7 +179,7 @@ bool TechnologyManager::load_technologies_file(
"year", ONE_EXACTLY, expect_uint(assign_variable_callback(year)),
"cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(cost)),
"unciv_military", ZERO_OR_ONE, expect_bool(assign_variable_callback(unciv_military)),
- "unit", ZERO_OR_ONE, expect_uint(assign_variable_callback(unit)),
+ "unit", ZERO_OR_ONE, expect_uint<decltype(unit_variant)::value_type>(assign_variable_callback_opt(unit_variant)),
"activate_unit", ZERO_OR_MORE, unit_type_manager.expect_unit_type_identifier(set_callback_pointer(activated_units)),
"activate_building", ZERO_OR_MORE, building_type_manager.expect_building_type_identifier(
set_callback_pointer(activated_buildings)
@@ -188,8 +188,8 @@ bool TechnologyManager::load_technologies_file(
)(tech_value);
ret &= add_technology(
- tech_key, area, year, cost, unciv_military, unit, std::move(activated_units), std::move(activated_buildings),
- std::move(modifiers), std::move(ai_chance)
+ tech_key, area, year, cost, unciv_military, std::move(unit_variant), std::move(activated_units),
+ std::move(activated_buildings), std::move(modifiers), std::move(ai_chance)
);
return ret;
})(root);
diff --git a/src/openvic-simulation/research/Technology.hpp b/src/openvic-simulation/research/Technology.hpp
index f13cca3..3afeb0b 100644
--- a/src/openvic-simulation/research/Technology.hpp
+++ b/src/openvic-simulation/research/Technology.hpp
@@ -2,6 +2,7 @@
#include <cstdint>
+#include "openvic-simulation/country/CountryInstance.hpp"
#include "openvic-simulation/economy/BuildingType.hpp"
#include "openvic-simulation/military/UnitType.hpp"
#include "openvic-simulation/misc/Modifier.hpp"
@@ -50,15 +51,16 @@ namespace OpenVic {
const Date::year_t PROPERTY(year);
const fixed_point_t PROPERTY(cost);
const bool PROPERTY(unciv_military);
- const uint8_t PROPERTY(unit);
- unit_set_t PROPERTY(activated_buildings);
- building_set_t PROPERTY(activated_units);
+ std::optional<CountryInstance::unit_variant_t> PROPERTY(unit_variant);
+ unit_set_t PROPERTY(activated_units);
+ building_set_t PROPERTY(activated_buildings);
ConditionalWeight PROPERTY(ai_chance);
Technology(
std::string_view new_identifier, TechnologyArea const& new_area, Date::year_t new_year, fixed_point_t new_cost,
- bool new_unciv_military, uint8_t new_unit, unit_set_t&& new_activated_units,
- building_set_t&& new_activated_buildings, ModifierValue&& new_values, ConditionalWeight&& new_ai_chance
+ bool new_unciv_military, std::optional<CountryInstance::unit_variant_t>&& new_unit_variant,
+ unit_set_t&& new_activated_units, building_set_t&& new_activated_buildings, ModifierValue&& new_values,
+ ConditionalWeight&& new_ai_chance
);
bool parse_scripts(DefinitionManager const& definition_manager);
@@ -87,8 +89,9 @@ namespace OpenVic {
bool add_technology(
std::string_view identifier, TechnologyArea const* area, Date::year_t year, fixed_point_t cost,
- bool unciv_military, uint8_t unit, Technology::unit_set_t&& activated_units,
- Technology::building_set_t&& activated_buildings, ModifierValue&& values, ConditionalWeight&& ai_chance
+ bool unciv_military, std::optional<CountryInstance::unit_variant_t>&& unit_variant,
+ Technology::unit_set_t&& activated_units, Technology::building_set_t&& activated_buildings,
+ ModifierValue&& values, ConditionalWeight&& ai_chance
);
bool add_technology_school(std::string_view identifier, ModifierValue&& values);
diff --git a/src/openvic-simulation/types/IndexedMap.hpp b/src/openvic-simulation/types/IndexedMap.hpp
index 30cf5cd..5682c0e 100644
--- a/src/openvic-simulation/types/IndexedMap.hpp
+++ b/src/openvic-simulation/types/IndexedMap.hpp
@@ -169,6 +169,16 @@ namespace OpenVic {
return true;
}
+ constexpr void write_non_empty_values(IndexedMap const& other) {
+ const size_t count = std::min(container_t::size(), other.size());
+ for (size_t index = 0; index < count; ++index) {
+ value_t const& value = other[index];
+ if (value) {
+ container_t::operator[](index) = value;
+ }
+ }
+ }
+
fixed_point_map_t<key_t const *> to_fixed_point_map() const
requires(std::same_as<value_t, fixed_point_t>)
{
diff --git a/src/openvic-simulation/types/Vector.hpp b/src/openvic-simulation/types/Vector.hpp
index 7ed952a..d318c2f 100644
--- a/src/openvic-simulation/types/Vector.hpp
+++ b/src/openvic-simulation/types/Vector.hpp
@@ -14,7 +14,7 @@ namespace OpenVic {
T x, y;
constexpr vec2_t() = default;
- constexpr vec2_t(T new_val) : x { new_val }, y { new_val } {}
+ explicit constexpr vec2_t(T new_val) : x { new_val }, y { new_val } {}
constexpr vec2_t(T new_x, T new_y) : x { new_x }, y { new_y } {}
constexpr vec2_t(vec2_t const&) = default;