diff --git a/engine/class_modules/apl/apl_death_knight.cpp b/engine/class_modules/apl/apl_death_knight.cpp index e88b91361c8..9056271c497 100644 --- a/engine/class_modules/apl/apl_death_knight.cpp +++ b/engine/class_modules/apl/apl_death_knight.cpp @@ -699,7 +699,6 @@ void unholy( player_t* p ) cds_shared->add_action( "antimagic_shell,if=death_knight.ams_absorb_percent>0&runic_power<30&rune<2" ); cds_shared->add_action( "legion_of_souls" ); cds_shared->add_action( "desecrate" ); - cds_shared->add_action( "apocalypse" ); cleave->add_action( "any_dnd,if=!death_and_decay.ticking&variable.adds_remain&(cooldown.apocalypse.remains|!talent.apocalypse)", "Cleave" ); cleave->add_action( "death_coil,if=!variable.pooling_runic_power&talent.improved_death_coil" ); diff --git a/engine/class_modules/sc_death_knight.cpp b/engine/class_modules/sc_death_knight.cpp index 33a7e79fc4a..135b772542f 100644 --- a/engine/class_modules/sc_death_knight.cpp +++ b/engine/class_modules/sc_death_knight.cpp @@ -155,17 +155,6 @@ struct nazgrim_pet_t; struct abomination_pet_t; } // namespace pets -namespace runeforge -{ -void apocalypse( special_effect_t& ); -void fallen_crusader( special_effect_t& ); -void razorice( special_effect_t& ); -void sanguination( special_effect_t& ); -void spellwarding( special_effect_t& ); -void stoneskin_gargoyle( special_effect_t& ); -void unending_thirst( special_effect_t& ); // Effect only procs on killing blows, NYI -} // namespace runeforge - enum runeforge_apocalypse { DEATH, @@ -1789,6 +1778,7 @@ struct death_knight_t : public parse_player_effects_t const assisted_combat_step_data_t& step ) const override; std::vector action_names_from_spell_id( unsigned int spell_id ) const override; std::string aura_expr_from_spell_id( unsigned int spell_id, bool on_self ) const override; + void create_special_effects() override; void init_rng() override; void init_base_stats() override; void init_scaling() override; @@ -11329,221 +11319,6 @@ void runic_attenuation_proc( const special_effect_t& e ) } // UNNAMED NAMESPACE -// Runeforges =============================================================== - -void runeforge::fallen_crusader( special_effect_t& effect ) -{ - struct fallen_crusader_heal_t final : public death_knight_heal_t - { - fallen_crusader_heal_t( std::string_view name, death_knight_t* p, const spell_data_t* data ) - : death_knight_heal_t( name, p, data ) - { - background = true; - target = p; - callbacks = may_crit = false; - base_pct_heal = data->effectN( 2 ).percent(); - base_pct_heal *= 1.0 + p->talent.unholy_bond->effectN( 2 ).percent(); - } - - // Procs by default target the target of the action that procced them. - void execute() override - { - target = p(); - death_knight_heal_t::execute(); - } - }; - - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - // Create unholy strength heal if necessary, buff is always created for APL support - p->runeforge.rune_of_the_fallen_crusader = true; - - effect.custom_buff = p->buffs.unholy_strength; - effect.execute_action = - get_action( "unholy_strength", p, effect.driver()->effectN( 1 ).trigger() ); - - new dbc_proc_callback_t( effect.player, effect ); -} - -void runeforge::razorice( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - if ( !p->background_actions.runeforge_razorice ) - p->background_actions.runeforge_razorice = get_action( "razorice", p ); - - // Store in which hand razorice is equipped, as it affects which abilities proc it - switch ( effect.item->slot ) - { - case SLOT_MAIN_HAND: - p->runeforge.rune_of_razorice_mh = true; - break; - case SLOT_OFF_HAND: - p->runeforge.rune_of_razorice_oh = true; - break; - default: - break; - } -} - -void runeforge::stoneskin_gargoyle( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - p->runeforge.rune_of_the_stoneskin_gargoyle = true; - - if ( !p->buffs.stoneskin_gargoyle ) - p->buffs.stoneskin_gargoyle = make_buff( p, "stoneskin_gargoyle", effect.driver() ) - ->set_default_value_from_effect_type( A_MOD_TOTAL_STAT_PERCENTAGE ); - else - p->buffs.stoneskin_gargoyle->set_max_stack( p->buffs.stoneskin_gargoyle->max_stack() + 1 ); - - // The buff isn't shown ingame, leave it visible in the sim for clarity - // p -> quiet = true; -} - -void runeforge::apocalypse( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - // Nothing happens if the runeforge is applied on both weapons - if ( p->runeforge.rune_of_apocalypse ) - return; - - p->spell.apocalypse_death_debuff = p->find_spell( 327095 ); - p->spell.apocalypse_famine_debuff = p->find_spell( 327092 ); - p->spell.apocalypse_war_debuff = p->find_spell( 327096 ); - p->spell.apocalypse_pestilence_damage = p->find_spell( 327093 ); - // Triggering the effects is handled in pet_melee_attack_t::impact() - p->runeforge.rune_of_apocalypse = true; - // Even though a pet procs it, the damage from Pestilence belongs directly to the player in logs - p->background_actions.runeforge_pestilence = get_action( "pestilence", p ); -} - -void runeforge::sanguination( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - // This runeforge doesn't stack - if ( p->runeforge.rune_of_sanguination ) - return; - - p->spell.sanguination_cooldown = p->find_spell( 326809 ); - - struct sanguination_heal_t final : public death_knight_heal_t - { - sanguination_heal_t( special_effect_t& effect ) - : death_knight_heal_t( "rune_of_sanguination", debug_cast( effect.player ), - effect.driver()->effectN( 1 ).trigger() ), - health_threshold( effect.driver()->effectN( 1 ).base_value() ) - { - background = true; - tick_pct_heal = data().effectN( 1 ).percent(); - tick_pct_heal *= 1.0 + p()->talent.unholy_bond->effectN( 1 ).percent(); - // Sated-type debuff, for simplicity the debuff's duration is used as a simple cooldown in simc - cooldown->duration = p()->spell.sanguination_cooldown->duration(); - } - - bool ready() override - { - if ( p()->health_percentage() > health_threshold ) - return false; - - return death_knight_heal_t::ready(); - } - - private: - double health_threshold; - }; - - p->runeforge.rune_of_sanguination = true; - - p->background_actions.runeforge_sanguination = new sanguination_heal_t( effect ); -} - -void runeforge::spellwarding( special_effect_t& effect ) -{ - struct spellwarding_absorb_t final : public absorb_t - { - spellwarding_absorb_t( std::string_view name, death_knight_t* p, const spell_data_t* data ) - : absorb_t( name, p, data ), health_percentage( p->spell.spellwarding_absorb->effectN( 2 ).percent() ) - // The absorb amount is hardcoded in the effect tooltip, the only data is in the runeforging action spell - { - target = p; - background = true; - harmful = false; - } - - void execute() override - { - base_dd_min = base_dd_max = health_percentage * player->resources.max[ RESOURCE_HEALTH ]; - - absorb_t::execute(); - } - - private: - double health_percentage; - }; - - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - p->spell.spellwarding_absorb = p->find_spell( 326855 ); - - // Stacking the rune doubles the damage reduction, and seems to create a second proc - p->runeforge.rune_of_spellwarding += effect.driver()->effectN( 2 ).percent(); - effect.execute_action = - get_action( "rune_of_spellwarding", p, effect.driver()->effectN( 1 ).trigger() ); - - new dbc_proc_callback_t( effect.player, effect ); -} - -// NYI -void runeforge::unending_thirst( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - // Placeholder for APL tracking purpose, effect NYI - debug_cast( effect.player )->runeforge.rune_of_unending_thirst = true; -} - // Resource Manipulation ==================================================== double death_knight_t::resource_gain( resource_e resource_type, double amount, gain_t* g, action_t* action ) @@ -13260,6 +13035,11 @@ void death_knight_t::create_pets() } } } +// death_knight_t::create_special_effects() ================================= +void death_knight_t::create_special_effects() +{ + player_t::create_special_effects(); +} // death_knight_t::init_rng ================================================= @@ -14292,9 +14072,6 @@ void death_knight_t::create_buffs() ->set_chance( 0.15 ) // This was found through testing 2022 July 21. Not in spelldata. ->set_default_value( talent.rune_mastery->effectN( 1 ).percent() ); - buffs.unholy_strength = make_buff( this, "unholy_strength", spell.unholy_strength_buff ) - ->set_default_value_from_effect_type( A_MOD_TOTAL_STAT_PERCENTAGE ); - buffs.icy_talons = make_fallback( talent.icy_talons.ok(), this, "icy_talons", talent.icy_talons->effectN( 1 ).trigger() ) ->set_default_value( talent.icy_talons->effectN( 1 ).percent() ) @@ -15910,13 +15687,6 @@ struct death_knight_module_t : public module_t void static_init() const override { - unique_gear::register_special_effect( 50401, runeforge::razorice ); - unique_gear::register_special_effect( 166441, runeforge::fallen_crusader ); - unique_gear::register_special_effect( 62157, runeforge::stoneskin_gargoyle ); - unique_gear::register_special_effect( 327087, runeforge::apocalypse ); - unique_gear::register_special_effect( 326801, runeforge::sanguination ); - unique_gear::register_special_effect( 326864, runeforge::spellwarding ); - unique_gear::register_special_effect( 326982, runeforge::unending_thirst ); } /* diff --git a/engine/class_modules/sc_death_knight_live.inc b/engine/class_modules/sc_death_knight_live.inc index 72770e55195..36a666a6bbd 100644 --- a/engine/class_modules/sc_death_knight_live.inc +++ b/engine/class_modules/sc_death_knight_live.inc @@ -1,32 +1,3 @@ -// ========================================================================== -// Dedmonwakeen's DPS-DPM Simulator. -// Send questions to natehieter@gmail.com -// ========================================================================== - -// TODO as of 2023-11-02 -// Class: -// Killing Blow based mechanics (free Death Strike, Rune of Unending Thirst) -// Disable noise from healing/defensive actions when simming a single, dps role, character -// Automate Rune energize in death_knight_action_t::execute() instead of per spell overrides -// Look into Death Strike OH handling (p -> dual_wield()?) and see if it can apply to other DW attacks -// Unholy: -// - Predict the first two Festering wounds on FS and use reaction time on the third? -// Blood: -// - Check that VB's absorb increase is correctly implemented -// - Healing from Consumption damage done - -#include "config.hpp" - -#include "action/action_callback.hpp" -#include "action/parse_effects.hpp" -#include "class_modules/apl/apl_death_knight.hpp" -#include "player/pet_spawner.hpp" - -#include "simulationcraft.hpp" - -namespace -{ // UNNAMED NAMESPACE - // Finds an action with the given name. If no action exists, a new one will // be created. // @@ -155,17 +126,6 @@ struct nazgrim_pet_t; struct abomination_pet_t; } // namespace pets -namespace runeforge -{ -void apocalypse( special_effect_t& ); -void fallen_crusader( special_effect_t& ); -void razorice( special_effect_t& ); -void sanguination( special_effect_t& ); -void spellwarding( special_effect_t& ); -void stoneskin_gargoyle( special_effect_t& ); -void unending_thirst( special_effect_t& ); // Effect only procs on killing blows, NYI -} // namespace runeforge - enum runeforge_apocalypse { DEATH, @@ -11253,221 +11213,6 @@ void runic_attenuation_proc( const special_effect_t& e ) } // UNNAMED NAMESPACE -// Runeforges =============================================================== - -void runeforge::fallen_crusader( special_effect_t& effect ) -{ - struct fallen_crusader_heal_t final : public death_knight_heal_t - { - fallen_crusader_heal_t( std::string_view name, death_knight_t* p, const spell_data_t* data ) - : death_knight_heal_t( name, p, data ) - { - background = true; - target = p; - callbacks = may_crit = false; - base_pct_heal = data->effectN( 2 ).percent(); - base_pct_heal *= 1.0 + p->talent.unholy_bond->effectN( 2 ).percent(); - } - - // Procs by default target the target of the action that procced them. - void execute() override - { - target = p(); - death_knight_heal_t::execute(); - } - }; - - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - // Create unholy strength heal if necessary, buff is always created for APL support - p->runeforge.rune_of_the_fallen_crusader = true; - - effect.custom_buff = p->buffs.unholy_strength; - effect.execute_action = - get_action( "unholy_strength", p, effect.driver()->effectN( 1 ).trigger() ); - - new dbc_proc_callback_t( effect.player, effect ); -} - -void runeforge::razorice( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - if ( !p->background_actions.runeforge_razorice ) - p->background_actions.runeforge_razorice = get_action( "razorice", p ); - - // Store in which hand razorice is equipped, as it affects which abilities proc it - switch ( effect.item->slot ) - { - case SLOT_MAIN_HAND: - p->runeforge.rune_of_razorice_mh = true; - break; - case SLOT_OFF_HAND: - p->runeforge.rune_of_razorice_oh = true; - break; - default: - break; - } -} - -void runeforge::stoneskin_gargoyle( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - p->runeforge.rune_of_the_stoneskin_gargoyle = true; - - if ( !p->buffs.stoneskin_gargoyle ) - p->buffs.stoneskin_gargoyle = make_buff( p, "stoneskin_gargoyle", effect.driver() ) - ->set_default_value_from_effect_type( A_MOD_TOTAL_STAT_PERCENTAGE ); - else - p->buffs.stoneskin_gargoyle->set_max_stack( p->buffs.stoneskin_gargoyle->max_stack() + 1 ); - - // The buff isn't shown ingame, leave it visible in the sim for clarity - // p -> quiet = true; -} - -void runeforge::apocalypse( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - // Nothing happens if the runeforge is applied on both weapons - if ( p->runeforge.rune_of_apocalypse ) - return; - - p->spell.apocalypse_death_debuff = p->find_spell( 327095 ); - p->spell.apocalypse_famine_debuff = p->find_spell( 327092 ); - p->spell.apocalypse_war_debuff = p->find_spell( 327096 ); - p->spell.apocalypse_pestilence_damage = p->find_spell( 327093 ); - // Triggering the effects is handled in pet_melee_attack_t::impact() - p->runeforge.rune_of_apocalypse = true; - // Even though a pet procs it, the damage from Pestilence belongs directly to the player in logs - p->background_actions.runeforge_pestilence = get_action( "pestilence", p ); -} - -void runeforge::sanguination( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - // This runeforge doesn't stack - if ( p->runeforge.rune_of_sanguination ) - return; - - p->spell.sanguination_cooldown = p->find_spell( 326809 ); - - struct sanguination_heal_t final : public death_knight_heal_t - { - sanguination_heal_t( special_effect_t& effect ) - : death_knight_heal_t( "rune_of_sanguination", debug_cast( effect.player ), - effect.driver()->effectN( 1 ).trigger() ), - health_threshold( effect.driver()->effectN( 1 ).base_value() ) - { - background = true; - tick_pct_heal = data().effectN( 1 ).percent(); - tick_pct_heal *= 1.0 + p()->talent.unholy_bond->effectN( 1 ).percent(); - // Sated-type debuff, for simplicity the debuff's duration is used as a simple cooldown in simc - cooldown->duration = p()->spell.sanguination_cooldown->duration(); - } - - bool ready() override - { - if ( p()->health_percentage() > health_threshold ) - return false; - - return death_knight_heal_t::ready(); - } - - private: - double health_threshold; - }; - - p->runeforge.rune_of_sanguination = true; - - p->background_actions.runeforge_sanguination = new sanguination_heal_t( effect ); -} - -void runeforge::spellwarding( special_effect_t& effect ) -{ - struct spellwarding_absorb_t final : public absorb_t - { - spellwarding_absorb_t( std::string_view name, death_knight_t* p, const spell_data_t* data ) - : absorb_t( name, p, data ), health_percentage( p->spell.spellwarding_absorb->effectN( 2 ).percent() ) - // The absorb amount is hardcoded in the effect tooltip, the only data is in the runeforging action spell - { - target = p; - background = true; - harmful = false; - } - - void execute() override - { - base_dd_min = base_dd_max = health_percentage * player->resources.max[ RESOURCE_HEALTH ]; - - absorb_t::execute(); - } - - private: - double health_percentage; - }; - - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - death_knight_t* p = debug_cast( effect.player ); - - p->spell.spellwarding_absorb = p->find_spell( 326855 ); - - // Stacking the rune doubles the damage reduction, and seems to create a second proc - p->runeforge.rune_of_spellwarding += effect.driver()->effectN( 2 ).percent(); - effect.execute_action = - get_action( "rune_of_spellwarding", p, effect.driver()->effectN( 1 ).trigger() ); - - new dbc_proc_callback_t( effect.player, effect ); -} - -// NYI -void runeforge::unending_thirst( special_effect_t& effect ) -{ - if ( effect.player->type != DEATH_KNIGHT ) - { - effect.type = SPECIAL_EFFECT_NONE; - return; - } - - // Placeholder for APL tracking purpose, effect NYI - debug_cast( effect.player )->runeforge.rune_of_unending_thirst = true; -} - // Resource Manipulation ==================================================== double death_knight_t::resource_gain( resource_e resource_type, double amount, gain_t* g, action_t* action ) @@ -15800,66 +15545,4 @@ public: private: death_knight_t& p; -}; - -// DEATH_KNIGHT MODULE INTERFACE ============================================ - -struct death_knight_module_t : public module_t -{ - death_knight_module_t() : module_t( DEATH_KNIGHT ) - { - } - - player_t* create_player( sim_t* sim, std::string_view name, race_e r = RACE_NONE ) const override - { - auto p = new death_knight_t( sim, name, r ); - p->report_extension = std::unique_ptr( new death_knight_report_t( *p ) ); - return p; - } - - void static_init() const override - { - unique_gear::register_special_effect( 50401, runeforge::razorice ); - unique_gear::register_special_effect( 166441, runeforge::fallen_crusader ); - unique_gear::register_special_effect( 62157, runeforge::stoneskin_gargoyle ); - unique_gear::register_special_effect( 327087, runeforge::apocalypse ); - unique_gear::register_special_effect( 326801, runeforge::sanguination ); - unique_gear::register_special_effect( 326864, runeforge::spellwarding ); - unique_gear::register_special_effect( 326982, runeforge::unending_thirst ); - } - - /* - void register_hotfixes() const override - { - hotfix::register_effect( "Death Knight", "2025-5-2", "Pact of the San'layn nerfed to 25%", 1124176, - hotfix::HOTFIX_FLAG_LIVE ) - .field( "base_value" ) - .operation( hotfix::HOTFIX_SET ) - .modifier( 25 ) - .verification_value( 50 ); - - hotfix::register_effect( "Death Knight", "2025-5-2", "Magus of the Dead Frostbolt buffed 40%", 803166, - hotfix::HOTFIX_FLAG_LIVE ) - .field( "ap_coefficient" ) - .operation( hotfix::HOTFIX_SET ) - .modifier( 0.8360352 ) - .verification_value( 0.597168 ); - }*/ - - void init( player_t* ) const override - { - } - bool valid() const override - { - return true; - } - void combat_begin( sim_t* ) const override - { - } - void combat_end( sim_t* ) const override - { - } -}; - -} // UNNAMED NAMESPACE - +}; \ No newline at end of file diff --git a/engine/player/death_knight_runeforges.cpp b/engine/player/death_knight_runeforges.cpp new file mode 100644 index 00000000000..57bf0ec9418 --- /dev/null +++ b/engine/player/death_knight_runeforges.cpp @@ -0,0 +1,251 @@ +#include "death_knight_runeforges.hpp" + +#include "action/absorb.hpp" +#include "action/action.hpp" +#include "action/dot.hpp" +#include "actor_target_data.hpp" +#include "buff/buff.hpp" +#include "darkmoon_deck.hpp" +#include "dbc/data_enums.hh" +#include "dbc/item_database.hpp" +#include "dbc/spell_data.hpp" +#include "ground_aoe.hpp" +#include "item/item.hpp" +#include "player/action_variable.hpp" +#include "player/consumable.hpp" +#include "player/pet_spawner.hpp" +#include "set_bonus.hpp" +#include "sim/cooldown.hpp" +#include "sim/proc_rng.hpp" +#include "sim/sim.hpp" +#include "unique_gear.hpp" +#include "unique_gear_helper.hpp" +#include "util/string_view.hpp" + +namespace unique_gear::death_knight_runeforges +{ +// can be called via unqualified lookup +void register_special_effect( unsigned spell_id, custom_cb_t init_callback, bool fallback = false ) +{ + unique_gear::register_special_effect( spell_id, init_callback, fallback ); +} + +void fallen_crusader( special_effect_t& effect ) +{ + struct fallen_crusader_heal_t final : public heal_t + { + fallen_crusader_heal_t( std::string_view name, player_t* p, const spell_data_t* data ) : heal_t( name, p, data ) + { + background = true; + target = p; + callbacks = may_crit = false; + base_pct_heal = data->effectN( 2 ).percent(); + const spell_data_t* talent = p->find_talent_spell( talent_tree::CLASS, "Unholy Bond" ); + base_pct_heal *= 1.0 + talent->effectN( 2 ).percent(); + } + + // Procs by default target the target of the action that procced them. + void execute() override + { + target = player; + heal_t::execute(); + } + }; + + if ( effect.player->type != DEATH_KNIGHT ) + { + effect.type = SPECIAL_EFFECT_NONE; + return; + } + + effect.custom_buff = create_buff( effect.player, "unholy_strength", effect.player->find_spell( 53365 ) ) + ->set_default_value_from_effect_type( A_MOD_TOTAL_STAT_PERCENTAGE ) + ->set_pct_buff_type( STAT_PCT_BUFF_STRENGTH ); + + effect.execute_action = new fallen_crusader_heal_t( "unholy_strength", effect.player, effect.driver()->effectN( 1 ).trigger() ); + + new dbc_proc_callback_t( effect.player, effect ); +} + +//void razorice( special_effect_t& effect ) +//{ +// if ( effect.player->type != DEATH_KNIGHT ) +// { +// effect.type = SPECIAL_EFFECT_NONE; +// return; +// } +// +// if ( !p->background_actions.runeforge_razorice ) +// p->background_actions.runeforge_razorice = get_action( "razorice", p ); +// +// // Store in which hand razorice is equipped, as it affects which abilities proc it +// switch ( effect.item->slot ) +// { +// case SLOT_MAIN_HAND: +// p->runeforge.rune_of_razorice_mh = true; +// break; +// case SLOT_OFF_HAND: +// p->runeforge.rune_of_razorice_oh = true; +// break; +// default: +// break; +// } +//} +// +//void stoneskin_gargoyle( special_effect_t& effect ) +//{ +// if ( effect.player->type != DEATH_KNIGHT ) +// { +// effect.type = SPECIAL_EFFECT_NONE; +// return; +// } +// +// death_knight_t* p = debug_cast( effect.player ); +// +// p->runeforge.rune_of_the_stoneskin_gargoyle = true; +// +// if ( !p->buffs.stoneskin_gargoyle ) +// p->buffs.stoneskin_gargoyle = make_buff( p, "stoneskin_gargoyle", effect.driver() ) +// ->set_default_value_from_effect_type( A_MOD_TOTAL_STAT_PERCENTAGE ); +// else +// p->buffs.stoneskin_gargoyle->set_max_stack( p->buffs.stoneskin_gargoyle->max_stack() + 1 ); +// +// // The buff isn't shown ingame, leave it visible in the sim for clarity +// // p -> quiet = true; +//} +// +//void apocalypse( special_effect_t& effect ) +//{ +// if ( effect.player->type != DEATH_KNIGHT ) +// { +// effect.type = SPECIAL_EFFECT_NONE; +// return; +// } +// +// death_knight_t* p = debug_cast( effect.player ); +// // Nothing happens if the runeforge is applied on both weapons +// if ( p->runeforge.rune_of_apocalypse ) +// return; +// +// p->spell.apocalypse_death_debuff = p->find_spell( 327095 ); +// p->spell.apocalypse_famine_debuff = p->find_spell( 327092 ); +// p->spell.apocalypse_war_debuff = p->find_spell( 327096 ); +// p->spell.apocalypse_pestilence_damage = p->find_spell( 327093 ); +// // Triggering the effects is handled in pet_melee_attack_t::impact() +// p->runeforge.rune_of_apocalypse = true; +// // Even though a pet procs it, the damage from Pestilence belongs directly to the player in logs +// p->background_actions.runeforge_pestilence = get_action( "pestilence", p ); +//} +// +//void sanguination( special_effect_t& effect ) +//{ +// if ( effect.player->type != DEATH_KNIGHT ) +// { +// effect.type = SPECIAL_EFFECT_NONE; +// return; +// } +// +// death_knight_t* p = debug_cast( effect.player ); +// // This runeforge doesn't stack +// if ( p->runeforge.rune_of_sanguination ) +// return; +// +// p->spell.sanguination_cooldown = p->find_spell( 326809 ); +// +// struct sanguination_heal_t final : public death_knight_heal_t +// { +// sanguination_heal_t( special_effect_t& effect ) +// : death_knight_heal_t( "rune_of_sanguination", debug_cast( effect.player ), +// effect.driver()->effectN( 1 ).trigger() ), +// health_threshold( effect.driver()->effectN( 1 ).base_value() ) +// { +// background = true; +// tick_pct_heal = data().effectN( 1 ).percent(); +// tick_pct_heal *= 1.0 + p()->talent.unholy_bond->effectN( 1 ).percent(); +// // Sated-type debuff, for simplicity the debuff's duration is used as a simple cooldown in simc +// cooldown->duration = p()->spell.sanguination_cooldown->duration(); +// } +// +// bool ready() override +// { +// if ( p()->health_percentage() > health_threshold ) +// return false; +// +// return death_knight_heal_t::ready(); +// } +// +// private: +// double health_threshold; +// }; +// +// p->runeforge.rune_of_sanguination = true; +// +// p->background_actions.runeforge_sanguination = new sanguination_heal_t( effect ); +//} +// +//void spellwarding( special_effect_t& effect ) +//{ +// struct spellwarding_absorb_t final : public absorb_t +// { +// spellwarding_absorb_t( std::string_view name, death_knight_t* p, const spell_data_t* data ) +// : absorb_t( name, p, data ), health_percentage( p->spell.spellwarding_absorb->effectN( 2 ).percent() ) +// // The absorb amount is hardcoded in the effect tooltip, the only data is in the runeforging action spell +// { +// target = p; +// background = true; +// harmful = false; +// } +// +// void execute() override +// { +// base_dd_min = base_dd_max = health_percentage * player->resources.max[ RESOURCE_HEALTH ]; +// +// absorb_t::execute(); +// } +// +// private: +// double health_percentage; +// }; +// +// if ( effect.player->type != DEATH_KNIGHT ) +// { +// effect.type = SPECIAL_EFFECT_NONE; +// return; +// } +// +// death_knight_t* p = debug_cast( effect.player ); +// +// p->spell.spellwarding_absorb = p->find_spell( 326855 ); +// +// // Stacking the rune doubles the damage reduction, and seems to create a second proc +// p->runeforge.rune_of_spellwarding += effect.driver()->effectN( 2 ).percent(); +// effect.execute_action = +// get_action( "rune_of_spellwarding", p, effect.driver()->effectN( 1 ).trigger() ); +// +// new dbc_proc_callback_t( effect.player, effect ); +//} +// +//// NYI +//void unending_thirst( special_effect_t& effect ) +//{ +// if ( effect.player->type != DEATH_KNIGHT ) +// { +// effect.type = SPECIAL_EFFECT_NONE; +// return; +// } +// +// // Placeholder for APL tracking purpose, effect NYI +// debug_cast( effect.player )->runeforge.rune_of_unending_thirst = true; +//} + +void register_special_effects() +{ + //register_special_effect( 50401, razorice ); + register_special_effect( 166441, fallen_crusader ); + //register_special_effect( 62157, stoneskin_gargoyle ); + //register_special_effect( 327087, apocalypse ); + //register_special_effect( 326801, sanguination ); + //register_special_effect( 326864, spellwarding ); + //register_special_effect( 326982, unending_thirst ); +} +} // namespace unique_gear::death_knight_runeforges \ No newline at end of file diff --git a/engine/player/death_knight_runeforges.hpp b/engine/player/death_knight_runeforges.hpp new file mode 100644 index 00000000000..58eca75ab0a --- /dev/null +++ b/engine/player/death_knight_runeforges.hpp @@ -0,0 +1,20 @@ +// ========================================================================== +// Dedmonwakeen's Raid DPS/TPS Simulator. +// Send questions to natehieter@gmail.com +// ========================================================================== + +#pragma once + +#include +#include + +struct sim_t; +struct special_effect_t; +struct spell_data_t; +struct action_t; +struct player_t; + +namespace unique_gear::death_knight_runeforges +{ + void register_special_effects(); +} \ No newline at end of file diff --git a/engine/player/unique_gear.cpp b/engine/player/unique_gear.cpp index 2354fe256c3..ca8e0560088 100644 --- a/engine/player/unique_gear.cpp +++ b/engine/player/unique_gear.cpp @@ -10,9 +10,11 @@ #include "player/soulbinds.hpp" #include "sc_enums.hpp" #include "sim/expressions.hpp" +#include "death_knight_runeforges.hpp" #include "unique_gear_dragonflight.hpp" #include "unique_gear_shadowlands.hpp" #include "unique_gear_thewarwithin.hpp" +#include "death_knight_runeforges.hpp" #include "util/util.hpp" #include @@ -4918,6 +4920,8 @@ void unique_gear::register_special_effects() thewarwithin::register_special_effects(); + death_knight_runeforges::register_special_effects(); + /* Legacy Effects, pre-5.0 */ register_special_effect( 45481, "ProcOn/hit_45479Trigger" ); /* Shattered Sun Pendant of Acumen */ register_special_effect( 45482, "ProcOn/hit_45480Trigger" ); /* Shattered Sun Pendant of Might */ diff --git a/source_files/QT_engine.pri b/source_files/QT_engine.pri index 867055bbd9b..13b9c9a8d8c 100644 --- a/source_files/QT_engine.pri +++ b/source_files/QT_engine.pri @@ -140,6 +140,7 @@ HEADERS += engine/player/unique_gear_dragonflight.hpp HEADERS += engine/player/unique_gear_helper.hpp HEADERS += engine/player/unique_gear_shadowlands.hpp HEADERS += engine/player/unique_gear_thewarwithin.hpp +HEADERS += engine/player/death_knight_runeforges.hpp HEADERS += engine/player/weapon.hpp HEADERS += engine/player/wrapper_callback.hpp HEADERS += engine/report/charts.hpp @@ -336,6 +337,7 @@ SOURCES += engine/player/unique_gear_helper.cpp SOURCES += engine/player/unique_gear_legion.cpp SOURCES += engine/player/unique_gear_shadowlands.cpp SOURCES += engine/player/unique_gear_thewarwithin.cpp +SOURCES += engine/player/death_knight_runeforges.cpp SOURCES += engine/report/charts.cpp SOURCES += engine/report/color.cpp SOURCES += engine/report/decorators.cpp diff --git a/source_files/VS_engine.props b/source_files/VS_engine.props index 54d3434e1ea..1f7c8f374fe 100644 --- a/source_files/VS_engine.props +++ b/source_files/VS_engine.props @@ -144,6 +144,7 @@ To change the list of source files run synchronize.py + @@ -339,6 +340,7 @@ To change the list of source files run synchronize.py + diff --git a/source_files/cmake_engine.txt b/source_files/cmake_engine.txt index 37cf1b34520..8167a5032bb 100644 --- a/source_files/cmake_engine.txt +++ b/source_files/cmake_engine.txt @@ -138,6 +138,7 @@ player/unique_gear_dragonflight.hpp player/unique_gear_helper.hpp player/unique_gear_shadowlands.hpp player/unique_gear_thewarwithin.hpp +player/death_knight_runeforges.hpp player/weapon.hpp player/wrapper_callback.hpp report/charts.hpp @@ -333,6 +334,7 @@ player/unique_gear_helper.cpp player/unique_gear_legion.cpp player/unique_gear_shadowlands.cpp player/unique_gear_thewarwithin.cpp +player/death_knight_runeforges.cpp report/charts.cpp report/color.cpp report/decorators.cpp diff --git a/source_files/engine_make b/source_files/engine_make index a3b572330f2..62797b281f5 100644 --- a/source_files/engine_make +++ b/source_files/engine_make @@ -139,6 +139,7 @@ SRC += \ player$(PATHSEP)unique_gear_legion.cpp \ player$(PATHSEP)unique_gear_shadowlands.cpp \ player$(PATHSEP)unique_gear_thewarwithin.cpp \ + player$(PATHSEP)death_knight_runeforges.cpp \ report$(PATHSEP)charts.cpp \ report$(PATHSEP)color.cpp \ report$(PATHSEP)decorators.cpp \