diff options
Diffstat (limited to 'src/openvic-simulation/modifier/Modifier.hpp')
-rw-r--r-- | src/openvic-simulation/modifier/Modifier.hpp | 225 |
1 files changed, 225 insertions, 0 deletions
diff --git a/src/openvic-simulation/modifier/Modifier.hpp b/src/openvic-simulation/modifier/Modifier.hpp new file mode 100644 index 0000000..f525d6a --- /dev/null +++ b/src/openvic-simulation/modifier/Modifier.hpp @@ -0,0 +1,225 @@ +#pragma once + +#include "openvic-simulation/scripts/ConditionScript.hpp" +#include "openvic-simulation/types/IdentifierRegistry.hpp" + +namespace OpenVic { + struct ModifierManager; + + struct ModifierEffect : HasIdentifier { + friend struct ModifierManager; + + enum class format_t { + PROPORTION_DECIMAL, /* An unscaled fraction/ratio, with 1 being "full"/"whole" */ + PERCENTAGE_DECIMAL, /* A fraction/ratio scaled so that 100 is "full"/"whole" */ + RAW_DECIMAL, /* A continuous quantity, e.g. attack strength */ + INT /* A discrete quantity, e.g. building count limit */ + }; + + private: + /* If true, positive values will be green and negative values will be red. + * If false, the colours will be switced. + */ + const bool PROPERTY_CUSTOM_PREFIX(positive_good, is); + const format_t PROPERTY(format); + std::string PROPERTY(localisation_key); + + // TODO - format/precision, e.g. 80% vs 0.8 vs 0.800, 2 vs 2.0 vs 200% + + ModifierEffect( + std::string_view new_identifier, bool new_positive_good, format_t new_format, std::string_view new_localisation_key + ); + + public: + ModifierEffect(ModifierEffect&&) = default; + }; + + struct ModifierValue { + friend struct ModifierManager; + + using effect_map_t = fixed_point_map_t<ModifierEffect const*>; + + private: + effect_map_t PROPERTY(values); + + public: + ModifierValue(); + ModifierValue(effect_map_t&& new_values); + ModifierValue(ModifierValue const&); + ModifierValue(ModifierValue&&); + + ModifierValue& operator=(ModifierValue const&); + ModifierValue& operator=(ModifierValue&&); + + /* Removes effect entries with a value of zero. */ + void trim(); + size_t get_effect_count() const; + void clear(); + bool empty() const; + + fixed_point_t get_effect(ModifierEffect const& effect, bool* effect_found = nullptr) const; + bool has_effect(ModifierEffect const& effect) const; + void set_effect(ModifierEffect const& effect, fixed_point_t value); + + ModifierValue& operator+=(ModifierValue const& right); + ModifierValue operator+(ModifierValue const& right) const; + ModifierValue operator-() const; + ModifierValue& operator-=(ModifierValue const& right); + ModifierValue operator-(ModifierValue const& right) const; + ModifierValue& operator*=(fixed_point_t const& right); + ModifierValue operator*(fixed_point_t const& right) const; + + fixed_point_t& operator[](ModifierEffect const& effect); + + void multiply_add(ModifierValue const& other, fixed_point_t multiplier); + + friend std::ostream& operator<<(std::ostream& stream, ModifierValue const& value); + }; + + struct Modifier : HasIdentifier, ModifierValue { + friend struct ModifierManager; + + using icon_t = uint8_t; + + private: + /* A modifier can have no icon (zero). */ + const icon_t PROPERTY(icon); + + protected: + Modifier(std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon = 0); + + public: + Modifier(Modifier&&) = default; + }; + + struct TriggeredModifier : Modifier { + friend struct ModifierManager; + + private: + ConditionScript trigger; + + protected: + TriggeredModifier( + std::string_view new_identifier, ModifierValue&& new_values, icon_t new_icon, ConditionScript&& new_trigger + ); + + bool parse_scripts(DefinitionManager const& definition_manager); + + public: + TriggeredModifier(TriggeredModifier&&) = default; + }; + + struct ModifierInstance { + + private: + Modifier const* PROPERTY(modifier); // We can assume this is never null + Date PROPERTY(expiry_date); + + public: + ModifierInstance(Modifier const& new_modifier, Date new_expiry_date); + }; + + template<typename Fn> + concept ModifierEffectValidator = std::predicate<Fn, ModifierEffect const&>; + + struct ModifierManager { + /* Some ModifierEffects are generated mid-load, such as max/min count modifiers for each building, so + * we can't lock it until loading is over. This means we can't rely on locking for pointer stability, + * so instead we store the effects in a deque which doesn't invalidate pointers on insert. + */ + private: + CaseInsensitiveIdentifierRegistry<ModifierEffect, RegistryStorageInfoDeque> IDENTIFIER_REGISTRY(modifier_effect); + case_insensitive_string_set_t complex_modifiers; + + IdentifierRegistry<Modifier> IDENTIFIER_REGISTRY(event_modifier); + IdentifierRegistry<Modifier> IDENTIFIER_REGISTRY(static_modifier); + IdentifierRegistry<TriggeredModifier> IDENTIFIER_REGISTRY(triggered_modifier); + + /* effect_validator takes in ModifierEffect const& */ + NodeTools::key_value_callback_t _modifier_effect_callback( + ModifierValue& modifier, NodeTools::key_value_callback_t default_callback, + ModifierEffectValidator auto effect_validator + ) const; + + public: + bool add_modifier_effect( + std::string_view identifier, bool positive_good, + ModifierEffect::format_t format = ModifierEffect::format_t::PROPORTION_DECIMAL, + std::string_view localisation_key = {} + ); + + bool register_complex_modifier(std::string_view identifier); + static std::string get_flat_identifier( + std::string_view complex_modifier_identifier, std::string_view variant_identifier + ); + + bool setup_modifier_effects(); + + bool add_event_modifier(std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon); + bool load_event_modifiers(ast::NodeCPtr root); + + bool add_static_modifier(std::string_view identifier, ModifierValue&& values); + bool load_static_modifiers(ast::NodeCPtr root); + + bool add_triggered_modifier( + std::string_view identifier, ModifierValue&& values, Modifier::icon_t icon, ConditionScript&& trigger + ); + bool load_triggered_modifiers(ast::NodeCPtr root); + + bool parse_scripts(DefinitionManager const& definition_manager); + + NodeTools::node_callback_t expect_validated_modifier_value_and_default( + NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback, + ModifierEffectValidator auto effect_validator + ) const; + NodeTools::node_callback_t expect_validated_modifier_value( + NodeTools::callback_t<ModifierValue&&> modifier_callback, ModifierEffectValidator auto effect_validator + ) const; + + NodeTools::node_callback_t expect_modifier_value_and_default( + NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback + ) const; + NodeTools::node_callback_t expect_modifier_value(NodeTools::callback_t<ModifierValue&&> modifier_callback) const; + + NodeTools::node_callback_t expect_whitelisted_modifier_value_and_default( + NodeTools::callback_t<ModifierValue&&> modifier_callback, string_set_t const& whitelist, + NodeTools::key_value_callback_t default_callback + ) const; + NodeTools::node_callback_t expect_whitelisted_modifier_value( + NodeTools::callback_t<ModifierValue&&> modifier_callback, string_set_t const& whitelist + ) const; + + NodeTools::node_callback_t expect_modifier_value_and_key_map_and_default( + NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback, + NodeTools::key_map_t&& key_map + ) const; + NodeTools::node_callback_t expect_modifier_value_and_key_map( + NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_map_t&& key_map + ) const; + + template<typename... Args> + NodeTools::node_callback_t expect_modifier_value_and_key_map_and_default( + NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback, + NodeTools::key_map_t&& key_map, Args... args + ) const { + NodeTools::add_key_map_entries(key_map, args...); + return expect_modifier_value_and_key_map_and_default(modifier_callback, default_callback, std::move(key_map)); + } + + template<typename... Args> + NodeTools::node_callback_t expect_modifier_value_and_keys_and_default( + NodeTools::callback_t<ModifierValue&&> modifier_callback, NodeTools::key_value_callback_t default_callback, + Args... args + ) const { + return expect_modifier_value_and_key_map_and_default(modifier_callback, default_callback, {}, args...); + } + template<typename... Args> + NodeTools::node_callback_t expect_modifier_value_and_keys( + NodeTools::callback_t<ModifierValue&&> modifier_callback, Args... args + ) const { + return expect_modifier_value_and_key_map_and_default( + modifier_callback, NodeTools::key_value_invalid_callback, {}, args... + ); + } + }; +} |