diff options
Diffstat (limited to 'src/openvic-simulation/modifier')
-rw-r--r-- | src/openvic-simulation/modifier/Modifier.cpp | 655 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/Modifier.hpp | 189 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierEffect.cpp | 49 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierEffect.hpp | 63 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierEffectCache.cpp | 231 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierEffectCache.hpp | 307 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierManager.cpp | 866 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierManager.hpp | 144 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierSum.cpp | 103 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierSum.hpp | 63 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierValue.cpp | 149 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/ModifierValue.hpp | 50 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/StaticModifierCache.cpp | 115 | ||||
-rw-r--r-- | src/openvic-simulation/modifier/StaticModifierCache.hpp | 64 |
14 files changed, 2212 insertions, 836 deletions
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; + }; +} |