TrinityCore
Loading...
Searching...
No Matches
SpellHistory.cpp
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include "SpellHistory.h"
19#include "DatabaseEnv.h"
20#include "Item.h"
21#include "ObjectMgr.h"
22#include "Opcodes.h"
23#include "Pet.h"
24#include "Player.h"
25#include "Spell.h"
26#include "SpellInfo.h"
27#include "SpellMgr.h"
28#include "SpellPackets.h"
29#include "WorldPacket.h"
30
31SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelay = std::chrono::duration_cast<SpellHistory::Clock::duration>(std::chrono::seconds(MONTH));
32SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelayCheck = std::chrono::duration_cast<SpellHistory::Clock::duration>(std::chrono::seconds(MONTH / 2));
33
34template<>
36{
37 static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_CHAR_SPELL_COOLDOWNS;
38 static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_CHAR_SPELL_COOLDOWN;
39
40 static void SetIdentifier(PreparedStatementBase* stmt, uint8 index, Unit* owner) { stmt->setUInt32(index, owner->GetGUID().GetCounter()); }
41
42 static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry)
43 {
44 *spellId = fields[0].GetUInt32();
45 if (!sSpellMgr->GetSpellInfo(*spellId))
46 return false;
47
48 cooldownEntry->SpellId = *spellId;
49 cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[2].GetUInt32()));
50 cooldownEntry->ItemId = fields[1].GetUInt32();
51 cooldownEntry->CategoryId = fields[3].GetUInt32();
52 cooldownEntry->CategoryEnd = Clock::from_time_t(time_t(fields[4].GetUInt32()));
53 return true;
54 }
55
56 static void WriteCooldown(PreparedStatementBase* stmt, uint8& index, CooldownStorageType::value_type const& cooldown)
57 {
58 stmt->setUInt32(index++, cooldown.first);
59 stmt->setUInt32(index++, cooldown.second.ItemId);
60 stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd)));
61 stmt->setUInt32(index++, cooldown.second.CategoryId);
62 stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CategoryEnd)));
63 }
64};
65
66template<>
68{
69 static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_PET_SPELL_COOLDOWNS;
70 static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_PET_SPELL_COOLDOWN;
71
72 static void SetIdentifier(PreparedStatementBase* stmt, uint8 index, Unit* owner) { stmt->setUInt32(index, owner->GetCharmInfo()->GetPetNumber()); }
73
74 static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry)
75 {
76 *spellId = fields[0].GetUInt32();
77 if (!sSpellMgr->GetSpellInfo(*spellId))
78 return false;
79
80 cooldownEntry->SpellId = *spellId;
81 cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[1].GetUInt32()));
82 cooldownEntry->ItemId = 0;
83 cooldownEntry->CategoryId = fields[2].GetUInt32();
84 cooldownEntry->CategoryEnd = Clock::from_time_t(time_t(fields[3].GetUInt32()));
85 return true;
86 }
87
88 static void WriteCooldown(PreparedStatementBase* stmt, uint8& index, CooldownStorageType::value_type const& cooldown)
89 {
90 stmt->setUInt32(index++, cooldown.first);
91 stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd)));
92 stmt->setUInt32(index++, cooldown.second.CategoryId);
93 stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CategoryEnd)));
94 }
95};
96
97template<class OwnerType>
99{
100 typedef PersistenceHelper<OwnerType> StatementInfo;
101
102 if (cooldownsResult)
103 {
104 do
105 {
106 uint32 spellId;
107 CooldownEntry cooldown;
108 if (StatementInfo::ReadCooldown(cooldownsResult->Fetch(), &spellId, &cooldown))
109 {
110 _spellCooldowns[spellId] = cooldown;
111 if (cooldown.CategoryId)
112 _categoryCooldowns[cooldown.CategoryId] = &_spellCooldowns[spellId];
113 }
114
115 } while (cooldownsResult->NextRow());
116 }
117}
118
119template<class OwnerType>
121{
122 typedef PersistenceHelper<OwnerType> StatementInfo;
123
124 uint8 index = 0;
125 CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsDeleteStatement);
126 StatementInfo::SetIdentifier(stmt, index++, _owner);
127 trans->Append(stmt);
128
129 for (auto const& p : _spellCooldowns)
130 {
131 if (!p.second.OnHold)
132 {
133 index = 0;
134 stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsInsertStatement);
135 StatementInfo::SetIdentifier(stmt, index++, _owner);
136 StatementInfo::WriteCooldown(stmt, index, p);
137 trans->Append(stmt);
138 }
139 }
140}
141
143{
144 Clock::time_point now = GameTime::GetSystemTime();
145 for (auto itr = _categoryCooldowns.begin(); itr != _categoryCooldowns.end();)
146 {
147 if (itr->second->CategoryEnd < now)
148 itr = _categoryCooldowns.erase(itr);
149 else
150 ++itr;
151 }
152
153 for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();)
154 {
155 if (itr->second.CooldownEnd < now)
156 itr = EraseCooldown(itr);
157 else
158 ++itr;
159 }
160}
161
162void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell /*= nullptr*/)
163{
164 HandleCooldowns(spellInfo, item ? item->GetEntry() : 0, spell);
165}
166
167void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell /*= nullptr*/)
168{
169 if (spell && spell->IsIgnoringCooldowns())
170 return;
171
172 if (Player* player = _owner->ToPlayer())
173 {
174 // potions start cooldown until exiting combat
175 if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemID))
176 {
177 if (itemTemplate->IsPotion() || spellInfo->IsCooldownStartedOnEvent())
178 {
179 player->SetLastPotionId(itemID);
180 return;
181 }
182 }
183 }
184
185 if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive())
186 return;
187
188 StartCooldown(spellInfo, itemID, spell);
189}
190
191bool SpellHistory::IsReady(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const
192{
194 if (IsSchoolLocked(spellInfo->GetSchoolMask()))
195 return false;
196
197 if (HasCooldown(spellInfo->Id, itemId, ignoreCategoryCooldown))
198 return false;
199
200 return true;
201}
202
203template<>
204void SpellHistory::WritePacket<Pet>(WorldPacket& packet) const
205{
206 Clock::time_point now = GameTime::GetSystemTime();
207
208 uint8 cooldownsCount = uint8(_spellCooldowns.size());
209 packet << uint8(cooldownsCount);
210
211 for (auto const& spellCooldown : _spellCooldowns)
212 {
213 packet << uint32(spellCooldown.first); // spell ID
214 packet << uint16(spellCooldown.second.CategoryId); // spell category
215 if (!spellCooldown.second.OnHold)
216 {
217 std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CooldownEnd - now);
218 if (cooldownDuration.count() <= 0)
219 {
220 packet << uint32(0);
221 packet << uint32(0);
222 continue;
223 }
224
225 std::chrono::milliseconds categoryDuration = std::chrono::duration_cast<std::chrono::milliseconds>(spellCooldown.second.CategoryEnd - now);
226 if (categoryDuration.count() > 0)
227 {
228 packet << uint32(0);
229 packet << uint32(categoryDuration.count());
230 }
231 else
232 {
233 packet << uint32(cooldownDuration.count());
234 packet << uint32(0);
235 }
236 }
237 }
238}
239
241{
242 Clock::time_point now = GameTime::GetSystemTime();
243
244 for (auto const& spellCooldown : _spellCooldowns)
245 {
247 historyEntry.SpellID = spellCooldown.first;
248 historyEntry.ItemID = spellCooldown.second.ItemId;
249 historyEntry.Category = spellCooldown.second.CategoryId;
250
251 if (spellCooldown.second.OnHold)
252 historyEntry.OnHold = true;
253 else
254 {
255 Milliseconds cooldownDuration = duration_cast<Milliseconds>(spellCooldown.second.CooldownEnd - now);
256 if (cooldownDuration <= 0ms)
257 continue;
258
259 Milliseconds categoryDuration = duration_cast<Milliseconds>(spellCooldown.second.CategoryEnd - now);
260 if (categoryDuration >= 0ms)
261 historyEntry.CategoryRecoveryTime = categoryDuration.count();
262
263 if (cooldownDuration > categoryDuration)
264 historyEntry.RecoveryTime = cooldownDuration.count();
265 }
266
267 initialSpells->SpellHistory.push_back(historyEntry);
268 }
269}
270
271void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/)
272{
273 // init cooldown values
274 uint32 categoryId = 0;
275 int32 cooldown = -1;
276 int32 categoryCooldown = -1;
277
278 GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown);
279
280 Clock::time_point curTime = GameTime::GetSystemTime();
281 Clock::time_point catrecTime;
282 Clock::time_point recTime;
283 bool needsCooldownPacket = false;
284
285 // overwrite time for selected category
286 if (onHold)
287 {
288 // use +MONTH as infinite cooldown marker
289 catrecTime = categoryCooldown > 0 ? (curTime + InfinityCooldownDelay) : curTime;
290 recTime = cooldown > 0 ? (curTime + InfinityCooldownDelay) : catrecTime;
291 }
292 else
293 {
294 // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK)
295 // prevent 0 cooldowns set by another way
296 if (cooldown <= 0 && categoryCooldown <= 0 && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75)))
298
299 // Now we have cooldown data (if found any), time to apply mods
300 if (Player* modOwner = _owner->GetSpellModOwner())
301 {
302 if (cooldown >= 0)
303 modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell);
304
305 if (categoryCooldown >= 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS))
306 modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell);
307 }
308
310 {
311 // Apply SPELL_AURA_MOD_COOLDOWN only to own spells
312 Player* playerOwner = GetPlayerOwner();
313 if (!playerOwner || playerOwner->HasSpell(spellInfo->Id))
314 {
315 needsCooldownPacket = true;
316 cooldown += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
317 }
318 }
319
320 // replace negative cooldowns by 0
321 if (cooldown < 0)
322 cooldown = 0;
323
324 if (categoryCooldown < 0)
325 categoryCooldown = 0;
326
327 // no cooldown after applying spell mods
328 if (cooldown == 0 && categoryCooldown == 0)
329 return;
330
331 catrecTime = categoryCooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(categoryCooldown)) : curTime;
332 recTime = cooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldown)) : catrecTime;
333 }
334
335 // self spell cooldown
336 if (recTime != curTime)
337 {
338 AddCooldown(spellInfo->Id, itemId, recTime, categoryId, catrecTime, onHold);
339
340 if (needsCooldownPacket)
341 {
342 if (Player* playerOwner = GetPlayerOwner())
343 {
344 WorldPacket spellCooldown;
345 BuildCooldownPacket(spellCooldown, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, cooldown);
346 playerOwner->SendDirectMessage(&spellCooldown);
347 }
348 }
349 }
350}
351
352void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= nullptr*/, bool startCooldown /*= true*/)
353{
354 // Send activate cooldown timer (possible 0) at client side
355 if (Player* player = GetPlayerOwner())
356 {
357 uint32 category = spellInfo->GetCategory();
358 GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
359
360 auto categoryItr = _categoryCooldowns.find(category);
361 if (categoryItr != _categoryCooldowns.end() && categoryItr->second->SpellId != spellInfo->Id)
362 {
363 WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
364 data << uint32(categoryItr->second->SpellId);
365 data << _owner->GetGUID();
366 player->SendDirectMessage(&data);
367
368 if (startCooldown)
369 StartCooldown(sSpellMgr->AssertSpellInfo(categoryItr->second->SpellId), itemId, spell);
370 }
371
372 WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
373 data << uint32(spellInfo->Id);
374 data << _owner->GetGUID();
375 player->SendDirectMessage(&data);
376 }
377
378 // start cooldowns at server side, if any
379 if (startCooldown)
380 StartCooldown(spellInfo, itemId, spell);
381}
382
383void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold /*= false*/)
384{
385 CooldownEntry& cooldownEntry = _spellCooldowns[spellId];
386 cooldownEntry.SpellId = spellId;
387 cooldownEntry.CooldownEnd = cooldownEnd;
388 cooldownEntry.ItemId = itemId;
389 cooldownEntry.CategoryId = categoryId;
390 cooldownEntry.CategoryEnd = categoryEnd;
391 cooldownEntry.OnHold = onHold;
392
393 if (categoryId)
394 _categoryCooldowns[categoryId] = &cooldownEntry;
395}
396
397void SpellHistory::ModifyCooldown(uint32 spellId, int32 cooldownModMs)
398{
399 auto itr = _spellCooldowns.find(spellId);
400 if (!cooldownModMs || itr == _spellCooldowns.end())
401 return;
402
403 Clock::time_point now = GameTime::GetSystemTime();
404 Clock::duration offset = std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldownModMs));
405 if (itr->second.CooldownEnd + offset > now)
406 itr->second.CooldownEnd += offset;
407 else
408 EraseCooldown(itr);
409
410 if (Player* playerOwner = GetPlayerOwner())
411 {
412 WorldPacket modifyCooldown(SMSG_MODIFY_COOLDOWN, 4 + 8 + 4);
413 modifyCooldown << uint32(spellId);
414 modifyCooldown << _owner->GetGUID();
415 modifyCooldown << int32(cooldownModMs);
416 playerOwner->SendDirectMessage(&modifyCooldown);
417 }
418}
419
420void SpellHistory::ResetCooldown(uint32 spellId, bool update /*= false*/)
421{
422 auto itr = _spellCooldowns.find(spellId);
423 if (itr == _spellCooldowns.end())
424 return;
425
426 ResetCooldown(itr, update);
427}
428
429void SpellHistory::ResetCooldown(CooldownStorageType::iterator& itr, bool update /*= false*/)
430{
431 if (update)
432 {
433 if (Player* playerOwner = GetPlayerOwner())
434 {
435 WorldPacket data(SMSG_CLEAR_COOLDOWN, 4 + 8);
436 data << uint32(itr->first);
437 data << _owner->GetGUID();
438 playerOwner->SendDirectMessage(&data);
439 }
440 }
441
442 itr = EraseCooldown(itr);
443}
444
446{
447 if (GetPlayerOwner())
448 {
449 std::vector<int32> cooldowns;
450 cooldowns.reserve(_spellCooldowns.size());
451 for (auto const& p : _spellCooldowns)
452 cooldowns.push_back(p.first);
453
454 SendClearCooldowns(cooldowns);
455 }
456
457 _categoryCooldowns.clear();
458 _spellCooldowns.clear();
459}
460
461bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const
462{
463 if (_spellCooldowns.count(spellInfo->Id) != 0)
464 return true;
465
466 if (ignoreCategoryCooldown)
467 return false;
468
469 uint32 category = 0;
470 GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
471 if (!category)
472 return false;
473
474 return _categoryCooldowns.count(category) != 0;
475}
476
477bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const
478{
479 return HasCooldown(sSpellMgr->AssertSpellInfo(spellId), itemId, ignoreCategoryCooldown);
480}
481
483{
484 // TODO: Delete this function and make SpellHistory::IsReady return enum with reason instead of bool
485 auto itr = _spellCooldowns.find(spellId);
486 return itr != _spellCooldowns.end() && itr->second.OnHold;
487}
488
490{
491 Clock::time_point end;
492 auto itr = _spellCooldowns.find(spellInfo->Id);
493 if (itr != _spellCooldowns.end())
494 end = itr->second.CooldownEnd;
495 else
496 {
497 auto catItr = _categoryCooldowns.find(spellInfo->GetCategory());
498 if (catItr == _categoryCooldowns.end())
499 return 0;
500
501 end = catItr->second->CategoryEnd;
502 }
503
504 Clock::time_point now = GameTime::GetSystemTime();
505 if (end < now)
506 return 0;
507
508 Clock::duration remaining = end - now;
509 return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count());
510}
511
513{
514 Clock::time_point now = GameTime::GetSystemTime();
515 Clock::time_point lockoutEnd = now + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(lockoutTime));
516 for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
517 if (SpellSchoolMask(1 << i) & schoolMask)
518 _schoolLockouts[i] = lockoutEnd;
519
520 std::set<uint32> knownSpells;
521 if (Player* plrOwner = _owner->ToPlayer())
522 {
523 for (auto const& p : plrOwner->GetSpellMap())
524 if (p.second.state != PLAYERSPELL_REMOVED)
525 knownSpells.insert(p.first);
526 }
527 else if (Pet* petOwner = _owner->ToPet())
528 {
529 for (auto const& p : petOwner->m_spells)
530 if (p.second.state != PETSPELL_REMOVED)
531 knownSpells.insert(p.first);
532 }
533 else
534 {
535 Creature* creatureOwner = _owner->ToCreature();
536 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
537 if (creatureOwner->m_spells[i])
538 knownSpells.insert(creatureOwner->m_spells[i]);
539 }
540
541 PacketCooldowns cooldowns;
542 WorldPacket spellCooldowns;
543 for (uint32 spellId : knownSpells)
544 {
545 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId);
546 if (spellInfo->IsCooldownStartedOnEvent())
547 continue;
548
550 continue;
551
552 if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellInfo) < lockoutTime)
553 {
554 cooldowns[spellId] = lockoutTime;
555 AddCooldown(spellId, 0, lockoutEnd, 0, now);
556 }
557 }
558
559 if (Player* player = GetPlayerOwner())
560 {
561 if (!cooldowns.empty())
562 {
563 BuildCooldownPacket(spellCooldowns, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
564 player->SendDirectMessage(&spellCooldowns);
565 }
566 }
567}
568
570{
571 Clock::time_point now = GameTime::GetSystemTime();
572 for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
573 if (SpellSchoolMask(1 << i) & schoolMask)
574 if (_schoolLockouts[i] > now)
575 return true;
576
577 return false;
578}
579
580bool SpellHistory::HasGlobalCooldown(SpellInfo const* spellInfo) const
581{
582 auto itr = _globalCooldowns.find(spellInfo->StartRecoveryCategory);
583 return itr != _globalCooldowns.end() && itr->second > GameTime::GetSystemTime();
584}
585
586void SpellHistory::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration)
587{
588 _globalCooldowns[spellInfo->StartRecoveryCategory] = GameTime::GetSystemTime() + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(duration));
589}
590
592{
593 _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::time_point(Clock::duration(0));
594}
595
600
601void SpellHistory::SendClearCooldowns(std::vector<int32> const& cooldowns) const
602{
603 if (Player* playerOwner = GetPlayerOwner())
604 {
605 for (int32 spell : cooldowns)
606 {
607 WorldPacket data(SMSG_CLEAR_COOLDOWN, 4 + 8);
608 data << uint32(spell);
609 data << _owner->GetGUID();
610 playerOwner->SendDirectMessage(&data);
611 }
612 }
613}
614
616{
617 data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + 4 + 4);
618 data << _owner->GetGUID();
619 data << uint8(flags);
620 data << uint32(spellId);
621 data << uint32(cooldown);
622}
623
625{
626 data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + (4 + 4) * cooldowns.size());
627 data << _owner->GetGUID();
628 data << uint8(flags);
629 for (auto const& cooldown : cooldowns)
630 {
631 data << cooldown.first;
632 data << cooldown.second;
633 }
634}
635
636void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown)
637{
638 ASSERT(cooldown || categoryId || categoryCooldown);
639 int32 tmpCooldown = -1;
640 uint32 tmpCategoryId = 0;
641 int32 tmpCategoryCooldown = -1;
642
643 // some special item spells without correct cooldown in SpellInfo
644 // cooldown information stored in item prototype
645 if (itemId)
646 {
647 if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
648 {
649 for (ItemEffect const& itemEffect : proto->Effects)
650 {
651 if (uint32(itemEffect.SpellID) == spellInfo->Id)
652 {
653 tmpCooldown = itemEffect.CoolDownMSec;
654 tmpCategoryId = itemEffect.SpellCategoryID;
655 tmpCategoryCooldown = itemEffect.CategoryCoolDownMSec;
656 break;
657 }
658 }
659 }
660 }
661
662 // if no cooldown found above then base at DBC data
663 if (tmpCooldown < 0 && tmpCategoryCooldown < 0)
664 {
665 tmpCooldown = spellInfo->RecoveryTime;
666 tmpCategoryId = spellInfo->GetCategory();
667 tmpCategoryCooldown = spellInfo->CategoryRecoveryTime;
668 }
669
670 if (cooldown)
671 *cooldown = tmpCooldown;
672 if (categoryId)
673 *categoryId = tmpCategoryId;
674 if (categoryCooldown)
675 *categoryCooldown = tmpCategoryCooldown;
676}
677
682
684{
685 // category cooldows are not preserved.
686 if (Player* player = _owner->ToPlayer())
687 {
688 // add all profession CDs created while in duel (if any)
689 for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
690 {
691 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
692
693 if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS ||
694 spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS)
695 _spellCooldownsBeforeDuel[itr->first] = _spellCooldowns[itr->first];
696 }
697
698 // check for spell with onHold active before and during the duel
699 for (auto itr = _spellCooldownsBeforeDuel.begin(); itr != _spellCooldownsBeforeDuel.end(); ++itr)
700 {
701 if (!itr->second.OnHold && !_spellCooldowns[itr->first].OnHold)
702 _spellCooldowns[itr->first] = _spellCooldownsBeforeDuel[itr->first];
703 }
704
705 // update the client: restore old cooldowns
706 PacketCooldowns cooldowns;
707
708 for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
709 {
710 Clock::time_point now = GameTime::GetSystemTime();
711 uint32 cooldownDuration = itr->second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(itr->second.CooldownEnd - now).count() : 0;
712
713 // cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs
714 if (cooldownDuration <= 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS || itr->second.OnHold)
715 continue;
716
717 cooldowns[itr->first] = cooldownDuration;
718 }
719
720 WorldPacket data;
722 player->SendDirectMessage(&data);
723 }
724}
725
726template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult);
727template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult);
728template void SpellHistory::SaveToDB<Player>(CharacterDatabaseTransaction trans);
729template void SpellHistory::SaveToDB<Pet>(CharacterDatabaseTransaction trans);
CharacterDatabaseStatements
@ CHAR_INS_PET_SPELL_COOLDOWN
@ CHAR_DEL_CHAR_SPELL_COOLDOWNS
@ CHAR_INS_CHAR_SPELL_COOLDOWN
@ CHAR_DEL_PET_SPELL_COOLDOWNS
@ IN_MILLISECONDS
Definition Common.h:35
@ MINUTE
Definition Common.h:29
@ MONTH
Definition Common.h:33
static const uint32 MAX_CREATURE_SPELLS
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
uint8_t uint8
Definition Define.h:135
int32_t int32
Definition Define.h:129
uint16_t uint16
Definition Define.h:134
uint32_t uint32
Definition Define.h:133
uint16 flags
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition Duration.h:24
#define ASSERT
Definition Errors.h:68
#define sObjectMgr
Definition ObjectMgr.h:1721
@ PETSPELL_REMOVED
Definition PetDefines.h:60
@ PLAYERSPELL_REMOVED
Definition Player.h:153
@ MAX_SPELL_SCHOOL
@ SPELL_PREVENTION_TYPE_SILENCE
SpellSchoolMask
@ RANGED_ATTACK
@ SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS
@ SPELL_AURA_MOD_COOLDOWN
@ SPELLMOD_COOLDOWN
@ SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS
Starts GCD for spells that should start their cooldown on events, requires SPELL_COOLDOWN_FLAG_INCLUD...
@ SPELL_COOLDOWN_FLAG_NONE
#define sSpellMgr
Definition SpellMgr.h:738
uint32 m_spells[MAX_CREATURE_SPELLS]
Definition Creature.h:238
Class used to access individual fields of database query result.
Definition Field.h:92
uint32 GetUInt32() const
Definition Field.cpp:61
Definition Item.h:62
LowType GetCounter() const
Definition ObjectGuid.h:156
static Creature * ToCreature(Object *o)
Definition Object.h:189
uint32 GetEntry() const
Definition Object.h:81
static ObjectGuid GetGUID(Object const *o)
Definition Object.h:78
static Player * ToPlayer(Object *o)
Definition Object.h:183
Definition Pet.h:40
bool HasSpell(uint32 spell) const override
Definition Player.cpp:3881
void setUInt32(uint8 index, uint32 value)
static Clock::duration const InfinityCooldownDelay
void LoadFromDB(PreparedQueryResult cooldownsResult)
GlobalCooldownStorageType _globalCooldowns
bool IsReady(SpellInfo const *spellInfo, uint32 itemId=0, bool ignoreCategoryCooldown=false) const
bool HasGlobalCooldown(SpellInfo const *spellInfo) const
void SendCooldownEvent(SpellInfo const *spellInfo, uint32 itemId=0, Spell *spell=nullptr, bool startCooldown=true)
void ResetCooldown(uint32 spellId, bool update=false)
void SaveToDB(CharacterDatabaseTransaction trans)
void SendClearCooldowns(std::vector< int32 > const &cooldowns) const
static Clock::duration const InfinityCooldownDelayCheck
void AddCooldown(uint32 spellId, uint32 itemId, std::chrono::duration< Type, Period > cooldownDuration)
void ResetAllCooldowns()
CooldownStorageType _spellCooldowns
uint32 GetRemainingCooldown(SpellInfo const *spellInfo) const
void AddGlobalCooldown(SpellInfo const *spellInfo, uint32 duration)
bool HasCooldownOnHold(uint32 spellId) const
void CancelGlobalCooldown(SpellInfo const *spellInfo)
void WritePacket(WorldPacket &packet) const
void RestoreCooldownStateAfterDuel()
static void GetCooldownDurations(SpellInfo const *spellInfo, uint32 itemId, int32 *cooldown, uint32 *categoryId, int32 *categoryCooldown)
CooldownStorageType _spellCooldownsBeforeDuel
void SaveCooldownStateBeforeDuel()
Clock::time_point _schoolLockouts[MAX_SPELL_SCHOOL]
std::unordered_map< uint32, uint32 > PacketCooldowns
void ModifyCooldown(uint32 spellId, int32 cooldownModMs)
CooldownStorageType::iterator EraseCooldown(CooldownStorageType::iterator itr)
void HandleCooldowns(SpellInfo const *spellInfo, Item const *item, Spell *spell=nullptr)
bool HasCooldown(SpellInfo const *spellInfo, uint32 itemId=0, bool ignoreCategoryCooldown=false) const
void LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime)
void StartCooldown(SpellInfo const *spellInfo, uint32 itemId, Spell *spell=nullptr, bool onHold=false)
Player * GetPlayerOwner() const
void BuildCooldownPacket(WorldPacket &data, uint8 flags, uint32 spellId, uint32 cooldown) const
bool IsSchoolLocked(SpellSchoolMask schoolMask) const
CategoryCooldownStorageType _categoryCooldowns
uint32 PreventionType
Definition SpellInfo.h:359
uint32 GetCategory() const
uint32 RecoveryTime
Definition SpellInfo.h:317
bool IsCooldownStartedOnEvent() const
bool IsPassive() const
uint32 Id
Definition SpellInfo.h:289
uint32 CategoryRecoveryTime
Definition SpellInfo.h:318
SpellSchoolMask GetSchoolMask() const
bool HasAttribute(SpellAttr0 attribute) const
Definition SpellInfo.h:375
bool IsAutoRepeatRangedSpell() const
uint32 StartRecoveryCategory
Definition SpellInfo.h:319
Definition Spell.h:152
bool IsIgnoringCooldowns() const
Definition Spell.cpp:7508
Definition Unit.h:783
Pet * ToPet()
Definition Unit.h:1804
CharmInfo * GetCharmInfo()
Definition Unit.h:1300
uint32 GetAttackTime(WeaponAttackType att) const
Definition Unit.cpp:8411
int32 GetTotalAuraModifier(AuraType auraType) const
Definition Unit.cpp:4815
Player * GetSpellModOwner() const
Definition Object.cpp:2224
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Object.cpp:2204
void Initialize(uint16 opcode, size_t newres=200)
Definition WorldPacket.h:51
std::vector< SpellHistoryEntry > SpellHistory
@ SMSG_COOLDOWN_EVENT
Definition Opcodes.h:338
@ SMSG_MODIFY_COOLDOWN
Definition Opcodes.h:1198
@ SMSG_SPELL_COOLDOWN
Definition Opcodes.h:337
@ SMSG_CLEAR_COOLDOWN
Definition Opcodes.h:507
SystemTimePoint GetSystemTime()
Current chrono system_clock time point.
Definition GameTime.cpp:52
uint32 GetPetNumber() const
Definition Unit.h:692
Clock::time_point CooldownEnd
Clock::time_point CategoryEnd
static void WriteCooldown(PreparedStatementBase *stmt, uint8 &index, CooldownStorageType::value_type const &cooldown)
static void SetIdentifier(PreparedStatementBase *stmt, uint8 index, Unit *owner)
static bool ReadCooldown(Field *fields, uint32 *spellId, CooldownEntry *cooldownEntry)
static bool ReadCooldown(Field *fields, uint32 *spellId, CooldownEntry *cooldownEntry)
static void WriteCooldown(PreparedStatementBase *stmt, uint8 &index, CooldownStorageType::value_type const &cooldown)
static void SetIdentifier(PreparedStatementBase *stmt, uint8 index, Unit *owner)