diff --git a/src/headless/main.cpp b/src/headless/main.cpp index afba914f8..9769be4c0 100644 --- a/src/headless/main.cpp +++ b/src/headless/main.cpp @@ -261,44 +261,42 @@ static bool run_headless(fs::path const& root, memory::vector& m print_memory_usage("Game Session Post-Start"); // TODO - REMOVE TEST CODE + InstanceManager& instance_manager = *game_manager.get_instance_manager(); SPDLOG_INFO("===== Ranking system test... ====="); - if (game_manager.get_instance_manager()) { - const auto print_ranking_list = [ // - ](std::string_view title, OpenVic::forwardable_span> countries) -> void { - memory::string countries_str; - for (CountryInstance& country : countries) { - countries_str += fmt::format( - "\n\t{} - Total #{} ({:.1}), Prestige #{} ({:.1}), Industry #{} ({:.1}), Military #{} ({:.1})", // - country, // - country.get_total_rank(), country.total_score.get_untracked(), // - country.get_prestige_rank(), country.get_prestige_untracked(), - country.get_industrial_rank(), country.get_industrial_power_untracked(), - country.get_military_rank(), country.military_power.get_untracked() - ); - } - SPDLOG_INFO("{}:{}", title, countries_str); - }; - - CountryInstanceManager const& country_instance_manager = game_manager.get_instance_manager()->get_country_instance_manager(); - - OpenVic::forwardable_span> great_powers = country_instance_manager.get_great_powers(); - print_ranking_list("Great Powers", great_powers); - print_ranking_list("Secondary Powers", country_instance_manager.get_secondary_powers()); - print_ranking_list("All countries", country_instance_manager.get_total_ranking()); - - SPDLOG_INFO("===== RGO test... ====="); - for (size_t i = 0; i < std::min(3, great_powers.size()); ++i) { - CountryInstance const& great_power = great_powers[i]; - ProvinceInstance const* const capital_province = great_power.get_capital(); - if (capital_province == nullptr) { - spdlog::warn_s("{} has no capital ProvinceInstance set.", great_power); - } else { - print_rgo(*capital_province); - } + const auto print_ranking_list = []( + std::string_view title, + OpenVic::forwardable_span> countries + ) -> void { + memory::string countries_str; + for (CountryInstance& country : countries) { + countries_str += fmt::format( + "\n\t{} - Total #{} ({:.1}), Prestige #{} ({:.1}), Industry #{} ({:.1}), Military #{} ({:.1})", // + country, // + country.get_total_rank(), country.total_score.get_untracked(), // + country.get_prestige_rank(), country.get_prestige_untracked(), + country.get_industrial_rank(), country.get_industrial_power_untracked(), + country.get_military_rank(), country.military_power.get_untracked() + ); + } + SPDLOG_INFO("{}:{}", title, countries_str); + }; + + CountryInstanceManager const& country_instance_manager = game_manager.get_instance_manager()->get_country_instance_manager(); + + OpenVic::forwardable_span> great_powers = country_instance_manager.get_great_powers(); + print_ranking_list("Great Powers", great_powers); + print_ranking_list("Secondary Powers", country_instance_manager.get_secondary_powers()); + print_ranking_list("All countries", country_instance_manager.get_total_ranking()); + + SPDLOG_INFO("===== RGO test... ====="); + for (size_t i = 0; i < std::min(3, great_powers.size()); ++i) { + CountryInstance const& great_power = great_powers[i]; + ProvinceInstance const* const capital_province = great_power.get_capital(); + if (capital_province == nullptr) { + spdlog::warn_s("{} has no capital ProvinceInstance set.", great_power); + } else { + print_rgo(*capital_province); } - } else { - spdlog::error_s("Instance manager not available!"); - ret = false; } if (ret) { @@ -308,7 +306,7 @@ static bool run_headless(fs::path const& root, memory::vector& m using test_time_units_t = std::chrono::milliseconds; - MapInstance& map_instance = game_manager.get_instance_manager()->get_map_instance(); + MapInstance& map_instance = instance_manager.get_map_instance(); SPDLOG_INFO("===== Land Pathfinding test... ====="); test_duration_t duration = std::chrono::duration_cast( // diff --git a/src/openvic-simulation/DefinitionManager.hpp b/src/openvic-simulation/DefinitionManager.hpp index e30d44d5f..d2b66c485 100644 --- a/src/openvic-simulation/DefinitionManager.hpp +++ b/src/openvic-simulation/DefinitionManager.hpp @@ -1,25 +1,24 @@ #pragma once -#include "openvic-simulation/country/CountryDefinition.hpp" +#include "openvic-simulation/country/CountryDefinitionManager.hpp" #include "openvic-simulation/defines/Define.hpp" -#include "openvic-simulation/diplomacy/DiplomaticAction.hpp" +#include "openvic-simulation/diplomacy/DiplomaticActionManager.hpp" #include "openvic-simulation/economy/EconomyManager.hpp" #include "openvic-simulation/history/HistoryManager.hpp" #include "openvic-simulation/interface/UI.hpp" -#include "openvic-simulation/map/Crime.hpp" +#include "openvic-simulation/map/CrimeManager.hpp" #include "openvic-simulation/map/MapDefinition.hpp" -#include "openvic-simulation/map/Mapmode.hpp" +#include "openvic-simulation/map/MapmodeManager.hpp" #include "openvic-simulation/military/MilitaryManager.hpp" -#include "openvic-simulation/misc/Decision.hpp" -#include "openvic-simulation/misc/Event.hpp" -#include "openvic-simulation/misc/SongChance.hpp" -#include "openvic-simulation/misc/SoundEffect.hpp" +#include "openvic-simulation/misc/DecisionManager.hpp" +#include "openvic-simulation/misc/EventManager.hpp" +#include "openvic-simulation/misc/SongChanceManager.hpp" +#include "openvic-simulation/misc/SoundEffectManager.hpp" #include "openvic-simulation/modifier/ModifierManager.hpp" #include "openvic-simulation/politics/PoliticsManager.hpp" #include "openvic-simulation/population/PopManager.hpp" #include "openvic-simulation/research/ResearchManager.hpp" #include "openvic-simulation/scripts/ScriptManager.hpp" -#include "openvic-simulation/interface/UI.hpp" namespace OpenVic { struct DefinitionManager { diff --git a/src/openvic-simulation/InstanceManager.cpp b/src/openvic-simulation/InstanceManager.cpp index a98f0cc7b..64cb30f90 100644 --- a/src/openvic-simulation/InstanceManager.cpp +++ b/src/openvic-simulation/InstanceManager.cpp @@ -5,6 +5,7 @@ #include "openvic-simulation/misc/GameAction.hpp" #include "openvic-simulation/types/TypedSpan.hpp" #include "openvic-simulation/utility/Logger.hpp" +#include "openvic-simulation/utility/ThreadDeps.hpp" using namespace OpenVic; @@ -168,8 +169,8 @@ void InstanceManager::update_gamestate() { update_modifier_sums(); // Update gamestate... - map_instance.update_gamestate(*this); - country_instance_manager.update_gamestate(today, map_instance); + map_instance.update_gamestate(); + country_instance_manager.update_gamestate_after_map(today); unit_instance_manager.update_gamestate(); gamestate_updated(); @@ -234,12 +235,19 @@ bool InstanceManager::setup() { } thread_pool.initialise_threadpool( - game_rules_manager, - good_instance_manager, - definition_manager.get_modifier_manager().get_modifier_effect_cache(), - definition_manager.get_define_manager().get_pops_defines(), - definition_manager.get_economy_manager().get_production_type_manager(), - strata_index_t(definition_manager.get_pop_manager().get_strata_count()), + ThreadDeps{ + game_rules_manager, + good_instance_manager, + map_instance, + definition_manager.get_define_manager().get_military_defines(), + definition_manager.get_modifier_manager().get_modifier_effect_cache(), + definition_manager.get_define_manager().get_pops_defines(), + definition_manager.get_economy_manager().get_production_type_manager(), + definition_manager.get_modifier_manager().get_static_modifier_cache(), + country_index_t(definition_manager.get_country_definition_manager().get_country_definition_count()), + good_index_t(definition_manager.get_economy_manager().get_good_definition_manager().get_good_definition_count()), + strata_index_t(definition_manager.get_pop_manager().get_strata_count()) + }, good_instance_manager.get_good_instances(), country_instance_manager.get_country_instances(), map_instance.get_province_instances() @@ -305,8 +313,8 @@ bool InstanceManager::load_bookmark(Bookmark const& new_bookmark) { OV_ERR_FAIL_COND_V_MSG(!all_has_state, false, "At least one land province has no state"); update_modifier_sums(); - map_instance.initialise_for_new_game(*this); - country_instance_manager.update_gamestate(today, map_instance); + map_instance.initialise_for_new_game(); + country_instance_manager.update_gamestate_after_map(today); market_instance.execute_orders(); return ret; @@ -359,12 +367,9 @@ void InstanceManager::update_modifier_sums() { // full copy of all the modifiers affecting them in their modifier sum, but provinces only having their directly/locally // applied modifiers in their modifier sum, hence requiring owner country modifier effect values to be looked up when // determining the value of a global effect on the province. - country_instance_manager.update_modifier_sums( - today, definition_manager.get_modifier_manager().get_static_modifier_cache() - ); - map_instance.update_modifier_sums( - today, definition_manager.get_modifier_manager().get_static_modifier_cache() - ); + country_instance_manager.update_modifier_sums_before_map(); + map_instance.update_modifier_sums(today); + country_instance_manager.update_modifier_sums_after_map(); } bool InstanceManager::queue_game_action(game_action_t&& game_action) { diff --git a/src/openvic-simulation/InstanceManager.hpp b/src/openvic-simulation/InstanceManager.hpp index 0b82ad79b..55f3fe54d 100644 --- a/src/openvic-simulation/InstanceManager.hpp +++ b/src/openvic-simulation/InstanceManager.hpp @@ -9,14 +9,14 @@ #include "openvic-simulation/country/CountryInstanceDeps.hpp" #include "openvic-simulation/country/CountryInstanceManager.hpp" #include "openvic-simulation/diplomacy/CountryRelation.hpp" -#include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/economy/GoodInstanceManager.hpp" #include "openvic-simulation/economy/production/ArtisanalProducerDeps.hpp" #include "openvic-simulation/economy/production/ResourceGatheringOperationDeps.hpp" #include "openvic-simulation/economy/trading/MarketInstance.hpp" #include "openvic-simulation/map/MapInstance.hpp" #include "openvic-simulation/map/Mapmode.hpp" #include "openvic-simulation/map/ProvinceInstanceDeps.hpp" -#include "openvic-simulation/military/UnitInstanceGroup.hpp" +#include "openvic-simulation/military/UnitInstanceManager.hpp" #include "openvic-simulation/misc/GameAction.hpp" #include "openvic-simulation/misc/SimulationClock.hpp" #include "openvic-simulation/politics/PoliticsInstanceManager.hpp" diff --git a/src/openvic-simulation/core/BulkInsertWrapper.hpp b/src/openvic-simulation/core/BulkInsertWrapper.hpp new file mode 100644 index 000000000..cd31e8bdf --- /dev/null +++ b/src/openvic-simulation/core/BulkInsertWrapper.hpp @@ -0,0 +1,201 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include "openvic-simulation/core/Assert.hpp" + +namespace OpenVic { + // not thread safe + template + struct bulk_insert_wrapper { + public: + // Member types based on std::vector + using container_type = Container; + using value_type = typename container_type::value_type; + using allocator_type = typename container_type::allocator_type; + using size_type = typename container_type::size_type; + using difference_type = typename container_type::difference_type; + using reference = typename container_type::reference; + using const_reference = typename container_type::const_reference; + using pointer = typename container_type::pointer; + using const_pointer = typename container_type::const_pointer; + using iterator = typename container_type::iterator; + using const_iterator = typename container_type::const_iterator; + using reverse_iterator = typename container_type::reverse_iterator; + using const_reverse_iterator = typename container_type::const_reverse_iterator; + + static_assert(std::is_default_constructible_v); + static_assert(std::is_trivially_destructible_v); + + private: + Container container; + std::atomic pending_extra_size {}; + + constexpr void flush_pending_room() { + if (pending_extra_size > size_type{}) { + size_type valid_size { size() }; + container.resize(valid_size + pending_extra_size); + container.resize(valid_size); + pending_extra_size = size_type{}; + } + } + + public: + constexpr allocator_type get_allocator() const { + return container.get_allocator(); + } + + constexpr bulk_insert_wrapper() noexcept {}; + + // Forwarding constructor for custom allocators or initial capacities + template + constexpr explicit bulk_insert_wrapper(Args&&... args) + : container(std::forward(args)...) {} + + // thread safe + constexpr void make_room_for(const size_type count) noexcept { + pending_extra_size += count; + } + + // Element access based on std::vector + constexpr reference operator[](const size_type pos) { + OV_HARDEN_ASSERT_ACCESS(pos, "operator[]"); + return container[pos]; + } + constexpr const_reference operator[](const size_type pos) const { + OV_HARDEN_ASSERT_ACCESS(pos, "operator[]"); + return container[pos]; + } + + constexpr reference front() { + OV_HARDEN_ASSERT_NONEMPTY("front"); + return container[0]; + } + constexpr const_reference front() const { + OV_HARDEN_ASSERT_NONEMPTY("front"); + return container[0]; + } + + constexpr reference back() { + OV_HARDEN_ASSERT_NONEMPTY("back"); + return container[size()-1]; + } + constexpr const_reference back() const { + OV_HARDEN_ASSERT_NONEMPTY("back"); + return container[size()-1]; + } + + constexpr value_type* data() noexcept { return container.data(); } + constexpr value_type const* data() const noexcept { return container.data(); } + + // Iterators based on std::vector + constexpr iterator begin() noexcept { + return container.begin(); + } + constexpr const_iterator begin() const noexcept { + return container.begin(); + } + constexpr const_iterator cbegin() const noexcept { + return container.cbegin(); + } + + constexpr iterator end() noexcept { + return container.end(); + } + constexpr const_iterator end() const noexcept { + return container.end(); + } + constexpr const_iterator cend() const noexcept { + return container.cend(); + } + + constexpr reverse_iterator rbegin() noexcept { + return container.rbegin(); + } + constexpr const_reverse_iterator rbegin() const noexcept { + return container.rbegin(); + } + constexpr const_reverse_iterator crbegin() const noexcept { + return container.crbegin(); + } + + constexpr reverse_iterator rend() noexcept { + return container.rend(); + } + constexpr const_reverse_iterator rend() const noexcept { + return container.rend(); + } + constexpr const_reverse_iterator crend() const noexcept { + return container.crend(); + } + + // Capacity based on std::vector + constexpr bool empty() const noexcept { return size() <= size_type{}; } + constexpr size_type size() const noexcept { return container.size(); } + constexpr size_type max_size() const noexcept { return container.max_size(); } + // reserve() is omitted as we manage that via make_room_for + constexpr size_type capacity() const noexcept { return container.capacity(); } + constexpr void shrink_to_fit() { + pending_extra_size = size_type{}; + container.shrink_to_fit(); + } + + // Modifiers based on std::vector + constexpr void clear() noexcept { + pending_extra_size = size_type{}; + container.clear(); + } + + // the following could be implemented: + // - insert + // - insert_range + // - emplace + // - erase + // - append_range (C++23) + // - pop_back + // - swap + + constexpr void push_back(value_type const& value) { + flush_pending_room(); + container.push_back(value); + + } + constexpr void push_back(value_type&& value) { + flush_pending_room(); + container.push_back(std::move(value)); + } + + template + requires std::is_trivially_destructible_v + constexpr reference emplace_back(Args&&... args) { + return container.emplace_back(std::forward(args)...); + } + + template + constexpr void append_range(OtherContainerT const& other) { + append_range(other.begin(), other.end()); + } + + template + constexpr void append_range(const InputIt first, const InputIt last) { + flush_pending_room(); + + const size_type new_valid_size = size() + std::distance(first, last); + if (new_valid_size > container.capacity()) { + assert(!"append_range called without make_room_for"); + container.reserve(new_valid_size); + } + + std::uninitialized_copy(first, last, end()); + container.resize(new_valid_size); + } + + // resize() is omitted as we manage that via make_room_for + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/core/Hash.hpp b/src/openvic-simulation/core/Hash.hpp index 2224dead1..3a6ef924b 100644 --- a/src/openvic-simulation/core/Hash.hpp +++ b/src/openvic-simulation/core/Hash.hpp @@ -47,9 +47,11 @@ namespace OpenVic { 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)) { + void const* const args_ptr = static_cast(&args); + void const* const last_pointer_of_args = static_cast( + &(std::get(arg_tuple)) + ); + if (args_ptr != last_pointer_of_args) { s <<= sizeof(Args) * CHAR_BIT; } s |= std::hash {}(args); diff --git a/src/openvic-simulation/core/portable/Deque.hpp b/src/openvic-simulation/core/portable/Deque.hpp index 95f80dcd1..3b40113c8 100644 --- a/src/openvic-simulation/core/portable/Deque.hpp +++ b/src/openvic-simulation/core/portable/Deque.hpp @@ -1022,8 +1022,10 @@ namespace OpenVic::utility::_detail::deque { // (_Myoff() + _Mysize() - 1) is for the last element, i.e. the back() of the deque. // Divide by _Block_size to get the unmasked index of the last used block. // Add 1 to get the unmasked index of the first unused block. - const auto _Unmasked_first_unused_block_idx = - static_cast(((_Myoff() + _Mysize() - 1) / _Block_size) + 1); + const auto _Unmasked_first_unused_block_idx = static_cast( + ((_Myoff() + _Mysize() - 1) / _Block_size) + + 1 + ); const auto _First_unused_block_idx = static_cast(_Unmasked_first_unused_block_idx & _Mask); @@ -1037,8 +1039,10 @@ namespace OpenVic::utility::_detail::deque { } } - const auto _Used_block_count = - static_cast(_Unmasked_first_unused_block_idx - _Unmasked_first_used_block_idx); + const auto _Used_block_count = static_cast( + _Unmasked_first_unused_block_idx + - _Unmasked_first_used_block_idx + ); size_type _New_block_count = _Minimum_map_size; // should be power of 2 @@ -1818,8 +1822,12 @@ namespace OpenVic::utility::_detail::deque { _EXPORT_STD template _NODISCARD bool operator==(const deque<_Ty, _Alloc>& _Left, const deque<_Ty, _Alloc>& _Right) { - return _Left.size() == _Right.size() && - _STD equal(_Left._Unchecked_begin(), _Left._Unchecked_end(), _Right._Unchecked_begin()); + return _Left.size() == _Right.size() + && _STD equal( + _Left._Unchecked_begin(), + _Left._Unchecked_end(), + _Right._Unchecked_begin() + ); } #if _HAS_CXX20 diff --git a/src/openvic-simulation/core/portable/ForwardableSpan.hpp b/src/openvic-simulation/core/portable/ForwardableSpan.hpp index fe117bc0f..a4414860a 100644 --- a/src/openvic-simulation/core/portable/ForwardableSpan.hpp +++ b/src/openvic-simulation/core/portable/ForwardableSpan.hpp @@ -46,12 +46,12 @@ namespace OpenVic::_detail::forwardable_span { concept span_array_convertible = std::is_convertible_v; template - concept span_compatible_iterator = - std::contiguous_iterator && span_array_convertible>, T>; + concept span_compatible_iterator = std::contiguous_iterator + && span_array_convertible>, T>; template - concept span_compatible_sentinel_for = - std::sized_sentinel_for && !std::is_convertible_v; + concept span_compatible_sentinel_for = std::sized_sentinel_for + && !std::is_convertible_v; template struct extent_storage { diff --git a/src/openvic-simulation/core/random/RandomGenerator.hpp b/src/openvic-simulation/core/random/RandomGenerator.hpp index a010c146a..cae5294f3 100644 --- a/src/openvic-simulation/core/random/RandomGenerator.hpp +++ b/src/openvic-simulation/core/random/RandomGenerator.hpp @@ -15,10 +15,16 @@ namespace OpenVic { struct RandomGenerator { using generator_type = T; - using state_type = - std::conditional_t; - using result_type = - std::conditional_t; + using state_type = std::conditional_t< + requires { typename generator_type::state_type; }, + typename generator_type::state_type, + void + >; + using result_type = std::conditional_t< + requires { typename generator_type::result_type; }, + typename generator_type::result_type, + void + >; [[nodiscard]] OV_ALWAYS_INLINE explicit constexpr RandomGenerator() requires requires { diff --git a/src/openvic-simulation/core/template/Concepts.hpp b/src/openvic-simulation/core/template/Concepts.hpp index d21d92c12..dc856af0e 100644 --- a/src/openvic-simulation/core/template/Concepts.hpp +++ b/src/openvic-simulation/core/template/Concepts.hpp @@ -41,15 +41,16 @@ namespace OpenVic { concept not_same_as = !std::same_as; template> - concept move_insertable_allocator_for = - requires(Allocator& alloc, Value* value, T move) { std::allocator_traits::construct(alloc, value, move); }; + concept move_insertable_allocator_for = requires(Allocator& alloc, Value* value, T move) { + std::allocator_traits::construct(alloc, value, move); + }; template struct enable_copy_insertable : std::false_type {}; template - concept copy_insertable_allocator = enable_copy_insertable::value || - move_insertable_allocator_for; + concept copy_insertable_allocator = enable_copy_insertable::value + || move_insertable_allocator_for; template struct enable_copy_insertable> : std::is_copy_constructible {}; @@ -58,8 +59,8 @@ namespace OpenVic { struct enable_move_insertable : std::false_type {}; template - concept move_insertable_allocator = - enable_move_insertable::value || move_insertable_allocator_for; + concept move_insertable_allocator = enable_move_insertable::value + || move_insertable_allocator_for; template struct enable_move_insertable> : std::is_move_constructible {}; @@ -111,8 +112,9 @@ namespace OpenVic { concept is_strongly_typed = derived_from_specialization_of; template - concept has_index = requires { typename T::index_t; } && - is_strongly_typed && requires { + concept has_index = requires { typename T::index_t; } + && is_strongly_typed + && requires { static_cast( static_cast().index)>>(std::declval().index) ); diff --git a/src/openvic-simulation/core/template/FunctionalConcepts.hpp b/src/openvic-simulation/core/template/FunctionalConcepts.hpp new file mode 100644 index 000000000..92a60dee4 --- /dev/null +++ b/src/openvic-simulation/core/template/FunctionalConcepts.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include +#include + +namespace OpenVic { +#define FWD(...) static_cast(__VA_ARGS__) + template + concept Functor = requires(Fn&& fn, Args&&... args) { + { std::invoke(FWD(fn), FWD(args)...) } -> std::same_as; + }; + + template + concept FunctorConvertible = requires(Fn&& fn, Args&&... args) { + { std::invoke(FWD(fn), FWD(args)...) } -> std::convertible_to; + }; + + template + concept Callback = Functor; +#undef FWD +} \ No newline at end of file diff --git a/src/openvic-simulation/country/CountryDefinition.cpp b/src/openvic-simulation/country/CountryDefinition.cpp index f22260228..6814f1ee7 100644 --- a/src/openvic-simulation/country/CountryDefinition.cpp +++ b/src/openvic-simulation/country/CountryDefinition.cpp @@ -2,23 +2,11 @@ #include -#include - -#include "openvic-simulation/dataloader/Dataloader.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/DefinitionManager.hpp" -#include "openvic-simulation/politics/Government.hpp" -#include "openvic-simulation/politics/Ideology.hpp" -#include "openvic-simulation/politics/PartyPolicy.hpp" #include "openvic-simulation/population/Culture.hpp" #include "openvic-simulation/types/Colour.hpp" -#include "openvic-simulation/types/FixedVector.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/types/TypedIndices.hpp" -#include "openvic-simulation/utility/Logger.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; CountryDefinition::CountryDefinition( std::string_view new_identifier, @@ -43,201 +31,4 @@ CountryDefinition::CountryDefinition( secondary_unit_colour { new_secondary_unit_colour }, tertiary_unit_colour { new_tertiary_unit_colour } {} -bool CountryDefinitionManager::add_country( - std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, - IdentifierRegistry&& parties, CountryDefinition::unit_names_map_t&& unit_names, bool dynamic_tag, - CountryDefinition::government_colour_map_t&& alternative_colours -) { - if (identifier.empty()) { - spdlog::error_s("Invalid country identifier - empty!"); - return false; - } - if (!valid_basic_identifier(identifier)) { - spdlog::error_s( - "Invalid country identifier: {} (can only contain alphanumeric characters and underscores)", identifier - ); - return false; - } - if (graphical_culture == nullptr) { - spdlog::error_s("Null graphical culture for country {}", identifier); - return false; - } - - static constexpr colour_t default_colour = colour_t::fill_as(colour_t::max_value); - - return country_definitions.emplace_item( - identifier, // - identifier, colour, CountryDefinition::index_t { get_country_definition_count() }, *graphical_culture, - std::move(parties), std::move(unit_names), dynamic_tag, std::move(alternative_colours), - /* Default to country colour for the chest and grey for the others. Update later if necessary. */ - colour, default_colour, default_colour - ); -} - -bool CountryDefinitionManager::load_countries( - DefinitionManager const& definition_manager, Dataloader const& dataloader, ast::NodeCPtr root -) { - static constexpr std::string_view common_dir = "common/"; - bool is_dynamic = false; - - const bool ret = expect_dictionary_reserve_length( - country_definitions, - [this, &definition_manager, &is_dynamic, &dataloader](std::string_view key, ast::NodeCPtr value) -> bool { - if (key == "dynamic_tags") { - return expect_bool([&is_dynamic](bool val) -> bool { - if (val == is_dynamic) { - spdlog::warn_s("Redundant \"is_dynamic\", already {}", val ? "true" : "false"); - } else { - if (is_dynamic) { - spdlog::warn_s("Changing \"is_dynamic\" back to false"); - } - is_dynamic = val; - } - return true; - })(value); - } - if (expect_string( - [this, &definition_manager, is_dynamic, &dataloader, &key](std::string_view filepath) -> bool { - if (load_country_data_file( - definition_manager, key, is_dynamic, - Dataloader::parse_defines( - dataloader.lookup_file(append_string_views(common_dir, filepath)) - ).get_file_node() - )) { - return true; - } - spdlog::critical_s("Failed to load country data file: {}", filepath); - return false; - } - )(value)) { - return true; - } - spdlog::critical_s("Failed to load country: {}", key); - return false; - } - )(root); - lock_country_definitions(); - return ret; -} - -bool CountryDefinitionManager::load_country_colours(ast::NodeCPtr root) { - return country_definitions.expect_item_dictionary_and_default( - [](std::string_view key, ast::NodeCPtr value) -> bool { - spdlog::warn_s("country_colors.txt references country tag {} which is not defined!", key); - return true; - }, - [](CountryDefinition& country, ast::NodeCPtr colour_node) -> bool { - return expect_dictionary_keys( - "color1", ONE_EXACTLY, expect_colour(assign_variable_callback(country.primary_unit_colour)), - "color2", ONE_EXACTLY, expect_colour(assign_variable_callback(country.secondary_unit_colour)), - "color3", ONE_EXACTLY, expect_colour(assign_variable_callback(country.tertiary_unit_colour)) - )(colour_node); - } - )(root); -} - -node_callback_t CountryDefinitionManager::load_country_party( - PoliticsManager const& politics_manager, IdentifierRegistry& country_parties -) const { - return [&politics_manager, &country_parties](ast::NodeCPtr value) -> bool { - std::string_view party_name; - Date start_date, end_date; - Ideology const* ideology = nullptr; - memory::FixedVector policies { - party_policy_group_index_t(politics_manager.get_issue_manager().get_party_policy_group_count()), - nullptr - }; - - bool ret = expect_dictionary_keys_and_default( - [&politics_manager, &policies, &party_name](std::string_view key, ast::NodeCPtr value) -> bool { - return politics_manager.get_issue_manager().expect_party_policy_group_str( - [&politics_manager, &policies, value, &party_name](PartyPolicyGroup const& party_policy_group) -> bool { - PartyPolicy const*& policy = policies[party_policy_group.index]; - - if (policy != nullptr) { - spdlog::error_s( - "Country party \"{}\" has duplicate entry for party policy group \"{}\"", - party_name, party_policy_group - ); - return false; - } - - return politics_manager.get_issue_manager().expect_party_policy_identifier( - [&party_policy_group, &policy](PartyPolicy const& party_policy) -> bool { - if (&party_policy.group == &party_policy_group) { - policy = &party_policy; - return true; - } - - // TODO - change this back to error/false once TGC no longer has this issue - spdlog::warn_s( - "Invalid party policy \"{}\", group is \"{}\" when \"{}\" was expected.", - party_policy, - party_policy.group, - party_policy_group - ); - return true; - } - )(value); - } - )(key); - }, - "name", ONE_EXACTLY, expect_string(assign_variable_callback(party_name)), - "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start_date)), - "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end_date)), - "ideology", ONE_EXACTLY, - politics_manager.get_ideology_manager().expect_ideology_identifier(assign_variable_callback_pointer(ideology)) - )(value); - - if (ideology == nullptr) { - spdlog::warn_s("Country party {} has no ideology, defaulting to nullptr / no ideology", party_name); - } - - ret &= country_parties.emplace_item( - party_name, - duplicate_warning_callback, - party_name, start_date, end_date, ideology, std::move(policies) - ); - - return ret; - }; -} - -bool CountryDefinitionManager::load_country_data_file( - DefinitionManager const& definition_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root -) { - colour_t colour; - GraphicalCultureType const* graphical_culture; - IdentifierRegistry parties { "country parties" }; - CountryDefinition::unit_names_map_t unit_names; - CountryDefinition::government_colour_map_t alternative_colours; - bool ret = expect_dictionary_keys_and_default( - [&definition_manager, &alternative_colours](std::string_view key, ast::NodeCPtr value) -> bool { - return definition_manager.get_politics_manager().get_government_type_manager().expect_government_type_str( - [&alternative_colours, value](GovernmentType const& government_type) -> bool { - return expect_colour(map_callback(alternative_colours, &government_type))(value); - } - )(key); - }, - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), - "graphical_culture", ONE_EXACTLY, - definition_manager.get_pop_manager().get_culture_manager().expect_graphical_culture_type_identifier( - assign_variable_callback_pointer(graphical_culture) - ), - "party", ZERO_OR_MORE, load_country_party(definition_manager.get_politics_manager(), parties), - "unit_names", ZERO_OR_ONE, - definition_manager.get_military_manager().get_unit_type_manager().expect_unit_type_dictionary_reserve_length( - unit_names, - [&unit_names](UnitType const& unit, ast::NodeCPtr value) -> bool { - return name_list_callback(map_callback(unit_names, &unit))(value); - } - ) - )(root); - - ret &= add_country( - name, colour, graphical_culture, std::move(parties), std::move(unit_names), is_dynamic, std::move(alternative_colours) - ); - return ret; -} - template struct fmt::formatter; diff --git a/src/openvic-simulation/country/CountryDefinition.hpp b/src/openvic-simulation/country/CountryDefinition.hpp index 47a07c250..244de5de6 100644 --- a/src/openvic-simulation/country/CountryDefinition.hpp +++ b/src/openvic-simulation/country/CountryDefinition.hpp @@ -2,8 +2,6 @@ #include -#include - #include "openvic-simulation/core/Typedefs.hpp" #include "openvic-simulation/country/CountryParty.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" @@ -14,18 +12,15 @@ namespace OpenVic { struct CountryDefinitionManager; - class Dataloader; - struct DefinitionManager; struct GraphicalCultureType; struct GovernmentType; - struct PoliticsManager; struct UnitType; /* Generic information about a TAG */ struct CountryDefinition : HasIdentifierAndColour, HasIndex { friend struct CountryDefinitionManager; - using unit_names_map_t = ordered_map; + using unit_names_map_t = ordered_map>; using government_colour_map_t = ordered_map; private: @@ -58,29 +53,6 @@ namespace OpenVic { // TODO - get_colour including alternative colours }; - - struct CountryDefinitionManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(country_definition); - - NodeTools::node_callback_t load_country_party( - PoliticsManager const& politics_manager, IdentifierRegistry& country_parties - ) const; - - public: - bool add_country( - std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, - IdentifierRegistry&& parties, CountryDefinition::unit_names_map_t&& unit_names, bool dynamic_tag, - CountryDefinition::government_colour_map_t&& alternative_colours - ); - - bool load_country_colours(ast::NodeCPtr root); - - bool load_countries(DefinitionManager const& definition_manager, Dataloader const& dataloader, ast::NodeCPtr root); - bool load_country_data_file( - DefinitionManager const& definition_manager, std::string_view name, bool is_dynamic, ast::NodeCPtr root - ); - }; } extern template struct fmt::formatter; diff --git a/src/openvic-simulation/country/CountryDefinitionManager.cpp b/src/openvic-simulation/country/CountryDefinitionManager.cpp new file mode 100644 index 000000000..4ef4a4c8d --- /dev/null +++ b/src/openvic-simulation/country/CountryDefinitionManager.cpp @@ -0,0 +1,218 @@ +#include "CountryDefinitionManager.hpp" + +#include + + + +#include "openvic-simulation/dataloader/Dataloader.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/DefinitionManager.hpp" +#include "openvic-simulation/politics/Government.hpp" +#include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/politics/PartyPolicy.hpp" +#include "openvic-simulation/population/Culture.hpp" +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/types/FixedVector.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/utility/Logger.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool CountryDefinitionManager::add_country( + std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, + IdentifierRegistry&& parties, CountryDefinition::unit_names_map_t&& unit_names, bool dynamic_tag, + CountryDefinition::government_colour_map_t&& alternative_colours +) { + if (identifier.empty()) { + spdlog::error_s("Invalid country identifier - empty!"); + return false; + } + if (!valid_basic_identifier(identifier)) { + spdlog::error_s( + "Invalid country identifier: {} (can only contain alphanumeric characters and underscores)", identifier + ); + return false; + } + if (graphical_culture == nullptr) { + spdlog::error_s("Null graphical culture for country {}", identifier); + return false; + } + + static constexpr colour_t default_colour = colour_t::fill_as(colour_t::max_value); + + return country_definitions.emplace_item( + identifier, // + identifier, colour, CountryDefinition::index_t { get_country_definition_count() }, *graphical_culture, + std::move(parties), std::move(unit_names), dynamic_tag, std::move(alternative_colours), + /* Default to country colour for the chest and grey for the others. Update later if necessary. */ + colour, default_colour, default_colour + ); +} + +bool CountryDefinitionManager::load_countries( + DefinitionManager const& definition_manager, Dataloader const& dataloader, ovdl::v2script::ast::Node const* root +) { + static constexpr std::string_view common_dir = "common/"; + bool is_dynamic = false; + + const bool ret = expect_dictionary_reserve_length( + country_definitions, + [this, &definition_manager, &is_dynamic, &dataloader](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + if (key == "dynamic_tags") { + return expect_bool([&is_dynamic](bool val) -> bool { + if (val == is_dynamic) { + spdlog::warn_s("Redundant \"is_dynamic\", already {}", val ? "true" : "false"); + } else { + if (is_dynamic) { + spdlog::warn_s("Changing \"is_dynamic\" back to false"); + } + is_dynamic = val; + } + return true; + })(value); + } + if (expect_string( + [this, &definition_manager, is_dynamic, &dataloader, &key](std::string_view filepath) -> bool { + if (load_country_data_file( + definition_manager, key, is_dynamic, + Dataloader::parse_defines( + dataloader.lookup_file(append_string_views(common_dir, filepath)) + ).get_file_node() + )) { + return true; + } + spdlog::critical_s("Failed to load country data file: {}", filepath); + return false; + } + )(value)) { + return true; + } + spdlog::critical_s("Failed to load country: {}", key); + return false; + } + )(root); + lock_country_definitions(); + return ret; +} + +bool CountryDefinitionManager::load_country_colours(ovdl::v2script::ast::Node const* root) { + return country_definitions.expect_item_dictionary_and_default( + [](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + spdlog::warn_s("country_colors.txt references country tag {} which is not defined!", key); + return true; + }, + [](CountryDefinition& country, ovdl::v2script::ast::Node const* colour_node) -> bool { + return expect_dictionary_keys( + "color1", ONE_EXACTLY, expect_colour(assign_variable_callback(country.primary_unit_colour)), + "color2", ONE_EXACTLY, expect_colour(assign_variable_callback(country.secondary_unit_colour)), + "color3", ONE_EXACTLY, expect_colour(assign_variable_callback(country.tertiary_unit_colour)) + )(colour_node); + } + )(root); +} + +NodeTools::node_callback_t CountryDefinitionManager::load_country_party( + PoliticsManager const& politics_manager, IdentifierRegistry& country_parties +) const { + return [&politics_manager, &country_parties](ovdl::v2script::ast::Node const* value) -> bool { + std::string_view party_name; + Date start_date, end_date; + Ideology const* ideology = nullptr; + memory::FixedVector policies { + party_policy_group_index_t(politics_manager.get_issue_manager().get_party_policy_group_count()), + nullptr + }; + + bool ret = expect_dictionary_keys_and_default( + [&politics_manager, &policies, &party_name](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + return politics_manager.get_issue_manager().expect_party_policy_group_str( + [&politics_manager, &policies, value, &party_name](PartyPolicyGroup const& party_policy_group) -> bool { + PartyPolicy const*& policy = policies[party_policy_group.index]; + + if (policy != nullptr) { + spdlog::error_s( + "Country party \"{}\" has duplicate entry for party policy group \"{}\"", + party_name, party_policy_group + ); + return false; + } + + return politics_manager.get_issue_manager().expect_party_policy_identifier( + [&party_policy_group, &policy](PartyPolicy const& party_policy) -> bool { + if (&party_policy.group == &party_policy_group) { + policy = &party_policy; + return true; + } + + // TODO - change this back to error/false once TGC no longer has this issue + spdlog::warn_s( + "Invalid party policy \"{}\", group is \"{}\" when \"{}\" was expected.", + party_policy, + party_policy.group, + party_policy_group + ); + return true; + } + )(value); + } + )(key); + }, + "name", ONE_EXACTLY, expect_string(assign_variable_callback(party_name)), + "start_date", ONE_EXACTLY, expect_date(assign_variable_callback(start_date)), + "end_date", ONE_EXACTLY, expect_date(assign_variable_callback(end_date)), + "ideology", ONE_EXACTLY, + politics_manager.get_ideology_manager().expect_ideology_identifier(assign_variable_callback_pointer(ideology)) + )(value); + + if (ideology == nullptr) { + spdlog::warn_s("Country party {} has no ideology, defaulting to nullptr / no ideology", party_name); + } + + ret &= country_parties.emplace_item( + party_name, + duplicate_warning_callback, + party_name, start_date, end_date, ideology, std::move(policies) + ); + + return ret; + }; +} + +bool CountryDefinitionManager::load_country_data_file( + DefinitionManager const& definition_manager, std::string_view name, bool is_dynamic, ovdl::v2script::ast::Node const* root +) { + colour_t colour; + GraphicalCultureType const* graphical_culture; + IdentifierRegistry parties { "country parties" }; + CountryDefinition::unit_names_map_t unit_names; + CountryDefinition::government_colour_map_t alternative_colours; + bool ret = expect_dictionary_keys_and_default( + [&definition_manager, &alternative_colours](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + return definition_manager.get_politics_manager().get_government_type_manager().expect_government_type_str( + [&alternative_colours, value](GovernmentType const& government_type) -> bool { + return expect_colour(map_callback(alternative_colours, &government_type))(value); + } + )(key); + }, + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "graphical_culture", ONE_EXACTLY, + definition_manager.get_pop_manager().get_culture_manager().expect_graphical_culture_type_identifier( + assign_variable_callback_pointer(graphical_culture) + ), + "party", ZERO_OR_MORE, load_country_party(definition_manager.get_politics_manager(), parties), + "unit_names", ZERO_OR_ONE, + definition_manager.get_military_manager().get_unit_type_manager().expect_unit_type_dictionary_reserve_length( + unit_names, + [&unit_names](UnitType const& unit, ovdl::v2script::ast::Node const* value) -> bool { + return name_list_callback(map_callback(unit_names, &unit))(value); + } + ) + )(root); + + ret &= add_country( + name, colour, graphical_culture, std::move(parties), std::move(unit_names), is_dynamic, std::move(alternative_colours) + ); + return ret; +} \ No newline at end of file diff --git a/src/openvic-simulation/country/CountryDefinitionManager.hpp b/src/openvic-simulation/country/CountryDefinitionManager.hpp new file mode 100644 index 000000000..53740f4e8 --- /dev/null +++ b/src/openvic-simulation/country/CountryDefinitionManager.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include "openvic-simulation/country/CountryDefinition.hpp" +#include "openvic-simulation/country/CountryParty.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + class Dataloader; + struct DefinitionManager; + struct PoliticsManager; + + struct CountryDefinitionManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(country_definition); + + NodeTools::node_callback_t load_country_party( + PoliticsManager const& politics_manager, IdentifierRegistry& country_parties + ) const; + + public: + bool add_country( + std::string_view identifier, colour_t colour, GraphicalCultureType const* graphical_culture, + IdentifierRegistry&& parties, CountryDefinition::unit_names_map_t&& unit_names, bool dynamic_tag, + CountryDefinition::government_colour_map_t&& alternative_colours + ); + + bool load_country_colours(ovdl::v2script::ast::Node const* root); + + bool load_countries(DefinitionManager const& definition_manager, Dataloader const& dataloader, ovdl::v2script::ast::Node const* root); + bool load_country_data_file( + DefinitionManager const& definition_manager, std::string_view name, bool is_dynamic, ovdl::v2script::ast::Node const* root + ); + }; +} diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 14bfe7a3d..9f8b9ca0e 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -20,14 +20,18 @@ #include "openvic-simulation/diplomacy/CountryRelation.hpp" #include "openvic-simulation/economy/BuildingLevel.hpp" #include "openvic-simulation/economy/BuildingType.hpp" +#include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/economy/GoodInstanceManager.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" #include "openvic-simulation/economy/trading/MarketInstance.hpp" #include "openvic-simulation/history/CountryHistory.hpp" #include "openvic-simulation/map/Crime.hpp" #include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/military/Leader.hpp" #include "openvic-simulation/military/UnitInstanceGroup.hpp" #include "openvic-simulation/military/UnitType.hpp" +#include "openvic-simulation/military/UnitTypeManager.hpp" #include "openvic-simulation/misc/GameRulesManager.hpp" #include "openvic-simulation/modifier/ModifierEffectCache.hpp" #include "openvic-simulation/modifier/StaticModifierCache.hpp" @@ -1541,9 +1545,9 @@ void CountryInstance::_update_technology(const Date today) { } daily_research_points += get_modifier_effect_value(*modifier_effect_cache.get_research_points()); - daily_research_points *= fixed_point_t::_1 + - get_modifier_effect_value(*modifier_effect_cache.get_research_points_modifier()) + - get_modifier_effect_value(*modifier_effect_cache.get_increase_research()); + daily_research_points *= fixed_point_t::_1 + + get_modifier_effect_value(*modifier_effect_cache.get_research_points_modifier()) + + get_modifier_effect_value(*modifier_effect_cache.get_increase_research()); if (daily_research_points.get_untracked() < 0) { daily_research_points.set(0); @@ -1692,8 +1696,8 @@ void CountryInstance::_update_military() { // Mobilisation calculations mobilisation_impact = get_modifier_effect_value(*modifier_effect_cache.get_mobilization_impact()); - mobilisation_economy_impact = get_modifier_effect_value(*modifier_effect_cache.get_mobilisation_economy_impact_tech()) + - get_modifier_effect_value(*modifier_effect_cache.get_mobilisation_economy_impact_country()); + mobilisation_economy_impact = get_modifier_effect_value(*modifier_effect_cache.get_mobilisation_economy_impact_tech()) + + get_modifier_effect_value(*modifier_effect_cache.get_mobilisation_economy_impact_country()); // TODO - use country_defines.get_min_mobilize_limit(); (wiki: "lowest maximum of brigades you can mobilize. (by default 3)") @@ -1712,9 +1716,9 @@ void CountryInstance::_update_military() { get_modifier_effect_value(*modifier_effect_cache.get_max_war_exhaustion()), fixed_point_t::_0 ); - organisation_regain = fixed_point_t::_1 + - get_modifier_effect_value(*modifier_effect_cache.get_org_regain()) + - get_modifier_effect_value(*modifier_effect_cache.get_morale_global()); + organisation_regain = fixed_point_t::_1 + + get_modifier_effect_value(*modifier_effect_cache.get_org_regain()) + + get_modifier_effect_value(*modifier_effect_cache.get_morale_global()); land_organisation = fixed_point_t::_1 + get_modifier_effect_value(*modifier_effect_cache.get_land_organisation()); naval_organisation = fixed_point_t::_1 + get_modifier_effect_value(*modifier_effect_cache.get_naval_organisation()); @@ -1730,8 +1734,8 @@ void CountryInstance::_update_military() { get_modifier_effect_value(*modifier_effect_cache.get_combat_width_additive()) ); dig_in_cap = get_modifier_effect_value(*modifier_effect_cache.get_dig_in_cap()).floor(); - military_tactics = military_defines.get_base_military_tactics() + - get_modifier_effect_value(*modifier_effect_cache.get_military_tactics()); + military_tactics = military_defines.get_base_military_tactics() + + get_modifier_effect_value(*modifier_effect_cache.get_military_tactics()); if (leadership_point_stockpile < 0) { leadership_point_stockpile = 0; @@ -1739,8 +1743,8 @@ void CountryInstance::_update_military() { create_leader_count = (leadership_point_stockpile / military_defines.get_leader_recruit_cost()).floor(); monthly_leadership_points += get_modifier_effect_value(*modifier_effect_cache.get_leadership()); - monthly_leadership_points *= fixed_point_t::_1 + - get_modifier_effect_value(*modifier_effect_cache.get_leadership_modifier()); + monthly_leadership_points *= fixed_point_t::_1 + + get_modifier_effect_value(*modifier_effect_cache.get_leadership_modifier()); if (monthly_leadership_points < 0) { monthly_leadership_points = 0; @@ -1782,7 +1786,7 @@ static constexpr Modifier const& get_country_status_static_effect( } } -void CountryInstance::update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache) { +void CountryInstance::update_modifier_sum_before_map(Date today, StaticModifierCache const& static_modifier_cache) { // Update sum of national modifiers modifier_sum.clear(); @@ -1856,15 +1860,23 @@ void CountryInstance::update_modifier_sum(Date today, StaticModifierCache const& // TODO - calculate stats for each unit type (locked and unlocked) } -void CountryInstance::contribute_province_modifier_sum(ModifierSum const& province_modifier_sum) { - modifier_sum.add_modifier_sum(province_modifier_sum); +void CountryInstance::update_modifier_sum_after_map(Date today) { + for (ProvinceInstance const* const province_ptr : controlled_provinces) { + if (OV_likely(province_ptr != nullptr)) { + modifier_sum.add_modifier_sum(province_ptr->get_modifier_sum()); + } + } +} + +void CountryInstance::make_room_for_province_modifier_sum(ModifierSum const& province_modifier_sum) { + modifier_sum.make_room_for(province_modifier_sum); } fixed_point_t CountryInstance::get_modifier_effect_value(ModifierEffect const& effect) const { return modifier_sum.get_modifier_effect_value(effect); } -void CountryInstance::update_gamestate(const Date today, MapInstance& map_instance) { +void CountryInstance::update_gamestate_after_map(const Date today) { if (is_civilised()) { civilisation_progress = 0; } else { @@ -1928,14 +1940,14 @@ void CountryInstance::update_gamestate(const Date today, MapInstance& map_instan province->set_connected_to_capital(false); province->set_is_overseas(province_definition.get_continent() != capital_continent); - for (ProvinceDefinition::adjacency_t const& adjacency : province_definition.get_adjacencies()) { - // TODO - should we limit based on adjacency type? Straits and impassable still work in game, - // and water provinces don't have an owner so they'll get caught by the later checks anyway. - CountryInstance* neighbour = map_instance.get_province_instance_by_definition(adjacency.get_to()).get_owner(); - if (neighbour != nullptr && neighbour != this) { - neighbouring_countries.insert(neighbour); - } - } + // for (ProvinceDefinition::adjacency_t const& adjacency : province_definition.get_adjacencies()) { + // // TODO - should we limit based on adjacency type? Straits and impassable still work in game, + // // and water provinces don't have an owner so they'll get caught by the later checks anyway. + // CountryInstance* neighbour = map_instance.get_province_instance_by_definition(adjacency.get_to()).get_owner(); + // if (neighbour != nullptr && neighbour != this) { + // neighbouring_countries.insert(neighbour); + // } + // } } if (occupied_provinces_proportion != 0) { @@ -1945,21 +1957,21 @@ void CountryInstance::update_gamestate(const Date today, MapInstance& map_instan if (capital != nullptr) { capital->set_connected_to_capital(true); - memory::vector> province_checklist { *capital }; + // memory::vector> province_checklist { *capital }; - for (size_t index = 0; index < province_checklist.size(); ++index) { - ProvinceInstance const& province = province_checklist[index]; + // for (size_t index = 0; index < province_checklist.size(); ++index) { + // ProvinceInstance const& province = province_checklist[index]; - for (ProvinceDefinition::adjacency_t const& adjacency : province.province_definition.get_adjacencies()) { - ProvinceInstance& adjacent_province = map_instance.get_province_instance_by_definition(adjacency.get_to()); + // for (ProvinceDefinition::adjacency_t const& adjacency : province.province_definition.get_adjacencies()) { + // ProvinceInstance& adjacent_province = map_instance.get_province_instance_by_definition(adjacency.get_to()); - if (adjacent_province.get_owner() == this && !adjacent_province.get_connected_to_capital()) { - adjacent_province.set_connected_to_capital(true); - adjacent_province.set_is_overseas(false); - province_checklist.emplace_back(adjacent_province); - } - } - } + // if (adjacent_province.get_owner() == this && !adjacent_province.get_connected_to_capital()) { + // adjacent_province.set_connected_to_capital(true); + // adjacent_province.set_is_overseas(false); + // province_checklist.emplace_back(adjacent_province); + // } + // } + // } } // Order of updates might need to be changed/functions split up to account for dependencies @@ -1977,8 +1989,8 @@ void CountryInstance::update_gamestate(const Date today, MapInstance& map_instan _update_politics(); _update_diplomacy(); - const CountryDefinition::government_colour_map_t::const_iterator it = - country_definition.get_alternative_colours().find(government_type.get_untracked()); + using const_it_t = typename CountryDefinition::government_colour_map_t::const_iterator; + const const_it_t it = country_definition.get_alternative_colours().find(government_type.get_untracked()); if (it != country_definition.get_alternative_colours().end()) { colour = it.value(); diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index c9404f043..7d44c2ca9 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -685,8 +685,9 @@ namespace OpenVic { bool update_rule_set(); public: - void update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache); - void contribute_province_modifier_sum(ModifierSum const& province_modifier_sum); + void update_modifier_sum_before_map(Date today, StaticModifierCache const& static_modifier_cache); + void update_modifier_sum_after_map(Date today); + void make_room_for_province_modifier_sum(ModifierSum const& province_modifier_sum); fixed_point_t get_modifier_effect_value(ModifierEffect const& effect) const; constexpr void for_each_contributing_modifier( ModifierEffect const& effect, ContributingModifierCallback auto callback @@ -694,7 +695,7 @@ namespace OpenVic { return modifier_sum.for_each_contributing_modifier(effect, std::move(callback)); } - void update_gamestate(const Date today, MapInstance& map_instance); + void update_gamestate_after_map(const Date today); void country_tick_before_map( TypedSpan reusable_goods_mask, forwardable_span< diff --git a/src/openvic-simulation/country/CountryInstanceManager.cpp b/src/openvic-simulation/country/CountryInstanceManager.cpp index 090745d07..09aba90de 100644 --- a/src/openvic-simulation/country/CountryInstanceManager.cpp +++ b/src/openvic-simulation/country/CountryInstanceManager.cpp @@ -4,6 +4,7 @@ #include #include "openvic-simulation/country/CountryDefinition.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/defines/CountryDefines.hpp" #include "openvic-simulation/DefinitionManager.hpp" #include "openvic-simulation/InstanceManager.hpp" @@ -197,8 +198,9 @@ CountryInstance const& CountryInstanceManager::get_country_instance_by_definitio } bool CountryInstanceManager::apply_history_to_countries(InstanceManager& instance_manager) { - CountryHistoryManager const& history_manager = - instance_manager.definition_manager.get_history_manager().get_country_manager(); + CountryHistoryManager const& history_manager = instance_manager.definition_manager + .get_history_manager() + .get_country_manager(); bool ret = true; @@ -292,16 +294,16 @@ bool CountryInstanceManager::apply_history_to_countries(InstanceManager& instanc return ret; } -void CountryInstanceManager::update_modifier_sums(const Date today, StaticModifierCache const& static_modifier_cache) { - for (CountryInstance& country : country_instances) { - country.update_modifier_sum(today, static_modifier_cache); - } +void CountryInstanceManager::update_modifier_sums_before_map() { + thread_pool.process(work_t::COUNTRY_UPDATE_MODIFIER_SUMS_BEFORE_MAP); } -void CountryInstanceManager::update_gamestate(const Date today, MapInstance& map_instance) { - for (CountryInstance& country : country_instances) { - country.update_gamestate(today, map_instance); - } +void CountryInstanceManager::update_modifier_sums_after_map() { + thread_pool.process(work_t::COUNTRY_UPDATE_MODIFIER_SUMS_AFTER_MAP); +} + +void CountryInstanceManager::update_gamestate_after_map(const Date today) { + thread_pool.process(work_t::COUNTRY_UPDATE_GAMESTATE_AFTER_MAP); // TODO - work out how to have ranking effects applied (e.g. static modifiers) applied at game start // we can't just move update_rankings to the top of this function as it will choose initial GPs based on @@ -311,10 +313,10 @@ void CountryInstanceManager::update_gamestate(const Date today, MapInstance& map } void CountryInstanceManager::country_manager_tick_before_map() { - thread_pool.process_country_ticks_before_map(); + thread_pool.process(work_t::COUNTRY_TICK_BEFORE_MAP); } void CountryInstanceManager::country_manager_tick_after_map() { - thread_pool.process_country_ticks_after_map(); + thread_pool.process(work_t::COUNTRY_TICK_AFTER_MAP); shared_country_values.update_costs(); } diff --git a/src/openvic-simulation/country/CountryInstanceManager.hpp b/src/openvic-simulation/country/CountryInstanceManager.hpp index 3a589642b..c0230b7b4 100644 --- a/src/openvic-simulation/country/CountryInstanceManager.hpp +++ b/src/openvic-simulation/country/CountryInstanceManager.hpp @@ -18,10 +18,8 @@ namespace OpenVic { struct CountryHistoryManager; struct GoodInstanceManager; struct InstanceManager; - struct MapInstance; struct PopsDefines; struct PopType; - struct StaticModifierCache; struct ThreadPool; struct CountryInstanceManager { @@ -71,8 +69,9 @@ namespace OpenVic { bool apply_history_to_countries(InstanceManager& instance_manager); - void update_modifier_sums(const Date today, StaticModifierCache const& static_modifier_cache); - void update_gamestate(const Date today, MapInstance& map_instance); + void update_modifier_sums_before_map(); + void update_modifier_sums_after_map(); + void update_gamestate_after_map(const Date today); void country_manager_tick_before_map(); void country_manager_tick_after_map(); }; diff --git a/src/openvic-simulation/country/SharedCountryValues.cpp b/src/openvic-simulation/country/SharedCountryValues.cpp index 39dc6e129..d7d5ca224 100644 --- a/src/openvic-simulation/country/SharedCountryValues.cpp +++ b/src/openvic-simulation/country/SharedCountryValues.cpp @@ -2,6 +2,7 @@ #include "openvic-simulation/defines/PopsDefines.hpp" #include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/economy/GoodInstanceManager.hpp" #include "openvic-simulation/military/UnitType.hpp" // IWYU pragma: keep for RegimentType size #include "openvic-simulation/population/PopNeedsMacro.hpp" #include "openvic-simulation/population/PopType.hpp" diff --git a/src/openvic-simulation/dataloader/Dataloader.cpp b/src/openvic-simulation/dataloader/Dataloader.cpp index 8fe7603e8..2888096cd 100644 --- a/src/openvic-simulation/dataloader/Dataloader.cpp +++ b/src/openvic-simulation/dataloader/Dataloader.cpp @@ -14,7 +14,10 @@ #include "openvic-simulation/core/string/Utility.hpp" #include "openvic-simulation/core/template/Concepts.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/DefinitionManager.hpp" +#include "openvic-simulation/history/Bookmark.hpp" +#include "openvic-simulation/history/HistoryManager.hpp" #include "openvic-simulation/interface/UI.hpp" #include "openvic-simulation/misc/GameRulesManager.hpp" #include "openvic-simulation/misc/SoundEffect.hpp" @@ -37,6 +40,8 @@ static fs::path ensure_forward_slash_path(std::string_view path) { #endif } +Dataloader::Dataloader() {} + bool Dataloader::set_roots(path_span_t new_roots, path_span_t new_replace_paths, bool warn_on_override) { if (!roots.empty()) { if (warn_on_override) { @@ -378,9 +383,10 @@ bool Dataloader::_load_pop_types(DefinitionManager& definition_manager) { bool ret = pop_manager.setup_stratas(); - GoodDefinitionManager const& good_definition_manager = - definition_manager.get_economy_manager().get_good_definition_manager(); - IdeologyManager const& ideology_manager = definition_manager.get_politics_manager().get_ideology_manager(); + EconomyManager const& economy_manager = definition_manager.get_economy_manager(); + GoodDefinitionManager const& good_definition_manager = economy_manager.get_good_definition_manager(); + PoliticsManager const& politics_manager = definition_manager.get_politics_manager(); + IdeologyManager const& ideology_manager = politics_manager.get_ideology_manager(); static constexpr std::string_view pop_type_directory = "poptypes"; @@ -585,8 +591,10 @@ bool Dataloader::_load_history(DefinitionManager& definition_manager, bool unuse DeploymentManager& deployment_manager = definition_manager.get_military_manager().get_deployment_manager(); static constexpr std::string_view country_history_directory = "history/countries"; - const path_vector_t country_history_files = - lookup_basic_identifier_prefixed_files_in_dir(country_history_directory, ".txt"); + const path_vector_t country_history_files = lookup_basic_identifier_prefixed_files_in_dir( + country_history_directory, + ".txt" + ); country_history_manager.reserve_more_country_histories(country_history_files.size()); deployment_manager.reserve_more_deployments(country_history_files.size()); @@ -597,8 +605,10 @@ bool Dataloader::_load_history(DefinitionManager& definition_manager, bool unuse const memory::string filename = file.stem().string(memory::string::allocator_type{}); const std::string_view country_id = extract_basic_identifier_prefix(filename); - CountryDefinition const* country = - definition_manager.get_country_definition_manager().get_country_definition_by_identifier(country_id); + CountryDefinitionManager const& country_definition_manager = definition_manager.get_country_definition_manager(); + CountryDefinition const* country = country_definition_manager.get_country_definition_by_identifier( + country_id + ); if (country == nullptr) { if (unused_history_file_warnings) { spdlog::warn_s("Found history file for non-existent country: {}", country_id); @@ -623,14 +633,17 @@ bool Dataloader::_load_history(DefinitionManager& definition_manager, bool unuse } } + HistoryManager& history_manager = definition_manager.get_history_manager(); { /* Province History */ ProvinceHistoryManager& province_history_manager = definition_manager.get_history_manager().get_province_manager(); MapDefinition const& map_definition = definition_manager.get_map_definition(); static constexpr std::string_view province_history_directory = "history/provinces"; - const path_vector_t province_history_files = - lookup_basic_identifier_prefixed_files_in_dir_recursive(province_history_directory, ".txt"); + const path_vector_t province_history_files = lookup_basic_identifier_prefixed_files_in_dir_recursive( + province_history_directory, + ".txt" + ); province_history_manager.reserve_more_province_histories(province_history_files.size()); @@ -660,8 +673,8 @@ bool Dataloader::_load_history(DefinitionManager& definition_manager, bool unuse static constexpr std::string_view pop_history_directory = "history/pops/"; const string_set_t pop_history_dirs = lookup_dirs_in_dir(pop_history_directory); - const Date last_bookmark_date = - definition_manager.get_history_manager().get_bookmark_manager().get_last_bookmark_date(); + BookmarkManager const& bookmark_manager = history_manager.get_bookmark_manager(); + const Date last_bookmark_date = bookmark_manager.get_last_bookmark_date(); for (memory::string const& dir : pop_history_dirs) { Date::from_chars_result result; @@ -692,8 +705,7 @@ bool Dataloader::_load_history(DefinitionManager& definition_manager, bool unuse { /* Diplomacy History */ - DiplomaticHistoryManager& diplomatic_history_manager = - definition_manager.get_history_manager().get_diplomacy_manager(); + DiplomaticHistoryManager& diplomatic_history_manager = history_manager.get_diplomacy_manager(); static constexpr std::string_view diplomacy_history_directory = "history/diplomacy"; diff --git a/src/openvic-simulation/dataloader/Dataloader.hpp b/src/openvic-simulation/dataloader/Dataloader.hpp index 9e433a9cb..2ae8fa9c2 100644 --- a/src/openvic-simulation/dataloader/Dataloader.hpp +++ b/src/openvic-simulation/dataloader/Dataloader.hpp @@ -11,7 +11,7 @@ #include "openvic-simulation/core/memory/Vector.hpp" #include "openvic-simulation/core/template/Concepts.hpp" #include "openvic-simulation/dataloader/ModManager.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" namespace OpenVic { namespace fs = std::filesystem; @@ -70,7 +70,7 @@ namespace OpenVic { void free_cache(); public: - constexpr Dataloader() {}; + Dataloader(); /// @brief Searches for the Victoria 2 install directory /// diff --git a/src/openvic-simulation/dataloader/ModManager.cpp b/src/openvic-simulation/dataloader/ModManager.cpp index 8a970883b..6caa348af 100644 --- a/src/openvic-simulation/dataloader/ModManager.cpp +++ b/src/openvic-simulation/dataloader/ModManager.cpp @@ -77,7 +77,7 @@ vector_ordered_set> Mod::generate_dependency_l ModManager::ModManager() {} -bool ModManager::load_mod_file(ast::NodeCPtr root) { +bool ModManager::load_mod_file(ovdl::v2script::ast::Node const* root) { std::string_view identifier; std::string_view path; std::optional user_dir; diff --git a/src/openvic-simulation/dataloader/ModManager.hpp b/src/openvic-simulation/dataloader/ModManager.hpp index 20a6837bc..85491913a 100644 --- a/src/openvic-simulation/dataloader/ModManager.hpp +++ b/src/openvic-simulation/dataloader/ModManager.hpp @@ -6,7 +6,7 @@ #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { @@ -47,7 +47,7 @@ namespace OpenVic { public: ModManager(); - bool load_mod_file(ast::NodeCPtr root); + bool load_mod_file(ovdl::v2script::ast::Node const* root); void set_loaded_mods(memory::vector>&& new_loaded_mods); size_t get_loaded_mod_count() const; }; diff --git a/src/openvic-simulation/dataloader/NodeCallbacks.hpp b/src/openvic-simulation/dataloader/NodeCallbacks.hpp new file mode 100644 index 000000000..8236ce9c9 --- /dev/null +++ b/src/openvic-simulation/dataloader/NodeCallbacks.hpp @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" + +namespace OpenVic { + namespace NodeTools { + template + using callback_t = fu2::function_base; + + using node_callback_t = callback_t; + + template + concept NodeCallback = Callback; + + template + concept KeyValueCallback = Callback; + + template + concept MapKeyValueCallback = Callback; + } +} \ No newline at end of file diff --git a/src/openvic-simulation/dataloader/NodeTools.cpp b/src/openvic-simulation/dataloader/NodeTools.cpp index a2ae665fc..faa57e164 100644 --- a/src/openvic-simulation/dataloader/NodeTools.cpp +++ b/src/openvic-simulation/dataloader/NodeTools.cpp @@ -15,6 +15,7 @@ #include #include +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" #include "openvic-simulation/core/ui/TextFormat.hpp" #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/fixed_point/String.hpp" @@ -29,7 +30,7 @@ using namespace OpenVic::NodeTools; template static NodeCallback auto _expect_type(Callback auto&& callback) { - return [callback = FWD(callback)](ast::NodeCPtr node) mutable -> bool { + return [callback = FWD(callback)](ovdl::v2script::ast::Node const* node) mutable -> bool { if (node != nullptr) { T const* cast_node = dryad::node_try_cast(node); if (cast_node != nullptr) { @@ -46,33 +47,33 @@ static NodeCallback auto _expect_type(Callback auto&& callback) { }; } -using _NodeIterator = typename decltype(std::declval()->children())::iterator; -using _NodeStatementRange = dryad::node_range<_NodeIterator, ast::Statement>; +using _NodeIterator = typename decltype(std::declval()->children())::iterator; +using _NodeStatementRange = dryad::node_range<_NodeIterator, ovdl::v2script::ast::Statement>; static NodeCallback auto _abstract_statement_node_callback(Callback<_NodeStatementRange> auto&& callback) { - return [callback = FWD(callback)](ast::NodeCPtr node) mutable -> bool { + return [callback = FWD(callback)](ovdl::v2script::ast::Node const* node) mutable -> bool { if (node != nullptr) { - if (auto const* file_tree = dryad::node_try_cast(node)) { + if (auto const* file_tree = dryad::node_try_cast(node)) { return callback(file_tree->statements()); } - if (auto const* list_value = dryad::node_try_cast(node)) { + if (auto const* list_value = dryad::node_try_cast(node)) { return callback(list_value->statements()); } spdlog::error_s( "Invalid node type {} when expecting {} or {}", - ast::get_type_name(node->kind()), type_name(), type_name() + ast::get_type_name(node->kind()), type_name(), type_name() ); } else { spdlog::error_s( "Null node when expecting {} or {}", - type_name(), type_name() + type_name(), type_name() ); } return false; }; } -template T> +template T> static Callback auto _abstract_symbol_node_callback(Callback> auto&& callback, bool allow_empty) { return [callback = FWD(callback), allow_empty](T const* node) mutable -> bool { if (allow_empty) { @@ -88,7 +89,7 @@ static Callback auto _abstract_symbol_node_callback(Callback T> +template T> static Callback auto _abstract_string_node_callback(Callback auto&& callback, bool allow_empty) { return _abstract_symbol_node_callback( [callback = FWD(callback)](ovdl::symbol symbol) mutable -> bool { @@ -98,49 +99,49 @@ static Callback auto _abstract_string_node_callback(Callback callback) { - return _expect_type(_abstract_string_node_callback(callback, false)); +NodeTools::node_callback_t NodeTools::expect_identifier(NodeTools::callback_t callback) { + return _expect_type(_abstract_string_node_callback(callback, false)); } static NodeCallback auto _expect_identifier(Callback> auto&& callback) { - return _expect_type(_abstract_symbol_node_callback(FWD(callback), false)); + return _expect_type(_abstract_symbol_node_callback(FWD(callback), false)); } -node_callback_t NodeTools::expect_string(callback_t callback, bool allow_empty) { - return _expect_type(_abstract_string_node_callback(callback, allow_empty)); +NodeTools::node_callback_t NodeTools::expect_string(NodeTools::callback_t callback, bool allow_empty) { + return _expect_type(_abstract_string_node_callback(callback, allow_empty)); } static NodeCallback auto _expect_string(Callback> auto&& callback, bool allow_empty) { - return _expect_type(_abstract_symbol_node_callback(FWD(callback), allow_empty)); + return _expect_type(_abstract_symbol_node_callback(FWD(callback), allow_empty)); } -node_callback_t NodeTools::expect_identifier_or_string(callback_t callback, bool allow_empty) { - return [callback, allow_empty](ast::NodeCPtr node) -> bool { +NodeTools::node_callback_t NodeTools::expect_identifier_or_string(NodeTools::callback_t callback, bool allow_empty) { + return [callback, allow_empty](ovdl::v2script::ast::Node const* node) -> bool { if (node != nullptr) { - auto const* cast_node = dryad::node_try_cast(node); + auto const* cast_node = dryad::node_try_cast(node); if (cast_node != nullptr) { - return _abstract_string_node_callback(FWD(callback), allow_empty)(cast_node); + return _abstract_string_node_callback(FWD(callback), allow_empty)(cast_node); } spdlog::error_s( "Invalid node type {} when expecting {} or {}", - ast::get_type_name(node->kind()), type_name(), type_name() + ast::get_type_name(node->kind()), type_name(), type_name() ); } else { spdlog::error_s( "Null node when expecting {} or {}", - type_name(), type_name() + type_name(), type_name() ); } return false; }; } -node_callback_t NodeTools::expect_bool(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_bool(NodeTools::callback_t callback) { static const case_insensitive_string_map_t bool_map { { "yes", true }, { "no", false } }; return expect_identifier(expect_mapped_string(bool_map, callback)); } -node_callback_t NodeTools::expect_int_bool(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_int_bool(NodeTools::callback_t callback) { return expect_uint64([callback](uint64_t num) mutable -> bool { if (num > 1) { spdlog::warn_s("Found int bool with value >1: {}", num); @@ -149,7 +150,7 @@ node_callback_t NodeTools::expect_int_bool(callback_t callback) { }); } -node_callback_t NodeTools::expect_int64(callback_t callback, int base) { +NodeTools::node_callback_t NodeTools::expect_int64(NodeTools::callback_t callback, int base) { return expect_identifier([callback, base](std::string_view identifier) mutable -> bool { int64_t val; std::from_chars_result result = string_to_int64(identifier, val, base); @@ -161,7 +162,7 @@ node_callback_t NodeTools::expect_int64(callback_t callback, int base) }); } -node_callback_t NodeTools::expect_uint64(callback_t callback, int base) { +NodeTools::node_callback_t NodeTools::expect_uint64(NodeTools::callback_t callback, int base) { return expect_identifier([callback, base](std::string_view identifier) mutable -> bool { uint64_t val; std::from_chars_result result = string_to_uint64(identifier, val, base); @@ -173,7 +174,7 @@ node_callback_t NodeTools::expect_uint64(callback_t callback, int base }); } -callback_t NodeTools::expect_fixed_point_str(callback_t callback) { +NodeTools::callback_t NodeTools::expect_fixed_point_str(NodeTools::callback_t callback) { return [callback](std::string_view identifier) mutable -> bool { bool successful = false; fixed_point_t val; @@ -186,12 +187,12 @@ callback_t NodeTools::expect_fixed_point_str(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_fixed_point(NodeTools::callback_t callback) { return expect_identifier(expect_fixed_point_str(callback)); } -node_callback_t NodeTools::expect_colour(callback_t callback) { - return [callback](ast::NodeCPtr node) mutable -> bool { +NodeTools::node_callback_t NodeTools::expect_colour(NodeTools::callback_t callback) { + return [callback](ovdl::v2script::ast::Node const* node) mutable -> bool { colour_t col = colour_t::null(); int32_t components = 0; bool ret = expect_list_of_length(3, expect_fixed_point( @@ -220,13 +221,13 @@ node_callback_t NodeTools::expect_colour(callback_t callback) { }; } -node_callback_t NodeTools::expect_colour_hex(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_colour_hex(NodeTools::callback_t callback) { return expect_uint([callback](colour_argb_t::integer_type val) mutable -> bool { return callback(colour_argb_t::from_argb(val)); }, 16); } -callback_t NodeTools::expect_date_str(callback_t callback) { +NodeTools::callback_t NodeTools::expect_date_str(NodeTools::callback_t callback) { return [callback](std::string_view identifier) mutable -> bool { Date::from_chars_result result; const Date date = Date::from_string_log(identifier, &result); @@ -238,39 +239,39 @@ callback_t NodeTools::expect_date_str(callback_t callbac }; } -node_callback_t NodeTools::expect_date(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_date(NodeTools::callback_t callback) { return expect_identifier(expect_date_str(callback)); } -node_callback_t NodeTools::expect_date_string(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_date_string(NodeTools::callback_t callback) { return expect_string(expect_date_str(callback)); } -node_callback_t NodeTools::expect_date_identifier_or_string(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_date_identifier_or_string(NodeTools::callback_t callback) { return expect_identifier_or_string(expect_date_str(callback)); } -node_callback_t NodeTools::expect_years(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_years(NodeTools::callback_t callback) { return expect_int([callback](Timespan::day_t val) mutable -> bool { return callback(Timespan::from_years(val)); }); } -node_callback_t NodeTools::expect_months(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_months(NodeTools::callback_t callback) { return expect_int([callback](Timespan::day_t val) mutable -> bool { return callback(Timespan::from_months(val)); }); } -node_callback_t NodeTools::expect_days(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_days(NodeTools::callback_t callback) { return expect_int([callback](Timespan::day_t val) mutable -> bool { return callback(Timespan::from_days(val)); }); } -template)> +template)> NodeCallback auto _expect_vec2(Callback> auto&& callback) { - return [callback = FWD(callback)](ast::NodeCPtr node) mutable -> bool { + return [callback = FWD(callback)](ovdl::v2script::ast::Node const* node) mutable -> bool { vec2_t vec; bool ret = expect_dictionary_keys( "x", ONE_EXACTLY, expect_func(assign_variable_callback(vec.x)), @@ -283,7 +284,7 @@ NodeCallback auto _expect_vec2(Callback> auto&& callback) { }; } -node_callback_t NodeTools::expect_text_format(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_text_format(NodeTools::callback_t callback) { using enum text_format_t; static const string_map_t format_map = { @@ -297,22 +298,22 @@ node_callback_t NodeTools::expect_text_format(callback_t callback return expect_identifier(expect_mapped_string(format_map, callback)); } -static node_callback_t _expect_int(callback_t callback) { +static NodeTools::node_callback_t _expect_int(NodeTools::callback_t callback) { return expect_int(callback); } -node_callback_t NodeTools::expect_ivec2(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_ivec2(NodeTools::callback_t callback) { return _expect_vec2(callback); } -node_callback_t NodeTools::expect_fvec2(callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_fvec2(NodeTools::callback_t callback) { return _expect_vec2(callback); } // seen in some gfx files, these vectors don't have x,y,z,w labels, so are loaded similarly to colours. -node_callback_t NodeTools::expect_fvec3(callback_t callback) { - return [callback](ast::NodeCPtr node) mutable -> bool { +NodeTools::node_callback_t NodeTools::expect_fvec3(NodeTools::callback_t callback) { + return [callback](ovdl::v2script::ast::Node const* node) mutable -> bool { fvec3_t vec; int32_t components = 0; bool ret = expect_list_of_length(3, expect_fixed_point( @@ -328,8 +329,8 @@ node_callback_t NodeTools::expect_fvec3(callback_t callback) { }; } -node_callback_t NodeTools::expect_fvec4(callback_t callback) { - return [callback](ast::NodeCPtr node) mutable -> bool { +NodeTools::node_callback_t NodeTools::expect_fvec4(NodeTools::callback_t callback) { + return [callback](ovdl::v2script::ast::Node const* node) mutable -> bool { fvec4_t vec; int32_t components = 0; bool ret = expect_list_of_length(4, expect_fixed_point( @@ -345,8 +346,8 @@ node_callback_t NodeTools::expect_fvec4(callback_t callback) { }; } -node_callback_t NodeTools::expect_assign(key_value_callback_t callback) { - return _expect_type([callback](ast::AssignStatement const* assign_node) mutable -> bool { +NodeTools::node_callback_t NodeTools::expect_assign(key_value_callback_t callback) { + return _expect_type([callback](ovdl::v2script::ast::AssignStatement const* assign_node) mutable -> bool { std::string_view left; bool ret = expect_identifier(assign_variable_callback(left))(assign_node->left()); if (ret) { @@ -361,7 +362,7 @@ node_callback_t NodeTools::expect_assign(key_value_callback_t callback) { }); } -node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callback, node_callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callback, NodeTools::node_callback_t callback) { return _abstract_statement_node_callback([length_callback, callback](_NodeStatementRange list) mutable -> bool { bool ret = true; size_t dist = ranges::distance(list); @@ -379,7 +380,7 @@ node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callb if (index >= size) { return ret; } - if (auto const* value = dryad::node_try_cast(sub_node)) { + if (auto const* value = dryad::node_try_cast(sub_node)) { ret &= callback(value->value()); continue; } @@ -389,8 +390,8 @@ node_callback_t NodeTools::expect_list_and_length(length_callback_t length_callb }); } -node_callback_t NodeTools::expect_list_of_length(size_t length, node_callback_t callback) { - return [length, callback](ast::NodeCPtr node) -> bool { +NodeTools::node_callback_t NodeTools::expect_list_of_length(size_t length, NodeTools::node_callback_t callback) { + return [length, callback](ovdl::v2script::ast::Node const* node) -> bool { bool size_ret = true; bool ret = expect_list_and_length( [length, &size_ret](size_t size) -> size_t { @@ -412,12 +413,12 @@ node_callback_t NodeTools::expect_list_of_length(size_t length, node_callback_t }; } -node_callback_t NodeTools::expect_list(node_callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_list(NodeTools::node_callback_t callback) { return expect_list_and_length(default_length_callback, callback); } -node_callback_t NodeTools::expect_length(callback_t callback) { - return [callback](ast::NodeCPtr node) mutable -> bool { +NodeTools::node_callback_t NodeTools::expect_length(NodeTools::callback_t callback) { + return [callback](ovdl::v2script::ast::Node const* node) mutable -> bool { bool size_ret = true; bool ret = expect_list_and_length( [callback, &size_ret](size_t size) mutable -> size_t { @@ -431,13 +432,13 @@ node_callback_t NodeTools::expect_length(callback_t callback) { } template -static node_callback_t _expect_key(Key key, NodeCallback auto&& callback, bool* key_found, bool allow_duplicates) { +static NodeTools::node_callback_t _expect_key(Key key, NodeCallback auto&& callback, bool* key_found, bool allow_duplicates) { if constexpr (std::same_as>) { if (!key) { if (key_found != nullptr) { *key_found = false; } - return [](ast::NodeCPtr) -> bool { + return [](ovdl::v2script::ast::Node const*) -> bool { spdlog::error_s("Failed to find expected interned key."); return false; }; @@ -455,8 +456,8 @@ static node_callback_t _expect_key(Key key, NodeCallback auto&& callback, bool* return _abstract_statement_node_callback([key, callback = FWD(callback), key_found, allow_duplicates](_NodeStatementRange list) mutable -> bool { bool ret = true; size_t keys_found = 0; - for (ast::Statement const* sub_node : list) { - auto const* assign_node = dryad::node_try_cast(sub_node); + for (ovdl::v2script::ast::Statement const* sub_node : list) { + auto const* assign_node = dryad::node_try_cast(sub_node); if (assign_node == nullptr) { continue; } @@ -503,26 +504,26 @@ static node_callback_t _expect_key(Key key, NodeCallback auto&& callback, bool* }); } -node_callback_t NodeTools::expect_key(std::string_view key, node_callback_t callback, bool* key_found, bool allow_duplicates) { +NodeTools::node_callback_t NodeTools::expect_key(std::string_view key, NodeTools::node_callback_t callback, bool* key_found, bool allow_duplicates) { return _expect_key(key, callback, key_found, allow_duplicates); } -node_callback_t -NodeTools::expect_key(ovdl::symbol key, node_callback_t callback, bool* key_found, bool allow_duplicates) { +NodeTools::node_callback_t +NodeTools::expect_key(ovdl::symbol key, NodeTools::node_callback_t callback, bool* key_found, bool allow_duplicates) { return _expect_key(key, callback, key_found, allow_duplicates); } -node_callback_t NodeTools::expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback) { return expect_list_and_length(length_callback, expect_assign(callback)); } -node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) { +NodeTools::node_callback_t NodeTools::expect_dictionary(key_value_callback_t callback) { return expect_dictionary_and_length(default_length_callback, callback); } -node_callback_t NodeTools::name_list_callback(callback_t callback) { - return [callback](ast::NodeCPtr node) mutable -> bool { - name_list_t list; +NodeTools::node_callback_t NodeTools::name_list_callback(NodeTools::callback_t&&> callback) { + return [callback](ovdl::v2script::ast::Node const* node) mutable -> bool { + memory::vector list; bool ret = expect_list_reserve_length( list, expect_identifier_or_string(vector_callback(list)) )(node); @@ -531,7 +532,7 @@ node_callback_t NodeTools::name_list_callback(callback_t callback }; } -std::ostream& OpenVic::operator<<(std::ostream& stream, name_list_t const& name_list) { +std::ostream& OpenVic::operator<<(std::ostream& stream, memory::vector const& name_list) { stream << '['; if (!name_list.empty()) { stream << name_list.front(); @@ -542,11 +543,11 @@ std::ostream& OpenVic::operator<<(std::ostream& stream, name_list_t const& name_ return stream << ']'; } -callback_t NodeTools::assign_variable_callback_string(memory::string& var) { +NodeTools::callback_t NodeTools::assign_variable_callback_string(memory::string& var) { return assign_variable_callback_cast(var); } -callback_t NodeTools::vector_callback_string(memory::vector& vec) { +NodeTools::callback_t NodeTools::vector_callback_string(memory::vector& vec) { return [&vec](std::string_view val) -> bool { vec.emplace_back(memory::string(val)); return true; diff --git a/src/openvic-simulation/dataloader/NodeTools.hpp b/src/openvic-simulation/dataloader/NodeTools.hpp index 343be7659..8018e0ded 100644 --- a/src/openvic-simulation/dataloader/NodeTools.hpp +++ b/src/openvic-simulation/dataloader/NodeTools.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include @@ -22,7 +21,9 @@ #include "openvic-simulation/core/memory/Vector.hpp" #include "openvic-simulation/core/string/Utility.hpp" #include "openvic-simulation/core/template/Concepts.hpp" +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" #include "openvic-simulation/core/ui/TextFormat.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/IndexedFlatMap.hpp" @@ -35,20 +36,17 @@ namespace OpenVic { namespace ast { - using namespace ovdl::v2script::ast; - using NodeCPtr = Node const*; - - static constexpr std::string_view get_type_name(NodeKind kind) { + static constexpr std::string_view get_type_name(ovdl::v2script::ast::NodeKind kind) { #ifdef _MSC_VER // type_name starts with "struct " using namespace std::string_view_literals; #define NODE_CASE(Node) \ - case Node: return OpenVic::type_name().substr("struct "sv.size()); + case Node: return OpenVic::type_name().substr("struct "sv.size()); #else #define NODE_CASE(Node) \ - case Node: return OpenVic::type_name(); + case Node: return OpenVic::type_name(); #endif switch (kind) { - using enum NodeKind; + using enum ovdl::v2script::ast::NodeKind; NODE_CASE(FileTree); NODE_CASE(IdentifierValue); NODE_CASE(StringValue); @@ -63,45 +61,19 @@ using namespace std::string_view_literals; #undef NODE_CASE } - struct name_list_t : memory::vector { - using base_type = memory::vector; - using base_type::base_type; - }; - std::ostream& operator<<(std::ostream& stream, name_list_t const& name_list); + std::ostream& operator<<(std::ostream& stream, memory::vector const& name_list); constexpr void reserve_more(reservable auto& t, size_t size) { t.reserve(t.size() + size); } namespace NodeTools { - template - concept Functor = requires(Fn&& fn, Args&&... args) { - { std::invoke(FWD(fn), FWD(args)...) } -> std::same_as; - }; - - template - concept FunctorConvertible = requires(Fn&& fn, Args&&... args) { - { std::invoke(FWD(fn), FWD(args)...) } -> std::convertible_to; - }; - - template - concept Callback = Functor; - - template - concept NodeCallback = Callback; - - template - concept KeyValueCallback = Callback; - - template - concept MapKeyValueCallback = Callback; - template constexpr static MapKeyValueCallback auto ignore_map(KeyValueCallback auto&& callback) { return [default_callback = MOV(callback)]( Map const& key_map, std::string_view key, - ast::NodeCPtr value + ovdl::v2script::ast::Node const* value ) mutable -> bool { return default_callback(key, value); }; @@ -110,29 +82,25 @@ using namespace std::string_view_literals; template concept LengthCallback = Functor; - template - using callback_t = fu2::function_base; - - using node_callback_t = callback_t; - constexpr bool success_callback(ast::NodeCPtr) { + constexpr bool success_callback(ovdl::v2script::ast::Node const*) { return true; } - using key_value_callback_t = callback_t; - constexpr bool key_value_success_callback(std::string_view, ast::NodeCPtr) { + using key_value_callback_t = NodeTools::callback_t; + constexpr bool key_value_success_callback(std::string_view, ovdl::v2script::ast::Node const*) { return true; } - inline bool key_value_warn_callback(std::string_view key, ast::NodeCPtr) { + inline bool key_value_warn_callback(std::string_view key, ovdl::v2script::ast::Node const*) { spdlog::warn_s("Invalid dictionary key: {}", key); return true; } - inline bool key_value_invalid_callback(std::string_view key, ast::NodeCPtr) { + inline bool key_value_invalid_callback(std::string_view key, ovdl::v2script::ast::Node const*) { spdlog::error_s("Invalid dictionary key: {}", key); return false; } template - inline bool map_key_value_invalid_callback(Map const& key_map, std::string_view key, ast::NodeCPtr) { + inline bool map_key_value_invalid_callback(Map const& key_map, std::string_view key, ovdl::v2script::ast::Node const*) { spdlog::error_s( "Invalid dictionary key \"{}\". Valid values are [{}]", key, string_join(key_map) @@ -141,7 +109,7 @@ using namespace std::string_view_literals; } template - inline bool map_key_value_ignore_invalid_callback(Map const& key_map, std::string_view key, ast::NodeCPtr) { + inline bool map_key_value_ignore_invalid_callback(Map const& key_map, std::string_view key, ovdl::v2script::ast::Node const*) { spdlog::warn_s( "Invalid dictionary key \"{}\" is ignored. Valid values are [{}]", key, string_join(key_map) @@ -149,21 +117,22 @@ using namespace std::string_view_literals; return true; } - node_callback_t expect_identifier(callback_t callback); - node_callback_t expect_string(callback_t callback, bool allow_empty = false); - node_callback_t expect_identifier_or_string(callback_t callback, bool allow_empty = false); + NodeTools::node_callback_t expect_identifier(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_string(NodeTools::callback_t callback, bool allow_empty = false); + NodeTools::node_callback_t expect_identifier_or_string(NodeTools::callback_t callback, bool allow_empty = false); - node_callback_t expect_bool(callback_t callback); - node_callback_t expect_int_bool(callback_t callback); + NodeTools::node_callback_t expect_bool(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_int_bool(NodeTools::callback_t callback); - node_callback_t expect_int64(callback_t callback, int base = 10); - node_callback_t expect_uint64(callback_t callback, int base = 10); + NodeTools::node_callback_t expect_int64(NodeTools::callback_t callback, int base = 10); + NodeTools::node_callback_t expect_uint64(NodeTools::callback_t callback, int base = 10); template - NodeCallback auto expect_int(callback_t& callback, int base = 10) { + NodeCallback auto expect_int(NodeTools::callback_t& callback, int base = 10) { return expect_int64([callback](int64_t val) mutable -> bool { - if (static_cast(std::numeric_limits::lowest()) <= val && - val <= static_cast(std::numeric_limits::max())) { + if (static_cast(std::numeric_limits::lowest()) <= val + && val <= static_cast(std::numeric_limits::max()) + ) { return callback(val); } spdlog::error_s( @@ -176,12 +145,12 @@ using namespace std::string_view_literals; }, base); } template - NodeCallback auto expect_int(callback_t&& callback, int base = 10) { + NodeCallback auto expect_int(NodeTools::callback_t&& callback, int base = 10) { return expect_int(callback, base); } template - NodeCallback auto expect_uint(callback_t& callback, int base = 10) { + NodeCallback auto expect_uint(NodeTools::callback_t& callback, int base = 10) { return expect_uint64([callback](uint64_t val) mutable -> bool { if (val <= static_cast(std::numeric_limits::max())) { return callback(val); @@ -194,12 +163,12 @@ using namespace std::string_view_literals; }, base); } template - NodeCallback auto expect_uint(callback_t&& callback, int base = 10) { + NodeCallback auto expect_uint(NodeTools::callback_t&& callback, int base = 10) { return expect_uint(callback, base); } template T, typename AsT = type_safe::underlying_type> - NodeCallback auto expect_strong_typedef(callback_t& callback, int base = 10) { + NodeCallback auto expect_strong_typedef(NodeTools::callback_t& callback, int base = 10) { if constexpr (std::unsigned_integral) { return expect_uint64( [callback](uint64_t val) mutable -> bool { @@ -233,64 +202,64 @@ using namespace std::string_view_literals; } } template T, typename AsT = type_safe::underlying_type> - NodeCallback auto expect_strong_typedef(callback_t&& callback, int base = 10) { + NodeCallback auto expect_strong_typedef(NodeTools::callback_t&& callback, int base = 10) { return expect_strong_typedef(callback, base); } template T> requires std::unsigned_integral> - NodeCallback auto expect_index(callback_t& callback, int base = 10) { + NodeCallback auto expect_index(NodeTools::callback_t& callback, int base = 10) { return expect_strong_typedef(callback, base); } template T> requires std::unsigned_integral> - NodeCallback auto expect_index(callback_t&& callback, int base = 10) { + NodeCallback auto expect_index(NodeTools::callback_t&& callback, int base = 10) { return expect_index(callback, base); } - callback_t expect_fixed_point_str(callback_t callback); - node_callback_t expect_fixed_point(callback_t callback); + NodeTools::callback_t expect_fixed_point_str(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_fixed_point(NodeTools::callback_t callback); /* Expect a list of 3 base 10 values, each either in the range [0, 1] or (1, 255], representing RGB components. */ - node_callback_t expect_colour(callback_t callback); + NodeTools::node_callback_t expect_colour(NodeTools::callback_t callback); /* Expect a hexadecimal value representing a colour in ARGB format. */ - node_callback_t expect_colour_hex(callback_t callback); + NodeTools::node_callback_t expect_colour_hex(NodeTools::callback_t callback); - node_callback_t expect_text_format(callback_t callback); + NodeTools::node_callback_t expect_text_format(NodeTools::callback_t callback); - callback_t expect_date_str(callback_t callback); - node_callback_t expect_date(callback_t callback); - node_callback_t expect_date_string(callback_t callback); - node_callback_t expect_date_identifier_or_string(callback_t callback); - node_callback_t expect_years(callback_t callback); - node_callback_t expect_months(callback_t callback); - node_callback_t expect_days(callback_t callback); + NodeTools::callback_t expect_date_str(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_date(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_date_string(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_date_identifier_or_string(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_years(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_months(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_days(NodeTools::callback_t callback); - node_callback_t expect_ivec2(callback_t callback); - node_callback_t expect_fvec2(callback_t callback); - node_callback_t expect_fvec3(callback_t callback); - node_callback_t expect_fvec4(callback_t callback); - node_callback_t expect_assign(key_value_callback_t callback); + NodeTools::node_callback_t expect_ivec2(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_fvec2(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_fvec3(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_fvec4(NodeTools::callback_t callback); + NodeTools::node_callback_t expect_assign(key_value_callback_t callback); using length_callback_t = fu2::function; constexpr size_t default_length_callback(size_t size) { return size; }; - node_callback_t expect_list_and_length(length_callback_t length_callback, node_callback_t callback); - node_callback_t expect_list_of_length(size_t length, node_callback_t callback); - node_callback_t expect_list(node_callback_t callback); - node_callback_t expect_length(callback_t callback); + NodeTools::node_callback_t expect_list_and_length(length_callback_t length_callback, NodeTools::node_callback_t callback); + NodeTools::node_callback_t expect_list_of_length(size_t length, NodeTools::node_callback_t callback); + NodeTools::node_callback_t expect_list(NodeTools::node_callback_t callback); + NodeTools::node_callback_t expect_length(NodeTools::callback_t callback); - node_callback_t expect_key( - ovdl::symbol key, node_callback_t callback, bool* key_found = nullptr, bool allow_duplicates = false + NodeTools::node_callback_t expect_key( + ovdl::symbol key, NodeTools::node_callback_t callback, bool* key_found = nullptr, bool allow_duplicates = false ); - node_callback_t expect_key( - std::string_view key, node_callback_t callback, bool* key_found = nullptr, bool allow_duplicates = false + NodeTools::node_callback_t expect_key( + std::string_view key, NodeTools::node_callback_t callback, bool* key_found = nullptr, bool allow_duplicates = false ); - node_callback_t expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback); - node_callback_t expect_dictionary(key_value_callback_t callback); + NodeTools::node_callback_t expect_dictionary_and_length(length_callback_t length_callback, key_value_callback_t callback); + NodeTools::node_callback_t expect_dictionary(key_value_callback_t callback); struct dictionary_entry_t { enum class expected_count_t : uint8_t { @@ -302,10 +271,10 @@ using namespace std::string_view_literals; ZERO_OR_MORE = _CAN_REPEAT, ONE_OR_MORE = _MUST_APPEAR | _CAN_REPEAT } expected_count; - node_callback_t callback; + NodeTools::node_callback_t callback; size_t count = 0; - dictionary_entry_t(expected_count_t new_expected_count, node_callback_t&& new_callback) + dictionary_entry_t(expected_count_t new_expected_count, NodeTools::node_callback_t&& new_callback) : expected_count { new_expected_count }, callback { MOV(new_callback) } {} constexpr bool must_appear() const { @@ -349,7 +318,7 @@ using namespace std::string_view_literals; KeyValueCallback auto dictionary_keys_callback( Map&& key_map, MapKeyValueCallback auto&& default_callback ) { - return [&key_map, default_callback = FWD(default_callback)](std::string_view key, ast::NodeCPtr value) mutable -> bool { + return [&key_map, default_callback = FWD(default_callback)](std::string_view key, ovdl::v2script::ast::Node const* value) mutable -> bool { typename std::remove_reference_t::iterator it = key_map.find(key); if (it == key_map.end()) { return default_callback(key_map, key, value); @@ -402,7 +371,7 @@ using namespace std::string_view_literals; Map&& key_map, LengthCallback auto&& length_callback, MapKeyValueCallback auto&& default_callback ) { return [length_callback = FWD(length_callback), default_callback = FWD(default_callback), key_map = MOV(key_map)]( - ast::NodeCPtr node + ovdl::v2script::ast::Node const* node ) mutable -> bool { bool ret = expect_dictionary_and_length( FWD(length_callback), dictionary_keys_callback(key_map, FWD(default_callback)) @@ -568,7 +537,7 @@ using namespace std::string_view_literals; return expect_dictionary_keys_and_length(reserve_length_callback(reservable), FWD(args)...); } - node_callback_t name_list_callback(callback_t callback); + NodeTools::node_callback_t name_list_callback(NodeTools::callback_t&&> callback); template Callback auto expect_mapped_string( @@ -598,7 +567,7 @@ using namespace std::string_view_literals; template requires std::is_integral_v || std::is_enum_v - callback_t assign_variable_callback_cast(auto& var) { + NodeTools::callback_t assign_variable_callback_cast(auto& var) { return [&var](T val) -> bool { var = val; return true; @@ -642,7 +611,7 @@ using namespace std::string_view_literals; }; } - callback_t assign_variable_callback_string(memory::string& var); + NodeTools::callback_t assign_variable_callback_string(memory::string& var); template Callback auto move_variable_callback(T& var) { @@ -664,7 +633,7 @@ using namespace std::string_view_literals; template requires requires(T& t) { t++; } KeyValueCallback auto increment_callback(T& var) { - return [&var](std::string_view, ast::NodeCPtr) -> bool { + return [&var](std::string_view, ovdl::v2script::ast::Node const*) -> bool { var++; return true; }; @@ -731,7 +700,7 @@ using namespace std::string_view_literals; }; } - callback_t vector_callback_string(memory::vector& vec); + NodeTools::callback_t vector_callback_string(memory::vector& vec); template Callback auto set_callback(tsl::ordered_set& set, bool warn = false) { diff --git a/src/openvic-simulation/dataloader/Node_forwarded.hpp b/src/openvic-simulation/dataloader/Node_forwarded.hpp new file mode 100644 index 000000000..b7ac682fc --- /dev/null +++ b/src/openvic-simulation/dataloader/Node_forwarded.hpp @@ -0,0 +1,10 @@ +#pragma once + +namespace dryad { + template class node; +} + +namespace ovdl::v2script::ast { + enum class NodeKind; + using Node = dryad::node; +} \ No newline at end of file diff --git a/src/openvic-simulation/dataloader/Vic2PathSearch.cpp b/src/openvic-simulation/dataloader/Vic2PathSearch.cpp index 3b169d6ea..279a80edb 100644 --- a/src/openvic-simulation/dataloader/Vic2PathSearch.cpp +++ b/src/openvic-simulation/dataloader/Vic2PathSearch.cpp @@ -130,8 +130,11 @@ static fs::path _search_for_game_path(fs::path hint_path) { if (hint_empty) { #if defined(_WIN32) - static const fs::path registry_path = - Windows::ReadRegValue(HKEY_LOCAL_MACHINE, "SOFTWARE\\WOW6432Node\\Paradox Interactive\\Victoria 2", "path"); + static const fs::path registry_path = Windows::ReadRegValue( + HKEY_LOCAL_MACHINE, + "SOFTWARE\\WOW6432Node\\Paradox Interactive\\Victoria 2", + "path" + ); if (!registry_path.empty()) { return registry_path; @@ -214,8 +217,12 @@ static fs::path _search_for_game_path(fs::path hint_path) { bool vic2_install_confirmed = false; // if current_path is not a regular file, this is a non-default Steam Library, skip this parser evaluation - if (fs::is_regular_file(current_path, error_code) && - (is_libraryfolders_vdf || filename_equals(libraryfolders, current_path))) { + if (fs::is_regular_file(current_path, error_code) + && ( + is_libraryfolders_vdf + || filename_equals(libraryfolders, current_path) + ) + ) { lexy_vdf::Parser parser; memory::string buffer; @@ -349,13 +356,20 @@ static fs::path _search_for_game_path(fs::path hint_path) { } bool is_Victoria_2_folder = false; - if ((is_common_folder || filename_equals(common_folder, vic2_steam_lib_directory)) && - fs::is_directory(vic2_steam_lib_directory, error_code)) { + if (( + is_common_folder + || filename_equals(common_folder, vic2_steam_lib_directory) + ) && fs::is_directory(vic2_steam_lib_directory, error_code) + ) { vic2_steam_lib_directory /= Victoria_2_folder; is_Victoria_2_folder = true; } - if ((is_Victoria_2_folder || filename_equals(Victoria_2_folder, vic2_steam_lib_directory)) && - fs::is_regular_file(vic2_steam_lib_directory / v2_game_exe, error_code)) { + + if (( + is_Victoria_2_folder + || filename_equals(Victoria_2_folder, vic2_steam_lib_directory) + ) && fs::is_regular_file(vic2_steam_lib_directory / v2_game_exe, error_code) + ) { return vic2_steam_lib_directory; } diff --git a/src/openvic-simulation/dataloader/Vic2PathSearch_Windows.hpp b/src/openvic-simulation/dataloader/Vic2PathSearch_Windows.hpp index f84d8002a..b3fc80d0e 100644 --- a/src/openvic-simulation/dataloader/Vic2PathSearch_Windows.hpp +++ b/src/openvic-simulation/dataloader/Vic2PathSearch_Windows.hpp @@ -88,11 +88,11 @@ namespace OpenVic::Windows { } bool is_predefined() const { - return (_key_handle == HKEY_CURRENT_USER) || (_key_handle == HKEY_LOCAL_MACHINE) || - (_key_handle == HKEY_CLASSES_ROOT) || (_key_handle == HKEY_CURRENT_CONFIG) || - (_key_handle == HKEY_CURRENT_USER_LOCAL_SETTINGS) || (_key_handle == HKEY_PERFORMANCE_DATA) || - (_key_handle == HKEY_PERFORMANCE_NLSTEXT) || (_key_handle == HKEY_PERFORMANCE_TEXT) || - (_key_handle == HKEY_USERS); + return (_key_handle == HKEY_CURRENT_USER) || (_key_handle == HKEY_LOCAL_MACHINE) + || (_key_handle == HKEY_CLASSES_ROOT) || (_key_handle == HKEY_CURRENT_CONFIG) + || (_key_handle == HKEY_CURRENT_USER_LOCAL_SETTINGS) || (_key_handle == HKEY_PERFORMANCE_DATA) + || (_key_handle == HKEY_PERFORMANCE_NLSTEXT) || (_key_handle == HKEY_PERFORMANCE_TEXT) + || (_key_handle == HKEY_USERS); } LSTATUS close_key() { diff --git a/src/openvic-simulation/defines/AIDefines.cpp b/src/openvic-simulation/defines/AIDefines.cpp index c57e3d00d..bf7cbc071 100644 --- a/src/openvic-simulation/defines/AIDefines.cpp +++ b/src/openvic-simulation/defines/AIDefines.cpp @@ -1,5 +1,7 @@ #include "AIDefines.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -9,7 +11,7 @@ std::string_view AIDefines::get_name() const { return "ai"; } -node_callback_t AIDefines::expect_defines() { +NodeTools::node_callback_t AIDefines::expect_defines() { return expect_dictionary_keys( "COLONY_WEIGHT", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(colony_weight)), "ADMINISTRATOR_WEIGHT", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(administrator_weight)), diff --git a/src/openvic-simulation/defines/AIDefines.hpp b/src/openvic-simulation/defines/AIDefines.hpp index 5cbc41f53..d6ec6c550 100644 --- a/src/openvic-simulation/defines/AIDefines.hpp +++ b/src/openvic-simulation/defines/AIDefines.hpp @@ -1,6 +1,7 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/utility/Getters.hpp" diff --git a/src/openvic-simulation/defines/CountryDefines.cpp b/src/openvic-simulation/defines/CountryDefines.cpp index fe28cf070..3cbeea88c 100644 --- a/src/openvic-simulation/defines/CountryDefines.cpp +++ b/src/openvic-simulation/defines/CountryDefines.cpp @@ -1,5 +1,7 @@ #include "CountryDefines.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -9,7 +11,7 @@ std::string_view CountryDefines::get_name() const { return "country"; } -node_callback_t CountryDefines::expect_defines() { +NodeTools::node_callback_t CountryDefines::expect_defines() { return expect_dictionary_keys( "YEARS_OF_NATIONALISM", ONE_EXACTLY, expect_years(assign_variable_callback(nationalism_duration)), "MONTHS_UNTIL_BROKEN", ZERO_OR_ONE, // NOT USED diff --git a/src/openvic-simulation/defines/CountryDefines.hpp b/src/openvic-simulation/defines/CountryDefines.hpp index 2bff371ad..2a0d380bd 100644 --- a/src/openvic-simulation/defines/CountryDefines.hpp +++ b/src/openvic-simulation/defines/CountryDefines.hpp @@ -2,7 +2,7 @@ #include -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/map/LifeRating.hpp" diff --git a/src/openvic-simulation/defines/Define.cpp b/src/openvic-simulation/defines/Define.cpp index 897b60e6d..1ecf0bf48 100644 --- a/src/openvic-simulation/defines/Define.cpp +++ b/src/openvic-simulation/defines/Define.cpp @@ -7,7 +7,7 @@ using namespace OpenVic::NodeTools; DefineManager::DefineManager() {} -bool DefineManager::load_defines_file(ast::NodeCPtr root) { +bool DefineManager::load_defines_file(ovdl::v2script::ast::Node const* root) { bool ret = expect_dictionary_keys( "defines", ONE_EXACTLY, expect_dictionary_keys( "start_date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(start_date)), diff --git a/src/openvic-simulation/defines/Define.hpp b/src/openvic-simulation/defines/Define.hpp index e1b2bc2f2..b17fd216a 100644 --- a/src/openvic-simulation/defines/Define.hpp +++ b/src/openvic-simulation/defines/Define.hpp @@ -33,6 +33,6 @@ namespace OpenVic { return date.in_range(start_date, end_date); } - bool load_defines_file(ast::NodeCPtr root); + bool load_defines_file(ovdl::v2script::ast::Node const* root); }; } diff --git a/src/openvic-simulation/defines/DiplomacyDefines.cpp b/src/openvic-simulation/defines/DiplomacyDefines.cpp index 15c148b45..07a242642 100644 --- a/src/openvic-simulation/defines/DiplomacyDefines.cpp +++ b/src/openvic-simulation/defines/DiplomacyDefines.cpp @@ -1,5 +1,7 @@ #include "DiplomacyDefines.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -9,7 +11,7 @@ std::string_view DiplomacyDefines::get_name() const { return "diplomacy"; } -node_callback_t DiplomacyDefines::expect_defines() { +NodeTools::node_callback_t DiplomacyDefines::expect_defines() { // The key map entries are added in two separate function calls, half each time, as adding them all at once causes a // runtime crash, presumably due to a limit on the number of loose lambda functions that can exist at the same time. diff --git a/src/openvic-simulation/defines/DiplomacyDefines.hpp b/src/openvic-simulation/defines/DiplomacyDefines.hpp index 07587a29b..34393a31e 100644 --- a/src/openvic-simulation/defines/DiplomacyDefines.hpp +++ b/src/openvic-simulation/defines/DiplomacyDefines.hpp @@ -1,6 +1,7 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/utility/Getters.hpp" diff --git a/src/openvic-simulation/defines/EconomyDefines.cpp b/src/openvic-simulation/defines/EconomyDefines.cpp index 0ae597d97..5caf3cdbf 100644 --- a/src/openvic-simulation/defines/EconomyDefines.cpp +++ b/src/openvic-simulation/defines/EconomyDefines.cpp @@ -1,5 +1,7 @@ #include "EconomyDefines.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -9,7 +11,7 @@ std::string_view EconomyDefines::get_name() const { return "economy"; } -node_callback_t EconomyDefines::expect_defines() { +NodeTools::node_callback_t EconomyDefines::expect_defines() { return expect_dictionary_keys( "MAX_DAILY_RESEARCH", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(max_daily_research)), "LOAN_BASE_INTEREST", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(loan_base_interest)), diff --git a/src/openvic-simulation/defines/EconomyDefines.hpp b/src/openvic-simulation/defines/EconomyDefines.hpp index 1da082fed..1588b2e41 100644 --- a/src/openvic-simulation/defines/EconomyDefines.hpp +++ b/src/openvic-simulation/defines/EconomyDefines.hpp @@ -1,6 +1,7 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/utility/Getters.hpp" diff --git a/src/openvic-simulation/defines/GraphicsDefines.cpp b/src/openvic-simulation/defines/GraphicsDefines.cpp index adaf8dce0..f8f5b49b3 100644 --- a/src/openvic-simulation/defines/GraphicsDefines.cpp +++ b/src/openvic-simulation/defines/GraphicsDefines.cpp @@ -1,5 +1,7 @@ #include "GraphicsDefines.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -9,7 +11,7 @@ std::string_view GraphicsDefines::get_name() const { return "graphics"; } -node_callback_t GraphicsDefines::expect_defines() { +NodeTools::node_callback_t GraphicsDefines::expect_defines() { return expect_dictionary_keys( "CITIES_SPRAWL_OFFSET", ONE_EXACTLY, expect_uint(assign_variable_callback(cities_sprawl_offset)), "CITIES_SPRAWL_WIDTH", ONE_EXACTLY, expect_uint(assign_variable_callback(cities_sprawl_width)), diff --git a/src/openvic-simulation/defines/GraphicsDefines.hpp b/src/openvic-simulation/defines/GraphicsDefines.hpp index 6f8218771..31a11cddc 100644 --- a/src/openvic-simulation/defines/GraphicsDefines.hpp +++ b/src/openvic-simulation/defines/GraphicsDefines.hpp @@ -1,6 +1,6 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { diff --git a/src/openvic-simulation/defines/MilitaryDefines.cpp b/src/openvic-simulation/defines/MilitaryDefines.cpp index 38037be25..9dcb974dd 100644 --- a/src/openvic-simulation/defines/MilitaryDefines.cpp +++ b/src/openvic-simulation/defines/MilitaryDefines.cpp @@ -2,6 +2,7 @@ #include +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/military/CombatWidth.hpp" using namespace OpenVic; @@ -13,7 +14,7 @@ std::string_view MilitaryDefines::get_name() const { return "military"; } -node_callback_t MilitaryDefines::expect_defines() { +NodeTools::node_callback_t MilitaryDefines::expect_defines() { return expect_dictionary_keys( "DIG_IN_INCREASE_EACH_DAYS", ONE_EXACTLY, expect_days(assign_variable_callback(dig_in_increase_each_days)), "REINFORCE_SPEED", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(reinforce_speed)), diff --git a/src/openvic-simulation/defines/MilitaryDefines.hpp b/src/openvic-simulation/defines/MilitaryDefines.hpp index c104893c0..f866fd1f1 100644 --- a/src/openvic-simulation/defines/MilitaryDefines.hpp +++ b/src/openvic-simulation/defines/MilitaryDefines.hpp @@ -1,6 +1,6 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/military/CombatWidth.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" diff --git a/src/openvic-simulation/defines/PopsDefines.cpp b/src/openvic-simulation/defines/PopsDefines.cpp index f28258985..efbf59510 100644 --- a/src/openvic-simulation/defines/PopsDefines.cpp +++ b/src/openvic-simulation/defines/PopsDefines.cpp @@ -1,5 +1,7 @@ #include "PopsDefines.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -9,7 +11,7 @@ std::string_view PopsDefines::get_name() const { return "pops"; } -node_callback_t PopsDefines::expect_defines() { +NodeTools::node_callback_t PopsDefines::expect_defines() { return expect_dictionary_keys( "BASE_CLERGY_FOR_LITERACY", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base_clergy_for_literacy)), "MAX_CLERGY_FOR_LITERACY", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(max_clergy_for_literacy)), diff --git a/src/openvic-simulation/defines/PopsDefines.hpp b/src/openvic-simulation/defines/PopsDefines.hpp index 631ae7892..aa4ea54aa 100644 --- a/src/openvic-simulation/defines/PopsDefines.hpp +++ b/src/openvic-simulation/defines/PopsDefines.hpp @@ -1,6 +1,7 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/population/PopSum.hpp" #include "openvic-simulation/map/LifeRating.hpp" diff --git a/src/openvic-simulation/diplomacy/CountryRelation.hpp b/src/openvic-simulation/diplomacy/CountryRelation.hpp index 6780ae663..364adbbe8 100644 --- a/src/openvic-simulation/diplomacy/CountryRelation.hpp +++ b/src/openvic-simulation/diplomacy/CountryRelation.hpp @@ -222,8 +222,10 @@ namespace OpenVic { return lhs; } - friend inline constexpr influence_value_type& - operator%=(influence_value_type& lhs, influence_value_type const& rhs) { + friend inline constexpr influence_value_type& operator%=( + influence_value_type& lhs, + influence_value_type const& rhs + ) { return lhs %= rhs.value; } diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp index 92f7ea98c..baa9e7c27 100644 --- a/src/openvic-simulation/diplomacy/DiplomaticAction.cpp +++ b/src/openvic-simulation/diplomacy/DiplomaticAction.cpp @@ -1,15 +1,5 @@ #include "DiplomaticAction.hpp" -#include -#include - -#include "openvic-simulation/InstanceManager.hpp" -#include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/diplomacy/CountryRelation.hpp" -#include "openvic-simulation/types/Date.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/utility/Logger.hpp" - using namespace OpenVic; DiplomaticActionType::DiplomaticActionType(DiplomaticActionType::Initializer&& initializer) @@ -18,348 +8,3 @@ DiplomaticActionType::DiplomaticActionType(DiplomaticActionType::Initializer&& i CancelableDiplomaticActionType::CancelableDiplomaticActionType(CancelableDiplomaticActionType::Initializer&& initializer) : allowed_to_cancel { std::move(initializer.allowed_cancel) }, DiplomaticActionType { std::move(initializer) } {} - -bool DiplomaticActionManager::add_diplomatic_action( - std::string_view identifier, DiplomaticActionType::Initializer&& initializer -) { - if (identifier.empty()) { - spdlog::error_s("Invalid diplomatic action identifier - empty!"); - return false; - } - return diplomatic_action_types.emplace_item( identifier, identifier, DiplomaticActionType { std::move(initializer) } ); -} - -bool DiplomaticActionManager::add_cancelable_diplomatic_action( - std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer -) { - if (identifier.empty()) { - spdlog::error_s("Invalid cancelable diplomatic action identifier - empty!"); - return false; - } - return diplomatic_action_types.emplace_item( identifier, identifier, CancelableDiplomaticActionType { std::move(initializer) } ); -} - -DiplomaticActionTickCache DiplomaticActionManager::create_diplomatic_action_tick( - std::string_view identifier, CountryInstance* sender, CountryInstance* receiver, std::any context_data, - InstanceManager& instance_manager -) { - auto type = diplomatic_action_types.get_item_by_identifier(identifier); - - DiplomaticActionTickCache result { { instance_manager, sender, receiver, context_data }, type }; - type->visit([&](auto type) { - CountryRelationManager::influence_value_type* influence = nullptr; - if (type.influence_cost != 0) { - influence = &result.argument.instance_manager.get_country_relation_manager().assign_or_get_influence_with( // - result.argument.sender, result.argument.receiver - ); - if (*influence < type.influence_cost) { - return; - } - } - if (!(result.allowed_to_commit = type.allowed_to_commit(result.argument))) { - return; - } - result.acceptance = type.get_acceptance(result.argument); - if (influence != nullptr) { - *influence -= type.influence_cost; - } - }); - - return result; -} - -bool DiplomaticActionManager::setup_diplomatic_actions() { - using Argument = DiplomaticActionType::Argument; - - bool result = true; - - // TODO: implement _RELATION_ON_ACCEPT & _RELATION_ON_DECLINE - - result &= add_diplomatic_action( - "form_alliance", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_country_alliance( // - arg.sender, arg.receiver, true - ); - }, - } - ); - result &= add_diplomatic_action( - "end_alliance", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_country_alliance( // - arg.sender, arg.receiver, false - ); - }, - } - ); - result &= add_diplomatic_action( - "call_ally", - { - .commit = [](Argument& arg) {}, - .allowed = - [](Argument const& arg) { - return false; - }, - .get_acceptance = - [](Argument const& arg) { - return 1; - }, - } - ); - result &= add_diplomatic_action( - "request_military_access", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_has_military_access_to( // - arg.sender, arg.receiver, true - ); - }, - } - ); - result &= add_diplomatic_action( // - "give_military_access", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_has_military_access_to( // - arg.sender, arg.receiver, true - ); - }, - } - ); - result &= add_diplomatic_action( - "increase_relations", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( - arg.sender, arg.receiver - ) += 25; // TODO: implement defines.diplomacy.INCREASERELATION_RELATION_ON_ACCEPT - }, - .allowed = - [](Argument const& arg) { - return false; - }, - } - ); - result &= add_diplomatic_action( - "decrease_relations", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( - arg.sender, arg.receiver - ) -= 25; // TODO: implement defines.diplomacy.DECREASERELATION_RELATION_ON_ACCEPT - }, - .allowed = - [](Argument const& arg) { - return false; - }, - } - ); - result &= add_diplomatic_action( - "war_subsidies", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_war_subsidies_to( // - arg.sender, arg.receiver, true - ); - }, - } - ); - result &= add_diplomatic_action( - "end_war_subsidies", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_war_subsidies_to( // - arg.sender, arg.receiver, false - ); - }, - } - ); - result &= add_diplomatic_action( - "declare_war", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_at_war_with( // - arg.sender, arg.receiver, true - ); - }, - } - ); - result &= add_diplomatic_action( - "offer_peace", - { - .commit = [](Argument& arg) {}, - } - ); - result &= add_diplomatic_action( - "command_units", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().assign_or_get_commands_units( // - arg.sender, arg.receiver - ) = true; - }, - } - ); - result &= add_diplomatic_action( - "discredit", - { - .influence_cost = 25, - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_discredited_date( // - arg.sender, arg.receiver, arg.instance_manager.get_today() + 180 - ); // TODO: implement defines.diplomacy.DISCREDIT_DAYS - }, - } - ); - result &= add_diplomatic_action( - "expel_advisors", - { - .influence_cost = 50, // TODO: implement defines.diplomacy.EXPELADVISORS_INFLUENCE_COST - .commit = [](Argument& arg) {}, - } - ); - result &= add_diplomatic_action( - "ban_embassy", - { - .influence_cost = 65, - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_embassy_banned_date( // - arg.sender, arg.receiver, arg.instance_manager.get_today() + Timespan::from_years(1) - ); // TODO: implement defines.diplomacy.BANEMBASSY_DAYS - }, - } - ); - result &= add_diplomatic_action( - "increase_opinion", - { - .influence_cost = 50, - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // - arg.sender, arg.receiver - )++; - }, - .allowed = - [](Argument const& arg) { - return true; - }, - } - ); - result &= add_diplomatic_action( - "decrease_opinion", - { - .influence_cost = 50, - .commit = - [](Argument& arg) { - --arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // - std::any_cast(arg.context_data), arg.receiver - ); - }, - .allowed = - [](Argument const& arg) { - return true; - }, - } - ); - result &= add_diplomatic_action( - "add_to_sphere", - { - .influence_cost = 100, - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // - arg.sender, arg.receiver - ) = CountryRelationManager::OpinionType::Sphere; - }, - .allowed = - [](Argument const& arg) { - return true; - }, - } - ); - result &= add_diplomatic_action( - "remove_from_foreign_sphere", - { - .influence_cost = 100, - .commit = - [](Argument& arg) { - --arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // - std::any_cast(arg.context_data), arg.receiver - ); - arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( // - arg.sender, arg.receiver - ) -= 10; - }, - .allowed = - [](Argument const& arg) { - return true; - }, - } - ); - result &= add_diplomatic_action( - "remove_from_own_sphere", - { - .influence_cost = 100, - .commit = - [](Argument& arg) { - --arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // - arg.sender, arg.receiver - ); - arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( // - arg.sender, arg.receiver - ) -= 10; - // TODO: implement REMOVEFROMSPHERE_PRESTIGE_COST and REMOVEFROMSPHERE_INFAMY_COST - }, - .allowed = - [](Argument const& arg) { - return true; - }, - } - ); - result &= add_diplomatic_action( - "justify_war", - { - .commit = [](Argument& arg) {}, - } - ); - result &= add_diplomatic_action( - "give_vision", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_has_vision( // - arg.sender, arg.receiver, true - ); - }, - } - ); - result &= add_diplomatic_action( - "remove_vision", - { - .commit = - [](Argument& arg) { - arg.instance_manager.get_country_relation_manager().set_has_vision( // - arg.sender, arg.receiver, false - ); - }, - } - ); - diplomatic_action_types.lock(); - - return result; -} diff --git a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp index 53db2c458..a1b58433a 100644 --- a/src/openvic-simulation/diplomacy/DiplomaticAction.hpp +++ b/src/openvic-simulation/diplomacy/DiplomaticAction.hpp @@ -11,7 +11,6 @@ #include "openvic-simulation/country/CountryInstance.hpp" #include "openvic-simulation/diplomacy/CountryRelation.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { struct InstanceManager; @@ -122,24 +121,4 @@ namespace OpenVic { bool allowed_to_commit = false; std::int32_t acceptance = -1; }; - - struct DiplomaticActionManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(diplomatic_action_type); - - public: - constexpr DiplomaticActionManager() {}; - - bool add_diplomatic_action(std::string_view identifier, DiplomaticActionType::Initializer&& initializer); - bool add_cancelable_diplomatic_action( - std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer - ); - - DiplomaticActionTickCache create_diplomatic_action_tick( - std::string_view identifier, CountryInstance* sender, CountryInstance* receiver, std::any context_data, - InstanceManager& instance_manager - ); - - bool setup_diplomatic_actions(); - }; } diff --git a/src/openvic-simulation/diplomacy/DiplomaticActionManager.cpp b/src/openvic-simulation/diplomacy/DiplomaticActionManager.cpp new file mode 100644 index 000000000..d1d505c2d --- /dev/null +++ b/src/openvic-simulation/diplomacy/DiplomaticActionManager.cpp @@ -0,0 +1,330 @@ +#include "DiplomaticActionManager.hpp" + +#include +#include + +#include "openvic-simulation/InstanceManager.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/diplomacy/CountryRelation.hpp" +#include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/utility/Logger.hpp" + +using namespace OpenVic; + +bool DiplomaticActionManager::add_diplomatic_action( + std::string_view identifier, DiplomaticActionType::Initializer&& initializer +) { + if (identifier.empty()) { + spdlog::error_s("Invalid diplomatic action identifier - empty!"); + return false; + } + return diplomatic_action_types.emplace_item( identifier, identifier, DiplomaticActionType { std::move(initializer) } ); +} + +bool DiplomaticActionManager::add_cancelable_diplomatic_action( + std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer +) { + if (identifier.empty()) { + spdlog::error_s("Invalid cancelable diplomatic action identifier - empty!"); + return false; + } + return diplomatic_action_types.emplace_item( identifier, identifier, CancelableDiplomaticActionType { std::move(initializer) } ); +} + +DiplomaticActionTickCache DiplomaticActionManager::create_diplomatic_action_tick( + std::string_view identifier, CountryInstance* sender, CountryInstance* receiver, std::any context_data, + InstanceManager& instance_manager +) { + auto type = diplomatic_action_types.get_item_by_identifier(identifier); + + DiplomaticActionTickCache result { { instance_manager, sender, receiver, context_data }, type }; + type->visit([&](auto type) { + CountryRelationManager::influence_value_type* influence = nullptr; + if (type.influence_cost != 0) { + influence = &result.argument.instance_manager.get_country_relation_manager().assign_or_get_influence_with( // + result.argument.sender, result.argument.receiver + ); + if (*influence < type.influence_cost) { + return; + } + } + if (!(result.allowed_to_commit = type.allowed_to_commit(result.argument))) { + return; + } + result.acceptance = type.get_acceptance(result.argument); + if (influence != nullptr) { + *influence -= type.influence_cost; + } + }); + + return result; +} + +bool DiplomaticActionManager::setup_diplomatic_actions() { + using Argument = DiplomaticActionType::Argument; + + bool result = true; + + // TODO: implement _RELATION_ON_ACCEPT & _RELATION_ON_DECLINE + + result &= add_diplomatic_action( + "form_alliance", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_country_alliance( // + arg.sender, arg.receiver, true + ); + }, + } + ); + result &= add_diplomatic_action( + "end_alliance", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_country_alliance( // + arg.sender, arg.receiver, false + ); + }, + } + ); + result &= add_diplomatic_action( + "call_ally", + { + .commit = [](Argument& arg) {}, + .allowed = [](Argument const& arg) { + return false; + }, + .get_acceptance = [](Argument const& arg) { + return 1; + }, + } + ); + result &= add_diplomatic_action( + "request_military_access", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_has_military_access_to( // + arg.sender, arg.receiver, true + ); + }, + } + ); + result &= add_diplomatic_action( // + "give_military_access", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_has_military_access_to( // + arg.sender, arg.receiver, true + ); + }, + } + ); + result &= add_diplomatic_action( + "increase_relations", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( + arg.sender, arg.receiver + ) += 25; // TODO: implement defines.diplomacy.INCREASERELATION_RELATION_ON_ACCEPT + }, + .allowed = [](Argument const& arg) { + return false; + }, + } + ); + result &= add_diplomatic_action( + "decrease_relations", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( + arg.sender, arg.receiver + ) -= 25; // TODO: implement defines.diplomacy.DECREASERELATION_RELATION_ON_ACCEPT + }, + .allowed = [](Argument const& arg) { + return false; + }, + } + ); + result &= add_diplomatic_action( + "war_subsidies", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_war_subsidies_to( // + arg.sender, arg.receiver, true + ); + }, + } + ); + result &= add_diplomatic_action( + "end_war_subsidies", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_war_subsidies_to( // + arg.sender, arg.receiver, false + ); + }, + } + ); + result &= add_diplomatic_action( + "declare_war", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_at_war_with( // + arg.sender, arg.receiver, true + ); + }, + } + ); + result &= add_diplomatic_action( + "offer_peace", + { + .commit = [](Argument& arg) {}, + } + ); + result &= add_diplomatic_action( + "command_units", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().assign_or_get_commands_units( // + arg.sender, arg.receiver + ) = true; + }, + } + ); + result &= add_diplomatic_action( + "discredit", + { + .influence_cost = 25, + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_discredited_date( // + arg.sender, arg.receiver, arg.instance_manager.get_today() + 180 + ); // TODO: implement defines.diplomacy.DISCREDIT_DAYS + }, + } + ); + result &= add_diplomatic_action( + "expel_advisors", + { + .influence_cost = 50, // TODO: implement defines.diplomacy.EXPELADVISORS_INFLUENCE_COST + .commit = [](Argument& arg) {}, + } + ); + result &= add_diplomatic_action( + "ban_embassy", + { + .influence_cost = 65, + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_embassy_banned_date( // + arg.sender, arg.receiver, arg.instance_manager.get_today() + Timespan::from_years(1) + ); // TODO: implement defines.diplomacy.BANEMBASSY_DAYS + }, + } + ); + result &= add_diplomatic_action( + "increase_opinion", + { + .influence_cost = 50, + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // + arg.sender, arg.receiver + )++; + }, + .allowed = [](Argument const& arg) { + return true; + }, + } + ); + result &= add_diplomatic_action( + "decrease_opinion", + { + .influence_cost = 50, + .commit = [](Argument& arg) { + --arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // + std::any_cast(arg.context_data), arg.receiver + ); + }, + .allowed = [](Argument const& arg) { + return true; + }, + } + ); + result &= add_diplomatic_action( + "add_to_sphere", + { + .influence_cost = 100, + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // + arg.sender, arg.receiver + ) = CountryRelationManager::OpinionType::Sphere; + }, + .allowed = [](Argument const& arg) { + return true; + }, + } + ); + result &= add_diplomatic_action( + "remove_from_foreign_sphere", + { + .influence_cost = 100, + .commit = [](Argument& arg) { + --arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // + std::any_cast(arg.context_data), arg.receiver + ); + arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( // + arg.sender, arg.receiver + ) -= 10; + }, + .allowed = [](Argument const& arg) { + return true; + }, + } + ); + result &= add_diplomatic_action( + "remove_from_own_sphere", + { + .influence_cost = 100, + .commit = [](Argument& arg) { + --arg.instance_manager.get_country_relation_manager().assign_or_get_country_opinion( // + arg.sender, arg.receiver + ); + arg.instance_manager.get_country_relation_manager().assign_or_get_country_relation( // + arg.sender, arg.receiver + ) -= 10; + // TODO: implement REMOVEFROMSPHERE_PRESTIGE_COST and REMOVEFROMSPHERE_INFAMY_COST + }, + .allowed = [](Argument const& arg) { + return true; + }, + } + ); + result &= add_diplomatic_action( + "justify_war", + { + .commit = [](Argument& arg) {}, + } + ); + result &= add_diplomatic_action( + "give_vision", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_has_vision( // + arg.sender, arg.receiver, true + ); + }, + } + ); + result &= add_diplomatic_action( + "remove_vision", + { + .commit = [](Argument& arg) { + arg.instance_manager.get_country_relation_manager().set_has_vision( // + arg.sender, arg.receiver, false + ); + }, + } + ); + diplomatic_action_types.lock(); + + return result; +} diff --git a/src/openvic-simulation/diplomacy/DiplomaticActionManager.hpp b/src/openvic-simulation/diplomacy/DiplomaticActionManager.hpp new file mode 100644 index 000000000..644badca7 --- /dev/null +++ b/src/openvic-simulation/diplomacy/DiplomaticActionManager.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/diplomacy/CountryRelation.hpp" +#include "openvic-simulation/diplomacy/DiplomaticAction.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct InstanceManager; + + struct DiplomaticActionManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(diplomatic_action_type); + + public: + constexpr DiplomaticActionManager() {}; + + bool add_diplomatic_action(std::string_view identifier, DiplomaticActionType::Initializer&& initializer); + bool add_cancelable_diplomatic_action( + std::string_view identifier, CancelableDiplomaticActionType::Initializer&& initializer + ); + + DiplomaticActionTickCache create_diplomatic_action_tick( + std::string_view identifier, CountryInstance* sender, CountryInstance* receiver, std::any context_data, + InstanceManager& instance_manager + ); + + bool setup_diplomatic_actions(); + }; +} diff --git a/src/openvic-simulation/economy/BuildingType.cpp b/src/openvic-simulation/economy/BuildingType.cpp index a726b4dcd..f3cc4ccc0 100644 --- a/src/openvic-simulation/economy/BuildingType.cpp +++ b/src/openvic-simulation/economy/BuildingType.cpp @@ -4,18 +4,15 @@ #include "openvic-simulation/core/error/ErrorMacros.hpp" #include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/map/State.hpp" #include "openvic-simulation/modifier/ModifierEffectCache.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/TypedIndices.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; BuildingType::BuildingType( index_t new_index, @@ -112,174 +109,3 @@ bool BuildingType::can_be_built_in( bool BuildingType::can_be_built_in(ProvinceDefinition const& location) const { return !is_port || location.has_port(); } - -BuildingTypeManager::BuildingTypeManager() : infrastructure_building_type { nullptr }, port_building_type { nullptr } {} - -bool BuildingTypeManager::add_building_type( - std::string_view identifier, BuildingType::building_type_args_t& building_type_args -) { - if (identifier.empty()) { - spdlog::error_s("Invalid building identifier - empty!"); - return false; - } - - std::optional province_building_index = building_type_args.in_province - ? std::make_optional(province_building_types.size()) - : std::nullopt; - - const bool ret = building_types.emplace_item( - identifier, - BuildingType::index_t { get_building_type_count() }, province_building_index, identifier, building_type_args - ); - - if (ret) { - building_type_types.emplace(building_type_args.type); - if (province_building_index.has_value()) { - province_building_types.emplace_back(building_types.back()); - } - } - - return ret; -} - -bool BuildingTypeManager::load_buildings_file( - GoodDefinitionManager const& good_definition_manager, ProductionTypeManager const& production_type_manager, - ModifierManager& modifier_manager, ast::NodeCPtr root -) { - bool ret = expect_dictionary_reserve_length( - building_types, [this, &good_definition_manager, &production_type_manager, &modifier_manager]( - std::string_view key, ast::NodeCPtr value - ) -> bool { - BuildingType::building_type_args_t building_type_args {}; - - bool ret = NodeTools::expect_dictionary_keys_and_default( - modifier_manager.expect_base_province_modifier(building_type_args.modifier), - "type", ONE_EXACTLY, expect_identifier(assign_variable_callback(building_type_args.type)), - "on_completion", ZERO_OR_ONE, expect_identifier(assign_variable_callback(building_type_args.on_completion)), - "completion_size", ZERO_OR_ONE, - expect_fixed_point(assign_variable_callback(building_type_args.completion_size)), - "max_level", ONE_EXACTLY, expect_strong_typedef(assign_variable_callback(building_type_args.max_level)), - "goods_cost", ONE_EXACTLY, good_definition_manager.expect_good_definition_decimal_map( - move_variable_callback(building_type_args.goods_cost) - ), - "cost", ZERO_OR_MORE, expect_fixed_point(assign_variable_callback(building_type_args.cost)), - "time", ONE_EXACTLY, expect_days(assign_variable_callback(building_type_args.build_time)), - "visibility", ONE_EXACTLY, expect_bool([key](bool visibility) -> bool { - if (!visibility) { - spdlog::warn_s( - "Visibility is \"no\" for building type \"{}\", the building will still be visible as this option has no effect!", - key - ); - } - return true; - }), - "onmap", ONE_EXACTLY, expect_bool(assign_variable_callback(building_type_args.on_map)), - "default_enabled", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.default_enabled)), - "production_type", ZERO_OR_ONE, production_type_manager.expect_production_type_identifier( - assign_variable_callback_pointer(building_type_args.production_type) - ), - "pop_build_factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.pop_build_factory)), - "strategic_factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.strategic_factory)), - "advanced_factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.advanced_factory)), - "fort_level", ZERO_OR_ONE, expect_strong_typedef(assign_variable_callback(building_type_args.fort_level)), - "naval_capacity", ZERO_OR_ONE, expect_uint(assign_variable_callback(building_type_args.naval_capacity)), - "colonial_points", ZERO_OR_ONE, - expect_list(expect_fixed_point(vector_callback(building_type_args.colonial_points))), - "province", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.in_province)), - "one_per_state", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.one_per_state)), - "colonial_range", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(building_type_args.colonial_range)), - "infrastructure", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(building_type_args.infrastructure)), - "spawn_railway_track", ZERO_OR_ONE, - expect_bool(assign_variable_callback(building_type_args.spawn_railway_track)), - "sail", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.sail)), - "steam", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.steam)), - "capital", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.capital)), - "port", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.port)) - )(value); - - ret &= add_building_type(key, building_type_args); - - return ret; - } - )(root); - lock_building_types(); - - auto& building_type_effects = modifier_manager.modifier_effect_cache.building_type_effects; - building_type_effects = std::move( - decltype(ModifierEffectCache::building_type_effects) { - generate_values, - building_type_index_t(get_building_type_count()) - } - ); - - for (BuildingType const& building_type : get_building_types()) { - using enum ModifierEffect::format_t; - using enum ModifierEffect::target_t; - - ModifierEffectCache::building_type_effects_t& this_building_type_effects = building_type_effects[building_type.index]; - - static constexpr std::string_view max_prefix = "max_"; - static constexpr std::string_view min_prefix = "min_build_"; - ret &= modifier_manager.register_technology_modifier_effect( - this_building_type_effects.max_level, append_string_views(max_prefix, building_type.get_identifier()), - FORMAT_x1_0DP_POS, memory::fmt::format("${}$ $TECH_MAX_LEVEL$", building_type) - ); - // TODO - add custom localisation for "min_build_$building_type$" modifiers - ret &= modifier_manager.register_terrain_modifier_effect( - this_building_type_effects.min_level, append_string_views(min_prefix, building_type.get_identifier()), - FORMAT_x1_0DP_NEG - ); - if (building_type.is_in_province) { - if (building_type.is_port) { - if (port_building_type == nullptr) { - port_building_type = &building_type; - } else { - spdlog::error_s( - "Building type {} is marked as a port, but we are already using {} as the port building type!", - building_type, *port_building_type - ); - ret = false; - } - } else if (building_type.type == "infrastructure") { - if (infrastructure_building_type == nullptr) { - infrastructure_building_type = &building_type; - } else { - spdlog::error_s( - "Building type {} is marked as a infrastructure, but we are already using {} as the infrastructure building type!", - building_type, *infrastructure_building_type - ); - ret = false; - } - } - } else { - if (building_type.is_port) { - spdlog::error_s( - "Building type {} is marked as a port, but is not a province building!", building_type - ); - ret = false; - } - } - } - - if (infrastructure_building_type == nullptr) { - spdlog::error_s("No infrastructure building type found!"); - ret = false; - } - - if (port_building_type == nullptr) { - spdlog::error_s("No port building type found!"); - ret = false; - } - - if (province_building_types.empty()) { - spdlog::error_s("No province building types found!"); - ret = false; - } else { - SPDLOG_INFO( - "Found {} province building types out of {} total building types", - province_building_types.size(), get_building_type_count() - ); - } - - return ret; -} diff --git a/src/openvic-simulation/economy/BuildingType.hpp b/src/openvic-simulation/economy/BuildingType.hpp index 708985636..042f23639 100644 --- a/src/openvic-simulation/economy/BuildingType.hpp +++ b/src/openvic-simulation/economy/BuildingType.hpp @@ -1,6 +1,5 @@ #pragma once -#include #include #include "openvic-simulation/core/memory/Vector.hpp" @@ -9,19 +8,15 @@ #include "openvic-simulation/economy/BuildingRestrictionCategory.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/TypedIndices.hpp" -#include "openvic-simulation/types/TypedSpan.hpp" namespace OpenVic { struct BuildingTypeManager; struct CountryInstance; struct GoodDefinition; - struct GoodDefinitionManager; struct ModifierEffectCache; struct ProductionType; - struct ProductionTypeManager; struct ProvinceDefinition; struct ProvinceInstance; @@ -109,27 +104,4 @@ namespace OpenVic { ) const; [[nodiscard]] bool can_be_built_in(ProvinceDefinition const& location) const; }; - - struct BuildingTypeManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(building_type); - string_set_t PROPERTY(building_type_types); - BuildingType const* PROPERTY(infrastructure_building_type); - BuildingType const* PROPERTY(port_building_type); - - memory::vector> province_building_types; - public: - constexpr TypedSpan> get_province_building_types() const { - return province_building_types; - } - - BuildingTypeManager(); - - bool add_building_type(std::string_view identifier, BuildingType::building_type_args_t& building_type_args); - - bool load_buildings_file( - GoodDefinitionManager const& good_definition_manager, ProductionTypeManager const& production_type_manager, - ModifierManager& modifier_manager, ast::NodeCPtr root - ); - }; } diff --git a/src/openvic-simulation/economy/BuildingTypeManager.cpp b/src/openvic-simulation/economy/BuildingTypeManager.cpp new file mode 100644 index 000000000..ac7bc009c --- /dev/null +++ b/src/openvic-simulation/economy/BuildingTypeManager.cpp @@ -0,0 +1,187 @@ +#include "BuildingTypeManager.hpp" + +#include + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/GoodDefinitionManager.hpp" +#include "openvic-simulation/economy/production/ProductionTypeManager.hpp" +#include "openvic-simulation/map/ProvinceDefinition.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +BuildingTypeManager::BuildingTypeManager() : infrastructure_building_type { nullptr }, port_building_type { nullptr } {} + +bool BuildingTypeManager::add_building_type( + std::string_view identifier, BuildingType::building_type_args_t& building_type_args +) { + if (identifier.empty()) { + spdlog::error_s("Invalid building identifier - empty!"); + return false; + } + + std::optional province_building_index = building_type_args.in_province + ? std::make_optional(province_building_types.size()) + : std::nullopt; + + const bool ret = building_types.emplace_item( + identifier, + BuildingType::index_t { get_building_type_count() }, province_building_index, identifier, building_type_args + ); + + if (ret) { + building_type_types.emplace(building_type_args.type); + if (province_building_index.has_value()) { + province_building_types.emplace_back(building_types.back()); + } + } + + return ret; +} + +bool BuildingTypeManager::load_buildings_file( + GoodDefinitionManager const& good_definition_manager, ProductionTypeManager const& production_type_manager, + ModifierManager& modifier_manager, ovdl::v2script::ast::Node const* root +) { + bool ret = expect_dictionary_reserve_length( + building_types, [this, &good_definition_manager, &production_type_manager, &modifier_manager]( + std::string_view key, ovdl::v2script::ast::Node const* value + ) -> bool { + BuildingType::building_type_args_t building_type_args {}; + + bool ret = NodeTools::expect_dictionary_keys_and_default( + modifier_manager.expect_base_province_modifier(building_type_args.modifier), + "type", ONE_EXACTLY, expect_identifier(assign_variable_callback(building_type_args.type)), + "on_completion", ZERO_OR_ONE, expect_identifier(assign_variable_callback(building_type_args.on_completion)), + "completion_size", ZERO_OR_ONE, + expect_fixed_point(assign_variable_callback(building_type_args.completion_size)), + "max_level", ONE_EXACTLY, expect_strong_typedef(assign_variable_callback(building_type_args.max_level)), + "goods_cost", ONE_EXACTLY, good_definition_manager.expect_good_definition_decimal_map( + move_variable_callback(building_type_args.goods_cost) + ), + "cost", ZERO_OR_MORE, expect_fixed_point(assign_variable_callback(building_type_args.cost)), + "time", ONE_EXACTLY, expect_days(assign_variable_callback(building_type_args.build_time)), + "visibility", ONE_EXACTLY, expect_bool([key](bool visibility) -> bool { + if (!visibility) { + spdlog::warn_s( + "Visibility is \"no\" for building type \"{}\", the building will still be visible as this option has no effect!", + key + ); + } + return true; + }), + "onmap", ONE_EXACTLY, expect_bool(assign_variable_callback(building_type_args.on_map)), + "default_enabled", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.default_enabled)), + "production_type", ZERO_OR_ONE, production_type_manager.expect_production_type_identifier( + assign_variable_callback_pointer(building_type_args.production_type) + ), + "pop_build_factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.pop_build_factory)), + "strategic_factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.strategic_factory)), + "advanced_factory", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.advanced_factory)), + "fort_level", ZERO_OR_ONE, expect_strong_typedef(assign_variable_callback(building_type_args.fort_level)), + "naval_capacity", ZERO_OR_ONE, expect_uint(assign_variable_callback(building_type_args.naval_capacity)), + "colonial_points", ZERO_OR_ONE, + expect_list(expect_fixed_point(vector_callback(building_type_args.colonial_points))), + "province", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.in_province)), + "one_per_state", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.one_per_state)), + "colonial_range", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(building_type_args.colonial_range)), + "infrastructure", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(building_type_args.infrastructure)), + "spawn_railway_track", ZERO_OR_ONE, + expect_bool(assign_variable_callback(building_type_args.spawn_railway_track)), + "sail", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.sail)), + "steam", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.steam)), + "capital", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.capital)), + "port", ZERO_OR_ONE, expect_bool(assign_variable_callback(building_type_args.port)) + )(value); + + ret &= add_building_type(key, building_type_args); + + return ret; + } + )(root); + lock_building_types(); + + auto& building_type_effects = modifier_manager.modifier_effect_cache.building_type_effects; + building_type_effects = std::move( + decltype(ModifierEffectCache::building_type_effects) { + generate_values, + building_type_index_t(get_building_type_count()) + } + ); + + for (BuildingType const& building_type : get_building_types()) { + using enum ModifierEffect::format_t; + using enum ModifierEffect::target_t; + + ModifierEffectCache::building_type_effects_t& this_building_type_effects = building_type_effects[building_type.index]; + + static constexpr std::string_view max_prefix = "max_"; + static constexpr std::string_view min_prefix = "min_build_"; + ret &= modifier_manager.register_technology_modifier_effect( + this_building_type_effects.max_level, append_string_views(max_prefix, building_type.get_identifier()), + FORMAT_x1_0DP_POS, memory::fmt::format("${}$ $TECH_MAX_LEVEL$", building_type) + ); + // TODO - add custom localisation for "min_build_$building_type$" modifiers + ret &= modifier_manager.register_terrain_modifier_effect( + this_building_type_effects.min_level, append_string_views(min_prefix, building_type.get_identifier()), + FORMAT_x1_0DP_NEG + ); + if (building_type.is_in_province) { + if (building_type.is_port) { + if (port_building_type == nullptr) { + port_building_type = &building_type; + } else { + spdlog::error_s( + "Building type {} is marked as a port, but we are already using {} as the port building type!", + building_type, *port_building_type + ); + ret = false; + } + } else if (building_type.type == "infrastructure") { + if (infrastructure_building_type == nullptr) { + infrastructure_building_type = &building_type; + } else { + spdlog::error_s( + "Building type {} is marked as a infrastructure, but we are already using {} as the infrastructure building type!", + building_type, *infrastructure_building_type + ); + ret = false; + } + } + } else { + if (building_type.is_port) { + spdlog::error_s( + "Building type {} is marked as a port, but is not a province building!", building_type + ); + ret = false; + } + } + } + + if (infrastructure_building_type == nullptr) { + spdlog::error_s("No infrastructure building type found!"); + ret = false; + } + + if (port_building_type == nullptr) { + spdlog::error_s("No port building type found!"); + ret = false; + } + + if (province_building_types.empty()) { + spdlog::error_s("No province building types found!"); + ret = false; + } else { + SPDLOG_INFO( + "Found {} province building types out of {} total building types", + province_building_types.size(), get_building_type_count() + ); + } + + return ret; +} diff --git a/src/openvic-simulation/economy/BuildingTypeManager.hpp b/src/openvic-simulation/economy/BuildingTypeManager.hpp new file mode 100644 index 000000000..05892b3e3 --- /dev/null +++ b/src/openvic-simulation/economy/BuildingTypeManager.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/economy/BuildingType.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/TypedSpan.hpp" + +namespace OpenVic { + struct CountryInstance; + struct GoodDefinitionManager; + struct ModifierEffectCache; + struct ProductionTypeManager; + + struct BuildingTypeManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(building_type); + string_set_t PROPERTY(building_type_types); + BuildingType const* PROPERTY(infrastructure_building_type); + BuildingType const* PROPERTY(port_building_type); + + memory::vector> province_building_types; + public: + constexpr TypedSpan> get_province_building_types() const { + return province_building_types; + } + + BuildingTypeManager(); + + bool add_building_type(std::string_view identifier, BuildingType::building_type_args_t& building_type_args); + + bool load_buildings_file( + GoodDefinitionManager const& good_definition_manager, ProductionTypeManager const& production_type_manager, + ModifierManager& modifier_manager, ovdl::v2script::ast::Node const* root + ); + }; +} diff --git a/src/openvic-simulation/economy/EconomyManager.hpp b/src/openvic-simulation/economy/EconomyManager.hpp index 77226313c..d5b080d42 100644 --- a/src/openvic-simulation/economy/EconomyManager.hpp +++ b/src/openvic-simulation/economy/EconomyManager.hpp @@ -1,10 +1,12 @@ #pragma once -#include +#include "openvic-simulation/economy/BuildingTypeManager.hpp" +#include "openvic-simulation/economy/GoodDefinitionManager.hpp" +#include "openvic-simulation/economy/production/ProductionTypeManager.hpp" -#include "openvic-simulation/economy/BuildingType.hpp" -#include "openvic-simulation/economy/GoodDefinition.hpp" -#include "openvic-simulation/economy/production/ProductionType.hpp" +namespace ovdl::v2script { + class Parser; +} namespace OpenVic { struct EconomyManager { @@ -27,7 +29,7 @@ namespace OpenVic { ); } - inline bool load_buildings_file(ModifierManager& modifier_manager, ast::NodeCPtr root) { + inline bool load_buildings_file(ModifierManager& modifier_manager, ovdl::v2script::ast::Node const* root) { return building_type_manager.load_buildings_file( good_definition_manager, production_type_manager, modifier_manager, root ); diff --git a/src/openvic-simulation/economy/GoodDefinition.cpp b/src/openvic-simulation/economy/GoodDefinition.cpp index 1d733593c..7f90e63ea 100644 --- a/src/openvic-simulation/economy/GoodDefinition.cpp +++ b/src/openvic-simulation/economy/GoodDefinition.cpp @@ -1,11 +1,6 @@ #include "GoodDefinition.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" -#include "openvic-simulation/core/string/Utility.hpp" -#include "openvic-simulation/types/FixedVector.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; GoodCategory::GoodCategory(std::string_view new_identifier) : HasIdentifier { new_identifier } {} @@ -27,198 +22,3 @@ GoodDefinition::GoodDefinition( is_tradeable { new_is_tradeable }, is_money { new_is_money }, counters_overseas_penalty { new_counters_overseas_penalty } {} - -bool GoodDefinitionManager::add_good_category(std::string_view identifier, size_t expected_goods_in_category) { - if (identifier.empty()) { - spdlog::error_s("Invalid good category identifier - empty!"); - return false; - } - - if (good_categories.emplace_item(identifier, identifier)) { - good_categories.back().good_definitions.reserve(expected_goods_in_category); - return true; - } else { - return false; - } -} - -bool GoodDefinitionManager::add_good_definition( - std::string_view identifier, colour_t colour, GoodCategory& category, fixed_point_t base_price, - bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty -) { - if (identifier.empty()) { - spdlog::error_s("Invalid good identifier - empty!"); - return false; - } - if (base_price <= 0) { - spdlog::error_s("Invalid base price for {}: {}", identifier, base_price); - return false; - } - - if (is_tradeable == is_money) { - spdlog::warn_s( - "Good {} has tradeable: {} and money: {}. Money goods are never tradeable. All other goods are tradeable. Setting tradeable has no effect.", - identifier, bool_to_yes_no(is_tradeable), bool_to_yes_no(is_money) - ); - } - - if (good_definitions.emplace_item( - identifier, - identifier, colour, GoodDefinition::index_t { get_good_definition_count() }, category, base_price, is_available_from_start, - is_tradeable, is_money, has_overseas_penalty - )) { - category.good_definitions.emplace_back(get_back_good_definition()); - return true; - } else { - return false; - } -} - -bool GoodDefinitionManager::load_goods_file(ast::NodeCPtr root) { - size_t total_expected_goods = 0; - - bool ret = expect_dictionary_reserve_length( - good_categories, - [this, &total_expected_goods](std::string_view key, ast::NodeCPtr value) -> bool { - size_t expected_goods_in_category = 0; - - bool ret = expect_length(assign_variable_callback(expected_goods_in_category))(value); - - if (add_good_category(key, expected_goods_in_category)) { - total_expected_goods += expected_goods_in_category; - } else { - ret = false; - } - - return ret; - } - )(root); - - lock_good_categories(); - reserve_more_good_definitions(total_expected_goods); - - ret &= good_categories.expect_item_dictionary( - [this](GoodCategory& good_category, ast::NodeCPtr good_category_value) -> bool { - return expect_dictionary( - [this, &good_category](std::string_view key, ast::NodeCPtr value) -> bool { - colour_t colour = colour_t::null(); - fixed_point_t base_price; - bool is_available_from_start = true, is_tradeable = true; - bool is_money = false, has_overseas_penalty = false; - - bool ret = expect_dictionary_keys( - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), - "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base_price)), - "available_from_start", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_available_from_start)), - "tradeable", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_tradeable)), - "money", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_money)), - "overseas_penalty", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_overseas_penalty)) - )(value); - - ret &= add_good_definition( - key, colour, good_category, base_price, is_available_from_start, is_tradeable, is_money, - has_overseas_penalty - ); - - return ret; - } - )(good_category_value); - } - )(root); - - lock_good_definitions(); - - return ret; -} - -bool GoodDefinitionManager::generate_modifiers(ModifierManager& modifier_manager) const { - static constexpr bool HAS_NO_EFFECT = true; - - using enum ModifierEffect::format_t; - using enum ModifierEffect::target_t; - - memory::FixedVector& good_effects = modifier_manager.modifier_effect_cache.good_effects; - good_effects = std::move( - decltype(ModifierEffectCache::good_effects) { - generate_values, - good_index_t(get_good_definition_count()) - } - ); - - bool ret = true; - - ret &= modifier_manager.register_complex_modifier("rgo_goods_input"); - ret &= modifier_manager.register_complex_modifier("rgo_goods_output"); - ret &= modifier_manager.register_complex_modifier("rgo_goods_throughput"); - ret &= modifier_manager.register_complex_modifier("factory_goods_input"); - ret &= modifier_manager.register_complex_modifier("factory_goods_output"); - ret &= modifier_manager.register_complex_modifier("factory_goods_throughput"); - ret &= modifier_manager.register_complex_modifier("artisan_goods_input"); - ret &= modifier_manager.register_complex_modifier("artisan_goods_output"); - ret &= modifier_manager.register_complex_modifier("artisan_goods_throughput"); - ret &= modifier_manager.register_complex_modifier("rgo_size"); - - for (GoodDefinition const& good : get_good_definitions()) { - const std::string_view good_identifier = good.get_identifier(); - ModifierEffectCache::good_effects_t& this_good_effects = good_effects[good.index]; - - const auto good_modifier = [&modifier_manager, &ret, &good_identifier]( - ModifierEffect const*& effect_cache, std::string_view name, ModifierEffect::format_t format, - std::string_view localisation_key, bool has_no_effect = false - ) -> void { - ret &= modifier_manager.register_technology_modifier_effect( - effect_cache, ModifierManager::get_flat_identifier(name, good_identifier), - format, localisation_key, has_no_effect - ); - }; - - const auto make_production_localisation_suffix = [&good]( - std::string_view localisation_suffix - ) -> memory::string { - return memory::fmt::format("${}$ ${}$", good, localisation_suffix); - }; - - good_modifier( - this_good_effects.rgo_goods_input, "rgo_goods_input", FORMAT_x100_1DP_PC_NEG, - make_production_localisation_suffix("TECH_INPUT"), HAS_NO_EFFECT - ); - good_modifier( - this_good_effects.rgo_goods_output, "rgo_goods_output", FORMAT_x100_1DP_PC_POS, - make_production_localisation_suffix("TECH_OUTPUT") - ); - good_modifier( - this_good_effects.rgo_goods_throughput, "rgo_goods_throughput", FORMAT_x100_1DP_PC_POS, - make_production_localisation_suffix("TECH_THROUGHPUT") - ); - good_modifier( - this_good_effects.factory_goods_input, "factory_goods_input", FORMAT_x100_1DP_PC_NEG, - make_production_localisation_suffix("TECH_INPUT") - ); - good_modifier( - this_good_effects.factory_goods_output, "factory_goods_output", FORMAT_x100_1DP_PC_POS, - make_production_localisation_suffix("TECH_OUTPUT") - ); - good_modifier( - this_good_effects.factory_goods_throughput, "factory_goods_throughput", FORMAT_x100_1DP_PC_POS, - make_production_localisation_suffix("TECH_THROUGHPUT") - ); - good_modifier( - this_good_effects.artisan_goods_input, "artisan_goods_input", FORMAT_x100_1DP_PC_NEG, - make_production_localisation_suffix("TECH_INPUT"), HAS_NO_EFFECT - ); - good_modifier( - this_good_effects.artisan_goods_output, "artisan_goods_output", FORMAT_x100_1DP_PC_POS, - make_production_localisation_suffix("TECH_OUTPUT"), HAS_NO_EFFECT - ); - good_modifier( - this_good_effects.artisan_goods_throughput, "artisan_goods_throughput", FORMAT_x100_1DP_PC_POS, - make_production_localisation_suffix("TECH_THROUGHPUT"), HAS_NO_EFFECT - ); - good_modifier( - this_good_effects.rgo_size, "rgo_size", FORMAT_x100_2DP_PC_POS, - memory::fmt::format("{}_RGO_SIZE", good) - ); - } - - return ret; -} diff --git a/src/openvic-simulation/economy/GoodDefinition.hpp b/src/openvic-simulation/economy/GoodDefinition.hpp index 44a19d2e5..c1f3fe993 100644 --- a/src/openvic-simulation/economy/GoodDefinition.hpp +++ b/src/openvic-simulation/economy/GoodDefinition.hpp @@ -3,9 +3,9 @@ #include #include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { @@ -51,23 +51,4 @@ namespace OpenVic { ); GoodDefinition(GoodDefinition&&) = default; }; - - struct ModifierManager; - - struct GoodDefinitionManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(good_category, good_categories); - IdentifierRegistry IDENTIFIER_REGISTRY(good_definition); - - public: - bool add_good_category(std::string_view identifier, size_t expected_goods_in_category); - - bool add_good_definition( - std::string_view identifier, colour_t colour, GoodCategory& category, fixed_point_t base_price, - bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty - ); - - bool load_goods_file(ast::NodeCPtr root); - bool generate_modifiers(ModifierManager& modifier_manager) const; - }; } diff --git a/src/openvic-simulation/economy/GoodDefinitionManager.cpp b/src/openvic-simulation/economy/GoodDefinitionManager.cpp new file mode 100644 index 000000000..5edb130d3 --- /dev/null +++ b/src/openvic-simulation/economy/GoodDefinitionManager.cpp @@ -0,0 +1,204 @@ +#include "GoodDefinitionManager.hpp" + +#include "openvic-simulation/core/string/Utility.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" +#include "openvic-simulation/types/FixedVector.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool GoodDefinitionManager::add_good_category(std::string_view identifier, size_t expected_goods_in_category) { + if (identifier.empty()) { + spdlog::error_s("Invalid good category identifier - empty!"); + return false; + } + + if (good_categories.emplace_item(identifier, identifier)) { + good_categories.back().good_definitions.reserve(expected_goods_in_category); + return true; + } else { + return false; + } +} + +bool GoodDefinitionManager::add_good_definition( + std::string_view identifier, colour_t colour, GoodCategory& category, fixed_point_t base_price, + bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty +) { + if (identifier.empty()) { + spdlog::error_s("Invalid good identifier - empty!"); + return false; + } + if (base_price <= 0) { + spdlog::error_s("Invalid base price for {}: {}", identifier, base_price); + return false; + } + + if (is_tradeable == is_money) { + spdlog::warn_s( + "Good {} has tradeable: {} and money: {}. Money goods are never tradeable. All other goods are tradeable. Setting tradeable has no effect.", + identifier, bool_to_yes_no(is_tradeable), bool_to_yes_no(is_money) + ); + } + + if (good_definitions.emplace_item( + identifier, + identifier, colour, GoodDefinition::index_t { get_good_definition_count() }, category, base_price, is_available_from_start, + is_tradeable, is_money, has_overseas_penalty + )) { + category.good_definitions.emplace_back(get_back_good_definition()); + return true; + } else { + return false; + } +} + +bool GoodDefinitionManager::load_goods_file(ovdl::v2script::ast::Node const* root) { + size_t total_expected_goods = 0; + + bool ret = expect_dictionary_reserve_length( + good_categories, + [this, &total_expected_goods](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + size_t expected_goods_in_category = 0; + + bool ret = expect_length(assign_variable_callback(expected_goods_in_category))(value); + + if (add_good_category(key, expected_goods_in_category)) { + total_expected_goods += expected_goods_in_category; + } else { + ret = false; + } + + return ret; + } + )(root); + + lock_good_categories(); + reserve_more_good_definitions(total_expected_goods); + + ret &= good_categories.expect_item_dictionary( + [this](GoodCategory& good_category, ovdl::v2script::ast::Node const* good_category_value) -> bool { + return expect_dictionary( + [this, &good_category](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + colour_t colour = colour_t::null(); + fixed_point_t base_price; + bool is_available_from_start = true, is_tradeable = true; + bool is_money = false, has_overseas_penalty = false; + + bool ret = expect_dictionary_keys( + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(base_price)), + "available_from_start", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_available_from_start)), + "tradeable", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_tradeable)), + "money", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_money)), + "overseas_penalty", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_overseas_penalty)) + )(value); + + ret &= add_good_definition( + key, colour, good_category, base_price, is_available_from_start, is_tradeable, is_money, + has_overseas_penalty + ); + + return ret; + } + )(good_category_value); + } + )(root); + + lock_good_definitions(); + + return ret; +} + +bool GoodDefinitionManager::generate_modifiers(ModifierManager& modifier_manager) const { + static constexpr bool HAS_NO_EFFECT = true; + + using enum ModifierEffect::format_t; + using enum ModifierEffect::target_t; + + memory::FixedVector& good_effects = modifier_manager.modifier_effect_cache.good_effects; + good_effects = std::move( + decltype(ModifierEffectCache::good_effects) { + generate_values, + good_index_t(get_good_definition_count()) + } + ); + + bool ret = true; + + ret &= modifier_manager.register_complex_modifier("rgo_goods_input"); + ret &= modifier_manager.register_complex_modifier("rgo_goods_output"); + ret &= modifier_manager.register_complex_modifier("rgo_goods_throughput"); + ret &= modifier_manager.register_complex_modifier("factory_goods_input"); + ret &= modifier_manager.register_complex_modifier("factory_goods_output"); + ret &= modifier_manager.register_complex_modifier("factory_goods_throughput"); + ret &= modifier_manager.register_complex_modifier("artisan_goods_input"); + ret &= modifier_manager.register_complex_modifier("artisan_goods_output"); + ret &= modifier_manager.register_complex_modifier("artisan_goods_throughput"); + ret &= modifier_manager.register_complex_modifier("rgo_size"); + + for (GoodDefinition const& good : get_good_definitions()) { + const std::string_view good_identifier = good.get_identifier(); + ModifierEffectCache::good_effects_t& this_good_effects = good_effects[good.index]; + + const auto good_modifier = [&modifier_manager, &ret, &good_identifier]( + ModifierEffect const*& effect_cache, std::string_view name, ModifierEffect::format_t format, + std::string_view localisation_key, bool has_no_effect = false + ) -> void { + ret &= modifier_manager.register_technology_modifier_effect( + effect_cache, ModifierManager::get_flat_identifier(name, good_identifier), + format, localisation_key, has_no_effect + ); + }; + + const auto make_production_localisation_suffix = [&good]( + std::string_view localisation_suffix + ) -> memory::string { + return memory::fmt::format("${}$ ${}$", good, localisation_suffix); + }; + + good_modifier( + this_good_effects.rgo_goods_input, "rgo_goods_input", FORMAT_x100_1DP_PC_NEG, + make_production_localisation_suffix("TECH_INPUT"), HAS_NO_EFFECT + ); + good_modifier( + this_good_effects.rgo_goods_output, "rgo_goods_output", FORMAT_x100_1DP_PC_POS, + make_production_localisation_suffix("TECH_OUTPUT") + ); + good_modifier( + this_good_effects.rgo_goods_throughput, "rgo_goods_throughput", FORMAT_x100_1DP_PC_POS, + make_production_localisation_suffix("TECH_THROUGHPUT") + ); + good_modifier( + this_good_effects.factory_goods_input, "factory_goods_input", FORMAT_x100_1DP_PC_NEG, + make_production_localisation_suffix("TECH_INPUT") + ); + good_modifier( + this_good_effects.factory_goods_output, "factory_goods_output", FORMAT_x100_1DP_PC_POS, + make_production_localisation_suffix("TECH_OUTPUT") + ); + good_modifier( + this_good_effects.factory_goods_throughput, "factory_goods_throughput", FORMAT_x100_1DP_PC_POS, + make_production_localisation_suffix("TECH_THROUGHPUT") + ); + good_modifier( + this_good_effects.artisan_goods_input, "artisan_goods_input", FORMAT_x100_1DP_PC_NEG, + make_production_localisation_suffix("TECH_INPUT"), HAS_NO_EFFECT + ); + good_modifier( + this_good_effects.artisan_goods_output, "artisan_goods_output", FORMAT_x100_1DP_PC_POS, + make_production_localisation_suffix("TECH_OUTPUT"), HAS_NO_EFFECT + ); + good_modifier( + this_good_effects.artisan_goods_throughput, "artisan_goods_throughput", FORMAT_x100_1DP_PC_POS, + make_production_localisation_suffix("TECH_THROUGHPUT"), HAS_NO_EFFECT + ); + good_modifier( + this_good_effects.rgo_size, "rgo_size", FORMAT_x100_2DP_PC_POS, + memory::fmt::format("{}_RGO_SIZE", good) + ); + } + + return ret; +} diff --git a/src/openvic-simulation/economy/GoodDefinitionManager.hpp b/src/openvic-simulation/economy/GoodDefinitionManager.hpp new file mode 100644 index 000000000..f8be59a8e --- /dev/null +++ b/src/openvic-simulation/economy/GoodDefinitionManager.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/economy/GoodDefinition.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct ModifierManager; + + struct GoodDefinitionManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(good_category, good_categories); + IdentifierRegistry IDENTIFIER_REGISTRY(good_definition); + + public: + bool add_good_category(std::string_view identifier, size_t expected_goods_in_category); + + bool add_good_definition( + std::string_view identifier, colour_t colour, GoodCategory& category, fixed_point_t base_price, + bool is_available_from_start, bool is_tradeable, bool is_money, bool has_overseas_penalty + ); + + bool load_goods_file(ovdl::v2script::ast::Node const* root); + bool generate_modifiers(ModifierManager& modifier_manager) const; + }; +} diff --git a/src/openvic-simulation/economy/GoodInstance.cpp b/src/openvic-simulation/economy/GoodInstance.cpp index 34ab86a35..855d812b2 100644 --- a/src/openvic-simulation/economy/GoodInstance.cpp +++ b/src/openvic-simulation/economy/GoodInstance.cpp @@ -1,7 +1,5 @@ #include "GoodInstance.hpp" -#include - #include "openvic-simulation/economy/GoodDefinition.hpp" using namespace OpenVic; @@ -14,50 +12,6 @@ GoodInstance::GoodInstance( GoodMarket { *new_game_rules_manager, *new_good_definition } {} -GoodInstanceManager::GoodInstanceManager( - GoodDefinitionManager const& new_good_definition_manager, - GameRulesManager const& game_rules_manager -) : good_definition_manager { new_good_definition_manager }, - good_instance_by_definition { - new_good_definition_manager.get_good_definitions(), - [game_rules_manager_ptr=&game_rules_manager](GoodDefinition const& good_definition) -> auto { - return std::make_tuple(&good_definition, game_rules_manager_ptr); - } - } { assert(new_good_definition_manager.good_definitions_are_locked()); } - bool GoodInstance::is_trading_good() const { return is_available && !good_definition.is_money; } - -GoodInstance* GoodInstanceManager::get_good_instance_by_identifier(std::string_view identifier) { - GoodDefinition const* good_definition = good_definition_manager.get_good_definition_by_identifier(identifier); - return good_definition == nullptr - ? nullptr - : &get_good_instance_by_definition(*good_definition); -} -GoodInstance const* GoodInstanceManager::get_good_instance_by_identifier(std::string_view identifier) const { - GoodDefinition const* good_definition = good_definition_manager.get_good_definition_by_identifier(identifier); - return good_definition == nullptr - ? nullptr - : &get_good_instance_by_definition(*good_definition); -} -GoodInstance* GoodInstanceManager::get_good_instance_by_index(typename GoodInstance::index_t index) { - return good_instance_by_definition.contains_index(index) - ? &good_instance_by_definition.at_index(index) - : nullptr; -} -GoodInstance const* GoodInstanceManager::get_good_instance_by_index(typename GoodInstance::index_t index) const { - return good_instance_by_definition.contains_index(index) - ? &good_instance_by_definition.at_index(index) - : nullptr; -} -GoodInstance& GoodInstanceManager::get_good_instance_by_definition(GoodDefinition const& good_definition) { - return good_instance_by_definition.at(good_definition); -} -GoodInstance const& GoodInstanceManager::get_good_instance_by_definition(GoodDefinition const& good_definition) const { - return good_instance_by_definition.at(good_definition); -} - -void GoodInstanceManager::enable_good(GoodDefinition const& good) { - get_good_instance_by_definition(good).is_available = true; -} diff --git a/src/openvic-simulation/economy/GoodInstance.hpp b/src/openvic-simulation/economy/GoodInstance.hpp index b51194321..0e3480896 100644 --- a/src/openvic-simulation/economy/GoodInstance.hpp +++ b/src/openvic-simulation/economy/GoodInstance.hpp @@ -3,12 +3,11 @@ #include "openvic-simulation/economy/trading/GoodMarket.hpp" #include "openvic-simulation/types/HasIndex.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" -#include "openvic-simulation/types/IndexedFlatMap.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { + struct GameRulesManager; struct GoodDefinition; - struct GoodDefinitionManager; struct GoodInstanceManager; struct GoodInstance : HasIdentifierAndColour, HasIndex, GoodMarket { @@ -29,31 +28,4 @@ namespace OpenVic { // is_tradeable has no effect on this, only is_money and availability bool is_trading_good() const; }; - - struct GoodInstanceManager { - private: - GoodDefinitionManager const& good_definition_manager; - OV_IFLATMAP_PROPERTY(GoodDefinition, GoodInstance, good_instance_by_definition); - - public: - GoodInstanceManager( - GoodDefinitionManager const& new_good_definition_manager, - GameRulesManager const& game_rules_manager - ); - - constexpr forwardable_span get_good_instances() { - return good_instance_by_definition.get_values(); - } - - constexpr forwardable_span get_good_instances() const { - return good_instance_by_definition.get_values(); - } - GoodInstance* get_good_instance_by_identifier(std::string_view identifier); - GoodInstance const* get_good_instance_by_identifier(std::string_view identifier) const; - GoodInstance* get_good_instance_by_index(typename GoodInstance::index_t index); - GoodInstance const* get_good_instance_by_index(typename GoodInstance::index_t index) const; - GoodInstance& get_good_instance_by_definition(GoodDefinition const& good_definition); - - void enable_good(GoodDefinition const& good); - }; } diff --git a/src/openvic-simulation/economy/GoodInstanceManager.cpp b/src/openvic-simulation/economy/GoodInstanceManager.cpp new file mode 100644 index 000000000..e1bfeee52 --- /dev/null +++ b/src/openvic-simulation/economy/GoodInstanceManager.cpp @@ -0,0 +1,51 @@ +#include "GoodInstanceManager.hpp" + +#include + +#include "openvic-simulation/economy/GoodDefinitionManager.hpp" + +using namespace OpenVic; + +GoodInstanceManager::GoodInstanceManager( + GoodDefinitionManager const& new_good_definition_manager, + GameRulesManager const& game_rules_manager +) : good_definition_manager { new_good_definition_manager }, + good_instance_by_definition { + new_good_definition_manager.get_good_definitions(), + [game_rules_manager_ptr=&game_rules_manager](GoodDefinition const& good_definition) -> auto { + return std::make_tuple(&good_definition, game_rules_manager_ptr); + } + } { assert(new_good_definition_manager.good_definitions_are_locked()); } + +GoodInstance* GoodInstanceManager::get_good_instance_by_identifier(std::string_view identifier) { + GoodDefinition const* good_definition = good_definition_manager.get_good_definition_by_identifier(identifier); + return good_definition == nullptr + ? nullptr + : &get_good_instance_by_definition(*good_definition); +} +GoodInstance const* GoodInstanceManager::get_good_instance_by_identifier(std::string_view identifier) const { + GoodDefinition const* good_definition = good_definition_manager.get_good_definition_by_identifier(identifier); + return good_definition == nullptr + ? nullptr + : &get_good_instance_by_definition(*good_definition); +} +GoodInstance* GoodInstanceManager::get_good_instance_by_index(typename GoodInstance::index_t index) { + return good_instance_by_definition.contains_index(index) + ? &good_instance_by_definition.at_index(index) + : nullptr; +} +GoodInstance const* GoodInstanceManager::get_good_instance_by_index(typename GoodInstance::index_t index) const { + return good_instance_by_definition.contains_index(index) + ? &good_instance_by_definition.at_index(index) + : nullptr; +} +GoodInstance& GoodInstanceManager::get_good_instance_by_definition(GoodDefinition const& good_definition) { + return good_instance_by_definition.at(good_definition); +} +GoodInstance const& GoodInstanceManager::get_good_instance_by_definition(GoodDefinition const& good_definition) const { + return good_instance_by_definition.at(good_definition); +} + +void GoodInstanceManager::enable_good(GoodDefinition const& good) { + get_good_instance_by_definition(good).is_available = true; +} diff --git a/src/openvic-simulation/economy/GoodInstanceManager.hpp b/src/openvic-simulation/economy/GoodInstanceManager.hpp new file mode 100644 index 000000000..3d15760b0 --- /dev/null +++ b/src/openvic-simulation/economy/GoodInstanceManager.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/types/HasIndex.hpp" +#include "openvic-simulation/types/IndexedFlatMap.hpp" + +namespace OpenVic { + struct GoodDefinitionManager; + + struct GoodInstanceManager { + private: + GoodDefinitionManager const& good_definition_manager; + OV_IFLATMAP_PROPERTY(GoodDefinition, GoodInstance, good_instance_by_definition); + + public: + GoodInstanceManager( + GoodDefinitionManager const& new_good_definition_manager, + GameRulesManager const& game_rules_manager + ); + + constexpr forwardable_span get_good_instances() { + return good_instance_by_definition.get_values(); + } + + constexpr forwardable_span get_good_instances() const { + return good_instance_by_definition.get_values(); + } + GoodInstance* get_good_instance_by_identifier(std::string_view identifier); + GoodInstance const* get_good_instance_by_identifier(std::string_view identifier) const; + GoodInstance* get_good_instance_by_index(typename GoodInstance::index_t index); + GoodInstance const* get_good_instance_by_index(typename GoodInstance::index_t index) const; + GoodInstance& get_good_instance_by_definition(GoodDefinition const& good_definition); + + void enable_good(GoodDefinition const& good); + }; +} diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp index c2c441f3f..451212736 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp @@ -12,6 +12,7 @@ #include "openvic-simulation/defines/EconomyDefines.hpp" #include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/economy/GoodInstanceManager.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" #include "openvic-simulation/economy/trading/MarketInstance.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" diff --git a/src/openvic-simulation/economy/production/ProductionType.cpp b/src/openvic-simulation/economy/production/ProductionType.cpp index b9a57f14e..0de190e2c 100644 --- a/src/openvic-simulation/economy/production/ProductionType.cpp +++ b/src/openvic-simulation/economy/production/ProductionType.cpp @@ -1,18 +1,11 @@ #include "ProductionType.hpp" -#include - -#include "openvic-simulation/core/error/ErrorMacros.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/misc/GameRulesManager.hpp" -#include "openvic-simulation/population/PopManager.hpp" #include "openvic-simulation/population/PopSize.hpp" #include "openvic-simulation/population/PopType.hpp" -#include "openvic-simulation/types/TypedIndices.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; ProductionType::ProductionType( GameRulesManager const& new_game_rules_manager, @@ -81,348 +74,3 @@ bool ProductionType::parse_scripts(DefinitionManager const& definition_manager) } return ret; } - -ProductionType const* ProductionTypeManager::get_good_to_rgo_production_type(GoodDefinition const& key) const { - return good_to_rgo_production_type.at(key); -} - -node_callback_t ProductionTypeManager::_expect_job( - GoodDefinitionManager const& good_definition_manager, - PopManager const& pop_manager, - NodeTools::callback_t< - pop_type_index_t, - Job::effect_t, - fixed_point_t, - fixed_point_t - > emplace_callback -) { - return [this, &good_definition_manager, &pop_manager, emplace_callback](ast::NodeCPtr node) mutable -> bool { - using enum Job::effect_t; - - std::string_view pop_type_identifier {}; - Job::effect_t effect_type { THROUGHPUT }; - fixed_point_t effect_multiplier = 1, desired_workforce_share = 1; - - static const string_map_t effect_map = { - { "input", INPUT }, { "output", OUTPUT }, { "throughput", THROUGHPUT } - }; - - if(!expect_dictionary_keys( - "poptype", ONE_EXACTLY, expect_identifier(assign_variable_callback(pop_type_identifier)), - "effect", ONE_EXACTLY, expect_identifier(expect_mapped_string(effect_map, assign_variable_callback(effect_type))), - "effect_multiplier", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(effect_multiplier)), - "amount", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(desired_workforce_share)) - )(node)) { - return false; - } - - PopType const* const found_pop_type = pop_manager.get_pop_type_by_identifier(pop_type_identifier); - if (found_pop_type == nullptr) { - return pop_type_identifier == "artisan" // Victoria 2 misconfiguration - ? true - : false; - } - return emplace_callback(found_pop_type->index, effect_type, effect_multiplier, desired_workforce_share); - }; -} - -node_callback_t ProductionTypeManager::_expect_job_list( - GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, - callback_t&&> callback -) { - return [this, &good_definition_manager, &pop_manager, callback](ast::NodeCPtr node) mutable -> bool { - memory::vector jobs; - bool ret = expect_list( - _expect_job( - good_definition_manager, - pop_manager, - vector_emplace_callback(jobs) - ) - )(node); - ret &= callback(std::move(jobs)); - return ret; - }; -} - -bool ProductionTypeManager::add_production_type( - GameRulesManager const& game_rules_manager, - TypedSpan pop_types, - const std::string_view identifier, - std::optional&& owner_before_move, - memory::vector&& jobs, - const ProductionType::template_type_t template_type, - const pop_size_t base_workforce_size, - fixed_point_map_t&& input_goods, - GoodDefinition const* const output_good, - const fixed_point_t base_output_quantity, - memory::vector&& bonuses, - fixed_point_map_t&& maintenance_requirements, - const bool is_coastal, - const bool is_farm, - const bool is_mine -) { - if (identifier.empty()) { - spdlog::error_s("Invalid production type identifier - empty!"); - return false; - } - - if (base_workforce_size <= 0) { - spdlog::error_s("Base workforce size ('workforce') for production type {} was 0 or unset!", identifier); - return false; - } - - if (base_output_quantity <= 0) { - spdlog::error_s("Base output quantity ('value') for production type {} was 0 or unset!", identifier); - return false; - } - - if (output_good == nullptr) { - spdlog::error_s("Output good for production type {} was null!", identifier); - return false; - } - - using enum ProductionType::template_type_t; - - if (template_type == ARTISAN) { - if (owner_before_move.has_value()) { - spdlog::warn_s( - "Artisanal production type {} should not have an owner - it is being ignored.", identifier - ); - owner_before_move.reset(); - } - - if (!jobs.empty()) { - spdlog::warn_s( - "Artisanal production type {} should not have employees - {} are being ignored.", - identifier, jobs.size() - ); - jobs.clear(); - } - } else { - if (!owner_before_move.has_value()) { - spdlog::error_s("Production type {} is missing an owner.", identifier); - return false; - } - - if (jobs.empty()) { - spdlog::error_s("Production type {} lacks jobs ('employees').", identifier); - return false; - } - } - - const bool ret = production_types.emplace_item( - identifier, - game_rules_manager, identifier, std::move(owner_before_move), std::move(jobs), template_type, base_workforce_size, std::move(input_goods), *output_good, - base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine - ); - - if (ret && (template_type == RGO)) { - ProductionType const& production_type = production_types.back(); - - ProductionType const*& current_rgo_pt = good_to_rgo_production_type.at(*output_good); - if (current_rgo_pt == nullptr || ( - (is_farm && !is_mine) - && (!current_rgo_pt->_is_farm || current_rgo_pt->_is_mine) - )) { - // pure farms are preferred over alternatives. Use first pt otherwise. - current_rgo_pt = &get_back_production_type(); - } - //else ignore, we already have an rgo pt - - std::optional const& owner_after_move = production_type.owner; - if (rgo_owner_sprite <= 0 - && template_type == RGO - && owner_after_move.has_value() - ) { - /* Set rgo owner sprite to that of the first RGO owner we find. */ - rgo_owner_sprite = pop_types[owner_after_move->pop_type_index].sprite; - } - } - - return ret; -} - -bool ProductionTypeManager::load_production_types_file( - GameRulesManager const& game_rules_manager, - GoodDefinitionManager const& good_definition_manager, - PopManager const& pop_manager, - ovdl::v2script::Parser const& parser -) { - using namespace std::string_view_literals; - auto template_symbol = parser.find_intern("template"sv); - if (!template_symbol) { - spdlog::error_s("template could not be interned."); - } - auto output_goods_symbol = parser.find_intern("output_goods"sv); - if (!output_goods_symbol) { - spdlog::error_s("output_goods could not be interned."); - } - - size_t expected_types = 0; - - /* Pass #1: find and store template identifiers */ - ordered_set templates; - ordered_map template_target_map; - bool ret = expect_dictionary( - [this, &expected_types, &templates, &template_target_map, &template_symbol, &output_goods_symbol] - (std::string_view key, ast::NodeCPtr value) -> bool { - expected_types++; - - std::string_view template_id = ""; - bool has_found_template = false; - const bool is_parsing_template_success = expect_key( - template_symbol, - expect_identifier(assign_variable_callback(template_id)), - &has_found_template - )(value); - - if (has_found_template) { - OV_ERR_FAIL_COND_V_MSG( - !is_parsing_template_success, - false, - memory::fmt::format("Failed get template identifier for {}", key) - ); - templates.emplace(template_id); - template_target_map.emplace(key, template_id); - } else { - bool has_found_output_goods = false; - expect_key( - output_goods_symbol, - success_callback, - &has_found_output_goods - )(value); - if (!has_found_output_goods) { - //assume it's a template - templates.emplace(key); - } - } - - return true; - } - )(parser.get_file_node()); - - /* Pass #2: create and populate the template map */ - ordered_map template_node_map; - ret &= expect_dictionary( - [this, &expected_types, &templates, &template_node_map](std::string_view key, ast::NodeCPtr value) -> bool { - if (templates.contains(key)) { - template_node_map.emplace(key, value); - expected_types--; - } - return true; - } - )(parser.get_file_node()); - - /* Pass #3: actually load production types */ - good_to_rgo_production_type = std::move(decltype(good_to_rgo_production_type){good_definition_manager.get_good_definitions()}); - - reserve_more_production_types(expected_types); - ret &= expect_dictionary( - [this, &game_rules_manager, &good_definition_manager, &pop_manager, &template_target_map, &template_node_map]( - std::string_view key, ast::NodeCPtr node) -> bool { - using enum ProductionType::template_type_t; - - if (template_node_map.contains(key)) { - return true; - } - - std::optional owner; - memory::vector jobs; - ProductionType::template_type_t template_type { FACTORY }; - GoodDefinition const* output_good = nullptr; - pop_size_t base_workforce_size = 0; - fixed_point_map_t input_goods, maintenance_requirements; - fixed_point_t base_output_quantity = 0; - memory::vector bonuses; - bool is_coastal = false, is_farm = false, is_mine = false; - - bool ret = true; - - static const string_map_t template_type_map = { - { "factory", FACTORY }, { "rgo", RGO }, { "artisan", ARTISAN } - }; - - auto parse_node = expect_dictionary_keys( - "template", ZERO_OR_ONE, success_callback, /* Already parsed using expect_key in Pass #1 above. */ - "bonus", ZERO_OR_MORE, [&bonuses](ast::NodeCPtr bonus_node) -> bool { - using enum scope_type_t; - - ConditionScript trigger { STATE, NO_SCOPE, NO_SCOPE }; - fixed_point_t bonus_value {}; - - const bool ret = expect_dictionary_keys( - "trigger", ONE_EXACTLY, trigger.expect_script(), - "value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(bonus_value)) - )(bonus_node); - - bonuses.emplace_back(std::move(trigger), bonus_value); - - return ret; - }, - "owner", ZERO_OR_ONE, _expect_job(good_definition_manager, pop_manager, emplace_opt_callback(owner)), - "employees", ZERO_OR_ONE, _expect_job_list(good_definition_manager, pop_manager, move_variable_callback(jobs)), - "type", ZERO_OR_ONE, - expect_identifier(expect_mapped_string(template_type_map, assign_variable_callback(template_type))), - "workforce", ZERO_OR_ONE, expect_strong_typedef(assign_variable_callback(base_workforce_size)), - "input_goods", ZERO_OR_ONE, - good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(input_goods)), - "output_goods", ZERO_OR_ONE, - good_definition_manager.expect_good_definition_identifier(assign_variable_callback_pointer(output_good)), - "value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(base_output_quantity)), - "efficiency", ZERO_OR_ONE, good_definition_manager.expect_good_definition_decimal_map( - move_variable_callback(maintenance_requirements) - ), - "is_coastal", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_coastal)), - "farm", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_farm)), - "mine", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_mine)) - ); - - /* Check if this ProductionType has a template, and if so parse it. */ - { - const typename decltype(template_target_map)::const_iterator target_it = template_target_map.find(key); - if (target_it != template_target_map.end()) { - const std::string_view template_id = target_it->second; - const typename decltype(template_node_map)::const_iterator node_it = template_node_map.find(template_id); - if (node_it != template_node_map.end()) { - ret &= parse_node(node_it->second); - } else { - spdlog::error_s( - "Missing template {} for production type {}!", - template_id, key - ); - ret = false; - } - } - } - - /* Parse the ProductionType's own entries, over those of its template if necessary. */ - ret &= parse_node(node); - - ret &= add_production_type( - game_rules_manager, - pop_manager.get_pop_types(), - key, std::move(owner), std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_good, - base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine - ); - - return ret; - } - )(parser.get_file_node()); - - production_types.lock(); - - if (rgo_owner_sprite <= 0) { - spdlog::error_s("No RGO owner pop type sprite found!"); - ret = false; - } - - return ret; -} - -bool ProductionTypeManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - for (ProductionType& production_type : production_types.get_items()) { - ret &= production_type.parse_scripts(definition_manager); - } - return ret; -} diff --git a/src/openvic-simulation/economy/production/ProductionType.hpp b/src/openvic-simulation/economy/production/ProductionType.hpp index ba95c5854..aac087095 100644 --- a/src/openvic-simulation/economy/production/ProductionType.hpp +++ b/src/openvic-simulation/economy/production/ProductionType.hpp @@ -1,16 +1,11 @@ #pragma once -#include - #include "openvic-simulation/core/memory/Vector.hpp" #include "openvic-simulation/population/PopSize.hpp" #include "openvic-simulation/scripts/ConditionScript.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/types/IndexedFlatMap.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" -#include "openvic-simulation/types/PopSprite.hpp" +#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/types/TypedIndices.hpp" -#include "openvic-simulation/types/TypedSpan.hpp" namespace OpenVic { struct PopType; @@ -98,56 +93,4 @@ namespace OpenVic { bool is_valid_for_factory_in(State& state) const; ProductionType(ProductionType&&) = default; }; - - struct ProductionTypeManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(production_type); - pop_sprite_t PROPERTY(rgo_owner_sprite, 0); - OV_IFLATMAP_PROPERTY(GoodDefinition, ProductionType const*, good_to_rgo_production_type); - - NodeTools::node_callback_t _expect_job( - GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, - NodeTools::callback_t< - pop_type_index_t, - Job::effect_t, - fixed_point_t, - fixed_point_t - > emplace_callback - ); - NodeTools::node_callback_t _expect_job_list( - GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, - NodeTools::callback_t&&> callback - ); - - public: - constexpr ProductionTypeManager() - : good_to_rgo_production_type { decltype(good_to_rgo_production_type){create_empty} } {} - - bool add_production_type( - GameRulesManager const& game_rules_manager, - TypedSpan pop_types, - const std::string_view identifier, - std::optional&& owner, - memory::vector&& jobs, - const ProductionType::template_type_t template_type, - const pop_size_t base_workforce_size, - fixed_point_map_t&& input_goods, - GoodDefinition const* const output_good, - const fixed_point_t base_output_quantity, - memory::vector&& bonuses, - fixed_point_map_t&& maintenance_requirements, - const bool is_coastal, - const bool is_farm, - const bool is_mine - ); - - bool load_production_types_file( - GameRulesManager const& game_rules_manager, - GoodDefinitionManager const& good_definition_manager, - PopManager const& pop_manager, - ovdl::v2script::Parser const& parser - ); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } \ No newline at end of file diff --git a/src/openvic-simulation/economy/production/ProductionTypeManager.cpp b/src/openvic-simulation/economy/production/ProductionTypeManager.cpp new file mode 100644 index 000000000..8006ac945 --- /dev/null +++ b/src/openvic-simulation/economy/production/ProductionTypeManager.cpp @@ -0,0 +1,360 @@ +#include "ProductionTypeManager.hpp" + +#include + +#include "openvic-simulation/core/error/ErrorMacros.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/GoodDefinitionManager.hpp" +#include "openvic-simulation/misc/GameRulesManager.hpp" +#include "openvic-simulation/population/PopManager.hpp" +#include "openvic-simulation/population/PopSize.hpp" +#include "openvic-simulation/population/PopType.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +ProductionType const* ProductionTypeManager::get_good_to_rgo_production_type(GoodDefinition const& key) const { + return good_to_rgo_production_type.at(key); +} + +NodeTools::node_callback_t ProductionTypeManager::_expect_job( + GoodDefinitionManager const& good_definition_manager, + PopManager const& pop_manager, + NodeTools::callback_t< + pop_type_index_t, + Job::effect_t, + fixed_point_t, + fixed_point_t + > emplace_callback +) { + return [this, &good_definition_manager, &pop_manager, emplace_callback](ovdl::v2script::ast::Node const* node) mutable -> bool { + using enum Job::effect_t; + + std::string_view pop_type_identifier {}; + Job::effect_t effect_type { THROUGHPUT }; + fixed_point_t effect_multiplier = 1, desired_workforce_share = 1; + + static const string_map_t effect_map = { + { "input", INPUT }, { "output", OUTPUT }, { "throughput", THROUGHPUT } + }; + + if(!expect_dictionary_keys( + "poptype", ONE_EXACTLY, expect_identifier(assign_variable_callback(pop_type_identifier)), + "effect", ONE_EXACTLY, expect_identifier(expect_mapped_string(effect_map, assign_variable_callback(effect_type))), + "effect_multiplier", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(effect_multiplier)), + "amount", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(desired_workforce_share)) + )(node)) { + return false; + } + + PopType const* const found_pop_type = pop_manager.get_pop_type_by_identifier(pop_type_identifier); + if (found_pop_type == nullptr) { + return pop_type_identifier == "artisan" // Victoria 2 misconfiguration + ? true + : false; + } + return emplace_callback(found_pop_type->index, effect_type, effect_multiplier, desired_workforce_share); + }; +} + +NodeTools::node_callback_t ProductionTypeManager::_expect_job_list( + GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, + NodeTools::callback_t&&> callback +) { + return [this, &good_definition_manager, &pop_manager, callback](ovdl::v2script::ast::Node const* node) mutable -> bool { + memory::vector jobs; + bool ret = expect_list( + _expect_job( + good_definition_manager, + pop_manager, + vector_emplace_callback(jobs) + ) + )(node); + ret &= callback(std::move(jobs)); + return ret; + }; +} + +bool ProductionTypeManager::add_production_type( + GameRulesManager const& game_rules_manager, + TypedSpan pop_types, + const std::string_view identifier, + std::optional&& owner_before_move, + memory::vector&& jobs, + const ProductionType::template_type_t template_type, + const pop_size_t base_workforce_size, + fixed_point_map_t&& input_goods, + GoodDefinition const* const output_good, + const fixed_point_t base_output_quantity, + memory::vector&& bonuses, + fixed_point_map_t&& maintenance_requirements, + const bool is_coastal, + const bool is_farm, + const bool is_mine +) { + if (identifier.empty()) { + spdlog::error_s("Invalid production type identifier - empty!"); + return false; + } + + if (base_workforce_size <= 0) { + spdlog::error_s("Base workforce size ('workforce') for production type {} was 0 or unset!", identifier); + return false; + } + + if (base_output_quantity <= 0) { + spdlog::error_s("Base output quantity ('value') for production type {} was 0 or unset!", identifier); + return false; + } + + if (output_good == nullptr) { + spdlog::error_s("Output good for production type {} was null!", identifier); + return false; + } + + using enum ProductionType::template_type_t; + + if (template_type == ARTISAN) { + if (owner_before_move.has_value()) { + spdlog::warn_s( + "Artisanal production type {} should not have an owner - it is being ignored.", identifier + ); + owner_before_move.reset(); + } + + if (!jobs.empty()) { + spdlog::warn_s( + "Artisanal production type {} should not have employees - {} are being ignored.", + identifier, jobs.size() + ); + jobs.clear(); + } + } else { + if (!owner_before_move.has_value()) { + spdlog::error_s("Production type {} is missing an owner.", identifier); + return false; + } + + if (jobs.empty()) { + spdlog::error_s("Production type {} lacks jobs ('employees').", identifier); + return false; + } + } + + const bool ret = production_types.emplace_item( + identifier, + game_rules_manager, identifier, std::move(owner_before_move), std::move(jobs), template_type, base_workforce_size, std::move(input_goods), *output_good, + base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine + ); + + if (ret && (template_type == RGO)) { + ProductionType const& production_type = production_types.back(); + + ProductionType const*& current_rgo_pt = good_to_rgo_production_type.at(*output_good); + if (current_rgo_pt == nullptr || ( + (is_farm && !is_mine) + && (!current_rgo_pt->_is_farm || current_rgo_pt->_is_mine) + )) { + // pure farms are preferred over alternatives. Use first pt otherwise. + current_rgo_pt = &get_back_production_type(); + } + //else ignore, we already have an rgo pt + + std::optional const& owner_after_move = production_type.owner; + if (rgo_owner_sprite <= 0 + && template_type == RGO + && owner_after_move.has_value() + ) { + /* Set rgo owner sprite to that of the first RGO owner we find. */ + rgo_owner_sprite = pop_types[owner_after_move->pop_type_index].sprite; + } + } + + return ret; +} + +bool ProductionTypeManager::load_production_types_file( + GameRulesManager const& game_rules_manager, + GoodDefinitionManager const& good_definition_manager, + PopManager const& pop_manager, + ovdl::v2script::Parser const& parser +) { + using namespace std::string_view_literals; + auto template_symbol = parser.find_intern("template"sv); + if (!template_symbol) { + spdlog::error_s("template could not be interned."); + } + auto output_goods_symbol = parser.find_intern("output_goods"sv); + if (!output_goods_symbol) { + spdlog::error_s("output_goods could not be interned."); + } + + size_t expected_types = 0; + + /* Pass #1: find and store template identifiers */ + ordered_set templates; + ordered_map template_target_map; + bool ret = expect_dictionary( + [this, &expected_types, &templates, &template_target_map, &template_symbol, &output_goods_symbol] + (std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + expected_types++; + + std::string_view template_id = ""; + bool has_found_template = false; + const bool is_parsing_template_success = expect_key( + template_symbol, + expect_identifier(assign_variable_callback(template_id)), + &has_found_template + )(value); + + if (has_found_template) { + OV_ERR_FAIL_COND_V_MSG( + !is_parsing_template_success, + false, + memory::fmt::format("Failed get template identifier for {}", key) + ); + templates.emplace(template_id); + template_target_map.emplace(key, template_id); + } else { + bool has_found_output_goods = false; + expect_key( + output_goods_symbol, + success_callback, + &has_found_output_goods + )(value); + if (!has_found_output_goods) { + //assume it's a template + templates.emplace(key); + } + } + + return true; + } + )(parser.get_file_node()); + + /* Pass #2: create and populate the template map */ + ordered_map template_node_map; + ret &= expect_dictionary( + [this, &expected_types, &templates, &template_node_map](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + if (templates.contains(key)) { + template_node_map.emplace(key, value); + expected_types--; + } + return true; + } + )(parser.get_file_node()); + + /* Pass #3: actually load production types */ + good_to_rgo_production_type = std::move(decltype(good_to_rgo_production_type){good_definition_manager.get_good_definitions()}); + + reserve_more_production_types(expected_types); + ret &= expect_dictionary( + [this, &game_rules_manager, &good_definition_manager, &pop_manager, &template_target_map, &template_node_map]( + std::string_view key, ovdl::v2script::ast::Node const* node) -> bool { + using enum ProductionType::template_type_t; + + if (template_node_map.contains(key)) { + return true; + } + + std::optional owner; + memory::vector jobs; + ProductionType::template_type_t template_type { FACTORY }; + GoodDefinition const* output_good = nullptr; + pop_size_t base_workforce_size = 0; + fixed_point_map_t input_goods, maintenance_requirements; + fixed_point_t base_output_quantity = 0; + memory::vector bonuses; + bool is_coastal = false, is_farm = false, is_mine = false; + + bool ret = true; + + static const string_map_t template_type_map = { + { "factory", FACTORY }, { "rgo", RGO }, { "artisan", ARTISAN } + }; + + auto parse_node = expect_dictionary_keys( + "template", ZERO_OR_ONE, success_callback, /* Already parsed using expect_key in Pass #1 above. */ + "bonus", ZERO_OR_MORE, [&bonuses](ovdl::v2script::ast::Node const* bonus_node) -> bool { + using enum scope_type_t; + + ConditionScript trigger { STATE, NO_SCOPE, NO_SCOPE }; + fixed_point_t bonus_value {}; + + const bool ret = expect_dictionary_keys( + "trigger", ONE_EXACTLY, trigger.expect_script(), + "value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(bonus_value)) + )(bonus_node); + + bonuses.emplace_back(std::move(trigger), bonus_value); + + return ret; + }, + "owner", ZERO_OR_ONE, _expect_job(good_definition_manager, pop_manager, emplace_opt_callback(owner)), + "employees", ZERO_OR_ONE, _expect_job_list(good_definition_manager, pop_manager, move_variable_callback(jobs)), + "type", ZERO_OR_ONE, + expect_identifier(expect_mapped_string(template_type_map, assign_variable_callback(template_type))), + "workforce", ZERO_OR_ONE, expect_strong_typedef(assign_variable_callback(base_workforce_size)), + "input_goods", ZERO_OR_ONE, + good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(input_goods)), + "output_goods", ZERO_OR_ONE, + good_definition_manager.expect_good_definition_identifier(assign_variable_callback_pointer(output_good)), + "value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(base_output_quantity)), + "efficiency", ZERO_OR_ONE, good_definition_manager.expect_good_definition_decimal_map( + move_variable_callback(maintenance_requirements) + ), + "is_coastal", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_coastal)), + "farm", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_farm)), + "mine", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_mine)) + ); + + /* Check if this ProductionType has a template, and if so parse it. */ + { + const typename decltype(template_target_map)::const_iterator target_it = template_target_map.find(key); + if (target_it != template_target_map.end()) { + const std::string_view template_id = target_it->second; + const typename decltype(template_node_map)::const_iterator node_it = template_node_map.find(template_id); + if (node_it != template_node_map.end()) { + ret &= parse_node(node_it->second); + } else { + spdlog::error_s( + "Missing template {} for production type {}!", + template_id, key + ); + ret = false; + } + } + } + + /* Parse the ProductionType's own entries, over those of its template if necessary. */ + ret &= parse_node(node); + + ret &= add_production_type( + game_rules_manager, + pop_manager.get_pop_types(), + key, std::move(owner), std::move(jobs), template_type, base_workforce_size, std::move(input_goods), output_good, + base_output_quantity, std::move(bonuses), std::move(maintenance_requirements), is_coastal, is_farm, is_mine + ); + + return ret; + } + )(parser.get_file_node()); + + production_types.lock(); + + if (rgo_owner_sprite <= 0) { + spdlog::error_s("No RGO owner pop type sprite found!"); + ret = false; + } + + return ret; +} + +bool ProductionTypeManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (ProductionType& production_type : production_types.get_items()) { + ret &= production_type.parse_scripts(definition_manager); + } + return ret; +} diff --git a/src/openvic-simulation/economy/production/ProductionTypeManager.hpp b/src/openvic-simulation/economy/production/ProductionTypeManager.hpp new file mode 100644 index 000000000..ea8579253 --- /dev/null +++ b/src/openvic-simulation/economy/production/ProductionTypeManager.hpp @@ -0,0 +1,70 @@ +#pragma once + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" +#include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/population/PopSize.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/IndexedFlatMap.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/PopSprite.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/TypedSpan.hpp" + +namespace ovdl::v2script { + class Parser; +} + +namespace OpenVic { + struct ProductionTypeManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(production_type); + pop_sprite_t PROPERTY(rgo_owner_sprite, 0); + OV_IFLATMAP_PROPERTY(GoodDefinition, ProductionType const*, good_to_rgo_production_type); + + NodeTools::node_callback_t _expect_job( + GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, + NodeTools::callback_t< + pop_type_index_t, + Job::effect_t, + fixed_point_t, + fixed_point_t + > emplace_callback + ); + NodeTools::node_callback_t _expect_job_list( + GoodDefinitionManager const& good_definition_manager, PopManager const& pop_manager, + NodeTools::callback_t&&> callback + ); + + public: + constexpr ProductionTypeManager() + : good_to_rgo_production_type { decltype(good_to_rgo_production_type){create_empty} } {} + + bool add_production_type( + GameRulesManager const& game_rules_manager, + TypedSpan pop_types, + const std::string_view identifier, + std::optional&& owner, + memory::vector&& jobs, + const ProductionType::template_type_t template_type, + const pop_size_t base_workforce_size, + fixed_point_map_t&& input_goods, + GoodDefinition const* const output_good, + const fixed_point_t base_output_quantity, + memory::vector&& bonuses, + fixed_point_map_t&& maintenance_requirements, + const bool is_coastal, + const bool is_farm, + const bool is_mine + ); + + bool load_production_types_file( + GameRulesManager const& game_rules_manager, + GoodDefinitionManager const& good_definition_manager, + PopManager const& pop_manager, + ovdl::v2script::Parser const& parser + ); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp index 8bc0435ba..eb1ee0c43 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp @@ -290,8 +290,9 @@ fixed_point_t ResourceGatheringOperation::produce() { + location.get_modifier_effect_value(*modifier_effect_cache.get_local_rgo_output()); if (production_type.get_is_farm_for_tech()) { - const fixed_point_t farm_rgo_throughput_and_output = - location.get_modifier_effect_value(*modifier_effect_cache.get_farm_rgo_throughput_and_output()); + const fixed_point_t farm_rgo_throughput_and_output = location.get_modifier_effect_value( + *modifier_effect_cache.get_farm_rgo_throughput_and_output() + ); throughput_multiplier += farm_rgo_throughput_and_output; output_multiplier += farm_rgo_throughput_and_output; } @@ -302,8 +303,9 @@ fixed_point_t ResourceGatheringOperation::produce() { } if (production_type.get_is_mine_for_tech()) { - const fixed_point_t mine_rgo_throughput_and_output = - location.get_modifier_effect_value(*modifier_effect_cache.get_mine_rgo_throughput_and_output()); + const fixed_point_t mine_rgo_throughput_and_output = location.get_modifier_effect_value( + *modifier_effect_cache.get_mine_rgo_throughput_and_output() + ); throughput_multiplier += mine_rgo_throughput_and_output; output_multiplier += mine_rgo_throughput_and_output; } @@ -331,9 +333,16 @@ fixed_point_t ResourceGatheringOperation::produce() { const fixed_point_t effect_multiplier = job.effect_multiplier; const fixed_point_t amount = job.amount; const fixed_point_t effect = effect_multiplier != fixed_point_t::_1 - && fp::from_fraction(employees_of_type, max_employee_count_cache) > amount - ? effect_multiplier * amount //special Vic2 logic - : fp::mul_div(effect_multiplier, employees_of_type, max_employee_count_cache); + && fp::from_fraction( + employees_of_type, + max_employee_count_cache + ) > amount + ? effect_multiplier * amount //special Vic2 logic + : fp::mul_div( + effect_multiplier, + employees_of_type, + max_employee_count_cache + ); switch (job.effect_type) { case Job::effect_t::OUTPUT: diff --git a/src/openvic-simulation/economy/trading/MarketInstance.cpp b/src/openvic-simulation/economy/trading/MarketInstance.cpp index b39cea789..4595114f6 100644 --- a/src/openvic-simulation/economy/trading/MarketInstance.cpp +++ b/src/openvic-simulation/economy/trading/MarketInstance.cpp @@ -2,7 +2,7 @@ #include "openvic-simulation/defines/CountryDefines.hpp" #include "openvic-simulation/economy/GoodDefinition.hpp" -#include "openvic-simulation/economy/GoodInstance.hpp" +#include "openvic-simulation/economy/GoodInstanceManager.hpp" #include "openvic-simulation/economy/trading/BuyUpToOrder.hpp" #include "openvic-simulation/economy/trading/MarketSellOrder.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -78,7 +78,7 @@ void MarketInstance::place_market_sell_order(MarketSellOrder&& market_sell_order } void MarketInstance::execute_orders() { - thread_pool.process_good_execute_orders(); + thread_pool.process(work_t::GOOD_EXECUTE_ORDERS); } void MarketInstance::record_price_history() { diff --git a/src/openvic-simulation/history/Bookmark.cpp b/src/openvic-simulation/history/Bookmark.cpp index d2fb7c6bc..e772a2437 100644 --- a/src/openvic-simulation/history/Bookmark.cpp +++ b/src/openvic-simulation/history/Bookmark.cpp @@ -1,16 +1,8 @@ #include "Bookmark.hpp" -#include - #include -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/types/Date.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/utility/Logger.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; Bookmark::Bookmark( index_t new_index, @@ -24,53 +16,3 @@ Bookmark::Bookmark( description { new_description }, date { new_date }, initial_camera_position { new_initial_camera_position } {} - -bool BookmarkManager::add_bookmark( - std::string_view name, std::string_view description, Date date, fvec2_t initial_camera_position -) { - return bookmarks.emplace_item( - name, - Bookmark::index_t { bookmarks.size() }, name, description, date, initial_camera_position - ); -} - -bool BookmarkManager::load_bookmark_file(fixed_point_t map_height, ast::NodeCPtr root) { - const bool ret = expect_dictionary_reserve_length( - bookmarks, - [this, map_height](std::string_view key, ast::NodeCPtr value) -> bool { - if (key != "bookmark") { - spdlog::error_s("Invalid bookmark declaration {}", key); - return false; - } - - std::string_view name, description; - Date date; - fvec2_t initial_camera_position; - - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name)), - "desc", ONE_EXACTLY, expect_string(assign_variable_callback(description)), - "date", ONE_EXACTLY, expect_date(assign_variable_callback(date)), - "cameraX", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(initial_camera_position.x)), - "cameraY", ONE_EXACTLY, - expect_fixed_point(flip_height_callback(assign_variable_callback(initial_camera_position.y), map_height)) - )(value); - - ret &= add_bookmark(name, description, date, initial_camera_position); - return ret; - } - )(root); - lock_bookmarks(); - - return ret; -} - -Date BookmarkManager::get_last_bookmark_date() const { - Date ret {}; - for (Bookmark const& bookmark : get_bookmarks()) { - if (bookmark.date > ret) { - ret = bookmark.date; - } - } - return ret; -} diff --git a/src/openvic-simulation/history/Bookmark.hpp b/src/openvic-simulation/history/Bookmark.hpp index 4584d628e..673a2ca1c 100644 --- a/src/openvic-simulation/history/Bookmark.hpp +++ b/src/openvic-simulation/history/Bookmark.hpp @@ -1,13 +1,12 @@ #pragma once -#include - #include +#include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/Vector.hpp" namespace OpenVic { struct Bookmark : HasIdentifier, HasIndex { @@ -29,19 +28,6 @@ namespace OpenVic { ); Bookmark(Bookmark&&) = default; }; - - struct BookmarkManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(bookmark); - - public: - bool add_bookmark( - std::string_view name, std::string_view description, Date date, fvec2_t initial_camera_position - ); - bool load_bookmark_file(fixed_point_t map_height, ast::NodeCPtr root); - - Date get_last_bookmark_date() const; - }; } template T, typename char_type> diff --git a/src/openvic-simulation/history/BookmarkManager.cpp b/src/openvic-simulation/history/BookmarkManager.cpp new file mode 100644 index 000000000..9246d0cc1 --- /dev/null +++ b/src/openvic-simulation/history/BookmarkManager.cpp @@ -0,0 +1,61 @@ +#include "BookmarkManager.hpp" + +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/utility/Logger.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool BookmarkManager::add_bookmark( + std::string_view name, std::string_view description, Date date, fvec2_t initial_camera_position +) { + return bookmarks.emplace_item( + name, + Bookmark::index_t { bookmarks.size() }, name, description, date, initial_camera_position + ); +} + +bool BookmarkManager::load_bookmark_file(fixed_point_t map_height, ovdl::v2script::ast::Node const* root) { + const bool ret = expect_dictionary_reserve_length( + bookmarks, + [this, map_height](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + if (key != "bookmark") { + spdlog::error_s("Invalid bookmark declaration {}", key); + return false; + } + + std::string_view name, description; + Date date; + fvec2_t initial_camera_position; + + bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(name)), + "desc", ONE_EXACTLY, expect_string(assign_variable_callback(description)), + "date", ONE_EXACTLY, expect_date(assign_variable_callback(date)), + "cameraX", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(initial_camera_position.x)), + "cameraY", ONE_EXACTLY, + expect_fixed_point(flip_height_callback(assign_variable_callback(initial_camera_position.y), map_height)) + )(value); + + ret &= add_bookmark(name, description, date, initial_camera_position); + return ret; + } + )(root); + lock_bookmarks(); + + return ret; +} + +Date BookmarkManager::get_last_bookmark_date() const { + Date ret {}; + for (Bookmark const& bookmark : get_bookmarks()) { + if (bookmark.date > ret) { + ret = bookmark.date; + } + } + return ret; +} diff --git a/src/openvic-simulation/history/BookmarkManager.hpp b/src/openvic-simulation/history/BookmarkManager.hpp new file mode 100644 index 000000000..9cdd99261 --- /dev/null +++ b/src/openvic-simulation/history/BookmarkManager.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include "openvic-simulation/history/Bookmark.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct BookmarkManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(bookmark); + + public: + bool add_bookmark( + std::string_view name, std::string_view description, Date date, fvec2_t initial_camera_position + ); + bool load_bookmark_file(fixed_point_t map_height, ovdl::v2script::ast::Node const* root); + + Date get_last_bookmark_date() const; + }; +} diff --git a/src/openvic-simulation/history/CountryHistory.cpp b/src/openvic-simulation/history/CountryHistory.cpp index 8a523256e..89da34bed 100644 --- a/src/openvic-simulation/history/CountryHistory.cpp +++ b/src/openvic-simulation/history/CountryHistory.cpp @@ -45,7 +45,7 @@ static constexpr auto _flag_callback(string_map_t& flags, bool value) { bool CountryHistoryMap::_load_history_entry( DefinitionManager const& definition_manager, Dataloader const& dataloader, DeploymentManager& deployment_manager, - CountryHistoryEntry& entry, ast::NodeCPtr root + CountryHistoryEntry& entry, ovdl::v2script::ast::Node const* root ) { PoliticsManager const& politics_manager = definition_manager.get_politics_manager(); IssueManager const& issue_manager = politics_manager.get_issue_manager(); @@ -94,7 +94,7 @@ bool CountryHistoryMap::_load_history_entry( ]( template_key_map_t const& key_map, std::string_view key, - ast::NodeCPtr value + ovdl::v2script::ast::Node const* value ) -> bool { ReformGroup const* reform_group_ptr = issue_manager.get_reform_group_by_identifier(key); if (reform_group_ptr != nullptr) { @@ -157,7 +157,7 @@ bool CountryHistoryMap::_load_history_entry( ), "civilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(entry.civilised)), "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.prestige)), - "ruling_party", ZERO_OR_ONE, [this, &entry](ast::NodeCPtr value) -> bool { + "ruling_party", ZERO_OR_ONE, [this, &entry](ovdl::v2script::ast::Node const* value) -> bool { country.expect_party_identifier(assign_variable_callback_pointer_opt(entry.ruling_party), true)(value); if (!entry.ruling_party.has_value()) { std::string_view def {}; @@ -193,7 +193,7 @@ bool CountryHistoryMap::_load_history_entry( }, "last_election", ZERO_OR_ONE, expect_date(assign_variable_callback(entry.last_election)), "upper_house", ZERO_OR_ONE, politics_manager.get_ideology_manager().expect_ideology_dictionary( - [&entry](Ideology const& ideology, ast::NodeCPtr value) -> bool { + [&entry](Ideology const& ideology, ovdl::v2script::ast::Node const* value) -> bool { return expect_fixed_point(map_callback(entry.upper_house_proportion_by_ideology, &ideology))(value); } ), @@ -226,13 +226,13 @@ bool CountryHistoryMap::_load_history_entry( "nonstate_consciousness", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(entry.nonstate_consciousness)), "is_releasable_vassal", ZERO_OR_ONE, expect_bool(assign_variable_callback(entry.releasable_vassal)), "decision", ZERO_OR_MORE, decision_manager.expect_decision_identifier(set_callback_pointer(entry.decisions), true), // if a mod lists an invalid decision here (as some hpm-derived mods that remove hpm decisions do) vic2 just ignores it. - "govt_flag", ZERO_OR_MORE, [&entry, &politics_manager](ast::NodeCPtr value) -> bool { + "govt_flag", ZERO_OR_MORE, [&entry, &politics_manager](ovdl::v2script::ast::Node const* value) -> bool { GovernmentTypeManager const& government_type_manager = politics_manager.get_government_type_manager(); GovernmentType const* government_type = nullptr; bool flag_expected = false; bool ret = expect_dictionary( [&entry, &government_type_manager, &government_type, &flag_expected](std::string_view id, - ast::NodeCPtr node) -> bool { + ovdl::v2script::ast::Node const* node) -> bool { if (id == "government") { bool ret = true; if (flag_expected) { @@ -330,7 +330,7 @@ CountryHistoryMap const* CountryHistoryManager::get_country_history(CountryDefin bool CountryHistoryManager::load_country_history_file( DefinitionManager& definition_manager, Dataloader const& dataloader, CountryDefinition const& country, decltype(CountryHistoryMap::ideology_keys) ideology_keys, - decltype(CountryHistoryMap::government_type_keys) government_type_keys, ast::NodeCPtr root + decltype(CountryHistoryMap::government_type_keys) government_type_keys, ovdl::v2script::ast::Node const* root ) { if (locked) { spdlog::error_s("Attempted to load country history file for {} after country history registry was locked!", country); @@ -343,10 +343,16 @@ bool CountryHistoryManager::load_country_history_file( decltype(country_histories)::iterator it = country_histories.find(&country); if (it == country_histories.end()) { - const std::pair result = - country_histories.emplace(&country, CountryHistoryMap { country, ideology_keys, government_type_keys }); - if (result.second) { - it = result.first; + auto const [new_it, result] = country_histories.emplace( + &country, + CountryHistoryMap { + country, + ideology_keys, + government_type_keys + } + ); + if (result) { + it = new_it; } else { spdlog::error_s("Failed to create country history map for country {}", country); return false; diff --git a/src/openvic-simulation/history/CountryHistory.hpp b/src/openvic-simulation/history/CountryHistory.hpp index 1cdb25977..898142c55 100644 --- a/src/openvic-simulation/history/CountryHistory.hpp +++ b/src/openvic-simulation/history/CountryHistory.hpp @@ -90,7 +90,7 @@ namespace OpenVic { memory::unique_ptr _make_entry(Date date) const override; bool _load_history_entry( DefinitionManager const& definition_manager, Dataloader const& dataloader, DeploymentManager& deployment_manager, - CountryHistoryEntry& entry, ast::NodeCPtr root + CountryHistoryEntry& entry, ovdl::v2script::ast::Node const* root ) override; }; @@ -111,7 +111,7 @@ namespace OpenVic { bool load_country_history_file( DefinitionManager& definition_manager, Dataloader const& dataloader, CountryDefinition const& country, decltype(CountryHistoryMap::ideology_keys) ideology_keys, - decltype(CountryHistoryMap::government_type_keys) government_type_keys, ast::NodeCPtr root + decltype(CountryHistoryMap::government_type_keys) government_type_keys, ovdl::v2script::ast::Node const* root ); }; } \ No newline at end of file diff --git a/src/openvic-simulation/history/DiplomaticHistory.cpp b/src/openvic-simulation/history/DiplomaticHistory.cpp index 1b4f20d86..1d7ced5a2 100644 --- a/src/openvic-simulation/history/DiplomaticHistory.cpp +++ b/src/openvic-simulation/history/DiplomaticHistory.cpp @@ -1,5 +1,6 @@ #include "DiplomaticHistory.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/DefinitionManager.hpp" #include "openvic-simulation/history/DiplomaticHistory.hpp" @@ -38,7 +39,7 @@ memory::vector> DiplomaticHistoryManage } bool DiplomaticHistoryManager::load_diplomacy_history_file( - CountryDefinitionManager const& country_definition_manager, ast::NodeCPtr root + CountryDefinitionManager const& country_definition_manager, ovdl::v2script::ast::Node const* root ) { // Default vanilla alliances is 2, 54 is the max I've seen in mods // Eliminates reallocations at the cost of memory usage @@ -51,7 +52,7 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file( reparations.reserve(1); return expect_dictionary_keys( - "alliance", ZERO_OR_MORE, [this, &country_definition_manager](ast::NodeCPtr node) -> bool { + "alliance", ZERO_OR_MORE, [this, &country_definition_manager](ovdl::v2script::ast::Node const* node) -> bool { CountryDefinition const* first = nullptr; CountryDefinition const* second = nullptr; Date start {}; @@ -73,7 +74,7 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file( } return ret; }, - "vassal", ZERO_OR_MORE, [this, &country_definition_manager](ast::NodeCPtr node) -> bool { + "vassal", ZERO_OR_MORE, [this, &country_definition_manager](ovdl::v2script::ast::Node const* node) -> bool { CountryDefinition const* overlord = nullptr; CountryDefinition const* subject = nullptr; Date start {}; @@ -95,7 +96,7 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file( } return ret; }, - "union", ZERO_OR_MORE, [this, &country_definition_manager](ast::NodeCPtr node) -> bool { + "union", ZERO_OR_MORE, [this, &country_definition_manager](ovdl::v2script::ast::Node const* node) -> bool { CountryDefinition const* overlord = nullptr; CountryDefinition const* subject = nullptr; Date start {}; @@ -117,7 +118,7 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file( } return ret; }, - "substate", ZERO_OR_MORE, [this, &country_definition_manager](ast::NodeCPtr node) -> bool { + "substate", ZERO_OR_MORE, [this, &country_definition_manager](ovdl::v2script::ast::Node const* node) -> bool { CountryDefinition const* overlord = nullptr; CountryDefinition const* subject = nullptr; Date start {}; @@ -139,7 +140,7 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file( } return ret; }, - "reparations", ZERO_OR_MORE, [this, &country_definition_manager](ast::NodeCPtr node) -> bool { + "reparations", ZERO_OR_MORE, [this, &country_definition_manager](ovdl::v2script::ast::Node const* node) -> bool { CountryDefinition const* receiver = nullptr; CountryDefinition const* sender = nullptr; Date start {}; @@ -164,7 +165,7 @@ bool DiplomaticHistoryManager::load_diplomacy_history_file( )(root); } -bool DiplomaticHistoryManager::load_war_history_file(DefinitionManager const& definition_manager, ast::NodeCPtr root) { +bool DiplomaticHistoryManager::load_war_history_file(DefinitionManager const& definition_manager, ovdl::v2script::ast::Node const* root) { std::string_view name {}; // Ensures that if ever multithreaded, only one vector is used per thread @@ -189,7 +190,7 @@ bool DiplomaticHistoryManager::load_war_history_file(DefinitionManager const& de bool ret = expect_dictionary_keys_and_default( [&definition_manager, ¤t_date, &name]( - std::string_view key, ast::NodeCPtr node + std::string_view key, ovdl::v2script::ast::Node const* node ) -> bool { bool ret = expect_date_str(assign_variable_callback(current_date))(key); @@ -278,7 +279,7 @@ bool DiplomaticHistoryManager::load_war_history_file(DefinitionManager const& de return participant_to_remove->period.try_set_end(current_date); } ), - "war_goal", ZERO_OR_MORE, [&definition_manager, ¤t_date](ast::NodeCPtr value) -> bool { + "war_goal", ZERO_OR_MORE, [&definition_manager, ¤t_date](ovdl::v2script::ast::Node const* value) -> bool { CountryDefinition const* actor = nullptr; CountryDefinition const* receiver = nullptr; WargoalType const* type = nullptr; diff --git a/src/openvic-simulation/history/DiplomaticHistory.hpp b/src/openvic-simulation/history/DiplomaticHistory.hpp index 3fc829da7..fc5f3f77f 100644 --- a/src/openvic-simulation/history/DiplomaticHistory.hpp +++ b/src/openvic-simulation/history/DiplomaticHistory.hpp @@ -3,7 +3,7 @@ #include #include "openvic-simulation/core/memory/Vector.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" #include "openvic-simulation/history/diplomacy/AllianceHistory.hpp" #include "openvic-simulation/history/diplomacy/ReparationsHistory.hpp" #include "openvic-simulation/history/diplomacy/SubjectHistory.hpp" @@ -58,7 +58,7 @@ namespace OpenVic { * should be checked for by functions that use get_wars() */ [[nodiscard]] memory::vector> get_wars(Date date) const; - bool load_diplomacy_history_file(CountryDefinitionManager const& country_definition_manager, ast::NodeCPtr root); - bool load_war_history_file(DefinitionManager const& definition_manager, ast::NodeCPtr root); + bool load_diplomacy_history_file(CountryDefinitionManager const& country_definition_manager, ovdl::v2script::ast::Node const* root); + bool load_war_history_file(DefinitionManager const& definition_manager, ovdl::v2script::ast::Node const* root); }; } diff --git a/src/openvic-simulation/history/HistoryManager.hpp b/src/openvic-simulation/history/HistoryManager.hpp index 80be9647a..2b84b637d 100644 --- a/src/openvic-simulation/history/HistoryManager.hpp +++ b/src/openvic-simulation/history/HistoryManager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "openvic-simulation/history/Bookmark.hpp" +#include "openvic-simulation/history/BookmarkManager.hpp" #include "openvic-simulation/history/CountryHistory.hpp" #include "openvic-simulation/history/DiplomaticHistory.hpp" #include "openvic-simulation/history/ProvinceHistory.hpp" diff --git a/src/openvic-simulation/history/HistoryMap.hpp b/src/openvic-simulation/history/HistoryMap.hpp index c106b0caa..6284cd26e 100644 --- a/src/openvic-simulation/history/HistoryMap.hpp +++ b/src/openvic-simulation/history/HistoryMap.hpp @@ -34,7 +34,7 @@ namespace OpenVic { ordered_map> PROPERTY(entries); bool _try_load_history_entry( - DefinitionManager const& definition_manager, Args... args, Date date, ast::NodeCPtr root + DefinitionManager const& definition_manager, Args... args, Date date, ovdl::v2script::ast::Node const* root ) { entry_type *const entry = _get_or_make_entry(definition_manager, date); if (entry != nullptr) { @@ -50,10 +50,10 @@ namespace OpenVic { virtual memory::unique_ptr _make_entry(Date date) const = 0; virtual bool _load_history_entry( - DefinitionManager const& definition_manager, Args... args, entry_type& entry, ast::NodeCPtr root + DefinitionManager const& definition_manager, Args... args, entry_type& entry, ovdl::v2script::ast::Node const* root ) = 0; - bool _load_history_file(DefinitionManager const& definition_manager, Args... args, ast::NodeCPtr root) { + bool _load_history_file(DefinitionManager const& definition_manager, Args... args, ovdl::v2script::ast::Node const* root) { return _try_load_history_entry( definition_manager, args..., _HistoryMapHelperFuncs::_get_start_date(definition_manager), root ); @@ -63,10 +63,10 @@ namespace OpenVic { DefinitionManager const& definition_manager, Args... args, Date date, - ast::NodeCPtr root, + ovdl::v2script::ast::Node const* root, NodeTools::template_key_map_t const& key_map, std::string_view key, - ast::NodeCPtr value + ovdl::v2script::ast::Node const* value ) { /* Date blocks (loaded into the corresponding HistoryEntry) */ Date::from_chars_result result; diff --git a/src/openvic-simulation/history/ProvinceHistory.cpp b/src/openvic-simulation/history/ProvinceHistory.cpp index 530c3698e..694ef29e2 100644 --- a/src/openvic-simulation/history/ProvinceHistory.cpp +++ b/src/openvic-simulation/history/ProvinceHistory.cpp @@ -36,12 +36,12 @@ memory::unique_ptr ProvinceHistoryMap::_make_entry(Date da } bool ProvinceHistoryMap::_load_history_entry( - DefinitionManager const& definition_manager, ProvinceHistoryEntry& entry, ast::NodeCPtr root + DefinitionManager const& definition_manager, ProvinceHistoryEntry& entry, ovdl::v2script::ast::Node const* root ) { BuildingTypeManager const& building_type_manager = definition_manager.get_economy_manager().get_building_type_manager(); CountryDefinitionManager const& country_definition_manager = definition_manager.get_country_definition_manager(); - GoodDefinitionManager const& good_definition_manager = - definition_manager.get_economy_manager().get_good_definition_manager(); + EconomyManager const& economy_manager = definition_manager.get_economy_manager(); + GoodDefinitionManager const& good_definition_manager = economy_manager.get_good_definition_manager(); IdeologyManager const& ideology_manager = definition_manager.get_politics_manager().get_ideology_manager(); TerrainTypeManager const& terrain_type_manager = definition_manager.get_map_definition().get_terrain_type_manager(); @@ -90,7 +90,7 @@ bool ProvinceHistoryMap::_load_history_entry( [this, &definition_manager, &building_type_manager, &entry]( template_key_map_t const& key_map, std::string_view key, - ast::NodeCPtr value + ovdl::v2script::ast::Node const* value ) -> bool { // used for province buildings like forts or railroads BuildingType const* const building_type_ptr = building_type_manager.get_building_type_by_identifier(key); @@ -173,7 +173,7 @@ bool ProvinceHistoryMap::_load_history_entry( "terrain", ZERO_OR_ONE, terrain_type_manager.expect_terrain_type_identifier( assign_variable_callback_pointer_opt(entry.terrain_type) ), - "party_loyalty", ZERO_OR_MORE, [&ideology_manager, &entry](ast::NodeCPtr node) -> bool { + "party_loyalty", ZERO_OR_MORE, [&ideology_manager, &entry](ovdl::v2script::ast::Node const* node) -> bool { Ideology const* ideology = nullptr; fixed_point_t amount = 0; /* PERCENTAGE_DECIMAL */ @@ -188,7 +188,7 @@ bool ProvinceHistoryMap::_load_history_entry( } return ret; }, - "state_building", ZERO_OR_MORE, [&building_type_manager, &entry](ast::NodeCPtr node) -> bool { + "state_building", ZERO_OR_MORE, [&building_type_manager, &entry](ovdl::v2script::ast::Node const* node) -> bool { BuildingType const* building_type_ptr = nullptr; building_level_t level = building_level_t { 0 }; @@ -276,16 +276,19 @@ ProvinceHistoryMap const* ProvinceHistoryManager::get_province_history(ProvinceD ProvinceHistoryMap* ProvinceHistoryManager::_get_or_make_province_history(ProvinceDefinition const& province) { decltype(province_histories)::iterator it = province_histories.find(&province); if (it == province_histories.end()) { - const std::pair result = - province_histories.emplace( - &province, - ProvinceHistoryMap { - province, - definition_manager.get_economy_manager().get_building_type_manager() - } - ); - if (result.second) { - it = result.first; + const auto [ + new_it, + was_emplaced + ] = province_histories.emplace( + &province, + ProvinceHistoryMap { + province, + definition_manager.get_economy_manager().get_building_type_manager() + } + ); + + if (was_emplaced) { + it = new_it; } else { spdlog::error_s("Failed to create province history map for province {}", province); return nullptr; @@ -295,7 +298,7 @@ ProvinceHistoryMap* ProvinceHistoryManager::_get_or_make_province_history(Provin } bool ProvinceHistoryManager::load_province_history_file( - DefinitionManager const& definition_manager, ProvinceDefinition const& province, ast::NodeCPtr root + DefinitionManager const& definition_manager, ProvinceDefinition const& province, ovdl::v2script::ast::Node const* root ) { if (locked) { spdlog::error_s( @@ -314,20 +317,20 @@ bool ProvinceHistoryManager::load_province_history_file( } bool ProvinceHistoryEntry::_load_province_pop_history( - DefinitionManager const& definition_manager, ast::NodeCPtr root, bool *non_integer_size + DefinitionManager const& definition_manager, ovdl::v2script::ast::Node const* root, bool *non_integer_size ) { PopManager const& pop_manager = definition_manager.get_pop_manager(); RebelManager const& rebel_manager = definition_manager.get_politics_manager().get_rebel_manager(); return pop_manager.expect_pop_type_dictionary_reserve_length( pops, - [this, &pop_manager, &rebel_manager, non_integer_size](PopType const& pop_type, ast::NodeCPtr pop_node) -> bool { + [this, &pop_manager, &rebel_manager, non_integer_size](PopType const& pop_type, ovdl::v2script::ast::Node const* pop_node) -> bool { return pop_manager.load_pop_bases_into_vector(rebel_manager, pops, pop_type, pop_node, non_integer_size); } )(root); } bool ProvinceHistoryMap::_load_province_pop_history( - DefinitionManager const& definition_manager, Date date, ast::NodeCPtr root, bool *non_integer_size + DefinitionManager const& definition_manager, Date date, ovdl::v2script::ast::Node const* root, bool *non_integer_size ) { ProvinceHistoryEntry* entry = _get_or_make_entry(definition_manager, date); if (entry != nullptr) { @@ -338,14 +341,14 @@ bool ProvinceHistoryMap::_load_province_pop_history( } bool ProvinceHistoryManager::load_pop_history_file( - DefinitionManager const& definition_manager, Date date, ast::NodeCPtr root, bool *non_integer_size + DefinitionManager const& definition_manager, Date date, ovdl::v2script::ast::Node const* root, bool *non_integer_size ) { if (locked) { spdlog::error_s("Attempted to load pop history file after province history registry was locked!"); return false; } return definition_manager.get_map_definition().expect_province_definition_dictionary( - [this, &definition_manager, date, non_integer_size](ProvinceDefinition const& province, ast::NodeCPtr node) -> bool { + [this, &definition_manager, date, non_integer_size](ProvinceDefinition const& province, ovdl::v2script::ast::Node const* node) -> bool { ProvinceHistoryMap* province_history = _get_or_make_province_history(province); if (province_history != nullptr) { return province_history->_load_province_pop_history(definition_manager, date, node, non_integer_size); diff --git a/src/openvic-simulation/history/ProvinceHistory.hpp b/src/openvic-simulation/history/ProvinceHistory.hpp index cdedee948..fcab3d584 100644 --- a/src/openvic-simulation/history/ProvinceHistory.hpp +++ b/src/openvic-simulation/history/ProvinceHistory.hpp @@ -54,7 +54,7 @@ namespace OpenVic { memory::vector SPAN_PROPERTY(pops); bool _load_province_pop_history( - DefinitionManager const& definition_manager, ast::NodeCPtr root, bool *non_integer_size + DefinitionManager const& definition_manager, ovdl::v2script::ast::Node const* root, bool *non_integer_size ); public: ProvinceDefinition const& province; @@ -73,12 +73,12 @@ namespace OpenVic { ProvinceHistoryMap(ProvinceDefinition const& new_province, BuildingTypeManager const& new_building_type_manager); memory::unique_ptr _make_entry(Date date) const override; - bool _load_history_entry(DefinitionManager const& definition_manager, ProvinceHistoryEntry& entry, ast::NodeCPtr root) + bool _load_history_entry(DefinitionManager const& definition_manager, ProvinceHistoryEntry& entry, ovdl::v2script::ast::Node const* root) override; private: bool _load_province_pop_history( - DefinitionManager const& definition_manager, Date date, ast::NodeCPtr root, bool* non_integer_size + DefinitionManager const& definition_manager, Date date, ovdl::v2script::ast::Node const* root, bool* non_integer_size ); }; @@ -102,10 +102,10 @@ namespace OpenVic { ProvinceHistoryMap const* get_province_history(ProvinceDefinition const* province) const; bool load_province_history_file( - DefinitionManager const& definition_manager, ProvinceDefinition const& province, ast::NodeCPtr root + DefinitionManager const& definition_manager, ProvinceDefinition const& province, ovdl::v2script::ast::Node const* root ); bool load_pop_history_file( - DefinitionManager const& definition_manager, Date date, ast::NodeCPtr root, bool* non_integer_size + DefinitionManager const& definition_manager, Date date, ovdl::v2script::ast::Node const* root, bool* non_integer_size ); }; } diff --git a/src/openvic-simulation/interface/GFXObject.cpp b/src/openvic-simulation/interface/GFXObject.cpp index c889bfbd3..4b7622f16 100644 --- a/src/openvic-simulation/interface/GFXObject.cpp +++ b/src/openvic-simulation/interface/GFXObject.cpp @@ -7,7 +7,7 @@ using namespace OpenVic; using namespace OpenVic::GFX; using namespace OpenVic::NodeTools; -node_callback_t Object::expect_objects(length_callback_t length_callback, callback_t&&> callback) { +NodeTools::node_callback_t Object::expect_objects(length_callback_t length_callback, NodeTools::callback_t&&> callback) { return expect_dictionary_keys_and_length( length_callback, @@ -107,7 +107,7 @@ bool Actor::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) { "actorfile", ONE_EXACTLY, expect_string(assign_variable_callback_string(model_file)), "scale", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(scale)), - "attach", ZERO_OR_MORE, [this](ast::NodeCPtr node) -> bool { + "attach", ZERO_OR_MORE, [this](ovdl::v2script::ast::Node const* node) -> bool { std::string_view actor_name {}, attach_node {}; Attachment::attach_id_t attach_id = 0; @@ -127,7 +127,7 @@ bool Actor::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) { "idle", ZERO_OR_ONE, expect_string(bind_set_animation(this, "idle"sv)), "move", ZERO_OR_ONE, expect_string(bind_set_animation(this, "move"sv)), "attack", ZERO_OR_ONE, expect_string(bind_set_animation(this, "attack"sv)), - "animation", ZERO_OR_MORE, [this](ast::NodeCPtr node) -> bool { + "animation", ZERO_OR_MORE, [this](ovdl::v2script::ast::Node const* node) -> bool { std::string_view name {}, file {}; fixed_point_t scroll_time = 0; @@ -239,7 +239,7 @@ bool Billboard::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map) { ret &= add_key_map_entries(key_map, "texturefile", ONE_EXACTLY, expect_string(assign_variable_callback_string(texture_file)), - "noOfFrames", ZERO_OR_ONE, expect_uint((callback_t)[this](frame_t frames_read) -> bool { + "noOfFrames", ZERO_OR_ONE, expect_uint((NodeTools::callback_t)[this](frame_t frames_read) -> bool { if (frames_read < 1) { spdlog::error_s( "Billboard {} had an invalid number of frames {}, setting number of frames to 1", diff --git a/src/openvic-simulation/interface/GFXSprite.cpp b/src/openvic-simulation/interface/GFXSprite.cpp index c488d0439..5c96df371 100644 --- a/src/openvic-simulation/interface/GFXSprite.cpp +++ b/src/openvic-simulation/interface/GFXSprite.cpp @@ -1,5 +1,7 @@ #include "GFXSprite.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::GFX; using namespace OpenVic::NodeTools; @@ -10,7 +12,7 @@ Font::Font( ) : HasIdentifierAndAlphaColour { new_identifier, new_colour, false }, fontname { new_fontname }, charset { new_charset }, height { new_height }, colour_codes { std::move(new_colour_codes) } {} -node_callback_t Sprite::expect_sprites(length_callback_t length_callback, callback_t&&> callback) { +NodeTools::node_callback_t Sprite::expect_sprites(length_callback_t length_callback, NodeTools::callback_t&&> callback) { return expect_dictionary_keys_and_length( length_callback, "spriteType", ZERO_OR_MORE, _expect_instance(callback), diff --git a/src/openvic-simulation/interface/GUI.cpp b/src/openvic-simulation/interface/GUI.cpp index 1e9dffbdf..24d8cb05e 100644 --- a/src/openvic-simulation/interface/GUI.cpp +++ b/src/openvic-simulation/interface/GUI.cpp @@ -41,7 +41,7 @@ bool Element::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIMa } bool Element::_fill_elements_key_map( - NodeTools::case_insensitive_key_map_t& key_map, callback_t&&> callback, UIManager const& ui_manager + NodeTools::case_insensitive_key_map_t& key_map, NodeTools::callback_t&&> callback, UIManager const& ui_manager ) { bool ret = true; ret &= add_key_map_entries(key_map, @@ -73,8 +73,8 @@ bool Scene::_fill_key_map(NodeTools::case_insensitive_key_map_t& key_map, UIMana return ret; } -node_callback_t Scene::expect_scene( - std::string_view scene_name, callback_t&&> callback, UIManager const& ui_manager +NodeTools::node_callback_t Scene::expect_scene( + std::string_view scene_name, NodeTools::callback_t&&> callback, UIManager const& ui_manager ) { return _expect_instance([scene_name, callback](memory::unique_base_ptr&& scene) mutable -> bool { scene->_set_name(scene_name); diff --git a/src/openvic-simulation/interface/LoadBase.hpp b/src/openvic-simulation/interface/LoadBase.hpp index e787ef501..a32da40ce 100644 --- a/src/openvic-simulation/interface/LoadBase.hpp +++ b/src/openvic-simulation/interface/LoadBase.hpp @@ -1,5 +1,6 @@ #pragma once +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/utility/Getters.hpp" @@ -16,7 +17,7 @@ namespace OpenVic { LoadBase(LoadBase&&) = default; virtual ~LoadBase() = default; - bool load(ast::NodeCPtr node, Context... context) { + bool load(ovdl::v2script::ast::Node const* node, Context... context) { NodeTools::case_insensitive_key_map_t key_map; bool ret = _fill_key_map(key_map, context...); ret &= NodeTools::expect_dictionary_key_map(std::move(key_map))(node); @@ -27,7 +28,7 @@ namespace OpenVic { static NodeTools::node_callback_t _expect_value( NodeTools::callback_t callback, Context... context ) { - return [callback, &context...](ast::NodeCPtr node) mutable -> bool { + return [callback, &context...](ovdl::v2script::ast::Node const* node) mutable -> bool { T value {}; bool ret = value.load(node, context...); ret &= callback(std::move(value)); @@ -39,7 +40,7 @@ namespace OpenVic { static NodeTools::node_callback_t _expect_instance( NodeTools::callback_t&&> callback, Context... context ) { - return [callback, &context...](ast::NodeCPtr node) mutable -> bool { + return [callback, &context...](ovdl::v2script::ast::Node const* node) mutable -> bool { memory::unique_base_ptr instance { memory::make_unique() }; bool ret = instance->load(node, context...); ret &= callback(std::move(instance)); diff --git a/src/openvic-simulation/interface/UI.cpp b/src/openvic-simulation/interface/UI.cpp index de6a29427..82746c675 100644 --- a/src/openvic-simulation/interface/UI.cpp +++ b/src/openvic-simulation/interface/UI.cpp @@ -48,7 +48,7 @@ bool UIManager::add_font( return ret; } -bool UIManager::_load_font(ast::NodeCPtr node) { +bool UIManager::_load_font(ovdl::v2script::ast::Node const* node) { std::string_view identifier, fontname, charset; colour_argb_t colour = colour_argb_t::fill_as(255); uint32_t height = 0; @@ -61,7 +61,7 @@ bool UIManager::_load_font(ast::NodeCPtr node) { "charset", ZERO_OR_ONE, expect_string(assign_variable_callback(charset)), "height", ZERO_OR_ONE, expect_uint(assign_variable_callback(height)), "colorcodes", ZERO_OR_ONE, expect_dictionary( - [&colour_codes](std::string_view key, ast::NodeCPtr value) -> bool { + [&colour_codes](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { if (key.size() != 1) { spdlog::error_s("Invalid colour code key: \"{}\" (expected single character)", key); return false; @@ -80,7 +80,7 @@ bool UIManager::_load_font(ast::NodeCPtr node) { NodeCallback auto UIManager::_load_fonts(std::string_view font_key) { return expect_dictionary_reserve_length( fonts, - [this, font_key](std::string_view key, ast::NodeCPtr node) -> bool { + [this, font_key](std::string_view key, ovdl::v2script::ast::Node const* node) -> bool { if (key != font_key) { spdlog::error_s( "Invalid key: \"{}\" (expected {})", @@ -99,7 +99,7 @@ void UIManager::lock_gfx_registries() { lock_objects(); } -bool UIManager::load_gfx_file(ast::NodeCPtr root) { +bool UIManager::load_gfx_file(ovdl::v2script::ast::Node const* root) { return expect_dictionary_keys( "spriteTypes", ZERO_OR_ONE, Sprite::expect_sprites( NodeTools::reserve_length_callback(sprites), @@ -146,7 +146,7 @@ bool UIManager::load_gfx_file(ast::NodeCPtr root) { )(root); } -bool UIManager::load_gui_file(std::string_view scene_name, ast::NodeCPtr root) { +bool UIManager::load_gui_file(std::string_view scene_name, ovdl::v2script::ast::Node const* root) { if (!sprites_are_locked()) { spdlog::error_s("Cannot load GUI files until GFX files (i.e. Sprites) are locked!"); return false; diff --git a/src/openvic-simulation/interface/UI.hpp b/src/openvic-simulation/interface/UI.hpp index 33a776427..c83d60814 100644 --- a/src/openvic-simulation/interface/UI.hpp +++ b/src/openvic-simulation/interface/UI.hpp @@ -14,7 +14,7 @@ namespace OpenVic { NamedBaseInstanceRegistry IDENTIFIER_REGISTRY(scene); - bool _load_font(ast::NodeCPtr node); + bool _load_font(ovdl::v2script::ast::Node const* node); NodeTools::NodeCallback auto _load_fonts(std::string_view font_key); public: @@ -29,7 +29,7 @@ namespace OpenVic { void lock_gfx_registries(); - bool load_gfx_file(ast::NodeCPtr root); - bool load_gui_file(std::string_view scene_name, ast::NodeCPtr root); + bool load_gfx_file(ovdl::v2script::ast::Node const* root); + bool load_gui_file(std::string_view scene_name, ovdl::v2script::ast::Node const* root); }; } diff --git a/src/openvic-simulation/map/Crime.cpp b/src/openvic-simulation/map/Crime.cpp index 45375d9ff..15a19abdd 100644 --- a/src/openvic-simulation/map/Crime.cpp +++ b/src/openvic-simulation/map/Crime.cpp @@ -1,10 +1,6 @@ #include "Crime.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; Crime::Crime( index_t new_index, @@ -16,61 +12,3 @@ Crime::Crime( ) : HasIndex { new_index }, TriggeredModifier { new_identifier, std::move(new_values), modifier_type_t::CRIME, new_icon, std::move(new_trigger) }, is_default_active { new_default_active } {} - -bool CrimeManager::add_crime_modifier( - std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger, - bool default_active -) { - if (identifier.empty()) { - spdlog::error_s("Invalid crime modifier effect identifier - empty!"); - return false; - } - - return crime_modifiers.emplace_item( - identifier, - duplicate_warning_callback, - Crime::index_t { get_crime_modifier_count() }, identifier, std::move(values), icon, std::move(trigger), default_active - ); -} - -bool CrimeManager::load_crime_modifiers(ModifierManager const& modifier_manager, ast::NodeCPtr root) { - const bool ret = expect_dictionary_reserve_length( - crime_modifiers, - [this, &modifier_manager](std::string_view key, ast::NodeCPtr value) -> bool { - using enum scope_type_t; - - ModifierValue modifier_value; - IconModifier::icon_t icon = 0; - ConditionScript trigger { PROVINCE, NO_SCOPE, NO_SCOPE }; - bool default_active = false; - - bool ret = NodeTools::expect_dictionary_keys_and_default( - modifier_manager.expect_base_province_modifier(modifier_value), - "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)), - "trigger", ZERO_OR_ONE, trigger.expect_script(), - "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(default_active)) - )(value); - - ret &= add_crime_modifier(key, std::move(modifier_value), icon, std::move(trigger), default_active); - - return ret; - } - )(root); - - lock_crime_modifiers(); - - return ret; -} - -bool CrimeManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - - for (Crime& crime : crime_modifiers.get_items()) { - ret &= crime.parse_scripts( - definition_manager, - true // Script can be null, meaning this won't emit an error message if the crime has no trigger in its definition - ); - } - - return ret; -} diff --git a/src/openvic-simulation/map/Crime.hpp b/src/openvic-simulation/map/Crime.hpp index b4430feeb..93bf64ffa 100644 --- a/src/openvic-simulation/map/Crime.hpp +++ b/src/openvic-simulation/map/Crime.hpp @@ -22,19 +22,4 @@ namespace OpenVic { ); Crime(Crime&&) = default; }; - - struct CrimeManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(crime_modifier); - - public: - bool add_crime_modifier( - std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger, - bool default_active - ); - - bool load_crime_modifiers(ModifierManager const& modifier_manager, ast::NodeCPtr root); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/map/CrimeManager.cpp b/src/openvic-simulation/map/CrimeManager.cpp new file mode 100644 index 000000000..05005444c --- /dev/null +++ b/src/openvic-simulation/map/CrimeManager.cpp @@ -0,0 +1,65 @@ +#include "CrimeManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool CrimeManager::add_crime_modifier( + std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger, + bool default_active +) { + if (identifier.empty()) { + spdlog::error_s("Invalid crime modifier effect identifier - empty!"); + return false; + } + + return crime_modifiers.emplace_item( + identifier, + duplicate_warning_callback, + Crime::index_t { get_crime_modifier_count() }, identifier, std::move(values), icon, std::move(trigger), default_active + ); +} + +bool CrimeManager::load_crime_modifiers(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root) { + const bool ret = expect_dictionary_reserve_length( + crime_modifiers, + [this, &modifier_manager](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + using enum scope_type_t; + + ModifierValue modifier_value; + IconModifier::icon_t icon = 0; + ConditionScript trigger { PROVINCE, NO_SCOPE, NO_SCOPE }; + bool default_active = false; + + bool ret = NodeTools::expect_dictionary_keys_and_default( + modifier_manager.expect_base_province_modifier(modifier_value), + "icon", ZERO_OR_ONE, expect_uint(assign_variable_callback(icon)), + "trigger", ZERO_OR_ONE, trigger.expect_script(), + "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(default_active)) + )(value); + + ret &= add_crime_modifier(key, std::move(modifier_value), icon, std::move(trigger), default_active); + + return ret; + } + )(root); + + lock_crime_modifiers(); + + return ret; +} + +bool CrimeManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + + for (Crime& crime : crime_modifiers.get_items()) { + ret &= crime.parse_scripts( + definition_manager, + true // Script can be null, meaning this won't emit an error message if the crime has no trigger in its definition + ); + } + + return ret; +} diff --git a/src/openvic-simulation/map/CrimeManager.hpp b/src/openvic-simulation/map/CrimeManager.hpp new file mode 100644 index 000000000..efd366b3f --- /dev/null +++ b/src/openvic-simulation/map/CrimeManager.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/map/Crime.hpp" +#include "openvic-simulation/modifier/Modifier.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct ConditionScript; + + struct CrimeManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(crime_modifier); + + public: + bool add_crime_modifier( + std::string_view identifier, ModifierValue&& values, IconModifier::icon_t icon, ConditionScript&& trigger, + bool default_active + ); + + bool load_crime_modifiers(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/map/MapDefinition.cpp b/src/openvic-simulation/map/MapDefinition.cpp index 8e021edce..f1f567d9d 100644 --- a/src/openvic-simulation/map/MapDefinition.cpp +++ b/src/openvic-simulation/map/MapDefinition.cpp @@ -624,18 +624,18 @@ bool MapDefinition::load_province_definitions(std::span lines) return ret; } -bool MapDefinition::load_province_positions(BuildingTypeManager const& building_type_manager, ast::NodeCPtr root) { +bool MapDefinition::load_province_positions(BuildingTypeManager const& building_type_manager, ovdl::v2script::ast::Node const* root) { return expect_province_definition_dictionary( - [this, &building_type_manager](ProvinceDefinition& province, ast::NodeCPtr node) -> bool { + [this, &building_type_manager](ProvinceDefinition& province, ovdl::v2script::ast::Node const* node) -> bool { return province.load_positions(*this, building_type_manager, node); } )(root); } -bool MapDefinition::load_region_colours(ast::NodeCPtr root, memory::vector& colours) { +bool MapDefinition::load_region_colours(ovdl::v2script::ast::Node const* root, memory::vector& colours) { return expect_dictionary_reserve_length( colours, - [&colours](std::string_view key, ast::NodeCPtr value) -> bool { + [&colours](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { if (key != "color") { spdlog::error_s("Invalid key in region colours: \"{}\"", key); return false; @@ -644,10 +644,10 @@ bool MapDefinition::load_region_colours(ast::NodeCPtr root, memory::vector colours) { +bool MapDefinition::load_region_file(ovdl::v2script::ast::Node const* root, std::span colours) { const bool ret = expect_dictionary_reserve_length( regions, - [this, &colours](std::string_view region_identifier, ast::NodeCPtr region_node) -> bool { + [this, &colours](std::string_view region_identifier, ovdl::v2script::ast::Node const* region_node) -> bool { memory::vector> provinces; bool ret = expect_list_reserve_length( @@ -739,8 +739,12 @@ void MapDefinition::_trace_river(BMP& rivers_bmp, ivec2_t start, river_t& river) for (const auto& neighbour : neighbours) { ivec2_t neighbour_pos = { segment.point.x + neighbour.offset.x, segment.point.y + neighbour.offset.y }; - if (neighbour_pos.x < 0 || neighbour_pos.y < 0 || neighbour_pos.x >= rivers_bmp.get_width() || - neighbour_pos.y >= rivers_bmp.get_height() || segment.direction == neighbour.old_direction) { + if (neighbour_pos.x < 0 + || neighbour_pos.y < 0 + || neighbour_pos.x >= rivers_bmp.get_width() + || neighbour_pos.y >= rivers_bmp.get_height() + || segment.direction == neighbour.old_direction + ) { continue; } @@ -848,10 +852,10 @@ bool MapDefinition::load_map_images(fs::path const& province_path, fs::path cons return false; } - if (province_bmp.get_width() != terrain_bmp.get_width() || - province_bmp.get_height() != terrain_bmp.get_height() || - province_bmp.get_width() != rivers_bmp.get_width() || - province_bmp.get_height() != rivers_bmp.get_height() + if (province_bmp.get_width() != terrain_bmp.get_width() + || province_bmp.get_height() != terrain_bmp.get_height() + || province_bmp.get_width() != rivers_bmp.get_width() + || province_bmp.get_height() != rivers_bmp.get_height() ) { spdlog::error_s( "Mismatched map BMP dims: provinces:{}x{}, terrain: {}x{}, rivers: {}x{}", @@ -1104,10 +1108,10 @@ bool MapDefinition::generate_and_load_province_adjacencies(std::span bool { + [this, &modifier_manager](std::string_view identifier, ovdl::v2script::ast::Node const* node) -> bool { if (identifier.empty()) { spdlog::error_s("Invalid climate identifier - empty!"); return false; @@ -1163,10 +1167,10 @@ bool MapDefinition::load_climate_file(ModifierManager const& modifier_manager, a return ret; } -bool MapDefinition::load_continent_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) { +bool MapDefinition::load_continent_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root) { bool ret = expect_dictionary_reserve_length( continents, - [this, &modifier_manager](std::string_view identifier, ast::NodeCPtr node) -> bool { + [this, &modifier_manager](std::string_view identifier, ovdl::v2script::ast::Node const* node) -> bool { if (identifier.empty()) { spdlog::error_s("Invalid continent identifier - empty!"); diff --git a/src/openvic-simulation/map/MapDefinition.hpp b/src/openvic-simulation/map/MapDefinition.hpp index a2aa45f95..63a8e7034 100644 --- a/src/openvic-simulation/map/MapDefinition.hpp +++ b/src/openvic-simulation/map/MapDefinition.hpp @@ -11,7 +11,7 @@ #include "openvic-simulation/core/memory/Vector.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/map/Region.hpp" -#include "openvic-simulation/map/TerrainType.hpp" +#include "openvic-simulation/map/TerrainTypeManager.hpp" #include "openvic-simulation/pathfinding/PointMap.hpp" #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/IdentifierRegistry.hpp" @@ -135,12 +135,12 @@ namespace OpenVic { bool load_province_definitions(std::span lines); /* Must be loaded after adjacencies so we know what provinces are coastal, and so can have a port */ - bool load_province_positions(BuildingTypeManager const& building_type_manager, ast::NodeCPtr root); - static bool load_region_colours(ast::NodeCPtr root, memory::vector& colours); - bool load_region_file(ast::NodeCPtr root, std::span colours); + bool load_province_positions(BuildingTypeManager const& building_type_manager, ovdl::v2script::ast::Node const* root); + static bool load_region_colours(ovdl::v2script::ast::Node const* root, memory::vector& colours); + bool load_region_file(ovdl::v2script::ast::Node const* root, std::span colours); bool load_map_images(fs::path const& province_path, fs::path const& terrain_path, fs::path const& rivers_path, bool detailed_errors); bool generate_and_load_province_adjacencies(std::span additional_adjacencies); - bool load_climate_file(ModifierManager const& modifier_manager, ast::NodeCPtr root); - bool load_continent_file(ModifierManager const& modifier_manager, ast::NodeCPtr root); + bool load_climate_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); + bool load_continent_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); }; } diff --git a/src/openvic-simulation/map/MapInstance.cpp b/src/openvic-simulation/map/MapInstance.cpp index 4b8cc6286..0df33026d 100644 --- a/src/openvic-simulation/map/MapInstance.cpp +++ b/src/openvic-simulation/map/MapInstance.cpp @@ -82,82 +82,109 @@ bool MapInstance::is_canal_enabled(canal_index_t canal_index) const { return enabled_canals.contains(canal_index); } -bool MapInstance::apply_history_to_provinces( +bool MapInstance::apply_history_to_province( ProvinceHistoryManager const& history_manager, const Date date, CountryInstanceManager& country_manager, MilitaryDefines const& military_defines, PopDeps const& pop_deps, TypedSpan pop_types, - TypedSpan reforms + TypedSpan reforms, + ProvinceInstance& province ) { + ProvinceDefinition const& province_definition = province.province_definition; + if (province_definition.is_water()) { + return true; + } + + ProvinceHistoryMap const* history_map = history_manager.get_province_history(&province_definition); + + if (history_map == nullptr) { + return true; + } + bool ret = true; + ProvinceHistoryEntry const* pop_history_entry = nullptr; + ProductionType const* rgo_production_type_nullable = nullptr; - for (ProvinceInstance& province : get_province_instances()) { - ProvinceDefinition const& province_definition = province.province_definition; - if (!province_definition.is_water()) { - ProvinceHistoryMap const* history_map = history_manager.get_province_history(&province_definition); - - if (history_map != nullptr) { - ProvinceHistoryEntry const* pop_history_entry = nullptr; - ProductionType const* rgo_production_type_nullable = nullptr; - - for (auto const& [entry_date, entry] : history_map->get_entries()) { - if (entry_date > date) { - if (pop_history_entry != nullptr) { - break; - } - } else { - province.apply_history_to_province(*entry, country_manager); - std::optional const& rgo_production_type_nullable_optional = - entry->get_rgo_production_type_nullable(); - if (rgo_production_type_nullable_optional.has_value()) { - rgo_production_type_nullable = rgo_production_type_nullable_optional.value(); - } - } - - if (!entry->get_pops().empty()) { - pop_history_entry = entry.get(); - } - } - - if (pop_history_entry == nullptr) { - spdlog::warn_s("No pop history entry for province {} for date {}", province, date); - } else { - ret &= province.add_pop_vec( - pop_history_entry->get_pops(), - pop_deps - ); - province.setup_pop_test_values(reforms); - - //update pops so OOB can use up to date max_supported_regiments - province._update_pops(military_defines); - } - - ret &= province.set_rgo_production_type_nullable( - pop_types, - rgo_production_type_nullable - ); + for (auto const& [entry_date, entry] : history_map->get_entries()) { + if (entry_date > date) { + if (pop_history_entry != nullptr) { + break; } + } else { + province.apply_history_to_province(*entry, country_manager); + std::optional const& rgo_production_type_nullable_optional = + entry->get_rgo_production_type_nullable(); + if (rgo_production_type_nullable_optional.has_value()) { + rgo_production_type_nullable = rgo_production_type_nullable_optional.value(); + } + } + + if (!entry->get_pops().empty()) { + pop_history_entry = entry.get(); } } + if (pop_history_entry == nullptr) { + spdlog::warn_s("No pop history entry for province {} for date {}", province, date); + } else { + ret &= province.add_pop_vec( + pop_history_entry->get_pops(), + pop_deps + ); + province.setup_pop_test_values(reforms); + + //update pops so OOB can use up to date max_supported_regiments + province._update_pops(military_defines); + } + + ret &= province.set_rgo_production_type_nullable( + pop_types, + rgo_production_type_nullable + ); + return ret; } -void MapInstance::update_modifier_sums(const Date today, StaticModifierCache const& static_modifier_cache) { +bool MapInstance::apply_history_to_provinces( + ProvinceHistoryManager const& history_manager, + const Date date, + CountryInstanceManager& country_manager, + MilitaryDefines const& military_defines, + PopDeps const& pop_deps, + TypedSpan pop_types, + TypedSpan reforms +) { + bool ret = true; + for (ProvinceInstance& province : get_province_instances()) { - province.update_modifier_sum(today, static_modifier_cache); + apply_history_to_province( + history_manager, + date, + country_manager, + military_defines, + pop_deps, + pop_types, + reforms, + province + ); } + + return ret; } -void MapInstance::update_gamestate(InstanceManager const& instance_manager) { +void MapInstance::update_modifier_sums(const Date today) { + thread_pool.process(work_t::PROVINCE_UPDATE_MODIFIER_SUMS); +} + +void MapInstance::update_gamestate() { highest_province_population = 0; total_map_population = 0; - for (ProvinceInstance& province : get_province_instances()) { - province.update_gamestate(instance_manager); + thread_pool.process(work_t::PROVINCE_UPDATE_GAMESTATE); + for (ProvinceInstance& province : get_province_instances()) { // Update population stats const pop_sum_t province_population = province.get_total_population(); if (highest_province_population < province_population) { @@ -170,13 +197,13 @@ void MapInstance::update_gamestate(InstanceManager const& instance_manager) { } void MapInstance::map_tick() { - thread_pool.process_province_ticks(); + thread_pool.process(work_t::PROVINCE_TICK); //state tick //after province tick as province tick sets pop employment to 0 //state tick will update pop employment via factories } -void MapInstance::initialise_for_new_game(InstanceManager const& instance_manager) { - update_gamestate(instance_manager); - thread_pool.process_province_initialise_for_new_game(); +void MapInstance::initialise_for_new_game() { + update_gamestate(); + thread_pool.process(work_t::PROVINCE_INITIALISE_FOR_NEW_GAME); } \ No newline at end of file diff --git a/src/openvic-simulation/map/MapInstance.hpp b/src/openvic-simulation/map/MapInstance.hpp index 0c37d5589..cc01bad52 100644 --- a/src/openvic-simulation/map/MapInstance.hpp +++ b/src/openvic-simulation/map/MapInstance.hpp @@ -1,8 +1,9 @@ #pragma once +#include "openvic-simulation/core/Typedefs.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" -#include "openvic-simulation/map/State.hpp" +#include "openvic-simulation/map/StateManager.hpp" #include "openvic-simulation/pathfinding/AStarPathing.hpp" #include "openvic-simulation/population/PopSum.hpp" #include "openvic-simulation/population/PopValuesFromProvince.hpp" @@ -42,6 +43,17 @@ namespace OpenVic { ArmyAStarPathing PROPERTY_REF(land_pathing); NavyAStarPathing PROPERTY_REF(sea_pathing); + OV_SPEED_INLINE bool apply_history_to_province( + ProvinceHistoryManager const& history_manager, + const Date date, + CountryInstanceManager& country_manager, + MilitaryDefines const& military_defines, + PopDeps const& pop_deps, + TypedSpan pop_types, + TypedSpan reforms, + ProvinceInstance& province + ); + public: MapInstance( MapDefinition const& new_map_definition, @@ -85,9 +97,9 @@ namespace OpenVic { TypedSpan reforms ); - void update_modifier_sums(const Date today, StaticModifierCache const& static_modifier_cache); - void update_gamestate(InstanceManager const& instance_manager); + void update_modifier_sums(const Date today); + void update_gamestate(); void map_tick(); - void initialise_for_new_game(InstanceManager const& instance_manager); + void initialise_for_new_game(); }; } diff --git a/src/openvic-simulation/map/Mapmode.cpp b/src/openvic-simulation/map/Mapmode.cpp index 7b4d75767..e82eb5631 100644 --- a/src/openvic-simulation/map/Mapmode.cpp +++ b/src/openvic-simulation/map/Mapmode.cpp @@ -5,16 +5,9 @@ #include #include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/economy/BuildingType.hpp" -#include "openvic-simulation/economy/GoodDefinition.hpp" // IWYU pragma: keep -#include "openvic-simulation/map/MapDefinition.hpp" +#include "openvic-simulation/economy/BuildingTypeManager.hpp" #include "openvic-simulation/map/MapInstance.hpp" -#include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" -#include "openvic-simulation/population/Culture.hpp" -#include "openvic-simulation/population/PopSum.hpp" -#include "openvic-simulation/population/Religion.hpp" -#include "openvic-simulation/types/OrderedContainersMath.hpp" using namespace OpenVic; using namespace OpenVic::colour_literals; @@ -46,351 +39,3 @@ Mapmode::base_stripe_t Mapmode::get_base_stripe_colours( ) const { return colour_func ? colour_func(map_instance, province, player_country, selected_province) : colour_argb_t::null(); } - -bool MapmodeManager::add_mapmode( - std::string_view identifier, - Mapmode::colour_func_t colour_func, - std::string_view localisation_key, - bool parchment_mapmode_allowed -) { - if (identifier.empty()) { - spdlog::error_s("Invalid mapmode identifier - empty!"); - return false; - } - if (colour_func == nullptr) { - spdlog::error_s("Mapmode colour function is null for identifier: {}", identifier); - return false; - } - return mapmodes.emplace_item( - identifier, - identifier, Mapmode::index_t { get_mapmode_count() }, colour_func, localisation_key, parchment_mapmode_allowed - ); -} - -bool MapmodeManager::generate_mapmode_colours( - MapInstance const& map_instance, Mapmode const* mapmode, - CountryInstance const* player_country, ProvinceInstance const* selected_province, - uint8_t* target -) const { - if (target == nullptr) { - spdlog::error_s("Mapmode colour target pointer is null!"); - return false; - } - - bool ret = true; - if (mapmode == nullptr) { - mapmode = &Mapmode::ERROR_MAPMODE; - spdlog::error_s( - "Trying to generate mapmode colours using null mapmode! Defaulting to \"{}\"", *mapmode - ); - ret = false; - } - - Mapmode::base_stripe_t* target_stripes = reinterpret_cast(target); - - target_stripes[ProvinceDefinition::NULL_PROVINCE_NUMBER] = colour_argb_t::null(); - - for (ProvinceInstance const& province : map_instance.get_province_instances()) { - target_stripes[province.province_definition.get_province_number()] = mapmode->get_base_stripe_colours( - map_instance, province, player_country, selected_province - ); - } - - return ret; -} - -static constexpr colour_argb_t::value_type ALPHA_VALUE = colour_argb_t::max_value; -/* White default colour, used in mapmodes including political, revolt risk and party loyaly. */ -static constexpr colour_argb_t DEFAULT_COLOUR_WHITE = (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); -/* Grey default colour, used in mapmodes including diplomatic, administrative and colonial, recruitment, - * national focus, RGO, population density, sphere of influence, ranking and migration. */ -static constexpr colour_argb_t DEFAULT_COLOUR_GREY = (0x7F7F7F_argb).with_alpha(ALPHA_VALUE); - -template -requires(std::same_as || std::same_as) -static constexpr auto get_colour_mapmode(T const*(P::*get_item)() const) { - return [get_item]( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - ProvinceDefinition const& province_definition = province.province_definition; - - T const* item = ([&province, &province_definition]() -> P const& { - if constexpr (std::same_as) { - return province_definition; - } else { - return province; - } - }().*get_item)(); - - if (item != nullptr) { - return colour_argb_t { item->get_colour(), ALPHA_VALUE }; - } else if (!province_definition.is_water()) { - return DEFAULT_COLOUR_WHITE; - } else { - return colour_argb_t::null(); - } - }; -} - -template -requires(std::same_as || std::same_as) -static constexpr auto get_colour_mapmode( - BASE_T const*(P::*get_base_item)() const, STRIPE_T const*(P::*get_stripe_item)() const -) { - return [get_base_item, get_stripe_item]( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - ProvinceDefinition const& province_definition = province.province_definition; - - P const& p = [&province, &province_definition]() -> P const& { - if constexpr (std::same_as) { - return province_definition; - } else { - return province; - } - }(); - - BASE_T const* base_item = (p.*get_base_item)(); - STRIPE_T const* stripe_item = (p.*get_stripe_item)(); - - Mapmode::base_stripe_t result = province_definition.is_water() ? colour_argb_t::null() : DEFAULT_COLOUR_WHITE; - - if (base_item != nullptr) { - result.base_colour = colour_argb_t { base_item->get_colour(), ALPHA_VALUE }; - } - if (stripe_item != nullptr) { - result.stripe_colour = colour_argb_t { stripe_item->get_colour(), ALPHA_VALUE }; - } - - return result; - }; -} - -template -static constexpr Mapmode::base_stripe_t shaded_mapmode( - ordered_map const& map -) { - const auto largest = get_largest_two_items(map); - if (largest.first != map.end()) { - const colour_argb_t base_colour = colour_argb_t { largest.first->first->get_colour(), ALPHA_VALUE }; - if (largest.second != map.end()) { - /* If second largest is at least a third... */ - if (largest.second->second * 3 >= get_total(map)) { - const colour_argb_t stripe_colour = colour_argb_t { largest.second->first->get_colour(), ALPHA_VALUE }; - return { base_colour, stripe_colour }; - } - } - return base_colour; - } - return colour_argb_t::null(); -} - -template -static constexpr auto shaded_mapmode(ordered_map const&(ProvinceInstance::*get_map)() const) { - return [get_map]( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - return shaded_mapmode((province.*get_map)()); - }; -} - -bool MapmodeManager::setup_mapmodes(MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager) { - if (mapmodes_are_locked()) { - spdlog::error_s("Cannot setup mapmodes - already locked!"); - return false; - } - - bool ret = true; - - // Default number of mapmodes - reserve_mapmodes(22); - - // The order of mapmodes matches their numbering, but is different from the order in which their buttons appear - ret &= add_mapmode( - "mapmode_terrain", - []( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - return colour_argb_t::null(); - }, - "MAPMODE_1", - false // Parchment mapmode not allowed - ); - ret &= add_mapmode("mapmode_political", get_colour_mapmode( - &ProvinceInstance::get_owner, &ProvinceInstance::get_controller - ), "MAPMODE_2"); - ret &= add_mapmode("mapmode_militancy", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_3"); - ret &= add_mapmode("mapmode_diplomatic", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_4"); - ret &= add_mapmode("mapmode_region", get_colour_mapmode(&ProvinceDefinition::get_region), "MAPMODE_5"); - BuildingType const* const infrastructure_building_type_ptr = building_type_manager.get_infrastructure_building_type(); - if (infrastructure_building_type_ptr == nullptr) { - spdlog::error_s("Cannot setup infrastructure mapmode because infrastructure_building_type is null."); - ret = false; - } else if (!infrastructure_building_type_ptr->province_building_index.has_value()) { - spdlog::error_s("Cannot setup infrastructure mapmode because infrastructure_building_type has no province_building_index."); - ret = false; - } else { - ret &= add_mapmode( - "mapmode_infrastructure", - [&building_type_manager, infrastructure_building_type_ptr]( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - if (province.province_definition.is_water()) { - return colour_argb_t::null(); - } - - BuildingType const& infrastructure_building_type = *infrastructure_building_type_ptr; - BuildingInstance const& infrastructure = province.get_buildings()[infrastructure_building_type.province_building_index.value()]; - const colour_argb_t::value_type val = colour_argb_t::colour_traits::component_from_fraction( - type_safe::get(infrastructure.get_level()), type_safe::get(infrastructure_building_type.max_level) + 1, 0.5f, 1.0f - ); - switch (infrastructure.get_expansion_state()) { - case BuildingInstance::ExpansionState::CannotExpand: - return colour_argb_t { val, 0, 0, ALPHA_VALUE }; - case BuildingInstance::ExpansionState::CanExpand: - return colour_argb_t { 0, 0, val, ALPHA_VALUE }; - default: - return colour_argb_t { 0, val, 0, ALPHA_VALUE }; - } - }, - "MAPMODE_6" - ); - } - ret &= add_mapmode("mapmode_colonial", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_7"); - ret &= add_mapmode("mapmode_administrative", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_8"); - ret &= add_mapmode("mapmode_recruitment", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_9"); - ret &= add_mapmode("mapmode_national_focus", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_10"); - ret &= add_mapmode("mapmode_rgo", get_colour_mapmode(&ProvinceInstance::get_rgo_good), "MAPMODE_11"); - ret &= add_mapmode( - "mapmode_population", - []( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - // TODO - explore non-linear scaling to have more variation among non-massive provinces - // TODO - when selecting a province, only show the population of provinces controlled (or owned?) - // by the same country, relative to the most populous province in that set of provinces - if (!province.province_definition.is_water()) { - const colour_argb_t::value_type val = colour_argb_t::colour_traits::component_from_fraction( - type_safe::get(province.get_total_population()), type_safe::get(map_instance.get_highest_province_population()) + 1, 0.1f, 1.0f - ); - return colour_argb_t { 0, val, 0, ALPHA_VALUE }; - } else { - return colour_argb_t::null(); - } - }, - "MAPMODE_12" - ); - ret &= add_mapmode("mapmode_culture", shaded_mapmode(&ProvinceInstance::get_population_by_culture), "MAPMODE_13"); - ret &= add_mapmode("mapmode_sphere", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_14"); - ret &= add_mapmode("mapmode_supply", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_15"); - ret &= add_mapmode("mapmode_party_loyalty", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_16"); - ret &= add_mapmode("mapmode_ranking", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_17"); - ret &= add_mapmode("mapmode_migration", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_18"); - ret &= add_mapmode("mapmode_civilisation_level", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_19"); - ret &= add_mapmode("mapmode_relations", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_20"); - ret &= add_mapmode("mapmode_crisis", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_21"); - ret &= add_mapmode( - "mapmode_naval", - []( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - ProvinceDefinition const& province_definition = province.province_definition; - - if (province_definition.has_port()) { - return (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); - } else if (!province_definition.is_water()) { - return (0x333333_argb).with_alpha(ALPHA_VALUE); - } else { - return colour_argb_t::null(); - } - }, - "MAPMODE_22" - ); - - /* - *** CUSTOM MAPMODES FOR TESTING *** - - For these mapmodes to have a button in-game you either need to remove the same number of default mapmodes - or add more mapmode buttons (GUIIconButton nodes with path ^".../Menubar/menubar/mapmode_") - */ - if constexpr (false) { - ret &= add_mapmode( - "mapmode_province", - []( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - return colour_argb_t { province.province_definition.get_colour(), ALPHA_VALUE }; - } - ); - ret &= add_mapmode( - "mapmode_index", - [&map_definition]( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - const colour_argb_t::value_type f = colour_argb_t::colour_traits::component_from_fraction( - province.province_definition.get_province_number(), map_definition.get_province_definition_count() + 1 - ); - return colour_argb_t::fill_as(f).with_alpha(ALPHA_VALUE); - } - ); - ret &= add_mapmode("mapmode_religion", shaded_mapmode(&ProvinceInstance::get_population_by_religion)); - ret &= add_mapmode("mapmode_terrain_type", get_colour_mapmode(&ProvinceInstance::get_terrain_type)); - ret &= add_mapmode( - "mapmode_adjacencies", - []( - MapInstance const& map_instance, ProvinceInstance const& province, - CountryInstance const* player_country, ProvinceInstance const* selected_province - ) -> Mapmode::base_stripe_t { - if (selected_province != nullptr) { - ProvinceDefinition const& selected_province_definition = selected_province->province_definition; - - if (*selected_province == province) { - return (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); - } - - ProvinceDefinition const& province_definition = province.province_definition; - - colour_argb_t base = colour_argb_t::null(), stripe = colour_argb_t::null(); - ProvinceDefinition::adjacency_t const* adj = - selected_province_definition.get_adjacency_to(province_definition); - - if (adj != nullptr) { - colour_argb_t::integer_type base_int; - switch (adj->get_type()) { - using enum ProvinceDefinition::adjacency_t::type_t; - case LAND: base_int = 0x00FF00; break; - case WATER: base_int = 0x0000FF; break; - case COASTAL: base_int = 0xF9D199; break; - case IMPASSABLE: base_int = 0x8B4513; break; - case STRAIT: base_int = 0x00FFFF; break; - case CANAL: base_int = 0x888888; break; - default: base_int = 0xFF0000; break; - } - base = colour_argb_t::from_integer(base_int).with_alpha(ALPHA_VALUE); - stripe = base; - } - - if (selected_province_definition.has_adjacency_going_through(province_definition)) { - stripe = (0xFFFF00_argb).with_alpha(ALPHA_VALUE); - } - - return { base, stripe }; - } - - return colour_argb_t::null(); - } - ); - } - - lock_mapmodes(); - - return ret; -} diff --git a/src/openvic-simulation/map/Mapmode.hpp b/src/openvic-simulation/map/Mapmode.hpp index ec00028f9..5ff73395c 100644 --- a/src/openvic-simulation/map/Mapmode.hpp +++ b/src/openvic-simulation/map/Mapmode.hpp @@ -5,13 +5,9 @@ #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { - struct BuildingTypeManager; - struct MapmodeManager; - struct MapDefinition; struct MapInstance; struct ProvinceInstance; struct CountryInstance; @@ -53,32 +49,4 @@ namespace OpenVic { CountryInstance const* player_country, ProvinceInstance const* selected_province ) const; }; - - struct MapmodeManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(mapmode); - - public: - constexpr MapmodeManager() {}; - - bool add_mapmode( - std::string_view identifier, - Mapmode::colour_func_t colour_func, - std::string_view localisation_key = {}, - bool parchment_mapmode_allowed = true - ); - - /* The mapmode colour image contains of a list of base colours and stripe colours. Each colour is four bytes - * in RGBA format, with the alpha value being used to interpolate with the terrain colour, so A = 0 is fully terrain - * and A = 255 is fully the RGB colour packaged with A. The base and stripe colours for each province are packed - * together adjacently, so each province's entry is 8 bytes long. - * The list contains all provinces indexed by their number + index 0 for the "null province". */ - bool generate_mapmode_colours( - MapInstance const& map_instance, Mapmode const* mapmode, - CountryInstance const* player_country, ProvinceInstance const* selected_province, - uint8_t* target - ) const; - - bool setup_mapmodes(MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager); - }; } diff --git a/src/openvic-simulation/map/MapmodeManager.cpp b/src/openvic-simulation/map/MapmodeManager.cpp new file mode 100644 index 000000000..b2cf4db67 --- /dev/null +++ b/src/openvic-simulation/map/MapmodeManager.cpp @@ -0,0 +1,367 @@ +#include "MapmodeManager.hpp" + +#include + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/economy/BuildingTypeManager.hpp" +#include "openvic-simulation/economy/GoodDefinition.hpp" // IWYU pragma: keep +#include "openvic-simulation/map/MapDefinition.hpp" +#include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/map/ProvinceDefinition.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/population/Culture.hpp" +#include "openvic-simulation/population/PopSum.hpp" +#include "openvic-simulation/population/Religion.hpp" +#include "openvic-simulation/types/OrderedContainersMath.hpp" + +using namespace OpenVic; +using namespace OpenVic::colour_literals; + +bool MapmodeManager::add_mapmode( + std::string_view identifier, + Mapmode::colour_func_t colour_func, + std::string_view localisation_key, + bool parchment_mapmode_allowed +) { + if (identifier.empty()) { + spdlog::error_s("Invalid mapmode identifier - empty!"); + return false; + } + if (colour_func == nullptr) { + spdlog::error_s("Mapmode colour function is null for identifier: {}", identifier); + return false; + } + return mapmodes.emplace_item( + identifier, + identifier, Mapmode::index_t { get_mapmode_count() }, colour_func, localisation_key, parchment_mapmode_allowed + ); +} + +bool MapmodeManager::generate_mapmode_colours( + MapInstance const& map_instance, Mapmode const* mapmode, + CountryInstance const* player_country, ProvinceInstance const* selected_province, + uint8_t* target +) const { + if (target == nullptr) { + spdlog::error_s("Mapmode colour target pointer is null!"); + return false; + } + + bool ret = true; + if (mapmode == nullptr) { + mapmode = &Mapmode::ERROR_MAPMODE; + spdlog::error_s( + "Trying to generate mapmode colours using null mapmode! Defaulting to \"{}\"", *mapmode + ); + ret = false; + } + + Mapmode::base_stripe_t* target_stripes = reinterpret_cast(target); + + target_stripes[ProvinceDefinition::NULL_PROVINCE_NUMBER] = colour_argb_t::null(); + + for (ProvinceInstance const& province : map_instance.get_province_instances()) { + target_stripes[province.province_definition.get_province_number()] = mapmode->get_base_stripe_colours( + map_instance, province, player_country, selected_province + ); + } + + return ret; +} + +static constexpr colour_argb_t::value_type ALPHA_VALUE = colour_argb_t::max_value; +/* White default colour, used in mapmodes including political, revolt risk and party loyaly. */ +static constexpr colour_argb_t DEFAULT_COLOUR_WHITE = (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); +/* Grey default colour, used in mapmodes including diplomatic, administrative and colonial, recruitment, + * national focus, RGO, population density, sphere of influence, ranking and migration. */ +static constexpr colour_argb_t DEFAULT_COLOUR_GREY = (0x7F7F7F_argb).with_alpha(ALPHA_VALUE); + +template +requires(std::same_as || std::same_as) +static constexpr auto get_colour_mapmode(T const*(P::*get_item)() const) { + return [get_item]( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + ProvinceDefinition const& province_definition = province.province_definition; + + T const* item = ([&province, &province_definition]() -> P const& { + if constexpr (std::same_as) { + return province_definition; + } else { + return province; + } + }().*get_item)(); + + if (item != nullptr) { + return colour_argb_t { item->get_colour(), ALPHA_VALUE }; + } else if (!province_definition.is_water()) { + return DEFAULT_COLOUR_WHITE; + } else { + return colour_argb_t::null(); + } + }; +} + +template +requires(std::same_as || std::same_as) +static constexpr auto get_colour_mapmode( + BASE_T const*(P::*get_base_item)() const, STRIPE_T const*(P::*get_stripe_item)() const +) { + return [get_base_item, get_stripe_item]( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + ProvinceDefinition const& province_definition = province.province_definition; + + P const& p = [&province, &province_definition]() -> P const& { + if constexpr (std::same_as) { + return province_definition; + } else { + return province; + } + }(); + + BASE_T const* base_item = (p.*get_base_item)(); + STRIPE_T const* stripe_item = (p.*get_stripe_item)(); + + Mapmode::base_stripe_t result = province_definition.is_water() ? colour_argb_t::null() : DEFAULT_COLOUR_WHITE; + + if (base_item != nullptr) { + result.base_colour = colour_argb_t { base_item->get_colour(), ALPHA_VALUE }; + } + if (stripe_item != nullptr) { + result.stripe_colour = colour_argb_t { stripe_item->get_colour(), ALPHA_VALUE }; + } + + return result; + }; +} + +template +static constexpr Mapmode::base_stripe_t shaded_mapmode( + ordered_map const& map +) { + const auto largest = get_largest_two_items(map); + if (largest.first != map.end()) { + const colour_argb_t base_colour = colour_argb_t { largest.first->first->get_colour(), ALPHA_VALUE }; + if (largest.second != map.end()) { + /* If second largest is at least a third... */ + if (largest.second->second * 3 >= get_total(map)) { + const colour_argb_t stripe_colour = colour_argb_t { largest.second->first->get_colour(), ALPHA_VALUE }; + return { base_colour, stripe_colour }; + } + } + return base_colour; + } + return colour_argb_t::null(); +} + +template +static constexpr auto shaded_mapmode(ordered_map const&(ProvinceInstance::*get_map)() const) { + return [get_map]( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + return shaded_mapmode((province.*get_map)()); + }; +} + +bool MapmodeManager::setup_mapmodes(MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager) { + if (mapmodes_are_locked()) { + spdlog::error_s("Cannot setup mapmodes - already locked!"); + return false; + } + + bool ret = true; + + // Default number of mapmodes + reserve_mapmodes(22); + + // The order of mapmodes matches their numbering, but is different from the order in which their buttons appear + ret &= add_mapmode( + "mapmode_terrain", + []( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + return colour_argb_t::null(); + }, + "MAPMODE_1", + false // Parchment mapmode not allowed + ); + ret &= add_mapmode("mapmode_political", get_colour_mapmode( + &ProvinceInstance::get_owner, &ProvinceInstance::get_controller + ), "MAPMODE_2"); + ret &= add_mapmode("mapmode_militancy", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_3"); + ret &= add_mapmode("mapmode_diplomatic", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_4"); + ret &= add_mapmode("mapmode_region", get_colour_mapmode(&ProvinceDefinition::get_region), "MAPMODE_5"); + BuildingType const* const infrastructure_building_type_ptr = building_type_manager.get_infrastructure_building_type(); + if (infrastructure_building_type_ptr == nullptr) { + spdlog::error_s("Cannot setup infrastructure mapmode because infrastructure_building_type is null."); + ret = false; + } else if (!infrastructure_building_type_ptr->province_building_index.has_value()) { + spdlog::error_s("Cannot setup infrastructure mapmode because infrastructure_building_type has no province_building_index."); + ret = false; + } else { + ret &= add_mapmode( + "mapmode_infrastructure", + [&building_type_manager, infrastructure_building_type_ptr]( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + if (province.province_definition.is_water()) { + return colour_argb_t::null(); + } + + BuildingType const& infrastructure_building_type = *infrastructure_building_type_ptr; + BuildingInstance const& infrastructure = province.get_buildings()[infrastructure_building_type.province_building_index.value()]; + const colour_argb_t::value_type val = colour_argb_t::colour_traits::component_from_fraction( + type_safe::get(infrastructure.get_level()), type_safe::get(infrastructure_building_type.max_level) + 1, 0.5f, 1.0f + ); + switch (infrastructure.get_expansion_state()) { + case BuildingInstance::ExpansionState::CannotExpand: + return colour_argb_t { val, 0, 0, ALPHA_VALUE }; + case BuildingInstance::ExpansionState::CanExpand: + return colour_argb_t { 0, 0, val, ALPHA_VALUE }; + default: + return colour_argb_t { 0, val, 0, ALPHA_VALUE }; + } + }, + "MAPMODE_6" + ); + } + ret &= add_mapmode("mapmode_colonial", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_7"); + ret &= add_mapmode("mapmode_administrative", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_8"); + ret &= add_mapmode("mapmode_recruitment", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_9"); + ret &= add_mapmode("mapmode_national_focus", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_10"); + ret &= add_mapmode("mapmode_rgo", get_colour_mapmode(&ProvinceInstance::get_rgo_good), "MAPMODE_11"); + ret &= add_mapmode( + "mapmode_population", + []( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + // TODO - explore non-linear scaling to have more variation among non-massive provinces + // TODO - when selecting a province, only show the population of provinces controlled (or owned?) + // by the same country, relative to the most populous province in that set of provinces + if (!province.province_definition.is_water()) { + const colour_argb_t::value_type val = colour_argb_t::colour_traits::component_from_fraction( + type_safe::get(province.get_total_population()), type_safe::get(map_instance.get_highest_province_population()) + 1, 0.1f, 1.0f + ); + return colour_argb_t { 0, val, 0, ALPHA_VALUE }; + } else { + return colour_argb_t::null(); + } + }, + "MAPMODE_12" + ); + ret &= add_mapmode("mapmode_culture", shaded_mapmode(&ProvinceInstance::get_population_by_culture), "MAPMODE_13"); + ret &= add_mapmode("mapmode_sphere", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_14"); + ret &= add_mapmode("mapmode_supply", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_15"); + ret &= add_mapmode("mapmode_party_loyalty", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_16"); + ret &= add_mapmode("mapmode_ranking", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_17"); + ret &= add_mapmode("mapmode_migration", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_18"); + ret &= add_mapmode("mapmode_civilisation_level", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_19"); + ret &= add_mapmode("mapmode_relations", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_20"); + ret &= add_mapmode("mapmode_crisis", Mapmode::ERROR_MAPMODE.get_colour_func(), "MAPMODE_21"); + ret &= add_mapmode( + "mapmode_naval", + []( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + ProvinceDefinition const& province_definition = province.province_definition; + + if (province_definition.has_port()) { + return (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); + } else if (!province_definition.is_water()) { + return (0x333333_argb).with_alpha(ALPHA_VALUE); + } else { + return colour_argb_t::null(); + } + }, + "MAPMODE_22" + ); + + /* + *** CUSTOM MAPMODES FOR TESTING *** + - For these mapmodes to have a button in-game you either need to remove the same number of default mapmodes + or add more mapmode buttons (GUIIconButton nodes with path ^".../Menubar/menubar/mapmode_") + */ + if constexpr (false) { + ret &= add_mapmode( + "mapmode_province", + []( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + return colour_argb_t { province.province_definition.get_colour(), ALPHA_VALUE }; + } + ); + ret &= add_mapmode( + "mapmode_index", + [&map_definition]( + MapInstance const& map_instance, ProvinceInstance const& province, + CountryInstance const* player_country, ProvinceInstance const* selected_province + ) -> Mapmode::base_stripe_t { + const colour_argb_t::value_type f = colour_argb_t::colour_traits::component_from_fraction( + province.province_definition.get_province_number(), map_definition.get_province_definition_count() + 1 + ); + return colour_argb_t::fill_as(f).with_alpha(ALPHA_VALUE); + } + ); + ret &= add_mapmode("mapmode_religion", shaded_mapmode(&ProvinceInstance::get_population_by_religion)); + ret &= add_mapmode("mapmode_terrain_type", get_colour_mapmode(&ProvinceInstance::get_terrain_type)); + ret &= add_mapmode( + "mapmode_adjacencies", + []( + MapInstance const& map_instance, ProvinceInstance const& scoped_prov_instance, + CountryInstance const* player_country, ProvinceInstance const* selected_prov_instance_ptr + ) -> Mapmode::base_stripe_t { + if (selected_prov_instance_ptr == nullptr) { + return colour_argb_t::null(); + } + + ProvinceDefinition const& selected_prov_definition = selected_prov_instance_ptr->province_definition; + + if (*selected_prov_instance_ptr == scoped_prov_instance) { + return (0xFFFFFF_argb).with_alpha(ALPHA_VALUE); + } + + ProvinceDefinition const& scoped_prov_definition = scoped_prov_instance.province_definition; + + colour_argb_t base = colour_argb_t::null(), stripe = colour_argb_t::null(); + ProvinceDefinition::adjacency_t const* adj = selected_prov_definition.get_adjacency_to( + scoped_prov_definition + ); + + if (adj != nullptr) { + colour_argb_t::integer_type base_int; + switch (adj->get_type()) { + using enum ProvinceDefinition::adjacency_t::type_t; + case LAND: base_int = 0x00FF00; break; + case WATER: base_int = 0x0000FF; break; + case COASTAL: base_int = 0xF9D199; break; + case IMPASSABLE: base_int = 0x8B4513; break; + case STRAIT: base_int = 0x00FFFF; break; + case CANAL: base_int = 0x888888; break; + default: base_int = 0xFF0000; break; + } + base = colour_argb_t::from_integer(base_int).with_alpha(ALPHA_VALUE); + stripe = base; + } + + if (selected_prov_definition.has_adjacency_going_through(scoped_prov_definition)) { + stripe = (0xFFFF00_argb).with_alpha(ALPHA_VALUE); + } + + return { base, stripe }; + } + ); + } + + lock_mapmodes(); + + return ret; +} diff --git a/src/openvic-simulation/map/MapmodeManager.hpp b/src/openvic-simulation/map/MapmodeManager.hpp new file mode 100644 index 000000000..ad6c6d7ed --- /dev/null +++ b/src/openvic-simulation/map/MapmodeManager.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "openvic-simulation/map/MapMode.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct BuildingTypeManager; + struct MapDefinition; + struct MapInstance; + struct ProvinceInstance; + struct CountryInstance; + + struct MapmodeManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(mapmode); + + public: + constexpr MapmodeManager() {}; + + bool add_mapmode( + std::string_view identifier, + Mapmode::colour_func_t colour_func, + std::string_view localisation_key = {}, + bool parchment_mapmode_allowed = true + ); + + /* The mapmode colour image contains of a list of base colours and stripe colours. Each colour is four bytes + * in RGBA format, with the alpha value being used to interpolate with the terrain colour, so A = 0 is fully terrain + * and A = 255 is fully the RGB colour packaged with A. The base and stripe colours for each province are packed + * together adjacently, so each province's entry is 8 bytes long. + * The list contains all provinces indexed by their number + index 0 for the "null province". */ + bool generate_mapmode_colours( + MapInstance const& map_instance, Mapmode const* mapmode, + CountryInstance const* player_country, ProvinceInstance const* selected_province, + uint8_t* target + ) const; + + bool setup_mapmodes(MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager); + }; +} diff --git a/src/openvic-simulation/map/ProvinceDefinition.cpp b/src/openvic-simulation/map/ProvinceDefinition.cpp index ee0a49181..35e45a4c2 100644 --- a/src/openvic-simulation/map/ProvinceDefinition.cpp +++ b/src/openvic-simulation/map/ProvinceDefinition.cpp @@ -4,6 +4,7 @@ #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/economy/BuildingType.hpp" +#include "openvic-simulation/economy/BuildingTypeManager.hpp" #include "openvic-simulation/map/MapDefinition.hpp" #include "openvic-simulation/types/fixed_point/Math.hpp" @@ -22,7 +23,7 @@ memory::string ProvinceDefinition::to_string() const { } bool ProvinceDefinition::load_positions( - MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager, ast::NodeCPtr root + MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager, ovdl::v2script::ast::Node const* root ) { const fixed_point_t map_height = map_definition.get_height(); @@ -44,7 +45,7 @@ bool ProvinceDefinition::load_positions( "building_position", ZERO_OR_ONE, building_type_manager.expect_building_type_dictionary_reserve_length( positions.building_position, - [this, map_height](BuildingType const& type, ast::NodeCPtr value) -> bool { + [this, map_height](BuildingType const& type, ovdl::v2script::ast::Node const* value) -> bool { return expect_fvec2(flip_y_callback(map_callback(positions.building_position, &type), map_height))(value); } ), diff --git a/src/openvic-simulation/map/ProvinceDefinition.hpp b/src/openvic-simulation/map/ProvinceDefinition.hpp index 29c991c0b..d69d08d9b 100644 --- a/src/openvic-simulation/map/ProvinceDefinition.hpp +++ b/src/openvic-simulation/map/ProvinceDefinition.hpp @@ -6,7 +6,7 @@ #include #include "openvic-simulation/core/memory/Vector.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" @@ -138,7 +138,7 @@ namespace OpenVic { /* The positions' y coordinates need to be inverted. */ bool load_positions( - MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager, ast::NodeCPtr root + MapDefinition const& map_definition, BuildingTypeManager const& building_type_manager, ovdl::v2script::ast::Node const* root ); fvec2_t get_text_position() const; diff --git a/src/openvic-simulation/map/ProvinceInstance.cpp b/src/openvic-simulation/map/ProvinceInstance.cpp index b10a92072..6d0e0cdaf 100644 --- a/src/openvic-simulation/map/ProvinceInstance.cpp +++ b/src/openvic-simulation/map/ProvinceInstance.cpp @@ -4,18 +4,26 @@ #include -#include "openvic-simulation/country/CountryDefinition.hpp" #include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/country/CountryInstanceManager.hpp" #include "openvic-simulation/defines/MilitaryDefines.hpp" -#include "openvic-simulation/DefinitionManager.hpp" #include "openvic-simulation/economy/BuildingInstance.hpp" -#include "openvic-simulation/economy/BuildingType.hpp" +#include "openvic-simulation/economy/BuildingTypeManager.hpp" #include "openvic-simulation/economy/production/Employee.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" -#include "openvic-simulation/InstanceManager.hpp" +#include "openvic-simulation/history/ProvinceHistory.hpp" +#include "openvic-simulation/map/Crime.hpp" +#include "openvic-simulation/map/MapInstance.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" +#include "openvic-simulation/map/Region.hpp" +#include "openvic-simulation/map/TerrainType.hpp" +#include "openvic-simulation/military/UnitInstanceGroup.hpp" +#include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/misc/GameRulesManager.hpp" #include "openvic-simulation/modifier/StaticModifierCache.hpp" +#include "openvic-simulation/politics/Reform.hpp" +#include "openvic-simulation/population/PopType.hpp" +#include "openvic-simulation/population/PopValuesFromProvince.hpp" #include "openvic-simulation/types/ConstructorTags.hpp" #include "openvic-simulation/types/TypedIndices.hpp" @@ -42,6 +50,8 @@ ProvinceInstance::ProvinceInstance( } } { + adjacencies.reserve(new_province_definition.get_adjacencies().size()); + adjacent_nonempty_land_provinces.reserve(new_province_definition.get_adjacencies().size()); modifier_sum.set_this_source(this); rgo.setup_location_ptr(*this); } @@ -216,10 +226,16 @@ void ProvinceInstance::_update_pops(MilitaryDefines const& military_defines) { using enum colony_status_t; - const fixed_point_t pop_size_per_regiment_multiplier = - colony_status == PROTECTORATE ? military_defines.get_pop_size_per_regiment_protectorate_multiplier() - : colony_status == COLONY ? military_defines.get_pop_size_per_regiment_colony_multiplier() - : is_owner_core() ? fixed_point_t::_1 : military_defines.get_pop_size_per_regiment_non_core_multiplier(); + fixed_point_t pop_size_per_regiment_multiplier; + if (colony_status == PROTECTORATE) { + pop_size_per_regiment_multiplier = military_defines.get_pop_size_per_regiment_protectorate_multiplier(); + } else if (colony_status == COLONY) { + pop_size_per_regiment_multiplier = military_defines.get_pop_size_per_regiment_colony_multiplier(); + } else if (is_owner_core()) { + pop_size_per_regiment_multiplier = fixed_point_t::_1; + } else { + pop_size_per_regiment_multiplier = military_defines.get_pop_size_per_regiment_non_core_multiplier(); + } for (Pop& pop : pops) { pops_cache_by_type[pop.get_type().index].push_back(pop); @@ -233,7 +249,7 @@ void ProvinceInstance::_update_pops(MilitaryDefines const& military_defines) { normalise_pops_aggregate(); } -void ProvinceInstance::update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache) { +void ProvinceInstance::update_modifier_sum(const Date today, StaticModifierCache const& static_modifier_cache) { // Update sum of direct province modifiers modifier_sum.clear(); @@ -284,7 +300,21 @@ void ProvinceInstance::update_modifier_sum(Date today, StaticModifierCache const } if (controller != nullptr) { - controller->contribute_province_modifier_sum(modifier_sum); + controller->make_room_for_province_modifier_sum(modifier_sum); + } +} + +void ProvinceInstance::update_adjecencies() { + has_empty_adjacent_province = false; + // We assume there are no duplicate province adjacencies, so each adjacency.get_to() is unique in the loop below + adjacent_nonempty_land_provinces.clear(); + for (const std::reference_wrapper adjacency_wrapper : get_adjacencies()) { + ProvinceInstance const& adjacency = adjacency_wrapper.get(); + if (adjacency.is_empty()) { + has_empty_adjacent_province = true; + } else if (!adjacency.province_definition.is_water()) { + adjacent_nonempty_land_provinces.emplace_back(adjacency); + } } } @@ -326,23 +356,8 @@ bool ProvinceInstance::convert_rgo_worker_pops_to_equivalent( return is_valid_operation; } -void ProvinceInstance::update_gamestate(InstanceManager const& instance_manager) { - has_empty_adjacent_province = false; - // We assume there are no duplicate province adjacencies, so each adjacency.get_to() is unique in the loop below - adjacent_nonempty_land_provinces.clear(); - - MapInstance const& map_instance = instance_manager.get_map_instance(); - for (ProvinceDefinition::adjacency_t const& adjacency : province_definition.get_adjacencies()) { - ProvinceDefinition const& adjacent_to_definition = adjacency.get_to(); - ProvinceInstance const& adjacent_to_instance = map_instance.get_province_instance_by_definition(adjacent_to_definition); - - if (adjacent_to_instance.is_empty()) { - has_empty_adjacent_province = true; - } else if (!adjacent_to_definition.is_water()) { - adjacent_nonempty_land_provinces.emplace_back(adjacent_to_instance); - } - } - +void ProvinceInstance::update_gamestate(const Date today, MilitaryDefines const& military_defines) { + update_adjecencies(); land_regiment_count = 0; for (ArmyInstance const& army : armies) { land_regiment_count += army.get_unit_count(); @@ -357,12 +372,10 @@ void ProvinceInstance::update_gamestate(InstanceManager const& instance_manager) occupation_duration = 0; } - const Date today = instance_manager.get_today(); - for (BuildingInstance& building : buildings) { building.update_gamestate(today); } - _update_pops(instance_manager.definition_manager.get_define_manager().get_military_defines()); + _update_pops(military_defines); } void ProvinceInstance::province_tick( @@ -498,6 +511,7 @@ bool ProvinceInstance::apply_history_to_province(ProvinceHistoryEntry const& ent void ProvinceInstance::initialise_for_new_game( const Date today, + MapInstance const& map_instance, PopValuesFromProvince& reusable_pop_values, RandomU32& random_number_generator, TypedSpan reusable_goods_mask, @@ -506,6 +520,12 @@ void ProvinceInstance::initialise_for_new_game( VECTORS_FOR_PROVINCE_TICK > reusable_vectors ) { + for (ProvinceDefinition::adjacency_t const& adjacency : province_definition.get_adjacencies()) { + ProvinceDefinition const& adjacent_to_definition = adjacency.get_to(); + ProvinceInstance const& adjacent_to_instance = map_instance.get_province_instance_by_definition(adjacent_to_definition); + adjacencies.emplace_back(adjacent_to_instance); + } + update_adjecencies(); initialise_rgo(); province_tick( today, diff --git a/src/openvic-simulation/map/ProvinceInstance.hpp b/src/openvic-simulation/map/ProvinceInstance.hpp index 7e6038a60..8cb248c2c 100644 --- a/src/openvic-simulation/map/ProvinceInstance.hpp +++ b/src/openvic-simulation/map/ProvinceInstance.hpp @@ -95,6 +95,7 @@ namespace OpenVic { bool PROPERTY_RW(connected_to_capital, false); bool PROPERTY_RW(is_overseas, false); bool PROPERTY(has_empty_adjacent_province, false); + memory::vector> SPAN_PROPERTY(adjacencies); memory::vector> SPAN_PROPERTY(adjacent_nonempty_land_provinces); Crime const* PROPERTY_RW(crime, nullptr); ResourceGatheringOperation PROPERTY(rgo); @@ -123,6 +124,7 @@ namespace OpenVic { ProductionType const& production_type ); void initialise_rgo(); + void update_adjecencies(); memory::FixedVector< memory::vector>, @@ -188,8 +190,8 @@ namespace OpenVic { PopDeps const& pop_deps ); size_t get_pop_count() const; + void update_modifier_sum(const Date today, StaticModifierCache const& static_modifier_cache); - void update_modifier_sum(Date today, StaticModifierCache const& static_modifier_cache); fixed_point_t get_modifier_effect_value(ModifierEffect const& effect) const; void for_each_contributing_modifier(ModifierEffect const& effect, ContributingModifierCallback auto callback) const { @@ -204,7 +206,7 @@ namespace OpenVic { get_owner_modifier_sum().for_each_contributing_modifier(effect, std::move(callback)); } } - void update_gamestate(InstanceManager const& instance_manager); + void update_gamestate(const Date today, MilitaryDefines const& military_defines); static constexpr size_t VECTORS_FOR_PROVINCE_TICK = std::max( ResourceGatheringOperation::VECTORS_FOR_RGO_TICK, Pop::VECTORS_FOR_POP_TICK @@ -221,6 +223,7 @@ namespace OpenVic { ); void initialise_for_new_game( const Date today, + MapInstance const& map_instance, PopValuesFromProvince& reusable_pop_values, RandomU32& random_number_generator, TypedSpan reusable_goods_mask, diff --git a/src/openvic-simulation/map/State.cpp b/src/openvic-simulation/map/State.cpp index 083f48618..2aa8a7be1 100644 --- a/src/openvic-simulation/map/State.cpp +++ b/src/openvic-simulation/map/State.cpp @@ -1,14 +1,11 @@ #include "State.hpp" -#include "openvic-simulation/core/error/ErrorMacros.hpp" #include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/map/MapDefinition.hpp" -#include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/map/Region.hpp" #include "openvic-simulation/population/Pop.hpp" #include "openvic-simulation/population/PopsAggregateDeps.hpp" -#include "openvic-simulation/population/PopType.hpp" #include "openvic-simulation/types/ConstructorTags.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -129,106 +126,6 @@ void StateSet::update_gamestate() { } } -bool StateManager::add_state_set( - MapInstance& map_instance, Region const& region, - PopsAggregateDeps const& pops_aggregate_deps, - forwardable_span strata_keys, - forwardable_span pop_type_keys -) { - OV_ERR_FAIL_COND_V_MSG(region.is_meta, false, memory::fmt::format("Cannot use meta region \"{}\" as state template!", region)); - OV_ERR_FAIL_COND_V_MSG(region.empty(), false, memory::fmt::format("Cannot use empty region \"{}\" as state template!", region)); - - memory::vector>> temp_provinces; - - for (ProvinceDefinition const& province : region.get_provinces()) { - ProvinceInstance& province_instance = map_instance.get_province_instance_by_definition(province); - - // add to existing state if shared owner & status... - for (memory::vector>& provinces : temp_provinces) { - if (provinces_belong_in_same_state(provinces.front(), province_instance)) { - provinces.push_back(province_instance); - // jump to the end of the outer loop, skipping the new state code - goto loop_end; - } - } - - // ...otherwise start a new state - temp_provinces.push_back({ province_instance }); - - loop_end:; - /* Either the province was added to an existing state and the program jumped to here, - * or it was used to create a new state and the program arrived here normally. */ - } - - state_sets.emplace_back(region); - - StateSet& state_set = state_sets.back(); - - // Reserve space for the maximum number of states (one per province) - state_set.states.reserve(region.size()); - - for (memory::vector>& provinces : temp_provinces) { - ProvinceInstance& capital = provinces.front(); - - State& state = *state_set.states.emplace( - /* TODO: capital province logic */ - state_set, &capital, - std::move(provinces), capital.get_colony_status(), - pops_aggregate_deps - ); - - for (ProvinceInstance& province : state.get_provinces()) { - province.set_state(&state); - } - } - - return true; -} - -bool StateManager::generate_states( - MapDefinition const& map_definition, - MapInstance& map_instance, - PopsAggregateDeps const& pops_aggregate_deps, - forwardable_span strata_keys, - forwardable_span pop_type_keys -) { - state_sets.clear(); - state_sets.reserve(map_definition.get_region_count()); - - bool ret = true; - size_t state_count = 0; - - for (Region const& region : map_definition.get_regions()) { - if (!region.is_meta) { - if (add_state_set( - map_instance, - region, - pops_aggregate_deps, - strata_keys, - pop_type_keys - )) { - state_count += state_sets.back().get_state_count(); - } else { - ret = false; - } - } - } - - SPDLOG_INFO("Generated {} states across {} state sets.", state_count, state_sets.size()); - - return ret; -} - -void StateManager::reset() { - state_sets.clear(); -} - -void StateManager::update_gamestate() { - for (StateSet& state_set : state_sets) { - state_set.update_gamestate(); - } -} - fmt::format_context::iterator fmt::formatter::format(State const& state, fmt::format_context& ctx) const { return formatter::format(state.get_identifier(), ctx); } \ No newline at end of file diff --git a/src/openvic-simulation/map/State.hpp b/src/openvic-simulation/map/State.hpp index 0c3a7409e..314715fb7 100644 --- a/src/openvic-simulation/map/State.hpp +++ b/src/openvic-simulation/map/State.hpp @@ -6,7 +6,6 @@ #include "openvic-simulation/core/memory/Colony.hpp" #include "openvic-simulation/core/memory/Vector.hpp" -#include "openvic-simulation/core/portable/ForwardableSpan.hpp" #include "openvic-simulation/population/PopsAggregate.hpp" #include "openvic-simulation/types/ColonyStatus.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -92,37 +91,6 @@ namespace OpenVic { void update_gamestate(); }; - - struct MapInstance; - - /* Contains all current states.*/ - struct StateManager { - private: - memory::vector SPAN_PROPERTY(state_sets); - - bool add_state_set( - MapInstance& map_instance, Region const& region, - PopsAggregateDeps const& pops_aggregate_deps, - forwardable_span strata_keys, - forwardable_span pop_type_keys - ); - - public: - /* Creates states from current province gamestate & regions, sets province state value. - * After this function, the `regions` property is unmanaged and must be carefully updated and - * validated by functions that modify it. */ - bool generate_states( - MapDefinition const& map_definition, - MapInstance& map_instance, - PopsAggregateDeps const& pops_aggregate_deps, - forwardable_span strata_keys, - forwardable_span pop_type_keys - ); - - void reset(); - - void update_gamestate(); - }; } template<> diff --git a/src/openvic-simulation/map/StateManager.cpp b/src/openvic-simulation/map/StateManager.cpp new file mode 100644 index 000000000..c52072caf --- /dev/null +++ b/src/openvic-simulation/map/StateManager.cpp @@ -0,0 +1,118 @@ +#include "StateManager.hpp" + +#include "openvic-simulation/core/error/ErrorMacros.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/map/MapDefinition.hpp" +#include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/map/Region.hpp" +#include "openvic-simulation/population/Pop.hpp" +#include "openvic-simulation/population/PopsAggregateDeps.hpp" +#include "openvic-simulation/population/PopType.hpp" + +using namespace OpenVic; + +// Whether two provinces in the same region should be grouped into the same state or not. +static bool provinces_belong_in_same_state(ProvinceInstance const& lhs, ProvinceInstance const& rhs) { + return lhs.get_owner() == rhs.get_owner() && lhs.get_colony_status() == rhs.get_colony_status(); +} + +bool StateManager::add_state_set( + MapInstance& map_instance, Region const& region, + PopsAggregateDeps const& pops_aggregate_deps, + forwardable_span strata_keys, + forwardable_span pop_type_keys +) { + OV_ERR_FAIL_COND_V_MSG(region.is_meta, false, memory::fmt::format("Cannot use meta region \"{}\" as state template!", region)); + OV_ERR_FAIL_COND_V_MSG(region.empty(), false, memory::fmt::format("Cannot use empty region \"{}\" as state template!", region)); + + memory::vector>> temp_provinces; + + for (ProvinceDefinition const& province : region.get_provinces()) { + ProvinceInstance& province_instance = map_instance.get_province_instance_by_definition(province); + + // add to existing state if shared owner & status... + for (memory::vector>& provinces : temp_provinces) { + if (provinces_belong_in_same_state(provinces.front(), province_instance)) { + provinces.push_back(province_instance); + // jump to the end of the outer loop, skipping the new state code + goto loop_end; + } + } + + // ...otherwise start a new state + temp_provinces.push_back({ province_instance }); + + loop_end:; + /* Either the province was added to an existing state and the program jumped to here, + * or it was used to create a new state and the program arrived here normally. */ + } + + state_sets.emplace_back(region); + + StateSet& state_set = state_sets.back(); + + // Reserve space for the maximum number of states (one per province) + state_set.states.reserve(region.size()); + + for (memory::vector>& provinces : temp_provinces) { + ProvinceInstance& capital = provinces.front(); + + State& state = *state_set.states.emplace( + /* TODO: capital province logic */ + state_set, &capital, + std::move(provinces), capital.get_colony_status(), + pops_aggregate_deps + ); + + for (ProvinceInstance& province : state.get_provinces()) { + province.set_state(&state); + } + } + + return true; +} + +bool StateManager::generate_states( + MapDefinition const& map_definition, + MapInstance& map_instance, + PopsAggregateDeps const& pops_aggregate_deps, + forwardable_span strata_keys, + forwardable_span pop_type_keys +) { + state_sets.clear(); + state_sets.reserve(map_definition.get_region_count()); + + bool ret = true; + size_t state_count = 0; + + for (Region const& region : map_definition.get_regions()) { + if (!region.is_meta) { + if (add_state_set( + map_instance, + region, + pops_aggregate_deps, + strata_keys, + pop_type_keys + )) { + state_count += state_sets.back().get_state_count(); + } else { + ret = false; + } + } + } + + SPDLOG_INFO("Generated {} states across {} state sets.", state_count, state_sets.size()); + + return ret; +} + +void StateManager::reset() { + state_sets.clear(); +} + +void StateManager::update_gamestate() { + for (StateSet& state_set : state_sets) { + state_set.update_gamestate(); + } +} \ No newline at end of file diff --git a/src/openvic-simulation/map/StateManager.hpp b/src/openvic-simulation/map/StateManager.hpp new file mode 100644 index 000000000..9d67fecf2 --- /dev/null +++ b/src/openvic-simulation/map/StateManager.hpp @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/core/portable/ForwardableSpan.hpp" +#include "openvic-simulation/map/State.hpp" +#include "openvic-simulation/population/PopsAggregate.hpp" +#include "openvic-simulation/utility/Getters.hpp" + +namespace OpenVic { + struct BaseIssue; + struct CountryInstance; + struct CountryParty; + struct Culture; + struct MapDefinition; + struct Pop; + struct PopsAggregateDeps; + struct PopType; + struct ProvinceInstance; + struct Religion; + struct Strata; + + struct MapInstance; + + /* Contains all current states.*/ + struct StateManager { + private: + memory::vector SPAN_PROPERTY(state_sets); + + bool add_state_set( + MapInstance& map_instance, Region const& region, + PopsAggregateDeps const& pops_aggregate_deps, + forwardable_span strata_keys, + forwardable_span pop_type_keys + ); + + public: + /* Creates states from current province gamestate & regions, sets province state value. + * After this function, the `regions` property is unmanaged and must be carefully updated and + * validated by functions that modify it. */ + bool generate_states( + MapDefinition const& map_definition, + MapInstance& map_instance, + PopsAggregateDeps const& pops_aggregate_deps, + forwardable_span strata_keys, + forwardable_span pop_type_keys + ); + + void reset(); + + void update_gamestate(); + }; +} diff --git a/src/openvic-simulation/map/TerrainType.cpp b/src/openvic-simulation/map/TerrainType.cpp index 329a40ffa..cb7de49ad 100644 --- a/src/openvic-simulation/map/TerrainType.cpp +++ b/src/openvic-simulation/map/TerrainType.cpp @@ -2,14 +2,10 @@ #include -#include "openvic-simulation/core/FormatValidate.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" -#include "openvic-simulation/types/FixedVector.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; TerrainType::TerrainType( index_t new_index, @@ -39,220 +35,3 @@ TerrainTypeMapping::TerrainTypeMapping( terrain_indices { std::move(new_terrain_indices) }, priority { new_priority }, has_texture { new_has_texture } {} - -bool TerrainTypeManager::generate_modifiers(ModifierManager& modifier_manager) const { - using enum ModifierEffect::format_t; - memory::FixedVector& unit_terrain_effects = - modifier_manager.modifier_effect_cache.unit_terrain_effects; - - unit_terrain_effects = std::move( - decltype(ModifierEffectCache::unit_terrain_effects) { - generate_values, - terrain_type_index_t(get_terrain_type_count()) - } - ); - - bool ret = true; - for (TerrainType const& terrain_type : get_terrain_types()) { - const std::string_view identifier = terrain_type.get_identifier(); - ModifierEffectCache::unit_terrain_effects_t& this_unit_terrain_effects = unit_terrain_effects[terrain_type.index]; - ret &= modifier_manager.register_unit_terrain_modifier_effect( - this_unit_terrain_effects.attack, ModifierManager::get_flat_identifier("attack", identifier), - FORMAT_x100_1DP_PC_POS, "UA_ATTACK" - ); - ret &= modifier_manager.register_unit_terrain_modifier_effect( - this_unit_terrain_effects.defence, ModifierManager::get_flat_identifier("defence", identifier), - FORMAT_x100_1DP_PC_POS, "UA_DEFENCE" - ); - ret &= modifier_manager.register_unit_terrain_modifier_effect( - this_unit_terrain_effects.movement, ModifierManager::get_flat_identifier("movement", identifier), - FORMAT_x100_1DP_PC_POS, "UA_MOVEMENT" - ); - ret &= modifier_manager.register_unit_terrain_modifier_effect( - this_unit_terrain_effects.attrition, ModifierManager::get_flat_identifier("attrition", identifier), - FORMAT_x1_1DP_PC_NEG, "UA_ATTRITION" - ); - } - return ret; -} - -bool TerrainTypeManager::add_terrain_type( - std::string_view identifier, - colour_t colour, - ModifierValue&& values, - fixed_point_t movement_cost, - fixed_point_t defence_bonus, - fixed_point_t combat_width_percentage_change, - bool is_water -) { - if (identifier.empty()) { - spdlog::error_s("Invalid terrain type identifier - empty!"); - return false; - } - - if (movement_cost < 0) { - spdlog::error_s( - "Invalid movement cost for terrain type \"{}\": {} (cannot be negative!)", - identifier, movement_cost - ); - return false; - } - - return terrain_types.emplace_item( - identifier, - TerrainType::index_t { get_terrain_type_count() }, identifier, - colour, std::move(values), movement_cost, defence_bonus, combat_width_percentage_change, is_water - ); -} - -bool TerrainTypeManager::add_terrain_type_mapping( - std::string_view identifier, - TerrainType const* type, - memory::vector&& terrain_indices, - TerrainTypeMapping::index_t priority, - bool has_texture -) { - if (!terrain_types_are_locked()) { - spdlog::error_s("Cannot register terrain type mappings until terrain types are locked!"); - return false; - } - - if (identifier.empty()) { - spdlog::error_s("Invalid terrain type mapping identifier - empty!"); - return false; - } - - if (type == nullptr) { - spdlog::error_s("Null terrain type for mapping {}", identifier); - return false; - } - - bool ret = true; - - for (TerrainTypeMapping::index_t idx : terrain_indices) { - const terrain_type_mappings_map_t::const_iterator it = terrain_type_mappings_map.find(idx); - - if (it == terrain_type_mappings_map.end()) { - terrain_type_mappings_map.emplace(idx, terrain_type_mappings.size()); - } else { - spdlog::error_s( - "Terrain index {} cannot map to {} as it already maps to {}", - static_cast(idx), identifier, ovfmt::validate(terrain_type_mappings.get_item_by_index(it->second)) - ); - ret = false; - } - } - - ret &= terrain_type_mappings.emplace_item( - identifier, - identifier, *type, std::move(terrain_indices), priority, has_texture - ); - - return ret; -} - -node_callback_t TerrainTypeManager::_load_terrain_type_categories(ModifierManager const& modifier_manager) { - return [this, &modifier_manager](ast::NodeCPtr root) -> bool { - const bool ret = expect_dictionary_reserve_length(terrain_types, - [this, &modifier_manager](std::string_view type_key, ast::NodeCPtr type_node) -> bool { - ModifierValue values; - fixed_point_t movement_cost, defence_bonus, combat_width_percentage_change; - colour_t colour = colour_t::null(); - bool is_water = false; - - bool ret = NodeTools::expect_dictionary_keys_and_default( - modifier_manager.expect_terrain_modifier(values), - "movement_cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(movement_cost)), - "defence", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(defence_bonus)), - "combat_width", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(combat_width_percentage_change)), - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), - "is_water", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_water)) - )(type_node); - - ret &= add_terrain_type( - type_key, colour, std::move(values), movement_cost, defence_bonus, combat_width_percentage_change, is_water - ); - - return ret; - } - )(root); - - lock_terrain_types(); - - return ret; - }; -} - -bool TerrainTypeManager::_load_terrain_type_mapping(std::string_view mapping_key, ast::NodeCPtr mapping_value) { - if (terrain_texture_limit <= 0) { - spdlog::error_s("Cannot define terrain type mapping before terrain texture limit: {}", mapping_key); - return false; - } - - if (!terrain_types_are_locked()) { - spdlog::error_s("Cannot define terrain type mapping before categories: {}", mapping_key); - return false; - } - - TerrainType const* type = nullptr; - memory::vector terrain_indices; - TerrainTypeMapping::index_t priority = 0; - bool has_texture = true; - - bool ret = expect_dictionary_keys_and_default( - [mapping_key](std::string_view key, ast::NodeCPtr value) -> bool { - spdlog::warn_s("Invalid key {} in terrain mapping {}", key, mapping_key); - return true; - }, - "type", ONE_EXACTLY, expect_terrain_type_identifier(assign_variable_callback_pointer(type)), - "color", ONE_EXACTLY, expect_list_reserve_length(terrain_indices, expect_uint( - [&terrain_indices](TerrainTypeMapping::index_t val) -> bool { - if (std::find(terrain_indices.begin(), terrain_indices.end(), val) == terrain_indices.end()) { - terrain_indices.push_back(val); - return true; - } - spdlog::error_s("Repeat terrain type mapping index: {}", val); - return false; - } - )), - "priority", ZERO_OR_ONE, expect_uint(assign_variable_callback(priority)), - "has_texture", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_texture)) - )(mapping_value); - - if (has_texture && ++terrain_texture_count == terrain_texture_limit + 1) { - spdlog::warn_s("More terrain textures than limit!"); - } - - ret &= add_terrain_type_mapping(mapping_key, type, std::move(terrain_indices), priority, has_texture); - - return true; -} - -TerrainTypeMapping const* TerrainTypeManager::get_terrain_type_mapping_for(TerrainTypeMapping::index_t idx) const { - const terrain_type_mappings_map_t::const_iterator it = terrain_type_mappings_map.find(idx); - - if (it != terrain_type_mappings_map.end()) { - return terrain_type_mappings.get_item_by_index(it->second); - } - - return nullptr; -} - -TerrainTypeMapping::index_t TerrainTypeManager::get_terrain_texture_limit() const { - return terrain_texture_limit; -} - -bool TerrainTypeManager::load_terrain_types(ModifierManager const& modifier_manager, ast::NodeCPtr root) { - const bool ret = expect_dictionary_keys_reserve_length_and_default( - terrain_type_mappings, - [this](std::string_view key, ast::NodeCPtr value) -> bool { - return _load_terrain_type_mapping(key, value); - }, - "terrain", ONE_EXACTLY, expect_uint(assign_variable_callback(terrain_texture_limit)), - "categories", ONE_EXACTLY, _load_terrain_type_categories(modifier_manager) - )(root); - - lock_terrain_type_mappings(); - - return ret; -} diff --git a/src/openvic-simulation/map/TerrainType.hpp b/src/openvic-simulation/map/TerrainType.hpp index 8527b8f45..3082ffc97 100644 --- a/src/openvic-simulation/map/TerrainType.hpp +++ b/src/openvic-simulation/map/TerrainType.hpp @@ -4,7 +4,6 @@ #include "openvic-simulation/modifier/Modifier.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { @@ -53,44 +52,4 @@ namespace OpenVic { ); TerrainTypeMapping(TerrainTypeMapping&&) = default; }; - - struct TerrainTypeManager { - private: - using terrain_type_mappings_map_t = ordered_map; - - IdentifierRegistry IDENTIFIER_REGISTRY(terrain_type); - IdentifierRegistry IDENTIFIER_REGISTRY(terrain_type_mapping); - terrain_type_mappings_map_t terrain_type_mappings_map; - - TerrainTypeMapping::index_t terrain_texture_limit = 0, terrain_texture_count = 0; - - NodeTools::node_callback_t _load_terrain_type_categories(ModifierManager const& modifier_manager); - bool _load_terrain_type_mapping(std::string_view key, ast::NodeCPtr value); - - public: - bool add_terrain_type( - std::string_view identifier, - colour_t colour, - ModifierValue&& values, - fixed_point_t movement_cost, - fixed_point_t defence_bonus, - fixed_point_t combat_width_percentage_change, - bool is_water - ); - - bool add_terrain_type_mapping( - std::string_view identifier, - TerrainType const* type, - memory::vector&& terrain_indices, - TerrainTypeMapping::index_t priority, - bool has_texture - ); - - TerrainTypeMapping const* get_terrain_type_mapping_for(TerrainTypeMapping::index_t idx) const; - - TerrainTypeMapping::index_t get_terrain_texture_limit() const; - - bool load_terrain_types(ModifierManager const& modifier_manager, ast::NodeCPtr root); - bool generate_modifiers(ModifierManager& modifier_manager) const; - }; } diff --git a/src/openvic-simulation/map/TerrainTypeManager.cpp b/src/openvic-simulation/map/TerrainTypeManager.cpp new file mode 100644 index 000000000..9d5476392 --- /dev/null +++ b/src/openvic-simulation/map/TerrainTypeManager.cpp @@ -0,0 +1,231 @@ +#include "TerrainTypeManager.hpp" + +#include + +#include "openvic-simulation/core/FormatValidate.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/types/FixedVector.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool TerrainTypeManager::generate_modifiers(ModifierManager& modifier_manager) const { + using enum ModifierEffect::format_t; + memory::FixedVector< + ModifierEffectCache::unit_terrain_effects_t, + terrain_type_index_t + >& unit_terrain_effects = modifier_manager.modifier_effect_cache.unit_terrain_effects; + + unit_terrain_effects = std::move( + decltype(ModifierEffectCache::unit_terrain_effects) { + generate_values, + terrain_type_index_t(get_terrain_type_count()) + } + ); + + bool ret = true; + for (TerrainType const& terrain_type : get_terrain_types()) { + const std::string_view identifier = terrain_type.get_identifier(); + ModifierEffectCache::unit_terrain_effects_t& this_unit_terrain_effects = unit_terrain_effects[terrain_type.index]; + ret &= modifier_manager.register_unit_terrain_modifier_effect( + this_unit_terrain_effects.attack, ModifierManager::get_flat_identifier("attack", identifier), + FORMAT_x100_1DP_PC_POS, "UA_ATTACK" + ); + ret &= modifier_manager.register_unit_terrain_modifier_effect( + this_unit_terrain_effects.defence, ModifierManager::get_flat_identifier("defence", identifier), + FORMAT_x100_1DP_PC_POS, "UA_DEFENCE" + ); + ret &= modifier_manager.register_unit_terrain_modifier_effect( + this_unit_terrain_effects.movement, ModifierManager::get_flat_identifier("movement", identifier), + FORMAT_x100_1DP_PC_POS, "UA_MOVEMENT" + ); + ret &= modifier_manager.register_unit_terrain_modifier_effect( + this_unit_terrain_effects.attrition, ModifierManager::get_flat_identifier("attrition", identifier), + FORMAT_x1_1DP_PC_NEG, "UA_ATTRITION" + ); + } + return ret; +} + +bool TerrainTypeManager::add_terrain_type( + std::string_view identifier, + colour_t colour, + ModifierValue&& values, + fixed_point_t movement_cost, + fixed_point_t defence_bonus, + fixed_point_t combat_width_percentage_change, + bool is_water +) { + if (identifier.empty()) { + spdlog::error_s("Invalid terrain type identifier - empty!"); + return false; + } + + if (movement_cost < 0) { + spdlog::error_s( + "Invalid movement cost for terrain type \"{}\": {} (cannot be negative!)", + identifier, movement_cost + ); + return false; + } + + return terrain_types.emplace_item( + identifier, + TerrainType::index_t { get_terrain_type_count() }, identifier, + colour, std::move(values), movement_cost, defence_bonus, combat_width_percentage_change, is_water + ); +} + +bool TerrainTypeManager::add_terrain_type_mapping( + std::string_view identifier, + TerrainType const* type, + memory::vector&& terrain_indices, + TerrainTypeMapping::index_t priority, + bool has_texture +) { + if (!terrain_types_are_locked()) { + spdlog::error_s("Cannot register terrain type mappings until terrain types are locked!"); + return false; + } + + if (identifier.empty()) { + spdlog::error_s("Invalid terrain type mapping identifier - empty!"); + return false; + } + + if (type == nullptr) { + spdlog::error_s("Null terrain type for mapping {}", identifier); + return false; + } + + bool ret = true; + + for (TerrainTypeMapping::index_t idx : terrain_indices) { + const terrain_type_mappings_map_t::const_iterator it = terrain_type_mappings_map.find(idx); + + if (it == terrain_type_mappings_map.end()) { + terrain_type_mappings_map.emplace(idx, terrain_type_mappings.size()); + } else { + spdlog::error_s( + "Terrain index {} cannot map to {} as it already maps to {}", + static_cast(idx), identifier, ovfmt::validate(terrain_type_mappings.get_item_by_index(it->second)) + ); + ret = false; + } + } + + ret &= terrain_type_mappings.emplace_item( + identifier, + identifier, *type, std::move(terrain_indices), priority, has_texture + ); + + return ret; +} + +NodeTools::node_callback_t TerrainTypeManager::_load_terrain_type_categories(ModifierManager const& modifier_manager) { + return [this, &modifier_manager](ovdl::v2script::ast::Node const* root) -> bool { + const bool ret = expect_dictionary_reserve_length(terrain_types, + [this, &modifier_manager](std::string_view type_key, ovdl::v2script::ast::Node const* type_node) -> bool { + ModifierValue values; + fixed_point_t movement_cost, defence_bonus, combat_width_percentage_change; + colour_t colour = colour_t::null(); + bool is_water = false; + + bool ret = NodeTools::expect_dictionary_keys_and_default( + modifier_manager.expect_terrain_modifier(values), + "movement_cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(movement_cost)), + "defence", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(defence_bonus)), + "combat_width", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(combat_width_percentage_change)), + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "is_water", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_water)) + )(type_node); + + ret &= add_terrain_type( + type_key, colour, std::move(values), movement_cost, defence_bonus, combat_width_percentage_change, is_water + ); + + return ret; + } + )(root); + + lock_terrain_types(); + + return ret; + }; +} + +bool TerrainTypeManager::_load_terrain_type_mapping(std::string_view mapping_key, ovdl::v2script::ast::Node const* mapping_value) { + if (terrain_texture_limit <= 0) { + spdlog::error_s("Cannot define terrain type mapping before terrain texture limit: {}", mapping_key); + return false; + } + + if (!terrain_types_are_locked()) { + spdlog::error_s("Cannot define terrain type mapping before categories: {}", mapping_key); + return false; + } + + TerrainType const* type = nullptr; + memory::vector terrain_indices; + TerrainTypeMapping::index_t priority = 0; + bool has_texture = true; + + bool ret = expect_dictionary_keys_and_default( + [mapping_key](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + spdlog::warn_s("Invalid key {} in terrain mapping {}", key, mapping_key); + return true; + }, + "type", ONE_EXACTLY, expect_terrain_type_identifier(assign_variable_callback_pointer(type)), + "color", ONE_EXACTLY, expect_list_reserve_length(terrain_indices, expect_uint( + [&terrain_indices](TerrainTypeMapping::index_t val) -> bool { + if (std::find(terrain_indices.begin(), terrain_indices.end(), val) == terrain_indices.end()) { + terrain_indices.push_back(val); + return true; + } + spdlog::error_s("Repeat terrain type mapping index: {}", val); + return false; + } + )), + "priority", ZERO_OR_ONE, expect_uint(assign_variable_callback(priority)), + "has_texture", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_texture)) + )(mapping_value); + + if (has_texture && ++terrain_texture_count == terrain_texture_limit + 1) { + spdlog::warn_s("More terrain textures than limit!"); + } + + ret &= add_terrain_type_mapping(mapping_key, type, std::move(terrain_indices), priority, has_texture); + + return true; +} + +TerrainTypeMapping const* TerrainTypeManager::get_terrain_type_mapping_for(TerrainTypeMapping::index_t idx) const { + const terrain_type_mappings_map_t::const_iterator it = terrain_type_mappings_map.find(idx); + + if (it != terrain_type_mappings_map.end()) { + return terrain_type_mappings.get_item_by_index(it->second); + } + + return nullptr; +} + +TerrainTypeMapping::index_t TerrainTypeManager::get_terrain_texture_limit() const { + return terrain_texture_limit; +} + +bool TerrainTypeManager::load_terrain_types(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root) { + const bool ret = expect_dictionary_keys_reserve_length_and_default( + terrain_type_mappings, + [this](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + return _load_terrain_type_mapping(key, value); + }, + "terrain", ONE_EXACTLY, expect_uint(assign_variable_callback(terrain_texture_limit)), + "categories", ONE_EXACTLY, _load_terrain_type_categories(modifier_manager) + )(root); + + lock_terrain_type_mappings(); + + return ret; +} diff --git a/src/openvic-simulation/map/TerrainTypeManager.hpp b/src/openvic-simulation/map/TerrainTypeManager.hpp new file mode 100644 index 000000000..5585d74f0 --- /dev/null +++ b/src/openvic-simulation/map/TerrainTypeManager.hpp @@ -0,0 +1,48 @@ +#pragma once + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/map/TerrainType.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/OrderedContainers.hpp" + +namespace OpenVic { + struct TerrainTypeManager { + private: + using terrain_type_mappings_map_t = ordered_map; + + IdentifierRegistry IDENTIFIER_REGISTRY(terrain_type); + IdentifierRegistry IDENTIFIER_REGISTRY(terrain_type_mapping); + terrain_type_mappings_map_t terrain_type_mappings_map; + + TerrainTypeMapping::index_t terrain_texture_limit = 0, terrain_texture_count = 0; + + NodeTools::node_callback_t _load_terrain_type_categories(ModifierManager const& modifier_manager); + bool _load_terrain_type_mapping(std::string_view key, ovdl::v2script::ast::Node const* value); + + public: + bool add_terrain_type( + std::string_view identifier, + colour_t colour, + ModifierValue&& values, + fixed_point_t movement_cost, + fixed_point_t defence_bonus, + fixed_point_t combat_width_percentage_change, + bool is_water + ); + + bool add_terrain_type_mapping( + std::string_view identifier, + TerrainType const* type, + memory::vector&& terrain_indices, + TerrainTypeMapping::index_t priority, + bool has_texture + ); + + TerrainTypeMapping const* get_terrain_type_mapping_for(TerrainTypeMapping::index_t idx) const; + + TerrainTypeMapping::index_t get_terrain_texture_limit() const; + + bool load_terrain_types(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); + bool generate_modifiers(ModifierManager& modifier_manager) const; + }; +} diff --git a/src/openvic-simulation/military/Deployment.cpp b/src/openvic-simulation/military/Deployment.cpp index dfeba572d..01de0eff9 100644 --- a/src/openvic-simulation/military/Deployment.cpp +++ b/src/openvic-simulation/military/Deployment.cpp @@ -1,14 +1,8 @@ #include "Deployment.hpp" -#include "openvic-simulation/dataloader/Dataloader.hpp" -#include "openvic-simulation/map/MapDefinition.hpp" -#include "openvic-simulation/military/MilitaryManager.hpp" #include "openvic-simulation/military/Leader.hpp" -#include "openvic-simulation/military/LeaderTrait.hpp" -#include "openvic-simulation/military/UnitType.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; UnitDeployment::UnitDeployment( std::string_view new_name, RegimentType const& new_type, ProvinceDefinition const* new_home @@ -17,274 +11,8 @@ UnitDeployment::UnitDeployment( UnitDeployment::UnitDeployment(std::string_view new_name, ShipType const& new_type) : name { new_name }, type { new_type } {} -template -UnitDeploymentGroup::UnitDeploymentGroup( - std::string_view new_name, ProvinceDefinition const& new_location, memory::vector<_Unit>&& new_units, - std::optional new_leader_index -) : name { new_name }, location { new_location }, units { std::move(new_units) }, leader_index { new_leader_index } {} - Deployment::Deployment( std::string_view new_path, memory::vector&& new_armies, memory::vector&& new_navies, memory::vector&& new_leaders ) : HasIdentifier { new_path }, armies { std::move(new_armies) }, navies { std::move(new_navies) }, leaders { std::move(new_leaders) } {} - -bool DeploymentManager::add_deployment( - std::string_view path, memory::vector&& armies, memory::vector&& navies, - memory::vector&& leaders -) { - if (path.empty()) { - spdlog::error_s("Attempted to load order of battle with no path! Something is very wrong!"); - return false; - } - - return deployments.emplace_item( - path, - path, std::move(armies), std::move(navies), std::move(leaders) - ); -} - -bool DeploymentManager::load_oob_file( - Dataloader const& dataloader, - MapDefinition const& map_definition, - MilitaryManager const& military_manager, - std::string_view history_path, - Deployment const*& deployment, - bool fail_on_missing -) { - deployment = get_deployment_by_identifier(history_path); - if (deployment != nullptr) { - spdlog::warn_s("Loading an already-loaded OOB file with path {}", history_path); - return true; - } - - if (missing_oob_files.contains(history_path)) { - return !fail_on_missing; - } - - static constexpr std::string_view oob_directory = "history/units/"; - - const fs::path lookedup_path = - dataloader.lookup_file(append_string_views(oob_directory, history_path), false); - - if (lookedup_path.empty()) { - missing_oob_files.emplace(history_path); - if (fail_on_missing) { - spdlog::warn_s("Could not find OOB file {}!", history_path); - return false; - } else { - return true; - } - } - - // Ensures that if ever multithreaded, only one vector is used per thread - // Else acts like static - thread_local memory::vector armies; - // Default max vanilla armies is 15, 23 is the max I've seen in mods - armies.reserve(8); - - thread_local memory::vector navies; - // Default max vanilla navies is 14, 13 is the max I've seen in mods - navies.reserve(8); - - thread_local memory::vector leaders; - // Default max vanilla leaders is 17, 23 is the max I've seen in mods - leaders.reserve(8); - - size_t general_count = 0, admiral_count = 0; - - using enum unit_branch_t; - - const auto leader_callback = [&map_definition, &military_manager, &general_count, &admiral_count](ast::NodeCPtr node) -> bool { - std::string_view leader_name {}; - unit_branch_t leader_branch = INVALID_BRANCH; - Date leader_date {}; - LeaderTrait const* leader_personality = nullptr; - LeaderTrait const* leader_background = nullptr; - fixed_point_t leader_prestige = 0; - std::string_view picture {}; - - constexpr bool allow_empty = false; - constexpr bool do_warn = true; - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(leader_name)), - "date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(leader_date)), - "type", ONE_EXACTLY, UnitTypeManager::expect_branch_identifier(assign_variable_callback(leader_branch)), - "personality", ONE_EXACTLY, military_manager.get_leader_trait_manager() - .expect_leader_trait_identifier_or_string(assign_variable_callback_pointer(leader_personality), allow_empty, do_warn), - "background", ONE_EXACTLY, military_manager.get_leader_trait_manager() - .expect_leader_trait_identifier_or_string(assign_variable_callback_pointer(leader_background), allow_empty, do_warn), - "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(leader_prestige)), - "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(picture)) - )(node); - - if (leader_name.empty()) { - spdlog::error_s("Leader has a missing or empty name!"); - ret = false; - } - - // Default cases for leader personality and background match vic2 behaviour of ignoring invalid traits. - if (leader_personality != nullptr && !leader_personality->is_personality_trait()) { - spdlog::warn_s( - "Leader {} has personality \"{}\" which is not a personality trait!", - leader_name, *leader_personality - ); - leader_personality = nullptr; - } - if (leader_background != nullptr && !leader_background->is_background_trait()) { - spdlog::warn_s( - "Leader {} has background \"{}\" which is not a background trait!", - leader_name, *leader_background - ); - leader_background = nullptr; - } - - switch (leader_branch) { - case LAND: - ++general_count; - break; - case NAVAL: - ++admiral_count; - break; - default: - spdlog::error_s("Invalid branch {} for leader {}", static_cast(leader_branch), leader_name); - return false; - } - - leaders.emplace_back( - leader_name, leader_branch, leader_date, leader_personality, leader_background, leader_prestige, picture - ); - - return ret; - }; - - bool ret = expect_dictionary_keys_and_default( - key_value_success_callback, // TODO: load SOI information - "leader", ZERO_OR_MORE, leader_callback, - "army", ZERO_OR_MORE, [&general_count, &map_definition, &military_manager, &leader_callback](ast::NodeCPtr node) -> bool { - std::string_view army_name {}; - ProvinceDefinition const* army_location = nullptr; - memory::vector army_regiments {}; - - const size_t starting_general_count = general_count; - - if(!expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(army_name)), - "location", ONE_EXACTLY, map_definition.expect_province_definition_identifier( - assign_variable_callback_pointer(army_location) - ), - "regiment", ONE_OR_MORE, [&map_definition, &military_manager, &army_regiments](ast::NodeCPtr node) -> bool { - std::string_view regiment_name {}; - RegimentType const* regiment_type = nullptr; - ProvinceDefinition const* regiment_home = nullptr; - - const bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(regiment_name)), - "type", ONE_EXACTLY, military_manager.get_unit_type_manager() - .expect_regiment_type_identifier(assign_variable_callback_pointer(regiment_type)), - "home", ZERO_OR_ONE, map_definition - .expect_province_definition_identifier(assign_variable_callback_pointer(regiment_home)) - )(node); - - if (regiment_home == nullptr) { - spdlog::warn_s("RegimentDeployment {} has no home province!", regiment_name); - } - - if (regiment_type == nullptr) { - spdlog::error_s("RegimentDeployment {} has no type!", regiment_name); - return false; - } - - army_regiments.emplace_back(regiment_name, *regiment_type, regiment_home); - - return ret; - }, - "leader", ZERO_OR_ONE, leader_callback - )(node)) { - return false; - } - - armies.emplace_back( - army_name, - *army_location, - std::move(army_regiments), - starting_general_count < general_count - ? std::optional { general_count - 1 } - : std::nullopt - ); - - return true; - }, - "navy", ZERO_OR_MORE, [&admiral_count, &map_definition, &military_manager, &leader_callback](ast::NodeCPtr node) -> bool { - std::string_view navy_name {}; - ProvinceDefinition const* navy_location = nullptr; - memory::vector navy_ships {}; - - const size_t starting_admiral_count = admiral_count; - - if(!expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(navy_name)), - "location", ONE_EXACTLY, map_definition.expect_province_definition_identifier( - assign_variable_callback_pointer(navy_location) - ), - "ship", ONE_OR_MORE, [&military_manager, &navy_ships](ast::NodeCPtr node) -> bool { - std::string_view ship_name {}; - ShipType const* ship_type = nullptr; - - const bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(ship_name)), - "type", ONE_EXACTLY, military_manager.get_unit_type_manager() - .expect_ship_type_identifier(assign_variable_callback_pointer(ship_type)) - )(node); - - if (ship_type == nullptr) { - spdlog::error_s("ShipDeployment {} has no type!", ship_name); - return false; - } - - navy_ships.emplace_back(ship_name, *ship_type); - - return ret; - }, - "leader", ZERO_OR_ONE, leader_callback - )(node)) { - return false; - } - - navies.emplace_back( - navy_name, - *navy_location, - std::move(navy_ships), - starting_admiral_count < admiral_count - ? std::optional { admiral_count - 1 } - : std::nullopt - ); - - return true; - } - )(Dataloader::parse_defines(lookedup_path).get_file_node()); - - if (general_count + admiral_count != leaders.size()) { - spdlog::error_s( - "Mismatch in sum (#{}) of general (#{}) and admiral (#{}) counts when compared to loaded leader count (#{}) for OOB file {}", - general_count + admiral_count, - general_count, - admiral_count, - leaders.size(), - history_path - ); - return false; - } - - if (add_deployment(history_path, std::move(armies), std::move(navies), std::move(leaders))) { - deployment = &get_back_deployment(); - } else { - ret = false; - } - - return ret; -} - -size_t DeploymentManager::get_missing_oob_file_count() const { - return missing_oob_files.size(); -} diff --git a/src/openvic-simulation/military/Deployment.hpp b/src/openvic-simulation/military/Deployment.hpp index c4c2574b7..71ad9fbc3 100644 --- a/src/openvic-simulation/military/Deployment.hpp +++ b/src/openvic-simulation/military/Deployment.hpp @@ -8,14 +8,10 @@ #include "openvic-simulation/military/Leader.hpp" #include "openvic-simulation/military/UnitBranchedGetterMacro.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/UnitBranchType.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { - class Dataloader; - struct MapDefinition; - struct MilitaryManager; struct ProvinceDefinition; template @@ -67,10 +63,15 @@ namespace OpenVic { std::optional PROPERTY(leader_index); public: - UnitDeploymentGroup( - std::string_view new_name, ProvinceDefinition const& new_location, memory::vector<_Unit>&& new_units, + constexpr UnitDeploymentGroup( + std::string_view new_name, + ProvinceDefinition const& new_location, + memory::vector<_Unit>&& new_units, std::optional new_leader_index - ); + ) : name { new_name }, + location { new_location }, + units { std::move(new_units) }, + leader_index { new_leader_index } {} UnitDeploymentGroup(UnitDeploymentGroup&&) = default; }; @@ -94,29 +95,4 @@ namespace OpenVic { OV_UNIT_BRANCHED_GETTER_CONST(get_unit_deployment_groups, armies, navies); }; - - struct DeploymentManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(deployment); - string_set_t missing_oob_files; - - public: - bool add_deployment( - std::string_view path, - memory::vector&& armies, - memory::vector&& navies, - memory::vector&& leaders - ); - - bool load_oob_file( - Dataloader const& dataloader, - MapDefinition const& map_definition, - MilitaryManager const& military_manager, - std::string_view history_path, - Deployment const*& deployment, - bool fail_on_missing - ); - - size_t get_missing_oob_file_count() const; - }; } diff --git a/src/openvic-simulation/military/DeploymentManager.cpp b/src/openvic-simulation/military/DeploymentManager.cpp new file mode 100644 index 000000000..a3d92aaa6 --- /dev/null +++ b/src/openvic-simulation/military/DeploymentManager.cpp @@ -0,0 +1,271 @@ +#include "DeploymentManager.hpp" + +#include "openvic-simulation/dataloader/Dataloader.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/map/MapDefinition.hpp" +#include "openvic-simulation/military/MilitaryManager.hpp" +#include "openvic-simulation/military/Leader.hpp" +#include "openvic-simulation/military/LeaderTrait.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool DeploymentManager::add_deployment( + std::string_view path, memory::vector&& armies, memory::vector&& navies, + memory::vector&& leaders +) { + if (path.empty()) { + spdlog::error_s("Attempted to load order of battle with no path! Something is very wrong!"); + return false; + } + + return deployments.emplace_item( + path, + path, std::move(armies), std::move(navies), std::move(leaders) + ); +} + +bool DeploymentManager::load_oob_file( + Dataloader const& dataloader, + MapDefinition const& map_definition, + MilitaryManager const& military_manager, + std::string_view history_path, + Deployment const*& deployment, + bool fail_on_missing +) { + deployment = get_deployment_by_identifier(history_path); + if (deployment != nullptr) { + spdlog::warn_s("Loading an already-loaded OOB file with path {}", history_path); + return true; + } + + if (missing_oob_files.contains(history_path)) { + return !fail_on_missing; + } + + static constexpr std::string_view oob_directory = "history/units/"; + + const fs::path lookedup_path = + dataloader.lookup_file(append_string_views(oob_directory, history_path), false); + + if (lookedup_path.empty()) { + missing_oob_files.emplace(history_path); + if (fail_on_missing) { + spdlog::warn_s("Could not find OOB file {}!", history_path); + return false; + } else { + return true; + } + } + + // Ensures that if ever multithreaded, only one vector is used per thread + // Else acts like static + thread_local memory::vector armies; + // Default max vanilla armies is 15, 23 is the max I've seen in mods + armies.reserve(8); + + thread_local memory::vector navies; + // Default max vanilla navies is 14, 13 is the max I've seen in mods + navies.reserve(8); + + thread_local memory::vector leaders; + // Default max vanilla leaders is 17, 23 is the max I've seen in mods + leaders.reserve(8); + + size_t general_count = 0, admiral_count = 0; + + using enum unit_branch_t; + + const auto leader_callback = [&map_definition, &military_manager, &general_count, &admiral_count](ovdl::v2script::ast::Node const* node) -> bool { + std::string_view leader_name {}; + unit_branch_t leader_branch = INVALID_BRANCH; + Date leader_date {}; + LeaderTrait const* leader_personality = nullptr; + LeaderTrait const* leader_background = nullptr; + fixed_point_t leader_prestige = 0; + std::string_view picture {}; + + constexpr bool allow_empty = false; + constexpr bool do_warn = true; + bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(leader_name)), + "date", ONE_EXACTLY, expect_date_identifier_or_string(assign_variable_callback(leader_date)), + "type", ONE_EXACTLY, UnitTypeManager::expect_branch_identifier(assign_variable_callback(leader_branch)), + "personality", ONE_EXACTLY, military_manager.get_leader_trait_manager() + .expect_leader_trait_identifier_or_string(assign_variable_callback_pointer(leader_personality), allow_empty, do_warn), + "background", ONE_EXACTLY, military_manager.get_leader_trait_manager() + .expect_leader_trait_identifier_or_string(assign_variable_callback_pointer(leader_background), allow_empty, do_warn), + "prestige", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(leader_prestige)), + "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(picture)) + )(node); + + if (leader_name.empty()) { + spdlog::error_s("Leader has a missing or empty name!"); + ret = false; + } + + // Default cases for leader personality and background match vic2 behaviour of ignoring invalid traits. + if (leader_personality != nullptr && !leader_personality->is_personality_trait()) { + spdlog::warn_s( + "Leader {} has personality \"{}\" which is not a personality trait!", + leader_name, *leader_personality + ); + leader_personality = nullptr; + } + if (leader_background != nullptr && !leader_background->is_background_trait()) { + spdlog::warn_s( + "Leader {} has background \"{}\" which is not a background trait!", + leader_name, *leader_background + ); + leader_background = nullptr; + } + + switch (leader_branch) { + case LAND: + ++general_count; + break; + case NAVAL: + ++admiral_count; + break; + default: + spdlog::error_s("Invalid branch {} for leader {}", static_cast(leader_branch), leader_name); + return false; + } + + leaders.emplace_back( + leader_name, leader_branch, leader_date, leader_personality, leader_background, leader_prestige, picture + ); + + return ret; + }; + + bool ret = expect_dictionary_keys_and_default( + key_value_success_callback, // TODO: load SOI information + "leader", ZERO_OR_MORE, leader_callback, + "army", ZERO_OR_MORE, [&general_count, &map_definition, &military_manager, &leader_callback](ovdl::v2script::ast::Node const* node) -> bool { + std::string_view army_name {}; + ProvinceDefinition const* army_location = nullptr; + memory::vector army_regiments {}; + + const size_t starting_general_count = general_count; + + if(!expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(army_name)), + "location", ONE_EXACTLY, map_definition.expect_province_definition_identifier( + assign_variable_callback_pointer(army_location) + ), + "regiment", ONE_OR_MORE, [&map_definition, &military_manager, &army_regiments](ovdl::v2script::ast::Node const* node) -> bool { + std::string_view regiment_name {}; + RegimentType const* regiment_type = nullptr; + ProvinceDefinition const* regiment_home = nullptr; + + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(regiment_name)), + "type", ONE_EXACTLY, military_manager.get_unit_type_manager() + .expect_regiment_type_identifier(assign_variable_callback_pointer(regiment_type)), + "home", ZERO_OR_ONE, map_definition + .expect_province_definition_identifier(assign_variable_callback_pointer(regiment_home)) + )(node); + + if (regiment_home == nullptr) { + spdlog::warn_s("RegimentDeployment {} has no home province!", regiment_name); + } + + if (regiment_type == nullptr) { + spdlog::error_s("RegimentDeployment {} has no type!", regiment_name); + return false; + } + + army_regiments.emplace_back(regiment_name, *regiment_type, regiment_home); + + return ret; + }, + "leader", ZERO_OR_ONE, leader_callback + )(node)) { + return false; + } + + armies.emplace_back( + army_name, + *army_location, + std::move(army_regiments), + starting_general_count < general_count + ? std::optional { general_count - 1 } + : std::nullopt + ); + + return true; + }, + "navy", ZERO_OR_MORE, [&admiral_count, &map_definition, &military_manager, &leader_callback](ovdl::v2script::ast::Node const* node) -> bool { + std::string_view navy_name {}; + ProvinceDefinition const* navy_location = nullptr; + memory::vector navy_ships {}; + + const size_t starting_admiral_count = admiral_count; + + if(!expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(navy_name)), + "location", ONE_EXACTLY, map_definition.expect_province_definition_identifier( + assign_variable_callback_pointer(navy_location) + ), + "ship", ONE_OR_MORE, [&military_manager, &navy_ships](ovdl::v2script::ast::Node const* node) -> bool { + std::string_view ship_name {}; + ShipType const* ship_type = nullptr; + + const bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(ship_name)), + "type", ONE_EXACTLY, military_manager.get_unit_type_manager() + .expect_ship_type_identifier(assign_variable_callback_pointer(ship_type)) + )(node); + + if (ship_type == nullptr) { + spdlog::error_s("ShipDeployment {} has no type!", ship_name); + return false; + } + + navy_ships.emplace_back(ship_name, *ship_type); + + return ret; + }, + "leader", ZERO_OR_ONE, leader_callback + )(node)) { + return false; + } + + navies.emplace_back( + navy_name, + *navy_location, + std::move(navy_ships), + starting_admiral_count < admiral_count + ? std::optional { admiral_count - 1 } + : std::nullopt + ); + + return true; + } + )(Dataloader::parse_defines(lookedup_path).get_file_node()); + + if (general_count + admiral_count != leaders.size()) { + spdlog::error_s( + "Mismatch in sum (#{}) of general (#{}) and admiral (#{}) counts when compared to loaded leader count (#{}) for OOB file {}", + general_count + admiral_count, + general_count, + admiral_count, + leaders.size(), + history_path + ); + return false; + } + + if (add_deployment(history_path, std::move(armies), std::move(navies), std::move(leaders))) { + deployment = &get_back_deployment(); + } else { + ret = false; + } + + return ret; +} + +size_t DeploymentManager::get_missing_oob_file_count() const { + return missing_oob_files.size(); +} diff --git a/src/openvic-simulation/military/DeploymentManager.hpp b/src/openvic-simulation/military/DeploymentManager.hpp new file mode 100644 index 000000000..3e4e9dfc0 --- /dev/null +++ b/src/openvic-simulation/military/DeploymentManager.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/military/Deployment.hpp" +#include "openvic-simulation/military/Leader.hpp" +#include "openvic-simulation/military/UnitBranchedGetterMacro.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + class Dataloader; + struct MapDefinition; + struct MilitaryManager; + + struct DeploymentManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(deployment); + string_set_t missing_oob_files; + + public: + bool add_deployment( + std::string_view path, + memory::vector&& armies, + memory::vector&& navies, + memory::vector&& leaders + ); + + bool load_oob_file( + Dataloader const& dataloader, + MapDefinition const& map_definition, + MilitaryManager const& military_manager, + std::string_view history_path, + Deployment const*& deployment, + bool fail_on_missing + ); + + size_t get_missing_oob_file_count() const; + }; +} diff --git a/src/openvic-simulation/military/LeaderTrait.cpp b/src/openvic-simulation/military/LeaderTrait.cpp index 972fd6131..e3dbf637d 100644 --- a/src/openvic-simulation/military/LeaderTrait.cpp +++ b/src/openvic-simulation/military/LeaderTrait.cpp @@ -1,12 +1,6 @@ #include "LeaderTrait.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/defines/MilitaryDefines.hpp" -#include "openvic-simulation/modifier/ModifierEffectCache.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; LeaderTrait::LeaderTrait(std::string_view new_identifier, trait_type_t new_type, ModifierValue&& new_modifiers) : Modifier { new_identifier, std::move(new_modifiers), modifier_type_t::LEADER }, trait_type { new_type } {} @@ -18,109 +12,3 @@ bool LeaderTrait::is_personality_trait() const { bool LeaderTrait::is_background_trait() const { return trait_type == trait_type_t::BACKGROUND; } - -LeaderTraitManager::LeaderTraitManager() - : leader_prestige_modifier { "leader_prestige", {}, Modifier::modifier_type_t::LEADER } {} - -bool LeaderTraitManager::setup_leader_prestige_modifier( - ModifierEffectCache const& modifier_effect_cache, MilitaryDefines const& military_defines -) { - if (!leader_prestige_modifier.empty()) { - spdlog::error_s("Leader prestige modifier already set up!"); - return false; - } - - bool ret = true; - - if (military_defines.get_leader_prestige_to_morale_factor() != 0) { - if (modifier_effect_cache.get_morale_leader() != nullptr) { - leader_prestige_modifier.set_effect( - *modifier_effect_cache.get_morale_leader(), military_defines.get_leader_prestige_to_morale_factor() - ); - } else { - spdlog::error_s("Cannot set leader prestige modifier morale effect - ModifierEffect is null!"); - ret = false; - } - } - - if (military_defines.get_leader_prestige_to_max_org_factor() != 0) { - if (modifier_effect_cache.get_organisation() != nullptr) { - leader_prestige_modifier.set_effect( - *modifier_effect_cache.get_organisation(), military_defines.get_leader_prestige_to_max_org_factor() - ); - } else { - spdlog::error_s("Cannot set leader prestige modifier organisation effect - ModifierEffect is null!"); - ret = false; - } - } - - return ret; -} - -bool LeaderTraitManager::add_leader_trait( - std::string_view identifier, LeaderTrait::trait_type_t type, ModifierValue&& modifiers -) { - if (identifier.empty()) { - spdlog::error_s("Invalid leader trait identifier - empty!"); - return false; - } - - if (!leader_traits.emplace_item(identifier, identifier, type, std::move(modifiers))) { - return false; - } - - using enum LeaderTrait::trait_type_t; - - switch (type) { - case PERSONALITY: personality_traits.push_back(&leader_traits.back()); break; - case BACKGROUND: background_traits.push_back(&leader_traits.back()); break; - } - - return true; -} - -bool LeaderTraitManager::load_leader_traits_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) { - // Reserve space for the leader traits so we can safely store pointers in personality_traits and - // background_traits without the risk of them being invalidated by reallocations of leader_traits. - bool ret = expect_dictionary_keys( - "personality", ONE_EXACTLY, expect_length( - [this](size_t length) -> bool { - reserve_more(leader_traits, length); - reserve_more(personality_traits, length); - return true; - } - ), - "background", ONE_EXACTLY, expect_length( - [this](size_t length) -> bool { - reserve_more(leader_traits, length); - reserve_more(background_traits, length); - return true; - } - ) - )(root); - - const auto trait_callback = [this, &modifier_manager](LeaderTrait::trait_type_t type) -> NodeCallback auto { - return expect_dictionary( - [this, &modifier_manager, type](std::string_view trait_identifier, ast::NodeCPtr value) -> bool { - ModifierValue modifiers; - - bool ret = NodeTools::expect_dictionary(modifier_manager.expect_leader_modifier(modifiers))(value); - - ret &= add_leader_trait(trait_identifier, type, std::move(modifiers)); - - return ret; - } - ); - }; - - using enum LeaderTrait::trait_type_t; - - ret &= - expect_dictionary_keys("personality", ONE_EXACTLY, trait_callback(PERSONALITY), "background", ONE_EXACTLY, trait_callback(BACKGROUND))( - root - ); - - lock_leader_traits(); - - return ret; -} diff --git a/src/openvic-simulation/military/LeaderTrait.hpp b/src/openvic-simulation/military/LeaderTrait.hpp index f43d90e25..e6b3fbc88 100644 --- a/src/openvic-simulation/military/LeaderTrait.hpp +++ b/src/openvic-simulation/military/LeaderTrait.hpp @@ -2,10 +2,7 @@ #include -#include "openvic-simulation/core/memory/Vector.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/modifier/Modifier.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { struct LeaderTrait : Modifier { @@ -34,28 +31,4 @@ namespace OpenVic { bool is_personality_trait() const; bool is_background_trait() const; }; - - struct MilitaryDefines; - struct ModifierEffectCache; - - struct LeaderTraitManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(leader_trait); - memory::vector SPAN_PROPERTY(personality_traits); - memory::vector SPAN_PROPERTY(background_traits); - - // As well as their background and personality traits, leaders get this modifier scaled by their prestige - Modifier PROPERTY(leader_prestige_modifier); - - public: - LeaderTraitManager(); - - bool setup_leader_prestige_modifier( - ModifierEffectCache const& modifier_effect_cache, MilitaryDefines const& military_defines - ); - - bool add_leader_trait(std::string_view identifier, LeaderTrait::trait_type_t type, ModifierValue&& modifiers); - - bool load_leader_traits_file(ModifierManager const& modifier_manager, ast::NodeCPtr root); - }; -} // namespace OpenVic +} diff --git a/src/openvic-simulation/military/LeaderTraitManager.cpp b/src/openvic-simulation/military/LeaderTraitManager.cpp new file mode 100644 index 000000000..6648654b3 --- /dev/null +++ b/src/openvic-simulation/military/LeaderTraitManager.cpp @@ -0,0 +1,115 @@ +#include "LeaderTraitManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/defines/MilitaryDefines.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +LeaderTraitManager::LeaderTraitManager() + : leader_prestige_modifier { "leader_prestige", {}, Modifier::modifier_type_t::LEADER } {} + +bool LeaderTraitManager::setup_leader_prestige_modifier( + ModifierEffectCache const& modifier_effect_cache, MilitaryDefines const& military_defines +) { + if (!leader_prestige_modifier.empty()) { + spdlog::error_s("Leader prestige modifier already set up!"); + return false; + } + + bool ret = true; + + if (military_defines.get_leader_prestige_to_morale_factor() != 0) { + if (modifier_effect_cache.get_morale_leader() != nullptr) { + leader_prestige_modifier.set_effect( + *modifier_effect_cache.get_morale_leader(), military_defines.get_leader_prestige_to_morale_factor() + ); + } else { + spdlog::error_s("Cannot set leader prestige modifier morale effect - ModifierEffect is null!"); + ret = false; + } + } + + if (military_defines.get_leader_prestige_to_max_org_factor() != 0) { + if (modifier_effect_cache.get_organisation() != nullptr) { + leader_prestige_modifier.set_effect( + *modifier_effect_cache.get_organisation(), military_defines.get_leader_prestige_to_max_org_factor() + ); + } else { + spdlog::error_s("Cannot set leader prestige modifier organisation effect - ModifierEffect is null!"); + ret = false; + } + } + + return ret; +} + +bool LeaderTraitManager::add_leader_trait( + std::string_view identifier, LeaderTrait::trait_type_t type, ModifierValue&& modifiers +) { + if (identifier.empty()) { + spdlog::error_s("Invalid leader trait identifier - empty!"); + return false; + } + + if (!leader_traits.emplace_item(identifier, identifier, type, std::move(modifiers))) { + return false; + } + + using enum LeaderTrait::trait_type_t; + + switch (type) { + case PERSONALITY: personality_traits.push_back(&leader_traits.back()); break; + case BACKGROUND: background_traits.push_back(&leader_traits.back()); break; + } + + return true; +} + +bool LeaderTraitManager::load_leader_traits_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root) { + // Reserve space for the leader traits so we can safely store pointers in personality_traits and + // background_traits without the risk of them being invalidated by reallocations of leader_traits. + bool ret = expect_dictionary_keys( + "personality", ONE_EXACTLY, expect_length( + [this](size_t length) -> bool { + reserve_more(leader_traits, length); + reserve_more(personality_traits, length); + return true; + } + ), + "background", ONE_EXACTLY, expect_length( + [this](size_t length) -> bool { + reserve_more(leader_traits, length); + reserve_more(background_traits, length); + return true; + } + ) + )(root); + + const auto trait_callback = [this, &modifier_manager](LeaderTrait::trait_type_t type) -> NodeCallback auto { + return expect_dictionary( + [this, &modifier_manager, type](std::string_view trait_identifier, ovdl::v2script::ast::Node const* value) -> bool { + ModifierValue modifiers; + + bool ret = NodeTools::expect_dictionary(modifier_manager.expect_leader_modifier(modifiers))(value); + + ret &= add_leader_trait(trait_identifier, type, std::move(modifiers)); + + return ret; + } + ); + }; + + using enum LeaderTrait::trait_type_t; + + ret &= + expect_dictionary_keys("personality", ONE_EXACTLY, trait_callback(PERSONALITY), "background", ONE_EXACTLY, trait_callback(BACKGROUND))( + root + ); + + lock_leader_traits(); + + return ret; +} diff --git a/src/openvic-simulation/military/LeaderTraitManager.hpp b/src/openvic-simulation/military/LeaderTraitManager.hpp new file mode 100644 index 000000000..10804bee9 --- /dev/null +++ b/src/openvic-simulation/military/LeaderTraitManager.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/military/LeaderTrait.hpp" +#include "openvic-simulation/modifier/Modifier.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct MilitaryDefines; + struct ModifierEffectCache; + + struct LeaderTraitManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(leader_trait); + memory::vector SPAN_PROPERTY(personality_traits); + memory::vector SPAN_PROPERTY(background_traits); + + // As well as their background and personality traits, leaders get this modifier scaled by their prestige + Modifier PROPERTY(leader_prestige_modifier); + + public: + LeaderTraitManager(); + + bool setup_leader_prestige_modifier( + ModifierEffectCache const& modifier_effect_cache, MilitaryDefines const& military_defines + ); + + bool add_leader_trait(std::string_view identifier, LeaderTrait::trait_type_t type, ModifierValue&& modifiers); + + bool load_leader_traits_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); + }; +} diff --git a/src/openvic-simulation/military/MilitaryManager.hpp b/src/openvic-simulation/military/MilitaryManager.hpp index 343d78903..347b5ea64 100644 --- a/src/openvic-simulation/military/MilitaryManager.hpp +++ b/src/openvic-simulation/military/MilitaryManager.hpp @@ -1,9 +1,9 @@ #pragma once -#include "openvic-simulation/military/Deployment.hpp" -#include "openvic-simulation/military/LeaderTrait.hpp" -#include "openvic-simulation/military/UnitType.hpp" -#include "openvic-simulation/military/Wargoal.hpp" +#include "openvic-simulation/military/DeploymentManager.hpp" +#include "openvic-simulation/military/LeaderTraitManager.hpp" +#include "openvic-simulation/military/UnitTypeManager.hpp" +#include "openvic-simulation/military/WargoalManager.hpp" namespace OpenVic { struct MilitaryManager { diff --git a/src/openvic-simulation/military/UnitInstanceGroup.cpp b/src/openvic-simulation/military/UnitInstanceGroup.cpp index 08e336d85..bfb9604ce 100644 --- a/src/openvic-simulation/military/UnitInstanceGroup.cpp +++ b/src/openvic-simulation/military/UnitInstanceGroup.cpp @@ -3,12 +3,8 @@ #include #include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/defines/MilitaryDefines.hpp" -#include "openvic-simulation/map/MapInstance.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" -#include "openvic-simulation/military/Deployment.hpp" -#include "openvic-simulation/military/LeaderTrait.hpp" -#include "openvic-simulation/population/Culture.hpp" +#include "openvic-simulation/military/Leader.hpp" #include "openvic-simulation/population/PopType.hpp" #include "openvic-simulation/types/OrderedContainersMath.hpp" @@ -274,308 +270,3 @@ fixed_point_t UnitInstanceGroupBranched::get_total_consumed_supply() cons return total_consumed_supply; } - -Pop* UnitInstanceManager::recruit_pop_in(ProvinceInstance& province, const bool is_rebel) const { - if (is_rebel) { - for (auto& pop : province.get_mutable_pops()) { - if (pop.get_rebel_type() != nullptr && pop.try_recruit()) { - return &pop; - } - } - } else { - for (auto& pop : province.get_mutable_pops()) { - if (pop.get_type().can_be_recruited && pop.try_recruit()) { - /* - Victoria 2 does not respect cultural restrictions when applying history. - */ - return &pop; - } - } - } - - //fallback to understrength pops - if (is_rebel) { - for (auto& pop : province.get_mutable_pops()) { - if (pop.get_rebel_type() != nullptr && pop.try_recruit_understrength()) { - return &pop; - } - } - } else { - for (auto& pop : province.get_mutable_pops()) { - if (pop.get_type().can_be_recruited && pop.try_recruit_understrength()) { - /* - Victoria 2 does not respect cultural restrictions when applying history. - */ - return &pop; - } - } - } - - return nullptr; -} - -template -UnitInstanceBranched& UnitInstanceManager::generate_unit_instance( - UnitDeployment const& unit_deployment, - MapInstance& map_instance, - const bool is_rebel -) { - UnitInstanceBranched& unit_instance = *get_unit_instances().insert( - [this, &unit_deployment, &map_instance, is_rebel]() -> UnitInstanceBranched { - if constexpr (Branch == LAND) { - RegimentDeployment const& regiment_deployment = unit_deployment; - ProvinceInstance& province = map_instance.get_province_instance_by_definition(*regiment_deployment.get_home()); - Pop* const pop_ptr = recruit_pop_in(province, is_rebel); - if (pop_ptr == nullptr) { - spdlog::warn_s( - "Regiment {} in province {} lacks backing pop.", regiment_deployment.get_name(), province.get_identifier() - ); - } - - return { - unique_id_counter++, - unit_deployment.get_name(), - unit_deployment.type, - pop_ptr, - false - }; - } else if constexpr (Branch == NAVAL) { - return { - unique_id_counter++, - unit_deployment.get_name(), - unit_deployment.type - }; - } - }() - ); - - unit_instance_map.emplace(unit_instance.unique_id, unit_instance); - - return unit_instance; -} - -template -bool UnitInstanceManager::generate_unit_instance_group( - MapInstance& map_instance, CountryInstance& country, UnitDeploymentGroup const& unit_deployment_group -) { - if (unit_deployment_group.get_units().empty()) { - spdlog::error_s( - "Trying to generate unit group \"{}\" with no units for country \"{}\"", - unit_deployment_group.get_name(), country - ); - return false; - } - - ProvinceInstance& location = map_instance.get_province_instance_by_definition(unit_deployment_group.get_location()); - UnitInstanceGroupBranched& unit_instance_group = *get_unit_instance_groups().emplace( - unique_id_counter++, - unit_deployment_group.get_name(), - country, - location - ); - unit_instance_group_map.emplace(unit_instance_group.unique_id, unit_instance_group); - - bool ret = true; - - for (UnitDeployment const& unit_deployment : unit_deployment_group.get_units()) { - ret &= unit_instance_group.add_unit( - generate_unit_instance(unit_deployment, map_instance, country.is_rebel_country()) - ); - } - - ret &= unit_instance_group.set_country(country); - - if (unit_deployment_group.get_leader_index().has_value()) { - memory::vector>& leaders = country.get_leaders(); - auto it = leaders.begin(); - - advance(it, *unit_deployment_group.get_leader_index()); - - if (it < leaders.end()) { - ret &= unit_instance_group.set_leader(&it->get()); - } else { - spdlog::error_s( - "Invalid leader index {} for unit group \"{}\" for country \"{}\"", - *unit_deployment_group.get_leader_index(), unit_deployment_group.get_name(), country - ); - ret = false; - } - } - - return ret; -} - -template -void UnitInstanceManager::generate_leader(CountryInstance& country, T&& leader_base) { - LeaderInstance& leader_instance = *leaders.emplace( - unique_id_counter++, - std::forward(leader_base), - country - ); - leader_instance_map.emplace(leader_instance.unique_id, leader_instance); - country.add_leader(leader_instance); - - if (leader_instance.get_picture().empty() && country.get_primary_culture() != nullptr) { - leader_instance.set_picture(culture_manager.get_leader_picture_name( - country.get_primary_culture()->group.get_leader(), leader_instance.branch - )); - } -} - -UnitInstanceManager::UnitInstanceManager( - CultureManager const& new_culture_manager, - LeaderTraitManager const& new_leader_trait_manager, - MilitaryDefines const& new_military_defines -) : culture_manager { new_culture_manager }, - leader_trait_manager { new_leader_trait_manager }, - military_defines { new_military_defines } {} - -bool UnitInstanceManager::generate_deployment( - MapInstance& map_instance, CountryInstance& country, Deployment const& deployment -) { - bool ret = true; - - for (LeaderBase const& leader : deployment.get_leaders()) { - generate_leader(country, leader); - } - - const auto generate_group = [this, &map_instance, &country, &ret, &deployment]() -> void { - for (UnitDeploymentGroup const& unit_deployment_group : deployment.get_unit_deployment_groups()) { - ret &= generate_unit_instance_group(map_instance, country, unit_deployment_group); - } - }; - - generate_group.template operator()(); - generate_group.template operator()(); - - return ret; -} - -void UnitInstanceManager::update_gamestate() { - for (ArmyInstance& army : armies) { - army.update_gamestate(); - } - for (NavyInstance& navy : navies) { - navy.update_gamestate(); - } -} - -void UnitInstanceManager::tick() { - for (ArmyInstance& army : armies) { - army.tick(); - } - for (NavyInstance& navy : navies) { - navy.tick(); - } -} - -LeaderInstance* UnitInstanceManager::get_leader_instance_by_unique_id(unique_id_t unique_id) { - const decltype(leader_instance_map)::const_iterator it = leader_instance_map.find(unique_id); - - if (it != leader_instance_map.end()) { - return &it->second.get(); - } else { - return nullptr; - } -} - -UnitInstance* UnitInstanceManager::get_unit_instance_by_unique_id(unique_id_t unique_id) { - const decltype(unit_instance_map)::const_iterator it = unit_instance_map.find(unique_id); - - if (it != unit_instance_map.end()) { - return &it->second.get(); - } else { - return nullptr; - } -} - -UnitInstanceGroup* UnitInstanceManager::get_unit_instance_group_by_unique_id(unique_id_t unique_id) { - const decltype(unit_instance_group_map)::const_iterator it = unit_instance_group_map.find(unique_id); - - if (it != unit_instance_group_map.end()) { - return &it->second.get(); - } else { - return nullptr; - } -} - -bool UnitInstanceManager::create_leader( - CountryInstance& country, - unit_branch_t branch, - Date creation_date, - std::string_view name, - LeaderTrait const* personality, - LeaderTrait const* background -) { - const fixed_point_t leader_creation_cost = military_defines.get_leader_recruit_cost(); - if (country.get_leadership_point_stockpile() < leader_creation_cost) { - spdlog::error_s( - "Country \"{}\" does not have enough leadership points ({:.2}) to create a {} (cost: {:.2})", - country, - country.get_leadership_point_stockpile(), - get_branched_leader_name(branch), - leader_creation_cost - ); - return false; - } - - country.set_leadership_point_stockpile(country.get_leadership_point_stockpile() - leader_creation_cost); - - // TODO - replace with RNG - static size_t item_selection_counter = 0; - - // Variable for storing a generated name if none is provided - memory::string name_storage; - if (name.empty()) { - Culture const* culture = country.get_primary_culture(); - - if (culture != nullptr) { - std::string_view first_name, connector, last_name; - - if (!culture->get_first_names().empty()) { - first_name = culture->get_first_names()[item_selection_counter++ % culture->get_first_names().size()]; - } - - if (!culture->get_last_names().empty()) { - last_name = culture->get_last_names()[item_selection_counter++ % culture->get_last_names().size()]; - } - - if (!first_name.empty() && !last_name.empty()) { - connector = " "; - } - - name_storage = append_string_views(first_name, connector, last_name); - name = name_storage; - } - } - - // TODO - use "noTrait" or "noPersonality" or "noBackground" if either is nullptr - - if (personality == nullptr && !leader_trait_manager.get_personality_traits().empty()) { - personality = leader_trait_manager.get_personality_traits()[ - item_selection_counter++ % leader_trait_manager.get_personality_traits().size() - ]; - } - - if (background == nullptr && !leader_trait_manager.get_background_traits().empty()) { - background = leader_trait_manager.get_background_traits()[ - item_selection_counter++ % leader_trait_manager.get_background_traits().size() - ]; - } - - // TODO - make starting prestige a random proportion of the maximum random prestige - const fixed_point_t starting_prestige = - military_defines.get_leader_max_random_prestige() * static_cast(item_selection_counter++ % 11) / 10; - - generate_leader(country, LeaderBase{ - name, - branch, - creation_date, - personality, - background, - starting_prestige, - {} // No picture, will be set up by generate_leader - }); - - return true; -} \ No newline at end of file diff --git a/src/openvic-simulation/military/UnitInstanceGroup.hpp b/src/openvic-simulation/military/UnitInstanceGroup.hpp index ee2804ab7..ac8080d5f 100644 --- a/src/openvic-simulation/military/UnitInstanceGroup.hpp +++ b/src/openvic-simulation/military/UnitInstanceGroup.hpp @@ -3,8 +3,6 @@ #include #include -#include "openvic-simulation/core/memory/Colony.hpp" -#include "openvic-simulation/military/Leader.hpp" #include "openvic-simulation/military/UnitInstance.hpp" #include "openvic-simulation/military/UnitType.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -14,9 +12,10 @@ #include "openvic-simulation/military/UnitBranchedGetterMacro.hpp" //below other imports that undef the macros namespace OpenVic { - struct ProvinceInstance; struct CountryInstance; + struct LeaderInstance; struct MapInstance; + struct ProvinceInstance; struct UnitInstanceGroup { private: @@ -146,89 +145,4 @@ namespace OpenVic { fixed_point_t get_total_consumed_supply() const; }; - - template - struct UnitDeployment; - - template - struct UnitDeploymentGroup; - - struct MapInstance; - struct Deployment; - struct CultureManager; - struct LeaderTraitManager; - struct MilitaryDefines; - struct Pop; - - struct UnitInstanceManager { - private: - // Used for leader pictures and names - CultureManager const& culture_manager; - LeaderTraitManager const& leader_trait_manager; - MilitaryDefines const& military_defines; - - // TODO - use single counter or separate for leaders vs units vs unit groups? (even separate for branches?) - // Starts at 1, so ID 0 represents an invalid value - unique_id_t unique_id_counter = 1; - - // TODO - maps from unique_ids to leader/unit/unit group pointers (one big map or multiple maps?) - - memory::colony PROPERTY(leaders); - ordered_map> PROPERTY(leader_instance_map); - - memory::colony PROPERTY(regiments); - memory::colony PROPERTY(ships); - ordered_map> PROPERTY(unit_instance_map); - - OV_UNIT_BRANCHED_GETTER(get_unit_instances, regiments, ships); - - memory::colony PROPERTY(armies); - memory::colony PROPERTY(navies); - ordered_map> PROPERTY(unit_instance_group_map); - - OV_UNIT_BRANCHED_GETTER(get_unit_instance_groups, armies, navies); - - Pop* recruit_pop_in(ProvinceInstance& province, const bool is_rebel) const; - template - UnitInstanceBranched& generate_unit_instance( - UnitDeployment const& unit_deployment, - MapInstance& map_instance, - const bool is_rebel - ); - template - bool generate_unit_instance_group( - MapInstance& map_instance, CountryInstance& country, UnitDeploymentGroup const& unit_deployment_group - ); - template - void generate_leader(CountryInstance& country, T&& leader_base); - - public: - UnitInstanceManager( - CultureManager const& new_culture_manager, - LeaderTraitManager const& new_leader_trait_manager, - MilitaryDefines const& new_military_defines - ); - - bool generate_deployment(MapInstance& map_instance, CountryInstance& country, Deployment const& deployment); - - void update_gamestate(); - void tick(); - - LeaderInstance* get_leader_instance_by_unique_id(unique_id_t unique_id); - UnitInstance* get_unit_instance_by_unique_id(unique_id_t unique_id); - UnitInstanceGroup* get_unit_instance_group_by_unique_id(unique_id_t unique_id); - - // Creates a new leader of the specified branch and adds it to the specified country. The leader's name and traits - // can be specified, but if they are not, the leader will be generated with a random name and traits. The country's - // leadership points will be checked and, if there are enough, have the leader creation cost subtracted from them. - // If the country does not have enough leadership points, the function will return false and no leader will be created. - bool create_leader( - CountryInstance& country, - unit_branch_t branch, - Date creation_date, - std::string_view name = {}, - LeaderTrait const* personality = nullptr, - LeaderTrait const* background = nullptr - ); - }; } diff --git a/src/openvic-simulation/military/UnitInstanceManager.cpp b/src/openvic-simulation/military/UnitInstanceManager.cpp new file mode 100644 index 000000000..11de7ac77 --- /dev/null +++ b/src/openvic-simulation/military/UnitInstanceManager.cpp @@ -0,0 +1,321 @@ +#include "UnitInstanceManager.hpp" + +#include + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/defines/MilitaryDefines.hpp" +#include "openvic-simulation/map/MapInstance.hpp" +#include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/military/Deployment.hpp" +#include "openvic-simulation/military/LeaderTraitManager.hpp" +#include "openvic-simulation/population/CultureManager.hpp" +#include "openvic-simulation/population/PopType.hpp" + +using namespace OpenVic; + +using enum unit_branch_t; + +Pop* UnitInstanceManager::recruit_pop_in(ProvinceInstance& province, const bool is_rebel) const { + if (is_rebel) { + for (auto& pop : province.get_mutable_pops()) { + if (pop.get_rebel_type() != nullptr && pop.try_recruit()) { + return &pop; + } + } + } else { + for (auto& pop : province.get_mutable_pops()) { + if (pop.get_type().can_be_recruited && pop.try_recruit()) { + /* + Victoria 2 does not respect cultural restrictions when applying history. + */ + return &pop; + } + } + } + + //fallback to understrength pops + if (is_rebel) { + for (auto& pop : province.get_mutable_pops()) { + if (pop.get_rebel_type() != nullptr && pop.try_recruit_understrength()) { + return &pop; + } + } + } else { + for (auto& pop : province.get_mutable_pops()) { + if (pop.get_type().can_be_recruited && pop.try_recruit_understrength()) { + /* + Victoria 2 does not respect cultural restrictions when applying history. + */ + return &pop; + } + } + } + + return nullptr; +} + +template +UnitInstanceBranched& UnitInstanceManager::generate_unit_instance( + UnitDeployment const& unit_deployment, + MapInstance& map_instance, + const bool is_rebel +) { + UnitInstanceBranched& unit_instance = *get_unit_instances().insert( + [this, &unit_deployment, &map_instance, is_rebel]() -> UnitInstanceBranched { + if constexpr (Branch == LAND) { + RegimentDeployment const& regiment_deployment = unit_deployment; + ProvinceInstance& province = map_instance.get_province_instance_by_definition(*regiment_deployment.get_home()); + Pop* const pop_ptr = recruit_pop_in(province, is_rebel); + if (pop_ptr == nullptr) { + spdlog::warn_s( + "Regiment {} in province {} lacks backing pop.", regiment_deployment.get_name(), province.get_identifier() + ); + } + + return { + unique_id_counter++, + unit_deployment.get_name(), + unit_deployment.type, + pop_ptr, + false + }; + } else if constexpr (Branch == NAVAL) { + return { + unique_id_counter++, + unit_deployment.get_name(), + unit_deployment.type + }; + } + }() + ); + + unit_instance_map.emplace(unit_instance.unique_id, unit_instance); + + return unit_instance; +} + +template +bool UnitInstanceManager::generate_unit_instance_group( + MapInstance& map_instance, CountryInstance& country, UnitDeploymentGroup const& unit_deployment_group +) { + if (unit_deployment_group.get_units().empty()) { + spdlog::error_s( + "Trying to generate unit group \"{}\" with no units for country \"{}\"", + unit_deployment_group.get_name(), country + ); + return false; + } + + ProvinceInstance& location = map_instance.get_province_instance_by_definition(unit_deployment_group.get_location()); + UnitInstanceGroupBranched& unit_instance_group = *get_unit_instance_groups().emplace( + unique_id_counter++, + unit_deployment_group.get_name(), + country, + location + ); + unit_instance_group_map.emplace(unit_instance_group.unique_id, unit_instance_group); + + bool ret = true; + + for (UnitDeployment const& unit_deployment : unit_deployment_group.get_units()) { + ret &= unit_instance_group.add_unit( + generate_unit_instance(unit_deployment, map_instance, country.is_rebel_country()) + ); + } + + ret &= unit_instance_group.set_country(country); + + if (unit_deployment_group.get_leader_index().has_value()) { + memory::vector>& leaders = country.get_leaders(); + auto it = leaders.begin(); + + advance(it, *unit_deployment_group.get_leader_index()); + + if (it < leaders.end()) { + ret &= unit_instance_group.set_leader(&it->get()); + } else { + spdlog::error_s( + "Invalid leader index {} for unit group \"{}\" for country \"{}\"", + *unit_deployment_group.get_leader_index(), unit_deployment_group.get_name(), country + ); + ret = false; + } + } + + return ret; +} + +template +void UnitInstanceManager::generate_leader(CountryInstance& country, T&& leader_base) { + LeaderInstance& leader_instance = *leaders.emplace( + unique_id_counter++, + std::forward(leader_base), + country + ); + leader_instance_map.emplace(leader_instance.unique_id, leader_instance); + country.add_leader(leader_instance); + + if (leader_instance.get_picture().empty() && country.get_primary_culture() != nullptr) { + leader_instance.set_picture(culture_manager.get_leader_picture_name( + country.get_primary_culture()->group.get_leader(), leader_instance.branch + )); + } +} + +UnitInstanceManager::UnitInstanceManager( + CultureManager const& new_culture_manager, + LeaderTraitManager const& new_leader_trait_manager, + MilitaryDefines const& new_military_defines +) : culture_manager { new_culture_manager }, + leader_trait_manager { new_leader_trait_manager }, + military_defines { new_military_defines } {} + +bool UnitInstanceManager::generate_deployment( + MapInstance& map_instance, CountryInstance& country, Deployment const& deployment +) { + bool ret = true; + + for (LeaderBase const& leader : deployment.get_leaders()) { + generate_leader(country, leader); + } + + const auto generate_group = [this, &map_instance, &country, &ret, &deployment]() -> void { + for (UnitDeploymentGroup const& unit_deployment_group : deployment.get_unit_deployment_groups()) { + ret &= generate_unit_instance_group(map_instance, country, unit_deployment_group); + } + }; + + generate_group.template operator()(); + generate_group.template operator()(); + + return ret; +} + +void UnitInstanceManager::update_gamestate() { + for (ArmyInstance& army : armies) { + army.update_gamestate(); + } + for (NavyInstance& navy : navies) { + navy.update_gamestate(); + } +} + +void UnitInstanceManager::tick() { + for (ArmyInstance& army : armies) { + army.tick(); + } + for (NavyInstance& navy : navies) { + navy.tick(); + } +} + +LeaderInstance* UnitInstanceManager::get_leader_instance_by_unique_id(unique_id_t unique_id) { + const decltype(leader_instance_map)::const_iterator it = leader_instance_map.find(unique_id); + + if (it != leader_instance_map.end()) { + return &it->second.get(); + } else { + return nullptr; + } +} + +UnitInstance* UnitInstanceManager::get_unit_instance_by_unique_id(unique_id_t unique_id) { + const decltype(unit_instance_map)::const_iterator it = unit_instance_map.find(unique_id); + + if (it != unit_instance_map.end()) { + return &it->second.get(); + } else { + return nullptr; + } +} + +UnitInstanceGroup* UnitInstanceManager::get_unit_instance_group_by_unique_id(unique_id_t unique_id) { + const decltype(unit_instance_group_map)::const_iterator it = unit_instance_group_map.find(unique_id); + + if (it != unit_instance_group_map.end()) { + return &it->second.get(); + } else { + return nullptr; + } +} + +bool UnitInstanceManager::create_leader( + CountryInstance& country, + unit_branch_t branch, + Date creation_date, + std::string_view name, + LeaderTrait const* personality, + LeaderTrait const* background +) { + const fixed_point_t leader_creation_cost = military_defines.get_leader_recruit_cost(); + if (country.get_leadership_point_stockpile() < leader_creation_cost) { + spdlog::error_s( + "Country \"{}\" does not have enough leadership points ({:.2}) to create a {} (cost: {:.2})", + country, + country.get_leadership_point_stockpile(), + get_branched_leader_name(branch), + leader_creation_cost + ); + return false; + } + + country.set_leadership_point_stockpile(country.get_leadership_point_stockpile() - leader_creation_cost); + + // TODO - replace with RNG + static size_t item_selection_counter = 0; + + // Variable for storing a generated name if none is provided + memory::string name_storage; + if (name.empty()) { + Culture const* culture = country.get_primary_culture(); + + if (culture != nullptr) { + std::string_view first_name, connector, last_name; + + if (!culture->get_first_names().empty()) { + first_name = culture->get_first_names()[item_selection_counter++ % culture->get_first_names().size()]; + } + + if (!culture->get_last_names().empty()) { + last_name = culture->get_last_names()[item_selection_counter++ % culture->get_last_names().size()]; + } + + if (!first_name.empty() && !last_name.empty()) { + connector = " "; + } + + name_storage = append_string_views(first_name, connector, last_name); + name = name_storage; + } + } + + // TODO - use "noTrait" or "noPersonality" or "noBackground" if either is nullptr + + if (personality == nullptr && !leader_trait_manager.get_personality_traits().empty()) { + personality = leader_trait_manager.get_personality_traits()[ + item_selection_counter++ % leader_trait_manager.get_personality_traits().size() + ]; + } + + if (background == nullptr && !leader_trait_manager.get_background_traits().empty()) { + background = leader_trait_manager.get_background_traits()[ + item_selection_counter++ % leader_trait_manager.get_background_traits().size() + ]; + } + + // TODO - make starting prestige a random proportion of the maximum random prestige + const fixed_point_t starting_prestige = + military_defines.get_leader_max_random_prestige() * static_cast(item_selection_counter++ % 11) / 10; + + generate_leader(country, LeaderBase{ + name, + branch, + creation_date, + personality, + background, + starting_prestige, + {} // No picture, will be set up by generate_leader + }); + + return true; +} \ No newline at end of file diff --git a/src/openvic-simulation/military/UnitInstanceManager.hpp b/src/openvic-simulation/military/UnitInstanceManager.hpp new file mode 100644 index 000000000..f2dde824e --- /dev/null +++ b/src/openvic-simulation/military/UnitInstanceManager.hpp @@ -0,0 +1,94 @@ +#pragma once + +#include +#include + +#include "openvic-simulation/core/memory/Colony.hpp" +#include "openvic-simulation/military/Deployment.hpp" +#include "openvic-simulation/military/Leader.hpp" +#include "openvic-simulation/military/UnitInstance.hpp" +#include "openvic-simulation/military/UnitInstanceGroup.hpp" +#include "openvic-simulation/types/UnitBranchType.hpp" +#include "openvic-simulation/utility/Getters.hpp" + +#include "openvic-simulation/military/UnitBranchedGetterMacro.hpp" //below other imports that undef the macros + +namespace OpenVic { + struct MapInstance; + struct CultureManager; + struct LeaderTraitManager; + struct MilitaryDefines; + struct Pop; + + struct UnitInstanceManager { + private: + // Used for leader pictures and names + CultureManager const& culture_manager; + LeaderTraitManager const& leader_trait_manager; + MilitaryDefines const& military_defines; + + // TODO - use single counter or separate for leaders vs units vs unit groups? (even separate for branches?) + // Starts at 1, so ID 0 represents an invalid value + unique_id_t unique_id_counter = 1; + + // TODO - maps from unique_ids to leader/unit/unit group pointers (one big map or multiple maps?) + + memory::colony PROPERTY(leaders); + ordered_map> PROPERTY(leader_instance_map); + + memory::colony PROPERTY(regiments); + memory::colony PROPERTY(ships); + ordered_map> PROPERTY(unit_instance_map); + + OV_UNIT_BRANCHED_GETTER(get_unit_instances, regiments, ships); + + memory::colony PROPERTY(armies); + memory::colony PROPERTY(navies); + ordered_map> PROPERTY(unit_instance_group_map); + + OV_UNIT_BRANCHED_GETTER(get_unit_instance_groups, armies, navies); + + Pop* recruit_pop_in(ProvinceInstance& province, const bool is_rebel) const; + template + UnitInstanceBranched& generate_unit_instance( + UnitDeployment const& unit_deployment, + MapInstance& map_instance, + const bool is_rebel + ); + template + bool generate_unit_instance_group( + MapInstance& map_instance, CountryInstance& country, UnitDeploymentGroup const& unit_deployment_group + ); + template + void generate_leader(CountryInstance& country, T&& leader_base); + + public: + UnitInstanceManager( + CultureManager const& new_culture_manager, + LeaderTraitManager const& new_leader_trait_manager, + MilitaryDefines const& new_military_defines + ); + + bool generate_deployment(MapInstance& map_instance, CountryInstance& country, Deployment const& deployment); + + void update_gamestate(); + void tick(); + + LeaderInstance* get_leader_instance_by_unique_id(unique_id_t unique_id); + UnitInstance* get_unit_instance_by_unique_id(unique_id_t unique_id); + UnitInstanceGroup* get_unit_instance_group_by_unique_id(unique_id_t unique_id); + + // Creates a new leader of the specified branch and adds it to the specified country. The leader's name and traits + // can be specified, but if they are not, the leader will be generated with a random name and traits. The country's + // leadership points will be checked and, if there are enough, have the leader creation cost subtracted from them. + // If the country does not have enough leadership points, the function will return false and no leader will be created. + bool create_leader( + CountryInstance& country, + unit_branch_t branch, + Date creation_date, + std::string_view name = {}, + LeaderTrait const* personality = nullptr, + LeaderTrait const* background = nullptr + ); + }; +} diff --git a/src/openvic-simulation/military/UnitType.cpp b/src/openvic-simulation/military/UnitType.cpp index 1225cc763..91b601b03 100644 --- a/src/openvic-simulation/military/UnitType.cpp +++ b/src/openvic-simulation/military/UnitType.cpp @@ -1,16 +1,9 @@ #include "UnitType.hpp" -#include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/map/TerrainType.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" -#include "openvic-simulation/modifier/ModifierEffectCache.hpp" -#include "openvic-simulation/types/FixedVector.hpp" -#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/map/TerrainType.hpp" // IWYU pragma: keep for fmt #include "openvic-simulation/types/UnitBranchType.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; using enum unit_branch_t; using enum UnitType::unit_category_t; @@ -82,337 +75,3 @@ UnitTypeBranched::UnitTypeBranched( fire_range { ship_type_args.fire_range }, evasion { ship_type_args.evasion }, torpedo_attack { ship_type_args.torpedo_attack } {} - -void UnitTypeManager::reserve_all_unit_types(size_t size) { - reserve_more_unit_types(size); - reserve_more_regiment_types(size); - reserve_more_ship_types(size); -} - -void UnitTypeManager::lock_all_unit_types() { - unit_types.lock(); - regiment_types.lock(); - ship_types.lock(); -} - -static bool _check_shared_parameters(std::string_view identifier, UnitType::unit_type_args_t const& unit_args) { - if (identifier.empty()) { - spdlog::error_s("Invalid unit identifier - empty!"); - return false; - } - - if (unit_args.icon < 0) { - spdlog::error_s( - "Invalid icon for unit {} - {} (must be >= 0)", - identifier, unit_args.icon - ); - return false; - } - - if (unit_args.unit_category == INVALID_UNIT_CATEGORY) { - spdlog::error_s("Invalid unit type for unit {}!", identifier); - return false; - } - - // TODO check that sprite, move_sound and select_sound exist - - return true; -} - -bool UnitTypeManager::add_regiment_type( - std::string_view identifier, UnitType::unit_type_args_t& unit_args, RegimentType::regiment_type_args_t const& regiment_type_args -) { - if (!_check_shared_parameters(identifier, unit_args)) { - return false; - } - - // TODO check that sprite_override, sprite_mount, and sprite_mount_attach_node exist - - if (ship_types.has_identifier(identifier)) { - spdlog::error_s("Land unit {} already exists as a naval unit!", identifier); - return false; - } - - bool ret = regiment_types.emplace_item( - identifier, - RegimentType::index_t { get_regiment_type_count() }, identifier, - unit_args, std::move(regiment_type_args) - ); - if (ret) { - // Cannot use get_back_regiment_type() as we need non-const but don't want to generate all non-const functions. - ret &= unit_types.emplace_via_copy(®iment_types.back()); - } - return ret; -} - -bool UnitTypeManager::add_ship_type( - std::string_view identifier, UnitType::unit_type_args_t& unit_args, ShipType::ship_type_args_t const& ship_type_args -) { - if (!_check_shared_parameters(identifier, unit_args)) { - return false; - } - - if (ship_type_args.naval_icon <= 0) { - spdlog::error_s("Invalid naval icon identifier - {} (must be positive)", ship_type_args.naval_icon); - return false; - } - - if (ship_type_args.supply_consumption_score <= 0) { - spdlog::warn_s("Supply consumption score for {} is not positive!", identifier); - } - - if (regiment_types.has_identifier(identifier)) { - spdlog::error_s("Naval unit {} already exists as a land unit!", identifier); - return false; - } - - bool ret = ship_types.emplace_item( - identifier, - ShipType::index_t { get_ship_type_count() }, identifier, - unit_args, ship_type_args - ); - if (ret) { - // Cannot use get_back_ship_type() as we need non-const but don't want to generate all non-const functions. - ret &= unit_types.emplace_via_copy(&ship_types.back()); - } - return ret; -} - -bool UnitTypeManager::load_unit_type_file( - GoodDefinitionManager const& good_definition_manager, TerrainTypeManager const& terrain_type_manager, - ModifierManager const& modifier_manager, ovdl::v2script::Parser const& parser -) { - using namespace std::string_view_literals; - auto type_symbol = parser.find_intern("type"sv); - if (!type_symbol) { - spdlog::error_s("type could not be interned."); - } - - return expect_dictionary([this, &good_definition_manager, &terrain_type_manager, &modifier_manager, &type_symbol]( - std::string_view key, ast::NodeCPtr value - ) -> bool { - - unit_branch_t branch = INVALID_BRANCH; - - bool ret = expect_key(type_symbol, expect_branch_identifier(assign_variable_callback(branch)))(value); - - /* We shouldn't just check ret as it can be false even if branch was successfully parsed, - * but more than one instance of the key was found. */ - if (branch != LAND && branch != NAVAL) { - spdlog::error_s("Failed to read branch for unit: {}", key); - return false; - } - - UnitType::unit_type_args_t unit_args {}; - - static const string_map_t unit_type_map { - { "infantry", INFANTRY }, - { "cavalry", CAVALRY }, - { "support", SUPPORT }, - { "special", SPECIAL }, - { "big_ship", BIG_SHIP }, - { "light_ship", LIGHT_SHIP }, - { "transport", TRANSPORT } - }; - - key_map_t key_map {}; - /* Shared dictionary entries */ - ret &= add_key_map_entries(key_map, - "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.icon)), - "type", ONE_EXACTLY, success_callback, /* Already loaded above using expect_key */ - "sprite", ONE_EXACTLY, expect_identifier(assign_variable_callback(unit_args.sprite)), - "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(unit_args.starts_unlocked)), - "unit_type", ONE_EXACTLY, - expect_identifier(expect_mapped_string(unit_type_map, assign_variable_callback(unit_args.unit_category))), - "floating_flag", ONE_EXACTLY, expect_bool(assign_variable_callback(unit_args.floating_flag)), - "priority", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.priority)), - "max_strength", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.max_strength)), - "default_organisation", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.default_organisation)), - "maximum_speed", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.maximum_speed)), - "weighted_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.weighted_value)), - "move_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.move_sound)), - "select_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.select_sound)), - "build_time", ONE_EXACTLY, expect_days(assign_variable_callback(unit_args.build_time)), - "build_cost", ONE_EXACTLY, - good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(unit_args.build_cost)), - "supply_consumption", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.supply_consumption)), - "supply_cost", ONE_EXACTLY, - good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(unit_args.supply_cost)) - ); - - auto add_terrain_modifier_value = [&unit_args, &terrain_type_manager, &modifier_manager]( - std::string_view default_key, ast::NodeCPtr default_value - ) -> bool { - TerrainType const* terrain_type = terrain_type_manager.get_terrain_type_by_identifier(default_key); - - if (terrain_type != nullptr) { - ModifierValue& modifier_value = unit_args.terrain_modifier_values[terrain_type]; - return expect_dictionary( - modifier_manager.expect_unit_terrain_modifier(modifier_value, terrain_type->get_identifier()) - )(default_value); - } - - return key_value_invalid_callback(default_key, default_value); - }; - - switch (branch) { - case LAND: { - RegimentType::regiment_type_args_t regiment_type_args {}; - bool is_restricted_to_primary_culture = false; - bool is_restricted_to_accepted_cultures = false; - - ret &= add_key_map_entries(key_map, - "primary_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_primary_culture)), - "accepted_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_accepted_cultures)), - "sprite_override", ZERO_OR_ONE, expect_identifier(assign_variable_callback(regiment_type_args.sprite_override)), - "sprite_mount", ZERO_OR_ONE, expect_identifier(assign_variable_callback(regiment_type_args.sprite_mount)), - "sprite_mount_attach_node", ZERO_OR_ONE, - expect_identifier(assign_variable_callback(regiment_type_args.sprite_mount_attach_node)), - "reconnaissance", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.reconnaissance)), - "attack", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.attack)), - "defence", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.defence)), - "discipline", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.discipline)), - "support", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.support)), - "maneuver", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.maneuver)), - "siege", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(regiment_type_args.siege)) - ); - - if (is_restricted_to_accepted_cultures) { - regiment_type_args.allowed_cultures = regiment_allowed_cultures_t::ACCEPTED_CULTURES; - } else if (is_restricted_to_primary_culture) { - regiment_type_args.allowed_cultures = regiment_allowed_cultures_t::PRIMARY_CULTURE; - } else { - regiment_type_args.allowed_cultures = regiment_allowed_cultures_t::ALL_CULTURES; - } - - ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier_value)(value); - - ret &= add_regiment_type(key, unit_args, regiment_type_args); - - return ret; - } - case NAVAL: { - ShipType::ship_type_args_t ship_type_args {}; - - ret &= add_key_map_entries(key_map, - "naval_icon", ONE_EXACTLY, expect_uint(assign_variable_callback(ship_type_args.naval_icon)), - "sail", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.sail)), - "transport", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.transport)), - "capital", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.capital)), - "colonial_points", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(ship_type_args.colonial_points)), - "can_build_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.build_overseas)), - "min_port_level", ONE_EXACTLY, expect_uint(assign_variable_callback(ship_type_args.min_port_level)), - "limit_per_port", ONE_EXACTLY, expect_int(assign_variable_callback(ship_type_args.limit_per_port)), - "supply_consumption_score", ZERO_OR_ONE, - expect_fixed_point(assign_variable_callback(ship_type_args.supply_consumption_score)), - "hull", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.hull)), - "gun_power", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.gun_power)), - "fire_range", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.fire_range)), - "evasion", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.evasion)), - "torpedo_attack", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(ship_type_args.torpedo_attack)) - ); - - ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier_value)(value); - - ret &= add_ship_type(key, unit_args, ship_type_args); - - return ret; - } - default: - /* Unreachable - an earlier check terminates the function if branch isn't LAND or NAVAL. */ - spdlog::error_s("Unknown branch for unit {}: {}", key, static_cast(branch)); - return false; - } - })(parser.get_file_node()); -} - -bool UnitTypeManager::generate_modifiers(ModifierManager& modifier_manager) const { - bool ret = true; - - const auto generate_stat_modifiers = [&modifier_manager, &ret]( - std::derived_from auto unit_type_effects, std::string_view identifier - ) -> void { - using enum ModifierEffect::format_t; - - const auto stat_modifier = [&modifier_manager, &ret, &identifier]( - ModifierEffect const*& effect_cache, std::string_view suffix, - ModifierEffect::format_t format, std::string_view localisation_key - ) -> void { - ret &= modifier_manager.register_technology_modifier_effect( - effect_cache, ModifierManager::get_flat_identifier(identifier, suffix), format, - memory::fmt::format("${}$: ${}$", identifier, localisation_key) - ); - }; - - ret &= modifier_manager.register_complex_modifier(identifier); - - // These are ordered following how they appear in the base game, hence the broken up land effects. - stat_modifier(unit_type_effects.default_organisation, "default_organisation", FORMAT_x1_1DP_POS, "DEFAULT_ORG"); - stat_modifier(unit_type_effects.build_time, "build_time", FORMAT_x1_0DP_DAYS_NEG, "BUILD_TIME"); - if constexpr (std::same_as) { - stat_modifier( - unit_type_effects.reconnaissance, "reconnaissance", FORMAT_x1_2DP_POS, "RECONAISSANCE" // paradox typo - ); - stat_modifier(unit_type_effects.siege, "siege", FORMAT_x1_2DP_POS, "SIEGE"); - } - stat_modifier(unit_type_effects.attack, "attack", FORMAT_x1_2DP_POS, "ATTACK"); - stat_modifier(unit_type_effects.defence, "defence", FORMAT_x1_2DP_POS, "DEFENCE"); - if constexpr (std::same_as) { - stat_modifier(unit_type_effects.discipline, "discipline", FORMAT_x100_0DP_PC_POS, "DISCIPLINE"); - stat_modifier(unit_type_effects.support, "support", FORMAT_x100_0DP_PC_POS, "SUPPORT"); - stat_modifier(unit_type_effects.maneuver, "maneuver", FORMAT_x1_0DP_POS, "Maneuver"); - } - stat_modifier(unit_type_effects.maximum_speed, "maximum_speed", FORMAT_x1_2DP_SPEED_POS, "MAXIMUM_SPEED"); - if constexpr(std::same_as) { - stat_modifier(unit_type_effects.gun_power, "gun_power", FORMAT_x1_2DP_POS, "GUN_POWER"); - stat_modifier(unit_type_effects.torpedo_attack, "torpedo_attack", FORMAT_x1_2DP_POS, "TORPEDO_ATTACK"); - stat_modifier(unit_type_effects.hull, "hull", FORMAT_x1_2DP_POS, "HULL"); - stat_modifier(unit_type_effects.fire_range, "fire_range", FORMAT_x100_0DP_POS, "FIRE_RANGE"); - stat_modifier(unit_type_effects.evasion, "evasion", FORMAT_x100_0DP_PC_POS, "EVASION"); - } - stat_modifier( - unit_type_effects.supply_consumption, "supply_consumption", FORMAT_x100_0DP_PC_NEG, "SUPPLY_CONSUMPTION" - ); - if constexpr(std::same_as) { - stat_modifier( - unit_type_effects.supply_consumption_score, "supply_consumption_score", FORMAT_x1_0DP_NEG, "SUPPLY_LOAD" - ); - // Works but doesn't appear in tooltips in the base game - stat_modifier(unit_type_effects.colonial_points, "colonial_points", FORMAT_x1_0DP_POS, "COLONIAL_POINTS_TECH"); - } - }; - - generate_stat_modifiers(modifier_manager.modifier_effect_cache.army_base_effects, "army_base"); - - memory::FixedVector& regiment_type_effects = - modifier_manager.modifier_effect_cache.regiment_type_effects; - - regiment_type_effects = std::move( - decltype(ModifierEffectCache::regiment_type_effects) { - generate_values, - regiment_type_index_t(get_regiment_type_count()) - } - ); - - for (RegimentType const& regiment_type : get_regiment_types()) { - generate_stat_modifiers(regiment_type_effects[regiment_type.index], regiment_type.get_identifier()); - } - - generate_stat_modifiers(modifier_manager.modifier_effect_cache.navy_base_effects, "navy_base"); - - memory::FixedVector& ship_type_effects = - modifier_manager.modifier_effect_cache.ship_type_effects; - - ship_type_effects = std::move( - decltype(ModifierEffectCache::ship_type_effects) { - generate_values, - ship_type_index_t(get_ship_type_count()) - } - ); - - for (ShipType const& ship_type : get_ship_types()) { - generate_stat_modifiers(ship_type_effects[ship_type.index], ship_type.get_identifier()); - } - - return ret; -} diff --git a/src/openvic-simulation/military/UnitType.hpp b/src/openvic-simulation/military/UnitType.hpp index 65dda9dca..dc3f6b2d6 100644 --- a/src/openvic-simulation/military/UnitType.hpp +++ b/src/openvic-simulation/military/UnitType.hpp @@ -3,24 +3,17 @@ #include #include -#include - -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/economy/GoodDefinition.hpp" #include "openvic-simulation/modifier/Modifier.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/TypedIndices.hpp" #include "openvic-simulation/types/UnitBranchType.hpp" namespace OpenVic { + struct GoodDefinition; struct TerrainType; - struct TerrainTypeManager; - struct Culture; - struct CountryInstance; struct UnitType : HasIdentifier { using icon_t = uint32_t; @@ -160,43 +153,4 @@ namespace OpenVic { ); UnitTypeBranched(UnitTypeBranched&&) = default; }; - - struct UnitTypeManager { - private: - IdentifierPointerRegistry IDENTIFIER_REGISTRY(unit_type); - IdentifierRegistry IDENTIFIER_REGISTRY(regiment_type); - IdentifierRegistry IDENTIFIER_REGISTRY(ship_type); - - public: - void reserve_all_unit_types(size_t size); - void lock_all_unit_types(); - - bool add_regiment_type( - std::string_view identifier, UnitType::unit_type_args_t& unit_args, - RegimentType::regiment_type_args_t const& regiment_type_args - ); - bool add_ship_type( - std::string_view identifier, UnitType::unit_type_args_t& unit_args, - ShipType::ship_type_args_t const& ship_type_args - ); - - static NodeTools::Callback auto expect_branch_str( - NodeTools::Callback auto callback - ) { - using enum unit_branch_t; - static const string_map_t branch_map { - { "land", LAND }, { "naval", NAVAL }, { "sea", NAVAL } - }; - return NodeTools::expect_mapped_string(branch_map, callback); - } - static NodeTools::NodeCallback auto expect_branch_identifier(NodeTools::Callback auto callback) { - return NodeTools::expect_identifier(expect_branch_str(callback)); - } - - bool load_unit_type_file( - GoodDefinitionManager const& good_definition_manager, TerrainTypeManager const& terrain_type_manager, - ModifierManager const& modifier_manager, ovdl::v2script::Parser const& parser - ); - bool generate_modifiers(ModifierManager& modifier_manager) const; - }; } diff --git a/src/openvic-simulation/military/UnitTypeManager.cpp b/src/openvic-simulation/military/UnitTypeManager.cpp new file mode 100644 index 000000000..7d5319774 --- /dev/null +++ b/src/openvic-simulation/military/UnitTypeManager.cpp @@ -0,0 +1,353 @@ +#include "UnitTypeManager.hpp" + +#include + +#include "openvic-simulation/country/CountryInstance.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/GoodDefinitionManager.hpp" +#include "openvic-simulation/map/TerrainTypeManager.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" +#include "openvic-simulation/modifier/ModifierEffectCache.hpp" +#include "openvic-simulation/types/FixedVector.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" +#include "openvic-simulation/types/UnitBranchType.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +using enum unit_branch_t; +using enum UnitType::unit_category_t; + +void UnitTypeManager::reserve_all_unit_types(size_t size) { + reserve_more_unit_types(size); + reserve_more_regiment_types(size); + reserve_more_ship_types(size); +} + +void UnitTypeManager::lock_all_unit_types() { + unit_types.lock(); + regiment_types.lock(); + ship_types.lock(); +} + +static bool _check_shared_parameters(std::string_view identifier, UnitType::unit_type_args_t const& unit_args) { + if (identifier.empty()) { + spdlog::error_s("Invalid unit identifier - empty!"); + return false; + } + + if (unit_args.icon < 0) { + spdlog::error_s( + "Invalid icon for unit {} - {} (must be >= 0)", + identifier, unit_args.icon + ); + return false; + } + + if (unit_args.unit_category == INVALID_UNIT_CATEGORY) { + spdlog::error_s("Invalid unit type for unit {}!", identifier); + return false; + } + + // TODO check that sprite, move_sound and select_sound exist + + return true; +} + +bool UnitTypeManager::add_regiment_type( + std::string_view identifier, UnitType::unit_type_args_t& unit_args, RegimentType::regiment_type_args_t const& regiment_type_args +) { + if (!_check_shared_parameters(identifier, unit_args)) { + return false; + } + + // TODO check that sprite_override, sprite_mount, and sprite_mount_attach_node exist + + if (ship_types.has_identifier(identifier)) { + spdlog::error_s("Land unit {} already exists as a naval unit!", identifier); + return false; + } + + bool ret = regiment_types.emplace_item( + identifier, + RegimentType::index_t { get_regiment_type_count() }, identifier, + unit_args, std::move(regiment_type_args) + ); + if (ret) { + // Cannot use get_back_regiment_type() as we need non-const but don't want to generate all non-const functions. + ret &= unit_types.emplace_via_copy(®iment_types.back()); + } + return ret; +} + +bool UnitTypeManager::add_ship_type( + std::string_view identifier, UnitType::unit_type_args_t& unit_args, ShipType::ship_type_args_t const& ship_type_args +) { + if (!_check_shared_parameters(identifier, unit_args)) { + return false; + } + + if (ship_type_args.naval_icon <= 0) { + spdlog::error_s("Invalid naval icon identifier - {} (must be positive)", ship_type_args.naval_icon); + return false; + } + + if (ship_type_args.supply_consumption_score <= 0) { + spdlog::warn_s("Supply consumption score for {} is not positive!", identifier); + } + + if (regiment_types.has_identifier(identifier)) { + spdlog::error_s("Naval unit {} already exists as a land unit!", identifier); + return false; + } + + bool ret = ship_types.emplace_item( + identifier, + ShipType::index_t { get_ship_type_count() }, identifier, + unit_args, ship_type_args + ); + if (ret) { + // Cannot use get_back_ship_type() as we need non-const but don't want to generate all non-const functions. + ret &= unit_types.emplace_via_copy(&ship_types.back()); + } + return ret; +} + +bool UnitTypeManager::load_unit_type_file( + GoodDefinitionManager const& good_definition_manager, TerrainTypeManager const& terrain_type_manager, + ModifierManager const& modifier_manager, ovdl::v2script::Parser const& parser +) { + using namespace std::string_view_literals; + auto type_symbol = parser.find_intern("type"sv); + if (!type_symbol) { + spdlog::error_s("type could not be interned."); + } + + return expect_dictionary([this, &good_definition_manager, &terrain_type_manager, &modifier_manager, &type_symbol]( + std::string_view key, ovdl::v2script::ast::Node const* value + ) -> bool { + + unit_branch_t branch = INVALID_BRANCH; + + bool ret = expect_key(type_symbol, expect_branch_identifier(assign_variable_callback(branch)))(value); + + /* We shouldn't just check ret as it can be false even if branch was successfully parsed, + * but more than one instance of the key was found. */ + if (branch != LAND && branch != NAVAL) { + spdlog::error_s("Failed to read branch for unit: {}", key); + return false; + } + + UnitType::unit_type_args_t unit_args {}; + + static const string_map_t unit_type_map { + { "infantry", INFANTRY }, + { "cavalry", CAVALRY }, + { "support", SUPPORT }, + { "special", SPECIAL }, + { "big_ship", BIG_SHIP }, + { "light_ship", LIGHT_SHIP }, + { "transport", TRANSPORT } + }; + + key_map_t key_map {}; + /* Shared dictionary entries */ + ret &= add_key_map_entries(key_map, + "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.icon)), + "type", ONE_EXACTLY, success_callback, /* Already loaded above using expect_key */ + "sprite", ONE_EXACTLY, expect_identifier(assign_variable_callback(unit_args.sprite)), + "active", ZERO_OR_ONE, expect_bool(assign_variable_callback(unit_args.starts_unlocked)), + "unit_type", ONE_EXACTLY, + expect_identifier(expect_mapped_string(unit_type_map, assign_variable_callback(unit_args.unit_category))), + "floating_flag", ONE_EXACTLY, expect_bool(assign_variable_callback(unit_args.floating_flag)), + "priority", ONE_EXACTLY, expect_uint(assign_variable_callback(unit_args.priority)), + "max_strength", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.max_strength)), + "default_organisation", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.default_organisation)), + "maximum_speed", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.maximum_speed)), + "weighted_value", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.weighted_value)), + "move_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.move_sound)), + "select_sound", ZERO_OR_ONE, expect_identifier(assign_variable_callback(unit_args.select_sound)), + "build_time", ONE_EXACTLY, expect_days(assign_variable_callback(unit_args.build_time)), + "build_cost", ONE_EXACTLY, + good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(unit_args.build_cost)), + "supply_consumption", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(unit_args.supply_consumption)), + "supply_cost", ONE_EXACTLY, + good_definition_manager.expect_good_definition_decimal_map(move_variable_callback(unit_args.supply_cost)) + ); + + auto add_terrain_modifier_value = [&unit_args, &terrain_type_manager, &modifier_manager]( + std::string_view default_key, ovdl::v2script::ast::Node const* default_value + ) -> bool { + TerrainType const* terrain_type = terrain_type_manager.get_terrain_type_by_identifier(default_key); + + if (terrain_type != nullptr) { + ModifierValue& modifier_value = unit_args.terrain_modifier_values[terrain_type]; + return expect_dictionary( + modifier_manager.expect_unit_terrain_modifier(modifier_value, terrain_type->get_identifier()) + )(default_value); + } + + return key_value_invalid_callback(default_key, default_value); + }; + + switch (branch) { + case LAND: { + RegimentType::regiment_type_args_t regiment_type_args {}; + bool is_restricted_to_primary_culture = false; + bool is_restricted_to_accepted_cultures = false; + + ret &= add_key_map_entries(key_map, + "primary_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_primary_culture)), + "accepted_culture", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_restricted_to_accepted_cultures)), + "sprite_override", ZERO_OR_ONE, expect_identifier(assign_variable_callback(regiment_type_args.sprite_override)), + "sprite_mount", ZERO_OR_ONE, expect_identifier(assign_variable_callback(regiment_type_args.sprite_mount)), + "sprite_mount_attach_node", ZERO_OR_ONE, + expect_identifier(assign_variable_callback(regiment_type_args.sprite_mount_attach_node)), + "reconnaissance", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.reconnaissance)), + "attack", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.attack)), + "defence", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.defence)), + "discipline", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.discipline)), + "support", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.support)), + "maneuver", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(regiment_type_args.maneuver)), + "siege", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(regiment_type_args.siege)) + ); + + if (is_restricted_to_accepted_cultures) { + regiment_type_args.allowed_cultures = regiment_allowed_cultures_t::ACCEPTED_CULTURES; + } else if (is_restricted_to_primary_culture) { + regiment_type_args.allowed_cultures = regiment_allowed_cultures_t::PRIMARY_CULTURE; + } else { + regiment_type_args.allowed_cultures = regiment_allowed_cultures_t::ALL_CULTURES; + } + + ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier_value)(value); + + ret &= add_regiment_type(key, unit_args, regiment_type_args); + + return ret; + } + case NAVAL: { + ShipType::ship_type_args_t ship_type_args {}; + + ret &= add_key_map_entries(key_map, + "naval_icon", ONE_EXACTLY, expect_uint(assign_variable_callback(ship_type_args.naval_icon)), + "sail", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.sail)), + "transport", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.transport)), + "capital", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.capital)), + "colonial_points", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(ship_type_args.colonial_points)), + "can_build_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(ship_type_args.build_overseas)), + "min_port_level", ONE_EXACTLY, expect_uint(assign_variable_callback(ship_type_args.min_port_level)), + "limit_per_port", ONE_EXACTLY, expect_int(assign_variable_callback(ship_type_args.limit_per_port)), + "supply_consumption_score", ZERO_OR_ONE, + expect_fixed_point(assign_variable_callback(ship_type_args.supply_consumption_score)), + "hull", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.hull)), + "gun_power", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.gun_power)), + "fire_range", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.fire_range)), + "evasion", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(ship_type_args.evasion)), + "torpedo_attack", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(ship_type_args.torpedo_attack)) + ); + + ret &= expect_dictionary_key_map_and_default(key_map, add_terrain_modifier_value)(value); + + ret &= add_ship_type(key, unit_args, ship_type_args); + + return ret; + } + default: + /* Unreachable - an earlier check terminates the function if branch isn't LAND or NAVAL. */ + spdlog::error_s("Unknown branch for unit {}: {}", key, static_cast(branch)); + return false; + } + })(parser.get_file_node()); +} + +bool UnitTypeManager::generate_modifiers(ModifierManager& modifier_manager) const { + bool ret = true; + + const auto generate_stat_modifiers = [&modifier_manager, &ret]( + std::derived_from auto unit_type_effects, std::string_view identifier + ) -> void { + using enum ModifierEffect::format_t; + + const auto stat_modifier = [&modifier_manager, &ret, &identifier]( + ModifierEffect const*& effect_cache, std::string_view suffix, + ModifierEffect::format_t format, std::string_view localisation_key + ) -> void { + ret &= modifier_manager.register_technology_modifier_effect( + effect_cache, ModifierManager::get_flat_identifier(identifier, suffix), format, + memory::fmt::format("${}$: ${}$", identifier, localisation_key) + ); + }; + + ret &= modifier_manager.register_complex_modifier(identifier); + + // These are ordered following how they appear in the base game, hence the broken up land effects. + stat_modifier(unit_type_effects.default_organisation, "default_organisation", FORMAT_x1_1DP_POS, "DEFAULT_ORG"); + stat_modifier(unit_type_effects.build_time, "build_time", FORMAT_x1_0DP_DAYS_NEG, "BUILD_TIME"); + if constexpr (std::same_as) { + stat_modifier( + unit_type_effects.reconnaissance, "reconnaissance", FORMAT_x1_2DP_POS, "RECONAISSANCE" // paradox typo + ); + stat_modifier(unit_type_effects.siege, "siege", FORMAT_x1_2DP_POS, "SIEGE"); + } + stat_modifier(unit_type_effects.attack, "attack", FORMAT_x1_2DP_POS, "ATTACK"); + stat_modifier(unit_type_effects.defence, "defence", FORMAT_x1_2DP_POS, "DEFENCE"); + if constexpr (std::same_as) { + stat_modifier(unit_type_effects.discipline, "discipline", FORMAT_x100_0DP_PC_POS, "DISCIPLINE"); + stat_modifier(unit_type_effects.support, "support", FORMAT_x100_0DP_PC_POS, "SUPPORT"); + stat_modifier(unit_type_effects.maneuver, "maneuver", FORMAT_x1_0DP_POS, "Maneuver"); + } + stat_modifier(unit_type_effects.maximum_speed, "maximum_speed", FORMAT_x1_2DP_SPEED_POS, "MAXIMUM_SPEED"); + if constexpr(std::same_as) { + stat_modifier(unit_type_effects.gun_power, "gun_power", FORMAT_x1_2DP_POS, "GUN_POWER"); + stat_modifier(unit_type_effects.torpedo_attack, "torpedo_attack", FORMAT_x1_2DP_POS, "TORPEDO_ATTACK"); + stat_modifier(unit_type_effects.hull, "hull", FORMAT_x1_2DP_POS, "HULL"); + stat_modifier(unit_type_effects.fire_range, "fire_range", FORMAT_x100_0DP_POS, "FIRE_RANGE"); + stat_modifier(unit_type_effects.evasion, "evasion", FORMAT_x100_0DP_PC_POS, "EVASION"); + } + stat_modifier( + unit_type_effects.supply_consumption, "supply_consumption", FORMAT_x100_0DP_PC_NEG, "SUPPLY_CONSUMPTION" + ); + if constexpr(std::same_as) { + stat_modifier( + unit_type_effects.supply_consumption_score, "supply_consumption_score", FORMAT_x1_0DP_NEG, "SUPPLY_LOAD" + ); + // Works but doesn't appear in tooltips in the base game + stat_modifier(unit_type_effects.colonial_points, "colonial_points", FORMAT_x1_0DP_POS, "COLONIAL_POINTS_TECH"); + } + }; + + generate_stat_modifiers(modifier_manager.modifier_effect_cache.army_base_effects, "army_base"); + + memory::FixedVector& regiment_type_effects = + modifier_manager.modifier_effect_cache.regiment_type_effects; + + regiment_type_effects = std::move( + decltype(ModifierEffectCache::regiment_type_effects) { + generate_values, + regiment_type_index_t(get_regiment_type_count()) + } + ); + + for (RegimentType const& regiment_type : get_regiment_types()) { + generate_stat_modifiers(regiment_type_effects[regiment_type.index], regiment_type.get_identifier()); + } + + generate_stat_modifiers(modifier_manager.modifier_effect_cache.navy_base_effects, "navy_base"); + + memory::FixedVector& ship_type_effects = + modifier_manager.modifier_effect_cache.ship_type_effects; + + ship_type_effects = std::move( + decltype(ModifierEffectCache::ship_type_effects) { + generate_values, + ship_type_index_t(get_ship_type_count()) + } + ); + + for (ShipType const& ship_type : get_ship_types()) { + generate_stat_modifiers(ship_type_effects[ship_type.index], ship_type.get_identifier()); + } + + return ret; +} diff --git a/src/openvic-simulation/military/UnitTypeManager.hpp b/src/openvic-simulation/military/UnitTypeManager.hpp new file mode 100644 index 000000000..c6bd41e9c --- /dev/null +++ b/src/openvic-simulation/military/UnitTypeManager.hpp @@ -0,0 +1,57 @@ +#pragma once + +#include + +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" +#include "openvic-simulation/military/UnitType.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/UnitBranchType.hpp" + +namespace ovdl::v2script { + class Parser; +} + +namespace OpenVic { + struct GoodDefinitionManager; + struct ModifierManager; + struct TerrainTypeManager; + + struct UnitTypeManager { + private: + IdentifierPointerRegistry IDENTIFIER_REGISTRY(unit_type); + IdentifierRegistry IDENTIFIER_REGISTRY(regiment_type); + IdentifierRegistry IDENTIFIER_REGISTRY(ship_type); + + public: + void reserve_all_unit_types(size_t size); + void lock_all_unit_types(); + + bool add_regiment_type( + std::string_view identifier, UnitType::unit_type_args_t& unit_args, + RegimentType::regiment_type_args_t const& regiment_type_args + ); + bool add_ship_type( + std::string_view identifier, UnitType::unit_type_args_t& unit_args, + ShipType::ship_type_args_t const& ship_type_args + ); + + static Callback auto expect_branch_str( + Callback auto callback + ) { + using enum unit_branch_t; + static const string_map_t branch_map { + { "land", LAND }, { "naval", NAVAL }, { "sea", NAVAL } + }; + return NodeTools::expect_mapped_string(branch_map, callback); + } + static NodeTools::NodeCallback auto expect_branch_identifier(Callback auto callback) { + return NodeTools::expect_identifier(expect_branch_str(callback)); + } + + bool load_unit_type_file( + GoodDefinitionManager const& good_definition_manager, TerrainTypeManager const& terrain_type_manager, + ModifierManager const& modifier_manager, ovdl::v2script::Parser const& parser + ); + bool generate_modifiers(ModifierManager& modifier_manager) const; + }; +} diff --git a/src/openvic-simulation/military/Wargoal.cpp b/src/openvic-simulation/military/Wargoal.cpp index 1945abb27..8731ed796 100644 --- a/src/openvic-simulation/military/Wargoal.cpp +++ b/src/openvic-simulation/military/Wargoal.cpp @@ -1,13 +1,8 @@ #include "Wargoal.hpp" -#include - #include -#include "openvic-simulation/dataloader/NodeTools.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; WargoalType::WargoalType( std::string_view new_identifier, std::string_view new_war_name, Timespan new_available_length, @@ -40,200 +35,3 @@ bool WargoalType::parse_scripts(DefinitionManager const& definition_manager) { ret &= on_po_accepted.parse_script(true, definition_manager); return ret; } - -bool WargoalTypeManager::add_wargoal_type( - std::string_view identifier, std::string_view war_name, Timespan available_length, - Timespan truce_length, WargoalType::sprite_t sprite_index, bool triggered_only, bool civil_war, - bool constructing, bool crisis, bool great_war_obligatory, bool mutual, bool all_allowed_states, - bool always, WargoalType::peace_modifiers_t&& modifiers, WargoalType::peace_options_t peace_options, - ConditionScript&& can_use, ConditionScript&& is_valid, ConditionScript&& allowed_states, - ConditionScript&& allowed_substate_regions, ConditionScript&& allowed_states_in_crisis, - ConditionScript&& allowed_countries, EffectScript&& on_add, EffectScript&& on_po_accepted -) { - if (identifier.empty()) { - spdlog::error_s("Invalid wargoal identifier - empty!"); - return false; - } - - if (war_name.empty()) { - spdlog::error_s("Invalid war name for wargoal {} - empty!", identifier); - return false; - } - - if (sprite_index == 0) { - spdlog::warn_s("Invalid sprite for wargoal {} - 0", identifier); - } - - return wargoal_types.emplace_item( - identifier, - identifier, war_name, available_length, truce_length, sprite_index, triggered_only, civil_war, constructing, crisis, - great_war_obligatory, mutual, all_allowed_states, always, std::move(modifiers), peace_options, std::move(can_use), - std::move(is_valid), std::move(allowed_states), std::move(allowed_substate_regions), - std::move(allowed_states_in_crisis), std::move(allowed_countries), std::move(on_add), std::move(on_po_accepted) - ); -} - -bool WargoalTypeManager::load_wargoal_file(ovdl::v2script::Parser const& parser) { - using namespace std::string_view_literals; - ovdl::symbol peace_order_symbol = parser.find_intern("peace_order"sv); - if (!peace_order_symbol) { - spdlog::error_s("peace_order could not be interned."); - } - - bool ret = expect_dictionary_reserve_length( - wargoal_types, - [this, &peace_order_symbol](std::string_view identifier, ast::NodeCPtr value) -> bool { - // If peace_order_symbol is false, we know there is no peace_order string in the parser - if (peace_order_symbol && identifier == peace_order_symbol.c_str()) { - return true; - } - - using enum WargoalType::peace_options_t; - using enum scope_type_t; - - std::string_view war_name; - Timespan available {}, truce {}; - WargoalType::sprite_t sprite_index = 0; - bool triggered_only = false, civil_war = false, constructing = true, crisis = true, great_war_obligatory = false, - mutual = false, all_allowed_states = false, always = false; - WargoalType::peace_options_t peace_options = NO_PEACE_OPTIONS; - WargoalType::peace_modifiers_t modifiers; - ConditionScript can_use { COUNTRY, COUNTRY, COUNTRY }; - ConditionScript is_valid { COUNTRY, COUNTRY, COUNTRY }; - ConditionScript allowed_states { STATE, COUNTRY, COUNTRY }; - ConditionScript allowed_substate_regions { STATE, COUNTRY, COUNTRY }; - ConditionScript allowed_states_in_crisis { STATE, COUNTRY, COUNTRY }; - ConditionScript allowed_countries { COUNTRY, COUNTRY, COUNTRY }; - EffectScript on_add, on_po_accepted; //country as default scope for both - - const auto expect_peace_option = [&peace_options](WargoalType::peace_options_t peace_option) -> node_callback_t { - return expect_bool([&peace_options, peace_option](bool val) -> bool { - if (val) { - peace_options |= peace_option; - } else { - peace_options &= ~peace_option; - } - return true; - }); - }; - - bool ret = expect_dictionary_keys_and_default( - [&modifiers, &identifier](std::string_view key, ast::NodeCPtr value) -> bool { - using enum WargoalType::PEACE_MODIFIERS; - static const string_map_t peace_modifier_map { - { "badboy_factor", BADBOY_FACTOR }, - { "prestige_factor", PRESTIGE_FACTOR }, - { "peace_cost_factor", PEACE_COST_FACTOR }, - { "penalty_factor", PENALTY_FACTOR }, - { "break_truce_prestige_factor", BREAK_TRUCE_PRESTIGE_FACTOR }, - { "break_truce_infamy_factor", BREAK_TRUCE_INFAMY_FACTOR }, - { "break_truce_militancy_factor", BREAK_TRUCE_MILITANCY_FACTOR }, - { "good_relation_prestige_factor", GOOD_RELATION_PRESTIGE_FACTOR }, - { "good_relation_infamy_factor", GOOD_RELATION_INFAMY_FACTOR }, - { "good_relation_militancy_factor", GOOD_RELATION_MILITANCY_FACTOR }, - { "tws_battle_factor", WAR_SCORE_BATTLE_FACTOR }, - { "construction_speed", CONSTRUCTION_SPEED } - }; - const decltype(peace_modifier_map)::const_iterator it = peace_modifier_map.find(key); - if (it != peace_modifier_map.end()) { - return expect_fixed_point( - [&modifiers, &identifier, &key, peace_modifier = it->second](fixed_point_t val) -> bool { - if (modifiers.emplace(peace_modifier, val).second) { - return true; - } - spdlog::error_s( - "Duplicate peace modifier {} in wargoal {}!", - key, identifier - ); - return false; - } - )(value); - } - - spdlog::error_s("Modifier {} in wargoal {} is invalid.", key, identifier); - return false; - }, - "war_name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(war_name)), - "months", ZERO_OR_ONE, expect_months(assign_variable_callback(available)), - "truce_months", ONE_EXACTLY, expect_months(assign_variable_callback(truce)), - "sprite_index", ONE_EXACTLY, expect_uint(assign_variable_callback(sprite_index)), - "is_triggered_only", ZERO_OR_ONE, expect_bool(assign_variable_callback(triggered_only)), - "is_civil_war", ZERO_OR_ONE, expect_bool(assign_variable_callback(civil_war)), - "constructing_cb", ZERO_OR_ONE, expect_bool(assign_variable_callback(constructing)), - "crisis", ZERO_OR_ONE, expect_bool(assign_variable_callback(crisis)), - "great_war_obligatory", ZERO_OR_ONE, expect_bool(assign_variable_callback(great_war_obligatory)), - "mutual", ZERO_OR_ONE, expect_bool(assign_variable_callback(mutual)), - /* START PEACE OPTIONS */ - "po_annex", ZERO_OR_ONE, expect_peace_option(PO_ANNEX), - "po_demand_state", ZERO_OR_ONE, expect_peace_option(PO_DEMAND_STATE), - "po_add_to_sphere", ZERO_OR_ONE, expect_peace_option(PO_ADD_TO_SPHERE), - "po_disarmament", ZERO_OR_ONE, expect_peace_option(PO_DISARMAMENT), - "po_destroy_forts", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_FORTS), - "po_destroy_naval_bases", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_NAVAL_BASES), - "po_reparations", ZERO_OR_ONE, expect_peace_option(PO_REPARATIONS), - "po_transfer_provinces", ZERO_OR_ONE, expect_peace_option(PO_TRANSFER_PROVINCES), - "po_remove_prestige", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_PRESTIGE), - "po_make_puppet", ZERO_OR_ONE, expect_peace_option(PO_MAKE_PUPPET), - "po_release_puppet", ZERO_OR_ONE, expect_peace_option(PO_RELEASE_PUPPET), - "po_status_quo", ZERO_OR_ONE, expect_peace_option(PO_STATUS_QUO), - "po_install_communist_gov_type", ZERO_OR_ONE, expect_peace_option(PO_INSTALL_COMMUNISM), - "po_uninstall_communist_gov_type", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_COMMUNISM), - "po_remove_cores", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_CORES), - "po_colony", ZERO_OR_ONE, expect_peace_option(PO_COLONY), - "po_gunboat", ZERO_OR_ONE, expect_peace_option(PO_REPAY_DEBT), - "po_clear_union_sphere", ZERO_OR_ONE, expect_peace_option(PO_CLEAR_UNION_SPHERE), - /* END PEACE OPTIONS */ - "can_use", ZERO_OR_ONE, can_use.expect_script(), - "is_valid", ZERO_OR_ONE, is_valid.expect_script(), - "on_add", ZERO_OR_ONE, on_add.expect_script(), - "on_po_accepted", ZERO_OR_ONE, on_po_accepted.expect_script(), - "allowed_states", ZERO_OR_ONE, allowed_states.expect_script(), - "all_allowed_states", ZERO_OR_ONE, expect_bool(assign_variable_callback(all_allowed_states)), - "allowed_substate_regions", ZERO_OR_ONE, allowed_substate_regions.expect_script(), - "allowed_states_in_crisis", ZERO_OR_ONE, allowed_states_in_crisis.expect_script(), - "allowed_countries", ZERO_OR_ONE, allowed_countries.expect_script(), - "always", ZERO_OR_ONE, expect_bool(assign_variable_callback(always)) - )(value); - - add_wargoal_type( - identifier, war_name, available, truce, sprite_index, triggered_only, civil_war, constructing, crisis, - great_war_obligatory, mutual, all_allowed_states, always, std::move(modifiers), peace_options, - std::move(can_use), std::move(is_valid), std::move(allowed_states), std::move(allowed_substate_regions), - std::move(allowed_states_in_crisis), std::move(allowed_countries), std::move(on_add), std::move(on_po_accepted) - ); - return ret; - } - )(parser.get_file_node()); - - /* load order in which CBs are prioritised by AI */ - ret &= expect_key( - peace_order_symbol, - expect_list( - expect_wargoal_type_identifier( - [this](WargoalType const& wargoal) -> bool { - if (!ranges::contains( - peace_priorities, - wargoal - )) { - peace_priorities.emplace_back(wargoal); - } else { - spdlog::warn_s("Wargoal {} is already in the peace priority list!", wargoal); - } - return true; - }, - true // warn instead of error - ) - ) - )(parser.get_file_node()); - - lock_wargoal_types(); - return ret; -} - -bool WargoalTypeManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - for (WargoalType& wargoal_type : wargoal_types.get_items()) { - ret &= wargoal_type.parse_scripts(definition_manager); - } - return ret; -} diff --git a/src/openvic-simulation/military/Wargoal.hpp b/src/openvic-simulation/military/Wargoal.hpp index daeb958a5..c57fc61e6 100644 --- a/src/openvic-simulation/military/Wargoal.hpp +++ b/src/openvic-simulation/military/Wargoal.hpp @@ -1,14 +1,10 @@ #pragma once -#include - -#include - #include "openvic-simulation/core/memory/String.hpp" #include "openvic-simulation/scripts/ConditionScript.hpp" #include "openvic-simulation/scripts/EffectScript.hpp" #include "openvic-simulation/core/template/EnumBitfield.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { @@ -99,25 +95,4 @@ namespace OpenVic { }; template<> struct enable_bitfield : std::true_type{}; - - struct WargoalTypeManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(wargoal_type); - memory::vector> SPAN_PROPERTY(peace_priorities); - - public: - bool add_wargoal_type( - std::string_view identifier, std::string_view war_name, Timespan available_length, - Timespan truce_length, WargoalType::sprite_t sprite_index, bool triggered_only, bool civil_war, - bool constructing, bool crisis, bool great_war_obligatory, bool mutual, bool all_allowed_states, - bool always, WargoalType::peace_modifiers_t&& modifiers, WargoalType::peace_options_t peace_options, - ConditionScript&& can_use, ConditionScript&& is_valid, ConditionScript&& allowed_states, - ConditionScript&& allowed_substate_regions, ConditionScript&& allowed_states_in_crisis, - ConditionScript&& allowed_countries, EffectScript&& on_add, EffectScript&& on_po_accepted - ); - - bool load_wargoal_file(ovdl::v2script::Parser const& parser); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/military/WargoalManager.cpp b/src/openvic-simulation/military/WargoalManager.cpp new file mode 100644 index 000000000..e769121d1 --- /dev/null +++ b/src/openvic-simulation/military/WargoalManager.cpp @@ -0,0 +1,207 @@ +#include "WargoalManager.hpp" + +#include + +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool WargoalTypeManager::add_wargoal_type( + std::string_view identifier, std::string_view war_name, Timespan available_length, + Timespan truce_length, WargoalType::sprite_t sprite_index, bool triggered_only, bool civil_war, + bool constructing, bool crisis, bool great_war_obligatory, bool mutual, bool all_allowed_states, + bool always, WargoalType::peace_modifiers_t&& modifiers, WargoalType::peace_options_t peace_options, + ConditionScript&& can_use, ConditionScript&& is_valid, ConditionScript&& allowed_states, + ConditionScript&& allowed_substate_regions, ConditionScript&& allowed_states_in_crisis, + ConditionScript&& allowed_countries, EffectScript&& on_add, EffectScript&& on_po_accepted +) { + if (identifier.empty()) { + spdlog::error_s("Invalid wargoal identifier - empty!"); + return false; + } + + if (war_name.empty()) { + spdlog::error_s("Invalid war name for wargoal {} - empty!", identifier); + return false; + } + + if (sprite_index == 0) { + spdlog::warn_s("Invalid sprite for wargoal {} - 0", identifier); + } + + return wargoal_types.emplace_item( + identifier, + identifier, war_name, available_length, truce_length, sprite_index, triggered_only, civil_war, constructing, crisis, + great_war_obligatory, mutual, all_allowed_states, always, std::move(modifiers), peace_options, std::move(can_use), + std::move(is_valid), std::move(allowed_states), std::move(allowed_substate_regions), + std::move(allowed_states_in_crisis), std::move(allowed_countries), std::move(on_add), std::move(on_po_accepted) + ); +} + +bool WargoalTypeManager::load_wargoal_file(ovdl::v2script::Parser const& parser) { + using namespace std::string_view_literals; + ovdl::symbol peace_order_symbol = parser.find_intern("peace_order"sv); + if (!peace_order_symbol) { + spdlog::error_s("peace_order could not be interned."); + } + + bool ret = expect_dictionary_reserve_length( + wargoal_types, + [this, &peace_order_symbol](std::string_view identifier, ovdl::v2script::ast::Node const* value) -> bool { + // If peace_order_symbol is false, we know there is no peace_order string in the parser + if (peace_order_symbol && identifier == peace_order_symbol.c_str()) { + return true; + } + + using enum WargoalType::peace_options_t; + using enum scope_type_t; + + std::string_view war_name; + Timespan available {}, truce {}; + WargoalType::sprite_t sprite_index = 0; + bool triggered_only = false, civil_war = false, constructing = true, crisis = true, great_war_obligatory = false, + mutual = false, all_allowed_states = false, always = false; + WargoalType::peace_options_t peace_options = NO_PEACE_OPTIONS; + WargoalType::peace_modifiers_t modifiers; + ConditionScript can_use { COUNTRY, COUNTRY, COUNTRY }; + ConditionScript is_valid { COUNTRY, COUNTRY, COUNTRY }; + ConditionScript allowed_states { STATE, COUNTRY, COUNTRY }; + ConditionScript allowed_substate_regions { STATE, COUNTRY, COUNTRY }; + ConditionScript allowed_states_in_crisis { STATE, COUNTRY, COUNTRY }; + ConditionScript allowed_countries { COUNTRY, COUNTRY, COUNTRY }; + EffectScript on_add, on_po_accepted; //country as default scope for both + + const auto expect_peace_option = [&peace_options](WargoalType::peace_options_t peace_option) -> NodeTools::node_callback_t { + return expect_bool([&peace_options, peace_option](bool val) -> bool { + if (val) { + peace_options |= peace_option; + } else { + peace_options &= ~peace_option; + } + return true; + }); + }; + + bool ret = expect_dictionary_keys_and_default( + [&modifiers, &identifier](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + using enum WargoalType::PEACE_MODIFIERS; + static const string_map_t peace_modifier_map { + { "badboy_factor", BADBOY_FACTOR }, + { "prestige_factor", PRESTIGE_FACTOR }, + { "peace_cost_factor", PEACE_COST_FACTOR }, + { "penalty_factor", PENALTY_FACTOR }, + { "break_truce_prestige_factor", BREAK_TRUCE_PRESTIGE_FACTOR }, + { "break_truce_infamy_factor", BREAK_TRUCE_INFAMY_FACTOR }, + { "break_truce_militancy_factor", BREAK_TRUCE_MILITANCY_FACTOR }, + { "good_relation_prestige_factor", GOOD_RELATION_PRESTIGE_FACTOR }, + { "good_relation_infamy_factor", GOOD_RELATION_INFAMY_FACTOR }, + { "good_relation_militancy_factor", GOOD_RELATION_MILITANCY_FACTOR }, + { "tws_battle_factor", WAR_SCORE_BATTLE_FACTOR }, + { "construction_speed", CONSTRUCTION_SPEED } + }; + const decltype(peace_modifier_map)::const_iterator it = peace_modifier_map.find(key); + if (it != peace_modifier_map.end()) { + return expect_fixed_point( + [&modifiers, &identifier, &key, peace_modifier = it->second](fixed_point_t val) -> bool { + if (modifiers.emplace(peace_modifier, val).second) { + return true; + } + spdlog::error_s( + "Duplicate peace modifier {} in wargoal {}!", + key, identifier + ); + return false; + } + )(value); + } + + spdlog::error_s("Modifier {} in wargoal {} is invalid.", key, identifier); + return false; + }, + "war_name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(war_name)), + "months", ZERO_OR_ONE, expect_months(assign_variable_callback(available)), + "truce_months", ONE_EXACTLY, expect_months(assign_variable_callback(truce)), + "sprite_index", ONE_EXACTLY, expect_uint(assign_variable_callback(sprite_index)), + "is_triggered_only", ZERO_OR_ONE, expect_bool(assign_variable_callback(triggered_only)), + "is_civil_war", ZERO_OR_ONE, expect_bool(assign_variable_callback(civil_war)), + "constructing_cb", ZERO_OR_ONE, expect_bool(assign_variable_callback(constructing)), + "crisis", ZERO_OR_ONE, expect_bool(assign_variable_callback(crisis)), + "great_war_obligatory", ZERO_OR_ONE, expect_bool(assign_variable_callback(great_war_obligatory)), + "mutual", ZERO_OR_ONE, expect_bool(assign_variable_callback(mutual)), + /* START PEACE OPTIONS */ + "po_annex", ZERO_OR_ONE, expect_peace_option(PO_ANNEX), + "po_demand_state", ZERO_OR_ONE, expect_peace_option(PO_DEMAND_STATE), + "po_add_to_sphere", ZERO_OR_ONE, expect_peace_option(PO_ADD_TO_SPHERE), + "po_disarmament", ZERO_OR_ONE, expect_peace_option(PO_DISARMAMENT), + "po_destroy_forts", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_FORTS), + "po_destroy_naval_bases", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_NAVAL_BASES), + "po_reparations", ZERO_OR_ONE, expect_peace_option(PO_REPARATIONS), + "po_transfer_provinces", ZERO_OR_ONE, expect_peace_option(PO_TRANSFER_PROVINCES), + "po_remove_prestige", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_PRESTIGE), + "po_make_puppet", ZERO_OR_ONE, expect_peace_option(PO_MAKE_PUPPET), + "po_release_puppet", ZERO_OR_ONE, expect_peace_option(PO_RELEASE_PUPPET), + "po_status_quo", ZERO_OR_ONE, expect_peace_option(PO_STATUS_QUO), + "po_install_communist_gov_type", ZERO_OR_ONE, expect_peace_option(PO_INSTALL_COMMUNISM), + "po_uninstall_communist_gov_type", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_COMMUNISM), + "po_remove_cores", ZERO_OR_ONE, expect_peace_option(PO_REMOVE_CORES), + "po_colony", ZERO_OR_ONE, expect_peace_option(PO_COLONY), + "po_gunboat", ZERO_OR_ONE, expect_peace_option(PO_REPAY_DEBT), + "po_clear_union_sphere", ZERO_OR_ONE, expect_peace_option(PO_CLEAR_UNION_SPHERE), + /* END PEACE OPTIONS */ + "can_use", ZERO_OR_ONE, can_use.expect_script(), + "is_valid", ZERO_OR_ONE, is_valid.expect_script(), + "on_add", ZERO_OR_ONE, on_add.expect_script(), + "on_po_accepted", ZERO_OR_ONE, on_po_accepted.expect_script(), + "allowed_states", ZERO_OR_ONE, allowed_states.expect_script(), + "all_allowed_states", ZERO_OR_ONE, expect_bool(assign_variable_callback(all_allowed_states)), + "allowed_substate_regions", ZERO_OR_ONE, allowed_substate_regions.expect_script(), + "allowed_states_in_crisis", ZERO_OR_ONE, allowed_states_in_crisis.expect_script(), + "allowed_countries", ZERO_OR_ONE, allowed_countries.expect_script(), + "always", ZERO_OR_ONE, expect_bool(assign_variable_callback(always)) + )(value); + + add_wargoal_type( + identifier, war_name, available, truce, sprite_index, triggered_only, civil_war, constructing, crisis, + great_war_obligatory, mutual, all_allowed_states, always, std::move(modifiers), peace_options, + std::move(can_use), std::move(is_valid), std::move(allowed_states), std::move(allowed_substate_regions), + std::move(allowed_states_in_crisis), std::move(allowed_countries), std::move(on_add), std::move(on_po_accepted) + ); + return ret; + } + )(parser.get_file_node()); + + /* load order in which CBs are prioritised by AI */ + ret &= expect_key( + peace_order_symbol, + expect_list( + expect_wargoal_type_identifier( + [this](WargoalType const& wargoal) -> bool { + if (!ranges::contains( + peace_priorities, + wargoal + )) { + peace_priorities.emplace_back(wargoal); + } else { + spdlog::warn_s("Wargoal {} is already in the peace priority list!", wargoal); + } + return true; + }, + true // warn instead of error + ) + ) + )(parser.get_file_node()); + + lock_wargoal_types(); + return ret; +} + +bool WargoalTypeManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (WargoalType& wargoal_type : wargoal_types.get_items()) { + ret &= wargoal_type.parse_scripts(definition_manager); + } + return ret; +} diff --git a/src/openvic-simulation/military/WargoalManager.hpp b/src/openvic-simulation/military/WargoalManager.hpp new file mode 100644 index 000000000..29af4867f --- /dev/null +++ b/src/openvic-simulation/military/WargoalManager.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" +#include "openvic-simulation/military/Wargoal.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/utility/Getters.hpp" + +namespace ovdl::v2script { + class Parser; +} + +namespace OpenVic { + struct DefinitionManager; + + struct WargoalTypeManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(wargoal_type); + memory::vector> SPAN_PROPERTY(peace_priorities); + + public: + bool add_wargoal_type( + std::string_view identifier, std::string_view war_name, Timespan available_length, + Timespan truce_length, WargoalType::sprite_t sprite_index, bool triggered_only, bool civil_war, + bool constructing, bool crisis, bool great_war_obligatory, bool mutual, bool all_allowed_states, + bool always, WargoalType::peace_modifiers_t&& modifiers, WargoalType::peace_options_t peace_options, + ConditionScript&& can_use, ConditionScript&& is_valid, ConditionScript&& allowed_states, + ConditionScript&& allowed_substate_regions, ConditionScript&& allowed_states_in_crisis, + ConditionScript&& allowed_countries, EffectScript&& on_add, EffectScript&& on_po_accepted + ); + + bool load_wargoal_file(ovdl::v2script::Parser const& parser); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/misc/Decision.cpp b/src/openvic-simulation/misc/Decision.cpp index c09d994cf..d616f486a 100644 --- a/src/openvic-simulation/misc/Decision.cpp +++ b/src/openvic-simulation/misc/Decision.cpp @@ -1,7 +1,7 @@ #include "Decision.hpp" + using namespace OpenVic; -using namespace OpenVic::NodeTools; Decision::Decision( std::string_view new_identifier, bool new_alert, bool new_news, std::string_view new_news_title, @@ -21,80 +21,3 @@ bool Decision::parse_scripts(DefinitionManager const& definition_manager) { ret &= effect.parse_script(false, definition_manager); return ret; } - -bool DecisionManager::add_decision( - std::string_view identifier, bool alert, bool news, std::string_view news_title, std::string_view news_desc_long, - std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture, ConditionScript&& potential, - ConditionScript&& allow, ConditionalWeightFactorMul&& ai_will_do, EffectScript&& effect -) { - if (identifier.empty()) { - spdlog::error_s("Invalid decision identifier - empty!"); - return false; - } - - if (news) { - if (news_desc_long.empty() || news_desc_medium.empty() || news_desc_short.empty()) { - spdlog::warn_s( - "Decision with ID {} is a news decision but doesn't have long, medium and short descriptions!", identifier - ); - } - } else { - if (!news_title.empty() || !news_desc_long.empty() || !news_desc_medium.empty() || !news_desc_short.empty()) { - spdlog::warn_s("Decision with ID {} is not a news decision but has news strings specified!", identifier); - } - } - - return decisions.emplace_item( - identifier, - duplicate_warning_callback, - identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture, std::move(potential), - std::move(allow), std::move(ai_will_do), std::move(effect) - ); -} - -bool DecisionManager::load_decision_file(ast::NodeCPtr root) { - return expect_dictionary_keys( - "political_decisions", ZERO_OR_ONE, expect_dictionary_reserve_length( - decisions, - [this](std::string_view identifier, ast::NodeCPtr node) -> bool { - using enum scope_type_t; - - bool alert = true, news = false; - std::string_view news_title, news_desc_long, news_desc_medium, news_desc_short, picture; - ConditionScript potential { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionScript allow { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionalWeightFactorMul ai_will_do { COUNTRY, COUNTRY, NO_SCOPE }; - EffectScript effect; - - bool ret = expect_dictionary_keys( - "alert", ZERO_OR_ONE, expect_bool(assign_variable_callback(alert)), - "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), - "news_title", ZERO_OR_ONE, expect_string(assign_variable_callback(news_title)), - "news_desc_long", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_long)), - "news_desc_medium", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_medium)), - "news_desc_short", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_short)), - "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(picture)), - "potential", ONE_EXACTLY, potential.expect_script(), - "allow", ONE_EXACTLY, allow.expect_script(), - "effect", ONE_EXACTLY, effect.expect_script(), - "ai_will_do", ZERO_OR_ONE, ai_will_do.expect_conditional_weight() - )(node); - - ret &= add_decision( - identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture, - std::move(potential), std::move(allow), std::move(ai_will_do), std::move(effect) - ); - - return ret; - } - ) - )(root); -} - -bool DecisionManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - for (Decision& decision : decisions.get_items()) { - ret &= decision.parse_scripts(definition_manager); - } - return ret; -} diff --git a/src/openvic-simulation/misc/Decision.hpp b/src/openvic-simulation/misc/Decision.hpp index c545cf0a4..159081463 100644 --- a/src/openvic-simulation/misc/Decision.hpp +++ b/src/openvic-simulation/misc/Decision.hpp @@ -2,7 +2,6 @@ #include "openvic-simulation/scripts/ConditionalWeight.hpp" #include "openvic-simulation/scripts/EffectScript.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { struct DecisionManager; @@ -35,21 +34,4 @@ namespace OpenVic { ); Decision(Decision&&) = default; }; - - struct DecisionManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(decision); - - public: - bool add_decision( - std::string_view identifier, bool alert, bool news, std::string_view news_title, std::string_view news_desc_long, - std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture, - ConditionScript&& potential, ConditionScript&& allow, ConditionalWeightFactorMul&& ai_will_do, - EffectScript&& effect - ); - - bool load_decision_file(ast::NodeCPtr root); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/misc/DecisionManager.cpp b/src/openvic-simulation/misc/DecisionManager.cpp new file mode 100644 index 000000000..c810bb735 --- /dev/null +++ b/src/openvic-simulation/misc/DecisionManager.cpp @@ -0,0 +1,83 @@ +#include "DecisionManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool DecisionManager::add_decision( + std::string_view identifier, bool alert, bool news, std::string_view news_title, std::string_view news_desc_long, + std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture, ConditionScript&& potential, + ConditionScript&& allow, ConditionalWeightFactorMul&& ai_will_do, EffectScript&& effect +) { + if (identifier.empty()) { + spdlog::error_s("Invalid decision identifier - empty!"); + return false; + } + + if (news) { + if (news_desc_long.empty() || news_desc_medium.empty() || news_desc_short.empty()) { + spdlog::warn_s( + "Decision with ID {} is a news decision but doesn't have long, medium and short descriptions!", identifier + ); + } + } else { + if (!news_title.empty() || !news_desc_long.empty() || !news_desc_medium.empty() || !news_desc_short.empty()) { + spdlog::warn_s("Decision with ID {} is not a news decision but has news strings specified!", identifier); + } + } + + return decisions.emplace_item( + identifier, + duplicate_warning_callback, + identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture, std::move(potential), + std::move(allow), std::move(ai_will_do), std::move(effect) + ); +} + +bool DecisionManager::load_decision_file(ovdl::v2script::ast::Node const* root) { + return expect_dictionary_keys( + "political_decisions", ZERO_OR_ONE, expect_dictionary_reserve_length( + decisions, + [this](std::string_view identifier, ovdl::v2script::ast::Node const* node) -> bool { + using enum scope_type_t; + + bool alert = true, news = false; + std::string_view news_title, news_desc_long, news_desc_medium, news_desc_short, picture; + ConditionScript potential { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionScript allow { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionalWeightFactorMul ai_will_do { COUNTRY, COUNTRY, NO_SCOPE }; + EffectScript effect; + + bool ret = expect_dictionary_keys( + "alert", ZERO_OR_ONE, expect_bool(assign_variable_callback(alert)), + "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), + "news_title", ZERO_OR_ONE, expect_string(assign_variable_callback(news_title)), + "news_desc_long", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_long)), + "news_desc_medium", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_medium)), + "news_desc_short", ZERO_OR_ONE, expect_string(assign_variable_callback(news_desc_short)), + "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(picture)), + "potential", ONE_EXACTLY, potential.expect_script(), + "allow", ONE_EXACTLY, allow.expect_script(), + "effect", ONE_EXACTLY, effect.expect_script(), + "ai_will_do", ZERO_OR_ONE, ai_will_do.expect_conditional_weight() + )(node); + + ret &= add_decision( + identifier, alert, news, news_title, news_desc_long, news_desc_medium, news_desc_short, picture, + std::move(potential), std::move(allow), std::move(ai_will_do), std::move(effect) + ); + + return ret; + } + ) + )(root); +} + +bool DecisionManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (Decision& decision : decisions.get_items()) { + ret &= decision.parse_scripts(definition_manager); + } + return ret; +} diff --git a/src/openvic-simulation/misc/DecisionManager.hpp b/src/openvic-simulation/misc/DecisionManager.hpp new file mode 100644 index 000000000..e65f6bbc9 --- /dev/null +++ b/src/openvic-simulation/misc/DecisionManager.hpp @@ -0,0 +1,25 @@ +#pragma once + +#include "openvic-simulation/misc/Decision.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct DecisionManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(decision); + + public: + bool add_decision( + std::string_view identifier, bool alert, bool news, std::string_view news_title, std::string_view news_desc_long, + std::string_view news_desc_medium, std::string_view news_desc_short, std::string_view picture, + ConditionScript&& potential, ConditionScript&& allow, ConditionalWeightFactorMul&& ai_will_do, + EffectScript&& effect + ); + + bool load_decision_file(ovdl::v2script::ast::Node const* root); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/misc/Event.cpp b/src/openvic-simulation/misc/Event.cpp index d54f8b7aa..9c493ef1b 100644 --- a/src/openvic-simulation/misc/Event.cpp +++ b/src/openvic-simulation/misc/Event.cpp @@ -1,15 +1,6 @@ #include "Event.hpp" -#include -#include - -#include - -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/politics/IssueManager.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; Event::EventOption::EventOption( std::string_view new_name, EffectScript&& new_effect, ConditionalWeightFactorMul&& new_ai_chance @@ -50,225 +41,3 @@ bool Event::parse_scripts(DefinitionManager const& definition_manager) { OnAction::OnAction(std::string_view new_identifier, weight_map_t&& new_weighted_events) : HasIdentifier { new_identifier }, weighted_events { std::move(new_weighted_events) } {} - -EventManager::EventManager() { - // Default total vanilla events is 1064 - events.reserve(1064); -} - -bool EventManager::register_event( - std::string_view identifier, std::string_view title, std::string_view description, std::string_view image, - Event::event_type_t type, bool triggered_only, bool major, bool fire_only_once, bool allows_multiple_instances, bool news, - std::string_view news_title, std::string_view news_desc_long, std::string_view news_desc_medium, - std::string_view news_desc_short, bool election, const issue_group_t election_issue_group, ConditionScript&& trigger, - ConditionalWeightTime&& mean_time_to_happen, EffectScript&& immediate, memory::vector&& options -) { - if (identifier.empty()) { - spdlog::error_s("Invalid event ID - empty!"); - return false; - } - if (title.empty()) { - spdlog::warn_s("Event with ID {} has no title!", identifier); - } - if (description.empty()) { - spdlog::warn_s("Event with ID {} has no description!", identifier); - } - if (options.empty()) { - spdlog::error_s("No options specified for event with ID {}", identifier); - return false; - } - if (election && std::holds_alternative(election_issue_group)) { - spdlog::warn_s("Event with ID {} is an election event but has no issue group!", identifier); - } else if (!election) { - std::visit([identifier](auto&& arg) { - using T = std::decay_t; // Get the clean type - - if constexpr (std::is_same_v>) { - spdlog::warn_s( - "Event with ID {} is not an election event but has party policy group {}!", - identifier, arg - ); - } else if constexpr (std::is_same_v>) { - spdlog::warn_s( - "Event with ID {} is not an election event but has reform group {}!", - identifier, arg - ); - } else if constexpr (!std::is_same_v){ - spdlog::error_s( - "Event with ID {} is not an election event but has unknown issue group {}!", - identifier, arg - ); - } - }, election_issue_group); - } - if (news) { - if (news_desc_long.empty() || news_desc_medium.empty() || news_desc_short.empty()) { - spdlog::warn_s( - "Event with ID {} is a news event but doesn't have long, medium and short descriptions!", identifier - ); - } - } else { - if (!news_title.empty() || !news_desc_long.empty() || !news_desc_medium.empty() || !news_desc_short.empty()) { - spdlog::warn_s("Event with ID {} is not a news event but has news strings specified!", identifier); - } - } - - for (Event::EventOption const& option : options) { - if (option.name.empty()) { - spdlog::warn_s("Event with ID {} has an option with no name!", identifier); - } - } - - // TODO - error if is_triggered_only with triggers or MTTH defined - - return events.emplace_item( - identifier, - duplicate_warning_callback, - identifier, title, description, image, type, triggered_only, major, fire_only_once, allows_multiple_instances, news, - news_title, news_desc_long, news_desc_medium, news_desc_short, election, election_issue_group, std::move(trigger), - std::move(mean_time_to_happen), std::move(immediate), std::move(options) - ); -} - -bool EventManager::add_on_action(std::string_view identifier, OnAction::weight_map_t&& weighted_events) { - if (identifier.empty()) { - spdlog::error_s("Invalid on_action identifier - empty!"); - return false; - } - - return on_actions.emplace_item( - identifier, - identifier, std::move(weighted_events) - ); -} - -bool EventManager::load_event_file(IssueManager const& issue_manager, ast::NodeCPtr root) { - return expect_dictionary_reserve_length( - events, - [this, &issue_manager](std::string_view key, ast::NodeCPtr value) -> bool { - using enum scope_type_t; - - Event::event_type_t type; - scope_type_t initial_scope; - - if (key == "country_event") { - type = Event::event_type_t::COUNTRY; - initial_scope = COUNTRY; - } else if (key == "province_event") { - type = Event::event_type_t::PROVINCE; - initial_scope = PROVINCE; - } else { - spdlog::error_s("Invalid event type: {}", key); - return false; - } - - std::string_view identifier, title, description, image, news_title, news_desc_long, news_desc_medium, - news_desc_short; - bool triggered_only = false, major = false, fire_only_once = false, allows_multiple_instances = false, - news = false, election = false; - issue_group_t election_issue_group; - ConditionScript trigger { initial_scope, COUNTRY, NO_SCOPE }; - ConditionalWeightTime mean_time_to_happen { initial_scope, COUNTRY, NO_SCOPE }; - EffectScript immediate; - - // Ensures that if ever multithreaded, only one vector is used per thread - // Else acts like static - thread_local memory::vector options; - // Default max vanilla options is 5 - options.reserve(2); - - bool ret = expect_dictionary_keys( - "id", ONE_EXACTLY, expect_identifier(assign_variable_callback(identifier)), - "title", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(title)), - "desc", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(description)), - "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(image), true), - "is_triggered_only", ZERO_OR_ONE, expect_bool(assign_variable_callback(triggered_only)), - "major", ZERO_OR_ONE, expect_bool(assign_variable_callback(major)), - "fire_only_once", ZERO_OR_ONE, expect_bool(assign_variable_callback(fire_only_once)), - "allow_multiple_instances", ZERO_OR_ONE, expect_bool(assign_variable_callback(allows_multiple_instances)), - "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), - "news_title", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_title)), - "news_desc_long", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_desc_long)), - "news_desc_medium", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_desc_medium)), - "news_desc_short", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_desc_short)), - "election", ZERO_OR_ONE, expect_bool(assign_variable_callback(election)), - "issue_group", ZERO_OR_ONE, issue_manager.expect_base_issue_group_identifier(assign_variable_callback(election_issue_group)), - "option", ONE_OR_MORE, [initial_scope](ast::NodeCPtr node) -> bool { - std::string_view name; - EffectScript effect; - ConditionalWeightFactorMul ai_chance { - initial_scope, - COUNTRY, - COUNTRY - }; - - bool ret = expect_dictionary_keys_and_default( - key_value_success_callback, /* Option effects, passed to the EffectScript below */ - "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(name)), - "ai_chance", ZERO_OR_ONE, ai_chance.expect_conditional_weight() - )(node); - - ret &= effect.expect_script()(node); - - options.emplace_back(name, std::move(effect), std::move(ai_chance)); - return ret; - }, - "trigger", ZERO_OR_ONE, trigger.expect_script(), - "mean_time_to_happen", ZERO_OR_ONE, mean_time_to_happen.expect_conditional_weight(), - "immediate", ZERO_OR_MORE, immediate.expect_script() - )(value); - - ret &= register_event( - identifier, title, description, image, type, triggered_only, major, fire_only_once, allows_multiple_instances, - news, news_title, news_desc_long, news_desc_medium, news_desc_short, election, election_issue_group, - std::move(trigger), std::move(mean_time_to_happen), std::move(immediate), std::move(options) - ); - return ret; - } - )(root); -} - -bool EventManager::load_on_action_file(ast::NodeCPtr root) { - bool ret = expect_dictionary([this](std::string_view identifier, ast::NodeCPtr node) -> bool { - OnAction::weight_map_t weighted_events; - bool ret = expect_dictionary_reserve_length( - weighted_events, - [this, &identifier, &weighted_events](std::string_view weight_str, ast::NodeCPtr event_node) -> bool { - bool ret = false; - uint64_t weight; - std::from_chars_result result = string_to_uint64(weight_str, weight); - ret = result.ec == std::errc{}; - if (!ret) { - spdlog::error_s("Invalid weight {} on action {}", weight_str, identifier); - return ret; - } - - Event const* event = nullptr; - ret &= expect_event_identifier(assign_variable_callback_pointer(event))(event_node); - - if (event != nullptr) { - ret &= map_callback(weighted_events, event)(weight); - } else if (ast::FlatValue const* event_name = dryad::node_try_cast(event_node); event_name && event_name->value()) { - spdlog::warn_s( - "Non-existing event {} loaded on action {} with weight {}!", - event_name->value().view(), identifier, weight - ); - } - - return ret; - } - )(node); - ret &= add_on_action(identifier, std::move(weighted_events)); - return ret; - })(root); - on_actions.lock(); - return ret; -} - -bool EventManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - for (Event& event : events.get_items()) { - ret &= event.parse_scripts(definition_manager); - } - return ret; -} diff --git a/src/openvic-simulation/misc/Event.hpp b/src/openvic-simulation/misc/Event.hpp index c909f8e54..777f044b1 100644 --- a/src/openvic-simulation/misc/Event.hpp +++ b/src/openvic-simulation/misc/Event.hpp @@ -5,12 +5,10 @@ #include "openvic-simulation/politics/IssueGroup.hpp" #include "openvic-simulation/scripts/ConditionalWeight.hpp" #include "openvic-simulation/scripts/EffectScript.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" namespace OpenVic { struct EventManager; - struct IssueManager; struct Event : HasIdentifier { friend struct EventManager; @@ -86,28 +84,4 @@ namespace OpenVic { OnAction(std::string_view new_identifier, weight_map_t&& new_weighted_events); OnAction(OnAction&&) = default; }; - - struct EventManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(event); - IdentifierRegistry IDENTIFIER_REGISTRY(on_action); - - public: - EventManager(); - - bool register_event( - std::string_view identifier, std::string_view title, std::string_view description, std::string_view image, - Event::event_type_t type, bool triggered_only, bool major, bool fire_only_once, bool allows_multiple_instances, - bool news, std::string_view news_title, std::string_view news_desc_long, std::string_view news_desc_medium, - std::string_view news_desc_short, bool election, const issue_group_t election_issue_group, ConditionScript&& trigger, - ConditionalWeightTime&& mean_time_to_happen, EffectScript&& immediate, memory::vector&& options - ); - - bool add_on_action(std::string_view identifier, OnAction::weight_map_t&& new_weighted_events); - - bool load_event_file(IssueManager const& issue_manager, ast::NodeCPtr root); - bool load_on_action_file(ast::NodeCPtr root); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/misc/EventManager.cpp b/src/openvic-simulation/misc/EventManager.cpp new file mode 100644 index 000000000..5feb60bfa --- /dev/null +++ b/src/openvic-simulation/misc/EventManager.cpp @@ -0,0 +1,237 @@ +#include "EventManager.hpp" + +#include +#include + +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/politics/IssueManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +EventManager::EventManager() { + // Default total vanilla events is 1064 + events.reserve(1064); +} + +bool EventManager::register_event( + std::string_view identifier, std::string_view title, std::string_view description, std::string_view image, + Event::event_type_t type, bool triggered_only, bool major, bool fire_only_once, bool allows_multiple_instances, bool news, + std::string_view news_title, std::string_view news_desc_long, std::string_view news_desc_medium, + std::string_view news_desc_short, bool election, const issue_group_t election_issue_group, ConditionScript&& trigger, + ConditionalWeightTime&& mean_time_to_happen, EffectScript&& immediate, memory::vector&& options +) { + if (identifier.empty()) { + spdlog::error_s("Invalid event ID - empty!"); + return false; + } + if (title.empty()) { + spdlog::warn_s("Event with ID {} has no title!", identifier); + } + if (description.empty()) { + spdlog::warn_s("Event with ID {} has no description!", identifier); + } + if (options.empty()) { + spdlog::error_s("No options specified for event with ID {}", identifier); + return false; + } + if (election && std::holds_alternative(election_issue_group)) { + spdlog::warn_s("Event with ID {} is an election event but has no issue group!", identifier); + } else if (!election) { + std::visit([identifier](auto&& arg) { + using T = std::decay_t; // Get the clean type + + if constexpr (std::is_same_v>) { + spdlog::warn_s( + "Event with ID {} is not an election event but has party policy group {}!", + identifier, arg + ); + } else if constexpr (std::is_same_v>) { + spdlog::warn_s( + "Event with ID {} is not an election event but has reform group {}!", + identifier, arg + ); + } else if constexpr (!std::is_same_v){ + spdlog::error_s( + "Event with ID {} is not an election event but has unknown issue group {}!", + identifier, arg + ); + } + }, election_issue_group); + } + if (news) { + if (news_desc_long.empty() || news_desc_medium.empty() || news_desc_short.empty()) { + spdlog::warn_s( + "Event with ID {} is a news event but doesn't have long, medium and short descriptions!", identifier + ); + } + } else { + if (!news_title.empty() || !news_desc_long.empty() || !news_desc_medium.empty() || !news_desc_short.empty()) { + spdlog::warn_s("Event with ID {} is not a news event but has news strings specified!", identifier); + } + } + + for (Event::EventOption const& option : options) { + if (option.name.empty()) { + spdlog::warn_s("Event with ID {} has an option with no name!", identifier); + } + } + + // TODO - error if is_triggered_only with triggers or MTTH defined + + return events.emplace_item( + identifier, + duplicate_warning_callback, + identifier, title, description, image, type, triggered_only, major, fire_only_once, allows_multiple_instances, news, + news_title, news_desc_long, news_desc_medium, news_desc_short, election, election_issue_group, std::move(trigger), + std::move(mean_time_to_happen), std::move(immediate), std::move(options) + ); +} + +bool EventManager::add_on_action(std::string_view identifier, OnAction::weight_map_t&& weighted_events) { + if (identifier.empty()) { + spdlog::error_s("Invalid on_action identifier - empty!"); + return false; + } + + return on_actions.emplace_item( + identifier, + identifier, std::move(weighted_events) + ); +} + +bool EventManager::load_event_file(IssueManager const& issue_manager, ovdl::v2script::ast::Node const* root) { + return expect_dictionary_reserve_length( + events, + [this, &issue_manager](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + using enum scope_type_t; + + Event::event_type_t type; + scope_type_t initial_scope; + + if (key == "country_event") { + type = Event::event_type_t::COUNTRY; + initial_scope = COUNTRY; + } else if (key == "province_event") { + type = Event::event_type_t::PROVINCE; + initial_scope = PROVINCE; + } else { + spdlog::error_s("Invalid event type: {}", key); + return false; + } + + std::string_view identifier, title, description, image, news_title, news_desc_long, news_desc_medium, + news_desc_short; + bool triggered_only = false, major = false, fire_only_once = false, allows_multiple_instances = false, + news = false, election = false; + issue_group_t election_issue_group; + ConditionScript trigger { initial_scope, COUNTRY, NO_SCOPE }; + ConditionalWeightTime mean_time_to_happen { initial_scope, COUNTRY, NO_SCOPE }; + EffectScript immediate; + + // Ensures that if ever multithreaded, only one vector is used per thread + // Else acts like static + thread_local memory::vector options; + // Default max vanilla options is 5 + options.reserve(2); + + bool ret = expect_dictionary_keys( + "id", ONE_EXACTLY, expect_identifier(assign_variable_callback(identifier)), + "title", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(title)), + "desc", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(description)), + "picture", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(image), true), + "is_triggered_only", ZERO_OR_ONE, expect_bool(assign_variable_callback(triggered_only)), + "major", ZERO_OR_ONE, expect_bool(assign_variable_callback(major)), + "fire_only_once", ZERO_OR_ONE, expect_bool(assign_variable_callback(fire_only_once)), + "allow_multiple_instances", ZERO_OR_ONE, expect_bool(assign_variable_callback(allows_multiple_instances)), + "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), + "news_title", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_title)), + "news_desc_long", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_desc_long)), + "news_desc_medium", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_desc_medium)), + "news_desc_short", ZERO_OR_ONE, expect_identifier_or_string(assign_variable_callback(news_desc_short)), + "election", ZERO_OR_ONE, expect_bool(assign_variable_callback(election)), + "issue_group", ZERO_OR_ONE, issue_manager.expect_base_issue_group_identifier(assign_variable_callback(election_issue_group)), + "option", ONE_OR_MORE, [initial_scope](ovdl::v2script::ast::Node const* node) -> bool { + std::string_view name; + EffectScript effect; + ConditionalWeightFactorMul ai_chance { + initial_scope, + COUNTRY, + COUNTRY + }; + + bool ret = expect_dictionary_keys_and_default( + key_value_success_callback, /* Option effects, passed to the EffectScript below */ + "name", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(name)), + "ai_chance", ZERO_OR_ONE, ai_chance.expect_conditional_weight() + )(node); + + ret &= effect.expect_script()(node); + + options.emplace_back(name, std::move(effect), std::move(ai_chance)); + return ret; + }, + "trigger", ZERO_OR_ONE, trigger.expect_script(), + "mean_time_to_happen", ZERO_OR_ONE, mean_time_to_happen.expect_conditional_weight(), + "immediate", ZERO_OR_MORE, immediate.expect_script() + )(value); + + ret &= register_event( + identifier, title, description, image, type, triggered_only, major, fire_only_once, allows_multiple_instances, + news, news_title, news_desc_long, news_desc_medium, news_desc_short, election, election_issue_group, + std::move(trigger), std::move(mean_time_to_happen), std::move(immediate), std::move(options) + ); + return ret; + } + )(root); +} + +bool EventManager::load_on_action_file(ovdl::v2script::ast::Node const* root) { + bool ret = expect_dictionary([this](std::string_view identifier, ovdl::v2script::ast::Node const* node) -> bool { + OnAction::weight_map_t weighted_events; + bool ret = expect_dictionary_reserve_length( + weighted_events, + [this, &identifier, &weighted_events](std::string_view weight_str, ovdl::v2script::ast::Node const* event_node) -> bool { + bool ret = false; + uint64_t weight; + std::from_chars_result result = string_to_uint64(weight_str, weight); + ret = result.ec == std::errc{}; + if (!ret) { + spdlog::error_s("Invalid weight {} on action {}", weight_str, identifier); + return ret; + } + + Event const* event = nullptr; + ret &= expect_event_identifier(assign_variable_callback_pointer(event))(event_node); + + if (event != nullptr) { + ret &= map_callback(weighted_events, event)(weight); + } else if ( + ovdl::v2script::ast::FlatValue const* event_name = dryad::node_try_cast(event_node); + event_name && event_name->value() + ) { + spdlog::warn_s( + "Non-existing event {} loaded on action {} with weight {}!", + event_name->value().view(), identifier, weight + ); + } + + return ret; + } + )(node); + ret &= add_on_action(identifier, std::move(weighted_events)); + return ret; + })(root); + on_actions.lock(); + return ret; +} + +bool EventManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (Event& event : events.get_items()) { + ret &= event.parse_scripts(definition_manager); + } + return ret; +} diff --git a/src/openvic-simulation/misc/EventManager.hpp b/src/openvic-simulation/misc/EventManager.hpp new file mode 100644 index 000000000..47d66d164 --- /dev/null +++ b/src/openvic-simulation/misc/EventManager.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/misc/Event.hpp" +#include "openvic-simulation/politics/IssueGroup.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct IssueManager; + + struct EventManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(event); + IdentifierRegistry IDENTIFIER_REGISTRY(on_action); + + public: + EventManager(); + + bool register_event( + std::string_view identifier, std::string_view title, std::string_view description, std::string_view image, + Event::event_type_t type, bool triggered_only, bool major, bool fire_only_once, bool allows_multiple_instances, + bool news, std::string_view news_title, std::string_view news_desc_long, std::string_view news_desc_medium, + std::string_view news_desc_short, bool election, const issue_group_t election_issue_group, ConditionScript&& trigger, + ConditionalWeightTime&& mean_time_to_happen, EffectScript&& immediate, memory::vector&& options + ); + + bool add_on_action(std::string_view identifier, OnAction::weight_map_t&& new_weighted_events); + + bool load_event_file(IssueManager const& issue_manager, ovdl::v2script::ast::Node const* root); + bool load_on_action_file(ovdl::v2script::ast::Node const* root); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/misc/SongChance.cpp b/src/openvic-simulation/misc/SongChance.cpp index 3ce39ee58..19093242c 100644 --- a/src/openvic-simulation/misc/SongChance.cpp +++ b/src/openvic-simulation/misc/SongChance.cpp @@ -1,7 +1,6 @@ #include "SongChance.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; SongChance::SongChance( size_t new_index, std::string_view new_filename, ConditionalWeightFactorMul&& new_chance @@ -10,47 +9,3 @@ SongChance::SongChance( bool SongChance::parse_scripts(DefinitionManager const& definition_manager) { return chance.parse_scripts(definition_manager); } - -bool SongChanceManager::load_songs_file(ast::NodeCPtr root) { - bool ret = true; - - ret &= expect_dictionary_reserve_length( - song_chances, - [this](std::string_view key, ast::NodeCPtr value) -> bool { - using enum scope_type_t; - - if (key != "song") { - spdlog::error_s("Invalid song declaration {}", key); - return false; - } - std::string_view name {}; - ConditionalWeightFactorMul chance { COUNTRY, COUNTRY, NO_SCOPE }; - - bool ret = expect_dictionary_keys( - "name", ONE_EXACTLY, expect_string(assign_variable_callback(name)), - "chance", ONE_EXACTLY, chance.expect_conditional_weight() - )(value); - - ret &= song_chances.emplace_item( - name, - song_chances.size(), name, std::move(chance) - ); - return ret; - } - )(root); - - if (song_chances.size() == 0) { - spdlog::error_s("No songs found in Songs.txt"); - return false; - } - - return ret; -} - -bool SongChanceManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - for (SongChance& songChance : song_chances.get_items()) { - ret &= songChance.parse_scripts(definition_manager); - } - return ret; -} diff --git a/src/openvic-simulation/misc/SongChance.hpp b/src/openvic-simulation/misc/SongChance.hpp index 818dc3244..45c56e3cb 100644 --- a/src/openvic-simulation/misc/SongChance.hpp +++ b/src/openvic-simulation/misc/SongChance.hpp @@ -1,6 +1,5 @@ #pragma once -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/scripts/ConditionalWeight.hpp" namespace OpenVic { @@ -20,14 +19,5 @@ namespace OpenVic { SongChance(size_t new_index, std::string_view new_filename, ConditionalWeightFactorMul&& new_chance); SongChance(SongChance&&) = default; }; - - struct SongChanceManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(song_chance); - //Songs.txt - public: - bool load_songs_file(ast::NodeCPtr root); - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/misc/SongChanceManager.cpp b/src/openvic-simulation/misc/SongChanceManager.cpp new file mode 100644 index 000000000..742a15d02 --- /dev/null +++ b/src/openvic-simulation/misc/SongChanceManager.cpp @@ -0,0 +1,50 @@ +#include "SongChanceManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool SongChanceManager::load_songs_file(ovdl::v2script::ast::Node const* root) { + bool ret = true; + + ret &= expect_dictionary_reserve_length( + song_chances, + [this](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + using enum scope_type_t; + + if (key != "song") { + spdlog::error_s("Invalid song declaration {}", key); + return false; + } + std::string_view name {}; + ConditionalWeightFactorMul chance { COUNTRY, COUNTRY, NO_SCOPE }; + + bool ret = expect_dictionary_keys( + "name", ONE_EXACTLY, expect_string(assign_variable_callback(name)), + "chance", ONE_EXACTLY, chance.expect_conditional_weight() + )(value); + + ret &= song_chances.emplace_item( + name, + song_chances.size(), name, std::move(chance) + ); + return ret; + } + )(root); + + if (song_chances.size() == 0) { + spdlog::error_s("No songs found in Songs.txt"); + return false; + } + + return ret; +} + +bool SongChanceManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (SongChance& songChance : song_chances.get_items()) { + ret &= songChance.parse_scripts(definition_manager); + } + return ret; +} diff --git a/src/openvic-simulation/misc/SongChanceManager.hpp b/src/openvic-simulation/misc/SongChanceManager.hpp new file mode 100644 index 000000000..6b2b36535 --- /dev/null +++ b/src/openvic-simulation/misc/SongChanceManager.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include "openvic-simulation/misc/SongChance.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct SongChanceManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(song_chance); + //Songs.txt + public: + bool load_songs_file(ovdl::v2script::ast::Node const* root); + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/misc/SoundEffect.cpp b/src/openvic-simulation/misc/SoundEffect.cpp index 968cc1319..db3bb21db 100644 --- a/src/openvic-simulation/misc/SoundEffect.cpp +++ b/src/openvic-simulation/misc/SoundEffect.cpp @@ -1,51 +1,7 @@ #include "SoundEffect.hpp" -#include "openvic-simulation/dataloader/Dataloader.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; SoundEffect::SoundEffect( // std::string_view new_identifier, std::filesystem::path&& new_file, fixed_point_t new_volume -) - : HasIdentifier { new_identifier }, file { std::move(new_file) }, volume { new_volume } {} - -bool SoundEffectManager::_load_sound_define(Dataloader const& dataloader, std::string_view sfx_identifier, ast::NodeCPtr root) { - std::filesystem::path file {}; - auto file_callback = [&dataloader, &file](std::string_view val) -> bool { - memory::string lookup = memory::fmt::format("sound/{}", val); - file = dataloader.lookup_file(lookup, false); - if (file.empty()) { - spdlog::warn_s("Lookup for \"{}\" failed!", lookup); - } - return true; - }; - - fixed_point_t volume = 1; - bool ret = expect_dictionary_keys( - "file", ONE_EXACTLY, expect_string(file_callback), // - "volume", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(volume)) // - )(root); - - if (sfx_identifier.empty()) { - spdlog::error_s("Invalid sound identifier - empty!"); - return false; - } - if (file.empty()) { - spdlog::warn_s("Sound filename {} was empty!", sfx_identifier); - } - - ret &= sound_effects.emplace_item( - sfx_identifier, - sfx_identifier, std::move(file), volume - ); - return ret; -} - -bool SoundEffectManager::load_sound_defines_file(Dataloader const& dataloader, ast::NodeCPtr root) { - return expect_dictionary_reserve_length(sound_effects, // - [this, &dataloader](std::string_view key, ast::NodeCPtr value) -> bool { - return _load_sound_define(dataloader, key, value); - } - )(root); -} +) : HasIdentifier { new_identifier }, file { std::move(new_file) }, volume { new_volume } {} diff --git a/src/openvic-simulation/misc/SoundEffect.hpp b/src/openvic-simulation/misc/SoundEffect.hpp index 628090b98..dc3bef6d9 100644 --- a/src/openvic-simulation/misc/SoundEffect.hpp +++ b/src/openvic-simulation/misc/SoundEffect.hpp @@ -2,7 +2,6 @@ #include -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" @@ -18,12 +17,4 @@ namespace OpenVic { SoundEffect(std::string_view new_identifier, std::filesystem::path&& new_file, fixed_point_t new_volume); SoundEffect(SoundEffect&&) = default; }; - - class SoundEffectManager { - IdentifierRegistry IDENTIFIER_REGISTRY(sound_effect); - bool _load_sound_define(Dataloader const& dataloader, std::string_view sfx_identifier, ast::NodeCPtr root); - - public: - bool load_sound_defines_file(Dataloader const& dataloader, ast::NodeCPtr root); - }; } diff --git a/src/openvic-simulation/misc/SoundEffectManager.cpp b/src/openvic-simulation/misc/SoundEffectManager.cpp new file mode 100644 index 000000000..4194cf2d6 --- /dev/null +++ b/src/openvic-simulation/misc/SoundEffectManager.cpp @@ -0,0 +1,47 @@ +#include "SoundEffectManager.hpp" + +#include "openvic-simulation/dataloader/Dataloader.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool SoundEffectManager::_load_sound_define(Dataloader const& dataloader, std::string_view sfx_identifier, ovdl::v2script::ast::Node const* root) { + std::filesystem::path file {}; + auto file_callback = [&dataloader, &file](std::string_view val) -> bool { + memory::string lookup = memory::fmt::format("sound/{}", val); + file = dataloader.lookup_file(lookup, false); + if (file.empty()) { + spdlog::warn_s("Lookup for \"{}\" failed!", lookup); + } + return true; + }; + + fixed_point_t volume = 1; + bool ret = expect_dictionary_keys( + "file", ONE_EXACTLY, expect_string(file_callback), // + "volume", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(volume)) // + )(root); + + if (sfx_identifier.empty()) { + spdlog::error_s("Invalid sound identifier - empty!"); + return false; + } + if (file.empty()) { + spdlog::warn_s("Sound filename {} was empty!", sfx_identifier); + } + + ret &= sound_effects.emplace_item( + sfx_identifier, + sfx_identifier, std::move(file), volume + ); + return ret; +} + +bool SoundEffectManager::load_sound_defines_file(Dataloader const& dataloader, ovdl::v2script::ast::Node const* root) { + return expect_dictionary_reserve_length(sound_effects, // + [this, &dataloader](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + return _load_sound_define(dataloader, key, value); + } + )(root); +} diff --git a/src/openvic-simulation/misc/SoundEffectManager.hpp b/src/openvic-simulation/misc/SoundEffectManager.hpp new file mode 100644 index 000000000..13b11c5db --- /dev/null +++ b/src/openvic-simulation/misc/SoundEffectManager.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "openvic-simulation/misc/SoundEffect.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + class Dataloader; + + class SoundEffectManager { + IdentifierRegistry IDENTIFIER_REGISTRY(sound_effect); + bool _load_sound_define(Dataloader const& dataloader, std::string_view sfx_identifier, ovdl::v2script::ast::Node const* root); + + public: + bool load_sound_defines_file(Dataloader const& dataloader, ovdl::v2script::ast::Node const* root); + }; +} diff --git a/src/openvic-simulation/modifier/ModifierManager.cpp b/src/openvic-simulation/modifier/ModifierManager.cpp index 5a881cbe3..c801706a7 100644 --- a/src/openvic-simulation/modifier/ModifierManager.cpp +++ b/src/openvic-simulation/modifier/ModifierManager.cpp @@ -677,10 +677,10 @@ bool ModifierManager::add_event_modifier( ); } -bool ModifierManager::load_event_modifiers(const ast::NodeCPtr root) { +bool ModifierManager::load_event_modifiers(ovdl::v2script::ast::Node const* const root) { const bool ret = expect_dictionary_reserve_length( event_modifiers, - [this](std::string_view key, ast::NodeCPtr value) -> bool { + [this](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { ModifierValue modifier_value; IconModifier::icon_t icon = 0; @@ -698,7 +698,7 @@ bool ModifierManager::load_event_modifiers(const ast::NodeCPtr root) { return ret; } -bool ModifierManager::load_static_modifiers(const ast::NodeCPtr root) { +bool ModifierManager::load_static_modifiers(ovdl::v2script::ast::Node const* const root) { return static_modifier_cache.load_static_modifiers(*this, root); } @@ -722,10 +722,10 @@ bool ModifierManager::add_triggered_modifier( ); } -bool ModifierManager::load_triggered_modifiers(const ast::NodeCPtr root) { +bool ModifierManager::load_triggered_modifiers(ovdl::v2script::ast::Node const* const root) { const bool ret = expect_dictionary_reserve_length( triggered_modifiers, - [this](const std::string_view key, const ast::NodeCPtr value) -> bool { + [this](const std::string_view key, ovdl::v2script::ast::Node const* const value) -> bool { using enum scope_type_t; ModifierValue modifier_value {}; @@ -763,7 +763,7 @@ bool ModifierManager::_add_flattened_modifier_cb( ModifierValue& modifier_value, const std::string_view prefix, const std::string_view key, - const ast::NodeCPtr value + ovdl::v2script::ast::Node const* const value ) const { const memory::string flat_identifier = get_flat_identifier(prefix, key); ModifierEffect const* effect = technology_modifier_effects.get_item_by_identifier(flat_identifier); @@ -778,7 +778,7 @@ bool ModifierManager::_add_flattened_modifier_cb( bool ModifierManager::_add_modifier_cb( ModifierValue& modifier_value, ModifierEffect const* const effect, - const ast::NodeCPtr value + ovdl::v2script::ast::Node const* const value ) const { if (effect->has_no_effect) { spdlog::warn_s("This modifier does nothing: {}", *effect); @@ -798,10 +798,10 @@ key_value_callback_t ModifierManager::_expect_modifier_effect_with_fallback( ModifierValue& modifier_value, key_value_callback_t fallback ) const { - return [this, ®istry, &modifier_value, fallback](const std::string_view key, const ast::NodeCPtr value) mutable -> bool { - if (dryad::node_has_kind(value) && complex_modifiers.contains(key)) { + return [this, ®istry, &modifier_value, fallback](const std::string_view key, ovdl::v2script::ast::Node const* const value) mutable -> bool { + if (dryad::node_has_kind(value) && complex_modifiers.contains(key)) { return expect_dictionary([this, &modifier_value, key]( - const std::string_view inner_key, const ast::NodeCPtr inner_value + const std::string_view inner_key, ovdl::v2script::ast::Node const* const inner_value ) -> bool { return _add_flattened_modifier_cb(modifier_value, key, inner_key, inner_value); })(value); @@ -820,10 +820,10 @@ key_value_callback_t ModifierManager::expect_leader_modifier(ModifierValue& modi } key_value_callback_t ModifierManager::expect_technology_modifier(ModifierValue& modifier_value) const { - return [this, &modifier_value](const std::string_view key, const ast::NodeCPtr value) { + return [this, &modifier_value](const std::string_view key, ovdl::v2script::ast::Node const* const value) { if (ascii_equal_case_insensitive(key, "rebel_org_gain")) { // because of course there's a special one std::string_view faction_identifier; - ast::NodeCPtr value_node = nullptr; + ovdl::v2script::ast::Node const* value_node = nullptr; bool ret = expect_dictionary_keys( "faction", ONE_EXACTLY, expect_identifier(assign_variable_callback(faction_identifier)), @@ -846,7 +846,7 @@ key_value_callback_t ModifierManager::expect_unit_terrain_modifier( ModifierValue& modifier_value, const std::string_view terrain_type_identifier ) const { - return [this, &modifier_value, terrain_type_identifier](const std::string_view key, const ast::NodeCPtr value) -> bool { + return [this, &modifier_value, terrain_type_identifier](const std::string_view key, ovdl::v2script::ast::Node const* const value) -> bool { const memory::string flat_identifier = get_flat_identifier(key, terrain_type_identifier); ModifierEffect const* effect = unit_terrain_modifier_effects.get_item_by_identifier(flat_identifier); if (effect == nullptr) { diff --git a/src/openvic-simulation/modifier/ModifierManager.hpp b/src/openvic-simulation/modifier/ModifierManager.hpp index d712ad464..760a752a8 100644 --- a/src/openvic-simulation/modifier/ModifierManager.hpp +++ b/src/openvic-simulation/modifier/ModifierManager.hpp @@ -70,13 +70,13 @@ namespace OpenVic { ModifierValue& modifier_value, const std::string_view prefix, const std::string_view key, - const ast::NodeCPtr value + ovdl::v2script::ast::Node const* const value ) const; bool _add_modifier_cb( ModifierValue& modifier_value, ModifierEffect const* const effect, - const ast::NodeCPtr value + ovdl::v2script::ast::Node const* const value ) const; NodeTools::key_value_callback_t _expect_modifier_effect( @@ -102,9 +102,9 @@ namespace OpenVic { const IconModifier::icon_t icon, const Modifier::modifier_type_t type = Modifier::modifier_type_t::EVENT ); - bool load_event_modifiers(const ast::NodeCPtr root); + bool load_event_modifiers(ovdl::v2script::ast::Node const* const root); - bool load_static_modifiers(const ast::NodeCPtr root); + bool load_static_modifiers(ovdl::v2script::ast::Node const* const root); bool add_triggered_modifier( const std::string_view identifier, @@ -112,7 +112,7 @@ namespace OpenVic { const IconModifier::icon_t icon, ConditionScript&& trigger ); - bool load_triggered_modifiers(const ast::NodeCPtr root); + bool load_triggered_modifiers(ovdl::v2script::ast::Node const* const root); bool parse_scripts(DefinitionManager const& definition_manager); void lock_all_modifier_except_base_country_effects(); diff --git a/src/openvic-simulation/modifier/ModifierSum.cpp b/src/openvic-simulation/modifier/ModifierSum.cpp index 1c4daba47..442a4e8e7 100644 --- a/src/openvic-simulation/modifier/ModifierSum.cpp +++ b/src/openvic-simulation/modifier/ModifierSum.cpp @@ -1,17 +1,20 @@ #include "ModifierSum.hpp" -#include "openvic-simulation/modifier/Modifier.hpp" +#include -#include "openvic-simulation/country/CountryInstance.hpp" -#include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/core/template/Concepts.hpp" +#include "openvic-simulation/country/CountryInstance.hpp" // IWYU pragma: keep for modifier_source_t +#include "openvic-simulation/map/ProvinceInstance.hpp" // IWYU pragma: keep for modifier_source_t +#include "openvic-simulation/modifier/Modifier.hpp" using namespace OpenVic; std::string_view modifier_entry_t::source_to_string(modifier_source_t const& source) { return std::visit( [](has_get_identifier auto const* has_identifier) -> std::string_view { - return has_identifier->get_identifier(); + return has_identifier == nullptr + ? "" + : has_identifier->get_identifier(); }, source ); @@ -20,7 +23,9 @@ std::string_view modifier_entry_t::source_to_string(modifier_source_t const& sou memory::string modifier_entry_t::to_string() const { return memory::fmt::format( "[{}, {}, {}, {}]", - modifier, multiplier, source_to_string(source), + ovfmt::validate(modifier), + multiplier, + source_to_string(source), ModifierEffect::target_to_string(excluded_targets) ); } @@ -30,10 +35,6 @@ void ModifierSum::clear() { value_sum.clear(); } -bool ModifierSum::empty() { - return modifiers.empty(); -} - fixed_point_t ModifierSum::get_modifier_effect_value(ModifierEffect const& effect, bool* effect_found) const { return value_sum.get_effect(effect, effect_found); } @@ -55,21 +56,10 @@ void ModifierSum::add_modifier( modifier_entry_t::source_or_null_fallback(source, this_source), excluded_targets | this_excluded_targets ); - value_sum.multiply_add_exclude_targets(new_entry.modifier, new_entry.multiplier, new_entry.excluded_targets); - } -} - -void ModifierSum::add_modifier_sum(ModifierSum const& modifier_sum) { - reserve_more(modifiers, modifier_sum.modifiers.size()); - - // We could test that excluded_targets != ALL_TARGETS, but in practice it's always - // called with an explcit/hardcoded value and so won't ever exclude everything. - for (modifier_entry_t const& modifier_entry : modifier_sum.modifiers) { - add_modifier( - modifier_entry.modifier, - modifier_entry.multiplier, - modifier_entry.source, - modifier_entry.excluded_targets + value_sum.multiply_add_exclude_targets( + *new_entry.modifier, + new_entry.multiplier, + new_entry.excluded_targets ); } } diff --git a/src/openvic-simulation/modifier/ModifierSum.hpp b/src/openvic-simulation/modifier/ModifierSum.hpp index 72b2459c2..a9c6573cb 100644 --- a/src/openvic-simulation/modifier/ModifierSum.hpp +++ b/src/openvic-simulation/modifier/ModifierSum.hpp @@ -1,9 +1,11 @@ #pragma once +#include #include +#include "openvic-simulation/core/BulkInsertWrapper.hpp" #include "openvic-simulation/core/memory/Vector.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" #include "openvic-simulation/modifier/ModifierValue.hpp" #include "openvic-simulation/modifier/Modifier.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -31,23 +33,40 @@ namespace OpenVic { ); } - Modifier const& modifier; + Modifier const* modifier; fixed_point_t multiplier; modifier_source_t source; ModifierEffect::target_t excluded_targets; + //invalid but required fore resizing to work + constexpr modifier_entry_t() + : modifier {nullptr}, + multiplier {fixed_point_t::_0}, + source {static_cast>(nullptr)}, + excluded_targets {} {} + + // constexpr modifier_entry_t( Modifier const& new_modifier, fixed_point_t new_multiplier, modifier_source_t const& new_source, ModifierEffect::target_t new_excluded_targets - ) : modifier { new_modifier }, + ) : modifier { &new_modifier }, multiplier { new_multiplier }, source { new_source }, excluded_targets { new_excluded_targets } {} + constexpr bool is_valid() const { + return multiplier != fixed_point_t::_0 + && modifier != nullptr + && ( + get_source_country() != nullptr + || get_source_province() != nullptr + ); + } + constexpr bool operator==(modifier_entry_t const& other) const { - return &modifier == &other.modifier + return modifier == other.modifier && multiplier == other.multiplier && source == other.source && excluded_targets == other.excluded_targets; @@ -67,14 +86,18 @@ namespace OpenVic { constexpr fixed_point_t get_modifier_effect_value( ModifierEffect const& effect, bool* effect_found = nullptr ) const { + if (!is_valid()) { + return fixed_point_t::_0; + } + if (ModifierEffect::excludes_targets(effect.targets, excluded_targets)) { - return modifier.get_effect(effect, effect_found) * multiplier; + return modifier->get_effect(effect, effect_found) * multiplier; } if (effect_found != nullptr) { *effect_found = false; } - return 0; + return fixed_point_t::_0; } }; @@ -84,7 +107,7 @@ namespace OpenVic { // but this has a non-negligible cost and we already calculate it as part of the "is_contributing" checks, so we pass // the pre-calculated value along as a callback argument. template - concept ContributingModifierCallback = NodeTools::Functor; + concept ContributingModifierCallback = Functor; struct ModifierSum { private: @@ -94,18 +117,22 @@ namespace OpenVic { // Targets to be excluded from all modifiers added to the sum, combined with any explicit exclusions. ModifierEffect::target_t PROPERTY_RW(this_excluded_targets, ModifierEffect::target_t::NO_TARGETS); - memory::vector SPAN_PROPERTY(modifiers); + bulk_insert_wrapper< + memory::vector + > SPAN_PROPERTY(modifiers); ModifierValue PROPERTY(value_sum); public: ModifierSum() {}; - ModifierSum(ModifierSum const&) = default; ModifierSum(ModifierSum&&) = default; - ModifierSum& operator=(ModifierSum const&) = default; - ModifierSum& operator=(ModifierSum&&) = default; + constexpr std::size_t size() const { + return modifiers.size(); + } + constexpr bool empty() const { + return modifiers.empty(); + } void clear(); - bool empty(); fixed_point_t get_modifier_effect_value(ModifierEffect const& effect, bool* effect_found = nullptr) const; bool has_modifier_effect(ModifierEffect const& effect) const; @@ -116,17 +143,34 @@ namespace OpenVic { modifier_entry_t::modifier_source_t const& source = {}, ModifierEffect::target_t excluded_targets = ModifierEffect::target_t::NO_TARGETS ); - // Reserves space for the number of modifier entries in the given sum and adds each of them using add_modifier + + constexpr void make_room_for(ModifierSum const& modifier_sum) { + modifiers.make_room_for(modifier_sum.size()); + } + + // Inserts modifiers directly via std::ranges::copy. Requires resizing beforehand! // with the modifier entries' attributes as arguments. This means non-null sources are preserved (null ones are // replaced with this_source, but in practice the other sum should've set them itself already) and exclusion targets // are combined with this_excluded_targets. - void add_modifier_sum(ModifierSum const& modifier_sum); + constexpr void add_modifier_sum(ModifierSum const& modifier_sum) { + // We could test that excluded_targets != ALL_TARGETS, but in practice it's always + // called with an explcit/hardcoded value and so won't ever exclude everything. + modifiers.append_range(modifier_sum.modifiers); + + for (modifier_entry_t const& m : modifier_sum.get_modifiers()) { + value_sum.multiply_add_exclude_targets( + *m.modifier, + m.multiplier, + m.excluded_targets + ); + } + } // TODO - help calculate value_sum[effect]? Early return if lookup in value_sum fails? constexpr void for_each_contributing_modifier( ModifierEffect const& effect, ContributingModifierCallback auto callback ) const { - for (modifier_entry_t const& modifier_entry : modifiers) { + for (modifier_entry_t const& modifier_entry : get_modifiers()) { const fixed_point_t contribution = modifier_entry.get_modifier_effect_value(effect); if (contribution != 0) { diff --git a/src/openvic-simulation/modifier/StaticModifierCache.cpp b/src/openvic-simulation/modifier/StaticModifierCache.cpp index bcd017804..49a86feb7 100644 --- a/src/openvic-simulation/modifier/StaticModifierCache.cpp +++ b/src/openvic-simulation/modifier/StaticModifierCache.cpp @@ -12,7 +12,7 @@ using enum Modifier::modifier_type_t; StaticModifierCache::StaticModifierCache() {} -bool StaticModifierCache::load_static_modifiers(ModifierManager& modifier_manager, const ast::NodeCPtr root) { +bool StaticModifierCache::load_static_modifiers(ModifierManager& modifier_manager, ovdl::v2script::ast::Node const* const root) { bool ret = true; key_map_t key_map {}; @@ -30,7 +30,7 @@ bool StaticModifierCache::load_static_modifiers(ModifierManager& modifier_manage ) -> void { ret &= add_key_map_entry( key_map, identifier, ONE_EXACTLY, - [&modifier_manager, identifier, icon](const ast::NodeCPtr value) -> bool { + [&modifier_manager, identifier, icon](ovdl::v2script::ast::Node const* const value) -> bool { if (modifier_manager.get_event_modifier_by_identifier(identifier) != nullptr) { return true; //an event modifier overrides a static modifier with the same identifier. } @@ -61,7 +61,7 @@ bool StaticModifierCache::load_static_modifiers(ModifierManager& modifier_manage key_map, base_modifier.get_identifier(), ONE_EXACTLY, expect_dictionary_keys_and_default( modifier_manager.expect_base_country_modifier(base_modifier), - "supply_limit", ZERO_OR_ONE, [this, &modifier_manager](const ast::NodeCPtr value) -> bool { + "supply_limit", ZERO_OR_ONE, [this, &modifier_manager](ovdl::v2script::ast::Node const* const value) -> bool { return modifier_manager._add_modifier_cb( base_modifier, modifier_manager.get_modifier_effect_cache().get_supply_limit_global_base(), diff --git a/src/openvic-simulation/modifier/StaticModifierCache.hpp b/src/openvic-simulation/modifier/StaticModifierCache.hpp index b30ac50eb..744e3f703 100644 --- a/src/openvic-simulation/modifier/StaticModifierCache.hpp +++ b/src/openvic-simulation/modifier/StaticModifierCache.hpp @@ -1,6 +1,6 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" #include "openvic-simulation/modifier/Modifier.hpp" #include "openvic-simulation/utility/Getters.hpp" @@ -72,7 +72,7 @@ namespace OpenVic { StaticModifierCache(); - bool load_static_modifiers(ModifierManager& modifier_manager, const ast::NodeCPtr root); + bool load_static_modifiers(ModifierManager& modifier_manager, ovdl::v2script::ast::Node const* const root); public: StaticModifierCache(StaticModifierCache&&) = default; diff --git a/src/openvic-simulation/pathfinding/AStarPathing.cpp b/src/openvic-simulation/pathfinding/AStarPathing.cpp index 062966390..1c84861d2 100644 --- a/src/openvic-simulation/pathfinding/AStarPathing.cpp +++ b/src/openvic-simulation/pathfinding/AStarPathing.cpp @@ -40,9 +40,13 @@ bool AStarPathing::_solve(search_iterator begin_point, search_iterator end_point search_iterator p = open_list.front(); // The currently processed point. // Find point closer to end_point, or same distance to end_point but closer to begin_point. - if (last_closest_point == search.end() || last_closest_point.value().abs_f_score > p.value().abs_f_score || - (last_closest_point.value().abs_f_score >= p.value().abs_f_score && // - last_closest_point.value().g_score > p.value().g_score)) { + if (last_closest_point == search.end() + || last_closest_point.value().abs_f_score > p.value().abs_f_score + || ( + last_closest_point.value().abs_f_score >= p.value().abs_f_score + && last_closest_point.value().g_score > p.value().g_score + ) + ) { last_closest_point = p; } diff --git a/src/openvic-simulation/pathfinding/FringePathing.cpp b/src/openvic-simulation/pathfinding/FringePathing.cpp index 67c9e1abc..280a67370 100644 --- a/src/openvic-simulation/pathfinding/FringePathing.cpp +++ b/src/openvic-simulation/pathfinding/FringePathing.cpp @@ -56,9 +56,13 @@ bool FringePathing::_solve(search_iterator begin_point, search_iterator end_poin } // Find point closer to end_point, or same distance to end_point but closer to begin_point. - if (last_closest_point == search.end() || last_closest_point.value().abs_f_score > p.value().abs_f_score || - (last_closest_point.value().abs_f_score >= p.value().abs_f_score && // - last_closest_point.value().g_score > p.value().g_score)) { + if (last_closest_point == search.end() + || last_closest_point.value().abs_f_score > p.value().abs_f_score + || ( + last_closest_point.value().abs_f_score >= p.value().abs_f_score + && last_closest_point.value().g_score > p.value().g_score + ) + ) { last_closest_point = p; } diff --git a/src/openvic-simulation/pathfinding/PathingBase.hpp b/src/openvic-simulation/pathfinding/PathingBase.hpp index ca7b73f34..6f3f4f44e 100644 --- a/src/openvic-simulation/pathfinding/PathingBase.hpp +++ b/src/openvic-simulation/pathfinding/PathingBase.hpp @@ -21,12 +21,15 @@ namespace OpenVic { using search_key_type = KeyT; using search_value_type = ValueT; using search_pair_type = std::pair; - using search_allocator_type = - foonathan::memory::std_allocator>; + using search_allocator_type = foonathan::memory::std_allocator< + search_pair_type, + memory::tracker + >; using search_container_type = std::vector; using search_map_type = tsl::ordered_map< search_key_type, search_value_type, // - std::hash, std::equal_to, search_allocator_type, search_container_type>; + std::hash, std::equal_to, search_allocator_type, search_container_type + >; using search_iterator = search_map_type::iterator; using search_const_iterator = search_map_type::const_iterator; diff --git a/src/openvic-simulation/pathfinding/PointMap.hpp b/src/openvic-simulation/pathfinding/PointMap.hpp index b4fee3665..aa0000e6a 100644 --- a/src/openvic-simulation/pathfinding/PointMap.hpp +++ b/src/openvic-simulation/pathfinding/PointMap.hpp @@ -22,12 +22,15 @@ namespace OpenVic { using points_key_type = uint64_t; using points_value_type = Point; using points_pair_type = std::pair; - using points_allocator_type = - foonathan::memory::std_allocator>; + using points_allocator_type = foonathan::memory::std_allocator< + points_pair_type, + memory::tracker + >; using points_container_type = std::vector; using points_map_type = tsl::ordered_map< points_key_type, points_value_type, // - std::hash, std::equal_to, points_allocator_type, points_container_type>; + std::hash, std::equal_to, points_allocator_type, points_container_type + >; using points_iterator = points_map_type::iterator; using points_const_iterator = points_map_type::const_iterator; @@ -63,15 +66,17 @@ namespace OpenVic { struct SegmentHash { inline constexpr std::size_t operator()(Segment const& segment) const { - return hash_murmur3(hash_murmur3(segment.key.first) << 32) | - hash_murmur3(segment.key.second); + return hash_murmur3(hash_murmur3(segment.key.first) << 32) + | hash_murmur3(segment.key.second); } }; using segments_type = Segment; using segments_hash_type = SegmentHash; - using segments_allocator_type = - foonathan::memory::std_allocator>; + using segments_allocator_type = foonathan::memory::std_allocator< + segments_type, + memory::tracker + >; using segments_container_type = std::vector; using segments_set_type = tsl::ordered_set< segments_type, segments_hash_type, std::equal_to, segments_allocator_type, segments_container_type>; @@ -80,13 +85,16 @@ namespace OpenVic { struct Point { using neighbor_point_id_type = points_key_type; - using neighbors_allocator_type = - foonathan::memory::std_allocator>; + using neighbors_allocator_type = foonathan::memory::std_allocator< + neighbor_point_id_type, + memory::tracker + >; using neighbors_container_type = std::vector; using neighbors_type = tsl::ordered_set< neighbor_point_id_type, // std::hash, std::equal_to, neighbors_allocator_type, - neighbors_container_type>; + neighbors_container_type + >; using neighbors_iterator = neighbors_type::iterator; using neighbors_const_iterator = neighbors_type::const_iterator; diff --git a/src/openvic-simulation/politics/Government.cpp b/src/openvic-simulation/politics/Government.cpp index 5dadda8bd..d76e316a4 100644 --- a/src/openvic-simulation/politics/Government.cpp +++ b/src/openvic-simulation/politics/Government.cpp @@ -5,7 +5,6 @@ #include "openvic-simulation/politics/Ideology.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; GovernmentType::GovernmentType( index_t new_index, @@ -29,115 +28,3 @@ bool GovernmentType::is_ideology_compatible(Ideology const& ideology) const { ideology ); } - -bool GovernmentTypeManager::add_government_type( - std::string_view identifier, memory::vector>&& ideologies, bool elections, bool appoint_ruling_party, - Timespan term_duration, std::string_view flag_type -) { - spdlog::scope scope { fmt::format("government type {}", identifier) }; - if (identifier.empty()) { - spdlog::error_s("Invalid government type identifier - empty!"); - return false; - } - - if (ideologies.empty()) { - spdlog::error_s("No compatible ideologies defined for government type {}", identifier); - return false; - } - - if (elections && term_duration < 0) { - spdlog::error_s("No or invalid term duration for government type {}", identifier); - return false; - } - - const bool ret = government_types.emplace_item( - identifier, - GovernmentType::index_t { get_government_type_count() }, identifier, std::move(ideologies), elections, appoint_ruling_party, term_duration, flag_type - ); - - /* flag_type can be empty here for default/non-ideological flag */ - if (ret) { - flag_types.emplace(flag_type); - } - - return ret; -} - -/* REQUIREMENTS: FS-525, SIM-27 */ -bool GovernmentTypeManager::load_government_types_file(IdeologyManager const& ideology_manager, ast::NodeCPtr root) { - spdlog::scope scope { "common/governments.txt" }; - bool ret = expect_dictionary_reserve_length( - government_types, - [this, &ideology_manager](std::string_view government_type_identifier, ast::NodeCPtr government_type_value) -> bool { - memory::vector> ideologies; - bool elections = false, appoint_ruling_party = false; - Timespan term_duration = 0; - std::string_view flag_type_identifier; - - size_t total_expected_ideologies = 0; - bool ret = expect_dictionary_keys_and_default( - increment_callback(total_expected_ideologies), - "election", ZERO_OR_ONE, expect_bool(assign_variable_callback(elections)), - "duration", ZERO_OR_ONE, expect_months(assign_variable_callback(term_duration)), - "appoint_ruling_party", ONE_EXACTLY, expect_bool(assign_variable_callback(appoint_ruling_party)), - "flagType", ZERO_OR_ONE, expect_identifier(assign_variable_callback(flag_type_identifier)) - )(government_type_value); - ideologies.reserve(total_expected_ideologies); - - ret &= expect_dictionary( - [this, &ideology_manager, &ideologies, government_type_identifier]( - std::string_view key, ast::NodeCPtr value - ) -> bool { - static const string_set_t reserved_keys = { "election", "duration", "appoint_ruling_party", "flagType" }; - if (reserved_keys.contains(key)) { - return true; - } - Ideology const* ideology_ptr = ideology_manager.get_ideology_by_identifier(key); - if (ideology_ptr == nullptr) { - spdlog::error_s( - "When loading government type {}, specified ideology {} is invalid!", - government_type_identifier, key - ); - return false; - } - return expect_bool([&ideologies, &ideology = *ideology_ptr, government_type_identifier](bool val) -> bool { - if (val) { - if (!ranges::contains( - ideologies, - ideology - )) { - ideologies.emplace_back(ideology); - return true; - } - spdlog::error_s( - "Government type {} marked as supporting ideology {}", - government_type_identifier, ideology - ); - return false; - } - spdlog::error_s( - "Government type {} redundantly marked as not supporting ideology {} multiple times", - government_type_identifier, ideology - ); - return false; - })(value); - } - )(government_type_value); - - ret &= add_government_type( - government_type_identifier, std::move(ideologies), elections, appoint_ruling_party, term_duration, - flag_type_identifier - ); - return ret; - } - )(root); - lock_government_types(); - - return ret; -} - -bool GovernmentTypeManager::is_valid_flag_type(std::string_view type) const { - return std::any_of(flag_types.begin(), flag_types.end(), [type](memory::string const& flag_type) -> bool { - return flag_type == type; - }); -} diff --git a/src/openvic-simulation/politics/Government.hpp b/src/openvic-simulation/politics/Government.hpp index b41a09984..58f138691 100644 --- a/src/openvic-simulation/politics/Government.hpp +++ b/src/openvic-simulation/politics/Government.hpp @@ -7,7 +7,6 @@ #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { @@ -37,20 +36,4 @@ namespace OpenVic { bool is_ideology_compatible(Ideology const& ideology) const; }; - - struct GovernmentTypeManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(government_type); - string_set_t PROPERTY(flag_types); - - public: - bool add_government_type( - std::string_view identifier, memory::vector>&& ideologies, bool elections, bool appoint_ruling_party, - Timespan term_duration, std::string_view flag_type - ); - - bool load_government_types_file(IdeologyManager const& ideology_manager, ast::NodeCPtr root); - - bool is_valid_flag_type(std::string_view type) const; - }; } diff --git a/src/openvic-simulation/politics/GovernmentManager.cpp b/src/openvic-simulation/politics/GovernmentManager.cpp new file mode 100644 index 000000000..deb9ed9c1 --- /dev/null +++ b/src/openvic-simulation/politics/GovernmentManager.cpp @@ -0,0 +1,121 @@ +#include "GovernmentManager.hpp" + +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/politics/IdeologyManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool GovernmentTypeManager::add_government_type( + std::string_view identifier, memory::vector>&& ideologies, bool elections, bool appoint_ruling_party, + Timespan term_duration, std::string_view flag_type +) { + spdlog::scope scope { fmt::format("government type {}", identifier) }; + if (identifier.empty()) { + spdlog::error_s("Invalid government type identifier - empty!"); + return false; + } + + if (ideologies.empty()) { + spdlog::error_s("No compatible ideologies defined for government type {}", identifier); + return false; + } + + if (elections && term_duration < 0) { + spdlog::error_s("No or invalid term duration for government type {}", identifier); + return false; + } + + const bool ret = government_types.emplace_item( + identifier, + GovernmentType::index_t { get_government_type_count() }, identifier, std::move(ideologies), elections, appoint_ruling_party, term_duration, flag_type + ); + + /* flag_type can be empty here for default/non-ideological flag */ + if (ret) { + flag_types.emplace(flag_type); + } + + return ret; +} + +/* REQUIREMENTS: FS-525, SIM-27 */ +bool GovernmentTypeManager::load_government_types_file(IdeologyManager const& ideology_manager, ovdl::v2script::ast::Node const* root) { + spdlog::scope scope { "common/governments.txt" }; + bool ret = expect_dictionary_reserve_length( + government_types, + [this, &ideology_manager](std::string_view government_type_identifier, ovdl::v2script::ast::Node const* government_type_value) -> bool { + memory::vector> ideologies; + bool elections = false, appoint_ruling_party = false; + Timespan term_duration = 0; + std::string_view flag_type_identifier; + + size_t total_expected_ideologies = 0; + bool ret = expect_dictionary_keys_and_default( + increment_callback(total_expected_ideologies), + "election", ZERO_OR_ONE, expect_bool(assign_variable_callback(elections)), + "duration", ZERO_OR_ONE, expect_months(assign_variable_callback(term_duration)), + "appoint_ruling_party", ONE_EXACTLY, expect_bool(assign_variable_callback(appoint_ruling_party)), + "flagType", ZERO_OR_ONE, expect_identifier(assign_variable_callback(flag_type_identifier)) + )(government_type_value); + ideologies.reserve(total_expected_ideologies); + + ret &= expect_dictionary( + [this, &ideology_manager, &ideologies, government_type_identifier]( + std::string_view key, ovdl::v2script::ast::Node const* value + ) -> bool { + static const string_set_t reserved_keys = { "election", "duration", "appoint_ruling_party", "flagType" }; + if (reserved_keys.contains(key)) { + return true; + } + Ideology const* ideology_ptr = ideology_manager.get_ideology_by_identifier(key); + if (ideology_ptr == nullptr) { + spdlog::error_s( + "When loading government type {}, specified ideology {} is invalid!", + government_type_identifier, key + ); + return false; + } + return expect_bool([&ideologies, &ideology = *ideology_ptr, government_type_identifier](bool val) -> bool { + if (val) { + if (!ranges::contains( + ideologies, + ideology + )) { + ideologies.emplace_back(ideology); + return true; + } + spdlog::error_s( + "Government type {} marked as supporting ideology {}", + government_type_identifier, ideology + ); + return false; + } + spdlog::error_s( + "Government type {} redundantly marked as not supporting ideology {} multiple times", + government_type_identifier, ideology + ); + return false; + })(value); + } + )(government_type_value); + + ret &= add_government_type( + government_type_identifier, std::move(ideologies), elections, appoint_ruling_party, term_duration, + flag_type_identifier + ); + return ret; + } + )(root); + lock_government_types(); + + return ret; +} + +bool GovernmentTypeManager::is_valid_flag_type(std::string_view type) const { + return std::any_of(flag_types.begin(), flag_types.end(), [type](memory::string const& flag_type) -> bool { + return flag_type == type; + }); +} diff --git a/src/openvic-simulation/politics/GovernmentManager.hpp b/src/openvic-simulation/politics/GovernmentManager.hpp new file mode 100644 index 000000000..90657c323 --- /dev/null +++ b/src/openvic-simulation/politics/GovernmentManager.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/politics/Government.hpp" +#include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct GovernmentTypeManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(government_type); + string_set_t PROPERTY(flag_types); + + public: + bool add_government_type( + std::string_view identifier, memory::vector>&& ideologies, bool elections, bool appoint_ruling_party, + Timespan term_duration, std::string_view flag_type + ); + + bool load_government_types_file(IdeologyManager const& ideology_manager, ovdl::v2script::ast::Node const* root); + + bool is_valid_flag_type(std::string_view type) const; + }; +} diff --git a/src/openvic-simulation/politics/Ideology.cpp b/src/openvic-simulation/politics/Ideology.cpp index 61067685e..2885586d3 100644 --- a/src/openvic-simulation/politics/Ideology.cpp +++ b/src/openvic-simulation/politics/Ideology.cpp @@ -3,7 +3,6 @@ #include "openvic-simulation/types/Colour.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; IdeologyGroup::IdeologyGroup(std::string_view new_identifier) : HasIdentifier { new_identifier } {} @@ -46,140 +45,3 @@ bool Ideology::parse_scripts(DefinitionManager const& definition_manager) { ret &= add_economic_reform.parse_scripts(definition_manager); return ret; } - -bool IdeologyManager::add_ideology_group(std::string_view identifier) { - spdlog::scope scope { fmt::format("ideology group {}", identifier) }; - if (identifier.empty()) { - spdlog::error_s("Invalid ideology group identifier - empty!"); - return false; - } - - return ideology_groups.emplace_item(identifier, identifier); -} - -bool IdeologyManager::add_ideology( - std::string_view identifier, - colour_t colour, - IdeologyGroup const* group, - bool uncivilised, - bool can_reduce_consciousness, - bool can_reduce_militancy, - std::optional spawn_date, - ConditionalWeightBase&& add_political_reform, - ConditionalWeightBase&& remove_political_reform, - ConditionalWeightBase&& add_social_reform, - ConditionalWeightBase&& remove_social_reform, - ConditionalWeightBase&& add_military_reform, - ConditionalWeightBase&& add_economic_reform -) { - spdlog::scope scope { fmt::format("ideology {}", identifier) }; - if (identifier.empty()) { - spdlog::error_s("Invalid ideology identifier - empty!"); - return false; - } - - if (group == nullptr) { - spdlog::error_s("Null ideology group for {}", identifier); - return false; - } - - const Ideology::index_t new_index = Ideology::index_t { ideologies.size() }; - return ideologies.emplace_item( - identifier, - identifier, - new_index, - colour, - *group, - uncivilised, - can_reduce_consciousness, - can_reduce_militancy, - spawn_date, - std::move(add_political_reform), - std::move(remove_political_reform), - std::move(add_social_reform), - std::move(remove_social_reform), - std::move(add_military_reform), - std::move(add_economic_reform) - ); -} - -/* REQUIREMENTS: - * POL-9, POL-10, POL-11, POL-12, POL-13, POL-14, POL-15 - */ -bool IdeologyManager::load_ideology_file(ast::NodeCPtr root) { - spdlog::scope scope { "common/ideologies.txt" }; - size_t expected_ideologies = 0; - bool ret = expect_dictionary_reserve_length( - ideology_groups, - [this, &expected_ideologies](std::string_view key, ast::NodeCPtr value) -> bool { - bool ret = expect_length(add_variable_callback(expected_ideologies))(value); - ret &= add_ideology_group(key); - return ret; - } - )(root); - lock_ideology_groups(); - - reserve_more_ideologies(expected_ideologies); - ret &= expect_dictionary([this](std::string_view ideology_group_key, ast::NodeCPtr ideology_group_value) -> bool { - IdeologyGroup const* ideology_group = get_ideology_group_by_identifier(ideology_group_key); - - return expect_dictionary([this, ideology_group](std::string_view key, ast::NodeCPtr value) -> bool { - using enum scope_type_t; - - colour_t colour = colour_t::null(); - bool uncivilised = true, can_reduce_consciousness = false, can_reduce_militancy = false; - std::optional spawn_date; - ConditionalWeightBase add_political_reform { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionalWeightBase remove_political_reform { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionalWeightBase add_social_reform { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionalWeightBase remove_social_reform { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionalWeightBase add_military_reform { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionalWeightBase add_economic_reform { COUNTRY, COUNTRY, NO_SCOPE }; - - bool ret = expect_dictionary_keys( - "uncivilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(uncivilised)), - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), - "date", ZERO_OR_ONE, expect_date(assign_variable_callback_opt(spawn_date)), - "can_reduce_consciousness", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_reduce_consciousness)), - "can_reduce_militancy", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_reduce_militancy)), - "add_political_reform", ONE_EXACTLY, add_political_reform.expect_conditional_weight(), - "remove_political_reform", ONE_EXACTLY, - remove_political_reform.expect_conditional_weight(), - "add_social_reform", ONE_EXACTLY, add_social_reform.expect_conditional_weight(), - "remove_social_reform", ONE_EXACTLY, remove_social_reform.expect_conditional_weight(), - "add_military_reform", ZERO_OR_ONE, add_military_reform.expect_conditional_weight(), - "add_economic_reform", ZERO_OR_ONE, add_economic_reform.expect_conditional_weight() - )(value); - - ret &= add_ideology( - key, - colour, - ideology_group, - uncivilised, - can_reduce_consciousness, - can_reduce_militancy, - spawn_date, - std::move(add_political_reform), - std::move(remove_political_reform), - std::move(add_social_reform), - std::move(remove_social_reform), - std::move(add_military_reform), - std::move(add_economic_reform) - ); - - return ret; - })(ideology_group_value); - })(root); - - lock_ideologies(); - - return ret; -} - -bool IdeologyManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - for (Ideology& ideology : ideologies.get_items()) { - ideology.parse_scripts(definition_manager); - } - return ret; -} diff --git a/src/openvic-simulation/politics/Ideology.hpp b/src/openvic-simulation/politics/Ideology.hpp index 1b4e98ffe..d8a65816d 100644 --- a/src/openvic-simulation/politics/Ideology.hpp +++ b/src/openvic-simulation/politics/Ideology.hpp @@ -3,7 +3,6 @@ #include "openvic-simulation/scripts/ConditionalWeight.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { @@ -58,33 +57,4 @@ namespace OpenVic { ); Ideology(Ideology&&) = default; }; - - struct IdeologyManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(ideology_group); - IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(ideology, ideologies); - - public: - bool add_ideology_group(std::string_view identifier); - - bool add_ideology( - std::string_view identifier, - colour_t colour, - IdeologyGroup const* group, - bool uncivilised, - bool can_reduce_consciousness, - bool can_reduce_militancy, - std::optional spawn_date, - ConditionalWeightBase&& add_political_reform, - ConditionalWeightBase&& remove_political_reform, - ConditionalWeightBase&& add_social_reform, - ConditionalWeightBase&& remove_social_reform, - ConditionalWeightBase&& add_military_reform, - ConditionalWeightBase&& add_economic_reform - ); - - bool load_ideology_file(ast::NodeCPtr root); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/politics/IdeologyManager.cpp b/src/openvic-simulation/politics/IdeologyManager.cpp new file mode 100644 index 000000000..336b24997 --- /dev/null +++ b/src/openvic-simulation/politics/IdeologyManager.cpp @@ -0,0 +1,144 @@ +#include "IdeologyManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/types/Colour.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool IdeologyManager::add_ideology_group(std::string_view identifier) { + spdlog::scope scope { fmt::format("ideology group {}", identifier) }; + if (identifier.empty()) { + spdlog::error_s("Invalid ideology group identifier - empty!"); + return false; + } + + return ideology_groups.emplace_item(identifier, identifier); +} + +bool IdeologyManager::add_ideology( + std::string_view identifier, + colour_t colour, + IdeologyGroup const* group, + bool uncivilised, + bool can_reduce_consciousness, + bool can_reduce_militancy, + std::optional spawn_date, + ConditionalWeightBase&& add_political_reform, + ConditionalWeightBase&& remove_political_reform, + ConditionalWeightBase&& add_social_reform, + ConditionalWeightBase&& remove_social_reform, + ConditionalWeightBase&& add_military_reform, + ConditionalWeightBase&& add_economic_reform +) { + spdlog::scope scope { fmt::format("ideology {}", identifier) }; + if (identifier.empty()) { + spdlog::error_s("Invalid ideology identifier - empty!"); + return false; + } + + if (group == nullptr) { + spdlog::error_s("Null ideology group for {}", identifier); + return false; + } + + const Ideology::index_t new_index = Ideology::index_t { ideologies.size() }; + return ideologies.emplace_item( + identifier, + identifier, + new_index, + colour, + *group, + uncivilised, + can_reduce_consciousness, + can_reduce_militancy, + spawn_date, + std::move(add_political_reform), + std::move(remove_political_reform), + std::move(add_social_reform), + std::move(remove_social_reform), + std::move(add_military_reform), + std::move(add_economic_reform) + ); +} + +/* REQUIREMENTS: + * POL-9, POL-10, POL-11, POL-12, POL-13, POL-14, POL-15 + */ +bool IdeologyManager::load_ideology_file(ovdl::v2script::ast::Node const* root) { + spdlog::scope scope { "common/ideologies.txt" }; + size_t expected_ideologies = 0; + bool ret = expect_dictionary_reserve_length( + ideology_groups, + [this, &expected_ideologies](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + bool ret = expect_length(add_variable_callback(expected_ideologies))(value); + ret &= add_ideology_group(key); + return ret; + } + )(root); + lock_ideology_groups(); + + reserve_more_ideologies(expected_ideologies); + ret &= expect_dictionary([this](std::string_view ideology_group_key, ovdl::v2script::ast::Node const* ideology_group_value) -> bool { + IdeologyGroup const* ideology_group = get_ideology_group_by_identifier(ideology_group_key); + + return expect_dictionary([this, ideology_group](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + using enum scope_type_t; + + colour_t colour = colour_t::null(); + bool uncivilised = true, can_reduce_consciousness = false, can_reduce_militancy = false; + std::optional spawn_date; + ConditionalWeightBase add_political_reform { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionalWeightBase remove_political_reform { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionalWeightBase add_social_reform { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionalWeightBase remove_social_reform { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionalWeightBase add_military_reform { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionalWeightBase add_economic_reform { COUNTRY, COUNTRY, NO_SCOPE }; + + bool ret = expect_dictionary_keys( + "uncivilized", ZERO_OR_ONE, expect_bool(assign_variable_callback(uncivilised)), + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "date", ZERO_OR_ONE, expect_date(assign_variable_callback_opt(spawn_date)), + "can_reduce_consciousness", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_reduce_consciousness)), + "can_reduce_militancy", ZERO_OR_ONE, expect_bool(assign_variable_callback(can_reduce_militancy)), + "add_political_reform", ONE_EXACTLY, add_political_reform.expect_conditional_weight(), + "remove_political_reform", ONE_EXACTLY, + remove_political_reform.expect_conditional_weight(), + "add_social_reform", ONE_EXACTLY, add_social_reform.expect_conditional_weight(), + "remove_social_reform", ONE_EXACTLY, remove_social_reform.expect_conditional_weight(), + "add_military_reform", ZERO_OR_ONE, add_military_reform.expect_conditional_weight(), + "add_economic_reform", ZERO_OR_ONE, add_economic_reform.expect_conditional_weight() + )(value); + + ret &= add_ideology( + key, + colour, + ideology_group, + uncivilised, + can_reduce_consciousness, + can_reduce_militancy, + spawn_date, + std::move(add_political_reform), + std::move(remove_political_reform), + std::move(add_social_reform), + std::move(remove_social_reform), + std::move(add_military_reform), + std::move(add_economic_reform) + ); + + return ret; + })(ideology_group_value); + })(root); + + lock_ideologies(); + + return ret; +} + +bool IdeologyManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (Ideology& ideology : ideologies.get_items()) { + ideology.parse_scripts(definition_manager); + } + return ret; +} diff --git a/src/openvic-simulation/politics/IdeologyManager.hpp b/src/openvic-simulation/politics/IdeologyManager.hpp new file mode 100644 index 000000000..7988a7ff5 --- /dev/null +++ b/src/openvic-simulation/politics/IdeologyManager.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct IdeologyManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(ideology_group); + IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(ideology, ideologies); + + public: + bool add_ideology_group(std::string_view identifier); + + bool add_ideology( + std::string_view identifier, + colour_t colour, + IdeologyGroup const* group, + bool uncivilised, + bool can_reduce_consciousness, + bool can_reduce_militancy, + std::optional spawn_date, + ConditionalWeightBase&& add_political_reform, + ConditionalWeightBase&& remove_political_reform, + ConditionalWeightBase&& add_social_reform, + ConditionalWeightBase&& remove_social_reform, + ConditionalWeightBase&& add_military_reform, + ConditionalWeightBase&& add_economic_reform + ); + + bool load_ideology_file(ovdl::v2script::ast::Node const* root); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/politics/IssueManager.cpp b/src/openvic-simulation/politics/IssueManager.cpp index 479c2b0ef..3ad8a5d2d 100644 --- a/src/openvic-simulation/politics/IssueManager.cpp +++ b/src/openvic-simulation/politics/IssueManager.cpp @@ -1,5 +1,6 @@ #include "IssueManager.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/modifier/ModifierManager.hpp" #include "openvic-simulation/core/Typedefs.hpp" @@ -123,7 +124,7 @@ bool IssueManager::add_reform( return true; } -bool IssueManager::_load_party_policy_group(size_t& expected_party_policies, std::string_view identifier, ast::NodeCPtr node) { +bool IssueManager::_load_party_policy_group(size_t& expected_party_policies, std::string_view identifier, ovdl::v2script::ast::Node const* node) { spdlog::scope scope { fmt::format("party policy group {}", identifier) }; return expect_length(add_variable_callback(expected_party_policies))(node) & add_party_policy_group(identifier); @@ -165,7 +166,7 @@ static constexpr colour_t create_issue_reform_colour(size_t index) { bool IssueManager::_load_party_policy( ModifierManager const& modifier_manager, std::string_view identifier, - PartyPolicyGroup& party_policy_group, ast::NodeCPtr node + PartyPolicyGroup& party_policy_group, ovdl::v2script::ast::Node const* node ) { spdlog::scope scope { fmt::format("party policy {}", identifier) }; @@ -177,7 +178,7 @@ bool IssueManager::_load_party_policy( modifier_manager.expect_base_country_modifier(values), "is_jingoism", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_jingoism)), "rules", ZERO_OR_ONE, RuleSet::expect_rule_set(move_variable_callback(rules)), - "war_exhaustion_effect", ZERO_OR_ONE, [](const ast::NodeCPtr _) -> bool { + "war_exhaustion_effect", ZERO_OR_ONE, [](ovdl::v2script::ast::Node const* const _) -> bool { spdlog::warn_s("war_exhaustion_effect does nothing (vanilla issues have it)."); return true; } @@ -192,7 +193,7 @@ bool IssueManager::_load_party_policy( } bool IssueManager::_load_reform_group( - size_t& expected_reforms, std::string_view identifier, ReformType& reform_type, ast::NodeCPtr node + size_t& expected_reforms, std::string_view identifier, ReformType& reform_type, ovdl::v2script::ast::Node const* node ) { spdlog::scope scope { fmt::format("reform group {}", identifier) }; bool ordered = false, administrative = false; @@ -210,7 +211,7 @@ bool IssueManager::_load_reform_group( bool IssueManager::_load_reform( ModifierManager const& modifier_manager, size_t ordinal, std::string_view identifier, - ReformGroup& reform_group, ast::NodeCPtr node + ReformGroup& reform_group, ovdl::v2script::ast::Node const* node ) { spdlog::scope scope { fmt::format("reform {}", identifier) }; using enum scope_type_t; @@ -254,7 +255,7 @@ bool IssueManager::_load_reform( * POL-113, POL-114, POL-115, POL-116 */ bool IssueManager::load_issues_file( - ModifierManager const& modifier_manager, ast::NodeCPtr root + ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root ) { spdlog::scope scope { "common/issues.txt" }; bool party_issues_found = false; @@ -265,7 +266,7 @@ bool IssueManager::load_issues_file( bool ret = expect_dictionary_reserve_length( reform_types, [this, &party_issues_found, &expected_party_policy_groups, &expected_reform_groups]( - std::string_view key, ast::NodeCPtr value + std::string_view key, ovdl::v2script::ast::Node const* value ) -> bool { if (key == "party_issues") { if (party_issues_found) { @@ -299,7 +300,7 @@ bool IssueManager::load_issues_file( /* Load issue and reform groups. */ ret &= expect_dictionary( [this, &party_issues_found, &expected_party_policies, &expected_reforms]( - std::string_view type_key, ast::NodeCPtr type_value + std::string_view type_key, ovdl::v2script::ast::Node const* type_value ) -> bool { if (type_key == "party_issues") { if (party_issues_found) { @@ -307,7 +308,7 @@ bool IssueManager::load_issues_file( } party_issues_found = true; - return expect_dictionary([this, &expected_party_policies](std::string_view key, ast::NodeCPtr value) -> bool { + return expect_dictionary([this, &expected_party_policies](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { return _load_party_policy_group(expected_party_policies, key, value); })(type_value); } else { @@ -319,7 +320,7 @@ bool IssueManager::load_issues_file( } return expect_dictionary( - [this, reform_type, &expected_reforms](std::string_view key, ast::NodeCPtr value) -> bool { + [this, reform_type, &expected_reforms](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { return _load_reform_group(expected_reforms, key, *reform_type, value); } )(type_value); @@ -338,7 +339,7 @@ bool IssueManager::load_issues_file( /* Load issues and reforms. */ ret &= expect_dictionary( [this, &party_issues_found, &modifier_manager]( - std::string_view type_key, ast::NodeCPtr type_value + std::string_view type_key, ovdl::v2script::ast::Node const* type_value ) -> bool { if (type_key == "party_issues") { if (party_issues_found) { @@ -347,7 +348,7 @@ bool IssueManager::load_issues_file( party_issues_found = true; return expect_dictionary([this, &modifier_manager]( - std::string_view group_key, ast::NodeCPtr group_value + std::string_view group_key, ovdl::v2script::ast::Node const* group_value ) -> bool { PartyPolicyGroup* party_policy_group = party_policy_groups.get_item_by_identifier(group_key); @@ -357,14 +358,14 @@ bool IssueManager::load_issues_file( } return expect_dictionary([this, &modifier_manager, party_policy_group]( - std::string_view key, ast::NodeCPtr value + std::string_view key, ovdl::v2script::ast::Node const* value ) -> bool { return _load_party_policy(modifier_manager, key, *party_policy_group, value); })(group_value); })(type_value); } else { return expect_dictionary([this, &party_issues_found, &modifier_manager]( - std::string_view group_key, ast::NodeCPtr group_value + std::string_view group_key, ovdl::v2script::ast::Node const* group_value ) -> bool { ReformGroup* reform_group = reform_groups.get_item_by_identifier(group_key); @@ -376,7 +377,7 @@ bool IssueManager::load_issues_file( size_t ordinal = 0; return expect_dictionary([this, &modifier_manager, reform_group, &ordinal]( - std::string_view key, ast::NodeCPtr value + std::string_view key, ovdl::v2script::ast::Node const* value ) -> bool { if (key == "next_step_only" || key == "administrative") { return true; diff --git a/src/openvic-simulation/politics/IssueManager.hpp b/src/openvic-simulation/politics/IssueManager.hpp index 8f4e0dea2..358cbc503 100644 --- a/src/openvic-simulation/politics/IssueManager.hpp +++ b/src/openvic-simulation/politics/IssueManager.hpp @@ -1,5 +1,6 @@ #pragma once +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" #include "openvic-simulation/politics/BaseIssue.hpp" #include "openvic-simulation/politics/IssueGroup.hpp" #include "openvic-simulation/politics/PartyPolicy.hpp" @@ -19,17 +20,17 @@ namespace OpenVic { IdentifierRegistry IDENTIFIER_REGISTRY(reform_group); IdentifierRegistry IDENTIFIER_REGISTRY(reform); - bool _load_party_policy_group(size_t& expected_party_policies, std::string_view identifier, ast::NodeCPtr node); + bool _load_party_policy_group(size_t& expected_party_policies, std::string_view identifier, ovdl::v2script::ast::Node const* node); bool _load_party_policy( ModifierManager const& modifier_manager, std::string_view identifier, - PartyPolicyGroup& party_policy_group, ast::NodeCPtr node + PartyPolicyGroup& party_policy_group, ovdl::v2script::ast::Node const* node ); bool _load_reform_group( - size_t& expected_reforms, std::string_view identifier, ReformType& reform_type, ast::NodeCPtr node + size_t& expected_reforms, std::string_view identifier, ReformType& reform_type, ovdl::v2script::ast::Node const* node ); bool _load_reform( ModifierManager const& modifier_manager, size_t ordinal, - std::string_view identifier, ReformGroup& reform_group, ast::NodeCPtr node + std::string_view identifier, ReformGroup& reform_group, ovdl::v2script::ast::Node const* node ); public: @@ -54,7 +55,7 @@ namespace OpenVic { return {}; } constexpr NodeTools::NodeCallback auto expect_base_issue_group_identifier( - NodeTools::Callback auto callback, bool warn = false + Callback auto callback, bool warn = false ) const { return NodeTools::expect_identifier( [this, callback, warn](std::string_view identifier) -> bool { @@ -91,7 +92,7 @@ namespace OpenVic { size_t ordinal, fixed_point_t administrative_multiplier, RuleSet&& rules, Reform::tech_cost_t technology_cost, ConditionScript&& allow, ConditionScript&& on_execute_trigger, EffectScript&& on_execute_effect ); - bool load_issues_file(ModifierManager const& modifier_manager, ast::NodeCPtr root); + bool load_issues_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); bool parse_scripts(DefinitionManager const& definition_manager); }; diff --git a/src/openvic-simulation/politics/NationalFocus.cpp b/src/openvic-simulation/politics/NationalFocus.cpp index f62bd07d1..1976f8e68 100644 --- a/src/openvic-simulation/politics/NationalFocus.cpp +++ b/src/openvic-simulation/politics/NationalFocus.cpp @@ -1,13 +1,8 @@ #include "NationalFocus.hpp" -#include "openvic-simulation/economy/GoodDefinition.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" #include "openvic-simulation/politics/Ideology.hpp" -#include "openvic-simulation/population/PopManager.hpp" -#include "openvic-simulation/core/FormatValidate.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; NationalFocusGroup::NationalFocusGroup(std::string_view new_identifier) : HasIdentifier { new_identifier } {} @@ -44,153 +39,3 @@ bool NationalFocus::parse_scripts(DefinitionManager const& definition_manager) { spdlog::scope scope { fmt::format("national focus {}", get_identifier()) }; return limit.parse_script(true, definition_manager); } - -inline bool NationalFocusManager::add_national_focus_group(std::string_view identifier) { - if (identifier.empty()) { - spdlog::error_s("No identifier for national focus group!"); - return false; - } - - return national_focus_groups.emplace_item(identifier, identifier); -} - -inline bool NationalFocusManager::add_national_focus( - std::string_view identifier, - NationalFocusGroup const& group, - uint8_t icon, - bool has_flashpoint, - fixed_point_t flashpoint_tension, - bool own_provinces, - bool outliner_show_as_percent, - ModifierValue&& modifiers, - Ideology const* loyalty_ideology, - fixed_point_t loyalty_value, - fixed_point_t encourage_railroads, - fixed_point_map_t&& encourage_goods, - fixed_point_map_t&& encourage_pop_types, - ConditionScript&& limit -) { - spdlog::scope scope { fmt::format("national focus {}", identifier) }; - if (identifier.empty()) { - spdlog::error_s("No identifier for national focus!"); - return false; - } - - if (icon < 1) { - spdlog::error_s("Invalid icon {} for national focus {}", icon, identifier); - return false; - } - - if ((loyalty_ideology == nullptr) != (loyalty_value == 0)) { - spdlog::warn_s( - "Party loyalty incorrectly defined for national focus {}: ideology = {}, value = {}", - identifier, ovfmt::validate(loyalty_ideology), loyalty_value - ); - } - - return national_foci.emplace_item( - identifier, - identifier, group, icon, has_flashpoint, flashpoint_tension, own_provinces, outliner_show_as_percent, - std::move(modifiers), loyalty_ideology, loyalty_value, encourage_railroads, std::move(encourage_goods), - std::move(encourage_pop_types), std::move(limit) - ); -} - -bool NationalFocusManager::load_national_foci_file( - PopManager const& pop_manager, IdeologyManager const& ideology_manager, - GoodDefinitionManager const& good_definition_manager, ModifierManager const& modifier_manager, ast::NodeCPtr root -) { - spdlog::scope scope { "common/national_focus.txt" }; - size_t expected_national_foci = 0; - - bool ret = expect_dictionary_reserve_length( - national_focus_groups, - [this, &expected_national_foci](std::string_view identifier, ast::NodeCPtr node) -> bool { - return expect_length(add_variable_callback(expected_national_foci))(node) & add_national_focus_group(identifier); - } - )(root); - - lock_national_focus_groups(); - - reserve_more_national_foci(expected_national_foci); - - ret &= expect_national_focus_group_dictionary( - [this, &pop_manager, &ideology_manager, &good_definition_manager, &modifier_manager]( - NationalFocusGroup const& group, ast::NodeCPtr group_node - ) -> bool { - return expect_dictionary( - [this, &group, &pop_manager, &ideology_manager, &good_definition_manager, &modifier_manager]( - std::string_view identifier, ast::NodeCPtr node - ) -> bool { - spdlog::scope scope { fmt::format("national focus {}", identifier) }; - using enum scope_type_t; - - uint8_t icon = 0; - bool has_flashpoint = false, own_provinces = true, outliner_show_as_percent = false; - fixed_point_t flashpoint_tension = 0; - ModifierValue modifiers; - Ideology const* loyalty_ideology = nullptr; - fixed_point_t loyalty_value = 0; - fixed_point_t encourage_railroads = 0; - fixed_point_map_t encourage_goods; - fixed_point_map_t encourage_pop_types; - ConditionScript limit { - PROVINCE | COUNTRY, PROVINCE | COUNTRY, NO_SCOPE - }; - - auto expect_base_province_modifier_cb = modifier_manager.expect_base_province_modifier(modifiers); - bool ret = NodeTools::expect_dictionary_keys_and_default( - [&good_definition_manager, &encourage_goods, &pop_manager, &encourage_pop_types, &expect_base_province_modifier_cb]( - std::string_view key, ast::NodeCPtr value - ) mutable -> bool { - GoodDefinition const* good = good_definition_manager.get_good_definition_by_identifier(key); - if (good != nullptr) { - return expect_fixed_point(map_callback(encourage_goods, good))(value); - } - - PopType const* pop_type = pop_manager.get_pop_type_by_identifier(key); - if (pop_type != nullptr) { - return expect_fixed_point(map_callback(encourage_pop_types, pop_type))(value); - } - - return expect_base_province_modifier_cb(key, value) || key_value_invalid_callback(key, value); - }, - "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), - "ideology", ZERO_OR_ONE, - ideology_manager.expect_ideology_identifier(assign_variable_callback_pointer(loyalty_ideology)), - "loyalty_value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(loyalty_value)), - "limit", ZERO_OR_ONE, limit.expect_script(), - "has_flashpoint", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_flashpoint)), - "flashpoint_tension", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(flashpoint_tension)), - "own_provinces", ZERO_OR_ONE, expect_bool(assign_variable_callback(own_provinces)), - "outliner_show_as_percent", ZERO_OR_ONE, - expect_bool(assign_variable_callback(outliner_show_as_percent)), - "railroads", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(encourage_railroads)) - )(node); - - ret &= add_national_focus( - identifier, group, icon, has_flashpoint, flashpoint_tension, own_provinces, outliner_show_as_percent, - std::move(modifiers), loyalty_ideology, loyalty_value, encourage_railroads, - std::move(encourage_goods), std::move(encourage_pop_types), std::move(limit) - ); - - return ret; - } - )(group_node); - } - )(root); - - lock_national_foci(); - - return ret; -} - -bool NationalFocusManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - - for (NationalFocus& national_focus : national_foci.get_items()) { - ret &= national_focus.parse_scripts(definition_manager); - } - - return ret; -} diff --git a/src/openvic-simulation/politics/NationalFocus.hpp b/src/openvic-simulation/politics/NationalFocus.hpp index 4fccee361..9f08e5179 100644 --- a/src/openvic-simulation/politics/NationalFocus.hpp +++ b/src/openvic-simulation/politics/NationalFocus.hpp @@ -2,8 +2,9 @@ #include "openvic-simulation/modifier/Modifier.hpp" #include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { @@ -57,41 +58,4 @@ namespace OpenVic { ); NationalFocus(NationalFocus&&) = default; }; - - struct PopManager; - struct IdeologyManager; - struct GoodDefinitionManager; - - struct NationalFocusManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(national_focus_group); - IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(national_focus, national_foci); - - public: - inline bool add_national_focus_group(std::string_view identifier); - - inline bool add_national_focus( - std::string_view identifier, - NationalFocusGroup const& group, - uint8_t icon, - bool has_flashpoint, - fixed_point_t flashpoint_tension, - bool own_provinces, - bool outliner_show_as_percent, - ModifierValue&& modifiers, - Ideology const* loyalty_ideology, - fixed_point_t loyalty_value, - fixed_point_t encourage_railroads, - fixed_point_map_t&& encourage_goods, - fixed_point_map_t&& encourage_pop_types, - ConditionScript&& limit - ); - - bool load_national_foci_file( - PopManager const& pop_manager, IdeologyManager const& ideology_manager, - GoodDefinitionManager const& good_definition_manager, ModifierManager const& modifier_manager, ast::NodeCPtr root - ); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/politics/NationalFocusManager.cpp b/src/openvic-simulation/politics/NationalFocusManager.cpp new file mode 100644 index 000000000..9a9e09265 --- /dev/null +++ b/src/openvic-simulation/politics/NationalFocusManager.cpp @@ -0,0 +1,161 @@ +#include "NationalFocusManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/GoodDefinitionManager.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" +#include "openvic-simulation/politics/IdeologyManager.hpp" +#include "openvic-simulation/population/PopManager.hpp" +#include "openvic-simulation/core/FormatValidate.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +inline bool NationalFocusManager::add_national_focus_group(std::string_view identifier) { + if (identifier.empty()) { + spdlog::error_s("No identifier for national focus group!"); + return false; + } + + return national_focus_groups.emplace_item(identifier, identifier); +} + +inline bool NationalFocusManager::add_national_focus( + std::string_view identifier, + NationalFocusGroup const& group, + uint8_t icon, + bool has_flashpoint, + fixed_point_t flashpoint_tension, + bool own_provinces, + bool outliner_show_as_percent, + ModifierValue&& modifiers, + Ideology const* loyalty_ideology, + fixed_point_t loyalty_value, + fixed_point_t encourage_railroads, + fixed_point_map_t&& encourage_goods, + fixed_point_map_t&& encourage_pop_types, + ConditionScript&& limit +) { + spdlog::scope scope { fmt::format("national focus {}", identifier) }; + if (identifier.empty()) { + spdlog::error_s("No identifier for national focus!"); + return false; + } + + if (icon < 1) { + spdlog::error_s("Invalid icon {} for national focus {}", icon, identifier); + return false; + } + + if ((loyalty_ideology == nullptr) != (loyalty_value == 0)) { + spdlog::warn_s( + "Party loyalty incorrectly defined for national focus {}: ideology = {}, value = {}", + identifier, ovfmt::validate(loyalty_ideology), loyalty_value + ); + } + + return national_foci.emplace_item( + identifier, + identifier, group, icon, has_flashpoint, flashpoint_tension, own_provinces, outliner_show_as_percent, + std::move(modifiers), loyalty_ideology, loyalty_value, encourage_railroads, std::move(encourage_goods), + std::move(encourage_pop_types), std::move(limit) + ); +} + +bool NationalFocusManager::load_national_foci_file( + PopManager const& pop_manager, IdeologyManager const& ideology_manager, + GoodDefinitionManager const& good_definition_manager, ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root +) { + spdlog::scope scope { "common/national_focus.txt" }; + size_t expected_national_foci = 0; + + bool ret = expect_dictionary_reserve_length( + national_focus_groups, + [this, &expected_national_foci](std::string_view identifier, ovdl::v2script::ast::Node const* node) -> bool { + return expect_length(add_variable_callback(expected_national_foci))(node) & add_national_focus_group(identifier); + } + )(root); + + lock_national_focus_groups(); + + reserve_more_national_foci(expected_national_foci); + + ret &= expect_national_focus_group_dictionary( + [this, &pop_manager, &ideology_manager, &good_definition_manager, &modifier_manager]( + NationalFocusGroup const& group, ovdl::v2script::ast::Node const* group_node + ) -> bool { + return expect_dictionary( + [this, &group, &pop_manager, &ideology_manager, &good_definition_manager, &modifier_manager]( + std::string_view identifier, ovdl::v2script::ast::Node const* node + ) -> bool { + spdlog::scope scope { fmt::format("national focus {}", identifier) }; + using enum scope_type_t; + + uint8_t icon = 0; + bool has_flashpoint = false, own_provinces = true, outliner_show_as_percent = false; + fixed_point_t flashpoint_tension = 0; + ModifierValue modifiers; + Ideology const* loyalty_ideology = nullptr; + fixed_point_t loyalty_value = 0; + fixed_point_t encourage_railroads = 0; + fixed_point_map_t encourage_goods; + fixed_point_map_t encourage_pop_types; + ConditionScript limit { + PROVINCE | COUNTRY, PROVINCE | COUNTRY, NO_SCOPE + }; + + auto expect_base_province_modifier_cb = modifier_manager.expect_base_province_modifier(modifiers); + bool ret = NodeTools::expect_dictionary_keys_and_default( + [&good_definition_manager, &encourage_goods, &pop_manager, &encourage_pop_types, &expect_base_province_modifier_cb]( + std::string_view key, ovdl::v2script::ast::Node const* value + ) mutable -> bool { + GoodDefinition const* good = good_definition_manager.get_good_definition_by_identifier(key); + if (good != nullptr) { + return expect_fixed_point(map_callback(encourage_goods, good))(value); + } + + PopType const* pop_type = pop_manager.get_pop_type_by_identifier(key); + if (pop_type != nullptr) { + return expect_fixed_point(map_callback(encourage_pop_types, pop_type))(value); + } + + return expect_base_province_modifier_cb(key, value) || key_value_invalid_callback(key, value); + }, + "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), + "ideology", ZERO_OR_ONE, + ideology_manager.expect_ideology_identifier(assign_variable_callback_pointer(loyalty_ideology)), + "loyalty_value", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(loyalty_value)), + "limit", ZERO_OR_ONE, limit.expect_script(), + "has_flashpoint", ZERO_OR_ONE, expect_bool(assign_variable_callback(has_flashpoint)), + "flashpoint_tension", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(flashpoint_tension)), + "own_provinces", ZERO_OR_ONE, expect_bool(assign_variable_callback(own_provinces)), + "outliner_show_as_percent", ZERO_OR_ONE, + expect_bool(assign_variable_callback(outliner_show_as_percent)), + "railroads", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(encourage_railroads)) + )(node); + + ret &= add_national_focus( + identifier, group, icon, has_flashpoint, flashpoint_tension, own_provinces, outliner_show_as_percent, + std::move(modifiers), loyalty_ideology, loyalty_value, encourage_railroads, + std::move(encourage_goods), std::move(encourage_pop_types), std::move(limit) + ); + + return ret; + } + )(group_node); + } + )(root); + + lock_national_foci(); + + return ret; +} + +bool NationalFocusManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + + for (NationalFocus& national_focus : national_foci.get_items()) { + ret &= national_focus.parse_scripts(definition_manager); + } + + return ret; +} diff --git a/src/openvic-simulation/politics/NationalFocusManager.hpp b/src/openvic-simulation/politics/NationalFocusManager.hpp new file mode 100644 index 000000000..78f72ead2 --- /dev/null +++ b/src/openvic-simulation/politics/NationalFocusManager.hpp @@ -0,0 +1,44 @@ +#pragma once + +#include "openvic-simulation/politics/NationalFocus.hpp" +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct PopManager; + struct IdeologyManager; + struct GoodDefinitionManager; + + struct NationalFocusManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(national_focus_group); + IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(national_focus, national_foci); + + public: + inline bool add_national_focus_group(std::string_view identifier); + + inline bool add_national_focus( + std::string_view identifier, + NationalFocusGroup const& group, + uint8_t icon, + bool has_flashpoint, + fixed_point_t flashpoint_tension, + bool own_provinces, + bool outliner_show_as_percent, + ModifierValue&& modifiers, + Ideology const* loyalty_ideology, + fixed_point_t loyalty_value, + fixed_point_t encourage_railroads, + fixed_point_map_t&& encourage_goods, + fixed_point_map_t&& encourage_pop_types, + ConditionScript&& limit + ); + + bool load_national_foci_file( + PopManager const& pop_manager, IdeologyManager const& ideology_manager, + GoodDefinitionManager const& good_definition_manager, ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root + ); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/politics/NationalValue.cpp b/src/openvic-simulation/politics/NationalValue.cpp index ef9bbee86..3a84ace64 100644 --- a/src/openvic-simulation/politics/NationalValue.cpp +++ b/src/openvic-simulation/politics/NationalValue.cpp @@ -1,43 +1,6 @@ #include "NationalValue.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; NationalValue::NationalValue(std::string_view new_identifier, ModifierValue&& new_modifiers) : Modifier { new_identifier, std::move(new_modifiers), modifier_type_t::NATIONAL_VALUE } {} - -bool NationalValueManager::add_national_value(std::string_view identifier, ModifierValue&& modifiers) { - if (identifier.empty()) { - spdlog::error_s("Invalid national value identifier - empty!"); - return false; - } - - return national_values.emplace_item( - identifier, - identifier, std::move(modifiers) - ); -} - -bool NationalValueManager::load_national_values_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) { - spdlog::scope scope { "common/nationalvalues.txt" }; - bool ret = expect_dictionary_reserve_length( - national_values, - [this, &modifier_manager](std::string_view national_value_identifier, ast::NodeCPtr value) -> bool { - spdlog::scope scope { fmt::format("national value {}", national_value_identifier) }; - ModifierValue modifiers; - bool ret = NodeTools::expect_dictionary( - modifier_manager.expect_base_country_modifier(modifiers) - )(value); - - ret &= add_national_value(national_value_identifier, std::move(modifiers)); - - return ret; - } - )(root); - - lock_national_values(); - - return ret; -} diff --git a/src/openvic-simulation/politics/NationalValue.hpp b/src/openvic-simulation/politics/NationalValue.hpp index e7b5bb0a8..439925047 100644 --- a/src/openvic-simulation/politics/NationalValue.hpp +++ b/src/openvic-simulation/politics/NationalValue.hpp @@ -1,7 +1,6 @@ #pragma once #include "openvic-simulation/modifier/Modifier.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { struct NationalValueManager; @@ -11,14 +10,4 @@ namespace OpenVic { NationalValue(std::string_view new_identifier, ModifierValue&& new_modifiers); NationalValue(NationalValue&&) = default; }; - - struct NationalValueManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(national_value); - - public: - bool add_national_value(std::string_view identifier, ModifierValue&& modifiers); - - bool load_national_values_file(ModifierManager const& modifier_manager, ast::NodeCPtr root); - }; } diff --git a/src/openvic-simulation/politics/NationalValueManager.cpp b/src/openvic-simulation/politics/NationalValueManager.cpp new file mode 100644 index 000000000..a71523e2f --- /dev/null +++ b/src/openvic-simulation/politics/NationalValueManager.cpp @@ -0,0 +1,41 @@ +#include "NationalValueManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool NationalValueManager::add_national_value(std::string_view identifier, ModifierValue&& modifiers) { + if (identifier.empty()) { + spdlog::error_s("Invalid national value identifier - empty!"); + return false; + } + + return national_values.emplace_item( + identifier, + identifier, std::move(modifiers) + ); +} + +bool NationalValueManager::load_national_values_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root) { + spdlog::scope scope { "common/nationalvalues.txt" }; + bool ret = expect_dictionary_reserve_length( + national_values, + [this, &modifier_manager](std::string_view national_value_identifier, ovdl::v2script::ast::Node const* value) -> bool { + spdlog::scope scope { fmt::format("national value {}", national_value_identifier) }; + ModifierValue modifiers; + bool ret = NodeTools::expect_dictionary( + modifier_manager.expect_base_country_modifier(modifiers) + )(value); + + ret &= add_national_value(national_value_identifier, std::move(modifiers)); + + return ret; + } + )(root); + + lock_national_values(); + + return ret; +} diff --git a/src/openvic-simulation/politics/NationalValueManager.hpp b/src/openvic-simulation/politics/NationalValueManager.hpp new file mode 100644 index 000000000..a3fa3f5fd --- /dev/null +++ b/src/openvic-simulation/politics/NationalValueManager.hpp @@ -0,0 +1,16 @@ +#pragma once + +#include "openvic-simulation/politics/NationalValue.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct NationalValueManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(national_value); + + public: + bool add_national_value(std::string_view identifier, ModifierValue&& modifiers); + + bool load_national_values_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); + }; +} diff --git a/src/openvic-simulation/politics/PoliticsManager.hpp b/src/openvic-simulation/politics/PoliticsManager.hpp index 1c9b750bb..3d3168480 100644 --- a/src/openvic-simulation/politics/PoliticsManager.hpp +++ b/src/openvic-simulation/politics/PoliticsManager.hpp @@ -1,11 +1,11 @@ #pragma once -#include "openvic-simulation/politics/Government.hpp" -#include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/politics/GovernmentManager.hpp" +#include "openvic-simulation/politics/IdeologyManager.hpp" #include "openvic-simulation/politics/IssueManager.hpp" -#include "openvic-simulation/politics/NationalFocus.hpp" -#include "openvic-simulation/politics/NationalValue.hpp" -#include "openvic-simulation/politics/Rebel.hpp" +#include "openvic-simulation/politics/NationalFocusManager.hpp" +#include "openvic-simulation/politics/NationalValueManager.hpp" +#include "openvic-simulation/politics/RebelManager.hpp" namespace OpenVic { struct PoliticsManager { @@ -18,21 +18,21 @@ namespace OpenVic { RebelManager PROPERTY_REF(rebel_manager); public: - inline bool load_government_types_file(ast::NodeCPtr root) { + inline bool load_government_types_file(ovdl::v2script::ast::Node const* root) { return government_type_manager.load_government_types_file(ideology_manager, root); } inline bool load_national_foci_file( PopManager const& pop_manager, GoodDefinitionManager const& good_definition_manager, - ModifierManager const& modifier_manager, ast::NodeCPtr root + ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root ) { return national_focus_manager.load_national_foci_file( pop_manager, ideology_manager, good_definition_manager, modifier_manager, root ); } - inline bool load_rebels_file(ast::NodeCPtr root) { + inline bool load_rebels_file(ovdl::v2script::ast::Node const* root) { return rebel_manager.load_rebels_file(ideology_manager, government_type_manager, root); } - inline bool load_issues_file(ModifierManager const& modifier_manager, ast::NodeCPtr root) { + inline bool load_issues_file(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root) { return issue_manager.load_issues_file(modifier_manager, root); } }; diff --git a/src/openvic-simulation/politics/Rebel.cpp b/src/openvic-simulation/politics/Rebel.cpp index df65fb2f2..65a9410df 100644 --- a/src/openvic-simulation/politics/Rebel.cpp +++ b/src/openvic-simulation/politics/Rebel.cpp @@ -4,10 +4,8 @@ #include "openvic-simulation/politics/Government.hpp" #include "openvic-simulation/politics/Ideology.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; RebelType::RebelType( index_t new_index, std::string_view new_identifier, @@ -44,190 +42,3 @@ bool RebelType::parse_scripts(DefinitionManager const& definition_manager) { ret &= demands_enforced_effect.parse_script(true, definition_manager); return ret; } - -bool RebelManager::add_rebel_type( - std::string_view new_identifier, RebelType::icon_t icon, RebelType::area_t area, bool break_alliance_on_win, - RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, - RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, - bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, bool reinforcing, - bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, ConditionalWeightFactorMul&& will_rise, - ConditionalWeightFactorMul&& spawn_chance, ConditionalWeightFactorMul&& movement_evaluation, - ConditionScript&& siege_won_trigger, EffectScript&& siege_won_effect, ConditionScript&& demands_enforced_trigger, - EffectScript&& demands_enforced_effect -) { - if (new_identifier.empty()) { - spdlog::error_s("Invalid rebel type identifier - empty!"); - return false; - } - - return rebel_types.emplace_item( - new_identifier, - RebelType::index_t { get_rebel_type_count() }, new_identifier, - icon, area, break_alliance_on_win, std::move(desired_governments), defection, independence, - defect_delay, ideology, allow_all_cultures, allow_all_culture_groups, allow_all_religions, allow_all_ideologies, - resilient, reinforcing, general, smart, unit_transfer, occupation_mult, std::move(will_rise), std::move(spawn_chance), - std::move(movement_evaluation), std::move(siege_won_trigger), std::move(siege_won_effect), - std::move(demands_enforced_trigger), std::move(demands_enforced_effect) - ); -} - -bool RebelManager::load_rebels_file( - IdeologyManager const& ideology_manager, GovernmentTypeManager const& government_type_manager, ast::NodeCPtr root -) { - spdlog::scope scope { "common/rebel_types.txt" }; - - static const string_map_t area_map = { - { "nation", RebelType::area_t::NATION }, - { "nation_religion", RebelType::area_t::NATION_RELIGION }, - { "nation_culture", RebelType::area_t::NATION_CULTURE }, - { "culture", RebelType::area_t::CULTURE }, - { "culture_group", RebelType::area_t::CULTURE_GROUP }, - { "religion", RebelType::area_t::RELIGION }, - { "all", RebelType::area_t::ALL } - }; - - static const string_map_t defection_map = { - { "none", RebelType::defection_t::NONE }, - { "culture", RebelType::defection_t::CULTURE }, - { "culture_group", RebelType::defection_t::CULTURE_GROUP }, - { "religion", RebelType::defection_t::RELIGION }, - { "ideology", RebelType::defection_t::IDEOLOGY }, - { "pan_nationalist", RebelType::defection_t::PAN_NATIONALIST }, - { "any", RebelType::defection_t::ANY } - }; - - static const string_map_t independence_map = { - { "none", RebelType::independence_t::NONE }, - { "culture", RebelType::independence_t::CULTURE }, - { "culture_group", RebelType::independence_t::CULTURE_GROUP }, - { "religion", RebelType::independence_t::RELIGION }, - { "colonial", RebelType::independence_t::COLONIAL }, - { "pan_nationalist", RebelType::independence_t::PAN_NATIONALIST }, - { "any", RebelType::independence_t::ANY } - }; - - bool ret = expect_dictionary_reserve_length( - rebel_types, - [this, &ideology_manager, &government_type_manager](std::string_view identifier, ast::NodeCPtr node) -> bool { - using enum scope_type_t; - - spdlog::scope scope { fmt::format("rebel type {}", identifier) }; - - RebelType::icon_t icon = 0; - RebelType::area_t area = RebelType::area_t::ALL; - RebelType::government_map_t desired_governments; - RebelType::defection_t defection = RebelType::defection_t::NONE; - RebelType::independence_t independence = RebelType::independence_t::NONE; - uint16_t defect_delay = 0; - Ideology const* ideology = nullptr; - bool break_alliance_on_win = false, allow_all_cultures = true, allow_all_culture_groups = true, - allow_all_religions = true, allow_all_ideologies = true, resilient = true, reinforcing = true, general = true, - smart = true, unit_transfer = false; - fixed_point_t occupation_mult = 0; - ConditionalWeightFactorMul will_rise { POP, COUNTRY, NO_SCOPE }; - ConditionalWeightFactorMul spawn_chance { POP, POP, NO_SCOPE }; - ConditionalWeightFactorMul movement_evaluation { PROVINCE, PROVINCE, NO_SCOPE }; - ConditionScript siege_won_trigger { PROVINCE, PROVINCE, NO_SCOPE }; - ConditionScript demands_enforced_trigger { COUNTRY, COUNTRY, NO_SCOPE }; - EffectScript siege_won_effect, demands_enforced_effect; - - bool ret = expect_dictionary_keys( - "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), - "area", ONE_EXACTLY, expect_identifier(expect_mapped_string(area_map, assign_variable_callback(area))), - "break_alliance_on_win", ZERO_OR_ONE, expect_bool(assign_variable_callback(break_alliance_on_win)), - "government", ONE_EXACTLY, government_type_manager.expect_government_type_dictionary_reserve_length( - desired_governments, - [this, &government_type_manager, &desired_governments]( - GovernmentType const& from, ast::NodeCPtr value - ) -> bool { - return government_type_manager.expect_government_type_identifier( - [&desired_governments, &from](GovernmentType const& to) -> bool { - return map_callback(desired_governments, &from)(&to); - } - )(value); - } - ), - "defection", ZERO_OR_ONE, - expect_identifier(expect_mapped_string(defection_map, assign_variable_callback(defection))), - "independence", ZERO_OR_ONE, - expect_identifier(expect_mapped_string(independence_map, assign_variable_callback(independence))), - "defect_delay", ONE_EXACTLY, expect_uint(assign_variable_callback(defect_delay)), - "ideology", ZERO_OR_ONE, - ideology_manager.expect_ideology_identifier(assign_variable_callback_pointer(ideology)), - "allow_all_cultures", ONE_EXACTLY, expect_bool(assign_variable_callback(allow_all_cultures)), - "allow_all_culture_groups", ZERO_OR_ONE, expect_bool(assign_variable_callback(allow_all_culture_groups)), - "allow_all_religions", ONE_EXACTLY, expect_bool(assign_variable_callback(allow_all_religions)), - "allow_all_ideologies", ONE_EXACTLY, expect_bool(assign_variable_callback(allow_all_ideologies)), - "resilient", ONE_EXACTLY, expect_bool(assign_variable_callback(resilient)), - "reinforcing", ONE_EXACTLY, expect_bool(assign_variable_callback(reinforcing)), - "general", ONE_EXACTLY, expect_bool(assign_variable_callback(general)), - "smart", ONE_EXACTLY, expect_bool(assign_variable_callback(smart)), - "unit_transfer", ONE_EXACTLY, expect_bool(assign_variable_callback(unit_transfer)), - "occupation_mult", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(occupation_mult)), - "will_rise", ONE_EXACTLY, will_rise.expect_conditional_weight(), - "spawn_chance", ONE_EXACTLY, spawn_chance.expect_conditional_weight(), - "movement_evaluation", ONE_EXACTLY, movement_evaluation.expect_conditional_weight(), - "siege_won_trigger", ZERO_OR_ONE, siege_won_trigger.expect_script(), - "siege_won_effect", ZERO_OR_ONE, siege_won_effect.expect_script(), - "demands_enforced_trigger", ZERO_OR_ONE, demands_enforced_trigger.expect_script(), - "demands_enforced_effect", ZERO_OR_ONE, demands_enforced_effect.expect_script() - )(node); - - ret &= add_rebel_type( - identifier, icon, area, break_alliance_on_win, std::move(desired_governments), defection, independence, - defect_delay, ideology, allow_all_cultures, allow_all_culture_groups, allow_all_religions, - allow_all_ideologies, resilient, reinforcing, general, smart, unit_transfer, occupation_mult, - std::move(will_rise), std::move(spawn_chance), std::move(movement_evaluation), std::move(siege_won_trigger), - std::move(siege_won_effect), std::move(demands_enforced_trigger), std::move(demands_enforced_effect) - ); - - return ret; - } - )(root); - - lock_rebel_types(); - - return ret; -} - -bool RebelManager::generate_modifiers(ModifierManager& modifier_manager) const { - using enum ModifierEffect::format_t; - using enum ModifierEffect::target_t; - - bool ret = true; - - static constexpr std::string_view identifier = "rebel_org_gain"; - - ret &= modifier_manager.register_complex_modifier(identifier); - - ret &= modifier_manager.register_technology_modifier_effect( - modifier_manager.modifier_effect_cache.rebel_org_gain_all, ModifierManager::get_flat_identifier(identifier, "all"), - FORMAT_x100_2DP_PC_NEG, "TECH_REBEL_ORG_GAIN" - ); - - auto& rebel_org_gain_effects = modifier_manager.modifier_effect_cache.rebel_org_gain_effects; - rebel_org_gain_effects = std::move( - decltype(ModifierEffectCache::rebel_org_gain_effects) { - generate_values, - rebel_type_index_t(get_rebel_type_count()) - } - ); - - for (RebelType const& rebel_type : get_rebel_types()) { - ret &= modifier_manager.register_technology_modifier_effect( - rebel_org_gain_effects[rebel_type.index], - ModifierManager::get_flat_identifier(identifier, rebel_type.get_identifier()), - FORMAT_x100_2DP_PC_NEG, - memory::fmt::format("${}_title$ $TECH_REBEL_ORG_GAIN$", rebel_type) - ); - } - return ret; -} - -bool RebelManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - for (RebelType& rebel_type : rebel_types.get_items()) { - ret &= rebel_type.parse_scripts(definition_manager); - } - return ret; -} diff --git a/src/openvic-simulation/politics/Rebel.hpp b/src/openvic-simulation/politics/Rebel.hpp index efccb83f7..d31ff4639 100644 --- a/src/openvic-simulation/politics/Rebel.hpp +++ b/src/openvic-simulation/politics/Rebel.hpp @@ -6,17 +6,12 @@ #include "openvic-simulation/scripts/EffectScript.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { struct GovernmentType; - struct GovernmentTypeManager; struct Ideology; - struct IdeologyManager; - struct ModifierManager; - struct RebelManager; struct RebelType : HasIndex, HasIdentifier { friend struct RebelManager; @@ -84,28 +79,4 @@ namespace OpenVic { ); RebelType(RebelType&&) = default; }; - - struct RebelManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(rebel_type); - - public: - bool add_rebel_type( - std::string_view new_identifier, RebelType::icon_t icon, RebelType::area_t area, bool break_alliance_on_win, - RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, - RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, - bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, - bool reinforcing, bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, - ConditionalWeightFactorMul&& will_rise, ConditionalWeightFactorMul&& spawn_chance, - ConditionalWeightFactorMul&& movement_evaluation, ConditionScript&& siege_won_trigger, - EffectScript&& siege_won_effect, ConditionScript&& demands_enforced_trigger, EffectScript&& demands_enforced_effect - ); - - bool load_rebels_file( - IdeologyManager const& ideology_manager, GovernmentTypeManager const& government_type_manager, ast::NodeCPtr root - ); - bool generate_modifiers(ModifierManager& modifier_manager) const; - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } \ No newline at end of file diff --git a/src/openvic-simulation/politics/RebelManager.cpp b/src/openvic-simulation/politics/RebelManager.cpp new file mode 100644 index 000000000..3ccca5609 --- /dev/null +++ b/src/openvic-simulation/politics/RebelManager.cpp @@ -0,0 +1,198 @@ +#include "RebelManager.hpp" + +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/politics/GovernmentManager.hpp" +#include "openvic-simulation/politics/IdeologyManager.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool RebelManager::add_rebel_type( + std::string_view new_identifier, RebelType::icon_t icon, RebelType::area_t area, bool break_alliance_on_win, + RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, + RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, + bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, bool reinforcing, + bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, ConditionalWeightFactorMul&& will_rise, + ConditionalWeightFactorMul&& spawn_chance, ConditionalWeightFactorMul&& movement_evaluation, + ConditionScript&& siege_won_trigger, EffectScript&& siege_won_effect, ConditionScript&& demands_enforced_trigger, + EffectScript&& demands_enforced_effect +) { + if (new_identifier.empty()) { + spdlog::error_s("Invalid rebel type identifier - empty!"); + return false; + } + + return rebel_types.emplace_item( + new_identifier, + RebelType::index_t { get_rebel_type_count() }, new_identifier, + icon, area, break_alliance_on_win, std::move(desired_governments), defection, independence, + defect_delay, ideology, allow_all_cultures, allow_all_culture_groups, allow_all_religions, allow_all_ideologies, + resilient, reinforcing, general, smart, unit_transfer, occupation_mult, std::move(will_rise), std::move(spawn_chance), + std::move(movement_evaluation), std::move(siege_won_trigger), std::move(siege_won_effect), + std::move(demands_enforced_trigger), std::move(demands_enforced_effect) + ); +} + +bool RebelManager::load_rebels_file( + IdeologyManager const& ideology_manager, GovernmentTypeManager const& government_type_manager, ovdl::v2script::ast::Node const* root +) { + spdlog::scope scope { "common/rebel_types.txt" }; + + static const string_map_t area_map = { + { "nation", RebelType::area_t::NATION }, + { "nation_religion", RebelType::area_t::NATION_RELIGION }, + { "nation_culture", RebelType::area_t::NATION_CULTURE }, + { "culture", RebelType::area_t::CULTURE }, + { "culture_group", RebelType::area_t::CULTURE_GROUP }, + { "religion", RebelType::area_t::RELIGION }, + { "all", RebelType::area_t::ALL } + }; + + static const string_map_t defection_map = { + { "none", RebelType::defection_t::NONE }, + { "culture", RebelType::defection_t::CULTURE }, + { "culture_group", RebelType::defection_t::CULTURE_GROUP }, + { "religion", RebelType::defection_t::RELIGION }, + { "ideology", RebelType::defection_t::IDEOLOGY }, + { "pan_nationalist", RebelType::defection_t::PAN_NATIONALIST }, + { "any", RebelType::defection_t::ANY } + }; + + static const string_map_t independence_map = { + { "none", RebelType::independence_t::NONE }, + { "culture", RebelType::independence_t::CULTURE }, + { "culture_group", RebelType::independence_t::CULTURE_GROUP }, + { "religion", RebelType::independence_t::RELIGION }, + { "colonial", RebelType::independence_t::COLONIAL }, + { "pan_nationalist", RebelType::independence_t::PAN_NATIONALIST }, + { "any", RebelType::independence_t::ANY } + }; + + bool ret = expect_dictionary_reserve_length( + rebel_types, + [this, &ideology_manager, &government_type_manager](std::string_view identifier, ovdl::v2script::ast::Node const* node) -> bool { + using enum scope_type_t; + + spdlog::scope scope { fmt::format("rebel type {}", identifier) }; + + RebelType::icon_t icon = 0; + RebelType::area_t area = RebelType::area_t::ALL; + RebelType::government_map_t desired_governments; + RebelType::defection_t defection = RebelType::defection_t::NONE; + RebelType::independence_t independence = RebelType::independence_t::NONE; + uint16_t defect_delay = 0; + Ideology const* ideology = nullptr; + bool break_alliance_on_win = false, allow_all_cultures = true, allow_all_culture_groups = true, + allow_all_religions = true, allow_all_ideologies = true, resilient = true, reinforcing = true, general = true, + smart = true, unit_transfer = false; + fixed_point_t occupation_mult = 0; + ConditionalWeightFactorMul will_rise { POP, COUNTRY, NO_SCOPE }; + ConditionalWeightFactorMul spawn_chance { POP, POP, NO_SCOPE }; + ConditionalWeightFactorMul movement_evaluation { PROVINCE, PROVINCE, NO_SCOPE }; + ConditionScript siege_won_trigger { PROVINCE, PROVINCE, NO_SCOPE }; + ConditionScript demands_enforced_trigger { COUNTRY, COUNTRY, NO_SCOPE }; + EffectScript siege_won_effect, demands_enforced_effect; + + bool ret = expect_dictionary_keys( + "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), + "area", ONE_EXACTLY, expect_identifier(expect_mapped_string(area_map, assign_variable_callback(area))), + "break_alliance_on_win", ZERO_OR_ONE, expect_bool(assign_variable_callback(break_alliance_on_win)), + "government", ONE_EXACTLY, government_type_manager.expect_government_type_dictionary_reserve_length( + desired_governments, + [this, &government_type_manager, &desired_governments]( + GovernmentType const& from, ovdl::v2script::ast::Node const* value + ) -> bool { + return government_type_manager.expect_government_type_identifier( + [&desired_governments, &from](GovernmentType const& to) -> bool { + return map_callback(desired_governments, &from)(&to); + } + )(value); + } + ), + "defection", ZERO_OR_ONE, + expect_identifier(expect_mapped_string(defection_map, assign_variable_callback(defection))), + "independence", ZERO_OR_ONE, + expect_identifier(expect_mapped_string(independence_map, assign_variable_callback(independence))), + "defect_delay", ONE_EXACTLY, expect_uint(assign_variable_callback(defect_delay)), + "ideology", ZERO_OR_ONE, + ideology_manager.expect_ideology_identifier(assign_variable_callback_pointer(ideology)), + "allow_all_cultures", ONE_EXACTLY, expect_bool(assign_variable_callback(allow_all_cultures)), + "allow_all_culture_groups", ZERO_OR_ONE, expect_bool(assign_variable_callback(allow_all_culture_groups)), + "allow_all_religions", ONE_EXACTLY, expect_bool(assign_variable_callback(allow_all_religions)), + "allow_all_ideologies", ONE_EXACTLY, expect_bool(assign_variable_callback(allow_all_ideologies)), + "resilient", ONE_EXACTLY, expect_bool(assign_variable_callback(resilient)), + "reinforcing", ONE_EXACTLY, expect_bool(assign_variable_callback(reinforcing)), + "general", ONE_EXACTLY, expect_bool(assign_variable_callback(general)), + "smart", ONE_EXACTLY, expect_bool(assign_variable_callback(smart)), + "unit_transfer", ONE_EXACTLY, expect_bool(assign_variable_callback(unit_transfer)), + "occupation_mult", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(occupation_mult)), + "will_rise", ONE_EXACTLY, will_rise.expect_conditional_weight(), + "spawn_chance", ONE_EXACTLY, spawn_chance.expect_conditional_weight(), + "movement_evaluation", ONE_EXACTLY, movement_evaluation.expect_conditional_weight(), + "siege_won_trigger", ZERO_OR_ONE, siege_won_trigger.expect_script(), + "siege_won_effect", ZERO_OR_ONE, siege_won_effect.expect_script(), + "demands_enforced_trigger", ZERO_OR_ONE, demands_enforced_trigger.expect_script(), + "demands_enforced_effect", ZERO_OR_ONE, demands_enforced_effect.expect_script() + )(node); + + ret &= add_rebel_type( + identifier, icon, area, break_alliance_on_win, std::move(desired_governments), defection, independence, + defect_delay, ideology, allow_all_cultures, allow_all_culture_groups, allow_all_religions, + allow_all_ideologies, resilient, reinforcing, general, smart, unit_transfer, occupation_mult, + std::move(will_rise), std::move(spawn_chance), std::move(movement_evaluation), std::move(siege_won_trigger), + std::move(siege_won_effect), std::move(demands_enforced_trigger), std::move(demands_enforced_effect) + ); + + return ret; + } + )(root); + + lock_rebel_types(); + + return ret; +} + +bool RebelManager::generate_modifiers(ModifierManager& modifier_manager) const { + using enum ModifierEffect::format_t; + using enum ModifierEffect::target_t; + + bool ret = true; + + static constexpr std::string_view identifier = "rebel_org_gain"; + + ret &= modifier_manager.register_complex_modifier(identifier); + + ret &= modifier_manager.register_technology_modifier_effect( + modifier_manager.modifier_effect_cache.rebel_org_gain_all, ModifierManager::get_flat_identifier(identifier, "all"), + FORMAT_x100_2DP_PC_NEG, "TECH_REBEL_ORG_GAIN" + ); + + auto& rebel_org_gain_effects = modifier_manager.modifier_effect_cache.rebel_org_gain_effects; + rebel_org_gain_effects = std::move( + decltype(ModifierEffectCache::rebel_org_gain_effects) { + generate_values, + rebel_type_index_t(get_rebel_type_count()) + } + ); + + for (RebelType const& rebel_type : get_rebel_types()) { + ret &= modifier_manager.register_technology_modifier_effect( + rebel_org_gain_effects[rebel_type.index], + ModifierManager::get_flat_identifier(identifier, rebel_type.get_identifier()), + FORMAT_x100_2DP_PC_NEG, + memory::fmt::format("${}_title$ $TECH_REBEL_ORG_GAIN$", rebel_type) + ); + } + return ret; +} + +bool RebelManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + for (RebelType& rebel_type : rebel_types.get_items()) { + ret &= rebel_type.parse_scripts(definition_manager); + } + return ret; +} diff --git a/src/openvic-simulation/politics/RebelManager.hpp b/src/openvic-simulation/politics/RebelManager.hpp new file mode 100644 index 000000000..ca99f779c --- /dev/null +++ b/src/openvic-simulation/politics/RebelManager.hpp @@ -0,0 +1,38 @@ +#pragma once + +#include + +#include "openvic-simulation/politics/Rebel.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/scripts/EffectScript.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct GovernmentTypeManager; + struct IdeologyManager; + struct ModifierManager; + + struct RebelManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(rebel_type); + + public: + bool add_rebel_type( + std::string_view new_identifier, RebelType::icon_t icon, RebelType::area_t area, bool break_alliance_on_win, + RebelType::government_map_t&& desired_governments, RebelType::defection_t defection, + RebelType::independence_t independence, uint16_t defect_delay, Ideology const* ideology, bool allow_all_cultures, + bool allow_all_culture_groups, bool allow_all_religions, bool allow_all_ideologies, bool resilient, + bool reinforcing, bool general, bool smart, bool unit_transfer, fixed_point_t occupation_mult, + ConditionalWeightFactorMul&& will_rise, ConditionalWeightFactorMul&& spawn_chance, + ConditionalWeightFactorMul&& movement_evaluation, ConditionScript&& siege_won_trigger, + EffectScript&& siege_won_effect, ConditionScript&& demands_enforced_trigger, EffectScript&& demands_enforced_effect + ); + + bool load_rebels_file( + IdeologyManager const& ideology_manager, GovernmentTypeManager const& government_type_manager, ovdl::v2script::ast::Node const* root + ); + bool generate_modifiers(ModifierManager& modifier_manager) const; + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/politics/RuleSet.hpp b/src/openvic-simulation/politics/RuleSet.hpp index b27d50eaf..3b25d2fad 100644 --- a/src/openvic-simulation/politics/RuleSet.hpp +++ b/src/openvic-simulation/politics/RuleSet.hpp @@ -6,7 +6,7 @@ #include "openvic-simulation/core/string/StringLiteral.hpp" #include "openvic-simulation/core/string/Utility.hpp" #include "openvic-simulation/core/Typedefs.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" #include "openvic-simulation/types/OptionalBool.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/utility/Logger.hpp" @@ -163,7 +163,7 @@ namespace OpenVic { #define COMMA , static NodeTools::node_callback_t expect_rule_set(NodeTools::callback_t ruleset_callback) { - return [ruleset_callback](ast::NodeCPtr root) mutable -> bool { + return [ruleset_callback](ovdl::v2script::ast::Node const* root) mutable -> bool { RuleSet ruleset; using enum NodeTools::dictionary_entry_t::expected_count_t; bool outer_result = NodeTools::expect_dictionary_keys( diff --git a/src/openvic-simulation/population/Culture.cpp b/src/openvic-simulation/population/Culture.cpp index 3880ee4a0..f113789b1 100644 --- a/src/openvic-simulation/population/Culture.cpp +++ b/src/openvic-simulation/population/Culture.cpp @@ -1,15 +1,10 @@ #include "Culture.hpp" + #include -#include "openvic-simulation/country/CountryDefinition.hpp" -#include "openvic-simulation/dataloader/Dataloader.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/Colour.hpp" -#include "openvic-simulation/core/string/Utility.hpp" -#include "openvic-simulation/core/Typedefs.hpp" using namespace OpenVic; -using namespace OpenVic::NodeTools; GraphicalCultureType::GraphicalCultureType(std::string_view new_identifier) : HasIdentifier { new_identifier } {} @@ -20,321 +15,8 @@ CultureGroup::CultureGroup( is_overseas { new_is_overseas }, union_country { new_union_country } {} Culture::Culture( - std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, name_list_t&& new_first_names, - name_list_t&& new_last_names, fixed_point_t new_radicalism, CountryDefinition const* new_primary_country + std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, memory::vector&& new_first_names, + memory::vector&& new_last_names, fixed_point_t new_radicalism, CountryDefinition const* new_primary_country ) : HasIdentifierAndColour { new_identifier, new_colour, false }, group { new_group }, first_names { std::move(new_first_names) }, last_names { std::move(new_last_names) }, radicalism { new_radicalism }, primary_country { new_primary_country } {} - -CultureManager::CultureManager() : default_graphical_culture_type { nullptr } {} - -bool CultureManager::add_graphical_culture_type(std::string_view identifier) { - if (identifier.empty()) { - spdlog::error_s("Invalid graphical culture type identifier - empty!"); - return false; - } - return graphical_culture_types.emplace_item( - identifier, - identifier - ); -} - -bool CultureManager::add_culture_group( - std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, bool is_overseas, - CountryDefinition const* union_country -) { - if (!graphical_culture_types.is_locked()) { - spdlog::error_s("Cannot register culture groups until graphical culture types are locked!"); - return false; - } - if (OV_unlikely(identifier.empty())) { - spdlog::error_s("Invalid culture group identifier - empty!"); - return false; - } - if (graphical_culture_type == nullptr) { - spdlog::error_s("Null graphical culture type for {}", identifier); - return false; - } - - if (leader.empty()) { - std::string_view default_leader {}; - if (culture_groups.empty()) { - default_leader = "NOLEADER"; - } else { - default_leader = culture_groups.front().leader; - } - leader = default_leader; - spdlog::warn_s( - "In culture group \"{}\" - group leader is undefined, set to default of: \"{}\".", - identifier, default_leader - ); - } - if (!culture_groups.emplace_item( - identifier, - identifier, leader, *graphical_culture_type, is_overseas, union_country - )) { - return false; - } - - leader_picture_counts.emplace(leader, general_admiral_picture_count_t { 0, 0 }); - return true; -} - -bool CultureManager::add_culture( - std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names, - name_list_t&& last_names, fixed_point_t radicalism, CountryDefinition const* primary_country -) { - if (!culture_groups.is_locked()) { - spdlog::error_s("Cannot register cultures until culture groups are locked!"); - return false; - } - if (identifier.empty()) { - spdlog::error_s("Invalid culture identifier - empty!"); - return false; - } - - // TODO - check radicalism range - - return cultures.emplace_item( - identifier, - identifier, colour, group, std::move(first_names), std::move(last_names), radicalism, primary_country - ); -} - -bool CultureManager::load_graphical_culture_type_file(ast::NodeCPtr root) { - const bool ret = expect_list_reserve_length( - graphical_culture_types, - expect_identifier(std::bind_front(&CultureManager::add_graphical_culture_type, this)) - )(root); - - lock_graphical_culture_types(); - - if (graphical_culture_types_empty()) { - spdlog::error_s("Cannot set default graphical culture type - none loaded!"); - return false; - } - - /* Last defined graphical culture type is used as default. */ - default_graphical_culture_type = &get_back_graphical_culture_type(); - - return ret; -} - -bool CultureManager::_load_culture_group( - CountryDefinitionManager const& country_definition_manager, size_t& total_expected_cultures, - std::string_view culture_group_key, ast::NodeCPtr culture_group_node -) { - std::string_view leader {}; - GraphicalCultureType const* unit_graphical_culture_type = default_graphical_culture_type; - bool is_overseas = true; - CountryDefinition const* union_country = nullptr; - - bool ret = expect_dictionary_keys_and_default( - increment_callback(total_expected_cultures), - "leader", ZERO_OR_ONE, expect_identifier(assign_variable_callback(leader)), // acts like ONE_EXACTLY, but vic2 doesn't complain and loads anyway if not present, for now I just commuted the error to a warning in the add function and fudged a default leader from the first defined culture group if it exists - brickpi - "unit", ZERO_OR_ONE, - expect_graphical_culture_type_identifier(assign_variable_callback_pointer(unit_graphical_culture_type)), - "union", ZERO_OR_ONE, - country_definition_manager.expect_country_definition_identifier(assign_variable_callback_pointer(union_country)), - "is_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_overseas)) - )(culture_group_node); - ret &= add_culture_group(culture_group_key, leader, unit_graphical_culture_type, is_overseas, union_country); - return ret; -} - -bool CultureManager::_load_culture( - CountryDefinitionManager const& country_definition_manager, CultureGroup const& culture_group, std::string_view culture_key, - ast::NodeCPtr culture_node -) { - colour_t colour = colour_t::null(); - name_list_t first_names {}, last_names {}; - fixed_point_t radicalism = 0; - CountryDefinition const* primary_country = nullptr; - - bool ret = expect_dictionary_keys( - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), - "first_names", ONE_EXACTLY, name_list_callback(move_variable_callback(first_names)), - "last_names", ONE_EXACTLY, name_list_callback(move_variable_callback(last_names)), - "radicalism", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(radicalism)), - "primary", ZERO_OR_ONE, - country_definition_manager.expect_country_definition_identifier(assign_variable_callback_pointer(primary_country)) - )(culture_node); - ret &= add_culture( - culture_key, colour, culture_group, std::move(first_names), std::move(last_names), radicalism, primary_country - ); - return ret; -} - -/* REQUIREMENTS: - * POP-59, POP-60, POP-61, POP-62, POP-63, POP-64, POP-65, POP-66, POP-67, POP-68, POP-69, POP-70, POP-71, - * POP-72, POP-73, POP-74, POP-75, POP-76, POP-77, POP-78, POP-79, POP-80, POP-81, POP-82, POP-83, POP-84, - * POP-85, POP-86, POP-87, POP-88, POP-89, POP-90, POP-91, POP-92, POP-93, POP-94, POP-95, POP-96, POP-97, - * POP-98, POP-99, POP-100, POP-101, POP-102, POP-103, POP-104, POP-105, POP-106, POP-107, POP-108, POP-109, POP-110, - * POP-111, POP-112, POP-113, POP-114, POP-115, POP-116, POP-117, POP-118, POP-119, POP-120, POP-121, POP-122, POP-123, - * POP-124, POP-125, POP-126, POP-127, POP-128, POP-129, POP-130, POP-131, POP-132, POP-133, POP-134, POP-135, POP-136, - * POP-137, POP-138, POP-139, POP-140, POP-141, POP-142, POP-143, POP-144, POP-145, POP-146, POP-147, POP-148, POP-149, - * POP-150, POP-151, POP-152, POP-153, POP-154, POP-155, POP-156, POP-157, POP-158, POP-159, POP-160, POP-161, POP-162, - * POP-163, POP-164, POP-165, POP-166, POP-167, POP-168, POP-169, POP-170, POP-171, POP-172, POP-173, POP-174, POP-175, - * POP-176, POP-177, POP-178, POP-179, POP-180, POP-181, POP-182, POP-183, POP-184, POP-185, POP-186, POP-187, POP-188, - * POP-189, POP-190, POP-191, POP-192, POP-193, POP-194, POP-195, POP-196, POP-197, POP-198, POP-199, POP-200, POP-201, - * POP-202, POP-203, POP-204, POP-205, POP-206, POP-207, POP-208, POP-209, POP-210, POP-211, POP-212, POP-213, POP-214, - * POP-215, POP-216, POP-217, POP-218, POP-219, POP-220, POP-221, POP-222, POP-223, POP-224, POP-225, POP-226, POP-227, - * POP-228, POP-229, POP-230, POP-231, POP-232, POP-233, POP-234, POP-235, POP-236, POP-237, POP-238, POP-239, POP-240, - * POP-241, POP-242, POP-243, POP-244, POP-245, POP-246, POP-247, POP-248, POP-249, POP-250, POP-251, POP-252, POP-253, - * POP-254, POP-255, POP-256, POP-257, POP-258, POP-259, POP-260, POP-261, POP-262, POP-263, POP-264, POP-265, POP-266, - * POP-267, POP-268, POP-269, POP-270, POP-271, POP-272, POP-273, POP-274, POP-275, POP-276, POP-277, POP-278, POP-279, - * POP-280, POP-281, POP-282, POP-283, POP-284 - */ -bool CultureManager::load_culture_file(CountryDefinitionManager const& country_definition_manager, ast::NodeCPtr root) { - if (!graphical_culture_types.is_locked()) { - spdlog::error_s("Cannot load culture groups until graphical culture types are locked!"); - return false; - } - - size_t total_expected_cultures = 0; - bool ret = expect_dictionary_reserve_length(culture_groups, - [this, &country_definition_manager, &total_expected_cultures]( - std::string_view key, ast::NodeCPtr value - ) -> bool { - return _load_culture_group(country_definition_manager, total_expected_cultures, key, value); - } - )(root); - lock_culture_groups(); - reserve_more_cultures(total_expected_cultures); - - ret &= expect_culture_group_dictionary( - [this, &country_definition_manager](CultureGroup const& culture_group, ast::NodeCPtr culture_group_value) -> bool { - return expect_dictionary( - [this, &country_definition_manager, &culture_group](std::string_view key, ast::NodeCPtr value) -> bool { - static const string_set_t reserved_keys = { "leader", "unit", "union", "is_overseas" }; - if (reserved_keys.contains(key)) { - return true; - } - return _load_culture(country_definition_manager, culture_group, key, value); - } - )(culture_group_value); - } - )(root); - lock_cultures(); - return ret; -} - -memory::string CultureManager::make_leader_picture_name( - std::string_view cultural_type, unit_branch_t branch, leader_count_t count -) { - if (cultural_type.empty()) { - spdlog::error_s("Cannot construct leader picture name - empty cultural type!"); - return {}; - } - - static constexpr std::string_view GENERAL_TEXT = "_general_"; - static constexpr std::string_view ADMIRAL_TEXT = "_admiral_"; - - std::string_view const* branch_text; - - using enum unit_branch_t; - - switch (branch) { - case LAND: - branch_text = &GENERAL_TEXT; - break; - case NAVAL: - branch_text = &ADMIRAL_TEXT; - break; - default: - spdlog::error_s("Cannot construct leader picture name - invalid branch type: {}", static_cast(branch)); - return {}; - } - - return append_string_views(cultural_type, *branch_text, std::to_string(count)); -} - -memory::string CultureManager::make_leader_picture_path(std::string_view leader_picture_name) { - if (leader_picture_name.empty()) { - spdlog::error_s("Cannot construct leader picture path - empty name!"); - return {}; - } - - return memory::fmt::format("gfx/interface/leaders/{}.dds", leader_picture_name); -} - -bool CultureManager::find_cultural_leader_pictures(Dataloader const& dataloader) { - if (!culture_groups_are_locked()) { - spdlog::error_s("Cannot search for cultural leader pictures until culture groups are locked!"); - return false; - } - - bool ret = true; - - for (auto [cultural_type, general_and_admiral_count] : mutable_iterator(leader_picture_counts)) { - const auto search = [&dataloader, &cultural_type, &ret]( - unit_branch_t branch, leader_count_t& leader_count - ) -> void { - while ( - leader_count < std::numeric_limits::max() && - !dataloader.lookup_file( - make_leader_picture_path(make_leader_picture_name(cultural_type, branch, leader_count)), false - ).empty() - ) { - leader_count++; - } - - if (leader_count < 1) { - spdlog::error_s( - "No {} pictures found for cultural type \"{}\"!", - get_branched_leader_name(branch), cultural_type - ); - ret = false; - } - }; - - using enum unit_branch_t; - - search(LAND, general_and_admiral_count.first); - search(NAVAL, general_and_admiral_count.second); - } - - return ret; -} - -memory::string CultureManager::get_leader_picture_name(std::string_view cultural_type, unit_branch_t branch) const { - const decltype(leader_picture_counts)::const_iterator it = leader_picture_counts.find(cultural_type); - if (it == leader_picture_counts.end()) { - spdlog::error_s("Cannot find leader picture counts for cultural type \"{}\"!", cultural_type); - return {}; - } - - leader_count_t desired_picture_count; - - using enum unit_branch_t; - - switch (branch) { - case LAND: - desired_picture_count = it->second.first; - break; - case NAVAL: - desired_picture_count = it->second.second; - break; - default: - spdlog::error_s( - "Cannot get \"{}\" leader picture name - invalid branch type: {}", - cultural_type, static_cast(branch) - ); - return {}; - } - - if (desired_picture_count < 1) { - spdlog::error_s( - "Cannot get \"{}\" {} picture name - no pictures of this type were found during game loading!", - cultural_type, get_branched_leader_name(branch) - ); - return {}; - } - - // This variable determines what index the leader picture name uses. It is static and increments each time it is used, - // hopefully resulting in a variety of different leader pictures being seen in-game. This is not necessarily a permanent - // solution, for example it may be replaced with randomly generated integers once our RNG system has been finalised. - static leader_count_t internal_counter = 0; - - return make_leader_picture_name(cultural_type, branch, internal_counter++ % desired_picture_count); -} diff --git a/src/openvic-simulation/population/Culture.hpp b/src/openvic-simulation/population/Culture.hpp index 1b03ab969..7f2071da0 100644 --- a/src/openvic-simulation/population/Culture.hpp +++ b/src/openvic-simulation/population/Culture.hpp @@ -1,14 +1,13 @@ #pragma once -#include "openvic-simulation/types/IdentifierRegistry.hpp" -#include "openvic-simulation/types/UnitBranchType.hpp" +#include "openvic-simulation/core/memory/String.hpp" +#include "openvic-simulation/core/memory/Vector.hpp" +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" namespace OpenVic { - struct CultureManager; struct CountryDefinition; - struct CountryDefinitionManager; - class Dataloader; struct GraphicalCultureType : HasIdentifier { friend struct CultureManager; @@ -43,8 +42,8 @@ namespace OpenVic { struct Culture : HasIdentifierAndColour { private: - name_list_t PROPERTY(first_names); - name_list_t PROPERTY(last_names); + memory::vector PROPERTY(first_names); + memory::vector PROPERTY(last_names); public: CultureGroup const& group; @@ -52,8 +51,8 @@ namespace OpenVic { CountryDefinition const* const primary_country; Culture( - std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, name_list_t&& new_first_names, - name_list_t&& new_last_names, fixed_point_t new_radicalism, CountryDefinition const* new_primary_country + std::string_view new_identifier, colour_t new_colour, CultureGroup const& new_group, memory::vector&& new_first_names, + memory::vector&& new_last_names, fixed_point_t new_radicalism, CountryDefinition const* new_primary_country ); Culture(Culture&&) = default; @@ -61,55 +60,4 @@ namespace OpenVic { return group.has_union_country(); } }; - - struct CultureManager { - using leader_count_t = uint32_t; - - private: - IdentifierRegistry IDENTIFIER_REGISTRY(graphical_culture_type); - IdentifierRegistry IDENTIFIER_REGISTRY(culture_group); - IdentifierRegistry IDENTIFIER_REGISTRY(culture); - - GraphicalCultureType const* PROPERTY(default_graphical_culture_type); - - using general_admiral_picture_count_t = std::pair; - // Cultural type string maps to (general picture count, admiral picture count) pair - string_map_t leader_picture_counts; - - bool _load_culture_group( - CountryDefinitionManager const& country_definition_manager, size_t& total_expected_cultures, - std::string_view culture_group_key, ast::NodeCPtr culture_group_node - ); - bool _load_culture( - CountryDefinitionManager const& country_definition_manager, CultureGroup const& culture_group, - std::string_view culture_key, ast::NodeCPtr node - ); - - public: - CultureManager(); - - bool add_graphical_culture_type(std::string_view identifier); - - bool add_culture_group( - std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, - bool is_overseas, CountryDefinition const* union_country - ); - - bool add_culture( - std::string_view identifier, colour_t colour, CultureGroup const& group, name_list_t&& first_names, - name_list_t&& last_names, fixed_point_t radicalism, CountryDefinition const* primary_country - ); - - bool load_graphical_culture_type_file(ast::NodeCPtr root); - bool load_culture_file(CountryDefinitionManager const& country_definition_manager, ast::NodeCPtr root); - - static memory::string make_leader_picture_name( - std::string_view cultural_type, unit_branch_t branch, leader_count_t count - ); - static memory::string make_leader_picture_path(std::string_view leader_picture_name); - - bool find_cultural_leader_pictures(Dataloader const& dataloader); - - memory::string get_leader_picture_name(std::string_view cultural_type, unit_branch_t branch) const; - }; } diff --git a/src/openvic-simulation/population/CultureManager.cpp b/src/openvic-simulation/population/CultureManager.cpp new file mode 100644 index 000000000..6e2882096 --- /dev/null +++ b/src/openvic-simulation/population/CultureManager.cpp @@ -0,0 +1,326 @@ +#include "CultureManager.hpp" + +#include + +#include "openvic-simulation/country/CountryDefinitionManager.hpp" +#include "openvic-simulation/dataloader/Dataloader.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/core/string/Utility.hpp" +#include "openvic-simulation/core/Typedefs.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +CultureManager::CultureManager() : default_graphical_culture_type { nullptr } {} + +bool CultureManager::add_graphical_culture_type(std::string_view identifier) { + if (identifier.empty()) { + spdlog::error_s("Invalid graphical culture type identifier - empty!"); + return false; + } + return graphical_culture_types.emplace_item( + identifier, + identifier + ); +} + +bool CultureManager::add_culture_group( + std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, bool is_overseas, + CountryDefinition const* union_country +) { + if (!graphical_culture_types.is_locked()) { + spdlog::error_s("Cannot register culture groups until graphical culture types are locked!"); + return false; + } + if (OV_unlikely(identifier.empty())) { + spdlog::error_s("Invalid culture group identifier - empty!"); + return false; + } + if (graphical_culture_type == nullptr) { + spdlog::error_s("Null graphical culture type for {}", identifier); + return false; + } + + if (leader.empty()) { + std::string_view default_leader {}; + if (culture_groups.empty()) { + default_leader = "NOLEADER"; + } else { + default_leader = culture_groups.front().leader; + } + leader = default_leader; + spdlog::warn_s( + "In culture group \"{}\" - group leader is undefined, set to default of: \"{}\".", + identifier, default_leader + ); + } + if (!culture_groups.emplace_item( + identifier, + identifier, leader, *graphical_culture_type, is_overseas, union_country + )) { + return false; + } + + leader_picture_counts.emplace(leader, general_admiral_picture_count_t { 0, 0 }); + return true; +} + +bool CultureManager::add_culture( + std::string_view identifier, colour_t colour, CultureGroup const& group, memory::vector&& first_names, + memory::vector&& last_names, fixed_point_t radicalism, CountryDefinition const* primary_country +) { + if (!culture_groups.is_locked()) { + spdlog::error_s("Cannot register cultures until culture groups are locked!"); + return false; + } + if (identifier.empty()) { + spdlog::error_s("Invalid culture identifier - empty!"); + return false; + } + + // TODO - check radicalism range + + return cultures.emplace_item( + identifier, + identifier, colour, group, std::move(first_names), std::move(last_names), radicalism, primary_country + ); +} + +bool CultureManager::load_graphical_culture_type_file(ovdl::v2script::ast::Node const* root) { + const bool ret = expect_list_reserve_length( + graphical_culture_types, + expect_identifier(std::bind_front(&CultureManager::add_graphical_culture_type, this)) + )(root); + + lock_graphical_culture_types(); + + if (graphical_culture_types_empty()) { + spdlog::error_s("Cannot set default graphical culture type - none loaded!"); + return false; + } + + /* Last defined graphical culture type is used as default. */ + default_graphical_culture_type = &get_back_graphical_culture_type(); + + return ret; +} + +bool CultureManager::_load_culture_group( + CountryDefinitionManager const& country_definition_manager, size_t& total_expected_cultures, + std::string_view culture_group_key, ovdl::v2script::ast::Node const* culture_group_node +) { + std::string_view leader {}; + GraphicalCultureType const* unit_graphical_culture_type = default_graphical_culture_type; + bool is_overseas = true; + CountryDefinition const* union_country = nullptr; + + bool ret = expect_dictionary_keys_and_default( + increment_callback(total_expected_cultures), + "leader", ZERO_OR_ONE, expect_identifier(assign_variable_callback(leader)), // acts like ONE_EXACTLY, but vic2 doesn't complain and loads anyway if not present, for now I just commuted the error to a warning in the add function and fudged a default leader from the first defined culture group if it exists - brickpi + "unit", ZERO_OR_ONE, + expect_graphical_culture_type_identifier(assign_variable_callback_pointer(unit_graphical_culture_type)), + "union", ZERO_OR_ONE, + country_definition_manager.expect_country_definition_identifier(assign_variable_callback_pointer(union_country)), + "is_overseas", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_overseas)) + )(culture_group_node); + ret &= add_culture_group(culture_group_key, leader, unit_graphical_culture_type, is_overseas, union_country); + return ret; +} + +bool CultureManager::_load_culture( + CountryDefinitionManager const& country_definition_manager, CultureGroup const& culture_group, std::string_view culture_key, + ovdl::v2script::ast::Node const* culture_node +) { + colour_t colour = colour_t::null(); + memory::vector first_names {}, last_names {}; + fixed_point_t radicalism = 0; + CountryDefinition const* primary_country = nullptr; + + bool ret = expect_dictionary_keys( + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "first_names", ONE_EXACTLY, name_list_callback(move_variable_callback(first_names)), + "last_names", ONE_EXACTLY, name_list_callback(move_variable_callback(last_names)), + "radicalism", ZERO_OR_ONE, expect_fixed_point(assign_variable_callback(radicalism)), + "primary", ZERO_OR_ONE, + country_definition_manager.expect_country_definition_identifier(assign_variable_callback_pointer(primary_country)) + )(culture_node); + ret &= add_culture( + culture_key, colour, culture_group, std::move(first_names), std::move(last_names), radicalism, primary_country + ); + return ret; +} + +/* REQUIREMENTS: + * POP-59, POP-60, POP-61, POP-62, POP-63, POP-64, POP-65, POP-66, POP-67, POP-68, POP-69, POP-70, POP-71, + * POP-72, POP-73, POP-74, POP-75, POP-76, POP-77, POP-78, POP-79, POP-80, POP-81, POP-82, POP-83, POP-84, + * POP-85, POP-86, POP-87, POP-88, POP-89, POP-90, POP-91, POP-92, POP-93, POP-94, POP-95, POP-96, POP-97, + * POP-98, POP-99, POP-100, POP-101, POP-102, POP-103, POP-104, POP-105, POP-106, POP-107, POP-108, POP-109, POP-110, + * POP-111, POP-112, POP-113, POP-114, POP-115, POP-116, POP-117, POP-118, POP-119, POP-120, POP-121, POP-122, POP-123, + * POP-124, POP-125, POP-126, POP-127, POP-128, POP-129, POP-130, POP-131, POP-132, POP-133, POP-134, POP-135, POP-136, + * POP-137, POP-138, POP-139, POP-140, POP-141, POP-142, POP-143, POP-144, POP-145, POP-146, POP-147, POP-148, POP-149, + * POP-150, POP-151, POP-152, POP-153, POP-154, POP-155, POP-156, POP-157, POP-158, POP-159, POP-160, POP-161, POP-162, + * POP-163, POP-164, POP-165, POP-166, POP-167, POP-168, POP-169, POP-170, POP-171, POP-172, POP-173, POP-174, POP-175, + * POP-176, POP-177, POP-178, POP-179, POP-180, POP-181, POP-182, POP-183, POP-184, POP-185, POP-186, POP-187, POP-188, + * POP-189, POP-190, POP-191, POP-192, POP-193, POP-194, POP-195, POP-196, POP-197, POP-198, POP-199, POP-200, POP-201, + * POP-202, POP-203, POP-204, POP-205, POP-206, POP-207, POP-208, POP-209, POP-210, POP-211, POP-212, POP-213, POP-214, + * POP-215, POP-216, POP-217, POP-218, POP-219, POP-220, POP-221, POP-222, POP-223, POP-224, POP-225, POP-226, POP-227, + * POP-228, POP-229, POP-230, POP-231, POP-232, POP-233, POP-234, POP-235, POP-236, POP-237, POP-238, POP-239, POP-240, + * POP-241, POP-242, POP-243, POP-244, POP-245, POP-246, POP-247, POP-248, POP-249, POP-250, POP-251, POP-252, POP-253, + * POP-254, POP-255, POP-256, POP-257, POP-258, POP-259, POP-260, POP-261, POP-262, POP-263, POP-264, POP-265, POP-266, + * POP-267, POP-268, POP-269, POP-270, POP-271, POP-272, POP-273, POP-274, POP-275, POP-276, POP-277, POP-278, POP-279, + * POP-280, POP-281, POP-282, POP-283, POP-284 + */ +bool CultureManager::load_culture_file(CountryDefinitionManager const& country_definition_manager, ovdl::v2script::ast::Node const* root) { + if (!graphical_culture_types.is_locked()) { + spdlog::error_s("Cannot load culture groups until graphical culture types are locked!"); + return false; + } + + size_t total_expected_cultures = 0; + bool ret = expect_dictionary_reserve_length(culture_groups, + [this, &country_definition_manager, &total_expected_cultures]( + std::string_view key, ovdl::v2script::ast::Node const* value + ) -> bool { + return _load_culture_group(country_definition_manager, total_expected_cultures, key, value); + } + )(root); + lock_culture_groups(); + reserve_more_cultures(total_expected_cultures); + + ret &= expect_culture_group_dictionary( + [this, &country_definition_manager](CultureGroup const& culture_group, ovdl::v2script::ast::Node const* culture_group_value) -> bool { + return expect_dictionary( + [this, &country_definition_manager, &culture_group](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + static const string_set_t reserved_keys = { "leader", "unit", "union", "is_overseas" }; + if (reserved_keys.contains(key)) { + return true; + } + return _load_culture(country_definition_manager, culture_group, key, value); + } + )(culture_group_value); + } + )(root); + lock_cultures(); + return ret; +} + +memory::string CultureManager::make_leader_picture_name( + std::string_view cultural_type, unit_branch_t branch, leader_count_t count +) { + if (cultural_type.empty()) { + spdlog::error_s("Cannot construct leader picture name - empty cultural type!"); + return {}; + } + + static constexpr std::string_view GENERAL_TEXT = "_general_"; + static constexpr std::string_view ADMIRAL_TEXT = "_admiral_"; + + std::string_view const* branch_text; + + using enum unit_branch_t; + + switch (branch) { + case LAND: + branch_text = &GENERAL_TEXT; + break; + case NAVAL: + branch_text = &ADMIRAL_TEXT; + break; + default: + spdlog::error_s("Cannot construct leader picture name - invalid branch type: {}", static_cast(branch)); + return {}; + } + + return append_string_views(cultural_type, *branch_text, std::to_string(count)); +} + +memory::string CultureManager::make_leader_picture_path(std::string_view leader_picture_name) { + if (leader_picture_name.empty()) { + spdlog::error_s("Cannot construct leader picture path - empty name!"); + return {}; + } + + return memory::fmt::format("gfx/interface/leaders/{}.dds", leader_picture_name); +} + +bool CultureManager::find_cultural_leader_pictures(Dataloader const& dataloader) { + if (!culture_groups_are_locked()) { + spdlog::error_s("Cannot search for cultural leader pictures until culture groups are locked!"); + return false; + } + + bool ret = true; + + for (auto [cultural_type, general_and_admiral_count] : mutable_iterator(leader_picture_counts)) { + const auto search = [&dataloader, &cultural_type, &ret]( + unit_branch_t branch, leader_count_t& leader_count + ) -> void { + while ( + leader_count < std::numeric_limits::max() && + !dataloader.lookup_file( + make_leader_picture_path(make_leader_picture_name(cultural_type, branch, leader_count)), false + ).empty() + ) { + leader_count++; + } + + if (leader_count < 1) { + spdlog::error_s( + "No {} pictures found for cultural type \"{}\"!", + get_branched_leader_name(branch), cultural_type + ); + ret = false; + } + }; + + using enum unit_branch_t; + + search(LAND, general_and_admiral_count.first); + search(NAVAL, general_and_admiral_count.second); + } + + return ret; +} + +memory::string CultureManager::get_leader_picture_name(std::string_view cultural_type, unit_branch_t branch) const { + const decltype(leader_picture_counts)::const_iterator it = leader_picture_counts.find(cultural_type); + if (it == leader_picture_counts.end()) { + spdlog::error_s("Cannot find leader picture counts for cultural type \"{}\"!", cultural_type); + return {}; + } + + leader_count_t desired_picture_count; + + using enum unit_branch_t; + + switch (branch) { + case LAND: + desired_picture_count = it->second.first; + break; + case NAVAL: + desired_picture_count = it->second.second; + break; + default: + spdlog::error_s( + "Cannot get \"{}\" leader picture name - invalid branch type: {}", + cultural_type, static_cast(branch) + ); + return {}; + } + + if (desired_picture_count < 1) { + spdlog::error_s( + "Cannot get \"{}\" {} picture name - no pictures of this type were found during game loading!", + cultural_type, get_branched_leader_name(branch) + ); + return {}; + } + + // This variable determines what index the leader picture name uses. It is static and increments each time it is used, + // hopefully resulting in a variety of different leader pictures being seen in-game. This is not necessarily a permanent + // solution, for example it may be replaced with randomly generated integers once our RNG system has been finalised. + static leader_count_t internal_counter = 0; + + return make_leader_picture_name(cultural_type, branch, internal_counter++ % desired_picture_count); +} diff --git a/src/openvic-simulation/population/CultureManager.hpp b/src/openvic-simulation/population/CultureManager.hpp new file mode 100644 index 000000000..d34066836 --- /dev/null +++ b/src/openvic-simulation/population/CultureManager.hpp @@ -0,0 +1,63 @@ +#pragma once + +#include "openvic-simulation/population/Culture.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/UnitBranchType.hpp" + +namespace OpenVic { + struct CultureManager; + struct CountryDefinition; + struct CountryDefinitionManager; + class Dataloader; + + struct CultureManager { + using leader_count_t = uint32_t; + + private: + IdentifierRegistry IDENTIFIER_REGISTRY(graphical_culture_type); + IdentifierRegistry IDENTIFIER_REGISTRY(culture_group); + IdentifierRegistry IDENTIFIER_REGISTRY(culture); + + GraphicalCultureType const* PROPERTY(default_graphical_culture_type); + + using general_admiral_picture_count_t = std::pair; + // Cultural type string maps to (general picture count, admiral picture count) pair + string_map_t leader_picture_counts; + + bool _load_culture_group( + CountryDefinitionManager const& country_definition_manager, size_t& total_expected_cultures, + std::string_view culture_group_key, ovdl::v2script::ast::Node const* culture_group_node + ); + bool _load_culture( + CountryDefinitionManager const& country_definition_manager, CultureGroup const& culture_group, + std::string_view culture_key, ovdl::v2script::ast::Node const* node + ); + + public: + CultureManager(); + + bool add_graphical_culture_type(std::string_view identifier); + + bool add_culture_group( + std::string_view identifier, std::string_view leader, GraphicalCultureType const* graphical_culture_type, + bool is_overseas, CountryDefinition const* union_country + ); + + bool add_culture( + std::string_view identifier, colour_t colour, CultureGroup const& group, memory::vector&& first_names, + memory::vector&& last_names, fixed_point_t radicalism, CountryDefinition const* primary_country + ); + + bool load_graphical_culture_type_file(ovdl::v2script::ast::Node const* root); + bool load_culture_file(CountryDefinitionManager const& country_definition_manager, ovdl::v2script::ast::Node const* root); + + static memory::string make_leader_picture_name( + std::string_view cultural_type, unit_branch_t branch, leader_count_t count + ); + static memory::string make_leader_picture_path(std::string_view leader_picture_name); + + bool find_cultural_leader_pictures(Dataloader const& dataloader); + + memory::string get_leader_picture_name(std::string_view cultural_type, unit_branch_t branch) const; + }; +} diff --git a/src/openvic-simulation/population/Pop.cpp b/src/openvic-simulation/population/Pop.cpp index d49673f7e..9fb8be96a 100644 --- a/src/openvic-simulation/population/Pop.cpp +++ b/src/openvic-simulation/population/Pop.cpp @@ -98,8 +98,12 @@ void Pop::setup_pop_test_values(TypedSpan reforms) num_migrated_external = test_size(1); num_migrated_colonial = test_size(2); - total_change = - num_grown + num_promoted + num_demoted + num_migrated_internal + num_migrated_external + num_migrated_colonial; + total_change = num_grown + + num_promoted + + num_demoted + + num_migrated_internal + + num_migrated_external + + num_migrated_colonial; /* Generates a number between 0 and max (inclusive) and sets map[&key] to it if it's at least min. */ static auto fill_span_with_test_weights = [](std::span span, int32_t min, int32_t max) -> void { @@ -242,9 +246,12 @@ void Pop::update_gamestate( consciousness = std::clamp(consciousness, MIN_CONSCIOUSNESS, MAX_CONSCIOUSNESS); literacy = std::clamp(literacy, MIN_LITERACY, MAX_LITERACY); - if ( - size < military_defines.get_min_pop_size_for_regiment() || owner == nullptr || - !is_culture_status_allowed(owner->get_allowed_regiment_cultures(), culture_status) + if (size < military_defines.get_min_pop_size_for_regiment() + || owner == nullptr + || !is_culture_status_allowed( + owner->get_allowed_regiment_cultures(), + culture_status + ) ) { max_supported_regiments = 0; } else { diff --git a/src/openvic-simulation/population/PopManager.cpp b/src/openvic-simulation/population/PopManager.cpp index 5210c84ef..4fee536d0 100644 --- a/src/openvic-simulation/population/PopManager.cpp +++ b/src/openvic-simulation/population/PopManager.cpp @@ -5,12 +5,13 @@ #include #include "openvic-simulation/core/FormatValidate.hpp" -#include "openvic-simulation/economy/GoodDefinition.hpp" -#include "openvic-simulation/military/UnitType.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/GoodDefinitionManager.hpp" +#include "openvic-simulation/military/UnitTypeManager.hpp" #include "openvic-simulation/modifier/ModifierManager.hpp" -#include "openvic-simulation/politics/Ideology.hpp" +#include "openvic-simulation/politics/IdeologyManager.hpp" #include "openvic-simulation/politics/IssueManager.hpp" -#include "openvic-simulation/politics/Rebel.hpp" +#include "openvic-simulation/politics/RebelManager.hpp" #include "openvic-simulation/population/Pop.hpp" #include "openvic-simulation/population/PopSize.hpp" #include "openvic-simulation/utility/Logger.hpp" @@ -81,7 +82,7 @@ bool PopManager::add_pop_type( PopType::income_type_t life_needs_income_types, PopType::income_type_t everyday_needs_income_types, PopType::income_type_t luxury_needs_income_types, - ast::NodeCPtr rebel_units, + ovdl::v2script::ast::Node const* rebel_units, pop_size_t max_size, pop_size_t merge_max_size, bool state_capital_only, @@ -100,12 +101,12 @@ bool PopManager::add_pop_type( fixed_point_t leadership_points, fixed_point_t research_leadership_optimum, fixed_point_t state_administration_multiplier, - ast::NodeCPtr equivalent, + ovdl::v2script::ast::Node const* equivalent, ConditionalWeightFactorMul&& country_migration_target, ConditionalWeightFactorMul&& migration_target, - ast::NodeCPtr promote_to_node, + ovdl::v2script::ast::Node const* promote_to_node, PopType::ideology_weight_map_t&& ideologies, - ast::NodeCPtr issues_node + ovdl::v2script::ast::Node const* issues_node ) { if (identifier.empty()) { spdlog::error_s("Invalid pop type identifier - empty!"); @@ -252,7 +253,7 @@ static NodeCallback auto expect_needs_income(PopType::income_type_t& types) { */ bool PopManager::load_pop_type_file( std::string_view filestem, GoodDefinitionManager const& good_definition_manager, IdeologyManager const& ideology_manager, - ast::NodeCPtr root + ovdl::v2script::ast::Node const* root ) { spdlog::scope scope { fmt::format("poptypes/{}.txt", filestem) }; using enum scope_type_t; @@ -264,19 +265,19 @@ bool PopManager::load_pop_type_file( fixed_point_map_t life_needs, everyday_needs, luxury_needs; PopType::income_type_t life_needs_income_types = NO_INCOME_TYPE, everyday_needs_income_types = NO_INCOME_TYPE, luxury_needs_income_types = NO_INCOME_TYPE; - ast::NodeCPtr rebel_units = nullptr; + ovdl::v2script::ast::Node const* rebel_units = nullptr; pop_size_t max_size = Pop::MAX_SIZE, merge_max_size = Pop::MAX_SIZE; bool state_capital_only = false, demote_migrant = false, is_artisan = false, allowed_to_vote = true, is_slave = false, can_be_recruited = false, can_reduce_consciousness = false, administrative_efficiency = false, can_invest = false, factory = false, can_work_factory = false, unemployment = false; fixed_point_t research_points = 0, leadership_points = 0, research_leadership_optimum = 0, state_administration_multiplier = 0; - ast::NodeCPtr equivalent = nullptr; + ovdl::v2script::ast::Node const* equivalent = nullptr; ConditionalWeightFactorMul country_migration_target { COUNTRY, POP, NO_SCOPE }; ConditionalWeightFactorMul migration_target { PROVINCE, POP, NO_SCOPE }; - ast::NodeCPtr promote_to_node = nullptr; + ovdl::v2script::ast::Node const* promote_to_node = nullptr; PopType::ideology_weight_map_t ideologies { ideology_manager.get_ideologies() }; - ast::NodeCPtr issues_node = nullptr; + ovdl::v2script::ast::Node const* issues_node = nullptr; bool ret = expect_dictionary_keys( "sprite", ONE_EXACTLY, expect_uint(assign_variable_callback(sprite)), @@ -308,7 +309,7 @@ bool PopManager::load_pop_type_file( "migration_target", ZERO_OR_ONE, migration_target.expect_conditional_weight(), "promote_to", ZERO_OR_ONE, assign_variable_callback(promote_to_node), "ideologies", ZERO_OR_ONE, ideology_manager.expect_ideology_dictionary( - [&filestem, &ideologies](Ideology const& ideology, ast::NodeCPtr node) -> bool { + [&filestem, &ideologies](Ideology const& ideology, ovdl::v2script::ast::Node const* node) -> bool { ConditionalWeightFactorMul weight { POP, POP, NO_SCOPE }; bool ret = weight.expect_conditional_weight()(node); @@ -413,7 +414,7 @@ bool PopManager::load_delayed_parse_pop_type_data( } if (promote_to_node != nullptr && !expect_pop_type_dictionary( - [pop_type](PopType const& type, ast::NodeCPtr node) -> bool { + [pop_type](PopType const& type, ovdl::v2script::ast::Node const* node) -> bool { if (pop_type && type == *pop_type) { spdlog::error_s("Pop type {} cannot have promotion weight to itself!", type); return false; @@ -430,7 +431,7 @@ bool PopManager::load_delayed_parse_pop_type_data( if (issues_node != nullptr && !expect_dictionary_reserve_length( pop_type->issues, - [pop_type, &issue_manager](std::string_view key, ast::NodeCPtr node) -> bool { + [pop_type, &issue_manager](std::string_view key, ovdl::v2script::ast::Node const* node) -> bool { BaseIssue const* issue = issue_manager.get_base_issue_by_identifier(key); if (issue == nullptr) { spdlog::error_s("Invalid issue in pop type {} issue weights: {}", ovfmt::validate(pop_type), key); @@ -450,7 +451,7 @@ bool PopManager::load_delayed_parse_pop_type_data( return ret; } -bool PopManager::load_pop_type_chances_file(ast::NodeCPtr root) { +bool PopManager::load_pop_type_chances_file(ovdl::v2script::ast::Node const* root) { return expect_dictionary_keys( "promotion_chance", ONE_EXACTLY, promotion_chance.expect_conditional_weight(), "demotion_chance", ONE_EXACTLY, demotion_chance.expect_conditional_weight(), @@ -463,7 +464,7 @@ bool PopManager::load_pop_type_chances_file(ast::NodeCPtr root) { } bool PopManager::load_pop_bases_into_vector( - RebelManager const& rebel_manager, memory::vector& vec, PopType const& type, ast::NodeCPtr pop_node, + RebelManager const& rebel_manager, memory::vector& vec, PopType const& type, ovdl::v2script::ast::Node const* pop_node, bool *non_integer_size ) const { Culture const* culture = nullptr; @@ -507,8 +508,10 @@ bool PopManager::generate_modifiers(ModifierManager& modifier_manager) const { static constexpr bool HAS_NO_EFFECT = true; - memory::FixedVector& strata_effects = - modifier_manager.modifier_effect_cache.strata_effects; + memory::FixedVector< + ModifierEffectCache::strata_effects_t, + strata_index_t + >& strata_effects = modifier_manager.modifier_effect_cache.strata_effects; strata_effects = std::move( decltype(ModifierEffectCache::strata_effects) { diff --git a/src/openvic-simulation/population/PopManager.hpp b/src/openvic-simulation/population/PopManager.hpp index 5e3d77f31..8bb58783b 100644 --- a/src/openvic-simulation/population/PopManager.hpp +++ b/src/openvic-simulation/population/PopManager.hpp @@ -2,8 +2,8 @@ #include -#include "openvic-simulation/population/Culture.hpp" -#include "openvic-simulation/population/Religion.hpp" +#include "openvic-simulation/population/CultureManager.hpp" +#include "openvic-simulation/population/ReligionManager.hpp" #include "openvic-simulation/population/PopType.hpp" namespace OpenVic { @@ -27,7 +27,7 @@ namespace OpenVic { * necessary defines are loaded. The nodes will remain valid as PopType files' Parser objects are already cached to * preserve their condition script nodes until all other defines are loaded and the scripts can be parsed. * Entries contain: (rebel, equivalent, promote_to, issues) */ - memory::vector> delayed_parse_nodes; + memory::vector> delayed_parse_nodes; ConditionalWeightFactorAdd PROPERTY(promotion_chance); ConditionalWeightFactorAdd PROPERTY(demotion_chance); @@ -63,7 +63,7 @@ namespace OpenVic { PopType::income_type_t life_needs_income_types, PopType::income_type_t everyday_needs_income_types, PopType::income_type_t luxury_needs_income_types, - ast::NodeCPtr rebel_units, + ovdl::v2script::ast::Node const* rebel_units, pop_size_t max_size, pop_size_t merge_max_size, bool state_capital_only, @@ -82,12 +82,12 @@ namespace OpenVic { fixed_point_t leadership_points, fixed_point_t research_leadership_optimum, fixed_point_t state_administration_multiplier, - ast::NodeCPtr equivalent, + ovdl::v2script::ast::Node const* equivalent, ConditionalWeightFactorMul&& country_migration_target, ConditionalWeightFactorMul&& migration_target, - ast::NodeCPtr promote_to_node, + ovdl::v2script::ast::Node const* promote_to_node, PopType::ideology_weight_map_t&& ideologies, - ast::NodeCPtr issues_node + ovdl::v2script::ast::Node const* issues_node ); bool setup_stratas(); @@ -96,14 +96,14 @@ namespace OpenVic { bool load_pop_type_file( std::string_view filestem, GoodDefinitionManager const& good_definition_manager, - IdeologyManager const& ideology_manager, ast::NodeCPtr root + IdeologyManager const& ideology_manager, ovdl::v2script::ast::Node const* root ); bool load_delayed_parse_pop_type_data(UnitTypeManager const& unit_type_manager, IssueManager const& issue_manager); - bool load_pop_type_chances_file(ast::NodeCPtr root); + bool load_pop_type_chances_file(ovdl::v2script::ast::Node const* root); bool load_pop_bases_into_vector( - RebelManager const& rebel_manager, memory::vector& vec, PopType const& type, ast::NodeCPtr pop_node, + RebelManager const& rebel_manager, memory::vector& vec, PopType const& type, ovdl::v2script::ast::Node const* pop_node, bool *non_integer_size ) const; diff --git a/src/openvic-simulation/population/PopValuesFromProvince.cpp b/src/openvic-simulation/population/PopValuesFromProvince.cpp index d40f71ba6..9becdb5c8 100644 --- a/src/openvic-simulation/population/PopValuesFromProvince.cpp +++ b/src/openvic-simulation/population/PopValuesFromProvince.cpp @@ -6,6 +6,7 @@ #include "openvic-simulation/economy/GoodInstance.hpp" #include "openvic-simulation/economy/production/ArtisanalProducer.hpp" #include "openvic-simulation/economy/production/ProductionType.hpp" +#include "openvic-simulation/economy/production/ProductionTypeManager.hpp" #include "openvic-simulation/modifier/ModifierEffectCache.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" #include "openvic-simulation/misc/GameRulesManager.hpp" diff --git a/src/openvic-simulation/population/Religion.cpp b/src/openvic-simulation/population/Religion.cpp index 49cd2f4f0..ffa9b3e97 100644 --- a/src/openvic-simulation/population/Religion.cpp +++ b/src/openvic-simulation/population/Religion.cpp @@ -1,9 +1,6 @@ #include "Religion.hpp" -#include "openvic-simulation/types/Colour.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; ReligionGroup::ReligionGroup(std::string_view new_identifier) : HasIdentifier { new_identifier } {} @@ -12,74 +9,8 @@ Religion::Religion( colour_t new_colour, ReligionGroup const& new_group, icon_t new_icon, - bool new_pagan + bool new_is_pagan ) : HasIdentifierAndColour { new_identifier, new_colour, false }, group { new_group }, icon { new_icon }, - pagan { new_pagan } {} - -bool ReligionManager::add_religion_group(std::string_view identifier) { - if (identifier.empty()) { - spdlog::error_s("Invalid religion group identifier - empty!"); - return false; - } - return religion_groups.emplace_item(identifier, identifier); -} - -bool ReligionManager::add_religion( - std::string_view identifier, colour_t colour, ReligionGroup const& group, Religion::icon_t icon, bool pagan -) { - if (!religion_groups.is_locked()) { - spdlog::error_s("Cannot register religions until religion groups are locked!"); - return false; - } - if (identifier.empty()) { - spdlog::error_s("Invalid religion identifier - empty!"); - return false; - } - if (icon <= 0) { - spdlog::error_s("Invalid religion icon for {}: {}", identifier, icon); - return false; - } - return religions.emplace_item( - identifier, - identifier, colour, group, icon, pagan - ); -} - -/* REQUIREMENTS: - * POP-286, POP-287, POP-288, POP-289, POP-290, POP-291, POP-292, - * POP-293, POP-294, POP-295, POP-296, POP-297, POP-298, POP-299 - */ -bool ReligionManager::load_religion_file(ast::NodeCPtr root) { - size_t total_expected_religions = 0; - bool ret = expect_dictionary_reserve_length( - religion_groups, - [this, &total_expected_religions](std::string_view key, ast::NodeCPtr value) -> bool { - bool ret = expect_length(add_variable_callback(total_expected_religions))(value); - ret &= add_religion_group(key); - return ret; - } - )(root); - lock_religion_groups(); - reserve_more_religions(total_expected_religions); - ret &= expect_religion_group_dictionary( - [this](ReligionGroup const& religion_group, ast::NodeCPtr religion_group_value) -> bool { - return expect_dictionary([this, &religion_group](std::string_view key, ast::NodeCPtr value) -> bool { - colour_t colour = colour_t::null(); - Religion::icon_t icon = 0; - bool pagan = false; - - bool ret = expect_dictionary_keys( - "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), - "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), - "pagan", ZERO_OR_ONE, expect_bool(assign_variable_callback(pagan)) - )(value); - ret &= add_religion(key, colour, religion_group, icon, pagan); - return ret; - })(religion_group_value); - } - )(root); - lock_religions(); - return ret; -} + is_pagan { new_is_pagan } {} diff --git a/src/openvic-simulation/population/Religion.hpp b/src/openvic-simulation/population/Religion.hpp index 66287d9cb..7b0112d92 100644 --- a/src/openvic-simulation/population/Religion.hpp +++ b/src/openvic-simulation/population/Religion.hpp @@ -1,8 +1,9 @@ #pragma once -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include + +#include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" namespace OpenVic { struct ReligionGroup : HasIdentifier { @@ -12,32 +13,20 @@ namespace OpenVic { }; struct Religion : HasIdentifierAndColour { - using icon_t = uint8_t; + using icon_t = std::uint8_t; public: ReligionGroup const& group; const icon_t icon; - const bool pagan; + const bool is_pagan; Religion( - std::string_view new_identifier, colour_t new_colour, ReligionGroup const& new_group, icon_t new_icon, - bool new_pagan + std::string_view new_identifier, + colour_t new_colour, + ReligionGroup const& new_group, + icon_t new_icon, + bool new_is_pagan ); Religion(Religion&&) = default; }; - - struct ReligionManager { - private: - IdentifierRegistry IDENTIFIER_REGISTRY(religion_group); - IdentifierRegistry IDENTIFIER_REGISTRY(religion); - - public: - bool add_religion_group(std::string_view identifier); - - bool add_religion( - std::string_view identifier, colour_t colour, ReligionGroup const& group, Religion::icon_t icon, bool pagan - ); - - bool load_religion_file(ast::NodeCPtr root); - }; } diff --git a/src/openvic-simulation/population/ReligionManager.cpp b/src/openvic-simulation/population/ReligionManager.cpp new file mode 100644 index 000000000..be471d6b3 --- /dev/null +++ b/src/openvic-simulation/population/ReligionManager.cpp @@ -0,0 +1,73 @@ +#include "ReligionManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/types/Colour.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool ReligionManager::add_religion_group(std::string_view identifier) { + if (identifier.empty()) { + spdlog::error_s("Invalid religion group identifier - empty!"); + return false; + } + return religion_groups.emplace_item(identifier, identifier); +} + +bool ReligionManager::add_religion( + std::string_view identifier, colour_t colour, ReligionGroup const& group, Religion::icon_t icon, bool is_pagan +) { + if (!religion_groups.is_locked()) { + spdlog::error_s("Cannot register religions until religion groups are locked!"); + return false; + } + if (identifier.empty()) { + spdlog::error_s("Invalid religion identifier - empty!"); + return false; + } + if (icon <= 0) { + spdlog::error_s("Invalid religion icon for {}: {}", identifier, icon); + return false; + } + return religions.emplace_item( + identifier, + identifier, colour, group, icon, is_pagan + ); +} + +/* REQUIREMENTS: + * POP-286, POP-287, POP-288, POP-289, POP-290, POP-291, POP-292, + * POP-293, POP-294, POP-295, POP-296, POP-297, POP-298, POP-299 + */ +bool ReligionManager::load_religion_file(ovdl::v2script::ast::Node const* root) { + size_t total_expected_religions = 0; + bool ret = expect_dictionary_reserve_length( + religion_groups, + [this, &total_expected_religions](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + bool ret = expect_length(add_variable_callback(total_expected_religions))(value); + ret &= add_religion_group(key); + return ret; + } + )(root); + lock_religion_groups(); + reserve_more_religions(total_expected_religions); + ret &= expect_religion_group_dictionary( + [this](ReligionGroup const& religion_group, ovdl::v2script::ast::Node const* religion_group_value) -> bool { + return expect_dictionary([this, &religion_group](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { + colour_t colour = colour_t::null(); + Religion::icon_t icon = 0; + bool is_pagan = false; + + bool ret = expect_dictionary_keys( + "icon", ONE_EXACTLY, expect_uint(assign_variable_callback(icon)), + "color", ONE_EXACTLY, expect_colour(assign_variable_callback(colour)), + "pagan", ZERO_OR_ONE, expect_bool(assign_variable_callback(is_pagan)) + )(value); + ret &= add_religion(key, colour, religion_group, icon, is_pagan); + return ret; + })(religion_group_value); + } + )(root); + lock_religions(); + return ret; +} diff --git a/src/openvic-simulation/population/ReligionManager.hpp b/src/openvic-simulation/population/ReligionManager.hpp new file mode 100644 index 000000000..ec9678c48 --- /dev/null +++ b/src/openvic-simulation/population/ReligionManager.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/population/Religion.hpp" +#include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct ReligionManager { + private: + IdentifierRegistry IDENTIFIER_REGISTRY(religion_group); + IdentifierRegistry IDENTIFIER_REGISTRY(religion); + + public: + bool add_religion_group(std::string_view identifier); + + bool add_religion( + std::string_view identifier, colour_t colour, ReligionGroup const& group, Religion::icon_t icon, bool is_pagan + ); + + bool load_religion_file(ovdl::v2script::ast::Node const* root); + }; +} diff --git a/src/openvic-simulation/research/Invention.cpp b/src/openvic-simulation/research/Invention.cpp index 75a0b676a..f7535cfaa 100644 --- a/src/openvic-simulation/research/Invention.cpp +++ b/src/openvic-simulation/research/Invention.cpp @@ -1,13 +1,6 @@ #include "Invention.hpp" -#include "openvic-simulation/economy/BuildingType.hpp" -#include "openvic-simulation/map/Crime.hpp" -#include "openvic-simulation/military/UnitType.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" -#include "openvic-simulation/research/Technology.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; Invention::Invention( index_t new_index, @@ -42,129 +35,3 @@ bool Invention::parse_scripts(DefinitionManager const& definition_manager) { return ret; } - -bool InventionManager::add_invention( - std::string_view identifier, ModifierValue&& values, bool news, Invention::unit_set_t&& activated_units, - Invention::building_set_t&& activated_buildings, Invention::crime_set_t&& enabled_crimes, bool unlock_gas_attack, - bool unlock_gas_defence, ConditionScript&& limit, ConditionalWeightBase&& chance, - memory::vector&& raw_associated_tech_identifiers -) { - if (identifier.empty()) { - spdlog::error_s("Invalid invention identifier - empty!"); - return false; - } - - return inventions.emplace_item( - identifier, Invention::index_t { get_invention_count() }, identifier, std::move(values), news, - std::move(activated_units), std::move(activated_buildings), std::move(enabled_crimes), unlock_gas_attack, - unlock_gas_defence, std::move(limit), std::move(chance), - std::move(raw_associated_tech_identifiers) - ); -} - -bool InventionManager::load_inventions_file( - TechnologyManager const& tech_manager, ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, - BuildingTypeManager const& building_type_manager, CrimeManager const& crime_manager, ast::NodeCPtr root -) { - return expect_dictionary_reserve_length(inventions, [&](std::string_view identifier, ast::NodeCPtr value) -> bool { - using enum scope_type_t; - - // TODO - use the same variable for all modifiers rather than combining them at the end? - ModifierValue loose_modifiers; - ModifierValue modifiers; - - Invention::unit_set_t activated_units; - Invention::building_set_t activated_buildings; - Invention::crime_set_t enabled_crimes; - - bool unlock_gas_attack = false; - bool unlock_gas_defence = false; - bool news = true; // defaults to true! - - ConditionScript limit { COUNTRY, COUNTRY, NO_SCOPE }; - ConditionalWeightBase chance { COUNTRY, COUNTRY, NO_SCOPE }; - - memory::vector found_tech_ids; - - auto parse_limit_and_find_techs = [&](ast::NodeCPtr node) -> bool { - - if (!limit.expect_script()(node)) { - return false; - } - - expect_dictionary( - [&](std::string_view key, ast::NodeCPtr /*value*/) -> bool { - if (tech_manager.get_technology_by_identifier(key) != nullptr) { - found_tech_ids.push_back(memory::string(key)); - } - return true; - } - )(node); - - return true; - }; - - bool ret = NodeTools::expect_dictionary_keys_and_default( - modifier_manager.expect_base_country_modifier(loose_modifiers), - "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), - "limit", ONE_EXACTLY, parse_limit_and_find_techs, - "chance", ONE_EXACTLY, chance.expect_conditional_weight(), - "effect", ZERO_OR_ONE, NodeTools::expect_dictionary_keys_and_default( - modifier_manager.expect_technology_modifier(modifiers), - "gas_attack", ZERO_OR_ONE, expect_bool(assign_variable_callback(unlock_gas_attack)), - "gas_defence", ZERO_OR_ONE, expect_bool(assign_variable_callback(unlock_gas_defence)), - "activate_unit", ZERO_OR_MORE, - unit_type_manager.expect_unit_type_identifier(set_callback_pointer(activated_units)), - "activate_building", ZERO_OR_MORE, building_type_manager.expect_building_type_identifier( - set_callback_pointer(activated_buildings) - ), - "enable_crime", ZERO_OR_ONE, crime_manager.expect_crime_modifier_identifier( - set_callback_pointer(enabled_crimes) - ) - ) - )(value); - - modifiers += loose_modifiers; - - ret &= add_invention( - identifier, std::move(modifiers), news, std::move(activated_units), std::move(activated_buildings), - std::move(enabled_crimes), unlock_gas_attack, unlock_gas_defence, std::move(limit), std::move(chance), - std::move(found_tech_ids) - ); - - return ret; - })(root); -} - -bool InventionManager::generate_invention_links(TechnologyManager& tech_manager) { - if (!inventions.is_locked()) { - spdlog::error_s("Cannot generate invention links until inventions are locked!"); - return false; - } - - bool ret = true; - - for (Invention& invention : inventions.get_items()) { - for (const memory::string& tech_id : invention.raw_associated_tech_identifiers) { - Technology* tech = tech_manager.get_technology_by_identifier(tech_id); - if (tech == nullptr) { - continue; - } - tech->add_invention(invention); - } - - invention.raw_associated_tech_identifiers.clear(); - } - - return ret; -} - -bool InventionManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - - for (Invention& invention : inventions.get_items()) { - ret &= invention.parse_scripts(definition_manager); - } - - return ret; -} diff --git a/src/openvic-simulation/research/Invention.hpp b/src/openvic-simulation/research/Invention.hpp index 9fe755763..9bdfa8aef 100644 --- a/src/openvic-simulation/research/Invention.hpp +++ b/src/openvic-simulation/research/Invention.hpp @@ -3,22 +3,15 @@ #include "openvic-simulation/modifier/Modifier.hpp" #include "openvic-simulation/scripts/ConditionalWeight.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { - struct BuildingType; - struct BuildingTypeManager; struct Crime; - struct CrimeManager; struct InventionManager; struct UnitType; - struct UnitTypeManager; - struct Technology; - struct TechnologyManager; struct Invention : HasIndex, Modifier { friend struct InventionManager; @@ -60,26 +53,4 @@ namespace OpenVic { ); Invention(Invention&&) = default; }; - - struct InventionManager { - IdentifierRegistry IDENTIFIER_REGISTRY(invention); - - public: - bool add_invention( - std::string_view identifier, ModifierValue&& values, bool news, Invention::unit_set_t&& activated_units, - Invention::building_set_t&& activated_buildings, Invention::crime_set_t&& enabled_crimes, bool unlock_gas_attack, - bool unlock_gas_defence, ConditionScript&& limit, ConditionalWeightBase&& chance, - memory::vector&& raw_associated_tech_identifiers - ); - - bool load_inventions_file( - TechnologyManager const& tech_manager, - ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, - BuildingTypeManager const& building_type_manager, CrimeManager const& crime_manager, ast::NodeCPtr root - ); // inventions/*.txt - - bool generate_invention_links(TechnologyManager& tech_manager); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } \ No newline at end of file diff --git a/src/openvic-simulation/research/InventionManager.cpp b/src/openvic-simulation/research/InventionManager.cpp new file mode 100644 index 000000000..755aaf19e --- /dev/null +++ b/src/openvic-simulation/research/InventionManager.cpp @@ -0,0 +1,137 @@ +#include "InventionManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/BuildingTypeManager.hpp" +#include "openvic-simulation/map/CrimeManager.hpp" +#include "openvic-simulation/military/UnitTypeManager.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" +#include "openvic-simulation/research/TechnologyManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool InventionManager::add_invention( + std::string_view identifier, ModifierValue&& values, bool news, Invention::unit_set_t&& activated_units, + Invention::building_set_t&& activated_buildings, Invention::crime_set_t&& enabled_crimes, bool unlock_gas_attack, + bool unlock_gas_defence, ConditionScript&& limit, ConditionalWeightBase&& chance, + memory::vector&& raw_associated_tech_identifiers +) { + if (identifier.empty()) { + spdlog::error_s("Invalid invention identifier - empty!"); + return false; + } + + return inventions.emplace_item( + identifier, Invention::index_t { get_invention_count() }, identifier, std::move(values), news, + std::move(activated_units), std::move(activated_buildings), std::move(enabled_crimes), unlock_gas_attack, + unlock_gas_defence, std::move(limit), std::move(chance), + std::move(raw_associated_tech_identifiers) + ); +} + +bool InventionManager::load_inventions_file( + TechnologyManager const& tech_manager, ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, + BuildingTypeManager const& building_type_manager, CrimeManager const& crime_manager, ovdl::v2script::ast::Node const* root +) { + return expect_dictionary_reserve_length(inventions, [&](std::string_view identifier, ovdl::v2script::ast::Node const* value) -> bool { + using enum scope_type_t; + + // TODO - use the same variable for all modifiers rather than combining them at the end? + ModifierValue loose_modifiers; + ModifierValue modifiers; + + Invention::unit_set_t activated_units; + Invention::building_set_t activated_buildings; + Invention::crime_set_t enabled_crimes; + + bool unlock_gas_attack = false; + bool unlock_gas_defence = false; + bool news = true; // defaults to true! + + ConditionScript limit { COUNTRY, COUNTRY, NO_SCOPE }; + ConditionalWeightBase chance { COUNTRY, COUNTRY, NO_SCOPE }; + + memory::vector found_tech_ids; + + auto parse_limit_and_find_techs = [&](ovdl::v2script::ast::Node const* node) -> bool { + + if (!limit.expect_script()(node)) { + return false; + } + + expect_dictionary( + [&](std::string_view key, ovdl::v2script::ast::Node const* /*value*/) -> bool { + if (tech_manager.get_technology_by_identifier(key) != nullptr) { + found_tech_ids.push_back(memory::string(key)); + } + return true; + } + )(node); + + return true; + }; + + bool ret = NodeTools::expect_dictionary_keys_and_default( + modifier_manager.expect_base_country_modifier(loose_modifiers), + "news", ZERO_OR_ONE, expect_bool(assign_variable_callback(news)), + "limit", ONE_EXACTLY, parse_limit_and_find_techs, + "chance", ONE_EXACTLY, chance.expect_conditional_weight(), + "effect", ZERO_OR_ONE, NodeTools::expect_dictionary_keys_and_default( + modifier_manager.expect_technology_modifier(modifiers), + "gas_attack", ZERO_OR_ONE, expect_bool(assign_variable_callback(unlock_gas_attack)), + "gas_defence", ZERO_OR_ONE, expect_bool(assign_variable_callback(unlock_gas_defence)), + "activate_unit", ZERO_OR_MORE, + unit_type_manager.expect_unit_type_identifier(set_callback_pointer(activated_units)), + "activate_building", ZERO_OR_MORE, building_type_manager.expect_building_type_identifier( + set_callback_pointer(activated_buildings) + ), + "enable_crime", ZERO_OR_ONE, crime_manager.expect_crime_modifier_identifier( + set_callback_pointer(enabled_crimes) + ) + ) + )(value); + + modifiers += loose_modifiers; + + ret &= add_invention( + identifier, std::move(modifiers), news, std::move(activated_units), std::move(activated_buildings), + std::move(enabled_crimes), unlock_gas_attack, unlock_gas_defence, std::move(limit), std::move(chance), + std::move(found_tech_ids) + ); + + return ret; + })(root); +} + +bool InventionManager::generate_invention_links(TechnologyManager& tech_manager) { + if (!inventions.is_locked()) { + spdlog::error_s("Cannot generate invention links until inventions are locked!"); + return false; + } + + bool ret = true; + + for (Invention& invention : inventions.get_items()) { + for (const memory::string& tech_id : invention.raw_associated_tech_identifiers) { + Technology* tech = tech_manager.get_technology_by_identifier(tech_id); + if (tech == nullptr) { + continue; + } + tech->add_invention(invention); + } + + invention.raw_associated_tech_identifiers.clear(); + } + + return ret; +} + +bool InventionManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + + for (Invention& invention : inventions.get_items()) { + ret &= invention.parse_scripts(definition_manager); + } + + return ret; +} diff --git a/src/openvic-simulation/research/InventionManager.hpp b/src/openvic-simulation/research/InventionManager.hpp new file mode 100644 index 000000000..db2eb3e46 --- /dev/null +++ b/src/openvic-simulation/research/InventionManager.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include "openvic-simulation/research/Invention.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct BuildingTypeManager; + struct CrimeManager; + struct InventionManager; + struct UnitTypeManager; + struct TechnologyManager; + + struct InventionManager { + IdentifierRegistry IDENTIFIER_REGISTRY(invention); + + public: + bool add_invention( + std::string_view identifier, ModifierValue&& values, bool news, Invention::unit_set_t&& activated_units, + Invention::building_set_t&& activated_buildings, Invention::crime_set_t&& enabled_crimes, bool unlock_gas_attack, + bool unlock_gas_defence, ConditionScript&& limit, ConditionalWeightBase&& chance, + memory::vector&& raw_associated_tech_identifiers + ); + + bool load_inventions_file( + TechnologyManager const& tech_manager, + ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, + BuildingTypeManager const& building_type_manager, CrimeManager const& crime_manager, ovdl::v2script::ast::Node const* root + ); // inventions/*.txt + + bool generate_invention_links(TechnologyManager& tech_manager); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/research/ResearchManager.hpp b/src/openvic-simulation/research/ResearchManager.hpp index d402a3462..c794f6847 100644 --- a/src/openvic-simulation/research/ResearchManager.hpp +++ b/src/openvic-simulation/research/ResearchManager.hpp @@ -1,7 +1,7 @@ #pragma once -#include "openvic-simulation/research/Invention.hpp" -#include "openvic-simulation/research/Technology.hpp" +#include "openvic-simulation/research/InventionManager.hpp" +#include "openvic-simulation/research/TechnologyManager.hpp" namespace OpenVic { struct ResearchManager { diff --git a/src/openvic-simulation/research/Technology.cpp b/src/openvic-simulation/research/Technology.cpp index 628d4894a..898d64a7e 100644 --- a/src/openvic-simulation/research/Technology.cpp +++ b/src/openvic-simulation/research/Technology.cpp @@ -1,13 +1,6 @@ #include "Technology.hpp" -#include "openvic-simulation/economy/BuildingType.hpp" -#include "openvic-simulation/military/UnitType.hpp" -#include "openvic-simulation/modifier/ModifierManager.hpp" -#include "openvic-simulation/types/FixedVector.hpp" -#include "openvic-simulation/types/TypedIndices.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; TechnologyFolder::TechnologyFolder(std::string_view new_identifier, index_t new_index) : HasIdentifier { new_identifier }, HasIndex { new_index } {} @@ -46,265 +39,3 @@ bool Technology::parse_scripts(DefinitionManager const& definition_manager) { TechnologySchool::TechnologySchool(std::string_view new_identifier, ModifierValue&& new_values) : Modifier { new_identifier, std::move(new_values), modifier_type_t::TECH_SCHOOL } {} - -bool TechnologyManager::add_technology_folder(std::string_view identifier) { - if (identifier.empty()) { - spdlog::error_s("Invalid technology folder identifier - empty!"); - return false; - } - - return technology_folders.emplace_item( - identifier, - identifier, TechnologyFolder::index_t { get_technology_folder_count() } - ); -} - -bool TechnologyManager::add_technology_area(std::string_view identifier, TechnologyFolder const& folder) { - if (identifier.empty()) { - spdlog::error_s("Invalid technology area identifier - empty!"); - return false; - } - - return technology_areas.emplace_item( - identifier, - identifier, folder - ); -} - -bool TechnologyManager::add_technology( - std::string_view identifier, TechnologyArea* area, Date::year_t year, fixed_point_t cost, bool unciv_military, - std::optional&& unit_variant, Technology::unit_set_t&& activated_units, - Technology::building_set_t&& activated_buildings, ModifierValue&& values, ConditionalWeightFactorMul&& ai_chance -) { - if (identifier.empty()) { - spdlog::error_s("Invalid technology identifier - empty!"); - return false; - } - - if (area == nullptr) { - spdlog::error_s("Null area for technology \"{}\"!", identifier); - return false; - } - - static constexpr size_t MAX_TECHS_IN_AREA = std::numeric_limits::max() + 1; - - const size_t index_in_area = area->get_tech_count(); - - if (index_in_area >= MAX_TECHS_IN_AREA) { - spdlog::error_s( - "Cannot add technology \"{}\" - too many technologies in area \"{}\"! Each area can have at most {} technologies.", - identifier, *area, MAX_TECHS_IN_AREA - ); - return false; - } - - if (!technologies.emplace_item( - identifier, - identifier, - Technology::index_t { get_technology_count() }, - *area, - year, - cost, - static_cast(index_in_area), - unciv_military, - std::move(unit_variant), - std::move(activated_units), - std::move(activated_buildings), - std::move(values), - std::move(ai_chance) - )) { - return false; - } - - area->tech_count++; - return true; -} - -bool TechnologyManager::add_technology_school(std::string_view identifier, ModifierValue&& values) { - if (identifier.empty()) { - spdlog::error_s("Invalid technology school identifier - empty!"); - return false; - } - - return technology_schools.emplace_item( - identifier, - identifier, std::move(values) - ); -} - -bool TechnologyManager::load_technology_file_folders_and_areas(ast::NodeCPtr root) { - return expect_dictionary_keys( - "folders", ONE_EXACTLY, [this](ast::NodeCPtr root_value) -> bool { - const bool ret = expect_dictionary_reserve_length( - technology_folders, - [this](std::string_view folder_key, ast::NodeCPtr folder_value) -> bool { - if (!add_technology_folder(folder_key)) { - spdlog::error_s("Failed to add and retrieve technology folder: \"{}\"", folder_key); - return false; - } - - TechnologyFolder const& current_folder = get_back_technology_folder(); - - return expect_list_reserve_length( - technology_areas, - expect_identifier( - [this, ¤t_folder](std::string_view identifier) -> bool { - return add_technology_area(identifier, current_folder); - } - ) - )(folder_value); - } - )(root_value); - - lock_technology_folders(); - lock_technology_areas(); - - return ret; - }, - /* Never fail because of "schools", even if it's missing or there are duplicate entries, - * those issues will be caught by load_technology_file_schools. */ - "schools", ZERO_OR_MORE, success_callback - )(root); -} - -bool TechnologyManager::load_technology_file_schools( - ModifierManager const& modifier_manager, ast::NodeCPtr root -) { - if (!technology_folders.is_locked() || !technology_areas.is_locked()) { - spdlog::error_s("Cannot load technology schools until technology folders and areas are locked!"); - return false; - } - return expect_dictionary_keys( - /* Never fail because of "folders", even if it's missing or there are duplicate entries, - * those issues will have been caught by load_technology_file_folders_and_areas. */ - "folders", ZERO_OR_MORE, success_callback, - "schools", ONE_EXACTLY, [this, &modifier_manager](ast::NodeCPtr root_value) -> bool { - const bool ret = expect_dictionary_reserve_length( - technology_schools, - [this, &modifier_manager](std::string_view school_key, ast::NodeCPtr school_value) -> bool { - ModifierValue modifiers; - - bool ret = NodeTools::expect_dictionary( - modifier_manager.expect_base_country_modifier(modifiers) - )(school_value); - - ret &= add_technology_school(school_key, std::move(modifiers)); - - return ret; - } - )(root_value); - - lock_technology_schools(); - - return ret; - } - )(root); -} - -bool TechnologyManager::load_technologies_file( - ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, - BuildingTypeManager const& building_type_manager, ast::NodeCPtr root -) { - return expect_dictionary_reserve_length(technologies, [this, &modifier_manager, &unit_type_manager, &building_type_manager]( - std::string_view tech_key, ast::NodeCPtr tech_value - ) -> bool { - using enum scope_type_t; - - ModifierValue modifiers; - TechnologyArea* area = nullptr; - Date::year_t year = 0; - fixed_point_t cost = 0; - bool unciv_military = false; - std::optional unit_variant; - Technology::unit_set_t activated_units; - Technology::building_set_t activated_buildings; - ConditionalWeightFactorMul ai_chance { COUNTRY, COUNTRY, NO_SCOPE }; - - bool ret = NodeTools::expect_dictionary_keys_and_default( - modifier_manager.expect_technology_modifier(modifiers), - "area", ONE_EXACTLY, technology_areas.expect_item_identifier(assign_variable_callback_pointer(area)), - "year", ONE_EXACTLY, expect_uint(assign_variable_callback(year)), - "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(cost)), - "unciv_military", ZERO_OR_ONE, expect_bool(assign_variable_callback(unciv_military)), - "unit", ZERO_OR_ONE, expect_uint(assign_variable_callback_opt(unit_variant)), - "activate_unit", ZERO_OR_MORE, - unit_type_manager.expect_unit_type_identifier(set_callback_pointer(activated_units)), - "activate_building", ZERO_OR_MORE, building_type_manager.expect_building_type_identifier( - set_callback_pointer(activated_buildings) - ), - "ai_chance", ONE_EXACTLY, ai_chance.expect_conditional_weight() - )(tech_value); - - ret &= add_technology( - tech_key, area, year, cost, unciv_military, std::move(unit_variant), std::move(activated_units), - std::move(activated_buildings), std::move(modifiers), std::move(ai_chance) - ); - return ret; - })(root); -} - -bool TechnologyManager::generate_modifiers(ModifierManager& modifier_manager) const { - using enum ModifierEffect::format_t; - using enum ModifierEffect::target_t; - - memory::FixedVector& research_bonus_effects = - modifier_manager.modifier_effect_cache.research_bonus_effects; - - research_bonus_effects = std::move( - decltype(ModifierEffectCache::research_bonus_effects) { - generate_values, - technology_folder_index_t(get_technology_folder_count()) - } - ); - - bool ret = true; - - for (TechnologyFolder const& folder : get_technology_folders()) { - const memory::string modifier_identifier = memory::fmt::format("{}_research_bonus", folder); - - ret &= modifier_manager.register_base_country_modifier_effect( - research_bonus_effects[folder.index], modifier_identifier, FORMAT_x100_1DP_PC_POS, modifier_identifier - ); - } - - return ret; -} - -bool TechnologyManager::parse_scripts(DefinitionManager const& definition_manager) { - bool ret = true; - - for (Technology& technology : technologies.get_items()) { - ret &= technology.parse_scripts(definition_manager); - } - - return ret; -} - -bool TechnologyManager::generate_technology_lists() { - if (!technology_folders.is_locked() || !technology_areas.is_locked() || !technologies.is_locked()) { - spdlog::error_s("Cannot generate technology lists until technology folders, areas, and technologies are locked!"); - return false; - } - - for (TechnologyArea const& area : technology_areas.get_items()) { - const_cast(area.folder).technology_areas.emplace_back(area); - } - - for (Technology const& tech : technologies.get_items()) { - const_cast(tech.area).technologies.emplace_back(tech); - } - - bool ret = true; - - for (TechnologyArea const& area : technology_areas.get_items()) { - if (area.get_technologies().size() != area.get_tech_count()) { - spdlog::error_s( - "Technology area \"{}\" has a mismatch between tech count ({}) and tech list size ({})!", - area, area.get_tech_count(), area.get_technologies().size() - ); - ret = false; - } - } - - return ret; -} diff --git a/src/openvic-simulation/research/Technology.hpp b/src/openvic-simulation/research/Technology.hpp index 25997a222..c0a7be304 100644 --- a/src/openvic-simulation/research/Technology.hpp +++ b/src/openvic-simulation/research/Technology.hpp @@ -9,17 +9,14 @@ #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" #include "openvic-simulation/types/HasIndex.hpp" -#include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/types/TypedIndices.hpp" #include "openvic-simulation/types/UnitVariant.hpp" namespace OpenVic { struct BuildingType; - struct BuildingTypeManager; struct Technology; struct TechnologyArea; struct TechnologyManager; - struct UnitTypeManager; struct Invention; struct TechnologyFolder : HasIdentifier, HasIndex { @@ -95,46 +92,4 @@ namespace OpenVic { public: TechnologySchool(std::string_view new_identifier, ModifierValue&& new_values); }; - - struct TechnologyManager { - friend struct InventionManager; - - IdentifierRegistry IDENTIFIER_REGISTRY(technology_folder); - IdentifierRegistry IDENTIFIER_REGISTRY(technology_area); - IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(technology, technologies); - IdentifierRegistry IDENTIFIER_REGISTRY(technology_school); - - IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_PLURAL(technology, technologies); - - public: - bool add_technology_folder(std::string_view identifier); - - bool add_technology_area(std::string_view identifier, TechnologyFolder const& folder); - - bool add_technology( - std::string_view identifier, TechnologyArea* area, Date::year_t year, fixed_point_t cost, bool unciv_military, - std::optional&& unit_variant, Technology::unit_set_t&& activated_units, - Technology::building_set_t&& activated_buildings, ModifierValue&& values, ConditionalWeightFactorMul&& ai_chance - ); - - bool add_technology_school(std::string_view identifier, ModifierValue&& values); - - /* Both of these functions load data from "common/technology.txt", they are separated because the schools depend - * on modifiers generated from the folder definitions and so loading must be staggered. */ - bool load_technology_file_folders_and_areas(ast::NodeCPtr root); - bool load_technology_file_schools(ModifierManager const& modifier_manager, ast::NodeCPtr root); - - /* Loaded from "technologies/.txt" files named after technology folders. */ - bool load_technologies_file( - ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, - BuildingTypeManager const& building_type_manager, ast::NodeCPtr root - ); - - bool generate_modifiers(ModifierManager& modifier_manager) const; - - /* Populates the lists of technology areas for each technology folder and technologies for each technology area. */ - bool generate_technology_lists(); - - bool parse_scripts(DefinitionManager const& definition_manager); - }; } diff --git a/src/openvic-simulation/research/TechnologyManager.cpp b/src/openvic-simulation/research/TechnologyManager.cpp new file mode 100644 index 000000000..a0faca333 --- /dev/null +++ b/src/openvic-simulation/research/TechnologyManager.cpp @@ -0,0 +1,275 @@ +#include "TechnologyManager.hpp" + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/economy/BuildingTypeManager.hpp" +#include "openvic-simulation/military/UnitTypeManager.hpp" +#include "openvic-simulation/modifier/ModifierManager.hpp" +#include "openvic-simulation/types/FixedVector.hpp" +#include "openvic-simulation/types/TypedIndices.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +bool TechnologyManager::add_technology_folder(std::string_view identifier) { + if (identifier.empty()) { + spdlog::error_s("Invalid technology folder identifier - empty!"); + return false; + } + + return technology_folders.emplace_item( + identifier, + identifier, TechnologyFolder::index_t { get_technology_folder_count() } + ); +} + +bool TechnologyManager::add_technology_area(std::string_view identifier, TechnologyFolder const& folder) { + if (identifier.empty()) { + spdlog::error_s("Invalid technology area identifier - empty!"); + return false; + } + + return technology_areas.emplace_item( + identifier, + identifier, folder + ); +} + +bool TechnologyManager::add_technology( + std::string_view identifier, TechnologyArea* area, Date::year_t year, fixed_point_t cost, bool unciv_military, + std::optional&& unit_variant, Technology::unit_set_t&& activated_units, + Technology::building_set_t&& activated_buildings, ModifierValue&& values, ConditionalWeightFactorMul&& ai_chance +) { + if (identifier.empty()) { + spdlog::error_s("Invalid technology identifier - empty!"); + return false; + } + + if (area == nullptr) { + spdlog::error_s("Null area for technology \"{}\"!", identifier); + return false; + } + + static constexpr size_t MAX_TECHS_IN_AREA = std::numeric_limits::max() + 1; + + const size_t index_in_area = area->get_tech_count(); + + if (index_in_area >= MAX_TECHS_IN_AREA) { + spdlog::error_s( + "Cannot add technology \"{}\" - too many technologies in area \"{}\"! Each area can have at most {} technologies.", + identifier, *area, MAX_TECHS_IN_AREA + ); + return false; + } + + if (!technologies.emplace_item( + identifier, + identifier, + Technology::index_t { get_technology_count() }, + *area, + year, + cost, + static_cast(index_in_area), + unciv_military, + std::move(unit_variant), + std::move(activated_units), + std::move(activated_buildings), + std::move(values), + std::move(ai_chance) + )) { + return false; + } + + area->tech_count++; + return true; +} + +bool TechnologyManager::add_technology_school(std::string_view identifier, ModifierValue&& values) { + if (identifier.empty()) { + spdlog::error_s("Invalid technology school identifier - empty!"); + return false; + } + + return technology_schools.emplace_item( + identifier, + identifier, std::move(values) + ); +} + +bool TechnologyManager::load_technology_file_folders_and_areas(ovdl::v2script::ast::Node const* root) { + return expect_dictionary_keys( + "folders", ONE_EXACTLY, [this](ovdl::v2script::ast::Node const* root_value) -> bool { + const bool ret = expect_dictionary_reserve_length( + technology_folders, + [this](std::string_view folder_key, ovdl::v2script::ast::Node const* folder_value) -> bool { + if (!add_technology_folder(folder_key)) { + spdlog::error_s("Failed to add and retrieve technology folder: \"{}\"", folder_key); + return false; + } + + TechnologyFolder const& current_folder = get_back_technology_folder(); + + return expect_list_reserve_length( + technology_areas, + expect_identifier( + [this, ¤t_folder](std::string_view identifier) -> bool { + return add_technology_area(identifier, current_folder); + } + ) + )(folder_value); + } + )(root_value); + + lock_technology_folders(); + lock_technology_areas(); + + return ret; + }, + /* Never fail because of "schools", even if it's missing or there are duplicate entries, + * those issues will be caught by load_technology_file_schools. */ + "schools", ZERO_OR_MORE, success_callback + )(root); +} + +bool TechnologyManager::load_technology_file_schools( + ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root +) { + if (!technology_folders.is_locked() || !technology_areas.is_locked()) { + spdlog::error_s("Cannot load technology schools until technology folders and areas are locked!"); + return false; + } + return expect_dictionary_keys( + /* Never fail because of "folders", even if it's missing or there are duplicate entries, + * those issues will have been caught by load_technology_file_folders_and_areas. */ + "folders", ZERO_OR_MORE, success_callback, + "schools", ONE_EXACTLY, [this, &modifier_manager](ovdl::v2script::ast::Node const* root_value) -> bool { + const bool ret = expect_dictionary_reserve_length( + technology_schools, + [this, &modifier_manager](std::string_view school_key, ovdl::v2script::ast::Node const* school_value) -> bool { + ModifierValue modifiers; + + bool ret = NodeTools::expect_dictionary( + modifier_manager.expect_base_country_modifier(modifiers) + )(school_value); + + ret &= add_technology_school(school_key, std::move(modifiers)); + + return ret; + } + )(root_value); + + lock_technology_schools(); + + return ret; + } + )(root); +} + +bool TechnologyManager::load_technologies_file( + ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, + BuildingTypeManager const& building_type_manager, ovdl::v2script::ast::Node const* root +) { + return expect_dictionary_reserve_length(technologies, [this, &modifier_manager, &unit_type_manager, &building_type_manager]( + std::string_view tech_key, ovdl::v2script::ast::Node const* tech_value + ) -> bool { + using enum scope_type_t; + + ModifierValue modifiers; + TechnologyArea* area = nullptr; + Date::year_t year = 0; + fixed_point_t cost = 0; + bool unciv_military = false; + std::optional unit_variant; + Technology::unit_set_t activated_units; + Technology::building_set_t activated_buildings; + ConditionalWeightFactorMul ai_chance { COUNTRY, COUNTRY, NO_SCOPE }; + + bool ret = NodeTools::expect_dictionary_keys_and_default( + modifier_manager.expect_technology_modifier(modifiers), + "area", ONE_EXACTLY, technology_areas.expect_item_identifier(assign_variable_callback_pointer(area)), + "year", ONE_EXACTLY, expect_uint(assign_variable_callback(year)), + "cost", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(cost)), + "unciv_military", ZERO_OR_ONE, expect_bool(assign_variable_callback(unciv_military)), + "unit", ZERO_OR_ONE, expect_uint(assign_variable_callback_opt(unit_variant)), + "activate_unit", ZERO_OR_MORE, + unit_type_manager.expect_unit_type_identifier(set_callback_pointer(activated_units)), + "activate_building", ZERO_OR_MORE, building_type_manager.expect_building_type_identifier( + set_callback_pointer(activated_buildings) + ), + "ai_chance", ONE_EXACTLY, ai_chance.expect_conditional_weight() + )(tech_value); + + ret &= add_technology( + tech_key, area, year, cost, unciv_military, std::move(unit_variant), std::move(activated_units), + std::move(activated_buildings), std::move(modifiers), std::move(ai_chance) + ); + return ret; + })(root); +} + +bool TechnologyManager::generate_modifiers(ModifierManager& modifier_manager) const { + using enum ModifierEffect::format_t; + using enum ModifierEffect::target_t; + + memory::FixedVector< + ModifierEffect const*, + technology_folder_index_t + >& research_bonus_effects = modifier_manager.modifier_effect_cache.research_bonus_effects; + + research_bonus_effects = std::move( + decltype(ModifierEffectCache::research_bonus_effects) { + generate_values, + technology_folder_index_t(get_technology_folder_count()) + } + ); + + bool ret = true; + + for (TechnologyFolder const& folder : get_technology_folders()) { + const memory::string modifier_identifier = memory::fmt::format("{}_research_bonus", folder); + + ret &= modifier_manager.register_base_country_modifier_effect( + research_bonus_effects[folder.index], modifier_identifier, FORMAT_x100_1DP_PC_POS, modifier_identifier + ); + } + + return ret; +} + +bool TechnologyManager::parse_scripts(DefinitionManager const& definition_manager) { + bool ret = true; + + for (Technology& technology : technologies.get_items()) { + ret &= technology.parse_scripts(definition_manager); + } + + return ret; +} + +bool TechnologyManager::generate_technology_lists() { + if (!technology_folders.is_locked() || !technology_areas.is_locked() || !technologies.is_locked()) { + spdlog::error_s("Cannot generate technology lists until technology folders, areas, and technologies are locked!"); + return false; + } + + for (TechnologyArea const& area : technology_areas.get_items()) { + const_cast(area.folder).technology_areas.emplace_back(area); + } + + for (Technology const& tech : technologies.get_items()) { + const_cast(tech.area).technologies.emplace_back(tech); + } + + bool ret = true; + + for (TechnologyArea const& area : technology_areas.get_items()) { + if (area.get_technologies().size() != area.get_tech_count()) { + spdlog::error_s( + "Technology area \"{}\" has a mismatch between tech count ({}) and tech list size ({})!", + area, area.get_tech_count(), area.get_technologies().size() + ); + ret = false; + } + } + + return ret; +} diff --git a/src/openvic-simulation/research/TechnologyManager.hpp b/src/openvic-simulation/research/TechnologyManager.hpp new file mode 100644 index 000000000..618664cdf --- /dev/null +++ b/src/openvic-simulation/research/TechnologyManager.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include "openvic-simulation/dataloader/Node_forwarded.hpp" +#include "openvic-simulation/scripts/ConditionalWeight.hpp" +#include "openvic-simulation/research/Technology.hpp" +#include "openvic-simulation/types/Date.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/types/UnitVariant.hpp" + +namespace OpenVic { + struct BuildingTypeManager; + struct TechnologyManager; + struct UnitTypeManager; + + struct TechnologyManager { + friend struct InventionManager; + + IdentifierRegistry IDENTIFIER_REGISTRY(technology_folder); + IdentifierRegistry IDENTIFIER_REGISTRY(technology_area); + IdentifierRegistry IDENTIFIER_REGISTRY_CUSTOM_PLURAL(technology, technologies); + IdentifierRegistry IDENTIFIER_REGISTRY(technology_school); + + IDENTIFIER_REGISTRY_NON_CONST_ACCESSORS_CUSTOM_PLURAL(technology, technologies); + + public: + bool add_technology_folder(std::string_view identifier); + + bool add_technology_area(std::string_view identifier, TechnologyFolder const& folder); + + bool add_technology( + std::string_view identifier, TechnologyArea* area, Date::year_t year, fixed_point_t cost, bool unciv_military, + std::optional&& unit_variant, Technology::unit_set_t&& activated_units, + Technology::building_set_t&& activated_buildings, ModifierValue&& values, ConditionalWeightFactorMul&& ai_chance + ); + + bool add_technology_school(std::string_view identifier, ModifierValue&& values); + + /* Both of these functions load data from "common/technology.txt", they are separated because the schools depend + * on modifiers generated from the folder definitions and so loading must be staggered. */ + bool load_technology_file_folders_and_areas(ovdl::v2script::ast::Node const* root); + bool load_technology_file_schools(ModifierManager const& modifier_manager, ovdl::v2script::ast::Node const* root); + + /* Loaded from "technologies/.txt" files named after technology folders. */ + bool load_technologies_file( + ModifierManager const& modifier_manager, UnitTypeManager const& unit_type_manager, + BuildingTypeManager const& building_type_manager, ovdl::v2script::ast::Node const* root + ); + + bool generate_modifiers(ModifierManager& modifier_manager) const; + + /* Populates the lists of technology areas for each technology folder and technologies for each technology area. */ + bool generate_technology_lists(); + + bool parse_scripts(DefinitionManager const& definition_manager); + }; +} diff --git a/src/openvic-simulation/scripts/Condition.cpp b/src/openvic-simulation/scripts/Condition.cpp index eb8ef376c..d406af424 100644 --- a/src/openvic-simulation/scripts/Condition.cpp +++ b/src/openvic-simulation/scripts/Condition.cpp @@ -1,12 +1,6 @@ #include "Condition.hpp" -#include - -#include "openvic-simulation/dataloader/NodeTools.hpp" -#include "openvic-simulation/DefinitionManager.hpp" - using namespace OpenVic; -using namespace OpenVic::NodeTools; using enum value_type_t; using enum scope_type_t; @@ -26,768 +20,3 @@ ConditionNode::ConditionNode( HasIdentifier const* new_condition_value_item ) : condition { new_condition }, value { std::move(new_value) }, valid { new_valid }, condition_key_item { new_condition_key_item }, condition_value_item { new_condition_key_item } {} - -bool ConditionManager::add_condition( - std::string_view identifier, value_type_t value_type, scope_type_t scope, scope_type_t scope_change, - identifier_type_t key_identifier_type, identifier_type_t value_identifier_type -) { - if (identifier.empty()) { - spdlog::error_s("Invalid condition identifier - empty!"); - return false; - } - - if (value_type == NO_TYPE || value_type > MAX_VALUE) { - spdlog::error_s( - "Condition {} has invalid value type: {}", - identifier, static_cast(value_type) - ); - return false; - } - if (scope == NO_SCOPE || scope > MAX_SCOPE) { - spdlog::error_s( - "Condition {} has invalid scope: {}", - identifier, static_cast(scope) - ); - return false; - } - - if (share_value_type(value_type, IDENTIFIER) && value_identifier_type == NO_IDENTIFIER) { - spdlog::error_s("Condition {} has no identifier type!", identifier); - return false; - } - - // don't perform the check for complex types - if (!share_value_type(value_type, COMPLEX)) { - if (!share_value_type(value_type, IDENTIFIER) && value_identifier_type != NO_IDENTIFIER) { - spdlog::warn_s("Condition {} specified an identifier type, but doesn't have an identifier!", identifier); - } - } - - return conditions.emplace_item( - identifier, - identifier, value_type, scope, scope_change, key_identifier_type, value_identifier_type - ); -} - -bool ConditionManager::setup_conditions(DefinitionManager const& definition_manager) { - bool ret = true; - - /* Special Scopes */ - ret &= add_condition("THIS", GROUP, COUNTRY, THIS); - ret &= add_condition("FROM", GROUP, COUNTRY, FROM); - ret &= add_condition("independence", GROUP, COUNTRY, COUNTRY); //only from rebels! - - /* Trigger Country Scopes */ - ret &= add_condition("all_core", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("any_core", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("any_greater_power", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("any_neighbor_country", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("any_owned_province", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("any_pop", GROUP, COUNTRY, POP); - ret &= add_condition("any_sphere_member", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("any_state", GROUP, COUNTRY, STATE); - ret &= add_condition("any_substate", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("capital_scope", GROUP, COUNTRY, PROVINCE); - ret &= add_condition("country", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("cultural_union", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("overlord", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("owner", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("sphere_owner", GROUP, COUNTRY, COUNTRY); - ret &= add_condition("war_countries", GROUP, COUNTRY, COUNTRY); - - /* Trigger State Scopes */ - ret &= add_condition("any_neighbor_province", GROUP, STATE, PROVINCE); - - /* Trigger Province Scopes */ - ret &= add_condition("controller", GROUP, PROVINCE, COUNTRY); - ret &= add_condition("state_scope", GROUP, PROVINCE, STATE); - - /* Trigger Pop Scopes */ - ret &= add_condition("location", GROUP, POP, PROVINCE); - - /* Special Conditions */ - ret &= add_condition("AND", GROUP, COUNTRY); - ret &= add_condition("OR", GROUP, COUNTRY); - ret &= add_condition("NOT", GROUP, COUNTRY); - - /* Global Conditions */ - ret &= add_condition("year", INTEGER, COUNTRY); - ret &= add_condition("month", INTEGER, COUNTRY); - ret &= add_condition("has_global_flag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, GLOBAL_FLAG); - ret &= add_condition("is_canal_enabled", INTEGER, COUNTRY); - ret &= add_condition("always", BOOLEAN, COUNTRY); - ret &= add_condition("world_wars_enabled", BOOLEAN, COUNTRY); - - /* Country Scope Conditions */ - ret &= add_condition("administration_spending", REAL, COUNTRY); - ret &= add_condition("ai", BOOLEAN, COUNTRY); - ret &= add_condition("alliance_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("average_consciousness", REAL, COUNTRY); - ret &= add_condition("average_militancy", REAL, COUNTRY); - ret &= add_condition("badboy", REAL, COUNTRY); - ret &= add_condition("big_producer", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("blockade", REAL, COUNTRY); - ret &= add_condition("brigades_compare", REAL, COUNTRY); - ret &= add_condition("can_build_factory_in_capital_state", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, BUILDING); - ret &= add_condition("can_build_fort_in_capital", COMPLEX, COUNTRY); - ret &= add_condition("can_build_railway_in_capital", COMPLEX, COUNTRY); - ret &= add_condition("can_nationalize", BOOLEAN, COUNTRY); - ret &= add_condition("can_create_vassals", BOOLEAN, COUNTRY); - ret &= add_condition("capital", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("casus_belli", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("check_variable", COMPLEX, COUNTRY, NO_SCOPE, NO_IDENTIFIER, VARIABLE); - ret &= add_condition("citizenship_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); - ret &= add_condition("civilization_progress", REAL, COUNTRY); - ret &= add_condition("civilized", BOOLEAN, COUNTRY); - ret &= add_condition("colonial_nation", BOOLEAN, COUNTRY); - ret &= add_condition("consciousness", REAL, COUNTRY); - ret &= add_condition("constructing_cb_progress", REAL, COUNTRY); - ret &= add_condition("constructing_cb_type", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CASUS_BELLI); - ret &= add_condition("continent", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CONTINENT); - ret &= add_condition("controls", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("crime_fighting", REAL, COUNTRY); - ret &= add_condition("crime_higher_than_education", BOOLEAN, COUNTRY); - ret &= add_condition("crisis_exist", BOOLEAN, COUNTRY); - ret &= add_condition("culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("culture_has_union_tag", BOOLEAN, COUNTRY); - ret &= add_condition("diplomatic_influence", COMPLEX, COUNTRY); - ret &= add_condition("economic_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); - ret &= add_condition("education_spending", REAL, COUNTRY); - ret &= add_condition("election", BOOLEAN, COUNTRY); - ret &= add_condition("exists", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("government", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, GOVERNMENT_TYPE); - ret &= add_condition("great_wars_enabled", BOOLEAN, COUNTRY); - ret &= add_condition("have_core_in", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("has_country_flag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_FLAG); - ret &= add_condition("has_country_modifier", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_EVENT_MODIFIER); - ret &= add_condition("has_cultural_sphere", BOOLEAN, COUNTRY); - ret &= add_condition("has_leader", STRING, COUNTRY); - ret &= add_condition("has_pop_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("has_pop_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); - ret &= add_condition("has_pop_type", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); - ret &= add_condition("has_recently_lost_war", BOOLEAN, COUNTRY); - ret &= add_condition("has_unclaimed_cores", BOOLEAN, COUNTRY); - ret &= add_condition("ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("industrial_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("in_sphere", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("in_default", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("invention", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, INVENTION); - ret &= add_condition("involved_in_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_claim_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_colonial_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_cultural_union", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_disarmed", BOOLEAN, COUNTRY); - ret &= add_condition("is_greater_power", BOOLEAN, COUNTRY); - ret &= add_condition("is_colonial", BOOLEAN, STATE); - ret &= add_condition("is_core", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG | PROVINCE_ID); - ret &= add_condition("is_culture_group", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG | CULTURE_GROUP); - ret &= add_condition("is_ideology_enabled", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("is_independant", BOOLEAN, COUNTRY); // paradox typo - ret &= add_condition("is_liberation_crisis", BOOLEAN, COUNTRY); - ret &= add_condition("is_mobilised", BOOLEAN, COUNTRY); - ret &= add_condition("is_next_reform", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, REFORM); - ret &= add_condition("is_our_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_possible_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_releasable_vassal", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_secondary_power", BOOLEAN, COUNTRY); - ret &= add_condition("is_sphere_leader_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_substate", BOOLEAN, COUNTRY); - ret &= add_condition("is_subject", BOOLEAN, COUNTRY); - ret &= add_condition("is_vassal", BOOLEAN, COUNTRY); - ret &= add_condition("literacy", REAL, COUNTRY); - ret &= add_condition("lost_national", REAL, COUNTRY); - ret &= add_condition("middle_strata_militancy", REAL, COUNTRY); - ret &= add_condition("middle_strata_everyday_needs", REAL, COUNTRY); - ret &= add_condition("middle_strata_life_needs", REAL, COUNTRY); - ret &= add_condition("middle_strata_luxury_needs", REAL, COUNTRY); - ret &= add_condition("middle_tax", REAL, COUNTRY); - ret &= add_condition("military_access", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("military_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("militancy", REAL, COUNTRY); - ret &= add_condition("military_spending", REAL, COUNTRY); - ret &= add_condition("money", REAL, COUNTRY); - ret &= add_condition("nationalvalue", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, NATIONAL_VALUE); - ret &= add_condition("national_provinces_occupied", REAL, COUNTRY); - ret &= add_condition("neighbour", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("num_of_allies", INTEGER, COUNTRY); - ret &= add_condition("num_of_cities", INTEGER, COUNTRY); - ret &= add_condition("num_of_ports", INTEGER, COUNTRY); - ret &= add_condition("num_of_revolts", INTEGER, COUNTRY); - ret &= add_condition("number_of_states", INTEGER, COUNTRY); - ret &= add_condition("num_of_substates", INTEGER, COUNTRY); - ret &= add_condition("num_of_vassals", INTEGER, COUNTRY); - ret &= add_condition("num_of_vassals_no_substates", INTEGER, COUNTRY); - ret &= add_condition("owns", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("part_of_sphere", BOOLEAN, COUNTRY); - ret &= add_condition("plurality", REAL, COUNTRY); - ret &= add_condition("political_movement_strength", REAL, COUNTRY); - ret &= add_condition("political_reform_want", REAL, COUNTRY); - ret &= add_condition("pop_majority_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("pop_majority_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("pop_majority_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); - ret &= add_condition("pop_militancy", REAL, COUNTRY); - ret &= add_condition("poor_strata_militancy", REAL, COUNTRY); - ret &= add_condition("poor_strata_everyday_needs", REAL, COUNTRY); - ret &= add_condition("poor_strata_life_needs", REAL, COUNTRY); - ret &= add_condition("poor_strata_luxury_needs", REAL, COUNTRY); - ret &= add_condition("poor_tax", REAL, COUNTRY); - ret &= add_condition("prestige", REAL, COUNTRY); - ret &= add_condition("primary_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("accepted_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); - ret &= add_condition("produces", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("rank", INTEGER, COUNTRY); - ret &= add_condition("rebel_power_fraction", REAL, COUNTRY); - ret &= add_condition("recruited_percentage", REAL, COUNTRY); - ret &= add_condition("relation", COMPLEX, COUNTRY); - ret &= add_condition("religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); - ret &= add_condition("religious_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); - ret &= add_condition("revanchism", REAL, COUNTRY); - ret &= add_condition("revolt_percentage", REAL, COUNTRY); - ret &= add_condition("rich_strata_militancy", REAL, COUNTRY); - ret &= add_condition("rich_strata_everyday_needs", REAL, COUNTRY); - ret &= add_condition("rich_strata_life_needs", REAL, COUNTRY); - ret &= add_condition("rich_strata_luxury_needs", REAL, COUNTRY); - ret &= add_condition("rich_tax", REAL, COUNTRY); - ret &= add_condition("rich_tax_above_poor", BOOLEAN, COUNTRY); - ret &= add_condition("ruling_party", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("ruling_party_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); - ret &= add_condition("social_movement_strength", REAL, COUNTRY); - ret &= add_condition("social_reform_want", REAL, COUNTRY); - ret &= add_condition("social_spending", REAL, COUNTRY); - ret &= add_condition("stronger_army_than", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("substate_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("tag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("tech_school", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TECH_SCHOOL); - ret &= add_condition("this_culture_union", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE_UNION); - ret &= add_condition("total_amount_of_divisions", INTEGER, COUNTRY); - ret &= add_condition("total_amount_of_ships", INTEGER, COUNTRY); - ret &= add_condition("total_defensives", INTEGER, COUNTRY); - ret &= add_condition("total_num_of_ports", INTEGER, COUNTRY); - ret &= add_condition("total_of_ours_sunk", INTEGER, COUNTRY); - ret &= add_condition("total_pops", INTEGER, COUNTRY); - ret &= add_condition("total_sea_battles", INTEGER, COUNTRY); - ret &= add_condition("total_sunk_by_us", INTEGER, COUNTRY); - ret &= add_condition("trade_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); - ret &= add_condition("treasury", REAL, COUNTRY); - ret &= add_condition("truce_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("unemployment", REAL, COUNTRY); - ret &= add_condition("unit_has_leader", BOOLEAN, COUNTRY); - ret &= add_condition("unit_in_battle", BOOLEAN, COUNTRY); - ret &= add_condition("upper_house", COMPLEX, COUNTRY); - ret &= add_condition("vassal_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("war", BOOLEAN, COUNTRY); - ret &= add_condition("war_exhaustion", REAL, COUNTRY); - ret &= add_condition("war_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); - ret &= add_condition("war_score", REAL, COUNTRY); - ret &= add_condition("war_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - - /* State Scope Conditions */ - ret &= add_condition("controlled_by", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("empty", BOOLEAN, STATE); - ret &= add_condition("flashpoint_tension", REAL, STATE); - ret &= add_condition("has_building", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, BUILDING); - ret &= add_condition("has_factories", BOOLEAN, STATE); - ret &= add_condition("has_flashpoint", BOOLEAN, STATE); - ret &= add_condition("is_slave", BOOLEAN, STATE); - ret &= add_condition("owned_by", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("trade_goods_in_state", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("work_available", COMPLEX, STATE); - - /* Province Scope Conditions */ - ret &= add_condition("can_build_factory", BOOLEAN, PROVINCE); - ret &= add_condition("controlled_by_rebels", BOOLEAN, PROVINCE); - ret &= add_condition("country_units_in_province", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("country_units_in_state", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("has_crime", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, CRIME); - ret &= add_condition("has_culture_core", BOOLEAN, PROVINCE); - ret &= add_condition("has_empty_adjacent_province", BOOLEAN, PROVINCE); - ret &= add_condition("has_empty_adjacent_state", BOOLEAN, PROVINCE); - ret &= add_condition("has_national_minority", BOOLEAN, PROVINCE); - ret &= add_condition("has_province_flag", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_FLAG); - ret &= add_condition("has_province_modifier", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_EVENT_MODIFIER); - ret &= add_condition("has_recent_imigration", INTEGER, PROVINCE); // paradox typo - ret &= add_condition("is_blockaded", BOOLEAN, PROVINCE); - ret &= add_condition("is_accepted_culture", IDENTIFIER | BOOLEAN, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_capital", BOOLEAN, PROVINCE); - ret &= add_condition("is_coastal", BOOLEAN, PROVINCE); - ret &= add_condition("is_overseas", BOOLEAN, PROVINCE); - ret &= add_condition("is_primary_culture", IDENTIFIER | BOOLEAN, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); - ret &= add_condition("is_state_capital", BOOLEAN, PROVINCE); - ret &= add_condition("is_state_religion", BOOLEAN, PROVINCE); - ret &= add_condition("life_rating", REAL, PROVINCE); - ret &= add_condition("minorities", BOOLEAN, PROVINCE); - ret &= add_condition("port", BOOLEAN, PROVINCE); - ret &= add_condition("province_control_days", INTEGER, PROVINCE); - ret &= add_condition("province_id", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("region", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, REGION); - ret &= add_condition("state_id", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); - ret &= add_condition("terrain", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, TERRAIN); - ret &= add_condition("trade_goods", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); - ret &= add_condition("unemployment_by_type", COMPLEX, PROVINCE); - ret &= add_condition("units_in_province", INTEGER, PROVINCE); - - /* Pop Scope Conditions */ - ret &= add_condition("agree_with_ruling_party", REAL, POP); - ret &= add_condition("cash_reserves", REAL, POP); - ret &= add_condition("everyday_needs", REAL, POP); - ret &= add_condition("life_needs", REAL, POP); - ret &= add_condition("luxury_needs", REAL, POP); - ret &= add_condition("political_movement", BOOLEAN, POP); - ret &= add_condition("pop_majority_issue", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); - ret &= add_condition("pop_type", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); - ret &= add_condition("social_movement", BOOLEAN, POP); - ret &= add_condition("strata", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_STRATA); - ret &= add_condition("type", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); - - const auto import_identifiers = [this, &ret]( - memory::vector const& identifiers, - value_type_t value_type, - scope_type_t scope, - scope_type_t scope_change = NO_SCOPE, - identifier_type_t key_identifier_type = NO_IDENTIFIER, - identifier_type_t value_identifier_type = NO_IDENTIFIER - ) -> void { - for (std::string_view const& identifier : identifiers) { - ret &= add_condition( - identifier, value_type, scope, scope_change, - key_identifier_type, value_identifier_type - ); - } - }; - - /* Scopes from other registries */ - import_identifiers( - definition_manager.get_country_definition_manager().get_country_definition_identifiers(), - GROUP, - COUNTRY, - COUNTRY, - COUNTRY_TAG, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_map_definition().get_region_identifiers(), - GROUP, - COUNTRY, - STATE, - REGION, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_map_definition().get_province_definition_identifiers(), - GROUP, - COUNTRY, - PROVINCE, - PROVINCE_ID, - NO_IDENTIFIER - ); - - /* Conditions from other registries */ - import_identifiers( - definition_manager.get_politics_manager().get_ideology_manager().get_ideology_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - IDEOLOGY, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_politics_manager().get_issue_manager().get_reform_group_identifiers(), - IDENTIFIER, - COUNTRY, - NO_SCOPE, - REFORM_GROUP, - REFORM - ); - - import_identifiers( - definition_manager.get_politics_manager().get_issue_manager().get_reform_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - REFORM, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_politics_manager().get_issue_manager().get_party_policy_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - PARTY_POLICY, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_pop_manager().get_pop_type_identifiers(), - REAL, - COUNTRY, - NO_SCOPE, - POP_TYPE, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_research_manager().get_technology_manager().get_technology_identifiers(), - BOOLEAN_INT, - COUNTRY, - NO_SCOPE, - TECHNOLOGY, - NO_IDENTIFIER - ); - - import_identifiers( - definition_manager.get_economy_manager().get_good_definition_manager().get_good_definition_identifiers(), - INTEGER, - COUNTRY, - NO_SCOPE, - TRADE_GOOD, - NO_IDENTIFIER - ); - - lock_conditions(); - - static constexpr std::string_view condition_root_identifier = "AND"; - root_condition = get_condition_by_identifier(condition_root_identifier); - - if (root_condition == nullptr) { - spdlog::error_s("Failed to find root condition: {}", condition_root_identifier); - ret = false; - } - - return ret; -} - -callback_t ConditionManager::expect_parse_identifier( - DefinitionManager const& definition_manager, identifier_type_t identifier_type, - callback_t callback -) const { - return [this, &definition_manager, identifier_type, callback](std::string_view identifier) mutable -> bool { - HasIdentifier const* identified = nullptr; - - #define EXPECT_CALL(type, name, manager, ...) \ - if (share_identifier_type(identifier_type, type)) { \ - identified = manager.get_##name##_by_identifier(identifier); \ - if (identified != nullptr) { \ - return callback(identified); \ - } __VA_OPT__(else { \ - /* TODO: the set is just a placeholder for actual logic */ \ - static const case_insensitive_string_set_t chances { __VA_ARGS__ }; \ - if (chances.contains(identifier)) { \ - return true; \ - } \ - }) \ - } - - //TODO: placeholder for not implemented stuff - #define EXPECT_CALL_PLACEHOLDER(type) \ - if (share_identifier_type(identifier_type, type)) { return true; } - - EXPECT_CALL_PLACEHOLDER(VARIABLE); - EXPECT_CALL_PLACEHOLDER(GLOBAL_FLAG); - EXPECT_CALL_PLACEHOLDER(COUNTRY_FLAG); - EXPECT_CALL_PLACEHOLDER(PROVINCE_FLAG); - EXPECT_CALL( - COUNTRY_TAG, country_definition, definition_manager.get_country_definition_manager(), "THIS", "FROM", "OWNER" - ); - EXPECT_CALL(PROVINCE_ID, province_definition, definition_manager.get_map_definition(), "THIS", "FROM"); - EXPECT_CALL(REGION, region, definition_manager.get_map_definition()); - EXPECT_CALL(IDEOLOGY, ideology, definition_manager.get_politics_manager().get_ideology_manager()); - EXPECT_CALL(REFORM_GROUP, reform_group, definition_manager.get_politics_manager().get_issue_manager()); - EXPECT_CALL(REFORM, reform, definition_manager.get_politics_manager().get_issue_manager()); - EXPECT_CALL(PARTY_POLICY, party_policy, definition_manager.get_politics_manager().get_issue_manager()); - EXPECT_CALL(POP_TYPE, pop_type, definition_manager.get_pop_manager()); - EXPECT_CALL(POP_STRATA, strata, definition_manager.get_pop_manager()); - EXPECT_CALL(TECHNOLOGY, technology, definition_manager.get_research_manager().get_technology_manager()); - EXPECT_CALL(INVENTION, invention, definition_manager.get_research_manager().get_invention_manager()); - EXPECT_CALL(TECH_SCHOOL, technology_school, definition_manager.get_research_manager().get_technology_manager()); - EXPECT_CALL(CULTURE, culture, definition_manager.get_pop_manager().get_culture_manager(), "THIS", "FROM"); - EXPECT_CALL(CULTURE_GROUP, culture_group, definition_manager.get_pop_manager().get_culture_manager()); - EXPECT_CALL(RELIGION, religion, definition_manager.get_pop_manager().get_religion_manager(), "THIS", "FROM"); - EXPECT_CALL(TRADE_GOOD, good_definition, definition_manager.get_economy_manager().get_good_definition_manager()); - EXPECT_CALL(BUILDING, building_type, definition_manager.get_economy_manager().get_building_type_manager(), "FACTORY"); - EXPECT_CALL(CASUS_BELLI, wargoal_type, definition_manager.get_military_manager().get_wargoal_type_manager()); - EXPECT_CALL(GOVERNMENT_TYPE, government_type, definition_manager.get_politics_manager().get_government_type_manager()); - EXPECT_CALL(COUNTRY_EVENT_MODIFIER | PROVINCE_EVENT_MODIFIER, event_modifier, definition_manager.get_modifier_manager()); - EXPECT_CALL(COUNTRY_EVENT_MODIFIER, triggered_modifier, definition_manager.get_modifier_manager()); - EXPECT_CALL(NATIONAL_VALUE, national_value, definition_manager.get_politics_manager().get_national_value_manager()); - EXPECT_CALL( - CULTURE_UNION, country_definition, definition_manager.get_country_definition_manager(), "THIS", "FROM", "THIS_UNION" - ); - EXPECT_CALL(CONTINENT, continent, definition_manager.get_map_definition()); - EXPECT_CALL(CRIME, crime_modifier, definition_manager.get_crime_manager()); - EXPECT_CALL(TERRAIN, terrain_type, definition_manager.get_map_definition().get_terrain_type_manager()); - - #undef EXPECT_CALL - #undef EXPECT_CALL_PLACEHOLDER - - return false; - }; -} - -node_callback_t ConditionManager::expect_condition_node( - DefinitionManager const& definition_manager, Condition const& condition, scope_type_t current_scope, - scope_type_t this_scope, scope_type_t from_scope, callback_t callback -) const { - return [this, &definition_manager, &condition, callback, current_scope, this_scope, from_scope]( - ast::NodeCPtr node - ) mutable -> bool { - bool ret = false; - ConditionNode::value_t value; - - const std::string_view identifier = condition.get_identifier(); - const value_type_t value_type = condition.value_type; - const scope_type_t scope = condition.scope; - const scope_type_t scope_change = condition.scope_change; - const identifier_type_t key_identifier_type = condition.key_identifier_type; - const identifier_type_t value_identifier_type = condition.value_identifier_type; - - HasIdentifier const* value_item = nullptr; - - const auto get_identifiable = [this, &definition_manager]( - identifier_type_t item_type, std::string_view id, bool log - ) -> HasIdentifier const* { - HasIdentifier const* keyval = nullptr; - bool ret = expect_parse_identifier( - definition_manager, - item_type, - assign_variable_callback(keyval) - )(id); - if (log && !ret) { - spdlog::error_s( - "Invalid identifier {} expected to have type {} found during condition node parsing!", - id, fmt::underlying(item_type) - ); - } - return keyval; - }; - - if (!ret && share_value_type(value_type, IDENTIFIER)) { - std::string_view value_identifier {}; - ret |= expect_identifier_or_string(assign_variable_callback(value_identifier))(node); - if (ret) { - value = ConditionNode::string_t { value_identifier }; - value_item = get_identifiable( - value_identifier_type, - value_identifier, - value_type == IDENTIFIER // don't log if there's a fallback - ); - ret |= value_item != nullptr; - } - } - - if (!ret && share_value_type(value_type, STRING)) { - std::string_view value_identifier {}; - bool local_ret = expect_identifier_or_string( - assign_variable_callback(value_identifier) - )(node); - ret |= local_ret; - if (local_ret) { - value = ConditionNode::string_t { value_identifier }; - } - } - - ret |= (!ret && share_value_type(value_type, BOOLEAN)) - && expect_bool(assign_variable_callback(value))(node); - ret |= (!ret && share_value_type(value_type, BOOLEAN_INT)) - && expect_int_bool(assign_variable_callback(value))(node); - ret |= (!ret && share_value_type(value_type, INTEGER)) - && expect_uint64(assign_variable_callback(value))(node); - ret |= (!ret && share_value_type(value_type, REAL)) - && expect_fixed_point(assign_variable_callback(value))(node); - - //entries with magic syntax, thanks paradox! - if (!ret && share_value_type(value_type, COMPLEX)) { - const auto expect_pair = [&ret, &value, node]( - std::string_view identifier_key, std::string_view value_key - ) -> void { - std::string_view pair_identifier {}; - fixed_point_t pair_value = 0; - ret |= expect_dictionary_keys( - identifier_key, ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(pair_identifier)), - value_key, ONE_EXACTLY, expect_fixed_point(assign_variable_callback(pair_value)) - )(node); - if (ret) { - value = ConditionNode::identifier_real_t { pair_identifier, pair_value }; - } - }; - - if (identifier == "can_build_railway_in_capital" || identifier == "can_build_fort_in_capital") { - bool in_whole_capital_state = false, limit_to_world_greatest_level = false; - ret |= expect_dictionary_keys( - "in_whole_capital_state", ONE_EXACTLY, expect_bool(assign_variable_callback(in_whole_capital_state)), - "limit_to_world_greatest_level", ONE_EXACTLY, - expect_bool(assign_variable_callback(limit_to_world_greatest_level)) - )(node); - if (ret) { - value = ConditionNode::double_boolean_t { in_whole_capital_state, limit_to_world_greatest_level }; - } - } else if (identifier == "check_variable") { - expect_pair("which", "value"); // { which = [name of variable] value = x } - } else if (identifier == "diplomatic_influence") { - expect_pair("who", "value"); // { who = [THIS/FROM/TAG] value = x } - } else if (identifier == "relation") { - expect_pair("who", "value"); // { who = [tag/this/from] value = x } - } else if (identifier == "unemployment_by_type") { - expect_pair("type", "value"); // { type = [poptype] value = x } - } else if (identifier == "upper_house") { - expect_pair("ideology", "value"); // { ideology = name value = 0.x } - } else if (identifier == "work_available") { - // { worker = [type] } - std::string_view worker {}; - ret |= expect_dictionary_keys( - "worker", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(worker)) - )(node); - if (ret) { - value = ConditionNode::string_t { worker }; - } - } else { - spdlog::error_s("Attempted to parse unknown complex condition {}!", identifier); - } - - #undef EXPECT_PAIR - } - - if (!ret && share_value_type(value_type, GROUP)) { - ConditionNode::condition_list_t node_list; - ret |= expect_condition_node_list( - definition_manager, - scope_change == NO_SCOPE ? current_scope : scope_change, - this_scope, - from_scope, - vector_callback(node_list) - )(node); - value = std::move(node_list); - } - - // scope validation - scope_type_t effective_current_scope = current_scope; - if (share_scope_type(effective_current_scope, THIS)) { - effective_current_scope = this_scope; - } else if (share_scope_type(effective_current_scope, FROM)) { - effective_current_scope = from_scope; - } - - if (!share_scope_type(scope, effective_current_scope) && effective_current_scope > scope) { - spdlog::warn_s( - "Condition or scope {} was found in wrong scope {}, expected {}!", - identifier, fmt::underlying(effective_current_scope), fmt::underlying(scope) - ); - ret = false; - } - - // key parsing - HasIdentifier const* key_item = nullptr; - if (condition.key_identifier_type != NO_IDENTIFIER) { - key_item = get_identifiable(key_identifier_type, identifier, true); - ret &= key_item != nullptr; - } - - if (!ret) { - spdlog::warn_s("Could not parse condition node {}, will always evaluate to false!", identifier); - Condition const* always_condition = conditions.get_item_by_identifier("always"); - return callback({ - always_condition, - std::move(ConditionNode::value_t(false)), - true, - key_item, - value_item - }); - } - - ret &= callback({ - &condition, - std::move(value), - ret, - key_item, - value_item - }); - - return ret; - }; -} - -/* Default callback for top condition scope. */ -static bool top_scope_fallback(std::string_view id, ast::NodeCPtr node) { - /* This is a non-condition key, and so not case-insensitive. */ - if (id == "factor") { - return true; - } else { - spdlog::error_s("Unknown node \"{}\" found while parsing conditions!", id); - return false; - } -}; - -node_callback_t ConditionManager::expect_condition_node_list( - DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, scope_type_t from_scope, - callback_t callback, bool top_scope -) const { - return [this, &definition_manager, callback, current_scope, this_scope, from_scope, top_scope](ast::NodeCPtr node) -> bool { - const auto expect_node = [this, &definition_manager, callback, current_scope, this_scope, from_scope] - (Condition const& condition, ast::NodeCPtr node) -> bool { - return expect_condition_node( - definition_manager, condition, current_scope, this_scope, from_scope, callback - )(node); - }; - const auto invalid_condition_node = [this, &expect_node, top_scope](std::string_view id, ast::NodeCPtr node) -> bool { - if (top_scope && id == "factor") { return true; } - if (ast::FlatValue const* node_name = dryad::node_try_cast(node); node_name && node_name->value()) { - spdlog::warn_s( - "Condition {} does not exist in scope at condition node: {}, and will always evaluate to false!", - id, node_name->value().view() - ); // TODO: make this error message more useful by pinning down node to an actual file or something - } else if (ast::ListValue const* list_values = dryad::node_try_cast(node); list_values) { - for (ast::Statement const* statement : list_values->statements()) { - dryad::visit_node(statement, - [&](ast::AssignStatement const* assign){ - if (ast::FlatValue const* node_name = dryad::node_try_cast(assign->left()); node_name && node_name->value()) { - spdlog::warn_s( - "Condition {} does not exist in scope at condition node: {}, and will always evaluate to false!", - id, node_name->value().view() - ); // TODO: make this error message more useful by pinning down node to an actual file or something - } - }, - [&](ast::ValueStatement const* value) { - if (ast::FlatValue const* node_name = dryad::node_try_cast(value->value()); node_name && node_name->value()) { - spdlog::warn_s( - "Condition {} does not exist in scope at condition node: {}, and will always evaluate to false!", - id, node_name->value().view() - ); // TODO: make this error message more useful by pinning down node to an actual file or something - } - } - ); - } - } - return true; - }; - - return conditions.expect_item_dictionary_and_default( - invalid_condition_node, - expect_node - )(node); - }; -} - -bool ConditionManager::expect_condition_script( - DefinitionManager const& definition_manager, scope_type_t initial_scope, scope_type_t this_scope, - scope_type_t from_scope, NodeTools::callback_t callback, std::span nodes -) const { - ConditionNode::condition_list_t conds; - bool ret = true; - for (const ast::NodeCPtr node : nodes) { - ret &= expect_condition_node_list( - definition_manager, - initial_scope, - this_scope, - from_scope, - NodeTools::vector_callback(conds), - true - )(node); - } - - ret &= callback({ root_condition, std::move(conds), true }); - - return ret; -} diff --git a/src/openvic-simulation/scripts/Condition.hpp b/src/openvic-simulation/scripts/Condition.hpp index 1411d05ca..d759097a6 100644 --- a/src/openvic-simulation/scripts/Condition.hpp +++ b/src/openvic-simulation/scripts/Condition.hpp @@ -1,21 +1,16 @@ #pragma once #include -#include #include -#include #include "openvic-simulation/core/memory/String.hpp" #include "openvic-simulation/core/memory/Vector.hpp" #include "openvic-simulation/core/template/EnumBitfield.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/HasIdentifier.hpp" -#include "openvic-simulation/types/IdentifierRegistry.hpp" +#include "openvic-simulation/utility/Getters.hpp" namespace OpenVic { - struct ConditionManager; - struct ConditionScript; - struct DefinitionManager; - enum class value_type_t : uint8_t { NO_TYPE = 0, IDENTIFIER = 1 << 0, @@ -244,40 +239,4 @@ namespace OpenVic { HasIdentifier const* new_condition_value_item = nullptr ); }; - - struct ConditionManager { - private: - CaseInsensitiveIdentifierRegistry IDENTIFIER_REGISTRY(condition); - Condition const* root_condition = nullptr; - - bool add_condition( - std::string_view identifier, value_type_t value_type, scope_type_t scope, - scope_type_t scope_change = scope_type_t::NO_SCOPE, - identifier_type_t key_identifier_type = identifier_type_t::NO_IDENTIFIER, - identifier_type_t value_identifier_type = identifier_type_t::NO_IDENTIFIER - ); - - NodeTools::callback_t expect_parse_identifier( - DefinitionManager const& definition_manager, identifier_type_t identifier_type, - NodeTools::callback_t callback - ) const; - - NodeTools::node_callback_t expect_condition_node( - DefinitionManager const& definition_manager, Condition const& condition, scope_type_t current_scope, - scope_type_t this_scope, scope_type_t from_scope, NodeTools::callback_t callback - ) const; - - NodeTools::node_callback_t expect_condition_node_list( - DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, - scope_type_t from_scope, NodeTools::callback_t callback, bool top_scope = false - ) const; - - public: - bool setup_conditions(DefinitionManager const& definition_manager); - - bool expect_condition_script( - DefinitionManager const& definition_manager, scope_type_t initial_scope, scope_type_t this_scope, - scope_type_t from_scope, NodeTools::callback_t callback, std::span nodes - ) const; - }; } diff --git a/src/openvic-simulation/scripts/ConditionManager.cpp b/src/openvic-simulation/scripts/ConditionManager.cpp new file mode 100644 index 000000000..de61819df --- /dev/null +++ b/src/openvic-simulation/scripts/ConditionManager.cpp @@ -0,0 +1,783 @@ +#include "ConditionManager.hpp" + +#include + +#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/DefinitionManager.hpp" + +using namespace OpenVic; +using namespace OpenVic::NodeTools; + +using enum value_type_t; +using enum scope_type_t; +using enum identifier_type_t; + +bool ConditionManager::add_condition( + std::string_view identifier, value_type_t value_type, scope_type_t scope, scope_type_t scope_change, + identifier_type_t key_identifier_type, identifier_type_t value_identifier_type +) { + if (identifier.empty()) { + spdlog::error_s("Invalid condition identifier - empty!"); + return false; + } + + if (value_type == NO_TYPE || value_type > MAX_VALUE) { + spdlog::error_s( + "Condition {} has invalid value type: {}", + identifier, static_cast(value_type) + ); + return false; + } + if (scope == NO_SCOPE || scope > MAX_SCOPE) { + spdlog::error_s( + "Condition {} has invalid scope: {}", + identifier, static_cast(scope) + ); + return false; + } + + if (share_value_type(value_type, IDENTIFIER) && value_identifier_type == NO_IDENTIFIER) { + spdlog::error_s("Condition {} has no identifier type!", identifier); + return false; + } + + // don't perform the check for complex types + if (!share_value_type(value_type, COMPLEX)) { + if (!share_value_type(value_type, IDENTIFIER) && value_identifier_type != NO_IDENTIFIER) { + spdlog::warn_s("Condition {} specified an identifier type, but doesn't have an identifier!", identifier); + } + } + + return conditions.emplace_item( + identifier, + identifier, value_type, scope, scope_change, key_identifier_type, value_identifier_type + ); +} + +bool ConditionManager::setup_conditions(DefinitionManager const& definition_manager) { + bool ret = true; + + /* Special Scopes */ + ret &= add_condition("THIS", GROUP, COUNTRY, THIS); + ret &= add_condition("FROM", GROUP, COUNTRY, FROM); + ret &= add_condition("independence", GROUP, COUNTRY, COUNTRY); //only from rebels! + + /* Trigger Country Scopes */ + ret &= add_condition("all_core", GROUP, COUNTRY, PROVINCE); + ret &= add_condition("any_core", GROUP, COUNTRY, PROVINCE); + ret &= add_condition("any_greater_power", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("any_neighbor_country", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("any_owned_province", GROUP, COUNTRY, PROVINCE); + ret &= add_condition("any_pop", GROUP, COUNTRY, POP); + ret &= add_condition("any_sphere_member", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("any_state", GROUP, COUNTRY, STATE); + ret &= add_condition("any_substate", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("capital_scope", GROUP, COUNTRY, PROVINCE); + ret &= add_condition("country", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("cultural_union", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("overlord", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("owner", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("sphere_owner", GROUP, COUNTRY, COUNTRY); + ret &= add_condition("war_countries", GROUP, COUNTRY, COUNTRY); + + /* Trigger State Scopes */ + ret &= add_condition("any_neighbor_province", GROUP, STATE, PROVINCE); + + /* Trigger Province Scopes */ + ret &= add_condition("controller", GROUP, PROVINCE, COUNTRY); + ret &= add_condition("state_scope", GROUP, PROVINCE, STATE); + + /* Trigger Pop Scopes */ + ret &= add_condition("location", GROUP, POP, PROVINCE); + + /* Special Conditions */ + ret &= add_condition("AND", GROUP, COUNTRY); + ret &= add_condition("OR", GROUP, COUNTRY); + ret &= add_condition("NOT", GROUP, COUNTRY); + + /* Global Conditions */ + ret &= add_condition("year", INTEGER, COUNTRY); + ret &= add_condition("month", INTEGER, COUNTRY); + ret &= add_condition("has_global_flag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, GLOBAL_FLAG); + ret &= add_condition("is_canal_enabled", INTEGER, COUNTRY); + ret &= add_condition("always", BOOLEAN, COUNTRY); + ret &= add_condition("world_wars_enabled", BOOLEAN, COUNTRY); + + /* Country Scope Conditions */ + ret &= add_condition("administration_spending", REAL, COUNTRY); + ret &= add_condition("ai", BOOLEAN, COUNTRY); + ret &= add_condition("alliance_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("average_consciousness", REAL, COUNTRY); + ret &= add_condition("average_militancy", REAL, COUNTRY); + ret &= add_condition("badboy", REAL, COUNTRY); + ret &= add_condition("big_producer", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); + ret &= add_condition("blockade", REAL, COUNTRY); + ret &= add_condition("brigades_compare", REAL, COUNTRY); + ret &= add_condition("can_build_factory_in_capital_state", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, BUILDING); + ret &= add_condition("can_build_fort_in_capital", COMPLEX, COUNTRY); + ret &= add_condition("can_build_railway_in_capital", COMPLEX, COUNTRY); + ret &= add_condition("can_nationalize", BOOLEAN, COUNTRY); + ret &= add_condition("can_create_vassals", BOOLEAN, COUNTRY); + ret &= add_condition("capital", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); + ret &= add_condition("casus_belli", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("check_variable", COMPLEX, COUNTRY, NO_SCOPE, NO_IDENTIFIER, VARIABLE); + ret &= add_condition("citizenship_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); + ret &= add_condition("civilization_progress", REAL, COUNTRY); + ret &= add_condition("civilized", BOOLEAN, COUNTRY); + ret &= add_condition("colonial_nation", BOOLEAN, COUNTRY); + ret &= add_condition("consciousness", REAL, COUNTRY); + ret &= add_condition("constructing_cb_progress", REAL, COUNTRY); + ret &= add_condition("constructing_cb_type", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CASUS_BELLI); + ret &= add_condition("continent", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CONTINENT); + ret &= add_condition("controls", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); + ret &= add_condition("crime_fighting", REAL, COUNTRY); + ret &= add_condition("crime_higher_than_education", BOOLEAN, COUNTRY); + ret &= add_condition("crisis_exist", BOOLEAN, COUNTRY); + ret &= add_condition("culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); + ret &= add_condition("culture_has_union_tag", BOOLEAN, COUNTRY); + ret &= add_condition("diplomatic_influence", COMPLEX, COUNTRY); + ret &= add_condition("economic_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); + ret &= add_condition("education_spending", REAL, COUNTRY); + ret &= add_condition("election", BOOLEAN, COUNTRY); + ret &= add_condition("exists", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("government", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, GOVERNMENT_TYPE); + ret &= add_condition("great_wars_enabled", BOOLEAN, COUNTRY); + ret &= add_condition("have_core_in", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("has_country_flag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_FLAG); + ret &= add_condition("has_country_modifier", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_EVENT_MODIFIER); + ret &= add_condition("has_cultural_sphere", BOOLEAN, COUNTRY); + ret &= add_condition("has_leader", STRING, COUNTRY); + ret &= add_condition("has_pop_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); + ret &= add_condition("has_pop_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); + ret &= add_condition("has_pop_type", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); + ret &= add_condition("has_recently_lost_war", BOOLEAN, COUNTRY); + ret &= add_condition("has_unclaimed_cores", BOOLEAN, COUNTRY); + ret &= add_condition("ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); + ret &= add_condition("industrial_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("in_sphere", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("in_default", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("invention", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, INVENTION); + ret &= add_condition("involved_in_crisis", BOOLEAN, COUNTRY); + ret &= add_condition("is_claim_crisis", BOOLEAN, COUNTRY); + ret &= add_condition("is_colonial_crisis", BOOLEAN, COUNTRY); + ret &= add_condition("is_cultural_union", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("is_disarmed", BOOLEAN, COUNTRY); + ret &= add_condition("is_greater_power", BOOLEAN, COUNTRY); + ret &= add_condition("is_colonial", BOOLEAN, STATE); + ret &= add_condition("is_core", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG | PROVINCE_ID); + ret &= add_condition("is_culture_group", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG | CULTURE_GROUP); + ret &= add_condition("is_ideology_enabled", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); + ret &= add_condition("is_independant", BOOLEAN, COUNTRY); // paradox typo + ret &= add_condition("is_liberation_crisis", BOOLEAN, COUNTRY); + ret &= add_condition("is_mobilised", BOOLEAN, COUNTRY); + ret &= add_condition("is_next_reform", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, REFORM); + ret &= add_condition("is_our_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("is_possible_vassal", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("is_releasable_vassal", IDENTIFIER | BOOLEAN, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("is_secondary_power", BOOLEAN, COUNTRY); + ret &= add_condition("is_sphere_leader_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("is_substate", BOOLEAN, COUNTRY); + ret &= add_condition("is_subject", BOOLEAN, COUNTRY); + ret &= add_condition("is_vassal", BOOLEAN, COUNTRY); + ret &= add_condition("literacy", REAL, COUNTRY); + ret &= add_condition("lost_national", REAL, COUNTRY); + ret &= add_condition("middle_strata_militancy", REAL, COUNTRY); + ret &= add_condition("middle_strata_everyday_needs", REAL, COUNTRY); + ret &= add_condition("middle_strata_life_needs", REAL, COUNTRY); + ret &= add_condition("middle_strata_luxury_needs", REAL, COUNTRY); + ret &= add_condition("middle_tax", REAL, COUNTRY); + ret &= add_condition("military_access", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("military_score", REAL | IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("militancy", REAL, COUNTRY); + ret &= add_condition("military_spending", REAL, COUNTRY); + ret &= add_condition("money", REAL, COUNTRY); + ret &= add_condition("nationalvalue", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, NATIONAL_VALUE); + ret &= add_condition("national_provinces_occupied", REAL, COUNTRY); + ret &= add_condition("neighbour", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("num_of_allies", INTEGER, COUNTRY); + ret &= add_condition("num_of_cities", INTEGER, COUNTRY); + ret &= add_condition("num_of_ports", INTEGER, COUNTRY); + ret &= add_condition("num_of_revolts", INTEGER, COUNTRY); + ret &= add_condition("number_of_states", INTEGER, COUNTRY); + ret &= add_condition("num_of_substates", INTEGER, COUNTRY); + ret &= add_condition("num_of_vassals", INTEGER, COUNTRY); + ret &= add_condition("num_of_vassals_no_substates", INTEGER, COUNTRY); + ret &= add_condition("owns", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); + ret &= add_condition("part_of_sphere", BOOLEAN, COUNTRY); + ret &= add_condition("plurality", REAL, COUNTRY); + ret &= add_condition("political_movement_strength", REAL, COUNTRY); + ret &= add_condition("political_reform_want", REAL, COUNTRY); + ret &= add_condition("pop_majority_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); + ret &= add_condition("pop_majority_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); + ret &= add_condition("pop_majority_religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); + ret &= add_condition("pop_militancy", REAL, COUNTRY); + ret &= add_condition("poor_strata_militancy", REAL, COUNTRY); + ret &= add_condition("poor_strata_everyday_needs", REAL, COUNTRY); + ret &= add_condition("poor_strata_life_needs", REAL, COUNTRY); + ret &= add_condition("poor_strata_luxury_needs", REAL, COUNTRY); + ret &= add_condition("poor_tax", REAL, COUNTRY); + ret &= add_condition("prestige", REAL, COUNTRY); + ret &= add_condition("primary_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); + ret &= add_condition("accepted_culture", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE); + ret &= add_condition("produces", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); + ret &= add_condition("rank", INTEGER, COUNTRY); + ret &= add_condition("rebel_power_fraction", REAL, COUNTRY); + ret &= add_condition("recruited_percentage", REAL, COUNTRY); + ret &= add_condition("relation", COMPLEX, COUNTRY); + ret &= add_condition("religion", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, RELIGION); + ret &= add_condition("religious_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); + ret &= add_condition("revanchism", REAL, COUNTRY); + ret &= add_condition("revolt_percentage", REAL, COUNTRY); + ret &= add_condition("rich_strata_militancy", REAL, COUNTRY); + ret &= add_condition("rich_strata_everyday_needs", REAL, COUNTRY); + ret &= add_condition("rich_strata_life_needs", REAL, COUNTRY); + ret &= add_condition("rich_strata_luxury_needs", REAL, COUNTRY); + ret &= add_condition("rich_tax", REAL, COUNTRY); + ret &= add_condition("rich_tax_above_poor", BOOLEAN, COUNTRY); + ret &= add_condition("ruling_party", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("ruling_party_ideology", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, IDEOLOGY); + ret &= add_condition("social_movement_strength", REAL, COUNTRY); + ret &= add_condition("social_reform_want", REAL, COUNTRY); + ret &= add_condition("social_spending", REAL, COUNTRY); + ret &= add_condition("stronger_army_than", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("substate_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("tag", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("tech_school", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, TECH_SCHOOL); + ret &= add_condition("this_culture_union", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, CULTURE_UNION); + ret &= add_condition("total_amount_of_divisions", INTEGER, COUNTRY); + ret &= add_condition("total_amount_of_ships", INTEGER, COUNTRY); + ret &= add_condition("total_defensives", INTEGER, COUNTRY); + ret &= add_condition("total_num_of_ports", INTEGER, COUNTRY); + ret &= add_condition("total_of_ours_sunk", INTEGER, COUNTRY); + ret &= add_condition("total_pops", INTEGER, COUNTRY); + ret &= add_condition("total_sea_battles", INTEGER, COUNTRY); + ret &= add_condition("total_sunk_by_us", INTEGER, COUNTRY); + ret &= add_condition("trade_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); + ret &= add_condition("treasury", REAL, COUNTRY); + ret &= add_condition("truce_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("unemployment", REAL, COUNTRY); + ret &= add_condition("unit_has_leader", BOOLEAN, COUNTRY); + ret &= add_condition("unit_in_battle", BOOLEAN, COUNTRY); + ret &= add_condition("upper_house", COMPLEX, COUNTRY); + ret &= add_condition("vassal_of", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("war", BOOLEAN, COUNTRY); + ret &= add_condition("war_exhaustion", REAL, COUNTRY); + ret &= add_condition("war_policy", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); + ret &= add_condition("war_score", REAL, COUNTRY); + ret &= add_condition("war_with", IDENTIFIER, COUNTRY, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + + /* State Scope Conditions */ + ret &= add_condition("controlled_by", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("empty", BOOLEAN, STATE); + ret &= add_condition("flashpoint_tension", REAL, STATE); + ret &= add_condition("has_building", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, BUILDING); + ret &= add_condition("has_factories", BOOLEAN, STATE); + ret &= add_condition("has_flashpoint", BOOLEAN, STATE); + ret &= add_condition("is_slave", BOOLEAN, STATE); + ret &= add_condition("owned_by", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("trade_goods_in_state", IDENTIFIER, STATE, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); + ret &= add_condition("work_available", COMPLEX, STATE); + + /* Province Scope Conditions */ + ret &= add_condition("can_build_factory", BOOLEAN, PROVINCE); + ret &= add_condition("controlled_by_rebels", BOOLEAN, PROVINCE); + ret &= add_condition("country_units_in_province", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("country_units_in_state", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("has_crime", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, CRIME); + ret &= add_condition("has_culture_core", BOOLEAN, PROVINCE); + ret &= add_condition("has_empty_adjacent_province", BOOLEAN, PROVINCE); + ret &= add_condition("has_empty_adjacent_state", BOOLEAN, PROVINCE); + ret &= add_condition("has_national_minority", BOOLEAN, PROVINCE); + ret &= add_condition("has_province_flag", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_FLAG); + ret &= add_condition("has_province_modifier", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_EVENT_MODIFIER); + ret &= add_condition("has_recent_imigration", INTEGER, PROVINCE); // paradox typo + ret &= add_condition("is_blockaded", BOOLEAN, PROVINCE); + ret &= add_condition("is_accepted_culture", IDENTIFIER | BOOLEAN, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("is_capital", BOOLEAN, PROVINCE); + ret &= add_condition("is_coastal", BOOLEAN, PROVINCE); + ret &= add_condition("is_overseas", BOOLEAN, PROVINCE); + ret &= add_condition("is_primary_culture", IDENTIFIER | BOOLEAN, PROVINCE, NO_SCOPE, NO_IDENTIFIER, COUNTRY_TAG); + ret &= add_condition("is_state_capital", BOOLEAN, PROVINCE); + ret &= add_condition("is_state_religion", BOOLEAN, PROVINCE); + ret &= add_condition("life_rating", REAL, PROVINCE); + ret &= add_condition("minorities", BOOLEAN, PROVINCE); + ret &= add_condition("port", BOOLEAN, PROVINCE); + ret &= add_condition("province_control_days", INTEGER, PROVINCE); + ret &= add_condition("province_id", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); + ret &= add_condition("region", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, REGION); + ret &= add_condition("state_id", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, PROVINCE_ID); + ret &= add_condition("terrain", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, TERRAIN); + ret &= add_condition("trade_goods", IDENTIFIER, PROVINCE, NO_SCOPE, NO_IDENTIFIER, TRADE_GOOD); + ret &= add_condition("unemployment_by_type", COMPLEX, PROVINCE); + ret &= add_condition("units_in_province", INTEGER, PROVINCE); + + /* Pop Scope Conditions */ + ret &= add_condition("agree_with_ruling_party", REAL, POP); + ret &= add_condition("cash_reserves", REAL, POP); + ret &= add_condition("everyday_needs", REAL, POP); + ret &= add_condition("life_needs", REAL, POP); + ret &= add_condition("luxury_needs", REAL, POP); + ret &= add_condition("political_movement", BOOLEAN, POP); + ret &= add_condition("pop_majority_issue", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, PARTY_POLICY); + ret &= add_condition("pop_type", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); + ret &= add_condition("social_movement", BOOLEAN, POP); + ret &= add_condition("strata", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_STRATA); + ret &= add_condition("type", IDENTIFIER, POP, NO_SCOPE, NO_IDENTIFIER, POP_TYPE); + + const auto import_identifiers = [this, &ret]( + memory::vector const& identifiers, + value_type_t value_type, + scope_type_t scope, + scope_type_t scope_change = NO_SCOPE, + identifier_type_t key_identifier_type = NO_IDENTIFIER, + identifier_type_t value_identifier_type = NO_IDENTIFIER + ) -> void { + for (std::string_view const& identifier : identifiers) { + ret &= add_condition( + identifier, value_type, scope, scope_change, + key_identifier_type, value_identifier_type + ); + } + }; + + /* Scopes from other registries */ + import_identifiers( + definition_manager.get_country_definition_manager().get_country_definition_identifiers(), + GROUP, + COUNTRY, + COUNTRY, + COUNTRY_TAG, + NO_IDENTIFIER + ); + + import_identifiers( + definition_manager.get_map_definition().get_region_identifiers(), + GROUP, + COUNTRY, + STATE, + REGION, + NO_IDENTIFIER + ); + + import_identifiers( + definition_manager.get_map_definition().get_province_definition_identifiers(), + GROUP, + COUNTRY, + PROVINCE, + PROVINCE_ID, + NO_IDENTIFIER + ); + + /* Conditions from other registries */ + import_identifiers( + definition_manager.get_politics_manager().get_ideology_manager().get_ideology_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + IDEOLOGY, + NO_IDENTIFIER + ); + + import_identifiers( + definition_manager.get_politics_manager().get_issue_manager().get_reform_group_identifiers(), + IDENTIFIER, + COUNTRY, + NO_SCOPE, + REFORM_GROUP, + REFORM + ); + + import_identifiers( + definition_manager.get_politics_manager().get_issue_manager().get_reform_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + REFORM, + NO_IDENTIFIER + ); + + import_identifiers( + definition_manager.get_politics_manager().get_issue_manager().get_party_policy_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + PARTY_POLICY, + NO_IDENTIFIER + ); + + import_identifiers( + definition_manager.get_pop_manager().get_pop_type_identifiers(), + REAL, + COUNTRY, + NO_SCOPE, + POP_TYPE, + NO_IDENTIFIER + ); + + import_identifiers( + definition_manager.get_research_manager().get_technology_manager().get_technology_identifiers(), + BOOLEAN_INT, + COUNTRY, + NO_SCOPE, + TECHNOLOGY, + NO_IDENTIFIER + ); + + import_identifiers( + definition_manager.get_economy_manager().get_good_definition_manager().get_good_definition_identifiers(), + INTEGER, + COUNTRY, + NO_SCOPE, + TRADE_GOOD, + NO_IDENTIFIER + ); + + lock_conditions(); + + static constexpr std::string_view condition_root_identifier = "AND"; + root_condition = get_condition_by_identifier(condition_root_identifier); + + if (root_condition == nullptr) { + spdlog::error_s("Failed to find root condition: {}", condition_root_identifier); + ret = false; + } + + return ret; +} + +NodeTools::callback_t ConditionManager::expect_parse_identifier( + DefinitionManager const& definition_manager, identifier_type_t identifier_type, + NodeTools::callback_t callback +) const { + return [this, &definition_manager, identifier_type, callback](std::string_view identifier) mutable -> bool { + HasIdentifier const* identified = nullptr; + + #define EXPECT_CALL(type, name, manager, ...) \ + if (share_identifier_type(identifier_type, type)) { \ + identified = manager.get_##name##_by_identifier(identifier); \ + if (identified != nullptr) { \ + return callback(identified); \ + } __VA_OPT__(else { \ + /* TODO: the set is just a placeholder for actual logic */ \ + static const case_insensitive_string_set_t chances { __VA_ARGS__ }; \ + if (chances.contains(identifier)) { \ + return true; \ + } \ + }) \ + } + + //TODO: placeholder for not implemented stuff + #define EXPECT_CALL_PLACEHOLDER(type) \ + if (share_identifier_type(identifier_type, type)) { return true; } + + EXPECT_CALL_PLACEHOLDER(VARIABLE); + EXPECT_CALL_PLACEHOLDER(GLOBAL_FLAG); + EXPECT_CALL_PLACEHOLDER(COUNTRY_FLAG); + EXPECT_CALL_PLACEHOLDER(PROVINCE_FLAG); + EXPECT_CALL( + COUNTRY_TAG, country_definition, definition_manager.get_country_definition_manager(), "THIS", "FROM", "OWNER" + ); + EXPECT_CALL(PROVINCE_ID, province_definition, definition_manager.get_map_definition(), "THIS", "FROM"); + EXPECT_CALL(REGION, region, definition_manager.get_map_definition()); + EXPECT_CALL(IDEOLOGY, ideology, definition_manager.get_politics_manager().get_ideology_manager()); + EXPECT_CALL(REFORM_GROUP, reform_group, definition_manager.get_politics_manager().get_issue_manager()); + EXPECT_CALL(REFORM, reform, definition_manager.get_politics_manager().get_issue_manager()); + EXPECT_CALL(PARTY_POLICY, party_policy, definition_manager.get_politics_manager().get_issue_manager()); + EXPECT_CALL(POP_TYPE, pop_type, definition_manager.get_pop_manager()); + EXPECT_CALL(POP_STRATA, strata, definition_manager.get_pop_manager()); + EXPECT_CALL(TECHNOLOGY, technology, definition_manager.get_research_manager().get_technology_manager()); + EXPECT_CALL(INVENTION, invention, definition_manager.get_research_manager().get_invention_manager()); + EXPECT_CALL(TECH_SCHOOL, technology_school, definition_manager.get_research_manager().get_technology_manager()); + EXPECT_CALL(CULTURE, culture, definition_manager.get_pop_manager().get_culture_manager(), "THIS", "FROM"); + EXPECT_CALL(CULTURE_GROUP, culture_group, definition_manager.get_pop_manager().get_culture_manager()); + EXPECT_CALL(RELIGION, religion, definition_manager.get_pop_manager().get_religion_manager(), "THIS", "FROM"); + EXPECT_CALL(TRADE_GOOD, good_definition, definition_manager.get_economy_manager().get_good_definition_manager()); + EXPECT_CALL(BUILDING, building_type, definition_manager.get_economy_manager().get_building_type_manager(), "FACTORY"); + EXPECT_CALL(CASUS_BELLI, wargoal_type, definition_manager.get_military_manager().get_wargoal_type_manager()); + EXPECT_CALL(GOVERNMENT_TYPE, government_type, definition_manager.get_politics_manager().get_government_type_manager()); + EXPECT_CALL(COUNTRY_EVENT_MODIFIER | PROVINCE_EVENT_MODIFIER, event_modifier, definition_manager.get_modifier_manager()); + EXPECT_CALL(COUNTRY_EVENT_MODIFIER, triggered_modifier, definition_manager.get_modifier_manager()); + EXPECT_CALL(NATIONAL_VALUE, national_value, definition_manager.get_politics_manager().get_national_value_manager()); + EXPECT_CALL( + CULTURE_UNION, country_definition, definition_manager.get_country_definition_manager(), "THIS", "FROM", "THIS_UNION" + ); + EXPECT_CALL(CONTINENT, continent, definition_manager.get_map_definition()); + EXPECT_CALL(CRIME, crime_modifier, definition_manager.get_crime_manager()); + EXPECT_CALL(TERRAIN, terrain_type, definition_manager.get_map_definition().get_terrain_type_manager()); + + #undef EXPECT_CALL + #undef EXPECT_CALL_PLACEHOLDER + + return false; + }; +} + +NodeTools::node_callback_t ConditionManager::expect_condition_node( + DefinitionManager const& definition_manager, Condition const& condition, scope_type_t current_scope, + scope_type_t this_scope, scope_type_t from_scope, NodeTools::callback_t callback +) const { + return [this, &definition_manager, &condition, callback, current_scope, this_scope, from_scope]( + ovdl::v2script::ast::Node const* node + ) mutable -> bool { + bool ret = false; + ConditionNode::value_t value; + + const std::string_view identifier = condition.get_identifier(); + const value_type_t value_type = condition.value_type; + const scope_type_t scope = condition.scope; + const scope_type_t scope_change = condition.scope_change; + const identifier_type_t key_identifier_type = condition.key_identifier_type; + const identifier_type_t value_identifier_type = condition.value_identifier_type; + + HasIdentifier const* value_item = nullptr; + + const auto get_identifiable = [this, &definition_manager]( + identifier_type_t item_type, std::string_view id, bool log + ) -> HasIdentifier const* { + HasIdentifier const* keyval = nullptr; + bool ret = expect_parse_identifier( + definition_manager, + item_type, + assign_variable_callback(keyval) + )(id); + if (log && !ret) { + spdlog::error_s( + "Invalid identifier {} expected to have type {} found during condition node parsing!", + id, fmt::underlying(item_type) + ); + } + return keyval; + }; + + if (!ret && share_value_type(value_type, IDENTIFIER)) { + std::string_view value_identifier {}; + ret |= expect_identifier_or_string(assign_variable_callback(value_identifier))(node); + if (ret) { + value = ConditionNode::string_t { value_identifier }; + value_item = get_identifiable( + value_identifier_type, + value_identifier, + value_type == IDENTIFIER // don't log if there's a fallback + ); + ret |= value_item != nullptr; + } + } + + if (!ret && share_value_type(value_type, STRING)) { + std::string_view value_identifier {}; + bool local_ret = expect_identifier_or_string( + assign_variable_callback(value_identifier) + )(node); + ret |= local_ret; + if (local_ret) { + value = ConditionNode::string_t { value_identifier }; + } + } + + ret |= (!ret && share_value_type(value_type, BOOLEAN)) + && expect_bool(assign_variable_callback(value))(node); + ret |= (!ret && share_value_type(value_type, BOOLEAN_INT)) + && expect_int_bool(assign_variable_callback(value))(node); + ret |= (!ret && share_value_type(value_type, INTEGER)) + && expect_uint64(assign_variable_callback(value))(node); + ret |= (!ret && share_value_type(value_type, REAL)) + && expect_fixed_point(assign_variable_callback(value))(node); + + //entries with magic syntax, thanks paradox! + if (!ret && share_value_type(value_type, COMPLEX)) { + const auto expect_pair = [&ret, &value, node]( + std::string_view identifier_key, std::string_view value_key + ) -> void { + std::string_view pair_identifier {}; + fixed_point_t pair_value = 0; + ret |= expect_dictionary_keys( + identifier_key, ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(pair_identifier)), + value_key, ONE_EXACTLY, expect_fixed_point(assign_variable_callback(pair_value)) + )(node); + if (ret) { + value = ConditionNode::identifier_real_t { pair_identifier, pair_value }; + } + }; + + if (identifier == "can_build_railway_in_capital" || identifier == "can_build_fort_in_capital") { + bool in_whole_capital_state = false, limit_to_world_greatest_level = false; + ret |= expect_dictionary_keys( + "in_whole_capital_state", ONE_EXACTLY, expect_bool(assign_variable_callback(in_whole_capital_state)), + "limit_to_world_greatest_level", ONE_EXACTLY, + expect_bool(assign_variable_callback(limit_to_world_greatest_level)) + )(node); + if (ret) { + value = ConditionNode::double_boolean_t { in_whole_capital_state, limit_to_world_greatest_level }; + } + } else if (identifier == "check_variable") { + expect_pair("which", "value"); // { which = [name of variable] value = x } + } else if (identifier == "diplomatic_influence") { + expect_pair("who", "value"); // { who = [THIS/FROM/TAG] value = x } + } else if (identifier == "relation") { + expect_pair("who", "value"); // { who = [tag/this/from] value = x } + } else if (identifier == "unemployment_by_type") { + expect_pair("type", "value"); // { type = [poptype] value = x } + } else if (identifier == "upper_house") { + expect_pair("ideology", "value"); // { ideology = name value = 0.x } + } else if (identifier == "work_available") { + // { worker = [type] } + std::string_view worker {}; + ret |= expect_dictionary_keys( + "worker", ONE_EXACTLY, expect_identifier_or_string(assign_variable_callback(worker)) + )(node); + if (ret) { + value = ConditionNode::string_t { worker }; + } + } else { + spdlog::error_s("Attempted to parse unknown complex condition {}!", identifier); + } + + #undef EXPECT_PAIR + } + + if (!ret && share_value_type(value_type, GROUP)) { + ConditionNode::condition_list_t node_list; + ret |= expect_condition_node_list( + definition_manager, + scope_change == NO_SCOPE ? current_scope : scope_change, + this_scope, + from_scope, + vector_callback(node_list) + )(node); + value = std::move(node_list); + } + + // scope validation + scope_type_t effective_current_scope = current_scope; + if (share_scope_type(effective_current_scope, THIS)) { + effective_current_scope = this_scope; + } else if (share_scope_type(effective_current_scope, FROM)) { + effective_current_scope = from_scope; + } + + if (!share_scope_type(scope, effective_current_scope) && effective_current_scope > scope) { + spdlog::warn_s( + "Condition or scope {} was found in wrong scope {}, expected {}!", + identifier, fmt::underlying(effective_current_scope), fmt::underlying(scope) + ); + ret = false; + } + + // key parsing + HasIdentifier const* key_item = nullptr; + if (condition.key_identifier_type != NO_IDENTIFIER) { + key_item = get_identifiable(key_identifier_type, identifier, true); + ret &= key_item != nullptr; + } + + if (!ret) { + spdlog::warn_s("Could not parse condition node {}, will always evaluate to false!", identifier); + Condition const* always_condition = conditions.get_item_by_identifier("always"); + return callback({ + always_condition, + std::move(ConditionNode::value_t(false)), + true, + key_item, + value_item + }); + } + + ret &= callback({ + &condition, + std::move(value), + ret, + key_item, + value_item + }); + + return ret; + }; +} + +/* Default callback for top condition scope. */ +static bool top_scope_fallback(std::string_view id, ovdl::v2script::ast::Node const* node) { + /* This is a non-condition key, and so not case-insensitive. */ + if (id == "factor") { + return true; + } else { + spdlog::error_s("Unknown node \"{}\" found while parsing conditions!", id); + return false; + } +}; + +NodeTools::node_callback_t ConditionManager::expect_condition_node_list( + DefinitionManager const& definition_manager, + scope_type_t current_scope, + scope_type_t this_scope, + scope_type_t from_scope, + NodeTools::callback_t callback, bool top_scope +) const { + return [this, &definition_manager, callback, current_scope, this_scope, from_scope, top_scope](ovdl::v2script::ast::Node const* node) -> bool { + const auto expect_node = [this, &definition_manager, callback, current_scope, this_scope, from_scope] + (Condition const& condition, ovdl::v2script::ast::Node const* node) -> bool { + return expect_condition_node( + definition_manager, condition, current_scope, this_scope, from_scope, callback + )(node); + }; + const auto invalid_condition_node = [this, &expect_node, top_scope](std::string_view id, ovdl::v2script::ast::Node const* node) -> bool { + if (top_scope && id == "factor") { return true; } + if (ovdl::v2script::ast::FlatValue const* node_name = dryad::node_try_cast(node); node_name && node_name->value()) { + spdlog::warn_s( + "Condition {} does not exist in scope at condition node: {}, and will always evaluate to false!", + id, node_name->value().view() + ); // TODO: make this error message more useful by pinning down node to an actual file or something + } else if (ovdl::v2script::ast::ListValue const* list_values = dryad::node_try_cast(node); list_values) { + for (ovdl::v2script::ast::Statement const* statement : list_values->statements()) { + dryad::visit_node(statement, + [&](ovdl::v2script::ast::AssignStatement const* assign){ + if (ovdl::v2script::ast::FlatValue const* node_name = dryad::node_try_cast(assign->left()); node_name && node_name->value()) { + spdlog::warn_s( + "Condition {} does not exist in scope at condition node: {}, and will always evaluate to false!", + id, node_name->value().view() + ); // TODO: make this error message more useful by pinning down node to an actual file or something + } + }, + [&](ovdl::v2script::ast::ValueStatement const* value) { + if (ovdl::v2script::ast::FlatValue const* node_name = dryad::node_try_cast(value->value()); node_name && node_name->value()) { + spdlog::warn_s( + "Condition {} does not exist in scope at condition node: {}, and will always evaluate to false!", + id, node_name->value().view() + ); // TODO: make this error message more useful by pinning down node to an actual file or something + } + } + ); + } + } + return true; + }; + + return conditions.expect_item_dictionary_and_default( + invalid_condition_node, + expect_node + )(node); + }; +} + +bool ConditionManager::expect_condition_script( + DefinitionManager const& definition_manager, scope_type_t initial_scope, scope_type_t this_scope, + scope_type_t from_scope, + NodeTools::callback_t callback, + std::span nodes +) const { + ConditionNode::condition_list_t conds; + bool ret = true; + for (ovdl::v2script::ast::Node const* const node : nodes) { + ret &= expect_condition_node_list( + definition_manager, + initial_scope, + this_scope, + from_scope, + NodeTools::vector_callback(conds), + true + )(node); + } + + ret &= callback({ root_condition, std::move(conds), true }); + + return ret; +} diff --git a/src/openvic-simulation/scripts/ConditionManager.hpp b/src/openvic-simulation/scripts/ConditionManager.hpp new file mode 100644 index 000000000..d654ef5f8 --- /dev/null +++ b/src/openvic-simulation/scripts/ConditionManager.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +#include "openvic-simulation/dataloader/NodeCallbacks.hpp" +#include "openvic-simulation/scripts/Condition.hpp" +#include "openvic-simulation/types/HasIdentifier.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct ConditionManager; + struct ConditionScript; + struct DefinitionManager; + + struct ConditionManager { + private: + CaseInsensitiveIdentifierRegistry IDENTIFIER_REGISTRY(condition); + Condition const* root_condition = nullptr; + + bool add_condition( + std::string_view identifier, value_type_t value_type, scope_type_t scope, + scope_type_t scope_change = scope_type_t::NO_SCOPE, + identifier_type_t key_identifier_type = identifier_type_t::NO_IDENTIFIER, + identifier_type_t value_identifier_type = identifier_type_t::NO_IDENTIFIER + ); + + NodeTools::callback_t expect_parse_identifier( + DefinitionManager const& definition_manager, identifier_type_t identifier_type, + NodeTools::callback_t callback + ) const; + + NodeTools::node_callback_t expect_condition_node( + DefinitionManager const& definition_manager, Condition const& condition, scope_type_t current_scope, + scope_type_t this_scope, scope_type_t from_scope, NodeTools::callback_t callback + ) const; + + NodeTools::node_callback_t expect_condition_node_list( + DefinitionManager const& definition_manager, scope_type_t current_scope, scope_type_t this_scope, + scope_type_t from_scope, NodeTools::callback_t callback, bool top_scope = false + ) const; + + public: + bool setup_conditions(DefinitionManager const& definition_manager); + + bool expect_condition_script( + DefinitionManager const& definition_manager, scope_type_t initial_scope, scope_type_t this_scope, + scope_type_t from_scope, NodeTools::callback_t callback, std::span nodes + ) const; + }; +} diff --git a/src/openvic-simulation/scripts/ConditionScript.cpp b/src/openvic-simulation/scripts/ConditionScript.cpp index 4fc97d83c..5e025063b 100644 --- a/src/openvic-simulation/scripts/ConditionScript.cpp +++ b/src/openvic-simulation/scripts/ConditionScript.cpp @@ -1,5 +1,6 @@ #include "ConditionScript.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/DefinitionManager.hpp" using namespace OpenVic; @@ -9,7 +10,7 @@ ConditionScript::ConditionScript( scope_type_t new_initial_scope, scope_type_t new_this_scope, scope_type_t new_from_scope ) : initial_scope { new_initial_scope }, this_scope { new_this_scope }, from_scope { new_from_scope } {} -bool ConditionScript::_parse_script(std::span nodes, DefinitionManager const& definition_manager) { +bool ConditionScript::_parse_script(std::span nodes, DefinitionManager const& definition_manager) { return definition_manager.get_script_manager().get_condition_manager().expect_condition_script( definition_manager, initial_scope, diff --git a/src/openvic-simulation/scripts/ConditionScript.hpp b/src/openvic-simulation/scripts/ConditionScript.hpp index fdc74a460..782aaef8f 100644 --- a/src/openvic-simulation/scripts/ConditionScript.hpp +++ b/src/openvic-simulation/scripts/ConditionScript.hpp @@ -15,7 +15,7 @@ namespace OpenVic { scope_type_t PROPERTY(from_scope); protected: - bool _parse_script(std::span nodes, DefinitionManager const& definition_manager) override; + bool _parse_script(std::span nodes, DefinitionManager const& definition_manager) override; public: ConditionScript(scope_type_t new_initial_scope, scope_type_t new_this_scope, scope_type_t new_from_scope); diff --git a/src/openvic-simulation/scripts/ConditionalWeight.cpp b/src/openvic-simulation/scripts/ConditionalWeight.cpp index c5a72da35..7a8be121c 100644 --- a/src/openvic-simulation/scripts/ConditionalWeight.cpp +++ b/src/openvic-simulation/scripts/ConditionalWeight.cpp @@ -1,5 +1,7 @@ #include "ConditionalWeight.hpp" +#include "openvic-simulation/dataloader/NodeTools.hpp" + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -17,7 +19,7 @@ template static NodeCallback auto expect_modifier( memory::vector& items, scope_type_t initial_scope, scope_type_t this_scope, scope_type_t from_scope ) { - return [&items, initial_scope, this_scope, from_scope](ast::NodeCPtr node) -> bool { + return [&items, initial_scope, this_scope, from_scope](ovdl::v2script::ast::Node const* node) -> bool { fixed_point_t weight = 0; bool successful = false; bool ret = expect_key("factor", expect_fixed_point(assign_variable_callback(weight)), &successful)(node); @@ -33,7 +35,7 @@ static NodeCallback auto expect_modifier( } template -node_callback_t ConditionalWeight::expect_conditional_weight() { +NodeTools::node_callback_t ConditionalWeight::expect_conditional_weight() { key_map_t key_map; bool successfully_set_up_base_keys = true; @@ -75,7 +77,7 @@ node_callback_t ConditionalWeight::expect_conditional_weight() { } if (!successfully_set_up_base_keys) { - return [](ast::NodeCPtr node) -> bool { + return [](ovdl::v2script::ast::Node const* node) -> bool { spdlog::error_s( "Failed to set up base keys for ConditionalWeight with base value: {}", static_cast(TYPE) ); @@ -86,7 +88,7 @@ node_callback_t ConditionalWeight::expect_conditional_weight() { return expect_dictionary_key_map( std::move(key_map), "modifier", ZERO_OR_MORE, expect_modifier(condition_weight_items, initial_scope, this_scope, from_scope), - "group", ZERO_OR_MORE, [this](ast::NodeCPtr node) -> bool { + "group", ZERO_OR_MORE, [this](ovdl::v2script::ast::Node const* node) -> bool { condition_weight_group_t items; const bool ret = expect_dictionary_keys( @@ -130,9 +132,9 @@ bool ConditionalWeight::parse_scripts(DefinitionManager const& definition_ template bool ConditionalWeight::operator==(ConditionalWeight const& other) const { - return initial_scope == other.initial_scope && - this_scope == other.this_scope && - from_scope == other.from_scope; + return initial_scope == other.initial_scope + && this_scope == other.this_scope + && from_scope == other.from_scope; } template struct OpenVic::ConditionalWeight; diff --git a/src/openvic-simulation/scripts/ConditionalWeight.hpp b/src/openvic-simulation/scripts/ConditionalWeight.hpp index fe45d7d0d..626fd99fe 100644 --- a/src/openvic-simulation/scripts/ConditionalWeight.hpp +++ b/src/openvic-simulation/scripts/ConditionalWeight.hpp @@ -3,7 +3,7 @@ #include #include "openvic-simulation/core/memory/Vector.hpp" -#include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/dataloader/Node_forwarded.hpp" #include "openvic-simulation/scripts/ConditionScript.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" diff --git a/src/openvic-simulation/scripts/EffectScript.cpp b/src/openvic-simulation/scripts/EffectScript.cpp index dd5a70949..ce0f4ef43 100644 --- a/src/openvic-simulation/scripts/EffectScript.cpp +++ b/src/openvic-simulation/scripts/EffectScript.cpp @@ -2,7 +2,7 @@ using namespace OpenVic; -bool EffectScript::_parse_script(std::span nodes, DefinitionManager const& definition_manager) { +bool EffectScript::_parse_script(std::span nodes, DefinitionManager const& definition_manager) { // TODO - parse effect script return true; } diff --git a/src/openvic-simulation/scripts/EffectScript.hpp b/src/openvic-simulation/scripts/EffectScript.hpp index c7adc5ac6..b4a655e16 100644 --- a/src/openvic-simulation/scripts/EffectScript.hpp +++ b/src/openvic-simulation/scripts/EffectScript.hpp @@ -7,6 +7,6 @@ namespace OpenVic { struct EffectScript final : Script { protected: - bool _parse_script(std::span nodes, DefinitionManager const& definition_manager) override; + bool _parse_script(std::span nodes, DefinitionManager const& definition_manager) override; }; } diff --git a/src/openvic-simulation/scripts/Script.hpp b/src/openvic-simulation/scripts/Script.hpp index 4178104d5..e2920ccba 100644 --- a/src/openvic-simulation/scripts/Script.hpp +++ b/src/openvic-simulation/scripts/Script.hpp @@ -1,16 +1,18 @@ #pragma once +#include "openvic-simulation/core/memory/Vector.hpp" #include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/utility/Logger.hpp" namespace OpenVic { // TODO - is template needed if context is always DefinitionManager const&? template struct Script { private: - memory::vector _root_nodes; + memory::vector _root_nodes; protected: - virtual bool _parse_script(std::span nodes, _Context... context) = 0; + virtual bool _parse_script(std::span nodes, _Context... context) = 0; public: constexpr Script() {}; diff --git a/src/openvic-simulation/scripts/ScriptManager.hpp b/src/openvic-simulation/scripts/ScriptManager.hpp index 3c1565755..734e4a37c 100644 --- a/src/openvic-simulation/scripts/ScriptManager.hpp +++ b/src/openvic-simulation/scripts/ScriptManager.hpp @@ -1,6 +1,6 @@ #pragma once -#include "openvic-simulation/scripts/Condition.hpp" +#include "openvic-simulation/scripts/ConditionManager.hpp" namespace OpenVic { struct ScriptManager { diff --git a/src/openvic-simulation/types/Colour.hpp b/src/openvic-simulation/types/Colour.hpp index beb4a224c..d77b3d4a2 100644 --- a/src/openvic-simulation/types/Colour.hpp +++ b/src/openvic-simulation/types/Colour.hpp @@ -362,8 +362,9 @@ namespace OpenVic { template requires( - _ColourTraits::has_alpha && std::same_as && - std::same_as + _ColourTraits::has_alpha + && std::same_as + && std::same_as ) OV_SPEED_INLINE explicit constexpr basic_colour_t(basic_colour_t const& colour) requires(colour_traits::has_alpha) @@ -371,8 +372,9 @@ namespace OpenVic { template requires( - !_ColourTraits::has_alpha && std::same_as && - std::same_as + !_ColourTraits::has_alpha + && std::same_as + && std::same_as ) OV_SPEED_INLINE explicit constexpr basic_colour_t( basic_colour_t const& colour, value_type a = max_value @@ -382,8 +384,8 @@ namespace OpenVic { template requires( - std::same_as && - std::same_as + std::same_as + && std::same_as ) OV_SPEED_INLINE explicit constexpr basic_colour_t(basic_colour_t const& colour) requires(!colour_traits::has_alpha) @@ -588,8 +590,10 @@ namespace OpenVic { constexpr double GREEN_MULTIPLIER = 0.7151522; constexpr double BLUE_MULTIPLIER = 0.0721750; - double luminance = std::pow(red / max_value, POWER) * RED_MULTIPLIER + - std::pow(green / max_value, POWER) * GREEN_MULTIPLIER + std::pow(blue, POWER) * BLUE_MULTIPLIER; + double luminance = std::pow(red / max_value, POWER) * RED_MULTIPLIER + + std::pow(green / max_value, POWER) + * GREEN_MULTIPLIER + std::pow(blue, POWER) + * BLUE_MULTIPLIER; return luminance >= LUMINANCE_FLIP ? basic_colour_t::from_floats(0, 0, 0) : basic_colour_t::from_floats(1, 1, 1); } diff --git a/src/openvic-simulation/types/CowVector.hpp b/src/openvic-simulation/types/CowVector.hpp index 38b9a62fd..94480ae48 100644 --- a/src/openvic-simulation/types/CowVector.hpp +++ b/src/openvic-simulation/types/CowVector.hpp @@ -97,8 +97,11 @@ namespace OpenVic { swap(other, *this); } else if (!other.empty()) { _data = _allocate_payload(other.size()); - _data->array_end = - uninitialized_move(other._data->array, other._data->array_end, _data->array, alloc); + _data->array_end = uninitialized_move( + other._data->array, + other._data->array_end, + _data->array, alloc + ); destroy(_data->array, _data->array_end, alloc); _data->array_end = _data->array; } @@ -128,8 +131,9 @@ namespace OpenVic { cow_vector tmp = std::move(x); writer& tmp_writer = *reinterpret_cast(&tmp); - if constexpr (allocator_traits::propagate_on_container_move_assignment::value || - allocator_traits::is_always_equal::value) { + if constexpr (allocator_traits::propagate_on_container_move_assignment::value + || allocator_traits::is_always_equal::value + ) { self_writer.swap(tmp_writer); } else if (alloc == x.alloc) { self_writer.swap(tmp_writer); @@ -449,8 +453,12 @@ namespace OpenVic { if constexpr (move_insertable_allocator) { _relocate(_data->array, _data->array_end, new_data->array, alloc); } else { - new_data->array_end = - uninitialized_move(_data->array, _data->array_end, new_data->array, alloc); + new_data->array_end = uninitialized_move( + _data->array, + _data->array_end, + new_data->array, + alloc + ); destroy(_data->array, _data->array_end, alloc); } _deallocate_payload(_data); @@ -1102,8 +1110,11 @@ namespace OpenVic { }; template - inline constexpr cow_vector::size_type cow_vector::payload::content_size = - std::max(1ul, (sizeof(payload) - sizeof(array)) / sizeof(T)); + inline constexpr cow_vector::size_type cow_vector::payload::content_size + = std::max( + 1ul, + (sizeof(payload) - sizeof(array)) / sizeof(T) + ); template [[nodiscard]] inline bool operator==(cow_vector const& x, cow_vector const& y) { diff --git a/src/openvic-simulation/types/Date.hpp b/src/openvic-simulation/types/Date.hpp index 93f4ef818..15ab0c2ff 100644 --- a/src/openvic-simulation/types/Date.hpp +++ b/src/openvic-simulation/types/Date.hpp @@ -114,8 +114,9 @@ namespace OpenVic { using month_t = uint8_t; using day_t = uint8_t; - static constexpr std::array DAYS_IN_MONTH = - std::to_array({ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }); + static constexpr std::array DAYS_IN_MONTH = std::to_array( + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + ); static constexpr Timespan::day_t MONTHS_IN_YEAR = DAYS_IN_MONTH.size(); static constexpr Timespan::day_t MAX_DAYS_IN_MONTH = *ranges::max_element(DAYS_IN_MONTH); @@ -348,9 +349,11 @@ namespace OpenVic { OV_SPEED_INLINE constexpr stack_string to_array(bool pad_year = false, bool pad_month = true, bool pad_day = true) const; struct stack_string final : StackString< - fmt::detail::count_digits(uint64_t(std::numeric_limits::max())) + - fmt::detail::count_digits(uint64_t(MONTHS_IN_YEAR)) + - fmt::detail::count_digits(uint64_t(MAX_DAYS_IN_MONTH)) + 4> { + fmt::detail::count_digits(uint64_t(std::numeric_limits::max())) + + fmt::detail::count_digits(uint64_t(MONTHS_IN_YEAR)) + + fmt::detail::count_digits(uint64_t(MAX_DAYS_IN_MONTH)) + + 4 + > { protected: using StackString::StackString; friend OV_SPEED_INLINE constexpr stack_string Date::to_array(bool pad_year, bool pad_month, bool pad_day) const; @@ -512,8 +515,9 @@ namespace OpenVic { Date date = from_string(str, from_chars); OV_ERR_FAIL_COND_V_MSG( - from_chars->ec == std::errc::invalid_argument && from_chars->type == errc_type::year && - from_chars->ptr == from_chars->type_first, + from_chars->ec == std::errc::invalid_argument + && from_chars->type == errc_type::year + && from_chars->ptr == from_chars->type_first, date, "Could not parse year value." ); OV_ERR_FAIL_COND_V_MSG( @@ -525,13 +529,15 @@ namespace OpenVic { "Only year value could be found." ); OV_ERR_FAIL_COND_V_MSG( - from_chars->ec == std::errc::invalid_argument && from_chars->type == errc_type::year && - from_chars->ptr != from_chars->type_first, + from_chars->ec == std::errc::invalid_argument + && from_chars->type == errc_type::year + && from_chars->ptr != from_chars->type_first, date, memory::fmt::format("Year value was missing a separator (\"{}\").", SEPARATOR_CHARACTER) ); OV_ERR_FAIL_COND_V_MSG( - from_chars->ec == std::errc::invalid_argument && from_chars->type == errc_type::month && - from_chars->ptr == from_chars->type_first, + from_chars->ec == std::errc::invalid_argument + && from_chars->type == errc_type::month + && from_chars->ptr == from_chars->type_first, date, "Could not parse month value." ); OV_ERR_FAIL_COND_V_MSG( @@ -539,8 +545,9 @@ namespace OpenVic { "Month value cannot be 0." ); OV_ERR_FAIL_COND_V_MSG( - from_chars->ec == std::errc::value_too_large && from_chars->type == errc_type::month && - from_chars->ptr == from_chars->type_first, + from_chars->ec == std::errc::value_too_large + && from_chars->type == errc_type::month + && from_chars->ptr == from_chars->type_first, date, memory::fmt::format("Month value cannot be larger than {}.", MONTHS_IN_YEAR) ); OV_ERR_FAIL_COND_V_MSG( @@ -548,21 +555,24 @@ namespace OpenVic { "Only year and month value could be found." ); OV_ERR_FAIL_COND_V_MSG( - from_chars->ec == std::errc::invalid_argument && from_chars->type == errc_type::month && - from_chars->ptr != from_chars->type_first, + from_chars->ec == std::errc::invalid_argument + && from_chars->type == errc_type::month + && from_chars->ptr != from_chars->type_first, date, memory::fmt::format("Month value was missing a separator (\"{}\").", SEPARATOR_CHARACTER) ); OV_ERR_FAIL_COND_V_MSG( - from_chars->ec == std::errc::invalid_argument && from_chars->type == errc_type::day && - from_chars->ptr == from_chars->type_first, + from_chars->ec == std::errc::invalid_argument + && from_chars->type == errc_type::day + && from_chars->ptr == from_chars->type_first, date, "Could not parse day value." ); OV_ERR_FAIL_COND_V_MSG( from_chars->ec == std::errc::not_supported && from_chars->type == errc_type::day, date, "Day value cannot be 0." ); OV_ERR_FAIL_COND_V_MSG( - from_chars->ec == std::errc::value_too_large && from_chars->type == errc_type::day && - from_chars->ptr == from_chars->type_first, + from_chars->ec == std::errc::value_too_large + && from_chars->type == errc_type::day + && from_chars->ptr == from_chars->type_first, date, memory::fmt::format("Day value cannot be larger than {} for {}.", DAYS_IN_MONTH[date.get_month() - 1], date.get_month()) ); @@ -601,8 +611,13 @@ namespace OpenVic { OV_SPEED_INLINE constexpr Date::stack_string Date::to_array(bool pad_year, bool pad_month, bool pad_day) const { stack_string str {}; - std::to_chars_result result = - to_chars(str._array.data(), str._array.data() + str._array.size(), pad_year, pad_month, pad_day); + std::to_chars_result result = to_chars( + str._array.data(), + str._array.data() + str._array.size(), + pad_year, + pad_month, + pad_day + ); str._string_size = result.ptr - str.data(); return str; } diff --git a/src/openvic-simulation/types/IdentifierRegistry.hpp b/src/openvic-simulation/types/IdentifierRegistry.hpp index 811fc6ecb..0f9ccba8a 100644 --- a/src/openvic-simulation/types/IdentifierRegistry.hpp +++ b/src/openvic-simulation/types/IdentifierRegistry.hpp @@ -3,6 +3,7 @@ #include #include "openvic-simulation/core/memory/SmartPtr.hpp" +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" #include "openvic-simulation/core/template/Concepts.hpp" @@ -113,9 +114,8 @@ namespace OpenVic { /* Registry Storage Info - how items are stored and indexed, and item-index conversion functions. */ template typename StorageInfo, typename Item> - concept RegistryStorageInfo = - std::same_as::storage_type::value_type, Item> && - requires( + concept RegistryStorageInfo = std::same_as::storage_type::value_type, Item> + && requires( typename StorageInfo::storage_type& items, typename StorageInfo::storage_type const& const_items, typename StorageInfo::index_type index ) { @@ -161,8 +161,8 @@ namespace OpenVic { string_map_case Case = StringMapCaseSensitive /* Identifier map parameters */ > requires( - RegistryItemInfo<_ItemInfo, typename ValueInfo::internal_value_type> && - RegistryStorageInfo<_StorageInfo, typename _ItemInfo::item_type> + RegistryItemInfo<_ItemInfo, typename ValueInfo::internal_value_type> + && RegistryStorageInfo<_StorageInfo, typename _ItemInfo::item_type> ) class UniqueKeyRegistry { public: @@ -209,7 +209,7 @@ namespace OpenVic { constexpr bool emplace_via_move( item_type&& item, - NodeTools::Callback auto duplicate_callback + Callback auto duplicate_callback ) { if (locked) { spdlog::error_s("Cannot add item to the {} registry - locked!", name); @@ -229,7 +229,7 @@ namespace OpenVic { template constexpr bool emplace_item( const std::string_view new_identifier, - NodeTools::Callback auto duplicate_callback, + Callback auto duplicate_callback, Args&&... args ) { if (locked) { @@ -319,7 +319,7 @@ namespace OpenVic { } static constexpr NodeTools::KeyValueCallback auto key_value_invalid_callback(std::string_view name) { - return [name](std::string_view key, ast::NodeCPtr) { + return [name](std::string_view key, ovdl::v2script::ast::Node const*) { spdlog::error_s("Invalid {}: {}", name, key); return false; }; @@ -366,8 +366,8 @@ namespace OpenVic { } \ return std::addressof(ValueInfo::get_external_value(ItemInfo::get_value(items[index]))); \ } \ - constexpr NodeTools::Callback auto expect_item_str( \ - NodeTools::Callback auto callback, bool allow_empty, bool warn = false \ + constexpr Callback auto expect_item_str( \ + Callback auto callback, bool allow_empty, bool warn = false \ ) CONST { \ return [this, callback, allow_empty, warn](std::string_view identifier) -> bool { \ if (identifier.empty()) { \ @@ -393,26 +393,26 @@ namespace OpenVic { }; \ } \ constexpr NodeTools::NodeCallback auto expect_item_identifier( \ - NodeTools::Callback auto callback, bool warn = false \ + Callback auto callback, bool warn = false \ ) CONST { \ return NodeTools::expect_identifier(expect_item_str(callback, false, warn)); \ } \ constexpr NodeTools::NodeCallback auto expect_item_string( \ - NodeTools::Callback auto callback, bool allow_empty, bool warn = false \ + Callback auto callback, bool allow_empty, bool warn = false \ ) CONST { \ return NodeTools::expect_string(expect_item_str(callback, allow_empty, warn), allow_empty); \ } \ constexpr NodeTools::NodeCallback auto expect_item_identifier_or_string( \ - NodeTools::Callback auto callback, bool allow_empty, bool warn = false \ + Callback auto callback, bool allow_empty, bool warn = false \ ) CONST { \ return NodeTools::expect_identifier_or_string(expect_item_str(callback, allow_empty, warn), allow_empty); \ } \ constexpr NodeTools::NodeCallback auto expect_item_assign_and_default( \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return NodeTools::expect_assign( \ - [this, default_callback, callback](std::string_view key, ast::NodeCPtr value) -> bool { \ + [this, default_callback, callback](std::string_view key, ovdl::v2script::ast::Node const* value) -> bool { \ external_value_type CONST* item = get_item_by_identifier(key); \ if (item != nullptr) { \ return callback(*item, value); \ @@ -423,14 +423,14 @@ namespace OpenVic { ); \ } \ constexpr NodeTools::NodeCallback auto expect_item_assign( \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return expect_item_assign_and_default(key_value_invalid_callback(name), callback); \ } \ constexpr NodeTools::NodeCallback auto expect_item_dictionary_and_length_and_default( \ NodeTools::LengthCallback auto length_callback, \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return NodeTools::expect_list_and_length( \ length_callback, expect_item_assign_and_default(default_callback, callback) \ @@ -438,7 +438,7 @@ namespace OpenVic { } \ constexpr NodeTools::NodeCallback auto expect_item_dictionary_and_length( \ NodeTools::LengthCallback auto length_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return expect_item_dictionary_and_length_and_default( \ length_callback, \ @@ -448,7 +448,7 @@ namespace OpenVic { } \ constexpr NodeTools::NodeCallback auto expect_item_dictionary_and_default( \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return expect_item_dictionary_and_length_and_default( \ NodeTools::default_length_callback, \ @@ -457,7 +457,7 @@ namespace OpenVic { ); \ } \ constexpr NodeTools::NodeCallback auto expect_item_dictionary( \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return expect_item_dictionary_and_length_and_default( \ NodeTools::default_length_callback, \ @@ -468,7 +468,7 @@ namespace OpenVic { constexpr NodeTools::NodeCallback auto expect_item_dictionary_reserve_length_and_default( \ reservable auto& reservable, \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return expect_item_dictionary_and_length_and_default( \ NodeTools::reserve_length_callback(reservable), \ @@ -478,7 +478,7 @@ namespace OpenVic { } \ constexpr NodeTools::NodeCallback auto expect_item_dictionary_reserve_length( \ reservable auto& reservable, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) CONST { \ return expect_item_dictionary_and_length_and_default( \ NodeTools::reserve_length_callback(reservable), \ @@ -520,16 +520,16 @@ namespace OpenVic { /* Parses a dictionary with item keys and decimal number values (in the form of fixed point values), * with the resulting map move-returned via `callback`. The values can be transformed by providing * a fixed point to fixed point function fixed_point_functor, which will be applied to ever parsed value. */ - template FixedPointFunctor = std::identity> + template FixedPointFunctor = std::identity> constexpr NodeTools::NodeCallback auto expect_item_decimal_map( - NodeTools::Callback&&> auto callback, + Callback&&> auto callback, FixedPointFunctor fixed_point_functor = {} ) const { - return [this, callback, fixed_point_functor](ast::NodeCPtr node) -> bool { + return [this, callback, fixed_point_functor](ovdl::v2script::ast::Node const* node) -> bool { fixed_point_map_t map; bool ret = expect_item_dictionary( - [&map, fixed_point_functor](external_value_type const& key, ast::NodeCPtr value) -> bool { + [&map, fixed_point_functor](external_value_type const& key, ovdl::v2script::ast::Node const* value) -> bool { return NodeTools::expect_fixed_point( [&map, fixed_point_functor, &key](fixed_point_t val) -> bool { map.emplace(&key, fixed_point_functor(val)); @@ -582,8 +582,11 @@ namespace OpenVic { has_get_identifier Value, template typename StorageInfo = RegistryStorageInfoVector, string_map_case Case = StringMapCaseSensitive > - using IdentifierPointerRegistry = - ValueRegistry>, StorageInfo, Case>; + using IdentifierPointerRegistry = ValueRegistry< + RegistryValueInfoPointer>, + StorageInfo, + Case + >; template< has_get_identifier Value, template typename StorageInfo = RegistryStorageInfoVector, @@ -651,9 +654,9 @@ public: \ memory::vector get_##singular##_identifiers() const { \ return registry.get_item_identifiers(); \ } \ - template FixedPointFunctor = std::identity> \ + template FixedPointFunctor = std::identity> \ constexpr NodeTools::NodeCallback auto expect_##singular##_decimal_map( \ - NodeTools::Callback&&> auto callback, \ + Callback&&> auto callback, \ FixedPointFunctor fixed_point_functor = {} \ ) const { \ return registry.expect_item_decimal_map(callback, fixed_point_functor); \ @@ -692,68 +695,68 @@ public: \ constexpr decltype(registry)::storage_type const_kw& get_##plural() const_kw { \ return registry.get_items(); \ } \ - constexpr NodeTools::Callback auto expect_##singular##_str( \ - NodeTools::Callback auto callback, bool allow_empty = false, \ + constexpr Callback auto expect_##singular##_str( \ + Callback auto callback, bool allow_empty = false, \ bool warn = false \ ) const_kw { \ return registry.expect_item_str(callback, allow_empty, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_identifier( \ - NodeTools::Callback auto callback, bool warn = false \ + Callback auto callback, bool warn = false \ ) const_kw { \ return registry.expect_item_identifier(callback, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_string( \ - NodeTools::Callback auto callback, bool allow_empty = false, \ + Callback auto callback, bool allow_empty = false, \ bool warn = false \ ) const_kw { \ return registry.expect_item_string(callback, allow_empty, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_identifier_or_string( \ - NodeTools::Callback auto callback, bool allow_empty = false, \ + Callback auto callback, bool allow_empty = false, \ bool warn = false \ ) const_kw { \ return registry.expect_item_identifier_or_string(callback, allow_empty, warn); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_assign_and_default( \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) const_kw { \ return registry.expect_item_assign_and_default(default_callback, callback); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_assign( \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) const_kw { \ return registry.expect_item_assign(callback); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_dictionary_and_length_and_default( \ NodeTools::LengthCallback auto length_callback, \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) const_kw { \ return registry.expect_item_dictionary_and_length_and_default(length_callback, default_callback, callback); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_dictionary_and_default( \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) const_kw { \ return registry.expect_item_dictionary_and_default(default_callback, callback); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_dictionary( \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) const_kw { \ return registry.expect_item_dictionary(callback); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_dictionary_reserve_length_and_default( \ reservable auto& reservable, \ NodeTools::KeyValueCallback auto default_callback, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) const_kw { \ return registry.expect_item_dictionary_reserve_length_and_default(reservable, default_callback, callback); \ } \ constexpr NodeTools::NodeCallback auto expect_##singular##_dictionary_reserve_length( \ reservable auto& reservable, \ - NodeTools::Callback auto callback \ + Callback auto callback \ ) const_kw { \ return registry.expect_item_dictionary_reserve_length(reservable, callback); \ } diff --git a/src/openvic-simulation/types/OrderedContainers.hpp b/src/openvic-simulation/types/OrderedContainers.hpp index a88e84720..0807a60d8 100644 --- a/src/openvic-simulation/types/OrderedContainers.hpp +++ b/src/openvic-simulation/types/OrderedContainers.hpp @@ -71,16 +71,30 @@ namespace OpenVic { class Key, class T, class Hash = container_hash, class KeyEqual = default_equal_to, class RawAllocator = foonathan::memory::default_allocator, class IndexType = std::uint_least32_t, class Allocator = foonathan::memory::std_allocator, memory::tracker>> - using vector_ordered_map = - tsl::ordered_map, Allocator>, IndexType>; + using vector_ordered_map = tsl::ordered_map< + Key, + T, + Hash, + KeyEqual, + Allocator, + std::vector, Allocator>, + IndexType + >; // Useful for stable memory addresses (so long as you don't remove or insert values) template< class Key, class T, class Hash = container_hash, class KeyEqual = default_equal_to, class RawAllocator = foonathan::memory::default_allocator, class IndexType = std::uint_least32_t, class Allocator = foonathan::memory::std_allocator, memory::tracker>> - using deque_ordered_map = - tsl::ordered_map, Allocator>, IndexType>; + using deque_ordered_map = tsl::ordered_map< + Key, + T, + Hash, + KeyEqual, + Allocator, + OpenVic::utility::deque, Allocator>, + IndexType + >; template< class Key, class T, class Hash = container_hash, class KeyEqual = default_equal_to, @@ -100,8 +114,14 @@ namespace OpenVic { class Key, class Hash = container_hash, class KeyEqual = default_equal_to, class RawAllocator = foonathan::memory::default_allocator, class IndexType = std::uint_least32_t, class Allocator = foonathan::memory::std_allocator>> - using deque_ordered_set = - tsl::ordered_set, IndexType>; + using deque_ordered_set = tsl::ordered_set< + Key, + Hash, + KeyEqual, + Allocator, + OpenVic::utility::deque, + IndexType + >; template< class Key, class Hash = container_hash, class KeyEqual = default_equal_to, @@ -182,8 +202,14 @@ namespace OpenVic { template< class Key, class RawAllocator = foonathan::memory::default_allocator, class IndexType = std::uint_least32_t, class Allocator = foonathan::memory::std_allocator>> - using case_insensitive_deque_ordered_set = - deque_ordered_set; + using case_insensitive_deque_ordered_set = deque_ordered_set< + Key, + case_insensitive_string_hash, + case_insensitive_string_equal, + RawAllocator, + IndexType, + Allocator + >; template< class Key, class RawAllocator = foonathan::memory::default_allocator, class IndexType = std::uint_least32_t, diff --git a/src/openvic-simulation/types/RingBuffer.hpp b/src/openvic-simulation/types/RingBuffer.hpp index dee68b6a0..3f7940edb 100644 --- a/src/openvic-simulation/types/RingBuffer.hpp +++ b/src/openvic-simulation/types/RingBuffer.hpp @@ -191,8 +191,8 @@ namespace OpenVic { } RingBuffer& operator=(RingBuffer&& other) noexcept( - allocator_traits::propagate_on_container_move_assignment::value || - std::is_nothrow_move_constructible::value + allocator_traits::propagate_on_container_move_assignment::value + || std::is_nothrow_move_constructible::value ) { if (allocator_traits::propagate_on_container_move_assignment::value || _allocator == other._allocator) { // We're either getting the other's allocator or they're already the same, diff --git a/src/openvic-simulation/types/Signal.hpp b/src/openvic-simulation/types/Signal.hpp index c7bbb7709..856de6c68 100644 --- a/src/openvic-simulation/types/Signal.hpp +++ b/src/openvic-simulation/types/Signal.hpp @@ -1057,8 +1057,8 @@ namespace OpenVic::_detail::signal { */ template requires( - (Callable || Callable || MemberFunctionPointer) && - function_traits::is_disconnectable + (Callable || Callable || MemberFunctionPointer) + && function_traits::is_disconnectable ) size_t disconnect(C const& c) { return disconnect_if([&](slot_ptr const& s) { diff --git a/src/openvic-simulation/utility/ThreadDeps.hpp b/src/openvic-simulation/utility/ThreadDeps.hpp new file mode 100644 index 000000000..49eb2bfd7 --- /dev/null +++ b/src/openvic-simulation/utility/ThreadDeps.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include "openvic-simulation/types/TypedIndices.hpp" + +namespace OpenVic { + struct GameRulesManager; + struct GoodInstanceManager; + struct MapInstance; + struct MilitaryDefines; + struct ModifierEffectCache; + struct PopsDefines; + struct ProductionTypeManager; + struct StaticModifierCache; + + struct ThreadDeps { + GameRulesManager const& game_rules_manager; + GoodInstanceManager const& good_instance_manager; + MapInstance const& map_instance; + MilitaryDefines const& military_defines; + ModifierEffectCache const& modifier_effect_cache; + PopsDefines const& pop_defines; + ProductionTypeManager const& production_type_manager; + StaticModifierCache const& static_modifier_cache; + country_index_t country_count; + good_index_t good_count; + strata_index_t strata_count; + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/utility/ThreadPool.cpp b/src/openvic-simulation/utility/ThreadPool.cpp index 605185d35..2ed9f229e 100644 --- a/src/openvic-simulation/utility/ThreadPool.cpp +++ b/src/openvic-simulation/utility/ThreadPool.cpp @@ -1,4 +1,5 @@ #include "ThreadPool.hpp" +#include "ThreadDeps.hpp" #include #include @@ -10,6 +11,7 @@ #include "openvic-simulation/economy/GoodInstance.hpp" #include "openvic-simulation/economy/trading/GoodMarket.hpp" #include "openvic-simulation/map/ProvinceInstance.hpp" +#include "openvic-simulation/population/PopValuesFromProvince.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/TypedIndices.hpp" #include "openvic-simulation/types/TypedSpan.hpp" @@ -18,20 +20,13 @@ using namespace OpenVic; void ThreadPool::loop_until_cancelled( work_t& work_type, - GameRulesManager const& game_rules_manager, - GoodInstanceManager const& good_instance_manager, - ModifierEffectCache const& modifier_effect_cache, - PopsDefines const& pop_defines, - ProductionTypeManager const& production_type_manager, - forwardable_span country_keys, - const good_index_t good_count, - const strata_index_t strata_count, + const ThreadDeps deps, forwardable_span work_bundles ) { - memory::FixedVector reusable_goods_mask { good_count, {} }; + memory::FixedVector reusable_goods_mask { deps.good_count, {} }; - memory::FixedVector reusable_country_map_0 { country_index_t(country_keys.size()), fixed_point_t::_0 }; - memory::FixedVector reusable_country_map_1 { country_index_t(country_keys.size()), fixed_point_t::_0 }; + memory::FixedVector reusable_country_map_0 { deps.country_count, fixed_point_t::_0 }; + memory::FixedVector reusable_country_map_1 { deps.country_count, fixed_point_t::_0 }; static constexpr std::size_t VECTOR_COUNT = std::max( GoodMarket::VECTORS_FOR_EXECUTE_ORDERS, @@ -44,12 +39,12 @@ void ThreadPool::loop_until_cancelled( std::span, VECTOR_COUNT> reusable_vectors_span = std::span(reusable_vectors); memory::vector reusable_good_index_vector; PopValuesFromProvince reusable_pop_values { - game_rules_manager, - good_instance_manager, - modifier_effect_cache, - production_type_manager, - pop_defines, - strata_count + deps.game_rules_manager, + deps.good_instance_manager, + deps.modifier_effect_cache, + deps.production_type_manager, + deps.pop_defines, + deps.strata_count }; while (!is_cancellation_requested) { @@ -70,11 +65,11 @@ void ThreadPool::loop_until_cancelled( work_type = work_t::NONE; } - switch (work_type_copy) { - case work_t::NONE: - break; - case work_t::GOOD_EXECUTE_ORDERS: - for (WorkBundle& work_bundle : work_bundles) { + for (WorkBundle& work_bundle : work_bundles) { + switch (work_type_copy) { + case work_t::NONE: + break; + case work_t::GOOD_EXECUTE_ORDERS: for (GoodMarket& good : work_bundle.goods_chunk) { good.execute_orders( reusable_country_map_0, @@ -82,10 +77,8 @@ void ThreadPool::loop_until_cancelled( reusable_vectors_span.first() ); } - } - break; - case work_t::PROVINCE_TICK: - for (WorkBundle& work_bundle : work_bundles) { + break; + case work_t::PROVINCE_TICK: for (ProvinceInstance& province : work_bundle.provinces_chunk) { province.province_tick( current_date, @@ -95,23 +88,20 @@ void ThreadPool::loop_until_cancelled( reusable_vectors_span.first() ); } - } - break; - case work_t::PROVINCE_INITIALISE_FOR_NEW_GAME: - for (WorkBundle& work_bundle : work_bundles) { + break; + case work_t::PROVINCE_INITIALISE_FOR_NEW_GAME: for (ProvinceInstance& province : work_bundle.provinces_chunk) { province.initialise_for_new_game( current_date, + deps.map_instance, reusable_pop_values, work_bundle.random_number_generator, reusable_goods_mask, reusable_vectors_span.first() ); } - } - break; - case work_t::COUNTRY_TICK_BEFORE_MAP: - for (WorkBundle& work_bundle : work_bundles) { + break; + case work_t::COUNTRY_TICK_BEFORE_MAP: for (CountryInstance& country : work_bundle.countries_chunk) { country.country_tick_before_map( reusable_goods_mask, @@ -119,15 +109,47 @@ void ThreadPool::loop_until_cancelled( reusable_good_index_vector ); } - } - break; - case work_t::COUNTRY_TICK_AFTER_MAP: - for (WorkBundle& work_bundle : work_bundles) { + break; + case work_t::COUNTRY_TICK_AFTER_MAP: for (CountryInstance& country : work_bundle.countries_chunk) { country.country_tick_after_map(current_date); } - } - break; + break; + case work_t::PROVINCE_UPDATE_GAMESTATE: + for (ProvinceInstance& province : work_bundle.provinces_chunk) { + province.update_gamestate( + current_date, + deps.military_defines + ); + } + break; + case work_t::COUNTRY_UPDATE_GAMESTATE_AFTER_MAP: + for (CountryInstance& country : work_bundle.countries_chunk) { + country.update_gamestate_after_map(current_date); + } + break; + case work_t::PROVINCE_UPDATE_MODIFIER_SUMS: + for (ProvinceInstance& province : work_bundle.provinces_chunk) { + province.update_modifier_sum( + current_date, + deps.static_modifier_cache + ); + } + break; + case work_t::COUNTRY_UPDATE_MODIFIER_SUMS_BEFORE_MAP: + for (CountryInstance& country : work_bundle.countries_chunk) { + country.update_modifier_sum_before_map( + current_date, + deps.static_modifier_cache + ); + } + break; + case work_t::COUNTRY_UPDATE_MODIFIER_SUMS_AFTER_MAP: + for (CountryInstance& country : work_bundle.countries_chunk) { + country.update_modifier_sum_after_map(current_date); + } + break; + } } { @@ -139,7 +161,7 @@ void ThreadPool::loop_until_cancelled( } } -void ThreadPool::process_work(const work_t work_type) { +void ThreadPool::process(const work_t work_type) { { std::unique_lock thread_lock { thread_mutex }; if (is_cancellation_requested) { @@ -184,12 +206,7 @@ ThreadPool::~ThreadPool() { } void ThreadPool::initialise_threadpool( - GameRulesManager const& game_rules_manager, - GoodInstanceManager const& good_instance_manager, - ModifierEffectCache const& modifier_effect_cache, - PopsDefines const& pop_defines, - ProductionTypeManager const& production_type_manager, - const strata_index_t strata_count, + ThreadDeps const& deps, forwardable_span goods, forwardable_span countries, forwardable_span provinces @@ -261,27 +278,13 @@ void ThreadPool::initialise_threadpool( [ this, &work_for_thread = work_per_thread[i], - &game_rules_manager, - &good_instance_manager, - &modifier_effect_cache, - &pop_defines, - &production_type_manager, - countries, - good_count = good_index_t(goods.size()), - strata_count, + deps, work_bundles_begin, work_bundles_end ]() -> void { loop_until_cancelled( work_for_thread, - game_rules_manager, - good_instance_manager, - modifier_effect_cache, - pop_defines, - production_type_manager, - countries, - good_count, - strata_count, + deps, std::span{ work_bundles_begin, work_bundles_end } ); } @@ -289,24 +292,4 @@ void ThreadPool::initialise_threadpool( work_bundles_begin = work_bundles_end; } -} - -void ThreadPool::process_good_execute_orders() { - process_work(work_t::GOOD_EXECUTE_ORDERS); -} - -void ThreadPool::process_province_ticks() { - process_work(work_t::PROVINCE_TICK); -} - -void ThreadPool::process_province_initialise_for_new_game() { - process_work(work_t::PROVINCE_INITIALISE_FOR_NEW_GAME); -} - -void ThreadPool::process_country_ticks_before_map() { - process_work(work_t::COUNTRY_TICK_BEFORE_MAP); -} - -void ThreadPool::process_country_ticks_after_map(){ - process_work(work_t::COUNTRY_TICK_AFTER_MAP); } \ No newline at end of file diff --git a/src/openvic-simulation/utility/ThreadPool.hpp b/src/openvic-simulation/utility/ThreadPool.hpp index 8251857ef..75a05fdf9 100644 --- a/src/openvic-simulation/utility/ThreadPool.hpp +++ b/src/openvic-simulation/utility/ThreadPool.hpp @@ -7,24 +7,17 @@ #include #include -#include "openvic-simulation/population/PopValuesFromProvince.hpp" #include "openvic-simulation/core/memory/Vector.hpp" #include "openvic-simulation/core/portable/ForwardableSpan.hpp" #include "openvic-simulation/core/random/RandomGenerator.hpp" #include "openvic-simulation/population/PopValuesFromProvince.hpp" #include "openvic-simulation/types/Date.hpp" -#include "openvic-simulation/types/TypedIndices.hpp" namespace OpenVic { - struct GameRulesManager; - struct GoodDefinition; - struct GoodInstanceManager; struct CountryInstance; struct GoodInstance; - struct ModifierEffectCache; - struct PopsDefines; - struct ProductionTypeManager; - struct Strata; + struct ProvinceInstance; + struct ThreadDeps; //bundle work so they always have the same rng regardless of hardware concurrency struct WorkBundle { @@ -47,18 +40,23 @@ namespace OpenVic { provinces_chunk { new_provinces_chunk } {} }; + + enum struct work_t : uint8_t { + NONE, + GOOD_EXECUTE_ORDERS, + PROVINCE_INITIALISE_FOR_NEW_GAME, + PROVINCE_TICK, + COUNTRY_TICK_BEFORE_MAP, + COUNTRY_TICK_AFTER_MAP, + PROVINCE_UPDATE_GAMESTATE, + COUNTRY_UPDATE_GAMESTATE_AFTER_MAP, + PROVINCE_UPDATE_MODIFIER_SUMS, + COUNTRY_UPDATE_MODIFIER_SUMS_BEFORE_MAP, + COUNTRY_UPDATE_MODIFIER_SUMS_AFTER_MAP + }; struct ThreadPool { private: - enum struct work_t : uint8_t { - NONE, - GOOD_EXECUTE_ORDERS, - PROVINCE_INITIALISE_FOR_NEW_GAME, - PROVINCE_TICK, - COUNTRY_TICK_BEFORE_MAP, - COUNTRY_TICK_AFTER_MAP - }; - constexpr static std::size_t WORK_BUNDLE_COUNT = 32; std::array all_work_bundles; memory::vector threads; @@ -71,39 +69,21 @@ namespace OpenVic { void loop_until_cancelled( work_t& work_type, - GameRulesManager const& game_rules_manager, - GoodInstanceManager const& good_instance_manager, - ModifierEffectCache const& modifier_effect_cache, - PopsDefines const& pop_defines, - ProductionTypeManager const& production_type_manager, - forwardable_span country_keys, - const good_index_t good_count, - const strata_index_t strata_count, + const ThreadDeps deps, forwardable_span work_bundles ); void await_completion(); - void process_work(const work_t work_type); public: ThreadPool(Date const& new_current_date); ~ThreadPool(); void initialise_threadpool( - GameRulesManager const& game_rules_manager, - GoodInstanceManager const& good_instance_manager, - ModifierEffectCache const& modifier_effect_cache, - PopsDefines const& pop_defines, - ProductionTypeManager const& production_type_manager, - const strata_index_t strata_count, + ThreadDeps const& deps, forwardable_span goods, forwardable_span countries, forwardable_span provinces ); - - void process_good_execute_orders(); - void process_province_ticks(); - void process_province_initialise_for_new_game(); - void process_country_ticks_before_map(); - void process_country_ticks_after_map(); + void process(const work_t work_type); }; } \ No newline at end of file diff --git a/tests/src/SpyAllocator.hpp b/tests/src/SpyAllocator.hpp new file mode 100644 index 000000000..22a1d21e0 --- /dev/null +++ b/tests/src/SpyAllocator.hpp @@ -0,0 +1,61 @@ +#include +#include + +// Telemetry structure remains the same +struct AllocationMetrics { + std::size_t total_allocated_bytes = 0; + std::size_t total_deallocated_bytes = 0; + std::size_t allocation_count = 0; + std::size_t deallocation_count = 0; + + void reset() { + total_allocated_bytes = 0; + total_deallocated_bytes = 0; + allocation_count = 0; + deallocation_count = 0; + } + + std::size_t active_bytes() const { + return total_allocated_bytes - total_deallocated_bytes; + } +}; + +// SpyAllocator now wraps an underlying Allocator type (defaults to std::allocator) +template > +class SpyAllocator { +public: + using value_type = T; + + // Shared telemetry state across allocator copies + std::shared_ptr metrics; + // The actual allocator doing the heavy lifting + Allocator upstream; + + // Default Constructor + SpyAllocator() + : metrics(std::make_shared()), upstream() {} + + // Explicitly pass an existing upstream allocator instance if needed + explicit SpyAllocator(const Allocator& alloc) + : metrics(std::make_shared()), upstream(alloc) {} + + value_type* allocate(const std::size_t n) { + // Delegate allocation to the upstream allocator + value_type* ptr = std::allocator_traits::allocate(upstream, n); + + if (metrics && ptr) { + metrics->total_allocated_bytes += (n * sizeof(value_type)); + metrics->allocation_count++; + } + return ptr; + } + + void deallocate(value_type* const ptr, const std::size_t n) noexcept { + if (metrics) { + metrics->total_deallocated_bytes += (n * sizeof(value_type)); + metrics->deallocation_count++; + } + // Delegate deallocation to the upstream allocator + std::allocator_traits::deallocate(upstream, ptr, n); + } +}; \ No newline at end of file diff --git a/tests/src/core/BulkInsertWrapper.cpp b/tests/src/core/BulkInsertWrapper.cpp new file mode 100644 index 000000000..7b2301ce6 --- /dev/null +++ b/tests/src/core/BulkInsertWrapper.cpp @@ -0,0 +1,162 @@ +#include +#include +#include + +#include "openvic-simulation/core/BulkInsertWrapper.hpp" + +#include + +#include "SpyAllocator.hpp" + +// A simple non-trivially destructible type to test constraint violations if needed, +// and a trivial one to satisfy emplace_back's requires clause. +struct TrivialPoint { + int x = 0; + int y = 0; +}; + +using namespace OpenVic; + +TEST_CASE("bulk_insert_wrapper Constructors", "[bulk_insert_wrapper][bulk_insert_wrapper-constructor]") { + constexpr bulk_insert_wrapper> empty; + CONSTEXPR_CHECK(empty.empty()); + CONSTEXPR_CHECK(empty.size() == 0); + CONSTEXPR_CHECK(empty.capacity() == 0); + CONSTEXPR_CHECK(empty.begin() == empty.end()); + CONSTEXPR_CHECK(empty.cbegin() == empty.cend()); + CONSTEXPR_CHECK(empty.rbegin() == empty.rend()); + + constexpr std::size_t expected_size = 3; + bulk_insert_wrapper> filled { + std::vector { 1, 2, 3 } + }; + CHECK(!filled.empty()); + CHECK(filled.size() == expected_size); + CHECK(filled.capacity() >= expected_size); + CHECK(std::distance(filled.begin(), filled.end()) == expected_size); + CHECK(std::distance(filled.cbegin(), filled.cend()) == expected_size); + CHECK(std::distance(filled.rbegin(), filled.rend()) == expected_size); + CHECK(filled[0] == 1); +} + +TEST_CASE("bulk_insert_wrapper make_room does not allocate", "[bulk_insert_wrapper][bulk_insert_wrapper-make_room]") { + SpyAllocator spy_allocator{}; + bulk_insert_wrapper< + std::vector< + int, + SpyAllocator + > + > empty { spy_allocator }; + empty.make_room_for(10); + + CHECK(empty.empty()); + CHECK(empty.size() == 0); + CHECK(empty.capacity() == 0); + CHECK(empty.begin() == empty.end()); + CHECK(empty.cbegin() == empty.cend()); + CHECK(empty.rbegin() == empty.rend()); + CHECK(spy_allocator.metrics->allocation_count == 0); +} + +// correct usage +TEST_CASE("bulk_insert_wrapper make_room + append_range", "[bulk_insert_wrapper][bulk_insert_wrapper-append_range]") { + SpyAllocator spy_allocator{}; + bulk_insert_wrapper< + std::vector< + int, + SpyAllocator + > + > wrapper { spy_allocator }; + + std::vector a { 1, 2 }; + std::vector b { 3, 4, 5 }; + + wrapper.make_room_for(a.size()); + wrapper.make_room_for(b.size()); + + wrapper.append_range(a); + CHECK(wrapper.size() == a.size()); + CHECK(wrapper.capacity() >= a.size() + b.size()); + + wrapper.append_range(b); + + CHECK(wrapper.size() == a.size() + b.size()); + CHECK(spy_allocator.metrics->allocation_count == 1); +} + +// cassert prevents testing in debug +#ifdef NDEBUG +// incorrect usage may not crash +TEST_CASE("bulk_insert_wrapper append_range without make_room", "[bulk_insert_wrapper][bulk_insert_wrapper-append_range]") { + SpyAllocator spy_allocator{}; + bulk_insert_wrapper< + std::vector< + int, + SpyAllocator + > + > wrapper { spy_allocator }; + + std::vector a { 1, 2 }; + std::vector b { 3, 4, 5 }; + + wrapper.append_range(a); + wrapper.append_range(b); + + CHECK(wrapper.size() == a.size() + b.size()); + CHECK(wrapper.capacity() >= a.size() + b.size()); + CHECK(spy_allocator.metrics->allocation_count <= 2); +} +#endif + +TEST_CASE("bulk_insert_wrapper clear", "[bulk_insert_wrapper][bulk_insert_wrapper-clear]") { + SpyAllocator spy_allocator{}; + bulk_insert_wrapper< + std::vector< + int, + SpyAllocator + > + > wrapper { spy_allocator }; + + std::vector a { 1, 2 }; + constexpr std::size_t extra_room = 1; + + wrapper.make_room_for(a.size()); + wrapper.make_room_for(extra_room); + + wrapper.append_range(a); + CHECK(wrapper.size() == a.size()); + CHECK(wrapper.capacity() >= a.size() + extra_room); + + wrapper.clear(); + CHECK(wrapper.empty()); + CHECK(spy_allocator.metrics->allocation_count == 1); +} + +TEST_CASE("bulk_insert_wrapper shrink_to_fit", "[bulk_insert_wrapper][bulk_insert_wrapper-shrink_to_fit]") { + SpyAllocator spy_allocator{}; + bulk_insert_wrapper< + std::vector< + int, + SpyAllocator + > + > wrapper { spy_allocator }; + + std::vector a { 1, 2 }; + constexpr std::size_t extra_room = 1; + + wrapper.make_room_for(a.size()); + wrapper.make_room_for(extra_room); + + wrapper.append_range(a); + CHECK(wrapper.size() == a.size()); + const std::size_t capacity_before_shrink = wrapper.capacity(); + CHECK(capacity_before_shrink >= a.size() + extra_room); + + wrapper.shrink_to_fit(); + CHECK(wrapper.size() == a.size()); + // shrink_to_fit() is non-binding, it may or may not actually shrink depending on underlying container implementation. + // just ensure we don't expand or allocate too often somehow + CHECK(wrapper.capacity() <= capacity_before_shrink); + CHECK(spy_allocator.metrics->allocation_count >= 1); + CHECK(spy_allocator.metrics->allocation_count <= 2); +} \ No newline at end of file diff --git a/tests/src/core/DualAdjacent.cpp b/tests/src/core/DualAdjacent.cpp index 88cc72954..dedf8e0fb 100644 --- a/tests/src/core/DualAdjacent.cpp +++ b/tests/src/core/DualAdjacent.cpp @@ -153,20 +153,29 @@ TEST_CASE("remove_if_dual_adjacent", "[algorithm][dual-adjacent][remove_if_dual_ remove_if_dual_adjacent(vector_down.begin(), vector_down.end(), std::bind_front(callback_bind, 9)) == vector_down.end() ); CHECK( - remove_if_dual_adjacent(vector_spread.begin(), vector_spread.end(), std::bind_front(callback_bind, 5)) == - vector_spread.end() + remove_if_dual_adjacent( + vector_spread.begin(), + vector_spread.end(), + std::bind_front(callback_bind, 5) + ) == vector_spread.end() ); CHECK( // remove_if_dual_adjacent(vector_up.begin(), vector_up.end(), std::bind_front(callback_bind, 9)) == vector_up.end() - 1 ); CHECK( - remove_if_dual_adjacent(vector_down.begin(), vector_down.end(), std::bind_front(callback_bind, 1)) == - vector_down.end() - 1 + remove_if_dual_adjacent( + vector_down.begin(), + vector_down.end(), + std::bind_front(callback_bind, 1) + ) == vector_down.end() - 1 ); CHECK( - remove_if_dual_adjacent(vector_spread.begin(), vector_spread.end(), std::bind_front(callback_bind, 6)) == - vector_spread.end() - 1 + remove_if_dual_adjacent( + vector_spread.begin(), + vector_spread.end(), + std::bind_front(callback_bind, 6) + ) == vector_spread.end() - 1 ); static constexpr auto removed_vector_up = std::to_array({ 1, 2, 3, 5, 6, 7, 8, 9, 9 }); diff --git a/tests/src/dataloader/NodeTools.cpp b/tests/src/dataloader/NodeTools.cpp index 6f7d25b1b..2246c1fe5 100644 --- a/tests/src/dataloader/NodeTools.cpp +++ b/tests/src/dataloader/NodeTools.cpp @@ -22,6 +22,7 @@ #include #include +#include "openvic-simulation/core/template/FunctionalConcepts.hpp" #include "openvic-simulation/types/Colour.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" @@ -37,7 +38,6 @@ #include using namespace OpenVic; -using namespace OpenVic::ast; using namespace std::string_view_literals; namespace snitch { @@ -48,8 +48,8 @@ namespace snitch { } struct Ast : ovdl::SymbolIntern { - dryad::tree tree; - dryad::node_map map; + dryad::tree tree; + dryad::node_map map; symbol_interner_type symbol_interner; template @@ -80,11 +80,11 @@ struct Ast : ovdl::SymbolIntern { return node; } - void set_location(const Node* n, ovdl::NodeLocation loc) { + void set_location(ovdl::v2script::ast::Node const* n, ovdl::NodeLocation loc) { map.insert(n, loc); } - ovdl::NodeLocation location_of(const Node* n) const { + ovdl::NodeLocation location_of(ovdl::v2script::ast::Node const* n) const { auto result = map.lookup(n); if (result == nullptr) { return {}; @@ -93,14 +93,14 @@ struct Ast : ovdl::SymbolIntern { } - template + template inline auto create_value_statement_factory() { return [this](std::string_view sv) { - return create(create_with_intern(sv)); + return create(create_with_intern(sv)); }; } - template + template inline auto create_value_statement_factory(std::string_view sv) { return [this, sv]() { return create_value_statement_factory()(sv); @@ -110,38 +110,38 @@ struct Ast : ovdl::SymbolIntern { template inline auto create_assign_statement_factory(std::string_view lhs, std::string_view rhs) { return [this, lhs, rhs]() { - return create(create_with_intern(lhs), create_with_intern(rhs)); + return create(create_with_intern(lhs), create_with_intern(rhs)); }; } - template + template inline auto create_assign_statement_factory(std::string_view lhs, std::string_view rhs) { - return create_assign_statement_factory(lhs, rhs); + return create_assign_statement_factory(lhs, rhs); } - template + template inline auto create_assign_statement_rhs_factory(std::string_view rhs) { return [this, rhs](std::string_view lhs) { - return create_assign_statement_factory(lhs, rhs)(); + return create_assign_statement_factory(lhs, rhs)(); }; } - inline ListValue* create_value_list_fill(size_t count, auto&& factory) { - StatementList slist; + inline ovdl::v2script::ast::ListValue* create_value_list_fill(size_t count, auto&& factory) { + ovdl::v2script::ast::StatementList slist; for (size_t index = 0; index < count; index++) { slist.push_back(factory()); } - return create(slist); + return create(slist); } - inline ListValue* create_value_list(auto&&... factories) { - StatementList slist; + inline ovdl::v2script::ast::ListValue* create_value_list(auto&&... factories) { + ovdl::v2script::ast::StatementList slist; (slist.push_back(factories()), ...); - return create(slist); + return create(slist); } - inline ListValue* create_assign_list(std::span span, auto&& factory) { - StatementList slist; + inline ovdl::v2script::ast::ListValue* create_assign_list(std::span span, auto&& factory) { + ovdl::v2script::ast::StatementList slist; for (std::string_view const& sv : span) { if constexpr (requires { factory(sv); }) { slist.push_back(factory(sv)); @@ -149,25 +149,25 @@ struct Ast : ovdl::SymbolIntern { slist.push_back(factory()); } } - return create(slist); + return create(slist); } - template - inline ListValue* create_assign_list(std::span span_lhs, std::span span_rhs) { - StatementList slist; + template + inline ovdl::v2script::ast::ListValue* create_assign_list(std::span span_lhs, std::span span_rhs) { + ovdl::v2script::ast::StatementList slist; for (auto [index, lhs] : span_lhs | ranges::views::enumerate) { index %= span_rhs.size(); slist.push_back(create_assign_statement_rhs_factory(span_rhs[index])(lhs)); } - return create(slist); + return create(slist); } - inline ListValue* create_assign_list(size_t count, auto&& factory) { + inline ovdl::v2script::ast::ListValue* create_assign_list(size_t count, auto&& factory) { static constexpr size_t bits_per_digit = 4; static constexpr size_t array_length = sizeof(size_t) * CHAR_BIT / bits_per_digit * 4; thread_local std::array hex_array {}; - StatementList slist; + ovdl::v2script::ast::StatementList slist; for (size_t index : ranges::views::iota(static_cast(0), count)) { std::to_chars_result result = std::to_chars(hex_array.data(), hex_array.data() + hex_array.size(), index); CHECK_IF(result.ec == std::errc {}); @@ -176,16 +176,16 @@ struct Ast : ovdl::SymbolIntern { } slist.push_back(factory(std::string_view { hex_array.data(), result.ptr })); } - return create(slist); + return create(slist); } - template - inline ListValue* create_assign_list(size_t count, std::span span_rhs) { + template + inline ovdl::v2script::ast::ListValue* create_assign_list(size_t count, std::span span_rhs) { static constexpr size_t bits_per_digit = 4; static constexpr size_t array_length = sizeof(size_t) * CHAR_BIT / bits_per_digit * 4; thread_local std::array hex_array {}; - StatementList slist; + ovdl::v2script::ast::StatementList slist; for (auto [index, lhs] : ranges::views::iota(static_cast(0), count) | ranges::views::enumerate) { index %= span_rhs.size(); std::to_chars_result result = std::to_chars(hex_array.data(), hex_array.data() + hex_array.size(), lhs); @@ -195,7 +195,7 @@ struct Ast : ovdl::SymbolIntern { } slist.push_back(create_assign_statement_rhs_factory(span_rhs[static_cast(index)])(lhs)); } - return create(slist); + return create(slist); } }; @@ -227,8 +227,8 @@ TEST_CASE("NodeTools AST get_type_name", "[NodeTools][NodeTools-ast-get_type_nam } TEST_CASE("NodeTools Default Callback functions", "[NodeTools][NodeTools-default-callback-functions]") { - dryad::tree tree; - Node* null_value = tree.create(); + dryad::tree tree; + ovdl::v2script::ast::Node* null_value = tree.create(); CHECK(NodeTools::success_callback(null_value)); CHECK(NodeTools::key_value_success_callback(""sv, null_value)); @@ -242,10 +242,10 @@ TEST_CASE("NodeTools Default Callback functions", "[NodeTools][NodeTools-default TEST_CASE("NodeTools expect string functions", "[NodeTools][NodeTools-expect-functions][NodeTools-expect-string-functions]") { Ast ast; - auto* id = ast.create_with_intern("expect"sv); - auto* str = ast.create_with_intern("expect"sv); + auto* id = ast.create_with_intern("expect"sv); + auto* str = ast.create_with_intern("expect"sv); - NodeTools::Callback auto string_view_callback = [](std::string_view sv) -> bool { + Callback auto string_view_callback = [](std::string_view sv) -> bool { CHECK_RETURN_BOOL(sv == "expect"sv); }; @@ -262,19 +262,19 @@ TEST_CASE("NodeTools expect string functions", "[NodeTools][NodeTools-expect-fun TEST_CASE("NodeTools expect bool functions", "[NodeTools][NodeTools-expect-functions][NodeTools-expect-bool-functions]") { Ast ast; - auto* yes_id = ast.create_with_intern("yes"sv); - auto* yes_str = ast.create_with_intern("yes"sv); - auto* no_id = ast.create_with_intern("no"sv); - auto* no_str = ast.create_with_intern("no"sv); + auto* yes_id = ast.create_with_intern("yes"sv); + auto* yes_str = ast.create_with_intern("yes"sv); + auto* no_id = ast.create_with_intern("no"sv); + auto* no_str = ast.create_with_intern("no"sv); - auto* _2_id = ast.create_with_intern("2"sv); - auto* _2_str = ast.create_with_intern("2"sv); - auto* _1_id = ast.create_with_intern("1"sv); - auto* _1_str = ast.create_with_intern("1"sv); - auto* _0_id = ast.create_with_intern("0"sv); - auto* _0_str = ast.create_with_intern("0"sv); + auto* _2_id = ast.create_with_intern("2"sv); + auto* _2_str = ast.create_with_intern("2"sv); + auto* _1_id = ast.create_with_intern("1"sv); + auto* _1_str = ast.create_with_intern("1"sv); + auto* _0_id = ast.create_with_intern("0"sv); + auto* _0_str = ast.create_with_intern("0"sv); - NodeTools::Callback auto bool_callback = [](bool val) -> bool { + Callback auto bool_callback = [](bool val) -> bool { return val; }; @@ -306,26 +306,26 @@ TEST_CASE("NodeTools expect bool functions", "[NodeTools][NodeTools-expect-funct TEST_CASE("NodeTools expect integer functions", "[NodeTools][NodeTools-expect-functions][NodeTools-expect-integer-functions]") { Ast ast; - auto* _2_id = ast.create_with_intern("2"sv); - auto* _2_str = ast.create_with_intern("2"sv); - auto* _1_id = ast.create_with_intern("1"sv); - auto* _1_str = ast.create_with_intern("1"sv); - auto* _0_id = ast.create_with_intern("0"sv); - auto* _0_str = ast.create_with_intern("0"sv); - - auto* _2002_id = ast.create_with_intern("2002"sv); - auto* _2002_str = ast.create_with_intern("2002"sv); - auto* _1001_id = ast.create_with_intern("1001"sv); - auto* _1001_str = ast.create_with_intern("1001"sv); - - auto* _max_long_id = ast.create_with_intern("9223372036854775807"sv); - auto* _max_long_str = ast.create_with_intern("9223372036854775807"sv); - auto* _max_ulong_id = ast.create_with_intern("18446744073709551615"sv); - auto* _max_ulong_str = ast.create_with_intern("18446744073709551615"sv); - auto* _min_long_id = ast.create_with_intern("-9223372036854775808"sv); - auto* _min_long_str = ast.create_with_intern("-9223372036854775808"sv); - - auto callback = [](FlatValue const* ptr, auto val) -> bool { + auto* _2_id = ast.create_with_intern("2"sv); + auto* _2_str = ast.create_with_intern("2"sv); + auto* _1_id = ast.create_with_intern("1"sv); + auto* _1_str = ast.create_with_intern("1"sv); + auto* _0_id = ast.create_with_intern("0"sv); + auto* _0_str = ast.create_with_intern("0"sv); + + auto* _2002_id = ast.create_with_intern("2002"sv); + auto* _2002_str = ast.create_with_intern("2002"sv); + auto* _1001_id = ast.create_with_intern("1001"sv); + auto* _1001_str = ast.create_with_intern("1001"sv); + + auto* _max_long_id = ast.create_with_intern("9223372036854775807"sv); + auto* _max_long_str = ast.create_with_intern("9223372036854775807"sv); + auto* _max_ulong_id = ast.create_with_intern("18446744073709551615"sv); + auto* _max_ulong_str = ast.create_with_intern("18446744073709551615"sv); + auto* _min_long_id = ast.create_with_intern("-9223372036854775808"sv); + auto* _min_long_str = ast.create_with_intern("-9223372036854775808"sv); + + auto callback = [](ovdl::v2script::ast::FlatValue const* ptr, auto val) -> bool { std::string_view sv = ptr->value().view(); decltype(val) check; @@ -374,7 +374,7 @@ TEST_CASE("NodeTools expect integer functions", "[NodeTools][NodeTools-expect-fu CHECK_FALSE(NodeTools::expect_uint64(std::bind_front(callback, _min_long_id))(_min_long_id)); CHECK_FALSE(NodeTools::expect_uint64(std::bind_front(callback, _min_long_str))(_min_long_str)); - auto base_callback = [](FlatValue const* ptr, int base, auto val) -> bool { + auto base_callback = [](ovdl::v2script::ast::FlatValue const* ptr, int base, auto val) -> bool { std::string_view sv = ptr->value().view(); decltype(val) check; @@ -387,21 +387,21 @@ TEST_CASE("NodeTools expect integer functions", "[NodeTools][NodeTools-expect-fu CHECK_RETURN_BOOL(check == val); }; - auto* _binary_id = ast.create_with_intern("0110"sv); - auto* _binary_str = ast.create_with_intern("0110"sv); + auto* _binary_id = ast.create_with_intern("0110"sv); + auto* _binary_str = ast.create_with_intern("0110"sv); CHECK(NodeTools::expect_uint64(std::bind_front(base_callback, _binary_id, 2), 2)(_binary_id)); CHECK_FALSE(NodeTools::expect_uint64(std::bind_front(base_callback, _binary_str, 2), 2)(_binary_str)); - auto* _octal_id = ast.create_with_intern("2674"sv); - auto* _octal_str = ast.create_with_intern("2674"sv); + auto* _octal_id = ast.create_with_intern("2674"sv); + auto* _octal_str = ast.create_with_intern("2674"sv); CHECK(NodeTools::expect_uint64(std::bind_front(base_callback, _octal_id, 8), 8)(_octal_id)); CHECK_FALSE(NodeTools::expect_uint64(std::bind_front(base_callback, _octal_str, 8), 8)(_octal_str)); SECTION("Detect Octal Base") { - auto* _detect_octal_id = ast.create_with_intern("02674"sv); - auto* _detect_octal_str = ast.create_with_intern("02674"sv); + auto* _detect_octal_id = ast.create_with_intern("02674"sv); + auto* _detect_octal_str = ast.create_with_intern("02674"sv); CHECK(NodeTools::expect_uint64(std::bind_front(base_callback, _detect_octal_id, 0), 0)(_detect_octal_id)); CHECK_FALSE(NodeTools::expect_uint64(std::bind_front(base_callback, _detect_octal_str, 0), 0)(_detect_octal_str)); @@ -410,8 +410,8 @@ TEST_CASE("NodeTools expect integer functions", "[NodeTools][NodeTools-expect-fu CHECK_FALSE(NodeTools::expect_uint64(std::bind_front(base_callback, _detect_octal_str, 8), 0)(_detect_octal_str)); } - auto* _hex_id = ast.create_with_intern("0xff2e5"sv); - auto* _hex_str = ast.create_with_intern("0xff2e5"sv); + auto* _hex_id = ast.create_with_intern("0xff2e5"sv); + auto* _hex_str = ast.create_with_intern("0xff2e5"sv); CHECK(NodeTools::expect_uint64(std::bind_front(base_callback, _hex_id, 16), 16)(_hex_id)); CHECK_FALSE(NodeTools::expect_uint64(std::bind_front(base_callback, _hex_str, 16), 16)(_hex_str)); @@ -431,14 +431,14 @@ TEST_CASE( ) { Ast ast; - auto* _0_01_id = ast.create_with_intern("0.01"sv); - auto* _0_01_str = ast.create_with_intern("0.01"sv); - auto* _2_21_id = ast.create_with_intern("2.21"sv); - auto* _2_21_str = ast.create_with_intern("2.21"sv); - auto* _303_id = ast.create_with_intern("303"sv); - auto* _303_str = ast.create_with_intern("303"sv); + auto* _0_01_id = ast.create_with_intern("0.01"sv); + auto* _0_01_str = ast.create_with_intern("0.01"sv); + auto* _2_21_id = ast.create_with_intern("2.21"sv); + auto* _2_21_str = ast.create_with_intern("2.21"sv); + auto* _303_id = ast.create_with_intern("303"sv); + auto* _303_str = ast.create_with_intern("303"sv); - auto callback = [](FlatValue const* ptr, fixed_point_t val) -> bool { + auto callback = [](ovdl::v2script::ast::FlatValue const* ptr, fixed_point_t val) -> bool { std::string_view sv = ptr->value().view(); fixed_point_t check = 0; @@ -483,7 +483,7 @@ TEST_CASE("NodeTools expect colour functions", "[NodeTools][NodeTools-expect-fun auto* _mix_list = ast.create_value_list(_0, _0_5, _255); - auto callback = [](ListValue const* ptr, colour_t val) -> bool { + auto callback = [](ovdl::v2script::ast::ListValue const* ptr, colour_t val) -> bool { auto list = ptr->statements(); CHECK_IF(ranges::distance(list) == 3); else { @@ -492,8 +492,8 @@ TEST_CASE("NodeTools expect colour functions", "[NodeTools][NodeTools-expect-fun colour_t check = colour_t::null(); for (auto [index, sub_node] : list | ranges::views::enumerate) { - if (auto const* value = dryad::node_try_cast(sub_node)) { - if (auto const* id = dryad::node_try_cast(value->value())) { + if (auto const* value = dryad::node_try_cast(sub_node)) { + if (auto const* id = dryad::node_try_cast(value->value())) { std::string_view sv = id->value().view(); fixed_point_t fp = 0; std::from_chars_result result = fp::from_chars(fp, sv.data(), sv.data() + sv.size()); @@ -522,10 +522,10 @@ TEST_CASE( ) { Ast ast; - auto* orange_id = ast.create_with_intern("0xFFBB55"sv); - auto* orange_str = ast.create_with_intern("0xFFBB55"sv); + auto* orange_id = ast.create_with_intern("0xFFBB55"sv); + auto* orange_str = ast.create_with_intern("0xFFBB55"sv); - auto callback = [](FlatValue const* ptr, colour_argb_t val) -> bool { + auto callback = [](ovdl::v2script::ast::FlatValue const* ptr, colour_argb_t val) -> bool { std::string_view sv = ptr->value().view(); colour_argb_t check; @@ -547,18 +547,18 @@ TEST_CASE( ) { Ast ast; - auto* left_id = ast.create_with_intern("left"sv); - auto* left_str = ast.create_with_intern("left"sv); - auto* right_id = ast.create_with_intern("right"sv); - auto* right_str = ast.create_with_intern("right"sv); - auto* centre_id = ast.create_with_intern("centre"sv); - auto* centre_str = ast.create_with_intern("centre"sv); - auto* center_id = ast.create_with_intern("center"sv); - auto* center_str = ast.create_with_intern("center"sv); - auto* justified_id = ast.create_with_intern("justified"sv); - auto* justified_str = ast.create_with_intern("justified"sv); - - auto callback = [](FlatValue const* ptr, text_format_t val) -> bool { + auto* left_id = ast.create_with_intern("left"sv); + auto* left_str = ast.create_with_intern("left"sv); + auto* right_id = ast.create_with_intern("right"sv); + auto* right_str = ast.create_with_intern("right"sv); + auto* centre_id = ast.create_with_intern("centre"sv); + auto* centre_str = ast.create_with_intern("centre"sv); + auto* center_id = ast.create_with_intern("center"sv); + auto* center_str = ast.create_with_intern("center"sv); + auto* justified_id = ast.create_with_intern("justified"sv); + auto* justified_str = ast.create_with_intern("justified"sv); + + auto callback = [](ovdl::v2script::ast::FlatValue const* ptr, text_format_t val) -> bool { using enum text_format_t; static const string_map_t format_map = // { // @@ -590,8 +590,8 @@ TEST_CASE( TEST_CASE("NodeTools expect Date functions", "[NodeTools][NodeTools-expect-functions][NodeTools-expect-date-functions]") { Ast ast; - auto* date_id = ast.create_with_intern("1.1.1"sv); - auto* date_str = ast.create_with_intern("1.1.1"sv); + auto* date_id = ast.create_with_intern("1.1.1"sv); + auto* date_str = ast.create_with_intern("1.1.1"sv); static auto sv_callback = [](std::string_view sv, Date val) -> bool { Date::from_chars_result result = {}; @@ -604,7 +604,7 @@ TEST_CASE("NodeTools expect Date functions", "[NodeTools][NodeTools-expect-funct CHECK_RETURN_BOOL(check == val); }; - auto callback = [](FlatValue const* ptr, Date val) -> bool { + auto callback = [](ovdl::v2script::ast::FlatValue const* ptr, Date val) -> bool { std::string_view sv = ptr->value().view(); return sv_callback(sv, val); }; @@ -625,11 +625,11 @@ TEST_CASE( ) { Ast ast; - auto* _5_id = ast.create_with_intern("5"sv); - auto* _5_str = ast.create_with_intern("5"sv); + auto* _5_id = ast.create_with_intern("5"sv); + auto* _5_str = ast.create_with_intern("5"sv); enum class TimespanType : uint8_t { Day, Month, Year }; - auto callback = [](FlatValue const* ptr, TimespanType type, Timespan val) -> bool { + auto callback = [](ovdl::v2script::ast::FlatValue const* ptr, TimespanType type, Timespan val) -> bool { std::string_view sv = ptr->value().view(); int64_t check_int; @@ -663,17 +663,17 @@ TEST_CASE("NodeTools expect vector functions", "[NodeTools][NodeTools-expect-fun Ast ast; auto _0_id_assign = ast.create_assign_statement_rhs_factory("0"sv); - auto _0_str_assign = ast.create_assign_statement_rhs_factory("0"sv); + auto _0_str_assign = ast.create_assign_statement_rhs_factory("0"sv); auto _0_5_id_assign = ast.create_assign_statement_rhs_factory("0.5"sv); - auto _0_5_str_assign = ast.create_assign_statement_rhs_factory("0.5"sv); + auto _0_5_str_assign = ast.create_assign_statement_rhs_factory("0.5"sv); auto _1_id_assign = ast.create_assign_statement_rhs_factory("1"sv); - auto _1_str_assign = ast.create_assign_statement_rhs_factory("1"sv); + auto _1_str_assign = ast.create_assign_statement_rhs_factory("1"sv); auto _127_id_assign = ast.create_assign_statement_rhs_factory("127"sv); - auto _127_str_assign = ast.create_assign_statement_rhs_factory("127"sv); + auto _127_str_assign = ast.create_assign_statement_rhs_factory("127"sv); auto _255_id_assign = ast.create_assign_statement_rhs_factory("255"sv); - auto _255_str_assign = ast.create_assign_statement_rhs_factory("255"sv); + auto _255_str_assign = ast.create_assign_statement_rhs_factory("255"sv); - static auto callback = [](ListValue const* ptr, auto val) -> bool { + static auto callback = [](ovdl::v2script::ast::ListValue const* ptr, auto val) -> bool { auto list = ptr->statements(); CHECK_IF(ranges::distance(list) == val.size()); else { @@ -684,9 +684,9 @@ TEST_CASE("NodeTools expect vector functions", "[NodeTools][NodeTools-expect-fun for (auto [index, statement] : list | ranges::views::enumerate) { std::string_view sv; - if (auto* assign = dryad::node_try_cast(statement)) { - auto* lhs = dryad::node_cast(assign->left()); - auto* rhs = dryad::node_cast(assign->right()); + if (auto* assign = dryad::node_try_cast(statement)) { + auto* lhs = dryad::node_cast(assign->left()); + auto* rhs = dryad::node_cast(assign->right()); sv = rhs->value().view(); CAPTURE(index); @@ -699,8 +699,8 @@ TEST_CASE("NodeTools expect vector functions", "[NodeTools][NodeTools-expect-fun case 3: CHECK(lhs->value().view() == "w"sv); break; } } else { - auto* value = dryad::node_cast(statement); - auto* flat_value = dryad::node_cast(value->value()); + auto* value = dryad::node_cast(statement); + auto* flat_value = dryad::node_cast(value->value()); sv = flat_value->value().view(); } @@ -720,16 +720,16 @@ TEST_CASE("NodeTools expect vector functions", "[NodeTools][NodeTools-expect-fun }; static const std::array xy = std::array { "x"sv, "y"sv }; - ListValue* _0_id_2_list = ast.create_assign_list(xy, _0_id_assign); - ListValue* _0_str_2_list = ast.create_assign_list(xy, _0_str_assign); - ListValue* _0_5_id_2_list = ast.create_assign_list(xy, _0_5_id_assign); - ListValue* _0_5_str_2_list = ast.create_assign_list(xy, _0_5_str_assign); - ListValue* _1_id_2_list = ast.create_assign_list(xy, _1_id_assign); - ListValue* _1_str_2_list = ast.create_assign_list(xy, _1_str_assign); - ListValue* _127_id_2_list = ast.create_assign_list(xy, _127_id_assign); - ListValue* _127_str_2_list = ast.create_assign_list(xy, _127_str_assign); - ListValue* _255_id_2_list = ast.create_assign_list(xy, _255_id_assign); - ListValue* _255_str_2_list = ast.create_assign_list(xy, _255_str_assign); + ovdl::v2script::ast::ListValue* _0_id_2_list = ast.create_assign_list(xy, _0_id_assign); + ovdl::v2script::ast::ListValue* _0_str_2_list = ast.create_assign_list(xy, _0_str_assign); + ovdl::v2script::ast::ListValue* _0_5_id_2_list = ast.create_assign_list(xy, _0_5_id_assign); + ovdl::v2script::ast::ListValue* _0_5_str_2_list = ast.create_assign_list(xy, _0_5_str_assign); + ovdl::v2script::ast::ListValue* _1_id_2_list = ast.create_assign_list(xy, _1_id_assign); + ovdl::v2script::ast::ListValue* _1_str_2_list = ast.create_assign_list(xy, _1_str_assign); + ovdl::v2script::ast::ListValue* _127_id_2_list = ast.create_assign_list(xy, _127_id_assign); + ovdl::v2script::ast::ListValue* _127_str_2_list = ast.create_assign_list(xy, _127_str_assign); + ovdl::v2script::ast::ListValue* _255_id_2_list = ast.create_assign_list(xy, _255_id_assign); + ovdl::v2script::ast::ListValue* _255_str_2_list = ast.create_assign_list(xy, _255_str_assign); CHECK(NodeTools::expect_ivec2(std::bind_front(callback, _0_id_2_list))(_0_id_2_list)); CHECK(NodeTools::expect_ivec2(std::bind_front(callback, _0_5_id_2_list))(_0_5_id_2_list)); @@ -756,26 +756,26 @@ TEST_CASE("NodeTools expect vector functions", "[NodeTools][NodeTools-expect-fun CHECK_FALSE(NodeTools::expect_fvec2(std::bind_front(callback, _255_str_2_list))(_255_str_2_list)); auto _0_id = ast.create_value_statement_factory("0"sv); - auto _0_str = ast.create_value_statement_factory("0"sv); + auto _0_str = ast.create_value_statement_factory("0"sv); auto _0_5_id = ast.create_value_statement_factory("0.5"sv); - auto _0_5_str = ast.create_value_statement_factory("0.5"sv); + auto _0_5_str = ast.create_value_statement_factory("0.5"sv); auto _1_id = ast.create_value_statement_factory("1"sv); - auto _1_str = ast.create_value_statement_factory("1"sv); + auto _1_str = ast.create_value_statement_factory("1"sv); auto _127_id = ast.create_value_statement_factory("127"sv); - auto _127_str = ast.create_value_statement_factory("127"sv); + auto _127_str = ast.create_value_statement_factory("127"sv); auto _255_id = ast.create_value_statement_factory("255"sv); - auto _255_str = ast.create_value_statement_factory("255"sv); - - ListValue* _0_id_3_list = ast.create_value_list_fill(3, _0_id); - ListValue* _0_str_3_list = ast.create_value_list_fill(3, _0_str); - ListValue* _0_5_id_3_list = ast.create_value_list_fill(3, _0_5_id); - ListValue* _0_5_str_3_list = ast.create_value_list_fill(3, _0_5_str); - ListValue* _1_id_3_list = ast.create_value_list_fill(3, _1_id); - ListValue* _1_str_3_list = ast.create_value_list_fill(3, _1_str); - ListValue* _127_id_3_list = ast.create_value_list_fill(3, _127_id); - ListValue* _127_str_3_list = ast.create_value_list_fill(3, _127_str); - ListValue* _255_id_3_list = ast.create_value_list_fill(3, _255_id); - ListValue* _255_str_3_list = ast.create_value_list_fill(3, _255_str); + auto _255_str = ast.create_value_statement_factory("255"sv); + + ovdl::v2script::ast::ListValue* _0_id_3_list = ast.create_value_list_fill(3, _0_id); + ovdl::v2script::ast::ListValue* _0_str_3_list = ast.create_value_list_fill(3, _0_str); + ovdl::v2script::ast::ListValue* _0_5_id_3_list = ast.create_value_list_fill(3, _0_5_id); + ovdl::v2script::ast::ListValue* _0_5_str_3_list = ast.create_value_list_fill(3, _0_5_str); + ovdl::v2script::ast::ListValue* _1_id_3_list = ast.create_value_list_fill(3, _1_id); + ovdl::v2script::ast::ListValue* _1_str_3_list = ast.create_value_list_fill(3, _1_str); + ovdl::v2script::ast::ListValue* _127_id_3_list = ast.create_value_list_fill(3, _127_id); + ovdl::v2script::ast::ListValue* _127_str_3_list = ast.create_value_list_fill(3, _127_str); + ovdl::v2script::ast::ListValue* _255_id_3_list = ast.create_value_list_fill(3, _255_id); + ovdl::v2script::ast::ListValue* _255_str_3_list = ast.create_value_list_fill(3, _255_str); CHECK(NodeTools::expect_fvec3(std::bind_front(callback, _0_id_3_list))(_0_id_3_list)); CHECK(NodeTools::expect_fvec3(std::bind_front(callback, _0_5_id_3_list))(_0_5_id_3_list)); @@ -789,16 +789,16 @@ TEST_CASE("NodeTools expect vector functions", "[NodeTools][NodeTools-expect-fun CHECK_FALSE(NodeTools::expect_fvec3(std::bind_front(callback, _127_str_3_list))(_127_str_3_list)); CHECK_FALSE(NodeTools::expect_fvec3(std::bind_front(callback, _255_str_3_list))(_255_str_3_list)); - ListValue* _0_id_4_list = ast.create_value_list_fill(4, _0_id); - ListValue* _0_str_4_list = ast.create_value_list_fill(4, _0_str); - ListValue* _0_5_id_4_list = ast.create_value_list_fill(4, _0_5_id); - ListValue* _0_5_str_4_list = ast.create_value_list_fill(4, _0_5_str); - ListValue* _1_id_4_list = ast.create_value_list_fill(4, _1_id); - ListValue* _1_str_4_list = ast.create_value_list_fill(4, _1_str); - ListValue* _127_id_4_list = ast.create_value_list_fill(4, _127_id); - ListValue* _127_str_4_list = ast.create_value_list_fill(4, _127_str); - ListValue* _255_id_4_list = ast.create_value_list_fill(4, _255_id); - ListValue* _255_str_4_list = ast.create_value_list_fill(4, _255_str); + ovdl::v2script::ast::ListValue* _0_id_4_list = ast.create_value_list_fill(4, _0_id); + ovdl::v2script::ast::ListValue* _0_str_4_list = ast.create_value_list_fill(4, _0_str); + ovdl::v2script::ast::ListValue* _0_5_id_4_list = ast.create_value_list_fill(4, _0_5_id); + ovdl::v2script::ast::ListValue* _0_5_str_4_list = ast.create_value_list_fill(4, _0_5_str); + ovdl::v2script::ast::ListValue* _1_id_4_list = ast.create_value_list_fill(4, _1_id); + ovdl::v2script::ast::ListValue* _1_str_4_list = ast.create_value_list_fill(4, _1_str); + ovdl::v2script::ast::ListValue* _127_id_4_list = ast.create_value_list_fill(4, _127_id); + ovdl::v2script::ast::ListValue* _127_str_4_list = ast.create_value_list_fill(4, _127_str); + ovdl::v2script::ast::ListValue* _255_id_4_list = ast.create_value_list_fill(4, _255_id); + ovdl::v2script::ast::ListValue* _255_str_4_list = ast.create_value_list_fill(4, _255_str); CHECK(NodeTools::expect_fvec4(std::bind_front(callback, _0_id_4_list))(_0_id_4_list)); CHECK(NodeTools::expect_fvec4(std::bind_front(callback, _0_5_id_4_list))(_0_5_id_4_list)); @@ -817,13 +817,13 @@ TEST_CASE("NodeTools expect assign functions", "[NodeTools][NodeTools-expect-fun Ast ast; auto* id = ast.create_assign_statement_rhs_factory("right"sv)("left"sv); - auto* str = ast.create_assign_statement_rhs_factory("right"sv)("left"sv); + auto* str = ast.create_assign_statement_rhs_factory("right"sv)("left"sv); - auto callback = [](AssignStatement const* ptr, std::string_view lhs, NodeCPtr rhs) -> bool { - auto* ptr_lhs = dryad::node_cast(ptr->left()); - auto* ptr_rhs = dryad::node_cast(ptr->right()); + auto callback = [](ovdl::v2script::ast::AssignStatement const* ptr, std::string_view lhs, ovdl::v2script::ast::Node const* rhs) -> bool { + auto* ptr_lhs = dryad::node_cast(ptr->left()); + auto* ptr_rhs = dryad::node_cast(ptr->right()); - auto* value = dryad::node_cast(rhs); + auto* value = dryad::node_cast(rhs); CHECK_IF(ptr_lhs->value().view() == lhs) { CHECK_IF(ptr_rhs->value().view() == value->value().view()) { @@ -845,7 +845,7 @@ TEST_CASE("NodeTools expect list functions", "[NodeTools][NodeTools-expect-funct Ast ast; auto id_value = ast.create_value_statement_factory(); - auto str_value = ast.create_value_statement_factory(); + auto str_value = ast.create_value_statement_factory(); auto* _0_id_list = ast.create_assign_list(0, id_value); auto* _0_str_list = ast.create_assign_list(0, str_value); @@ -865,7 +865,7 @@ TEST_CASE("NodeTools expect list functions", "[NodeTools][NodeTools-expect-funct return false; }; - static auto callback = [](ListValue const* ptr, size_t count, NodeCPtr val) -> bool { + static auto callback = [](ovdl::v2script::ast::ListValue const* ptr, size_t count, ovdl::v2script::ast::Node const* val) -> bool { auto list = ptr->statements(); CHECK_IF(ranges::distance(list) == count); else { @@ -873,11 +873,11 @@ TEST_CASE("NodeTools expect list functions", "[NodeTools][NodeTools-expect-funct } for (auto [index, statement] : list | ranges::views::enumerate) { - auto* check_value = dryad::node_cast(statement); + auto* check_value = dryad::node_cast(statement); - auto* flat_value = dryad::node_cast(val); + auto* flat_value = dryad::node_cast(val); - auto* check_flat_value = dryad::node_cast(check_value->value()); + auto* check_flat_value = dryad::node_cast(check_value->value()); std::string_view check = check_flat_value->value().view(); CAPTURE(index); @@ -968,7 +968,7 @@ TEST_CASE("NodeTools expect length functions", "[NodeTools][NodeTools-expect-fun Ast ast; auto id_value = ast.create_value_statement_factory(); - auto str_value = ast.create_value_statement_factory(); + auto str_value = ast.create_value_statement_factory(); auto* _0_id_list = ast.create_assign_list(0, id_value); auto* _0_str_list = ast.create_assign_list(0, str_value); @@ -981,7 +981,7 @@ TEST_CASE("NodeTools expect length functions", "[NodeTools][NodeTools-expect-fun auto* _24_id_list = ast.create_assign_list(24, id_value); auto* _24_str_list = ast.create_assign_list(24, str_value); - static auto callback = [](ListValue const* ptr, size_t count, size_t val) -> bool { + static auto callback = [](ovdl::v2script::ast::ListValue const* ptr, size_t count, size_t val) -> bool { auto list = ptr->statements(); CHECK_IF(ranges::distance(list) == count); else { @@ -1002,7 +1002,7 @@ TEST_CASE("NodeTools expect length functions", "[NodeTools][NodeTools-expect-fun CHECK(NodeTools::expect_length(std::bind_front(callback, _24_id_list, 24))(_24_id_list)); CHECK(NodeTools::expect_length(std::bind_front(callback, _24_str_list, 24))(_24_str_list)); - static auto callback_false = [](ListValue const* ptr, size_t count, size_t val) -> bool { + static auto callback_false = [](ovdl::v2script::ast::ListValue const* ptr, size_t count, size_t val) -> bool { auto list = ptr->statements(); CHECK_FALSE_IF(ranges::distance(list) == count); else { @@ -1032,18 +1032,18 @@ TEST_CASE("NodeTools expect key functions", "[NodeTools][NodeTools-expect-functi Ast ast; auto _0_id_assign = ast.create_assign_statement_rhs_factory("0"sv); - auto _0_str_assign = ast.create_assign_statement_rhs_factory("0"sv); + auto _0_str_assign = ast.create_assign_statement_rhs_factory("0"sv); auto _0_5_id_assign = ast.create_assign_statement_rhs_factory("0.5"sv); - auto _0_5_str_assign = ast.create_assign_statement_rhs_factory("0.5"sv); + auto _0_5_str_assign = ast.create_assign_statement_rhs_factory("0.5"sv); auto _1_id_assign = ast.create_assign_statement_rhs_factory("1"sv); - auto _1_str_assign = ast.create_assign_statement_rhs_factory("1"sv); + auto _1_str_assign = ast.create_assign_statement_rhs_factory("1"sv); auto _127_id_assign = ast.create_assign_statement_rhs_factory("127"sv); - auto _127_str_assign = ast.create_assign_statement_rhs_factory("127"sv); + auto _127_str_assign = ast.create_assign_statement_rhs_factory("127"sv); auto _255_id_assign = ast.create_assign_statement_rhs_factory("255"sv); - auto _255_str_assign = ast.create_assign_statement_rhs_factory("255"sv); + auto _255_str_assign = ast.create_assign_statement_rhs_factory("255"sv); - static auto callback = [](ListValue const* ptr, NodeCPtr val) -> bool { - auto* flat_val = dryad::node_cast(val); + static auto callback = [](ovdl::v2script::ast::ListValue const* ptr, ovdl::v2script::ast::Node const* val) -> bool { + auto* flat_val = dryad::node_cast(val); std::string_view val_sv = flat_val->value().view(); CAPTURE(val_sv); @@ -1051,9 +1051,9 @@ TEST_CASE("NodeTools expect key functions", "[NodeTools][NodeTools-expect-functi bool callback_return = false; for (auto [index, statement] : list | ranges::views::enumerate) { - auto* assign = dryad::node_cast(statement); - auto* lhs = dryad::node_cast(assign->left()); - auto* rhs = dryad::node_cast(assign->right()); + auto* assign = dryad::node_cast(statement); + auto* lhs = dryad::node_cast(assign->left()); + auto* rhs = dryad::node_cast(assign->right()); if (flat_val != rhs) { continue; @@ -1078,16 +1078,16 @@ TEST_CASE("NodeTools expect key functions", "[NodeTools][NodeTools-expect-functi CHECK_RETURN_BOOL(callback_return); }; - ListValue* _0_id_list = ast.create_assign_list(8, _0_id_assign); - ListValue* _0_str_list = ast.create_assign_list(8, _0_str_assign); - ListValue* _0_5_id_list = ast.create_assign_list(7, _0_5_id_assign); - ListValue* _0_5_str_list = ast.create_assign_list(7, _0_5_str_assign); - ListValue* _1_id_list = ast.create_assign_list(6, _1_id_assign); - ListValue* _1_str_list = ast.create_assign_list(6, _1_str_assign); - ListValue* _127_id_list = ast.create_assign_list(5, _127_id_assign); - ListValue* _127_str_list = ast.create_assign_list(5, _127_str_assign); - ListValue* _255_id_list = ast.create_assign_list(4, _255_id_assign); - ListValue* _255_str_list = ast.create_assign_list(4, _255_str_assign); + ovdl::v2script::ast::ListValue* _0_id_list = ast.create_assign_list(8, _0_id_assign); + ovdl::v2script::ast::ListValue* _0_str_list = ast.create_assign_list(8, _0_str_assign); + ovdl::v2script::ast::ListValue* _0_5_id_list = ast.create_assign_list(7, _0_5_id_assign); + ovdl::v2script::ast::ListValue* _0_5_str_list = ast.create_assign_list(7, _0_5_str_assign); + ovdl::v2script::ast::ListValue* _1_id_list = ast.create_assign_list(6, _1_id_assign); + ovdl::v2script::ast::ListValue* _1_str_list = ast.create_assign_list(6, _1_str_assign); + ovdl::v2script::ast::ListValue* _127_id_list = ast.create_assign_list(5, _127_id_assign); + ovdl::v2script::ast::ListValue* _127_str_list = ast.create_assign_list(5, _127_str_assign); + ovdl::v2script::ast::ListValue* _255_id_list = ast.create_assign_list(4, _255_id_assign); + ovdl::v2script::ast::ListValue* _255_str_list = ast.create_assign_list(4, _255_str_assign); ovdl::symbol _0 = ast.symbol_interner.find_intern("0"); ovdl::symbol _1 = ast.symbol_interner.find_intern("1"); @@ -1166,21 +1166,21 @@ TEST_CASE("NodeTools expect key functions", "[NodeTools][NodeTools-expect-functi CHECK(NodeTools::expect_key(_0.view(), std::bind_front(callback, _255_id_list), nullptr, true)(_255_id_list)); CHECK(NodeTools::expect_key(_0.view(), std::bind_front(callback, _255_str_list), nullptr, true)(_255_str_list)); - static auto callback_dup = [](ListValue const* ptr, NodeCPtr val) -> bool { - auto* flat_val = dryad::node_cast(val); + static auto callback_dup = [](ovdl::v2script::ast::ListValue const* ptr, ovdl::v2script::ast::Node const* val) -> bool { + auto* flat_val = dryad::node_cast(val); std::string_view val_sv = flat_val->value().view(); CAPTURE(val_sv); auto list = ptr->statements(); - static auto transformer = [](ranges::common_pair pair) -> decltype(pair) { + static auto transformer = [](ranges::common_pair pair) -> decltype(pair) { pair.first /= 2; return pair; }; for (auto [index, statement] : list | ranges::views::enumerate | ranges::views::transform(transformer)) { - auto* assign = dryad::node_cast(statement); - auto* lhs = dryad::node_cast(assign->left()); - auto* rhs = dryad::node_cast(assign->right()); + auto* assign = dryad::node_cast(statement); + auto* lhs = dryad::node_cast(assign->left()); + auto* rhs = dryad::node_cast(assign->right()); if (flat_val != rhs) { continue; @@ -1205,15 +1205,15 @@ TEST_CASE("NodeTools expect key functions", "[NodeTools][NodeTools-expect-functi CHECK_RETURN_BOOL(false); }; - auto build_list_dup = [&ast](size_t count, auto&& factory) -> ListValue* { - StatementList slist; + auto build_list_dup = [&ast](size_t count, auto&& factory) -> ovdl::v2script::ast::ListValue* { + ovdl::v2script::ast::StatementList slist; for (size_t index : ranges::views::iota(static_cast(0), count)) { std::to_chars_result result = std::to_chars(hex_array.data(), hex_array.data() + hex_array.size(), index); CHECK_OR_CONTINUE(result.ec == std::errc {}); slist.push_back(factory(std::string_view { hex_array.data(), result.ptr })); slist.push_back(factory(std::string_view { hex_array.data(), result.ptr })); } - return ast.create(slist); + return ast.create(slist); }; _0_id_list = build_list_dup(12, _0_id_assign); @@ -1283,28 +1283,28 @@ TEST_CASE( Ast ast; auto _0_id_assign = ast.create_assign_statement_rhs_factory("0"sv); - auto _0_str_assign = ast.create_assign_statement_rhs_factory("0"sv); + auto _0_str_assign = ast.create_assign_statement_rhs_factory("0"sv); auto _0_5_id_assign = ast.create_assign_statement_rhs_factory("0.5"sv); - auto _0_5_str_assign = ast.create_assign_statement_rhs_factory("0.5"sv); + auto _0_5_str_assign = ast.create_assign_statement_rhs_factory("0.5"sv); auto _1_id_assign = ast.create_assign_statement_rhs_factory("1"sv); - auto _1_str_assign = ast.create_assign_statement_rhs_factory("1"sv); + auto _1_str_assign = ast.create_assign_statement_rhs_factory("1"sv); auto _127_id_assign = ast.create_assign_statement_rhs_factory("127"sv); - auto _127_str_assign = ast.create_assign_statement_rhs_factory("127"sv); + auto _127_str_assign = ast.create_assign_statement_rhs_factory("127"sv); auto _255_id_assign = ast.create_assign_statement_rhs_factory("255"sv); - auto _255_str_assign = ast.create_assign_statement_rhs_factory("255"sv); + auto _255_str_assign = ast.create_assign_statement_rhs_factory("255"sv); - static auto callback = [](ListValue const* ptr, std::string_view key, NodeCPtr val) -> bool { + static auto callback = [](ovdl::v2script::ast::ListValue const* ptr, std::string_view key, ovdl::v2script::ast::Node const* val) -> bool { CAPTURE(key); - auto* flat_val = dryad::node_cast(val); + auto* flat_val = dryad::node_cast(val); std::string_view val_sv = flat_val->value().view(); CAPTURE(val_sv); auto list = ptr->statements(); for (auto [index, statement] : list | ranges::views::enumerate) { - auto* assign = dryad::node_cast(statement); - auto* lhs = dryad::node_cast(assign->left()); - auto* rhs = dryad::node_cast(assign->right()); + auto* assign = dryad::node_cast(statement); + auto* lhs = dryad::node_cast(assign->left()); + auto* rhs = dryad::node_cast(assign->right()); if (flat_val != rhs) { continue; @@ -1330,16 +1330,16 @@ TEST_CASE( CHECK_RETURN_BOOL(false); }; - ListValue* _0_id_list = ast.create_assign_list(8, _0_id_assign); - ListValue* _0_str_list = ast.create_assign_list(8, _0_str_assign); - ListValue* _0_5_id_list = ast.create_assign_list(7, _0_5_id_assign); - ListValue* _0_5_str_list = ast.create_assign_list(7, _0_5_str_assign); - ListValue* _1_id_list = ast.create_assign_list(6, _1_id_assign); - ListValue* _1_str_list = ast.create_assign_list(6, _1_str_assign); - ListValue* _127_id_list = ast.create_assign_list(5, _127_id_assign); - ListValue* _127_str_list = ast.create_assign_list(5, _127_str_assign); - ListValue* _255_id_list = ast.create_assign_list(4, _255_id_assign); - ListValue* _255_str_list = ast.create_assign_list(4, _255_str_assign); + ovdl::v2script::ast::ListValue* _0_id_list = ast.create_assign_list(8, _0_id_assign); + ovdl::v2script::ast::ListValue* _0_str_list = ast.create_assign_list(8, _0_str_assign); + ovdl::v2script::ast::ListValue* _0_5_id_list = ast.create_assign_list(7, _0_5_id_assign); + ovdl::v2script::ast::ListValue* _0_5_str_list = ast.create_assign_list(7, _0_5_str_assign); + ovdl::v2script::ast::ListValue* _1_id_list = ast.create_assign_list(6, _1_id_assign); + ovdl::v2script::ast::ListValue* _1_str_list = ast.create_assign_list(6, _1_str_assign); + ovdl::v2script::ast::ListValue* _127_id_list = ast.create_assign_list(5, _127_id_assign); + ovdl::v2script::ast::ListValue* _127_str_list = ast.create_assign_list(5, _127_str_assign); + ovdl::v2script::ast::ListValue* _255_id_list = ast.create_assign_list(4, _255_id_assign); + ovdl::v2script::ast::ListValue* _255_str_list = ast.create_assign_list(4, _255_str_assign); CHECK(NodeTools::expect_dictionary(std::bind_front(callback, _0_id_list))(_0_id_list)); CHECK(NodeTools::expect_dictionary(std::bind_front(callback, _0_str_list))(_0_str_list)); @@ -1352,7 +1352,7 @@ TEST_CASE( CHECK(NodeTools::expect_dictionary(std::bind_front(callback, _255_id_list))(_255_id_list)); CHECK(NodeTools::expect_dictionary(std::bind_front(callback, _255_str_list))(_255_str_list)); - static auto length_callback = [](ListValue const* ptr, size_t length) -> size_t { + static auto length_callback = [](ovdl::v2script::ast::ListValue const* ptr, size_t length) -> size_t { auto list = ptr->statements(); CHECK(ranges::distance(list) == length); return length; diff --git a/tests/src/pathfinding/PointMap.cpp b/tests/src/pathfinding/PointMap.cpp index 21951a1fb..99ce148a1 100644 --- a/tests/src/pathfinding/PointMap.cpp +++ b/tests/src/pathfinding/PointMap.cpp @@ -74,8 +74,9 @@ TEST_CASE("PointMap Add/Remove", "[point-map][point-map-add-remove]") { // Tests for get_closest_position_in_segment. a.connect_points(2, 3); CHECK( - a.get_closest_position_in_segment(fvec2_t { fixed_point_t::_0_50, fixed_point_t::_0_50 }) == - fvec2_t { fixed_point_t::_0_50, 1 } + a.get_closest_position_in_segment( + fvec2_t { fixed_point_t::_0_50, fixed_point_t::_0_50 } + ) == fvec2_t { fixed_point_t::_0_50, 1 } ); a.connect_points(3, 4); diff --git a/tests/src/types/FixedPoint.cpp b/tests/src/types/FixedPoint.cpp index efbbab8ba..c58e07146 100644 --- a/tests/src/types/FixedPoint.cpp +++ b/tests/src/types/FixedPoint.cpp @@ -141,13 +141,19 @@ TEST_CASE("fixed_point_t Parse methods", "[fixed_point_t][fixed_point_t-parse]") static constexpr std::string_view plus_fixed_point_str = "+4.5432"sv; fixed_point_t fp = fixed_point_t::_0; CHECK( - fp::from_chars(fp, plus_fixed_point_str.data(), plus_fixed_point_str.data() + plus_fixed_point_str.size()).ec == - std::errc::invalid_argument + fp::from_chars( + fp, + plus_fixed_point_str.data(), + plus_fixed_point_str.data() + plus_fixed_point_str.size() + ).ec == std::errc::invalid_argument ); CHECK(fp == 0.0_a); CHECK( - fp::from_chars_with_plus(fp, plus_fixed_point_str.data(), plus_fixed_point_str.data() + plus_fixed_point_str.size()).ec == - std::errc {} + fp::from_chars_with_plus( + fp, + plus_fixed_point_str.data(), + plus_fixed_point_str.data() + plus_fixed_point_str.size() + ).ec == std::errc {} ); CHECK(fp == 4.5432_a); } diff --git a/tests/src/types/Signal.cpp b/tests/src/types/Signal.cpp index cdeb6f274..8a8d31e38 100644 --- a/tests/src/types/Signal.cpp +++ b/tests/src/types/Signal.cpp @@ -16,8 +16,11 @@ using namespace OpenVic; using namespace std::string_view_literals; -using SignalTypes = - snitch::type_list, nothread::signal, basic_signal>; +using SignalTypes = snitch::type_list< + OpenVic::signal, + nothread::signal, + basic_signal +>; void test_func(int& sum, int i) { sum += i; diff --git a/tests/src/types/Vector2.cpp b/tests/src/types/Vector2.cpp index 727f51844..28a23ae09 100644 --- a/tests/src/types/Vector2.cpp +++ b/tests/src/types/Vector2.cpp @@ -143,8 +143,7 @@ TEST_CASE("fvec2_t Operators", "[vec2_t][fvec2_t][fvec2_t-operators]") { CONSTEXPR_CHECK(power1 + power2 == testing::approx_vec2 { 1.25, 1.625 }); CONSTEXPR_CHECK( - decimal1 - decimal2 == - testing::approx_vec2 { + decimal1 - decimal2 == testing::approx_vec2 { (1.1_a).epsilon(testing::INACCURATE_EPSILON), // (1.5_a).epsilon(testing::INACCURATE_EPSILON) // } @@ -152,8 +151,7 @@ TEST_CASE("fvec2_t Operators", "[vec2_t][fvec2_t][fvec2_t-operators]") { CONSTEXPR_CHECK(power1 - power2 == testing::approx_vec2 { 0.25, 1.375 }); CONSTEXPR_CHECK( - decimal1 * decimal2 == - testing::approx_vec2 { + decimal1 * decimal2 == testing::approx_vec2 { (2.76_a).epsilon(testing::INACCURATE_EPSILON), // (16.66_a).epsilon(testing::INACCURATE_EPSILON) // } diff --git a/tests/src/types/Vector3.cpp b/tests/src/types/Vector3.cpp index b6c4c1d9f..2b0af8e5b 100644 --- a/tests/src/types/Vector3.cpp +++ b/tests/src/types/Vector3.cpp @@ -117,9 +117,12 @@ TEST_CASE("dvec3_t Length methods", "[vec3_t][dvec3_t][dvec3_t-length]") { TEST_CASE("fvec3_t Operators", "[vec3_t][fvec3_t][fvec3_t-operators]") { static constexpr fixed_point_t _2_30 = fixed_point_t::_2 + fixed_point_t::_0_20 + fixed_point_t::_0_10; static constexpr fixed_point_t _4_90 = fixed_point_t::_4 + fixed_point_t::_0_50 + fixed_point_t::_0_20 * 2; - static constexpr fixed_point_t _7_80 = // - fixed_point_t::_4 + fixed_point_t::_2 + fixed_point_t::_1 + fixed_point_t::_0_50 + fixed_point_t::_0_20 + - fixed_point_t::_0_10; + static constexpr fixed_point_t _7_80 = fixed_point_t::_4 + + fixed_point_t::_2 + + fixed_point_t::_1 + + fixed_point_t::_0_50 + + fixed_point_t::_0_20 + + fixed_point_t::_0_10; static constexpr fixed_point_t _1_20 = fixed_point_t::_0_20 * 6; static constexpr fixed_point_t _3_40 = fixed_point_t::_0_20 * 17; static constexpr fixed_point_t _5_60 = fixed_point_t::_4 + fixed_point_t::_1_50 + fixed_point_t::_0_10; @@ -138,8 +141,7 @@ TEST_CASE("fvec3_t Operators", "[vec3_t][fvec3_t][fvec3_t-operators]") { CONSTEXPR_CHECK(power1 + power2 == testing::approx_vec3(1.25, 1.625, 0.875)); CONSTEXPR_CHECK( - decimal1 - decimal2 == - testing::approx_vec3 { + decimal1 - decimal2 == testing::approx_vec3 { (1.1_a).epsilon(testing::INACCURATE_EPSILON), // (1.5_a).epsilon(testing::INACCURATE_EPSILON), // (2.2_a).epsilon(testing::INACCURATE_EPSILON) // @@ -148,8 +150,7 @@ TEST_CASE("fvec3_t Operators", "[vec3_t][fvec3_t][fvec3_t-operators]") { CONSTEXPR_CHECK(power1 - power2 == testing::approx_vec3(0.25, 1.375, 0.375)); CONSTEXPR_CHECK( - decimal1 * decimal2 == - testing::approx_vec3 { + decimal1 * decimal2 == testing::approx_vec3 { (2.76_a).epsilon(testing::INACCURATE_EPSILON), // (16.66_a).epsilon(testing::INACCURATE_EPSILON), // (43.68_a).epsilon(testing::INACCURATE_EPSILON) // diff --git a/tests/src/types/Vector4.cpp b/tests/src/types/Vector4.cpp index e5715d87f..00bf38a33 100644 --- a/tests/src/types/Vector4.cpp +++ b/tests/src/types/Vector4.cpp @@ -116,9 +116,12 @@ TEST_CASE("dvec4_t Length methods", "[vec4_t][dvec4_t][dvec4_t-length]") { TEST_CASE("fvec4_t Operators", "[vec4_t][fvec4_t][fvec4_t-operators]") { static constexpr fixed_point_t _2_30 = fixed_point_t::_2 + fixed_point_t::_0_20 + fixed_point_t::_0_10; static constexpr fixed_point_t _4_90 = fixed_point_t::_4 + fixed_point_t::_0_50 + fixed_point_t::_0_20 * 2; - static constexpr fixed_point_t _7_80 = // - fixed_point_t::_4 + fixed_point_t::_2 + fixed_point_t::_1 + fixed_point_t::_0_50 + fixed_point_t::_0_20 + - fixed_point_t::_0_10; + static constexpr fixed_point_t _7_80 = fixed_point_t::_4 + + fixed_point_t::_2 + + fixed_point_t::_1 + + fixed_point_t::_0_50 + + fixed_point_t::_0_20 + + fixed_point_t::_0_10; static constexpr fixed_point_t _3_20 = fixed_point_t::_2 + fixed_point_t::_1 + fixed_point_t::_0_20; static constexpr fixed_point_t _1_20 = fixed_point_t::_0_20 * 6; static constexpr fixed_point_t _3_40 = fixed_point_t::_0_20 * 17; @@ -139,8 +142,7 @@ TEST_CASE("fvec4_t Operators", "[vec4_t][fvec4_t][fvec4_t-operators]") { CONSTEXPR_CHECK(power1 + power2 == testing::approx_vec4(1.25, 1.625, 0.875, 0.875)); CONSTEXPR_CHECK( - decimal1 - decimal2 == - testing::approx_vec4 { + decimal1 - decimal2 == testing::approx_vec4 { (1.1_a).epsilon(testing::INACCURATE_EPSILON), // (1.5_a).epsilon(testing::INACCURATE_EPSILON), // (2.2_a).epsilon(testing::INACCURATE_EPSILON), // @@ -151,8 +153,7 @@ TEST_CASE("fvec4_t Operators", "[vec4_t][fvec4_t][fvec4_t-operators]") { CONSTEXPR_CHECK(power1 - power2 == testing::approx_vec4(0.25, 1.375, 0.375, -0.625)); CONSTEXPR_CHECK( - decimal1 * decimal2 == - testing::approx_vec4 { + decimal1 * decimal2 == testing::approx_vec4 { (2.76_a).epsilon(testing::INACCURATE_EPSILON), // (16.66_a).epsilon(testing::INACCURATE_EPSILON), // (43.68_a).epsilon(testing::INACCURATE_EPSILON), // @@ -163,8 +164,12 @@ TEST_CASE("fvec4_t Operators", "[vec4_t][fvec4_t][fvec4_t-operators]") { CONSTEXPR_CHECK(int1 / int2 == testing::approx_vec4(4, 2.5, 3, 2)); CONSTEXPR_CHECK( - decimal1 / decimal2 == - testing::approx_vec4(1.91666666666666666, 1.44117647058823529, 1.39285714285714286, 1.88235294118) + decimal1 / decimal2 == testing::approx_vec4( + 1.91666666666666666, + 1.44117647058823529, + 1.39285714285714286, + 1.88235294118 + ) ); CONSTEXPR_CHECK(power1 / power2 == testing::approx_vec4(1.5, 12.0, 2.5, 1.0 / 6.0));