aboutsummaryrefslogtreecommitdiff
path: root/src/game/SpellAuras.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/SpellAuras.cpp')
-rw-r--r--src/game/SpellAuras.cpp374
1 files changed, 203 insertions, 171 deletions
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;
+ }
}
}
+