TrinityCore
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 "CharmInfo.h"
20#include "DB2Stores.h"
21#include "DatabaseEnv.h"
22#include "Duration.h"
23#include "Item.h"
24#include "Map.h"
25#include "ObjectMgr.h"
26#include "Pet.h"
27#include "PetPackets.h"
28#include "Player.h"
29#include "Spell.h"
30#include "SpellInfo.h"
31#include "SpellMgr.h"
32#include "SpellPackets.h"
33#include "World.h"
34
36
37template<>
39{
40 static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_CHAR_SPELL_COOLDOWNS;
41 static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_CHAR_SPELL_COOLDOWN;
42 static CharacterDatabaseStatements const ChargesDeleteStatement = CHAR_DEL_CHAR_SPELL_CHARGES;
43 static CharacterDatabaseStatements const ChargesInsertStatement = CHAR_INS_CHAR_SPELL_CHARGES;
44
45 static void SetIdentifier(PreparedStatementBase* stmt, uint8 index, Unit* owner) { stmt->setUInt64(index, owner->GetGUID().GetCounter()); }
46
47 static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry)
48 {
49 *spellId = fields[0].GetUInt32();
50 if (!sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE))
51 return false;
52
53 cooldownEntry->SpellId = *spellId;
54 cooldownEntry->CooldownEnd = time_point_cast<Duration>(Clock::from_time_t(fields[2].GetInt64()));
55 cooldownEntry->ItemId = fields[1].GetUInt32();
56 cooldownEntry->CategoryId = fields[3].GetUInt32();
57 cooldownEntry->CategoryEnd = time_point_cast<Duration>(Clock::from_time_t(fields[4].GetInt64()));
58 return true;
59 }
60
61 static bool ReadCharge(Field* fields, uint32* categoryId, ChargeEntry* chargeEntry)
62 {
63 *categoryId = fields[0].GetUInt32();
64 if (!sSpellCategoryStore.LookupEntry(*categoryId))
65 return false;
66
67 chargeEntry->RechargeStart = time_point_cast<Duration>(Clock::from_time_t(fields[1].GetInt64()));
68 chargeEntry->RechargeEnd = time_point_cast<Duration>(Clock::from_time_t(fields[2].GetInt64()));
69 return true;
70 }
71
72 static void WriteCooldown(PreparedStatementBase* stmt, uint8& index, CooldownStorageType::value_type const& cooldown)
73 {
74 stmt->setUInt32(index++, cooldown.first);
75 stmt->setUInt32(index++, cooldown.second.ItemId);
76 stmt->setInt64(index++, Clock::to_time_t(cooldown.second.CooldownEnd));
77 stmt->setUInt32(index++, cooldown.second.CategoryId);
78 stmt->setInt64(index++, Clock::to_time_t(cooldown.second.CategoryEnd));
79 }
80
81 static void WriteCharge(PreparedStatementBase* stmt, uint8& index, uint32 chargeCategory, ChargeEntry const& charge)
82 {
83 stmt->setUInt32(index++, chargeCategory);
84 stmt->setInt64(index++, Clock::to_time_t(charge.RechargeStart));
85 stmt->setInt64(index++, Clock::to_time_t(charge.RechargeEnd));
86 }
87};
88
89template<>
91{
92 static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_PET_SPELL_COOLDOWNS;
93 static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_PET_SPELL_COOLDOWN;
94 static CharacterDatabaseStatements const ChargesDeleteStatement = CHAR_DEL_PET_SPELL_CHARGES;
95 static CharacterDatabaseStatements const ChargesInsertStatement = CHAR_INS_PET_SPELL_CHARGES;
96
97 static void SetIdentifier(PreparedStatementBase* stmt, uint8 index, Unit* owner) { stmt->setUInt32(index, owner->GetCharmInfo()->GetPetNumber()); }
98
99 static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry)
100 {
101 *spellId = fields[0].GetUInt32();
102 if (!sSpellMgr->GetSpellInfo(*spellId, DIFFICULTY_NONE))
103 return false;
104
105 cooldownEntry->SpellId = *spellId;
106 cooldownEntry->CooldownEnd = time_point_cast<Duration>(Clock::from_time_t(fields[1].GetInt64()));
107 cooldownEntry->ItemId = 0;
108 cooldownEntry->CategoryId = fields[2].GetUInt32();
109 cooldownEntry->CategoryEnd = time_point_cast<Duration>(Clock::from_time_t(fields[3].GetInt64()));
110 return true;
111 }
112
113 static bool ReadCharge(Field* fields, uint32* categoryId, ChargeEntry* chargeEntry)
114 {
115 *categoryId = fields[0].GetUInt32();
116 if (!sSpellCategoryStore.LookupEntry(*categoryId))
117 return false;
118
119 chargeEntry->RechargeStart = time_point_cast<Duration>(Clock::from_time_t(fields[1].GetInt64()));
120 chargeEntry->RechargeEnd = time_point_cast<Duration>(Clock::from_time_t(fields[2].GetInt64()));
121 return true;
122 }
123
124 static void WriteCooldown(PreparedStatementBase* stmt, uint8& index, CooldownStorageType::value_type const& cooldown)
125 {
126 stmt->setUInt32(index++, cooldown.first);
127 stmt->setInt64(index++, Clock::to_time_t(cooldown.second.CooldownEnd));
128 stmt->setUInt32(index++, cooldown.second.CategoryId);
129 stmt->setInt64(index++, Clock::to_time_t(cooldown.second.CategoryEnd));
130 }
131
132 static void WriteCharge(PreparedStatementBase* stmt, uint8& index, uint32 chargeCategory, ChargeEntry const& charge)
133 {
134 stmt->setUInt32(index++, chargeCategory);
135 stmt->setInt64(index++, Clock::to_time_t(charge.RechargeStart));
136 stmt->setInt64(index++, Clock::to_time_t(charge.RechargeEnd));
137 }
138};
139
140SpellHistory::SpellHistory(Unit* owner) : _owner(owner), _schoolLockouts()
141{
142}
143
145
146template<class OwnerType>
148{
149 using StatementInfo = PersistenceHelper<OwnerType>;
150
151 if (cooldownsResult)
152 {
153 do
154 {
155 uint32 spellId;
156 CooldownEntry cooldown;
157 if (StatementInfo::ReadCooldown(cooldownsResult->Fetch(), &spellId, &cooldown))
158 {
159 _spellCooldowns[spellId] = cooldown;
160 if (cooldown.CategoryId)
161 _categoryCooldowns[cooldown.CategoryId] = &_spellCooldowns[spellId];
162 }
163
164 } while (cooldownsResult->NextRow());
165 }
166
167 if (chargesResult)
168 {
169 do
170 {
171 Field* fields = chargesResult->Fetch();
172 uint32 categoryId = 0;
173 ChargeEntry charges;
174 if (StatementInfo::ReadCharge(fields, &categoryId, &charges))
175 _categoryCharges[categoryId].push_back(charges);
176
177 } while (chargesResult->NextRow());
178 }
179}
180
181template<class OwnerType>
183{
184 using StatementInfo = PersistenceHelper<OwnerType>;
185
186 uint8 index = 0;
187 CharacterDatabasePreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsDeleteStatement);
188 StatementInfo::SetIdentifier(stmt, index++, _owner);
189 trans->Append(stmt);
190
191 for (auto const& p : _spellCooldowns)
192 {
193 if (!p.second.OnHold)
194 {
195 index = 0;
196 stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsInsertStatement);
197 StatementInfo::SetIdentifier(stmt, index++, _owner);
198 StatementInfo::WriteCooldown(stmt, index, p);
199 trans->Append(stmt);
200 }
201 }
202
203 stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::ChargesDeleteStatement);
204 StatementInfo::SetIdentifier(stmt, 0, _owner);
205 trans->Append(stmt);
206
207 for (auto const& p : _categoryCharges)
208 {
209 for (ChargeEntry const& charge : p.second)
210 {
211 index = 0;
212 stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::ChargesInsertStatement);
213 StatementInfo::SetIdentifier(stmt, index++, _owner);
214 StatementInfo::WriteCharge(stmt, index, p.first, charge);
215 trans->Append(stmt);
216 }
217 }
218}
219
221{
222 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
223 for (auto itr = _categoryCooldowns.begin(); itr != _categoryCooldowns.end();)
224 {
225 if (itr->second->CategoryEnd < now)
226 itr = _categoryCooldowns.erase(itr);
227 else
228 ++itr;
229 }
230
231 for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();)
232 {
233 if (itr->second.CooldownEnd < now)
234 itr = EraseCooldown(itr);
235 else
236 ++itr;
237 }
238
239 for (auto& [chargeCategoryId, chargeRefreshTimes] : _categoryCharges)
240 while (!chargeRefreshTimes.empty() && chargeRefreshTimes.front().RechargeEnd <= now)
241 chargeRefreshTimes.pop_front();
242}
243
244void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell /*= nullptr*/)
245{
246 HandleCooldowns(spellInfo, item ? item->GetEntry() : 0, spell);
247}
248
249void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/)
250{
251 if (spell && spell->IsIgnoringCooldowns())
252 return;
253
254 if (ConsumeCharge(spellInfo->ChargeCategoryId))
255 return;
256
257 if (Player* player = _owner->ToPlayer())
258 {
259 // potions start cooldown until exiting combat
260 if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId))
261 {
262 if (itemTemplate->IsPotion() || spellInfo->IsCooldownStartedOnEvent())
263 {
264 player->SetLastPotionId(itemId);
265 return;
266 }
267 }
268 }
269
270 if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive())
271 return;
272
273 StartCooldown(spellInfo, itemId, spell);
274}
275
276bool SpellHistory::IsReady(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const
277{
279 if (IsSchoolLocked(spellInfo->GetSchoolMask()))
280 return false;
281
282 if (HasCooldown(spellInfo, itemId))
283 return false;
284
285 if (!HasCharge(spellInfo->ChargeCategoryId))
286 return false;
287
288 return true;
289}
290
291template<class PacketType>
292void SpellHistory::WritePacket(PacketType* /*packet*/) const
293{
294 static_assert(!std::is_same<PacketType, PacketType>::value /*static_assert(false)*/, "This packet is not supported.");
295}
296
297template<>
299{
300 sendSpellHistory->Entries.reserve(_spellCooldowns.size());
301
302 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
303 for (auto const& p : _spellCooldowns)
304 {
306 historyEntry.SpellID = p.first;
307 historyEntry.ItemID = p.second.ItemId;
308
309 if (p.second.OnHold)
310 historyEntry.OnHold = true;
311 else
312 {
313 Milliseconds cooldownDuration = duration_cast<Milliseconds>(p.second.CooldownEnd - now);
314 if (cooldownDuration.count() <= 0)
315 continue;
316
317 Milliseconds categoryDuration = duration_cast<Milliseconds>(p.second.CategoryEnd - now);
318 if (categoryDuration.count() > 0)
319 {
320 historyEntry.Category = p.second.CategoryId;
321 historyEntry.CategoryRecoveryTime = uint32(categoryDuration.count());
322 }
323
324 if (cooldownDuration.count() > categoryDuration.count())
325 historyEntry.RecoveryTime = uint32(cooldownDuration.count());
326 }
327
328 sendSpellHistory->Entries.push_back(historyEntry);
329 }
330}
331
332template<>
334{
335 sendSpellCharges->Entries.reserve(_categoryCharges.size());
336
337 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
338 for (auto const& p : _categoryCharges)
339 {
340 if (!p.second.empty())
341 {
342 Milliseconds cooldownDuration = duration_cast<Milliseconds>(p.second.front().RechargeEnd - now);
343 if (cooldownDuration.count() <= 0)
344 continue;
345
347 chargeEntry.Category = p.first;
348 chargeEntry.NextRecoveryTime = uint32(cooldownDuration.count());
349 chargeEntry.ConsumedCharges = uint8(p.second.size());
350 sendSpellCharges->Entries.push_back(chargeEntry);
351 }
352 }
353}
354
355template<>
357{
358 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
359
360 petSpells->Cooldowns.reserve(_spellCooldowns.size());
361 for (auto const& p : _spellCooldowns)
362 {
364 petSpellCooldown.SpellID = p.first;
365 petSpellCooldown.Category = p.second.CategoryId;
366
367 if (!p.second.OnHold)
368 {
369 Milliseconds cooldownDuration = duration_cast<Milliseconds>(p.second.CooldownEnd - now);
370 if (cooldownDuration.count() <= 0)
371 continue;
372
373 petSpellCooldown.Duration = uint32(cooldownDuration.count());
374 Milliseconds categoryDuration = duration_cast<Milliseconds>(p.second.CategoryEnd - now);
375 if (categoryDuration.count() > 0)
376 petSpellCooldown.CategoryDuration = uint32(categoryDuration.count());
377 }
378 else
379 petSpellCooldown.CategoryDuration = 0x80000000;
380
381 petSpells->Cooldowns.push_back(petSpellCooldown);
382 }
383
384 petSpells->SpellHistory.reserve(_categoryCharges.size());
385 for (auto const& p : _categoryCharges)
386 {
387 if (!p.second.empty())
388 {
389 Milliseconds cooldownDuration = duration_cast<Milliseconds>(p.second.front().RechargeEnd - now);
390 if (cooldownDuration.count() <= 0)
391 continue;
392
394 petChargeEntry.CategoryID = p.first;
395 petChargeEntry.RecoveryTime = uint32(cooldownDuration.count());
396 petChargeEntry.ConsumedCharges = int8(p.second.size());
397
398 petSpells->SpellHistory.push_back(petChargeEntry);
399 }
400 }
401}
402
403void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/, Optional<Duration> forcedCooldown /*= {}*/)
404{
405 // init cooldown values
406 uint32 categoryId = 0;
407 Duration cooldown = Duration::zero();
408 Duration categoryCooldown = Duration::zero();
409
410 TimePoint curTime = time_point_cast<Duration>(GameTime::GetTime<Clock>());
411 TimePoint catrecTime;
412 TimePoint recTime;
413 bool needsCooldownPacket = false;
414
415 if (!forcedCooldown)
416 GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown);
417 else
418 cooldown = *forcedCooldown;
419
420 // overwrite time for selected category
421 if (onHold)
422 {
423 // use +MONTH as infinite cooldown marker
424 catrecTime = categoryCooldown > Duration::zero() ? (curTime + InfinityCooldownDelay) : curTime;
425 recTime = cooldown > Duration::zero() ? (curTime + InfinityCooldownDelay) : catrecTime;
426 }
427 else
428 {
429 if (!forcedCooldown)
430 {
431 // Now we have cooldown data (if found any), time to apply mods
432 if (Player* modOwner = _owner->GetSpellModOwner())
433 {
434 auto applySpellMod = [&](Milliseconds& value)
435 {
436 int32 intValue = value.count();
437 modOwner->ApplySpellMod(spellInfo, SpellModOp::Cooldown, intValue, spell);
438 value = Milliseconds(intValue);
439 };
440
441 if (cooldown >= Duration::zero())
442 applySpellMod(cooldown);
443
444 if (categoryCooldown >= Clock::duration::zero() && !spellInfo->HasAttribute(SPELL_ATTR6_NO_CATEGORY_COOLDOWN_MODS))
445 applySpellMod(categoryCooldown);
446 }
447
449 {
450 cooldown = Duration(int64(cooldown.count() * _owner->m_unitData->ModSpellHaste));
451 categoryCooldown = Duration(int64(categoryCooldown.count() * _owner->m_unitData->ModSpellHaste));
452 }
453
455 {
456 cooldown = Duration(int64(cooldown.count() * _owner->m_unitData->ModHasteRegen));
457 categoryCooldown = Duration(int64(categoryCooldown.count() * _owner->m_unitData->ModHasteRegen));
458 }
459
461 {
462 // Apply SPELL_AURA_MOD_COOLDOWN only to own spells
463 Player* playerOwner = GetPlayerOwner();
464 if (!playerOwner || playerOwner->HasSpell(spellInfo->Id))
465 {
466 needsCooldownPacket = true;
467 cooldown += Milliseconds(cooldownMod); // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
468 }
469 }
470
471 // Apply SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN modifiers
472 // Note: This aura applies its modifiers to all cooldowns of spells with set category, not to category cooldown only
473 if (categoryId)
474 {
476 {
477 if (cooldown > Duration::zero())
478 cooldown += Milliseconds(categoryModifier);
479
480 if (categoryCooldown > Duration::zero())
481 categoryCooldown += Milliseconds(categoryModifier);
482 }
483
484 SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.AssertEntry(categoryId);
486 categoryCooldown = duration_cast<Milliseconds>(Clock::from_time_t(sWorld->GetNextDailyQuestsResetTime()) - GameTime::GetTime<Clock>());
487 }
488 }
489 else
490 needsCooldownPacket = true;
491
492 // replace negative cooldowns by 0
493 if (cooldown < Duration::zero())
494 cooldown = Duration::zero();
495
496 if (categoryCooldown < Duration::zero())
497 categoryCooldown = Duration::zero();
498
499 // no cooldown after applying spell mods
500 if (cooldown == Duration::zero() && categoryCooldown == Duration::zero())
501 return;
502
503 catrecTime = categoryCooldown != Duration::zero() ? curTime + categoryCooldown : curTime;
504 recTime = cooldown != Duration::zero() ? curTime + cooldown : catrecTime;
505 }
506
507 // self spell cooldown
508 if (recTime != curTime)
509 {
510 AddCooldown(spellInfo->Id, itemId, recTime, categoryId, catrecTime, onHold);
511
512 if (needsCooldownPacket)
513 {
514 if (Player* playerOwner = GetPlayerOwner())
515 {
517 spellCooldown.Caster = _owner->GetGUID();
518 spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE;
519 spellCooldown.SpellCooldowns.emplace_back(spellInfo->Id, uint32(cooldown.count()));
520 playerOwner->SendDirectMessage(spellCooldown.Write());
521 }
522 }
523 }
524}
525
526void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= nullptr*/, bool startCooldown /*= true*/)
527{
528 // Send activate cooldown timer (possible 0) at client side
529 if (Player* player = GetPlayerOwner())
530 {
531 uint32 category = spellInfo->GetCategory();
532 GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
533
534 auto categoryItr = _categoryCooldowns.find(category);
535 if (categoryItr != _categoryCooldowns.end() && categoryItr->second->SpellId != spellInfo->Id)
536 {
537 player->SendDirectMessage(WorldPackets::Spells::CooldownEvent(player != _owner, categoryItr->second->SpellId).Write());
538
539 if (startCooldown)
540 StartCooldown(sSpellMgr->AssertSpellInfo(categoryItr->second->SpellId, _owner->GetMap()->GetDifficultyID()), itemId, spell);
541 }
542
543 player->SendDirectMessage(WorldPackets::Spells::CooldownEvent(player != _owner, spellInfo->Id).Write());
544 }
545
546 // start cooldowns at server side, if any
547 if (startCooldown)
548 StartCooldown(spellInfo, itemId, spell);
549}
550
551void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, TimePoint cooldownEnd, uint32 categoryId, TimePoint categoryEnd, bool onHold /*= false*/)
552{
553 CooldownEntry& cooldownEntry = _spellCooldowns[spellId];
554 // scripts can start multiple cooldowns for a given spell, only store the longest one
555 if (cooldownEnd > cooldownEntry.CooldownEnd || categoryEnd > cooldownEntry.CategoryEnd || onHold)
556 {
557 cooldownEntry.SpellId = spellId;
558 cooldownEntry.CooldownEnd = cooldownEnd;
559 cooldownEntry.ItemId = itemId;
560 cooldownEntry.CategoryId = categoryId;
561 cooldownEntry.CategoryEnd = categoryEnd;
562 cooldownEntry.OnHold = onHold;
563
564 if (categoryId)
565 _categoryCooldowns[categoryId] = &cooldownEntry;
566 }
567}
568
569void SpellHistory::ModifySpellCooldown(uint32 spellId, Duration cooldownMod, bool withoutCategoryCooldown)
570{
571 auto itr = _spellCooldowns.find(spellId);
572 if (!cooldownMod.count() || itr == _spellCooldowns.end())
573 return;
574
575 ModifySpellCooldown(itr, cooldownMod, withoutCategoryCooldown);
576}
577
578void SpellHistory::ModifySpellCooldown(CooldownStorageType::iterator& itr, Duration cooldownMod, bool withoutCategoryCooldown)
579{
580 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
581
582 itr->second.CooldownEnd += cooldownMod;
583
584 if (itr->second.CategoryId)
585 {
586 if (!withoutCategoryCooldown)
587 itr->second.CategoryEnd += cooldownMod;
588
589 // Because category cooldown existence is tied to regular cooldown, we cannot allow a situation where regular cooldown is shorter than category
590 if (itr->second.CooldownEnd < itr->second.CategoryEnd)
591 itr->second.CooldownEnd = itr->second.CategoryEnd;
592 }
593
594 if (Player* playerOwner = GetPlayerOwner())
595 {
597 modifyCooldown.IsPet = _owner != playerOwner;
598 modifyCooldown.SpellID = itr->second.SpellId;
599 modifyCooldown.DeltaTime = duration_cast<Milliseconds>(cooldownMod).count();
600 modifyCooldown.WithoutCategoryCooldown = withoutCategoryCooldown;
601 playerOwner->SendDirectMessage(modifyCooldown.Write());
602 }
603
604 if (itr->second.CooldownEnd <= now)
605 itr = EraseCooldown(itr);
606}
607
608void SpellHistory::ModifyCooldown(uint32 spellId, Duration cooldownMod, bool withoutCategoryCooldown)
609{
610 if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, _owner->GetMap()->GetDifficultyID()))
611 ModifyCooldown(spellInfo, cooldownMod, withoutCategoryCooldown);
612}
613
614void SpellHistory::ModifyCooldown(SpellInfo const* spellInfo, Duration cooldownMod, bool withoutCategoryCooldown)
615{
616 if (!cooldownMod.count())
617 return;
618
619 if (GetChargeRecoveryTime(spellInfo->ChargeCategoryId) > 0 && GetMaxCharges(spellInfo->ChargeCategoryId) > 0)
620 ModifyChargeRecoveryTime(spellInfo->ChargeCategoryId, cooldownMod);
621 else
622 ModifySpellCooldown(spellInfo->Id, cooldownMod, withoutCategoryCooldown);
623}
624
625void SpellHistory::ResetCooldown(uint32 spellId, bool update /*= false*/)
626{
627 auto itr = _spellCooldowns.find(spellId);
628 if (itr == _spellCooldowns.end())
629 return;
630
631 ResetCooldown(itr, update);
632}
633
634void SpellHistory::ResetCooldown(CooldownStorageType::iterator& itr, bool update /*= false*/)
635{
636 if (update)
637 {
638 if (Player* playerOwner = GetPlayerOwner())
639 {
641 clearCooldown.IsPet = _owner != playerOwner;
642 clearCooldown.SpellID = itr->first;
643 clearCooldown.ClearOnHold = false;
644 playerOwner->SendDirectMessage(clearCooldown.Write());
645 }
646 }
647
648 itr = EraseCooldown(itr);
649}
650
652{
653 if (GetPlayerOwner())
654 {
655 std::vector<int32> cooldowns;
656 cooldowns.reserve(_spellCooldowns.size());
657 for (auto const& p : _spellCooldowns)
658 cooldowns.push_back(p.first);
659
660 SendClearCooldowns(cooldowns);
661 }
662
663 _categoryCooldowns.clear();
664 _spellCooldowns.clear();
665}
666
667bool SpellHistory::HasCooldown(SpellInfo const* spellInfo, uint32 itemId /*= 0*/) const
668{
670 return false;
671
672 if (_spellCooldowns.count(spellInfo->Id) != 0)
673 return true;
674
675 if (spellInfo->CooldownAuraSpellId && _owner->HasAura(spellInfo->CooldownAuraSpellId))
676 return true;
677
678 uint32 category = 0;
679 GetCooldownDurations(spellInfo, itemId, nullptr, &category, nullptr);
680 if (!category)
681 return false;
682
683 return _categoryCooldowns.count(category) != 0;
684}
685
686bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/) const
687{
688 return HasCooldown(sSpellMgr->AssertSpellInfo(spellId, _owner->GetMap()->GetDifficultyID()), itemId);
689}
690
692{
693 TimePoint end;
694 auto itr = _spellCooldowns.find(spellInfo->Id);
695 if (itr != _spellCooldowns.end())
696 end = itr->second.CooldownEnd;
697 else
698 {
699 auto catItr = _categoryCooldowns.find(spellInfo->GetCategory());
700 if (catItr == _categoryCooldowns.end())
701 return Duration::zero();
702
703 end = catItr->second->CategoryEnd;
704 }
705
706 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
707 if (end < now)
708 return Duration::zero();
709
710 Clock::duration remaining = end - now;
711 return duration_cast<Milliseconds>(remaining);
712}
713
715{
716 TimePoint end;
717 auto catItr = _categoryCooldowns.find(categoryId);
718 if (catItr == _categoryCooldowns.end())
719 return Duration::zero();
720
721 end = catItr->second->CategoryEnd;
722
723 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
724 if (end < now)
725 return Duration::zero();
726
727 Clock::duration remaining = end - now;
728 return duration_cast<Milliseconds>(remaining);
729}
730
732{
733 return GetRemainingCategoryCooldown(spellInfo->GetCategory());
734}
735
737{
738 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
739 TimePoint lockoutEnd = now + lockoutTime;
740 for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
741 if (SpellSchoolMask(1 << i) & schoolMask)
742 _schoolLockouts[i] = lockoutEnd;
743
744 std::set<uint32> knownSpells;
745 if (Player* plrOwner = _owner->ToPlayer())
746 {
747 for (auto const& p : plrOwner->GetSpellMap())
748 if (p.second.state != PLAYERSPELL_REMOVED)
749 knownSpells.insert(p.first);
750 }
751 else if (Pet* petOwner = _owner->ToPet())
752 {
753 for (auto const& p : petOwner->m_spells)
754 if (p.second.state != PETSPELL_REMOVED)
755 knownSpells.insert(p.first);
756 }
757 else
758 {
759 Creature* creatureOwner = _owner->ToCreature();
760 for (uint32 spell : creatureOwner->m_spells)
761 if (spell)
762 knownSpells.insert(spell);
763 }
764
766 spellCooldown.Caster = _owner->GetGUID();
768 for (uint32 spellId : knownSpells)
769 {
770 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId, _owner->GetMap()->GetDifficultyID());
771 if (spellInfo->IsCooldownStartedOnEvent())
772 continue;
773
775 continue;
776
777 if (!(schoolMask & spellInfo->GetSchoolMask()))
778 continue;
779
780 if (GetRemainingCooldown(spellInfo) < lockoutTime)
781 AddCooldown(spellId, 0, lockoutEnd, 0, now);
782
783 // always send cooldown, even if it will be shorter than already existing cooldown for LossOfControl UI
784 spellCooldown.SpellCooldowns.emplace_back(spellId, lockoutTime.count());
785 }
786
787 if (Player* player = GetPlayerOwner())
788 if (!spellCooldown.SpellCooldowns.empty())
789 player->SendDirectMessage(spellCooldown.Write());
790}
791
793{
794 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
795 for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i)
796 if (SpellSchoolMask(1 << i) & schoolMask)
797 if (_schoolLockouts[i] > now)
798 return true;
799
800 return false;
801}
802
804{
805 if (!sSpellCategoryStore.LookupEntry(chargeCategoryId))
806 return false;
807
808 int32 chargeRecovery = GetChargeRecoveryTime(chargeCategoryId);
809 if (chargeRecovery > 0 && GetMaxCharges(chargeCategoryId) > 0)
810 {
811 TimePoint recoveryStart;
812 std::deque<ChargeEntry>& charges = _categoryCharges[chargeCategoryId];
813 if (charges.empty())
814 recoveryStart = time_point_cast<Duration>(GameTime::GetTime<Clock>());
815 else
816 recoveryStart = charges.back().RechargeEnd;
817
818 charges.emplace_back(recoveryStart, Milliseconds(chargeRecovery));
819 return true;
820 }
821
822 return false;
823}
824
825void SpellHistory::ModifyChargeRecoveryTime(uint32 chargeCategoryId, Duration cooldownMod)
826{
827 SpellCategoryEntry const* chargeCategoryEntry = sSpellCategoryStore.LookupEntry(chargeCategoryId);
828 if (!chargeCategoryEntry)
829 return;
830
831 auto itr = _categoryCharges.find(chargeCategoryId);
832 if (itr == _categoryCharges.end() || itr->second.empty())
833 return;
834
835 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
836
837 for (ChargeEntry& entry : itr->second)
838 {
839 entry.RechargeStart += cooldownMod;
840 entry.RechargeEnd += cooldownMod;
841 }
842
843 while (!itr->second.empty() && itr->second.front().RechargeEnd < now)
844 itr->second.pop_front();
845
846 SendSetSpellCharges(chargeCategoryId, itr->second);
847}
848
850{
851 auto itr = _categoryCharges.find(chargeCategoryId);
852 if (itr != _categoryCharges.end() && !itr->second.empty())
853 {
854 itr->second.pop_back();
855
856 SendSetSpellCharges(chargeCategoryId, itr->second);
857 }
858}
859
860void SpellHistory::ResetCharges(uint32 chargeCategoryId)
861{
862 auto itr = _categoryCharges.find(chargeCategoryId);
863 if (itr != _categoryCharges.end())
864 {
865 _categoryCharges.erase(itr);
866
867 if (Player* player = GetPlayerOwner())
868 {
870 clearSpellCharges.IsPet = _owner != player;
871 clearSpellCharges.Category = chargeCategoryId;
872 player->SendDirectMessage(clearSpellCharges.Write());
873 }
874 }
875}
876
878{
879 _categoryCharges.clear();
880
881 if (Player* player = GetPlayerOwner())
882 {
884 clearAllSpellCharges.IsPet = _owner != player;
885 player->SendDirectMessage(clearAllSpellCharges.Write());
886 }
887}
888
889bool SpellHistory::HasCharge(uint32 chargeCategoryId) const
890{
891 if (!sSpellCategoryStore.LookupEntry(chargeCategoryId))
892 return true;
893
894 // Check if the spell is currently using charges (untalented warlock Dark Soul)
895 int32 maxCharges = GetMaxCharges(chargeCategoryId);
896 if (maxCharges <= 0)
897 return true;
898
899 auto itr = _categoryCharges.find(chargeCategoryId);
900 return itr == _categoryCharges.end() || int32(itr->second.size()) < maxCharges;
901}
902
904{
905 SpellCategoryEntry const* chargeCategoryEntry = sSpellCategoryStore.LookupEntry(chargeCategoryId);
906 if (!chargeCategoryEntry)
907 return 0;
908
909 uint32 charges = chargeCategoryEntry->MaxCharges;
911 return charges;
912}
913
915{
916 SpellCategoryEntry const* chargeCategoryEntry = sSpellCategoryStore.LookupEntry(chargeCategoryId);
917 if (!chargeCategoryEntry)
918 return 0;
919
920 int32 recoveryTime = chargeCategoryEntry->ChargeRecoveryTime;
922
923 float recoveryTimeF = float(recoveryTime);
925
927 recoveryTimeF *= _owner->m_unitData->ModSpellHaste;
928
930 recoveryTimeF *= _owner->m_unitData->ModHasteRegen;
931
932 return int32(std::floor(recoveryTimeF));
933}
934
935bool SpellHistory::HasGlobalCooldown(SpellInfo const* spellInfo) const
936{
937 auto itr = _globalCooldowns.find(spellInfo->StartRecoveryCategory);
938 return itr != _globalCooldowns.end() && itr->second > Clock::now();
939}
940
941void SpellHistory::AddGlobalCooldown(SpellInfo const* spellInfo, Duration duration)
942{
943 _globalCooldowns[spellInfo->StartRecoveryCategory] = time_point_cast<Duration>(Clock::now() + duration);
944}
945
947{
949}
950
952{
953 TimePoint end;
954 auto cdItr = _globalCooldowns.find(spellInfo->StartRecoveryCategory);
955 if (cdItr == _globalCooldowns.end())
956 return Duration::zero();
957
958 end = cdItr->second;
959 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
960 if (end < now)
961 return Duration::zero();
962
963 Clock::duration remaining = end - now;
964 return duration_cast<Milliseconds>(remaining);
965}
966
968{
969 _pauseTime = time_point_cast<Duration>(GameTime::GetTime<Clock>());
970}
971
973{
974 if (!_pauseTime)
975 return;
976
977 Duration pausedDuration = time_point_cast<Duration>(GameTime::GetTime<Clock>()) - *_pauseTime;
978
979 for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();)
980 itr->second.CooldownEnd += pausedDuration;
981
982 for (auto& [chargeCategoryId, chargeRefreshTimes] : _categoryCharges)
983 for (ChargeEntry& chargeEntry : chargeRefreshTimes)
984 chargeEntry.RechargeEnd += pausedDuration;
985
986 _pauseTime.reset();
987
988 Update();
989}
990
992{
994}
995
996void SpellHistory::SendClearCooldowns(std::vector<int32> const& cooldowns) const
997{
998 if (Player const* playerOwner = GetPlayerOwner())
999 {
1001 clearCooldowns.IsPet = _owner != playerOwner;
1002 clearCooldowns.SpellID = cooldowns;
1003 playerOwner->SendDirectMessage(clearCooldowns.Write());
1004 }
1005}
1006
1007void SpellHistory::SendSetSpellCharges(uint32 chargeCategoryId, ChargeEntryCollection const& chargeCollection)
1008{
1009 if (Player* player = GetPlayerOwner())
1010 {
1012 setSpellCharges.Category = chargeCategoryId;
1013 if (!chargeCollection.empty())
1014 setSpellCharges.NextRecoveryTime = uint32(duration_cast<Milliseconds>(chargeCollection.front().RechargeEnd - GameTime::GetTime<Clock>()).count());
1015 setSpellCharges.ConsumedCharges = uint8(chargeCollection.size());
1016 setSpellCharges.IsPet = player != _owner;
1017 player->SendDirectMessage(setSpellCharges.Write());
1018 }
1019}
1020
1021void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, Duration* cooldown, uint32* categoryId, Duration* categoryCooldown)
1022{
1023 ASSERT(cooldown || categoryId || categoryCooldown);
1024 Duration tmpCooldown = Duration::min();
1025 uint32 tmpCategoryId = 0;
1026 Duration tmpCategoryCooldown = Duration::min();
1027
1028 // cooldown information stored in ItemEffect.db2, overriding normal cooldown and category
1029 if (itemId)
1030 {
1031 if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId))
1032 {
1033 for (ItemEffectEntry const* itemEffect : proto->Effects)
1034 {
1035 if (uint32(itemEffect->SpellID) == spellInfo->Id)
1036 {
1037 tmpCooldown = Milliseconds(itemEffect->CoolDownMSec);
1038 tmpCategoryId = itemEffect->SpellCategoryID;
1039 tmpCategoryCooldown = Milliseconds(itemEffect->CategoryCoolDownMSec);
1040 break;
1041 }
1042 }
1043 }
1044 }
1045
1046 // if no cooldown found above then base at DBC data
1047 if (tmpCooldown < Duration::zero() && tmpCategoryCooldown < Duration::zero())
1048 {
1049 tmpCooldown = Milliseconds(spellInfo->RecoveryTime);
1050 tmpCategoryId = spellInfo->GetCategory();
1051 tmpCategoryCooldown = Milliseconds(spellInfo->CategoryRecoveryTime);
1052 }
1053
1054 if (cooldown)
1055 *cooldown = tmpCooldown;
1056 if (categoryId)
1057 *categoryId = tmpCategoryId;
1058 if (categoryCooldown)
1059 *categoryCooldown = tmpCategoryCooldown;
1060}
1061
1063{
1065}
1066
1068{
1069 if (Player* player = _owner->ToPlayer())
1070 {
1071 // add all profession CDs created while in duel (if any)
1072 for (auto const& c : _spellCooldowns)
1073 {
1074 SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(c.first, DIFFICULTY_NONE);
1075
1076 if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS ||
1077 spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS)
1078 _spellCooldownsBeforeDuel[c.first] = _spellCooldowns[c.first];
1079 }
1080
1081 // check for spell with onHold active before and during the duel
1082 for (CooldownStorageType::value_type const& pair : _spellCooldownsBeforeDuel)
1083 {
1084 if (!pair.second.OnHold &&
1085 _spellCooldowns.find(pair.first) != _spellCooldowns.end() &&
1086 !_spellCooldowns[pair.first].OnHold)
1087 _spellCooldowns[pair.first] = _spellCooldownsBeforeDuel[pair.first];
1088 }
1089
1090 // update the client: restore old cooldowns
1092 spellCooldown.Caster = _owner->GetGUID();
1094
1095 for (auto const& c : _spellCooldowns)
1096 {
1097 TimePoint now = time_point_cast<Duration>(GameTime::GetTime<Clock>());
1098 uint32 cooldownDuration = uint32(c.second.CooldownEnd > now ? duration_cast<Milliseconds>(c.second.CooldownEnd - now).count() : 0);
1099
1100 // cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs
1101 if (cooldownDuration <= 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS || c.second.OnHold)
1102 continue;
1103
1104 spellCooldown.SpellCooldowns.emplace_back(c.first, cooldownDuration);
1105 }
1106
1107 player->SendDirectMessage(spellCooldown.Write());
1108 }
1109}
1110
1111template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult);
1112template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult);
1113template void SpellHistory::SaveToDB<Player>(CharacterDatabaseTransaction trans);
1114template void SpellHistory::SaveToDB<Pet>(CharacterDatabaseTransaction trans);
CharacterDatabaseStatements
@ CHAR_DEL_PET_SPELL_CHARGES
@ CHAR_INS_PET_SPELL_CHARGES
@ CHAR_INS_PET_SPELL_COOLDOWN
@ CHAR_DEL_CHAR_SPELL_COOLDOWNS
@ CHAR_INS_CHAR_SPELL_COOLDOWN
@ CHAR_INS_CHAR_SPELL_CHARGES
@ CHAR_DEL_PET_SPELL_COOLDOWNS
@ CHAR_DEL_CHAR_SPELL_CHARGES
@ IN_MILLISECONDS
Definition: Common.h:35
@ MINUTE
Definition: Common.h:29
@ MONTH
Definition: Common.h:33
DB2Storage< SpellCategoryEntry > sSpellCategoryStore("SpellCategory.db2", &SpellCategoryLoadInfo::Instance)
@ DIFFICULTY_NONE
Definition: DBCEnums.h:874
@ SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET
Definition: DBCEnums.h:1921
SQLTransaction< CharacterDatabaseConnection > CharacterDatabaseTransaction
std::shared_ptr< PreparedResultSet > PreparedQueryResult
DatabaseWorkerPool< CharacterDatabaseConnection > CharacterDatabase
Accessor to the character database.
Definition: DatabaseEnv.cpp:21
uint8_t uint8
Definition: Define.h:144
int64_t int64
Definition: Define.h:137
int8_t int8
Definition: Define.h:140
int32_t int32
Definition: Define.h:138
uint32_t uint32
Definition: Define.h:142
std::chrono::seconds Seconds
Seconds shorthand typedef.
Definition: Duration.h:32
std::chrono::milliseconds Milliseconds
Milliseconds shorthand typedef.
Definition: Duration.h:29
#define ASSERT
Definition: Errors.h:68
#define sObjectMgr
Definition: ObjectMgr.h:1946
std::optional< T > Optional
Optional helper class to wrap optional values within.
Definition: Optional.h:25
@ PETSPELL_REMOVED
Definition: PetDefines.h:72
@ PLAYERSPELL_REMOVED
Definition: Player.h:189
@ SPELL_PREVENTION_TYPE_SILENCE
SpellSchoolMask
@ MAX_SPELL_SCHOOL
@ SPELL_ATTR6_NO_CATEGORY_COOLDOWN_MODS
@ SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN
@ SPELL_AURA_CHARGE_RECOVERY_MOD
@ SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE
@ SPELL_AURA_IGNORE_SPELL_COOLDOWN
@ SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN
@ SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER
@ SPELL_AURA_MOD_COOLDOWN
@ SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE_REGEN
@ SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE
@ SPELL_AURA_MOD_MAX_CHARGES
@ SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS
Starts GCD for spells that should start their cooldown on events, requires SPELL_COOLDOWN_FLAG_INCLUD...
Definition: SpellHistory.h:41
@ SPELL_COOLDOWN_FLAG_LOSS_OF_CONTROL_UI
Shows interrupt cooldown in loss of control ui.
Definition: SpellHistory.h:42
@ SPELL_COOLDOWN_FLAG_NONE
Definition: SpellHistory.h:39
#define sSpellMgr
Definition: SpellMgr.h:849
uint32 m_spells[MAX_CREATURE_SPELLS]
Definition: Creature.h:302
Class used to access individual fields of database query result.
Definition: Field.h:90
uint32 GetUInt32() const
Definition: Field.cpp:62
Definition: Item.h:170
Difficulty GetDifficultyID() const
Definition: Map.h:324
LowType GetCounter() const
Definition: ObjectGuid.h:293
static Creature * ToCreature(Object *o)
Definition: Object.h:219
uint32 GetEntry() const
Definition: Object.h:161
static ObjectGuid GetGUID(Object const *o)
Definition: Object.h:159
static Player * ToPlayer(Object *o)
Definition: Object.h:213
Definition: Pet.h:40
bool HasSpell(uint32 spell) const override
Definition: Player.cpp:3792
void setInt64(const uint8 index, const int64 value)
void setUInt32(const uint8 index, const uint32 value)
void setUInt64(const uint8 index, const uint64 value)
void AddCooldown(uint32 spellId, uint32 itemId, Duration cooldownDuration)
Definition: SpellHistory.h:108
Duration GetRemainingGlobalCooldown(SpellInfo const *spellInfo) const
void ResetAllCharges()
GlobalCooldownStorageType _globalCooldowns
Definition: SpellHistory.h:206
void SendSetSpellCharges(uint32 chargeCategoryId, ChargeEntryCollection const &chargeCollection)
void LoadFromDB(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult)
TimePoint _schoolLockouts[MAX_SPELL_SCHOOL]
Definition: SpellHistory.h:204
bool HasGlobalCooldown(SpellInfo const *spellInfo) const
Duration GetRemainingCategoryCooldown(uint32 categoryId) const
int32 GetMaxCharges(uint32 chargeCategoryId) const
void ModifyChargeRecoveryTime(uint32 chargeCategoryId, Duration cooldownMod)
void SendCooldownEvent(SpellInfo const *spellInfo, uint32 itemId=0, Spell *spell=nullptr, bool startCooldown=true)
void ResetCooldown(uint32 spellId, bool update=false)
bool IsReady(SpellInfo const *spellInfo, uint32 itemId=0) const
void AddGlobalCooldown(SpellInfo const *spellInfo, Duration duration)
bool ConsumeCharge(uint32 chargeCategoryId)
void SaveToDB(CharacterDatabaseTransaction trans)
void SendClearCooldowns(std::vector< int32 > const &cooldowns) const
void RestoreCharge(uint32 chargeCategoryId)
static Duration const InfinityCooldownDelay
Definition: SpellHistory.h:103
Optional< TimePoint > _pauseTime
Definition: SpellHistory.h:207
void LockSpellSchool(SpellSchoolMask schoolMask, Duration lockoutTime)
void WritePacket(PacketType *packet) const
std::deque< ChargeEntry > ChargeEntryCollection
Definition: SpellHistory.h:73
Duration GetRemainingCooldown(SpellInfo const *spellInfo) const
SpellHistory(Unit *owner)
void ResetAllCooldowns()
CooldownStorageType _spellCooldowns
Definition: SpellHistory.h:201
void ResumeCooldowns()
void ResetCharges(uint32 chargeCategoryId)
static void GetCooldownDurations(SpellInfo const *spellInfo, uint32 itemId, Duration *cooldown, uint32 *categoryId, Duration *categoryCooldown)
void CancelGlobalCooldown(SpellInfo const *spellInfo)
void RestoreCooldownStateAfterDuel()
void ModifyCooldown(uint32 spellId, Duration cooldownMod, bool withoutCategoryCooldown=false)
CooldownStorageType _spellCooldownsBeforeDuel
Definition: SpellHistory.h:202
bool HasCharge(uint32 chargeCategoryId) const
void SaveCooldownStateBeforeDuel()
Milliseconds Duration
Definition: SpellHistory.h:50
std::chrono::time_point< Clock, Duration > TimePoint
Definition: SpellHistory.h:51
CooldownStorageType::iterator EraseCooldown(CooldownStorageType::iterator itr)
Definition: SpellHistory.h:190
void HandleCooldowns(SpellInfo const *spellInfo, Item const *item, Spell *spell=nullptr)
void PauseCooldowns()
void ModifySpellCooldown(uint32 spellId, Duration cooldownMod, bool withoutCategoryCooldown)
void StartCooldown(SpellInfo const *spellInfo, uint32 itemId, Spell *spell=nullptr, bool onHold=false, Optional< Duration > forcedCooldown={})
Player * GetPlayerOwner() const
int32 GetChargeRecoveryTime(uint32 chargeCategoryId) const
ChargeStorageType _categoryCharges
Definition: SpellHistory.h:205
bool IsSchoolLocked(SpellSchoolMask schoolMask) const
CategoryCooldownStorageType _categoryCooldowns
Definition: SpellHistory.h:203
bool HasCooldown(SpellInfo const *spellInfo, uint32 itemId=0) const
uint32 PreventionType
Definition: SpellInfo.h:411
uint32 GetCategory() const
Definition: SpellInfo.cpp:1386
uint32 const Id
Definition: SpellInfo.h:325
uint32 RecoveryTime
Definition: SpellInfo.h:366
bool IsCooldownStartedOnEvent() const
Definition: SpellInfo.cpp:1649
bool IsPassive() const
Definition: SpellInfo.cpp:1592
uint32 CooldownAuraSpellId
Definition: SpellInfo.h:370
uint32 CategoryRecoveryTime
Definition: SpellInfo.h:367
SpellSchoolMask GetSchoolMask() const
Definition: SpellInfo.cpp:2465
bool HasAttribute(SpellAttr0 attribute) const
Definition: SpellInfo.h:449
uint32 ChargeCategoryId
Definition: SpellInfo.h:414
uint32 StartRecoveryCategory
Definition: SpellInfo.h:368
Definition: Spell.h:255
bool IsIgnoringCooldowns() const
Definition: Spell.cpp:8158
Definition: Unit.h:627
Pet * ToPet()
Definition: Unit.h:1750
bool HasAuraTypeWithMiscvalue(AuraType auraType, int32 miscValue) const
Definition: Unit.cpp:4687
UF::UpdateField< UF::UnitData, 0, TYPEID_UNIT > m_unitData
Definition: Unit.h:1814
CharmInfo * GetCharmInfo()
Definition: Unit.h:1221
float GetTotalAuraMultiplierByMiscValue(AuraType auraType, int32 misc_value) const
Definition: Unit.cpp:4999
bool HasAuraType(AuraType auraType) const
Definition: Unit.cpp:4674
int32 GetTotalAuraModifier(AuraType auraType) const
Definition: Unit.cpp:4929
bool HasAura(uint32 spellId, ObjectGuid casterGUID=ObjectGuid::Empty, ObjectGuid itemCasterGUID=ObjectGuid::Empty, uint32 reqEffMask=0) const
Definition: Unit.cpp:4664
int32 GetTotalAuraModifierByMiscValue(AuraType auraType, int32 misc_value) const
Definition: Unit.cpp:4989
bool HasAuraTypeWithAffectMask(AuraType auraType, SpellInfo const *affectedSpell) const
Definition: Unit.cpp:4696
Map * GetMap() const
Definition: Object.h:624
Player * GetSpellModOwner() const
Definition: Object.cpp:2272
Player * GetCharmerOrOwnerPlayerOrPlayerItself() const
Definition: Object.cpp:2252
std::vector< PetSpellCooldown > Cooldowns
Definition: PetPackets.h:118
std::vector< PetSpellHistory > SpellHistory
Definition: PetPackets.h:119
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
WorldPacket const * Write() override
std::vector< SpellChargeEntry > Entries
Definition: SpellPackets.h:662
std::vector< SpellHistoryEntry > Entries
Definition: SpellPackets.h:609
WorldPacket const * Write() override
WorldPacket const * Write() override
std::vector< SpellCooldownStruct > SpellCooldowns
Definition: SpellPackets.h:584
#define sWorld
Definition: World.h:931
uint32 GetPetNumber() const
Definition: CharmInfo.h:94
uint16 SpellCategoryID
int32 CategoryCoolDownMSec
static void WriteCooldown(PreparedStatementBase *stmt, uint8 &index, CooldownStorageType::value_type const &cooldown)
static void SetIdentifier(PreparedStatementBase *stmt, uint8 index, Unit *owner)
static void WriteCharge(PreparedStatementBase *stmt, uint8 &index, uint32 chargeCategory, ChargeEntry const &charge)
static bool ReadCooldown(Field *fields, uint32 *spellId, CooldownEntry *cooldownEntry)
static bool ReadCharge(Field *fields, uint32 *categoryId, ChargeEntry *chargeEntry)
static bool ReadCooldown(Field *fields, uint32 *spellId, CooldownEntry *cooldownEntry)
static void WriteCharge(PreparedStatementBase *stmt, uint8 &index, uint32 chargeCategory, ChargeEntry const &charge)
static bool ReadCharge(Field *fields, uint32 *categoryId, ChargeEntry *chargeEntry)
static void WriteCooldown(PreparedStatementBase *stmt, uint8 &index, CooldownStorageType::value_type const &cooldown)
static void SetIdentifier(PreparedStatementBase *stmt, uint8 index, Unit *owner)