diff --git a/engine/class_modules/monk/sc_monk.cpp b/engine/class_modules/monk/sc_monk.cpp index 6f2108ecfe9..72d0ee8ee7d 100644 --- a/engine/class_modules/monk/sc_monk.cpp +++ b/engine/class_modules/monk/sc_monk.cpp @@ -589,7 +589,7 @@ void monk_action_t::execute() base_t::execute(); - trigger_storm_earth_and_fire( this ); + // trigger_storm_earth_and_fire( this ); } template @@ -761,6 +761,30 @@ monk_buff_t::monk_buff_t( monk_td_t *target_data, std::string_view name, const s { } +void monk_buff_t::expire( timespan_t delay ) +{ + if ( !p().freeze_expiration ) + base_t::expire( delay ); +} + +void monk_buff_t::expire( action_t *action, timespan_t delay ) +{ + if ( !p().freeze_expiration ) + base_t::expire( action, delay ); +} + +void monk_buff_t::expire_override( int expiration_stacks, timespan_t remaining_duration ) +{ + if ( !p().freeze_expiration ) + base_t::expire_override( expiration_stacks, remaining_duration ); +} + +void monk_buff_t::decrement( int stacks, double value ) +{ + if ( !p().freeze_expiration ) + base_t::decrement( stacks, value ); +} + monk_td_t &monk_buff_t::get_td( player_t *t ) { return *( p().get_target_data( t ) ); @@ -819,16 +843,6 @@ struct monk_snapshot_stats_t : public snapshot_stats_t monk_snapshot_stats_t( monk_t *player, util::string_view options ) : snapshot_stats_t( player, options ) { } - - void execute() override - { - snapshot_stats_t::execute(); - - // auto *monk = debug_cast( player ); - // monk->stagger->sample_data->buffed_base_value = monk->stagger->base_value(); - // monk->stagger->sample_data->buffed_percent_player_level = monk->stagger->percent( monk->level() ); - // monk->stagger->sample_data->buffed_percent_target_level = monk->stagger->percent( target->level() ); - } }; namespace pet_summon @@ -839,6 +853,9 @@ namespace pet_summon struct storm_earth_and_fire_t : public monk_spell_t { + action_t *fire_sef; + action_t *earth_sef; + storm_earth_and_fire_t( monk_t *p, util::string_view options_str ) : monk_spell_t( p, "storm_earth_and_fire", p->talent.windwalker.storm_earth_and_fire ) { @@ -848,6 +865,9 @@ struct storm_earth_and_fire_t : public monk_spell_t trigger_gcd = timespan_t::zero(); may_combo_strike = true; callbacks = harmful = may_miss = may_crit = may_dodge = may_parry = may_block = false; + + fire_sef = new monk_spell_t( p, "sef_fire_elemental", spell_data_t::nil() ); + earth_sef = new monk_spell_t( p, "sef_earth_elemental", spell_data_t::nil() ); } bool ready() override @@ -862,8 +882,6 @@ struct storm_earth_and_fire_t : public monk_spell_t { monk_spell_t::execute(); - p()->summon_storm_earth_and_fire( data().duration() ); - if ( p()->talent.windwalker.ordered_elements.ok() ) { p()->cooldown.rising_sun_kick->reset( true ); @@ -905,9 +923,225 @@ struct storm_earth_and_fire_fixate_t : public monk_spell_t } // namespace pet_summon -namespace attacks +enum sef_type_e { + SEF_TYPE_FIRE, + SEF_TYPE_EARTH +}; + +template +struct sef_action_t : monk_spell_t +{ + struct child_action_t : TBase + { + using base_t = TBase; + sef_type_e type; + bool is_fixated; + player_t *target; + + template + child_action_t( sef_type_e type, Args &&...args ) : base_t( std::forward( args )... ), type( type ) + { + base_t::background = true; + + // Action name must be amended after construction, requiring changing out + // several members of `action_t` as well. + switch ( type ) + { + case SEF_TYPE_FIRE: + base_t::name_str += "_sef_fire"; + break; + case SEF_TYPE_EARTH: + base_t::name_str += "_sef_earth"; + break; + } + base_t::internal_id = base_t::p()->get_action_id( base_t::name_str ); + base_t::gain = base_t::p()->get_gain( base_t::name_str ); + base_t::cooldown = base_t::p()->get_cooldown( base_t::name_str, this ); + base_t::internal_cooldown = base_t::p()->get_cooldown( base_t::name_str + "_internal", this ); + base_t::stats = base_t::p()->get_stats( base_t::name_str, this ); + } + + bool from_caster_spells( const spelleffect_data_t *eff ) const + { + switch ( eff->subtype() ) + { + case A_MOD_DAMAGE_FROM_CASTER: + case A_MOD_DAMAGE_FROM_CASTER_SPELLS: + case A_MOD_CRIT_CHANCE_FROM_CASTER: + case A_MOD_CRIT_CHANCE_FROM_CASTER_SPELLS: + case A_MOD_AUTO_ATTACK_FROM_CASTER: + case A_MOD_CRIT_DAMAGE_PCT_FROM_CASTER_SPELLS: + case A_MOD_DAMAGE_FROM_CASTER_SPELLS_LABEL: + return true; + default: + return false; + } + return false; + } + + bool add_mod( const spelleffect_data_t *eff ) const + { + switch ( eff->subtype() ) + { + case A_ADD_FLAT_MODIFIER: + case A_ADD_PCT_MODIFIER: + case A_ADD_PCT_LABEL_MODIFIER: + case A_ADD_FLAT_LABEL_MODIFIER: + return true; + default: + return false; + } + return false; + } + + void init_finished() override + { + base_t::init_finished(); + + switch ( type ) + { + case SEF_TYPE_FIRE: + base_t::p()->find_action( "sef_fire_elemental" )->add_child( this ); + break; + case SEF_TYPE_EARTH: + base_t::p()->find_action( "sef_earth_elemental" )->add_child( this ); + break; + } + } + + double composite_player_multiplier( const action_state_t * ) const override + { + // Composite Player Multiplier is strictly school mods, which get skipped. + return 1.0; + } + + double composite_crit_chance() const override + { + auto cc = base_t::derived_t::composite_crit_chance(); + + for ( const auto &i : base_t::crit_chance_effects ) + if ( !from_caster_spells( i.eff ) ) + cc += base_t::get_effect_value( i ); + + return cc; + } + + double composite_crit_damage_bonus_multiplier() const override + { + // Crit Damage Bonus Multipliers double dip. + auto cd = std::pow( base_t::derived_t::composite_crit_damage_bonus_multiplier(), 2 ); + + // If non-A_ADD, apply again. + for ( const auto &i : base_t::crit_bonus_effects ) + if ( !add_mod( i.eff ) ) + cd *= 1.0 + base_t::get_effect_value( i, false ); + + return cd; + } + double composite_target_crit_damage_bonus_multiplier( player_t *target ) const override + { + auto cd = base_t::derived_t::composite_target_crit_damage_bonus_multiplier( target ); + auto td = base_t::p()->get_target_data( target ); + + for ( const auto &i : base_t::target_crit_bonus_effects ) + if ( !from_caster_spells( i.eff ) ) + cd *= 1.0 + base_t::get_effect_value( i, td ); + + return cd; + } + + double composite_da_multiplier( const action_state_t *state ) const override + { + auto da = base_t::derived_t::composite_da_multiplier( state ); + + for ( const auto &i : base_t::da_multiplier_effects ) + if ( !from_caster_spells( i.eff ) ) + da *= 1.0 + base_t::get_effect_value( i, false ); + + return da; + } + + double composite_ta_multiplier( const action_state_t *state ) const override + { + auto ta = base_t::derived_t::composite_ta_multiplier( state ); + + for ( const auto &i : base_t::ta_multiplier_effects ) + if ( !from_caster_spells( i.eff ) ) + ta *= 1.0 + base_t::get_effect_value( i, false ); + + return ta; + } + }; + + action_t *parent_action; + action_t *sef_fire_action; + action_t *sef_earth_action; + + template + sef_action_t( TBase *parent, Args &&...args ) + : monk_spell_t( parent->p(), parent->name_str + "_sef_composite" ), + parent_action( parent ), + sef_fire_action( new child_action_t( SEF_TYPE_FIRE, parent->p(), std::forward( args )... ) ), + sef_earth_action( new child_action_t( SEF_TYPE_EARTH, parent->p(), std::forward( args )... ) ) + { + } + + void execute() override + { + p()->freeze_expiration = true; + sef_fire_action->execute(); + sef_earth_action->execute(); + p()->freeze_expiration = false; + + parent_action->execute(); + base_t::execute(); + } +}; + +// struct sef_action_t : base_t +// { +// action_t *sef_fire_action; +// action_t *sef_earth_action; + +// // TODO: Target Fire/Earth actions +// /* +// * If a non-sleeping target exists with MotC debuff <10s, target that. +// * If it is already targeted by another elemental, do not retarget. +// * If all MotC debuffs >=10s, do not retarget. +// * If fixated, do not retarget. +// */ + +// template +// sef_action_t( monk_t *player, Args &&...args ) : base_t( player, std::forward( args )... ) +// { +// if ( !player->talent.windwalker.storm_earth_and_fire->ok() ) +// return; + +// sef_fire_action = +// new child_action_t( player, fmt::format( "{}_{}", base_t::name_str, "fire" ), std::forward( args )... +// ); +// sef_earth_action = +// new child_action_t( player, fmt::format( "{}_{}", base_t::name_str, "earth" ), std::forward( args )... +// ); + +// // TODO: Set SEF actions as children of the parent SEF action for each elemental. +// } + +// void execute() override +// { +// base_t::p()->freeze_expiration = true; +// sef_fire_action->execute_on_target( base_t::p()->target ); +// sef_earth_action->execute_on_target( base_t::p()->target ); + +// base_t::p()->freeze_expiration = false; +// base_t::execute(); +// } +// }; + +namespace attacks +{ // ========================================================================== // Windwalking Aura Toggle // ========================================================================== @@ -1708,7 +1942,7 @@ struct charred_passions_t : base_action_t // Blackout Kick Baseline ability ======================================= struct blackout_kick_t : overwhelming_force_t> { - blackout_kick_totm_proc_t *bok_totm_proc; + action_t *bok_totm_proc; cooldown_t *keg_smash_cooldown; blackout_kick_t( monk_t *p, util::string_view options_str ) @@ -1739,8 +1973,13 @@ struct blackout_kick_t : overwhelming_force_tshared.teachings_of_the_monastery->ok() ) { - bok_totm_proc = new blackout_kick_totm_proc_t( p ); - add_child( bok_totm_proc ); + if ( action_t *totm = p->find_action( "blackout_kick_totm_proc" ); totm ) + bok_totm_proc = totm; + else + { + bok_totm_proc = new blackout_kick_totm_proc_t( p ); + add_child( bok_totm_proc ); + } } if ( p->baseline.windwalker.blackout_kick_rank_2->ok() ) @@ -1848,8 +2087,8 @@ struct blackout_kick_t : overwhelming_force_ttalent.windwalker.memory_of_the_monastery.enabled() && p()->bugs ) { - // TODO: Confirm proper mechanics for this. Tested 17/06/2024 and behaviour has it expire previous stacks before - // triggering new which feels like a bug. + // TODO: Confirm proper mechanics for this. Tested 17/06/2024 and behaviour has it expire previous stacks + // before triggering new which feels like a bug. p()->buff.memory_of_the_monastery->expire(); } @@ -4251,7 +4490,7 @@ struct celestial_conduit_t : public monk_spell_t struct celestial_conduit_dmg_t : public monk_spell_t { celestial_conduit_dmg_t( monk_t *p ) - : monk_spell_t( p, "celestial_conduit_dmg", p->talent.conduit_of_the_celestials.celestial_conduit_dmg ) + : monk_spell_t( p, "celestial_conduit_damage", p->talent.conduit_of_the_celestials.celestial_conduit_dmg ) { background = true; aoe = -1; @@ -6292,6 +6531,7 @@ monk_t::monk_t( sim_t *sim, util::string_view name, race_e r ) efficient_training_energy( 0 ), flurry_strikes_energy( 0 ), flurry_strikes_damage( 0 ), + freeze_expiration( false ), buff(), gain(), proc(), @@ -6339,9 +6579,9 @@ monk_t::monk_t( sim_t *sim, util::string_view name, race_e r ) } user_options.initial_chi = talent.windwalker.combat_wisdom.ok() ? (int)talent.windwalker.combat_wisdom->effectN( 1 ).base_value() : 0; - user_options.chi_burst_healing_targets = 8; - user_options.motc_override = 0; - user_options.squirm_frequency = 15; + user_options.motc_override = 0; + user_options.squirm_frequency = 15; + user_options.sef_beta = false; } void monk_t::parse_player_effects() @@ -6449,121 +6689,117 @@ action_t *monk_t::create_action( util::string_view name, util::string_view optio if ( name == "auto_attack" ) return new auto_attack_t( this, options_str ); if ( name == "crackling_jade_lightning" ) - return new crackling_jade_lightning_t( this, options_str ); + return make_action( options_str ); if ( name == "tiger_palm" ) - return new tiger_palm_t( this, options_str ); + return make_action( options_str ); if ( name == "blackout_kick" ) - return new blackout_kick_t( this, options_str ); + return make_action( options_str ); if ( name == "expel_harm" ) - return new expel_harm_t( this, options_str ); + return make_action( options_str ); if ( name == "leg_sweep" ) - return new leg_sweep_t( this, options_str ); + return make_action( options_str ); if ( name == "paralysis" ) - return new paralysis_t( this, options_str ); + return make_action( options_str ); if ( name == "rising_sun_kick" ) - return new rising_sun_kick_t( this, options_str ); + return make_action( options_str ); if ( name == "roll" ) - return new roll_t( this, options_str ); + return make_action( options_str ); if ( name == "spear_hand_strike" ) - return new spear_hand_strike_t( this, options_str ); + return make_action( options_str ); if ( name == "spinning_crane_kick" ) - return new spinning_crane_kick_t( this, options_str ); + return make_action( options_str ); if ( name == "vivify" ) - return new vivify_t( this, options_str ); + return make_action( options_str ); // Brewmaster if ( name == "breath_of_fire" ) - return new breath_of_fire_t( this, options_str ); + return make_action( options_str ); if ( name == "celestial_brew" ) - return new celestial_brew_t( this, options_str ); + return make_action( options_str ); if ( name == "exploding_keg" ) - return new exploding_keg_t( this, options_str ); + return make_action( options_str ); if ( name == "fortifying_brew" ) - return new fortifying_brew_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_niuzao" ) - return new niuzao_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_niuzao_the_black_ox" ) - return new niuzao_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "keg_smash" ) - return new press_the_advantage_t( this, options_str ); + return make_action>( options_str ); if ( name == "purifying_brew" ) - return new purifying_brew_t( this, options_str ); + return make_action( options_str ); if ( name == "provoke" ) - return new provoke_t( this, options_str ); + return make_action( options_str ); // Mistweaver if ( name == "enveloping_mist" ) - return new enveloping_mist_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_chiji" ) - return new chiji_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_chiji_the_red_crane" ) - return new chiji_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_yulon" ) - return new yulon_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_yulon_the_jade_serpent" ) - return new yulon_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "life_cocoon" ) - return new life_cocoon_t( this, options_str ); + return make_action( options_str ); if ( name == "mana_tea" ) - return new mana_tea_t( this, options_str ); - // if ( name == "renewing_mist" ) - // return new renewing_mist_t( this, options_str ); + return make_action( options_str ); if ( name == "revival" ) - return new revival_t( this, options_str ); + return make_action( options_str ); if ( name == "thunder_focus_tea" ) - return new thunder_focus_tea_t( this, options_str ); - // if ( name == "zen_pulse" ) - // return new zen_pulse_t( this, options_str ); + return make_action( options_str ); // Windwalker if ( name == "fists_of_fury" ) - return new fists_of_fury_t( this, options_str ); + return make_action( options_str ); if ( name == "flying_serpent_kick" ) - return new flying_serpent_kick_t( this, options_str ); + return make_action( options_str ); if ( name == "touch_of_karma" ) - return new touch_of_karma_t( this, options_str ); + return make_action( options_str ); if ( name == "touch_of_death" ) - return new touch_of_death_t( this, options_str ); + return make_action( options_str ); if ( name == "storm_earth_and_fire" ) - return new storm_earth_and_fire_t( this, options_str ); + return make_action( options_str ); if ( name == "storm_earth_and_fire_fixate" ) - return new storm_earth_and_fire_fixate_t( this, options_str ); + return make_action( options_str ); // Talents if ( name == "chi_burst" ) - return new chi_burst_t( this, options_str ); + return make_action( options_str ); if ( name == "chi_torpedo" ) - return new chi_torpedo_t( this, options_str ); + return make_action( options_str ); if ( name == "black_ox_brew" ) - return new black_ox_brew_t( this, options_str ); + return make_action( options_str ); if ( name == "dampen_harm" ) - return new dampen_harm_t( this, options_str ); + return make_action( options_str ); if ( name == "diffuse_magic" ) - return new diffuse_magic_t( this, options_str ); + return make_action( options_str ); if ( name == "strike_of_the_windlord" ) - return new strike_of_the_windlord_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_xuen" ) - return new xuen_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "invoke_xuen_the_white_tiger" ) - return new xuen_spell_t( this, options_str ); + return make_action( options_str ); if ( name == "refreshing_jade_wind" ) - return new refreshing_jade_wind_t( this, options_str ); + return make_action( options_str ); if ( name == "rushing_jade_wind" ) - return new rushing_jade_wind_t( this, options_str ); + return make_action( options_str ); if ( name == "whirling_dragon_punch" ) - return new whirling_dragon_punch_t( this, options_str ); + return make_action( options_str ); // Covenant Abilities if ( name == "jadefire_stomp" ) - return new jadefire_stomp_t( this, options_str ); + return make_action( options_str ); if ( name == "weapons_of_order" ) - return new weapons_of_order_t( this, options_str ); + return make_action( options_str ); // Hero Talents if ( name == "celestial_conduit" ) - return new celestial_conduit_t( this, options_str ); + return make_action( options_str ); if ( name == "unity_within" ) - return new unity_within_t( this, options_str ); + return make_action( options_str ); return base_t::create_action( name, options_str ); } @@ -8791,9 +9027,9 @@ void monk_t::create_options() base_t::create_options(); add_option( opt_int( "monk.initial_chi", user_options.initial_chi, 0, 6 ) ); - add_option( opt_int( "monk.chi_burst_healing_targets", user_options.chi_burst_healing_targets, 0, 30 ) ); add_option( opt_int( "monk.motc_override", user_options.motc_override, 0, 5 ) ); add_option( opt_float( "monk.squirm_frequency", user_options.squirm_frequency, 0, 30 ) ); + add_option( opt_bool( "monk.sef_beta", user_options.sef_beta ) ); } // monk_t::copy_from ========================================================= @@ -8807,6 +9043,32 @@ void monk_t::copy_from( player_t *source ) user_options = source_p->user_options; } +template +action_t *monk_t::make_action( Args &&...args ) +{ + // 1. create action A no matter what + // 2. if the action should not be replicated (channeled, repeated action), return action A + // 3. if action A is affected by sef, create sef composite action B using action A + // 4. when executing, use sef composite action B execute as to override necessary behaviour + + TAction *parent = new TAction( this, std::forward( args )... ); + if ( parent->channeled ) + return parent; + if ( talent.windwalker.storm_earth_and_fire->ok() && + parent->data().affected_by( talent.windwalker.storm_earth_and_fire ) ) + return new actions::sef_action_t( parent, std::forward( args )... ); + return parent; +} + +// monk_t::copy_from ========================================================= +action_t *monk_t::find_action( unsigned int id ) const +{ + for ( action_t *action : action_list ) + if ( id == action->id ) + return action; + return nullptr; +} + // monk_t::primary_resource ================================================= resource_e monk_t::primary_resource() const diff --git a/engine/class_modules/monk/sc_monk.hpp b/engine/class_modules/monk/sc_monk.hpp index 7fdb544461d..e5a0d8acdf2 100644 --- a/engine/class_modules/monk/sc_monk.hpp +++ b/engine/class_modules/monk/sc_monk.hpp @@ -83,9 +83,14 @@ enum class sef_ability_e SEF_MAX }; +template +struct sef_action_t; + template struct monk_action_t : public parse_action_effects_t { + using derived_t = Base; + using base_t = parse_action_effects_t; sef_ability_e sef_ability; bool ww_mastery; bool may_combo_strike; @@ -97,8 +102,6 @@ struct monk_action_t : public parse_action_effects_t std::array _resource_by_stance; public: - using base_t = parse_action_effects_t; - template monk_action_t( Args &&...args ); std::string full_name() const; @@ -176,6 +179,8 @@ struct monk_melee_attack_t : public monk_action_t struct monk_buff_t : public buff_t { + using base_t = buff_t; + monk_buff_t( monk_t *player, std::string_view name, const spell_data_t *spell_data = spell_data_t::nil(), const item_t *item = nullptr ); monk_buff_t( monk_td_t *player, std::string_view name, const spell_data_t *spell_data = spell_data_t::nil(), @@ -184,6 +189,11 @@ struct monk_buff_t : public buff_t const monk_td_t *find_td( player_t *target ) const; monk_t &p(); const monk_t &p() const; + + void expire( timespan_t delay = timespan_t::zero() ) override; + void expire( action_t *action, timespan_t delay = timespan_t::zero() ); + void decrement( int stacks = 1, double value = DEFAULT_VALUE() ) override; + void expire_override( int expiration_stacks, timespan_t remaining_duration ) override; }; struct summon_pet_t : public monk_spell_t @@ -489,6 +499,8 @@ struct monk_t : public stagger_t int efficient_training_energy; int flurry_strikes_energy; double flurry_strikes_damage; + bool freeze_expiration; + bool freeze_buffs; //============================================== // Monk Movement @@ -1302,9 +1314,10 @@ struct monk_t : public stagger_t int initial_chi; double expel_harm_effectiveness; double jadefire_stomp_uptime; - int chi_burst_healing_targets; int motc_override; double squirm_frequency; + + bool sef_beta; } user_options; // exterminate these structs @@ -1384,6 +1397,10 @@ struct monk_t : public stagger_t void reset() override; void create_options() override; void copy_from( player_t * ) override; + template + action_t *make_action( Args &&...args ); + using player_t::find_action; + action_t *find_action( unsigned int id ) const; resource_e primary_resource() const override; role_e primary_role() const override; stat_e convert_hybrid_stat( stat_e s ) const override; diff --git a/engine/class_modules/monk/sc_monk_pets.cpp b/engine/class_modules/monk/sc_monk_pets.cpp index 5ab5f7e6f7c..4c078ff1183 100644 --- a/engine/class_modules/monk/sc_monk_pets.cpp +++ b/engine/class_modules/monk/sc_monk_pets.cpp @@ -349,20 +349,6 @@ struct monk_pet_buff_t : public buff_t return p().o(); }; }; - -// =============================================================================== -// Tier 28 Primordial Power Buff -// =============================================================================== - -struct primordial_power_buff_t : public monk_pet_buff_t -{ - primordial_power_buff_t( monk_pet_t &p, util::string_view n, const spell_data_t *s ) : monk_pet_buff_t( p, n, s ) - { - add_invalidate( CACHE_PLAYER_DAMAGE_MULTIPLIER ); - set_reverse( true ); - set_reverse_stack_count( s->max_stacks() ); - } -}; } // namespace buffs // ========================================================================== @@ -376,58 +362,42 @@ struct storm_earth_and_fire_pet_t : public monk_pet_t template struct sef_action_base_t : public pet_action_base_t { - using super_t = pet_action_base_t; - using base_t = sef_action_base_t; + using base_t = pet_action_base_t; const action_t *source_action; - // Windwalker Tier 28 4-piece info - // Currently Primordial Power is only a visual buff and not tied to any direct damage buff - // the buff is pulled from the player - // Currently the buff appears if SEF is summoned before Primordial Potential becomes Primordial Power - // buff does not appear if SEF is summoned after Primordial Power is active. - - sef_action_base_t( util::string_view n, storm_earth_and_fire_pet_t *p, - const spell_data_t *data = spell_data_t::nil() ) - : super_t( n, p, data ), source_action( nullptr ) + sef_action_base_t( std::string_view name, storm_earth_and_fire_pet_t *p, + const spell_data_t *spell_data = spell_data_t::nil() ) + : base_t( name, p, spell_data ), source_action( nullptr ) { - // Make SEF attacks always background, so they do not consume resources - // or do anything associated with "foreground actions". - this->background = this->may_crit = true; - this->callbacks = false; + base_t::background = true; + base_t::cooldown->duration = timespan_t::zero(); - // Cooldowns are handled automatically by the mirror abilities, the SEF specific ones need none. - this->cooldown->duration = timespan_t::zero(); + // TODO: Is this correct? + base_t::callbacks = false; + } - // No costs are needed either - this->base_costs[ RESOURCE_ENERGY ] = 0; - this->base_costs[ RESOURCE_CHI ] = 0; + double base_cost() const override + { + return 0.0; } void init() override { - super_t::init(); + base_t::init(); - // Find source_action from the owner by matching the action name and - // spell id with eachother. This basically means that by default, any - // spell-data driven ability with 1:1 mapping of name/spell id will - // always be chosen as the source action. In some cases this needs to be - // overridden (see sef_zen_sphere_t for example). - for ( const action_t *a : this->o()->action_list ) - { - if ( ( this->id > 0 && this->id == a->id ) || util::str_compare_ci( this->name_str, a->name_str ) ) - { - source_action = a; - break; - } - } + // Look up source action based on id, falling back to name if not found. + if ( action_t *action = base_t::o()->find_action( base_t::id ) ) + source_action = action; + else + source_action = base_t::o()->find_action( base_t::name_str ); - if ( source_action ) - { - this->update_flags = source_action->update_flags; - auto pet_multiplier_snapshot = this->snapshot_flags & STATE_MUL_PET; - this->snapshot_flags = source_action->snapshot_flags | pet_multiplier_snapshot; - } + if ( !source_action ) + return; + + base_t::update_flags = source_action->update_flags; + // isn't base_t::snapshot_flags == source_action->snapshot_flags or 0? + base_t::snapshot_flags = source_action->snapshot_flags | ( base_t::snapshot_flags & STATE_MUL_PET ); } void snapshot_internal( action_state_t *state, unsigned flags, result_amount_type rt ) override @@ -509,6 +479,7 @@ struct storm_earth_and_fire_pet_t : public monk_pet_t struct sef_melee_attack_t : public sef_action_base_t { + using base_t = sef_action_base_t; sef_melee_attack_t( util::string_view n, storm_earth_and_fire_pet_t *p, const spell_data_t *data = spell_data_t::nil() ) : base_t( n, p, data ) @@ -533,6 +504,7 @@ struct storm_earth_and_fire_pet_t : public monk_pet_t struct sef_spell_t : public sef_action_base_t { + using base_t = sef_action_base_t; sef_spell_t( util::string_view n, storm_earth_and_fire_pet_t *p, const spell_data_t *data = spell_data_t::nil() ) : base_t( n, p, data ) { @@ -739,7 +711,7 @@ struct storm_earth_and_fire_pet_t : public monk_pet_t sef_glory_of_the_dawn_t *glory_of_the_dawn; sef_rising_sun_kick_dmg_t( storm_earth_and_fire_pet_t *player ) - : sef_melee_attack_t( "rising_sun_kick_dmg", player, + : sef_melee_attack_t( "rising_sun_kick_damage", player, player->o()->talent.monk.rising_sun_kick->effectN( 1 ).trigger() ) { background = true; @@ -1203,8 +1175,6 @@ struct storm_earth_and_fire_pet_t : public monk_pet_t propagate_const bok_proc_sef = nullptr; propagate_const pressure_point_sef = nullptr; propagate_const rushing_jade_wind_sef = nullptr; - // Tier 28 Buff - propagate_const primordial_power = nullptr; } buff; storm_earth_and_fire_pet_t( util::string_view name, monk_t *owner, bool dual_wield, weapon_e weapon_type )