From 135c4d85477c224bc5ba6f7be6d8cacf4671f16c Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Thu, 9 Jul 2026 14:04:58 -0600 Subject: [PATCH 1/9] [monk] Zenith Stomp can be cast while channeling Spinning Crane Kick. Zenith Stomp is considered a combo strike ability. --- engine/class_modules/monk/sc_monk.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 7087ce112ae..cc763b8154d 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -3958,8 +3958,9 @@ struct zenith_stomp_t : monk_spell_t aoe = -1; reduced_aoe_targets = player->talent.monk.zenith_stomp->effectN( 1 ).base_value(); - may_combo_strike = false; + may_combo_strike = player->wowv_ge( { 12, 1, 0 } ); ww_mastery = true; + cast_during_sck = true; } void init() override From 3af7969a4c23659b0b9ed9e736e07b1df307de44 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 11 Jul 2026 13:05:49 -0600 Subject: [PATCH 2/9] [monk] Reimplement `cast_during_sck` using `action_t::usable_during_current_cast()` and by setting `action_t::usable_while_casting` and `action_t::use_while_casting` automatically to avoid a terrible APL experience. --- engine/class_modules/monk/sc_monk.cpp | 31 ++++++--------------------- engine/class_modules/monk/sc_monk.hpp | 2 +- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index cc763b8154d..aeb3053313e 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -221,18 +221,12 @@ bool monk_action_t::usable_moving() const } template -bool monk_action_t::ready() +bool monk_action_t::usable_during_current_cast() const { - // Spell data nil or not_found - if ( base_t::data().id() == 0 ) - return false; - - // These abilities are able to be used during Spinning Crane Kick - if ( cast_during_sck ) - base_t::usable_while_casting = p()->channeling && p()->baseline.monk.spinning_crane_kick && - ( p()->channeling->id == p()->baseline.monk.spinning_crane_kick->id() ); + if ( cast_during_sck && p()->channeling && p()->channeling->id == p()->baseline.monk.spinning_crane_kick->id() ) + return true; - return base_t::ready(); + return base_t::usable_during_current_cast(); } template @@ -260,20 +254,8 @@ void monk_action_t::init() } } - // Allow this ability to be cast during SCK - if ( cast_during_sck && !base_t::background && !base_t::dual ) - { - if ( base_t::usable_while_casting ) - { - cast_during_sck = false; - p()->sim->print_debug( "{}: cast_during_sck ignored because usable_while_casting = true", full_name() ); - } - else - { - base_t::usable_while_casting = true; - base_t::use_while_casting = true; - } - } + if ( cast_during_sck ) + base_t::usable_while_casting = base_t::use_while_casting = true; } template @@ -2502,7 +2484,6 @@ struct keg_smash_t : monk_melee_attack_t fuel_on_the_fire( nullptr ) { parse_options( options_str ); - // TODO: can cast_during_sck be automated? cast_during_sck = true; full_amount_targets = 1; diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index c96afdd6741..923171a3450 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -97,7 +97,7 @@ struct monk_action_t : public parse_action_effects_t std::unique_ptr create_expression( std::string_view name_str ) override; bool usable_moving() const override; - bool ready() override; + bool usable_during_current_cast() const override; void init() override; void init_finished() override; void reset_swing(); From a29f51f42792df411e2a3485e9ebf0357b844879 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 11 Jul 2026 13:14:35 -0600 Subject: [PATCH 3/9] [monk] Clean up some unused actions. Use Slicing Winds constructor for Flying Serpent Kick actions if talent is selected. --- engine/class_modules/monk/sc_monk.cpp | 115 +------------------------- 1 file changed, 2 insertions(+), 113 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index aeb3053313e..21deaee0570 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -2799,60 +2799,6 @@ struct touch_of_karma_t : public monk_melee_attack_t } }; -struct provoke_t : public monk_melee_attack_t -{ - provoke_t( monk_t *p, std::string_view options_str ) : monk_melee_attack_t( p, "provoke", p->baseline.monk.provoke ) - { - parse_options( options_str ); - use_off_gcd = true; - ignore_false_positive = true; - } - - void impact( action_state_t *s ) override - { - if ( s->target->is_enemy() ) - target->taunt( player ); - - monk_melee_attack_t::impact( s ); - } -}; - -struct spear_hand_strike_t : public monk_melee_attack_t -{ - spear_hand_strike_t( monk_t *p, std::string_view options_str ) - : monk_melee_attack_t( p, "spear_hand_strike", p->talent.monk.spear_hand_strike ) - { - parse_options( options_str ); - ignore_false_positive = true; - is_interrupt = true; - cast_during_sck = player->specialization() != MONK_WINDWALKER; - may_miss = may_block = may_dodge = may_parry = false; - } -}; - -struct leg_sweep_t : public monk_melee_attack_t -{ - leg_sweep_t( monk_t *p, std::string_view options_str ) - : monk_melee_attack_t( p, "leg_sweep", p->baseline.monk.leg_sweep ) - { - parse_options( options_str ); - ignore_false_positive = true; - may_miss = may_block = may_dodge = may_parry = false; - cast_during_sck = true; - } -}; - -struct paralysis_t : public monk_melee_attack_t -{ - paralysis_t( monk_t *p, std::string_view options_str ) - : monk_melee_attack_t( p, "paralysis", p->talent.monk.paralysis ) - { - parse_options( options_str ); - ignore_false_positive = true; - may_miss = may_block = may_dodge = may_parry = false; - } -}; - struct flying_serpent_kick_t : public monk_melee_attack_t { bool first_charge; @@ -3139,31 +3085,6 @@ struct black_ox_brew_t : public brew_t } }; -struct roll_t : public monk_spell_t -{ - roll_t( monk_t *player, std::string_view options_str ) - : monk_spell_t( player, "roll", - ( player->talent.monk.chi_torpedo->ok() ? spell_data_t::not_found() : player->baseline.monk.roll ) ) - { - cast_during_sck = true; - - parse_options( options_str ); - } -}; - -struct chi_torpedo_t : public monk_spell_t -{ - chi_torpedo_t( monk_t *player, std::string_view options_str ) - : monk_spell_t( - player, "chi_torpedo", - ( player->talent.monk.chi_torpedo->ok() ? player->talent.monk.chi_torpedo : spell_data_t::not_found() ) ) - { - parse_options( options_str ); - - cast_during_sck = true; - } -}; - struct crackling_jade_lightning_t : public monk_spell_t { struct aoe_dot_t : public monk_spell_t @@ -4013,26 +3934,6 @@ struct zenith_t : public monk_spell_t } }; -struct vivify_t : public harmonic_surge_t -{ - vivify_t( monk_t *player, std::string_view options_str ) : base_t( player, "vivify", player->baseline.monk.vivify ) - { - parse_options( options_str ); - - spell_power_mod.direct = data().effectN( 1 ).sp_coeff(); - base_execute_time += player->talent.monk.vivacious_vivification->effectN( 1 ).time_value(); - - cast_during_sck = false; - } - - void execute() override - { - base_t::execute(); - - p()->action.chi_wave->execute(); - } -}; - struct expel_harm_t : monk_heal_t { struct damage_t : monk_spell_t @@ -5364,28 +5265,14 @@ action_t *monk_t::create_action( std::string_view name, std::string_view options return new blackout_kick_t( this, options_str ); if ( name == "expel_harm" ) return new expel_harm_t( this, options_str ); - if ( name == "leg_sweep" ) - return new leg_sweep_t( this, options_str ); - if ( name == "paralysis" ) - return new paralysis_t( this, options_str ); if ( name == "rising_sun_kick" ) return new rising_sun_kick_t( this, options_str ); if ( name == "rushing_wind_kick" ) return new rushing_wind_kick_t( this, options_str ); - if ( name == "roll" ) - return new roll_t( this, options_str ); - if ( name == "spear_hand_strike" ) - return new spear_hand_strike_t( this, options_str ); if ( name == "spinning_crane_kick" ) return new spinning_crane_kick_t( this, options_str ); - if ( name == "vivify" ) - return new vivify_t( this, options_str ); if ( name == "fortifying_brew" ) return new fortifying_brew_t( this, options_str ); - if ( name == "provoke" ) - return new provoke_t( this, options_str ); - if ( name == "chi_torpedo" ) - return new chi_torpedo_t( this, options_str ); if ( name == "touch_of_death" ) return new touch_of_death_t( this, options_str ); @@ -5418,6 +5305,8 @@ action_t *monk_t::create_action( std::string_view name, std::string_view options // Windwalker if ( name == "fists_of_fury" ) return new fists_of_fury_t( this, options_str ); + if ( name == "flying_serpent_kick" && talent.windwalker.slicing_winds->ok() ) + return new slicing_winds_t( this, options_str ); if ( name == "flying_serpent_kick" ) return new flying_serpent_kick_t( this, options_str ); if ( name == "slicing_winds" ) From e952690a35efa4f42d17267691ce633c86cb5ef2 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 11 Jul 2026 14:33:38 -0600 Subject: [PATCH 4/9] [monk] Remove movement skills and kick from APL. --- engine/class_modules/apl/apl_monk.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/engine/class_modules/apl/apl_monk.cpp b/engine/class_modules/apl/apl_monk.cpp index 76e8d8f2972..526cf1aa92e 100644 --- a/engine/class_modules/apl/apl_monk.cpp +++ b/engine/class_modules/apl/apl_monk.cpp @@ -186,10 +186,6 @@ void live_apl( monk_t* player ) // Default List def->add_action( "auto_attack,target_if=max:target.time_to_die", "Default List" ); def->add_action( "touch_of_karma,target_if=max:target.time_to_die" ); - def->add_action( "roll,if=movement.distance>5", "Move to target" ); - def->add_action( "chi_torpedo,if=movement.distance>5" ); - def->add_action( "flying_serpent_kick,if=movement.distance>5" ); - def->add_action( "spear_hand_strike,if=target.debuff.casting.react" ); def->add_action( "potion,if=buff.invoke_xuen_the_white_tiger.remains>15|fight_remains<=30" ); def->add_action( "potion,if=talent.flurry_strikes&chi>2&(time<5|cooldown.zenith.up&time<5|time>300&((trinket.1.is.algethar_puzzle_box&trinket.1.cooldown.remains>100|trinket.2.is.algethar_puzzle_box&trinket.2.cooldown.remains>100)|!trinket.1.has_use_buff&!trinket.2.has_use_buff)&talent.flurry_strikes|time>300&buff.zenith.up)" ); def->add_action( "variable,name=has_external_pi,value=cooldown.invoke_power_infusion_0.duration>0", "Enable PI if available" ); From 45e0927e7b425eeeba8440a869cf1a698946b970 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 11 Jul 2026 14:33:54 -0600 Subject: [PATCH 5/9] [monk] Zenith Stomp can be cast during Celestial Conduit channel. --- engine/class_modules/monk/sc_monk.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 21deaee0570..a0f5c508b48 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -3873,6 +3873,15 @@ struct zenith_stomp_t : monk_spell_t zenith->add_child( this ); } + bool usable_during_current_cast() const override + { + if ( p()->channeling && + p()->channeling->id == p()->talent.conduit_of_the_celestials.celestial_conduit_action->id() ) + return true; + + return monk_spell_t::usable_during_current_cast(); + } + bool ready() override { if ( source == ZENITH_STOMP_TRIGGER ) From f00a1ff32717eac1ef9b84f7544ce2b30c046400 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 11 Jul 2026 14:47:13 -0600 Subject: [PATCH 6/9] [monk] Only provide two stacks of Zenith Stomp buff if TEB3 is talented. --- engine/class_modules/monk/sc_monk.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index a0f5c508b48..d81cc02a5ad 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -6344,10 +6344,8 @@ void monk_t::create_buffs() buff.zenith = make_buff_fallback( talent.windwalker.zenith->ok(), this, "zenith" ); buff.zenith_stomp = - make_buff_fallback( talent.monk.zenith_stomp->ok(), this, "zenith_stomp", talent.monk.zenith_stomp_buff ) - ->modify_initial_stack( as( talent.windwalker.tigereye_brew_3->ok() - ? talent.windwalker.tigereye_brew_3->effectN( 1 ).base_value() - : 0 ) ); + make_buff_fallback( talent.windwalker.tigereye_brew_3->ok(), this, "zenith_stomp", talent.monk.zenith_stomp_buff ) + ->set_initial_stack( as( talent.windwalker.tigereye_brew_3->effectN( 1 ).base_value() ) ); buff.rushing_wind_kick = make_buff_fallback( talent.windwalker.rushing_wind_kick->ok(), this, "rushing_wind_kick", talent.windwalker.rushing_wind_kick_buff ); From 338c59c10cb81387b09fac3ca8bb67c87cb471f5 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sat, 11 Jul 2026 15:41:53 -0600 Subject: [PATCH 7/9] [monk] Correct Zenith Stomp charges and note Zenith Stomp sqrt scaling bug. --- engine/class_modules/monk/sc_monk.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index d81cc02a5ad..2660eee363f 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -3858,8 +3858,12 @@ struct zenith_stomp_t : monk_spell_t trigger_gcd = 0_ms; } - aoe = -1; - reduced_aoe_targets = player->talent.monk.zenith_stomp->effectN( 1 ).base_value(); + aoe = -1; + // 2026-07-11 Zenith Stomp is erroneously referencing effect 1 for sqrt scaling + // in tooltip, which instead is used to set up the Zenith Stomp buff that controls + // whether or not the action is available. + // reduced_aoe_targets = player->talent.monk.zenith_stomp->effectN( 1 ).base_value(); + reduced_aoe_targets = 5.0; may_combo_strike = player->wowv_ge( { 12, 1, 0 } ); ww_mastery = true; cast_during_sck = true; @@ -3897,7 +3901,8 @@ struct zenith_stomp_t : monk_spell_t { monk_spell_t::execute(); - p()->buff.zenith_stomp->decrement(); + if ( source == ZENITH_STOMP_CAST ) + p()->buff.zenith_stomp->decrement(); } }; @@ -6343,9 +6348,8 @@ void monk_t::create_buffs() buff.zenith = make_buff_fallback( talent.windwalker.zenith->ok(), this, "zenith" ); - buff.zenith_stomp = - make_buff_fallback( talent.windwalker.tigereye_brew_3->ok(), this, "zenith_stomp", talent.monk.zenith_stomp_buff ) - ->set_initial_stack( as( talent.windwalker.tigereye_brew_3->effectN( 1 ).base_value() ) ); + buff.zenith_stomp = make_buff_fallback( talent.windwalker.tigereye_brew_3->ok(), this, "zenith_stomp", + talent.monk.zenith_stomp_buff ); buff.rushing_wind_kick = make_buff_fallback( talent.windwalker.rushing_wind_kick->ok(), this, "rushing_wind_kick", talent.windwalker.rushing_wind_kick_buff ); @@ -7285,6 +7289,10 @@ class monk_report_t : public player_report_extension_t ReportIssue( "Chi Burst consumes both stacks of the buff on use", "2024-08-09", true ); ReportIssue( "Press the Advantage Tiger Palm does not trigger Overwhelming Force", "2026-02-09", true ); ReportIssue( "Dragonfire Brew causes Breath of Fire damage to scale with Stagger level", "2026-04-14", true ); + ReportIssue( + "Zenith Stomp erroneously references effect 1 for sqrt scaling in tooltip, which is instead used to modify the " + "Zenith Stomp charge buff.", + "2026-07-11", true ); os << "
\n"; os << "

Known Bugs and Issues

\n"; From d455b3985641eba6fa9649cd1dff67d4e2c5db19 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:18:57 -0600 Subject: [PATCH 8/9] [monk] Use `player` instead of `p` in action constructors. Prototype for updates to make `use_during_sck` concept more general. --- engine/class_modules/monk/sc_monk.cpp | 137 ++++++++++++++------------ engine/class_modules/monk/sc_monk.hpp | 4 +- 2 files changed, 78 insertions(+), 63 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 2660eee363f..18033f82f40 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -46,6 +46,11 @@ Manatee #include "simulationcraft.hpp" +#define CAST_DURING( ... ) cast_during_ids = { __VA_ARGS__ } +#define SPINNING_CRANE_KICK_IDS \ + player->baseline.brewmaster.spinning_crane_kick->id(), player->baseline.monk.spinning_crane_kick->id() +#define CELESTIAL_CONDUIT_IDS player->talent.conduit_of_the_celestials.celestial_conduit_action->id() + namespace monk { namespace functions @@ -73,8 +78,10 @@ monk_action_t::monk_action_t( Args &&...args ) : parse_action_effects_t( std::forward( args )... ), ww_mastery( false ), may_combo_strike( false ), - cast_during_sck( false ), - track_cd_waste( false ) + cast_during_ids(), + track_cd_waste( false ), + persistent_multiplier_effects(), + _resource_by_stance() { range::fill( _resource_by_stance, RESOURCE_MAX ); @@ -221,12 +228,13 @@ bool monk_action_t::usable_moving() const } template -bool monk_action_t::usable_during_current_cast() const +bool monk_action_t::ready() { - if ( cast_during_sck && p()->channeling && p()->channeling->id == p()->baseline.monk.spinning_crane_kick->id() ) - return true; + if ( p()->channeling ) + base_t::usable_while_casting = range::any_of( + cast_during_ids, [ this ]( const unsigned &id ) { return id != 0 && p()->channeling->id == id; } ); - return base_t::usable_during_current_cast(); + return base_t::ready(); } template @@ -254,7 +262,7 @@ void monk_action_t::init() } } - if ( cast_during_sck ) + if ( cast_during_ids.size() && !base_t::background && !base_t::dual ) base_t::usable_while_casting = base_t::use_while_casting = true; } @@ -828,7 +836,7 @@ struct tiger_palm_t : public harmonic_surge_tbuff.combat_wisdom ); } @@ -1101,7 +1109,7 @@ struct rising_sun_kick_t : monk_melee_attack_t parse_options( options_str ); may_combo_strike = true; - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); add_child( rising_sun_kick ); } @@ -1362,7 +1370,7 @@ struct blackout_kick_t : overwhelming_force_ttalent.brewmaster.rushing_jade_wind->effectN( 1 ).trigger() ); @@ -1615,8 +1623,8 @@ struct spinning_crane_kick_t : public monk_melee_attack_t interrupt_auto_attack = player->specialization() != MONK_WINDWALKER; if ( player->specialization() == MONK_BREWMASTER ) { - dot_behavior = DOT_EXTEND; - cast_during_sck = true; + dot_behavior = DOT_EXTEND; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); } if ( player->specialization() == MONK_WINDWALKER ) @@ -1916,7 +1924,7 @@ struct whirling_dragon_punch_t : public monk_melee_attack_t // action using `tick_action` is nonviable. ticks must be scheduled manually. may_combo_strike = true; - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); aoe = new damage_t( player, "aoe", player->talent.windwalker.whirling_dragon_punch_aoe_tick ); aoe->aoe = -1; @@ -2029,7 +2037,7 @@ struct strike_of_the_windlord_t : public monk_melee_attack_t off_hand( nullptr ) { may_combo_strike = true; - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); cooldown->hasted = false; trigger_gcd = data().gcd(); @@ -2484,7 +2492,7 @@ struct keg_smash_t : monk_melee_attack_t fuel_on_the_fire( nullptr ) { parse_options( options_str ); - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); full_amount_targets = 1; reduced_aoe_targets = data().effectN( 7 ).base_value(); @@ -2605,14 +2613,14 @@ struct stomp_t : monk_melee_attack_t struct touch_of_death_t : public monk_melee_attack_t { - touch_of_death_t( monk_t *p, std::string_view options_str ) - : monk_melee_attack_t( p, "touch_of_death", p->baseline.monk.touch_of_death ) + touch_of_death_t( monk_t *player, std::string_view options_str ) + : monk_melee_attack_t( player, "touch_of_death", player->baseline.monk.touch_of_death ) { ww_mastery = true; may_crit = hasted_ticks = false; may_combo_strike = true; - cast_during_sck = true; - ignores_armor = true; // instead use the trick to have no multipliers apply? + CAST_DURING( SPINNING_CRANE_KICK_IDS ); + ignores_armor = true; // instead use the trick to have no multipliers apply? parse_options( options_str ); cooldown->duration = data().cooldown(); @@ -2688,7 +2696,8 @@ struct touch_of_death_t : public monk_melee_attack_t struct touch_of_karma_dot_t : public residual_action::residual_periodic_action_t { using base_t = residual_action::residual_periodic_action_t; - touch_of_karma_dot_t( monk_t *p ) : base_t( "touch_of_karma", p, p->baseline.windwalker.touch_of_karma_tick ) + touch_of_karma_dot_t( monk_t *player ) + : base_t( "touch_of_karma", player, player->baseline.windwalker.touch_of_karma_tick ) { may_miss = may_crit = false; dual = true; @@ -2721,13 +2730,14 @@ struct touch_of_karma_t : public monk_melee_attack_t double interval_stddev_opt; double pct_health; touch_of_karma_dot_t *touch_of_karma_dot; - touch_of_karma_t( monk_t *p, std::string_view options_str ) - : monk_melee_attack_t( p, "touch_of_karma", p->baseline.windwalker.touch_of_karma ), + + touch_of_karma_t( monk_t *player, std::string_view options_str ) + : monk_melee_attack_t( player, "touch_of_karma", player->baseline.windwalker.touch_of_karma ), interval( 100 ), interval_stddev( 0.05 ), interval_stddev_opt( 0 ), pct_health( 0.5 ), - touch_of_karma_dot( new touch_of_karma_dot_t( p ) ) + touch_of_karma_dot( new touch_of_karma_dot_t( player ) ) { add_option( opt_float( "interval", interval ) ); add_option( opt_float( "interval_stddev", interval_stddev_opt ) ); @@ -2737,7 +2747,7 @@ struct touch_of_karma_t : public monk_melee_attack_t cooldown->duration = data().cooldown(); base_dd_min = base_dd_max = 0; ap_type = attack_power_type::NO_WEAPON; - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); double max_pct = data().effectN( 3 ).percent(); @@ -2802,8 +2812,9 @@ struct touch_of_karma_t : public monk_melee_attack_t struct flying_serpent_kick_t : public monk_melee_attack_t { bool first_charge; - flying_serpent_kick_t( monk_t *p, std::string_view options_str ) - : monk_melee_attack_t( p, "flying_serpent_kick", p->baseline.windwalker.flying_serpent_kick ), first_charge( true ) + flying_serpent_kick_t( monk_t *player, std::string_view options_str ) + : monk_melee_attack_t( player, "flying_serpent_kick", player->baseline.windwalker.flying_serpent_kick ), + first_charge( true ) { parse_options( options_str ); may_crit = true; @@ -3226,7 +3237,7 @@ struct breath_of_fire_t : public monk_spell_t aoe = -1; reduced_aoe_targets = 1.0; full_amount_targets = 1; - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); dot = new dot_t( player ); add_child( dot ); @@ -3276,27 +3287,29 @@ struct fortifying_brew_t : brew_t { struct niuzaos_protection_t : public monk_absorb_t { - niuzaos_protection_t( monk_t *p ) - : monk_absorb_t( p, "niuzaos_protection", p->talent.conduit_of_the_celestials.niuzaos_protection ) + niuzaos_protection_t( monk_t *player ) + : monk_absorb_t( player, "niuzaos_protection", player->talent.conduit_of_the_celestials.niuzaos_protection ) { background = true; - target = p; - base_dd_min = p->max_health() * data().effectN( 2 ).percent(); + target = player; + base_dd_min = player->max_health() * data().effectN( 2 ).percent(); base_dd_max = base_dd_min; } }; niuzaos_protection_t *absorb; - fortifying_brew_t( monk_t *p, std::string_view options_str ) - : brew_t( p, "fortifying_brew", p->talent.monk.fortifying_brew.find_override_spell() ), - absorb( p->talent.conduit_of_the_celestials.niuzaos_protection->ok() ? new niuzaos_protection_t( p ) : nullptr ) + fortifying_brew_t( monk_t *player, std::string_view options_str ) + : brew_t( player, "fortifying_brew", player->talent.monk.fortifying_brew.find_override_spell() ), + absorb( nullptr ) { - cast_during_sck = true; - parse_options( options_str ); harmful = may_crit = false; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); + + if ( player->talent.conduit_of_the_celestials.niuzaos_protection->ok() ) + absorb = new niuzaos_protection_t( player ); } void execute() override @@ -3333,14 +3346,15 @@ struct exploding_keg_t : public monk_spell_t { cooldown_t *keg_smash; - exploding_keg_t( monk_t *p, std::string_view options_str ) - : monk_spell_t( p, "exploding_keg", p->talent.brewmaster.exploding_keg ) + exploding_keg_t( monk_t *player, std::string_view options_str ) + : monk_spell_t( player, "exploding_keg", player->talent.brewmaster.exploding_keg ) { parse_options( options_str ); - cast_during_sck = true; - aoe = -1; - add_child( p->action.exploding_keg ); + CAST_DURING( SPINNING_CRANE_KICK_IDS ); + aoe = -1; + + add_child( player->action.exploding_keg ); keg_smash = player->get_cooldown( "keg_smash" ); } @@ -3444,9 +3458,9 @@ struct purifying_brew_t : public brew_t { parse_options( options_str ); - harmful = false; - cast_during_sck = true; - use_off_gcd = true; + harmful = false; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); + use_off_gcd = true; } bool ready() override @@ -3558,7 +3572,7 @@ struct xuen_summon_t : public monk_spell_t { parse_options( options_str ); - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); } void execute() override @@ -3685,12 +3699,12 @@ struct flight_of_the_red_crane_t : conduit_of_the_celestials_container_t struct niuzao_spell_t : public monk_spell_t { - niuzao_spell_t( monk_t *p, std::string_view options_str ) - : monk_spell_t( p, "invoke_niuzao_the_black_ox", p->talent.brewmaster.invoke_niuzao_the_black_ox ) + niuzao_spell_t( monk_t *player, std::string_view options_str ) + : monk_spell_t( player, "invoke_niuzao_the_black_ox", player->talent.brewmaster.invoke_niuzao_the_black_ox ) { parse_options( options_str ); - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); // Specifically set for 10.1 class trinket harmful = true; // Forcing the minimum GCD to 750 milliseconds @@ -3709,8 +3723,8 @@ struct niuzao_spell_t : public monk_spell_t struct unity_within_t : public monk_spell_t { - unity_within_t( monk_t *p, std::string_view options_str ) - : monk_spell_t( p, "unity_within", p->talent.conduit_of_the_celestials.unity_within ) + unity_within_t( monk_t *player, std::string_view options_str ) + : monk_spell_t( player, "unity_within", player->talent.conduit_of_the_celestials.unity_within ) { parse_options( options_str ); @@ -3866,7 +3880,7 @@ struct zenith_stomp_t : monk_spell_t reduced_aoe_targets = 5.0; may_combo_strike = player->wowv_ge( { 12, 1, 0 } ); ww_mastery = true; - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS, CELESTIAL_CONDUIT_IDS ); } void init() override @@ -3968,7 +3982,7 @@ struct expel_harm_t : monk_heal_t damage( new damage_t( player ) ) { parse_options( options_str ); - cast_during_sck = true; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); if ( player->talent.windwalker.combat_wisdom->ok() ) background = true; @@ -4018,15 +4032,15 @@ struct expel_harm_t : monk_heal_t struct celestial_fortune_t : public monk_heal_t { - celestial_fortune_t( monk_t *p ) - : monk_heal_t( p, "celestial_fortune", p->baseline.brewmaster.celestial_fortune_heal ) + celestial_fortune_t( monk_t *player ) + : monk_heal_t( player, "celestial_fortune", player->baseline.brewmaster.celestial_fortune_heal ) { background = true; proc = true; target = player; may_crit = false; - base_multiplier = p->baseline.brewmaster.celestial_fortune->effectN( 1 ).percent(); + base_multiplier = player->baseline.brewmaster.celestial_fortune->effectN( 1 ).percent(); } void init() override @@ -4045,8 +4059,8 @@ struct absorb_brew_t : public brew_t : brew_t( player, name, spell_data ) { parse_options( options_str ); - cast_during_sck = true; - harmful = false; + CAST_DURING( SPINNING_CRANE_KICK_IDS ); + harmful = false; } void execute() override @@ -4474,13 +4488,14 @@ struct empty_barrel_buff_t : buffs::monk_buff_t<> struct touch_of_karma_buff_t : public monk_buff_t<> { - touch_of_karma_buff_t( monk_t *p, std::string_view n, const spell_data_t *s ) : monk_buff_t( p, n, s ) + touch_of_karma_buff_t( monk_t *player, std::string_view name, const spell_data_t *spell_data ) + : monk_buff_t( player, name, spell_data ) { default_value = 0; set_cooldown( timespan_t::zero() ); - set_trigger_spell( p->baseline.windwalker.touch_of_karma ); + set_trigger_spell( player->baseline.windwalker.touch_of_karma ); - set_duration( s->duration() ); + set_duration( spell_data->duration() ); } bool trigger( int stacks, double value, double chance, timespan_t duration ) override diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index 923171a3450..20fdf73ed50 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -63,7 +63,7 @@ struct monk_action_t : public parse_action_effects_t { bool ww_mastery; bool may_combo_strike; - bool cast_during_sck; + std::vector cast_during_ids; bool track_cd_waste; std::vector persistent_multiplier_effects; @@ -96,8 +96,8 @@ struct monk_action_t : public parse_action_effects_t std::unique_ptr create_expression( std::string_view name_str ) override; + bool ready() override; bool usable_moving() const override; - bool usable_during_current_cast() const override; void init() override; void init_finished() override; void reset_swing(); From b17057f8ea140419ec547f966f084f0399b8e9c1 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Sun, 12 Jul 2026 13:34:43 -0600 Subject: [PATCH 9/9] [monk] Fix condition used to determine when spells should be checked if they're able to be CWC'd. --- engine/class_modules/monk/sc_monk.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 18033f82f40..a591a53fff0 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -230,9 +230,10 @@ bool monk_action_t::usable_moving() const template bool monk_action_t::ready() { - if ( p()->channeling ) - base_t::usable_while_casting = range::any_of( - cast_during_ids, [ this ]( const unsigned &id ) { return id != 0 && p()->channeling->id == id; } ); + if ( cast_during_ids.size() ) + base_t::usable_while_casting = range::any_of( cast_during_ids, [ this ]( const unsigned &id ) { + return id != 0 && p()->channeling && p()->channeling->id == id; + } ); return base_t::ready(); }