diff --git a/engine/class_modules/warlock/sc_warlock.cpp b/engine/class_modules/warlock/sc_warlock.cpp index 9d7c914272e..9df3013abbd 100644 --- a/engine/class_modules/warlock/sc_warlock.cpp +++ b/engine/class_modules/warlock/sc_warlock.cpp @@ -216,7 +216,6 @@ warlock_t::warlock_t( sim_t* sim, util::string_view name, race_e r ) eye_explosion_instanced_bug_cb( false ), eye_explosion_instanced_bug_sb( false ), eye_explosion_instanced_bug_rof( true ), - fel_armaments_extra_effect_bug( false ), tyrant_antoran_armaments_target_mul( 1.0 ) { cooldowns.haunt = get_cooldown( "haunt" ); @@ -268,14 +267,6 @@ warlock_t::warlock_t( sim_t* sim, util::string_view name, race_e r ) assert( ( buffs.hellbent_commander->check() == expected_stacks ) && "Incorrent Demon Count for Hellbent Commander" ); } - if ( bugs && talents.fel_armaments.ok() ) - { - // On each Heartbeat, the player periodically applies a hidden Fel Armaments aura to the Felguard, triggering procs - auto active_pet = warlock_pet_list.active; - if ( active_pet && active_pet->pet_type == PET_FELGUARD ) - trigger_aura_applied_callbacks( proc_data_entries.fel_armaments_2, active_pet ); - } - for ( auto pet : active_pets ) { auto lock_pet = dynamic_cast( pet ); @@ -571,9 +562,6 @@ std::string warlock_t::create_profile( save_e stype ) if ( !eye_explosion_instanced_bug_rof ) profile_str += "warlock.eye_explosion_instanced_bug_rof=" + util::to_string( as( eye_explosion_instanced_bug_rof ) ) + "\n"; - if ( fel_armaments_extra_effect_bug ) - profile_str += - "warlock.fel_armaments_extra_effect_bug" + util::to_string( as( fel_armaments_extra_effect_bug ) ) + "\n"; if ( tyrant_antoran_armaments_target_mul < 1.0 ) profile_str += "warlock.tyrant_antoran_armaments_target_mul=" + util::to_string( tyrant_antoran_armaments_target_mul ) + "\n"; @@ -596,7 +584,6 @@ void warlock_t::copy_from( player_t* source ) eye_explosion_instanced_bug_cb = p->eye_explosion_instanced_bug_cb; eye_explosion_instanced_bug_sb = p->eye_explosion_instanced_bug_sb; eye_explosion_instanced_bug_rof = p->eye_explosion_instanced_bug_rof; - fel_armaments_extra_effect_bug = p->fel_armaments_extra_effect_bug; tyrant_antoran_armaments_target_mul = p->tyrant_antoran_armaments_target_mul; rng_settings = p->rng_settings; diff --git a/engine/class_modules/warlock/sc_warlock.hpp b/engine/class_modules/warlock/sc_warlock.hpp index 289182055e7..c71cfb4f89a 100644 --- a/engine/class_modules/warlock/sc_warlock.hpp +++ b/engine/class_modules/warlock/sc_warlock.hpp @@ -467,7 +467,6 @@ struct warlock_t : public parse_player_effects_t player_talent_t rune_of_shadows; player_talent_t carnivorous_stalkers; // Chance for Dreadstalkers to perform additional Dreadbites player_talent_t fel_armaments; - const spell_data_t* fel_armaments_2; // Another effect of Fel Armaments that, due to a bug, is always active player_talent_t imp_gang_boss; const spell_data_t* imp_gang_boss_buff; // Buff on Wild Imps @@ -795,7 +794,6 @@ struct warlock_t : public parse_player_effects_t proc_data_t agony_energize; proc_data_t demonbolt_energize; proc_data_t incinerate_energize; - proc_data_t fel_armaments_2; proc_data_t marked_soul; } proc_data_entries; @@ -1116,7 +1114,6 @@ struct warlock_t : public parse_player_effects_t bool eye_explosion_instanced_bug_cb; bool eye_explosion_instanced_bug_sb; bool eye_explosion_instanced_bug_rof; - bool fel_armaments_extra_effect_bug; double tyrant_antoran_armaments_target_mul; warlock_t( sim_t* sim, util::string_view name, race_e r ); diff --git a/engine/class_modules/warlock/sc_warlock_actions.cpp b/engine/class_modules/warlock/sc_warlock_actions.cpp index 7a4eab634f3..25fc799d40a 100644 --- a/engine/class_modules/warlock/sc_warlock_actions.cpp +++ b/engine/class_modules/warlock/sc_warlock_actions.cpp @@ -1426,13 +1426,7 @@ using namespace helpers; initial_stacks += ( int )( p()->talents.sudden_onset->effectN( 2 ).base_value() ); if ( active_4pc() ) - { - // NOTE: 2026-04-24 Tier set is only applying 1 additional stack to Agony on initial cast if Sudden Onset is not talented (bug?) - if ( p()->bugs && !p()->talents.sudden_onset.ok() ) - initial_stacks += ( int )( p()->tier.wl_affliction_12_0_class_set_4pc->effectN( 1 ).base_value() * 0.5 ); - else - initial_stacks += ( int )( p()->tier.wl_affliction_12_0_class_set_4pc->effectN( 1 ).base_value() ); - } + initial_stacks += ( int )( p()->tier.wl_affliction_12_0_class_set_4pc->effectN( 1 ).base_value() ); int delta_stacks = initial_stacks - td( execute_state->target )->dots.agony->current_stack(); @@ -1470,19 +1464,27 @@ using namespace helpers; void execute() override { - // NOTE: 2026-02-20 Currently ingame a UA applied by Fatal Echoes also processes/consumes the UA 'execute' effects: + // NOTE: 2026-04-29 Currently ingame a UA applied by Fatal Echoes also processes/consumes some UA 'execute' effects: // - Succulent Soul: consumes a stack and triggers its effects (Demonic Soul dmg and Manifested Avarice rng proc) // - Cull the Weak: reduces the cooldown of Dark Harvest - // - Shard Instability: consumes a stack but does nothing (bug?) because the Fatal Echoes UA is already free and instant - // - Hellcaller Blackened Soul: increments wither stacks + // - Hellcaller Blackened Soul: increments Wither stacks + // - Shard Instability: unaffected; Fatal Echoes does not consume a stack of this buff warlock_spell_t::execute(); if ( p()->talents.cull_the_weak.ok() ) p()->cooldowns.dark_harvest->adjust( -p()->talents.cull_the_weak->effectN( 1 ).time_value() ); - // Seems that Shard Instability buff takes effect (and is consumed) even if it is obtained while Unstable Affliction is being cast (bug?) - p()->buffs.shard_instability->decrement(); + // NOTE: 2026-04-29 If Shard Instability buff is gained during the casting of Unstable Affliction, that UA cast benefits from the cost + // reduction but does not consume the effect (bug?). As expected, a Fatal Echoes UA proc does not consume it either. + if ( p()->talents.shard_instability.ok() && time_to_execute == 0_ms && !is_fatal_echoes_execute ) + { + // NOTE: 2026-04-29 Unstable Affliction consumes all Shard Instability stacks at once (bug) + if ( p()->bugs ) + p()->buffs.shard_instability->expire(); + else + p()->buffs.shard_instability->decrement(); + } if ( soul_harvester() && p()->buffs.succulent_soul->check() ) { @@ -1539,14 +1541,19 @@ using namespace helpers; { p()->procs.fatal_echoes->occur(); make_event( sim, 1_ms, [ this, t = d->target ] { - const bool prev_ua_ticking = td( t )->dots.unstable_affliction->is_ticking(); - this->set_target( t ); - this->is_fatal_echoes_execute = true; - this->execute(); - this->is_fatal_echoes_execute = false; - // When UA is applied by Fatal Echoes, Cascading Calamity is also triggered - if ( p()->talents.cascading_calamity.ok() && !prev_ua_ticking ) - p()->buffs.cascading_calamity->trigger(); + // NOTE: 2026-04-29 Fatal Echoes proc needs a Soul Shard to trigger (bug) + if ( !p()->bugs || p()->resources.current[ RESOURCE_SOUL_SHARD ] >= 1.0 ) + { + const bool prev_ua_ticking = td( t )->dots.unstable_affliction->is_ticking(); + this->set_target( t ); + this->time_to_execute = 0_ms; + this->is_fatal_echoes_execute = true; + this->execute(); + this->is_fatal_echoes_execute = false; + // When UA is applied by Fatal Echoes, Cascading Calamity is also triggered + if ( p()->talents.cascading_calamity.ok() && !prev_ua_ticking ) + p()->buffs.cascading_calamity->trigger(); + } } ); } } @@ -1585,7 +1592,8 @@ using namespace helpers; double cost_pct_multiplier() const override { - if ( is_fatal_echoes_execute ) + // NOTE: 2026-04-29 Fatal Echoes proc consumes a Soul Shard (bug) + if ( !p()->bugs && is_fatal_echoes_execute ) return 0.0; return warlock_spell_t::cost_pct_multiplier(); @@ -1920,8 +1928,8 @@ using namespace helpers; base_dd_min = base_dd_max = 0; spell_power_mod.direct = 0; - // NOTE: 2026-02-20 DoT (Malefic Grasp) extra ticks are not affected by Death's Embrace (bug?) - affected_by.deaths_embrace = !p->bugs && p->talents.deaths_embrace.ok(); + // DoT (Malefic Grasp) extra ticks are affected by Death's Embrace + affected_by.deaths_embrace = p->talents.deaths_embrace.ok(); } void impact( action_state_t* s ) override @@ -2013,7 +2021,7 @@ using namespace helpers; extra_tick_mul( p->talents.malefic_grasp_2->effectN( 2 ).percent() ) { channeled = true; - // NOTE: 2026-02-20 Malefic Grasp extra ticks are not affected by Death's Embrace (bug?) + // NOTE: 2026-04-29 Malefic Grasp ticks are not affected by Death's Embrace (bug?) affected_by.deaths_embrace = !p->bugs && p->talents.deaths_embrace.ok(); if ( p->talents.cunning_cruelty.ok() ) @@ -2080,8 +2088,7 @@ using namespace helpers; if ( soul_harvester() && p()->buffs.nightfall->check() ) { - // NOTE: 2026-03-21 Malefic Grasp consumes Nightfall without triggering Wicked Reaping (bug) - if ( !p()->bugs && p()->hero.wicked_reaping.ok() ) + if ( p()->hero.wicked_reaping.ok() ) p()->proc_actions.wicked_reaping->execute_on_target( target ); if ( p()->hero.quietus.ok() && p()->hero.shared_fate.ok() ) @@ -5081,7 +5088,7 @@ using namespace helpers; struct summon_mother_of_chaos_t : public warlock_spell_t { summon_mother_of_chaos_t( warlock_t* p ) - : warlock_spell_t( "Summon Mother of Chaos (Summon)", p, p->hero.summon_mother ) + : warlock_spell_t( "Summon Mother of Chaos", p, p->hero.summon_mother ) { harmful = may_crit = false; background = true; @@ -5322,11 +5329,13 @@ using namespace helpers; dot->decrement( 1 ); assert( ( dot->is_ticking() && dot->current_stack() > 0 ) && "UA stack decrement event should not cancel the DoT" ); + // NOTE: 2026-04-29 Fatal Echoes proc needs a Soul Shard to trigger (bug) // if ( p->talents.fatal_echoes.ok() && !target->is_sleeping() && dot->is_ticking() && dot->current_stack() > 0 && p->prd_rng.fatal_echoes->trigger() ) - if ( p->talents.fatal_echoes.ok() && !target->is_sleeping() && p->prd_rng.fatal_echoes->trigger() ) + if ( p->talents.fatal_echoes.ok() && !target->is_sleeping() && p->prd_rng.fatal_echoes->trigger() && ( !p->bugs || p->resources.current[ RESOURCE_SOUL_SHARD ] >= 1.0 ) ) { p->procs.fatal_echoes->occur(); dot->current_action->set_target( target ); + dot->current_action->time_to_execute = 0_ms; debug_cast( dot->current_action )->is_fatal_echoes_execute = true; dot->current_action->execute(); debug_cast( dot->current_action )->is_fatal_echoes_execute = false; diff --git a/engine/class_modules/warlock/sc_warlock_init.cpp b/engine/class_modules/warlock/sc_warlock_init.cpp index 00a8c314877..ac6cd41e6b5 100644 --- a/engine/class_modules/warlock/sc_warlock_init.cpp +++ b/engine/class_modules/warlock/sc_warlock_init.cpp @@ -125,10 +125,6 @@ namespace warlock // NOTE: 2026-02-17 Mark of Perotharn is being applied twice in what appears to be a bug if ( bugs ) parse_passive_effects( hero.mark_of_perotharn, true ); - - // NOTE: 2026-03-21 An additional effect of Fel Armaments talent is applied even if the talent is not selected (bug) - if ( demonology() && bugs && ( talents.fel_armaments.ok() || fel_armaments_extra_effect_bug ) ) - parse_passive_effects( talents.fel_armaments_2, true ); } void warlock_t::init_spells_affliction() @@ -287,7 +283,6 @@ namespace warlock talents.carnivorous_stalkers = find_talent_spell( talent_tree::SPECIALIZATION, "Carnivorous Stalkers" ); // Should be ID 386194; talents.fel_armaments = find_talent_spell( talent_tree::SPECIALIZATION, "Fel Armaments" ); // Should be ID 1263935 - talents.fel_armaments_2 = conditional_spell_lookup( warlock_base.demonology_warlock->ok() && bugs, 1263938 ); // Always active due to a bug talents.imp_gang_boss = find_talent_spell( talent_tree::SPECIALIZATION, "Imp Gang Boss" ); // Should be ID 1250768 @@ -685,7 +680,6 @@ namespace warlock proc_data_entries.agony_energize = talents.agony_energize; proc_data_entries.demonbolt_energize = talents.demonbolt_energize; proc_data_entries.incinerate_energize = warlock_base.incinerate_energize; - proc_data_entries.fel_armaments_2 = talents.fel_armaments_2; proc_data_entries.marked_soul = hero.marked_soul; } @@ -1636,8 +1630,6 @@ namespace warlock add_option( opt_deprecated( "eye_explosion_instanced_bug_sb", "warlock.eye_explosion_instanced_bug_sb" ) ); add_option( opt_bool( "warlock.eye_explosion_instanced_bug_rof", eye_explosion_instanced_bug_rof ) ); add_option( opt_deprecated( "eye_explosion_instanced_bug_rof", "warlock.eye_explosion_instanced_bug_rof" ) ); - add_option( opt_bool( "warlock.fel_armaments_extra_effect_bug", fel_armaments_extra_effect_bug ) ); - add_option( opt_deprecated( "fel_armaments_extra_effect_bug", "warlock.fel_armaments_extra_effect_bug" ) ); add_option( opt_float( "warlock.tyrant_antoran_armaments_target_mul", tyrant_antoran_armaments_target_mul, 0.0, 1.0 )); add_option( opt_deprecated( "tyrant_antoran_armaments_target_mul", "warlock.tyrant_antoran_armaments_target_mul" ) ); diff --git a/engine/class_modules/warlock/sc_warlock_pets.hpp b/engine/class_modules/warlock/sc_warlock_pets.hpp index 6587face1ac..c492d21a957 100644 --- a/engine/class_modules/warlock/sc_warlock_pets.hpp +++ b/engine/class_modules/warlock/sc_warlock_pets.hpp @@ -633,7 +633,7 @@ struct desperate_soul_t : public warlock_pet_t { int wraths; - desperate_soul_t( warlock_t*, util::string_view = "desperate_souls" ); + desperate_soul_t( warlock_t*, util::string_view = "desperate_soul" ); void arise() override; action_t* create_action( util::string_view , util::string_view ) override; };