diff options
Diffstat (limited to 'src')
25 files changed, 1177 insertions, 119 deletions
diff --git a/src/openvic-simulation/GameManager.hpp b/src/openvic-simulation/GameManager.hpp index 571b4bc..66b7e57 100644 --- a/src/openvic-simulation/GameManager.hpp +++ b/src/openvic-simulation/GameManager.hpp @@ -1,6 +1,5 @@ #pragma once -#include "openvic-simulation/misc/Decision.hpp" #include "openvic-simulation/country/Country.hpp" #include "openvic-simulation/diplomacy/DiplomacyManager.hpp" #include "openvic-simulation/economy/EconomyManager.hpp" @@ -9,14 +8,15 @@ #include "openvic-simulation/map/Crime.hpp" #include "openvic-simulation/map/Map.hpp" #include "openvic-simulation/military/MilitaryManager.hpp" +#include "openvic-simulation/misc/Decision.hpp" #include "openvic-simulation/misc/Define.hpp" +#include "openvic-simulation/misc/Event.hpp" #include "openvic-simulation/misc/Modifier.hpp" #include "openvic-simulation/misc/SimulationClock.hpp" #include "openvic-simulation/politics/PoliticsManager.hpp" #include "openvic-simulation/pop/Pop.hpp" #include "openvic-simulation/research/ResearchManager.hpp" -#include "openvic-simulation/research/Technology.hpp" -#include "openvic-simulation/misc/Event.hpp" +#include "openvic-simulation/scripts/ScriptManager.hpp" namespace OpenVic { struct GameManager { @@ -38,6 +38,7 @@ namespace OpenVic { DecisionManager PROPERTY_REF(decision_manager); UIManager PROPERTY_REF(ui_manager); DiplomacyManager PROPERTY_REF(diplomacy_manager); + ScriptManager PROPERTY_REF(script_manager); SimulationClock PROPERTY_REF(simulation_clock); time_t session_start; /* SS-54, as well as allowing time-tracking */ diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 223b795..fd4f2b0 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -948,69 +948,41 @@ bool Dataloader::load_defines(GameManager& game_manager) { Logger::error("Failed to load diplomatic actions!"); ret = false; } + if(!game_manager.get_script_manager().get_condition_manager().setup_conditions(game_manager)) { + Logger::error("Failed to set up conditions!"); + ret = false; + } - parse_scripts(game_manager); + ret &= parse_scripts(game_manager); free_cache(); return ret; } +#define PARSE_SCRIPTS(name, mgr) \ + if (!mgr.parse_scripts(game_manager)) { Logger::error("Failed to parse ", name, " scripts!"); ret = false; } \ + else Logger::info("Successfully parsed ", name, " scripts!"); + bool Dataloader::parse_scripts(GameManager& game_manager) const { bool ret = true; - - if (!game_manager.get_pop_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse pop scripts!"); - ret = false; - } - if (!game_manager.get_politics_manager().get_ideology_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse ideology scripts!"); - ret = false; - } - if (!game_manager.get_politics_manager().get_issue_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse reform scripts!"); - ret = false; - } - if (!game_manager.get_economy_manager().get_production_type_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse production type scripts!"); - ret = false; - } - if (!game_manager.get_politics_manager().get_rebel_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse rebel type scripts!"); - ret = false; - } - if (!game_manager.get_research_manager().get_technology_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse technology scripts!"); - ret = false; - } - if (!game_manager.get_crime_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse crime scripts!"); - ret = false; - } - if (!game_manager.get_modifier_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse triggered modifier scripts!"); - ret = false; - } - if (!game_manager.get_research_manager().get_invention_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse invention scripts!"); - ret = false; - } - if (!game_manager.get_military_manager().get_wargoal_type_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse wargoal type scripts!"); - ret = false; - } - if (!game_manager.get_decision_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse decision scripts!"); - ret = false; - } - if (!game_manager.get_event_manager().parse_scripts(game_manager)) { - Logger::error("Failed to parse event scripts!"); - ret = false; - } - + PARSE_SCRIPTS("pop", game_manager.get_pop_manager()); + PARSE_SCRIPTS("ideology", game_manager.get_politics_manager().get_ideology_manager()); + PARSE_SCRIPTS("reform", game_manager.get_politics_manager().get_issue_manager()); + PARSE_SCRIPTS("production type", game_manager.get_economy_manager().get_production_type_manager()); + PARSE_SCRIPTS("rebel type", game_manager.get_politics_manager().get_rebel_manager()); + PARSE_SCRIPTS("technology", game_manager.get_research_manager().get_technology_manager()); + PARSE_SCRIPTS("crime", game_manager.get_crime_manager()); + PARSE_SCRIPTS("triggered modifier", game_manager.get_modifier_manager()); + PARSE_SCRIPTS("invention", game_manager.get_research_manager().get_invention_manager()); + PARSE_SCRIPTS("wargoal type", game_manager.get_military_manager().get_wargoal_type_manager()); + PARSE_SCRIPTS("decision", game_manager.get_decision_manager()); + PARSE_SCRIPTS("event", game_manager.get_event_manager()); return ret; } +#undef PARSE_SCRIPTS + static bool _load_localisation_file(Dataloader::localisation_callback_t callback, std::vector<csv::LineObject> const& lines) { bool ret = true; for (csv::LineObject const& line : lines) { diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index e429ed9..4612f77 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -70,13 +70,17 @@ node_callback_t NodeTools::expect_identifier_or_string(callback_t<std::string_vi } node_callback_t NodeTools::expect_bool(callback_t<bool> callback) { - static const string_map_t<bool> bool_map = { { "yes", true }, { "no", false } }; + static const case_insensitive_string_map_t<bool> bool_map = { { "yes", true }, { "no", false } }; return expect_identifier(expect_mapped_string(bool_map, callback)); } node_callback_t NodeTools::expect_int_bool(callback_t<bool> callback) { - static const string_map_t<bool> bool_map = { { "1", true }, { "0", false } }; - return expect_identifier(expect_mapped_string(bool_map, callback)); + return expect_uint64([callback](uint64_t num) -> bool { + if (num > 1) { + Logger::warning("Found int bool with value >1: ", num); + } + return callback(num != 0); + }); } node_callback_t NodeTools::expect_int64(callback_t<int64_t> callback, int base) { diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 8ad731b..00ba02d 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -21,9 +21,12 @@ namespace OpenVic { * searched for without needing to be copied into a string */ template<typename T, class Hash = container_hash<std::string>, class KeyEqual = std::equal_to<>> using string_map_t = ordered_map<std::string, T, Hash, KeyEqual>; + template<typename T> + using case_insensitive_string_map_t = string_map_t<T, case_insensitive_string_hash, case_insensitive_string_equal>; /* String set type supporting heterogeneous key lookup */ using string_set_t = ordered_set<std::string>; + using case_insensitive_string_set_t = case_insensitive_ordered_set<std::string>; namespace NodeTools { @@ -247,10 +250,12 @@ namespace OpenVic { node_callback_t name_list_callback(callback_t<std::vector<std::string>&&> callback); - template<typename T> - Callback<std::string_view> auto expect_mapped_string(string_map_t<T> const& map, Callback<T> auto callback) { + template<typename T, class Hash, class KeyEqual> + Callback<std::string_view> auto expect_mapped_string( + string_map_t<T, Hash, KeyEqual> const& map, Callback<T> auto callback + ) { return [&map, callback](std::string_view string) -> bool { - const typename string_map_t<T>::const_iterator it = map.find(string); + const typename string_map_t<T, Hash, KeyEqual>::const_iterator it = map.find(string); if (it != map.end()) { return callback(it->second); } diff --git a/src/openvic-simulation/economy/ProductionType.cpp b/src/openvic-simulation/economy/ProductionType.cpp index 4714ee5..63a651b 100644 --- a/src/openvic-simulation/economy/ProductionType.cpp +++ b/src/openvic-simulation/economy/ProductionType.cpp @@ -1,7 +1,5 @@ #include "ProductionType.hpp" -#include "openvic-simulation/types/OrderedContainers.hpp" - using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -197,7 +195,7 @@ bool ProductionTypeManager::load_production_types_file( const node_callback_t parse_node = expect_dictionary_keys( "template", ZERO_OR_ONE, success_callback, "bonus", ZERO_OR_MORE, [&bonuses](ast::NodeCPtr bonus_node) -> bool { - ConditionScript trigger; + ConditionScript trigger { scope_t::STATE, scope_t::NO_SCOPE, scope_t::NO_SCOPE }; fixed_point_t value {}; const bool ret = expect_dictionary_keys( "trigger", ONE_EXACTLY, trigger.expect_script(), diff --git a/src/openvic-simulation/map/Crime.cpp b/src/openvic-simulation/map/Crime.cpp index 65760a3..3f5a718 100644 --- a/src/openvic-simulation/map/Crime.cpp +++ b/src/openvic-simulation/map/Crime.cpp @@ -27,7 +27,7 @@ bool CrimeManager::load_crime_modifiers(ModifierManager const& modifier_manager, [this, &modifier_manager](std::string_view key, ast::NodeCPtr value) -> bool { ModifierValue modifier_value; Modifier::icon_t icon = 0; - ConditionScript trigger; + ConditionScript trigger { scope_t::PROVINCE, scope_t::NO_SCOPE, scope_t::NO_SCOPE }; bool default_active = false; bool ret = modifier_manager.expect_modifier_value_and_keys( move_variable_callback(modifier_value), diff --git a/src/openvic-simulation/military/Wargoal.cpp b/src/openvic-simulation/military/Wargoal.cpp index 28cbc51..174cd09 100644 --- a/src/openvic-simulation/military/Wargoal.cpp +++ b/src/openvic-simulation/military/Wargoal.cpp @@ -84,9 +84,13 @@ bool WargoalTypeManager::load_wargoal_file(ast::NodeCPtr root) { mutual = false, all_allowed_states = false, always = false; peace_options_t peace_options = NO_PEACE_OPTIONS; WargoalType::peace_modifiers_t modifiers; - ConditionScript can_use, is_valid, allowed_states, allowed_substate_regions, allowed_states_in_crisis, - allowed_countries; - EffectScript on_add, on_po_accepted; + ConditionScript can_use { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::COUNTRY }; + ConditionScript is_valid { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::COUNTRY }; + ConditionScript allowed_states { scope_t::STATE, scope_t::COUNTRY, scope_t::COUNTRY }; + ConditionScript allowed_substate_regions { scope_t::STATE, scope_t::COUNTRY, scope_t::COUNTRY }; + ConditionScript allowed_states_in_crisis { scope_t::STATE, scope_t::COUNTRY, scope_t::COUNTRY }; + ConditionScript allowed_countries { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::COUNTRY }; + EffectScript on_add, on_po_accepted; //country as default scope for both const auto expect_peace_option = [&peace_options](peace_options_t peace_option) -> node_callback_t { return expect_bool([&peace_options, peace_option](bool val) -> bool { diff --git a/src/openvic-simulation/misc/Decision.cpp b/src/openvic-simulation/misc/Decision.cpp index 36eb871..df78c80 100644 --- a/src/openvic-simulation/misc/Decision.cpp +++ b/src/openvic-simulation/misc/Decision.cpp @@ -56,8 +56,9 @@ bool DecisionManager::load_decision_file(ast::NodeCPtr root) { [this](std::string_view identifier, ast::NodeCPtr node) -> bool { bool alert = true, news = false; std::string_view news_title, news_desc_long, news_desc_medium, news_desc_short, picture; - ConditionScript potential, allow; - ConditionalWeight ai_will_do; + ConditionScript potential { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionScript allow { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight ai_will_do { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; EffectScript effect; bool ret = expect_dictionary_keys( "alert", ZERO_OR_ONE, expect_bool(assign_variable_callback(alert)), diff --git a/src/openvic-simulation/misc/Event.cpp b/src/openvic-simulation/misc/Event.cpp index fb97d63..eb5f7da 100644 --- a/src/openvic-simulation/misc/Event.cpp +++ b/src/openvic-simulation/misc/Event.cpp @@ -6,8 +6,8 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -Event::EventOption::EventOption(std::string_view new_title, EffectScript&& new_effect, ConditionalWeight&& new_ai_chance) - : title { new_title }, effect { std::move(new_effect) }, ai_chance { std::move(new_ai_chance) } {} +Event::EventOption::EventOption(std::string_view new_name, EffectScript&& new_effect, ConditionalWeight&& new_ai_chance) + : name { new_name }, effect { std::move(new_effect) }, ai_chance { std::move(new_ai_chance) } {} bool Event::EventOption::parse_scripts(GameManager& game_manager) { bool ret = true; @@ -86,6 +86,17 @@ bool EventManager::register_event( } } + if (options.empty()) { + Logger::error("Event with ID ", identifier, " has no options!"); + return false; + } else { + for (Event::EventOption const& option : options) { + if (option.name.empty()) { + Logger::warning("Event with ID ", identifier, " has an option with no name!"); + } + } + } + // TODO - error if is_triggered_only with triggers or MTTH defined return events.add_item({ @@ -108,25 +119,29 @@ bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeC return expect_dictionary( [this, &issue_manager](std::string_view key, ast::NodeCPtr value) -> bool { Event::event_type_t type; - std::string_view identifier, title, description, image, news_title, news_desc_long, news_desc_medium, - news_desc_short; - bool triggered_only = false, major = false, fire_only_once = false, allows_multiple_instances = false, - news = false, election = false; - IssueGroup const* election_issue_group = nullptr; - ConditionScript trigger; - ConditionalWeight mean_time_to_happen; - EffectScript immediate; - std::vector<Event::EventOption> options; + scope_t initial_scope; if (key == "country_event") { type = Event::event_type_t::COUNTRY; + initial_scope = scope_t::COUNTRY; } else if (key == "province_event") { type = Event::event_type_t::PROVINCE; + initial_scope = scope_t::PROVINCE; } else { Logger::error("Invalid event type: ", key); return false; } + std::string_view identifier, title, description, image, news_title, news_desc_long, news_desc_medium, + news_desc_short; + bool triggered_only = false, major = false, fire_only_once = false, allows_multiple_instances = false, + news = false, election = false; + IssueGroup const* election_issue_group = nullptr; + ConditionScript trigger { initial_scope, initial_scope, scope_t::NO_SCOPE }; + ConditionalWeight mean_time_to_happen { initial_scope, initial_scope, scope_t::NO_SCOPE }; + EffectScript immediate; + std::vector<Event::EventOption> options; + bool ret = expect_dictionary_keys( "id", ONE_EXACTLY, expect_identifier(assign_variable_callback(identifier)), "title", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(title)), @@ -144,20 +159,24 @@ bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeC "election", ZERO_OR_ONE, expect_bool(assign_variable_callback(election)), "issue_group", ZERO_OR_ONE, issue_manager.expect_issue_group_identifier(assign_variable_callback_pointer(election_issue_group)), - "option", ONE_OR_MORE, [&options](ast::NodeCPtr node) -> bool { - std::string_view title; + "option", ONE_OR_MORE, [&options, initial_scope](ast::NodeCPtr node) -> bool { + std::string_view name; EffectScript effect; - ConditionalWeight ai_chance; + ConditionalWeight ai_chance { + initial_scope, + initial_scope, + scope_t::COUNTRY | scope_t::PROVINCE + }; bool ret = expect_dictionary_keys_and_default( key_value_success_callback, - "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(title)), + "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(name)), "ai_chance", ZERO_OR_ONE, ai_chance.expect_conditional_weight(ConditionalWeight::FACTOR) )(node); ret &= effect.expect_script()(node); - options.push_back({ title, std::move(effect), std::move(ai_chance) }); + options.push_back({ name, std::move(effect), std::move(ai_chance) }); return ret; }, "trigger", ZERO_OR_ONE, trigger.expect_script(), diff --git a/src/openvic-simulation/misc/Event.hpp b/src/openvic-simulation/misc/Event.hpp index 37f9270..72e96fd 100644 --- a/src/openvic-simulation/misc/Event.hpp +++ b/src/openvic-simulation/misc/Event.hpp @@ -20,11 +20,11 @@ namespace OpenVic { friend struct Event; private: - std::string PROPERTY(title); + std::string PROPERTY(name); EffectScript PROPERTY(effect); ConditionalWeight PROPERTY(ai_chance); - EventOption(std::string_view new_title, EffectScript&& new_effect, ConditionalWeight&& new_ai_chance); + EventOption(std::string_view new_name, EffectScript&& new_effect, ConditionalWeight&& new_ai_chance); bool parse_scripts(GameManager& game_manager); diff --git a/src/openvic-simulation/misc/Modifier.cpp b/src/openvic-simulation/misc/Modifier.cpp index ca4c950..c83fcf0 100644 --- a/src/openvic-simulation/misc/Modifier.cpp +++ b/src/openvic-simulation/misc/Modifier.cpp @@ -344,7 +344,7 @@ bool ModifierManager::load_triggered_modifiers(ast::NodeCPtr root) { [this](std::string_view key, ast::NodeCPtr value) -> bool { ModifierValue modifier_value; Modifier::icon_t icon = 0; - ConditionScript trigger; + ConditionScript trigger { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; bool ret = expect_modifier_value_and_keys( move_variable_callback(modifier_value), diff --git a/src/openvic-simulation/politics/Ideology.cpp b/src/openvic-simulation/politics/Ideology.cpp index 2678bee..692b809 100644 --- a/src/openvic-simulation/politics/Ideology.cpp +++ b/src/openvic-simulation/politics/Ideology.cpp @@ -85,8 +85,12 @@ bool IdeologyManager::load_ideology_file(ast::NodeCPtr root) { colour_t colour = colour_t::null(); bool uncivilised = true, can_reduce_militancy = false; Date spawn_date; - ConditionalWeight add_political_reform, remove_political_reform, add_social_reform, remove_social_reform, - add_military_reform, add_economic_reform; + ConditionalWeight add_political_reform { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight remove_political_reform { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight add_social_reform { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight remove_social_reform { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight add_military_reform { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight add_economic_reform { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; bool ret = expect_dictionary_keys( "uncivilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(uncivilised)), diff --git a/src/openvic-simulation/politics/Issue.cpp b/src/openvic-simulation/politics/Issue.cpp index 28e34cb..30dc1cd 100644 --- a/src/openvic-simulation/politics/Issue.cpp +++ b/src/openvic-simulation/politics/Issue.cpp @@ -159,7 +159,8 @@ bool IssueManager::_load_reform( ModifierValue values; RuleSet rules; Reform::tech_cost_t technology_cost = 0; - ConditionScript allow, on_execute_trigger; + ConditionScript allow { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionScript on_execute_trigger { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; EffectScript on_execute_effect; bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(values), diff --git a/src/openvic-simulation/politics/NationalFocus.cpp b/src/openvic-simulation/politics/NationalFocus.cpp index d4066af..e509039 100644 --- a/src/openvic-simulation/politics/NationalFocus.cpp +++ b/src/openvic-simulation/politics/NationalFocus.cpp @@ -1,7 +1,5 @@ #include "NationalFocus.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" - using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -74,7 +72,7 @@ bool NationalFocusManager::load_national_foci_file(PopManager const& pop_manager NationalFocus::pop_promotion_map_t promotions; NationalFocus::party_loyalty_map_t loyalties; NationalFocus::production_map_t production; - ConditionScript limit; + ConditionScript limit { scope_t::PROVINCE | scope_t::COUNTRY, scope_t::PROVINCE | scope_t::COUNTRY, scope_t::NO_SCOPE }; Ideology const* last_specified_ideology = nullptr; // weird, I know diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp index ca8e945..7181085 100644 --- a/src/openvic-simulation/politics/Rebel.cpp +++ b/src/openvic-simulation/politics/Rebel.cpp @@ -104,8 +104,11 @@ bool RebelManager::load_rebels_file( allow_all_religions = true, allow_all_ideologies = true, resilient = true, reinforcing = true, general = true, smart = true, unit_transfer = false; fixed_point_t occupation_mult = 0; - ConditionalWeight will_rise, spawn_chance, movement_evaluation; - ConditionScript siege_won_trigger, demands_enforced_trigger; + ConditionalWeight will_rise { scope_t::POP, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight spawn_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }; + ConditionalWeight movement_evaluation { scope_t::PROVINCE, scope_t::PROVINCE, scope_t::NO_SCOPE }; + ConditionScript siege_won_trigger { scope_t::PROVINCE, scope_t::PROVINCE, scope_t::NO_SCOPE }; + ConditionScript demands_enforced_trigger { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; EffectScript siege_won_effect, demands_enforced_effect; bool ret = expect_dictionary_keys( diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index dd5bd83..ce310e1 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -1,14 +1,8 @@ #include "Pop.hpp" -#include <cassert> - -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/map/Province.hpp" #include "openvic-simulation/politics/Ideology.hpp" #include "openvic-simulation/politics/Issue.hpp" #include "openvic-simulation/politics/Rebel.hpp" -#include "openvic-simulation/types/Colour.hpp" -#include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/TslHelper.hpp" using namespace OpenVic; @@ -67,7 +61,15 @@ bool PopType::parse_scripts(GameManager const& game_manager) { return ret; } -PopManager::PopManager() : slave_sprite { 0 }, administrative_sprite { 0 } {} +PopManager::PopManager() + : slave_sprite { 0 }, administrative_sprite { 0 }, + promotion_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }, + demotion_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }, + migration_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }, + colonialmigration_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }, + emigration_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }, + assimilation_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }, + conversion_chance { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE } {} bool PopManager::add_strata(std::string_view identifier) { if (identifier.empty()) { @@ -148,7 +150,8 @@ bool PopManager::load_pop_type_file( can_be_recruited = false, can_reduce_consciousness = false, administrative_efficiency = false, can_build = false, factory = false, can_work_factory = false, unemployment = false; Pop::pop_size_t max_size = 0, merge_max_size = 0; - ConditionalWeight country_migration_target, migration_target; + ConditionalWeight country_migration_target { scope_t::COUNTRY, scope_t::POP, scope_t::NO_SCOPE }; + ConditionalWeight migration_target { scope_t::PROVINCE, scope_t::POP, scope_t::NO_SCOPE }; ast::NodeCPtr promote_to_node = nullptr; PopType::ideology_weight_map_t ideologies; ast::NodeCPtr issues_node = nullptr; @@ -193,7 +196,7 @@ bool PopManager::load_pop_type_file( "promote_to", ZERO_OR_ONE, assign_variable_callback(promote_to_node), "ideologies", ZERO_OR_ONE, ideology_manager.expect_ideology_dictionary( [&filestem, &ideologies](Ideology const& ideology, ast::NodeCPtr node) -> bool { - ConditionalWeight weight; + ConditionalWeight weight { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }; bool ret = weight.expect_conditional_weight(ConditionalWeight::FACTOR)(node); if (!ideologies.emplace(&ideology, std::move(weight)).second) { Logger::error("Duplicate ideology in pop type ", filestem, " ideology weights: ", ideology); @@ -236,7 +239,7 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana Logger::error("Pop type ", type, " cannot have promotion weight to itself!"); return false; } - ConditionalWeight weight; + ConditionalWeight weight { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }; bool ret = weight.expect_conditional_weight(ConditionalWeight::FACTOR)(node); if (!pop_type->promote_to.emplace(&type, std::move(weight)).second) { Logger::error("Duplicate pop type in pop type ", pop_type, " promotion weights: ", type); @@ -258,7 +261,7 @@ bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_mana Logger::error("Invalid issue in pop type ", pop_type, " issue weights: ", key); return false; } - ConditionalWeight weight; + ConditionalWeight weight { scope_t::POP, scope_t::POP, scope_t::NO_SCOPE }; bool ret = weight.expect_conditional_weight(ConditionalWeight::FACTOR)(node); if (!pop_type->issues.emplace(issue, std::move(weight)).second) { Logger::error("Duplicate issue in pop type ", pop_type, " issue weights: ", issue->get_identifier()); diff --git a/src/openvic-simulation/research/Invention.cpp b/src/openvic-simulation/research/Invention.cpp index 473b0b4..92bf059 100644 --- a/src/openvic-simulation/research/Invention.cpp +++ b/src/openvic-simulation/research/Invention.cpp @@ -2,7 +2,6 @@ #include "openvic-simulation/economy/BuildingType.hpp" #include "openvic-simulation/map/Crime.hpp" -#include "openvic-simulation/military/Unit.hpp" using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -57,8 +56,8 @@ bool InventionManager::load_inventions_file( bool unlock_gas_defence = false; bool news = true; //defaults to true! - ConditionScript limit; - ConditionalWeight chance; + ConditionScript limit { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; + ConditionalWeight chance { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(loose_modifiers), "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), diff --git a/src/openvic-simulation/research/Technology.cpp b/src/openvic-simulation/research/Technology.cpp index b151f1a..4271d48 100644 --- a/src/openvic-simulation/research/Technology.cpp +++ b/src/openvic-simulation/research/Technology.cpp @@ -127,7 +127,7 @@ bool TechnologyManager::load_technologies_file( uint8_t unit = 0; Technology::unit_set_t activated_units; Technology::building_set_t activated_buildings; - ConditionalWeight ai_chance; + ConditionalWeight ai_chance { scope_t::COUNTRY, scope_t::COUNTRY, scope_t::NO_SCOPE }; bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(modifiers), "area", ONE_EXACTLY, expect_technology_area_identifier(assign_variable_callback_pointer(area)), 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<uint64_t>(value_type)); + return false; + } + if (scope == scope_t::NO_SCOPE || scope > scope_t::MAX_SCOPE) { + Logger::error("Condition ", identifier, " has invalid scope: ", static_cast<uint64_t>(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<std::string_view> 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<std::string_view> ConditionManager::expect_parse_identifier( + GameManager const& game_manager, identifier_type_t identifier_type, + callback_t<HasIdentifier const*> 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<ConditionNode&&> 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<ConditionNode&&> 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<ConditionNode&&> 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 <string_view> +#include <variant> + +#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<value_type_t> : 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<scope_t> : 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<identifier_type_t> : 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<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>; + + 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<Condition> 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<std::string_view> expect_parse_identifier( + GameManager const& game_manager, identifier_type_t identifier_type, + NodeTools::callback_t<HasIdentifier const*> 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<ConditionNode&&> 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<ConditionNode&&> 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<ConditionNode&&> 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<GameManager const&> { + + 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<typename T> -static NodeCallback auto expect_modifier(std::vector<T>& items) { - return [&items](ast::NodeCPtr node) -> bool { +static NodeCallback auto expect_modifier(std::vector<T>& 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<T>& 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<condition_weight_item_t> 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); + }; +} |