diff --git a/data/sql/updates/pending_db_characters/rev_1629988683345293500.sql b/data/sql/updates/pending_db_characters/rev_1629988683345293500.sql new file mode 100644 index 000000000..e5c3cf274 --- /dev/null +++ b/data/sql/updates/pending_db_characters/rev_1629988683345293500.sql @@ -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`; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index e24d3b6f8..88d3cb86c 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -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, " diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 2d928d6e2..8b232adb8 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -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) { diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 5a6d7651e..0c1c05e3f 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -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 diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 304ef11aa..1cb77bf96 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -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; diff --git a/src/server/game/Entities/Creature/CreatureData.h b/src/server/game/Entities/Creature/CreatureData.h index a42a12845..98743956c 100644 --- a/src/server/game/Entities/Creature/CreatureData.h +++ b/src/server/game/Entities/Creature/CreatureData.h @@ -417,6 +417,15 @@ struct TrainerSpellData [[nodiscard]] TrainerSpell const* Find(uint32 spell_id) const; }; -typedef std::map CreatureSpellCooldowns; +struct CreatureSpellCooldown +{ + CreatureSpellCooldown() : category(0), end(0) { } + CreatureSpellCooldown(uint16 categoryId, uint32 endTime) : category(categoryId), end(endTime) { } + + uint16 category; + uint32 end; +}; + +typedef std::map CreatureSpellCooldowns; #endif // AZEROTHCORE_CREATUREDATA_H diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 682968f54..8ba10e901 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -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); } } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 635d0cc05..572bc7481 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -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) diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 2a1419fd7..a60ca73bf 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -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; diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index 1629bbacf..a4db1a5e8 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -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); diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index d4cf9c550..aed309b97 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -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 diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 81f399993..0a30ab72c 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -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; diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 96bbbbf29..60135989d 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -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(); diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 95a3101ea..3182b4081 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -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*/) diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 60ab3d59b..679a0dcbf 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -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()); + } } } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 00a6be2ea..c21ec73db 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -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); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index 3576274ca..6c9aadfaf 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -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 diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp index bed76caf3..7337b3215 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp @@ -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); diff --git a/src/server/shared/DataStores/DBCStructure.h b/src/server/shared/DataStores/DBCStructure.h index 92a847f5d..d4ea07a19 100644 --- a/src/server/shared/DataStores/DBCStructure.h +++ b/src/server/shared/DataStores/DBCStructure.h @@ -13,6 +13,7 @@ #include "Util.h" #include #include +#include // 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 SpellCategorySet; -typedef std::map SpellCategoryStore; +typedef std::set> SpellCategorySet; +typedef std::unordered_map SpellCategoryStore; typedef std::set PetFamilySpellsSet; typedef std::map PetFamilySpellsStore;