From 3f9911dd1cbfca513c8615d778a8295906d8b7d5 Mon Sep 17 00:00:00 2001 From: Spartan322 Date: Thu, 4 Jan 2024 20:48:46 -0500 Subject: Add skeleton CountryRelationManager Change `DiplomaticAction::Argument` to refer to `CountryInstance*` instead of `Country*` Add `GameManager&` argument to `DiplomaticActionManager::setup_diplomatic_actions` Add some ErrorMacros, likely and unlikely macros and stringization macros (based on Godot's macros) Implement `increase_relations` DiplomaticAction commit function Implement `decrease_relations` DiplomaticAction commit function Fix include StringUtils.hpp errors --- src/openvic-simulation/dataloader/Dataloader.cpp | 2 +- .../diplomacy/CountryRelation.cpp | 53 ++++++++++ .../diplomacy/CountryRelation.hpp | 95 +++++++++++++++++ .../diplomacy/DiplomacyManager.hpp | 2 + .../diplomacy/DiplomaticAction.cpp | 117 ++++++++++++++++----- .../diplomacy/DiplomaticAction.hpp | 24 +++-- src/openvic-simulation/utility/ErrorMacros.hpp | 68 ++++++++++++ src/openvic-simulation/utility/StringUtils.hpp | 2 + src/openvic-simulation/utility/Utility.hpp | 62 +++++++++-- 9 files changed, 375 insertions(+), 50 deletions(-) create mode 100644 src/openvic-simulation/diplomacy/CountryRelation.cpp create mode 100644 src/openvic-simulation/diplomacy/CountryRelation.hpp create mode 100644 src/openvic-simulation/utility/ErrorMacros.hpp (limited to 'src/openvic-simulation') diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 0078ed2..a3d00cc 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -946,7 +946,7 @@ bool Dataloader::load_defines(GameManager& game_manager) { Logger::error("Failed to load on actions!"); ret = false; } - if (!game_manager.get_diplomacy_manager().get_diplomatic_action_manager().setup_diplomatic_actions()) { + if (!game_manager.get_diplomacy_manager().get_diplomatic_action_manager().setup_diplomatic_actions(game_manager)) { Logger::error("Failed to load diplomatic actions!"); ret = false; } diff --git a/src/openvic-simulation/diplomacy/CountryRelation.cpp b/src/openvic-simulation/diplomacy/CountryRelation.cpp new file mode 100644 index 0000000..d07739c --- /dev/null +++ b/src/openvic-simulation/diplomacy/CountryRelation.cpp @@ -0,0 +1,53 @@ +#include "CountryRelation.hpp" + +#include + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/utility/ErrorMacros.hpp" + +using namespace OpenVic; + +CountryRelationInstanceProxy::CountryRelationInstanceProxy(std::string_view id) : country_id { id } {} + +CountryRelationInstanceProxy::CountryRelationInstanceProxy(CountryInstance const* country) + : country_id { country->get_base_country()->get_identifier() } {} + +CountryRelationInstanceProxy::operator std::string_view() const { + return country_id; +} + +CountryRelationManager::CountryRelationManager(/* TODO: Country Instance Manager Reference */) {} + +bool CountryRelationManager::add_country(CountryRelationInstanceProxy country) { + // TODO: iterate through Country Instances adding country to every previously existing country_relations + return true; +} + +bool CountryRelationManager::remove_country(CountryRelationInstanceProxy country) { + // TODO: iterate through country_relations removing every pair that references the country's id + return true; +} + +country_relation_value_t CountryRelationManager::get_country_relation( + CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient +) const { + auto it = country_relations.find({ country.country_id, recepient.country_id }); + OV_ERR_FAIL_COND_V(it == country_relations.end(), 0); + return it->second; +} + +country_relation_value_t* +CountryRelationManager::get_country_relation_ptr(CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient) { + auto it = country_relations.find({ country.country_id, recepient.country_id }); + OV_ERR_FAIL_COND_V(it == country_relations.end(), nullptr); + return &it.value(); +} + +bool CountryRelationManager::set_country_relation( + CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient, country_relation_value_t value +) { + auto it = country_relations.find({ country.country_id, recepient.country_id }); + OV_ERR_FAIL_COND_V(it == country_relations.end(), false); + it.value() = value; + return true; +} diff --git a/src/openvic-simulation/diplomacy/CountryRelation.hpp b/src/openvic-simulation/diplomacy/CountryRelation.hpp new file mode 100644 index 0000000..76b3897 --- /dev/null +++ b/src/openvic-simulation/diplomacy/CountryRelation.hpp @@ -0,0 +1,95 @@ +#pragma once + +#include +#include + +#include "openvic-simulation/types/OrderedContainers.hpp" +#include "openvic-simulation/utility/Utility.hpp" + +namespace OpenVic { + struct CountryInstance; + + struct CountryRelationInstanceProxy { + std::string_view country_id; + + CountryRelationInstanceProxy(std::string_view id); + CountryRelationInstanceProxy(CountryInstance const* country); + + operator std::string_view() const; + }; + + struct CountryRelationPair : std::pair { + using base_type = std::pair; + using base_type::base_type; + + inline constexpr auto operator<=>(CountryRelationPair const& rhs) const { + using ordering = decltype(OpenVic::utility::three_way(std::tie(first, second), std::tie(rhs.first, rhs.second))); + + auto tied = std::tie(first, second); + if (tied == std::tie(rhs.first, rhs.second)) { + return ordering::equivalent; + } else if (tied == std::tie(rhs.second, rhs.first)) { + return ordering::equivalent; + } + + auto& [lhs_left, lhs_right] = *this; + auto& [rhs_left, rhs_right] = rhs; + + auto& lhs_comp_first = lhs_left > lhs_right ? lhs_left : lhs_right; + auto& lhs_comp_second = lhs_left < lhs_right ? lhs_left : lhs_right; + + auto& rhs_comp_first = rhs_left > rhs_right ? rhs_left : rhs_right; + auto& rhs_comp_second = rhs_left < rhs_right ? rhs_left : rhs_right; + + return OpenVic::utility::three_way( + std::tie(lhs_comp_first, lhs_comp_second), std::tie(rhs_comp_first, rhs_comp_second) + ); + } + + inline constexpr bool operator==(CountryRelationPair const& rhs) const { + auto tied = std::tie(first, second); + return tied == std::tie(rhs.first, rhs.second) || tied == std::tie(rhs.second, rhs.first); + } + }; +} + +namespace std { + template<> + struct hash { + size_t operator()(OpenVic::CountryRelationPair const& pair) const { + std::size_t seed1(0); + OpenVic::utility::hash_combine(seed1, pair.first); + OpenVic::utility::hash_combine(seed1, pair.second); + + std::size_t seed2(0); + OpenVic::utility::hash_combine(seed2, pair.second); + OpenVic::utility::hash_combine(seed2, pair.first); + + return std::min(seed1, seed2); + } + }; +} + +namespace OpenVic { + using country_relation_value_t = int16_t; + + struct CountryRelationManager { + private: + // TODO: reference of manager responsible for storing CountryInstances + ordered_map country_relations; + + public: + CountryRelationManager(/* TODO: Country Instance Manager Reference */); + + bool add_country(CountryRelationInstanceProxy country); + bool remove_country(CountryRelationInstanceProxy country); + + country_relation_value_t + get_country_relation(CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient) const; + country_relation_value_t* + get_country_relation_ptr(CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient); + bool set_country_relation( + CountryRelationInstanceProxy country, CountryRelationInstanceProxy recepient, country_relation_value_t value + ); + }; +} diff --git a/src/openvic-simulation/diplomacy/DiplomacyManager.hpp b/src/openvic-simulation/diplomacy/DiplomacyManager.hpp index c71bdbf..7aca409 100644 --- a/src/openvic-simulation/diplomacy/DiplomacyManager.hpp +++ b/src/openvic-simulation/diplomacy/DiplomacyManager.hpp @@ -1,9 +1,11 @@ #pragma once +#include "openvic-simulation/diplomacy/CountryRelation.hpp" #include "openvic-simulation/diplomacy/DiplomaticAction.hpp" namespace OpenVic { class DiplomacyManager { DiplomaticActionManager PROPERTY_REF(diplomatic_action_manager); + CountryRelationManager PROPERTY_REF(country_relation_manager); }; } diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp index d10eda3..a386d9f 100644 --- a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp +++ b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp @@ -1,19 +1,19 @@ #include "DiplomaticAction.hpp" #include -#include +#include "openvic-simulation/GameManager.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/utility/Logger.hpp" using namespace OpenVic; DiplomaticActionType::DiplomaticActionType(DiplomaticActionType::Initializer&& initializer) - : commit_action_caller { std::move(initializer.commit) }, - allowed_to_commit { std::move(initializer.allowed) }, get_acceptance { std::move(initializer.get_acceptance) } {} + : commit_action_caller { std::move(initializer.commit) }, allowed_to_commit { std::move(initializer.allowed) }, + get_acceptance { std::move(initializer.get_acceptance) } {} CancelableDiplomaticActionType::CancelableDiplomaticActionType(CancelableDiplomaticActionType::Initializer&& initializer) - : allowed_to_cancel{std::move(initializer.allowed_cancel)}, DiplomaticActionType(std::move(initializer)) {} + : allowed_to_cancel { std::move(initializer.allowed_cancel) }, DiplomaticActionType(std::move(initializer)) {} DiplomaticActionManager::DiplomaticActionManager() {} @@ -38,7 +38,7 @@ bool DiplomaticActionManager::add_cancelable_diplomatic_action( } DiplomaticActionTickCache DiplomaticActionManager::create_diplomatic_action_tick( - std::string_view identifier, Country& sender, Country& reciever, std::any context_data + std::string_view identifier, CountryInstance* sender, CountryInstance* reciever, std::any context_data ) { auto type = diplomatic_action_types.get_item_by_identifier(identifier); @@ -52,92 +52,151 @@ DiplomaticActionTickCache DiplomaticActionManager::create_diplomatic_action_tick return result; } -bool DiplomaticActionManager::setup_diplomatic_actions() { +bool DiplomaticActionManager::setup_diplomatic_actions(GameManager& manager) { using Argument = DiplomaticActionType::Argument; bool result = true; result &= add_diplomatic_action( "form_alliance", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "call_ally", { .commit = [](Argument& arg) {}, - .allowed = [](const Argument& arg) { return false; }, - .get_acceptance = [](const Argument& arg) { return 1; } + .allowed = + [](const Argument& arg) { + return false; + }, + .get_acceptance = + [](const Argument& arg) { + return 1; + }, } ); result &= add_cancelable_diplomatic_action( "request_military_access", { .commit = [](Argument& arg) {}, - .allowed_cancel = [](const Argument& arg) { return true; } + .allowed_cancel = + [](const Argument& arg) { + return true; + }, } ); - result &= add_diplomatic_action( - "give_military_access", - { [](Argument& arg) {} } - ); + result &= add_diplomatic_action("give_military_access", { [](Argument& arg) {} }); result &= add_diplomatic_action( "increase_relations", { - .commit = [](Argument& arg) {}, - .allowed = [](const Argument& arg) { return false; }, + .commit = + [&manager](Argument& arg) { + auto relation = manager.get_diplomacy_manager().get_country_relation_manager().get_country_relation_ptr( + arg.sender, arg.reciever + ); + if (!relation) { + return; + } + *relation += 25; + }, + .allowed = + [](const Argument& arg) { + return false; + }, } ); result &= add_diplomatic_action( "decrease_relations", - { [](Argument& arg) {} } + { + .commit = + [&manager](Argument& arg) { + auto relation = manager.get_diplomacy_manager().get_country_relation_manager().get_country_relation_ptr( + arg.sender, arg.reciever + ); + if (!relation) { + return; + } + *relation -= 25; + }, + .allowed = + [](const Argument& arg) { + return false; + }, + } ); result &= add_diplomatic_action( "war_subsidies", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "declare_war", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "offer_peace", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "command_units", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "discredit", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "expel_advisors", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "increase_opinion", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "decrease_opinion", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "add_to_sphere", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "remove_from_sphere", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "justify_war", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); result &= add_diplomatic_action( "give_vision", - { [](Argument& arg) {} } + { + [](Argument& arg) {}, + } ); diplomatic_action_types.lock(); diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp index 7254510..3d0cbca 100644 --- a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp +++ b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp @@ -8,17 +8,20 @@ #include #include "openvic-simulation/country/Country.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/types/FunctionRef.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { + struct GameManager; + struct DiplomaticActionType { friend struct DiplomaticActionManager; friend struct CancelableDiplomaticActionType; struct Argument { - Country& sender; - Country& reciever; + CountryInstance* sender; + CountryInstance* reciever; std::any context_data; }; @@ -69,7 +72,7 @@ namespace OpenVic { allowed_to_cancel_func allowed_cancel = allowed_to_cancel_default; operator DiplomaticActionType::Initializer() { - return {commit, allowed, get_acceptance}; + return { commit, allowed, get_acceptance }; } }; @@ -88,7 +91,7 @@ namespace OpenVic { constexpr DiplomaticActionTypeStorage(std::string_view identifier, T&& t) : HasIdentifier(identifier), base_type(t) {} template - constexpr decltype(auto) visit(Visitor&& vis){ + constexpr decltype(auto) visit(Visitor&& vis) { return std::visit(std::forward(vis), *this); } @@ -100,7 +103,7 @@ namespace OpenVic { constexpr bool is_cancelable() const { return visit([](auto&& arg) -> bool { using T = std::decay_t; - if constexpr(std::same_as) { + if constexpr (std::same_as) { return true; } else { return false; @@ -124,11 +127,14 @@ namespace OpenVic { DiplomaticActionManager(); bool add_diplomatic_action(std::string_view identifier, DiplomaticActionType::Initializer&& initializer); - bool add_cancelable_diplomatic_action(std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer); + bool add_cancelable_diplomatic_action( + std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer + ); - DiplomaticActionTickCache - create_diplomatic_action_tick(std::string_view identifier, Country& sender, Country& reciever, std::any context_data); + DiplomaticActionTickCache create_diplomatic_action_tick( + std::string_view identifier, CountryInstance* sender, CountryInstance* reciever, std::any context_data + ); - bool setup_diplomatic_actions(); + bool setup_diplomatic_actions(GameManager& manager); }; } diff --git a/src/openvic-simulation/utility/ErrorMacros.hpp b/src/openvic-simulation/utility/ErrorMacros.hpp new file mode 100644 index 0000000..47b73d5 --- /dev/null +++ b/src/openvic-simulation/utility/ErrorMacros.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "openvic-simulation/utility/Logger.hpp" // IWYU pragma: keep +#include "openvic-simulation/utility/Utility.hpp" + +/** + * Try using `ERR_FAIL_COND_MSG`. + * Only use this macro if there is no sensible error message. + * If checking for null use ERR_FAIL_NULL_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_MSG instead. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, the current function returns. + */ +#define OV_ERR_FAIL_COND(m_cond) \ + if (OV_unlikely(m_cond)) { \ + ::OpenVic::Logger::error("Condition \"" _OV_STR(m_cond) "\" is true."); \ + return; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg` and the current function returns. + * + * If checking for null use ERR_FAIL_NULL_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_MSG instead. + */ +#define OV_ERR_FAIL_COND_MSG(m_cond, m_msg) \ + if (OV_unlikely(m_cond)) { \ + ::OpenVic::Logger::error("Condition \"" _OV_STR(m_cond) "\" is true.", m_msg); \ + return; \ + } else \ + ((void)0) + +/** + * Try using `ERR_FAIL_COND_V_MSG`. + * Only use this macro if there is no sensible error message. + * If checking for null use ERR_FAIL_NULL_V_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. + * + * Ensures `m_cond` is false. + * If `m_cond` is true, the current function returns `m_retval`. + */ +#define OV_ERR_FAIL_COND_V(m_cond, m_retval) \ + if (OV_unlikely(m_cond)) { \ + ::OpenVic::Logger::error( \ + "Condition \"" _OV_STR(m_cond) "\" is true. Returning: " _OV_STR(m_retval) \ + ); \ + return m_retval; \ + } else \ + ((void)0) + +/** + * Ensures `m_cond` is false. + * If `m_cond` is true, prints `m_msg` and the current function returns `m_retval`. + * + * If checking for null use ERR_FAIL_NULL_V_MSG instead. + * If checking index bounds use ERR_FAIL_INDEX_V_MSG instead. + */ +#define OV_ERR_FAIL_COND_V_MSG(m_cond, m_retval, m_msg) \ + if (OV_unlikely(m_cond)) { \ + ::OpenVic::Logger::error( \ + "Condition \"" _OV_STR(m_cond) "\" is true. Returning: " _OV_STR(m_retval), m_msg \ + ); \ + return m_retval; \ + } else \ + ((void)0) diff --git a/src/openvic-simulation/utility/StringUtils.hpp b/src/openvic-simulation/utility/StringUtils.hpp index c5a0b71..1be818a 100644 --- a/src/openvic-simulation/utility/StringUtils.hpp +++ b/src/openvic-simulation/utility/StringUtils.hpp @@ -1,9 +1,11 @@ #pragma once +#include #include #include #include #include +#include #include namespace OpenVic::StringUtils { diff --git a/src/openvic-simulation/utility/Utility.hpp b/src/openvic-simulation/utility/Utility.hpp index 0387e7f..dbbcf8f 100644 --- a/src/openvic-simulation/utility/Utility.hpp +++ b/src/openvic-simulation/utility/Utility.hpp @@ -4,6 +4,21 @@ #include #include +#if defined(__GNUC__) +#define OV_likely(x) __builtin_expect(!!(x), 1) +#define OV_unlikely(x) __builtin_expect(!!(x), 0) +#else +#define OV_likely(x) x +#define OV_unlikely(x) x +#endif + +// Turn argument to string constant: +// https://gcc.gnu.org/onlinedocs/cpp/Stringizing.html#Stringizing +#ifndef _OV_STR +#define _OV_STR(m_x) #m_x +#define _OV_MKSTR(m_x) _OV_STR(m_x) +#endif + namespace OpenVic::utility { [[noreturn]] inline void unreachable() { // Uses compiler specific extensions if possible. @@ -25,29 +40,35 @@ namespace OpenVic::utility { template constexpr inline void hash_combine_index(std::size_t& s, const T& v) { std::hash h; - if constexpr(Shift == 0) { + if constexpr (Shift == 0) { s = h(v); } else { s ^= h(v) << Shift; } } - template + template constexpr void perfect_hash(std::size_t& s, T&& v, Args&&... args) { - static_assert(sizeof(T) + (sizeof(Args) + ...) <= sizeof(std::size_t), "Perfect hashes must be able to fit into size_t"); + static_assert( + sizeof(T) + (sizeof(Args) + ...) <= sizeof(std::size_t), "Perfect hashes must be able to fit into size_t" + ); std::hash h; - if constexpr(sizeof...(args) == 0) { + if constexpr (sizeof...(args) == 0) { s = h(v); } else { const std::tuple arg_tuple { args... }; s = h(v) << (sizeof(T) * CHAR_BIT); - ([&]{ - // If args is not last pointer of args - if (static_cast(&(std::get(arg_tuple))) != static_cast(&args)) { - s <<= sizeof(Args) * CHAR_BIT; - } - s |= std::hash{}(args); - }(), ...); + ( + [&] { + // If args is not last pointer of args + if (static_cast(&(std::get(arg_tuple))) != + static_cast(&args)) { + s <<= sizeof(Args) * CHAR_BIT; + } + s |= std::hash {}(args); + }(), + ... + ); } } @@ -59,4 +80,23 @@ namespace OpenVic::utility { template class Z> inline constexpr bool is_specialization_of_v = is_specialization_of::value; + + inline constexpr auto three_way(auto&& left, auto&& right) { + // This is Apple's fault again + #if __cpp_lib_three_way_comparison >= 201907L + if constexpr (std::three_way_comparable_with, std::decay_t>) { + return left <=> right; + } else + #endif + { + if (left < right) { + return std::weak_ordering::less; + } else if (left > right) { + return std::weak_ordering::greater; + } else { + return std::weak_ordering::equivalent; + } + } + }; + } -- cgit v1.2.3-56-ga3b1