From c302d411650f1cae81ba835f156f76e88099d017 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Tue, 10 Sep 2024 18:39:42 -0600 Subject: [PATCH] [gear] Update role mults for Sikran's Endless Arsenal, Shadowed Essence, Mad Queen's Mandate, Skyterror's Corrosive Organ (#9484) * [gear] Fix or add many missing \`role_mult\`s. * [gear/html] Fix Elemental Focusing Lens multipliers and improve composite base multiplier reporting. * [gear] Provide a default `role_mult` spell in case none can be found on related spells. Assert if no `desc_vars` exist for the provided effect. --- engine/player/unique_gear_thewarwithin.cpp | 99 ++++++++++++---------- engine/player/unique_gear_thewarwithin.hpp | 1 + engine/report/report_html_player.cpp | 8 +- 3 files changed, 63 insertions(+), 45 deletions(-) diff --git a/engine/player/unique_gear_thewarwithin.cpp b/engine/player/unique_gear_thewarwithin.cpp index 24e6bd82afb..ade1e1b2bc7 100644 --- a/engine/player/unique_gear_thewarwithin.cpp +++ b/engine/player/unique_gear_thewarwithin.cpp @@ -213,7 +213,7 @@ custom_cb_t primary_food( unsigned id, stat_e stat, size_t primary_idx = 3, bool auto coeff = effect.player->find_spell( food_coeff_spell_id ); auto buff = create_buff>( effect.player, effect.driver() ); - + if ( primary_idx ) { auto _amt = coeff->effectN( primary_idx ).average( effect ); @@ -578,7 +578,8 @@ void pouch_of_pocket_grenades( special_effect_t& effect ) auto missile = driver->effectN( 1 ).trigger(); auto damage = missile->effectN( 1 ).trigger(); // TODO: determine which coeff is the correct one. assuming driver is correct. - auto amount = driver->effectN( 1 ).average( effect ) * role_mult( effect ); + auto amount = driver->effectN( 1 ).average( effect ); + auto multiplier = role_mult( effect ); effect.spell_id = driver->id(); @@ -588,6 +589,8 @@ void pouch_of_pocket_grenades( special_effect_t& effect ) auto grenade = create_proc_action( "pocket_grenade", effect, damage ); grenade->base_dd_min += amount; grenade->base_dd_max += amount; + // We cannot use `*=`, as two copies of the embellishment would doubly apply role mult. + grenade->base_multiplier = multiplier; if ( found ) return; @@ -614,7 +617,8 @@ void elemental_focusing_lens( special_effect_t& effect ) if ( !gems.size() ) return; - auto amount = effect.driver()->effectN( 1 ).average( effect ) * role_mult( effect ); + auto amount = effect.driver()->effectN( 1 ).average( effect ); + auto multiplier = role_mult( effect ); effect.spell_id = effect.trigger()->id(); @@ -644,6 +648,8 @@ void elemental_focusing_lens( special_effect_t& effect ) auto dam = create_proc_action( fmt::format( "elemental_focusing_lens_{}", name ), effect, id ); dam->base_dd_min += amount; dam->base_dd_max += amount; + // We cannot use `*=`, as two copies of the embellishment would doubly apply role mult. + dam->base_multiplier = multiplier; dam->name_str_reporting = util::inverse_tokenize( name ); damages.push_back( dam ); proxy->add_child( dam ); @@ -813,7 +819,9 @@ void deepening_darkness( special_effect_t& effect ) create_proc_action( "deepening_darkness", effect, effect.player->find_spell( 446753 ), true ); damage->base_dd_min = damage->base_dd_max = - effect.driver()->effectN( 2 ).average( effect ) * role_mult( effect ) * writhing_mul( effect.player ); + effect.driver()->effectN( 2 ).average( effect ); + damage->base_multiplier *= role_mult( effect ); + damage->base_multiplier *= writhing_mul( effect.player ); auto buff = create_buff( effect.player, effect.player->find_spell( 446743 ) ) ->set_expire_callback( [ damage ]( buff_t*, int, timespan_t d ) { @@ -992,7 +1000,7 @@ void spymasters_web( special_effect_t& effect ) void execute() override { generic_proc_t::execute(); - + use_buff->expire(); use_buff->trigger( stacking_buff->check() ); stacking_buff->expire(); @@ -1135,7 +1143,7 @@ void aberrant_spellforge( special_effect_t& effect ) : generic_proc_t( e, "aberrant_shadows", 451866 ), stack( b ) { base_dd_min = base_dd_max = data->effectN( 1 ).average( e ); - base_multiplier *= role_mult( e ); + base_multiplier *= role_mult( e.player, e.player->find_spell( 445593 ) ); for ( auto a : player->action_list ) { @@ -1272,7 +1280,7 @@ void sikrans_endless_arsenal( special_effect_t& effect ) // setup flourish auto f_dam = create_proc_action( "surekian_flourish", e, 445434 ); f_dam->base_td = data->effectN( 1 ).average( e ) * f_dam->base_tick_time / f_dam->dot_duration; - f_dam->base_multiplier *= role_mult( e ); + f_dam->base_multiplier *= role_mult( e.player, e.player->find_spell( 445434 ) ); add_child( f_dam ); auto f_stance = create_buff( e.player, e.player->find_spell( 447962 ) ) @@ -1284,8 +1292,8 @@ void sikrans_endless_arsenal( special_effect_t& effect ) // setup decimation auto d_dam = create_proc_action( "surekian_decimation", e, 448090 ); // TODO: confirm there is no standard +15% per target up to five - d_dam->base_dd_min = d_dam->base_dd_max = data->effectN( 4 ).average( e ) * role_mult( e ); - d_dam->base_multiplier *= role_mult( e ); + d_dam->base_dd_min = d_dam->base_dd_max = data->effectN( 4 ).average( e ); + d_dam->base_multiplier *= role_mult( e.player, e.player->find_spell( 448090 ) ); add_child( d_dam ); auto d_shield = create_proc_action( "surekian_brutality", e, 448519 ); @@ -1325,8 +1333,8 @@ void sikrans_endless_arsenal( special_effect_t& effect ) auto b_dam = create_proc_action( "surekian_barrage", e, 445475 ); // TODO: confirm damage isn't split and has no diminishing returns b_dam->split_aoe_damage = false; - b_dam->base_dd_min = b_dam->base_dd_max = data->effectN( 6 ).average( e ) * role_mult( e ); - b_dam->base_multiplier *= role_mult( e ); + b_dam->base_dd_min = b_dam->base_dd_max = data->effectN( 6 ).average( e ); + b_dam->base_multiplier *= role_mult( e.player, e.player->find_spell( 445475 ) ); add_child( b_dam ); auto b_speed = create_buff( e.player, e.player->find_spell( 448436 ) ) @@ -1818,7 +1826,7 @@ void treacherous_transmitter( special_effect_t& effect ) } } }; - + effect.disable_buff(); effect.stat = effect.player->convert_hybrid_stat( STAT_STR_AGI_INT ); effect.execute_action = create_proc_action( "cryptic_instructions", effect ); @@ -1860,7 +1868,7 @@ void mad_queens_mandate( special_effect_t& effect ) heal_speed( e.trigger()->missile_speed() ), hp_mul( 0.5 ) // not present in spell data { - base_dd_min = base_dd_max = data->effectN( 1 ).average( e ) * role_mult( e ); + base_dd_min = base_dd_max = data->effectN( 1 ).average( e ); base_multiplier *= role_mult( e ); heal = create_proc_action( "abyssal_gluttony_heal", e, "abyssal_gluttony_heal", @@ -2272,7 +2280,7 @@ void skarmorak_shard( special_effect_t& e ) void void_pactstone( special_effect_t& e ) { auto buff = create_buff( e.player, e.player->find_spell( 450962 ) ) - // Will Throw a warning currently, as the mod rating misc_value1 is empty. Does not work in game either. + // Will Throw a warning currently, as the mod rating misc_value1 is empty. Does not work in game either. ->add_stat_from_effect_type( A_MOD_RATING, e.driver()->effectN( 2 ).average( e ) ); auto damage = create_proc_action( "void_pulse", e, 450960 ); @@ -2490,7 +2498,7 @@ void opressive_orators_larynx( special_effect_t& e ) { background = true; base_dd_min = base_dd_max = equip_driver->effectN( 2 ).average( e ); - base_multiplier = role_mult( e ); + base_multiplier *= role_mult( e ); } double composite_da_multiplier( const action_state_t* state ) const override @@ -2569,7 +2577,7 @@ void arakara_sacbrood( special_effect_t& e ) { damage = create_proc_action( "spidersting", e, e.player->find_spell( 452229 ) ); damage->base_td = e.player->find_spell( 443541 )->effectN( 2 ).average( e ); - damage->base_multiplier = role_mult( e ); + damage->base_multiplier *= role_mult( e.player, e.player->find_spell( 443541 ) ); missile = create_proc_action( "spiderfling", e, e.player->find_spell( 452227 ) ); missile->impact_action = damage; } @@ -2583,7 +2591,7 @@ void arakara_sacbrood( special_effect_t& e ) // In game this buff seems to stack infinitely, while data suggests 1 max stack. auto spiderling_buff = create_buff( e.player, e.player->find_spell( 452226 ) ) - ->set_max_stack( 99 ); + ->set_max_stack( 99 ); auto spiderling = new special_effect_t( e.player ); spiderling->name_str = "spiderling"; @@ -2624,7 +2632,7 @@ void skyterrors_corrosive_organ( special_effect_t& e ) background = true; aoe = data().max_targets(); split_aoe_damage = false; - base_dd_min = base_dd_max = equip_driver->effectN( 2 ).average( e ) * role_mult( e ); + base_dd_min = base_dd_max = equip_driver->effectN( 2 ).average( e ); base_multiplier *= role_mult( e ); } @@ -2925,7 +2933,8 @@ void mereldars_toll( special_effect_t& effect ) target_debuff = e.trigger(); impact_action = create_proc_action( "mereldars_toll_damage", e, e.trigger() ); - impact_action->base_dd_min = impact_action->base_dd_max = data->effectN( 2 ).average( e ) * role_mult( e ); + impact_action->base_dd_min = impact_action->base_dd_max = data->effectN( 2 ).average( e ); + impact_action->base_multiplier *= role_mult( e ); // TODO: confirm 950ms delay in damage impact_action->travel_delay = e.driver()->effectN( 2 ).misc_value1() * 0.001; impact_action->stats = stats; @@ -3091,7 +3100,7 @@ void signet_of_the_priory( special_effect_t& effect ) effect.disable_buff(); auto signet = debug_cast( create_proc_action( "signet_of_the_priory", effect, data ) ); - effect.execute_action = signet; + effect.execute_action = signet; effect.stat = STAT_ANY_DPS; // TODO: determine reasonable default for party buff options @@ -3131,9 +3140,10 @@ void harvesters_edict( special_effect_t& effect ) // TODO: confirm damage doesn't increase per extra target auto damage = create_proc_action( "volatile_blood_blast", effect, effect.driver() ); damage->base_dd_min = damage->base_dd_max = - effect.driver()->effectN( 1 ).average( effect ) * role_mult( effect ); + effect.driver()->effectN( 1 ).average( effect ); + damage->base_multiplier *= role_mult( effect ); // TODO: determine travel speed to hit target, assuming 5yd/s based on 443549 range/duration - damage->travel_speed = 5.0; + damage->travel_speed = 5.0; auto buff = create_buff( effect.player, effect.player->find_spell( 451303 ) ) ->add_stat_from_effect_type( A_MOD_RATING, effect.driver()->effectN( 2 ).average( effect ) ); @@ -3369,7 +3379,7 @@ void darkmoon_deck_vivacity( special_effect_t& effect ) magical_multi( nullptr ) { auto values = e.player->find_spell( 454857 ); - + impact = create_buff( e.player, e.player->find_spell( 454862 ) ) ->add_stat_from_effect( 1, values->effectN( 1 ).average( e ) ) ->add_stat_from_effect( 2, values->effectN( 2 ).average( e ) ); @@ -3437,7 +3447,7 @@ void darkmoon_deck_vivacity( special_effect_t& effect ) effect.spell_id = 454859; - effect.proc_flags2_ = PF2_ALL_HIT | PF2_PERIODIC_DAMAGE; + effect.proc_flags2_ = PF2_ALL_HIT | PF2_PERIODIC_DAMAGE; new vivacity_cb_t( effect ); } @@ -3878,6 +3888,7 @@ void shadowed_essence( special_effect_t& effect ) // TODO: determine if damage is affected by role mult auto damage = create_proc_action( "shadowed_essence_damage", e, 455654 ); damage->base_dd_min = damage->base_dd_max = e.driver()->effectN( 1 ).average( e ); + damage->base_multiplier *= role_mult( e.player ); auto missile = create_proc_action( "shadowed_essence", e, 455653 ); missile->add_child( damage ); @@ -4070,7 +4081,7 @@ void shining_arathor_insignia( special_effect_t& effect ) // TODO: determine if this is affected by role mult auto damage_proc = create_proc_action( "shining_arathor_insignia_damage", effect, 455433 ); damage_proc->base_dd_min = damage_proc->base_dd_max = effect.driver()->effectN( 1 ).average( effect ); - + effect.execute_action = damage_proc; new dbc_proc_callback_t( effect.player, effect ); @@ -4086,7 +4097,8 @@ void void_reapers_claw( special_effect_t& effect ) effect.tick = effect.trigger()->effectN( 1 ).period(); // TODO: confirm effect value is for the entire dot and not per tick effect.discharge_amount = - effect.driver()->effectN( 1 ).average( effect ) * effect.tick / effect.duration_ * role_mult( effect ); + effect.driver()->effectN( 1 ).average( effect ) * effect.tick / effect.duration_; + effect.discharge_amount *= role_mult( effect ); new dbc_proc_callback_t( effect.player, effect ); } @@ -4211,16 +4223,6 @@ void befoulers_syringe( special_effect_t& effect ) } }; - struct befouling_strike_t : public generic_proc_t - { - befouling_strike_t( const special_effect_t& e ) - : generic_proc_t( e, "befouling_strike", e.player->find_spell( 442280 ) ) - { - base_dd_min = base_dd_max = e.driver()->effectN( 2 ).average( e ); - base_multiplier *= role_mult( e ); - } - }; - // create on-next melee damage auto strike = create_proc_action( "befouling_strike", effect, 442280 ); strike->base_dd_min = strike->base_dd_max = effect.driver()->effectN( 2 ).average( effect ); @@ -4264,7 +4266,8 @@ void voltaic_stormcaller( special_effect_t& effect ) voltaic_stormstrike_t( const special_effect_t& e ) : generic_aoe_proc_t( e, "voltaic_stormstrike", 455910, true ) { - base_dd_min = base_dd_max = e.driver()->effectN( 1 ).average( e ) * role_mult( e ); + base_dd_min = base_dd_max = e.driver()->effectN( 1 ).average( e ); + base_multiplier *= role_mult( e ); buff = create_buff( e.player, e.player->find_spell( 456652 ) ); buff->add_stat_from_effect_type( A_MOD_RATING, e.driver()->effectN( 2 ).average( e ) ); @@ -4298,7 +4301,7 @@ void harvesters_interdiction( special_effect_t& effect ) new dbc_proc_callback_t( effect.player, effect ); } -// Siphoning Stilleto +// Siphoning Stilleto // 453573 Driver // Effect 1: Self Damage // Effect 2: Damage @@ -4329,7 +4332,9 @@ void siphoning_stilleto( special_effect_t& effect ) damage = create_proc_action( "siphoning_stilleto", e, 458624 ); damage->base_dd_min = damage->base_dd_max = - e.driver()->effectN( 2 ).average( e ) * role_mult( e ) * writhing_mul( e.player ); + e.driver()->effectN( 2 ).average( e ); + damage->base_multiplier *= role_mult( e ); + damage->base_multiplier *= writhing_mul( e.player ); } void execute( action_t*, action_state_t* ) override @@ -4618,7 +4623,7 @@ void woven_dusk( special_effect_t& effect ) auto buff = create_buff( effect.player, effect.player->find_spell( 457630 ) ) ->add_stat_from_effect_type( A_MOD_RATING, effect.driver()->effectN( 2 ).average( effect ) ); - // Something *VERY* weird is going on here. It appears to work properly for Discipline Priest, but not Shadow. + // Something *VERY* weird is going on here. It appears to work properly for Discipline Priest, but not Shadow. if ( effect.player->specialization() == PRIEST_DISCIPLINE ) { buff->set_chance( 1.0 )->set_rppm( RPPM_DISABLE ); @@ -4877,8 +4882,8 @@ void register_special_effects() register_special_effect( 455419, items::spelunkers_waning_candle ); register_special_effect( 455436, items::unstable_power_core ); register_special_effect( 451742, items::stormrider_flight_badge ); - register_special_effect( 451750, DISABLED_EFFECT ); // stormrider flight badge special effect - register_special_effect( 435502, items::shadowbinding_ritual_knife ); + register_special_effect( 451750, DISABLED_EFFECT ); // stormrider flight badge special effect + register_special_effect( 435502, items::shadowbinding_ritual_knife ); register_special_effect( 455432, items::shining_arathor_insignia ); // Weapons @@ -4925,8 +4930,10 @@ action_t* create_action( player_t* p, util::string_view n, util::string_view opt double role_mult( player_t* player, const spell_data_t* s_data ) { double mult = 1.0; + auto vars = player->dbc->spell_desc_vars( s_data->id() ).desc_vars(); - if ( auto vars = player->dbc->spell_desc_vars( s_data->id() ).desc_vars() ) + assert( vars && "No spell description variables found. role_mult( player_t* ) can provide a default value." ); + if ( vars ) { std::cmatch m; std::regex get_var( R"(\$rolemult=\$(.*))" ); // find the $rolemult= variable @@ -4961,6 +4968,12 @@ double role_mult( const special_effect_t& effect ) return role_mult( effect.player, effect.driver() ); } +// Default role_mult if none can be found on any related spell. +double role_mult( player_t *p ) +{ + return role_mult( p, p->find_spell( 445339 ) ); +} + // writhing armor banding embellishment, doubles nerubian embellishment values double writhing_mul( player_t* p ) { diff --git a/engine/player/unique_gear_thewarwithin.hpp b/engine/player/unique_gear_thewarwithin.hpp index ae5924ae63c..26bb2ba1d39 100644 --- a/engine/player/unique_gear_thewarwithin.hpp +++ b/engine/player/unique_gear_thewarwithin.hpp @@ -24,5 +24,6 @@ void register_hotfixes(); action_t* create_action( player_t*, std::string_view, std::string_view ); double role_mult( player_t*, const spell_data_t* ); double role_mult( const special_effect_t& ); +double role_mult( player_t* ); double writhing_mul( player_t* ); } // namespace unique_gear::thewarwithin diff --git a/engine/report/report_html_player.cpp b/engine/report/report_html_player.cpp index 9b55c86cfea..dac3ecfd654 100644 --- a/engine/report/report_html_player.cpp +++ b/engine/report/report_html_player.cpp @@ -1036,13 +1036,15 @@ void print_html_action_info( report::sc_html_stream& os, unsigned stats_mask, co "
  • spell_power_mod.direct:{:.6f}
  • " "
  • base_dd_min:{:.2f}
  • " "
  • base_dd_max:{:.2f}
  • " - "
  • base_dd_mult:{:.2f}
  • \n", + "
  • base_dd_mult:{:.2f}
  • \n" + "
  • base_multiplier:{:.2f}
  • \n", a->may_crit ? "true" : "false", a->attack_power_mod.direct, a->spell_power_mod.direct, a->base_dd_min, a->base_dd_max, - a->base_dd_multiplier ); + a->base_dd_multiplier, + a->base_multiplier ); } if ( a->dot_duration > timespan_t::zero() ) @@ -1056,6 +1058,7 @@ void print_html_action_info( report::sc_html_stream& os, unsigned stats_mask, co "
  • spell_power_mod.tick:{:.6f}
  • " "
  • base_td:{:.2f}
  • " "
  • base_td_mult:{:.2f}
  • " + "
  • base_multiplier:{:.2f}
  • " "
  • dot_duration:{:.2f}
  • " "
  • base_tick_time:{:.2f}
  • " "
  • hasted_ticks:{}
  • " @@ -1068,6 +1071,7 @@ void print_html_action_info( report::sc_html_stream& os, unsigned stats_mask, co a->spell_power_mod.tick, a->base_td, a->base_td_multiplier, + a->base_multiplier, a->dot_duration.total_seconds(), a->base_tick_time.total_seconds(), a->hasted_ticks ? "true" : "false",