Skip to content

Commit

Permalink
Core/Auras: Improve aura interactions with immunities on spell effect…
Browse files Browse the repository at this point in the history
… level

* Effects that target is immune to will be suppressed instead of completely removed

Closes #28096
  • Loading branch information
Shauren committed Sep 5, 2022
1 parent f3384fe commit 3306a4d
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 104 deletions.
11 changes: 6 additions & 5 deletions src/server/game/Entities/Creature/Creature.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2203,15 +2203,15 @@ void Creature::LoadTemplateImmunities()
}
}

bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const
bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute /*= false*/) const
{
if (!spellInfo)
return false;

bool immunedToAllEffects = true;
for (SpellEffectInfo const& spellEffectInfo : spellInfo->GetEffects())
{
if (spellEffectInfo.IsEffect() && !IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster))
if (spellEffectInfo.IsEffect() && !IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute))
{
immunedToAllEffects = false;
break;
Expand All @@ -2221,15 +2221,16 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* c
if (immunedToAllEffects)
return true;

return Unit::IsImmunedToSpell(spellInfo, caster);
return Unit::IsImmunedToSpell(spellInfo, caster, requireImmunityPurgesEffectAttribute);
}

bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster) const
bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster,
bool requireImmunityPurgesEffectAttribute /*= false*/) const
{
if (GetCreatureTemplate()->type == CREATURE_TYPE_MECHANICAL && spellEffectInfo.IsEffect(SPELL_EFFECT_HEAL))
return true;

return Unit::IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster);
return Unit::IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute);
}

bool Creature::isElite() const
Expand Down
4 changes: 2 additions & 2 deletions src/server/game/Entities/Creature/Creature.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool CanResetTalents(Player* player, bool pet) const;
bool CanCreatureAttack(Unit const* victim, bool force = true) const;
void LoadTemplateImmunities();
bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster) const override;
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster) const override;
bool IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override;
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override;
bool isElite() const;
bool isWorldBoss() const;

Expand Down
5 changes: 3 additions & 2 deletions src/server/game/Entities/Player/Player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1976,15 +1976,16 @@ void Player::SetObjectScale(float scale)
SetCombatReach(scale * DEFAULT_PLAYER_COMBAT_REACH);
}

bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster) const
bool Player::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster,
bool requireImmunityPurgesEffectAttribute /*= false*/) const
{
// players are immune to taunt (the aura and the spell effect)
if (spellEffectInfo.IsAura(SPELL_AURA_MOD_TAUNT))
return true;
if (spellEffectInfo.IsEffect(SPELL_EFFECT_ATTACK_ME))
return true;

return Unit::IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster);
return Unit::IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute);
}

void Player::RegenerateAll()
Expand Down
2 changes: 1 addition & 1 deletion src/server/game/Entities/Player/Player.h
Original file line number Diff line number Diff line change
Expand Up @@ -914,7 +914,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>

static bool BuildEnumData(PreparedQueryResult result, WorldPacket* data);

bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster) const override;
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override;

bool IsFalling() { return GetPositionZ() < m_lastFallZ; }
bool IsInAreaTriggerRadius(AreaTriggerEntry const* trigger) const;
Expand Down
5 changes: 3 additions & 2 deletions src/server/game/Entities/Totem/Totem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ void Totem::UnSummon(uint32 msTime)
AddObjectToRemoveList();
}

bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster) const
bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, 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
Expand All @@ -176,5 +177,5 @@ bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo c
break;
}

return Creature::IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster);
return Creature::IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute);
}
2 changes: 1 addition & 1 deletion src/server/game/Entities/Totem/Totem.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class TC_GAME_API Totem : public Minion
void UpdateAttackPowerAndDamage(bool /*ranged*/) override { }
void UpdateDamagePhysical(WeaponAttackType /*attType*/) override { }

bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster) const override;
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override;

protected:
TotemType m_type;
Expand Down
125 changes: 74 additions & 51 deletions src/server/game/Entities/Unit/Unit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3739,59 +3739,59 @@ void Unit::RemoveAura(Aura* aura, AuraRemoveMode mode)
RemoveAura(aurApp, mode);
}

void Unit::RemoveAppliedAuras(std::function<bool(AuraApplication const*)> const& check)
void Unit::RemoveAppliedAuras(std::function<bool(AuraApplication const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
{
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<bool(Aura const*)> const& check)
void Unit::RemoveOwnedAuras(std::function<bool(Aura const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
{
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<bool(AuraApplication const*)> const& check)
void Unit::RemoveAppliedAuras(uint32 spellId, std::function<bool(AuraApplication const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
{
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<bool(Aura const*)> const& check)
void Unit::RemoveOwnedAuras(uint32 spellId, std::function<bool(Aura const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
{
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<bool(AuraApplication const*)> const& check)
void Unit::RemoveAurasByType(AuraType auraType, std::function<bool(AuraApplication const*)> const& check, AuraRemoveMode removeMode /*= AURA_REMOVE_BY_DEFAULT*/)
{
for (AuraEffectList::iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();)
{
Expand All @@ -3803,7 +3803,7 @@ void Unit::RemoveAurasByType(AuraType auraType, std::function<bool(AuraApplicati
if (check(aurApp))
{
uint32 removedAuras = m_removedAurasCount;
RemoveAura(aurApp);
RemoveAura(aurApp, removeMode);
if (m_removedAurasCount > removedAuras + 1)
iter = m_modAuras[auraType].begin();
}
Expand Down Expand Up @@ -4098,42 +4098,31 @@ void Unit::RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, ui
void Unit::RemoveMovementImpairingAuras(bool withRoot)
{
if (withRoot)
RemoveAurasWithMechanic(1 << MECHANIC_ROOT);

// 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;
}
RemoveAurasWithMechanic(1 << MECHANIC_ROOT, AURA_REMOVE_BY_DEFAULT, 0, true);

// turn off snare auras by setting amount to 0
for (SpellEffectInfo const& spellEffectInfo : aura->GetSpellInfo()->GetEffects())
if (iter->second->HasEffect(spellEffectInfo.EffectIndex) && spellEffectInfo.Mechanic == MECHANIC_SNARE)
aura->GetEffect(spellEffectInfo.EffectIndex)->ChangeAmount(0);

++iter;
}
RemoveAurasWithMechanic(1 << MECHANIC_SNARE, AURA_REMOVE_BY_DEFAULT, 0, false);
}

void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode, uint32 except)
void Unit::RemoveAurasWithMechanic(uint32 mechanicMaskToRemove, AuraRemoveMode 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)

This comment has been minimized.

Copy link
@trickerer

trickerer Sep 6, 2022

Contributor

Possibly missing parenthesis

This comment has been minimized.

Copy link
@Shauren

Shauren Sep 6, 2022

Author Member

No, bitwise & has higher precedence than ||

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()
Expand Down Expand Up @@ -7702,14 +7691,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<SpellImmuneContainer::const_iterator> 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_DISPEL_AURAS_ON_IMMUNITY))
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_UNAFFECTED_BY_INVULNERABILITY))
Expand All @@ -7718,15 +7723,15 @@ 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;
}

// Spells that don't have effectMechanics.
if (uint32 mechanic = spellInfo->Mechanic)
{
SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
if (mechanicList.count(mechanic) > 0)
if (hasImmunity(mechanicList, mechanic))
return true;
}

Expand All @@ -7738,7 +7743,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste
if (!spellEffectInfo.IsEffect())
continue;

if (!IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster))
if (!IsImmunedToSpellEffect(spellInfo, spellEffectInfo, caster, requireImmunityPurgesEffectAttribute))
{
immuneToAllEffects = false;
break;
Expand All @@ -7760,7 +7765,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, WorldObject const* caste
SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second);
// 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_DISPEL_AURAS_ON_IMMUNITY)))
|| !spellInfo->IsPositive() || !caster || !IsFriendlyTo(caster))
if (!spellInfo->CanPierceImmuneAura(immuneSpellInfo))
schoolImmunityMask |= itr->first;
}
Expand Down Expand Up @@ -7802,23 +7808,40 @@ uint32 Unit::GetMechanicImmunityMask() const
return mask;
}

bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster) const
bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster,
bool requireImmunityPurgesEffectAttribute /*= false*/) const
{
if (!spellInfo || !spellEffectInfo.IsEffect())
return false;

if (spellInfo->HasAttribute(SPELL_ATTR0_UNAFFECTED_BY_INVULNERABILITY))
return false;

auto hasImmunity = [requireImmunityPurgesEffectAttribute](SpellImmuneContainer const& container, uint32 key)

This comment has been minimized.

Copy link
@lineagedr

lineagedr Sep 10, 2022

Contributor

Since this code already exists in IsImmunedToSpell wouldn't it be better to move it to a function?

{
Trinity::IteratorPair<SpellImmuneContainer::const_iterator> 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_DISPEL_AURAS_ON_IMMUNITY))
return true;

return false;
});
};

// If m_immuneToEffect type contain this effect type, IMMUNE effect.
SpellImmuneContainer const& effectList = m_spellImmune[IMMUNITY_EFFECT];
if (effectList.count(spellEffectInfo.Effect) > 0)
if (hasImmunity(effectList, spellEffectInfo.Effect))
return true;

if (uint32 mechanic = spellEffectInfo.Mechanic)
{
SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
if (mechanicList.count(mechanic) > 0)
if (hasImmunity(mechanicList, mechanic))
return true;
}

Expand All @@ -7827,7 +7850,7 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo co
if (!spellInfo->HasAttribute(SPELL_ATTR3_IGNORE_HIT_RESULT))
{
SpellImmuneContainer const& list = m_spellImmune[IMMUNITY_STATE];
if (list.count(aura) > 0)
if (hasImmunity(list, aura))
return true;
}

Expand Down
Loading

0 comments on commit 3306a4d

Please sign in to comment.