aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
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
commit6713ab633b130da6ade5df60cb94a1b22c593d21 (patch)
treed2d812bd80bc8530889230a9dfc58df8f3c37915
parentae2742113ec7283a2a5afa62f8bfd98a865c4208 (diff)
parent67d7a855992ab25ff2c47b12860c2eb7c9241ae2 (diff)
Merge pull request #70 from OpenVicProject/diplomacy-basics
Diplomacy History
-rw-r--r--src/openvic-simulation/dataloader/Dataloader.cpp21
-rw-r--r--src/openvic-simulation/history/DiplomaticHistory.cpp311
-rw-r--r--src/openvic-simulation/history/DiplomaticHistory.hpp131
-rw-r--r--src/openvic-simulation/history/HistoryManager.hpp3
-rw-r--r--src/openvic-simulation/military/MilitaryManager.hpp3
-rw-r--r--src/openvic-simulation/military/Wargoal.cpp309
-rw-r--r--src/openvic-simulation/military/Wargoal.hpp129
-rw-r--r--src/openvic-simulation/types/EnumBitfield.hpp84
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, &current_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, &current_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, &current_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, &current_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, &current_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, &current_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;
+}