From 6f07de81a6ca430c522527958e05440d67b04937 Mon Sep 17 00:00:00 2001 From: zaaarf Date: Sun, 7 Jan 2024 23:47:39 +0100 Subject: feat: condition loading and parsing Co-authored-by: Hop311 --- src/openvic-simulation/scripts/Condition.cpp | 753 +++++++++++++++++++++ src/openvic-simulation/scripts/Condition.hpp | 254 +++++++ src/openvic-simulation/scripts/ConditionScript.cpp | 16 +- src/openvic-simulation/scripts/ConditionScript.hpp | 11 + .../scripts/ConditionalWeight.cpp | 13 +- .../scripts/ConditionalWeight.hpp | 5 +- src/openvic-simulation/scripts/ScriptManager.hpp | 10 + 7 files changed, 1054 insertions(+), 8 deletions(-) create mode 100644 src/openvic-simulation/scripts/Condition.cpp create mode 100644 src/openvic-simulation/scripts/Condition.hpp create mode 100644 src/openvic-simulation/scripts/ScriptManager.hpp (limited to 'src/openvic-simulation/scripts') diff --git a/src/openvic-simulation/scripts/Condition.cpp b/src/openvic-simulation/scripts/Condition.cpp new file mode 100644 index 0000000..73b3606 --- /dev/null +++ b/src/openvic-simulation/scripts/Condition.cpp @@ -0,0 +1,753 @@ +#include "Condition.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/GameManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +Condition::Condition( + std::string_view new_identifier, value_type_t new_value_type, scope_t new_scope, + scope_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 } {} + +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 } {} + +bool ConditionManager::add_condition( + std::string_view identifier, value_type_t value_type, scope_t scope, scope_t scope_change, + identifier_type_t key_identifier_type, identifier_type_t value_identifier_type +) { + if (identifier.empty()) { + Logger::error("Invalid condition identifier - empty!"); + return false; + } + + if (value_type == value_type_t::NO_TYPE || value_type > value_type_t::MAX_VALUE) { + Logger::error("Condition ", identifier, " has invalid value type: ", static_cast(value_type)); + return false; + } + if (scope == scope_t::NO_SCOPE || scope > scope_t::MAX_SCOPE) { + Logger::error("Condition ", identifier, " has invalid scope: ", static_cast(scope)); + return false; + } + + if (share_value_type(value_type, value_type_t::IDENTIFIER) && value_identifier_type == identifier_type_t::NONE) { + Logger::error("Condition ", identifier, " has no identifier type!"); + return false; + } + + // don't perform the check for complex types + if (!share_value_type(value_type, value_type_t::COMPLEX)) { + if (!share_value_type(value_type, value_type_t::IDENTIFIER) && value_identifier_type != identifier_type_t::NONE) { + 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 + }); +} + +bool ConditionManager::setup_conditions(GameManager const& game_manager) { + bool ret = true; + + using enum value_type_t; + using enum scope_t; + using enum identifier_type_t; + + /* 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, NONE, 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, NONE, 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, NONE, 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, NONE, 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, NONE, PROVINCE_ID); + ret &= add_condition("casus_belli", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("check_variable", COMPLEX, COUNTRY, NO_SCOPE, NONE, VARIABLE); + ret &= add_condition("citizenship_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, CASUS_BELLI); + ret &= add_condition("continent", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, CONTINENT); + ret &= add_condition("controls", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, 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, NONE, ISSUE); + ret &= add_condition("education_spending", REAL, COUNTRY); + ret &= add_condition("election", BOOLEAN, COUNTRY); + ret &= add_condition("exists", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("government", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, GOVERNMENT_TYPE); + ret &= add_condition("great_wars_enabled", BOOLEAN, COUNTRY); + ret &= add_condition("have_core_in", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("has_country_flag", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_FLAG); + ret &= add_condition("has_country_modifier", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, CULTURE); + ret &= add_condition("has_pop_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, RELIGION); + ret &= add_condition("has_pop_type", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, IDEOLOGY); + ret &= add_condition("industrial_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("in_sphere", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("in_default", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("invention", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, 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, NONE, COUNTRY_TAG | PROVINCE_ID); + ret &= add_condition("is_culture_group", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG | CULTURE_GROUP); + ret &= add_condition("is_ideology_enabled", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, REFORM); + ret &= add_condition("is_our_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("is_possible_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("is_releasable_vassal", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("is_secondary_power", BOOLEAN, COUNTRY); + ret &= add_condition("is_sphere_leader_of", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, COUNTRY_TAG); + ret &= add_condition("military_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, NATIONAL_VALUE); + ret &= add_condition("national_provinces_occupied", REAL, COUNTRY); + ret &= add_condition("neighbour", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, 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, NONE, CULTURE); + ret &= add_condition("pop_majority_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, IDEOLOGY); + ret &= add_condition("pop_majority_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, CULTURE); + ret &= add_condition("accepted_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, CULTURE); + ret &= add_condition("produces", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, RELIGION); + ret &= add_condition("religious_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, COUNTRY_TAG); + ret &= add_condition("ruling_party_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, COUNTRY_TAG); + ret &= add_condition("substate_of", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("tag", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("tech_school", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, TECH_SCHOOL); + ret &= add_condition("this_culture_union", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, ISSUE); + ret &= add_condition("treasury", REAL, COUNTRY); + ret &= add_condition("truce_with", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, 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, NONE, COUNTRY_TAG); + ret &= add_condition("war", BOOLEAN, COUNTRY); + ret &= add_condition("war_exhaustion", REAL, COUNTRY); + ret &= add_condition("war_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, ISSUE); + ret &= add_condition("war_score", REAL, COUNTRY); + ret &= add_condition("war_with", IDENTIFIER, COUNTRY, NO_SCOPE, NONE, COUNTRY_TAG); + + /* State Scope Conditions */ + ret &= add_condition("controlled_by", IDENTIFIER, STATE, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("empty", BOOLEAN, STATE); + ret &= add_condition("flashpoint_tension", REAL, STATE); + ret &= add_condition("has_building", IDENTIFIER, STATE, NO_SCOPE, NONE, 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, NONE, COUNTRY_TAG); + ret &= add_condition("trade_goods_in_state", IDENTIFIER, STATE, NO_SCOPE, NONE, TRADE_GOOD); + ret &= add_condition("work_available", COMPLEX, STATE); + + /* 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, NONE, COUNTRY_TAG); + ret &= add_condition("country_units_in_state", IDENTIFIER, PROVINCE, NO_SCOPE, NONE, COUNTRY_TAG); + ret &= add_condition("has_crime", IDENTIFIER, PROVINCE, NO_SCOPE, NONE, 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, NONE, PROVINCE_FLAG); + ret &= add_condition("has_province_modifier", IDENTIFIER, PROVINCE, NO_SCOPE, NONE, 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, NONE, 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, NONE, 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, NONE, PROVINCE_ID); + ret &= add_condition("region", IDENTIFIER, PROVINCE, NO_SCOPE, NONE, REGION); + ret &= add_condition("state_id", IDENTIFIER, PROVINCE, NO_SCOPE, NONE, PROVINCE_ID); + ret &= add_condition("terrain", IDENTIFIER, PROVINCE, NO_SCOPE, NONE, TERRAIN); + ret &= add_condition("trade_goods", IDENTIFIER, PROVINCE, NO_SCOPE, NONE, 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, NONE, ISSUE); + ret &= add_condition("pop_type", IDENTIFIER, POP, NO_SCOPE, NONE, POP_TYPE); + ret &= add_condition("social_movement", BOOLEAN, POP); + ret &= add_condition("strata", IDENTIFIER, POP, NO_SCOPE, NONE, POP_STRATA); + ret &= add_condition("type", IDENTIFIER, POP, NO_SCOPE, NONE, POP_TYPE); + + const auto import_identifiers = [this, &ret]( + std::vector const& identifiers, + value_type_t value_type, + scope_t scope, + scope_t scope_change = scope_t::NO_SCOPE, + identifier_type_t key_identifier_type = identifier_type_t::NONE, + identifier_type_t value_identifier_type = identifier_type_t::NONE + ) -> 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( + game_manager.get_country_manager().get_country_identifiers(), + GROUP, + COUNTRY, + COUNTRY, + COUNTRY_TAG, + NONE + ); + + import_identifiers( + game_manager.get_map().get_region_identifiers(), + GROUP, + COUNTRY, + STATE, + REGION, + NONE + ); + + import_identifiers( + game_manager.get_map().get_province_identifiers(), + GROUP, + COUNTRY, + PROVINCE, + PROVINCE_ID, + NONE + ); + + /* Conditions from other registries */ + import_identifiers( + game_manager.get_politics_manager().get_ideology_manager().get_ideology_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + IDEOLOGY, + NONE + ); + + import_identifiers( + game_manager.get_politics_manager().get_issue_manager().get_reform_group_identifiers(), + IDENTIFIER, + COUNTRY, + NO_SCOPE, + REFORM_GROUP, + REFORM + ); + + import_identifiers( + game_manager.get_politics_manager().get_issue_manager().get_reform_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + REFORM, + NONE + ); + + import_identifiers( + game_manager.get_politics_manager().get_issue_manager().get_issue_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + ISSUE, + NONE + ); + + import_identifiers( + game_manager.get_pop_manager().get_pop_type_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + POP_TYPE, + NONE + ); + + import_identifiers( + game_manager.get_research_manager().get_technology_manager().get_technology_identifiers(), + BOOLEAN_INT, + COUNTRY, + NO_SCOPE, + TECHNOLOGY, + NONE + ); + + import_identifiers( + game_manager.get_economy_manager().get_good_manager().get_good_identifiers(), + INTEGER, + COUNTRY, + NO_SCOPE, + TRADE_GOOD, + NONE + ); + + 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 ConditionManager::expect_parse_identifier( + GameManager const& game_manager, identifier_type_t identifier_type, + callback_t callback +) const { + using enum identifier_type_t; + return [this, &game_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, game_manager.get_country_manager(), "THIS", "FROM", "OWNER"); + EXPECT_CALL(PROVINCE_ID, province, game_manager.get_map(), "THIS", "FROM"); + EXPECT_CALL(REGION, region, game_manager.get_map()); + EXPECT_CALL(IDEOLOGY, ideology, game_manager.get_politics_manager().get_ideology_manager()); + EXPECT_CALL(REFORM_GROUP, reform_group, game_manager.get_politics_manager().get_issue_manager()); + EXPECT_CALL(REFORM, reform, game_manager.get_politics_manager().get_issue_manager()); + EXPECT_CALL(ISSUE, issue, game_manager.get_politics_manager().get_issue_manager()); + EXPECT_CALL(POP_TYPE, pop_type, game_manager.get_pop_manager()); + EXPECT_CALL(POP_STRATA, strata, game_manager.get_pop_manager()); + EXPECT_CALL(TECHNOLOGY, technology, game_manager.get_research_manager().get_technology_manager()); + EXPECT_CALL(INVENTION, invention, game_manager.get_research_manager().get_invention_manager()); + EXPECT_CALL(TECH_SCHOOL, technology_school, game_manager.get_research_manager().get_technology_manager()); + EXPECT_CALL(CULTURE, culture, game_manager.get_pop_manager().get_culture_manager(), "THIS", "FROM"); + EXPECT_CALL(CULTURE_GROUP, culture_group, game_manager.get_pop_manager().get_culture_manager()); + EXPECT_CALL(RELIGION, religion, game_manager.get_pop_manager().get_religion_manager(), "THIS", "FROM"); + EXPECT_CALL(TRADE_GOOD, good, game_manager.get_economy_manager().get_good_manager()); + EXPECT_CALL(BUILDING, building_type, game_manager.get_economy_manager().get_building_type_manager(), "FACTORY"); + EXPECT_CALL(CASUS_BELLI, wargoal_type, game_manager.get_military_manager().get_wargoal_type_manager()); + EXPECT_CALL(GOVERNMENT_TYPE, government_type, game_manager.get_politics_manager().get_government_type_manager()); + EXPECT_CALL(MODIFIER, event_modifier, game_manager.get_modifier_manager()); + EXPECT_CALL(MODIFIER, triggered_modifier, game_manager.get_modifier_manager()); + EXPECT_CALL(MODIFIER, static_modifier, game_manager.get_modifier_manager()); + EXPECT_CALL(NATIONAL_VALUE, national_value, game_manager.get_politics_manager().get_national_value_manager()); + EXPECT_CALL(CULTURE_UNION, country, game_manager.get_country_manager(), "THIS", "FROM", "THIS_UNION"); + EXPECT_CALL(CONTINENT, continent, game_manager.get_map()); + EXPECT_CALL(CRIME, crime_modifier, game_manager.get_crime_manager()); + EXPECT_CALL(TERRAIN, terrain_type, game_manager.get_map().get_terrain_type_manager()); + + #undef EXPECT_CALL + #undef EXPECT_CALL_PLACEHOLDER + + return false; + }; +} + +node_callback_t ConditionManager::expect_condition_node( + GameManager const& game_manager, Condition const& condition, scope_t this_scope, + scope_t from_scope, scope_t cur_scope, callback_t callback +) const { + using enum value_type_t; + return [this, &game_manager, &condition, callback, this_scope, from_scope, cur_scope]( + 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_t scope = condition.get_scope(); + const scope_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, &game_manager]( + identifier_type_t item_type, std::string_view id, bool log + ) -> HasIdentifier const* { + HasIdentifier const* keyval = nullptr; + bool ret = expect_parse_identifier( + game_manager, + item_type, + assign_variable_callback(keyval) + )(id); + if (log && !ret) { + Logger::error( + "Invalid identifier ", id, + " expected to have type ", get_identifier_type_string(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 }; + } + } + + 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)) { + #define EXPECT_PAIR(key, value) \ + std::string_view key; \ + fixed_point_t real_##value; \ + ret |= expect_dictionary_keys( \ + #key, ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(key)), \ + #value, ONE_EXACTLY, expect_fixed_point(assign_variable_callback(real_##value)) \ + )(node); \ + if (ret) { \ + value = ConditionNode::identifier_real_t { key, real_##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( + game_manager, this_scope, from_scope, + scope_change == scope_t::NO_SCOPE ? cur_scope : scope_change, + false, + vector_callback(node_list) + )(node); + value = std::move(node_list); + } + + // scope validation + scope_t effective_current_scope = cur_scope; + if (share_scope(effective_current_scope, scope_t::THIS)) { + effective_current_scope = this_scope; + } else if (share_scope(effective_current_scope, scope_t::FROM)) { + effective_current_scope = from_scope; + } + + if (!share_scope(scope, effective_current_scope) && effective_current_scope > scope) { + Logger::warning( + "Condition or scope ", identifier, " was found in wrong scope ", + get_scope_string(effective_current_scope), ", expected ", + get_scope_string(scope), "!" + ); + ret = false; + } + + // key parsing + HasIdentifier const* key_item = nullptr; + if (condition.key_identifier_type != identifier_type_t::NONE) { + 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; + }; +} + +/* Default callback for top condition scope. */ +static bool top_scope_fallback(std::string_view id, ast::NodeCPtr node) { + /* This is a non-condition key, and so not case-insensitive. */ + if (id == "factor") { + return true; + } else { + Logger::error("Unknown node \"", id, "\" found while parsing conditions!"); + return false; + } +}; + +node_callback_t ConditionManager::expect_condition_node_list( + GameManager const& game_manager, scope_t this_scope, scope_t from_scope, + scope_t cur_scope, bool top_scope, callback_t callback +) const { + using enum value_type_t; + using enum scope_t; + return [this, &game_manager, callback, this_scope, from_scope, cur_scope, top_scope](ast::NodeCPtr node) -> bool { + const auto expect_node = [ + this, &game_manager, callback, this_scope, from_scope, cur_scope + ](Condition const& condition, ast::NodeCPtr node) -> bool { + return expect_condition_node( + game_manager, condition, this_scope, from_scope, cur_scope, callback + )(node); + }; + + bool ret = conditions.expect_item_dictionary_and_default( + expect_node, top_scope ? top_scope_fallback : key_value_invalid_callback + )(node); + if (!ret) { + Logger::error("Error parsing condition node:\n", node); + } + return ret; + }; +} + +node_callback_t ConditionManager::expect_condition_script( + GameManager const& game_manager, scope_t initial_scope, scope_t this_scope, scope_t from_scope, + callback_t callback +) const { + return [this, &game_manager, initial_scope, this_scope, from_scope, callback](ast::NodeCPtr node) -> bool { + + ConditionNode::condition_list_t conds; + bool ret = expect_condition_node_list( + game_manager, + this_scope, + from_scope, + initial_scope, + true, + NodeTools::vector_callback(conds) + )(node); + + ret &= callback({ root_condition, std::move(conds), true }); + + return ret; + }; +} diff --git a/src/openvic-simulation/scripts/Condition.hpp b/src/openvic-simulation/scripts/Condition.hpp new file mode 100644 index 0000000..55fe8fc --- /dev/null +++ b/src/openvic-simulation/scripts/Condition.hpp @@ -0,0 +1,254 @@ +#pragma once + +#include +#include + +#include "openvic-simulation/types/EnumBitfield.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct ConditionManager; + struct ConditionScript; + struct GameManager; + + 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 + }; + template<> struct enable_bitfield : std::true_type {}; + + // Order matters in this enum, for the fallback system to work + // smaller entities must have smaller integers associated! + enum class scope_t : uint8_t { //TODO: maybe distinguish TRANSPARENT from NO_SCOPE + 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 + }; + template<> struct enable_bitfield : std::true_type {}; + + enum class identifier_type_t : uint32_t { + NONE = 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, + MODIFIER = 1 << 23, + NATIONAL_VALUE = 1 << 24, + CULTURE_UNION = 1 << 25, // same as COUNTRY_TAG but also accepts scope this_union + CONTINENT = 1 << 26, + CRIME = 1 << 27, + TERRAIN = 1 << 28, + }; + template<> struct enable_bitfield : std::true_type {}; + + /* Returns true if the values have any bit in common. */ + constexpr inline bool share_value_type(value_type_t lhs, value_type_t rhs) { + return (lhs & rhs) != value_type_t::NO_TYPE; + } + constexpr inline bool share_scope(scope_t lhs, scope_t rhs) { + return (lhs & rhs) != scope_t::NO_SCOPE; + } + constexpr inline bool share_identifier_type(identifier_type_t lhs, identifier_type_t rhs) { + return (lhs & rhs) != identifier_type_t::NONE; + } + +#define _BUILD_STRING(entry, share) if (share(value, entry)) { ret += #entry " | "; } + +#define BUILD_STRING(entry) _BUILD_STRING(entry, share_value_type) + + inline std::string get_value_type_string(value_type_t value) { + using enum value_type_t; + if (value == NO_TYPE) { + return "[NO_TYPE]"; + } + std::string ret = {}; + 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); + return "[" + ret.substr(0, ret.length() - 3) + "]"; + } + +#undef BUILD_STRING +#define BUILD_STRING(entry) _BUILD_STRING(entry, share_scope) + + inline std::string get_scope_string(scope_t value) { + using enum scope_t; + if (value == NO_SCOPE) { + return "[NO_SCOPE]"; + } + std::string ret = {}; + BUILD_STRING(COUNTRY); + BUILD_STRING(STATE); + BUILD_STRING(PROVINCE); + BUILD_STRING(POP); + BUILD_STRING(THIS); + BUILD_STRING(FROM); + return "[" + ret.substr(0, ret.length() - 3) + "]"; + } + +#undef BUILD_STRING +#define BUILD_STRING(entry) _BUILD_STRING(entry, share_identifier_type) + + inline std::string get_identifier_type_string(identifier_type_t value) { + using enum identifier_type_t; + if (value == NONE) { + return "[NONE]"; + } + std::string ret = {}; + 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(MODIFIER); + BUILD_STRING(NATIONAL_VALUE); + BUILD_STRING(CULTURE_UNION); + BUILD_STRING(CONTINENT); + BUILD_STRING(CRIME); + BUILD_STRING(TERRAIN); + return "[" + ret.substr(0, ret.length() - 3) + "]"; + } + +#undef BUILD_STRING +#undef _BUILD_STRING + + struct Condition : HasIdentifier { + friend struct ConditionManager; + using enum identifier_type_t; + + private: + const value_type_t PROPERTY(value_type); + const scope_t PROPERTY(scope); + const scope_t PROPERTY(scope_change); + const identifier_type_t PROPERTY(key_identifier_type); + const identifier_type_t PROPERTY(value_identifier_type); + + Condition( + std::string_view new_identifier, value_type_t new_value_type, scope_t new_scope, + scope_t new_scope_change, identifier_type_t new_key_identifier_type, + identifier_type_t new_value_identifier_type + ); + + public: + Condition(Condition&&) = default; + }; + + struct ConditionNode { + friend struct ConditionManager; + friend struct ConditionScript; + + using string_t = std::string; + using boolean_t = bool; + using double_boolean_t = std::pair; + using integer_t = uint64_t; + using real_t = fixed_point_t; + using identifier_real_t = std::pair; + using condition_list_t = std::vector; + using value_t = std::variant; + + 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); + + 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 + ); + }; + + struct ConditionManager { + private: + CaseInsensitiveIdentifierRegistry IDENTIFIER_REGISTRY(condition); + Condition const* root_condition = nullptr; + + bool add_condition( + std::string_view identifier, value_type_t value_type, scope_t scope, + scope_t scope_change = scope_t::NO_SCOPE, + identifier_type_t key_identifier_type = identifier_type_t::NONE, + identifier_type_t value_identifier_type = identifier_type_t::NONE + ); + + NodeTools::callback_t expect_parse_identifier( + GameManager const& game_manager, identifier_type_t identifier_type, + NodeTools::callback_t callback + ) const; + + NodeTools::node_callback_t expect_condition_node( + GameManager const& game_manager, Condition const& condition, scope_t this_scope, + scope_t from_scope, scope_t cur_scope, NodeTools::callback_t callback + ) const; + + NodeTools::node_callback_t expect_condition_node_list( + GameManager const& game_manager, scope_t this_scope, scope_t from_scope, + scope_t cur_scope, bool top_scope, NodeTools::callback_t callback + ) const; + + public: + bool setup_conditions(GameManager const& game_manager); + + NodeTools::node_callback_t expect_condition_script( + GameManager const& game_manager, scope_t initial_scope, scope_t this_scope, scope_t from_scope, + NodeTools::callback_t callback + ) const; + }; +} diff --git a/src/openvic-simulation/scripts/ConditionScript.cpp b/src/openvic-simulation/scripts/ConditionScript.cpp index af2faf7..368054b 100644 --- a/src/openvic-simulation/scripts/ConditionScript.cpp +++ b/src/openvic-simulation/scripts/ConditionScript.cpp @@ -1,8 +1,20 @@ #include "ConditionScript.hpp" +#include "openvic-simulation/GameManager.hpp" + using namespace OpenVic; +using namespace OpenVic::NodeTools; + +ConditionScript::ConditionScript( + scope_t new_initial_scope, scope_t new_this_scope, scope_t new_from_scope +) : initial_scope { new_initial_scope }, this_scope { new_this_scope }, from_scope { new_from_scope } {} bool ConditionScript::_parse_script(ast::NodeCPtr root, GameManager const& game_manager) { - // TODO - parse condition script - return true; + return game_manager.get_script_manager().get_condition_manager().expect_condition_script( + game_manager, + initial_scope, + this_scope, + from_scope, + move_variable_callback(condition_root) + )(root); } diff --git a/src/openvic-simulation/scripts/ConditionScript.hpp b/src/openvic-simulation/scripts/ConditionScript.hpp index cb967a2..4c7d01f 100644 --- a/src/openvic-simulation/scripts/ConditionScript.hpp +++ b/src/openvic-simulation/scripts/ConditionScript.hpp @@ -1,12 +1,23 @@ #pragma once +#include "openvic-simulation/scripts/Condition.hpp" #include "openvic-simulation/scripts/Script.hpp" namespace OpenVic { struct GameManager; struct ConditionScript final : Script { + + private: + ConditionNode PROPERTY_REF(condition_root); + scope_t PROPERTY(initial_scope); + scope_t PROPERTY(this_scope); + scope_t PROPERTY(from_scope); + protected: bool _parse_script(ast::NodeCPtr root, GameManager const& game_manager) override; + + public: + ConditionScript(scope_t new_initial_scope, scope_t new_this_scope, scope_t new_from_scope); }; } diff --git a/src/openvic-simulation/scripts/ConditionalWeight.cpp b/src/openvic-simulation/scripts/ConditionalWeight.cpp index 17bbbd6..e5bb7d0 100644 --- a/src/openvic-simulation/scripts/ConditionalWeight.cpp +++ b/src/openvic-simulation/scripts/ConditionalWeight.cpp @@ -3,9 +3,12 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; +ConditionalWeight::ConditionalWeight(scope_t new_initial_scope, scope_t new_this_scope, scope_t new_from_scope) + : initial_scope { new_initial_scope }, this_scope { new_this_scope }, from_scope { new_from_scope } {} + template -static NodeCallback auto expect_modifier(std::vector& items) { - return [&items](ast::NodeCPtr node) -> bool { +static NodeCallback auto expect_modifier(std::vector& items, scope_t initial_scope, scope_t this_scope, scope_t from_scope) { + return [&items, initial_scope, this_scope, from_scope](ast::NodeCPtr node) -> bool { fixed_point_t weight = 0; bool successful = false; bool ret = expect_key("factor", expect_fixed_point(assign_variable_callback(weight)), &successful)(node); @@ -13,7 +16,7 @@ static NodeCallback auto expect_modifier(std::vector& items) { Logger::info("ConditionalWeight modifier missing factor key!"); return false; } - ConditionScript condition; + ConditionScript condition { initial_scope, this_scope, from_scope }; ret &= condition.expect_script()(node); items.emplace_back(std::make_pair(weight, std::move(condition))); return ret; @@ -26,11 +29,11 @@ node_callback_t ConditionalWeight::expect_conditional_weight(base_key_t base_key base_key_to_string(base_key), ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(base)), "days", ZERO_OR_ONE, success_callback, "years", ZERO_OR_ONE, success_callback, - "modifier", ZERO_OR_MORE, expect_modifier(condition_weight_items), + "modifier", ZERO_OR_MORE, expect_modifier(condition_weight_items, initial_scope, this_scope, from_scope), "group", ZERO_OR_MORE, [this](ast::NodeCPtr node) -> bool { condition_weight_group_t items; const bool ret = expect_dictionary_keys( - "modifier", ONE_OR_MORE, expect_modifier(items) + "modifier", ONE_OR_MORE, expect_modifier(items, initial_scope, this_scope, from_scope) )(node); if (!items.empty()) { condition_weight_items.emplace_back(std::move(items)); diff --git a/src/openvic-simulation/scripts/ConditionalWeight.hpp b/src/openvic-simulation/scripts/ConditionalWeight.hpp index a965ae1..7453fe9 100644 --- a/src/openvic-simulation/scripts/ConditionalWeight.hpp +++ b/src/openvic-simulation/scripts/ConditionalWeight.hpp @@ -21,11 +21,14 @@ namespace OpenVic { private: fixed_point_t PROPERTY(base); std::vector PROPERTY(condition_weight_items); + scope_t PROPERTY(initial_scope); + scope_t PROPERTY(this_scope); + scope_t PROPERTY(from_scope); struct parse_scripts_visitor_t; public: - ConditionalWeight() = default; + ConditionalWeight(scope_t new_initial_scope, scope_t new_this_scope, scope_t new_from_scope); ConditionalWeight(ConditionalWeight&&) = default; static constexpr std::string_view base_key_to_string(base_key_t base_key) { diff --git a/src/openvic-simulation/scripts/ScriptManager.hpp b/src/openvic-simulation/scripts/ScriptManager.hpp new file mode 100644 index 0000000..3c15657 --- /dev/null +++ b/src/openvic-simulation/scripts/ScriptManager.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "openvic-simulation/scripts/Condition.hpp" + +namespace OpenVic { + struct ScriptManager { + private: + ConditionManager PROPERTY_REF(condition_manager); + }; +} -- cgit v1.2.3-56-ga3b1