aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Server/Packets/SpellPackets.cpp11
-rw-r--r--src/server/game/Server/Packets/SpellPackets.h14
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp2
-rw-r--r--src/server/game/Spells/Auras/SpellAuraDefines.h2
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp31
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.h2
-rw-r--r--src/server/game/Spells/SpellHistory.cpp57
-rw-r--r--src/server/game/Spells/SpellHistory.h11
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp6
9 files changed, 126 insertions, 10 deletions
diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp
index 90de14da4df..23391e57175 100644
--- a/src/server/game/Server/Packets/SpellPackets.cpp
+++ b/src/server/game/Server/Packets/SpellPackets.cpp
@@ -612,12 +612,21 @@ WorldPacket const* ModifyCooldown::Write()
_worldPacket << int32(SpellID);
_worldPacket << int32(DeltaTime);
_worldPacket << Bits<1>(IsPet);
- _worldPacket << Bits<1>(WithoutCategoryCooldown);
+ _worldPacket << Bits<1>(SkipCategory);
_worldPacket.FlushBits();
return &_worldPacket;
}
+WorldPacket const* UpdateCooldown::Write()
+{
+ _worldPacket << int32(SpellID);
+ _worldPacket << float(ModChange);
+ _worldPacket << float(ModRate);
+
+ return &_worldPacket;
+}
+
ByteBuffer& operator<<(ByteBuffer& data, SpellCooldownStruct const& cooldown)
{
data << uint32(cooldown.SrecID);
diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h
index 14ade1c2a71..63798895ebb 100644
--- a/src/server/game/Server/Packets/SpellPackets.h
+++ b/src/server/game/Server/Packets/SpellPackets.h
@@ -561,11 +561,23 @@ namespace WorldPackets
WorldPacket const* Write() override;
bool IsPet = false;
- bool WithoutCategoryCooldown = false;
+ bool SkipCategory = false;
int32 DeltaTime = 0;
int32 SpellID = 0;
};
+ class UpdateCooldown final : public ServerPacket
+ {
+ public:
+ UpdateCooldown() : ServerPacket(SMSG_UPDATE_COOLDOWN, 4 + 4 + 4) { }
+
+ WorldPacket const* Write() override;
+
+ int32 SpellID = 0;
+ float ModChange = 1.0f;
+ float ModRate = 1.0f;
+ };
+
struct SpellCooldownStruct
{
SpellCooldownStruct() { }
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index bf1576592f9..c1ebd755f4c 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -2203,7 +2203,7 @@ void OpcodeTable::InitializeServerOpcodes()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_CELESTIAL_BODY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_CHARACTER_FLAGS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_CHARGE_CATEGORY_COOLDOWN, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_COOLDOWN, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_COOLDOWN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_CRAFTING_NPC_RECIPES, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_DAILY_MISSION_COUNTER, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_UPDATE_EXPANSION_LEVEL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h
index aeff29951af..de69713eccc 100644
--- a/src/server/game/Spells/Auras/SpellAuraDefines.h
+++ b/src/server/game/Spells/Auras/SpellAuraDefines.h
@@ -234,7 +234,7 @@ enum AuraType : uint32
SPELL_AURA_MOD_RANGED_HASTE = 140,
SPELL_AURA_141 = 141, // old SPELL_AURA_MOD_RANGED_AMMO_HASTE, unused now
SPELL_AURA_MOD_BASE_RESISTANCE_PCT = 142,
- SPELL_AURA_MOD_RECOVERY_RATE_BY_SPELL_LABEL = 143, // NYI
+ SPELL_AURA_MOD_RECOVERY_RATE_BY_SPELL_LABEL = 143,
SPELL_AURA_SAFE_FALL = 144,
SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT2 = 145,
SPELL_AURA_ALLOW_TAME_PET_TYPE = 146,
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index bbe1339d171..2b455e8fba5 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -212,7 +212,7 @@ NonDefaultConstructible<pAuraEffectHandler> AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleAuraModRangedHaste, //140 SPELL_AURA_MOD_RANGED_HASTE
&AuraEffect::HandleUnused, //141 SPELL_AURA_141
&AuraEffect::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT
- &AuraEffect::HandleNULL, //143 SPELL_AURA_MOD_RECOVERY_RATE_BY_SPELL_LABEL
+ &AuraEffect::HandleModRecoveryRateBySpellLabel, //143 SPELL_AURA_MOD_RECOVERY_RATE_BY_SPELL_LABEL also implemented in SpellHistory::StartCooldown
&AuraEffect::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes
&AuraEffect::HandleAuraModIncreaseHealthPercent, //145 SPELL_AURA_MOD_INCREASE_HEALTH_PERCENT2
&AuraEffect::HandleNoImmediateEffect, //146 SPELL_AURA_ALLOW_TAME_PET_TYPE
@@ -355,7 +355,7 @@ NonDefaultConstructible<pAuraEffectHandler> AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNoImmediateEffect, //283 SPELL_AURA_MOD_HEALING_RECEIVED implemented in Unit::SpellHealingBonus
&AuraEffect::HandleAuraLinked, //284 SPELL_AURA_LINKED
&AuraEffect::HandleAuraLinked, //285 SPELL_AURA_LINKED_2
- &AuraEffect::HandleNULL, //286 SPELL_AURA_MOD_RECOVERY_RATE
+ &AuraEffect::HandleModRecoveryRate, //286 SPELL_AURA_MOD_RECOVERY_RATE also implemented in SpellHistory::StartCooldown
&AuraEffect::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult
&AuraEffect::HandleNoImmediateEffect, //288 SPELL_AURA_IGNORE_HIT_DIRECTION implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult Unit::RollMeleeOutcomeAgainst
&AuraEffect::HandleNoImmediateEffect, //289 SPELL_AURA_PREVENT_DURABILITY_LOSS implemented in Player::DurabilityPointsLoss
@@ -6239,6 +6239,33 @@ void AuraEffect::HandleModSpellCategoryCooldown(AuraApplication const* aurApp, u
target->RemoveSpellCategoryCooldownMod(GetMiscValue(), GetAmount());
}
+void AuraEffect::HandleModRecoveryRate(AuraApplication const* aurApp, uint8 mode, bool apply) const
+{
+ if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
+ return;
+
+ float rate = 100.0f / (std::max<float>(GetAmount(), -99.0f) + 100.0f);
+
+ aurApp->GetTarget()->GetSpellHistory()->UpdateCooldownRecoveryRate([&](SpellHistory::CooldownEntry const& cooldown)
+ {
+ return IsAffectingSpell(sSpellMgr->GetSpellInfo(cooldown.SpellId, DIFFICULTY_NONE));
+ }, rate, apply);
+}
+
+void AuraEffect::HandleModRecoveryRateBySpellLabel(AuraApplication const* aurApp, uint8 mode, bool apply) const
+{
+ if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
+ return;
+
+ float rate = 100.0f / (std::max<float>(GetAmount(), -99.0f) + 100.0f);
+
+ aurApp->GetTarget()->GetSpellHistory()->UpdateCooldownRecoveryRate([&](SpellHistory::CooldownEntry const& cooldown)
+ {
+ SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(cooldown.SpellId, DIFFICULTY_NONE);
+ return spellInfo->HasLabel(GetMiscValue()) || (GetMiscValueB() && spellInfo->HasLabel(GetMiscValueB()));
+ }, rate, apply);
+}
+
void AuraEffect::HandleShowConfirmationPrompt(AuraApplication const* aurApp, uint8 mode, bool apply) const
{
if (!(mode & AURA_EFFECT_HANDLE_REAL))
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h
index f739a9793f8..c9d27770593 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.h
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.h
@@ -324,6 +324,8 @@ class TC_GAME_API AuraEffect
void HandleAuraForceWeather(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleEnableAltPower(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleModSpellCategoryCooldown(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+ void HandleModRecoveryRate(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+ void HandleModRecoveryRateBySpellLabel(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleShowConfirmationPrompt(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleOverridePetSpecs(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAllowUsingGameobjectsWhileMounted(AuraApplication const* aurApp, uint8 mode, bool apply) const;
diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp
index 207298a85a8..707018891c1 100644
--- a/src/server/game/Spells/SpellHistory.cpp
+++ b/src/server/game/Spells/SpellHistory.cpp
@@ -27,6 +27,7 @@
#include "PetPackets.h"
#include "Player.h"
#include "Spell.h"
+#include "SpellAuraEffects.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "SpellPackets.h"
@@ -422,6 +423,8 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
{
if (!forcedCooldown)
{
+ Duration baseCooldown = cooldown;
+
// Now we have cooldown data (if found any), time to apply mods
if (Player* modOwner = _owner->GetSpellModOwner())
{
@@ -451,6 +454,34 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
categoryCooldown = Duration(int64(categoryCooldown.count() * _owner->m_unitData->ModHasteRegen));
}
+ {
+ auto calcRecoveryRate = [&](AuraEffect const* modRecoveryRate)
+ {
+ float rate = 100.0f / (std::max<float>(modRecoveryRate->GetAmount(), -99.0f) + 100.0f);
+ if (baseCooldown <= 1h
+ && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_FOR_MOD_TIME_RATE)
+ && !modRecoveryRate->GetSpellEffectInfo().EffectAttributes.HasFlag(SpellEffectAttributes::IgnoreDuringCooldownTimeRateCalculation))
+ rate *= *_owner->m_unitData->ModTimeRate;
+
+ return rate;
+ };
+
+ float recoveryRate = 1.0f;
+ for (AuraEffect const* modRecoveryRate : _owner->GetAuraEffectsByType(SPELL_AURA_MOD_RECOVERY_RATE))
+ if (modRecoveryRate->IsAffectingSpell(spellInfo))
+ recoveryRate *= calcRecoveryRate(modRecoveryRate);
+
+ for (AuraEffect const* modRecoveryRate : _owner->GetAuraEffectsByType(SPELL_AURA_MOD_RECOVERY_RATE_BY_SPELL_LABEL))
+ if (spellInfo->HasLabel(modRecoveryRate->GetMiscValue()) || (modRecoveryRate->GetMiscValueB() && spellInfo->HasLabel(modRecoveryRate->GetMiscValueB())))
+ recoveryRate *= calcRecoveryRate(modRecoveryRate);
+
+ if (recoveryRate > 0.0f)
+ {
+ cooldown = Duration(int64(cooldown.count() * recoveryRate));
+ categoryCooldown = Duration(int64(categoryCooldown.count() * recoveryRate));
+ }
+ }
+
if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN))
{
// Apply SPELL_AURA_MOD_COOLDOWN only to own spells
@@ -591,7 +622,7 @@ void SpellHistory::ModifySpellCooldown(CooldownStorageType::iterator& itr, Durat
modifyCooldown.IsPet = _owner != playerOwner;
modifyCooldown.SpellID = itr->second.SpellId;
modifyCooldown.DeltaTime = duration_cast<Milliseconds>(cooldownMod).count();
- modifyCooldown.WithoutCategoryCooldown = withoutCategoryCooldown;
+ modifyCooldown.SkipCategory = withoutCategoryCooldown;
playerOwner->SendDirectMessage(modifyCooldown.Write());
}
@@ -599,6 +630,30 @@ void SpellHistory::ModifySpellCooldown(CooldownStorageType::iterator& itr, Durat
itr = EraseCooldown(itr);
}
+void SpellHistory::UpdateCooldownRecoveryRate(CooldownStorageType::iterator& itr, float modChange, bool apply)
+{
+ if (modChange <= 0.0f)
+ return;
+
+ if (!apply)
+ modChange = 1.0f / modChange;
+
+ TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
+
+ itr->second.CooldownEnd = now + duration_cast<Duration>((itr->second.CooldownEnd - now) * modChange);
+
+ if (itr->second.CategoryId)
+ itr->second.CategoryEnd = now + duration_cast<Duration>((itr->second.CategoryEnd - now) * modChange);
+
+ if (Player* playerOwner = GetPlayerOwner())
+ {
+ WorldPackets::Spells::UpdateCooldown updateCooldown;
+ updateCooldown.SpellID = itr->second.SpellId;
+ updateCooldown.ModChange = modChange;
+ playerOwner->SendDirectMessage(updateCooldown.Write());
+ }
+}
+
void SpellHistory::ModifyCooldown(uint32 spellId, Duration cooldownMod, bool withoutCategoryCooldown)
{
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, _owner->GetMap()->GetDifficultyID()))
diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h
index 536cb86ece4..b731cbfc329 100644
--- a/src/server/game/Spells/SpellHistory.h
+++ b/src/server/game/Spells/SpellHistory.h
@@ -139,6 +139,16 @@ public:
}
}
+ template <Trinity::invocable_r<bool, CooldownEntry const&> Predicate>
+ void UpdateCooldownRecoveryRate(Predicate&& predicate, float modChange, bool apply)
+ {
+ for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
+ {
+ if (std::forward<Predicate>(predicate)(itr->second))
+ UpdateCooldownRecoveryRate(itr, modChange, apply);
+ }
+ }
+
void ResetCooldown(uint32 spellId, bool update = false);
template <Trinity::invocable_r<bool, CooldownEntry const&> Predicate>
void ResetCooldowns(Predicate&& predicate, bool update = false)
@@ -200,6 +210,7 @@ private:
Player* GetPlayerOwner() const;
void ModifySpellCooldown(uint32 spellId, Duration cooldownMod, bool withoutCategoryCooldown);
void ModifySpellCooldown(CooldownStorageType::iterator& itr, Duration cooldownMod, bool withoutCategoryCooldown);
+ void UpdateCooldownRecoveryRate(CooldownStorageType::iterator& itr, float modChange, bool apply);
void ResetCooldown(CooldownStorageType::iterator& itr, bool update = false);
void SendClearCooldowns(std::vector<int32> const& cooldowns) const;
CooldownStorageType::iterator EraseCooldown(CooldownStorageType::iterator itr)
diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp
index e33bab1c47a..2d949cc2a4c 100644
--- a/src/server/scripts/Spells/spell_shaman.cpp
+++ b/src/server/scripts/Spells/spell_shaman.cpp
@@ -1694,11 +1694,11 @@ class spell_sha_molten_assault : public SpellScript
Trinity::WorldObjectListSearcher searcher(caster, targets, check, GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER);
Cell::VisitAllObjects(lavaLashTarget, searcher, range + EXTRA_CELL_SEARCH_RADIUS);
- auto withoutFlameShock = std::ranges::partition(targets, Trinity::UnitAuraCheck(true, SPELL_SHAMAN_FLAME_SHOCK, GetCaster()->GetGUID()));
- std::size_t flameShocksMissing = GetEffectValue() + 1 - std::ranges::distance(targets.begin(), withoutFlameShock.begin());
+ auto withoutFlameShockItr = std::partition(targets.begin(), targets.end(), Trinity::UnitAuraCheck(true, SPELL_SHAMAN_FLAME_SHOCK, GetCaster()->GetGUID()));
+ std::size_t flameShocksMissing = GetEffectValue() + 1 - std::ranges::distance(targets.begin(), withoutFlameShockItr);
if (flameShocksMissing)
- Trinity::Containers::RandomShuffle(withoutFlameShock);
+ Trinity::Containers::RandomShuffle(withoutFlameShockItr, targets.end());
CastSpellExtraArgs args;
args.SetTriggerFlags(TRIGGERED_IGNORE_GCD | TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD | TRIGGERED_IGNORE_POWER_COST | TRIGGERED_IGNORE_CAST_IN_PROGRESS);