aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2024-01-30 21:29:55 +0100
committerShauren <shauren.trinity@gmail.com>2024-01-30 21:29:55 +0100
commitf70a5817e1c07891185d716611d45f50b1c73b78 (patch)
tree6d08eeee3021a72e0742fd82d836696556a57f96 /src
parent9e13fee4a6374680e99ab4407fed73940954ea91 (diff)
Core/Creatures: Move immunities to separate table and implemented setting immunties to dispel, spell effects, aura types, aoe and chain targeting
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp57
-rw-r--r--src/server/game/Entities/Creature/Creature.h3
-rw-r--r--src/server/game/Entities/Creature/CreatureData.h3
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp8
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp15
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h6
-rw-r--r--src/server/game/Spells/Spell.cpp51
-rw-r--r--src/server/game/Spells/Spell.h22
-rw-r--r--src/server/game/Spells/SpellInfo.cpp60
-rw-r--r--src/server/game/Spells/SpellInfo.h2
-rw-r--r--src/server/game/Spells/SpellMgr.cpp53
-rw-r--r--src/server/game/Spells/SpellMgr.h17
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp9
15 files changed, 213 insertions, 97 deletions
diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp
index e90a67f4392..63d4c4cc872 100644
--- a/src/server/database/Database/Implementation/WorldDatabase.cpp
+++ b/src/server/database/Database/Implementation/WorldDatabase.cpp
@@ -62,7 +62,7 @@ void WorldDatabaseConnection::DoPrepareStatements()
PrepareStatement(WORLD_SEL_CREATURE_ADDON_BY_GUID, "SELECT guid FROM creature_addon WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_DEL_CREATURE, "DELETE FROM creature WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(WORLD_SEL_COMMANDS, "SELECT name, help FROM command", CONNECTION_SYNCH);
- PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, Classification, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, family, trainer_class, type, VehicleId, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, mechanic_immune_mask, spell_school_immune_mask, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", CONNECTION_SYNCH);
+ PrepareStatement(WORLD_SEL_CREATURE_TEMPLATE, "SELECT entry, KillCredit1, KillCredit2, name, femaleName, subname, TitleAlt, IconName, RequiredExpansion, VignetteID, faction, npcflag, speed_walk, speed_run, scale, Classification, dmgschool, BaseAttackTime, RangeAttackTime, BaseVariance, RangeVariance, unit_class, unit_flags, unit_flags2, unit_flags3, family, trainer_class, type, VehicleId, AIName, MovementType, ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, CreatureImmunitiesId, flags_extra, ScriptName, StringId FROM creature_template ct LEFT JOIN creature_template_movement ctm ON ct.entry = ctm.CreatureId WHERE entry = ? OR 1 = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_GAMEOBJECT_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM gameobject WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH);
PrepareStatement(WORLD_SEL_CREATURE_NEAREST, "SELECT guid, id, position_x, position_y, position_z, map, (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) AS order_ FROM creature WHERE map = ? AND (POW(position_x - ?, 2) + POW(position_y - ?, 2) + POW(position_z - ?, 2)) <= ? ORDER BY order_", CONNECTION_SYNCH);
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index a8077ffae0b..c91976a67b1 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -314,7 +314,7 @@ Creature::Creature(bool isWorldObject) : Unit(isWorldObject), MapObject(), m_Pla
m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(UI64LIT(0)), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false), m_cannotReachTarget(false), m_cannotReachTimer(0),
m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_creatureDifficulty(nullptr), _waypointPathId(0), _currentWaypointNodeInfo(0, 0),
m_formation(nullptr), m_triggerJustAppeared(true), m_respawnCompatibilityMode(false), _lastDamagedTime(0),
- _regenerateHealth(true), _isMissingCanSwimFlagOutOfCombat(false), _gossipMenuId(0), _sparringHealthPct(0)
+ _regenerateHealth(true), _isMissingCanSwimFlagOutOfCombat(false), _creatureImmunitiesId(0), _gossipMenuId(0), _sparringHealthPct(0)
{
m_regenTimer = CREATURE_REGEN_INTERVAL;
@@ -685,7 +685,7 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/,
LoadCreaturesAddon();
LoadCreaturesSparringHealth();
- LoadTemplateImmunities();
+ LoadTemplateImmunities(cInfo->CreatureImmunitiesId);
GetThreatManager().EvaluateSuppressed();
@@ -2423,40 +2423,45 @@ void Creature::DespawnOrUnsummon(Milliseconds timeToDespawn /*= 0s*/, Seconds fo
ForcedDespawn(timeToDespawn.count(), forceRespawnTimer);
}
-void Creature::LoadTemplateImmunities()
+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 (uint32 i = MECHANIC_NONE + 1; i < MAX_MECHANIC; ++i)
- ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, i, false);
+ auto applyCreatureImmunities = [this](CreatureImmunities const* immunities, bool apply)
+ {
+ for (std::size_t i = 0; i < immunities->School.size(); ++i)
+ if (immunities->School[i])
+ ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, apply);
- for (uint32 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
- ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, false);
+ for (std::size_t i = 0; i < immunities->DispelType.size(); ++i)
+ if (immunities->DispelType[i])
+ ApplySpellImmune(placeholderSpellId, IMMUNITY_DISPEL, i, apply);
- // don't inherit immunities for hunter pets
- if (GetOwnerGUID().IsPlayer() && IsHunterPet())
- return;
+ for (std::size_t i = 0; i < immunities->Mechanic.size(); ++i)
+ if (immunities->Mechanic[i])
+ ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, i, apply);
- if (uint64 mask = GetCreatureTemplate()->MechanicImmuneMask)
- {
- for (uint32 i = MECHANIC_NONE + 1; i < MAX_MECHANIC; ++i)
- {
- if (mask & (UI64LIT(1) << (i - 1)))
- ApplySpellImmune(placeholderSpellId, IMMUNITY_MECHANIC, i, true);
- }
- }
+ for (SpellEffectName effect : immunities->Effect)
+ ApplySpellImmune(placeholderSpellId, IMMUNITY_EFFECT, effect, apply);
- if (uint32 mask = GetCreatureTemplate()->SpellSchoolImmuneMask)
+ for (AuraType aura : immunities->Aura)
+ ApplySpellImmune(placeholderSpellId, IMMUNITY_STATE, aura, apply);
+ };
+
+ // unapply template immunities (in case we're updating entry)
+ if (CreatureImmunities const* immunities = SpellMgr::GetCreatureImmunities(_creatureImmunitiesId))
+ applyCreatureImmunities(immunities, false);
+
+ // apply new immunities
+ if (CreatureImmunities const* immunities = SpellMgr::GetCreatureImmunities(creatureImmunitiesId))
{
- for (uint8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
- {
- if (mask & (1 << i))
- ApplySpellImmune(placeholderSpellId, IMMUNITY_SCHOOL, 1 << i, true);
- }
+ _creatureImmunitiesId = creatureImmunitiesId;
+ applyCreatureImmunities(immunities, true);
}
+ else
+ _creatureImmunitiesId = 0;
}
bool Creature::IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster,
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index 7e9764b1331..591a1750818 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -158,7 +158,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool isCanInteractWithBattleMaster(Player* player, bool msg) const;
bool CanResetTalents(Player* player) const;
bool CanCreatureAttack(Unit const* victim, bool force = true) const;
- void LoadTemplateImmunities();
+ void LoadTemplateImmunities(int32 creatureImmunitiesId);
bool IsImmunedToSpellEffect(SpellInfo const* spellInfo, SpellEffectInfo const& spellEffectInfo, WorldObject const* caster, bool requireImmunityPurgesEffectAttribute = false) const override;
bool IsElite() const;
bool isWorldBoss() const;
@@ -524,6 +524,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool _isMissingCanSwimFlagOutOfCombat;
+ int32 _creatureImmunitiesId;
uint32 _gossipMenuId;
Optional<uint32> _trainerId;
float _sparringHealthPct;
diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h
index 6c66d75d708..b0ea6ef0082 100644
--- a/src/server/game/Entities/Creature/CreatureData.h
+++ b/src/server/game/Entities/Creature/CreatureData.h
@@ -543,8 +543,7 @@ struct TC_GAME_API CreatureTemplate
int32 WidgetSetID;
int32 WidgetSetUnitConditionID;
bool RegenHealth;
- uint64 MechanicImmuneMask;
- uint32 SpellSchoolImmuneMask;
+ int32 CreatureImmunitiesId;
uint32 flags_extra;
uint32 ScriptID;
std::string StringId;
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index a71c3e7e4d5..c6966763d9d 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -442,7 +442,7 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
owner->DisablePetControlsOnMount(REACT_PASSIVE, COMMAND_FOLLOW);
// must be after SetMinion (owner guid check)
- LoadTemplateImmunities();
+ LoadTemplateImmunities(0);
m_loading = false;
});
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index e0e3c0011cf..73bd1e4697c 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -8399,11 +8399,9 @@ void Unit::UpdateSpeed(UnitMoveType mtype)
if (int32 normalization = GetMaxPositiveAuraModifier(SPELL_AURA_USE_NORMAL_MOVEMENT_SPEED))
{
if (Creature* creature = ToCreature())
- {
- uint64 immuneMask = creature->GetCreatureTemplate()->MechanicImmuneMask;
- if (immuneMask & (1 << (MECHANIC_SNARE - 1)) || immuneMask & (1 << (MECHANIC_DAZE - 1)))
- break;
- }
+ if (CreatureImmunities const* immunities = SpellMgr::GetCreatureImmunities(creature->GetCreatureTemplate()->CreatureImmunitiesId))
+ if (immunities->Mechanic[MECHANIC_SNARE] || immunities->Mechanic[MECHANIC_DAZE])
+ break;
// Use speed from aura
float max_speed = normalization / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 0c4ca35be92..70c1d64c309 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -367,9 +367,9 @@ void ObjectMgr::LoadCreatureTemplates()
// "ctm.Ground, ctm.Swim, ctm.Flight, ctm.Rooted, ctm.Chase, ctm.Random, ctm.InteractionPauseTimer, ExperienceModifier, "
// 39 40 41 42 43
// "RacialLeader, movementId, WidgetSetID, WidgetSetUnitConditionID, RegenHealth, "
- // 44 45 46
- // "mechanic_immune_mask, spell_school_immune_mask, flags_extra, "
- // 47 48
+ // 44 45
+ // "CreatureImmunitiesId, flags_extra, "
+ // 46 47
// "ScriptName, StringId FROM creature_template WHERE entry = ? OR 1 = ?");
WorldDatabasePreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_CREATURE_TEMPLATE);
@@ -475,11 +475,10 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields)
creatureTemplate.WidgetSetID = fields[41].GetInt32();
creatureTemplate.WidgetSetUnitConditionID = fields[42].GetInt32();
creatureTemplate.RegenHealth = fields[43].GetBool();
- creatureTemplate.MechanicImmuneMask = fields[44].GetUInt64();
- creatureTemplate.SpellSchoolImmuneMask = fields[45].GetUInt32();
- creatureTemplate.flags_extra = fields[46].GetUInt32();
- creatureTemplate.ScriptID = GetScriptId(fields[47].GetString());
- creatureTemplate.StringId = fields[48].GetString();
+ creatureTemplate.CreatureImmunitiesId = fields[44].GetInt32();
+ creatureTemplate.flags_extra = fields[45].GetUInt32();
+ creatureTemplate.ScriptID = GetScriptId(fields[46].GetString());
+ creatureTemplate.StringId = fields[47].GetString();
}
void ObjectMgr::LoadCreatureTemplateGossip()
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 63a3640d236..c42f5d8714c 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -568,7 +568,7 @@ enum SpellAttr3 : uint32
SPELL_ATTR3_IGNORE_CASTER_AND_TARGET_RESTRICTIONS = 0x10000000, /*NYI*/ // TITLE Ignore Caster & Target Restrictions
SPELL_ATTR3_IGNORE_CASTER_MODIFIERS = 0x20000000, // TITLE Ignore Caster Modifiers
SPELL_ATTR3_DO_NOT_DISPLAY_RANGE = 0x40000000, // TITLE Do Not Display Range (client only)
- SPELL_ATTR3_NOT_ON_AOE_IMMUNE = 0x80000000 /*NYI, no aoe immunity implementation*/ // TITLE Not On AOE Immune
+ SPELL_ATTR3_NOT_ON_AOE_IMMUNE = 0x80000000 // TITLE Not On AOE Immune
};
// EnumUtils: DESCRIBE THIS
@@ -2598,7 +2598,9 @@ 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))
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 6f3e829f799..9ab610412df 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -1418,15 +1418,18 @@ void Spell::SelectImplicitAreaTargets(SpellEffectInfo const& spellEffectInfo, Sp
if (!m_caster->IsUnit() || !m_caster->ToUnit()->IsInRaidWith(targetedUnit))
targets.push_back(m_targets.GetUnitTarget());
else
- SearchAreaTargets(targets, spellEffectInfo, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions.get());
+ SearchAreaTargets(targets, spellEffectInfo, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(),
+ spellEffectInfo.ImplicitTargetConditions.get(), Trinity::WorldObjectSpellAreaTargetSearchReason::Area);
}
break;
case TARGET_UNIT_CASTER_AND_SUMMONS:
targets.push_back(m_caster);
- SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions.get());
+ SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(),
+ spellEffectInfo.ImplicitTargetConditions.get(), Trinity::WorldObjectSpellAreaTargetSearchReason::Area);
break;
default:
- SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions.get());
+ SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(),
+ spellEffectInfo.ImplicitTargetConditions.get(), Trinity::WorldObjectSpellAreaTargetSearchReason::Area);
break;
}
@@ -2186,20 +2189,23 @@ WorldObject* Spell::SearchNearbyTarget(SpellEffectInfo const& spellEffectInfo, f
return target;
}
-void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, SpellEffectInfo const& spellEffectInfo, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList)
+void Spell::SearchAreaTargets(std::list<WorldObject*>& targets, SpellEffectInfo const& spellEffectInfo, float range, Position const* position, WorldObject* referer,
+ SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList,
+ Trinity::WorldObjectSpellAreaTargetSearchReason searchReason)
{
uint32 containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
if (!containerTypeMask)
return;
float extraSearchRadius = range > 0.0f ? EXTRA_CELL_SEARCH_RADIUS : 0.0f;
- Trinity::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList, objectType);
+ Trinity::WorldObjectSpellAreaTargetCheck check(range, position, m_caster, referer, m_spellInfo, selectionType, condList, objectType, searchReason);
Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck> searcher(m_caster, targets, check, containerTypeMask);
searcher.i_phaseShift = &PhasingHandler::GetAlwaysVisiblePhaseShift();
SearchTargets<Trinity::WorldObjectListSearcher<Trinity::WorldObjectSpellAreaTargetCheck>>(searcher, containerTypeMask, m_caster, position, range + extraSearchRadius);
}
-void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal)
+void Spell::SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType,
+ SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal)
{
// max dist for jump target selection
float jumpRadius = 0.0f;
@@ -2241,7 +2247,8 @@ 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, spellEffectInfo, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions.get());
+ SearchAreaTargets(tempTargets, spellEffectInfo, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions.get(),
+ Trinity::WorldObjectSpellAreaTargetSearchReason::Chain);
tempTargets.remove(target);
// remove targets which are always invalid for chain spells
@@ -9189,15 +9196,16 @@ bool WorldObjectSpellNearbyTargetCheck::operator()(WorldObject* target)
}
WorldObjectSpellAreaTargetCheck::WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster,
- WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType)
- : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList, objectType), _range(range), _position(position) { }
+ WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType,
+ WorldObjectSpellAreaTargetSearchReason searchReason /*= Area*/)
+ : WorldObjectSpellTargetCheck(caster, referer, spellInfo, selectionType, condList, objectType), _range(range), _position(position), _searchReason(searchReason) { }
bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) const
{
- if (target->ToGameObject())
+ if (GameObject* gameObjectTarget = target->ToGameObject())
{
// isInRange including the dimension of the GO
- bool isInRange = target->ToGameObject()->IsInRange(_position->GetPositionX(), _position->GetPositionY(), _position->GetPositionZ(), _range);
+ bool isInRange = gameObjectTarget->IsInRange(_position->GetPositionX(), _position->GetPositionY(), _position->GetPositionZ(), _range);
if (!isInRange)
return false;
}
@@ -9206,6 +9214,27 @@ bool WorldObjectSpellAreaTargetCheck::operator()(WorldObject* target) const
bool isInsideCylinder = target->IsWithinDist2d(_position, _range) && std::abs(target->GetPositionZ() - _position->GetPositionZ()) <= _range;
if (!isInsideCylinder)
return false;
+
+ if (Creature* creatureTarget = target->ToCreature())
+ {
+ if (CreatureImmunities const* immunities = SpellMgr::GetCreatureImmunities(creatureTarget->GetCreatureTemplate()->CreatureImmunitiesId))
+ {
+ switch (_searchReason)
+ {
+ case WorldObjectSpellAreaTargetSearchReason::Area:
+ if (immunities->ImmuneAoE)
+ return false;
+ break;
+ case WorldObjectSpellAreaTargetSearchReason::Chain:
+ if (immunities->ImmuneChain)
+ return false;
+ break;
+ default:
+ break;
+ }
+
+ }
+ }
}
return WorldObjectSpellTargetCheck::operator ()(target);
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index d5037f91356..ff0b2efef11 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -67,6 +67,11 @@ enum SpellValueMod : uint8;
enum TriggerCastFlags : uint32;
enum WeaponAttackType : uint8;
+namespace Trinity
+{
+enum class WorldObjectSpellAreaTargetSearchReason;
+}
+
#define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILLISECONDS)
#define MAX_SPELL_RANGE_TOLERANCE 3.0f
#define TRAJECTORY_MISSILE_SIZE 3.0f
@@ -455,8 +460,11 @@ class TC_GAME_API Spell
template<class SEARCHER> static void SearchTargets(SEARCHER& searcher, uint32 containerMask, WorldObject* referer, Position const* pos, float radius);
WorldObject* SearchNearbyTarget(SpellEffectInfo const& spellEffectInfo, float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList = nullptr);
- void SearchAreaTargets(std::list<WorldObject*>& targets, SpellEffectInfo const& spellEffectInfo, float range, Position const* position, WorldObject* referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList);
- void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal);
+ void SearchAreaTargets(std::list<WorldObject*>& targets, SpellEffectInfo const& spellEffectInfo, float range, Position const* position, WorldObject* referer,
+ SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, ConditionContainer const* condList,
+ Trinity::WorldObjectSpellAreaTargetSearchReason searchReason);
+ void SearchChainTargets(std::list<WorldObject*>& targets, uint32 chainTargets, WorldObject* target, SpellTargetObjectTypes objectType,
+ SpellTargetCheckTypes selectType, SpellEffectInfo const& spellEffectInfo, bool isChainHeal);
GameObject* SearchSpellFocus();
@@ -950,12 +958,20 @@ namespace Trinity
bool operator()(WorldObject* target);
};
+ enum class WorldObjectSpellAreaTargetSearchReason
+ {
+ Area,
+ Chain
+ };
+
struct TC_GAME_API WorldObjectSpellAreaTargetCheck : public WorldObjectSpellTargetCheck
{
float _range;
Position const* _position;
+ WorldObjectSpellAreaTargetSearchReason _searchReason;
WorldObjectSpellAreaTargetCheck(float range, Position const* position, WorldObject* caster,
- WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType);
+ WorldObject* referer, SpellInfo const* spellInfo, SpellTargetCheckTypes selectionType, ConditionContainer const* condList, SpellTargetObjectTypes objectType,
+ WorldObjectSpellAreaTargetSearchReason searchReason = WorldObjectSpellAreaTargetSearchReason::Area);
bool operator()(WorldObject* target) const;
};
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 5f6748e93e8..cb2875edd8e 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -231,7 +231,7 @@ struct SpellEffectInfo::ImmunityInfo
uint32 SchoolImmuneMask = 0;
uint32 ApplyHarmfulAuraImmuneMask = 0;
uint64 MechanicImmuneMask = 0;
- uint32 DispelImmune = 0;
+ uint32 DispelImmuneMask = 0;
uint32 DamageSchoolMask = 0;
Trinity::Containers::FlatSet<AuraType> AuraTypeImmune;
@@ -2275,6 +2275,11 @@ SpellCastResult SpellInfo::CheckTarget(WorldObject const* caster, WorldObject co
if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER_CONTROLLED_NPC) && unitTarget->IsControlledByPlayer())
return SPELL_FAILED_TARGET_IS_PLAYER_CONTROLLED;
+
+ if (HasAttribute(SPELL_ATTR3_NOT_ON_AOE_IMMUNE))
+ if (CreatureImmunities const* immunities = SpellMgr::GetCreatureImmunities(unitTarget->ToCreature()->GetCreatureTemplate()->CreatureImmunitiesId))
+ if (immunities->ImmuneAoE)
+ return SPELL_FAILED_BAD_TARGETS;
}
else if (HasAttribute(SPELL_ATTR5_NOT_ON_PLAYER))
return SPELL_FAILED_TARGET_IS_PLAYER;
@@ -3309,7 +3314,7 @@ void SpellInfo::_LoadImmunityInfo()
uint32 schoolImmunityMask = 0;
uint32 applyHarmfulAuraImmunityMask = 0;
uint64 mechanicImmunityMask = 0;
- uint32 dispelImmunity = 0;
+ uint32 dispelImmunityMask = 0;
uint32 damageImmunityMask = 0;
int32 miscVal = effect.MiscValue;
@@ -3321,6 +3326,17 @@ void SpellInfo::_LoadImmunityInfo()
{
case SPELL_AURA_MECHANIC_IMMUNITY_MASK:
{
+ if (CreatureImmunities const* creatureImmunities = SpellMgr::GetCreatureImmunities(miscVal))
+ {
+ schoolImmunityMask |= creatureImmunities->School.to_ulong();
+ dispelImmunityMask |= creatureImmunities->DispelType.to_ulong();
+ mechanicImmunityMask |= creatureImmunities->Mechanic.to_ullong();
+ for (SpellEffectName effectType : creatureImmunities->Effect)
+ immuneInfo.SpellEffectImmune.insert(effectType);
+ for (AuraType aura : creatureImmunities->Aura)
+ immuneInfo.AuraTypeImmune.insert(aura);
+ }
+
switch (miscVal)
{
case 96: // Free Friend, Uncontrollable Frenzy, Warlord's Presence
@@ -3460,29 +3476,6 @@ void SpellInfo::_LoadImmunityInfo()
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);
-
- // These flag can be recognized wrong:
- if (miscVal & (1 << 6))
- immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_DECREASE_SPEED);
- if (miscVal & (1 << 0))
- {
- immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT);
- immuneInfo.AuraTypeImmune.insert(SPELL_AURA_MOD_ROOT_2);
- }
- 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:
@@ -3544,7 +3537,7 @@ void SpellInfo::_LoadImmunityInfo()
}
case SPELL_AURA_DISPEL_IMMUNITY:
{
- dispelImmunity = uint32(miscVal);
+ dispelImmunityMask = 1u << miscVal;
break;
}
default:
@@ -3554,7 +3547,7 @@ void SpellInfo::_LoadImmunityInfo()
immuneInfo.SchoolImmuneMask = schoolImmunityMask;
immuneInfo.ApplyHarmfulAuraImmuneMask = applyHarmfulAuraImmunityMask;
immuneInfo.MechanicImmuneMask = mechanicImmunityMask;
- immuneInfo.DispelImmune = dispelImmunity;
+ immuneInfo.DispelImmuneMask = dispelImmunityMask;
immuneInfo.DamageSchoolMask = damageImmunityMask;
immuneInfo.AuraTypeImmune.shrink_to_fit();
@@ -3563,7 +3556,7 @@ void SpellInfo::_LoadImmunityInfo()
if (immuneInfo.SchoolImmuneMask
|| immuneInfo.ApplyHarmfulAuraImmuneMask
|| immuneInfo.MechanicImmuneMask
- || immuneInfo.DispelImmune
+ || immuneInfo.DispelImmuneMask
|| immuneInfo.DamageSchoolMask
|| !immuneInfo.AuraTypeImmune.empty()
|| !immuneInfo.SpellEffectImmune.empty())
@@ -3704,16 +3697,19 @@ void SpellInfo::ApplyAllSpellImmunitiesTo(Unit* target, SpellEffectInfo const& s
}
}
- if (uint32 dispelImmunity = immuneInfo->DispelImmune)
+ if (uint32 dispelImmunity = immuneInfo->DispelImmuneMask)
{
- target->ApplySpellImmune(Id, IMMUNITY_DISPEL, dispelImmunity, apply);
+ for (uint32 i = 0; i < DISPEL_MAX; ++i)
+ if (dispelImmunity & (1 << i))
+ target->ApplySpellImmune(Id, IMMUNITY_DISPEL, i, 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)
+ uint32 dispelMask = spellInfo->GetDispelMask();
+ if ((dispelMask & dispelImmunity) == dispelMask)
return true;
return false;
@@ -3769,7 +3765,7 @@ bool SpellInfo::CanSpellProvideImmunityAgainstAura(SpellInfo const* auraSpellInf
if ((mechanicImmunity & (UI64LIT(1) << auraSpellInfo->Mechanic)) != 0)
return true;
- if (uint32 dispelImmunity = immuneInfo->DispelImmune)
+ if (uint32 dispelImmunity = immuneInfo->DispelImmuneMask)
if (auraSpellInfo->Dispel == dispelImmunity)
return true;
diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h
index 1c968c94bc3..cbc56e7a079 100644
--- a/src/server/game/Spells/SpellInfo.h
+++ b/src/server/game/Spells/SpellInfo.h
@@ -273,7 +273,7 @@ public:
float CalcRadius(WorldObject* caster = nullptr, SpellTargetIndex targetIndex = SpellTargetIndex::TargetA, Spell* = nullptr) const;
uint32 GetProvidedTargetMask() const;
- uint32 GetMissingTargetMask(bool srcSet = false, bool destSet = false, uint32 mask = 0) const;
+ uint32 GetMissingTargetMask(bool srcSet = false, bool dstSet = false, uint32 mask = 0) const;
SpellEffectImplicitTargetTypes GetImplicitTargetType() const;
SpellTargetObjectTypes GetUsedTargetObjectType() const;
diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp
index ecc2af47afb..2d4fc015a91 100644
--- a/src/server/game/Spells/SpellMgr.cpp
+++ b/src/server/game/Spells/SpellMgr.cpp
@@ -32,6 +32,7 @@
#include "Spell.h"
#include "SpellAuraDefines.h"
#include "SpellInfo.h"
+#include "StringConvert.h"
#include <G3D/g3dmath.h>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/composite_key.hpp>
@@ -97,6 +98,7 @@ namespace
std::vector<ServersideSpellName> mServersideSpellNames;
std::unordered_map<std::pair<uint32, Difficulty>, SpellProcEntry> mSpellProcMap;
+ std::unordered_map<int32, CreatureImmunities> mCreatureImmunities;
}
PetFamilySpellsStore sPetFamilySpellsStore;
@@ -672,6 +674,11 @@ SpellAreaForAreaMapBounds SpellMgr::GetSpellAreaForAreaMapBounds(uint32 area_id)
return mSpellAreaForAreaMap.equal_range(area_id);
}
+CreatureImmunities const* SpellMgr::GetCreatureImmunities(int32 creatureImmunitiesId)
+{
+ return Trinity::Containers::MapGetValuePtr(mCreatureImmunities, creatureImmunitiesId);
+}
+
SpellInfo const* SpellMgr::GetSpellInfo(uint32 spellId, Difficulty difficulty) const
{
auto itr = mSpellInfoMap.find(boost::make_tuple(spellId, difficulty));
@@ -4946,6 +4953,52 @@ void SpellMgr::LoadSpellInfoImmunities()
{
uint32 oldMSTime = getMSTime();
+ mCreatureImmunities.clear();
+
+ // 0 1 2 3 4 5 6 7
+ 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].GetInt32();
+ uint8 school = fields[1].GetInt8();
+ uint16 dispelType = fields[2].GetInt16();
+ uint64 mechanics = fields[3].GetInt64();
+
+ CreatureImmunities& immunities = mCreatureImmunities[id];
+ immunities.School = school;
+ immunities.DispelType = dispelType;
+ immunities.Mechanic = mechanics;
+ immunities.ImmuneAoE = fields[6].GetBool();
+ immunities.ImmuneChain = fields[7].GetBool();
+
+ if (immunities.School.to_ullong() != school)
+ TC_LOG_ERROR("sql.sql", "Invalid value in `SchoolMask` {} for creature immunities {}, truncated", school, id);
+ if (immunities.DispelType.to_ullong() != dispelType)
+ TC_LOG_ERROR("sql.sql", "Invalid value in `DispelTypeMask` {} for creature immunities {}, truncated", dispelType, id);
+ if (immunities.Mechanic.to_ullong() != mechanics)
+ TC_LOG_ERROR("sql.sql", "Invalid value in `MechanicsMask` {} for creature immunities {}, truncated", mechanics, id);
+
+ for (std::string_view token : Trinity::Tokenize(fields[4].GetStringView(), ',', false))
+ {
+ if (Optional<uint32> effect = Trinity::StringTo<uint32>(token); effect && effect < uint32(TOTAL_SPELL_EFFECTS))
+ immunities.Effect.push_back(SpellEffectName(*effect));
+ else
+ TC_LOG_ERROR("sql.sql", "Invalid effect type in `Effects` {} for creature immunities {}, skipped", token, id);
+ }
+
+ for (std::string_view token : Trinity::Tokenize(fields[5].GetStringView(), ',', false))
+ {
+ if (Optional<uint32> aura = Trinity::StringTo<uint32>(token); aura && aura < TOTAL_AURAS)
+ immunities.Aura.push_back(AuraType(*aura));
+ else
+ TC_LOG_ERROR("sql.sql", "Invalid aura type in `Auras` {} for creature immunities {}, skipped", token, id);
+ }
+ }
+ while (result->NextRow());
+ }
+
for (SpellInfo const& spellInfo : mSpellInfoMap)
const_cast<SpellInfo&>(spellInfo)._LoadImmunityInfo();
diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h
index b547c154c79..95f67f2cdaa 100644
--- a/src/server/game/Spells/SpellMgr.h
+++ b/src/server/game/Spells/SpellMgr.h
@@ -30,7 +30,7 @@
#include "RaceMask.h"
#include "SharedDefines.h"
#include "SpellDefines.h"
-
+#include <bitset>
#include <functional>
#include <map>
#include <set>
@@ -64,6 +64,7 @@ struct SpellShapeshiftEntry;
struct SpellTargetRestrictionsEntry;
struct SpellTotemsEntry;
struct SpellXSpellVisualEntry;
+enum AuraType : uint32;
// only used in code
enum SpellCategories
@@ -593,6 +594,17 @@ struct SpellLearnSpellNode
bool AutoLearned; // This marks the spell as automatically learned from another source that - will only be used for unlearning
};
+struct CreatureImmunities
+{
+ std::bitset<MAX_SPELL_SCHOOL> School;
+ std::bitset<DISPEL_MAX> DispelType;
+ std::bitset<MAX_MECHANIC> Mechanic;
+ std::vector<SpellEffectName> Effect;
+ std::vector<AuraType> Aura;
+ bool ImmuneAoE = false; // NYI
+ bool ImmuneChain = false; // NYI
+};
+
typedef std::multimap<uint32, SpellLearnSpellNode> SpellLearnSpellMap;
typedef std::pair<SpellLearnSpellMap::const_iterator, SpellLearnSpellMap::const_iterator> SpellLearnSpellMapBounds;
@@ -745,6 +757,9 @@ class TC_GAME_API SpellMgr
SpellAreaForAuraMapBounds GetSpellAreaForAuraMapBounds(uint32 spell_id) const;
SpellAreaForAreaMapBounds GetSpellAreaForAreaMapBounds(uint32 area_id) const;
+ // Immunities
+ static CreatureImmunities const* GetCreatureImmunities(int32 creatureImmunitiesId);
+
// SpellInfo object management
SpellInfo const* GetSpellInfo(uint32 spellId, Difficulty difficulty) const;
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index df1e28ffada..ed6e2014ead 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -43,6 +43,7 @@ EndScriptData */
#include "Player.h"
#include "RBAC.h"
#include "SmartEnum.h"
+#include "SpellMgr.h"
#include "Transport.h"
#include "World.h"
#include "WorldSession.h"
@@ -482,7 +483,9 @@ public:
uint32 faction = target->GetFaction();
uint64 npcflags;
memcpy(&npcflags, target->m_unitData->NpcFlags.begin(), sizeof(npcflags));
- uint32 mechanicImmuneMask = cInfo->MechanicImmuneMask;
+ uint64 mechanicImmuneMask = 0;
+ if (CreatureImmunities const* immunities = SpellMgr::GetCreatureImmunities(cInfo->CreatureImmunitiesId))
+ mechanicImmuneMask = immunities->Mechanic.to_ullong();
uint32 displayid = target->GetDisplayId();
uint32 nativeid = target->GetNativeDisplayId();
uint32 entry = target->GetEntry();
@@ -557,9 +560,9 @@ public:
if (target->HasNpcFlag2(flag))
handler->PSendSysMessage("* %s (0x%X)", EnumUtils::ToTitle(flag), flag);
- handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, mechanicImmuneMask);
+ handler->PSendSysMessage(LANG_NPCINFO_MECHANIC_IMMUNE, Trinity::StringFormat("0x{:X}", mechanicImmuneMask).c_str());
for (Mechanics m : EnumUtils::Iterate<Mechanics>())
- if (m && (mechanicImmuneMask & (1 << (m - 1))))
+ if (m && (mechanicImmuneMask & (UI64LIT(1) << m)))
handler->PSendSysMessage("%s (0x%X)", EnumUtils::ToTitle(m), m);
return true;