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
26 changed files with 1655 additions and 914 deletions
@@ -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);
+46 -28
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)
+5 -3
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;
@@ -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
+1 -1
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;
});
+2 -2
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);
}
+1 -1
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;
+213 -211
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);
}
+17 -11
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
+63 -57
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)
+15 -380
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;
+3 -3
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)
{
+141 -105
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);
}
+14 -4
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);
};
+4 -4
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()));
+546 -48
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)
+42 -6
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]; }
};
+77 -12
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));
}
+21
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;
+3
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();
+20 -22
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;
}
+29 -11
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))