aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorQAston <none@none>2010-01-24 13:08:25 +0100
committerQAston <none@none>2010-01-24 13:08:25 +0100
commitd1a40eb6888c325b3f01ac8a90f1dbac7ed56ea0 (patch)
treed73e698cb5ce789e70382a150aa57298c8c06b02
parent79accd730ecbf396b856ee42e25f2c23e51dceed (diff)
*Do not keep area aura targets in combat with aura owner
*Add some safety checks to area aura target map update. --HG-- branch : trunk
-rw-r--r--src/game/Spell.cpp1
-rw-r--r--src/game/SpellAuraEffects.cpp2
-rw-r--r--src/game/SpellAuras.cpp374
-rw-r--r--src/game/SpellAuras.h27
-rw-r--r--src/game/SpellEffects.cpp10
-rw-r--r--src/game/Unit.cpp98
-rw-r--r--src/game/Unit.h7
7 files changed, 276 insertions, 243 deletions
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index b213aa71a88..f48a808225d 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -1404,6 +1404,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
if(aurSpellInfo->SpellFamilyFlags[1] & 0x000020)
m_caster->CastSpell(unit, 41637, true, NULL, NULL, m_originalCasterGUID);
}
+ m_spellAura->_RegisterForTargets();
}
}
}
diff --git a/src/game/SpellAuraEffects.cpp b/src/game/SpellAuraEffects.cpp
index c78c9815913..754b6bca17f 100644
--- a/src/game/SpellAuraEffects.cpp
+++ b/src/game/SpellAuraEffects.cpp
@@ -386,7 +386,7 @@ void AuraEffect::GetTargetList(std::list<Unit *> & targetList) const
{
Aura::ApplicationMap const & targetMap = GetBase()->GetApplicationMap();
// remove all targets which were not added to new list - they no longer deserve area aura
- for (Aura::ApplicationMap::const_iterator appIter = targetMap.begin(); appIter != targetMap.end(); appIter++)
+ for (Aura::ApplicationMap::const_iterator appIter = targetMap.begin(); appIter != targetMap.end(); ++appIter)
{
if(appIter->second->HasEffect(GetEffIndex()))
targetList.push_back(appIter->second->GetTarget());
diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp
index 95a70a545e7..7c7ff437801 100644
--- a/src/game/SpellAuras.cpp
+++ b/src/game/SpellAuras.cpp
@@ -36,8 +36,9 @@
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
-AuraApplication::AuraApplication(Unit * target, Unit * caster, Aura * aura)
- : m_target(target), m_base(aura), m_slot(MAX_AURAS), m_flags(AFLAG_NONE), m_needClientUpdate(false), m_removeMode(AURA_REMOVE_NONE), m_canBeRemoved(false)
+AuraApplication::AuraApplication(Unit * target, Unit * caster, Aura * aura, uint8 effMask)
+ : m_target(target), m_base(aura), m_slot(MAX_AURAS), m_flags(AFLAG_NONE), m_needClientUpdate(false)
+ , m_removeMode(AURA_REMOVE_NONE), m_effectsToApply(effMask)
{
assert(GetTarget() && GetBase());
@@ -138,6 +139,7 @@ void AuraApplication::_HandleEffect(uint8 effIndex, bool apply)
AuraEffect * aurEff = GetBase()->GetEffect(effIndex);
assert(aurEff);
assert(HasEffect(effIndex) == (!apply));
+ assert((1<<effIndex) & m_effectsToApply);
sLog.outDebug("AuraApplication::_HandleEffect: %u, apply: %u: amount: %u", aurEff->GetAuraType(), apply, aurEff->GetAmount());
Unit * caster = GetBase()->GetCaster();
@@ -451,14 +453,129 @@ void Aura::_Remove(AuraRemoveMode removeMode)
}
}
-void Aura::UpdateTargetMap(Unit * caster)
+void Aura::UpdateTargetMap(Unit * caster, bool apply)
{
m_updateTargetMapInterval = UPDATE_TARGET_MAP_INTERVAL;
- for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
- if(m_effects[i] && !IsRemoved())
- UpdateTargetMapForEffect(caster, i);
+
+ // fill up to date target list
+ // target, effMask
+ std::map<Unit *, uint8> targets;
+
+ FillTargetMap(targets, caster);
+
+ UnitList targetsToRemove;
+
+ // mark all auras as ready to remove
+ for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end();++appIter)
+ {
+ std::map<Unit *, uint8>::iterator existing = targets.find(appIter->second->GetTarget());
+ // not found in current area - remove the aura
+ if (existing == targets.end())
+ targetsToRemove.push_back(appIter->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() != existing->second)
+ targetsToRemove.push_back(appIter->second->GetTarget());
+ // nothing todo - aura already applied
+ // remove from auras to register list
+ targets.erase(existing);
+ }
+ }
+
+ // register auras for units
+ for (std::map<Unit *, uint8>::iterator itr = targets.begin(); itr!= targets.end();)
+ {
+ bool addUnit = true;
+ // check target immunities
+ if (itr->first->IsImmunedToSpell(GetSpellProto()))
+ addUnit = false;
+
+ if (addUnit)
+ {
+ // persistent area aura does not hit flying targets
+ if (GetType() == DYNOBJ_AURA_TYPE)
+ {
+ if (itr->first->isInFlight())
+ addUnit = false;
+ }
+ // unit auras can not stack with each other
+ else // (GetType() == UNIT_AURA_TYPE)
+ {
+ // Allow to remove by stack when aura is going to be applied on owner
+ if (itr->first != GetOwner())
+ {
+ // check if not stacking aura already on target
+ // this one prevents unwanted usefull buff loss because of stacking and prevents overriding auras periodicaly by 2 near area aura owners
+ for (Unit::AuraApplicationMap::iterator iter = itr->first->GetAppliedAuras().begin(); iter != itr->first->GetAppliedAuras().end(); ++iter)
+ {
+ Aura const * aura = iter->second->GetBase();
+ if(!spellmgr.CanAurasStack(GetSpellProto(), aura->GetSpellProto(), aura->GetCasterGUID() == GetCasterGUID()))
+ {
+ addUnit = false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!addUnit)
+ targets.erase(itr++);
+ else
+ {
+ // owner has to be in world, or effect has to be applied to self
+ assert((!GetOwner()->IsInWorld() && GetOwner() == itr->first) || GetOwner()->IsInMap(itr->first));
+ itr->first->_CreateAuraApplication(this, itr->second);
+ ++itr;
+ }
+ }
+
+ // remove auras from units no longer needing them
+ for (UnitList::iterator itr = targetsToRemove.begin(); itr != targetsToRemove.end();++itr)
+ {
+ if (AuraApplication * aurApp = GetApplicationOfTarget((*itr)->GetGUID()))
+ (*itr)->_UnapplyAura(aurApp, AURA_REMOVE_BY_DEFAULT);
+ }
+
+ if (!apply)
+ return;
+
+ // apply aura effects for units
+ for (std::map<Unit *, uint8>::iterator itr = targets.begin(); itr!= targets.end();++itr)
+ {
+ if (AuraApplication * aurApp = GetApplicationOfTarget(itr->first->GetGUID()))
+ {
+ // owner has to be in world, or effect has to be applied to self
+ assert((!GetOwner()->IsInWorld() && GetOwner() == itr->first) || GetOwner()->IsInMap(itr->first));
+ itr->first->_ApplyAura(aurApp, itr->second);
+ }
+ }
}
+// targets have to be registered and not have effect applied yet to use this function
+void Aura::_ApplyEffectForTargets(uint8 effIndex)
+{
+ Unit * caster = GetCaster();
+ // prepare list of aura targets
+ UnitList targetList;
+ for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end(); ++appIter)
+ {
+ if ((appIter->second->GetEffectsToApply() & (1<<effIndex)) && !appIter->second->HasEffect(effIndex))
+ targetList.push_back(appIter->second->GetTarget());
+ }
+
+ // apply effect to targets
+ for (UnitList::iterator itr = targetList.begin(); itr != targetList.end(); ++itr)
+ {
+ if (GetApplicationOfTarget((*itr)->GetGUID()))
+ {
+ // owner has to be in world, or effect has to be applied to self
+ assert((!GetOwner()->IsInWorld() && GetOwner() == *itr) || GetOwner()->IsInMap(*itr));
+ (*itr)->_ApplyAuraEffect(this, effIndex);
+ }
+ }
+}
void Aura::UpdateOwner(uint32 diff, WorldObject * owner)
{
assert(owner == m_owner);
@@ -1370,130 +1487,81 @@ void UnitAura::Remove(AuraRemoveMode removeMode)
GetUnitOwner()->RemoveOwnedAura(this, removeMode);
}
-void UnitAura::UpdateTargetMapForEffect(Unit * caster, uint8 effIndex)
+void UnitAura::FillTargetMap(std::map<Unit *, uint8> & targets, Unit * caster)
{
- if (GetSpellProto()->Effect[effIndex] == SPELL_EFFECT_APPLY_AURA)
- {
- AuraApplication * aurApp = GetApplicationOfTarget(GetOwner()->GetGUID());
- if (!aurApp || !aurApp->HasEffect(effIndex))
- GetUnitOwner()->_ApplyAuraEffect(this, effIndex);
- return;
- }
-
- float radius;
- if (GetSpellProto()->Effect[effIndex] == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY)
- radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[effIndex]));
- else
- radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[effIndex]));
-
+ Player * modOwner = NULL;
if (caster)
- if(Player* modOwner = caster->GetSpellModOwner())
- modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, radius);
-
- // fill up to date target list
- UnitList targets;
+ modOwner = caster->GetSpellModOwner();
- if (!GetUnitOwner()->hasUnitState(UNIT_STAT_ISOLATED))
+ for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS ; ++effIndex)
{
- switch(GetSpellProto()->Effect[effIndex])
+ if (!HasEffect(effIndex))
+ continue;
+ UnitList targetList;
+ // non-area aura
+ if (GetSpellProto()->Effect[effIndex] == SPELL_EFFECT_APPLY_AURA)
{
- case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
- targets.push_back(GetUnitOwner());
- GetUnitOwner()->GetPartyMemberInDist(targets, radius);
- break;
- case SPELL_EFFECT_APPLY_AREA_AURA_RAID:
- targets.push_back(GetUnitOwner());
- GetUnitOwner()->GetRaidMember(targets, radius);
- break;
- case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
- {
- targets.push_back(GetUnitOwner());
- Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius);
- Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetUnitOwner(), targets, u_check);
- GetUnitOwner()->VisitNearbyObject(radius, searcher);
- break;
- }
- case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
- {
- Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius); // No GetCharmer in searcher
- Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetUnitOwner(), targets, u_check);
- GetUnitOwner()->VisitNearbyObject(radius, searcher);
- break;
- }
- case SPELL_EFFECT_APPLY_AREA_AURA_PET:
- targets.push_back(GetUnitOwner());
- case SPELL_EFFECT_APPLY_AREA_AURA_OWNER:
- {
- if(Unit *owner = GetUnitOwner()->GetCharmerOrOwner())
- if (GetUnitOwner()->IsWithinDistInMap(owner, radius))
- targets.push_back(owner);
- break;
- }
+ targetList.push_back(GetUnitOwner());
}
- }
-
- // mark all auras as ready to remove
- for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end(); appIter++)
- if (appIter->second->HasEffect(effIndex))
- appIter->second->_SetCanBeRemoved(true);
-
- for (UnitList::iterator appIter = targets.begin(); appIter != targets.end(); appIter++)
- {
- // add an aura to units new in list
- ApplicationMap::iterator itr = m_applications.find((*appIter)->GetGUID());
-
- if (itr == m_applications.end())
+ else
{
- // check target immunities
- if ((*appIter)->IsImmunedToSpell(GetSpellProto())
- || (*appIter)->hasUnitState(UNIT_STAT_ISOLATED))
- continue;
+ float radius;
+ if (GetSpellProto()->Effect[effIndex] == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY)
+ radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[effIndex]));
+ else
+ radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[effIndex]));
- // Allow to remove by stack when aura is going to be applied on owner
- if (*appIter != GetOwner())
+ if (modOwner)
+ modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, radius);
+
+ if (!GetUnitOwner()->hasUnitState(UNIT_STAT_ISOLATED))
{
- bool addUnit = true;
- // check if not stacking aura already on target
- // this one prevents unwanted usefull buff loss because of stacking and prevents overriding auras periodicaly by 2 near area aura owners
- for (Unit::AuraApplicationMap::iterator iter = (*appIter)->GetAppliedAuras().begin(); iter != (*appIter)->GetAppliedAuras().end(); ++iter)
+ switch(GetSpellProto()->Effect[effIndex])
{
- Aura const * aura = iter->second->GetBase();
- if(!spellmgr.CanAurasStack(GetSpellProto(), aura->GetSpellProto(), aura->GetCasterGUID() == GetCasterGUID()))
+ case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
+ targetList.push_back(GetUnitOwner());
+ GetUnitOwner()->GetPartyMemberInDist(targetList, radius);
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_RAID:
+ targetList.push_back(GetUnitOwner());
+ GetUnitOwner()->GetRaidMember(targetList, radius);
+ break;
+ case SPELL_EFFECT_APPLY_AREA_AURA_FRIEND:
+ {
+ targetList.push_back(GetUnitOwner());
+ Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius);
+ Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetUnitOwner(), targetList, u_check);
+ GetUnitOwner()->VisitNearbyObject(radius, searcher);
+ break;
+ }
+ case SPELL_EFFECT_APPLY_AREA_AURA_ENEMY:
+ {
+ Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(GetUnitOwner(), GetUnitOwner(), radius); // No GetCharmer in searcher
+ Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetUnitOwner(), targetList, u_check);
+ GetUnitOwner()->VisitNearbyObject(radius, searcher);
+ break;
+ }
+ case SPELL_EFFECT_APPLY_AREA_AURA_PET:
+ targetList.push_back(GetUnitOwner());
+ case SPELL_EFFECT_APPLY_AREA_AURA_OWNER:
{
- addUnit = false;
+ if(Unit *owner = GetUnitOwner()->GetCharmerOrOwner())
+ if (GetUnitOwner()->IsWithinDistInMap(owner, radius))
+ targetList.push_back(owner);
break;
}
}
- if (!addUnit)
- continue;
}
}
- if (itr == m_applications.end() || !itr->second->HasEffect(effIndex))
+ for (UnitList::iterator itr = targetList.begin(); itr!= targetList.end();++itr)
{
- if((*appIter)->IsImmunedToSpellEffect(GetSpellProto(), effIndex))
- continue;
- // add new unit to persistent area aura
- (*appIter)->_ApplyAuraEffect(this, effIndex);
-
- // start combat with targeted enemy
- if(GetSpellProto()->Effect[effIndex] == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY)
- GetUnitOwner()->CombatStart(*appIter);
+ std::map<Unit *, uint8>::iterator existing = targets.find(*itr);
+ if (existing != targets.end())
+ existing->second |= 1<<effIndex;
+ else
+ targets[*itr] = 1<<effIndex;
}
-
- itr = m_applications.find((*appIter)->GetGUID());
- if (itr != m_applications.end())
- // mark aura of unit already in list to be not removed
- itr->second->_SetCanBeRemoved(false);
- }
-
- // remove auras which are not in current area
- for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end();)
- {
- AuraApplication * aurApp = appIter->second;
- ++appIter;
- if (aurApp->_CanBeRemoved())
- aurApp->GetTarget()->_UnapplyAuraEffect(aurApp, effIndex, AURA_REMOVE_BY_DEFAULT);
}
}
@@ -1510,74 +1578,38 @@ void DynObjAura::Remove(AuraRemoveMode removeMode)
_Remove(removeMode);
}
-void DynObjAura::UpdateTargetMapForEffect(Unit * caster, uint8 effIndex)
+void DynObjAura::FillTargetMap(std::map<Unit *, uint8> & targets, Unit * caster)
{
- float radius = GetDynobjOwner()->GetRadius();
-
- // fill up to date target list
- UnitList targets;
-
Unit * dynObjOwnerCaster = GetDynobjOwner()->GetCaster();
+ float radius = GetDynobjOwner()->GetRadius();
- if(GetSpellProto()->EffectImplicitTargetB[effIndex] == TARGET_DEST_DYNOBJ_ALLY
- || GetSpellProto()->EffectImplicitTargetB[effIndex] == TARGET_UNIT_AREA_ALLY_DST)
- {
- Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetDynobjOwner(), dynObjOwnerCaster, radius);
- Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetDynobjOwner(), targets, u_check);
- GetDynobjOwner()->VisitNearbyObject(radius, searcher);
- }
- else
- {
- Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(GetDynobjOwner(), dynObjOwnerCaster, radius);
- Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetDynobjOwner(), targets, u_check);
- GetDynobjOwner()->VisitNearbyObject(radius, searcher);
- }
-
- // mark all auras as ready to remove
- for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end(); appIter++)
- if (appIter->second->HasEffect(effIndex))
- appIter->second->_SetCanBeRemoved(true);
-
- for (UnitList::iterator appIter = targets.begin(); appIter != targets.end(); appIter++)
+ for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
{
- // add an aura to units new in list
- ApplicationMap::iterator itr = m_applications.find((*appIter)->GetGUID());
-
- if (itr == m_applications.end())
+ if (!HasEffect(effIndex))
+ continue;
+ UnitList targetList;
+ if(GetSpellProto()->EffectImplicitTargetB[effIndex] == TARGET_DEST_DYNOBJ_ALLY
+ || GetSpellProto()->EffectImplicitTargetB[effIndex] == TARGET_UNIT_AREA_ALLY_DST)
{
- // persistent area aura does not hit flying targets
- if ((*appIter)->isInFlight()
- // check target immunities
- || (*appIter)->IsImmunedToSpell(GetSpellProto())
- || (*appIter)->hasUnitState(UNIT_STAT_ISOLATED))
- continue;
+ Trinity::AnyFriendlyUnitInObjectRangeCheck u_check(GetDynobjOwner(), dynObjOwnerCaster, radius);
+ Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(GetDynobjOwner(), targetList, u_check);
+ GetDynobjOwner()->VisitNearbyObject(radius, searcher);
}
-
- if (itr == m_applications.end() || !itr->second->HasEffect(effIndex))
+ else
{
- if((*appIter)->IsImmunedToSpellEffect(GetSpellProto(), effIndex))
- continue;
- // add new unit to persistent area aura
- (*appIter)->_ApplyAuraEffect(this, effIndex);
-
- // start combat with targeted enemy
- if(GetSpellProto()->EffectImplicitTargetB[effIndex] != TARGET_DEST_DYNOBJ_ALLY
- && GetSpellProto()->EffectImplicitTargetB[effIndex] != TARGET_UNIT_AREA_ALLY_DST)
- dynObjOwnerCaster->CombatStart(*appIter);
+ Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(GetDynobjOwner(), dynObjOwnerCaster, radius);
+ Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(GetDynobjOwner(), targetList, u_check);
+ GetDynobjOwner()->VisitNearbyObject(radius, searcher);
}
- itr = m_applications.find((*appIter)->GetGUID());
- if (itr != m_applications.end())
- // mark aura of unit already in list to be not removed
- itr->second->_SetCanBeRemoved(false);
- }
-
- // remove auras which are not in current area
- for (ApplicationMap::iterator appIter = m_applications.begin(); appIter != m_applications.end();)
- {
- AuraApplication * aurApp = appIter->second;
- ++appIter;
- if (aurApp->_CanBeRemoved())
- aurApp->GetTarget()->_UnapplyAuraEffect(aurApp, effIndex, AURA_REMOVE_BY_DEFAULT);
+ for (UnitList::iterator itr = targetList.begin(); itr!= targetList.end();++itr)
+ {
+ std::map<Unit *, uint8>::iterator existing = targets.find(*itr);
+ if (existing != targets.end())
+ existing->second |= 1<<effIndex;
+ else
+ targets[*itr] = 1<<effIndex;
+ }
}
}
+
diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h
index dfa5fca6432..7289b5c4d36 100644
--- a/src/game/SpellAuras.h
+++ b/src/game/SpellAuras.h
@@ -38,27 +38,26 @@ class DynamicObject;
class AuraApplication
{
- friend AuraApplication * Unit::__ApplyAura(Aura * aura);
- friend void Unit::__UnapplyAura(AuraApplicationMap::iterator &i);
- friend bool Unit::_ApplyAuraEffect(Aura * aura, uint8 effIndex);
+ friend void Unit::_ApplyAura(AuraApplication * aurApp, uint8 effMask);
+ friend void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode);
+ friend void Unit::_ApplyAuraEffect(Aura * aura, uint8 effIndex);
+ friend AuraApplication * Unit::_CreateAuraApplication(Aura * aura, uint8 effMask);
private:
Unit * const m_target;
Aura * const m_base;
uint8 m_slot; // Aura slot on unit
uint8 m_flags; // Aura info flag
+ uint8 m_effectsToApply; // Used only at spell hit to determine which effect should be applied
AuraRemoveMode m_removeMode:8; // Store info for know remove aura reason
bool m_needClientUpdate:1;
bool m_isNeedManyNegativeEffects:1;
- bool m_canBeRemoved:1; // used only in aura list update of Aura
- explicit AuraApplication(Unit * target, Unit * caster, Aura * base);
+ explicit AuraApplication(Unit * target, Unit * caster, Aura * base, uint8 effMask);
void _Remove();
private:
bool _CheckPositive(Unit * caster) const;
void _HandleEffect(uint8 effIndex, bool apply);
public:
- bool _CanBeRemoved() const {return m_canBeRemoved;}
- void _SetCanBeRemoved(bool val) {m_canBeRemoved = val;}
Unit * GetTarget() const { return m_target; }
Aura * GetBase() const { return m_base; }
@@ -68,6 +67,7 @@ class AuraApplication
uint8 GetEffectMask() const { return m_flags & (AFLAG_EFF_INDEX_0 | AFLAG_EFF_INDEX_1 | AFLAG_EFF_INDEX_2); }
bool HasEffect(uint8 effect) const { assert(effect < MAX_SPELL_EFFECTS); return m_flags & (1<<effect); }
bool IsPositive() const { return m_flags & AFLAG_POSITIVE; }
+ uint8 GetEffectsToApply() const { return m_effectsToApply; }
void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; }
AuraRemoveMode GetRemoveMode() const {return m_removeMode;}
@@ -106,11 +106,12 @@ class TRINITY_DLL_SPEC Aura
void _Remove(AuraRemoveMode removeMode);
virtual void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT) = 0;
- virtual void UpdateTargetMapForEffect(Unit * caster, uint8 effIndex) = 0;
- void UpdateTargetMap(Unit * caster);
+ virtual void FillTargetMap(std::map<Unit *, uint8> & targets, Unit * caster) = 0;
+ void UpdateTargetMap(Unit * caster, bool apply = true);
- void ApplyForTargets() {Unit * caster = GetCaster(); UpdateTargetMap(caster);}
- void ApplyEffectForTargets(uint8 effIndex) {Unit * caster = GetCaster(); UpdateTargetMapForEffect(caster, effIndex);}
+ 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);
void Update(uint32 diff, Unit * caster);
@@ -202,7 +203,7 @@ class TRINITY_DLL_SPEC UnitAura : public Aura
void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT);
- void UpdateTargetMapForEffect(Unit * caster, uint8 effIndex);
+ void FillTargetMap(std::map<Unit *, uint8> & targets, Unit * caster);
// Allow Apply Aura Handler to modify and access m_AuraDRGroup
void SetDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; }
@@ -220,6 +221,6 @@ class TRINITY_DLL_SPEC DynObjAura : public Aura
public:
void Remove(AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT);
- void UpdateTargetMapForEffect(Unit * caster, uint8 effIndex);
+ void FillTargetMap(std::map<Unit *, uint8> & targets, Unit * caster);
};
#endif
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp
index 81ff8116c00..796d007ba64 100644
--- a/src/game/SpellEffects.cpp
+++ b/src/game/SpellEffects.cpp
@@ -2601,8 +2601,7 @@ void Spell::EffectApplyAura(uint32 i)
if (!m_spellAura)
return;
assert (unitTarget == m_spellAura->GetOwner());
- if (!m_spellAura->IsRemoved())
- m_spellAura->ApplyEffectForTargets(i);
+ m_spellAura->_ApplyEffectForTargets(i);
}
void Spell::EffectApplyAreaAura(uint32 i)
@@ -2610,8 +2609,7 @@ void Spell::EffectApplyAreaAura(uint32 i)
if (!m_spellAura)
return;
assert (unitTarget == m_spellAura->GetOwner());
- if (!m_spellAura->IsRemoved())
- m_spellAura->ApplyEffectForTargets(i);
+ m_spellAura->_ApplyEffectForTargets(i);
}
void Spell::EffectUnlearnSpecialization( uint32 i )
@@ -3140,10 +3138,10 @@ void Spell::EffectPersistentAA(uint32 i)
assert(false);
return;
}
+ m_spellAura->_RegisterForTargets();
}
assert(m_spellAura->GetDynobjOwner());
- if (!m_spellAura->IsRemoved())
- m_spellAura->ApplyEffectForTargets(i);
+ m_spellAura->_ApplyEffectForTargets(i);
}
void Spell::EffectEnergize(uint32 i)
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index 65038562091..316088d51bb 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -3578,8 +3578,11 @@ void Unit::_AddAura(UnitAura * aura, Unit * caster)
}
}
-AuraApplication * Unit::__ApplyAura(Aura * aura)
+// 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, uint8 effMask)
{
+ // can't apply aura on unit which is going to be deleted - to not create a memory leak
assert(!m_cleanupDone);
// aura musn't be removed
assert(!aura->IsRemoved());
@@ -3594,9 +3597,7 @@ AuraApplication * Unit::__ApplyAura(Aura * aura)
Unit * caster = aura->GetCaster();
- // Add all pointers to lists here to prevent possible pointer invalidation on spellcast/auraapply/auraremove
-
- AuraApplication * aurApp = new AuraApplication(this, caster, aura);
+ AuraApplication * aurApp = new AuraApplication(this, caster, aura, effMask);
m_appliedAuras.insert(AuraApplicationMap::value_type(aurId, aurApp));
if(aurSpellInfo->AuraInterruptFlags)
@@ -3605,35 +3606,72 @@ AuraApplication * Unit::__ApplyAura(Aura * aura)
AddInterruptMask(aurSpellInfo->AuraInterruptFlags);
}
- AuraState aState = GetSpellAuraState(aura->GetSpellProto());
- if(aState)
+ if(AuraState aState = GetSpellAuraState(aura->GetSpellProto()))
m_auraStateAuras.insert(AuraStateAurasMap::value_type(aState, aurApp));
aura->_ApplyForTarget(this, caster, aurApp);
+ return aurApp;
+}
+
+void Unit::_ApplyAuraEffect(Aura * aura, uint8 effIndex)
+{
+ assert(aura);
+ assert(aura->HasEffect(effIndex));
+ AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID());
+ assert(aurApp);
+ if (!aurApp->GetEffectMask())
+ _ApplyAura(aurApp, 1<<effIndex);
+ else
+ aurApp->_HandleEffect(effIndex, true);
+}
+
+// handles effects of aura application
+// should be done after registering aura in lists
+void Unit::_ApplyAura(AuraApplication * aurApp, uint8 effMask)
+{
+ Aura * aura = aurApp->GetBase();
_RemoveNoStackAurasDueToAura(aura);
+ if (aurApp->GetRemoveMode())
+ return;
+
// Update target aura state flag
- if(aState)
+ if(AuraState aState = GetSpellAuraState(aura->GetSpellProto()))
ModifyAuraState(aState, true);
+ if (aurApp->GetRemoveMode())
+ return;
+
// Sitdown on apply aura req seated
- if (aurSpellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED && !IsSitState())
+ if (aura->GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED && !IsSitState())
SetStandState(UNIT_STAND_STATE_SIT);
- aura->HandleAuraSpecificMods(aurApp, caster, true);
+ Unit * caster = aura->GetCaster();
if (aurApp->GetRemoveMode())
- return NULL;
+ return;
- return aurApp;
+ aura->HandleAuraSpecificMods(aurApp, caster, true);
+
+ // apply effects of the aura
+ for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS; ++i)
+ {
+ if (effMask & 1<<i && (!aurApp->GetRemoveMode()))
+ aurApp->_HandleEffect(i, true);
+ }
}
-void Unit::__UnapplyAura(AuraApplicationMap::iterator &i)
+// removes aura application from lists and unapplies effects
+void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode)
{
AuraApplication * aurApp = i->second;
+ assert(aurApp);
+ assert(!aurApp->GetRemoveMode());
assert(aurApp->GetTarget() == this);
+ aurApp->SetRemoveMode(removeMode);
Aura * aura = aurApp->GetBase();
+ sLog.outDebug("Aura %u now is remove mode %d", aura->GetId(), removeMode);
// dead loop is killing the server probably
assert(m_removedAurasCount < 0xFFFFFFFF);
@@ -3702,42 +3740,6 @@ void Unit::__UnapplyAura(AuraApplicationMap::iterator &i)
i = m_appliedAuras.begin();
}
-bool Unit::_ApplyAuraEffect(Aura * aura, uint8 effIndex)
-{
- // check if aura has requested effect - should always do
- assert(aura);
- assert(aura->HasEffect(effIndex));
- AuraApplication * aurApp = aura->GetApplicationOfTarget(GetGUID());
- if (!aurApp)
- {
- // real aura apply
- aurApp = __ApplyAura(aura);
- if (!aurApp)
- return false;
- }
- // add effect to unit
- aurApp->_HandleEffect(effIndex, true);
- return true;
-}
-
-// Not implemented - afaik there should be no way to remove effects separately
-void Unit::_UnapplyAuraEffect(AuraApplication * aurApp, uint8 effIndex, AuraRemoveMode removeMode)
-{
- assert(aurApp);
- assert(aurApp->HasEffect(effIndex));
- _UnapplyAura(aurApp, removeMode);
-}
-
-void Unit::_UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode)
-{
- AuraApplication * aurApp = i->second;
- assert(aurApp);
- assert(!aurApp->GetRemoveMode());
- aurApp->SetRemoveMode(removeMode);
- sLog.outDebug("Aura %u now is remove mode %d", aurApp->GetBase()->GetId(), removeMode);
- __UnapplyAura(i);
-}
-
void Unit::_UnapplyAura(AuraApplication * aurApp, AuraRemoveMode removeMode)
{
// aura can be removed from unit only if it's applied on it, shouldn't happen
diff --git a/src/game/Unit.h b/src/game/Unit.h
index 5ff61195cc4..07281fd90e8 100644
--- a/src/game/Unit.h
+++ b/src/game/Unit.h
@@ -1536,10 +1536,9 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
// aura apply/remove helpers - you should better not use these
void _AddAura(UnitAura * aura, Unit * caster);
- AuraApplication * __ApplyAura(Aura * aura);
- void __UnapplyAura(AuraApplicationMap::iterator &i);
- bool _ApplyAuraEffect(Aura * aura, uint8 effIndex);
- void _UnapplyAuraEffect(AuraApplication * aurApp, uint8 effIndex, AuraRemoveMode removeMode);
+ AuraApplication * _CreateAuraApplication(Aura * aura, uint8 effMask);
+ void _ApplyAuraEffect(Aura * aura, uint8 effIndex);
+ void _ApplyAura(AuraApplication * aurApp, uint8 effMask);
void _UnapplyAura(AuraApplicationMap::iterator &i, AuraRemoveMode removeMode);
void _UnapplyAura(AuraApplication * aurApp, AuraRemoveMode removeMode);
void _RemoveNoStackAuraApplicationsDueToAura(Aura * aura);