Core/Spells: expand the single target aura system to support a specific amount of targets
* limit Living Bomb targets to three per caster
This commit is contained in:
1 parent
3d93df80a6
commit
9bf40cfee4
9 files changed
+101
-66
No files matched your search
@@ -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();*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -974,6 +974,8 @@ class TC_GAME_API Unit : public WorldObject
|
||||
|
||||
typedef std::map<uint8, AuraApplication*> VisibleAuraMap;
|
||||
|
||||
typedef std::unordered_map<uint32 /*spellId*/, AuraList> 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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1034,6 +1034,7 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntry const** effe
|
||||
_auraState = AURA_STATE_NONE;
|
||||
|
||||
_allowedMechanicMask = 0;
|
||||
MaxAuraTargets = 0;
|
||||
}
|
||||
|
||||
SpellInfo::~SpellInfo()
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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];
|
||||
|
||||
Reference in new issue
Block a user