aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author zaaarf <80046572+zaaarf@users.noreply.github.com>2024-01-07 23:51:06 +0100
committer GitHub <noreply@github.com>2024-01-07 23:51:06 +0100
commit79b8b73304753fedab822e6aa859fa15673f52cc (patch)
treecb25cfb89f8b06fc34f5c906e658a1b322120089
parent400469f08f10ffd044d9948c3f0db340c8a60db0 (diff)
parent6f07de81a6ca430c522527958e05440d67b04937 (diff)
Merge pull request #111 from OpenVicProject/conditions-checking
Condition definition checking/loading
-rw-r--r--src/openvic-simulation/GameManager.hpp7
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp74
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.cpp10
-rw-r--r--src/openvic-simulation/dataloader/NodeTools.hpp11
-rw-r--r--src/openvic-simulation/economy/ProductionType.cpp4
-rw-r--r--src/openvic-simulation/map/Crime.cpp2
-rw-r--r--src/openvic-simulation/military/Wargoal.cpp10
-rw-r--r--src/openvic-simulation/misc/Decision.cpp5
-rw-r--r--src/openvic-simulation/misc/Event.cpp51
-rw-r--r--src/openvic-simulation/misc/Event.hpp4
-rw-r--r--src/openvic-simulation/misc/Modifier.cpp2
-rw-r--r--src/openvic-simulation/politics/Ideology.cpp8
-rw-r--r--src/openvic-simulation/politics/Issue.cpp3
-rw-r--r--src/openvic-simulation/politics/NationalFocus.cpp4
-rw-r--r--src/openvic-simulation/politics/Rebel.cpp7
-rw-r--r--src/openvic-simulation/pop/Pop.cpp25
-rw-r--r--src/openvic-simulation/research/Invention.cpp5
-rw-r--r--src/openvic-simulation/research/Technology.cpp2
-rw-r--r--src/openvic-simulation/scripts/Condition.cpp753
-rw-r--r--src/openvic-simulation/scripts/Condition.hpp254
-rw-r--r--src/openvic-simulation/scripts/ConditionScript.cpp16
-rw-r--r--src/openvic-simulation/scripts/ConditionScript.hpp11
-rw-r--r--src/openvic-simulation/scripts/ConditionalWeight.cpp13
-rw-r--r--src/openvic-simulation/scripts/ConditionalWeight.hpp5
-rw-r--r--src/openvic-simulation/scripts/ScriptManager.hpp10
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 9f8b6c4..6370786 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);
+ };
+}