From 9b4315128bc9d5daceb1a78c84e0a7e8be205fda Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 2 Nov 2026 02:29:50 -0700 Subject: [PATCH 1/2] [spell_data] Improve rank spell interface, update modules to match. --- engine/class_modules/sc_priest.cpp | 327 ++++++++++++++-------------- engine/class_modules/sc_shaman.cpp | 119 +++++----- engine/class_modules/sc_warlock.cpp | 103 +++++---- engine/class_modules/sc_warrior.cpp | 122 ++++++----- engine/dbc/spell_data.cpp | 78 +++++-- engine/dbc/spell_data.hpp | 65 ++++-- engine/player/player.cpp | 27 --- engine/player/player.hpp | 4 - 8 files changed, 464 insertions(+), 381 deletions(-) diff --git a/engine/class_modules/sc_priest.cpp b/engine/class_modules/sc_priest.cpp index 22321d9e9c..7527f272c5 100644 --- a/engine/class_modules/sc_priest.cpp +++ b/engine/class_modules/sc_priest.cpp @@ -7,6 +7,7 @@ #include "simulationcraft.hpp" #include "action/parse_effects.hpp" #include "class_modules/apl/priest.hpp" +#include "dbc/spell_data.hpp" namespace priestspace { @@ -32,6 +33,26 @@ struct priest_td_t final : public actor_target_data_t priest_td_t( player_t* target, priest_t& p ); }; +// Priest racials are not in the racial spell table, so check the spell's race mask instead +// Rank 1 is checked first so other races and levels below rank 1 never reach find_rank +struct priest_racial_ranks_t final : spell_ranks_t +{ + template + priest_racial_ranks_t( Pack&&... pack ) + : spell_ranks_t{ std::forward( pack )... } + {} + + const spell_data_t* find_racial_rank_spell( int rank = RANK_MAX_USABLE, spell_ranks_t::rank_ptype_t field = &spell_container_t::spell ) + { + // make a nil `spell_container_t` and this can be find_rank(...) override + ranks.front().cache_data( player ); + if ( !( ranks.front().spell()->race_mask() & util::race_mask( player->race ) ) ) + return spell_data_t::not_found(); + + return std::invoke( field, spell_ranks_t::find_rank( rank, field ) ); + } +}; + // utility to create target_effect_t compatible functions from priest_td_t member references template static std::function d_fn( T d ) @@ -59,17 +80,6 @@ struct priest_t final : public parse_player_effects_t priest_td_t* get_target_data( player_t* target ) const override; - // Priest racials are not in the racial spell table, so check the spell's race mask instead - // Rank 1 is checked first so other races and levels below rank 1 never reach find_rank - const spell_data_t* find_racial_rank( util::span ranks, - rank_ptype_t field = &spell_container_t::spell ) const - { - if ( !( ranks.front().spell( this )->race_mask() & util::race_mask( race ) ) ) - return spell_data_t::not_found(); - - return std::invoke( field, find_rank( ranks ), this ); - } - std::string default_potion() const override { return priest_apl::potion( this ); } std::string default_flask() const override { return priest_apl::flask( this ); } std::string default_food() const override { return priest_apl::food( this ); } @@ -119,6 +129,23 @@ struct priest_t final : public parse_player_effects_t action_t* touch_of_weakness = nullptr; } active; + struct + { + priest_racial_ranks_t dark_sacrifice; + spell_ranks_t devouring_plague; + priest_racial_ranks_t hex_of_weakness; + spell_ranks_t holy_fire; + spell_ranks_t holy_nova; + spell_ranks_t mind_blast; + spell_ranks_t mind_flay; + spell_ranks_t penance; + spell_ranks_t shadow_word_death; + spell_ranks_t shadow_word_pain; + spell_ranks_t smite; + priest_racial_ranks_t starshards; + priest_racial_ranks_t touch_of_weakness; + } rank; + private: target_specific_t _target_data; }; @@ -196,29 +223,6 @@ struct priest_action_t : public parse_action_effects_t using priest_spell_t = priest_action_t; -// Hex of Weakness - -constexpr std::array hex_of_weakness_ranks { - spell_container_t().set_spell( 9035 ), - spell_container_t().set_spell( 19281 ), - spell_container_t().set_spell( 19282 ), - spell_container_t().set_spell( 19283 ), - spell_container_t().set_spell( 19284 ), - spell_container_t().set_spell( 19285 ), -}; - -// Touch of Weakness - -// spell: self buff, debuff: damage and attack power debuff on the attacker -constexpr std::array touch_of_weakness_ranks { - spell_container_t().set_spell( 2652 ).set_debuff( 2943 ), - spell_container_t().set_spell( 19261 ).set_debuff( 19249 ), - spell_container_t().set_spell( 19262 ).set_debuff( 19251 ), - spell_container_t().set_spell( 19264 ).set_debuff( 19252 ), - spell_container_t().set_spell( 19265 ).set_debuff( 19253 ), - spell_container_t().set_spell( 19266 ).set_debuff( 19254 ), -}; - void priest_t::init_base_stats() { parse_player_effects_t::init_base_stats(); @@ -234,6 +238,116 @@ void priest_t::init_spells() { parse_player_effects_t::init_spells(); + rank.dark_sacrifice = { this, + spell_container_t().set_spell( 1277324 ), + spell_container_t().set_spell( 1277325 ), + spell_container_t().set_spell( 1277326 ), + spell_container_t().set_spell( 1277327 ), + spell_container_t().set_spell( 1277328 ), + }; + rank.devouring_plague = { this, + spell_container_t().set_spell( 2944 ), + spell_container_t().set_spell( 19276 ), + spell_container_t().set_spell( 19277 ), + spell_container_t().set_spell( 19278 ), + spell_container_t().set_spell( 19279 ), + spell_container_t().set_spell( 19280 ), + }; + rank.hex_of_weakness = { this, + spell_container_t().set_spell( 9035 ), + spell_container_t().set_spell( 19281 ), + spell_container_t().set_spell( 19282 ), + spell_container_t().set_spell( 19283 ), + spell_container_t().set_spell( 19284 ), + spell_container_t().set_spell( 19285 ), + }; + rank.holy_fire = { this, + spell_container_t().set_spell( 14914 ), + spell_container_t().set_spell( 15262 ), + spell_container_t().set_spell( 15263 ), + spell_container_t().set_spell( 15264 ), + spell_container_t().set_spell( 15265 ), + spell_container_t().set_spell( 15266 ), + spell_container_t().set_spell( 15267 ), + spell_container_t().set_spell( 15261 ), // Rank 8 is 15261 for some reason + }; + rank.holy_nova = { this, + spell_container_t().set_spell( 15237 ), + spell_container_t().set_spell( 15430 ), + spell_container_t().set_spell( 15431 ), + spell_container_t().set_spell( 27799 ), + spell_container_t().set_spell( 27800 ), + spell_container_t().set_spell( 27801 ), + }; + rank.mind_blast = { this, + spell_container_t().set_spell( 8092 ), + spell_container_t().set_spell( 8102 ), + spell_container_t().set_spell( 8103 ), + spell_container_t().set_spell( 8104 ), + spell_container_t().set_spell( 8105 ), + spell_container_t().set_spell( 8106 ), + spell_container_t().set_spell( 10945 ), + spell_container_t().set_spell( 10946 ), + spell_container_t().set_spell( 10947 ), + }; + rank.mind_flay = { this, + spell_container_t().set_spell( 15407 ), + spell_container_t().set_spell( 17311 ), + spell_container_t().set_spell( 17312 ), + spell_container_t().set_spell( 17313 ), + spell_container_t().set_spell( 17314 ), + spell_container_t().set_spell( 18807 ), + }; + rank.penance = { this, + spell_container_t().set_spell( 402174 ).set_driver( 402261 ).set_attack( 402284 ), + spell_container_t().set_spell( 1240720 ).set_driver( 1240734 ).set_attack( 1240727 ), + spell_container_t().set_spell( 1240721 ).set_driver( 1240736 ).set_attack( 1240730 ), + spell_container_t().set_spell( 1316995 ).set_driver( 1316994 ).set_attack( 1316993 ), + }; + rank.shadow_word_death = { this, + spell_container_t().set_spell( 1309595 ), + spell_container_t().set_spell( 1309633 ), + spell_container_t().set_spell( 1309635 ), + spell_container_t().set_spell( 1309636 ), + }; + rank.shadow_word_pain = { this, + spell_container_t().set_spell( 589 ), + spell_container_t().set_spell( 594 ), + spell_container_t().set_spell( 970 ), + spell_container_t().set_spell( 992 ), + spell_container_t().set_spell( 2767 ), + spell_container_t().set_spell( 10892 ), + spell_container_t().set_spell( 10893 ), + spell_container_t().set_spell( 10894 ), + }; + rank.smite = { this, + spell_container_t().set_spell( 585 ), + spell_container_t().set_spell( 591 ), + spell_container_t().set_spell( 598 ), + spell_container_t().set_spell( 984 ), + spell_container_t().set_spell( 1004 ), + spell_container_t().set_spell( 6060 ), + spell_container_t().set_spell( 10933 ), + spell_container_t().set_spell( 10934 ), + }; + rank.starshards = { this, + spell_container_t().set_spell( 10797 ), + spell_container_t().set_spell( 19296 ), + spell_container_t().set_spell( 19299 ), + spell_container_t().set_spell( 19302 ), + spell_container_t().set_spell( 19303 ), + spell_container_t().set_spell( 19304 ), + spell_container_t().set_spell( 19305 ), + }; + rank.touch_of_weakness = { this, + spell_container_t().set_spell( 2652 ).set_debuff( 2943 ), + spell_container_t().set_spell( 19261 ).set_debuff( 19249 ), + spell_container_t().set_spell( 19262 ).set_debuff( 19251 ), + spell_container_t().set_spell( 19264 ).set_debuff( 19252 ), + spell_container_t().set_spell( 19265 ).set_debuff( 19253 ), + spell_container_t().set_spell( 19266 ).set_debuff( 19254 ), + }; + parse_all_class_passives(); parse_all_passive_talents(); @@ -257,11 +371,11 @@ void priest_t::init_spells() deregister_passive_spell( talent.wand_specialization ); spell.dark_inspiration = find_spell( 1326168 ); - spell.hex_of_weakness = find_racial_rank( hex_of_weakness_ranks ); + spell.hex_of_weakness = rank.hex_of_weakness.find_racial_rank_spell(); spell.holy_purpose = talent.searing_light->effectN( 2 ).trigger(); spell.shadow_weaving = talent.shadow_weaving->effectN( 1 ).trigger(); - spell.touch_of_weakness = find_racial_rank( touch_of_weakness_ranks ); - spell.touch_of_weakness_debuff = find_racial_rank( touch_of_weakness_ranks, &spell_container_t::debuff ); + spell.touch_of_weakness = rank.touch_of_weakness.find_racial_rank_spell(); + spell.touch_of_weakness_debuff = rank.touch_of_weakness.find_racial_rank_spell( RANK_MAX_USABLE, &spell_container_t::debuff ); } void priest_t::create_buffs() @@ -423,21 +537,10 @@ void priest_t::parse_action_effects( action_t* action ) } // Smite - -constexpr std::array smite_ranks { - spell_container_t().set_spell( 585 ), - spell_container_t().set_spell( 591 ), - spell_container_t().set_spell( 598 ), - spell_container_t().set_spell( 984 ), - spell_container_t().set_spell( 1004 ), - spell_container_t().set_spell( 6060 ), - spell_container_t().set_spell( 10933 ), - spell_container_t().set_spell( 10934 ), -}; struct smite_t : public priest_spell_t { smite_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "smite", player, player->find_rank( smite_ranks )->spell( player ) ) + priest_spell_t( "smite", player, player->rank.smite.find_rank()->spell() ) { parse_options( options_str ); parse_power_in_light(); @@ -445,43 +548,20 @@ struct smite_t : public priest_spell_t }; // Shadow Word: Pain - -constexpr std::array shadow_word_pain_ranks { - spell_container_t().set_spell( 589 ), - spell_container_t().set_spell( 594 ), - spell_container_t().set_spell( 970 ), - spell_container_t().set_spell( 992 ), - spell_container_t().set_spell( 2767 ), - spell_container_t().set_spell( 10892 ), - spell_container_t().set_spell( 10893 ), - spell_container_t().set_spell( 10894 ), -}; struct shadow_word_pain_t : public priest_spell_t { shadow_word_pain_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "shadow_word_pain", player, player->find_rank( shadow_word_pain_ranks )->spell( player ) ) + priest_spell_t( "shadow_word_pain", player, player->rank.shadow_word_pain.find_rank()->spell() ) { parse_options( options_str ); } }; // Mind Blast - -constexpr std::array mind_blast_ranks { - spell_container_t().set_spell( 8092 ), - spell_container_t().set_spell( 8102 ), - spell_container_t().set_spell( 8103 ), - spell_container_t().set_spell( 8104 ), - spell_container_t().set_spell( 8105 ), - spell_container_t().set_spell( 8106 ), - spell_container_t().set_spell( 10945 ), - spell_container_t().set_spell( 10946 ), - spell_container_t().set_spell( 10947 ), -}; struct mind_blast_t : public priest_spell_t { mind_blast_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "mind_blast", player, player->find_rank( mind_blast_ranks )->spell( player ) ) + priest_spell_t( "mind_blast", player, player->rank.mind_blast.find_rank()->spell() ) { parse_options( options_str ); } @@ -496,40 +576,22 @@ struct mind_blast_t : public priest_spell_t }; // Devouring Plague - // TODO: Heal the caster for the damage dealt -constexpr std::array devouring_plague_ranks { - spell_container_t().set_spell( 2944 ), - spell_container_t().set_spell( 19276 ), - spell_container_t().set_spell( 19277 ), - spell_container_t().set_spell( 19278 ), - spell_container_t().set_spell( 19279 ), - spell_container_t().set_spell( 19280 ), -}; struct devouring_plague_t : public priest_spell_t { devouring_plague_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "devouring_plague", player, player->find_rank( devouring_plague_ranks )->spell( player ) ) + priest_spell_t( "devouring_plague", player, player->rank.devouring_plague.find_rank()->spell() ) { parse_options( options_str ); } }; // Mind Flay - -constexpr std::array mind_flay_ranks { - spell_container_t().set_spell( 15407 ), - spell_container_t().set_spell( 17311 ), - spell_container_t().set_spell( 17312 ), - spell_container_t().set_spell( 17313 ), - spell_container_t().set_spell( 17314 ), - spell_container_t().set_spell( 18807 ), -}; struct mind_flay_t : public priest_spell_t { mind_flay_t( priest_t* player, util::string_view options_str ) : priest_spell_t( "mind_flay", player, player->talent.mind_flay.ok() - ? player->find_rank( mind_flay_ranks )->spell( player ) + ? player->rank.mind_flay.find_rank()->spell() : spell_data_t::not_found() ) { parse_options( options_str ); @@ -563,18 +625,11 @@ struct mind_flay_t : public priest_spell_t }; // Shadow Word: Death - // TODO: Backlash damage, effect 2 -constexpr std::array shadow_word_death_ranks { - spell_container_t().set_spell( 1309595 ), - spell_container_t().set_spell( 1309633 ), - spell_container_t().set_spell( 1309635 ), - spell_container_t().set_spell( 1309636 ), -}; struct shadow_word_death_t : public priest_spell_t { shadow_word_death_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "shadow_word_death", player, player->find_rank( shadow_word_death_ranks )->spell( player ) ) + priest_spell_t( "shadow_word_death", player, player->rank.shadow_word_death.find_rank()->spell() ) { parse_options( options_str ); @@ -592,40 +647,21 @@ struct shadow_word_death_t : public priest_spell_t }; // Holy Fire - -constexpr std::array holy_fire_ranks { - spell_container_t().set_spell( 14914 ), - spell_container_t().set_spell( 15262 ), - spell_container_t().set_spell( 15263 ), - spell_container_t().set_spell( 15264 ), - spell_container_t().set_spell( 15265 ), - spell_container_t().set_spell( 15266 ), - spell_container_t().set_spell( 15267 ), - spell_container_t().set_spell( 15261 ), // Rank 8 is 15261 for some reason -}; struct holy_fire_t : public priest_spell_t { holy_fire_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "holy_fire", player, player->find_rank( holy_fire_ranks )->spell( player ) ) + priest_spell_t( "holy_fire", player, player->rank.holy_fire.find_rank()->spell() ) { parse_options( options_str ); } }; // Penance TODO: Check Penance vs Inner Focus - Right now only first bolt gets the crit bonus - // driver: channel, attack: damage bolt -constexpr std::array penance_ranks { - spell_container_t().set_spell( 402174 ).set_driver( 402261 ).set_attack( 402284 ), - spell_container_t().set_spell( 1240720 ).set_driver( 1240734 ).set_attack( 1240727 ), - spell_container_t().set_spell( 1240721 ).set_driver( 1240736 ).set_attack( 1240730 ), - spell_container_t().set_spell( 1316995 ).set_driver( 1316994 ).set_attack( 1316993 ), -}; - struct penance_damage_t : public priest_spell_t { penance_damage_t( priest_t* player ) : - priest_spell_t( "penance_damage", player, player->find_rank( penance_ranks )->attack( player ) ) + priest_spell_t( "penance_damage", player, player->rank.penance.find_rank()->attack() ) { background = true; parse_power_in_light(); @@ -636,34 +672,25 @@ struct penance_t : public priest_spell_t { penance_t( priest_t* player, util::string_view options_str ) : priest_spell_t( "penance", player, player->talent.penance.ok() - ? player->find_rank( penance_ranks )->spell( player ) + ? player->rank.penance.find_rank()->spell() : spell_data_t::not_found() ) { parse_options( options_str ); channeled = true; may_miss = may_crit = false; - parse_effect_data( player->find_rank( penance_ranks )->driver( player )->effectN( 2 ) ); + parse_effect_data( player->rank.penance.find_rank()->driver()->effectN( 2 ) ); tick_action = new penance_damage_t( player ); } }; // Holy Nova - // TODO: Party heal -constexpr std::array holy_nova_ranks { - spell_container_t().set_spell( 15237 ), - spell_container_t().set_spell( 15430 ), - spell_container_t().set_spell( 15431 ), - spell_container_t().set_spell( 27799 ), - spell_container_t().set_spell( 27800 ), - spell_container_t().set_spell( 27801 ), -}; struct holy_nova_t : public priest_spell_t { holy_nova_t( priest_t* player, util::string_view options_str ) : priest_spell_t( "holy_nova", player, player->talent.holy_nova.ok() - ? player->find_rank( holy_nova_ranks )->spell( player ) + ? player->rank.holy_nova.find_rank()->spell() : spell_data_t::not_found() ) { parse_options( options_str ); @@ -672,20 +699,10 @@ struct holy_nova_t : public priest_spell_t }; // Starshards - -constexpr std::array starshards_ranks { - spell_container_t().set_spell( 10797 ), - spell_container_t().set_spell( 19296 ), - spell_container_t().set_spell( 19299 ), - spell_container_t().set_spell( 19302 ), - spell_container_t().set_spell( 19303 ), - spell_container_t().set_spell( 19304 ), - spell_container_t().set_spell( 19305 ), -}; struct starshards_t : public priest_spell_t { - starshards_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "starshards", player, player->find_racial_rank( starshards_ranks ) ) + starshards_t( priest_t* player, util::string_view options_str ): + priest_spell_t( "starshards", player, player->rank.starshards.find_racial_rank_spell() ) { parse_options( options_str ); channeled = true; @@ -693,22 +710,14 @@ struct starshards_t : public priest_spell_t }; // Dark Sacrifice - // TODO: Health cost, effect 1 -constexpr std::array dark_sacrifice_ranks { - spell_container_t().set_spell( 1277324 ), - spell_container_t().set_spell( 1277325 ), - spell_container_t().set_spell( 1277326 ), - spell_container_t().set_spell( 1277327 ), - spell_container_t().set_spell( 1277328 ), -}; struct dark_sacrifice_t : public priest_spell_t { // TODO: Check if spirit is snapshot on cast or updates dynamically double spirit_per_tick = 0.0; - dark_sacrifice_t( priest_t* player, util::string_view options_str ) : - priest_spell_t( "dark_sacrifice", player, player->find_racial_rank( dark_sacrifice_ranks ) ) + dark_sacrifice_t( priest_t* player, util::string_view options_str ): + priest_spell_t( "dark_sacrifice", player, player->rank.dark_sacrifice.find_racial_rank_spell() ) { parse_options( options_str ); harmful = false; @@ -770,7 +779,7 @@ struct hex_of_weakness_t : public priest_spell_t }; // Touch of Weakness - +// spell: self buff, debuff: damage and attack power debuff on the attacker struct touch_of_weakness_damage_t : public priest_spell_t { touch_of_weakness_damage_t( priest_t* player ) : @@ -941,4 +950,4 @@ const module_t* module_t::priest() { static priestspace::priest_module_t m; return &m; -} \ No newline at end of file +} diff --git a/engine/class_modules/sc_shaman.cpp b/engine/class_modules/sc_shaman.cpp index 3478169baa..c3e4ccf122 100644 --- a/engine/class_modules/sc_shaman.cpp +++ b/engine/class_modules/sc_shaman.cpp @@ -51,6 +51,14 @@ struct shaman_t final : public player_t action_t* flametongue_attack = nullptr; + struct + { + spell_ranks_t earth_shock; + spell_ranks_t flame_shock; + spell_ranks_t flametongue_weapon; + spell_ranks_t rockbiter_weapon; + spell_ranks_t strength_of_earth_totem; + } rank; }; // SHAMAN MODULE INTERFACE ================================================== @@ -130,6 +138,52 @@ void shaman_t::init_spells() { player_t::init_spells(); + rank.earth_shock = { this, + spell_container_t().set_spell( 8042 ), + spell_container_t().set_spell( 8044 ), + spell_container_t().set_spell( 8045 ), + spell_container_t().set_spell( 8046 ), + spell_container_t().set_spell( 10412 ), + spell_container_t().set_spell( 10413 ), + spell_container_t().set_spell( 10414 ), + }; + + rank.flame_shock = { this, + spell_container_t().set_spell( 8050 ), + spell_container_t().set_spell( 8052 ), + spell_container_t().set_spell( 8053 ), + spell_container_t().set_spell( 10447 ), + spell_container_t().set_spell( 10448 ), + spell_container_t().set_spell( 29228 ), + }; + + rank.flametongue_weapon = { this, + spell_container_t().set_spell( 8024 ).set_driver( 8026 ).set_attack( 29469 ), + spell_container_t().set_spell( 8027 ).set_driver( 8028 ).set_attack( 29470 ), + spell_container_t().set_spell( 8030 ).set_driver( 8029 ).set_attack( 10444 ), + spell_container_t().set_spell( 16339 ).set_driver( 10445 ).set_attack( 10444 ), + spell_container_t().set_spell( 16341 ).set_driver( 16343 ).set_attack( 10444 ), + spell_container_t().set_spell( 16342 ).set_driver( 16344 ).set_attack( 10444 ), + }; + + rank.rockbiter_weapon = { this, + spell_container_t().set_spell( 8017 ).set_passive( 10400 ), + spell_container_t().set_spell( 8018 ).set_passive( 15567 ), + spell_container_t().set_spell( 8019 ).set_passive( 15568 ), + spell_container_t().set_spell( 10399 ).set_passive( 15569 ), + spell_container_t().set_spell( 16314 ).set_passive( 16311 ), + spell_container_t().set_spell( 16315 ).set_passive( 16312 ), + spell_container_t().set_spell( 16316 ).set_passive( 16313 ), + }; + + rank.strength_of_earth_totem = { this, + spell_container_t().set_spell( 8075 ).set_aura( 8076 ), + spell_container_t().set_spell( 8160 ).set_aura( 8162 ), + spell_container_t().set_spell( 8161 ).set_aura( 8163 ), + spell_container_t().set_spell( 10442 ).set_aura( 10441 ), + spell_container_t().set_spell( 25361 ).set_aura( 25362 ), + }; + parse_all_class_passives(); parse_all_passive_talents(); @@ -161,19 +215,10 @@ void shaman_t::invalidate_cache( cache_e c ) // Earth Shock -constexpr std::array earth_shock_ranks { - spell_container_t().set_spell( 8042 ), - spell_container_t().set_spell( 8044 ), - spell_container_t().set_spell( 8045 ), - spell_container_t().set_spell( 8046 ), - spell_container_t().set_spell( 10412 ), - spell_container_t().set_spell( 10413 ), - spell_container_t().set_spell( 10414 ), -}; struct earth_shock_t : public shaman_spell_t { earth_shock_t( shaman_t* player, util::string_view options_str ) : - shaman_spell_t( "earth_shock", player, player->find_rank( earth_shock_ranks )->spell( player ) ) + shaman_spell_t( "earth_shock", player, player->rank.earth_shock.find_rank()->spell() ) { parse_options( options_str ); @@ -184,18 +229,10 @@ struct earth_shock_t : public shaman_spell_t // Flame Shock -constexpr std::array flame_shock_ranks { - spell_container_t().set_spell( 8050 ), - spell_container_t().set_spell( 8052 ), - spell_container_t().set_spell( 8053 ), - spell_container_t().set_spell( 10447 ), - spell_container_t().set_spell( 10448 ), - spell_container_t().set_spell( 29228 ), -}; struct flame_shock_t : public shaman_spell_t { flame_shock_t( shaman_t* player, util::string_view options_str ) : - shaman_spell_t( "flame_shock", player, player->find_rank( flame_shock_ranks )->spell( player) ) + shaman_spell_t( "flame_shock", player, player->rank.flame_shock.find_rank()->spell() ) { parse_options( options_str ); @@ -208,23 +245,14 @@ struct flame_shock_t : public shaman_spell_t // Shaman Weapon Imbues // ========================================================================== -constexpr std::array flametongue_weapon_ranks { - spell_container_t().set_spell( 8024 ).set_driver( 8026 ).set_attack( 29469 ), - spell_container_t().set_spell( 8027 ).set_driver( 8028 ).set_attack( 29470 ), - spell_container_t().set_spell( 8030 ).set_driver( 8029 ).set_attack( 10444 ), - spell_container_t().set_spell( 16339 ).set_driver( 10445 ).set_attack( 10444 ), - spell_container_t().set_spell( 16341 ).set_driver( 16343 ).set_attack( 10444 ), - spell_container_t().set_spell( 16342 ).set_driver( 16344 ).set_attack( 10444 ), -}; - struct flametongue_attack_t : public shaman_spell_t { flametongue_attack_t( shaman_t* player ) : - shaman_spell_t( "flametongue_attack", player, player->find_rank( flametongue_weapon_ranks )->attack( player ) ) + shaman_spell_t( "flametongue_attack", player, player->rank.flametongue_weapon.find_rank()->attack() ) { background = true; - auto driver = player->find_rank( flametongue_weapon_ranks )->driver( player ); + auto driver = player->rank.flametongue_weapon.find_rank()->driver(); double proc_value = driver->effectN( 1 ).average( player, player->level() ); double weapon_speed = player->equipped_weapons[ SLOT_MAIN_HAND ].swing_time.total_seconds(); @@ -236,7 +264,7 @@ struct flametongue_attack_t : public shaman_spell_t struct flametongue_weapon_t : public shaman_spell_t { flametongue_weapon_t( shaman_t* player, util::string_view options_str ) : - shaman_spell_t( "flametongue_weapon", player, player->find_rank( flametongue_weapon_ranks )->spell( player ) ) + shaman_spell_t( "flametongue_weapon", player, player->rank.flametongue_weapon.find_rank()->spell() ) { parse_options( options_str ); harmful = false; @@ -262,19 +290,10 @@ struct flametongue_weapon_t : public shaman_spell_t // rockbiter weapon -constexpr std::array rockbiter_weapon_ranks { - spell_container_t().set_spell( 8017 ).set_passive( 10400 ), - spell_container_t().set_spell( 8018 ).set_passive( 15567 ), - spell_container_t().set_spell( 8019 ).set_passive( 15568 ), - spell_container_t().set_spell( 10399 ).set_passive( 15569 ), - spell_container_t().set_spell( 16314 ).set_passive( 16311 ), - spell_container_t().set_spell( 16315 ).set_passive( 16312 ), - spell_container_t().set_spell( 16316 ).set_passive( 16313 ), -}; struct rockbiter_weapon_t : public shaman_spell_t { rockbiter_weapon_t( shaman_t* player, util::string_view options_str ) : - shaman_spell_t( "rockbiter_weapon", player, player->find_rank( rockbiter_weapon_ranks )->spell( player ) ) + shaman_spell_t( "rockbiter_weapon", player, player->rank.rockbiter_weapon.find_rank()->spell() ) { parse_options( options_str ); harmful = false; @@ -306,17 +325,10 @@ struct rockbiter_weapon_t : public shaman_spell_t // TODO: Make totems a unit for use with range? // TODO: Party buff -constexpr std::array strength_of_earth_totem_ranks { - spell_container_t().set_spell( 8075 ).set_aura( 8076 ), - spell_container_t().set_spell( 8160 ).set_aura( 8162 ), - spell_container_t().set_spell( 8161 ).set_aura( 8163 ), - spell_container_t().set_spell( 10442 ).set_aura( 10441 ), - spell_container_t().set_spell( 25361 ).set_aura( 25362 ), -}; struct strength_of_earth_totem_t : public shaman_spell_t { strength_of_earth_totem_t( shaman_t* player, util::string_view options_str ) : - shaman_spell_t( "strength_of_earth_totem", player, player->find_rank( strength_of_earth_totem_ranks )->spell( player ) ) + shaman_spell_t( "strength_of_earth_totem", player, player->rank.strength_of_earth_totem.find_rank()->spell() ) { parse_options( options_str ); harmful = false; @@ -423,11 +435,12 @@ void shaman_t::create_buffs() { player_t::create_buffs(); - buff.flametongue_weapon = make_buff( this, "flametongue_weapon", find_rank( flametongue_weapon_ranks )->spell( this ) ); - buff.rockbiter_weapon = make_buff( this, "rockbiter_weapon", find_rank( rockbiter_weapon_ranks )->passive( this ) ); + buff.flametongue_weapon = make_buff( this, "flametongue_weapon", rank.flametongue_weapon.find_rank()->spell() ); + buff.rockbiter_weapon = make_buff( this, "rockbiter_weapon", rank.rockbiter_weapon.find_rank()->passive() ); - buff.strength_of_earth = make_buff( this, "strength_of_earth", find_rank( strength_of_earth_totem_ranks )->aura( this ) ) - ->set_duration( find_rank( strength_of_earth_totem_ranks )->spell( this )->duration() ); + auto soet_rank = rank.strength_of_earth_totem.find_rank(); + buff.strength_of_earth = make_buff( this, "strength_of_earth", soet_rank->aura() ) + ->set_duration( soet_rank->spell()->duration() ); } void shaman_t::create_actions() diff --git a/engine/class_modules/sc_warlock.cpp b/engine/class_modules/sc_warlock.cpp index 4931480770..46ae1ca561 100644 --- a/engine/class_modules/sc_warlock.cpp +++ b/engine/class_modules/sc_warlock.cpp @@ -27,6 +27,14 @@ struct warlock_t final : public player_t { pet_t* imp = nullptr; } pets; + + struct + { + spell_ranks_t corruption; + spell_ranks_t firebolt; + spell_ranks_t immolate; + spell_ranks_t shadow_bolt; + } rank; }; struct warlock_module_t : public module_t @@ -70,64 +78,30 @@ struct warlock_action_t : public parse_action_effects_t using warlock_spell_t = warlock_action_t; // Shadow Bolt - -constexpr std::array shadow_bolt_ranks { - spell_container_t().set_spell( 686 ), - spell_container_t().set_spell( 695 ), - spell_container_t().set_spell( 705 ), - spell_container_t().set_spell( 1088 ), - spell_container_t().set_spell( 1106 ), - spell_container_t().set_spell( 7641 ), - spell_container_t().set_spell( 11659 ), - spell_container_t().set_spell( 11660 ), - spell_container_t().set_spell( 11661 ), - spell_container_t().set_spell( 25307 ), -}; struct shadow_bolt_t : public warlock_spell_t { shadow_bolt_t( warlock_t* player, util::string_view options_str ) : - warlock_spell_t( "shadow_bolt", player, player->find_rank( shadow_bolt_ranks )->spell( player ) ) + warlock_spell_t( "shadow_bolt", player, player->rank.shadow_bolt.find_rank()->spell() ) { parse_options( options_str ); } }; // Immolate - -constexpr std::array immolate_ranks { - spell_container_t().set_spell( 348 ), - spell_container_t().set_spell( 707 ), - spell_container_t().set_spell( 1094 ), - spell_container_t().set_spell( 2941 ), - spell_container_t().set_spell( 11665 ), - spell_container_t().set_spell( 11667 ), - spell_container_t().set_spell( 11668 ), - spell_container_t().set_spell( 25309 ), -}; struct immolate_t : public warlock_spell_t { immolate_t( warlock_t* player, util::string_view options_str ) : - warlock_spell_t( "immolate", player, player->find_rank( immolate_ranks )->spell( player ) ) + warlock_spell_t( "immolate", player, player->rank.immolate.find_rank()->spell() ) { parse_options( options_str ); } }; // Corruption - -constexpr std::array corruption_ranks { - spell_container_t().set_spell( 172 ), - spell_container_t().set_spell( 6222 ), - spell_container_t().set_spell( 6223 ), - spell_container_t().set_spell( 7648 ), - spell_container_t().set_spell( 11671 ), - spell_container_t().set_spell( 11672 ), - spell_container_t().set_spell( 25311 ), -}; struct corruption_t : public warlock_spell_t { corruption_t( warlock_t* player, util::string_view options_str ) : - warlock_spell_t( "corruption", player, player->find_rank( corruption_ranks )->spell( player ) ) + warlock_spell_t( "corruption", player, player->rank.corruption.find_rank()->spell() ) { parse_options( options_str ); } @@ -251,21 +225,12 @@ namespace pets { action_t* create_action( std::string_view, std::string_view ) override; }; - constexpr std::array firebolt_ranks { - spell_container_t().set_spell( 3110 ), // 1 - spell_container_t().set_spell( 7799 ), // 8 - spell_container_t().set_spell( 7800 ), // 18 - spell_container_t().set_spell( 7801 ), // 28 - spell_container_t().set_spell( 7802 ), // 38 - spell_container_t().set_spell( 11762 ), // 48 - spell_container_t().set_spell( 11763 ), // 58 - }; // TODO: There's some funky delay on beta. Some casts are back-to-back, some have a 0.5-1.0s gap even when // the imp has mana struct firebolt_t : warlock_pet_spell_t { firebolt_t( std::string_view n, imp_pet_t* p, std::string_view options_str ) : - warlock_pet_spell_t( n, p, p->find_rank( firebolt_ranks )->spell( p ) ) + warlock_pet_spell_t( n, p, p->o()->rank.firebolt.find_rank()->spell() ) { parse_options( options_str ); @@ -318,6 +283,50 @@ void warlock_t::init_spells() { player_t::init_spells(); + rank.corruption = { this, + spell_container_t().set_spell( 172 ), + spell_container_t().set_spell( 6222 ), + spell_container_t().set_spell( 6223 ), + spell_container_t().set_spell( 7648 ), + spell_container_t().set_spell( 11671 ), + spell_container_t().set_spell( 11672 ), + spell_container_t().set_spell( 25311 ), + }; + + rank.firebolt = { this, + spell_container_t().set_spell( 3110 ), // 1 + spell_container_t().set_spell( 7799 ), // 8 + spell_container_t().set_spell( 7800 ), // 18 + spell_container_t().set_spell( 7801 ), // 28 + spell_container_t().set_spell( 7802 ), // 38 + spell_container_t().set_spell( 11762 ), // 48 + spell_container_t().set_spell( 11763 ), // 58 + }; + + rank.immolate = { this, + spell_container_t().set_spell( 348 ), + spell_container_t().set_spell( 707 ), + spell_container_t().set_spell( 1094 ), + spell_container_t().set_spell( 2941 ), + spell_container_t().set_spell( 11665 ), + spell_container_t().set_spell( 11667 ), + spell_container_t().set_spell( 11668 ), + spell_container_t().set_spell( 25309 ), + }; + + rank.shadow_bolt = { this, + spell_container_t().set_spell( 686 ), + spell_container_t().set_spell( 695 ), + spell_container_t().set_spell( 705 ), + spell_container_t().set_spell( 1088 ), + spell_container_t().set_spell( 1106 ), + spell_container_t().set_spell( 7641 ), + spell_container_t().set_spell( 11659 ), + spell_container_t().set_spell( 11660 ), + spell_container_t().set_spell( 11661 ), + spell_container_t().set_spell( 25307 ), + }; + parse_all_class_passives(); parse_all_passive_talents(); } diff --git a/engine/class_modules/sc_warrior.cpp b/engine/class_modules/sc_warrior.cpp index cde55706ef..377be9b988 100644 --- a/engine/class_modules/sc_warrior.cpp +++ b/engine/class_modules/sc_warrior.cpp @@ -56,6 +56,16 @@ struct warrior_t : public player_t gain_t* melee = nullptr; } gain; + struct + { + spell_ranks_t battle_shout; + spell_ranks_t bloodrage; + spell_ranks_t charge; + spell_ranks_t heroic_strike; + spell_ranks_t rend; + spell_ranks_t thunder_clap; + } rank; + action_t* heroic_strike = nullptr; }; @@ -108,20 +118,11 @@ struct warrior_action_t : public parse_action_effects_t using warrior_attack_t = warrior_action_t; using warrior_spell_t = warrior_action_t; -constexpr std::array battle_shout_ranks { - spell_container_t().set_spell( 5242 ), - spell_container_t().set_spell( 6192 ), - spell_container_t().set_spell( 6673 ), - spell_container_t().set_spell( 11549 ), - spell_container_t().set_spell( 11550 ), - spell_container_t().set_spell( 11551 ), - spell_container_t().set_spell( 25289 ), -}; // TODO: Party buff version struct battle_shout_t : public warrior_spell_t { battle_shout_t( warrior_t* player, util::string_view options_str ) : - warrior_spell_t( "battle_shout", player, player->find_rank( battle_shout_ranks )->spell( player ) ) + warrior_spell_t( "battle_shout", player, player->rank.battle_shout.find_rank()->spell() ) { parse_options( options_str ); @@ -141,13 +142,10 @@ struct battle_shout_t : public warrior_spell_t } }; -constexpr std::array bloodrage_ranks { - spell_container_t().set_spell( 2687 ).set_buff( 29131 ), -}; struct bloodrage_t : public warrior_spell_t { bloodrage_t( warrior_t* player, util::string_view options_str ) : - warrior_spell_t( "bloodrage", player, player->find_rank( bloodrage_ranks )->spell( player ) ) + warrior_spell_t( "bloodrage", player, player->rank.bloodrage.find_rank()->spell() ) { parse_options( options_str ); @@ -264,19 +262,10 @@ double warrior_t::rage_per_swing( const weapon_t& w ) const // ///////////////////////////////////////////////////////////////////////////////////// -constexpr std::array rend_ranks { - spell_container_t().set_spell( 772 ), - spell_container_t().set_spell( 6546 ), - spell_container_t().set_spell( 6547 ), - spell_container_t().set_spell( 6548 ), - spell_container_t().set_spell( 11572 ), - spell_container_t().set_spell( 11573 ), - spell_container_t().set_spell( 11574 ), -}; struct rend_t : public warrior_attack_t { rend_t( warrior_t* player, util::string_view options_str ) : - warrior_attack_t( "rend", player, player->find_rank( rend_ranks )->spell( player ) ) + warrior_attack_t( "rend", player, player->rank.rend.find_rank()->spell() ) { parse_options( options_str ); @@ -327,21 +316,10 @@ struct heroic_strike_queue_t : public action_t } }; -constexpr std::array heroic_strike_ranks { - spell_container_t().set_spell( 78 ), - spell_container_t().set_spell( 284 ), - spell_container_t().set_spell( 285 ), - spell_container_t().set_spell( 1608 ), - spell_container_t().set_spell( 11564 ), - spell_container_t().set_spell( 11565 ), - spell_container_t().set_spell( 11566 ), - spell_container_t().set_spell( 11567 ), - spell_container_t().set_spell( 25286 ), -}; struct heroic_strike_t : public warrior_attack_t { heroic_strike_t( warrior_t* player, util::string_view options_str ) : - warrior_attack_t( "heroic_strike", player, player->find_rank( heroic_strike_ranks )->spell( player ) ) + warrior_attack_t( "heroic_strike", player, player->rank.heroic_strike.find_rank()->spell() ) { parse_options( options_str ); @@ -356,15 +334,10 @@ struct heroic_strike_t : public warrior_attack_t } }; -constexpr std::array charge_ranks { - spell_container_t().set_spell( 100 ), - spell_container_t().set_spell( 6178 ), - spell_container_t().set_spell( 11578 ), -}; struct charge_t : public warrior_attack_t { charge_t( warrior_t* player, util::string_view options_str ) : - warrior_attack_t( "charge", player, player->find_rank( charge_ranks )->spell( player ) ) + warrior_attack_t( "charge", player, player->rank.charge.find_rank()->spell() ) { parse_options( options_str ); @@ -378,18 +351,10 @@ struct charge_t : public warrior_attack_t } }; -constexpr std::array thunder_clap_ranks { - spell_container_t().set_spell( 6343 ), - spell_container_t().set_spell( 8198 ), - spell_container_t().set_spell( 8204 ), - spell_container_t().set_spell( 8205 ), - spell_container_t().set_spell( 11580 ), - spell_container_t().set_spell( 11581 ), -}; struct thunder_clap_t : public warrior_attack_t { thunder_clap_t( warrior_t* player, util::string_view options_str ) : - warrior_attack_t( "thunder_clap", player, player->find_rank( thunder_clap_ranks )->spell( player ) ) + warrior_attack_t( "thunder_clap", player, player->rank.thunder_clap.find_rank()->spell() ) { parse_options( options_str ); @@ -452,10 +417,10 @@ void warrior_t::create_buffs() { buff.heroic_strike_queued = make_buff( this, "heroic_strike_queued" ); - buff.battle_shout = make_buff( this, "battle_shout", find_rank( battle_shout_ranks )->spell( this ) ); + buff.battle_shout = make_buff( this, "battle_shout", rank.battle_shout.find_rank()->spell() ); buff.bloodrage = make_buff( this, "bloodrage", - find_rank( bloodrage_ranks )->buff( this ) ) + rank.bloodrage.find_rank()->buff() ) ->set_tick_callback( [ this ]( buff_t*, int, timespan_t ) { resource_gain( RESOURCE_RAGE, 1.0, get_gain( "bloodrage" ) ); } ); @@ -466,6 +431,57 @@ void warrior_t::init_spells() { player_t::init_spells(); + rank.battle_shout = { this, + spell_container_t().set_spell( 5242 ), + spell_container_t().set_spell( 6192 ), + spell_container_t().set_spell( 6673 ), + spell_container_t().set_spell( 11549 ), + spell_container_t().set_spell( 11550 ), + spell_container_t().set_spell( 11551 ), + spell_container_t().set_spell( 25289 ), + }; + + rank.bloodrage = { this, + spell_container_t().set_spell( 2687 ).set_buff( 29131 ), + }; + + rank.rend = { this, + spell_container_t().set_spell( 772 ), + spell_container_t().set_spell( 6546 ), + spell_container_t().set_spell( 6547 ), + spell_container_t().set_spell( 6548 ), + spell_container_t().set_spell( 11572 ), + spell_container_t().set_spell( 11573 ), + spell_container_t().set_spell( 11574 ), + }; + + rank.heroic_strike = { this, + spell_container_t().set_spell( 78 ), + spell_container_t().set_spell( 284 ), + spell_container_t().set_spell( 285 ), + spell_container_t().set_spell( 1608 ), + spell_container_t().set_spell( 11564 ), + spell_container_t().set_spell( 11565 ), + spell_container_t().set_spell( 11566 ), + spell_container_t().set_spell( 11567 ), + spell_container_t().set_spell( 25286 ), + }; + + rank.charge = { this, + spell_container_t().set_spell( 100 ), + spell_container_t().set_spell( 6178 ), + spell_container_t().set_spell( 11578 ), + }; + + rank.thunder_clap = { this, + spell_container_t().set_spell( 6343 ), + spell_container_t().set_spell( 8198 ), + spell_container_t().set_spell( 8204 ), + spell_container_t().set_spell( 8205 ), + spell_container_t().set_spell( 11580 ), + spell_container_t().set_spell( 11581 ), + }; + parse_all_class_passives(); parse_all_passive_talents(); } diff --git a/engine/dbc/spell_data.cpp b/engine/dbc/spell_data.cpp index 973f112c09..d4e891f4d1 100644 --- a/engine/dbc/spell_data.cpp +++ b/engine/dbc/spell_data.cpp @@ -5,6 +5,7 @@ #include "generated/power_type_data.inc" #include +#include #if SC_USE_PTR #include "generated/sc_spell_data_ptr.inc" #include "generated/power_type_data_ptr.inc" @@ -858,26 +859,67 @@ util::span spellpower_data_t::hotfixes( con return find_hotfixes( data, pd.id() ); } -const spell_data_t* spell_container_t::get_field( const player_t* player, auto id_field, auto spell_field ) const +void spell_container_t::cache_data( const player_t* player ) { - auto spell = std::invoke( spell_field, this ); - auto id = std::invoke( id_field, this ); - assert( id != 0 ); + if ( cached ) + return; - if ( !spell ) - spell = player->find_spell( id ); + cached = true; - // cannot check spell->id() == id as the spell yields not_found if not available to player - assert( spell ); - - return spell; + _spell = player->find_spell( spell_id ); + _driver = player->find_spell( driver_id ); + _attack = player->find_spell( attack_id ); + _passive = player->find_spell( passive_id ); + _aura = player->find_spell( aura_id ); + _buff = player->find_spell( buff_id ); + _talent = player->find_spell( talent_id ); + _debuff = player->find_spell( debuff_id ); } -const spell_data_t* spell_container_t::spell( const player_t* p ) const { return get_field( p, &spell_container_t::spell_id, &spell_container_t::_spell ); } -const spell_data_t* spell_container_t::driver( const player_t* p ) const { return get_field( p, &spell_container_t::driver_id, &spell_container_t::_driver ); } -const spell_data_t* spell_container_t::attack( const player_t* p ) const { return get_field( p, &spell_container_t::attack_id, &spell_container_t::_attack ); } -const spell_data_t* spell_container_t::passive( const player_t* p ) const { return get_field( p, &spell_container_t::passive_id, &spell_container_t::_passive ); } -const spell_data_t* spell_container_t::aura( const player_t* p ) const { return get_field( p, &spell_container_t::aura_id, &spell_container_t::_aura ); } -const spell_data_t* spell_container_t::buff( const player_t* p ) const { return get_field( p, &spell_container_t::buff_id, &spell_container_t::_buff ); } -const spell_data_t* spell_container_t::talent( const player_t* p ) const { return get_field( p, &spell_container_t::talent_id, &spell_container_t::_talent ); } -const spell_data_t* spell_container_t::debuff( const player_t* p ) const { return get_field( p, &spell_container_t::debuff_id, &spell_container_t::_debuff ); } +const spell_data_t* spell_container_t::spell() const { assert( cached ); return _spell; } +const spell_data_t* spell_container_t::driver() const { assert( cached ); return _driver; } +const spell_data_t* spell_container_t::attack() const { assert( cached ); return _attack; } +const spell_data_t* spell_container_t::passive() const { assert( cached ); return _attack; } +const spell_data_t* spell_container_t::aura() const { assert( cached ); return _aura; } +const spell_data_t* spell_container_t::buff() const { assert( cached ); return _buff; } +const spell_data_t* spell_container_t::talent() const { assert( cached ); return _talent; } +const spell_data_t* spell_container_t::debuff() const { assert( cached ); return _debuff; } + +const spell_container_t* spell_ranks_t::find_rank( int rank, spell_ranks_t::rank_ptype_t field ) +{ + assert( !ranks.empty() ); + + if ( rank == RANK_MAX ) + { + auto& r = ranks.back(); + r.cache_data( player ); + return &ranks.back(); + } + else if ( rank > 0 ) + { + if ( static_cast( rank ) <= ranks.size() ) + { + auto& r = ranks[ rank - 1 ]; + r.cache_data( player ); + return &ranks[ rank - 1 ]; + } + } + else + { + // rank spells are expected to be ordered lowest -> highest, otherwise condition 1 fails + // as a result, first ok spell while reverse iterating yields the highest rank spell that is available + + // if the member being referenced for existence is not &spell_container_t::spell, provide a non-default field + for ( auto& it : ranks | std::ranges::views::reverse ) + { + auto& r = it; + r.cache_data( player ); + if ( auto reference_spell = std::invoke( field, it ); reference_spell && reference_spell->ok() ) + return ⁢ + } + } + + auto& r = ranks.front(); + r.cache_data( player ); + return &ranks.front(); +} diff --git a/engine/dbc/spell_data.hpp b/engine/dbc/spell_data.hpp index a49270e0bc..ba9a2fbf7d 100644 --- a/engine/dbc/spell_data.hpp +++ b/engine/dbc/spell_data.hpp @@ -912,9 +912,13 @@ inline const spelleffect_data_t& spelleffect_data_t::nil() { return spelleffect_ * * Cached collection of spell_data_t pointers. Intended for talents, rank spells etc. */ -struct spell_container_t +struct spell_ranks_t; +struct spell_container_t// : private noncopyable { + friend spell_ranks_t; private: + bool cached = false; + unsigned spell_id = 0; unsigned driver_id = 0; unsigned attack_id = 0; @@ -933,26 +937,28 @@ struct spell_container_t const spell_data_t* _talent = nullptr; const spell_data_t* _debuff = nullptr; - const spell_data_t* get_field( const player_t* player, auto spell_field, auto id_field ) const; - public: - constexpr spell_container_t& set_spell( unsigned id ) { spell_id = id; return *this; } - constexpr spell_container_t& set_driver( unsigned id ) { driver_id = id; return *this; } - constexpr spell_container_t& set_attack( unsigned id ) { attack_id = id; return *this; } - constexpr spell_container_t& set_passive( unsigned id ) { passive_id = id; return *this; } - constexpr spell_container_t& set_aura( unsigned id ) { aura_id = id; return *this; } - constexpr spell_container_t& set_buff( unsigned id ) { buff_id = id; return *this; } - constexpr spell_container_t& set_talent( unsigned id ) { talent_id = id; return *this; } - constexpr spell_container_t& set_debuff( unsigned id ) { debuff_id = id; return *this; } - - const spell_data_t* spell( const player_t* p ) const; - const spell_data_t* driver( const player_t* p ) const; - const spell_data_t* attack( const player_t* p ) const; - const spell_data_t* passive( const player_t* p ) const; - const spell_data_t* aura( const player_t* p ) const; - const spell_data_t* buff( const player_t* p ) const; - const spell_data_t* talent( const player_t* p ) const; - const spell_data_t* debuff( const player_t* p ) const; + spell_container_t& set_spell( unsigned id ) { spell_id = id; return *this; } + spell_container_t& set_driver( unsigned id ) { driver_id = id; return *this; } + spell_container_t& set_attack( unsigned id ) { attack_id = id; return *this; } + spell_container_t& set_passive( unsigned id ) { passive_id = id; return *this; } + spell_container_t& set_aura( unsigned id ) { aura_id = id; return *this; } + spell_container_t& set_buff( unsigned id ) { buff_id = id; return *this; } + spell_container_t& set_talent( unsigned id ) { talent_id = id; return *this; } + spell_container_t& set_debuff( unsigned id ) { debuff_id = id; return *this; } + + virtual void cache_data( const player_t* player ); + + const spell_data_t* spell() const; + const spell_data_t* driver() const; + const spell_data_t* attack() const; + const spell_data_t* passive() const; + const spell_data_t* aura() const; + const spell_data_t* buff() const; + const spell_data_t* talent() const; + const spell_data_t* debuff() const; + + virtual ~spell_container_t() = default; }; enum spell_rank_e : int @@ -961,5 +967,24 @@ enum spell_rank_e : int RANK_MAX = -1, // highest rank in the table }; +struct spell_ranks_t +{ + const player_t* player; + // std::array ranks; + std::vector ranks; + + spell_ranks_t() = default; + template + spell_ranks_t( player_t* player, Pack&&... pack ) + : player( player ), + ranks{ std::forward( pack )... } + {} + + virtual ~spell_ranks_t() = default; + + using rank_ptype_t = const spell_data_t*(spell_container_t::*)() const; + virtual const spell_container_t* find_rank( int rank = RANK_MAX_USABLE, rank_ptype_t field = &spell_container_t::spell ); +}; + #endif // SC_DBC_SPELL_DATA_HPP diff --git a/engine/player/player.cpp b/engine/player/player.cpp index 4fe24ee2a2..6889ce0210 100644 --- a/engine/player/player.cpp +++ b/engine/player/player.cpp @@ -68,7 +68,6 @@ #include #include #include -#include namespace { @@ -10915,32 +10914,6 @@ const spell_data_t* player_t::find_spell( unsigned int id, specialization_e s ) return spell_data_t::not_found(); } -const spell_container_t* player_t::find_rank( util::span ranks, int rank, - player_t::rank_ptype_t field ) const -{ - assert( !ranks.empty() ); - - if ( rank == RANK_MAX ) - return &ranks.back(); - else if ( rank > 0 ) - { - if ( static_cast( rank ) <= ranks.size() ) - return &ranks[ rank - 1 ]; - } - else - { - // rank spells are expected to be ordered lowest -> highest, otherwise condition 1 fails - // as a result, first ok spell while reverse iterating yields the highest rank spell that is available - - // if the member being referenced for existence is not &spell_container_t::spell, provide a non-default field - for ( const auto& it : ranks | std::ranges::views::reverse ) - if ( auto reference_spell = std::invoke( field, it, this ); reference_spell && reference_spell->ok() ) - return ⁢ - } - - assert( false && "unreachable" ); -} - namespace { std::unique_ptr deprecate_expression( player_t& p, util::string_view old_name, util::string_view new_name, action_t* a = nullptr ) diff --git a/engine/player/player.hpp b/engine/player/player.hpp index 5abb562693..954941de54 100644 --- a/engine/player/player.hpp +++ b/engine/player/player.hpp @@ -740,10 +740,6 @@ struct player_t : public actor_t const spell_data_t* find_spell( unsigned int id, specialization_e s ) const; const spell_data_t* find_spell( unsigned int id ) const; - using rank_ptype_t = const spell_data_t*(spell_container_t::*)(const player_t*) const; - const spell_container_t* find_rank( util::span ranks, int rank = RANK_MAX_USABLE, // ) const; - rank_ptype_t field = &spell_container_t::spell ) const; - pet_t* find_pet( util::string_view name ) const; item_t* find_item_by_name( util::string_view name ); item_t* find_item_by_id( unsigned id ); From 916eb4bdc787ce6456f15cf2dd75af532fe8eab0 Mon Sep 17 00:00:00 2001 From: Kate Martin <51387586+renanthera@users.noreply.github.com> Date: Mon, 2 Nov 2026 04:01:08 -0700 Subject: [PATCH 2/2] [ranks] Improve pointer caching. --- engine/dbc/spell_data.cpp | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/engine/dbc/spell_data.cpp b/engine/dbc/spell_data.cpp index d4e891f4d1..27872c5885 100644 --- a/engine/dbc/spell_data.cpp +++ b/engine/dbc/spell_data.cpp @@ -888,21 +888,16 @@ const spell_data_t* spell_container_t::debuff() const { assert( cached ); retur const spell_container_t* spell_ranks_t::find_rank( int rank, spell_ranks_t::rank_ptype_t field ) { assert( !ranks.empty() ); + spell_container_t* container = nullptr; if ( rank == RANK_MAX ) { - auto& r = ranks.back(); - r.cache_data( player ); - return &ranks.back(); + container = &ranks.back(); } else if ( rank > 0 ) { if ( static_cast( rank ) <= ranks.size() ) - { - auto& r = ranks[ rank - 1 ]; - r.cache_data( player ); - return &ranks[ rank - 1 ]; - } + container = &ranks[ rank - 1 ]; } else { @@ -912,14 +907,18 @@ const spell_container_t* spell_ranks_t::find_rank( int rank, spell_ranks_t::rank // if the member being referenced for existence is not &spell_container_t::spell, provide a non-default field for ( auto& it : ranks | std::ranges::views::reverse ) { - auto& r = it; - r.cache_data( player ); + it.cache_data( player ); if ( auto reference_spell = std::invoke( field, it ); reference_spell && reference_spell->ok() ) - return ⁢ + { + container = ⁢ + break; + } } } - auto& r = ranks.front(); - r.cache_data( player ); - return &ranks.front(); + if ( !container ) + container = &ranks.front(); + + container->cache_data( player ); + return container; }