diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index c22ea40171..cd2308cd92 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -2150,7 +2150,8 @@ void Creature::LoadTemplateImmunities() } } -bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const +bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, + bool requireImmunityPurgesEffectAttribute /*= false*/) const { if (!spellInfo->Effects[index].IsEffect()) return true; @@ -2158,7 +2159,7 @@ bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, if (GetCreatureTemplate()->type == CREATURE_TYPE_MECHANICAL && spellInfo->Effects[index].Effect == SPELL_EFFECT_HEAL) return true; - return Unit::IsImmunedToSpellEffect(spellInfo, index, caster); + return Unit::IsImmunedToSpellEffect(spellInfo, index, caster, requireImmunityPurgesEffectAttribute); } bool Creature::isElite() const diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 08c40b716c..27acb76984 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -129,7 +129,7 @@ class TC_GAME_API Creature : public Unit, public GridObject, public Ma bool IsClassTrainerOf(Player const* player) const; bool CanCreatureAttack(Unit const* victim, bool force = true) const; void LoadTemplateImmunities(); - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override; bool isElite() const; bool isWorldBoss() const; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index f5c6a62eb5..7f38976282 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1798,7 +1798,8 @@ void Player::SetObjectScale(float scale) SendMovementSetCollisionHeight(GetCollisionHeight(), UPDATE_COLLISION_HEIGHT_SCALE); } -bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const +bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, + bool requireImmunityPurgesEffectAttribute /*= false*/) const { // players are immune to taunt (the aura and the spell effect) if (spellInfo->Effects[index].IsAura(SPELL_AURA_MOD_TAUNT)) @@ -1806,7 +1807,7 @@ bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Wo if (spellInfo->Effects[index].IsEffect(SPELL_EFFECT_ATTACK_ME)) return true; - return Unit::IsImmunedToSpellEffect(spellInfo, index, caster); + return Unit::IsImmunedToSpellEffect(spellInfo, index, caster, requireImmunityPurgesEffectAttribute); } void Player::RegenerateAll(uint32 diff) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b0bacda8cc..eb4df8dca2 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1031,7 +1031,7 @@ class TC_GAME_API Player : public Unit, public GridObject void Update(uint32 time) override; - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override; bool IsInAreaTriggerRadius(AreaTriggerEntry const* trigger) const; diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 4f54182567..0913356517 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -145,7 +145,8 @@ void Totem::UnSummon(uint32 msTime) AddObjectToRemoveList(); } -bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const +bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, + bool requireImmunityPurgesEffectAttribute /*= false*/) const { // immune to all positive spells, except of stoneclaw totem absorb and sentry totem bind sight // totems positive spells have unit_caster target @@ -166,5 +167,5 @@ bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Wor break; } - return Creature::IsImmunedToSpellEffect(spellInfo, index, caster); + return Creature::IsImmunedToSpellEffect(spellInfo, index, caster, requireImmunityPurgesEffectAttribute); } diff --git a/src/server/game/Entities/Totem/Totem.h b/src/server/game/Entities/Totem/Totem.h index 28fffd928a..5a6fa6e0e8 100644 --- a/src/server/game/Entities/Totem/Totem.h +++ b/src/server/game/Entities/Totem/Totem.h @@ -61,7 +61,7 @@ class TC_GAME_API Totem : public Minion void UpdateAttackPowerAndDamage(bool /*ranged*/) override { } void UpdateDamagePhysical(WeaponAttackType /*attType*/) override { } - bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const override; + bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override; protected: TotemType m_type; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 0dea0cc2d1..b0400dc634 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -3465,59 +3465,59 @@ void Unit::RemoveAura(Aura* aura, AuraRemoveFlags mode) RemoveAura(aurApp, mode); } -void Unit::RemoveAppliedAuras(std::function const& check) +void Unit::RemoveAppliedAuras(std::function const& check, AuraRemoveFlags removeMode /* = AuraRemoveFlags::ByDefault*/) { for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();) { if (check(iter->second)) { - RemoveAura(iter); + RemoveAura(iter, removeMode); continue; } ++iter; } } -void Unit::RemoveOwnedAuras(std::function const& check) +void Unit::RemoveOwnedAuras(std::function const& check, AuraRemoveFlags removeMode /* = AuraRemoveFlags::ByDefault*/) { for (AuraMap::iterator iter = m_ownedAuras.begin(); iter != m_ownedAuras.end();) { if (check(iter->second)) { - RemoveOwnedAura(iter); + RemoveOwnedAura(iter, removeMode); continue; } ++iter; } } -void Unit::RemoveAppliedAuras(uint32 spellId, std::function const& check) +void Unit::RemoveAppliedAuras(uint32 spellId, std::function const& check, AuraRemoveFlags removeMode /* = AuraRemoveFlags::ByDefault*/) { for (AuraApplicationMap::iterator iter = m_appliedAuras.lower_bound(spellId); iter != m_appliedAuras.upper_bound(spellId);) { if (check(iter->second)) { - RemoveAura(iter); + RemoveAura(iter, removeMode); continue; } ++iter; } } -void Unit::RemoveOwnedAuras(uint32 spellId, std::function const& check) +void Unit::RemoveOwnedAuras(uint32 spellId, std::function const& check, AuraRemoveFlags removeMode /* = AuraRemoveFlags::ByDefault*/) { for (AuraMap::iterator iter = m_ownedAuras.lower_bound(spellId); iter != m_ownedAuras.upper_bound(spellId);) { if (check(iter->second)) { - RemoveOwnedAura(iter); + RemoveOwnedAura(iter, removeMode); continue; } ++iter; } } -void Unit::RemoveAurasByType(AuraType auraType, std::function const& check) +void Unit::RemoveAurasByType(AuraType auraType, std::function const& check, AuraRemoveFlags removeMode /* = AuraRemoveFlags::ByDefault*/) { for (AuraEffectList::iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();) { @@ -3529,7 +3529,7 @@ void Unit::RemoveAurasByType(AuraType auraType, std::function removedAuras + 1) iter = m_modAuras[auraType].begin(); } @@ -3856,42 +3856,31 @@ void Unit::RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, ui void Unit::RemoveMovementImpairingAuras(bool withRoot) { if (withRoot) - RemoveAurasWithMechanic(1 << MECHANIC_ROOT); + RemoveAurasWithMechanic(1 << MECHANIC_ROOT, AuraRemoveFlags::ByDefault, 0, true); - // Snares - for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();) - { - Aura const* aura = iter->second->GetBase(); - if (aura->GetSpellInfo()->Mechanic == MECHANIC_SNARE) - { - RemoveAura(iter); - continue; - } - - // turn off snare auras by setting amount to 0 - for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - if (((1 << i) & iter->second->GetEffectMask()) && aura->GetSpellInfo()->Effects[i].Mechanic == MECHANIC_SNARE) - aura->GetEffect(i)->ChangeAmount(0); - - ++iter; - } + RemoveAurasWithMechanic(1 << MECHANIC_ROOT, AuraRemoveFlags::ByDefault, 0, false); } -void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveFlags removemode, uint32 except) +void Unit::RemoveAurasWithMechanic(uint32 mechanicMaskToRemove, AuraRemoveFlags removeMode, uint32 exceptSpellId, bool withEffectMechanics) { - for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();) + RemoveAppliedAuras([=](AuraApplication const* aurApp) { - Aura const* aura = iter->second->GetBase(); - if (!except || aura->GetId() != except) - { - if (aura->GetSpellInfo()->GetAllEffectsMechanicMask() & mechanic_mask) - { - RemoveAura(iter, removemode); - continue; - } - } - ++iter; - } + Aura* aura = aurApp->GetBase(); + if (exceptSpellId && aura->GetId() == exceptSpellId) + return false; + + uint32 appliedMechanicMask = aura->GetSpellInfo()->GetSpellMechanicMaskByEffectMask(aurApp->GetEffectMask()); + if (!(appliedMechanicMask & mechanicMaskToRemove)) + return false; + + // spell mechanic matches required mask for removal + if ((1 << aura->GetSpellInfo()->Mechanic) & mechanicMaskToRemove || withEffectMechanics) + return true; + + // effect mechanic matches required mask for removal - don't remove, only update targets + aura->UpdateTargetMap(aura->GetCaster()); + return false; + }, removeMode); } void Unit::RemoveAurasByShapeShift() @@ -7313,14 +7302,30 @@ bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const return false; } -bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const +bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute /*= false*/) const { if (!spellInfo) return false; + auto hasImmunity = [requireImmunityPurgesEffectAttribute](SpellImmuneContainer const& container, uint32 key) + { + Trinity::IteratorPair range = Trinity::Containers::MapEqualRange(container, key); + if (!requireImmunityPurgesEffectAttribute) + return range.begin() != range.end(); + + return std::any_of(range.begin(), range.end(), [](SpellImmuneContainer::value_type const& entry) + { + if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(entry.second)) + if (immunitySourceSpell->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) + return true; + + return false; + }); + }; + // Single spell immunity. SpellImmuneContainer const& idList = m_spellImmune[IMMUNITY_ID]; - if (idList.count(spellInfo->Id) > 0) + if (hasImmunity(idList, spellInfo->Id)) return true; if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) @@ -7329,7 +7334,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste if (uint32 dispel = spellInfo->Dispel) { SpellImmuneContainer const& dispelList = m_spellImmune[IMMUNITY_DISPEL]; - if (dispelList.count(dispel) > 0) + if (hasImmunity(dispelList, dispel)) return true; } @@ -7337,7 +7342,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste if (uint32 mechanic = spellInfo->Mechanic) { SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - if (mechanicList.count(mechanic) > 0) + if (hasImmunity(mechanicList, mechanic)) return true; } @@ -7349,7 +7354,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste if (!spellInfo->Effects[i].IsEffect()) continue; - if (!IsImmunedToSpellEffect(spellInfo, i, caster)) + if (!IsImmunedToSpellEffect(spellInfo, i, caster, requireImmunityPurgesEffectAttribute)) { immuneToAllEffects = false; break; @@ -7374,7 +7379,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste // Consider the school immune if any of these conditions are not satisfied. // In case of no immuneSpellInfo, ignore that condition and check only the other conditions - if ((immuneSpellInfo && !immuneSpellInfo->IsPositive()) || !spellInfo->IsPositive() || !caster || !IsFriendlyTo(caster)) + if ((immuneSpellInfo && !immuneSpellInfo->IsPositive() && (!requireImmunityPurgesEffectAttribute || immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))) + || !spellInfo->IsPositive() || !caster || !IsFriendlyTo(caster)) if (!spellInfo->CanPierceImmuneAura(immuneSpellInfo)) schoolImmunityMask |= itr->first; } @@ -7416,7 +7422,8 @@ uint32 Unit::GetMechanicImmunityMask() const return mask; } -bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const +bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, + bool requireImmunityPurgesEffectAttribute /*= false*/) const { if (!spellInfo || !spellInfo->Effects[index].IsEffect()) return false; @@ -7424,16 +7431,33 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Worl if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES)) return false; + auto hasImmunity = [requireImmunityPurgesEffectAttribute](SpellImmuneContainer const& container, uint32 key) + { + Trinity::IteratorPair range = Trinity::Containers::MapEqualRange(container, key); + if (!requireImmunityPurgesEffectAttribute) + return range.begin() != range.end(); + + return std::any_of(range.begin(), range.end(), [](SpellImmuneContainer::value_type const& entry) + { + if (SpellInfo const* immunitySourceSpell = sSpellMgr->GetSpellInfo(entry.second)) + if (immunitySourceSpell->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) + return true; + + return false; + }); + }; + + // If m_immuneToEffect type contain this effect type, IMMUNE effect. uint32 effect = spellInfo->Effects[index].Effect; SpellImmuneContainer const& effectList = m_spellImmune[IMMUNITY_EFFECT]; - if (effectList.count(effect) > 0) + if (hasImmunity(effectList, effect)) return true; if (uint32 mechanic = spellInfo->Effects[index].Mechanic) { SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC]; - if (mechanicList.count(mechanic) > 0) + if (hasImmunity(mechanicList, mechanic)) return true; } @@ -7442,7 +7466,7 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Worl if (uint32 aura = spellInfo->Effects[index].ApplyAuraName) { SpellImmuneContainer const& list = m_spellImmune[IMMUNITY_STATE]; - if (list.count(aura) > 0) + if (hasImmunity(list, aura)) return true; if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 71650f5a05..6a79e0fe2e 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1332,14 +1332,14 @@ class TC_GAME_API Unit : public WorldObject void RemoveAura(Aura* aur, AuraRemoveFlags mode = AuraRemoveFlags::ByDefault); // Convenience methods removing auras by predicate - void RemoveAppliedAuras(std::function const& check); - void RemoveOwnedAuras(std::function const& check); + void RemoveAppliedAuras(std::function const& check, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); + void RemoveOwnedAuras(std::function const& check, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); // Optimized overloads taking advantage of map key - void RemoveAppliedAuras(uint32 spellId, std::function const& check); - void RemoveOwnedAuras(uint32 spellId, std::function const& check); + void RemoveAppliedAuras(uint32 spellId, std::function const& check, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); + void RemoveOwnedAuras(uint32 spellId, std::function const& check, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); - void RemoveAurasByType(AuraType auraType, std::function const& check); + void RemoveAurasByType(AuraType auraType, std::function const& check, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); void RemoveAurasDueToSpell(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, uint8 reqEffMask = 0, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); void RemoveAuraFromStack(uint32 spellId, ObjectGuid casterGUID = ObjectGuid::Empty, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault); @@ -1352,7 +1352,7 @@ class TC_GAME_API Unit : public WorldObject void RemoveAurasWithInterruptFlags(InterruptFlags flag, SpellInfo const* source = nullptr); void RemoveAurasWithAttribute(uint32 flags); void RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID); - void RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveFlags removemode = AuraRemoveFlags::ByDefault, uint32 except = 0); + void RemoveAurasWithMechanic(uint32 mechanicMaskToRemove, AuraRemoveFlags removeMode = AuraRemoveFlags::ByDefault, uint32 exceptSpellId = 0, bool withEffectMechanics = false); void RemoveMovementImpairingAuras(bool withRoot); void RemoveAurasByShapeShift(); @@ -1616,14 +1616,14 @@ class TC_GAME_API Unit : public WorldObject float CalculateDefaultCoefficient(SpellInfo const* spellInfo, DamageEffectType damagetype) const; void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply); - bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const; + bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const; uint32 GetSchoolImmunityMask() const; uint32 GetDamageImmunityMask() const; uint32 GetMechanicImmunityMask() const; bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const; bool IsImmunedToDamage(SpellInfo const* spellInfo) const; - virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster) const; // redefined in Creature + virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const; // redefined in Creature static bool IsDamageReducedByArmor(SpellSchoolMask damageSchoolMask, SpellInfo const* spellInfo = nullptr); static uint32 CalcArmorReducedDamage(Unit const* attacker, Unit* victim, const uint32 damage, SpellInfo const* spellInfo, WeaponAttackType attackType = MAX_ATTACK, uint8 attackerLevel = 0); diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index ff9b7f9e5a..d371d8ee6e 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -201,7 +201,7 @@ void AuraApplication::_HandleEffect(uint8 effIndex, bool apply) SetNeedClientUpdate(); } -void AuraApplication::UpdateApplyEffectMask(uint8 newEffMask) +void AuraApplication::UpdateApplyEffectMask(uint8 newEffMask, bool canHandleNewEffects) { if (_effectsToApply == newEffMask) return; @@ -216,20 +216,17 @@ void AuraApplication::UpdateApplyEffectMask(uint8 newEffMask) return; } + // update real effects only if they were applied already for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) - { - // update real effects only if they were applied already - if (!(_flags & (1 << i))) - continue; - - if (removeEffMask & (1 << i)) + if (HasEffect(i) && (removeEffMask & (1 << i))) _HandleEffect(i, false); - if (addEffMask & (1 << i)) - _HandleEffect(i, true); - } - _effectsToApply = newEffMask; + + if (canHandleNewEffects) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (addEffMask & (1 << i)) + _HandleEffect(i, true); } void AuraApplication::BuildUpdatePacket(WorldPackets::Spells::AuraInfo& auraInfo, bool remove) const @@ -351,7 +348,7 @@ Aura* Aura::TryRefreshStackOrCreate(AuraCreateInfo& createInfo, bool updateEffec // check effmask on owner application (if existing) if (updateEffectMask) if (AuraApplication* aurApp = foundAura->GetApplicationOfTarget(unit->GetGUID())) - aurApp->UpdateApplyEffectMask(effMask); + aurApp->UpdateApplyEffectMask(effMask, false); return foundAura; } else @@ -672,12 +669,17 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) { // 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 (!CanBeAppliedOn(itr->first)) + if (itr->first->IsImmunedToSpell(GetSpellInfo(), caster, true) || !CanBeAppliedOn(itr->first)) { targetsToRemove.push_back(applicationPair.second->GetTarget()); continue; } + // check target immunities (for existing targets) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (itr->first->IsImmunedToSpellEffect(GetSpellInfo(), i, caster, true)) + itr->second &= ~(1 << i); + // needs to add/remove effects from application, don't remove from map so it gets updated if (applicationPair.second->GetEffectMask() != itr->second) continue; @@ -692,13 +694,17 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) for (auto itr = targets.begin(); itr != targets.end();) { bool addUnit = true; - // check target immunities - for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) - if (itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, caster)) - itr->second &= ~(1 << effIndex); + AuraApplication* aurApp = GetApplicationOfTarget(itr->first->GetGUID()); + if (!aurApp) + { + // check target immunities (for new targets) + for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i) + if (itr->first->IsImmunedToSpellEffect(GetSpellInfo(), i, caster)) + itr->second &= ~(1 << i); - if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), caster) || !CanBeAppliedOn(itr->first)) - addUnit = false; + if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo(), caster) || !CanBeAppliedOn(itr->first)) + addUnit = false; + } if (addUnit && !itr->first->IsHighestExclusiveAura(this, true)) addUnit = false; @@ -741,14 +747,16 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply) ABORT(); } - if (AuraApplication* aurApp = GetApplicationOfTarget(itr->first->GetGUID())) + if (aurApp) { - // aura is already applied, this means we need to update effects of current application - itr->first->_UnapplyAura(aurApp, AuraRemoveFlags::ByDefault); + aurApp->UpdateApplyEffectMask(itr->second, true); // aura is already applied, this means we need to update effects of current application + itr = targets.erase(itr); + } + else + { + itr->first->_CreateAuraApplication(this, itr->second); + ++itr; } - - itr->first->_CreateAuraApplication(this, itr->second); - ++itr; } } diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index f0290a5b06..a5883be7ae 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -82,7 +82,7 @@ class TC_GAME_API AuraApplication bool IsPositive() const { return (_flags & AFLAG_POSITIVE) != 0; } bool IsSelfcast() const { return (_flags & AFLAG_CASTER) != 0; } uint8 GetEffectsToApply() const { return _effectsToApply; } - void UpdateApplyEffectMask(uint8 newEffMask); + void UpdateApplyEffectMask(uint8 newEffMask, bool canHandleNewEffects); void SetRemoveMode(AuraRemoveFlags mode) { _removeMode = mode; } EnumFlag GetRemoveMode() const { return _removeMode; } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 6207c99adb..c2042af6f6 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -960,7 +960,7 @@ void Spell::EffectApplyAura(SpellEffIndex effIndex) if (!aurApp) aurApp = unitTarget->_CreateAuraApplication(_spellAura, 1 << effIndex); else - aurApp->UpdateApplyEffectMask(aurApp->GetEffectsToApply() | 1 << effIndex); + aurApp->UpdateApplyEffectMask(aurApp->GetEffectsToApply() | 1 << effIndex, false); } void Spell::EffectUnlearnSpecialization(SpellEffIndex effIndex) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 2b3fd1a7d6..7548040fa7 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -3117,8 +3117,23 @@ void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, uint8 effIndex, bool app if (mechanicImmunity & (1 << i)) target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply); - if(apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) - target->RemoveAurasWithMechanic(mechanicImmunity, AuraRemoveFlags::ByDefault, Id); + if (HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT)) + { + if (apply) + target->RemoveAurasWithMechanic(mechanicImmunity, AuraRemoveFlags::ByDefault, Id); + else + { + target->RemoveAppliedAuras([mechanicImmunity](AuraApplication const* aurApp) + { + Aura* aura = aurApp->GetBase(); + if (aura->GetSpellInfo()->GetAllEffectsMechanicMask() & mechanicImmunity) + aura->UpdateTargetMap(aura->GetCaster()); + + // only update targets, don't remove anything + return false; + }); + } + } } if (uint32 dispelImmunity = immuneInfo->DispelImmune)