diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Server/Packets/SpellPackets.cpp | 1 | ||||
-rw-r--r-- | src/server/game/Server/Packets/SpellPackets.h | 1 | ||||
-rw-r--r-- | src/server/game/Spells/Auras/SpellAuras.cpp | 4 | ||||
-rw-r--r-- | src/server/game/Spells/Spell.cpp | 33 | ||||
-rw-r--r-- | src/server/game/Spells/SpellEffects.cpp | 3 | ||||
-rw-r--r-- | src/server/game/Spells/SpellHistory.cpp | 212 | ||||
-rw-r--r-- | src/server/game/Spells/SpellHistory.h | 40 | ||||
-rw-r--r-- | src/server/scripts/World/duel_reset.cpp | 31 |
9 files changed, 182 insertions, 147 deletions
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 3eb62648133..bafed0f303d 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -12301,7 +12301,7 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) { m_weaponChangeTimer = spellProto->StartRecoveryTime; - GetSpellHistory()->AddGlobalCooldown(spellProto, m_weaponChangeTimer); + GetSpellHistory()->AddGlobalCooldown(spellProto, Milliseconds(m_weaponChangeTimer)); WorldPackets::Spells::SpellCooldown spellCooldown; spellCooldown.Caster = GetGUID(); @@ -24536,7 +24536,7 @@ void Player::ApplyEquipCooldown(Item* pItem) continue; // Don't replace longer cooldowns by equip cooldown if we have any. - if (GetSpellHistory()->GetRemainingCooldown(effectSpellInfo) > 30 * IN_MILLISECONDS) + if (GetSpellHistory()->GetRemainingCooldown(effectSpellInfo) > 30s) continue; GetSpellHistory()->AddCooldown(effectData->SpellID, pItem->GetEntry(), std::chrono::seconds(30)); diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp index c3b9b6c8b4b..7500a7cdb7e 100644 --- a/src/server/game/Server/Packets/SpellPackets.cpp +++ b/src/server/game/Server/Packets/SpellPackets.cpp @@ -613,6 +613,7 @@ WorldPacket const* WorldPackets::Spells::ModifyCooldown::Write() _worldPacket << int32(SpellID); _worldPacket << int32(DeltaTime); _worldPacket.WriteBit(IsPet); + _worldPacket.WriteBit(WithoutCategoryCooldown); _worldPacket.FlushBits(); return &_worldPacket; diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h index 6a2410fe571..43d53c9d50c 100644 --- a/src/server/game/Server/Packets/SpellPackets.h +++ b/src/server/game/Server/Packets/SpellPackets.h @@ -572,6 +572,7 @@ namespace WorldPackets WorldPacket const* Write() override; bool IsPet = false; + bool WithoutCategoryCooldown = false; int32 DeltaTime = 0; int32 SpellID = 0; }; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index ace2c4492e9..99e24997054 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1474,11 +1474,11 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b { // This additional check is needed to add a minimal delay before cooldown in in effect // to allow all bubbles broken by a single damage source proc mana return - if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetSpellInfo()) <= 11 * IN_MILLISECONDS) + if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetSpellInfo()) <= 11s) break; } else // and add if needed - caster->GetSpellHistory()->AddCooldown(aura->GetId(), 0, std::chrono::seconds(12)); + caster->GetSpellHistory()->AddCooldown(aura->GetId(), 0, 12s); } // effect on caster diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 997327c3147..6158e123e19 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -8120,12 +8120,6 @@ void Spell::PrepareTriggersExecutedOnHit() } // Global cooldowns management -enum GCDLimits -{ - MIN_GCD = 750, - MAX_GCD = 1500 -}; - bool CanHaveGlobalCooldown(WorldObject const* caster) { // Only players or controlled units have global cooldown @@ -8148,22 +8142,29 @@ void Spell::TriggerGlobalCooldown() if (!CanHaveGlobalCooldown(m_caster)) return; - int32 gcd = m_spellInfo->StartRecoveryTime; - if (!gcd || !m_spellInfo->StartRecoveryCategory) + Milliseconds gcd(m_spellInfo->StartRecoveryTime); + if (gcd == 0ms || !m_spellInfo->StartRecoveryCategory) return; if (m_caster->GetTypeId() == TYPEID_PLAYER) if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN)) return; + constexpr Milliseconds MinGCD = 750ms; + constexpr Milliseconds MaxGCD = 1500ms; + // Global cooldown can't leave range 1..1.5 secs // There are some spells (mostly not cast directly by player) that have < 1 sec and > 1.5 sec global cooldowns // but as tests show are not affected by any spell mods. - if (m_spellInfo->StartRecoveryTime >= MIN_GCD && m_spellInfo->StartRecoveryTime <= MAX_GCD) + if (gcd >= MinGCD && gcd <= MaxGCD) { // gcd modifier auras are applied only to own spells and only players have such mods if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo, SpellModOp::StartCooldown, gcd, this); + { + int32 intGcd = gcd.count(); + modOwner->ApplySpellMod(m_spellInfo, SpellModOp::StartCooldown, intGcd, this); + gcd = Milliseconds(intGcd); + } bool isMeleeOrRangedSpell = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED || @@ -8171,16 +8172,16 @@ void Spell::TriggerGlobalCooldown() m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY); // Apply haste rating - if (gcd > MIN_GCD && ((m_spellInfo->StartRecoveryCategory == 133 && !isMeleeOrRangedSpell))) + if (gcd > MinGCD && ((m_spellInfo->StartRecoveryCategory == 133 && !isMeleeOrRangedSpell))) { - gcd = int32(float(gcd) * m_caster->ToUnit()->m_unitData->ModSpellHaste); - RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD); + gcd = Milliseconds(int64(gcd.count() * m_caster->ToUnit()->m_unitData->ModSpellHaste)); + RoundToInterval(gcd, MinGCD, MaxGCD); } - if (gcd > MIN_GCD && m_caster->ToUnit()->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN, m_spellInfo)) + if (gcd > MinGCD && m_caster->ToUnit()->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN, m_spellInfo)) { - gcd = int32(float(gcd) * m_caster->ToUnit()->m_unitData->ModHasteRegen); - RoundToInterval<int32>(gcd, MIN_GCD, MAX_GCD); + gcd = Milliseconds(int64(gcd.count() * m_caster->ToUnit()->m_unitData->ModHasteRegen)); + RoundToInterval(gcd, MinGCD, MaxGCD); } } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 86d1eb92704..a367c246e60 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -2965,7 +2965,8 @@ void Spell::EffectInterruptCast() if (unitCaster) { int32 duration = m_spellInfo->GetDuration(); - unitTarget->GetSpellHistory()->LockSpellSchool(curSpellInfo->GetSchoolMask(), unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effectInfo->EffectIndex)); + duration = unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effectInfo->EffectIndex); + unitTarget->GetSpellHistory()->LockSpellSchool(curSpellInfo->GetSchoolMask(), Milliseconds(duration)); if (m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC) Unit::ProcSkillsAndAuras(unitCaster, unitTarget, PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_NEG, PROC_FLAG_TAKEN_SPELL_MAGIC_DMG_CLASS_NEG, PROC_SPELL_TYPE_MASK_ALL, PROC_SPELL_PHASE_HIT, PROC_HIT_INTERRUPT, nullptr, nullptr, nullptr); diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 0b5433f0b1c..caa91fd6d18 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -31,7 +31,7 @@ #include "SpellPackets.h" #include "World.h" -SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelay = std::chrono::duration_cast<SpellHistory::Clock::duration>(Seconds(MONTH)); +SpellHistory::Duration const SpellHistory::InfinityCooldownDelay = Seconds(MONTH); template<> struct SpellHistory::PersistenceHelper<Player> @@ -242,7 +242,7 @@ void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, HandleCooldowns(spellInfo, item ? item->GetEntry() : 0, spell); } -void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell /*= nullptr*/) +void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/) { if (spell && spell->IsIgnoringCooldowns()) return; @@ -253,11 +253,11 @@ void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Sp if (Player* player = _owner->ToPlayer()) { // potions start cooldown until exiting combat - if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemID)) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(itemId)) { if (itemTemplate->IsPotion() || spellInfo->IsCooldownStartedOnEvent()) { - player->SetLastPotionId(itemID); + player->SetLastPotionId(itemId); return; } } @@ -266,7 +266,7 @@ void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Sp if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive()) return; - StartCooldown(spellInfo, itemID, spell); + StartCooldown(spellInfo, itemId, spell); } bool SpellHistory::IsReady(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, bool ignoreCategoryCooldown /*= false*/) const @@ -394,98 +394,113 @@ void SpellHistory::WritePacket(WorldPackets::Pet::PetSpells* petSpells) const } } -void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/) +void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/, Optional<Duration> forcedCooldown /*= {}*/) { // init cooldown values uint32 categoryId = 0; - int32 cooldown = -1; - int32 categoryCooldown = -1; - - GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown); + Duration cooldown = Duration::zero(); + Duration categoryCooldown = Duration::zero(); Clock::time_point curTime = GameTime::GetGameTimePoint<Clock>(); Clock::time_point catrecTime; Clock::time_point recTime; bool needsCooldownPacket = false; + if (!forcedCooldown) + GetCooldownDurations(spellInfo, itemId, &cooldown, &categoryId, &categoryCooldown); + else + cooldown = *forcedCooldown; + // 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; + catrecTime = categoryCooldown > Duration::zero() ? (curTime + InfinityCooldownDelay) : curTime; + recTime = cooldown > Duration::zero() ? (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->m_unitData->RangedAttackRoundBaseTime; - - // Now we have cooldown data (if found any), time to apply mods - if (Player* modOwner = _owner->GetSpellModOwner()) + if (!forcedCooldown) { - if (cooldown >= 0) - modOwner->ApplySpellMod(spellInfo, SpellModOp::Cooldown, cooldown, spell); + // shoot spells used equipped item cooldown values already assigned in SetBaseAttackTime(RANGED_ATTACK) + // prevent 0 cooldowns set by another way + if (cooldown <= Duration::zero() && categoryCooldown <= Duration::zero() && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75))) + cooldown = Milliseconds(*_owner->m_unitData->RangedAttackRoundBaseTime); - if (categoryCooldown >= 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) - modOwner->ApplySpellMod(spellInfo, SpellModOp::Cooldown, categoryCooldown, spell); - } + // Now we have cooldown data (if found any), time to apply mods + if (Player* modOwner = _owner->GetSpellModOwner()) + { + auto applySpellMod = [&](Milliseconds& value) + { + int32 intValue = value.count(); + modOwner->ApplySpellMod(spellInfo, SpellModOp::Cooldown, intValue, spell); + value = Milliseconds(intValue); + }; - if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE, spellInfo)) - { - cooldown = int32(cooldown * _owner->m_unitData->ModSpellHaste); - categoryCooldown = int32(categoryCooldown * _owner->m_unitData->ModSpellHaste); - } + if (cooldown >= Duration::zero()) + applySpellMod(cooldown); - if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN, spellInfo)) - { - cooldown = int32(cooldown * _owner->m_unitData->ModHasteRegen); - categoryCooldown = int32(categoryCooldown * _owner->m_unitData->ModHasteRegen); - } + if (categoryCooldown >= Clock::duration::zero() && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) + applySpellMod(categoryCooldown); + } - 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)) + if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE, spellInfo)) { - needsCooldownPacket = true; - cooldown += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks + cooldown = Duration(int64(cooldown.count() * _owner->m_unitData->ModSpellHaste)); + categoryCooldown = Duration(int64(categoryCooldown.count() * _owner->m_unitData->ModSpellHaste)); } - } - // 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 (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN, spellInfo)) { - if (cooldown > 0) - cooldown += categoryModifier; + cooldown = Duration(int64(cooldown.count() * _owner->m_unitData->ModHasteRegen)); + categoryCooldown = Duration(int64(categoryCooldown.count() * _owner->m_unitData->ModHasteRegen)); + } - if (categoryCooldown > 0) - categoryCooldown += categoryModifier; + 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 += Milliseconds(cooldownMod); // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks + } } - SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.AssertEntry(categoryId); - if (categoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET) - categoryCooldown = int32(std::chrono::duration_cast<Milliseconds>(Clock::from_time_t(sWorld->GetNextDailyQuestsResetTime()) - Clock::now()).count()); + // 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 > Duration::zero()) + cooldown += Milliseconds(categoryModifier); + + if (categoryCooldown > Duration::zero()) + categoryCooldown += Milliseconds(categoryModifier); + } + + SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.AssertEntry(categoryId); + if (categoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET) + categoryCooldown = std::chrono::duration_cast<Milliseconds>(Clock::from_time_t(sWorld->GetNextDailyQuestsResetTime()) - Clock::now()); + } } + else + needsCooldownPacket = true; // replace negative cooldowns by 0 - if (cooldown < 0) - cooldown = 0; + if (cooldown < Duration::zero()) + cooldown = Duration::zero(); - if (categoryCooldown < 0) - categoryCooldown = 0; + if (categoryCooldown < Duration::zero()) + categoryCooldown = Duration::zero(); // no cooldown after applying spell mods - if (cooldown == 0 && categoryCooldown == 0) + if (cooldown == Duration::zero() && categoryCooldown == Duration::zero()) return; - catrecTime = categoryCooldown ? curTime + std::chrono::duration_cast<Clock::duration>(Milliseconds(categoryCooldown)) : curTime; - recTime = cooldown ? curTime + std::chrono::duration_cast<Clock::duration>(Milliseconds(cooldown)) : catrecTime; + catrecTime = categoryCooldown != Duration::zero() ? curTime + std::chrono::duration_cast<Clock::duration>(Milliseconds(categoryCooldown)) : curTime; + recTime = cooldown != Duration::zero() ? curTime + std::chrono::duration_cast<Clock::duration>(Milliseconds(cooldown)) : catrecTime; } // self spell cooldown @@ -500,7 +515,7 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel WorldPackets::Spells::SpellCooldown spellCooldown; spellCooldown.Caster = _owner->GetGUID(); spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; - spellCooldown.SpellCooldowns.emplace_back(spellInfo->Id, uint32(cooldown)); + spellCooldown.SpellCooldowns.emplace_back(spellInfo->Id, uint32(cooldown.count())); playerOwner->SendDirectMessage(spellCooldown.Write()); } } @@ -535,18 +550,22 @@ void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId / void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold /*= false*/) { CooldownEntry& cooldownEntry = _spellCooldowns[spellId]; - cooldownEntry.SpellId = spellId; - cooldownEntry.CooldownEnd = cooldownEnd; - cooldownEntry.ItemId = itemId; - cooldownEntry.CategoryId = categoryId; - cooldownEntry.CategoryEnd = categoryEnd; - cooldownEntry.OnHold = onHold; + // scripts can start multiple cooldowns for a given spell, only store the longest one + if (cooldownEnd > cooldownEntry.CooldownEnd || categoryEnd > cooldownEntry.CategoryEnd || onHold) + { + cooldownEntry.SpellId = spellId; + cooldownEntry.CooldownEnd = cooldownEnd; + cooldownEntry.ItemId = itemId; + cooldownEntry.CategoryId = categoryId; + cooldownEntry.CategoryEnd = categoryEnd; + cooldownEntry.OnHold = onHold; - if (categoryId) - _categoryCooldowns[categoryId] = &cooldownEntry; + if (categoryId) + _categoryCooldowns[categoryId] = &cooldownEntry; + } } -void SpellHistory::ModifySpellCooldown(uint32 spellId, Clock::duration offset) +void SpellHistory::ModifySpellCooldown(uint32 spellId, Duration offset) { auto itr = _spellCooldowns.find(spellId); if (!offset.count() || itr == _spellCooldowns.end()) @@ -569,13 +588,13 @@ void SpellHistory::ModifySpellCooldown(uint32 spellId, Clock::duration offset) } } -void SpellHistory::ModifyCooldown(uint32 spellId, Clock::duration cooldownMod) +void SpellHistory::ModifyCooldown(uint32 spellId, Duration cooldownMod) { if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId, _owner->GetMap()->GetDifficultyID())) ModifyCooldown(spellInfo, cooldownMod); } -void SpellHistory::ModifyCooldown(SpellInfo const* spellInfo, Clock::duration cooldownMod) +void SpellHistory::ModifyCooldown(SpellInfo const* spellInfo, Duration cooldownMod) { if (!cooldownMod.count()) return; @@ -649,7 +668,7 @@ bool SpellHistory::HasCooldown(uint32 spellId, uint32 itemId /*= 0*/, bool ignor return HasCooldown(sSpellMgr->AssertSpellInfo(spellId, _owner->GetMap()->GetDifficultyID()), itemId, ignoreCategoryCooldown); } -uint32 SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const +SpellHistory::Duration SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const { Clock::time_point end; auto itr = _spellCooldowns.find(spellInfo->Id); @@ -659,23 +678,23 @@ uint32 SpellHistory::GetRemainingCooldown(SpellInfo const* spellInfo) const { auto catItr = _categoryCooldowns.find(spellInfo->GetCategory()); if (catItr == _categoryCooldowns.end()) - return 0; + return Duration::zero(); end = catItr->second->CategoryEnd; } Clock::time_point now = GameTime::GetGameTimePoint<Clock>(); if (end < now) - return 0; + return Duration::zero(); Clock::duration remaining = end - now; - return uint32(std::chrono::duration_cast<Milliseconds>(remaining).count()); + return std::chrono::duration_cast<Milliseconds>(remaining); } -void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime) +void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, Duration lockoutTime) { Clock::time_point now = GameTime::GetGameTimePoint<Clock>(); - Clock::time_point lockoutEnd = now + std::chrono::duration_cast<Clock::duration>(Milliseconds(lockoutTime)); + Clock::time_point lockoutEnd = now + lockoutTime; for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i) if (SpellSchoolMask(1 << i) & schoolMask) _schoolLockouts[i] = lockoutEnd; @@ -703,7 +722,7 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim WorldPackets::Spells::SpellCooldown spellCooldown; spellCooldown.Caster = _owner->GetGUID(); - spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; + spellCooldown.Flags = SPELL_COOLDOWN_FLAG_LOSS_OF_CONTROL_UI; for (uint32 spellId : knownSpells) { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId, _owner->GetMap()->GetDifficultyID()); @@ -713,11 +732,14 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim if (!(spellInfo->PreventionType & SPELL_PREVENTION_TYPE_SILENCE)) continue; - if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellInfo) < lockoutTime) - { - spellCooldown.SpellCooldowns.emplace_back(spellId, lockoutTime); + if (!(schoolMask & spellInfo->GetSchoolMask())) + continue; + + if (GetRemainingCooldown(spellInfo) < lockoutTime) AddCooldown(spellId, 0, lockoutEnd, 0, now); - } + + // always send cooldown, even if it will be shorter than already existing cooldown for LossOfControl UI + spellCooldown.SpellCooldowns.emplace_back(spellId, lockoutTime.count()); } if (Player* player = GetPlayerOwner()) @@ -758,7 +780,7 @@ bool SpellHistory::ConsumeCharge(uint32 chargeCategoryId) return false; } -void SpellHistory::ModifyChargeRecoveryTime(uint32 chargeCategoryId, Clock::duration cooldownMod) +void SpellHistory::ModifyChargeRecoveryTime(uint32 chargeCategoryId, Duration cooldownMod) { SpellCategoryEntry const* chargeCategoryEntry = sSpellCategoryStore.LookupEntry(chargeCategoryId); if (!chargeCategoryEntry) @@ -874,9 +896,9 @@ bool SpellHistory::HasGlobalCooldown(SpellInfo const* spellInfo) const return itr != _globalCooldowns.end() && itr->second > Clock::now(); } -void SpellHistory::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration) +void SpellHistory::AddGlobalCooldown(SpellInfo const* spellInfo, Duration duration) { - _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::now() + std::chrono::duration_cast<Clock::duration>(Milliseconds(duration)); + _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::now() + duration; } void SpellHistory::CancelGlobalCooldown(SpellInfo const* spellInfo) @@ -914,12 +936,12 @@ void SpellHistory::SendSetSpellCharges(uint32 chargeCategoryId, ChargeEntryColle } } -void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown) +void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, Duration* cooldown, uint32* categoryId, Duration* categoryCooldown) { ASSERT(cooldown || categoryId || categoryCooldown); - int32 tmpCooldown = -1; + Duration tmpCooldown = Duration::min(); uint32 tmpCategoryId = 0; - int32 tmpCategoryCooldown = -1; + Duration tmpCategoryCooldown = Duration::min(); // cooldown information stored in ItemEffect.db2, overriding normal cooldown and category if (itemId) @@ -930,9 +952,9 @@ void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemI { if (uint32(itemEffect->SpellID) == spellInfo->Id) { - tmpCooldown = itemEffect->CoolDownMSec; + tmpCooldown = Milliseconds(itemEffect->CoolDownMSec); tmpCategoryId = itemEffect->SpellCategoryID; - tmpCategoryCooldown = itemEffect->CategoryCoolDownMSec; + tmpCategoryCooldown = Milliseconds(itemEffect->CategoryCoolDownMSec); break; } } @@ -940,11 +962,11 @@ void SpellHistory::GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemI } // if no cooldown found above then base at DBC data - if (tmpCooldown < 0 && tmpCategoryCooldown < 0) + if (tmpCooldown < Duration::zero() && tmpCategoryCooldown < Duration::zero()) { - tmpCooldown = spellInfo->RecoveryTime; + tmpCooldown = Milliseconds(spellInfo->RecoveryTime); tmpCategoryId = spellInfo->GetCategory(); - tmpCategoryCooldown = spellInfo->CategoryRecoveryTime; + tmpCategoryCooldown = Milliseconds(spellInfo->CategoryRecoveryTime); } if (cooldown) diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h index df7996916ad..28ff23ddeed 100644 --- a/src/server/game/Spells/SpellHistory.h +++ b/src/server/game/Spells/SpellHistory.h @@ -20,8 +20,8 @@ #include "SharedDefines.h" #include "DatabaseEnvFwd.h" +#include "Duration.h" #include "GameTime.h" -#include <chrono> #include <deque> #include <vector> #include <unordered_map> @@ -37,28 +37,31 @@ enum SpellCooldownFlags { SPELL_COOLDOWN_FLAG_NONE = 0x0, SPELL_COOLDOWN_FLAG_INCLUDE_GCD = 0x1, ///< Starts GCD in addition to normal cooldown specified in the packet - SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS = 0x2 ///< Starts GCD for spells that should start their cooldown on events, requires SPELL_COOLDOWN_FLAG_INCLUDE_GCD set + SPELL_COOLDOWN_FLAG_INCLUDE_EVENT_COOLDOWNS = 0x2, ///< Starts GCD for spells that should start their cooldown on events, requires SPELL_COOLDOWN_FLAG_INCLUDE_GCD set + SPELL_COOLDOWN_FLAG_LOSS_OF_CONTROL_UI = 0x4, ///< Shows interrupt cooldown in loss of control ui + SPELL_COOLDOWN_FLAG_ON_HOLD = 0x8 ///< Forces cooldown to behave as if SpellInfo::IsCooldownStartedOnEvent was true }; class TC_GAME_API SpellHistory { public: using Clock = std::chrono::system_clock; + using Duration = Milliseconds; // Cooldowns are stored only with millisecond precision, not whatever Clock's precision is struct CooldownEntry { uint32 SpellId = 0; - Clock::time_point CooldownEnd; + Clock::time_point CooldownEnd = Clock::time_point::min(); uint32 ItemId = 0; uint32 CategoryId = 0; - Clock::time_point CategoryEnd; + Clock::time_point CategoryEnd = Clock::time_point::min(); bool OnHold = false; }; struct ChargeEntry { ChargeEntry() = default; - ChargeEntry(Clock::time_point startTime, std::chrono::milliseconds rechargeTime) : RechargeStart(startTime), RechargeEnd(startTime + rechargeTime) { } + ChargeEntry(Clock::time_point startTime, Duration rechargeTime) : RechargeStart(startTime), RechargeEnd(startTime + rechargeTime) { } ChargeEntry(Clock::time_point startTime, Clock::time_point endTime) : RechargeStart(startTime), RechargeEnd(endTime) { } Clock::time_point RechargeStart; @@ -82,27 +85,26 @@ public: void Update(); void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr); - void HandleCooldowns(SpellInfo const* spellInfo, uint32 itemID, Spell* spell = nullptr); + void HandleCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = nullptr); bool IsReady(SpellInfo const* spellInfo, uint32 itemId = 0, bool ignoreCategoryCooldown = false) const; template<class PacketType> void WritePacket(PacketType* packet) const; // Cooldowns - static Clock::duration const InfinityCooldownDelay; // used for set "infinity cooldowns" for spells and check + static Duration const InfinityCooldownDelay; // used for set "infinity cooldowns" for spells and check - void StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = nullptr, bool onHold = false); + void StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = nullptr, bool onHold = false, Optional<Duration> forcedCooldown = {}); void SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId = 0, Spell* spell = nullptr, bool startCooldown = true); - template<class Type, class Period> - void AddCooldown(uint32 spellId, uint32 itemId, std::chrono::duration<Type, Period> cooldownDuration) + void AddCooldown(uint32 spellId, uint32 itemId, Duration cooldownDuration) { Clock::time_point now = GameTime::GetGameTimePoint<Clock>(); - AddCooldown(spellId, itemId, now + std::chrono::duration_cast<Clock::duration>(cooldownDuration), 0, now); + AddCooldown(spellId, itemId, now + cooldownDuration, 0, now); } void AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, uint32 categoryId, Clock::time_point categoryEnd, bool onHold = false); - void ModifyCooldown(uint32 spellId, Clock::duration cooldownMod); - void ModifyCooldown(SpellInfo const* spellInfo, Clock::duration cooldownMod); + void ModifyCooldown(uint32 spellId, Duration cooldownMod); + void ModifyCooldown(SpellInfo const* spellInfo, Duration cooldownMod); void ResetCooldown(uint32 spellId, bool update = false); void ResetCooldown(CooldownStorageType::iterator& itr, bool update = false); template<typename Predicate> @@ -128,15 +130,15 @@ public: void ResetAllCooldowns(); bool HasCooldown(SpellInfo const* spellInfo, uint32 itemId = 0, bool ignoreCategoryCooldown = false) const; bool HasCooldown(uint32 spellId, uint32 itemId = 0, bool ignoreCategoryCooldown = false) const; - uint32 GetRemainingCooldown(SpellInfo const* spellInfo) const; + Duration GetRemainingCooldown(SpellInfo const* spellInfo) const; // School lockouts - void LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime); + void LockSpellSchool(SpellSchoolMask schoolMask, Duration lockoutTime); bool IsSchoolLocked(SpellSchoolMask schoolMask) const; // Charges bool ConsumeCharge(uint32 chargeCategoryId); - void ModifyChargeRecoveryTime(uint32 chargeCategoryId, Clock::duration cooldownMod); + void ModifyChargeRecoveryTime(uint32 chargeCategoryId, Duration cooldownMod); void RestoreCharge(uint32 chargeCategoryId); void ResetCharges(uint32 chargeCategoryId); void ResetAllCharges(); @@ -146,7 +148,7 @@ public: // Global cooldown bool HasGlobalCooldown(SpellInfo const* spellInfo) const; - void AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration); + void AddGlobalCooldown(SpellInfo const* spellInfo, Duration duration); void CancelGlobalCooldown(SpellInfo const* spellInfo); void SaveCooldownStateBeforeDuel(); @@ -154,7 +156,7 @@ public: private: Player* GetPlayerOwner() const; - void ModifySpellCooldown(uint32 spellId, Clock::duration cooldownMod); + void ModifySpellCooldown(uint32 spellId, Duration cooldownMod); void SendClearCooldowns(std::vector<int32> const& cooldowns) const; CooldownStorageType::iterator EraseCooldown(CooldownStorageType::iterator itr) { @@ -164,7 +166,7 @@ private: void SendSetSpellCharges(uint32 chargeCategoryId, ChargeEntryCollection const& chargeCollection); - static void GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, int32* cooldown, uint32* categoryId, int32* categoryCooldown); + static void GetCooldownDurations(SpellInfo const* spellInfo, uint32 itemId, Duration* cooldown, uint32* categoryId, Duration* categoryCooldown); Unit* _owner; CooldownStorageType _spellCooldowns; diff --git a/src/server/scripts/World/duel_reset.cpp b/src/server/scripts/World/duel_reset.cpp index 947d705cc80..7246abc8377 100644 --- a/src/server/scripts/World/duel_reset.cpp +++ b/src/server/scripts/World/duel_reset.cpp @@ -94,25 +94,32 @@ class DuelResetScript : public PlayerScript player->GetSpellHistory()->ResetCooldowns([player, onStartDuel](SpellHistory::CooldownStorageType::iterator itr) -> bool { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first, DIFFICULTY_NONE); - uint32 remainingCooldown = player->GetSpellHistory()->GetRemainingCooldown(spellInfo); - int32 totalCooldown = spellInfo->RecoveryTime; - int32 categoryCooldown = spellInfo->CategoryRecoveryTime; + Milliseconds remainingCooldown = player->GetSpellHistory()->GetRemainingCooldown(spellInfo); + Milliseconds totalCooldown = Milliseconds(spellInfo->RecoveryTime); + Milliseconds categoryCooldown = Milliseconds(spellInfo->CategoryRecoveryTime); - player->ApplySpellMod(spellInfo, SpellModOp::Cooldown, totalCooldown, nullptr); + auto applySpellMod = [&](Milliseconds& value) + { + int32 intValue = value.count(); + player->ApplySpellMod(spellInfo, SpellModOp::Cooldown, intValue, nullptr); + value = Milliseconds(intValue); + }; + + applySpellMod(totalCooldown); if (int32 cooldownMod = player->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN)) - totalCooldown += cooldownMod * IN_MILLISECONDS; + totalCooldown += Milliseconds(cooldownMod); if (!spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) - player->ApplySpellMod(spellInfo, SpellModOp::Cooldown, categoryCooldown, nullptr); + applySpellMod(categoryCooldown); - return remainingCooldown > 0 + return remainingCooldown > 0ms && !itr->second.OnHold - && Milliseconds(totalCooldown) < Minutes(10) - && Milliseconds(categoryCooldown) < Minutes(10) - && Milliseconds(remainingCooldown) < Minutes(10) - && (onStartDuel ? Milliseconds(totalCooldown - remainingCooldown) > Seconds(30) : true) - && (onStartDuel ? Milliseconds(categoryCooldown - remainingCooldown) > Seconds(30) : true); + && Milliseconds(totalCooldown) < 10min + && Milliseconds(categoryCooldown) < 10min + && Milliseconds(remainingCooldown) < 10min + && (onStartDuel ? totalCooldown - remainingCooldown > 30s : true) + && (onStartDuel ? categoryCooldown - remainingCooldown > 30s : true); }, true); // pet cooldowns |