From 1d8bab3b58924d17cba5b9fd36ec223845ae936f Mon Sep 17 00:00:00 2001 From: hondacrx Date: Wed, 17 Mar 2021 16:08:17 -0400 Subject: [PATCH] Core/Auras: periodics refactor part 1: Port From (https://github.com/TrinityCore/TrinityCore/commit/5f9e0d92d563c983c229db1569414916b3dce51e) --- Source/Game/Entities/Unit/Unit.Spells.cs | 21 ++++- Source/Game/Spells/Auras/Aura.cs | 8 ++ Source/Game/Spells/Auras/AuraEffect.cs | 101 ++++++++++++----------- Source/Game/Spells/SpellManager.cs | 30 ++++++- Source/Scripts/Spells/Druid.cs | 8 +- Source/Scripts/Spells/Generic.cs | 2 +- 6 files changed, 112 insertions(+), 58 deletions(-) diff --git a/Source/Game/Entities/Unit/Unit.Spells.cs b/Source/Game/Entities/Unit/Unit.Spells.cs index 4646fe2cf..d77159445 100644 --- a/Source/Game/Entities/Unit/Unit.Spells.cs +++ b/Source/Game/Entities/Unit/Unit.Spells.cs @@ -1127,6 +1127,20 @@ namespace Game.Entities CastSpell(targets, spellInfo, null, triggered ? TriggerCastFlags.FullMask : TriggerCastFlags.None, castItem, triggeredByAura, originalCaster); } + public void CastSpell(Item item, uint spellId, bool triggered, Item castItem = null, AuraEffect triggeredByAura = null, ObjectGuid originalCaster = default) + { + SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, GetMap().GetDifficultyID()); + if (spellInfo == null) + { + Log.outError(LogFilter.Unit, "CastSpell: unknown spell id {0} by caster: {1}", spellId, GetGUID().ToString()); + return; + } + SpellCastTargets targets = new SpellCastTargets(); + targets.SetItemTarget(item); + + CastSpell(targets, spellInfo, null, triggered ? TriggerCastFlags.FullMask : TriggerCastFlags.None, castItem, triggeredByAura, originalCaster); + } + public void CastCustomSpell(Unit target, uint spellId, int bp0, int bp1, int bp2, bool triggered, Item castItem = null, AuraEffect triggeredByAura = null, ObjectGuid originalCaster = default) { @@ -2859,11 +2873,12 @@ namespace Game.Entities { uint amount = 0; var periodicAuras = GetAuraEffectsByType(auraType); - foreach (var eff in periodicAuras) + foreach (var aurEff in periodicAuras) { - if (eff.GetCasterGUID() != caster || eff.GetId() != spellId || eff.GetEffIndex() != effectIndex || eff.GetTotalTicks() == 0) + if (aurEff.GetCasterGUID() != caster || aurEff.GetId() != spellId || aurEff.GetEffIndex() != effectIndex || aurEff.GetTotalTicks() == 0) continue; - amount += (uint)((eff.GetAmount() * Math.Max(eff.GetTotalTicks() - eff.GetTickNumber(), 0)) / eff.GetTotalTicks()); + + amount += (uint)((aurEff.GetAmount() * aurEff.GetRemainingTicks()) / aurEff.GetTotalTicks()); break; } diff --git a/Source/Game/Spells/Auras/Aura.cs b/Source/Game/Spells/Auras/Aura.cs index eb5c39b63..1cce0aea6 100644 --- a/Source/Game/Spells/Auras/Aura.cs +++ b/Source/Game/Spells/Auras/Aura.cs @@ -742,6 +742,14 @@ namespace Game.Spells if (!m_periodicCosts.Empty()) m_timeCla = 1 * Time.InMilliseconds; + + // also reset periodic counters + for (byte i = 0; i < SpellConst.MaxEffects; ++i) + { + AuraEffect aurEff = GetEffect(i); + if (aurEff != null) + aurEff.ResetTicks(); + } } void RefreshTimers(bool resetPeriodicTimer) diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 1fbcc685e..a39d7c668 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -125,18 +125,25 @@ namespace Game.Spells amount *= GetBase().GetStackAmount(); return amount; } + void ResetPeriodic(bool resetPeriodicTimer = false) + { + _ticksDone = 0; + if (resetPeriodicTimer) + { + _periodicTimer = _period; + // Start periodic on next tick or at aura apply + if (!m_spellInfo.HasAttribute(SpellAttr5.StartPeriodicAtApply)) + _periodicTimer = 0; + } + } public void CalculatePeriodic(Unit caster, bool resetPeriodicTimer = true, bool load = false) { - m_period = (int)GetSpellEffectInfo().ApplyAuraPeriod; + _period = (int)GetSpellEffectInfo().ApplyAuraPeriod; // prepare periodics switch (GetAuraType()) { case AuraType.ObsModPower: - // 3 spells have no amplitude set - if (m_period == 0) - m_period = 1 * Time.InMilliseconds; - break; case AuraType.PeriodicDamage: case AuraType.PeriodicHeal: case AuraType.ObsModHealth: @@ -155,7 +162,7 @@ namespace Game.Spells break; } - GetBase().CallScriptEffectCalcPeriodicHandlers(this, ref m_isPeriodic, ref m_period); + GetBase().CallScriptEffectCalcPeriodicHandlers(this, ref m_isPeriodic, ref _period); if (!m_isPeriodic) return; @@ -163,42 +170,41 @@ namespace Game.Spells Player modOwner = caster != null ? caster.GetSpellModOwner() : null; // Apply casting time mods - if (m_period != 0) + if (_period != 0) { // Apply periodic time mod if (modOwner != null) - modOwner.ApplySpellMod(GetSpellInfo(), SpellModOp.ActivationTime, ref m_period); + modOwner.ApplySpellMod(GetSpellInfo(), SpellModOp.ActivationTime, ref _period); if (caster != null) { // Haste modifies periodic time of channeled spells if (m_spellInfo.IsChanneled()) - caster.ModSpellDurationTime(m_spellInfo, ref m_period); + caster.ModSpellDurationTime(m_spellInfo, ref _period); else if (m_spellInfo.HasAttribute(SpellAttr5.HasteAffectDuration)) - m_period = (int)(m_period * caster.m_unitData.ModCastingSpeed); + _period = (int)(_period * caster.m_unitData.ModCastingSpeed); } } + else // prevent infinite loop on Update + m_isPeriodic = false; if (load) // aura loaded from db { - m_tickNumber = (uint)(m_period != 0 ? GetBase().GetDuration() / m_period : 0); - m_periodicTimer = m_period != 0 ? GetBase().GetDuration() % m_period : 0; + if (_period != 0 && !GetBase().IsPermanent()) + { + uint elapsedTime = (uint)(GetBase().GetMaxDuration() - GetBase().GetDuration()); + _ticksDone = elapsedTime / (uint)_period; + _periodicTimer = (int)(elapsedTime % _period); + } + if (m_spellInfo.HasAttribute(SpellAttr5.StartPeriodicAtApply)) - ++m_tickNumber; + ++_ticksDone; } else // aura just created or reapplied { - m_tickNumber = 0; - // reset periodic timer on aura create or reapply // we don't reset periodic timers when aura is triggered by proc - if (resetPeriodicTimer) - { - m_periodicTimer = 0; - // Start periodic on next tick or at aura apply - if (m_period != 0 && !m_spellInfo.HasAttribute(SpellAttr5.StartPeriodicAtApply)) - m_periodicTimer += m_period; - } + ResetPeriodic(resetPeriodicTimer); } } public void CalculateSpellMod() @@ -397,13 +403,16 @@ namespace Game.Spells { if (m_isPeriodic && (GetBase().GetDuration() >= 0 || GetBase().IsPassive() || GetBase().IsPermanent())) { - if (m_periodicTimer > diff) - m_periodicTimer -= (int)diff; - else // tick also at m_periodicTimer == 0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N + _periodicTimer += (int)diff; + while (_periodicTimer >= _period) { - ++m_tickNumber; - // update before tick (aura can be removed in TriggerSpell or PeriodicTick calls) - m_periodicTimer += m_period - (int)diff; + _periodicTimer -= _period; + + if (!GetBase().IsPermanent() && (_ticksDone + 1) > GetTotalTicks()) + break; + + ++_ticksDone; + UpdatePeriodic(caster); GetApplicationList(out List effectApplications); @@ -481,7 +490,7 @@ namespace Game.Spells // on 2 tick - 133% (handled in 6 second) // Apply bonus for 1 - 4 tick - switch (m_tickNumber) + switch (_ticksDone) { case 1: // 0% aurEff.ChangeAmount(0); @@ -874,7 +883,7 @@ namespace Game.Spells public uint GetId() { return m_spellInfo.Id; } public uint GetEffIndex() { return _effectInfo.EffectIndex; } public int GetBaseAmount() { return m_baseAmount; } - public int GetPeriod() { return m_period; } + public int GetPeriod() { return _period; } public int GetMiscValueB() { return GetSpellEffectInfo().MiscValueB; } public int GetMiscValue() { return GetSpellEffectInfo().MiscValue; } @@ -883,8 +892,8 @@ namespace Game.Spells public bool HasAmount() { return m_amount != 0; } public void SetAmount(int _amount) { m_amount = _amount; m_canBeRecalculated = false; } - public int GetPeriodicTimer() { return m_periodicTimer; } - public void SetPeriodicTimer(int periodicTimer) { m_periodicTimer = periodicTimer; } + public int GetPeriodicTimer() { return _periodicTimer; } + public void SetPeriodicTimer(int periodicTimer) { _periodicTimer = periodicTimer; } void RecalculateAmount() { @@ -909,17 +918,10 @@ namespace Game.Spells public void SetDonePct(float val) { m_donePct = val; } public float GetDonePct() { return m_donePct; } - public uint GetTickNumber() { return m_tickNumber; } - public int GetTotalTicks() - { - return m_period != 0 ? (GetBase().GetMaxDuration() / m_period) : 1; - } - public void ResetPeriodic(bool resetPeriodicTimer = false) - { - if (resetPeriodicTimer) - m_periodicTimer = m_period; - m_tickNumber = 0; - } + public void ResetTicks() { _ticksDone = 0; } + public uint GetTickNumber() { return _ticksDone; } + public uint GetRemainingTicks() { return GetTotalTicks() - _ticksDone; } + public uint GetTotalTicks() { return (_period != 0 && !GetBase().IsPermanent()) ? (uint)(GetBase().GetMaxDuration() / _period) : 0u; } public bool IsPeriodic() { return m_isPeriodic; } void SetPeriodic(bool isPeriodic) { m_isPeriodic = isPeriodic; } @@ -946,9 +948,10 @@ namespace Game.Spells float m_critChance; float m_donePct; - int m_periodicTimer; - int m_period; - uint m_tickNumber; + // periodic stuff + int _periodicTimer; + int _period; // time between consecutive ticks + uint _ticksDone; // ticks counter bool m_canBeRecalculated; bool m_isPeriodic; @@ -5276,14 +5279,14 @@ namespace Game.Spells return; // Negative Energy Periodic case 46284: - target.CastCustomSpell(triggerSpellId, SpellValueMod.MaxTargets, (int)(m_tickNumber / 10 + 1), null, true, null, this); + target.CastCustomSpell(triggerSpellId, SpellValueMod.MaxTargets, (int)(_ticksDone / 10 + 1), null, true, null, this); return; // Poison (Grobbulus) case 28158: case 54362: // Slime Pool (Dreadscale & Acidmaw) case 66882: - target.CastCustomSpell(triggerSpellId, SpellValueMod.RadiusMod, (int)((((float)m_tickNumber / 60) * 0.9f + 0.1f) * 10000 * 2 / 3), null, true, null, this); + target.CastCustomSpell(triggerSpellId, SpellValueMod.RadiusMod, (int)(((_ticksDone / 60.0f) * 0.9f + 0.1f) * 10000.0f * 2.0f / 3.0f), null, true, null, this); return; // Slime Spray - temporary here until preventing default effect works again // added on 9.10.2010 @@ -5443,7 +5446,7 @@ namespace Game.Spells case 72854: // Unbound Plague case 72855: // Unbound Plague case 72856: // Unbound Plague - damage *= (uint)Math.Pow(1.25f, m_tickNumber); + damage *= (uint)Math.Pow(1.25f, _ticksDone); break; default: break; diff --git a/Source/Game/Spells/SpellManager.cs b/Source/Game/Spells/SpellManager.cs index d14dadff3..b4f04549e 100644 --- a/Source/Game/Spells/SpellManager.cs +++ b/Source/Game/Spells/SpellManager.cs @@ -2431,7 +2431,7 @@ namespace Game.Entities if (effect.EffectIndex >= SpellConst.MaxEffects) { - Log.outError(LogFilter.Sql, $"Serverside spell {spellId} difficulty {difficulty} has more than 32 effects, effect at index {effect.EffectIndex} skipped"); + Log.outError(LogFilter.Sql, $"Serverside spell {spellId} difficulty {difficulty} has more than {SpellConst.MaxEffects} effects, effect at index {effect.EffectIndex} skipped"); continue; } @@ -2960,6 +2960,30 @@ namespace Game.Entities switch (spellInfo.Id) { + case 6727: // Poison Mushroom + case 7331: // Healing Aura (TEST) (Rank 1) + /* + 30400, // Nether Beam - Perseverance + Blizzlike to have it disabled? DBC says: + "This is currently turned off to increase performance. Enable this to make it fire more frequently." + */ + case 34589: // Dangerous Water + case 52562: // Arthas Zombie Catcher + case 57550: // Tirion Aggro + case 65755: + spellInfo.GetEffect(0).ApplyAuraPeriod = 1 * Time.InMilliseconds; + break; + case 24707: // Food + case 26263: // Dim Sum + case 29055: // Refreshing Red Apple + case 37504: // Karazhan - Chess NPC AI, action timer + // first effect has correct amplitude + spellInfo.GetEffect(1).ApplyAuraPeriod = spellInfo.GetEffect(0).ApplyAuraPeriod; + break; + // Vomit + case 43327: + spellInfo.GetEffect(1).ApplyAuraPeriod = 1 * Time.InMilliseconds; + break; case 63026: // Summon Aspirant Test NPC (HACK: Target shouldn't be changed) case 63137: // Summon Valiant Test (HACK: Target shouldn't be changed; summon position should be untied from spell destination) spellInfo.GetEffect(0).TargetA = new SpellImplicitTargetInfo(Targets.DestDb); @@ -3356,7 +3380,7 @@ namespace Game.Entities break; case 69846: // Frost Bomb spellInfo.Speed = 0.0f; // This spell's summon happens instantly - break; + break; case 70106: // Chilled to the Bone spellInfo.AttributesEx3 |= SpellAttr3.NoDoneBonus; spellInfo.AttributesEx6 |= SpellAttr6.NoDonePctDamageMods; @@ -4001,14 +4025,12 @@ namespace Game.Entities struct ServersideSpellName { public SpellNameRecord Name; - public string NameStorage; public ServersideSpellName(uint id, string name) { Name = new(); Name.Name = new LocalizedString(); - NameStorage = name; Name.Id = id; for (Locale i = 0; i < Locale.Total; ++i) Name.Name[i] = name; diff --git a/Source/Scripts/Spells/Druid.cs b/Source/Scripts/Spells/Druid.cs index 405e15a95..830889bed 100644 --- a/Source/Scripts/Spells/Druid.cs +++ b/Source/Scripts/Spells/Druid.cs @@ -455,9 +455,15 @@ namespace Scripts.Spells.Druid { void CalculateAmount(AuraEffect aurEff, ref int amount, ref bool canBeRecalculated) { + if (aurEff.GetTotalTicks() == 0) + { + amount = 0; + return; + } + Unit caster = GetCaster(); if (caster) - amount = MathFunctions.CalculatePct(caster.GetCreatePowers(PowerType.Mana), amount) / aurEff.GetTotalTicks(); + amount = MathFunctions.CalculatePct(caster.GetCreatePowers(PowerType.Mana), amount) / (int)aurEff.GetTotalTicks(); else amount = 0; } diff --git a/Source/Scripts/Spells/Generic.cs b/Source/Scripts/Spells/Generic.cs index d62670c16..ca95f1c16 100644 --- a/Source/Scripts/Spells/Generic.cs +++ b/Source/Scripts/Spells/Generic.cs @@ -1321,7 +1321,7 @@ namespace Scripts.Spells.Generic { void CalculateAmount(AuraEffect aurEff, ref int amount, ref bool canBeRecalculated) { - if (!GetCaster()) + if (!GetCaster() || aurEff.GetTotalTicks() == 0) return; float heal = 0.0f;