From 46816df0aa7dce423ffdf1187dd608f4d7012e3a Mon Sep 17 00:00:00 2001 From: millanzarreta Date: Sun, 26 Jul 2026 09:09:26 +0200 Subject: [PATCH] [Warlock] Implement Impetuous Wrath and 12.1.0 Affliction updates --- engine/class_modules/apl/warlock.cpp | 5 +- engine/class_modules/warlock/sc_warlock.hpp | 1 + .../warlock/sc_warlock_actions.cpp | 89 +++++++++++++++---- .../class_modules/warlock/sc_warlock_init.cpp | 9 +- .../class_modules/warlock/sc_warlock_pets.cpp | 12 +-- 5 files changed, 90 insertions(+), 26 deletions(-) diff --git a/engine/class_modules/apl/warlock.cpp b/engine/class_modules/apl/warlock.cpp index 2832e4b259a..68f1de2a9b1 100644 --- a/engine/class_modules/apl/warlock.cpp +++ b/engine/class_modules/apl/warlock.cpp @@ -92,7 +92,10 @@ void affliction( player_t* p ) default_->add_action( "call_action_list,name=items" ); default_->add_action( "call_action_list,name=soul_harvester,if=hero_tree.soul_harvester" ); default_->add_action( "call_action_list,name=hellcaller,if=hero_tree.hellcaller" ); - default_->add_action( "seed_of_corruption,if=talent.nocturnal_yield&active_enemies>1&buff.nightfall.react&(buff.nightfall.react=buff.nightfall.max_stack|buff.nightfall.remainssim->dbc->wowv() < wowv_t( 12, 1, 0 ) ) + { + default_->add_action( "seed_of_corruption,if=talent.nocturnal_yield&active_enemies>1&buff.nightfall.react&(buff.nightfall.react=buff.nightfall.max_stack|buff.nightfall.remainsadd_action( "malefic_grasp,chain=1,early_chain_if=buff.nightfall.react,if=pet.darkglare.active" ); default_->add_action( "drain_soul,chain=1,early_chain_if=buff.nightfall.react,interrupt_if=tick_time>0.5" ); default_->add_action( "shadow_bolt" ); diff --git a/engine/class_modules/warlock/sc_warlock.hpp b/engine/class_modules/warlock/sc_warlock.hpp index 24dfb1b7b51..e4d1f1006dd 100644 --- a/engine/class_modules/warlock/sc_warlock.hpp +++ b/engine/class_modules/warlock/sc_warlock.hpp @@ -428,6 +428,7 @@ struct warlock_t : public parse_player_effects_t player_talent_t niskaran_methods; player_talent_t potent_soul_shards; player_talent_t nocturnal_yield; + player_talent_t impetuous_wrath; player_talent_t xavius_gambit; // Unstable Affliction Damage Multiplier player_talent_t ravenous_afflictions; diff --git a/engine/class_modules/warlock/sc_warlock_actions.cpp b/engine/class_modules/warlock/sc_warlock_actions.cpp index ce7b5ad50ff..8fce7b559df 100644 --- a/engine/class_modules/warlock/sc_warlock_actions.cpp +++ b/engine/class_modules/warlock/sc_warlock_actions.cpp @@ -929,6 +929,12 @@ using namespace helpers; if ( p()->talents.withering_bolt.ok() ) m *= 1.0 + p()->talents.withering_bolt->effectN( 1 ).percent() * std::min( ( int )( p()->talents.withering_bolt->effectN( 2 ).base_value() ), p()->get_target_data( t )->count_affliction_dots() ); + if ( sim->dbc->wowv() >= wowv_t( 12, 1, 0 ) ) + { + if ( p()->talents.impetuous_wrath.ok() ) + m *= 1.0 + ( td( t )->debuffs.haunt->check() ? p()->talents.impetuous_wrath->effectN( 2 ).percent() : p()->talents.impetuous_wrath->effectN( 1 ).percent() ); + } + return m; } }; @@ -1960,11 +1966,14 @@ using namespace helpers; if ( ( sim->dbc->wowv() < wowv_t( 12, 1, 0 ) ) && p()->talents.patient_zero.ok() ) p()->patient_zero_target = main_seed_target; - // 2026-06-30 Hotfix: Nightfall SoC detonates existing SoC when smart targeting leaves the primary seed on an already seeded target - if ( p()->talents.nocturnal_yield.ok() && p()->buffs.nightfall->check() && mstdata->dots.seed_of_corruption->is_ticking() && mstdata->soc_threshold > 0 ) + if ( p()->sim->dbc->wowv() < wowv_t( 12, 1, 0 ) ) { - mstdata->soc_threshold = 0; - mstdata->dots.seed_of_corruption->cancel(); + // 2026-06-30 Hotfix: Nightfall SoC detonates existing SoC when smart targeting leaves the primary seed on an already seeded target + if ( p()->talents.nocturnal_yield.ok() && p()->buffs.nightfall->check() && mstdata->dots.seed_of_corruption->is_ticking() && mstdata->soc_threshold > 0 ) + { + mstdata->soc_threshold = 0; + mstdata->dots.seed_of_corruption->cancel(); + } } warlock_spell_t::execute(); @@ -1973,17 +1982,20 @@ using namespace helpers; const bool soc_had_prev_succulent_soul = p()->buffs.succulent_soul->check(); - if ( time_to_execute == 0_ms && soul_harvester() && p()->talents.nocturnal_yield.ok() && p()->buffs.nightfall->check() ) + if ( p()->sim->dbc->wowv() < wowv_t( 12, 1, 0 ) ) { - if ( p()->hero.wicked_reaping.ok() ) - p()->proc_actions.wicked_reaping->execute_on_target( main_seed_target ); + if ( time_to_execute == 0_ms && soul_harvester() && p()->talents.nocturnal_yield.ok() && p()->buffs.nightfall->check() ) + { + if ( p()->hero.wicked_reaping.ok() ) + p()->proc_actions.wicked_reaping->execute_on_target( main_seed_target ); - if ( p()->hero.quietus.ok() && p()->hero.shared_fate.ok() ) - p()->proc_actions.shared_fate->execute_on_target( main_seed_target ); + if ( p()->hero.quietus.ok() && p()->hero.shared_fate.ok() ) + p()->proc_actions.shared_fate->execute_on_target( main_seed_target ); - // Feast of Souls is processed after SoC captures its previous Succulent Soul state but before the delayed stack removal - if ( p()->hero.quietus.ok() && p()->hero.feast_of_souls.ok() && p()->prd_rng.feast_of_souls->trigger( execute_state ) ) - p()->feast_of_souls_gain(); + // Feast of Souls is processed after SoC captures its previous Succulent Soul state but before the delayed stack removal + if ( p()->hero.quietus.ok() && p()->hero.feast_of_souls.ok() && p()->prd_rng.feast_of_souls->trigger( execute_state ) ) + p()->feast_of_souls_gain(); + } } if ( soul_harvester() ) @@ -2012,8 +2024,18 @@ using namespace helpers; // NOTE: 2026-02-26 If Nightfall is obtained during the casting of Seed of Corruption, that SoC cast // benefits from the cost reduction but does not consume the effect. (bug?) - if ( p()->talents.nocturnal_yield.ok() && time_to_execute == 0_ms ) - p()->buffs.nightfall->decrement(); + if ( p()->sim->dbc->wowv() < wowv_t( 12, 1, 0 ) ) + { + if ( p()->talents.nocturnal_yield.ok() && time_to_execute == 0_ms ) + p()->buffs.nightfall->decrement(); + } + + // NOTE: 2026-07-26 Seed of Corruption is not consuming Shard Instability buff (bug) + if ( p()->sim->dbc->wowv() >= wowv_t( 12, 1, 0 ) ) + { + if ( !p()->bugs && time_to_execute == 0_ms ) + p()->buffs.shard_instability->decrement(); + } if ( p()->talents.cull_the_weak.ok() ) p()->cooldowns.dark_harvest->adjust( -p()->talents.cull_the_weak->effectN( 1 ).time_value() ); @@ -2240,8 +2262,11 @@ using namespace helpers; void snapshot_state( action_state_t* s, result_amount_type rt ) override { - // NOTE: 2026-02-20 Malefic Grasp does not benefit from the Nightfall damage bonus under any circumstances (bug) - double dmg_mul = p()->bugs ? 0.0 : p()->talents.nightfall_buff->effectN( 2 ).percent(); + // NOTE: 2026-07-26 12.0.7: Malefic Grasp does not benefit from the Nightfall damage bonus under any circumstances (bug) + // NOTE: 2026-07-26 12.1.0: PTR Nightfall does not buff Malefic Grasp damage unless the Necrolyte Teachings hero talent (Soul Harvester) is used (bug) + double dmg_mul = ( sim->dbc->wowv() < wowv_t( 12, 1, 0 ) ) + ? ( p()->bugs ? 0.0 : p()->talents.nightfall_buff->effectN( 2 ).percent() ) + : ( ( p()->bugs && !p()->hero.necrolyte_teachings.ok() ) ? 0.0 : p()->talents.nightfall_buff->effectN( 2 ).percent() ); debug_cast( s )->td_multiplier = 1.0 + ( p()->buffs.nightfall->check() ? dmg_mul : 0.0 ); debug_cast( s )->tick_time_multiplier = 1.0 + ( p()->buffs.nightfall->check() ? p()->talents.nightfall_buff->effectN( 3 ).percent() : 0.0 ); @@ -2310,6 +2335,19 @@ using namespace helpers; } } + double composite_target_multiplier( player_t* t ) const override + { + double m = warlock_spell_t::composite_target_multiplier( t ); + + if ( sim->dbc->wowv() >= wowv_t( 12, 1, 0 ) ) + { + if ( p()->talents.impetuous_wrath.ok() ) + m *= 1.0 + ( td( t )->debuffs.haunt->check() ? p()->talents.impetuous_wrath->effectN( 2 ).percent() : p()->talents.impetuous_wrath->effectN( 1 ).percent() ); + } + + return m; + } + double composite_ta_multiplier( const action_state_t* s ) const override { double m = warlock_spell_t::composite_ta_multiplier( s ); @@ -2449,6 +2487,12 @@ using namespace helpers; if ( p()->talents.withering_bolt.ok() ) m *= 1.0 + p()->talents.withering_bolt->effectN( 1 ).percent() * std::min( ( int )( p()->talents.withering_bolt->effectN( 2 ).base_value() ), td( t )->count_affliction_dots() ); + if ( sim->dbc->wowv() >= wowv_t( 12, 1, 0 ) ) + { + if ( p()->talents.impetuous_wrath.ok() ) + m *= 1.0 + ( td( t )->debuffs.haunt->check() ? p()->talents.impetuous_wrath->effectN( 2 ).percent() : p()->talents.impetuous_wrath->effectN( 1 ).percent() ); + } + return m; } @@ -2521,6 +2565,19 @@ using namespace helpers; { background = dual = true; } + + double composite_target_multiplier( player_t* t ) const override + { + double m = warlock_spell_t::composite_target_multiplier( t ); + + if ( sim->dbc->wowv() >= wowv_t( 12, 1, 0 ) ) + { + if ( p()->talents.impetuous_wrath.ok() ) + m *= 1.0 + ( td( t )->debuffs.haunt->check() ? p()->talents.impetuous_wrath->effectN( 4 ).percent() : p()->talents.impetuous_wrath->effectN( 3 ).percent() ); + } + + return m; + } }; dark_harvest_dmg_t* dark_harvest_dmg; diff --git a/engine/class_modules/warlock/sc_warlock_init.cpp b/engine/class_modules/warlock/sc_warlock_init.cpp index c925bc04377..fd3f301a24a 100644 --- a/engine/class_modules/warlock/sc_warlock_init.cpp +++ b/engine/class_modules/warlock/sc_warlock_init.cpp @@ -146,7 +146,7 @@ namespace warlock talents.nightfall = find_talent_spell( talent_tree::SPECIALIZATION, "Nightfall" ); // Should be ID 108558 talents.nightfall_buff = conditional_spell_lookup( warlock_base.affliction_warlock->ok(), 264571 ); - talents.nightfall_buff_2 = conditional_spell_lookup( warlock_base.affliction_warlock->ok(), 1260279 ); + talents.nightfall_buff_2 = conditional_spell_lookup( warlock_base.affliction_warlock->ok() && ( sim->dbc->wowv() < wowv_t( 12, 1, 0 ) ), 1260279 ); talents.haunt = find_talent_spell( talent_tree::SPECIALIZATION, "Haunt" ); // Should be ID 48181 @@ -208,7 +208,10 @@ namespace warlock talents.potent_soul_shards = find_talent_spell( talent_tree::SPECIALIZATION, "Potent Soul Shards" ); // Should be ID 1259815 - talents.nocturnal_yield = find_talent_spell( talent_tree::SPECIALIZATION, "Nocturnal Yield" ); // Should be ID 1260271 + if ( sim->dbc->wowv() >= wowv_t( 12, 1, 0 ) ) + talents.impetuous_wrath = find_talent_spell( talent_tree::SPECIALIZATION, "Impetuous Wrath" ); // Should be ID 1312998 + else + talents.nocturnal_yield = find_talent_spell( talent_tree::SPECIALIZATION, "Nocturnal Yield" ); // Should be ID 1260271 talents.xavius_gambit = find_talent_spell( talent_tree::SPECIALIZATION, "Xavius' Gambit" ); // Should be ID 416615 @@ -765,7 +768,7 @@ namespace warlock void warlock_t::create_buffs_affliction() { - buffs.nightfall = make_buff( this, "nightfall", talents.nocturnal_yield.ok() ? talents.nightfall_buff_2 : talents.nightfall_buff ); + buffs.nightfall = make_buff( this, "nightfall", ( sim->dbc->wowv() < wowv_t( 12, 1, 0 ) && talents.nocturnal_yield.ok() ) ? talents.nightfall_buff_2 : talents.nightfall_buff ); buffs.darkglare_presence = make_buff( this, "darkglare_presence", talents.darkglare_presence_buff ); diff --git a/engine/class_modules/warlock/sc_warlock_pets.cpp b/engine/class_modules/warlock/sc_warlock_pets.cpp index f927eae5245..0959c373f7a 100644 --- a/engine/class_modules/warlock/sc_warlock_pets.cpp +++ b/engine/class_modules/warlock/sc_warlock_pets.cpp @@ -2670,13 +2670,13 @@ namespace diabolist if ( p()->o()->demonology() ) { - // Added in build: 11.2.0.62253: reduces Diab Demons Damage by 20% for Demonology + // Modify Diab Demons damage for Demonology m *= 1.0 + p()->o()->hero.diabolic_ritual->effectN( 3 ).percent(); } if ( p()->o()->destruction() ) { - // Added in build 11.2.0.62253: Increases Diab Demons damage by 15% for Destruction, missing from Patch Notes. + // Modify Diab Demons damage for Destruction m *= 1.0 + p()->o()->hero.diabolic_ritual->effectN( 4 ).percent(); } @@ -2746,13 +2746,13 @@ namespace diabolist if ( p()->o()->demonology() ) { - // Added in build: 11.2.0.62253: reduces Diab Demons Damage by 20% for Demonology + // Modify Diab Demons damage for Demonology m *= 1.0 + p()->o()->hero.diabolic_ritual->effectN( 3 ).percent(); } if ( p()->o()->destruction() ) { - // Added in build 11.2.0.62253: Increases Diab Demons damage by 15% for Destruction, missing from Patch Notes. + // Modify Diab Demons damage for Destruction m *= 1.0 + p()->o()->hero.diabolic_ritual->effectN( 4 ).percent(); } @@ -2841,13 +2841,13 @@ namespace diabolist if ( p()->o()->demonology() ) { - // Added in build: 11.2.0.62253: reduces Diab Demons Damage by 20% for Demonology + // Modify Diab Demons damage for Demonology m *= 1.0 + p()->o()->hero.diabolic_ritual->effectN( 3 ).percent(); } if ( p()->o()->destruction() ) { - // Added in build 11.2.0.62253: Increases Diab Demons damage by 15% for Destruction, missing from Patch Notes. + // Modify Diab Demons damage for Destruction m *= 1.0 + p()->o()->hero.diabolic_ritual->effectN( 4 ).percent(); }