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:
parent
0e0ff86e3e
commit
a90570a2de
26 changed files with 1655 additions and 914 deletions
|
|
@ -0,0 +1,23 @@
|
|||
--
|
||||
CREATE TABLE `creature_immunities` (
|
||||
`ID` int NOT NULL,
|
||||
`SchoolMask` tinyint NOT NULL DEFAULT '0',
|
||||
`DispelTypeMask` smallint NOT NULL DEFAULT '0',
|
||||
`MechanicsMask` bigint NOT NULL DEFAULT '0',
|
||||
`Effects` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`Auras` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
`ImmuneAoE` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`ImmuneChain` tinyint(1) NOT NULL DEFAULT '0',
|
||||
`Comment` mediumtext COLLATE utf8mb4_unicode_ci NOT NULL,
|
||||
PRIMARY KEY (`ID`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
ALTER TABLE `creature_template` ADD `CreatureImmunitiesId` int NOT NULL DEFAULT '0' AFTER `RegenHealth`;
|
||||
|
||||
UPDATE `creature_template` SET `CreatureImmunitiesId`=COALESCE((SELECT ci.`ID` FROM `creature_immunities` ci WHERE ci.`SchoolMask`=`spell_school_immune_mask` AND ci.`MechanicsMask`=`mechanic_immune_mask`*2),0);
|
||||
|
||||
ALTER TABLE `creature_template`
|
||||
DROP `spell_school_immune_mask`,
|
||||
DROP `mechanic_immune_mask`;
|
||||
|
||||
ALTER TABLE `creature_template` DROP COLUMN `scale`;
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
--
|
||||
|
||||
DELETE FROM `creature_immunities` WHERE `ID` IN (95,96,315,477,679,878,1537,1557,1614,1615,1630,1632,1664,1676,1682,1693,1694,1695,1733);
|
||||
INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES
|
||||
(95, 0, 0, 32, '', '', 0, 0, 'Immune to Fear'),
|
||||
(96, 0, 0, 1234599598, '', '5,7,12,26,33', 0, 0, 'Immune to CC (Free Friend, Uncontrollable Frenzy, Warlord\'s Presence)'),
|
||||
(315, 127, 0, 0, '', '', 0, 0, 'Immune to damage'),
|
||||
(477, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
|
||||
(679, 0, 0, 1234599078, '', '5,7,12,26,33', 0, 0, 'Immune to CC'),
|
||||
(878, 0, 0, 4096, '', '12', 0, 0, 'Immune to Stun'),
|
||||
(1537, 0, 0, 32, '', '7', 0, 0, 'Immune to Fear'),
|
||||
(1557, 0, 0, 1234599078, '98,124,144,145', '', 0, 0, 'Immune CC+Stun+Move'),
|
||||
(1614, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
|
||||
(1615, 0, 0, 1301707942, '68', '5,7,12,26,27,33,60', 0, 0, 'Immune to CC+Interrupt'),
|
||||
(1630, 0, 0, 69954075814, '114', '5,7,11,12,26,33', 0, 0, 'Immune to CC+Taunt'),
|
||||
(1632, 0, 0, 584472182, '', '', 0, 0, 'Immune to hard CC'),
|
||||
(1664, 0, 0, 2176, '', '', 0, 0, 'Immune to slow'),
|
||||
(1676, 0, 0, 570425344, '', '', 0, 0, 'Immune to immunities'),
|
||||
(1682, 0, 0, 4096, '', '', 0, 0, 'Immune to Stun'),
|
||||
(1693, 0, 0, 584472182, '68', '', 0, 0, 'Immune to hard CC+Interrupt'),
|
||||
(1694, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
|
||||
(1695, 0, 0, 4096, '', '12', 0, 0, 'Immune to Stun'),
|
||||
(1733, 0, 0, 1234599078, '98,124,144,145', '', 0, 0, 'Immune CC+Stun+Move');
|
||||
|
||||
DELETE FROM `creature_immunities` WHERE `ID` IN (1762,1771,1794,1799,1808,1835,1887,1893,1944,1964,1971);
|
||||
INSERT INTO `creature_immunities` (`ID`, `SchoolMask`, `DispelTypeMask`, `MechanicsMask`, `Effects`, `Auras`, `ImmuneAoE`, `ImmuneChain`, `Comment`) VALUES
|
||||
(1762, 0, 4, 0, '', '', 0, 0, 'Immune to Curses'),
|
||||
(1771, 0, 0, 0, '', '', 1, 0, 'Immune to AoE'),
|
||||
(1794, 0, 0, 67108864, '68', '', 0, 0, 'Immune to Interrupts'),
|
||||
(1799, 0, 0, 67109376, '68', '27,60', 0, 0, 'Immune to Interrupts+Silence'),
|
||||
(1808, 0, 0, 0, '98,124,144,145', '', 0, 0, 'Immune to knockback'),
|
||||
(1835, 0, 0, 1075183616, '', '', 0, 0, 'Immune to polymorph+banish+shackle+sap'),
|
||||
(1887, 0, 0, 0, '98,124,144,145', '', 0, 0, 'Immune to knockback'),
|
||||
(1893, 0, 0, 1091728546, '', '', 0, 0, 'Immune to Charm+Fear+Freeze+Horror+Knockout+Polymorph+Root+Sap+Shackle+Sleep+Stun'),
|
||||
(1944, 0, 0, 68719476736, '114', '11', 0, 0, 'Immune to Taunt'),
|
||||
(1964, 0, 0, 67109376, '68', '27,60', 0, 0, 'Immune to Interrupts+Silence'),
|
||||
(1971, 0, 0, 4096, '', '', 0, 0, 'Immune to Stun');
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
});
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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]; }
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ void TestCreature::ForceInitValues(ObjectGuid::LowType guidLow, uint32 entry)
|
|||
_fakeCreatureTemplate->speed_run = 1.14286f;
|
||||
_fakeCreatureTemplate->speed_swim = 1.0f;
|
||||
_fakeCreatureTemplate->speed_flight = 1.0f;
|
||||
_fakeCreatureTemplate->scale = 1.0f;
|
||||
_fakeCreatureTemplate->DamageModifier = 1.0f;
|
||||
_fakeCreatureTemplate->BaseAttackTime = 2000;
|
||||
_fakeCreatureTemplate->RangeAttackTime = 2000;
|
||||
|
|
|
|||
330
src/test/server/game/Spells/SpellImmunityTest.cpp
Normal file
330
src/test/server/game/Spells/SpellImmunityTest.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file SpellImmunityTest.cpp
|
||||
* @brief Tests for spell immunity mechanics
|
||||
*/
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include "Unit.h" // needed for SpellSchoolMask and mask helper
|
||||
|
||||
namespace
|
||||
{
|
||||
enum EffectType : uint8_t
|
||||
{
|
||||
EFFECT_NONE,
|
||||
EFFECT_SCHOOL_DAMAGE,
|
||||
EFFECT_HEALTH_LEECH,
|
||||
EFFECT_POWER_DRAIN,
|
||||
EFFECT_POWER_BURN,
|
||||
EFFECT_NORMALIZED_WEAPON_DMG,
|
||||
EFFECT_WEAPON_PERCENT_DAMAGE,
|
||||
EFFECT_APPLY_AURA,
|
||||
EFFECT_DUMMY
|
||||
};
|
||||
|
||||
enum AuraType : uint8_t
|
||||
{
|
||||
AURA_NONE,
|
||||
AURA_MOD_DECREASE_SPEED,
|
||||
AURA_PERIODIC_DAMAGE,
|
||||
AURA_TRANSFORM,
|
||||
AURA_MOD_STUN
|
||||
};
|
||||
|
||||
struct EffectDesc
|
||||
{
|
||||
EffectType effect = EFFECT_NONE;
|
||||
AuraType aura = AURA_NONE;
|
||||
};
|
||||
|
||||
struct SpellDesc
|
||||
{
|
||||
std::array<EffectDesc, 3> effects{};
|
||||
};
|
||||
|
||||
bool IsDamageEffect(EffectType effect)
|
||||
{
|
||||
switch (effect)
|
||||
{
|
||||
case EFFECT_SCHOOL_DAMAGE:
|
||||
case EFFECT_HEALTH_LEECH:
|
||||
case EFFECT_POWER_DRAIN:
|
||||
case EFFECT_POWER_BURN:
|
||||
case EFFECT_NORMALIZED_WEAPON_DMG:
|
||||
case EFFECT_WEAPON_PERCENT_DAMAGE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool HasOnlyDamageEffects(SpellDesc const& spell)
|
||||
{
|
||||
bool hasAny = false;
|
||||
|
||||
for (EffectDesc const& e : spell.effects)
|
||||
{
|
||||
if (e.effect == EFFECT_NONE)
|
||||
continue;
|
||||
|
||||
hasAny = true;
|
||||
if (!IsDamageEffect(e.effect))
|
||||
return false;
|
||||
}
|
||||
|
||||
return hasAny;
|
||||
}
|
||||
|
||||
// Helper to classify spells which apply a stun aura
|
||||
bool IsStunSpell(SpellDesc const& spell)
|
||||
{
|
||||
for (EffectDesc const& e : spell.effects)
|
||||
{
|
||||
if (e.effect == EFFECT_APPLY_AURA && e.aura == AURA_MOD_STUN)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// The last parameter defaults to false to avoid updating existing tests
|
||||
// that don't care about mechanic immunities. Bladestorm grants a
|
||||
// specific mechanic immunity (including stun) that should block
|
||||
// a spell like Lethargy.
|
||||
SpellMissInfo ComputeSpellHitResult(bool isImmunedToSpell, bool isImmunedToDamage,
|
||||
SpellDesc const& spell, bool immuneToStun = false)
|
||||
{
|
||||
// Mirrors current core ordering:
|
||||
// 1) full spell immunity check
|
||||
// 2) mechanic immunity check (e.g. bladestorm vs stun)
|
||||
// 3) damage immunity only for damage-only spells
|
||||
if (isImmunedToSpell)
|
||||
return SPELL_MISS_IMMUNE;
|
||||
|
||||
if (immuneToStun && IsStunSpell(spell))
|
||||
return SPELL_MISS_IMMUNE;
|
||||
|
||||
if (HasOnlyDamageEffects(spell) && isImmunedToDamage)
|
||||
return SPELL_MISS_IMMUNE;
|
||||
|
||||
return SPELL_MISS_NONE;
|
||||
}
|
||||
|
||||
struct EffectApplyResult
|
||||
{
|
||||
bool damageApplied = false;
|
||||
bool slowApplied = false;
|
||||
};
|
||||
|
||||
EffectApplyResult ApplyEffectsWithMovementImmunity(SpellDesc const& spell, bool immuneToMovementImpairing)
|
||||
{
|
||||
EffectApplyResult result;
|
||||
|
||||
for (EffectDesc const& e : spell.effects)
|
||||
{
|
||||
if (e.effect == EFFECT_NONE)
|
||||
continue;
|
||||
|
||||
if (IsDamageEffect(e.effect))
|
||||
result.damageApplied = true;
|
||||
|
||||
if (e.effect == EFFECT_APPLY_AURA && e.aura == AURA_MOD_DECREASE_SPEED)
|
||||
{
|
||||
if (!immuneToMovementImpairing)
|
||||
result.slowApplied = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
SpellDesc MakeDamageOnlySpell()
|
||||
{
|
||||
SpellDesc spell;
|
||||
spell.effects[0] = { EFFECT_SCHOOL_DAMAGE, AURA_NONE };
|
||||
return spell;
|
||||
}
|
||||
|
||||
SpellDesc MakeFrostboltLikeSpell()
|
||||
{
|
||||
SpellDesc spell;
|
||||
spell.effects[0] = { EFFECT_SCHOOL_DAMAGE, AURA_NONE };
|
||||
spell.effects[1] = { EFFECT_APPLY_AURA, AURA_MOD_DECREASE_SPEED };
|
||||
return spell;
|
||||
}
|
||||
|
||||
SpellDesc MakeCycloneLikeSpell()
|
||||
{
|
||||
SpellDesc spell;
|
||||
spell.effects[0] = { EFFECT_APPLY_AURA, AURA_TRANSFORM };
|
||||
return spell;
|
||||
}
|
||||
|
||||
SpellDesc MakeSlowOnlySpell()
|
||||
{
|
||||
// represents effects such as Frost Trap or Desecration, which only slow
|
||||
SpellDesc spell;
|
||||
spell.effects[0] = { EFFECT_APPLY_AURA, AURA_MOD_DECREASE_SPEED };
|
||||
return spell;
|
||||
}
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, HasOnlyDamageEffects_TrueForPureDamage)
|
||||
{
|
||||
SpellDesc spell = MakeDamageOnlySpell();
|
||||
EXPECT_TRUE(HasOnlyDamageEffects(spell));
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, HasOnlyDamageEffects_FalseForDamagePlusAura)
|
||||
{
|
||||
SpellDesc spell = MakeFrostboltLikeSpell();
|
||||
EXPECT_FALSE(HasOnlyDamageEffects(spell));
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, SpellImmunity_BlocksAllSpells)
|
||||
{
|
||||
SpellDesc damageOnly = MakeDamageOnlySpell();
|
||||
SpellDesc cycloneLike = MakeCycloneLikeSpell();
|
||||
|
||||
EXPECT_EQ(ComputeSpellHitResult(true, false, damageOnly), SPELL_MISS_IMMUNE);
|
||||
EXPECT_EQ(ComputeSpellHitResult(true, false, cycloneLike), SPELL_MISS_IMMUNE);
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, DamageImmunity_BlocksDamageOnlySpell)
|
||||
{
|
||||
SpellDesc damageOnly = MakeDamageOnlySpell();
|
||||
|
||||
EXPECT_EQ(ComputeSpellHitResult(false, true, damageOnly), SPELL_MISS_IMMUNE);
|
||||
}
|
||||
|
||||
// Specific case for spell ID 16621 "Self Invulnerability".
|
||||
// This aura grants immunity to melee/physical damage only. A rogue
|
||||
// using Sinister Strike (a physical melee attack) should be completely
|
||||
// blocked by the effect. The test uses the same simplified damage-only
|
||||
// spell description as above but documents the physical context.
|
||||
TEST(SpellImmunityTest, SelfInvulnerability_BlocksMeleeDamage)
|
||||
{
|
||||
SpellDesc meleeAttack = MakeDamageOnlySpell();
|
||||
// simulate physical damage coming from a rogue melee ability
|
||||
|
||||
EXPECT_EQ(ComputeSpellHitResult(false, true, meleeAttack), SPELL_MISS_IMMUNE);
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, DamageImmunity_DoesNotMissMixedSpell)
|
||||
{
|
||||
// This is the key fix: damage immunity must not force SPELL_MISS_IMMUNE
|
||||
// for mixed spells that include non-damage effects.
|
||||
SpellDesc frostboltLike = MakeFrostboltLikeSpell();
|
||||
|
||||
EXPECT_EQ(ComputeSpellHitResult(false, true, frostboltLike), SPELL_MISS_NONE);
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, HandOfFreedomStyle_MovementImmunity_AllowsDamageBlocksSlow)
|
||||
{
|
||||
SpellDesc frostboltLike = MakeFrostboltLikeSpell();
|
||||
|
||||
EffectApplyResult result = ApplyEffectsWithMovementImmunity(frostboltLike, true);
|
||||
|
||||
EXPECT_TRUE(result.damageApplied);
|
||||
EXPECT_FALSE(result.slowApplied);
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, NoMovementImmunity_FrostboltStyle_AppliesDamageAndSlow)
|
||||
{
|
||||
SpellDesc frostboltLike = MakeFrostboltLikeSpell();
|
||||
|
||||
EffectApplyResult result = ApplyEffectsWithMovementImmunity(frostboltLike, false);
|
||||
|
||||
EXPECT_TRUE(result.damageApplied);
|
||||
EXPECT_TRUE(result.slowApplied);
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, CycloneLikeSpell_DivineShieldStyle_Immune)
|
||||
{
|
||||
SpellDesc cycloneLike = MakeCycloneLikeSpell();
|
||||
|
||||
EXPECT_EQ(ComputeSpellHitResult(true, false, cycloneLike), SPELL_MISS_IMMUNE);
|
||||
}
|
||||
|
||||
// Regression test for issue #10671:
|
||||
// Divine Shield (full spell immunity) should block purely slowing spells
|
||||
// such as Hunter Frost Trap or DK Desecration. Previously the effect was
|
||||
// applied because the core only checked damage-only spells when deciding
|
||||
// immunity based on damage or spell state.
|
||||
TEST(SpellImmunityTest, SpellImmunity_BlocksSlowOnlySpell)
|
||||
{
|
||||
SpellDesc slowOnly = MakeSlowOnlySpell();
|
||||
EXPECT_EQ(ComputeSpellHitResult(true, false, slowOnly), SPELL_MISS_IMMUNE);
|
||||
}
|
||||
|
||||
// Ensure that damage-only immunity (e.g. from Hand of Protection) does not
|
||||
// accidentally prevent slow-only spells. This covers the regression when
|
||||
// Divine Shield was incorrectly modelled as damage immunity only.
|
||||
TEST(SpellImmunityTest, DamageImmunity_DoesNotBlockSlowOnlySpell)
|
||||
{
|
||||
SpellDesc slowOnly = MakeSlowOnlySpell();
|
||||
EXPECT_EQ(ComputeSpellHitResult(false, true, slowOnly), SPELL_MISS_NONE);
|
||||
}
|
||||
|
||||
// New coverage for school-mask logic. These exercises the helper used by
|
||||
// Unit::IsImmunedToDamage to ensure broad masks are handled correctly.
|
||||
TEST(SpellImmunityTest, ImmunityMask_PartialOverlapDoesNotCount)
|
||||
{
|
||||
SpellSchoolMask immune = SPELL_SCHOOL_MASK_FROST;
|
||||
SpellSchoolMask checkAll = SPELL_SCHOOL_MASK_ALL;
|
||||
|
||||
// a frost-only immunity should *not* make you immune to all damage
|
||||
EXPECT_FALSE(Unit::IsImmuneMaskFully(immune, checkAll));
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, ImmunityMask_FullCoverageAccepted)
|
||||
{
|
||||
SpellSchoolMask immune = SPELL_SCHOOL_MASK_MAGIC; // holy+spell
|
||||
SpellSchoolMask checkMagic = SPELL_SCHOOL_MASK_MAGIC;
|
||||
SpellSchoolMask checkSpell = SPELL_SCHOOL_MASK_SPELL;
|
||||
|
||||
EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkMagic));
|
||||
EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkSpell));
|
||||
}
|
||||
|
||||
TEST(SpellImmunityTest, ImmunityMask_SupersetMatches)
|
||||
{
|
||||
SpellSchoolMask immune = SPELL_SCHOOL_MASK_ALL;
|
||||
SpellSchoolMask checkMagic = SPELL_SCHOOL_MASK_MAGIC;
|
||||
|
||||
EXPECT_TRUE(Unit::IsImmuneMaskFully(immune, checkMagic));
|
||||
}
|
||||
|
||||
// Bladestorm grants a mechanic immunity mask which includes stuns (e.g.
|
||||
// Lethargy 69133). The following test mirrors that behaviour by
|
||||
// modelling a simple spell that applies a stun aura and exercising the
|
||||
// new `immuneToStun` flag in ComputeSpellHitResult.
|
||||
TEST(SpellImmunityTest, Bladestorm_ImmuneToStun)
|
||||
{
|
||||
SpellDesc stunSpell;
|
||||
stunSpell.effects[0] = {EFFECT_APPLY_AURA, AURA_MOD_STUN};
|
||||
|
||||
// without any special immunity the stun should land
|
||||
EXPECT_EQ(ComputeSpellHitResult(false, false, stunSpell), SPELL_MISS_NONE);
|
||||
|
||||
// with a bladestorm‑style stun immunity it is blocked
|
||||
EXPECT_EQ(ComputeSpellHitResult(false, false, stunSpell, true), SPELL_MISS_IMMUNE);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue