diff --git a/Source/Game/Networking/Packets/SpellPackets.cs b/Source/Game/Networking/Packets/SpellPackets.cs index d174a7a1e..3a7502182 100644 --- a/Source/Game/Networking/Packets/SpellPackets.cs +++ b/Source/Game/Networking/Packets/SpellPackets.cs @@ -1409,7 +1409,7 @@ namespace Game.Networking.Packets data.WriteBit(Duration.HasValue); data.WriteBit(Remaining.HasValue); data.WriteBit(TimeMod.HasValue); - data.WriteBits(Points.Length, 6); + data.WriteBits(Points.Count, 6); data.WriteBits(EstimatedPoints.Count, 6); data.WriteBit(ContentTuning.HasValue); @@ -1448,7 +1448,7 @@ namespace Game.Networking.Packets public Optional Duration; public Optional Remaining; Optional TimeMod; - public float[] Points = Array.Empty(); + public List Points = new(); public List EstimatedPoints = new(); } diff --git a/Source/Game/Spells/Auras/Aura.cs b/Source/Game/Spells/Auras/Aura.cs index d6975a784..6d30c9ffa 100644 --- a/Source/Game/Spells/Auras/Aura.cs +++ b/Source/Game/Spells/Auras/Aura.cs @@ -45,6 +45,7 @@ namespace Game.Spells Death } + [Flags] public enum AuraFlags { None = 0x00, @@ -226,21 +227,29 @@ namespace Game.Spells // send stack amount for aura which could be stacked (never 0 - causes incorrect display) or charges // stack amount has priority over charges (checked on retail with spell 50262) auraData.Applications = aura.IsUsingStacks() ? aura.GetStackAmount() : aura.GetCharges(); - if (!auraData.Flags.HasAnyFlag(AuraFlags.NoCaster)) + if (!auraData.Flags.HasFlag(AuraFlags.NoCaster)) auraData.CastUnit.Set(aura.GetCasterGUID()); - if (auraData.Flags.HasAnyFlag(AuraFlags.Duration)) + if (auraData.Flags.HasFlag(AuraFlags.Duration)) { auraData.Duration.Set(aura.GetMaxDuration()); auraData.Remaining.Set(aura.GetDuration()); } - if (auraData.Flags.HasAnyFlag(AuraFlags.Scalable)) + if (auraData.Flags.HasFlag(AuraFlags.Scalable)) { - auraData.Points = new float[GetBase().GetAuraEffects().Length]; foreach (AuraEffect effect in GetBase().GetAuraEffects()) + { if (effect != null && HasEffect(effect.GetEffIndex())) // Not all of aura's effects have to be applied on every target - auraData.Points[effect.GetEffIndex()] = effect.GetAmount(); + { + auraData.Points.Add(effect.GetAmount()); + if (effect.GetEstimatedAmount().HasValue) + auraData.EstimatedPoints.Add(effect.GetEstimatedAmount().Value); + } + } + + if (!auraData.EstimatedPoints.Empty()) + auraData.EstimatedPoints.Resize((uint)auraData.Points.Count); // pad to equal sizes } } diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index cecc40b5c..66ae059dd 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -123,6 +123,24 @@ namespace Game.Spells GetBase().CallScriptEffectCalcAmountHandlers(this, ref amount, ref m_canBeRecalculated); if (!GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack)) amount *= GetBase().GetStackAmount(); + + if (caster) + { + uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? GetBase().GetStackAmount() : 1u; + + switch (GetAuraType()) + { + case AuraType.PeriodicDamage: + case AuraType.PeriodicLeech: + _estimatedAmount = caster.SpellDamageBonusDone(GetBase().GetUnitOwner(), GetSpellInfo(), (uint)amount, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); + break; + case AuraType.PeriodicHeal: + _estimatedAmount = caster.SpellHealingBonusDone(GetBase().GetUnitOwner(), GetSpellInfo(), (uint)amount, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); + break; + default: + break; + } + } return amount; } @@ -774,6 +792,8 @@ namespace Game.Spells public bool HasAmount() { return _amount != 0; } public void SetAmount(int amount) { _amount = amount; m_canBeRecalculated = false; } + public float? GetEstimatedAmount() { return _estimatedAmount; } + public int GetPeriodicTimer() { return _periodicTimer; } public void SetPeriodicTimer(int periodicTimer) { _periodicTimer = periodicTimer; } @@ -820,6 +840,7 @@ namespace Game.Spells public int m_baseAmount; int _amount; + float? _estimatedAmount; // for periodic damage and healing auras this will include damage done bonuses // periodic stuff int _periodicTimer; @@ -4893,6 +4914,8 @@ namespace Game.Spells CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal); + uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? GetBase().GetStackAmount() : 1u; + // ignore non positive values (can be result apply spellmods to aura damage uint damage = (uint)Math.Max(GetAmount(), 0); @@ -4904,9 +4927,9 @@ namespace Game.Spells case AuraType.PeriodicDamage: { if (caster != null) - damage = (uint)caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + damage = caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); - damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); // There is a Chance to make a Soul Shard when Drain soul does damage if (caster != null && GetSpellInfo().SpellFamilyName == SpellFamilyNames.Warlock && GetSpellInfo().SpellFamilyFlags[0].HasAnyFlag(0x00004000u)) @@ -4946,7 +4969,7 @@ namespace Game.Spells case AuraType.PeriodicDamagePercent: // ceil obtained value, it may happen that 10 ticks for 10% damage may not kill owner damage = (uint)Math.Ceiling(MathFunctions.CalculatePct((float)target.GetMaxHealth(), (float)damage)); - damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); break; default: break; @@ -5026,13 +5049,15 @@ namespace Game.Spells CleanDamage cleanDamage = new(0, 0, WeaponAttackType.BaseAttack, MeleeHitOutcome.Normal); + uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? GetBase().GetStackAmount() : 1u; + // ignore negative values (can be result apply spellmods to aura damage uint damage = (uint)Math.Max(GetAmount(), 0); if (caster) - damage = caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + damage = caster.SpellDamageBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); - damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + damage = target.SpellDamageBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); bool crit = RandomHelper.randChance(GetCritChanceFor(caster, target)); if (crit) @@ -5093,8 +5118,8 @@ namespace Game.Spells float gainMultiplier = GetSpellEffectInfo().CalcValueMultiplier(caster); - uint heal = (caster.SpellHealingBonusDone(caster, GetSpellInfo(), (uint)(new_damage * gainMultiplier), DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount())); - heal = caster.SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + uint heal = caster.SpellHealingBonusDone(caster, GetSpellInfo(), (uint)(new_damage * gainMultiplier), DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); + heal = caster.SpellHealingBonusTaken(caster, GetSpellInfo(), heal, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); HealInfo healInfo = new(caster, caster, heal, GetSpellInfo(), GetSpellInfo().GetSchoolMask()); caster.HealBySpell(healInfo); @@ -5157,17 +5182,17 @@ namespace Game.Spells if (GetBase().IsPermanent() && target.IsFullHealth()) return; + uint stackAmountForBonuses = !GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.NoScaleWithStack) ? GetBase().GetStackAmount() : 1u; + // ignore negative values (can be result apply spellmods to aura damage uint damage = (uint)Math.Max(GetAmount(), 0); if (GetAuraType() == AuraType.ObsModHealth) - { damage = (uint)target.CountPctFromMaxHealth((int)damage); - } else if (caster != null) - damage = caster.SpellHealingBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + damage = caster.SpellHealingBonusDone(target, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); - damage = target.SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), GetBase().GetStackAmount()); + damage = target.SpellHealingBonusTaken(caster, GetSpellInfo(), damage, DamageEffectType.DOT, GetSpellEffectInfo(), stackAmountForBonuses); bool crit = RandomHelper.randChance(GetCritChanceFor(caster, target)); if (crit)