fix(Core/Spell): spell immunity system and new separate immunities table (#24956)

Co-authored-by: ariel- <ariel-@users.noreply.github.com>
Co-authored-by: Keader <keader.android@gmail.com>
Co-authored-by: Shauren <shauren.trinity@gmail.com>
This commit is contained in:
sogladev 2026-03-22 23:36:35 +01:00 committed by GitHub
parent 0e0ff86e3e
commit a90570a2de
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 1655 additions and 914 deletions

View file

@ -0,0 +1,23 @@
--
CREATE TABLE `creature_immunities` (
`ID` int NOT NULL,
`SchoolMask` tinyint NOT NULL DEFAULT '0',
`DispelTypeMask` smallint NOT NULL DEFAULT '0',
`MechanicsMask` bigint NOT NULL DEFAULT '0',
`Effects` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`Auras` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
`ImmuneAoE` tinyint(1) NOT NULL DEFAULT '0',
`ImmuneChain` tinyint(1) NOT NULL DEFAULT '0',
`Comment` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `creature_template` ADD `CreatureImmunitiesId` int NOT NULL DEFAULT '0' AFTER `RegenHealth`;
UPDATE `creature_template` SET `CreatureImmunitiesId`=COALESCE((SELECT ci.`ID` FROM `creature_immunities` ci WHERE ci.`SchoolMask`=`spell_school_immune_mask` AND ci.`MechanicsMask`=`mechanic_immune_mask`*2),0);
ALTER TABLE `creature_template`
DROP `spell_school_immune_mask`,
DROP `mechanic_immune_mask`;
ALTER TABLE `creature_template` DROP COLUMN `scale`;

View file

@ -0,0 +1,37 @@
--
DELETE FROM `creature_immunities` WHERE `ID` IN (95,96,315,477,679,878,1537,1557,1614,1615,1630,1632,1664,1676,1682,1693,1694,1695,1733);
INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES
(95, 0, 0, 32, '', '', 0, 0, 'Immune to Fear'),
(96, 0, 0, 1234599598, '', '5,7,12,26,33', 0, 0, 'Immune to CC (Free Friend, Uncontrollable Frenzy, Warlord\'s Presence)'),
(315, 127, 0, 0, '', '', 0, 0, 'Immune to damage'),
(477, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
(679, 0, 0, 1234599078, '', '5,7,12,26,33', 0, 0, 'Immune to CC'),
(878, 0, 0, 4096, '', '12', 0, 0, 'Immune to Stun'),
(1537, 0, 0, 32, '', '7', 0, 0, 'Immune to Fear'),
(1557, 0, 0, 1234599078, '98,124,144,145', '', 0, 0, 'Immune CC+Stun+Move'),
(1614, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
(1615, 0, 0, 1301707942, '68', '5,7,12,26,27,33,60', 0, 0, 'Immune to CC+Interrupt'),
(1630, 0, 0, 69954075814, '114', '5,7,11,12,26,33', 0, 0, 'Immune to CC+Taunt'),
(1632, 0, 0, 584472182, '', '', 0, 0, 'Immune to hard CC'),
(1664, 0, 0, 2176, '', '', 0, 0, 'Immune to slow'),
(1676, 0, 0, 570425344, '', '', 0, 0, 'Immune to immunities'),
(1682, 0, 0, 4096, '', '', 0, 0, 'Immune to Stun'),
(1693, 0, 0, 584472182, '68', '', 0, 0, 'Immune to hard CC+Interrupt'),
(1694, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
(1695, 0, 0, 4096, '', '12', 0, 0, 'Immune to Stun'),
(1733, 0, 0, 1234599078, '98,124,144,145', '', 0, 0, 'Immune CC+Stun+Move');
DELETE FROM `creature_immunities` WHERE `ID` IN (1762,1771,1794,1799,1808,1835,1887,1893,1944,1964,1971);
INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES
(1762, 0, 4, 0, '', '', 0, 0, 'Immune to Curses'),
(1771, 0, 0, 0, '', '', 1, 0, 'Immune to AoE'),
(1794, 0, 0, 67108864, '68', '', 0, 0, 'Immune to Interrupts'),
(1799, 0, 0, 67109376, '68', '27,60', 0, 0, 'Immune to Interrupts+Silence'),
(1808, 0, 0, 0, '98,124,144,145', '', 0, 0, 'Immune to knockback'),
(1835, 0, 0, 1075183616, '', '', 0, 0, 'Immune to polymorph+banish+shackle+sap'),
(1887, 0, 0, 0, '98,124,144,145', '', 0, 0, 'Immune to knockback'),
(1893, 0, 0, 1091728546, '', '', 0, 0, 'Immune to Charm+Fear+Freeze+Horror+Knockout+Polymorph+Root+Sap+Shackle+Sleep+Stun'),
(1944, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
(1964, 0, 0, 67109376, '68', '27,60', 0, 0, 'Immune to Interrupts+Silence'),
(1971, 0, 0, 4096, '', '', 0, 0, 'Immune to Stun');

View file

@ -77,7 +77,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_ID_BY_GUID, "SELECT id FROM waypoint_scripts WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, security, help FROM command", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, type, type_flags, lootid, pickpocketloot, skinloot, PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, CreatureImmunitiesId, flags_extra, ScriptName FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_WAYPOINT_SCRIPT_BY_ID, "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_ITEM_TEMPLATE_BY_NAME, "SELECT entry FROM item_template WHERE name = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id1 = ? OR id2 = ? OR id3 = ?", CONNECTION_SYNCH);

View file

@ -643,7 +643,7 @@ bool Creature::UpdateEntry(uint32 Entry, const CreatureData* data, bool changele
SetControlled(true, UNIT_STATE_ROOT);
UpdateMovementFlags();
LoadSpellTemplateImmunity();
LoadTemplateImmunities(cInfo->CreatureImmunitiesId);
if (updateAI)
{
@ -2132,39 +2132,55 @@ void Creature::InitializeReactState()
SetReactState(REACT_AGGRESSIVE);
}
bool Creature::HasMechanicTemplateImmunity(uint32 mask) const
bool Creature::HasMechanicTemplateImmunity(uint64 mask) const
{
return !GetOwnerGUID().IsPlayer() && (GetCreatureTemplate()->MechanicImmuneMask & mask);
if (GetOwnerGUID().IsPlayer())
return false;
if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(_creatureImmunitiesId))
return (immunities->Mechanic.to_ullong() & mask) != 0;
// no custom immunity entry => no mechanic immunity
return false;
}
void Creature::LoadSpellTemplateImmunity()
void Creature::LoadTemplateImmunities(int32 creatureImmunitiesId)
{
// uint32 max used for "spell id", the immunity system will not perform SpellInfo checks against invalid spells
// used so we know which immunities were loaded from template
static uint32 const placeholderSpellId = std::numeric_limits<uint32>::max();
static uint32 constexpr placeholderSpellId = std::numeric_limits<uint32>::max();
// unapply template immunities (in case we're updating entry)
for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i)
auto applyCreatureImmunities = [this](CreatureImmunities const* immunities, bool apply)
{
ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, i, false);
}
if (!immunities)
return;
for (std::size_t i = 0; i < immunities->School.size(); ++i)
if (immunities->School[i])
ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, apply);
for (std::size_t i = 0; i < immunities->DispelType.size(); ++i)
if (immunities->DispelType[i])
ApplySpellImmune(placeholderSpellId, IMMUNITY_DISPEL, i, apply);
for (std::size_t i = 0; i < immunities->Mechanic.size(); ++i)
if (immunities->Mechanic[i])
ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, i, apply);
for (SpellEffects effect : immunities->Effect)
ApplySpellImmune(placeholderSpellId, IMMUNITY_EFFECT, effect, apply);
for (AuraType aura : immunities->Aura)
ApplySpellImmune(placeholderSpellId, IMMUNITY_STATE, aura, apply);
};
// don't inherit immunities for hunter pets
if (GetOwnerGUID().IsPlayer() && IsHunterPet())
{
return;
}
// unapply old template if any
if (CreatureImmunities const* oldImmunities = sSpellMgr->GetCreatureImmunities(_creatureImmunitiesId))
applyCreatureImmunities(oldImmunities, false);
if (uint8 mask = GetCreatureTemplate()->SpellSchoolImmuneMask)
// apply requested immunities
if (CreatureImmunities const* newImmunities = sSpellMgr->GetCreatureImmunities(creatureImmunitiesId))
{
for (uint8 i = SPELL_SCHOOL_NORMAL; i <= SPELL_SCHOOL_ARCANE; ++i)
{
if (mask & (1 << i))
{
ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, true);
}
}
_creatureImmunitiesId = creatureImmunitiesId;
applyCreatureImmunities(newImmunities, true);
}
else
_creatureImmunitiesId = 0;
}
bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
@ -2179,11 +2195,13 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
// Xinef: this should exclude self casts...
// Spells that don't have effectMechanics.
if (spellInfo->Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Mechanic - 1)))
if (spellInfo->Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(UI64LIT(1) << spellInfo->Mechanic))
return true;
// This check must be done instead of 'if (GetCreatureTemplate()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))' for not break
// the check of mechanic immunity on DB (tested) because GetCreatureTemplate()->MechanicImmuneMask and m_spellImmune[IMMUNITY_MECHANIC] don't have same data.
// The above helper uses the creature_immunities table rather than a
// simple mask on creature_template. We can't rely on the old mask field
// (which has been removed) because it no longer exists and did not always
// match the runtime immunity set stored in m_spellImmune.
bool immunedToAllEffects = true;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (spellInfo->Effects[i].IsEffect() && !IsImmunedToSpellEffect(spellInfo, i))
@ -2197,16 +2215,16 @@ bool Creature::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
return Unit::IsImmunedToSpell(spellInfo, spell);
}
bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const
{
// Xinef: this should exclude self casts...
if (spellInfo->Effects[index].Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(1 << (spellInfo->Effects[index].Mechanic - 1)))
if (spellInfo->Effects[index].Mechanic > MECHANIC_NONE && HasMechanicTemplateImmunity(UI64LIT(1) << spellInfo->Effects[index].Mechanic))
return true;
if (GetCreatureTemplate()->type == CREATURE_TYPE_MECHANICAL && spellInfo->Effects[index].Effect == SPELL_EFFECT_HEAL)
return true;
return Unit::IsImmunedToSpellEffect(spellInfo, index);
return Unit::IsImmunedToSpellEffect(spellInfo, index, caster);
}
SpellInfo const* Creature::reachWithSpellAttack(Unit* victim)

View file

@ -105,12 +105,11 @@ public:
bool isCanInteractWithBattleMaster(Player* player, bool msg) const;
bool CanResetTalents(Player* player) const;
bool CanCreatureAttack(Unit const* victim, bool skipDistCheck = false) const;
void LoadSpellTemplateImmunity();
bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr) override;
[[nodiscard]] bool HasMechanicTemplateImmunity(uint32 mask) const;
[[nodiscard]] bool HasMechanicTemplateImmunity(uint64 mask) const;
// redefine Unit::IsImmunedToSpell
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const override;
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const override;
// redefine Unit::IsImmunedToSpellEffect
[[nodiscard]] bool isElite() const
{
@ -186,6 +185,8 @@ public:
void UpdateAttackPowerAndDamage(bool ranged = false) override;
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& minDamage, float& maxDamage, uint8 damageIndex) override;
void LoadTemplateImmunities(int32 creatureImmunitiesId);
void LoadSparringPct();
[[nodiscard]] float GetSparringPct() const { return _sparringPct; }
@ -486,6 +487,7 @@ protected:
SpellSchoolMask m_meleeDamageSchoolMask;
uint32 m_originalEntry;
int32 _creatureImmunitiesId;
uint32 _gossipMenuId;
bool m_moveInLineOfSightDisabled;

View file

@ -202,7 +202,6 @@ struct CreatureTemplate
float speed_swim;
float speed_flight;
float detection_range; // Detection Range for Line of Sight aggro
float scale;
uint32 rank;
uint32 dmgschool;
float DamageModifier;
@ -237,8 +236,7 @@ struct CreatureTemplate
bool RacialLeader;
uint32 movementId;
bool RegenHealth;
uint32 MechanicImmuneMask;
uint8 SpellSchoolImmuneMask;
int32 CreatureImmunitiesId;
uint32 flags_extra;
uint32 ScriptID;
WorldPacket queryData; // pussywizard

View file

@ -491,7 +491,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
}
// must be after SetMinion (owner guid check)
//LoadTemplateImmunities();
LoadTemplateImmunities(0);
//LoadMechanicTemplateImmunity();
m_loading = false;
});

View file

@ -179,7 +179,7 @@ void Totem::UnSummon(Milliseconds msTime)
AddObjectToRemoveList();
}
bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const
{
// xinef: immune to all positive spells, except of stoneclaw totem absorb, sentry totem bind sight and intervene
// totems positive spells have unit_caster target
@ -209,5 +209,5 @@ bool Totem::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) con
break;
}
return Creature::IsImmunedToSpellEffect(spellInfo, index);
return Creature::IsImmunedToSpellEffect(spellInfo, index, caster);
}

View file

@ -70,7 +70,7 @@ public:
void UpdateAttackPowerAndDamage(bool /*ranged*/) override {}
void UpdateDamagePhysical(WeaponAttackType /*attType*/) override {}
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const override;
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster = nullptr) const override;
protected:
TotemType m_type;

View file

@ -72,6 +72,7 @@
#include "Vehicle.h"
#include "World.h"
#include "WorldPacket.h"
#include <algorithm>
#include <cmath>
float baseMoveSpeed[MAX_MOVE_TYPE] =
@ -372,9 +373,6 @@ Unit::Unit() : WorldObject(),
m_transform = 0;
m_canModifyStats = false;
for (uint8 i = 0; i < MAX_SPELL_IMMUNITY; ++i)
m_spellImmune[i].clear();
for (uint8 i = 0; i < UNIT_MOD_END; ++i)
{
m_auraFlatModifiersGroup[i][BASE_VALUE] = 0.0f;
@ -891,6 +889,29 @@ bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint
return false;
}
bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster /*= nullptr*/)
{
if (!spellInfo)
return false;
for (uint8 i = EFFECT_0; i < MAX_SPELL_EFFECTS; ++i)
{
if (!(effectMask & (1u << i)))
continue;
if (IsImmunedToSpellEffect(spellInfo, i, caster))
return true;
}
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
if (IsImmunedToSchool(spellInfo))
return true;
}
return false;
}
bool Unit::HasBreakableByDamageAuraType(AuraType type, uint32 excludeAura) const
{
AuraEffectList const& auras = GetAuraEffectsByType(type);
@ -2075,7 +2096,7 @@ void Unit::DealDamageShieldDamage(Unit* victim)
}
// ...or immuned
if (IsImmunedToDamageOrSchool(i_spellProto))
if (IsImmunedToDamage(victim, i_spellProto))
{
victim->SendSpellDamageImmune(this, i_spellProto->Id);
continue;
@ -3203,7 +3224,7 @@ void Unit::SendMeleeAttackStop(Unit* victim)
bool Unit::isSpellBlocked(Unit* victim, SpellInfo const* spellProto, WeaponAttackType attackType)
{
// These spells can't be blocked
if (spellProto && spellProto->HasAttribute(SPELL_ATTR0_NO_ACTIVE_DEFENSE))
if (spellProto && (spellProto->HasAttribute(SPELL_ATTR0_NO_ACTIVE_DEFENSE) || spellProto->HasAttribute(SPELL_ATTR3_ALWAYS_HIT)))
return false;
if (victim->HasIgnoreHitDirectionAura() || victim->HasInArc(M_PI, this))
@ -3571,6 +3592,9 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit* victim, SpellInfo const* spellInfo
// Resist
SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool CanReflect)
{
if (spell->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))
return SPELL_MISS_NONE;
// Check for immune
if (victim->IsImmunedToSpell(spell))
return SPELL_MISS_IMMUNE;
@ -3581,11 +3605,6 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spell, bool Ca
&& (!IsHostileTo(victim))) // prevent from affecting enemy by "positive" spell
return SPELL_MISS_NONE;
// Check for immune
// xinef: check for school immunity only
if (victim->IsImmunedToSchool(spell))
return SPELL_MISS_IMMUNE;
if (this == victim)
return SPELL_MISS_NONE;
@ -3650,13 +3669,17 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, Spell const* spell, bool CanRef
return SPELL_MISS_NONE;
}
// Check for immune
// xinef: check for school immunity only
if (victim->IsImmunedToSchool(spell))
// Check for immune to spell effects (includes school, mechanics, state, dispel immunities)
if (victim->IsImmunedToSpell(spellInfo, MAX_EFFECT_MASK, this))
{
return SPELL_MISS_IMMUNE;
}
// Damage immunity is only checked if the spell has damage effects, this immunity must not prevent aura apply
// returns SPELL_MISS_IMMUNE in that case, for other spells, the SMSG_SPELL_GO must show hit
if (spellInfo->HasOnlyDamageEffects() && victim->IsImmunedToDamage(this, spellInfo))
return SPELL_MISS_IMMUNE;
if (this == victim)
{
return SPELL_MISS_NONE;
@ -5447,7 +5470,7 @@ void Unit::RemoveMovementImpairingAuras(bool withRoot)
}
}
void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode, uint32 except)
void Unit::RemoveAurasWithMechanic(uint64 mechanic_mask, AuraRemoveMode removemode, uint32 except)
{
for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
{
@ -5466,7 +5489,7 @@ void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemo
void Unit::RemoveAurasByShapeShift()
{
uint32 mechanic_mask = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT);
uint64 mechanic_mask = (UI64LIT(1) << MECHANIC_SNARE) | (UI64LIT(1) << MECHANIC_ROOT);
for (AuraApplicationMap::iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end();)
{
Aura const* aura = iter->second->GetBase();
@ -6015,17 +6038,17 @@ bool Unit::HasNegativeAuraWithAttribute(uint32 flag, ObjectGuid guid)
return false;
}
bool Unit::HasAuraWithMechanic(uint32 mechanicMask) const
bool Unit::HasAuraWithMechanic(uint64 mechanicMask) const
{
for (AuraApplicationMap::const_iterator iter = m_appliedAuras.begin(); iter != m_appliedAuras.end(); ++iter)
{
SpellInfo const* spellInfo = iter->second->GetBase()->GetSpellInfo();
if (spellInfo->Mechanic && (mechanicMask & (1 << spellInfo->Mechanic)))
if (spellInfo->Mechanic && (mechanicMask & (UI64LIT(1) << spellInfo->Mechanic)))
return true;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (iter->second->HasEffect(i) && spellInfo->Effects[i].Effect && spellInfo->Effects[i].Mechanic)
if (mechanicMask & (1 << spellInfo->Effects[i].Mechanic))
if (mechanicMask & (UI64LIT(1) << spellInfo->Effects[i].Mechanic))
return true;
}
@ -8833,7 +8856,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui
});
}
if (uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask())
if (uint64 mechanicMask = spellProto->GetAllEffectsMechanicMask())
{
int32 modifierMax = 0;
int32 modifierMin = 0;
@ -8848,7 +8871,7 @@ uint32 Unit::SpellDamageBonusTaken(Unit* caster, SpellInfo const* spellProto, ui
if (!caster || caster->GetGUID() != (*i)->GetCasterGUID())
continue;
if (mechanicMask & uint32(1 << (*i)->GetMiscValue()))
if (mechanicMask & uint64(UI64LIT(1) << (*i)->GetMiscValue()))
{
if ((*i)->GetAmount() > 0)
{
@ -9739,107 +9762,90 @@ int32 Unit::SpellBaseHealingBonusDone(SpellSchoolMask schoolMask)
return AdvertisedBenefit;
}
bool Unit::IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const
uint32 Unit::GetDamageImmunityMask() const
{
if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE)
{
return false;
}
// If m_immuneToDamage type contain magic, IMMUNE damage.
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
if ((itr->type & meleeSchoolMask) == meleeSchoolMask)
return true;
return false;
uint32 mask = 0;
SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
for (auto const& [immunitySchoolMask, _] : damageList)
mask |= immunitySchoolMask;
return mask;
}
bool Unit::IsImmunedToDamage(SpellInfo const* spellInfo) const
bool Unit::IsImmunedToDamage(SpellSchoolMask schoolMask) const
{
if (!spellInfo)
{
return false;
}
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
{
return false;
}
if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
return false;
}
uint32 schoolMask = spellInfo->GetSchoolMask();
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
{
return false;
}
// If m_immuneToDamage type contain magic, IMMUNE damage.
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
if ((itr->type & schoolMask) == schoolMask)
// If m_immuneToSchool type contains all requested schools, IMMUNE damage.
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for (auto const& itr : schoolList)
if (IsImmuneMaskFully(SpellSchoolMask(itr.first), schoolMask))
return true;
return false;
// If m_immuneToDamage type contains all requested schools, IMMUNE damage.
SpellImmuneContainer const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
return std::ranges::any_of(damageList, [schoolMask](auto const& immune)
{
return Unit::IsImmuneMaskFully(SpellSchoolMask(immune.first), schoolMask);
});
}
bool Unit::IsImmunedToDamage(Spell const* spell) const
bool Unit::IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const
{
SpellInfo const* spellInfo = spell->GetSpellInfo();
if (!spellInfo)
{
return false;
}
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
{
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
return false;
}
if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
return false;
}
uint32 schoolMask = spell->GetSpellSchoolMask();
SpellSchoolMask schoolMask = SpellSchoolMask(spellInfo->GetSchoolMask());
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
{
return false;
}
// If m_immuneToDamage type contain magic, IMMUNE damage.
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
auto hasImmunity = [&](SpellImmuneContainer const& container)
{
if ((itr->type & schoolMask) == schoolMask)
uint32 schoolImmunityMask = 0;
for (auto const& [immunitySchoolMask, immunityAuraId] : container)
{
return true;
SpellInfo const* immuneAuraInfo = sSpellMgr->GetSpellInfo(immunityAuraId);
if (immuneAuraInfo && !immuneAuraInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && caster && caster->IsFriendlyTo(this))
continue;
if (immuneAuraInfo && spellInfo->CanPierceImmuneAura(immuneAuraInfo))
continue;
schoolImmunityMask |= immunitySchoolMask;
}
}
return false;
}
bool Unit::IsImmunedToSchool(SpellSchoolMask meleeSchoolMask) const
{
if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE)
{
return false;
}
// We need to be immune to all types
return (schoolImmunityMask & schoolMask) == schoolMask;
};
// If m_immuneToSchool type contain this school type, IMMUNE damage.
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
if ((itr->type & meleeSchoolMask) == meleeSchoolMask)
return true;
if (hasImmunity(m_spellImmune[IMMUNITY_SCHOOL]))
return true;
// If m_immuneToDamage type contain magic, IMMUNE damage.
if (hasImmunity(m_spellImmune[IMMUNITY_DAMAGE]))
return true;
return false;
}
bool Unit::IsImmunedToSchool(SpellSchoolMask schoolMask) const
{
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
return false;
// Check IMMUNITY_SCHOOL: returns true if ALL schools in the mask are covered by at least one immunity
// (e.g., Anti-Magic Shell provides school immunity to all magic schools)
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
return std::ranges::any_of(schoolList, [schoolMask](auto const& immune)
{
return (immune.first & schoolMask) == schoolMask;
});
}
bool Unit::IsImmunedToSchool(SpellInfo const* spellInfo) const
{
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
@ -9853,10 +9859,10 @@ bool Unit::IsImmunedToSchool(SpellInfo const* spellInfo) const
if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471)
{
// If m_immuneToSchool type contain this school type, IMMUNE damage.
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
if ((itr->type & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId)))
// 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;
}
@ -9879,11 +9885,11 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const
if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471)
{
// If m_immuneToSchool type contain this school type, IMMUNE damage.
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
// 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->type & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->spellId)))
if ((itr->first & schoolMask) == schoolMask && !spellInfo->CanPierceImmuneAura(sSpellMgr->GetSpellInfo(itr->second)))
{
return true;
}
@ -9893,19 +9899,49 @@ bool Unit::IsImmunedToSchool(Spell const* spell) const
return false;
}
bool Unit::IsImmunedToDamageOrSchool(SpellSchoolMask meleeSchoolMask) const
bool Unit::IsImmunedToDamageOrSchool(SpellSchoolMask schoolMask) const
{
if (meleeSchoolMask == SPELL_SCHOOL_MASK_NONE)
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
{
return false;
}
return IsImmunedToDamage(meleeSchoolMask) || IsImmunedToSchool(meleeSchoolMask);
return IsImmunedToDamage(schoolMask) || IsImmunedToSchool(schoolMask);
}
bool Unit::IsImmunedToDamageOrSchool(SpellInfo const* spellInfo) const
bool Unit::IsImmunedToAuraPeriodicTick(Unit const* caster, SpellInfo const* spellInfo) const
{
return IsImmunedToDamage(spellInfo) || IsImmunedToSchool(spellInfo);
if (!spellInfo)
return false;
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) || spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
return false;
SpellSchoolMask schoolMask = SpellSchoolMask(spellInfo->GetSchoolMask());
if (schoolMask == SPELL_SCHOOL_MASK_NONE)
return false;
auto hasImmunity = [&](SpellImmuneContainer const& container)
{
uint32 schoolImmunityMask = 0;
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))
continue;
schoolImmunityMask |= immunitySchoolMask;
}
// We need to be immune to all types
return (schoolImmunityMask & schoolMask) == schoolMask;
};
// If m_immuneToSchool type contain this school type, IMMUNE periodic tick.
if (hasImmunity(m_spellImmune[IMMUNITY_SCHOOL]))
return true;
return false;
}
bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
@ -9914,39 +9950,35 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
return false;
// Single spell immunity.
SpellImmuneList const& idList = m_spellImmune[IMMUNITY_ID];
for (SpellImmuneList::const_iterator itr = idList.begin(); itr != idList.end(); ++itr)
if (itr->type == spellInfo->Id)
return true;
SpellImmuneContainer const& idList = m_spellImmune[IMMUNITY_ID];
if (idList.count(spellInfo->Id) > 0)
return true;
// xinef: my special immunity, if spellid is not on this list it means npc is immune
SpellImmuneList const& allowIdList = m_spellImmune[IMMUNITY_ALLOW_ID];
SpellImmuneContainer const& allowIdList = m_spellImmune[IMMUNITY_ALLOW_ID];
if (!allowIdList.empty())
{
for (SpellImmuneList::const_iterator itr = allowIdList.begin(); itr != allowIdList.end(); ++itr)
if (itr->type == spellInfo->Id)
return false;
return true;
if (allowIdList.count(spellInfo->Id) == 0)
return true;
return false;
}
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
return false;
if (spellInfo->Dispel)
if (uint32 dispel = spellInfo->Dispel)
{
SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
for (SpellImmuneList::const_iterator itr = dispelList.begin(); itr != dispelList.end(); ++itr)
if (itr->type == spellInfo->Dispel)
return true;
SpellImmuneContainer const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
if (dispelList.count(dispel) > 0)
return true;
}
// Spells that don't have effectMechanics.
if (spellInfo->Mechanic)
if (uint32 mechanic = spellInfo->Mechanic)
{
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
if (itr->type == spellInfo->Mechanic)
return true;
SpellImmuneContainer const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
if (mechanicList.count(mechanic) > 0)
return true;
}
bool immuneToAllEffects = true;
@ -9971,7 +10003,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
if (immuneToAllEffects) //Return immune only if the target is immune to all spell effects.
return true;
if (spellInfo->Id != 42292 && spellInfo->Id != 59752 && spellInfo->Id != 19574 && spellInfo->Id != 34471)
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
SpellSchoolMask spellSchoolMask = spellInfo->GetSchoolMask();
if (spell)
@ -9981,16 +10013,24 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
if (spellSchoolMask != SPELL_SCHOOL_MASK_NONE)
{
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
SpellImmuneContainer const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
for (auto itr = schoolList.begin(); itr != schoolList.end(); ++itr)
{
SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->spellId);
if (((itr->type & spellSchoolMask) == spellSchoolMask)
&& (!immuneSpellInfo || immuneSpellInfo->IsPositive()) && !spellInfo->IsPositive()
&& !spellInfo->CanPierceImmuneAura(immuneSpellInfo))
SpellInfo const* immuneSpellInfo = sSpellMgr->GetSpellInfo(itr->second);
if (!(itr->first & spellSchoolMask))
continue;
if (immuneSpellInfo && !immuneSpellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))
{
return true;
Unit const* spellCaster = spell ? spell->GetCaster() : nullptr;
if (spellCaster && spellCaster->IsFriendlyTo(this))
continue;
}
if (spellInfo->CanPierceImmuneAura(immuneSpellInfo))
continue;
return true;
}
}
}
@ -9998,7 +10038,7 @@ bool Unit::IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell)
return false;
}
bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) const
bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index, Unit const* caster /*= nullptr*/) const
{
if (!spellInfo || !spellInfo->Effects[index].IsEffect())
return false;
@ -10010,12 +10050,16 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons
if (spellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES) && !HasSpiritOfRedemptionAura())
return false;
// Special-case handling for Vezax's Aura of Despair.
static constexpr uint32 SPELL_AURA_OF_DESPAIR_1 = 62692;
static constexpr uint32 SPELL_AURA_OF_DESPAIR_2 = 64848;
//If m_immuneToEffect type contain this effect type, IMMUNE effect.
uint32 effect = spellInfo->Effects[index].Effect;
SpellImmuneList const& effectList = m_spellImmune[IMMUNITY_EFFECT];
for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
SpellImmuneContainer const& effectList = m_spellImmune[IMMUNITY_EFFECT];
for (SpellImmuneContainer::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
{
if (itr->type == effect && (itr->spellId != 62692 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
if (itr->first == effect && (itr->second != SPELL_AURA_OF_DESPAIR_1 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
{
return true;
}
@ -10023,41 +10067,32 @@ bool Unit::IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) cons
if (uint32 mechanic = spellInfo->Effects[index].Mechanic)
{
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
if (itr->type == mechanic)
return true;
auto const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
if (mechanicList.count(mechanic) > 0)
return true;
}
if (uint32 aura = spellInfo->Effects[index].ApplyAuraName)
if (!spellInfo->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))
{
SpellImmuneList const& list = m_spellImmune[IMMUNITY_STATE];
for (SpellImmuneList::const_iterator itr = list.begin(); itr != list.end(); ++itr)
if (uint32 aura = spellInfo->Effects[index].ApplyAuraName)
{
if (itr->type == aura && (itr->spellId != 64848 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
SpellImmuneContainer const& list = m_spellImmune[IMMUNITY_STATE];
for (SpellImmuneContainer::const_iterator itr = list.begin(); itr != list.end(); ++itr)
{
if (!spellInfo->HasAttribute(SPELL_ATTR3_ALWAYS_HIT))
{
if (itr->blockType == SPELL_BLOCK_TYPE_ALL || spellInfo->IsPositive()) // xinef: added for pet scaling
{
return true;
}
}
}
}
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
// Check for immune to application of harmful magical effects
AuraEffectList const& immuneAuraApply = GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL);
for (AuraEffectList::const_iterator iter = immuneAuraApply.begin(); iter != immuneAuraApply.end(); ++iter)
{
if (/*(spellInfo->Dispel == DISPEL_MAGIC || spellInfo->Dispel == DISPEL_CURSE || spellInfo->Dispel == DISPEL_DISEASE) &&*/ // Magic debuff, xinef: all kinds?
((*iter)->GetMiscValue() & spellInfo->GetSchoolMask()) && // Check school
!spellInfo->IsPositiveEffect(index) && // Harmful
spellInfo->Effects[index].Effect != SPELL_EFFECT_PERSISTENT_AREA_AURA) // Not Persistent area auras
{
if (itr->first == aura && (itr->second != SPELL_AURA_OF_DESPAIR_2 || (spellInfo->Effects[index].MiscValue == POWER_MANA && !CanRestoreMana(spellInfo))))
return true;
}
if (!spellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
// Check for immune to application of harmful magical effects
for (AuraEffect* immuneAuraApply : GetAuraEffectsByType(SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL))
{
if (!(immuneAuraApply->GetMiscValue() & spellInfo->GetSchoolMask())) // Check school
continue;
if (spellInfo->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || (caster && !IsFriendlyTo(caster))) // Harmful
return true;
}
}
}
@ -10286,17 +10321,17 @@ uint32 Unit::MeleeDamageBonusTaken(Unit* attacker, uint32 pdamage, WeaponAttackT
});
// Mod damage from spell mechanic
uint32 mechanicMask = spellProto->GetAllEffectsMechanicMask();
uint64 mechanicMask = spellProto->GetAllEffectsMechanicMask();
// Shred, Maul - "Effects which increase Bleed damage also increase Shred damage"
if (spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[0] & 0x00008800)
mechanicMask |= (1 << MECHANIC_BLEED);
mechanicMask |= (UI64LIT(1) << MECHANIC_BLEED);
if (mechanicMask)
{
TakenTotalMod *= GetTotalAuraMultiplier(SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT, [mechanicMask](AuraEffect const* aurEff) -> bool
{
if (mechanicMask & uint32(1 << (aurEff->GetMiscValue())))
if (mechanicMask & uint64(UI64LIT(1) << (aurEff->GetMiscValue())))
return true;
return false;
});
@ -10364,51 +10399,17 @@ private:
uint32 _type;
};
void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType)
void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType /*blockType*/)
{
if (apply)
{
// xinef: immunities with spellId 0 are intended to be applied only once (script purposes mosty)
if (spellId == 0 && std::find_if(m_spellImmune[op].begin(), m_spellImmune[op].end(), spellIdImmunityPredicate(type)) != m_spellImmune[op].end())
return;
SpellImmune immune;
immune.spellId = spellId;
immune.type = type;
immune.blockType = blockType;
m_spellImmune[op].push_back(std::move(immune));
}
m_spellImmune[op].emplace(type, spellId);
else
{
for (SpellImmuneList::iterator itr = m_spellImmune[op].begin(); itr != m_spellImmune[op].end(); ++itr)
auto bounds = m_spellImmune[op].equal_range(type);
for (auto itr = bounds.first; itr != bounds.second;)
{
if (itr->spellId == spellId && itr->type == type)
{
m_spellImmune[op].erase(itr);
break;
}
}
}
}
void Unit::ApplySpellDispelImmunity(SpellInfo const* spellProto, DispelType type, bool apply)
{
ApplySpellImmune(spellProto->Id, IMMUNITY_DISPEL, type, apply);
if (apply && spellProto->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
{
// Create dispel mask by dispel type
uint32 dispelMask = SpellInfo::GetDispelMask(type);
// Dispel all existing auras vs current dispel type
AuraApplicationMap& auras = GetAppliedAuras();
for (AuraApplicationMap::iterator itr = auras.begin(); itr != auras.end();)
{
SpellInfo const* spell = itr->second->GetBase()->GetSpellInfo();
if (spell->GetDispelMask() & dispelMask)
{
// Dispel aura
RemoveAura(itr);
}
if (itr->second == spellId)
itr = m_spellImmune[op].erase(itr);
else
++itr;
}
@ -11174,8 +11175,9 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
{
if (Creature* creature = ToCreature())
{
uint32 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask;
if (immuneMask & (1 << (MECHANIC_SNARE - 1)) || immuneMask & (1 << (MECHANIC_DAZE - 1)))
// use creature helper which now consults creature_immunities table
if (creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_SNARE) ||
creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_DAZE))
break;
}
@ -11196,7 +11198,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
if (creature
&& !IsPet()
&& !(IsControlledByPlayer() && IsVehicle())
&& !(creature->HasMechanicTemplateImmunity(MECHANIC_SNARE))
&& !(creature->HasMechanicTemplateImmunity(UI64LIT(1) << MECHANIC_SNARE))
&& !(creature->IsDungeonBoss()))
{
// 1.6% for each % under 30.
@ -15075,7 +15077,7 @@ Aura* Unit::AddAura(SpellInfo const* spellInfo, uint8 effMask, Unit* target)
{
if (!(effMask & (1 << i)))
continue;
if (target->IsImmunedToSpellEffect(spellInfo, i))
if (target->IsImmunedToSpellEffect(spellInfo, i, this))
effMask &= ~(1 << i);
}

View file

@ -1408,7 +1408,7 @@ public:
void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = 0, bool isAutoshot = false);
void RemoveAurasWithAttribute(uint32 flags);
void RemoveAurasWithFamily(SpellFamilyNames family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, ObjectGuid casterGUID);
void RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode = AURA_REMOVE_BY_DEFAULT, uint32 except = 0);
void RemoveAurasWithMechanic(uint64 mechanic_mask, AuraRemoveMode removemode = AURA_REMOVE_BY_DEFAULT, uint32 except = 0);
void RemoveMovementImpairingAuras(bool withRoot);
void RemoveAurasByShapeShift();
@ -1500,7 +1500,7 @@ public:
bool HasNegativeAuraWithInterruptFlag(uint32 flag, ObjectGuid guid = ObjectGuid::Empty);
[[nodiscard]] bool HasVisibleAuraType(AuraType auraType) const;
bool HasNegativeAuraWithAttribute(uint32 flag, ObjectGuid guid = ObjectGuid::Empty);
[[nodiscard]] bool HasAuraWithMechanic(uint32 mechanicMask) const;
[[nodiscard]] bool HasAuraWithMechanic(uint64 mechanicMask) const;
[[nodiscard]] bool HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const;
@ -1636,17 +1636,22 @@ public:
// Spells immunities
void ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply, SpellImmuneBlockType blockType = SPELL_BLOCK_TYPE_ALL);
void ApplySpellDispelImmunity(SpellInfo const* spellProto, DispelType type, bool apply);
virtual bool IsImmunedToSpell(SpellInfo const* spellInfo, Spell const* spell = nullptr);
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask meleeSchoolMask) const;
[[nodiscard]] bool IsImmunedToDamage(SpellInfo const* spellInfo) const;
[[nodiscard]] bool IsImmunedToDamage(Spell const* spell) const;
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask meleeSchoolMask) const;
bool IsImmunedToSpell(SpellInfo const* spellInfo, uint32 effectMask, Unit const* caster = nullptr);
[[nodiscard]] bool IsImmunedToDamage(SpellSchoolMask schoolMask) const;
[[nodiscard]] bool IsImmunedToDamage(Unit const* caster, SpellInfo const* spellInfo) const;
[[nodiscard]] bool IsImmunedToSchool(SpellSchoolMask schoolMask) 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 meleeSchoolMask) const;
bool IsImmunedToDamageOrSchool(SpellInfo const* spellInfo) const;
virtual bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, uint32 index) 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;
// Critic chances
bool isBlockCritical();
@ -2080,7 +2085,8 @@ public:
float m_modAttackSpeedPct[3];
SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
typedef std::unordered_multimap<uint32 /*type*/, uint32 /*spellId*/> SpellImmuneContainer;
SpellImmuneContainer m_spellImmune[MAX_SPELL_IMMUNITY];
uint32 m_lastSanctuaryTime;
// pet auras

View file

@ -522,18 +522,18 @@ void ObjectMgr::LoadCreatureTemplates()
// 0 1 2 3 4 5 6 7 8
QueryResult result = WorldDatabase.Query("SELECT entry, difficulty_entry_1, difficulty_entry_2, difficulty_entry_3, KillCredit1, KillCredit2, name, subname, IconName, "
// 9 10 11 12 13 14 15 16 17 18 19 20 21 22
"gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, scale, `rank`, dmgschool, "
// 23 24 25 26 27 28 29 30 31 32
// 9 10 11 12 13 14 15 16 17 18 19 20 21
"gossip_menu_id, minlevel, maxlevel, exp, faction, npcflag, speed_walk, speed_run, speed_swim, speed_flight, detection_range, `rank`, dmgschool, "
// 22 23 24 25 26 27 28 29 30 31
"DamageModifier, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, dynamicflags, family, "
// 33 34 35 36 37
// 32 33 34 35 36
"type, type_flags, lootid, pickpocketloot, skinloot, "
// 38 39 40 41 42 43 44 45 46 47
// 37 38 39 40 41 42 43 44 45 46
"PetSpellDataId, VehicleId, mingold, maxgold, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, "
// 48 49 50 51 52 53 54 55 56 57 58 59
"ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, mechanic_immune_mask, "
// 60 61 62
"spell_school_immune_mask, flags_extra, ScriptName "
// 47 48 49 50 51 52 53 54 55 56 57
"ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, HoverHeight, HealthModifier, ManaModifier, ArmorModifier, ExperienceModifier, RacialLeader, movementId, RegenHealth, "
// 58 59 60
"CreatureImmunitiesId, flags_extra, ScriptName "
"FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId ORDER BY entry DESC;");
if (!result)
@ -619,24 +619,23 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook)
creatureTemplate.speed_swim = fields[17].Get<float>();
creatureTemplate.speed_flight = fields[18].Get<float>();
creatureTemplate.detection_range = fields[19].Get<float>();
creatureTemplate.scale = fields[20].Get<float>();
creatureTemplate.rank = uint32(fields[21].Get<uint8>());
creatureTemplate.dmgschool = uint32(fields[22].Get<int8>());
creatureTemplate.DamageModifier = fields[23].Get<float>();
creatureTemplate.BaseAttackTime = fields[24].Get<uint32>();
creatureTemplate.RangeAttackTime = fields[25].Get<uint32>();
creatureTemplate.BaseVariance = fields[26].Get<float>();
creatureTemplate.RangeVariance = fields[27].Get<float>();
creatureTemplate.unit_class = uint32(fields[28].Get<uint8>());
creatureTemplate.unit_flags = fields[29].Get<uint32>();
creatureTemplate.unit_flags2 = fields[30].Get<uint32>();
creatureTemplate.dynamicflags = fields[31].Get<uint32>();
creatureTemplate.family = uint32(fields[32].Get<uint8>());
creatureTemplate.type = uint32(fields[33].Get<uint8>());
creatureTemplate.type_flags = fields[34].Get<uint32>();
creatureTemplate.lootid = fields[35].Get<uint32>();
creatureTemplate.pickpocketLootId = fields[36].Get<uint32>();
creatureTemplate.SkinLootId = fields[37].Get<uint32>();
creatureTemplate.rank = uint32(fields[20].Get<uint8>());
creatureTemplate.dmgschool = uint32(fields[21].Get<int8>());
creatureTemplate.DamageModifier = fields[22].Get<float>();
creatureTemplate.BaseAttackTime = fields[23].Get<uint32>();
creatureTemplate.RangeAttackTime = fields[24].Get<uint32>();
creatureTemplate.BaseVariance = fields[25].Get<float>();
creatureTemplate.RangeVariance = fields[26].Get<float>();
creatureTemplate.unit_class = uint32(fields[27].Get<uint8>());
creatureTemplate.unit_flags = fields[28].Get<uint32>();
creatureTemplate.unit_flags2 = fields[29].Get<uint32>();
creatureTemplate.dynamicflags = fields[30].Get<uint32>();
creatureTemplate.family = uint32(fields[31].Get<uint8>());
creatureTemplate.type = uint32(fields[32].Get<uint8>());
creatureTemplate.type_flags = fields[33].Get<uint32>();
creatureTemplate.lootid = fields[34].Get<uint32>();
creatureTemplate.pickpocketLootId = fields[35].Get<uint32>();
creatureTemplate.SkinLootId = fields[36].Get<uint32>();
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
{
@ -648,49 +647,56 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields, bool triggerHook)
creatureTemplate.spells[i] = 0;
}
creatureTemplate.PetSpellDataId = fields[38].Get<uint32>();
creatureTemplate.VehicleId = fields[39].Get<uint32>();
creatureTemplate.mingold = fields[40].Get<uint32>();
creatureTemplate.maxgold = fields[41].Get<uint32>();
creatureTemplate.AIName = fields[42].Get<std::string>();
creatureTemplate.MovementType = uint32(fields[43].Get<uint8>());
if (!fields[44].IsNull())
creatureTemplate.PetSpellDataId = fields[37].Get<uint32>();
creatureTemplate.VehicleId = fields[38].Get<uint32>();
creatureTemplate.mingold = fields[39].Get<uint32>();
creatureTemplate.maxgold = fields[40].Get<uint32>();
creatureTemplate.AIName = fields[41].Get<std::string>();
creatureTemplate.MovementType = uint32(fields[42].Get<uint8>());
if (!fields[43].IsNull())
{
creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[44].Get<uint8>());
creatureTemplate.Movement.Ground = static_cast<CreatureGroundMovementType>(fields[43].Get<uint8>());
}
creatureTemplate.Movement.Swim = fields[45].Get<bool>();
if (!fields[46].IsNull())
creatureTemplate.Movement.Swim = fields[44].Get<bool>();
if (!fields[45].IsNull())
{
creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[46].Get<uint8>());
creatureTemplate.Movement.Flight = static_cast<CreatureFlightMovementType>(fields[45].Get<uint8>());
}
creatureTemplate.Movement.Rooted = fields[47].Get<bool>();
creatureTemplate.Movement.Rooted = fields[46].Get<bool>();
if (!fields[47].IsNull())
{
creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[47].Get<uint8>());
}
if (!fields[48].IsNull())
{
creatureTemplate.Movement.Chase = static_cast<CreatureChaseMovementType>(fields[48].Get<uint8>());
creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[48].Get<uint8>());
}
if (!fields[49].IsNull())
{
creatureTemplate.Movement.Random = static_cast<CreatureRandomMovementType>(fields[49].Get<uint8>());
}
if (!fields[50].IsNull())
{
creatureTemplate.Movement.InteractionPauseTimer = fields[50].Get<uint32>();
creatureTemplate.Movement.InteractionPauseTimer = fields[49].Get<uint32>();
}
creatureTemplate.HoverHeight = fields[51].Get<float>();
creatureTemplate.ModHealth = fields[52].Get<float>();
creatureTemplate.ModMana = fields[53].Get<float>();
creatureTemplate.ModArmor = fields[54].Get<float>();
creatureTemplate.ModExperience = fields[55].Get<float>();
creatureTemplate.RacialLeader = fields[56].Get<bool>();
creatureTemplate.movementId = fields[57].Get<uint32>();
creatureTemplate.RegenHealth = fields[58].Get<bool>();
creatureTemplate.MechanicImmuneMask = fields[59].Get<uint32>();
creatureTemplate.SpellSchoolImmuneMask = fields[60].Get<uint8>();
creatureTemplate.flags_extra = fields[61].Get<uint32>();
creatureTemplate.ScriptID = GetScriptId(fields[62].Get<std::string>());
creatureTemplate.HoverHeight = fields[50].Get<float>();
creatureTemplate.ModHealth = fields[51].Get<float>();
creatureTemplate.ModMana = fields[52].Get<float>();
creatureTemplate.ModArmor = fields[53].Get<float>();
creatureTemplate.ModExperience = fields[54].Get<float>();
creatureTemplate.RacialLeader = fields[55].Get<bool>();
creatureTemplate.movementId = fields[56].Get<uint32>();
creatureTemplate.RegenHealth = fields[57].Get<bool>();
creatureTemplate.CreatureImmunitiesId = fields[58].Get<int32>();
creatureTemplate.flags_extra = fields[59].Get<uint32>();
creatureTemplate.ScriptID = GetScriptId(fields[60].Get<std::string>());
// Warn about deprecated immunity flags that should be moved to `creature_immunities` table
if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT)
LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit NO_TAUNT (0x100) set. This will be migrated to the `creature_immunities` table in a future update.", entry);
if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_AVOID_AOE)
LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit AVOID_AOE (0x400000) set. This will be migrated to the `creature_immunities` table in a future update.", entry);
if (creatureTemplate.flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK)
LOG_WARN("server.loading", "Creature (Entry: {}) has deprecated flags_extra bit IMMUNITY_KNOCKBACK (0x40000000) set. This will be migrated to the `creature_immunities` table in a future update.", entry);
// useful if the creature template load is being triggered from outside this class
if (triggerHook)

View file

@ -1223,7 +1223,7 @@ bool AuraEffect::CheckEffectProc(AuraApplication* aurApp, ProcEventInfo& eventIn
case SPELL_AURA_MECHANIC_IMMUNITY:
case SPELL_AURA_MOD_MECHANIC_RESISTANCE:
// compare mechanic
if (!spellInfo || !(spellInfo->GetAllEffectsMechanicMask() & (1 << GetMiscValue())))
if (!spellInfo || !(spellInfo->GetAllEffectsMechanicMask() & (UI64LIT(1) << GetMiscValue())))
return false;
break;
case SPELL_AURA_MOD_CASTING_SPEED_NOT_STACK:
@ -3933,312 +3933,7 @@ void AuraEffect::HandleModStateImmunityMask(AuraApplication const* aurApp, uint8
return;
Unit* target = aurApp->GetTarget();
std::list <AuraType> aura_immunity_list;
uint32 mechanic_immunity_list = 0;
int32 miscVal = GetMiscValue();
switch (miscVal)
{
case 96:
case 1615:
{
if (!GetAmount())
{
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_CHARM);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
}
break;
}
case 679:
{
if (GetId() == 57742)
{
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
}
break;
}
case 1557:
{
if (GetId() == 64187)
{
mechanic_immunity_list = (1 << MECHANIC_STUN);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
}
else
{
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
}
break;
}
case 1614:
case 1694:
{
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT);
break;
}
case 1630:
{
if (!GetAmount())
{
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT);
}
else
{
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
}
break;
}
case 477:
case 1733:
case 1632:
{
if (!GetAmount())
{
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT)
| (1 << MECHANIC_FEAR) | (1 << MECHANIC_STUN)
| (1 << MECHANIC_SLEEP) | (1 << MECHANIC_CHARM)
| (1 << MECHANIC_SAPPED) | (1 << MECHANIC_HORROR)
| (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED)
| (1 << MECHANIC_FREEZE) | (1 << MECHANIC_TURN) | (1 << MECHANIC_BANISH);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_BANISH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
}
break;
}
case 878:
{
if (GetAmount() == 1)
{
mechanic_immunity_list = (1 << MECHANIC_SNARE) | (1 << MECHANIC_STUN)
| (1 << MECHANIC_DISORIENTED) | (1 << MECHANIC_FREEZE);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
}
break;
}
default:
break;
}
if (aura_immunity_list.empty())
{
// Roots, OK
if (GetMiscValue() & (1 << 0))
{
mechanic_immunity_list = (1 << MECHANIC_SNARE);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_ROOT);
}
// Taunt, OK
if (GetMiscValue() & (1 << 1))
{
aura_immunity_list.push_back(SPELL_AURA_MOD_TAUNT);
}
// Crowd-Control auras?
if (GetMiscValue() & (1 << 2))
{
mechanic_immunity_list = (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_DISORIENTED);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
}
// Interrupt, OK
if (GetMiscValue() & (1 << 3))
{
mechanic_immunity_list = (1 << MECHANIC_INTERRUPT);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_INTERRUPT, apply);
}
// Transform?
if (GetMiscValue() & (1 << 4))
{
aura_immunity_list.push_back(SPELL_AURA_TRANSFORM);
}
// Stun auras breakable by damage (Incapacitate effects), OK
if (GetMiscValue() & (1 << 5))
{
mechanic_immunity_list = (1 << MECHANIC_KNOCKOUT);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply);
}
// // Slowing effects
if (GetMiscValue() & (1 << 6))
{
mechanic_immunity_list = (1 << MECHANIC_SNARE);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
}
// Charm auras?, 90%
if ((GetMiscValue() & (1 << 7)))
{
mechanic_immunity_list = (1 << MECHANIC_CHARM);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_CHARM);
aura_immunity_list.push_back(SPELL_AURA_MOD_POSSESS);
}
// UNK
// if ((GetMiscValue() & (1 << 8)))
// {
// }
// Fear, OK
if (GetMiscValue() & (1 << 9))
{
mechanic_immunity_list = (1 << MECHANIC_FEAR);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_FEAR);
}
// Stuns, OK
if (GetMiscValue() & (1 << 10))
{
mechanic_immunity_list = (1 << MECHANIC_STUN);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
aura_immunity_list.push_back(SPELL_AURA_MOD_STUN);
}
}
// apply immunities
for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter)
target->ApplySpellImmune(GetId(), IMMUNITY_STATE, *iter, apply);
// Patch 3.0.3 Bladestorm now breaks all snares and roots on the warrior when activated.
if (GetId() == 46924)
{
// Knockback and hex
target->ApplySpellImmune(GetId(), IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, apply);
}
if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
{
target->RemoveAurasWithMechanic(mechanic_immunity_list, AURA_REMOVE_BY_DEFAULT, GetId());
for (std::list <AuraType>::iterator iter = aura_immunity_list.begin(); iter != aura_immunity_list.end(); ++iter)
target->RemoveAurasByType(*iter);
}
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
}
void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const
@ -4247,67 +3942,7 @@ void AuraEffect::HandleModMechanicImmunity(AuraApplication const* aurApp, uint8
return;
Unit* target = aurApp->GetTarget();
uint32 mechanic = 0;
switch (GetId())
{
case 46924: // BladeStorm
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
break;
case 34471: // The Beast Within
case 19574: // Bestial Wrath
case 38484: // Bestial Wrath
case 40081: // Free friend (Black Temple)
mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_CHARM, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DISORIENTED, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FEAR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SLEEP, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_FREEZE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_KNOCKOUT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_POLYMORPH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_BANISH, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SHACKLE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_TURN, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_HORROR, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_DAZE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SAPPED, apply);
break;
case 42292: // PvP trinket
case 59752: // Every Man for Himself
case 65547: // PvP trinket for Faction Champions (ToC 25)
case 53490: // Bullheaded
case 46227: // Medalion of Immunity
mechanic = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
target->RemoveAurasByType(SPELL_AURA_PREVENTS_FLEEING); // xinef: Patch 2.3.0 PvP Trinkets: Insignia of the Alliance, Insignia of the Horde, Medallion of the Alliance, and Medallion of the Horde now clear the debuff from Judgement of Justice.
// Actually we should apply immunities here, too, but the aura has only 100 ms duration, so there is practically no point
break;
case 54508: // Demonic Empowerment
mechanic = (1 << MECHANIC_SNARE) | (1 << MECHANIC_ROOT);
target->RemoveAurasByType(SPELL_AURA_MOD_STUN);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_SNARE, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_ROOT, apply);
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, MECHANIC_STUN, apply);
break;
default:
if (GetMiscValue() < 1)
return;
mechanic = 1 << GetMiscValue();
target->ApplySpellImmune(GetId(), IMMUNITY_MECHANIC, GetMiscValue(), apply);
break;
}
if (apply && GetSpellInfo()->HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
{
// Xinef: exception for purely snare mechanic (eg. hands of freedom)!
if (mechanic == (1 << MECHANIC_SNARE))
target->RemoveMovementImpairingAuras(false);
else
target->RemoveAurasWithMechanic(mechanic, AURA_REMOVE_BY_DEFAULT, GetId());
}
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
}
void AuraEffect::HandleAuraModEffectImmunity(AuraApplication const* aurApp, uint8 mode, bool apply) const
@ -4415,7 +4050,7 @@ void AuraEffect::HandleAuraModDmgImmunity(AuraApplication const* aurApp, uint8 m
Unit* target = aurApp->GetTarget();
target->ApplySpellImmune(GetId(), IMMUNITY_DAMAGE, GetMiscValue(), apply);
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
if (apply)
target->GetThreatMgr().EvaluateSuppressed();
@ -4427,8 +4062,7 @@ void AuraEffect::HandleAuraModDispelImmunity(AuraApplication const* aurApp, uint
return;
Unit* target = aurApp->GetTarget();
target->ApplySpellDispelImmunity(m_spellInfo, DispelType(GetMiscValue()), (apply));
m_spellInfo->ApplyAllSpellImmunitiesTo(target, &m_spellInfo->Effects[m_effIndex], apply);
}
/*********************************************************/
@ -6643,7 +6277,7 @@ void AuraEffect::HandlePeriodicDamageAurasTick(Unit* target, Unit* caster) const
if (!target->IsAlive())
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()) || target->IsTotem())
if (target->IsImmunedToDamage(caster, GetSpellInfo()) || target->IsTotem())
{
SendTickImmune(target, caster);
return;
@ -6795,7 +6429,7 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
if (!target->IsAlive())
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()))
if (target->IsImmunedToDamage(caster, GetSpellInfo()))
{
SendTickImmune(target, caster);
return;
@ -6905,7 +6539,7 @@ void AuraEffect::HandlePeriodicHealthFunnelAuraTick(Unit* target, Unit* caster)
if (!caster || !caster->IsAlive() || !target->IsAlive())
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED))
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
{
SendTickImmune(target, caster);
return;
@ -6934,7 +6568,7 @@ void AuraEffect::HandlePeriodicHealAurasTick(Unit* target, Unit* caster) const
if (!target->IsAlive())
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED))
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
{
SendTickImmune(target, caster);
return;
@ -7073,7 +6707,7 @@ void AuraEffect::HandlePeriodicManaLeechAuraTick(Unit* target, Unit* caster) con
if (!caster || !caster->IsAlive() || !target->IsAlive() || !target->HasActivePowerType(PowerType))
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()))
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
{
SendTickImmune(target, caster);
return;
@ -7148,7 +6782,7 @@ void AuraEffect::HandleObsModPowerAuraTick(Unit* target, Unit* caster) const
if (!target->IsAlive() || !target->GetMaxPower(PowerType))
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED))
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
{
SendTickImmune(target, caster);
return;
@ -7181,7 +6815,7 @@ void AuraEffect::HandlePeriodicEnergizeAuraTick(Unit* target, Unit* caster) cons
if (!target->IsAlive() || !target->GetMaxPower(PowerType))
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED))
if (target->IsImmunedToAuraPeriodicTick(caster, GetSpellInfo()))
{
SendTickImmune(target, caster);
return;
@ -7212,7 +6846,7 @@ void AuraEffect::HandlePeriodicPowerBurnAuraTick(Unit* target, Unit* caster) con
if (!caster || !target->IsAlive() || !target->HasActivePowerType(PowerType))
return;
if (target->HasUnitState(UNIT_STATE_ISOLATED) || target->IsImmunedToDamageOrSchool(GetSpellInfo()))
if (target->IsImmunedToDamage(caster, GetSpellInfo()))
{
SendTickImmune(target, caster);
return;
@ -7306,7 +6940,8 @@ void AuraEffect::HandleProcTriggerDamageAuraProc(AuraApplication* aurApp, ProcEv
Unit* triggerTarget = target == eventInfo.GetActor() ? eventInfo.GetActionTarget() : eventInfo.GetActor();
if (!triggerTarget)
return;
if (triggerTarget->HasUnitState(UNIT_STATE_ISOLATED) || triggerTarget->IsImmunedToDamageOrSchool(GetSpellInfo()))
if (triggerTarget->IsImmunedToDamage(target, GetSpellInfo()))
{
SendTickImmune(triggerTarget, target);
return;

View file

@ -552,7 +552,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
if (IsArea())
for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
{
if ((existing->second & (1 << effIndex)) && existing->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex))
if ((existing->second & (1 << effIndex)) && existing->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster()))
existing->second &= ~(1 << effIndex);
}
@ -594,7 +594,7 @@ void Aura::UpdateTargetMap(Unit* caster, bool apply)
// check target immunities
for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
{
if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex))
if ((itr->second & (1 << effIndex)) && itr->first->IsImmunedToSpellEffect(GetSpellInfo(), effIndex, GetCaster()))
itr->second &= ~(1 << effIndex);
}
if (!itr->second || itr->first->IsImmunedToSpell(GetSpellInfo()) || !CanBeAppliedOn(itr->first))
@ -2822,7 +2822,7 @@ void UnitAura::FillTargetMap(std::map<Unit*, uint8>& targets, Unit* caster)
{
float radius = GetSpellInfo()->Effects[effIndex].CalcRadius(caster);
if (!GetUnitOwner()->HasUnitState(UNIT_STATE_ISOLATED))
if (!GetUnitOwner()->HasAuraState(AURA_STATE_BANISHED, GetSpellInfo(), caster))
{
switch (GetSpellInfo()->Effects[effIndex].Effect)
{

View file

@ -1315,7 +1315,7 @@ void Spell::SelectImplicitAreaTargets(SpellEffIndex effIndex, SpellImplicitTarge
// Xinef: the distance should be increased by caster size, it is neglected in latter calculations
std::list<WorldObject*> targets;
float radius = m_spellInfo->Effects[effIndex].CalcRadius(m_caster) * m_spellValue->RadiusMod;
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions);
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), m_spellInfo->Effects[effIndex].ImplicitTargetConditions, Acore::WorldObjectSpellAreaTargetSearchReason::Area);
CallScriptObjectAreaTargetSelectHandlers(targets, effIndex, targetType);
@ -2091,12 +2091,12 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargetObjectTypes objec
return target;
}
void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList)
void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason)
{
uint32 containerTypeMask = GetSearcherTypeMask(objectType, condList);
if (!containerTypeMask)
return;
Acore::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList);
Acore::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList, searchReason);
Acore::WorldObjectListSearcher<Acore::WorldObjectSpellAreaTargetCheck> searcher(m_caster, targets, check, containerTypeMask);
SearchTargets<Acore::WorldObjectListSearcher<Acore::WorldObjectSpellAreaTargetCheck> > (searcher, containerTypeMask, m_caster, position, range);
}
@ -2142,7 +2142,7 @@ void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTar
WorldObject* chainSource = m_spellInfo->HasAttribute(SPELL_ATTR2_CHAIN_FROM_CASTER) ? m_caster : target;
std::list<WorldObject*> tempTargets;
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList);
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, condList, Acore::WorldObjectSpellAreaTargetSearchReason::Chain);
tempTargets.remove(target);
// remove targets which are always invalid for chain spells
@ -2310,7 +2310,7 @@ void Spell::AddUnitTarget(Unit* target, uint32 effectMask, bool checkIfValid /*=
// Check for effect immune skip if immuned
for (uint32 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
if (target->IsImmunedToSpellEffect(m_spellInfo, effIndex))
if (target->IsImmunedToSpellEffect(m_spellInfo, effIndex, m_caster))
effectMask &= ~(1 << effIndex);
ObjectGuid targetGUID = target->GetGUID();
@ -2777,83 +2777,93 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target)
// Fill base damage struct (unitTarget - is real spell target)
SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo, m_spellSchoolMask);
// Add bonuses and fill damageInfo struct
// Dancing Rune Weapon...
if (m_caster->GetEntry() == 27893)
// Check damage immunity
if (unitTarget->IsImmunedToDamage(caster, m_spellInfo))
{
if (Unit* owner = m_caster->GetOwner())
owner->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
m_damage = 0;
// no packet found in sniffs
}
else
caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
// xinef: override miss info after absorb / block calculations
if (missInfo == SPELL_MISS_NONE && damageInfo.damage == 0)
{
//if (damageInfo.absorb > 0)
// missInfo = SPELL_MISS_ABSORB;
if (damageInfo.blocked)
missInfo = SPELL_MISS_BLOCK;
}
// Xinef: override with forced crit, only visual result
if (GetSpellValue()->ForcedCritResult)
{
damageInfo.HitInfo |= SPELL_HIT_TYPE_CRIT;
}
Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
// xinef: health leech handling
if (m_spellInfo->HasEffect(SPELL_EFFECT_HEALTH_LEECH))
{
uint8 effIndex = EFFECT_0;
for (; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_HEALTH_LEECH)
break;
float healMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this);
// get max possible damage, don't count overkill for heal
uint32 healthGain = uint32(-unitTarget->GetHealthGain(-int32(damageInfo.damage)) * healMultiplier);
if (m_caster->IsAlive())
// Add bonuses and fill damageInfo struct
// Dancing Rune Weapon...
if (m_caster->GetEntry() == 27893)
{
healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL, effIndex);
healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL);
HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellInfo->GetSchoolMask());
m_caster->HealBySpell(healInfo);
if (Unit* owner = m_caster->GetOwner())
owner->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
}
else
caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
// xinef: override miss info after absorb / block calculations
if (missInfo == SPELL_MISS_NONE && damageInfo.damage == 0)
{
//if (damageInfo.absorb > 0)
// missInfo = SPELL_MISS_ABSORB;
if (damageInfo.blocked)
missInfo = SPELL_MISS_BLOCK;
}
// Xinef: override with forced crit, only visual result
if (GetSpellValue()->ForcedCritResult)
{
damageInfo.HitInfo |= SPELL_HIT_TYPE_CRIT;
}
Unit::DealDamageMods(damageInfo.target, damageInfo.damage, &damageInfo.absorb);
// xinef: health leech handling
if (m_spellInfo->HasEffect(SPELL_EFFECT_HEALTH_LEECH))
{
uint8 effIndex = EFFECT_0;
for (; effIndex < MAX_SPELL_EFFECTS; ++effIndex)
if (m_spellInfo->Effects[effIndex].Effect == SPELL_EFFECT_HEALTH_LEECH)
break;
float healMultiplier = m_spellInfo->Effects[effIndex].CalcValueMultiplier(m_originalCaster, this);
// get max possible damage, don't count overkill for heal
uint32 healthGain = uint32(-unitTarget->GetHealthGain(-int32(damageInfo.damage)) * healMultiplier);
if (m_caster->IsAlive())
{
healthGain = m_caster->SpellHealingBonusDone(m_caster, m_spellInfo, healthGain, HEAL, effIndex);
healthGain = m_caster->SpellHealingBonusTaken(m_caster, m_spellInfo, healthGain, HEAL);
HealInfo healInfo(m_caster, m_caster, healthGain, m_spellInfo, m_spellInfo->GetSchoolMask());
m_caster->HealBySpell(healInfo);
}
}
// Send log damage message to client
caster->SendSpellNonMeleeDamageLog(&damageInfo);
// Xinef: send info to target about reflect
if (reflectedSpell)
effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit);
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
caster->DealSpellDamage(&damageInfo, true, this);
// do procs after damage, eg healing effects
// no need to check if target is alive, done in procdamageandspell
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
if (canEffectTrigger)
{
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, missInfo);
uint32 hitMask = m_procEx | dmgInfo.GetHitMask();
Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
if (caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, dmgInfo.GetHitMask());
}
m_damage = damageInfo.damage;
}
// Send log damage message to client
caster->SendSpellNonMeleeDamageLog(&damageInfo);
// Xinef: send info to target about reflect
if (reflectedSpell)
effectUnit->SendSpellNonMeleeReflectLog(&damageInfo, effectUnit);
procVictim |= PROC_FLAG_TAKEN_DAMAGE;
caster->DealSpellDamage(&damageInfo, true, this);
// do procs after damage, eg healing effects
// no need to check if target is alive, done in procdamageandspell
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
if (canEffectTrigger)
{
DamageInfo dmgInfo(damageInfo, SPELL_DIRECT_DAMAGE, m_attackType, missInfo);
uint32 hitMask = m_procEx | dmgInfo.GetHitMask();
Unit::ProcSkillsAndAuras(caster, unitTarget, procAttacker, procVictim, hitMask, damageInfo.damage, m_attackType, m_spellInfo, m_triggeredByAuraSpell.spellInfo,
m_triggeredByAuraSpell.effectIndex, this, &dmgInfo);
if (caster->IsPlayer() && m_spellInfo->HasAttribute(SPELL_ATTR0_CANCELS_AUTO_ATTACK_COMBAT) == 0 &&
m_spellInfo->HasAttribute(SPELL_ATTR4_SUPPRESS_WEAPON_PROCS) == 0 && (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
caster->ToPlayer()->CastItemCombatSpell(unitTarget, m_attackType, procVictim, dmgInfo.GetHitMask());
}
m_damage = damageInfo.damage;
}
// Passive spell hits/misses or active spells only misses (only triggers)
else
@ -2939,7 +2949,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(this)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this)))
if (m_spellInfo->Speed && ((m_damage > 0 && unit->IsImmunedToDamage(m_caster, m_spellInfo)) || unit->IsImmunedToSchool(this) || unit->IsImmunedToSpell(m_spellInfo, this)))
{
return SPELL_MISS_IMMUNE;
}
@ -2950,7 +2960,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
{
if (effectMask & (1 << effectNumber))
{
if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber))
if (unit->IsImmunedToSpellEffect(m_spellInfo, effectNumber, m_caster))
effectMask &= ~(1 << effectNumber);
// Xinef: Buggs out polymorph
// Xinef: And this is checked in MagicSpellHitResult, why we check resistance twice?
@ -3096,14 +3106,6 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA
if (m_spellAura)
{
// Prevent aura application if target is banished and immuned
if (m_targets.GetUnitTarget() && m_targets.GetUnitTarget()->IsImmunedToDamageOrSchool(m_spellAura->GetSpellInfo())
&& m_targets.GetUnitTarget()->HasUnitState(UNIT_STATE_ISOLATED))
{
m_spellAura->Remove();
return SPELL_MISS_IMMUNE;
}
// Set aura stack amount to desired value
if (m_spellValue->AuraStackAmount > 1)
{
@ -5606,7 +5608,7 @@ void Spell::HandleEffects(Unit* pUnitTarget, Item* pItemTarget, GameObject* pGOT
}
}
SpellCastResult Spell::CheckCast(bool strict)
SpellCastResult Spell::CheckCast(bool strict, uint32* /*param1*/, uint32* /*param2*/)
{
// check death state
if (!m_caster->IsAlive() && !m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE) && !(m_spellInfo->HasAttribute(SPELL_ATTR0_ALLOW_CAST_WHILE_DEAD) || (IsTriggered() && !m_triggeredByAuraSpell)))
@ -6015,10 +6017,9 @@ SpellCastResult Spell::CheckCast(bool strict)
// xinef: Enraged Regeneration: While this is active, the warrior is blocked from using abilities that trigger being enraged (which would do nothing and waste the cooldowns).
if (m_spellInfo->Mechanic && m_spellInfo->IsSelfCast())
{
SpellImmuneList const& mechanicList = m_caster->m_spellImmune[IMMUNITY_MECHANIC];
for (SpellImmuneList::const_iterator itr = mechanicList.begin(); itr != mechanicList.end(); ++itr)
if (itr->type == m_spellInfo->Mechanic)
return SPELL_FAILED_DAMAGE_IMMUNE;
auto const& mechanicList = m_caster->m_spellImmune[IMMUNITY_MECHANIC];
if (mechanicList.count(m_spellInfo->Mechanic) > 0)
return SPELL_FAILED_DAMAGE_IMMUNE;
}
}
@ -7158,7 +7159,7 @@ SpellCastResult Spell::CheckPower()
return SPELL_CAST_OK;
}
SpellCastResult Spell::CheckItems()
SpellCastResult Spell::CheckItems(uint32* param1, uint32* param2)
{
Player* player = m_caster->ToPlayer();
if (!player)
@ -7566,21 +7567,29 @@ SpellCastResult Spell::CheckItems()
}
case SPELL_EFFECT_PROSPECTING:
{
if (!m_targets.GetItemTarget())
Item* item = m_targets.GetItemTarget();
if (!item)
return SPELL_FAILED_CANT_BE_PROSPECTED;
//ensure item is a prospectable ore
if (!(m_targets.GetItemTarget()->GetTemplate()->HasFlag(ITEM_FLAG_IS_PROSPECTABLE)))
if (!(item->GetTemplate()->HasFlag(ITEM_FLAG_IS_PROSPECTABLE)))
return SPELL_FAILED_CANT_BE_PROSPECTED;
//prevent prospecting in trade slot
if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID())
if (item->GetOwnerGUID() != m_caster->GetGUID())
return SPELL_FAILED_CANT_BE_PROSPECTED;
//Check for enough skill in jewelcrafting
uint32 item_prospectingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank;
uint32 item_prospectingskilllevel = item->GetTemplate()->RequiredSkillRank;
if (item_prospectingskilllevel > player->GetSkillValue(SKILL_JEWELCRAFTING))
return SPELL_FAILED_LOW_CASTLEVEL;
//make sure the player has the required ores in inventory
if (m_targets.GetItemTarget()->GetCount() < 5)
if (item->GetCount() < 5)
{
if (param1 && param2)
{
*param1 = item->GetEntry();
*param2 = 5;
}
return SPELL_FAILED_NEED_MORE_ITEMS;
}
if (!LootTemplates_Prospecting.HaveLootFor(m_targets.GetItemTargetEntry()))
return SPELL_FAILED_CANT_BE_PROSPECTED;
@ -7589,21 +7598,29 @@ SpellCastResult Spell::CheckItems()
}
case SPELL_EFFECT_MILLING:
{
if (!m_targets.GetItemTarget())
Item* item = m_targets.GetItemTarget();
if (!item)
return SPELL_FAILED_CANT_BE_MILLED;
//ensure item is a millable herb
if (!(m_targets.GetItemTarget()->GetTemplate()->HasFlag(ITEM_FLAG_IS_MILLABLE)))
if (!(item->GetTemplate()->HasFlag(ITEM_FLAG_IS_MILLABLE)))
return SPELL_FAILED_CANT_BE_MILLED;
//prevent milling in trade slot
if (m_targets.GetItemTarget()->GetOwnerGUID() != m_caster->GetGUID())
if (item->GetOwnerGUID() != m_caster->GetGUID())
return SPELL_FAILED_CANT_BE_MILLED;
//Check for enough skill in inscription
uint32 item_millingskilllevel = m_targets.GetItemTarget()->GetTemplate()->RequiredSkillRank;
uint32 item_millingskilllevel = item->GetTemplate()->RequiredSkillRank;
if (item_millingskilllevel > player->GetSkillValue(SKILL_INSCRIPTION))
return SPELL_FAILED_LOW_CASTLEVEL;
//make sure the player has the required herbs in inventory
if (m_targets.GetItemTarget()->GetCount() < 5)
if (item->GetCount() < 5)
{
if (param1 && param2)
{
*param1 = item->GetEntry();
*param2 = 5;
}
return SPELL_FAILED_NEED_MORE_ITEMS;
}
if (!LootTemplates_Milling.HaveLootFor(m_targets.GetItemTargetEntry()))
return SPELL_FAILED_CANT_BE_MILLED;
@ -9063,8 +9080,8 @@ namespace Acore
}
WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList)
: WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position)
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason)
: WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList), _range(range), _position(position), _searchReason(searchReason)
{
}
@ -9077,8 +9094,27 @@ namespace Acore
}
else if (!target->IsWithinDist3d(_position, _range))
return false;
else if (target->IsCreature() && target->ToCreature()->IsAvoidingAOE()) // pussywizard
return false;
else if (Creature* c = target->ToCreature())
{
if (c->IsAvoidingAOE()) // pussywizard
return false;
if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(c->GetCreatureTemplate()->CreatureImmunitiesId))
{
switch (_searchReason)
{
case Acore::WorldObjectSpellAreaTargetSearchReason::Area:
if (immunities->ImmuneAoE)
return false;
break;
case Acore::WorldObjectSpellAreaTargetSearchReason::Chain:
if (immunities->ImmuneChain)
return false;
break;
default:
break;
}
}
}
return WorldObjectSpellTargetCheck::operator ()(target);
}

View file

@ -38,6 +38,15 @@ class SpellEvent;
class ByteBuffer;
class BasicEvent;
namespace Acore
{
enum class WorldObjectSpellAreaTargetSearchReason
{
Area,
Chain
};
}
#define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILLISECONDS)
#define TRAJECTORY_MISSILE_SIZE 3.0f
@ -444,7 +453,7 @@ public:
template<class SEARCHER> void SearchTargets(SEARCHER& searcher, uint32 containerMask, Unit* referer, Position const* pos, float radius);
WorldObject* SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList = nullptr);
void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList);
void SearchAreaTargets(std::list<WorldObject*>& targets, float range, Position const* position, Unit* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason = Acore::WorldObjectSpellAreaTargetSearchReason::Area);
void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellTargetSelectionCategories selectCategory, ConditionList* condList, bool isChainHeal);
SpellCastResult prepare(SpellCastTargets const* targets, AuraEffect const* triggeredByAura = nullptr);
@ -460,7 +469,7 @@ public:
void TakeReagents();
void TakeCastItem();
SpellCastResult CheckCast(bool strict);
SpellCastResult CheckCast(bool strict, uint32* param1 = nullptr, uint32* param2 = nullptr);
SpellCastResult CheckPetCast(Unit* target);
// handlers
@ -472,7 +481,7 @@ public:
void OnSpellLaunch();
SpellCastResult CheckItems();
SpellCastResult CheckItems(uint32* param1 = nullptr, uint32* param2 = nullptr);
SpellCastResult CheckSpellFocus();
SpellCastResult CheckRange(bool strict);
SpellCastResult CheckPower();
@ -827,8 +836,9 @@ namespace Acore
{
float _range;
Position const* _position;
Acore::WorldObjectSpellAreaTargetSearchReason _searchReason;
WorldObjectSpellAreaTargetCheck(float range, Position const* position, Unit* caster,
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList);
Unit* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionList* condList, Acore::WorldObjectSpellAreaTargetSearchReason searchReason = Acore::WorldObjectSpellAreaTargetSearchReason::Area);
bool operator()(WorldObject* target);
};

View file

@ -856,7 +856,7 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
SpellInfo const* spell = iter->second->GetBase()->GetSpellInfo();
// Pounce Bleed shouldn't be removed by Cloak of Shadows.
if (spell->GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED)
if (spell->GetAllEffectsMechanicMask() & (UI64LIT(1) << MECHANIC_BLEED))
return;
bool dmgClassNone = false;
@ -1889,7 +1889,7 @@ void Spell::EffectEnergize(SpellEffIndex effIndex)
if (!unitTarget->IsAlive())
return;
if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED))
if (unitTarget->IsImmunedToAuraPeriodicTick(m_caster, m_spellInfo))
{
m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id);
return;
@ -3681,7 +3681,7 @@ void Spell::EffectHealMaxHealth(SpellEffIndex /*effIndex*/)
if (!unitTarget || !unitTarget->IsAlive())
return;
if (unitTarget->HasUnitState(UNIT_STATE_ISOLATED))
if (unitTarget->IsImmunedToAuraPeriodicTick(m_caster, m_spellInfo))
{
m_caster->SendSpellDamageImmune(unitTarget, GetSpellInfo()->Id);
return;
@ -5152,7 +5152,7 @@ void Spell::EffectDispelMechanic(SpellEffIndex effIndex)
continue;
if (roll_chance_i(aura->CalcDispelChance(unitTarget, !unitTarget->IsFriendlyTo(m_caster))))
{
if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (1 << mechanic)))
if ((aura->GetSpellInfo()->GetAllEffectsMechanicMask() & (UI64LIT(1) << mechanic)))
{
dispel_list.push(std::make_pair(aura->GetId(), aura->GetCasterGUID()));

View file

@ -605,6 +605,14 @@ SpellTargetObjectTypes SpellEffectInfo::GetUsedTargetObjectType() const
return _data[Effect].UsedTargetObjectType;
}
ImmunityInfo const* SpellEffectInfo::GetImmunityInfo() const
{
if (!_spellInfo)
return nullptr;
return _spellInfo->GetImmunityInfo(EffectIndex);
}
std::array<SpellEffectInfo::StaticData, TOTAL_SPELL_EFFECTS> SpellEffectInfo::_data =
{ {
// implicit target type used target object type
@ -915,6 +923,29 @@ bool SpellInfo::HasAreaAuraEffect() const
return false;
}
bool SpellInfo::HasOnlyDamageEffects() const
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if (Effects[i].IsEffect())
{
switch (Effects[i].Effect)
{
case SPELL_EFFECT_SCHOOL_DAMAGE:
case SPELL_EFFECT_HEALTH_LEECH:
case SPELL_EFFECT_POWER_DRAIN:
case SPELL_EFFECT_POWER_BURN:
case SPELL_EFFECT_NORMALIZED_WEAPON_DMG:
case SPELL_EFFECT_WEAPON_PERCENT_DAMAGE:
continue;
default:
return false;
}
}
}
return true;
}
bool SpellInfo::IsExplicitDiscovery() const
{
return ((Effects[0].Effect == SPELL_EFFECT_CREATE_RANDOM_ITEM
@ -1328,54 +1359,25 @@ bool SpellInfo::IsAffectedBySpellMod(SpellModifier const* mod) const
return IsAffected(affectSpell->SpellFamilyName, mod->mask);
}
bool SpellInfo::CanPierceImmuneAura(SpellInfo const* aura) const
bool SpellInfo::CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const
{
// aura can't be pierced
if (!aura || aura->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
{
return false;
}
// these spells pierce all avalible spells (Resurrection Sickness for example)
if (HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
// Dispels other auras on immunity, check if this spell makes the unit immune to aura
if (HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT) && CanSpellProvideImmunityAgainstAura(auraSpellInfo))
return true;
// these spells (Cyclone for example) can pierce all...
if (HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) || HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
if (aura->Mechanic != MECHANIC_IMMUNE_SHIELD &&
aura->Mechanic != MECHANIC_INVULNERABILITY &&
aura->Mechanic != MECHANIC_BANISH)
{
return true;
}
}
return false;
}
bool SpellInfo::CanDispelAura(SpellInfo const* aura) const
bool SpellInfo::CanDispelAura(SpellInfo const* auraSpellInfo) const
{
// Xinef: Passive auras cannot be dispelled
if (aura->IsPassive())
if (auraSpellInfo->IsPassive())
return false;
// These auras (like Divine Shield) can't be dispelled
if (aura->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
if (auraSpellInfo->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
return false;
// These spells (like Mass Dispel) can dispell all auras
if (HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
return true;
// These auras (Cyclone for example) are not dispelable
if ((aura->HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS) && aura->Mechanic != MECHANIC_NONE)
|| aura->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
return false;
}
return true;
}
@ -1666,6 +1668,12 @@ SpellCastResult SpellInfo::CheckTarget(Unit const* caster, WorldObject const* ta
if (AttributesEx & SPELL_ATTR1_ONLY_PEACEFUL_TARGETS && (unitTarget->IsInCombat() || unitTarget->IsPetInCombat()))
return SPELL_FAILED_TARGET_AFFECTING_COMBAT;
if (HasAttribute(SPELL_ATTR3_NOT_ON_AOE_IMMUNE))
if (auto creature = unitTarget->ToCreature())
if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(creature->GetCreatureTemplate()->CreatureImmunitiesId))
if (immunities->ImmuneAoE)
return SPELL_FAILED_BAD_TARGETS;
// only spells with SPELL_ATTR3_ONLY_ON_GHOSTS can target ghosts
if (IsRequiringDeadTarget())
{
@ -1888,35 +1896,35 @@ SpellSchoolMask SpellInfo::GetSchoolMask() const
return SpellSchoolMask(SchoolMask);
}
uint32 SpellInfo::GetAllEffectsMechanicMask() const
uint64 SpellInfo::GetAllEffectsMechanicMask() const
{
uint32 mask = 0;
uint64 mask = 0;
if (Mechanic)
mask |= 1 << Mechanic;
mask |= UI64LIT(1) << Mechanic;
for (int i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (Effects[i].IsEffect() && Effects[i].Mechanic)
mask |= 1 << Effects[i].Mechanic;
mask |= UI64LIT(1) << Effects[i].Mechanic;
return mask;
}
uint32 SpellInfo::GetEffectMechanicMask(uint8 effIndex) const
uint64 SpellInfo::GetEffectMechanicMask(uint8 effIndex) const
{
uint32 mask = 0;
uint64 mask = 0;
if (Mechanic)
mask |= 1 << Mechanic;
mask |= UI64LIT(1) << Mechanic;
if (Effects[effIndex].IsEffect() && Effects[effIndex].Mechanic)
mask |= 1 << Effects[effIndex].Mechanic;
mask |= UI64LIT(1) << Effects[effIndex].Mechanic;
return mask;
}
uint32 SpellInfo::GetSpellMechanicMaskByEffectMask(uint32 effectMask) const
uint64 SpellInfo::GetSpellMechanicMaskByEffectMask(uint32 effectMask) const
{
uint32 mask = 0;
uint64 mask = 0;
if (Mechanic)
mask |= 1 << Mechanic;
mask |= UI64LIT(1) << Mechanic;
for (int i = 0; i < MAX_SPELL_EFFECTS; ++i)
if ((effectMask & (1 << i)) && Effects[i].Mechanic)
mask |= 1 << Effects[i].Mechanic;
mask |= UI64LIT(1) << Effects[i].Mechanic;
return mask;
}
@ -2000,9 +2008,13 @@ AuraStateType SpellInfo::LoadAuraState() const
return AURA_STATE_ENRAGE;
// Bleeding aura state
if (GetAllEffectsMechanicMask() & 1 << MECHANIC_BLEED)
if (GetAllEffectsMechanicMask() & (UI64LIT(1) << MECHANIC_BLEED))
return AURA_STATE_BLEEDING;
// Banished aura state
if (Mechanic == MECHANIC_BANISH)
return AURA_STATE_BANISHED;
if (GetSchoolMask() & SPELL_SCHOOL_MASK_FROST)
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (Effects[i].IsAura() && (Effects[i].ApplyAuraName == SPELL_AURA_MOD_STUN
@ -2213,6 +2225,492 @@ SpellSpecificType SpellInfo::LoadSpellSpecific() const
return SPELL_SPECIFIC_NORMAL;
}
// immunity helper functions moved from SpellAuraEffects and global switches
void SpellInfo::_LoadImmunityInfo()
{
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
SpellEffectInfo& effectInfo = Effects[i];
if (!effectInfo.Effect)
continue;
uint32 schoolImmunityMask = 0;
uint32 applyHarmfulAuraImmunityMask = 0;
uint64 mechanicImmunityMask = 0;
uint32 dispelImmunityMask = 0;
uint32 damageImmunityMask = 0;
int32 miscVal = effectInfo.MiscValue;
int32 amount = effectInfo.CalcValue();
ImmunityInfo& immuneInfo = _immunityInfo[i];
switch (effectInfo.ApplyAuraName)
{
case SPELL_AURA_MECHANIC_IMMUNITY_MASK:
{
switch (miscVal)
{
case 27:
mechanicImmunityMask |= (1ULL << MECHANIC_SILENCE);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_SILENCE);
break;
case 96:
case 1615:
{
if (amount)
{
mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT)
| (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN)
| (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM)
| (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR)
| (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED)
| (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
}
break;
}
case 679:
{
if (Id == 57742)
{
mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT)
| (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN)
| (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM)
| (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR)
| (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED)
| (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR);
}
break;
}
case 1557:
{
if (Id == 64187)
{
mechanicImmunityMask |= (1ULL << MECHANIC_STUN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
}
else
{
mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT)
| (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN)
| (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM)
| (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR)
| (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED)
| (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
}
break;
}
case 1614:
case 1694:
{
immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT);
break;
}
case 1630:
{
if (Id == 64112)
{
immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_ATTACK_ME);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_TAUNT);
}
else
{
mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT)
| (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN)
| (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM)
| (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR)
| (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED)
| (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
}
break;
}
case 477:
case 1733:
{
if (!amount)
{
mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT)
| (1ULL << MECHANIC_FEAR) | (1ULL << MECHANIC_STUN)
| (1ULL << MECHANIC_SLEEP) | (1ULL << MECHANIC_CHARM)
| (1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_HORROR)
| (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_DISORIENTED)
| (1ULL << MECHANIC_FREEZE) | (1ULL << MECHANIC_TURN);
immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK);
immuneInfo.SpellEffectImmune.insert(SPELL_EFFECT_KNOCK_BACK_DEST);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
}
break;
}
case 878:
{
if (Id == 66092)
{
mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_STUN)
| (1ULL << MECHANIC_DISORIENTED) | (1ULL << MECHANIC_FREEZE);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
}
break;
}
default:
break;
}
if (immuneInfo.AuraTypeImmune.empty())
{
if (miscVal & (1 << 10))
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_STUN);
if (miscVal & (1 << 1))
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_TRANSFORM);
if (miscVal & (1 << 6))
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
if (miscVal & (1 << 0))
{
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
}
if (miscVal & (1 << 2))
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_CONFUSE);
if (miscVal & (1 << 9))
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_FEAR);
if (miscVal & (1 << 7))
immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DISARM);
}
break;
}
case SPELL_AURA_MECHANIC_IMMUNITY:
{
switch (Id)
{
case 34471: // The Beast Within
case 19574: // Bestial Wrath
case 42292: // PvP trinket
case 46227: // Medallion of Immunity
case 59752: // Every Man for Himself
case 53490: // Bullheaded
case 65547: // PvP Trinket
case 134946: // Supremacy of the Alliance
case 134956: // Supremacy of the Horde
case 195710: // Honorable Medallion
case 208683: // Gladiator's Medallion
mechanicImmunityMask |= IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
break;
case 54508: // Demonic Empowerment
mechanicImmunityMask |= (1ULL << MECHANIC_SNARE) | (1ULL << MECHANIC_ROOT) | (1ULL << MECHANIC_STUN);
break;
default:
if (miscVal < 1)
break;
mechanicImmunityMask |= 1ULL << miscVal;
break;
}
break;
}
case SPELL_AURA_EFFECT_IMMUNITY:
{
immuneInfo.SpellEffectImmune.insert(static_cast<SpellEffects>(miscVal));
break;
}
case SPELL_AURA_STATE_IMMUNITY:
{
immuneInfo.AuraTypeImmune.insert(static_cast<AuraType>(miscVal));
break;
}
case SPELL_AURA_SCHOOL_IMMUNITY:
{
schoolImmunityMask |= uint32(miscVal);
break;
}
case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL:
{
applyHarmfulAuraImmunityMask |= uint32(miscVal);
break;
}
case SPELL_AURA_DAMAGE_IMMUNITY:
{
damageImmunityMask |= uint32(miscVal);
break;
}
case SPELL_AURA_DISPEL_IMMUNITY:
{
dispelImmunityMask = uint32(miscVal);
break;
}
default:
break;
}
immuneInfo.SchoolImmuneMask = schoolImmunityMask;
immuneInfo.ApplyHarmfulAuraImmuneMask = applyHarmfulAuraImmunityMask;
immuneInfo.MechanicImmuneMask = mechanicImmunityMask;
immuneInfo.DispelImmuneMask = dispelImmunityMask;
immuneInfo.DamageSchoolMask = damageImmunityMask;
}
}
void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const* effect, bool apply) const
{
ImmunityInfo const* immuneInfo = nullptr;
if (effect)
{
uint8 effIndex = effect->EffectIndex;
if (effIndex < MAX_SPELL_EFFECTS)
immuneInfo = &_immunityInfo[effIndex];
}
if (!immuneInfo)
return;
if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask)
{
target->ApplySpellImmune(Id, IMMUNITY_SCHOOL, schoolImmunity, apply);
if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
{
target->RemoveAppliedAuras([this, target, schoolImmunity](AuraApplication const* aurApp) -> bool
{
SpellInfo const* auraSpellInfo = aurApp->GetBase()->GetSpellInfo();
if (auraSpellInfo->Id == Id) // Don't remove self
return false;
if (auraSpellInfo->IsPassive()) // Don't remove passive auras
return false;
if (!(auraSpellInfo->GetSchoolMask() & schoolImmunity)) // Check for school mask
return false;
if (!CanDispelAura(auraSpellInfo))
return false;
if (!HasAttribute(SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS))
{
Unit* existingAuraCaster = aurApp->GetBase()->GetCaster();
if (existingAuraCaster && existingAuraCaster->IsFriendlyTo(target)) // Don't remove friendly auras
return false;
}
return true;
});
}
}
if (uint64 mechanicImmunity = immuneInfo->MechanicImmuneMask)
{
for (uint32 i = 0; i < MAX_MECHANIC; ++i)
if (mechanicImmunity & (1ULL << i))
target->ApplySpellImmune(Id, IMMUNITY_MECHANIC, i, apply);
if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
target->RemoveAurasWithMechanic(mechanicImmunity, AURA_REMOVE_BY_DEFAULT, Id);
}
if (uint32 dispelImmunity = immuneInfo->DispelImmuneMask)
{
target->ApplySpellImmune(Id, IMMUNITY_DISPEL, dispelImmunity, apply);
if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
{
target->RemoveAppliedAuras([dispelImmunity](AuraApplication const* aurApp) -> bool
{
SpellInfo const* spellInfo = aurApp->GetBase()->GetSpellInfo();
if (spellInfo->Dispel == dispelImmunity)
return true;
return false;
});
}
}
if (uint32 damageImmunity = immuneInfo->DamageSchoolMask)
target->ApplySpellImmune(Id, IMMUNITY_DAMAGE, damageImmunity, apply);
for (AuraType auraType : immuneInfo->AuraTypeImmune)
{
target->ApplySpellImmune(Id, IMMUNITY_STATE, auraType, apply);
if (apply && HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
{
target->RemoveAppliedAuras([this, auraType](AuraApplication const* aurApp) -> bool
{
Aura const* aura = aurApp->GetBase();
if (!aura->GetSpellInfo()->HasAura(auraType))
return false;
return CanDispelAura(aura->GetSpellInfo());
});
}
}
for (SpellEffects effectType : immuneInfo->SpellEffectImmune)
target->ApplySpellImmune(Id, IMMUNITY_EFFECT, effectType, apply);
}
bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInfo) const
{
if (!auraSpellInfo)
return false;
for (SpellEffectInfo const& effectInfo : Effects)
{
if (!effectInfo.Effect)
continue;
ImmunityInfo const* immuneInfo = effectInfo.GetImmunityInfo();
if (!immuneInfo)
continue;
if (!auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
if (uint32 schoolImmunity = immuneInfo->SchoolImmuneMask)
if ((auraSpellInfo->SchoolMask & schoolImmunity) != 0)
return true;
}
if (uint64 mechanicImmunity = immuneInfo->MechanicImmuneMask)
if ((mechanicImmunity & (1ULL << auraSpellInfo->Mechanic)) != 0)
return true;
if (uint32 dispelImmunity = immuneInfo->DispelImmuneMask)
if (auraSpellInfo->Dispel == dispelImmunity)
return true;
bool immuneToAllEffects = true;
for (SpellEffectInfo const& auraSpellEffectInfo : auraSpellInfo->Effects)
{
if (!auraSpellEffectInfo.Effect)
continue;
uint32 effectName = auraSpellEffectInfo.Effect;
if (!effectName)
continue;
if (immuneInfo->SpellEffectImmune.find(static_cast<SpellEffects>(effectName)) == immuneInfo->SpellEffectImmune.cend())
{
immuneToAllEffects = false;
break;
}
if (uint32 mechanic = auraSpellEffectInfo.Mechanic)
{
if (!(immuneInfo->MechanicImmuneMask & (1ULL << mechanic)))
{
immuneToAllEffects = false;
break;
}
}
if (uint32 auraName = auraSpellEffectInfo.ApplyAuraName)
{
bool isImmuneToAuraEffectApply = false;
if (immuneInfo->AuraTypeImmune.find(static_cast<AuraType>(auraName)) != immuneInfo->AuraTypeImmune.cend())
isImmuneToAuraEffectApply = true;
if (!isImmuneToAuraEffectApply && !auraSpellInfo->IsPositiveEffect(auraSpellEffectInfo.EffectIndex) &&
!auraSpellInfo->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES))
{
if (uint32 applyHarmfulAuraImmuneMask = immuneInfo->ApplyHarmfulAuraImmuneMask)
if ((auraSpellInfo->SchoolMask & applyHarmfulAuraImmuneMask) != 0)
isImmuneToAuraEffectApply = true;
}
if (!isImmuneToAuraEffectApply)
{
immuneToAllEffects = false;
break;
}
}
}
if (immuneToAllEffects)
return true;
}
return false;
}
// based on client sub_007FDFA0
bool SpellInfo::CanSpellCastOverrideAuraEffect(AuraEffect const* aurEff) const
{
if (!HasAttribute(SPELL_ATTR1_IMMUNITY_PURGES_EFFECT))
return false;
if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR0_NO_IMMUNITIES))
return false;
SpellEffectInfo const* aurEffInfo = &aurEff->GetSpellInfo()->Effects[aurEff->GetEffIndex()];
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
SpellEffectInfo const& effectInfo = Effects[i];
if (!effectInfo.Effect)
continue;
if (effectInfo.Effect != SPELL_EFFECT_APPLY_AURA)
continue;
uint32 const miscValue = static_cast<uint32>(effectInfo.MiscValue);
switch (effectInfo.ApplyAuraName)
{
case SPELL_AURA_STATE_IMMUNITY:
if (miscValue != aurEffInfo->ApplyAuraName)
continue;
break;
case SPELL_AURA_SCHOOL_IMMUNITY:
case SPELL_AURA_MOD_IMMUNE_AURA_APPLY_SCHOOL:
if (aurEff->GetSpellInfo()->HasAttribute(SPELL_ATTR2_NO_SCHOOL_IMMUNITIES) || !(aurEff->GetSpellInfo()->SchoolMask & miscValue))
continue;
break;
case SPELL_AURA_DISPEL_IMMUNITY:
if (miscValue != aurEff->GetSpellInfo()->Dispel)
continue;
break;
case SPELL_AURA_MECHANIC_IMMUNITY:
if (miscValue != aurEff->GetSpellInfo()->Mechanic)
{
if (miscValue != aurEffInfo->Mechanic)
continue;
}
break;
default:
continue;
}
return true;
}
return false;
}
float SpellInfo::GetMinRange(bool positive) const
{
if (!RangeEntry)

View file

@ -22,6 +22,7 @@
#include "Object.h"
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include <boost/container/flat_set.hpp>
#include "Util.h"
class Unit;
@ -213,6 +214,26 @@ enum SpellCustomAttributes
uint32 GetTargetFlagMask(SpellTargetObjectTypes objType);
struct SpellDiminishInfo
{
DiminishingGroup DiminishGroup = DIMINISHING_NONE;
DiminishingReturnsType DiminishReturnType = DRTYPE_NONE;
int32 DiminishMaxLevel = 3; // DIMINISHING_LEVEL_IMMUNE
int32 DiminishDurationLimit = 0;
};
struct AC_GAME_API ImmunityInfo
{
uint32 SchoolImmuneMask = 0;
uint32 ApplyHarmfulAuraImmuneMask = 0;
uint64 MechanicImmuneMask = 0;
uint32 DispelImmuneMask = 0;
uint32 DamageSchoolMask = 0;
boost::container::flat_set<AuraType> AuraTypeImmune;
boost::container::flat_set<SpellEffects> SpellEffectImmune;
};
class SpellImplicitTargetInfo
{
private:
@ -302,6 +323,8 @@ public:
SpellEffectImplicitTargetTypes GetImplicitTargetType() const;
SpellTargetObjectTypes GetUsedTargetObjectType() const;
[[nodiscard]] ImmunityInfo const* GetImmunityInfo() const;
private:
struct StaticData
{
@ -310,6 +333,7 @@ private:
};
static std::array<StaticData, TOTAL_SPELL_EFFECTS> _data;
};
class AC_GAME_API SpellInfo
@ -412,6 +436,7 @@ public:
bool HasAura(AuraType aura) const;
bool HasAnyAura() const;
bool HasAreaAuraEffect() const;
bool HasOnlyDamageEffects() const;
inline bool HasAttribute(SpellAttr0 attribute) const { return (Attributes & attribute) != 0; }
inline bool HasAttribute(SpellAttr1 attribute) const { return (AttributesEx & attribute) != 0; }
@ -473,8 +498,12 @@ public:
bool IsAffectedBySpellMods() const;
bool IsAffectedBySpellMod(SpellModifier const* mod) const;
bool CanPierceImmuneAura(SpellInfo const* aura) const;
bool CanDispelAura(SpellInfo const* aura) const;
bool CanPierceImmuneAura(SpellInfo const* auraSpellInfo) const;
bool CanDispelAura(SpellInfo const* auraSpellInfo) const;
void ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const* effect, bool apply) const;
bool CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInfo) const;
bool CanSpellCastOverrideAuraEffect(AuraEffect const* aurEff) const;
bool IsSingleTarget() const;
bool IsAuraExclusiveBySpecificWith(SpellInfo const* spellInfo) const;
@ -490,11 +519,12 @@ public:
bool ValidateAttribute6SpellDamageMods(Unit const* caster, const AuraEffect* auraEffect, bool isDot) const;
SpellSchoolMask GetSchoolMask() const;
uint32 GetAllEffectsMechanicMask() const;
uint32 GetEffectMechanicMask(uint8 effIndex) const;
uint32 GetSpellMechanicMaskByEffectMask(uint32 effectMask) const;
uint64 GetAllEffectsMechanicMask() const;
uint64 GetEffectMechanicMask(uint8 effIndex) const;
uint64 GetSpellMechanicMaskByEffectMask(uint32 effectMask) const;
Mechanics GetEffectMechanic(uint8 effIndex) const;
bool HasAnyEffectMechanic() const;
[[nodiscard]] ImmunityInfo const* GetImmunityInfo(uint8 effIndex) const { return effIndex < MAX_SPELL_EFFECTS ? &_immunityInfo[effIndex] : nullptr; }
uint32 GetDispelMask() const;
static uint32 GetDispelMask(DispelType type);
uint32 GetExplicitTargetMask() const;
@ -541,7 +571,13 @@ public:
// unloading helpers
void _UnloadImplicitTargetConditionLists();
private:
// immunity helpers
void _LoadImmunityInfo();
SpellDiminishInfo _diminishInfoNonTriggered;
SpellDiminishInfo _diminishInfoTriggered;
ImmunityInfo _immunityInfo[MAX_SPELL_EFFECTS];
std::array<SpellEffectInfo, MAX_SPELL_EFFECTS>& _GetEffects() { return Effects; }
SpellEffectInfo& _GetEffect(SpellEffIndex index) { ASSERT(index < Effects.size()); return Effects[index]; }
};

View file

@ -16,6 +16,7 @@
*/
#include "SpellMgr.h"
#include "Log.h"
#include "BattlefieldMgr.h"
#include "BattlegroundIC.h"
#include "Chat.h"
@ -29,6 +30,7 @@
#include "Spell.h"
#include "SpellAuraDefines.h"
#include "SpellInfo.h"
#include "Tokenize.h"
#include "World.h"
bool IsPrimaryProfessionSkill(uint32 skill)
@ -53,6 +55,53 @@ bool IsPartOfSkillLine(uint32 skillId, uint32 spellId)
return false;
}
CreatureImmunities const* SpellMgr::GetCreatureImmunities(int32 creatureImmunitiesId) const
{
return Acore::Containers::MapGetValuePtr(mCreatureImmunities, creatureImmunitiesId);
}
void SpellMgr::LoadCreatureImmunities()
{
mCreatureImmunities.clear();
if (QueryResult result = WorldDatabase.Query("SELECT ID, SchoolMask, DispelTypeMask, MechanicsMask, Effects, Auras, ImmuneAoE, ImmuneChain FROM creature_immunities"))
{
do
{
Field* fields = result->Fetch();
int32 id = fields[0].Get<int32>();
uint8 school = fields[1].Get<uint8>();
uint16 dispelType = fields[2].Get<uint16>();
uint64 mechanics = fields[3].Get<uint64>();
CreatureImmunities& immunities = mCreatureImmunities[id];
immunities.School = school;
immunities.DispelType = dispelType;
immunities.Mechanic = mechanics;
immunities.ImmuneAoE = fields[6].Get<bool>();
immunities.ImmuneChain = fields[7].Get<bool>();
{
std::string effects = fields[4].Get<std::string>();
for (std::string_view token : Acore::Tokenize(effects, ',', false))
{
if (Optional<uint32> val = Acore::StringTo<uint32>(token); val && *val < uint32(TOTAL_SPELL_EFFECTS))
immunities.Effect.push_back(SpellEffects(*val));
else
LOG_ERROR("sql.sql", "Invalid effect type in `Effects` {} for creature immunities {}, skipped", token, id);
}
}
{
std::string auras = fields[5].Get<std::string>();
for (std::string_view token : Acore::Tokenize(auras, ',', false))
{
if (Optional<uint32> val = Acore::StringTo<uint32>(token); val && *val < TOTAL_AURAS)
immunities.Aura.push_back(AuraType(*val));
else
LOG_ERROR("sql.sql", "Invalid aura type in `Auras` {} for creature immunities {}, skipped", token, id);
}
}
} while (result->NextRow());
}
}
DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto, bool triggered)
{
if (spellproto->IsPositive())
@ -212,29 +261,29 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellInfo const* spellproto,
}
// Lastly - Set diminishing depending on mechanic
uint32 mechanic = spellproto->GetAllEffectsMechanicMask();
if (mechanic & (1 << MECHANIC_CHARM))
uint64 mechanic = spellproto->GetAllEffectsMechanicMask();
if (mechanic & (1ULL << MECHANIC_CHARM))
return DIMINISHING_MIND_CONTROL;
if (mechanic & (1 << MECHANIC_SILENCE))
if (mechanic & (1ULL << MECHANIC_SILENCE))
return DIMINISHING_SILENCE;
if (mechanic & (1 << MECHANIC_SLEEP))
if (mechanic & (1ULL << MECHANIC_SLEEP))
return DIMINISHING_SLEEP;
if (mechanic & ((1 << MECHANIC_SAPPED) | (1 << MECHANIC_POLYMORPH) | (1 << MECHANIC_SHACKLE)))
if (mechanic & ((1ULL << MECHANIC_SAPPED) | (1ULL << MECHANIC_POLYMORPH) | (1ULL << MECHANIC_SHACKLE)))
return DIMINISHING_DISORIENT;
// Mechanic Knockout, except Blast Wave
if (mechanic & (1 << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292)
if (mechanic & (1ULL << MECHANIC_KNOCKOUT) && spellproto->SpellIconID != 292)
return DIMINISHING_DISORIENT;
if (mechanic & (1 << MECHANIC_DISARM))
if (mechanic & (1ULL << MECHANIC_DISARM))
return DIMINISHING_DISARM;
if (mechanic & (1 << MECHANIC_FEAR))
if (mechanic & (1ULL << MECHANIC_FEAR))
return DIMINISHING_FEAR;
if (mechanic & (1 << MECHANIC_STUN))
if (mechanic & (1ULL << MECHANIC_STUN))
return triggered ? DIMINISHING_STUN : DIMINISHING_CONTROLLED_STUN;
if (mechanic & (1 << MECHANIC_BANISH))
if (mechanic & (1ULL << MECHANIC_BANISH))
return DIMINISHING_BANISH;
if (mechanic & (1 << MECHANIC_ROOT))
if (mechanic & (1ULL << MECHANIC_ROOT))
return triggered ? DIMINISHING_ROOT : DIMINISHING_CONTROLLED_ROOT;
if (mechanic & (1 << MECHANIC_HORROR))
if (mechanic & (1ULL << MECHANIC_HORROR))
return DIMINISHING_HORROR;
return DIMINISHING_NONE;
@ -2876,6 +2925,8 @@ void SpellMgr::LoadSpellInfoStore()
}
}
LoadCreatureImmunities();
LOG_INFO("server.loading", ">> Loaded Spell Custom Attributes in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
@ -3714,3 +3765,17 @@ void SpellMgr::LoadSpellInfoCustomAttributes()
LOG_INFO("server.loading", ">> Loaded SpellInfo Custom Attributes in {} ms", GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
void SpellMgr::LoadSpellInfoImmunities()
{
uint32 oldMSTime = getMSTime();
for (SpellInfo* spellInfo : mSpellInfoMap)
{
if (!spellInfo)
continue;
spellInfo->_LoadImmunityInfo();
}
LOG_INFO("server.loading", ">> Loaded SpellInfo immunity infos in {} ms", GetMSTimeDiffToNow(oldMSTime));
}

View file

@ -25,6 +25,9 @@
#include "SharedDefines.h"
#include "Unit.h"
#include <vector>
#include <bitset>
class SpellInfo;
class Player;
class Unit;
@ -557,6 +560,18 @@ typedef std::multimap<uint32, uint32> SpellsRequiringSpellMap;
typedef std::pair<SpellsRequiringSpellMap::const_iterator, SpellsRequiringSpellMap::const_iterator> SpellsRequiringSpellMapBounds;
// Spell learning properties (accessed using SpellMgr functions)
struct CreatureImmunities
{
std::bitset<MAX_SPELL_SCHOOL> School;
std::bitset<DISPEL_MAX> DispelType;
std::bitset<MAX_MECHANIC> Mechanic;
std::vector<SpellEffects> Effect;
std::vector<AuraType> Aura;
bool ImmuneAoE = false;
bool ImmuneChain = false;
};
typedef std::unordered_map<int32, CreatureImmunities> CreatureImmunitiesMap;
struct SpellLearnSkillNode
{
uint16 skill;
@ -634,6 +649,9 @@ private:
public:
static SpellMgr* instance();
// creature immunity definitions loaded from DB
CreatureImmunities const* GetCreatureImmunities(int32 creatureImmunitiesId) const;
// Spell correctness for client using
static bool ComputeIsSpellValid(SpellInfo const* spellInfo, bool msg = true);
static bool IsSpellValid(SpellInfo const* spellInfo);
@ -772,10 +790,12 @@ public:
void LoadPetDefaultSpells();
void LoadSpellAreas();
void LoadSpellInfoStore();
void LoadCreatureImmunities();
void LoadSpellCooldownOverrides();
void UnloadSpellInfoStore();
void UnloadSpellInfoImplicitTargetConditionLists();
void LoadSpellInfoCustomAttributes();
void LoadSpellInfoImmunities();
void LoadSpellInfoCorrections();
void LoadSpellSpecificAndAuraState();
void LoadSpellJumpDistances();
@ -792,6 +812,7 @@ private:
SpellGroupStackMap mSpellGroupStack;
SameEffectStackMap mSpellSameEffectStack;
SpellProcMap mSpellProcMap;
CreatureImmunitiesMap mCreatureImmunities;
SpellBonusMap mSpellBonusMap;
SpellThreatMap mSpellThreatMap;
SpellMixologyMap mSpellMixologyMap;

View file

@ -436,6 +436,9 @@ void World::SetInitialWorldSettings()
LOG_INFO("server.loading", "Loading Spell Jump Distances...");
sSpellMgr->LoadSpellJumpDistances();
LOG_INFO("server.loading", "Loading SpellInfo Immunity infos...");
sSpellMgr->LoadSpellInfoImmunities();
LOG_INFO("server.loading", "Loading Player Totem models...");
sObjectMgr->LoadPlayerTotemModels();

View file

@ -655,8 +655,17 @@ public:
CreatureTemplate const* cInfo = target->GetCreatureTemplate();
uint32 faction = target->GetFaction();
uint32 npcflags = target->GetNpcFlags();
uint32 mechanicImmuneMask = cInfo->MechanicImmuneMask;
uint32 spellSchoolImmuneMask = cInfo->SpellSchoolImmuneMask;
uint64 mechanicImmuneMask = 0;
uint32 spellSchoolImmuneMask = 0;
if (CreatureImmunities const* immunities = sSpellMgr->GetCreatureImmunities(cInfo->CreatureImmunitiesId))
{
mechanicImmuneMask = immunities->Mechanic.to_ullong();
for (std::size_t j = 0; j < immunities->School.size(); ++j)
if (immunities->School[j])
spellSchoolImmuneMask |= (1u << j);
}
uint32 displayid = target->GetDisplayId();
uint32 nativeid = target->GetNativeDisplayId();
uint32 entry = target->GetEntry();
@ -673,6 +682,7 @@ public:
int64 curRespawnDelay = target->GetRespawnTimeEx() - GameTime::GetGameTime().count();
if (curRespawnDelay < 0)
curRespawnDelay = 0;
std::string curRespawnDelayStr = secsToTimeString(uint64(curRespawnDelay), true);
std::string defRespawnDelayStr = secsToTimeString(target->GetRespawnDelay(), true);
@ -689,31 +699,19 @@ public:
handler->PSendSysMessage(LANG_NPCINFO_POSITION, float(target->GetPositionX()), float(target->GetPositionY()), float(target->GetPositionZ()));
handler->PSendSysMessage(LANG_NPCINFO_AIINFO, target->GetAIName(), target->GetScriptName());
for (uint8 i = 0; i < NPCFLAG_COUNT; i++)
{
if (npcflags & npcFlagTexts[i].flag)
{
handler->PSendSysMessage(npcFlagTexts[i].text, npcFlagTexts[i].flag);
}
}
for (auto npcFlagText : npcFlagTexts)
if (npcflags & npcFlagText.flag)
handler->PSendSysMessage(npcFlagText.text, npcFlagText.flag);
handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, mechanicImmuneMask);
handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, Acore::StringFormat("0x{:X}", mechanicImmuneMask).c_str());
for (uint8 i = 1; i < MAX_MECHANIC; ++i)
{
if (mechanicImmuneMask & (1 << (mechanicImmunes[i].flag - 1)))
{
if (mechanicImmuneMask & (UI64LIT(1) << i))
handler->PSendSysMessage(mechanicImmunes[i].text, mechanicImmunes[i].flag);
}
}
handler->PSendSysMessage(LANG_NPCINFO_SPELL_SCHOOL_IMMUNE, spellSchoolImmuneMask);
for (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
{
if (spellSchoolImmuneMask & (1 << spellSchoolImmunes[i].flag))
{
handler->PSendSysMessage(spellSchoolImmunes[i].text, spellSchoolImmunes[i].flag);
}
}
for (auto spellSchoolImmune : spellSchoolImmunes)
if (spellSchoolImmuneMask & (1 << spellSchoolImmune.flag))
handler->PSendSysMessage(spellSchoolImmune.text, spellSchoolImmune.flag);
return true;
}

View file

@ -420,7 +420,7 @@ enum SpellAttr1 : uint32
SPELL_ATTR1_TOGGLE_FAR_SIGHT = 0x00002000, // TITLE Farsight aura (client only)
SPELL_ATTR1_TRACK_TARGET_IN_CHANNEL = 0x00004000, // TITLE Track target while channeling DESCRIPTION While channeling, adjust facing to face target
SPELL_ATTR1_IMMUNITY_PURGES_EFFECT = 0x00008000, // TITLE Immunity cancels preapplied auras DESCRIPTION For immunity spells, cancel all auras that this spell would make you immune to when the spell is applied
SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS = 0x00010000, // TITLE Unaffected by school immunities DESCRIPTION Will not pierce Divine Shield, Ice Block and other full invulnerabilities
SPELL_ATTR1_IMMUNITY_TO_HOSTILE_AND_FRIENDLY_EFFECTS = 0x00010000, // TITLE Immunity to Hostile & Friendly Effects DESCRIPTION Immunity applied by this aura will also be checked for friendly spells (school immunity only) - used by Cyclone for example to cause friendly spells and healing over time to be immune
SPELL_ATTR1_NO_AUTOCAST_AI = 0x00020000, // TITLE Cannot be autocast by pet DESCRIPTION (AI)
SPELL_ATTR1_PREVENTS_ANIM = 0x00040000, // TITLE NYI, auras apply UNIT_FLAG_PREVENT_EMOTES_FROM_CHAT_TEXT
SPELL_ATTR1_EXCLUDE_CASTER = 0x00080000, // TITLE Cannot be self-cast
@ -1285,7 +1285,7 @@ enum AuraStateType
//AURA_STATE_UNKNOWN6 = 6, // | not used
AURA_STATE_HUNTER_PARRY = 7, // C |
//AURA_STATE_UNKNOWN7 = 7, // c | creature cheap shot / focused bursts spells
//AURA_STATE_UNKNOWN8 = 8, // t| test spells
AURA_STATE_BANISHED = 8, // c t| banished
//AURA_STATE_UNKNOWN9 = 9, // |
AURA_STATE_WARRIOR_VICTORY_RUSH = 10, // C | warrior victory rush
//AURA_STATE_UNKNOWN11 = 11, // C t| 60348 - Maelstrom Ready!, test spells
@ -1342,17 +1342,34 @@ enum Mechanics : uint32
MECHANIC_IMMUNE_SHIELD = 29, // Divine (Blessing) Shield/Protection and Ice Block
MECHANIC_SAPPED = 30,
MECHANIC_ENRAGED = 31,
MAX_MECHANIC = 32 // SKIP
MECHANIC_WOUNDED = 32,
MECHANIC_INFECTED_2 = 33,
MECHANIC_INFECTED_3 = 34,
MECHANIC_INFECTED_4 = 35,
MECHANIC_TAUNTED = 36,
MAX_MECHANIC = 37 // SKIP
};
// Used for spell 42292 Immune Movement Impairment and Loss of Control (0x49967ca6)
#define IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK (\
(1<<MECHANIC_CHARM)|(1<<MECHANIC_DISORIENTED)|(1<<MECHANIC_FEAR)| \
(1<<MECHANIC_ROOT)|(1<<MECHANIC_SLEEP)|(1<<MECHANIC_SNARE)| \
(1<<MECHANIC_STUN)|(1<<MECHANIC_FREEZE)|(1<<MECHANIC_KNOCKOUT)| \
(1<<MECHANIC_POLYMORPH)|(1<<MECHANIC_BANISH)|(1<<MECHANIC_SHACKLE)| \
(1<<MECHANIC_TURN)|(1<<MECHANIC_HORROR)|(1<<MECHANIC_DAZE)| \
(1<<MECHANIC_SAPPED))
inline constexpr uint64 IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK =
(UI64LIT(1) << MECHANIC_CHARM) |
(UI64LIT(1) << MECHANIC_DISORIENTED) |
(UI64LIT(1) << MECHANIC_FEAR) |
(UI64LIT(1) << MECHANIC_ROOT) |
(UI64LIT(1) << MECHANIC_SLEEP) |
(UI64LIT(1) << MECHANIC_SNARE) |
(UI64LIT(1) << MECHANIC_STUN) |
(UI64LIT(1) << MECHANIC_FREEZE) |
(UI64LIT(1) << MECHANIC_SILENCE) |
(UI64LIT(1) << MECHANIC_DISARM) |
(UI64LIT(1) << MECHANIC_KNOCKOUT) |
(UI64LIT(1) << MECHANIC_POLYMORPH) |
(UI64LIT(1) << MECHANIC_BANISH) |
(UI64LIT(1) << MECHANIC_SHACKLE) |
(UI64LIT(1) << MECHANIC_TURN) |
(UI64LIT(1) << MECHANIC_HORROR) |
(UI64LIT(1) << MECHANIC_DAZE) |
(UI64LIT(1) << MECHANIC_SAPPED);
// Spell dispel type
enum DispelType
@ -1368,7 +1385,8 @@ enum DispelType
DISPEL_SPE_NPC_ONLY = 8,
DISPEL_ENRAGE = 9,
DISPEL_ZG_TICKET = 10,
DESPEL_OLD_UNUSED = 11
DESPEL_OLD_UNUSED = 11,
DISPEL_MAX
};
#define DISPEL_ALL_MASK ((1<<DISPEL_MAGIC) | (1<<DISPEL_CURSE) | (1<<DISPEL_DISEASE) | (1<<DISPEL_POISON))

View file

@ -60,7 +60,6 @@ void TestCreature::ForceInitValues(ObjectGuid::LowType guidLow, uint32 entry)
_fakeCreatureTemplate->speed_run = 1.14286f;
_fakeCreatureTemplate->speed_swim = 1.0f;
_fakeCreatureTemplate->speed_flight = 1.0f;
_fakeCreatureTemplate->scale = 1.0f;
_fakeCreatureTemplate->DamageModifier = 1.0f;
_fakeCreatureTemplate->BaseAttackTime = 2000;
_fakeCreatureTemplate->RangeAttackTime = 2000;

View file

@ -0,0 +1,330 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file SpellImmunityTest.cpp
* @brief Tests for spell immunity mechanics
*/
#include "gtest/gtest.h"
#include <array>
#include <cstdint>
#include "Unit.h" // needed for SpellSchoolMask and mask helper
namespace
{
enum EffectType : uint8_t
{
EFFECT_NONE,
EFFECT_SCHOOL_DAMAGE,
EFFECT_HEALTH_LEECH,
EFFECT_POWER_DRAIN,
EFFECT_POWER_BURN,
EFFECT_NORMALIZED_WEAPON_DMG,
EFFECT_WEAPON_PERCENT_DAMAGE,
EFFECT_APPLY_AURA,
EFFECT_DUMMY
};
enum AuraType : uint8_t
{
AURA_NONE,
AURA_MOD_DECREASE_SPEED,
AURA_PERIODIC_DAMAGE,
AURA_TRANSFORM,
AURA_MOD_STUN
};
struct EffectDesc
{
EffectType effect = EFFECT_NONE;
AuraType aura = AURA_NONE;
};
struct SpellDesc
{
std::array<EffectDesc, 3> effects{};
};
bool IsDamageEffect(EffectType effect)
{
switch (effect)
{
case EFFECT_SCHOOL_DAMAGE:
case EFFECT_HEALTH_LEECH:
case EFFECT_POWER_DRAIN:
case EFFECT_POWER_BURN:
case EFFECT_NORMALIZED_WEAPON_DMG:
case EFFECT_WEAPON_PERCENT_DAMAGE:
return true;
default:
return false;
}
}
bool HasOnlyDamageEffects(SpellDesc const& spell)
{
bool hasAny = false;
for (EffectDesc const& e : spell.effects)
{
if (e.effect == EFFECT_NONE)
continue;
hasAny = true;
if (!IsDamageEffect(e.effect))
return false;
}
return hasAny;
}
// Helper to classify spells which apply a stun aura
bool IsStunSpell(SpellDesc const& spell)
{
for (EffectDesc const& e : spell.effects)
{
if (e.effect == EFFECT_APPLY_AURA && e.aura == AURA_MOD_STUN)
return true;
}
return false;
}
// 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
// a spell like Lethargy.
SpellMissInfo ComputeSpellHitResult(bool isImmunedToSpell, bool isImmunedToDamage,
SpellDesc const& spell, bool immuneToStun = false)
{
// Mirrors current core ordering:
// 1) full spell immunity check
// 2) mechanic immunity check (e.g. bladestorm vs stun)
// 3) damage immunity only for damage-only spells
if (isImmunedToSpell)
return SPELL_MISS_IMMUNE;
if (immuneToStun && IsStunSpell(spell))
return SPELL_MISS_IMMUNE;
if (HasOnlyDamageEffects(spell) && isImmunedToDamage)
return SPELL_MISS_IMMUNE;
return SPELL_MISS_NONE;
}
struct EffectApplyResult
{
bool damageApplied = false;
bool slowApplied = false;
};
EffectApplyResult ApplyEffectsWithMovementImmunity(SpellDesc const& spell, bool immuneToMovementImpairing)
{
EffectApplyResult result;
for (EffectDesc const& e : spell.effects)
{
if (e.effect == EFFECT_NONE)
continue;
if (IsDamageEffect(e.effect))
result.damageApplied = true;
if (e.effect == EFFECT_APPLY_AURA && e.aura == AURA_MOD_DECREASE_SPEED)
{
if (!immuneToMovementImpairing)
result.slowApplied = true;
}
}
return result;
}
SpellDesc MakeDamageOnlySpell()
{
SpellDesc spell;
spell.effects[0] = { EFFECT_SCHOOL_DAMAGE, AURA_NONE };
return spell;
}
SpellDesc MakeFrostboltLikeSpell()
{
SpellDesc spell;
spell.effects[0] = { EFFECT_SCHOOL_DAMAGE, AURA_NONE };
spell.effects[1] = { EFFECT_APPLY_AURA, AURA_MOD_DECREASE_SPEED };
return spell;
}
SpellDesc MakeCycloneLikeSpell()
{
SpellDesc spell;
spell.effects[0] = { EFFECT_APPLY_AURA, AURA_TRANSFORM };
return spell;
}
SpellDesc MakeSlowOnlySpell()
{
// represents effects such as Frost Trap or Desecration, which only slow
SpellDesc spell;
spell.effects[0] = { EFFECT_APPLY_AURA, AURA_MOD_DECREASE_SPEED };
return spell;
}
}
TEST(SpellImmunityTest, HasOnlyDamageEffects_TrueForPureDamage)
{
SpellDesc spell = MakeDamageOnlySpell();
EXPECT_TRUE(HasOnlyDamageEffects(spell));
}
TEST(SpellImmunityTest, HasOnlyDamageEffects_FalseForDamagePlusAura)
{
SpellDesc spell = MakeFrostboltLikeSpell();
EXPECT_FALSE(HasOnlyDamageEffects(spell));
}
TEST(SpellImmunityTest, SpellImmunity_BlocksAllSpells)
{
SpellDesc damageOnly = MakeDamageOnlySpell();
SpellDesc cycloneLike = MakeCycloneLikeSpell();
EXPECT_EQ(ComputeSpellHitResult(true, false, damageOnly), SPELL_MISS_IMMUNE);
EXPECT_EQ(ComputeSpellHitResult(true, false, cycloneLike), SPELL_MISS_IMMUNE);
}
TEST(SpellImmunityTest, DamageImmunity_BlocksDamageOnlySpell)
{
SpellDesc damageOnly = MakeDamageOnlySpell();
EXPECT_EQ(ComputeSpellHitResult(false, true, damageOnly), SPELL_MISS_IMMUNE);
}
// Specific case for spell ID 16621 "Self Invulnerability".
// This aura grants immunity to melee/physical damage only. A rogue
// using Sinister Strike (a physical melee attack) should be completely
// blocked by the effect. The test uses the same simplified damage-only
// spell description as above but documents the physical context.
TEST(SpellImmunityTest, SelfInvulnerability_BlocksMeleeDamage)
{
SpellDesc meleeAttack = MakeDamageOnlySpell();
// simulate physical damage coming from a rogue melee ability
EXPECT_EQ(ComputeSpellHitResult(false, true, meleeAttack), SPELL_MISS_IMMUNE);
}
TEST(SpellImmunityTest, DamageImmunity_DoesNotMissMixedSpell)
{
// This is the key fix: damage immunity must not force SPELL_MISS_IMMUNE
// for mixed spells that include non-damage effects.
SpellDesc frostboltLike = MakeFrostboltLikeSpell();
EXPECT_EQ(ComputeSpellHitResult(false, true, frostboltLike), SPELL_MISS_NONE);
}
TEST(SpellImmunityTest, HandOfFreedomStyle_MovementImmunity_AllowsDamageBlocksSlow)
{
SpellDesc frostboltLike = MakeFrostboltLikeSpell();
EffectApplyResult result = ApplyEffectsWithMovementImmunity(frostboltLike, true);
EXPECT_TRUE(result.damageApplied);
EXPECT_FALSE(result.slowApplied);
}
TEST(SpellImmunityTest, NoMovementImmunity_FrostboltStyle_AppliesDamageAndSlow)
{
SpellDesc frostboltLike = MakeFrostboltLikeSpell();
EffectApplyResult result = ApplyEffectsWithMovementImmunity(frostboltLike, false);
EXPECT_TRUE(result.damageApplied);
EXPECT_TRUE(result.slowApplied);
}
TEST(SpellImmunityTest, CycloneLikeSpell_DivineShieldStyle_Immune)
{
SpellDesc cycloneLike = MakeCycloneLikeSpell();
EXPECT_EQ(ComputeSpellHitResult(true, false, cycloneLike), SPELL_MISS_IMMUNE);
}
// Regression test for issue #10671:
// Divine Shield (full spell immunity) should block purely slowing spells
// such as Hunter Frost Trap or DK Desecration. Previously the effect was
// applied because the core only checked damage-only spells when deciding
// immunity based on damage or spell state.
TEST(SpellImmunityTest, SpellImmunity_BlocksSlowOnlySpell)
{
SpellDesc slowOnly = MakeSlowOnlySpell();
EXPECT_EQ(ComputeSpellHitResult(true, false, slowOnly), SPELL_MISS_IMMUNE);
}
// Ensure that damage-only immunity (e.g. from Hand of Protection) does not
// accidentally prevent slow-only spells. This covers the regression when
// Divine Shield was incorrectly modelled as damage immunity only.
TEST(SpellImmunityTest, DamageImmunity_DoesNotBlockSlowOnlySpell)
{
SpellDesc slowOnly = MakeSlowOnlySpell();
EXPECT_EQ(ComputeSpellHitResult(false, true, slowOnly), SPELL_MISS_NONE);
}
// New coverage for school-mask logic. These exercises the helper used by
// Unit::IsImmunedToDamage to ensure broad masks are handled correctly.
TEST(SpellImmunityTest, ImmunityMask_PartialOverlapDoesNotCount)
{
SpellSchoolMask immune = SPELL_SCHOOL_MASK_FROST;
SpellSchoolMask checkAll = SPELL_SCHOOL_MASK_ALL;
// a frost-only immunity should *not* make you immune to all damage
EXPECT_FALSE(Unit::IsImmuneMaskFully(immune, checkAll));
}
TEST(SpellImmunityTest, ImmunityMask_FullCoverageAccepted)
{
SpellSchoolMask immune = SPELL_SCHOOL_MASK_MAGIC; // holy+spell
SpellSchoolMask checkMagic = SPELL_SCHOOL_MASK_MAGIC;
SpellSchoolMask checkSpell = SPELL_SCHOOL_MASK_SPELL;
EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkMagic));
EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkSpell));
}
TEST(SpellImmunityTest, ImmunityMask_SupersetMatches)
{
SpellSchoolMask immune = SPELL_SCHOOL_MASK_ALL;
SpellSchoolMask checkMagic = SPELL_SCHOOL_MASK_MAGIC;
EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkMagic));
}
// Bladestorm grants a mechanic immunity mask which includes stuns (e.g.
// Lethargy 69133). The following test mirrors that behaviour by
// modelling a simple spell that applies a stun aura and exercising the
// new `immuneToStun` flag in ComputeSpellHitResult.
TEST(SpellImmunityTest, Bladestorm_ImmuneToStun)
{
SpellDesc stunSpell;
stunSpell.effects[0] = {EFFECT_APPLY_AURA, AURA_MOD_STUN};
// without any special immunity the stun should land
EXPECT_EQ(ComputeSpellHitResult(false, false, stunSpell), SPELL_MISS_NONE);
// with a bladestormstyle stun immunity it is blocked
EXPECT_EQ(ComputeSpellHitResult(false, false, stunSpell, true), SPELL_MISS_IMMUNE);
}