From a862d6afcc99dc793665f02e7809ccf1d9c64ea7 Mon Sep 17 00:00:00 2001 From: gastank <42421688+gastank@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:02:02 -0700 Subject: [PATCH] [Parse] implement impact & execute callbacks `parse_effects()` now accepts functor arguments with the signature `void F( parse_callback_e )` * `PARSE_CALLBACK_PRE_IMPACT` will run the callback before action_t::impact() * `PARSE_CALLBACK_POST_IMPACT` will run the callback after action_t::impact() * `PARSE_CALLBACK_POST_EXECUTE` will run the callback after action_t::execute() `EXPIRE_BUFF` argument to `parse_effects()` will create a callback that expires the buff after execute() `DECREMENT_BUFF` argument to `parse_effects()` will create a callback that decrements the buff after execute() --- engine/action/parse_effects.cpp | 292 ++++++++++++-------------- engine/action/parse_effects.hpp | 282 ++++++++++++++----------- engine/class_modules/monk/sc_monk.cpp | 6 +- engine/class_modules/sc_druid.cpp | 43 ++-- 4 files changed, 313 insertions(+), 310 deletions(-) diff --git a/engine/action/parse_effects.cpp b/engine/action/parse_effects.cpp index c2100b9769a..5c64cb1b00f 100644 --- a/engine/action/parse_effects.cpp +++ b/engine/action/parse_effects.cpp @@ -77,7 +77,7 @@ std::string pet_type( uint32_t opt ) } } // namespace opt_strings -std::string player_effect_t::value_type_name( uint8_t t ) const +std::string player_effect_t::value_type_name( uint16_t t ) const { if ( t & VALUE_OVERRIDE ) return "Value Override"; @@ -108,7 +108,7 @@ void player_effect_t::print_parsed_line( report::sc_html_stream& os, const sim_t notes.emplace_back( "Passive" ); if ( idx ) - notes.emplace_back( "Consume" ); + notes.emplace_back( "Callback" ); if ( type & AFFECTED_OVERRIDE ) notes.emplace_back( "Value-override" ); @@ -147,7 +147,7 @@ void player_effect_t::print_parsed_line( report::sc_html_stream& os, const sim_t util::string_join( notes ) ); } -std::string target_effect_t::value_type_name( uint8_t t ) const +std::string target_effect_t::value_type_name( uint16_t t ) const { if ( t & VALUE_OVERRIDE ) return "Value Override"; @@ -420,9 +420,9 @@ const modified_spelleffect_t& modified_spell_data_t::effectN( size_t idx ) const return effects[ idx - 1 ]; } -void modified_spell_data_t::parse_effect( pack_t& tmp, const spell_data_t* s_data, size_t i ) +void modified_spell_data_t::parse_effect( const pack_t& pack, size_t i ) { - const auto& eff = s_data->effectN( i ); + const auto& eff = pack.spell->effectN( i ); bool m; // dummy throwaway double val = eff.base_value(); double val_mul = 0.01; @@ -465,14 +465,16 @@ void modified_spell_data_t::parse_effect( pack_t& tmp, const sp if ( range::contains( effN.permanent, &eff ) || range::contains( effN.conditional, &eff, &modify_effect_t::eff ) ) return; - if ( tmp.data.value != 0.0 ) + auto tmp = pack.data; // local copy + + if ( tmp.value != 0.0 ) { - val = tmp.data.value; + val = tmp.value; val_mul = 1.0; } else { - apply_affecting_mods( tmp, val, m, s_data, i ); + apply_affecting_mods( pack, val, m, i ); val *= val_mul; } @@ -482,7 +484,7 @@ void modified_spell_data_t::parse_effect( pack_t& tmp, const sp std::string val_str = flat ? fmt::format( "{}", val ) : fmt::format( "{}%", val * 100 ); // always active - if ( !tmp.data.buff && !tmp.data.func ) + if ( !tmp.buff && !tmp.func ) { if ( flat ) effN.value += val; @@ -494,14 +496,14 @@ void modified_spell_data_t::parse_effect( pack_t& tmp, const sp // conditionally active else { - tmp.data.value = val; - tmp.data.flat = flat; - tmp.data.eff = &eff; + tmp.value = val; + tmp.flat = flat; + tmp.eff = &eff; if ( eff.flags( EX_SUPPRESS_STACKING ) ) - tmp.data.use_stacks = false; + tmp.use_stacks = false; - effN.conditional.push_back( tmp.data ); + effN.conditional.push_back( std::move( tmp ) ); } } @@ -556,10 +558,10 @@ void modified_spell_data_t::parsed_effects_html( report::sc_html_stream& os, con // main effect parsing function with constexpr checks to handle player_effect_t vs target_effect_t template -void parse_effects_t::parse_effect( pack_t& tmp, const spell_data_t* s_data, size_t i, bool force ) +bool parse_effects_t::parse_effect( pack_t& pack, size_t i, bool force ) { - const auto& eff = s_data->effectN( i ); - bool mastery = s_data->flags( SX_MASTERY_AFFECTS_POINTS ); + const auto& eff = pack.spell->effectN( i ); + bool mastery = pack.spell->flags( SX_MASTERY_AFFECTS_POINTS ); double val = 0.0; double val_mul = 0.01; @@ -570,48 +572,50 @@ void parse_effects_t::parse_effect( pack_t& tmp, const spell_data_t* s_data, if constexpr ( is_detected_v && is_detected_v ) { - if ( tmp.data.buff && tmp.data.type & USE_DEFAULT ) - val = tmp.data.buff->default_value * 100; + if ( pack.data.buff && pack.data.type & USE_DEFAULT ) + val = pack.data.buff->default_value * 100; if ( !is_valid_aura( eff ) ) - return; + return false; } else { if ( !is_valid_target_aura( eff ) ) - return; + return false; } - if ( tmp.data.value != 0.0 ) + if ( pack.data.value != 0.0 ) { - val = tmp.data.value; + val = pack.data.value; val_mul = 1.0; mastery = false; } else { - apply_affecting_mods( tmp, val, mastery, s_data, i ); + apply_affecting_mods( pack, val, mastery, i ); if ( mastery ) val_mul = 1.0; } + auto tmp = pack.data; // local copy + std::string type_str; bool flat = false; - std::vector* vec = get_effect_vector( eff, tmp, val_mul, type_str, flat, force ); + std::vector* vec = get_effect_vector( eff, tmp, val_mul, type_str, flat, force, pack ); if ( !vec ) - return; + return false; if constexpr ( is_detected_v ) { - if ( !val && tmp.data.type == USE_DATA ) - return; + if ( !val && tmp.type == USE_DATA ) + return false; } else { if ( !val ) - return; + return false; } val *= val_mul; @@ -620,58 +624,57 @@ void parse_effects_t::parse_effect( pack_t& tmp, const spell_data_t* s_data, : flat ? fmt::format( "{}", val ) : fmt::format( "{:.1f}%", val * ( 1 / val_mul ) ); - if ( tmp.data.value != 0.0 ) + if ( tmp.value != 0.0 ) val_str = val_str + " (value override)"; if constexpr ( is_detected_v ) { if ( eff.flags( EX_SUPPRESS_STACKING ) ) - tmp.data.use_stacks = false; + tmp.use_stacks = false; } if constexpr ( is_detected_v && is_detected_v ) { - if ( tmp.data.buff ) + if ( tmp.buff ) { - if ( tmp.data.type & USE_CURRENT ) + if ( tmp.type & USE_CURRENT ) val_str = flat ? "current value" : "current value percent"; - else if ( tmp.data.type & USE_DEFAULT ) + else if ( tmp.type & USE_DEFAULT ) val_str = val_str + " (default value)"; } } if constexpr ( is_detected_v ) { - if ( tmp.data.value_func ) + if ( tmp.value_func ) val_str += " (value function)"; } - debug_message( tmp.data, type_str, val_str, mastery, s_data, i ); + debug_message( tmp, type_str, val_str, mastery, pack.spell, i ); - tmp.data.value = val; - tmp.data.mastery = mastery; - tmp.data.eff = &eff; + tmp.value = val; + tmp.mastery = mastery; + tmp.eff = &eff; if constexpr ( is_detected_v ) { - if ( tmp.data.func || tmp.data.value_func || tmp.data.type & USE_CURRENT || tmp.data.idx || tmp.data.mastery || - !tmp.data.use_stacks ) + if ( tmp.func || tmp.value_func || tmp.type & USE_CURRENT || tmp.mastery || !tmp.use_stacks || pack.callback ) { - tmp.data.simple = false; + tmp.simple = false; } } - vec->push_back( tmp.data ); + if ( pack.copy ) + pack.copy->push_back( tmp ); - if ( tmp.copy ) - tmp.copy->push_back( tmp.data ); + vec->push_back( std::move( tmp ) ); + + return true; } // explicit template instantiation -template void parse_effects_t::parse_effect( pack_t&, const spell_data_t*, size_t, - bool ); -template void parse_effects_t::parse_effect( pack_t&, const spell_data_t*, size_t, - bool ); +template bool parse_effects_t::parse_effect( pack_t&, size_t, bool ); +template bool parse_effects_t::parse_effect( pack_t&, size_t, bool ); double parse_effects_t::get_effect_value( const player_effect_t& i, bool benefit ) const { @@ -708,13 +711,13 @@ double parse_effects_t::get_effect_value_full( const player_effect_t& i, bool be if ( i.use_stacks ) eff_val *= stack; - - buff_idx_to_consume |= i.idx; } if ( i.mastery ) eff_val *= _player->cache.mastery(); + callback_idx |= i.idx; + return eff_val; } @@ -1029,15 +1032,14 @@ bool parse_player_effects_t::is_valid_aura( const spelleffect_data_t& eff ) cons } std::vector* parse_player_effects_t::get_effect_vector( const spelleffect_data_t& eff, - pack_t& pack, - double& val_mul, std::string& str, - bool& /* flat */, bool /* force */ ) -{ - auto& data = pack.data; - - auto invalidate = [ &data ]( cache_e c ) { - if ( data.buff ) - data.buff->add_invalidate( c ); + player_effect_t& tmp, double& val_mul, + std::string& str, bool& /* flat */, + bool /* force */, + const pack_t& /* pack */ ) +{ + auto invalidate = [ &tmp ]( cache_e c ) { + if ( tmp.buff ) + tmp.buff->add_invalidate( c ); }; switch ( eff.subtype() ) @@ -1049,8 +1051,8 @@ std::vector* parse_player_effects_t::get_effect_vector( const s return &auto_attack_speed_effects; case A_MOD_TOTAL_STAT_PERCENTAGE: - data.opt_enum = eff.misc_value2(); - str = opt_strings::attributes_invalidate( data ); + tmp.opt_enum = eff.misc_value2(); + str = opt_strings::attributes_invalidate( tmp ); if ( eff.spell()->equipped_class() == ITEM_CLASS_ARMOR && eff.spell()->flags( SX_REQUIRES_EQUIPPED_ARMOR_TYPE ) ) { @@ -1065,8 +1067,8 @@ std::vector* parse_player_effects_t::get_effect_vector( const s return &attribute_multiplier_effects; case A_MOD_RATING_MULTIPLIER: - data.opt_enum = eff.misc_value1(); - str = opt_strings::ratings_invalidate( data ); + tmp.opt_enum = eff.misc_value1(); + str = opt_strings::ratings_invalidate( tmp ); return &rating_multiplier_effects; case A_MOD_VERSATILITY_PCT: @@ -1091,19 +1093,19 @@ std::vector* parse_player_effects_t::get_effect_vector( const s return &crit_chance_effects; case A_MOD_DAMAGE_PERCENT_DONE: - data.opt_enum = eff.misc_value1(); - str = opt_strings::school( data.opt_enum ); + tmp.opt_enum = eff.misc_value1(); + str = opt_strings::school( tmp.opt_enum ); str += " damage"; invalidate( CACHE_PLAYER_DAMAGE_MULTIPLIER ); return &player_multiplier_effects; case A_MOD_PET_DAMAGE_DONE: - data.opt_enum = 0; + tmp.opt_enum = 0; str = "pet damage"; return &pet_multiplier_effects; case A_MOD_GUARDIAN_DAMAGE_DONE: - data.opt_enum = 1; + tmp.opt_enum = 1; str = "guardian damage"; return &pet_multiplier_effects; @@ -1209,26 +1211,25 @@ bool parse_player_effects_t::is_valid_target_aura( const spelleffect_data_t& eff } std::vector* parse_player_effects_t::get_effect_vector( const spelleffect_data_t& eff, - pack_t& pack, - double& /* val_mul */, std::string& str, - bool& /* flat */, bool /* force */ ) + target_effect_t& tmp, double& /* val_mul */, + std::string& str, bool& /* flat */, + bool /* force */, + const pack_t& /* pack */ ) { - auto& data = pack.data; - switch ( eff.subtype() ) { case A_MOD_DAMAGE_FROM_CASTER: - data.opt_enum = eff.misc_value1(); - str = opt_strings::school( data.opt_enum ); + tmp.opt_enum = eff.misc_value1(); + str = opt_strings::school( tmp.opt_enum ); return &target_multiplier_effects; case A_MOD_DAMAGE_FROM_CASTER_PET: - data.opt_enum = 0; + tmp.opt_enum = 0; str = "pet"; return &target_pet_multiplier_effects; case A_MOD_DAMAGE_FROM_CASTER_GUARDIAN: - data.opt_enum = 1; + tmp.opt_enum = 1; str = "guardian"; return &target_pet_multiplier_effects; @@ -1321,6 +1322,55 @@ size_t parse_player_effects_t::total_effects_count() target_pet_multiplier_effects.size(); } +void parse_action_base_t::parse_callback_function( pack_t& pack, parse_cb_t cb ) +{ + assert( pack.data.idx == 0 && "cannot parse multiple parse callbacks" ); + // 32 max as parse_effects_t::callback_idx is uint32_t + assert( callback_list.size() < 32 && "cannot register more than 32 parse callbacks" ); + + // set values on main pack, to be propagated to all copies + pack.callback = std::move( cb ); + // this is set BEFORE the callback is added to the vector, so the idx will be 1bit left of size() + pack.data.idx = 1U << ( callback_list.size() ); +} + +void parse_action_base_t::parse_callback_function( pack_t& pack, parse_flag_e type ) +{ + assert( pack.data.buff && "EXPIRE_BUFF/DECREMENT_BUFF requires a buff" ); + + if ( type == EXPIRE_BUFF ) + { + parse_callback_function( pack, [ a = _action, b = pack.data.buff ]( parse_callback_e cb_type ) { + if ( cb_type == PARSE_CALLBACK_POST_EXECUTE ) + b->expire( a ); + } ); + } + else if ( type == DECREMENT_BUFF ) + { + parse_callback_function( pack, [ a = _action, b = pack.data.buff ]( parse_callback_e cb_type ) { + if ( cb_type == PARSE_CALLBACK_POST_EXECUTE && b->can_expire( a ) ) + b->decrement(); + } ); + } +} + +void parse_action_base_t::register_callback_function( pack_t& pack ) +{ + callback_list.push_back( std::move( pack.callback ) ); + + _player->sim->print_debug( "action-effects: {} ({}) registering parse callback on {} {} ({})", _action->name(), + _action->id, pack.data.buff ? "buff" : "spell", pack.spell->name_cstr(), + pack.spell->id() ); +} + +void parse_action_base_t::trigger_callbacks( parse_callback_e cb_type ) +{ + if ( callback_idx ) + for ( size_t i = 0; i < callback_list.size(); i++ ) + if ( callback_idx & ( 1U << i ) ) + callback_list[ i ]( cb_type ); +} + bool parse_action_base_t::is_valid_aura( const spelleffect_data_t& eff ) const { // Only parse apply aura effects @@ -1344,9 +1394,9 @@ bool parse_action_base_t::is_valid_aura( const spelleffect_data_t& eff ) const // non-templated implementation of parse_action_effects_t::get_effect_vector() std::vector* parse_action_base_t::get_effect_vector( const spelleffect_data_t& eff, - pack_t& pack, - double& val_mul, std::string& str, - bool& flat, bool force ) + player_effect_t& tmp, double& val_mul, + std::string& str, bool& flat, bool force, + const pack_t& pack ) { if ( !_action->special && eff.subtype() == A_MOD_AUTO_ATTACK_PCT ) { @@ -1359,7 +1409,7 @@ std::vector* parse_action_base_t::get_effect_vector( const spel if ( !check_affected_list( pack.affect_lists, eff, force ) ) return nullptr; else if ( force ) - pack.data.type |= AFFECTED_OVERRIDE; + tmp.type |= AFFECTED_OVERRIDE; } if ( !force && !_action->data().affected_by_all( eff ) ) @@ -1414,9 +1464,9 @@ std::vector* parse_action_base_t::get_effect_vector( const spel { auto school = dbc::get_school_type( eff.misc_value1() ); - if ( pack.data.buff ) + if ( tmp.buff ) { - pack.data.buff->add_stack_change_callback( [ a = _action, school ]( buff_t*, int old_, int new_ ) { + tmp.buff->add_stack_change_callback( [ a = _action, school ]( buff_t*, int old_, int new_ ) { if ( !old_ ) a->set_school_override( school ); else if ( !new_ ) @@ -1424,8 +1474,8 @@ std::vector* parse_action_base_t::get_effect_vector( const spel } ); } - pack.data.type |= parse_flag_e::ALLOW_ZERO; - pack.data.opt_enum = eff.misc_value1(); + tmp.type |= parse_flag_e::ALLOW_ZERO; + tmp.opt_enum = eff.misc_value1(); flat = true; str = fmt::format( "spell school|{}", util::school_type_string( school ) ); return &spell_school_effects; @@ -1491,9 +1541,9 @@ bool parse_action_base_t::is_valid_target_aura( const spelleffect_data_t& eff ) } std::vector* parse_action_base_t::get_effect_vector( const spelleffect_data_t& eff, - pack_t& pack, - double& /* val_mul */, std::string& str, - bool& flat, bool force ) + target_effect_t& tmp, double& /* val_mul */, + std::string& str, bool& flat, bool force, + const pack_t& pack ) { if ( !_action->special && eff.subtype() == A_MOD_AUTO_ATTACK_FROM_CASTER ) { @@ -1506,7 +1556,7 @@ std::vector* parse_action_base_t::get_effect_vector( const spel if ( !check_affected_list( pack.affect_lists, eff, force ) ) return nullptr; else if ( force ) - pack.data.type |= AFFECTED_OVERRIDE; + tmp.type |= AFFECTED_OVERRIDE; } if ( !force && !_action->data().affected_by_all( eff ) ) @@ -1683,67 +1733,3 @@ size_t parse_action_base_t::total_effects_count() target_crit_chance_effects.size() + target_crit_bonus_effects.size(); } - -void parse_action_base_t::initialize_buff_list_on_vector( std::vector& vec ) -{ - for ( auto& i : vec ) - { - if ( i.idx == UINT32_MAX ) - { - if ( i.buff && i.buff->can_expire( _action ) ) - { - auto it = range::find( _buff_list, i.buff ); - if ( it == _buff_list.end() ) - { - // max 32 buffs as buff_idx_to_consume is a uint32_t - assert( _buff_list.size() < 32 && "Too many buffs for initialize_buff_list()" ); - _buff_list.push_back( i.buff ); - i.idx = static_cast( _buff_list.size() ); - } - else - { - i.idx = 1 << std::distance( _buff_list.begin(), it ); - } - } - else - { - i.idx = 0; - } - } - } -} - -void parse_action_base_t::initialize_buff_list() -{ - // only check player_effect_t vectors - initialize_buff_list_on_vector( ta_multiplier_effects ); - initialize_buff_list_on_vector( da_multiplier_effects ); - initialize_buff_list_on_vector( execute_time_effects ); - initialize_buff_list_on_vector( flat_execute_time_effects ); - initialize_buff_list_on_vector( gcd_effects ); - initialize_buff_list_on_vector( dot_duration_effects ); - initialize_buff_list_on_vector( flat_dot_duration_effects ); - initialize_buff_list_on_vector( tick_time_effects ); - initialize_buff_list_on_vector( flat_tick_time_effects ); - initialize_buff_list_on_vector( recharge_multiplier_effects ); - initialize_buff_list_on_vector( recharge_rate_effects ); - initialize_buff_list_on_vector( cost_effects ); - initialize_buff_list_on_vector( flat_cost_effects ); - initialize_buff_list_on_vector( crit_chance_effects ); - initialize_buff_list_on_vector( crit_chance_multiplier_effects ); - initialize_buff_list_on_vector( crit_bonus_effects ); - initialize_buff_list_on_vector( spell_school_effects ); -} - -void parse_action_base_t::consume_buff_list() -{ - auto buff_it = _buff_list.begin(); - while ( buff_idx_to_consume ) - { - if ( buff_idx_to_consume & 1U ) - ( *buff_it )->expire( _action ); - - buff_idx_to_consume >>= 1; - buff_it++; - } -} diff --git a/engine/action/parse_effects.hpp b/engine/action/parse_effects.hpp index 53ea9c038c3..76df2bace65 100644 --- a/engine/action/parse_effects.hpp +++ b/engine/action/parse_effects.hpp @@ -11,18 +11,26 @@ #include "player/stats.hpp" #include "util/io.hpp" -enum parse_flag_e : uint8_t +enum parse_flag_e : uint16_t { - USE_DATA = 0x00, - USE_DEFAULT = 0x01, - USE_CURRENT = 0x02, - IGNORE_STACKS = 0x04, - ALLOW_ZERO = 0x08, - CONSUME_BUFF = 0x10, + USE_DATA = 0x0000, + USE_DEFAULT = 0x0001, + USE_CURRENT = 0x0002, + IGNORE_STACKS = 0x0004, + ALLOW_ZERO = 0x0008, + EXPIRE_BUFF = 0x0010, + DECREMENT_BUFF = 0x0020, // internal flags that should not be used in parse_effects() - VALUE_OVERRIDE = 0x20, - AFFECTED_OVERRIDE = 0x40, - MANUAL_ENTRY = 0x80 + VALUE_OVERRIDE = 0x0100, + AFFECTED_OVERRIDE = 0x0200, + MANUAL_ENTRY = 0x0400 +}; + +enum parse_callback_e +{ + PARSE_CALLBACK_PRE_IMPACT, + PARSE_CALLBACK_POST_IMPACT, + PARSE_CALLBACK_POST_EXECUTE, }; // effects dependent on player state @@ -36,9 +44,9 @@ struct player_effect_t // full processing std::function func = nullptr; std::function value_func = nullptr; - uint8_t type = USE_DATA; + uint16_t type = USE_DATA; bool mastery = false; - uint32_t idx = 0; // used for consume_buff linkage during init_finished() + uint32_t idx = 0; // index of parse_action_base_t::callback_list // effect linkback const spelleffect_data_t* eff = &spelleffect_data_t::nil(); // optional enum identifier @@ -81,7 +89,7 @@ struct player_effect_t opt_enum == other.opt_enum; } - std::string value_type_name( uint8_t ) const; + std::string value_type_name( uint16_t ) const; void print_parsed_line( report::sc_html_stream&, const sim_t&, bool, const std::function&, @@ -93,7 +101,7 @@ struct target_effect_t { std::function func = nullptr; double value = 0.0; - uint8_t type = USE_DATA; // for internal flags only + uint16_t type = USE_DATA; // for internal flags only bool mastery = false; const spelleffect_data_t* eff = &spelleffect_data_t::nil(); uint32_t opt_enum = UINT32_MAX; @@ -118,7 +126,7 @@ struct target_effect_t return value == other.value && mastery == other.mastery && eff == other.eff && opt_enum == other.opt_enum; } - std::string value_type_name( uint8_t ) const; + std::string value_type_name( uint16_t ) const; void print_parsed_line( report::sc_html_stream&, const sim_t&, bool, const std::function&, @@ -253,15 +261,50 @@ struct affect_list_t { remove_spell( s ); return remove_spell( ss... ); } }; +// local aliases +namespace +{ +using parse_cb_t = std::function; +template using detect_simple = decltype( T::simple ); +template using detect_buff = decltype( T::buff ); +template using detect_func = decltype( T::func ); +template using detect_value_func = decltype( T::value_func ); +template using detect_use_stacks = decltype( T::use_stacks ); +template using detect_type = decltype( T::type ); +template using detect_value = decltype( T::value ); +template using detect_idx = decltype( T::idx ); +} + // used to store values from parameter pack recursion of parse_effect/parse_target_effects template >> struct pack_t { U data; + const spell_data_t* spell; // uninitalized std::vector list; uint32_t mask = 0U; std::vector* copy = nullptr; std::vector affect_lists; + parse_cb_t callback = nullptr; + + pack_t( const spell_data_t* s_data ) : spell( s_data ) {} + + pack_t( buff_t* buff ) + { + if ( buff ) + { + spell = &buff->data(); + + if constexpr ( is_detected_v ) + { + data.buff = buff; + } + } + else + { + spell = nullptr; + } + } }; template @@ -278,41 +321,6 @@ struct parse_base_t parse_base_t() = default; virtual ~parse_base_t() = default; - // detectors for is_detected_v<> - template using detect_simple = decltype( T::simple ); - template using detect_buff = decltype( T::buff ); - template using detect_func = decltype( T::func ); - template using detect_value_func = decltype( T::value_func ); - template using detect_use_stacks = decltype( T::use_stacks ); - template using detect_type = decltype( T::type ); - template using detect_value = decltype( T::value ); - template using detect_idx = decltype( T::idx ); - - // handle first argument to parse_effects(), set buff if necessary and return spell_data_t* - template - const spell_data_t* resolve_parse_data( T data, pack_t& tmp ) - { - if constexpr ( std::is_invocable_v ) - { - if ( !data ) - return nullptr; - - if constexpr ( is_detected_v ) - tmp.data.buff = data; - - return &data->data(); - } - else if constexpr ( std::is_invocable_v ) - { - return data; - } - else - { - static_assert( static_false, "Invalid data type for resolve_parse_data" ); - return nullptr; - } - } - // returns the value of the effect. overload if different value access method is needed for other spell_data_t* // castable types, such as conduits double mod_spell_effects_value( const spell_data_t*, const spelleffect_data_t& e ) { return e.base_value(); } @@ -321,16 +329,23 @@ struct parse_base_t void apply_affecting_mod( double&, bool&, const spell_data_t*, size_t, T ); template - void apply_affecting_mods( const pack_t& tmp, double& val, bool& mastery, const spell_data_t* base, size_t idx ) + void apply_affecting_mods( const pack_t& pack, double& val, bool& mastery, size_t idx ) { // Apply effect modifying effects from mod list. Blizz only currently supports modifying effects 1-5 if ( idx > 5 ) return; - for ( size_t j = 0; j < tmp.list.size(); j++ ) - apply_affecting_mod( val, mastery, base, idx, tmp.list[ j ] ); + for ( size_t j = 0; j < pack.list.size(); j++ ) + apply_affecting_mod( val, mastery, pack.spell, idx, pack.list[ j ] ); } + virtual void parse_callback_function( pack_t&, parse_cb_t ) + { assert( false && "cannot register parse callback on this base" ); } + virtual void parse_callback_function( pack_t&, parse_flag_e ) + { assert( false && "cannot register parse callback on this base" ); } + virtual void register_callback_function( pack_t& ) + { assert( false && "cannot register parse callback on this base" ); } + // populate pack with optional arguments to parse_effects(). // parsing order of precedence is: // 1) spell_data_t* added to list of spells to check for effect modifying effects @@ -339,67 +354,78 @@ struct parse_base_t // 4) floating point value to directly set the effect value and override all parsing // 5) integral bitmask to ignore effect# n corresponding to the n'th bit template - void parse_spell_effect_mod( pack_t& tmp, T mod ) + void parse_spell_effect_mod( pack_t& pack, T mod ) { if constexpr ( std::is_invocable_v ) { - tmp.list.push_back( mod ); + pack.list.push_back( mod ); } else if constexpr ( std::is_convertible_v> && is_detected_v ) { - tmp.data.value_func = std::move( mod ); + pack.data.value_func = std::move( mod ); } else if constexpr ( ( std::is_convertible_v> || std::is_convertible_v> ) && is_detected_v ) { - tmp.data.func = std::move( mod ); + pack.data.func = std::move( mod ); + } + else if constexpr ( std::is_convertible_v && std::is_same_v ) + { + parse_callback_function( pack, std::move( mod ) ); } else if constexpr ( std::is_same_v ) { if constexpr ( is_detected_v ) { if ( mod == IGNORE_STACKS ) - tmp.data.use_stacks = false; + { + pack.data.use_stacks = false; + return; + } } if constexpr ( is_detected_v ) { - if ( ( mod == USE_DEFAULT || mod == USE_CURRENT ) && !( tmp.data.type & VALUE_OVERRIDE ) ) + if ( ( mod == USE_DEFAULT || mod == USE_CURRENT ) && !( pack.data.type & VALUE_OVERRIDE ) ) { - tmp.data.type &= ~( USE_DEFAULT | USE_CURRENT ); - tmp.data.type |= mod; + pack.data.type &= ~( USE_DEFAULT | USE_CURRENT ); + pack.data.type |= mod; + return; } } - if constexpr ( is_detected_v ) + if constexpr ( std::is_same_v ) { - if ( mod == CONSUME_BUFF ) - tmp.data.idx = UINT32_MAX;; + if ( mod == EXPIRE_BUFF || mod == DECREMENT_BUFF ) + { + parse_callback_function( pack, mod ); + return; + } } } else if constexpr ( std::is_floating_point_v && is_detected_v ) { - tmp.data.value = mod; + pack.data.value = mod; if constexpr ( is_detected_v ) { - tmp.data.type &= ~( USE_DEFAULT | USE_CURRENT ); - tmp.data.type |= VALUE_OVERRIDE; + pack.data.type &= ~( USE_DEFAULT | USE_CURRENT ); + pack.data.type |= VALUE_OVERRIDE; } } else if constexpr ( std::is_same_v || ( std::is_integral_v && !std::is_same_v ) ) { - tmp.mask = mod; + pack.mask = mod; } else if constexpr ( std::is_same_v ) { - tmp.affect_lists.push_back( std::move( mod ) ); + pack.affect_lists.push_back( std::move( mod ) ); } else if constexpr ( std::is_convertible_v() ), const std::vector> ) { - tmp.copy = &( *mod ); + pack.copy = &( *mod ); } else { @@ -408,9 +434,9 @@ struct parse_base_t } template - void parse_spell_effect_mods( pack_t& tmp, Ts... mods ) + void parse_spell_effect_mods( pack_t& pack, Ts... mods ) { - ( parse_spell_effect_mod( tmp, mods ), ... ); + ( parse_spell_effect_mod( pack, mods ), ... ); } }; @@ -501,30 +527,26 @@ struct modified_spell_data_t : public parse_base_t if ( !_spell.ok() ) return this; - pack_t pack; - const spell_data_t* spell = resolve_parse_data( data, pack ); + pack_t pack( data ); - if ( !spell || !spell->ok() ) + if ( !pack.spell || !pack.spell->ok() ) return this; // parse mods and populate pack parse_spell_effect_mods( pack, mods... ); - for ( size_t i = 1; i <= spell->effect_count(); i++ ) + for ( size_t i = 1; i <= pack.spell->effect_count(); i++ ) { if ( pack.mask & 1 << ( i - 1 ) ) continue; - // local copy of pack per effect - pack_t tmp = pack; - - parse_effect( tmp, spell, i ); + parse_effect( pack, i ); } return this; } - void parse_effect( pack_t&, const spell_data_t*, size_t ); + void parse_effect( const pack_t&, size_t ); void print_parsed_spell( report::sc_html_stream&, const sim_t& ); @@ -540,13 +562,14 @@ struct parse_effects_t : public parse_base_t { protected: player_t* _player; - mutable uint32_t buff_idx_to_consume = 0; + std::vector callback_list; + mutable uint32_t callback_idx = 0; public: parse_effects_t( player_t* p ) : _player( p ) {} template - void parse_effect( pack_t&, const spell_data_t*, size_t, bool ); + bool parse_effect( pack_t&, size_t, bool ); // Syntax: parse_effects( data[, spells|condition|ignore_mask|value|flags][,...] ) // (buff_t*) or @@ -578,40 +601,41 @@ struct parse_effects_t : public parse_base_t template void parse_effects( T data, Ts... mods ) { - pack_t pack; - const spell_data_t* spell = resolve_parse_data( data, pack ); + pack_t pack( data ); - if ( !spell || !spell->ok() ) + if ( !pack.spell || !pack.spell->ok() ) return; // parse mods and populate pack parse_spell_effect_mods( pack, mods... ); - for ( size_t i = 1; i <= spell->effect_count(); i++ ) + bool has_entry = false; + + for ( size_t i = 1; i <= pack.spell->effect_count(); i++ ) { if ( pack.mask & 1 << ( i - 1 ) ) continue; - // local copy of pack per effect - pack_t tmp = pack; - - parse_effect( tmp, spell, i, false ); + has_entry = parse_effect( pack, i, false ) || has_entry; } + + if ( has_entry && pack.callback ) + register_callback_function( pack ); } template void force_effect( T data, unsigned idx, Ts... mods ) { - pack_t pack; - const spell_data_t* spell = resolve_parse_data( data, pack ); + pack_t pack( data ); - if ( !spell || !spell->ok() || !can_force( spell->effectN( idx ) ) ) + if ( !pack.spell || !pack.spell->ok() || !can_force( pack.spell->effectN( idx ) ) ) return; // parse mods and populate pack parse_spell_effect_mods( pack, mods... ); - parse_effect( pack, spell, idx, true ); + if ( parse_effect( pack, idx, true ) && pack.callback ) + register_callback_function( pack ); } // Syntax: parse_target_effects( func, debuff[, spells|ignore_mask][,...] ) @@ -625,25 +649,21 @@ struct parse_effects_t : public parse_base_t void parse_target_effects( const std::function& fn, const spell_data_t* spell, Ts... mods ) { - pack_t pack; - if ( !spell || !spell->ok() ) return; + pack_t pack( spell ); pack.data.func = std::move( fn ); // parse mods and populate pack parse_spell_effect_mods( pack, mods... ); - for ( size_t i = 1; i <= spell->effect_count(); i++ ) + for ( size_t i = 1; i <= pack.spell->effect_count(); i++ ) { if ( pack.mask & 1 << ( i - 1 ) ) continue; - // local copy of pack per effect - pack_t tmp = pack; - - parse_effect( tmp, spell, i, false ); + parse_effect( pack, i, false ); } } @@ -651,28 +671,27 @@ struct parse_effects_t : public parse_base_t void force_target_effect( const std::function& fn, const spell_data_t* spell, unsigned idx, Ts... mods ) { - pack_t pack; - if ( !spell || !spell->ok() || !can_force( spell->effectN( idx ) ) ) return; + pack_t pack( spell ); pack.data.func = std::move( fn ); // parse mods and populate pack parse_spell_effect_mods( pack, mods... ); - parse_effect( pack, spell, idx, true ); + parse_effect( pack, idx, true ); } virtual bool is_valid_aura( const spelleffect_data_t& ) const { return false; } virtual bool is_valid_target_aura( const spelleffect_data_t& ) const { return false; } - virtual std::vector* get_effect_vector( const spelleffect_data_t& eff, pack_t& pack, - double& val_mul, std::string& str, bool& flat, - bool force ) = 0; - virtual std::vector* get_effect_vector( const spelleffect_data_t& eff, pack_t& data, - double& val_mul, std::string& str, bool& flat, - bool force ) = 0; + virtual std::vector* get_effect_vector( const spelleffect_data_t& eff, player_effect_t& tmp, + double& val_mul, std::string& str, bool& flat, bool force, + const pack_t& pack ) = 0; + virtual std::vector* get_effect_vector( const spelleffect_data_t& eff, target_effect_t& tmp, + double& val_mul, std::string& str, bool& flat, bool force, + const pack_t& pack ) = 0; virtual void debug_message( const player_effect_t& data, std::string_view type_str, std::string_view val_str, bool mastery, const spell_data_t* s_data, size_t i ) = 0; @@ -748,10 +767,10 @@ struct parse_player_effects_t : public player_t, public parse_effects_t bool is_valid_aura( const spelleffect_data_t& ) const override; bool is_valid_target_aura( const spelleffect_data_t& ) const override; - std::vector* get_effect_vector( const spelleffect_data_t&, pack_t&, double&, - std::string&, bool&, bool ) override; - std::vector* get_effect_vector( const spelleffect_data_t&, pack_t&, double&, - std::string&, bool&, bool ) override; + std::vector* get_effect_vector( const spelleffect_data_t&, player_effect_t&, double&, std::string&, + bool&, bool, const pack_t& ) override; + std::vector* get_effect_vector( const spelleffect_data_t&, target_effect_t&, double&, std::string&, + bool&, bool, const pack_t& ) override; void debug_message( const player_effect_t&, std::string_view, std::string_view, bool, const spell_data_t*, size_t ) override; @@ -809,19 +828,24 @@ struct parse_action_base_t : public parse_effects_t std::vector target_crit_bonus_effects; private: - std::vector _buff_list; action_t* _action; public: parse_action_base_t( player_t* p, action_t* a ) : parse_effects_t( p ), _action( a ) {} + void parse_callback_function( pack_t& pack, parse_cb_t cb ) override; + void parse_callback_function( pack_t& pack, parse_flag_e type ) override; + void register_callback_function( pack_t& pack ) override; + + void trigger_callbacks( parse_callback_e ); + bool is_valid_aura( const spelleffect_data_t& ) const override; bool is_valid_target_aura( const spelleffect_data_t& ) const override; - std::vector* get_effect_vector( const spelleffect_data_t&, pack_t&, double&, - std::string&, bool&, bool ) override; - std::vector* get_effect_vector( const spelleffect_data_t&, pack_t&, double&, - std::string&, bool&, bool ) override; + std::vector* get_effect_vector( const spelleffect_data_t&, player_effect_t&, double&, std::string&, + bool&, bool, const pack_t& ) override; + std::vector* get_effect_vector( const spelleffect_data_t&, target_effect_t&, double&, std::string&, + bool&, bool, const pack_t& ) override; void debug_message( const player_effect_t&, std::string_view, std::string_view, bool, const spell_data_t*, size_t ) override; @@ -832,12 +856,6 @@ struct parse_action_base_t : public parse_effects_t bool check_affected_list( const std::vector&, const spelleffect_data_t&, bool& ); - void initialize_buff_list_on_vector( std::vector& ); - - void initialize_buff_list(); - - void consume_buff_list(); - void parsed_effects_html( report::sc_html_stream& ); virtual void print_parsed_custom_type( report::sc_html_stream& ) {} @@ -935,14 +953,22 @@ struct parse_action_effects_t : public BASE, public parse_action_base_t void init_finished() override { BASE::init_finished(); - initialize_buff_list(); initialize_cooldown_buffs(); } + void impact( action_state_t* s ) override + { + trigger_callbacks( PARSE_CALLBACK_PRE_IMPACT ); + BASE::impact( s ); + trigger_callbacks( PARSE_CALLBACK_POST_IMPACT ); + } + + void execute() override { BASE::execute(); - consume_buff_list(); + trigger_callbacks( PARSE_CALLBACK_POST_EXECUTE ); + callback_idx = 0; } double cost_flat_modifier() const override diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index c46f589dabf..ff0cab22701 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -178,7 +178,7 @@ void monk_action_t::apply_buff_effects() parse_effects( p()->buff.counterstrike, affect_list_t( 1 ).add_spell( p()->baseline.brewmaster.spinning_crane_kick->effectN( 1 ).trigger()->id() ), - CONSUME_BUFF ); + EXPIRE_BUFF ); // Mistweaver parse_effects( p()->buff.jadefire_brand, p()->talent.windwalker.jadefire_brand_heal ); @@ -217,8 +217,8 @@ void monk_action_t::apply_buff_effects() // TODO: parse_effects implementation for A_MOD_HEALING_RECEIVED_FROM_SPELL (283) parse_effects( p()->talent.master_of_harmony.aspect_of_harmony_heal, p()->talent.master_of_harmony.coalescence, [ & ] { return p()->buff.aspect_of_harmony.heal_ticking(); } ); - parse_effects( p()->buff.balanced_stratagem_physical, CONSUME_BUFF ); - parse_effects( p()->buff.balanced_stratagem_magic, CONSUME_BUFF ); + parse_effects( p()->buff.balanced_stratagem_physical, EXPIRE_BUFF ); + parse_effects( p()->buff.balanced_stratagem_magic, EXPIRE_BUFF ); // Shado-Pan parse_effects( p()->buff.wisdom_of_the_wall_crit ); diff --git a/engine/class_modules/sc_druid.cpp b/engine/class_modules/sc_druid.cpp index 2fee587318d..91543e542ea 100644 --- a/engine/class_modules/sc_druid.cpp +++ b/engine/class_modules/sc_druid.cpp @@ -2017,12 +2017,6 @@ struct druid_action_t : public parse_action_effects_t, public druid_action return pers; } - void init_finished() override - { - ab::init_finished(); - ab::initialize_buff_list_on_vector( persistent_multiplier_effects ); - } - size_t total_effects_count() override { return ab::total_effects_count() + persistent_multiplier_effects.size(); @@ -2869,13 +2863,6 @@ struct cat_attack_t : public druid_attack_t } } - void init_finished() override - { - base_t::init_finished(); - base_t::initialize_buff_list_on_vector( persistent_periodic_effects ); - base_t::initialize_buff_list_on_vector( persistent_direct_effects ); - } - size_t total_effects_count() override { return base_t::total_effects_count() + persistent_periodic_effects.size() + persistent_direct_effects.size(); @@ -4892,7 +4879,11 @@ struct rip_t final : public trigger_thriving_growth_tbuff.feline_potential ) .set_value( eff.percent() ) .set_eff( &eff ) - .set_idx( UINT32_MAX ); + .set_idx( 1U << callback_list.size() ); + callback_list.push_back( [ this, b = p->buff.feline_potential ]( parse_callback_e cb_type ) { + if ( cb_type == PARSE_CALLBACK_POST_EXECUTE ) + b->expire( this ); + } ); } } @@ -14134,13 +14125,13 @@ void druid_t::parse_action_effects( action_t* action ) _a->parse_effects( mastery.razor_claws ); _a->parse_effects( buff.apex_predators_craving ); _a->parse_effects( buff.berserk_cat ); - _a->parse_effects( buff.coiled_to_spring, CONSUME_BUFF ); + _a->parse_effects( buff.coiled_to_spring, EXPIRE_BUFF ); _a->parse_effects( buff.incarnation_cat ); _a->parse_effects( buff.predator, USE_CURRENT ); - _a->parse_effects( buff.predatory_swiftness, CONSUME_BUFF ); + _a->parse_effects( buff.predatory_swiftness, EXPIRE_BUFF ); _a->parse_effects( talent.taste_for_blood, [ this ] { return buff.tigers_fury->check();}, talent.taste_for_blood->effectN( 2 ).percent() ); - _a->parse_effects( buff.fell_prey, CONSUME_BUFF, effect_mask_t( true ).disable( 2 ) ); + _a->parse_effects( buff.fell_prey, EXPIRE_BUFF, effect_mask_t( true ).disable( 2 ) ); // applies 15% to rampant ferocity (label 2740) via hidden script _a->parse_effects( buff.fell_prey, effect_mask_t( false ).enable( 2 ), buff.fell_prey->data().effectN( 1 ).percent() ); @@ -14162,7 +14153,7 @@ void druid_t::parse_action_effects( action_t* action ) talent.berserk_unchecked_aggression ); _a->parse_effects( buff.incarnation_bear, bear_mask, talent.berserk_ravage, talent.berserk_unchecked_aggression ); - _a->parse_effects( buff.dream_of_cenarius, effect_mask_t( true ).disable( 5 ), CONSUME_BUFF ); + _a->parse_effects( buff.dream_of_cenarius, effect_mask_t( true ).disable( 5 ), EXPIRE_BUFF ); // dot damage is buffed via script so copy da_mult entries to ta_mult // thrash damage buff always applies @@ -14177,12 +14168,12 @@ void druid_t::parse_action_effects( action_t* action ) _a->parse_effects( spec.fury_of_nature, effect_mask_t( false ).enable( 2, 3 ), talent.fury_of_nature->effectN( 1 ).percent() ); - _a->parse_effects( buff.gory_fur, CONSUME_BUFF ); + _a->parse_effects( buff.gory_fur, EXPIRE_BUFF ); _a->parse_effects( buff.rage_of_the_sleeper ); _a->parse_effects( talent.reinvigoration, effect_mask_t( true ).disable( talent.innate_resolve.ok() ? 1 : 2 ) ); _a->parse_effects( buff.tooth_and_claw ); - _a->parse_effects( buff.vicious_cycle_mangle, USE_DEFAULT, CONSUME_BUFF ); - _a->parse_effects( buff.vicious_cycle_maul, USE_DEFAULT, CONSUME_BUFF ); + _a->parse_effects( buff.vicious_cycle_mangle, USE_DEFAULT, EXPIRE_BUFF ); + _a->parse_effects( buff.vicious_cycle_maul, USE_DEFAULT, EXPIRE_BUFF ); _a->parse_effects( buff.guardians_tenacity ); // effects#5 and #6 are ignored regardless of lunar calling @@ -14192,16 +14183,16 @@ void druid_t::parse_action_effects( action_t* action ) _a->parse_effects( buff.abundance ); _a->parse_effects( buff.clearcasting_tree, talent.flash_of_clarity ); _a->parse_effects( buff.incarnation_tree ); - _a->parse_effects( buff.natures_swiftness, talent.natures_splendor, CONSUME_BUFF ); + _a->parse_effects( buff.natures_swiftness, talent.natures_splendor, EXPIRE_BUFF ); // Hero talents - _a->parse_effects( buff.blooming_infusion_damage, CONSUME_BUFF ); - _a->parse_effects( buff.blooming_infusion_heal, CONSUME_BUFF ); - _a->parse_effects( buff.feline_potential, CONSUME_BUFF ); + _a->parse_effects( buff.blooming_infusion_damage, EXPIRE_BUFF ); + _a->parse_effects( buff.blooming_infusion_heal, EXPIRE_BUFF ); + _a->parse_effects( buff.feline_potential, EXPIRE_BUFF ); _a->parse_effects( buff.harmony_of_the_grove ); _a->parse_effects( buff.root_network ); _a->parse_effects( buff.strategic_infusion ); - _a->parse_effects( buff.ursine_potential, CONSUME_BUFF ); + _a->parse_effects( buff.ursine_potential, EXPIRE_BUFF ); } void druid_t::parse_action_target_effects( action_t* action )