aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2021-11-20 00:28:48 +0100
committerShauren <shauren.trinity@gmail.com>2021-11-20 00:28:48 +0100
commit8cc6520b89583d4bf6549b28fa5b7390fc2b0b9d (patch)
tree708be5e5c81e611a9d0c4afb0b59c86eeefd4b7e /src
parentd4d47b52e3f68f9042f582a2a4c29b92bbb1e260 (diff)
Core/Spells: SpellHistory updates
* Add duration override argument to StartCooldown (for cooldowns sent to client) * Research new SMSG_SPELL_COOLDOWN flags * Send interrupt school lockouts with newly defined SPELL_COOLDOWN_FLAG_LOSS_OF_CONTROL_UI * Fixed packet structure of SMSG_MODIFY_COOLDOWN * std::chorno-ification
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Player/Player.cpp4
-rw-r--r--src/server/game/Server/Packets/SpellPackets.cpp1
-rw-r--r--src/server/game/Server/Packets/SpellPackets.h1
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp4
-rw-r--r--src/server/game/Spells/Spell.cpp33
-rw-r--r--src/server/game/Spells/SpellEffects.cpp3
-rw-r--r--src/server/game/Spells/SpellHistory.cpp212
-rw-r--r--src/server/game/Spells/SpellHistory.h40
-rw-r--r--src/server/scripts/World/duel_reset.cpp31
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