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 int AoeDamageTargetCap = 20;
|
||||
|
||||
public const int MaxPowersPerSpell = 5;
|
||||
|
||||
public const uint VisualKitFood = 406;
|
||||
|
||||
@@ -406,7 +406,9 @@ namespace Framework.Constants
|
||||
AuraStack,
|
||||
CritChance,
|
||||
DurationPct,
|
||||
Duration
|
||||
Duration,
|
||||
ParentSpellTargetCount,
|
||||
ParentSpellTargetIndex
|
||||
}
|
||||
|
||||
public enum CombatRating
|
||||
|
||||
@@ -2108,6 +2108,23 @@ namespace Game.Spells
|
||||
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)
|
||||
{
|
||||
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 (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());
|
||||
|
||||
if (m_originalCaster.IsPlayer())
|
||||
{
|
||||
// cap damage of player AOE
|
||||
long targetAmount = GetUnitTargetCountForEffect(spellEffectInfo.EffectIndex);
|
||||
if (targetAmount > 20)
|
||||
m_damage = (int)(m_damage * 20 / targetAmount);
|
||||
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 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:
|
||||
m_spellValue.Duration = value;
|
||||
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 CriticalChance;
|
||||
public int? Duration;
|
||||
public int? ParentSpellTargetCount;
|
||||
public int? ParentSpellTargetIndex;
|
||||
}
|
||||
|
||||
// Spell modifier (used for modify other spells)
|
||||
|
||||
@@ -255,11 +255,16 @@ namespace Game.Spells
|
||||
}
|
||||
|
||||
SpellCastTargets targets = new();
|
||||
int? targetCount = null;
|
||||
int? targetIndex = null;
|
||||
if (effectHandleMode == SpellEffectHandleMode.LaunchTarget)
|
||||
{
|
||||
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
|
||||
return;
|
||||
|
||||
targets.SetUnitTarget(unitTarget);
|
||||
targetCount = (int)GetUnitTargetCountForEffect(effectInfo.EffectIndex);
|
||||
targetIndex = GetUnitTargetIndexForEffect(unitTarget.GetGUID(), effectInfo.EffectIndex);
|
||||
}
|
||||
else //if (effectHandleMode == SpellEffectHandleMode.Launch)
|
||||
{
|
||||
@@ -320,6 +325,12 @@ namespace Game.Spells
|
||||
for (int i = 0; i < SpellConst.MaxEffects; ++i)
|
||||
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);
|
||||
}, delay);
|
||||
}
|
||||
@@ -348,11 +359,16 @@ namespace Game.Spells
|
||||
}
|
||||
|
||||
SpellCastTargets targets = new();
|
||||
int? targetCount = null;
|
||||
int? targetIndex = null;
|
||||
if (effectHandleMode == SpellEffectHandleMode.HitTarget)
|
||||
{
|
||||
if (!spellInfo.NeedsToBeTriggeredByCaster(m_spellInfo))
|
||||
return;
|
||||
|
||||
targets.SetUnitTarget(unitTarget);
|
||||
targetCount = (int)GetUnitTargetCountForEffect(effectInfo.EffectIndex);
|
||||
targetIndex = GetUnitTargetIndexForEffect(unitTarget.GetGUID(), effectInfo.EffectIndex);
|
||||
}
|
||||
else //if (effectHandleMode == SpellEffectHandleMode.Hit)
|
||||
{
|
||||
@@ -381,6 +397,12 @@ namespace Game.Spells
|
||||
for (int i = 0; i < SpellConst.MaxEffects; ++i)
|
||||
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
|
||||
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)
|
||||
{
|
||||
ImmunityInfo immuneInfo = spellEffectInfo.GetImmunityInfo();
|
||||
@@ -2646,7 +2678,7 @@ namespace Game.Spells
|
||||
|
||||
return mechanicImmunityMask;
|
||||
}
|
||||
|
||||
|
||||
public float GetMinRange(bool positive = false)
|
||||
{
|
||||
if (RangeEntry == null)
|
||||
@@ -3001,7 +3033,7 @@ namespace Game.Spells
|
||||
return itr;
|
||||
|
||||
costs.Add(new SpellPowerCost() { Power = powerType, Amount = 0 });
|
||||
return costs.Last();
|
||||
return costs.Last();
|
||||
}
|
||||
|
||||
foreach (SpellPowerRecord power in PowerCosts)
|
||||
@@ -3329,7 +3361,7 @@ namespace Game.Spells
|
||||
|
||||
bool _isPositiveEffectImpl(SpellInfo spellInfo, SpellEffectInfo effect, List<Tuple<SpellInfo, uint>> visited)
|
||||
{
|
||||
if ( !effect.IsEffect())
|
||||
if (!effect.IsEffect())
|
||||
return true;
|
||||
|
||||
// attribute may be already set in DB
|
||||
@@ -3619,7 +3651,7 @@ namespace Game.Spells
|
||||
if (_isPositiveTarget(spellTriggeredEffect) && !_isPositiveEffectImpl(spellTriggeredProto, spellTriggeredEffect, visited))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case AuraType.PeriodicTriggerSpell:
|
||||
case AuraType.ModStun:
|
||||
@@ -3972,8 +4004,8 @@ namespace Game.Spells
|
||||
public uint ExcludeCasterAuraSpell { get; set; }
|
||||
public uint ExcludeTargetAuraSpell { get; set; }
|
||||
public AuraType CasterAuraType { get; set; }
|
||||
public AuraType TargetAuraType { get; set; }
|
||||
public AuraType ExcludeCasterAuraType { get; set; }
|
||||
public AuraType TargetAuraType { get; set; }
|
||||
public AuraType ExcludeCasterAuraType { get; set; }
|
||||
public AuraType ExcludeTargetAuraType { get; set; }
|
||||
public SpellCastTimesRecord CastTimeEntry { get; set; }
|
||||
public uint RecoveryTime { get; set; }
|
||||
@@ -4032,6 +4064,8 @@ namespace Game.Spells
|
||||
public uint ExplicitTargetMask { get; set; }
|
||||
public SpellChainNode ChainEntry { get; set; }
|
||||
|
||||
public SqrtDamageAndHealingDiminishingStruct SqrtDamageAndHealingDiminishing;
|
||||
|
||||
List<SpellEffectInfo> _effects = new();
|
||||
List<SpellXSpellVisualRecord> _visuals = new();
|
||||
SpellSpecificType _spellSpecific;
|
||||
@@ -4047,6 +4081,12 @@ namespace Game.Spells
|
||||
public uint MaxScalingLevel;
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user