mirror of
https://github.com/TrinityCore/TrinityCore.git
synced 2026-01-16 07:30:42 +01:00
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:
@@ -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));
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -572,6 +572,7 @@ namespace WorldPackets
|
||||
WorldPacket const* Write() override;
|
||||
|
||||
bool IsPet = false;
|
||||
bool WithoutCategoryCooldown = false;
|
||||
int32 DeltaTime = 0;
|
||||
int32 SpellID = 0;
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user