From 4a44d6a190981eb1e84358ff996ca15abeebd052 Mon Sep 17 00:00:00 2001 From: sogladev Date: Mon, 20 Jul 2026 01:53:19 +0200 Subject: [PATCH] fix(Core/Unit): correct partial school immunity for dual-school spells (#26254) --- src/server/game/Entities/Unit/Unit.cpp | 137 ++++-------------- src/server/game/Entities/Unit/Unit.h | 3 +- src/server/game/Spells/Spell.cpp | 2 +- .../game/Spells/SpellInfoCorrections.cpp | 10 ++ 4 files changed, 42 insertions(+), 110 deletions(-) diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index b8beef5f0..0b04c04aa 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -939,34 +939,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit if (hasCheckedEffect && immuneToAllEffects) return true; - if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) - { - 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 (immunityAuraId == spellInfo->Id) - continue; - - if ((immunitySchoolMask & schoolMask) != schoolMask) - continue; - - if (IgnoresSchoolImmunityFromFriendlyCaster(caster, immunityAuraId, immuneSpellInfo)) - continue; - - if (spellInfo->CanPierceImmuneAura(immuneSpellInfo)) - continue; - - return true; - } - } - } + if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) && HasSchoolImmunityForMask(spellInfo->GetSchoolMask(), caster, spellInfo)) + return true; return false; } @@ -9860,6 +9834,32 @@ bool Unit::IgnoresSchoolImmunityFromFriendlyCaster(Unit const* caster, uint32 im return immunityAuraId == std::numeric_limits::max(); } +bool Unit::HasSchoolImmunityForMask(SpellSchoolMask schoolMask, Unit const* caster, SpellInfo const* spellInfo) const +{ + if (schoolMask == SPELL_SCHOOL_MASK_NONE) + return false; + + uint32 accumulatedMask = 0; + for (auto const& [immunitySchoolMask, immunityAuraId] : m_spellImmune[IMMUNITY_SCHOOL]) + { + // Skip the spell's own immunity entry + if (spellInfo && immunityAuraId == spellInfo->Id) + continue; + + SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(immunityAuraId); + + if (IgnoresSchoolImmunityFromFriendlyCaster(caster, immunityAuraId, immuneSpellInfo)) + continue; + + if (spellInfo && immuneSpellInfo && spellInfo->CanPierceImmuneAura(immuneSpellInfo)) + continue; + + accumulatedMask |= immunitySchoolMask; + } + + return (SpellSchoolMask(accumulatedMask) & schoolMask) == schoolMask; +} + bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const { if (schoolMask == SPELL_SCHOOL_MASK_NONE) @@ -9935,68 +9935,10 @@ bool Unit::IsImmunedToSchool(SpellSchoolMask schoolMask) const }); } -bool Unit::IsImmunedToSchool(SpellInfo const* spellInfo) const -{ - if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura()) - return false; - - uint32 schoolMask = spellInfo->GetSchoolMask(); - if (schoolMask == SPELL_SCHOOL_MASK_NONE) - { - return false; - } - - if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471) - { - // Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered and spell can't pierce - 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))) - return true; - } - - return false; -} - -bool Unit::IsImmunedToSchool(Spell const* spell) const -{ - SpellInfo const* spellInfo = spell->GetSpellInfo(); - if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura()) - { - return false; - } - - uint32 schoolMask = spell->GetSpellSchoolMask(); - if (schoolMask == SPELL_SCHOOL_MASK_NONE) - { - return false; - } - - if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471) - { - // Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered and spell can't pierce - SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) - { - SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second); - if ((itr->first & schoolMask) == schoolMask - && !IgnoresSchoolImmunityFromFriendlyCaster(spell->GetCaster(), itr->second, immuneSpellInfo) - && !spellInfo->CanPierceImmuneAura(immuneSpellInfo)) - { - return true; - } - } - } - - return false; -} - bool Unit::IsImmunedToDamageOrSchool(SpellSchoolMask schoolMask) const { if (schoolMask == SPELL_SCHOOL_MASK_NONE) - { return false; - } return IsImmunedToDamage(schoolMask) || IsImmunedToSchool(schoolMask); } @@ -10102,27 +10044,8 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Unit const* caster, Spel if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES)) { - if (spellSchoolMask != SPELL_SCHOOL_MASK_NONE) - { - SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL]; - for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr) - { - if (itr->second == spellInfo->Id) - continue; - - SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second); - if (!(itr->first & spellSchoolMask)) - continue; - - if (IgnoresSchoolImmunityFromFriendlyCaster(caster, itr->second, immuneSpellInfo)) - continue; - - if (spellInfo->CanPierceImmuneAura(immuneSpellInfo)) - continue; - - return true; - } - } + if (HasSchoolImmunityForMask(spellSchoolMask, caster, spellInfo)) + return true; } return false; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 211482078..b589fdd13 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1645,14 +1645,13 @@ public: [[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const; [[nodiscard]] bool IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const; [[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask schoolMask) const; + [[nodiscard]] bool HasSchoolImmunityForMask(SpellSchoolMask schoolMask, Unit const* caster, SpellInfo const* spellInfo) const; static bool IsImmuneMaskFully(SpellSchoolMask immuneMask, SpellSchoolMask schoolMask) { return (immuneMask & schoolMask) == schoolMask; } [[nodiscard]] uint32 GetSchoolImmunityMask() const; [[nodiscard]] uint32 GetDamageImmunityMask() const; - [[nodiscard]] bool IsImmunedToSchool(SpellInfo const* spellInfo) const; - [[nodiscard]] bool IsImmunedToSchool(Spell const* spell) const; [[nodiscard]] bool IsImmunedToDamageOrSchool(SpellSchoolMask schoolMask) const; [[nodiscard]] bool IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spellInfo) const; virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 4609a074d..4ce8e3870 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2982,7 +2982,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA return SPELL_MISS_EVADE; // For delayed spells immunity may be applied between missile launch and hit - check immunity for that case - if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(m_caster, m_spellInfo)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this))) + if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(m_caster, m_spellInfo)) || unit->IsImmunedToSpell(m_spellInfo, this))) { return SPELL_MISS_IMMUNE; } diff --git a/src/server/game/Spells/SpellInfoCorrections.cpp b/src/server/game/Spells/SpellInfoCorrections.cpp index 892ebec5a..2451f16a5 100644 --- a/src/server/game/Spells/SpellInfoCorrections.cpp +++ b/src/server/game/Spells/SpellInfoCorrections.cpp @@ -5202,6 +5202,16 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->Effects[EFFECT_0].BasePoints = 1; }); + ApplySpellFix({ + 42292, // PvP Trinket + 59752, // Every Man for Himself + 19574, // Bestial Wrath + 34471 // The Beast Within + }, [](SpellInfo* spellInfo) + { + spellInfo->AttributesEx2 |= SPELL_ATTR2_NO_SCHOOL_IMMUNITIES; + }); + // 51036 Summon Venture Co. Air Patrol ApplySpellFix({ 51036 }, [](SpellInfo* spellInfo) {