diff options
Diffstat (limited to 'src')
48 files changed, 1391 insertions, 512 deletions
diff --git a/src/headless/main.cpp b/src/headless/main.cpp index d4c316a..7f33314 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -18,7 +18,7 @@ static void print_help(std::ostream& stream, char const* program_name) { << "(Paths with spaces need to be enclosed in \"quotes\").\n"; } -static bool headless_load(GameManager& game_manager, Dataloader const& dataloader) { +static bool headless_load(GameManager& game_manager, Dataloader& dataloader) { bool ret = true; if (!dataloader.load_defines(game_manager)) { diff --git a/src/openvic-simulation/GameManager.cpp b/src/openvic-simulation/GameManager.cpp index 3f66352..c7dd470 100644 --- a/src/openvic-simulation/GameManager.cpp +++ b/src/openvic-simulation/GameManager.cpp @@ -66,7 +66,6 @@ bool GameManager::load_bookmark(Bookmark const* new_bookmark) { ret &= map.apply_history_to_provinces(history_manager.get_province_manager(), today); map.get_state_manager().generate_states(map); // TODO - apply country history - // TODO - apply pop history return ret; } diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 5da787e..11b1e90 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -3,7 +3,7 @@ using namespace OpenVic; bool CountryInstance::add_accepted_culture(Culture const* new_accepted_culture) { - if(std::find(accepted_cultures.begin(), accepted_cultures.end(), new_accepted_culture) != accepted_cultures.end()) { + if (std::find(accepted_cultures.begin(), accepted_cultures.end(), new_accepted_culture) != accepted_cultures.end()) { Logger::warning("Attempted to add accepted culture ", new_accepted_culture->get_identifier(), " to country ", base_country->get_identifier(), ": already present!"); return false; } diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index f78499c..a09b999 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -261,7 +261,7 @@ static bool _lua_parse(v2script::Parser& parser) { return parser.lua_defines_parse(); } -ovdl::v2script::Parser Dataloader::parse_lua_defines(fs::path const& path) { +v2script::Parser Dataloader::parse_lua_defines(fs::path const& path) { return _run_ovdl_parser<v2script::Parser, &_lua_parse>(path); } @@ -273,6 +273,14 @@ csv::Windows1252Parser Dataloader::parse_csv(fs::path const& path) { return _run_ovdl_parser<csv::Windows1252Parser, &_csv_parse>(path); } +v2script::Parser& Dataloader::parse_defines_cached(fs::path const& path) { + return cached_parsers.emplace_back(parse_defines(path)); +} + +void Dataloader::free_cache() { + cached_parsers.clear(); +} + bool Dataloader::_load_interface_files(UIManager& ui_manager) const { static constexpr std::string_view interface_directory = "interface/"; @@ -291,8 +299,7 @@ bool Dataloader::_load_interface_files(UIManager& ui_manager) const { }; for (std::string_view const& gui_file : gui_files) { if (!ui_manager.load_gui_file( - gui_file, - parse_defines(lookup_file(append_string_views(interface_directory, gui_file))).get_file_node() + gui_file, parse_defines(lookup_file(append_string_views(interface_directory, gui_file))).get_file_node() )) { Logger::error("Failed to load interface gui file: ", gui_file); ret = false; @@ -303,19 +310,21 @@ bool Dataloader::_load_interface_files(UIManager& ui_manager) const { return ret; } -bool Dataloader::_load_pop_types(GameManager& game_manager) const { +bool Dataloader::_load_pop_types(GameManager& game_manager) { PopManager& pop_manager = game_manager.get_pop_manager(); UnitManager const& unit_manager = game_manager.get_military_manager().get_unit_manager(); GoodManager const& good_manager = game_manager.get_economy_manager().get_good_manager(); + IdeologyManager const& ideology_manager = game_manager.get_politics_manager().get_ideology_manager(); static constexpr std::string_view pop_type_directory = "poptypes"; const path_vector_t pop_type_files = lookup_files_in_dir(pop_type_directory, ".txt"); pop_manager.reserve_pop_types(pop_type_files.size()); + bool ret = apply_to_files( pop_type_files, - [&pop_manager, &unit_manager, &good_manager](fs::path const& file) -> bool { + [this, &pop_manager, &unit_manager, &good_manager, &ideology_manager](fs::path const& file) -> bool { return pop_manager.load_pop_type_file( - file.stem().string(), unit_manager, good_manager, parse_defines(file).get_file_node() + file.stem().string(), unit_manager, good_manager, ideology_manager, parse_defines_cached(file).get_file_node() ); } ); @@ -332,6 +341,10 @@ bool Dataloader::_load_pop_types(GameManager& game_manager) const { } ret &= pop_manager.generate_modifiers(game_manager.get_modifier_manager()); + + static constexpr std::string_view pop_type_chances_file = "common/pop_types.txt"; + ret &= pop_manager.load_pop_type_chances_file(parse_defines_cached(lookup_file(pop_type_chances_file)).get_file_node()); + return ret; } @@ -339,16 +352,19 @@ bool Dataloader::_load_units(GameManager& game_manager) const { static constexpr std::string_view units_directory = "units"; UnitManager& unit_manager = game_manager.get_military_manager().get_unit_manager(); + bool ret = apply_to_files( lookup_files_in_dir(units_directory, ".txt"), [&game_manager, &unit_manager](fs::path const& file) -> bool { - return unit_manager.load_unit_file(game_manager.get_economy_manager().get_good_manager(), parse_defines(file).get_file_node()); + return unit_manager.load_unit_file( + game_manager.get_economy_manager().get_good_manager(), parse_defines(file).get_file_node() + ); } ); unit_manager.lock_units(); - if(!unit_manager.generate_modifiers(game_manager.get_modifier_manager())) { + if (!unit_manager.generate_modifiers(game_manager.get_modifier_manager())) { Logger::error("Failed to generate unit-based modifiers!"); ret = false; } @@ -360,9 +376,10 @@ bool Dataloader::_load_goods(GameManager& game_manager) const { static constexpr std::string_view goods_file = "common/goods.txt"; GoodManager& good_manager = game_manager.get_economy_manager().get_good_manager(); + bool ret = good_manager.load_goods_file(parse_defines(lookup_file(goods_file)).get_file_node()); - if(!good_manager.generate_modifiers(game_manager.get_modifier_manager())) { + if (!good_manager.generate_modifiers(game_manager.get_modifier_manager())) { Logger::error("Failed to generate good-based modifiers!"); ret = false; } @@ -370,15 +387,15 @@ bool Dataloader::_load_goods(GameManager& game_manager) const { return ret; } -bool Dataloader::_load_rebel_types(GameManager& game_manager) const { +bool Dataloader::_load_rebel_types(GameManager& game_manager) { static constexpr std::string_view rebel_types_file = "common/rebel_types.txt"; PoliticsManager& politics_manager = game_manager.get_politics_manager(); RebelManager& rebel_manager = politics_manager.get_rebel_manager(); - bool ret = politics_manager.load_rebels_file(parse_defines(lookup_file(rebel_types_file)).get_file_node()); + bool ret = politics_manager.load_rebels_file(parse_defines_cached(lookup_file(rebel_types_file)).get_file_node()); - if(!rebel_manager.generate_modifiers(game_manager.get_modifier_manager())) { + if (!rebel_manager.generate_modifiers(game_manager.get_modifier_manager())) { Logger::error("Failed to generate rebel type-based modifiers!"); ret &= false; } @@ -386,7 +403,7 @@ bool Dataloader::_load_rebel_types(GameManager& game_manager) const { return ret; } -bool Dataloader::_load_technologies(GameManager& game_manager) const { +bool Dataloader::_load_technologies(GameManager& game_manager) { static constexpr std::string_view technology_file = "common/technology.txt"; TechnologyManager& technology_manager = game_manager.get_research_manager().get_technology_manager(); @@ -395,32 +412,32 @@ bool Dataloader::_load_technologies(GameManager& game_manager) const { const v2script::Parser technology_file_parser = parse_defines(lookup_file(technology_file)); - if(!technology_manager.load_technology_file_areas(technology_file_parser.get_file_node())) { + if (!technology_manager.load_technology_file_areas(technology_file_parser.get_file_node())) { Logger::error("Failed to load technology areas and folders!"); ret = false; } ModifierManager& modifier_manager = game_manager.get_modifier_manager(); - if(!technology_manager.generate_modifiers(modifier_manager)) { + if (!technology_manager.generate_modifiers(modifier_manager)) { Logger::error("Failed to generate technollogy-based modifiers!"); ret = false; } - if(!technology_manager.load_technology_file_schools(modifier_manager, technology_file_parser.get_file_node())) { + if (!technology_manager.load_technology_file_schools(modifier_manager, technology_file_parser.get_file_node())) { Logger::error("Failed to load technology schools!"); ret = false; } static constexpr std::string_view technologies_directory = "technologies"; - if(!apply_to_files( + if (!apply_to_files( lookup_files_in_dir(technologies_directory, ".txt"), - [&game_manager, &technology_manager, &modifier_manager](fs::path const& file) -> bool { + [this, &game_manager, &technology_manager, &modifier_manager](fs::path const& file) -> bool { return technology_manager.load_technologies_file( modifier_manager, game_manager.get_military_manager().get_unit_manager(), game_manager.get_economy_manager().get_building_type_manager(), - parse_defines(file).get_file_node() + parse_defines_cached(file).get_file_node() ); } )) { @@ -432,20 +449,20 @@ bool Dataloader::_load_technologies(GameManager& game_manager) const { return ret; } -bool Dataloader::_load_inventions(GameManager& game_manager) const { +bool Dataloader::_load_inventions(GameManager& game_manager) { static constexpr std::string_view inventions_directory = "inventions"; InventionManager& invention_manager = game_manager.get_research_manager().get_invention_manager(); bool ret = apply_to_files( lookup_files_in_dir(inventions_directory, ".txt"), - [&game_manager, &invention_manager](fs::path const& file) -> bool { + [this, &game_manager, &invention_manager](fs::path const& file) -> bool { return invention_manager.load_inventions_file( game_manager.get_modifier_manager(), game_manager.get_military_manager().get_unit_manager(), game_manager.get_economy_manager().get_building_type_manager(), game_manager.get_crime_manager(), - parse_defines(file).get_file_node() + parse_defines_cached(file).get_file_node() ); } ); @@ -455,14 +472,15 @@ bool Dataloader::_load_inventions(GameManager& game_manager) const { return ret; } -bool Dataloader::_load_decisions(GameManager& game_manager) const { +bool Dataloader::_load_decisions(GameManager& game_manager) { static constexpr std::string_view decisions_directory = "decisions"; + DecisionManager& decision_manager = game_manager.get_decision_manager(); bool ret = apply_to_files( lookup_files_in_dir(decisions_directory, ".txt"), - [&decision_manager](fs::path const& file) -> bool { - return decision_manager.load_decision_file(parse_defines(file).get_file_node()); + [this, &decision_manager](fs::path const& file) -> bool { + return decision_manager.load_decision_file(parse_defines_cached(file).get_file_node()); } ); @@ -551,6 +569,7 @@ bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_fi game_manager.get_history_manager().get_province_manager().lock_province_histories(game_manager.get_map(), false); + /* Diplomacy History */ static constexpr std::string_view diplomacy_history_directory = "history/diplomacy"; ret &= apply_to_files( lookup_files_in_dir(diplomacy_history_directory, ".txt"), @@ -560,11 +579,15 @@ bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_fi ); } ); + + /* War History */ static constexpr std::string_view war_history_directory = "history/wars"; ret &= apply_to_files( lookup_files_in_dir(war_history_directory, ".txt"), [this, &game_manager](fs::path const& file) -> bool { - return game_manager.get_history_manager().get_diplomacy_manager().load_war_history_file(game_manager, parse_defines(file).get_file_node()); + return game_manager.get_history_manager().get_diplomacy_manager().load_war_history_file( + game_manager, parse_defines(file).get_file_node() + ); } ); game_manager.get_history_manager().get_diplomacy_manager().lock_diplomatic_history(); @@ -572,7 +595,7 @@ bool Dataloader::_load_history(GameManager& game_manager, bool unused_history_fi return ret; } -bool Dataloader::_load_events(GameManager& game_manager) const { +bool Dataloader::_load_events(GameManager& game_manager) { static constexpr std::string_view events_directory = "events"; const bool ret = apply_to_files( lookup_files_in_dir(events_directory, ".txt"), @@ -595,15 +618,15 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { static constexpr std::string_view default_provinces = "provinces.bmp"; static constexpr std::string_view default_positions = "positions.txt"; static constexpr std::string_view default_terrain = "terrain.bmp"; - static constexpr std::string_view default_rivers = "rivers.bmp"; // TODO + static constexpr std::string_view default_rivers = "rivers.bmp"; // TODO - load rivers into map pixel data static constexpr std::string_view default_terrain_definition = "terrain.txt"; - static constexpr std::string_view default_tree_definition = "trees.txt"; // TODO - static constexpr std::string_view default_continent = "continent.txt"; // TODO + static constexpr std::string_view default_tree_definition = "trees.txt"; /* Tree textures and density values (unused). */ + static constexpr std::string_view default_continent = "continent.txt"; static constexpr std::string_view default_adjacencies = "adjacencies.csv"; static constexpr std::string_view default_region = "region.txt"; - static constexpr std::string_view default_region_sea = "region_sea.txt"; // TODO - static constexpr std::string_view default_province_flag_sprite = "province_flag_sprites"; // TODO - static constexpr std::string_view climate_file = "climate.txt"; // TODO + static constexpr std::string_view default_region_sea = "region_sea.txt"; /* Some empty province sets (unused). */ + static constexpr std::string_view default_province_flag_sprite = "province_flag_sprites"; /* Canal sprite/model names. */ + static constexpr std::string_view climate_file = "climate.txt"; /* Parser stored so the filename string_views persist until the end of this function. */ const v2script::Parser parser = parse_defines(lookup_file(append_string_views(map_directory, defaults_filename))); @@ -720,7 +743,12 @@ bool Dataloader::_load_map_dir(GameManager& game_manager) const { return ret; } -bool Dataloader::load_defines(GameManager& game_manager) const { +bool Dataloader::load_defines(GameManager& game_manager) { + if (roots.empty()) { + Logger::error("Cannot load defines - Dataloader has no roots!"); + return false; + } + static constexpr std::string_view defines_file = "common/defines.lua"; static constexpr std::string_view buildings_file = "common/buildings.txt"; static constexpr std::string_view bookmark_file = "common/bookmarks.txt"; @@ -768,10 +796,6 @@ bool Dataloader::load_defines(GameManager& game_manager) const { Logger::error("Failed to load units!"); ret = false; } - if (!_load_pop_types(game_manager)) { - Logger::error("Failed to load pop types!"); - ret = false; - } if (!game_manager.get_pop_manager().get_culture_manager().load_graphical_culture_type_file( parse_defines(lookup_file(graphical_culture_type_file)).get_file_node() )) { @@ -791,7 +815,7 @@ bool Dataloader::load_defines(GameManager& game_manager) const { ret = false; } if (!game_manager.get_politics_manager().get_ideology_manager().load_ideology_file( - parse_defines(lookup_file(ideology_file)).get_file_node() + parse_defines_cached(lookup_file(ideology_file)).get_file_node() )) { Logger::error("Failed to load ideologies!"); ret = false; @@ -802,15 +826,25 @@ bool Dataloader::load_defines(GameManager& game_manager) const { Logger::error("Failed to load government types!"); ret = false; } + if (!_load_pop_types(game_manager)) { + Logger::error("Failed to load pop types!"); + ret = false; + } if (!game_manager.get_politics_manager().load_issues_file( game_manager.get_modifier_manager(), - parse_defines(lookup_file(issues_file)).get_file_node() + parse_defines_cached(lookup_file(issues_file)).get_file_node() + )) { + Logger::error("Failed to load issues and reforms!"); + ret = false; + } + if (!game_manager.get_pop_manager().load_delayed_parse_pop_type_data( + game_manager.get_politics_manager().get_issue_manager() )) { - Logger::error("Failed to load issues!"); + Logger::error("Failed to load delayed parse pop type data (promotion and issue weights)!"); ret = false; } if (!game_manager.get_economy_manager().load_production_types_file(game_manager.get_pop_manager(), - parse_defines(lookup_file(production_types_file)).get_file_node() + parse_defines_cached(lookup_file(production_types_file)).get_file_node() )) { Logger::error("Failed to load production types!"); ret = false; @@ -830,7 +864,7 @@ bool Dataloader::load_defines(GameManager& game_manager) const { } if (!game_manager.get_politics_manager().load_national_foci_file( game_manager.get_pop_manager(), game_manager.get_economy_manager().get_good_manager(), - game_manager.get_modifier_manager(), parse_defines(lookup_file(national_foci_file)).get_file_node() + game_manager.get_modifier_manager(), parse_defines_cached(lookup_file(national_foci_file)).get_file_node() )) { Logger::error("Failed to load national foci!"); ret = false; @@ -842,7 +876,7 @@ bool Dataloader::load_defines(GameManager& game_manager) const { ret = false; } if (!game_manager.get_crime_manager().load_crime_modifiers( - game_manager.get_modifier_manager(), parse_defines(lookup_file(crime_modifiers_file)).get_file_node() + game_manager.get_modifier_manager(), parse_defines_cached(lookup_file(crime_modifiers_file)).get_file_node() )) { Logger::error("Failed to load crime modifiers!"); ret = false; @@ -860,7 +894,7 @@ bool Dataloader::load_defines(GameManager& game_manager) const { ret = false; } if (!game_manager.get_modifier_manager().load_triggered_modifiers( - parse_defines(lookup_file(triggered_modifiers_file)).get_file_node() + parse_defines_cached(lookup_file(triggered_modifiers_file)).get_file_node() )) { Logger::error("Failed to load triggered modifiers!"); ret = false; @@ -879,8 +913,8 @@ bool Dataloader::load_defines(GameManager& game_manager) const { Logger::error("Failed to load leader traits!"); ret = false; } - if (!game_manager.get_military_manager().get_wargoal_manager().load_wargoal_file( - parse_defines(lookup_file(cb_types_file)).get_file_node() + if (!game_manager.get_military_manager().get_wargoal_type_manager().load_wargoal_file( + parse_defines_cached(lookup_file(cb_types_file)).get_file_node() )) { Logger::error("Failed to load wargoals!"); ret = false; @@ -916,6 +950,65 @@ bool Dataloader::load_defines(GameManager& game_manager) const { ret = false; } + parse_scripts(game_manager); + + free_cache(); + + return ret; +} + +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; + } + return ret; } diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp index 107c93a..069ccec 100644 --- a/src/openvic-simulation/dataloader/Dataloader.hpp +++ b/src/openvic-simulation/dataloader/Dataloader.hpp @@ -18,17 +18,18 @@ namespace OpenVic { private: path_vector_t roots; + std::vector<ovdl::v2script::Parser> cached_parsers; bool _load_interface_files(UIManager& ui_manager) const; - bool _load_pop_types(GameManager& game_manager) const; + bool _load_pop_types(GameManager& game_manager); bool _load_units(GameManager& game_manager) const; bool _load_goods(GameManager& game_manager) const; - bool _load_rebel_types(GameManager& game_manager) const; - bool _load_technologies(GameManager& game_manager) const; - bool _load_inventions(GameManager& game_manager) const; - bool _load_events(GameManager& game_manager) const; + bool _load_rebel_types(GameManager& game_manager); + bool _load_technologies(GameManager& game_manager); + bool _load_inventions(GameManager& game_manager); + bool _load_events(GameManager& game_manager); bool _load_map_dir(GameManager& game_manager) const; - bool _load_decisions(GameManager& game_manager) const; + bool _load_decisions(GameManager& game_manager); bool _load_history(GameManager& game_manager, bool unused_history_file_warnings) const; /* _DirIterator is fs::directory_iterator or fs::recursive_directory_iterator. _UniqueKey is the type of a callable @@ -47,6 +48,17 @@ namespace OpenVic { static ovdl::v2script::Parser parse_lua_defines(fs::path const& path); static ovdl::csv::Windows1252Parser parse_csv(fs::path const& path); + /* Cache the Parser so it won't be freed until free_cache is called. This is used to preserve condition and effect + * script Nodes until all defines are loaded and the scripts can be parsed. The reference returned by this function + * is only guaranteed to be valid until the function is next called. */ + ovdl::v2script::Parser& parse_defines_cached(fs::path const& path); + + private: + /* Clear the cache vector, freeing all cached Parsers and their Node trees. Pointers to cached Parsers' Nodes should + * be set to null before this is called to avoid segfaults. */ + void free_cache(); + + public: Dataloader() = default; /// @brief Searches for the Victoria 2 install directory @@ -92,8 +104,16 @@ namespace OpenVic { string_set_t lookup_dirs_in_dir(std::string_view path) const; - bool load_defines(GameManager& game_manager) const; + /* Load and parse all of the text defines data, including parsing cached condition and effect scripts after all the + * static data is loaded. Paths to the base and mod defines must have been supplied with set_roots.*/ + bool load_defines(GameManager& game_manager); + private: + /* Parse the cached Nodes of every condition and effect script in the defines. + * This is called by load_defines after all static data has been loaded. */ + bool parse_scripts(GameManager& game_manager) const; + + public: enum locale_t : size_t { English, French, German, Polish, Spanish, Italian, Swedish, Czech, Hungarian, Dutch, Portugese, Russian, Finnish, _LocaleCount diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index 1dbc99c..e429ed9 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -274,25 +274,42 @@ node_callback_t NodeTools::expect_length(callback_t<size_t> callback) { }; } -node_callback_t NodeTools::expect_key(std::string_view key, node_callback_t callback, bool* key_found) { - return _expect_type<ast::AbstractListNode>([key, callback, key_found](ast::AbstractListNode const& list_node) -> bool { - std::vector<ast::NodeUPtr> const& list = list_node._statements; - for (ast::NodeUPtr const& sub_node : list_node._statements) { - ast::AssignNode const* assign_node = sub_node->cast_to<ast::AssignNode>(); - if (assign_node != nullptr && assign_node->_name == key) { +node_callback_t NodeTools::expect_key(std::string_view key, node_callback_t callback, bool* key_found, bool allow_duplicates) { + return _expect_type<ast::AbstractListNode>( + [key, callback, key_found, allow_duplicates](ast::AbstractListNode const& list_node) -> bool { + bool ret = true; + size_t keys_found = 0; + std::vector<ast::NodeUPtr> const& list = list_node._statements; + for (ast::NodeUPtr const& sub_node : list_node._statements) { + ast::AssignNode const* assign_node = sub_node->cast_to<ast::AssignNode>(); + if (assign_node != nullptr && assign_node->_name == key) { + if (keys_found++ == 0) { + ret &= callback(&*assign_node->_initializer); + if (allow_duplicates) { + break; + } + } + } + } + if (keys_found == 0) { + if (key_found != nullptr) { + *key_found = false; + } else { + Logger::error("Failed to find expected key: \"", key, "\""); + } + ret = false; + } else { if (key_found != nullptr) { *key_found = true; } - return callback(&*assign_node->_initializer); + if (!allow_duplicates && keys_found > 1) { + Logger::error("Found ", keys_found, " instances of key: \"", key, "\" (expected 1)"); + ret = false; + } } + return ret; } - if (key_found != nullptr) { - *key_found = false; - } else { - Logger::error("Failed to find expected key: ", key); - } - return false; - }); + ); } node_callback_t NodeTools::expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback) { @@ -315,7 +332,7 @@ bool NodeTools::add_key_map_entry( } bool NodeTools::remove_key_map_entry(key_map_t& key_map, std::string_view key) { - if(key_map.erase(key) == 0) { + if (key_map.erase(key) == 0) { Logger::error("Failed to find dictionary key to remove: ", key); return false; } diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index f3224aa..f5f960f 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -1,11 +1,8 @@ #pragma once -#include <concepts> #include <cstdint> #include <functional> -#include <map> #include <optional> -#include <set> #include <type_traits> #include <openvic-dataloader/v2script/AbstractSyntaxTree.hpp> @@ -134,7 +131,9 @@ namespace OpenVic { node_callback_t expect_list(node_callback_t callback); node_callback_t expect_length(callback_t<size_t> callback); - node_callback_t expect_key(std::string_view key, node_callback_t callback, bool* key_found = nullptr); + node_callback_t expect_key( + std::string_view key, node_callback_t callback, bool* key_found = nullptr, bool allow_duplicates = false + ); node_callback_t expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback); node_callback_t expect_dictionary(key_value_callback_t callback); diff --git a/src/openvic-simulation/economy/BuildingInstance.hpp b/src/openvic-simulation/economy/BuildingInstance.hpp index e027a69..5d48321 100644 --- a/src/openvic-simulation/economy/BuildingInstance.hpp +++ b/src/openvic-simulation/economy/BuildingInstance.hpp @@ -14,7 +14,7 @@ namespace OpenVic { level_t PROPERTY_RW(level); ExpansionState PROPERTY(expansion_state); - Date PROPERTY(start_date) + Date PROPERTY(start_date); Date PROPERTY(end_date); float PROPERTY(expansion_progress); diff --git a/src/openvic-simulation/economy/ProductionType.cpp b/src/openvic-simulation/economy/ProductionType.cpp index f0ad6ea..4714ee5 100644 --- a/src/openvic-simulation/economy/ProductionType.cpp +++ b/src/openvic-simulation/economy/ProductionType.cpp @@ -6,15 +6,28 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; EmployedPop::EmployedPop( - PopType const* pop_type, bool artisan, effect_t effect, fixed_point_t effect_multiplier, fixed_point_t amount -) : pop_type { pop_type }, artisan { artisan }, effect { effect }, effect_multiplier { effect_multiplier }, - amount { amount } {} + PopType const* new_pop_type, bool new_artisan, effect_t new_effect, fixed_point_t new_effect_multiplier, + fixed_point_t new_amount +) : pop_type { new_pop_type }, artisan { new_artisan }, effect { new_effect }, effect_multiplier { new_effect_multiplier }, + amount { new_amount } {} ProductionType::ProductionType( - PRODUCTION_TYPE_ARGS -) : HasIdentifier { identifier }, owner { owner }, employees { employees }, type { type }, workforce { workforce }, - input_goods { std::move(input_goods) }, output_goods { output_goods }, value { value }, bonuses { std::move(bonuses) }, - efficiency { std::move(efficiency) }, coastal { coastal }, farm { farm }, mine { mine } {} + std::string_view new_identifier, EmployedPop new_owner, std::vector<EmployedPop> new_employees, type_t new_type, + Pop::pop_size_t new_workforce, Good::good_map_t&& new_input_goods, Good const* new_output_goods, + fixed_point_t new_value, std::vector<bonus_t>&& new_bonuses, Good::good_map_t&& new_efficiency, bool new_coastal, + bool new_farm, bool new_mine +) : HasIdentifier { new_identifier }, owner { new_owner }, employees { new_employees }, type { new_type }, + workforce { new_workforce }, input_goods { std::move(new_input_goods) }, output_goods { new_output_goods }, + value { new_value }, bonuses { std::move(new_bonuses) }, efficiency { std::move(new_efficiency) }, + coastal { new_coastal }, farm { new_farm }, mine { new_mine } {} + +bool ProductionType::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (auto& [bonus_script, bonus_value] : bonuses) { + ret &= bonus_script.parse_script(false, game_manager); + } + return ret; +} ProductionTypeManager::ProductionTypeManager() : rgo_owner_sprite { 0 } {} @@ -72,7 +85,11 @@ node_callback_t ProductionTypeManager::_expect_employed_pop_list( return false; \ } -bool ProductionTypeManager::add_production_type(PRODUCTION_TYPE_ARGS) { +bool ProductionTypeManager::add_production_type( + std::string_view identifier, EmployedPop owner, std::vector<EmployedPop> employees, ProductionType::type_t type, + Pop::pop_size_t workforce, Good::good_map_t&& input_goods, Good const* output_goods, fixed_point_t value, + std::vector<ProductionType::bonus_t>&& bonuses, Good::good_map_t&& efficiency, bool coastal, bool farm, bool mine +) { if (identifier.empty()) { Logger::error("Invalid production type identifier - empty!"); return false; @@ -110,22 +127,6 @@ bool ProductionTypeManager::add_production_type(PRODUCTION_TYPE_ARGS) { return ret; } -#define PARSE_NODE \ - expect_dictionary_keys_and_default( \ - key_value_success_callback, \ - "owner", ZERO_OR_ONE, _expect_employed_pop(good_manager, pop_manager, move_variable_callback(owner)), \ - "employees", ZERO_OR_ONE, _expect_employed_pop_list(good_manager, pop_manager, move_variable_callback(employees)), \ - "type", ZERO_OR_ONE, expect_identifier(expect_mapped_string(type_map, assign_variable_callback(type))), \ - "workforce", ZERO_OR_ONE, expect_uint(assign_variable_callback(workforce)), \ - "input_goods", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(input_goods)), \ - "output_goods", ZERO_OR_ONE, good_manager.expect_good_identifier(assign_variable_callback_pointer(output_goods)), \ - "value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(value)), \ - "efficiency", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(efficiency)), \ - "is_coastal", ZERO_OR_ONE, expect_bool(assign_variable_callback(coastal)), \ - "farm", ZERO_OR_ONE, expect_bool(assign_variable_callback(farm)), \ - "mine", ZERO_OR_ONE, expect_bool(assign_variable_callback(mine)) \ - ) - bool ProductionTypeManager::load_production_types_file( GoodManager const& good_manager, PopManager const& pop_manager, ast::NodeCPtr root ) { @@ -183,7 +184,7 @@ bool ProductionTypeManager::load_production_types_file( Pop::pop_size_t workforce = 0; // 0 is a meaningless value -> unset Good::good_map_t input_goods, efficiency; fixed_point_t value = 0; // 0 is a meaningless value -> unset - std::vector<Bonus> bonuses; + std::vector<ProductionType::bonus_t> bonuses; bool coastal = false, farm = false, mine = false; bool ret = true; @@ -193,16 +194,47 @@ bool ProductionTypeManager::load_production_types_file( { "factory", FACTORY }, { "rgo", RGO }, { "artisan", ARTISAN } }; + 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; + fixed_point_t value {}; + const bool ret = expect_dictionary_keys( + "trigger", ONE_EXACTLY, trigger.expect_script(), + "value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(value)) + )(bonus_node); + bonuses.emplace_back(std::move(trigger), value); + return ret; + }, + "owner", ZERO_OR_ONE, _expect_employed_pop(good_manager, pop_manager, move_variable_callback(owner)), + "employees", ZERO_OR_ONE, _expect_employed_pop_list(good_manager, pop_manager, move_variable_callback(employees)), + "type", ZERO_OR_ONE, expect_identifier(expect_mapped_string(type_map, assign_variable_callback(type))), + "workforce", ZERO_OR_ONE, expect_uint(assign_variable_callback(workforce)), + "input_goods", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(input_goods)), + "output_goods", ZERO_OR_ONE, good_manager.expect_good_identifier(assign_variable_callback_pointer(output_goods)), + "value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(value)), + "efficiency", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(efficiency)), + "is_coastal", ZERO_OR_ONE, expect_bool(assign_variable_callback(coastal)), + "farm", ZERO_OR_ONE, expect_bool(assign_variable_callback(farm)), + "mine", ZERO_OR_ONE, expect_bool(assign_variable_callback(mine)) + ); + // apply template first - if (template_target_map.contains(key)) { - std::string_view template_id = template_target_map[key]; - if (template_node_map.contains(template_id)) { - ast::NodeCPtr template_node = template_node_map[template_id]; - ret &= PARSE_NODE(template_node); + { + const typename decltype(template_target_map)::const_iterator target_it = template_target_map.find(key); + if (target_it != template_target_map.end()) { + const std::string_view template_id = target_it->second; + const typename decltype(template_node_map)::const_iterator node_it = template_node_map.find(template_id); + if (node_it != template_node_map.end()) { + ret &= parse_node(node_it->second); + } else { + Logger::error("Missing template ", template_id, " for production type ", key, "!"); + ret = false; + } } } - ret &= PARSE_NODE(node); + ret &= parse_node(node); ret &= add_production_type( key, owner, employees, type, workforce, std::move(input_goods), output_goods, value, std::move(bonuses), @@ -221,3 +253,11 @@ bool ProductionTypeManager::load_production_types_file( return ret; } + +bool ProductionTypeManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (ProductionType& production_type : production_types.get_items()) { + ret &= production_type.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/economy/ProductionType.hpp b/src/openvic-simulation/economy/ProductionType.hpp index 4fdceda..5d51f60 100644 --- a/src/openvic-simulation/economy/ProductionType.hpp +++ b/src/openvic-simulation/economy/ProductionType.hpp @@ -2,14 +2,10 @@ #include "openvic-simulation/economy/Good.hpp" #include "openvic-simulation/pop/Pop.hpp" +#include "openvic-simulation/scripts/ConditionScript.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" -#define PRODUCTION_TYPE_ARGS \ - std::string_view identifier, EmployedPop owner, std::vector<EmployedPop> employees, ProductionType::type_t type, \ - Pop::pop_size_t workforce, Good::good_map_t&& input_goods, Good const* output_goods, fixed_point_t value, \ - std::vector<Bonus>&& bonuses, Good::good_map_t&& efficiency, bool coastal, bool farm, bool mine - namespace OpenVic { struct ProductionTypeManager; @@ -26,40 +22,46 @@ namespace OpenVic { fixed_point_t PROPERTY(amount); EmployedPop( - PopType const* pop_type, bool artisan, effect_t effect, fixed_point_t effect_multiplier, fixed_point_t amount + PopType const* new_pop_type, bool new_artisan, effect_t new_effect, fixed_point_t new_effect_multiplier, + fixed_point_t new_amount ); public: EmployedPop() = default; }; - struct Bonus { - // TODO: trigger condition(s) - const fixed_point_t value; - }; - struct ProductionType : HasIdentifier { friend struct ProductionTypeManager; + enum struct type_t { FACTORY, RGO, ARTISAN }; + + using bonus_t = std::pair<ConditionScript, fixed_point_t>; + private: const EmployedPop PROPERTY(owner); - const std::vector<EmployedPop> PROPERTY(employees); - const enum struct type_t { FACTORY, RGO, ARTISAN } PROPERTY(type); + std::vector<EmployedPop> PROPERTY(employees); + const type_t PROPERTY(type); const Pop::pop_size_t workforce; - const Good::good_map_t PROPERTY(input_goods); + Good::good_map_t PROPERTY(input_goods); Good const* PROPERTY(output_goods); const fixed_point_t PROPERTY(value); - const std::vector<Bonus> PROPERTY(bonuses); + std::vector<bonus_t> PROPERTY(bonuses); - const Good::good_map_t PROPERTY(efficiency); + Good::good_map_t PROPERTY(efficiency); const bool PROPERTY_CUSTOM_PREFIX(coastal, is); // is_coastal const bool PROPERTY_CUSTOM_PREFIX(farm, is); const bool PROPERTY_CUSTOM_PREFIX(mine, is); - ProductionType(PRODUCTION_TYPE_ARGS); + ProductionType( + std::string_view new_identifier, EmployedPop new_owner, std::vector<EmployedPop> new_employees, type_t new_type, + Pop::pop_size_t new_workforce, Good::good_map_t&& new_input_goods, Good const* new_output_goods, + fixed_point_t new_value, std::vector<bonus_t>&& new_bonuses, Good::good_map_t&& new_efficiency, bool new_coastal, + bool new_farm, bool new_mine + ); + bool parse_scripts(GameManager const& game_manager); public: ProductionType(ProductionType&&) = default; }; @@ -80,8 +82,14 @@ namespace OpenVic { public: ProductionTypeManager(); - bool add_production_type(PRODUCTION_TYPE_ARGS); + bool add_production_type( + std::string_view identifier, EmployedPop owner, std::vector<EmployedPop> employees, ProductionType::type_t type, + Pop::pop_size_t workforce, Good::good_map_t&& input_goods, Good const* output_goods, fixed_point_t value, + std::vector<ProductionType::bonus_t>&& bonuses, Good::good_map_t&& efficiency, bool coastal, bool farm, bool mine + ); bool load_production_types_file(GoodManager const& good_manager, PopManager const& pop_manager, ast::NodeCPtr root); + + bool parse_scripts(GameManager const& game_manager); }; } diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp index 48b30e6..681b2b9 100644 --- a/src/openvic-simulation/history/CountryHistory.cpp +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -52,20 +52,40 @@ bool CountryHistoryMap::_load_history_entry( })(value); } - Technology const* technology = technology_manager.get_technology_by_identifier(key); - if (technology != nullptr) { - bool flag; - if (expect_int_bool(assign_variable_callback(flag))(value)) { - return entry.technologies.emplace(technology, flag).second; - } else return false; + { + Technology const* technology = technology_manager.get_technology_by_identifier(key); + if (technology != nullptr) { + return expect_int_bool( + [&entry, technology](bool flag) -> bool { + if (!entry.technologies.emplace(technology, flag).second) { + Logger::error( + "Duplicate entry for technology ", technology->get_identifier(), " in history of ", + entry.get_country().get_identifier(), " at date ", entry.get_date() + ); + return false; + } + return true; + } + )(value); + } } - Invention const* invention = invention_manager.get_invention_by_identifier(key); - if (invention != nullptr) { - bool flag; - if (expect_bool(assign_variable_callback(flag))(value)) { - return entry.inventions.emplace(invention, flag).second; - } else return false; + { + Invention const* invention = invention_manager.get_invention_by_identifier(key); + if (invention != nullptr) { + return expect_bool( + [&entry, invention](bool flag) -> bool { + if (!entry.inventions.emplace(invention, flag).second) { + Logger::error( + "Duplicate entry for invention ", invention->get_identifier(), " in history of ", + entry.get_country().get_identifier(), " at date ", entry.get_date() + ); + return false; + } + return true; + } + )(value); + } } return _load_history_sub_entry_callback( diff --git a/src/openvic-simulation/history/DiplomaticHistory.cpp b/src/openvic-simulation/history/DiplomaticHistory.cpp index 9fd6060..088ec0a 100644 --- a/src/openvic-simulation/history/DiplomaticHistory.cpp +++ b/src/openvic-simulation/history/DiplomaticHistory.cpp @@ -221,7 +221,7 @@ bool DiplomaticHistoryManager::load_war_history_file(GameManager const& game_man bool ret = expect_dictionary_keys( "actor", ONE_EXACTLY, game_manager.get_country_manager().expect_country_identifier(assign_variable_callback_pointer(actor)), "receiver", ONE_EXACTLY, game_manager.get_country_manager().expect_country_identifier(assign_variable_callback_pointer(receiver)), - "casus_belli", ONE_EXACTLY, game_manager.get_military_manager().get_wargoal_manager().expect_wargoal_type_identifier(assign_variable_callback_pointer(type)), + "casus_belli", ONE_EXACTLY, game_manager.get_military_manager().get_wargoal_type_manager().expect_wargoal_type_identifier(assign_variable_callback_pointer(type)), "country", ZERO_OR_ONE, game_manager.get_country_manager().expect_country_identifier(assign_variable_callback_pointer(*third_party)), "state_province_id", ZERO_OR_ONE, game_manager.get_map().expect_province_identifier(assign_variable_callback_pointer(*target)) )(value); diff --git a/src/openvic-simulation/interface/GFX.hpp b/src/openvic-simulation/interface/GFX.hpp index cdc49c2..2bae4b3 100644 --- a/src/openvic-simulation/interface/GFX.hpp +++ b/src/openvic-simulation/interface/GFX.hpp @@ -138,7 +138,7 @@ namespace OpenVic::GFX { class MaskedFlag final : public Sprite { friend std::unique_ptr<MaskedFlag> std::make_unique<MaskedFlag>(); - std::string PROPERTY(overlay_file) + std::string PROPERTY(overlay_file); std::string PROPERTY(mask_file); protected: diff --git a/src/openvic-simulation/map/Crime.cpp b/src/openvic-simulation/map/Crime.cpp index c0bc04e..65760a3 100644 --- a/src/openvic-simulation/map/Crime.cpp +++ b/src/openvic-simulation/map/Crime.cpp @@ -3,17 +3,22 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -Crime::Crime(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, bool new_default_active) - : TriggeredModifier { new_identifier, std::move(new_values), new_icon }, default_active { new_default_active } {} +Crime::Crime( + std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, ConditionScript&& new_trigger, + bool new_default_active +) : TriggeredModifier { new_identifier, std::move(new_values), new_icon, std::move(new_trigger) }, + default_active { new_default_active } {} bool CrimeManager::add_crime_modifier( - std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, bool default_active + std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, ConditionScript&& trigger, bool default_active ) { if (identifier.empty()) { Logger::error("Invalid crime modifier effect identifier - empty!"); return false; } - return crime_modifiers.add_item({ identifier, std::move(values), icon, default_active }, duplicate_warning_callback); + return crime_modifiers.add_item( + { identifier, std::move(values), icon, std::move(trigger), default_active }, duplicate_warning_callback + ); } bool CrimeManager::load_crime_modifiers(ModifierManager const& modifier_manager, ast::NodeCPtr root) { @@ -22,17 +27,26 @@ 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; bool default_active = false; bool ret = modifier_manager.expect_modifier_value_and_keys( move_variable_callback(modifier_value), "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)), - "trigger", ONE_EXACTLY, success_callback, // TODO - load condition + "trigger", ONE_EXACTLY, trigger.expect_script(), "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(default_active)) )(value); - ret &= add_crime_modifier(key, std::move(modifier_value), icon, default_active); + ret &= add_crime_modifier(key, std::move(modifier_value), icon, std::move(trigger), default_active); return ret; } )(root); lock_crime_modifiers(); return ret; } + +bool CrimeManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (Crime& crime : crime_modifiers.get_items()) { + ret &= crime.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/map/Crime.hpp b/src/openvic-simulation/map/Crime.hpp index 26f52ab..01044f9 100644 --- a/src/openvic-simulation/map/Crime.hpp +++ b/src/openvic-simulation/map/Crime.hpp @@ -9,7 +9,10 @@ namespace OpenVic { private: const bool PROPERTY(default_active); - Crime(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, bool new_default_active); + Crime( + std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, ConditionScript&& new_trigger, + bool new_default_active + ); public: Crime(Crime&&) = default; @@ -21,9 +24,12 @@ namespace OpenVic { public: bool add_crime_modifier( - std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, bool default_active + std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, ConditionScript&& trigger, + bool default_active ); bool load_crime_modifiers(ModifierManager const& modifier_manager, ast::NodeCPtr root); + + bool parse_scripts(GameManager const& game_manager); }; } diff --git a/src/openvic-simulation/map/Map.hpp b/src/openvic-simulation/map/Map.hpp index 3e7ff35..33ca4b2 100644 --- a/src/openvic-simulation/map/Map.hpp +++ b/src/openvic-simulation/map/Map.hpp @@ -75,7 +75,7 @@ namespace OpenVic { Province::index_t PROPERTY(max_provinces); Province* PROPERTY(selected_province); - Pop::pop_size_t PROPERTY(highest_province_population) + Pop::pop_size_t PROPERTY(highest_province_population); Pop::pop_size_t PROPERTY(total_map_population); Province::index_t get_index_from_colour(colour_t colour) const; diff --git a/src/openvic-simulation/map/State.cpp b/src/openvic-simulation/map/State.cpp index faf8d1b..c1f802d 100644 --- a/src/openvic-simulation/map/State.cpp +++ b/src/openvic-simulation/map/State.cpp @@ -59,7 +59,7 @@ StateSet::states_t& StateSet::get_states() { void StateManager::generate_states(Map& map) { regions.clear(); regions.reserve(map.get_region_count()); - for(Region const& region : map.get_regions()) { + for (Region const& region : map.get_regions()) { if (!region.get_meta()) { regions.emplace_back(map, region); } diff --git a/src/openvic-simulation/military/MilitaryManager.hpp b/src/openvic-simulation/military/MilitaryManager.hpp index aeb5a7b..2efa3ea 100644 --- a/src/openvic-simulation/military/MilitaryManager.hpp +++ b/src/openvic-simulation/military/MilitaryManager.hpp @@ -11,6 +11,6 @@ namespace OpenVic { UnitManager PROPERTY_REF(unit_manager); LeaderTraitManager PROPERTY_REF(leader_trait_manager); DeploymentManager PROPERTY_REF(deployment_manager); - WargoalTypeManager PROPERTY_REF(wargoal_manager); + WargoalTypeManager PROPERTY_REF(wargoal_type_manager); }; } diff --git a/src/openvic-simulation/military/Wargoal.cpp b/src/openvic-simulation/military/Wargoal.cpp index 7a12b7e..28cbc51 100644 --- a/src/openvic-simulation/military/Wargoal.cpp +++ b/src/openvic-simulation/military/Wargoal.cpp @@ -6,84 +6,98 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; WargoalType::WargoalType( - std::string_view new_identifier, - std::string_view new_sprite, - std::string_view new_war_name, - Timespan new_available_length, - Timespan new_truce_length, - bool new_triggered_only, - bool new_civil_war, - bool new_constructing, - bool new_crisis, - bool new_great_war, - bool new_mutual, - const peace_modifiers_t&& new_modifiers, - peace_options_t new_peace_options -) : HasIdentifier { new_identifier }, - sprite { new_sprite }, - war_name { new_war_name }, - available_length { new_available_length }, - truce_length { new_truce_length }, - triggered_only { new_triggered_only }, - civil_war { new_civil_war }, - constructing { new_constructing }, - crisis { new_crisis }, - great_war { new_great_war }, - mutual { new_mutual }, - modifiers { std::move(new_modifiers) }, - peace_options { new_peace_options } {} - -const std::vector<WargoalType const*>& WargoalTypeManager::get_peace_priority_list() const { - return peace_priorities; + std::string_view new_identifier, std::string_view new_war_name, Timespan new_available_length, + Timespan new_truce_length, sprite_t new_sprite_index, bool new_triggered_only, bool new_civil_war, + bool new_constructing, bool new_crisis, bool new_great_war_obligatory, bool new_mutual, + bool new_all_allowed_states, bool new_always, peace_modifiers_t&& new_modifiers, peace_options_t new_peace_options, + ConditionScript&& new_can_use, ConditionScript&& new_is_valid, ConditionScript&& new_allowed_states, + ConditionScript&& new_allowed_substate_regions, ConditionScript&& new_allowed_states_in_crisis, + ConditionScript&& new_allowed_countries, EffectScript&& new_on_add, EffectScript&& new_on_po_accepted +) : HasIdentifier { new_identifier }, war_name { new_war_name }, available_length { new_available_length }, + truce_length { new_truce_length }, sprite_index { new_sprite_index }, triggered_only { new_triggered_only }, + civil_war { new_civil_war }, constructing { new_constructing }, crisis { new_crisis }, + great_war_obligatory { new_great_war_obligatory }, mutual { new_mutual }, all_allowed_states { new_all_allowed_states }, + always { new_always }, modifiers { std::move(new_modifiers) }, peace_options { new_peace_options }, + can_use { std::move(new_can_use) }, is_valid { std::move(new_is_valid) }, allowed_states { std::move(new_allowed_states) }, + allowed_substate_regions { std::move(new_allowed_substate_regions) }, + allowed_states_in_crisis { std::move(new_allowed_states_in_crisis) }, + allowed_countries { std::move(new_allowed_countries) }, on_add { std::move(new_on_add) }, + on_po_accepted { std::move(new_on_po_accepted) } {} + +bool WargoalType::parse_scripts(GameManager& game_manager) { + bool ret = true; + ret &= can_use.parse_script(true, game_manager); + ret &= is_valid.parse_script(true, game_manager); + ret &= allowed_states.parse_script(true, game_manager); + ret &= allowed_substate_regions.parse_script(true, game_manager); + ret &= allowed_states_in_crisis.parse_script(true, game_manager); + ret &= allowed_countries.parse_script(true, game_manager); + ret &= on_add.parse_script(true, game_manager); + ret &= on_po_accepted.parse_script(true, game_manager); + return ret; } bool WargoalTypeManager::add_wargoal_type( - std::string_view identifier, - std::string_view sprite, - std::string_view war_name, - Timespan available_length, - Timespan truce_length, - bool triggered_only, - bool civil_war, - bool constructing, - bool crisis, - bool great_war, - bool mutual, - WargoalType::peace_modifiers_t&& modifiers, - peace_options_t peace_options + std::string_view identifier, std::string_view war_name, Timespan available_length, + Timespan truce_length, WargoalType::sprite_t sprite_index, bool triggered_only, bool civil_war, + bool constructing, bool crisis, bool great_war_obligatory, bool mutual, bool all_allowed_states, + bool always, WargoalType::peace_modifiers_t&& modifiers, peace_options_t peace_options, + ConditionScript&& can_use, ConditionScript&& is_valid, ConditionScript&& allowed_states, + ConditionScript&& allowed_substate_regions, ConditionScript&& allowed_states_in_crisis, + ConditionScript&& allowed_countries, EffectScript&& on_add, EffectScript&& on_po_accepted ) { if (identifier.empty()) { Logger::error("Invalid wargoal identifier - empty!"); return false; } - if (sprite.empty()) { - Logger::error("Invalid sprite for wargoal ", identifier, " - empty!"); - return false; - } - if (war_name.empty()) { Logger::error("Invalid war name for wargoal ", identifier, " - empty!"); return false; } + if (sprite_index == 0) { + Logger::warning("Invalid sprite for wargoal ", identifier, " - 0"); + } + return wargoal_types.add_item({ - identifier, sprite, war_name, available_length, truce_length, triggered_only, civil_war, constructing, crisis, - great_war, mutual, std::move(modifiers), peace_options + identifier, war_name, available_length, truce_length, sprite_index, triggered_only, civil_war, constructing, crisis, + great_war_obligatory, mutual, all_allowed_states, always, std::move(modifiers), peace_options, std::move(can_use), + std::move(is_valid), std::move(allowed_states), std::move(allowed_substate_regions), + std::move(allowed_states_in_crisis), std::move(allowed_countries), std::move(on_add), std::move(on_po_accepted) }); } bool WargoalTypeManager::load_wargoal_file(ast::NodeCPtr root) { bool ret = expect_dictionary( [this](std::string_view identifier, ast::NodeCPtr value) -> bool { - if (identifier == "peace_order") return true; + if (identifier == "peace_order") { + return true; + } - std::string_view sprite, war_name; - Timespan available, truce; - bool triggered_only = false, civil_war = false, constructing = true, crisis = true, great_war = false, - mutual = false; - peace_options_t peace_options {}; + using enum peace_options_t; + + std::string_view war_name; + Timespan available {}, truce {}; + WargoalType::sprite_t sprite_index = 0; + bool triggered_only = false, civil_war = false, constructing = true, crisis = true, great_war_obligatory = false, + 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; + + 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 { + if (val) { + peace_options |= peace_option; + } else { + peace_options &= ~peace_option; + } + return true; + }); + }; bool ret = expect_dictionary_keys_and_default( [&modifiers, &identifier](std::string_view key, ast::NodeCPtr value) -> bool { @@ -113,105 +127,53 @@ bool WargoalTypeManager::load_wargoal_file(ast::NodeCPtr root) { Logger::error("Modifier ", key, " in wargoal ", identifier, " is invalid."); return false; }, - "sprite_index", ONE_EXACTLY, expect_identifier(assign_variable_callback(sprite)), "war_name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(war_name)), "months", ZERO_OR_ONE, expect_months(assign_variable_callback(available)), "truce_months", ONE_EXACTLY, expect_months(assign_variable_callback(truce)), + "sprite_index", ONE_EXACTLY, expect_uint(assign_variable_callback(sprite_index)), "is_triggered_only", ZERO_OR_ONE, expect_bool(assign_variable_callback(triggered_only)), "is_civil_war", ZERO_OR_ONE, expect_bool(assign_variable_callback(civil_war)), "constructing_cb", ZERO_OR_ONE, expect_bool(assign_variable_callback(constructing)), "crisis", ZERO_OR_ONE, expect_bool(assign_variable_callback(crisis)), - "great_war_obligatory", ZERO_OR_ONE, expect_bool(assign_variable_callback(great_war)), + "great_war_obligatory", ZERO_OR_ONE, expect_bool(assign_variable_callback(great_war_obligatory)), "mutual", ZERO_OR_ONE, expect_bool(assign_variable_callback(mutual)), - /* PEACE OPTIONS */ - "po_annex", ZERO_OR_ONE, expect_bool([&peace_options](bool annex) -> bool { - if (annex) peace_options |= peace_options_t::PO_ANNEX; - return true; - }), - "po_demand_state", ZERO_OR_ONE, expect_bool([&peace_options](bool demand_state) -> bool { - if (demand_state) peace_options |= peace_options_t::PO_DEMAND_STATE; - return true; - }), - "po_add_to_sphere", ZERO_OR_ONE, expect_bool([&peace_options](bool add_to_sphere) -> bool { - if (add_to_sphere) peace_options |= peace_options_t::PO_ADD_TO_SPHERE; - return true; - }), - "po_disarmament", ZERO_OR_ONE, expect_bool([&peace_options](bool disarm) -> bool { - if (disarm) peace_options |= peace_options_t::PO_DISARMAMENT; - return true; - }), - "po_destroy_forts", ZERO_OR_ONE, expect_bool([&peace_options](bool disarm) -> bool { - if (disarm) peace_options |= peace_options_t::PO_REMOVE_FORTS; - return true; - }), - "po_destroy_naval_bases", ZERO_OR_ONE, expect_bool([&peace_options](bool disarm) -> bool { - if (disarm) peace_options |= peace_options_t::PO_REMOVE_NAVAL_BASES; - return true; - }), - "po_reparations", ZERO_OR_ONE, expect_bool([&peace_options](bool reps) -> bool { - if (reps) peace_options |= peace_options_t::PO_REPARATIONS; - return true; - }), - "po_transfer_provinces", ZERO_OR_ONE, expect_bool([&peace_options](bool provinces) -> bool { - if (provinces) peace_options |= peace_options_t::PO_TRANSFER_PROVINCES; - return true; - }), - "po_remove_prestige", ZERO_OR_ONE, expect_bool([&peace_options](bool humiliate) -> bool { - if (humiliate) peace_options |= peace_options_t::PO_REMOVE_PRESTIGE; - return true; - }), - "po_make_puppet", ZERO_OR_ONE, expect_bool([&peace_options](bool puppet) -> bool { - if (puppet) peace_options |= peace_options_t::PO_MAKE_PUPPET; - return true; - }), - "po_release_puppet", ZERO_OR_ONE, expect_bool([&peace_options](bool puppet) -> bool { - if (puppet) peace_options |= peace_options_t::PO_RELEASE_PUPPET; - return true; - }), - "po_status_quo", ZERO_OR_ONE, expect_bool([&peace_options](bool status_quo) -> bool { - if (status_quo) peace_options |= peace_options_t::PO_STATUS_QUO; - return true; - }), - "po_install_communist_gov_type", ZERO_OR_ONE, expect_bool([&peace_options](bool puppet) -> bool { - if (puppet) peace_options |= peace_options_t::PO_INSTALL_COMMUNISM; - return true; - }), - "po_uninstall_communist_gov_type", ZERO_OR_ONE, expect_bool([&peace_options](bool puppet) -> bool { - if (puppet) peace_options |= peace_options_t::PO_REMOVE_COMMUNISM; - return true; - }), - "po_remove_cores", ZERO_OR_ONE, expect_bool([&peace_options](bool uncore) -> bool { - if (uncore) peace_options |= peace_options_t::PO_REMOVE_CORES; - return true; - }), - "po_colony", ZERO_OR_ONE, expect_bool([&peace_options](bool colony) -> bool { - if (colony) peace_options |= peace_options_t::PO_COLONY; - return true; - }), - "po_gunboat", ZERO_OR_ONE, expect_bool([&peace_options](bool gunboat) -> bool { - if (gunboat) peace_options |= peace_options_t::PO_REPAY_DEBT; - return true; - }), - "po_clear_union_sphere", ZERO_OR_ONE, expect_bool([&peace_options](bool clear) -> bool { - if (clear) peace_options |= peace_options_t::PO_CLEAR_UNION_SPHERE; - return true; - }), - /* TODO: CONDITION & EFFECT BLOCKS */ - "can_use", ZERO_OR_ONE, success_callback, - "is_valid", ZERO_OR_ONE, success_callback, - "on_add", ZERO_OR_ONE, success_callback, - "on_po_accepted", ZERO_OR_ONE, success_callback, - "allowed_states", ZERO_OR_ONE, success_callback, - "all_allowed_states", ZERO_OR_ONE, success_callback, - "allowed_substate_regions", ZERO_OR_ONE, success_callback, - "allowed_states_in_crisis", ZERO_OR_ONE, success_callback, - "allowed_countries", ZERO_OR_ONE, success_callback, - "always", ZERO_OR_ONE, success_callback // usage unknown / quirk + /* START PEACE OPTIONS */ + "po_annex", ZERO_OR_ONE, expect_peace_option(PO_ANNEX), + "po_demand_state", ZERO_OR_ONE, expect_peace_option(PO_DEMAND_STATE), + "po_add_to_sphere", ZERO_OR_ONE, expect_peace_option(PO_ADD_TO_SPHERE), + "po_disarmament", ZERO_OR_ONE, expect_peace_option(PO_DISARMAMENT), + "po_destroy_forts", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_FORTS), + "po_destroy_naval_bases", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_NAVAL_BASES), + "po_reparations", ZERO_OR_ONE, expect_peace_option(PO_REPARATIONS), + "po_transfer_provinces", ZERO_OR_ONE, expect_peace_option(PO_TRANSFER_PROVINCES), + "po_remove_prestige", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_PRESTIGE), + "po_make_puppet", ZERO_OR_ONE, expect_peace_option(PO_MAKE_PUPPET), + "po_release_puppet", ZERO_OR_ONE, expect_peace_option(PO_RELEASE_PUPPET), + "po_status_quo", ZERO_OR_ONE, expect_peace_option(PO_STATUS_QUO), + "po_install_communist_gov_type", ZERO_OR_ONE, expect_peace_option(PO_INSTALL_COMMUNISM), + "po_uninstall_communist_gov_type", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_COMMUNISM), + "po_remove_cores", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_CORES), + "po_colony", ZERO_OR_ONE, expect_peace_option(PO_COLONY), + "po_gunboat", ZERO_OR_ONE, expect_peace_option(PO_REPAY_DEBT), + "po_clear_union_sphere", ZERO_OR_ONE, expect_peace_option(PO_CLEAR_UNION_SPHERE), + /* END PEACE OPTIONS */ + "can_use", ZERO_OR_ONE, can_use.expect_script(), + "is_valid", ZERO_OR_ONE, is_valid.expect_script(), + "on_add", ZERO_OR_ONE, on_add.expect_script(), + "on_po_accepted", ZERO_OR_ONE, on_po_accepted.expect_script(), + "allowed_states", ZERO_OR_ONE, allowed_states.expect_script(), + "all_allowed_states", ZERO_OR_ONE, expect_bool(assign_variable_callback(all_allowed_states)), + "allowed_substate_regions", ZERO_OR_ONE, allowed_substate_regions.expect_script(), + "allowed_states_in_crisis", ZERO_OR_ONE, allowed_states_in_crisis.expect_script(), + "allowed_countries", ZERO_OR_ONE, allowed_countries.expect_script(), + "always", ZERO_OR_ONE, expect_bool(assign_variable_callback(always)) )(value); add_wargoal_type( - identifier, sprite, war_name, available, truce, triggered_only, civil_war, constructing, crisis, great_war, - mutual, std::move(modifiers), peace_options + identifier, war_name, available, truce, sprite_index, triggered_only, civil_war, constructing, crisis, + great_war_obligatory, mutual, all_allowed_states, always, std::move(modifiers), peace_options, + std::move(can_use), std::move(is_valid), std::move(allowed_states), std::move(allowed_substate_regions), + std::move(allowed_states_in_crisis), std::move(allowed_countries), std::move(on_add), std::move(on_po_accepted) ); return ret; } @@ -237,4 +199,12 @@ bool WargoalTypeManager::load_wargoal_file(ast::NodeCPtr root) { lock_wargoal_types(); return ret; -}
\ No newline at end of file +} + +bool WargoalTypeManager::parse_scripts(GameManager& game_manager) { + bool ret = true; + for (WargoalType& wargoal_type : wargoal_types.get_items()) { + ret &= wargoal_type.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/military/Wargoal.hpp b/src/openvic-simulation/military/Wargoal.hpp index 66fce90..a4f2e7c 100644 --- a/src/openvic-simulation/military/Wargoal.hpp +++ b/src/openvic-simulation/military/Wargoal.hpp @@ -1,6 +1,8 @@ #pragma once #include "openvic-simulation/misc/Modifier.hpp" +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" #include "openvic-simulation/types/EnumBitfield.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/utility/Getters.hpp" @@ -9,30 +11,33 @@ namespace OpenVic { struct WargoalTypeManager; enum class peace_options_t : uint32_t { - PO_ANNEX = 0b100000000000000000, - PO_DEMAND_STATE = 0b010000000000000000, - PO_COLONY = 0b001000000000000000, - PO_ADD_TO_SPHERE = 0b000100000000000000, - PO_DISARMAMENT = 0b000010000000000000, - PO_REMOVE_FORTS = 0b000001000000000000, - PO_REMOVE_NAVAL_BASES = 0b000000100000000000, - PO_REPARATIONS = 0b000000010000000000, - PO_REPAY_DEBT = 0b000000001000000000, - PO_REMOVE_PRESTIGE = 0b000000000100000000, - PO_MAKE_PUPPET = 0b000000000010000000, - PO_RELEASE_PUPPET = 0b000000000001000000, - PO_STATUS_QUO = 0b000000000000100000, - PO_INSTALL_COMMUNISM = 0b000000000000010000, - PO_REMOVE_COMMUNISM = 0b000000000000001000, - PO_REMOVE_CORES = 0b000000000000000100, // only usable with ANNEX, DEMAND_STATE, or TRANSFER_PROVINCES - PO_TRANSFER_PROVINCES = 0b000000000000000010, - PO_CLEAR_UNION_SPHERE = 0b000000000000000001 + NO_PEACE_OPTIONS = 0, + PO_ANNEX = 1 << 0, + PO_DEMAND_STATE = 1 << 1, + PO_COLONY = 1 << 2, + PO_ADD_TO_SPHERE = 1 << 3, + PO_DISARMAMENT = 1 << 4, + PO_REMOVE_FORTS = 1 << 5, + PO_REMOVE_NAVAL_BASES = 1 << 6, + PO_REPARATIONS = 1 << 7, + PO_REPAY_DEBT = 1 << 8, + PO_REMOVE_PRESTIGE = 1 << 9, + PO_MAKE_PUPPET = 1 << 10, + PO_RELEASE_PUPPET = 1 << 11, + PO_STATUS_QUO = 1 << 12, + PO_INSTALL_COMMUNISM = 1 << 13, + PO_REMOVE_COMMUNISM = 1 << 14, + PO_REMOVE_CORES = 1 << 15, // only usable with ANNEX, DEMAND_STATE, or TRANSFER_PROVINCES + PO_TRANSFER_PROVINCES = 1 << 16, + PO_CLEAR_UNION_SPHERE = 1 << 17 }; template<> struct enable_bitfield<peace_options_t> : std::true_type{}; struct WargoalType : HasIdentifier { friend struct WargoalTypeManager; + using sprite_t = uint8_t; + enum class PEACE_MODIFIERS { BADBOY_FACTOR, PRESTIGE_FACTOR, @@ -50,37 +55,41 @@ namespace OpenVic { using peace_modifiers_t = fixed_point_map_t<PEACE_MODIFIERS>; private: - const std::string PROPERTY(sprite); - const std::string PROPERTY(war_name); + std::string PROPERTY(war_name); const Timespan PROPERTY(available_length); const Timespan PROPERTY(truce_length); - const bool PROPERTY(triggered_only); // only able to be added via effects (or within the code) - const bool PROPERTY(civil_war); + const sprite_t PROPERTY(sprite_index); + const bool PROPERTY_CUSTOM_PREFIX(triggered_only, is); // only able to be added via effects or on_actions + const bool PROPERTY_CUSTOM_PREFIX(civil_war, is); const bool PROPERTY(constructing); // can be added to existing wars or justified const bool PROPERTY(crisis); // able to be added to crises - const bool PROPERTY(great_war); // automatically add to great wars - const bool PROPERTY(mutual); // attacked and defender share wargoal - const peace_modifiers_t PROPERTY(modifiers); - const peace_options_t PROPERTY(peace_options); - - // TODO: can_use, prerequisites, on_add, on_po_accepted + const bool PROPERTY_CUSTOM_PREFIX(great_war_obligatory, is); // automatically add to great war peace offers/demands + const bool PROPERTY_CUSTOM_PREFIX(mutual, is); // attacked and defender share wargoal + const bool PROPERTY(all_allowed_states); // take all valid states rather than just one + const bool PROPERTY(always); // available without justifying + peace_modifiers_t PROPERTY(modifiers); + peace_options_t PROPERTY(peace_options); + ConditionScript PROPERTY(can_use); + ConditionScript PROPERTY(is_valid); + ConditionScript PROPERTY(allowed_states); + ConditionScript PROPERTY(allowed_substate_regions); + ConditionScript PROPERTY(allowed_states_in_crisis); + ConditionScript PROPERTY(allowed_countries); + EffectScript PROPERTY(on_add); + EffectScript PROPERTY(on_po_accepted); WargoalType( - std::string_view new_identifier, - std::string_view new_sprite, - std::string_view new_war_name, - Timespan new_available_length, - Timespan new_truce_length, - bool new_triggered_only, - bool new_civil_war, - bool new_constructing, - bool new_crisis, - bool new_great_war, - bool new_mutual, - const peace_modifiers_t&& new_modifiers, - peace_options_t new_peace_options + std::string_view new_identifier, std::string_view new_war_name, Timespan new_available_length, + Timespan new_truce_length, sprite_t new_sprite_index, bool new_triggered_only, bool new_civil_war, + bool new_constructing, bool new_crisis, bool new_great_war_obligatory, bool new_mutual, + bool new_all_allowed_states, bool new_always, peace_modifiers_t&& new_modifiers, peace_options_t new_peace_options, + ConditionScript&& new_can_use, ConditionScript&& new_is_valid, ConditionScript&& new_allowed_states, + ConditionScript&& new_allowed_substate_regions, ConditionScript&& new_allowed_states_in_crisis, + ConditionScript&& new_allowed_countries, EffectScript&& new_on_add, EffectScript&& new_on_po_accepted ); + bool parse_scripts(GameManager& game_manager); + public: WargoalType(WargoalType&&) = default; }; @@ -91,24 +100,18 @@ namespace OpenVic { std::vector<WargoalType const*> PROPERTY(peace_priorities); public: - const std::vector<WargoalType const*>& get_peace_priority_list() const; - bool add_wargoal_type( - std::string_view identifier, - std::string_view sprite, - std::string_view war_name, - Timespan available_length, - Timespan truce_length, - bool triggered_only, - bool civil_war, - bool constructing, - bool crisis, - bool great_war, - bool mutual, - WargoalType::peace_modifiers_t&& modifiers, - peace_options_t peace_options + std::string_view identifier, std::string_view war_name, Timespan available_length, + Timespan truce_length, WargoalType::sprite_t sprite_index, bool triggered_only, bool civil_war, + bool constructing, bool crisis, bool great_war_obligatory, bool mutual, bool all_allowed_states, + bool always, WargoalType::peace_modifiers_t&& modifiers, peace_options_t peace_options, + ConditionScript&& can_use, ConditionScript&& is_valid, ConditionScript&& allowed_states, + ConditionScript&& allowed_substate_regions, ConditionScript&& allowed_states_in_crisis, + ConditionScript&& allowed_countries, EffectScript&& on_add, EffectScript&& on_po_accepted ); bool load_wargoal_file(ast::NodeCPtr root); + + bool parse_scripts(GameManager& game_manager); }; } // namespace OpenVic
\ No newline at end of file diff --git a/src/openvic-simulation/misc/Decision.cpp b/src/openvic-simulation/misc/Decision.cpp index d50a16a..36eb871 100644 --- a/src/openvic-simulation/misc/Decision.cpp +++ b/src/openvic-simulation/misc/Decision.cpp @@ -6,14 +6,26 @@ using namespace OpenVic::NodeTools; Decision::Decision( std::string_view new_identifier, bool new_alert, bool new_news, std::string_view new_news_title, std::string_view new_news_desc_long, std::string_view new_news_desc_medium, std::string_view new_news_desc_short, - std::string_view new_picture + std::string_view new_picture, ConditionScript&& new_potential, ConditionScript&& new_allow, + ConditionalWeight&& new_ai_will_do, EffectScript&& new_effect ) : HasIdentifier { new_identifier }, alert { new_alert }, news { new_news }, news_title { new_news_title }, news_desc_long { new_news_desc_long }, news_desc_medium { new_news_desc_medium }, - news_desc_short { new_news_desc_short }, picture { new_picture } {} + news_desc_short { new_news_desc_short }, picture { new_picture }, potential { std::move(new_potential) }, + allow { std::move(new_allow) }, ai_will_do { std::move(new_ai_will_do) }, effect { std::move(new_effect) } {} + +bool Decision::parse_scripts(GameManager& game_manager) { + bool ret = true; + ret &= potential.parse_script(false, game_manager); + ret &= allow.parse_script(false, game_manager); + ret &= ai_will_do.parse_scripts(game_manager); + ret &= effect.parse_script(false, game_manager); + return ret; +} bool DecisionManager::add_decision( std::string_view identifier, bool alert, bool news, std::string_view news_title, std::string_view news_desc_long, - std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture + std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture, ConditionScript&& potential, + ConditionScript&& allow, ConditionalWeight&& ai_will_do, EffectScript&& effect ) { if (identifier.empty()) { Logger::error("Invalid decision identifier - empty!"); @@ -33,7 +45,8 @@ bool DecisionManager::add_decision( } return decisions.add_item({ - identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture + identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture, std::move(potential), + std::move(allow), std::move(ai_will_do), std::move(effect) }, duplicate_warning_callback); } @@ -43,6 +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; + EffectScript effect; bool ret = expect_dictionary_keys( "alert", ZERO_OR_ONE, expect_bool(assign_variable_callback(alert)), "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), @@ -51,16 +67,25 @@ bool DecisionManager::load_decision_file(ast::NodeCPtr root) { "news_desc_medium", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_medium)), "news_desc_short", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_short)), "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(picture)), - "potential", ONE_EXACTLY, success_callback, //TODO - "allow", ONE_EXACTLY, success_callback, //TODO - "effect", ONE_EXACTLY, success_callback, //TODO - "ai_will_do", ZERO_OR_ONE, success_callback //TODO + "potential", ONE_EXACTLY, potential.expect_script(), + "allow", ONE_EXACTLY, allow.expect_script(), + "effect", ONE_EXACTLY, effect.expect_script(), + "ai_will_do", ZERO_OR_ONE, ai_will_do.expect_conditional_weight(ConditionalWeight::FACTOR) )(node); ret &= add_decision( - identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture + identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture, + std::move(potential), std::move(allow), std::move(ai_will_do), std::move(effect) ); return ret; } ) )(root); } + +bool DecisionManager::parse_scripts(GameManager& game_manager) { + bool ret = true; + for (Decision& decision : decisions.get_items()) { + ret &= decision.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/misc/Decision.hpp b/src/openvic-simulation/misc/Decision.hpp index 32141fe..593b6e4 100644 --- a/src/openvic-simulation/misc/Decision.hpp +++ b/src/openvic-simulation/misc/Decision.hpp @@ -1,5 +1,7 @@ #pragma once +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { @@ -11,18 +13,25 @@ namespace OpenVic { private: const bool PROPERTY_CUSTOM_PREFIX(alert, has); const bool PROPERTY_CUSTOM_PREFIX(news, is); - const std::string PROPERTY(news_title); - const std::string PROPERTY(news_desc_long); - const std::string PROPERTY(news_desc_medium); - const std::string PROPERTY(news_desc_short); - const std::string PROPERTY(picture); + std::string PROPERTY(news_title); + std::string PROPERTY(news_desc_long); + std::string PROPERTY(news_desc_medium); + std::string PROPERTY(news_desc_short); + std::string PROPERTY(picture); + ConditionScript PROPERTY(potential); + ConditionScript PROPERTY(allow); + ConditionalWeight PROPERTY(ai_will_do); + EffectScript PROPERTY(effect); Decision( std::string_view new_identifier, bool new_alert, bool new_news, std::string_view new_news_title, std::string_view new_news_desc_long, std::string_view new_news_desc_medium, std::string_view new_news_desc_short, - std::string_view new_picture + std::string_view new_picture, ConditionScript&& new_potential, ConditionScript&& new_allow, + ConditionalWeight&& new_ai_will_do, EffectScript&& new_effect ); + bool parse_scripts(GameManager& game_manager); + public: Decision(Decision&&) = default; }; @@ -34,9 +43,12 @@ namespace OpenVic { public: bool add_decision( std::string_view identifier, bool alert, bool news, std::string_view news_title, std::string_view news_desc_long, - std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture + std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture, + ConditionScript&& potential, ConditionScript&& allow, ConditionalWeight&& ai_will_do, EffectScript&& effect ); bool load_decision_file(ast::NodeCPtr root); + + bool parse_scripts(GameManager& game_manager); }; } diff --git a/src/openvic-simulation/misc/Event.cpp b/src/openvic-simulation/misc/Event.cpp index 4507de4..fb97d63 100644 --- a/src/openvic-simulation/misc/Event.cpp +++ b/src/openvic-simulation/misc/Event.cpp @@ -6,19 +6,41 @@ using namespace OpenVic; using namespace OpenVic::NodeTools; -Event::EventOption::EventOption(std::string_view new_title) : title { new_title } {} +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) } {} + +bool Event::EventOption::parse_scripts(GameManager& game_manager) { + bool ret = true; + ret &= effect.parse_script(false, game_manager); + ret &= ai_chance.parse_scripts(game_manager); + return ret; +} Event::Event( std::string_view new_identifier, std::string_view new_title, std::string_view new_description, std::string_view new_image, event_type_t new_type, bool new_triggered_only, bool new_major, bool new_fire_only_once, bool new_allows_multiple_instances, bool new_news, std::string_view new_news_title, std::string_view new_news_desc_long, std::string_view new_news_desc_medium, std::string_view new_news_desc_short, bool new_election, - IssueGroup const* new_election_issue_group, std::vector<EventOption>&& new_options + IssueGroup const* new_election_issue_group, ConditionScript&& new_trigger, ConditionalWeight&& new_mean_time_to_happen, + EffectScript&& new_immediate, std::vector<EventOption>&& new_options ) : HasIdentifier { new_identifier }, title { new_title }, description { new_description }, image { new_image }, type { new_type }, triggered_only { new_triggered_only }, major { new_major }, fire_only_once { new_fire_only_once }, allows_multiple_instances { new_allows_multiple_instances }, news { new_news }, news_title { new_news_title }, news_desc_long { new_news_desc_long }, news_desc_medium { new_news_desc_medium }, news_desc_short { new_news_desc_short }, - election { new_election }, election_issue_group { new_election_issue_group }, options { std::move(new_options) } {} + election { new_election }, election_issue_group { new_election_issue_group }, trigger { std::move(new_trigger) }, + mean_time_to_happen { std::move(new_mean_time_to_happen) }, immediate { std::move(new_immediate) }, + options { std::move(new_options) } {} + +bool Event::parse_scripts(GameManager& game_manager) { + bool ret = true; + ret &= trigger.parse_script(true, game_manager); + ret &= mean_time_to_happen.parse_scripts(game_manager); + ret &= immediate.parse_script(true, game_manager); + for (EventOption& option : options) { + ret &= option.parse_scripts(game_manager); + } + return ret; +} OnAction::OnAction(std::string_view new_identifier, weight_map_t&& new_weighted_events) : HasIdentifier { new_identifier }, weighted_events { std::move(new_weighted_events) } {} @@ -27,8 +49,8 @@ bool EventManager::register_event( std::string_view identifier, std::string_view title, std::string_view description, std::string_view image, Event::event_type_t type, bool triggered_only, bool major, bool fire_only_once, bool allows_multiple_instances, bool news, std::string_view news_title, std::string_view news_desc_long, std::string_view news_desc_medium, - std::string_view news_desc_short, bool election, IssueGroup const* election_issue_group, - std::vector<Event::EventOption>&& options + std::string_view news_desc_short, bool election, IssueGroup const* election_issue_group, ConditionScript&& trigger, + ConditionalWeight&& mean_time_to_happen, EffectScript&& immediate, std::vector<Event::EventOption>&& options ) { if (identifier.empty()) { Logger::error("Invalid event ID - empty!"); @@ -68,7 +90,8 @@ bool EventManager::register_event( return events.add_item({ identifier, title, description, image, type, triggered_only, major, fire_only_once, allows_multiple_instances, news, - news_title, news_desc_long, news_desc_medium, news_desc_short, election, election_issue_group, std::move(options) + news_title, news_desc_long, news_desc_medium, news_desc_short, election, election_issue_group, std::move(trigger), + std::move(mean_time_to_happen), std::move(immediate), std::move(options) }, duplicate_warning_callback); } @@ -90,6 +113,9 @@ bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeC 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; if (key == "country_event") { @@ -120,25 +146,28 @@ bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeC 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; + EffectScript effect; + ConditionalWeight ai_chance; 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(title)), + "ai_chance", ZERO_OR_ONE, ai_chance.expect_conditional_weight(ConditionalWeight::FACTOR) )(node); - // TODO: option effects + ret &= effect.expect_script()(node); - options.push_back({ title }); + options.push_back({ title, std::move(effect), std::move(ai_chance) }); return ret; }, - "trigger", ZERO_OR_ONE, success_callback, // TODO - trigger condition - "mean_time_to_happen", ZERO_OR_ONE, success_callback, // TODO - MTTH weighted conditions - "immediate", ZERO_OR_MORE, success_callback // TODO - immediate effects + "trigger", ZERO_OR_ONE, trigger.expect_script(), + "mean_time_to_happen", ZERO_OR_ONE, mean_time_to_happen.expect_conditional_weight(ConditionalWeight::MONTHS), + "immediate", ZERO_OR_MORE, immediate.expect_script() )(value); ret &= register_event( identifier, title, description, image, type, triggered_only, major, fire_only_once, allows_multiple_instances, news, news_title, news_desc_long, news_desc_medium, news_desc_short, election, election_issue_group, - std::move(options) + std::move(trigger), std::move(mean_time_to_happen), std::move(immediate), std::move(options) ); return ret; } @@ -176,4 +205,12 @@ bool EventManager::load_on_action_file(ast::NodeCPtr root) { })(root); on_actions.lock(); return ret; -}
\ No newline at end of file +} + +bool EventManager::parse_scripts(GameManager& game_manager) { + bool ret = true; + for (Event& event : events.get_items()) { + ret &= event.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/misc/Event.hpp b/src/openvic-simulation/misc/Event.hpp index c41ef3f..37f9270 100644 --- a/src/openvic-simulation/misc/Event.hpp +++ b/src/openvic-simulation/misc/Event.hpp @@ -1,5 +1,7 @@ #pragma once +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" @@ -15,12 +17,16 @@ namespace OpenVic { struct EventOption { friend struct EventManager; + friend struct Event; private: std::string PROPERTY(title); - // TODO: option effects + EffectScript PROPERTY(effect); + ConditionalWeight PROPERTY(ai_chance); - EventOption(std::string_view new_title); + EventOption(std::string_view new_title, EffectScript&& new_effect, ConditionalWeight&& new_ai_chance); + + bool parse_scripts(GameManager& game_manager); public: EventOption(EventOption const&) = delete; @@ -48,18 +54,23 @@ namespace OpenVic { bool PROPERTY_CUSTOM_PREFIX(election, is); IssueGroup const* PROPERTY(election_issue_group); - std::vector<EventOption> PROPERTY(options); + ConditionScript PROPERTY(trigger); + ConditionalWeight PROPERTY(mean_time_to_happen); + EffectScript PROPERTY(immediate); - // TODO: triggers, MTTH, immediate effects + std::vector<EventOption> PROPERTY(options); Event( std::string_view new_identifier, std::string_view new_title, std::string_view new_description, std::string_view new_image, event_type_t new_type, bool new_triggered_only, bool new_major, bool new_fire_only_once, bool new_allows_multiple_instances, bool new_news, std::string_view new_news_title, std::string_view new_news_desc_long, std::string_view new_news_desc_medium, std::string_view new_news_desc_short, - bool new_election, IssueGroup const* new_election_issue_group, std::vector<EventOption>&& new_options + bool new_election, IssueGroup const* new_election_issue_group, ConditionScript&& new_trigger, + ConditionalWeight&& new_mean_time_to_happen, EffectScript&& new_immediate, std::vector<EventOption>&& new_options ); + bool parse_scripts(GameManager& game_manager); + public: Event(Event&&) = default; }; @@ -88,13 +99,15 @@ namespace OpenVic { std::string_view identifier, std::string_view title, std::string_view description, std::string_view image, Event::event_type_t type, bool triggered_only, bool major, bool fire_only_once, bool allows_multiple_instances, bool news, std::string_view news_title, std::string_view news_desc_long, std::string_view news_desc_medium, - std::string_view news_desc_short, bool election, IssueGroup const* election_issue_group, - std::vector<Event::EventOption>&& options + std::string_view news_desc_short, bool election, IssueGroup const* election_issue_group, ConditionScript&& trigger, + ConditionalWeight&& mean_time_to_happen, EffectScript&& immediate, std::vector<Event::EventOption>&& options ); bool add_on_action(std::string_view identifier, OnAction::weight_map_t&& new_weighted_events); bool load_event_file(IssueManager const& issue_manager, ast::NodeCPtr root); bool load_on_action_file(ast::NodeCPtr root); + + bool parse_scripts(GameManager& game_manager); }; } // namespace OpenVic diff --git a/src/openvic-simulation/misc/Modifier.cpp b/src/openvic-simulation/misc/Modifier.cpp index 94d38e0..bbc8f59 100644 --- a/src/openvic-simulation/misc/Modifier.cpp +++ b/src/openvic-simulation/misc/Modifier.cpp @@ -82,8 +82,13 @@ ModifierValue ModifierValue::operator-(ModifierValue const& right) const { Modifier::Modifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon) : HasIdentifier { new_identifier }, ModifierValue { std::move(new_values) }, icon { new_icon } {} -TriggeredModifier::TriggeredModifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon) - : Modifier { new_identifier, std::move(new_values), new_icon } {} +TriggeredModifier::TriggeredModifier( + std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, ConditionScript&& new_trigger +) : Modifier { new_identifier, std::move(new_values), new_icon }, trigger { std::move(new_trigger) } {} + +bool TriggeredModifier::parse_scripts(GameManager const& game_manager) { + return trigger.parse_script(false, game_manager); +} ModifierInstance::ModifierInstance(Modifier const& modifier, Date expiry_date) : modifier { modifier }, expiry_date { expiry_date } {} @@ -319,12 +324,14 @@ bool ModifierManager::load_static_modifiers(ast::NodeCPtr root) { return ret; } -bool ModifierManager::add_triggered_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon) { +bool ModifierManager::add_triggered_modifier( + std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, ConditionScript&& trigger +) { if (identifier.empty()) { Logger::error("Invalid triggered modifier effect identifier - empty!"); return false; } - return triggered_modifiers.add_item({ identifier, std::move(values), icon }, duplicate_warning_callback); + return triggered_modifiers.add_item({ identifier, std::move(values), icon, std::move(trigger) }, duplicate_warning_callback); } bool ModifierManager::load_triggered_modifiers(ast::NodeCPtr root) { @@ -333,12 +340,14 @@ 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; + bool ret = expect_modifier_value_and_keys( move_variable_callback(modifier_value), "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)), - "trigger", ONE_EXACTLY, success_callback // TODO - load condition + "trigger", ONE_EXACTLY, trigger.expect_script() )(value); - ret &= add_triggered_modifier(key, std::move(modifier_value), icon); + ret &= add_triggered_modifier(key, std::move(modifier_value), icon, std::move(trigger)); return ret; } )(root); @@ -346,6 +355,14 @@ bool ModifierManager::load_triggered_modifiers(ast::NodeCPtr root) { return ret; } +bool ModifierManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (TriggeredModifier& modifier : triggered_modifiers.get_items()) { + ret &= modifier.parse_scripts(game_manager); + } + return ret; +} + key_value_callback_t ModifierManager::_modifier_effect_callback( ModifierValue& modifier, key_value_callback_t default_callback, ModifierEffectValidator auto effect_validator ) const { diff --git a/src/openvic-simulation/misc/Modifier.hpp b/src/openvic-simulation/misc/Modifier.hpp index f3a2499..033cc47 100644 --- a/src/openvic-simulation/misc/Modifier.hpp +++ b/src/openvic-simulation/misc/Modifier.hpp @@ -1,5 +1,6 @@ #pragma once +#include "openvic-simulation/scripts/ConditionScript.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { @@ -83,10 +84,14 @@ namespace OpenVic { friend struct ModifierManager; private: - // TODO - trigger condition + ConditionScript trigger; protected: - TriggeredModifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon); + TriggeredModifier( + std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, ConditionScript&& new_trigger + ); + + bool parse_scripts(GameManager const& game_manager); public: TriggeredModifier(TriggeredModifier&&) = default; @@ -139,9 +144,13 @@ namespace OpenVic { bool add_static_modifier(std::string_view identifier, ModifierValue&& values); bool load_static_modifiers(ast::NodeCPtr root); - bool add_triggered_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon); + bool add_triggered_modifier( + std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, ConditionScript&& trigger + ); bool load_triggered_modifiers(ast::NodeCPtr root); + bool parse_scripts(GameManager const& game_manager); + NodeTools::node_callback_t expect_validated_modifier_value_and_default( NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback, ModifierEffectValidator auto effect_validator diff --git a/src/openvic-simulation/politics/Ideology.cpp b/src/openvic-simulation/politics/Ideology.cpp index 577ab79..2678bee 100644 --- a/src/openvic-simulation/politics/Ideology.cpp +++ b/src/openvic-simulation/politics/Ideology.cpp @@ -9,9 +9,27 @@ IdeologyGroup::IdeologyGroup(std::string_view new_identifier) : HasIdentifier { Ideology::Ideology( std::string_view new_identifier, colour_t new_colour, IdeologyGroup const& new_group, bool new_uncivilised, - bool new_can_reduce_militancy, Date new_spawn_date + bool new_can_reduce_militancy, Date new_spawn_date, ConditionalWeight&& new_add_political_reform, + ConditionalWeight&& new_remove_political_reform, ConditionalWeight&& new_add_social_reform, + ConditionalWeight&& new_remove_social_reform, ConditionalWeight&& new_add_military_reform, + ConditionalWeight&& new_add_economic_reform ) : HasIdentifierAndColour { new_identifier, new_colour, false }, group { new_group }, uncivilised { new_uncivilised }, - can_reduce_militancy { new_can_reduce_militancy }, spawn_date { new_spawn_date } {} + can_reduce_militancy { new_can_reduce_militancy }, spawn_date { new_spawn_date }, + add_political_reform { std::move(new_add_political_reform) }, + remove_political_reform { std::move(new_remove_political_reform) }, + add_social_reform { std::move(new_add_social_reform) }, remove_social_reform { std::move(new_remove_social_reform) }, + add_military_reform { std::move(new_add_military_reform) }, add_economic_reform { std::move(new_add_economic_reform) } {} + +bool Ideology::parse_scripts(GameManager const& game_manager) { + bool ret = true; + ret &= add_political_reform.parse_scripts(game_manager); + ret &= remove_political_reform.parse_scripts(game_manager); + ret &= add_social_reform.parse_scripts(game_manager); + ret &= remove_social_reform.parse_scripts(game_manager); + ret &= add_military_reform.parse_scripts(game_manager); + ret &= add_economic_reform.parse_scripts(game_manager); + return ret; +} bool IdeologyManager::add_ideology_group(std::string_view identifier) { if (identifier.empty()) { @@ -24,7 +42,9 @@ bool IdeologyManager::add_ideology_group(std::string_view identifier) { bool IdeologyManager::add_ideology( std::string_view identifier, colour_t colour, IdeologyGroup const* group, bool uncivilised, bool can_reduce_militancy, - Date spawn_date + Date spawn_date, ConditionalWeight&& add_political_reform, ConditionalWeight&& remove_political_reform, + ConditionalWeight&& add_social_reform, ConditionalWeight&& remove_social_reform, ConditionalWeight&& add_military_reform, + ConditionalWeight&& add_economic_reform ) { if (identifier.empty()) { Logger::error("Invalid ideology identifier - empty!"); @@ -36,7 +56,11 @@ bool IdeologyManager::add_ideology( return false; } - return ideologies.add_item({ identifier, colour, *group, uncivilised, can_reduce_militancy, spawn_date }); + return ideologies.add_item({ + identifier, colour, *group, uncivilised, can_reduce_militancy, spawn_date, std::move(add_political_reform), + std::move(remove_political_reform), std::move(add_social_reform), std::move(remove_social_reform), + std::move(add_military_reform), std::move(add_economic_reform) + }); } /* REQUIREMENTS: @@ -61,20 +85,26 @@ 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; bool ret = expect_dictionary_keys( "uncivilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(uncivilised)), "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), "date", ZERO_OR_ONE, expect_date(assign_variable_callback(spawn_date)), "can_reduce_militancy", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_reduce_militancy)), - "add_political_reform", ONE_EXACTLY, success_callback, - "remove_political_reform", ONE_EXACTLY, success_callback, - "add_social_reform", ONE_EXACTLY, success_callback, - "remove_social_reform", ONE_EXACTLY, success_callback, - "add_military_reform", ZERO_OR_ONE, success_callback, - "add_economic_reform", ZERO_OR_ONE, success_callback + "add_political_reform", ONE_EXACTLY, add_political_reform.expect_conditional_weight(ConditionalWeight::BASE), + "remove_political_reform", ONE_EXACTLY, remove_political_reform.expect_conditional_weight(ConditionalWeight::BASE), + "add_social_reform", ONE_EXACTLY, add_social_reform.expect_conditional_weight(ConditionalWeight::BASE), + "remove_social_reform", ONE_EXACTLY, remove_social_reform.expect_conditional_weight(ConditionalWeight::BASE), + "add_military_reform", ZERO_OR_ONE, add_military_reform.expect_conditional_weight(ConditionalWeight::BASE), + "add_economic_reform", ZERO_OR_ONE, add_economic_reform.expect_conditional_weight(ConditionalWeight::BASE) )(value); - ret &= add_ideology(key, colour, ideology_group, uncivilised, can_reduce_militancy, spawn_date); + ret &= add_ideology( + key, colour, ideology_group, uncivilised, can_reduce_militancy, spawn_date, std::move(add_political_reform), + std::move(remove_political_reform), std::move(add_social_reform), std::move(remove_social_reform), + std::move(add_military_reform), std::move(add_economic_reform) + ); return ret; })(ideology_group_value); })(root); @@ -82,3 +112,11 @@ bool IdeologyManager::load_ideology_file(ast::NodeCPtr root) { return ret; } + +bool IdeologyManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (Ideology& ideology : ideologies.get_items()) { + ideology.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/politics/Ideology.hpp b/src/openvic-simulation/politics/Ideology.hpp index 8039dbd..ad07f06 100644 --- a/src/openvic-simulation/politics/Ideology.hpp +++ b/src/openvic-simulation/politics/Ideology.hpp @@ -1,5 +1,6 @@ #pragma once +#include "openvic-simulation/scripts/ConditionalWeight.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { @@ -23,14 +24,25 @@ namespace OpenVic { const bool PROPERTY_CUSTOM_PREFIX(uncivilised, is); const bool PROPERTY(can_reduce_militancy); const Date PROPERTY(spawn_date); + ConditionalWeight PROPERTY(add_political_reform); + ConditionalWeight PROPERTY(remove_political_reform); + ConditionalWeight PROPERTY(add_social_reform); + ConditionalWeight PROPERTY(remove_social_reform); + ConditionalWeight PROPERTY(add_military_reform); + ConditionalWeight PROPERTY(add_economic_reform); // TODO - willingness to repeal/pass reforms (and its modifiers) Ideology( std::string_view new_identifier, colour_t new_colour, IdeologyGroup const& new_group, bool new_uncivilised, - bool new_can_reduce_militancy, Date new_spawn_date + bool new_can_reduce_militancy, Date new_spawn_date, ConditionalWeight&& new_add_political_reform, + ConditionalWeight&& new_remove_political_reform, ConditionalWeight&& new_add_social_reform, + ConditionalWeight&& new_remove_social_reform, ConditionalWeight&& new_add_military_reform, + ConditionalWeight&& new_add_economic_reform ); + bool parse_scripts(GameManager const& game_manager); + public: Ideology(Ideology&&) = default; }; @@ -45,9 +57,14 @@ namespace OpenVic { bool add_ideology( std::string_view identifier, colour_t colour, IdeologyGroup const* group, bool uncivilised, - bool can_reduce_militancy, Date spawn_date + bool can_reduce_militancy, Date spawn_date, ConditionalWeight&& add_political_reform, + ConditionalWeight&& remove_political_reform, ConditionalWeight&& add_social_reform, + ConditionalWeight&& remove_social_reform, ConditionalWeight&& add_military_reform, + ConditionalWeight&& add_economic_reform ); bool load_ideology_file(ast::NodeCPtr root); + + bool parse_scripts(GameManager const& game_manager); }; } diff --git a/src/openvic-simulation/politics/Issue.cpp b/src/openvic-simulation/politics/Issue.cpp index 4ad1625..3e64af4 100644 --- a/src/openvic-simulation/politics/Issue.cpp +++ b/src/openvic-simulation/politics/Issue.cpp @@ -17,9 +17,19 @@ ReformGroup::ReformGroup(std::string_view new_identifier, ReformType const& new_ Reform::Reform( std::string_view new_identifier, ModifierValue&& new_values, ReformGroup const& new_group, size_t new_ordinal, - RuleSet&& new_rules, tech_cost_t new_technology_cost -) : Issue { new_identifier, std::move(new_values), new_group, std::move(new_rules), false }, - ordinal { new_ordinal }, reform_group { new_group }, technology_cost { new_technology_cost } {} + RuleSet&& new_rules, tech_cost_t new_technology_cost, ConditionScript&& new_allow, + ConditionScript&& new_on_execute_trigger, EffectScript&& new_on_execute_effect +) : Issue { new_identifier, std::move(new_values), new_group, std::move(new_rules), false }, ordinal { new_ordinal }, + reform_group { new_group }, technology_cost { new_technology_cost }, allow { std::move(new_allow) }, + on_execute_trigger { std::move(new_on_execute_trigger) }, on_execute_effect { std::move(new_on_execute_effect) } {} + +bool Reform::parse_scripts(GameManager& game_manager) { + bool ret = true; + ret &= allow.parse_script(true, game_manager); + ret &= on_execute_trigger.parse_script(true, game_manager); + ret &= on_execute_effect.parse_script(true, game_manager); + return ret; +} bool IssueManager::add_issue_group(std::string_view identifier) { if (identifier.empty()) { @@ -30,7 +40,9 @@ bool IssueManager::add_issue_group(std::string_view identifier) { return issue_groups.add_item({ identifier }); } -bool IssueManager::add_issue(std::string_view identifier, ModifierValue&& values, IssueGroup const* group, RuleSet&& rules, bool jingoism) { +bool IssueManager::add_issue( + std::string_view identifier, ModifierValue&& values, IssueGroup const* group, RuleSet&& rules, bool jingoism +) { if (identifier.empty()) { Logger::error("Invalid issue identifier - empty!"); return false; @@ -68,8 +80,9 @@ bool IssueManager::add_reform_group(std::string_view identifier, ReformType cons } bool IssueManager::add_reform( - std::string_view identifier, ModifierValue&& values, ReformGroup const* group, size_t ordinal, - RuleSet&& rules, Reform::tech_cost_t technology_cost + std::string_view identifier, ModifierValue&& values, ReformGroup const* group, size_t ordinal, RuleSet&& rules, + Reform::tech_cost_t technology_cost, ConditionScript&& allow, ConditionScript&& on_execute_trigger, + EffectScript&& on_execute_effect ) { if (identifier.empty()) { Logger::error("Invalid issue identifier - empty!"); @@ -89,7 +102,10 @@ bool IssueManager::add_reform( Logger::warning("Non-zero technology cost ", technology_cost, " found in civilised reform ", identifier, "!"); } - return reforms.add_item({ identifier, std::move(values), *group, ordinal, std::move(rules), technology_cost }); + return reforms.add_item({ + identifier, std::move(values), *group, ordinal, std::move(rules), technology_cost, std::move(allow), + std::move(on_execute_trigger), std::move(on_execute_effect) + }); } bool IssueManager::_load_issue_group(size_t& expected_issues, std::string_view identifier, ast::NodeCPtr node) { @@ -129,17 +145,25 @@ bool IssueManager::_load_reform( ModifierManager const& modifier_manager, RuleManager const& rule_manager, size_t ordinal, std::string_view identifier, ReformGroup const* group, ast::NodeCPtr node ) { - // TODO: conditions to allow, policy rule changes ModifierValue values; RuleSet rules; Reform::tech_cost_t technology_cost = 0; + ConditionScript allow, on_execute_trigger; + EffectScript on_execute_effect; + bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(values), "technology_cost", ZERO_OR_ONE, expect_uint(assign_variable_callback(technology_cost)), - "allow", ZERO_OR_ONE, success_callback, //TODO: allow block + "allow", ZERO_OR_ONE, allow.expect_script(), "rules", ZERO_OR_ONE, rule_manager.expect_rule_set(move_variable_callback(rules)), - "on_execute", ZERO_OR_MORE, success_callback //TODO: trigger/effect blocks + "on_execute", ZERO_OR_ONE, expect_dictionary_keys( + "trigger", ZERO_OR_ONE, on_execute_trigger.expect_script(), + "effect", ONE_EXACTLY, on_execute_effect.expect_script() + ) )(node); - ret &= add_reform(identifier, std::move(values), group, ordinal, std::move(rules), technology_cost); + ret &= add_reform( + identifier, std::move(values), group, ordinal, std::move(rules), technology_cost, std::move(allow), + std::move(on_execute_trigger), std::move(on_execute_effect) + ); return ret; } @@ -157,10 +181,12 @@ bool IssueManager::load_issues_file(ModifierManager const& modifier_manager, Rul size_t expected_reform_groups = 0; bool ret = expect_dictionary_reserve_length(reform_types, [this, &expected_issue_groups, &expected_reform_groups](std::string_view key, ast::NodeCPtr value) -> bool { - if (key == "party_issues") + if (key == "party_issues") { return expect_length(add_variable_callback(expected_issue_groups))(value); - else return expect_length(add_variable_callback(expected_reform_groups))(value) - & add_reform_type(key, key == "economic_reforms" || "education_reforms" || "military_reforms"); + } else { + return expect_length(add_variable_callback(expected_reform_groups))(value) + & add_reform_type(key, key == "economic_reforms" || key == "education_reforms" || key == "military_reforms"); + } } )(root); lock_reform_types(); @@ -216,3 +242,11 @@ bool IssueManager::load_issues_file(ModifierManager const& modifier_manager, Rul return ret; } + +bool IssueManager::parse_scripts(GameManager& game_manager) { + bool ret = true; + for (Reform& reform : reforms.get_items()) { + ret &= reform.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/politics/Issue.hpp b/src/openvic-simulation/politics/Issue.hpp index a034959..b836e6f 100644 --- a/src/openvic-simulation/politics/Issue.hpp +++ b/src/openvic-simulation/politics/Issue.hpp @@ -1,8 +1,10 @@ #pragma once -#include "openvic-simulation/politics/Rule.hpp" -#include "openvic-simulation/misc/Modifier.hpp" #include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/misc/Modifier.hpp" +#include "openvic-simulation/politics/Rule.hpp" +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { @@ -76,12 +78,18 @@ namespace OpenVic { ReformGroup const& PROPERTY(reform_group); // stores an already casted reference const size_t PROPERTY(ordinal); // assigned by the parser to allow policy sorting const tech_cost_t PROPERTY(technology_cost); + ConditionScript PROPERTY(allow); + ConditionScript PROPERTY(on_execute_trigger); + EffectScript PROPERTY(on_execute_effect); Reform( std::string_view new_identifier, ModifierValue&& new_values, ReformGroup const& new_group, size_t new_ordinal, - RuleSet&& new_rules, tech_cost_t new_technology_cost + RuleSet&& new_rules, tech_cost_t new_technology_cost, ConditionScript&& new_allow, + ConditionScript&& new_on_execute_trigger, EffectScript&& new_on_execute_effect ); + bool parse_scripts(GameManager& game_manager); + public: Reform(Reform&&) = default; }; @@ -104,16 +112,24 @@ namespace OpenVic { size_t& expected_reforms, std::string_view identifier, ReformType const* type, ast::NodeCPtr node ); bool _load_reform( - ModifierManager const& modifier_manager, RuleManager const& rule_manager, size_t ordinal, std::string_view identifier, - ReformGroup const* group, ast::NodeCPtr node + ModifierManager const& modifier_manager, RuleManager const& rule_manager, size_t ordinal, + std::string_view identifier, ReformGroup const* group, ast::NodeCPtr node ); public: bool add_issue_group(std::string_view identifier); - bool add_issue(std::string_view identifier, ModifierValue&& values, IssueGroup const* group, RuleSet&& rules, bool jingoism); + bool add_issue( + std::string_view identifier, ModifierValue&& values, IssueGroup const* group, RuleSet&& rules, bool jingoism + ); bool add_reform_type(std::string_view identifier, bool uncivilised); bool add_reform_group(std::string_view identifier, ReformType const* type, bool ordered, bool administrative); - bool add_reform(std::string_view identifier, ModifierValue&& values, ReformGroup const* group, size_t ordinal, RuleSet&& rules, Reform::tech_cost_t technology_cost); + bool add_reform( + std::string_view identifier, ModifierValue&& values, ReformGroup const* group, size_t ordinal, RuleSet&& rules, + Reform::tech_cost_t technology_cost, ConditionScript&& allow, ConditionScript&& on_execute_trigger, + EffectScript&& on_execute_effect + ); bool load_issues_file(ModifierManager const& modifier_manager, RuleManager const& rule_manager, ast::NodeCPtr root); + + bool parse_scripts(GameManager& game_manager); }; } diff --git a/src/openvic-simulation/politics/NationalFocus.cpp b/src/openvic-simulation/politics/NationalFocus.cpp index a2003eb..d4066af 100644 --- a/src/openvic-simulation/politics/NationalFocus.cpp +++ b/src/openvic-simulation/politics/NationalFocus.cpp @@ -14,14 +14,20 @@ NationalFocus::NationalFocus( ModifierValue&& new_modifiers, pop_promotion_map_t&& new_encouraged_promotion, party_loyalty_map_t&& new_encouraged_loyalty, - production_map_t&& new_encouraged_production + production_map_t&& new_encouraged_production, + ConditionScript&& new_limit ) : HasIdentifier { new_identifier }, icon { new_icon }, group { new_group }, modifiers { std::move(new_modifiers) }, encouraged_promotion { std::move(new_encouraged_promotion) }, encouraged_loyalty { std::move(new_encouraged_loyalty) }, - encouraged_production { std::move(new_encouraged_production) } {} + encouraged_production { std::move(new_encouraged_production) }, + limit { std::move(new_limit) } {} + +bool NationalFocus::parse_scripts(GameManager const& game_manager) { + return limit.parse_script(true, game_manager); +} inline bool NationalFocusManager::add_national_focus_group(std::string_view identifier) { if (identifier.empty()) { @@ -38,7 +44,8 @@ inline bool NationalFocusManager::add_national_focus( ModifierValue&& modifiers, NationalFocus::pop_promotion_map_t&& encouraged_promotion, NationalFocus::party_loyalty_map_t&& encouraged_loyalty, - NationalFocus::production_map_t&& encouraged_production + NationalFocus::production_map_t&& encouraged_production, + ConditionScript&& limit ) { if (identifier.empty()) { Logger::error("No identifier for national focus!"); @@ -48,7 +55,10 @@ inline bool NationalFocusManager::add_national_focus( Logger::error("Invalid icon ", icon, " for national focus ", identifier); return false; } - return national_foci.add_item({ identifier, icon, group, std::move(modifiers), std::move(encouraged_promotion), std::move(encouraged_loyalty), std::move(encouraged_production) }); + return national_foci.add_item({ + identifier, icon, group, std::move(modifiers), std::move(encouraged_promotion), std::move(encouraged_loyalty), + std::move(encouraged_production), std::move(limit) + }); } bool NationalFocusManager::load_national_foci_file(PopManager const& pop_manager, IdeologyManager const& ideology_manager, GoodManager const& good_manager, ModifierManager const& modifier_manager, ast::NodeCPtr root) { @@ -64,6 +74,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; Ideology const* last_specified_ideology = nullptr; // weird, I know @@ -98,13 +109,16 @@ bool NationalFocusManager::load_national_foci_file(PopManager const& pop_manager loyalties[last_specified_ideology] += boost; return ret; }, - "limit", ZERO_OR_ONE, success_callback, // TODO: implement conditions + "limit", ZERO_OR_ONE, limit.expect_script(), "has_flashpoint", ZERO_OR_ONE, success_callback, // special case, include in limit "own_provinces", ZERO_OR_ONE, success_callback, // special case, include in limit "outliner_show_as_percent", ZERO_OR_ONE, success_callback // special case )(node); - add_national_focus(identifier, icon, group, std::move(modifiers), std::move(promotions), std::move(loyalties), std::move(production)); + add_national_focus( + identifier, icon, group, std::move(modifiers), std::move(promotions), std::move(loyalties), + std::move(production), std::move(limit) + ); return ret; })(node); @@ -114,3 +128,11 @@ bool NationalFocusManager::load_national_foci_file(PopManager const& pop_manager return ret; } + +bool NationalFocusManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (NationalFocus& national_focus : national_foci.get_items()) { + ret &= national_focus.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/politics/NationalFocus.hpp b/src/openvic-simulation/politics/NationalFocus.hpp index 0fe0ddc..44e58e3 100644 --- a/src/openvic-simulation/politics/NationalFocus.hpp +++ b/src/openvic-simulation/politics/NationalFocus.hpp @@ -1,13 +1,12 @@ #pragma once -#include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/utility/Getters.hpp" +#include "openvic-simulation/economy/Good.hpp" #include "openvic-simulation/misc/Modifier.hpp" -#include "openvic-simulation/pop/Pop.hpp" #include "openvic-simulation/politics/Ideology.hpp" -#include "openvic-simulation/economy/Good.hpp" - -#include <optional> +#include "openvic-simulation/pop/Pop.hpp" +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { struct NationalFocusManager; @@ -34,6 +33,7 @@ namespace OpenVic { pop_promotion_map_t PROPERTY(encouraged_promotion); party_loyalty_map_t PROPERTY(encouraged_loyalty); production_map_t PROPERTY(encouraged_production); + ConditionScript PROPERTY(limit); NationalFocus( std::string_view new_identifier, @@ -42,9 +42,12 @@ namespace OpenVic { ModifierValue&& new_modifiers, pop_promotion_map_t&& new_encouraged_promotion, party_loyalty_map_t&& new_encouraged_loyalty, - production_map_t&& new_encouraged_production + production_map_t&& new_encouraged_production, + ConditionScript&& new_limit ); + bool parse_scripts(GameManager const& game_manager); + public: NationalFocus(NationalFocus&&) = default; }; @@ -64,9 +67,15 @@ namespace OpenVic { ModifierValue&& modifiers, NationalFocus::pop_promotion_map_t&& encouraged_promotion, NationalFocus::party_loyalty_map_t&& encouraged_loyalty, - NationalFocus::production_map_t&& encouraged_production + NationalFocus::production_map_t&& encouraged_production, + ConditionScript&& limit + ); + + bool load_national_foci_file( + PopManager const& pop_manager, IdeologyManager const& ideology_manager, GoodManager const& good_manager, + ModifierManager const& modifier_manager, ast::NodeCPtr root ); - bool load_national_foci_file(PopManager const& pop_manager, IdeologyManager const& ideology_manager, GoodManager const& good_manager, ModifierManager const& modifier_manager, ast::NodeCPtr root); + bool parse_scripts(GameManager const& game_manager); }; } // namespace OpenVic diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp index 6850e83..ca8e945 100644 --- a/src/openvic-simulation/politics/Rebel.cpp +++ b/src/openvic-simulation/politics/Rebel.cpp @@ -8,20 +8,41 @@ RebelType::RebelType( RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, bool reinforcing, - bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult + bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, ConditionalWeight&& new_will_rise, + ConditionalWeight&& new_spawn_chance, ConditionalWeight&& new_movement_evaluation, ConditionScript&& new_siege_won_trigger, + EffectScript&& new_siege_won_effect, ConditionScript&& new_demands_enforced_trigger, + EffectScript&& new_demands_enforced_effect ) : HasIdentifier { new_identifier }, icon { icon }, area { area }, break_alliance_on_win { break_alliance_on_win }, desired_governments { std::move(desired_governments) }, defection { defection }, independence { independence }, defect_delay { defect_delay }, ideology { ideology }, allow_all_cultures { allow_all_cultures }, allow_all_culture_groups { allow_all_culture_groups }, allow_all_religions { allow_all_religions }, allow_all_ideologies { allow_all_ideologies }, resilient { resilient }, reinforcing { reinforcing }, general { general }, - smart { smart }, unit_transfer { unit_transfer }, occupation_mult { occupation_mult } {} + smart { smart }, unit_transfer { unit_transfer }, occupation_mult { occupation_mult }, + will_rise { std::move(new_will_rise) }, spawn_chance { std::move(new_spawn_chance) }, + movement_evaluation { std::move(new_movement_evaluation) }, siege_won_trigger { std::move(new_siege_won_trigger) }, + siege_won_effect { std::move(new_siege_won_effect) }, demands_enforced_trigger { std::move(new_demands_enforced_trigger) }, + demands_enforced_effect { std::move(new_demands_enforced_effect) } {} + +bool RebelType::parse_scripts(GameManager& game_manager) { + bool ret = true; + ret &= will_rise.parse_scripts(game_manager); + ret &= spawn_chance.parse_scripts(game_manager); + ret &= movement_evaluation.parse_scripts(game_manager); + ret &= siege_won_trigger.parse_script(true, game_manager); + ret &= siege_won_effect.parse_script(true, game_manager); + ret &= demands_enforced_trigger.parse_script(true, game_manager); + ret &= demands_enforced_effect.parse_script(true, game_manager); + return ret; +} bool RebelManager::add_rebel_type( std::string_view new_identifier, RebelType::icon_t icon, RebelType::area_t area, bool break_alliance_on_win, RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, bool reinforcing, - bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult + bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, ConditionalWeight&& will_rise, + ConditionalWeight&& spawn_chance, ConditionalWeight&& movement_evaluation, ConditionScript&& siege_won_trigger, + EffectScript&& siege_won_effect, ConditionScript&& demands_enforced_trigger, EffectScript&& demands_enforced_effect ) { if (new_identifier.empty()) { Logger::error("Invalid rebel type identifier - empty!"); @@ -31,7 +52,9 @@ bool RebelManager::add_rebel_type( return rebel_types.add_item({ new_identifier, icon, area, break_alliance_on_win, std::move(desired_governments), defection, independence, defect_delay, ideology, allow_all_cultures, allow_all_culture_groups, allow_all_religions, allow_all_ideologies, - resilient, reinforcing, general, smart, unit_transfer, occupation_mult + resilient, reinforcing, general, smart, unit_transfer, occupation_mult, std::move(will_rise), std::move(spawn_chance), + std::move(movement_evaluation), std::move(siege_won_trigger), std::move(siege_won_effect), + std::move(demands_enforced_trigger), std::move(demands_enforced_effect) }); } @@ -81,6 +104,9 @@ 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; + EffectScript siege_won_effect, demands_enforced_effect; bool ret = expect_dictionary_keys( "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), @@ -118,19 +144,21 @@ bool RebelManager::load_rebels_file( "smart", ONE_EXACTLY, expect_bool(assign_variable_callback(smart)), "unit_transfer", ONE_EXACTLY, expect_bool(assign_variable_callback(unit_transfer)), "occupation_mult", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(occupation_mult)), - "will_rise", ONE_EXACTLY, success_callback, //TODO - "spawn_chance", ONE_EXACTLY, success_callback, //TODO - "movement_evaluation", ONE_EXACTLY, success_callback, //TODO - "siege_won_trigger", ZERO_OR_ONE, success_callback, //TODO - "siege_won_effect", ZERO_OR_ONE, success_callback, //TODO - "demands_enforced_trigger", ZERO_OR_ONE, success_callback, //TODO - "demands_enforced_effect", ZERO_OR_ONE, success_callback //TODO + "will_rise", ONE_EXACTLY, will_rise.expect_conditional_weight(ConditionalWeight::FACTOR), + "spawn_chance", ONE_EXACTLY, spawn_chance.expect_conditional_weight(ConditionalWeight::FACTOR), + "movement_evaluation", ONE_EXACTLY, movement_evaluation.expect_conditional_weight(ConditionalWeight::FACTOR), + "siege_won_trigger", ZERO_OR_ONE, siege_won_trigger.expect_script(), + "siege_won_effect", ZERO_OR_ONE, siege_won_effect.expect_script(), + "demands_enforced_trigger", ZERO_OR_ONE, demands_enforced_trigger.expect_script(), + "demands_enforced_effect", ZERO_OR_ONE, demands_enforced_effect.expect_script() )(node); ret &= add_rebel_type( identifier, icon, area, break_alliance_on_win, std::move(desired_governments), defection, independence, defect_delay, ideology, allow_all_cultures, allow_all_culture_groups, allow_all_religions, - allow_all_ideologies, resilient, reinforcing, general, smart, unit_transfer, occupation_mult + allow_all_ideologies, resilient, reinforcing, general, smart, unit_transfer, occupation_mult, + std::move(will_rise), std::move(spawn_chance), std::move(movement_evaluation), std::move(siege_won_trigger), + std::move(siege_won_effect), std::move(demands_enforced_trigger), std::move(demands_enforced_effect) ); return ret; @@ -155,4 +183,12 @@ bool RebelManager::generate_modifiers(ModifierManager& modifier_manager) const { ); } return ret; -}
\ No newline at end of file +} + +bool RebelManager::parse_scripts(GameManager& game_manager) { + bool ret = true; + for (RebelType& rebel_type : rebel_types.get_items()) { + ret &= rebel_type.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/politics/Rebel.hpp b/src/openvic-simulation/politics/Rebel.hpp index f7e8795..f098fe0 100644 --- a/src/openvic-simulation/politics/Rebel.hpp +++ b/src/openvic-simulation/politics/Rebel.hpp @@ -5,6 +5,8 @@ #include "openvic-simulation/misc/Modifier.hpp" #include "openvic-simulation/politics/Government.hpp" #include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" @@ -51,15 +53,28 @@ namespace OpenVic { const bool PROPERTY_CUSTOM_PREFIX(smart, is); const bool PROPERTY_CUSTOM_NAME(unit_transfer, will_transfer_units); const fixed_point_t PROPERTY(occupation_mult); + ConditionalWeight PROPERTY(will_rise); + ConditionalWeight PROPERTY(spawn_chance); + ConditionalWeight PROPERTY(movement_evaluation); + ConditionScript PROPERTY(siege_won_trigger); + EffectScript PROPERTY(siege_won_effect); + ConditionScript PROPERTY(demands_enforced_trigger); + EffectScript PROPERTY(demands_enforced_effect); RebelType( std::string_view new_identifier, RebelType::icon_t icon, RebelType::area_t area, bool break_alliance_on_win, RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, - bool reinforcing, bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult + bool reinforcing, bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, + ConditionalWeight&& new_will_rise, ConditionalWeight&& new_spawn_chance, + ConditionalWeight&& new_movement_evaluation, ConditionScript&& new_siege_won_trigger, + EffectScript&& new_siege_won_effect, ConditionScript&& new_demands_enforced_trigger, + EffectScript&& new_demands_enforced_effect ); + bool parse_scripts(GameManager& game_manager); + public: RebelType(RebelType&&) = default; }; @@ -74,10 +89,15 @@ namespace OpenVic { RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, - bool reinforcing, bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult + bool reinforcing, bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, + ConditionalWeight&& will_rise, ConditionalWeight&& spawn_chance, ConditionalWeight&& movement_evaluation, + ConditionScript&& siege_won_trigger, EffectScript&& siege_won_effect, ConditionScript&& demands_enforced_trigger, + EffectScript&& demands_enforced_effect ); bool load_rebels_file(IdeologyManager const& ideology_manager, GovernmentTypeManager const& government_type_manager, ast::NodeCPtr root); bool generate_modifiers(ModifierManager& modifier_manager) const; + + bool parse_scripts(GameManager& game_manager); }; }
\ No newline at end of file diff --git a/src/openvic-simulation/pop/Pop.cpp b/src/openvic-simulation/pop/Pop.cpp index 2b031c2..be664d5 100644 --- a/src/openvic-simulation/pop/Pop.cpp +++ b/src/openvic-simulation/pop/Pop.cpp @@ -4,9 +4,12 @@ #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; using namespace OpenVic::NodeTools; @@ -32,7 +35,9 @@ PopType::PopType( rebel_units_t&& new_rebel_units, Pop::pop_size_t new_max_size, Pop::pop_size_t new_merge_max_size, bool new_state_capital_only, bool new_demote_migrant, bool new_is_artisan, bool new_allowed_to_vote, bool new_is_slave, bool new_can_be_recruited, bool new_can_reduce_consciousness, bool new_administrative_efficiency, bool new_can_build, - bool new_factory, bool new_can_work_factory, bool new_unemployment + bool new_factory, bool new_can_work_factory, bool new_unemployment, ConditionalWeight&& new_country_migration_target, + ConditionalWeight&& new_migration_target, poptype_weight_map_t&& new_promote_to, ideology_weight_map_t&& new_ideologies, + issue_weight_map_t&& new_issues ) : HasIdentifierAndColour { new_identifier, new_colour, false }, strata { new_strata }, sprite { new_sprite }, life_needs { std::move(new_life_needs) }, everyday_needs { std::move(new_everyday_needs) }, luxury_needs { std::move(new_luxury_needs) }, rebel_units { std::move(new_rebel_units) }, max_size { new_max_size }, @@ -41,12 +46,30 @@ PopType::PopType( is_slave { new_is_slave }, can_be_recruited { new_can_be_recruited }, can_reduce_consciousness { new_can_reduce_consciousness }, administrative_efficiency { new_administrative_efficiency }, can_build { new_can_build }, factory { new_factory }, can_work_factory { new_can_work_factory }, - unemployment { new_unemployment } { + unemployment { new_unemployment }, country_migration_target { std::move(new_country_migration_target) }, + migration_target { std::move(new_migration_target) }, promote_to { std::move(new_promote_to) }, + ideologies { std::move(new_ideologies) }, issues { std::move(new_issues) } { assert(sprite > 0); assert(max_size >= 0); assert(merge_max_size >= 0); } +bool PopType::parse_scripts(GameManager const& game_manager) { + bool ret = true; + ret &= country_migration_target.parse_scripts(game_manager); + ret &= migration_target.parse_scripts(game_manager); + for (auto [pop_type, weight] : mutable_iterator(promote_to)) { + ret &= weight.parse_scripts(game_manager); + } + for (auto [ideology, weight] : mutable_iterator(ideologies)) { + ret &= weight.parse_scripts(game_manager); + } + for (auto [issue, weight] : mutable_iterator(issues)) { + ret &= weight.parse_scripts(game_manager); + } + return ret; +} + PopManager::PopManager() : slave_sprite { 0 }, administrative_sprite { 0 } {} bool PopManager::add_strata(std::string_view identifier) { @@ -63,7 +86,8 @@ bool PopManager::add_pop_type( PopType::rebel_units_t&& rebel_units, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size, bool state_capital_only, bool demote_migrant, bool is_artisan, bool allowed_to_vote, bool is_slave, bool can_be_recruited, bool can_reduce_consciousness, bool administrative_efficiency, bool can_build, bool factory, bool can_work_factory, - bool unemployment + bool unemployment, ConditionalWeight&& country_migration_target, ConditionalWeight&& migration_target, + ast::NodeCPtr promote_to_node, PopType::ideology_weight_map_t&& ideologies, ast::NodeCPtr issues_node ) { if (identifier.empty()) { Logger::error("Invalid pop type identifier - empty!"); @@ -89,8 +113,12 @@ bool PopManager::add_pop_type( identifier, colour, *strata, sprite, std::move(life_needs), std::move(everyday_needs), std::move(luxury_needs), std::move(rebel_units), max_size, merge_max_size, state_capital_only, demote_migrant, is_artisan, allowed_to_vote, is_slave, can_be_recruited, can_reduce_consciousness, - administrative_efficiency, can_build, factory, can_work_factory, unemployment + administrative_efficiency, can_build, factory, can_work_factory, unemployment, std::move(country_migration_target), + std::move(migration_target), {}, std::move(ideologies), {} }); + if (ret) { + delayed_parse_promote_to_and_issues_nodes.emplace_back(promote_to_node, issues_node); + } if (slave_sprite <= 0 && ret && is_slave) { /* Set slave sprite to that of the first is_slave pop type we find. */ slave_sprite = sprite; @@ -111,7 +139,8 @@ void PopManager::reserve_pop_types(size_t count) { * POP-3, POP-4, POP-5, POP-6, POP-7, POP-8, POP-9, POP-10, POP-11, POP-12, POP-13, POP-14 */ bool PopManager::load_pop_type_file( - std::string_view filestem, UnitManager const& unit_manager, GoodManager const& good_manager, ast::NodeCPtr root + std::string_view filestem, UnitManager const& unit_manager, GoodManager const& good_manager, + IdeologyManager const& ideology_manager, ast::NodeCPtr root ) { colour_t colour = colour_t::null(); Strata const* strata = nullptr; @@ -122,6 +151,11 @@ 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; + ast::NodeCPtr promote_to_node = nullptr; + PopType::ideology_weight_map_t ideologies; + ast::NodeCPtr issues_node = nullptr; + bool ret = expect_dictionary_keys( "sprite", ONE_EXACTLY, expect_uint(assign_variable_callback(sprite)), "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), @@ -157,11 +191,21 @@ bool PopManager::load_pop_type_file( "luxury_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(luxury_needs)), "everyday_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(everyday_needs)), "life_needs", ZERO_OR_ONE, good_manager.expect_good_decimal_map(move_variable_callback(life_needs)), - "country_migration_target", ZERO_OR_ONE, success_callback, // TODO - pop migration weight scripts - "migration_target", ZERO_OR_ONE, success_callback, - "promote_to", ZERO_OR_ONE, success_callback, // TODO - pop promotion weight scripts - "ideologies", ZERO_OR_ONE, success_callback, // TODO - pop politics weight scripts - "issues", ZERO_OR_ONE, success_callback, + "country_migration_target", ZERO_OR_ONE, country_migration_target.expect_conditional_weight(ConditionalWeight::FACTOR), + "migration_target", ZERO_OR_ONE, migration_target.expect_conditional_weight(ConditionalWeight::FACTOR), + "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; + 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); + ret = false; + } + return ret; + } + ), + "issues", ZERO_OR_ONE, assign_variable_callback(issues_node), "demote_migrant", ZERO_OR_ONE, expect_bool(assign_variable_callback(demote_migrant)), "administrative_efficiency", ZERO_OR_ONE, expect_bool(assign_variable_callback(administrative_efficiency)), "tax_eff", ZERO_OR_ONE, success_callback, // TODO - tax collection modifier @@ -178,11 +222,74 @@ bool PopManager::load_pop_type_file( filestem, colour, strata, sprite, std::move(life_needs), std::move(everyday_needs), std::move(luxury_needs), std::move(rebel_units), max_size, merge_max_size, state_capital_only, demote_migrant, is_artisan, allowed_to_vote, is_slave, can_be_recruited, can_reduce_consciousness, administrative_efficiency, can_build, factory, can_work_factory, - unemployment + unemployment, std::move(country_migration_target), std::move(migration_target), promote_to_node, std::move(ideologies), + issues_node ); return ret; } +bool PopManager::load_delayed_parse_pop_type_data(IssueManager const& issue_manager) { + bool ret = true; + for (size_t index = 0; index < delayed_parse_promote_to_and_issues_nodes.size(); ++index) { + const auto [promote_to_node, issues_node] = delayed_parse_promote_to_and_issues_nodes[index]; + PopType* pop_type = pop_types.get_item_by_index(index); + if (promote_to_node != nullptr && !expect_pop_type_dictionary( + [pop_type](PopType const& type, ast::NodeCPtr node) -> bool { + if (pop_type == &type) { + Logger::error("Pop type ", type, " cannot have promotion weight to itself!"); + return false; + } + ConditionalWeight weight; + 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); + ret = false; + } + return ret; + } + )(promote_to_node)) { + Logger::error("Errors parsing pop type ", pop_type, " promotion weights!"); + ret = false; + } + if (issues_node != nullptr && !expect_dictionary( + [pop_type, &issue_manager](std::string_view key, ast::NodeCPtr node) -> bool { + Issue const* issue = issue_manager.get_issue_by_identifier(key); + if (issue == nullptr) { + issue = issue_manager.get_reform_by_identifier(key); + } + if (issue == nullptr) { + Logger::error("Invalid issue in pop type ", pop_type, " issue weights: ", key); + return false; + } + ConditionalWeight weight; + 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()); + ret = false; + } + return ret; + } + )(issues_node)) { + Logger::error("Errors parsing pop type ", pop_type, " issue weights!"); + ret = false; + } + } + delayed_parse_promote_to_and_issues_nodes.clear(); + return ret; +} + +bool PopManager::load_pop_type_chances_file(ast::NodeCPtr root) { + return expect_dictionary_keys( + "promotion_chance", ONE_EXACTLY, promotion_chance.expect_conditional_weight(ConditionalWeight::FACTOR), + "demotion_chance", ONE_EXACTLY, demotion_chance.expect_conditional_weight(ConditionalWeight::FACTOR), + "migration_chance", ONE_EXACTLY, migration_chance.expect_conditional_weight(ConditionalWeight::FACTOR), + "colonialmigration_chance", ONE_EXACTLY, colonialmigration_chance.expect_conditional_weight(ConditionalWeight::FACTOR), + "emigration_chance", ONE_EXACTLY, emigration_chance.expect_conditional_weight(ConditionalWeight::FACTOR), + "assimilation_chance", ONE_EXACTLY, assimilation_chance.expect_conditional_weight(ConditionalWeight::FACTOR), + "conversion_chance", ONE_EXACTLY, conversion_chance.expect_conditional_weight(ConditionalWeight::FACTOR) + )(root); +} + bool PopManager::load_pop_into_vector( RebelManager const& rebel_manager, std::vector<Pop>& vec, PopType const& type, ast::NodeCPtr pop_node, bool *non_integer_size @@ -235,3 +342,18 @@ bool PopManager::generate_modifiers(ModifierManager& modifier_manager) const { } return ret; } + +bool PopManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (PopType& pop_type : pop_types.get_items()) { + ret &= pop_type.parse_scripts(game_manager); + } + ret &= promotion_chance.parse_scripts(game_manager); + ret &= demotion_chance.parse_scripts(game_manager); + ret &= migration_chance.parse_scripts(game_manager); + ret &= colonialmigration_chance.parse_scripts(game_manager); + ret &= emigration_chance.parse_scripts(game_manager); + ret &= assimilation_chance.parse_scripts(game_manager); + ret &= conversion_chance.parse_scripts(game_manager); + return ret; +} diff --git a/src/openvic-simulation/pop/Pop.hpp b/src/openvic-simulation/pop/Pop.hpp index 0c84aae..2304165 100644 --- a/src/openvic-simulation/pop/Pop.hpp +++ b/src/openvic-simulation/pop/Pop.hpp @@ -4,6 +4,7 @@ #include "openvic-simulation/military/Unit.hpp" #include "openvic-simulation/pop/Culture.hpp" #include "openvic-simulation/pop/Religion.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" namespace OpenVic { @@ -11,6 +12,10 @@ namespace OpenVic { struct PopType; struct RebelType; struct RebelManager; + struct Ideology; + struct IdeologyManager; + struct Issue; + struct IssueManager; /* REQUIREMENTS: * POP-18, POP-19, POP-20, POP-21, POP-34, POP-35, POP-36, POP-37 @@ -65,14 +70,17 @@ namespace OpenVic { using sprite_t = uint8_t; using rebel_units_t = fixed_point_map_t<Unit const*>; + using poptype_weight_map_t = ordered_map<PopType const*, ConditionalWeight>; + using ideology_weight_map_t = ordered_map<Ideology const*, ConditionalWeight>; + using issue_weight_map_t = ordered_map<Issue const*, ConditionalWeight>; private: Strata const& PROPERTY(strata); const sprite_t PROPERTY(sprite); - const Good::good_map_t PROPERTY(life_needs); - const Good::good_map_t PROPERTY(everyday_needs); - const Good::good_map_t PROPERTY(luxury_needs); - const rebel_units_t PROPERTY(rebel_units); + Good::good_map_t PROPERTY(life_needs); + Good::good_map_t PROPERTY(everyday_needs); + Good::good_map_t PROPERTY(luxury_needs); + rebel_units_t PROPERTY(rebel_units); const Pop::pop_size_t PROPERTY(max_size); const Pop::pop_size_t PROPERTY(merge_max_size); const bool PROPERTY(state_capital_only); @@ -88,7 +96,11 @@ namespace OpenVic { const bool PROPERTY(can_work_factory); const bool PROPERTY(unemployment); - // TODO - country and province migration targets, promote_to targets, ideologies and issues + ConditionalWeight PROPERTY(country_migration_target); /* Scope - country, THIS - pop */ + ConditionalWeight PROPERTY(migration_target); /* Scope - province, THIS - pop */ + poptype_weight_map_t PROPERTY(promote_to); /* Scope - pop */ + ideology_weight_map_t PROPERTY(ideologies); /* Scope - pop */ + issue_weight_map_t PROPERTY(issues); /* Scope - province, THIS - country (?) */ PopType( std::string_view new_identifier, colour_t new_colour, Strata const& new_strata, sprite_t new_sprite, @@ -97,9 +109,12 @@ namespace OpenVic { bool new_state_capital_only, bool new_demote_migrant, bool new_is_artisan, bool new_allowed_to_vote, bool new_is_slave, bool new_can_be_recruited, bool new_can_reduce_consciousness, bool new_administrative_efficiency, bool new_can_build, bool new_factory, bool new_can_work_factory, - bool new_unemployment + bool new_unemployment, ConditionalWeight&& new_country_migration_target, ConditionalWeight&& new_migration_target, + poptype_weight_map_t&& new_promote_to, ideology_weight_map_t&& new_ideologies, issue_weight_map_t&& new_issues ); + bool parse_scripts(GameManager const& game_manager); + public: PopType(PopType&&) = default; }; @@ -111,6 +126,20 @@ namespace OpenVic { /* Using strata/stratas instead of stratum/strata to avoid confusion. */ IdentifierRegistry<Strata> IDENTIFIER_REGISTRY(strata); IdentifierRegistry<PopType> IDENTIFIER_REGISTRY(pop_type); + /* promote_to can't be parsed until after all PopTypes are registered, and issues requires Issues to be loaded, + * which themselves depend on pop strata. To get around this, the nodes for these variables are stored here and + * parsed after both PopTypes and Issues. The nodes will remain valid as PopType files' Parser objects are cached + * to preserve their condition script nodes until all other defines are loaded and the scripts can be parsed. */ + std::vector<std::pair<ast::NodeCPtr, ast::NodeCPtr>> delayed_parse_promote_to_and_issues_nodes; + + ConditionalWeight PROPERTY(promotion_chance); + ConditionalWeight PROPERTY(demotion_chance); + ConditionalWeight PROPERTY(migration_chance); + ConditionalWeight PROPERTY(colonialmigration_chance); + ConditionalWeight PROPERTY(emigration_chance); + ConditionalWeight PROPERTY(assimilation_chance); + ConditionalWeight PROPERTY(conversion_chance); + PopType::sprite_t PROPERTY(slave_sprite); PopType::sprite_t PROPERTY(administrative_sprite); @@ -128,19 +157,28 @@ namespace OpenVic { PopType::rebel_units_t&& rebel_units, Pop::pop_size_t max_size, Pop::pop_size_t merge_max_size, bool state_capital_only, bool demote_migrant, bool is_artisan, bool allowed_to_vote, bool is_slave, bool can_be_recruited, bool can_reduce_consciousness, bool administrative_efficiency, bool can_build, bool factory, - bool can_work_factory, bool unemployment + bool can_work_factory, bool unemployment, ConditionalWeight&& country_migration_target, + ConditionalWeight&& migration_target, ast::NodeCPtr promote_to_node, PopType::ideology_weight_map_t&& ideologies, + ast::NodeCPtr issues_node ); void reserve_pop_types(size_t count); bool load_pop_type_file( - std::string_view filestem, UnitManager const& unit_manager, GoodManager const& good_manager, ast::NodeCPtr root + std::string_view filestem, UnitManager const& unit_manager, GoodManager const& good_manager, + IdeologyManager const& ideology_manager, ast::NodeCPtr root ); + bool load_delayed_parse_pop_type_data(IssueManager const& issue_manager); + + bool load_pop_type_chances_file(ast::NodeCPtr root); + bool load_pop_into_vector( RebelManager const& rebel_manager, std::vector<Pop>& vec, PopType const& type, ast::NodeCPtr pop_node, bool *non_integer_size ) const; bool generate_modifiers(ModifierManager& modifier_manager) const; + + bool parse_scripts(GameManager const& game_manager); }; } diff --git a/src/openvic-simulation/research/Invention.cpp b/src/openvic-simulation/research/Invention.cpp index f9f3f7b..473b0b4 100644 --- a/src/openvic-simulation/research/Invention.cpp +++ b/src/openvic-simulation/research/Invention.cpp @@ -10,16 +10,23 @@ using namespace OpenVic::NodeTools; Invention::Invention( std::string_view new_identifier, ModifierValue&& new_values, bool new_news, unit_set_t&& new_activated_units, building_set_t&& new_activated_buildings, crime_set_t&& new_enabled_crimes, bool new_unlock_gas_attack, - bool new_unlock_gas_defence + bool new_unlock_gas_defence, ConditionScript&& new_limit, ConditionalWeight&& new_chance ) : Modifier { new_identifier, std::move(new_values), 0 }, news { new_news }, activated_units { std::move(new_activated_units) }, activated_buildings { std::move(new_activated_buildings) }, enabled_crimes { std::move(new_enabled_crimes) }, unlock_gas_attack { new_unlock_gas_attack }, - unlock_gas_defence { new_unlock_gas_defence } {} //TODO icon + unlock_gas_defence { new_unlock_gas_defence }, limit { std::move(new_limit) }, chance { std::move(new_chance) } {} + +bool Invention::parse_scripts(GameManager const& game_manager) { + bool ret = true; + ret &= limit.parse_script(false, game_manager); + ret &= chance.parse_scripts(game_manager); + return ret; +} bool InventionManager::add_invention( std::string_view identifier, ModifierValue&& values, bool news, Invention::unit_set_t&& activated_units, Invention::building_set_t&& activated_buildings, Invention::crime_set_t&& enabled_crimes, - bool unlock_gas_attack, bool unlock_gas_defence + bool unlock_gas_attack, bool unlock_gas_defence, ConditionScript&& limit, ConditionalWeight&& chance ) { if (identifier.empty()) { Logger::error("Invalid invention identifier - empty!"); @@ -28,7 +35,7 @@ bool InventionManager::add_invention( return inventions.add_item({ identifier, std::move(values), news, std::move(activated_units), std::move(activated_buildings), - std::move(enabled_crimes), unlock_gas_attack, unlock_gas_defence + std::move(enabled_crimes), unlock_gas_attack, unlock_gas_defence, std::move(limit), std::move(chance) }); } @@ -50,10 +57,13 @@ bool InventionManager::load_inventions_file( bool unlock_gas_defence = false; bool news = true; //defaults to true! + ConditionScript limit; + ConditionalWeight chance; + bool ret = modifier_manager.expect_modifier_value_and_keys(move_variable_callback(loose_modifiers), "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), - "limit", ONE_EXACTLY, success_callback, - "chance", ONE_EXACTLY, success_callback, + "limit", ONE_EXACTLY, limit.expect_script(), + "chance", ONE_EXACTLY, chance.expect_conditional_weight(ConditionalWeight::BASE), "effect", ZERO_OR_ONE, modifier_manager.expect_modifier_value_and_keys( move_variable_callback(modifiers), "gas_attack", ZERO_OR_ONE, expect_bool(assign_variable_callback(unlock_gas_attack)), @@ -72,10 +82,18 @@ bool InventionManager::load_inventions_file( ret &= add_invention( identifier, std::move(modifiers), news, std::move(activated_units), std::move(activated_buildings), - std::move(enabled_crimes), unlock_gas_attack, unlock_gas_defence + std::move(enabled_crimes), unlock_gas_attack, unlock_gas_defence, std::move(limit), std::move(chance) ); return ret; } )(root); -}
\ No newline at end of file +} + +bool InventionManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (Invention& invention : inventions.get_items()) { + ret &= invention.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/research/Invention.hpp b/src/openvic-simulation/research/Invention.hpp index 5d31155..2b8100d 100644 --- a/src/openvic-simulation/research/Invention.hpp +++ b/src/openvic-simulation/research/Invention.hpp @@ -1,6 +1,7 @@ #pragma once #include "openvic-simulation/misc/Modifier.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" @@ -27,13 +28,17 @@ namespace OpenVic { crime_set_t PROPERTY(enabled_crimes); const bool PROPERTY_CUSTOM_PREFIX(unlock_gas_attack, will); const bool PROPERTY_CUSTOM_PREFIX(unlock_gas_defence, will); + ConditionScript PROPERTY(limit); + ConditionalWeight PROPERTY(chance); Invention( std::string_view new_identifier, ModifierValue&& new_values, bool new_news, unit_set_t&& new_activated_units, building_set_t&& new_activated_buildings, crime_set_t&& new_enabled_crimes, bool new_unlock_gas_attack, - bool new_unlock_gas_defence + bool new_unlock_gas_defence, ConditionScript&& new_limit, ConditionalWeight&& new_chance ); + bool parse_scripts(GameManager const& game_manager); + public: Invention(Invention&&) = default; }; @@ -45,12 +50,14 @@ namespace OpenVic { bool add_invention( std::string_view identifier, ModifierValue&& values, bool news, Invention::unit_set_t&& activated_units, Invention::building_set_t&& activated_buildings, Invention::crime_set_t&& enabled_crimes, bool unlock_gas_attack, - bool unlock_gas_defence + bool unlock_gas_defence, ConditionScript&& limit, ConditionalWeight&& chance ); bool load_inventions_file( ModifierManager const& modifier_manager, UnitManager const& unit_manager, BuildingTypeManager const& building_type_manager, CrimeManager const& crime_manager, ast::NodeCPtr root ); // inventions/*.txt + + bool parse_scripts(GameManager const& game_manager); }; }
\ No newline at end of file diff --git a/src/openvic-simulation/research/Technology.cpp b/src/openvic-simulation/research/Technology.cpp index 7851707..b151f1a 100644 --- a/src/openvic-simulation/research/Technology.cpp +++ b/src/openvic-simulation/research/Technology.cpp @@ -11,10 +11,14 @@ TechnologyArea::TechnologyArea(std::string_view new_identifier, TechnologyFolder Technology::Technology( std::string_view new_identifier, TechnologyArea const& new_area, Date::year_t new_year, fixed_point_t new_cost, bool new_unciv_military, uint8_t new_unit, unit_set_t&& new_activated_units, building_set_t&& new_activated_buildings, - ModifierValue&& new_values + ModifierValue&& new_values, ConditionalWeight&& new_ai_chance ) : Modifier { new_identifier, std::move(new_values), 0 }, area { new_area }, year { new_year }, cost { new_cost }, unciv_military { new_unciv_military }, unit { new_unit }, activated_buildings { std::move(new_activated_units) }, - activated_units { std::move(new_activated_buildings) } {} + activated_units { std::move(new_activated_buildings) }, ai_chance { std::move(new_ai_chance) } {} + +bool Technology::parse_scripts(GameManager const& game_manager) { + return ai_chance.parse_scripts(game_manager); +} TechnologySchool::TechnologySchool(std::string_view new_identifier, ModifierValue&& new_values) : Modifier { new_identifier, std::move(new_values), 0 } {} @@ -45,7 +49,7 @@ bool TechnologyManager::add_technology_area(std::string_view identifier, Technol bool TechnologyManager::add_technology( std::string_view identifier, TechnologyArea const* area, Date::year_t year, fixed_point_t cost, bool unciv_military, uint8_t unit, Technology::unit_set_t&& activated_units, Technology::building_set_t&& activated_buildings, - ModifierValue&& values + ModifierValue&& values, ConditionalWeight&& ai_chance ) { if (identifier.empty()) { Logger::error("Invalid technology identifier - empty!"); @@ -59,7 +63,7 @@ bool TechnologyManager::add_technology( return technologies.add_item({ identifier, *area, year, cost, unciv_military, unit, std::move(activated_units), std::move(activated_buildings), - std::move(values) + std::move(values), std::move(ai_chance) }); } @@ -87,8 +91,9 @@ bool TechnologyManager::load_technology_file_areas(ast::NodeCPtr root) { })(root_value); lock_technology_folders(); lock_technology_areas(); - } else if (root_key == "schools") return true; //ignore - else return false; + } else { + return root_key == "schools"; /* Ignore schools, error otherwise */ + } })(root); } @@ -102,8 +107,9 @@ bool TechnologyManager::load_technology_file_schools(ModifierManager const& modi return ret; })(root_value); lock_technology_schools(); - } else if (root_key == "folders") return true; //ignore - else return false; + } else { + return root_key == "folders"; /* Ignore folders, error otherwise */ + } })(root); } @@ -121,6 +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; 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)), @@ -132,12 +139,12 @@ bool TechnologyManager::load_technologies_file( "activate_building", ZERO_OR_MORE, building_type_manager.expect_building_type_identifier( set_callback_pointer(activated_buildings) ), - "ai_chance", ONE_EXACTLY, success_callback //TODO + "ai_chance", ONE_EXACTLY, ai_chance.expect_conditional_weight(ConditionalWeight::FACTOR) )(tech_value); ret &= add_technology( tech_key, area, year, cost, unciv_military, unit, std::move(activated_units), std::move(activated_buildings), - std::move(modifiers) + std::move(modifiers), std::move(ai_chance) ); return ret; })(root); @@ -162,3 +169,11 @@ bool TechnologyManager::generate_modifiers(ModifierManager& modifier_manager) co return ret; } + +bool TechnologyManager::parse_scripts(GameManager const& game_manager) { + bool ret = true; + for (Technology& technology : technologies.get_items()) { + ret &= technology.parse_scripts(game_manager); + } + return ret; +} diff --git a/src/openvic-simulation/research/Technology.hpp b/src/openvic-simulation/research/Technology.hpp index 3b0b1da..a4287a5 100644 --- a/src/openvic-simulation/research/Technology.hpp +++ b/src/openvic-simulation/research/Technology.hpp @@ -5,6 +5,7 @@ #include "openvic-simulation/economy/BuildingType.hpp" #include "openvic-simulation/military/Unit.hpp" #include "openvic-simulation/misc/Modifier.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" @@ -43,17 +44,18 @@ namespace OpenVic { const fixed_point_t PROPERTY(cost); const bool PROPERTY(unciv_military); const uint8_t PROPERTY(unit); - const unit_set_t PROPERTY(activated_buildings); - const building_set_t PROPERTY(activated_units); - - //TODO: implement rules/modifiers and ai_chance + unit_set_t PROPERTY(activated_buildings); + building_set_t PROPERTY(activated_units); + ConditionalWeight PROPERTY(ai_chance); Technology( std::string_view new_identifier, TechnologyArea const& new_area, Date::year_t new_year, fixed_point_t new_cost, bool new_unciv_military, uint8_t new_unit, unit_set_t&& new_activated_units, - building_set_t&& new_activated_buildings, ModifierValue&& new_values + building_set_t&& new_activated_buildings, ModifierValue&& new_values, ConditionalWeight&& new_ai_chance ); + bool parse_scripts(GameManager const& game_manager); + public: Technology(Technology&&) = default; }; @@ -79,7 +81,8 @@ namespace OpenVic { bool add_technology( std::string_view identifier, TechnologyArea const* area, Date::year_t year, fixed_point_t cost, bool unciv_military, uint8_t unit, Technology::unit_set_t&& activated_units, - Technology::building_set_t&& activated_buildings, ModifierValue&& values); + Technology::building_set_t&& activated_buildings, ModifierValue&& values, ConditionalWeight&& ai_chance + ); bool add_technology_school(std::string_view identifier, ModifierValue&& values); @@ -90,5 +93,7 @@ namespace OpenVic { BuildingTypeManager const& building_type_manager, ast::NodeCPtr root ); // technologies/*.txt bool generate_modifiers(ModifierManager& modifier_manager) const; + + bool parse_scripts(GameManager const& game_manager); }; }
\ No newline at end of file diff --git a/src/openvic-simulation/scripts/ConditionScript.cpp b/src/openvic-simulation/scripts/ConditionScript.cpp new file mode 100644 index 0000000..af2faf7 --- /dev/null +++ b/src/openvic-simulation/scripts/ConditionScript.cpp @@ -0,0 +1,8 @@ +#include "ConditionScript.hpp" + +using namespace OpenVic; + +bool ConditionScript::_parse_script(ast::NodeCPtr root, GameManager const& game_manager) { + // TODO - parse condition script + return true; +} diff --git a/src/openvic-simulation/scripts/ConditionScript.hpp b/src/openvic-simulation/scripts/ConditionScript.hpp new file mode 100644 index 0000000..cb967a2 --- /dev/null +++ b/src/openvic-simulation/scripts/ConditionScript.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "openvic-simulation/scripts/Script.hpp" + +namespace OpenVic { + struct GameManager; + + struct ConditionScript final : Script<GameManager const&> { + protected: + bool _parse_script(ast::NodeCPtr root, GameManager const& game_manager) override; + }; +} diff --git a/src/openvic-simulation/scripts/ConditionalWeight.cpp b/src/openvic-simulation/scripts/ConditionalWeight.cpp new file mode 100644 index 0000000..29dc93b --- /dev/null +++ b/src/openvic-simulation/scripts/ConditionalWeight.cpp @@ -0,0 +1,64 @@ +#include "ConditionalWeight.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +template<typename T> +static NodeCallback auto expect_modifier(std::vector<T>& items) { + return [&items](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); + if (!successful) { + Logger::info("ConditionalWeight modifier missing factor key!"); + return false; + } + ConditionScript condition; + ret &= condition.expect_script()(node); + items.emplace_back(std::make_pair(weight, std::move(condition))); + return ret; + }; +} + +node_callback_t ConditionalWeight::expect_conditional_weight(base_key_t base_key) { + return expect_dictionary_keys( + // TODO - add days and years as options with a shared expected count of ONE_EXACTLY + base_key_to_string(base_key), ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base)), + "modifier", ZERO_OR_MORE, expect_modifier(condition_weight_items), + "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) + )(node); + if (!items.empty()) { + condition_weight_items.emplace_back(std::move(items)); + return ret; + } + Logger::error("ConditionalWeight group must have at least one modifier!"); + return false; + } + ); +} + +struct ConditionalWeight::parse_scripts_visitor_t { + GameManager const& game_manager; + + bool operator()(condition_weight_t& condition_weight) const { + return condition_weight.second.parse_script(false, game_manager); + } + bool operator()(condition_weight_item_t& item) const { + return std::visit(*this, item); + } + template<typename T> + bool operator()(std::vector<T>& items) const { + bool ret = true; + for (T& item : items) { + ret &= (*this)(item); + } + return ret; + } +}; + +bool ConditionalWeight::parse_scripts(GameManager const& game_manager) { + return parse_scripts_visitor_t { game_manager }(condition_weight_items); +} diff --git a/src/openvic-simulation/scripts/ConditionalWeight.hpp b/src/openvic-simulation/scripts/ConditionalWeight.hpp new file mode 100644 index 0000000..a965ae1 --- /dev/null +++ b/src/openvic-simulation/scripts/ConditionalWeight.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include <variant> +#include <vector> + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" + +namespace OpenVic { + struct ConditionalWeight { + using condition_weight_t = std::pair<fixed_point_t, ConditionScript>; + using condition_weight_group_t = std::vector<condition_weight_t>; + using condition_weight_item_t = std::variant<condition_weight_t, condition_weight_group_t>; + + enum class base_key_t : uint8_t { + BASE, FACTOR, MONTHS + }; + using enum base_key_t; + + private: + fixed_point_t PROPERTY(base); + std::vector<condition_weight_item_t> PROPERTY(condition_weight_items); + + struct parse_scripts_visitor_t; + + public: + ConditionalWeight() = default; + ConditionalWeight(ConditionalWeight&&) = default; + + static constexpr std::string_view base_key_to_string(base_key_t base_key) { + switch (base_key) { + case base_key_t::BASE: return "base"; + case base_key_t::FACTOR: return "factor"; + case base_key_t::MONTHS: return "months"; // TODO - add functionality for days or months or years + default: return "INVALID BASE KEY"; + } + } + + NodeTools::node_callback_t expect_conditional_weight(base_key_t base_key); + + bool parse_scripts(GameManager const& game_manager); + }; +} diff --git a/src/openvic-simulation/scripts/EffectScript.cpp b/src/openvic-simulation/scripts/EffectScript.cpp new file mode 100644 index 0000000..f51c053 --- /dev/null +++ b/src/openvic-simulation/scripts/EffectScript.cpp @@ -0,0 +1,8 @@ +#include "EffectScript.hpp" + +using namespace OpenVic; + +bool EffectScript::_parse_script(ast::NodeCPtr root, GameManager& game_manager) { + // TODO - parse effect script + return true; +} diff --git a/src/openvic-simulation/scripts/EffectScript.hpp b/src/openvic-simulation/scripts/EffectScript.hpp new file mode 100644 index 0000000..f5acf78 --- /dev/null +++ b/src/openvic-simulation/scripts/EffectScript.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "openvic-simulation/scripts/Script.hpp" + +namespace OpenVic { + struct GameManager; + + struct EffectScript final : Script<GameManager&> { + protected: + bool _parse_script(ast::NodeCPtr root, GameManager& game_manager) override; + }; +} diff --git a/src/openvic-simulation/scripts/Script.hpp b/src/openvic-simulation/scripts/Script.hpp new file mode 100644 index 0000000..8efc277 --- /dev/null +++ b/src/openvic-simulation/scripts/Script.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include "openvic-simulation/dataloader/NodeTools.hpp" + +namespace OpenVic { + template<typename... _Context> + struct Script { + private: + ast::NodeCPtr _root; + + protected: + virtual bool _parse_script(ast::NodeCPtr root, _Context... context) = 0; + + public: + Script() : _root { nullptr } {} + Script(Script&&) = default; + + constexpr bool has_defines_node() const { + return _root != nullptr; + } + + constexpr NodeTools::NodeCallback auto expect_script() { + return NodeTools::assign_variable_callback(_root); + } + + bool parse_script(bool can_be_null, _Context... context) { + if (_root == nullptr) { + if (!can_be_null) { + Logger::error("Null/missing script node!"); + } + return can_be_null; + } + const bool ret = _parse_script(_root, context...); + _root = nullptr; + return ret; + } + }; +} diff --git a/src/openvic-simulation/types/Colour.hpp b/src/openvic-simulation/types/Colour.hpp index 38a1468..38c7b1e 100644 --- a/src/openvic-simulation/types/Colour.hpp +++ b/src/openvic-simulation/types/Colour.hpp @@ -3,15 +3,9 @@ #include <algorithm> #include <array> #include <cassert> -#include <charconv> #include <climits> -#include <cmath> -#include <compare> #include <cstdint> -#include <iomanip> #include <limits> -#include <span> -#include <sstream> #include <string> #include <string_view> #include <tuple> |