From 3a67df780270b2e98414cd6d5b37ee747ed396b1 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Mon, 5 May 2025 20:27:55 -0400 Subject: [PATCH 01/11] Fix Intuition's Base Proc Chance --- engine/class_modules/sc_mage.cpp | 60 ++++++++++++++------------------ 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 8c55188b1cd..1237b50a8aa 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -1924,6 +1924,7 @@ struct mage_spell_t : public spell_t { bool chill = false; bool clearcasting = false; + bool intuition_blp = false; bool from_the_ashes = false; bool frostfire_infusion = true; bool frostfire_mastery = true; @@ -2274,9 +2275,6 @@ struct mage_spell_t : public spell_t if ( triggers.frostfire_mastery && harmful && !background ) trigger_frostfire_mastery(); - - if ( !background && harmful ) - trigger_intuition( false ); } void impact( action_state_t* s ) override @@ -2464,28 +2462,6 @@ struct mage_spell_t : public spell_t p()->state.trigger_glorious_incandescence = false; } - - - // The blp_exclude_initial param controls whether the spell is allowed to start a new BLP "chain" - // by itself. Some spells (orbs from Orb Barrage, AE echoes) only contribute to ongoing chains. - void trigger_intuition( bool blp_exclude_initial ) - { - if ( !p()->talents.intuition.ok() || p()->buffs.intuition->check() ) - return; - - constexpr int blp_threshold = 11; - - if ( p()->state.intuition_blp_count > 0 || !blp_exclude_initial ) - p()->state.intuition_blp_count += 1; - - if ( p()->state.intuition_blp_count >= blp_threshold - || ( !background && harmful && rng().roll( p()->talents.intuition->effectN( 1 ).percent() ) ) ) - { - // Needs to be triggered with a delay so that ABar doesn't eat its own proc - make_event( *sim, [ this ] { p()->buffs.intuition->trigger(); } ); - p()->state.intuition_blp_count = 0; - } - } }; double mage_spell_state_t::composite_crit_chance() const @@ -2620,6 +2596,25 @@ struct arcane_mage_spell_t : public mage_spell_t return 1.0 + p()->buffs.arcane_charge->check() * ( base + mastery ); } + void execute() override + { + mage_spell_t::execute(); + + if ( p()->talents.intuition.ok() && !p()->buffs.intuition->check() && triggers.intuition_blp ) + { + constexpr int blp_threshold = 11; + + p()->state.intuition_blp_count += 1; + if ( p()->state.intuition_blp_count >= blp_threshold + || ( !background && rng().roll( p()->bugs ? 0.01 : p()->talents.intuition->effectN( 1 ).percent() ) ) ) + { + // Needs to be triggered with a delay so that ABar doesn't eat its own proc + make_event( *sim, [ this ] { p()->buffs.intuition->trigger(); } ); + p()->state.intuition_blp_count = 0; + } + } + } + void impact( action_state_t* s ) override { mage_spell_t::impact( s ); @@ -3431,6 +3426,7 @@ struct arcane_orb_t final : public arcane_mage_spell_t aoe = -1; cooldown->charges += as( p->talents.charged_orb->effectN( 1 ).base_value() ); triggers.clearcasting = type == ao_type::NORMAL; + triggers.intuition_blp = true; std::string_view bolt_name; switch ( type ) @@ -3464,9 +3460,6 @@ struct arcane_orb_t final : public arcane_mage_spell_t { arcane_mage_spell_t::execute(); p()->trigger_arcane_charge(); - - if ( background ) - trigger_intuition( type == ao_type::ORB_BARRAGE ); } void impact( action_state_t* s ) override @@ -3511,6 +3504,7 @@ struct arcane_barrage_t final : public dematerialize_spell_t base_aoe_multiplier *= p->talents.arcing_cleave->effectN( 2 ).percent(); affected_by.arcane_debilitation = true; triggers.clearcasting = true; + triggers.intuition_blp = true; base_multiplier *= 1.0 + p->sets->set( MAGE_ARCANE, TWW1, B2 )->effectN( 1 ).percent(); glorious_incandescence_charges = as( p->find_spell( 451223 )->effectN( 1 ).base_value() ); arcane_soul_charges = as( p->find_spell( 453413 )->effectN( 1 ).base_value() ); @@ -3634,6 +3628,7 @@ struct arcane_blast_t final : public dematerialize_spell_t parse_options( options_str ); affected_by.arcane_debilitation = true; triggers.clearcasting = true; + triggers.intuition_blp = true; base_multiplier *= 1.0 + p->talents.consortiums_bauble->effectN( 2 ).percent(); base_multiplier *= 1.0 + p->sets->set( MAGE_ARCANE, TWW1, B2 )->effectN( 1 ).percent(); base_costs[ RESOURCE_MANA ] *= 1.0 + p->talents.consortiums_bauble->effectN( 1 ).percent(); @@ -3752,6 +3747,7 @@ struct arcane_explosion_t final : public arcane_mage_spell_t aoe = -1; affected_by.savant = true; triggers.clearcasting = type != ae_type::ENERGY_RECON; + triggers.intuition_blp = true; if ( type == ae_type::NORMAL ) { @@ -3779,9 +3775,6 @@ struct arcane_explosion_t final : public arcane_mage_spell_t p()->buffs.static_cloud->expire(); p()->buffs.static_cloud->trigger(); - if ( background ) - trigger_intuition( type == ae_type::ECHO ); - if ( type == ae_type::ENERGY_RECON ) return; @@ -3955,6 +3948,7 @@ struct arcane_missiles_t final : public custom_state_spell_t( "arcane_missiles_tick", p ); cost_reductions = { p->buffs.clearcasting }; if ( p->talents.slipstream.ok() ) @@ -4100,6 +4094,7 @@ struct arcane_surge_t final : public arcane_mage_spell_t reduced_aoe_targets = data().effectN( 3 ).base_value(); // TODO 11.1: Applies to Arcane Surge instead of Arcane Orb base_multiplier *= 1.0 + p->sets->set( MAGE_ARCANE, TWW1, B4 )->effectN( 1 ).percent(); + triggers.intuition_blp = true; } timespan_t travel_time() const override @@ -4575,8 +4570,6 @@ struct fireball_t final : public fire_mage_spell_t triggers.phoenix_reborn = triggers.unleashed_inferno = TT_MAIN_TARGET; triggers.ignite = triggers.from_the_ashes = true; affected_by.unleashed_inferno = affected_by.flame_accelerant = true; - if ( p->bugs && sim->dbc->wowv() < wowv_t{ 11, 1, 5 } ) - base_dd_multiplier *= 1.0 + p->talents.master_of_flame->effectN( 3 ).percent(); if ( frostfire ) { @@ -6762,6 +6755,7 @@ struct touch_of_the_magi_t final : public arcane_mage_spell_t { parse_options( options_str ); triggers.clearcasting = true; + triggers.intuition_blp = true; if ( data().ok() ) add_child( p->action.touch_of_the_magi_explosion ); From 6063818cecad886a2444553c109a2e3f5f23139a Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Wed, 7 May 2025 21:18:03 -0400 Subject: [PATCH 02/11] Fixed intuition's trigger name + removed inclusion of bugs --- engine/class_modules/sc_mage.cpp | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 1237b50a8aa..eff205b6832 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -1924,7 +1924,7 @@ struct mage_spell_t : public spell_t { bool chill = false; bool clearcasting = false; - bool intuition_blp = false; + bool intuition = false; bool from_the_ashes = false; bool frostfire_infusion = true; bool frostfire_mastery = true; @@ -2600,13 +2600,14 @@ struct arcane_mage_spell_t : public mage_spell_t { mage_spell_t::execute(); - if ( p()->talents.intuition.ok() && !p()->buffs.intuition->check() && triggers.intuition_blp ) + if ( p()->talents.intuition.ok() && !p()->buffs.intuition->check() && triggers.intuition ) { constexpr int blp_threshold = 11; + constexpr int base_proc_chance = 0.01; p()->state.intuition_blp_count += 1; if ( p()->state.intuition_blp_count >= blp_threshold - || ( !background && rng().roll( p()->bugs ? 0.01 : p()->talents.intuition->effectN( 1 ).percent() ) ) ) + || ( !background && rng().roll( base_proc_chance ) ) ) { // Needs to be triggered with a delay so that ABar doesn't eat its own proc make_event( *sim, [ this ] { p()->buffs.intuition->trigger(); } ); @@ -3426,7 +3427,7 @@ struct arcane_orb_t final : public arcane_mage_spell_t aoe = -1; cooldown->charges += as( p->talents.charged_orb->effectN( 1 ).base_value() ); triggers.clearcasting = type == ao_type::NORMAL; - triggers.intuition_blp = true; + triggers.intuition = true; std::string_view bolt_name; switch ( type ) @@ -3504,7 +3505,7 @@ struct arcane_barrage_t final : public dematerialize_spell_t base_aoe_multiplier *= p->talents.arcing_cleave->effectN( 2 ).percent(); affected_by.arcane_debilitation = true; triggers.clearcasting = true; - triggers.intuition_blp = true; + triggers.intuition = true; base_multiplier *= 1.0 + p->sets->set( MAGE_ARCANE, TWW1, B2 )->effectN( 1 ).percent(); glorious_incandescence_charges = as( p->find_spell( 451223 )->effectN( 1 ).base_value() ); arcane_soul_charges = as( p->find_spell( 453413 )->effectN( 1 ).base_value() ); @@ -3628,7 +3629,7 @@ struct arcane_blast_t final : public dematerialize_spell_t parse_options( options_str ); affected_by.arcane_debilitation = true; triggers.clearcasting = true; - triggers.intuition_blp = true; + triggers.intuition = true; base_multiplier *= 1.0 + p->talents.consortiums_bauble->effectN( 2 ).percent(); base_multiplier *= 1.0 + p->sets->set( MAGE_ARCANE, TWW1, B2 )->effectN( 1 ).percent(); base_costs[ RESOURCE_MANA ] *= 1.0 + p->talents.consortiums_bauble->effectN( 1 ).percent(); @@ -3747,7 +3748,7 @@ struct arcane_explosion_t final : public arcane_mage_spell_t aoe = -1; affected_by.savant = true; triggers.clearcasting = type != ae_type::ENERGY_RECON; - triggers.intuition_blp = true; + triggers.intuition = true; if ( type == ae_type::NORMAL ) { @@ -3948,7 +3949,7 @@ struct arcane_missiles_t final : public custom_state_spell_t( "arcane_missiles_tick", p ); cost_reductions = { p->buffs.clearcasting }; if ( p->talents.slipstream.ok() ) @@ -4094,7 +4095,7 @@ struct arcane_surge_t final : public arcane_mage_spell_t reduced_aoe_targets = data().effectN( 3 ).base_value(); // TODO 11.1: Applies to Arcane Surge instead of Arcane Orb base_multiplier *= 1.0 + p->sets->set( MAGE_ARCANE, TWW1, B4 )->effectN( 1 ).percent(); - triggers.intuition_blp = true; + triggers.intuition = true; } timespan_t travel_time() const override @@ -6755,7 +6756,7 @@ struct touch_of_the_magi_t final : public arcane_mage_spell_t { parse_options( options_str ); triggers.clearcasting = true; - triggers.intuition_blp = true; + triggers.intuition = true; if ( data().ok() ) add_child( p->action.touch_of_the_magi_explosion ); From f0a38aed50637bb5fa42c3cf5df7c0177404f645 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Wed, 7 May 2025 21:34:01 -0400 Subject: [PATCH 03/11] woops, it's an int not a double --- engine/class_modules/sc_mage.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index eff205b6832..f10dc3fe149 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -2603,7 +2603,7 @@ struct arcane_mage_spell_t : public mage_spell_t if ( p()->talents.intuition.ok() && !p()->buffs.intuition->check() && triggers.intuition ) { constexpr int blp_threshold = 11; - constexpr int base_proc_chance = 0.01; + constexpr double base_proc_chance = 0.01; p()->state.intuition_blp_count += 1; if ( p()->state.intuition_blp_count >= blp_threshold From ebeebd1fe4f2ec29685d02c0cb7ea4dbfe8c1945 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Thu, 8 May 2025 10:56:59 -0400 Subject: [PATCH 04/11] Added a comment --- engine/class_modules/sc_mage.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index f10dc3fe149..18976d51ebd 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -2603,6 +2603,7 @@ struct arcane_mage_spell_t : public mage_spell_t if ( p()->talents.intuition.ok() && !p()->buffs.intuition->check() && triggers.intuition ) { constexpr int blp_threshold = 11; + // Tooltip claims there's a 5% chance to proc Intuition, yet seemingly, it's 1% constexpr double base_proc_chance = 0.01; p()->state.intuition_blp_count += 1; From 36a18023fc1b9e3bcc0051d7b913b73452c0c8f0 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Tue, 3 Jun 2025 10:59:20 -0400 Subject: [PATCH 05/11] clearcasting + arcane tier effect --- engine/class_modules/sc_mage.cpp | 73 +++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 20 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 18976d51ebd..fb3c113c32f 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -472,6 +472,8 @@ struct mage_t final : public player_t unsigned initial_spellfire_spheres = 5; arcane_phoenix_rotation arcane_phoenix_rotation_override = arcane_phoenix_rotation::DEFAULT; bool ice_nova_consumes_winters_chill = true; + double clearcasting_chance = 0.0068; + double illuminated_thoughts_clearcasting_chance = 0.0938; } options; // Pets @@ -575,6 +577,7 @@ struct mage_t final : public player_t int embedded_splinters; int magis_spark_spells; int intuition_blp_count; + int clearcasting_blp_count; } state; struct expression_support_t @@ -1026,7 +1029,7 @@ struct mage_t final : public player_t void trigger_arcane_charge( int stacks = 1 ); bool trigger_brain_freeze( double chance, proc_t* source, timespan_t delay = 0.15_s ); bool trigger_crowd_control( const action_state_t* s, spell_mechanic type, timespan_t adjust = 0_ms ); - bool trigger_clearcasting( double chance, timespan_t delay = 0.15_s ); + bool trigger_clearcasting( double chance, timespan_t delay = 0.15_s, bool predictable = true, bool guaranteed_jackpot = false ); bool trigger_fof( double chance, proc_t* source, int stacks = 1 ); void trigger_icicle( player_t* icicle_target, bool chain = false ); void trigger_icicle_gain( player_t* icicle_target, action_t* icicle_action, double chance = 1.0, timespan_t duration = timespan_t::min() ); @@ -2259,15 +2262,32 @@ struct mage_spell_t : public spell_t // This will prevent for example Arcane Missiles consuming its own Clearcasting proc. consume_cost_reductions(); - if ( p()->spec.clearcasting->ok() && triggers.clearcasting ) + if ( triggers.clearcasting && p()->spec.clearcasting->ok() ) { - // TODO: implement the hidden BLP - double chance = p()->spec.clearcasting->effectN( 2 ).percent(); - chance += p()->talents.illuminated_thoughts->effectN( 1 ).percent(); - // Arcane Blast gets an additional 5% chance. Not mentioned in the spell data (or even the description). + constexpr int cc_blp_threshold = 13; + // The tooltip chance present on Clearcasting/Illuminated Thoughts is the total expected outcome of Clearcasting applications, not it's random proc chance. + // Whenever combining both the proc chance and its bad luck protection, the final application rate is equal to its tooltip chance. + double proc_chance = p()->options.clearcasting_chance; + if ( p()->talents.illuminated_thoughts.ok() ) + proc_chance = p()->options.illuminated_thoughts_clearcasting_chance; + // Arcane Blast has an unmentioned 5% increase in total expected Clearcasting applications -- same BLP threshold, but higher proc chance. if ( id == 30451 ) - chance += 0.05; - p()->trigger_clearcasting( chance ); + { + proc_chance = 0.0938; + if ( p()->talents.illuminated_thoughts.ok() ) + proc_chance = 0.1618; + } + + // Likely a bug: Arcane Orb procs from Orb Barrage uniquely decrements the BLP, effectively nullifying the incoming increment from Barrage. + if ( name_str == "orb_barrage_arcane_orb" ) + p()->state.clearcasting_blp_count -= 1; + else + p()->state.clearcasting_blp_count += 1; + if ( !background && rng().roll( proc_chance ) || ( p()->state.clearcasting_blp_count >= cc_blp_threshold ) ) + { + p()->trigger_clearcasting( 1.0, 100_ms, false ); + p()->state.clearcasting_blp_count = 0; + } } if ( !background && affected_by.ice_floes && time_to_execute > 0_ms ) @@ -3427,7 +3447,7 @@ struct arcane_orb_t final : public arcane_mage_spell_t may_miss = false; aoe = -1; cooldown->charges += as( p->talents.charged_orb->effectN( 1 ).base_value() ); - triggers.clearcasting = type == ao_type::NORMAL; + triggers.clearcasting = true; triggers.intuition = true; std::string_view bolt_name; @@ -6769,7 +6789,8 @@ struct touch_of_the_magi_t final : public arcane_mage_spell_t p()->trigger_arcane_charge( as( data().effectN( 2 ).base_value() ) ); p()->buffs.leydrinker->trigger(); - p()->trigger_jackpot( true ); + // Clearcastings generated by TWW2's tier effect is independent, allowing ToTM to sometimes apply two applications with one cast. + p()->trigger_clearcasting( p()->sets->has_set_bonus( MAGE_ARCANE, TWW2, B2 ), 0_ms, true, true ); } void impact( action_state_t* s ) override @@ -7496,7 +7517,7 @@ struct time_anomaly_tick_event_t final : public mage_event_t mage->buffs.arcane_surge->trigger( 1000 * mage->talents.time_anomaly->effectN( 1 ).time_value() ); break; case TA_CLEARCASTING: - mage->trigger_clearcasting( 1.0, 0_ms ); + mage->trigger_clearcasting( 1.0, 0_ms, false ); break; case TA_COMBUSTION: mage->buffs.combustion->trigger( 1000 * mage->talents.time_anomaly->effectN( 4 ).time_value() ); @@ -7581,7 +7602,7 @@ struct splinterstorm_event_t final : public mage_event_t else // Doesn't seem to be affected by Illuminated Thoughts. // TODO: get more data and double check - mage->trigger_clearcasting( mage->talents.splinterstorm->effectN( 4 ).percent(), 0_ms ); + mage->trigger_clearcasting( mage->talents.splinterstorm->effectN( 4 ).percent(), 0_ms, false ); } mage->events.splinterstorm = make_event( @@ -7917,6 +7938,8 @@ void mage_t::create_options() return true; } ) ); add_option( opt_bool( "mage.ice_nova_consumes_winters_chill", options.ice_nova_consumes_winters_chill ) ); + add_option( opt_float( "mage.clearcasting_chance", options.clearcasting_chance)); + add_option( opt_float( "mage.illuminated_thoughts_clearcasting_chance", options.illuminated_thoughts_clearcasting_chance)); player_t::create_options(); } @@ -8367,7 +8390,7 @@ void mage_t::init_spells() void mage_t::init_special_effects() { auto spell = sets->set( specialization(), TWW2, B2 ); - if ( spell->ok() ) + if ( spell->ok() && ( specialization() != MAGE_ARCANE ) ) { auto effect = new special_effect_t( this ); effect->name_str = "mage_jackpot_proc"; @@ -9484,11 +9507,20 @@ void mage_t::trigger_jackpot( bool guaranteed ) bool has_4pc = sets->has_set_bonus( specialization(), TWW2, B4 ); switch ( specialization() ) { + // TWW2's tier effect for Arcane doesn't randomly generate Clearcasting (has a misleading tooltip). Instead, it solely grants Clarity/AA whenever any source of CC is applied at a 13% chance. case MAGE_ARCANE: - buffs.clarity->trigger(); - trigger_clearcasting( 1.0, 0_ms ); - if ( has_4pc ) - buffs.aether_attunement->trigger(); + if ( guaranteed || rng().roll( 0.13 ) ) + { + buffs.clarity->trigger(); + if ( guaranteed ) + buffs.clarity->predict(); + if ( has_4pc ) + { + buffs.aether_attunement->trigger(); + if ( guaranteed ) + buffs.aether_attunement->predict(); + } + } break; case MAGE_FIRE: { @@ -9690,7 +9722,7 @@ void mage_t::trigger_splinter( player_t* target, int count ) } } -bool mage_t::trigger_clearcasting( double chance, timespan_t delay ) +bool mage_t::trigger_clearcasting( double chance, timespan_t delay, bool predictable, bool guaranteed_jackpot ) { if ( specialization() != MAGE_ARCANE ) return false; @@ -9702,10 +9734,11 @@ bool mage_t::trigger_clearcasting( double chance, timespan_t delay ) make_event( *sim, delay, [ this ] { buffs.clearcasting->trigger(); } ); else buffs.clearcasting->trigger(); - - if ( chance >= 1.0 ) + if ( predictable ) buffs.clearcasting->predict(); buffs.big_brained->trigger(); + + trigger_jackpot( guaranteed_jackpot ); } return success; From be793ce14a58a966dc8fcf935c9eb928e855b690 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Sat, 7 Jun 2025 07:10:58 -0400 Subject: [PATCH 06/11] convert cc to use rppm + addressed comments + make it a bit neater (i think) --- engine/class_modules/sc_mage.cpp | 75 +++++++++++++++++++------------- 1 file changed, 44 insertions(+), 31 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index daf2a2df48c..40bdcc94fc3 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -473,7 +473,9 @@ struct mage_t final : public player_t arcane_phoenix_rotation arcane_phoenix_rotation_override = arcane_phoenix_rotation::DEFAULT; bool ice_nova_consumes_winters_chill = true; double clearcasting_chance = 0.0068; - double illuminated_thoughts_clearcasting_chance = 0.0938; + double it_clearcasting_chance = 0.0938; + double blast_clearcasting_chance = 0.0938; + double blast_it_clearcasting_chance = 0.1618; } options; // Pets @@ -525,6 +527,7 @@ struct mage_t final : public player_t { real_ppm_t* energy_reconstitution; real_ppm_t* frostfire_infusion; + real_ppm_t* arcane_jackpot; } rppm; struct accumulated_rngs_t @@ -1032,7 +1035,7 @@ struct mage_t final : public player_t void trigger_arcane_charge( int stacks = 1 ); bool trigger_brain_freeze( double chance, proc_t* source, timespan_t delay = 0.15_s ); bool trigger_crowd_control( const action_state_t* s, spell_mechanic type, timespan_t adjust = 0_ms ); - bool trigger_clearcasting( double chance, timespan_t delay = 0.15_s, bool predictable = true, bool guaranteed_jackpot = false ); + bool trigger_clearcasting( double chance = 1.0, timespan_t delay = 0_ms, bool never_predictable = false ); bool trigger_fof( double chance, proc_t* source, int stacks = 1 ); void trigger_icicle( player_t* icicle_target, bool chain = false ); void trigger_icicle_gain( player_t* icicle_target, action_t* icicle_action, double chance = 1.0, timespan_t duration = timespan_t::min() ); @@ -2265,31 +2268,29 @@ struct mage_spell_t : public spell_t // This will prevent for example Arcane Missiles consuming its own Clearcasting proc. consume_cost_reductions(); - if ( triggers.clearcasting && p()->spec.clearcasting->ok() ) + if ( p()->spec.clearcasting->ok() && triggers.clearcasting ) { constexpr int cc_blp_threshold = 13; // The tooltip chance present on Clearcasting/Illuminated Thoughts is the total expected outcome of Clearcasting applications, not it's random proc chance. // Whenever combining both the proc chance and its bad luck protection, the final application rate is equal to its tooltip chance. double proc_chance = p()->options.clearcasting_chance; if ( p()->talents.illuminated_thoughts.ok() ) - proc_chance = p()->options.illuminated_thoughts_clearcasting_chance; + proc_chance = p()->options.it_clearcasting_chance; // Arcane Blast has an unmentioned 5% increase in total expected Clearcasting applications -- same BLP threshold, but higher proc chance. if ( id == 30451 ) { - proc_chance = 0.0938; + proc_chance = p()->options.blast_clearcasting_chance; if ( p()->talents.illuminated_thoughts.ok() ) - proc_chance = 0.1618; + proc_chance = p()->options.blast_it_clearcasting_chance; } - // Likely a bug: Arcane Orb procs from Orb Barrage uniquely decrements the BLP, effectively nullifying the incoming increment from Barrage. - if ( name_str == "orb_barrage_arcane_orb" ) - p()->state.clearcasting_blp_count -= 1; - else - p()->state.clearcasting_blp_count += 1; - if ( !background && rng().roll( proc_chance ) || ( p()->state.clearcasting_blp_count >= cc_blp_threshold ) ) + p()->state.clearcasting_blp_count += 1; + if ( p()->state.clearcasting_blp_count >= cc_blp_threshold ) + proc_chance = 1.0; + if ( proc_chance == 1.0 || !background ) { - p()->trigger_clearcasting( 1.0, 100_ms, false ); - p()->state.clearcasting_blp_count = 0; + if ( p()->trigger_clearcasting( proc_chance, 100_ms ) ) + p()->state.clearcasting_blp_count = 0; } } @@ -3450,7 +3451,7 @@ struct arcane_orb_t final : public arcane_mage_spell_t may_miss = false; aoe = -1; cooldown->charges += as( p->talents.charged_orb->effectN( 1 ).base_value() ); - triggers.clearcasting = true; + triggers.clearcasting = type != ao_type::ORB_BARRAGE; triggers.intuition = true; std::string_view bolt_name; @@ -3485,6 +3486,10 @@ struct arcane_orb_t final : public arcane_mage_spell_t { arcane_mage_spell_t::execute(); p()->trigger_arcane_charge(); + + // Likely a bug: Arcane Orb procs from Orb Barrage uniquely decrements the BLP, effectively nullifying the incoming increment from Barrage. + if ( type == ao_type::ORB_BARRAGE ) + p()->state.clearcasting_blp_count -= 1; } void impact( action_state_t* s ) override @@ -3576,7 +3581,7 @@ struct arcane_barrage_t final : public dematerialize_spell_t if ( p()->buffs.arcane_soul->check() ) { - p()->trigger_clearcasting( 1.0, 0_ms ); + p()->trigger_clearcasting(); p()->trigger_arcane_charge( arcane_soul_charges ); p()->buffs.arcane_soul_damage->trigger(); } @@ -4155,7 +4160,7 @@ struct arcane_surge_t final : public arcane_mage_spell_t timespan_t arcane_surge_duration = p()->buffs.arcane_surge->buff_duration() + bonus_duration; p()->buffs.arcane_surge->trigger( arcane_surge_duration ); - p()->trigger_clearcasting( 1.0, 0_ms ); + p()->trigger_clearcasting(); if ( p()->pets.arcane_phoenix ) p()->pets.arcane_phoenix->summon( arcane_surge_duration ); // TODO: The extra random pet duration can sometimes result in an extra cast. @@ -4552,7 +4557,7 @@ struct evocation_t final : public arcane_mage_spell_t { arcane_mage_spell_t::execute(); - p()->trigger_clearcasting( 1.0, 0_ms ); + p()->trigger_clearcasting(); p()->trigger_arcane_charge(); if ( is_precombat && execute_state ) @@ -6793,7 +6798,11 @@ struct touch_of_the_magi_t final : public arcane_mage_spell_t p()->trigger_arcane_charge( as( data().effectN( 2 ).base_value() ) ); p()->buffs.leydrinker->trigger(); // Clearcastings generated by TWW2's tier effect is independent, allowing ToTM to sometimes apply two applications with one cast. - p()->trigger_clearcasting( p()->sets->has_set_bonus( MAGE_ARCANE, TWW2, B2 ), 0_ms, true, true ); + if ( p()->sets->has_set_bonus( MAGE_ARCANE, TWW2, B2 ) ) + { + p()->trigger_clearcasting(); + p()->trigger_jackpot( true ); + } } void impact( action_state_t* s ) override @@ -7520,7 +7529,7 @@ struct time_anomaly_tick_event_t final : public mage_event_t mage->buffs.arcane_surge->trigger( 1000 * mage->talents.time_anomaly->effectN( 1 ).time_value() ); break; case TA_CLEARCASTING: - mage->trigger_clearcasting( 1.0, 0_ms, false ); + mage->trigger_clearcasting( 1.0, 0_ms, true ); break; case TA_COMBUSTION: mage->buffs.combustion->trigger( 1000 * mage->talents.time_anomaly->effectN( 4 ).time_value() ); @@ -7605,7 +7614,7 @@ struct splinterstorm_event_t final : public mage_event_t else // Doesn't seem to be affected by Illuminated Thoughts. // TODO: get more data and double check - mage->trigger_clearcasting( mage->talents.splinterstorm->effectN( 4 ).percent(), 0_ms, false ); + mage->trigger_clearcasting( mage->talents.splinterstorm->effectN( 4 ).percent() ); } mage->events.splinterstorm = make_event( @@ -7942,7 +7951,9 @@ void mage_t::create_options() } ) ); add_option( opt_bool( "mage.ice_nova_consumes_winters_chill", options.ice_nova_consumes_winters_chill ) ); add_option( opt_float( "mage.clearcasting_chance", options.clearcasting_chance)); - add_option( opt_float( "mage.illuminated_thoughts_clearcasting_chance", options.illuminated_thoughts_clearcasting_chance)); + add_option( opt_float( "mage.it_clearcasting_chance", options.it_clearcasting_chance)); + add_option( opt_float( "mage.blast_clearcasting_chance", options.blast_clearcasting_chance)); + add_option( opt_float( "mage.blast_it_clearcasting_chance", options.blast_it_clearcasting_chance)); player_t::create_options(); } @@ -8838,6 +8849,7 @@ void mage_t::init_rng() shuffled_rng.time_anomaly = get_shuffled_rng( "time_anomaly", 1, 16 ); rppm.energy_reconstitution = get_rppm( "energy_reconstitution", talents.energy_reconstitution ); rppm.frostfire_infusion = get_rppm( "frostfire_infusion", talents.frostfire_infusion ); + rppm.arcane_jackpot = get_rppm( "arcane_jackpot", sets->set( MAGE_ARCANE, TWW2, B2 ) ); // Accumulated RNG is also not present in the game data. accumulated_rng.pyromaniac = get_accumulated_rng( "pyromaniac", talents.pyromaniac.ok() ? 0.00605 : 0.0 ); @@ -9549,17 +9561,18 @@ void mage_t::trigger_jackpot( bool guaranteed ) bool has_4pc = sets->has_set_bonus( specialization(), TWW2, B4 ); switch ( specialization() ) { - // TWW2's tier effect for Arcane doesn't randomly generate Clearcasting (has a misleading tooltip). Instead, it solely grants Clarity/AA whenever any source of CC is applied at a 13% chance. + // TWW2's tier effect for Arcane doesn't randomly generate Clearcasting (has a misleading tooltip). + // Instead, it solely grants Clarity/AA whenever any source of CC is applied at 2RPPM -- appears as if guaranteed jackpots don't reset the RPPM. case MAGE_ARCANE: - if ( guaranteed || rng().roll( 0.13 ) ) + if ( guaranteed || rppm.arcane_jackpot->trigger() ) { buffs.clarity->trigger(); - if ( guaranteed ) - buffs.clarity->predict(); if ( has_4pc ) - { buffs.aether_attunement->trigger(); - if ( guaranteed ) + if ( guaranteed ) + { + buffs.clarity->predict(); + if ( has_4pc ) buffs.aether_attunement->predict(); } } @@ -9764,7 +9777,7 @@ void mage_t::trigger_splinter( player_t* target, int count ) } } -bool mage_t::trigger_clearcasting( double chance, timespan_t delay, bool predictable, bool guaranteed_jackpot ) +bool mage_t::trigger_clearcasting( double chance, timespan_t delay, bool never_predictable ) { if ( specialization() != MAGE_ARCANE ) return false; @@ -9776,11 +9789,11 @@ bool mage_t::trigger_clearcasting( double chance, timespan_t delay, bool predict make_event( *sim, delay, [ this ] { buffs.clearcasting->trigger(); } ); else buffs.clearcasting->trigger(); - if ( predictable ) + if ( chance >= 1.0 && !never_predictable ) buffs.clearcasting->predict(); buffs.big_brained->trigger(); - trigger_jackpot( guaranteed_jackpot ); + trigger_jackpot(); } return success; From f3c5a497d63423d19baaa6ebdbd750ee9ef28316 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Sun, 8 Jun 2025 19:59:37 -0400 Subject: [PATCH 07/11] expressions + predicting buffs + intuition not consuming dmg amp bug --- engine/class_modules/sc_mage.cpp | 57 +++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 40bdcc94fc3..79b887b971d 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -476,6 +476,7 @@ struct mage_t final : public player_t double it_clearcasting_chance = 0.0938; double blast_clearcasting_chance = 0.0938; double blast_it_clearcasting_chance = 0.1618; + double intuition_chance = 0.01; // Tooltip claims there's a 5% chance to proc Intuition, yet seemingly, it's 1% } options; // Pets @@ -577,6 +578,7 @@ struct mage_t final : public player_t bool trigger_flash_freezeburn; bool trigger_glorious_incandescence; bool heat_shimmer; + bool just_gained_intuition; // Bug: if Intuition is gained then subsequently consumed, the damage of Barrage will not be amplified. int embedded_splinters; int magis_spark_spells; int intuition_blp_count; @@ -2288,10 +2290,10 @@ struct mage_spell_t : public spell_t if ( p()->state.clearcasting_blp_count >= cc_blp_threshold ) proc_chance = 1.0; if ( proc_chance == 1.0 || !background ) - { - if ( p()->trigger_clearcasting( proc_chance, 100_ms ) ) + { + if ( p()->trigger_clearcasting( proc_chance, 100_ms, background ) ) p()->state.clearcasting_blp_count = 0; - } + } } if ( !background && affected_by.ice_floes && time_to_execute > 0_ms ) @@ -2627,16 +2629,22 @@ struct arcane_mage_spell_t : public mage_spell_t if ( p()->talents.intuition.ok() && !p()->buffs.intuition->check() && triggers.intuition ) { constexpr int blp_threshold = 11; - // Tooltip claims there's a 5% chance to proc Intuition, yet seemingly, it's 1% - constexpr double base_proc_chance = 0.01; p()->state.intuition_blp_count += 1; - if ( p()->state.intuition_blp_count >= blp_threshold - || ( !background && rng().roll( base_proc_chance ) ) ) + // Snapshot whether Intuition is guaranteed as to have it predictable with the mentioned delay below. + bool guaranteed = p()->state.intuition_blp_count >= blp_threshold; + if ( guaranteed || ( !background && rng().roll( p()->options.intuition_chance ) ) ) { // Needs to be triggered with a delay so that ABar doesn't eat its own proc - make_event( *sim, [ this ] { p()->buffs.intuition->trigger(); } ); + make_event( *sim, [ this, guaranteed ] + { + p()->buffs.intuition->trigger(); + if ( !background && guaranteed ) + p()->buffs.intuition->predict(); + } ); p()->state.intuition_blp_count = 0; + p()->state.just_gained_intuition = true; + make_event( *sim, 30_ms, [ this ] { p()->state.just_gained_intuition = false; } ); } } } @@ -3636,7 +3644,8 @@ struct arcane_barrage_t final : public dematerialize_spell_t am *= arcane_charge_multiplier( true ); am *= 1.0 + p()->buffs.arcane_harmony->check_stack_value(); am *= 1.0 + p()->buffs.nether_precision->check_value(); - am *= 1.0 + p()->buffs.intuition->check_value(); + if ( !p()->bugs || !p()->state.just_gained_intuition ) + am *= 1.0 + p()->buffs.intuition->check_value(); am *= 1.0 + p()->buffs.arcane_soul_damage->check_stack_value(); return am; @@ -7950,11 +7959,11 @@ void mage_t::create_options() return true; } ) ); add_option( opt_bool( "mage.ice_nova_consumes_winters_chill", options.ice_nova_consumes_winters_chill ) ); - add_option( opt_float( "mage.clearcasting_chance", options.clearcasting_chance)); - add_option( opt_float( "mage.it_clearcasting_chance", options.it_clearcasting_chance)); - add_option( opt_float( "mage.blast_clearcasting_chance", options.blast_clearcasting_chance)); - add_option( opt_float( "mage.blast_it_clearcasting_chance", options.blast_it_clearcasting_chance)); - + add_option( opt_float( "mage.clearcasting_chance", options.clearcasting_chance) ); + add_option( opt_float( "mage.it_clearcasting_chance", options.it_clearcasting_chance) ); + add_option( opt_float( "mage.blast_clearcasting_chance", options.blast_clearcasting_chance) ); + add_option( opt_float( "mage.blast_it_clearcasting_chance", options.blast_it_clearcasting_chance) ); + add_option( opt_float( "mage.intuition_chance", options.intuition_chance) ); player_t::create_options(); } @@ -8517,7 +8526,7 @@ void mage_t::create_buffs() ->set_default_value_from_effect( 1 ) ->modify_default_value( talents.aether_fragment->effectN( 1 ).percent() ) ->set_chance( talents.intuition.ok() ); - buffs.leydrinker = make_buff( this, "leydrinker", find_spell( 453758 ) ) + buffs.leydrinker = make_buff( this, "leydrinker", find_spell( 453758 ) ) ->set_chance( talents.leydrinker.ok() ); buffs.nether_precision = make_buff( this, "nether_precision", find_spell( 383783 ) ) ->set_default_value( talents.nether_precision->effectN( 1 ).percent() ) @@ -9384,6 +9393,24 @@ std::unique_ptr mage_t::create_expression( std::string_view name ) { return state.embedded_splinters; } ); } + if ( util::str_compare_ci( name, "clearcasting_blp_remains" ) ) + { + return make_fn_expr( name, [ this ] + { return 13 - state.clearcasting_blp_count; } ); + } + + if ( util::str_compare_ci( name, "intuition_blp_remains" ) ) + { + return make_fn_expr( name, [ this ] + { return 11 - state.intuition_blp_count; } ); + } + + if ( util::str_compare_ci( name, "has_intuition_damage_amp" ) ) + { + return make_fn_expr( name, [ this ] + { return buffs.intuition->check() && !state.just_gained_intuition; } ); + } + auto splits = util::string_split( name, "." ); if ( splits.size() == 3 && util::str_compare_ci( splits[ 0 ], "ground_aoe" ) ) From 0ff821c45c39a4cae942606d64eeb1990c7fc863 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Sun, 8 Jun 2025 20:38:43 -0400 Subject: [PATCH 08/11] woops. removed spaces. --- engine/class_modules/sc_mage.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 79b887b971d..daf30e9b880 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -2290,10 +2290,10 @@ struct mage_spell_t : public spell_t if ( p()->state.clearcasting_blp_count >= cc_blp_threshold ) proc_chance = 1.0; if ( proc_chance == 1.0 || !background ) - { + { if ( p()->trigger_clearcasting( proc_chance, 100_ms, background ) ) p()->state.clearcasting_blp_count = 0; - } + } } if ( !background && affected_by.ice_floes && time_to_execute > 0_ms ) @@ -8526,7 +8526,7 @@ void mage_t::create_buffs() ->set_default_value_from_effect( 1 ) ->modify_default_value( talents.aether_fragment->effectN( 1 ).percent() ) ->set_chance( talents.intuition.ok() ); - buffs.leydrinker = make_buff( this, "leydrinker", find_spell( 453758 ) ) + buffs.leydrinker = make_buff( this, "leydrinker", find_spell( 453758 ) ) ->set_chance( talents.leydrinker.ok() ); buffs.nether_precision = make_buff( this, "nether_precision", find_spell( 383783 ) ) ->set_default_value( talents.nether_precision->effectN( 1 ).percent() ) From cb848898190f4aea86ae6d321c67d7765001f3f2 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Mon, 9 Jun 2025 11:37:32 -0400 Subject: [PATCH 09/11] leydrinker: removed 150ms delay + AE background actions can proc it --- engine/class_modules/sc_mage.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index daf30e9b880..23d87645e8f 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -2590,12 +2590,7 @@ struct arcane_mage_spell_t : public mage_spell_t p()->buffs.aether_attunement_counter->trigger(); if ( rng().roll( p()->talents.leydrinker->effectN( 1 ).percent() ) ) - { - if ( p()->buffs.leydrinker->check() ) - make_event( *sim, 150_ms, [ this ] { p()->buffs.leydrinker->trigger(); } ); - else - p()->buffs.leydrinker->trigger(); - } + p()->buffs.leydrinker->trigger(); } break; } @@ -3808,6 +3803,9 @@ struct arcane_explosion_t final : public arcane_mage_spell_t if ( echo && p()->buffs.clearcasting->check() ) make_event( *sim, 500_ms, [ this, t = target ] { echo->execute_on_target( t ); } ); + if ( type != ae_type::NORMAL && rng().roll( p()->talents.leydrinker->effectN( 1 ).percent() ) ) + p()->buffs.leydrinker->trigger(); + arcane_mage_spell_t::execute(); if ( p()->buffs.static_cloud->at_max_stacks() ) From ac72b6e9a9710f261369b257a45a9d14a7b63221 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Fri, 13 Jun 2025 16:50:08 -0400 Subject: [PATCH 10/11] delay for ae + orb barr consuming intuition + orb barrage barrs do not roll the proc chance for cc --- engine/class_modules/sc_mage.cpp | 55 +++++++++++++++++++++----------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index 23d87645e8f..c03b805595a 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -578,7 +578,7 @@ struct mage_t final : public player_t bool trigger_flash_freezeburn; bool trigger_glorious_incandescence; bool heat_shimmer; - bool just_gained_intuition; // Bug: if Intuition is gained then subsequently consumed, the damage of Barrage will not be amplified. + bool prevent_intuition_damage_amp; // Bug: if Intuition is gained then subsequently consumed, the damage of Barrage will not be amplified. int embedded_splinters; int magis_spark_spells; int intuition_blp_count; @@ -2266,6 +2266,7 @@ struct mage_spell_t : public spell_t { spell_t::execute(); + bool snapshot_clearcasting = p()->buffs.clearcasting->check(); // Make sure we remove all cost reduction buffs before we trigger new ones. // This will prevent for example Arcane Missiles consuming its own Clearcasting proc. consume_cost_reductions(); @@ -2273,6 +2274,7 @@ struct mage_spell_t : public spell_t if ( p()->spec.clearcasting->ok() && triggers.clearcasting ) { constexpr int cc_blp_threshold = 13; + timespan_t delay = 100_ms; // The tooltip chance present on Clearcasting/Illuminated Thoughts is the total expected outcome of Clearcasting applications, not it's random proc chance. // Whenever combining both the proc chance and its bad luck protection, the final application rate is equal to its tooltip chance. double proc_chance = p()->options.clearcasting_chance; @@ -2289,9 +2291,13 @@ struct mage_spell_t : public spell_t p()->state.clearcasting_blp_count += 1; if ( p()->state.clearcasting_blp_count >= cc_blp_threshold ) proc_chance = 1.0; + // Arcane Explosion, if consuming Clearcasting, has the random proc chance occur precisely whenever the Echo is executed. + if ( proc_chance != 1.0 && id == 1449 && snapshot_clearcasting ) + delay = 500_ms; + if ( proc_chance == 1.0 || !background ) { - if ( p()->trigger_clearcasting( proc_chance, 100_ms, background ) ) + if ( p()->trigger_clearcasting( proc_chance, delay, background ) ) p()->state.clearcasting_blp_count = 0; } } @@ -2630,16 +2636,23 @@ struct arcane_mage_spell_t : public mage_spell_t bool guaranteed = p()->state.intuition_blp_count >= blp_threshold; if ( guaranteed || ( !background && rng().roll( p()->options.intuition_chance ) ) ) { - // Needs to be triggered with a delay so that ABar doesn't eat its own proc - make_event( *sim, [ this, guaranteed ] + // Needs to be triggered with a delay so that ABar doesn't eat its own proc. + // Orb Barrage at 10blp has the incoming Barrage consuming Intuition; additionally, doesn't have its damage amplified by Intuition's multiplier. + if ( id == 153626 ) + p()->buffs.intuition->trigger(); + else { - p()->buffs.intuition->trigger(); - if ( !background && guaranteed ) - p()->buffs.intuition->predict(); - } ); - p()->state.intuition_blp_count = 0; - p()->state.just_gained_intuition = true; - make_event( *sim, 30_ms, [ this ] { p()->state.just_gained_intuition = false; } ); + make_event( *sim, [ this, guaranteed ] + { + p()->buffs.intuition->trigger(); + if ( !background && guaranteed ) + p()->buffs.intuition->predict(); + } ); + } + make_event( *sim, [ this ] { p()->state.intuition_blp_count = 0; } ); + + p()->state.prevent_intuition_damage_amp = true; + make_event( *sim, 30_ms, [ this ] { p()->state.prevent_intuition_damage_amp = false; } ); } } } @@ -3489,10 +3502,6 @@ struct arcane_orb_t final : public arcane_mage_spell_t { arcane_mage_spell_t::execute(); p()->trigger_arcane_charge(); - - // Likely a bug: Arcane Orb procs from Orb Barrage uniquely decrements the BLP, effectively nullifying the incoming increment from Barrage. - if ( type == ao_type::ORB_BARRAGE ) - p()->state.clearcasting_blp_count -= 1; } void impact( action_state_t* s ) override @@ -3568,8 +3577,16 @@ struct arcane_barrage_t final : public dematerialize_spell_t // Arcane Charge from the Orb cast increases Barrage damage, but does not change // how many targets it hits. Snapshot the buff stacks before executing the Orb. snapshot_charges = p()->buffs.arcane_charge->check(); - if ( rng().roll( snapshot_charges * p()->talents.orb_barrage->effectN( 1 ).percent() ) ) - orb_barrage->execute_on_target( target ); + if ( p()->talents.orb_barrage->ok() ) + { + triggers.clearcasting = true; + if ( rng().roll( snapshot_charges * p()->talents.orb_barrage->effectN( 1 ).percent() ) ) + { + orb_barrage->execute_on_target( target ); + // Likely a bug: Arcane Orb procs from Orb Barrage uniquely prevent Barrage from rolling Clearcasting's proc chance, and incrementing its BLP. + triggers.clearcasting = false; + } + } p()->benefits.arcane_charge.arcane_barrage->update(); @@ -3639,7 +3656,7 @@ struct arcane_barrage_t final : public dematerialize_spell_t am *= arcane_charge_multiplier( true ); am *= 1.0 + p()->buffs.arcane_harmony->check_stack_value(); am *= 1.0 + p()->buffs.nether_precision->check_value(); - if ( !p()->bugs || !p()->state.just_gained_intuition ) + if ( !p()->bugs || !p()->state.prevent_intuition_damage_amp ) am *= 1.0 + p()->buffs.intuition->check_value(); am *= 1.0 + p()->buffs.arcane_soul_damage->check_stack_value(); @@ -9406,7 +9423,7 @@ std::unique_ptr mage_t::create_expression( std::string_view name ) if ( util::str_compare_ci( name, "has_intuition_damage_amp" ) ) { return make_fn_expr( name, [ this ] - { return buffs.intuition->check() && !state.just_gained_intuition; } ); + { return buffs.intuition->check() && !state.prevent_intuition_damage_amp; } ); } auto splits = util::string_split( name, "." ); From 90da67966a73ed07e4d3860fa72a35476f92bab1 Mon Sep 17 00:00:00 2001 From: pinepinepine Date: Sun, 15 Jun 2025 22:55:09 -0400 Subject: [PATCH 11/11] whitespaces + small expression to track queueable missiles --- engine/class_modules/sc_mage.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/engine/class_modules/sc_mage.cpp b/engine/class_modules/sc_mage.cpp index c03b805595a..f38f6e4405a 100644 --- a/engine/class_modules/sc_mage.cpp +++ b/engine/class_modules/sc_mage.cpp @@ -578,7 +578,8 @@ struct mage_t final : public player_t bool trigger_flash_freezeburn; bool trigger_glorious_incandescence; bool heat_shimmer; - bool prevent_intuition_damage_amp; // Bug: if Intuition is gained then subsequently consumed, the damage of Barrage will not be amplified. + bool prevent_intuition_damage_amp; // Bug: if Intuition is gained then subsequently consumed, the damage of Barrage will not be amplified. + bool gained_initial_clearcasting; // To track queueable Missiles for the APL -- cannot queue Missiles if subsequently gaining CC from a hard cast. int embedded_splinters; int magis_spark_spells; int intuition_blp_count; @@ -7974,11 +7975,11 @@ void mage_t::create_options() return true; } ) ); add_option( opt_bool( "mage.ice_nova_consumes_winters_chill", options.ice_nova_consumes_winters_chill ) ); - add_option( opt_float( "mage.clearcasting_chance", options.clearcasting_chance) ); - add_option( opt_float( "mage.it_clearcasting_chance", options.it_clearcasting_chance) ); - add_option( opt_float( "mage.blast_clearcasting_chance", options.blast_clearcasting_chance) ); - add_option( opt_float( "mage.blast_it_clearcasting_chance", options.blast_it_clearcasting_chance) ); - add_option( opt_float( "mage.intuition_chance", options.intuition_chance) ); + add_option( opt_float( "mage.clearcasting_chance", options.clearcasting_chance ) ); + add_option( opt_float( "mage.it_clearcasting_chance", options.it_clearcasting_chance ) ); + add_option( opt_float( "mage.blast_clearcasting_chance", options.blast_clearcasting_chance ) ); + add_option( opt_float( "mage.blast_it_clearcasting_chance", options.blast_it_clearcasting_chance ) ); + add_option( opt_float( "mage.intuition_chance", options.intuition_chance ) ); player_t::create_options(); } @@ -9426,6 +9427,12 @@ std::unique_ptr mage_t::create_expression( std::string_view name ) { return buffs.intuition->check() && !state.prevent_intuition_damage_amp; } ); } + if ( util::str_compare_ci( name, "queueable_missiles" ) ) + { + return make_fn_expr( name, [ this ] + { return !state.gained_initial_clearcasting; } ); + } + auto splits = util::string_split( name, "." ); if ( splits.size() == 3 && util::str_compare_ci( splits[ 0 ], "ground_aoe" ) ) @@ -9827,6 +9834,11 @@ bool mage_t::trigger_clearcasting( double chance, timespan_t delay, bool never_p bool success = rng().roll( chance ); if ( success ) { + if ( !buffs.clearcasting->check() ) + { + state.gained_initial_clearcasting = true; + make_event( *sim, 50_ms, [ this ] { state.gained_initial_clearcasting = false; } ); + } if ( delay > 0_ms && buffs.clearcasting->check() ) make_event( *sim, delay, [ this ] { buffs.clearcasting->trigger(); } ); else