2010-10-07 15:35:36 +02:00
/*
2020-01-02 06:44:10 +01:00
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
2010-10-07 15:35:36 +02:00
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
2008-11-17 22:07:21 -06:00
*/
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
#include "SpellMgr.h"
2012-03-02 15:23:07 -03:00
#include "BattlefieldMgr.h"
2017-05-18 23:52:58 +02:00
#include "BattlefieldWG.h"
#include "BattlegroundMgr.h"
#include "Chat.h"
2020-04-08 13:36:59 +02:00
#include "Containers.h"
2017-06-04 01:00:45 +02:00
#include "DB2Stores.h"
2017-05-18 23:52:58 +02:00
#include "DatabaseEnv.h"
2021-03-30 02:27:50 +03:00
#include "LanguageMgr.h"
2017-05-18 23:52:58 +02:00
#include "Log.h"
2017-06-04 01:00:45 +02:00
#include "MotionMaster.h"
2017-05-18 23:52:58 +02:00
#include "ObjectMgr.h"
2012-11-17 05:18:37 +01:00
#include "Player.h"
2017-05-18 23:52:58 +02:00
#include "SharedDefines.h"
2017-08-13 12:03:43 +02:00
#include "Spell.h"
2017-05-18 23:52:58 +02:00
#include "SpellAuraDefines.h"
#include "SpellInfo.h"
2017-07-18 15:16:32 +02:00
#include <G3D/g3dmath.h>
2020-06-12 01:29:18 +02:00
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/composite_key.hpp>
#include <boost/multi_index/hashed_index.hpp>
#include <boost/multi_index/member.hpp>
namespace
{
struct SpellIdDifficultyIndex ;
struct SpellIdIndex ;
boost :: multi_index :: multi_index_container <
SpellInfo ,
boost :: multi_index :: indexed_by <
boost :: multi_index :: hashed_unique <
boost :: multi_index :: tag < SpellIdDifficultyIndex > ,
boost :: multi_index :: composite_key <
SpellInfo ,
2021-03-12 21:59:10 +01:00
boost :: multi_index :: member < SpellInfo , uint32 const , & SpellInfo :: Id > ,
boost :: multi_index :: member < SpellInfo , Difficulty const , & SpellInfo :: Difficulty >
2020-06-12 01:29:18 +02:00
>
> ,
boost :: multi_index :: hashed_non_unique <
boost :: multi_index :: tag < SpellIdIndex > ,
2021-03-12 21:59:10 +01:00
boost :: multi_index :: member < SpellInfo , uint32 const , & SpellInfo :: Id >
2020-06-12 01:29:18 +02:00
>
>
> mSpellInfoMap ;
2021-03-14 14:45:01 +01:00
class ServersideSpellName
2021-03-12 21:59:10 +01:00
{
2021-03-14 14:45:01 +01:00
public :
explicit ServersideSpellName ( uint32 id , std :: string name ) : _nameStorage ( std :: move ( name ))
2021-03-12 21:59:10 +01:00
{
Name . ID = id ;
InitPointers ();
}
2021-03-14 14:45:01 +01:00
ServersideSpellName ( ServersideSpellName const & right ) : _nameStorage ( right . _nameStorage )
2021-03-12 21:59:10 +01:00
{
Name . ID = right . Name . ID ;
InitPointers ();
}
2021-03-14 14:45:01 +01:00
ServersideSpellName ( ServersideSpellName && right ) noexcept : _nameStorage ( std :: move ( right . _nameStorage ))
2021-03-12 21:59:10 +01:00
{
Name . ID = right . Name . ID ;
InitPointers ();
right . InitPointers ();
}
SpellNameEntry Name ;
private :
void InitPointers ()
{
2021-03-14 14:45:01 +01:00
std :: fill ( std :: begin ( Name . Name . Str ), std :: end ( Name . Name . Str ), _nameStorage . c_str ());
2021-03-12 21:59:10 +01:00
}
2021-03-14 14:45:01 +01:00
std :: string _nameStorage ;
2021-03-12 21:59:10 +01:00
};
std :: vector < ServersideSpellName > mServersideSpellNames ;
2020-06-12 01:29:18 +02:00
std :: unordered_map < std :: pair < uint32 , Difficulty > , SpellProcEntry > mSpellProcMap ;
}
2009-10-17 15:51:44 -07:00
2015-09-07 18:03:33 +02:00
PetFamilySpellsStore sPetFamilySpellsStore ;
2011-07-26 23:09:28 +02:00
bool IsPrimaryProfessionSkill ( uint32 skill )
2008-11-17 22:07:21 -06:00
{
2011-09-15 14:08:17 +02:00
SkillLineEntry const * pSkill = sSkillLineStore . LookupEntry ( skill );
2014-11-09 02:51:35 +02:00
return pSkill && pSkill -> CategoryID == SKILL_CATEGORY_PROFESSION ;
2012-12-22 22:21:33 +01:00
}
2009-10-17 15:51:44 -07:00
2012-12-22 22:21:33 +01:00
bool IsWeaponSkill ( uint32 skill )
{
SkillLineEntry const * pSkill = sSkillLineStore . LookupEntry ( skill );
2014-11-09 02:51:35 +02:00
return pSkill && pSkill -> CategoryID == SKILL_CATEGORY_WEAPON ;
2009-07-23 18:32:32 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
bool IsPartOfSkillLine ( uint32 skillId , uint32 spellId )
2010-02-20 23:57:54 +01:00
{
2011-07-26 23:09:28 +02:00
SkillLineAbilityMapBounds skillBounds = sSpellMgr -> GetSkillLineAbilityMapBounds ( spellId );
for ( SkillLineAbilityMap :: const_iterator itr = skillBounds . first ; itr != skillBounds . second ; ++ itr )
2018-03-12 18:20:26 +01:00
if ( itr -> second -> SkillLine == int32 ( skillId ))
2011-05-29 15:46:14 +02:00
return true ;
2011-07-26 23:09:28 +02:00
return false ;
2010-02-20 23:57:54 +01:00
}
2013-10-28 14:36:07 -04:00
SpellMgr :: SpellMgr () { }
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellMgr ::~ SpellMgr ()
{
UnloadSpellInfoStore ();
}
2016-03-11 17:11:05 +01:00
SpellMgr * SpellMgr :: instance ()
{
static SpellMgr instance ;
return & instance ;
}
2011-07-26 23:09:28 +02:00
/// Some checks for spells, to prevent adding deprecated/broken spells for trainers, spell book, etc
2011-11-07 11:18:00 -06:00
bool SpellMgr :: IsSpellValid ( SpellInfo const * spellInfo , Player * player , bool msg )
2011-07-26 23:09:28 +02:00
{
// not exist
if ( ! spellInfo )
return false ;
2009-10-17 15:51:44 -07:00
2013-08-07 10:31:13 +02:00
bool needCheckReagents = false ;
2011-07-26 23:09:28 +02:00
// check effects
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo -> GetEffects ())
2011-07-26 23:09:28 +02:00
{
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . Effect )
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
// craft spell for crafting non-existed item (break client recipes list show)
case SPELL_EFFECT_CREATE_ITEM :
2017-04-06 23:39:34 +02:00
case SPELL_EFFECT_CREATE_LOOT :
2008-11-17 22:07:21 -06:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . ItemType == 0 )
2011-07-26 23:09:28 +02:00
{
2016-02-20 13:23:35 +01:00
// skip auto-loot crafting spells, it does not need explicit item info (but has special fake items sometimes).
2011-07-26 23:09:28 +02:00
if ( ! spellInfo -> IsLootCrafting ())
2009-04-09 17:32:22 +02:00
{
2011-07-26 23:09:28 +02:00
if ( msg )
2009-04-09 17:32:22 +02:00
{
2011-11-07 11:18:00 -06:00
if ( player )
2016-02-20 13:23:35 +01:00
ChatHandler ( player -> GetSession ()). PSendSysMessage ( "The craft spell %u does not have a create item entry." , spellInfo -> Id );
2011-07-26 23:09:28 +02:00
else
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The craft spell %u does not have a create item entry." , spellInfo -> Id );
2009-04-09 17:32:22 +02:00
}
2008-11-17 22:07:21 -06:00
return false ;
2011-07-26 23:09:28 +02:00
}
}
2016-02-20 13:23:35 +01:00
// also possible IsLootCrafting case but fake items must exist anyway
2021-09-04 15:13:15 +02:00
else if ( ! sObjectMgr -> GetItemTemplate ( spellEffectInfo . ItemType ))
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
if ( msg )
2008-11-17 22:07:21 -06:00
{
2011-11-07 11:18:00 -06:00
if ( player )
2021-09-04 15:13:15 +02:00
ChatHandler ( player -> GetSession ()). PSendSysMessage ( "Craft spell %u has created a non-existing in DB item (Entry: %u) and then..." , spellInfo -> Id , spellEffectInfo . ItemType );
2011-07-26 23:09:28 +02:00
else
2021-09-04 15:13:15 +02:00
TC_LOG_ERROR ( "sql.sql" , "Craft spell %u has created a non-existing item in DB item (Entry: %u) and then..." , spellInfo -> Id , spellEffectInfo . ItemType );
2008-11-17 22:07:21 -06:00
}
2011-07-26 23:09:28 +02:00
return false ;
2011-06-17 12:00:11 +02:00
}
2011-07-26 23:09:28 +02:00
2013-08-07 10:31:13 +02:00
needCheckReagents = true ;
2011-07-26 23:09:28 +02:00
break ;
}
case SPELL_EFFECT_LEARN_SPELL :
{
2021-09-04 15:13:15 +02:00
SpellInfo const * spellInfo2 = sSpellMgr -> GetSpellInfo ( spellEffectInfo . TriggerSpell , DIFFICULTY_NONE );
2011-11-07 11:18:00 -06:00
if ( ! IsSpellValid ( spellInfo2 , player , msg ))
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
if ( msg )
2008-11-17 22:07:21 -06:00
{
2011-11-07 11:18:00 -06:00
if ( player )
2021-09-04 15:13:15 +02:00
ChatHandler ( player -> GetSession ()). PSendSysMessage ( "Spell %u learn to broken spell %u, and then..." , spellInfo -> Id , spellEffectInfo . TriggerSpell );
2011-07-26 23:09:28 +02:00
else
2021-09-04 15:13:15 +02:00
TC_LOG_ERROR ( "sql.sql" , "Spell %u learn to invalid spell %u, and then..." , spellInfo -> Id , spellEffectInfo . TriggerSpell );
2008-11-17 22:07:21 -06:00
}
2011-07-26 23:09:28 +02:00
return false ;
2011-06-17 12:00:11 +02:00
}
2011-07-26 23:09:28 +02:00
break ;
2008-11-17 22:07:21 -06:00
}
2021-09-04 15:13:15 +02:00
default :
break ;
2008-11-17 22:07:21 -06:00
}
}
2009-10-17 15:51:44 -07:00
2013-08-07 10:31:13 +02:00
if ( needCheckReagents )
2011-07-26 23:09:28 +02:00
{
for ( uint8 j = 0 ; j < MAX_SPELL_REAGENTS ; ++ j )
{
if ( spellInfo -> Reagent [ j ] > 0 && ! sObjectMgr -> GetItemTemplate ( spellInfo -> Reagent [ j ]))
{
if ( msg )
{
2011-11-07 11:18:00 -06:00
if ( player )
2016-02-20 13:23:35 +01:00
ChatHandler ( player -> GetSession ()). PSendSysMessage ( "Craft spell %u refers a non-existing reagent in DB item (Entry: %u) and then..." , spellInfo -> Id , spellInfo -> Reagent [ j ]);
2011-07-26 23:09:28 +02:00
else
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "Craft spell %u refers to a non-existing reagent in DB, item (Entry: %u) and then ... ", spellInfo->Id, spellInfo->Reagent[j]) ;
2011-07-26 23:09:28 +02:00
}
return false ;
}
}
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
return true ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellChainNode const * SpellMgr :: GetSpellChainNode ( uint32 spell_id ) const
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
SpellChainMap :: const_iterator itr = mSpellChains . find ( spell_id );
if ( itr == mSpellChains . end ())
2020-08-14 17:06:03 +02:00
return nullptr ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
return & itr -> second ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 SpellMgr :: GetFirstSpellInChain ( uint32 spell_id ) const
{
if ( SpellChainNode const * node = GetSpellChainNode ( spell_id ))
return node -> first -> Id ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
return spell_id ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 SpellMgr :: GetLastSpellInChain ( uint32 spell_id ) const
{
if ( SpellChainNode const * node = GetSpellChainNode ( spell_id ))
return node -> last -> Id ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
return spell_id ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 SpellMgr :: GetNextSpellInChain ( uint32 spell_id ) const
{
if ( SpellChainNode const * node = GetSpellChainNode ( spell_id ))
2011-08-03 11:23:08 +02:00
if ( node -> next )
return node -> next -> Id ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
return 0 ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 SpellMgr :: GetPrevSpellInChain ( uint32 spell_id ) const
{
if ( SpellChainNode const * node = GetSpellChainNode ( spell_id ))
2011-08-03 11:23:08 +02:00
if ( node -> prev )
return node -> prev -> Id ;
2010-04-18 23:20:07 +02:00
2011-07-26 23:09:28 +02:00
return 0 ;
}
2010-04-18 23:20:07 +02:00
2011-07-26 23:09:28 +02:00
uint8 SpellMgr :: GetSpellRank ( uint32 spell_id ) const
{
if ( SpellChainNode const * node = GetSpellChainNode ( spell_id ))
return node -> rank ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
return 0 ;
}
2010-04-18 23:20:07 +02:00
2011-07-26 23:09:28 +02:00
uint32 SpellMgr :: GetSpellWithRank ( uint32 spell_id , uint32 rank , bool strict ) const
{
if ( SpellChainNode const * node = GetSpellChainNode ( spell_id ))
{
if ( rank != node -> rank )
return GetSpellWithRank ( node -> rank < rank ? node -> next -> Id : node -> prev -> Id , rank , strict );
}
else if ( strict && rank > 1 )
return 0 ;
return spell_id ;
}
2009-10-17 15:51:44 -07:00
2017-07-26 00:53:25 +02:00
Trinity :: IteratorPair < SpellRequiredMap :: const_iterator > SpellMgr :: GetSpellsRequiredForSpellBounds ( uint32 spell_id ) const
2011-07-26 23:09:28 +02:00
{
2017-07-26 00:53:25 +02:00
return Trinity :: Containers :: MapEqualRange ( mSpellReq , spell_id );
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellsRequiringSpellMapBounds SpellMgr :: GetSpellsRequiringSpellBounds ( uint32 spell_id ) const
{
2013-01-02 09:53:43 +01:00
return mSpellsReqSpell . equal_range ( spell_id );
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
bool SpellMgr :: IsSpellRequiringSpell ( uint32 spellid , uint32 req_spellid ) const
{
SpellsRequiringSpellMapBounds spellsRequiringSpell = GetSpellsRequiringSpellBounds ( req_spellid );
for ( SpellsRequiringSpellMap :: const_iterator itr = spellsRequiringSpell . first ; itr != spellsRequiringSpell . second ; ++ itr )
2009-05-25 16:50:01 -05:00
{
2011-07-26 23:09:28 +02:00
if ( itr -> second == spellid )
return true ;
2009-05-25 16:50:01 -05:00
}
2011-07-26 23:09:28 +02:00
return false ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellLearnSkillNode const * SpellMgr :: GetSpellLearnSkill ( uint32 spell_id ) const
{
SpellLearnSkillMap :: const_iterator itr = mSpellLearnSkills . find ( spell_id );
if ( itr != mSpellLearnSkills . end ())
return & itr -> second ;
else
2020-08-14 17:06:03 +02:00
return nullptr ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellLearnSpellMapBounds SpellMgr :: GetSpellLearnSpellMapBounds ( uint32 spell_id ) const
2008-11-17 22:07:21 -06:00
{
2013-01-02 09:53:43 +01:00
return mSpellLearnSpells . equal_range ( spell_id );
2011-07-26 23:09:28 +02:00
}
2010-12-19 17:06:33 +01:00
2011-07-26 23:09:28 +02:00
bool SpellMgr :: IsSpellLearnSpell ( uint32 spell_id ) const
{
return mSpellLearnSpells . find ( spell_id ) != mSpellLearnSpells . end ();
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
bool SpellMgr :: IsSpellLearnToSpell ( uint32 spell_id1 , uint32 spell_id2 ) const
{
SpellLearnSpellMapBounds bounds = GetSpellLearnSpellMapBounds ( spell_id1 );
for ( SpellLearnSpellMap :: const_iterator i = bounds . first ; i != bounds . second ; ++ i )
2015-02-08 16:23:31 +01:00
if ( i -> second . Spell == spell_id2 )
2011-07-26 23:09:28 +02:00
return true ;
return false ;
}
2009-10-17 15:51:44 -07:00
2013-05-13 11:30:48 +03:00
SpellTargetPosition const * SpellMgr :: GetSpellTargetPosition ( uint32 spell_id , SpellEffIndex effIndex ) const
2011-07-26 23:09:28 +02:00
{
2013-05-13 11:30:48 +03:00
SpellTargetPositionMap :: const_iterator itr = mSpellTargetPositions . find ( std :: make_pair ( spell_id , effIndex ));
2011-07-26 23:09:28 +02:00
if ( itr != mSpellTargetPositions . end ())
return & itr -> second ;
2020-08-14 17:06:03 +02:00
return nullptr ;
2011-07-26 23:09:28 +02:00
}
SpellSpellGroupMapBounds SpellMgr :: GetSpellSpellGroupMapBounds ( uint32 spell_id ) const
{
spell_id = GetFirstSpellInChain ( spell_id );
2013-01-02 09:53:43 +01:00
return mSpellSpellGroup . equal_range ( spell_id );
2011-07-26 23:09:28 +02:00
}
2012-10-07 01:31:43 +02:00
bool SpellMgr :: IsSpellMemberOfSpellGroup ( uint32 spellid , SpellGroup groupid ) const
2011-07-26 23:09:28 +02:00
{
SpellSpellGroupMapBounds spellGroup = GetSpellSpellGroupMapBounds ( spellid );
2012-05-30 08:01:02 +02:00
for ( SpellSpellGroupMap :: const_iterator itr = spellGroup . first ; itr != spellGroup . second ; ++ itr )
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
if ( itr -> second == groupid )
return true ;
2008-11-17 22:07:21 -06:00
}
2011-07-26 23:09:28 +02:00
return false ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellGroupSpellMapBounds SpellMgr :: GetSpellGroupSpellMapBounds ( SpellGroup group_id ) const
{
2013-01-02 09:53:43 +01:00
return mSpellGroupSpell . equal_range ( group_id );
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: GetSetOfSpellsInSpellGroup ( SpellGroup group_id , std :: set < uint32 >& foundSpells ) const
{
std :: set < SpellGroup > usedGroups ;
GetSetOfSpellsInSpellGroup ( group_id , foundSpells , usedGroups );
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: GetSetOfSpellsInSpellGroup ( SpellGroup group_id , std :: set < uint32 >& foundSpells , std :: set < SpellGroup >& usedGroups ) const
{
if ( usedGroups . find ( group_id ) != usedGroups . end ())
return ;
usedGroups . insert ( group_id );
SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds ( group_id );
2012-05-30 08:01:02 +02:00
for ( SpellGroupSpellMap :: const_iterator itr = groupSpell . first ; itr != groupSpell . second ; ++ itr )
2011-07-26 23:09:28 +02:00
{
if ( itr -> second < 0 )
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
SpellGroup currGroup = ( SpellGroup ) abs ( itr -> second );
GetSetOfSpellsInSpellGroup ( currGroup , foundSpells , usedGroups );
2008-11-17 22:07:21 -06:00
}
2011-07-26 23:09:28 +02:00
else
{
foundSpells . insert ( itr -> second );
}
}
}
2009-10-17 15:51:44 -07:00
2017-04-14 04:45:12 -03:00
bool SpellMgr :: AddSameEffectStackRuleSpellGroups ( SpellInfo const * spellInfo , uint32 auraType , int32 amount , std :: map < SpellGroup , int32 >& groups ) const
2011-10-18 23:27:39 +02:00
{
2011-10-18 23:38:55 +02:00
uint32 spellId = spellInfo -> GetFirstRankSpell () -> Id ;
2017-04-14 03:47:23 -03:00
auto spellGroupBounds = GetSpellSpellGroupMapBounds ( spellId );
2011-10-18 23:38:55 +02:00
// Find group with SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT if it belongs to one
2017-04-14 03:47:23 -03:00
for ( auto itr = spellGroupBounds . first ; itr != spellGroupBounds . second ; ++ itr )
2011-10-18 23:38:55 +02:00
{
SpellGroup group = itr -> second ;
2017-04-14 03:47:23 -03:00
auto found = mSpellSameEffectStack . find ( group );
if ( found != mSpellSameEffectStack . end ())
2011-10-18 23:38:55 +02:00
{
2017-04-14 03:47:23 -03:00
// check auraTypes
2017-04-14 04:45:12 -03:00
if ( ! found -> second . count ( auraType ))
2017-04-14 03:47:23 -03:00
continue ;
// Put the highest amount in the map
auto groupItr = groups . find ( group );
if ( groupItr == groups . end ())
groups . emplace ( group , amount );
else
2011-10-18 23:38:55 +02:00
{
2017-04-14 03:47:23 -03:00
int32 curr_amount = groups [ group ];
// Take absolute value because this also counts for the highest negative aura
if ( std :: abs ( curr_amount ) < std :: abs ( amount ))
groupItr -> second = amount ;
2011-10-18 23:38:55 +02:00
}
2017-04-14 03:47:23 -03:00
// return because a spell should be in only one SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT group per auraType
return true ;
2011-10-18 23:38:55 +02:00
}
}
// Not in a SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT group, so return false
return false ;
2011-10-18 23:27:39 +02:00
}
2011-07-26 23:09:28 +02:00
SpellGroupStackRule SpellMgr :: CheckSpellGroupStackRules ( SpellInfo const * spellInfo1 , SpellInfo const * spellInfo2 ) const
{
2017-03-19 04:11:55 -03:00
ASSERT ( spellInfo1 );
ASSERT ( spellInfo2 );
2011-07-26 23:09:28 +02:00
uint32 spellid_1 = spellInfo1 -> GetFirstRankSpell () -> Id ;
uint32 spellid_2 = spellInfo2 -> GetFirstRankSpell () -> Id ;
2017-03-19 04:11:55 -03:00
2011-07-26 23:09:28 +02:00
// find SpellGroups which are common for both spells
SpellSpellGroupMapBounds spellGroup1 = GetSpellSpellGroupMapBounds ( spellid_1 );
std :: set < SpellGroup > groups ;
2012-05-30 08:01:02 +02:00
for ( SpellSpellGroupMap :: const_iterator itr = spellGroup1 . first ; itr != spellGroup1 . second ; ++ itr )
2011-07-26 23:09:28 +02:00
{
if ( IsSpellMemberOfSpellGroup ( spellid_2 , itr -> second ))
2008-11-26 15:14:03 -06:00
{
2011-07-26 23:09:28 +02:00
bool add = true ;
SpellGroupSpellMapBounds groupSpell = GetSpellGroupSpellMapBounds ( itr -> second );
2012-05-30 08:01:02 +02:00
for ( SpellGroupSpellMap :: const_iterator itr2 = groupSpell . first ; itr2 != groupSpell . second ; ++ itr2 )
2008-11-26 15:14:03 -06:00
{
2011-07-26 23:09:28 +02:00
if ( itr2 -> second < 0 )
{
SpellGroup currGroup = ( SpellGroup ) abs ( itr2 -> second );
if ( IsSpellMemberOfSpellGroup ( spellid_1 , currGroup ) && IsSpellMemberOfSpellGroup ( spellid_2 , currGroup ))
{
add = false ;
break ;
}
}
2008-11-26 15:14:03 -06:00
}
2011-07-26 23:09:28 +02:00
if ( add )
groups . insert ( itr -> second );
2008-11-26 15:14:03 -06:00
}
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellGroupStackRule rule = SPELL_GROUP_STACK_RULE_DEFAULT ;
2012-05-30 08:01:02 +02:00
for ( std :: set < SpellGroup >:: iterator itr = groups . begin (); itr != groups . end (); ++ itr )
2011-07-26 23:09:28 +02:00
{
SpellGroupStackMap :: const_iterator found = mSpellGroupStack . find ( * itr );
if ( found != mSpellGroupStack . end ())
rule = found -> second ;
if ( rule )
break ;
}
return rule ;
}
2014-06-07 23:43:11 +01:00
SpellGroupStackRule SpellMgr :: GetSpellGroupStackRule ( SpellGroup group ) const
{
SpellGroupStackMap :: const_iterator itr = mSpellGroupStack . find ( group );
if ( itr != mSpellGroupStack . end ())
return itr -> second ;
return SPELL_GROUP_STACK_RULE_DEFAULT ;
}
2020-06-12 01:29:18 +02:00
SpellProcEntry const * SpellMgr :: GetSpellProcEntry ( SpellInfo const * spellInfo ) const
2011-06-26 13:54:44 +02:00
{
2020-06-12 01:29:18 +02:00
SpellProcEntry const * procEntry = Trinity :: Containers :: MapGetValuePtr ( mSpellProcMap , { spellInfo -> Id , spellInfo -> Difficulty });
if ( procEntry )
return procEntry ;
if ( DifficultyEntry const * difficulty = sDifficultyStore . LookupEntry ( spellInfo -> Difficulty ))
{
do
{
procEntry = Trinity :: Containers :: MapGetValuePtr ( mSpellProcMap , { spellInfo -> Id , Difficulty ( difficulty -> FallbackDifficultyID ) });
if ( procEntry )
return procEntry ;
difficulty = sDifficultyStore . LookupEntry ( difficulty -> FallbackDifficultyID );
} while ( difficulty );
}
return nullptr ;
2011-07-26 23:09:28 +02:00
}
2011-06-26 13:54:44 +02:00
2016-11-19 03:05:44 -03:00
bool SpellMgr :: CanSpellTriggerProcOnEvent ( SpellProcEntry const & procEntry , ProcEventInfo & eventInfo )
2011-07-26 23:09:28 +02:00
{
// proc type doesn't match
2016-05-11 20:38:24 +02:00
if ( ! ( eventInfo . GetTypeMask () & procEntry . ProcFlags ))
2011-07-26 23:09:28 +02:00
return false ;
2011-06-26 13:54:44 +02:00
2011-07-26 23:09:28 +02:00
// check XP or honor target requirement
2016-05-11 20:38:24 +02:00
if ( procEntry . AttributesMask & PROC_ATTR_REQ_EXP_OR_HONOR )
2011-07-26 23:09:28 +02:00
if ( Player * actor = eventInfo . GetActor () -> ToPlayer ())
if ( eventInfo . GetActionTarget () && ! actor -> isHonorOrXPTarget ( eventInfo . GetActionTarget ()))
return false ;
2011-06-26 13:54:44 +02:00
2017-08-13 12:03:43 +02:00
// check power requirement
if ( procEntry . AttributesMask & PROC_ATTR_REQ_POWER_COST )
{
if ( ! eventInfo . GetProcSpell ())
return false ;
std :: vector < SpellPowerCost > const & costs = eventInfo . GetProcSpell () -> GetPowerCost ();
auto m = std :: find_if ( costs . begin (), costs . end (), []( SpellPowerCost const & cost ) { return cost . Amount > 0 ; });
if ( m == costs . end ())
return false ;
}
2011-07-26 23:09:28 +02:00
// always trigger for these types
if ( eventInfo . GetTypeMask () & ( PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH ))
return true ;
2011-07-10 13:13:02 +02:00
2017-08-13 12:03:43 +02:00
// do triggered cast checks
2016-10-12 01:32:40 -03:00
// Do not consider autoattacks as triggered spells
if ( ! ( procEntry . AttributesMask & PROC_ATTR_TRIGGERED_CAN_PROC ) && ! ( eventInfo . GetTypeMask () & AUTO_ATTACK_PROC_FLAG_MASK ))
2017-08-13 12:03:43 +02:00
{
if ( Spell const * spell = eventInfo . GetProcSpell ())
{
if ( spell -> IsTriggered ())
{
SpellInfo const * spellInfo = spell -> GetSpellInfo ();
if ( ! spellInfo -> HasAttribute ( SPELL_ATTR3_TRIGGERED_CAN_TRIGGER_PROC_2 ) &&
! spellInfo -> HasAttribute ( SPELL_ATTR2_TRIGGERED_CAN_TRIGGER_PROC ))
return false ;
}
}
}
2011-07-10 13:13:02 +02:00
// check school mask (if set) for other trigger types
2016-05-11 20:38:24 +02:00
if ( procEntry . SchoolMask && ! ( eventInfo . GetSchoolMask () & procEntry . SchoolMask ))
2011-07-10 13:13:02 +02:00
return false ;
// check spell family name/flags (if set) for spells
2018-02-11 20:42:06 -03:00
if ( eventInfo . GetTypeMask () & SPELL_PROC_FLAG_MASK )
2011-07-10 13:13:02 +02:00
{
2016-11-04 14:21:28 -03:00
if ( SpellInfo const * eventSpellInfo = eventInfo . GetSpellInfo ())
if ( ! eventSpellInfo -> IsAffected ( procEntry . SpellFamilyName , procEntry . SpellFamilyMask ))
return false ;
2011-07-10 13:13:02 +02:00
2018-02-11 20:42:06 -03:00
// check spell type mask (if set)
2016-05-11 20:38:24 +02:00
if ( procEntry . SpellTypeMask && ! ( eventInfo . GetSpellTypeMask () & procEntry . SpellTypeMask ))
2011-07-10 13:13:02 +02:00
return false ;
}
// check spell phase mask
if ( eventInfo . GetTypeMask () & REQ_SPELL_PHASE_PROC_FLAG_MASK )
{
2016-05-11 20:38:24 +02:00
if ( ! ( eventInfo . GetSpellPhaseMask () & procEntry . SpellPhaseMask ))
2011-07-10 13:13:02 +02:00
return false ;
}
// check hit mask (on taken hit or on done hit, but not on spell cast phase)
if (( eventInfo . GetTypeMask () & TAKEN_HIT_PROC_FLAG_MASK ) || (( eventInfo . GetTypeMask () & DONE_HIT_PROC_FLAG_MASK ) && ! ( eventInfo . GetSpellPhaseMask () & PROC_SPELL_PHASE_CAST )))
{
2016-05-11 20:38:24 +02:00
uint32 hitMask = procEntry . HitMask ;
2011-07-10 13:13:02 +02:00
// get default values if hit mask not set
if ( ! hitMask )
{
// for taken procs allow normal + critical hits by default
if ( eventInfo . GetTypeMask () & TAKEN_HIT_PROC_FLAG_MASK )
hitMask |= PROC_HIT_NORMAL | PROC_HIT_CRITICAL ;
// for done procs allow normal + critical + absorbs by default
else
hitMask |= PROC_HIT_NORMAL | PROC_HIT_CRITICAL | PROC_HIT_ABSORB ;
}
if ( ! ( eventInfo . GetHitMask () & hitMask ))
return false ;
}
return true ;
}
2011-08-21 12:00:36 +02:00
SpellThreatEntry const * SpellMgr :: GetSpellThreatEntry ( uint32 spellID ) const
2011-07-26 23:09:28 +02:00
{
2011-08-21 12:00:36 +02:00
SpellThreatMap :: const_iterator itr = mSpellThreatMap . find ( spellID );
if ( itr != mSpellThreatMap . end ())
return & itr -> second ;
else
{
uint32 firstSpell = GetFirstSpellInChain ( spellID );
2011-11-23 19:17:33 +01:00
itr = mSpellThreatMap . find ( firstSpell );
2011-08-21 12:00:36 +02:00
if ( itr != mSpellThreatMap . end ())
return & itr -> second ;
}
2020-08-14 17:06:03 +02:00
return nullptr ;
2011-07-26 23:09:28 +02:00
}
2011-06-26 13:54:44 +02:00
2011-07-26 23:09:28 +02:00
SkillLineAbilityMapBounds SpellMgr :: GetSkillLineAbilityMapBounds ( uint32 spell_id ) const
{
2013-01-02 09:53:43 +01:00
return mSkillLineAbilityMap . equal_range ( spell_id );
2011-07-26 23:09:28 +02:00
}
2011-06-26 13:54:44 +02:00
2013-02-26 11:05:10 +01:00
PetAura const * SpellMgr :: GetPetAura ( uint32 spell_id , uint8 eff ) const
2011-07-26 23:09:28 +02:00
{
SpellPetAuraMap :: const_iterator itr = mSpellPetAuraMap . find (( spell_id << 8 ) + eff );
if ( itr != mSpellPetAuraMap . end ())
return & itr -> second ;
else
2020-08-14 17:06:03 +02:00
return nullptr ;
2011-07-26 23:09:28 +02:00
}
2011-06-26 13:54:44 +02:00
2011-07-26 23:09:28 +02:00
SpellEnchantProcEntry const * SpellMgr :: GetSpellEnchantProcEvent ( uint32 enchId ) const
{
SpellEnchantProcEventMap :: const_iterator itr = mSpellEnchantProcEventMap . find ( enchId );
if ( itr != mSpellEnchantProcEventMap . end ())
return & itr -> second ;
2020-08-14 17:06:03 +02:00
return nullptr ;
2011-06-26 13:54:44 +02:00
}
2011-07-26 23:09:28 +02:00
bool SpellMgr :: IsArenaAllowedEnchancment ( uint32 ench_id ) const
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
return mEnchantCustomAttr [ ench_id ];
}
2010-12-19 17:06:33 +01:00
2018-02-26 22:37:06 -03:00
std :: vector < int32 > const * SpellMgr :: GetSpellLinked ( int32 spell_id ) const
2011-07-26 23:09:28 +02:00
{
2018-02-26 22:37:06 -03:00
return Trinity :: Containers :: MapGetValuePtr ( mSpellLinkedMap , spell_id );
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
PetLevelupSpellSet const * SpellMgr :: GetPetLevelupSpellList ( uint32 petFamily ) const
{
PetLevelupSpellMap :: const_iterator itr = mPetLevelupSpellMap . find ( petFamily );
if ( itr != mPetLevelupSpellMap . end ())
return & itr -> second ;
else
2020-08-14 17:06:03 +02:00
return nullptr ;
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
PetDefaultSpellsEntry const * SpellMgr :: GetPetDefaultSpellsEntry ( int32 id ) const
{
PetDefaultSpellsMap :: const_iterator itr = mPetDefaultSpellsMap . find ( id );
if ( itr != mPetDefaultSpellsMap . end ())
return & itr -> second ;
2020-08-14 17:06:03 +02:00
return nullptr ;
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellAreaMapBounds SpellMgr :: GetSpellAreaMapBounds ( uint32 spell_id ) const
{
2013-01-02 09:53:43 +01:00
return mSpellAreaMap . equal_range ( spell_id );
2011-07-26 23:09:28 +02:00
}
2010-02-02 22:35:01 +01:00
2012-10-16 21:43:23 -05:00
SpellAreaForQuestMapBounds SpellMgr :: GetSpellAreaForQuestMapBounds ( uint32 quest_id ) const
2011-07-26 23:09:28 +02:00
{
2013-01-02 09:53:43 +01:00
return mSpellAreaForQuestMap . equal_range ( quest_id );
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellAreaForQuestMapBounds SpellMgr :: GetSpellAreaForQuestEndMapBounds ( uint32 quest_id ) const
{
2013-01-02 09:53:43 +01:00
return mSpellAreaForQuestEndMap . equal_range ( quest_id );
2011-07-26 23:09:28 +02:00
}
2010-02-02 22:35:01 +01:00
2011-07-26 23:09:28 +02:00
SpellAreaForAuraMapBounds SpellMgr :: GetSpellAreaForAuraMapBounds ( uint32 spell_id ) const
{
2013-01-02 09:53:43 +01:00
return mSpellAreaForAuraMap . equal_range ( spell_id );
2011-07-26 23:09:28 +02:00
}
2010-02-01 19:22:32 +01:00
2011-07-26 23:09:28 +02:00
SpellAreaForAreaMapBounds SpellMgr :: GetSpellAreaForAreaMapBounds ( uint32 area_id ) const
{
2013-01-02 09:53:43 +01:00
return mSpellAreaForAreaMap . equal_range ( area_id );
2011-07-26 23:09:28 +02:00
}
2010-02-02 22:35:01 +01:00
2020-06-12 01:29:18 +02:00
SpellInfo const * SpellMgr :: GetSpellInfo ( uint32 spellId , Difficulty difficulty ) const
{
auto itr = mSpellInfoMap . find ( boost :: make_tuple ( spellId , difficulty ));
if ( itr != mSpellInfoMap . end ())
return &* itr ;
if ( DifficultyEntry const * difficultyEntry = sDifficultyStore . LookupEntry ( difficulty ))
{
do
{
itr = mSpellInfoMap . find ( boost :: make_tuple ( spellId , Difficulty ( difficultyEntry -> FallbackDifficultyID )));
if ( itr != mSpellInfoMap . end ())
return &* itr ;
difficultyEntry = sDifficultyStore . LookupEntry ( difficultyEntry -> FallbackDifficultyID );
} while ( difficultyEntry );
}
return nullptr ;
}
auto _GetSpellInfo ( uint32 spellId )
{
return Trinity :: Containers :: MakeIteratorPair ( mSpellInfoMap . get < SpellIdIndex > (). equal_range ( spellId ));
}
void SpellMgr :: ForEachSpellInfo ( std :: function < void ( SpellInfo const * ) > callback )
{
for ( SpellInfo const & spellInfo : mSpellInfoMap )
callback ( & spellInfo );
}
void SpellMgr :: ForEachSpellInfoDifficulty ( uint32 spellId , std :: function < void ( SpellInfo const * ) > callback )
{
for ( SpellInfo const & spellInfo : _GetSpellInfo ( spellId ))
callback ( & spellInfo );
}
2011-07-26 23:09:28 +02:00
bool SpellArea :: IsFitToRequirements ( Player const * player , uint32 newZone , uint32 newArea ) const
{
2016-02-20 13:23:35 +01:00
if ( gender != GENDER_NONE ) // is not expected gender
2019-07-02 12:17:03 +04:00
if ( ! player || gender != player -> GetNativeGender ())
2011-07-26 23:09:28 +02:00
return false ;
2016-02-20 13:23:35 +01:00
if ( raceMask ) // is not expected race
2019-07-01 21:36:32 +02:00
if ( ! player || ! raceMask . HasRace ( player -> GetRace ()))
2011-07-26 23:09:28 +02:00
return false ;
2016-02-20 13:23:35 +01:00
if ( areaId ) // is not in expected zone
2011-07-26 23:09:28 +02:00
if ( newZone != areaId && newArea != areaId )
return false ;
2016-02-20 13:23:35 +01:00
if ( questStart ) // is not in expected required quest state
2012-10-16 21:43:23 -05:00
if ( ! player || ((( 1 << player -> GetQuestStatus ( questStart )) & questStartStatus ) == 0 ))
2011-07-26 23:09:28 +02:00
return false ;
2016-02-20 13:23:35 +01:00
if ( questEnd ) // is not in expected forbidden quest state
2012-10-16 21:43:23 -05:00
if ( ! player || ((( 1 << player -> GetQuestStatus ( questEnd )) & questEndStatus ) == 0 ))
2011-07-26 23:09:28 +02:00
return false ;
2016-02-20 13:23:35 +01:00
if ( auraSpell ) // does not have expected aura
2011-07-26 23:09:28 +02:00
if ( ! player || ( auraSpell > 0 && ! player -> HasAura ( auraSpell )) || ( auraSpell < 0 && player -> HasAura ( - auraSpell )))
return false ;
2013-08-07 10:31:13 +02:00
if ( player )
{
if ( Battleground * bg = player -> GetBattleground ())
return bg -> IsSpellAllowed ( spellId , player );
}
// Extra conditions
2011-07-26 23:09:28 +02:00
switch ( spellId )
2010-02-02 22:35:01 +01:00
{
2013-01-03 17:37:39 +01:00
case 91604 : // No fly Zone - Wintergrasp
2012-02-21 11:33:49 -05:00
{
if ( ! player )
return false ;
2012-03-02 22:00:26 -03:00
Battlefield * Bf = sBattlefieldMgr -> GetBattlefieldToZoneId ( player -> GetZoneId ());
2012-02-21 11:33:49 -05:00
if ( ! Bf || Bf -> CanFlyIn () || ( ! player -> HasAuraType ( SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED ) && ! player -> HasAuraType ( SPELL_AURA_FLY )))
return false ;
break ;
}
2012-03-02 15:23:07 -03:00
case 56618 : // Horde Controls Factory Phase Shift
case 56617 : // Alliance Controls Factory Phase Shift
2013-01-03 16:58:38 +01:00
{
if ( ! player )
return false ;
2012-03-02 15:23:07 -03:00
2013-01-03 16:58:38 +01:00
Battlefield * bf = sBattlefieldMgr -> GetBattlefieldToZoneId ( player -> GetZoneId ());
2012-03-02 15:23:07 -03:00
2013-01-03 16:58:38 +01:00
if ( ! bf || bf -> GetTypeId () != BATTLEFIELD_WG )
return false ;
2012-03-02 15:23:07 -03:00
2013-01-03 16:58:38 +01:00
// team that controls the workshop in the specified area
uint32 team = bf -> GetData ( newArea );
2012-08-22 22:56:31 +01:00
2013-01-03 16:58:38 +01:00
if ( team == TEAM_HORDE )
return spellId == 56618 ;
else if ( team == TEAM_ALLIANCE )
return spellId == 56617 ;
2012-03-02 15:23:07 -03:00
break ;
2013-01-03 16:58:38 +01:00
}
2012-08-26 16:46:25 +02:00
case 57940 : // Essence of Wintergrasp - Northrend
case 58045 : // Essence of Wintergrasp - Wintergrasp
{
if ( ! player )
return false ;
2012-08-27 00:26:02 +01:00
2012-08-26 16:46:25 +02:00
if ( Battlefield * battlefieldWG = sBattlefieldMgr -> GetBattlefieldByBattleId ( BATTLEFIELD_BATTLEID_WG ))
return battlefieldWG -> IsEnabled () && ( player -> GetTeamId () == battlefieldWG -> GetDefenderTeam ()) && ! battlefieldWG -> IsWarTime ();
break ;
}
2012-08-26 19:38:05 +02:00
case 74411 : // Battleground - Dampening
{
if ( ! player )
return false ;
if ( Battlefield * bf = sBattlefieldMgr -> GetBattlefieldToZoneId ( player -> GetZoneId ()))
return bf -> IsWarTime ();
break ;
}
2010-02-02 22:35:01 +01:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
return true ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2013-07-21 18:56:25 +02:00
void SpellMgr :: UnloadSpellInfoChains ()
2010-01-23 17:05:20 +01:00
{
2013-07-21 18:56:25 +02:00
for ( SpellChainMap :: iterator itr = mSpellChains . begin (); itr != mSpellChains . end (); ++ itr )
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : _GetSpellInfo ( itr -> first ))
2020-08-14 17:06:03 +02:00
const_cast < SpellInfo &> ( spellInfo ). ChainEntry = nullptr ;
2013-07-21 18:56:25 +02:00
mSpellChains . clear ();
}
2010-12-19 17:06:33 +01:00
2013-07-21 18:56:25 +02:00
void SpellMgr :: LoadSpellRanks ()
{
uint32 oldMSTime = getMSTime ();
2016-06-15 16:20:43 +02:00
std :: map < uint32 /*spell*/ , uint32 /*next*/ > chains ;
std :: set < uint32 > hasPrev ;
for ( SkillLineAbilityEntry const * skillAbility : sSkillLineAbilityStore )
2010-01-23 17:05:20 +01:00
{
2016-06-15 16:20:43 +02:00
if ( ! skillAbility -> SupercedesSpell )
2011-07-26 23:09:28 +02:00
continue ;
2010-01-23 17:05:20 +01:00
2020-06-12 01:29:18 +02:00
if ( ! GetSpellInfo ( skillAbility -> SupercedesSpell , DIFFICULTY_NONE ) || ! GetSpellInfo ( skillAbility -> Spell , DIFFICULTY_NONE ))
2011-07-26 23:09:28 +02:00
continue ;
2013-07-21 18:56:25 +02:00
2018-03-12 18:20:26 +01:00
chains [ skillAbility -> SupercedesSpell ] = skillAbility -> Spell ;
hasPrev . insert ( skillAbility -> Spell );
2016-06-15 16:20:43 +02:00
}
2013-07-21 18:56:25 +02:00
2016-06-15 16:20:43 +02:00
// each key in chains that isn't present in hasPrev is a first rank
for ( auto itr = chains . begin (); itr != chains . end (); ++ itr )
{
if ( hasPrev . count ( itr -> first ))
continue ;
2012-11-16 09:51:44 +01:00
2020-06-12 01:29:18 +02:00
SpellInfo const * first = AssertSpellInfo ( itr -> first , DIFFICULTY_NONE );
SpellInfo const * next = AssertSpellInfo ( itr -> second , DIFFICULTY_NONE );
2016-06-15 16:20:43 +02:00
mSpellChains [ itr -> first ]. first = first ;
mSpellChains [ itr -> first ]. prev = nullptr ;
mSpellChains [ itr -> first ]. next = next ;
mSpellChains [ itr -> first ]. last = next ;
mSpellChains [ itr -> first ]. rank = 1 ;
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & difficultyInfo : _GetSpellInfo ( itr -> first ))
const_cast < SpellInfo &> ( difficultyInfo ). ChainEntry = & mSpellChains [ itr -> first ];
2016-06-15 16:20:43 +02:00
mSpellChains [ itr -> second ]. first = first ;
mSpellChains [ itr -> second ]. prev = first ;
mSpellChains [ itr -> second ]. next = nullptr ;
mSpellChains [ itr -> second ]. last = next ;
mSpellChains [ itr -> second ]. rank = 2 ;
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & difficultyInfo : _GetSpellInfo ( itr -> second ))
const_cast < SpellInfo &> ( difficultyInfo ). ChainEntry = & mSpellChains [ itr -> second ];
2016-06-15 16:20:43 +02:00
uint8 rank = 3 ;
auto nextItr = chains . find ( itr -> second );
while ( nextItr != chains . end ())
2011-07-26 23:09:28 +02:00
{
2020-06-12 01:29:18 +02:00
SpellInfo const * prev = AssertSpellInfo ( nextItr -> first , DIFFICULTY_NONE ); // already checked in previous iteration (or above, in case this is the first one)
SpellInfo const * last = AssertSpellInfo ( nextItr -> second , DIFFICULTY_NONE );
2013-07-21 18:56:25 +02:00
2016-06-15 16:20:43 +02:00
mSpellChains [ nextItr -> first ]. next = last ;
2013-07-21 18:56:25 +02:00
2016-06-15 16:20:43 +02:00
mSpellChains [ nextItr -> second ]. first = first ;
mSpellChains [ nextItr -> second ]. prev = prev ;
mSpellChains [ nextItr -> second ]. next = nullptr ;
mSpellChains [ nextItr -> second ]. last = last ;
mSpellChains [ nextItr -> second ]. rank = rank ++ ;
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & difficultyInfo : _GetSpellInfo ( nextItr -> second ))
const_cast < SpellInfo &> ( difficultyInfo ). ChainEntry = & mSpellChains [ nextItr -> second ];
2012-11-16 09:51:44 +01:00
2016-06-15 16:20:43 +02:00
// fill 'last'
do
2011-07-26 23:09:28 +02:00
{
2016-06-15 16:20:43 +02:00
mSpellChains [ prev -> Id ]. last = last ;
prev = mSpellChains [ prev -> Id ]. prev ;
} while ( prev );
nextItr = chains . find ( nextItr -> second );
2011-07-26 23:09:28 +02:00
}
2013-07-21 18:56:25 +02:00
}
2010-01-23 17:05:20 +01:00
2016-06-15 16:20:43 +02:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell rank records in %u ms" , uint32 ( mSpellChains . size ()), GetMSTimeDiffToNow ( oldMSTime ));
2010-01-23 17:05:20 +01:00
}
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellRequired ()
2008-11-17 22:07:21 -06:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2011-07-26 23:09:28 +02:00
mSpellsReqSpell . clear (); // need for reload case
mSpellReq . clear (); // need for reload case
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
// 0 1
2011-07-26 23:09:28 +02:00
QueryResult result = WorldDatabase . Query ( "SELECT spell_id, req_spell from spell_required" );
2009-10-17 15:51:44 -07:00
2010-01-01 14:19:29 -06:00
if ( ! result )
2009-07-30 11:37:08 +08:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell required records. DB table `spell_required` is empty." );
2012-08-03 15:54:54 +02:00
2009-07-30 11:37:08 +08:00
return ;
}
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
uint32 count = 0 ;
2009-07-30 11:37:08 +08:00
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
uint32 spell_id = fields [ 0 ]. GetUInt32 ();
2011-07-26 23:09:28 +02:00
uint32 spell_req = fields [ 1 ]. GetUInt32 ();
2012-05-30 08:01:02 +02:00
2011-07-26 23:09:28 +02:00
// check if chain is made with valid first spell
2020-06-12 01:29:18 +02:00
SpellInfo const * spell = GetSpellInfo ( spell_id , DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spell )
2009-07-30 11:37:08 +08:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "spell_id %u in `spell_required` table could not be found in dbc, skipped." , spell_id );
2011-07-26 23:09:28 +02:00
continue ;
}
2012-05-30 08:01:02 +02:00
2020-06-12 01:29:18 +02:00
SpellInfo const * reqSpell = GetSpellInfo ( spell_req , DIFFICULTY_NONE );
2013-07-24 15:18:42 +02:00
if ( ! reqSpell )
2011-07-26 23:09:28 +02:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "req_spell %u in `spell_required` table could not be found in dbc, skipped." , spell_req );
2011-07-26 23:09:28 +02:00
continue ;
}
2012-05-30 08:01:02 +02:00
2013-07-24 15:18:42 +02:00
if ( spell -> IsRankOf ( reqSpell ))
2011-07-26 23:09:28 +02:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "req_spell %u and spell_id %u in `spell_required` table are ranks of the same spell, entry not needed, skipped." , spell_req , spell_id );
2011-07-26 23:09:28 +02:00
continue ;
}
2012-05-30 08:01:02 +02:00
2011-07-26 23:09:28 +02:00
if ( IsSpellRequiringSpell ( spell_id , spell_req ))
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "Duplicate entry of req_spell %u and spell_id %u in `spell_required`, skipped." , spell_req , spell_id );
2009-07-30 11:37:08 +08:00
continue ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mSpellReq . insert ( std :: pair < uint32 , uint32 > ( spell_id , spell_req ));
mSpellsReqSpell . insert ( std :: pair < uint32 , uint32 > ( spell_req , spell_id ));
2012-05-30 08:01:02 +02:00
++ count ;
2010-01-01 14:19:29 -06:00
} while ( result -> NextRow ());
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell required records in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2012-08-03 15:54:54 +02:00
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellLearnSkills ()
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mSpellLearnSkills . clear (); // need for reload case
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
// search auto-learned skills and add its to map also for use in unlearn spells/talents
uint32 dbc_count = 0 ;
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & entry : mSpellInfoMap )
2011-07-26 23:09:28 +02:00
{
2020-06-12 01:29:18 +02:00
if ( entry . Difficulty != DIFFICULTY_NONE )
2011-07-26 23:09:28 +02:00
continue ;
2009-10-17 15:51:44 -07:00
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : entry . GetEffects ())
2009-01-25 16:35:14 -06:00
{
2016-10-02 15:49:29 -03:00
SpellLearnSkillNode dbc_node ;
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . Effect )
2011-07-26 23:09:28 +02:00
{
2016-10-02 15:49:29 -03:00
case SPELL_EFFECT_SKILL :
2021-09-04 15:13:15 +02:00
dbc_node . skill = uint16 ( spellEffectInfo . MiscValue );
dbc_node . step = uint16 ( spellEffectInfo . CalcValue ());
2016-10-02 15:49:29 -03:00
if ( dbc_node . skill != SKILL_RIDING )
dbc_node . value = 1 ;
else
dbc_node . value = dbc_node . step * 75 ;
dbc_node . maxvalue = dbc_node . step * 75 ;
break ;
case SPELL_EFFECT_DUAL_WIELD :
dbc_node . skill = SKILL_DUAL_WIELD ;
dbc_node . step = 1 ;
2011-07-26 23:09:28 +02:00
dbc_node . value = 1 ;
2016-10-02 15:49:29 -03:00
dbc_node . maxvalue = 1 ;
break ;
default :
continue ;
2011-07-26 23:09:28 +02:00
}
2016-10-02 15:49:29 -03:00
2020-06-12 01:29:18 +02:00
mSpellLearnSkills [ entry . Id ] = dbc_node ;
2016-10-02 15:49:29 -03:00
++ dbc_count ;
break ;
2009-01-25 16:35:14 -06:00
}
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u Spell Learn Skills from DBC in %u ms" , dbc_count , GetMSTimeDiffToNow ( oldMSTime ));
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellLearnSpells ()
2009-01-16 15:53:34 -06:00
{
2011-07-26 23:09:28 +02:00
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mSpellLearnSpells . clear (); // need for reload case
2009-10-17 15:51:44 -07:00
2010-01-01 14:19:29 -06:00
// 0 1 2
2010-09-11 21:10:54 +02:00
QueryResult result = WorldDatabase . Query ( "SELECT entry, SpellID, Active FROM spell_learn_spell" );
2010-01-01 14:19:29 -06:00
if ( ! result )
2008-11-17 22:07:21 -06:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell learn spells. DB table `spell_learn_spell` is empty." );
2008-11-17 22:07:21 -06:00
return ;
}
2009-10-17 15:51:44 -07:00
2009-05-15 16:26:00 -05:00
uint32 count = 0 ;
2008-11-17 22:07:21 -06:00
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2012-11-12 10:29:52 +01:00
uint32 spell_id = fields [ 0 ]. GetUInt32 ();
2009-10-17 15:51:44 -07:00
2009-05-15 16:26:00 -05:00
SpellLearnSpellNode node ;
2015-02-08 16:23:31 +01:00
node . Spell = fields [ 1 ]. GetUInt32 ();
node . OverridesSpell = 0 ;
node . Active = fields [ 2 ]. GetBool ();
node . AutoLearned = false ;
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( spell_id , DIFFICULTY_NONE );
2015-01-30 23:58:16 +01:00
if ( ! spellInfo )
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_learn_spell` does not exist." , spell_id );
2009-05-15 16:26:00 -05:00
continue ;
}
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
if ( ! GetSpellInfo ( node . Spell , DIFFICULTY_NONE ))
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_learn_spell` learning non-existing spell %u." , spell_id , node . Spell );
2009-07-01 18:07:57 -05:00
continue ;
}
2009-10-17 15:51:44 -07:00
2015-01-30 23:58:16 +01:00
if ( spellInfo -> HasAttribute ( SPELL_ATTR0_CU_IS_TALENT ))
2009-07-01 18:07:57 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_learn_spell` attempts learning talent spell %u, skipped." , spell_id , node . Spell );
2009-05-15 16:26:00 -05:00
continue ;
}
2009-10-17 15:51:44 -07:00
2011-04-29 20:47:02 +02:00
mSpellLearnSpells . insert ( SpellLearnSpellMap :: value_type ( spell_id , node ));
2009-10-17 15:51:44 -07:00
2009-05-15 16:26:00 -05:00
++ count ;
2010-01-01 14:19:29 -06:00
} while ( result -> NextRow ());
2009-10-17 15:51:44 -07:00
2013-05-12 17:56:37 +02:00
// copy state loaded from db
SpellLearnSpellMap dbSpellLearnSpells = mSpellLearnSpells ;
2009-05-15 16:26:00 -05:00
// search auto-learned spells and add its to map also for use in unlearn spells/talents
uint32 dbc_count = 0 ;
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & entry : mSpellInfoMap )
2009-05-15 16:26:00 -05:00
{
2020-06-12 01:29:18 +02:00
if ( entry . Difficulty != DIFFICULTY_NONE )
2009-05-15 16:26:00 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : entry . GetEffects ())
2009-05-15 16:26:00 -05:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsEffect ( SPELL_EFFECT_LEARN_SPELL ))
2009-05-15 16:26:00 -05:00
{
SpellLearnSpellNode dbc_node ;
2021-09-04 15:13:15 +02:00
dbc_node . Spell = spellEffectInfo . TriggerSpell ;
2015-02-08 16:23:31 +01:00
dbc_node . Active = true ; // all dbc based learned spells is active (show in spell book or hide by client itself)
dbc_node . OverridesSpell = 0 ;
2009-10-17 15:51:44 -07:00
2009-05-15 16:26:00 -05:00
// ignore learning not existed spells (broken/outdated/or generic learnig spell 483
2020-06-12 01:29:18 +02:00
if ( ! GetSpellInfo ( dbc_node . Spell , DIFFICULTY_NONE ))
2009-05-15 16:26:00 -05:00
continue ;
2009-10-17 15:51:44 -07:00
2013-12-25 14:16:55 -03:30
// talent or passive spells or skill-step spells auto-cast and not need dependent learning,
// pet teaching spells must not be dependent learning (cast)
2009-05-15 16:26:00 -05:00
// other required explicit dependent learning
2021-09-04 15:13:15 +02:00
dbc_node . AutoLearned = spellEffectInfo . TargetA . GetTarget () == TARGET_UNIT_PET || entry . HasAttribute ( SPELL_ATTR0_CU_IS_TALENT ) || entry . IsPassive () || entry . HasEffect ( SPELL_EFFECT_SKILL_STEP );
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
SpellLearnSpellMapBounds db_node_bounds = dbSpellLearnSpells . equal_range ( entry . Id );
2009-10-17 15:51:44 -07:00
2009-05-15 16:26:00 -05:00
bool found = false ;
2009-10-17 16:20:24 -07:00
for ( SpellLearnSpellMap :: const_iterator itr = db_node_bounds . first ; itr != db_node_bounds . second ; ++ itr )
2009-05-15 16:26:00 -05:00
{
2015-02-08 16:23:31 +01:00
if ( itr -> second . Spell == dbc_node . Spell )
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u is an auto-learn spell %u in spell.dbc and the record in `spell_learn_spell` is redundant. Please update your DB." ,
2020-06-12 01:29:18 +02:00
entry . Id , dbc_node . Spell );
2009-05-15 16:26:00 -05:00
found = true ;
break ;
}
}
2009-10-17 15:51:44 -07:00
2010-01-01 14:19:29 -06:00
if ( ! found ) // add new spell-spell pair if not found
2009-05-15 16:26:00 -05:00
{
2020-06-12 01:29:18 +02:00
mSpellLearnSpells . insert ( SpellLearnSpellMap :: value_type ( entry . Id , dbc_node ));
2009-05-15 16:26:00 -05:00
++ dbc_count ;
}
}
}
2008-12-22 02:11:11 +01:00
}
2009-10-17 15:51:44 -07:00
2015-02-22 12:44:24 +01:00
for ( SpellLearnSpellEntry const * spellLearnSpell : sSpellLearnSpellStore )
2015-02-08 16:23:31 +01:00
{
2021-02-24 21:20:14 +02:00
if ( ! GetSpellInfo ( spellLearnSpell -> SpellID , DIFFICULTY_NONE ) ||
! GetSpellInfo ( spellLearnSpell -> LearnSpellID , DIFFICULTY_NONE ))
2015-02-08 16:23:31 +01:00
continue ;
2021-02-24 21:20:14 +02:00
SpellLearnSpellMapBounds db_node_bounds = dbSpellLearnSpells . equal_range ( spellLearnSpell -> SpellID );
2015-02-08 16:23:31 +01:00
bool found = false ;
for ( SpellLearnSpellMap :: const_iterator itr = db_node_bounds . first ; itr != db_node_bounds . second ; ++ itr )
{
2021-02-24 21:20:14 +02:00
if ( int32 ( itr -> second . Spell ) == spellLearnSpell -> LearnSpellID )
2015-02-08 16:23:31 +01:00
{
2021-02-24 21:20:14 +02:00
TC_LOG_ERROR ( "sql.sql" , "Found redundant record (entry: %u, SpellID: %u) in `spell_learn_spell`, spell added automatically from SpellLearnSpell.db2" , spellLearnSpell -> SpellID , spellLearnSpell -> LearnSpellID );
2015-02-08 16:23:31 +01:00
found = true ;
break ;
}
}
if ( found )
continue ;
// Check if it is already found in Spell.dbc, ignore silently if yes
2021-02-24 21:20:14 +02:00
SpellLearnSpellMapBounds dbc_node_bounds = GetSpellLearnSpellMapBounds ( spellLearnSpell -> SpellID );
2015-02-08 16:23:31 +01:00
found = false ;
for ( SpellLearnSpellMap :: const_iterator itr = dbc_node_bounds . first ; itr != dbc_node_bounds . second ; ++ itr )
{
2021-02-24 21:20:14 +02:00
if ( int32 ( itr -> second . Spell ) == spellLearnSpell -> LearnSpellID )
2015-02-08 16:23:31 +01:00
{
found = true ;
break ;
}
}
if ( found )
continue ;
SpellLearnSpellNode dbcLearnNode ;
2021-02-24 21:20:14 +02:00
dbcLearnNode . Spell = spellLearnSpell -> LearnSpellID ;
2015-02-08 16:23:31 +01:00
dbcLearnNode . OverridesSpell = spellLearnSpell -> OverridesSpellID ;
dbcLearnNode . Active = true ;
dbcLearnNode . AutoLearned = false ;
2021-02-24 21:20:14 +02:00
mSpellLearnSpells . insert ( SpellLearnSpellMap :: value_type ( spellLearnSpell -> SpellID , dbcLearnNode ));
2015-02-08 16:23:31 +01:00
++ dbc_count ;
}
2016-04-29 20:04:01 +02:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell learn spells, %u found in Spell.dbc in %u ms" , count , dbc_count , GetMSTimeDiffToNow ( oldMSTime ));
2009-05-15 16:26:00 -05:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellTargetPositions ()
2009-05-15 16:26:00 -05:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mSpellTargetPositions . clear (); // need for reload case
2009-10-17 15:51:44 -07:00
2014-11-30 03:45:48 -05:00
// 0 1 2 3 4 5
2014-11-21 17:29:08 -05:00
QueryResult result = WorldDatabase . Query ( "SELECT ID, EffectIndex, MapID, PositionX, PositionY, PositionZ FROM spell_target_position" );
2009-11-19 03:53:49 -08:00
if ( ! result )
2009-05-15 16:26:00 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell target coordinates. DB table `spell_target_position` is empty." );
2009-05-15 16:26:00 -05:00
return ;
}
2009-10-17 15:51:44 -07:00
2010-12-19 17:06:33 +01:00
uint32 count = 0 ;
2009-05-15 16:26:00 -05:00
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2015-01-30 20:34:40 +01:00
uint32 spellId = fields [ 0 ]. GetUInt32 ();
2013-05-13 11:30:48 +03:00
SpellEffIndex effIndex = SpellEffIndex ( fields [ 1 ]. GetUInt8 ());
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellTargetPosition st ;
2013-05-13 11:30:48 +03:00
st . target_mapId = fields [ 2 ]. GetUInt16 ();
st . target_X = fields [ 3 ]. GetFloat ();
st . target_Y = fields [ 4 ]. GetFloat ();
st . target_Z = fields [ 5 ]. GetFloat ();
2011-07-26 23:09:28 +02:00
MapEntry const * mapEntry = sMapStore . LookupEntry ( st . target_mapId );
if ( ! mapEntry )
2008-12-22 02:11:11 +01:00
{
2021-10-13 11:49:30 +02:00
TC_LOG_ERROR ( "sql.sql" , "Spell (Id: %u, EffectIndex: %u) is using a non-existant MapID (ID: %u)." , spellId , uint32 ( effIndex ), st . target_mapId );
2011-07-26 23:09:28 +02:00
continue ;
}
2009-10-17 15:51:44 -07:00
2014-11-21 17:29:08 -05:00
if ( st . target_X == 0 && st . target_Y == 0 && st . target_Z == 0 )
2011-07-26 23:09:28 +02:00
{
2021-10-13 11:49:30 +02:00
TC_LOG_ERROR ( "sql.sql" , "Spell (Id: %u, EffectIndex: %u): target coordinates not provided." , spellId , uint32 ( effIndex ));
2011-07-26 23:09:28 +02:00
continue ;
}
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( spellId , DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spellInfo )
{
2015-01-30 20:34:40 +01:00
TC_LOG_ERROR ( "sql.sql" , "Spell (Id: %u) listed in `spell_target_position` does not exist." , spellId );
2011-07-26 23:09:28 +02:00
continue ;
}
2021-09-04 15:13:15 +02:00
if ( effIndex >= spellInfo -> GetEffects (). size ())
2014-11-24 18:57:49 +01:00
{
2021-10-13 11:49:30 +02:00
TC_LOG_ERROR ( "sql.sql" , "Spell (Id: %u, EffectIndex: %u) listed in `spell_target_position` does not have an effect at index %u." , spellId , uint32 ( effIndex ), uint32 ( effIndex ));
2014-11-24 18:57:49 +01:00
continue ;
}
2014-11-21 17:29:08 -05:00
// target facing is in degrees for 6484 & 9268... (blizz sucks)
2021-09-04 15:13:15 +02:00
if ( spellInfo -> GetEffect ( effIndex ). PositionFacing > 2 * M_PI )
st . target_Orientation = spellInfo -> GetEffect ( effIndex ). PositionFacing * float ( M_PI ) / 180 ;
2014-11-21 17:29:08 -05:00
else
2021-09-04 15:13:15 +02:00
st . target_Orientation = spellInfo -> GetEffect ( effIndex ). PositionFacing ;
2014-11-21 17:29:08 -05:00
2021-09-04 15:13:15 +02:00
if ( spellInfo -> GetEffect ( effIndex ). TargetA . GetTarget () == TARGET_DEST_DB || spellInfo -> GetEffect ( effIndex ). TargetB . GetTarget () == TARGET_DEST_DB )
2014-11-29 14:52:53 +01:00
{
2015-01-30 20:34:40 +01:00
std :: pair < uint32 , SpellEffIndex > key = std :: make_pair ( spellId , effIndex );
2013-05-13 11:30:48 +03:00
mSpellTargetPositions [ key ] = st ;
++ count ;
2011-07-26 23:09:28 +02:00
}
2013-05-13 11:30:48 +03:00
else
2011-07-26 23:09:28 +02:00
{
2021-10-13 11:49:30 +02:00
TC_LOG_ERROR ( "sql.sql" , "Spell (Id: %u, effIndex: %u) listed in `spell_target_position` does not have a target TARGET_DEST_DB (17)." , spellId , uint32 ( effIndex ));
2011-07-26 23:09:28 +02:00
continue ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2009-11-19 03:53:49 -08:00
} while ( result -> NextRow ());
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
/*
// Check all spells
2015-01-30 20:34:40 +01:00
for (uint32 i = 1; i < GetSpellInfoStoreSize(); ++i)
2011-07-26 23:09:28 +02:00
{
SpellInfo const* spellInfo = GetSpellInfo(i);
if (!spellInfo)
continue;
2015-01-30 20:34:40 +01:00
for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
2011-07-26 23:09:28 +02:00
{
2015-01-30 20:34:40 +01:00
SpellEffectInfo const* effect = spellInfo->GetEffect(j);
if (!effect)
continue;
if (effect->TargetA.GetTarget() != TARGET_DEST_DB && effect->TargetB.GetTarget() != TARGET_DEST_DB)
continue;
if (!GetSpellTargetPosition(i, SpellEffIndex(j)))
TC_LOG_DEBUG("spells", "Spell (Id: %u, EffectIndex: %u) does not have record in `spell_target_position`.", i, j);
2011-07-26 23:09:28 +02:00
}
2015-01-30 20:34:40 +01:00
}
*/
2011-07-26 23:09:28 +02:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell teleport coordinates in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellGroups ()
2008-11-17 22:07:21 -06:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2011-07-26 23:09:28 +02:00
mSpellSpellGroup . clear (); // need for reload case
mSpellGroupSpell . clear ();
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
// 0 1
2011-07-26 23:09:28 +02:00
QueryResult result = WorldDatabase . Query ( "SELECT id, spell_id FROM spell_group" );
if ( ! result )
2008-11-17 22:07:21 -06:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell group definitions. DB table `spell_group` is empty." );
2011-07-26 23:09:28 +02:00
return ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
std :: set < uint32 > groups ;
2012-05-30 08:01:02 +02:00
uint32 count = 0 ;
2011-07-26 23:09:28 +02:00
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 group_id = fields [ 0 ]. GetUInt32 ();
if ( group_id <= SPELL_GROUP_DB_RANGE_MIN && group_id >= SPELL_GROUP_CORE_RANGE_MAX )
{
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "sql.sql" , "SpellGroup id %u listed in `spell_group` is in core range, but is not defined in core!" , group_id );
2011-07-26 23:09:28 +02:00
continue ;
2008-11-17 22:07:21 -06:00
}
2011-07-26 23:09:28 +02:00
int32 spell_id = fields [ 1 ]. GetInt32 ();
2009-10-17 15:51:44 -07:00
2017-04-14 03:47:23 -03:00
groups . insert ( group_id );
mSpellGroupSpell . emplace ( SpellGroup ( group_id ), spell_id );
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
} while ( result -> NextRow ());
2017-04-14 03:47:23 -03:00
for ( auto itr = mSpellGroupSpell . begin (); itr != mSpellGroupSpell . end ();)
2009-05-24 12:16:12 -05:00
{
2011-07-26 23:09:28 +02:00
if ( itr -> second < 0 )
2009-05-24 12:16:12 -05:00
{
2011-07-26 23:09:28 +02:00
if ( groups . find ( abs ( itr -> second )) == groups . end ())
{
2013-11-08 10:50:51 +01:00
TC_LOG_ERROR ( "sql.sql" , "SpellGroup id %u listed in `spell_group` does not exist" , abs ( itr -> second ));
2017-04-14 03:47:23 -03:00
itr = mSpellGroupSpell . erase ( itr );
2011-07-26 23:09:28 +02:00
}
else
++ itr ;
2009-05-24 12:16:12 -05:00
}
2011-07-26 23:09:28 +02:00
else
2009-05-24 12:16:12 -05:00
{
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( itr -> second , DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spellInfo )
2009-05-24 12:16:12 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_group` does not exist" , itr -> second );
2017-04-14 03:47:23 -03:00
itr = mSpellGroupSpell . erase ( itr );
2009-05-24 12:16:12 -05:00
}
2011-07-26 23:09:28 +02:00
else if ( spellInfo -> GetRank () > 1 )
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_group` is not the first rank of the spell." , itr -> second );
2017-04-14 03:47:23 -03:00
itr = mSpellGroupSpell . erase ( itr );
2011-07-26 23:09:28 +02:00
}
else
++ itr ;
2009-05-24 12:16:12 -05:00
}
}
2009-10-17 15:51:44 -07:00
2017-04-14 03:47:23 -03:00
for ( auto groupItr = groups . begin (); groupItr != groups . end (); ++ groupItr )
2009-05-24 12:16:12 -05:00
{
2011-07-26 23:09:28 +02:00
std :: set < uint32 > spells ;
GetSetOfSpellsInSpellGroup ( SpellGroup ( * groupItr ), spells );
2017-04-14 03:47:23 -03:00
for ( auto spellItr = spells . begin (); spellItr != spells . end (); ++ spellItr )
2009-05-24 12:16:12 -05:00
{
2011-07-26 23:09:28 +02:00
++ count ;
2017-04-14 03:47:23 -03:00
mSpellSpellGroup . emplace ( * spellItr , SpellGroup ( * groupItr ));
2009-05-24 12:16:12 -05:00
}
}
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell group definitions in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2009-05-24 12:16:12 -05:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellGroupStackRules ()
2009-05-24 12:16:12 -05:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2011-07-26 23:09:28 +02:00
mSpellGroupStack . clear (); // need for reload case
2017-04-14 03:47:23 -03:00
mSpellSameEffectStack . clear ();
std :: vector < uint32 > sameEffectGroups ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
// 0 1
QueryResult result = WorldDatabase . Query ( "SELECT group_id, stack_rule FROM spell_group_stack_rules" );
if ( ! result )
2009-05-24 12:16:12 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell group stack rules. DB table `spell_group_stack_rules` is empty." );
2011-07-26 23:09:28 +02:00
return ;
}
2010-01-01 14:19:29 -06:00
2012-05-30 08:01:02 +02:00
uint32 count = 0 ;
2011-07-26 23:09:28 +02:00
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 group_id = fields [ 0 ]. GetUInt32 ();
2012-05-30 08:01:02 +02:00
uint8 stack_rule = fields [ 1 ]. GetInt8 ();
2011-07-26 23:09:28 +02:00
if ( stack_rule >= SPELL_GROUP_STACK_RULE_MAX )
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "SpellGroupStackRule %u listed in `spell_group_stack_rules` does not exist." , stack_rule );
2009-05-24 12:16:12 -05:00
continue ;
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2017-04-14 03:47:23 -03:00
auto bounds = GetSpellGroupSpellMapBounds (( SpellGroup ) group_id );
if ( bounds . first == bounds . second )
2009-05-24 12:16:12 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "SpellGroup id %u listed in `spell_group_stack_rules` does not exist." , group_id );
2011-07-26 23:09:28 +02:00
continue ;
2009-05-24 12:16:12 -05:00
}
2009-10-17 15:51:44 -07:00
2017-04-14 16:52:13 -03:00
mSpellGroupStack . emplace ( SpellGroup ( group_id ), SpellGroupStackRule ( stack_rule ));
2017-04-14 03:47:23 -03:00
// different container for same effect stack rules, need to check effect types
2017-04-14 16:52:13 -03:00
if ( stack_rule == SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT )
2017-04-14 03:47:23 -03:00
sameEffectGroups . push_back ( group_id );
2011-07-26 23:09:28 +02:00
++ count ;
} while ( result -> NextRow ());
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell group stack rules in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2017-04-14 03:47:23 -03:00
count = 0 ;
oldMSTime = getMSTime ();
TC_LOG_INFO ( "server.loading" , ">> Parsing SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT stack rules..." );
for ( uint32 group_id : sameEffectGroups )
{
std :: set < uint32 > spellIds ;
GetSetOfSpellsInSpellGroup ( SpellGroup ( group_id ), spellIds );
std :: unordered_set < uint32 > auraTypes ;
// we have to 'guess' what effect this group corresponds to
{
std :: unordered_multiset < uint32 /*auraName*/ > frequencyContainer ;
// only waylay for the moment (shared group)
std :: vector < std :: vector < uint32 /*auraName*/ >> const SubGroups =
{
{ SPELL_AURA_MOD_MELEE_HASTE , SPELL_AURA_MOD_MELEE_RANGED_HASTE , SPELL_AURA_MOD_RANGED_HASTE }
};
for ( uint32 spellId : spellIds )
{
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = AssertSpellInfo ( spellId , DIFFICULTY_NONE );
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo -> GetEffects ())
2017-04-14 03:47:23 -03:00
{
2021-09-04 15:13:15 +02:00
if ( ! spellEffectInfo . IsAura ())
2017-04-14 03:47:23 -03:00
continue ;
2021-09-04 15:13:15 +02:00
uint32 auraName = spellEffectInfo . ApplyAuraName ;
2017-04-14 03:47:23 -03:00
for ( std :: vector < uint32 > const & subGroup : SubGroups )
{
if ( std :: find ( subGroup . begin (), subGroup . end (), auraName ) != subGroup . end ())
{
// count as first aura
auraName = subGroup . front ();
break ;
}
}
frequencyContainer . insert ( auraName );
}
}
uint32 auraType = 0 ;
size_t auraTypeCount = 0 ;
for ( uint32 auraName : frequencyContainer )
{
size_t currentCount = frequencyContainer . count ( auraName );
if ( currentCount > auraTypeCount )
{
auraType = auraName ;
auraTypeCount = currentCount ;
}
}
for ( std :: vector < uint32 > const & subGroup : SubGroups )
{
if ( auraType == subGroup . front ())
{
auraTypes . insert ( subGroup . begin (), subGroup . end ());
break ;
}
}
if ( auraTypes . empty ())
auraTypes . insert ( auraType );
}
// re-check spells against guessed group
for ( uint32 spellId : spellIds )
{
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = AssertSpellInfo ( spellId , DIFFICULTY_NONE );
2017-04-14 03:47:23 -03:00
bool found = false ;
while ( spellInfo )
{
for ( uint32 auraType : auraTypes )
{
2020-06-12 01:29:18 +02:00
if ( spellInfo -> HasAura ( AuraType ( auraType )))
2017-04-14 03:47:23 -03:00
{
found = true ;
break ;
}
}
if ( found )
break ;
spellInfo = spellInfo -> GetNextRankSpell ();
}
// not found either, log error
if ( ! found )
TC_LOG_ERROR ( "sql.sql" , "SpellId %u listed in `spell_group` with stack rule 3 does not share aura assigned for group %u" , spellId , group_id );
}
mSpellSameEffectStack [ SpellGroup ( group_id )] = auraTypes ;
++ count ;
}
TC_LOG_INFO ( "server.loading" , ">> Parsed %u SPELL_GROUP_STACK_RULE_EXCLUSIVE_SAME_EFFECT stack rules in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2011-07-26 23:09:28 +02:00
}
2010-12-19 17:06:33 +01:00
2017-08-13 12:03:43 +02:00
void SpellMgr :: LoadSpellProcs ()
{
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2017-08-13 12:03:43 +02:00
mSpellProcMap . clear (); // need for reload case
2011-07-26 23:09:28 +02:00
2017-08-13 12:03:43 +02:00
// 0 1 2 3 4 5 6
QueryResult result = WorldDatabase . Query ( "SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, SpellFamilyMask3, "
2016-11-18 02:15:45 -03:00
// 7 8 9 10 11 12 13 14 15 16
"ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, DisableEffectsMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc" );
2013-07-21 15:12:34 +02:00
2017-08-13 12:03:43 +02:00
uint32 count = 0 ;
if ( result )
{
do
2009-05-24 12:16:12 -05:00
{
2017-08-13 12:03:43 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2017-08-13 12:03:43 +02:00
int32 spellId = fields [ 0 ]. GetInt32 ();
2009-10-17 15:51:44 -07:00
2017-08-13 12:03:43 +02:00
bool allRanks = false ;
if ( spellId < 0 )
2013-07-21 15:12:34 +02:00
{
2017-08-13 12:03:43 +02:00
allRanks = true ;
spellId = - spellId ;
2013-07-21 15:12:34 +02:00
}
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( spellId , DIFFICULTY_NONE );
2017-08-13 12:03:43 +02:00
if ( ! spellInfo )
2011-07-26 23:09:28 +02:00
{
2017-08-13 12:03:43 +02:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_proc` does not exist" , spellId );
continue ;
2009-05-24 12:16:12 -05:00
}
2013-07-21 15:12:34 +02:00
2017-08-13 12:03:43 +02:00
if ( allRanks )
{
if ( ! spellInfo -> IsRanked ())
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks." , spellId );
2013-07-21 15:12:34 +02:00
2017-08-13 12:03:43 +02:00
if ( spellInfo -> GetFirstRankSpell () -> Id != uint32 ( spellId ))
{
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_proc` is not the first rank of the spell." , spellId );
continue ;
}
}
2013-07-21 15:12:34 +02:00
2017-08-13 12:03:43 +02:00
SpellProcEntry baseProcEntry ;
baseProcEntry . SchoolMask = fields [ 1 ]. GetInt8 ();
baseProcEntry . SpellFamilyName = fields [ 2 ]. GetUInt16 ();
baseProcEntry . SpellFamilyMask [ 0 ] = fields [ 3 ]. GetUInt32 ();
baseProcEntry . SpellFamilyMask [ 1 ] = fields [ 4 ]. GetUInt32 ();
baseProcEntry . SpellFamilyMask [ 2 ] = fields [ 5 ]. GetUInt32 ();
baseProcEntry . SpellFamilyMask [ 3 ] = fields [ 6 ]. GetUInt32 ();
baseProcEntry . ProcFlags = fields [ 7 ]. GetUInt32 ();
baseProcEntry . SpellTypeMask = fields [ 8 ]. GetUInt32 ();
baseProcEntry . SpellPhaseMask = fields [ 9 ]. GetUInt32 ();
baseProcEntry . HitMask = fields [ 10 ]. GetUInt32 ();
baseProcEntry . AttributesMask = fields [ 11 ]. GetUInt32 ();
2016-11-18 02:15:45 -03:00
baseProcEntry . DisableEffectsMask = fields [ 12 ]. GetUInt32 ();
baseProcEntry . ProcsPerMinute = fields [ 13 ]. GetFloat ();
baseProcEntry . Chance = fields [ 14 ]. GetFloat ();
baseProcEntry . Cooldown = Milliseconds ( fields [ 15 ]. GetUInt32 ());
baseProcEntry . Charges = fields [ 16 ]. GetUInt8 ();
2017-08-13 12:03:43 +02:00
while ( spellInfo )
{
2020-06-12 01:29:18 +02:00
if ( mSpellProcMap . find ({ spellInfo -> Id , spellInfo -> Difficulty }) != mSpellProcMap . end ())
2017-08-13 12:03:43 +02:00
{
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_proc` already has its first rank in the table." , spellInfo -> Id );
break ;
}
2009-10-17 15:51:44 -07:00
2017-08-13 12:03:43 +02:00
SpellProcEntry procEntry = SpellProcEntry ( baseProcEntry );
// take defaults from dbcs
if ( ! procEntry . ProcFlags )
procEntry . ProcFlags = spellInfo -> ProcFlags ;
if ( ! procEntry . Charges )
procEntry . Charges = spellInfo -> ProcCharges ;
if ( ! procEntry . Chance && ! procEntry . ProcsPerMinute )
procEntry . Chance = float ( spellInfo -> ProcChance );
2017-12-26 19:52:09 +01:00
if ( procEntry . Cooldown == Milliseconds :: zero ())
procEntry . Cooldown = Milliseconds ( spellInfo -> ProcCooldown );
2017-08-13 12:03:43 +02:00
// validate data
if ( procEntry . SchoolMask & ~ SPELL_SCHOOL_MASK_ALL )
TC_LOG_ERROR ( "sql.sql" , "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u" , spellInfo -> Id , procEntry . SchoolMask );
if ( procEntry . SpellFamilyName && ! DB2Manager :: IsValidSpellFamiliyName ( SpellFamilyNames ( procEntry . SpellFamilyName )))
TC_LOG_ERROR ( "sql.sql" , "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u" , spellInfo -> Id , procEntry . SpellFamilyName );
if ( procEntry . Chance < 0 )
{
TC_LOG_ERROR ( "sql.sql" , "`spell_proc` table entry for spellId %u has negative value in the `Chance` field" , spellInfo -> Id );
procEntry . Chance = 0 ;
}
if ( procEntry . ProcsPerMinute < 0 )
{
TC_LOG_ERROR ( "sql.sql" , "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field" , spellInfo -> Id );
procEntry . ProcsPerMinute = 0 ;
}
if ( ! procEntry . ProcFlags )
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered." , spellInfo -> Id );
if ( procEntry . SpellTypeMask & ~ PROC_SPELL_TYPE_MASK_ALL )
TC_LOG_ERROR ( "sql.sql" , "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u" , spellInfo -> Id , procEntry . SpellTypeMask );
2018-02-11 20:42:06 -03:00
if ( procEntry . SpellTypeMask && ! ( procEntry . ProcFlags & SPELL_PROC_FLAG_MASK ))
2017-08-13 12:03:43 +02:00
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value." , spellInfo -> Id );
if ( ! procEntry . SpellPhaseMask && procEntry . ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK )
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered." , spellInfo -> Id );
if ( procEntry . SpellPhaseMask & ~ PROC_SPELL_PHASE_MASK_ALL )
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u" , spellInfo -> Id , procEntry . SpellPhaseMask );
if ( procEntry . SpellPhaseMask && ! ( procEntry . ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK ))
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value." , spellInfo -> Id );
if ( procEntry . HitMask & ~ PROC_HIT_MASK_ALL )
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u" , spellInfo -> Id , procEntry . HitMask );
if ( procEntry . HitMask && ! ( procEntry . ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || ( procEntry . ProcFlags & DONE_HIT_PROC_FLAG_MASK && ( ! procEntry . SpellPhaseMask || procEntry . SpellPhaseMask & ( PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH )))))
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values." , spellInfo -> Id );
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo -> GetEffects ())
if (( procEntry . DisableEffectsMask & ( 1u << spellEffectInfo . EffectIndex )) && ! spellEffectInfo . IsAura ())
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u has DisableEffectsMask with effect %u, but effect %u is not an aura effect" , spellInfo -> Id , static_cast < uint32 > ( spellEffectInfo . EffectIndex ), static_cast < uint32 > ( spellEffectInfo . EffectIndex ));
2018-02-12 00:17:34 -03:00
if ( procEntry . AttributesMask & PROC_ATTR_REQ_SPELLMOD )
{
bool found = false ;
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo -> GetEffects ())
2018-02-12 00:17:34 -03:00
{
2021-09-04 15:13:15 +02:00
if ( ! spellEffectInfo . IsAura ())
2018-02-12 00:17:34 -03:00
continue ;
2021-10-16 22:05:02 +02:00
if ( spellEffectInfo . ApplyAuraName == SPELL_AURA_ADD_PCT_MODIFIER || spellEffectInfo . ApplyAuraName == SPELL_AURA_ADD_FLAT_MODIFIER
|| spellEffectInfo . ApplyAuraName == SPELL_AURA_ADD_PCT_MODIFIER_BY_SPELL_LABEL || spellEffectInfo . ApplyAuraName == SPELL_AURA_ADD_FLAT_MODIFIER_BY_SPELL_LABEL )
2018-02-12 00:17:34 -03:00
{
found = true ;
break ;
}
}
if ( ! found )
TC_LOG_ERROR ( "sql.sql" , "The `spell_proc` table entry for spellId %u has Attribute PROC_ATTR_REQ_SPELLMOD, but spell has no spell mods. Proc will not be triggered" , spellInfo -> Id );
}
2017-08-13 12:03:43 +02:00
2020-06-12 01:29:18 +02:00
mSpellProcMap [{ spellInfo -> Id , spellInfo -> Difficulty }] = procEntry ;
2017-08-13 12:03:43 +02:00
if ( allRanks )
spellInfo = spellInfo -> GetNextRankSpell ();
else
break ;
}
++ count ;
} while ( result -> NextRow ());
2013-07-21 15:12:34 +02:00
}
2017-08-13 12:03:43 +02:00
else
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty." );
2012-08-03 15:54:54 +02:00
2017-08-13 12:03:43 +02:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell proc conditions and data in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2009-10-17 15:51:44 -07:00
2016-10-17 02:19:29 -03:00
// Define can trigger auras
bool isTriggerAura [ TOTAL_AURAS ];
// Triggered always, even from triggered spells
bool isAlwaysTriggeredAura [ TOTAL_AURAS ];
// SpellTypeMask to add to the proc
uint32 spellTypeMask [ TOTAL_AURAS ];
// List of auras that CAN trigger but may not exist in spell_proc
// in most cases needed to drop charges
// some aura types need additional checks (eg SPELL_AURA_MECHANIC_IMMUNITY needs mechanic check)
// see AuraEffect::CheckEffectProc
for ( uint16 i = 0 ; i < TOTAL_AURAS ; ++ i )
{
isTriggerAura [ i ] = false ;
isAlwaysTriggeredAura [ i ] = false ;
spellTypeMask [ i ] = PROC_SPELL_TYPE_MASK_ALL ;
}
isTriggerAura [ SPELL_AURA_DUMMY ] = true ;
2021-03-15 23:53:30 +02:00
isTriggerAura [ SPELL_AURA_PERIODIC_DUMMY ] = true ;
2016-10-17 02:19:29 -03:00
isTriggerAura [ SPELL_AURA_MOD_CONFUSE ] = true ;
isTriggerAura [ SPELL_AURA_MOD_THREAT ] = true ;
isTriggerAura [ SPELL_AURA_MOD_STUN ] = true ; // Aura does not have charges but needs to be removed on trigger
isTriggerAura [ SPELL_AURA_MOD_DAMAGE_DONE ] = true ;
isTriggerAura [ SPELL_AURA_MOD_DAMAGE_TAKEN ] = true ;
isTriggerAura [ SPELL_AURA_MOD_RESISTANCE ] = true ;
isTriggerAura [ SPELL_AURA_MOD_STEALTH ] = true ;
isTriggerAura [ SPELL_AURA_MOD_FEAR ] = true ; // Aura does not have charges but needs to be removed on trigger
isTriggerAura [ SPELL_AURA_MOD_ROOT ] = true ;
isTriggerAura [ SPELL_AURA_TRANSFORM ] = true ;
isTriggerAura [ SPELL_AURA_REFLECT_SPELLS ] = true ;
isTriggerAura [ SPELL_AURA_DAMAGE_IMMUNITY ] = true ;
isTriggerAura [ SPELL_AURA_PROC_TRIGGER_SPELL ] = true ;
isTriggerAura [ SPELL_AURA_PROC_TRIGGER_DAMAGE ] = true ;
isTriggerAura [ SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK ] = true ;
isTriggerAura [ SPELL_AURA_SCHOOL_ABSORB ] = true ; // Savage Defense untested
isTriggerAura [ SPELL_AURA_MOD_POWER_COST_SCHOOL_PCT ] = true ;
isTriggerAura [ SPELL_AURA_MOD_POWER_COST_SCHOOL ] = true ;
isTriggerAura [ SPELL_AURA_REFLECT_SPELLS_SCHOOL ] = true ;
isTriggerAura [ SPELL_AURA_MECHANIC_IMMUNITY ] = true ;
isTriggerAura [ SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN ] = true ;
isTriggerAura [ SPELL_AURA_SPELL_MAGNET ] = true ;
isTriggerAura [ SPELL_AURA_MOD_ATTACK_POWER ] = true ;
isTriggerAura [ SPELL_AURA_MOD_POWER_REGEN_PERCENT ] = true ;
isTriggerAura [ SPELL_AURA_INTERCEPT_MELEE_RANGED_ATTACKS ] = true ;
isTriggerAura [ SPELL_AURA_OVERRIDE_CLASS_SCRIPTS ] = true ;
isTriggerAura [ SPELL_AURA_MOD_MECHANIC_RESISTANCE ] = true ;
isTriggerAura [ SPELL_AURA_RANGED_ATTACK_POWER_ATTACKER_BONUS ] = true ;
isTriggerAura [ SPELL_AURA_MOD_MELEE_HASTE ] = true ;
isTriggerAura [ SPELL_AURA_MOD_MELEE_HASTE_3 ] = true ;
isTriggerAura [ SPELL_AURA_MOD_ATTACKER_MELEE_HIT_CHANCE ] = true ;
isTriggerAura [ SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE ] = true ;
2021-01-03 01:41:23 +02:00
isTriggerAura [ SPELL_AURA_MOD_SCHOOL_MASK_DAMAGE_FROM_CASTER ] = true ;
2016-10-17 02:19:29 -03:00
isTriggerAura [ SPELL_AURA_MOD_SPELL_DAMAGE_FROM_CASTER ] = true ;
isTriggerAura [ SPELL_AURA_MOD_SPELL_CRIT_CHANCE ] = true ;
isTriggerAura [ SPELL_AURA_ABILITY_IGNORE_AURASTATE ] = true ;
2017-01-23 15:35:35 -03:00
isTriggerAura [ SPELL_AURA_MOD_INVISIBILITY ] = true ;
isTriggerAura [ SPELL_AURA_FORCE_REACTION ] = true ;
isTriggerAura [ SPELL_AURA_MOD_TAUNT ] = true ;
isTriggerAura [ SPELL_AURA_MOD_DETAUNT ] = true ;
isTriggerAura [ SPELL_AURA_MOD_DAMAGE_PERCENT_DONE ] = true ;
isTriggerAura [ SPELL_AURA_MOD_ATTACK_POWER_PCT ] = true ;
isTriggerAura [ SPELL_AURA_MOD_HIT_CHANCE ] = true ;
isTriggerAura [ SPELL_AURA_MOD_WEAPON_CRIT_PERCENT ] = true ;
isTriggerAura [ SPELL_AURA_MOD_BLOCK_PERCENT ] = true ;
2016-10-17 02:19:29 -03:00
isTriggerAura [ SPELL_AURA_MOD_ROOT_2 ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_OVERRIDE_CLASS_SCRIPTS ] = true ;
2016-10-30 00:16:10 -03:00
isAlwaysTriggeredAura [ SPELL_AURA_MOD_STEALTH ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_MOD_CONFUSE ] = true ;
2016-10-17 02:19:29 -03:00
isAlwaysTriggeredAura [ SPELL_AURA_MOD_FEAR ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_MOD_ROOT ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_MOD_STUN ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_TRANSFORM ] = true ;
2017-01-23 15:35:35 -03:00
isAlwaysTriggeredAura [ SPELL_AURA_MOD_INVISIBILITY ] = true ;
2016-10-17 02:19:29 -03:00
isAlwaysTriggeredAura [ SPELL_AURA_SPELL_MAGNET ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_SCHOOL_ABSORB ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_MOD_STEALTH ] = true ;
isAlwaysTriggeredAura [ SPELL_AURA_MOD_ROOT_2 ] = true ;
spellTypeMask [ SPELL_AURA_MOD_STEALTH ] = PROC_SPELL_TYPE_DAMAGE | PROC_SPELL_TYPE_NO_DMG_HEAL ;
spellTypeMask [ SPELL_AURA_MOD_CONFUSE ] = PROC_SPELL_TYPE_DAMAGE ;
spellTypeMask [ SPELL_AURA_MOD_FEAR ] = PROC_SPELL_TYPE_DAMAGE ;
spellTypeMask [ SPELL_AURA_MOD_ROOT ] = PROC_SPELL_TYPE_DAMAGE ;
spellTypeMask [ SPELL_AURA_MOD_ROOT_2 ] = PROC_SPELL_TYPE_DAMAGE ;
spellTypeMask [ SPELL_AURA_MOD_STUN ] = PROC_SPELL_TYPE_DAMAGE ;
spellTypeMask [ SPELL_AURA_TRANSFORM ] = PROC_SPELL_TYPE_DAMAGE ;
2017-01-23 15:35:35 -03:00
spellTypeMask [ SPELL_AURA_MOD_INVISIBILITY ] = PROC_SPELL_TYPE_DAMAGE ;
2016-10-17 02:19:29 -03:00
2017-08-13 12:03:43 +02:00
// This generates default procs to retain compatibility with previous proc system
TC_LOG_INFO ( "server.loading" , "Generating spell proc data from SpellMap..." );
count = 0 ;
oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
2017-08-13 12:03:43 +02:00
{
2016-11-07 23:50:18 -03:00
// Data already present in DB, overwrites default proc
2020-06-12 01:29:18 +02:00
if ( mSpellProcMap . find ({ spellInfo . Id , spellInfo . Difficulty }) != mSpellProcMap . end ())
2017-08-13 12:03:43 +02:00
continue ;
2016-05-11 20:38:24 +02:00
2016-11-07 23:50:18 -03:00
// Nothing to do if no flags set
2020-06-12 01:29:18 +02:00
if ( ! spellInfo . ProcFlags )
2016-11-07 23:50:18 -03:00
continue ;
2016-10-17 02:19:29 -03:00
bool addTriggerFlag = false ;
uint32 procSpellTypeMask = PROC_SPELL_TYPE_NONE ;
2021-08-18 23:17:54 +02:00
uint32 nonProcMask = 0 ;
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo . GetEffects ())
2017-08-13 12:03:43 +02:00
{
2021-09-04 15:13:15 +02:00
if ( ! spellEffectInfo . IsEffect ())
2017-08-13 12:03:43 +02:00
continue ;
2009-10-17 15:51:44 -07:00
2021-09-04 15:13:15 +02:00
uint32 auraName = spellEffectInfo . ApplyAuraName ;
2017-08-13 12:03:43 +02:00
if ( ! auraName )
continue ;
2010-02-14 19:13:14 -07:00
2017-08-13 12:03:43 +02:00
if ( ! isTriggerAura [ auraName ])
2021-08-18 23:17:54 +02:00
{
// explicitly disable non proccing auras to avoid losing charges on self proc
2021-09-04 15:13:15 +02:00
nonProcMask |= 1 << spellEffectInfo . EffectIndex ;
2017-08-13 12:03:43 +02:00
continue ;
2021-08-18 23:17:54 +02:00
}
2009-10-17 15:51:44 -07:00
2016-10-17 02:19:29 -03:00
procSpellTypeMask |= spellTypeMask [ auraName ];
if ( isAlwaysTriggeredAura [ auraName ])
2017-08-13 12:03:43 +02:00
addTriggerFlag = true ;
2016-11-07 23:50:18 -03:00
// many proc auras with taken procFlag mask don't have attribute "can proc with triggered"
// they should proc nevertheless (example mage armor spells with judgement)
2020-06-12 01:29:18 +02:00
if ( ! addTriggerFlag && ( spellInfo . ProcFlags & TAKEN_HIT_PROC_FLAG_MASK ) != 0 )
2016-11-07 23:50:18 -03:00
{
switch ( auraName )
{
case SPELL_AURA_PROC_TRIGGER_SPELL :
case SPELL_AURA_PROC_TRIGGER_DAMAGE :
addTriggerFlag = true ;
break ;
default :
break ;
}
}
2011-07-26 23:09:28 +02:00
}
2016-10-17 02:19:29 -03:00
if ( ! procSpellTypeMask )
2016-12-03 20:27:15 -03:00
{
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo . GetEffects ())
2016-12-03 20:27:15 -03:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsAura ())
2016-12-03 20:27:15 -03:00
{
2020-06-12 01:29:18 +02:00
TC_LOG_ERROR ( "sql.sql" , "Spell Id %u has DBC ProcFlags %u, but it's of non-proc aura type, it probably needs an entry in `spell_proc` table to be handled correctly." , spellInfo . Id , spellInfo . ProcFlags );
2016-12-03 20:27:15 -03:00
break ;
}
}
2017-08-13 12:03:43 +02:00
continue ;
2016-12-03 20:27:15 -03:00
}
2013-07-21 15:12:34 +02:00
2017-08-13 12:03:43 +02:00
SpellProcEntry procEntry ;
procEntry . SchoolMask = 0 ;
2020-06-12 01:29:18 +02:00
procEntry . ProcFlags = spellInfo . ProcFlags ;
2016-10-12 09:58:05 -03:00
procEntry . SpellFamilyName = 0 ;
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo . GetEffects ())
if ( spellEffectInfo . IsEffect () && isTriggerAura [ spellEffectInfo . ApplyAuraName ])
procEntry . SpellFamilyMask |= spellEffectInfo . SpellClassMask ;
2011-07-26 23:09:28 +02:00
2016-10-12 09:58:05 -03:00
if ( procEntry . SpellFamilyMask )
2020-06-12 01:29:18 +02:00
procEntry . SpellFamilyName = spellInfo . SpellFamilyName ;
2016-10-12 09:58:05 -03:00
2016-10-17 02:19:29 -03:00
procEntry . SpellTypeMask = procSpellTypeMask ;
2017-08-13 12:03:43 +02:00
procEntry . SpellPhaseMask = PROC_SPELL_PHASE_HIT ;
procEntry . HitMask = PROC_HIT_NONE ; // uses default proc @see SpellMgr::CanSpellTriggerProcOnEvent
2011-07-26 23:09:28 +02:00
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo . GetEffects ())
2011-07-26 23:09:28 +02:00
{
2021-09-04 15:13:15 +02:00
if ( ! spellEffectInfo . IsAura ())
2017-01-23 15:41:43 -03:00
continue ;
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . ApplyAuraName )
2009-05-15 16:26:00 -05:00
{
2017-01-23 15:41:43 -03:00
// Reflect auras should only proc off reflects
case SPELL_AURA_REFLECT_SPELLS :
case SPELL_AURA_REFLECT_SPELLS_SCHOOL :
procEntry . HitMask = PROC_HIT_REFLECT ;
break ;
// Only drop charge on crit
case SPELL_AURA_MOD_WEAPON_CRIT_PERCENT :
procEntry . HitMask = PROC_HIT_CRITICAL ;
break ;
// Only drop charge on block
case SPELL_AURA_MOD_BLOCK_PERCENT :
procEntry . HitMask = PROC_HIT_BLOCK ;
break ;
2018-03-16 12:34:42 -03:00
// proc auras with another aura reducing hit chance (eg 63767) only proc on missed attack
case SPELL_AURA_MOD_HIT_CHANCE :
if ( spellEffectInfo . CalcValue () <= - 100 )
procEntry . HitMask = PROC_HIT_MISS ;
break ;
2017-01-23 15:41:43 -03:00
default :
continue ;
2009-05-15 16:26:00 -05:00
}
2017-01-23 15:41:43 -03:00
break ;
2017-08-13 12:03:43 +02:00
}
2013-07-21 15:12:34 +02:00
2017-08-13 12:03:43 +02:00
procEntry . AttributesMask = 0 ;
2021-08-18 23:17:54 +02:00
procEntry . DisableEffectsMask = nonProcMask ;
2020-06-12 01:29:18 +02:00
if ( spellInfo . ProcFlags & PROC_FLAG_KILL )
2017-08-13 12:03:43 +02:00
procEntry . AttributesMask |= PROC_ATTR_REQ_EXP_OR_HONOR ;
if ( addTriggerFlag )
procEntry . AttributesMask |= PROC_ATTR_TRIGGERED_CAN_PROC ;
2011-07-26 23:09:28 +02:00
2017-08-13 12:03:43 +02:00
procEntry . ProcsPerMinute = 0 ;
2020-06-12 01:29:18 +02:00
procEntry . Chance = spellInfo . ProcChance ;
procEntry . Cooldown = Milliseconds ( spellInfo . ProcCooldown );
procEntry . Charges = spellInfo . ProcCharges ;
2017-08-13 12:03:43 +02:00
2020-06-12 01:29:18 +02:00
mSpellProcMap [{ spellInfo . Id , spellInfo . Difficulty }] = procEntry ;
2011-07-26 23:09:28 +02:00
++ count ;
2012-12-27 06:04:45 +01:00
}
2011-07-26 23:09:28 +02:00
2017-08-13 12:03:43 +02:00
TC_LOG_INFO ( "server.loading" , ">> Generated spell proc data for %u spells in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2011-07-26 23:09:28 +02:00
}
void SpellMgr :: LoadSpellThreats ()
2008-11-17 22:07:21 -06:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2011-07-26 23:09:28 +02:00
mSpellThreatMap . clear (); // need for reload case
2009-10-17 15:51:44 -07:00
2011-08-21 12:00:36 +02:00
// 0 1 2 3
QueryResult result = WorldDatabase . Query ( "SELECT entry, flatMod, pctMod, apPctMod FROM spell_threat" );
2009-11-19 03:53:49 -08:00
if ( ! result )
2008-11-17 22:07:21 -06:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 aggro generating spells. DB table `spell_threat` is empty." );
2008-11-17 22:07:21 -06:00
return ;
}
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
uint32 count = 0 ;
2008-11-17 22:07:21 -06:00
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 entry = fields [ 0 ]. GetUInt32 ();
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
if ( ! GetSpellInfo ( entry , DIFFICULTY_NONE ))
2008-11-17 22:07:21 -06:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_threat` does not exist." , entry );
2008-11-17 22:07:21 -06:00
continue ;
}
2009-10-17 15:51:44 -07:00
2011-08-21 12:00:36 +02:00
SpellThreatEntry ste ;
2012-05-30 08:01:02 +02:00
ste . flatMod = fields [ 1 ]. GetInt32 ();
2011-08-21 12:00:36 +02:00
ste . pctMod = fields [ 2 ]. GetFloat ();
ste . apPctMod = fields [ 3 ]. GetFloat ();
2009-10-17 15:51:44 -07:00
2011-08-21 12:00:36 +02:00
mSpellThreatMap [ entry ] = ste ;
2011-09-13 14:15:35 +02:00
++ count ;
2011-07-26 23:09:28 +02:00
} while ( result -> NextRow ());
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u SpellThreatEntries in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSkillLineAbilityMap ()
{
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mSkillLineAbilityMap . clear ();
uint32 count = 0 ;
2011-09-13 14:15:35 +02:00
for ( uint32 i = 0 ; i < sSkillLineAbilityStore . GetNumRows (); ++ i )
2011-07-26 23:09:28 +02:00
{
SkillLineAbilityEntry const * SkillInfo = sSkillLineAbilityStore . LookupEntry ( i );
if ( ! SkillInfo )
2008-11-17 22:07:21 -06:00
continue ;
2009-10-17 15:51:44 -07:00
2018-03-12 18:20:26 +01:00
mSkillLineAbilityMap . insert ( SkillLineAbilityMap :: value_type ( SkillInfo -> Spell , SkillInfo ));
2011-07-26 23:09:28 +02:00
++ count ;
}
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u SkillLineAbility MultiMap Data in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2011-07-26 23:09:28 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellPetAuras ()
{
uint32 oldMSTime = getMSTime ();
mSpellPetAuraMap . clear (); // need for reload case
// 0 1 2 3
QueryResult result = WorldDatabase . Query ( "SELECT spell, effectId, pet, aura FROM spell_pet_auras" );
if ( ! result )
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell pet auras. DB table `spell_pet_auras` is empty." );
2011-07-26 23:09:28 +02:00
return ;
}
uint32 count = 0 ;
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2011-07-26 23:09:28 +02:00
uint32 spell = fields [ 0 ]. GetUInt32 ();
2021-09-04 15:13:15 +02:00
SpellEffIndex eff = SpellEffIndex ( fields [ 1 ]. GetUInt8 ());
2011-07-26 23:09:28 +02:00
uint32 pet = fields [ 2 ]. GetUInt32 ();
uint32 aura = fields [ 3 ]. GetUInt32 ();
SpellPetAuraMap :: iterator itr = mSpellPetAuraMap . find (( spell << 8 ) + eff );
if ( itr != mSpellPetAuraMap . end ())
itr -> second . AddAura ( pet , aura );
else
2009-05-15 16:26:00 -05:00
{
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( spell , DIFFICULTY_NONE );
2009-11-19 03:53:49 -08:00
if ( ! spellInfo )
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_pet_auras` does not exist." , spell );
2009-05-15 16:26:00 -05:00
continue ;
}
2021-09-04 15:13:15 +02:00
if ( eff >= spellInfo -> GetEffects (). size ())
2014-11-24 18:57:49 +01:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "spells" , "The spell %u listed in `spell_pet_auras` does not have any effect at index %u" , spell , uint32 ( eff ));
2014-11-24 18:57:49 +01:00
continue ;
}
2021-09-04 15:13:15 +02:00
if ( spellInfo -> GetEffect ( eff ). Effect != SPELL_EFFECT_DUMMY &&
( spellInfo -> GetEffect ( eff ). Effect != SPELL_EFFECT_APPLY_AURA ||
spellInfo -> GetEffect ( eff ). ApplyAuraName != SPELL_AURA_DUMMY ))
2009-05-15 16:26:00 -05:00
{
2021-09-04 15:13:15 +02:00
TC_LOG_ERROR ( "spells" , "The spell %u listed in `spell_pet_auras` does not have any dummy aura or dummy effect." , spell );
2009-05-15 16:26:00 -05:00
continue ;
}
2009-10-17 15:51:44 -07:00
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo2 = GetSpellInfo ( aura , DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spellInfo2 )
2008-11-17 22:07:21 -06:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The aura %u listed in `spell_pet_auras` does not exist." , aura );
2011-07-26 23:09:28 +02:00
continue ;
2009-05-15 16:26:00 -05:00
}
2009-10-17 15:51:44 -07:00
2021-09-04 15:13:15 +02:00
PetAura pa ( pet , aura , spellInfo -> GetEffect ( eff ). TargetA . GetTarget () == TARGET_UNIT_PET , spellInfo -> GetEffect ( eff ). CalcValue ());
2011-07-26 23:09:28 +02:00
mSpellPetAuraMap [( spell << 8 ) + eff ] = pa ;
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2008-11-17 22:07:21 -06:00
++ count ;
2010-04-07 19:13:19 +02:00
} while ( result -> NextRow ());
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell pet auras in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
// Fill custom data about enchancments
void SpellMgr :: LoadEnchantCustomAttr ()
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 size = sSpellItemEnchantmentStore . GetNumRows ();
mEnchantCustomAttr . resize ( size );
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
for ( uint32 i = 0 ; i < size ; ++ i )
2020-06-12 01:29:18 +02:00
mEnchantCustomAttr [ i ] = false ;
2009-10-17 15:51:44 -07:00
2012-05-30 08:01:02 +02:00
uint32 count = 0 ;
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
2009-05-15 16:26:00 -05:00
{
2013-03-08 21:41:30 +01:00
/// @todo find a better check
2020-06-12 01:29:18 +02:00
if ( ! spellInfo . HasAttribute ( SPELL_ATTR2_PRESERVE_ENCHANT_IN_ARENA ) || ! spellInfo . HasAttribute ( SPELL_ATTR0_NOT_SHAPESHIFT ))
2011-07-26 23:09:28 +02:00
continue ;
2009-10-17 15:51:44 -07:00
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo . GetEffects ())
2009-09-23 00:45:47 -07:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . Effect == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY )
2009-09-23 00:45:47 -07:00
{
2021-09-04 15:13:15 +02:00
uint32 enchId = spellEffectInfo . MiscValue ;
2011-07-26 23:09:28 +02:00
SpellItemEnchantmentEntry const * ench = sSpellItemEnchantmentStore . LookupEntry ( enchId );
if ( ! ench )
continue ;
mEnchantCustomAttr [ enchId ] = true ;
++ count ;
break ;
2009-09-23 00:45:47 -07:00
}
}
}
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u custom enchant attributes in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellEnchantProcData ()
2008-11-17 22:07:21 -06:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2011-07-26 23:09:28 +02:00
mSpellEnchantProcEventMap . clear (); // need for reload case
2009-10-17 15:51:44 -07:00
2017-02-22 00:11:49 -03:00
// 0 1 2 3 4
QueryResult result = WorldDatabase . Query ( "SELECT EnchantID, Chance, ProcsPerMinute, HitMask, AttributesMask FROM spell_enchant_proc_data" );
2011-07-26 23:09:28 +02:00
if ( ! result )
2011-06-17 22:35:05 +02:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell enchant proc event conditions. DB table `spell_enchant_proc_data` is empty." );
2011-07-26 23:09:28 +02:00
return ;
2011-06-17 22:35:05 +02:00
}
2012-05-30 08:01:02 +02:00
uint32 count = 0 ;
2011-07-26 23:09:28 +02:00
do
2008-11-17 22:07:21 -06:00
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 enchantId = fields [ 0 ]. GetUInt32 ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellItemEnchantmentEntry const * ench = sSpellItemEnchantmentStore . LookupEntry ( enchantId );
if ( ! ench )
2011-05-15 18:55:38 +07:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The enchancment %u listed in `spell_enchant_proc_data` does not exist." , enchantId );
2011-07-26 23:09:28 +02:00
continue ;
2011-05-15 18:55:38 +07:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
SpellEnchantProcEntry spe ;
2010-02-14 19:13:14 -07:00
2017-02-22 00:11:49 -03:00
spe . Chance = fields [ 1 ]. GetFloat ();
spe . ProcsPerMinute = fields [ 2 ]. GetFloat ();
spe . HitMask = fields [ 3 ]. GetUInt32 ();
spe . AttributesMask = fields [ 4 ]. GetUInt32 ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mSpellEnchantProcEventMap [ enchantId ] = spe ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
++ count ;
} while ( result -> NextRow ());
2010-12-27 22:27:11 +01:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u enchant proc data definitions in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2010-12-27 22:27:11 +01:00
}
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellLinked ()
2008-11-17 22:07:21 -06:00
{
2011-07-26 23:09:28 +02:00
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mSpellLinkedMap . clear (); // need for reload case
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
// 0 1 2
QueryResult result = WorldDatabase . Query ( "SELECT spell_trigger, spell_effect, type FROM spell_linked_spell" );
if ( ! result )
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 linked spells. DB table `spell_linked_spell` is empty." );
2011-07-26 23:09:28 +02:00
return ;
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 count = 0 ;
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
int32 trigger = fields [ 0 ]. GetInt32 ();
2012-05-30 08:01:02 +02:00
int32 effect = fields [ 1 ]. GetInt32 ();
int32 type = fields [ 2 ]. GetUInt8 ();
2010-02-14 19:13:14 -07:00
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( abs ( trigger ), DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spellInfo )
2011-06-28 13:53:48 +02:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_linked_spell` does not exist." , abs ( trigger ));
2011-07-26 23:09:28 +02:00
continue ;
2011-06-28 13:53:48 +02:00
}
2014-10-09 18:54:09 +02:00
if ( effect >= 0 )
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfo -> GetEffects ())
2014-10-09 18:54:09 +02:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . CalcValue () == abs ( effect ))
TC_LOG_ERROR ( "sql.sql" , "The spell %u Effect: %u listed in `spell_linked_spell` has same bp%u like effect (possible hack)." , abs ( trigger ), abs ( effect ), uint32 ( spellEffectInfo . EffectIndex ));
2014-10-09 18:54:09 +02:00
}
2020-06-12 01:29:18 +02:00
spellInfo = GetSpellInfo ( abs ( effect ), DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spellInfo )
2011-06-28 13:53:48 +02:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_linked_spell` does not exist." , abs ( effect ));
2011-07-26 23:09:28 +02:00
continue ;
}
2010-06-16 19:36:50 +02:00
2011-07-26 23:09:28 +02:00
if ( type ) //we will find a better way when more types are needed
{
if ( trigger > 0 )
trigger += SPELL_LINKED_MAX_SPELLS * type ;
else
trigger -= SPELL_LINKED_MAX_SPELLS * type ;
2011-06-28 13:53:48 +02:00
}
2011-07-26 23:09:28 +02:00
mSpellLinkedMap [ trigger ]. push_back ( effect );
2010-01-21 01:28:18 +01:00
2011-07-26 23:09:28 +02:00
++ count ;
} while ( result -> NextRow ());
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u linked spells in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadPetLevelupSpellMap ()
2009-05-15 16:26:00 -05:00
{
2011-07-26 23:09:28 +02:00
uint32 oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
mPetLevelupSpellMap . clear (); // need for reload case
2010-01-23 17:05:20 +01:00
2011-07-26 23:09:28 +02:00
uint32 count = 0 ;
uint32 family_count = 0 ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
for ( uint32 i = 0 ; i < sCreatureFamilyStore . GetNumRows (); ++ i )
2009-05-15 16:26:00 -05:00
{
2011-07-26 23:09:28 +02:00
CreatureFamilyEntry const * creatureFamily = sCreatureFamilyStore . LookupEntry ( i );
if ( ! creatureFamily ) // not exist
continue ;
2009-10-17 16:20:24 -07:00
2011-07-26 23:09:28 +02:00
for ( uint8 j = 0 ; j < 2 ; ++ j )
2009-08-23 10:22:58 -05:00
{
2014-11-08 16:55:02 +02:00
if ( ! creatureFamily -> SkillLine [ j ])
2011-07-26 23:09:28 +02:00
continue ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
for ( uint32 k = 0 ; k < sSkillLineAbilityStore . GetNumRows (); ++ k )
{
SkillLineAbilityEntry const * skillLine = sSkillLineAbilityStore . LookupEntry ( k );
if ( ! skillLine )
continue ;
2011-02-27 03:53:24 +01:00
2014-11-09 02:51:35 +02:00
if ( skillLine -> SkillLine != creatureFamily -> SkillLine [ j ])
2011-07-26 23:09:28 +02:00
continue ;
2011-02-27 03:53:24 +01:00
2016-06-22 22:04:42 +02:00
if ( skillLine -> AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN )
2011-07-26 23:09:28 +02:00
continue ;
2011-02-27 03:53:24 +01:00
2020-06-12 01:29:18 +02:00
SpellInfo const * spell = GetSpellInfo ( skillLine -> Spell , DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spell ) // not exist or triggered or talent
continue ;
2011-02-27 03:53:24 +01:00
2011-07-26 23:09:28 +02:00
if ( ! spell -> SpellLevel )
continue ;
2011-02-27 03:53:24 +01:00
2016-02-06 17:53:37 +01:00
PetLevelupSpellSet & spellSet = mPetLevelupSpellMap [ i ];
2011-07-26 23:09:28 +02:00
if ( spellSet . empty ())
++ family_count ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
spellSet . insert ( PetLevelupSpellSet :: value_type ( spell -> SpellLevel , spell -> Id ));
++ count ;
}
}
2009-08-23 10:22:58 -05:00
}
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u pet levelup and default spells for %u families in %u ms" , count , family_count , GetMSTimeDiffToNow ( oldMSTime ));
2009-05-15 16:26:00 -05:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
bool LoadPetDefaultSpells_helper ( CreatureTemplate const * cInfo , PetDefaultSpellsEntry & petDefSpells )
2009-05-15 16:26:00 -05:00
{
2011-07-26 23:09:28 +02:00
// skip empty list;
bool have_spell = false ;
for ( uint8 j = 0 ; j < MAX_CREATURE_SPELL_DATA_SLOT ; ++ j )
{
if ( petDefSpells . spellid [ j ])
{
have_spell = true ;
break ;
}
}
if ( ! have_spell )
2009-05-16 20:23:56 +02:00
return false ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
// remove duplicates with levelupSpells if any
2020-08-14 17:06:03 +02:00
if ( PetLevelupSpellSet const * levelupSpells = cInfo -> family ? sSpellMgr -> GetPetLevelupSpellList ( cInfo -> family ) : nullptr )
2011-07-26 23:09:28 +02:00
{
for ( uint8 j = 0 ; j < MAX_CREATURE_SPELL_DATA_SLOT ; ++ j )
{
if ( ! petDefSpells . spellid [ j ])
continue ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
for ( PetLevelupSpellSet :: const_iterator itr = levelupSpells -> begin (); itr != levelupSpells -> end (); ++ itr )
{
if ( itr -> second == petDefSpells . spellid [ j ])
{
petDefSpells . spellid [ j ] = 0 ;
break ;
}
}
}
}
2010-07-26 19:27:28 +02:00
2011-07-26 23:09:28 +02:00
// skip empty list;
have_spell = false ;
for ( uint8 j = 0 ; j < MAX_CREATURE_SPELL_DATA_SLOT ; ++ j )
{
if ( petDefSpells . spellid [ j ])
{
have_spell = true ;
break ;
}
}
2010-07-26 19:27:28 +02:00
2011-07-26 23:09:28 +02:00
return have_spell ;
2010-07-26 19:27:28 +02:00
}
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadPetDefaultSpells ()
2010-07-26 19:27:28 +02:00
{
2011-07-26 23:09:28 +02:00
uint32 oldMSTime = getMSTime ();
2010-07-26 19:27:28 +02:00
2011-07-26 23:09:28 +02:00
mPetDefaultSpellsMap . clear ();
2010-07-26 19:27:28 +02:00
2011-07-26 23:09:28 +02:00
uint32 countCreature = 0 ;
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , "Loading summonable creature templates..." );
2011-07-26 23:09:28 +02:00
oldMSTime = getMSTime ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
// different summon spells
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellEntry : mSpellInfoMap )
2011-07-26 23:09:28 +02:00
{
2020-06-12 01:29:18 +02:00
if ( spellEntry . Difficulty != DIFFICULTY_NONE )
2011-07-26 23:09:28 +02:00
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellEntry . GetEffects ())
2009-07-31 13:21:01 +02:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsEffect ( SPELL_EFFECT_SUMMON ) || spellEffectInfo . IsEffect ( SPELL_EFFECT_SUMMON_PET ))
2011-07-26 23:09:28 +02:00
{
2021-09-04 15:13:15 +02:00
uint32 creature_id = spellEffectInfo . MiscValue ;
2011-07-26 23:09:28 +02:00
CreatureTemplate const * cInfo = sObjectMgr -> GetCreatureTemplate ( creature_id );
if ( ! cInfo )
continue ;
2009-10-17 15:51:44 -07:00
2015-07-04 19:47:58 +02:00
// get default pet spells from creature_template
2011-07-26 23:09:28 +02:00
int32 petSpellsId = cInfo -> Entry ;
if ( mPetDefaultSpellsMap . find ( cInfo -> Entry ) != mPetDefaultSpellsMap . end ())
continue ;
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
PetDefaultSpellsEntry petDefSpells ;
for ( uint8 j = 0 ; j < MAX_CREATURE_SPELL_DATA_SLOT ; ++ j )
petDefSpells . spellid [ j ] = cInfo -> spells [ j ];
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
if ( LoadPetDefaultSpells_helper ( cInfo , petDefSpells ))
{
mPetDefaultSpellsMap [ petSpellsId ] = petDefSpells ;
++ countCreature ;
}
}
}
}
2009-10-17 15:51:44 -07:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u summonable creature templates in %u ms" , countCreature , GetMSTimeDiffToNow ( oldMSTime ));
2009-07-31 13:21:01 +02:00
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellAreas ()
2009-05-15 16:26:00 -05:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2011-07-26 23:09:28 +02:00
mSpellAreaMap . clear (); // need for reload case
2017-12-02 20:59:21 +01:00
mSpellAreaForAreaMap . clear ();
2011-07-26 23:09:28 +02:00
mSpellAreaForQuestMap . clear ();
mSpellAreaForQuestEndMap . clear ();
mSpellAreaForAuraMap . clear ();
2009-10-17 15:51:44 -07:00
2012-10-16 21:43:23 -05:00
// 0 1 2 3 4 5 6 7 8 9
2018-01-28 19:33:33 +01:00
QueryResult result = WorldDatabase . Query ( "SELECT spell, area, quest_start, quest_start_status, quest_end_status, quest_end, aura_spell, racemask, gender, flags FROM spell_area" );
2010-01-01 14:19:29 -06:00
if ( ! result )
2009-05-15 16:26:00 -05:00
{
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell area requirements. DB table `spell_area` is empty." );
2012-08-03 15:54:54 +02:00
2009-05-15 16:26:00 -05:00
return ;
}
2011-07-26 23:09:28 +02:00
uint32 count = 0 ;
2009-05-15 16:26:00 -05:00
do
{
2011-09-15 14:08:17 +02:00
Field * fields = result -> Fetch ();
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
uint32 spell = fields [ 0 ]. GetUInt32 ();
SpellArea spellArea ;
spellArea . spellId = spell ;
spellArea . areaId = fields [ 1 ]. GetUInt32 ();
spellArea . questStart = fields [ 2 ]. GetUInt32 ();
2012-10-16 21:43:23 -05:00
spellArea . questStartStatus = fields [ 3 ]. GetUInt32 ();
spellArea . questEndStatus = fields [ 4 ]. GetUInt32 ();
spellArea . questEnd = fields [ 5 ]. GetUInt32 ();
spellArea . auraSpell = fields [ 6 ]. GetInt32 ();
2020-03-30 19:06:11 +02:00
spellArea . raceMask . RawValue = fields [ 7 ]. GetUInt64 ();
2012-10-16 21:43:23 -05:00
spellArea . gender = Gender ( fields [ 8 ]. GetUInt8 ());
2018-01-28 19:33:33 +01:00
spellArea . flags = fields [ 9 ]. GetUInt8 ();
2011-07-26 23:09:28 +02:00
2020-06-12 01:29:18 +02:00
if ( SpellInfo const * spellInfo = GetSpellInfo ( spell , DIFFICULTY_NONE ))
2010-01-30 19:25:04 +01:00
{
2018-01-28 19:33:33 +01:00
if ( spellArea . flags & SPELL_AREA_FLAG_AUTOCAST )
2011-07-26 23:09:28 +02:00
const_cast < SpellInfo *> ( spellInfo ) -> Attributes |= SPELL_ATTR0_CANT_CANCEL ;
2010-01-30 19:25:04 +01:00
}
2011-07-26 23:09:28 +02:00
else
2010-01-30 19:25:04 +01:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` does not exist" , spell );
2010-01-30 19:25:04 +01:00
continue ;
}
2009-10-17 15:51:44 -07:00
2009-05-15 16:26:00 -05:00
{
2011-07-26 23:09:28 +02:00
bool ok = true ;
SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds ( spellArea . spellId );
for ( SpellAreaMap :: const_iterator itr = sa_bounds . first ; itr != sa_bounds . second ; ++ itr )
{
if ( spellArea . spellId != itr -> second . spellId )
continue ;
if ( spellArea . areaId != itr -> second . areaId )
continue ;
if ( spellArea . questStart != itr -> second . questStart )
continue ;
if ( spellArea . auraSpell != itr -> second . auraSpell )
continue ;
if (( spellArea . raceMask & itr -> second . raceMask ) == 0 )
continue ;
if ( spellArea . gender != itr -> second . gender )
continue ;
2010-01-29 17:54:29 +01:00
2011-07-26 23:09:28 +02:00
// duplicate by requirements
2012-10-16 21:43:23 -05:00
ok = false ;
2011-07-26 23:09:28 +02:00
break ;
}
2010-01-29 17:54:29 +01:00
2011-07-26 23:09:28 +02:00
if ( ! ok )
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` is already listed with similar requirements." , spell );
2011-07-26 23:09:28 +02:00
continue ;
2009-05-15 16:26:00 -05:00
}
2010-01-29 17:54:29 +01:00
}
2011-07-26 23:09:28 +02:00
2016-02-02 19:13:04 +01:00
if ( spellArea . areaId && ! sAreaTableStore . LookupEntry ( spellArea . areaId ))
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has a wrong area (%u) requirement." , spell , spellArea . areaId );
2009-05-15 16:26:00 -05:00
continue ;
}
2011-07-26 23:09:28 +02:00
if ( spellArea . questStart && ! sObjectMgr -> GetQuestTemplate ( spellArea . questStart ))
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has a wrong start quest (%u) requirement." , spell , spellArea . questStart );
2010-01-29 17:54:29 +01:00
continue ;
2009-05-15 16:26:00 -05:00
}
2011-07-26 23:09:28 +02:00
if ( spellArea . questEnd )
2009-05-15 16:26:00 -05:00
{
2011-07-26 23:09:28 +02:00
if ( ! sObjectMgr -> GetQuestTemplate ( spellArea . questEnd ))
2009-05-15 16:26:00 -05:00
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has a wrong ending quest (%u) requirement." , spell , spellArea . questEnd );
2011-07-26 23:09:28 +02:00
continue ;
2010-01-29 17:54:29 +01:00
}
2009-05-15 16:26:00 -05:00
}
2011-07-26 23:09:28 +02:00
if ( spellArea . auraSpell )
{
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( abs ( spellArea . auraSpell ), DIFFICULTY_NONE );
2011-07-26 23:09:28 +02:00
if ( ! spellInfo )
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has wrong aura spell (%u) requirement" , spell , abs ( spellArea . auraSpell ));
2011-07-26 23:09:28 +02:00
continue ;
}
if ( uint32 ( abs ( spellArea . auraSpell )) == spellArea . spellId )
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has aura spell (%u) requirement for itself" , spell , abs ( spellArea . auraSpell ));
2011-07-26 23:09:28 +02:00
continue ;
}
// not allow autocast chains by auraSpell field (but allow use as alternative if not present)
2018-01-28 19:33:33 +01:00
if ( spellArea . flags & SPELL_AREA_FLAG_AUTOCAST && spellArea . auraSpell > 0 )
2011-07-26 23:09:28 +02:00
{
bool chain = false ;
SpellAreaForAuraMapBounds saBound = GetSpellAreaForAuraMapBounds ( spellArea . spellId );
for ( SpellAreaForAuraMap :: const_iterator itr = saBound . first ; itr != saBound . second ; ++ itr )
{
2018-01-28 19:33:33 +01:00
if ( itr -> second -> flags & SPELL_AREA_FLAG_AUTOCAST && itr -> second -> auraSpell > 0 )
2011-07-26 23:09:28 +02:00
{
chain = true ;
break ;
}
}
if ( chain )
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has the aura spell (%u) requirement that it autocasts itself from the aura." , spell , spellArea . auraSpell );
2011-07-26 23:09:28 +02:00
continue ;
}
SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds ( spellArea . auraSpell );
for ( SpellAreaMap :: const_iterator itr2 = saBound2 . first ; itr2 != saBound2 . second ; ++ itr2 )
{
2018-01-28 19:33:33 +01:00
if ( itr2 -> second . flags & SPELL_AREA_FLAG_AUTOCAST && itr2 -> second . auraSpell > 0 )
2011-07-26 23:09:28 +02:00
{
chain = true ;
break ;
}
}
if ( chain )
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has the aura spell (%u) requirement that the spell itself autocasts from the aura." , spell , spellArea . auraSpell );
2011-07-26 23:09:28 +02:00
continue ;
}
}
}
2020-03-30 19:06:11 +02:00
if ( spellArea . raceMask && ( spellArea . raceMask . RawValue & RACEMASK_ALL_PLAYABLE ) == 0 )
2011-07-26 23:09:28 +02:00
{
2020-03-30 19:06:11 +02:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has wrong race mask (" UI64FMTD ") requirement." , spell , spellArea . raceMask . RawValue );
2009-05-15 16:26:00 -05:00
continue ;
2011-07-26 23:09:28 +02:00
}
if ( spellArea . gender != GENDER_NONE && spellArea . gender != GENDER_FEMALE && spellArea . gender != GENDER_MALE )
{
2016-02-20 13:23:35 +01:00
TC_LOG_ERROR ( "sql.sql" , "The spell %u listed in `spell_area` has wrong gender (%u) requirement." , spell , spellArea . gender );
2011-07-26 23:09:28 +02:00
continue ;
}
SpellArea const * sa = & mSpellAreaMap . insert ( SpellAreaMap :: value_type ( spell , spellArea )) -> second ;
// for search by current zone/subzone at zone/subzone change
if ( spellArea . areaId )
mSpellAreaForAreaMap . insert ( SpellAreaForAreaMap :: value_type ( spellArea . areaId , sa ));
2019-08-28 10:33:41 +04:30
// for search at quest update checks
if ( spellArea . questStart || spellArea . questEnd )
{
if ( spellArea . questStart == spellArea . questEnd )
mSpellAreaForQuestMap . insert ( SpellAreaForQuestMap :: value_type ( spellArea . questStart , sa ));
else
{
if ( spellArea . questStart )
mSpellAreaForQuestMap . insert ( SpellAreaForQuestMap :: value_type ( spellArea . questStart , sa ));
if ( spellArea . questEnd )
mSpellAreaForQuestMap . insert ( SpellAreaForQuestMap :: value_type ( spellArea . questEnd , sa ));
}
}
2009-10-17 15:51:44 -07:00
2011-07-26 23:09:28 +02:00
// for search at quest start/reward
if ( spellArea . questEnd )
mSpellAreaForQuestEndMap . insert ( SpellAreaForQuestMap :: value_type ( spellArea . questEnd , sa ));
// for search at aura apply
if ( spellArea . auraSpell )
mSpellAreaForAuraMap . insert ( SpellAreaForAuraMap :: value_type ( abs ( spellArea . auraSpell ), sa ));
++ count ;
} while ( result -> NextRow ());
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell area requirements in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
2008-11-17 22:07:21 -06:00
}
2009-10-17 15:51:44 -07:00
2014-11-16 14:20:01 +01:00
typedef std :: vector < SpellEffectEntry const *> SpellEffectVector ;
2012-09-16 00:35:48 +02:00
2011-07-26 23:09:28 +02:00
void SpellMgr :: LoadSpellInfoStore ()
2008-11-17 22:07:21 -06:00
{
2010-12-19 17:06:33 +01:00
uint32 oldMSTime = getMSTime ();
2011-07-26 23:09:28 +02:00
UnloadSpellInfoStore ();
2020-06-12 01:29:18 +02:00
std :: unordered_map < std :: pair < uint32 , Difficulty > , SpellInfoLoadHelper > loadData ;
2010-01-01 14:19:29 -06:00
2020-04-08 13:36:59 +02:00
std :: unordered_map < int32 , BattlePetSpeciesEntry const *> battlePetSpeciesByCreature ;
std :: unordered_map < uint32 , BattlePetSpeciesEntry const *> battlePetSpeciesBySpellId ;
for ( BattlePetSpeciesEntry const * battlePetSpecies : sBattlePetSpeciesStore )
if ( battlePetSpecies -> CreatureID )
battlePetSpeciesByCreature [ battlePetSpecies -> CreatureID ] = battlePetSpecies ;
2015-07-07 12:13:14 +02:00
for ( SpellEffectEntry const * effect : sSpellEffectStore )
2013-07-30 15:27:24 +01:00
{
2018-03-12 18:20:26 +01:00
ASSERT ( effect -> EffectIndex < MAX_SPELL_EFFECTS , "MAX_SPELL_EFFECTS must be at least %d" , effect -> EffectIndex + 1 );
2016-08-08 10:18:06 +02:00
ASSERT ( effect -> Effect < TOTAL_SPELL_EFFECTS , "TOTAL_SPELL_EFFECTS must be at least %u" , effect -> Effect + 1 );
2018-03-12 18:20:26 +01:00
ASSERT ( effect -> EffectAura < int32 ( TOTAL_AURAS ), "TOTAL_AURAS must be at least %d" , effect -> EffectAura + 1 );
2016-08-08 10:18:06 +02:00
ASSERT ( effect -> ImplicitTarget [ 0 ] < TOTAL_SPELL_TARGETS , "TOTAL_SPELL_TARGETS must be at least %u" , effect -> ImplicitTarget [ 0 ] + 1 );
ASSERT ( effect -> ImplicitTarget [ 1 ] < TOTAL_SPELL_TARGETS , "TOTAL_SPELL_TARGETS must be at least %u" , effect -> ImplicitTarget [ 1 ] + 1 );
2014-11-24 18:57:49 +01:00
2020-06-12 01:29:18 +02:00
loadData [{ effect -> SpellID , Difficulty ( effect -> DifficultyID ) }]. Effects [ effect -> EffectIndex ] = effect ;
2020-04-08 13:36:59 +02:00
if ( effect -> Effect == SPELL_EFFECT_SUMMON )
if ( SummonPropertiesEntry const * summonProperties = sSummonPropertiesStore . LookupEntry ( effect -> EffectMiscValue [ 1 ]))
2021-11-16 11:51:05 +01:00
if ( summonProperties -> Slot == SUMMON_SLOT_MINIPET && summonProperties -> GetFlags (). HasFlag ( SummonPropertiesFlags :: SummonFromBattlePetJournal ))
2020-04-08 13:36:59 +02:00
if ( BattlePetSpeciesEntry const * battlePetSpecies = Trinity :: Containers :: MapGetValuePtr ( battlePetSpeciesByCreature , effect -> EffectMiscValue [ 0 ]))
mBattlePets [ effect -> SpellID ] = battlePetSpecies ;
2021-03-30 02:27:50 +03:00
if ( effect -> Effect == SPELL_EFFECT_LANGUAGE )
sLanguageMgr -> LoadSpellEffectLanguage ( effect );
2013-07-30 15:27:24 +01:00
}
2012-09-15 22:02:17 +02:00
2016-01-24 15:56:10 +01:00
for ( SpellAuraOptionsEntry const * auraOptions : sSpellAuraOptionsStore )
2020-06-12 01:29:18 +02:00
loadData [{ auraOptions -> SpellID , Difficulty ( auraOptions -> DifficultyID ) }]. AuraOptions = auraOptions ;
2016-01-24 15:56:10 +01:00
for ( SpellAuraRestrictionsEntry const * auraRestrictions : sSpellAuraRestrictionsStore )
2020-06-12 01:29:18 +02:00
loadData [{ auraRestrictions -> SpellID , Difficulty ( auraRestrictions -> DifficultyID ) }]. AuraRestrictions = auraRestrictions ;
2016-01-24 15:56:10 +01:00
for ( SpellCastingRequirementsEntry const * castingRequirements : sSpellCastingRequirementsStore )
2020-06-12 01:29:18 +02:00
loadData [{ castingRequirements -> SpellID , DIFFICULTY_NONE }]. CastingRequirements = castingRequirements ;
2016-01-24 15:56:10 +01:00
for ( SpellCategoriesEntry const * categories : sSpellCategoriesStore )
2020-06-12 01:29:18 +02:00
loadData [{ categories -> SpellID , Difficulty ( categories -> DifficultyID ) }]. Categories = categories ;
2016-01-24 15:56:10 +01:00
for ( SpellClassOptionsEntry const * classOptions : sSpellClassOptionsStore )
2020-06-12 01:29:18 +02:00
loadData [{ classOptions -> SpellID , DIFFICULTY_NONE }]. ClassOptions = classOptions ;
2016-01-24 15:56:10 +01:00
for ( SpellCooldownsEntry const * cooldowns : sSpellCooldownsStore )
2020-06-12 01:29:18 +02:00
loadData [{ cooldowns -> SpellID , Difficulty ( cooldowns -> DifficultyID ) }]. Cooldowns = cooldowns ;
2016-01-24 15:56:10 +01:00
for ( SpellEquippedItemsEntry const * equippedItems : sSpellEquippedItemsStore )
2020-06-12 01:29:18 +02:00
loadData [{ equippedItems -> SpellID , DIFFICULTY_NONE }]. EquippedItems = equippedItems ;
2016-01-24 15:56:10 +01:00
for ( SpellInterruptsEntry const * interrupts : sSpellInterruptsStore )
2020-06-12 01:29:18 +02:00
loadData [{ interrupts -> SpellID , Difficulty ( interrupts -> DifficultyID ) }]. Interrupts = interrupts ;
2016-01-24 15:56:10 +01:00
2021-06-04 21:18:29 +02:00
for ( SpellLabelEntry const * label : sSpellLabelStore )
2021-10-16 16:42:49 +02:00
loadData [{ label -> SpellID , DIFFICULTY_NONE }]. Labels . push_back ( label );
2021-06-04 21:18:29 +02:00
2016-01-24 15:56:10 +01:00
for ( SpellLevelsEntry const * levels : sSpellLevelsStore )
2020-06-12 01:29:18 +02:00
loadData [{ levels -> SpellID , Difficulty ( levels -> DifficultyID ) }]. Levels = levels ;
2016-01-24 15:56:10 +01:00
2018-02-18 16:32:03 +01:00
for ( SpellMiscEntry const * misc : sSpellMiscStore )
2020-06-12 01:29:18 +02:00
loadData [{ misc -> SpellID , Difficulty ( misc -> DifficultyID ) }]. Misc = misc ;
for ( SpellPowerEntry const * power : sSpellPowerStore )
{
Difficulty difficulty = DIFFICULTY_NONE ;
uint8 index = power -> OrderIndex ;
if ( SpellPowerDifficultyEntry const * powerDifficulty = sSpellPowerDifficultyStore . LookupEntry ( power -> ID ))
{
difficulty = Difficulty ( powerDifficulty -> DifficultyID );
index = powerDifficulty -> OrderIndex ;
}
loadData [{ power -> SpellID , difficulty }]. Powers [ index ] = power ;
}
2018-02-18 16:32:03 +01:00
2016-01-24 15:56:10 +01:00
for ( SpellReagentsEntry const * reagents : sSpellReagentsStore )
2020-06-12 01:29:18 +02:00
loadData [{ reagents -> SpellID , DIFFICULTY_NONE }]. Reagents = reagents ;
2016-01-24 15:56:10 +01:00
2021-10-10 19:12:17 +02:00
for ( SpellReagentsCurrencyEntry const * reagentsCurrency : sSpellReagentsCurrencyStore )
loadData [{ reagentsCurrency -> SpellID , DIFFICULTY_NONE }]. ReagentsCurrency . push_back ( reagentsCurrency );
2016-01-24 15:56:10 +01:00
for ( SpellScalingEntry const * scaling : sSpellScalingStore )
2020-06-12 01:29:18 +02:00
loadData [{ scaling -> SpellID , DIFFICULTY_NONE }]. Scaling = scaling ;
2016-01-24 15:56:10 +01:00
for ( SpellShapeshiftEntry const * shapeshift : sSpellShapeshiftStore )
2020-06-12 01:29:18 +02:00
loadData [{ shapeshift -> SpellID , DIFFICULTY_NONE }]. Shapeshift = shapeshift ;
2016-01-24 15:56:10 +01:00
for ( SpellTargetRestrictionsEntry const * targetRestrictions : sSpellTargetRestrictionsStore )
2020-06-12 01:29:18 +02:00
loadData [{ targetRestrictions -> SpellID , Difficulty ( targetRestrictions -> DifficultyID ) }]. TargetRestrictions = targetRestrictions ;
2016-01-24 15:56:10 +01:00
for ( SpellTotemsEntry const * totems : sSpellTotemsStore )
2020-06-12 01:29:18 +02:00
loadData [{ totems -> SpellID , DIFFICULTY_NONE }]. Totems = totems ;
2016-01-24 15:56:10 +01:00
2015-07-07 12:13:14 +02:00
for ( SpellXSpellVisualEntry const * visual : sSpellXSpellVisualStore )
2020-06-12 01:29:18 +02:00
{
SpellVisualVector & visuals = loadData [{ visual -> SpellID , Difficulty ( visual -> DifficultyID ) }]. Visuals ;
auto where = std :: lower_bound ( visuals . begin (), visuals . end (), visual , []( SpellXSpellVisualEntry const * first , SpellXSpellVisualEntry const * second )
{
return first -> CasterPlayerConditionID > second -> CasterPlayerConditionID ;
});
2015-07-07 12:13:14 +02:00
2020-06-12 01:29:18 +02:00
// sorted with unconditional visuals being last
visuals . insert ( where , visual );
}
for ( std :: pair < std :: pair < uint32 , Difficulty > const , SpellInfoLoadHelper >& data : loadData )
2016-01-24 15:56:10 +01:00
{
2020-06-12 01:29:18 +02:00
SpellNameEntry const * spellNameEntry = sSpellNameStore . LookupEntry ( data . first . first );
if ( ! spellNameEntry )
continue ;
// fill blanks
if ( DifficultyEntry const * difficultyEntry = sDifficultyStore . LookupEntry ( data . first . second ))
2016-01-24 15:56:10 +01:00
{
2020-06-12 01:29:18 +02:00
do
{
if ( SpellInfoLoadHelper const * fallbackData = Trinity :: Containers :: MapGetValuePtr ( loadData , { data . first . first , Difficulty ( difficultyEntry -> FallbackDifficultyID ) }))
{
if ( ! data . second . AuraOptions )
data . second . AuraOptions = fallbackData -> AuraOptions ;
if ( ! data . second . AuraRestrictions )
data . second . AuraRestrictions = fallbackData -> AuraRestrictions ;
2021-10-10 19:02:52 +02:00
if ( ! data . second . CastingRequirements )
data . second . CastingRequirements = fallbackData -> CastingRequirements ;
2020-06-12 01:29:18 +02:00
if ( ! data . second . Categories )
data . second . Categories = fallbackData -> Categories ;
2021-10-10 19:02:52 +02:00
if ( ! data . second . ClassOptions )
data . second . ClassOptions = fallbackData -> ClassOptions ;
2020-06-12 01:29:18 +02:00
if ( ! data . second . Cooldowns )
data . second . Cooldowns = fallbackData -> Cooldowns ;
for ( std :: size_t i = 0 ; i < data . second . Effects . size (); ++ i )
if ( ! data . second . Effects [ i ])
data . second . Effects [ i ] = fallbackData -> Effects [ i ];
2021-10-10 19:02:52 +02:00
if ( ! data . second . EquippedItems )
data . second . EquippedItems = fallbackData -> EquippedItems ;
2020-06-12 01:29:18 +02:00
if ( ! data . second . Interrupts )
data . second . Interrupts = fallbackData -> Interrupts ;
2021-10-10 19:02:52 +02:00
if ( data . second . Labels . empty ())
data . second . Labels = fallbackData -> Labels ;
2021-06-04 21:18:29 +02:00
2020-06-12 01:29:18 +02:00
if ( ! data . second . Levels )
data . second . Levels = fallbackData -> Levels ;
if ( ! data . second . Misc )
data . second . Misc = fallbackData -> Misc ;
for ( std :: size_t i = 0 ; i < fallbackData -> Powers . size (); ++ i )
if ( ! data . second . Powers [ i ])
data . second . Powers [ i ] = fallbackData -> Powers [ i ];
2021-10-10 19:02:52 +02:00
if ( ! data . second . Reagents )
data . second . Reagents = fallbackData -> Reagents ;
2021-10-10 19:12:17 +02:00
if ( data . second . ReagentsCurrency . empty ())
data . second . ReagentsCurrency = fallbackData -> ReagentsCurrency ;
2021-10-10 19:02:52 +02:00
if ( ! data . second . Scaling )
data . second . Scaling = fallbackData -> Scaling ;
if ( ! data . second . Shapeshift )
data . second . Shapeshift = fallbackData -> Shapeshift ;
2020-06-12 01:29:18 +02:00
if ( ! data . second . TargetRestrictions )
data . second . TargetRestrictions = fallbackData -> TargetRestrictions ;
2021-10-10 19:02:52 +02:00
if ( ! data . second . Totems )
data . second . Totems = fallbackData -> Totems ;
// visuals fall back only to first difficulty that defines any visual
// they do not stack all difficulties in fallback chain
if ( data . second . Visuals . empty ())
data . second . Visuals = fallbackData -> Visuals ;
2020-06-12 01:29:18 +02:00
}
difficultyEntry = sDifficultyStore . LookupEntry ( difficultyEntry -> FallbackDifficultyID );
} while ( difficultyEntry );
2016-01-24 15:56:10 +01:00
}
2020-06-12 01:29:18 +02:00
2021-10-10 19:02:52 +02:00
mSpellInfoMap . emplace ( spellNameEntry , data . first . second , data . second );
2016-01-24 15:56:10 +01:00
}
2011-07-26 23:09:28 +02:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded SpellInfo store in %u ms" , GetMSTimeDiffToNow ( oldMSTime ));
2011-07-26 23:09:28 +02:00
}
void SpellMgr :: UnloadSpellInfoStore ()
{
mSpellInfoMap . clear ();
2021-03-12 21:59:10 +01:00
mServersideSpellNames . clear ();
2011-07-26 23:09:28 +02:00
}
2012-02-21 20:03:27 +01:00
void SpellMgr :: UnloadSpellInfoImplicitTargetConditionLists ()
{
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
const_cast < SpellInfo &> ( spellInfo ). _UnloadImplicitTargetConditionLists ();
2012-02-21 20:03:27 +01:00
}
2021-03-12 21:59:10 +01:00
void SpellMgr :: LoadSpellInfoServerside ()
{
uint32 oldMSTime = getMSTime ();
std :: unordered_map < std :: pair < uint32 , Difficulty > , std :: vector < SpellEffectEntry >> spellEffects ;
// 0 1 2 3 4 5 6
QueryResult effectsResult = WorldDatabase . Query ( "SELECT SpellID, EffectIndex, DifficultyID, Effect, EffectAura, EffectAmplitude, EffectAttributes, "
// 7 8 9 10 11 12 13
"EffectAuraPeriod, EffectBonusCoefficient, EffectChainAmplitude, EffectChainTargets, EffectItemType, EffectMechanic, EffectPointsPerResource, "
// 14 15 16 17 18 19 20
"EffectPosFacing, EffectRealPointsPerLevel, EffectTriggerSpell, BonusCoefficientFromAP, PvpMultiplier, Coefficient, Variance, "
// 21 22 23 24 25 26
"ResourceCoefficient, GroupSizeBasePointsCoefficient, EffectBasePoints, EffectMiscValue1, EffectMiscValue2, EffectRadiusIndex1, "
// 27 28 29 30 31 32
"EffectRadiusIndex2, EffectSpellClassMask1, EffectSpellClassMask2, EffectSpellClassMask3, EffectSpellClassMask4, ImplicitTarget1, "
// 33
"ImplicitTarget2 FROM serverside_spell_effect" );
if ( effectsResult )
{
do
{
Field * fields = effectsResult -> Fetch ();
uint32 spellId = fields [ 0 ]. GetUInt32 ();
Difficulty difficulty = Difficulty ( fields [ 2 ]. GetUInt32 ());
SpellEffectEntry effect { };
effect . EffectIndex = fields [ 1 ]. GetInt32 ();
effect . Effect = fields [ 3 ]. GetInt32 ();
effect . EffectAura = fields [ 4 ]. GetInt16 ();
effect . EffectAmplitude = fields [ 5 ]. GetFloat ();
effect . EffectAttributes = fields [ 6 ]. GetInt32 ();
effect . EffectAuraPeriod = fields [ 7 ]. GetInt32 ();
effect . EffectBonusCoefficient = fields [ 8 ]. GetFloat ();
effect . EffectChainAmplitude = fields [ 9 ]. GetFloat ();
effect . EffectChainTargets = fields [ 10 ]. GetInt32 ();
effect . EffectItemType = fields [ 11 ]. GetInt32 ();
effect . EffectMechanic = Mechanics ( fields [ 12 ]. GetInt32 ());
effect . EffectPointsPerResource = fields [ 13 ]. GetFloat ();
effect . EffectPosFacing = fields [ 14 ]. GetFloat ();
effect . EffectRealPointsPerLevel = fields [ 15 ]. GetFloat ();
effect . EffectTriggerSpell = fields [ 16 ]. GetInt32 ();
effect . BonusCoefficientFromAP = fields [ 17 ]. GetFloat ();
effect . PvpMultiplier = fields [ 18 ]. GetFloat ();
effect . Coefficient = fields [ 19 ]. GetFloat ();
effect . Variance = fields [ 20 ]. GetFloat ();
effect . ResourceCoefficient = fields [ 21 ]. GetFloat ();
effect . GroupSizeBasePointsCoefficient = fields [ 22 ]. GetFloat ();
effect . EffectBasePoints = fields [ 23 ]. GetFloat ();
effect . EffectMiscValue [ 0 ] = fields [ 24 ]. GetInt32 ();
effect . EffectMiscValue [ 1 ] = fields [ 25 ]. GetInt32 ();
effect . EffectRadiusIndex [ 0 ] = fields [ 26 ]. GetUInt32 ();
effect . EffectRadiusIndex [ 1 ] = fields [ 27 ]. GetUInt32 ();
effect . EffectSpellClassMask = flag128 ( fields [ 28 ]. GetInt32 (), fields [ 29 ]. GetInt32 (), fields [ 30 ]. GetInt32 (), fields [ 31 ]. GetInt32 ());
effect . ImplicitTarget [ 0 ] = fields [ 32 ]. GetInt16 ();
effect . ImplicitTarget [ 1 ] = fields [ 33 ]. GetInt16 ();
auto existingSpellBounds = _GetSpellInfo ( spellId );
if ( existingSpellBounds . begin () != existingSpellBounds . end ())
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u effext index %u references a regular spell loaded from file. Adding serverside effects to existing spells is not allowed." ,
spellId , uint32 ( difficulty ), effect . EffectIndex );
continue ;
}
if ( difficulty != DIFFICULTY_NONE && ! sDifficultyStore . HasRecord ( difficulty ))
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u effect index %u references non-existing difficulty %u, skipped" ,
spellId , effect . EffectIndex , uint32 ( difficulty ));
continue ;
}
if ( effect . EffectIndex >= MAX_SPELL_EFFECTS )
{
2021-03-14 14:45:01 +01:00
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u has more than %d effects, effect at index %u skipped" ,
spellId , uint32 ( difficulty ), MAX_SPELL_EFFECTS , effect . EffectIndex );
2021-03-12 21:59:10 +01:00
continue ;
}
if ( effect . Effect >= TOTAL_SPELL_EFFECTS )
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u has invalid effect type %u at index %u, skipped" ,
spellId , uint32 ( difficulty ), effect . Effect , effect . EffectIndex );
continue ;
}
if ( effect . EffectAura >= int32 ( TOTAL_AURAS ))
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u has invalid aura type %u at index %u, skipped" ,
spellId , uint32 ( difficulty ), effect . EffectAura , effect . EffectIndex );
continue ;
}
if ( effect . ImplicitTarget [ 0 ] >= TOTAL_SPELL_TARGETS )
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u has invalid targetA type %u at index %u, skipped" ,
spellId , uint32 ( difficulty ), effect . ImplicitTarget [ 0 ], effect . EffectIndex );
continue ;
}
if ( effect . ImplicitTarget [ 1 ] >= TOTAL_SPELL_TARGETS )
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u has invalid targetB type %u at index %u, skipped" ,
spellId , uint32 ( difficulty ), effect . ImplicitTarget [ 1 ], effect . EffectIndex );
continue ;
}
if ( effect . EffectRadiusIndex [ 0 ] && ! sSpellRadiusStore . HasRecord ( effect . EffectRadiusIndex [ 0 ]))
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u has invalid radius id %u at index %u, set to 0" ,
spellId , uint32 ( difficulty ), effect . EffectRadiusIndex [ 0 ], effect . EffectIndex );
}
if ( effect . EffectRadiusIndex [ 1 ] && ! sSpellRadiusStore . HasRecord ( effect . EffectRadiusIndex [ 1 ]))
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u has invalid max radius id %u at index %u, set to 0" ,
spellId , uint32 ( difficulty ), effect . EffectRadiusIndex [ 1 ], effect . EffectIndex );
}
spellEffects [{ spellId , difficulty }]. push_back ( std :: move ( effect ));
} while ( effectsResult -> NextRow ());
}
// 0 1 2 3 4 5 6 7 8
QueryResult spellsResult = WorldDatabase . Query ( "SELECT Id, DifficultyID, CategoryId, Dispel, Mechanic, Attributes, AttributesEx, AttributesEx2, AttributesEx3, "
// 9 10 11 12 13 14 15 16 17 18
"AttributesEx4, AttributesEx5, AttributesEx6, AttributesEx7, AttributesEx8, AttributesEx9, AttributesEx10, AttributesEx11, AttributesEx12, AttributesEx13, "
// 19 20 21 22 23 24 25 26 27
"AttributesEx14, Stances, StancesNot, Targets, TargetCreatureType, RequiresSpellFocus, FacingCasterFlags, CasterAuraState, TargetAuraState, "
// 28 29 30 31 32 33 34
"ExcludeCasterAuraState, ExcludeTargetAuraState, CasterAuraSpell, TargetAuraSpell, ExcludeCasterAuraSpell, ExcludeTargetAuraSpell, CastingTimeIndex, "
// 35 36 37 38 39 40 41
"RecoveryTime, CategoryRecoveryTime, StartRecoveryCategory, StartRecoveryTime, InterruptFlags, AuraInterruptFlags1, AuraInterruptFlags2, "
// 42 43 44 45 46 47 48 49 50 51
"ChannelInterruptFlags1, ChannelInterruptFlags2, ProcFlags, ProcChance, ProcCharges, ProcCooldown, ProcBasePPM, MaxLevel, BaseLevel, SpellLevel, "
// 52 53 54 55 56 57 58 59 60
"DurationIndex, RangeIndex, Speed, LaunchDelay, StackAmount, EquippedItemClass, EquippedItemSubClassMask, EquippedItemInventoryTypeMask, ContentTuningId, "
// 61 62 63 64 65 66 67 68 69 70
"SpellName, ConeAngle, ConeWidth, MaxTargetLevel, MaxAffectedTargets, SpellFamilyName, SpellFamilyFlags1, SpellFamilyFlags2, SpellFamilyFlags3, SpellFamilyFlags4, "
// 71 72 73 74 75
"DmgClass, PreventionType, AreaGroupId, SchoolMask, ChargeCategoryId FROM serverside_spell" );
if ( spellsResult )
{
mServersideSpellNames . reserve ( spellsResult -> GetRowCount ());
do
{
Field * fields = spellsResult -> Fetch ();
uint32 spellId = fields [ 0 ]. GetUInt32 ();
Difficulty difficulty = Difficulty ( fields [ 2 ]. GetUInt32 ());
2021-03-12 22:25:47 +01:00
if ( sSpellNameStore . HasRecord ( spellId ))
{
TC_LOG_ERROR ( "sql.sql" , "Serverside spell %u difficulty %u is already loaded from file. Overriding existing spells is not allowed." ,
spellId , uint32 ( difficulty ));
continue ;
}
2021-03-12 21:59:10 +01:00
mServersideSpellNames . emplace_back ( spellId , fields [ 61 ]. GetString ());
SpellInfo & spellInfo = const_cast < SpellInfo &> ( * mSpellInfoMap . emplace ( & mServersideSpellNames . back (). Name , difficulty , spellEffects [{ spellId , difficulty }]). first );
spellInfo . CategoryId = fields [ 2 ]. GetUInt32 ();
spellInfo . Dispel = fields [ 3 ]. GetUInt32 ();
spellInfo . Mechanic = fields [ 4 ]. GetUInt32 ();
spellInfo . Attributes = fields [ 5 ]. GetUInt32 ();
spellInfo . AttributesEx = fields [ 6 ]. GetUInt32 ();
spellInfo . AttributesEx2 = fields [ 7 ]. GetUInt32 ();
spellInfo . AttributesEx3 = fields [ 8 ]. GetUInt32 ();
spellInfo . AttributesEx4 = fields [ 9 ]. GetUInt32 ();
spellInfo . AttributesEx5 = fields [ 10 ]. GetUInt32 ();
spellInfo . AttributesEx6 = fields [ 11 ]. GetUInt32 ();
spellInfo . AttributesEx7 = fields [ 12 ]. GetUInt32 ();
spellInfo . AttributesEx8 = fields [ 13 ]. GetUInt32 ();
spellInfo . AttributesEx9 = fields [ 14 ]. GetUInt32 ();
spellInfo . AttributesEx10 = fields [ 15 ]. GetUInt32 ();
spellInfo . AttributesEx11 = fields [ 16 ]. GetUInt32 ();
spellInfo . AttributesEx12 = fields [ 17 ]. GetUInt32 ();
spellInfo . AttributesEx13 = fields [ 18 ]. GetUInt32 ();
spellInfo . AttributesEx14 = fields [ 19 ]. GetUInt32 ();
spellInfo . Stances = fields [ 20 ]. GetUInt64 ();
spellInfo . StancesNot = fields [ 21 ]. GetUInt64 ();
spellInfo . Targets = fields [ 22 ]. GetUInt32 ();
spellInfo . TargetCreatureType = fields [ 23 ]. GetUInt32 ();
spellInfo . RequiresSpellFocus = fields [ 24 ]. GetUInt32 ();
spellInfo . FacingCasterFlags = fields [ 25 ]. GetUInt32 ();
spellInfo . CasterAuraState = fields [ 26 ]. GetUInt32 ();
spellInfo . TargetAuraState = fields [ 27 ]. GetUInt32 ();
spellInfo . ExcludeCasterAuraState = fields [ 28 ]. GetUInt32 ();
spellInfo . ExcludeTargetAuraState = fields [ 29 ]. GetUInt32 ();
spellInfo . CasterAuraSpell = fields [ 30 ]. GetUInt32 ();
spellInfo . TargetAuraSpell = fields [ 31 ]. GetUInt32 ();
spellInfo . ExcludeCasterAuraSpell = fields [ 32 ]. GetUInt32 ();
spellInfo . ExcludeTargetAuraSpell = fields [ 33 ]. GetUInt32 ();
spellInfo . CastTimeEntry = sSpellCastTimesStore . LookupEntry ( fields [ 34 ]. GetUInt32 ());
spellInfo . RecoveryTime = fields [ 35 ]. GetUInt32 ();
spellInfo . CategoryRecoveryTime = fields [ 36 ]. GetUInt32 ();
spellInfo . StartRecoveryCategory = fields [ 37 ]. GetUInt32 ();
spellInfo . StartRecoveryTime = fields [ 38 ]. GetUInt32 ();
2021-03-24 23:36:53 +01:00
spellInfo . InterruptFlags = SpellInterruptFlags ( fields [ 39 ]. GetUInt32 ());
2021-03-21 12:05:32 +01:00
spellInfo . AuraInterruptFlags = SpellAuraInterruptFlags ( fields [ 40 ]. GetUInt32 ());
spellInfo . AuraInterruptFlags2 = SpellAuraInterruptFlags2 ( fields [ 41 ]. GetUInt32 ());
spellInfo . ChannelInterruptFlags = SpellAuraInterruptFlags ( fields [ 42 ]. GetUInt32 ());
spellInfo . ChannelInterruptFlags2 = SpellAuraInterruptFlags2 ( fields [ 43 ]. GetUInt32 ());
2021-03-12 21:59:10 +01:00
spellInfo . ProcFlags = fields [ 44 ]. GetUInt32 ();
spellInfo . ProcChance = fields [ 45 ]. GetUInt32 ();
spellInfo . ProcCharges = fields [ 46 ]. GetUInt32 ();
spellInfo . ProcCooldown = fields [ 47 ]. GetUInt32 ();
spellInfo . ProcBasePPM = fields [ 48 ]. GetFloat ();
spellInfo . MaxLevel = fields [ 49 ]. GetUInt32 ();
spellInfo . BaseLevel = fields [ 50 ]. GetUInt32 ();
spellInfo . SpellLevel = fields [ 51 ]. GetUInt32 ();
spellInfo . DurationEntry = sSpellDurationStore . LookupEntry ( fields [ 52 ]. GetUInt32 ());
spellInfo . RangeEntry = sSpellRangeStore . LookupEntry ( fields [ 53 ]. GetUInt32 ());
spellInfo . Speed = fields [ 54 ]. GetFloat ();
spellInfo . LaunchDelay = fields [ 55 ]. GetFloat ();
spellInfo . StackAmount = fields [ 56 ]. GetUInt32 ();
spellInfo . EquippedItemClass = fields [ 57 ]. GetInt32 ();
spellInfo . EquippedItemSubClassMask = fields [ 58 ]. GetInt32 ();
spellInfo . EquippedItemInventoryTypeMask = fields [ 59 ]. GetInt32 ();
spellInfo . ContentTuningId = fields [ 60 ]. GetUInt32 ();
spellInfo . ConeAngle = fields [ 62 ]. GetFloat ();
spellInfo . Width = fields [ 63 ]. GetFloat ();
spellInfo . MaxTargetLevel = fields [ 64 ]. GetUInt32 ();
spellInfo . MaxAffectedTargets = fields [ 65 ]. GetUInt32 ();
spellInfo . SpellFamilyName = fields [ 66 ]. GetUInt32 ();
spellInfo . SpellFamilyFlags = flag128 ( fields [ 67 ]. GetUInt32 (), fields [ 68 ]. GetUInt32 (), fields [ 69 ]. GetUInt32 (), fields [ 70 ]. GetUInt32 ());
spellInfo . DmgClass = fields [ 71 ]. GetUInt32 ();
spellInfo . PreventionType = fields [ 72 ]. GetUInt32 ();
spellInfo . RequiredAreasID = fields [ 73 ]. GetInt32 ();
spellInfo . SchoolMask = fields [ 74 ]. GetUInt32 ();
spellInfo . ChargeCategoryId = fields [ 75 ]. GetUInt32 ();
} while ( spellsResult -> NextRow ());
}
TC_LOG_INFO ( "server.loading" , ">> Loaded " SZFMTD " serverside spells %u ms" , mServersideSpellNames . size (), GetMSTimeDiffToNow ( oldMSTime ));
}
2013-07-23 22:50:28 +02:00
void SpellMgr :: LoadSpellInfoCustomAttributes ()
2011-07-26 23:09:28 +02:00
{
uint32 oldMSTime = getMSTime ();
2014-01-31 04:32:03 +00:00
uint32 oldMSTime2 = oldMSTime ;
QueryResult result = WorldDatabase . Query ( "SELECT entry, attributes FROM spell_custom_attr" );
if ( ! result )
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell custom attributes from DB. DB table `spell_custom_attr` is empty." );
else
{
uint32 count = 0 ;
do
{
Field * fields = result -> Fetch ();
uint32 spellId = fields [ 0 ]. GetUInt32 ();
uint32 attributes = fields [ 1 ]. GetUInt32 ();
2020-06-12 01:29:18 +02:00
auto spells = _GetSpellInfo ( spellId );
if ( spells . begin () == spells . end ())
2014-01-31 04:32:03 +00:00
{
TC_LOG_ERROR ( "sql.sql" , "Table `spell_custom_attr` has wrong spell (entry: %u), ignored." , spellId );
continue ;
}
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : spells )
2017-01-01 03:05:33 +01:00
{
2020-06-12 01:29:18 +02:00
if ( attributes & SPELL_ATTR0_CU_SHARE_DAMAGE )
2017-01-01 03:05:33 +01:00
{
2020-06-12 01:29:18 +02:00
if ( ! spellInfo . HasEffect ( SPELL_EFFECT_SCHOOL_DAMAGE ))
{
TC_LOG_ERROR ( "sql.sql" , "Spell %u listed in table `spell_custom_attr` with SPELL_ATTR0_CU_SHARE_DAMAGE has no SPELL_EFFECT_SCHOOL_DAMAGE, ignored." , spellId );
continue ;
}
2017-01-01 03:05:33 +01:00
}
2014-01-31 04:32:03 +00:00
2020-06-12 01:29:18 +02:00
const_cast < SpellInfo &> ( spellInfo ). AttributesCu |= attributes ;
}
2014-01-31 04:32:03 +00:00
++ count ;
} while ( result -> NextRow ());
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell custom attributes from DB in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime2 ));
}
2015-01-30 23:58:16 +01:00
std :: set < uint32 > talentSpells ;
for ( uint32 i = 0 ; i < sTalentStore . GetNumRows (); ++ i )
if ( TalentEntry const * talentInfo = sTalentStore . LookupEntry ( i ))
talentSpells . insert ( talentInfo -> SpellID );
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
2011-07-26 23:09:28 +02:00
{
2020-06-12 01:29:18 +02:00
SpellInfo * spellInfoMutable = const_cast < SpellInfo *> ( & spellInfo );
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfoMutable -> GetEffects ())
2009-05-15 16:26:00 -05:00
{
2018-04-03 03:14:18 -03:00
// all bleed effects and spells ignore armor
if ( spellInfo . GetEffectMechanicMask ( spellEffectInfo . EffectIndex ) & ( 1 << MECHANIC_BLEED ))
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_IGNORE_ARMOR ;
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . ApplyAuraName )
2011-07-26 23:09:28 +02:00
{
2017-02-04 23:50:32 +01:00
case SPELL_AURA_MOD_POSSESS :
case SPELL_AURA_MOD_CONFUSE :
case SPELL_AURA_MOD_CHARM :
case SPELL_AURA_AOE_CHARM :
case SPELL_AURA_MOD_FEAR :
case SPELL_AURA_MOD_STUN :
2020-06-12 01:29:18 +02:00
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_AURA_CC ;
2017-02-04 23:50:32 +01:00
break ;
2021-09-04 15:13:15 +02:00
default :
break ;
2011-07-26 23:09:28 +02:00
}
2011-08-26 09:41:33 +02:00
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . ApplyAuraName )
2021-03-06 23:47:30 +01:00
{
case SPELL_AURA_OPEN_STABLE : // No point in saving this, since the stable dialog can't be open on aura load anyway.
// Auras that require both caster & target to be in world cannot be saved
case SPELL_AURA_CONTROL_VEHICLE :
case SPELL_AURA_BIND_SIGHT :
case SPELL_AURA_MOD_POSSESS :
case SPELL_AURA_MOD_POSSESS_PET :
case SPELL_AURA_MOD_CHARM :
case SPELL_AURA_AOE_CHARM :
// Controlled by Battleground
case SPELL_AURA_BATTLEGROUND_PLAYER_POSITION :
case SPELL_AURA_BATTLEGROUND_PLAYER_POSITION_FACTIONAL :
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_AURA_CANNOT_BE_SAVED ;
break ;
2021-09-04 15:13:15 +02:00
default :
break ;
2021-03-06 23:47:30 +01:00
}
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . Effect )
2018-02-11 22:22:04 -03:00
{
case SPELL_EFFECT_SCHOOL_DAMAGE :
case SPELL_EFFECT_HEALTH_LEECH :
case SPELL_EFFECT_HEAL :
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL :
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE :
case SPELL_EFFECT_WEAPON_DAMAGE :
case SPELL_EFFECT_POWER_BURN :
case SPELL_EFFECT_HEAL_MECHANICAL :
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG :
case SPELL_EFFECT_HEAL_PCT :
case SPELL_EFFECT_DAMAGE_FROM_MAX_HEALTH_PCT :
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_CAN_CRIT ;
break ;
2021-09-04 15:13:15 +02:00
default :
break ;
2018-02-11 22:22:04 -03:00
}
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . Effect )
2009-05-15 16:26:00 -05:00
{
case SPELL_EFFECT_SCHOOL_DAMAGE :
case SPELL_EFFECT_WEAPON_DAMAGE :
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL :
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG :
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE :
case SPELL_EFFECT_HEAL :
2020-06-12 01:29:18 +02:00
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_DIRECT_DAMAGE ;
2009-05-15 16:26:00 -05:00
break ;
2011-08-21 12:00:36 +02:00
case SPELL_EFFECT_POWER_DRAIN :
case SPELL_EFFECT_POWER_BURN :
case SPELL_EFFECT_HEAL_MAX_HEALTH :
case SPELL_EFFECT_HEALTH_LEECH :
case SPELL_EFFECT_HEAL_PCT :
case SPELL_EFFECT_ENERGIZE_PCT :
case SPELL_EFFECT_ENERGIZE :
case SPELL_EFFECT_HEAL_MECHANICAL :
2020-06-12 01:29:18 +02:00
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_NO_INITIAL_THREAT ;
2011-08-21 12:00:36 +02:00
break ;
2009-05-15 16:26:00 -05:00
case SPELL_EFFECT_CHARGE :
2010-07-19 19:22:05 +02:00
case SPELL_EFFECT_CHARGE_DEST :
2009-05-15 16:26:00 -05:00
case SPELL_EFFECT_JUMP :
2010-07-19 21:24:09 +02:00
case SPELL_EFFECT_JUMP_DEST :
2009-08-27 20:14:50 -05:00
case SPELL_EFFECT_LEAP_BACK :
2020-06-12 01:29:18 +02:00
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_CHARGE ;
2009-05-15 16:26:00 -05:00
break ;
2010-01-13 18:22:31 +01:00
case SPELL_EFFECT_PICKPOCKET :
2020-06-12 01:29:18 +02:00
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_PICKPOCKET ;
2009-05-15 16:26:00 -05:00
break ;
2010-12-27 22:27:11 +01:00
case SPELL_EFFECT_ENCHANT_ITEM :
case SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY :
case SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC :
case SPELL_EFFECT_ENCHANT_HELD_ITEM :
{
// only enchanting profession enchantments procs can stack
2020-06-12 01:29:18 +02:00
if ( IsPartOfSkillLine ( SKILL_ENCHANTING , spellInfo . Id ))
2010-12-27 22:27:11 +01:00
{
2021-09-04 15:13:15 +02:00
uint32 enchantId = spellEffectInfo . MiscValue ;
2011-06-28 23:48:59 +07:00
SpellItemEnchantmentEntry const * enchant = sSpellItemEnchantmentStore . LookupEntry ( enchantId );
2013-08-24 22:56:29 +01:00
if ( ! enchant )
break ;
2010-12-27 22:27:11 +01:00
for ( uint8 s = 0 ; s < MAX_ITEM_ENCHANTMENT_EFFECTS ; ++ s )
{
2014-11-10 02:51:55 +02:00
if ( enchant -> Effect [ s ] != ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL )
2010-12-27 22:27:11 +01:00
continue ;
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & procInfo : _GetSpellInfo ( enchant -> EffectArg [ s ]))
{
// if proced directly from enchantment, not via proc aura
// NOTE: Enchant Weapon - Blade Ward also has proc aura spell and is proced directly
// however its not expected to stack so this check is good
if ( procInfo . HasAura ( SPELL_AURA_PROC_TRIGGER_SPELL ))
continue ;
2010-12-27 22:27:11 +01:00
2020-06-12 01:29:18 +02:00
const_cast < SpellInfo &> ( procInfo ). AttributesCu |= SPELL_ATTR0_CU_ENCHANT_PROC ;
}
2010-12-27 22:27:11 +01:00
}
}
break ;
}
2021-09-04 15:13:15 +02:00
default :
break ;
2009-05-15 16:26:00 -05:00
}
}
2009-10-17 15:51:44 -07:00
2017-02-04 23:50:32 +01:00
// spells ignoring hit result should not be binary
2020-06-12 01:29:18 +02:00
if ( ! spellInfoMutable -> HasAttribute ( SPELL_ATTR3_IGNORE_HIT_RESULT ))
2017-02-04 23:50:32 +01:00
{
bool setFlag = false ;
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfoMutable -> GetEffects ())
2017-02-04 23:50:32 +01:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsEffect ())
2017-02-04 23:50:32 +01:00
{
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . Effect )
2017-02-04 23:50:32 +01:00
{
2021-09-04 15:13:15 +02:00
case SPELL_EFFECT_SCHOOL_DAMAGE :
case SPELL_EFFECT_WEAPON_DAMAGE :
case SPELL_EFFECT_WEAPON_DAMAGE_NOSCHOOL :
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG :
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE :
case SPELL_EFFECT_TRIGGER_SPELL :
case SPELL_EFFECT_TRIGGER_SPELL_WITH_VALUE :
2017-02-04 23:50:32 +01:00
break ;
2021-09-04 15:13:15 +02:00
case SPELL_EFFECT_PERSISTENT_AREA_AURA :
case SPELL_EFFECT_APPLY_AURA :
case SPELL_EFFECT_APPLY_AREA_AURA_PARTY :
case SPELL_EFFECT_APPLY_AREA_AURA_RAID :
case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND :
case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY :
case SPELL_EFFECT_APPLY_AREA_AURA_PET :
case SPELL_EFFECT_APPLY_AREA_AURA_OWNER :
case SPELL_EFFECT_APPLY_AURA_ON_PET :
case SPELL_EFFECT_APPLY_AREA_AURA_SUMMONS :
case SPELL_EFFECT_APPLY_AREA_AURA_PARTY_NONRANDOM :
if ( spellEffectInfo . ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE ||
spellEffectInfo . ApplyAuraName == SPELL_AURA_PERIODIC_DAMAGE_PERCENT ||
spellEffectInfo . ApplyAuraName == SPELL_AURA_DUMMY ||
spellEffectInfo . ApplyAuraName == SPELL_AURA_PERIODIC_LEECH ||
spellEffectInfo . ApplyAuraName == SPELL_AURA_PERIODIC_HEALTH_FUNNEL ||
spellEffectInfo . ApplyAuraName == SPELL_AURA_PERIODIC_DUMMY )
break ;
2019-03-30 17:48:36 +01:00
/* fallthrough */
2021-09-04 15:13:15 +02:00
default :
{
// No value and not interrupt cast or crowd control without SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY flag
if ( ! spellEffectInfo . CalcValue () && ! (( spellEffectInfo . Effect == SPELL_EFFECT_INTERRUPT_CAST || spellInfoMutable -> HasAttribute ( SPELL_ATTR0_CU_AURA_CC )) && ! spellInfoMutable -> HasAttribute ( SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY )))
break ;
2017-02-04 23:50:32 +01:00
2021-09-04 15:13:15 +02:00
// Sindragosa Frost Breath
if ( spellInfoMutable -> Id == 69649 || spellInfoMutable -> Id == 71056 || spellInfoMutable -> Id == 71057 || spellInfoMutable -> Id == 71058 || spellInfoMutable -> Id == 73061 || spellInfoMutable -> Id == 73062 || spellInfoMutable -> Id == 73063 || spellInfoMutable -> Id == 73064 )
break ;
2017-02-04 23:50:32 +01:00
2021-09-04 15:13:15 +02:00
// Frostbolt
if ( spellInfoMutable -> SpellFamilyName == SPELLFAMILY_MAGE && ( spellInfoMutable -> SpellFamilyFlags [ 0 ] & 0x20 ))
break ;
2017-02-04 23:50:32 +01:00
2021-09-04 15:13:15 +02:00
// Frost Fever
if ( spellInfoMutable -> Id == 55095 )
break ;
2017-02-04 23:50:32 +01:00
2021-09-04 15:13:15 +02:00
// Haunt
if ( spellInfoMutable -> SpellFamilyName == SPELLFAMILY_WARLOCK && ( spellInfoMutable -> SpellFamilyFlags [ 1 ] & 0x40000 ))
break ;
2017-02-04 23:50:32 +01:00
2021-09-04 15:13:15 +02:00
setFlag = true ;
break ;
}
2017-02-04 23:50:32 +01:00
}
if ( setFlag )
{
2020-06-12 01:29:18 +02:00
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_BINARY_SPELL ;
2017-02-04 23:50:32 +01:00
break ;
}
}
}
}
// Remove normal school mask to properly calculate damage
2020-06-12 01:29:18 +02:00
if (( spellInfoMutable -> SchoolMask & SPELL_SCHOOL_MASK_NORMAL ) && ( spellInfoMutable -> SchoolMask & SPELL_SCHOOL_MASK_MAGIC ))
2017-02-04 23:50:32 +01:00
{
2020-06-12 01:29:18 +02:00
spellInfoMutable -> SchoolMask &= ~ SPELL_SCHOOL_MASK_NORMAL ;
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_SCHOOLMASK_NORMAL_WITH_MAGIC ;
2017-02-04 23:50:32 +01:00
}
2018-01-23 11:40:15 -03:00
spellInfoMutable -> _InitializeSpellPositivity ();
2020-06-12 01:29:18 +02:00
if ( talentSpells . count ( spellInfoMutable -> Id ))
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_IS_TALENT ;
2015-01-30 23:58:16 +01:00
2020-06-21 15:59:43 +02:00
if ( G3D :: fuzzyNe ( spellInfoMutable -> Width , 0.0f ))
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_CONE_LINE ;
2020-06-12 01:29:18 +02:00
switch ( spellInfoMutable -> SpellFamilyName )
2017-02-04 23:50:32 +01:00
{
case SPELLFAMILY_WARRIOR :
// Shout / Piercing Howl
2020-06-12 01:29:18 +02:00
if ( spellInfoMutable -> SpellFamilyFlags [ 0 ] & 0x20000 /* || spellInfo->SpellFamilyFlags[1] & 0x20*/ )
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_AURA_CC ;
2017-02-04 23:50:32 +01:00
break ;
case SPELLFAMILY_DRUID :
// Roar
2020-06-12 01:29:18 +02:00
if ( spellInfoMutable -> SpellFamilyFlags [ 0 ] & 0x8 )
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_AURA_CC ;
2017-02-04 23:50:32 +01:00
break ;
case SPELLFAMILY_GENERIC :
// Stoneclaw Totem effect
2020-06-12 01:29:18 +02:00
if ( spellInfoMutable -> Id == 5729 )
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_AURA_CC ;
2017-02-04 23:50:32 +01:00
break ;
default :
break ;
}
2013-07-29 19:00:34 +02:00
2020-06-12 01:29:18 +02:00
spellInfoMutable -> _InitializeExplicitTargetMask ();
2018-12-31 21:35:55 +01:00
if ( spellInfoMutable -> Speed > 0.0f )
{
auto visualNeedsAmmo = []( SpellXSpellVisualEntry const * spellXspellVisual )
{
SpellVisualEntry const * spellVisual = sSpellVisualStore . LookupEntry ( spellXspellVisual -> SpellVisualID );
if ( ! spellVisual )
return false ;
std :: vector < SpellVisualMissileEntry const *> const * spellVisualMissiles = sDB2Manager . GetSpellVisualMissiles ( spellVisual -> SpellVisualMissileSetID );
if ( ! spellVisualMissiles )
return false ;
for ( SpellVisualMissileEntry const * spellVisualMissile : * spellVisualMissiles )
{
SpellVisualEffectNameEntry const * spellVisualEffectName = sSpellVisualEffectNameStore . LookupEntry ( spellVisualMissile -> SpellVisualEffectNameID );
if ( ! spellVisualEffectName )
continue ;
SpellVisualEffectNameType type = SpellVisualEffectNameType ( spellVisualEffectName -> Type );
if ( type == SpellVisualEffectNameType :: UnitAmmoBasic || type == SpellVisualEffectNameType :: UnitAmmoPreferred )
return true ;
}
return false ;
};
for ( SpellXSpellVisualEntry const * spellXspellVisual : spellInfoMutable -> _visuals )
{
if ( visualNeedsAmmo ( spellXspellVisual ))
{
spellInfoMutable -> AttributesCu |= SPELL_ATTR0_CU_NEEDS_AMMO_DATA ;
break ;
}
}
}
2011-07-26 23:09:28 +02:00
}
2018-02-11 22:52:28 -03:00
// addition for binary spells, omit spells triggering other spells
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
2017-02-04 23:50:32 +01:00
{
2020-06-12 01:29:18 +02:00
SpellInfo * spellInfoMutable = const_cast < SpellInfo *> ( & spellInfo );
2018-02-11 22:22:04 -03:00
if ( ! spellInfoMutable -> HasAttribute ( SPELL_ATTR0_CU_BINARY_SPELL ))
2017-02-04 23:50:32 +01:00
{
2018-02-11 22:22:04 -03:00
bool allNonBinary = true ;
bool overrideAttr = false ;
2021-09-04 15:13:15 +02:00
for ( SpellEffectInfo const & spellEffectInfo : spellInfoMutable -> GetEffects ())
2017-02-04 23:50:32 +01:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsAura () && spellEffectInfo . TriggerSpell )
2017-02-04 23:50:32 +01:00
{
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . ApplyAuraName )
2017-02-04 23:50:32 +01:00
{
2018-02-11 22:22:04 -03:00
case SPELL_AURA_PERIODIC_TRIGGER_SPELL :
case SPELL_AURA_PERIODIC_TRIGGER_SPELL_WITH_VALUE :
2021-09-04 15:13:15 +02:00
if ( SpellInfo const * triggerSpell = sSpellMgr -> GetSpellInfo ( spellEffectInfo . TriggerSpell , DIFFICULTY_NONE ))
2018-02-11 22:22:04 -03:00
{
overrideAttr = true ;
if ( triggerSpell -> HasAttribute ( SPELL_ATTR0_CU_BINARY_SPELL ))
allNonBinary = false ;
}
break ;
default :
break ;
2017-02-04 23:50:32 +01:00
}
}
}
2018-02-11 22:22:04 -03:00
if ( overrideAttr && allNonBinary )
spellInfoMutable -> AttributesCu &= ~ SPELL_ATTR0_CU_BINARY_SPELL ;
2017-02-04 23:50:32 +01:00
}
2018-02-11 22:22:04 -03:00
// remove attribute from spells that can't crit
if ( spellInfo . HasAttribute ( SPELL_ATTR0_CU_CAN_CRIT ))
if ( spellInfo . HasAttribute ( SPELL_ATTR2_CANT_CRIT ))
spellInfoMutable -> AttributesCu &= ~ SPELL_ATTR0_CU_CAN_CRIT ;
2017-02-04 23:50:32 +01:00
}
2018-04-01 20:56:08 -06:00
// add custom attribute to liquid auras
for ( LiquidTypeEntry const * liquid : sLiquidTypeStore )
{
if ( liquid -> SpellID )
for ( SpellInfo const & spellInfo : _GetSpellInfo ( liquid -> SpellID ))
const_cast < SpellInfo &> ( spellInfo ). AttributesCu |= SPELL_ATTR0_CU_AURA_CANNOT_BE_SAVED ;
}
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded SpellInfo custom attributes in %u ms" , GetMSTimeDiffToNow ( oldMSTime ));
2011-07-26 23:09:28 +02:00
}
2017-04-20 21:57:25 +02:00
inline void ApplySpellFix ( std :: initializer_list < uint32 > spellIds , void ( * fix )( SpellInfo * ))
{
for ( uint32 spellId : spellIds )
{
2020-06-12 01:29:18 +02:00
auto range = _GetSpellInfo ( spellId );
if ( range . begin () == range . end ())
2017-04-20 21:57:25 +02:00
{
TC_LOG_ERROR ( "server.loading" , "Spell info correction specified for non-existing spell %u" , spellId );
continue ;
}
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : range )
fix ( & const_cast < SpellInfo &> ( spellInfo ));
2017-04-20 21:57:25 +02:00
}
}
2021-03-16 00:07:34 +01:00
inline void ApplySpellEffectFix ( SpellInfo * spellInfo , SpellEffIndex effectIndex , void ( * fix )( SpellEffectInfo * ))
{
2021-09-04 15:13:15 +02:00
if ( spellInfo -> GetEffects (). size () <= effectIndex )
2021-03-16 00:07:34 +01:00
{
TC_LOG_ERROR ( "server.loading" , "Spell effect info correction specified for non-existing effect %u of spell %u" , uint32 ( effectIndex ), spellInfo -> Id );
return ;
}
2021-09-04 15:13:15 +02:00
fix ( const_cast < SpellEffectInfo *> ( & spellInfo -> GetEffect ( effectIndex )));
2021-03-16 00:07:34 +01:00
}
2012-12-24 12:34:46 -05:00
void SpellMgr :: LoadSpellInfoCorrections ()
2011-07-26 23:09:28 +02:00
{
uint32 oldMSTime = getMSTime ();
2017-12-14 11:42:42 -03:00
// Some spells have no amplitude set
{
ApplySpellFix ({
6727 , // Poison Mushroom
7331 , // Healing Aura (TEST) (Rank 1)
/*
30400, // Nether Beam - Perseverance
Blizzlike to have it disabled? DBC says:
"This is currently turned off to increase performance. Enable this to make it fire more frequently."
*/
34589 , // Dangerous Water
52562 , // Arthas Zombie Catcher
57550 , // Tirion Aggro
65755
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraPeriod = 1 * IN_MILLISECONDS ;
});
2017-12-14 11:42:42 -03:00
});
ApplySpellFix ({
24707 , // Food
26263 , // Dim Sum
2021-03-16 00:07:34 +01:00
29055 // Refreshing Red Apple
2017-12-14 11:42:42 -03:00
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraPeriod = 1 * IN_MILLISECONDS ;
});
});
// Karazhan - Chess NPC AI, action timer
ApplySpellFix ({ 37504 }, []( SpellInfo * spellInfo )
{
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraPeriod = 5 * IN_MILLISECONDS ;
});
2017-12-14 11:42:42 -03:00
});
// Vomit
ApplySpellFix ({ 43327 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraPeriod = 1 * IN_MILLISECONDS ;
});
2017-12-14 11:42:42 -03:00
});
}
2017-12-15 00:14:55 -03:00
// specific code for cases with no trigger spell provided in field
{
// Brood Affliction: Bronze
ApplySpellFix ({ 23170 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 23171 ;
});
2017-12-15 00:14:55 -03:00
});
// Feed Captured Animal
ApplySpellFix ({ 29917 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 29916 ;
});
2017-12-15 00:14:55 -03:00
});
// Remote Toy
ApplySpellFix ({ 37027 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 37029 ;
});
2017-12-15 00:14:55 -03:00
});
// Eye of Grillok
ApplySpellFix ({ 38495 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 38530 ;
});
2017-12-15 00:14:55 -03:00
});
// Tear of Azzinoth Summon Channel - it's not really supposed to do anything, and this only prevents the console spam
ApplySpellFix ({ 39857 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 39856 ;
});
2017-12-15 00:14:55 -03:00
});
// Personalized Weather
ApplySpellFix ({ 46736 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 46737 ;
spellEffectInfo -> ApplyAuraName = SPELL_AURA_PERIODIC_TRIGGER_SPELL ;
});
2017-12-15 00:14:55 -03:00
});
}
2018-01-13 06:45:21 -03:00
// Allows those to crit
ApplySpellFix ({
379 , // Earth Shield
71607 , // Item - Bauble of True Blood 10m
71646 , // Item - Bauble of True Blood 25m
71610 , // Item - Althor's Abacus trigger 10m
71641 // Item - Althor's Abacus trigger 25m
}, []( SpellInfo * spellInfo )
{
// We need more spells to find a general way (if there is any)
spellInfo -> DmgClass = SPELL_DAMAGE_CLASS_MAGIC ;
});
2017-04-20 21:57:25 +02:00
ApplySpellFix ({
63026 , // Summon Aspirant Test NPC (HACK: Target shouldn't be changed)
63137 // Summon Valiant Test (HACK: Target shouldn't be changed; summon position should be untied from spell destination)
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_DEST_DB );
});
2017-04-20 21:57:25 +02:00
});
// Summon Skeletons
ApplySpellFix ({ 52611 , 52612 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> MiscValueB = 64 ;
});
2017-04-20 21:57:25 +02:00
});
ApplySpellFix ({
40244 , // Simon Game Visual
40245 , // Simon Game Visual
40246 , // Simon Game Visual
40247 , // Simon Game Visual
42835 // Spout, remove damage effect, only anim is needed
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
2021-09-04 15:13:15 +02:00
spellEffectInfo -> Effect = SPELL_EFFECT_NONE ;
2021-03-16 00:07:34 +01:00
});
2017-04-20 21:57:25 +02:00
});
ApplySpellFix ({
63665 , // Charge (Argent Tournament emote on riders)
31298 , // Sleep (needs target selection script)
51904 , // Summon Ghouls On Scarlet Crusade (this should use conditions table, script for this spell needs to be fixed)
68933 , // Wrath of Air Totem rank 2 (Aura)
29200 // Purify Helboar Meat
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_CASTER );
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ();
});
2017-04-20 21:57:25 +02:00
});
2017-02-25 04:43:11 -03:00
ApplySpellFix ({
56690 , // Thrust Spear
60586 , // Mighty Spear Thrust
60776 , // Claw Swipe
60881 , // Fatal Strike
60864 // Jaws of Death
}, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx4 |= SPELL_ATTR4_FIXED_DAMAGE ;
});
2017-04-20 21:57:25 +02:00
// Howl of Azgalor
ApplySpellFix ({ 31344 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_100_YARDS ); // 100yards instead of 50000?!
});
2017-04-20 21:57:25 +02:00
});
ApplySpellFix ({
42818 , // Headless Horseman - Wisp Flight Port
42821 // Headless Horseman - Wisp Flight Missile
}, []( SpellInfo * spellInfo )
{
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 6 ); // 100 yards
});
// They Must Burn Bomb Aura (self)
ApplySpellFix ({ 36350 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 36325 ; // They Must Burn Bomb Drop (DND)
});
2017-04-20 21:57:25 +02:00
});
// Execute
ApplySpellFix ({ 5308 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_CANT_TRIGGER_PROC ;
});
ApplySpellFix ({
31347 , // Doom
36327 , // Shoot Arcane Explosion Arrow
39365 , // Thundering Storm
41071 , // Raise Dead (HACK)
42442 , // Vengeance Landing Cannonfire
42611 , // Shoot
44978 , // Wild Magic
45001 , // Wild Magic
45002 , // Wild Magic
45004 , // Wild Magic
45006 , // Wild Magic
45010 , // Wild Magic
45761 , // Shoot Gun
45863 , // Cosmetic - Incinerate to Random Target
48246 , // Ball of Flame
41635 , // Prayer of Mending
44869 , // Spectral Blast
45027 , // Revitalize
45976 , // Muru Portal Channel
52124 , // Sky Darkener Assault
52479 , // Gift of the Harvester
61588 , // Blazing Harpoon
55479 , // Force Obedience
28560 , // Summon Blizzard (Sapphiron)
53096 , // Quetz'lun's Judgment
70743 , // AoD Special
70614 , // AoD Special - Vegard
4020 , // Safirdrang's Chill
52438 , // Summon Skittering Swarmer (Force Cast)
52449 , // Summon Skittering Infector (Force Cast)
53609 , // Summon Anub'ar Assassin (Force Cast)
2017-12-30 20:28:41 +01:00
53457 , // Summon Impale Trigger (AoE)
2018-01-30 13:04:25 +01:00
45907 , // Torch Target Picker
52953 , // Torch
2019-05-07 21:36:29 +02:00
58121 , // Torch
2019-06-20 20:57:57 +02:00
43109 , // Throw Torch
58552 , // Return to Orgrimmar
2019-06-30 22:46:50 +02:00
58533 , // Return to Stormwind
21855 // Challenge Flag
2017-04-20 21:57:25 +02:00
}, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 1 ;
});
2017-12-30 20:28:41 +01:00
ApplySpellFix ({
36384 // Skartax Purple Beam
}, []( SpellInfo * spellInfo )
2017-04-20 21:57:25 +02:00
{
spellInfo -> MaxAffectedTargets = 2 ;
});
ApplySpellFix ({
28542 , // Life Drain - Sapphiron
29213 , // Curse of the Plaguebringer - Noth
29576 , // Multi-Shot
37790 , // Spread Shot
39992 , // Needle Spine
40816 , // Saber Lash
41303 , // Soul Drain
41376 , // Spite
45248 , // Shadow Blades
46771 , // Flame Sear
66588 // Flaming Spear
}, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 3 ;
});
ApplySpellFix ({
38310 , // Multi-Shot
53385 // Divine Storm (Damage)
}, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 4 ;
});
ApplySpellFix ({
42005 , // Bloodboil
38296 , // Spitfire Totem
37676 , // Insidious Whisper
46008 , // Negative Energy
45641 , // Fire Bloom
55665 , // Life Drain - Sapphiron (H)
28796 // Poison Bolt Volly - Faerlina
}, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 5 ;
});
// Curse of the Plaguebringer - Noth (H)
ApplySpellFix ({ 54835 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 8 ;
});
ApplySpellFix ({
40827 , // Sinful Beam
40859 , // Sinister Beam
40860 , // Vile Beam
40861 , // Wicked Beam
54098 // Poison Bolt Volly - Faerlina (H)
}, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 10 ;
});
// Unholy Frenzy
ApplySpellFix ({ 50312 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 15 ;
});
// Murmur's Touch
ApplySpellFix ({ 33711 , 38794 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 1 ;
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TriggerSpell = 33760 ;
});
2017-04-20 21:57:25 +02:00
});
// Fingers of Frost
ApplySpellFix ({ 44544 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> SpellClassMask [ 0 ] |= 0x20000 ;
});
2017-04-20 21:57:25 +02:00
});
2017-01-01 21:11:15 -02:00
ApplySpellFix ({
52212 , // Death and Decay
41485 , // Deadly Poison - Black Temple
41487 // Envenom - Black Temple
}, []( SpellInfo * spellInfo )
2016-10-30 00:16:10 -03:00
{
spellInfo -> AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE ;
});
2017-04-20 21:57:25 +02:00
// Oscillation Field
ApplySpellFix ({ 37408 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS ;
});
// Crafty's Ultra-Advanced Proto-Typical Shortening Blaster
ApplySpellFix ({ 51912 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraPeriod = 3000 ;
});
2017-04-20 21:57:25 +02:00
});
// Nether Portal - Perseverence
ApplySpellFix ({ 30421 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_2 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> BasePoints += 30000 ;
});
2017-04-20 21:57:25 +02:00
});
// Parasitic Shadowfiend Passive
ApplySpellFix ({ 41913 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraName = SPELL_AURA_DUMMY ; // proc debuff, and summon infinite fiends
});
2017-04-20 21:57:25 +02:00
});
ApplySpellFix ({
27892 , // To Anchor 1
27928 , // To Anchor 1
27935 , // To Anchor 1
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_10_YARDS );
});
2017-04-20 21:57:25 +02:00
});
// Wrath of the Plaguebringer
ApplySpellFix ({ 29214 , 54836 }, []( SpellInfo * spellInfo )
{
// target allys instead of enemies, target A is src_caster, spells with effect like that have ally target
// this is the only known exception, probably just wrong data
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ( TARGET_UNIT_SRC_AREA_ALLY );
});
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ( TARGET_UNIT_SRC_AREA_ALLY );
});
2017-04-20 21:57:25 +02:00
});
// Vampiric Embrace
ApplySpellFix ({ 15290 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_NO_INITIAL_AGGRO ;
});
// Earthbind Totem (instant pulse)
ApplySpellFix ({ 6474 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx5 |= SPELL_ATTR5_START_PERIODIC_AT_APPLY ;
});
ApplySpellFix ({
70728 , // Exploit Weakness (needs target selection script)
70840 // Devious Minds (needs target selection script)
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_CASTER );
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ( TARGET_UNIT_PET );
});
2017-04-20 21:57:25 +02:00
});
// Ride Carpet
ApplySpellFix ({ 45602 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> BasePoints = 0 ; // force seat 0, vehicle doesn't have the required seat flags for "no seat specified (-1)"
});
2017-04-20 21:57:25 +02:00
});
// Easter Lay Noblegarden Egg Aura - Interrupt flags copied from aura which this aura is linked with
ApplySpellFix ({ 61719 }, []( SpellInfo * spellInfo )
{
2021-03-21 12:05:32 +01:00
spellInfo -> AuraInterruptFlags = SpellAuraInterruptFlags :: HostileActionReceived | SpellAuraInterruptFlags :: Damage ;
2017-04-20 21:57:25 +02:00
});
ApplySpellFix ({
71838 , // Drain Life - Bryntroll Normal
71839 // Drain Life - Bryntroll Heroic
}, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx2 |= SPELL_ATTR2_CANT_CRIT ;
});
ApplySpellFix ({
2019-03-05 19:13:16 +01:00
51597 , // Summon Scourged Captive
2017-04-20 21:57:25 +02:00
56606 , // Ride Jokkum
61791 // Ride Vehicle (Yogg-Saron)
}, []( SpellInfo * spellInfo )
{
/// @todo: remove this when basepoints of all Ride Vehicle auras are calculated correctly
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> BasePoints = 1 ;
});
2017-04-20 21:57:25 +02:00
});
2019-03-05 19:13:16 +01:00
// Summon Scourged Captive
ApplySpellFix ({ 51597 }, []( SpellInfo * spellInfo )
{
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> Scaling . Variance = 0.0f ;
});
});
2017-04-20 21:57:25 +02:00
// Black Magic
ApplySpellFix ({ 59630 }, []( SpellInfo * spellInfo )
{
spellInfo -> Attributes |= SPELL_ATTR0_PASSIVE ;
});
// Paralyze
ApplySpellFix ({ 48278 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS ;
});
ApplySpellFix ({
51798 , // Brewfest - Relay Race - Intro - Quest Complete
47134 // Quest Complete
}, []( SpellInfo * spellInfo )
{
2017-11-23 22:21:36 +01:00
//! HACK: This spell break quest complete for alliance and on retail not used
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
2021-09-04 15:13:15 +02:00
spellEffectInfo -> Effect = SPELL_EFFECT_NONE ;
2021-03-16 00:07:34 +01:00
});
2017-04-20 21:57:25 +02:00
});
// Siege Cannon (Tol Barad)
ApplySpellFix ({ 85123 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_200_YARDS );
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_SRC_AREA_ENTRY );
});
2017-04-20 21:57:25 +02:00
});
// Gathering Storms
ApplySpellFix ({ 198300 }, []( SpellInfo * spellInfo )
{
spellInfo -> ProcCharges = 1 ; // override proc charges, has 0 (unlimited) in db2
});
ApplySpellFix ({
2019-07-05 23:46:55 +02:00
15538 , // Gout of Flame
2017-04-20 21:57:25 +02:00
42490 , // Energized!
42492 , // Cast Energized
43115 // Plague Vial
}, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx |= SPELL_ATTR1_NO_THREAT ;
});
// Test Ribbon Pole Channel
ApplySpellFix ({ 29726 }, []( SpellInfo * spellInfo )
{
2021-03-21 12:05:32 +01:00
spellInfo -> ChannelInterruptFlags &= ~ SpellAuraInterruptFlags :: Action ;
2017-04-20 21:57:25 +02:00
});
// Sic'em
ApplySpellFix ({ 42767 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_NEARBY_ENTRY );
});
2017-04-20 21:57:25 +02:00
});
2017-08-27 14:59:18 +02:00
// Burn Body
ApplySpellFix ({ 42793 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_2 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> MiscValue = 24008 ; // Fallen Combatant
});
2017-08-27 14:59:18 +02:00
});
2017-09-05 22:05:29 +02:00
// Gift of the Naaru (priest and monk variants)
ApplySpellFix ({ 59544 , 121093 }, []( SpellInfo * spellInfo )
{
spellInfo -> SpellFamilyFlags [ 2 ] = 0x80000000 ;
});
2016-12-29 23:28:08 -03:00
ApplySpellFix ({
50661 , // Weakened Resolve
2017-01-19 14:09:27 -03:00
68979 , // Unleashed Souls
48714 , // Compelled
7853 , // The Art of Being a Water Terror: Force Cast on Player
2016-12-29 23:28:08 -03:00
}, []( SpellInfo * spellInfo )
2016-11-09 00:58:46 -03:00
{
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 13 ); // 50000yd
});
2019-10-28 19:33:26 +01:00
ApplySpellFix ({
44327 , // Trained Rock Falcon/Hawk Hunting
44408 // Trained Rock Falcon/Hawk Hunting
}, []( SpellInfo * spellInfo )
{
spellInfo -> Speed = 0.f ;
});
2017-12-16 19:00:19 +01:00
// Summon Corpse Scarabs
ApplySpellFix ({ 28864 , 29105 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_10_YARDS );
});
2017-12-16 19:00:19 +01:00
});
2019-05-18 01:31:41 +02:00
ApplySpellFix ({
37851 , // Tag Greater Felfire Diemetradon
37918 // Arcano-pince
}, []( SpellInfo * spellInfo )
2019-05-17 12:08:20 +02:00
{
spellInfo -> RecoveryTime = 3000 ;
});
// Jormungar Strike
ApplySpellFix ({ 56513 }, []( SpellInfo * spellInfo )
{
spellInfo -> RecoveryTime = 2000 ;
});
ApplySpellFix ({
54997 , // Cast Net (tooltip says 10s but sniffs say 6s)
56524 // Acid Breath
}, []( SpellInfo * spellInfo )
{
spellInfo -> RecoveryTime = 6000 ;
});
ApplySpellFix ({
47911 , // EMP
48620 , // Wing Buffet
51752 // Stampy's Stompy-Stomp
}, []( SpellInfo * spellInfo )
{
spellInfo -> RecoveryTime = 10000 ;
});
ApplySpellFix ({
37727 , // Touch of Darkness
54996 // Ice Slick (tooltip says 20s but sniffs say 12s)
}, []( SpellInfo * spellInfo )
{
spellInfo -> RecoveryTime = 12000 ;
});
// Signal Helmet to Attack
ApplySpellFix ({ 51748 }, []( SpellInfo * spellInfo )
{
spellInfo -> RecoveryTime = 15000 ;
});
2019-05-18 01:31:41 +02:00
ApplySpellFix ({
51756 , // Charge
37919 , //Arcano-dismantle
37917 //Arcano-Cloak
}, []( SpellInfo * spellInfo )
2019-05-17 12:08:20 +02:00
{
spellInfo -> RecoveryTime = 20000 ;
});
2019-07-05 18:41:47 +02:00
// Summon Frigid Bones
ApplySpellFix ({ 53525 }, []( SpellInfo * spellInfo )
{
2019-07-12 13:14:07 +02:00
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 4 ); // 2 minutes
2019-07-05 18:41:47 +02:00
});
2017-04-20 21:57:25 +02:00
//
// VIOLET HOLD SPELLS
//
// Water Globule (Ichoron)
ApplySpellFix ({ 54258 , 54264 , 54265 , 54266 , 54267 }, []( SpellInfo * spellInfo )
{
// in 3.3.5 there is only one radius in dbc which is 0 yards in this case
// use max radius from 4.3.4
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_25_YARDS );
});
2017-04-20 21:57:25 +02:00
});
// ENDOF VIOLET HOLD
//
// ULDUAR SPELLS
//
// Pursued (Flame Leviathan)
ApplySpellFix ({ 62374 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_50000_YARDS ); // 50000yd
});
2017-04-20 21:57:25 +02:00
});
// Focused Eyebeam Summon Trigger (Kologarn)
ApplySpellFix ({ 63342 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 1 ;
});
ApplySpellFix ({
65584 , // Growth of Nature (Freya)
64381 // Strength of the Pack (Auriaya)
}, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS ;
});
ApplySpellFix ({
63018 , // Searing Light (XT-002)
65121 , // Searing Light (25m) (XT-002)
63024 , // Gravity Bomb (XT-002)
64234 // Gravity Bomb (25m) (XT-002)
}, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 1 ;
});
ApplySpellFix ({
64386 , // Terrifying Screech (Auriaya)
64389 , // Sentinel Blast (Auriaya)
64678 // Sentinel Blast (Auriaya)
}, []( SpellInfo * spellInfo )
{
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 28 ); // 5 seconds, wrong DBC data?
});
// Potent Pheromones (Freya)
ApplySpellFix ({ 64321 }, []( SpellInfo * spellInfo )
{
// spell should dispel area aura, but doesn't have the attribute
// may be db data bug, or blizz may keep reapplying area auras every update with checking immunity
// that will be clear if we get more spells with problem like this
spellInfo -> AttributesEx |= SPELL_ATTR1_DISPEL_AURAS_ON_IMMUNITY ;
});
2017-03-23 04:26:09 -03:00
// Blizzard (Thorim)
ApplySpellFix ({ 62576 , 62602 }, []( SpellInfo * spellInfo )
{
// DBC data is wrong for EFFECT_0, it's a different dynobject target than EFFECT_1
// Both effects should be shared by the same DynObject
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_DEST_CASTER_LEFT );
});
2017-03-23 04:26:09 -03:00
});
2017-04-20 21:57:25 +02:00
// Spinning Up (Mimiron)
ApplySpellFix ({ 63414 }, []( SpellInfo * spellInfo )
{
2021-03-21 12:05:32 +01:00
spellInfo -> ChannelInterruptFlags = SpellAuraInterruptFlags :: None ;
spellInfo -> ChannelInterruptFlags2 = SpellAuraInterruptFlags2 :: None ;
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ( TARGET_UNIT_CASTER );
});
2017-04-20 21:57:25 +02:00
});
// Rocket Strike (Mimiron)
ApplySpellFix ({ 63036 }, []( SpellInfo * spellInfo )
{
spellInfo -> Speed = 0 ;
});
// Magnetic Field (Mimiron)
ApplySpellFix ({ 64668 }, []( SpellInfo * spellInfo )
{
spellInfo -> Mechanic = MECHANIC_NONE ;
});
// Empowering Shadows (Yogg-Saron)
ApplySpellFix ({ 64468 , 64486 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 3 ; // same for both modes?
});
// Cosmic Smash (Algalon the Observer)
ApplySpellFix ({ 62301 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 1 ;
});
// Cosmic Smash (Algalon the Observer)
ApplySpellFix ({ 64598 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 3 ;
});
// Cosmic Smash (Algalon the Observer)
ApplySpellFix ({ 62293 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ( TARGET_DEST_CASTER );
});
2017-04-20 21:57:25 +02:00
});
// Cosmic Smash (Algalon the Observer)
ApplySpellFix ({ 62311 , 64596 }, []( SpellInfo * spellInfo )
{
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 6 ); // 100yd
});
ApplySpellFix ({
64014 , // Expedition Base Camp Teleport
64024 , // Conservatory Teleport
64025 , // Halls of Invention Teleport
64028 , // Colossal Forge Teleport
64029 , // Shattered Walkway Teleport
64030 , // Antechamber Teleport
64031 , // Scrapyard Teleport
64032 , // Formation Grounds Teleport
65042 // Prison of Yogg-Saron Teleport
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_DEST_DB );
});
2017-04-20 21:57:25 +02:00
});
// ENDOF ULDUAR SPELLS
//
// TRIAL OF THE CRUSADER SPELLS
//
// Infernal Eruption
ApplySpellFix ({ 66258 }, []( SpellInfo * spellInfo )
{
// increase duration from 15 to 18 seconds because caster is already
// unsummoned when spell missile hits the ground so nothing happen in result
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 85 );
});
// ENDOF TRIAL OF THE CRUSADER SPELLS
//
// ICECROWN CITADEL SPELLS
//
ApplySpellFix ({
70781 , // Light's Hammer Teleport
70856 , // Oratory of the Damned Teleport
70857 , // Rampart of Skulls Teleport
70858 , // Deathbringer's Rise Teleport
70859 , // Upper Spire Teleport
70860 , // Frozen Throne Teleport
70861 // Sindragosa's Lair Teleport
}, []( SpellInfo * spellInfo )
{
// THESE SPELLS ARE WORKING CORRECTLY EVEN WITHOUT THIS HACK
// THE ONLY REASON ITS HERE IS THAT CURRENT GRID SYSTEM
// DOES NOT ALLOW FAR OBJECT SELECTION (dist > 333)
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_DEST_DB );
});
2017-04-20 21:57:25 +02:00
});
2017-08-15 02:49:34 -07:00
// Coldflame (Lord Marrowgar)
2020-09-06 15:39:28 +02:00
ApplySpellFix ({ 69146 }, []( SpellInfo * spellInfo )
2017-08-15 02:49:34 -07:00
{
spellInfo -> AttributesEx4 &= ~ SPELL_ATTR4_IGNORE_RESISTANCES ;
});
2017-04-20 21:57:25 +02:00
// Shadow's Fate
ApplySpellFix ({ 71169 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS ;
});
// Lock Players and Tap Chest
ApplySpellFix ({ 72347 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
spellInfo -> AttributesEx3 &= ~ SPELL_ATTR3_NO_INITIAL_AGGRO ;
2017-04-20 21:57:25 +02:00
});
// Resistant Skin (Deathbringer Saurfang adds)
ApplySpellFix ({ 72723 }, []( SpellInfo * spellInfo )
{
// this spell initially granted Shadow damage immunity, however it was removed but the data was left in client
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_2 , []( SpellEffectInfo * spellEffectInfo )
{
2021-09-04 15:13:15 +02:00
spellEffectInfo -> Effect = SPELL_EFFECT_NONE ;
2021-03-16 00:07:34 +01:00
});
2017-04-20 21:57:25 +02:00
});
// Coldflame Jets (Traps after Saurfang)
ApplySpellFix ({ 70460 }, []( SpellInfo * spellInfo )
{
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 1 ); // 10 seconds
});
ApplySpellFix ({
71412 , // Green Ooze Summon (Professor Putricide)
71415 // Orange Ooze Summon (Professor Putricide)
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_TARGET_ANY );
});
2017-04-20 21:57:25 +02:00
});
// Awaken Plagued Zombies
ApplySpellFix ({ 71159 }, []( SpellInfo * spellInfo )
{
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 21 );
});
// Volatile Ooze Beam Protection (Professor Putricide)
ApplySpellFix ({ 70530 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> Effect = SPELL_EFFECT_APPLY_AURA ; // for an unknown reason this was SPELL_EFFECT_APPLY_AREA_AURA_RAID
});
2017-04-20 21:57:25 +02:00
});
// Mutated Strength (Professor Putricide)
ApplySpellFix ({ 71604 }, []( SpellInfo * spellInfo )
{
// THIS IS HERE BECAUSE COOLDOWN ON CREATURE PROCS WERE NOT IMPLEMENTED WHEN THE SCRIPT WAS WRITTEN
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
2021-09-04 15:13:15 +02:00
spellEffectInfo -> Effect = SPELL_EFFECT_NONE ;
2021-03-16 00:07:34 +01:00
});
2017-04-20 21:57:25 +02:00
});
// Unbound Plague (Professor Putricide) (needs target selection script)
ApplySpellFix ({ 70911 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ( TARGET_UNIT_TARGET_ENEMY );
});
2017-04-20 21:57:25 +02:00
});
// Empowered Flare (Blood Prince Council)
ApplySpellFix ({ 71708 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS ;
});
// Swarming Shadows
ApplySpellFix ({ 71266 }, []( SpellInfo * spellInfo )
{
spellInfo -> RequiredAreasID = 0 ; // originally, these require area 4522, which is... outside of Icecrown Citadel
});
// Corruption
ApplySpellFix ({ 70602 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_STACK_FOR_DIFF_CASTERS ;
});
// Column of Frost (visual marker)
ApplySpellFix ({ 70715 }, []( SpellInfo * spellInfo )
{
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 32 ); // 6 seconds (missing)
});
// Mana Void (periodic aura)
ApplySpellFix ({ 71085 }, []( SpellInfo * spellInfo )
{
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 9 ); // 30 seconds (missing)
});
// Summon Suppressor (needs target selection script)
ApplySpellFix ({ 70936 }, []( SpellInfo * spellInfo )
{
2016-12-22 17:40:24 -03:00
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 157 ); // 90yd
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_TARGET_ANY );
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ();
});
2017-04-20 21:57:25 +02:00
});
// Sindragosa's Fury
ApplySpellFix ({ 70598 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_DEST_DEST );
});
2017-04-20 21:57:25 +02:00
});
// Frost Bomb
ApplySpellFix ({ 69846 }, []( SpellInfo * spellInfo )
{
spellInfo -> Speed = 0.0f ; // This spell's summon happens instantly
});
2017-04-07 17:41:43 -03:00
// Chilled to the Bone
ApplySpellFix ({ 70106 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS ;
2018-01-08 20:31:21 +01:00
spellInfo -> AttributesEx6 |= SPELL_ATTR6_IGNORE_CASTER_DAMAGE_MODIFIERS ;
2017-04-07 17:41:43 -03:00
});
2017-04-20 21:57:25 +02:00
// Ice Lock
ApplySpellFix ({ 71614 }, []( SpellInfo * spellInfo )
{
spellInfo -> Mechanic = MECHANIC_STUN ;
});
// Defile
ApplySpellFix ({ 72762 }, []( SpellInfo * spellInfo )
{
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 559 ); // 53 seconds
});
// Defile
ApplySpellFix ({ 72743 }, []( SpellInfo * spellInfo )
{
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 22 ); // 45 seconds
});
// Defile
ApplySpellFix ({ 72754 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_200_YARDS ); // 200yd
});
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_200_YARDS ); // 200yd
});
2017-04-20 21:57:25 +02:00
});
2017-01-19 14:11:58 -03:00
// Val'kyr Target Search
ApplySpellFix ({ 69030 }, []( SpellInfo * spellInfo )
{
spellInfo -> Attributes |= SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY ;
});
2017-04-20 21:57:25 +02:00
// Raging Spirit Visual
ApplySpellFix ({ 69198 }, []( SpellInfo * spellInfo )
{
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 13 ); // 50000yd
});
// Harvest Soul
ApplySpellFix ({ 73655 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_NO_DONE_BONUS ;
});
// Summon Shadow Trap
ApplySpellFix ({ 73540 }, []( SpellInfo * spellInfo )
{
2017-08-08 07:36:46 -07:00
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 3 ); // 60 seconds
2017-04-20 21:57:25 +02:00
});
// Shadow Trap (visual)
ApplySpellFix ({ 73530 }, []( SpellInfo * spellInfo )
{
2017-08-08 07:36:46 -07:00
spellInfo -> DurationEntry = sSpellDurationStore . LookupEntry ( 27 ); // 3 seconds
2017-04-20 21:57:25 +02:00
});
// Summon Spirit Bomb
ApplySpellFix ({ 74302 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 2 ;
});
// Summon Spirit Bomb
ApplySpellFix ({ 73579 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_25_YARDS ); // 25yd
});
2017-04-20 21:57:25 +02:00
});
// Raise Dead
ApplySpellFix ({ 72376 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 3 ;
});
// Jump
ApplySpellFix ({ 71809 }, []( SpellInfo * spellInfo )
{
2017-03-24 08:59:04 -03:00
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 5 ); // 40yd
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_10_YARDS ); // 10yd
spellEffectInfo -> MiscValue = 190 ;
});
2017-04-20 21:57:25 +02:00
});
// Broken Frostmourne
ApplySpellFix ({ 72405 }, []( SpellInfo * spellInfo )
{
2017-03-24 08:59:04 -03:00
spellInfo -> AttributesEx |= SPELL_ATTR1_NO_THREAT ;
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_20_YARDS ); // 20yd
});
2017-04-20 21:57:25 +02:00
});
// ENDOF ICECROWN CITADEL SPELLS
//
// RUBY SANCTUM SPELLS
//
// Soul Consumption
ApplySpellFix ({ 74799 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> RadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_12_YARDS );
});
2017-04-20 21:57:25 +02:00
});
// Twilight Mending
ApplySpellFix ({ 75509 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE ;
spellInfo -> AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS ;
});
// Awaken Flames
ApplySpellFix ({ 75888 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx |= SPELL_ATTR1_CANT_TARGET_SELF ;
});
// ENDOF RUBY SANCTUM SPELLS
//
// EYE OF ETERNITY SPELLS
//
ApplySpellFix ({
57473 , // Arcane Storm bonus explicit visual spell
57431 , // Summon Static Field
56091 , // Flame Spike (Wyrmrest Skytalon)
56092 , // Engulf in Flames (Wyrmrest Skytalon)
57090 , // Revivify (Wyrmrest Skytalon)
57143 // Life Burst (Wyrmrest Skytalon)
}, []( SpellInfo * spellInfo )
{
// All spells work even without these changes. The LOS attribute is due to problem
// from collision between maps & gos with active destroyed state.
spellInfo -> AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS ;
});
// Arcane Barrage (cast by players and NONMELEEDAMAGELOG with caster Scion of Eternity (original caster)).
ApplySpellFix ({ 63934 }, []( SpellInfo * spellInfo )
{
// This would never crit on retail and it has attribute for SPELL_ATTR3_NO_DONE_BONUS because is handled from player,
// until someone figures how to make scions not critting without hack and without making them main casters this should stay here.
spellInfo -> AttributesEx2 |= SPELL_ATTR2_CANT_CRIT ;
});
// ENDOF EYE OF ETERNITY SPELLS
ApplySpellFix ({
40055 , // Introspection
40165 , // Introspection
40166 , // Introspection
40167 , // Introspection
}, []( SpellInfo * spellInfo )
{
spellInfo -> Attributes |= SPELL_ATTR0_NEGATIVE_1 ;
});
//
// STONECORE SPELLS
//
ApplySpellFix ({
95284 , // Teleport (from entrance to Slabhide)
95285 // Teleport (from Slabhide to entrance)
}, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetB = SpellImplicitTargetInfo ( TARGET_DEST_DB );
});
2017-04-20 21:57:25 +02:00
});
// ENDOF STONECORE SPELLS
//
// HALLS OF ORIGINATION SPELLS
//
ApplySpellFix ({
76606 , // Disable Beacon Beams L
76608 // Disable Beacon Beams R
}, []( SpellInfo * spellInfo )
{
// Little hack, Increase the radius so it can hit the Cave In Stalkers in the platform.
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> MaxRadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_45_YARDS );
});
2017-04-20 21:57:25 +02:00
});
// ENDOF HALLS OF ORIGINATION SPELLS
// Threatening Gaze
ApplySpellFix ({ 24314 }, []( SpellInfo * spellInfo )
{
2021-03-21 12:05:32 +01:00
spellInfo -> AuraInterruptFlags |= SpellAuraInterruptFlags :: Action | SpellAuraInterruptFlags :: Moving | SpellAuraInterruptFlags :: Anim ;
2017-04-20 21:57:25 +02:00
});
2018-07-22 18:34:39 +02:00
// Travel Form (dummy) - cannot be cast indoors.
ApplySpellFix ({ 783 }, []( SpellInfo * spellInfo )
{
spellInfo -> Attributes |= SPELL_ATTR0_OUTDOORS_ONLY ;
});
2017-04-20 21:57:25 +02:00
// Tree of Life (Passive)
ApplySpellFix ({ 5420 }, []( SpellInfo * spellInfo )
{
spellInfo -> Stances = UI64LIT ( 1 ) << ( FORM_TREE_OF_LIFE - 1 );
});
// Feral Charge (Cat Form)
ApplySpellFix ({ 49376 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 &= ~ SPELL_ATTR3_CANT_TRIGGER_PROC ;
});
// Gaze of Occu'thar
ApplySpellFix ({ 96942 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx &= ~ SPELL_ATTR1_CHANNELED_1 ;
});
// Evolution
ApplySpellFix ({ 75610 }, []( SpellInfo * spellInfo )
{
spellInfo -> MaxAffectedTargets = 1 ;
});
// Evolution
ApplySpellFix ({ 75697 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_SRC_AREA_ENTRY );
});
2017-04-20 21:57:25 +02:00
});
//
// ISLE OF CONQUEST SPELLS
//
// Teleport
ApplySpellFix ({ 66551 }, []( SpellInfo * spellInfo )
{
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 13 ); // 50000yd
});
// ENDOF ISLE OF CONQUEST SPELLS
2017-12-20 02:24:06 -03:00
// Aura of Fear
ApplySpellFix ({ 40453 }, []( SpellInfo * spellInfo )
{
// Bad DBC data? Copying 25820 here due to spell description
// either is a periodic with chance on tick, or a proc
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraName = SPELL_AURA_PROC_TRIGGER_SPELL ;
spellEffectInfo -> ApplyAuraPeriod = 0 ;
});
spellInfo -> ProcChance = 10 ;
});
2019-08-12 16:59:52 +02:00
// Survey Sinkholes
ApplySpellFix ({ 45853 }, []( SpellInfo * spellInfo )
{
spellInfo -> RangeEntry = sSpellRangeStore . LookupEntry ( 5 ); // 40 yards
});
2019-05-31 08:26:42 +02:00
// Baron Rivendare (Stratholme) - Unholy Aura
ApplySpellFix ({ 17466 , 17467 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_NO_INITIAL_AGGRO ;
});
2019-07-29 21:37:15 +02:00
2019-07-11 18:32:15 +02:00
// Spore - Spore Visual
ApplySpellFix ({ 42525 }, []( SpellInfo * spellInfo )
{
spellInfo -> AttributesEx3 |= SPELL_ATTR3_DEATH_PERSISTENT ;
spellInfo -> AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_DEAD ;
});
2019-05-31 08:26:42 +02:00
2019-10-03 08:49:05 -03:00
// Soul Sickness (Forge of Souls)
ApplySpellFix ({ 69131 }, []( SpellInfo * spellInfo )
{
ApplySpellEffectFix ( spellInfo , EFFECT_1 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> ApplyAuraName = SPELL_AURA_MOD_DECREASE_SPEED ;
});
});
2017-08-22 18:28:07 +02:00
//
// FIRELANDS SPELLS
//
// Torment Searcher
ApplySpellFix ({ 99253 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> MaxRadiusEntry = sSpellRadiusStore . LookupEntry ( EFFECT_RADIUS_15_YARDS );
});
2017-08-22 18:28:07 +02:00
});
// Torment Damage
ApplySpellFix ({ 99256 }, []( SpellInfo * spellInfo )
{
spellInfo -> Attributes |= SPELL_ATTR0_NEGATIVE_1 ;
});
// Blaze of Glory
ApplySpellFix ({ 99252 }, []( SpellInfo * spellInfo )
{
2021-03-21 12:05:32 +01:00
spellInfo -> AuraInterruptFlags |= SpellAuraInterruptFlags :: LeaveWorld ;
2017-08-22 18:28:07 +02:00
});
// ENDOF FIRELANDS SPELLS
2020-04-25 19:04:31 +02:00
//
// ANTORUS THE BURNING THRONE SPELLS
//
// Decimation
ApplySpellFix ({ 244449 }, []( SpellInfo * spellInfo )
{
// For some reason there is a instakill effect that serves absolutely no purpose.
// Until we figure out what it's actually used for we disable it.
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_2 , []( SpellEffectInfo * spellEffectInfo )
{
2021-09-04 15:13:15 +02:00
spellEffectInfo -> Effect = SPELL_EFFECT_NONE ;
2021-03-16 00:07:34 +01:00
});
2020-04-25 19:04:31 +02:00
});
// ENDOF ANTORUS THE BURNING THRONE SPELLS
2017-04-20 21:57:25 +02:00
// Summon Master Li Fei
ApplySpellFix ({ 102445 }, []( SpellInfo * spellInfo )
{
2021-03-16 00:07:34 +01:00
ApplySpellEffectFix ( spellInfo , EFFECT_0 , []( SpellEffectInfo * spellEffectInfo )
{
spellEffectInfo -> TargetA = SpellImplicitTargetInfo ( TARGET_DEST_DB );
});
2017-04-20 21:57:25 +02:00
});
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & s : mSpellInfoMap )
2011-07-26 23:09:28 +02:00
{
2020-06-12 01:29:18 +02:00
SpellInfo * spellInfo = & const_cast < SpellInfo &> ( s );
2011-07-26 23:09:28 +02:00
if ( ! spellInfo )
continue ;
2021-09-04 15:13:15 +02:00
// Fix range for trajectory triggered spell
for ( SpellEffectInfo const & spellEffectInfo : spellInfo -> GetEffects ())
2011-07-26 23:09:28 +02:00
{
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsEffect () && ( spellEffectInfo . TargetA . GetTarget () == TARGET_DEST_TRAJ || spellEffectInfo . TargetB . GetTarget () == TARGET_DEST_TRAJ ))
2017-02-06 01:10:15 +01:00
{
// Get triggered spell if any
2021-09-04 15:13:15 +02:00
for ( SpellInfo const & spellInfoTrigger : _GetSpellInfo ( spellEffectInfo . TriggerSpell ))
2017-02-06 01:10:15 +01:00
{
float maxRangeMain = spellInfo -> GetMaxRange ();
2021-09-04 15:13:15 +02:00
float maxRangeTrigger = spellInfoTrigger . GetMaxRange ();
2017-02-06 01:10:15 +01:00
// check if triggered spell has enough max range to cover trajectory
if ( maxRangeTrigger < maxRangeMain )
2021-09-04 15:13:15 +02:00
const_cast < SpellInfo &> ( spellInfoTrigger ). RangeEntry = spellInfo -> RangeEntry ;
2017-02-06 01:10:15 +01:00
}
}
2021-09-04 15:13:15 +02:00
switch ( spellEffectInfo . Effect )
2011-07-26 23:09:28 +02:00
{
case SPELL_EFFECT_CHARGE :
case SPELL_EFFECT_CHARGE_DEST :
case SPELL_EFFECT_JUMP :
case SPELL_EFFECT_JUMP_DEST :
case SPELL_EFFECT_LEAP_BACK :
2019-08-31 01:29:54 +02:00
if ( ! spellInfo -> Speed && ! spellInfo -> SpellFamilyName && ! spellInfo -> HasAttribute ( SPELL_ATTR9_SPECIAL_DELAY_CALCULATION ))
2012-12-24 12:34:46 -05:00
spellInfo -> Speed = SPEED_CHARGE ;
2011-07-26 23:09:28 +02:00
break ;
2021-09-04 15:13:15 +02:00
default :
break ;
2011-07-26 23:09:28 +02:00
}
2017-07-18 15:16:32 +02:00
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . TargetA . GetSelectionCategory () == TARGET_SELECT_CATEGORY_CONE || spellEffectInfo . TargetB . GetSelectionCategory () == TARGET_SELECT_CATEGORY_CONE )
2017-07-18 15:16:32 +02:00
if ( G3D :: fuzzyEq ( spellInfo -> ConeAngle , 0.f ))
spellInfo -> ConeAngle = 90.f ;
2018-03-01 20:38:07 -03:00
// Area auras may not target area (they're self cast)
2021-09-04 15:13:15 +02:00
if ( spellEffectInfo . IsAreaAuraEffect () && spellEffectInfo . IsTargetingArea ())
2018-03-01 20:38:07 -03:00
{
2021-09-04 15:13:15 +02:00
const_cast < SpellEffectInfo &> ( spellEffectInfo ). TargetA = SpellImplicitTargetInfo ( TARGET_UNIT_CASTER );
const_cast < SpellEffectInfo &> ( spellEffectInfo ). TargetB = SpellImplicitTargetInfo ( 0 );
2018-03-01 20:38:07 -03:00
}
2011-07-26 23:09:28 +02:00
}
2017-03-02 00:18:00 -03:00
// disable proc for magnet auras, they're handled differently
2020-06-12 01:29:18 +02:00
if ( spellInfo -> HasAura ( SPELL_AURA_SPELL_MAGNET ))
2017-03-03 21:36:56 -03:00
spellInfo -> ProcFlags = 0 ;
2017-03-02 00:18:00 -03:00
2017-03-20 12:35:09 -03:00
// due to the way spell system works, unit would change orientation in Spell::_cast
2020-06-12 01:29:18 +02:00
if ( spellInfo -> HasAura ( SPELL_AURA_CONTROL_VEHICLE ))
2017-03-20 12:35:09 -03:00
spellInfo -> AttributesEx5 |= SPELL_ATTR5_DONT_TURN_DURING_CAST ;
2017-04-15 12:32:36 +02:00
if ( spellInfo -> ActiveIconFileDataId == 135754 ) // flight
2011-07-26 23:09:28 +02:00
spellInfo -> Attributes |= SPELL_ATTR0_PASSIVE ;
2021-08-15 11:09:46 +02:00
if ( spellInfo -> IsSingleTarget ())
spellInfo -> MaxAffectedTargets = 1 ;
2009-05-15 16:26:00 -05:00
}
2009-10-17 15:51:44 -07:00
2013-08-24 22:56:29 +01:00
if ( SummonPropertiesEntry * properties = const_cast < SummonPropertiesEntry *> ( sSummonPropertiesStore . LookupEntry ( 121 )))
2020-04-07 17:27:40 +02:00
properties -> Title = AsUnderlyingType ( SummonTitle :: Totem );
2013-08-24 22:56:29 +01:00
if ( SummonPropertiesEntry * properties = const_cast < SummonPropertiesEntry *> ( sSummonPropertiesStore . LookupEntry ( 647 ))) // 52893
2020-04-07 17:27:40 +02:00
properties -> Title = AsUnderlyingType ( SummonTitle :: Totem );
2016-09-24 04:04:34 +02:00
if ( SummonPropertiesEntry * properties = const_cast < SummonPropertiesEntry *> ( sSummonPropertiesStore . LookupEntry ( 628 ))) // Hungry Plaguehound
2018-03-12 18:20:26 +01:00
properties -> Control = SUMMON_CATEGORY_PET ;
2012-12-27 20:22:22 +01:00
2013-11-08 10:50:51 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded SpellInfo corrections in %u ms" , GetMSTimeDiffToNow ( oldMSTime ));
2011-08-03 08:18:24 +02:00
}
2014-12-01 20:44:06 +01:00
2016-10-07 00:51:52 -03:00
void SpellMgr :: LoadSpellInfoSpellSpecificAndAuraState ()
{
uint32 oldMSTime = getMSTime ();
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
2016-10-07 00:51:52 -03:00
{
// AuraState depends on SpellSpecific
2020-06-12 01:29:18 +02:00
const_cast < SpellInfo &> ( spellInfo ). _LoadSpellSpecific ();
const_cast < SpellInfo &> ( spellInfo ). _LoadAuraState ();
2016-10-07 00:51:52 -03:00
}
TC_LOG_INFO ( "server.loading" , ">> Loaded SpellInfo SpellSpecific and AuraState in %u ms" , GetMSTimeDiffToNow ( oldMSTime ));
}
2016-10-17 01:52:40 -03:00
void SpellMgr :: LoadSpellInfoDiminishing ()
{
uint32 oldMSTime = getMSTime ();
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
const_cast < SpellInfo &> ( spellInfo ). _LoadSpellDiminishInfo ();
2016-10-17 01:52:40 -03:00
TC_LOG_INFO ( "server.loading" , ">> Loaded SpellInfo diminishing infos in %u ms" , GetMSTimeDiffToNow ( oldMSTime ));
}
2016-11-12 21:58:00 +01:00
void SpellMgr :: LoadSpellInfoImmunities ()
{
uint32 oldMSTime = getMSTime ();
2020-06-12 01:29:18 +02:00
for ( SpellInfo const & spellInfo : mSpellInfoMap )
const_cast < SpellInfo &> ( spellInfo ). _LoadImmunityInfo ();
2016-11-12 21:58:00 +01:00
TC_LOG_INFO ( "server.loading" , ">> Loaded SpellInfo immunity infos in %u ms" , GetMSTimeDiffToNow ( oldMSTime ));
}
2014-12-01 20:44:06 +01:00
void SpellMgr :: LoadPetFamilySpellsStore ()
{
2016-01-24 15:56:10 +01:00
std :: unordered_map < uint32 , SpellLevelsEntry const *> levelsBySpell ;
for ( SpellLevelsEntry const * levels : sSpellLevelsStore )
if ( ! levels -> DifficultyID )
levelsBySpell [ levels -> SpellID ] = levels ;
2016-10-01 17:51:05 +02:00
for ( SkillLineAbilityEntry const * skillLine : sSkillLineAbilityStore )
2014-12-01 20:44:06 +01:00
{
2020-06-12 01:29:18 +02:00
SpellInfo const * spellInfo = GetSpellInfo ( skillLine -> Spell , DIFFICULTY_NONE );
2014-12-01 20:44:06 +01:00
if ( ! spellInfo )
continue ;
2018-03-12 18:20:26 +01:00
auto levels = levelsBySpell . find ( skillLine -> Spell );
2016-01-24 15:56:10 +01:00
if ( levels != levelsBySpell . end () && levels -> second -> SpellLevel )
2014-12-01 20:44:06 +01:00
continue ;
2016-10-01 17:51:05 +02:00
if ( spellInfo -> IsPassive ())
2014-12-01 20:44:06 +01:00
{
2016-10-01 17:51:05 +02:00
for ( CreatureFamilyEntry const * cFamily : sCreatureFamilyStore )
2014-12-01 20:44:06 +01:00
{
2016-10-01 17:51:05 +02:00
if ( skillLine -> SkillLine != cFamily -> SkillLine [ 0 ] && skillLine -> SkillLine != cFamily -> SkillLine [ 1 ])
continue ;
2014-12-01 20:44:06 +01:00
2016-10-01 17:51:05 +02:00
if ( skillLine -> AcquireMethod != SKILL_LINE_ABILITY_LEARNED_ON_SKILL_LEARN )
continue ;
2014-12-01 20:44:06 +01:00
2016-10-01 17:51:05 +02:00
sPetFamilySpellsStore [ cFamily -> ID ]. insert ( spellInfo -> Id );
2014-12-01 20:44:06 +01:00
}
}
}
}
2018-04-23 23:44:59 +02:00
void SpellMgr :: LoadSpellTotemModel ()
{
uint32 oldMSTime = getMSTime ();
QueryResult result = WorldDatabase . Query ( "SELECT SpellID, RaceID, DisplayID from spell_totem_model" );
if ( ! result )
{
TC_LOG_INFO ( "server.loading" , ">> Loaded 0 spell totem model records. DB table `spell_totem_model` is empty." );
return ;
}
uint32 count = 0 ;
do
{
Field * fields = result -> Fetch ();
uint32 spellId = fields [ 0 ]. GetUInt32 ();
uint8 race = fields [ 1 ]. GetUInt8 ();
uint32 displayId = fields [ 2 ]. GetUInt32 ();
2020-06-12 01:29:18 +02:00
SpellInfo const * spellEntry = GetSpellInfo ( spellId , DIFFICULTY_NONE );
2018-04-23 23:44:59 +02:00
if ( ! spellEntry )
{
TC_LOG_ERROR ( "sql.sql" , "SpellID: %u in `spell_totem_model` table could not be found in dbc, skipped." , spellId );
continue ;
}
ChrRacesEntry const * raceEntry = sChrRacesStore . LookupEntry ( race );
if ( ! raceEntry )
{
TC_LOG_ERROR ( "sql.sql" , "Race %u defined in `spell_totem_model` does not exists, skipped." , uint32 ( race ));
continue ;
}
CreatureDisplayInfoEntry const * displayEntry = sCreatureDisplayInfoStore . LookupEntry ( displayId );
if ( ! displayEntry )
{
TC_LOG_ERROR ( "sql.sql" , "SpellID: %u defined in `spell_totem_model` has non-existing model (%u)." , spellId , displayId );
continue ;
}
mSpellTotemModel [ std :: make_pair ( spellId , race )] = displayId ;
++ count ;
} while ( result -> NextRow ());
TC_LOG_INFO ( "server.loading" , ">> Loaded %u spell totem model records in %u ms" , count , GetMSTimeDiffToNow ( oldMSTime ));
}
uint32 SpellMgr :: GetModelForTotem ( uint32 spellId , uint8 race ) const
{
auto itr = mSpellTotemModel . find ( std :: make_pair ( spellId , race ));
if ( itr != mSpellTotemModel . end ())
return itr -> second ;
return 0 ;
}
2020-04-08 13:36:59 +02:00
BattlePetSpeciesEntry const * SpellMgr :: GetBattlePetSpecies ( uint32 spellId ) const
{
return Trinity :: Containers :: MapGetValuePtr ( mBattlePets , spellId );
}