Core/Spells: Implement sqrt based aoe damage diminishing
Port From (https://github.com/TrinityCore/TrinityCore/commit/42a6e0eb1afa431fc1efe7d4c5a62758fed42684)
This commit is contained in:
@@ -20,6 +20,8 @@ namespace Framework.Constants
|
|||||||
|
|
||||||
public const float TrajectoryMissileSize = 3.0f;
|
public const float TrajectoryMissileSize = 3.0f;
|
||||||
|
|
||||||
|
public const int AoeDamageTargetCap = 20;
|
||||||
|
|
||||||
public const int MaxPowersPerSpell = 5;
|
public const int MaxPowersPerSpell = 5;
|
||||||
|
|
||||||
public const uint VisualKitFood = 406;
|
public const uint VisualKitFood = 406;
|
||||||
|
|||||||
@@ -406,7 +406,9 @@ namespace Framework.Constants
|
|||||||
AuraStack,
|
AuraStack,
|
||||||
CritChance,
|
CritChance,
|
||||||
DurationPct,
|
DurationPct,
|
||||||
Duration
|
Duration,
|
||||||
|
ParentSpellTargetCount,
|
||||||
|
ParentSpellTargetIndex
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum CombatRating
|
public enum CombatRating
|
||||||
|
|||||||
@@ -2108,6 +2108,23 @@ namespace Game.Spells
|
|||||||
m_destTargets[effIndex] = dest;
|
m_destTargets[effIndex] = dest;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GetUnitTargetIndexForEffect(ObjectGuid target, uint effect)
|
||||||
|
{
|
||||||
|
int index = 0;
|
||||||
|
foreach (TargetInfo uniqueTargetInfo in m_UniqueTargetInfo)
|
||||||
|
{
|
||||||
|
if (uniqueTargetInfo.MissCondition == SpellMissInfo.None && (uniqueTargetInfo.EffectMask & (1 << (int)effect)) != 0)
|
||||||
|
{
|
||||||
|
if (uniqueTargetInfo.TargetGUID == target)
|
||||||
|
break;
|
||||||
|
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
public long GetUnitTargetCountForEffect(uint effect)
|
public long GetUnitTargetCountForEffect(uint effect)
|
||||||
{
|
{
|
||||||
return m_UniqueTargetInfo.Count(targetInfo => targetInfo.MissCondition == SpellMissInfo.None && (targetInfo.EffectMask & (1 << (int)effect)) != 0);
|
return m_UniqueTargetInfo.Count(targetInfo => targetInfo.MissCondition == SpellMissInfo.None && (targetInfo.EffectMask & (1 << (int)effect)) != 0);
|
||||||
@@ -7277,16 +7294,42 @@ namespace Game.Spells
|
|||||||
|
|
||||||
if (m_originalCaster != null && m_damage > 0)
|
if (m_originalCaster != null && m_damage > 0)
|
||||||
{
|
{
|
||||||
if (spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SpellEffectName.PersistentAreaAura) || m_spellInfo.HasAttribute(SpellAttr5.TreatAsAreaEffect))
|
bool isAoeTarget = spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SpellEffectName.PersistentAreaAura);
|
||||||
|
if (isAoeTarget || m_spellInfo.HasAttribute(SpellAttr5.TreatAsAreaEffect))
|
||||||
{
|
{
|
||||||
m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_originalCaster.GetGUID());
|
m_damage = unit.CalculateAOEAvoidance(m_damage, (uint)m_spellInfo.SchoolMask, m_originalCaster.GetGUID());
|
||||||
|
|
||||||
if (m_originalCaster.IsPlayer())
|
if (m_originalCaster.IsPlayer())
|
||||||
{
|
{
|
||||||
// cap damage of player AOE
|
long targetCount = !isAoeTarget && m_spellValue.ParentSpellTargetCount.HasValue ? m_spellValue.ParentSpellTargetCount.Value : GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex);
|
||||||
long targetAmount = GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex);
|
int targetIndex = !isAoeTarget && m_spellValue.ParentSpellTargetIndex.HasValue ? m_spellValue.ParentSpellTargetIndex.Value : GetUnitTargetIndexForEffect(targetInfo.TargetGUID, spellEffectInfo.EffectIndex);
|
||||||
if (targetAmount > 20)
|
|
||||||
m_damage = (int)(m_damage * 20 / targetAmount);
|
// sqrt target cap damage calculation
|
||||||
|
if (m_spellInfo.SqrtDamageAndHealingDiminishing.MaxTargets != 0
|
||||||
|
&& targetCount > m_spellInfo.SqrtDamageAndHealingDiminishing.MaxTargets
|
||||||
|
&& targetIndex >= m_spellInfo.SqrtDamageAndHealingDiminishing.NumNonDiminishedTargets)
|
||||||
|
m_damage = (int)(m_damage * Math.Sqrt((float)m_spellInfo.SqrtDamageAndHealingDiminishing.MaxTargets / Math.Min(SpellConst.AoeDamageTargetCap, targetCount)));
|
||||||
|
if (targetCount > SpellConst.AoeDamageTargetCap)
|
||||||
|
m_damage = (int)(m_damage * SpellConst.AoeDamageTargetCap / targetCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_originalCaster && m_healing > 0)
|
||||||
|
{
|
||||||
|
bool isAoeTarget = spellEffectInfo.IsTargetingArea() || spellEffectInfo.IsAreaAuraEffect() || spellEffectInfo.IsEffect(SpellEffectName.PersistentAreaAura);
|
||||||
|
if (isAoeTarget || m_spellInfo.HasAttribute(SpellAttr5.TreatAsAreaEffect))
|
||||||
|
{
|
||||||
|
if (m_originalCaster.IsPlayer())
|
||||||
|
{
|
||||||
|
long targetCount = !isAoeTarget && m_spellValue.ParentSpellTargetCount.HasValue ? m_spellValue.ParentSpellTargetCount.Value : GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex);
|
||||||
|
int targetIndex = !isAoeTarget && m_spellValue.ParentSpellTargetIndex.HasValue ? m_spellValue.ParentSpellTargetIndex.Value : GetUnitTargetIndexForEffect(targetInfo.TargetGUID, spellEffectInfo.EffectIndex);
|
||||||
|
|
||||||
|
// sqrt target cap healing calculation
|
||||||
|
if (m_spellInfo.SqrtDamageAndHealingDiminishing.MaxTargets != 0
|
||||||
|
&& targetCount > m_spellInfo.SqrtDamageAndHealingDiminishing.MaxTargets
|
||||||
|
&& targetIndex >= m_spellInfo.SqrtDamageAndHealingDiminishing.NumNonDiminishedTargets)
|
||||||
|
m_healing = (int)(m_healing * Math.Sqrt((float)m_spellInfo.SqrtDamageAndHealingDiminishing.MaxTargets / Math.Min(SpellConst.AoeDamageTargetCap, targetCount)));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -7407,6 +7450,12 @@ namespace Game.Spells
|
|||||||
case SpellValueMod.Duration:
|
case SpellValueMod.Duration:
|
||||||
m_spellValue.Duration = value;
|
m_spellValue.Duration = value;
|
||||||
break;
|
break;
|
||||||
|
case SpellValueMod.ParentSpellTargetCount:
|
||||||
|
m_spellValue.ParentSpellTargetCount = value;
|
||||||
|
break;
|
||||||
|
case SpellValueMod.ParentSpellTargetIndex:
|
||||||
|
m_spellValue.ParentSpellTargetIndex = value;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -8796,6 +8845,8 @@ namespace Game.Spells
|
|||||||
public float DurationMul;
|
public float DurationMul;
|
||||||
public float CriticalChance;
|
public float CriticalChance;
|
||||||
public int? Duration;
|
public int? Duration;
|
||||||
|
public int? ParentSpellTargetCount;
|
||||||
|
public int? ParentSpellTargetIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Spell modifier (used for modify other spells)
|
// Spell modifier (used for modify other spells)
|
||||||
|
|||||||
@@ -255,11 +255,16 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
|
|
||||||
SpellCastTargets targets = new();
|
SpellCastTargets targets = new();
|
||||||
|
int? targetCount = null;
|
||||||
|
int? targetIndex = null;
|
||||||
if (effectHandleMode == SpellEffectHandleMode.LaunchTarget)
|
if (effectHandleMode == SpellEffectHandleMode.LaunchTarget)
|
||||||
{
|
{
|
||||||
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
|
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
targets.SetUnitTarget(unitTarget);
|
targets.SetUnitTarget(unitTarget);
|
||||||
|
targetCount = (int)GetUnitTargetCountForEffect(effectInfo.EffectIndex);
|
||||||
|
targetIndex = GetUnitTargetIndexForEffect(unitTarget.GetGUID(), effectInfo.EffectIndex);
|
||||||
}
|
}
|
||||||
else //if (effectHandleMode == SpellEffectHandleMode.Launch)
|
else //if (effectHandleMode == SpellEffectHandleMode.Launch)
|
||||||
{
|
{
|
||||||
@@ -320,6 +325,12 @@ namespace Game.Spells
|
|||||||
for (int i = 0; i < SpellConst.MaxEffects; ++i)
|
for (int i = 0; i < SpellConst.MaxEffects; ++i)
|
||||||
args.AddSpellMod(SpellValueMod.BasePoint0 + i, value);
|
args.AddSpellMod(SpellValueMod.BasePoint0 + i, value);
|
||||||
|
|
||||||
|
if (targetCount.HasValue)
|
||||||
|
args.AddSpellMod(SpellValueMod.ParentSpellTargetCount, targetCount.Value);
|
||||||
|
|
||||||
|
if (targetIndex.HasValue)
|
||||||
|
args.AddSpellMod(SpellValueMod.ParentSpellTargetIndex, targetIndex.Value);
|
||||||
|
|
||||||
caster.CastSpell(targets, triggerSpell, args);
|
caster.CastSpell(targets, triggerSpell, args);
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
}
|
||||||
@@ -348,11 +359,16 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
|
|
||||||
SpellCastTargets targets = new();
|
SpellCastTargets targets = new();
|
||||||
|
int? targetCount = null;
|
||||||
|
int? targetIndex = null;
|
||||||
if (effectHandleMode == SpellEffectHandleMode.HitTarget)
|
if (effectHandleMode == SpellEffectHandleMode.HitTarget)
|
||||||
{
|
{
|
||||||
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
|
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
targets.SetUnitTarget(unitTarget);
|
targets.SetUnitTarget(unitTarget);
|
||||||
|
targetCount = (int)GetUnitTargetCountForEffect(effectInfo.EffectIndex);
|
||||||
|
targetIndex = GetUnitTargetIndexForEffect(unitTarget.GetGUID(), effectInfo.EffectIndex);
|
||||||
}
|
}
|
||||||
else //if (effectHandleMode == SpellEffectHandleMode.Hit)
|
else //if (effectHandleMode == SpellEffectHandleMode.Hit)
|
||||||
{
|
{
|
||||||
@@ -381,6 +397,12 @@ namespace Game.Spells
|
|||||||
for (int i = 0; i < SpellConst.MaxEffects; ++i)
|
for (int i = 0; i < SpellConst.MaxEffects; ++i)
|
||||||
args.AddSpellMod(SpellValueMod.BasePoint0 + i, damage);
|
args.AddSpellMod(SpellValueMod.BasePoint0 + i, damage);
|
||||||
|
|
||||||
|
if (targetCount.HasValue)
|
||||||
|
args.AddSpellMod(SpellValueMod.ParentSpellTargetCount, targetCount.Value);
|
||||||
|
|
||||||
|
if (targetIndex.HasValue)
|
||||||
|
args.AddSpellMod(SpellValueMod.ParentSpellTargetIndex, targetIndex.Value);
|
||||||
|
|
||||||
// original caster guid only for GO cast
|
// original caster guid only for GO cast
|
||||||
m_caster.CastSpell(targets, spellInfo.Id, args);
|
m_caster.CastSpell(targets, spellInfo.Id, args);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2393,6 +2393,38 @@ namespace Game.Spells
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _LoadSqrtTargetLimit(int maxTargets, int numNonDiminishedTargets, uint? maxTargetsEffectValueHolder, uint? numNonDiminishedTargetsEffectValueHolder)
|
||||||
|
{
|
||||||
|
SqrtDamageAndHealingDiminishing.MaxTargets = maxTargets;
|
||||||
|
SqrtDamageAndHealingDiminishing.NumNonDiminishedTargets = numNonDiminishedTargets;
|
||||||
|
|
||||||
|
if (maxTargetsEffectValueHolder.HasValue)
|
||||||
|
{
|
||||||
|
if (maxTargetsEffectValueHolder < GetEffects().Count)
|
||||||
|
{
|
||||||
|
SpellEffectInfo valueHolder = GetEffect(maxTargetsEffectValueHolder.Value);
|
||||||
|
int expectedValue = valueHolder.CalcBaseValue(null, null, 0, -1);
|
||||||
|
if (maxTargets != expectedValue)
|
||||||
|
Log.outError(LogFilter.Spells, $"SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {Id} has different value in effect {maxTargetsEffectValueHolder.Value} than expected, recheck target caps (expected {maxTargets}, got {expectedValue})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log.outError(LogFilter.Spells, $"SpellInfo::_LoadSqrtTargetLimit(maxTargets): Spell {Id} does not have effect {maxTargetsEffectValueHolder.Value}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numNonDiminishedTargetsEffectValueHolder.HasValue)
|
||||||
|
{
|
||||||
|
if (numNonDiminishedTargetsEffectValueHolder < GetEffects().Count)
|
||||||
|
{
|
||||||
|
SpellEffectInfo valueHolder = GetEffect(numNonDiminishedTargetsEffectValueHolder.Value);
|
||||||
|
int expectedValue = valueHolder.CalcBaseValue(null, null, 0, -1);
|
||||||
|
if (numNonDiminishedTargets != expectedValue)
|
||||||
|
Log.outError(LogFilter.Spells, $"SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {Id} has different value in effect {numNonDiminishedTargetsEffectValueHolder.Value} than expected, recheck target caps (expected {numNonDiminishedTargets}, got {expectedValue})");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Log.outError(LogFilter.Spells, $"SpellInfo::_LoadSqrtTargetLimit(numNonDiminishedTargets): Spell {Id} does not have effect {numNonDiminishedTargetsEffectValueHolder.Value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void ApplyAllSpellImmunitiesTo(Unit target, SpellEffectInfo spellEffectInfo, bool apply)
|
public void ApplyAllSpellImmunitiesTo(Unit target, SpellEffectInfo spellEffectInfo, bool apply)
|
||||||
{
|
{
|
||||||
ImmunityInfo immuneInfo = spellEffectInfo.GetImmunityInfo();
|
ImmunityInfo immuneInfo = spellEffectInfo.GetImmunityInfo();
|
||||||
@@ -3001,7 +3033,7 @@ namespace Game.Spells
|
|||||||
return itr;
|
return itr;
|
||||||
|
|
||||||
costs.Add(new SpellPowerCost() { Power = powerType, Amount = 0 });
|
costs.Add(new SpellPowerCost() { Power = powerType, Amount = 0 });
|
||||||
return costs.Last();
|
return costs.Last();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (SpellPowerRecord power in PowerCosts)
|
foreach (SpellPowerRecord power in PowerCosts)
|
||||||
@@ -3329,7 +3361,7 @@ namespace Game.Spells
|
|||||||
|
|
||||||
bool _isPositiveEffectImpl(SpellInfo spellInfo, SpellEffectInfo effect, List<Tuple<SpellInfo, uint>> visited)
|
bool _isPositiveEffectImpl(SpellInfo spellInfo, SpellEffectInfo effect, List<Tuple<SpellInfo, uint>> visited)
|
||||||
{
|
{
|
||||||
if ( !effect.IsEffect())
|
if (!effect.IsEffect())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// attribute may be already set in DB
|
// attribute may be already set in DB
|
||||||
@@ -3972,8 +4004,8 @@ namespace Game.Spells
|
|||||||
public uint ExcludeCasterAuraSpell { get; set; }
|
public uint ExcludeCasterAuraSpell { get; set; }
|
||||||
public uint ExcludeTargetAuraSpell { get; set; }
|
public uint ExcludeTargetAuraSpell { get; set; }
|
||||||
public AuraType CasterAuraType { get; set; }
|
public AuraType CasterAuraType { get; set; }
|
||||||
public AuraType TargetAuraType { get; set; }
|
public AuraType TargetAuraType { get; set; }
|
||||||
public AuraType ExcludeCasterAuraType { get; set; }
|
public AuraType ExcludeCasterAuraType { get; set; }
|
||||||
public AuraType ExcludeTargetAuraType { get; set; }
|
public AuraType ExcludeTargetAuraType { get; set; }
|
||||||
public SpellCastTimesRecord CastTimeEntry { get; set; }
|
public SpellCastTimesRecord CastTimeEntry { get; set; }
|
||||||
public uint RecoveryTime { get; set; }
|
public uint RecoveryTime { get; set; }
|
||||||
@@ -4032,6 +4064,8 @@ namespace Game.Spells
|
|||||||
public uint ExplicitTargetMask { get; set; }
|
public uint ExplicitTargetMask { get; set; }
|
||||||
public SpellChainNode ChainEntry { get; set; }
|
public SpellChainNode ChainEntry { get; set; }
|
||||||
|
|
||||||
|
public SqrtDamageAndHealingDiminishingStruct SqrtDamageAndHealingDiminishing;
|
||||||
|
|
||||||
List<SpellEffectInfo> _effects = new();
|
List<SpellEffectInfo> _effects = new();
|
||||||
List<SpellXSpellVisualRecord> _visuals = new();
|
List<SpellXSpellVisualRecord> _visuals = new();
|
||||||
SpellSpecificType _spellSpecific;
|
SpellSpecificType _spellSpecific;
|
||||||
@@ -4047,6 +4081,12 @@ namespace Game.Spells
|
|||||||
public uint MaxScalingLevel;
|
public uint MaxScalingLevel;
|
||||||
public uint ScalesFromItemLevel;
|
public uint ScalesFromItemLevel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct SqrtDamageAndHealingDiminishingStruct
|
||||||
|
{
|
||||||
|
public int MaxTargets; // The amount of targets after the damage decreases by the Square Root AOE formula
|
||||||
|
public int NumNonDiminishedTargets; // The amount of targets that still take the full amount before the damage decreases by the Square Root AOE formula
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class SpellEffectInfo
|
public class SpellEffectInfo
|
||||||
|
|||||||
Reference in New Issue
Block a user