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