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
This commit is contained in:
Shauren
2021-11-20 00:28:48 +01:00
parent d4d47b52e3
commit 8cc6520b89
9 changed files with 187 additions and 152 deletions

View File

@@ -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));

View File

@@ -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;

View File

@@ -572,6 +572,7 @@ namespace WorldPackets
WorldPacket const* Write() override;
bool IsPet = false;
bool WithoutCategoryCooldown = false;
int32 DeltaTime = 0;
int32 SpellID = 0;
};

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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);
}
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 (_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 (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))
// Now we have cooldown data (if found any), time to apply mods
if (Player* modOwner = _owner->GetSpellModOwner())
{
needsCooldownPacket = true;
cooldown += cooldownMod * IN_MILLISECONDS; // SPELL_AURA_MOD_COOLDOWN does not affect category cooldows, verified with shaman shocks
}
}
auto applySpellMod = [&](Milliseconds& value)
{
int32 intValue = value.count();
modOwner->ApplySpellMod(spellInfo, SpellModOp::Cooldown, intValue, spell);
value = Milliseconds(intValue);
};
// 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 (cooldown >= Duration::zero())
applySpellMod(cooldown);
if (categoryCooldown > 0)
categoryCooldown += categoryModifier;
if (categoryCooldown >= Clock::duration::zero() && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS))
applySpellMod(categoryCooldown);
}
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());
if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE, spellInfo))
{
cooldown = Duration(int64(cooldown.count() * _owner->m_unitData->ModSpellHaste));
categoryCooldown = Duration(int64(categoryCooldown.count() * _owner->m_unitData->ModSpellHaste));
}
if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN, spellInfo))
{
cooldown = Duration(int64(cooldown.count() * _owner->m_unitData->ModHasteRegen));
categoryCooldown = Duration(int64(categoryCooldown.count() * _owner->m_unitData->ModHasteRegen));
}
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
}
}
// 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)

View File

@@ -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;

View File

@@ -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