diff options
Diffstat (limited to 'src/game/SpellAuras.cpp')
-rw-r--r-- | src/game/SpellAuras.cpp | 374 |
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; + } } } + |