diff options
-rw-r--r-- | src/server/game/Entities/Pet/Pet.cpp | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 8 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 103 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.h | 14 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraDefines.h | 44 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.h | 17 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 507 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.h | 114 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 64 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 5 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 66 | ||||
-rw-r--r-- | src/server/game/Spells/SpellScript.cpp | 21 | ||||
-rw-r--r-- | src/server/game/Spells/SpellScript.h | 2 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_misc.cpp | 5 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_dh.cpp | 1 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_paladin.cpp | 4 |
17 files changed, 586 insertions, 398 deletions
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 7df230526dc..f7bd8b75bb1 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -1210,7 +1210,12 @@ void Pet::_LoadAuras(uint32 timediff) AuraLoadEffectInfo& info = effectInfo[key]; ObjectGuid castId = ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellInfo->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()); - if (Aura* aura = Aura::TryCreate(spellInfo, castId, key.EffectMask, this, nullptr, difficulty, info.BaseAmounts.data(), nullptr, casterGuid)) + AuraCreateInfo createInfo(castId, spellInfo, difficulty, key.EffectMask, this); + createInfo + .SetCasterGUID(casterGuid) + .SetBaseAmount(info.BaseAmounts.data()); + + if (Aura* aura = Aura::TryCreate(createInfo)) { if (!aura->CanBeSaved()) { diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 441b6c8bd4d..bbb4ccf5887 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -18759,7 +18759,13 @@ void Player::_LoadAuras(PreparedQueryResult auraResult, PreparedQueryResult effe AuraLoadEffectInfo& info = effectInfo[key]; ObjectGuid castId = ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellInfo->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()); - if (Aura* aura = Aura::TryCreate(spellInfo, castId, key.EffectMask, this, nullptr, difficulty, info.BaseAmounts.data(), nullptr, casterGuid, itemGuid, castItemId, castItemLevel)) + AuraCreateInfo createInfo(castId, spellInfo, difficulty, key.EffectMask, this); + createInfo + .SetCasterGUID(casterGuid) + .SetBaseAmount(info.BaseAmounts.data()) + .SetCastItem(itemGuid, castItemId, castItemLevel); + + if (Aura* aura = Aura::TryCreate(createInfo)) { if (!aura->CanBeSaved()) { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fadd871a970..b75fc274072 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -3255,44 +3255,31 @@ void Unit::DeMorph() SetDisplayId(GetNativeDisplayId()); } -Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint32 effMask, Unit* caster, int32* baseAmount /*= nullptr*/, Item* castItem /*= nullptr*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/, bool resetPeriodicTimer /*= true*/, ObjectGuid castItemGuid /*= ObjectGuid::Empty*/, uint32 castItemId /*= 0*/, int32 castItemLevel /*= -1*/) +Aura* Unit::_TryStackingOrRefreshingExistingAura(AuraCreateInfo& createInfo) { - ASSERT(!casterGUID.IsEmpty() || caster); + ASSERT(!createInfo.CasterGUID.IsEmpty() || createInfo.Caster); // Check if these can stack anyway - if (!casterGUID && !newAura->IsStackableOnOneSlotWithDifferentCasters()) - casterGUID = caster->GetGUID(); + if (!createInfo.CasterGUID && !createInfo.GetSpellInfo()->IsStackableOnOneSlotWithDifferentCasters()) + createInfo.CasterGUID = createInfo.Caster->GetGUID(); // passive and Incanter's Absorption and auras with different type can stack with themselves any number of times - if (!newAura->IsMultiSlotAura()) + if (!createInfo.GetSpellInfo()->IsMultiSlotAura()) { // check if cast item changed - if (castItem) - { - castItemGuid = castItem->GetGUID(); - if (Player* owner = castItem->GetOwner()) - { - castItemId = castItem->GetEntry(); - castItemLevel = int32(castItem->GetItemLevel(owner)); - } - else if (castItem->GetOwnerGUID() == caster->GetGUID()) - { - castItemId = castItem->GetEntry(); - castItemLevel = int32(castItem->GetItemLevel(caster->ToPlayer())); - } - } + ObjectGuid castItemGUID = createInfo.CastItemGUID; // find current aura from spell and change it's stackamount, or refresh it's duration - if (Aura* foundAura = GetOwnedAura(newAura->Id, casterGUID, (newAura->HasAttribute(SPELL_ATTR0_CU_ENCHANT_PROC)) ? castItemGuid : ObjectGuid::Empty, 0)) + if (Aura* foundAura = GetOwnedAura(createInfo.GetSpellInfo()->Id, createInfo.CasterGUID, createInfo.GetSpellInfo()->HasAttribute(SPELL_ATTR0_CU_ENCHANT_PROC) ? castItemGUID : ObjectGuid::Empty, 0)) { // effect masks do not match // extremely rare case // let's just recreate aura - if (effMask != foundAura->GetEffectMask()) + if (createInfo.GetAuraEffectMask() != foundAura->GetEffectMask()) return nullptr; // update basepoints with new values - effect amount will be recalculated in ModStackAmount - for (SpellEffectInfo const* effect : newAura->GetEffects()) + for (SpellEffectInfo const* effect : createInfo.GetSpellInfo()->GetEffects()) { if (!effect) continue; @@ -3301,9 +3288,9 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint3 if (!eff) continue; - int bp; - if (baseAmount) - bp = *(baseAmount + effect->EffectIndex); + int32 bp; + if (createInfo.BaseAmount) + bp = *(createInfo.BaseAmount + effect->EffectIndex); else bp = effect->BasePoints; @@ -3312,18 +3299,18 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint3 } // correct cast item guid if needed - if (castItemGuid != foundAura->GetCastItemGUID()) + if (castItemGUID != foundAura->GetCastItemGUID()) { ObjectGuid* oldGUID = const_cast<ObjectGuid*>(&foundAura->m_castItemGuid); - *oldGUID = castItemGuid; + *oldGUID = castItemGUID; uint32* oldItemId = const_cast<uint32*>(&foundAura->m_castItemId); - *oldItemId = castItemId; + *oldItemId = createInfo.CastItemId; int32* oldItemLevel = const_cast<int32*>(&foundAura->m_castItemLevel); - *oldItemLevel = castItemLevel; + *oldItemLevel = createInfo.CastItemLevel; } // try to increase stack amount - foundAura->ModStackAmount(1, AURA_REMOVE_BY_DEFAULT, resetPeriodicTimer); + foundAura->ModStackAmount(1, AURA_REMOVE_BY_DEFAULT, createInfo.ResetPeriodicTimer); return foundAura; } } @@ -3334,7 +3321,7 @@ Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint3 void Unit::_AddAura(UnitAura* aura, Unit* caster) { ASSERT(!m_cleanupDone); - m_ownedAuras.insert(AuraMap::value_type(aura->GetId(), aura)); + m_ownedAuras.emplace(aura->GetId(), aura); _RemoveNoStackAurasDueToAura(aura); @@ -3371,7 +3358,7 @@ void Unit::_AddAura(UnitAura* aura, Unit* caster) // creates aura application instance and registers it in lists // aura application effects are handled separately to prevent aura list corruption -AuraApplication * Unit::_CreateAuraApplication(Aura* aura, uint32 effMask) +AuraApplication* Unit::_CreateAuraApplication(Aura* aura, uint32 effMask) { // can't apply aura on unit which is going to be deleted - to not create a memory leak ASSERT(!m_cleanupDone); @@ -3421,7 +3408,7 @@ void Unit::_ApplyAuraEffect(Aura* aura, uint8 effIndex) // handles effects of aura application // should be done after registering aura in lists -void Unit::_ApplyAura(AuraApplication * aurApp, uint32 effMask) +void Unit::_ApplyAura(AuraApplication* aurApp, uint32 effMask) { Aura* aura = aurApp->GetBase(); @@ -3464,7 +3451,7 @@ void Unit::_ApplyAura(AuraApplication * aurApp, uint32 effMask) } // removes aura application from lists and unapplies effects -void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode) +void Unit::_UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveMode removeMode) { AuraApplication * aurApp = i->second; ASSERT(aurApp); @@ -3552,7 +3539,7 @@ void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMo i = m_appliedAuras.begin(); } -void Unit::_UnapplyAura(AuraApplication * aurApp, AuraRemoveMode removeMode) +void Unit::_UnapplyAura(AuraApplication* aurApp, AuraRemoveMode removeMode) { // aura can be removed from unit only if it's applied on it, shouldn't happen ASSERT(aurApp->GetBase()->GetApplicationOfTarget(GetGUID()) == aurApp); @@ -3621,8 +3608,8 @@ void Unit::_RegisterAuraEffect(AuraEffect* aurEff, bool apply) m_modAuras[aurEff->GetAuraType()].remove(aurEff); } -// All aura base removes should go threw this function! -void Unit::RemoveOwnedAura(AuraMap::iterator &i, AuraRemoveMode removeMode) +// All aura base removes should go through this function! +void Unit::RemoveOwnedAura(AuraMap::iterator& i, AuraRemoveMode removeMode) { Aura* aura = i->second; ASSERT(!aura->IsRemoved()); @@ -3952,7 +3939,12 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U if (aura->IsSingleTarget()) aura->UnregisterSingleTarget(); - if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), aura->GetCastGUID(), effMask, stealer, nullptr, aura->GetCastDifficulty(), &baseDamage[0], nullptr, aura->GetCasterGUID())) + AuraCreateInfo createInfo(aura->GetCastGUID(), aura->GetSpellInfo(), aura->GetCastDifficulty(), effMask, stealer); + createInfo + .SetCasterGUID(aura->GetCasterGUID()) + .SetBaseAmount(baseDamage); + + if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo)) { // created aura must not be single target aura,, so stealer won't loose it on recast if (newAura->IsSingleTarget()) @@ -12315,12 +12307,19 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target) { if (!(effMask & (1 << i))) continue; + if (target->IsImmunedToSpellEffect(spellInfo, i, this)) effMask &= ~(1 << i); } + if (!effMask) + return nullptr; + ObjectGuid castId = ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellInfo->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()); - if (Aura* aura = Aura::TryRefreshStackOrCreate(spellInfo, castId, effMask, target, this, GetMap()->GetDifficultyID())) + AuraCreateInfo createInfo(castId, spellInfo, GetMap()->GetDifficultyID(), effMask, target); + createInfo.SetCaster(this); + + if (Aura* aura = Aura::TryRefreshStackOrCreate(createInfo)) { aura->ApplyForTargets(); return aura; @@ -12769,18 +12768,25 @@ void Unit::HandleSpellClick(Unit* clicker, int8 seatId /*= -1*/) { CastSpellExtraArgs args(flags); args.OriginalCaster = origCasterGUID; - args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0+i), seatId+1); + args.AddSpellMod(SpellValueMod(SPELLVALUE_BASE_POINT0 + i), seatId + 1); caster->CastSpell(target, clickPair.second.spellId, args); } else // This can happen during Player::_LoadAuras { - int32 bp0[MAX_SPELL_EFFECTS]; + int32 bp[MAX_SPELL_EFFECTS] = { }; for (SpellEffectInfo const* effect : spellEntry->GetEffects()) if (effect) - bp0[effect->EffectIndex] = effect->BasePoints; + bp[effect->EffectIndex] = effect->BasePoints; + + bp[i] = seatId; - bp0[i] = seatId; - Aura::TryRefreshStackOrCreate(spellEntry, ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), MAX_EFFECT_MASK, this, clicker, GetMap()->GetDifficultyID(), bp0, nullptr, origCasterGUID); + AuraCreateInfo createInfo(ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), spellEntry, GetMap()->GetDifficultyID(), MAX_EFFECT_MASK, this); + createInfo + .SetCaster(clicker) + .SetBaseAmount(bp) + .SetCasterGUID(origCasterGUID); + + Aura::TryRefreshStackOrCreate(createInfo); } } else @@ -12788,7 +12794,14 @@ void Unit::HandleSpellClick(Unit* clicker, int8 seatId /*= -1*/) if (IsInMap(caster)) caster->CastSpell(target, spellEntry->Id, CastSpellExtraArgs().SetOriginalCaster(origCasterGUID)); else - Aura::TryRefreshStackOrCreate(spellEntry, ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), MAX_EFFECT_MASK, this, clicker, GetMap()->GetDifficultyID(), nullptr, nullptr, origCasterGUID); + { + AuraCreateInfo createInfo(ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, GetMapId(), spellEntry->Id, GetMap()->GenerateLowGuid<HighGuid::Cast>()), spellEntry, GetMap()->GetDifficultyID(), MAX_EFFECT_MASK, this); + createInfo + .SetCaster(clicker) + .SetCasterGUID(origCasterGUID); + + Aura::TryRefreshStackOrCreate(createInfo); + } } spellClickHandled = true; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index a2bb7f1a301..ff5c76a1bb5 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -762,7 +762,7 @@ class TC_GAME_API Unit : public WorldObject typedef std::list<AuraEffect*> AuraEffectList; typedef std::list<Aura*> AuraList; - typedef std::list<AuraApplication *> AuraApplicationList; + typedef std::list<AuraApplication*> AuraApplicationList; typedef std::array<DiminishingReturn, DIMINISHING_MAX> Diminishing; typedef std::vector<std::pair<uint32 /*procEffectMask*/, AuraApplication*>> AuraApplicationProcContainer; @@ -1337,13 +1337,13 @@ class TC_GAME_API Unit : public WorldObject bool InitTamedPet(Pet* pet, uint8 level, uint32 spell_id); // aura apply/remove helpers - you should better not use these - Aura* _TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint32 effMask, Unit* caster, int32* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty, bool resetPeriodicTimer = true, ObjectGuid castItemGuid = ObjectGuid::Empty, uint32 castItemId = 0, int32 castItemLevel = -1); + Aura* _TryStackingOrRefreshingExistingAura(AuraCreateInfo& createInfo); void _AddAura(UnitAura* aura, Unit* caster); - AuraApplication * _CreateAuraApplication(Aura* aura, uint32 effMask); + AuraApplication* _CreateAuraApplication(Aura* aura, uint32 effMask); void _ApplyAuraEffect(Aura* aura, uint8 effIndex); - void _ApplyAura(AuraApplication * aurApp, uint32 effMask); - void _UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode); - void _UnapplyAura(AuraApplication * aurApp, AuraRemoveMode removeMode); + void _ApplyAura(AuraApplication* aurApp, uint32 effMask); + void _UnapplyAura(AuraApplicationMap::iterator& i, AuraRemoveMode removeMode); + void _UnapplyAura(AuraApplication* aurApp, AuraRemoveMode removeMode); void _RemoveNoStackAurasDueToAura(Aura* aura); void _RegisterAuraEffect(AuraEffect* aurEff, bool apply); @@ -1351,7 +1351,7 @@ class TC_GAME_API Unit : public WorldObject AuraMap & GetOwnedAuras() { return m_ownedAuras; } AuraMap const& GetOwnedAuras() const { return m_ownedAuras; } - void RemoveOwnedAura(AuraMap::iterator &i, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); + void RemoveOwnedAura(AuraMap::iterator& i, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); void RemoveOwnedAura(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, uint32 reqEffMask = 0, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); void RemoveOwnedAura(Aura* aura, AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT); diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h index 0706e3709e1..60b7475c978 100644 --- a/src/server/game/Spells/Auras/SpellAuraDefines.h +++ b/src/server/game/Spells/Auras/SpellAuraDefines.h @@ -18,6 +18,13 @@ #define TRINITY_SPELLAURADEFINES_H #include "Define.h" +#include "ObjectGuid.h" + +class Item; +class SpellInfo; +class Unit; +class WorldObject; +enum Difficulty : uint8; #define MAX_AURAS 255 @@ -641,4 +648,41 @@ enum ShapeshiftForm FORM_FORGEBORNE_REVERIES = 42 }; +struct TC_GAME_API AuraCreateInfo +{ + friend class Aura; + friend class UnitAura; + friend class DynObjAura; + + AuraCreateInfo(ObjectGuid castId, SpellInfo const* spellInfo, Difficulty castDifficulty, uint32 auraEffMask, WorldObject* owner); + + AuraCreateInfo& SetCasterGUID(ObjectGuid const& guid) { CasterGUID = guid; return *this; } + AuraCreateInfo& SetCaster(Unit* caster) { Caster = caster; return *this; } + AuraCreateInfo& SetBaseAmount(int32 const* bp) { BaseAmount = bp; return *this; } + AuraCreateInfo& SetCastItem(ObjectGuid const& guid, uint32 itemId, int32 itemLevel) { CastItemGUID = guid; CastItemId = itemId; CastItemLevel = itemLevel; return *this; } + AuraCreateInfo& SetPeriodicReset(bool reset) { ResetPeriodicTimer = reset; return *this; } + AuraCreateInfo& SetOwnerEffectMask(uint32 effMask) { _targetEffectMask = effMask; return *this; } + + SpellInfo const* GetSpellInfo() const { return _spellInfo; } + uint32 GetAuraEffectMask() const { return _auraEffectMask; } + + ObjectGuid CasterGUID; + Unit* Caster = nullptr; + int32 const* BaseAmount = nullptr; + ObjectGuid CastItemGUID; + uint32 CastItemId = 0; + int32 CastItemLevel = -1; + bool* IsRefresh = nullptr; + bool ResetPeriodicTimer = true; + +private: + ObjectGuid _castId; + SpellInfo const* _spellInfo = nullptr; + Difficulty _castDifficulty = Difficulty(0); + uint32 _auraEffectMask = 0; + WorldObject* _owner = nullptr; + + uint32 _targetEffectMask = 0; +}; + #endif diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index abc447b79b9..159a153ab1f 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -575,7 +575,7 @@ NonDefaultConstructible<pAuraEffectHandler> AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNULL, //503 SPELL_AURA_MOD_PLAYER_CHOICE_REROLLS }; -AuraEffect::AuraEffect(Aura* base, SpellEffectInfo const* spellEfffectInfo, int32 *baseAmount, Unit* caster) : +AuraEffect::AuraEffect(Aura* base, SpellEffectInfo const* spellEfffectInfo, int32 const* baseAmount, Unit* caster) : m_base(base), m_spellInfo(base->GetSpellInfo()), m_effectInfo(spellEfffectInfo), m_spellmod(nullptr), m_baseAmount(baseAmount ? *baseAmount : spellEfffectInfo->CalcBaseValue(caster, base->GetType() == UNIT_AURA_TYPE ? base->GetOwner()->ToUnit() : nullptr, base->GetCastItemId(), base->GetCastItemLevel())), _amount(), _periodicTimer(0), _period(0), _ticksDone(0), diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h index bf8c4bc9ab7..1a96b81ea68 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.h +++ b/src/server/game/Spells/Auras/SpellAuraEffects.h @@ -18,23 +18,24 @@ #ifndef TRINITY_SPELLAURAEFFECTS_H #define TRINITY_SPELLAURAEFFECTS_H -class Unit; -class AuraEffect; -class Aura; - #include "SpellAuras.h" +class AuraEffect; +class Unit; + typedef void(AuraEffect::*pAuraEffectHandler)(AuraApplication const* aurApp, uint8 mode, bool apply) const; class TC_GAME_API AuraEffect { - friend void Aura::_InitEffects(uint32 effMask, Unit* caster, int32* baseAmount); - friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint32 effMask, Unit* caster, int32* baseAmount, Item* castItem, ObjectGuid casterGUID, bool resetPeriodicTimer, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); + friend void Aura::_InitEffects(uint32 effMask, Unit* caster, int32 const* baseAmount); friend Aura::~Aura(); + friend class Unit; - public: + private: ~AuraEffect(); - AuraEffect(Aura* base, SpellEffectInfo const* spellEfffectInfo, int32 *baseAmount, Unit* caster); + explicit AuraEffect(Aura* base, SpellEffectInfo const* spellEfffectInfo, int32 const* baseAmount, Unit* caster); + + public: Unit* GetCaster() const { return GetBase()->GetCaster(); } ObjectGuid GetCasterGUID() const { return GetBase()->GetCasterGUID(); } Aura* GetBase() const { return m_base; } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 2ecd3c29e38..7ea0658e916 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -38,7 +38,33 @@ #include "Vehicle.h" #include "World.h" -AuraApplication::AuraApplication(Unit* target, Unit* caster, Aura* aura, uint32 effMask): +class ChargeDropEvent : public BasicEvent +{ +public: + ChargeDropEvent(Aura* base, AuraRemoveMode mode) : _base(base), _mode(mode) { } + bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override + { + // _base is always valid (look in Aura::_Remove()) + _base->ModChargesDelayed(-1, _mode); + return true; + } + +private: + Aura* _base; + AuraRemoveMode _mode; +}; + +AuraCreateInfo::AuraCreateInfo(ObjectGuid castId, SpellInfo const* spellInfo, Difficulty castDifficulty, uint32 auraEffMask, WorldObject* owner) : + _castId(castId), _spellInfo(spellInfo), _castDifficulty(castDifficulty), _auraEffectMask(auraEffMask), _owner(owner) +{ + ASSERT(spellInfo); + ASSERT(auraEffMask); + ASSERT(owner); + + ASSERT(auraEffMask <= MAX_EFFECT_MASK); +} + +AuraApplication::AuraApplication(Unit* target, Unit* caster, Aura* aura, uint32 effMask) : _target(target), _base(aura), _removeMode(AURA_REMOVE_NONE), _slot(MAX_AURAS), _flags(AFLAG_NONE), _effectsToApply(effMask), _needClientUpdate(false), _effectMask(0) { @@ -154,6 +180,37 @@ void AuraApplication::_HandleEffect(uint8 effIndex, bool apply) SetNeedClientUpdate(); } +void AuraApplication::UpdateApplyEffectMask(uint32 newEffMask) +{ + if (_effectsToApply == newEffMask) + return; + + uint32 removeEffMask = (_effectsToApply ^ newEffMask) & (~newEffMask); + uint32 addEffMask = (_effectsToApply ^ newEffMask) & (~_effectsToApply); + + // quick check, removes application completely + if (removeEffMask == _effectsToApply && !addEffMask) + { + _target->_UnapplyAura(this, AURA_REMOVE_BY_DEFAULT); + return; + } + + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + { + // update real effects only if they were applied already + if (!(_effectMask & (1 << i))) + continue; + + if (removeEffMask & (1 << i)) + _HandleEffect(i, false); + + if (addEffMask & (1 << i)) + _HandleEffect(i, true); + } + + _effectsToApply = newEffMask; +} + void AuraApplication::SetNeedClientUpdate() { if (_needClientUpdate || GetRemoveMode() != AURA_REMOVE_NONE) @@ -264,101 +321,130 @@ uint32 Aura::BuildEffectMaskForOwner(SpellInfo const* spellProto, uint32 availab return effMask & availableEffectMask; } -Aura* Aura::TryRefreshStackOrCreate(SpellInfo const* spellproto, ObjectGuid castId, uint32 tryEffMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32* baseAmount /*= nullptr*/, Item* castItem /*= nullptr*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/, bool* refresh /*= nullptr*/, bool resetPeriodicTimer /*= true*/, ObjectGuid castItemGuid /*= ObjectGuid::Empty*/, uint32 castItemId /*= 0*/, int32 castItemLevel /*= -1*/) +Aura* Aura::TryRefreshStackOrCreate(AuraCreateInfo& createInfo) { - ASSERT(spellproto); - ASSERT(owner); - ASSERT(caster || !casterGUID.IsEmpty()); - ASSERT(tryEffMask <= MAX_EFFECT_MASK); - if (refresh) - *refresh = false; + ASSERT(createInfo.Caster || !createInfo.CasterGUID.IsEmpty()); + + if (createInfo.IsRefresh) + *createInfo.IsRefresh = false; + + createInfo._auraEffectMask = Aura::BuildEffectMaskForOwner(createInfo._spellInfo, createInfo._auraEffectMask, createInfo._owner); + createInfo._targetEffectMask &= createInfo._auraEffectMask; + + uint32 effMask = createInfo._auraEffectMask; + if (createInfo._targetEffectMask) + effMask = createInfo._targetEffectMask; - uint32 effMask = Aura::BuildEffectMaskForOwner(spellproto, tryEffMask, owner); if (!effMask) return nullptr; - if (Aura* foundAura = owner->ToUnit()->_TryStackingOrRefreshingExistingAura(spellproto, effMask, caster, baseAmount, castItem, casterGUID, resetPeriodicTimer, castItemGuid, castItemLevel)) + if (Aura* foundAura = createInfo._owner->ToUnit()->_TryStackingOrRefreshingExistingAura(createInfo)) { // we've here aura, which script triggered removal after modding stack amount // check the state here, so we won't create new Aura object if (foundAura->IsRemoved()) return nullptr; - if (refresh) - *refresh = true; + if (createInfo.IsRefresh) + *createInfo.IsRefresh = true; + // add owner + Unit* unit = createInfo._owner->ToUnit(); + + // check effmask on owner application (if existing) + if (AuraApplication* aurApp = foundAura->GetApplicationOfTarget(unit->GetGUID())) + aurApp->UpdateApplyEffectMask(effMask); return foundAura; } else - return Create(spellproto, castId, effMask, owner, caster, castDifficulty, baseAmount, castItem, casterGUID, castItemGuid, castItemId, castItemLevel); + return Create(createInfo); } -Aura* Aura::TryCreate(SpellInfo const* spellproto, ObjectGuid castId, uint32 tryEffMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32* baseAmount /*= nullptr*/, Item* castItem /*= nullptr*/, ObjectGuid casterGUID /*= ObjectGuid::Empty*/, ObjectGuid castItemGuid /*= ObjectGuid::Empty*/, uint32 castItemId /*= 0*/, int32 castItemLevel /*= -1*/) +Aura* Aura::TryCreate(AuraCreateInfo& createInfo) { - ASSERT(spellproto); - ASSERT(owner); - ASSERT(caster || !casterGUID.IsEmpty()); - ASSERT(tryEffMask <= MAX_EFFECT_MASK); - uint32 effMask = Aura::BuildEffectMaskForOwner(spellproto, tryEffMask, owner); + ASSERT(createInfo.Caster || !createInfo.CasterGUID.IsEmpty()); + + uint32 effMask = createInfo._auraEffectMask; + if (createInfo._targetEffectMask) + effMask = createInfo._targetEffectMask; + + effMask = Aura::BuildEffectMaskForOwner(createInfo._spellInfo, effMask, createInfo._owner); if (!effMask) return nullptr; - return Create(spellproto, castId, effMask, owner, caster, castDifficulty, baseAmount, castItem, casterGUID, castItemGuid, castItemId, castItemLevel); + return Create(createInfo); } -Aura* Aura::Create(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel) +Aura* Aura::Create(AuraCreateInfo& createInfo) { - ASSERT(effMask); - ASSERT(spellproto); - ASSERT(owner); - ASSERT(caster || !casterGUID.IsEmpty()); - ASSERT(effMask <= MAX_EFFECT_MASK); + ASSERT(createInfo.Caster || !createInfo.CasterGUID.IsEmpty()); + // try to get caster of aura - if (!casterGUID.IsEmpty()) + if (!createInfo.CasterGUID.IsEmpty()) { - if (owner->GetGUID() == casterGUID) - caster = owner->ToUnit(); + if (createInfo._owner->GetGUID() == createInfo.CasterGUID) + createInfo.Caster = createInfo._owner->ToUnit(); else - caster = ObjectAccessor::GetUnit(*owner, casterGUID); + createInfo.Caster = ObjectAccessor::GetUnit(*createInfo._owner, createInfo.CasterGUID); } else - casterGUID = caster->GetGUID(); + createInfo.CasterGUID = createInfo.Caster->GetGUID(); // check if aura can be owned by owner - if (owner->isType(TYPEMASK_UNIT)) - if (!owner->IsInWorld() || ((Unit*)owner)->IsDuringRemoveFromWorld()) + if (createInfo._owner->isType(TYPEMASK_UNIT)) + if (!createInfo._owner->IsInWorld() || createInfo._owner->ToUnit()->IsDuringRemoveFromWorld()) // owner not in world so don't allow to own not self cast single target auras - if (casterGUID != owner->GetGUID() && spellproto->IsSingleTarget()) + if (createInfo.CasterGUID != createInfo._owner->GetGUID() && createInfo._spellInfo->IsSingleTarget()) return nullptr; Aura* aura = nullptr; - switch (owner->GetTypeId()) + switch (createInfo._owner->GetTypeId()) { case TYPEID_UNIT: case TYPEID_PLAYER: - aura = new UnitAura(spellproto, castId, effMask, owner, caster, castDifficulty, baseAmount, castItem, casterGUID, castItemGuid, castItemId, castItemLevel); + { + aura = new UnitAura(createInfo); + + // aura can be removed in Unit::_AddAura call + if (aura->IsRemoved()) + return nullptr; + + // add owner + uint32 effMask = createInfo._auraEffectMask; + if (createInfo._targetEffectMask) + effMask = createInfo._targetEffectMask; + + effMask = Aura::BuildEffectMaskForOwner(createInfo._spellInfo, effMask, createInfo._owner); + ASSERT(effMask); + + Unit* unit = createInfo._owner->ToUnit(); + aura->ToUnitAura()->AddStaticApplication(unit, effMask); break; + } case TYPEID_DYNAMICOBJECT: - aura = new DynObjAura(spellproto, castId, effMask, owner, caster, castDifficulty, baseAmount, castItem, casterGUID, castItemGuid, castItemId, castItemLevel); + createInfo._auraEffectMask = Aura::BuildEffectMaskForOwner(createInfo._spellInfo, createInfo._auraEffectMask, createInfo._owner); + ASSERT(createInfo._auraEffectMask); + + aura = new DynObjAura(createInfo); break; default: ABORT(); return nullptr; } - // aura can be removed in Unit::_AddAura call + // scripts, etc. if (aura->IsRemoved()) return nullptr; + return aura; } -Aura::Aura(SpellInfo const* spellproto, ObjectGuid castId, WorldObject* owner, Unit* caster, Difficulty castDifficulty, - Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel) : -m_spellInfo(spellproto), m_castDifficulty(castDifficulty), m_castGuid(castId), m_casterGuid(!casterGUID.IsEmpty() ? casterGUID : caster->GetGUID()), -m_castItemGuid(castItem ? castItem->GetGUID() : castItemGuid), m_castItemId(castItem ? castItem->GetEntry() : castItemId), -m_castItemLevel(castItemLevel), m_spellVisual({ caster ? caster->GetCastSpellXSpellVisualId(spellproto) : spellproto->GetSpellXSpellVisualId(), 0 }), -m_applyTime(GameTime::GetGameTime()), m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), -m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), +Aura::Aura(AuraCreateInfo const& createInfo) : +m_spellInfo(createInfo._spellInfo), m_castDifficulty(createInfo._castDifficulty), m_castGuid(createInfo._castId), m_casterGuid(createInfo.CasterGUID.IsEmpty() ? createInfo.Caster->GetGUID() : createInfo.CasterGUID), +m_castItemGuid(createInfo.CastItemGUID), m_castItemId(createInfo.CastItemId), +m_castItemLevel(createInfo.CastItemLevel), m_spellVisual({ createInfo.Caster ? createInfo.Caster->GetCastSpellXSpellVisualId(createInfo._spellInfo) : createInfo._spellInfo->GetSpellXSpellVisualId(), 0 }), +m_applyTime(GameTime::GetGameTime()), m_owner(createInfo._owner), m_timeCla(0), m_updateTargetMapInterval(0), +m_casterLevel(createInfo.Caster ? createInfo.Caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), m_procCooldown(std::chrono::steady_clock::time_point::min()), m_lastProcAttemptTime(std::chrono::steady_clock::now() - Seconds(10)), m_lastProcSuccessTime(std::chrono::steady_clock::now() - Seconds(120)) @@ -370,9 +456,9 @@ m_lastProcAttemptTime(std::chrono::steady_clock::now() - Seconds(10)), m_lastPro if (!m_periodicCosts.empty()) m_timeCla = 1 * IN_MILLISECONDS; - m_maxDuration = CalcMaxDuration(caster); + m_maxDuration = CalcMaxDuration(createInfo.Caster); m_duration = m_maxDuration; - m_procCharges = CalcMaxCharges(caster); + m_procCharges = CalcMaxCharges(createInfo.Caster); m_isUsingCharges = m_procCharges != 0; // m_casterLevel = cast item level/caster level, caster level should be saved to db, confirmed with sniffs } @@ -385,7 +471,7 @@ AuraScript* Aura::GetScriptByName(std::string const& scriptName) const return nullptr; } -void Aura::_InitEffects(uint32 effMask, Unit* caster, int32 *baseAmount) +void Aura::_InitEffects(uint32 effMask, Unit* caster, int32 const* baseAmount) { // shouldn't be in constructor - functions in AuraEffect::AuraEffect use polymorphism _effects.resize(GetSpellInfo()->GetEffects().size()); @@ -434,7 +520,7 @@ AuraObjectType Aura::GetType() const return (m_owner->GetTypeId() == TYPEID_DYNAMICOBJECT) ? DYNOBJ_AURA_TYPE : UNIT_AURA_TYPE; } -void Aura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication * auraApp) +void Aura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication* auraApp) { ASSERT(target); ASSERT(auraApp); @@ -454,7 +540,7 @@ void Aura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication * auraApp } } -void Aura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication * auraApp) +void Aura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication* auraApp) { ASSERT(target); ASSERT(auraApp->GetRemoveMode()); @@ -474,7 +560,7 @@ void Aura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication * auraA ASSERT(itr->second == auraApp); m_applications.erase(itr); - m_removedApplications.push_back(auraApp); + _removedApplications.push_back(auraApp); // reset cooldown state for spells if (caster && GetSpellInfo()->IsCooldownStartedOnEvent()) @@ -517,61 +603,45 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) FillTargetMap(targets, caster); - std::deque<Unit*> targetsToRemove; + std::vector<Unit*> targetsToRemove; // mark all auras as ready to remove - for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end();++appIter) + for (auto const& applicationPair : m_applications) { - auto itr = targets.find(appIter->second->GetTarget()); + auto itr = targets.find(applicationPair.second->GetTarget()); // not found in current area - remove the aura if (itr == targets.end()) - targetsToRemove.push_back(appIter->second->GetTarget()); + targetsToRemove.push_back(applicationPair.second->GetTarget()); else { // needs readding - remove now, will be applied in next update cycle // (dbcs do not have auras which apply on same type of targets but have different radius, so this is not really needed) - if (appIter->second->GetEffectMask() != itr->second || !CanBeAppliedOn(itr->first)) - targetsToRemove.push_back(appIter->second->GetTarget()); - // nothing todo - aura already applied + if (!CanBeAppliedOn(itr->first)) + { + targetsToRemove.push_back(applicationPair.second->GetTarget()); + continue; + } + + // needs to add/remove effects from application, don't remove from map so it gets updated + if (applicationPair.second->GetEffectMask() != itr->second) + continue; + + // nothing to do - aura already applied // remove from auras to register list targets.erase(itr); } } // register auras for units - for (auto itr = targets.begin(); itr!= targets.end();) + for (auto itr = targets.begin(); itr != targets.end();) { - // aura mustn't be already applied on target - if (AuraApplication* aurApp = GetApplicationOfTarget(itr->first->GetGUID())) - { - // the core created 2 different units with same guid - // this is a major failue, which i can't fix right now - // let's remove one unit from aura list - // this may cause area aura "bouncing" between 2 units after each update - // but because we know the reason of a crash we can remove the assertion for now - if (aurApp->GetTarget() != itr->first) - { - // remove from auras to register list - itr = targets.erase(itr); - continue; - } - else - { - // ok, we have one unit twice in target map (impossible, but...) - ABORT(); - } - } - bool addUnit = true; // check target immunities for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - { if (itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, caster)) itr->second &= ~(1 << effIndex); - } - if (!itr->second - || itr->first->IsImmunedToSpell(GetSpellInfo(), caster) - || !CanBeAppliedOn(itr->first)) + + if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), caster) || !CanBeAppliedOn(itr->first)) addUnit = false; if (addUnit && !itr->first->IsHighestExclusiveAura(this, true)) @@ -605,6 +675,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) } } } + if (!addUnit) itr = targets.erase(itr); else @@ -618,6 +689,13 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) itr->first->GetName().c_str(), itr->first->IsInWorld() ? itr->first->GetMap()->GetId() : uint32(-1)); ABORT(); } + + if (AuraApplication* aurApp = GetApplicationOfTarget(itr->first->GetGUID())) + { + // aura is already applied, this means we need to update effects of current application + itr->first->_UnapplyAura(aurApp, AURA_REMOVE_BY_DEFAULT); + } + itr->first->_CreateAuraApplication(this, itr->second); ++itr; } @@ -913,20 +991,20 @@ void Aura::SetStackAmount(uint8 stackAmount) m_stackAmount = stackAmount; Unit* caster = GetCaster(); - std::list<AuraApplication*> applications; - GetApplicationList(applications); + std::vector<AuraApplication*> applications; + GetApplicationVector(applications); - for (std::list<AuraApplication*>::const_iterator apptItr = applications.begin(); apptItr != applications.end(); ++apptItr) - if (!(*apptItr)->GetRemoveMode()) - HandleAuraSpecificMods(*apptItr, caster, false, true); + for (AuraApplication* aurApp : applications) + if (!aurApp->GetRemoveMode()) + HandleAuraSpecificMods(aurApp, caster, false, true); - for (AuraEffect* effect : GetAuraEffects()) - if (effect) - effect->ChangeAmount(effect->CalculateAmount(caster), false, true); + for (AuraEffect* aurEff : GetAuraEffects()) + if (aurEff) + aurEff->ChangeAmount(aurEff->CalculateAmount(caster), false, true); - for (std::list<AuraApplication*>::const_iterator apptItr = applications.begin(); apptItr != applications.end(); ++apptItr) - if (!(*apptItr)->GetRemoveMode()) - HandleAuraSpecificMods(*apptItr, caster, true, true); + for (AuraApplication* aurApp : applications) + if (!aurApp->GetRemoveMode()) + HandleAuraSpecificMods(aurApp, caster, true, true); SetNeedClientUpdateForTargets(); } @@ -1013,6 +1091,14 @@ bool Aura::IsDeathPersistent() const return GetSpellInfo()->IsDeathPersistent(); } +bool Aura::IsRemovedOnShapeLost(Unit* target) const +{ + return GetCasterGUID() == target->GetGUID() + && m_spellInfo->Stances + && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT) + && !m_spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT); +} + bool Aura::CanBeSaved() const { if (IsPassive()) @@ -1191,18 +1277,20 @@ void Aura::HandleAllEffects(AuraApplication * aurApp, uint8 mode, bool apply) uint32 Aura::GetEffectMask() const { uint32 effMask = 0; - for (AuraEffect* effect : GetAuraEffects()) - if (effect) - effMask |= 1 << effect->GetEffIndex(); + for (AuraEffect* aurEff : GetAuraEffects()) + if (aurEff) + effMask |= 1 << aurEff->GetEffIndex(); return effMask; } -void Aura::GetApplicationList(Unit::AuraApplicationList& applicationList) const +void Aura::GetApplicationVector(std::vector<AuraApplication*>& applicationList) const { - for (Aura::ApplicationMap::const_iterator appIter = m_applications.begin(); appIter != m_applications.end(); ++appIter) + for (auto const& applicationPair : m_applications) { - if (appIter->second->GetEffectMask()) - applicationList.push_back(appIter->second); + if (!applicationPair.second->GetEffectMask()) + continue; + + applicationList.push_back(applicationPair.second); } } @@ -1828,11 +1916,10 @@ float Aura::CalcPPMProcChance(Unit* actor) const void Aura::_DeleteRemovedApplications() { - while (!m_removedApplications.empty()) - { - delete m_removedApplications.front(); - m_removedApplications.pop_front(); - } + for (AuraApplication* aurApp : _removedApplications) + delete aurApp; + + _removedApplications.clear(); } void Aura::LoadScripts() @@ -1987,7 +2074,7 @@ void Aura::CallScriptEffectUpdatePeriodicHandlers(AuraEffect* aurEff) } } -void Aura::CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32 & amount, bool & canBeRecalculated) +void Aura::CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2001,7 +2088,7 @@ void Aura::CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32 & } } -void Aura::CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool & isPeriodic, int32 & amplitude) +void Aura::CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool& isPeriodic, int32& amplitude) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2015,7 +2102,7 @@ void Aura::CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool & } } -void Aura::CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellModifier* & spellMod) +void Aura::CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellModifier*& spellMod) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2042,7 +2129,7 @@ void Aura::CallScriptEffectCalcCritChanceHandlers(AuraEffect const* aurEff, Aura } } -void Aura::CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool& defaultPrevented) +void Aura::CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount, bool& defaultPrevented) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2060,7 +2147,7 @@ void Aura::CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication co } } -void Aura::CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount) +void Aura::CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2074,7 +2161,7 @@ void Aura::CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplicati } } -void Aura::CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool & /*defaultPrevented*/) +void Aura::CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount, bool& /*defaultPrevented*/) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2088,7 +2175,7 @@ void Aura::CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplicatio } } -void Aura::CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount) +void Aura::CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2102,7 +2189,7 @@ void Aura::CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraAppli } } -void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount) +void Aura::CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& splitAmount) { for (auto scritr = m_loadedScripts.begin(); scritr != m_loadedScripts.end(); ++scritr) { @@ -2243,17 +2330,16 @@ void Aura::CallScriptAfterEffectProcHandlers(AuraEffect* aurEff, AuraApplication } } -UnitAura::UnitAura(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, - int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel) - : Aura(spellproto, castId, owner, caster, castDifficulty, castItem, casterGUID, castItemGuid, castItemId, castItemLevel) +UnitAura::UnitAura(AuraCreateInfo const& createInfo) + : Aura(createInfo) { m_AuraDRGroup = DIMINISHING_NONE; LoadScripts(); - _InitEffects(effMask, caster, baseAmount); - GetUnitOwner()->_AddAura(this, caster); + _InitEffects(createInfo._auraEffectMask, createInfo.Caster, createInfo.BaseAmount); + GetUnitOwner()->_AddAura(this, createInfo.Caster); } -void UnitAura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication * aurApp) +void UnitAura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication* aurApp) { Aura::_ApplyForTarget(target, caster, aurApp); @@ -2262,7 +2348,7 @@ void UnitAura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication * aur target->ApplyDiminishingAura(group, true); } -void UnitAura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication * aurApp) +void UnitAura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication* aurApp) { Aura::_UnapplyForTarget(target, caster, aurApp); @@ -2284,115 +2370,118 @@ void UnitAura::FillTargetMap(std::unordered_map<Unit*, uint32>& targets, Unit* c if (!ref) ref = GetUnitOwner(); + // add non area aura targets + // static applications go through spell system first, so we assume they meet conditions + for (auto const& targetPair : _staticApplications) + if (Unit* target = ObjectAccessor::GetUnit(*GetUnitOwner(), targetPair.first)) + targets.emplace(target, targetPair.second); + for (SpellEffectInfo const* effect : GetSpellInfo()->GetEffects()) { if (!effect || !HasEffect(effect->EffectIndex)) continue; - std::vector<Unit*> units; - ConditionContainer* condList = effect->ImplicitTargetConditions; - // non-area aura + // area auras only if (effect->Effect == SPELL_EFFECT_APPLY_AURA) - { - if (!condList || sConditionMgr->IsObjectMeetToConditions(GetUnitOwner(), ref, *condList)) - units.push_back(GetUnitOwner()); - } - else - { - // skip area update if owner is not in world! - if (!GetUnitOwner()->IsInWorld()) - continue; + continue; - if (GetUnitOwner()->HasUnitState(UNIT_STATE_ISOLATED)) - continue; + // skip area update if owner is not in world! + if (!GetUnitOwner()->IsInWorld()) + continue; - float radius = effect->CalcRadius(caster); - SpellTargetCheckTypes selectionType = TARGET_CHECK_DEFAULT; - switch (effect->Effect) - { - case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: - case SPELL_EFFECT_APPLY_AREA_AURA_PARTY_NONRANDOM: - selectionType = TARGET_CHECK_PARTY; - break; - case SPELL_EFFECT_APPLY_AREA_AURA_RAID: - selectionType = TARGET_CHECK_RAID; - break; - case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: - selectionType = TARGET_CHECK_ALLY; - break; - case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: - selectionType = TARGET_CHECK_ENEMY; - break; - case SPELL_EFFECT_APPLY_AREA_AURA_PET: - if (!condList || sConditionMgr->IsObjectMeetToConditions(GetUnitOwner(), ref, *condList)) - units.push_back(GetUnitOwner()); - /* fallthrough */ - case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: - { - if (Unit* owner = GetUnitOwner()->GetCharmerOrOwner()) - if (GetUnitOwner()->IsWithinDistInMap(owner, radius)) - if (!condList || sConditionMgr->IsObjectMeetToConditions(owner, ref, *condList)) - units.push_back(owner); - break; - } - case SPELL_EFFECT_APPLY_AURA_ON_PET: - { - if (Unit* pet = ObjectAccessor::GetUnit(*GetUnitOwner(), GetUnitOwner()->GetPetGUID())) - if (!condList || sConditionMgr->IsObjectMeetToConditions(pet, ref, *condList)) - units.push_back(pet); - break; - } - case SPELL_EFFECT_APPLY_AREA_AURA_SUMMONS: - { - if (!condList || sConditionMgr->IsObjectMeetToConditions(GetUnitOwner(), ref, *condList)) - units.push_back(GetUnitOwner()); + if (GetUnitOwner()->HasUnitState(UNIT_STATE_ISOLATED)) + continue; - selectionType = TARGET_CHECK_SUMMONED; - break; - } - } + std::vector<Unit*> units; + ConditionContainer* condList = effect->ImplicitTargetConditions; - if (selectionType != TARGET_CHECK_DEFAULT) + float radius = effect->CalcRadius(ref); + SpellTargetCheckTypes selectionType = TARGET_CHECK_DEFAULT; + switch (effect->Effect) + { + case SPELL_EFFECT_APPLY_AREA_AURA_PARTY: + case SPELL_EFFECT_APPLY_AREA_AURA_PARTY_NONRANDOM: + selectionType = TARGET_CHECK_PARTY; + break; + case SPELL_EFFECT_APPLY_AREA_AURA_RAID: + selectionType = TARGET_CHECK_RAID; + break; + case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND: + selectionType = TARGET_CHECK_ALLY; + break; + case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY: + selectionType = TARGET_CHECK_ENEMY; + break; + case SPELL_EFFECT_APPLY_AREA_AURA_PET: + if (!condList || sConditionMgr->IsObjectMeetToConditions(GetUnitOwner(), ref, *condList)) + units.push_back(GetUnitOwner()); + /* fallthrough */ + case SPELL_EFFECT_APPLY_AREA_AURA_OWNER: { - Trinity::WorldObjectSpellAreaTargetCheck check(radius, GetUnitOwner(), ref, GetUnitOwner(), m_spellInfo, selectionType, condList, TARGET_OBJECT_TYPE_UNIT); - Trinity::UnitListSearcher<Trinity::WorldObjectSpellAreaTargetCheck> searcher(GetUnitOwner(), units, check); - Cell::VisitAllObjects(GetUnitOwner(), searcher, radius); + if (Unit* owner = GetUnitOwner()->GetCharmerOrOwner()) + if (GetUnitOwner()->IsWithinDistInMap(owner, radius)) + if (!condList || sConditionMgr->IsObjectMeetToConditions(owner, ref, *condList)) + units.push_back(owner); + break; + } + case SPELL_EFFECT_APPLY_AURA_ON_PET: + { + if (Unit* pet = ObjectAccessor::GetUnit(*GetUnitOwner(), GetUnitOwner()->GetPetGUID())) + if (!condList || sConditionMgr->IsObjectMeetToConditions(pet, ref, *condList)) + units.push_back(pet); + break; + } + case SPELL_EFFECT_APPLY_AREA_AURA_SUMMONS: + { + if (!condList || sConditionMgr->IsObjectMeetToConditions(GetUnitOwner(), ref, *condList)) + units.push_back(GetUnitOwner()); - // by design WorldObjectSpellAreaTargetCheck allows not-in-world units (for spells) but for auras it is not acceptable - units.erase(std::remove_if(units.begin(), units.end(), [this](Unit* unit) { return !unit->IsSelfOrInSameMap(GetUnitOwner()); }), units.end()); + selectionType = TARGET_CHECK_SUMMONED; + break; } } - for (Unit* unit : units) + if (selectionType != TARGET_CHECK_DEFAULT) { - auto itr = targets.find(unit); - if (itr != targets.end()) - itr->second |= 1 << effect->EffectIndex; - else - targets[unit] = 1 << effect->EffectIndex; + Trinity::WorldObjectSpellAreaTargetCheck check(radius, GetUnitOwner(), ref, GetUnitOwner(), m_spellInfo, selectionType, condList, TARGET_OBJECT_TYPE_UNIT); + Trinity::UnitListSearcher<Trinity::WorldObjectSpellAreaTargetCheck> searcher(GetUnitOwner(), units, check); + Cell::VisitAllObjects(GetUnitOwner(), searcher, radius); + + // by design WorldObjectSpellAreaTargetCheck allows not-in-world units (for spells) but for auras it is not acceptable + units.erase(std::remove_if(units.begin(), units.end(), [this](Unit* unit) { return !unit->IsSelfOrInSameMap(GetUnitOwner()); }), units.end()); } + + for (Unit* unit : units) + targets[unit] |= 1 << effect->EffectIndex; } } -DynObjAura::DynObjAura(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, - int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel) - : Aura(spellproto, castId, owner, caster, castDifficulty, castItem, casterGUID, castItemGuid, castItemId, castItemLevel) +void UnitAura::AddStaticApplication(Unit* target, uint32 effMask) +{ + // only valid for non-area auras + for (SpellEffectInfo const* effect : GetSpellInfo()->GetEffects()) + { + if (effect && (effMask & (1 << effect->EffectIndex)) && effect->Effect != SPELL_EFFECT_APPLY_AURA) + effMask &= ~(1 << effect->EffectIndex); + } + + if (!effMask) + return; + + _staticApplications[target->GetGUID()] |= effMask; +} + +DynObjAura::DynObjAura(AuraCreateInfo const& createInfo) + : Aura(createInfo) { LoadScripts(); ASSERT(GetDynobjOwner()); ASSERT(GetDynobjOwner()->IsInWorld()); - ASSERT(GetDynobjOwner()->GetMap() == caster->GetMap()); - _InitEffects(effMask, caster, baseAmount); + ASSERT(GetDynobjOwner()->GetMap() == createInfo.Caster->GetMap()); + _InitEffects(createInfo._auraEffectMask, createInfo.Caster, createInfo.BaseAmount); GetDynobjOwner()->SetAura(this); } -bool ChargeDropEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/) -{ - // _base is always valid (look in Aura::_Remove()) - _base->ModChargesDelayed(-1, _mode); - return true; -} - void DynObjAura::Remove(AuraRemoveMode removeMode) { if (IsRemoved()) @@ -2426,12 +2515,6 @@ void DynObjAura::FillTargetMap(std::unordered_map<Unit*, uint32>& targets, Unit* units.erase(std::remove_if(units.begin(), units.end(), [this](Unit* unit) { return !unit->IsSelfOrInSameMap(GetDynobjOwner()); }), units.end()); for (Unit* unit : units) - { - auto itr = targets.find(unit); - if (itr != targets.end()) - itr->second |= 1 << effect->EffectIndex; - else - targets[unit] = 1 << effect->EffectIndex; - } + targets[unit] |= 1 << effect->EffectIndex; } } diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 38ac5b41c38..189b65a7d1d 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -20,7 +20,6 @@ #include "SpellAuraDefines.h" #include "SpellInfo.h" -#include "Unit.h" class SpellInfo; struct SpellModifier; @@ -36,23 +35,25 @@ namespace WorldPackets } // forward decl -class AuraEffect; class Aura; -class DynamicObject; +class AuraEffect; class AuraScript; -class ProcInfo; +class DamageInfo; +class DispelInfo; +class DynObjAura; class ChargeDropEvent; +class DynamicObject; +class ProcEventInfo; +class Unit; +class UnitAura; // update aura target map every 500 ms instead of every update - reduce amount of grid searcher calls #define UPDATE_TARGET_MAP_INTERVAL 500 class TC_GAME_API AuraApplication { - friend void Unit::_ApplyAura(AuraApplication * aurApp, uint32 effMask); - friend void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode); - friend void Unit::_ApplyAuraEffect(Aura* aura, uint8 effIndex); - friend void Unit::RemoveAura(AuraApplication * aurApp, AuraRemoveMode mode); - friend AuraApplication * Unit::_CreateAuraApplication(Aura* aura, uint32 effMask); + friend class Unit; + private: Unit* const _target; Aura* const _base; @@ -65,21 +66,23 @@ class TC_GAME_API AuraApplication explicit AuraApplication(Unit* target, Unit* caster, Aura* base, uint32 effMask); void _Remove(); - private: + void _InitFlags(Unit* caster, uint32 effMask); void _HandleEffect(uint8 effIndex, bool apply); - public: + public: Unit* GetTarget() const { return _target; } Aura* GetBase() const { return _base; } uint8 GetSlot() const { return _slot; } uint16 GetFlags() const { return _flags; } uint32 GetEffectMask() const { return _effectMask; } - bool HasEffect(uint8 effect) const { ASSERT(effect < MAX_SPELL_EFFECTS); return (_effectMask & (1 << effect)) != 0; } + bool HasEffect(uint8 effect) const { ASSERT(effect < MAX_SPELL_EFFECTS); return (_effectMask & (1 << effect)) != 0; } bool IsPositive() const { return (_flags & AFLAG_POSITIVE) != 0; } bool IsSelfcast() const { return (_flags & AFLAG_NOCASTER) != 0; } + uint32 GetEffectsToApply() const { return _effectsToApply; } + void UpdateApplyEffectMask(uint32 newEffMask); void SetRemoveMode(AuraRemoveMode mode) { _removeMode = mode; } AuraRemoveMode GetRemoveMode() const { return _removeMode; } @@ -114,16 +117,18 @@ struct AuraLoadEffectInfo class TC_GAME_API Aura { - friend Aura* Unit::_TryStackingOrRefreshingExistingAura(SpellInfo const* newAura, uint32 effMask, Unit* caster, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, bool resetPeriodicTimer, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); + friend class Unit; + public: - typedef std::map<ObjectGuid, AuraApplication*> ApplicationMap; + typedef std::unordered_map<ObjectGuid, AuraApplication*> ApplicationMap; static uint32 BuildEffectMaskForOwner(SpellInfo const* spellProto, uint32 availableEffectMask, WorldObject* owner); - static Aura* TryRefreshStackOrCreate(SpellInfo const* spellproto, ObjectGuid castId, uint32 tryEffMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty, bool* refresh = nullptr, bool resetPeriodicTimer = true, ObjectGuid castItemGuid = ObjectGuid::Empty, uint32 castItemId = 0, int32 castItemLevel = -1); - static Aura* TryCreate(SpellInfo const* spellproto, ObjectGuid castId, uint32 tryEffMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32* baseAmount = nullptr, Item* castItem = nullptr, ObjectGuid casterGUID = ObjectGuid::Empty, ObjectGuid castItemGuid = ObjectGuid::Empty, uint32 castItemId = 0, int32 castItemLevel = -1); - static Aura* Create(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32* baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); - Aura(SpellInfo const* spellproto, ObjectGuid castId, WorldObject* owner, Unit* caster, Difficulty castDifficulty, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); - void _InitEffects(uint32 effMask, Unit* caster, int32 *baseAmount); + static Aura* TryRefreshStackOrCreate(AuraCreateInfo& createInfo); + static Aura* TryCreate(AuraCreateInfo& createInfo); + static Aura* Create(AuraCreateInfo& createInfo); + explicit Aura(AuraCreateInfo const& createInfo); + void _InitEffects(uint32 effMask, Unit* caster, int32 const* baseAmount); + void SaveCasterInfo(Unit* caster); virtual ~Aura(); SpellInfo const* GetSpellInfo() const { return m_spellInfo; } @@ -138,8 +143,8 @@ class TC_GAME_API Aura SpellCastVisual GetSpellVisual() const { return m_spellVisual; } Unit* GetCaster() const; WorldObject* GetOwner() const { return m_owner; } - Unit* GetUnitOwner() const { ASSERT(GetType() == UNIT_AURA_TYPE); return (Unit*)m_owner; } - DynamicObject* GetDynobjOwner() const { ASSERT(GetType() == DYNOBJ_AURA_TYPE); return (DynamicObject*)m_owner; } + Unit* GetUnitOwner() const { ASSERT(GetType() == UNIT_AURA_TYPE); return m_owner->ToUnit(); } + DynamicObject* GetDynobjOwner() const { ASSERT(GetType() == DYNOBJ_AURA_TYPE); return m_owner->ToDynObject(); } AuraObjectType GetType() const; @@ -151,8 +156,8 @@ class TC_GAME_API Aura virtual void FillTargetMap(std::unordered_map<Unit*, uint32>& targets, Unit* caster) = 0; void UpdateTargetMap(Unit* caster, bool apply = true); - void _RegisterForTargets() {Unit* caster = GetCaster(); UpdateTargetMap(caster, false);} - void ApplyForTargets() {Unit* caster = GetCaster(); UpdateTargetMap(caster, true);} + void _RegisterForTargets() { Unit* caster = GetCaster(); UpdateTargetMap(caster, false); } + void ApplyForTargets() { Unit* caster = GetCaster(); UpdateTargetMap(caster, true); } void _ApplyEffectForTargets(uint8 effIndex); void UpdateOwner(uint32 diff, WorldObject* owner); @@ -192,13 +197,7 @@ class TC_GAME_API Aura bool IsPassive() const; bool IsDeathPersistent() const; - bool IsRemovedOnShapeLost(Unit* target) const - { - return GetCasterGUID() == target->GetGUID() - && m_spellInfo->Stances - && !m_spellInfo->HasAttribute(SPELL_ATTR2_NOT_NEED_SHAPESHIFT) - && !m_spellInfo->HasAttribute(SPELL_ATTR0_NOT_SHAPESHIFT); - } + bool IsRemovedOnShapeLost(Unit* target) const; bool CanBeSaved() const; bool IsRemoved() const { return m_isRemoved; } @@ -231,7 +230,7 @@ class TC_GAME_API Aura // Helpers for targets ApplicationMap const& GetApplicationMap() { return m_applications; } - void GetApplicationList(Unit::AuraApplicationList& applicationList) const; + void GetApplicationVector(std::vector<AuraApplication*>& applicationVector) const; AuraApplication const* GetApplicationOfTarget(ObjectGuid guid) const { ApplicationMap::const_iterator itr = m_applications.find(guid); if (itr != m_applications.end()) return itr->second; return nullptr; } AuraApplication* GetApplicationOfTarget(ObjectGuid guid) { ApplicationMap::iterator itr = m_applications.find(guid); if (itr != m_applications.end()) return itr->second; return nullptr; } bool IsAppliedOnTarget(ObjectGuid guid) const { return m_applications.find(guid) != m_applications.end(); } @@ -265,15 +264,15 @@ class TC_GAME_API Aura void CallScriptAfterEffectRemoveHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, AuraEffectHandleModes mode); bool CallScriptEffectPeriodicHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp); void CallScriptEffectUpdatePeriodicHandlers(AuraEffect* aurEff); - void CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32 & amount, bool & canBeRecalculated); - void CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool & isPeriodic, int32 & amplitude); - void CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellModifier* & spellMod); + void CallScriptEffectCalcAmountHandlers(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated); + void CallScriptEffectCalcPeriodicHandlers(AuraEffect const* aurEff, bool& isPeriodic, int32& amplitude); + void CallScriptEffectCalcSpellModHandlers(AuraEffect const* aurEff, SpellModifier*& spellMod); void CallScriptEffectCalcCritChanceHandlers(AuraEffect const* aurEff, AuraApplication const* aurApp, Unit const* victim, float& critChance); - void CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool & defaultPrevented); - void CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount); - void CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount, bool & defaultPrevented); - void CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & absorbAmount); - void CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo & dmgInfo, uint32 & splitAmount); + void CallScriptEffectAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount, bool & defaultPrevented); + void CallScriptEffectAfterAbsorbHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount); + void CallScriptEffectManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount, bool & defaultPrevented); + void CallScriptEffectAfterManaShieldHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& absorbAmount); + void CallScriptEffectSplitHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, DamageInfo& dmgInfo, uint32& splitAmount); void CallScriptEnterLeaveCombatHandlers(AuraApplication const* aurApp, bool isNowInCombat); // Spell Proc Hooks bool CallScriptCheckProcHandlers(AuraApplication const* aurApp, ProcEventInfo& eventInfo); @@ -284,6 +283,12 @@ class TC_GAME_API Aura bool CallScriptEffectProcHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); void CallScriptAfterEffectProcHandlers(AuraEffect* aurEff, AuraApplication const* aurApp, ProcEventInfo& eventInfo); + UnitAura* ToUnitAura() { if (GetType() == UNIT_AURA_TYPE) return reinterpret_cast<UnitAura*>(this); else return nullptr; } + UnitAura const* ToUnitAura() const { if (GetType() == UNIT_AURA_TYPE) return reinterpret_cast<UnitAura const*>(this); else return nullptr; } + + DynObjAura* ToDynObjAura() { if (GetType() == DYNOBJ_AURA_TYPE) return reinterpret_cast<DynObjAura*>(this); else return nullptr; } + DynObjAura const* ToDynObjAura() const { if (GetType() == DYNOBJ_AURA_TYPE) return reinterpret_cast<DynObjAura const*>(this); else return nullptr; } + template <class Script> Script* GetScript(std::string const& scriptName) const { @@ -333,19 +338,19 @@ class TC_GAME_API Aura std::chrono::steady_clock::time_point m_lastProcSuccessTime; private: - Unit::AuraApplicationList m_removedApplications; + std::vector<AuraApplication*> _removedApplications; AuraEffectVector _effects; }; class TC_GAME_API UnitAura : public Aura { - friend Aura* Aura::Create(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); + friend Aura* Aura::Create(AuraCreateInfo& createInfo); + protected: + explicit UnitAura(AuraCreateInfo const& createInfo); public: - UnitAura(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); - - void _ApplyForTarget(Unit* target, Unit* caster, AuraApplication * aurApp) override; - void _UnapplyForTarget(Unit* target, Unit* caster, AuraApplication * aurApp) override; + void _ApplyForTarget(Unit* target, Unit* caster, AuraApplication* aurApp) override; + void _UnapplyForTarget(Unit* target, Unit* caster, AuraApplication* aurApp) override; void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT) override; @@ -355,13 +360,18 @@ class TC_GAME_API UnitAura : public Aura void SetDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; } DiminishingGroup GetDiminishGroup() const { return m_AuraDRGroup; } + void AddStaticApplication(Unit* target, uint32 effMask); + private: DiminishingGroup m_AuraDRGroup; // Diminishing + std::unordered_map<ObjectGuid, uint32> _staticApplications; // non-area auras }; class TC_GAME_API DynObjAura : public Aura { - friend Aura* Aura::Create(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); + friend Aura* Aura::Create(AuraCreateInfo& createInfo); + protected: + explicit DynObjAura(AuraCreateInfo const& createInfo); public: DynObjAura(SpellInfo const* spellproto, ObjectGuid castId, uint32 effMask, WorldObject* owner, Unit* caster, Difficulty castDifficulty, int32 *baseAmount, Item* castItem, ObjectGuid casterGUID, ObjectGuid castItemGuid, uint32 castItemId, int32 castItemLevel); @@ -370,16 +380,4 @@ class TC_GAME_API DynObjAura : public Aura void FillTargetMap(std::unordered_map<Unit*, uint32>& targets, Unit* caster) override; }; -class TC_GAME_API ChargeDropEvent : public BasicEvent -{ - friend class Aura; - protected: - ChargeDropEvent(Aura* base, AuraRemoveMode mode) : _base(base), _mode(mode) { } - bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override; - - private: - Aura* _base; - AuraRemoveMode _mode; -}; - #endif diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index cfb0e4703e9..209a1de6d28 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -597,7 +597,8 @@ m_spellValue(new SpellValue(m_spellInfo, caster)), _spellEvent(nullptr) memset(m_misc.Raw.Data, 0, sizeof(m_misc.Raw.Data)); m_SpellVisual.SpellXSpellVisualID = caster->GetCastSpellXSpellVisualId(m_spellInfo); m_triggeredByAuraSpell = nullptr; - m_spellAura = nullptr; + _spellAura = nullptr; + _dynObjAura = nullptr; //Auto Shot & Shoot (wand) m_autoRepeat = m_spellInfo->IsAutoRepeatRangedSpell(); @@ -2457,9 +2458,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) uint32 procVictim = m_procVictim; uint32 hitMask = PROC_HIT_NONE; - m_spellAura = nullptr; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied - - // Spells with this flag cannot trigger if effect is cast on self + // Spells with this flag cannot trigger if effect is cast on self bool const canEffectTrigger = !m_spellInfo->HasAttribute(SPELL_ATTR3_CANT_TRIGGER_PROC) && unitTarget->CanProc() && (CanExecuteTriggersOnHit(mask) || missInfo == SPELL_MISS_IMMUNE || missInfo == SPELL_MISS_IMMUNE2); Unit* spellHitTarget = nullptr; @@ -2744,13 +2743,10 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) } } - uint32 aura_effmask = 0; - for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) - if (effect && (effectMask & (1 << effect->EffectIndex) && effect->IsUnitOwnedAuraEffect())) - aura_effmask |= 1 << effect->EffectIndex; + uint32 aura_effmask = Aura::BuildEffectMaskForOwner(m_spellInfo, effectMask, unit); // Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add - DiminishingGroup const diminishGroup = m_spellInfo->GetDiminishingReturnsGroupForSpell(); + DiminishingGroup diminishGroup = m_spellInfo->GetDiminishingReturnsGroupForSpell(); DiminishingLevels diminishLevel = DIMINISHING_LEVEL_1; if (diminishGroup && aura_effmask) @@ -2774,19 +2770,36 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) auraSpellEffect->CalcBaseValue(m_originalCaster, unit, m_castItemEntry, m_castItemLevel); bool refresh = false; - bool const resetPeriodicTimer = !(_triggeredCastFlags & TRIGGERED_DONT_RESET_PERIODIC_TIMER); - m_spellAura = Aura::TryRefreshStackOrCreate(m_spellInfo, m_castId, effectMask, unit, - m_originalCaster, GetCastDifficulty(), basePoints, - m_CastItem, ObjectGuid::Empty, &refresh, resetPeriodicTimer, ObjectGuid::Empty, m_castItemEntry, m_castItemLevel); - if (m_spellAura) + + if (!_spellAura) + { + bool const resetPeriodicTimer = !(_triggeredCastFlags & TRIGGERED_DONT_RESET_PERIODIC_TIMER); + uint32 const allAuraEffectMask = Aura::BuildEffectMaskForOwner(m_spellInfo, MAX_EFFECT_MASK, unit); + + AuraCreateInfo createInfo(m_castId, m_spellInfo, GetCastDifficulty(), allAuraEffectMask, unit); + createInfo + .SetCaster(m_originalCaster) + .SetBaseAmount(basePoints) + .SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel) + .SetPeriodicReset(resetPeriodicTimer) + .SetOwnerEffectMask(aura_effmask) + .IsRefresh = &refresh; + + if (Aura* aura = Aura::TryRefreshStackOrCreate(createInfo)) + _spellAura = aura->ToUnitAura(); + } + else + _spellAura->AddStaticApplication(unit, aura_effmask); + + if (_spellAura) { // Set aura stack amount to desired value if (m_spellValue->AuraStackAmount > 1) { if (!refresh) - m_spellAura->SetStackAmount(m_spellValue->AuraStackAmount); + _spellAura->SetStackAmount(m_spellValue->AuraStackAmount); else - m_spellAura->ModStackAmount(m_spellValue->AuraStackAmount); + _spellAura->ModStackAmount(m_spellValue->AuraStackAmount); } // Now Reduce spell duration using data received at spell hit @@ -2805,12 +2818,14 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) } } - int32 duration = m_spellAura->GetMaxDuration(); + int32 duration = _spellAura->GetMaxDuration(); // unit is immune to aura if it was diminished to 0 duration if (!positive && !unit->ApplyDiminishingToDuration(m_spellInfo, duration, m_originalCaster, diminishLevel)) { - m_spellAura->Remove(); + _spellAura->Remove(); + _spellAura = nullptr; + bool found = false; for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) if (effect && (effectMask & (1 << effect->EffectIndex) && effect->Effect != SPELL_EFFECT_APPLY_AURA)) @@ -2820,7 +2835,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) } else { - static_cast<UnitAura*>(m_spellAura)->SetDiminishGroup(diminishGroup); + _spellAura->SetDiminishGroup(diminishGroup); duration = m_originalCaster->ModSpellDuration(m_spellInfo, unit, duration, positive, effectMask); @@ -2837,7 +2852,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) duration = 0; for (SpellEffectInfo const* effect : m_spellInfo->GetEffects()) if (effect) - if (AuraEffect const* eff = m_spellAura->GetEffect(effect->EffectIndex)) + if (AuraEffect const* eff = _spellAura->GetEffect(effect->EffectIndex)) if (int32 period = eff->GetPeriod()) // period is hastened by UNIT_MOD_CAST_SPEED duration = std::max(std::max(origDuration / period, 1) * period, duration); @@ -2847,12 +2862,11 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask) } } - if (duration != m_spellAura->GetMaxDuration()) + if (duration != _spellAura->GetMaxDuration()) { - m_spellAura->SetMaxDuration(duration); - m_spellAura->SetDuration(duration); + _spellAura->SetMaxDuration(duration); + _spellAura->SetDuration(duration); } - m_spellAura->_RegisterForTargets(); } } } @@ -3692,8 +3706,6 @@ uint64 Spell::handle_delayed(uint64 t_offset) void Spell::_handle_immediate_phase() { - m_spellAura = nullptr; - // handle some immediate features of the spell here HandleThreatSpells(); diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index adaa408b65c..cb34bb00c0c 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -42,6 +42,7 @@ class AuraEffect; class BasicEvent; class Corpse; class DynamicObject; +class DynObjAura; class GameObject; class Item; class Object; @@ -53,6 +54,7 @@ class SpellImplicitTargetInfo; class SpellInfo; class SpellScript; class Unit; +class UnitAura; class WorldObject; struct SpellPowerCost; struct SummonPropertiesEntry; @@ -769,7 +771,8 @@ class TC_GAME_API Spell SpellEffectHandleMode effectHandleMode; SpellEffectInfo const* effectInfo; // used in effects handlers - Aura* m_spellAura; + UnitAura* _spellAura; + DynObjAura* _dynObjAura; // ------------------------------------------- GameObject* focusObject; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 63319f25b84..0a135d782ad 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -1023,10 +1023,10 @@ void Spell::EffectApplyAura(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!m_spellAura || !unitTarget) + if (!_spellAura || !unitTarget) return; - ASSERT(unitTarget == m_spellAura->GetOwner()); - m_spellAura->_ApplyEffectForTargets(effIndex); + + _spellAura->_ApplyEffectForTargets(effIndex); } void Spell::EffectApplyAreaAura(SpellEffIndex effIndex) @@ -1034,10 +1034,10 @@ void Spell::EffectApplyAreaAura(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT_TARGET) return; - if (!m_spellAura || !unitTarget) + if (!_spellAura || !unitTarget) return; - ASSERT (unitTarget == m_spellAura->GetOwner()); - m_spellAura->_ApplyEffectForTargets(effIndex); + + _spellAura->_ApplyEffectForTargets(effIndex); } void Spell::EffectUnlearnSpecialization(SpellEffIndex /*effIndex*/) @@ -1446,32 +1446,44 @@ void Spell::EffectPersistentAA(SpellEffIndex effIndex) if (effectHandleMode != SPELL_EFFECT_HANDLE_HIT) return; - if (!m_spellAura) + // only handle at last effect + for (uint8 i = effIndex + 1; i < MAX_SPELL_EFFECTS; ++i) + if (SpellEffectInfo const* otherEffect = m_spellInfo->GetEffect(i)) + if (otherEffect && otherEffect->IsEffect(SPELL_EFFECT_PERSISTENT_AREA_AURA)) + return; + + ASSERT(!_dynObjAura); + + Unit* caster = m_caster->GetEntry() == WORLD_TRIGGER ? m_originalCaster : m_caster; + float radius = effectInfo->CalcRadius(caster); + + // Caster not in world, might be spell triggered from aura removal + if (!caster->IsInWorld()) + return; + + DynamicObject* dynObj = new DynamicObject(false); + if (!dynObj->CreateDynamicObject(caster->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL, m_SpellVisual)) { - Unit* caster = m_caster->GetEntry() == WORLD_TRIGGER ? m_originalCaster : m_caster; - float radius = effectInfo->CalcRadius(caster); + delete dynObj; + return; + } - // Caster not in world, might be spell triggered from aura removal - if (!caster->IsInWorld()) - return; - DynamicObject* dynObj = new DynamicObject(false); - if (!dynObj->CreateDynamicObject(caster->GetMap()->GenerateLowGuid<HighGuid::DynamicObject>(), caster, m_spellInfo, *destTarget, radius, DYNAMIC_OBJECT_AREA_SPELL, m_SpellVisual)) - { - delete dynObj; - return; - } + AuraCreateInfo createInfo(m_castId, m_spellInfo, GetCastDifficulty(), MAX_EFFECT_MASK, dynObj); + createInfo + .SetCaster(caster) + .SetBaseAmount(m_spellValue->EffectBasePoints) + .SetCastItem(m_castItemGUID, m_castItemEntry, m_castItemLevel); - if (Aura* aura = Aura::TryCreate(m_spellInfo, m_castId, MAX_EFFECT_MASK, dynObj, caster, GetCastDifficulty(), &m_spellValue->EffectBasePoints[0], nullptr, ObjectGuid::Empty, ObjectGuid::Empty, m_castItemEntry, m_castItemLevel)) - { - m_spellAura = aura; - m_spellAura->_RegisterForTargets(); - } - else - return; + if (Aura* aura = Aura::TryCreate(createInfo)) + { + _dynObjAura = aura->ToDynObjAura(); + _dynObjAura->_RegisterForTargets(); } + else + return; - ASSERT(m_spellAura->GetDynobjOwner()); - m_spellAura->_ApplyEffectForTargets(effIndex); + ASSERT(_dynObjAura->GetDynobjOwner()); + _dynObjAura->_ApplyEffectForTargets(effIndex); } void Spell::EffectEnergize(SpellEffIndex /*effIndex*/) diff --git a/src/server/game/Spells/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp index 7213776011f..577273d4ff7 100644 --- a/src/server/game/Spells/SpellScript.cpp +++ b/src/server/game/Spells/SpellScript.cpp @@ -21,6 +21,7 @@ #include "ScriptMgr.h" #include "SpellAuras.h" #include "SpellMgr.h" +#include "Unit.h" #include <sstream> #include <string> @@ -648,18 +649,22 @@ void SpellScript::SetHitHeal(int32 heal) m_spell->m_healing = heal; } -Aura* SpellScript::GetHitAura() const +Aura* SpellScript::GetHitAura(bool dynObjAura /*= false*/) const { if (!IsInTargetHook()) { TC_LOG_ERROR("scripts", "Script: `%s` Spell: `%u`: function SpellScript::GetHitAura was called, but function has no effect in current hook!", m_scriptName->c_str(), m_scriptSpellId); return nullptr; } - if (!m_spell->m_spellAura) - return nullptr; - if (m_spell->m_spellAura->IsRemoved()) + + Aura* aura = m_spell->_spellAura; + if (dynObjAura) + aura = m_spell->_dynObjAura; + + if (!aura || aura->IsRemoved()) return nullptr; - return m_spell->m_spellAura; + + return aura; } void SpellScript::PreventHitAura() @@ -669,8 +674,10 @@ void SpellScript::PreventHitAura() TC_LOG_ERROR("scripts", "Script: `%s` Spell: `%u`: function SpellScript::PreventHitAura was called, but function has no effect in current hook!", m_scriptName->c_str(), m_scriptSpellId); return; } - if (m_spell->m_spellAura) - m_spell->m_spellAura->Remove(); + if (UnitAura* aura = m_spell->_spellAura) + aura->Remove(); + if (DynObjAura* aura = m_spell->_dynObjAura) + aura->Remove(); } void SpellScript::PreventHitEffect(SpellEffIndex effIndex) diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h index 5e5e5285e49..575e5721bbe 100644 --- a/src/server/game/Spells/SpellScript.h +++ b/src/server/game/Spells/SpellScript.h @@ -473,7 +473,7 @@ class TC_GAME_API SpellScript : public _SpellScript void PreventHitHeal() { SetHitHeal(0); } Spell* GetSpell() const { return m_spell; } // returns current spell hit target aura - Aura* GetHitAura() const; + Aura* GetHitAura(bool dynObjAura = false) const; // prevents applying aura on current spell hit target void PreventHitAura(); diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 95dcf69e05d..b96b66c0229 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -323,7 +323,10 @@ public: if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, target->GetMap()->GetDifficultyID())) { ObjectGuid castId = ObjectGuid::Create<HighGuid::Cast>(SPELL_CAST_SOURCE_NORMAL, target->GetMapId(), spellId, target->GetMap()->GenerateLowGuid<HighGuid::Cast>()); - Aura::TryRefreshStackOrCreate(spellInfo, castId, MAX_EFFECT_MASK, target, target, target->GetMap()->GetDifficultyID()); + AuraCreateInfo createInfo(castId, spellInfo, target->GetMap()->GetDifficultyID(), MAX_EFFECT_MASK, target); + createInfo.SetCaster(target); + + Aura::TryRefreshStackOrCreate(createInfo); } return true; diff --git a/src/server/scripts/Spells/spell_dh.cpp b/src/server/scripts/Spells/spell_dh.cpp index 406456513e8..e76618661fc 100644 --- a/src/server/scripts/Spells/spell_dh.cpp +++ b/src/server/scripts/Spells/spell_dh.cpp @@ -25,6 +25,7 @@ #include "SpellAuraEffects.h" #include "SpellMgr.h" #include "SpellScript.h" +#include "Unit.h" enum DemonHunterSpells { diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 59eea2cc385..b7bf9fbbdbd 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -813,8 +813,8 @@ class spell_pal_light_s_beacon : public SpellScriptLoader { if ((*itr)->GetId() == SPELL_PALADIN_BEACON_OF_LIGHT) { - std::list<AuraApplication*> applications; - (*itr)->GetApplicationList(applications); + std::vector<AuraApplication*> applications; + (*itr)->GetApplicationVector(applications); if (!applications.empty()) { CastSpellExtraArgs args(aurEff); |