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:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
void applyCreatureImmunities(CreatureImmunities immunities, bool apply)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
// unapply template immunities (in case we're updating entry)
|
||||
for (uint i = 0; i < (int)Mechanics.Max; ++i)
|
||||
ApplySpellImmune(placeholderSpellId, SpellImmunity.Mechanic, i, false);
|
||||
CreatureImmunities immunities = Global.SpellMgr.GetCreatureImmunities(_creatureImmunitiesId);
|
||||
if (immunities != null)
|
||||
applyCreatureImmunities(immunities, 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)
|
||||
// apply new immunities
|
||||
immunities = Global.SpellMgr.GetCreatureImmunities(creatureImmunitiesId);
|
||||
if (immunities != null)
|
||||
{
|
||||
for (uint i = 0 + 1; i < (int)Mechanics.Max; ++i)
|
||||
{
|
||||
if ((mechanicMask & (1ul << ((int)i - 1))) != 0)
|
||||
ApplySpellImmune(placeholderSpellId, SpellImmunity.Mechanic, i, true);
|
||||
_creatureImmunitiesId = creatureImmunitiesId;
|
||||
applyCreatureImmunities(immunities, true);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
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;
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
+37
-10
@@ -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
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user