fix(Core/Immunities): ignore school immunity from friendly caster (#25408)
This commit is contained in:
parent
dbae4648ba
commit
b5c8b99ad6
3 changed files with 80 additions and 28 deletions
|
|
@ -74,6 +74,7 @@
|
|||
#include "WorldPacket.h"
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
|
||||
float baseMoveSpeed[MAX_MOVE_TYPE] =
|
||||
{
|
||||
|
|
@ -910,6 +911,9 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit
|
|||
if (!spellInfo)
|
||||
return false;
|
||||
|
||||
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
|
||||
return false;
|
||||
|
||||
bool immuneToAllEffects = true;
|
||||
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 (IsImmunedToSchool(spellInfo))
|
||||
return true;
|
||||
if (spellInfo->Id == 42292 || spellInfo->Id == 59752 || spellInfo->Id == 19574 || spellInfo->Id == 34471)
|
||||
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;
|
||||
|
|
@ -9810,6 +9834,18 @@ uint32 Unit::GetDamageImmunityMask() const
|
|||
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
|
||||
{
|
||||
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)
|
||||
{
|
||||
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;
|
||||
|
||||
if (immuneAuraInfo && spellInfo->CanPierceImmuneAura(immuneAuraInfo))
|
||||
|
|
@ -9928,7 +9964,10 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const
|
|||
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
|
||||
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;
|
||||
}
|
||||
|
|
@ -9966,7 +10005,7 @@ bool Unit::IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spel
|
|||
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
|
||||
{
|
||||
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;
|
||||
|
||||
schoolImmunityMask |= immunitySchoolMask;
|
||||
|
|
@ -10045,6 +10084,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
|||
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
|
||||
{
|
||||
SpellSchoolMask spellSchoolMask = spellInfo->GetSchoolMask();
|
||||
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
|
||||
if (spell)
|
||||
{
|
||||
spellSchoolMask = spell->GetSpellSchoolMask();
|
||||
|
|
@ -10059,12 +10099,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
|
|||
if (!(itr->first & spellSchoolMask))
|
||||
continue;
|
||||
|
||||
if (immuneSpellInfo && !immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))
|
||||
{
|
||||
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
|
||||
if (spellCaster && spellCaster->IsFriendlyTo(this))
|
||||
continue;
|
||||
}
|
||||
if (IgnoresSchoolImmunityFromFriendlyCaster(spellCaster, itr->second, immuneSpellInfo))
|
||||
continue;
|
||||
|
||||
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
|
||||
continue;
|
||||
|
|
@ -15109,7 +15145,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target)
|
|||
if (!spellInfo)
|
||||
return nullptr;
|
||||
|
||||
if (target->IsImmunedToSpell(spellInfo))
|
||||
if (target->IsImmunedToSpell(spellInfo, effMask, this))
|
||||
return nullptr;
|
||||
|
||||
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);
|
||||
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = 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(Unit const* caster, SpellInfo const* spellInfo) const;
|
||||
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask schoolMask) const;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include "Unit.h" // needed for SpellSchoolMask and mask helper
|
||||
|
|
@ -79,30 +80,24 @@ namespace
|
|||
|
||||
bool HasOnlyDamageEffects(SpellDesc const& spell)
|
||||
{
|
||||
bool hasAny = false;
|
||||
|
||||
for (EffectDesc const& e : spell.effects)
|
||||
bool hasAny = std::ranges::any_of(spell.effects, [](EffectDesc const& effect)
|
||||
{
|
||||
if (e.effect == EFFECT_NONE)
|
||||
continue;
|
||||
return effect.effect != EFFECT_NONE;
|
||||
});
|
||||
|
||||
hasAny = true;
|
||||
if (!IsDamageEffect(e.effect))
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasAny;
|
||||
return hasAny && std::ranges::all_of(spell.effects, [](EffectDesc const& effect)
|
||||
{
|
||||
return effect.effect == EFFECT_NONE || IsDamageEffect(effect.effect);
|
||||
});
|
||||
}
|
||||
|
||||
// Helper to classify spells which apply a stun aura
|
||||
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 true;
|
||||
}
|
||||
return false;
|
||||
return effect.effect == EFFECT_APPLY_AURA && effect.aura == AURA_MOD_STUN;
|
||||
});
|
||||
}
|
||||
|
||||
bool IsEffectBlockedByStunImmunity(EffectDesc const& effect, bool immuneToStun)
|
||||
|
|
@ -130,6 +125,11 @@ namespace
|
|||
return hasAnyEffect;
|
||||
}
|
||||
|
||||
bool IsBlockedBySchoolImmunity(bool casterFriendly, bool immunityAppliesToFriendly)
|
||||
{
|
||||
return !casterFriendly || immunityAppliesToFriendly;
|
||||
}
|
||||
|
||||
// The last parameter defaults to false to avoid updating existing tests
|
||||
// that don't care about mechanic immunities. Bladestorm grants a
|
||||
// specific mechanic immunity (including stun) that should block
|
||||
|
|
@ -364,3 +364,18 @@ TEST(SpellImmunityTest, StunImmunity_DoesNotFullyBlockMixedSpell)
|
|||
EXPECT_TRUE(IsStunSpell(mixedSpell));
|
||||
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));
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue