mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-13 01:08:35 +00:00
fix(Core/Spells): several improvements to cooldowns (#7559)
- Reworked spell category cooldowns. - Implemented category cooldowns for pets. - Properly shows pet spell cooldowns in player's UI. - Corrected pet spell cooldowns with infinity duration. - Do not add/remove infinity spell cooldown on aura apply/remove if casted by item. - Closes #5263
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
INSERT INTO `version_db_characters` (`sql_rev`) VALUES ('1629988683345293500');
|
||||
|
||||
ALTER TABLE `character_spell_cooldown` ADD COLUMN `category` MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL AFTER `spell`;
|
||||
ALTER TABLE `pet_spell_cooldown` ADD COLUMN `category` MEDIUMINT UNSIGNED DEFAULT 0 NOT NULL AFTER `spell`;
|
||||
@@ -84,7 +84,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_MAILDATE, "SELECT MIN(deliver_time) FROM mail WHERE receiver = ? AND (checked & 1) = 0", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_SOCIALLIST, "SELECT friend, flags, note FROM character_social JOIN characters ON characters.guid = character_social.friend WHERE character_social.guid = ? AND deleteinfos_name IS NULL LIMIT 255", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_HOMEBIND, "SELECT mapId, zoneId, posX, posY, posZ FROM character_homebind WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time, needSend FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, category, item, time, needSend FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_ACHIEVEMENTS, "SELECT achievement, date FROM character_achievement WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_CHARACTER_CRITERIAPROGRESS, "SELECT criteria, counter, date FROM character_achievement_progress WHERE guid = ?", CONNECTION_ASYNC);
|
||||
@@ -535,11 +535,11 @@ void CharacterDatabaseConnection::DoPrepareStatements()
|
||||
PrepareStatement(CHAR_ADD_CHAR_PET_DECLINEDNAME, "INSERT INTO character_pet_declinedname (id, owner, genitive, dative, accusative, instrumental, prepositional) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, category, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PET_SPELLS, "DELETE FROM pet_spell WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PET_SPELL_COOLDOWNS, "DELETE FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, category, time) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_DEL_PET_SPELL_BY_SPELL, "DELETE FROM pet_spell WHERE guid = ? AND spell = ?", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PET_SPELL, "INSERT INTO pet_spell (guid, spell, active) VALUES (?, ?, ?)", CONNECTION_ASYNC);
|
||||
PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
|
||||
|
||||
@@ -395,7 +395,7 @@ void LoadDBCStores(const std::string& dataPath)
|
||||
|
||||
for (auto i : sSpellStore)
|
||||
if (i->Category)
|
||||
sSpellsByCategoryStore[i->Category].insert(i->Id);
|
||||
sSpellsByCategoryStore[i->Category].emplace(false, i->Id);
|
||||
|
||||
for (SkillRaceClassInfoEntry const* entry : sSkillRaceClassInfoStore)
|
||||
{
|
||||
|
||||
@@ -2547,9 +2547,12 @@ bool Creature::IsSpellProhibited(SpellSchoolMask idSchoolMask) const
|
||||
return false;
|
||||
}
|
||||
|
||||
void Creature::_AddCreatureSpellCooldown(uint32 spell_id, uint32 end_time)
|
||||
void Creature::_AddCreatureSpellCooldown(uint32 spell_id, uint16 categoryId, uint32 end_time)
|
||||
{
|
||||
m_CreatureSpellCooldowns[spell_id] = World::GetGameTimeMS() + end_time;
|
||||
CreatureSpellCooldown spellCooldown;
|
||||
spellCooldown.category = categoryId;
|
||||
spellCooldown.end = World::GetGameTimeMS() + end_time;
|
||||
m_CreatureSpellCooldowns[spell_id] = std::move(spellCooldown);
|
||||
}
|
||||
|
||||
void Creature::AddSpellCooldown(uint32 spell_id, uint32 /*itemid*/, uint32 end_time, bool /*needSendToClient*/, bool /*forceSendToSpectator*/)
|
||||
@@ -2561,33 +2564,31 @@ void Creature::AddSpellCooldown(uint32 spell_id, uint32 /*itemid*/, uint32 end_t
|
||||
// used in proc system, otherwise normal creature cooldown
|
||||
if (end_time)
|
||||
{
|
||||
_AddCreatureSpellCooldown(spellInfo->Id, end_time);
|
||||
_AddCreatureSpellCooldown(spellInfo->Id, 0, end_time);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 spellcooldown = spellInfo->RecoveryTime;
|
||||
uint32 categorycooldown = spellInfo->CategoryRecoveryTime;
|
||||
if(Player* modOwner = GetSpellModOwner())
|
||||
uint32 categoryId = spellInfo->GetCategory();
|
||||
uint32 categorycooldown = categoryId ? spellInfo->CategoryRecoveryTime : 0;
|
||||
if (Player* modOwner = GetSpellModOwner())
|
||||
{
|
||||
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, spellcooldown);
|
||||
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categorycooldown);
|
||||
}
|
||||
|
||||
if (spellcooldown)
|
||||
_AddCreatureSpellCooldown(spellInfo->Id, spellcooldown);
|
||||
|
||||
if (categorycooldown)
|
||||
if (spellInfo->GetCategory())
|
||||
SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(categoryId);
|
||||
if (categorycooldown && i_scstore != sSpellsByCategoryStore.end())
|
||||
{
|
||||
for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
|
||||
{
|
||||
SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(spellInfo->GetCategory());
|
||||
if (i_scstore != sSpellsByCategoryStore.end())
|
||||
{
|
||||
uint32 cattime = categorycooldown;
|
||||
for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
|
||||
if (GetSpellCooldown(*i_scset) < cattime)
|
||||
_AddCreatureSpellCooldown(*i_scset, cattime);
|
||||
}
|
||||
_AddCreatureSpellCooldown(i_scset->second, categoryId, categorycooldown);
|
||||
}
|
||||
}
|
||||
else if (spellcooldown)
|
||||
{
|
||||
_AddCreatureSpellCooldown(spellInfo->Id, 0, spellcooldown);
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Creature::GetSpellCooldown(uint32 spell_id) const
|
||||
@@ -2596,13 +2597,13 @@ uint32 Creature::GetSpellCooldown(uint32 spell_id) const
|
||||
if (itr == m_CreatureSpellCooldowns.end())
|
||||
return 0;
|
||||
|
||||
return itr->second > World::GetGameTimeMS() ? itr->second - World::GetGameTimeMS() : 0;
|
||||
return itr->second.end > World::GetGameTimeMS() ? itr->second.end - World::GetGameTimeMS() : 0;
|
||||
}
|
||||
|
||||
bool Creature::HasSpellCooldown(uint32 spell_id) const
|
||||
{
|
||||
CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id);
|
||||
return (itr != m_CreatureSpellCooldowns.end() && itr->second > World::GetGameTimeMS());
|
||||
return (itr != m_CreatureSpellCooldowns.end() && itr->second.end > World::GetGameTimeMS());
|
||||
}
|
||||
|
||||
bool Creature::HasSpell(uint32 spellID) const
|
||||
|
||||
@@ -142,7 +142,7 @@ public:
|
||||
[[nodiscard]] SpellSchoolMask GetMeleeDamageSchoolMask() const override { return m_meleeDamageSchoolMask; }
|
||||
void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); }
|
||||
|
||||
void _AddCreatureSpellCooldown(uint32 spell_id, uint32 end_time);
|
||||
void _AddCreatureSpellCooldown(uint32 spell_id, uint16 categoryId, uint32 end_time);
|
||||
void AddSpellCooldown(uint32 spell_id, uint32 /*itemid*/, uint32 end_time, bool needSendToClient = false, bool forceSendToSpectator = false) override;
|
||||
[[nodiscard]] bool HasSpellCooldown(uint32 spell_id) const override;
|
||||
[[nodiscard]] uint32 GetSpellCooldown(uint32 spell_id) const;
|
||||
|
||||
@@ -417,6 +417,15 @@ struct TrainerSpellData
|
||||
[[nodiscard]] TrainerSpell const* Find(uint32 spell_id) const;
|
||||
};
|
||||
|
||||
typedef std::map<uint32, time_t> CreatureSpellCooldowns;
|
||||
struct CreatureSpellCooldown
|
||||
{
|
||||
CreatureSpellCooldown() : category(0), end(0) { }
|
||||
CreatureSpellCooldown(uint16 categoryId, uint32 endTime) : category(categoryId), end(endTime) { }
|
||||
|
||||
uint16 category;
|
||||
uint32 end;
|
||||
};
|
||||
|
||||
typedef std::map<uint32, CreatureSpellCooldown> CreatureSpellCooldowns;
|
||||
|
||||
#endif // AZEROTHCORE_CREATUREDATA_H
|
||||
|
||||
@@ -1147,7 +1147,8 @@ void Pet::_LoadSpellCooldowns(PreparedQueryResult result)
|
||||
Field* fields = result->Fetch();
|
||||
|
||||
uint32 spell_id = fields[0].GetUInt32();
|
||||
time_t db_time = time_t(fields[1].GetUInt32());
|
||||
uint16 category = fields[1].GetUInt16();
|
||||
time_t db_time = time_t(fields[2].GetUInt32());
|
||||
|
||||
if (!sSpellMgr->GetSpellInfo(spell_id))
|
||||
{
|
||||
@@ -1161,7 +1162,7 @@ void Pet::_LoadSpellCooldowns(PreparedQueryResult result)
|
||||
|
||||
uint32 cooldown = (db_time - curTime) * IN_MILLISECONDS;
|
||||
cooldowns[spell_id] = cooldown;
|
||||
_AddCreatureSpellCooldown(spell_id, cooldown);
|
||||
_AddCreatureSpellCooldown(spell_id, category, cooldown);
|
||||
|
||||
LOG_DEBUG("entities.pet", "Pet (Number: %u) spell %u cooldown loaded (%u secs).", m_charmInfo->GetPetNumber(), spell_id, uint32(db_time - curTime));
|
||||
} while (result->NextRow());
|
||||
@@ -1181,7 +1182,8 @@ void Pet::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout)
|
||||
trans->Append(stmt);
|
||||
|
||||
time_t curTime = time(nullptr);
|
||||
uint32 checkTime = World::GetGameTimeMS() + 30 * IN_MILLISECONDS;
|
||||
uint32 curMSTime = World::GetGameTimeMS();
|
||||
uint32 infTime = curMSTime + infinityCooldownDelayCheck;
|
||||
|
||||
// remove oudated and save active
|
||||
CreatureSpellCooldowns::iterator itr, itr2;
|
||||
@@ -1189,15 +1191,19 @@ void Pet::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout)
|
||||
{
|
||||
itr2 = itr;
|
||||
++itr;
|
||||
if (itr2->second <= World::GetGameTimeMS() + 1000)
|
||||
m_CreatureSpellCooldowns.erase(itr2);
|
||||
else if (logout || itr2->second > checkTime)
|
||||
|
||||
if (itr2->second.end <= curMSTime + 1000)
|
||||
{
|
||||
uint32 cooldown = ((itr2->second - World::GetGameTimeMS()) / IN_MILLISECONDS) + curTime;
|
||||
m_CreatureSpellCooldowns.erase(itr2);
|
||||
}
|
||||
else if (itr->second.end <= infTime && (logout || itr->second.end > (curMSTime + 30 * IN_MILLISECONDS)))
|
||||
{
|
||||
uint32 cooldown = ((itr2->second.end - curMSTime) / IN_MILLISECONDS) + curTime;
|
||||
stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_SPELL_COOLDOWN);
|
||||
stmt->setUInt32(0, m_charmInfo->GetPetNumber());
|
||||
stmt->setUInt32(1, itr2->first);
|
||||
stmt->setUInt32(2, cooldown);
|
||||
stmt->setUInt16(2, itr2->second.category);
|
||||
stmt->setUInt32(3, cooldown);
|
||||
trans->Append(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2707,7 +2707,7 @@ void Player::SendInitialSpells()
|
||||
data << uint32(itr->first);
|
||||
|
||||
data << uint16(itr->second.itemid); // cast item id
|
||||
data << uint16(sEntry->GetCategory()); // spell category
|
||||
data << uint16(itr->second.category); // spell category
|
||||
|
||||
// send infinity cooldown in special format
|
||||
if (itr->second.end >= infTime)
|
||||
@@ -2718,17 +2718,8 @@ void Player::SendInitialSpells()
|
||||
}
|
||||
|
||||
uint32 cooldown = itr->second.end > curTime ? itr->second.end - curTime : 0;
|
||||
|
||||
if (!sEntry->GetCategory() || (sEntry->CategoryRecoveryTime != sEntry->RecoveryTime && sEntry->RecoveryTime && sEntry->CategoryRecoveryTime)) // may be wrong, but anyway better than nothing...
|
||||
{
|
||||
data << cooldown; // cooldown
|
||||
data << uint32(0); // category cooldown
|
||||
}
|
||||
else
|
||||
{
|
||||
data << uint32(0); // cooldown
|
||||
data << cooldown; // category cooldown
|
||||
}
|
||||
data << uint32(itr->second.category ? 0 : cooldown); // cooldown
|
||||
data << uint32(itr->second.category ? cooldown : 0); // category cooldown
|
||||
}
|
||||
|
||||
GetSession()->SendPacket(&data);
|
||||
@@ -3406,13 +3397,12 @@ void Player::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */)
|
||||
SendClearCooldown(spell_id, this);
|
||||
}
|
||||
|
||||
// I am not sure which one is more efficient
|
||||
void Player::RemoveCategoryCooldown(uint32 cat)
|
||||
{
|
||||
SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(cat);
|
||||
if (i_scstore != sSpellsByCategoryStore.end())
|
||||
for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
|
||||
RemoveSpellCooldown(*i_scset, true);
|
||||
RemoveSpellCooldown(i_scset->second, true);
|
||||
}
|
||||
|
||||
void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns)
|
||||
@@ -3467,7 +3457,7 @@ void Player::_LoadSpellCooldowns(PreparedQueryResult result)
|
||||
{
|
||||
// some cooldowns can be already set at aura loading...
|
||||
|
||||
//QueryResult* result = CharacterDatabase.PQuery("SELECT spell, item, time FROM character_spell_cooldown WHERE guid = '%u'", GetGUID().GetCounter()());
|
||||
//QueryResult* result = CharacterDatabase.PQuery("SELECT spell, category, item, time FROM character_spell_cooldown WHERE guid = '%u'", GetGUID().GetCounter()());
|
||||
|
||||
if (result)
|
||||
{
|
||||
@@ -3477,9 +3467,10 @@ void Player::_LoadSpellCooldowns(PreparedQueryResult result)
|
||||
{
|
||||
Field* fields = result->Fetch();
|
||||
uint32 spell_id = fields[0].GetUInt32();
|
||||
uint32 item_id = fields[1].GetUInt32();
|
||||
uint32 db_time = fields[2].GetUInt32();
|
||||
bool needSend = fields[3].GetBool();
|
||||
uint16 category = fields[1].GetUInt16();
|
||||
uint32 item_id = fields[2].GetUInt32();
|
||||
uint32 db_time = fields[3].GetUInt32();
|
||||
bool needSend = fields[4].GetBool();
|
||||
|
||||
if (!sSpellMgr->GetSpellInfo(spell_id))
|
||||
{
|
||||
@@ -3491,7 +3482,7 @@ void Player::_LoadSpellCooldowns(PreparedQueryResult result)
|
||||
if (db_time <= curTime)
|
||||
continue;
|
||||
|
||||
AddSpellCooldown(spell_id, item_id, (db_time - curTime)*IN_MILLISECONDS, needSend);
|
||||
_AddSpellCooldown(spell_id, category, item_id, (db_time - curTime) * IN_MILLISECONDS, needSend);
|
||||
|
||||
LOG_DEBUG("entities.player.loading", "Player (%s) spell %u, item %u cooldown loaded (%u secs).", GetGUID().ToString().c_str(), spell_id, item_id, uint32(db_time - curTime));
|
||||
} while (result->NextRow());
|
||||
@@ -3527,7 +3518,7 @@ void Player::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout
|
||||
{
|
||||
if (first_round)
|
||||
{
|
||||
ss << "INSERT INTO character_spell_cooldown (guid, spell, item, time, needSend) VALUES ";
|
||||
ss << "INSERT INTO character_spell_cooldown (guid, spell, category, item, time, needSend) VALUES ";
|
||||
first_round = false;
|
||||
}
|
||||
// next new/changed record prefix
|
||||
@@ -3535,7 +3526,7 @@ void Player::_SaveSpellCooldowns(CharacterDatabaseTransaction trans, bool logout
|
||||
ss << ',';
|
||||
|
||||
uint64 cooldown = uint64(((itr->second.end - curMSTime) / IN_MILLISECONDS) + curTime);
|
||||
ss << '(' << GetGUID().GetCounter() << ',' << itr->first << ',' << itr->second.itemid << ',' << cooldown << ',' << (itr->second.needSendToClient ? '1' : '0') << ')';
|
||||
ss << '(' << GetGUID().GetCounter() << ',' << itr->first << ',' << itr->second.category << "," << itr->second.itemid << ',' << cooldown << ',' << (itr->second.needSendToClient ? '1' : '0') << ')';
|
||||
++itr;
|
||||
}
|
||||
else
|
||||
@@ -8862,15 +8853,26 @@ void Player::PetSpellInitialize()
|
||||
data << uint8(cooldownsCount);
|
||||
|
||||
uint32 curTime = World::GetGameTimeMS();
|
||||
uint32 infTime = World::GetGameTimeMS() + infinityCooldownDelayCheck;
|
||||
|
||||
for (CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr)
|
||||
{
|
||||
uint32 cooldown = (itr->second > curTime) ? itr->second - curTime : 0;
|
||||
uint16 category = itr->second.category;
|
||||
uint32 cooldown = (itr->second.end > curTime) ? itr->second.end - curTime : 0;
|
||||
|
||||
data << uint32(itr->first); // spellid
|
||||
data << uint16(0); // spell category?
|
||||
data << uint32(cooldown); // cooldown
|
||||
data << uint32(0); // category cooldown
|
||||
data << uint16(itr->second.category); // spell category
|
||||
|
||||
// send infinity cooldown in special format
|
||||
if (itr->second.end >= infTime)
|
||||
{
|
||||
data << uint32(1); // cooldown
|
||||
data << uint32(0x80000000); // category cooldown
|
||||
continue;
|
||||
}
|
||||
|
||||
data << uint32(category ? 0 : cooldown); // cooldown
|
||||
data << uint32(category ? cooldown : 0); // category cooldown
|
||||
}
|
||||
|
||||
GetSession()->SendPacket(&data);
|
||||
@@ -8954,14 +8956,26 @@ void Player::VehicleSpellInitialize()
|
||||
data << uint8(cooldownCount);
|
||||
|
||||
uint32 curTime = World::GetGameTimeMS();
|
||||
uint32 infTime = World::GetGameTimeMS() + infinityCooldownDelayCheck;
|
||||
|
||||
for (CreatureSpellCooldowns::const_iterator itr = vehicle->m_CreatureSpellCooldowns.begin(); itr != vehicle->m_CreatureSpellCooldowns.end(); ++itr)
|
||||
{
|
||||
uint32 cooldown = (itr->second > curTime) ? itr->second - curTime : 0;
|
||||
data << uint32(itr->first); // SpellId
|
||||
data << uint16(0); // unk
|
||||
data << uint32(cooldown); // spell cooldown
|
||||
data << uint32(0); // category cooldown
|
||||
uint16 category = itr->second.category;
|
||||
uint32 cooldown = (itr->second.end > curTime) ? itr->second.end - curTime : 0;
|
||||
|
||||
data << uint32(itr->first); // spellid
|
||||
data << uint16(itr->second.category); // spell category
|
||||
|
||||
// send infinity cooldown in special format
|
||||
if (itr->second.end >= infTime)
|
||||
{
|
||||
data << uint32(1); // cooldown
|
||||
data << uint32(0x80000000); // category cooldown
|
||||
continue;
|
||||
}
|
||||
|
||||
data << uint32(category ? 0 : cooldown); // cooldown
|
||||
data << uint32(category ? cooldown : 0); // category cooldown
|
||||
}
|
||||
|
||||
GetSession()->SendPacket(&data);
|
||||
@@ -10152,59 +10166,79 @@ void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 ite
|
||||
recTime = rec ? rec : catrecTime;
|
||||
}
|
||||
|
||||
// self spell cooldown
|
||||
if (recTime > 0)
|
||||
{
|
||||
AddSpellCooldown(spellInfo->Id, itemId, recTime, true, true);
|
||||
|
||||
if (needsCooldownPacket)
|
||||
{
|
||||
WorldPacket data;
|
||||
BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, rec);
|
||||
SendDirectMessage(&data);
|
||||
}
|
||||
}
|
||||
|
||||
// category spells
|
||||
if (cat && catrec > 0)
|
||||
{
|
||||
_AddSpellCooldown(spellInfo->Id, cat, itemId, catrecTime, true, true);
|
||||
if (needsCooldownPacket)
|
||||
{
|
||||
WorldPacket data;
|
||||
BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, catrecTime);
|
||||
SendDirectMessage(&data);
|
||||
}
|
||||
|
||||
SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(cat);
|
||||
if (i_scstore != sSpellsByCategoryStore.end())
|
||||
{
|
||||
for (SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
|
||||
{
|
||||
if (*i_scset == spellInfo->Id) // skip main spell, already handled above
|
||||
continue;
|
||||
|
||||
// Only within the same spellfamily
|
||||
SpellInfo const* categorySpellInfo = sSpellMgr->GetSpellInfo(*i_scset);
|
||||
if (!categorySpellInfo || categorySpellInfo->SpellFamilyName != spellInfo->SpellFamilyName)
|
||||
if (i_scset->second == spellInfo->Id) // skip main spell, already handled above
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
AddSpellCooldown(*i_scset, itemId, catrecTime, !spellInfo->IsCooldownStartedOnEvent() && spellInfo->CategoryRecoveryTime != spellInfo->RecoveryTime && spellInfo->RecoveryTime && spellInfo->CategoryRecoveryTime); // Xinef: send category cooldowns on login if category cooldown is different from base cooldown
|
||||
// If spell category is applied by item, then other spells should be exists in item templates
|
||||
if ((itemId > 0) != i_scset->first)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_AddSpellCooldown(i_scset->second, cat, itemId, catrecTime, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// self spell cooldown
|
||||
if (recTime > 0)
|
||||
{
|
||||
_AddSpellCooldown(spellInfo->Id, 0, itemId, recTime, true, true);
|
||||
|
||||
if (needsCooldownPacket)
|
||||
{
|
||||
WorldPacket data;
|
||||
BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, rec);
|
||||
SendDirectMessage(&data);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, uint32 end_time, bool needSendToClient, bool forceSendToSpectator)
|
||||
void Player::_AddSpellCooldown(uint32 spellid, uint16 categoryId, uint32 itemid, uint32 end_time, bool needSendToClient, bool forceSendToSpectator)
|
||||
{
|
||||
SpellCooldown sc;
|
||||
sc.end = World::GetGameTimeMS() + end_time;
|
||||
sc.category = categoryId;
|
||||
sc.itemid = itemid;
|
||||
sc.maxduration = end_time;
|
||||
sc.sendToSpectator = false;
|
||||
sc.needSendToClient = needSendToClient;
|
||||
|
||||
if (end_time >= SPECTATOR_COOLDOWN_MIN * IN_MILLISECONDS && end_time <= SPECTATOR_COOLDOWN_MAX * IN_MILLISECONDS)
|
||||
{
|
||||
if (NeedSendSpectatorData() && forceSendToSpectator && (itemid || HasActiveSpell(spellid)))
|
||||
{
|
||||
sc.sendToSpectator = true;
|
||||
ArenaSpectator::SendCommand_Cooldown(FindMap(), GetGUID(), "ACD", spellid, end_time / IN_MILLISECONDS, end_time / IN_MILLISECONDS);
|
||||
}
|
||||
m_spellCooldowns[spellid] = sc;
|
||||
}
|
||||
|
||||
m_spellCooldowns[spellid] = std::move(sc);
|
||||
}
|
||||
|
||||
void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, uint32 end_time, bool needSendToClient, bool forceSendToSpectator)
|
||||
{
|
||||
_AddSpellCooldown(spellid, 0, itemid, end_time, needSendToClient, forceSendToSpectator);
|
||||
}
|
||||
|
||||
void Player::ModifySpellCooldown(uint32 spellId, int32 cooldown)
|
||||
|
||||
@@ -182,6 +182,7 @@ typedef GuidList WhisperListContainer;
|
||||
struct SpellCooldown
|
||||
{
|
||||
uint32 end;
|
||||
uint16 category;
|
||||
uint32 itemid;
|
||||
uint32 maxduration;
|
||||
bool sendToSpectator: 1;
|
||||
@@ -1655,8 +1656,6 @@ public:
|
||||
void DropModCharge(SpellModifier* mod, Spell* spell);
|
||||
void SetSpellModTakingSpell(Spell* spell, bool apply);
|
||||
|
||||
static uint32 const infinityCooldownDelay = 0x9A7EC800; // used for set "infinity cooldowns" for spells and check, MONTH*IN_MILLISECONDS
|
||||
static uint32 const infinityCooldownDelayCheck = 0x4D3F6400; //MONTH*IN_MILLISECONDS/2;
|
||||
[[nodiscard]] bool HasSpellCooldown(uint32 spell_id) const override
|
||||
{
|
||||
SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id);
|
||||
@@ -1674,6 +1673,7 @@ public:
|
||||
}
|
||||
void AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = nullptr, bool infinityCooldown = false);
|
||||
void AddSpellCooldown(uint32 spell_id, uint32 itemid, uint32 end_time, bool needSendToClient = false, bool forceSendToSpectator = false) override;
|
||||
void _AddSpellCooldown(uint32 spell_id, uint16 categoryId, uint32 itemid, uint32 end_time, bool needSendToClient = false, bool forceSendToSpectator = false);
|
||||
void ModifySpellCooldown(uint32 spellId, int32 cooldown);
|
||||
void SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId = 0, Spell* spell = nullptr, bool setCooldown = true);
|
||||
void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override;
|
||||
|
||||
@@ -1489,8 +1489,8 @@ void Player::UpdatePotionCooldown(Spell* spell)
|
||||
{
|
||||
if (spell->IsIgnoringCooldowns())
|
||||
return;
|
||||
else
|
||||
SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell);
|
||||
|
||||
SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell);
|
||||
}
|
||||
|
||||
SetLastPotionId(0);
|
||||
|
||||
@@ -248,6 +248,9 @@ enum UnitRename
|
||||
};
|
||||
|
||||
static constexpr uint32 MAX_CREATURE_SPELLS = 8;
|
||||
static constexpr uint32 infinityCooldownDelay = 0x9A7EC800; // used for set "infinity cooldowns" for spells and check, MONTH*IN_MILLISECONDS
|
||||
static constexpr uint32 infinityCooldownDelayCheck = 0x4D3F6400; // MONTH*IN_MILLISECONDS/2;
|
||||
|
||||
#define MAX_SPELL_CHARM 4
|
||||
#define MAX_SPELL_VEHICLE 6
|
||||
#define MAX_SPELL_POSSESS 8
|
||||
|
||||
@@ -2913,15 +2913,13 @@ void ObjectMgr::LoadItemTemplates()
|
||||
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
|
||||
if (itemTemplate.Spells[i].SpellId && itemTemplate.Spells[i].SpellCategory && itemTemplate.Spells[i].SpellCategoryCooldown)
|
||||
{
|
||||
SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(itemTemplate.Spells[i].SpellCategory);
|
||||
SpellCategoryStore::iterator ct = sSpellsByCategoryStore.find(itemTemplate.Spells[i].SpellCategory);
|
||||
if (ct != sSpellsByCategoryStore.end())
|
||||
{
|
||||
const SpellCategorySet& ct_set = ct->second;
|
||||
if (ct_set.find(itemTemplate.Spells[i].SpellId) == ct_set.end())
|
||||
sSpellsByCategoryStore[itemTemplate.Spells[i].SpellCategory].insert(itemTemplate.Spells[i].SpellId);
|
||||
ct->second.emplace(true, itemTemplate.Spells[i].SpellId);
|
||||
}
|
||||
else
|
||||
sSpellsByCategoryStore[itemTemplate.Spells[i].SpellCategory].insert(itemTemplate.Spells[i].SpellId);
|
||||
sSpellsByCategoryStore[itemTemplate.Spells[i].SpellCategory].emplace(true, itemTemplate.Spells[i].SpellId);
|
||||
}
|
||||
|
||||
++count;
|
||||
|
||||
@@ -733,7 +733,7 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe
|
||||
|
||||
if (result == SPELL_CAST_OK)
|
||||
{
|
||||
pet->ToCreature()->AddSpellCooldown(spellid, 0, 0);
|
||||
pet->ToCreature()->AddSpellCooldown(spellid, 0, spellInfo->IsCooldownStartedOnEvent() ? infinityCooldownDelay : 0);
|
||||
|
||||
unit_target = spell->m_targets.GetUnitTarget();
|
||||
|
||||
|
||||
@@ -547,7 +547,15 @@ void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket)
|
||||
|
||||
pet->RemoveOwnedAura(spellId, ObjectGuid::Empty, 0, AURA_REMOVE_BY_CANCEL);
|
||||
|
||||
pet->AddSpellCooldown(spellId, 0, 0);
|
||||
if (spellInfo->IsCooldownStartedOnEvent())
|
||||
{
|
||||
pet->AddSpellCooldown(spellId, 0, 0);
|
||||
|
||||
WorldPacket data(SMSG_COOLDOWN_EVENT, 4 + 8);
|
||||
data << uint32(spellInfo->Id);
|
||||
data << pet->GetGUID();
|
||||
_player->SendDirectMessage(&data);
|
||||
}
|
||||
}
|
||||
|
||||
void WorldSession::HandleCancelGrowthAuraOpcode(WorldPacket& /*recvPacket*/)
|
||||
|
||||
@@ -485,10 +485,9 @@ void Aura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication* auraApp)
|
||||
// set infinity cooldown state for spells
|
||||
if (caster && caster->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (m_spellInfo->IsCooldownStartedOnEvent())
|
||||
if (m_spellInfo->IsCooldownStartedOnEvent() && !m_castItemGuid)
|
||||
{
|
||||
Item* castItem = m_castItemGuid ? caster->ToPlayer()->GetItemByGuid(m_castItemGuid) : nullptr;
|
||||
caster->ToPlayer()->AddSpellAndCategoryCooldowns(m_spellInfo, castItem ? castItem->GetEntry() : 0, nullptr, true);
|
||||
caster->ToPlayer()->AddSpellAndCategoryCooldowns(m_spellInfo, 0, nullptr, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -518,9 +517,11 @@ void Aura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication* auraAp
|
||||
// reset cooldown state for spells
|
||||
if (caster && caster->GetTypeId() == TYPEID_PLAYER)
|
||||
{
|
||||
if (GetSpellInfo()->IsCooldownStartedOnEvent())
|
||||
if (GetSpellInfo()->IsCooldownStartedOnEvent() && !m_castItemGuid)
|
||||
{
|
||||
// note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases)
|
||||
caster->ToPlayer()->SendCooldownEvent(GetSpellInfo());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -949,9 +949,10 @@ void Spell::EffectTriggerSpell(SpellEffIndex effIndex)
|
||||
}
|
||||
|
||||
// Remove spell cooldown (not category) if spell triggering spell with cooldown and same category
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime
|
||||
&& m_spellInfo->GetCategory() == spellInfo->GetCategory())
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER && spellInfo->CategoryRecoveryTime && m_spellInfo->GetCategory() == spellInfo->GetCategory())
|
||||
{
|
||||
m_caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id);
|
||||
}
|
||||
|
||||
// original caster guid only for GO cast
|
||||
m_caster->CastSpell(targets, spellInfo, &values, TriggerCastFlags(TRIGGERED_FULL_MASK & ~TRIGGERED_NO_PERIODIC_RESET), nullptr, nullptr, m_originalCasterGUID);
|
||||
@@ -1002,9 +1003,10 @@ void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex)
|
||||
}
|
||||
|
||||
// Remove spell cooldown (not category) if spell triggering spell with cooldown and same category
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime
|
||||
&& m_spellInfo->GetCategory() == spellInfo->GetCategory())
|
||||
if (m_caster->GetTypeId() == TYPEID_PLAYER && spellInfo->CategoryRecoveryTime && m_spellInfo->GetCategory() == spellInfo->GetCategory())
|
||||
{
|
||||
m_caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id);
|
||||
}
|
||||
|
||||
// original caster guid only for GO cast
|
||||
m_caster->CastSpell(targets, spellInfo, &values, TRIGGERED_FULL_MASK, nullptr, nullptr, m_originalCasterGUID);
|
||||
|
||||
@@ -1451,7 +1451,7 @@ void TriggerBurningPitch(Creature* c)
|
||||
if (!c->HasSpellCooldown(spellId))
|
||||
{
|
||||
c->CastSpell((Unit*)nullptr, spellId, false);
|
||||
c->_AddCreatureSpellCooldown(spellId, urand(3000, 4000));
|
||||
c->_AddCreatureSpellCooldown(spellId, 0, urand(3000, 4000));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1917,7 +1917,7 @@ public:
|
||||
return;
|
||||
|
||||
me->CastSpell((Unit*)nullptr, spellId, true);
|
||||
me->_AddCreatureSpellCooldown(spellId, 9000);
|
||||
me->_AddCreatureSpellCooldown(spellId, 0, 9000);
|
||||
}
|
||||
|
||||
bool CanAIAttack(const Unit* /*target*/) const override
|
||||
|
||||
@@ -304,8 +304,8 @@ public:
|
||||
case EVENT_SPELL_ROAR:
|
||||
Talk(EMOTE_ROAR);
|
||||
|
||||
me->_AddCreatureSpellCooldown(SPELL_STAGGERING_ROAR, 0);
|
||||
me->_AddCreatureSpellCooldown(SPELL_DREADFUL_ROAR, 0);
|
||||
me->_AddCreatureSpellCooldown(SPELL_STAGGERING_ROAR, 0, 0);
|
||||
me->_AddCreatureSpellCooldown(SPELL_DREADFUL_ROAR, 0, 0);
|
||||
|
||||
if (me->GetDisplayId() == DISPLAYID_DEFAULT)
|
||||
me->CastSpell((Unit*)nullptr, SPELL_STAGGERING_ROAR, false);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "Util.h"
|
||||
#include <set>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
|
||||
// Structures using to access raw DBC data and required packing to portability
|
||||
|
||||
@@ -1658,8 +1659,8 @@ struct SpellEntry
|
||||
//uint32 SpellDifficultyId; // 233 3.3.0
|
||||
};
|
||||
|
||||
typedef std::set<uint32> SpellCategorySet;
|
||||
typedef std::map<uint32, SpellCategorySet > SpellCategoryStore;
|
||||
typedef std::set<std::pair<bool, uint32>> SpellCategorySet;
|
||||
typedef std::unordered_map<uint32, SpellCategorySet> SpellCategoryStore;
|
||||
typedef std::set<uint32> PetFamilySpellsSet;
|
||||
typedef std::map<uint32, PetFamilySpellsSet > PetFamilySpellsStore;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user