diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index 39818bf4..730ea141 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -2,6 +2,7 @@ #include "openvic-simulation/DefinitionManager.hpp" #include "openvic-simulation/console/ConsoleInstance.hpp" +#include "openvic-simulation/misc/GameAction.hpp" #include "openvic-simulation/utility/Logger.hpp" using namespace OpenVic; @@ -96,7 +97,7 @@ InstanceManager::InstanceManager( }, simulation_clock { [this]() -> void { - queue_game_action(game_action_type_t::GAME_ACTION_TICK, {}); + queue_game_action(); }, [this]() -> void { execute_game_actions(); @@ -335,17 +336,12 @@ void InstanceManager::update_modifier_sums() { ); } -bool InstanceManager::queue_game_action(game_action_type_t type, game_action_argument_t&& argument) { +bool InstanceManager::queue_game_action(game_action_t&& game_action) { if (currently_executing_game_actions) { spdlog::error_s("Attempted to queue a game action while already executing game actions!"); return false; } - if (type >= game_action_type_t::MAX_GAME_ACTION) { - spdlog::critical_s("Invalid game action type {}", static_cast(type)); - return false; - } - - game_action_queue.emplace_back(type, std::move(argument)); + game_action_queue.emplace_back(std::move(game_action)); return true; } diff --git a/src/openvic-simulation/InstanceManager.hpp b/src/openvic-simulation/InstanceManager.hpp index 8a19a92e..ad1b432f 100644 --- a/src/openvic-simulation/InstanceManager.hpp +++ b/src/openvic-simulation/InstanceManager.hpp @@ -1,5 +1,9 @@ #pragma once +#include + +#include + #include "openvic-simulation/console/ConsoleInstance.hpp" #include "openvic-simulation/country/CountryInstanceManager.hpp" #include "openvic-simulation/country/CountryInstanceDeps.hpp" @@ -21,8 +25,6 @@ #include "openvic-simulation/utility/ThreadPool.hpp" #include "openvic-simulation/utility/Containers.hpp" -#include - namespace OpenVic { struct DefinitionManager; @@ -97,6 +99,15 @@ namespace OpenVic { bool set_today_and_update(Date new_today); - bool queue_game_action(game_action_type_t type, game_action_argument_t&& argument); + template + bool queue_game_action(Args&&... args) { + return queue_game_action( + game_action_t( + std::in_place_type, + std::forward(args)... + ) + ); + } + bool queue_game_action(game_action_t&& game_action); }; } diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index 5f9a0771..bbe79873 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -172,12 +172,12 @@ bool ProvinceInstance::remove_core(CountryInstance& core_to_remove, bool warn) { return true; } -bool ProvinceInstance::expand_building(size_t building_index) { - BuildingInstance* building = buildings.get_item_by_index(building_index); +bool ProvinceInstance::expand_building(building_type_index_t building_type_index) { + BuildingInstance* building = buildings.get_item_by_index(type_safe::get(building_type_index)); if (building == nullptr) { spdlog::error_s( "Trying to expand non-existent building index {} in province {}", - building_index, *this + building_type_index, *this ); return false; } diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp index 943e3485..32653a67 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -158,7 +158,7 @@ namespace OpenVic { return owner == nullptr; } - bool expand_building(size_t building_index); + bool expand_building(building_type_index_t building_type_index); bool add_pop(Pop&& pop); bool add_pop_vec( diff --git a/src/openvic-simulation/misc/GameAction.cpp b/src/openvic-simulation/misc/GameAction.cpp index 2ae12c0f..534dd1cf 100644 --- a/src/openvic-simulation/misc/GameAction.cpp +++ b/src/openvic-simulation/misc/GameAction.cpp @@ -1,401 +1,202 @@ #include "GameAction.hpp" -#include -#include - #include "openvic-simulation/DefinitionManager.hpp" #include "openvic-simulation/InstanceManager.hpp" -#include "utility/Containers.hpp" #include "openvic-simulation/utility/Typedefs.hpp" using namespace OpenVic; -memory::string OpenVic::game_action_argument_to_string(game_action_argument_t const& argument) { - return memory::fmt::format("{}", argument); -} - -GameActionManager::GameActionManager(InstanceManager& new_instance_manager) - : instance_manager { new_instance_manager } {} - -std::array< - GameActionManager::game_action_callback_t, static_cast(game_action_type_t::MAX_GAME_ACTION) -> GameActionManager::GAME_ACTION_CALLBACKS { - // GAME_ACTION_NONE - &GameActionManager::game_action_callback_none, - - // Core - // GAME_ACTION_TICK - &GameActionManager::game_action_callback_tick, - // GAME_ACTION_SET_PAUSE - &GameActionManager::game_action_callback_set_pause, - // GAME_ACTION_SET_SPEED - &GameActionManager::game_action_callback_set_speed, - // GAME_ACTION_SET_AI - &GameActionManager::game_action_callback_set_ai, - - // Production - // GAME_ACTION_EXPAND_PROVINCE_BUILDING - &GameActionManager::game_action_callback_expand_province_building, - - // Budget - // GAME_ACTION_SET_STRATA_TAX - &GameActionManager::game_action_callback_set_strata_tax, - // GAME_ACTION_SET_ARMY_SPENDING - &GameActionManager::game_action_callback_set_army_spending, - // GAME_ACTION_SET_NAVY_SPENDING - &GameActionManager::game_action_callback_set_navy_spending, - // GAME_ACTION_SET_CONSTRUCTION_SPENDING - &GameActionManager::game_action_callback_set_construction_spending, - // GAME_ACTION_SET_EDUCATION_SPENDING - &GameActionManager::game_action_callback_set_education_spending, - // GAME_ACTION_SET_ADMINISTRATION_SPENDING - &GameActionManager::game_action_callback_set_administration_spending, - // GAME_ACTION_SET_SOCIAL_SPENDING - &GameActionManager::game_action_callback_set_social_spending, - // GAME_ACTION_SET_MILITARY_SPENDING - &GameActionManager::game_action_callback_set_military_spending, - // GAME_ACTION_SET_TARIFF_RATE - &GameActionManager::game_action_callback_set_tariff_rate, - - // Technology - // GAME_ACTION_START_RESEARCH - &GameActionManager::game_action_callback_start_research, - - // Politics - - // Population - - // Trade - // GAME_ACTION_SET_GOOD_AUTOMATED - &GameActionManager::game_action_callback_set_good_automated, - // GAME_ACTION_SET_GOOD_TRADE_ORDER - &GameActionManager::game_action_callback_set_good_trade_order, - - // Diplomacy - - // Military - // GAME_ACTION_CREATE_LEADER - &GameActionManager::game_action_callback_create_leader, - // GAME_ACTION_SET_USE_LEADER - &GameActionManager::game_action_callback_set_use_leader, - // GAME_ACTION_SET_AUTO_CREATE_LEADERS - &GameActionManager::game_action_callback_set_auto_create_leaders, - // GAME_ACTION_SET_AUTO_ASSIGN_LEADERS - &GameActionManager::game_action_callback_set_auto_assign_leaders, - // GAME_ACTION_SET_MOBILISE - &GameActionManager::game_action_callback_set_mobilise -}; - -bool GameActionManager::execute_game_action(game_action_t const& game_action) const { - return (this->*GAME_ACTION_CALLBACKS[static_cast(game_action.first)])(game_action.second); -} - -bool GameActionManager::game_action_callback_none(game_action_argument_t const& argument) const { - if (OV_unlikely(!std::holds_alternative(argument))) { - spdlog::warn_s("GAME_ACTION_NONE called with invalid argument: {}", game_action_argument_to_string(argument)); - } - +bool GameActionManager::VariantVisitor::operator() (none_argument_t const& argument) const { return false; } // Core -bool GameActionManager::game_action_callback_tick(game_action_argument_t const& argument) const { - if (OV_unlikely(!std::holds_alternative(argument))) { - spdlog::warn_s("GAME_ACTION_TICK called with invalid argument: {}", game_action_argument_to_string(argument)); - } - +bool GameActionManager::VariantVisitor::operator() (tick_argument_t const& argument) const { instance_manager.tick(); return true; } -bool GameActionManager::game_action_callback_set_pause(game_action_argument_t const& argument) const { - bool const* pause = std::get_if(&argument); - if (OV_unlikely(pause == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_PAUSE called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - +bool GameActionManager::VariantVisitor::operator() (set_pause_argument_t const& new_is_paused) const { const bool old_pause = instance_manager.get_simulation_clock().is_paused(); - - instance_manager.get_simulation_clock().set_paused(*pause); - + instance_manager.get_simulation_clock().set_paused(type_safe::get(new_is_paused)); return old_pause != instance_manager.get_simulation_clock().is_paused(); } -bool GameActionManager::game_action_callback_set_speed(game_action_argument_t const& argument) const { - int64_t const* speed = std::get_if(&argument); - if (OV_unlikely(speed == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_SPEED called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - +bool GameActionManager::VariantVisitor::operator() (set_speed_argument_t const& new_speed) const { const SimulationClock::speed_t old_speed = instance_manager.get_simulation_clock().get_simulation_speed(); - - instance_manager.get_simulation_clock().set_simulation_speed(*speed); - + instance_manager.get_simulation_clock().set_simulation_speed(type_safe::get(new_speed)); return old_speed != instance_manager.get_simulation_clock().get_simulation_speed(); } -bool GameActionManager::game_action_callback_set_ai(game_action_argument_t const& argument) const { - std::pair const* country_ai = std::get_if>(&argument); - if (OV_unlikely(country_ai == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_AI called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_ai->first); +bool GameActionManager::VariantVisitor::operator() (set_ai_argument_t const& argument) const { + const auto [country_index, new_is_ai] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_AI called with invalid country index: {}", country_ai->first); + spdlog::error_s("GAME_ACTION_SET_AI called with invalid country index: {}", country_index); return false; } const bool old_ai = country->is_ai(); - - country->set_ai(country_ai->second); - + country->set_ai(new_is_ai); return old_ai != country->is_ai(); } // Production -bool GameActionManager::game_action_callback_expand_province_building(game_action_argument_t const& argument) const { - std::pair const* province_building = std::get_if>(&argument); - if (OV_unlikely(province_building == nullptr)) { - spdlog::error_s( - "GAME_ACTION_EXPAND_PROVINCE_BUILDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - ProvinceInstance* province = instance_manager.get_map_instance().get_province_instance_from_number(province_building->first); +bool GameActionManager::VariantVisitor::operator() (expand_province_building_argument_t const& argument) const { + const auto [province_index, building_type_index] = argument; + ProvinceInstance* province = instance_manager.get_map_instance().get_province_instance_by_index(province_index); if (OV_unlikely(province == nullptr)) { - spdlog::error_s("GAME_ACTION_EXPAND_PROVINCE_BUILDING called with invalid province index: {}", province_building->first); + spdlog::error_s("GAME_ACTION_EXPAND_PROVINCE_BUILDING called with invalid province index: {}", province_index); return false; } - return province->expand_building(province_building->second); + return province->expand_building(building_type_index); } // Budget -bool GameActionManager::game_action_callback_set_strata_tax(game_action_argument_t const& argument) const { - std::tuple const* country_strata_value = - std::get_if>(&argument); - if (OV_unlikely(country_strata_value == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_STRATA_TAX called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(std::get<0>(*country_strata_value)); +bool GameActionManager::VariantVisitor::operator() (set_strata_tax_argument_t const& argument) const { + const auto [country_index, strata_index, tax_rate] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_STRATA_TAX called with invalid country index: {}", std::get<0>(*country_strata_value)); + spdlog::error_s("GAME_ACTION_SET_STRATA_TAX called with invalid country index: {}", country_index); return false; } - Strata const* strata = - instance_manager.definition_manager.get_pop_manager().get_strata_by_index(std::get<1>(*country_strata_value)); + Strata const* strata = instance_manager.definition_manager.get_pop_manager().get_strata_by_index(type_safe::get(strata_index)); if (OV_unlikely(strata == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_STRATA_TAX called with invalid strata index: {}", std::get<1>(*country_strata_value)); + spdlog::error_s("GAME_ACTION_SET_STRATA_TAX called with invalid strata index: {}", strata_index); return false; } - country->set_strata_tax_rate_slider_value(*strata, std::get<2>(*country_strata_value)); + country->set_strata_tax_rate_slider_value(*strata, tax_rate); return false; } -bool GameActionManager::game_action_callback_set_army_spending(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_ARMY_SPENDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_army_spending_argument_t const& argument) const { + const auto [country_index, spending] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_ARMY_SPENDING called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_ARMY_SPENDING called with invalid country index: {}", country_index); return false; } - country->set_army_spending_slider_value(country_value->second); + country->set_army_spending_slider_value(spending); return false; } -bool GameActionManager::game_action_callback_set_navy_spending(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_NAVY_SPENDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_navy_spending_argument_t const& argument) const { + const auto [country_index, spending] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_NAVY_SPENDING called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_NAVY_SPENDING called with invalid country index: {}", country_index); return false; } - country->set_navy_spending_slider_value(country_value->second); + country->set_navy_spending_slider_value(spending); return false; } -bool GameActionManager::game_action_callback_set_construction_spending(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_CONSTRUCTION_SPENDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_construction_spending_argument_t const& argument) const { + const auto [country_index, spending] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_CONSTRUCTION_SPENDING called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_CONSTRUCTION_SPENDING called with invalid country index: {}", country_index); return false; } - country->set_construction_spending_slider_value(country_value->second); + country->set_construction_spending_slider_value(spending); return false; } -bool GameActionManager::game_action_callback_set_education_spending(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_EDUCATION_SPENDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_education_spending_argument_t const& argument) const { + const auto [country_index, spending] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_EDUCATION_SPENDING called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_EDUCATION_SPENDING called with invalid country index: {}", country_index); return false; } - country->set_education_spending_slider_value(country_value->second); + country->set_education_spending_slider_value(spending); return false; } -bool GameActionManager::game_action_callback_set_administration_spending(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_ADMINISTRATION_SPENDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_administration_spending_argument_t const& argument) const { + const auto [country_index, spending] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_ADMINISTRATION_SPENDING called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_ADMINISTRATION_SPENDING called with invalid country index: {}", country_index); return false; } - country->set_administration_spending_slider_value(country_value->second); + country->set_administration_spending_slider_value(spending); return false; } -bool GameActionManager::game_action_callback_set_social_spending(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_SOCIAL_SPENDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_social_spending_argument_t const& argument) const { + const auto [country_index, spending] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_SOCIAL_SPENDING called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_SOCIAL_SPENDING called with invalid country index: {}", country_index); return false; } - country->set_social_spending_slider_value(country_value->second); + country->set_social_spending_slider_value(spending); return false; } -bool GameActionManager::game_action_callback_set_military_spending(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_MILITARY_SPENDING called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_military_spending_argument_t const& argument) const { + const auto [country_index, spending] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_MILITARY_SPENDING called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_MILITARY_SPENDING called with invalid country index: {}", country_index); return false; } - country->set_military_spending_slider_value(country_value->second); + country->set_military_spending_slider_value(spending); return false; } -bool GameActionManager::game_action_callback_set_tariff_rate(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_TARIFF_RATE called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_tariff_rate_argument_t const& argument) const { + const auto [country_index, tariff_rate] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_TARIFF_RATE called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_TARIFF_RATE called with invalid country index: {}", country_index); return false; } - country->set_tariff_rate_slider_value(country_value->second); + country->set_tariff_rate_slider_value(tariff_rate); return false; } // Technology -bool GameActionManager::game_action_callback_start_research(game_action_argument_t const& argument) const { - std::pair const* country_tech = std::get_if>(&argument); - if (OV_unlikely(country_tech == nullptr)) { - spdlog::error_s("GAME_ACTION_START_RESEARCH called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_tech->first); +bool GameActionManager::VariantVisitor::operator() (start_research_argument_t const& argument) const { + const auto [country_index, technology_index] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_START_RESEARCH called with invalid country index: {}", country_tech->first); + spdlog::error_s("GAME_ACTION_START_RESEARCH called with invalid country index: {}", country_index); return false; } - Technology const* technology = - instance_manager.definition_manager.get_research_manager().get_technology_manager().get_technology_by_index( - country_tech->second - ); + Technology const* technology = instance_manager.definition_manager + .get_research_manager() + .get_technology_manager() + .get_technology_by_index(type_safe::get(technology_index)); if (OV_unlikely(technology == nullptr)) { - spdlog::error_s("GAME_ACTION_START_RESEARCH called with invalid technology index: {}", country_tech->second); + spdlog::error_s("GAME_ACTION_START_RESEARCH called with invalid technology index: {}", technology_index); return false; } @@ -411,31 +212,19 @@ bool GameActionManager::game_action_callback_start_research(game_action_argument // Population // Trade -bool GameActionManager::game_action_callback_set_good_automated(game_action_argument_t const& argument) const { - std::tuple const* country_good_automated = - std::get_if>(&argument); - if (OV_unlikely(country_good_automated == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_GOOD_AUTOMATED called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(std::get<0>(*country_good_automated)); +bool GameActionManager::VariantVisitor::operator() (set_good_automated_argument_t const& argument) const { + const auto [country_index, good_index, new_is_automated] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_GOOD_AUTOMATED called with invalid country index: {}", std::get<0>(*country_good_automated) - ); + spdlog::error_s("GAME_ACTION_SET_GOOD_AUTOMATED called with invalid country index: {}", country_index); return false; } - GoodInstance const* good = - instance_manager.get_good_instance_manager().get_good_instance_by_index(std::get<1>(*country_good_automated)); + GoodInstance const* good = instance_manager.get_good_instance_manager().get_good_instance_by_index(good_index); if (OV_unlikely(good == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_GOOD_AUTOMATED called with invalid good index: {}", std::get<1>(*country_good_automated)); + spdlog::error_s("GAME_ACTION_SET_GOOD_AUTOMATED called with invalid good index: {}", good_index); return false; } @@ -443,38 +232,24 @@ bool GameActionManager::game_action_callback_set_good_automated(game_action_argu const bool old_automated = good_data.is_automated; - good_data.is_automated = std::get<2>(*country_good_automated); + good_data.is_automated = new_is_automated; return old_automated != good_data.is_automated; } -bool GameActionManager::game_action_callback_set_good_trade_order(game_action_argument_t const& argument) const { - std::tuple const* country_good_sell_amount = - std::get_if>(&argument); - if (OV_unlikely(country_good_sell_amount == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_GOOD_TRADE_ORDER called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(std::get<0>(*country_good_sell_amount)); +bool GameActionManager::VariantVisitor::operator() (set_good_trade_order_argument_t const& argument) const { + const auto [country_index, good_index, new_is_selling, new_cutoff] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_GOOD_TRADE_ORDER called with invalid country index: {}", std::get<0>(*country_good_sell_amount) - ); + spdlog::error_s("GAME_ACTION_SET_GOOD_TRADE_ORDER called with invalid country index: {}", country_index); return false; } - GoodInstance const* good = - instance_manager.get_good_instance_manager().get_good_instance_by_index(std::get<1>(*country_good_sell_amount)); + GoodInstance const* good = instance_manager.get_good_instance_manager().get_good_instance_by_index(good_index); if (OV_unlikely(good == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_GOOD_TRADE_ORDER called with invalid good index: {}", std::get<1>(*country_good_sell_amount) - ); + spdlog::error_s("GAME_ACTION_SET_GOOD_TRADE_ORDER called with invalid good index: {}", good_index); return false; } @@ -482,8 +257,8 @@ bool GameActionManager::game_action_callback_set_good_trade_order(game_action_ar if (OV_unlikely(good_data.is_automated)) { spdlog::error_s( - "GAME_ACTION_SET_GOOD_TRADE_ORDER called for automated good! Country: \"{}\", good: \"{}\", args: {}", - *country, *good, game_action_argument_to_string(argument) + "GAME_ACTION_SET_GOOD_TRADE_ORDER called for automated good! Country: {}, good: {}", + *country, *good ); return false; } @@ -491,8 +266,8 @@ bool GameActionManager::game_action_callback_set_good_trade_order(game_action_ar const bool old_is_selling = good_data.is_selling; const fixed_point_t old_stockpile_cutoff = good_data.stockpile_cutoff; - good_data.is_selling = std::get<2>(*country_good_sell_amount); - good_data.stockpile_cutoff = std::get<3>(*country_good_sell_amount); + good_data.is_selling = new_is_selling; + good_data.stockpile_cutoff = new_cutoff; if (good_data.stockpile_cutoff.is_negative()) { spdlog::error_s( @@ -511,18 +286,12 @@ bool GameActionManager::game_action_callback_set_good_trade_order(game_action_ar // Diplomacy // Military -bool GameActionManager::game_action_callback_create_leader(game_action_argument_t const& argument) const { - std::pair const* country_branch = std::get_if>(&argument); - if (OV_unlikely(country_branch == nullptr)) { - spdlog::error_s("GAME_ACTION_CREATE_LEADER called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_branch->first); +bool GameActionManager::VariantVisitor::operator() (create_leader_argument_t const& argument) const { + const auto [country_index, unit_branch] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_CREATE_LEADER called with invalid country index: {}", country_branch->first); + spdlog::error_s("GAME_ACTION_CREATE_LEADER called with invalid country index: {}", country_index); return false; } @@ -536,98 +305,72 @@ bool GameActionManager::game_action_callback_create_leader(game_action_argument_ return instance_manager.get_unit_instance_manager().create_leader( *country, - country_branch->second ? unit_branch_t::LAND : unit_branch_t::NAVAL, + unit_branch, instance_manager.get_today() ); } -bool GameActionManager::game_action_callback_set_use_leader(game_action_argument_t const& argument) const { - std::pair const* leader_use = std::get_if>(&argument); - if (OV_unlikely(leader_use == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_USE_LEADER called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } +bool GameActionManager::VariantVisitor::operator() (set_use_leader_argument_t const& argument) const { + const auto [unique_id, new_should_use] = argument; - LeaderInstance* leader = instance_manager.get_unit_instance_manager().get_leader_instance_by_unique_id(leader_use->first); + LeaderInstance* leader = instance_manager.get_unit_instance_manager().get_leader_instance_by_unique_id(unique_id); if (OV_unlikely(leader == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_USE_LEADER called with invalid leader index: {}", leader_use->first); + spdlog::error_s("GAME_ACTION_SET_USE_LEADER called with invalid leader unique id: {}", unique_id); return false; } const bool old_use = leader->get_can_be_used(); - leader->set_can_be_used(leader_use->second); + leader->set_can_be_used(new_should_use); return old_use != leader->get_can_be_used(); } -bool GameActionManager::game_action_callback_set_auto_create_leaders(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_AUTO_CREATE_LEADERS called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_auto_create_leaders_argument_t const& argument) const { + const auto [country_index, new_should_auto_create] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_AUTO_CREATE_LEADERS called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_AUTO_CREATE_LEADERS called with invalid country index: {}", country_index); return false; } const bool old_auto_create = country->get_auto_create_leaders(); - country->set_auto_create_leaders(country_value->second); + country->set_auto_create_leaders(new_should_auto_create); return old_auto_create != country->get_auto_create_leaders(); } -bool GameActionManager::game_action_callback_set_auto_assign_leaders(game_action_argument_t const& argument) const { - std::pair const* country_value = std::get_if>(&argument); - if (OV_unlikely(country_value == nullptr)) { - spdlog::error_s( - "GAME_ACTION_SET_AUTO_ASSIGN_LEADERS called with invalid argument: {}", game_action_argument_to_string(argument) - ); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_value->first); +bool GameActionManager::VariantVisitor::operator() (set_auto_assign_leaders_argument_t const& argument) const { + const auto [country_index, new_should_auto_assign] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_AUTO_ASSIGN_LEADERS called with invalid country index: {}", country_value->first); + spdlog::error_s("GAME_ACTION_SET_AUTO_ASSIGN_LEADERS called with invalid country index: {}", country_index); return false; } const bool old_auto_assign = country->get_auto_assign_leaders(); - country->set_auto_assign_leaders(country_value->second); + country->set_auto_assign_leaders(new_should_auto_assign); return old_auto_assign != country->get_auto_assign_leaders(); } -bool GameActionManager::game_action_callback_set_mobilise(game_action_argument_t const& argument) const { - std::pair const* country_mobilise = std::get_if>(&argument); - if (OV_unlikely(country_mobilise == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_MOBILISE called with invalid argument: {}", game_action_argument_to_string(argument)); - return false; - } - - CountryInstance* country = - instance_manager.get_country_instance_manager().get_country_instance_by_index(country_mobilise->first); +bool GameActionManager::VariantVisitor::operator() (set_mobilise_argument_t const& argument) const { + const auto [country_index, new_is_mobilised] = argument; + CountryInstance* country = instance_manager.get_country_instance_manager().get_country_instance_by_index(country_index); if (OV_unlikely(country == nullptr)) { - spdlog::error_s("GAME_ACTION_SET_MOBILISE called with invalid country index: {}", country_mobilise->first); + spdlog::error_s("GAME_ACTION_SET_MOBILISE called with invalid country index: {}", country_index); return false; } const bool old_mobilise = country->is_mobilised(); - country->set_mobilised(country_mobilise->second); + country->set_mobilised(new_is_mobilised); return old_mobilise != country->is_mobilised(); } diff --git a/src/openvic-simulation/misc/GameAction.hpp b/src/openvic-simulation/misc/GameAction.hpp index cc3035d8..a959ee9a 100644 --- a/src/openvic-simulation/misc/GameAction.hpp +++ b/src/openvic-simulation/misc/GameAction.hpp @@ -1,158 +1,153 @@ #pragma once -#include +#include #include -#include +#include #include #include -#include "openvic-simulation/types/TypedIndices.hpp" +#include + #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/UniqueId.hpp" +#include "openvic-simulation/types/UnitBranchType.hpp" namespace OpenVic { +namespace detail { + template + struct reduce_tuple { + using type = std::tuple; + }; - enum struct game_action_type_t : uint64_t { - GAME_ACTION_NONE, - - // Core - GAME_ACTION_TICK, - GAME_ACTION_SET_PAUSE, - GAME_ACTION_SET_SPEED, - GAME_ACTION_SET_AI, - - // Production - GAME_ACTION_EXPAND_PROVINCE_BUILDING, - // TODO - enable/disable subsidies, open, close factory + ways to do these for all factories - // - build, expand and destroy factory - // - change a factory's hiring priority - // - invest in a project - // - foreign investment? - - // Budget - GAME_ACTION_SET_STRATA_TAX, - GAME_ACTION_SET_ARMY_SPENDING, - GAME_ACTION_SET_NAVY_SPENDING, - GAME_ACTION_SET_CONSTRUCTION_SPENDING, - GAME_ACTION_SET_EDUCATION_SPENDING, - GAME_ACTION_SET_ADMINISTRATION_SPENDING, - GAME_ACTION_SET_SOCIAL_SPENDING, - GAME_ACTION_SET_MILITARY_SPENDING, - GAME_ACTION_SET_TARIFF_RATE, - // TODO - take and repay loans - - // Technology - GAME_ACTION_START_RESEARCH, - - // Politics - // TODO - change ruling party, hold elections, pass reform, suppress movement, westernise, take decision, release nation - - // Population - // TODO - maybe put "set national focus" under this category? - - // Trade - GAME_ACTION_SET_GOOD_AUTOMATED, - GAME_ACTION_SET_GOOD_TRADE_ORDER, - - // Diplomacy - // TODO - diplomatic actions, set influence priority, add wargoal, intervene in war, cancel CB justification, crisis stuff - - // Military - GAME_ACTION_CREATE_LEADER, - GAME_ACTION_SET_USE_LEADER, - GAME_ACTION_SET_AUTO_CREATE_LEADERS, - GAME_ACTION_SET_AUTO_ASSIGN_LEADERS, - GAME_ACTION_SET_MOBILISE, - // TODO - rally point settings, build army/navy, stack actions (move, merge, split, delete (undermanned), (dis)embark, - // set hunt rebels, reorganise, change leader, etc) - - // TODO - Others: - // - select event option - // - set country and interesting or not (buttons upon right clicking on flag icons) - // - upgrade colony status - // - execute commands (or should the console just convert commands which affect gamestate to regular game actions?) - - MAX_GAME_ACTION + template + struct reduce_tuple { + using type = std::pair; }; - using game_action_argument_t = std::variant< - std::monostate, - bool, - int64_t, - std::pair, - std::pair, - std::pair, - std::tuple, - std::pair, - std::pair, - std::tuple, - std::tuple - >; + template + struct reduce_tuple { + using type = T; + }; - memory::string game_action_argument_to_string(game_action_argument_t const& argument); + struct ts_op_structured_binding_passthrough {}; +} - using game_action_t = std::pair; +// To add a new game action: +// 1. **Copy** an existing line. +// 2. **Edit** the game action name and its argument type. +// +// The format for each action is: X(action_name, argument_type1, argument_type2, ...) +// `none` is handled as example throughout the file +#define FOR_EACH_GAME_ACTION(X) \ +X(tick, std::monostate) \ +X(set_pause, bool) \ +X(set_speed, int64_t) \ +X(set_ai, country_index_t, bool) \ +X(expand_province_building, province_index_t, building_type_index_t) \ +X(set_strata_tax, country_index_t, strata_index_t, fixed_point_t) \ +X(set_army_spending, country_index_t, fixed_point_t) \ +X(set_navy_spending, country_index_t, fixed_point_t) \ +X(set_construction_spending, country_index_t, fixed_point_t) \ +X(set_education_spending, country_index_t, fixed_point_t) \ +X(set_administration_spending, country_index_t, fixed_point_t) \ +X(set_social_spending, country_index_t, fixed_point_t) \ +X(set_military_spending, country_index_t, fixed_point_t) \ +X(set_tariff_rate, country_index_t, fixed_point_t) \ +X(start_research, country_index_t, technology_index_t) \ +X(set_good_automated, country_index_t, good_index_t, bool) \ +X(set_good_trade_order, country_index_t, good_index_t, bool, fixed_point_t) \ +X(create_leader, country_index_t, unit_branch_t) \ +X(set_use_leader, unique_id_t, bool) \ +X(set_auto_create_leaders, country_index_t, bool) \ +X(set_auto_assign_leaders, country_index_t, bool) \ +X(set_mobilise, country_index_t, bool) +// <--- ADD NEW GAME ACTIONS HERE (copy/edit an X(...) line) + +//the argument type alias for each game action +#define ARG_TYPE(name) name##_argument_t + +//define type aliases for game action arguments +//example: +struct none_argument_t : type_safe::strong_typedef { + using strong_typedef::strong_typedef; + constexpr none_argument_t(std::monostate const& value) : strong_typedef(value) {} + constexpr none_argument_t(std::monostate&& value) + : strong_typedef(static_cast(value)) {} +}; +#define USING_ARG_TYPE(name, ...) \ +struct ARG_TYPE(name) : type_safe::strong_typedef::type>, detail::ts_op_structured_binding_passthrough { \ + using strong_typedef::strong_typedef; \ + constexpr ARG_TYPE(name)(detail::reduce_tuple<__VA_ARGS__>::type const& value) : strong_typedef(value) {} \ + constexpr ARG_TYPE(name)(detail::reduce_tuple<__VA_ARGS__>::type && value) \ + : strong_typedef(static_cast::type&&>(value)) {} \ +}; + +FOR_EACH_GAME_ACTION(USING_ARG_TYPE) +#undef USING_ARG_TYPE + +//define game_action_t as a variant of all possible game action arguments +#define COMMA_ARG_TYPE(name, ...) , ARG_TYPE(name) + using game_action_t = std::variant< + none_argument_t + FOR_EACH_GAME_ACTION(COMMA_ARG_TYPE) + >; +#undef COMMA_ARG_TYPE struct InstanceManager; struct GameActionManager { private: - InstanceManager& instance_manager; - + struct VariantVisitor { + private: + InstanceManager& instance_manager; + public: + constexpr VariantVisitor(InstanceManager& new_instance_manager) : instance_manager { new_instance_manager } {} +//define a method for each game method +#define DEFINE_ARG_TYPE_AND_METHOD(name, ...) bool operator()(ARG_TYPE(name) const& argument) const; +//example: +bool operator()(none_argument_t const& argument) const; + FOR_EACH_GAME_ACTION(DEFINE_ARG_TYPE_AND_METHOD) +#undef DEFINE_ARG_TYPE_AND_METHOD + }; + + VariantVisitor visitor; public: - GameActionManager(InstanceManager& new_instance_manager); + constexpr GameActionManager(InstanceManager& new_instance_manager) : visitor { new_instance_manager } {} - bool execute_game_action(game_action_t const& game_action) const; - - private: - // Return value indicates whether a gamestate update is needed or not - // TODO - replace return bool with value indicating what needs updating in the gamestate and/or UI? - // Or send those signals via the InstanceManager&, e.g. if multiple values need to be sent for a single action - using game_action_callback_t = bool(GameActionManager::*)(game_action_argument_t const&) const; - - static std::array< - game_action_callback_t, static_cast(game_action_type_t::MAX_GAME_ACTION) - > GAME_ACTION_CALLBACKS; - - bool game_action_callback_none(game_action_argument_t const& argument) const; - - // Core - bool game_action_callback_tick(game_action_argument_t const& argument) const; - bool game_action_callback_set_pause(game_action_argument_t const& argument) const; - bool game_action_callback_set_speed(game_action_argument_t const& argument) const; - bool game_action_callback_set_ai(game_action_argument_t const& argument) const; - - // Production - bool game_action_callback_expand_province_building(game_action_argument_t const& argument) const; - - // Budget - bool game_action_callback_set_strata_tax(game_action_argument_t const& argument) const; - bool game_action_callback_set_army_spending(game_action_argument_t const& argument) const; - bool game_action_callback_set_navy_spending(game_action_argument_t const& argument) const; - bool game_action_callback_set_construction_spending(game_action_argument_t const& argument) const; - bool game_action_callback_set_education_spending(game_action_argument_t const& argument) const; - bool game_action_callback_set_administration_spending(game_action_argument_t const& argument) const; - bool game_action_callback_set_social_spending(game_action_argument_t const& argument) const; - bool game_action_callback_set_military_spending(game_action_argument_t const& argument) const; - bool game_action_callback_set_tariff_rate(game_action_argument_t const& argument) const; - - // Technology - bool game_action_callback_start_research(game_action_argument_t const& argument) const; - - // Politics - - // Population - - // Trade - bool game_action_callback_set_good_automated(game_action_argument_t const& argument) const; - bool game_action_callback_set_good_trade_order(game_action_argument_t const& argument) const; - - // Diplomacy - - // Military - bool game_action_callback_create_leader(game_action_argument_t const& argument) const; - bool game_action_callback_set_use_leader(game_action_argument_t const& argument) const; - bool game_action_callback_set_auto_create_leaders(game_action_argument_t const& argument) const; - bool game_action_callback_set_auto_assign_leaders(game_action_argument_t const& argument) const; - bool game_action_callback_set_mobilise(game_action_argument_t const& argument) const; + constexpr bool execute_game_action(game_action_t const& game_action) const { + return std::visit(visitor, game_action); + } }; + + template T> + decltype(auto) get(T& value) { + return std::get(type_safe::get(value)); + } + + template T> + decltype(auto) get(T const& value) { + return std::get(type_safe::get(value)); + } +} + +namespace std { + template T> + struct tuple_size : tuple_size()))>> {}; + + template T> + struct tuple_element : tuple_element()))>> {}; + + template T> + decltype(auto) get(T& value) { + return OpenVic::get(value); + } + + template T> + decltype(auto) get(T const& value) { + return OpenVic::get(value); + } } + +#undef ARG_TYPE +#undef FOR_EACH_GAME_ACTION \ No newline at end of file