From af3054e8cc8a3508aaf837e8bf96e822e4738fc9 Mon Sep 17 00:00:00 2001 From: aezeor Date: Fri, 6 Jun 2025 11:13:24 -0400 Subject: [PATCH 1/3] winning streak removal delayed to final hit of the triggering spell --- engine/class_modules/sc_demon_hunter.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index 53ee97a8820..0644fe12598 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -1571,7 +1571,7 @@ class demon_hunter_action_t : public parse_action_effects_t struct { // General - + // Havoc affect_flags a_fire_inside; affect_flags demonic_presence; @@ -2365,19 +2365,20 @@ struct winning_streak_removal_trigger_t : public BASE { // 2025-02-08 -- Winning Streak! residual keeps the highest value of stacks and won't refresh if the stacks on // the non-residual version are less than the stacks on the residual version. + int stacks = BASE::p()->buff.winning_streak->stack(); // 2025-04-13 -- Winning Streak! removal seems to only happen after the triggering spell has finished dealing all // damage - make_event( *BASE::p()->sim, winning_streak_removal_delay, [ this ] { - int residual_stacks = BASE::p()->buff.winning_streak_residual->stack(); + make_event( *BASE::p()->sim, winning_streak_removal_delay, [ this, stacks ] { int new_stacks = BASE::p()->buff.winning_streak->stack(); + int residual_stacks = BASE::p()->buff.winning_streak_residual->stack(); BASE::p()->buff.winning_streak->expire(); BASE::p()->proc.winning_streak_drop_from_tww2_havoc_2pc->occur(); if ( new_stacks >= residual_stacks ) { BASE::p()->buff.winning_streak_residual->expire(); - BASE::p()->buff.winning_streak_residual->trigger( new_stacks + residual_stacks ); + BASE::p()->buff.winning_streak_residual->trigger( stacks + residual_stacks ); } else { @@ -4897,7 +4898,8 @@ struct auto_attack_damage_t : public burning_blades_trigger_tdebuffs.reavers_mark->up() ) { + if ( target_data->debuffs.reavers_mark->up() ) + { m *= 1.0 + target_data->debuffs.reavers_mark->check_stack_value(); } @@ -5269,6 +5271,8 @@ struct blade_dance_t : public blade_dance_base_t blade_dance_t( demon_hunter_t* p, util::string_view options_str ) : blade_dance_base_t( "blade_dance", p, p->spec.blade_dance, options_str, nullptr ) { + winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); + if ( attacks.empty() ) { attacks.push_back( p->get_background_action( "blade_dance_1", data().effectN( 2 ) ) ); @@ -5319,7 +5323,8 @@ struct death_sweep_t : public blade_dance_base_t death_sweep_t( demon_hunter_t* p, util::string_view options_str ) : blade_dance_base_t( "death_sweep", p, p->spec.death_sweep, options_str, nullptr ) { - thrill_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); + thrill_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); + winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); if ( attacks.empty() ) { @@ -5577,6 +5582,8 @@ struct chaos_strike_t : public chaos_strike_base_t chaos_strike_t( util::string_view name, demon_hunter_t* p, util::string_view options_str = {} ) : chaos_strike_base_t( name, p, p->spec.chaos_strike, options_str ) { + winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 3 ).misc_value1() + 1 ); + if ( attacks.empty() ) { attacks.push_back( p->get_background_action( fmt::format( "{}_damage_1", name ), @@ -5614,6 +5621,8 @@ struct annihilation_t : public demonsurge_trigger_tspec.annihilation, options_str ) { + winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 3 ).misc_value1() + 1 ); + if ( attacks.empty() ) { attacks.push_back( p->get_background_action( fmt::format( "{}_damage_1", name ), From 6fe3620053c3f7e79cbe1dacc561f68b2168a8ad Mon Sep 17 00:00:00 2001 From: aezeor Date: Wed, 11 Jun 2025 11:37:07 -0400 Subject: [PATCH 2/3] treat winning streak as own event --- engine/class_modules/sc_demon_hunter.cpp | 91 ++++++++++++++++-------- 1 file changed, 60 insertions(+), 31 deletions(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index 0644fe12598..f66d89238d8 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -262,6 +262,8 @@ class demon_hunter_t : public parse_player_effects_t event_t* exit_melee_event; // Event to disable melee abilities mid-VR. + event_t* winning_streak_conversion_event; + // Buffs struct buffs_t { @@ -1107,6 +1109,37 @@ class demon_hunter_t : public parse_player_effects_t target_specific_t _target_data; }; +struct winning_streak_conversion_event_t : public event_t +{ + demon_hunter_t& dh; + winning_streak_conversion_event_t( demon_hunter_t* p, timespan_t delay ) : event_t( *p->sim, delay ), dh( *p ) + { + } + const char* name() const override + { + return "winning_streak_conversion"; + } + + void execute() override + { + int residual_stacks = dh.buff.winning_streak_residual->stack(); + int new_stacks = dh.buff.winning_streak->stack(); + dh.buff.winning_streak->expire(); + dh.proc.winning_streak_drop_from_tww2_havoc_2pc->occur(); + + if ( new_stacks >= residual_stacks ) + { + dh.buff.winning_streak_residual->expire(); + dh.buff.winning_streak_residual->trigger( new_stacks + residual_stacks ); + } + else + { + dh.proc.winning_streak_drop_wasted_from_tww2_havoc_2pc->occur(); + } + dh.winning_streak_conversion_event = nullptr; + } +}; + // Delayed Execute Event ==================================================== struct delayed_execute_event_t : public event_t @@ -2365,26 +2398,23 @@ struct winning_streak_removal_trigger_t : public BASE { // 2025-02-08 -- Winning Streak! residual keeps the highest value of stacks and won't refresh if the stacks on // the non-residual version are less than the stacks on the residual version. - int stacks = BASE::p()->buff.winning_streak->stack(); // 2025-04-13 -- Winning Streak! removal seems to only happen after the triggering spell has finished dealing all // damage - make_event( *BASE::p()->sim, winning_streak_removal_delay, [ this, stacks ] { - int new_stacks = BASE::p()->buff.winning_streak->stack(); - int residual_stacks = BASE::p()->buff.winning_streak_residual->stack(); - BASE::p()->buff.winning_streak->expire(); - BASE::p()->proc.winning_streak_drop_from_tww2_havoc_2pc->occur(); - - if ( new_stacks >= residual_stacks ) - { - BASE::p()->buff.winning_streak_residual->expire(); - BASE::p()->buff.winning_streak_residual->trigger( stacks + residual_stacks ); - } - else - { - BASE::p()->proc.winning_streak_drop_wasted_from_tww2_havoc_2pc->occur(); - } - } ); + assert( BASE::p()->winning_streak_conversion_event == nullptr ); + BASE::p()->winning_streak_conversion_event = + make_event( *BASE::sim, BASE::p(), winning_streak_removal_delay ); + } + } + bool ready() override + { + if ( BASE::p()->winning_streak_conversion_event ) + { + return false; + } + else + { + return true; } } }; @@ -5272,7 +5302,6 @@ struct blade_dance_t : public blade_dance_base_t : blade_dance_base_t( "blade_dance", p, p->spec.blade_dance, options_str, nullptr ) { winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); - if ( attacks.empty() ) { attacks.push_back( p->get_background_action( "blade_dance_1", data().effectN( 2 ) ) ); @@ -5323,8 +5352,8 @@ struct death_sweep_t : public blade_dance_base_t death_sweep_t( demon_hunter_t* p, util::string_view options_str ) : blade_dance_base_t( "death_sweep", p, p->spec.death_sweep, options_str, nullptr ) { - thrill_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); - winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); + thrill_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); + // winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); if ( attacks.empty() ) { @@ -5583,7 +5612,6 @@ struct chaos_strike_t : public chaos_strike_base_t : chaos_strike_base_t( name, p, p->spec.chaos_strike, options_str ) { winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 3 ).misc_value1() + 1 ); - if ( attacks.empty() ) { attacks.push_back( p->get_background_action( fmt::format( "{}_damage_1", name ), @@ -5622,7 +5650,6 @@ struct annihilation_t : public demonsurge_trigger_tspec.annihilation, options_str ) { winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 3 ).misc_value1() + 1 ); - if ( attacks.empty() ) { attacks.push_back( p->get_background_action( fmt::format( "{}_damage_1", name ), @@ -7136,6 +7163,7 @@ struct metamorphosis_buff_t : public demon_hunter_buff_t if ( p()->set_bonuses.tww2_havoc_4pc->ok() && ( p()->buff.winning_streak->up() || p()->buff.winning_streak_residual->up() ) ) { + event_t::cancel( p()->winning_streak_conversion_event ); // 2025-02-08 -- Necessary Sacrifice will not be triggered if the number of stacks on Winning Streak! is less than // the number of stacks on Necessary Sacrifice @@ -9522,15 +9550,16 @@ void demon_hunter_t::reset() { base_t::reset(); - soul_fragment_pick_up = nullptr; - frailty_driver = nullptr; - exit_melee_event = nullptr; - next_fragment_spawn = 0; - metamorphosis_health = 0; - frailty_accumulator = 0.0; - shattered_destiny_accumulator = 0.0; - wounded_quarry_accumulator = 0.0; - last_reavers_mark_applied = nullptr; + soul_fragment_pick_up = nullptr; + frailty_driver = nullptr; + exit_melee_event = nullptr; + winning_streak_conversion_event = nullptr; + next_fragment_spawn = 0; + metamorphosis_health = 0; + frailty_accumulator = 0.0; + shattered_destiny_accumulator = 0.0; + wounded_quarry_accumulator = 0.0; + last_reavers_mark_applied = nullptr; for ( size_t i = 0; i < soul_fragments.size(); i++ ) { From 7966d289a5ff749fc830b204162fe4b550b7df9d Mon Sep 17 00:00:00 2001 From: aezeor Date: Wed, 11 Jun 2025 11:54:53 -0400 Subject: [PATCH 3/3] remove assert and uncomment death sweep delay --- engine/class_modules/sc_demon_hunter.cpp | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index f66d89238d8..97edb7cc89a 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -2401,22 +2401,11 @@ struct winning_streak_removal_trigger_t : public BASE // 2025-04-13 -- Winning Streak! removal seems to only happen after the triggering spell has finished dealing all // damage - assert( BASE::p()->winning_streak_conversion_event == nullptr ); + event_t::cancel( BASE::p()->winning_streak_conversion_event ); BASE::p()->winning_streak_conversion_event = make_event( *BASE::sim, BASE::p(), winning_streak_removal_delay ); } } - bool ready() override - { - if ( BASE::p()->winning_streak_conversion_event ) - { - return false; - } - else - { - return true; - } - } }; template @@ -5352,8 +5341,8 @@ struct death_sweep_t : public blade_dance_base_t death_sweep_t( demon_hunter_t* p, util::string_view options_str ) : blade_dance_base_t( "death_sweep", p, p->spec.death_sweep, options_str, nullptr ) { - thrill_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); - // winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); + thrill_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); + winning_streak_removal_delay = timespan_t::from_millis( data().effectN( 5 ).misc_value1() + 1 ); if ( attacks.empty() ) {