diff options
-rw-r--r-- | sql/updates/world/master/2024_01_30_02_world.sql | 244 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/WorldDatabase.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.cpp | 57 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/Creature.h | 3 | ||||
-rw-r--r-- | src/server/game/Entities/Creature/CreatureData.h | 3 | ||||
-rw-r--r-- | src/server/game/Entities/Pet/Pet.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Unit/Unit.cpp | 8 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 15 | ||||
-rw-r--r-- | src/server/game/Miscellaneous/SharedDefines.h | 6 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 51 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.h | 22 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.cpp | 60 | ||||
-rw-r--r-- | src/server/game/Spells/SpellInfo.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/SpellMgr.cpp | 53 | ||||
-rw-r--r-- | src/server/game/Spells/SpellMgr.h | 17 | ||||
-rw-r--r-- | src/server/scripts/Commands/cs_npc.cpp | 9 |
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; |