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 Clock::time_point end;
485 auto itr = _spellCooldowns.find(spellInfo->Id);
486 if (itr != _spellCooldowns.end())
487 end = itr->second.CooldownEnd;
488 else
489 {
490 auto catItr = _categoryCooldowns.find(spellInfo->GetCategory());
491 if (catItr == _categoryCooldowns.end())
492 return 0;
493
494 end = catItr->second->CategoryEnd;
495 }
496
497 Clock::time_point now = GameTime::GetSystemTime();
498 if (end < now)
499 return 0;
500
501 Clock::duration remaining = end - now;
502 return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count());
503}
504
506{
507 Clock::time_point now = GameTime::GetSystemTime();
508 Clock::time_point lockoutEnd = now + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(lockoutTime));
509 for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
510 if (SpellSchoolMask(1 << i) & schoolMask)
511 _schoolLockouts[i] = lockoutEnd;
512
513 std::set<uint32> knownSpells;
514 if (Player* plrOwner = _owner->ToPlayer())
515 {
516 for (auto const& p : plrOwner->GetSpellMap())
517 if (p.second.state != PLAYERSPELL_REMOVED)
518 knownSpells.insert(p.first);
519 }
520 else if (Pet* petOwner = _owner->ToPet())
521 {
522 for (auto const& p : petOwner->m_spells)
523 if (p.second.state != PETSPELL_REMOVED)
524 knownSpells.insert(p.first);
525 }
526 else
527 {
528 Creature* creatureOwner = _owner->ToCreature();
529 for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i)
530 if (creatureOwner->m_spells[i])
531 knownSpells.insert(creatureOwner->m_spells[i]);
532 }
533
534 PacketCooldowns cooldowns;
535 WorldPacket spellCooldowns;
536 for (uint32 spellId : knownSpells)
537 {
538 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId);
539 if (spellInfo->IsCooldownStartedOnEvent())
540 continue;
541
543 continue;
544
545 if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellInfo) < lockoutTime)
546 {
547 cooldowns[spellId] = lockoutTime;
548 AddCooldown(spellId, 0, lockoutEnd, 0, now);
549 }
550 }
551
552 if (Player* player = GetPlayerOwner())
553 {
554 if (!cooldowns.empty())
555 {
556 BuildCooldownPacket(spellCooldowns, SPELL_COOLDOWN_FLAG_NONE, cooldowns);
557 player->SendDirectMessage(&spellCooldowns);
558 }
559 }
560}
561
563{
564 Clock::time_point now = GameTime::GetSystemTime();
565 for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
566 if (SpellSchoolMask(1 << i) & schoolMask)
567 if (_schoolLockouts[i] > now)
568 return true;
569
570 return false;
571}
572
573bool SpellHistory::HasGlobalCooldown(SpellInfo const* spellInfo) const
574{
575 auto itr = _globalCooldowns.find(spellInfo->StartRecoveryCategory);
576 return itr != _globalCooldowns.end() && itr->second > GameTime::GetSystemTime();
577}
578
579void SpellHistory::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration)
580{
581 _globalCooldowns[spellInfo->StartRecoveryCategory] = GameTime::GetSystemTime() + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(duration));
582}
583
585{
586 _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::time_point(Clock::duration(0));
587}
588
593
594void SpellHistory::SendClearCooldowns(std::vector<int32> const& cooldowns) const
595{
596 if (Player* playerOwner = GetPlayerOwner())
597 {
598 for (int32 spell : cooldowns)
599 {
600 WorldPacket data(SMSG_CLEAR_COOLDOWN, 4 + 8);
601 data << uint32(spell);
602 data << _owner->GetGUID();
603 playerOwner->SendDirectMessage(&data);
604 }
605 }
606}
607
609{
610 data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + 4 + 4);
611 data << _owner->GetGUID();
612 data << uint8(flags);
613 data << uint32(spellId);
614 data << uint32(cooldown);
615}
616
618{
619 data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + (4 + 4) * cooldowns.size());
620 data << _owner->GetGUID();
621 data << uint8(flags);
622 for (auto const& cooldown : cooldowns)
623 {
624 data << cooldown.first;
625 data << cooldown.second;
626 }
627}
628
629void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown)
630{
631 ASSERT(cooldown || categoryId || categoryCooldown);
632 int32 tmpCooldown = -1;
633 uint32 tmpCategoryId = 0;
634 int32 tmpCategoryCooldown = -1;
635
636 // some special item spells without correct cooldown in SpellInfo
637 // cooldown information stored in item prototype
638 if (itemId)
639 {
640 if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
641 {
642 for (ItemEffect const& itemEffect : proto->Effects)
643 {
644 if (uint32(itemEffect.SpellID) == spellInfo->Id)
645 {
646 tmpCooldown = itemEffect.CoolDownMSec;
647 tmpCategoryId = itemEffect.SpellCategoryID;
648 tmpCategoryCooldown = itemEffect.CategoryCoolDownMSec;
649 break;
650 }
651 }
652 }
653 }
654
655 // if no cooldown found above then base at DBC data
656 if (tmpCooldown < 0 && tmpCategoryCooldown < 0)
657 {
658 tmpCooldown = spellInfo->RecoveryTime;
659 tmpCategoryId = spellInfo->GetCategory();
660 tmpCategoryCooldown = spellInfo->CategoryRecoveryTime;
661 }
662
663 if (cooldown)
664 *cooldown = tmpCooldown;
665 if (categoryId)
666 *categoryId = tmpCategoryId;
667 if (categoryCooldown)
668 *categoryCooldown = tmpCategoryCooldown;
669}
670
675
677{
678 // category cooldows are not preserved.
679 if (Player* player = _owner->ToPlayer())
680 {
681 // add all profession CDs created while in duel (if any)
682 for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
683 {
684 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first);
685
686 if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS ||
687 spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS)
688 _spellCooldownsBeforeDuel[itr->first] = _spellCooldowns[itr->first];
689 }
690
691 // check for spell with onHold active before and during the duel
692 for (auto itr = _spellCooldownsBeforeDuel.begin(); itr != _spellCooldownsBeforeDuel.end(); ++itr)
693 {
694 if (!itr->second.OnHold && !_spellCooldowns[itr->first].OnHold)
695 _spellCooldowns[itr->first] = _spellCooldownsBeforeDuel[itr->first];
696 }
697
698 // update the client: restore old cooldowns
699 PacketCooldowns cooldowns;
700
701 for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end(); ++itr)
702 {
703 Clock::time_point now = GameTime::GetSystemTime();
704 uint32 cooldownDuration = itr->second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(itr->second.CooldownEnd - now).count() : 0;
705
706 // cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs
707 if (cooldownDuration <= 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS || itr->second.OnHold)
708 continue;
709
710 cooldowns[itr->first] = cooldownDuration;
711 }
712
713 WorldPacket data;
715 player->SendDirectMessage(&data);
716 }
717}
718
719template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult);
720template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult);
721template void SpellHistory::SaveToDB<Player>(CharacterDatabaseTransaction trans);
722template 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:151
@ 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:229
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:186
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:180
Definition Pet.h:40
bool HasSpell(uint32 spell) const override
Definition Player.cpp:3853
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)
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:7525
Definition Unit.h:769
Pet * ToPet()
Definition Unit.h:1788
CharmInfo * GetCharmInfo()
Definition Unit.h:1287
uint32 GetAttackTime(WeaponAttackType att) const
Definition Unit.cpp:8471
int32 GetTotalAuraModifier(AuraType auraType) const
Definition Unit.cpp:4792
Player * GetSpellModOwner() const
Definition Object.cpp:2223
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition Object.cpp:2203
void Initialize(uint16 opcode, size_t newres=200)
Definition WorldPacket.h:73
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:678
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)