aboutsummaryrefslogtreecommitdiff
path: root/src/server
diff options
context:
space:
mode:
authorariel- <ariel.silva305@gmail.com>2015-03-26 21:51:19 -0300
committerariel- <ariel.silva305@gmail.com>2015-04-13 12:13:46 -0300
commite70790576414dde16b56fd828d52a79ef540d3e9 (patch)
tree8700db9f05a094118574f9152d9541a23d3dad93 /src/server
parent61aa5ec412ee462e76f8d71d888706b02bfed372 (diff)
Port commit 56186319bdd41dd26b6cc14f84e6e41eef5d953b (6.x branch)
Core/Spells: Cooldown updates Updates #14418
Diffstat (limited to 'src/server')
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp7
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp79
-rw-r--r--src/server/game/Entities/Creature/Creature.h12
-rw-r--r--src/server/game/Entities/Item/Item.h2
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp107
-rw-r--r--src/server/game/Entities/Pet/Pet.h2
-rw-r--r--src/server/game/Entities/Player/Player.cpp533
-rw-r--r--src/server/game/Entities/Player/Player.h34
-rw-r--r--src/server/game/Entities/Totem/Totem.cpp3
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp161
-rw-r--r--src/server/game/Entities/Unit/Unit.h47
-rw-r--r--src/server/game/Handlers/PetHandler.cpp20
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp2
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h8
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp7
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp28
-rw-r--r--src/server/game/Spells/Spell.cpp85
-rw-r--r--src/server/game/Spells/Spell.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp13
-rw-r--r--src/server/game/Spells/SpellHistory.cpp641
-rw-r--r--src/server/game/Spells/SpellHistory.h137
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp8
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp11
-rw-r--r--src/server/scripts/Northrend/zone_storm_peaks.cpp1
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp7
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp19
-rw-r--r--src/server/scripts/Spells/spell_item.cpp17
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp19
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp5
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp45
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp7
-rw-r--r--src/server/scripts/Spells/spell_warrior.cpp3
-rw-r--r--src/server/scripts/World/npcs_special.cpp57
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.cpp7
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.h3
35 files changed, 1044 insertions, 1094 deletions
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp
index abb6126ca2c..2522c97de25 100644
--- a/src/server/game/AI/CoreAI/PetAI.cpp
+++ b/src/server/game/AI/CoreAI/PetAI.cpp
@@ -22,6 +22,7 @@
#include "Player.h"
#include "Spell.h"
#include "ObjectAccessor.h"
+#include "SpellHistory.h"
#include "SpellMgr.h"
#include "Creature.h"
#include "Util.h"
@@ -146,15 +147,15 @@ void PetAI::UpdateAI(uint32 diff)
if (!spellInfo)
continue;
- if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo))
+ if (me->GetCharmInfo() && me->GetSpellHistory()->HasGlobalCooldown(spellInfo))
continue;
if (spellInfo->IsPositive())
{
if (spellInfo->CanBeUsedInCombat())
{
- // check spell cooldown
- if (me->HasSpellCooldown(spellInfo->Id))
+ // check spell cooldown & school lock
+ if (!me->GetSpellHistory()->IsReady(spellInfo))
continue;
// Check if we're in combat or commanded to attack
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index cf0579200f7..4c949f54a3c 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -150,8 +150,6 @@ m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(
for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
m_spells[i] = 0;
- m_CreatureSpellCooldowns.clear();
- m_CreatureCategoryCooldowns.clear();
DisableReputationGain = false;
m_SightDistance = sWorld->getFloatConfig(CONFIG_SIGHT_MONSTER);
@@ -2154,83 +2152,6 @@ uint32 Creature::GetShieldBlockValue() const //dunno mob block
return (getLevel()/2 + uint32(GetStat(STAT_STRENGTH)/20));
}
-void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time)
-{
- m_CreatureSpellCooldowns[spell_id] = end_time;
-}
-
-void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time)
-{
- m_CreatureCategoryCooldowns[category] = apply_time;
-}
-
-void Creature::AddCreatureSpellCooldown(uint32 spellid)
-{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid);
- if (!spellInfo)
- return;
-
- uint32 cooldown = spellInfo->GetRecoveryTime();
- if (Player* modOwner = GetSpellModOwner())
- modOwner->ApplySpellMod(spellid, SPELLMOD_COOLDOWN, cooldown);
-
- if (cooldown)
- _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/IN_MILLISECONDS);
-
- if (spellInfo->GetCategory())
- _AddCreatureCategoryCooldown(spellInfo->GetCategory(), time(NULL));
-}
-
-bool Creature::HasCategoryCooldown(uint32 spell_id) const
-{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id);
- if (!spellInfo)
- return false;
-
- CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->GetCategory());
- return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / IN_MILLISECONDS)) > time(NULL));
-}
-
-uint32 Creature::GetCreatureSpellCooldownDelay(uint32 spellId) const
-{
- CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spellId);
- time_t t = time(NULL);
- return uint32(itr != m_CreatureSpellCooldowns.end() && itr->second > t ? itr->second - t : 0);
-}
-
-bool Creature::HasSpellCooldown(uint32 spell_id) const
-{
- CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
- return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || HasCategoryCooldown(spell_id);
-}
-
-void Creature::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
-{
- time_t curTime = time(NULL);
- for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
- {
- if (m_spells[i] == 0)
- continue;
-
- uint32 unSpellId = m_spells[i];
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(unSpellId);
-
- // Not send cooldown for this spells
- if (spellInfo->IsCooldownStartedOnEvent())
- continue;
-
- if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
- continue;
-
- if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetCreatureSpellCooldownDelay(unSpellId) < unTimeMs)
- {
- _AddCreatureSpellCooldown(unSpellId, curTime + unTimeMs/IN_MILLISECONDS);
- if (UnitAI* ai = GetAI())
- ai->SpellInterrupted(unSpellId, unTimeMs);
- }
- }
-}
-
bool Creature::HasSpell(uint32 spellID) const
{
uint8 i;
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index ec06bf90595..433d4a21b27 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -414,8 +414,6 @@ struct TrainerSpellData
TrainerSpell const* Find(uint32 spell_id) const;
};
-typedef std::map<uint32, time_t> CreatureSpellCooldowns;
-
// max different by z coordinate for creature aggro reaction
#define CREATURE_Z_ATTACK_RANGE 3
@@ -494,14 +492,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
SpellSchoolMask GetMeleeDamageSchoolMask() const override { return m_meleeDamageSchoolMask; }
void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
- void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time);
- void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time);
- void AddCreatureSpellCooldown(uint32 spellid);
- bool HasSpellCooldown(uint32 spell_id) const;
- bool HasCategoryCooldown(uint32 spell_id) const;
- uint32 GetCreatureSpellCooldownDelay(uint32 spellId) const;
- virtual void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override;
-
bool HasSpell(uint32 spellID) const override;
bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr);
@@ -575,8 +565,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject
SpellInfo const* reachWithSpellCure(Unit* victim);
uint32 m_spells[CREATURE_MAX_SPELLS];
- CreatureSpellCooldowns m_CreatureSpellCooldowns;
- CreatureSpellCooldowns m_CreatureCategoryCooldowns;
bool CanStartAttack(Unit const* u, bool force) const;
float GetAttackDistance(Unit const* player) const;
diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h
index c82ae723f4c..b832aeb5614 100644
--- a/src/server/game/Entities/Item/Item.h
+++ b/src/server/game/Entities/Item/Item.h
@@ -201,8 +201,6 @@ enum ItemUpdateState
ITEM_REMOVED = 3
};
-#define MAX_ITEM_SPELLS 5
-
bool ItemCanGoIntoBag(ItemTemplate const* proto, ItemTemplate const* pBagProto);
class Item : public Object
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index d5d6bdf9831..97cdb0cf1df 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -24,6 +24,7 @@
#include "SpellMgr.h"
#include "Pet.h"
#include "Formulas.h"
+#include "SpellHistory.h"
#include "SpellAuras.h"
#include "SpellAuraEffects.h"
#include "Unit.h"
@@ -407,7 +408,7 @@ void Pet::SavePetToDB(PetSaveMode mode)
RemoveAllAuras();
_SaveSpells(trans);
- _SaveSpellCooldowns(trans);
+ GetSpellHistory()->SaveToDB<Pet>(trans);
CharacterDatabase.CommitTransaction(trans);
// current/stable/not_in_slot
@@ -1106,77 +1107,11 @@ uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel) const
void Pet::_LoadSpellCooldowns()
{
- m_CreatureSpellCooldowns.clear();
- m_CreatureCategoryCooldowns.clear();
-
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_COOLDOWN);
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
- PreparedQueryResult result = CharacterDatabase.Query(stmt);
-
- if (result)
- {
- time_t curTime = time(NULL);
-
- PacketCooldowns cooldowns;
- WorldPacket data;
-
- do
- {
- Field* fields = result->Fetch();
-
- uint32 spell_id = fields[0].GetUInt32();
- time_t db_time = time_t(fields[1].GetUInt32());
-
- if (!sSpellMgr->GetSpellInfo(spell_id))
- {
- TC_LOG_ERROR("entities.pet", "Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.", m_charmInfo->GetPetNumber(), spell_id);
- continue;
- }
+ PreparedQueryResult cooldownsResult = CharacterDatabase.Query(stmt);
- // skip outdated cooldown
- if (db_time <= curTime)
- continue;
-
- cooldowns[spell_id] = uint32(db_time - curTime)*IN_MILLISECONDS;
-
- _AddCreatureSpellCooldown(spell_id, db_time);
-
- TC_LOG_DEBUG("entities.pet", "Pet (Number: %u) spell %u cooldown loaded (%u secs).", m_charmInfo->GetPetNumber(), spell_id, uint32(db_time-curTime));
- }
- while (result->NextRow());
-
- if (!cooldowns.empty())
- {
- BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
- GetOwner()->GetSession()->SendPacket(&data);
- }
- }
-}
-
-void Pet::_SaveSpellCooldowns(SQLTransaction& trans)
-{
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS);
- stmt->setUInt32(0, m_charmInfo->GetPetNumber());
- trans->Append(stmt);
-
- time_t curTime = time(NULL);
-
- // remove oudated and save active
- for (CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin(); itr != m_CreatureSpellCooldowns.end();)
- {
- if (itr->second <= curTime)
- m_CreatureSpellCooldowns.erase(itr++);
- else
- {
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_SPELL_COOLDOWN);
- stmt->setUInt32(0, m_charmInfo->GetPetNumber());
- stmt->setUInt32(1, itr->first);
- stmt->setUInt32(2, uint32(itr->second));
- trans->Append(stmt);
-
- ++itr;
- }
- }
+ GetSpellHistory()->LoadFromDB<Pet>(cooldownsResult);
}
void Pet::_LoadSpells()
@@ -2025,40 +1960,6 @@ void Pet::SynchronizeLevelWithOwner()
}
}
-void Pet::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
-{
- PacketCooldowns cooldowns;
- WorldPacket data;
- time_t curTime = time(NULL);
- for (PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if (itr->second.state == PETSPELL_REMOVED)
- continue;
-
- uint32 unSpellId = itr->first;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(unSpellId);
-
- // Not send cooldown for this spells
- if (spellInfo->IsCooldownStartedOnEvent())
- continue;
-
- if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
- continue;
-
- if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetCreatureSpellCooldownDelay(unSpellId) < unTimeMs)
- {
- cooldowns[unSpellId] = unTimeMs;
- _AddCreatureSpellCooldown(unSpellId, curTime + unTimeMs/IN_MILLISECONDS);
- }
- }
-
- if (!cooldowns.empty())
- {
- BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
- GetOwner()->GetSession()->SendPacket(&data);
- }
-}
-
Player* Pet::GetOwner() const
{
return Minion::GetOwner()->ToPlayer();
diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h
index 62d82aaab1b..1cc86ea2a20 100644
--- a/src/server/game/Entities/Pet/Pet.h
+++ b/src/server/game/Entities/Pet/Pet.h
@@ -108,7 +108,6 @@ class Pet : public Guardian
bool IsPetAura(Aura const* aura);
void _LoadSpellCooldowns();
- void _SaveSpellCooldowns(SQLTransaction& trans);
void _LoadAuras(uint32 timediff);
void _SaveAuras(SQLTransaction& trans);
void _LoadSpells();
@@ -121,7 +120,6 @@ class Pet : public Guardian
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
void CleanupActionBar();
- virtual void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override;
PetSpellMap m_spells;
AutoSpellList m_autospells;
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 32fd5c53803..dc82513bdb6 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -67,6 +67,7 @@
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellMgr.h"
+#include "SpellHistory.h"
#include "Transport.h"
#include "UpdateData.h"
#include "UpdateFieldFlags.h"
@@ -3345,12 +3346,10 @@ void Player::InitStatsForLevel(bool reapplyMods)
void Player::SendInitialSpells()
{
- time_t curTime = time(NULL);
- time_t infTime = curTime + infinityCooldownDelayCheck;
-
+ uint16 spellCooldowns = GetSpellHistory()->GetCooldownsSizeForPacket();
uint16 spellCount = 0;
- WorldPacket data(SMSG_INITIAL_SPELLS, (1+2+4*m_spells.size()+2+m_spellCooldowns.size()*(2+2+2+4+4)));
+ WorldPacket data(SMSG_INITIAL_SPELLS, (1 + 2 + 4 * m_spells.size() + 2 + spellCooldowns * (2 + 2 + 2 + 4 + 4)));
data << uint8(0);
size_t countPos = data.wpos();
@@ -3372,40 +3371,7 @@ void Player::SendInitialSpells()
data.put<uint16>(countPos, spellCount); // write real count value
- uint16 spellCooldowns = m_spellCooldowns.size();
- data << uint16(spellCooldowns);
- for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr)
- {
- SpellInfo const* sEntry = sSpellMgr->GetSpellInfo(itr->first);
- if (!sEntry)
- continue;
-
- data << uint32(itr->first);
-
- data << uint16(itr->second.itemid); // cast item id
- data << uint16(sEntry->GetCategory()); // spell category
-
- // send infinity cooldown in special format
- if (itr->second.end >= infTime)
- {
- data << uint32(1); // cooldown
- data << uint32(0x80000000); // category cooldown
- continue;
- }
-
- time_t cooldown = itr->second.end > curTime ? (itr->second.end-curTime)*IN_MILLISECONDS : 0;
-
- if (sEntry->GetCategory()) // may be wrong, but anyway better than nothing...
- {
- data << uint32(0); // cooldown
- data << uint32(cooldown); // category cooldown
- }
- else
- {
- data << uint32(cooldown); // cooldown
- data << uint32(0); // category cooldown
- }
- }
+ GetSpellHistory()->WritePacket<Player>(data);
GetSession()->SendPacket(&data);
@@ -4202,154 +4168,19 @@ bool Player::Has310Flyer(bool checkAllSpells, uint32 excludeSpellId)
return false;
}
-void Player::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */)
-{
- m_spellCooldowns.erase(spell_id);
-
- if (update)
- SendClearCooldown(spell_id, this);
-}
-
-// I am not sure which one is more efficient
-void Player::RemoveCategoryCooldown(uint32 cat)
-{
- SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(cat);
- if (i_scstore != sSpellsByCategoryStore.end())
- for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
- RemoveSpellCooldown(*i_scset, true);
-}
-
-void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */)
-{
- SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(cat);
- if (ct == sSpellsByCategoryStore.end())
- return;
-
- const SpellCategorySet& ct_set = ct->second;
- for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();)
- {
- if (ct_set.find(i->first) != ct_set.end())
- RemoveSpellCooldown((i++)->first, update);
- else
- ++i;
- }
-}
-
void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns)
{
// remove cooldowns on spells that have < 10 min CD
-
- SpellCooldowns::iterator itr, next;
- for (itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); itr = next)
+ GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- next = itr;
- ++next;
- SpellInfo const* entry = sSpellMgr->GetSpellInfo(itr->first);
- // check if spellentry is present and if the cooldown is less than 10 min
- if (entry &&
- entry->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS &&
- entry->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS)
- {
- // remove & notify
- RemoveSpellCooldown(itr->first, true);
- }
- }
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
+ return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS;
+ }, true);
// pet cooldowns
if (removeActivePetCooldowns)
if (Pet* pet = GetPet())
- {
- // notify player
- for (CreatureSpellCooldowns::const_iterator itr2 = pet->m_CreatureSpellCooldowns.begin(); itr2 != pet->m_CreatureSpellCooldowns.end(); ++itr2)
- SendClearCooldown(itr2->first, pet);
-
- // actually clear cooldowns
- pet->m_CreatureSpellCooldowns.clear();
- }
-}
-
-void Player::RemoveAllSpellCooldown()
-{
- if (!m_spellCooldowns.empty())
- {
- for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr)
- SendClearCooldown(itr->first, this);
-
- m_spellCooldowns.clear();
- }
-}
-
-void Player::_LoadSpellCooldowns(PreparedQueryResult result)
-{
- // some cooldowns can be already set at aura loading...
-
- //QueryResult* result = CharacterDatabase.PQuery("SELECT spell, item, time FROM character_spell_cooldown WHERE guid = '%u'", GetGUIDLow());
-
- if (result)
- {
- time_t curTime = time(NULL);
-
- do
- {
- Field* fields = result->Fetch();
- uint32 spell_id = fields[0].GetUInt32();
- uint32 item_id = fields[1].GetUInt32();
- time_t db_time = time_t(fields[2].GetUInt32());
-
- if (!sSpellMgr->GetSpellInfo(spell_id))
- {
- TC_LOG_ERROR("entities.player.loading", "Player %u has unknown spell %u in `character_spell_cooldown`, skipping.", GetGUIDLow(), spell_id);
- continue;
- }
-
- // skip outdated cooldown
- if (db_time <= curTime)
- continue;
-
- AddSpellCooldown(spell_id, item_id, db_time);
-
- TC_LOG_DEBUG("entities.player.loading", "Player (GUID: %u) spell %u, item %u cooldown loaded (%u secs).", GetGUIDLow(), spell_id, item_id, uint32(db_time-curTime));
- }
- while (result->NextRow());
- }
-}
-
-void Player::_SaveSpellCooldowns(SQLTransaction& trans)
-{
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN);
- stmt->setUInt32(0, GetGUIDLow());
- trans->Append(stmt);
-
- time_t curTime = time(NULL);
- time_t infTime = curTime + infinityCooldownDelayCheck;
-
- bool first_round = true;
- std::ostringstream ss;
-
- // remove outdated and save active
- for (SpellCooldowns::iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end();)
- {
- if (itr->second.end <= curTime)
- m_spellCooldowns.erase(itr++);
- else if (itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload
- {
- if (first_round)
- {
- ss << "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES ";
- first_round = false;
- }
- // next new/changed record prefix
- else
- ss << ',';
- ss << '(' << GetGUIDLow() << ',' << itr->first << ',' << itr->second.itemid << ',' << uint64(itr->second.end) << ')';
- ++itr;
- }
- else
- ++itr;
- }
- // if something changed execute
- if (!first_round)
- trans->Append(ss.str().c_str());
+ pet->GetSpellHistory()->ResetAllCooldowns();
}
uint32 Player::ResetTalentsCost() const
@@ -4897,7 +4728,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe
stmt->setUInt32(0, guid);
trans->Append(stmt);
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN);
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWNS);
stmt->setUInt32(0, guid);
trans->Append(stmt);
@@ -8382,7 +8213,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32
if (procVictim & PROC_FLAG_TAKEN_DAMAGE)
//if (damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE)
{
- for (uint8 i = 0; i < MAX_ITEM_SPELLS; ++i)
+ for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
_Spell const& spellData = proto->Spells[i];
@@ -12425,10 +12256,9 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update)
{
m_weaponChangeTimer = spellProto->StartRecoveryTime;
- GetGlobalCooldownMgr().AddGlobalCooldown(spellProto, m_weaponChangeTimer);
-
+ GetSpellHistory()->AddGlobalCooldown(spellProto, m_weaponChangeTimer);
WorldPacket data;
- BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_INCLUDE_GCD, cooldownSpell, 0);
+ GetSpellHistory()->BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_INCLUDE_GCD, cooldownSpell, 0);
GetSession()->SendPacket(&data);
}
}
@@ -17811,7 +17641,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
// has to be called after last Relocate() in Player::LoadFromDB
SetFallInformation(0, GetPositionZ());
- _LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS));
+ GetSpellHistory()->LoadFromDB<Player>(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS));
// Spell code allow apply any auras to dead character in load time in aura/spell/item loading
// Do now before stats re-calculation cleanup for ghost state unexpected auras
@@ -19517,7 +19347,7 @@ void Player::SaveToDB(bool create /*=false*/)
_SaveMonthlyQuestStatus(trans);
_SaveTalents(trans);
_SaveSpells(trans);
- _SaveSpellCooldowns(trans);
+ GetSpellHistory()->SaveToDB<Player>(trans);
_SaveActions(trans);
_SaveAuras(trans);
_SaveSkills(trans);
@@ -20754,41 +20584,8 @@ void Player::PetSpellInitialize()
data.put<uint8>(spellsCountPos, addlist);
- uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size();
- data << uint8(cooldownsCount);
-
- time_t curTime = time(NULL);
-
- for (CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr)
- {
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
- if (!spellInfo)
- {
- data << uint32(0);
- data << uint16(0);
- data << uint32(0);
- data << uint32(0);
- continue;
- }
-
- time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0;
- data << uint32(itr->first); // spell ID
-
- CreatureSpellCooldowns::const_iterator categoryitr = pet->m_CreatureCategoryCooldowns.find(spellInfo->GetCategory());
- if (categoryitr != pet->m_CreatureCategoryCooldowns.end())
- {
- time_t categoryCooldown = (categoryitr->second > curTime) ? (categoryitr->second - curTime) * IN_MILLISECONDS : 0;
- data << uint16(spellInfo->GetCategory()); // spell category
- data << uint32(cooldown); // spell cooldown
- data << uint32(categoryCooldown); // category cooldown
- }
- else
- {
- data << uint16(0);
- data << uint32(cooldown);
- data << uint32(0);
- }
- }
+ //Cooldowns
+ pet->GetSpellHistory()->WritePacket<Pet>(data);
GetSession()->SendPacket(&data);
}
@@ -20827,7 +20624,7 @@ void Player::VehicleSpellInitialize()
if (!vehicle)
return;
- uint8 cooldownCount = vehicle->m_CreatureSpellCooldowns.size();
+ uint8 cooldownCount = vehicle->GetSpellHistory()->GetCooldownsSizeForPacket();
WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * 10 + 1 + 1 + cooldownCount * (4 + 2 + 4 + 4));
data << uint64(vehicle->GetGUID()); // Guid
@@ -20868,41 +20665,7 @@ void Player::VehicleSpellInitialize()
data << uint8(0); // Auras?
// Cooldowns
- data << uint8(cooldownCount);
-
- time_t now = sWorld->GetGameTime();
-
- for (CreatureSpellCooldowns::const_iterator itr = vehicle->m_CreatureSpellCooldowns.begin(); itr != vehicle->m_CreatureSpellCooldowns.end(); ++itr)
- {
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
- if (!spellInfo)
- {
- data << uint32(0);
- data << uint16(0);
- data << uint32(0);
- data << uint32(0);
- continue;
- }
-
- time_t cooldown = (itr->second > now) ? (itr->second - now) * IN_MILLISECONDS : 0;
- data << uint32(itr->first); // spell ID
-
- CreatureSpellCooldowns::const_iterator categoryitr = vehicle->m_CreatureCategoryCooldowns.find(spellInfo->GetCategory());
- if (categoryitr != vehicle->m_CreatureCategoryCooldowns.end())
- {
- time_t categoryCooldown = (categoryitr->second > now) ? (categoryitr->second - now) * IN_MILLISECONDS : 0;
- data << uint16(spellInfo->GetCategory()); // spell category
- data << uint32(cooldown); // spell cooldown
- data << uint32(categoryCooldown); // category cooldown
- }
- else
- {
- data << uint16(0);
- data << uint32(cooldown);
- data << uint32(0);
- }
- }
-
+ vehicle->GetSpellHistory()->WritePacket<Pet>(data);
GetSession()->SendPacket(&data);
}
@@ -21557,39 +21320,6 @@ void Player::ContinueTaxiFlight()
GetSession()->SendDoFlight(mountDisplayId, path, startNode);
}
-void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs)
-{
- PacketCooldowns cooldowns;
- WorldPacket data;
- time_t curTime = time(NULL);
- for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
- {
- if (itr->second->state == PLAYERSPELL_REMOVED)
- continue;
- uint32 unSpellId = itr->first;
- SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(unSpellId);
-
- // Not send cooldown for this spells
- if (spellInfo->IsCooldownStartedOnEvent())
- continue;
-
- if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
- continue;
-
- if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetSpellCooldownDelay(unSpellId) < unTimeMs)
- {
- cooldowns[unSpellId] = unTimeMs;
- AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILLISECONDS);
- }
- }
-
- if (!cooldowns.empty())
- {
- BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
- GetSession()->SendPacket(&data);
- }
-}
-
void Player::InitDataForForm(bool reapplyMods)
{
ShapeshiftForm form = GetShapeshiftForm();
@@ -22011,206 +21741,6 @@ void Player::UpdatePvP(bool state, bool _override)
}
}
-bool Player::HasSpellCooldown(uint32 spell_id) const
-{
- SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
- return itr != m_spellCooldowns.end() && itr->second.end > time(NULL);
-}
-
-uint32 Player::GetSpellCooldownDelay(uint32 spell_id) const
-{
- SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
- time_t t = time(NULL);
- return uint32(itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0);
-}
-
-void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown)
-{
- // init cooldown values
- uint32 cat = 0;
- int32 rec = -1;
- int32 catrec = -1;
-
- // some special item spells without correct cooldown in SpellInfo
- // cooldown information stored in item prototype
- // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client.
-
- if (itemId)
- {
- if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
- {
- for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx)
- {
- if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
- {
- cat = proto->Spells[idx].SpellCategory;
- rec = proto->Spells[idx].SpellCooldown;
- catrec = proto->Spells[idx].SpellCategoryCooldown;
- break;
- }
- }
- }
- }
-
- // if no cooldown found above then base at DBC data
- if (rec < 0 && catrec < 0)
- {
- cat = spellInfo->GetCategory();
- rec = spellInfo->RecoveryTime;
- catrec = spellInfo->CategoryRecoveryTime;
- }
-
- time_t curTime = time(NULL);
-
- time_t catrecTime;
- time_t recTime;
-
- bool needsCooldownPacket = false;
-
- // overwrite time for selected category
- if (infinityCooldown)
- {
- // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped)
- // but not allow ignore until reset or re-login
- catrecTime = catrec > 0 ? curTime+infinityCooldownDelay : 0;
- recTime = rec > 0 ? curTime+infinityCooldownDelay : catrecTime;
- }
- else
- {
- // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
- // prevent 0 cooldowns set by another way
- if (rec <= 0 && catrec <= 0 && (cat == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75)))
- rec = GetAttackTime(RANGED_ATTACK);
-
- // Now we have cooldown data (if found any), time to apply mods
- if (rec > 0)
- ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell);
-
- if (catrec > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS))
- ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);
-
- if (int32 cooldownMod = GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN))
- {
- // Apply SPELL_AURA_MOD_COOLDOWN only to own spells
- if (HasSpell(spellInfo->Id))
- {
- needsCooldownPacket = true;
- rec += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
- }
- }
-
- // replace negative cooldowns by 0
- if (rec < 0) rec = 0;
- if (catrec < 0) catrec = 0;
-
- // no cooldown after applying spell mods
- if (rec == 0 && catrec == 0)
- return;
-
- catrecTime = catrec ? curTime+catrec/IN_MILLISECONDS : 0;
- recTime = rec ? curTime+rec/IN_MILLISECONDS : catrecTime;
- }
-
- // self spell cooldown
- if (recTime > 0)
- {
- AddSpellCooldown(spellInfo->Id, itemId, recTime);
-
- if (needsCooldownPacket)
- {
- WorldPacket data;
- BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, rec);
- SendDirectMessage(&data);
- }
- }
-
- // category spells
- if (cat && catrec > 0)
- {
- SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(cat);
- if (i_scstore != sSpellsByCategoryStore.end())
- {
- for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
- {
- if (*i_scset == spellInfo->Id) // skip main spell, already handled above
- continue;
-
- AddSpellCooldown(*i_scset, itemId, catrecTime);
- }
- }
- }
-}
-
-void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
-{
- SpellCooldown sc;
- sc.end = end_time;
- sc.itemid = itemid;
- m_spellCooldowns[spellid] = sc;
-}
-
-void Player::ModifySpellCooldown(uint32 spellId, int32 cooldown)
-{
- SpellCooldowns::iterator itr = m_spellCooldowns.find(spellId);
- if (itr == m_spellCooldowns.end())
- return;
-
- time_t now = time(NULL);
- if (itr->second.end + (cooldown / IN_MILLISECONDS) > now)
- itr->second.end += (cooldown / IN_MILLISECONDS);
- else
- m_spellCooldowns.erase(itr);
-
- WorldPacket data(SMSG_MODIFY_COOLDOWN, 4 + 8 + 4);
- data << uint32(spellId); // Spell ID
- data << uint64(GetGUID()); // Player GUID
- data << int32(cooldown); // Cooldown mod in milliseconds
- GetSession()->SendPacket(&data);
-
- TC_LOG_DEBUG("misc", "ModifySpellCooldown:: Player: %s (GUID: %u) Spell: %u cooldown: %u", GetName().c_str(), GetGUIDLow(), spellId, GetSpellCooldownDelay(spellId));
-}
-
-void Player::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= NULL*/, bool setCooldown /*= true*/)
-{
- // start cooldowns at server side, if any
- if (setCooldown)
- AddSpellAndCategoryCooldowns(spellInfo, itemId, spell);
-
- // Send activate cooldown timer (possible 0) at client side
- WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
- data << uint32(spellInfo->Id);
- data << uint64(GetGUID());
- SendDirectMessage(&data);
-
- uint32 cat = spellInfo->GetCategory();
- if (cat && spellInfo->CategoryRecoveryTime)
- {
- SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(cat);
- if (ct != sSpellsByCategoryStore.end())
- {
- SpellCategorySet const& catSet = ct->second;
- for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end(); ++i)
- {
- if (i->first == spellInfo->Id) // skip main spell, already handled above
- continue;
-
- SpellInfo const* spellInfo2 = sSpellMgr->GetSpellInfo(i->first);
- if (!spellInfo2 || !spellInfo2->IsCooldownStartedOnEvent())
- continue;
-
- if (catSet.find(i->first) != catSet.end())
- {
- // Send activate cooldown timer (possible 0) at client side
- WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
- data << uint32(i->first);
- data << uint64(GetGUID());
- SendDirectMessage(&data);
- }
- }
- }
- }
-}
-
void Player::UpdatePotionCooldown(Spell* spell)
{
// no potion used i combat or still in combat
@@ -22222,14 +21752,14 @@ void Player::UpdatePotionCooldown(Spell* spell)
{
// spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions)
if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(m_lastPotionId))
- for (uint8 idx = 0; idx < MAX_ITEM_SPELLS; ++idx)
+ for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx)
if (proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE)
if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Spells[idx].SpellId))
- SendCooldownEvent(spellInfo, m_lastPotionId);
+ GetSpellHistory()->SendCooldownEvent(spellInfo, m_lastPotionId);
}
// from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
else
- SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell);
+ GetSpellHistory()->SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell);
m_lastPotionId = 0;
}
@@ -23139,14 +22669,13 @@ void Player::ApplyEquipCooldown(Item* pItem)
continue;
// Don't replace longer cooldowns by equip cooldown if we have any.
- SpellCooldowns::iterator itr = m_spellCooldowns.find(spellData.SpellId);
- if (itr != m_spellCooldowns.end() && itr->second.itemid == pItem->GetEntry() && itr->second.end > time(NULL) + 30)
+ if (GetSpellHistory()->GetRemainingCooldown(spellData.SpellId) > 30 * IN_MILLISECONDS)
continue;
- AddSpellCooldown(spellData.SpellId, pItem->GetEntry(), time(NULL) + 30);
+ GetSpellHistory()->AddCooldown(spellData.SpellId, pItem->GetEntry(), std::chrono::seconds(30));
- WorldPacket data(SMSG_ITEM_COOLDOWN, 12);
- data << pItem->GetGUID();
+ WorldPacket data(SMSG_ITEM_COOLDOWN, 8 + 4);
+ data << uint64(pItem->GetGUID());
data << uint32(spellData.SpellId);
GetSession()->SendPacket(&data);
}
@@ -24017,7 +23546,7 @@ uint32 Player::GetResurrectionSpellId()
}
// Reincarnation (passive spell) // prio: 1 // Glyph of Renewed Life
- if (prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasAura(58059) || HasItemCount(17030)))
+ if (prio < 1 && HasSpell(20608) && !GetSpellHistory()->HasCooldown(21169) && (HasAura(58059) || HasItemCount(17030)))
spell_id = 21169;
return spell_id;
@@ -26072,14 +25601,6 @@ void Player::RemoveAtLoginFlag(AtLoginFlags flags, bool persist /*= false*/)
}
}
-void Player::SendClearCooldown(uint32 spell_id, Unit* target)
-{
- WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8);
- data << uint32(spell_id);
- data << uint64(target->GetGUID());
- SendDirectMessage(&data);
-}
-
void Player::ResetMap()
{
// this may be called during Map::Update
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e3a7f39b2ba..af640e4c73c 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -128,13 +128,6 @@ typedef std::unordered_map<uint32, PlayerTalent*> PlayerTalentMap;
typedef std::unordered_map<uint32, PlayerSpell*> PlayerSpellMap;
typedef std::list<SpellModifier*> SpellModList;
-struct SpellCooldown
-{
- time_t end;
- uint16 itemid;
-};
-
-typedef std::map<uint32, SpellCooldown> SpellCooldowns;
typedef std::unordered_map<uint32 /*instanceId*/, time_t/*releaseTime*/> InstanceTimeMap;
enum TrainerSpellState
@@ -1655,8 +1648,6 @@ class Player : public Unit, public GridObject<Player>
PlayerSpellMap const& GetSpellMap() const { return m_spells; }
PlayerSpellMap & GetSpellMap() { return m_spells; }
- SpellCooldowns const& GetSpellCooldownMap() const { return m_spellCooldowns; }
-
void AddSpellMod(SpellModifier* mod, bool apply);
bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = NULL);
template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell = NULL);
@@ -1666,26 +1657,7 @@ class Player : public Unit, public GridObject<Player>
void DropModCharge(SpellModifier* mod, Spell* spell);
void SetSpellModTakingSpell(Spell* spell, bool apply);
- static uint32 const infinityCooldownDelay = MONTH; // used for set "infinity cooldowns" for spells and check
- static uint32 const infinityCooldownDelayCheck = MONTH/2;
- bool HasSpellCooldown(uint32 spell_id) const;
- uint32 GetSpellCooldownDelay(uint32 spell_id) const;
- void AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false);
- void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time);
- void ModifySpellCooldown(uint32 spellId, int32 cooldown);
- void SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId = 0, Spell* spell = NULL, bool setCooldown = true);
- void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override;
- void RemoveSpellCooldown(uint32 spell_id, bool update = false);
- void RemoveSpellCategoryCooldown(uint32 cat, bool update = false);
- void SendClearCooldown(uint32 spell_id, Unit* target);
-
- GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; }
-
- void RemoveCategoryCooldown(uint32 cat);
void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false);
- void RemoveAllSpellCooldown();
- void _LoadSpellCooldowns(PreparedQueryResult result);
- void _SaveSpellCooldowns(SQLTransaction& trans);
uint32 GetLastPotionId() { return m_lastPotionId; }
void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; }
void UpdatePotionCooldown(Spell* spell = NULL);
@@ -1975,8 +1947,6 @@ class Player : public Unit, public GridObject<Player>
//End of PvP System
- inline SpellCooldowns GetSpellCooldowns() const { return m_spellCooldowns; }
-
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, 1); }
static DrunkenState GetDrunkenstateByValue(uint8 value);
@@ -2493,8 +2463,6 @@ class Player : public Unit, public GridObject<Player>
PlayerTalentMap* m_talents[MAX_TALENT_SPECS];
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
- GlobalCooldownMgr m_GlobalCooldownMgr;
-
uint8 m_activeSpec;
uint8 m_specsCount;
@@ -2662,8 +2630,6 @@ class Player : public Unit, public GridObject<Player>
AchievementMgr* m_achievementMgr;
ReputationMgr* m_reputationMgr;
- SpellCooldowns m_spellCooldowns;
-
uint32 m_ChampioningFaction;
uint32 m_timeSyncCounter;
diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp
index 39a078a907f..85ee51aebf5 100644
--- a/src/server/game/Entities/Totem/Totem.cpp
+++ b/src/server/game/Entities/Totem/Totem.cpp
@@ -20,6 +20,7 @@
#include "Group.h"
#include "Opcodes.h"
#include "Player.h"
+#include "SpellHistory.h"
#include "SpellMgr.h"
#include "SpellInfo.h"
#include "WorldPacket.h"
@@ -128,7 +129,7 @@ void Totem::UnSummon(uint32 msTime)
owner->SendAutoRepeatCancel(this);
if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(GetUInt32Value(UNIT_CREATED_BY_SPELL)))
- owner->SendCooldownEvent(spell, 0, NULL, false);
+ GetSpellHistory()->SendCooldownEvent(spell, 0, nullptr, false);
if (Group* group = owner->GetGroup())
{
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 5f864210e8f..21f5cb9ee4c 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -51,6 +51,7 @@
#include "SpellAuras.h"
#include "Spell.h"
#include "SpellInfo.h"
+#include "SpellHistory.h"
#include "SpellMgr.h"
#include "TemporarySummon.h"
#include "Totem.h"
@@ -163,7 +164,7 @@ Unit::Unit(bool isWorldObject) :
i_AI(NULL), i_disabledAI(NULL), m_AutoRepeatFirstCast(false), m_procDeep(0),
m_removedAurasCount(0), i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_ThreatManager(this),
m_vehicle(NULL), m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE),
- m_HostileRefManager(this), _lastDamagedTime(0)
+ m_HostileRefManager(this), _lastDamagedTime(0), m_spellHistory(new SpellHistory(this))
{
m_objectType |= TYPEMASK_UNIT;
m_objectTypeId = TYPEID_UNIT;
@@ -257,24 +258,6 @@ Unit::Unit(bool isWorldObject) :
}
////////////////////////////////////////////////////////////
-// Methods of class GlobalCooldownMgr
-bool GlobalCooldownMgr::HasGlobalCooldown(SpellInfo const* spellInfo) const
-{
- GlobalCooldownList::const_iterator itr = m_GlobalCooldowns.find(spellInfo->StartRecoveryCategory);
- return itr != m_GlobalCooldowns.end() && itr->second.duration && getMSTimeDiff(itr->second.cast_time, getMSTime()) < itr->second.duration;
-}
-
-void GlobalCooldownMgr::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 gcd)
-{
- m_GlobalCooldowns[spellInfo->StartRecoveryCategory] = GlobalCooldown(gcd, getMSTime());
-}
-
-void GlobalCooldownMgr::CancelGlobalCooldown(SpellInfo const* spellInfo)
-{
- m_GlobalCooldowns[spellInfo->StartRecoveryCategory].duration = 0;
-}
-
-////////////////////////////////////////////////////////////
// Methods of class Unit
Unit::~Unit()
{
@@ -291,6 +274,7 @@ Unit::~Unit()
delete i_motionMaster;
delete m_charmInfo;
delete movespline;
+ delete m_spellHistory;
ASSERT(!m_duringRemoveFromWorld);
ASSERT(!m_attacking);
@@ -2925,6 +2909,8 @@ void Unit::_UpdateSpells(uint32 time)
++itr;
}
}
+
+ m_spellHistory->Update();
}
void Unit::_UpdateAutoRepeatSpell()
@@ -4860,13 +4846,13 @@ void Unit::AddGameObject(GameObject* gameObj)
m_gameObj.push_back(gameObj);
gameObj->SetOwnerGUID(GetGUID());
- if (GetTypeId() == TYPEID_PLAYER && gameObj->GetSpellId())
+ if (gameObj->GetSpellId())
{
SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(gameObj->GetSpellId());
// Need disable spell use for owner
if (createBySpell && createBySpell->IsCooldownStartedOnEvent())
// note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases)
- ToPlayer()->AddSpellAndCategoryCooldowns(createBySpell, 0, NULL, true);
+ GetSpellHistory()->StartCooldown(createBySpell, 0, nullptr, true);
}
}
@@ -4891,14 +4877,11 @@ void Unit::RemoveGameObject(GameObject* gameObj, bool del)
{
RemoveAurasDueToSpell(spellid);
- if (GetTypeId() == TYPEID_PLAYER)
- {
- SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(spellid);
- // Need activate spell use for owner
- if (createBySpell && createBySpell->IsCooldownStartedOnEvent())
- // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases)
- ToPlayer()->SendCooldownEvent(createBySpell);
- }
+ SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(spellid);
+ // Need activate spell use for owner
+ if (createBySpell && createBySpell->IsCooldownStartedOnEvent())
+ // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases)
+ GetSpellHistory()->SendCooldownEvent(createBySpell);
}
m_gameObj.remove(gameObj);
@@ -5488,8 +5471,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster);
for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr)
{
- if (!ToPlayer()->HasSpellCooldown(*itr))
- ToPlayer()->AddSpellCooldown(*itr, 0, time(NULL) + cooldown);
+ if (!GetSpellHistory()->HasCooldown(*itr))
+ GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown));
}
break;
}
@@ -5534,8 +5517,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster);
for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr)
{
- if (!ToPlayer()->HasSpellCooldown(*itr))
- ToPlayer()->AddSpellCooldown(*itr, 0, time(NULL) + cooldown);
+ if (!GetSpellHistory()->HasCooldown(*itr))
+ GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown));
}
break;
}
@@ -5639,20 +5622,17 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
// Glyph of Ice Block
case 56372:
{
- Player* player = ToPlayer();
- if (!player)
+ if (GetTypeId() != TYPEID_PLAYER)
return false;
- SpellCooldowns const cooldowns = player->GetSpellCooldowns();
- // remove cooldowns on all ranks of Frost Nova
- for (SpellCooldowns::const_iterator itr = cooldowns.begin(); itr != cooldowns.end(); ++itr)
+ GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
SpellInfo const* cdSpell = sSpellMgr->GetSpellInfo(itr->first);
- // Frost Nova
- if (cdSpell && cdSpell->SpellFamilyName == SPELLFAMILY_MAGE
- && cdSpell->SpellFamilyFlags[0] & 0x00000040)
- player->RemoveSpellCooldown(cdSpell->Id, true);
- }
+ if (!cdSpell || cdSpell->SpellFamilyName != SPELLFAMILY_MAGE
+ || !(cdSpell->SpellFamilyFlags[0] & 0x00000040))
+ return false;
+ return true;
+ }, true);
break;
}
case 47020: // Enter vehicle XT-002 (Scrapbot)
@@ -6061,7 +6041,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
int32 basepoints1 = CalculatePct(GetMaxPower(Powers(POWER_MANA)), triggerAmount * 2);
// Improved Leader of the Pack
// Check cooldown of heal spell cooldown
- if (GetTypeId() == TYPEID_PLAYER && !ToPlayer()->HasSpellCooldown(34299))
+ if (!GetSpellHistory()->HasCooldown(34299))
CastCustomSpell(this, 68285, &basepoints1, 0, 0, true, 0, triggeredByAura);
break;
}
@@ -6732,7 +6712,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return false;
// custom cooldown processing case
- if (cooldown && player->HasSpellCooldown(dummySpell->Id))
+ if (cooldown && GetSpellHistory()->HasCooldown(dummySpell->Id))
return false;
if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID())
@@ -6798,7 +6778,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
// apply cooldown before cast to prevent processing itself
if (cooldown)
- player->AddSpellCooldown(dummySpell->Id, 0, time(NULL) + cooldown);
+ player->GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown));
// Attack Twice
for (uint32 i = 0; i < 2; ++i)
@@ -7040,7 +7020,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return false;
// custom cooldown processing case
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(dummySpell->Id))
+ if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(dummySpell->Id))
return false;
uint32 spellId = 0;
@@ -7087,13 +7067,13 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
return false;
// Remove cooldown (Chain Lightning - has Category Recovery time)
- ToPlayer()->RemoveSpellCooldown(spellId);
+ GetSpellHistory()->ResetCooldown(spellId);
}
CastSpell(victim, spellId, true, castItem, triggeredByAura);
if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(dummySpell->Id, 0, time(NULL) + cooldown);
+ GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown));
return true;
}
@@ -7106,8 +7086,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
uint32 spell = sSpellMgr->GetSpellWithRank(26364, aurEff->GetSpellInfo()->GetRank());
// custom cooldown processing case
- if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(spell))
- ToPlayer()->RemoveSpellCooldown(spell);
+ if (GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(spell))
+ GetSpellHistory()->ResetCooldown(spell);
CastSpell(target, spell, true, castItem, triggeredByAura);
aurEff->GetBase()->DropCharge();
@@ -7399,7 +7379,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
if (cooldown_spell_id == 0)
cooldown_spell_id = triggered_spell_id;
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(cooldown_spell_id))
+ if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(cooldown_spell_id))
return false;
if (basepoints0)
@@ -7408,7 +7388,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere
CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster);
if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(cooldown_spell_id, 0, time(NULL) + cooldown);
+ GetSpellHistory()->AddCooldown(cooldown_spell_id, 0, std::chrono::seconds(cooldown));
return true;
}
@@ -7629,9 +7609,9 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp
*handled = true;
if (cooldown && GetTypeId() == TYPEID_PLAYER)
{
- if (ToPlayer()->HasSpellCooldown(100000))
+ if (GetSpellHistory()->HasCooldown(100000))
return false;
- ToPlayer()->AddSpellCooldown(100000, 0, time(NULL) + cooldown);
+ GetSpellHistory()->AddCooldown(100000, 0, std::chrono::seconds(cooldown));
}
return true;
}
@@ -8300,7 +8280,11 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
return false;
// Howling Blast
- ToPlayer()->RemoveSpellCategoryCooldown(1248, true);
+ GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
+ {
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
+ return spellInfo && spellInfo->GetCategory() == 1248;
+ }, true);
}
// Custom basepoints/target for exist spell
@@ -8319,13 +8303,13 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
if (!target)
return false;
- if (cooldown && target->GetTypeId() == TYPEID_PLAYER && target->ToPlayer()->HasSpellCooldown(trigger_spell_id))
+ if (cooldown && target->GetTypeId() == TYPEID_PLAYER && target->GetSpellHistory()->HasCooldown(trigger_spell_id))
return false;
target->CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura);
if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(trigger_spell_id, 0, time(NULL) + cooldown);
+ GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown));
return true;
}
// Cast positive spell on enemy target
@@ -8408,8 +8392,11 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
case 50227:
{
// Remove cooldown on Shield Slam
- if (GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->RemoveSpellCategoryCooldown(1209, true);
+ GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
+ {
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
+ return spellInfo && spellInfo->GetCategory() == 1209;
+ }, true);
break;
}
// Maelstrom Weapon
@@ -8469,8 +8456,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
case 58628:
{
// remove cooldown of Death Grip
- if (GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->RemoveSpellCooldown(49576, true);
+ GetSpellHistory()->ResetCooldown(49576, true);
return true;
}
// Savage Defense
@@ -8501,7 +8487,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
}
}
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(trigger_spell_id))
+ if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(trigger_spell_id))
return false;
// extra attack should hit same target
@@ -8518,7 +8504,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg
CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura);
if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(trigger_spell_id, 0, time(NULL) + cooldown);
+ GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown));
return true;
}
@@ -8614,13 +8600,13 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, Au
return false;
}
- if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id))
+ if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(triggered_spell_id))
return false;
CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura);
if (cooldown && GetTypeId() == TYPEID_PLAYER)
- ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown);
+ GetSpellHistory()->AddCooldown(triggered_spell_id, 0, std::chrono::seconds(cooldown));
return true;
}
@@ -9355,14 +9341,11 @@ void Unit::SetMinion(Minion *minion, bool apply)
if (minion->IsPetGhoul())
minion->setPowerType(POWER_ENERGY);
- if (GetTypeId() == TYPEID_PLAYER)
- {
- // Send infinity cooldown - client does that automatically but after relog cooldown needs to be set again
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL));
+ // Send infinity cooldown - client does that automatically but after relog cooldown needs to be set again
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL));
- if (spellInfo && (spellInfo->IsCooldownStartedOnEvent()))
- ToPlayer()->AddSpellAndCategoryCooldowns(spellInfo, 0, NULL, true);
- }
+ if (spellInfo && (spellInfo->IsCooldownStartedOnEvent()))
+ GetSpellHistory()->StartCooldown(spellInfo, 0, nullptr, true);
}
else
{
@@ -9396,13 +9379,10 @@ void Unit::SetMinion(Minion *minion, bool apply)
}
}
- if (GetTypeId() == TYPEID_PLAYER)
- {
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL));
- // Remove infinity cooldown
- if (spellInfo && (spellInfo->IsCooldownStartedOnEvent()))
- ToPlayer()->SendCooldownEvent(spellInfo);
- }
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL));
+ // Remove infinity cooldown
+ if (spellInfo && (spellInfo->IsCooldownStartedOnEvent()))
+ GetSpellHistory()->SendCooldownEvent(spellInfo);
//if (minion->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
{
@@ -17788,27 +17768,6 @@ void Unit::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target)
data->append(fieldBuffer);
}
-void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown)
-{
- data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + 4 + 4);
- data << uint64(GetGUID());
- data << uint8(flags);
- data << uint32(spellId);
- data << uint32(cooldown);
-}
-
-void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns)
-{
- data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + (4 + 4) * cooldowns.size());
- data << uint64(GetGUID());
- data << uint8(flags);
- for (std::unordered_map<uint32, uint32>::const_iterator itr = cooldowns.begin(); itr != cooldowns.end(); ++itr)
- {
- data << uint32(itr->first);
- data << uint32(itr->second);
- }
-}
-
int32 Unit::GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool checkMiscValue /*= false*/, int32 miscValue /*= 0*/) const
{
int32 val = 0;
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index ddf43f30971..088a0bbdf73 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -339,6 +339,7 @@ class AuraEffect;
class Creature;
class Spell;
class SpellInfo;
+class SpellHistory;
class DynamicObject;
class GameObject;
class Item;
@@ -1031,30 +1032,6 @@ enum CurrentSpellTypes
#define CURRENT_FIRST_NON_MELEE_SPELL 1
#define CURRENT_MAX_SPELL 4
-struct GlobalCooldown
-{
- explicit GlobalCooldown(uint32 _dur = 0, uint32 _time = 0) : duration(_dur), cast_time(_time) { }
-
- uint32 duration;
- uint32 cast_time;
-};
-
-typedef std::unordered_map<uint32 /*category*/, GlobalCooldown> GlobalCooldownList;
-
-class GlobalCooldownMgr // Shared by Player and CharmInfo
-{
-public:
- GlobalCooldownMgr() { }
-
-public:
- bool HasGlobalCooldown(SpellInfo const* spellInfo) const;
- void AddGlobalCooldown(SpellInfo const* spellInfo, uint32 gcd);
- void CancelGlobalCooldown(SpellInfo const* spellInfo);
-
-private:
- GlobalCooldownList m_GlobalCooldowns;
-};
-
enum ActiveStates
{
ACT_PASSIVE = 0x01, // 0x01 - passive
@@ -1171,8 +1148,6 @@ struct CharmInfo
CharmSpellInfo* GetCharmSpell(uint8 index) { return &(_charmspells[index]); }
- GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; }
-
void SetIsCommandAttack(bool val);
bool IsCommandAttack();
void SetIsCommandFollow(bool val);
@@ -1205,8 +1180,6 @@ struct CharmInfo
float _stayX;
float _stayY;
float _stayZ;
-
- GlobalCooldownMgr m_GlobalCooldownMgr;
};
// for clearing special attacks
@@ -1237,16 +1210,6 @@ enum PlayerTotemType
SUMMON_TYPE_TOTEM_AIR = 83
};
-/// 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
-};
-
-typedef std::unordered_map<uint32, uint32> PacketCooldowns;
-
// delay time next attack to prevent client attack animation problems
#define ATTACK_DISPLAY_DELAY 200
#define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player
@@ -1585,8 +1548,6 @@ class Unit : public WorldObject
void SetAuraStack(uint32 spellId, Unit* target, uint32 stack);
void SendPlaySpellVisual(uint32 id);
void SendPlaySpellImpact(ObjectGuid guid, uint32 id);
- void BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown);
- void BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns);
void DeMorph();
@@ -1845,7 +1806,6 @@ class Unit : public WorldObject
void SetChannelObjectGuid(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CHANNEL_OBJECT, guid); }
void SetCurrentCastSpell(Spell* pSpell);
- virtual void ProhibitSpellSchool(SpellSchoolMask /*idSchoolMask*/, uint32 /*unTimeMs*/) { }
void InterruptSpell(CurrentSpellTypes spellType, bool withDelayed = true, bool withInstant = true);
void FinishSpell(CurrentSpellTypes spellType, bool ok = true);
@@ -1863,6 +1823,9 @@ class Unit : public WorldObject
Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
int32 GetCurrentSpellCastTime(uint32 spell_id) const;
+ SpellHistory* GetSpellHistory() { return m_spellHistory; }
+ SpellHistory const* GetSpellHistory() const { return m_spellHistory; }
+
ObjectGuid m_SummonSlot[MAX_SUMMON_SLOT];
ObjectGuid m_ObjectSlot[MAX_GAMEOBJECT_SLOT];
@@ -2293,6 +2256,8 @@ class Unit : public WorldObject
bool _isWalkingBeforeCharm; ///< Are we walking before we were charmed?
time_t _lastDamagedTime; // Part of Evade mechanics
+
+ SpellHistory* m_spellHistory;
};
namespace Trinity
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index 9ff2041ee92..0bf33eee234 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -30,6 +30,7 @@
#include "Pet.h"
#include "World.h"
#include "Group.h"
+#include "SpellHistory.h"
#include "SpellInfo.h"
#include "Player.h"
@@ -344,8 +345,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
if (result == SPELL_CAST_OK)
{
- pet->ToCreature()->AddCreatureSpellCooldown(spellid);
-
unit_target = spell->m_targets.GetUnitTarget();
//10% chance to play special pet attack talk, else growl
@@ -379,8 +378,8 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
else
spell->SendPetCastResult(result);
- if (!pet->ToCreature()->HasSpellCooldown(spellid))
- GetPlayer()->SendClearCooldown(spellid, pet);
+ if (!pet->GetSpellHistory()->HasCooldown(spellid))
+ pet->GetSpellHistory()->ResetCooldown(spellid, true);
spell->finish(false);
delete spell;
@@ -794,7 +793,6 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
{
if (Creature* creature = caster->ToCreature())
{
- creature->AddCreatureSpellCooldown(spellId);
if (Pet* pet = creature->ToPet())
{
// 10% chance to play special pet attack talk, else growl
@@ -812,16 +810,8 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPacket& recvPacket)
{
spell->SendPetCastResult(result);
- if (caster->GetTypeId() == TYPEID_PLAYER)
- {
- if (!caster->ToPlayer()->HasSpellCooldown(spellId))
- GetPlayer()->SendClearCooldown(spellId, caster);
- }
- else
- {
- if (!caster->ToCreature()->HasSpellCooldown(spellId))
- GetPlayer()->SendClearCooldown(spellId, caster);
- }
+ if (!caster->GetSpellHistory()->HasCooldown(spellId))
+ caster->GetSpellHistory()->ResetCooldown(spellId, true);
spell->finish(false);
delete spell;
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 5f641bf713f..a6108b36c13 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -491,8 +491,6 @@ void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket)
}
pet->RemoveOwnedAura(spellId, ObjectGuid::Empty, 0, AURA_REMOVE_BY_CANCEL);
-
- pet->AddCreatureSpellCooldown(spellId);
}
void WorldSession::HandleCancelGrowthAuraOpcode(WorldPacket& /*recvPacket*/) { }
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index 685bcd338a9..06c58a408da 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -3599,4 +3599,12 @@ enum DiminishingLevels
DIMINISHING_LEVEL_TAUNT_IMMUNE = 4
};
+/// 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
+};
+
#endif
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 7651278346f..d576b6c4e7f 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -27,6 +27,7 @@
#include "ObjectAccessor.h"
#include "Util.h"
#include "Spell.h"
+#include "SpellHistory.h"
#include "SpellAuraEffects.h"
#include "Battleground.h"
#include "OutdoorPvPMgr.h"
@@ -1108,15 +1109,13 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const
// Remove cooldown of spells triggered on stance change - they may share cooldown with stance spell
if (spellId)
{
- if (target->GetTypeId() == TYPEID_PLAYER)
- target->ToPlayer()->RemoveSpellCooldown(spellId);
+ target->GetSpellHistory()->ResetCooldown(spellId);
target->CastSpell(target, spellId, true, NULL, this);
}
if (spellId2)
{
- if (target->GetTypeId() == TYPEID_PLAYER)
- target->ToPlayer()->RemoveSpellCooldown(spellId2);
+ target->GetSpellHistory()->ResetCooldown(spellId2);
target->CastSpell(target, spellId2, true, NULL, this);
}
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index 0ba059b1952..5479dcdbf00 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -25,6 +25,7 @@
#include "Player.h"
#include "Unit.h"
#include "Spell.h"
+#include "SpellHistory.h"
#include "SpellAuraEffects.h"
#include "DynamicObject.h"
#include "ObjectAccessor.h"
@@ -418,7 +419,7 @@ void Aura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication * auraApp
if (m_spellInfo->IsCooldownStartedOnEvent())
{
Item* castItem = m_castItemGuid ? caster->ToPlayer()->GetItemByGuid(m_castItemGuid) : NULL;
- caster->ToPlayer()->AddSpellAndCategoryCooldowns(m_spellInfo, castItem ? castItem->GetEntry() : 0, NULL, true);
+ caster->GetSpellHistory()->StartCooldown(m_spellInfo, castItem ? castItem->GetEntry() : 0, nullptr, true);
}
}
}
@@ -446,12 +447,9 @@ void Aura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication * auraA
m_removedApplications.push_back(auraApp);
// reset cooldown state for spells
- if (caster && caster->GetTypeId() == TYPEID_PLAYER)
- {
- if (GetSpellInfo()->IsCooldownStartedOnEvent())
- // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases)
- caster->ToPlayer()->SendCooldownEvent(GetSpellInfo());
- }
+ if (caster && GetSpellInfo()->IsCooldownStartedOnEvent())
+ // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases)
+ caster->GetSpellHistory()->SendCooldownEvent(GetSpellInfo());
}
// removes aura from all targets
@@ -1215,7 +1213,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
break;
case 60970: // Heroic Fury (remove Intercept cooldown)
if (target->GetTypeId() == TYPEID_PLAYER)
- target->ToPlayer()->RemoveSpellCooldown(20252, true);
+ target->GetSpellHistory()->ResetCooldown(20252, true);
break;
}
break;
@@ -1497,15 +1495,15 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
// check cooldown
if (caster->GetTypeId() == TYPEID_PLAYER)
{
- if (caster->ToPlayer()->HasSpellCooldown(aura->GetId()))
+ if (caster->GetSpellHistory()->HasCooldown(aura->GetId()))
{
// This additional check is needed to add a minimal delay before cooldown in in effect
// to allow all bubbles broken by a single damage source proc mana return
- if (caster->ToPlayer()->GetSpellCooldownDelay(aura->GetId()) <= 11)
+ if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetId()) <= 11)
break;
}
else // and add if needed
- caster->ToPlayer()->AddSpellCooldown(aura->GetId(), 0, uint32(time(NULL) + 12));
+ caster->GetSpellHistory()->AddCooldown(aura->GetId(), 0, std::chrono::seconds(12));
}
// effect on caster
@@ -1558,14 +1556,14 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b
// Glyph of Guardian Spirit
if (AuraEffect* aurEff = player->GetAuraEffect(63231, 0))
{
- if (!player->HasSpellCooldown(47788))
+ if (!player->GetSpellHistory()->HasCooldown(47788))
break;
- player->RemoveSpellCooldown(GetSpellInfo()->Id, true);
- player->AddSpellCooldown(GetSpellInfo()->Id, 0, uint32(time(NULL) + aurEff->GetAmount()));
+ player->GetSpellHistory()->ResetCooldown(GetSpellInfo()->Id, true);
+ player->GetSpellHistory()->AddCooldown(GetSpellInfo()->Id, 0, std::chrono::seconds(aurEff->GetAmount()));
WorldPacket data;
- player->BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, GetSpellInfo()->Id, aurEff->GetAmount()*IN_MILLISECONDS);
+ player->GetSpellHistory()->BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, GetSpellInfo()->Id, aurEff->GetAmount() * IN_MILLISECONDS);
player->SendDirectMessage(&data);
}
break;
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index dbe87daa1a1..11ec5bf05a5 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -50,6 +50,7 @@
#include "SpellScript.h"
#include "InstanceScript.h"
#include "SpellInfo.h"
+#include "SpellHistory.h"
#include "Battlefield.h"
#include "BattlefieldMgr.h"
@@ -3226,7 +3227,7 @@ void Spell::cast(bool skipCheck)
//Clear spell cooldowns after every spell is cast if .cheat cooldown is enabled.
if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN))
- m_caster->ToPlayer()->RemoveSpellCooldown(m_spellInfo->Id, true);
+ m_caster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true);
}
SetExecutedCurrently(false);
@@ -3430,30 +3431,7 @@ void Spell::_handle_finish_phase()
void Spell::SendSpellCooldown()
{
- Player* _player = m_caster->ToPlayer();
- if (!_player)
- {
- // Handle pet cooldowns here if needed instead of in PetAI to avoid hidden cooldown restarts
- Creature* _creature = m_caster->ToCreature();
- if (_creature && (_creature->IsPet() || _creature->IsGuardian()))
- _creature->AddCreatureSpellCooldown(m_spellInfo->Id);
-
- return;
- }
-
- // mana/health/etc potions, disabled by client (until combat out as declarate)
- if (m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent()))
- {
- // need in some way provided data for Spell::finish SendCooldownEvent
- _player->SetLastPotionId(m_CastItem->GetEntry());
- return;
- }
-
- // have infinity cooldown but set at aura apply // do not set cooldown for triggered spells (needed by reincarnation)
- if (m_spellInfo->IsCooldownStartedOnEvent() || m_spellInfo->IsPassive() || (_triggeredCastFlags & TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD))
- return;
-
- _player->AddSpellAndCategoryCooldowns(m_spellInfo, m_CastItem ? m_CastItem->GetEntry() : 0, this);
+ m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this);
}
void Spell::update(uint32 difftime)
@@ -4635,23 +4613,26 @@ SpellCastResult Spell::CheckCast(bool strict)
return SPELL_FAILED_CASTER_DEAD;
// check cooldowns to prevent cheating
- if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE))
+ if (!m_spellInfo->IsPassive())
{
- //can cast triggered (by aura only?) spells while have this flag
- if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE) && m_caster->ToPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY))
- return SPELL_FAILED_SPELL_IN_PROGRESS;
+ if (m_caster->GetTypeId() == TYPEID_PLAYER)
+ {
+ //can cast triggered (by aura only?) spells while have this flag
+ if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE) && m_caster->ToPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY))
+ return SPELL_FAILED_SPELL_IN_PROGRESS;
- if (m_caster->ToPlayer()->HasSpellCooldown(m_spellInfo->Id))
+ // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat
+ if (m_caster->ToPlayer()->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent()))
+ return SPELL_FAILED_NOT_READY;
+ }
+
+ if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo))
{
if (m_triggeredByAuraSpell)
return SPELL_FAILED_DONT_REPORT;
else
return SPELL_FAILED_NOT_READY;
}
-
- // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat
- if (m_caster->ToPlayer()->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent()))
- return SPELL_FAILED_NOT_READY;
}
if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && !m_caster->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS))
@@ -5504,13 +5485,13 @@ SpellCastResult Spell::CheckPetCast(Unit* target)
}
// cooldown
- if (Creature const* creatureCaster = m_caster->ToCreature())
- if (creatureCaster->HasSpellCooldown(m_spellInfo->Id))
+ if (Creature* creatureCaster = m_caster->ToCreature())
+ if (!creatureCaster->GetSpellHistory()->IsReady(m_spellInfo))
return SPELL_FAILED_NOT_READY;
// Check if spell is affected by GCD
if (m_spellInfo->StartRecoveryCategory > 0)
- if (m_caster->GetCharmInfo() && m_caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo))
+ if (m_caster->GetCharmInfo() && m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo))
return SPELL_FAILED_NOT_READY;
return CheckCast(true);
@@ -5801,7 +5782,7 @@ SpellCastResult Spell::CheckItems()
if (!proto)
return SPELL_FAILED_ITEM_NOT_READY;
- for (uint8 i = 0; i < MAX_ITEM_SPELLS; ++i)
+ for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
if (proto->Spells[i].SpellCharges)
if (m_CastItem->GetSpellCharges(i) == 0)
return SPELL_FAILED_NO_CHARGES_REMAIN;
@@ -7230,13 +7211,11 @@ enum GCDLimits
bool Spell::HasGlobalCooldown() const
{
- // Only player or controlled units have global cooldown
- if (m_caster->GetCharmInfo())
- return m_caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo);
- else if (m_caster->GetTypeId() == TYPEID_PLAYER)
- return m_caster->ToPlayer()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo);
- else
+ // Only players or controlled units have global cooldown
+ if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo())
return false;
+
+ return m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo);
}
void Spell::TriggerGlobalCooldown()
@@ -7245,6 +7224,10 @@ void Spell::TriggerGlobalCooldown()
if (!gcd)
return;
+ // Only players or controlled units have global cooldown
+ if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo())
+ return;
+
if (m_caster->GetTypeId() == TYPEID_PLAYER)
if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN))
return;
@@ -7266,11 +7249,7 @@ void Spell::TriggerGlobalCooldown()
gcd = MAX_GCD;
}
- // Only players or controlled units have global cooldown
- if (m_caster->GetCharmInfo())
- m_caster->GetCharmInfo()->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd);
- else if (m_caster->GetTypeId() == TYPEID_PLAYER)
- m_caster->ToPlayer()->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd);
+ m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd);
}
void Spell::CancelGlobalCooldown()
@@ -7283,10 +7262,10 @@ void Spell::CancelGlobalCooldown()
return;
// Only players or controlled units have global cooldown
- if (m_caster->GetCharmInfo())
- m_caster->GetCharmInfo()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo);
- else if (m_caster->GetTypeId() == TYPEID_PLAYER)
- m_caster->ToPlayer()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo);
+ if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo())
+ return;
+
+ m_caster->GetSpellHistory()->CancelGlobalCooldown(m_spellInfo);
}
namespace Trinity
diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h
index 2ae2930aeb0..1aac88ac602 100644
--- a/src/server/game/Spells/Spell.h
+++ b/src/server/game/Spells/Spell.h
@@ -473,6 +473,7 @@ class Spell
void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; }
bool IsNextMeleeSwingSpell() const;
bool IsTriggered() const { return (_triggeredCastFlags & TRIGGERED_FULL_MASK) != 0; }
+ bool IsIgnoringCooldowns() const { return (_triggeredCastFlags & TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD) != 0; }
bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; }
bool IsAutoActionResetSpell() const;
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index cb518ee6099..3829e12b790 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -48,6 +48,7 @@
#include "GridNotifiers.h"
#include "Formulas.h"
#include "ScriptMgr.h"
+#include "SpellHistory.h"
#include "GameObjectAI.h"
#include "AccountMgr.h"
#include "InstanceScript.h"
@@ -769,8 +770,8 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
return;
// Reset cooldown on stealth if needed
- if (unitTarget->ToPlayer()->HasSpellCooldown(1784))
- unitTarget->ToPlayer()->RemoveSpellCooldown(1784);
+ if (unitTarget->GetSpellHistory()->HasCooldown(1784))
+ unitTarget->GetSpellHistory()->ResetCooldown(1784);
unitTarget->CastSpell(unitTarget, 1784, true);
return;
@@ -879,7 +880,7 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
// Remove spell cooldown (not category) if spell triggering spell with cooldown and same category
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime
&& m_spellInfo->GetCategory() == spellInfo->GetCategory())
- m_caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id);
+ m_caster->GetSpellHistory()->ResetCooldown(spellInfo->Id);
// original caster guid only for GO cast
m_caster->CastSpell(targets, spellInfo, &values, TRIGGERED_FULL_MASK, NULL, NULL, m_originalCasterGUID);
@@ -932,7 +933,7 @@ void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex)
// Remove spell cooldown (not category) if spell triggering spell with cooldown and same category
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime
&& m_spellInfo->GetCategory() == spellInfo->GetCategory())
- m_caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id);
+ m_caster->GetSpellHistory()->ResetCooldown(spellInfo->Id);
// original caster guid only for GO cast
m_caster->CastSpell(targets, spellInfo, &values, TRIGGERED_FULL_MASK, NULL, NULL, m_originalCasterGUID);
@@ -3472,7 +3473,7 @@ void Spell::EffectInterruptCast(SpellEffIndex effIndex)
if (m_originalCaster)
{
int32 duration = m_spellInfo->GetDuration();
- unitTarget->ProhibitSpellSchool(curSpellInfo->GetSchoolMask(), unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effIndex));
+ unitTarget->GetSpellHistory()->LockSpellSchool(curSpellInfo->GetSchoolMask(), unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effIndex));
}
ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo->Id);
unitTarget->InterruptSpell(CurrentSpellTypes(i), false);
@@ -5839,7 +5840,7 @@ void Spell::EffectCastButtons(SpellEffIndex effIndex)
if (!spellInfo)
continue;
- if (!p_caster->HasSpell(spell_id) || p_caster->HasSpellCooldown(spell_id))
+ if (!p_caster->HasSpell(spell_id) || p_caster->GetSpellHistory()->HasCooldown(spell_id))
continue;
if (!spellInfo->HasAttribute(SPELL_ATTR7_SUMMON_PLAYER_TOTEM))
diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp
new file mode 100644
index 00000000000..b16993f28a3
--- /dev/null
+++ b/src/server/game/Spells/SpellHistory.cpp
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#include "WorldPacket.h"
+#include "SpellHistory.h"
+#include "Pet.h"
+#include "Player.h"
+#include "SpellInfo.h"
+#include "Spell.h"
+#include "World.h"
+
+SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelay = std::chrono::duration_cast<SpellHistory::Clock::duration>(std::chrono::seconds(MONTH));
+SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelayCheck = std::chrono::duration_cast<SpellHistory::Clock::duration>(std::chrono::seconds(MONTH / 2));
+
+template<>
+struct SpellHistory::PersistenceHelper<Player>
+{
+ static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_CHAR_SPELL_COOLDOWNS;
+ static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_CHAR_SPELL_COOLDOWN;
+
+ static void SetIdentifier(PreparedStatement* stmt, uint8 index, Unit* owner) { stmt->setUInt32(index, owner->GetGUID().GetCounter()); }
+
+ static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry)
+ {
+ *spellId = fields[0].GetUInt32();
+ if (!sSpellMgr->GetSpellInfo(*spellId))
+ return false;
+
+ cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[2].GetUInt32()));
+ cooldownEntry->ItemId = fields[1].GetUInt32();
+ return true;
+ }
+
+ static void WriteCooldown(PreparedStatement* stmt, uint8& index, CooldownStorageType::value_type const& cooldown)
+ {
+ stmt->setUInt32(index++, cooldown.first);
+ stmt->setUInt32(index++, cooldown.second.ItemId);
+ stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd)));
+ }
+};
+
+template<>
+struct SpellHistory::PersistenceHelper<Pet>
+{
+ static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_PET_SPELL_COOLDOWNS;
+ static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_PET_SPELL_COOLDOWN;
+
+ static void SetIdentifier(PreparedStatement* stmt, uint8 index, Unit* owner) { stmt->setUInt32(index, owner->GetCharmInfo()->GetPetNumber()); }
+
+ static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry)
+ {
+ *spellId = fields[0].GetUInt32();
+ if (!sSpellMgr->GetSpellInfo(*spellId))
+ return false;
+
+ cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[1].GetUInt32()));
+ cooldownEntry->ItemId = 0;
+ return true;
+ }
+
+ static void WriteCooldown(PreparedStatement* stmt, uint8& index, CooldownStorageType::value_type const& cooldown)
+ {
+ stmt->setUInt32(index++, cooldown.first);
+ stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd)));
+ }
+};
+
+template<class OwnerType>
+void SpellHistory::LoadFromDB(PreparedQueryResult cooldownsResult)
+{
+ typedef PersistenceHelper<OwnerType> StatementInfo;
+
+ if (cooldownsResult)
+ {
+ do
+ {
+ uint32 spellId;
+ CooldownEntry cooldown;
+ if (StatementInfo::ReadCooldown(cooldownsResult->Fetch(), &spellId, &cooldown))
+ _spellCooldowns[spellId] = cooldown;
+
+ } while (cooldownsResult->NextRow());
+ }
+}
+
+template<class OwnerType>
+void SpellHistory::SaveToDB(SQLTransaction& trans)
+{
+ typedef PersistenceHelper<OwnerType> StatementInfo;
+
+ uint8 index = 0;
+ PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsDeleteStatement);
+ StatementInfo::SetIdentifier(stmt, index++, _owner);
+ trans->Append(stmt);
+
+ for (auto const& p : _spellCooldowns)
+ {
+ if (!p.second.OnHold)
+ {
+ index = 0;
+ stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsInsertStatement);
+ StatementInfo::SetIdentifier(stmt, index++, _owner);
+ StatementInfo::WriteCooldown(stmt, index, p);
+ trans->Append(stmt);
+ }
+ }
+}
+
+void SpellHistory::Update()
+{
+ SQLTransaction t;
+ Clock::time_point now = Clock::now();
+ for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();)
+ {
+ if (itr->second.CooldownEnd < now)
+ itr = _spellCooldowns.erase(itr);
+ else
+ ++itr;
+ }
+}
+
+void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell /*= nullptr*/)
+{
+ if (Player* player = _owner->ToPlayer())
+ {
+ // potions start cooldown until exiting combat
+ if (item && (item->IsPotion() || spellInfo->IsCooldownStartedOnEvent()))
+ {
+ player->SetLastPotionId(item->GetEntry());
+ return;
+ }
+ }
+
+ if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive() || (spell && spell->IsIgnoringCooldowns()))
+ return;
+
+ StartCooldown(spellInfo, item ? item->GetEntry() : 0, spell);
+}
+
+bool SpellHistory::IsReady(SpellInfo const* spellInfo) const
+{
+ if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE)
+ if (IsSchoolLocked(spellInfo->GetSchoolMask()))
+ return false;
+
+ if (HasCooldown(spellInfo->Id))
+ return false;
+
+ return true;
+}
+
+template<>
+void SpellHistory::WritePacket<Pet>(WorldPacket& packet) const
+{
+ Clock::time_point now = Clock::now();
+
+ uint8 cooldownsCount = _spellCooldowns.size();
+ packet << uint8(cooldownsCount);
+
+ for (auto const& spellCooldown : _spellCooldowns)
+ {
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellCooldown.first);
+ if (!spellInfo)
+ {
+ packet << uint32(0);
+ packet << uint16(0);
+ packet << uint32(0);
+ packet << uint32(0);
+ continue;
+ }
+
+ packet << uint32(spellCooldown.first); // spell ID
+ packet << uint16(spellInfo->GetCategory()); // spell category
+ if (!spellCooldown.second.OnHold)
+ {
+ uint32 cooldownDuration = spellCooldown.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now).count() : 0;
+ if (cooldownDuration <= 0)
+ {
+ packet << uint32(0);
+ packet << uint32(0);
+ continue;
+ }
+
+ if (spellInfo->GetCategory())
+ {
+ packet << uint32(0);
+ packet << uint32(cooldownDuration);
+ }
+ else
+ {
+ packet << uint32(cooldownDuration);
+ packet << uint32(0);
+ }
+ }
+ }
+}
+
+template<>
+void SpellHistory::WritePacket<Player>(WorldPacket& packet) const
+{
+ Clock::time_point now = Clock::now();
+ Clock::time_point infTime = now + InfinityCooldownDelayCheck;
+
+ uint16 cooldownsCount = _spellCooldowns.size();
+ size_t dataPos = packet.wpos();
+ packet << uint16(cooldownsCount);
+
+ for (auto const& spellCooldown : _spellCooldowns)
+ {
+ SpellInfo const* sEntry = sSpellMgr->GetSpellInfo(spellCooldown.first);
+ if (!sEntry)
+ {
+ --cooldownsCount;
+ continue;
+ }
+
+ packet << uint32(spellCooldown.first);
+
+ packet << uint16(spellCooldown.second.ItemId); // cast item id
+ packet << uint16(sEntry->GetCategory()); // spell category
+
+ // send infinity cooldown in special format
+ if (spellCooldown.second.CooldownEnd >= infTime)
+ {
+ packet << uint32(1); // cooldown
+ packet << uint32(0x80000000); // category cooldown
+ continue;
+ }
+
+ uint32 cooldownDuration = spellCooldown.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now).count() : 0;
+
+ if (sEntry->GetCategory()) // may be wrong, but anyway better than nothing...
+ {
+ packet << uint32(0); // cooldown
+ packet << uint32(cooldownDuration); // category cooldown
+ }
+ else
+ {
+ packet << uint32(cooldownDuration); // cooldown
+ packet << uint32(0); // category cooldown
+ }
+ }
+
+ packet.put<uint16>(dataPos, cooldownsCount);
+}
+
+void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/)
+{
+ // init cooldown values
+ uint32 categoryId = 0;
+ int32 cooldown = -1;
+ int32 categoryCooldown = -1;
+
+ // some special item spells without correct cooldown in SpellInfo
+ // cooldown information stored in item prototype
+ if (itemId)
+ {
+ if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
+ {
+ for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx)
+ {
+ if (uint32(proto->Spells[idx].SpellId) == spellInfo->Id)
+ {
+ categoryId = proto->Spells[idx].SpellCategory;
+ cooldown = proto->Spells[idx].SpellCooldown;
+ categoryCooldown = proto->Spells[idx].SpellCategoryCooldown;
+ break;
+ }
+ }
+ }
+ }
+
+ // if no cooldown found above then base at DBC data
+ if (cooldown < 0 && categoryCooldown < 0)
+ {
+ categoryId = spellInfo->GetCategory();
+ cooldown = spellInfo->RecoveryTime;
+ categoryCooldown = spellInfo->CategoryRecoveryTime;
+ }
+
+ Clock::time_point curTime = Clock::now();
+ Clock::time_point catrecTime;
+ Clock::time_point recTime;
+ bool needsCooldownPacket = false;
+
+ // overwrite time for selected category
+ if (onHold)
+ {
+ // use +MONTH as infinite cooldown marker
+ catrecTime = categoryCooldown > 0 ? (curTime + InfinityCooldownDelay) : curTime;
+ recTime = cooldown > 0 ? (curTime + InfinityCooldownDelay) : catrecTime;
+ }
+ else
+ {
+ // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
+ // prevent 0 cooldowns set by another way
+ if (cooldown <= 0 && categoryCooldown <= 0 && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75)))
+ cooldown = _owner->GetAttackTime(RANGED_ATTACK);
+
+ // Now we have cooldown data (if found any), time to apply mods
+ if (Player* modOwner = _owner->GetSpellModOwner())
+ {
+ if (cooldown > 0)
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell);
+
+ if (categoryCooldown > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS))
+ modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell);
+ }
+
+ if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN))
+ {
+ // Apply SPELL_AURA_MOD_COOLDOWN only to own spells
+ Player* playerOwner = GetPlayerOwner();
+ if (!playerOwner || playerOwner->HasSpell(spellInfo->Id))
+ {
+ needsCooldownPacket = true;
+ cooldown += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
+ }
+ }
+
+ // replace negative cooldowns by 0
+ if (cooldown < 0)
+ cooldown = 0;
+
+ if (categoryCooldown < 0)
+ categoryCooldown = 0;
+
+ // no cooldown after applying spell mods
+ if (cooldown == 0 && categoryCooldown == 0)
+ return;
+
+ catrecTime = categoryCooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(categoryCooldown)) : curTime;
+ recTime = cooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldown)) : catrecTime;
+ }
+
+ // self spell cooldown
+ if (recTime != curTime)
+ {
+ AddCooldown(spellInfo->Id, itemId, recTime, onHold);
+
+ if (needsCooldownPacket)
+ {
+ if (Player* playerOwner = GetPlayerOwner())
+ {
+ WorldPacket spellCooldown;
+ BuildCooldownPacket(spellCooldown, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, cooldown);
+ playerOwner->SendDirectMessage(&spellCooldown);
+ }
+ }
+ }
+
+ // category spells
+ if (categoryId && catrecTime != curTime)
+ {
+ SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(categoryId);
+ if (i_scstore != sSpellsByCategoryStore.end())
+ {
+ for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
+ {
+ if (*i_scset == spellInfo->Id) // skip main spell, already handled above
+ continue;
+
+ AddCooldown(*i_scset, itemId, catrecTime, onHold);
+ }
+ }
+ }
+}
+
+void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= nullptr*/, bool startCooldown /*= true*/)
+{
+ // start cooldowns at server side, if any
+ if (startCooldown)
+ StartCooldown(spellInfo, itemId, spell);
+
+ if (Player* player = GetPlayerOwner())
+ {
+ // Send activate cooldown timer (possible 0) at client side
+ WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
+ data << uint32(spellInfo->Id);
+ data << uint64(_owner->GetGUID());
+ player->SendDirectMessage(&data);
+
+ uint32 category = spellInfo->GetCategory();
+ if (category && spellInfo->CategoryRecoveryTime)
+ {
+ SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(category);
+ if (ct != sSpellsByCategoryStore.end())
+ {
+ for (auto const& cooldownPair : _spellCooldowns)
+ {
+ uint32 categorySpell = cooldownPair.first;
+ if (!ct->second.count(categorySpell))
+ continue;
+
+ if (categorySpell == spellInfo->Id) // skip main spell, already handled above
+ continue;
+
+ SpellInfo const* spellInfo2 = sSpellMgr->EnsureSpellInfo(categorySpell);
+ if (!spellInfo2->IsCooldownStartedOnEvent())
+ continue;
+
+ data.Initialize(SMSG_COOLDOWN_EVENT, 4 + 8);
+ data << uint32(categorySpell);
+ data << uint64(_owner->GetGUID());
+ player->SendDirectMessage(&data);
+ }
+ }
+ }
+ }
+}
+
+void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, bool onHold /*= false*/)
+{
+ CooldownEntry& cooldownEntry = _spellCooldowns[spellId];
+ cooldownEntry.CooldownEnd = cooldownEnd;
+ cooldownEntry.ItemId = itemId;
+ cooldownEntry.OnHold = onHold;
+}
+
+void SpellHistory::ModifyCooldown(uint32 spellId, int32 cooldownModMs)
+{
+ auto itr = _spellCooldowns.find(spellId);
+ if (!cooldownModMs || itr == _spellCooldowns.end())
+ return;
+
+ Clock::time_point now = Clock::now();
+ Clock::duration offset = std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldownModMs));
+ if (itr->second.CooldownEnd + offset > now)
+ itr->second.CooldownEnd += offset;
+ else
+ _spellCooldowns.erase(itr);
+
+ if (Player* playerOwner = GetPlayerOwner())
+ {
+ WorldPacket modifyCooldown(SMSG_MODIFY_COOLDOWN, 4 + 8 + 4);
+ modifyCooldown << uint32(spellId);
+ modifyCooldown << uint64(_owner->GetGUID());
+ modifyCooldown << int32(cooldownModMs);
+ playerOwner->SendDirectMessage(&modifyCooldown);
+ }
+}
+
+void SpellHistory::ResetCooldown(uint32 spellId, bool update /*= false*/)
+{
+ auto itr = _spellCooldowns.find(spellId);
+ if (itr == _spellCooldowns.end())
+ return;
+
+ ResetCooldown(itr, update);
+}
+
+void SpellHistory::ResetCooldown(CooldownStorageType::iterator& itr, bool update /*= false*/)
+{
+ if (update)
+ {
+ if (Player* playerOwner = GetPlayerOwner())
+ {
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, 4 + 8);
+ data << uint32(itr->first);
+ data << uint64(_owner->GetGUID());
+ playerOwner->SendDirectMessage(&data);
+ }
+ }
+
+ itr = _spellCooldowns.erase(itr);
+}
+
+void SpellHistory::ResetAllCooldowns()
+{
+ if (GetPlayerOwner())
+ {
+ std::vector<int32> cooldowns;
+ cooldowns.reserve(_spellCooldowns.size());
+ for (auto const& p : _spellCooldowns)
+ cooldowns.push_back(p.first);
+
+ SendClearCooldowns(cooldowns);
+ }
+
+ _spellCooldowns.clear();
+}
+
+bool SpellHistory::HasCooldown(uint32 spellId) const
+{
+ return _spellCooldowns.count(spellId) != 0;
+}
+
+uint32 SpellHistory::GetRemainingCooldown(uint32 spellId) const
+{
+ auto itr = _spellCooldowns.find(spellId);
+ if (itr == _spellCooldowns.end())
+ return 0;
+
+ Clock::time_point now = Clock::now();
+ if (itr->second.CooldownEnd < now)
+ return 0;
+
+ Clock::duration remaining = itr->second.CooldownEnd - now;
+ return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count());
+}
+
+void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime)
+{
+ Clock::time_point lockoutEnd = Clock::now() + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(lockoutTime));
+ for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ if (SpellSchoolMask(1 << i) & schoolMask)
+ _schoolLockouts[i] = lockoutEnd;
+
+ std::set<uint32> knownSpells;
+ if (Player* plrOwner = _owner->ToPlayer())
+ {
+ for (auto const& p : plrOwner->GetSpellMap())
+ if (p.second->state != PLAYERSPELL_REMOVED)
+ knownSpells.insert(p.first);
+ }
+ else if (Pet* petOwner = _owner->ToPet())
+ {
+ for (auto const& p : petOwner->m_spells)
+ if (p.second.state != PETSPELL_REMOVED)
+ knownSpells.insert(p.first);
+ }
+ else
+ {
+ Creature* creatureOwner = _owner->ToCreature();
+ for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i)
+ if (creatureOwner->m_spells[i])
+ knownSpells.insert(creatureOwner->m_spells[i]);
+ }
+
+ PacketCooldowns cooldowns;
+ WorldPacket spellCooldowns;
+ for (uint32 spellId : knownSpells)
+ {
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(spellId);
+ if (spellInfo->IsCooldownStartedOnEvent())
+ continue;
+
+ if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
+ continue;
+
+ if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellId) < lockoutTime)
+ {
+ cooldowns[spellId] = lockoutTime;
+ AddCooldown(spellId, 0, lockoutEnd);
+ }
+ }
+
+ if (Player* player = GetPlayerOwner())
+ {
+ if (!cooldowns.empty())
+ {
+ BuildCooldownPacket(spellCooldowns, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
+ player->SendDirectMessage(&spellCooldowns);
+ }
+ }
+}
+
+bool SpellHistory::IsSchoolLocked(SpellSchoolMask schoolMask) const
+{
+ Clock::time_point now = Clock::now();
+ for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
+ if (SpellSchoolMask(1 << i) & schoolMask)
+ if (_schoolLockouts[i] > now)
+ return true;
+
+ return false;
+}
+
+bool SpellHistory::HasGlobalCooldown(SpellInfo const* spellInfo) const
+{
+ auto itr = _globalCooldowns.find(spellInfo->StartRecoveryCategory);
+ return itr != _globalCooldowns.end() && itr->second > Clock::now();
+}
+
+void SpellHistory::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration)
+{
+ _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::now() + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(duration));
+}
+
+void SpellHistory::CancelGlobalCooldown(SpellInfo const* spellInfo)
+{
+ _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::time_point(Clock::duration(0));
+}
+
+Player* SpellHistory::GetPlayerOwner() const
+{
+ return _owner->GetCharmerOrOwnerPlayerOrPlayerItself();
+}
+
+void SpellHistory::SendClearCooldowns(std::vector<int32> const& cooldowns) const
+{
+ if (Player* playerOwner = GetPlayerOwner())
+ {
+ for (int32 spell : cooldowns)
+ {
+ WorldPacket data(SMSG_CLEAR_COOLDOWN, 4 + 8);
+ data << uint32(spell);
+ data << uint64(_owner->GetGUID());
+ playerOwner->SendDirectMessage(&data);
+ }
+ }
+}
+
+void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown) const
+{
+ data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + 4 + 4);
+ data << uint64(_owner->GetGUID());
+ data << uint8(flags);
+ data << uint32(spellId);
+ data << uint32(cooldown);
+}
+
+void SpellHistory::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns) const
+{
+ data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + (4 + 4) * cooldowns.size());
+ data << uint8(flags);
+ for (auto const& cooldown : cooldowns)
+ {
+ data << cooldown.first;
+ data << cooldown.second;
+ }
+}
+
+template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult);
+template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult);
+template void SpellHistory::SaveToDB<Player>(SQLTransaction& trans);
+template void SpellHistory::SaveToDB<Pet>(SQLTransaction& trans);
diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h
new file mode 100644
index 00000000000..f1533d57aef
--- /dev/null
+++ b/src/server/game/Spells/SpellHistory.h
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SpellHistory_h__
+#define SpellHistory_h__
+
+#include "SharedDefines.h"
+#include "QueryResult.h"
+#include "Transaction.h"
+#include <chrono>
+#include <deque>
+
+class Item;
+class Player;
+class Spell;
+class SpellInfo;
+class Unit;
+struct SpellCategoryEntry;
+
+class SpellHistory
+{
+public:
+ typedef std::chrono::system_clock Clock;
+
+ struct CooldownEntry
+ {
+ CooldownEntry() : ItemId(0), OnHold(false) { }
+ CooldownEntry(Clock::time_point endTime, uint32 itemId) : CooldownEnd(endTime), ItemId(itemId), OnHold(false) { }
+
+ Clock::time_point CooldownEnd;
+ uint32 ItemId;
+ bool OnHold;
+ };
+
+ typedef std::unordered_map<uint32 /*spellId*/, CooldownEntry> CooldownStorageType;
+ typedef std::unordered_map<uint32 /*categoryId*/, Clock::time_point> GlobalCooldownStorageType;
+
+ explicit SpellHistory(Unit* owner) : _owner(owner), _schoolLockouts() { }
+
+ template<class OwnerType>
+ void LoadFromDB(PreparedQueryResult cooldownsResult);
+
+ template<class OwnerType>
+ void SaveToDB(SQLTransaction& trans);
+
+ void Update();
+
+ void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr);
+ bool IsReady(SpellInfo const* spellInfo) const;
+ template<class OwnerType>
+ void WritePacket(WorldPacket& packet) const;
+
+ // Cooldowns
+ static Clock::duration const InfinityCooldownDelay; // used for set "infinity cooldowns" for spells and check
+ static Clock::duration const InfinityCooldownDelayCheck;
+
+ void StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = nullptr, bool onHold = false);
+ void SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId = 0, Spell* spell = nullptr, bool startCooldown = true);
+
+ template<class Type, class Period>
+ void AddCooldown(uint32 spellId, uint32 itemId, std::chrono::duration<Type, Period> cooldownDuration)
+ {
+ AddCooldown(spellId, itemId, Clock::now() + std::chrono::duration_cast<Clock::duration>(cooldownDuration));
+ }
+
+ void AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, bool onHold = false);
+ void ModifyCooldown(uint32 spellId, int32 cooldownModMs);
+ void ResetCooldown(uint32 spellId, bool update = false);
+ void ResetCooldown(CooldownStorageType::iterator& itr, bool update = false);
+ template<typename Predicate>
+ void ResetCooldowns(Predicate predicate, bool update = false)
+ {
+ std::vector<int32> resetCooldowns;
+ resetCooldowns.reserve(_spellCooldowns.size());
+ for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();)
+ {
+ if (predicate(itr))
+ {
+ resetCooldowns.push_back(itr->first);
+ ResetCooldown(itr, false);
+ }
+ else
+ ++itr;
+ }
+
+ if (update && !resetCooldowns.empty())
+ SendClearCooldowns(resetCooldowns);
+ }
+
+ void ResetAllCooldowns();
+ bool HasCooldown(uint32 spellId) const;
+ uint32 GetRemainingCooldown(uint32 spellId) const;
+
+ // School lockouts
+ void LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime);
+ bool IsSchoolLocked(SpellSchoolMask schoolMask) const;
+
+ // Global cooldown
+ bool HasGlobalCooldown(SpellInfo const* spellInfo) const;
+ void AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration);
+ void CancelGlobalCooldown(SpellInfo const* spellInfo);
+
+ void BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown) const;
+
+ CooldownStorageType::size_type GetCooldownsSizeForPacket() const { return _spellCooldowns.size(); }
+
+private:
+ Player* GetPlayerOwner() const;
+ void SendClearCooldowns(std::vector<int32> const& cooldowns) const;
+
+ typedef std::unordered_map<uint32, uint32> PacketCooldowns;
+ void BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns) const;
+
+ Unit* _owner;
+ CooldownStorageType _spellCooldowns;
+ Clock::time_point _schoolLockouts[MAX_SPELL_SCHOOL];
+ GlobalCooldownStorageType _globalCooldowns;
+
+ template<class T>
+ struct PersistenceHelper { };
+};
+
+#endif // SpellHistory_h__
diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp
index 0b74eb6bee7..a43416dc67d 100644
--- a/src/server/scripts/Commands/cs_misc.cpp
+++ b/src/server/scripts/Commands/cs_misc.cpp
@@ -35,6 +35,7 @@
#include "GroupMgr.h"
#include "MMapFactory.h"
#include "DisableMgr.h"
+#include "SpellHistory.h"
class misc_commandscript : public CommandScript
{
@@ -708,7 +709,7 @@ public:
if (!*args)
{
- target->RemoveAllSpellCooldown();
+ target->GetSpellHistory()->ResetAllCooldowns();
handler->PSendSysMessage(LANG_REMOVEALL_COOLDOWN, nameLink.c_str());
}
else
@@ -718,14 +719,15 @@ public:
if (!spellIid)
return false;
- if (!sSpellMgr->GetSpellInfo(spellIid))
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellIid);
+ if (!spellInfo)
{
handler->PSendSysMessage(LANG_UNKNOWN_SPELL, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str());
handler->SetSentErrorMessage(true);
return false;
}
- target->RemoveSpellCooldown(spellIid, true);
+ target->GetSpellHistory()->ResetCooldown(spellIid, true);
handler->PSendSysMessage(LANG_REMOVE_COOLDOWN, spellIid, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str());
}
return true;
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
index 15bdedc75db..68430f6f0f7 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp
@@ -24,6 +24,7 @@
#include "PassiveAI.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellHistory.h"
#include "SpellAuraEffects.h"
#include "SpellScript.h"
#include "Transport.h"
@@ -644,10 +645,10 @@ protected:
{
if (Instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == IN_PROGRESS &&
!me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE) &&
- !me->HasSpellCooldown(BurningPitchId))
+ !me->GetSpellHistory()->HasCooldown(BurningPitchId))
{
DoCastAOE(BurningPitchId, true);
- me->_AddCreatureSpellCooldown(BurningPitchId, time(NULL) + urand(3000, 4000) / IN_MILLISECONDS);
+ me->GetSpellHistory()->AddCooldown(BurningPitchId, 0, std::chrono::milliseconds(urand(3000, 4000)));
}
}
@@ -1469,7 +1470,7 @@ struct npc_gunship_boarding_addAI : public gunship_npc_AI
DoCast(me, SPELL_BATTLE_EXPERIENCE, true);
DoCast(me, SPELL_TELEPORT_TO_ENEMY_SHIP, true);
DoCast(me, Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_MELEE_TARGETING_ON_ORGRIMS_HAMMER : SPELL_MELEE_TARGETING_ON_SKYBREAKER, true);
- me->_AddCreatureSpellCooldown(BurningPitchId, time(NULL) + 3);
+ me->GetSpellHistory()->AddCooldown(BurningPitchId, 0, std::chrono::seconds(3));
std::list<Player*> players;
Trinity::UnitAuraCheck check(true, Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_ON_ORGRIMS_HAMMER_DECK : SPELL_ON_SKYBREAKER_DECK);
@@ -1698,11 +1699,11 @@ class npc_gunship_rocketeer : public CreatureScript
return;
uint32 spellId = me->GetEntry() == NPC_SKYBREAKER_MORTAR_SOLDIER ? SPELL_ROCKET_ARTILLERY_A : SPELL_ROCKET_ARTILLERY_H;
- if (me->HasSpellCooldown(spellId))
+ if (me->GetSpellHistory()->HasCooldown(spellId))
return;
DoCastAOE(spellId, true);
- me->_AddCreatureSpellCooldown(spellId, time(NULL) + 9);
+ me->GetSpellHistory()->AddCooldown(spellId, 0, std::chrono::seconds(9));
}
};
diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp
index bb1e88ce158..d5d9f8ae77e 100644
--- a/src/server/scripts/Northrend/zone_storm_peaks.cpp
+++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp
@@ -19,6 +19,7 @@
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "ScriptedEscortAI.h"
+#include "SpellHistory.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
#include "Vehicle.h"
diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp
index 993e64f22dc..68c115f9faf 100644
--- a/src/server/scripts/Spells/spell_generic.cpp
+++ b/src/server/scripts/Spells/spell_generic.cpp
@@ -34,6 +34,7 @@
#include "Pet.h"
#include "ReputationMgr.h"
#include "SkillDiscovery.h"
+#include "SpellHistory.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
#include "Vehicle.h"
@@ -1293,8 +1294,8 @@ class spell_gen_divine_storm_cd_reset : public SpellScriptLoader
void HandleScript(SpellEffIndex /*effIndex*/)
{
Player* caster = GetCaster()->ToPlayer();
- if (caster->HasSpellCooldown(SPELL_DIVINE_STORM))
- caster->RemoveSpellCooldown(SPELL_DIVINE_STORM, true);
+ if (caster->GetSpellHistory()->HasCooldown(SPELL_DIVINE_STORM))
+ caster->GetSpellHistory()->ResetCooldown(SPELL_DIVINE_STORM, true);
}
void Register() override
@@ -3330,7 +3331,7 @@ class spell_pvp_trinket_wotf_shared_cd : public SpellScriptLoader
{
// This is only needed because spells cast from spell_linked_spell are triggered by default
// Spell::SendSpellCooldown() skips all spells with TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD
- GetCaster()->ToPlayer()->AddSpellAndCategoryCooldowns(GetSpellInfo(), GetCastItem() ? GetCastItem()->GetEntry() : 0, GetSpell());
+ GetCaster()->GetSpellHistory()->StartCooldown(GetSpellInfo(), 0, GetSpell());
}
void Register() override
diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp
index 3ee337f81d4..75df264360f 100644
--- a/src/server/scripts/Spells/spell_hunter.cpp
+++ b/src/server/scripts/Spells/spell_hunter.cpp
@@ -27,6 +27,7 @@
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
+#include "SpellHistory.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
@@ -246,7 +247,7 @@ class spell_hun_chimera_shot : public SpellScriptLoader
if (spellId)
caster->CastCustomSpell(unitTarget, spellId, &basePoint, 0, 0, true);
if (spellId == SPELL_HUNTER_CHIMERA_SHOT_SCORPID && caster->ToPlayer()) // Scorpid Sting - Add 1 minute cooldown
- caster->ToPlayer()->AddSpellCooldown(spellId, 0, uint32(time(NULL) + 60));
+ caster->GetSpellHistory()->AddCooldown(spellId, 0, std::chrono::minutes(1));
}
}
@@ -654,24 +655,20 @@ class spell_hun_readiness : public SpellScriptLoader
void HandleDummy(SpellEffIndex /*effIndex*/)
{
- Player* caster = GetCaster()->ToPlayer();
// immediately finishes the cooldown on your other Hunter abilities except Bestial Wrath
- const SpellCooldowns& cm = caster->ToPlayer()->GetSpellCooldownMap();
- for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
+ GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
- SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first);
+ SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
///! If spellId in cooldown map isn't valid, the above will return a null pointer.
- if (spellInfo &&
- spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER &&
+ if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER &&
spellInfo->Id != SPELL_HUNTER_READINESS &&
spellInfo->Id != SPELL_HUNTER_BESTIAL_WRATH &&
spellInfo->Id != SPELL_DRAENEI_GIFT_OF_THE_NAARU &&
spellInfo->GetRecoveryTime() > 0)
- caster->RemoveSpellCooldown((itr++)->first, true);
- else
- ++itr;
- }
+ return true;
+ return false;
+ }, true);
}
void Register() override
diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp
index b290cb75a2b..717382a0e36 100644
--- a/src/server/scripts/Spells/spell_item.cpp
+++ b/src/server/scripts/Spells/spell_item.cpp
@@ -24,6 +24,7 @@
#include "Player.h"
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
+#include "SpellHistory.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
#include "SkillDiscovery.h"
@@ -1349,7 +1350,7 @@ class spell_item_red_rider_air_rifle : public SpellScriptLoader
caster->CastSpell(caster, SPELL_AIR_RIFLE_HOLD_VISUAL, true);
// needed because this spell shares GCD with its triggered spells (which must not be cast with triggered flag)
if (Player* player = caster->ToPlayer())
- player->GetGlobalCooldownMgr().CancelGlobalCooldown(GetSpellInfo());
+ player->GetSpellHistory()->CancelGlobalCooldown(GetSpellInfo());
if (urand(0, 4))
caster->CastSpell(target, SPELL_AIR_RIFLE_SHOOT, false);
else
@@ -2368,7 +2369,7 @@ class spell_item_rocket_boots : public SpellScriptLoader
if (Battleground* bg = caster->GetBattleground())
bg->EventPlayerDroppedFlag(caster);
- caster->RemoveSpellCooldown(SPELL_ROCKET_BOOTS_PROC);
+ caster->GetSpellHistory()->ResetCooldown(SPELL_ROCKET_BOOTS_PROC);
caster->CastSpell(caster, SPELL_ROCKET_BOOTS_PROC, true, NULL);
}
@@ -2548,14 +2549,14 @@ class spell_item_refocus : public SpellScriptLoader
if (!caster || caster->getClass() != CLASS_HUNTER)
return;
- if (caster->HasSpellCooldown(SPELL_AIMED_SHOT))
- caster->RemoveSpellCooldown(SPELL_AIMED_SHOT, true);
+ if (caster->GetSpellHistory()->HasCooldown(SPELL_AIMED_SHOT))
+ caster->GetSpellHistory()->ResetCooldown(SPELL_AIMED_SHOT, true);
- if (caster->HasSpellCooldown(SPELL_MULTISHOT))
- caster->RemoveSpellCooldown(SPELL_MULTISHOT, true);
+ if (caster->GetSpellHistory()->HasCooldown(SPELL_MULTISHOT))
+ caster->GetSpellHistory()->ResetCooldown(SPELL_MULTISHOT, true);
- if (caster->HasSpellCooldown(SPELL_VOLLEY))
- caster->RemoveSpellCooldown(SPELL_VOLLEY, true);
+ if (caster->GetSpellHistory()->HasCooldown(SPELL_VOLLEY))
+ caster->GetSpellHistory()->ResetCooldown(SPELL_VOLLEY, true);
}
void Register() override
diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp
index ba9f66d255b..4edbf8822f7 100644
--- a/src/server/scripts/Spells/spell_mage.cpp
+++ b/src/server/scripts/Spells/spell_mage.cpp
@@ -23,6 +23,7 @@
#include "Player.h"
#include "ScriptMgr.h"
+#include "SpellHistory.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
@@ -171,22 +172,12 @@ class spell_mage_cold_snap : public SpellScriptLoader
void HandleDummy(SpellEffIndex /*effIndex*/)
{
- Player* caster = GetCaster()->ToPlayer();
- // immediately finishes the cooldown on Frost spells
- const SpellCooldowns& cm = caster->GetSpellCooldownMap();
- for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
+ GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
-
- if (spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
- (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) &&
- spellInfo->Id != SPELL_MAGE_COLD_SNAP && spellInfo->GetRecoveryTime() > 0)
- {
- caster->RemoveSpellCooldown((itr++)->first, true);
- }
- else
- ++itr;
- }
+ return spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) &&
+ spellInfo->Id != SPELL_MAGE_COLD_SNAP && spellInfo->GetRecoveryTime() > 0;
+ }, true);
}
void Register() override
diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp
index cf320a05346..197d55486a8 100644
--- a/src/server/scripts/Spells/spell_paladin.cpp
+++ b/src/server/scripts/Spells/spell_paladin.cpp
@@ -25,6 +25,7 @@
#include "ScriptMgr.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
+#include "SpellHistory.h"
#include "Group.h"
enum PaladinSpells
@@ -133,7 +134,7 @@ class spell_pal_ardent_defender : public SpellScriptLoader
int32 remainingHealth = victim->GetHealth() - dmgInfo.GetDamage();
uint32 allowedHealth = victim->CountPctFromMaxHealth(35);
// If damage kills us
- if (remainingHealth <= 0 && !victim->ToPlayer()->HasSpellCooldown(PAL_SPELL_ARDENT_DEFENDER_HEAL))
+ if (remainingHealth <= 0 && !victim->GetSpellHistory()->HasCooldown(PAL_SPELL_ARDENT_DEFENDER_HEAL))
{
// Cast healing spell, completely avoid damage
absorbAmount = dmgInfo.GetDamage();
@@ -148,7 +149,7 @@ class spell_pal_ardent_defender : public SpellScriptLoader
int32 healAmount = int32(victim->CountPctFromMaxHealth(uint32(healPct * pctFromDefense)));
victim->CastCustomSpell(victim, PAL_SPELL_ARDENT_DEFENDER_HEAL, &healAmount, NULL, NULL, true, NULL, aurEff);
- victim->ToPlayer()->AddSpellCooldown(PAL_SPELL_ARDENT_DEFENDER_HEAL, 0, time(NULL) + 120);
+ victim->GetSpellHistory()->AddCooldown(PAL_SPELL_ARDENT_DEFENDER_HEAL, 0, std::chrono::minutes(2));
}
else if (remainingHealth < int32(allowedHealth))
{
diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp
index edb5cd5260c..da50f471f1c 100644
--- a/src/server/scripts/Spells/spell_rogue.cpp
+++ b/src/server/scripts/Spells/spell_rogue.cpp
@@ -25,6 +25,7 @@
#include "ScriptMgr.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
+#include "SpellHistory.h"
#include "Containers.h"
enum RogueSpells
@@ -139,11 +140,11 @@ class spell_rog_cheat_death : public SpellScriptLoader
void Absorb(AuraEffect* /*aurEff*/, DamageInfo & dmgInfo, uint32 & absorbAmount)
{
Player* target = GetTarget()->ToPlayer();
- if (dmgInfo.GetDamage() < target->GetHealth() || target->HasSpellCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN) || !roll_chance_i(absorbChance))
+ if (dmgInfo.GetDamage() < target->GetHealth() || target->GetSpellHistory()->HasCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN) || !roll_chance_i(absorbChance))
return;
target->CastSpell(target, SPELL_ROGUE_CHEAT_DEATH_COOLDOWN, true);
- target->AddSpellCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN, 0, time(NULL) + 60);
+ target->GetSpellHistory()->AddCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN, 0, std::chrono::minutes(1));
uint32 health10 = target->CountPctFromMaxHealth(10);
@@ -443,35 +444,21 @@ class spell_rog_preparation : public SpellScriptLoader
void HandleDummy(SpellEffIndex /*effIndex*/)
{
- Player* caster = GetCaster()->ToPlayer();
-
- //immediately finishes the cooldown on certain Rogue abilities
- const SpellCooldowns& cm = caster->GetSpellCooldownMap();
- for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
+ Unit* caster = GetCaster();
+ caster->GetSpellHistory()->ResetCooldowns([caster](SpellHistory::CooldownStorageType::iterator itr) -> bool
{
SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first);
-
- if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE)
- {
- if (spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_COLDB_SHADOWSTEP || // Cold Blood, Shadowstep
- spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_VAN_EVAS_SPRINT) // Vanish, Evasion, Sprint
- caster->RemoveSpellCooldown((itr++)->first, true);
- else if (caster->HasAura(SPELL_ROGUE_GLYPH_OF_PREPARATION))
- {
- if (spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_DISMANTLE || // Dismantle
- spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_KICK || // Kick
- (spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_BLADE_FLURRY && // Blade Flurry
- spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_BLADE_FLURRY))
- caster->RemoveSpellCooldown((itr++)->first, true);
- else
- ++itr;
- }
- else
- ++itr;
- }
- else
- ++itr;
- }
+ if (spellInfo->SpellFamilyName != SPELLFAMILY_ROGUE)
+ return false;
+
+ return (spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_COLDB_SHADOWSTEP || // Cold Blood, Shadowstep
+ spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_VAN_EVAS_SPRINT) || // Vanish, Evasion, Sprint
+ (caster->HasAura(SPELL_ROGUE_GLYPH_OF_PREPARATION) &&
+ (spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_DISMANTLE || // Dismantle
+ spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_KICK || // Kick
+ (spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_BLADE_FLURRY && // Blade Flurry
+ spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_BLADE_FLURRY)));
+ }, true);
}
void Register() override
diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp
index 2ee0d5091b5..5564a8275c8 100644
--- a/src/server/scripts/Spells/spell_shaman.cpp
+++ b/src/server/scripts/Spells/spell_shaman.cpp
@@ -25,6 +25,7 @@
#include "ScriptMgr.h"
#include "GridNotifiers.h"
#include "Unit.h"
+#include "SpellHistory.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
@@ -338,7 +339,7 @@ class spell_sha_earth_shield : public SpellScriptLoader
{
//! HACK due to currenct proc system implementation
if (Player* player = GetTarget()->ToPlayer())
- if (player->HasSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL))
+ if (player->GetSpellHistory()->HasCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL))
return false;
return true;
}
@@ -351,7 +352,7 @@ class spell_sha_earth_shield : public SpellScriptLoader
/// @hack: due to currenct proc system implementation
if (Player* player = GetTarget()->ToPlayer())
- player->AddSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL, 0, time(NULL) + 3);
+ player->GetSpellHistory()->AddCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL, 0, std::chrono::seconds(3));
}
void Register() override
@@ -804,7 +805,7 @@ class spell_sha_item_t10_elemental_2p_bonus : public SpellScriptLoader
{
PreventDefaultAction();
if (Player* target = GetTarget()->ToPlayer())
- target->ModifySpellCooldown(SPELL_SHAMAN_ELEMENTAL_MASTERY, -aurEff->GetAmount());
+ target->GetSpellHistory()->ModifyCooldown(SPELL_SHAMAN_ELEMENTAL_MASTERY, -aurEff->GetAmount());
}
void Register() override
diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp
index fa2b323e220..c7839a59608 100644
--- a/src/server/scripts/Spells/spell_warrior.cpp
+++ b/src/server/scripts/Spells/spell_warrior.cpp
@@ -23,6 +23,7 @@
#include "Player.h"
#include "ScriptMgr.h"
+#include "SpellHistory.h"
#include "SpellScript.h"
#include "SpellAuraEffects.h"
@@ -847,7 +848,7 @@ class spell_warr_vigilance_trigger : public SpellScriptLoader
// Remove Taunt cooldown
if (Player* target = GetHitPlayer())
- target->RemoveSpellCooldown(SPELL_WARRIOR_TAUNT, true);
+ target->GetSpellHistory()->ResetCooldown(SPELL_WARRIOR_TAUNT, true);
}
void Register() override
diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp
index 93010f06283..d1d2ddc8a80 100644
--- a/src/server/scripts/World/npcs_special.cpp
+++ b/src/server/scripts/World/npcs_special.cpp
@@ -53,6 +53,7 @@ EndContentData */
#include "GridNotifiersImpl.h"
#include "Cell.h"
#include "CellImpl.h"
+#include "SpellHistory.h"
#include "SpellAuras.h"
#include "Pet.h"
#include "CreatureTextMgr.h"
@@ -1214,14 +1215,14 @@ public:
if (creature->IsQuestGiver())
player->PrepareQuestMenu(creature->GetGUID());
- if (player->HasSpellCooldown(SPELL_INT) ||
- player->HasSpellCooldown(SPELL_ARM) ||
- player->HasSpellCooldown(SPELL_DMG) ||
- player->HasSpellCooldown(SPELL_RES) ||
- player->HasSpellCooldown(SPELL_STR) ||
- player->HasSpellCooldown(SPELL_AGI) ||
- player->HasSpellCooldown(SPELL_STM) ||
- player->HasSpellCooldown(SPELL_SPI))
+ if (player->GetSpellHistory()->HasCooldown(SPELL_INT) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_ARM) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_DMG) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_RES) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_STR) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_AGI) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_STM) ||
+ player->GetSpellHistory()->HasCooldown(SPELL_SPI))
player->SEND_GOSSIP_MENU(7393, creature->GetGUID());
else
{
@@ -1281,52 +1282,44 @@ public:
bool OnGossipSelect(Player* player, Creature* creature, uint32 sender, uint32 action) override
{
player->PlayerTalkClass->ClearMenus();
+ uint32 spellId = 0;
switch (sender)
{
case GOSSIP_SENDER_MAIN:
SendAction(player, creature, action);
break;
case GOSSIP_SENDER_MAIN + 1:
- creature->CastSpell(player, SPELL_DMG, false);
- player->AddSpellCooldown(SPELL_DMG, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_DMG;
break;
case GOSSIP_SENDER_MAIN + 2:
- creature->CastSpell(player, SPELL_RES, false);
- player->AddSpellCooldown(SPELL_RES, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_RES;
break;
case GOSSIP_SENDER_MAIN + 3:
- creature->CastSpell(player, SPELL_ARM, false);
- player->AddSpellCooldown(SPELL_ARM, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_ARM;
break;
case GOSSIP_SENDER_MAIN + 4:
- creature->CastSpell(player, SPELL_SPI, false);
- player->AddSpellCooldown(SPELL_SPI, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_SPI;
break;
case GOSSIP_SENDER_MAIN + 5:
- creature->CastSpell(player, SPELL_INT, false);
- player->AddSpellCooldown(SPELL_INT, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_INT;
break;
case GOSSIP_SENDER_MAIN + 6:
- creature->CastSpell(player, SPELL_STM, false);
- player->AddSpellCooldown(SPELL_STM, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_STM;
break;
case GOSSIP_SENDER_MAIN + 7:
- creature->CastSpell(player, SPELL_STR, false);
- player->AddSpellCooldown(SPELL_STR, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_STR;
break;
case GOSSIP_SENDER_MAIN + 8:
- creature->CastSpell(player, SPELL_AGI, false);
- player->AddSpellCooldown(SPELL_AGI, 0, time(NULL) + 7200);
- SendAction(player, creature, action);
+ spellId = SPELL_AGI;
break;
}
+
+ if (spellId)
+ {
+ creature->CastSpell(player, spellId, false);
+ player->GetSpellHistory()->AddCooldown(spellId, 0, std::chrono::hours(2));
+ SendAction(player, creature, action);
+ }
return true;
}
};
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
index c8632b8a3c2..1ca01501d01 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp
@@ -105,7 +105,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT friend, flags, note FROM character_social JOIN characters ON characters.guid = character_social.friend WHERE character_social.guid = ? AND deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time FROM character_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_GUILD_MEMBER, "SELECT guildid, rank FROM guild_member WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED, "SELECT g.guildid, g.name, gr.rname, gr.rid, gm.pnote, gm.offnote "
@@ -497,7 +497,8 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_CHAR_REP_FACTION_CHANGE, "UPDATE character_reputation SET faction = ?, standing = ? WHERE faction = ? AND guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET knownTitles = ? WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_RES_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET chosenTitle = 0 WHERE guid = ?", CONNECTION_ASYNC);
- PrepareStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN, "DELETE FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_CHAR_SPELL_COOLDOWNS, "DELETE FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_CHAR_SPELL_COOLDOWN, "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHARACTER, "DELETE FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_ACTION, "DELETE FROM character_action WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_AURA, "DELETE FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
@@ -577,7 +578,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_INS_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_PET_AURA, "SELECT caster_guid, spell, effect_mask, recalculate_mask, stackcount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxduration, remaintime, remaincharges FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH);
PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_BOTH);
PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h
index e56a24d6865..f88a912e022 100644
--- a/src/server/shared/Database/Implementation/CharacterDatabase.h
+++ b/src/server/shared/Database/Implementation/CharacterDatabase.h
@@ -436,7 +436,8 @@ enum CharacterDatabaseStatements
CHAR_UPD_CHAR_REP_FACTION_CHANGE,
CHAR_UPD_CHAR_TITLES_FACTION_CHANGE,
CHAR_RES_CHAR_TITLES_FACTION_CHANGE,
- CHAR_DEL_CHAR_SPELL_COOLDOWN,
+ CHAR_DEL_CHAR_SPELL_COOLDOWNS,
+ CHAR_INS_CHAR_SPELL_COOLDOWN,
CHAR_DEL_CHARACTER,
CHAR_DEL_CHAR_ACTION,
CHAR_DEL_CHAR_AURA,