From ec54ccfb35e5c3040502f273eebe3deb016668c5 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Tue, 23 Jun 2026 18:36:19 +0200 Subject: [PATCH 1/6] [Mage] 12.1 changes + set bonus --- engine/class_modules/sc_mage.cpp | 134 +++++++++++++++++++++++++++++-- 1 file changed, 129 insertions(+), 5 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 960e4c6581a..e83564452ea 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -276,11 +276,13 @@ struct mage_t final : public player_t buff_t* arcane_salvo; buff_t* arcane_surge; buff_t* clearcasting; + buff_t* cumulative_power; buff_t* enlightened; buff_t* evocation; buff_t* intuition; buff_t* overpowered_missiles; buff_t* presence_of_mind; + buff_t* prismatic_bolt; // Fire @@ -581,6 +583,7 @@ struct mage_t final : public player_t // Row 8 player_talent_t touch_of_the_archmage_1; + player_talent_t prismatic_bolt_1; player_talent_t evocation; player_talent_t mana_adept; player_talent_t enlightened; @@ -589,12 +592,14 @@ struct mage_t final : public player_t // Row 9 player_talent_t touch_of_the_archmage_2; + player_talent_t prismatic_bolt_2; player_talent_t prodigious_savant; player_talent_t eureka; player_talent_t arcane_singularity; // Row 10 player_talent_t touch_of_the_archmage_3; + player_talent_t prismatic_bolt_3; player_talent_t charged_missiles; player_talent_t high_voltage; player_talent_t overflowing_insight; @@ -2811,6 +2816,14 @@ struct arcane_barrage_t final : public arcane_mage_spell_t p()->buffs.intuition->expire(); int salvo = p()->buffs.arcane_salvo->check(); + + if ( p()->talents.prismatic_bolt_1.ok() && salvo > 0 ) + { + double chance = salvo * p()->talents.prismatic_bolt_1->effectN( 1 ).percent(); + if ( rng().roll( chance ) ) + p()->buffs.prismatic_bolt->trigger(); + } + if ( p()->buffs.arcane_soul->check() ) { p()->trigger_clearcasting( 1.0, true, true ); @@ -2927,6 +2940,8 @@ struct arcane_blast_t final : public arcane_mage_spell_t if ( p()->buffs.presence_of_mind->up() ) p()->buffs.presence_of_mind->decrement(); + + p()->buffs.cumulative_power->expire(); } double action_multiplier() const override @@ -2934,6 +2949,7 @@ struct arcane_blast_t final : public arcane_mage_spell_t double am = arcane_mage_spell_t::action_multiplier(); am *= arcane_charge_multiplier(); + am *= 1.0 + p()->buffs.cumulative_power->check_stack_value(); return am; } @@ -2945,6 +2961,66 @@ struct arcane_blast_t final : public arcane_mage_spell_t return arcane_mage_spell_t::execute_time(); } + + bool ready() override + { + // Arcane Blast is upgraded into Prismatic Bolt while the buff is up. + if ( p()->buffs.prismatic_bolt->check() ) + return false; + + return arcane_mage_spell_t::ready(); + } +}; + +struct prismatic_bolt_t final : public arcane_mage_spell_t +{ + prismatic_bolt_t( std::string_view n, mage_t* p, std::string_view options_str ) : + arcane_mage_spell_t( n, p, p->find_spell( 1295924 ) ) + { + parse_options( options_str ); + triggers.clearcasting = triggers.spellfire_sphere = triggers.mana_cascade = true; + + aoe = -1; + radius = 8; + reduced_aoe_targets = data().effectN( 4 ).base_value(); + + // The cleave damage is from a separate triggered spell (1295939). + double primary_coef = data().effectN( 1 ).sp_coeff(); + double secondary_coef = p->find_spell( 1295939 )->effectN( 2 ).sp_coeff(); + spell_power_mod.direct = primary_coef; + base_aoe_multiplier = secondary_coef / primary_coef; + } + + bool ready() override + { + if ( !p()->buffs.prismatic_bolt->check() ) + return false; + + return arcane_mage_spell_t::ready(); + } + + void execute() override + { + arcane_mage_spell_t::execute(); + + p()->buffs.prismatic_bolt->expire(); + p()->buffs.cumulative_power->expire(); + + p()->trigger_arcane_charge( as( data().effectN( 3 ).base_value() ) ); + p()->trigger_arcane_salvo( salvo_source, as( p()->talents.expanded_mind->effectN( 3 ).base_value() ) ); + + if ( p()->talents.prismatic_bolt_2.ok() ) + p()->trigger_clearcasting( p()->talents.prismatic_bolt_2->effectN( 1 ).percent() ); + } + + double action_multiplier() const override + { + double am = arcane_mage_spell_t::action_multiplier(); + + am *= 1.0 + p()->buffs.cumulative_power->check_stack_value(); + + return am; + } }; struct arcane_explosion_t final : public arcane_mage_spell_t @@ -2976,7 +3052,10 @@ struct arcane_pulse_t final : public arcane_mage_spell_t parse_options( options_str ); aoe = -1; triggers.clearcasting = triggers.spellfire_sphere = triggers.mana_cascade = !echo; - reduced_aoe_targets = data().effectN( 3 ).base_value(); + // With the 12.1 PTR, pulse went from having 3 effects to 2, removing 'energize (1240466)' + // The reduced-AoE-targets threshold effect went from effect #3 (live 12.0.7) to effect #2 (12.1 PTR) + // TODO: Remove check when 12.1 goes live + reduced_aoe_targets = data().effectN( p->dbc->wowv() >= wowv_t{ 12, 1, 0 } ? 2 : 3 ).base_value(); if ( echo ) { @@ -2997,19 +3076,31 @@ struct arcane_pulse_t final : public arcane_mage_spell_t { double c = arcane_mage_spell_t::cost_pct_multiplier(); - c *= 1.0 + p()->buffs.arcane_charge->check() * p()->buffs.arcane_charge->data().effectN( 5 ).percent(); + // TODO: Remove when 12.1 goes live + if ( p()->dbc->wowv() < wowv_t{ 12, 1, 0 } ) + { + c *= 1.0 + p()->buffs.arcane_charge->check() * p()->buffs.arcane_charge->data().effectN( 5 ).percent(); + } return c; } void execute() override { - p()->benefits.arcane_charge.arcane_pulse->update(); + // TODO: Remove check when 12.1 goes live + if ( p()->dbc->wowv() < wowv_t{ 12, 1, 0 } ) + { + p()->benefits.arcane_charge.arcane_pulse->update(); + } // TODO: radius increase? arcane_mage_spell_t::execute(); - p()->trigger_arcane_charge( as( data().effectN( 2 ).base_value() ) ); + // TODO: Remove check when 12.1 goes live -- in 12.1, pulse generates charges per enemy hit in impact() instead. + if ( p()->dbc->wowv() < wowv_t{ 12, 1, 0 } ) + { + p()->trigger_arcane_charge( as( data().effectN( 2 ).base_value() ) ); + } // In-game, Arcane Pulse internally sets a target it hits as a "Background Target", // resulting in all of Pulse's background effects to be directed towards them. @@ -3020,18 +3111,39 @@ struct arcane_pulse_t final : public arcane_mage_spell_t p()->trigger_arcane_salvo( salvo_source, as( p()->talents.expanded_mind->effectN( 1 ).base_value() ) ); effect_target = rng().range( target_list() ); p()->trigger_splinter( effect_target ); + p()->buffs.cumulative_power->expire(); // does not affect the reverb } if ( arcane_pulse_echo && rng().roll( p()->talents.reverberate->effectN( 1 ).percent() ) ) make_event( *sim, 500_ms, [ this, t = effect_target ] { arcane_pulse_echo->execute_on_target( t ); } ); } + void impact( action_state_t* s ) override + { + arcane_mage_spell_t::impact( s ); + + // 12.1: Arcane Pulse now generates 1 arcane charge per enemy hit + // TODO: Remove check when 12.1 goes live + if ( p()->dbc->wowv() >= wowv_t{ 12, 1, 0 } && result_is_hit( s->result ) ) + p()->trigger_arcane_charge( 1 ); + } + double action_multiplier() const override { double am = arcane_mage_spell_t::action_multiplier(); - am *= arcane_charge_multiplier(); + // TODO: Remove check when 12.1 goes live + if ( p()->dbc->wowv() < wowv_t{ 12, 1, 0 } ) + { + am *= arcane_charge_multiplier(); + } + // TODO: Remove check when 12.1 goes live + if ( p()->dbc->wowv() >= wowv_t{ 12, 1, 0 } ) + { + am *= 1.0 + p()->buffs.cumulative_power->check_stack_value(); + } + return am; } }; @@ -3146,6 +3258,9 @@ struct arcane_missiles_tick_t final : public custom_state_spell_tsets->has_set_bonus( MAGE_ARCANE, MID2, B4 ) ) + p()->buffs.cumulative_power->trigger(); + p()->trigger_arcane_salvo( salvo_source ); p()->trigger_arcane_salvo( crystal_source, as( p()->talents.focusing_crystal->effectN( 2 ).base_value() ), p()->talents.focusing_crystal->effectN( 1 ).percent() ); @@ -5653,6 +5768,7 @@ action_t* mage_t::create_action( std::string_view name, std::string_view options if ( name == "arcane_surge" ) return new arcane_surge_t( name, this, options_str ); if ( name == "evocation" ) return new evocation_t( name, this, options_str ); if ( name == "presence_of_mind" ) return new presence_of_mind_t( name, this, options_str ); + if ( name == "prismatic_bolt" ) return new prismatic_bolt_t( name, this, options_str ); if ( name == "touch_of_the_magi" ) return new touch_of_the_magi_t( name, this, options_str ); // Fire @@ -6020,6 +6136,7 @@ void mage_t::init_spells() talents.impetus = find_talent_spell( talent_tree::SPECIALIZATION, "Impetus" ); // Row 8 talents.touch_of_the_archmage_1 = find_talent_spell( talent_tree::SPECIALIZATION, 1257942 ); + talents.prismatic_bolt_1 = find_talent_spell( talent_tree::SPECIALIZATION, 1295923 ); talents.evocation = find_talent_spell( talent_tree::SPECIALIZATION, "Evocation" ); talents.mana_adept = find_talent_spell( talent_tree::SPECIALIZATION, "Mana Adept" ); talents.enlightened = find_talent_spell( talent_tree::SPECIALIZATION, "Enlightened" ); @@ -6027,11 +6144,13 @@ void mage_t::init_spells() talents.illuminated_thoughts = find_talent_spell( talent_tree::SPECIALIZATION, "Illuminated Thoughts" ); // Row 9 talents.touch_of_the_archmage_2 = find_talent_spell( talent_tree::SPECIALIZATION, 1257947 ); + talents.prismatic_bolt_2 = find_talent_spell( talent_tree::SPECIALIZATION, 1295944 ); talents.prodigious_savant = find_talent_spell( talent_tree::SPECIALIZATION, "Prodigious Savant" ); talents.eureka = find_talent_spell( talent_tree::SPECIALIZATION, "Eureka" ); talents.arcane_singularity = find_talent_spell( talent_tree::SPECIALIZATION, "Arcane Singularity" ); // Row 10 talents.touch_of_the_archmage_3 = find_talent_spell( talent_tree::SPECIALIZATION, 1257950 ); + talents.prismatic_bolt_3 = find_talent_spell( talent_tree::SPECIALIZATION, 1295946 ); talents.charged_missiles = find_talent_spell( talent_tree::SPECIALIZATION, "Charged Missiles" ); talents.high_voltage = find_talent_spell( talent_tree::SPECIALIZATION, "High Voltage" ); talents.overflowing_insight = find_talent_spell( talent_tree::SPECIALIZATION, "Overflowing Insight" ); @@ -6338,6 +6457,11 @@ void mage_t::create_buffs() ->set_cooldown( 0_ms ) ->set_stack_change_callback( [ this ] ( buff_t*, int, int cur ) { if ( cur == 0 ) cooldowns.presence_of_mind->start( cooldowns.presence_of_mind->action ); } ); + buffs.prismatic_bolt = make_buff( this, "prismatic_bolt", find_spell( 1295942 ) ) + ->set_chance( talents.prismatic_bolt_1.ok() ); + buffs.cumulative_power = make_buff( this, "cumulative_power", find_spell( 1296930 ) ) + ->set_default_value_from_effect( 1 ) + ->set_chance( sets->has_set_bonus( MAGE_ARCANE, MID2, B4 ) ); // Fire From 3a87104fc1fa7a3535cb6e7a4afec2a0fc9cac3e Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Tue, 23 Jun 2026 20:11:37 +0200 Subject: [PATCH 2/6] separate aoe spell for prismatic bolt --- engine/class_modules/sc_mage.cpp | 41 ++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index e83564452ea..8f492df4965 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -2972,23 +2972,32 @@ struct arcane_blast_t final : public arcane_mage_spell_t } }; +// The aoe part of Prismatic Bolt is its own spell (1295939) +struct prismatic_bolt_aoe_t final : public arcane_mage_spell_t +{ + prismatic_bolt_aoe_t( std::string_view n, mage_t* p ) : + arcane_mage_spell_t( n, p, p->find_spell( 1295939 ) ) + { + background = true; + aoe = -1; + radius = 8; + reduced_aoe_targets = p->find_spell( 1295924 )->effectN( 4 ).base_value(); + target_filter_callback = secondary_targets_only(); + } +}; + struct prismatic_bolt_t final : public arcane_mage_spell_t { + action_t* aoe_damage; + prismatic_bolt_t( std::string_view n, mage_t* p, std::string_view options_str ) : arcane_mage_spell_t( n, p, p->find_spell( 1295924 ) ) { parse_options( options_str ); triggers.clearcasting = triggers.spellfire_sphere = triggers.mana_cascade = true; - aoe = -1; - radius = 8; - reduced_aoe_targets = data().effectN( 4 ).base_value(); - - // The cleave damage is from a separate triggered spell (1295939). - double primary_coef = data().effectN( 1 ).sp_coeff(); - double secondary_coef = p->find_spell( 1295939 )->effectN( 2 ).sp_coeff(); - spell_power_mod.direct = primary_coef; - base_aoe_multiplier = secondary_coef / primary_coef; + aoe_damage = get_action( "prismatic_bolt_aoe", p ); + add_child( aoe_damage ); } bool ready() override @@ -3013,6 +3022,14 @@ struct prismatic_bolt_t final : public arcane_mage_spell_t p()->trigger_clearcasting( p()->talents.prismatic_bolt_2->effectN( 1 ).percent() ); } + void impact( action_state_t* s ) override + { + arcane_mage_spell_t::impact( s ); + + if ( result_is_hit( s->result ) ) + aoe_damage->execute_on_target( s->target ); + } + double action_multiplier() const override { double am = arcane_mage_spell_t::action_multiplier(); @@ -6435,6 +6452,9 @@ void mage_t::create_buffs() buffs.clearcasting = make_buff( this, "clearcasting", find_spell( 263725 ) ) ->set_default_value_from_effect( 1 ) ->set_chance( spec.clearcasting->ok() ) ; + buffs.cumulative_power = make_buff( this, "cumulative_power", find_spell( 1296930 ) ) + ->set_default_value_from_effect( 1 ) + ->set_chance( sets->has_set_bonus( MAGE_ARCANE, MID2, B4 ) ); buffs.enlightened = make_buff( this, "enlightened", find_spell( 1217242 ) ) ->set_schools_from_effect( 4 ) ->add_invalidate( CACHE_PLAYER_DAMAGE_MULTIPLIER ) @@ -6459,9 +6479,6 @@ void mage_t::create_buffs() { if ( cur == 0 ) cooldowns.presence_of_mind->start( cooldowns.presence_of_mind->action ); } ); buffs.prismatic_bolt = make_buff( this, "prismatic_bolt", find_spell( 1295942 ) ) ->set_chance( talents.prismatic_bolt_1.ok() ); - buffs.cumulative_power = make_buff( this, "cumulative_power", find_spell( 1296930 ) ) - ->set_default_value_from_effect( 1 ) - ->set_chance( sets->has_set_bonus( MAGE_ARCANE, MID2, B4 ) ); // Fire From 5151301efe05216b468378fe5d18746c91752803 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Tue, 23 Jun 2026 21:17:18 +0200 Subject: [PATCH 3/6] use impact_action to simplify --- engine/class_modules/sc_mage.cpp | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 8f492df4965..5fee86882b4 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -2988,16 +2988,14 @@ struct prismatic_bolt_aoe_t final : public arcane_mage_spell_t struct prismatic_bolt_t final : public arcane_mage_spell_t { - action_t* aoe_damage; - prismatic_bolt_t( std::string_view n, mage_t* p, std::string_view options_str ) : arcane_mage_spell_t( n, p, p->find_spell( 1295924 ) ) { parse_options( options_str ); triggers.clearcasting = triggers.spellfire_sphere = triggers.mana_cascade = true; - aoe_damage = get_action( "prismatic_bolt_aoe", p ); - add_child( aoe_damage ); + impact_action = get_action( "prismatic_bolt_aoe", p ); + add_child( impact_action ); } bool ready() override @@ -3022,14 +3020,6 @@ struct prismatic_bolt_t final : public arcane_mage_spell_t p()->trigger_clearcasting( p()->talents.prismatic_bolt_2->effectN( 1 ).percent() ); } - void impact( action_state_t* s ) override - { - arcane_mage_spell_t::impact( s ); - - if ( result_is_hit( s->result ) ) - aoe_damage->execute_on_target( s->target ); - } - double action_multiplier() const override { double am = arcane_mage_spell_t::action_multiplier(); From 7d89cf9baf783d891778faed6955e486f3a0a3f2 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Tue, 30 Jun 2026 18:09:35 +0200 Subject: [PATCH 4/6] use num_targets_hit on pulse + addressed reverb echo bug with impetus + removed redundant checks --- engine/class_modules/sc_mage.cpp | 45 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 5fee86882b4..940377ca9ed 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -582,7 +582,7 @@ struct mage_t final : public player_t player_talent_t impetus; // Row 8 - player_talent_t touch_of_the_archmage_1; + player_talent_t touch_of_the_archmage_1; // TODO: Remove everything related to Touch of the Archmage when 12.1 goes live player_talent_t prismatic_bolt_1; player_talent_t evocation; player_talent_t mana_adept; @@ -2980,7 +2980,6 @@ struct prismatic_bolt_aoe_t final : public arcane_mage_spell_t { background = true; aoe = -1; - radius = 8; reduced_aoe_targets = p->find_spell( 1295924 )->effectN( 4 ).base_value(); target_filter_callback = secondary_targets_only(); } @@ -3052,9 +3051,11 @@ struct arcane_explosion_t final : public arcane_mage_spell_t struct arcane_pulse_t final : public arcane_mage_spell_t { action_t* arcane_pulse_echo = nullptr; + bool is_echo; arcane_pulse_t( std::string_view n, mage_t* p, std::string_view options_str, bool echo = false ) : - arcane_mage_spell_t( n, p, echo ? p->find_spell( 1243460 ) : p->talents.arcane_pulse ) + arcane_mage_spell_t( n, p, echo ? p->find_spell( 1243460 ) : p->talents.arcane_pulse ), + is_echo( echo ) { parse_options( options_str ); aoe = -1; @@ -3103,11 +3104,22 @@ struct arcane_pulse_t final : public arcane_mage_spell_t // TODO: radius increase? arcane_mage_spell_t::execute(); - // TODO: Remove check when 12.1 goes live -- in 12.1, pulse generates charges per enemy hit in impact() instead. + // TODO: Remove check when 12.1 goes live. if ( p()->dbc->wowv() < wowv_t{ 12, 1, 0 } ) { p()->trigger_arcane_charge( as( data().effectN( 2 ).base_value() ) ); } + else + { + // 12.1: Arcane Pulse generates 1 arcane charge per enemy hit. + int charges_per_hit = 1; + + // Bug: with Impetus talented, the Reverberate echo generates double the arcane charges of the main cast + if ( is_echo && p()->bugs && p()->talents.impetus.ok() ) + charges_per_hit = 2; + + p()->trigger_arcane_charge( num_targets_hit * charges_per_hit ); + } // In-game, Arcane Pulse internally sets a target it hits as a "Background Target", // resulting in all of Pulse's background effects to be directed towards them. @@ -3125,16 +3137,6 @@ struct arcane_pulse_t final : public arcane_mage_spell_t make_event( *sim, 500_ms, [ this, t = effect_target ] { arcane_pulse_echo->execute_on_target( t ); } ); } - void impact( action_state_t* s ) override - { - arcane_mage_spell_t::impact( s ); - - // 12.1: Arcane Pulse now generates 1 arcane charge per enemy hit - // TODO: Remove check when 12.1 goes live - if ( p()->dbc->wowv() >= wowv_t{ 12, 1, 0 } && result_is_hit( s->result ) ) - p()->trigger_arcane_charge( 1 ); - } - double action_multiplier() const override { double am = arcane_mage_spell_t::action_multiplier(); @@ -3144,13 +3146,15 @@ struct arcane_pulse_t final : public arcane_mage_spell_t { am *= arcane_charge_multiplier(); } - - // TODO: Remove check when 12.1 goes live - if ( p()->dbc->wowv() >= wowv_t{ 12, 1, 0 } ) + // On 12.1, the main cast no longer benefits from Impetus's arcane charge multiplier, + // but the Reverberate echo still does. This is likely a bug. + else if ( is_echo && p()->bugs && p()->talents.impetus.ok() ) { - am *= 1.0 + p()->buffs.cumulative_power->check_stack_value(); + am *= arcane_charge_multiplier(); } - + + am *= 1.0 + p()->buffs.cumulative_power->check_stack_value(); + return am; } }; @@ -3265,8 +3269,7 @@ struct arcane_missiles_tick_t final : public custom_state_spell_tsets->has_set_bonus( MAGE_ARCANE, MID2, B4 ) ) - p()->buffs.cumulative_power->trigger(); + p()->buffs.cumulative_power->trigger(); p()->trigger_arcane_salvo( salvo_source ); p()->trigger_arcane_salvo( crystal_source, as( p()->talents.focusing_crystal->effectN( 2 ).base_value() ), From b8453f478e98d03b97529d714323f237aef007d1 Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Tue, 30 Jun 2026 20:37:03 +0200 Subject: [PATCH 5/6] use background --- engine/class_modules/sc_mage.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 940377ca9ed..c4c67fbde23 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -3051,11 +3051,9 @@ struct arcane_explosion_t final : public arcane_mage_spell_t struct arcane_pulse_t final : public arcane_mage_spell_t { action_t* arcane_pulse_echo = nullptr; - bool is_echo; arcane_pulse_t( std::string_view n, mage_t* p, std::string_view options_str, bool echo = false ) : - arcane_mage_spell_t( n, p, echo ? p->find_spell( 1243460 ) : p->talents.arcane_pulse ), - is_echo( echo ) + arcane_mage_spell_t( n, p, echo ? p->find_spell( 1243460 ) : p->talents.arcane_pulse ) { parse_options( options_str ); aoe = -1; @@ -3115,7 +3113,7 @@ struct arcane_pulse_t final : public arcane_mage_spell_t int charges_per_hit = 1; // Bug: with Impetus talented, the Reverberate echo generates double the arcane charges of the main cast - if ( is_echo && p()->bugs && p()->talents.impetus.ok() ) + if ( background && p()->bugs && p()->talents.impetus.ok() ) charges_per_hit = 2; p()->trigger_arcane_charge( num_targets_hit * charges_per_hit ); @@ -3148,7 +3146,7 @@ struct arcane_pulse_t final : public arcane_mage_spell_t } // On 12.1, the main cast no longer benefits from Impetus's arcane charge multiplier, // but the Reverberate echo still does. This is likely a bug. - else if ( is_echo && p()->bugs && p()->talents.impetus.ok() ) + else if ( background && p()->bugs && p()->talents.impetus.ok() ) { am *= arcane_charge_multiplier(); } From 3268bb6bf21fee0da7ceb6e81d0265e2c3bd94fb Mon Sep 17 00:00:00 2001 From: Lucasmingus Date: Wed, 1 Jul 2026 03:53:58 +0200 Subject: [PATCH 6/6] address newest changes to orb mastery --- engine/class_modules/sc_mage.cpp | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index c4c67fbde23..9b780a74b7e 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -2637,6 +2637,9 @@ struct arcane_orb_bolt_t final : public arcane_mage_spell_t if ( p()->state.eureka || type == ao_type::ORB_MASTERY ) am *= 1.0 + p()->talents.eureka->effectN( 1 ).percent(); + if ( type == ao_type::ORB_MASTERY ) + am *= p()->talents.orb_mastery->effectN( 2 ).percent(); + return am; } }; @@ -2660,7 +2663,9 @@ struct arcane_orb_t final : public custom_state_spell_tdbc->wowv() >= wowv_t{ 12, 1, 0 } ); std::string_view bolt_name; switch ( type ) @@ -2692,7 +2697,9 @@ struct arcane_orb_t final : public custom_state_spell_ttalents.orb_mastery.ok() ) { - cost_reductions = { p->buffs.clearcasting }; + // TODO: Remove version check when 12.1 goes live + if ( p->dbc->wowv() < wowv_t{ 12, 1, 0 } ) + cost_reductions = { p->buffs.clearcasting }; orb_mastery = get_action( "orb_mastery_arcane_orb", p, "", ao_type::ORB_MASTERY ); add_child( orb_mastery ); } @@ -2700,20 +2707,29 @@ struct arcane_orb_t final : public custom_state_spell_tdata.eureka = p()->talents.orb_mastery.ok() && p()->talents.eureka.ok() && clearcasting_snapshot; + // TODO: Remove version check when 12.1 goes live + cast_state( s )->data.eureka = p()->talents.orb_mastery.ok() && p()->talents.eureka.ok() && + ( p()->dbc->wowv() >= wowv_t{ 12, 1, 0 } || clearcasting_snapshot ); custom_state_spell_t::snapshot_state( s, rt ); } void execute() override { - triggers.clearcasting = !background; - if ( orb_mastery && p()->buffs.clearcasting->check() ) + // TODO: Remove version check when 12.1 goes live + if ( p()->dbc->wowv() < wowv_t{ 12, 1, 0 } ) + triggers.clearcasting = !background; + + // TODO: Remove version check when 12.1 goes live (pre-12.1 requires CC to fire) + if ( orb_mastery && ( p()->dbc->wowv() >= wowv_t{ 12, 1, 0 } || p()->buffs.clearcasting->check() ) ) { int count = as( p()->talents.orb_mastery->effectN( 1 ).base_value() ); make_repeating_event( *sim, 150_ms, [ this, t = target ] { orb_mastery->execute_on_target( t ); }, count ); - clearcasting_snapshot = true; - // Orb Mastery's execution prevents Clearcasting from being triggered with the initial Orb cast -- behaves identically to Barrage with Orb Barrage. - triggers.clearcasting = false; + if ( p()->dbc->wowv() < wowv_t{ 12, 1, 0 } ) + { + clearcasting_snapshot = true; + // Orb Mastery's execution prevents Clearcasting from being triggered with the initial Orb cast. + triggers.clearcasting = false; + } } custom_state_spell_t::execute();