From 6b6063f83d7f83249905f018c3379bfd8737d6ee Mon Sep 17 00:00:00 2001 From: Tom Herbert <18316812+taherbert@users.noreply.github.com> Date: Fri, 26 Jun 2026 10:50:45 -0700 Subject: [PATCH 1/2] [Devourer] Model Voidfall building procs as a pseudo-random distribution The builder proc is a PRD in game, not a flat roll: the per-cast chance starts near 16% after a proc and rises with each miss, averaging the nominal 35% and guaranteed by the 7th cast. Verified against ~24k builder casts from logs. Use accumulated_rng with prd::find_constant, and check the spending buff before the roll so the accumulator only advances on real builder attempts. Mean proc rate and DPS are unchanged; the cadence is less streaky. --- engine/class_modules/sc_demon_hunter.cpp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/engine/class_modules/sc_demon_hunter.cpp b/engine/class_modules/sc_demon_hunter.cpp index c3434a65f7a..a8b896643fe 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -1159,6 +1159,13 @@ class demon_hunter_t : public parse_player_effects_t { } shuffled_rng; + // Accumulated proc objects + struct accumulated_rngs_t + { + // Annihilator + accumulated_rng_t* voidfall = nullptr; + } accumulated_rng; + // Special struct actives_t { @@ -2959,14 +2966,12 @@ struct voidfall_building_trigger_t : public BASE if ( !BASE::dh()->talent.annihilator.voidfall->ok() ) return; - if ( !BASE::rng().roll( BASE::dh()->talent.annihilator.voidfall->effectN( 3 ).percent() ) ) + // can't build (or roll) while spending is up + if ( BASE::dh()->buff.voidfall_spending->up() ) return; - // can't gain building while spending is up - if ( BASE::dh()->buff.voidfall_spending->up() ) - { + if ( !BASE::dh()->accumulated_rng.voidfall->trigger() ) return; - } BASE::dh()->proc.voidfall_from_builder->occur(); BASE::dh()->buff.voidfall_building->trigger( @@ -10442,6 +10447,10 @@ void demon_hunter_t::init_rng() break; } + // Accumulated proc objects + accumulated_rng.voidfall = get_accumulated_rng( + "voidfall", prd::find_constant( talent.annihilator.voidfall->effectN( 3 ).percent() ) ); + player_t::init_rng(); } From 9a35e99d94aa850ca09d6d748880aa57a6903753 Mon Sep 17 00:00:00 2001 From: Richard Harrah <1672786+ToppleTheNun@users.noreply.github.com> Date: Sat, 27 Jun 2026 21:44:20 -0400 Subject: [PATCH 2/2] [DH] remove unnecessary assignment --- 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 a8b896643fe..e46e7d74774 100644 --- a/engine/class_modules/sc_demon_hunter.cpp +++ b/engine/class_modules/sc_demon_hunter.cpp @@ -1163,7 +1163,7 @@ class demon_hunter_t : public parse_player_effects_t struct accumulated_rngs_t { // Annihilator - accumulated_rng_t* voidfall = nullptr; + accumulated_rng_t* voidfall; } accumulated_rng; // Special