diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 7d473493a9..48da0becb8 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -26921,21 +26921,17 @@ void Player::ActivateSpec(uint8 spec) RemoveAllControlled(); // remove limited target auras at other targets - AurasBySpellIdMap& ltAurasBySpellId = GetAllLimitedCastAuras(); - for (AurasBySpellIdMap::iterator itr = ltAurasBySpellId.begin(); itr != ltAurasBySpellId.end(); itr++) + AuraList& scAuras = GetSingleCastAuras(); + for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();) { - AuraList& list = itr->second; - for (AuraList::iterator iter = list.begin(); iter != list.end();) + Aura* aura = *iter; + if (aura->GetUnitOwner() != this) { - Aura* aura = *iter; - if (aura->GetUnitOwner() != this) - { - aura->Remove(); - iter = list.begin(); - } - else - ++iter; + aura->Remove(); + iter = scAuras.begin(); } + else + ++iter; } /*RemoveAllAurasOnDeath(); diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 90a479ac50..90a22cf1a5 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -3041,44 +3041,29 @@ void Unit::_AddAura(UnitAura* aura, Unit* caster) if (aura->IsRemoved()) return; - aura->SetIsLimitedTarget(caster && (aura->GetSpellInfo()->IsSingleTarget() || aura->GetSpellInfo()->GetAuraTargetLimit())); - - if (aura->IsLimitedTarget()) + aura->SetIsSingleTarget(caster && aura->GetSpellInfo()->IsSingleTarget()); + if (aura->IsSingleTarget()) { - ASSERT((IsInWorld() && !IsDuringRemoveFromWorld()) || (aura->GetCasterGUID() == GetGUID()) || - (IsLoading() && aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE))); - /* @HACK: Player is not in world during loading auras. - * Single target auras are not saved or loaded from database - * but may be created as a result of aura links - */ + ASSERT((IsInWorld() && !IsDuringRemoveFromWorld()) || aura->GetCasterGUID() == GetGUID()); + /* @HACK: Player is not in world during loading auras. + * Single target auras are not saved or loaded from database + * but may be created as a result of aura links. + */ - // register limited target aura - caster->GetLimitedCastAuras(aura->GetId()).push_back(aura); + // register single target aura + caster->GetSingleCastAuras().push_back(aura); - Unit::AuraList& ltAuras = caster->GetLimitedCastAuras(aura->GetId()); - uint32 targetLimit = aura->GetSpellInfo()->GetAuraTargetLimit(); + std::queue aurasSharingLimit; + // remove other single target auras + for (Aura* scAura : caster->GetSingleCastAuras()) + if (scAura != aura && scAura->IsSingleTargetWith(aura)) + aurasSharingLimit.push(scAura); - // remove other limited target auras - for (Unit::AuraList::iterator itr = ltAuras.begin(); itr != ltAuras.end();) + uint32 maxOtherAuras = aura->GetSpellInfo()->MaxAffectedTargets - 1; + while (aurasSharingLimit.size() > maxOtherAuras) { - if ((*itr) != aura && (*itr)->IsLimitedTargetWith(aura) && !targetLimit) - { - (*itr)->Remove(); - itr = ltAuras.begin(); - } - else if ((*itr) != aura && (*itr)->IsLimitedTargetWith(aura) && targetLimit && ltAuras.size() > targetLimit) - { - // We have more auras in our target limit list than we are allowed to have so we remove the oldest entry - if ((*itr) == ltAuras.front()) - { - (*itr)->Remove(); - itr = ltAuras.begin(); - } - else - ++itr; - } - else - ++itr; + aurasSharingLimit.front()->Remove(); + aurasSharingLimit.pop(); } } } @@ -3337,8 +3322,8 @@ void Unit::RemoveOwnedAura(AuraMap::iterator& i, AuraRemoveFlags removeMode) m_removedAuras.push_back(aura); // Unregister single target aura - if (aura->IsLimitedTarget()) - aura->UnregisterLimitedTarget(); + if (aura->IsSingleTarget()) + aura->UnregisterSingleTarget(); aura->_Remove(removeMode); @@ -3648,9 +3633,9 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, W } else { - // limited target state must be removed before aura creation to preserve existing limited target aura - if (aura->IsLimitedTarget()) - aura->UnregisterLimitedTarget(); + // single target state must be removed before aura creation to preserve existing single target aura + if (aura->IsSingleTarget()) + aura->UnregisterSingleTarget(); AuraCreateInfo createInfo(aura->GetSpellInfo(), effMask, stealer); createInfo @@ -3659,13 +3644,13 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, W if (Aura* newAura = Aura::TryRefreshStackOrCreate(createInfo)) { - // created aura must not be limited target aura, so stealer won't loose it on recast - if (newAura->IsLimitedTarget()) + // created aura must not be single target aura,, so stealer won't loose it on recast + if (newAura->IsSingleTarget()) { - newAura->UnregisterLimitedTarget(); + newAura->UnregisterSingleTarget(); // bring back single target aura status to the old aura - aura->SetIsLimitedTarget(true); - caster->GetLimitedCastAuras(aura->GetId()).push_back(aura); + aura->SetIsSingleTarget(true); + caster->GetSingleCastAuras().push_back(aura); } // FIXME: using aura->GetMaxDuration() maybe not blizzlike but it fixes stealing of spells like Innervate newAura->SetLoadedState(aura->GetMaxDuration(), int32(dur), stealCharge ? stolenCharges : aura->GetCharges(), stolenCharges, recalculateMask, aura->GetCritChance(), aura->CanApplyResilience(), &damage[0]); @@ -3732,7 +3717,7 @@ void Unit::RemoveAurasWithAttribute(uint32 flags) } } -void Unit::RemoveNotOwnLimitedTargetAuras(bool onPhaseChange /*= false*/) +void Unit::RemoveNotOwnSingleTargetAuras(bool onPhaseChange /*= false*/) { // single target auras from other casters // Iterate m_ownedAuras - aura is marked as single target in Unit::AddAura (and pushed to m_ownedAuras). @@ -3747,7 +3732,7 @@ void Unit::RemoveNotOwnLimitedTargetAuras(bool onPhaseChange /*= false*/) { Aura const* aura = iter->second; - if (aura->GetCasterGUID() != GetGUID() && aura->IsLimitedTarget()) + if (aura->GetCasterGUID() != GetGUID() && aura->IsSingleTarget()) { if (!onPhaseChange) RemoveOwnedAura(iter); @@ -3764,22 +3749,18 @@ void Unit::RemoveNotOwnLimitedTargetAuras(bool onPhaseChange /*= false*/) ++iter; } - // limited target auras at other targets - AurasBySpellIdMap& ltAurasBySpellId = GetAllLimitedCastAuras(); - for (AurasBySpellIdMap::iterator itr = ltAurasBySpellId.begin(); itr != ltAurasBySpellId.end(); itr++) + // single target auras at other targets + AuraList& scAuras = GetSingleCastAuras(); + for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();) { - AuraList& list = itr->second; - for (AuraList::iterator iter = list.begin(); iter != list.end();) + Aura* aura = *iter; + if (aura->GetUnitOwner() != this && (!onPhaseChange || !aura->GetUnitOwner()->IsInPhase(this))) { - Aura* aura = *iter; - if (aura->GetUnitOwner() != this && (!onPhaseChange || !aura->GetUnitOwner()->IsInPhase(this))) - { - aura->Remove(); - iter = list.begin(); - } - else - ++iter; + aura->Remove(); + iter = scAuras.begin(); } + else + ++iter; } } @@ -4161,14 +4142,14 @@ void Unit::_ApplyAllAuraStatMods() (*i).second->GetBase()->HandleAllEffects(i->second, AURA_EFFECT_HANDLE_STAT, true); } -bool Unit::HasLimitedTargetAuraForSpell(uint32 spellId) const +bool Unit::HasSingleCastAuraOfSpell(uint32 spellId) const { - AurasBySpellIdMap::const_iterator itr = m_ltAuras.find(spellId); + AuraList const& scAuras = GetSingleCastAuras(); + for (Aura const* aura : scAuras) + if (aura->GetSpellInfo()->Id == spellId) + return true; - if (itr == m_ltAuras.end()) - return false; - - return !itr->second.empty(); + return false; } AuraEffect* Unit::GetAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid caster) const @@ -9547,7 +9528,7 @@ void Unit::RemoveFromWorld() RemoveCharmAuras(); RemoveBindSightAuras(); - RemoveNotOwnLimitedTargetAuras(); + RemoveNotOwnSingleTargetAuras(); RemoveAurasWithInterruptFlags(SpellAuraInterruptFlags::LeaveWorld); RemoveAllGameObjects(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 61e264ea8b..51bc6ef16e 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1347,7 +1347,7 @@ class TC_GAME_API Unit : public WorldObject void RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, WorldObject* stealer, int32 stolenCharges = 1); void RemoveAurasDueToItemSpell(uint32 spellId, ObjectGuid castItemGuid); void RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID = ObjectGuid::Empty, Aura* except = nullptr, bool negative = true, bool positive = true); - void RemoveNotOwnLimitedTargetAuras(bool onPhaseChange = false); + void RemoveNotOwnSingleTargetAuras(bool onPhaseChange = false); template void RemoveAurasWithInterruptFlags(InterruptFlags flag, SpellInfo const* source = nullptr); void RemoveAurasWithAttribute(uint32 flags); @@ -1371,9 +1371,9 @@ class TC_GAME_API Unit : public WorldObject void _ApplyAllAuraStatMods(); AuraEffectList const& GetAuraEffectsByType(AuraType type) const { return m_modAuras[type]; } - AuraList& GetLimitedCastAuras(uint32 spellId) { return m_ltAuras[spellId]; } - AurasBySpellIdMap& GetAllLimitedCastAuras() { return m_ltAuras; } - bool HasLimitedTargetAuraForSpell(uint32 spellId) const; + AuraList & GetSingleCastAuras() { return m_scAuras; } + AuraList const& GetSingleCastAuras() const { return m_scAuras; } + bool HasSingleCastAuraOfSpell(uint32 spellId) const; AuraEffect* GetAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid casterGUID = ObjectGuid::Empty) const; AuraEffect* GetAuraEffectOfRankedSpell(uint32 spellId, uint8 effIndex, ObjectGuid casterGUID = ObjectGuid::Empty) const; @@ -1853,7 +1853,7 @@ class TC_GAME_API Unit : public WorldObject uint32 m_removedAurasCount; AuraEffectList m_modAuras[TOTAL_AURAS]; - AurasBySpellIdMap m_ltAuras; // cast limited target auras + AuraList m_scAuras; // cast singlecast auras AuraApplicationList m_interruptableAuras; // auras which have interrupt mask applied on unit AuraStateAurasMap m_auraStateAuras; // Used for improve performance of aura state checks on aura apply/remove EnumFlag m_interruptMask; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 77a529c749..14e65b663c 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -539,7 +539,7 @@ enum SpellAttr5 : uint32 SPELL_ATTR5_REMOVE_ENTERING_ARENA = 0x00000004, // TITLE Remove Entering Arena DESCRIPTION Force this aura to be removed on entering arena, regardless of other properties SPELL_ATTR5_ALLOW_WHILE_STUNNED = 0x00000008, // TITLE Allow While Stunned SPELL_ATTR5_TRIGGERS_CHANNELING = 0x00000010, // TITLE Triggers Channeling - SPELL_ATTR5_LIMIT_N = 0x00000020, /*INCOMPLETE IMPL*/ // TITLE Limit N DESCRIPTION Remove previous application to another unit if applied + SPELL_ATTR5_LIMIT_N = 0x00000020, // TITLE Limit N DESCRIPTION Remove previous application to another unit if applied SPELL_ATTR5_IGNORE_AREA_EFFECT_PVP_CHECK = 0x00000040, /*NYI*/ // TITLE Ignore Area Effect PvP Check SPELL_ATTR5_NOT_ON_PLAYER = 0x00000080, /*NYI*/ // TITLE Not On Player SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC = 0x00000100, /*NYI*/ // TITLE Not On Player Controlled NPC diff --git a/src/server/game/Phasing/PhasingHandler.cpp b/src/server/game/Phasing/PhasingHandler.cpp index cc16320b39..96036f4e0c 100644 --- a/src/server/game/Phasing/PhasingHandler.cpp +++ b/src/server/game/Phasing/PhasingHandler.cpp @@ -72,7 +72,7 @@ void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, bool updateVi { AddPhase(controlled, phaseId, updateVisibility); }); - unit->RemoveNotOwnLimitedTargetAuras(true); + unit->RemoveNotOwnSingleTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -89,7 +89,7 @@ void PhasingHandler::RemovePhase(WorldObject* object, uint32 phaseId, bool updat { RemovePhase(controlled, phaseId, updateVisibility); }); - unit->RemoveNotOwnLimitedTargetAuras(true); + unit->RemoveNotOwnSingleTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -112,7 +112,7 @@ void PhasingHandler::AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, boo { AddPhaseGroup(controlled, phaseGroupId, updateVisibility); }); - unit->RemoveNotOwnLimitedTargetAuras(true); + unit->RemoveNotOwnSingleTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -135,7 +135,7 @@ void PhasingHandler::RemovePhaseGroup(WorldObject* object, uint32 phaseGroupId, { RemovePhaseGroup(controlled, phaseGroupId, updateVisibility); }); - unit->RemoveNotOwnLimitedTargetAuras(true); + unit->RemoveNotOwnSingleTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -277,7 +277,7 @@ void PhasingHandler::OnAreaChange(WorldObject* object) }); if (changed) - unit->RemoveNotOwnLimitedTargetAuras(true); + unit->RemoveNotOwnSingleTargetAuras(true); } UpdateVisibilityIfNeeded(object, true, changed); @@ -388,7 +388,7 @@ void PhasingHandler::OnConditionChange(WorldObject* object) }); if (changed) - unit->RemoveNotOwnLimitedTargetAuras(true); + unit->RemoveNotOwnSingleTargetAuras(true); } UpdateVisibilityIfNeeded(object, true, changed); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 7a931fe9fe..e58555fa94 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -448,7 +448,7 @@ m_spellInfo(createInfo._spellInfo), m_casterGuid(createInfo.CasterGUID), m_castItemGuid(createInfo.CastItemGUID), m_applyTime(GameTime::GetGameTime()), m_owner(createInfo._owner), m_timeCla(0), m_updateTargetMapInterval(0), _casterInfo(), m_procCharges(0), m_stackAmount(1), -m_isRemoved(false), m_isLimitedTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), +m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), m_procCooldown(std::chrono::steady_clock::time_point::min()) { if (m_spellInfo->ManaPerSecond) @@ -1147,7 +1147,7 @@ bool Aura::CanBeSaved() const return false; } - if (IsLimitedTarget() || GetSpellInfo()->IsSingleTarget() || GetSpellInfo()->GetAuraTargetLimit()) + if (IsSingleTarget() || GetSpellInfo()->IsSingleTarget()) return false; } @@ -1222,7 +1222,7 @@ bool Aura::CanBeSentToClient() const || HasEffectType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS) || HasEffectType(SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE); } -bool Aura::IsLimitedTargetWith(Aura const* aura) const +bool Aura::IsSingleTargetWith(Aura const* aura) const { // Same spell? if (GetSpellInfo()->IsRankOf(aura->GetSpellInfo())) @@ -1232,7 +1232,6 @@ bool Aura::IsLimitedTargetWith(Aura const* aura) const // spell with single target specific types switch (spec) { - case SPELL_SPECIFIC_JUDGEMENT: case SPELL_SPECIFIC_MAGE_POLYMORPH: if (aura->GetSpellInfo()->GetSpellSpecific() == spec) return true; @@ -1244,13 +1243,13 @@ bool Aura::IsLimitedTargetWith(Aura const* aura) const return false; } -void Aura::UnregisterLimitedTarget() +void Aura::UnregisterSingleTarget() { - ASSERT(m_isLimitedTarget); + ASSERT(m_isSingleTarget); Unit* caster = GetCaster(); ASSERT(caster); - caster->GetLimitedCastAuras(GetId()).remove(this); - SetIsLimitedTarget(false); + caster->GetSingleCastAuras().remove(this); + SetIsSingleTarget(false); } int32 Aura::CalcDispelChance(Unit const* auraTarget, bool offensive) const @@ -1695,7 +1694,7 @@ bool Aura::CanBeAppliedOn(Unit* target) if (GetOwner() != target) return false; // do not apply non-selfcast single target auras - if (GetCasterGUID() != GetOwner()->GetGUID() && (GetSpellInfo()->IsSingleTarget() || GetSpellInfo()->GetAuraTargetLimit())) + if (GetCasterGUID() != GetOwner()->GetGUID() && GetSpellInfo()->IsSingleTarget()) return false; return true; } diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 8369c06c64..f0290a5b06 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -190,10 +190,10 @@ class TC_GAME_API Aura bool IsRemoved() const { return m_isRemoved; } bool CanBeSentToClient() const; // Limited cast aura helpers - bool IsLimitedTarget() const {return m_isLimitedTarget; } - bool IsLimitedTargetWith(Aura const* aura) const; - void SetIsLimitedTarget(bool val) { m_isLimitedTarget = val; } - void UnregisterLimitedTarget(); + bool IsSingleTarget() const {return m_isSingleTarget; } + bool IsSingleTargetWith(Aura const* aura) const; + void SetIsSingleTarget(bool val) { m_isSingleTarget = val; } + void UnregisterSingleTarget(); int32 CalcDispelChance(Unit const* auraTarget, bool offensive) const; void SetLoadedState(int32 maxduration, int32 duration, int32 charges, uint8 stackamount, uint8 recalculateMask, float critChance, bool applyResilience, int32* amount); @@ -302,7 +302,7 @@ class TC_GAME_API Aura ApplicationMap m_applications; bool m_isRemoved:1; - bool m_isLimitedTarget:1; // true if it's a limited target spell and registered at caster - can change at spell steal for example + bool m_isSingleTarget:1; // true if it's a limited target spell and registered at caster - can change at spell steal for example bool m_isUsingCharges:1; ChargeDropEvent* m_dropEvent; diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index e263d5e048..b164602de5 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1061,7 +1061,6 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntry const** effe _auraState = AURA_STATE_NONE; _allowedMechanicMask = 0; - MaxAuraTargets = 0; } SpellInfo::~SpellInfo() diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index c6adfc9191..7a42d992c4 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -450,7 +450,6 @@ class TC_GAME_API SpellInfo SpellEffectInfo Effects[MAX_SPELL_EFFECTS]; uint32 ExplicitTargetMask; SpellChainNode const* ChainEntry; - uint32 MaxAuraTargets; // struct access functions SpellTargetRestrictionsEntry const* GetSpellTargetRestrictions() const; @@ -551,7 +550,6 @@ class TC_GAME_API SpellInfo bool CanDispelAura(SpellInfo const* auraSpellInfo) const; bool IsSingleTarget() const; - uint32 GetAuraTargetLimit() const { return MaxAuraTargets; }; bool IsAuraExclusiveBySpecificWith(SpellInfo const* spellInfo) const; bool IsAuraExclusiveBySpecificPerCasterWith(SpellInfo const* spellInfo) const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 1a22e789b0..eda2f54dcb 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -5807,7 +5807,8 @@ void SpellMgr::LoadSpellInfoCorrections() // Living Bomb ApplySpellFix({ 44457 }, [](SpellInfo* spellInfo) { - spellInfo->MaxAuraTargets = 3; + spellInfo->AttributesEx5 |= SPELL_ATTR5_LIMIT_N; + spellInfo->MaxAffectedTargets = 3; }); // Overhead Smash @@ -6028,6 +6029,9 @@ void SpellMgr::LoadSpellInfoCorrections() if (spellInfo->ActiveIconID == 2158) // flight spellInfo->Attributes |= SPELL_ATTR0_PASSIVE; + if (spellInfo->IsSingleTarget() && !spellInfo->MaxAffectedTargets) + spellInfo->MaxAffectedTargets = 1; + switch (spellInfo->SpellFamilyName) { case SPELLFAMILY_PALADIN: diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index d6606177bc..5f96e101a4 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -982,7 +982,7 @@ class spell_mage_nether_vortex : public AuraScript bool CheckProc(ProcEventInfo& /*eventInfo*/) { - return !GetTarget()->HasLimitedTargetAuraForSpell(SPELL_MAGE_SLOW); + return !GetTarget()->HasSingleCastAuraOfSpell(SPELL_MAGE_SLOW); } void HandleEffectProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo)