aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/openvic-simulation/DefinitionManager.hpp2
-rw-r--r--src/openvic-simulation/InstanceManager.cpp30
-rw-r--r--src/openvic-simulation/country/CountryInstance.cpp224
-rw-r--r--src/openvic-simulation/country/CountryInstance.hpp76
-rw-r--r--src/openvic-simulation/economy/BuildingType.cpp25
-rw-r--r--src/openvic-simulation/economy/GoodDefinition.cpp95
-rw-r--r--src/openvic-simulation/economy/GoodDefinition.hpp3
-rw-r--r--src/openvic-simulation/map/Crime.cpp8
-rw-r--r--src/openvic-simulation/map/MapDefinition.cpp13
-rw-r--r--src/openvic-simulation/map/MapInstance.cpp6
-rw-r--r--src/openvic-simulation/map/MapInstance.hpp1
-rw-r--r--src/openvic-simulation/map/ProvinceDefinition.cpp2
-rw-r--r--src/openvic-simulation/map/ProvinceInstance.cpp126
-rw-r--r--src/openvic-simulation/map/ProvinceInstance.hpp20
-rw-r--r--src/openvic-simulation/map/TerrainType.cpp10
-rw-r--r--src/openvic-simulation/military/LeaderTrait.cpp10
-rw-r--r--src/openvic-simulation/military/UnitType.cpp107
-rw-r--r--src/openvic-simulation/military/UnitType.hpp6
-rw-r--r--src/openvic-simulation/modifier/Modifier.cpp655
-rw-r--r--src/openvic-simulation/modifier/Modifier.hpp189
-rw-r--r--src/openvic-simulation/modifier/ModifierEffect.cpp49
-rw-r--r--src/openvic-simulation/modifier/ModifierEffect.hpp63
-rw-r--r--src/openvic-simulation/modifier/ModifierEffectCache.cpp231
-rw-r--r--src/openvic-simulation/modifier/ModifierEffectCache.hpp307
-rw-r--r--src/openvic-simulation/modifier/ModifierManager.cpp866
-rw-r--r--src/openvic-simulation/modifier/ModifierManager.hpp144
-rw-r--r--src/openvic-simulation/modifier/ModifierSum.cpp103
-rw-r--r--src/openvic-simulation/modifier/ModifierSum.hpp63
-rw-r--r--src/openvic-simulation/modifier/ModifierValue.cpp149
-rw-r--r--src/openvic-simulation/modifier/ModifierValue.hpp50
-rw-r--r--src/openvic-simulation/modifier/StaticModifierCache.cpp115
-rw-r--r--src/openvic-simulation/modifier/StaticModifierCache.hpp64
-rw-r--r--src/openvic-simulation/politics/Issue.cpp11
-rw-r--r--src/openvic-simulation/politics/NationalFocus.cpp4
-rw-r--r--src/openvic-simulation/politics/NationalValue.cpp7
-rw-r--r--src/openvic-simulation/politics/Rebel.cpp14
-rw-r--r--src/openvic-simulation/politics/Rebel.hpp3
-rw-r--r--src/openvic-simulation/pop/Pop.cpp24
-rw-r--r--src/openvic-simulation/research/Invention.cpp19
-rw-r--r--src/openvic-simulation/research/Technology.cpp22
-rw-r--r--src/openvic-simulation/utility/NumberUtils.hpp4
41 files changed, 2912 insertions, 1008 deletions
diff --git a/src/openvic-simulation/DefinitionManager.hpp b/src/openvic-simulation/DefinitionManager.hpp
index 887cc1b..b5df507 100644
--- a/src/openvic-simulation/DefinitionManager.hpp
+++ b/src/openvic-simulation/DefinitionManager.hpp
@@ -14,7 +14,7 @@
#include "openvic-simulation/misc/Event.hpp"
#include "openvic-simulation/misc/SongChance.hpp"
#include "openvic-simulation/misc/SoundEffect.hpp"
-#include "openvic-simulation/modifier/Modifier.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
#include "openvic-simulation/politics/PoliticsManager.hpp"
#include "openvic-simulation/pop/Pop.hpp"
#include "openvic-simulation/research/ResearchManager.hpp"
diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp
index c9ce24a..eaa0692 100644
--- a/src/openvic-simulation/InstanceManager.cpp
+++ b/src/openvic-simulation/InstanceManager.cpp
@@ -12,7 +12,7 @@ InstanceManager::InstanceManager(
map_instance { new_definition_manager.get_map_definition() },
simulation_clock {
std::bind(&InstanceManager::tick, this), std::bind(&InstanceManager::update_gamestate, this),
- clock_state_changed_callback ? std::move(clock_state_changed_callback) : []() {}
+ clock_state_changed_callback ? std::move(clock_state_changed_callback) : []() {}
},
game_instance_setup { false },
game_session_started { false },
@@ -39,10 +39,36 @@ void InstanceManager::update_gamestate() {
Logger::info("Update: ", today);
+ if constexpr (ProvinceInstance::ADD_OWNER_CONTRIBUTION) {
+ // Calculate local province modifier sums first, then national country modifier sums, then loop over owned provinces
+ // adding their contributions to the owner country's modifier sum and loop over them again to add the country's total
+ // (including province contributions) to the provinces' modifier sum. This results in every country and province
+ // having a full copy of all the modifiers affecting them in their modifier sum.
+ map_instance.update_modifier_sums(
+ today, definition_manager.get_modifier_manager().get_static_modifier_cache()
+ );
+ country_instance_manager.update_modifier_sums(
+ today, definition_manager.get_modifier_manager().get_static_modifier_cache()
+ );
+ } else {
+ // Calculate national country modifier sums first, then local province modifier sums, adding province contributions
+ // to owner countries' modifier sums if each province has an owner. This results in every country having a full copy
+ // of all the modifiers affecting them in their modifier sum, but provinces only having their directly/locally applied
+ // modifiers in their modifier sum, hence requiring both province and owner country modifier effect values to be looked
+ // up and added together to get the full effect on the province.
+ country_instance_manager.update_modifier_sums(
+ today, definition_manager.get_modifier_manager().get_static_modifier_cache()
+ );
+ map_instance.update_modifier_sums(
+ today, definition_manager.get_modifier_manager().get_static_modifier_cache()
+ );
+ }
+
// 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()
+ today, definition_manager.get_define_manager(), definition_manager.get_military_manager().get_unit_type_manager(),
+ definition_manager.get_modifier_manager().get_modifier_effect_cache()
);
gamestate_updated();
diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp
index 55ceb23..417ca0a 100644
--- a/src/openvic-simulation/country/CountryInstance.cpp
+++ b/src/openvic-simulation/country/CountryInstance.cpp
@@ -5,6 +5,8 @@
#include "openvic-simulation/map/Crime.hpp"
#include "openvic-simulation/map/MapInstance.hpp"
#include "openvic-simulation/misc/Define.hpp"
+#include "openvic-simulation/modifier/ModifierEffectCache.hpp"
+#include "openvic-simulation/modifier/StaticModifierCache.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/research/Invention.hpp"
#include "openvic-simulation/research/Technology.hpp"
@@ -17,16 +19,16 @@ static constexpr colour_t ERROR_COLOUR = colour_t::from_integer(0xFF0000);
CountryInstance::CountryInstance(
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(building_type_unlock_levels)::keys_t const& building_type_keys,
+ decltype(technology_unlock_levels)::keys_t const& technology_keys,
+ decltype(invention_unlock_levels)::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(crime_unlock_levels)::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
+ decltype(regiment_type_unlock_levels)::keys_t const& regiment_type_unlock_levels_keys,
+ decltype(ship_type_unlock_levels)::keys_t const& ship_type_unlock_levels_keys
) : /* Main attributes */
country_definition { new_country_definition },
colour { ERROR_COLOUR },
@@ -41,6 +43,8 @@ CountryInstance::CountryInstance(
controlled_provinces {},
core_provinces {},
states {},
+ modifier_sum {},
+ event_modifiers {},
/* Production */
industrial_power { 0 },
@@ -48,14 +52,14 @@ CountryInstance::CountryInstance(
industrial_power_from_investments {},
industrial_rank { 0 },
foreign_investments {},
- unlocked_building_types { &building_type_keys },
+ building_type_unlock_levels { &building_type_keys },
/* Budget */
cash_stockpile { 0 },
/* Technology */
- unlocked_technologies { &technology_keys },
- unlocked_inventions { &invention_keys },
+ technology_unlock_levels { &technology_keys },
+ invention_unlock_levels { &invention_keys },
current_research { nullptr },
invested_research_points { 0 },
expected_completion_date {},
@@ -79,7 +83,7 @@ CountryInstance::CountryInstance(
infamy { 0 },
plurality { 0 },
revanchism { 0 },
- unlocked_crimes { &crime_keys },
+ crime_unlock_levels { &crime_keys },
/* Population */
primary_culture { nullptr },
@@ -121,32 +125,32 @@ CountryInstance::CountryInstance(
war_exhaustion { 0 },
mobilised { false },
disarmed { false },
- unlocked_regiment_types { &unlocked_regiment_types_keys },
+ regiment_type_unlock_levels { &regiment_type_unlock_levels_keys },
allowed_regiment_cultures { RegimentType::allowed_cultures_t::NO_CULTURES },
- unlocked_ship_types { &unlocked_ship_types_keys },
+ ship_type_unlock_levels { &ship_type_unlock_levels_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()) {
+ for (BuildingType const& building_type : *building_type_unlock_levels.get_keys()) {
if (building_type.is_default_enabled()) {
unlock_building_type(building_type);
}
}
- for (Crime const& crime : *unlocked_crimes.get_keys()) {
+ for (Crime const& crime : *crime_unlock_levels.get_keys()) {
if (crime.is_default_active()) {
unlock_crime(crime);
}
}
- for (RegimentType const& regiment_type : *unlocked_regiment_types.get_keys()) {
+ for (RegimentType const& regiment_type : *regiment_type_unlock_levels.get_keys()) {
if (regiment_type.is_active()) {
unlock_unit_type(regiment_type);
}
}
- for (ShipType const& ship_type : *unlocked_ship_types.get_keys()) {
+ for (ShipType const& ship_type : *ship_type_unlock_levels.get_keys()) {
if (ship_type.is_active()) {
unlock_unit_type(ship_type);
}
@@ -335,7 +339,7 @@ template bool CountryInstance::remove_leader(LeaderBranched<UnitType::branch_t::
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>();
+ IndexedMap<UnitTypeBranched<Branch>, unlock_level_t>& unlocked_unit_types = get_unit_type_unlock_levels<Branch>();
typename IndexedMap<UnitTypeBranched<Branch>, unlock_level_t>::value_ref_t unlock_level = unlocked_unit_types[unit_type];
@@ -384,9 +388,9 @@ bool CountryInstance::is_unit_type_unlocked(UnitType const& unit_type) const {
switch (unit_type.get_branch()) {
case LAND:
- return unlocked_regiment_types[static_cast<UnitTypeBranched<LAND> const&>(unit_type)] > 0;
+ return regiment_type_unlock_levels[static_cast<UnitTypeBranched<LAND> const&>(unit_type)] > 0;
case NAVAL:
- return unlocked_ship_types[static_cast<UnitTypeBranched<NAVAL> const&>(unit_type)] > 0;
+ return ship_type_unlock_levels[static_cast<UnitTypeBranched<NAVAL> const&>(unit_type)] > 0;
default:
Logger::error(
"Attempted to check if unit type \"", unit_type.get_identifier(), "\" with invalid branch ",
@@ -397,7 +401,7 @@ bool CountryInstance::is_unit_type_unlocked(UnitType const& unit_type) const {
}
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];
+ decltype(building_type_unlock_levels)::value_ref_t unlock_level = building_type_unlock_levels[building_type];
// This catches subtracting below 0 or adding above the int types maximum value
if (unlock_level + unlock_level_change < 0) {
@@ -420,11 +424,11 @@ bool CountryInstance::unlock_building_type(BuildingType const& building_type) {
}
bool CountryInstance::is_building_type_unlocked(BuildingType const& building_type) const {
- return unlocked_building_types[building_type] > 0;
+ return building_type_unlock_levels[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];
+ decltype(crime_unlock_levels)::value_ref_t unlock_level = crime_unlock_levels[crime];
// This catches subtracting below 0 or adding above the int types maximum value
if (unlock_level + unlock_level_change < 0) {
@@ -447,7 +451,7 @@ bool CountryInstance::unlock_crime(Crime const& crime) {
}
bool CountryInstance::is_crime_unlocked(Crime const& crime) const {
- return unlocked_crimes[crime] > 0;
+ return crime_unlock_levels[crime] > 0;
}
bool CountryInstance::modify_gas_attack_unlock(unlock_level_t unlock_level_change) {
@@ -543,7 +547,7 @@ CountryInstance::unit_variant_t CountryInstance::get_max_unlocked_unit_variant()
}
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];
+ decltype(technology_unlock_levels)::value_ref_t unlock_level = technology_unlock_levels[technology];
// This catches subtracting below 0 or adding above the int types maximum value
if (unlock_level + unlock_level_change < 0) {
@@ -576,7 +580,7 @@ bool CountryInstance::modify_technology_unlock(Technology const& technology, unl
}
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];
+ const unlock_level_t unlock_level_change = unlock_level - technology_unlock_levels[technology];
return unlock_level_change != 0 ? modify_technology_unlock(technology, unlock_level_change) : true;
}
@@ -585,11 +589,11 @@ bool CountryInstance::unlock_technology(Technology const& technology) {
}
bool CountryInstance::is_technology_unlocked(Technology const& technology) const {
- return unlocked_technologies[technology] > 0;
+ return technology_unlock_levels[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];
+ decltype(invention_unlock_levels)::value_ref_t unlock_level = invention_unlock_levels[invention];
// This catches subtracting below 0 or adding above the int types maximum value
if (unlock_level + unlock_level_change < 0) {
@@ -628,7 +632,7 @@ bool CountryInstance::modify_invention_unlock(Invention const& invention, unlock
}
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];
+ const unlock_level_t unlock_level_change = unlock_level - invention_unlock_levels[invention];
return unlock_level_change != 0 ? modify_invention_unlock(invention, unlock_level_change) : true;
}
@@ -637,7 +641,7 @@ bool CountryInstance::unlock_invention(Invention const& invention) {
}
bool CountryInstance::is_invention_unlocked(Invention const& invention) const {
- return unlocked_inventions[invention] > 0;
+ return invention_unlock_levels[invention] > 0;
}
bool CountryInstance::is_primary_culture(Culture const& culture) const {
@@ -671,7 +675,7 @@ bool CountryInstance::apply_history_to_country(
bool ret = true;
- set_optional(primary_culture, entry.get_primary_culture());
+ set_optional(primary_culture, entry.get_primary_culture());
for (auto const& [culture, add] : entry.get_accepted_cultures()) {
if (add) {
ret &= add_accepted_culture(*culture);
@@ -820,7 +824,10 @@ void CountryInstance::_update_diplomacy() {
// TODO - update diplomatic points and colonial power
}
-void CountryInstance::_update_military(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager) {
+void CountryInstance::_update_military(
+ DefineManager const& define_manager, UnitTypeManager const& unit_type_manager,
+ ModifierEffectCache const& modifier_effect_cache
+) {
regiment_count = 0;
for (ArmyInstance const* army : armies) {
@@ -851,8 +858,8 @@ void CountryInstance::_update_military(DefineManager const& define_manager, Unit
max_supported_regiment_count += state->get_max_supported_regiments();
}
- // TODO - apply country/tech modifiers to supply consumption
- supply_consumption = 1;
+ supply_consumption =
+ fixed_point_t::_1() + get_modifier_effect_value_nullcheck(modifier_effect_cache.get_supply_consumption());
const size_t regular_army_size = std::min(4 * deployed_non_mobilised_regiments, max_supported_regiment_count);
@@ -894,7 +901,7 @@ void CountryInstance::_update_military(DefineManager const& define_manager, Unit
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_impact = get_modifier_effect_value_nullcheck(modifier_effect_cache.get_mobilization_impact());
mobilisation_max_regiment_count =
((fixed_point_t::_1() + mobilisation_impact) * fixed_point_t::parse(regiment_count)).to_int64_t();
@@ -927,7 +934,125 @@ bool CountryInstance::update_rule_set() {
return rule_set.trim_and_resolve_conflicts(true);
}
-void CountryInstance::update_gamestate(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager) {
+void CountryInstance::update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache) {
+ // Update sum of national modifiers
+ modifier_sum.clear();
+
+ const ModifierSum::modifier_source_t country_source { this };
+
+ // Erase expired event modifiers and add non-expired ones to the sum
+ std::erase_if(event_modifiers, [this, today, &country_source](ModifierInstance const& modifier) -> bool {
+ if (today <= modifier.get_expiry_date()) {
+ modifier_sum.add_modifier(*modifier.get_modifier(), country_source);
+ return false;
+ } else {
+ return true;
+ }
+ });
+
+ // Add static modifiers
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_base_modifier(), country_source);
+
+ switch (country_status) {
+ using enum country_status_t;
+ case COUNTRY_STATUS_GREAT_POWER:
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_great_power(), country_source);
+ break;
+ case COUNTRY_STATUS_SECONDARY_POWER:
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_secondary_power(), country_source);
+ break;
+ case COUNTRY_STATUS_CIVILISED:
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_civilised(), country_source);
+ break;
+ default:
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_uncivilised(), country_source);
+ }
+ if (is_disarmed()) {
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_disarming(), country_source);
+ }
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_war_exhaustion(), country_source, war_exhaustion);
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_infamy(), country_source, infamy);
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_literacy(), country_source, national_literacy);
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_plurality(), country_source, plurality);
+ // TODO - difficulty modifiers, war, peace, debt_default_to, bad_debter, generalised_debt_default,
+ // total_occupation, total_blockaded, in_bankrupcy
+
+ // TODO - handle triggered modifiers
+
+ if (ruling_party != nullptr) {
+ for (Issue const* issue : ruling_party->get_policies()) {
+ // The ruling party's issues here could be null as they're stored in an IndexedMap which has
+ // values for every IssueGroup regardless of whether or not they have a policy set.
+ modifier_sum.add_modifier_nullcheck(issue, country_source);
+ }
+ }
+
+ for (Reform const* reform : reforms) {
+ // The country's reforms here could be null as they're stored in an IndexedMap which has
+ // values for every ReformGroup regardless of whether or not they have a reform set.
+ modifier_sum.add_modifier_nullcheck(reform, country_source);
+ }
+
+ modifier_sum.add_modifier_nullcheck(national_value, country_source);
+
+ modifier_sum.add_modifier_nullcheck(tech_school, country_source);
+
+ for (Technology const& technology : *technology_unlock_levels.get_keys()) {
+ if (is_technology_unlocked(technology)) {
+ modifier_sum.add_modifier(technology, country_source);
+ }
+ }
+
+ for (Invention const& invention : *invention_unlock_levels.get_keys()) {
+ if (is_invention_unlocked(invention)) {
+ modifier_sum.add_modifier(invention, country_source);
+ }
+ }
+
+ if constexpr (ProvinceInstance::ADD_OWNER_CONTRIBUTION) {
+ // Add province base modifiers (with local province modifier effects removed)
+ for (ProvinceInstance const* province : owned_provinces) {
+ contribute_province_modifier_sum(province->get_modifier_sum());
+ }
+
+ // This has to be done after adding all province modifiers to the country's sum, otherwise provinces
+ // earlier in the list wouldn't be affected by modifiers from provinces later in the list.
+ for (ProvinceInstance* province : owned_provinces) {
+ province->contribute_country_modifier_sum(modifier_sum);
+ }
+ }
+
+ // TODO - calculate stats for each unit type (locked and unlocked)
+}
+
+void CountryInstance::contribute_province_modifier_sum(ModifierSum const& province_modifier_sum) {
+ using enum ModifierEffect::target_t;
+
+ modifier_sum.add_modifier_sum_exclude_targets(province_modifier_sum, PROVINCE);
+}
+
+fixed_point_t CountryInstance::get_modifier_effect_value(ModifierEffect const& effect) const {
+ return modifier_sum.get_effect(effect);
+}
+
+fixed_point_t CountryInstance::get_modifier_effect_value_nullcheck(ModifierEffect const* effect) const {
+ return modifier_sum.get_effect_nullcheck(effect);
+}
+
+void CountryInstance::push_contributing_modifiers(
+ ModifierEffect const& effect, std::vector<ModifierSum::modifier_entry_t>& contributions
+) const {
+ modifier_sum.push_contributing_modifiers(effect, contributions);
+}
+
+std::vector<ModifierSum::modifier_entry_t> CountryInstance::get_contributing_modifiers(ModifierEffect const& effect) const {
+ return modifier_sum.get_contributing_modifiers(effect);
+}
+
+void CountryInstance::update_gamestate(
+ DefineManager const& define_manager, UnitTypeManager const& unit_type_manager,
+ ModifierEffectCache const& modifier_effect_cache
+) {
// Order of updates might need to be changed/functions split up to account for dependencies
_update_production(define_manager);
_update_budget();
@@ -936,7 +1061,7 @@ void CountryInstance::update_gamestate(DefineManager const& define_manager, Unit
_update_population();
_update_trade();
_update_diplomacy();
- _update_military(define_manager, unit_type_manager);
+ _update_military(define_manager, unit_type_manager, modifier_effect_cache);
total_score = prestige + industrial_power + military_power;
@@ -1095,16 +1220,16 @@ CountryInstance const& CountryInstanceManager::get_country_instance_from_definit
bool CountryInstanceManager::generate_country_instances(
CountryDefinitionManager const& country_definition_manager,
- 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::building_type_unlock_levels)::keys_t const& building_type_keys,
+ decltype(CountryInstance::technology_unlock_levels)::keys_t const& technology_keys,
+ decltype(CountryInstance::invention_unlock_levels)::keys_t const& invention_keys,
decltype(CountryInstance::upper_house)::keys_t const& ideology_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::crime_unlock_levels)::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
+ decltype(CountryInstance::regiment_type_unlock_levels)::keys_t const& regiment_type_unlock_levels_keys,
+ decltype(CountryInstance::ship_type_unlock_levels)::keys_t const& ship_type_unlock_levels_keys
) {
reserve_more(country_instances, country_definition_manager.get_country_definition_count());
@@ -1121,8 +1246,8 @@ bool CountryInstanceManager::generate_country_instances(
government_type_keys,
crime_keys,
pop_type_keys,
- unlocked_regiment_types_keys,
- unlocked_ship_types_keys
+ regiment_type_unlock_levels_keys,
+ ship_type_unlock_levels_keys
});
}
@@ -1171,11 +1296,18 @@ bool CountryInstanceManager::apply_history_to_countries(
return ret;
}
+void CountryInstanceManager::update_modifier_sums(Date today, StaticModifierCache const& static_modifier_cache) {
+ for (CountryInstance& country : country_instances.get_items()) {
+ country.update_modifier_sum(today, static_modifier_cache);
+ }
+}
+
void CountryInstanceManager::update_gamestate(
- Date today, DefineManager const& define_manager, UnitTypeManager const& unit_type_manager
+ Date today, DefineManager const& define_manager, UnitTypeManager const& unit_type_manager,
+ ModifierEffectCache const& modifier_effect_cache
) {
for (CountryInstance& country : country_instances.get_items()) {
- country.update_gamestate(define_manager, unit_type_manager);
+ country.update_gamestate(define_manager, unit_type_manager, modifier_effect_cache);
}
update_rankings(today, define_manager);
diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp
index a7128aa..ea30c0d 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/modifier/ModifierSum.hpp"
#include "openvic-simulation/politics/Rule.hpp"
#include "openvic-simulation/pop/Pop.hpp"
#include "openvic-simulation/types/Date.hpp"
@@ -34,6 +35,8 @@ namespace OpenVic {
struct CountryHistoryEntry;
struct MapInstance;
struct DefineManager;
+ struct ModifierEffectCache;
+ struct StaticModifierCache;
/* 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. */
@@ -80,13 +83,17 @@ namespace OpenVic {
ordered_set<ProvinceInstance*> PROPERTY(core_provinces);
ordered_set<State*> PROPERTY(states);
+ // The total/resultant modifier affecting this country, including owned province contributions.
+ ModifierSum PROPERTY(modifier_sum);
+ std::vector<ModifierInstance> PROPERTY(event_modifiers);
+
/* 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);
+ IndexedMap<BuildingType, unlock_level_t> PROPERTY(building_type_unlock_levels);
// TODO - total amount of each good produced
/* Budget */
@@ -94,8 +101,8 @@ namespace OpenVic {
// TODO - cash stockpile change over last 30 days
/* Technology */
- IndexedMap<Technology, unlock_level_t> PROPERTY(unlocked_technologies);
- IndexedMap<Invention, unlock_level_t> PROPERTY(unlocked_inventions);
+ IndexedMap<Technology, unlock_level_t> PROPERTY(technology_unlock_levels);
+ IndexedMap<Invention, unlock_level_t> PROPERTY(invention_unlock_levels);
Technology const* PROPERTY(current_research);
fixed_point_t PROPERTY(invested_research_points);
Date PROPERTY(expected_completion_date);
@@ -118,10 +125,10 @@ namespace OpenVic {
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(infamy); // in 0-25+ range
+ fixed_point_t PROPERTY(plurality); // in 0-100 range
fixed_point_t PROPERTY(revanchism);
- IndexedMap<Crime, unlock_level_t> PROPERTY(unlocked_crimes);
+ IndexedMap<Crime, unlock_level_t> PROPERTY(crime_unlock_levels);
// TODO - rebel movements
/* Population */
@@ -165,32 +172,32 @@ namespace OpenVic {
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);
+ fixed_point_t PROPERTY(war_exhaustion); // in 0-100 range
bool PROPERTY_CUSTOM_PREFIX(mobilised, is);
bool PROPERTY_CUSTOM_PREFIX(disarmed, is);
- IndexedMap<RegimentType, unlock_level_t> PROPERTY(unlocked_regiment_types);
+ IndexedMap<RegimentType, unlock_level_t> PROPERTY(regiment_type_unlock_levels);
RegimentType::allowed_cultures_t PROPERTY(allowed_regiment_cultures);
- IndexedMap<ShipType, unlock_level_t> PROPERTY(unlocked_ship_types);
+ IndexedMap<ShipType, unlock_level_t> PROPERTY(ship_type_unlock_levels);
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);
+ UNIT_BRANCHED_GETTER(get_unit_type_unlock_levels, regiment_type_unlock_levels, ship_type_unlock_levels);
CountryInstance(
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(building_type_unlock_levels)::keys_t const& building_type_keys,
+ decltype(technology_unlock_levels)::keys_t const& technology_keys,
+ decltype(invention_unlock_levels)::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(crime_unlock_levels)::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
+ decltype(regiment_type_unlock_levels)::keys_t const& regiment_type_unlock_levels_keys,
+ decltype(ship_type_unlock_levels)::keys_t const& ship_type_unlock_levels_keys
);
public:
@@ -290,13 +297,28 @@ namespace OpenVic {
void _update_population();
void _update_trade();
void _update_diplomacy();
- void _update_military(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager);
+ void _update_military(
+ DefineManager const& define_manager, UnitTypeManager const& unit_type_manager,
+ ModifierEffectCache const& modifier_effect_cache
+ );
bool update_rule_set();
public:
- void update_gamestate(DefineManager const& define_manager, UnitTypeManager const& unit_type_manager);
+ void update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache);
+ void contribute_province_modifier_sum(ModifierSum const& province_modifier_sum);
+ fixed_point_t get_modifier_effect_value(ModifierEffect const& effect) const;
+ fixed_point_t get_modifier_effect_value_nullcheck(ModifierEffect const* effect) const;
+ void push_contributing_modifiers(
+ ModifierEffect const& effect, std::vector<ModifierSum::modifier_entry_t>& contributions
+ ) const;
+ std::vector<ModifierSum::modifier_entry_t> get_contributing_modifiers(ModifierEffect const& effect) const;
+
+ void update_gamestate(
+ DefineManager const& define_manager, UnitTypeManager const& unit_type_manager,
+ ModifierEffectCache const& modifier_effect_cache
+ );
void tick();
};
@@ -324,16 +346,16 @@ namespace OpenVic {
bool generate_country_instances(
CountryDefinitionManager const& country_definition_manager,
- 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::building_type_unlock_levels)::keys_t const& building_type_keys,
+ decltype(CountryInstance::technology_unlock_levels)::keys_t const& technology_keys,
+ decltype(CountryInstance::invention_unlock_levels)::keys_t const& invention_keys,
decltype(CountryInstance::upper_house)::keys_t const& ideology_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::crime_unlock_levels)::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
+ decltype(CountryInstance::regiment_type_unlock_levels)::keys_t const& regiment_type_unlock_levels_keys,
+ decltype(CountryInstance::ship_type_unlock_levels)::keys_t const& ship_type_unlock_levels_keys
);
bool apply_history_to_countries(
@@ -341,7 +363,11 @@ namespace OpenVic {
MapInstance& map_instance
);
- void update_gamestate(Date today, DefineManager const& define_manager, UnitTypeManager const& unit_type_manager);
+ void update_modifier_sums(Date today, StaticModifierCache const& static_modifier_cache);
+ void update_gamestate(
+ Date today, DefineManager const& define_manager, UnitTypeManager const& unit_type_manager,
+ ModifierEffectCache const& modifier_effect_cache
+ );
void tick();
};
}
diff --git a/src/openvic-simulation/economy/BuildingType.cpp b/src/openvic-simulation/economy/BuildingType.cpp
index e7c0d59..a21361f 100644
--- a/src/openvic-simulation/economy/BuildingType.cpp
+++ b/src/openvic-simulation/economy/BuildingType.cpp
@@ -1,5 +1,7 @@
#include "BuildingType.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -59,9 +61,13 @@ bool BuildingTypeManager::load_buildings_file(
building_types, [this, &good_definition_manager, &production_type_manager, &modifier_manager](
std::string_view key, ast::NodeCPtr value
) -> bool {
+ using enum Modifier::modifier_type_t;
+
BuildingType::building_type_args_t building_type_args {};
- bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(building_type_args.modifier),
+ bool ret = modifier_manager.expect_modifier_value_and_keys(
+ move_variable_callback(building_type_args.modifier),
+ BUILDING,
"type", ONE_EXACTLY, expect_identifier(assign_variable_callback(building_type_args.type)),
"on_completion", ZERO_OR_ONE, expect_identifier(assign_variable_callback(building_type_args.on_completion)),
"completion_size", ZERO_OR_ONE,
@@ -112,18 +118,27 @@ bool BuildingTypeManager::load_buildings_file(
)(root);
lock_building_types();
- for (BuildingType const& building_type : building_types.get_items()) {
+ IndexedMap<BuildingType, ModifierEffectCache::building_type_effects_t>& building_type_effects =
+ modifier_manager.modifier_effect_cache.building_type_effects;
+
+ building_type_effects.set_keys(&get_building_types());
+
+ for (BuildingType const& building_type : get_building_types()) {
using enum ModifierEffect::format_t;
+ using enum ModifierEffect::target_t;
+
+ ModifierEffectCache::building_type_effects_t& this_building_type_effects = building_type_effects[building_type];
static constexpr std::string_view max_prefix = "max_";
static constexpr std::string_view min_prefix = "min_build_";
ret &= modifier_manager.add_modifier_effect(
- StringUtils::append_string_views(max_prefix, building_type.get_identifier()), true, INT,
- StringUtils::append_string_views("$", building_type.get_identifier(), "$ $TECH_MAX_LEVEL$")
+ this_building_type_effects.max_level, StringUtils::append_string_views(max_prefix, building_type.get_identifier()),
+ true, INT, PROVINCE, StringUtils::append_string_views("$", building_type.get_identifier(), "$ $TECH_MAX_LEVEL$")
);
// TODO - add custom localisation for "min_build_$building_type$" modifiers
ret &= modifier_manager.add_modifier_effect(
- StringUtils::append_string_views(min_prefix, building_type.get_identifier()), false, INT
+ this_building_type_effects.min_level, StringUtils::append_string_views(min_prefix, building_type.get_identifier()),
+ false, INT, PROVINCE
);
if (building_type.is_in_province()) {
diff --git a/src/openvic-simulation/economy/GoodDefinition.cpp b/src/openvic-simulation/economy/GoodDefinition.cpp
index ed24549..f8bd9b7 100644
--- a/src/openvic-simulation/economy/GoodDefinition.cpp
+++ b/src/openvic-simulation/economy/GoodDefinition.cpp
@@ -1,5 +1,7 @@
#include "GoodDefinition.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -89,39 +91,82 @@ bool GoodDefinitionManager::load_goods_file(ast::NodeCPtr root) {
bool GoodDefinitionManager::generate_modifiers(ModifierManager& modifier_manager) const {
using enum ModifierEffect::format_t;
+ using enum ModifierEffect::target_t;
+
+ IndexedMap<GoodDefinition, ModifierEffectCache::good_effects_t>& good_effects =
+ modifier_manager.modifier_effect_cache.good_effects;
+
+ good_effects.set_keys(&get_good_definitions());
bool ret = true;
- const auto good_modifier = [this, &modifier_manager, &ret](
- std::string_view name, bool is_positive_good, auto make_localisation_suffix
- ) -> void {
- ret &= modifier_manager.register_complex_modifier(name);
+ ret &= modifier_manager.register_complex_modifier("artisan_goods_input");
+ ret &= modifier_manager.register_complex_modifier("artisan_goods_output");
+ ret &= modifier_manager.register_complex_modifier("artisan_goods_throughput");
+ ret &= modifier_manager.register_complex_modifier("factory_goods_input");
+ ret &= modifier_manager.register_complex_modifier("factory_goods_output");
+ ret &= modifier_manager.register_complex_modifier("factory_goods_throughput");
+ ret &= modifier_manager.register_complex_modifier("rgo_goods_output");
+ ret &= modifier_manager.register_complex_modifier("rgo_goods_throughput");
+ ret &= modifier_manager.register_complex_modifier("rgo_size");
- for (GoodDefinition const& good : get_good_definitions()) {
+ for (GoodDefinition const& good : get_good_definitions()) {
+ const std::string_view good_identifier = good.get_identifier();
+ ModifierEffectCache::good_effects_t& this_good_effects = good_effects[good];
+
+ const auto good_modifier = [&modifier_manager, &ret, &good_identifier](
+ ModifierEffect const*& effect_cache, std::string_view name, bool is_positive_good,
+ std::string_view localisation_key
+ ) -> void {
ret &= modifier_manager.add_modifier_effect(
- ModifierManager::get_flat_identifier(name, good.get_identifier()), is_positive_good, PROPORTION_DECIMAL,
- make_localisation_suffix(good.get_identifier())
+ effect_cache, ModifierManager::get_flat_identifier(name, good_identifier), is_positive_good,
+ PROPORTION_DECIMAL, COUNTRY, localisation_key
);
- }
- };
+ };
- const auto make_production_localisation_suffix = [](std::string_view localisation_suffix) -> auto {
- return [localisation_suffix](std::string_view good_identifier) -> std::string {
- return StringUtils::append_string_views("$", good_identifier, "$ $", localisation_suffix, "$");
+ const auto make_production_localisation_suffix = [&good_identifier](
+ std::string_view localisation_suffix
+ ) -> std::string {
+ return StringUtils::append_string_views("$", good_identifier, "$ $", localisation_suffix, "$");
};
- };
-
- good_modifier("artisan_goods_input", false, make_production_localisation_suffix("TECH_INPUT"));
- good_modifier("artisan_goods_output", true, make_production_localisation_suffix("TECH_OUTPUT"));
- good_modifier("artisan_goods_throughput", true, make_production_localisation_suffix("TECH_THROUGHPUT"));
- good_modifier("factory_goods_input", false, make_production_localisation_suffix("TECH_INPUT"));
- good_modifier("factory_goods_output", true, make_production_localisation_suffix("TECH_OUTPUT"));
- good_modifier("factory_goods_throughput", true, make_production_localisation_suffix("TECH_THROUGHPUT"));
- good_modifier("rgo_goods_output", true, make_production_localisation_suffix("TECH_OUTPUT"));
- good_modifier("rgo_goods_throughput", true, make_production_localisation_suffix("TECH_THROUGHPUT"));
- good_modifier("rgo_size", true, [](std::string_view good_identifier) -> std::string {
- return StringUtils::append_string_views(good_identifier, "_RGO_SIZE");
- });
+
+ good_modifier(
+ this_good_effects.artisan_goods_input, "artisan_goods_input", false,
+ make_production_localisation_suffix("TECH_INPUT")
+ );
+ good_modifier(
+ this_good_effects.artisan_goods_output, "artisan_goods_output", true,
+ make_production_localisation_suffix("TECH_OUTPUT")
+ );
+ good_modifier(
+ this_good_effects.artisan_goods_throughput, "artisan_goods_throughput", true,
+ make_production_localisation_suffix("TECH_THROUGHPUT")
+ );
+ good_modifier(
+ this_good_effects.factory_goods_input, "factory_goods_input", false,
+ make_production_localisation_suffix("TECH_INPUT")
+ );
+ good_modifier(
+ this_good_effects.factory_goods_output, "factory_goods_output", true,
+ make_production_localisation_suffix("TECH_OUTPUT")
+ );
+ good_modifier(
+ this_good_effects.factory_goods_throughput, "factory_goods_throughput", true,
+ make_production_localisation_suffix("TECH_THROUGHPUT")
+ );
+ good_modifier(
+ this_good_effects.rgo_goods_output, "rgo_goods_output", true,
+ make_production_localisation_suffix("TECH_OUTPUT")
+ );
+ good_modifier(
+ this_good_effects.rgo_goods_throughput, "rgo_goods_throughput", true,
+ make_production_localisation_suffix("TECH_THROUGHPUT")
+ );
+ good_modifier(
+ this_good_effects.rgo_size, "rgo_size", true,
+ StringUtils::append_string_views(good_identifier, "_RGO_SIZE")
+ );
+ }
return ret;
}
diff --git a/src/openvic-simulation/economy/GoodDefinition.hpp b/src/openvic-simulation/economy/GoodDefinition.hpp
index bc231cb..823c1a3 100644
--- a/src/openvic-simulation/economy/GoodDefinition.hpp
+++ b/src/openvic-simulation/economy/GoodDefinition.hpp
@@ -1,6 +1,5 @@
#pragma once
-#include "openvic-simulation/modifier/Modifier.hpp"
#include "openvic-simulation/types/IdentifierRegistry.hpp"
namespace OpenVic {
@@ -51,6 +50,8 @@ namespace OpenVic {
GoodDefinition(GoodDefinition&&) = default;
};
+ struct ModifierManager;
+
struct GoodDefinitionManager {
private:
IdentifierRegistry<GoodCategory> IDENTIFIER_REGISTRY_CUSTOM_PLURAL(good_category, good_categories);
diff --git a/src/openvic-simulation/map/Crime.cpp b/src/openvic-simulation/map/Crime.cpp
index fb521ad..176f71b 100644
--- a/src/openvic-simulation/map/Crime.cpp
+++ b/src/openvic-simulation/map/Crime.cpp
@@ -1,5 +1,7 @@
#include "Crime.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -27,17 +29,23 @@ bool CrimeManager::load_crime_modifiers(ModifierManager const& modifier_manager,
const bool ret = expect_dictionary_reserve_length(
crime_modifiers,
[this, &modifier_manager](std::string_view key, ast::NodeCPtr value) -> bool {
+ using enum Modifier::modifier_type_t;
+
ModifierValue modifier_value;
IconModifier::icon_t icon = 0;
ConditionScript trigger { scope_t::PROVINCE, scope_t::NO_SCOPE, scope_t::NO_SCOPE };
bool default_active = false;
+
bool ret = modifier_manager.expect_modifier_value_and_keys(
move_variable_callback(modifier_value),
+ CRIME,
"icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)),
"trigger", ONE_EXACTLY, trigger.expect_script(),
"active", ZERO_OR_ONE, expect_bool(assign_variable_callback(default_active))
)(value);
+
ret &= add_crime_modifier(key, std::move(modifier_value), icon, std::move(trigger), default_active);
+
return ret;
}
)(root);
diff --git a/src/openvic-simulation/map/MapDefinition.cpp b/src/openvic-simulation/map/MapDefinition.cpp
index 828a679..5fe7189 100644
--- a/src/openvic-simulation/map/MapDefinition.cpp
+++ b/src/openvic-simulation/map/MapDefinition.cpp
@@ -3,6 +3,7 @@
#include <cstdint>
#include <vector>
+#include "openvic-simulation/modifier/ModifierManager.hpp"
#include "openvic-simulation/types/Colour.hpp"
#include "openvic-simulation/types/OrderedContainers.hpp"
#include "openvic-simulation/types/Vector.hpp"
@@ -954,8 +955,12 @@ bool MapDefinition::load_climate_file(ModifierManager const& modifier_manager, a
bool ret = true;
Climate* cur_climate = climates.get_item_by_identifier(identifier);
if (cur_climate == nullptr) {
+ using enum Modifier::modifier_type_t;
+
ModifierValue values;
- ret &= modifier_manager.expect_modifier_value(move_variable_callback(values))(node);
+
+ ret &= modifier_manager.expect_modifier_value(move_variable_callback(values), CLIMATE)(node);
+
ret &= climates.add_item({ identifier, std::move(values), Modifier::modifier_type_t::CLIMATE });
} else {
ret &= expect_list_reserve_length(*cur_climate, expect_province_definition_identifier(
@@ -999,6 +1004,8 @@ bool MapDefinition::load_continent_file(ModifierManager const& modifier_manager,
bool ret = expect_dictionary_reserve_length(
continents,
[this, &modifier_manager](std::string_view identifier, ast::NodeCPtr node) -> bool {
+ using enum Modifier::modifier_type_t;
+
if (identifier.empty()) {
Logger::error("Invalid continent identifier - empty!");
return false;
@@ -1006,7 +1013,9 @@ bool MapDefinition::load_continent_file(ModifierManager const& modifier_manager,
ModifierValue values;
std::vector<ProvinceDefinition const*> prov_list;
- 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),
+ CONTINENT,
"provinces", ONE_EXACTLY, expect_list_reserve_length(prov_list, expect_province_definition_identifier(
[&prov_list](ProvinceDefinition const& province) -> bool {
if (province.continent == nullptr) {
diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp
index 0ce8cea..d8ec2fb 100644
--- a/src/openvic-simulation/map/MapInstance.cpp
+++ b/src/openvic-simulation/map/MapInstance.cpp
@@ -118,6 +118,12 @@ bool MapInstance::apply_history_to_provinces(
return ret;
}
+void MapInstance::update_modifier_sums(Date today, StaticModifierCache const& static_modifier_cache) {
+ for (ProvinceInstance& province : province_instances.get_items()) {
+ province.update_modifier_sum(today, static_modifier_cache);
+ }
+}
+
void MapInstance::update_gamestate(Date today, DefineManager const& define_manager) {
for (ProvinceInstance& province : province_instances.get_items()) {
province.update_gamestate(today, define_manager);
diff --git a/src/openvic-simulation/map/MapInstance.hpp b/src/openvic-simulation/map/MapInstance.hpp
index 99c13d3..e7c623e 100644
--- a/src/openvic-simulation/map/MapInstance.hpp
+++ b/src/openvic-simulation/map/MapInstance.hpp
@@ -52,6 +52,7 @@ namespace OpenVic {
IssueManager const& issue_manager
);
+ void update_modifier_sums(Date today, StaticModifierCache const& static_modifier_cache);
void update_gamestate(Date today, DefineManager const& define_manager);
void tick(Date today);
};
diff --git a/src/openvic-simulation/map/ProvinceDefinition.cpp b/src/openvic-simulation/map/ProvinceDefinition.cpp
index 14828e8..60c8d6c 100644
--- a/src/openvic-simulation/map/ProvinceDefinition.cpp
+++ b/src/openvic-simulation/map/ProvinceDefinition.cpp
@@ -89,7 +89,7 @@ bool ProvinceDefinition::load_positions(
port = true;
port_adjacent_province = province;
} else {
- /* Expected provinces with invalid ports: 39, 296, 1047, 1406, 2044 */
+ /* Expected provinces with invalid ports: 39, 296, 1047, 1406, 2044 */
Logger::warning(
"Invalid port for province ", get_identifier(), ": facing province ", province,
" which has: water = ", province->is_water(), ", adjacent = ", is_adjacent_to(province)
diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp
index 06b3f1e..236efd2 100644
--- a/src/openvic-simulation/map/ProvinceInstance.cpp
+++ b/src/openvic-simulation/map/ProvinceInstance.cpp
@@ -2,9 +2,13 @@
#include "openvic-simulation/country/CountryInstance.hpp"
#include "openvic-simulation/history/ProvinceHistory.hpp"
+#include "openvic-simulation/map/Crime.hpp"
#include "openvic-simulation/map/ProvinceDefinition.hpp"
+#include "openvic-simulation/map/Region.hpp"
+#include "openvic-simulation/map/TerrainType.hpp"
#include "openvic-simulation/military/UnitInstanceGroup.hpp"
#include "openvic-simulation/misc/Define.hpp"
+#include "openvic-simulation/modifier/StaticModifierCache.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
using namespace OpenVic;
@@ -21,6 +25,8 @@ ProvinceInstance::ProvinceInstance(
owner { nullptr },
controller { nullptr },
cores {},
+ modifier_sum {},
+ event_modifiers {},
slave { false },
crime { nullptr },
rgo { nullptr },
@@ -185,6 +191,126 @@ void ProvinceInstance::_update_pops(DefineManager const& define_manager) {
}
}
+void ProvinceInstance::update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache) {
+ // Update sum of direct province modifiers
+ modifier_sum.clear();
+
+ const ModifierSum::modifier_source_t province_source { this };
+
+ // Erase expired event modifiers and add non-expired ones to the sum
+ std::erase_if(event_modifiers, [this, today, &province_source](ModifierInstance const& modifier) -> bool {
+ if (today <= modifier.get_expiry_date()) {
+ modifier_sum.add_modifier(*modifier.get_modifier(), province_source);
+ return false;
+ } else {
+ return true;
+ }
+ });
+
+ // Add static modifiers
+ if (is_owner_core()) {
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_core(), province_source);
+ }
+ if (province_definition.is_water()) {
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_sea_zone(), province_source);
+ } else {
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_land_province(), province_source);
+
+ if (province_definition.is_coastal()) {
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_coastal(), province_source);
+ } else {
+ modifier_sum.add_modifier_nullcheck(static_modifier_cache.get_non_coastal(), province_source);
+ }
+
+ // TODO - overseas, blockaded, no_adjacent_controlled, has_siege, occupied, nationalism, infrastructure
+ }
+
+ for (BuildingInstance const& building : buildings.get_items()) {
+ modifier_sum.add_modifier(building.get_building_type(), province_source);
+ }
+
+ modifier_sum.add_modifier_nullcheck(crime, province_source);
+
+ modifier_sum.add_modifier_nullcheck(province_definition.get_continent(), province_source);
+
+ modifier_sum.add_modifier_nullcheck(province_definition.get_climate(), province_source);
+
+ modifier_sum.add_modifier_nullcheck(terrain_type, province_source);
+
+ if constexpr (!ADD_OWNER_CONTRIBUTION) {
+ if (owner != nullptr) {
+ owner->contribute_province_modifier_sum(modifier_sum);
+ }
+ }
+}
+
+void ProvinceInstance::contribute_country_modifier_sum(ModifierSum const& owner_modifier_sum) {
+ modifier_sum.add_modifier_sum_exclude_source(owner_modifier_sum, this);
+}
+
+fixed_point_t ProvinceInstance::get_modifier_effect_value(ModifierEffect const& effect) const {
+ if constexpr (ADD_OWNER_CONTRIBUTION) {
+ return modifier_sum.get_effect(effect);
+ } else {
+ using enum ModifierEffect::target_t;
+
+ if (owner != nullptr) {
+ if (ModifierEffect::excludes_targets(effect.get_targets(), PROVINCE)) {
+ // Non-province targeted effects are already added to the country modifier sum
+ return owner->get_modifier_effect_value(effect);
+ } else {
+ // Province-targeted effects aren't passed to the country modifier sum
+ return owner->get_modifier_effect_value(effect) + modifier_sum.get_effect(effect);
+ }
+ } else {
+ return modifier_sum.get_effect(effect);
+ }
+ }
+}
+
+fixed_point_t ProvinceInstance::get_modifier_effect_value_nullcheck(ModifierEffect const* effect) const {
+ if (effect != nullptr) {
+ return get_modifier_effect_value(*effect);
+ } else {
+ return fixed_point_t::_0();
+ }
+}
+
+void ProvinceInstance::push_contributing_modifiers(
+ ModifierEffect const& effect, std::vector<ModifierSum::modifier_entry_t>& contributions
+) const {
+ if constexpr (ADD_OWNER_CONTRIBUTION) {
+ modifier_sum.push_contributing_modifiers(effect, contributions);
+ } else {
+ using enum ModifierEffect::target_t;
+
+ if (owner != nullptr) {
+ if (ModifierEffect::excludes_targets(effect.get_targets(), PROVINCE)) {
+ // Non-province targeted effects are already added to the country modifier sum
+ owner->push_contributing_modifiers(effect, contributions);
+ } else {
+ // Province-targeted effects aren't passed to the country modifier sum
+ modifier_sum.push_contributing_modifiers(effect, contributions);
+ owner->push_contributing_modifiers(effect, contributions);
+ }
+ } else {
+ modifier_sum.push_contributing_modifiers(effect, contributions);
+ }
+ }
+}
+
+std::vector<ModifierSum::modifier_entry_t> ProvinceInstance::get_contributing_modifiers(ModifierEffect const& effect) const {
+ if constexpr (ADD_OWNER_CONTRIBUTION) {
+ return modifier_sum.get_contributing_modifiers(effect);
+ } else {
+ std::vector<ModifierSum::modifier_entry_t> contributions;
+
+ push_contributing_modifiers(effect, contributions);
+
+ return contributions;
+ }
+}
+
void ProvinceInstance::update_gamestate(Date today, DefineManager const& define_manager) {
for (BuildingInstance& building : buildings.get_items()) {
building.update_gamestate(today);
diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp
index fa0be98..e7c0326 100644
--- a/src/openvic-simulation/map/ProvinceInstance.hpp
+++ b/src/openvic-simulation/map/ProvinceInstance.hpp
@@ -5,6 +5,7 @@
#include "openvic-simulation/economy/BuildingInstance.hpp"
#include "openvic-simulation/military/UnitInstance.hpp"
#include "openvic-simulation/military/UnitType.hpp"
+#include "openvic-simulation/modifier/ModifierSum.hpp"
#include "openvic-simulation/pop/Pop.hpp"
#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp"
#include "openvic-simulation/types/HasIdentifier.hpp"
@@ -21,6 +22,7 @@ namespace OpenVic {
struct Ideology;
struct Culture;
struct Religion;
+ struct StaticModifierCache;
struct BuildingTypeManager;
struct ProvinceHistoryEntry;
struct IssueManager;
@@ -68,6 +70,15 @@ namespace OpenVic {
CountryInstance* PROPERTY(controller);
ordered_set<CountryInstance*> PROPERTY(cores);
+ public:
+ static constexpr bool ADD_OWNER_CONTRIBUTION = true;
+
+ private:
+ // The total/resultant modifier affecting this province, including owner country contributions if
+ // ADD_OWNER_CONTRIBUTION is true.
+ ModifierSum PROPERTY(modifier_sum);
+ std::vector<ModifierInstance> PROPERTY(event_modifiers);
+
bool PROPERTY(slave);
Crime const* PROPERTY_RW(crime);
// TODO - change this into a factory-like structure
@@ -124,6 +135,15 @@ namespace OpenVic {
bool add_pop_vec(std::vector<PopBase> const& pop_vec);
size_t get_pop_count() const;
+ void update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache);
+ void contribute_country_modifier_sum(ModifierSum const& owner_modifier_sum);
+ fixed_point_t get_modifier_effect_value(ModifierEffect const& effect) const;
+ fixed_point_t get_modifier_effect_value_nullcheck(ModifierEffect const* effect) const;
+ void push_contributing_modifiers(
+ ModifierEffect const& effect, std::vector<ModifierSum::modifier_entry_t>& contributions
+ ) const;
+ std::vector<ModifierSum::modifier_entry_t> get_contributing_modifiers(ModifierEffect const& effect) const;
+
void update_gamestate(Date today, DefineManager const& define_manager);
void tick(Date today);
diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp
index b4cc430..1531507 100644
--- a/src/openvic-simulation/map/TerrainType.cpp
+++ b/src/openvic-simulation/map/TerrainType.cpp
@@ -1,5 +1,6 @@
#include "TerrainType.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
#include "openvic-simulation/types/Colour.hpp"
using namespace OpenVic;
@@ -71,14 +72,21 @@ node_callback_t TerrainTypeManager::_load_terrain_type_categories(ModifierManage
return [this, &modifier_manager](ast::NodeCPtr root) -> bool {
const bool ret = expect_dictionary_reserve_length(terrain_types,
[this, &modifier_manager](std::string_view type_key, ast::NodeCPtr type_node) -> bool {
+ using enum Modifier::modifier_type_t;
+
ModifierValue values;
colour_t colour = colour_t::null();
bool is_water = false;
- 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),
+ TERRAIN,
"color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)),
"is_water", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_water))
)(type_node);
+
ret &= add_terrain_type(type_key, colour, std::move(values), is_water);
+
return ret;
}
)(root);
diff --git a/src/openvic-simulation/military/LeaderTrait.cpp b/src/openvic-simulation/military/LeaderTrait.cpp
index da7331a..7835636 100644
--- a/src/openvic-simulation/military/LeaderTrait.cpp
+++ b/src/openvic-simulation/military/LeaderTrait.cpp
@@ -1,5 +1,7 @@
#include "LeaderTrait.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -30,17 +32,21 @@ bool LeaderTraitManager::load_leader_traits_file(ModifierManager const& modifier
return expect_dictionary_reserve_length(
leader_traits,
[this, &modifier_manager, type](std::string_view trait_identifier, ast::NodeCPtr value) -> bool {
+ using enum Modifier::modifier_type_t;
+
static const string_set_t allowed_modifiers = {
- "attack", "defence", "morale", "organisation", "reconnaissance",
+ "attack", "defence leader", "morale", "organisation", "reconnaissance",
"speed", "attrition", "experience", "reliability"
};
ModifierValue modifiers;
+
bool ret = modifier_manager.expect_whitelisted_modifier_value(
- move_variable_callback(modifiers), allowed_modifiers
+ move_variable_callback(modifiers), LEADER, allowed_modifiers
)(value);
ret &= add_leader_trait(trait_identifier, type, std::move(modifiers));
+
return ret;
}
);
diff --git a/src/openvic-simulation/military/UnitType.cpp b/src/openvic-simulation/military/UnitType.cpp
index 71bc5c3..22e108d 100644
--- a/src/openvic-simulation/military/UnitType.cpp
+++ b/src/openvic-simulation/military/UnitType.cpp
@@ -2,6 +2,7 @@
#include "openvic-simulation/country/CountryInstance.hpp"
#include "openvic-simulation/map/TerrainType.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -28,8 +29,17 @@ UnitType::UnitType(
build_time { unit_args.build_time },
build_cost { std::move(unit_args.build_cost) },
supply_consumption { unit_args.supply_consumption },
- supply_cost { std::move(unit_args.supply_cost) },
- terrain_modifiers { std::move(unit_args.terrain_modifiers) } {}
+ supply_cost { std::move(unit_args.supply_cost) } {
+
+ using enum Modifier::modifier_type_t;
+
+ for (auto [terrain, modifier_value] : mutable_iterator(unit_args.terrain_modifier_values)) {
+ terrain_modifiers.emplace(terrain, Modifier {
+ StringUtils::append_string_views(new_identifier, " ", terrain->get_identifier()), std::move(modifier_value),
+ UNIT_TERRAIN
+ });
+ }
+}
bool UnitTypeBranched<LAND>::allowed_cultures_check_culture_in_country(
allowed_cultures_t allowed_cultures, Culture const& culture, CountryInstance const& country
@@ -231,16 +241,20 @@ bool UnitTypeManager::load_unit_type_file(
good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(unit_args.supply_cost))
);
- auto add_terrain_modifier = [&unit_args, &terrain_type_manager, &modifier_manager](
+ auto add_terrain_modifier_value = [&unit_args, &terrain_type_manager, &modifier_manager](
std::string_view default_key, ast::NodeCPtr default_value
) -> bool {
TerrainType const* terrain_type = terrain_type_manager.get_terrain_type_by_identifier(default_key);
+
if (terrain_type != nullptr) {
+ using enum Modifier::modifier_type_t;
+
// TODO - restrict what modifier effects can be used here
return modifier_manager.expect_modifier_value(
- map_callback(unit_args.terrain_modifiers, terrain_type)
+ map_callback(unit_args.terrain_modifier_values, terrain_type), UNIT_TERRAIN
)(default_value);
}
+
return key_value_invalid_callback(default_key, default_value);
};
@@ -274,7 +288,7 @@ bool UnitTypeManager::load_unit_type_file(
regiment_type_args.allowed_cultures = RegimentType::allowed_cultures_t::ALL_CULTURES;
}
- ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier)(value);
+ ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier_value)(value);
ret &= add_regiment_type(key, unit_args, regiment_type_args);
@@ -301,7 +315,7 @@ bool UnitTypeManager::load_unit_type_file(
"torpedo_attack", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(ship_type_args.torpedo_attack))
);
- ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier)(value);
+ ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier_value)(value);
ret &= add_ship_type(key, unit_args, ship_type_args);
@@ -319,59 +333,72 @@ bool UnitTypeManager::generate_modifiers(ModifierManager& modifier_manager) cons
bool ret = true;
const auto generate_stat_modifiers = [&modifier_manager, &ret](
- std::string_view identifier, UnitType::branch_t branch
+ std::derived_from<ModifierEffectCache::unit_type_effects_t> auto unit_type_effects, std::string_view identifier
) -> void {
+ using enum ModifierEffect::format_t;
+ using enum ModifierEffect::target_t;
+
const auto stat_modifier = [&modifier_manager, &ret, &identifier](
- std::string_view suffix, bool is_positive_good, ModifierEffect::format_t format, std::string_view localisation_key
+ ModifierEffect const*& effect_cache, std::string_view suffix, bool is_positive_good,
+ ModifierEffect::format_t format, std::string_view localisation_key
) -> void {
ret &= modifier_manager.add_modifier_effect(
- ModifierManager::get_flat_identifier(identifier, suffix), is_positive_good, format,
+ effect_cache, ModifierManager::get_flat_identifier(identifier, suffix), is_positive_good, format, COUNTRY,
StringUtils::append_string_views("$", identifier, "$: $", localisation_key, "$")
);
};
- using enum ModifierEffect::format_t;
-
ret &= modifier_manager.register_complex_modifier(identifier);
- stat_modifier("attack", true, RAW_DECIMAL, "ATTACK");
- stat_modifier("defence", true, RAW_DECIMAL, "DEFENCE");
- stat_modifier("default_organisation", true, RAW_DECIMAL, "DEFAULT_ORG");
- stat_modifier("maximum_speed", true, RAW_DECIMAL, "MAXIMUM_SPEED");
- stat_modifier("build_time", false, INT, "BUILD_TIME");
- stat_modifier("supply_consumption", false, PROPORTION_DECIMAL, "SUPPLY_CONSUMPTION");
+ stat_modifier(unit_type_effects.attack, "attack", true, RAW_DECIMAL, "ATTACK");
+ stat_modifier(unit_type_effects.defence, "defence", true, RAW_DECIMAL, "DEFENCE");
+ stat_modifier(unit_type_effects.default_organisation, "default_organisation", true, RAW_DECIMAL, "DEFAULT_ORG");
+ stat_modifier(unit_type_effects.maximum_speed, "maximum_speed", true, RAW_DECIMAL, "MAXIMUM_SPEED");
+ stat_modifier(unit_type_effects.build_time, "build_time", false, INT, "BUILD_TIME");
+ stat_modifier(
+ unit_type_effects.supply_consumption, "supply_consumption", false, PROPORTION_DECIMAL, "SUPPLY_CONSUMPTION"
+ );
- switch (branch) {
- case LAND:
- stat_modifier("reconnaissance", true, RAW_DECIMAL, "RECONAISSANCE");
- stat_modifier("discipline", true, PROPORTION_DECIMAL, "DISCIPLINE");
- stat_modifier("support", true, PROPORTION_DECIMAL, "SUPPORT");
- stat_modifier("maneuver", true, INT, "Maneuver");
- stat_modifier("siege", true, RAW_DECIMAL, "SIEGE");
- break;
- case NAVAL:
- stat_modifier("colonial_points", true, INT, "COLONIAL_POINTS_TECH");
- stat_modifier("supply_consumption_score", false, INT, "SUPPLY_LOAD");
- stat_modifier("hull", true, RAW_DECIMAL, "HULL");
- stat_modifier("gun_power", true, RAW_DECIMAL, "GUN_POWER");
- stat_modifier("fire_range", true, RAW_DECIMAL, "FIRE_RANGE");
- stat_modifier("evasion", true, PROPORTION_DECIMAL, "EVASION");
- stat_modifier("torpedo_attack", true, RAW_DECIMAL, "TORPEDO_ATTACK");
- break;
- default:
+ if constexpr (std::same_as<decltype(unit_type_effects), ModifierEffectCache::regiment_type_effects_t>) {
+ stat_modifier(unit_type_effects.reconnaissance, "reconnaissance", true, RAW_DECIMAL, "RECONAISSANCE");
+ stat_modifier(unit_type_effects.discipline, "discipline", true, PROPORTION_DECIMAL, "DISCIPLINE");
+ stat_modifier(unit_type_effects.support, "support", true, PROPORTION_DECIMAL, "SUPPORT");
+ stat_modifier(unit_type_effects.maneuver, "maneuver", true, INT, "Maneuver");
+ stat_modifier(unit_type_effects.siege, "siege", true, RAW_DECIMAL, "SIEGE");
+ } else if constexpr(std::same_as<decltype(unit_type_effects), ModifierEffectCache::ship_type_effects_t>) {
+ stat_modifier(unit_type_effects.colonial_points, "colonial_points", true, INT, "COLONIAL_POINTS_TECH");
+ stat_modifier(unit_type_effects.supply_consumption_score, "supply_consumption_score", false, INT, "SUPPLY_LOAD");
+ stat_modifier(unit_type_effects.hull, "hull", true, RAW_DECIMAL, "HULL");
+ stat_modifier(unit_type_effects.gun_power, "gun_power", true, RAW_DECIMAL, "GUN_POWER");
+ stat_modifier(unit_type_effects.fire_range, "fire_range", true, RAW_DECIMAL, "FIRE_RANGE");
+ stat_modifier(unit_type_effects.evasion, "evasion", true, PROPORTION_DECIMAL, "EVASION");
+ stat_modifier(unit_type_effects.torpedo_attack, "torpedo_attack", true, RAW_DECIMAL, "TORPEDO_ATTACK");
+ } else {
/* Unreachable - unit types are only added via add_regiment_type or add_ship_type which set branch to LAND or NAVAL. */
- Logger::error("Invalid branch for unit ", identifier, ": ", static_cast<int>(branch));
+ Logger::error("Invalid branch for unit ", identifier, " - not LAND or NAVAL!");
}
};
- generate_stat_modifiers("army_base", LAND);
+ generate_stat_modifiers(modifier_manager.modifier_effect_cache.army_base_effects, "army_base");
+
+ IndexedMap<RegimentType, ModifierEffectCache::regiment_type_effects_t>& regiment_type_effects =
+ modifier_manager.modifier_effect_cache.regiment_type_effects;
+
+ regiment_type_effects.set_keys(&get_regiment_types());
+
for (RegimentType const& regiment_type : get_regiment_types()) {
- generate_stat_modifiers(regiment_type.get_identifier(), LAND);
+ generate_stat_modifiers(regiment_type_effects[regiment_type], regiment_type.get_identifier());
}
- generate_stat_modifiers("navy_base", NAVAL);
+ generate_stat_modifiers(modifier_manager.modifier_effect_cache.navy_base_effects, "navy_base");
+
+ IndexedMap<ShipType, ModifierEffectCache::ship_type_effects_t>& ship_type_effects =
+ modifier_manager.modifier_effect_cache.ship_type_effects;
+
+ ship_type_effects.set_keys(&get_ship_types());
+
for (ShipType const& ship_type : get_ship_types()) {
- generate_stat_modifiers(ship_type.get_identifier(), NAVAL);
+ generate_stat_modifiers(ship_type_effects[ship_type], ship_type.get_identifier());
}
return ret;
diff --git a/src/openvic-simulation/military/UnitType.hpp b/src/openvic-simulation/military/UnitType.hpp
index 3fa9af5..9527041 100644
--- a/src/openvic-simulation/military/UnitType.hpp
+++ b/src/openvic-simulation/military/UnitType.hpp
@@ -20,7 +20,7 @@ namespace OpenVic {
struct UnitType : HasIdentifier {
using icon_t = uint32_t;
- using terrain_modifiers_t = ordered_map<TerrainType const*, ModifierValue>;
+ using terrain_modifiers_t = ordered_map<TerrainType const*, Modifier>;
enum struct branch_t : uint8_t { INVALID_BRANCH, LAND, NAVAL };
enum struct unit_category_t : uint8_t {
@@ -28,6 +28,8 @@ namespace OpenVic {
};
struct unit_type_args_t {
+ using terrain_modifier_values_t = ordered_map<TerrainType const*, ModifierValue>;
+
icon_t icon = 0;
unit_category_t unit_category = unit_category_t::INVALID_UNIT_CATEGORY;
// TODO defaults for move_sound and select_sound
@@ -38,7 +40,7 @@ namespace OpenVic {
supply_consumption = 0;
Timespan build_time;
GoodDefinition::good_definition_map_t build_cost, supply_cost;
- terrain_modifiers_t terrain_modifiers;
+ terrain_modifier_values_t terrain_modifier_values;
unit_type_args_t() = default;
unit_type_args_t(unit_type_args_t&&) = default;
diff --git a/src/openvic-simulation/modifier/Modifier.cpp b/src/openvic-simulation/modifier/Modifier.cpp
index b26b8dd..3cec7df 100644
--- a/src/openvic-simulation/modifier/Modifier.cpp
+++ b/src/openvic-simulation/modifier/Modifier.cpp
@@ -1,131 +1,31 @@
#include "Modifier.hpp"
-#include <string>
-
-#include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp>
-
-#include <dryad/node.hpp>
-
-#include "openvic-simulation/types/OrderedContainers.hpp"
-#include "openvic-simulation/utility/TslHelper.hpp"
-
using namespace OpenVic;
-using namespace OpenVic::NodeTools;
-
-static std::string make_default_modifier_effect_localisation_key(std::string_view identifier) {
- return "MODIFIER_" + StringUtils::string_toupper(identifier);
-}
-
-ModifierEffect::ModifierEffect(
- std::string_view new_identifier, bool new_positive_good, format_t new_format, std::string_view new_localisation_key
-) : HasIdentifier { new_identifier }, positive_good { new_positive_good }, format { new_format },
- localisation_key {
- new_localisation_key.empty() ? make_default_modifier_effect_localisation_key(new_identifier) : new_localisation_key
- } {}
-
-ModifierValue::ModifierValue() = default;
-ModifierValue::ModifierValue(effect_map_t&& new_values) : values { std::move(new_values) } {}
-ModifierValue::ModifierValue(ModifierValue const&) = default;
-ModifierValue::ModifierValue(ModifierValue&&) = default;
-
-ModifierValue& ModifierValue::operator=(ModifierValue const&) = default;
-ModifierValue& ModifierValue::operator=(ModifierValue&&) = default;
-
-void ModifierValue::trim() {
- erase_if(values, [](effect_map_t::value_type const& value) -> bool {
- return value.second == fixed_point_t::_0();
- });
-}
-
-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* effect_found) const {
- const effect_map_t::const_iterator it = values.find(&effect);
- if (it != values.end()) {
- if (effect_found != nullptr) {
- *effect_found = true;
- }
- return it->second;
- }
- if (effect_found != nullptr) {
- *effect_found = false;
- }
- return fixed_point_t::_0();
-}
-
-bool ModifierValue::has_effect(ModifierEffect const& effect) const {
- return values.contains(&effect);
-}
-
-void ModifierValue::set_effect(ModifierEffect const& effect, fixed_point_t value) {
- values[&effect] = value;
-}
-
-ModifierValue& ModifierValue::operator+=(ModifierValue const& right) {
- for (effect_map_t::value_type const& value : right.values) {
- values[value.first] += value.second;
- }
- return *this;
-}
-
-ModifierValue ModifierValue::operator+(ModifierValue const& right) const {
- ModifierValue copy = *this;
- return copy += right;
-}
-
-ModifierValue ModifierValue::operator-() const {
- ModifierValue copy = *this;
- for (auto value : mutable_iterator(copy.values)) {
- value.second = -value.second;
- }
- return copy;
-}
-
-ModifierValue& ModifierValue::operator-=(ModifierValue const& right) {
- for (effect_map_t::value_type const& value : right.values) {
- values[value.first] -= value.second;
- }
- return *this;
-}
-
-ModifierValue ModifierValue::operator-(ModifierValue const& right) const {
- ModifierValue copy = *this;
- return copy -= right;
-}
-
-ModifierValue& ModifierValue::operator*=(fixed_point_t const& right) {
- for (auto value : mutable_iterator(values)) {
- value.second *= right;
- }
- return *this;
-}
-
-ModifierValue ModifierValue::operator*(fixed_point_t const& right) const {
- ModifierValue copy = *this;
- return copy *= right;
-}
-
-fixed_point_t& ModifierValue::operator[](ModifierEffect const& effect) {
- return values[&effect];
-}
-
-void ModifierValue::multiply_add(ModifierValue const& other, fixed_point_t multiplier) {
- if (multiplier == fixed_point_t::_1()) {
- *this += other;
- } else if (multiplier != fixed_point_t::_0()) {
- for (effect_map_t::value_type const& value : other.values) {
- values[value.first] += value.second * multiplier;
- }
+std::string_view Modifier::modifier_type_to_string(modifier_type_t type) {
+ using enum Modifier::modifier_type_t;
+
+ switch (type) {
+#define _CASE(X) case X: return #X;
+ _CASE(EVENT)
+ _CASE(STATIC)
+ _CASE(TRIGGERED)
+ _CASE(CRIME)
+ _CASE(TERRAIN)
+ _CASE(CLIMATE)
+ _CASE(CONTINENT)
+ _CASE(BUILDING)
+ _CASE(LEADER)
+ _CASE(UNIT_TERRAIN)
+ _CASE(NATIONAL_VALUE)
+ _CASE(NATIONAL_FOCUS)
+ _CASE(ISSUE)
+ _CASE(REFORM)
+ _CASE(TECHNOLOGY)
+ _CASE(INVENTION)
+ _CASE(TECH_SCHOOL)
+#undef _CASE
+ default: return "INVALID MODIFIER TYPE";
}
}
@@ -147,510 +47,3 @@ bool TriggeredModifier::parse_scripts(DefinitionManager const& definition_manage
ModifierInstance::ModifierInstance(Modifier const& new_modifier, Date new_expiry_date)
: modifier { &new_modifier }, expiry_date { new_expiry_date } {}
-
-bool ModifierManager::add_modifier_effect(
- std::string_view identifier, bool positive_good, ModifierEffect::format_t format, std::string_view localisation_key
-) {
- if (identifier.empty()) {
- Logger::error("Invalid modifier effect identifier - empty!");
- return false;
- }
- return modifier_effects.add_item({ std::move(identifier), positive_good, format, localisation_key });
-}
-
-bool ModifierManager::setup_modifier_effects() {
- bool ret = true;
-
- using enum ModifierEffect::format_t;
- /* Tech/inventions only */
- ret &= add_modifier_effect("cb_creation_speed", true, PROPORTION_DECIMAL, "CB_MANUFACTURE_TECH");
- ret &= add_modifier_effect("combat_width", false);
- ret &= add_modifier_effect("plurality", true, PERCENTAGE_DECIMAL, "TECH_PLURALITY");
- ret &= add_modifier_effect("pop_growth", true, PROPORTION_DECIMAL, "TECH_POP_GROWTH");
- ret &= add_modifier_effect("regular_experience_level", true, RAW_DECIMAL, "REGULAR_EXP_TECH");
- ret &= add_modifier_effect("reinforce_rate", true, PROPORTION_DECIMAL, "REINFORCE_TECH");
- ret &= add_modifier_effect("seperatism", false, PROPORTION_DECIMAL, "SEPARATISM_TECH"); // paradox typo
- ret &= add_modifier_effect("shared_prestige", true, RAW_DECIMAL, "SHARED_PRESTIGE_TECH");
- ret &= add_modifier_effect("tax_eff", true, PROPORTION_DECIMAL, "TECH_TAX_EFF");
-
- /* Country Modifier Effects */
- ret &= add_modifier_effect("administrative_efficiency", true);
- ret &= add_modifier_effect(
- "administrative_efficiency_modifier", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("administrative_efficiency")
- );
- ret &= add_modifier_effect("artisan_input", false);
- ret &= add_modifier_effect("artisan_output", true);
- ret &= add_modifier_effect("artisan_throughput", true);
- ret &= add_modifier_effect("badboy", false, RAW_DECIMAL);
- ret &= add_modifier_effect("cb_generation_speed_modifier", true);
- ret &= add_modifier_effect(
- "civilization_progress_modifier", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("civilization_progress")
- );
- ret &= add_modifier_effect("colonial_life_rating", false, INT, "COLONIAL_LIFE_TECH");
- ret &= add_modifier_effect("colonial_migration", true, PROPORTION_DECIMAL, "COLONIAL_MIGRATION_TECH");
- ret &= add_modifier_effect("colonial_points", true, INT, "COLONIAL_POINTS_TECH");
- ret &= add_modifier_effect("colonial_prestige", true, PROPORTION_DECIMAL, "COLONIAL_PRESTIGE_MODIFIER_TECH");
- ret &= add_modifier_effect("core_pop_consciousness_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect("core_pop_militancy_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect("dig_in_cap", true, INT, "DIGIN_FROM_TECH");
- ret &= add_modifier_effect("diplomatic_points", true, PROPORTION_DECIMAL, "DIPLOMATIC_POINTS_TECH");
- ret &= add_modifier_effect(
- "diplomatic_points_modifier", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("diplopoints_gain")
- );
- ret &= add_modifier_effect("education_efficiency", true);
- ret &= add_modifier_effect(
- "education_efficiency_modifier", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("education_efficiency")
- );
- ret &= add_modifier_effect("factory_cost", false);
- ret &= add_modifier_effect("factory_input", false);
- ret &= add_modifier_effect("factory_maintenance", false);
- ret &= add_modifier_effect("factory_output", true);
- ret &= add_modifier_effect("factory_owner_cost", false);
- ret &= add_modifier_effect("factory_throughput", true);
- ret &= add_modifier_effect(
- "global_assimilation_rate", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("assimilation_rate")
- );
- ret &= add_modifier_effect(
- "global_immigrant_attract", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("immigant_attract")
- );
- ret &= add_modifier_effect("global_pop_consciousness_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect("global_pop_militancy_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect(
- "global_population_growth", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("population_growth")
- );
- ret &= add_modifier_effect("goods_demand", false);
- ret &= add_modifier_effect("import_cost", false);
- ret &= add_modifier_effect("increase_research", true, PROPORTION_DECIMAL, "INC_RES_TECH");
- ret &= add_modifier_effect("influence", true, PROPORTION_DECIMAL, "TECH_GP_INFLUENCE");
- ret &= add_modifier_effect(
- "influence_modifier", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("greatpower_influence_gain")
- );
- ret &= add_modifier_effect("issue_change_speed", true);
- ret &= add_modifier_effect(
- "land_attack_modifier", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("land_attack")
- );
- ret &= add_modifier_effect("land_attrition", false, PROPORTION_DECIMAL, "LAND_ATTRITION_TECH");
- ret &= add_modifier_effect(
- "land_defense_modifier", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("land_defense")
- );
- ret &= add_modifier_effect("land_organisation", true);
- ret &= add_modifier_effect("land_unit_start_experience", true, RAW_DECIMAL);
- ret &= add_modifier_effect("leadership", true, RAW_DECIMAL, "LEADERSHIP");
- ret &= add_modifier_effect(
- "leadership_modifier", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("global_leadership_modifier")
- );
- ret &= add_modifier_effect("literacy_con_impact", false);
- ret &= add_modifier_effect("loan_interest", false);
- ret &= add_modifier_effect(
- "max_loan_modifier", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("max_loan_amount")
- );
- ret &= add_modifier_effect("max_military_spending", true);
- ret &= add_modifier_effect("max_national_focus", true, INT, "TECH_MAX_FOCUS");
- ret &= add_modifier_effect("max_social_spending", true);
- ret &= add_modifier_effect("max_tariff", true);
- ret &= add_modifier_effect("max_tax", true);
- ret &= add_modifier_effect("max_war_exhaustion", true, PERCENTAGE_DECIMAL, "MAX_WAR_EXHAUSTION");
- ret &= add_modifier_effect("military_tactics", true, PROPORTION_DECIMAL, "MIL_TACTICS_TECH");
- ret &= add_modifier_effect("min_military_spending", true);
- ret &= add_modifier_effect("min_social_spending", true);
- ret &= add_modifier_effect("min_tariff", true);
- ret &= add_modifier_effect("min_tax", true);
- ret &= add_modifier_effect(
- "minimum_wage", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("minimun_wage")
- );
- ret &= add_modifier_effect("mobilisation_economy_impact", false);
- ret &= add_modifier_effect("mobilisation_size", true);
- ret &= add_modifier_effect("mobilization_impact", false);
- ret &= add_modifier_effect(
- "naval_attack_modifier", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("naval_attack")
- );
- ret &= add_modifier_effect("naval_attrition", false, PROPORTION_DECIMAL, "NAVAL_ATTRITION_TECH");
- ret &= add_modifier_effect(
- "naval_defense_modifier", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("naval_defense")
- );
- ret &= add_modifier_effect("naval_organisation", true);
- ret &= add_modifier_effect("naval_unit_start_experience", true, RAW_DECIMAL);
- ret &= add_modifier_effect("non_accepted_pop_consciousness_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect("non_accepted_pop_militancy_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect("org_regain", true);
- ret &= add_modifier_effect("pension_level", true);
- ret &= add_modifier_effect("permanent_prestige", true, RAW_DECIMAL, "PERMANENT_PRESTIGE_TECH");
- ret &= add_modifier_effect("political_reform_desire", false);
- ret &= add_modifier_effect("poor_savings_modifier", true);
- ret &= add_modifier_effect("prestige", true, RAW_DECIMAL);
- ret &= add_modifier_effect("reinforce_speed", true);
- ret &= add_modifier_effect("research_points", true, RAW_DECIMAL);
- ret &= add_modifier_effect("research_points_modifier", true);
- ret &= add_modifier_effect("research_points_on_conquer", true);
- ret &= add_modifier_effect("rgo_output", true);
- ret &= add_modifier_effect("rgo_throughput", true);
- ret &= add_modifier_effect("ruling_party_support", true);
- ret &= add_modifier_effect(
- "self_unciv_economic_modifier", false, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("self_unciv_economic")
- );
- ret &= add_modifier_effect(
- "self_unciv_military_modifier", false, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("self_unciv_military")
- );
- ret &= add_modifier_effect("social_reform_desire", false);
- ret &= add_modifier_effect("soldier_to_pop_loss", true, PROPORTION_DECIMAL, "SOLDIER_TO_POP_LOSS_TECH");
- ret &= add_modifier_effect("supply_consumption", false);
- ret &= add_modifier_effect("supply_range", true, PROPORTION_DECIMAL, "SUPPLY_RANGE_TECH");
- ret &= add_modifier_effect("suppression_points_modifier", true, PROPORTION_DECIMAL, "SUPPRESSION_TECH");
- ret &= add_modifier_effect(
- "tariff_efficiency_modifier", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("tariff_efficiency")
- );
- ret &= add_modifier_effect("tax_efficiency", true);
- ret &= add_modifier_effect("unemployment_benefit", true);
- ret &= add_modifier_effect(
- "unciv_economic_modifier", false, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("unciv_economic")
- );
- ret &= add_modifier_effect(
- "unciv_military_modifier", false, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("unciv_military")
- );
- ret &= add_modifier_effect("unit_recruitment_time", false);
- ret &= add_modifier_effect("war_exhaustion", false, PROPORTION_DECIMAL, "WAR_EXHAUST_BATTLES");
-
- /* Province Modifier Effects */
- ret &= add_modifier_effect("assimilation_rate", true);
- ret &= add_modifier_effect("boost_strongest_party", false);
- ret &= add_modifier_effect("farm_rgo_eff", true, PROPORTION_DECIMAL, "TECH_FARM_OUTPUT");
- ret &= add_modifier_effect(
- "farm_rgo_size", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("farm_size")
- );
- ret &= add_modifier_effect(
- "immigrant_attract", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("immigant_attract")
- );
- ret &= add_modifier_effect(
- "immigrant_push", false, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("immigant_push")
- );
- ret &= add_modifier_effect("life_rating", true);
- ret &= add_modifier_effect(
- "local_artisan_input", false, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("artisan_input")
- );
- ret &= add_modifier_effect(
- "local_artisan_output", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("artisan_output")
- );
- ret &= add_modifier_effect(
- "local_artisan_throughput", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("artisan_throughput")
- );
- ret &= add_modifier_effect(
- "local_factory_input", false, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("factory_input")
- );
- ret &= add_modifier_effect(
- "local_factory_output", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("factory_output")
- );
- ret &= add_modifier_effect(
- "local_factory_throughput", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("factory_throughput")
- );
- ret &= add_modifier_effect("local_repair", true);
- ret &= add_modifier_effect(
- "local_rgo_output", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("rgo_output")
- );
- ret &= add_modifier_effect(
- "local_rgo_throughput", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("rgo_throughput")
- );
- ret &= add_modifier_effect(
- "local_ruling_party_support", true, PROPORTION_DECIMAL,
- make_default_modifier_effect_localisation_key("ruling_party_support")
- );
- ret &= add_modifier_effect("local_ship_build", false);
- ret &= add_modifier_effect("max_attrition", false, RAW_DECIMAL);
- ret &= add_modifier_effect("mine_rgo_eff", true, PROPORTION_DECIMAL, "TECH_MINE_OUTPUT");
- ret &= add_modifier_effect(
- "mine_rgo_size", true, PROPORTION_DECIMAL, make_default_modifier_effect_localisation_key("mine_size")
- );
- ret &= add_modifier_effect("movement_cost", false);
- ret &= add_modifier_effect("number_of_voters", false);
- ret &= add_modifier_effect("pop_consciousness_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect("pop_militancy_modifier", false, RAW_DECIMAL);
- ret &= add_modifier_effect("population_growth", true);
- ret &= add_modifier_effect("supply_limit", true, RAW_DECIMAL);
-
- /* Military Modifier Effects */
- ret &= add_modifier_effect("attack", true, INT, "TRAIT_ATTACK");
- ret &= add_modifier_effect("attrition", false, RAW_DECIMAL, "ATTRITION");
- ret &= add_modifier_effect("defence", true, INT, "TRAIT_DEFEND");
- ret &= add_modifier_effect("experience", true, PROPORTION_DECIMAL, "TRAIT_EXPERIENCE");
- ret &= add_modifier_effect("morale", true, PROPORTION_DECIMAL, "TRAIT_MORALE");
- ret &= add_modifier_effect("organisation", true, PROPORTION_DECIMAL, "TRAIT_ORGANISATION");
- ret &= add_modifier_effect("reconnaissance", true, PROPORTION_DECIMAL, "TRAIT_RECONAISSANCE");
- ret &= add_modifier_effect("reliability", true, RAW_DECIMAL, "TRAIT_RELIABILITY");
- ret &= add_modifier_effect("speed", true, PROPORTION_DECIMAL, "TRAIT_SPEED");
-
- return ret;
-}
-
-bool ModifierManager::register_complex_modifier(std::string_view identifier) {
- if (complex_modifiers.emplace(identifier).second) {
- return true;
- } else {
- Logger::error("Duplicate complex modifier: ", identifier);
- return false;
- }
-}
-
-std::string ModifierManager::get_flat_identifier(
- std::string_view complex_modifier_identifier, std::string_view variant_identifier
-) {
- return StringUtils::append_string_views(complex_modifier_identifier, " ", variant_identifier);
-}
-
-bool ModifierManager::add_event_modifier(std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon) {
- if (identifier.empty()) {
- Logger::error("Invalid event modifier effect identifier - empty!");
- return false;
- }
-
- return event_modifiers.add_item(
- { identifier, std::move(values), Modifier::modifier_type_t::EVENT, icon }, duplicate_warning_callback
- );
-}
-
-bool ModifierManager::load_event_modifiers(ast::NodeCPtr root) {
- const bool ret = expect_dictionary_reserve_length(
- event_modifiers,
- [this](std::string_view key, ast::NodeCPtr value) -> bool {
- ModifierValue modifier_value;
- IconModifier::icon_t icon = 0;
- bool ret = expect_modifier_value_and_keys(
- move_variable_callback(modifier_value),
- "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon))
- )(value);
- ret &= add_event_modifier(key, std::move(modifier_value), icon);
- return ret;
- }
- )(root);
-
- lock_event_modifiers();
-
- return ret;
-}
-
-bool ModifierManager::add_static_modifier(std::string_view identifier, ModifierValue&& values) {
- if (identifier.empty()) {
- Logger::error("Invalid static modifier effect identifier - empty!");
- return false;
- }
-
- return static_modifiers.add_item(
- { identifier, std::move(values), Modifier::modifier_type_t::STATIC }, duplicate_warning_callback
- );
-}
-
-bool ModifierManager::load_static_modifiers(ast::NodeCPtr root) {
- const bool ret = expect_dictionary_reserve_length(
- static_modifiers,
- [this](std::string_view key, ast::NodeCPtr value) -> bool {
- ModifierValue modifier_value;
- bool ret = expect_modifier_value(move_variable_callback(modifier_value))(value);
- ret &= add_static_modifier(key, std::move(modifier_value));
- return ret;
- }
- )(root);
-
- lock_static_modifiers();
-
- return ret;
-}
-
-bool ModifierManager::add_triggered_modifier(
- std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger
-) {
- if (identifier.empty()) {
- Logger::error("Invalid triggered modifier effect identifier - empty!");
- return false;
- }
-
- return triggered_modifiers.add_item(
- { identifier, std::move(values), Modifier::modifier_type_t::TRIGGERED, icon, std::move(trigger) },
- duplicate_warning_callback
- );
-}
-
-bool ModifierManager::load_triggered_modifiers(ast::NodeCPtr root) {
- const bool ret = expect_dictionary_reserve_length(
- triggered_modifiers,
- [this](std::string_view key, ast::NodeCPtr value) -> bool {
- ModifierValue modifier_value;
- IconModifier::icon_t icon = 0;
- ConditionScript trigger { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE };
-
- bool ret = expect_modifier_value_and_keys(
- move_variable_callback(modifier_value),
- "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)),
- "trigger", ONE_EXACTLY, trigger.expect_script()
- )(value);
- ret &= add_triggered_modifier(key, std::move(modifier_value), icon, std::move(trigger));
- return ret;
- }
- )(root);
-
- lock_triggered_modifiers();
-
- return ret;
-}
-
-bool ModifierManager::parse_scripts(DefinitionManager const& definition_manager) {
- bool ret = true;
-
- for (TriggeredModifier& modifier : triggered_modifiers.get_items()) {
- ret &= modifier.parse_scripts(definition_manager);
- }
-
- return ret;
-}
-
-key_value_callback_t ModifierManager::_modifier_effect_callback(
- ModifierValue& modifier, key_value_callback_t default_callback, ModifierEffectValidator auto effect_validator
-) const {
- const auto add_modifier_cb = [this, &modifier, effect_validator](
- ModifierEffect const* effect, ast::NodeCPtr value
- ) -> bool {
- if (effect_validator(*effect)) {
- static const case_insensitive_string_set_t no_effect_modifiers {
- "boost_strongest_party", "poor_savings_modifier", "local_artisan_input", "local_artisan_throughput",
- "local_artisan_output", "artisan_input", "artisan_throughput", "artisan_output",
- "import_cost", "unciv_economic_modifier", "unciv_military_modifier"
- };
- if (no_effect_modifiers.contains(effect->get_identifier())) {
- Logger::warning("This modifier does nothing: ", effect->get_identifier());
- }
- return expect_fixed_point(map_callback(modifier.values, effect))(value);
- } else {
- Logger::error("Failed to validate modifier effect: ", effect->get_identifier());
- return false;
- }
- };
-
- const auto add_flattened_modifier_cb = [this, add_modifier_cb](
- std::string_view prefix, std::string_view key, ast::NodeCPtr value
- ) -> bool {
- const std::string flat_identifier = get_flat_identifier(prefix, key);
- ModifierEffect const* effect = get_modifier_effect_by_identifier(flat_identifier);
- if (effect != nullptr) {
- return add_modifier_cb(effect, value);
- } else {
- Logger::error("Could not find flattened modifier: ", flat_identifier);
- return false;
- }
- };
-
- return [this, default_callback, add_modifier_cb, add_flattened_modifier_cb](
- std::string_view key, ast::NodeCPtr value
- ) -> bool {
- ModifierEffect const* effect = get_modifier_effect_by_identifier(key);
- if (effect != nullptr && dryad::node_has_kind<ast::IdentifierValue>(value)) {
- return add_modifier_cb(effect, value);
- } else if (complex_modifiers.contains(key) && dryad::node_has_kind<ast::ListValue>(value)) {
- if (key == "rebel_org_gain") { //because of course there's a special one
- std::string_view faction_identifier;
- ast::NodeCPtr value_node = nullptr;
- bool ret = expect_dictionary_keys(
- "faction", ONE_EXACTLY, expect_identifier(assign_variable_callback(faction_identifier)),
- "value", ONE_EXACTLY, assign_variable_callback(value_node)
- )(value);
- ret &= add_flattened_modifier_cb(key, faction_identifier, value_node);
- return ret;
- } else {
- return expect_dictionary(std::bind_front(add_flattened_modifier_cb, key))(value);
- }
- } else if (key == "war_exhaustion_effect") {
- Logger::warning("war_exhaustion_effect does nothing (vanilla issues have it).");
- return true;
- } else {
- return default_callback(key, value);
- }
- };
-}
-
-node_callback_t ModifierManager::expect_validated_modifier_value_and_default(
- callback_t<ModifierValue&&> modifier_callback, key_value_callback_t default_callback,
- ModifierEffectValidator auto effect_validator
-) const {
- return [this, modifier_callback, default_callback, effect_validator](ast::NodeCPtr root) -> bool {
- ModifierValue modifier;
- bool ret = expect_dictionary_reserve_length(
- modifier.values,
- _modifier_effect_callback(modifier, default_callback, effect_validator)
- )(root);
- ret &= modifier_callback(std::move(modifier));
- return ret;
- };
-}
-
-node_callback_t ModifierManager::expect_validated_modifier_value(
- callback_t<ModifierValue&&> modifier_callback, ModifierEffectValidator auto effect_validator
-) const {
- return expect_validated_modifier_value_and_default(modifier_callback, key_value_invalid_callback, effect_validator);
-}
-
-node_callback_t ModifierManager::expect_modifier_value_and_default(
- callback_t<ModifierValue&&> modifier_callback, key_value_callback_t default_callback
-) const {
- return expect_validated_modifier_value_and_default(modifier_callback, default_callback, [](ModifierEffect const&) -> bool {
- return true;
- });
-}
-
-node_callback_t ModifierManager::expect_modifier_value(callback_t<ModifierValue&&> modifier_callback) const {
- return expect_modifier_value_and_default(modifier_callback, key_value_invalid_callback);
-}
-
-node_callback_t ModifierManager::expect_whitelisted_modifier_value_and_default(
- callback_t<ModifierValue&&> modifier_callback, string_set_t const& whitelist, key_value_callback_t default_callback
-) const {
- return expect_validated_modifier_value_and_default(
- modifier_callback, default_callback,
- [&whitelist](ModifierEffect const& effect) -> bool {
- return whitelist.contains(effect.get_identifier());
- }
- );
-}
-
-node_callback_t ModifierManager::expect_whitelisted_modifier_value(
- callback_t<ModifierValue&&> modifier_callback, string_set_t const& whitelist
-) const {
- return expect_whitelisted_modifier_value_and_default(modifier_callback, whitelist, key_value_invalid_callback);
-}
-
-node_callback_t ModifierManager::expect_modifier_value_and_key_map_and_default(
- callback_t<ModifierValue&&> modifier_callback, key_value_callback_t default_callback, key_map_t&& key_map
-) const {
- return [this, modifier_callback, default_callback, key_map = std::move(key_map)](ast::NodeCPtr node) mutable -> bool {
- bool ret = expect_modifier_value_and_default(
- modifier_callback,
- dictionary_keys_callback(key_map, default_callback)
- )(node);
- ret &= check_key_map_counts(key_map);
- return ret;
- };
-}
-
-node_callback_t ModifierManager::expect_modifier_value_and_key_map(
- callback_t<ModifierValue&&> modifier_callback, key_map_t&& key_map
-) const {
- return expect_modifier_value_and_key_map_and_default(modifier_callback, key_value_invalid_callback, std::move(key_map));
-}
-
-namespace OpenVic { // so the compiler shuts up
- std::ostream& operator<<(std::ostream& stream, ModifierValue const& value) {
- for (ModifierValue::effect_map_t::value_type const& effect : value.values) {
- stream << effect.first << ": " << effect.second << "\n";
- }
- return stream;
- }
-}
diff --git a/src/openvic-simulation/modifier/Modifier.hpp b/src/openvic-simulation/modifier/Modifier.hpp
index 80d2db6..1277c76 100644
--- a/src/openvic-simulation/modifier/Modifier.hpp
+++ b/src/openvic-simulation/modifier/Modifier.hpp
@@ -1,89 +1,26 @@
#pragma once
+#include <string_view>
+
+#include "openvic-simulation/modifier/ModifierValue.hpp"
#include "openvic-simulation/scripts/ConditionScript.hpp"
-#include "openvic-simulation/types/IdentifierRegistry.hpp"
+#include "openvic-simulation/types/Date.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
namespace OpenVic {
- struct ModifierManager;
-
- struct ModifierEffect : HasIdentifier {
- friend struct ModifierManager;
-
- enum class format_t {
- PROPORTION_DECIMAL, /* An unscaled fraction/ratio, with 1 being "full"/"whole" */
- PERCENTAGE_DECIMAL, /* A fraction/ratio scaled so that 100 is "full"/"whole" */
- RAW_DECIMAL, /* A continuous quantity, e.g. attack strength */
- INT /* A discrete quantity, e.g. building count limit */
- };
-
- private:
- /* If true, positive values will be green and negative values will be red.
- * If false, the colours will be switced.
- */
- const bool PROPERTY_CUSTOM_PREFIX(positive_good, is);
- const format_t PROPERTY(format);
- std::string PROPERTY(localisation_key);
-
- // TODO - format/precision, e.g. 80% vs 0.8 vs 0.800, 2 vs 2.0 vs 200%
-
- ModifierEffect(
- std::string_view new_identifier, bool new_positive_good, format_t new_format, std::string_view new_localisation_key
- );
-
- public:
- ModifierEffect(ModifierEffect&&) = default;
- };
-
- struct ModifierValue {
- friend struct ModifierManager;
-
- using effect_map_t = fixed_point_map_t<ModifierEffect const*>;
-
- private:
- effect_map_t PROPERTY(values);
-
- public:
- ModifierValue();
- ModifierValue(effect_map_t&& new_values);
- ModifierValue(ModifierValue const&);
- ModifierValue(ModifierValue&&);
-
- ModifierValue& operator=(ModifierValue const&);
- ModifierValue& operator=(ModifierValue&&);
-
- /* 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* effect_found = nullptr) const;
- bool has_effect(ModifierEffect const& effect) const;
- void set_effect(ModifierEffect const& effect, fixed_point_t value);
-
- ModifierValue& operator+=(ModifierValue const& right);
- ModifierValue operator+(ModifierValue const& right) const;
- ModifierValue operator-() const;
- ModifierValue& operator-=(ModifierValue const& right);
- ModifierValue operator-(ModifierValue const& right) const;
- ModifierValue& operator*=(fixed_point_t const& right);
- ModifierValue operator*(fixed_point_t const& right) const;
-
- fixed_point_t& operator[](ModifierEffect const& effect);
-
- void multiply_add(ModifierValue const& other, fixed_point_t multiplier);
-
- friend std::ostream& operator<<(std::ostream& stream, ModifierValue const& value);
- };
+ struct UnitType;
struct Modifier : HasIdentifier, ModifierValue {
friend struct ModifierManager;
+ friend struct UnitType;
enum struct modifier_type_t : uint8_t {
- EVENT, STATIC, TRIGGERED, CRIME, TERRAIN, CLIMATE, CONTINENT, BUILDING, LEADER, NATIONAL_VALUE, NATIONAL_FOCUS,
- ISSUE, REFORM, TECHNOLOGY, INVENTION, TECH_SCHOOL
+ EVENT, STATIC, TRIGGERED, CRIME, TERRAIN, CLIMATE, CONTINENT, BUILDING, LEADER, UNIT_TERRAIN,
+ NATIONAL_VALUE, NATIONAL_FOCUS, ISSUE, REFORM, TECHNOLOGY, INVENTION, TECH_SCHOOL
};
+ static std::string_view modifier_type_to_string(modifier_type_t type);
+
private:
const modifier_type_t PROPERTY(type);
@@ -137,108 +74,4 @@ namespace OpenVic {
public:
ModifierInstance(Modifier const& new_modifier, Date new_expiry_date);
};
-
- template<typename Fn>
- concept ModifierEffectValidator = std::predicate<Fn, ModifierEffect const&>;
-
- struct ModifierManager {
- /* Some ModifierEffects are generated mid-load, such as max/min count modifiers for each building, so
- * we can't lock it until loading is over. This means we can't rely on locking for pointer stability,
- * so instead we store the effects in a deque which doesn't invalidate pointers on insert.
- */
- private:
- CaseInsensitiveIdentifierRegistry<ModifierEffect, RegistryStorageInfoDeque> IDENTIFIER_REGISTRY(modifier_effect);
- case_insensitive_string_set_t complex_modifiers;
-
- IdentifierRegistry<IconModifier> IDENTIFIER_REGISTRY(event_modifier);
- IdentifierRegistry<Modifier> IDENTIFIER_REGISTRY(static_modifier);
- IdentifierRegistry<TriggeredModifier> IDENTIFIER_REGISTRY(triggered_modifier);
-
- /* effect_validator takes in ModifierEffect const& */
- NodeTools::key_value_callback_t _modifier_effect_callback(
- ModifierValue& modifier, NodeTools::key_value_callback_t default_callback,
- ModifierEffectValidator auto effect_validator
- ) const;
-
- public:
- bool add_modifier_effect(
- std::string_view identifier, bool positive_good,
- ModifierEffect::format_t format = ModifierEffect::format_t::PROPORTION_DECIMAL,
- std::string_view localisation_key = {}
- );
-
- bool register_complex_modifier(std::string_view identifier);
- static std::string get_flat_identifier(
- std::string_view complex_modifier_identifier, std::string_view variant_identifier
- );
-
- bool setup_modifier_effects();
-
- bool add_event_modifier(std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon);
- bool load_event_modifiers(ast::NodeCPtr root);
-
- bool add_static_modifier(std::string_view identifier, ModifierValue&& values);
- bool load_static_modifiers(ast::NodeCPtr root);
-
- bool add_triggered_modifier(
- std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger
- );
- bool load_triggered_modifiers(ast::NodeCPtr root);
-
- bool parse_scripts(DefinitionManager const& definition_manager);
-
- NodeTools::node_callback_t expect_validated_modifier_value_and_default(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback,
- ModifierEffectValidator auto effect_validator
- ) const;
- NodeTools::node_callback_t expect_validated_modifier_value(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, ModifierEffectValidator auto effect_validator
- ) const;
-
- NodeTools::node_callback_t expect_modifier_value_and_default(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback
- ) const;
- NodeTools::node_callback_t expect_modifier_value(NodeTools::callback_t<ModifierValue&&> modifier_callback) const;
-
- NodeTools::node_callback_t expect_whitelisted_modifier_value_and_default(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, string_set_t const& whitelist,
- NodeTools::key_value_callback_t default_callback
- ) const;
- NodeTools::node_callback_t expect_whitelisted_modifier_value(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, string_set_t const& whitelist
- ) const;
-
- NodeTools::node_callback_t expect_modifier_value_and_key_map_and_default(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback,
- NodeTools::key_map_t&& key_map
- ) const;
- NodeTools::node_callback_t expect_modifier_value_and_key_map(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_map_t&& key_map
- ) const;
-
- template<typename... Args>
- NodeTools::node_callback_t expect_modifier_value_and_key_map_and_default(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback,
- NodeTools::key_map_t&& key_map, Args... args
- ) const {
- NodeTools::add_key_map_entries(key_map, args...);
- return expect_modifier_value_and_key_map_and_default(modifier_callback, default_callback, std::move(key_map));
- }
-
- template<typename... Args>
- NodeTools::node_callback_t expect_modifier_value_and_keys_and_default(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback,
- Args... args
- ) const {
- return expect_modifier_value_and_key_map_and_default(modifier_callback, default_callback, {}, args...);
- }
- template<typename... Args>
- NodeTools::node_callback_t expect_modifier_value_and_keys(
- NodeTools::callback_t<ModifierValue&&> modifier_callback, Args... args
- ) const {
- return expect_modifier_value_and_key_map_and_default(
- modifier_callback, NodeTools::key_value_invalid_callback, {}, args...
- );
- }
- };
}
diff --git a/src/openvic-simulation/modifier/ModifierEffect.cpp b/src/openvic-simulation/modifier/ModifierEffect.cpp
new file mode 100644
index 0000000..2ffb9a5
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierEffect.cpp
@@ -0,0 +1,49 @@
+#include "ModifierEffect.hpp"
+
+#include "openvic-simulation/utility/StringUtils.hpp"
+
+using namespace OpenVic;
+
+std::string ModifierEffect::target_to_string(target_t target) {
+ using enum target_t;
+
+ if (target == NO_TARGETS) {
+ return "NO TARGETS";
+ }
+
+ if (target == ALL_TARGETS) {
+ return "ALL TARGETS";
+ }
+
+ static constexpr std::string_view SEPARATOR = " | ";
+
+ std::string ret;
+
+ const auto append_target = [target, &ret](target_t check_target, std::string_view target_str) -> void {
+ if (!ModifierEffect::excludes_targets(target, check_target)) {
+ if (!ret.empty()) {
+ ret += SEPARATOR;
+ }
+ ret += target_str;
+ }
+ };
+
+ append_target(COUNTRY, "COUNTRY");
+ append_target(PROVINCE, "PROVINCE");
+ append_target(UNIT, "UNIT");
+ append_target(~ALL_TARGETS, "INVALID TARGET");
+
+ return ret;
+}
+
+std::string ModifierEffect::make_default_modifier_effect_localisation_key(std::string_view identifier) {
+ return "MODIFIER_" + StringUtils::string_toupper(identifier);
+}
+
+ModifierEffect::ModifierEffect(
+ std::string_view new_identifier, bool new_positive_good, format_t new_format, target_t new_targets,
+ std::string_view new_localisation_key
+) : HasIdentifier { new_identifier }, positive_good { new_positive_good }, format { new_format }, targets { new_targets },
+ localisation_key {
+ new_localisation_key.empty() ? make_default_modifier_effect_localisation_key(new_identifier) : new_localisation_key
+ } {}
diff --git a/src/openvic-simulation/modifier/ModifierEffect.hpp b/src/openvic-simulation/modifier/ModifierEffect.hpp
new file mode 100644
index 0000000..ff82acf
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierEffect.hpp
@@ -0,0 +1,63 @@
+#pragma once
+
+#include <string>
+#include <string_view>
+
+#include "openvic-simulation/types/EnumBitfield.hpp"
+#include "openvic-simulation/types/HasIdentifier.hpp"
+
+namespace OpenVic {
+ struct ModifierManager;
+
+ struct ModifierEffect : HasIdentifier {
+ friend struct ModifierManager;
+
+ enum class format_t : uint8_t {
+ PROPORTION_DECIMAL, /* An unscaled fraction/ratio, with 1 being "full"/"whole" */
+ PERCENTAGE_DECIMAL, /* A fraction/ratio scaled so that 100 is "full"/"whole" */
+ RAW_DECIMAL, /* A continuous quantity, e.g. attack strength */
+ INT /* A discrete quantity, e.g. building count limit */
+ };
+
+ enum class target_t : uint8_t {
+ NO_TARGETS = 0,
+ COUNTRY = 1 << 0,
+ PROVINCE = 1 << 1,
+ UNIT = 1 << 2,
+ ALL_TARGETS = (1 << 3) - 1
+ };
+
+ static constexpr bool excludes_targets(target_t targets, target_t excluded_target);
+
+ static std::string target_to_string(target_t target);
+
+ static std::string make_default_modifier_effect_localisation_key(std::string_view identifier);
+
+ private:
+ /* If true, positive values will be green and negative values will be red.
+ * If false, the colours will be switced.
+ */
+ const bool PROPERTY_CUSTOM_PREFIX(positive_good, is);
+ const format_t PROPERTY(format);
+ const target_t PROPERTY(targets);
+ std::string PROPERTY(localisation_key);
+
+ // TODO - format/precision, e.g. 80% vs 0.8 vs 0.800, 2 vs 2.0 vs 200%
+
+ ModifierEffect(
+ std::string_view new_identifier, bool new_positive_good, format_t new_format, target_t mew_targets,
+ std::string_view new_localisation_key
+ );
+
+ public:
+ ModifierEffect(ModifierEffect&&) = default;
+ };
+
+ template<> struct enable_bitfield<ModifierEffect::target_t> : std::true_type {};
+
+ constexpr bool ModifierEffect::excludes_targets(target_t targets, target_t excluded_targets) {
+ using enum target_t;
+
+ return (targets & excluded_targets) == NO_TARGETS;
+ }
+}
diff --git a/src/openvic-simulation/modifier/ModifierEffectCache.cpp b/src/openvic-simulation/modifier/ModifierEffectCache.cpp
new file mode 100644
index 0000000..9653d23
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierEffectCache.cpp
@@ -0,0 +1,231 @@
+#include "ModifierEffectCache.hpp"
+
+#include "openvic-simulation/economy/BuildingType.hpp"
+#include "openvic-simulation/economy/GoodDefinition.hpp"
+#include "openvic-simulation/politics/Rebel.hpp"
+#include "openvic-simulation/research/Technology.hpp"
+
+using namespace OpenVic;
+
+ModifierEffectCache::building_type_effects_t::building_type_effects_t()
+ : min_level { nullptr },
+ max_level { nullptr } {}
+
+ModifierEffectCache::good_effects_t::good_effects_t()
+ : artisan_goods_input { nullptr },
+ artisan_goods_output { nullptr },
+ artisan_goods_throughput { nullptr },
+ factory_goods_input { nullptr },
+ factory_goods_output { nullptr },
+ factory_goods_throughput { nullptr },
+ rgo_goods_output { nullptr },
+ rgo_goods_throughput { nullptr },
+ rgo_size { nullptr } {}
+
+ModifierEffectCache::unit_type_effects_t::unit_type_effects_t()
+ : attack { nullptr },
+ defence { nullptr },
+ default_organisation { nullptr },
+ maximum_speed { nullptr },
+ build_time { nullptr },
+ supply_consumption { nullptr } {}
+
+ModifierEffectCache::regiment_type_effects_t::regiment_type_effects_t()
+ : unit_type_effects_t {},
+ reconnaissance { nullptr },
+ discipline { nullptr },
+ support { nullptr },
+ maneuver { nullptr },
+ siege { nullptr } {}
+
+ModifierEffectCache::ship_type_effects_t::ship_type_effects_t()
+ : unit_type_effects_t {},
+ colonial_points { nullptr },
+ supply_consumption_score { nullptr },
+ hull { nullptr },
+ gun_power { nullptr },
+ fire_range { nullptr },
+ evasion { nullptr },
+ torpedo_attack { nullptr } {}
+
+ModifierEffectCache::strata_effects_t::strata_effects_t()
+ : income_modifier { nullptr },
+ vote { nullptr },
+ life_needs { nullptr },
+ everyday_needs { nullptr },
+ luxury_needs { nullptr } {}
+
+ModifierEffectCache::ModifierEffectCache()
+ : /* Tech/inventions only */
+ cb_creation_speed { nullptr },
+ combat_width_additive { nullptr },
+ plurality { nullptr },
+ pop_growth { nullptr },
+ prestige_gain_multiplier { nullptr },
+ regular_experience_level { nullptr },
+ reinforce_rate { nullptr },
+ separatism { nullptr },
+ shared_prestige { nullptr },
+ tax_eff { nullptr },
+
+ /* Country Modifier Effects */
+ administrative_efficiency { nullptr },
+ administrative_efficiency_modifier { nullptr },
+ artisan_input { nullptr },
+ artisan_output { nullptr },
+ artisan_throughput { nullptr },
+ badboy { nullptr },
+ cb_generation_speed_modifier { nullptr },
+ civilization_progress_modifier { nullptr },
+ colonial_life_rating { nullptr },
+ colonial_migration { nullptr },
+ colonial_points { nullptr },
+ colonial_prestige { nullptr },
+ core_pop_consciousness_modifier { nullptr },
+ core_pop_militancy_modifier { nullptr },
+ dig_in_cap { nullptr },
+ diplomatic_points { nullptr },
+ diplomatic_points_modifier { nullptr },
+ education_efficiency { nullptr },
+ education_efficiency_modifier { nullptr },
+ factory_cost { nullptr },
+ factory_input { nullptr },
+ factory_maintenance { nullptr },
+ factory_output { nullptr },
+ factory_owner_cost { nullptr },
+ factory_throughput { nullptr },
+ global_assimilation_rate { nullptr },
+ global_immigrant_attract { nullptr },
+ global_pop_consciousness_modifier { nullptr },
+ global_pop_militancy_modifier { nullptr },
+ global_population_growth { nullptr },
+ goods_demand { nullptr },
+ import_cost { nullptr },
+ increase_research { nullptr },
+ influence { nullptr },
+ influence_modifier { nullptr },
+ issue_change_speed { nullptr },
+ land_attack_modifier { nullptr },
+ land_attrition { nullptr },
+ land_defense_modifier { nullptr },
+ land_organisation { nullptr },
+ land_unit_start_experience { nullptr },
+ leadership { nullptr },
+ leadership_modifier { nullptr },
+ literacy_con_impact { nullptr },
+ loan_interest { nullptr },
+ max_loan_modifier { nullptr },
+ max_military_spending { nullptr },
+ max_national_focus { nullptr },
+ max_social_spending { nullptr },
+ max_tariff { nullptr },
+ max_tax { nullptr },
+ max_war_exhaustion { nullptr },
+ military_tactics { nullptr },
+ min_military_spending { nullptr },
+ min_social_spending { nullptr },
+ min_tariff { nullptr },
+ min_tax { nullptr },
+ minimum_wage { nullptr },
+ mobilisation_economy_impact { nullptr },
+ mobilisation_size { nullptr },
+ mobilization_impact { nullptr },
+ naval_attack_modifier { nullptr },
+ naval_attrition { nullptr },
+ naval_defense_modifier { nullptr },
+ naval_organisation { nullptr },
+ naval_unit_start_experience { nullptr },
+ non_accepted_pop_consciousness_modifier { nullptr },
+ non_accepted_pop_militancy_modifier { nullptr },
+ org_regain { nullptr },
+ pension_level { nullptr },
+ permanent_prestige { nullptr },
+ political_reform_desire { nullptr },
+ poor_savings_modifier { nullptr },
+ prestige_monthly_gain { nullptr },
+ reinforce_speed { nullptr },
+ research_points { nullptr },
+ research_points_modifier { nullptr },
+ research_points_on_conquer { nullptr },
+ rgo_output { nullptr },
+ rgo_throughput { nullptr },
+ ruling_party_support { nullptr },
+ self_unciv_economic_modifier { nullptr },
+ self_unciv_military_modifier { nullptr },
+ social_reform_desire { nullptr },
+ soldier_to_pop_loss { nullptr },
+ supply_consumption { nullptr },
+ supply_range { nullptr },
+ suppression_points_modifier { nullptr },
+ tariff_efficiency_modifier { nullptr },
+ tax_efficiency { nullptr },
+ unemployment_benefit { nullptr },
+ unciv_economic_modifier { nullptr },
+ unciv_military_modifier { nullptr },
+ unit_recruitment_time { nullptr },
+ war_exhaustion { nullptr },
+
+ /* Province Modifier Effects */
+ assimilation_rate { nullptr },
+ boost_strongest_party { nullptr },
+ combat_width_percentage_change { nullptr },
+ defence_terrain { nullptr },
+ farm_rgo_eff { nullptr },
+ farm_rgo_size { nullptr },
+ immigrant_attract { nullptr },
+ immigrant_push { nullptr },
+ life_rating { nullptr },
+ local_artisan_input { nullptr },
+ local_artisan_output { nullptr },
+ local_artisan_throughput { nullptr },
+ local_factory_input { nullptr },
+ local_factory_output { nullptr },
+ local_factory_throughput { nullptr },
+ local_repair { nullptr },
+ local_rgo_output { nullptr },
+ local_rgo_throughput { nullptr },
+ local_ruling_party_support { nullptr },
+ local_ship_build { nullptr },
+ max_attrition { nullptr },
+ mine_rgo_eff { nullptr },
+ mine_rgo_size { nullptr },
+ movement_cost_base { nullptr },
+ movement_cost_percentage_change { nullptr },
+ number_of_voters { nullptr },
+ pop_consciousness_modifier { nullptr },
+ pop_militancy_modifier { nullptr },
+ population_growth { nullptr },
+ supply_limit { nullptr },
+
+ /* Military Modifier Effects */
+ attack { nullptr },
+ attrition { nullptr },
+ defence_leader { nullptr },
+ experience { nullptr },
+ morale { nullptr },
+ organisation { nullptr },
+ reconnaissance { nullptr },
+ reliability { nullptr },
+ speed { nullptr },
+
+ /* BuildingType Effects */
+ building_type_effects { nullptr },
+
+ /* GoodDefinition Effects */
+ good_effects { nullptr },
+
+ /* UnitType Effects */
+ army_base_effects {},
+ regiment_type_effects { nullptr },
+ navy_base_effects {},
+ ship_type_effects { nullptr },
+
+ /* Rebel Effects */
+ rebel_org_gain_all { nullptr },
+ rebel_org_gain_effects { nullptr },
+
+ /* Pop Effects */
+ strata_effects { nullptr },
+
+ /* Technology Effects */
+ research_bonus_effects { nullptr } {}
diff --git a/src/openvic-simulation/modifier/ModifierEffectCache.hpp b/src/openvic-simulation/modifier/ModifierEffectCache.hpp
new file mode 100644
index 0000000..261ddde
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierEffectCache.hpp
@@ -0,0 +1,307 @@
+#pragma once
+
+#include "openvic-simulation/military/UnitType.hpp"
+#include "openvic-simulation/types/IndexedMap.hpp"
+#include "openvic-simulation/utility/Getters.hpp"
+
+namespace OpenVic {
+ struct ModifierEffect;
+ struct ModifierManager;
+
+ struct BuildingTypeManager;
+ struct BuildingType;
+ struct GoodDefinitionManager;
+ struct GoodDefinition;
+ struct RebelManager;
+ struct RebelType;
+ struct PopManager;
+ struct Strata;
+ struct TechnologyManager;
+ struct TechnologyFolder;
+
+ struct ModifierEffectCache {
+ friend struct ModifierManager;
+ friend struct BuildingTypeManager;
+ friend struct GoodDefinitionManager;
+ friend struct UnitTypeManager;
+ friend struct RebelManager;
+ friend struct PopManager;
+ friend struct TechnologyManager;
+
+ private:
+ /* Tech/inventions only */
+ ModifierEffect const* PROPERTY(cb_creation_speed);
+ ModifierEffect const* PROPERTY(combat_width_additive);
+ ModifierEffect const* PROPERTY(plurality);
+ ModifierEffect const* PROPERTY(pop_growth);
+ ModifierEffect const* PROPERTY(prestige_gain_multiplier);
+ ModifierEffect const* PROPERTY(regular_experience_level);
+ ModifierEffect const* PROPERTY(reinforce_rate);
+ ModifierEffect const* PROPERTY(separatism);
+ ModifierEffect const* PROPERTY(shared_prestige);
+ ModifierEffect const* PROPERTY(tax_eff);
+
+ /* Country Modifier Effects */
+ ModifierEffect const* PROPERTY(administrative_efficiency);
+ ModifierEffect const* PROPERTY(administrative_efficiency_modifier);
+ ModifierEffect const* PROPERTY(artisan_input);
+ ModifierEffect const* PROPERTY(artisan_output);
+ ModifierEffect const* PROPERTY(artisan_throughput);
+ ModifierEffect const* PROPERTY(badboy);
+ ModifierEffect const* PROPERTY(cb_generation_speed_modifier);
+ ModifierEffect const* PROPERTY(civilization_progress_modifier);
+ ModifierEffect const* PROPERTY(colonial_life_rating);
+ ModifierEffect const* PROPERTY(colonial_migration);
+ ModifierEffect const* PROPERTY(colonial_points);
+ ModifierEffect const* PROPERTY(colonial_prestige);
+ ModifierEffect const* PROPERTY(core_pop_consciousness_modifier);
+ ModifierEffect const* PROPERTY(core_pop_militancy_modifier);
+ ModifierEffect const* PROPERTY(dig_in_cap);
+ ModifierEffect const* PROPERTY(diplomatic_points);
+ ModifierEffect const* PROPERTY(diplomatic_points_modifier);
+ ModifierEffect const* PROPERTY(education_efficiency);
+ ModifierEffect const* PROPERTY(education_efficiency_modifier);
+ ModifierEffect const* PROPERTY(factory_cost);
+ ModifierEffect const* PROPERTY(factory_input);
+ ModifierEffect const* PROPERTY(factory_maintenance);
+ ModifierEffect const* PROPERTY(factory_output);
+ ModifierEffect const* PROPERTY(factory_owner_cost);
+ ModifierEffect const* PROPERTY(factory_throughput);
+ ModifierEffect const* PROPERTY(global_assimilation_rate);
+ ModifierEffect const* PROPERTY(global_immigrant_attract);
+ ModifierEffect const* PROPERTY(global_pop_consciousness_modifier);
+ ModifierEffect const* PROPERTY(global_pop_militancy_modifier);
+ ModifierEffect const* PROPERTY(global_population_growth);
+ ModifierEffect const* PROPERTY(goods_demand);
+ ModifierEffect const* PROPERTY(import_cost);
+ ModifierEffect const* PROPERTY(increase_research);
+ ModifierEffect const* PROPERTY(influence);
+ ModifierEffect const* PROPERTY(influence_modifier);
+ ModifierEffect const* PROPERTY(issue_change_speed);
+ ModifierEffect const* PROPERTY(land_attack_modifier);
+ ModifierEffect const* PROPERTY(land_attrition);
+ ModifierEffect const* PROPERTY(land_defense_modifier);
+ ModifierEffect const* PROPERTY(land_organisation);
+ ModifierEffect const* PROPERTY(land_unit_start_experience);
+ ModifierEffect const* PROPERTY(leadership);
+ ModifierEffect const* PROPERTY(leadership_modifier);
+ ModifierEffect const* PROPERTY(literacy_con_impact);
+ ModifierEffect const* PROPERTY(loan_interest);
+ ModifierEffect const* PROPERTY(max_loan_modifier);
+ ModifierEffect const* PROPERTY(max_military_spending);
+ ModifierEffect const* PROPERTY(max_national_focus);
+ ModifierEffect const* PROPERTY(max_social_spending);
+ ModifierEffect const* PROPERTY(max_tariff);
+ ModifierEffect const* PROPERTY(max_tax);
+ ModifierEffect const* PROPERTY(max_war_exhaustion);
+ ModifierEffect const* PROPERTY(military_tactics);
+ ModifierEffect const* PROPERTY(min_military_spending);
+ ModifierEffect const* PROPERTY(min_social_spending);
+ ModifierEffect const* PROPERTY(min_tariff);
+ ModifierEffect const* PROPERTY(min_tax);
+ ModifierEffect const* PROPERTY(minimum_wage);
+ ModifierEffect const* PROPERTY(mobilisation_economy_impact);
+ ModifierEffect const* PROPERTY(mobilisation_size);
+ ModifierEffect const* PROPERTY(mobilization_impact);
+ ModifierEffect const* PROPERTY(naval_attack_modifier);
+ ModifierEffect const* PROPERTY(naval_attrition);
+ ModifierEffect const* PROPERTY(naval_defense_modifier);
+ ModifierEffect const* PROPERTY(naval_organisation);
+ ModifierEffect const* PROPERTY(naval_unit_start_experience);
+ ModifierEffect const* PROPERTY(non_accepted_pop_consciousness_modifier);
+ ModifierEffect const* PROPERTY(non_accepted_pop_militancy_modifier);
+ ModifierEffect const* PROPERTY(org_regain);
+ ModifierEffect const* PROPERTY(pension_level);
+ ModifierEffect const* PROPERTY(permanent_prestige);
+ ModifierEffect const* PROPERTY(political_reform_desire);
+ ModifierEffect const* PROPERTY(poor_savings_modifier);
+ ModifierEffect const* PROPERTY(prestige_monthly_gain);
+ ModifierEffect const* PROPERTY(reinforce_speed);
+ ModifierEffect const* PROPERTY(research_points);
+ ModifierEffect const* PROPERTY(research_points_modifier);
+ ModifierEffect const* PROPERTY(research_points_on_conquer);
+ ModifierEffect const* PROPERTY(rgo_output);
+ ModifierEffect const* PROPERTY(rgo_throughput);
+ ModifierEffect const* PROPERTY(ruling_party_support);
+ ModifierEffect const* PROPERTY(self_unciv_economic_modifier);
+ ModifierEffect const* PROPERTY(self_unciv_military_modifier);
+ ModifierEffect const* PROPERTY(social_reform_desire);
+ ModifierEffect const* PROPERTY(soldier_to_pop_loss);
+ ModifierEffect const* PROPERTY(supply_consumption);
+ ModifierEffect const* PROPERTY(supply_range);
+ ModifierEffect const* PROPERTY(suppression_points_modifier);
+ ModifierEffect const* PROPERTY(tariff_efficiency_modifier);
+ ModifierEffect const* PROPERTY(tax_efficiency);
+ ModifierEffect const* PROPERTY(unemployment_benefit);
+ ModifierEffect const* PROPERTY(unciv_economic_modifier);
+ ModifierEffect const* PROPERTY(unciv_military_modifier);
+ ModifierEffect const* PROPERTY(unit_recruitment_time);
+ ModifierEffect const* PROPERTY(war_exhaustion);
+
+ /* Province Modifier Effects */
+ ModifierEffect const* PROPERTY(assimilation_rate);
+ ModifierEffect const* PROPERTY(boost_strongest_party);
+ ModifierEffect const* PROPERTY(combat_width_percentage_change);
+ ModifierEffect const* PROPERTY(defence_terrain);
+ ModifierEffect const* PROPERTY(farm_rgo_eff);
+ ModifierEffect const* PROPERTY(farm_rgo_size);
+ ModifierEffect const* PROPERTY(immigrant_attract);
+ ModifierEffect const* PROPERTY(immigrant_push);
+ ModifierEffect const* PROPERTY(life_rating);
+ ModifierEffect const* PROPERTY(local_artisan_input);
+ ModifierEffect const* PROPERTY(local_artisan_output);
+ ModifierEffect const* PROPERTY(local_artisan_throughput);
+ ModifierEffect const* PROPERTY(local_factory_input);
+ ModifierEffect const* PROPERTY(local_factory_output);
+ ModifierEffect const* PROPERTY(local_factory_throughput);
+ ModifierEffect const* PROPERTY(local_repair);
+ ModifierEffect const* PROPERTY(local_rgo_output);
+ ModifierEffect const* PROPERTY(local_rgo_throughput);
+ ModifierEffect const* PROPERTY(local_ruling_party_support);
+ ModifierEffect const* PROPERTY(local_ship_build);
+ ModifierEffect const* PROPERTY(max_attrition);
+ ModifierEffect const* PROPERTY(mine_rgo_eff);
+ ModifierEffect const* PROPERTY(mine_rgo_size);
+ ModifierEffect const* PROPERTY(movement_cost_base);
+ ModifierEffect const* PROPERTY(movement_cost_percentage_change);
+ ModifierEffect const* PROPERTY(number_of_voters);
+ ModifierEffect const* PROPERTY(pop_consciousness_modifier);
+ ModifierEffect const* PROPERTY(pop_militancy_modifier);
+ ModifierEffect const* PROPERTY(population_growth);
+ ModifierEffect const* PROPERTY(supply_limit);
+
+ /* Military Modifier Effects */
+ ModifierEffect const* PROPERTY(attack);
+ ModifierEffect const* PROPERTY(attrition);
+ ModifierEffect const* PROPERTY(defence_leader);
+ ModifierEffect const* PROPERTY(experience);
+ ModifierEffect const* PROPERTY(morale);
+ ModifierEffect const* PROPERTY(organisation);
+ ModifierEffect const* PROPERTY(reconnaissance);
+ ModifierEffect const* PROPERTY(reliability);
+ ModifierEffect const* PROPERTY(speed);
+
+ /* BuildingType Effects */
+ public:
+ struct building_type_effects_t {
+ friend struct BuildingTypeManager;
+
+ private:
+ ModifierEffect const* PROPERTY(min_level);
+ ModifierEffect const* PROPERTY(max_level);
+
+ public:
+ building_type_effects_t();
+ };
+
+ private:
+ IndexedMap<BuildingType, building_type_effects_t> PROPERTY(building_type_effects);
+
+ /* GoodDefinition Effects */
+ public:
+ struct good_effects_t {
+ friend struct GoodDefinitionManager;
+
+ private:
+ ModifierEffect const* PROPERTY(artisan_goods_input);
+ ModifierEffect const* PROPERTY(artisan_goods_output);
+ ModifierEffect const* PROPERTY(artisan_goods_throughput);
+ ModifierEffect const* PROPERTY(factory_goods_input);
+ ModifierEffect const* PROPERTY(factory_goods_output);
+ ModifierEffect const* PROPERTY(factory_goods_throughput);
+ ModifierEffect const* PROPERTY(rgo_goods_output);
+ ModifierEffect const* PROPERTY(rgo_goods_throughput);
+ ModifierEffect const* PROPERTY(rgo_size);
+
+ public:
+ good_effects_t();
+ };
+
+ private:
+ IndexedMap<GoodDefinition, good_effects_t> PROPERTY(good_effects);
+
+ /* UnitType Effects */
+ public:
+ struct unit_type_effects_t {
+ friend struct UnitTypeManager;
+
+ private:
+ ModifierEffect const* PROPERTY(attack);
+ ModifierEffect const* PROPERTY(defence);
+ ModifierEffect const* PROPERTY(default_organisation);
+ ModifierEffect const* PROPERTY(maximum_speed);
+ ModifierEffect const* PROPERTY(build_time);
+ ModifierEffect const* PROPERTY(supply_consumption);
+
+ protected:
+ unit_type_effects_t();
+ };
+
+ struct regiment_type_effects_t : unit_type_effects_t {
+ friend struct UnitTypeManager;
+
+ private:
+ ModifierEffect const* PROPERTY(reconnaissance);
+ ModifierEffect const* PROPERTY(discipline);
+ ModifierEffect const* PROPERTY(support);
+ ModifierEffect const* PROPERTY(maneuver);
+ ModifierEffect const* PROPERTY(siege);
+
+ public:
+ regiment_type_effects_t();
+ };
+
+ struct ship_type_effects_t : unit_type_effects_t {
+ friend struct UnitTypeManager;
+
+ private:
+ ModifierEffect const* PROPERTY(colonial_points);
+ ModifierEffect const* PROPERTY(supply_consumption_score);
+ ModifierEffect const* PROPERTY(hull);
+ ModifierEffect const* PROPERTY(gun_power);
+ ModifierEffect const* PROPERTY(fire_range);
+ ModifierEffect const* PROPERTY(evasion);
+ ModifierEffect const* PROPERTY(torpedo_attack);
+
+ public:
+ ship_type_effects_t();
+ };
+
+ private:
+ regiment_type_effects_t PROPERTY(army_base_effects);
+ IndexedMap<RegimentType, regiment_type_effects_t> PROPERTY(regiment_type_effects);
+ ship_type_effects_t PROPERTY(navy_base_effects);
+ IndexedMap<ShipType, ship_type_effects_t> PROPERTY(ship_type_effects);
+
+ /* Rebel Effects */
+ ModifierEffect const* PROPERTY(rebel_org_gain_all);
+ IndexedMap<RebelType, ModifierEffect const*> PROPERTY(rebel_org_gain_effects);
+
+ /* Pop Effects */
+ public:
+ struct strata_effects_t {
+ friend struct PopManager;
+
+ private:
+ ModifierEffect const* PROPERTY(income_modifier);
+ ModifierEffect const* PROPERTY(vote);
+ ModifierEffect const* PROPERTY(life_needs);
+ ModifierEffect const* PROPERTY(everyday_needs);
+ ModifierEffect const* PROPERTY(luxury_needs);
+
+ public:
+ strata_effects_t();
+ };
+
+ private:
+ IndexedMap<Strata, strata_effects_t> PROPERTY(strata_effects);
+
+ /* Technology Effects */
+ IndexedMap<TechnologyFolder, ModifierEffect const*> PROPERTY(research_bonus_effects);
+
+ ModifierEffectCache();
+
+ public:
+ ModifierEffectCache(ModifierEffectCache&&) = default;
+ };
+}
diff --git a/src/openvic-simulation/modifier/ModifierManager.cpp b/src/openvic-simulation/modifier/ModifierManager.cpp
new file mode 100644
index 0000000..daf186f
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierManager.cpp
@@ -0,0 +1,866 @@
+#include "ModifierManager.hpp"
+
+using namespace OpenVic;
+using namespace OpenVic::NodeTools;
+
+bool ModifierManager::add_modifier_effect(
+ ModifierEffect const*& effect_cache, std::string_view identifier, bool positive_good, ModifierEffect::format_t format,
+ ModifierEffect::target_t targets, std::string_view localisation_key
+) {
+ using enum ModifierEffect::target_t;
+
+ if (identifier.empty()) {
+ Logger::error("Invalid modifier effect identifier - empty!");
+ return false;
+ }
+
+ if (targets == NO_TARGETS) {
+ Logger::error("Invalid targets for modifier effect \"", identifier, "\" - none!");
+ return false;
+ }
+
+ if (!NumberUtils::is_power_of_two(static_cast<uint64_t>(targets))) {
+ Logger::error(
+ "Invalid targets for modifier effect \"", identifier, "\" - ", ModifierEffect::target_to_string(targets),
+ " (can only contain one target)"
+ );
+ return false;
+ }
+
+ if (effect_cache != nullptr) {
+ Logger::error(
+ "Cache variable for modifier effect \"", identifier, "\" is already filled with modifier effect \"",
+ effect_cache->get_identifier(), "\""
+ );
+ return false;
+ }
+
+ const bool ret = modifier_effects.add_item({ std::move(identifier), positive_good, format, targets, localisation_key });
+
+ if (ret) {
+ effect_cache = &modifier_effects.get_items().back();
+ }
+
+ return ret;
+}
+
+bool ModifierManager::setup_modifier_effects() {
+ // Variant Modifier Effeects
+ static const std::string combat_width = "combat_width";
+ static const std::string movement_cost = "movement_cost";
+ static const std::string prestige = "prestige";
+ static const std::string defence = "defence";
+
+ bool ret = true;
+
+ using enum ModifierEffect::format_t;
+ using enum ModifierEffect::target_t;
+ using enum Modifier::modifier_type_t;
+
+ /* Tech/inventions only */
+ ret &= add_modifier_effect(
+ modifier_effect_cache.cb_creation_speed, "cb_creation_speed", true, PROPORTION_DECIMAL, COUNTRY, "CB_MANUFACTURE_TECH"
+ );
+ // When applied to countries (army tech/inventions), combat_width is an additive integer value.
+ ret &= add_modifier_effect(
+ modifier_effect_cache.combat_width_additive, "combat_width add", false, INT, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key(combat_width)
+ );
+ ret &= register_modifier_effect_variants(
+ combat_width, modifier_effect_cache.combat_width_additive, { TECHNOLOGY, INVENTION }
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.plurality, "plurality", true, PERCENTAGE_DECIMAL, COUNTRY, "TECH_PLURALITY"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.pop_growth, "pop_growth", true, PROPORTION_DECIMAL, COUNTRY, "TECH_POP_GROWTH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.prestige_gain_multiplier, "prestige gain_multiplier", true, PROPORTION_DECIMAL, COUNTRY,
+ "PRESTIGE_MODIFIER_TECH"
+ );
+ ret &= register_modifier_effect_variants(
+ prestige, modifier_effect_cache.prestige_gain_multiplier, { TECHNOLOGY, INVENTION }
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.regular_experience_level, "regular_experience_level", true, RAW_DECIMAL, COUNTRY,
+ "REGULAR_EXP_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.reinforce_rate, "reinforce_rate", true, PROPORTION_DECIMAL, COUNTRY, "REINFORCE_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.separatism, "seperatism", // paradox typo
+ false, PROPORTION_DECIMAL, COUNTRY, "SEPARATISM_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.shared_prestige, "shared_prestige", true, RAW_DECIMAL, COUNTRY, "SHARED_PRESTIGE_TECH"
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.tax_eff, "tax_eff", true, PERCENTAGE_DECIMAL, COUNTRY, "TECH_TAX_EFF");
+
+ /* Country Modifier Effects */
+ ret &= add_modifier_effect(
+ modifier_effect_cache.administrative_efficiency, "administrative_efficiency", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.administrative_efficiency_modifier, "administrative_efficiency_modifier", true,
+ PROPORTION_DECIMAL, COUNTRY, ModifierEffect::make_default_modifier_effect_localisation_key("administrative_efficiency")
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.artisan_input, "artisan_input", false, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.artisan_output, "artisan_output", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.artisan_throughput, "artisan_throughput", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.badboy, "badboy", false, RAW_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.cb_generation_speed_modifier, "cb_generation_speed_modifier", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.civilization_progress_modifier, "civilization_progress_modifier", true, PROPORTION_DECIMAL,
+ COUNTRY, ModifierEffect::make_default_modifier_effect_localisation_key("civilization_progress")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.colonial_life_rating, "colonial_life_rating", false, INT, COUNTRY, "COLONIAL_LIFE_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.colonial_migration, "colonial_migration", true, PROPORTION_DECIMAL, COUNTRY,
+ "COLONIAL_MIGRATION_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.colonial_points, "colonial_points", true, INT, COUNTRY, "COLONIAL_POINTS_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.colonial_prestige, "colonial_prestige", true, PROPORTION_DECIMAL, COUNTRY,
+ "COLONIAL_PRESTIGE_MODIFIER_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.core_pop_consciousness_modifier, "core_pop_consciousness_modifier", false, RAW_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.core_pop_militancy_modifier, "core_pop_militancy_modifier", false, RAW_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.dig_in_cap, "dig_in_cap", true, INT, COUNTRY, "DIGIN_FROM_TECH");
+ ret &= add_modifier_effect(
+ modifier_effect_cache.diplomatic_points, "diplomatic_points", true, PROPORTION_DECIMAL, COUNTRY,
+ "DIPLOMATIC_POINTS_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.diplomatic_points_modifier, "diplomatic_points_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("diplopoints_gain")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.education_efficiency, "education_efficiency", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.education_efficiency_modifier, "education_efficiency_modifier", true, PROPORTION_DECIMAL,
+ COUNTRY, ModifierEffect::make_default_modifier_effect_localisation_key("education_efficiency")
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.factory_cost, "factory_cost", false, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.factory_input, "factory_input", false, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.factory_maintenance, "factory_maintenance", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.factory_output, "factory_output", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.factory_owner_cost, "factory_owner_cost", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.factory_throughput, "factory_throughput", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.global_assimilation_rate, "global_assimilation_rate", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("assimilation_rate")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.global_immigrant_attract, "global_immigrant_attract", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("immigant_attract")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.global_pop_consciousness_modifier, "global_pop_consciousness_modifier", false, RAW_DECIMAL,
+ COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.global_pop_militancy_modifier, "global_pop_militancy_modifier", false, RAW_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.global_population_growth, "global_population_growth", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("population_growth")
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.goods_demand, "goods_demand", false, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.import_cost, "import_cost", false, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.increase_research, "increase_research", true, PROPORTION_DECIMAL, COUNTRY, "INC_RES_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.influence, "influence", true, PROPORTION_DECIMAL, COUNTRY, "TECH_GP_INFLUENCE"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.influence_modifier, "influence_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("greatpower_influence_gain")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.issue_change_speed, "issue_change_speed", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.land_attack_modifier, "land_attack_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("land_attack")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.land_attrition, "land_attrition", false, PROPORTION_DECIMAL, COUNTRY, "LAND_ATTRITION_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.land_defense_modifier, "land_defense_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("land_defense")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.land_organisation, "land_organisation", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.land_unit_start_experience, "land_unit_start_experience", true, RAW_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.leadership, "leadership", true, RAW_DECIMAL, COUNTRY, "LEADERSHIP");
+ ret &= add_modifier_effect(
+ modifier_effect_cache.leadership_modifier, "leadership_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("global_leadership_modifier")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.literacy_con_impact, "literacy_con_impact", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.loan_interest, "loan_interest", false, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.max_loan_modifier, "max_loan_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("max_loan_amount")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.max_military_spending, "max_military_spending", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.max_national_focus, "max_national_focus", true, INT, COUNTRY, "TECH_MAX_FOCUS"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.max_social_spending, "max_social_spending", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.max_tariff, "max_tariff", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.max_tax, "max_tax", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.max_war_exhaustion, "max_war_exhaustion", true, PERCENTAGE_DECIMAL, COUNTRY, "MAX_WAR_EXHAUSTION"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.military_tactics, "military_tactics", true, PROPORTION_DECIMAL, COUNTRY, "MIL_TACTICS_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.min_military_spending, "min_military_spending", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.min_social_spending, "min_social_spending", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.min_tariff, "min_tariff", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.min_tax, "min_tax", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.minimum_wage, "minimum_wage", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("minimun_wage")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.mobilisation_economy_impact, "mobilisation_economy_impact", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.mobilisation_size, "mobilisation_size", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.mobilization_impact, "mobilization_impact", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.naval_attack_modifier, "naval_attack_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("naval_attack")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.naval_attrition, "naval_attrition", false, PROPORTION_DECIMAL, COUNTRY, "NAVAL_ATTRITION_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.naval_defense_modifier, "naval_defense_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("naval_defense")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.naval_organisation, "naval_organisation", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.naval_unit_start_experience, "naval_unit_start_experience", true, RAW_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.non_accepted_pop_consciousness_modifier, "non_accepted_pop_consciousness_modifier", false,
+ RAW_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.non_accepted_pop_militancy_modifier, "non_accepted_pop_militancy_modifier", false, RAW_DECIMAL,
+ COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.org_regain, "org_regain", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.pension_level, "pension_level", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.permanent_prestige, "permanent_prestige", true, RAW_DECIMAL, COUNTRY, "PERMANENT_PRESTIGE_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.political_reform_desire, "political_reform_desire", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.poor_savings_modifier, "poor_savings_modifier", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.prestige_monthly_gain, "prestige monthly_gain", true, RAW_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key(prestige)
+ );
+ ret &= register_modifier_effect_variants(
+ prestige, modifier_effect_cache.prestige_monthly_gain, { EVENT, STATIC, TRIGGERED }
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.reinforce_speed, "reinforce_speed", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.research_points, "research_points", true, RAW_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.research_points_modifier, "research_points_modifier", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.research_points_on_conquer, "research_points_on_conquer", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.rgo_output, "rgo_output", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(modifier_effect_cache.rgo_throughput, "rgo_throughput", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.ruling_party_support, "ruling_party_support", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.self_unciv_economic_modifier, "self_unciv_economic_modifier", false, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("self_unciv_economic")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.self_unciv_military_modifier, "self_unciv_military_modifier", false, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("self_unciv_military")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.social_reform_desire, "social_reform_desire", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.soldier_to_pop_loss, "soldier_to_pop_loss", true, PROPORTION_DECIMAL, COUNTRY,
+ "SOLDIER_TO_POP_LOSS_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.supply_consumption, "supply_consumption", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.supply_range, "supply_range", true, PROPORTION_DECIMAL, COUNTRY, "SUPPLY_RANGE_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.suppression_points_modifier, "suppression_points_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ "SUPPRESSION_TECH"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.tariff_efficiency_modifier, "tariff_efficiency_modifier", true, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("tariff_efficiency")
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.tax_efficiency, "tax_efficiency", true, PROPORTION_DECIMAL, COUNTRY);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.unemployment_benefit, "unemployment_benefit", true, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.unciv_economic_modifier, "unciv_economic_modifier", false, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("unciv_economic")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.unciv_military_modifier, "unciv_military_modifier", false, PROPORTION_DECIMAL, COUNTRY,
+ ModifierEffect::make_default_modifier_effect_localisation_key("unciv_military")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.unit_recruitment_time, "unit_recruitment_time", false, PROPORTION_DECIMAL, COUNTRY
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.war_exhaustion, "war_exhaustion", false, PROPORTION_DECIMAL, COUNTRY, "WAR_EXHAUST_BATTLES"
+ );
+
+ /* Province Modifier Effects */
+ ret &= add_modifier_effect(
+ modifier_effect_cache.assimilation_rate, "assimilation_rate", true, PROPORTION_DECIMAL, PROVINCE
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.boost_strongest_party, "boost_strongest_party", false, PROPORTION_DECIMAL, PROVINCE
+ );
+ // When applied to provinces (terrain), combat_width is a multiplicative proportional decimal value.
+ ret &= add_modifier_effect(
+ modifier_effect_cache.combat_width_percentage_change, "combat_width percentage_change", false, PROPORTION_DECIMAL,
+ PROVINCE, ModifierEffect::make_default_modifier_effect_localisation_key(combat_width)
+ );
+ ret &= register_modifier_effect_variants(combat_width, modifier_effect_cache.combat_width_percentage_change, { TERRAIN });
+ ret &= add_modifier_effect(modifier_effect_cache.defence_terrain, "defence terrain", true, INT, PROVINCE, "TRAIT_DEFEND");
+ ret &= register_modifier_effect_variants(defence, modifier_effect_cache.defence_terrain, { TERRAIN });
+ ret &= add_modifier_effect(
+ modifier_effect_cache.farm_rgo_eff, "farm_rgo_eff", true, PROPORTION_DECIMAL, PROVINCE, "TECH_FARM_OUTPUT"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.farm_rgo_size, "farm_rgo_size", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("farm_size")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.immigrant_attract, "immigrant_attract", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("immigant_attract")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.immigrant_push, "immigrant_push", false, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("immigant_push")
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.life_rating, "life_rating", true, PROPORTION_DECIMAL, PROVINCE);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_artisan_input, "local_artisan_input", false, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("artisan_input")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_artisan_output, "local_artisan_output", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("artisan_output")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_artisan_throughput, "local_artisan_throughput", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("artisan_throughput")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_factory_input, "local_factory_input", false, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("factory_input")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_factory_output, "local_factory_output", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("factory_output")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_factory_throughput, "local_factory_throughput", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("factory_throughput")
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.local_repair, "local_repair", true, PROPORTION_DECIMAL, PROVINCE);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_rgo_output, "local_rgo_output", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("rgo_output")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_rgo_throughput, "local_rgo_throughput", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("rgo_throughput")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_ruling_party_support, "local_ruling_party_support", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("ruling_party_support")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.local_ship_build, "local_ship_build", false, PROPORTION_DECIMAL, PROVINCE
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.max_attrition, "max_attrition", false, RAW_DECIMAL, PROVINCE);
+ ret &= add_modifier_effect(
+ modifier_effect_cache.mine_rgo_eff, "mine_rgo_eff", true, PROPORTION_DECIMAL, PROVINCE, "TECH_MINE_OUTPUT"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.mine_rgo_size, "mine_rgo_size", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key("mine_size")
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.movement_cost_base, "movement_cost base", true, PROPORTION_DECIMAL, PROVINCE,
+ ModifierEffect::make_default_modifier_effect_localisation_key(movement_cost)
+ );
+ ret &= register_modifier_effect_variants(movement_cost, modifier_effect_cache.movement_cost_base, { TERRAIN });
+ ret &= add_modifier_effect(
+ modifier_effect_cache.movement_cost_percentage_change, "movement_cost percentage_change", false, PROPORTION_DECIMAL,
+ PROVINCE, ModifierEffect::make_default_modifier_effect_localisation_key(movement_cost)
+ );
+ ret &= register_modifier_effect_variants(
+ movement_cost, modifier_effect_cache.movement_cost_percentage_change, { EVENT, BUILDING }
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.number_of_voters, "number_of_voters", false, PROPORTION_DECIMAL, PROVINCE
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.pop_consciousness_modifier, "pop_consciousness_modifier", false, RAW_DECIMAL, PROVINCE
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.pop_militancy_modifier, "pop_militancy_modifier", false, RAW_DECIMAL, PROVINCE
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.population_growth, "population_growth", true, PROPORTION_DECIMAL, PROVINCE
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.supply_limit, "supply_limit", true, RAW_DECIMAL, PROVINCE);
+
+ /* Military Modifier Effects */
+ ret &= add_modifier_effect(modifier_effect_cache.attack, "attack", true, INT, UNIT, "TRAIT_ATTACK");
+ ret &= add_modifier_effect(modifier_effect_cache.attrition, "attrition", false, RAW_DECIMAL, UNIT, "ATTRITION");
+ ret &= add_modifier_effect(modifier_effect_cache.defence_leader, "defence leader", true, INT, UNIT, "TRAIT_DEFEND");
+ ret &= register_modifier_effect_variants(defence, modifier_effect_cache.defence_leader, { LEADER });
+ ret &= add_modifier_effect(
+ modifier_effect_cache.experience, "experience", true, PROPORTION_DECIMAL, UNIT, "TRAIT_EXPERIENCE"
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.morale, "morale", true, PROPORTION_DECIMAL, UNIT, "TRAIT_MORALE");
+ ret &= add_modifier_effect(
+ modifier_effect_cache.organisation, "organisation", true, PROPORTION_DECIMAL, UNIT, "TRAIT_ORGANISATION"
+ );
+ ret &= add_modifier_effect(
+ modifier_effect_cache.reconnaissance, "reconnaissance", true, PROPORTION_DECIMAL, UNIT, "TRAIT_RECONAISSANCE"
+ );
+ ret &= add_modifier_effect(modifier_effect_cache.reliability, "reliability", true, RAW_DECIMAL, UNIT, "TRAIT_RELIABILITY");
+ ret &= add_modifier_effect(modifier_effect_cache.speed, "speed", true, PROPORTION_DECIMAL, UNIT, "TRAIT_SPEED");
+
+ return ret;
+}
+
+bool ModifierManager::register_complex_modifier(std::string_view identifier) {
+ if (complex_modifiers.emplace(identifier).second) {
+ return true;
+ } else {
+ Logger::error("Duplicate complex modifier: ", identifier);
+ return false;
+ }
+}
+
+std::string ModifierManager::get_flat_identifier(
+ std::string_view complex_modifier_identifier, std::string_view variant_identifier
+) {
+ return StringUtils::append_string_views(complex_modifier_identifier, " ", variant_identifier);
+}
+
+// We use std::string const& identifier instead of std::string_view identifier as the map [] lookup operator only accepts
+// strings. In order to use string_views we need to use the find method but that returns an iterator with a const references
+// to the key and value (in order to prevent modification of the key), so it's simplest to just use [] with a string.
+bool ModifierManager::register_modifier_effect_variants(
+ std::string const& identifier, ModifierEffect const* effect, std::vector<Modifier::modifier_type_t> const& types
+) {
+ if (identifier.empty()) {
+ Logger::error("Invalid modifier effect variants identifier - empty!");
+ return false;
+ }
+
+ if (effect == nullptr) {
+ Logger::error("Invalid modifier effect variants effect for \"", identifier, "\" - nullptr!");
+ return false;
+ }
+
+ if (types.empty()) {
+ Logger::error("Invalid modifier effect variants types for \"", identifier, "\" - empty!");
+ return false;
+ }
+
+ effect_variant_map_t& variant_map = modifier_effect_variants[identifier];
+
+ bool ret = true;
+
+ for (const Modifier::modifier_type_t type : types) {
+ ModifierEffect const*& variant_effect = variant_map[type];
+
+ if (variant_effect != nullptr) {
+ Logger::error(
+ "Duplicate modifier effect variant for \"", identifier, "\" with type \"",
+ Modifier::modifier_type_to_string(type), "\" - already registered as \"",
+ variant_effect->get_identifier(), "\", setting to \"", effect->get_identifier(), "\""
+ );
+ ret = false;
+ }
+
+ variant_effect = effect;
+ }
+
+ return ret;
+}
+
+bool ModifierManager::add_event_modifier(std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon) {
+ using enum Modifier::modifier_type_t;
+
+ if (identifier.empty()) {
+ Logger::error("Invalid event modifier effect identifier - empty!");
+ return false;
+ }
+
+ return event_modifiers.add_item(
+ { identifier, std::move(values), EVENT, icon }, duplicate_warning_callback
+ );
+}
+
+bool ModifierManager::load_event_modifiers(ast::NodeCPtr root) {
+ const bool ret = expect_dictionary_reserve_length(
+ event_modifiers,
+ [this](std::string_view key, ast::NodeCPtr value) -> bool {
+ using enum Modifier::modifier_type_t;
+
+ ModifierValue modifier_value;
+ IconModifier::icon_t icon = 0;
+
+ bool ret = expect_modifier_value_and_keys(
+ move_variable_callback(modifier_value),
+ EVENT,
+ "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon))
+ )(value);
+
+ ret &= add_event_modifier(key, std::move(modifier_value), icon);
+
+ return ret;
+ }
+ )(root);
+
+ lock_event_modifiers();
+
+ return ret;
+}
+
+bool ModifierManager::add_static_modifier(std::string_view identifier, ModifierValue&& values) {
+ using enum Modifier::modifier_type_t;
+
+ if (identifier.empty()) {
+ Logger::error("Invalid static modifier effect identifier - empty!");
+ return false;
+ }
+
+ return static_modifiers.add_item(
+ { identifier, std::move(values), STATIC }, duplicate_warning_callback
+ );
+}
+
+bool ModifierManager::load_static_modifiers(ast::NodeCPtr root) {
+ bool ret = expect_dictionary_reserve_length(
+ static_modifiers,
+ [this](std::string_view key, ast::NodeCPtr value) -> bool {
+ using enum Modifier::modifier_type_t;
+
+ ModifierValue modifier_value;
+
+ bool ret = expect_modifier_value(move_variable_callback(modifier_value), STATIC)(value);
+
+ ret &= add_static_modifier(key, std::move(modifier_value));
+
+ return ret;
+ }
+ )(root);
+
+ lock_static_modifiers();
+
+ ret &= static_modifier_cache.load_static_modifiers(*this);
+
+ return ret;
+}
+
+bool ModifierManager::add_triggered_modifier(
+ std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger
+) {
+ using enum Modifier::modifier_type_t;
+
+ if (identifier.empty()) {
+ Logger::error("Invalid triggered modifier effect identifier - empty!");
+ return false;
+ }
+
+ return triggered_modifiers.add_item(
+ { identifier, std::move(values), TRIGGERED, icon, std::move(trigger) },
+ duplicate_warning_callback
+ );
+}
+
+bool ModifierManager::load_triggered_modifiers(ast::NodeCPtr root) {
+ const bool ret = expect_dictionary_reserve_length(
+ triggered_modifiers,
+ [this](std::string_view key, ast::NodeCPtr value) -> bool {
+ using enum Modifier::modifier_type_t;
+
+ ModifierValue modifier_value;
+ IconModifier::icon_t icon = 0;
+ ConditionScript trigger { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE };
+
+ bool ret = expect_modifier_value_and_keys(
+ move_variable_callback(modifier_value),
+ TRIGGERED,
+ "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)),
+ "trigger", ONE_EXACTLY, trigger.expect_script()
+ )(value);
+
+ ret &= add_triggered_modifier(key, std::move(modifier_value), icon, std::move(trigger));
+
+ return ret;
+ }
+ )(root);
+
+ lock_triggered_modifiers();
+
+ return ret;
+}
+
+bool ModifierManager::parse_scripts(DefinitionManager const& definition_manager) {
+ bool ret = true;
+
+ for (TriggeredModifier& modifier : triggered_modifiers.get_items()) {
+ ret &= modifier.parse_scripts(definition_manager);
+ }
+
+ return ret;
+}
+
+key_value_callback_t ModifierManager::_modifier_effect_callback(
+ ModifierValue& modifier, Modifier::modifier_type_t type, key_value_callback_t default_callback,
+ ModifierEffectValidator auto effect_validator
+) const {
+ const auto add_modifier_cb = [this, &modifier, effect_validator](
+ ModifierEffect const* effect, ast::NodeCPtr value
+ ) -> bool {
+ if (effect_validator(*effect)) {
+ static const case_insensitive_string_set_t no_effect_modifiers {
+ "boost_strongest_party", "poor_savings_modifier", "local_artisan_input", "local_artisan_throughput",
+ "local_artisan_output", "artisan_input", "artisan_throughput", "artisan_output",
+ "import_cost", "unciv_economic_modifier", "unciv_military_modifier"
+ };
+
+ if (no_effect_modifiers.contains(effect->get_identifier())) {
+ Logger::warning("This modifier does nothing: ", effect->get_identifier());
+ }
+
+ return expect_fixed_point(map_callback(modifier.values, effect))(value);
+ } else {
+ Logger::error("Failed to validate modifier effect: ", effect->get_identifier());
+ return false;
+ }
+ };
+
+ const auto add_flattened_modifier_cb = [this, add_modifier_cb](
+ std::string_view prefix, std::string_view key, ast::NodeCPtr value
+ ) -> bool {
+ const std::string flat_identifier = get_flat_identifier(prefix, key);
+ ModifierEffect const* effect = get_modifier_effect_by_identifier(flat_identifier);
+ if (effect != nullptr) {
+ return add_modifier_cb(effect, value);
+ } else {
+ Logger::error("Could not find flattened modifier: ", flat_identifier);
+ return false;
+ }
+ };
+
+ return [this, type, default_callback, add_modifier_cb, add_flattened_modifier_cb](
+ std::string_view key, ast::NodeCPtr value
+ ) -> bool {
+
+ if (dryad::node_has_kind<ast::IdentifierValue>(value)) {
+ ModifierEffect const* effect = get_modifier_effect_by_identifier(key);
+
+ if (effect != nullptr) {
+ return add_modifier_cb(effect, value);
+ } else if (key == "war_exhaustion_effect") {
+ Logger::warning("war_exhaustion_effect does nothing (vanilla issues have it).");
+ return true;
+ } else {
+ const decltype(modifier_effect_variants)::const_iterator effect_it = modifier_effect_variants.find(key);
+
+ if (effect_it != modifier_effect_variants.end()) {
+ effect_variant_map_t const& variants = effect_it->second;
+
+ const effect_variant_map_t::const_iterator variant_it = variants.find(type);
+
+ if (variant_it != variants.end()) {
+ effect = variant_it->second;
+
+ if (effect != nullptr) {
+ return add_modifier_cb(effect, value);
+ }
+ }
+
+ Logger::error(
+ "Modifier effect \"", key, "\" does not have a valid variant for use in ",
+ Modifier::modifier_type_to_string(type), " modifiers."
+ );
+ return false;
+ }
+ }
+ } else if (dryad::node_has_kind<ast::ListValue>(value) && complex_modifiers.contains(key)) {
+ if (key == "rebel_org_gain") { // because of course there's a special one
+ std::string_view faction_identifier;
+ ast::NodeCPtr value_node = nullptr;
+
+ bool ret = expect_dictionary_keys(
+ "faction", ONE_EXACTLY, expect_identifier(assign_variable_callback(faction_identifier)),
+ "value", ONE_EXACTLY, assign_variable_callback(value_node)
+ )(value);
+
+ ret &= add_flattened_modifier_cb(key, faction_identifier, value_node);
+
+ return ret;
+ } else {
+ return expect_dictionary(std::bind_front(add_flattened_modifier_cb, key))(value);
+ }
+ }
+
+ return default_callback(key, value);
+ };
+}
+
+node_callback_t ModifierManager::expect_validated_modifier_value_and_default(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type, key_value_callback_t default_callback,
+ ModifierEffectValidator auto effect_validator
+) const {
+ return [this, modifier_callback, type, default_callback, effect_validator](ast::NodeCPtr root) -> bool {
+ ModifierValue modifier;
+
+ bool ret = expect_dictionary_reserve_length(
+ modifier.values,
+ _modifier_effect_callback(modifier, type, default_callback, effect_validator)
+ )(root);
+
+ ret &= modifier_callback(std::move(modifier));
+
+ return ret;
+ };
+}
+
+node_callback_t ModifierManager::expect_validated_modifier_value(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ ModifierEffectValidator auto effect_validator
+) const {
+ return expect_validated_modifier_value_and_default(modifier_callback, type, key_value_invalid_callback, effect_validator);
+}
+
+node_callback_t ModifierManager::expect_modifier_value_and_default(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type, key_value_callback_t default_callback
+) const {
+ return expect_validated_modifier_value_and_default(
+ modifier_callback, type, default_callback, [](ModifierEffect const&) -> bool {
+ return true;
+ }
+ );
+}
+
+node_callback_t ModifierManager::expect_modifier_value(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type
+) const {
+ return expect_modifier_value_and_default(modifier_callback, type, key_value_invalid_callback);
+}
+
+node_callback_t ModifierManager::expect_whitelisted_modifier_value_and_default(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type, string_set_t const& whitelist,
+ key_value_callback_t default_callback
+) const {
+ return expect_validated_modifier_value_and_default(
+ modifier_callback, type, default_callback,
+ [&whitelist](ModifierEffect const& effect) -> bool {
+ return whitelist.contains(effect.get_identifier());
+ }
+ );
+}
+
+node_callback_t ModifierManager::expect_whitelisted_modifier_value(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type, string_set_t const& whitelist
+) const {
+ return expect_whitelisted_modifier_value_and_default(modifier_callback, type, whitelist, key_value_invalid_callback);
+}
+
+node_callback_t ModifierManager::expect_modifier_value_and_key_map_and_default(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type, key_value_callback_t default_callback,
+ key_map_t&& key_map
+) const {
+ return [this, modifier_callback, type, default_callback, key_map = std::move(key_map)](
+ ast::NodeCPtr node
+ ) mutable -> bool {
+ bool ret = expect_modifier_value_and_default(
+ modifier_callback, type, dictionary_keys_callback(key_map, default_callback)
+ )(node);
+
+ ret &= check_key_map_counts(key_map);
+
+ return ret;
+ };
+}
+
+node_callback_t ModifierManager::expect_modifier_value_and_key_map(
+ callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type, key_map_t&& key_map
+) const {
+ return expect_modifier_value_and_key_map_and_default(
+ modifier_callback, type, key_value_invalid_callback, std::move(key_map)
+ );
+}
diff --git a/src/openvic-simulation/modifier/ModifierManager.hpp b/src/openvic-simulation/modifier/ModifierManager.hpp
new file mode 100644
index 0000000..51f89ae
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierManager.hpp
@@ -0,0 +1,144 @@
+#pragma once
+
+#include "openvic-simulation/modifier/Modifier.hpp"
+#include "openvic-simulation/modifier/ModifierEffectCache.hpp"
+#include "openvic-simulation/modifier/StaticModifierCache.hpp"
+#include "openvic-simulation/types/IdentifierRegistry.hpp"
+
+namespace OpenVic {
+
+ template<typename Fn>
+ concept ModifierEffectValidator = std::predicate<Fn, ModifierEffect const&>;
+
+ struct ModifierManager {
+ friend struct StaticModifierCache;
+ friend struct BuildingTypeManager;
+ friend struct GoodDefinitionManager;
+ friend struct UnitTypeManager;
+ friend struct RebelManager;
+ friend struct PopManager;
+ friend struct TechnologyManager;
+
+ using effect_variant_map_t = ordered_map<Modifier::modifier_type_t, ModifierEffect const*>;
+
+ /* Some ModifierEffects are generated mid-load, such as max/min count modifiers for each building, so
+ * we can't lock it until loading is over. This means we can't rely on locking for pointer stability,
+ * so instead we store the effects in a deque which doesn't invalidate pointers on insert.
+ */
+ private:
+ CaseInsensitiveIdentifierRegistry<ModifierEffect, RegistryStorageInfoDeque> IDENTIFIER_REGISTRY(modifier_effect);
+ case_insensitive_string_set_t complex_modifiers;
+ string_map_t<effect_variant_map_t> modifier_effect_variants;
+
+ IdentifierRegistry<IconModifier> IDENTIFIER_REGISTRY(event_modifier);
+ IdentifierRegistry<Modifier> IDENTIFIER_REGISTRY(static_modifier);
+ IdentifierRegistry<TriggeredModifier> IDENTIFIER_REGISTRY(triggered_modifier);
+
+ ModifierEffectCache PROPERTY(modifier_effect_cache);
+ StaticModifierCache PROPERTY(static_modifier_cache);
+
+ /* effect_validator takes in ModifierEffect const& */
+ NodeTools::key_value_callback_t _modifier_effect_callback(
+ ModifierValue& modifier, Modifier::modifier_type_t type, NodeTools::key_value_callback_t default_callback,
+ ModifierEffectValidator auto effect_validator
+ ) const;
+
+ public:
+ bool add_modifier_effect(
+ ModifierEffect const*& effect_cache,
+ std::string_view identifier,
+ bool positive_good,
+ ModifierEffect::format_t format,
+ ModifierEffect::target_t targets,
+ std::string_view localisation_key = {}
+ );
+
+ bool register_complex_modifier(std::string_view identifier);
+ static std::string get_flat_identifier(
+ std::string_view complex_modifier_identifier, std::string_view variant_identifier
+ );
+
+ bool register_modifier_effect_variants(
+ std::string const& identifier, ModifierEffect const* effect, std::vector<Modifier::modifier_type_t> const& types
+ );
+
+ bool setup_modifier_effects();
+
+ bool add_event_modifier(std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon);
+ bool load_event_modifiers(ast::NodeCPtr root);
+
+ bool add_static_modifier(std::string_view identifier, ModifierValue&& values);
+ bool load_static_modifiers(ast::NodeCPtr root);
+
+ bool add_triggered_modifier(
+ std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger
+ );
+ bool load_triggered_modifiers(ast::NodeCPtr root);
+
+ bool parse_scripts(DefinitionManager const& definition_manager);
+
+ NodeTools::node_callback_t expect_validated_modifier_value_and_default(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ NodeTools::key_value_callback_t default_callback, ModifierEffectValidator auto effect_validator
+ ) const;
+ NodeTools::node_callback_t expect_validated_modifier_value(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ ModifierEffectValidator auto effect_validator
+ ) const;
+
+ NodeTools::node_callback_t expect_modifier_value_and_default(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ NodeTools::key_value_callback_t default_callback
+ ) const;
+ NodeTools::node_callback_t expect_modifier_value(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type
+ ) const;
+
+ NodeTools::node_callback_t expect_whitelisted_modifier_value_and_default(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ string_set_t const& whitelist, NodeTools::key_value_callback_t default_callback
+ ) const;
+ NodeTools::node_callback_t expect_whitelisted_modifier_value(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ string_set_t const& whitelist
+ ) const;
+
+ // In the functions below, key_map refers to a map from identifier strings to NodeTools::dictionary_entry_t,
+ // allowing any non-modifier effect keys found to be parsed in a custom way, similar to expect_dictionary_keys.
+ NodeTools::node_callback_t expect_modifier_value_and_key_map_and_default(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ NodeTools::key_value_callback_t default_callback, NodeTools::key_map_t&& key_map
+ ) const;
+ NodeTools::node_callback_t expect_modifier_value_and_key_map(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ NodeTools::key_map_t&& key_map
+ ) const;
+
+ template<typename... Args>
+ NodeTools::node_callback_t expect_modifier_value_and_key_map_and_default(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ NodeTools::key_value_callback_t default_callback, NodeTools::key_map_t&& key_map, Args... args
+ ) const {
+ NodeTools::add_key_map_entries(key_map, args...);
+ return expect_modifier_value_and_key_map_and_default(
+ modifier_callback, type, default_callback, std::move(key_map)
+ );
+ }
+
+ template<typename... Args>
+ NodeTools::node_callback_t expect_modifier_value_and_keys_and_default(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type,
+ NodeTools::key_value_callback_t default_callback, Args... args
+ ) const {
+ return expect_modifier_value_and_key_map_and_default(modifier_callback, type, default_callback, {}, args...);
+ }
+ template<typename... Args>
+ NodeTools::node_callback_t expect_modifier_value_and_keys(
+ NodeTools::callback_t<ModifierValue&&> modifier_callback, Modifier::modifier_type_t type, Args... args
+ ) const {
+ return expect_modifier_value_and_key_map_and_default(
+ modifier_callback, type, NodeTools::key_value_invalid_callback, {}, args...
+ );
+ }
+ };
+}
diff --git a/src/openvic-simulation/modifier/ModifierSum.cpp b/src/openvic-simulation/modifier/ModifierSum.cpp
index 8e5ce48..676e872 100644
--- a/src/openvic-simulation/modifier/ModifierSum.cpp
+++ b/src/openvic-simulation/modifier/ModifierSum.cpp
@@ -1,7 +1,28 @@
#include "ModifierSum.hpp"
+#include "openvic-simulation/modifier/Modifier.hpp"
+
+#include "openvic-simulation/country/CountryInstance.hpp"
+#include "openvic-simulation/map/ProvinceInstance.hpp"
+
using namespace OpenVic;
+std::string_view ModifierSum::source_to_string(modifier_source_t const& source) {
+ return std::visit(
+ [](HasGetIdentifier auto const* has_identifier) -> std::string_view {
+ return has_identifier->get_identifier();
+ },
+ source
+ );
+}
+
+std::string ModifierSum::modifier_entry_t::to_string() const {
+ return StringUtils::append_string_views(
+ "[", modifier->get_identifier(), ", ", multiplier.to_string(), ", ", source_to_string(source), ", ",
+ ModifierEffect::target_to_string(excluded_targets), "]"
+ );
+}
+
void ModifierSum::clear() {
modifiers.clear();
value_sum.clear();
@@ -15,44 +36,88 @@ fixed_point_t ModifierSum::get_effect(ModifierEffect const& effect, bool* effect
return value_sum.get_effect(effect, effect_found);
}
+fixed_point_t ModifierSum::get_effect_nullcheck(ModifierEffect const* effect, bool* effect_found) const {
+ return value_sum.get_effect_nullcheck(effect, effect_found);
+}
+
bool ModifierSum::has_effect(ModifierEffect const& effect) const {
return value_sum.has_effect(effect);
}
-void ModifierSum::add_modifier(Modifier const& modifier, fixed_point_t multiplier) {
- modifiers[&modifier] += multiplier;
- value_sum.multiply_add(modifier, multiplier);
+void ModifierSum::add_modifier(
+ Modifier const& modifier, modifier_source_t const& source, fixed_point_t multiplier,
+ ModifierEffect::target_t excluded_targets
+) {
+ using enum ModifierEffect::target_t;
+
+ // We could test that excluded_targets != ALL_TARGETS, but in practice it's always
+ // called with an explcit/hardcoded value and so won't ever exclude everything.
+ if (multiplier != fixed_point_t::_0()) {
+ modifiers.emplace_back(&modifier, multiplier, source, excluded_targets);
+ value_sum.multiply_add_exclude_targets(modifier, multiplier, excluded_targets);
+ }
+}
+
+void ModifierSum::add_modifier_nullcheck(
+ Modifier const* modifier, modifier_source_t const& source, fixed_point_t multiplier,
+ ModifierEffect::target_t excluded_targets
+) {
+ if (modifier != nullptr) {
+ add_modifier(*modifier, source, multiplier, excluded_targets);
+ }
}
void ModifierSum::add_modifier_sum(ModifierSum const& modifier_sum) {
- modifiers += modifier_sum.modifiers;
+ modifiers.insert(modifiers.end(), modifier_sum.modifiers.begin(), modifier_sum.modifiers.end());
value_sum += modifier_sum.value_sum;
}
-ModifierSum& ModifierSum::operator+=(Modifier const& modifier) {
- add_modifier(modifier);
- return *this;
+void ModifierSum::add_modifier_sum_exclude_targets(
+ ModifierSum const& modifier_sum, ModifierEffect::target_t excluded_targets
+) {
+ // We could test that excluded_targets != ALL_TARGETS, but in practice it's always
+ // called with an explcit/hardcoded value and so won't ever exclude everything.
+ for (modifier_entry_t const& modifier_entry : modifier_sum.modifiers) {
+ add_modifier(
+ *modifier_entry.modifier, modifier_entry.source, modifier_entry.multiplier,
+ modifier_entry.excluded_targets | excluded_targets
+ );
+ }
}
-ModifierSum& ModifierSum::operator+=(ModifierSum const& modifier_sum) {
- add_modifier_sum(modifier_sum);
- return *this;
+void ModifierSum::add_modifier_sum_exclude_source(ModifierSum const& modifier_sum, modifier_source_t const& excluded_source) {
+ for (modifier_entry_t const& modifier_entry : modifier_sum.modifiers) {
+ if (modifier_entry.source != excluded_source) {
+ add_modifier(
+ *modifier_entry.modifier, modifier_entry.source, modifier_entry.multiplier, modifier_entry.excluded_targets
+ );
+ }
+ }
}
// TODO - include value_sum[effect] in result? Early return if lookup in value_sum fails?
-std::vector<std::pair<Modifier const*, fixed_point_t>> ModifierSum::get_contributing_modifiers(
- ModifierEffect const& effect
+
+void ModifierSum::push_contributing_modifiers(
+ ModifierEffect const& effect, std::vector<modifier_entry_t>& contributions
) const {
- std::vector<std::pair<Modifier const*, fixed_point_t>> ret;
+ using enum ModifierEffect::target_t;
- for (auto const& [modifier, multiplier] : modifiers) {
- bool effect_found = false;
- const fixed_point_t value = modifier->get_effect(effect, &effect_found);
+ for (modifier_entry_t const& modifier_entry : modifiers) {
+ if (ModifierEffect::excludes_targets(effect.get_targets(), modifier_entry.excluded_targets)) {
+ bool effect_found = false;
+ const fixed_point_t value = modifier_entry.modifier->get_effect(effect, &effect_found);
- if (effect_found) {
- ret.emplace_back(modifier, value * multiplier);
+ if (effect_found) {
+ contributions.push_back(modifier_entry);
+ }
}
}
+}
+
+std::vector<ModifierSum::modifier_entry_t> ModifierSum::get_contributing_modifiers(ModifierEffect const& effect) const {
+ std::vector<modifier_entry_t> contributions;
+
+ push_contributing_modifiers(effect, contributions);
- return ret;
+ return contributions;
}
diff --git a/src/openvic-simulation/modifier/ModifierSum.hpp b/src/openvic-simulation/modifier/ModifierSum.hpp
index 957ffab..858ca71 100644
--- a/src/openvic-simulation/modifier/ModifierSum.hpp
+++ b/src/openvic-simulation/modifier/ModifierSum.hpp
@@ -1,30 +1,77 @@
#pragma once
-#include "openvic-simulation/modifier/Modifier.hpp"
-#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp"
+#include <variant>
+
+#include "openvic-simulation/modifier/ModifierValue.hpp"
+#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
namespace OpenVic {
+ struct CountryInstance;
+ struct ProvinceInstance;
+ struct Modifier;
+
struct ModifierSum {
+ using modifier_source_t = std::variant<CountryInstance const*, ProvinceInstance const*>;
+
+ static std::string_view source_to_string(modifier_source_t const& source);
+
+ struct modifier_entry_t {
+ Modifier const* modifier;
+ fixed_point_t multiplier;
+ modifier_source_t source;
+ ModifierEffect::target_t excluded_targets;
+
+ constexpr modifier_entry_t(
+ Modifier const* new_modifier,
+ fixed_point_t new_multiplier,
+ modifier_source_t const& new_source,
+ ModifierEffect::target_t new_excluded_targets
+ ) : modifier { new_modifier },
+ multiplier { new_multiplier },
+ source { new_source },
+ excluded_targets { new_excluded_targets } {}
+
+ constexpr bool operator==(modifier_entry_t const& other) const {
+ return modifier == other.modifier
+ && multiplier == other.multiplier
+ && source_to_string(source) == source_to_string(other.source)
+ && excluded_targets == other.excluded_targets;
+ }
+
+ std::string to_string() const;
+ };
+
private:
- fixed_point_map_t<Modifier const*> PROPERTY(modifiers);
+ std::vector<modifier_entry_t> PROPERTY(modifiers);
ModifierValue PROPERTY(value_sum);
public:
ModifierSum() = default;
+ ModifierSum(ModifierSum const&) = default;
ModifierSum(ModifierSum&&) = default;
+ ModifierSum& operator=(ModifierSum const&) = default;
+ ModifierSum& operator=(ModifierSum&&) = default;
void clear();
bool empty();
fixed_point_t get_effect(ModifierEffect const& effect, bool* effect_found = nullptr) const;
+ fixed_point_t get_effect_nullcheck(ModifierEffect const* effect, bool* effect_found = nullptr) const;
bool has_effect(ModifierEffect const& effect) const;
- void add_modifier(Modifier const& modifier, fixed_point_t multiplier = fixed_point_t::_1());
+ void add_modifier(
+ Modifier const& modifier, modifier_source_t const& source, fixed_point_t multiplier = fixed_point_t::_1(),
+ ModifierEffect::target_t excluded_targets = ModifierEffect::target_t::ALL_TARGETS
+ );
+ void add_modifier_nullcheck(
+ Modifier const* modifier, modifier_source_t const& source, fixed_point_t multiplier = fixed_point_t::_1(),
+ ModifierEffect::target_t excluded_targets = ModifierEffect::target_t::ALL_TARGETS
+ );
void add_modifier_sum(ModifierSum const& modifier_sum);
+ void add_modifier_sum_exclude_targets(ModifierSum const& modifier_sum, ModifierEffect::target_t excluded_targets);
+ void add_modifier_sum_exclude_source(ModifierSum const& modifier_sum, modifier_source_t const& excluded_source);
- ModifierSum& operator+=(Modifier const& modifier);
- ModifierSum& operator+=(ModifierSum const& modifier_sum);
-
- std::vector<std::pair<Modifier const*, fixed_point_t>> get_contributing_modifiers(ModifierEffect const& effect) const;
+ void push_contributing_modifiers(ModifierEffect const& effect, std::vector<modifier_entry_t>& contributions) const;
+ std::vector<modifier_entry_t> get_contributing_modifiers(ModifierEffect const& effect) const;
};
}
diff --git a/src/openvic-simulation/modifier/ModifierValue.cpp b/src/openvic-simulation/modifier/ModifierValue.cpp
new file mode 100644
index 0000000..e3e7b0b
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierValue.cpp
@@ -0,0 +1,149 @@
+#include "ModifierValue.hpp"
+
+#include "openvic-simulation/utility/TslHelper.hpp"
+
+using namespace OpenVic;
+
+ModifierValue::ModifierValue() = default;
+ModifierValue::ModifierValue(effect_map_t&& new_values) : values { std::move(new_values) } {}
+ModifierValue::ModifierValue(ModifierValue const&) = default;
+ModifierValue::ModifierValue(ModifierValue&&) = default;
+
+ModifierValue& ModifierValue::operator=(ModifierValue const&) = default;
+ModifierValue& ModifierValue::operator=(ModifierValue&&) = default;
+
+void ModifierValue::trim() {
+ erase_if(values, [](effect_map_t::value_type const& value) -> bool {
+ return value.second == fixed_point_t::_0();
+ });
+}
+
+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* effect_found) const {
+ const effect_map_t::const_iterator it = values.find(&effect);
+ if (it != values.end()) {
+ if (effect_found != nullptr) {
+ *effect_found = true;
+ }
+ return it->second;
+ }
+
+ if (effect_found != nullptr) {
+ *effect_found = false;
+ }
+ return fixed_point_t::_0();
+}
+
+fixed_point_t ModifierValue::get_effect_nullcheck(ModifierEffect const* effect, bool* effect_found) const {
+ if (effect != nullptr) {
+ return get_effect(*effect, effect_found);
+ }
+
+ if (effect_found != nullptr) {
+ *effect_found = false;
+ }
+ return fixed_point_t::_0();
+}
+
+bool ModifierValue::has_effect(ModifierEffect const& effect) const {
+ return values.contains(&effect);
+}
+
+void ModifierValue::set_effect(ModifierEffect const& effect, fixed_point_t value) {
+ values[&effect] = value;
+}
+
+ModifierValue& ModifierValue::operator+=(ModifierValue const& right) {
+ for (effect_map_t::value_type const& value : right.values) {
+ values[value.first] += value.second;
+ }
+ return *this;
+}
+
+ModifierValue ModifierValue::operator+(ModifierValue const& right) const {
+ ModifierValue copy = *this;
+ return copy += right;
+}
+
+ModifierValue ModifierValue::operator-() const {
+ ModifierValue copy = *this;
+ for (auto value : mutable_iterator(copy.values)) {
+ value.second = -value.second;
+ }
+ return copy;
+}
+
+ModifierValue& ModifierValue::operator-=(ModifierValue const& right) {
+ for (effect_map_t::value_type const& value : right.values) {
+ values[value.first] -= value.second;
+ }
+ return *this;
+}
+
+ModifierValue ModifierValue::operator-(ModifierValue const& right) const {
+ ModifierValue copy = *this;
+ return copy -= right;
+}
+
+ModifierValue& ModifierValue::operator*=(fixed_point_t const& right) {
+ for (auto value : mutable_iterator(values)) {
+ value.second *= right;
+ }
+ return *this;
+}
+
+ModifierValue ModifierValue::operator*(fixed_point_t const& right) const {
+ ModifierValue copy = *this;
+ return copy *= right;
+}
+
+void ModifierValue::apply_exclude_targets(ModifierEffect::target_t excluded_targets) {
+ using enum ModifierEffect::target_t;
+
+ // We could test if excluded_targets is NO_TARGETS (and so we do nothing) or ALL_TARGETS (and so we clear everything),
+ // but so long as this is always called with an explicit/hardcoded value then we'll never have either of those cases.
+ erase_if(
+ values,
+ [excluded_targets](effect_map_t::value_type const& value) -> bool {
+ return !ModifierEffect::excludes_targets(value.first->get_targets(), excluded_targets);
+ }
+ );
+}
+
+void ModifierValue::multiply_add_exclude_targets(
+ ModifierValue const& other, fixed_point_t multiplier, ModifierEffect::target_t excluded_targets
+) {
+ using enum ModifierEffect::target_t;
+
+ if (multiplier == fixed_point_t::_1() && excluded_targets == NO_TARGETS) {
+ *this += other;
+ } else if (multiplier != fixed_point_t::_0()) {
+ // We could test that excluded_targets != ALL_TARGETS, but in practice it's always
+ // called with an explcit/hardcoded value and so won't ever exclude everything.
+ for (effect_map_t::value_type const& value : other.values) {
+ if (ModifierEffect::excludes_targets(value.first->get_targets(), excluded_targets)) {
+ values[value.first] += value.second * multiplier;
+ }
+ }
+ }
+}
+
+namespace OpenVic { // so the compiler shuts up
+ std::ostream& operator<<(std::ostream& stream, ModifierValue const& value) {
+ for (ModifierValue::effect_map_t::value_type const& effect : value.values) {
+ stream << effect.first << ": " << effect.second << "\n";
+ }
+ return stream;
+ }
+}
diff --git a/src/openvic-simulation/modifier/ModifierValue.hpp b/src/openvic-simulation/modifier/ModifierValue.hpp
new file mode 100644
index 0000000..6e6a29b
--- /dev/null
+++ b/src/openvic-simulation/modifier/ModifierValue.hpp
@@ -0,0 +1,50 @@
+#pragma once
+
+#include "openvic-simulation/modifier/ModifierEffect.hpp"
+#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp"
+
+namespace OpenVic {
+ struct ModifierValue {
+ friend struct ModifierManager;
+
+ using effect_map_t = fixed_point_map_t<ModifierEffect const*>;
+
+ private:
+ effect_map_t PROPERTY(values);
+
+ public:
+ ModifierValue();
+ ModifierValue(effect_map_t&& new_values);
+ ModifierValue(ModifierValue const&);
+ ModifierValue(ModifierValue&&);
+
+ ModifierValue& operator=(ModifierValue const&);
+ ModifierValue& operator=(ModifierValue&&);
+
+ /* 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* effect_found = nullptr) const;
+ fixed_point_t get_effect_nullcheck(ModifierEffect const* effect, bool* effect_found = nullptr) const;
+ bool has_effect(ModifierEffect const& effect) const;
+ void set_effect(ModifierEffect const& effect, fixed_point_t value);
+
+ ModifierValue& operator+=(ModifierValue const& right);
+ ModifierValue operator+(ModifierValue const& right) const;
+ ModifierValue operator-() const;
+ ModifierValue& operator-=(ModifierValue const& right);
+ ModifierValue operator-(ModifierValue const& right) const;
+ ModifierValue& operator*=(fixed_point_t const& right);
+ ModifierValue operator*(fixed_point_t const& right) const;
+
+ void apply_exclude_targets(ModifierEffect::target_t excluded_targets);
+ void multiply_add_exclude_targets(
+ ModifierValue const& other, fixed_point_t multiplier, ModifierEffect::target_t excluded_targets
+ );
+
+ friend std::ostream& operator<<(std::ostream& stream, ModifierValue const& value);
+ };
+}
diff --git a/src/openvic-simulation/modifier/StaticModifierCache.cpp b/src/openvic-simulation/modifier/StaticModifierCache.cpp
new file mode 100644
index 0000000..b03d827
--- /dev/null
+++ b/src/openvic-simulation/modifier/StaticModifierCache.cpp
@@ -0,0 +1,115 @@
+#include "StaticModifierCache.hpp"
+
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
+using namespace OpenVic;
+
+StaticModifierCache::StaticModifierCache()
+ : // Country modifiers
+ very_easy_player { nullptr },
+ easy_player { nullptr },
+ hard_player { nullptr },
+ very_hard_player { nullptr },
+ very_easy_ai { nullptr },
+ easy_ai { nullptr },
+ hard_ai { nullptr },
+ very_hard_ai { nullptr },
+ base_modifier { nullptr },
+ war { nullptr },
+ peace { nullptr },
+ disarming { nullptr },
+ war_exhaustion { nullptr },
+ infamy { nullptr },
+ debt_default_to { nullptr },
+ bad_debter { nullptr },
+ great_power { nullptr },
+ secondary_power { nullptr },
+ civilised { nullptr },
+ uncivilised { nullptr },
+ literacy { nullptr },
+ plurality { nullptr },
+ generalised_debt_default { nullptr },
+ total_occupation { nullptr },
+ total_blockaded { nullptr },
+ in_bankruptcy { nullptr },
+ // Province modifiers
+ overseas { nullptr },
+ coastal { nullptr },
+ non_coastal { nullptr },
+ coastal_sea { nullptr },
+ sea_zone { nullptr },
+ land_province { nullptr },
+ blockaded { nullptr },
+ no_adjacent_controlled { nullptr },
+ core { nullptr },
+ has_siege { nullptr },
+ occupied { nullptr },
+ nationalism { nullptr },
+ infrastructure { nullptr } {}
+
+bool StaticModifierCache::load_static_modifiers(ModifierManager& modifier_manager) {
+ bool ret = true;
+
+ const auto set_static_modifier = [&modifier_manager, &ret](
+ Modifier const*& modifier, std::string_view name, fixed_point_t multiplier = 1
+ ) -> void {
+ Modifier* mutable_modifier = modifier_manager.static_modifiers.get_item_by_identifier(name);
+
+ if (mutable_modifier != nullptr) {
+ if (multiplier != fixed_point_t::_1()) {
+ *mutable_modifier *= multiplier;
+ }
+
+ modifier = mutable_modifier;
+ } else {
+ Logger::error("Failed to set static modifier: ", name);
+ ret = false;
+ }
+ };
+
+ // Country modifiers
+ set_static_modifier(very_easy_player, "very_easy_player");
+ set_static_modifier(easy_player, "easy_player");
+ set_static_modifier(hard_player, "hard_player");
+ set_static_modifier(very_hard_player, "very_hard_player");
+ set_static_modifier(very_easy_ai, "very_easy_ai");
+ set_static_modifier(easy_ai, "easy_ai");
+ set_static_modifier(hard_ai, "hard_ai");
+ set_static_modifier(very_hard_ai, "very_hard_ai");
+
+ set_static_modifier(base_modifier, "base_values");
+ set_static_modifier(war, "war");
+ set_static_modifier(peace, "peace");
+ set_static_modifier(disarming, "disarming");
+ set_static_modifier(war_exhaustion, "war_exhaustion");
+ set_static_modifier(infamy, "badboy");
+ set_static_modifier(debt_default_to, "debt_default_to");
+ set_static_modifier(bad_debter, "bad_debter");
+ set_static_modifier(great_power, "great_power");
+ set_static_modifier(secondary_power, "second_power");
+ set_static_modifier(civilised, "civ_nation");
+ set_static_modifier(uncivilised, "unciv_nation");
+ set_static_modifier(literacy, "average_literacy");
+ set_static_modifier(plurality, "plurality");
+ set_static_modifier(generalised_debt_default, "generalised_debt_default");
+ set_static_modifier(total_occupation, "total_occupation", 100);
+ set_static_modifier(total_blockaded, "total_blockaded");
+ set_static_modifier(in_bankruptcy, "in_bankrupcy");
+
+ // Province modifiers
+ set_static_modifier(overseas, "overseas");
+ set_static_modifier(coastal, "coastal");
+ set_static_modifier(non_coastal, "non_coastal");
+ set_static_modifier(coastal_sea, "coastal_sea");
+ set_static_modifier(sea_zone, "sea_zone");
+ set_static_modifier(land_province, "land_province");
+ set_static_modifier(blockaded, "blockaded");
+ set_static_modifier(no_adjacent_controlled, "no_adjacent_controlled");
+ set_static_modifier(core, "core");
+ set_static_modifier(has_siege, "has_siege");
+ set_static_modifier(occupied, "occupied");
+ set_static_modifier(nationalism, "nationalism");
+ set_static_modifier(infrastructure, "infrastructure");
+
+ return ret;
+}
diff --git a/src/openvic-simulation/modifier/StaticModifierCache.hpp b/src/openvic-simulation/modifier/StaticModifierCache.hpp
new file mode 100644
index 0000000..4b17b82
--- /dev/null
+++ b/src/openvic-simulation/modifier/StaticModifierCache.hpp
@@ -0,0 +1,64 @@
+#pragma once
+
+#include "openvic-simulation/utility/Getters.hpp"
+
+namespace OpenVic {
+ struct Modifier;
+ struct ModifierManager;
+
+ struct StaticModifierCache {
+ friend struct ModifierManager;
+
+ private:
+ // Country modifiers
+ Modifier const* PROPERTY(very_easy_player);
+ Modifier const* PROPERTY(easy_player);
+ Modifier const* PROPERTY(hard_player);
+ Modifier const* PROPERTY(very_hard_player);
+ Modifier const* PROPERTY(very_easy_ai);
+ Modifier const* PROPERTY(easy_ai);
+ Modifier const* PROPERTY(hard_ai);
+ Modifier const* PROPERTY(very_hard_ai);
+
+ Modifier const* PROPERTY(base_modifier);
+ Modifier const* PROPERTY(war);
+ Modifier const* PROPERTY(peace);
+ Modifier const* PROPERTY(disarming);
+ Modifier const* PROPERTY(war_exhaustion);
+ Modifier const* PROPERTY(infamy);
+ Modifier const* PROPERTY(debt_default_to);
+ Modifier const* PROPERTY(bad_debter);
+ Modifier const* PROPERTY(great_power);
+ Modifier const* PROPERTY(secondary_power);
+ Modifier const* PROPERTY(civilised);
+ Modifier const* PROPERTY(uncivilised);
+ Modifier const* PROPERTY(literacy);
+ Modifier const* PROPERTY(plurality);
+ Modifier const* PROPERTY(generalised_debt_default);
+ Modifier const* PROPERTY(total_occupation);
+ Modifier const* PROPERTY(total_blockaded);
+ Modifier const* PROPERTY(in_bankruptcy);
+
+ // Province modifiers
+ Modifier const* PROPERTY(overseas);
+ Modifier const* PROPERTY(coastal);
+ Modifier const* PROPERTY(non_coastal);
+ Modifier const* PROPERTY(coastal_sea);
+ Modifier const* PROPERTY(sea_zone);
+ Modifier const* PROPERTY(land_province);
+ Modifier const* PROPERTY(blockaded);
+ Modifier const* PROPERTY(no_adjacent_controlled);
+ Modifier const* PROPERTY(core);
+ Modifier const* PROPERTY(has_siege);
+ Modifier const* PROPERTY(occupied);
+ Modifier const* PROPERTY(nationalism);
+ Modifier const* PROPERTY(infrastructure);
+
+ StaticModifierCache();
+
+ bool load_static_modifiers(ModifierManager& registry);
+
+ public:
+ StaticModifierCache(StaticModifierCache&&) = default;
+ };
+}
diff --git a/src/openvic-simulation/politics/Issue.cpp b/src/openvic-simulation/politics/Issue.cpp
index 3174e23..1faa345 100644
--- a/src/openvic-simulation/politics/Issue.cpp
+++ b/src/openvic-simulation/politics/Issue.cpp
@@ -1,5 +1,7 @@
#include "Issue.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -176,11 +178,15 @@ bool IssueManager::_load_issue(
ModifierManager const& modifier_manager, RuleManager const& rule_manager, std::string_view identifier,
IssueGroup const* group, ast::NodeCPtr node
) {
+ using enum Modifier::modifier_type_t;
+
ModifierValue values;
RuleSet rules;
bool jingoism = false;
- 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),
+ ISSUE,
"is_jingoism", ZERO_OR_ONE, expect_bool(assign_variable_callback(jingoism)),
"rules", ZERO_OR_ONE, rule_manager.expect_rule_set(move_variable_callback(rules))
)(node);
@@ -213,6 +219,8 @@ bool IssueManager::_load_reform(
ModifierManager const& modifier_manager, RuleManager const& rule_manager, size_t ordinal, std::string_view identifier,
ReformGroup const* group, ast::NodeCPtr node
) {
+ using enum Modifier::modifier_type_t;
+
ModifierValue values;
RuleSet rules;
fixed_point_t administrative_multiplier = 0;
@@ -223,6 +231,7 @@ bool IssueManager::_load_reform(
bool ret = modifier_manager.expect_modifier_value_and_keys(
move_variable_callback(values),
+ REFORM,
"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(),
diff --git a/src/openvic-simulation/politics/NationalFocus.cpp b/src/openvic-simulation/politics/NationalFocus.cpp
index 4887ed6..4d1546c 100644
--- a/src/openvic-simulation/politics/NationalFocus.cpp
+++ b/src/openvic-simulation/politics/NationalFocus.cpp
@@ -1,6 +1,7 @@
#include "NationalFocus.hpp"
#include "openvic-simulation/economy/GoodDefinition.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/pop/Pop.hpp"
@@ -116,6 +117,8 @@ bool NationalFocusManager::load_national_foci_file(
[this, &group, &pop_manager, &ideology_manager, &good_definition_manager, &modifier_manager](
std::string_view identifier, ast::NodeCPtr node
) -> bool {
+ using enum Modifier::modifier_type_t;
+
uint8_t icon = 0;
bool has_flashpoint = false, own_provinces = true, outliner_show_as_percent = false;
fixed_point_t flashpoint_tension = 0;
@@ -131,6 +134,7 @@ bool NationalFocusManager::load_national_foci_file(
bool ret = modifier_manager.expect_modifier_value_and_keys_and_default(
move_variable_callback(modifiers),
+ NATIONAL_FOCUS,
[&good_definition_manager, &encourage_goods, &pop_manager, &encourage_pop_types](
std::string_view key, ast::NodeCPtr value
) -> bool {
diff --git a/src/openvic-simulation/politics/NationalValue.cpp b/src/openvic-simulation/politics/NationalValue.cpp
index e9f7f2a..16f723b 100644
--- a/src/openvic-simulation/politics/NationalValue.cpp
+++ b/src/openvic-simulation/politics/NationalValue.cpp
@@ -1,5 +1,7 @@
#include "NationalValue.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -19,11 +21,14 @@ bool NationalValueManager::load_national_values_file(ModifierManager const& modi
bool ret = expect_dictionary_reserve_length(
national_values,
[this, &modifier_manager](std::string_view national_value_identifier, ast::NodeCPtr value) -> bool {
+ using enum Modifier::modifier_type_t;
+
ModifierValue modifiers;
- bool ret = modifier_manager.expect_modifier_value(move_variable_callback(modifiers))(value);
+ bool ret = modifier_manager.expect_modifier_value(move_variable_callback(modifiers), NATIONAL_VALUE)(value);
ret &= add_national_value(national_value_identifier, std::move(modifiers));
+
return ret;
}
)(root);
diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp
index fef61c0..9bba1b9 100644
--- a/src/openvic-simulation/politics/Rebel.cpp
+++ b/src/openvic-simulation/politics/Rebel.cpp
@@ -2,6 +2,8 @@
#include <string_view>
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -175,6 +177,7 @@ bool RebelManager::load_rebels_file(
bool RebelManager::generate_modifiers(ModifierManager& modifier_manager) const {
using enum ModifierEffect::format_t;
+ using enum ModifierEffect::target_t;
bool ret = true;
@@ -184,12 +187,19 @@ bool RebelManager::generate_modifiers(ModifierManager& modifier_manager) const {
ret &= modifier_manager.register_complex_modifier(identifier);
ret &= modifier_manager.add_modifier_effect(
- ModifierManager::get_flat_identifier(identifier, "all"), is_positive_good, PROPORTION_DECIMAL, "TECH_REBEL_ORG_GAIN"
+ modifier_manager.modifier_effect_cache.rebel_org_gain_all, ModifierManager::get_flat_identifier(identifier, "all"),
+ is_positive_good, PROPORTION_DECIMAL, COUNTRY, "TECH_REBEL_ORG_GAIN"
);
+ IndexedMap<RebelType, ModifierEffect const*>& rebel_org_gain_effects =
+ modifier_manager.modifier_effect_cache.rebel_org_gain_effects;
+
+ rebel_org_gain_effects.set_keys(&get_rebel_types());
+
for (RebelType const& rebel_type : get_rebel_types()) {
ret &= modifier_manager.add_modifier_effect(
- ModifierManager::get_flat_identifier(identifier, rebel_type.get_identifier()), is_positive_good, PROPORTION_DECIMAL,
+ rebel_org_gain_effects[rebel_type], ModifierManager::get_flat_identifier(identifier, rebel_type.get_identifier()),
+ is_positive_good, PROPORTION_DECIMAL, COUNTRY,
StringUtils::append_string_views("$", rebel_type.get_identifier(), "_title$ $TECH_REBEL_ORG_GAIN$")
);
}
diff --git a/src/openvic-simulation/politics/Rebel.hpp b/src/openvic-simulation/politics/Rebel.hpp
index fc82c84..b47bcbb 100644
--- a/src/openvic-simulation/politics/Rebel.hpp
+++ b/src/openvic-simulation/politics/Rebel.hpp
@@ -2,7 +2,6 @@
#include <cstdint>
-#include "openvic-simulation/modifier/Modifier.hpp"
#include "openvic-simulation/politics/Government.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/scripts/ConditionalWeight.hpp"
@@ -79,6 +78,8 @@ namespace OpenVic {
RebelType(RebelType&&) = default;
};
+ struct ModifierManager;
+
struct RebelManager {
private:
IdentifierRegistry<RebelType> IDENTIFIER_REGISTRY(rebel_type);
diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp
index dd829d9..885903a 100644
--- a/src/openvic-simulation/pop/Pop.cpp
+++ b/src/openvic-simulation/pop/Pop.cpp
@@ -5,6 +5,7 @@
#include "openvic-simulation/map/ProvinceInstance.hpp"
#include "openvic-simulation/military/UnitType.hpp"
#include "openvic-simulation/misc/Define.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
#include "openvic-simulation/politics/Ideology.hpp"
#include "openvic-simulation/politics/Issue.hpp"
#include "openvic-simulation/politics/Rebel.hpp"
@@ -654,24 +655,33 @@ bool PopManager::load_pop_bases_into_vector(
bool PopManager::generate_modifiers(ModifierManager& modifier_manager) const {
using enum ModifierEffect::format_t;
+ using enum ModifierEffect::target_t;
+
+ IndexedMap<Strata, ModifierEffectCache::strata_effects_t>& strata_effects =
+ modifier_manager.modifier_effect_cache.strata_effects;
+
+ strata_effects.set_keys(&get_stratas());
bool ret = true;
for (Strata const& strata : get_stratas()) {
const auto strata_modifier = [&modifier_manager, &ret, &strata](
- std::string_view suffix, bool is_positive_good
+ ModifierEffect const*& effect_cache, std::string_view suffix, bool is_positive_good
) -> void {
ret &= modifier_manager.add_modifier_effect(
- StringUtils::append_string_views(strata.get_identifier(), suffix), is_positive_good
+ effect_cache, StringUtils::append_string_views(strata.get_identifier(), suffix), is_positive_good,
+ PROPORTION_DECIMAL, COUNTRY
);
};
- strata_modifier("_income_modifier", true);
- strata_modifier("_vote", true);
+ ModifierEffectCache::strata_effects_t& this_strata_effects = strata_effects[strata];
+
+ strata_modifier(this_strata_effects.income_modifier, "_income_modifier", true); // Has no effect in game
+ strata_modifier(this_strata_effects.vote, "_vote", true);
- strata_modifier("_life_needs", false);
- strata_modifier("_everyday_needs", false);
- strata_modifier("_luxury_needs", false);
+ strata_modifier(this_strata_effects.life_needs, "_life_needs", false);
+ strata_modifier(this_strata_effects.everyday_needs, "_everyday_needs", false);
+ strata_modifier(this_strata_effects.luxury_needs, "_luxury_needs", false);
}
return ret;
diff --git a/src/openvic-simulation/research/Invention.cpp b/src/openvic-simulation/research/Invention.cpp
index 299cefb..808dad4 100644
--- a/src/openvic-simulation/research/Invention.cpp
+++ b/src/openvic-simulation/research/Invention.cpp
@@ -3,6 +3,7 @@
#include "openvic-simulation/economy/BuildingType.hpp"
#include "openvic-simulation/map/Crime.hpp"
#include "openvic-simulation/military/UnitType.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -54,12 +55,16 @@ bool InventionManager::add_invention(
}
bool InventionManager::load_inventions_file(
- ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, BuildingTypeManager const& building_type_manager,
- CrimeManager const& crime_manager, ast::NodeCPtr root
+ ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager,
+ BuildingTypeManager const& building_type_manager, CrimeManager const& crime_manager, ast::NodeCPtr root
) {
return expect_dictionary_reserve_length(
inventions, [this, &modifier_manager, &unit_type_manager, &building_type_manager, &crime_manager](
- std::string_view identifier, ast::NodeCPtr value) -> bool {
+ std::string_view identifier, ast::NodeCPtr value
+ ) -> bool {
+ using enum Modifier::modifier_type_t;
+
+ // TODO - use the same variable for all modifiers rather than combining them at the end?
ModifierValue loose_modifiers;
ModifierValue modifiers;
@@ -74,15 +79,19 @@ bool InventionManager::load_inventions_file(
ConditionScript limit { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE };
ConditionalWeight chance { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE };
- bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(loose_modifiers),
+ bool ret = modifier_manager.expect_modifier_value_and_keys(
+ move_variable_callback(loose_modifiers),
+ INVENTION,
"news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)),
"limit", ONE_EXACTLY, limit.expect_script(),
"chance", ONE_EXACTLY, chance.expect_conditional_weight(ConditionalWeight::BASE),
"effect", ZERO_OR_ONE, modifier_manager.expect_modifier_value_and_keys(
move_variable_callback(modifiers),
+ INVENTION,
"gas_attack", ZERO_OR_ONE, expect_bool(assign_variable_callback(unlock_gas_attack)),
"gas_defence", ZERO_OR_ONE, expect_bool(assign_variable_callback(unlock_gas_defence)),
- "activate_unit", ZERO_OR_MORE, unit_type_manager.expect_unit_type_identifier(set_callback_pointer(activated_units)),
+ "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)
),
diff --git a/src/openvic-simulation/research/Technology.cpp b/src/openvic-simulation/research/Technology.cpp
index a5256f3..a770a40 100644
--- a/src/openvic-simulation/research/Technology.cpp
+++ b/src/openvic-simulation/research/Technology.cpp
@@ -1,5 +1,7 @@
#include "Technology.hpp"
+#include "openvic-simulation/modifier/ModifierManager.hpp"
+
using namespace OpenVic;
using namespace OpenVic::NodeTools;
@@ -140,8 +142,13 @@ bool TechnologyManager::load_technology_file_schools(
const bool ret = expect_dictionary_reserve_length(
technology_schools,
[this, &modifier_manager](std::string_view school_key, ast::NodeCPtr school_value) -> bool {
+ using enum Modifier::modifier_type_t;
+
ModifierValue modifiers;
- bool ret = modifier_manager.expect_modifier_value(move_variable_callback(modifiers))(school_value);
+
+ bool ret = modifier_manager.expect_modifier_value(
+ move_variable_callback(modifiers), TECH_SCHOOL
+ )(school_value);
ret &= add_technology_school(school_key, std::move(modifiers));
@@ -163,6 +170,8 @@ bool TechnologyManager::load_technologies_file(
return expect_dictionary_reserve_length(technologies, [this, &modifier_manager, &unit_type_manager, &building_type_manager](
std::string_view tech_key, ast::NodeCPtr tech_value
) -> bool {
+ using enum Modifier::modifier_type_t;
+
ModifierValue modifiers;
TechnologyArea const* area = nullptr;
Date::year_t year = 0;
@@ -175,6 +184,7 @@ bool TechnologyManager::load_technologies_file(
bool ret = modifier_manager.expect_modifier_value_and_keys(
move_variable_callback(modifiers),
+ TECHNOLOGY,
"area", ONE_EXACTLY, expect_technology_area_identifier(assign_variable_callback_pointer(area)),
"year", ONE_EXACTLY, expect_uint(assign_variable_callback(year)),
"cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(cost)),
@@ -197,13 +207,21 @@ bool TechnologyManager::load_technologies_file(
bool TechnologyManager::generate_modifiers(ModifierManager& modifier_manager) const {
using enum ModifierEffect::format_t;
+ using enum ModifierEffect::target_t;
+
+ IndexedMap<TechnologyFolder, ModifierEffect const*>& research_bonus_effects =
+ modifier_manager.modifier_effect_cache.research_bonus_effects;
+
+ research_bonus_effects.set_keys(&get_technology_folders());
bool ret = true;
for (TechnologyFolder const& folder : get_technology_folders()) {
const std::string modifier_identifier = StringUtils::append_string_views(folder.get_identifier(), "_research_bonus");
- ret &= modifier_manager.add_modifier_effect(modifier_identifier, true, PROPORTION_DECIMAL, modifier_identifier);
+ ret &= modifier_manager.add_modifier_effect(
+ research_bonus_effects[folder], modifier_identifier, true, PROPORTION_DECIMAL, COUNTRY, modifier_identifier
+ );
}
return ret;
diff --git a/src/openvic-simulation/utility/NumberUtils.hpp b/src/openvic-simulation/utility/NumberUtils.hpp
index d814019..8656f27 100644
--- a/src/openvic-simulation/utility/NumberUtils.hpp
+++ b/src/openvic-simulation/utility/NumberUtils.hpp
@@ -49,4 +49,8 @@ namespace OpenVic::NumberUtils {
return c;
}
+
+ constexpr bool is_power_of_two(uint64_t n) {
+ return n > 0 && (n & (n - 1)) == 0;
+ }
}