From 9bf40cfee473bf26f5be965f2d4ea2ca744a3bf9 Mon Sep 17 00:00:00 2001 From: Ovahlord Date: Sun, 12 May 2019 20:03:17 +0200 Subject: [PATCH] Core/Spells: expand the single target aura system to support a specific amount of targets * limit Living Bomb targets to three per caster --- src/server/game/Entities/Player/Player.cpp | 23 +++--- src/server/game/Entities/Unit/Unit.cpp | 83 +++++++++++++-------- src/server/game/Entities/Unit/Unit.h | 10 ++- src/server/game/Phasing/PhasingHandler.cpp | 12 +-- src/server/game/Spells/Auras/SpellAuras.cpp | 18 ++--- src/server/game/Spells/Auras/SpellAuras.h | 12 +-- src/server/game/Spells/SpellInfo.cpp | 1 + src/server/game/Spells/SpellInfo.h | 2 + src/server/game/Spells/SpellMgr.cpp | 6 ++ 9 files changed, 101 insertions(+), 66 deletions(-) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index a4b06f68800..cfcdcc04e44 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -27234,19 +27234,24 @@ void Player::ActivateSpec(uint8 spec) ExitVehicle(); RemoveAllControlled(); - // remove single target auras at other targets - AuraList& scAuras = GetSingleCastAuras(); - for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();) + // remove limited target auras at other targets + AurasBySpellIdMap& ltAurasBySpellId = GetAllLimitedCastAuras(); + for (AurasBySpellIdMap::iterator itr = ltAurasBySpellId.begin(); itr != ltAurasBySpellId.end(); itr++) { - Aura* aura = *iter; - if (aura->GetUnitOwner() != this) + AuraList list = itr->second; + for (AuraList::iterator iter = list.begin(); iter != list.end();) { - aura->Remove(); - iter = scAuras.begin(); + Aura* aura = *iter; + if (aura->GetUnitOwner() != this) + { + aura->Remove(); + iter = list.begin(); + } + else + ++iter; } - else - ++iter; } + /*RemoveAllAurasOnDeath(); if (GetPet()) GetPet()->RemoveAllAurasOnDeath();*/ diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 35130f2ac1f..55813e65f72 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -3390,8 +3390,9 @@ void Unit::_AddAura(UnitAura* aura, Unit* caster) if (aura->IsRemoved()) return; - aura->SetIsSingleTarget(caster && (aura->GetSpellInfo()->IsSingleTarget() || aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE))); - if (aura->IsSingleTarget()) + aura->SetIsLimitedTarget(caster && (aura->GetSpellInfo()->IsSingleTarget() || aura->GetSpellInfo()->GetAuraTargetLimit() || aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE))); + + if (aura->IsLimitedTarget()) { ASSERT((IsInWorld() && !IsDuringRemoveFromWorld()) || (aura->GetCasterGUID() == GetGUID()) || (IsLoading() && aura->HasEffectType(SPELL_AURA_CONTROL_VEHICLE))); @@ -3400,17 +3401,31 @@ void Unit::_AddAura(UnitAura* aura, Unit* caster) * but may be created as a result of aura links (player mounts with passengers) */ - // register single target aura - caster->GetSingleCastAuras().push_back(aura); - // remove other single target auras - Unit::AuraList& scAuras = caster->GetSingleCastAuras(); - for (Unit::AuraList::iterator itr = scAuras.begin(); itr != scAuras.end();) + // register limited target aura + caster->GetLimitedCastAuras(aura->GetSpellInfo()->Id).push_back(aura); + + Unit::AuraList& ltAuras = caster->GetLimitedCastAuras(aura->GetSpellInfo()->Id); + bool isSingleTarget = aura->GetSpellInfo()->IsSingleTarget(); + uint32 targetLimit = aura->GetSpellInfo()->GetAuraTargetLimit(); + + // remove other limited target auras + for (Unit::AuraList::iterator itr = ltAuras.begin(); itr != ltAuras.end();) { - if ((*itr) != aura && - (*itr)->IsSingleTargetWith(aura)) + if ((*itr) != aura && (*itr)->IsLimitedTargetWith(aura) && isSingleTarget) { (*itr)->Remove(); - itr = scAuras.begin(); + 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; @@ -3667,8 +3682,8 @@ void Unit::RemoveOwnedAura(AuraMap::iterator &i, AuraRemoveMode removeMode) m_removedAuras.push_back(aura); // Unregister single target aura - if (aura->IsSingleTarget()) - aura->UnregisterSingleTarget(); + if (aura->IsLimitedTarget()) + aura->UnregisterLimitedTarget(); aura->_Remove(removeMode); @@ -3980,19 +3995,19 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, U } else { - // single target state must be removed before aura creation to preserve existing single target aura - if (aura->IsSingleTarget()) - aura->UnregisterSingleTarget(); + // limited target state must be removed before aura creation to preserve existing limited target aura + if (aura->IsLimitedTarget()) + aura->UnregisterLimitedTarget(); if (Aura* newAura = Aura::TryRefreshStackOrCreate(aura->GetSpellInfo(), effMask, stealer, nullptr, &baseDamage[0], nullptr, aura->GetCasterGUID())) { - // created aura must not be single target aura,, so stealer won't loose it on recast - if (newAura->IsSingleTarget()) + // created aura must not be limited target aura, so stealer won't loose it on recast + if (newAura->IsLimitedTarget()) { - newAura->UnregisterSingleTarget(); + newAura->UnregisterLimitedTarget(); // bring back single target aura status to the old aura - aura->SetIsSingleTarget(true); - caster->GetSingleCastAuras().push_back(aura); + aura->SetIsLimitedTarget(true); + caster->GetLimitedCastAuras(aura->GetSpellInfo()->Id).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 ? 1 : aura->GetCharges(), 1, recalculateMask, &damage[0]); @@ -4058,7 +4073,7 @@ void Unit::RemoveAurasWithAttribute(uint32 flags) } } -void Unit::RemoveNotOwnSingleTargetAuras(bool onPhaseChange /*= false*/) +void Unit::RemoveNotOwnLimitedTargetAuras(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). @@ -4073,7 +4088,7 @@ void Unit::RemoveNotOwnSingleTargetAuras(bool onPhaseChange /*= false*/) { Aura const* aura = iter->second; - if (aura->GetCasterGUID() != GetGUID() && aura->IsSingleTarget()) + if (aura->GetCasterGUID() != GetGUID() && aura->IsLimitedTarget()) { if (!onPhaseChange) RemoveOwnedAura(iter); @@ -4090,18 +4105,22 @@ void Unit::RemoveNotOwnSingleTargetAuras(bool onPhaseChange /*= false*/) ++iter; } - // single target auras at other targets - AuraList& scAuras = GetSingleCastAuras(); - for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();) + // limited target auras at other targets + AurasBySpellIdMap& ltAurasBySpellId = GetAllLimitedCastAuras(); + for (AurasBySpellIdMap::iterator itr = ltAurasBySpellId.begin(); itr != ltAurasBySpellId.end(); ++itr) { - Aura* aura = *iter; - if (aura->GetUnitOwner() != this && (!onPhaseChange || !aura->GetUnitOwner()->IsInPhase(this))) + AuraList& list = itr->second; + for (AuraList::iterator iter = list.begin(); iter != list.end();) { - aura->Remove(); - iter = scAuras.begin(); + Aura* aura = *iter; + if (aura->GetUnitOwner() != this && (!onPhaseChange || !aura->GetUnitOwner()->IsInPhase(this))) + { + aura->Remove(); + iter = list.begin(); + } + else + ++iter; } - else - ++iter; } } @@ -10275,7 +10294,7 @@ void Unit::RemoveFromWorld() RemoveCharmAuras(); RemoveBindSightAuras(); - RemoveNotOwnSingleTargetAuras(); + RemoveNotOwnLimitedTargetAuras(); RemoveAllGameObjects(); RemoveAllDynObjects(); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index f5cd853ab15..70329103fb5 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -974,6 +974,8 @@ class TC_GAME_API Unit : public WorldObject typedef std::map VisibleAuraMap; + typedef std::unordered_map AurasBySpellIdMap; + virtual ~Unit(); UnitAI* GetAI() { return i_AI; } @@ -1465,7 +1467,7 @@ class TC_GAME_API Unit : public WorldObject void RemoveAurasDueToSpellBySteal(uint32 spellId, ObjectGuid casterGUID, Unit* stealer); void RemoveAurasDueToItemSpell(uint32 spellId, ObjectGuid castItemGuid); void RemoveAurasByType(AuraType auraType, ObjectGuid casterGUID = ObjectGuid::Empty, Aura* except = nullptr, bool negative = true, bool positive = true); - void RemoveNotOwnSingleTargetAuras(bool onPhaseChange = false); + void RemoveNotOwnLimitedTargetAuras(bool onPhaseChange = false); void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = 0); void RemoveAurasWithAttribute(uint32 flags); void RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID); @@ -1487,8 +1489,8 @@ class TC_GAME_API Unit : public WorldObject void _ApplyAllAuraStatMods(); AuraEffectList const& GetAuraEffectsByType(AuraType type) const { return m_modAuras[type]; } - AuraList & GetSingleCastAuras() { return m_scAuras; } - AuraList const& GetSingleCastAuras() const { return m_scAuras; } + AuraList& GetLimitedCastAuras(uint32 spellId) { return m_ltAuras[spellId]; } + AurasBySpellIdMap& GetAllLimitedCastAuras() { return m_ltAuras; } AuraEffect* GetAuraEffect(uint32 spellId, uint8 effIndex, ObjectGuid casterGUID = ObjectGuid::Empty) const; AuraEffect* GetAuraEffectOfRankedSpell(uint32 spellId, uint8 effIndex, ObjectGuid casterGUID = ObjectGuid::Empty) const; @@ -1949,7 +1951,7 @@ class TC_GAME_API Unit : public WorldObject uint32 m_removedAurasCount; AuraEffectList m_modAuras[TOTAL_AURAS]; - AuraList m_scAuras; // cast singlecast auras + AurasBySpellIdMap m_ltAuras; // cast limited target 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 uint32 m_interruptMask; diff --git a/src/server/game/Phasing/PhasingHandler.cpp b/src/server/game/Phasing/PhasingHandler.cpp index 933922f3eb3..b984f705e0c 100644 --- a/src/server/game/Phasing/PhasingHandler.cpp +++ b/src/server/game/Phasing/PhasingHandler.cpp @@ -70,7 +70,7 @@ void PhasingHandler::AddPhase(WorldObject* object, uint32 phaseId, bool updateVi { AddPhase(controlled, phaseId, updateVisibility); }); - unit->RemoveNotOwnSingleTargetAuras(true); + unit->RemoveNotOwnLimitedTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -87,7 +87,7 @@ void PhasingHandler::RemovePhase(WorldObject* object, uint32 phaseId, bool updat { RemovePhase(controlled, phaseId, updateVisibility); }); - unit->RemoveNotOwnSingleTargetAuras(true); + unit->RemoveNotOwnLimitedTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -110,7 +110,7 @@ void PhasingHandler::AddPhaseGroup(WorldObject* object, uint32 phaseGroupId, boo { AddPhaseGroup(controlled, phaseGroupId, updateVisibility); }); - unit->RemoveNotOwnSingleTargetAuras(true); + unit->RemoveNotOwnLimitedTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -133,7 +133,7 @@ void PhasingHandler::RemovePhaseGroup(WorldObject* object, uint32 phaseGroupId, { RemovePhaseGroup(controlled, phaseGroupId, updateVisibility); }); - unit->RemoveNotOwnSingleTargetAuras(true); + unit->RemoveNotOwnLimitedTargetAuras(true); } UpdateVisibilityIfNeeded(object, updateVisibility, changed); @@ -275,7 +275,7 @@ void PhasingHandler::OnAreaChange(WorldObject* object) }); if (changed) - unit->RemoveNotOwnSingleTargetAuras(true); + unit->RemoveNotOwnLimitedTargetAuras(true); } UpdateVisibilityIfNeeded(object, true, changed); @@ -386,7 +386,7 @@ void PhasingHandler::OnConditionChange(WorldObject* object) }); if (changed) - unit->RemoveNotOwnSingleTargetAuras(true); + unit->RemoveNotOwnLimitedTargetAuras(true); } UpdateVisibilityIfNeeded(object, true, changed); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 31d47d48e88..cbbbb5fba15 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -325,7 +325,7 @@ Aura* Aura::Create(SpellInfo const* spellproto, uint8 effMask, WorldObject* owne if (owner->isType(TYPEMASK_UNIT)) if (!owner->IsInWorld() || ((Unit*)owner)->IsDuringRemoveFromWorld()) // owner not in world so don't allow to own not self cast single target auras - if (casterGUID != owner->GetGUID() && spellproto->IsSingleTarget()) + if (casterGUID != owner->GetGUID() && (spellproto->IsSingleTarget() || spellproto->GetAuraTargetLimit())) return nullptr; Aura* aura = nullptr; @@ -354,7 +354,7 @@ m_spellInfo(spellproto), m_casterGuid(casterGUID ? casterGUID : caster->GetGUID( m_castItemGuid(castItem ? castItem->GetGUID() : ObjectGuid::Empty), m_applyTime(time(nullptr)), m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), -m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), +m_isRemoved(false), m_isLimitedTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), m_procCooldown(std::chrono::steady_clock::time_point::min()) { if (m_spellInfo->ManaPerSecond) @@ -983,7 +983,7 @@ bool Aura::CanBeSaved() const return false; } - if (IsSingleTarget() || GetSpellInfo()->IsSingleTarget()) + if (IsLimitedTarget() || GetSpellInfo()->IsSingleTarget() || GetSpellInfo()->GetAuraTargetLimit()) return false; } @@ -1054,7 +1054,7 @@ bool Aura::CanBeSentToClient() const || HasEffectType(SPELL_AURA_CAST_WHILE_WALKING) || HasEffectType(SPELL_AURA_OVERRIDE_ACTIONBAR_SPELLS); } -bool Aura::IsSingleTargetWith(Aura const* aura) const +bool Aura::IsLimitedTargetWith(Aura const* aura) const { // Same spell? if (GetSpellInfo()->IsRankOf(aura->GetSpellInfo())) @@ -1079,13 +1079,13 @@ bool Aura::IsSingleTargetWith(Aura const* aura) const return false; } -void Aura::UnregisterSingleTarget() +void Aura::UnregisterLimitedTarget() { - ASSERT(m_isSingleTarget); + ASSERT(m_isLimitedTarget); Unit* caster = GetCaster(); ASSERT(caster); - caster->GetSingleCastAuras().remove(this); - SetIsSingleTarget(false); + caster->GetLimitedCastAuras(GetSpellInfo()->Id).remove(this); + SetIsLimitedTarget(false); } int32 Aura::CalcDispelChance(Unit const* auraTarget, bool offensive) const @@ -1525,7 +1525,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()) + if (GetCasterGUID() != GetOwner()->GetGUID() && (GetSpellInfo()->IsSingleTarget() || GetSpellInfo()->GetAuraTargetLimit())) return false; return true; } diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index b9bca5461c4..da3d2cdf99c 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -166,11 +166,11 @@ class TC_GAME_API Aura bool CanBeSaved() const; bool IsRemoved() const { return m_isRemoved; } bool CanBeSentToClient() const; - // Single cast aura helpers - bool IsSingleTarget() const {return m_isSingleTarget; } - bool IsSingleTargetWith(Aura const* aura) const; - void SetIsSingleTarget(bool val) { m_isSingleTarget = val; } - void UnregisterSingleTarget(); + // 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(); int32 CalcDispelChance(Unit const* auraTarget, bool offensive) const; void SetLoadedState(int32 maxduration, int32 duration, int32 charges, uint8 stackamount, uint8 recalculateMask, int32 * amount); @@ -266,7 +266,7 @@ class TC_GAME_API Aura ApplicationMap m_applications; bool m_isRemoved:1; - bool m_isSingleTarget:1; // true if it's a single target spell and registered at caster - can change at spell steal for example + 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_isUsingCharges:1; ChargeDropEvent* m_dropEvent; diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 572c6d6f157..a7c7b465ca1 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1034,6 +1034,7 @@ 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 3303bd2a6a4..417278c1ded 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -429,6 +429,7 @@ class TC_GAME_API SpellInfo SpellEffectInfo Effects[MAX_SPELL_EFFECTS]; uint32 ExplicitTargetMask; SpellChainNode const* ChainEntry; + uint32 MaxAuraTargets; // struct access functions SpellTargetRestrictionsEntry const* GetSpellTargetRestrictions() const; @@ -517,6 +518,7 @@ 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 401e6d464b2..13b7d3f3fae 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -5257,6 +5257,12 @@ void SpellMgr::LoadSpellInfoCorrections() // END OF BLACKWING DESCENT SPELLS + // Living Bomb + ApplySpellFix({ 44457 }, [](SpellInfo* spellInfo) + { + spellInfo->MaxAuraTargets = 3; + }); + for (uint32 i = 0; i < GetSpellInfoStoreSize(); ++i) { SpellInfo* spellInfo = mSpellInfoMap[i];