/* * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or (at your * option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . */ #ifndef TRINITYCORE_SPELL_HISTORY_H #define TRINITYCORE_SPELL_HISTORY_H #include "Concepts.h" #include "DatabaseEnvFwd.h" #include "Duration.h" #include "GameTime.h" #include "Optional.h" #include "SharedDefines.h" #include #include #include class Item; class Player; class Spell; class SpellInfo; class Unit; namespace WorldPackets::Pet { class PetSpells; } namespace WorldPackets::Spells { class SendSpellHistory; class SendSpellCharges; } /// Spell cooldown flags sent in SMSG_SPELL_COOLDOWN enum SpellCooldownFlags { SPELL_COOLDOWN_FLAG_NONE = 0x0, SPELL_COOLDOWN_FLAG_INCLUDE_GCD = 0x1, ///< Starts GCD in addition to normal cooldown specified in the packet SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS = 0x2, ///< Starts GCD for spells that should start their cooldown on events, requires SPELL_COOLDOWN_FLAG_INCLUDE_GCD set SPELL_COOLDOWN_FLAG_LOSS_OF_CONTROL_UI = 0x4, ///< Shows interrupt cooldown in loss of control ui SPELL_COOLDOWN_FLAG_ON_HOLD = 0x8 ///< Forces cooldown to behave as if SpellInfo::IsCooldownStartedOnEvent was true }; class TC_GAME_API SpellHistory { public: using Clock = std::chrono::system_clock; using Duration = Milliseconds; // Cooldowns are stored only with millisecond precision, not whatever Clock's precision is using TimePoint = std::chrono::time_point; struct CooldownEntry { uint32 SpellId = 0; TimePoint CooldownEnd = TimePoint::min(); uint32 ItemId = 0; uint32 CategoryId = 0; TimePoint CategoryEnd = TimePoint::min(); bool OnHold = false; }; struct ChargeEntry { ChargeEntry() = default; ChargeEntry(TimePoint startTime, Duration rechargeTime) : RechargeStart(startTime), RechargeEnd(startTime + rechargeTime) { } ChargeEntry(TimePoint startTime, TimePoint endTime) : RechargeStart(startTime), RechargeEnd(endTime) { } TimePoint RechargeStart; TimePoint RechargeEnd; }; using ChargeEntryCollection = std::deque; using CooldownStorageType = std::unordered_map; using CategoryCooldownStorageType = std::unordered_map; using ChargeStorageType = std::unordered_map; using GlobalCooldownStorageType = std::unordered_map; explicit SpellHistory(Unit* owner); ~SpellHistory(); SpellHistory(SpellHistory const&) = delete; SpellHistory(SpellHistory&&) = delete; SpellHistory& operator=(SpellHistory const&) = delete; SpellHistory& operator=(SpellHistory&&) = delete; template void LoadFromDB(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult); template void SaveToDB(CharacterDatabaseTransaction trans); void Update(); void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr); void HandleCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = nullptr); bool IsReady(SpellInfo const* spellInfo, uint32 itemId = 0) const; void WritePacket(WorldPackets::Spells::SendSpellHistory* sendSpellHistory) const; void WritePacket(WorldPackets::Spells::SendSpellCharges* sendSpellCharges) const; void WritePacket(WorldPackets::Pet::PetSpells* petSpells) const; // Cooldowns static Duration const InfinityCooldownDelay; // used for set "infinity cooldowns" for spells and check void StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = nullptr, bool onHold = false, Optional forcedCooldown = {}); void SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId = 0, Spell* spell = nullptr, bool startCooldown = true); void AddCooldown(uint32 spellId, uint32 itemId, Duration cooldownDuration) { TimePoint now = time_point_cast(GameTime::GetTime()); AddCooldown(spellId, itemId, now + cooldownDuration, 0, now); } void AddCooldown(uint32 spellId, uint32 itemId, TimePoint cooldownEnd, uint32 categoryId, TimePoint categoryEnd, bool onHold = false); void ModifyCooldown(uint32 spellId, Duration cooldownMod, bool withoutCategoryCooldown = false); void ModifyCooldown(SpellInfo const* spellInfo, Duration cooldownMod, bool withoutCategoryCooldown = false); template Predicate> void ModifyCoooldowns(Predicate&& predicate, Duration cooldownMod, bool withoutCategoryCooldown = false) { for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();) { if (std::forward(predicate)(itr->second)) ModifySpellCooldown(itr, cooldownMod, withoutCategoryCooldown); else ++itr; } } template Predicate> void UpdateCooldownRecoveryRate(Predicate&& predicate, float modChange, bool apply) { for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr) { if (std::forward(predicate)(itr->second)) UpdateCooldownRecoveryRate(itr, modChange, apply); } } void ResetCooldown(uint32 spellId, bool update = false); template Predicate> void ResetCooldowns(Predicate&& predicate, bool update = false) { std::vector resetCooldowns; resetCooldowns.reserve(_spellCooldowns.size()); for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();) { if (std::forward(predicate)(itr->second)) { resetCooldowns.push_back(int32(itr->first)); ResetCooldown(itr, false); } else ++itr; } if (update && !resetCooldowns.empty()) SendClearCooldowns(resetCooldowns); } void ResetAllCooldowns(); bool HasCooldown(SpellInfo const* spellInfo, uint32 itemId = 0) const; bool HasCooldown(uint32 spellId, uint32 itemId = 0) const; Duration GetRemainingCooldown(SpellInfo const* spellInfo) const; Duration GetRemainingCategoryCooldown(uint32 categoryId) const; Duration GetRemainingCategoryCooldown(SpellInfo const* spellInfo) const; // School lockouts void LockSpellSchool(SpellSchoolMask schoolMask, Duration lockoutTime); bool IsSchoolLocked(SpellSchoolMask schoolMask) const; // Charges void ConsumeCharge(uint32 chargeCategoryId); void ModifyChargeRecoveryTime(uint32 chargeCategoryId, Duration cooldownMod); void UpdateChargeRecoveryRate(uint32 chargeCategoryId, float modChange, bool apply); void RestoreCharge(uint32 chargeCategoryId); void ResetCharges(uint32 chargeCategoryId); void ResetAllCharges(); bool HasCharge(uint32 chargeCategoryId) const; int32 GetMaxCharges(uint32 chargeCategoryId) const; int32 GetChargeRecoveryTime(uint32 chargeCategoryId) const; // Global cooldown bool HasGlobalCooldown(SpellInfo const* spellInfo) const; void AddGlobalCooldown(SpellInfo const* spellInfo, Duration duration); void CancelGlobalCooldown(SpellInfo const* spellInfo); Duration GetRemainingGlobalCooldown(SpellInfo const* spellInfo) const; bool IsPaused() const { return _pauseTime.has_value(); } void PauseCooldowns(); void ResumeCooldowns(); static void GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, Duration* cooldown, uint32* categoryId, Duration* categoryCooldown); void SaveCooldownStateBeforeDuel(); void RestoreCooldownStateAfterDuel(); 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 const& cooldowns) const; CooldownStorageType::iterator EraseCooldown(CooldownStorageType::iterator itr) { _categoryCooldowns.erase(itr->second.CategoryId); return _spellCooldowns.erase(itr); } void SendSetSpellCharges(uint32 chargeCategoryId, ChargeEntryCollection const& chargeCollection) const; Unit* _owner; CooldownStorageType _spellCooldowns; CooldownStorageType _spellCooldownsBeforeDuel; CategoryCooldownStorageType _categoryCooldowns; TimePoint _schoolLockouts[MAX_SPELL_SCHOOL]; ChargeStorageType _categoryCharges; GlobalCooldownStorageType _globalCooldowns; Optional _pauseTime; template struct PersistenceHelper { }; }; #endif // TRINITYCORE_SPELL_HISTORY_H