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:
@@ -1973,12 +1973,28 @@ namespace Framework.Constants
|
||||
public enum SpellEffectAttributes
|
||||
{
|
||||
None = 0,
|
||||
UnaffectedByInvulnerability = 0x01, // not cancelled by immunities
|
||||
NoScaleWithStack = 0x40,
|
||||
NoImmunity = 0x01, /*NYI*/ // not cancelled by immunities
|
||||
PositionIsFacingRelative = 0x02, /*NYI*/
|
||||
JumpChargeUnitMeleeRange = 0x04, /*NYI*/
|
||||
JumpChargeUnitStrictPathCheck = 0x08, /*NYI*/
|
||||
ExcludeOwnParty = 0x10, /*NYI*/
|
||||
AlwaysAoeLineOfSight = 0x20,
|
||||
SuppressPointsStacking = 0x40,
|
||||
ChainFromInitialTarget = 0x80,
|
||||
StackAuraAmountOnRecast = 0x8000, // refreshing periodic auras with this attribute will add remaining damage to new aura
|
||||
AllowAnyExplicitTarget = 0x100000,
|
||||
IgnoreDuringCooldownTimeRateCalculation = 0x800000
|
||||
UncontrolledNoBackwards = 0x100, /*NYI*/
|
||||
AuraPointsStack = 0x000200, /*NYI*/ // refreshing periodic auras with this attribute will add remaining damage to new aura
|
||||
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
|
||||
|
||||
@@ -1337,7 +1337,7 @@ namespace Game.Maps
|
||||
{
|
||||
GridMapTypeMask i_mapTypeMask;
|
||||
List<WorldObject> i_objects;
|
||||
PhaseShift i_phaseShift;
|
||||
public PhaseShift i_phaseShift;
|
||||
ICheck<WorldObject> i_check;
|
||||
|
||||
public WorldObjectListSearcher(WorldObject searcher, List<WorldObject> objects, ICheck<WorldObject> check, GridMapTypeMask mapTypeMask = GridMapTypeMask.All)
|
||||
|
||||
@@ -2748,7 +2748,7 @@ namespace Game.Spells
|
||||
if (GetUnitOwner().HasUnitState(UnitState.Isolated))
|
||||
continue;
|
||||
|
||||
List<Unit> units = new();
|
||||
List<WorldObject> units = new();
|
||||
var condList = spellEffectInfo.ImplicitTargetConditions;
|
||||
|
||||
float radius = spellEffectInfo.CalcRadius(refe);
|
||||
@@ -2803,21 +2803,26 @@ namespace Game.Spells
|
||||
}
|
||||
|
||||
if (selectionType != SpellTargetCheckTypes.Default)
|
||||
{
|
||||
var containerTypeMask = Spell.GetSearcherTypeMask(GetSpellInfo(), spellEffectInfo, SpellTargetObjectTypes.Unit, condList);
|
||||
if (containerTypeMask != 0)
|
||||
{
|
||||
WorldObjectSpellAreaTargetCheck check = new(radius, GetUnitOwner(), refe, GetUnitOwner(), GetSpellInfo(), selectionType, condList, SpellTargetObjectTypes.Unit);
|
||||
UnitListSearcher searcher = new(GetUnitOwner(), units, check);
|
||||
Cell.VisitAllObjects(GetUnitOwner(), searcher, radius + extraSearchRadius);
|
||||
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
|
||||
units.RemoveAll(unit => !unit.IsSelfOrInSameMap(GetUnitOwner()));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Unit unit in units)
|
||||
foreach (WorldObject unit in units)
|
||||
{
|
||||
if (!targets.ContainsKey(unit))
|
||||
targets[unit] = 0;
|
||||
targets[unit.ToUnit()] = 0;
|
||||
|
||||
targets[unit] |= 1u << (int)spellEffectInfo.EffectIndex;
|
||||
targets[unit.ToUnit()] |= 1u << (int)spellEffectInfo.EffectIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ namespace Game.Spells
|
||||
}
|
||||
|
||||
GetBase().CallScriptEffectCalcAmountHandlers(this, ref amount, ref m_canBeRecalculated);
|
||||
if (!GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack))
|
||||
if (!GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.SuppressPointsStacking))
|
||||
amount *= GetBase().GetStackAmount();
|
||||
|
||||
_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)
|
||||
{
|
||||
uint stackAmountForBonuses = !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? stack : 1u;
|
||||
uint stackAmountForBonuses = !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.SuppressPointsStacking) ? stack : 1u;
|
||||
|
||||
switch (spellEffectInfo.ApplyAuraName)
|
||||
{
|
||||
@@ -5057,7 +5057,7 @@ namespace Game.Spells
|
||||
|
||||
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
|
||||
uint damage = (uint)Math.Max(GetAmount(), 0);
|
||||
@@ -5184,7 +5184,7 @@ namespace Game.Spells
|
||||
|
||||
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
|
||||
uint damage = (uint)Math.Max(GetAmount(), 0);
|
||||
@@ -5311,7 +5311,7 @@ namespace Game.Spells
|
||||
if (GetBase().IsPermanent() && target.IsFullHealth())
|
||||
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
|
||||
uint damage = (uint)Math.Max(GetAmount(), 0);
|
||||
|
||||
+37
-31
@@ -398,6 +398,7 @@ namespace Game.Spells
|
||||
spellEffectInfo.ImplicitTargetConditions == effects[j].ImplicitTargetConditions &&
|
||||
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.EffectAttributes.HasFlag(SpellEffectAttributes.PlayersOnly) == effects[j].EffectAttributes.HasFlag(SpellEffectAttributes.PlayersOnly) &&
|
||||
CheckScriptEffectImplicitTargets(spellEffectInfo.EffectIndex, (uint)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;
|
||||
switch (targetType.GetTarget())
|
||||
{
|
||||
@@ -782,7 +783,7 @@ namespace Game.Spells
|
||||
var condList = spellEffectInfo.ImplicitTargetConditions;
|
||||
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)
|
||||
{
|
||||
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))
|
||||
targets.Add(m_targets.GetUnitTarget());
|
||||
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;
|
||||
case Targets.UnitCasterAndSummons:
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1484,7 +1485,7 @@ namespace Game.Spells
|
||||
var condList = spellEffectInfo.ImplicitTargetConditions;
|
||||
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)
|
||||
{
|
||||
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
|
||||
GridMapTypeMask retMask = GridMapTypeMask.All;
|
||||
@@ -1650,11 +1651,11 @@ namespace Game.Spells
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer))
|
||||
if (spellInfo.HasAttribute(SpellAttr3.OnlyOnPlayer) || spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.PlayersOnly))
|
||||
retMask &= GridMapTypeMask.Corpse | GridMapTypeMask.Player;
|
||||
if (m_spellInfo.HasAttribute(SpellAttr3.OnlyOnGhosts))
|
||||
if (spellInfo.HasAttribute(SpellAttr3.OnlyOnGhosts))
|
||||
retMask &= GridMapTypeMask.Player;
|
||||
if (m_spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
||||
if (spellInfo.HasAttribute(SpellAttr5.NotOnPlayer))
|
||||
retMask &= ~GridMapTypeMask.Player;
|
||||
|
||||
if (condList != null)
|
||||
@@ -1662,7 +1663,7 @@ namespace Game.Spells
|
||||
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)
|
||||
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)
|
||||
return null;
|
||||
|
||||
var check = new WorldObjectSpellNearbyTargetCheck(range, m_caster, m_spellInfo, selectionType, condList, objectType);
|
||||
var searcher = new WorldObjectLastSearcher(m_caster, check, containerTypeMask);
|
||||
SearchTargets(searcher, containerTypeMask, m_caster, m_caster.GetPosition(), range);
|
||||
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)
|
||||
return;
|
||||
|
||||
@@ -1750,7 +1752,7 @@ namespace Game.Spells
|
||||
|
||||
WorldObject chainSource = m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) ? m_caster : target;
|
||||
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);
|
||||
|
||||
// remove targets which are always invalid for chain spells
|
||||
@@ -1761,7 +1763,7 @@ namespace Game.Spells
|
||||
while (chainTargets != 0)
|
||||
{
|
||||
// try to get unit for next chain jump
|
||||
WorldObject found = null;
|
||||
WorldObject foundObj = null;
|
||||
// get unit with highest hp deficit in dist
|
||||
if (isChainHeal)
|
||||
{
|
||||
@@ -1772,9 +1774,9 @@ namespace Game.Spells
|
||||
if (unitTarget != null)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -1785,24 +1787,28 @@ namespace Game.Spells
|
||||
{
|
||||
foreach (var obj in tempTargets)
|
||||
{
|
||||
if (found == null)
|
||||
{
|
||||
if (chainSource.IsWithinDist(obj, jumpRadius) && IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2))
|
||||
found = obj;
|
||||
}
|
||||
else if (chainSource.GetDistanceOrder(obj, found) && IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2))
|
||||
found = obj;
|
||||
bool isBestDistanceMatch = foundObj != null ? chainSource.GetDistanceOrder(obj, foundObj) : chainSource.IsWithinDist(obj, jumpRadius);
|
||||
if (!isBestDistanceMatch)
|
||||
continue;
|
||||
|
||||
if (!IsWithinLOS(chainSource, obj, false, ModelIgnoreFlags.M2))
|
||||
continue;
|
||||
|
||||
if (spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.EnforceLineOfSightToChainTargets) && !IsWithinLOS(m_caster, obj, false, ModelIgnoreFlags.M2))
|
||||
continue;
|
||||
|
||||
foundObj = obj;
|
||||
}
|
||||
}
|
||||
// not found any valid target - chain ends
|
||||
if (found == null)
|
||||
if (foundObj == null)
|
||||
break;
|
||||
|
||||
if (!m_spellInfo.HasAttribute(SpellAttr2.ChainFromCaster) && !spellEffectInfo.EffectAttributes.HasFlag(SpellEffectAttributes.ChainFromInitialTarget))
|
||||
chainSource = found;
|
||||
chainSource = foundObj;
|
||||
|
||||
targets.Add(found);
|
||||
tempTargets.Remove(found);
|
||||
targets.Add(foundObj);
|
||||
tempTargets.Remove(foundObj);
|
||||
--chainTargets;
|
||||
}
|
||||
}
|
||||
@@ -7118,7 +7124,7 @@ namespace Game.Spells
|
||||
}
|
||||
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
|
||||
WorldObject caster = null;
|
||||
|
||||
@@ -3379,6 +3379,9 @@ namespace Game.Spells
|
||||
if (spellInfo.HasAttribute(SpellAttr4.AuraIsBuff))
|
||||
return true;
|
||||
|
||||
if (effect.EffectAttributes.HasFlag(SpellEffectAttributes.IsHarmful))
|
||||
return false;
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user