From 5c0aa4a05e2ac6c32369963cd81b905317e5b94e Mon Sep 17 00:00:00 2001 From: Aezeor Date: Fri, 7 Nov 2025 23:19:58 -0500 Subject: [PATCH 1/6] hardcast meta when already in meta triggers volatile instinct --- engine/class_modules/sc_demon_hunter.cpp | 34 ++++++++++++++---------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index 9eede248198..cdc2c1c35e0 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -907,6 +907,7 @@ class demon_hunter_t : public parse_player_effects_t const spell_data_t* pierce_the_veil; const spell_data_t* reapers_toll; const spell_data_t* volatile_instinct; + const spell_data_t* demonsurge_meta_trigger; } hero_spec; // Set Bonus effects @@ -3171,7 +3172,7 @@ struct student_of_suffering_trigger_t : public BASE using base_t = student_of_suffering_trigger_t; student_of_suffering_trigger_t( util::string_view n, demon_hunter_t* p, const spell_data_t* s = spell_data_t::nil(), - util::string_view o = {} ) + util::string_view o = {} ) : BASE( n, p, s, o ) { } @@ -4868,6 +4869,13 @@ struct metamorphosis_t : public mass_acceleration_trigger_tbuff.metamorphosis_move->distance_moved = landing_distance; p()->buff.metamorphosis_move->trigger(); } + + if ( p()->talent.scarred.volatile_instinct->ok() ) + { + p()->trigger_demonsurge( + demonsurge_ability::ENTER_META, + timespan_t::from_millis( p()->hero_spec.demonsurge_meta_trigger->effectN( 1 ).misc_value1() ), false ); + } break; case DEMON_HUNTER_VENGEANCE: p()->buff.metamorphosis->trigger(); @@ -5942,7 +5950,8 @@ struct reap_t : public reap_base_t } }; -struct void_ray_t : public student_of_suffering_trigger_t>> +struct void_ray_t + : public student_of_suffering_trigger_t>> { struct void_ray_tick_t : public demon_hunter_spell_t { @@ -8642,6 +8651,11 @@ struct metamorphosis_buff_t : public demon_hunter_buff_t p()->buff.demonsurge_abilities[ demonsurge_ability::ANNIHILATION ]->trigger(); p()->buff.demonsurge_abilities[ demonsurge_ability::DEATH_SWEEP ]->trigger(); p()->buff.demonsurge_demonsurge->trigger(); + + if ( p()->talent.scarred.volatile_instinct->ok() ) + { + p()->trigger_demonsurge( demonsurge_ability::ENTER_META, false ); + } } const timespan_t extend_duration = p()->talent.havoc.demonic->effectN( 1 ).time_value(); @@ -8693,19 +8707,9 @@ struct metamorphosis_buff_t : public demon_hunter_buff_t p()->buff.enduring_torment->expire(); } - if ( p()->talent.scarred.volatile_instinct->ok() ) + if ( p()->talent.scarred.volatile_instinct->ok() && p()->specialization() == DEMON_HUNTER_DEVOURER ) { - switch ( p()->specialization() ) - { - case DEMON_HUNTER_DEVOURER: - p()->buff.volatile_instinct->trigger(); - break; - case DEMON_HUNTER_HAVOC: - p()->trigger_demonsurge( demonsurge_ability::ENTER_META, false ); - break; - default: - break; - } + p()->buff.volatile_instinct->trigger(); } } @@ -10801,6 +10805,8 @@ void demon_hunter_t::init_spells() hero_spec.reapers_toll = spec_talent_spell_lookup( DEMON_HUNTER_DEVOURER, talent.scarred.demonsurge, 1245470 ); hero_spec.volatile_instinct = spec_talent_spell_lookup( DEMON_HUNTER_DEVOURER, talent.scarred.volatile_instinct, 1272462 ); + hero_spec.demonsurge_meta_trigger = + spec_talent_spell_lookup( DEMON_HUNTER_HAVOC, talent.scarred.volatile_instinct, 1238696 ); switch ( specialization() ) { From 796e54c929bc22290f96e9083df06fd06a6f9afe Mon Sep 17 00:00:00 2001 From: Aezeor Date: Fri, 7 Nov 2025 23:39:49 -0500 Subject: [PATCH 2/6] deathsweep uses 700 ms delay for demonsurge --- engine/class_modules/sc_demon_hunter.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index cdc2c1c35e0..1275c494ee3 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -12089,8 +12089,19 @@ void demon_hunter_t::trigger_demonic() const void demon_hunter_t::trigger_demonsurge( const demonsurge_ability ability, const bool check_buff ) { - trigger_demonsurge( ability, timespan_t::from_millis( hero_spec.demonsurge_trigger->effectN( 1 ).misc_value1() ), - check_buff ); + timespan_t delay; + + // TOCHECK: Death sweep currently uses a 700 ms delay, while all other abilities use 450 ms delay. + switch ( ability ) + { + case demonsurge_ability::DEATH_SWEEP: + delay = timespan_t::from_millis( hero_spec.demonsurge_meta_trigger->effectN( 1 ).misc_value1() ); + break; + default: + delay = timespan_t::from_millis( hero_spec.demonsurge_trigger->effectN( 1 ).misc_value1() ); + break; + } + trigger_demonsurge( ability, delay, check_buff ); } void demon_hunter_t::trigger_demonsurge( const demonsurge_ability ability, timespan_t delay, const bool check_buff ) From 85574b6966d6d68b7952fcc6494a5f6df46b9475 Mon Sep 17 00:00:00 2001 From: Aezeor Date: Fri, 14 Nov 2025 18:41:47 -0500 Subject: [PATCH 3/6] first blood attack procs essence break --- engine/class_modules/sc_demon_hunter.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index 1275c494ee3..12238d6095b 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -6813,6 +6813,11 @@ struct blade_dance_base_t first_blood_attacks.back()->trail_of_ruin_dot = trail_of_ruin_dot; } } + + if ( attacks.front() ) + { + first_blood_attacks.front()->first_attack = true; + } } } From 4c7e24a17db8be979294cf6c7ef2b1ef59f28796 Mon Sep 17 00:00:00 2001 From: Aezeor Date: Tue, 18 Nov 2025 20:57:06 -0500 Subject: [PATCH 4/6] typo --- engine/class_modules/sc_demon_hunter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index 12238d6095b..d6db83a2621 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -6814,7 +6814,7 @@ struct blade_dance_base_t } } - if ( attacks.front() ) + if ( first_blood_attacks.front() ) { first_blood_attacks.front()->first_attack = true; } From 3c85a3316b682992702b35ac039c2e4e58cb37da Mon Sep 17 00:00:00 2001 From: Aezeor Date: Wed, 19 Nov 2025 16:14:33 -0500 Subject: [PATCH 5/6] soul pickup fixes and delayed drain type fix --- engine/class_modules/sc_demon_hunter.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index d6db83a2621..fef303f9ee1 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -4928,7 +4928,7 @@ struct pick_up_fragment_t : public demon_hunter_spell_t // Evaluate if_expr to make sure the actor still wants to consume. if ( frag && frag->active() && ( !expr || expr->eval() ) && dh->active.consume_soul_greater ) { - frag->consume( true ); + frag->consume(); } dh->soul_fragment_pick_up = nullptr; @@ -5016,7 +5016,7 @@ struct pick_up_fragment_t : public demon_hunter_spell_t // Havoc Lesser and Greater souls: 8 yards // Havoc Greater Demon soul: 10 yards // TODO: 11.2 Empowered soul for both specs: 6 yards - // TOCHECK: Devourer souls (currently default to previous 6 yard) + // Devourer has a 4 yard pickup range double dtm; if ( frag->is_type( soul_fragment::EMPOWERED_DEMON ) ) { @@ -5033,7 +5033,7 @@ struct pick_up_fragment_t : public demon_hunter_spell_t dtm = std::max( 0.0, frag->get_distance( p() ) - 4.0 ); break; default: - dtm = std::max( 0.0, frag->get_distance( p() ) - 6.0 ); + dtm = std::max( 0.0, frag->get_distance( p() ) - 4.0 ); break; } if ( frag->is_type( soul_fragment::GREATER_DEMON ) ) @@ -11895,7 +11895,7 @@ double demon_hunter_t::fury_state_t::fury_drain_per_second( int stacks ) const double drain = base_fury_drain_per_second( stacks ); bool has_reduced_drain = !p()->in_combat || p()->buff.voidrush->check() || - p()->executing && p()->executing->id == p()->talent.devourer.collapsing_star->id() || + p()->executing && p()->executing->id == p()->spec.collapsing_star_spell->id() || p()->channeling && p()->channeling->id == p()->talent.devourer.void_ray->id(); if ( has_reduced_drain ) @@ -12003,7 +12003,7 @@ void demon_hunter_t::activate_soul_fragment( soul_fragment_t* frag ) { if ( it->is_type( soul_fragment::LESSER ) && it->active() ) { - it->consume( true ); + it->consume(); if ( sim->debug ) { From f36f86fbe77cea55ff4a3feae209b2271f3d015b Mon Sep 17 00:00:00 2001 From: Aezeor Date: Wed, 19 Nov 2025 16:19:48 -0500 Subject: [PATCH 6/6] better format --- engine/class_modules/sc_demon_hunter.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index fef303f9ee1..851e261f138 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -5032,9 +5032,12 @@ struct pick_up_fragment_t : public demon_hunter_spell_t case DEMON_HUNTER_VENGEANCE: dtm = std::max( 0.0, frag->get_distance( p() ) - 4.0 ); break; - default: + case DEMON_HUNTER_DEVOURER: dtm = std::max( 0.0, frag->get_distance( p() ) - 4.0 ); break; + default: + dtm = std::max( 0.0, frag->get_distance( p() ) - 6.0 ); + break; } if ( frag->is_type( soul_fragment::GREATER_DEMON ) ) {