diff options
author | Shauren <shauren.trinity@gmail.com> | 2015-10-29 17:55:57 +0100 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2015-10-29 17:56:18 +0100 |
commit | 7dcddd90be6e49281ca04239de5839e757ef3f7a (patch) | |
tree | c9bc94e1c6e970c708393622e640f51f703d35b9 /src | |
parent | 3f32307d377d53f31ed92f068fdc4db8069a3b6c (diff) |
Core/Spells: Improved spell category cooldown handling
* Category cooldown is stored with the spell that started the cooldown (and only resetting cooldown on that spell will clear cooldowns on entire category - this fully mirrors client behavior)
* This significantly reduces the amount of data saved to database for cooldowns
* Spell casts from items that have a different category specified than on spell will now check for cooldown during the cast
(cherry picked from commit 1efb3f08e278530f59d681f676b031a7fc6db3ac)
Closes #15766
Closes #15137
Closes #14837
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/CharacterDatabase.cpp | 8 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.cpp | 7 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStores.h | 1 | ||||
-rw-r--r-- | src/server/game/DataStores/DBCStructure.h | 2 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 9 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 2 | ||||
-rw-r--r-- | src/server/game/Spells/SpellHistory.cpp | 219 | ||||
-rw-r--r-- | src/server/game/Spells/SpellHistory.h | 29 |
10 files changed, 144 insertions, 139 deletions
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 4557dcab996..afc8c783ca6 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/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 = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time, categoryId, categoryEnd 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 " @@ -498,7 +498,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() 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_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_INS_CHAR_SPELL_COOLDOWN, "INSERT INTO character_spell_cooldown (guid, spell, item, time, categoryId, categoryEnd) 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); @@ -578,12 +578,12 @@ 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 casterGuid, spell, effectMask, recalculateMask, 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 = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time, categoryId, categoryEnd 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); PrepareStatement(CHAR_DEL_PET_SPELL_COOLDOWNS, "DELETE FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_BOTH); - PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time) VALUES (?, ?, ?)", CONNECTION_BOTH); + PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time, categoryId, categoryEnd) VALUES (?, ?, ?, ?, ?)", CONNECTION_BOTH); PrepareStatement(CHAR_DEL_PET_SPELL_BY_SPELL, "DELETE FROM pet_spell WHERE guid = ? and spell = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_BOTH); PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, " diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index e268b376b34..14e4631548e 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -163,7 +163,6 @@ DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt); DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt); DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt); DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt); -SpellCategoryStore sSpellsByCategoryStore; PetFamilySpellsStore sPetFamilySpellsStore; DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore(SpellCastTimefmt); @@ -430,12 +429,6 @@ void LoadDBCStores(const std::string& dataPath) LoadDBC(availableDbcLocales, bad_dbc_files, sSkillTiersStore, dbcPath, "SkillTiers.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sSoundEntriesStore, dbcPath, "SoundEntries.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sSpellStore, dbcPath, "Spell.dbc", &CustomSpellEntryfmt, &CustomSpellEntryIndex); - for (uint32 i = 1; i < sSpellStore.GetNumRows(); ++i) - { - SpellEntry const* spell = sSpellStore.LookupEntry(i); - if (spell && spell->Category) - sSpellsByCategoryStore[spell->Category].insert(i); - } for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j) { diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index d955e9581ab..858cd33f8fc 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -165,7 +165,6 @@ extern DBCStorage <SpellDurationEntry> sSpellDurationStore; extern DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore; extern DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore; extern DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore; -extern SpellCategoryStore sSpellsByCategoryStore; extern PetFamilySpellsStore sPetFamilySpellsStore; extern DBCStorage <SpellRadiusEntry> sSpellRadiusStore; extern DBCStorage <SpellRangeEntry> sSpellRangeStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 092ef714145..72317a196cb 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1737,8 +1737,6 @@ struct SpellEntry //uint32 SpellDifficultyId; // 233 3.3.0 }; -typedef std::set<uint32> SpellCategorySet; -typedef std::map<uint32, SpellCategorySet > SpellCategoryStore; typedef std::set<uint32> PetFamilySpellsSet; typedef std::map<uint32, PetFamilySpellsSet > PetFamilySpellsStore; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 5df5fa90578..26f2e912725 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -15912,7 +15912,7 @@ bool Player::GetQuestRewardStatus(uint32 quest_id) const uint16 eventId = sGameEventMgr->GetEventIdForQuest(qInfo); if (m_seasonalquests.find(eventId) != m_seasonalquests.end()) return m_seasonalquests.find(eventId)->second.find(quest_id) != m_seasonalquests.find(eventId)->second.end(); - + return false; } @@ -22723,7 +22723,7 @@ void Player::ApplyEquipCooldown(Item* pItem) continue; // Don't replace longer cooldowns by equip cooldown if we have any. - if (GetSpellHistory()->GetRemainingCooldown(spellData.SpellId) > 30 * IN_MILLISECONDS) + if (GetSpellHistory()->GetRemainingCooldown(sSpellMgr->EnsureSpellInfo(spellData.SpellId)) > 30 * IN_MILLISECONDS) continue; GetSpellHistory()->AddCooldown(spellData.SpellId, pItem->GetEntry(), std::chrono::seconds(30)); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index db352ec80dd..b9547070a76 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2715,15 +2715,6 @@ void ObjectMgr::LoadItemTemplates() TC_LOG_ERROR("sql.sql", "Item (Entry: %u) has broken spell in spellid_%d (%d)", entry, j+1, itemTemplate.Spells[j].SpellId); itemTemplate.Spells[j].SpellId = 0; } - - if (spellInfo && itemTemplate.Spells[j].SpellCategory - && itemTemplate.Spells[j].SpellCategory != SPELL_CATEGORY_FOOD) - { - bool added = sSpellsByCategoryStore[itemTemplate.Spells[j].SpellCategory].insert(itemTemplate.Spells[j].SpellId).second; - if (added) - TC_LOG_DEBUG("sql.sql", "Item(Entry: %u) spellid_%d (%d) category %u added to sSpellsByCategoryStore", - entry, j + 1, itemTemplate.Spells[j].SpellId, itemTemplate.Spells[j].SpellCategory); - } } } } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index e42f66c6ffb..9313301832a 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1487,7 +1487,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b { // 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->GetSpellHistory()->GetRemainingCooldown(aura->GetId()) <= 11) + if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetSpellInfo()) <= 11) break; } else // and add if needed diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index e1302d48d2f..4e98efe10d4 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -4668,7 +4668,7 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_NOT_READY; } - if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo)) + if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo, m_castItemEntry)) { if (m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index ca0e8cc6238..13d1c6f50b2 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -43,6 +43,8 @@ struct SpellHistory::PersistenceHelper<Player> cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[2].GetUInt32())); cooldownEntry->ItemId = fields[1].GetUInt32(); + cooldownEntry->CategoryId = fields[3].GetUInt32(); + cooldownEntry->CategoryEnd = Clock::from_time_t(time_t(fields[4].GetUInt32())); return true; } @@ -51,6 +53,8 @@ struct SpellHistory::PersistenceHelper<Player> stmt->setUInt32(index++, cooldown.first); stmt->setUInt32(index++, cooldown.second.ItemId); stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd))); + stmt->setUInt32(index++, cooldown.second.CategoryId); + stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CategoryEnd))); } }; @@ -70,6 +74,8 @@ struct SpellHistory::PersistenceHelper<Pet> cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[1].GetUInt32())); cooldownEntry->ItemId = 0; + cooldownEntry->CategoryId = fields[2].GetUInt32(); + cooldownEntry->CategoryEnd = Clock::from_time_t(time_t(fields[3].GetUInt32())); return true; } @@ -77,6 +83,8 @@ struct SpellHistory::PersistenceHelper<Pet> { stmt->setUInt32(index++, cooldown.first); stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd))); + stmt->setUInt32(index++, cooldown.second.CategoryId); + stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CategoryEnd))); } }; @@ -92,7 +100,11 @@ void SpellHistory::LoadFromDB(PreparedQueryResult cooldownsResult) uint32 spellId; CooldownEntry cooldown; if (StatementInfo::ReadCooldown(cooldownsResult->Fetch(), &spellId, &cooldown)) + { _spellCooldowns[spellId] = cooldown; + if (cooldown.CategoryId) + _categoryCooldowns[cooldown.CategoryId] = &_spellCooldowns[spellId]; + } } while (cooldownsResult->NextRow()); } @@ -125,10 +137,18 @@ void SpellHistory::Update() { SQLTransaction t; Clock::time_point now = Clock::now(); + for (auto itr = _categoryCooldowns.begin(); itr != _categoryCooldowns.end();) + { + if (itr->second->CategoryEnd < now) + itr = _categoryCooldowns.erase(itr); + else + ++itr; + } + for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();) { if (itr->second.CooldownEnd < now) - itr = _spellCooldowns.erase(itr); + itr = EraseCooldown(itr); else ++itr; } @@ -136,29 +156,37 @@ void SpellHistory::Update() void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell /*= nullptr*/) { + HandleCooldowns(spellInfo, item ? item->GetEntry() : 0, spell); +} + +void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell /*= nullptr*/) +{ if (Player* player = _owner->ToPlayer()) { // potions start cooldown until exiting combat - if (item && (item->IsPotion() || spellInfo->IsCooldownStartedOnEvent())) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemID)) { - player->SetLastPotionId(item->GetEntry()); - return; + if (itemTemplate->IsPotion() || spellInfo->IsCooldownStartedOnEvent()) + { + player->SetLastPotionId(itemID); + return; + } } } if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive() || (spell && spell->IsIgnoringCooldowns())) return; - StartCooldown(spellInfo, item ? item->GetEntry() : 0, spell); + StartCooldown(spellInfo, itemID, spell); } -bool SpellHistory::IsReady(SpellInfo const* spellInfo) const +bool SpellHistory::IsReady(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const { if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) if (IsSchoolLocked(spellInfo->GetSchoolMask())) return false; - if (HasCooldown(spellInfo->Id)) + if (HasCooldown(spellInfo->Id, itemId)) return false; return true; @@ -168,42 +196,33 @@ 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 + packet << uint32(spellCooldown.first); // spell ID + packet << uint16(spellCooldown.second.CategoryId); // 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) + std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now); + if (cooldownDuration.count() <= 0) { packet << uint32(0); packet << uint32(0); continue; } - if (spellInfo->GetCategory()) + std::chrono::milliseconds categoryDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CategoryEnd - now); + if (categoryDuration.count() > 0) { packet << uint32(0); - packet << uint32(cooldownDuration); + packet << uint32(categoryDuration.count()); } else { - packet << uint32(cooldownDuration); + packet << uint32(cooldownDuration.count()); packet << uint32(0); } } @@ -216,23 +235,13 @@ 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); + packet << uint16(_spellCooldowns.size()); 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 + packet << uint16(spellCooldown.second.CategoryId); // spell category // send infinity cooldown in special format if (spellCooldown.second.CooldownEnd >= infTime) @@ -242,21 +251,26 @@ void SpellHistory::WritePacket<Player>(WorldPacket& packet) const continue; } - uint32 cooldownDuration = spellCooldown.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now).count() : 0; + std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now); + if (cooldownDuration.count() <= 0) + { + packet << uint32(0); + packet << uint32(0); + continue; + } - if (sEntry->GetCategory()) // may be wrong, but anyway better than nothing... + std::chrono::milliseconds categoryDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CategoryEnd - now); + if (categoryDuration.count() >= 0) { packet << uint32(0); // cooldown - packet << uint32(cooldownDuration); // category cooldown + packet << uint32(categoryDuration.count()); // category cooldown } else { - packet << uint32(cooldownDuration); // cooldown + packet << uint32(cooldownDuration.count()); // 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*/) @@ -351,7 +365,7 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel // self spell cooldown if (recTime != curTime) { - AddCooldown(spellInfo->Id, itemId, recTime, onHold); + AddCooldown(spellInfo->Id, itemId, recTime, categoryId, catrecTime, onHold); if (needsCooldownPacket) { @@ -363,22 +377,6 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel } } } - - // 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*/) @@ -387,49 +385,27 @@ void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId / if (startCooldown) StartCooldown(spellInfo, itemId, spell); + // Send activate cooldown timer (possible 0) at client side 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*/) +void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold /*= false*/) { CooldownEntry& cooldownEntry = _spellCooldowns[spellId]; cooldownEntry.CooldownEnd = cooldownEnd; cooldownEntry.ItemId = itemId; + cooldownEntry.CategoryId = categoryId; + cooldownEntry.CategoryEnd = categoryEnd; cooldownEntry.OnHold = onHold; + + if (categoryId) + _categoryCooldowns[categoryId] = &cooldownEntry; } void SpellHistory::ModifyCooldown(uint32 spellId, int32 cooldownModMs) @@ -443,7 +419,7 @@ void SpellHistory::ModifyCooldown(uint32 spellId, int32 cooldownModMs) if (itr->second.CooldownEnd + offset > now) itr->second.CooldownEnd += offset; else - _spellCooldowns.erase(itr); + EraseCooldown(itr); if (Player* playerOwner = GetPlayerOwner()) { @@ -477,7 +453,7 @@ void SpellHistory::ResetCooldown(CooldownStorageType::iterator& itr, bool update } } - itr = _spellCooldowns.erase(itr); + itr = EraseCooldown(itr); } void SpellHistory::ResetAllCooldowns() @@ -492,31 +468,69 @@ void SpellHistory::ResetAllCooldowns() SendClearCooldowns(cooldowns); } + _categoryCooldowns.clear(); _spellCooldowns.clear(); } -bool SpellHistory::HasCooldown(uint32 spellId) const +bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const { - return _spellCooldowns.count(spellId) != 0; + if (_spellCooldowns.count(spellInfo->Id) != 0) + return true; + + uint32 category = 0; + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId)) + { + for (uint32 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i) + { + if (itemTemplate->Spells[i].SpellId == spellInfo->Id) + { + category = itemTemplate->Spells[i].SpellCategory; + break; + } + } + } + + if (!category) + category = spellInfo->GetCategory(); + + if (!category) + return false; + + return _categoryCooldowns.count(category) != 0; } -uint32 SpellHistory::GetRemainingCooldown(uint32 spellId) const +bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/) const { - auto itr = _spellCooldowns.find(spellId); - if (itr == _spellCooldowns.end()) - return 0; + return HasCooldown(sSpellMgr->EnsureSpellInfo(spellId), itemId); +} + +uint32 SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const +{ + Clock::time_point end; + auto itr = _spellCooldowns.find(spellInfo->Id); + if (itr != _spellCooldowns.end()) + end = itr->second.CooldownEnd; + else + { + auto catItr = _categoryCooldowns.find(spellInfo->GetCategory()); + if (catItr == _categoryCooldowns.end()) + return 0; + + end = catItr->second->CategoryEnd; + } Clock::time_point now = Clock::now(); - if (itr->second.CooldownEnd < now) + if (end < now) return 0; - Clock::duration remaining = itr->second.CooldownEnd - now; + Clock::duration remaining = end - 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)); + Clock::time_point now = Clock::now(); + Clock::time_point lockoutEnd = 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; @@ -553,10 +567,10 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE) continue; - if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellId) < lockoutTime) + if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellInfo) < lockoutTime) { cooldowns[spellId] = lockoutTime; - AddCooldown(spellId, 0, lockoutEnd); + AddCooldown(spellId, 0, lockoutEnd, 0, now); } } @@ -644,6 +658,7 @@ void SpellHistory::SaveCooldownStateBeforeDuel() void SpellHistory::RestoreCooldownStateAfterDuel() { + // category cooldows are not preserved. if (Player* player = _owner->ToPlayer()) { // add all profession CDs created while in duel (if any) diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h index 86737ec7da9..572f407da96 100644 --- a/src/server/game/Spells/SpellHistory.h +++ b/src/server/game/Spells/SpellHistory.h @@ -38,15 +38,15 @@ public: 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; + uint32 ItemId = 0; + uint32 CategoryId = 0; + Clock::time_point CategoryEnd; + bool OnHold = false; }; typedef std::unordered_map<uint32 /*spellId*/, CooldownEntry> CooldownStorageType; + typedef std::unordered_map<uint32 /*categoryId*/, CooldownEntry*> CategoryCooldownStorageType; typedef std::unordered_map<uint32 /*categoryId*/, Clock::time_point> GlobalCooldownStorageType; explicit SpellHistory(Unit* owner) : _owner(owner), _schoolLockouts() { } @@ -60,7 +60,8 @@ public: void Update(); void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr); - bool IsReady(SpellInfo const* spellInfo) const; + void HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell = nullptr); + bool IsReady(SpellInfo const* spellInfo, uint32 itemId = 0) const; template<class OwnerType> void WritePacket(WorldPacket& packet) const; @@ -74,10 +75,11 @@ public: 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)); + Clock::time_point now = Clock::now(); + AddCooldown(spellId, itemId, now + std::chrono::duration_cast<Clock::duration>(cooldownDuration), 0, now); } - void AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, bool onHold = false); + void AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold = false); void ModifyCooldown(uint32 spellId, int32 cooldownModMs); void ResetCooldown(uint32 spellId, bool update = false); void ResetCooldown(CooldownStorageType::iterator& itr, bool update = false); @@ -102,8 +104,9 @@ public: } void ResetAllCooldowns(); - bool HasCooldown(uint32 spellId) const; - uint32 GetRemainingCooldown(uint32 spellId) const; + bool HasCooldown(SpellInfo const* spellInfo, uint32 itemId = 0) const; + bool HasCooldown(uint32 spellId, uint32 itemId = 0) const; + uint32 GetRemainingCooldown(SpellInfo const* spellInfo) const; // School lockouts void LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime); @@ -123,6 +126,11 @@ public: private: Player* GetPlayerOwner() const; void SendClearCooldowns(std::vector<int32> const& cooldowns) const; + CooldownStorageType::iterator EraseCooldown(CooldownStorageType::iterator itr) + { + _categoryCooldowns.erase(itr->second.CategoryId); + return _spellCooldowns.erase(itr); + } typedef std::unordered_map<uint32, uint32> PacketCooldowns; void BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns) const; @@ -130,6 +138,7 @@ private: Unit* _owner; CooldownStorageType _spellCooldowns; CooldownStorageType _spellCooldownsBeforeDuel; + CategoryCooldownStorageType _categoryCooldowns; Clock::time_point _schoolLockouts[MAX_SPELL_SCHOOL]; GlobalCooldownStorageType _globalCooldowns; |