diff options
author | BrickPi <49528459+BrickPi@users.noreply.github.com> | 2023-11-08 21:41:43 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-11-08 21:41:43 +0100 |
commit | 6713ab633b130da6ade5df60cb94a1b22c593d21 (patch) | |
tree | d2d812bd80bc8530889230a9dfc58df8f3c37915 | |
parent | ae2742113ec7283a2a5afa62f8bfd98a865c4208 (diff) | |
parent | 67d7a855992ab25ff2c47b12860c2eb7c9241ae2 (diff) |
Merge pull request #70 from OpenVicProject/diplomacy-basics
Diplomacy History
-rw-r--r-- | src/openvic-simulation/dataloader/Dataloader.cpp | 21 | ||||
-rw-r--r-- | src/openvic-simulation/history/DiplomaticHistory.cpp | 311 | ||||
-rw-r--r-- | src/openvic-simulation/history/DiplomaticHistory.hpp | 131 | ||||
-rw-r--r-- | src/openvic-simulation/history/HistoryManager.hpp | 3 | ||||
-rw-r--r-- | src/openvic-simulation/military/MilitaryManager.hpp | 3 | ||||
-rw-r--r-- | src/openvic-simulation/military/Wargoal.cpp | 309 | ||||
-rw-r--r-- | src/openvic-simulation/military/Wargoal.hpp | 129 | ||||
-rw-r--r-- | src/openvic-simulation/types/EnumBitfield.hpp | 84 |
8 files changed, 991 insertions, 0 deletions
diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 6911de5..aefb6fc 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -639,6 +639,22 @@ 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); + static constexpr std::string_view diplomacy_history_directory = "history/diplomacy"; + ret &= apply_to_files( + lookup_files_in_dir(diplomacy_history_directory, ".txt"), + [this, &game_manager](fs::path const& file) -> bool { + return game_manager.get_history_manager().get_diplomacy_manager().load_diplomacy_history_file(game_manager, parse_defines(file).get_file_node()); + } + ); + 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()); + } + ); + game_manager.get_history_manager().get_diplomacy_manager().lock_diplomatic_history(); + return ret; } @@ -778,6 +794,7 @@ bool Dataloader::load_defines(GameManager& game_manager) const { static const std::string production_types_file = "common/production_types.txt"; static const std::string religion_file = "common/religion.txt"; static const std::string leader_traits_file = "common/traits.txt"; + static const std::string cb_types_file = "common/cb_types.txt"; bool ret = true; @@ -872,6 +889,10 @@ 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())) { + Logger::error("Failed to load wargoals!"); + ret = false; + } if (!game_manager.get_history_manager().load_bookmark_file(parse_defines(lookup_file(bookmark_file)).get_file_node())) { Logger::error("Failed to load bookmarks!"); ret = false; diff --git a/src/openvic-simulation/history/DiplomaticHistory.cpp b/src/openvic-simulation/history/DiplomaticHistory.cpp new file mode 100644 index 0000000..6f9d73e --- /dev/null +++ b/src/openvic-simulation/history/DiplomaticHistory.cpp @@ -0,0 +1,311 @@ +#include "DiplomaticHistory.hpp" + +#include "openvic-simulation/GameManager.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +WarHistory::added_wargoal_t::added_wargoal_t( + Date new_added, + Country const* new_actor, + Country const* new_receiver, + WargoalType const* new_wargoal, + std::optional<Country const*> new_third_party, + std::optional<Province const*> new_target +) : added { new_added }, actor { new_actor }, receiver { new_receiver }, wargoal { new_wargoal }, third_party { new_third_party }, target { new_target } {} + +Country const* WarHistory::added_wargoal_t::get_actor() const { + return actor; +} + +Country const* WarHistory::added_wargoal_t::get_receiver() const { + return receiver; +} + +WargoalType const* WarHistory::added_wargoal_t::get_wargoal_type() const { + return wargoal; +} + +std::optional<Country const*> const& WarHistory::added_wargoal_t::get_third_party() const { + return third_party; +} + +std::optional<Province const*> const& WarHistory::added_wargoal_t::get_target() const { + return target; +} + +Date WarHistory::added_wargoal_t::get_date_added() const { + return added; +} + +WarHistory::war_participant_t::war_participant_t( + Country const* new_country, + Date new_joined, + std::optional<Date> new_exited +) : country { new_country }, joined { new_joined }, exited { new_exited } {} + +Country const* WarHistory::war_participant_t::get_country() const { + return country; +} + +Date WarHistory::war_participant_t::get_date_joined() const { + return joined; +} + +std::optional<Date> WarHistory::war_participant_t::get_date_exited() const { + return exited; +} + +WarHistory::WarHistory( + std::string_view new_war_name, + std::vector<war_participant_t>&& new_attackers, + std::vector<war_participant_t>&& new_defenders, + std::vector<added_wargoal_t>&& new_wargoals +) : war_name { new_war_name }, attackers { std::move(new_attackers) }, defenders { std::move(new_defenders) }, wargoals { std::move(new_wargoals) } {} + +std::string_view WarHistory::get_war_name() const { + return war_name; +} + +std::vector<WarHistory::war_participant_t> const& WarHistory::get_attackers() const { + return attackers; +} + +std::vector<WarHistory::war_participant_t> const& WarHistory::get_defenders() const { + return defenders; +} + +std::vector<WarHistory::added_wargoal_t> const& WarHistory::get_wargoals() const { + return wargoals; +} + +AllianceHistory::AllianceHistory( + Country const* new_first, Country const* new_second, + Date new_start, Date new_end +) : first { new_first }, second { new_second }, start { new_start }, end { new_end } {} + +Country const* AllianceHistory::get_first() const { + return first; +} + +Country const* AllianceHistory::get_second() const { + return second; +} + +SubjectHistory::SubjectHistory( + Country const* new_overlord, Country const* new_subject, + const type_t new_type, const Date new_start, const Date new_end +) : overlord { new_overlord }, subject { new_subject }, type { new_type }, start { new_start }, end { new_end } {} + +Country const* SubjectHistory::get_overlord() const { + return overlord; +} + +Country const* SubjectHistory::get_subject() const { + return subject; +} + +const SubjectHistory::type_t SubjectHistory::get_subject_type() const { + return type; +} + +void DiplomaticHistoryManager::lock_diplomatic_history() { + Logger::info("Locked diplomacy history registry after registering ", alliances.size() + subjects.size() + wars.size(), " items"); + locked = true; +} + +bool DiplomaticHistoryManager::is_locked() const { + return locked; +} + +std::vector<AllianceHistory const*> DiplomaticHistoryManager::get_alliances(Date date) const { + std::vector<AllianceHistory const*> ret; + for (const auto& alliance : alliances) { + if (alliance.start <= date && alliance.end >= date) { + ret.push_back(&alliance); + } + } + return ret; +} + +std::vector<SubjectHistory const*> DiplomaticHistoryManager::get_subjects(Date date) const { + std::vector<SubjectHistory const*> ret; + for (const auto& subject : subjects) { + if (subject.start <= date && subject.end >= date) { + ret.push_back(&subject); + } + } + return ret; +} + +std::vector<WarHistory const*> DiplomaticHistoryManager::get_wars(Date date) const { + std::vector<WarHistory const*> ret; + for (const auto& war : wars) { + Date start; + for (const auto& wargoal : war.wargoals) { + if (wargoal.added < start) start = wargoal.added; + } + if (start >= date) ret.push_back(&war); + } + return ret; +} + +bool DiplomaticHistoryManager::load_diplomacy_history_file(GameManager& game_manager, ast::NodeCPtr root) { + return expect_dictionary_keys( + "alliance", ZERO_OR_MORE, [this, &game_manager](ast::NodeCPtr node) -> bool { + Country const* first; + Country const* second; + Date start, end; + + bool ret = expect_dictionary_keys( + "first", ONE_EXACTLY, expect_identifier_or_string(game_manager.get_country_manager().expect_country_str(assign_variable_callback_pointer(first))), + "second", ONE_EXACTLY, expect_identifier_or_string(game_manager.get_country_manager().expect_country_str(assign_variable_callback_pointer(second))), + "start_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(start))), + "end_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(end))) + )(node); + + alliances.push_back({ first, second, start, end }); + return ret; + }, + "vassal", ZERO_OR_MORE, [this, &game_manager](ast::NodeCPtr node) -> bool { + Country const* overlord; + Country const* subject; + Date start, end; + + bool ret = expect_dictionary_keys( + "first", ONE_EXACTLY, expect_identifier_or_string(game_manager.get_country_manager().expect_country_str(assign_variable_callback_pointer(overlord))), + "second", ONE_EXACTLY, expect_identifier_or_string(game_manager.get_country_manager().expect_country_str(assign_variable_callback_pointer(subject))), + "start_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(start))), + "end_date", ONE_EXACTLY, expect_identifier_or_string(expect_date_str(assign_variable_callback(end))) + )(node); + + subjects.push_back({ overlord, subject, SubjectHistory::type_t::VASSAL, start, end }); + return ret; + }, + "union", ZERO_OR_MORE, [this, &game_manager](ast::NodeCPtr node) -> bool { + Country const* overlord; + Country const* subject; + Date start, end; + + bool ret = expect_dictionary_keys( + "first", ONE_EXACTLY, game_manager.get_country_manager().expect_country_identifier(assign_variable_callback_pointer(overlord)), + "second", ONE_EXACTLY, game_manager.get_country_manager().expect_country_identifier(assign_variable_callback_pointer(subject)), + "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start)), + "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end)) + )(node); + + subjects.push_back({ overlord, subject, SubjectHistory::type_t::UNION, start, end }); + return ret; + }, + "substate", ZERO_OR_MORE, [this, &game_manager](ast::NodeCPtr node) -> bool { + Country const* overlord; + Country const* subject; + Date start, end; + + bool ret = expect_dictionary_keys( + "first", ONE_EXACTLY, game_manager.get_country_manager().expect_country_identifier(assign_variable_callback_pointer(overlord)), + "second", ONE_EXACTLY, game_manager.get_country_manager().expect_country_identifier(assign_variable_callback_pointer(subject)), + "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start)), + "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end)) + )(node); + + subjects.push_back({ overlord, subject, SubjectHistory::type_t::SUBSTATE, start, end }); + return ret; + } + )(root); +} + +bool DiplomaticHistoryManager::load_war_history_file(GameManager& game_manager, ast::NodeCPtr root) { + std::string name = ""; + std::vector<WarHistory::war_participant_t> attackers; + std::vector<WarHistory::war_participant_t> defenders; + std::vector<WarHistory::added_wargoal_t> wargoals; + Date current_date; + + bool ret = expect_dictionary_keys_and_default( + [&game_manager, &attackers, &defenders, &wargoals, ¤t_date, &name](std::string_view key, ast::NodeCPtr node) -> bool { + bool ret = expect_date_str(assign_variable_callback(current_date))(key); + ret &= expect_dictionary_keys( + "add_attacker", ZERO_OR_MORE, game_manager.get_country_manager().expect_country_identifier([&attackers, ¤t_date, &name](Country const& country) -> bool { + for (const auto& attacker : attackers) { + if (attacker.get_country() == &country) { + Logger::error("In history of war ", name, " at date ", current_date.to_string(), ": Attempted to add attacking country ", attacker.get_country()->get_identifier(), " which is already present!"); + return false; + } + } + attackers.push_back({ &country, current_date, {} }); + return true; + }), + "add_defender", ZERO_OR_MORE, game_manager.get_country_manager().expect_country_identifier([&defenders, ¤t_date, &name](Country const& country) -> bool { + for (const auto& defender : defenders) { + if (defender.get_country() == &country) { + Logger::error("In history of war ", name, " at date ", current_date.to_string(), ": Attempted to add defending country ", defender.get_country()->get_identifier(), " which is already present!"); + return false; + } + } + defenders.push_back({ &country, current_date, {} }); + return true; + }), + "rem_attacker", ZERO_OR_MORE, game_manager.get_country_manager().expect_country_identifier([&attackers, ¤t_date, &name](Country const& country) -> bool { + WarHistory::war_participant_t* participant_to_remove = nullptr; + + for (auto& attacker : attackers) { + if (attacker.country == &country) { + participant_to_remove = &attacker; + break; + } + } + + if (participant_to_remove == nullptr) { + Logger::error("In history of war ", name, " at date ", current_date.to_string(), ": Attempted to remove attacking country ", country.get_identifier(), " which was not present!"); + return false; + } + + participant_to_remove->exited.emplace(current_date); + return true; + }), + "rem_defender", ZERO_OR_MORE, game_manager.get_country_manager().expect_country_identifier([&defenders, ¤t_date, &name](Country const& country) -> bool { + WarHistory::war_participant_t* participant_to_remove = nullptr; + + for (auto& defender : defenders) { + if (defender.country == &country) { + participant_to_remove = &defender; + break; + } + } + + if (participant_to_remove == nullptr) { + Logger::error("In history of war ", name, " at date ", current_date.to_string(), ": Attempted to remove attacking country ", country.get_identifier(), " which was not present!"); + return false; + } + + participant_to_remove->exited.emplace(current_date); + return true; + }), + "war_goal", ZERO_OR_MORE, [&game_manager, &wargoals, ¤t_date](ast::NodeCPtr value) -> bool { + Country const* actor; + Country const* receiver; + WargoalType const* type; + std::optional<Country const*> third_party; + std::optional<Province const*> target; + + 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)), + "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); + wargoals.push_back({ current_date, actor, receiver, type, third_party, target }); + return ret; + } + )(node); + return ret; + }, + "name", ZERO_OR_ONE, expect_string(assign_variable_callback_string(name)) + )(root); + + wars.push_back({ name, std::move(attackers), std::move(defenders), std::move(wargoals) }); + return ret; +}
\ No newline at end of file diff --git a/src/openvic-simulation/history/DiplomaticHistory.hpp b/src/openvic-simulation/history/DiplomaticHistory.hpp new file mode 100644 index 0000000..84c2bd7 --- /dev/null +++ b/src/openvic-simulation/history/DiplomaticHistory.hpp @@ -0,0 +1,131 @@ +#pragma once + +#include <bitset> +#include <vector> +#include <map> +#include <optional> + +#include "openvic-simulation/country/Country.hpp" +#include "openvic-simulation/military/Wargoal.hpp" + +namespace OpenVic { + struct DiplomaticHistoryManager; + + struct WarHistory { + friend struct DiplomaticHistoryManager; + + struct added_wargoal_t { + friend struct DiplomaticHistoryManager; + + private: + Date added; + Country const* actor; + Country const* receiver; + WargoalType const* wargoal; + std::optional<Country const*> third_party; + std::optional<Province const*> target; + + added_wargoal_t(Date new_added, Country const* new_actor, Country const* new_receiver, WargoalType const* new_wargoal, std::optional<Country const*> new_third_party, std::optional<Province const*> new_target); + + public: + Date get_date_added() const; + Country const* get_actor() const; + Country const* get_receiver() const; + WargoalType const* get_wargoal_type() const; + std::optional<Country const*> const& get_third_party() const; + std::optional<Province const*> const& get_target() const; + }; + + struct war_participant_t { + friend struct DiplomaticHistoryManager; + + private: + Country const* country; + Date joined; + std::optional<Date> exited; + + war_participant_t(Country const* new_country, Date new_joined, std::optional<Date> new_exited); + + public: + Country const* get_country() const; + Date get_date_joined() const; + std::optional<Date> get_date_exited() const; + }; + + private: + std::string war_name; // edge cases where this is empty/undef for some reason, probably need to just generate war names like usual for that. + std::vector<war_participant_t> attackers; + std::vector<war_participant_t> defenders; + std::vector<added_wargoal_t> wargoals; + + WarHistory(std::string_view new_war_name, std::vector<war_participant_t>&& new_attackers, std::vector<war_participant_t>&& new_defenders, std::vector<added_wargoal_t>&& new_wargoals); + + public: + std::string_view get_war_name() const; + std::vector<war_participant_t> const& get_attackers() const; + std::vector<war_participant_t> const& get_defenders() const; + std::vector<added_wargoal_t> const& get_wargoals() const; + }; + + struct AllianceHistory { + friend struct DiplomaticHistoryManager; + + private: + Country const* first; + Country const* second; + const Date start; + const Date end; + + AllianceHistory(Country const* new_first, Country const* new_second, const Date new_start, const Date new_end); + + public: + Country const* get_first() const; + Country const* get_second() const; + }; + + struct SubjectHistory { + friend struct DiplomaticHistoryManager; + + enum class type_t { + VASSAL, + UNION, + SUBSTATE + }; + + private: + Country const* overlord; + Country const* subject; + const type_t type; + const Date start; + const Date end; + + SubjectHistory(Country const* new_overlord, Country const* new_subject, const type_t new_type, const Date new_start, const Date new_end); + + public: + Country const* get_overlord() const; + Country const* get_subject() const; + const type_t get_subject_type() const; + }; + + struct DiplomaticHistoryManager { + private: + std::vector<AllianceHistory> alliances; + std::vector<SubjectHistory> subjects; + std::vector<WarHistory> wars; + bool locked = false; + + public: + DiplomaticHistoryManager() {} + + void lock_diplomatic_history(); + bool is_locked() const; + + std::vector<AllianceHistory const*> get_alliances(Date date) const; + std::vector<SubjectHistory const*> get_subjects(Date date) const; + /* Returns all wars that begin before date. NOTE: Some wargoals may be added or countries may join after date, should be checked for by functions that use get_wars() */ + std::vector<WarHistory const*> get_wars(Date date) const; + + bool load_diplomacy_history_file(GameManager& game_manager, ast::NodeCPtr root); + bool load_war_history_file(GameManager& game_manager, ast::NodeCPtr root); + }; +} // namespace OpenVic
\ No newline at end of file diff --git a/src/openvic-simulation/history/HistoryManager.hpp b/src/openvic-simulation/history/HistoryManager.hpp index 5782e97..ec6d1c5 100644 --- a/src/openvic-simulation/history/HistoryManager.hpp +++ b/src/openvic-simulation/history/HistoryManager.hpp @@ -3,6 +3,7 @@ #include "openvic-simulation/history/Bookmark.hpp" #include "openvic-simulation/history/CountryHistory.hpp" #include "openvic-simulation/history/ProvinceHistory.hpp" +#include "openvic-simulation/history/DiplomaticHistory.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { @@ -11,11 +12,13 @@ namespace OpenVic { BookmarkManager bookmark_manager; CountryHistoryManager country_manager; ProvinceHistoryManager province_manager; + DiplomaticHistoryManager diplomacy_manager; public: REF_GETTERS(bookmark_manager) REF_GETTERS(country_manager) REF_GETTERS(province_manager) + REF_GETTERS(diplomacy_manager) inline bool load_bookmark_file(ast::NodeCPtr root) { return bookmark_manager.load_bookmark_file(root); diff --git a/src/openvic-simulation/military/MilitaryManager.hpp b/src/openvic-simulation/military/MilitaryManager.hpp index 0e7e1b1..ba13b70 100644 --- a/src/openvic-simulation/military/MilitaryManager.hpp +++ b/src/openvic-simulation/military/MilitaryManager.hpp @@ -3,6 +3,7 @@ #include "openvic-simulation/military/Deployment.hpp" #include "openvic-simulation/military/LeaderTrait.hpp" #include "openvic-simulation/military/Unit.hpp" +#include "openvic-simulation/military/Wargoal.hpp" namespace OpenVic { struct MilitaryManager { @@ -10,10 +11,12 @@ namespace OpenVic { UnitManager unit_manager; LeaderTraitManager leader_trait_manager; DeploymentManager deployment_manager; + WargoalTypeManager wargoal_manager; public: REF_GETTERS(unit_manager) REF_GETTERS(leader_trait_manager) REF_GETTERS(deployment_manager) + REF_GETTERS(wargoal_manager) }; } diff --git a/src/openvic-simulation/military/Wargoal.cpp b/src/openvic-simulation/military/Wargoal.cpp new file mode 100644 index 0000000..8bc4446 --- /dev/null +++ b/src/openvic-simulation/military/Wargoal.cpp @@ -0,0 +1,309 @@ +#include "Wargoal.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" + +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 } {} + +std::string_view WargoalType::get_sprite() const { + return sprite; +} + +std::string_view WargoalType::get_war_name() const { + return war_name; +} + +const Timespan WargoalType::get_available_length() const { + return available_length; +} + +const Timespan WargoalType::get_truce_length() const { + return truce_length; +} + +const bool WargoalType::is_triggered_only() const { + return triggered_only; +} + +const bool WargoalType::is_civil_war() const { + return civil_war; +} + +const bool WargoalType::is_constructing() const { + return constructing; +} + +const bool WargoalType::is_crisis() const { + return crisis; +} + +const bool WargoalType::is_great_war() const { + return great_war; +} + +const bool WargoalType::is_mutual() const { + return mutual; +} + +WargoalType::peace_modifiers_t const& WargoalType::get_modifiers() const { + return modifiers; +} + +const peace_options_t WargoalType::get_peace_options() const { + return peace_options; +} + +WargoalTypeManager::WargoalTypeManager() : wargoal_types { "wargoal types" } {} + +const std::vector<WargoalType const*>& WargoalTypeManager::get_peace_priority_list() const { + return peace_priorities; +} + +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 +) { + 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; + } + + 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 }); +} + +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; + + std::string_view sprite, war_name; + Timespan available, truce; + bool triggered_only = false, civil_war = false, constructing = false, crisis = false, great_war = false, mutual = false; + peace_options_t peace_options; + WargoalType::peace_modifiers_t modifiers; + + bool ret = expect_dictionary_keys_and_default( + [&modifiers, &identifier](std::string_view key, ast::NodeCPtr value) -> bool { + fixed_point_t modifier; + expect_fixed_point(assign_variable_callback(modifier))(value); + + if (key == "badboy_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::BADBOY_FACTOR] += modifier; + return true; + } + if (key == "prestige_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::PRESTIGE_FACTOR] += modifier; + return true; + } + if (key == "peace_cost_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::PEACE_COST_FACTOR] += modifier; + return true; + } + if (key == "penalty_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::PENALTY_FACTOR] += modifier; + return true; + } + if (key == "break_truce_prestige_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::BREAK_TRUCE_PRESTIGE_FACTOR] += modifier; + return true; + } + if (key == "break_truce_infamy_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::BREAK_TRUCE_INFAMY_FACTOR] += modifier; + return true; + } + if (key == "break_truce_militancy_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::BREAK_TRUCE_MILITANCY_FACTOR] += modifier; + return true; + } + if (key == "good_relation_prestige_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::GOOD_RELATION_PRESTIGE_FACTOR] += modifier; + return true; + } + if (key == "good_relation_infamy_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::GOOD_RELATION_INFAMY_FACTOR] += modifier; + return true; + } + if (key == "good_relation_militancy_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::GOOD_RELATION_MILITANCY_FACTOR] += modifier; + return true; + } + if (key == "tws_battle_factor") { + modifiers[WargoalType::PEACE_MODIFIERS::WAR_SCORE_BATTLE_FACTOR] += modifier; + return true; + } + if (key == "construction_speed") { + modifiers[WargoalType::PEACE_MODIFIERS::CONSTRUCTION_SPEED] += modifier; + return true; + } + + 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", ONE_EXACTLY, expect_months(assign_variable_callback(available)), + "truce_months", ONE_EXACTLY, expect_months(assign_variable_callback(truce)), + "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)), + "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 + )(value); + + add_wargoal_type(identifier, sprite, war_name, available, truce, triggered_only, civil_war, constructing, crisis, great_war, mutual, std::move(modifiers), peace_options); + return ret; + } + )(root); + + /* load order in which CBs are prioritised by AI */ + ret &= expect_dictionary_keys_and_default( + key_value_success_callback, + "peace_order", ONE_EXACTLY, expect_list([this](ast::NodeCPtr value) -> bool { + WargoalType const* wargoal; + bool ret = expect_wargoal_type_identifier(assign_variable_callback_pointer(wargoal))(value); + if (ret) { + peace_priorities.push_back(wargoal); + } else { + Logger::warning("Attempted to add invalid wargoal type to AI peace order!"); + } + return true; + }) + )(root); + + lock_wargoal_types(); + return ret; +}
\ No newline at end of file diff --git a/src/openvic-simulation/military/Wargoal.hpp b/src/openvic-simulation/military/Wargoal.hpp new file mode 100644 index 0000000..dbb8d67 --- /dev/null +++ b/src/openvic-simulation/military/Wargoal.hpp @@ -0,0 +1,129 @@ +#pragma once + +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/EnumBitfield.hpp" +#include "openvic-simulation/Modifier.hpp" + +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 + }; + template<> struct enable_bitfield<peace_options_t> : std::true_type{}; + + struct WargoalType : HasIdentifier { + friend struct WargoalTypeManager; + + enum class PEACE_MODIFIERS { + BADBOY_FACTOR, + PRESTIGE_FACTOR, + PEACE_COST_FACTOR, + PENALTY_FACTOR, + BREAK_TRUCE_PRESTIGE_FACTOR, + BREAK_TRUCE_INFAMY_FACTOR, + BREAK_TRUCE_MILITANCY_FACTOR, + GOOD_RELATION_PRESTIGE_FACTOR, + GOOD_RELATION_INFAMY_FACTOR, + GOOD_RELATION_MILITANCY_FACTOR, + WAR_SCORE_BATTLE_FACTOR, + CONSTRUCTION_SPEED + }; + using peace_modifiers_t = std::map<PEACE_MODIFIERS, fixed_point_t>; + + private: + const std::string sprite; + const std::string war_name; + const Timespan available_length; + const Timespan truce_length; + const bool triggered_only; // only able to be added via effects (or within the code) + const bool civil_war; + const bool constructing; // can be added to existing wars or justified + const bool crisis; // able to be added to crises + const bool great_war; // automatically add to great wars + const bool mutual; // attacked and defender share wargoal + const peace_modifiers_t modifiers; + const peace_options_t peace_options; + + // TODO: can_use, prerequisites, on_add, 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 + ); + + public: + WargoalType(WargoalType&&) = default; + + std::string_view get_sprite() const; + std::string_view get_war_name() const; + const Timespan get_available_length() const; + const Timespan get_truce_length() const; + const bool is_triggered_only() const; + const bool is_civil_war() const; + const bool is_constructing() const; + const bool is_crisis() const; + const bool is_great_war() const; + const bool is_mutual() const; + peace_modifiers_t const& get_modifiers() const; + const peace_options_t get_peace_options() const; + }; + + struct WargoalTypeManager { + private: + IdentifierRegistry<WargoalType> wargoal_types; + std::vector<WargoalType const*> peace_priorities; + + public: + WargoalTypeManager(); + + 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 + ); + IDENTIFIER_REGISTRY_ACCESSORS(wargoal_type) + + bool load_wargoal_file(ast::NodeCPtr root); + }; +} // namespace OpenVic
\ No newline at end of file diff --git a/src/openvic-simulation/types/EnumBitfield.hpp b/src/openvic-simulation/types/EnumBitfield.hpp new file mode 100644 index 0000000..2b0a1cf --- /dev/null +++ b/src/openvic-simulation/types/EnumBitfield.hpp @@ -0,0 +1,84 @@ +#pragma once + +#include <type_traits> + +namespace OpenVic { + template<typename E> + struct is_scoped_enum final : std::bool_constant < requires { + requires std::is_enum_v<E>; + requires !std::is_convertible_v<E, std::underlying_type_t<E>>; + } > {}; + + template<class T> + inline constexpr bool is_scoped_enum_v = is_scoped_enum<T>::value; + + template<typename T> + concept IsScopedEnum = is_scoped_enum_v<T>; + + template<IsScopedEnum T> + struct enable_bitfield final : std::false_type { + }; + + template<IsScopedEnum T> + inline constexpr bool enable_bitfield_v = enable_bitfield<T>::value; + + template<typename T> + concept EnumSupportBitfield = enable_bitfield_v<T>; +} + +template<OpenVic::EnumSupportBitfield T> +[[nodiscard]] constexpr inline auto operator|(const T lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + return static_cast<T>(static_cast<underlying_type>(lhs) | static_cast<underlying_type>(rhs)); +} + +template<OpenVic::EnumSupportBitfield T> +[[nodiscard]] constexpr inline auto operator&(const T lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + return static_cast<T>(static_cast<underlying_type>(lhs) & static_cast<underlying_type>(rhs)); +} + +template<OpenVic::EnumSupportBitfield T> +[[nodiscard]] constexpr inline auto operator^(const T lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + return static_cast<T>(static_cast<underlying_type>(lhs) ^ static_cast<underlying_type>(rhs)); +} + +template<OpenVic::EnumSupportBitfield T> +[[nodiscard]] constexpr inline auto operator~(const T lhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + return static_cast<T>(~static_cast<underlying_type>(lhs)); +} + +template<OpenVic::EnumSupportBitfield T> +constexpr inline decltype(auto) operator|=(T& lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + lhs = static_cast<T>(static_cast<underlying_type>(lhs) | static_cast<underlying_type>(rhs)); + return lhs; +} + +template<OpenVic::EnumSupportBitfield T> +constexpr inline decltype(auto) operator&=(T& lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + lhs = static_cast<T>(static_cast<underlying_type>(lhs) & static_cast<underlying_type>(rhs)); + return lhs; +} + +template<OpenVic::EnumSupportBitfield T> +constexpr inline decltype(auto) operator^=(T& lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + lhs = static_cast<T>(static_cast<underlying_type>(lhs) ^ static_cast<underlying_type>(rhs)); + return lhs; +} + +template<OpenVic::EnumSupportBitfield T> +[[nodiscard]] constexpr inline bool operator<<(const T lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + return (lhs & rhs) == rhs; +} + +template<OpenVic::EnumSupportBitfield T> +[[nodiscard]] constexpr inline bool operator>>(const T lhs, const T rhs) noexcept { + using underlying_type = std::underlying_type_t<T>; + return (lhs & rhs) == lhs; +} |