fix(Core/Immunities): ignore school immunity from friendly caster (#25408)
This commit is contained in:
@@ -74,6 +74,7 @@
|
|||||||
#include "WorldPacket.h"
|
#include "WorldPacket.h"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
float baseMoveSpeed[MAX_MOVE_TYPE] =
|
float baseMoveSpeed[MAX_MOVE_TYPE] =
|
||||||
{
|
{
|
||||||
@@ -910,6 +911,9 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit
|
|||||||
if (!spellInfo)
|
if (!spellInfo)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
|
||||||
|
return false;
|
||||||
|
|
||||||
bool immuneToAllEffects = true;
|
bool immuneToAllEffects = true;
|
||||||
bool hasCheckedEffect = false;
|
bool hasCheckedEffect = false;
|
||||||
|
|
||||||
@@ -940,8 +944,28 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit
|
|||||||
|
|
||||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||||
{
|
{
|
||||||
if (IsImmunedToSchool(spellInfo))
|
if (spellInfo->Id == 42292 || spellInfo->Id == 59752 || spellInfo->Id == 19574 || spellInfo->Id == 34471)
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
|
SpellSchoolMask schoolMask = spellInfo->GetSchoolMask();
|
||||||
|
if (schoolMask != SPELL_SCHOOL_MASK_NONE)
|
||||||
|
{
|
||||||
|
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||||
|
for (auto const& [immunitySchoolMask, immunityAuraId] : schoolList)
|
||||||
|
{
|
||||||
|
SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
|
||||||
|
if ((immunitySchoolMask & schoolMask) != schoolMask)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (IgnoresSchoolImmunityFromFriendlyCaster(caster, immunityAuraId, immuneSpellInfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@@ -9810,6 +9834,18 @@ uint32 Unit::GetDamageImmunityMask() const
|
|||||||
return mask;
|
return mask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Unit::IgnoresSchoolImmunityFromFriendlyCaster(Unit const* caster, uint32 immunityAuraId, SpellInfo const* immunitySpellInfo) const
|
||||||
|
{
|
||||||
|
if (!caster || !caster->IsFriendlyTo(this))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (immunitySpellInfo)
|
||||||
|
return !immunitySpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS);
|
||||||
|
|
||||||
|
// Creature template immunities are loaded with a placeholder spell id.
|
||||||
|
return immunityAuraId == std::numeric_limits<uint32>::max();
|
||||||
|
}
|
||||||
|
|
||||||
bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const
|
bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const
|
||||||
{
|
{
|
||||||
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
|
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
|
||||||
@@ -9847,7 +9883,7 @@ bool Unit::IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) con
|
|||||||
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
|
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
|
||||||
{
|
{
|
||||||
SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
|
SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
|
||||||
if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this))
|
if (IgnoresSchoolImmunityFromFriendlyCaster(caster, immunityAuraId, immuneAuraInfo))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (immuneAuraInfo && spellInfo->CanPierceImmuneAura(immuneAuraInfo))
|
if (immuneAuraInfo && spellInfo->CanPierceImmuneAura(immuneAuraInfo))
|
||||||
@@ -9928,7 +9964,10 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const
|
|||||||
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||||
for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr)
|
||||||
{
|
{
|
||||||
if ((itr->first & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->second)))
|
SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second);
|
||||||
|
if ((itr->first & schoolMask) == schoolMask
|
||||||
|
&& !IgnoresSchoolImmunityFromFriendlyCaster(spell->GetCaster(), itr->second, immuneSpellInfo)
|
||||||
|
&& !spellInfo->CanPierceImmuneAura(immuneSpellInfo))
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -9966,7 +10005,7 @@ bool Unit::IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spel
|
|||||||
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
|
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
|
||||||
{
|
{
|
||||||
SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
|
SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
|
||||||
if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this))
|
if (IgnoresSchoolImmunityFromFriendlyCaster(caster, immunityAuraId, immuneAuraInfo))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
schoolImmunityMask |= immunitySchoolMask;
|
schoolImmunityMask |= immunitySchoolMask;
|
||||||
@@ -10045,6 +10084,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
|||||||
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||||
{
|
{
|
||||||
SpellSchoolMask spellSchoolMask = spellInfo->GetSchoolMask();
|
SpellSchoolMask spellSchoolMask = spellInfo->GetSchoolMask();
|
||||||
|
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
|
||||||
if (spell)
|
if (spell)
|
||||||
{
|
{
|
||||||
spellSchoolMask = spell->GetSpellSchoolMask();
|
spellSchoolMask = spell->GetSpellSchoolMask();
|
||||||
@@ -10059,12 +10099,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
|||||||
if (!(itr->first & spellSchoolMask))
|
if (!(itr->first & spellSchoolMask))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (immuneSpellInfo && !immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))
|
if (IgnoresSchoolImmunityFromFriendlyCaster(spellCaster, itr->second, immuneSpellInfo))
|
||||||
{
|
continue;
|
||||||
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
|
|
||||||
if (spellCaster && spellCaster->IsFriendlyTo(this))
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
|
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
|
||||||
continue;
|
continue;
|
||||||
@@ -15109,7 +15145,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target)
|
|||||||
if (!spellInfo)
|
if (!spellInfo)
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
if (target->IsImmunedToSpell(spellInfo))
|
if (target->IsImmunedToSpell(spellInfo, effMask, this))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
|
||||||
|
|||||||
@@ -1638,6 +1638,7 @@ public:
|
|||||||
void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL);
|
void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL);
|
||||||
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr);
|
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr);
|
||||||
bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster = nullptr);
|
bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster = nullptr);
|
||||||
|
bool IgnoresSchoolImmunityFromFriendlyCaster(Unit const* caster, uint32 immunityAuraId, SpellInfo const* immunitySpellInfo) const;
|
||||||
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const;
|
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const;
|
||||||
[[nodiscard]] bool IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const;
|
[[nodiscard]] bool IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const;
|
||||||
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask schoolMask) const;
|
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask schoolMask) const;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
|
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include "Unit.h" // needed for SpellSchoolMask and mask helper
|
#include "Unit.h" // needed for SpellSchoolMask and mask helper
|
||||||
@@ -79,30 +80,24 @@ namespace
|
|||||||
|
|
||||||
bool HasOnlyDamageEffects(SpellDesc const& spell)
|
bool HasOnlyDamageEffects(SpellDesc const& spell)
|
||||||
{
|
{
|
||||||
bool hasAny = false;
|
bool hasAny = std::ranges::any_of(spell.effects, [](EffectDesc const& effect)
|
||||||
|
|
||||||
for (EffectDesc const& e : spell.effects)
|
|
||||||
{
|
{
|
||||||
if (e.effect == EFFECT_NONE)
|
return effect.effect != EFFECT_NONE;
|
||||||
continue;
|
});
|
||||||
|
|
||||||
hasAny = true;
|
return hasAny && std::ranges::all_of(spell.effects, [](EffectDesc const& effect)
|
||||||
if (!IsDamageEffect(e.effect))
|
{
|
||||||
return false;
|
return effect.effect == EFFECT_NONE || IsDamageEffect(effect.effect);
|
||||||
}
|
});
|
||||||
|
|
||||||
return hasAny;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Helper to classify spells which apply a stun aura
|
// Helper to classify spells which apply a stun aura
|
||||||
bool IsStunSpell(SpellDesc const& spell)
|
bool IsStunSpell(SpellDesc const& spell)
|
||||||
{
|
{
|
||||||
for (EffectDesc const& e : spell.effects)
|
return std::ranges::any_of(spell.effects, [](EffectDesc const& effect)
|
||||||
{
|
{
|
||||||
if (e.effect == EFFECT_APPLY_AURA && e.aura == AURA_MOD_STUN)
|
return effect.effect == EFFECT_APPLY_AURA && effect.aura == AURA_MOD_STUN;
|
||||||
return true;
|
});
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsEffectBlockedByStunImmunity(EffectDesc const& effect, bool immuneToStun)
|
bool IsEffectBlockedByStunImmunity(EffectDesc const& effect, bool immuneToStun)
|
||||||
@@ -130,6 +125,11 @@ namespace
|
|||||||
return hasAnyEffect;
|
return hasAnyEffect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsBlockedBySchoolImmunity(bool casterFriendly, bool immunityAppliesToFriendly)
|
||||||
|
{
|
||||||
|
return !casterFriendly || immunityAppliesToFriendly;
|
||||||
|
}
|
||||||
|
|
||||||
// The last parameter defaults to false to avoid updating existing tests
|
// The last parameter defaults to false to avoid updating existing tests
|
||||||
// that don't care about mechanic immunities. Bladestorm grants a
|
// that don't care about mechanic immunities. Bladestorm grants a
|
||||||
// specific mechanic immunity (including stun) that should block
|
// specific mechanic immunity (including stun) that should block
|
||||||
@@ -364,3 +364,18 @@ TEST(SpellImmunityTest, StunImmunity_DoesNotFullyBlockMixedSpell)
|
|||||||
EXPECT_TRUE(IsStunSpell(mixedSpell));
|
EXPECT_TRUE(IsStunSpell(mixedSpell));
|
||||||
EXPECT_EQ(ComputeSpellHitResult(false, false, mixedSpell, true), SPELL_MISS_NONE);
|
EXPECT_EQ(ComputeSpellHitResult(false, false, mixedSpell, true), SPELL_MISS_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(SpellImmunityTest, SchoolImmunity_TemplateStyle_AllowsFriendlySpell)
|
||||||
|
{
|
||||||
|
EXPECT_FALSE(IsBlockedBySchoolImmunity(true, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SpellImmunityTest, SchoolImmunity_ExplicitFriendlyBlockStillApplies)
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(IsBlockedBySchoolImmunity(true, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(SpellImmunityTest, SchoolImmunity_BlocksHostileSpell)
|
||||||
|
{
|
||||||
|
EXPECT_TRUE(IsBlockedBySchoolImmunity(false, false));
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user