Core/Creatures: Move immunities to separate table and implemented setting immunties to dispel, spell effects, aura types, aoe and chain targeting

Port From (https://github.com/TrinityCore/TrinityCore/commit/f70a5817e1c07891185d716611d45f50b1c73b78)
This commit is contained in:
hondacrx
2024-02-06 17:21:07 -05:00
parent f3993521bc
commit 7c812ae904
15 changed files with 216 additions and 98 deletions
+2
View File
@@ -45,6 +45,8 @@ namespace System.Collections
_version = 0;
}
public BitSet(ulong value) : this(new uint[] { (uint)(value & (ulong)uint.MaxValue), (uint)(value >> 32) }) { }
public BitSet(BitSet bits)
{
if (bits == null)
@@ -260,6 +260,7 @@ namespace Framework.Constants
Enrage = 9,
ZGTicket = 10,
OldUnused = 11,
Max,
AllMask = ((1 << Magic) | (1 << Curse) | (1 << Disease) | (1 << Poison))
}
@@ -1783,7 +1784,7 @@ namespace Framework.Constants
IgnoreCasterAndTargetRestrictions = 0x10000000, /*Nyi*/ // Ignore Caster & Target Restrictions
IgnoreCasterModifiers = 0x20000000, // Ignore Caster Modifiers
DoNotDisplayRange = 0x40000000, // Do Not Display Range (Client Only)
NotOnAoeImmune = 0x80000000 /*Nyi, No Aoe Immunity Implementation*/ // Not On Aoe Immune
NotOnAoeImmune = 0x80000000 // Not On Aoe Immune
}
public enum SpellAttr4 : uint
{
@@ -2981,4 +2982,10 @@ namespace Framework.Constants
TargetAndBeacon = 2,
TargetParty = 3,
}
public enum WorldObjectSpellAreaTargetSearchReason
{
Area,
Chain
}
}
@@ -46,7 +46,7 @@ namespace Framework.Database
PrepareStatement(WorldStatements.SEL_CREATURE_ADDON_BY_GUID, "SELECT guid FROM creature_addon WHERE guid = ?");
PrepareStatement(WorldStatements.DEL_CREATURE, "DELETE FROM creature WHERE guid = ?");
PrepareStatement(WorldStatements.SEL_COMMANDS, "SELECT name, help FROM command");
PrepareStatement(WorldStatements.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 = ?");
PrepareStatement(WorldStatements.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 = ?");
PrepareStatement(WorldStatements.SEL_CREATURE_BY_ID, "SELECT guid FROM creature WHERE id = ?");
PrepareStatement(WorldStatements.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_");
PrepareStatement(WorldStatements.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_");
@@ -186,6 +186,13 @@ namespace System.Collections.Generic
return blockValues;
}
public static uint ToUInt(this BitSet array)
{
uint[] blockValues = new uint[array.Length / 32 + 1];
array.CopyTo(blockValues, 0);
return blockValues[0];
}
public static void Clear(this Array array)
{
Array.Clear(array, 0, array.Length);
+8 -5
View File
@@ -83,7 +83,10 @@ namespace Game.Chat
uint faction = target.GetFaction();
ulong npcflags = (ulong)target.m_unitData.NpcFlags[1] << 32 | target.m_unitData.NpcFlags[0];
ulong mechanicImmuneMask = cInfo.MechanicImmuneMask;
ulong mechanicImmuneMask = 0;
CreatureImmunities immunities = Global.SpellMgr.GetCreatureImmunities(cInfo.CreatureImmunitiesId);
if (immunities != null)
mechanicImmuneMask = immunities.Mechanic.ToUInt();
uint displayid = target.GetDisplayId();
uint nativeid = target.GetNativeDisplayId();
uint entry = target.GetEntry();
@@ -110,17 +113,17 @@ namespace Game.Chat
handler.SendSysMessage(CypherStrings.NpcinfoUnitFieldFlags, (uint)target.m_unitData.Flags);
foreach (UnitFlags value in Enum.GetValues(typeof(UnitFlags)))
if (target.HasUnitFlag(value))
handler.SendSysMessage("{0} (0x{1:X})", (UnitFlags)value, value);
handler.SendSysMessage("{0} (0x{1:X})", value, value);
handler.SendSysMessage(CypherStrings.NpcinfoUnitFieldFlags2, (uint)target.m_unitData.Flags2);
foreach (UnitFlags2 value in Enum.GetValues(typeof(UnitFlags2)))
if (target.HasUnitFlag2(value))
handler.SendSysMessage("{0} (0x{1:X})", (UnitFlags2)value, value);
handler.SendSysMessage("{0} (0x{1:X})", value, value);
handler.SendSysMessage(CypherStrings.NpcinfoUnitFieldFlags3, (uint)target.m_unitData.Flags3);
foreach (UnitFlags3 value in Enum.GetValues(typeof(UnitFlags3)))
if (target.HasUnitFlag3(value))
handler.SendSysMessage("{0} (0x{1:X})", (UnitFlags3)value, value);
handler.SendSysMessage("{0} (0x{1:X})", value, value);
handler.SendSysMessage(CypherStrings.NpcinfoDynamicFlags, target.GetDynamicFlags());
handler.SendSysMessage(CypherStrings.CommandRawpawntimes, defRespawnDelayStr, curRespawnDelayStr);
@@ -153,7 +156,7 @@ namespace Game.Chat
if (npcflags.HasAnyFlag(value))
handler.SendSysMessage("{0} (0x{1:X})", (NPCFlags)value, value);
handler.SendSysMessage(CypherStrings.NpcinfoMechanicImmune, mechanicImmuneMask);
handler.SendSysMessage(CypherStrings.NpcinfoMechanicImmune, $"0x{mechanicImmuneMask:X}");
foreach (int value in Enum.GetValues(typeof(Mechanics)))
if (Convert.ToBoolean(mechanicImmuneMask & (1ul << (value - 1))))
handler.SendSysMessage("{0} (0x{1:X})", (Mechanics)value, value);
@@ -29,6 +29,7 @@ namespace Game.Entities
bool _isMissingCanSwimFlagOutOfCombat;
int _creatureImmunitiesId;
uint _gossipMenuId;
uint? _trainerId;
float _sparringHealthPct;
+34 -25
View File
@@ -397,7 +397,7 @@ namespace Game.Entities
LoadCreaturesAddon();
LoadCreaturesSparringHealth();
LoadTemplateImmunities();
LoadTemplateImmunities(cInfo.CreatureImmunitiesId);
GetThreatManager().EvaluateSuppressed();
//We must update last scriptId or it looks like we reloaded a script, breaking some things such as gossip temporarily
@@ -2177,38 +2177,47 @@ namespace Game.Entities
ForcedDespawn((uint)msTimeToDespawn.TotalMilliseconds, forceRespawnTimer);
}
public void LoadTemplateImmunities()
public void LoadTemplateImmunities(int 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
uint placeholderSpellId = uint.MaxValue;
// unapply template immunities (in case we're updating entry)
for (uint i = 0; i < (int)Mechanics.Max; ++i)
ApplySpellImmune(placeholderSpellId, SpellImmunity.Mechanic, i, false);
for (var i = (int)SpellSchools.Normal; i < (int)SpellSchools.Max; ++i)
ApplySpellImmune(placeholderSpellId, SpellImmunity.School, 1u << i, false);
// don't inherit immunities for hunter pets
if (GetOwnerGUID().IsPlayer() && IsHunterPet())
return;
ulong mechanicMask = GetCreatureTemplate().MechanicImmuneMask;
if (mechanicMask != 0)
void applyCreatureImmunities(CreatureImmunities immunities, bool apply)
{
for (uint i = 0 + 1; i < (int)Mechanics.Max; ++i)
{
if ((mechanicMask & (1ul << ((int)i - 1))) != 0)
ApplySpellImmune(placeholderSpellId, SpellImmunity.Mechanic, i, true);
}
for (var i = 0; i < immunities.School.Count; ++i)
if (immunities.School[i])
ApplySpellImmune(placeholderSpellId, SpellImmunity.School, 1u << i, apply);
for (int i = 0; i < immunities.DispelType.Count; ++i)
if (immunities.DispelType[i])
ApplySpellImmune(placeholderSpellId, SpellImmunity.Dispel, (uint)i, apply);
for (var i = 0; i < immunities.Mechanic.Count; ++i)
if (immunities.Mechanic[i])
ApplySpellImmune(placeholderSpellId, SpellImmunity.Mechanic, (uint)i, apply);
foreach (SpellEffectName effect in immunities.Effect)
ApplySpellImmune(placeholderSpellId, SpellImmunity.Effect, effect, apply);
foreach (AuraType aura in immunities.Aura)
ApplySpellImmune(placeholderSpellId, SpellImmunity.State, aura, apply);
}
uint schoolMask = GetCreatureTemplate().SpellSchoolImmuneMask;
if (schoolMask != 0)
for (var i = (int)SpellSchools.Normal; i <= (int)SpellSchools.Max; ++i)
if ((schoolMask & (1 << i)) != 0)
ApplySpellImmune(placeholderSpellId, SpellImmunity.School, 1u << i, true);
// unapply template immunities (in case we're updating entry)
CreatureImmunities immunities = Global.SpellMgr.GetCreatureImmunities(_creatureImmunitiesId);
if (immunities != null)
applyCreatureImmunities(immunities, false);
// apply new immunities
immunities = Global.SpellMgr.GetCreatureImmunities(creatureImmunitiesId);
if (immunities != null)
{
_creatureImmunitiesId = creatureImmunitiesId;
applyCreatureImmunities(immunities, true);
}
else
_creatureImmunitiesId = 0;
}
public override bool IsImmunedToSpellEffect(SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, WorldObject caster, bool requireImmunityPurgesEffectAttribute = false)
@@ -55,8 +55,7 @@ namespace Game.Entities
public int WidgetSetID;
public int WidgetSetUnitConditionID;
public bool RegenHealth;
public ulong MechanicImmuneMask;
public uint SpellSchoolImmuneMask;
public int CreatureImmunitiesId;
public CreatureFlagsExtra FlagsExtra;
public uint ScriptID;
public string StringId;
+1 -1
View File
@@ -370,7 +370,7 @@ namespace Game.Entities
owner.DisablePetControlsOnMount(ReactStates.Passive, CommandStates.Follow);
// must be after SetMinion (owner guid check)
LoadTemplateImmunities();
LoadTemplateImmunities(0);
m_loading = false;
});
+2 -2
View File
@@ -562,8 +562,8 @@ namespace Game.Entities
Creature creature1 = ToCreature();
if (creature1 != null)
{
ulong immuneMask = creature1.GetCreatureTemplate().MechanicImmuneMask;
if (Convert.ToBoolean(immuneMask & (1 << ((int)Mechanics.Snare - 1))) || Convert.ToBoolean(immuneMask & (1 << ((int)Mechanics.Daze - 1))))
CreatureImmunities immunities = Global.SpellMgr.GetCreatureImmunities(creature1.GetCreatureTemplate().CreatureImmunitiesId);
if (immunities != null && (immunities.Mechanic[(int)Mechanics.Snare] || immunities.Mechanic[(int)Mechanics.Daze]))
break;
}
+4 -5
View File
@@ -1840,11 +1840,10 @@ namespace Game
creature.WidgetSetID = fields.Read<int>(41);
creature.WidgetSetUnitConditionID = fields.Read<int>(42);
creature.RegenHealth = fields.Read<bool>(43);
creature.MechanicImmuneMask = fields.Read<ulong>(44);
creature.SpellSchoolImmuneMask = fields.Read<uint>(45);
creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(46);
creature.ScriptID = GetScriptId(fields.Read<string>(47));
creature.StringId = fields.Read<string>(48);
creature.CreatureImmunitiesId = fields.Read<int>(44);
creature.FlagsExtra = (CreatureFlagsExtra)fields.Read<uint>(45);
creature.ScriptID = GetScriptId(fields.Read<string>(46));
creature.StringId = fields.Read<string>(47);
creatureTemplateStorage[entry] = creature;
}
+12 -12
View File
@@ -1810,18 +1810,6 @@ namespace Game.Networking.Packets
}
}
public struct CreatureImmunities
{
public uint School;
public uint Value;
public void Write(WorldPacket data)
{
data.WriteUInt32(School);
data.WriteUInt32(Value);
}
}
public struct SpellHealPrediction
{
public ObjectGuid BeaconGUID;
@@ -1915,6 +1903,18 @@ namespace Game.Networking.Packets
public List<TargetLocation> TargetPoints = new();
public CreatureImmunities Immunities;
public SpellHealPrediction Predict;
public struct CreatureImmunities
{
public uint School;
public uint Value;
public void Write(WorldPacket data)
{
data.WriteUInt32(School);
data.WriteUInt32(Value);
}
}
}
public struct LearnedSpellInfo
+38 -11
View File
@@ -909,15 +909,15 @@ namespace Game.Spells
if (!m_caster.IsUnit() || !m_caster.ToUnit().IsInRaidWith(targetedUnit))
targets.Add(m_targets.GetUnitTarget());
else
SearchAreaTargets(targets, spellEffectInfo, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions);
SearchAreaTargets(targets, spellEffectInfo, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions, WorldObjectSpellAreaTargetSearchReason.Area);
}
break;
case Targets.UnitCasterAndSummons:
targets.Add(m_caster);
SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions);
SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions, WorldObjectSpellAreaTargetSearchReason.Area);
break;
default:
SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions);
SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions, WorldObjectSpellAreaTargetSearchReason.Area);
break;
}
@@ -1713,14 +1713,15 @@ namespace Game.Spells
return searcher.GetTarget();
}
void SearchAreaTargets(List<WorldObject> targets, SpellEffectInfo spellEffectInfo, float range, Position position, WorldObject referer, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, List<Condition> condList)
void SearchAreaTargets(List<WorldObject> targets, SpellEffectInfo spellEffectInfo, float range, Position position, WorldObject referer,
SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, List<Condition> condList, WorldObjectSpellAreaTargetSearchReason searchReason)
{
var containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
if (containerTypeMask == 0)
return;
float extraSearchRadius = range > 0.0f ? SharedConst.ExtraCellSearchRadius : 0.0f;
var check = new WorldObjectSpellAreaTargetCheck(range, position, m_caster, referer, m_spellInfo, selectionType, condList, objectType);
var check = new WorldObjectSpellAreaTargetCheck(range, position, m_caster, referer, m_spellInfo, selectionType, condList, objectType, searchReason);
var searcher = new WorldObjectListSearcher(m_caster, targets, check, containerTypeMask);
SearchTargets(searcher, containerTypeMask, m_caster, position, range + extraSearchRadius);
}
@@ -1764,7 +1765,7 @@ namespace Game.Spells
WorldObject chainSource = m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) ? m_caster : target;
List<WorldObject> tempTargets = new();
SearchAreaTargets(tempTargets, spellEffectInfo, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions);
SearchAreaTargets(tempTargets, spellEffectInfo, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions, WorldObjectSpellAreaTargetSearchReason.Chain);
tempTargets.Remove(target);
// remove targets which are always invalid for chain spells
@@ -3996,7 +3997,7 @@ namespace Game.Spells
foreach (var targetInfo in m_UniqueTargetInfo)
{
if (targetInfo.EffectMask == 0) // No effect apply - all immuned add state
// possibly SPELL_MISS_IMMUNE2 for this??
// possibly SPELL_MISS_IMMUNE2 for this??
targetInfo.MissCondition = SpellMissInfo.Immune2;
if (targetInfo.MissCondition == SpellMissInfo.None || (targetInfo.MissCondition == SpellMissInfo.Block && !m_spellInfo.HasAttribute(SpellAttr3.CompletelyBlocked))) // Add only hits and partial blocked
@@ -9155,21 +9156,24 @@ namespace Game.Spells
{
float _range;
Position _position;
WorldObjectSpellAreaTargetSearchReason _searchReason;
public WorldObjectSpellAreaTargetCheck(float range, Position position, WorldObject caster, WorldObject referer, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List<Condition> condList, SpellTargetObjectTypes objectType)
public WorldObjectSpellAreaTargetCheck(float range, Position position, WorldObject caster, WorldObject referer, SpellInfo spellInfo, SpellTargetCheckTypes selectionType, List<Condition> condList, SpellTargetObjectTypes objectType,
WorldObjectSpellAreaTargetSearchReason searchReason = WorldObjectSpellAreaTargetSearchReason.Area)
: base(caster, referer, spellInfo, selectionType, condList, objectType)
{
_range = range;
_position = position;
_searchReason = searchReason;
}
public override bool Invoke(WorldObject target)
{
if (target.ToGameObject() != null)
GameObject gameObjectTarget = target.ToGameObject();
if (gameObjectTarget != null)
{
// 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;
}
@@ -9178,6 +9182,29 @@ namespace Game.Spells
bool isInsideCylinder = target.IsWithinDist2d(_position, _range) && Math.Abs(target.GetPositionZ() - _position.GetPositionZ()) <= _range;
if (!isInsideCylinder)
return false;
Creature creatureTarget = target.ToCreature();
if (creatureTarget != null)
{
CreatureImmunities immunities = Global.SpellMgr.GetCreatureImmunities(creatureTarget.GetCreatureTemplate().CreatureImmunitiesId);
if (immunities != null)
{
switch (_searchReason)
{
case WorldObjectSpellAreaTargetSearchReason.Area:
if (immunities.ImmuneAoE)
return false;
break;
case WorldObjectSpellAreaTargetSearchReason.Chain:
if (immunities.ImmuneChain)
return false;
break;
default:
break;
}
}
}
}
return base.Invoke(target);
+32 -32
View File
@@ -12,6 +12,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Framework.Collections;
namespace Game.Spells
{
@@ -1105,6 +1106,13 @@ namespace Game.Spells
if (HasAttribute(SpellAttr5.NotOnPlayerControlledNpc) && unitTarget.IsControlledByPlayer())
return SpellCastResult.TargetIsPlayerControlled;
if (HasAttribute(SpellAttr3.NotOnAoeImmune))
{
CreatureImmunities immunities = Global.SpellMgr.GetCreatureImmunities(unitTarget.ToCreature().GetCreatureTemplate().CreatureImmunitiesId);
if (immunities != null && immunities.ImmuneAoE)
return SpellCastResult.BadTargets;
}
}
else if (HasAttribute(SpellAttr5.NotOnPlayer))
return SpellCastResult.TargetIsPlayer;
@@ -2134,7 +2142,7 @@ namespace Game.Spells
uint schoolImmunityMask = 0;
uint applyHarmfulAuraImmunityMask = 0;
ulong mechanicImmunityMask = 0;
uint dispelImmunity = 0;
uint dispelImmunityMask = 0;
uint damageImmunityMask = 0;
int miscVal = effect.MiscValue;
@@ -2146,6 +2154,18 @@ namespace Game.Spells
{
case AuraType.MechanicImmunityMask:
{
CreatureImmunities creatureImmunities = Global.SpellMgr.GetCreatureImmunities(miscVal);
if (creatureImmunities != null)
{
schoolImmunityMask |= creatureImmunities.School.ToUInt();
dispelImmunityMask |= creatureImmunities.DispelType.ToUInt();
mechanicImmunityMask |= creatureImmunities.Mechanic.ToUInt();
foreach (SpellEffectName effectType in creatureImmunities.Effect)
immuneInfo.SpellEffectImmune.Add(effectType);
foreach (AuraType aura in creatureImmunities.Aura)
immuneInfo.AuraTypeImmune.Add(aura);
}
switch (miscVal)
{
case 96: // Free Friend, Uncontrollable Frenzy, Warlord's Presence
@@ -2286,29 +2306,6 @@ namespace Game.Spells
default:
break;
}
if (immuneInfo.AuraTypeImmune.Empty())
{
if (miscVal.HasAnyFlag(1 << 10))
immuneInfo.AuraTypeImmune.Add(AuraType.ModStun);
if (miscVal.HasAnyFlag(1 << 1))
immuneInfo.AuraTypeImmune.Add(AuraType.Transform);
// These flag can be recognized wrong:
if (miscVal.HasAnyFlag(1 << 6))
immuneInfo.AuraTypeImmune.Add(AuraType.ModDecreaseSpeed);
if (miscVal.HasAnyFlag(1 << 0))
{
immuneInfo.AuraTypeImmune.Add(AuraType.ModRoot);
immuneInfo.AuraTypeImmune.Add(AuraType.ModRoot2);
}
if (miscVal.HasAnyFlag(1 << 2))
immuneInfo.AuraTypeImmune.Add(AuraType.ModConfuse);
if (miscVal.HasAnyFlag(1 << 9))
immuneInfo.AuraTypeImmune.Add(AuraType.ModFear);
if (miscVal.HasAnyFlag(1 << 7))
immuneInfo.AuraTypeImmune.Add(AuraType.ModDisarm);
}
break;
}
case AuraType.MechanicImmunity:
@@ -2370,7 +2367,7 @@ namespace Game.Spells
}
case AuraType.DispelImmunity:
{
dispelImmunity = (uint)miscVal;
dispelImmunityMask = 1u << miscVal;
break;
}
default:
@@ -2380,7 +2377,7 @@ namespace Game.Spells
immuneInfo.SchoolImmuneMask = schoolImmunityMask;
immuneInfo.ApplyHarmfulAuraImmuneMask = applyHarmfulAuraImmunityMask;
immuneInfo.MechanicImmuneMask = mechanicImmunityMask;
immuneInfo.DispelImmune = dispelImmunity;
immuneInfo.DispelImmuneMask = dispelImmunityMask;
immuneInfo.DamageSchoolMask = damageImmunityMask;
_allowedMechanicMask |= immuneInfo.MechanicImmuneMask;
@@ -2513,17 +2510,20 @@ namespace Game.Spells
}
}
uint dispelImmunity = immuneInfo.DispelImmune;
uint dispelImmunity = immuneInfo.DispelImmuneMask;
if (dispelImmunity != 0)
{
target.ApplySpellImmune(Id, SpellImmunity.Dispel, dispelImmunity, apply);
for (int i = 0; i < (int)DispelType.Max; ++i)
if ((dispelImmunity & (1u << i)) != 0)
target.ApplySpellImmune(Id, SpellImmunity.Dispel, (uint)i, apply);
if (apply && HasAttribute(SpellAttr1.ImmunityPurgesEffect))
{
target.RemoveAppliedAuras(aurApp =>
{
SpellInfo spellInfo = aurApp.GetBase().GetSpellInfo();
if ((uint)spellInfo.Dispel == dispelImmunity)
uint dispelMask = spellInfo.GetDispelMask();
if ((dispelMask & dispelImmunity) == dispelMask)
return true;
return false;
@@ -2582,7 +2582,7 @@ namespace Game.Spells
if ((mechanicImmunity & (1ul << (int)auraSpellInfo.Mechanic)) != 0)
return true;
uint dispelImmunity = immuneInfo.DispelImmune;
uint dispelImmunity = immuneInfo.DispelImmuneMask;
if (dispelImmunity != 0)
if ((uint)auraSpellInfo.Dispel == dispelImmunity)
return true;
@@ -3883,7 +3883,7 @@ namespace Game.Spells
public void _UnloadImplicitTargetConditionLists()
{
// find the same instances of ConditionList and delete them.
foreach (SpellEffectInfo effect in _effects)
foreach (SpellEffectInfo effect in _effects)
effect.ImplicitTargetConditions = null;
}
@@ -5294,7 +5294,7 @@ namespace Game.Spells
public uint SchoolImmuneMask;
public uint ApplyHarmfulAuraImmuneMask;
public ulong MechanicImmuneMask;
public uint DispelImmune;
public uint DispelImmuneMask;
public uint DamageSchoolMask;
public List<AuraType> AuraTypeImmune = new();
+65 -1
View File
@@ -1,6 +1,7 @@
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
using Framework.Collections;
using Framework.Constants;
using Framework.Database;
using Framework.Dynamic;
@@ -12,6 +13,7 @@ using Game.Miscellaneous;
using Game.Movement;
using Game.Spells;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@@ -603,6 +605,11 @@ namespace Game.Entities
return mSpellAreaForAreaMap.LookupByKey(area_id);
}
public CreatureImmunities GetCreatureImmunities(int creatureImmunitiesId)
{
return mCreatureImmunities.LookupByKey(creatureImmunitiesId);
}
public SpellInfo GetSpellInfo(uint spellId, Difficulty difficulty)
{
var list = mSpellInfoMap.LookupByKey(spellId);
@@ -4502,6 +4509,52 @@ namespace Game.Entities
{
uint oldMSTime = Time.GetMSTime();
mCreatureImmunities.Clear();
// 0 1 2 3 4 5 6 7
SQLResult result = DB.World.Query("SELECT ID, SchoolMask, DispelTypeMask, MechanicsMask, Effects, Auras, ImmuneAoE, ImmuneChain FROM creature_immunities");
if (!result.IsEmpty())
{
do
{
int id = result.Read<int>(0);
byte school = result.Read<byte>(1);
ushort dispelType = result.Read<ushort>(2);
ulong mechanics = result.Read<ulong>(3);
CreatureImmunities immunities = mCreatureImmunities[id];
immunities.School = new BitSet(new uint[] { school });
immunities.DispelType = new BitSet(new uint[] { dispelType });
immunities.Mechanic = new BitSet(mechanics);
immunities.ImmuneAoE = result.Read<bool>(6);
immunities.ImmuneChain = result.Read<bool>(7);
if (immunities.School.ToUInt() != school)
Log.outError(LogFilter.Sql, $"Invalid value in `SchoolMask` {school} for creature immunities {id}, truncated");
if (immunities.DispelType.ToUInt() != dispelType)
Log.outError(LogFilter.Sql, $"Invalid value in `DispelTypeMask` {dispelType} for creature immunities {id}, truncated");
if (immunities.Mechanic.ToUInt() != mechanics)
Log.outError(LogFilter.Sql, $"Invalid value in `MechanicsMask` {mechanics} for creature immunities {id}, truncated");
foreach (string token in new StringArray(result.Read<string>(4), ','))
{
if (uint.TryParse(token, out uint effect) && effect != 0 && effect < (int)SpellEffectName.TotalSpellEffects)
immunities.Effect.Add((SpellEffectName)effect);
else
Log.outError(LogFilter.Sql, $"Invalid effect type in `Effects` {token} for creature immunities {id}, skipped");
}
foreach (string token in new StringArray(result.Read<string>(5), ','))
{
if (uint.TryParse(token, out uint aura) && aura != 0 && aura < (int)AuraType.Total)
immunities.Aura.Add((AuraType)aura);
else
Log.outError(LogFilter.Sql, $"Invalid aura type in `Auras` {token} for creature immunities {id}, skipped");
}
}
while (result.NextRow());
}
foreach (SpellInfo spellInfo in mSpellInfoMap.Values)
{
if (spellInfo == null)
@@ -4811,6 +4864,7 @@ namespace Game.Entities
MultiMap<SpellGroup, AuraType> mSpellSameEffectStack = new();
List<ServersideSpellName> mServersideSpellNames = new();
Dictionary<(uint id, Difficulty difficulty), SpellProcEntry> mSpellProcMap = new();
Dictionary<int, CreatureImmunities> mCreatureImmunities = new();
Dictionary<uint, SpellThreatEntry> mSpellThreatMap = new();
Dictionary<uint, PetAura> mSpellPetAuraMap = new();
MultiMap<(SpellLinkedType, uint), int> mSpellLinkedMap = new();
@@ -4920,7 +4974,6 @@ namespace Game.Entities
}
}
public class PetDefaultSpellsEntry
{
public uint[] spellid = new uint[4];
@@ -5097,4 +5150,15 @@ namespace Game.Entities
public float target_Z;
public float target_Orientation;
}
public class CreatureImmunities
{
public BitSet School = new((int)SpellSchools.Max);
public BitSet DispelType = new((int)Framework.Constants.DispelType.Max);
public BitSet Mechanic = new((int)Mechanics.Max);
public List<SpellEffectName> Effect = new();
public List<AuraType> Aura = new();
public bool ImmuneAoE; // NYI
public bool ImmuneChain; // NYI
}
}