diff options
author | hop311 <hop3114@gmail.com> | 2024-11-01 22:38:23 +0100 |
---|---|---|
committer | hop311 <hop3114@gmail.com> | 2024-11-01 22:38:23 +0100 |
commit | fa0235d4eac01816a4832249f28ccadfb6dd6e40 (patch) | |
tree | b1d3e6bb4b3a0accc42dd8e0038e51b134ad99ae | |
parent | 8defcd5daa1acd2c61aa1cd0a26478d472fed9b0 (diff) |
Create basic condition script parsing and execution framework
-rw-r--r-- | src/openvic-simulation/scripts/Condition.cpp | 811 | ||||
-rw-r--r-- | src/openvic-simulation/scripts/Condition.hpp | 310 | ||||
-rw-r--r-- | src/openvic-simulation/scripts/ConditionScript.cpp | 10 | ||||
-rw-r--r-- | src/openvic-simulation/scripts/ConditionScript.hpp | 7 | ||||
-rw-r--r-- | src/openvic-simulation/scripts/ConditionalWeight.cpp | 42 | ||||
-rw-r--r-- | src/openvic-simulation/scripts/ConditionalWeight.hpp | 7 |
6 files changed, 314 insertions, 873 deletions
diff --git a/src/openvic-simulation/scripts/Condition.cpp b/src/openvic-simulation/scripts/Condition.cpp index bb8b662..a58ee40 100644 --- a/src/openvic-simulation/scripts/Condition.cpp +++ b/src/openvic-simulation/scripts/Condition.cpp @@ -2,696 +2,93 @@ #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/DefinitionManager.hpp" +#include "openvic-simulation/InstanceManager.hpp" using namespace OpenVic; using namespace OpenVic::NodeTools; -using enum value_type_t; -using enum scope_type_t; -using enum identifier_type_t; +using no_argument_t = ConditionNode::no_argument_t; +using this_argument_t = ConditionNode::this_argument_t; +using from_argument_t = ConditionNode::from_argument_t; +using integer_t = ConditionNode::integer_t; +using argument_t = ConditionNode::argument_t; -Condition::Condition( - std::string_view new_identifier, value_type_t new_value_type, scope_type_t new_scope, - scope_type_t new_scope_change, identifier_type_t new_key_identifier_type, - identifier_type_t new_value_identifier_type -) : HasIdentifier { new_identifier }, value_type { new_value_type }, scope { new_scope }, - scope_change { new_scope_change }, key_identifier_type { new_key_identifier_type }, - value_identifier_type { new_value_identifier_type } {} +using no_scope_t = ConditionNode::no_scope_t; +using scope_t = ConditionNode::scope_t; + +static constexpr std::string_view THIS_KEYWORD = "THIS"; +static constexpr std::string_view FROM_KEYWORD = "FROM"; ConditionNode::ConditionNode( - Condition const* new_condition, value_t&& new_value, bool new_valid, - HasIdentifier const* new_condition_key_item, - HasIdentifier const* new_condition_value_item -) : condition { new_condition }, value { std::move(new_value) }, valid { new_valid }, - condition_key_item { new_condition_key_item }, condition_value_item { new_condition_key_item } {} + Condition const* new_condition, + argument_t&& new_argument +) : condition { new_condition }, + argument { std::move(new_argument) } {} + +bool ConditionNode::execute( + InstanceManager const& instance_manager, scope_t const& current_scope, scope_t const& this_scope, + scope_t const& from_scope +) const { + if (condition == nullptr) { + Logger::error("ConditionNode has no condition!"); + return false; + } + + return condition->get_execute_callback()( + instance_manager, current_scope, this_scope, from_scope, argument + ); +} + +Condition::Condition( + std::string_view new_identifier, + parse_callback_t&& new_parse_callback, + execute_callback_t&& new_execute_callback +) : HasIdentifier { new_identifier }, + parse_callback { std::move(new_parse_callback) }, + execute_callback { std::move(new_execute_callback) } {} bool ConditionManager::add_condition( - std::string_view identifier, value_type_t value_type, scope_type_t scope, scope_type_t scope_change, - identifier_type_t key_identifier_type, identifier_type_t value_identifier_type + std::string_view identifier, + Condition::parse_callback_t&& parse_callback, + Condition::execute_callback_t&& execute_callback ) { if (identifier.empty()) { Logger::error("Invalid condition identifier - empty!"); return false; } - if (value_type == NO_TYPE || value_type > MAX_VALUE) { - Logger::error("Condition ", identifier, " has invalid value type: ", static_cast<uint64_t>(value_type)); - return false; - } - if (scope == NO_SCOPE || scope > MAX_SCOPE) { - Logger::error("Condition ", identifier, " has invalid scope: ", static_cast<uint64_t>(scope)); + if (parse_callback == nullptr) { + Logger::error("Condition ", identifier, " has no parse callback!"); return false; } - if (share_value_type(value_type, IDENTIFIER) && value_identifier_type == NO_IDENTIFIER) { - Logger::error("Condition ", identifier, " has no identifier type!"); + if (execute_callback == nullptr) { + Logger::error("Condition ", identifier, " has no execute callback!"); return false; } - // don't perform the check for complex types - if (!share_value_type(value_type, COMPLEX)) { - if (!share_value_type(value_type, IDENTIFIER) && value_identifier_type != NO_IDENTIFIER) { - Logger::warning("Condition ", identifier, " specified an identifier type, but doesn't have an identifier!"); - } - } - return conditions.add_item({ identifier, - value_type, - scope, - scope_change, - key_identifier_type, - value_identifier_type + std::move(parse_callback), + std::move(execute_callback) }); } -bool ConditionManager::setup_conditions(DefinitionManager const& definition_manager) { - bool ret = true; - - /* Special Scopes */ - ret &= add_condition("THIS", GROUP, COUNTRY, THIS); - ret &= add_condition("FROM", GROUP, COUNTRY, FROM); - ret &= add_condition("independence", GROUP, COUNTRY, COUNTRY); //only from rebels! - - /* Trigger Country Scopes */ - ret &= add_condition("all_core", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("any_core", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("any_greater_power", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("any_neighbor_country", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("any_owned_province", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("any_pop", GROUP, COUNTRY, POP); - ret &= add_condition("any_sphere_member", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("any_state", GROUP, COUNTRY, STATE); - ret &= add_condition("any_substate", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("capital_scope", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("country", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("cultural_union", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("overlord", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("owner", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("sphere_owner", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("war_countries", GROUP, COUNTRY, COUNTRY); - - /* Trigger State Scopes */ - ret &= add_condition("any_neighbor_province", GROUP, STATE, PROVINCE); - - /* Trigger Province Scopes */ - ret &= add_condition("controller", GROUP, PROVINCE, COUNTRY); - ret &= add_condition("state_scope", GROUP, PROVINCE, STATE); - - /* Trigger Pop Scopes */ - ret &= add_condition("location", GROUP, POP, PROVINCE); - - /* Special Conditions */ - ret &= add_condition("AND", GROUP, COUNTRY); - ret &= add_condition("OR", GROUP, COUNTRY); - ret &= add_condition("NOT", GROUP, COUNTRY); - - /* Global Conditions */ - ret &= add_condition("year", INTEGER, COUNTRY); - ret &= add_condition("month", INTEGER, COUNTRY); - ret &= add_condition("has_global_flag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, GLOBAL_FLAG); - ret &= add_condition("is_canal_enabled", INTEGER, COUNTRY); - ret &= add_condition("always", BOOLEAN, COUNTRY); - ret &= add_condition("world_wars_enabled", BOOLEAN, COUNTRY); - - /* Country Scope Conditions */ - ret &= add_condition("administration_spending", REAL, COUNTRY); - ret &= add_condition("ai", BOOLEAN, COUNTRY); - ret &= add_condition("alliance_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("average_consciousness", REAL, COUNTRY); - ret &= add_condition("average_militancy", REAL, COUNTRY); - ret &= add_condition("badboy", REAL, COUNTRY); - ret &= add_condition("big_producer", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("blockade", REAL, COUNTRY); - ret &= add_condition("brigades_compare", REAL, COUNTRY); - ret &= add_condition("can_build_factory_in_capital_state", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, BUILDING); - ret &= add_condition("can_build_fort_in_capital", COMPLEX, COUNTRY); - ret &= add_condition("can_build_railway_in_capital", COMPLEX, COUNTRY); - ret &= add_condition("can_nationalize", BOOLEAN, COUNTRY); - ret &= add_condition("can_create_vassals", BOOLEAN, COUNTRY); - ret &= add_condition("capital", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("casus_belli", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("check_variable", COMPLEX, COUNTRY, NO_SCOPE, NO_IDENTIFIER, VARIABLE); - ret &= add_condition("citizenship_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, ISSUE); - ret &= add_condition("civilization_progress", REAL, COUNTRY); - ret &= add_condition("civilized", BOOLEAN, COUNTRY); - ret &= add_condition("colonial_nation", BOOLEAN, COUNTRY); - ret &= add_condition("consciousness", REAL, COUNTRY); - ret &= add_condition("constructing_cb_progress", REAL, COUNTRY); - ret &= add_condition("constructing_cb_type", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CASUS_BELLI); - ret &= add_condition("continent", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CONTINENT); - ret &= add_condition("controls", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("crime_fighting", REAL, COUNTRY); - ret &= add_condition("crime_higher_than_education", BOOLEAN, COUNTRY); - ret &= add_condition("crisis_exist", BOOLEAN, COUNTRY); - ret &= add_condition("culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("culture_has_union_tag", BOOLEAN, COUNTRY); - ret &= add_condition("diplomatic_influence", COMPLEX, COUNTRY); - ret &= add_condition("economic_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, ISSUE); - ret &= add_condition("education_spending", REAL, COUNTRY); - ret &= add_condition("election", BOOLEAN, COUNTRY); - ret &= add_condition("exists", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("government", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, GOVERNMENT_TYPE); - ret &= add_condition("great_wars_enabled", BOOLEAN, COUNTRY); - ret &= add_condition("have_core_in", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("has_country_flag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_FLAG); - ret &= add_condition("has_country_modifier", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_EVENT_MODIFIER); - ret &= add_condition("has_cultural_sphere", BOOLEAN, COUNTRY); - ret &= add_condition("has_leader", STRING, COUNTRY); - ret &= add_condition("has_pop_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("has_pop_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); - ret &= add_condition("has_pop_type", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); - ret &= add_condition("has_recently_lost_war", BOOLEAN, COUNTRY); - ret &= add_condition("has_unclaimed_cores", BOOLEAN, COUNTRY); - ret &= add_condition("ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("industrial_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("in_sphere", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("in_default", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("invention", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, INVENTION); - ret &= add_condition("involved_in_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_claim_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_colonial_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_cultural_union", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_disarmed", BOOLEAN, COUNTRY); - ret &= add_condition("is_greater_power", BOOLEAN, COUNTRY); - ret &= add_condition("is_colonial", BOOLEAN, STATE); - ret &= add_condition("is_core", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG | PROVINCE_ID); - ret &= add_condition("is_culture_group", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG | CULTURE_GROUP); - ret &= add_condition("is_ideology_enabled", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("is_independant", BOOLEAN, COUNTRY); //paradox typo - ret &= add_condition("is_liberation_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_mobilised", BOOLEAN, COUNTRY); - ret &= add_condition("is_next_reform", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, REFORM); - ret &= add_condition("is_our_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_possible_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_releasable_vassal", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_secondary_power", BOOLEAN, COUNTRY); - ret &= add_condition("is_sphere_leader_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_substate", BOOLEAN, COUNTRY); - ret &= add_condition("is_vassal", BOOLEAN, COUNTRY); - ret &= add_condition("literacy", REAL, COUNTRY); - ret &= add_condition("lost_national", REAL, COUNTRY); - ret &= add_condition("middle_strata_militancy", REAL, COUNTRY); - ret &= add_condition("middle_strata_everyday_needs", REAL, COUNTRY); - ret &= add_condition("middle_strata_life_needs", REAL, COUNTRY); - ret &= add_condition("middle_strata_luxury_needs", REAL, COUNTRY); - ret &= add_condition("middle_tax", REAL, COUNTRY); - ret &= add_condition("military_access", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("military_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("militancy", REAL, COUNTRY); - ret &= add_condition("military_spending", REAL, COUNTRY); - ret &= add_condition("money", REAL, COUNTRY); - ret &= add_condition("nationalvalue", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, NATIONAL_VALUE); - ret &= add_condition("national_provinces_occupied", REAL, COUNTRY); - ret &= add_condition("neighbour", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("num_of_allies", INTEGER, COUNTRY); - ret &= add_condition("num_of_cities", INTEGER, COUNTRY); - ret &= add_condition("num_of_ports", INTEGER, COUNTRY); - ret &= add_condition("num_of_revolts", INTEGER, COUNTRY); - ret &= add_condition("number_of_states", INTEGER, COUNTRY); - ret &= add_condition("num_of_substates", INTEGER, COUNTRY); - ret &= add_condition("num_of_vassals", INTEGER, COUNTRY); - ret &= add_condition("num_of_vassals_no_substates", INTEGER, COUNTRY); - ret &= add_condition("owns", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("part_of_sphere", BOOLEAN, COUNTRY); - ret &= add_condition("plurality", REAL, COUNTRY); - ret &= add_condition("political_movement_strength", REAL, COUNTRY); - ret &= add_condition("political_reform_want", REAL, COUNTRY); - ret &= add_condition("pop_majority_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("pop_majority_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("pop_majority_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); - ret &= add_condition("pop_militancy", REAL, COUNTRY); - ret &= add_condition("poor_strata_militancy", REAL, COUNTRY); - ret &= add_condition("poor_strata_everyday_needs", REAL, COUNTRY); - ret &= add_condition("poor_strata_life_needs", REAL, COUNTRY); - ret &= add_condition("poor_strata_luxury_needs", REAL, COUNTRY); - ret &= add_condition("poor_tax", REAL, COUNTRY); - ret &= add_condition("prestige", REAL, COUNTRY); - ret &= add_condition("primary_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("accepted_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("produces", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("rank", INTEGER, COUNTRY); - ret &= add_condition("rebel_power_fraction", REAL, COUNTRY); - ret &= add_condition("recruited_percentage", REAL, COUNTRY); - ret &= add_condition("relation", COMPLEX, COUNTRY); - ret &= add_condition("religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); - ret &= add_condition("religious_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, ISSUE); - ret &= add_condition("revanchism", REAL, COUNTRY); - ret &= add_condition("revolt_percentage", REAL, COUNTRY); - ret &= add_condition("rich_strata_militancy", REAL, COUNTRY); - ret &= add_condition("rich_strata_everyday_needs", REAL, COUNTRY); - ret &= add_condition("rich_strata_life_needs", REAL, COUNTRY); - ret &= add_condition("rich_strata_luxury_needs", REAL, COUNTRY); - ret &= add_condition("rich_tax", REAL, COUNTRY); - ret &= add_condition("rich_tax_above_poor", BOOLEAN, COUNTRY); - ret &= add_condition("ruling_party", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("ruling_party_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("social_movement_strength", REAL, COUNTRY); - ret &= add_condition("social_reform_want", REAL, COUNTRY); - ret &= add_condition("social_spending", REAL, COUNTRY); - ret &= add_condition("stronger_army_than", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("substate_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("tag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("tech_school", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TECH_SCHOOL); - ret &= add_condition("this_culture_union", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE_UNION); - ret &= add_condition("total_amount_of_divisions", INTEGER, COUNTRY); - ret &= add_condition("total_amount_of_ships", INTEGER, COUNTRY); - ret &= add_condition("total_defensives", INTEGER, COUNTRY); - ret &= add_condition("total_num_of_ports", INTEGER, COUNTRY); - ret &= add_condition("total_of_ours_sunk", INTEGER, COUNTRY); - ret &= add_condition("total_pops", INTEGER, COUNTRY); - ret &= add_condition("total_sea_battles", INTEGER, COUNTRY); - ret &= add_condition("total_sunk_by_us", INTEGER, COUNTRY); - ret &= add_condition("trade_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, ISSUE); - ret &= add_condition("treasury", REAL, COUNTRY); - ret &= add_condition("truce_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("unemployment", REAL, COUNTRY); - ret &= add_condition("unit_has_leader", BOOLEAN, COUNTRY); - ret &= add_condition("unit_in_battle", BOOLEAN, COUNTRY); - ret &= add_condition("upper_house", COMPLEX, COUNTRY); - ret &= add_condition("vassal_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("war", BOOLEAN, COUNTRY); - ret &= add_condition("war_exhaustion", REAL, COUNTRY); - ret &= add_condition("war_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, ISSUE); - ret &= add_condition("war_score", REAL, COUNTRY); - ret &= add_condition("war_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - - /* State Scope Conditions */ - ret &= add_condition("controlled_by", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("empty", BOOLEAN, STATE); - ret &= add_condition("flashpoint_tension", REAL, STATE); - ret &= add_condition("has_building", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, BUILDING); - ret &= add_condition("has_factories", BOOLEAN, STATE); - ret &= add_condition("has_flashpoint", BOOLEAN, STATE); - ret &= add_condition("is_slave", BOOLEAN, STATE); - ret &= add_condition("owned_by", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("trade_goods_in_state", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("work_available", COMPLEX, STATE); +ConditionManager::ConditionManager() : root_condition { nullptr } {} - /* Province Scope Conditions */ - ret &= add_condition("can_build_factory", BOOLEAN, PROVINCE); - ret &= add_condition("controlled_by_rebels", BOOLEAN, PROVINCE); - ret &= add_condition("country_units_in_province", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("country_units_in_state", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("has_crime", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, CRIME); - ret &= add_condition("has_culture_core", BOOLEAN, PROVINCE); - ret &= add_condition("has_empty_adjacent_province", BOOLEAN, PROVINCE); - ret &= add_condition("has_empty_adjacent_state", BOOLEAN, PROVINCE); - ret &= add_condition("has_national_minority", BOOLEAN, PROVINCE); - ret &= add_condition("has_province_flag", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_FLAG); - ret &= add_condition("has_province_modifier", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_EVENT_MODIFIER); - ret &= add_condition("has_recent_imigration", INTEGER, PROVINCE); //paradox typo - ret &= add_condition("is_blockaded", BOOLEAN, PROVINCE); - ret &= add_condition("is_accepted_culture", IDENTIFIER | BOOLEAN, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_capital", BOOLEAN, PROVINCE); - ret &= add_condition("is_coastal", BOOLEAN, PROVINCE); - ret &= add_condition("is_overseas", BOOLEAN, PROVINCE); - ret &= add_condition("is_primary_culture", IDENTIFIER | BOOLEAN, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_state_capital", BOOLEAN, PROVINCE); - ret &= add_condition("is_state_religion", BOOLEAN, PROVINCE); - ret &= add_condition("life_rating", REAL, PROVINCE); - ret &= add_condition("minorities", BOOLEAN, PROVINCE); - ret &= add_condition("port", BOOLEAN, PROVINCE); - ret &= add_condition("province_control_days", INTEGER, PROVINCE); - ret &= add_condition("province_id", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("region", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, REGION); - ret &= add_condition("state_id", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("terrain", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, TERRAIN); - ret &= add_condition("trade_goods", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("unemployment_by_type", COMPLEX, PROVINCE); - ret &= add_condition("units_in_province", INTEGER, PROVINCE); - - /* Pop Scope Conditions */ - ret &= add_condition("agree_with_ruling_party", REAL, POP); - ret &= add_condition("cash_reserves", REAL, POP); - ret &= add_condition("everyday_needs", REAL, POP); - ret &= add_condition("life_needs", REAL, POP); - ret &= add_condition("luxury_needs", REAL, POP); - ret &= add_condition("political_movement", BOOLEAN, POP); - ret &= add_condition("pop_majority_issue", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, ISSUE); - ret &= add_condition("pop_type", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); - ret &= add_condition("social_movement", BOOLEAN, POP); - ret &= add_condition("strata", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_STRATA); - ret &= add_condition("type", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); - - const auto import_identifiers = [this, &ret]( - std::vector<std::string_view> const& identifiers, - value_type_t value_type, - scope_type_t scope, - scope_type_t scope_change = NO_SCOPE, - identifier_type_t key_identifier_type = NO_IDENTIFIER, - identifier_type_t value_identifier_type = NO_IDENTIFIER - ) -> void { - for (std::string_view const& identifier : identifiers) { - ret &= add_condition( - identifier, value_type, scope, scope_change, - key_identifier_type, value_identifier_type - ); - } - }; - - /* Scopes from other registries */ - import_identifiers( - definition_manager.get_country_definition_manager().get_country_definition_identifiers(), - GROUP, - COUNTRY, - COUNTRY, - COUNTRY_TAG, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_map_definition().get_region_identifiers(), - GROUP, - COUNTRY, - STATE, - REGION, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_map_definition().get_province_definition_identifiers(), - GROUP, - COUNTRY, - PROVINCE, - PROVINCE_ID, - NO_IDENTIFIER - ); - - /* Conditions from other registries */ - import_identifiers( - definition_manager.get_politics_manager().get_ideology_manager().get_ideology_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - IDEOLOGY, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_politics_manager().get_issue_manager().get_reform_group_identifiers(), - IDENTIFIER, - COUNTRY, - NO_SCOPE, - REFORM_GROUP, - REFORM - ); - - import_identifiers( - definition_manager.get_politics_manager().get_issue_manager().get_reform_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - REFORM, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_politics_manager().get_issue_manager().get_issue_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - ISSUE, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_pop_manager().get_pop_type_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - POP_TYPE, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_research_manager().get_technology_manager().get_technology_identifiers(), - BOOLEAN_INT, - COUNTRY, - NO_SCOPE, - TECHNOLOGY, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_economy_manager().get_good_definition_manager().get_good_definition_identifiers(), - INTEGER, - COUNTRY, - NO_SCOPE, - TRADE_GOOD, - NO_IDENTIFIER - ); - - lock_conditions(); - - static constexpr std::string_view condition_root_identifier = "AND"; - root_condition = get_condition_by_identifier(condition_root_identifier); - - if (root_condition == nullptr) { - Logger::error("Failed to find root condition: ", condition_root_identifier); - ret = false; - } - - return ret; -} - -callback_t<std::string_view> ConditionManager::expect_parse_identifier( - DefinitionManager const& definition_manager, identifier_type_t identifier_type, - callback_t<HasIdentifier const*> callback +Callback<Condition const&, ast::NodeCPtr> auto ConditionManager::expect_condition_node( + DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, + scope_type_t from_scope, Callback<ConditionNode&&> auto callback ) const { - return [this, &definition_manager, identifier_type, callback](std::string_view identifier) -> bool { - HasIdentifier const* identified = nullptr; - - #define EXPECT_CALL(type, name, manager, ...) \ - if (share_identifier_type(identifier_type, type)) { \ - identified = manager.get_##name##_by_identifier(identifier); \ - if (identified != nullptr) { \ - return callback(identified); \ - } __VA_OPT__(else { \ - /* TODO: the set is just a placeholder for actual logic */ \ - static const case_insensitive_string_set_t chances { __VA_ARGS__ }; \ - if (chances.contains(identifier)) { \ - return true; \ - } \ - }) \ - } - - //TODO: placeholder for not implemented stuff - #define EXPECT_CALL_PLACEHOLDER(type) \ - if (share_identifier_type(identifier_type, type)) { return true; } - - EXPECT_CALL_PLACEHOLDER(VARIABLE); - EXPECT_CALL_PLACEHOLDER(GLOBAL_FLAG); - EXPECT_CALL_PLACEHOLDER(COUNTRY_FLAG); - EXPECT_CALL_PLACEHOLDER(PROVINCE_FLAG); - EXPECT_CALL( - COUNTRY_TAG, country_definition, definition_manager.get_country_definition_manager(), "THIS", "FROM", "OWNER" - ); - EXPECT_CALL(PROVINCE_ID, province_definition, definition_manager.get_map_definition(), "THIS", "FROM"); - EXPECT_CALL(REGION, region, definition_manager.get_map_definition()); - EXPECT_CALL(IDEOLOGY, ideology, definition_manager.get_politics_manager().get_ideology_manager()); - EXPECT_CALL(REFORM_GROUP, reform_group, definition_manager.get_politics_manager().get_issue_manager()); - EXPECT_CALL(REFORM, reform, definition_manager.get_politics_manager().get_issue_manager()); - EXPECT_CALL(ISSUE, issue, definition_manager.get_politics_manager().get_issue_manager()); - EXPECT_CALL(POP_TYPE, pop_type, definition_manager.get_pop_manager()); - EXPECT_CALL(POP_STRATA, strata, definition_manager.get_pop_manager()); - EXPECT_CALL(TECHNOLOGY, technology, definition_manager.get_research_manager().get_technology_manager()); - EXPECT_CALL(INVENTION, invention, definition_manager.get_research_manager().get_invention_manager()); - EXPECT_CALL(TECH_SCHOOL, technology_school, definition_manager.get_research_manager().get_technology_manager()); - EXPECT_CALL(CULTURE, culture, definition_manager.get_pop_manager().get_culture_manager(), "THIS", "FROM"); - EXPECT_CALL(CULTURE_GROUP, culture_group, definition_manager.get_pop_manager().get_culture_manager()); - EXPECT_CALL(RELIGION, religion, definition_manager.get_pop_manager().get_religion_manager(), "THIS", "FROM"); - EXPECT_CALL(TRADE_GOOD, good_definition, definition_manager.get_economy_manager().get_good_definition_manager()); - EXPECT_CALL(BUILDING, building_type, definition_manager.get_economy_manager().get_building_type_manager(), "FACTORY"); - EXPECT_CALL(CASUS_BELLI, wargoal_type, definition_manager.get_military_manager().get_wargoal_type_manager()); - EXPECT_CALL(GOVERNMENT_TYPE, government_type, definition_manager.get_politics_manager().get_government_type_manager()); - EXPECT_CALL(COUNTRY_EVENT_MODIFIER | PROVINCE_EVENT_MODIFIER, event_modifier, definition_manager.get_modifier_manager()); - EXPECT_CALL(COUNTRY_EVENT_MODIFIER, triggered_modifier, definition_manager.get_modifier_manager()); - EXPECT_CALL(NATIONAL_VALUE, national_value, definition_manager.get_politics_manager().get_national_value_manager()); - EXPECT_CALL( - CULTURE_UNION, country_definition, definition_manager.get_country_definition_manager(), "THIS", "FROM", "THIS_UNION" - ); - EXPECT_CALL(CONTINENT, continent, definition_manager.get_map_definition()); - EXPECT_CALL(CRIME, crime_modifier, definition_manager.get_crime_manager()); - EXPECT_CALL(TERRAIN, terrain_type, definition_manager.get_map_definition().get_terrain_type_manager()); - - #undef EXPECT_CALL - #undef EXPECT_CALL_PLACEHOLDER - - return false; - }; -} - -node_callback_t ConditionManager::expect_condition_node( - DefinitionManager const& definition_manager, Condition const& condition, scope_type_t current_scope, - scope_type_t this_scope, scope_type_t from_scope, callback_t<ConditionNode&&> callback -) const { - return [this, &definition_manager, &condition, callback, current_scope, this_scope, from_scope]( - ast::NodeCPtr node + return [&definition_manager, current_scope, this_scope, from_scope, callback]( + Condition const& condition, ast::NodeCPtr node ) -> bool { - bool ret = false; - ConditionNode::value_t value; - - const std::string_view identifier = condition.get_identifier(); - const value_type_t value_type = condition.get_value_type(); - const scope_type_t scope = condition.get_scope(); - const scope_type_t scope_change = condition.get_scope_change(); - const identifier_type_t key_identifier_type = condition.get_key_identifier_type(); - const identifier_type_t value_identifier_type = condition.get_value_identifier_type(); - - HasIdentifier const* value_item = nullptr; - - const auto get_identifiable = [this, &definition_manager]( - identifier_type_t item_type, std::string_view id, bool log - ) -> HasIdentifier const* { - HasIdentifier const* keyval = nullptr; - bool ret = expect_parse_identifier( - definition_manager, - item_type, - assign_variable_callback(keyval) - )(id); - if (log && !ret) { - Logger::error( - "Invalid identifier ", id, " expected to have type ", item_type, " found during condition node parsing!" - ); - } - return keyval; - }; - - if (!ret && share_value_type(value_type, IDENTIFIER)) { - std::string_view value_identifier {}; - ret |= expect_identifier_or_string(assign_variable_callback(value_identifier))(node); - if (ret) { - value = ConditionNode::string_t { value_identifier }; - value_item = get_identifiable( - value_identifier_type, - value_identifier, - value_type == IDENTIFIER // don't log if there's a fallback - ); - ret |= value_item != nullptr; - } - } - - if (!ret && share_value_type(value_type, STRING)) { - std::string_view value_identifier {}; - bool local_ret = expect_identifier_or_string( - assign_variable_callback(value_identifier) - )(node); - ret |= local_ret; - if (local_ret) { - value = ConditionNode::string_t { value_identifier }; + return condition.get_parse_callback()( + definition_manager, current_scope, this_scope, from_scope, node, + [callback, &condition](argument_t&& argument) -> bool { + return callback(ConditionNode { &condition, std::move(argument) }); } - } - - ret |= (!ret && share_value_type(value_type, BOOLEAN)) - && expect_bool(assign_variable_callback(value))(node); - ret |= (!ret && share_value_type(value_type, BOOLEAN_INT)) - && expect_int_bool(assign_variable_callback(value))(node); - ret |= (!ret && share_value_type(value_type, INTEGER)) - && expect_uint64(assign_variable_callback(value))(node); - ret |= (!ret && share_value_type(value_type, REAL)) - && expect_fixed_point(assign_variable_callback(value))(node); - - //entries with magic syntax, thanks paradox! - if (!ret && share_value_type(value_type, COMPLEX)) { - const auto expect_pair = [&ret, &value, node]( - std::string_view identifier_key, std::string_view value_key - ) -> void { - std::string_view pair_identifier {}; - fixed_point_t pair_value = 0; - ret |= expect_dictionary_keys( - identifier_key, ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(pair_identifier)), - value_key, ONE_EXACTLY, expect_fixed_point(assign_variable_callback(pair_value)) - )(node); - if (ret) { - value = ConditionNode::identifier_real_t { pair_identifier, pair_value }; - } - }; - - if (identifier == "can_build_railway_in_capital" || identifier == "can_build_fort_in_capital") { - bool in_whole_capital_state = false, limit_to_world_greatest_level = false; - ret |= expect_dictionary_keys( - "in_whole_capital_state", ONE_EXACTLY, expect_bool(assign_variable_callback(in_whole_capital_state)), - "limit_to_world_greatest_level", ONE_EXACTLY, - expect_bool(assign_variable_callback(limit_to_world_greatest_level)) - )(node); - if (ret) { - value = ConditionNode::double_boolean_t { in_whole_capital_state, limit_to_world_greatest_level }; - } - } else if (identifier == "check_variable") { - expect_pair("which", "value"); // { which = [name of variable] value = x } - } else if (identifier == "diplomatic_influence") { - expect_pair("who", "value"); // { who = [THIS/FROM/TAG] value = x } - } else if (identifier == "relation") { - expect_pair("who", "value"); // { who = [tag/this/from] value = x } - } else if (identifier == "unemployment_by_type") { - expect_pair("type", "value"); // { type = [poptype] value = x } - } else if (identifier == "upper_house") { - expect_pair("ideology", "value"); // { ideology = name value = 0.x } - } else if (identifier == "work_available") { - // { worker = [type] } - std::string_view worker {}; - ret |= expect_dictionary_keys( - "worker", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(worker)) - )(node); - if (ret) { - value = ConditionNode::string_t { worker }; - } - } else { - Logger::error("Attempted to parse unknown complex condition ", identifier, "!"); - } - - #undef EXPECT_PAIR - } - - if (!ret && share_value_type(value_type, GROUP)) { - ConditionNode::condition_list_t node_list; - ret |= expect_condition_node_list( - definition_manager, - scope_change == NO_SCOPE ? current_scope : scope_change, - this_scope, - from_scope, - vector_callback(node_list) - )(node); - value = std::move(node_list); - } - - // scope validation - scope_type_t effective_current_scope = current_scope; - if (share_scope_type(effective_current_scope, THIS)) { - effective_current_scope = this_scope; - } else if (share_scope_type(effective_current_scope, FROM)) { - effective_current_scope = from_scope; - } - - if (!share_scope_type(scope, effective_current_scope) && effective_current_scope > scope) { - Logger::warning( - "Condition or scope ", identifier, " was found in wrong scope ", effective_current_scope, ", expected ", - scope, "!" - ); - ret = false; - } - - // key parsing - HasIdentifier const* key_item = nullptr; - if (condition.key_identifier_type != NO_IDENTIFIER) { - key_item = get_identifiable(key_identifier_type, identifier, true); - ret &= key_item != nullptr; - } - - if (!ret) { - Logger::error("Could not parse condition node ", identifier); - } - - ret &= callback({ - &condition, - std::move(value), - ret, - key_item, - value_item - }); - - return ret; + ); }; } @@ -706,27 +103,24 @@ static bool top_scope_fallback(std::string_view id, ast::NodeCPtr node) { } }; -node_callback_t ConditionManager::expect_condition_node_list( +NodeCallback auto ConditionManager::expect_condition_node_list_and_length( DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, scope_type_t from_scope, - callback_t<ConditionNode&&> callback, bool top_scope + Callback<ConditionNode&&> auto callback, LengthCallback auto length_callback, bool top_scope ) const { - return [this, &definition_manager, callback, current_scope, this_scope, from_scope, top_scope](ast::NodeCPtr node) -> bool { - const auto expect_node = [ - this, &definition_manager, callback, current_scope, this_scope, from_scope - ](Condition const& condition, ast::NodeCPtr node) -> bool { - return expect_condition_node( - definition_manager, condition, current_scope, this_scope, from_scope, callback - )(node); - }; + return conditions.expect_item_dictionary_and_length_and_default( + std::move(length_callback), + top_scope ? top_scope_fallback : key_value_invalid_callback, + expect_condition_node(definition_manager, current_scope, this_scope, from_scope, std::move(callback)) + ); +} - bool ret = conditions.expect_item_dictionary_and_default( - top_scope ? top_scope_fallback : key_value_invalid_callback, expect_node - )(node); - if (!ret) { - Logger::error("Error parsing condition node:\n", node); - } - return ret; - }; +NodeCallback auto ConditionManager::expect_condition_node_list( + DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, scope_type_t from_scope, + Callback<ConditionNode&&> auto callback, bool top_scope +) const { + return expect_condition_node_list_and_length( + definition_manager, current_scope, this_scope, from_scope, std::move(callback), default_length_callback, top_scope + ); } node_callback_t ConditionManager::expect_condition_script( @@ -734,19 +128,56 @@ node_callback_t ConditionManager::expect_condition_script( scope_type_t from_scope, callback_t<ConditionNode&&> callback ) const { return [this, &definition_manager, initial_scope, this_scope, from_scope, callback](ast::NodeCPtr node) -> bool { + if (root_condition != nullptr) { + return expect_condition_node( + definition_manager, + initial_scope, + this_scope, + from_scope, + callback + )(*root_condition, node); + } else { + Logger::error("Cannot parse condition script: root condition not set!"); + return false; + } + }; +} - ConditionNode::condition_list_t conds; - bool ret = expect_condition_node_list( - definition_manager, - initial_scope, - this_scope, - from_scope, - NodeTools::vector_callback(conds), - true - )(node); +bool ConditionManager::setup_conditions(DefinitionManager const& definition_manager) { + if (root_condition != nullptr || !conditions_empty()) { + Logger::error("Cannot set up conditions - root condition is not null and/or condition registry is not empty!"); + return false; + } - ret &= callback({ root_condition, std::move(conds), true }); + bool ret = true; - return ret; - }; + // TODO - register all conditions here with parsing and execution callbacks + + if ( + add_condition( + "root condition", + []( + DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, + scope_type_t from_scope, ast::NodeCPtr node, callback_t<ConditionNode::argument_t&&> callback + ) -> bool { + return true; + }, + []( + InstanceManager const& instance_manager, scope_t const& current_scope, scope_t const& this_scope, + scope_t const& from_scope, argument_t const& argument + ) -> bool { + Logger::error("Condition execution not yet implemented!"); + return false; + } + ) + ) { + root_condition = &get_conditions().back(); + } else { + Logger::error("Failed to set root condition! Will not be able to parse condition scripts!"); + ret = false; + } + + lock_conditions(); + + return ret; } diff --git a/src/openvic-simulation/scripts/Condition.hpp b/src/openvic-simulation/scripts/Condition.hpp index 1f4929a..c390b26 100644 --- a/src/openvic-simulation/scripts/Condition.hpp +++ b/src/openvic-simulation/scripts/Condition.hpp @@ -1,95 +1,37 @@ #pragma once #include <ostream> +#include <string> #include <string_view> #include <variant> +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/EnumBitfield.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { - struct ConditionManager; - struct ConditionScript; - struct DefinitionManager; - - enum class value_type_t : uint8_t { - NO_TYPE = 0, - IDENTIFIER = 1 << 0, - STRING = 1 << 1, - BOOLEAN = 1 << 2, - BOOLEAN_INT = 1 << 3, - INTEGER = 1 << 4, - REAL = 1 << 5, - COMPLEX = 1 << 6, - GROUP = 1 << 7, - MAX_VALUE = (1 << 8) - 1 - }; - - // Order matters in this enum, for the fallback system to work - // smaller entities must have smaller integers associated! - enum class scope_type_t : uint8_t { //TODO: maybe distinguish TRANSPARENT from NO_SCOPE + enum class scope_type_t : uint8_t { NO_SCOPE = 0, POP = 1 << 0, PROVINCE = 1 << 1, STATE = 1 << 2, COUNTRY = 1 << 3, - THIS = 1 << 4, - FROM = 1 << 5, - MAX_SCOPE = (1 << 6) - 1 - }; - - enum class identifier_type_t : uint32_t { - NO_IDENTIFIER = 0, - VARIABLE = 1 << 0, - GLOBAL_FLAG = 1 << 1, - COUNTRY_FLAG = 1 << 2, - PROVINCE_FLAG = 1 << 3, - COUNTRY_TAG = 1 << 4, - PROVINCE_ID = 1 << 5, - REGION = 1 << 6, - IDEOLOGY = 1 << 7, - REFORM_GROUP = 1 << 8, - REFORM = 1 << 9, - ISSUE = 1 << 10, - POP_TYPE = 1 << 11, - POP_STRATA = 1 << 12, - TECHNOLOGY = 1 << 13, - INVENTION = 1 << 14, - TECH_SCHOOL = 1 << 15, - CULTURE = 1 << 16, - CULTURE_GROUP = 1 << 17, - RELIGION = 1 << 18, - TRADE_GOOD = 1 << 19, - BUILDING = 1 << 20, - CASUS_BELLI = 1 << 21, - GOVERNMENT_TYPE = 1 << 22, - COUNTRY_EVENT_MODIFIER = 1 << 23, - PROVINCE_EVENT_MODIFIER = 1 << 24, - NATIONAL_VALUE = 1 << 25, - CULTURE_UNION = 1 << 26, // same as COUNTRY_TAG but also accepts scope this_union - CONTINENT = 1 << 27, - CRIME = 1 << 28, - TERRAIN = 1 << 29 + THIS = 1 << 4, // Indicator bit for scope switching ("use the THIS scope", not a scope in and of itself) + FROM = 1 << 5, // Indicator bit for scope switching ("use the FROM scope", not a scope in and of itself) + FULL_SCOPE_MASK = (1 << 6) - 1, // All possible scope bits (including THIS and FROM) + ALL_SCOPES = POP | PROVINCE | STATE | COUNTRY // All real scopes (without THIS and FROM) }; /* Allows enum types to be used with bitwise operators. */ - template<> struct enable_bitfield<value_type_t> : std::true_type {}; template<> struct enable_bitfield<scope_type_t> : std::true_type {}; - template<> struct enable_bitfield<identifier_type_t> : std::true_type {}; /* Returns true if the values have any bit in common. */ - inline constexpr bool share_value_type(value_type_t lhs, value_type_t rhs) { - return (lhs & rhs) != value_type_t::NO_TYPE; - } inline constexpr bool share_scope_type(scope_type_t lhs, scope_type_t rhs) { return (lhs & rhs) != scope_type_t::NO_SCOPE; } - inline constexpr bool share_identifier_type(identifier_type_t lhs, identifier_type_t rhs) { - return (lhs & rhs) != identifier_type_t::NO_IDENTIFIER; - } -#define _BUILD_STRING(entry, share) \ - if (share(value, entry)) { \ +#define BUILD_STRING(entry) \ + if (share_scope_type(value, entry)) { \ if (type_found) { \ stream << " | "; \ } else { \ @@ -98,179 +40,181 @@ namespace OpenVic { stream << #entry; \ } -#define BUILD_STRING(entry) _BUILD_STRING(entry, share_value_type) - - inline std::ostream& operator<<(std::ostream& stream, value_type_t value) { - using enum value_type_t; - if (value == NO_TYPE) { - return stream << "[NO_TYPE]"; - } - bool type_found = false; - stream << '['; - BUILD_STRING(IDENTIFIER); - BUILD_STRING(STRING); - BUILD_STRING(BOOLEAN); - BUILD_STRING(BOOLEAN_INT); - BUILD_STRING(INTEGER); - BUILD_STRING(REAL); - BUILD_STRING(COMPLEX); - BUILD_STRING(GROUP); - if (!type_found) { - stream << "INVALID VALUE TYPE"; - } - return stream << ']'; - } - -#undef BUILD_STRING -#define BUILD_STRING(entry) _BUILD_STRING(entry, share_scope_type) - inline std::ostream& operator<<(std::ostream& stream, scope_type_t value) { using enum scope_type_t; + if (value == NO_SCOPE) { return stream << "[NO_SCOPE]"; } + bool type_found = false; + stream << '['; + BUILD_STRING(COUNTRY); BUILD_STRING(STATE); BUILD_STRING(PROVINCE); BUILD_STRING(POP); BUILD_STRING(THIS); BUILD_STRING(FROM); + if (!type_found) { stream << "INVALID SCOPE"; } + return stream << ']'; } #undef BUILD_STRING -#define BUILD_STRING(entry) _BUILD_STRING(entry, share_identifier_type) - inline std::ostream& operator<<(std::ostream& stream, identifier_type_t value) { - using enum identifier_type_t; - if (value == NO_IDENTIFIER) { - return stream << "[NO_IDENTIFIER]"; + struct ConditionManager; + struct ConditionScript; + struct CountryDefinition; + struct CountryInstance; + struct ProvinceDefinition; + struct ProvinceInstance; + struct Pop; + struct GoodDefinition; + struct ProvinceSetModifier; + using Continent = ProvinceSetModifier; + struct Condition; + struct DefinitionManager; + struct InstanceManager; + + struct ConditionNode { + friend struct ConditionManager; + friend struct ConditionScript; + + // std::variant's default constructor sets it to the first type in its parameter list, so for argument_t and scope_t + // a default-constructed instance represents no_argument_t or no_scope_t. + + struct no_argument_t {}; + struct this_argument_t {}; + struct from_argument_t {}; + using integer_t = int64_t; + using argument_t = std::variant< + // No argument + no_argument_t, + // Script reference arguments + this_argument_t, from_argument_t, + // List argument + std::vector<ConditionNode>, + // Value arguments + bool, std::string, integer_t, fixed_point_t, + // Game object arguments + CountryDefinition const*, ProvinceDefinition const*, GoodDefinition const*, Continent const* + >; + + static constexpr bool is_this_argument(argument_t const& argument) { + return std::holds_alternative<this_argument_t>(argument); } - bool type_found = false; - stream << '['; - BUILD_STRING(VARIABLE); - BUILD_STRING(GLOBAL_FLAG); - BUILD_STRING(COUNTRY_FLAG); - BUILD_STRING(PROVINCE_FLAG); - BUILD_STRING(COUNTRY_TAG); - BUILD_STRING(PROVINCE_ID); - BUILD_STRING(REGION); - BUILD_STRING(IDEOLOGY); - BUILD_STRING(REFORM_GROUP); - BUILD_STRING(REFORM); - BUILD_STRING(ISSUE); - BUILD_STRING(POP_TYPE); - BUILD_STRING(POP_STRATA); - BUILD_STRING(TECHNOLOGY); - BUILD_STRING(INVENTION); - BUILD_STRING(TECH_SCHOOL); - BUILD_STRING(CULTURE); - BUILD_STRING(CULTURE_GROUP); - BUILD_STRING(RELIGION); - BUILD_STRING(TRADE_GOOD); - BUILD_STRING(BUILDING); - BUILD_STRING(CASUS_BELLI); - BUILD_STRING(GOVERNMENT_TYPE); - BUILD_STRING(COUNTRY_EVENT_MODIFIER); - BUILD_STRING(PROVINCE_EVENT_MODIFIER); - BUILD_STRING(NATIONAL_VALUE); - BUILD_STRING(CULTURE_UNION); - BUILD_STRING(CONTINENT); - BUILD_STRING(CRIME); - BUILD_STRING(TERRAIN); - if (!type_found) { - stream << "INVALID IDENTIFIER TYPE"; + + static constexpr bool is_from_argument(argument_t const& argument) { + return std::holds_alternative<from_argument_t>(argument); } - return stream << ']'; - } -#undef BUILD_STRING -#undef _BUILD_STRING + struct no_scope_t {}; + using scope_t = std::variant< + no_scope_t, + CountryInstance const*, + ProvinceInstance const*, + Pop const* + >; - struct Condition : HasIdentifier { - friend struct ConditionManager; - using enum identifier_type_t; + static constexpr bool is_no_scope(scope_t const& scope) { + return std::holds_alternative<no_scope_t>(scope); + } private: - const value_type_t PROPERTY(value_type); - const scope_type_t PROPERTY(scope); - const scope_type_t PROPERTY(scope_change); - const identifier_type_t PROPERTY(key_identifier_type); - const identifier_type_t PROPERTY(value_identifier_type); + Condition const* PROPERTY(condition); + argument_t PROPERTY(argument); - Condition( - std::string_view new_identifier, value_type_t new_value_type, scope_type_t new_scope, - scope_type_t new_scope_change, identifier_type_t new_key_identifier_type, - identifier_type_t new_value_identifier_type + ConditionNode( + Condition const* new_condition = nullptr, argument_t&& new_argument = no_argument_t {} ); public: - Condition(Condition&&) = default; + ConditionNode(ConditionNode&&) = default; + ConditionNode& operator=(ConditionNode&&) = default; + + constexpr bool is_initialised() const { + return condition != nullptr; + } + + bool execute( + InstanceManager const& instance_manager, scope_t const& current_scope, scope_t const& this_scope, + scope_t const& from_scope + ) const; }; - struct ConditionNode { + struct Condition : HasIdentifier { friend struct ConditionManager; - friend struct ConditionScript; - using string_t = std::string; - using boolean_t = bool; - using double_boolean_t = std::pair<bool, bool>; - using integer_t = uint64_t; - using real_t = fixed_point_t; - using identifier_real_t = std::pair<std::string, real_t>; - using condition_list_t = std::vector<ConditionNode>; - using value_t = std::variant< - string_t, boolean_t, double_boolean_t, integer_t, real_t, identifier_real_t, condition_list_t + using parse_callback_t = NodeTools::callback_t< + // bool(definition_manager, current_scope, this_scope, from_scope, node, callback) + DefinitionManager const&, scope_type_t, scope_type_t, scope_type_t, ast::NodeCPtr, + NodeTools::callback_t<ConditionNode::argument_t&&> + >; + using execute_callback_t = NodeTools::callback_t< + // bool(instance_manager, current_scope, this_scope, from_scope, argument) + InstanceManager const&, ConditionNode::scope_t const&, ConditionNode::scope_t const&, + ConditionNode::scope_t const&, ConditionNode::argument_t const& >; private: - Condition const* PROPERTY(condition); - value_t PROPERTY(value); - HasIdentifier const* PROPERTY(condition_key_item); - HasIdentifier const* PROPERTY(condition_value_item); - bool PROPERTY_CUSTOM_PREFIX(valid, is); + parse_callback_t PROPERTY(parse_callback); + execute_callback_t PROPERTY(execute_callback); - ConditionNode( - Condition const* new_condition = nullptr, value_t&& new_value = 0, - bool new_valid = false, - HasIdentifier const* new_condition_key_item = nullptr, - HasIdentifier const* new_condition_value_item = nullptr + Condition( + std::string_view new_identifier, + parse_callback_t&& new_parse_callback, + execute_callback_t&& new_execute_callback ); + + public: + Condition(Condition&&) = default; }; struct ConditionManager { private: CaseInsensitiveIdentifierRegistry<Condition> IDENTIFIER_REGISTRY(condition); - Condition const* root_condition = nullptr; + Condition const* PROPERTY(root_condition); bool add_condition( - std::string_view identifier, value_type_t value_type, scope_type_t scope, - scope_type_t scope_change = scope_type_t::NO_SCOPE, - identifier_type_t key_identifier_type = identifier_type_t::NO_IDENTIFIER, - identifier_type_t value_identifier_type = identifier_type_t::NO_IDENTIFIER + std::string_view identifier, + Condition::parse_callback_t&& parse_callback, + Condition::execute_callback_t&& execute_callback ); - NodeTools::callback_t<std::string_view> expect_parse_identifier( - DefinitionManager const& definition_manager, identifier_type_t identifier_type, - NodeTools::callback_t<HasIdentifier const*> callback + template< + scope_type_t CHANGE_SCOPE = scope_type_t::NO_SCOPE, + scope_type_t ALLOWED_SCOPES = scope_type_t::ALL_SCOPES, + bool TOP_SCOPE = false + > + static bool _parse_condition_node_list_callback( + DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, + scope_type_t from_scope, ast::NodeCPtr node, NodeTools::callback_t<ConditionNode::argument_t&&> callback + ); + + NodeTools::Callback<Condition const&, ast::NodeCPtr> auto expect_condition_node( + DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, + scope_type_t from_scope, NodeTools::Callback<ConditionNode&&> auto callback ) const; - NodeTools::node_callback_t expect_condition_node( - DefinitionManager const& definition_manager, Condition const& condition, scope_type_t current_scope, - scope_type_t this_scope, scope_type_t from_scope, NodeTools::callback_t<ConditionNode&&> callback + NodeTools::NodeCallback auto expect_condition_node_list_and_length( + DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, + scope_type_t from_scope, NodeTools::Callback<ConditionNode&&> auto callback, + NodeTools::LengthCallback auto length_callback, bool top_scope = false ) const; - NodeTools::node_callback_t expect_condition_node_list( + NodeTools::NodeCallback auto expect_condition_node_list( DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, - scope_type_t from_scope, NodeTools::callback_t<ConditionNode&&> callback, bool top_scope = false + scope_type_t from_scope, NodeTools::Callback<ConditionNode&&> auto callback, bool top_scope = false ) const; public: + ConditionManager(); + bool setup_conditions(DefinitionManager const& definition_manager); NodeTools::node_callback_t expect_condition_script( diff --git a/src/openvic-simulation/scripts/ConditionScript.cpp b/src/openvic-simulation/scripts/ConditionScript.cpp index c556bd2..a3db1ce 100644 --- a/src/openvic-simulation/scripts/ConditionScript.cpp +++ b/src/openvic-simulation/scripts/ConditionScript.cpp @@ -18,3 +18,13 @@ bool ConditionScript::_parse_script(ast::NodeCPtr root, DefinitionManager const& move_variable_callback(condition_root) )(root); } + +bool ConditionScript::execute( + InstanceManager const& instance_manager, + ConditionNode::scope_t const& initial_scope, + ConditionNode::scope_t const& this_scope, + ConditionNode::scope_t const& from_scope +) const { + return !condition_root.is_initialised() + || condition_root.execute(instance_manager, initial_scope, this_scope, from_scope); +} diff --git a/src/openvic-simulation/scripts/ConditionScript.hpp b/src/openvic-simulation/scripts/ConditionScript.hpp index aa386e8..0e09422 100644 --- a/src/openvic-simulation/scripts/ConditionScript.hpp +++ b/src/openvic-simulation/scripts/ConditionScript.hpp @@ -19,5 +19,12 @@ namespace OpenVic { public: ConditionScript(scope_type_t new_initial_scope, scope_type_t new_this_scope, scope_type_t new_from_scope); + + bool execute( + InstanceManager const& instance_manager, + ConditionNode::scope_t const& initial_scope, + ConditionNode::scope_t const& this_scope, + ConditionNode::scope_t const& from_scope + ) const; }; } diff --git a/src/openvic-simulation/scripts/ConditionalWeight.cpp b/src/openvic-simulation/scripts/ConditionalWeight.cpp index 1bb83d0..77970b7 100644 --- a/src/openvic-simulation/scripts/ConditionalWeight.cpp +++ b/src/openvic-simulation/scripts/ConditionalWeight.cpp @@ -132,3 +132,45 @@ struct ConditionalWeight::parse_scripts_visitor_t { bool ConditionalWeight::parse_scripts(DefinitionManager const& definition_manager) { return parse_scripts_visitor_t { definition_manager }(condition_weight_items); } + +fixed_point_t ConditionalWeight::execute( + InstanceManager const& instance_manager, + ConditionNode::scope_t const& initial_scope, + ConditionNode::scope_t const& this_scope, + ConditionNode::scope_t const& from_scope +) const { + struct visitor_t { + InstanceManager const& instance_manager; + ConditionNode::scope_t const& initial_scope; + ConditionNode::scope_t const& this_scope; + ConditionNode::scope_t const& from_scope; + fixed_point_t result; + + void operator()(condition_weight_t const& item) { + if (item.second.execute(instance_manager, initial_scope, this_scope, from_scope)) { + // TODO - Should this always be multiplicative, or additive for some conditional weight scripts? + result *= item.first; + } + } + + void operator()(condition_weight_group_t const& group) { + for (condition_weight_t const& item : group) { + // TODO - should this execute for all items in a group? Maybe it should stop after one of them fails? + (*this)(item); + } + } + } visitor { + instance_manager, initial_scope, this_scope, from_scope, base + }; + + for (condition_weight_item_t const& item : condition_weight_items) { + // TODO - this is only valid if all weights are applied multiplicatively, otherwise it must be changed + if (visitor.result == fixed_point_t::_0()) { + return fixed_point_t::_0(); + } + + std::visit(visitor, item); + } + + return visitor.result; +} diff --git a/src/openvic-simulation/scripts/ConditionalWeight.hpp b/src/openvic-simulation/scripts/ConditionalWeight.hpp index fcab0a6..4ebf55d 100644 --- a/src/openvic-simulation/scripts/ConditionalWeight.hpp +++ b/src/openvic-simulation/scripts/ConditionalWeight.hpp @@ -34,5 +34,12 @@ namespace OpenVic { NodeTools::node_callback_t expect_conditional_weight(base_key_t base_key); bool parse_scripts(DefinitionManager const& definition_manager); + + fixed_point_t execute( + InstanceManager const& instance_manager, + ConditionNode::scope_t const& initial_scope, + ConditionNode::scope_t const& this_scope, + ConditionNode::scope_t const& from_scope + ) const; }; } |