Core/Spells: Defined and implemented more spell effect attributes - AlwaysAoeLineOfSight, IsHarmful, PlayersOnly, EnforceLineOfSightToChainTargets

Port From (https://github.com/TrinityCore/TrinityCore/commit/69317c25947e682036813aa7f6e9200056232a13)
This commit is contained in:
hondacrx
2024-01-30 10:39:11 -05:00
parent 48089c8a45
commit 0b81750b42
6 changed files with 81 additions and 51 deletions
+21 -5
View File
@@ -1973,12 +1973,28 @@ namespace Framework.Constants
public enum SpellEffectAttributes public enum SpellEffectAttributes
{ {
None = 0, None = 0,
UnaffectedByInvulnerability = 0x01, // not cancelled by immunities NoImmunity = 0x01, /*NYI*/ // not cancelled by immunities
NoScaleWithStack = 0x40, PositionIsFacingRelative = 0x02, /*NYI*/
JumpChargeUnitMeleeRange = 0x04, /*NYI*/
JumpChargeUnitStrictPathCheck = 0x08, /*NYI*/
ExcludeOwnParty = 0x10, /*NYI*/
AlwaysAoeLineOfSight = 0x20,
SuppressPointsStacking = 0x40,
ChainFromInitialTarget = 0x80, ChainFromInitialTarget = 0x80,
StackAuraAmountOnRecast = 0x8000, // refreshing periodic auras with this attribute will add remaining damage to new aura UncontrolledNoBackwards = 0x100, /*NYI*/
AllowAnyExplicitTarget = 0x100000, AuraPointsStack = 0x000200, /*NYI*/ // refreshing periodic auras with this attribute will add remaining damage to new aura
IgnoreDuringCooldownTimeRateCalculation = 0x800000 NoCopyDamageInterruptsOrProcs = 0x400, /*NYI*/
AddTargetCombatReachToAOE = 0x800, /*NYI*/
IsHarmful = 0x1000,
ForceScaleToOverrideCameraMinHeight = 0x2000, /*NYI*/
PlayersOnly = 0x004000,
ComputePointsOnlyAtCastTime = 0x8000, /*NYI*/
EnforceLineOfSightToChainTargets = 0x10000,
AreaEffectsUseTargetRadius = 0x20000, /*NYI*/
TeleportWithVehicle = 0x40000, /*NYI*/
ScalePointsByChallengeModeDamageScaler = 0x80000, /*NYI*/
DontFailSpellOnTargetingFailure = 0x100000, /*NYI*/
IgnoreDuringCooldownTimeRateCalculation = 0x800000, /*NYI*/
} }
public enum SpellProcsPerMinuteModType : byte public enum SpellProcsPerMinuteModType : byte
+1 -1
View File
@@ -1337,7 +1337,7 @@ namespace Game.Maps
{ {
GridMapTypeMask i_mapTypeMask; GridMapTypeMask i_mapTypeMask;
List<WorldObject> i_objects; List<WorldObject> i_objects;
PhaseShift i_phaseShift; public PhaseShift i_phaseShift;
ICheck<WorldObject> i_check; ICheck<WorldObject> i_check;
public WorldObjectListSearcher(WorldObject searcher, List<WorldObject> objects, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All) public WorldObjectListSearcher(WorldObject searcher, List<WorldObject> objects, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
+14 -9
View File
@@ -2748,7 +2748,7 @@ namespace Game.Spells
if (GetUnitOwner().HasUnitState(UnitState.Isolated)) if (GetUnitOwner().HasUnitState(UnitState.Isolated))
continue; continue;
List<Unit> units = new(); List<WorldObject> units = new();
var condList = spellEffectInfo.ImplicitTargetConditions; var condList = spellEffectInfo.ImplicitTargetConditions;
float radius = spellEffectInfo.CalcRadius(refe); float radius = spellEffectInfo.CalcRadius(refe);
@@ -2804,20 +2804,25 @@ namespace Game.Spells
if (selectionType != SpellTargetCheckTypes.Default) if (selectionType != SpellTargetCheckTypes.Default)
{ {
WorldObjectSpellAreaTargetCheck check = new(radius, GetUnitOwner(), refe, GetUnitOwner(), GetSpellInfo(), selectionType, condList, SpellTargetObjectTypes.Unit); var containerTypeMask = Spell.GetSearcherTypeMask(GetSpellInfo(), spellEffectInfo, SpellTargetObjectTypes.Unit, condList);
UnitListSearcher searcher = new(GetUnitOwner(), units, check); if (containerTypeMask != 0)
Cell.VisitAllObjects(GetUnitOwner(), searcher, radius + extraSearchRadius); {
WorldObjectSpellAreaTargetCheck check = new(radius, GetUnitOwner(), refe, GetUnitOwner(), GetSpellInfo(), selectionType, condList, SpellTargetObjectTypes.Unit);
WorldObjectListSearcher searcher = new(GetUnitOwner(), units, check, containerTypeMask);
searcher.i_phaseShift = PhasingHandler.GetAlwaysVisiblePhaseShift();
Spell.SearchTargets(searcher, containerTypeMask, GetUnitOwner(), GetUnitOwner(), radius + extraSearchRadius);
// by design WorldObjectSpellAreaTargetCheck allows not-in-world units (for spells) but for auras it is not acceptable // by design WorldObjectSpellAreaTargetCheck allows not-in-world units (for spells) but for auras it is not acceptable
units.RemoveAll(unit => !unit.IsSelfOrInSameMap(GetUnitOwner())); units.RemoveAll(unit => !unit.IsSelfOrInSameMap(GetUnitOwner()));
}
} }
foreach (Unit unit in units) foreach (WorldObject unit in units)
{ {
if (!targets.ContainsKey(unit)) if (!targets.ContainsKey(unit))
targets[unit] = 0; targets[unit.ToUnit()] = 0;
targets[unit] |= 1u << (int)spellEffectInfo.EffectIndex; targets[unit.ToUnit()] |= 1u << (int)spellEffectInfo.EffectIndex;
} }
} }
} }
+5 -5
View File
@@ -116,7 +116,7 @@ namespace Game.Spells
} }
GetBase().CallScriptEffectCalcAmountHandlers(this, ref amount, ref m_canBeRecalculated); GetBase().CallScriptEffectCalcAmountHandlers(this, ref amount, ref m_canBeRecalculated);
if (!GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack)) if (!GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.SuppressPointsStacking))
amount *= GetBase().GetStackAmount(); amount *= GetBase().GetStackAmount();
_estimatedAmount = CalculateEstimatedAmount(caster, amount); _estimatedAmount = CalculateEstimatedAmount(caster, amount);
@@ -126,7 +126,7 @@ namespace Game.Spells
public static float? CalculateEstimatedAmount(Unit caster, Unit target, SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, int amount, byte stack, AuraEffect aurEff) public static float? CalculateEstimatedAmount(Unit caster, Unit target, SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, int amount, byte stack, AuraEffect aurEff)
{ {
uint stackAmountForBonuses = !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? stack : 1u; uint stackAmountForBonuses = !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.SuppressPointsStacking) ? stack : 1u;
switch (spellEffectInfo.ApplyAuraName) switch (spellEffectInfo.ApplyAuraName)
{ {
@@ -5057,7 +5057,7 @@ namespace Game.Spells
CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal); CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal);
uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? GetBase().GetStackAmount() : 1u; uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.SuppressPointsStacking) ? GetBase().GetStackAmount() : 1u;
// ignore non positive values (can be result apply spellmods to aura damage // ignore non positive values (can be result apply spellmods to aura damage
uint damage = (uint)Math.Max(GetAmount(), 0); uint damage = (uint)Math.Max(GetAmount(), 0);
@@ -5184,7 +5184,7 @@ namespace Game.Spells
CleanDamage cleanDamage = new(0, 0, GetSpellInfo().GetAttackType(), MeleeHitOutcome.Normal); CleanDamage cleanDamage = new(0, 0, GetSpellInfo().GetAttackType(), MeleeHitOutcome.Normal);
uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? GetBase().GetStackAmount() : 1u; uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.SuppressPointsStacking) ? GetBase().GetStackAmount() : 1u;
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
uint damage = (uint)Math.Max(GetAmount(), 0); uint damage = (uint)Math.Max(GetAmount(), 0);
@@ -5311,7 +5311,7 @@ namespace Game.Spells
if (GetBase().IsPermanent() && target.IsFullHealth()) if (GetBase().IsPermanent() && target.IsFullHealth())
return; return;
uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? GetBase().GetStackAmount() : 1u; uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.SuppressPointsStacking) ? GetBase().GetStackAmount() : 1u;
// ignore negative values (can be result apply spellmods to aura damage // ignore negative values (can be result apply spellmods to aura damage
uint damage = (uint)Math.Max(GetAmount(), 0); uint damage = (uint)Math.Max(GetAmount(), 0);
+37 -31
View File
@@ -398,6 +398,7 @@ namespace Game.Spells
spellEffectInfo.ImplicitTargetConditions == effects[j].ImplicitTargetConditions && spellEffectInfo.ImplicitTargetConditions == effects[j].ImplicitTargetConditions &&
spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex.TargetA) == effects[j].CalcRadius(m_caster, SpellTargetIndex.TargetA) && spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex.TargetA) == effects[j].CalcRadius(m_caster, SpellTargetIndex.TargetA) &&
spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex.TargetB) == effects[j].CalcRadius(m_caster, SpellTargetIndex.TargetB) && spellEffectInfo.CalcRadius(m_caster, SpellTargetIndex.TargetB) == effects[j].CalcRadius(m_caster, SpellTargetIndex.TargetB) &&
spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.PlayersOnly) == effects[j].EffectAttributes.HasFlag(SpellEffectAttributes.PlayersOnly) &&
CheckScriptEffectImplicitTargets(spellEffectInfo.EffectIndex, (uint)j)) CheckScriptEffectImplicitTargets(spellEffectInfo.EffectIndex, (uint)j))
{ {
effectMask |= 1u << j; effectMask |= 1u << j;
@@ -658,7 +659,7 @@ namespace Game.Spells
} }
} }
WorldObject target = SearchNearbyTarget(range, targetType.GetObjectType(), targetType.GetCheckType(), condList); WorldObject target = SearchNearbyTarget(spellEffectInfo, range, targetType.GetObjectType(), targetType.GetCheckType(), condList);
float randomRadius = 0.0f; float randomRadius = 0.0f;
switch (targetType.GetTarget()) switch (targetType.GetTarget())
{ {
@@ -782,7 +783,7 @@ namespace Game.Spells
var condList = spellEffectInfo.ImplicitTargetConditions; var condList = spellEffectInfo.ImplicitTargetConditions;
float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue.RadiusMod; float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue.RadiusMod;
GridMapTypeMask containerTypeMask = GetSearcherTypeMask(objectType, condList); GridMapTypeMask containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
if (containerTypeMask != 0) if (containerTypeMask != 0)
{ {
float extraSearchRadius = radius > 0.0f ? SharedConst.ExtraCellSearchRadius : 0.0f; float extraSearchRadius = radius > 0.0f ? SharedConst.ExtraCellSearchRadius : 0.0f;
@@ -892,15 +893,15 @@ namespace Game.Spells
if (!m_caster.IsUnit() || !m_caster.ToUnit().IsInRaidWith(targetedUnit)) if (!m_caster.IsUnit() || !m_caster.ToUnit().IsInRaidWith(targetedUnit))
targets.Add(m_targets.GetUnitTarget()); targets.Add(m_targets.GetUnitTarget());
else else
SearchAreaTargets(targets, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); SearchAreaTargets(targets, spellEffectInfo, radius, targetedUnit, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions);
} }
break; break;
case Targets.UnitCasterAndSummons: case Targets.UnitCasterAndSummons:
targets.Add(m_caster); targets.Add(m_caster);
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions);
break; break;
default: default:
SearchAreaTargets(targets, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions); SearchAreaTargets(targets, spellEffectInfo, radius, center, referer, targetType.GetObjectType(), targetType.GetCheckType(), spellEffectInfo.ImplicitTargetConditions);
break; break;
} }
@@ -1484,7 +1485,7 @@ namespace Game.Spells
var condList = spellEffectInfo.ImplicitTargetConditions; var condList = spellEffectInfo.ImplicitTargetConditions;
float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue.RadiusMod; float radius = spellEffectInfo.CalcRadius(m_caster, targetIndex) * m_spellValue.RadiusMod;
GridMapTypeMask containerTypeMask = GetSearcherTypeMask(objectType, condList); GridMapTypeMask containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
if (containerTypeMask != 0) if (containerTypeMask != 0)
{ {
WorldObjectSpellLineTargetCheck check = new(m_caster, dst, m_spellInfo.Width != 0 ? m_spellInfo.Width : m_caster.GetCombatReach(), radius, m_caster, m_spellInfo, selectionType, condList, objectType); WorldObjectSpellLineTargetCheck check = new(m_caster, dst, m_spellInfo.Width != 0 ? m_spellInfo.Width : m_caster.GetCombatReach(), radius, m_caster, m_spellInfo, selectionType, condList, objectType);
@@ -1625,7 +1626,7 @@ namespace Game.Spells
} }
} }
public GridMapTypeMask GetSearcherTypeMask(SpellTargetObjectTypes objType, List<Condition> condList) public static GridMapTypeMask GetSearcherTypeMask(SpellInfo spellInfo, SpellEffectInfo spellEffectInfo, SpellTargetObjectTypes objType, List<Condition> condList)
{ {
// this function selects which containers need to be searched for spell target // this function selects which containers need to be searched for spell target
GridMapTypeMask retMask = GridMapTypeMask.All; GridMapTypeMask retMask = GridMapTypeMask.All;
@@ -1650,11 +1651,11 @@ namespace Game.Spells
break; break;
} }
if (m_spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer)) if (spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer) || spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.PlayersOnly))
retMask &= GridMapTypeMask.Corpse | GridMapTypeMask.Player; retMask &= GridMapTypeMask.Corpse | GridMapTypeMask.Player;
if (m_spellInfo.HasAttribute(SpellAttr3.OnlyOnGhosts)) if (spellInfo.HasAttribute(SpellAttr3.OnlyOnGhosts))
retMask &= GridMapTypeMask.Player; retMask &= GridMapTypeMask.Player;
if (m_spellInfo.HasAttribute(SpellAttr5.NotOnPlayer)) if (spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
retMask &= ~GridMapTypeMask.Player; retMask &= ~GridMapTypeMask.Player;
if (condList != null) if (condList != null)
@@ -1662,7 +1663,7 @@ namespace Game.Spells
return retMask; return retMask;
} }
void SearchTargets(Notifier notifier, GridMapTypeMask containerMask, WorldObject referer, Position pos, float radius) public static void SearchTargets(Notifier notifier, GridMapTypeMask containerMask, WorldObject referer, Position pos, float radius)
{ {
if (containerMask == 0) if (containerMask == 0)
return; return;
@@ -1688,20 +1689,21 @@ namespace Game.Spells
} }
} }
WorldObject SearchNearbyTarget(float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, List<Condition> condList) WorldObject SearchNearbyTarget(SpellEffectInfo spellEffectInfo, float range, SpellTargetObjectTypes objectType, SpellTargetCheckTypes selectionType, List<Condition> condList)
{ {
GridMapTypeMask containerTypeMask = GetSearcherTypeMask(objectType, condList); GridMapTypeMask containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
if (containerTypeMask == 0) if (containerTypeMask == 0)
return null; return null;
var check = new WorldObjectSpellNearbyTargetCheck(range, m_caster, m_spellInfo, selectionType, condList, objectType); var check = new WorldObjectSpellNearbyTargetCheck(range, m_caster, m_spellInfo, selectionType, condList, objectType);
var searcher = new WorldObjectLastSearcher(m_caster, check, containerTypeMask); var searcher = new WorldObjectLastSearcher(m_caster, check, containerTypeMask);
SearchTargets(searcher, containerTypeMask, m_caster, m_caster.GetPosition(), range); SearchTargets(searcher, containerTypeMask, m_caster, m_caster.GetPosition(), range);
return searcher.GetTarget(); return searcher.GetTarget();
} }
void SearchAreaTargets(List<WorldObject> targets, 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)
{ {
var containerTypeMask = GetSearcherTypeMask(objectType, condList); var containerTypeMask = GetSearcherTypeMask(m_spellInfo, spellEffectInfo, objectType, condList);
if (containerTypeMask == 0) if (containerTypeMask == 0)
return; return;
@@ -1750,7 +1752,7 @@ namespace Game.Spells
WorldObject chainSource = m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) ? m_caster : target; WorldObject chainSource = m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) ? m_caster : target;
List<WorldObject> tempTargets = new(); List<WorldObject> tempTargets = new();
SearchAreaTargets(tempTargets, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions); SearchAreaTargets(tempTargets, spellEffectInfo, searchRadius, chainSource, m_caster, objectType, selectType, spellEffectInfo.ImplicitTargetConditions);
tempTargets.Remove(target); tempTargets.Remove(target);
// remove targets which are always invalid for chain spells // remove targets which are always invalid for chain spells
@@ -1761,7 +1763,7 @@ namespace Game.Spells
while (chainTargets != 0) while (chainTargets != 0)
{ {
// try to get unit for next chain jump // try to get unit for next chain jump
WorldObject found = null; WorldObject foundObj = null;
// get unit with highest hp deficit in dist // get unit with highest hp deficit in dist
if (isChainHeal) if (isChainHeal)
{ {
@@ -1772,9 +1774,9 @@ namespace Game.Spells
if (unitTarget != null) if (unitTarget != null)
{ {
uint deficit = (uint)(unitTarget.GetMaxHealth() - unitTarget.GetHealth()); uint deficit = (uint)(unitTarget.GetMaxHealth() - unitTarget.GetHealth());
if ((deficit > maxHPDeficit || found == null) && chainSource.IsWithinDist(unitTarget, jumpRadius) && IsWithinLOS(chainSource, unitTarget, false, ModelIgnoreFlags.M2)) if ((deficit > maxHPDeficit || foundObj == null) && chainSource.IsWithinDist(unitTarget, jumpRadius) && IsWithinLOS(chainSource, unitTarget, false, ModelIgnoreFlags.M2))
{ {
found = obj; foundObj = obj;
maxHPDeficit = deficit; maxHPDeficit = deficit;
} }
} }
@@ -1785,24 +1787,28 @@ namespace Game.Spells
{ {
foreach (var obj in tempTargets) foreach (var obj in tempTargets)
{ {
if (found == null) bool isBestDistanceMatch = foundObj != null ? chainSource.GetDistanceOrder(obj, foundObj) : chainSource.IsWithinDist(obj, jumpRadius);
{ if (!isBestDistanceMatch)
if (chainSource.IsWithinDist(obj, jumpRadius) && IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2)) continue;
found = obj;
} if (!IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2))
else if (chainSource.GetDistanceOrder(obj, found) && IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2)) continue;
found = obj;
if (spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.EnforceLineOfSightToChainTargets) && !IsWithinLOS(m_caster, obj, false, ModelIgnoreFlags.M2))
continue;
foundObj = obj;
} }
} }
// not found any valid target - chain ends // not found any valid target - chain ends
if (found == null) if (foundObj == null)
break; break;
if (!m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) && !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.ChainFromInitialTarget)) if (!m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) && !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.ChainFromInitialTarget))
chainSource = found; chainSource = foundObj;
targets.Add(found); targets.Add(foundObj);
tempTargets.Remove(found); tempTargets.Remove(foundObj);
--chainTargets; --chainTargets;
} }
} }
@@ -7118,7 +7124,7 @@ namespace Game.Spells
} }
default: default:
{ {
if (losPosition == null || m_spellInfo.HasAttribute(SpellAttr5.AlwaysAoeLineOfSight)) if (losPosition == null || m_spellInfo.HasAttribute(SpellAttr5.AlwaysAoeLineOfSight) || spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.AlwaysAoeLineOfSight))
{ {
// Get GO cast coordinates if original caster . GO // Get GO cast coordinates if original caster . GO
WorldObject caster = null; WorldObject caster = null;
+3
View File
@@ -3379,6 +3379,9 @@ namespace Game.Spells
if (spellInfo.HasAttribute(SpellAttr4.AuraIsBuff)) if (spellInfo.HasAttribute(SpellAttr4.AuraIsBuff))
return true; return true;
if (effect.EffectAttributes.HasFlag(SpellEffectAttributes.IsHarmful))
return false;
visited.Add(Tuple.Create(spellInfo, effect.EffectIndex)); visited.Add(Tuple.Create(spellInfo, effect.EffectIndex));
//We need scaling level info for some auras that compute bp 0 or positive but should be debuffs //We need scaling level info for some auras that compute bp 0 or positive but should be debuffs