From 160551c5688c495928cf05785e5a4b4e65ed7105 Mon Sep 17 00:00:00 2001 From: Justin Driggers Date: Wed, 25 Mar 2026 18:19:40 -0400 Subject: [PATCH 1/3] [Gear] Gloom-Spattered Dreadscale --- engine/player/unique_gear_midnight.cpp | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index aa3769f2d2c..243fd82d8b1 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -2956,6 +2956,84 @@ void tangle_of_vibrant_vines( special_effect_t& effect ) new dbc_proc_callback_t( effect.player, effect ); } + +// 1260633 driver +// 1260627 damage +// 1263141 absorb +void gloomspattered_dreadscale( special_effect_t& effect ) +{ + struct fractional_absorb_t : public absorb_buff_t + { + double absorb_fraction; + + fractional_absorb_t( player_t* player, std::string_view name, const spell_data_t* spell ) + : absorb_buff_t( player, name, spell ), absorb_fraction( 1.0 ) + { + } + + double consume( double amount, action_state_t* state = nullptr ) override + { + return absorb_buff_t::consume( amount * absorb_fraction, state ); + } + + absorb_buff_t* set_absorb_fraction( double fraction ) + { + absorb_fraction = fraction; + return this; + } + }; + + struct gloomspattered_dreadscale_absorb_t : public absorb_t + { + gloomspattered_dreadscale_absorb_t( player_t* player ) + : absorb_t( "gloomspattered_dreadscale_absorb", player, player->find_spell( 1263141 ) ) + { + harmful = false; + target = player; + } + + absorb_buff_t* create_buff( const action_state_t* state ) override + { + buff_t* b = buff_t::find( state->target, name_str, player ); + if ( b ) + return debug_cast( b ); + + auto buff = make_buff( player, name_str, &data() ); + buff->set_absorb_fraction( data().effectN( 2 ).percent() ); + buff->set_absorb_source( stats ); + + return buff; + } + + void execute_with_amount( double amount ) + { + base_dd_min = base_dd_max = amount; + absorb_t::execute(); + } + }; + + struct gloomspattered_dreadscale_t : public generic_aoe_proc_t + { + gloomspattered_dreadscale_absorb_t* absorb; + + gloomspattered_dreadscale_t( const special_effect_t& e ) + : generic_aoe_proc_t( e, "gloomspattered_dreadscale_damage", e.driver(), true ), + absorb( new gloomspattered_dreadscale_absorb_t( e.player ) ) + { + // Most spell data comes from the driver, but damage is determined by another spell + base_dd_min = base_dd_max = e.player->find_spell( 1260627 )->effectN( 1 ).average( e ); + } + + void execute() override + { + generic_aoe_proc_t::execute(); + + absorb->execute_with_amount( execute_state->result_amount * execute_state->n_targets ); + } + }; + + effect.execute_action = create_proc_action( "gloomspattered_dreadscale", effect ); +} } // namespace trinkets namespace weapons @@ -3588,6 +3666,7 @@ void register_special_effects() register_special_effect( 1247311, DISABLED_EFFECT ); // Drum of Renewed Bonds on use register_special_effect( 1253120, trinkets::glorious_crusaders_keepsake ); register_special_effect( 1253112, trinkets::sylvan_wakrapuku ); + register_special_effect( 1260633, trinkets::gloomspattered_dreadscale ); // Weapons register_special_effect( { 1253357, 1253359 }, weapons::torments_duality ); // umbral sabre & radiant foil register_special_effect( 1266257, weapons::lightless_lament ); From 7619078fb56ad4bb0c6f15ccbe4bbdb07602c10b Mon Sep 17 00:00:00 2001 From: Justin Driggers Date: Wed, 25 Mar 2026 21:24:20 -0400 Subject: [PATCH 2/3] * Remove absorb action * Accumulate shield amount on impact and trigger and reset on execute * Get spell coefficient from on-equip effect --- engine/player/unique_gear_midnight.cpp | 53 ++++++++++---------------- 1 file changed, 20 insertions(+), 33 deletions(-) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index 243fd82d8b1..8daf75c9276 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -2983,52 +2983,38 @@ void gloomspattered_dreadscale( special_effect_t& effect ) } }; - struct gloomspattered_dreadscale_absorb_t : public absorb_t + struct gloomspattered_dreadscale_t : public generic_aoe_proc_t { - gloomspattered_dreadscale_absorb_t( player_t* player ) - : absorb_t( "gloomspattered_dreadscale_absorb", player, player->find_spell( 1263141 ) ) - { - harmful = false; - target = player; - } + buff_t* absorb; + double shield_amount; - absorb_buff_t* create_buff( const action_state_t* state ) override + gloomspattered_dreadscale_t( const special_effect_t& effect ) + : generic_aoe_proc_t( effect, "gloomspattered_dreadscale_damage", effect.driver(), true ), shield_amount( 0 ) { - buff_t* b = buff_t::find( state->target, name_str, player ); - if ( b ) - return debug_cast( b ); + auto equip = find_special_effect( effect.player, 1260627 ); + assert( equip && "Gloom-Spattered Dreadscale missing equip effect" ); - auto buff = make_buff( player, name_str, &data() ); - buff->set_absorb_fraction( data().effectN( 2 ).percent() ); - buff->set_absorb_source( stats ); + base_dd_min = base_dd_max = equip->driver()->effectN( 1 ).average( effect ); - return buff; + auto absorb_spell = effect.player->find_spell( 1263141 ); + absorb = create_buff( effect.player, "gloomspattered_dreadscale_absorb", absorb_spell ) + ->set_absorb_fraction( absorb_spell->effectN( 2 ).percent() ) + ->set_absorb_source( effect.player->get_stats( "gloomspattered_dreadscale_absorb" ) ); } - void execute_with_amount( double amount ) + void execute() override { - base_dd_min = base_dd_max = amount; - absorb_t::execute(); - } - }; - - struct gloomspattered_dreadscale_t : public generic_aoe_proc_t - { - gloomspattered_dreadscale_absorb_t* absorb; + generic_aoe_proc_t::execute(); - gloomspattered_dreadscale_t( const special_effect_t& e ) - : generic_aoe_proc_t( e, "gloomspattered_dreadscale_damage", e.driver(), true ), - absorb( new gloomspattered_dreadscale_absorb_t( e.player ) ) - { - // Most spell data comes from the driver, but damage is determined by another spell - base_dd_min = base_dd_max = e.player->find_spell( 1260627 )->effectN( 1 ).average( e ); + absorb->trigger( -1, shield_amount ); + shield_amount = 0; } - void execute() override + void impact( action_state_t* state ) override { - generic_aoe_proc_t::execute(); + generic_aoe_proc_t::impact( state ); - absorb->execute_with_amount( execute_state->result_amount * execute_state->n_targets ); + shield_amount += state->result_amount; } }; @@ -3667,6 +3653,7 @@ void register_special_effects() register_special_effect( 1253120, trinkets::glorious_crusaders_keepsake ); register_special_effect( 1253112, trinkets::sylvan_wakrapuku ); register_special_effect( 1260633, trinkets::gloomspattered_dreadscale ); + register_special_effect( 1260627, DISABLED_EFFECT ); // Gloom-Spattered Dreadscale Passive Driver // Weapons register_special_effect( { 1253357, 1253359 }, weapons::torments_duality ); // umbral sabre & radiant foil register_special_effect( 1266257, weapons::lightless_lament ); From 53f4643aec57436e96b2cba58a5bc74c9ba628ed Mon Sep 17 00:00:00 2001 From: Justin Driggers Date: Wed, 25 Mar 2026 22:48:58 -0400 Subject: [PATCH 3/3] * Fix absorb reporting --- engine/player/unique_gear_midnight.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/engine/player/unique_gear_midnight.cpp b/engine/player/unique_gear_midnight.cpp index 8daf75c9276..dc9ff5dec60 100644 --- a/engine/player/unique_gear_midnight.cpp +++ b/engine/player/unique_gear_midnight.cpp @@ -2966,8 +2966,9 @@ void gloomspattered_dreadscale( special_effect_t& effect ) { double absorb_fraction; - fractional_absorb_t( player_t* player, std::string_view name, const spell_data_t* spell ) - : absorb_buff_t( player, name, spell ), absorb_fraction( 1.0 ) + fractional_absorb_t( player_t* player, std::string_view name, const spell_data_t* spell, + const item_t* item = nullptr ) + : absorb_buff_t( player, name, spell, item ), absorb_fraction( 1.0 ) { } @@ -2989,7 +2990,7 @@ void gloomspattered_dreadscale( special_effect_t& effect ) double shield_amount; gloomspattered_dreadscale_t( const special_effect_t& effect ) - : generic_aoe_proc_t( effect, "gloomspattered_dreadscale_damage", effect.driver(), true ), shield_amount( 0 ) + : generic_aoe_proc_t( effect, "gloomspattered_dreadscale", effect.driver(), true ), shield_amount( 0 ) { auto equip = find_special_effect( effect.player, 1260627 ); assert( equip && "Gloom-Spattered Dreadscale missing equip effect" ); @@ -2997,9 +2998,9 @@ void gloomspattered_dreadscale( special_effect_t& effect ) base_dd_min = base_dd_max = equip->driver()->effectN( 1 ).average( effect ); auto absorb_spell = effect.player->find_spell( 1263141 ); - absorb = create_buff( effect.player, "gloomspattered_dreadscale_absorb", absorb_spell ) + absorb = create_buff( effect.player, name(), absorb_spell ) ->set_absorb_fraction( absorb_spell->effectN( 2 ).percent() ) - ->set_absorb_source( effect.player->get_stats( "gloomspattered_dreadscale_absorb" ) ); + ->set_absorb_source( effect.player->get_stats( "gloomspattered_dreadscale_absorb", this ) ); } void execute() override