aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sql/updates/world/master/2024_01_30_02_world.sql244
-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
16 files changed, 457 insertions, 97 deletions
diff --git a/sql/updates/world/master/2024_01_30_02_world.sql b/sql/updates/world/master/2024_01_30_02_world.sql
new file mode 100644
index 00000000000..0f86732e398
--- /dev/null
+++ b/sql/updates/world/master/2024_01_30_02_world.sql
@@ -0,0 +1,244 @@
+--
+-- Table structure for table `creature_immunities`
+--
+DROP TABLE IF EXISTS `creature_immunities`;
+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;
+
+--
+-- Dumping data for table `creature_immunities_temp`
+--
+INSERT INTO `creature_immunities` (`ID`,`SchoolMask`,`MechanicsMask`,`Effects`,`Auras`,`Comment`) VALUES
+(-2,4,0,'','',''),
+(-3,8,0,'','',''),
+(-4,16,0,'','',''),
+(-5,32,0,'','',''),
+(-6,52,0,'','',''),
+(-7,64,0,'','',''),
+(-8,84,0,'','',''),
+(-9,96,0,'','',''),
+(-10,126,0,'','',''),
+(-11,0,2,'','',''),
+(-12,0,16,'','',''),
+(-13,8,16,'','',''),
+(-14,0,18,'','',''),
+(-15,0,32,'','',''),
+(-16,64,32,'','',''),
+(-17,0,34,'','',''),
+(-18,0,40,'','',''),
+(-19,0,64,'','',''),
+(-20,0,512,'','',''),
+(-21,0,514,'','',''),
+(-22,0,1024,'','',''),
+(-23,0,1056,'','',''),
+(-24,0,2048,'','',''),
+(-25,0,2050,'','',''),
+(-26,0,2052,'','',''),
+(-27,0,2080,'','',''),
+(-28,0,2176,'','',''),
+(-29,0,2178,'','',''),
+(-30,0,2208,'','',''),
+(-31,0,2210,'','',''),
+(-32,0,2562,'','',''),
+(-33,0,3074,'','',''),
+(-34,0,4096,'','',''),
+(-35,0,4098,'','',''),
+(-36,0,4112,'','',''),
+(-37,0,4624,'','',''),
+(-38,0,6144,'','',''),
+(-39,64,6688,'','',''),
+(-40,0,6810,'','',''),
+(-41,0,8224,'','',''),
+(-42,64,10368,'','',''),
+(-43,0,14982,'','',''),
+(-44,0,16448,'','',''),
+(-45,0,21114,'','',''),
+(-46,0,32768,'','',''),
+(-47,0,32770,'','',''),
+(-48,0,36864,'','',''),
+(-49,0,65278,'','',''),
+(-50,0,131072,'','',''),
+(-51,0,131074,'','',''),
+(-52,0,131106,'','',''),
+(-53,0,132128,'','',''),
+(-54,0,133122,'','',''),
+(-55,0,133248,'','',''),
+(-56,0,133250,'','',''),
+(-57,0,135200,'','',''),
+(-58,0,135202,'','',''),
+(-59,0,139264,'','',''),
+(-60,0,139298,'','',''),
+(-61,0,141440,'','',''),
+(-62,0,143360,'','',''),
+(-63,0,262144,'','',''),
+(-64,0,262146,'','',''),
+(-65,0,262172,'','',''),
+(-66,0,268288,'','',''),
+(-67,0,274592,'','',''),
+(-68,0,278534,'','',''),
+(-69,0,404998,'','',''),
+(-70,0,530432,'','',''),
+(-71,0,16777248,'','',''),
+(-72,124,16777248,'','',''),
+(-73,126,16777248,'','',''),
+(-74,127,16777248,'','',''),
+(-75,0,16777250,'','',''),
+(-76,0,16777312,'','',''),
+(-77,0,16777344,'','',''),
+(-78,0,16779424,'','',''),
+(-79,0,16779428,'','',''),
+(-80,0,16779488,'','',''),
+(-81,0,16783392,'','',''),
+(-82,0,16791714,'','',''),
+(-83,0,16821412,'','',''),
+(-84,0,17039394,'','',''),
+(-85,0,17039520,'','',''),
+(-86,0,17045536,'','',''),
+(-87,0,17825824,'','',''),
+(-88,0,17826336,'','',''),
+(-89,0,25169952,'','',''),
+(-90,0,25574434,'','',''),
+(-91,0,26216098,'','',''),
+(-92,0,26624126,'','',''),
+(-93,0,29393010,'','',''),
+(-94,0,134221834,'','',''),
+(-95,0,134487696,'','',''),
+(-96,0,134487698,'','',''),
+(-97,0,142884558,'','',''),
+(-98,0,151262366,'','',''),
+(-99,0,151262398,'','',''),
+(-100,0,151273214,'','',''),
+(-101,0,151314166,'','',''),
+(-102,0,151390950,'','',''),
+(-103,0,160853686,'','',''),
+(-104,0,160857782,'','',''),
+(-105,0,160857790,'','',''),
+(-106,0,160857854,'','',''),
+(-107,0,161361662,'','',''),
+(-108,0,553648160,'','',''),
+(-109,0,688553716,'','',''),
+(-110,0,688553724,'','',''),
+(-111,0,1002405630,'','',''),
+(-112,0,1073741824,'','',''),
+(-113,0,1073758240,'','',''),
+(-114,0,1073873920,'','',''),
+(-115,0,1073882274,'','',''),
+(-116,8,1073882274,'','',''),
+(-117,0,1073882290,'','',''),
+(-118,0,1073915042,'','',''),
+(-119,0,1074009602,'','',''),
+(-120,0,1082677838,'','',''),
+(-121,0,1082686030,'','',''),
+(-122,0,1082686046,'','',''),
+(-123,0,1090659490,'','',''),
+(-124,0,1090664098,'','',''),
+(-125,0,1090679030,'','',''),
+(-126,0,1090936574,'','',''),
+(-127,0,1090940662,'','',''),
+(-128,0,1090940670,'','',''),
+(-129,0,1090944766,'','',''),
+(-130,0,1100093094,'','',''),
+(-131,0,1100248830,'','',''),
+(-132,0,1100377278,'','',''),
+(-133,0,1100904190,'','',''),
+(-134,0,1100939006,'','',''),
+(-135,0,1102476334,'','',''),
+(-136,0,1102477054,'','',''),
+(-137,0,1102872318,'','',''),
+(-138,0,1169581822,'','',''),
+(-139,0,1207969830,'','',''),
+(-140,0,1207983274,'','',''),
+(-141,0,1208099366,'','',''),
+(-142,0,1208245410,'','',''),
+(-143,0,1209413306,'','',''),
+(-144,0,1224758964,'','',''),
+(-145,0,1224758966,'','',''),
+(-146,0,1225030910,'','',''),
+(-147,0,1225064188,'','',''),
+(-148,0,1225162494,'','',''),
+(-149,0,1225195198,'','',''),
+(-150,0,1225424638,'','',''),
+(-151,0,1225588478,'','',''),
+(-152,0,1225686782,'','',''),
+(-153,0,1225719550,'','',''),
+(-154,0,1226194616,'','',''),
+(-155,0,1230420214,'','',''),
+(-156,0,1233419518,'','',''),
+(-157,0,1233551102,'','',''),
+(-158,0,1233682174,'','',''),
+(-159,0,1234075390,'','',''),
+(-160,0,1234337534,'','',''),
+(-161,0,1234595582,'','',''),
+(-162,0,1234597614,'','',''),
+(-163,0,1234599158,'','',''),
+(-164,0,1234599606,'','',''),
+(-165,1,1234599606,'','',''),
+(-166,4,1234599606,'','',''),
+(-167,8,1234599606,'','',''),
+(-168,64,1234599606,'','',''),
+(-169,124,1234599606,'','',''),
+(-170,0,1234599654,'','',''),
+(-171,0,1234599662,'','',''),
+(-172,0,1234599670,'','',''),
+(-173,0,1234599678,'','',''),
+(-174,16,1234599678,'','',''),
+(-175,0,1234632446,'','',''),
+(-176,0,1234992894,'','',''),
+(-177,0,1235119870,'','',''),
+(-178,4,1235119870,'','',''),
+(-179,0,1235123374,'','',''),
+(-180,0,1235123454,'','',''),
+(-181,0,1235123950,'','',''),
+(-182,0,1235123964,'','',''),
+(-183,0,1235123966,'','',''),
+(-184,0,1235156734,'','',''),
+(-185,0,1237221118,'','',''),
+(-186,0,1237745406,'','',''),
+(-187,0,1238793982,'','',''),
+(-188,0,1238794238,'','',''),
+(-189,0,1239318270,'','',''),
+(-190,0,1292271358,'','',''),
+(-191,0,1301706478,'','',''),
+(-192,0,1301708478,'','',''),
+(-193,0,1301708534,'','',''),
+(-194,0,1301708542,'','',''),
+(-195,0,1305903102,'','',''),
+(-196,0,1335787518,'','',''),
+(-197,0,1539276542,'','',''),
+(-198,0,1539309310,'','',''),
+(-199,0,1774857982,'','',''),
+(-200,0,1775140606,'','',''),
+(-201,0,2076147454,'','',''),
+(-202,127,2080374526,'','',''),
+(-203,0,3120561916,'','',''),
+(-204,0,4294967038,'','','');
+
+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`;
+
+UPDATE `trinity_string` SET
+ `content_default`=REPLACE(`content_default`,'%u','%s'),
+ `content_loc1`=REPLACE(`content_loc1`,'%u','%s'),
+ `content_loc2`=REPLACE(`content_loc2`,'%u','%s'),
+ `content_loc3`=REPLACE(`content_loc3`,'%u','%s'),
+ `content_loc4`=REPLACE(`content_loc4`,'%u','%s'),
+ `content_loc5`=REPLACE(`content_loc5`,'%u','%s'),
+ `content_loc6`=REPLACE(`content_loc6`,'%u','%s'),
+ `content_loc7`=REPLACE(`content_loc7`,'%u','%s'),
+ `content_loc8`=REPLACE(`content_loc8`,'%u','%s')
+WHERE `entry`=5037;
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;