From 579f113ece5425ad4da4bfca56a0e698c8989807 Mon Sep 17 00:00:00 2001 From: Azevara <46403881+Azevara@users.noreply.github.com> Date: Sun, 28 Jul 2024 01:00:24 -0400 Subject: [PATCH] [Warlock] Malevolence (#9130) --- engine/class_modules/warlock/sc_warlock.cpp | 4 +- engine/class_modules/warlock/sc_warlock.hpp | 10 +++- .../warlock/sc_warlock_actions.cpp | 57 ++++++++++++++++--- .../class_modules/warlock/sc_warlock_init.cpp | 8 +++ 4 files changed, 68 insertions(+), 11 deletions(-) diff --git a/engine/class_modules/warlock/sc_warlock.cpp b/engine/class_modules/warlock/sc_warlock.cpp index 4a35b4bc59d..b2a4991dbb7 100644 --- a/engine/class_modules/warlock/sc_warlock.cpp +++ b/engine/class_modules/warlock/sc_warlock.cpp @@ -110,7 +110,9 @@ warlock_td_t::warlock_td_t( player_t* target, warlock_t& p ) ->set_period( p.hero.blackened_soul_trigger->effectN( 1 ).period() ) ->set_tick_time_behavior( buff_tick_time_behavior::UNHASTED ) ->set_tick_callback( [ this, target ]( buff_t*, int, timespan_t ) - { warlock.proc_actions.blackened_soul->execute_on_target( target ); } ); + { warlock.proc_actions.blackened_soul->execute_on_target( target ); } ) + ->set_tick_behavior( buff_tick_behavior::REFRESH ) + ->set_freeze_stacks( true ); target->register_on_demise_callback( &p, [ this ]( player_t* ) { target_demise(); } ); } diff --git a/engine/class_modules/warlock/sc_warlock.hpp b/engine/class_modules/warlock/sc_warlock.hpp index 1590531ce12..c1c9bc22eb4 100644 --- a/engine/class_modules/warlock/sc_warlock.hpp +++ b/engine/class_modules/warlock/sc_warlock.hpp @@ -524,7 +524,9 @@ struct warlock_t : public player_t player_talent_t seeds_of_their_demise; // TODO: This still has data buffing Blackened Soul player_talent_t mark_of_perotharn; - player_talent_t malevolence; + player_talent_t malevolence; // TODO: While buff is active this guarantees Blackened Soul, but this could be leftover from earlier versions + const spell_data_t* malevolence_buff; + const spell_data_t* malevolence_dmg; // Soul Harvester player_talent_t demonic_soul; @@ -550,6 +552,7 @@ struct warlock_t : public player_t action_t* doom_proc; action_t* rain_of_fire_tick; action_t* blackened_soul; + action_t* malevolence; } proc_actions; struct tier_sets_t @@ -625,6 +628,9 @@ struct warlock_t : public player_t propagate_const infernal_bolt; propagate_const abyssal_dominion; propagate_const ruination; + + // Hellcaller Buffs + propagate_const malevolence; } buffs; // Gains - Many are automatically handled @@ -843,6 +849,6 @@ namespace helpers bool crescendo_check( warlock_t* p ); void nightfall_updater( warlock_t* p, dot_t* d ); - void trigger_blackened_soul( warlock_t* p ); + void trigger_blackened_soul( warlock_t* p, bool malevolence ); } } // namespace warlock diff --git a/engine/class_modules/warlock/sc_warlock_actions.cpp b/engine/class_modules/warlock/sc_warlock_actions.cpp index 0a81f86915d..690e3c3a409 100644 --- a/engine/class_modules/warlock/sc_warlock_actions.cpp +++ b/engine/class_modules/warlock/sc_warlock_actions.cpp @@ -232,7 +232,7 @@ using namespace helpers; if ( hellcaller() && base_shards > 0 && harmful && p()->hero.blackened_soul.ok() ) { - helpers::trigger_blackened_soul( p() ); + helpers::trigger_blackened_soul( p(), false ); } } } @@ -1292,11 +1292,13 @@ using namespace helpers; { warlock_spell_t::impact( s ); - if ( td( s->target )->dots_wither->current_stack() > 1 ) - td( s->target )->dots_wither->decrement( 1 ); + player_t* tar = s->target; - if ( td( s->target )->dots_wither->current_stack() <= 1 ) - make_event( *sim, 0_ms, [ this, s ] { td( s->target )->debuffs_blackened_soul->expire(); } ); + if ( td( tar )->dots_wither->current_stack() > 1 ) + td( tar )->dots_wither->decrement( 1 ); + + if ( td( tar )->dots_wither->current_stack() <= 1 ) + make_event( *sim, 0_ms, [ this, tar ] { td( tar )->debuffs_blackened_soul->expire(); } ); double seeds_rng = 0.15; @@ -1314,6 +1316,34 @@ using namespace helpers; } }; + struct malevolence_damage_t : public warlock_spell_t + { + malevolence_damage_t( warlock_t* p ) + : warlock_spell_t( "Malevolence (Proc)", p, p->hero.malevolence_dmg ) + { background = dual = true; } + }; + + struct malevolence_t : public warlock_spell_t + { + malevolence_t( warlock_t* p, util::string_view options_str ) + : warlock_spell_t( "Malevolence", p, p->hero.malevolence, options_str ) + { + harmful = may_crit = false; + trigger_gcd = p->hero.malevolence_buff->gcd(); + resource_current = RESOURCE_MANA; + base_costs[ RESOURCE_MANA ] = p->hero.malevolence_buff->cost( POWER_MANA ); + } + + void execute() override + { + warlock_spell_t::execute(); + + p()->buffs.malevolence->trigger(); + + helpers::trigger_blackened_soul( p(), true ); + } + }; + // Hellcaller Actions End // Affliction Actions Begin @@ -4097,7 +4127,7 @@ using namespace helpers; } } - void helpers::trigger_blackened_soul( warlock_t* p ) + void helpers::trigger_blackened_soul( warlock_t* p, bool malevolence ) { for ( const auto target : p->sim->target_non_sleeping_list ) { @@ -4108,7 +4138,10 @@ using namespace helpers; if ( !tdata->dots_wither->is_ticking() ) continue; - tdata->dots_wither->increment( 1 ); + tdata->dots_wither->increment( malevolence ? as( p->hero.malevolence->effectN( 1 ).base_value() ) : 1 ); + + if ( p->buffs.malevolence->check() && !malevolence ) + tdata->dots_wither->increment( as( p->hero.malevolence->effectN( 2 ).base_value() ) ); // TOCHECK: Chance for this effect is not in spell data! if ( p->hero.bleakheart_tactics.ok() && p->rng().roll( 0.15 ) ) @@ -4117,7 +4150,8 @@ using namespace helpers; p->procs.bleakheart_tactics->occur(); } - bool collapse = p->hero.seeds_of_their_demise.ok() && target->health_percentage() <= p->hero.seeds_of_their_demise->effectN( 2 ).base_value() ; + bool collapse = p->buffs.malevolence->check(); + collapse = collapse || p->hero.seeds_of_their_demise.ok() && target->health_percentage() <= p->hero.seeds_of_their_demise->effectN( 2 ).base_value() ; collapse = collapse || p->hero.seeds_of_their_demise.ok() && tdata->dots_wither->current_stack() >= as( p->hero.seeds_of_their_demise->effectN( 1 ).base_value() ); if ( collapse ) @@ -4130,6 +4164,9 @@ using namespace helpers; tdata->debuffs_blackened_soul->trigger(); p->procs.blackened_soul->occur(); } + + if ( malevolence ) + p->proc_actions.malevolence->execute_on_target( target ); } } @@ -4342,6 +4379,9 @@ using namespace helpers; if ( action_name == "wither" ) return new wither_t( this, options_str ); + if ( action_name == "malevolence" ) + return new malevolence_t( this, options_str ); + return nullptr; } @@ -4381,6 +4421,7 @@ using namespace helpers; void warlock_t::create_hellcaller_proc_actions() { proc_actions.blackened_soul = new blackened_soul_t( this ); + proc_actions.malevolence = new malevolence_damage_t( this ); } void warlock_t::init_special_effects() diff --git a/engine/class_modules/warlock/sc_warlock_init.cpp b/engine/class_modules/warlock/sc_warlock_init.cpp index 005fa3a5f34..06b5db68f1e 100644 --- a/engine/class_modules/warlock/sc_warlock_init.cpp +++ b/engine/class_modules/warlock/sc_warlock_init.cpp @@ -547,6 +547,10 @@ namespace warlock hero.seeds_of_their_demise = find_talent_spell( talent_tree::HERO, "Seeds of Their Demise" ); // Should be ID 440055 hero.mark_of_perotharn = find_talent_spell( talent_tree::HERO, "Mark of Peroth'arn" ); // Should be ID 440045 + + hero.malevolence = find_talent_spell( talent_tree::HERO, "Malevolence" ); // Should be ID 430014 + hero.malevolence_buff = find_spell( 442726 ); + hero.malevolence_dmg = find_spell( 446285 ); } void warlock_t::init_base_stats() @@ -792,6 +796,10 @@ namespace warlock void warlock_t::create_buffs_hellcaller() { + buffs.malevolence = make_buff( this, "malevolence", hero.malevolence_buff ) + ->set_cooldown( hero.malevolence_buff->cooldown() - 1_s ) + ->set_pct_buff_type( STAT_PCT_BUFF_HASTE ) + ->set_default_value_from_effect( 1 ); } void warlock_t::create_pets()