diff --git a/Source/Game/Networking/Packets/SpellPackets.cs b/Source/Game/Networking/Packets/SpellPackets.cs index cd2b12f0f..0d89ef0f0 100644 --- a/Source/Game/Networking/Packets/SpellPackets.cs +++ b/Source/Game/Networking/Packets/SpellPackets.cs @@ -501,16 +501,32 @@ namespace Game.Networking.Packets _worldPacket.WriteUInt32(SpellID); _worldPacket.WriteInt32(DeltaTime); _worldPacket.WriteBit(IsPet); - _worldPacket.WriteBit(WithoutCategoryCooldown); + _worldPacket.WriteBit(SkipCategory); _worldPacket.FlushBits(); } public bool IsPet; - public bool WithoutCategoryCooldown; + public bool SkipCategory; public int DeltaTime; public uint SpellID; } + class UpdateCooldown : ServerPacket + { + public uint SpellID; + public float ModChange = 1.0f; + public float ModRate = 1.0f; + + public UpdateCooldown() : base(ServerOpcodes.UpdateCooldown, ConnectionType.Instance) { } + + public override void Write() + { + _worldPacket.WriteUInt32(SpellID); + _worldPacket.WriteFloat(ModChange); + _worldPacket.WriteFloat(ModRate); + } + } + public class SpellCooldownPkt : ServerPacket { public SpellCooldownPkt() : base(ServerOpcodes.SpellCooldown, ConnectionType.Instance) { } diff --git a/Source/Game/Spells/Auras/AuraEffect.cs b/Source/Game/Spells/Auras/AuraEffect.cs index 78c7310d1..1766c487e 100644 --- a/Source/Game/Spells/Auras/AuraEffect.cs +++ b/Source/Game/Spells/Auras/AuraEffect.cs @@ -5813,6 +5813,35 @@ namespace Game.Spells target.RemoveSpellCategoryCooldownMod(GetMiscValue(), GetAmount()); } + [AuraEffectHandler(AuraType.ModRecoveryRate)] + void HandleModRecoveryRate(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) + { + if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask)) + return; + + float rate = 100.0f / (Math.Max(GetAmount(), -99.0f) + 100.0f); + + aurApp.GetTarget().GetSpellHistory().UpdateCooldownRecoveryRate(cooldown => + { + return IsAffectingSpell(Global.SpellMgr.GetSpellInfo(cooldown.SpellId, Difficulty.None)); + }, rate, apply); + } + + [AuraEffectHandler(AuraType.ModRecoveryRateBySpellLabel)] + void HandleModRecoveryRateBySpellLabel(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) + { + if (!mode.HasAnyFlag(AuraEffectHandleModes.ChangeAmountMask)) + return; + + float rate = 100.0f / (Math.Max(GetAmount(), -99.0f) + 100.0f); + + aurApp.GetTarget().GetSpellHistory().UpdateCooldownRecoveryRate(cooldown => + { + SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(cooldown.SpellId, Difficulty.None); + return spellInfo.HasLabel((uint)GetMiscValue()) || (GetMiscValueB() != 0 && spellInfo.HasLabel((uint)GetMiscValueB())); + }, rate, apply); + } + [AuraEffectHandler(AuraType.ShowConfirmationPrompt)] [AuraEffectHandler(AuraType.ShowConfirmationPromptWithDifficulty)] void HandleShowConfirmationPrompt(AuraApplication aurApp, AuraEffectHandleModes mode, bool apply) diff --git a/Source/Game/Spells/SpellHistory.cs b/Source/Game/Spells/SpellHistory.cs index b1d1bee4e..ae37b7452 100644 --- a/Source/Game/Spells/SpellHistory.cs +++ b/Source/Game/Spells/SpellHistory.cs @@ -354,6 +354,8 @@ namespace Game.Spells { if (!forcedCooldown.HasValue) { + TimeSpan baseCooldown = cooldown; + // Now we have cooldown data (if found any), time to apply mods Player modOwner = _owner.GetSpellModOwner(); if (modOwner != null) @@ -384,6 +386,35 @@ namespace Game.Spells categoryCooldown = TimeSpan.FromMilliseconds(categoryCooldown.TotalMilliseconds * _owner.m_unitData.ModHasteRegen); } + { + float calcRecoveryRate(AuraEffect modRecoveryRate) + { + float rate = 100.0f / (Math.Max(modRecoveryRate.GetAmount(), -99.0f) + 100.0f); + if (baseCooldown <= TimeSpan.FromHours(1) + && !spellInfo.HasAttribute(SpellAttr6.IgnoreForModTimeRate) + && !modRecoveryRate.GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes.IgnoreDuringCooldownTimeRateCalculation)) + rate *= _owner.m_unitData.ModTimeRate; + + return rate; + } + + + float recoveryRate = 1.0f; + foreach (AuraEffect modRecoveryRate in _owner.GetAuraEffectsByType(AuraType.ModRecoveryRate)) + if (modRecoveryRate.IsAffectingSpell(spellInfo)) + recoveryRate *= calcRecoveryRate(modRecoveryRate); + + foreach (AuraEffect modRecoveryRate in _owner.GetAuraEffectsByType(AuraType.ModRecoveryRateBySpellLabel)) + if (spellInfo.HasLabel((uint)modRecoveryRate.GetMiscValue()) || (modRecoveryRate.GetMiscValueB() != 0 && spellInfo.HasLabel((uint)modRecoveryRate.GetMiscValueB()))) + recoveryRate *= calcRecoveryRate(modRecoveryRate); + + if (recoveryRate > 0.0f) + { + cooldown = TimeSpan.FromMilliseconds((long)(cooldown.TotalMilliseconds * recoveryRate)); + categoryCooldown = TimeSpan.FromMilliseconds((long)(categoryCooldown.TotalMilliseconds * recoveryRate)); + } + } + int cooldownMod = _owner.GetTotalAuraModifier(AuraType.ModCooldown); if (cooldownMod != 0) { @@ -535,7 +566,7 @@ namespace Game.Spells modifyCooldown.IsPet = _owner != playerOwner; modifyCooldown.SpellID = cooldownEntry.SpellId; modifyCooldown.DeltaTime = (int)cooldownMod.TotalMilliseconds; - modifyCooldown.WithoutCategoryCooldown = withoutCategoryCooldown; + modifyCooldown.SkipCategory = withoutCategoryCooldown; playerOwner.SendPacket(modifyCooldown); } @@ -546,6 +577,40 @@ namespace Game.Spells } } + public void UpdateCooldownRecoveryRate(Func predicate, float modChange, bool apply) + { + foreach (var cooldownEntry in _spellCooldowns.Values) + { + if (predicate(cooldownEntry)) + UpdateCooldownRecoveryRate(cooldownEntry, modChange, apply); + } + } + + public void UpdateCooldownRecoveryRate(CooldownEntry cooldownEntry, float modChange, bool apply) + { + if (modChange <= 0.0f) + return; + + if (!apply) + modChange = 1.0f / modChange; + + DateTime now = GameTime.GetDateAndTime(); + + cooldownEntry.CooldownEnd = now + TimeSpan.FromMilliseconds((cooldownEntry.CooldownEnd - now).TotalMilliseconds * modChange); + + if (cooldownEntry.CategoryId != 0) + cooldownEntry.CategoryEnd = now + TimeSpan.FromMilliseconds((cooldownEntry.CategoryEnd - now).TotalMilliseconds * modChange); + + Player playerOwner = GetPlayerOwner(); + if (playerOwner != null) + { + UpdateCooldown updateCooldown = new(); + updateCooldown.SpellID = cooldownEntry.SpellId; + updateCooldown.ModChange = modChange; + playerOwner.SendPacket(updateCooldown); + } + } + public void ModifyCooldown(uint spellId, TimeSpan cooldownMod, bool withoutCategoryCooldown = false) { SpellInfo spellInfo = Global.SpellMgr.GetSpellInfo(spellId, _owner.GetMap().GetDifficultyID());