void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime) { Clock::time_point now = Clock::now(); Clock::time_point lockoutEnd = now + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(lockoutTime)); for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i) if (SpellSchoolMask(1 << i) & schoolMask) _schoolLockouts[i] = lockoutEnd; std::set<uint32> knownSpells; if (Player* plrOwner = _owner->ToPlayer()) { for (auto const& p : plrOwner->GetSpellMap()) if (p.second->state != PLAYERSPELL_REMOVED) knownSpells.insert(p.first); } else if (Pet* petOwner = _owner->ToPet()) { for (auto const& p : petOwner->m_spells) if (p.second.state != PETSPELL_REMOVED) knownSpells.insert(p.first); } else { Creature* creatureOwner = _owner->ToCreature(); for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) if (creatureOwner->m_spells[i]) knownSpells.insert(creatureOwner->m_spells[i]); } WorldPackets::Spells::SpellCooldown spellCooldown; spellCooldown.Caster = _owner->GetGUID(); spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; for (uint32 spellId : knownSpells) { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId); if (spellInfo->IsCooldownStartedOnEvent()) continue; if (!(spellInfo->PreventionType & SPELL_PREVENTION_TYPE_SILENCE)) continue; if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellInfo) < lockoutTime) { spellCooldown.SpellCooldowns.emplace_back(spellId, lockoutTime); AddCooldown(spellId, 0, lockoutEnd, 0, now); } } if (Player* player = GetPlayerOwner()) if (!spellCooldown.SpellCooldowns.empty()) player->SendDirectMessage(spellCooldown.Write()); }
void SpellHistory::RestoreCooldownStateAfterDuel() { if (Player* player = _owner->ToPlayer()) { // add all profession CDs created while in duel (if any) for (auto const& c : _spellCooldowns) { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(c.first); if (spellInfo->RecoveryTime > 10 * MINUTE * IN_MILLISECONDS || spellInfo->CategoryRecoveryTime > 10 * MINUTE * IN_MILLISECONDS) _spellCooldownsBeforeDuel[c.first] = _spellCooldowns[c.first]; } // check for spell with onHold active before and during the duel for (auto itr = _spellCooldownsBeforeDuel.begin(); itr != _spellCooldownsBeforeDuel.end(); ++itr) { if (!itr->second.OnHold && _spellCooldowns.find(itr->first) != _spellCooldowns.end() && !_spellCooldowns[itr->first].OnHold) _spellCooldowns[itr->first] = _spellCooldownsBeforeDuel[itr->first]; } // update the client: restore old cooldowns WorldPackets::Spells::SpellCooldown spellCooldown; spellCooldown.Caster = _owner->GetGUID(); spellCooldown.Flags = SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS; for (auto const& c : _spellCooldowns) { Clock::time_point now = Clock::now(); uint32 cooldownDuration = uint32(c.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(c.second.CooldownEnd - now).count() : 0); // cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs if (cooldownDuration <= 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS || c.second.OnHold) continue; spellCooldown.SpellCooldowns.emplace_back(c.first, cooldownDuration); } player->SendDirectMessage(spellCooldown.Write()); } }
void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/) { // init cooldown values uint32 categoryId = 0; int32 cooldown = -1; int32 categoryCooldown = -1; GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown); Clock::time_point curTime = Clock::now(); Clock::time_point catrecTime; Clock::time_point recTime; bool needsCooldownPacket = false; // overwrite time for selected category if (onHold) { // use +MONTH as infinite cooldown marker catrecTime = categoryCooldown > 0 ? (curTime + InfinityCooldownDelay) : curTime; recTime = cooldown > 0 ? (curTime + InfinityCooldownDelay) : catrecTime; } else { // shoot spells used equipped item cooldown values already assigned in SetBaseAttackTime(RANGED_ATTACK) // prevent 0 cooldowns set by another way if (cooldown <= 0 && categoryCooldown <= 0 && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75))) cooldown = _owner->GetUInt32Value(UNIT_FIELD_RANGEDATTACKTIME); // Now we have cooldown data (if found any), time to apply mods if (Player* modOwner = _owner->GetSpellModOwner()) { if (cooldown > 0) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell); if (categoryCooldown > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell); } if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE, spellInfo)) { cooldown = int32(cooldown * _owner->GetFloatValue(UNIT_MOD_CAST_HASTE)); categoryCooldown = int32(categoryCooldown * _owner->GetFloatValue(UNIT_MOD_CAST_HASTE)); } if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN, spellInfo)) { cooldown = int32(cooldown * _owner->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN)); categoryCooldown = int32(categoryCooldown * _owner->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN)); } if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN)) { // Apply SPELL_AURA_MOD_COOLDOWN only to own spells Player* playerOwner = GetPlayerOwner(); if (!playerOwner || playerOwner->HasSpell(spellInfo->Id)) { needsCooldownPacket = true; cooldown += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks } } // Apply SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN modifiers // Note: This aura applies its modifiers to all cooldowns of spells with set category, not to category cooldown only if (categoryId) { if (int32 categoryModifier = _owner->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN, categoryId)) { if (cooldown > 0) cooldown += categoryModifier; if (categoryCooldown > 0) categoryCooldown += categoryModifier; } SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.AssertEntry(categoryId); if (categoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET) categoryCooldown = int32(std::chrono::duration_cast<std::chrono::milliseconds>(Clock::from_time_t(sWorld->GetNextDailyQuestsResetTime()) - Clock::now()).count()); } // replace negative cooldowns by 0 if (cooldown < 0) cooldown = 0; if (categoryCooldown < 0) categoryCooldown = 0; // no cooldown after applying spell mods if (cooldown == 0 && categoryCooldown == 0) return; catrecTime = categoryCooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(categoryCooldown)) : curTime; recTime = cooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldown)) : catrecTime; } // self spell cooldown if (recTime != curTime) { AddCooldown(spellInfo->Id, itemId, recTime, categoryId, catrecTime, onHold); if (needsCooldownPacket) { if (Player* playerOwner = GetPlayerOwner()) { WorldPackets::Spells::SpellCooldown spellCooldown; spellCooldown.Caster = _owner->GetGUID(); spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; spellCooldown.SpellCooldowns.emplace_back(spellInfo->Id, uint32(cooldown)); playerOwner->SendDirectMessage(spellCooldown.Write()); } } } }
void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/) { // init cooldown values uint32 categoryId = 0; int32 cooldown = -1; int32 categoryCooldown = -1; // some special item spells without correct cooldown in SpellInfo // cooldown information stored in item prototype if (itemId) { if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId)) { for (ItemEffectEntry const* itemEffect : proto->Effects) { if (itemEffect->SpellID == spellInfo->Id) { categoryId = itemEffect->Category; cooldown = itemEffect->Cooldown; categoryCooldown = itemEffect->CategoryCooldown; break; } } } } // if no cooldown found above then base at DBC data if (cooldown < 0 && categoryCooldown < 0) { categoryId = spellInfo->GetCategory(); cooldown = spellInfo->RecoveryTime; categoryCooldown = spellInfo->CategoryRecoveryTime; } Clock::time_point curTime = Clock::now(); Clock::time_point catrecTime; Clock::time_point recTime; bool needsCooldownPacket = false; // overwrite time for selected category if (onHold) { // use +MONTH as infinite cooldown marker catrecTime = categoryCooldown > 0 ? (curTime + InfinityCooldownDelay) : curTime; recTime = cooldown > 0 ? (curTime + InfinityCooldownDelay) : catrecTime; } else { // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) // prevent 0 cooldowns set by another way if (cooldown <= 0 && categoryCooldown <= 0 && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75))) cooldown = _owner->GetAttackTime(RANGED_ATTACK); // Now we have cooldown data (if found any), time to apply mods if (Player* modOwner = _owner->GetSpellModOwner()) { if (cooldown > 0) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell); if (categoryCooldown > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell); } if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN)) { // Apply SPELL_AURA_MOD_COOLDOWN only to own spells Player* playerOwner = GetPlayerOwner(); if (!playerOwner || playerOwner->HasSpell(spellInfo->Id)) { needsCooldownPacket = true; cooldown += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks } } // Apply SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN modifiers // Note: This aura applies its modifiers to all cooldowns of spells with set category, not to category cooldown only if (categoryId) { if (int32 categoryModifier = _owner->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN, categoryId)) { if (cooldown > 0) cooldown += categoryModifier; if (categoryCooldown > 0) categoryCooldown += categoryModifier; } SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.AssertEntry(categoryId); if (categoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET) categoryCooldown = int32(std::chrono::duration_cast<std::chrono::milliseconds>(Clock::from_time_t(sWorld->GetNextDailyQuestsResetTime()) - Clock::now()).count()); } // replace negative cooldowns by 0 if (cooldown < 0) cooldown = 0; if (categoryCooldown < 0) categoryCooldown = 0; // no cooldown after applying spell mods if (cooldown == 0 && categoryCooldown == 0) return; catrecTime = categoryCooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(categoryCooldown)) : curTime; recTime = cooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldown)) : catrecTime; } // self spell cooldown if (recTime != curTime) { AddCooldown(spellInfo->Id, itemId, recTime, onHold); if (needsCooldownPacket) { if (Player* playerOwner = GetPlayerOwner()) { WorldPackets::Spells::SpellCooldown spellCooldown; spellCooldown.Caster = _owner->GetGUID(); spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; spellCooldown.SpellCooldowns.emplace_back(spellInfo->Id, uint32(cooldown)); playerOwner->SendDirectMessage(spellCooldown.Write()); } } } // category spells if (categoryId && catrecTime != curTime) { SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(categoryId); if (i_scstore != sSpellsByCategoryStore.end()) { for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset) { if (*i_scset == spellInfo->Id) // skip main spell, already handled above continue; AddCooldown(*i_scset, itemId, catrecTime, onHold); } } } }