diff options
-rw-r--r-- | src/server/game/Server/Packets/SpellPackets.cpp | 11 | ||||
-rw-r--r-- | src/server/game/Server/Packets/SpellPackets.h | 14 | ||||
-rw-r--r-- | src/server/game/Server/Protocol/Opcodes.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraDefines.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.cpp | 31 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuraEffects.h | 2 | ||||
-rw-r--r-- | src/server/game/Spells/SpellHistory.cpp | 57 | ||||
-rw-r--r-- | src/server/game/Spells/SpellHistory.h | 11 | ||||
-rw-r--r-- | src/server/scripts/Spells/spell_shaman.cpp | 6 |
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); |