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
{
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
+1 -1
View File
@@ -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)
+14 -9
View File
@@ -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);
@@ -2804,20 +2804,25 @@ namespace Game.Spells
if (selectionType != SpellTargetCheckTypes.Default)
{
WorldObjectSpellAreaTargetCheck check = new(radius, GetUnitOwner(), refe, GetUnitOwner(), GetSpellInfo(), selectionType, condList, SpellTargetObjectTypes.Unit);
UnitListSearcher searcher = new(GetUnitOwner(), units, check);
Cell.VisitAllObjects(GetUnitOwner(), searcher, radius + extraSearchRadius);
var containerTypeMask = Spell.GetSearcherTypeMask(GetSpellInfo(), spellEffectInfo, SpellTargetObjectTypes.Unit, condList);
if (containerTypeMask != 0)
{
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
units.RemoveAll(unit => !unit.IsSelfOrInSameMap(GetUnitOwner()));
// 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;
}
}
}
+5 -5
View File
@@ -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
View File
@@ -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;
+3
View File
@@ -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