Core/Spells: Implement sqrt based aoe damage diminishing

Port From (https://github.com/TrinityCore/TrinityCore/commit/42a6e0eb1afa431fc1efe7d4c5a62758fed42684)
This commit is contained in:
hondacrx
2023-09-14 04:24:19 -04:00
parent 572bab53e4
commit 6975212812
5 changed files with 129 additions and 12 deletions
@@ -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;
+3 -1
View File
@@ -406,7 +406,9 @@ namespace Framework.Constants
AuraStack,
CritChance,
DurationPct,
Duration
Duration,
ParentSpellTargetCount,
ParentSpellTargetIndex
}
public enum CombatRating
+56 -5
View File
@@ -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)
+22
View File
@@ -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);
}
+46 -6
View File
@@ -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