diff options
Diffstat (limited to 'src')
74 files changed, 2062 insertions, 1542 deletions
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 3e2087cd017..7d36fd9de67 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -29,6 +29,7 @@ #include "Util.h" #include "Group.h" #include "SpellInfo.h" +#include "SpellHistory.h" int PetAI::Permissible(const Creature* creature) { @@ -147,7 +148,7 @@ void PetAI::UpdateAI(uint32 diff) if (!spellInfo) continue; - if (me->GetCharmInfo() && me->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(spellInfo)) + if (me->GetCharmInfo() && me->GetSpellHistory()->HasGlobalCooldown(spellInfo)) continue; if (spellInfo->IsPositive()) @@ -155,7 +156,7 @@ void PetAI::UpdateAI(uint32 diff) if (spellInfo->CanBeUsedInCombat()) { // check spell cooldown - if (me->HasSpellCooldown(spellInfo->Id)) + if (!me->GetSpellHistory()->IsReady(spellInfo)) continue; // Check if we're in combat or commanded to attack diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index e9d52a5539d..4932a83d665 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -733,7 +733,7 @@ void AchievementMgr<Guild>::LoadFromDB(PreparedQueryResult achievementResult, Pr { Field* fields = criteriaResult->Fetch(); uint32 id = fields[0].GetUInt32(); - uint32 counter = fields[1].GetUInt32(); + uint64 counter = fields[1].GetUInt64(); time_t date = time_t(fields[2].GetUInt32()); ObjectGuid::LowType guid = fields[3].GetUInt64(); diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 875505425f5..fed1c1e60b3 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -37,6 +37,7 @@ DB2Storage<ItemModifiedAppearanceEntry> sItemModifiedAppearanceStore(ItemMod DB2Storage<ItemSparseEntry> sItemSparseStore(ItemSparsefmt); DB2Storage<ItemXBonusTreeEntry> sItemXBonusTreeStore(ItemXBonusTreeEntryfmt); DB2Storage<KeyChainEntry> sKeyChainStore(KeyChainfmt); +DB2Storage<MountEntry> sMountStore(Mountfmt); DB2Storage<OverrideSpellDataEntry> sOverrideSpellDataStore(OverrideSpellDataEntryfmt); DB2Storage<PhaseGroupEntry> sPhaseGroupStore(PhaseGroupEntryfmt); DB2Storage<SpellAuraRestrictionsEntry> sSpellAuraRestrictionsStore(SpellAuraRestrictionsEntryfmt); @@ -138,6 +139,7 @@ void DB2Manager::LoadStores(std::string const& dataPath) LoadDB2(availableDb2Locales, bad_db2_files, _stores, sItemSparseStore, db2Path, "Item-sparse.db2"); LoadDB2(availableDb2Locales, bad_db2_files, _stores, sItemXBonusTreeStore, db2Path, "ItemXBonusTree.db2"); LoadDB2(availableDb2Locales, bad_db2_files, _stores, sKeyChainStore, db2Path, "KeyChain.db2"); + LoadDB2(availableDb2Locales, bad_db2_files, _stores, sMountStore, db2Path, "Mount.db2"); LoadDB2(availableDb2Locales, bad_db2_files, _stores, sOverrideSpellDataStore, db2Path, "OverrideSpellData.db2"); LoadDB2(availableDb2Locales, bad_db2_files, _stores, sPhaseGroupStore, db2Path, "PhaseXPhaseGroup.db2"); LoadDB2(availableDb2Locales, bad_db2_files, _stores, sSpellAuraRestrictionsStore, db2Path, "SpellAuraRestrictions.db2"); @@ -191,15 +193,19 @@ void DB2Manager::LoadStores(std::string const& dataPath) _heirloomCurvePoints[curvePoint->CurveID][curvePoint->Index] = curvePoint; } - for (uint32 i = 0; i < sSpellPowerStore.GetNumRows(); ++i) - if (SpellPowerEntry const* power = sSpellPowerStore.LookupEntry(i)) - sSpellPowerBySpellIDStore[power->SpellID] = power; + for (uint32 i = 0; i < sMountStore.GetNumRows(); ++i) + if (MountEntry const* mount = sMountStore.LookupEntry(i)) + _mountsBySpellId[mount->SpellId] = mount; for (uint32 i = 0; i < sPhaseGroupStore.GetNumRows(); ++i) if (PhaseGroupEntry const* group = sPhaseGroupStore.LookupEntry(i)) if (PhaseEntry const* phase = sPhaseStore.LookupEntry(group->PhaseID)) _phasesByGroup[group->PhaseGroupID].insert(phase->ID); + for (uint32 i = 0; i < sSpellPowerStore.GetNumRows(); ++i) + if (SpellPowerEntry const* power = sSpellPowerStore.LookupEntry(i)) + sSpellPowerBySpellIDStore[power->SpellID] = power; + for (uint32 i = 1; i < sTaxiPathStore.GetNumRows(); ++i) if (TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(i)) sTaxiPathSetBySource[entry->From][entry->To] = TaxiPathBySourceAndDestination(entry->ID, entry->Cost); @@ -448,6 +454,15 @@ DB2Manager::ItemBonusList DB2Manager::GetItemBonusList(uint32 bonusListId) const return ItemBonusList(); } +MountEntry const* DB2Manager::GetMount(uint32 spellId) const +{ + auto itr = _mountsBySpellId.find(spellId); + if (itr != _mountsBySpellId.end()) + return itr->second; + + return nullptr; +} + std::set<uint32> DB2Manager::GetPhasesForGroup(uint32 group) const { auto itr = _phasesByGroup.find(group); diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index f721fd4bc19..f43dd2098f2 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -72,6 +72,7 @@ public: typedef std::unordered_map<uint32 /*bonusListId*/, ItemBonusList> ItemBonusListContainer; typedef std::unordered_multimap<uint32 /*itemId*/, uint32 /*bonusTreeId*/> ItemToBonusTreeContainer; typedef std::unordered_map<uint32, std::set<ItemBonusTreeNodeEntry const*>> ItemBonusTreeContainer; + typedef std::unordered_map<uint32, MountEntry const*> MountContainer; typedef std::unordered_map<uint32, std::set<uint32>> PhaseGroupContainer; static DB2Manager& Instance() @@ -92,6 +93,7 @@ public: uint32 GetItemDisplayId(uint32 itemId, uint32 appearanceModId) const; std::set<uint32> GetItemBonusTree(uint32 itemId, uint32 itemBonusTreeMod) const; ItemBonusList GetItemBonusList(uint32 bonusListId) const; + MountEntry const* GetMount(uint32 spellId) const; std::set<uint32> GetPhasesForGroup(uint32 group) const; private: @@ -103,6 +105,7 @@ private: ItemBonusListContainer _itemBonusLists; ItemToBonusTreeContainer _itemToBonusTree; ItemBonusTreeContainer _itemBonusTrees; + MountContainer _mountsBySpellId; PhaseGroupContainer _phasesByGroup; }; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index 7c68e5bb39a..30044aed5ce 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -64,7 +64,7 @@ struct HolidaysEntry uint32 CalendarFlags[MAX_HOLIDAY_FLAGS]; // 29-38 //uint32 HolidayNameID; // 39 HolidayNames.dbc //uint32 HolidayDescriptionID; // 40 HolidayDescriptions.dbc - char* TextureFilename; // 41 + LocalizedString* TextureFilename; // 41 uint32 Priority; // 42 uint32 CalendarFilterType; // 43 (-1 = Fishing Contest, 0 = Unk, 1 = Darkmoon Festival, 2 = Yearly holiday) //uint32 Flags; // 44 (0 = Darkmoon Faire, Fishing Contest and Wotlk Launch, rest is 1) @@ -239,6 +239,20 @@ struct KeyChainEntry uint8 Key[KEYCHAIN_SIZE]; }; +struct MountEntry +{ + uint32 Id; + uint32 MountTypeId; + uint32 DisplayId; + uint32 Flags; + LocalizedString* Name; + LocalizedString* Description; + LocalizedString* SourceDescription; + uint32 Source; + uint32 SpellId; + uint32 PlayerConditionId; +}; + #define MAX_OVERRIDE_SPELL 10 struct OverrideSpellDataEntry @@ -375,14 +389,14 @@ struct SpellTotemsEntry struct TaxiNodesEntry { - uint32 ID; // 0 - uint32 MapID; // 1 - DBCPosition3D Pos; // 2-4 - char* Name_lang; // 5 - uint32 MountCreatureID[2]; // 6-7 - uint32 ConditionID; // 8 - uint32 Flags; // 9 - float MapOffset[2]; // 10-11 + uint32 ID; // 0 + uint32 MapID; // 1 + DBCPosition3D Pos; // 2-4 + LocalizedString* Name_lang; // 5 + uint32 MountCreatureID[2]; // 6-7 + uint32 ConditionID; // 8 + uint32 Flags; // 9 + float MapOffset[2]; // 10-11 }; struct TaxiPathEntry diff --git a/src/server/game/DataStores/DB2fmt.h b/src/server/game/DataStores/DB2fmt.h index 3a56fb75ca5..84b69ef03b4 100644 --- a/src/server/game/DataStores/DB2fmt.h +++ b/src/server/game/DataStores/DB2fmt.h @@ -32,6 +32,7 @@ char const ItemModifiedAppearanceEntryfmt[] = "niiiii"; char const ItemSparsefmt[] = "niiiiffiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiffffffffffiiifisssssiiiiiiiiiiiiiiiiiiifiiifiii"; char const ItemXBonusTreeEntryfmt[] = "nii"; char const KeyChainfmt[] = "nbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"; +char const Mountfmt[] = "niiisssiii"; char const OverrideSpellDataEntryfmt[] = "niiiiiiiiiixx"; char const PhaseGroupEntryfmt[] = "nii"; char const SpellAuraRestrictionsEntryfmt[] = "diiiiiiii"; diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index 72994461add..0a97e431fdc 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -474,10 +474,19 @@ enum ItemLimitCategoryMode ITEM_LIMIT_CATEGORY_MODE_EQUIP = 1 // limit applied to amount equipped items (including used gems) }; +enum MountCapabilityFlags +{ + MOUNT_CAPABILITY_FLAG_CAN_PITCH = 0x4, // client checks MOVEMENTFLAG2_FULL_SPEED_PITCHING + MOUNT_CAPABILITY_FLAG_CAN_SWIM = 0x8, // client checks MOVEMENTFLAG_SWIMMING +}; + enum MountFlags { - MOUNT_FLAG_CAN_PITCH = 0x4, // client checks MOVEMENTFLAG2_FULL_SPEED_PITCHING - MOUNT_FLAG_CAN_SWIM = 0x8, // client checks MOVEMENTFLAG_SWIMMING + MOUNT_FLAG_SELF_MOUNT = 0x02, // Player becomes the mount himself + MOUNT_FLAG_FACTION_SPECIFIC = 0x04, + MOUNT_FLAG_PREFERRED_SWIMMING = 0x10, + MOUNT_FLAG_PREFERRED_WATER_WALKING = 0x20, + MOUNT_FLAG_HIDE_IF_UNKNOWN = 0x40 }; enum SkillRaceClassInfoFlags @@ -494,7 +503,7 @@ enum SpellCategoryFlags { SPELL_CATEGORY_FLAG_COOLDOWN_SCALES_WITH_WEAPON_SPEED = 0x01, // unused SPELL_CATEGORY_FLAG_COOLDOWN_STARTS_ON_EVENT = 0x04, - SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_MIDNIGHT = 0x08 + SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET = 0x08 }; enum TotemCategoryType diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index bd087be84dd..5ae83833cfc 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -828,7 +828,7 @@ uint32 GetExpansionForLevel(uint32 level) return CURRENT_EXPANSION; } -bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) +bool IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId) { if (requiredTotemCategoryId == 0) return true; diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index edbb5aa55bf..984f767cf91 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -61,7 +61,7 @@ uint32 GetExpansionForLevel(uint32 level); ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId); -bool IsTotemCategoryCompatiableWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); +bool IsTotemCategoryCompatibleWith(uint32 itemTotemCategoryId, uint32 requiredTotemCategoryId); void Zone2MapCoordinates(float &x, float &y, uint32 zone); void Map2ZoneCoordinates(float &x, float &y, uint32 zone); diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 0bad082801d..c5b6fe52ae4 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -1577,7 +1577,7 @@ struct SpellCategoriesEntry uint32 Mechanic; // 6 uint32 PreventionType; // 7 uint32 StartRecoveryCategory; // 8 - //uint32 ChargeCategory; // 9 + uint32 ChargeCategory; // 9 }; typedef std::set<uint32> SpellCategorySet; @@ -1601,8 +1601,8 @@ struct SpellCategoryEntry //uint8 UsesPerWeek; // 2 //uint8 Padding[3]; // 2 //char* Name_lang; // 3 - //uint32 MaxCharges; // 4 - //uint32 ChargeRecoveryTime; // 5 + int32 MaxCharges; // 4 + int32 ChargeRecoveryTime; // 5 }; struct SpellFocusObjectEntry diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index 1d404d66311..f6612fca298 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -137,8 +137,8 @@ char const SkillTiersfmt[] = "niiiiiiiiiiiiiiii"; char const SoundEntriesfmt[] = "nxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"; char const SpecializationSpellsEntryfmt[] = "niiix"; char const SpellCastTimefmt[] = "nixx"; -char const SpellCategoriesEntryfmt[] = "diiiiiiiix"; -char const SpellCategoryfmt[] = "nixxxx"; +char const SpellCategoriesEntryfmt[] = "diiiiiiiii"; +char const SpellCategoryfmt[] = "nixxii"; char const SpellDurationfmt[] = "niii"; char const SpellEffectEntryfmt[] = "iiifiiiffiiiiiifiifiiiiifiiiiif"; const std::string CustomSpellEffectEntryfmt = "ppppppppppppppappppppppppp"; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index f6a3a5c600c..096b4b749be 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -157,8 +157,6 @@ m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo( for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) m_spells[i] = 0; - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); DisableReputationGain = false; m_SightDistance = sWorld->getFloatConfig(CONFIG_SIGHT_MONSTER); @@ -2181,83 +2179,6 @@ void Creature::SetInCombatWithZone() } } -void Creature::_AddCreatureSpellCooldown(uint32 spell_id, time_t end_time) -{ - m_CreatureSpellCooldowns[spell_id] = end_time; -} - -void Creature::_AddCreatureCategoryCooldown(uint32 category, time_t apply_time) -{ - m_CreatureCategoryCooldowns[category] = apply_time; -} - -void Creature::AddCreatureSpellCooldown(uint32 spellid) -{ - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); - if (!spellInfo) - return; - - uint32 cooldown = spellInfo->GetRecoveryTime(); - if (Player* modOwner = GetSpellModOwner()) - modOwner->ApplySpellMod(spellid, SPELLMOD_COOLDOWN, cooldown); - - if (cooldown) - _AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/IN_MILLISECONDS); - - if (spellInfo->GetCategory()) - _AddCreatureCategoryCooldown(spellInfo->GetCategory(), time(NULL)); -} - -bool Creature::HasCategoryCooldown(uint32 spell_id) const -{ - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spell_id); - if (!spellInfo) - return false; - - CreatureSpellCooldowns::const_iterator itr = m_CreatureCategoryCooldowns.find(spellInfo->GetCategory()); - return(itr != m_CreatureCategoryCooldowns.end() && time_t(itr->second + (spellInfo->CategoryRecoveryTime / IN_MILLISECONDS)) > time(NULL)); -} - -uint32 Creature::GetCreatureSpellCooldownDelay(uint32 spellId) const -{ - CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spellId); - time_t t = time(NULL); - return uint32(itr != m_CreatureSpellCooldowns.end() && itr->second > t ? itr->second - t : 0); -} - -bool Creature::HasSpellCooldown(uint32 spell_id) const -{ - CreatureSpellCooldowns::const_iterator itr = m_CreatureSpellCooldowns.find(spell_id); - return (itr != m_CreatureSpellCooldowns.end() && itr->second > time(NULL)) || HasCategoryCooldown(spell_id); -} - -void Creature::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) -{ - time_t curTime = time(NULL); - for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) - { - if (m_spells[i] == 0) - continue; - - uint32 unSpellId = m_spells[i]; - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(unSpellId); - - // Not send cooldown for this spells - if (spellInfo->IsCooldownStartedOnEvent()) - continue; - - if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE) - continue; - - if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetCreatureSpellCooldownDelay(unSpellId) < unTimeMs) - { - _AddCreatureSpellCooldown(unSpellId, curTime + unTimeMs/IN_MILLISECONDS); - if (UnitAI* ai = GetAI()) - ai->SpellInterrupted(unSpellId, unTimeMs); - } - } -} - bool Creature::HasSpell(uint32 spellID) const { uint8 i; diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index a3230dd98ba..43de647c00a 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -529,14 +529,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject SpellSchoolMask GetMeleeDamageSchoolMask() const override { return m_meleeDamageSchoolMask; } void SetMeleeDamageSchool(SpellSchools school) { m_meleeDamageSchoolMask = SpellSchoolMask(1 << school); } - void _AddCreatureSpellCooldown(uint32 spell_id, time_t end_time); - void _AddCreatureCategoryCooldown(uint32 category, time_t apply_time); - void AddCreatureSpellCooldown(uint32 spellid); - bool HasSpellCooldown(uint32 spell_id) const; - bool HasCategoryCooldown(uint32 spell_id) const; - uint32 GetCreatureSpellCooldownDelay(uint32 spellId) const; - virtual void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override; - bool HasSpell(uint32 spellID) const override; bool UpdateEntry(uint32 entry, CreatureData const* data = nullptr); @@ -610,8 +602,6 @@ class Creature : public Unit, public GridObject<Creature>, public MapObject SpellInfo const* reachWithSpellCure(Unit* victim); uint32 m_spells[CREATURE_MAX_SPELLS]; - CreatureSpellCooldowns m_CreatureSpellCooldowns; - CreatureSpellCooldowns m_CreatureCategoryCooldowns; bool CanStartAttack(Unit const* u, bool force) const; float GetAttackDistance(Unit const* player) const; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 0c88ea937e7..5534b6e08e3 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -289,8 +289,8 @@ bool Item::Create(ObjectGuid::LowType guidlow, uint32 itemid, Player const* owne SetUInt32Value(ITEM_FIELD_MAXDURABILITY, itemProto->MaxDurability); SetUInt32Value(ITEM_FIELD_DURABILITY, itemProto->MaxDurability); - for (uint8 i = 0; i < itemProto->Effects.size(); ++i) - SetSpellCharges(i, itemProto->Effects[i].Charges); + for (uint8 i = 0; i < itemProto->Effects.size() && i < 5; ++i) + SetSpellCharges(i, itemProto->Effects[i]->Charges); SetUInt32Value(ITEM_FIELD_DURATION, itemProto->GetDuration()); SetUInt32Value(ITEM_FIELD_CREATE_PLAYED_TIME, 0); diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index 0a089a09b15..0824b0b3c29 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -581,20 +581,6 @@ const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS] = MAX_ITEM_SUBCLASS_GLYPH }; -#pragma pack(push, 1) - -struct ItemEffect -{ - uint32 SpellID; - uint32 Trigger; - int32 Charges; - int32 Cooldown; - uint32 Category; - int32 CategoryCooldown; -}; - -#pragma pack(pop) - #define MIN_ITEM_LEVEL 1 #define MAX_ITEM_LEVEL 1000 @@ -645,6 +631,7 @@ struct ItemTemplate uint32 GetArea() const { return ExtendedData->Area; } uint32 GetMap() const { return ExtendedData->Map; } uint32 GetBagFamily() const { return ExtendedData->BagFamily; } + uint32 GetTotemCategory() const { return ExtendedData->TotemCategory; } SocketColor GetSocketColor(uint32 index) const { ASSERT(index < MAX_ITEM_PROTO_SOCKETS); return SocketColor(ExtendedData->SocketColor[index]); } uint32 GetSocketBonus() const { return ExtendedData->SocketBonus; } uint32 GetGemProperties() const { return ExtendedData->GemProperties; } @@ -657,7 +644,7 @@ struct ItemTemplate void GetBaseDamage(float& minDamage, float& maxDamage) const { GetDamage(ExtendedData->ItemLevel, minDamage, maxDamage); } uint32 MaxDurability; - std::vector<ItemEffect> Effects; + std::vector<ItemEffectEntry const*> Effects; // extra fields, not part of db2 files uint32 ScriptId; diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 429af16f132..a59866c402f 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -26,6 +26,7 @@ #include "Formulas.h" #include "SpellAuras.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "CreatureAI.h" #include "Unit.h" #include "Util.h" @@ -408,7 +409,7 @@ void Pet::SavePetToDB(PetSaveMode mode) RemoveAllAuras(); _SaveSpells(trans); - _SaveSpellCooldowns(trans); + GetSpellHistory()->SaveToDB<Pet>(trans); CharacterDatabase.CommitTransaction(trans); // current/stable/not_in_slot @@ -511,6 +512,10 @@ void Pet::DeleteFromDB(uint32 guidlow) stmt->setUInt32(0, guidlow); trans->Append(stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_CHARGES); + stmt->setUInt32(0, guidlow); + trans->Append(stmt); + CharacterDatabase.CommitTransaction(trans); } @@ -1072,77 +1077,15 @@ uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel) const void Pet::_LoadSpellCooldowns() { - m_CreatureSpellCooldowns.clear(); - m_CreatureCategoryCooldowns.clear(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_COOLDOWN); stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - PreparedQueryResult result = CharacterDatabase.Query(stmt); - - if (result) - { - time_t curTime = time(NULL); - - PacketCooldowns cooldowns; - WorldPacket data; - - do - { - Field* fields = result->Fetch(); - - uint32 spell_id = fields[0].GetUInt32(); - time_t db_time = time_t(fields[1].GetUInt32()); - - if (!sSpellMgr->GetSpellInfo(spell_id)) - { - TC_LOG_ERROR("entities.pet", "Pet %u have unknown spell %u in `pet_spell_cooldown`, skipping.", m_charmInfo->GetPetNumber(), spell_id); - continue; - } - - // skip outdated cooldown - if (db_time <= curTime) - continue; - - cooldowns[spell_id] = uint32(db_time - curTime)*IN_MILLISECONDS; - - _AddCreatureSpellCooldown(spell_id, db_time); - - TC_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()); - - if (!cooldowns.empty()) - { - BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns); - GetOwner()->GetSession()->SendPacket(&data); - } - } -} + PreparedQueryResult cooldownsResult = CharacterDatabase.Query(stmt); -void Pet::_SaveSpellCooldowns(SQLTransaction& trans) -{ - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_PET_SPELL_COOLDOWNS); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_CHARGES); stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - trans->Append(stmt); - - time_t curTime = time(NULL); + PreparedQueryResult chargesResult = CharacterDatabase.Query(stmt); - // remove oudated and save active - for (CreatureSpellCooldowns::iterator itr = m_CreatureSpellCooldowns.begin(); itr != m_CreatureSpellCooldowns.end();) - { - if (itr->second <= curTime) - m_CreatureSpellCooldowns.erase(itr++); - else - { - stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET_SPELL_COOLDOWN); - stmt->setUInt32(0, m_charmInfo->GetPetNumber()); - stmt->setUInt32(1, itr->first); - stmt->setUInt32(2, uint32(itr->second)); - trans->Append(stmt); - - ++itr; - } - } + GetSpellHistory()->LoadFromDB<Pet>(cooldownsResult, chargesResult); } void Pet::_LoadSpells() @@ -1983,40 +1926,6 @@ void Pet::SynchronizeLevelWithOwner() } } -void Pet::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) -{ - PacketCooldowns cooldowns; - WorldPacket data; - time_t curTime = time(NULL); - for (PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if (itr->second.state == PETSPELL_REMOVED) - continue; - - uint32 unSpellId = itr->first; - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(unSpellId); - - // Not send cooldown for this spells - if (spellInfo->IsCooldownStartedOnEvent()) - continue; - - if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE) - continue; - - if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetCreatureSpellCooldownDelay(unSpellId) < unTimeMs) - { - cooldowns[unSpellId] = unTimeMs; - _AddCreatureSpellCooldown(unSpellId, curTime + unTimeMs/IN_MILLISECONDS); - } - } - - if (!cooldowns.empty()) - { - BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns); - GetOwner()->GetSession()->SendPacket(&data); - } -} - Player* Pet::GetOwner() const { return Minion::GetOwner()->ToPlayer(); diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h index e9ace117dde..eb8868bf175 100644 --- a/src/server/game/Entities/Pet/Pet.h +++ b/src/server/game/Entities/Pet/Pet.h @@ -106,7 +106,6 @@ class Pet : public Guardian bool IsPetAura(Aura const* aura); void _LoadSpellCooldowns(); - void _SaveSpellCooldowns(SQLTransaction& trans); void _LoadAuras(uint32 timediff); void _SaveAuras(SQLTransaction& trans); void _LoadSpells(); @@ -119,7 +118,6 @@ class Pet : public Guardian bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true); bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true); void CleanupActionBar(); - virtual void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override; PetSpellMap m_spells; AutoSpellList m_autospells; diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index f50f20b2d9e..1c21f546bbf 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -74,6 +74,7 @@ #include "Spell.h" #include "SpellAuraEffects.h" #include "SpellAuras.h" +#include "SpellHistory.h" #include "SpellMgr.h" #include "SpellPackets.h" #include "Transport.h" @@ -1163,7 +1164,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, WorldPackets::Character::Charac { if (iProto->Effects.size() >= 1) { - switch (iProto->Effects[0].Category) + switch (iProto->Effects[0]->Category) { case SPELL_CATEGORY_FOOD: // food count = getClass() == CLASS_DEATH_KNIGHT ? 10 : 4; @@ -3939,152 +3940,19 @@ void Player::RemoveSpell(uint32 spell_id, bool disabled, bool learn_low_rank) } } -void Player::RemoveSpellCooldown(uint32 spell_id, bool update /* = false */) -{ - m_spellCooldowns.erase(spell_id); - - if (update) - 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); -} - -void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */) -{ - SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(cat); - if (ct == sSpellsByCategoryStore.end()) - return; - - const SpellCategorySet& ct_set = ct->second; - for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();) - { - if (ct_set.find(i->first) != ct_set.end()) - RemoveSpellCooldown((i++)->first, update); - else - ++i; - } -} - void Player::RemoveArenaSpellCooldowns(bool removeActivePetCooldowns) { // remove cooldowns on spells that have < 10 min CD - - SpellCooldowns::iterator itr, next; - for (itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); itr = next) + GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) { - next = itr; - ++next; - SpellInfo const* entry = sSpellMgr->GetSpellInfo(itr->first); - // check if spellentry is present and if the cooldown is less than 10 min - if (entry && - entry->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && - entry->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS) - { - // remove & notify - RemoveSpellCooldown(itr->first, true); - } - } + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); + return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS; + }, true); // pet cooldowns if (removeActivePetCooldowns) if (Pet* pet = GetPet()) - { - // notify player - for (CreatureSpellCooldowns::const_iterator itr2 = pet->m_CreatureSpellCooldowns.begin(); itr2 != pet->m_CreatureSpellCooldowns.end(); ++itr2) - SendClearCooldown(itr2->first, pet); - - // actually clear cooldowns - pet->m_CreatureSpellCooldowns.clear(); - } -} - -void Player::RemoveAllSpellCooldown() -{ - if (!m_spellCooldowns.empty()) - { - SendClearAllCooldowns(this); - m_spellCooldowns.clear(); - } -} - -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'", GetGUIDLow()); - - if (result) - { - time_t curTime = time(NULL); - - do - { - Field* fields = result->Fetch(); - uint32 spell_id = fields[0].GetUInt32(); - uint32 item_id = fields[1].GetUInt32(); - time_t db_time = time_t(fields[2].GetUInt32()); - - if (!sSpellMgr->GetSpellInfo(spell_id)) - { - TC_LOG_ERROR("entities.player.loading", "%s has unknown spell %u in `character_spell_cooldown`, skipping.", GetGUID().ToString().c_str(), spell_id); - continue; - } - - // skip outdated cooldown - if (db_time <= curTime) - continue; - - AddSpellCooldown(spell_id, item_id, db_time); - - TC_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()); - } -} - -void Player::_SaveSpellCooldowns(SQLTransaction& trans) -{ - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN); - stmt->setUInt64(0, GetGUID().GetCounter()); - trans->Append(stmt); - - time_t curTime = time(NULL); - time_t infTime = curTime + infinityCooldownDelayCheck; - - bool first_round = true; - std::ostringstream ss; - - // remove outdated and save active - for (SpellCooldowns::iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end();) - { - if (itr->second.end <= curTime) - m_spellCooldowns.erase(itr++); - else if (itr->second.end <= infTime) // not save locked cooldowns, it will be reset or set at reload - { - if (first_round) - { - ss << "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES "; - first_round = false; - } - // next new/changed record prefix - else - ss << ','; - ss << '(' << GetGUID().GetCounter() << ',' << itr->first << ',' << itr->second.itemid << ',' << uint64(itr->second.end) << ')'; - ++itr; - } - else - ++itr; - } - // if something changed execute - if (!first_round) - trans->Append(ss.str().c_str()); + pet->GetSpellHistory()->ResetAllCooldowns(); } uint32 Player::GetNextResetTalentsCost() const @@ -4578,7 +4446,11 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe stmt->setUInt64(0, guid); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_COOLDOWNS); + stmt->setUInt64(0, guid); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_SPELL_CHARGES); stmt->setUInt64(0, guid); trans->Append(stmt); @@ -8095,14 +7967,14 @@ void Player::ApplyItemEquipSpell(Item* item, bool apply, bool form_change) for (uint8 i = 0; i < proto->Effects.size(); ++i) { - ItemEffect const& effectData = proto->Effects[i]; + ItemEffectEntry const* effectData = proto->Effects[i]; // wrong triggering type - if (apply && effectData.Trigger != ITEM_SPELLTRIGGER_ON_EQUIP) + if (apply && effectData->Trigger != ITEM_SPELLTRIGGER_ON_EQUIP) continue; // check if it is valid spell - SpellInfo const* spellproto = sSpellMgr->GetSpellInfo(effectData.SpellID); + SpellInfo const* spellproto = sSpellMgr->GetSpellInfo(effectData->SpellID); if (!spellproto) continue; @@ -8219,16 +8091,16 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 { for (uint8 i = 0; i < proto->Effects.size(); ++i) { - ItemEffect const& effectData = proto->Effects[i]; + ItemEffectEntry const* effectData = proto->Effects[i]; // wrong triggering type - if (effectData.Trigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT) + if (effectData->Trigger != ITEM_SPELLTRIGGER_CHANCE_ON_HIT) continue; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(effectData.SpellID); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(effectData->SpellID); if (!spellInfo) { - TC_LOG_ERROR("entities.player.items", "WORLD: unknown Item spellid %i", effectData.SpellID); + TC_LOG_ERROR("entities.player.items", "WORLD: unknown Item spellid %i", effectData->SpellID); continue; } @@ -8322,10 +8194,10 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 // special learning case if (proto->Effects.size() >= 2) { - if (proto->Effects[0].SpellID == 483 || proto->Effects[0].SpellID == 55884) + if (proto->Effects[0]->SpellID == 483 || proto->Effects[0]->SpellID == 55884) { - uint32 learn_spell_id = proto->Effects[0].SpellID; - uint32 learning_spell_id = proto->Effects[1].SpellID; + uint32 learn_spell_id = proto->Effects[0]->SpellID; + uint32 learning_spell_id = proto->Effects[1]->SpellID; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learn_spell_id); if (!spellInfo) @@ -8350,16 +8222,16 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 // item spells cast at use for (uint8 i = 0; i < proto->Effects.size(); ++i) { - ItemEffect const& effectData = proto->Effects[i]; + ItemEffectEntry const* effectData = proto->Effects[i]; // wrong triggering type - if (effectData.Trigger != ITEM_SPELLTRIGGER_ON_USE) + if (effectData->Trigger != ITEM_SPELLTRIGGER_ON_USE) continue; - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(effectData.SpellID); + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(effectData->SpellID); if (!spellInfo) { - TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring", proto->GetId(), effectData.SpellID); + TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring", proto->GetId(), effectData->SpellID); continue; } @@ -10332,6 +10204,34 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& des return CanStoreItem(bag, slot, dest, pItem->GetEntry(), count, pItem, swap, NULL); } +bool Player::HasItemTotemCategory(uint32 TotemCategory) const +{ + Item* item; + for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) + { + item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i); + if (item && IsTotemCategoryCompatibleWith(item->GetTemplate()->GetTotemCategory(), TotemCategory)) + return true; + } + + Bag* bag; + for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) + { + bag = GetBagByPos(i); + if (bag) + { + for (uint32 j = 0; j < bag->GetBagSize(); ++j) + { + item = GetUseableItemByPos(i, j); + if (item && IsTotemCategoryCompatibleWith(item->GetTemplate()->GetTotemCategory(), TotemCategory)) + return true; + } + } + } + + return false; +} + InventoryResult Player::CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemTemplate const* pProto, uint32& count, bool swap, Item* pSrcItem) const { Item* pItem2 = GetItemByPos(bag, slot); @@ -11486,8 +11386,8 @@ InventoryResult Player::CanUseItem(ItemTemplate const* proto) const // learning (recipes, mounts, pets, etc.) if (proto->Effects.size() >= 2) - if (proto->Effects[0].SpellID == 483 || proto->Effects[0].SpellID == 55884) - if (HasSpell(proto->Effects[1].SpellID)) + if (proto->Effects[0]->SpellID == 483 || proto->Effects[0]->SpellID == 55884) + if (HasSpell(proto->Effects[1]->SpellID)) return EQUIP_ERR_INTERNAL_BAG_ERROR; return EQUIP_ERR_OK; @@ -11694,10 +11594,10 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool const ItemTemplate* proto = pItem->GetTemplate(); for (uint8 i = 0; i < proto->Effects.size(); ++i) - if (proto->Effects[i].Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger + if (proto->Effects[i]->Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) - if (!HasAura(proto->Effects[i].SpellID)) - CastSpell(this, proto->Effects[i].SpellID, true, pItem); + if (!HasAura(proto->Effects[i]->SpellID)) + CastSpell(this, proto->Effects[i]->SpellID, true, pItem); return pItem; } @@ -11737,10 +11637,10 @@ Item* Player::_StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool const ItemTemplate* proto = pItem2->GetTemplate(); for (uint8 i = 0; i < proto->Effects.size(); ++i) - if (proto->Effects[i].Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger + if (proto->Effects[i]->Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger if (bag == INVENTORY_SLOT_BAG_0 || (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)) - if (!HasAura(proto->Effects[i].SpellID)) - CastSpell(this, proto->Effects[i].SpellID, true, pItem2); + if (!HasAura(proto->Effects[i]->SpellID)) + CastSpell(this, proto->Effects[i]->SpellID, true, pItem2); return pItem2; } @@ -11793,11 +11693,13 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) { m_weaponChangeTimer = spellProto->StartRecoveryTime; - GetGlobalCooldownMgr().AddGlobalCooldown(spellProto, m_weaponChangeTimer); + GetSpellHistory()->AddGlobalCooldown(spellProto, m_weaponChangeTimer); - WorldPacket data; - BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_INCLUDE_GCD, cooldownSpell, 0); - GetSession()->SendPacket(&data); + WorldPackets::Spells::SpellCooldown spellCooldown; + spellCooldown.Caster = GetGUID(); + spellCooldown.Flags = SPELL_COOLDOWN_FLAG_INCLUDE_GCD; + spellCooldown.SpellCooldowns.emplace_back(cooldownSpell, 0); + GetSession()->SendPacket(spellCooldown.Write()); } } } @@ -12083,8 +11985,8 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) const ItemTemplate* proto = pItem->GetTemplate(); for (uint8 i = 0; i < proto->Effects.size(); ++i) - if (proto->Effects[i].Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger - RemoveAurasDueToSpell(proto->Effects[i].SpellID); + if (proto->Effects[i]->Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE) // On obtain trigger + RemoveAurasDueToSpell(proto->Effects[i]->SpellID); ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount()); sScriptMgr->OnItemRemove(this, pItem); @@ -17196,7 +17098,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) // has to be called after last Relocate() in Player::LoadFromDB SetFallInformation(0, GetPositionZ()); - _LoadSpellCooldowns(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS)); + GetSpellHistory()->LoadFromDB<Player>(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS), holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_SPELL_CHARGES)); // Spell code allow apply any auras to dead character in load time in aura/spell/item loading // Do now before stats re-calculation cleanup for ghost state unexpected auras @@ -19084,7 +18986,7 @@ void Player::SaveToDB(bool create /*=false*/) _SaveMonthlyQuestStatus(trans); _SaveTalents(trans); _SaveSpells(trans); - _SaveSpellCooldowns(trans); + GetSpellHistory()->SaveToDB<Player>(trans); _SaveActions(trans); _SaveAuras(trans); _SaveSkills(trans); @@ -20383,41 +20285,8 @@ void Player::PetSpellInitialize() data.put<uint8>(spellsCountPos, addlist); - uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size(); - data << uint8(cooldownsCount); - - time_t curTime = time(NULL); - - for (CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - if (!spellInfo) - { - data << uint32(0); - data << uint16(0); - data << uint32(0); - data << uint32(0); - continue; - } - - time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILLISECONDS : 0; - data << uint32(itr->first); // spell ID - - CreatureSpellCooldowns::const_iterator categoryitr = pet->m_CreatureCategoryCooldowns.find(spellInfo->GetCategory()); - if (categoryitr != pet->m_CreatureCategoryCooldowns.end()) - { - time_t categoryCooldown = (categoryitr->second > curTime) ? (categoryitr->second - curTime) * IN_MILLISECONDS : 0; - data << uint16(spellInfo->GetCategory()); // spell category - data << uint32(cooldown); // spell cooldown - data << uint32(categoryCooldown); // category cooldown - } - else - { - data << uint16(0); - data << uint32(cooldown); - data << uint32(0); - } - } + // Cooldowns + //pet->GetSpellHistory()->WritePacket(&petSpells); GetSession()->SendPacket(&data); } @@ -20445,7 +20314,9 @@ void Player::PossessSpellInitialize() charmInfo->BuildActionBar(&data); data << uint8(0); // spells count - data << uint8(0); // cooldowns count + + // Cooldowns + //charm->GetSpellHistory()->WritePacket(&petSpells); GetSession()->SendPacket(&data); } @@ -20456,7 +20327,7 @@ void Player::VehicleSpellInitialize() if (!vehicle) return; - uint8 cooldownCount = vehicle->m_CreatureSpellCooldowns.size(); + uint8 cooldownCount = 0; WorldPacket data(SMSG_PET_SPELLS, 8 + 2 + 4 + 4 + 4 * 10 + 1 + 1 + cooldownCount * (4 + 2 + 4 + 4)); data << vehicle->GetGUID(); // Guid @@ -20497,41 +20368,7 @@ void Player::VehicleSpellInitialize() data << uint8(0); // Auras? // Cooldowns - data << uint8(cooldownCount); - - time_t now = sWorld->GetGameTime(); - - for (CreatureSpellCooldowns::const_iterator itr = vehicle->m_CreatureSpellCooldowns.begin(); itr != vehicle->m_CreatureSpellCooldowns.end(); ++itr) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); - if (!spellInfo) - { - data << uint32(0); - data << uint16(0); - data << uint32(0); - data << uint32(0); - continue; - } - - time_t cooldown = (itr->second > now) ? (itr->second - now) * IN_MILLISECONDS : 0; - data << uint32(itr->first); // spell ID - - CreatureSpellCooldowns::const_iterator categoryitr = vehicle->m_CreatureCategoryCooldowns.find(spellInfo->GetCategory()); - if (categoryitr != vehicle->m_CreatureCategoryCooldowns.end()) - { - time_t categoryCooldown = (categoryitr->second > now) ? (categoryitr->second - now) * IN_MILLISECONDS : 0; - data << uint16(spellInfo->GetCategory()); // spell category - data << uint32(cooldown); // spell cooldown - data << uint32(categoryCooldown); // category cooldown - } - else - { - data << uint16(0); - data << uint32(cooldown); - data << uint32(0); - } - } - + //vehicle->GetSpellHistory()->WritePacket(&petSpells); GetSession()->SendPacket(&data); } @@ -20584,7 +20421,8 @@ void Player::CharmSpellInitialize() } } - data << uint8(0); // cooldowns count + // Cooldowns + //charm->GetSpellHistory()->WritePacket(&petSpells); GetSession()->SendPacket(&data); } @@ -21165,39 +21003,6 @@ void Player::ContinueTaxiFlight() GetSession()->SendDoFlight(mountDisplayId, path, startNode); } -void Player::ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) -{ - PacketCooldowns cooldowns; - WorldPacket data; - time_t curTime = time(NULL); - for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr) - { - if (itr->second->state == PLAYERSPELL_REMOVED) - continue; - uint32 unSpellId = itr->first; - SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(unSpellId); - - // Not send cooldown for this spells - if (spellInfo->IsCooldownStartedOnEvent()) - continue; - - if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE) - continue; - - if ((idSchoolMask & spellInfo->GetSchoolMask()) && GetSpellCooldownDelay(unSpellId) < unTimeMs) - { - cooldowns[unSpellId] = unTimeMs; - AddSpellCooldown(unSpellId, 0, curTime + unTimeMs/IN_MILLISECONDS); - } - } - - if (!cooldowns.empty()) - { - BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, cooldowns); - GetSession()->SendPacket(&data); - } -} - void Player::InitDataForForm(bool reapplyMods) { ShapeshiftForm form = GetShapeshiftForm(); @@ -21822,228 +21627,6 @@ void Player::UpdatePvP(bool state, bool override) } } -bool Player::HasSpellCooldown(uint32 spell_id) const -{ - SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id); - return itr != m_spellCooldowns.end() && itr->second.end > time(NULL); -} - -uint32 Player::GetSpellCooldownDelay(uint32 spell_id) const -{ - SpellCooldowns::const_iterator itr = m_spellCooldowns.find(spell_id); - time_t t = time(NULL); - return uint32(itr != m_spellCooldowns.end() && itr->second.end > t ? itr->second.end - t : 0); -} - -void Player::AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown) -{ - // init cooldown values - uint32 cat = 0; - int32 rec = -1; - int32 catrec = -1; - - // some special item spells without correct cooldown in SpellInfo - // cooldown information stored in item prototype - // This used in same way in WorldSession::HandleItemQuerySingleOpcode data sending to client. - - if (itemId) - { - if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId)) - { - for (uint8 idx = 0; idx < proto->Effects.size(); ++idx) - { - if (uint32(proto->Effects[idx].SpellID) == spellInfo->Id) - { - cat = proto->Effects[idx].Category; - rec = proto->Effects[idx].Cooldown; - catrec = proto->Effects[idx].CategoryCooldown; - break; - } - } - } - } - - // if no cooldown found above then base at DBC data - if (rec < 0 && catrec < 0) - { - cat = spellInfo->GetCategory(); - rec = spellInfo->RecoveryTime; - catrec = spellInfo->CategoryRecoveryTime; - } - - time_t curTime = time(NULL); - - time_t catrecTime; - time_t recTime; - - bool needsCooldownPacket = false; - - // overwrite time for selected category - if (infinityCooldown) - { - // use +MONTH as infinity mark for spell cooldown (will checked as MONTH/2 at save ans skipped) - // but not allow ignore until reset or re-login - catrecTime = catrec > 0 ? curTime+infinityCooldownDelay : 0; - recTime = rec > 0 ? curTime+infinityCooldownDelay : catrecTime; - } - else - { - // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) - // prevent 0 cooldowns set by another way - if (rec <= 0 && catrec <= 0 && (cat == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75))) - rec = GetAttackTime(RANGED_ATTACK); - - // Now we have cooldown data (if found any), time to apply mods - if (rec > 0) - ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell); - - if (catrec > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) - ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell); - - if (int32 cooldownMod = GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN)) - { - // Apply SPELL_AURA_MOD_COOLDOWN only to own spells - if (HasSpell(spellInfo->Id)) - { - needsCooldownPacket = true; - rec += cooldownMod * IN_MILLISECONDS; // 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 (cat) - { - if (int32 categoryModifier = GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_SPELL_CATEGORY_COOLDOWN, cat)) - { - if (rec > 0) - rec += categoryModifier; - - if (catrec > 0) - catrec += categoryModifier; - } - - SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.LookupEntry(cat); - ASSERT(categoryEntry); - if (categoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_MIDNIGHT) - { - tm date; - localtime_r(&curTime, &date); - catrec = catrec * DAY - (date.tm_hour * HOUR + date.tm_min * MINUTE + date.tm_sec) * IN_MILLISECONDS; - } - } - - // replace negative cooldowns by 0 - if (rec < 0) - rec = 0; - - if (catrec < 0) - catrec = 0; - - // no cooldown after applying spell mods - if (rec == 0 && catrec == 0) - return; - - catrecTime = catrec ? curTime+catrec/IN_MILLISECONDS : 0; - recTime = rec ? curTime+rec/IN_MILLISECONDS : catrecTime; - } - - // self spell cooldown - if (recTime > 0) - { - AddSpellCooldown(spellInfo->Id, itemId, recTime); - - if (needsCooldownPacket) - { - WorldPacket data; - BuildCooldownPacket(data, SPELL_COOLDOWN_FLAG_NONE, spellInfo->Id, rec); - SendDirectMessage(&data); - } - } - - // category spells - if (cat && catrec > 0) - { - 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; - - AddSpellCooldown(*i_scset, itemId, catrecTime); - } - } - } -} - -void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time) -{ - SpellCooldown sc; - sc.end = end_time; - sc.itemid = itemid; - m_spellCooldowns[spellid] = sc; -} - -void Player::ModifySpellCooldown(uint32 spellId, int32 cooldown) -{ - SpellCooldowns::iterator itr = m_spellCooldowns.find(spellId); - if (itr == m_spellCooldowns.end()) - return; - - time_t now = time(NULL); - if (itr->second.end + (cooldown / IN_MILLISECONDS) > now) - itr->second.end += (cooldown / IN_MILLISECONDS); - else - m_spellCooldowns.erase(itr); - - WorldPacket data(SMSG_MODIFY_COOLDOWN, 4 + 8 + 4); - data << uint32(spellId); // Spell ID - data << GetGUID(); // Player GUID - data << int32(cooldown); // Cooldown mod in milliseconds - GetSession()->SendPacket(&data); - - TC_LOG_DEBUG("misc", "ModifySpellCooldown:: Player: %s (%s) Spell: %u cooldown: %u", GetName().c_str(), GetGUID().ToString().c_str(), spellId, GetSpellCooldownDelay(spellId)); -} - -void Player::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= NULL*/, bool setCooldown /*= true*/) -{ - // start cooldowns at server side, if any - if (setCooldown) - AddSpellAndCategoryCooldowns(spellInfo, itemId, spell); - - // Send activate cooldown timer (possible 0) at client side - WorldPackets::Spells::CooldownEvent packet(GetGUID(), spellInfo->Id); - SendDirectMessage(packet.Write()); - - uint32 cat = spellInfo->GetCategory(); - if (cat && spellInfo->CategoryRecoveryTime) - { - SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(cat); - if (ct != sSpellsByCategoryStore.end()) - { - SpellCategorySet const& catSet = ct->second; - for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end(); ++i) - { - if (i->first == spellInfo->Id) // skip main spell, already handled above - continue; - - SpellInfo const* spellInfo2 = sSpellMgr->GetSpellInfo(i->first); - if (!spellInfo2 || !spellInfo2->IsCooldownStartedOnEvent()) - continue; - - if (catSet.find(i->first) != catSet.end()) - { - // Send activate cooldown timer (possible 0) at client side - WorldPackets::Spells::CooldownEvent packet(GetGUID(), i->first); - SendDirectMessage(packet.Write()); - } - } - } - } -} - void Player::UpdatePotionCooldown(Spell* spell) { // no potion used i combat or still in combat @@ -22056,13 +21639,13 @@ void Player::UpdatePotionCooldown(Spell* spell) // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions) if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(m_lastPotionId)) for (uint8 idx = 0; idx < proto->Effects.size(); ++idx) - if (proto->Effects[idx].Trigger == ITEM_SPELLTRIGGER_ON_USE) - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Effects[idx].SpellID)) - SendCooldownEvent(spellInfo, m_lastPotionId); + if (proto->Effects[idx]->Trigger == ITEM_SPELLTRIGGER_ON_USE) + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Effects[idx]->SpellID)) + GetSpellHistory()->SendCooldownEvent(spellInfo, m_lastPotionId); } // from spell cases (m_lastPotionId set in Spell::SendSpellCooldown) else - SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell); + GetSpellHistory()->SendCooldownEvent(spell->m_spellInfo, m_lastPotionId, spell); m_lastPotionId = 0; } @@ -22816,8 +22399,15 @@ void Player::SendInitialPacketsBeforeAddToMap() /// SMSG_SEND_UNLEARN_SPELLS SendDirectMessage(WorldPackets::Spells::SendUnlearnSpells().Write()); - /// @todo: SMSG_SEND_SPELL_HISTORY - /// @todo: SMSG_SEND_SPELL_CHARGES + /// SMSG_SEND_SPELL_HISTORY + WorldPackets::Spells::SendSpellHistory sendSpellHistory; + GetSpellHistory()->WritePacket(&sendSpellHistory); + SendDirectMessage(sendSpellHistory.Write()); + + /// SMSG_SEND_SPELL_CHARGES + WorldPackets::Spells::SendSpellCharges sendSpellCharges; + GetSpellHistory()->WritePacket(&sendSpellCharges); + SendDirectMessage(sendSpellCharges.Write()); /// SMSG_ACTION_BUTTONS SendInitialActionButtons(); @@ -22974,22 +22564,21 @@ void Player::ApplyEquipCooldown(Item* pItem) for (uint8 i = 0; i < proto->Effects.size(); ++i) { - ItemEffect const& effectData = proto->Effects[i]; + ItemEffectEntry const* effectData = proto->Effects[i]; // wrong triggering type (note: ITEM_SPELLTRIGGER_ON_NO_DELAY_USE not have cooldown) - if (effectData.Trigger != ITEM_SPELLTRIGGER_ON_USE) + if (effectData->Trigger != ITEM_SPELLTRIGGER_ON_USE) continue; // Don't replace longer cooldowns by equip cooldown if we have any. - SpellCooldowns::iterator itr = m_spellCooldowns.find(effectData.SpellID); - if (itr != m_spellCooldowns.end() && itr->second.itemid == pItem->GetEntry() && itr->second.end > time(NULL) + 30) + if (GetSpellHistory()->GetRemainingCooldown(effectData->SpellID) > 30 * IN_MILLISECONDS) continue; - AddSpellCooldown(effectData.SpellID, pItem->GetEntry(), time(NULL) + 30); + GetSpellHistory()->AddCooldown(effectData->SpellID, pItem->GetEntry(), std::chrono::seconds(30)); WorldPacket data(SMSG_ITEM_COOLDOWN, 12); data << pItem->GetGUID(); - data << uint32(effectData.SpellID); + data << uint32(effectData->SpellID); GetSession()->SendPacket(&data); } } @@ -23936,7 +23525,7 @@ uint32 Player::GetResurrectionSpellId() } // Reincarnation (passive spell) // prio: 1 // Glyph of Renewed Life - if (prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasAura(58059) || HasItemCount(17030))) + if (prio < 1 && HasSpell(20608) && !GetSpellHistory()->HasCooldown(21169) && (HasAura(58059) || HasItemCount(17030))) spell_id = 21169; return spell_id; @@ -25708,47 +25297,6 @@ void Player::RemoveAtLoginFlag(AtLoginFlags flags, bool persist /*= false*/) } } -void Player::SendClearCooldown(uint32 spell_id, Unit* target) -{ - WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8); - data << uint32(spell_id); - data << target->GetGUID(); - SendDirectMessage(&data); -} - -void Player::SendClearAllCooldowns(Unit* target) -{ - uint32 spellCount = m_spellCooldowns.size(); - ObjectGuid guid = target ? target->GetGUID() : ObjectGuid::Empty; - - WorldPacket data(SMSG_CLEAR_COOLDOWNS, 4+8); - data.WriteBit(guid[1]); - data.WriteBit(guid[3]); - data.WriteBit(guid[6]); - data.WriteBits(spellCount, 24); // Spell Count - data.WriteBit(guid[7]); - data.WriteBit(guid[5]); - data.WriteBit(guid[2]); - data.WriteBit(guid[4]); - data.WriteBit(guid[0]); - - data.FlushBits(); - - data.WriteByteSeq(guid[7]); - data.WriteByteSeq(guid[2]); - data.WriteByteSeq(guid[4]); - data.WriteByteSeq(guid[5]); - data.WriteByteSeq(guid[1]); - data.WriteByteSeq(guid[3]); - for (SpellCooldowns::const_iterator itr = m_spellCooldowns.begin(); itr != m_spellCooldowns.end(); ++itr) - data << uint32(itr->first); // Spell ID - - data.WriteByteSeq(guid[0]); - data.WriteByteSeq(guid[6]); - - SendDirectMessage(&data); -} - void Player::ResetMap() { // this may be called during Map::Update diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index b0a7b48ca84..a2d5ed72bd8 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -285,13 +285,6 @@ struct CUFProfile // More fields can be added to BoolOptions without changing DB schema (up to 32, currently 27) }; -struct SpellCooldown -{ - time_t end; - uint16 itemid; -}; - -typedef std::map<uint32, SpellCooldown> SpellCooldowns; typedef std::unordered_map<uint32 /*instanceId*/, time_t/*releaseTime*/> InstanceTimeMap; enum TrainerSpellState @@ -975,6 +968,7 @@ enum PlayerLoginQueryIndex PLAYER_LOGIN_QUERY_LOAD_SOCIAL_LIST, PLAYER_LOGIN_QUERY_LOAD_HOME_BIND, PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS, + PLAYER_LOGIN_QUERY_LOAD_SPELL_CHARGES, PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES, PLAYER_LOGIN_QUERY_LOAD_GUILD, PLAYER_LOGIN_QUERY_LOAD_ARENA_INFO, @@ -1937,8 +1931,6 @@ class Player : public Unit, public GridObject<Player> PlayerSpellMap const& GetSpellMap() const { return m_spells; } PlayerSpellMap & GetSpellMap() { return m_spells; } - SpellCooldowns const& GetSpellCooldownMap() const { return m_spellCooldowns; } - void AddSpellMod(SpellModifier* mod, bool apply); bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = NULL); template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell = NULL); @@ -1948,27 +1940,7 @@ class Player : public Unit, public GridObject<Player> void DropModCharge(SpellModifier* mod, Spell* spell); void SetSpellModTakingSpell(Spell* spell, bool apply); - static uint32 const infinityCooldownDelay = MONTH; // used for set "infinity cooldowns" for spells and check - static uint32 const infinityCooldownDelayCheck = MONTH/2; - bool HasSpellCooldown(uint32 spell_id) const; - uint32 GetSpellCooldownDelay(uint32 spell_id) const; - void AddSpellAndCategoryCooldowns(SpellInfo const* spellInfo, uint32 itemId, Spell* spell = NULL, bool infinityCooldown = false); - void AddSpellCooldown(uint32 spell_id, uint32 itemid, time_t end_time); - void ModifySpellCooldown(uint32 spellId, int32 cooldown); - void SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId = 0, Spell* spell = NULL, bool setCooldown = true); - void ProhibitSpellSchool(SpellSchoolMask idSchoolMask, uint32 unTimeMs) override; - void RemoveSpellCooldown(uint32 spell_id, bool update = false); - void RemoveSpellCategoryCooldown(uint32 cat, bool update = false); - void SendClearCooldown(uint32 spell_id, Unit* target); - void SendClearAllCooldowns(Unit* target); - - GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; } - - void RemoveCategoryCooldown(uint32 cat); void RemoveArenaSpellCooldowns(bool removeActivePetCooldowns = false); - void RemoveAllSpellCooldown(); - void _LoadSpellCooldowns(PreparedQueryResult result); - void _SaveSpellCooldowns(SQLTransaction& trans); uint32 GetLastPotionId() { return m_lastPotionId; } void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; } void UpdatePotionCooldown(Spell* spell = NULL); @@ -2262,8 +2234,6 @@ class Player : public Unit, public GridObject<Player> //End of PvP System - inline SpellCooldowns GetSpellCooldowns() const { return m_spellCooldowns; } - void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0); uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION); } static DrunkenState GetDrunkenstateByValue(uint8 value); @@ -2844,8 +2814,6 @@ class Player : public Unit, public GridObject<Player> std::unordered_map<uint32 /*overridenSpellId*/, std::unordered_set<uint32> /*newSpellId*/> m_overrideSpells; uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use - GlobalCooldownMgr m_GlobalCooldownMgr; - PlayerTalentInfo* _talentMgr; ActionButtonList m_actionButtons; @@ -3001,8 +2969,6 @@ class Player : public Unit, public GridObject<Player> AchievementMgr<Player>* m_achievementMgr; ReputationMgr* m_reputationMgr; - SpellCooldowns m_spellCooldowns; - uint32 m_ChampioningFaction; std::queue<uint32> m_timeSyncQueue; diff --git a/src/server/game/Entities/Totem/Totem.cpp b/src/server/game/Entities/Totem/Totem.cpp index 1a7a7fbb00a..724c1a3c3d9 100644 --- a/src/server/game/Entities/Totem/Totem.cpp +++ b/src/server/game/Entities/Totem/Totem.cpp @@ -22,6 +22,7 @@ #include "ObjectMgr.h" #include "Opcodes.h" #include "Player.h" +#include "SpellHistory.h" #include "SpellMgr.h" #include "SpellInfo.h" #include "WorldPacket.h" @@ -124,7 +125,7 @@ void Totem::UnSummon(uint32 msTime) owner->SendAutoRepeatCancel(this); if (SpellInfo const* spell = sSpellMgr->GetSpellInfo(GetUInt32Value(UNIT_CREATED_BY_SPELL))) - owner->SendCooldownEvent(spell, 0, NULL, false); + GetSpellHistory()->SendCooldownEvent(spell, 0, nullptr, false); if (Group* group = owner->GetGroup()) { diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index fe16759ff98..92caf93a49f 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -51,6 +51,7 @@ #include "SpellAuras.h" #include "Spell.h" #include "SpellInfo.h" +#include "SpellHistory.h" #include "SpellMgr.h" #include "TemporarySummon.h" #include "Totem.h" @@ -193,7 +194,7 @@ Unit::Unit(bool isWorldObject) : i_AI(NULL), i_disabledAI(NULL), m_AutoRepeatFirstCast(false), m_procDeep(0), m_removedAurasCount(0), i_motionMaster(new MotionMaster(this)), m_regenTimer(0), m_ThreatManager(this), m_vehicle(NULL), m_vehicleKit(NULL), m_unitTypeMask(UNIT_MASK_NONE), - m_HostileRefManager(this), _lastDamagedTime(0) + m_HostileRefManager(this), _lastDamagedTime(0), _spellHistory(new SpellHistory(this)) { m_objectType |= TYPEMASK_UNIT; m_objectTypeId = TYPEID_UNIT; @@ -290,24 +291,6 @@ Unit::Unit(bool isWorldObject) : } //////////////////////////////////////////////////////////// -// Methods of class GlobalCooldownMgr -bool GlobalCooldownMgr::HasGlobalCooldown(SpellInfo const* spellInfo) const -{ - GlobalCooldownList::const_iterator itr = m_GlobalCooldowns.find(spellInfo->StartRecoveryCategory); - return itr != m_GlobalCooldowns.end() && itr->second.duration && getMSTimeDiff(itr->second.cast_time, getMSTime()) < itr->second.duration; -} - -void GlobalCooldownMgr::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 gcd) -{ - m_GlobalCooldowns[spellInfo->StartRecoveryCategory] = GlobalCooldown(gcd, getMSTime()); -} - -void GlobalCooldownMgr::CancelGlobalCooldown(SpellInfo const* spellInfo) -{ - m_GlobalCooldowns[spellInfo->StartRecoveryCategory].duration = 0; -} - -//////////////////////////////////////////////////////////// // Methods of class Unit Unit::~Unit() { @@ -324,6 +307,7 @@ Unit::~Unit() delete i_motionMaster; delete m_charmInfo; delete movespline; + delete _spellHistory; ASSERT(!m_duringRemoveFromWorld); ASSERT(!m_attacking); @@ -2124,20 +2108,11 @@ void Unit::SendMeleeAttackStart(Unit* victim) packet.Attacker = GetGUID(); packet.Victim = victim->GetGUID(); SendMessageToSet(packet.Write(), true); - TC_LOG_DEBUG("entities.unit", "WORLD: Sent SMSG_ATTACKSTART"); } void Unit::SendMeleeAttackStop(Unit* victim) { - WorldPackets::Combat::SAttackStop packet; - packet.Attacker = GetGUID(); - if (victim) - { - packet.Victim = victim->GetGUID(); - packet.NowDead = victim->isDead(); - } - - SendMessageToSet(packet.Write(), true); + SendMessageToSet(WorldPackets::Combat::SAttackStop(this, victim).Write(), true); if (victim) TC_LOG_INFO("entities.unit", "%s stopped attacking %s", GetGUID().ToString().c_str(), victim->GetGUID().ToString().c_str()); @@ -2708,6 +2683,8 @@ void Unit::_UpdateSpells(uint32 time) ++itr; } } + + _spellHistory->Update(); } void Unit::_UpdateAutoRepeatSpell() @@ -4647,13 +4624,13 @@ void Unit::AddGameObject(GameObject* gameObj) m_gameObj.push_back(gameObj); gameObj->SetOwnerGUID(GetGUID()); - if (GetTypeId() == TYPEID_PLAYER && gameObj->GetSpellId()) + if (gameObj->GetSpellId()) { SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(gameObj->GetSpellId()); // Need disable spell use for owner if (createBySpell && createBySpell->IsCooldownStartedOnEvent()) // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases) - ToPlayer()->AddSpellAndCategoryCooldowns(createBySpell, 0, NULL, true); + GetSpellHistory()->StartCooldown(createBySpell, 0, nullptr, true); } } @@ -4678,14 +4655,11 @@ void Unit::RemoveGameObject(GameObject* gameObj, bool del) { RemoveAurasDueToSpell(spellid); - if (GetTypeId() == TYPEID_PLAYER) - { - SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(spellid); - // Need activate spell use for owner - if (createBySpell && createBySpell->IsCooldownStartedOnEvent()) - // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases) - ToPlayer()->SendCooldownEvent(createBySpell); - } + SpellInfo const* createBySpell = sSpellMgr->GetSpellInfo(spellid); + // Need activate spell use for owner + if (createBySpell && createBySpell->IsCooldownStartedOnEvent()) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existing cases) + GetSpellHistory()->SendCooldownEvent(createBySpell); } m_gameObj.remove(gameObj); @@ -5223,8 +5197,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) { - if (!ToPlayer()->HasSpellCooldown(*itr)) - ToPlayer()->AddSpellCooldown(*itr, 0, time(NULL) + cooldown); + if (!GetSpellHistory()->HasCooldown(*itr)) + GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); } break; } @@ -5269,8 +5243,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) { - if (!ToPlayer()->HasSpellCooldown(*itr)) - ToPlayer()->AddSpellCooldown(*itr, 0, time(NULL) + cooldown); + if (!GetSpellHistory()->HasCooldown(*itr)) + GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); } break; } @@ -5850,7 +5824,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere return false; // custom cooldown processing case - if (cooldown && player->HasSpellCooldown(dummySpell->Id)) + if (cooldown && GetSpellHistory()->HasCooldown(dummySpell->Id)) return false; if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID()) @@ -5897,7 +5871,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // apply cooldown before cast to prevent processing itself if (cooldown) - player->AddSpellCooldown(dummySpell->Id, 0, time(NULL) + cooldown); + player->GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown)); // Attack Twice for (uint32 i = 0; i < 2; ++i) @@ -6036,10 +6010,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere { uint32 spell = 26364; - // custom cooldown processing case - if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(spell)) - ToPlayer()->RemoveSpellCooldown(spell); - + GetSpellHistory()->ResetCooldown(spell); CastSpell(target, spell, true, castItem, triggeredByAura); return true; } @@ -6138,7 +6109,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere if (cooldown_spell_id == 0) cooldown_spell_id = triggered_spell_id; - if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(cooldown_spell_id)) + if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(cooldown_spell_id)) return false; if (basepoints0) @@ -6146,8 +6117,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere else CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster); - if (cooldown && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->AddSpellCooldown(cooldown_spell_id, 0, time(NULL) + cooldown); + if (cooldown) + GetSpellHistory()->AddCooldown(cooldown_spell_id, 0, std::chrono::seconds(cooldown)); return true; } @@ -6294,9 +6265,10 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 /*damage*/, Aura* triggeredByAura *handled = true; if (cooldown && GetTypeId() == TYPEID_PLAYER) { - if (ToPlayer()->HasSpellCooldown(100000)) + if (GetSpellHistory()->HasCooldown(100000)) return false; - ToPlayer()->AddSpellCooldown(100000, 0, time(NULL) + cooldown); + + GetSpellHistory()->AddCooldown(100000, 0, std::chrono::seconds(cooldown)); } return true; } @@ -6810,7 +6782,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg } } - if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(trigger_spell_id)) + if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(trigger_spell_id)) return false; // extra attack should hit same target @@ -6826,8 +6798,8 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg else CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - if (cooldown && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->AddSpellCooldown(trigger_spell_id, 0, time(NULL) + cooldown); + if (cooldown) + GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown)); return true; } @@ -6882,13 +6854,13 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, Au return false; } - if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->HasSpellCooldown(triggered_spell_id)) + if (cooldown && GetTypeId() == TYPEID_PLAYER && ToPlayer()->GetSpellHistory()->HasCooldown(triggered_spell_id)) return false; CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura); - if (cooldown && GetTypeId() == TYPEID_PLAYER) - ToPlayer()->AddSpellCooldown(triggered_spell_id, 0, time(NULL) + cooldown); + if (cooldown) + GetSpellHistory()->AddCooldown(triggered_spell_id, 0, std::chrono::seconds(cooldown)); return true; } @@ -7621,14 +7593,11 @@ void Unit::SetMinion(Minion *minion, bool apply) if (minion->IsPetGhoul()) minion->setPowerType(POWER_ENERGY); - if (GetTypeId() == TYPEID_PLAYER) - { - // Send infinity cooldown - client does that automatically but after relog cooldown needs to be set again - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL)); + // Send infinity cooldown - client does that automatically but after relog cooldown needs to be set again + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL)); - if (spellInfo && (spellInfo->IsCooldownStartedOnEvent())) - ToPlayer()->AddSpellAndCategoryCooldowns(spellInfo, 0, NULL, true); - } + if (spellInfo && (spellInfo->IsCooldownStartedOnEvent())) + GetSpellHistory()->StartCooldown(spellInfo, 0, nullptr, true); } else { @@ -7662,13 +7631,10 @@ void Unit::SetMinion(Minion *minion, bool apply) } } - if (GetTypeId() == TYPEID_PLAYER) - { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL)); - // Remove infinity cooldown - if (spellInfo && (spellInfo->IsCooldownStartedOnEvent())) - ToPlayer()->SendCooldownEvent(spellInfo); - } + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL)); + // Remove infinity cooldown + if (spellInfo && (spellInfo->IsCooldownStartedOnEvent())) + GetSpellHistory()->SendCooldownEvent(spellInfo); //if (minion->HasUnitTypeMask(UNIT_MASK_GUARDIAN)) { @@ -9672,12 +9638,12 @@ MountCapabilityEntry const* Unit::GetMountCapability(uint32 mountType) const if (HasExtraUnitMovementFlag(MOVEMENTFLAG2_FULL_SPEED_PITCHING)) { - if (!(mountCapability->Flags & MOUNT_FLAG_CAN_PITCH)) + if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_CAN_PITCH)) continue; } else if (HasUnitMovementFlag(MOVEMENTFLAG_SWIMMING)) { - if (!(mountCapability->Flags & MOUNT_FLAG_CAN_SWIM)) + if (!(mountCapability->Flags & MOUNT_CAPABILITY_FLAG_CAN_SWIM)) continue; } else if (!(mountCapability->Flags & 0x1)) // unknown flags, checked in 4.2.2 14545 client @@ -11465,17 +11431,9 @@ void Unit::SetPower(Powers power, int32 val) if (IsInWorld()) { WorldPackets::Combat::PowerUpdate packet; - WorldPackets::Combat::PowerUpdatePower power; packet.Guid = GetGUID(); /// @todo: Support multiple counts ? - /*for (uint8 i = 0; i < 1; i++) - { - _power.Power = val; - _power.PowerType = powerIndex; - }*/ - power.Power = val; - power.PowerType = powerIndex; - packet.Powers.push_back(power); + packet.Powers.emplace_back(val, powerIndex); SendMessageToSet(packet.Write(), GetTypeId() == TYPEID_PLAYER); } @@ -16404,27 +16362,6 @@ void Unit::DestroyForPlayer(Player* target) const WorldObject::DestroyForPlayer(target); } -void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown) -{ - data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + 4 + 4); - data << GetGUID(); - data << uint8(flags); - data << uint32(spellId); - data << uint32(cooldown); -} - -void Unit::BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns) -{ - data.Initialize(SMSG_SPELL_COOLDOWN, 8 + 1 + (4 + 4) * cooldowns.size()); - data << GetGUID(); - data << uint8(flags); - for (std::unordered_map<uint32, uint32>::const_iterator itr = cooldowns.begin(); itr != cooldowns.end(); ++itr) - { - data << uint32(itr->first); - data << uint32(itr->second); - } -} - int32 Unit::GetHighestExclusiveSameEffectSpellGroupValue(AuraEffect const* aurEff, AuraType auraType, bool checkMiscValue /*= false*/, int32 miscValue /*= 0*/) const { int32 val = 0; diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 82f7240bc76..3f9570b548f 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -397,6 +397,7 @@ class Creature; class Spell; class SpellInfo; class SpellEffectInfo; +class SpellHistory; class DynamicObject; class GameObject; class Item; @@ -1105,30 +1106,6 @@ enum CurrentSpellTypes #define CURRENT_FIRST_NON_MELEE_SPELL 1 #define CURRENT_MAX_SPELL 4 -struct GlobalCooldown -{ - explicit GlobalCooldown(uint32 _dur = 0, uint32 _time = 0) : duration(_dur), cast_time(_time) { } - - uint32 duration; - uint32 cast_time; -}; - -typedef std::unordered_map<uint32 /*category*/, GlobalCooldown> GlobalCooldownList; - -class GlobalCooldownMgr // Shared by Player and CharmInfo -{ -public: - GlobalCooldownMgr() { } - -public: - bool HasGlobalCooldown(SpellInfo const* spellInfo) const; - void AddGlobalCooldown(SpellInfo const* spellInfo, uint32 gcd); - void CancelGlobalCooldown(SpellInfo const* spellInfo); - -private: - GlobalCooldownList m_GlobalCooldowns; -}; - enum ActiveStates { ACT_PASSIVE = 0x01, // 0x01 - passive @@ -1246,8 +1223,6 @@ struct CharmInfo CharmSpellInfo* GetCharmSpell(uint8 index) { return &(_charmspells[index]); } - GlobalCooldownMgr& GetGlobalCooldownMgr() { return m_GlobalCooldownMgr; } - void SetIsCommandAttack(bool val); bool IsCommandAttack(); void SetIsCommandFollow(bool val); @@ -1280,8 +1255,6 @@ struct CharmInfo float _stayX; float _stayY; float _stayZ; - - GlobalCooldownMgr m_GlobalCooldownMgr; }; // for clearing special attacks @@ -1312,16 +1285,6 @@ enum PlayerTotemType SUMMON_TYPE_TOTEM_AIR = 83 }; -/// Spell cooldown flags sent in SMSG_SPELL_COOLDOWN -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 -}; - -typedef std::unordered_map<uint32, uint32> PacketCooldowns; - // delay time next attack to prevent client attack animation problems #define ATTACK_DISPLAY_DELAY 200 #define MAX_PLAYER_STEALTH_DETECT_RANGE 30.0f // max distance for detection targets by player @@ -1658,8 +1621,6 @@ class Unit : public WorldObject Aura* AddAura(SpellInfo const* spellInfo, uint32 effMask, Unit* target); void SetAuraStack(uint32 spellId, Unit* target, uint32 stack); void SendPlaySpellVisualKit(uint32 id, uint32 unkParam); - void BuildCooldownPacket(WorldPacket& data, uint8 flags, uint32 spellId, uint32 cooldown); - void BuildCooldownPacket(WorldPacket& data, uint8 flags, PacketCooldowns const& cooldowns); void DeMorph(); @@ -1920,7 +1881,6 @@ class Unit : public WorldObject void SetChannelObjectGuid(ObjectGuid guid) { SetGuidValue(UNIT_FIELD_CHANNEL_OBJECT, guid); } void SetCurrentCastSpell(Spell* pSpell); - virtual void ProhibitSpellSchool(SpellSchoolMask /*idSchoolMask*/, uint32 /*unTimeMs*/) { } void InterruptSpell(CurrentSpellTypes spellType, bool withDelayed = true, bool withInstant = true); void FinishSpell(CurrentSpellTypes spellType, bool ok = true); @@ -1939,6 +1899,9 @@ class Unit : public WorldObject int32 GetCurrentSpellCastTime(uint32 spell_id) const; virtual SpellInfo const* GetCastSpellInfo(SpellInfo const* spellInfo) const; + SpellHistory* GetSpellHistory() { return _spellHistory; } + SpellHistory const* GetSpellHistory() const { return _spellHistory; } + ObjectGuid m_SummonSlot[MAX_SUMMON_SLOT]; ObjectGuid m_ObjectSlot[MAX_GAMEOBJECT_SLOT]; @@ -2374,6 +2337,8 @@ class Unit : public WorldObject uint16 _aiAnimKitId; uint16 _movementAnimKitId; uint16 _meleeAnimKitId; + + SpellHistory* _spellHistory; }; namespace Trinity diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 25d6512acaf..479b63008d4 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -2438,14 +2438,7 @@ void ObjectMgr::LoadItemTemplates() if (itemItr == _itemTemplateStore.end()) continue; - ItemEffect effect; - effect.SpellID = effectEntry->SpellID; - effect.Trigger = effectEntry->Trigger; - effect.Charges = effectEntry->Charges; - effect.Cooldown = effectEntry->Cooldown; - effect.Category = effectEntry->Category; - effect.CategoryCooldown = effectEntry->CategoryCooldown; - itemItr->second.Effects.push_back(effect); + itemItr->second.Effects.push_back(effectEntry); } // Check if item templates for DBC referenced character start outfit are present diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index 74df5b443bd..ee5bc6cd84a 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -173,7 +173,7 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket& /*recvData*/) for (uint8 j = 0; j < MAX_HOLIDAY_FLAGS; ++j) data << uint32(holiday->CalendarFlags[j]); // 10 * m_calendarFlags - data << holiday->TextureFilename; // m_textureFilename (holiday name) + data << holiday->TextureFilename->Str[sWorld->GetDefaultDbcLocale()]; // m_textureFilename (holiday name) } SendPacket(&data); diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 255eea8fbe6..0385b8f1a39 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -162,6 +162,10 @@ bool LoginQueryHolder::Initialize() stmt->setUInt64(0, lowGuid); res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SPELL_COOLDOWNS, stmt); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_SPELL_CHARGES); + stmt->setUInt64(0, lowGuid); + res &= SetPreparedQuery(PLAYER_LOGIN_QUERY_LOAD_SPELL_CHARGES, stmt); + if (sWorld->getBoolConfig(CONFIG_DECLINED_NAMES_USED)) { stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES); diff --git a/src/server/game/Handlers/CombatHandler.cpp b/src/server/game/Handlers/CombatHandler.cpp index 36f37403f32..9b9a32b2c2a 100644 --- a/src/server/game/Handlers/CombatHandler.cpp +++ b/src/server/game/Handlers/CombatHandler.cpp @@ -31,19 +31,19 @@ void WorldSession::HandleAttackSwingOpcode(WorldPackets::Combat::AttackSwing& packet) { - Unit* pEnemy = ObjectAccessor::GetUnit(*_player, packet.Victim); + Unit* enemy = ObjectAccessor::GetUnit(*_player, packet.Victim); - if (!pEnemy) + if (!enemy) { // stop attack state at client - SendAttackStop(NULL); + SendAttackStop(nullptr); return; } - if (!_player->IsValidAttackTarget(pEnemy)) + if (!_player->IsValidAttackTarget(enemy)) { // stop attack state at client - SendAttackStop(pEnemy); + SendAttackStop(enemy); return; } @@ -56,12 +56,12 @@ void WorldSession::HandleAttackSwingOpcode(WorldPackets::Combat::AttackSwing& pa ASSERT(seat); if (!(seat->Flags & VEHICLE_SEAT_FLAG_CAN_ATTACK)) { - SendAttackStop(pEnemy); + SendAttackStop(enemy); return; } } - _player->Attack(pEnemy, true); + _player->Attack(enemy, true); } void WorldSession::HandleAttackStopOpcode(WorldPackets::Combat::AttackStop& /*recvData*/) @@ -82,6 +82,5 @@ void WorldSession::HandleSetSheathedOpcode(WorldPackets::Combat::SetSheathed& pa void WorldSession::SendAttackStop(Unit const* enemy) { - WorldPackets::Combat::SAttackStop packet(GetPlayer()->GetGUID(), enemy->GetGUID(), enemy->isDead()); - SendPacket(packet.Write()); + SendPacket(WorldPackets::Combat::SAttackStop(GetPlayer(), enemy).Write()); } diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index c1ac3cb18b2..193cc951a48 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -30,6 +30,7 @@ #include "Pet.h" #include "World.h" #include "Group.h" +#include "SpellHistory.h" #include "SpellInfo.h" #include "Player.h" @@ -361,8 +362,6 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe if (result == SPELL_CAST_OK) { - pet->ToCreature()->AddCreatureSpellCooldown(spellid); - unit_target = spell->m_targets.GetUnitTarget(); //10% chance to play special pet attack talk, else growl @@ -396,8 +395,8 @@ void WorldSession::HandlePetActionHelper(Unit* pet, ObjectGuid guid1, uint32 spe else spell->SendPetCastResult(result); - if (!pet->ToCreature()->HasSpellCooldown(spellid)) - GetPlayer()->SendClearCooldown(spellid, pet); + if (!pet->GetSpellHistory()->HasCooldown(spellid)) + pet->GetSpellHistory()->ResetCooldown(spellid, true); spell->finish(false); delete spell; @@ -804,7 +803,6 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& { if (Creature* creature = caster->ToCreature()) { - creature->AddCreatureSpellCooldown(spellId); if (Pet* pet = creature->ToPet()) { // 10% chance to play special pet attack talk, else growl @@ -822,16 +820,8 @@ void WorldSession::HandlePetCastSpellOpcode(WorldPackets::Spells::PetCastSpell& { spell->SendPetCastResult(result); - if (caster->GetTypeId() == TYPEID_PLAYER) - { - if (!caster->ToPlayer()->HasSpellCooldown(spellId)) - GetPlayer()->SendClearCooldown(spellId, caster); - } - else - { - if (!caster->ToCreature()->HasSpellCooldown(spellId)) - GetPlayer()->SendClearCooldown(spellId, caster); - } + if (!caster->GetSpellHistory()->HasCooldown(spellId)) + caster->GetSpellHistory()->ResetCooldown(spellId, true); spell->finish(false); delete spell; diff --git a/src/server/game/Handlers/PetitionsHandler.cpp b/src/server/game/Handlers/PetitionsHandler.cpp index bb658612f25..6dd8d95bbf6 100644 --- a/src/server/game/Handlers/PetitionsHandler.cpp +++ b/src/server/game/Handlers/PetitionsHandler.cpp @@ -177,7 +177,9 @@ void WorldSession::HandlePetitionShowSignatures(WorldPackets::Petition::Petition signature.Choice = 0; signaturesPacket.Signatures.push_back(signature); - result->NextRow(); + // Checking the return value just to be double safe + if (!result->NextRow()) + break; } SendPacket(signaturesPacket.Write()); @@ -447,7 +449,9 @@ void WorldSession::HandleOfferPetition(WorldPackets::Petition::OfferPetition& pa signature.Choice = 0; signaturesPacket.Signatures.push_back(signature); - result->NextRow(); + // Checking the return value just to be double safe + if (!result->NextRow()) + break; } player->GetSession()->SendPacket(signaturesPacket.Write()); @@ -551,7 +555,10 @@ void WorldSession::HandleTurnInPetition(WorldPackets::Petition::TurnInPetition& { Field* fields = result->Fetch(); guild->AddMember(ObjectGuid::Create<HighGuid::Player>(fields[0].GetUInt64())); - result->NextRow(); + + // Checking the return value just to be double safe + if (!result->NextRow()) + break; } SQLTransaction trans = CharacterDatabase.BeginTransaction(); diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index 3a173e4e582..b3a094b86c5 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -98,7 +98,7 @@ void WorldSession::HandleUseItemOpcode(WorldPackets::Spells::UseItem& packet) { for (uint32 i = 0; i < proto->Effects.size(); ++i) { - if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Effects[i].SpellID)) + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(proto->Effects[i]->SpellID)) { if (!spellInfo->CanBeUsedInCombat()) { @@ -418,8 +418,6 @@ void WorldSession::HandlePetCancelAuraOpcode(WorldPacket& recvPacket) } pet->RemoveOwnedAura(spellId, ObjectGuid::Empty, 0, AURA_REMOVE_BY_CANCEL); - - pet->AddCreatureSpellCooldown(spellId); } void WorldSession::HandleCancelGrowthAuraOpcode(WorldPacket& /*recvPacket*/) { } diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 5cffc0eb7aa..53413dc55f3 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -392,7 +392,7 @@ bool LootItem::AllowedForPlayer(Player const* player) const return false; // not show loot for players without profession or those who already know the recipe - if ((pProto->GetFlags() & ITEM_PROTO_FLAG_SMART_LOOT) && (!player->HasSkill(pProto->GetRequiredSkill()) || player->HasSpell(pProto->Effects[1].SpellID))) + if ((pProto->GetFlags() & ITEM_PROTO_FLAG_SMART_LOOT) && (!player->HasSkill(pProto->GetRequiredSkill()) || player->HasSpell(pProto->Effects[1]->SpellID))) return false; // not show loot for not own team diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 75ea54a9dc5..a14bcb50107 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -35,11 +35,6 @@ namespace WorldPackets { class LootResponse; } - - namespace Item - { - struct ItemInstance; - } } enum RollType @@ -155,8 +150,7 @@ struct LootStoreItem { } bool Roll(bool rate) const; // Checks if the entry takes it's chance (at loot generation) - bool IsValid(LootStore const& store, uint32 entry) const; - // Checks correctness of values + bool IsValid(LootStore const& store, uint32 entry) const; // Checks correctness of values }; struct LootItem diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index 635350b6dad..7eb38f3e637 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -635,7 +635,7 @@ void MotionMaster::DirectDelete(_Ty curr) void MotionMaster::DelayedDelete(_Ty curr) { - TC_LOG_FATAL("misc", "Unit (Entry %u) is trying to delete its updating MG (Type %u)!", _owner->GetEntry(), curr->GetMovementGeneratorType()); + TC_LOG_FATAL("misc", "Unit (Entry %u) is trying to delete its updating Movement Generator (Type %u)!", _owner->GetEntry(), curr->GetMovementGeneratorType()); if (isStatic(curr)) return; if (!_expList) diff --git a/src/server/game/Reputation/ReputationMgr.cpp b/src/server/game/Reputation/ReputationMgr.cpp index 92a41e8c6a6..adc4768501c 100644 --- a/src/server/game/Reputation/ReputationMgr.cpp +++ b/src/server/game/Reputation/ReputationMgr.cpp @@ -49,7 +49,7 @@ bool ReputationMgr::IsAtWar(uint32 faction_id) const if (!factionEntry) { TC_LOG_ERROR("misc", "ReputationMgr::IsAtWar: Can't get AtWar flag of %s for unknown faction (faction id) #%u.", _player->GetName().c_str(), faction_id); - return 0; + return false; } return IsAtWar(factionEntry); diff --git a/src/server/game/Server/Packets/CharacterPackets.cpp b/src/server/game/Server/Packets/CharacterPackets.cpp index 8b3d92dd1ff..0eabb1ee351 100644 --- a/src/server/game/Server/Packets/CharacterPackets.cpp +++ b/src/server/game/Server/Packets/CharacterPackets.cpp @@ -46,7 +46,7 @@ WorldPackets::Character::EnumCharactersResult::CharacterInfo::CharacterInfo(Fiel PreLoadPosition.y = fields[11].GetFloat(); PreLoadPosition.z = fields[12].GetFloat(); - if (uint32 guildId = fields[13].GetUInt32()) + if (ObjectGuid::LowType guildId = fields[13].GetUInt64()) GuildGuid = ObjectGuid::Create<HighGuid::Guild>(guildId); uint32 playerFlags = fields[14].GetUInt32(); diff --git a/src/server/game/Server/Packets/CombatPackets.cpp b/src/server/game/Server/Packets/CombatPackets.cpp index 5a23729f7bc..69718cb932f 100644 --- a/src/server/game/Server/Packets/CombatPackets.cpp +++ b/src/server/game/Server/Packets/CombatPackets.cpp @@ -31,6 +31,16 @@ WorldPacket const* WorldPackets::Combat::AttackStart::Write() return &_worldPacket; } +WorldPackets::Combat::SAttackStop::SAttackStop(Unit const* attacker, Unit const* victim) : ServerPacket(SMSG_ATTACKSTOP, 16 + 16 + 1) +{ + Attacker = attacker->GetGUID(); + if (victim) + { + Victim = victim->GetGUID(); + NowDead = victim->isDead(); + } +} + WorldPacket const* WorldPackets::Combat::SAttackStop::Write() { _worldPacket << Attacker; @@ -152,7 +162,7 @@ WorldPacket const* WorldPackets::Combat::PowerUpdate::Write() { _worldPacket << Guid; _worldPacket << uint32(Powers.size()); - for (WorldPackets::Combat::PowerUpdatePower const& power : Powers) + for (PowerUpdatePower const& power : Powers) { _worldPacket << power.Power; _worldPacket << power.PowerType; diff --git a/src/server/game/Server/Packets/CombatPackets.h b/src/server/game/Server/Packets/CombatPackets.h index 4e2aa3c34b2..4eeb61d3523 100644 --- a/src/server/game/Server/Packets/CombatPackets.h +++ b/src/server/game/Server/Packets/CombatPackets.h @@ -70,7 +70,7 @@ namespace WorldPackets { public: SAttackStop() : ServerPacket(SMSG_ATTACKSTOP, 16 + 16 + 1) { } - SAttackStop(ObjectGuid attacker, ObjectGuid victim, bool nowDead) : ServerPacket(SMSG_ATTACKSTOP, 16 + 16 + 1), Attacker(attacker), Victim(victim), NowDead(nowDead) { } + SAttackStop(Unit const* attacker, Unit const* victim); WorldPacket const* Write() override; @@ -162,7 +162,7 @@ namespace WorldPackets WorldPacket const* Write() override; - Optional<WorldPackets::Spells::SpellCastLogData> LogData; + Optional<Spells::SpellCastLogData> LogData; uint32 HitInfo = 0; // Flags ObjectGuid AttackerGUID; ObjectGuid VictimGUID; @@ -188,6 +188,8 @@ namespace WorldPackets struct PowerUpdatePower { + PowerUpdatePower(int32 power, uint8 powerType) : Power(power), PowerType(powerType) { } + int32 Power = 0; uint8 PowerType = 0; }; @@ -196,7 +198,7 @@ namespace WorldPackets { public: PowerUpdate() : ServerPacket(SMSG_POWER_UPDATE, 16 + 4 + 1) { } - + WorldPacket const* Write() override; ObjectGuid Guid; diff --git a/src/server/game/Server/Packets/ItemPackets.cpp b/src/server/game/Server/Packets/ItemPackets.cpp index dbc7ba2615a..9277e0b73d5 100644 --- a/src/server/game/Server/Packets/ItemPackets.cpp +++ b/src/server/game/Server/Packets/ItemPackets.cpp @@ -175,16 +175,16 @@ void WorldPackets::Item::ItemInstance::Initalize(::Item const* item) { ItemBonus.HasValue = true; ItemBonus.Value.BonusListIDs.insert(ItemBonus.Value.BonusListIDs.end(), bonusListIds.begin(), bonusListIds.end()); - ItemBonus.Value.Context = 0; /// @todo + ItemBonus.Value.Context = item->GetUInt32Value(ITEM_FIELD_CONTEXT); } - for (uint8 i = 1; i < MAX_ITEM_MODIFIERS; ++i) + uint32 mask = item->GetUInt32Value(ITEM_FIELD_MODIFIERS_MASK); + Modifications.HasValue = mask != 0; + + for (size_t i = 0; mask != 0; mask >>= 1, ++i) { - if (int32 mod = item->GetModifier(ItemModifier(i))) - { - Modifications.HasValue = true; - Modifications.Value.Insert(i - 1, mod); - } + if ((mask & 1) != 0) + Modifications.Value.Insert(i, item->GetModifier(ItemModifier(i))); } } diff --git a/src/server/game/Server/Packets/NPCPackets.cpp b/src/server/game/Server/Packets/NPCPackets.cpp index 4dc3d66a852..7bf96b03bab 100644 --- a/src/server/game/Server/Packets/NPCPackets.cpp +++ b/src/server/game/Server/Packets/NPCPackets.cpp @@ -16,7 +16,6 @@ */ #include "NPCPackets.h" -#include "ItemPackets.h" void WorldPackets::NPC::Hello::Read() { diff --git a/src/server/game/Server/Packets/NPCPackets.h b/src/server/game/Server/Packets/NPCPackets.h index bce5dbbd074..e02fd21dd39 100644 --- a/src/server/game/Server/Packets/NPCPackets.h +++ b/src/server/game/Server/Packets/NPCPackets.h @@ -85,8 +85,8 @@ namespace WorldPackets void Read() override; ObjectGuid GossipUnit; - int32 GossipIndex; - int32 GossipID; + int32 GossipIndex = 0; + int32 GossipID = 0; std::string PromotionCode; }; diff --git a/src/server/game/Server/Packets/SpellPackets.cpp b/src/server/game/Server/Packets/SpellPackets.cpp index 50251c71c8b..a79ff8d4c96 100644 --- a/src/server/game/Server/Packets/SpellPackets.cpp +++ b/src/server/game/Server/Packets/SpellPackets.cpp @@ -112,15 +112,15 @@ WorldPacket const* WorldPackets::Spells::AuraUpdate::Write() _worldPacket << uint32(data.ActiveFlags); _worldPacket << uint16(data.CastLevel); _worldPacket << uint8(data.Applications); - _worldPacket << uint32(data.EstimatedPoints.size()); _worldPacket << uint32(data.Points.size()); - - if (!data.EstimatedPoints.empty()) - _worldPacket.append(data.EstimatedPoints.data(), data.EstimatedPoints.size()); + _worldPacket << uint32(data.EstimatedPoints.size()); if (!data.Points.empty()) _worldPacket.append(data.Points.data(), data.Points.size()); + if (!data.EstimatedPoints.empty()) + _worldPacket.append(data.EstimatedPoints.data(), data.EstimatedPoints.size()); + _worldPacket.WriteBit(data.CastUnit.HasValue); _worldPacket.WriteBit(data.Duration.HasValue); _worldPacket.WriteBit(data.Remaining.HasValue); @@ -487,3 +487,114 @@ WorldPacket const* WorldPackets::Spells::CooldownEvent::Write() return &_worldPacket; } + +WorldPacket const* WorldPackets::Spells::ClearCooldowns::Write() +{ + _worldPacket << Guid; + _worldPacket << uint32(SpellID.size()); + if (!SpellID.empty()) + _worldPacket.append(SpellID.data(), SpellID.size()); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Spells::ClearCooldown::Write() +{ + _worldPacket << CasterGUID; + _worldPacket << uint32(SpellID); + _worldPacket.WriteBit(ClearOnHold); + _worldPacket.FlushBits(); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Spells::ModifyCooldown::Write() +{ + _worldPacket << int32(SpellID); + _worldPacket << UnitGUID; + _worldPacket << int32(DeltaTime); + + return &_worldPacket; +} + +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellCooldownStruct const& cooldown) +{ + data << uint32(cooldown.SrecID); + data << uint32(cooldown.ForcedCooldown); + return data; +} + +WorldPacket const* WorldPackets::Spells::SpellCooldown::Write() +{ + _worldPacket << Caster; + _worldPacket << uint8(Flags); + _worldPacket << uint32(SpellCooldowns.size()); + for (SpellCooldownStruct const& cooldown : SpellCooldowns) + _worldPacket << cooldown; + + return &_worldPacket; +} + +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellHistoryEntry const& historyEntry) +{ + data << uint32(historyEntry.SpellID); + data << uint32(historyEntry.ItemID); + data << uint32(historyEntry.Category); + data << int32(historyEntry.RecoveryTime); + data << int32(historyEntry.CategoryRecoveryTime); + data.WriteBit(historyEntry.OnHold); + data.FlushBits(); + + return data; +} + +WorldPacket const* WorldPackets::Spells::SendSpellHistory::Write() +{ + _worldPacket << uint32(Entries.size()); + for (SpellHistoryEntry const& historyEntry : Entries) + _worldPacket << historyEntry; + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Spells::ClearAllSpellCharges::Write() +{ + _worldPacket << Unit; + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Spells::ClearSpellCharges::Write() +{ + _worldPacket << Unit; + _worldPacket << int32(Category); + + return &_worldPacket; +} + +WorldPacket const* WorldPackets::Spells::SetSpellCharges::Write() +{ + _worldPacket << int32(Category); + _worldPacket << float(Count); + _worldPacket.WriteBit(IsPet); + _worldPacket.FlushBits(); + + return &_worldPacket; +} + +ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::Spells::SpellChargeEntry const& chargeEntry) +{ + data << uint32(chargeEntry.Category); + data << uint32(chargeEntry.NextRecoveryTime); + data << uint8(chargeEntry.ConsumedCharges); + return data; +} + +WorldPacket const* WorldPackets::Spells::SendSpellCharges::Write() +{ + _worldPacket << uint32(Entries.size()); + for (SpellChargeEntry const& chargeEntry : Entries) + _worldPacket << chargeEntry; + + return &_worldPacket; +} diff --git a/src/server/game/Server/Packets/SpellPackets.h b/src/server/game/Server/Packets/SpellPackets.h index ed84885f0a0..269bf1e5935 100644 --- a/src/server/game/Server/Packets/SpellPackets.h +++ b/src/server/game/Server/Packets/SpellPackets.h @@ -226,8 +226,8 @@ namespace WorldPackets void Read() override; - uint8 PackSlot; - uint8 Slot; + uint8 PackSlot = 0; + uint8 Slot = 0; ObjectGuid CastItem; SpellCastRequest Cast; }; @@ -420,6 +420,132 @@ namespace WorldPackets ObjectGuid CasterGUID; int32 SpellID; }; + + class ClearCooldowns final : public ServerPacket + { + public: + ClearCooldowns() : ServerPacket(SMSG_CLEAR_COOLDOWNS, 4 + 16) { } + + WorldPacket const* Write() override; + + std::vector<int32> SpellID; + ObjectGuid Guid; + }; + + class ClearCooldown final : public ServerPacket + { + public: + ClearCooldown() : ServerPacket(SMSG_CLEAR_COOLDOWN, 16 + 4 + 1) { } + + WorldPacket const* Write() override; + + ObjectGuid CasterGUID; + int32 SpellID = 0; + bool ClearOnHold = false; + }; + + class ModifyCooldown final : public ServerPacket + { + public: + ModifyCooldown() : ServerPacket(SMSG_MODIFY_COOLDOWN, 16 + 4 + 4) { } + + WorldPacket const* Write() override; + + ObjectGuid UnitGUID; + int32 DeltaTime = 0; + int32 SpellID = 0; + }; + + struct SpellCooldownStruct + { + SpellCooldownStruct() { } + SpellCooldownStruct(uint32 spellId, uint32 forcedCooldown) : SrecID(spellId), ForcedCooldown(forcedCooldown) { } + + uint32 SrecID = 0; + uint32 ForcedCooldown = 0; + }; + + class SpellCooldown : public ServerPacket + { + public: + SpellCooldown() : ServerPacket(SMSG_SPELL_COOLDOWN, 4 + 16 + 1) { } + + WorldPacket const* Write() override; + + std::vector<SpellCooldownStruct> SpellCooldowns; + ObjectGuid Caster; + uint8 Flags = 0; + }; + + struct SpellHistoryEntry + { + uint32 SpellID = 0; + uint32 ItemID = 0; + uint32 Category = 0; + int32 RecoveryTime = 0; + int32 CategoryRecoveryTime = 0; + bool OnHold = false; + }; + + class SendSpellHistory final : public ServerPacket + { + public: + SendSpellHistory() : ServerPacket(SMSG_SEND_SPELL_HISTORY, 4) { } + + WorldPacket const* Write() override; + + std::vector<SpellHistoryEntry> Entries; + }; + + class ClearAllSpellCharges final : public ServerPacket + { + public: + ClearAllSpellCharges() : ServerPacket(SMSG_CLEAR_ALL_SPELL_CHARGES, 16) { } + + WorldPacket const* Write() override; + + ObjectGuid Unit; + }; + + class ClearSpellCharges final : public ServerPacket + { + public: + ClearSpellCharges() : ServerPacket(SMSG_CLEAR_SPELL_CHARGES, 20) { } + + WorldPacket const* Write() override; + + ObjectGuid Unit; + int32 Category = 0; + }; + + class SetSpellCharges final : public ServerPacket + { + public: + SetSpellCharges() : ServerPacket(SMSG_SET_SPELL_CHARGES, 1 + 4 + 4) { } + + WorldPacket const* Write() override; + + bool IsPet = false; + float Count = 0.0f; + int32 Category = 0; + }; + + struct SpellChargeEntry + { + uint32 Category = 0; + uint32 NextRecoveryTime = 0; + uint8 ConsumedCharges = 0; + }; + + class SendSpellCharges final : public ServerPacket + { + public: + SendSpellCharges() : ServerPacket(SMSG_SEND_SPELL_CHARGES, 4) { } + + WorldPacket const* Write() override; + + std::vector<SpellChargeEntry> Entries; + }; } } diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp index 99ae9cb4c9c..762a9b1eaa0 100644 --- a/src/server/game/Server/Protocol/Opcodes.cpp +++ b/src/server/game/Server/Protocol/Opcodes.cpp @@ -1119,21 +1119,22 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHEAT_IGNORE_DIMISHING_RETURNS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHECK_FOR_BOTS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CHECK_WARGAME_ENTRY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_ALL_SPELL_CHARGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_ALL_SPELL_CHARGES, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_BOSS_EMOTES, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_COOLDOWN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_COOLDOWNS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_COOLDOWN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_COOLDOWNS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_LOSS_OF_CONTROL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_QUEST_COMPLETED_BIT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_QUEST_COMPLETED_BITS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_SPELL_CHARGES, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_SPELL_CHARGES, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLEAR_TARGET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLIENTCACHE_VERSION, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CLIENT_CONTROL_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COIN_REMOVED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMBAT_EVENT_FAILED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMBAT_LOG_MULTIPLE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMBAT_LOG_UNK, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_MAP_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_PARTY_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMMENTATOR_PLAYER_INFO, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1153,7 +1154,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_CONTACT_STATUS, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CONVERT_RUNE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_COOLDOWN_CHEAT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_COOLDOWN_EVENT, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_COOLDOWN_EVENT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_LOCATION, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_RECLAIM_DELAY, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_CORPSE_TRANSPORT_QUERY, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1308,8 +1309,8 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_PLAYER_JOINED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_PLAYER_LEFT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_PRESENCE_CHANGE, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_RANK_CHANGED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_RANKS_UPDATED, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_RANK_CHANGED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_TAB_ADDED, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_TAB_DELETED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_EVENT_TAB_MODIFIED, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1333,13 +1334,12 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_PERMISSIONS_QUERY_RESULTS, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_QUERY_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_RANKS, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_SEND_RANK_CHANGE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_REPUTATION_REACTION_CHANGED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_RESET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_REWARDS_LIST, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_ROSTER, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_ROSTER_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_SEND_RANK_CHANGE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_GUILD_SEND_RANK_CHANGE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_HEALTH_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_HIGHEST_THREAT_UPDATE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_HOTFIX_NOTIFY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1377,7 +1377,6 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_INVALID_PROMOTION_CODE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_INVENTORY_CHANGE_FAILURE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_IS_QUEST_COMPLETE_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_COMBAT_LOG_UNK, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_COOLDOWN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_ENCHANT_TIME_UPDATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_EXPIRE_PURCHASE_REFUND, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1385,7 +1384,6 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_PUSH_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_REFUND_INFO_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_REFUND_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_ITEM_TEXT_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_TIME_UPDATE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_ITEM_UPGRADE_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_JOINED_BATTLEGROUND_QUEUE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1469,7 +1467,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_MIRROR_IMAGE_COMPONENTED_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_MIRROR_IMAGE_CREATURE_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_MISSILE_CANCEL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_MODIFY_COOLDOWN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_MODIFY_COOLDOWN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_MONEY_NOTIFY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOTD, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_MOUNT_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE); @@ -1660,6 +1658,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_LOG_DATA, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_OPTIONS_ENABLED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_PVP_SEASON, STATUS_NEVER, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_ITEM_TEXT_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_PETITION_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUERY_TIME_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUESTGIVER_INVALID_QUEST, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1672,10 +1671,10 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUESTGIVER_REQUEST_ITEMS, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUESTGIVER_STATUS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUESTGIVER_STATUS_MULTIPLE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_COMPLETION_NPC_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_CONFIRM_ACCEPT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_FORCE_REMOVED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_LOG_FULL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_COMPLETION_NPC_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_POI_QUERY_RESPONSE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_PUSH_RESULT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_QUEST_QUERY_RESPONSE, STATUS_NEVER, CONNECTION_TYPE_REALM); @@ -1758,8 +1757,8 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_MAIL_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_RAID_TARGET_UPDATE_ALL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_RAID_TARGET_UPDATE_SINGLE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_SPELL_CHARGES, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_SPELL_HISTORY, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_SPELL_CHARGES, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_SPELL_HISTORY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SEND_UNLEARN_SPELLS, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SERVER_FIRST_ACHIEVEMENT, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SERVER_FIRST_ACHIEVEMENTS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1795,7 +1794,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PROJECTILE_POSITION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_QUEST_COMPLETED_BIT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_RAID_DIFFICULTY, STATUS_NEVER, CONNECTION_TYPE_REALM); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_SPELL_CHARGES, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_SPELL_CHARGES, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_TASK_COMPLETE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_TIME_ZONE_INFORMATION, STATUS_NEVER, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_VEHICLE_REC_ID, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); @@ -1814,7 +1813,7 @@ void OpcodeTable::Initialize() DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELLSTEALLOG, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_ABSORB_LOG, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_CATEGORY_COOLDOWN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); - DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_COOLDOWN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); + DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_COOLDOWN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_DAMAGE_SHIELD, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_DELAYED, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); DEFINE_SERVER_OPCODE_HANDLER(SMSG_SPELL_DISPEL_LOG, STATUS_UNHANDLED, CONNECTION_TYPE_REALM); diff --git a/src/server/game/Server/Protocol/Opcodes.h b/src/server/game/Server/Protocol/Opcodes.h index f80614f9624..50c2daa05aa 100644 --- a/src/server/game/Server/Protocol/Opcodes.h +++ b/src/server/game/Server/Protocol/Opcodes.h @@ -267,13 +267,17 @@ enum OpcodeClient : uint32 CMSG_GARRISON_MISSION_BONUS_ROLL = 0x02C2, CMSG_GARRISON_OPEN_TRADESKILL_NPC = 0x099D, CMSG_GARRISON_PURCHASE_BUILDING = 0x04A2, + CMSG_GARRISON_RECRUIT_FOLLOWER = 0x028E, + CMSG_GARRISON_REMOVE_FOLLOWER = 0x03A5, CMSG_GARRISON_REMOVE_FOLLOWER_FROM_BUILDING = 0x01BB, CMSG_GARRISON_REQUEST_BLUEPRINT_AND_SPECIALIZATION_DATA = 0x16F2, CMSG_GARRISON_REQUEST_LANDING_PAGE_SHIPMENT_INFO = 0x0623, CMSG_GARRISON_REQUEST_UPGRADEABLE = 0x02BD, + CMSG_GARRISON_SET_BUILDING_ACTIVE = 0x041A, CMSG_GARRISON_SET_FOLLOWER_INACTIVE = 0x03BC, CMSG_GARRISON_SET_RECRUITMENT_PREFERENCES = 0x0074, CMSG_GARRISON_START_MISSION = 0x1624, + CMSG_GARRISON_SWAP_BUILDINGS = 0x09BD, CMSG_GARRISON_UNK1 = 0x02F5, CMSG_GETDEATHBINDZONE = 0xBADD, CMSG_GET_CHALLENGE_MODE_REWARDS = 0x1C2F, @@ -779,6 +783,7 @@ enum OpcodeClient : uint32 CMSG_USE_CRITTER_ITEM = 0x0BBE, CMSG_USE_EQUIPMENT_SET = 0x0756, CMSG_USE_ITEM = 0x08B6, + CMSG_USE_PARTY_GARRISON = 0x14DB, CMSG_USE_TOY = 0x16E2, CMSG_VIOLENCE_LEVEL = 0x098D, CMSG_VOICE_ADD_IGNORE = 0x098A, @@ -821,8 +826,9 @@ enum OpcodeServer : uint32 SMSG_ABORT_NEW_WORLD = 0x138C, SMSG_ACCOUNT_CRITERIA_UPDATE = 0x0912, SMSG_ACCOUNT_DATA_TIMES = 0x0120, - SMSG_ACCOUNT_INFO_RESPONSE = 0xBADD, + SMSG_ACCOUNT_INFO_RESPONSE = 0x03AB, SMSG_ACCOUNT_MOUNT_UPDATE = 0x0140, + SMSG_ACCOUNT_PROFILE = 0x1B1A, SMSG_ACCOUNT_RESTRICTED_WARNING = 0xBADD, SMSG_ACCOUNT_TOYS_UPDATE = 0x0590, SMSG_ACHIEVEMENT_DELETED = 0x050E, @@ -842,7 +848,11 @@ enum OpcodeServer : uint32 SMSG_ALL_ACHIEVEMENT_DATA = 0x0030, SMSG_ALL_GUILD_ACHIEVEMENTS = 0x1866, SMSG_ARCHAEOLOGY_SURVERY_CAST = 0x0008, + SMSG_AREA_SHARE_INFO_RESPONSE = 0x0005, + SMSG_AREA_SHARE_MAPPINGS_RESPONSE = 0x13E3, SMSG_AREA_SPIRIT_HEALER_TIME = 0x1182, + SMSG_AREA_TRIGGER_DEBUG_PLAYER_INSIDE = 0x0789, + SMSG_AREA_TRIGGER_DEBUG_SWEEP = 0x11EB, SMSG_AREA_TRIGGER_DENIED = 0x018A, SMSG_AREA_TRIGGER_MESSAGE = 0xBADD, SMSG_AREA_TRIGGER_MOVEMENT_UPDATE = 0xBADD, @@ -883,7 +893,7 @@ enum OpcodeServer : uint32 SMSG_AUTH_CHALLENGE = 0x1759, SMSG_AUTH_RESPONSE = 0x0DA9, SMSG_AVAILABLE_VOICE_CHANNEL = 0x04D4, - SMSG_AVERAGE_ITEM_LEVEL_INFORM = 0xBADD, + SMSG_AVERAGE_ITEM_LEVEL_INFORM = 0x1332, SMSG_BARBER_SHOP_RESULT = 0x150D, SMSG_BATTLEFIELD_LIST = 0x11A1, SMSG_BATTLEFIELD_MGR_DROP_TIMER_CANCELED = 0x179A, @@ -1021,7 +1031,7 @@ enum OpcodeServer : uint32 SMSG_CHEAT_IGNORE_DIMISHING_RETURNS = 0x194C, SMSG_CHECK_FOR_BOTS = 0xBADD, SMSG_CHECK_WARGAME_ENTRY = 0x1203, - SMSG_CLEAR_ALL_SPELL_CHARGE = 0x088B, + SMSG_CLEAR_ALL_SPELL_CHARGES = 0x088B, SMSG_CLEAR_BOSS_EMOTES = 0x118B, SMSG_CLEAR_COOLDOWN = 0x0226, SMSG_CLEAR_COOLDOWNS = 0x0BFA, @@ -1044,7 +1054,7 @@ enum OpcodeServer : uint32 SMSG_COMMENTATOR_SKIRMISH_QUEUE_RESULT2 = 0xBADD, SMSG_COMMENTATOR_STATE_CHANGED = 0x133A, SMSG_COMPLAINT_RESULT = 0x0B15, - SMSG_COMPLETE_SHIPMENT_RESPONSE = 0x0F3A, + SMSG_COMPLETE_SHIPMENT_RESPONSE = 0x0739, SMSG_COMPRESSED_MOVES = 0xBADD, SMSG_COMPRESSED_PACKET = 0x07CA, SMSG_COMSAT_CONNECT_FAIL = 0xBADD, @@ -1073,8 +1083,11 @@ enum OpcodeServer : uint32 SMSG_DAMAGE_DONE_OBSOLETE = 0xBADD, SMSG_DANCE_QUERY_RESPONSE = 0xBADD, SMSG_DANCE_STUDIO_CREATE_RESULT = 0x178D, + SMSG_DB_LOOKUP_RESULTS = 0x053A, SMSG_DB_REPLY = 0x1939, SMSG_DEATH_RELEASE_LOC = 0x098C, + SMSG_DEBUG_AISTATE = 0x091A, + SMSG_DEBUG_DRAW_AURA = 0x1919, SMSG_DEBUG_SERVER_GEO = 0xBADD, SMSG_DEFENSE_MESSAGE = 0x1442, SMSG_DESTROY_ARENA_UNIT = 0x19E1, @@ -1097,8 +1110,9 @@ enum OpcodeServer : uint32 SMSG_DUEL_OUT_OF_BOUNDS = 0x0111, SMSG_DUEL_REQUESTED = 0x0827, SMSG_DUEL_WINNER = 0x0935, - SMSG_DUMP_RIDE_TICKETS_RESPONSE = 0xBADD, + SMSG_DUMP_RIDE_TICKETS_RESPONSE = 0x01E1, SMSG_DURABILITY_DAMAGE_DEATH = 0x01EC, + SMSG_DYNAMIC_DROP_ROLL_RESULT = 0x0189, SMSG_ECHO_PARTY_SQUELCH = 0xBADD, SMSG_EMOTE = 0x071D, SMSG_ENABLE_BARBER_SHOP = 0x13A2, @@ -1121,6 +1135,7 @@ enum OpcodeServer : uint32 SMSG_FLOOD_DETECTED = 0xBADD, SMSG_FORCEACTIONSHOW = 0xBADD, SMSG_FORCED_DEATH_UPDATE = 0x09EB, + SMSG_FORCE_ACTION_SHOW_RESPONSE = 0x173A, SMSG_FORCE_ANIM = 0x0028, SMSG_FORCE_DISPLAY_UPDATE = 0xBADD, SMSG_FORCE_OBJECT_RELINK = 0x059D, @@ -1134,7 +1149,7 @@ enum OpcodeServer : uint32 SMSG_GAMEOBJECT_PLAY_SPELL_VISUAL_KIT = 0x01DB, SMSG_GAMEOBJECT_QUERY_RESPONSE = 0x128A, SMSG_GAMEOBJECT_RESET_STATE = 0x090E, - SMSG_GAME_EVENT_DEBUG_LOG = 0xBADD, + SMSG_GAME_EVENT_DEBUG_LOG = 0x0121, SMSG_GAME_SPEED_SET = 0x0D40, SMSG_GAME_TIME_SET = 0x0B40, SMSG_GAME_TIME_UPDATE = 0x1709, @@ -1143,16 +1158,25 @@ enum OpcodeServer : uint32 SMSG_GARRISON_ASSIGN_FOLLOWER_TO_BUILDING_RESULT = 0x0B7C, SMSG_GARRISON_BUILDING_ACTIVATED = 0x1974, SMSG_GARRISON_BUILDING_REMOVED = 0x1151, + SMSG_GARRISON_BUILDING_SET_ACTIVE_SPECIALIZATION_RESULT = 0x0282, SMSG_GARRISON_COMPLETE_MISSION_RESULT = 0x0D54, + SMSG_GARRISON_DELETE_RESULT = 0x19C1, SMSG_GARRISON_FOLLOWER_CHANGED_XP = 0x1B64, SMSG_GARRISON_LANDINGPAGE_SHIPMENTS = 0x1901, SMSG_GARRISON_LEARN_BLUEPRINT_RESULT = 0x000B, + SMSG_GARRISON_LEARN_SPECIALIZATION_RESULT = 0x01D1, + SMSG_GARRISON_LIST_FOLLOWERS_CHEAT_RESULT = 0x1942, SMSG_GARRISON_MISSION_BONUS_ROLL_RESULT = 0x0952, SMSG_GARRISON_MONUMENT_SELECTED_TROPHY_ID_LOADED = 0x09EA, + SMSG_GARRISON_OPEN_ARCHITECT = 0x0004, + SMSG_GARRISON_OPEN_MISSION_NPC = 0x0542, SMSG_GARRISON_OPEN_TRADESKILL_NPC_RESPONSE = 0x09C1, SMSG_GARRISON_PLACE_BUILDING_RESULT = 0x0082, + SMSG_GARRISON_PLOT_PLACED = 0x0583, SMSG_GARRISON_PLOT_REMOVED = 0x0513, SMSG_GARRISON_REMOTE_INFO = 0x0151, + SMSG_GARRISON_REMOVE_FOLLOWER_FROM_BUILDING_RESULT = 0x0089, + SMSG_GARRISON_REMOVE_FOLLOWER_RESULT = 0x196A, SMSG_GARRISON_REQUEST_BLUEPRINT_AND_SPECIALIZATION_DATA_RESULT = 0x0964, SMSG_GARRISON_SET_NUM_FOLLOWER_ACTIVATIONS_REMAINING = 0x0D92, SMSG_GARRISON_START_MISSION_RESULT = 0x0D01, @@ -1165,9 +1189,15 @@ enum OpcodeServer : uint32 SMSG_GET_GARRISON_INFO_RESULT = 0x0521, SMSG_GET_SHIPMENT_INFO_RESPONSE = 0x1BAB, SMSG_GET_TROPHY_LIST_RESPONSE = 0x0126, + SMSG_GHOST = 0x0BC2, + SMSG_GHOSTEE_GONE = 0x0922, + SMSG_GM_ACCOUNT_ONLINE_RESPONSE = 0x1D9E, + SMSG_GM_CHANGE_ARENA_RATING = 0x1139, + SMSG_GM_CHARACTER_RESTORE_RESPONSE = 0x11E2, SMSG_GM_MESSAGECHAT = 0xBADD, SMSG_GM_PLAYER_INFO = 0x0118, SMSG_GM_REQUEST_PLAYER_INFO = 0x1381, + SMSG_GM_SUMMON = 0x1223, SMSG_GM_TICKET_CASE_STATUS = 0x1D8D, SMSG_GM_TICKET_GET_TICKET_RESPONSE = 0x0389, SMSG_GM_TICKET_RESOLVE_RESPONSE = 0x091F, @@ -1249,6 +1279,7 @@ enum OpcodeServer : uint32 SMSG_HOTFIX_NOTIFY_BLOB = 0x1D1D, SMSG_IGNORE_DIMINISHING_RETURNS_CHEAT = 0xBADD, SMSG_IGNORE_REQUIREMENTS_CHEAT = 0xBADD, + SMSG_IMMIGRANT_HOST_SEARCH_LOG = 0x111A, SMSG_INCREASE_CAST_TIME_FOR_SPELL = 0x1179, SMSG_INITIALIZE_FACTIONS = 0x0B10, SMSG_INITIAL_SETUP = 0x0B07, @@ -1326,6 +1357,7 @@ enum OpcodeServer : uint32 SMSG_LF_GUILD_POST = 0x1817, SMSG_LF_GUILD_RECRUITS = 0x1008, SMSG_LIST_INVENTORY = 0x0940, + SMSG_LIST_TARGETS = 0x058F, SMSG_LIVE_REGION_ACCOUNT_RESTORE_RESULT = 0x113A, SMSG_LIVE_REGION_CHARACTER_COPY_RESULT = 0x082E, SMSG_LIVE_REGION_GET_ACCOUNT_CHARACTER_LIST_RESULT = 0x11E1, @@ -1352,6 +1384,7 @@ enum OpcodeServer : uint32 SMSG_LOOT_ROLLS_COMPLETE = 0x0D2F, SMSG_LOOT_ROLL_WON = 0x071E, SMSG_LOOT_SLOT_CHANGED = 0xBADD, + SMSG_LOOT_UPDATED = 0x0301, SMSG_LOSS_OF_CONTROL_AURA_UPDATE = 0x0305, SMSG_MAIL_LIST_RESULT = 0x0B3F, SMSG_MAIL_QUERY_NEXT_TIME_RESULT = 0x153D, @@ -1375,6 +1408,8 @@ enum OpcodeServer : uint32 SMSG_MOTD = 0x0442, SMSG_MOUNT_RESULT = 0x000F, SMSG_MOVE_APPLY_MOVEMENT_FORCE = 0x06CB, + SMSG_MOVE_CHARACTER_CHEAT_FAILURE = 0x00F1, + SMSG_MOVE_CHARACTER_CHEAT_SUCCESS = 0x11AA, SMSG_MOVE_DISABLE_CAN_TRANSITION_BETWEEN_SWIM_AND_FLY = 0x00C5, SMSG_MOVE_DISABLE_COLLISION = 0x135A, SMSG_MOVE_DISABLE_GRAVITY = 0x02C6, @@ -1479,6 +1514,7 @@ enum OpcodeServer : uint32 SMSG_OPEN_CONTAINER = 0x05AA, SMSG_OPEN_LFG_DUNGEON_FINDER = 0x0F17, SMSG_OPEN_SHIPMENT_NPC_FROM_GOSSIP = 0x0709, + SMSG_OPEN_SHIPMENT_NPC_RESULT = 0x1B81, SMSG_OVERRIDE_LIGHT = 0x01EA, SMSG_PAGE_TEXT = 0x158D, SMSG_PAGE_TEXT_QUERY_RESPONSE = 0x05A0, @@ -1555,6 +1591,7 @@ enum OpcodeServer : uint32 SMSG_POWER_UPDATE = 0x0B27, SMSG_PRE_RESSURECT = 0x0D2E, SMSG_PROC_RESIST = 0x1DAD, + SMSG_PROFILE_DATA_RESPONSE = 0x1902, SMSG_PROPOSE_LEVEL_GRANT = 0x1B8C, SMSG_PUREMOUNT_CANCELLED_OBSOLETE = 0xBADD, SMSG_PVP_CREDIT = 0x13A9, @@ -1563,6 +1600,8 @@ enum OpcodeServer : uint32 SMSG_PVP_SEASON = 0x09E3, SMSG_QUERY_BATTLE_PET_NAME_RESPONSE = 0x09EC, SMSG_QUERY_ITEM_TEXT_RESPONSE = 0x011A, + SMSG_QUERY_OBJ_POSITION = 0x0529, + SMSG_QUERY_OBJ_ROTATION = 0x171A, SMSG_QUERY_PETITION_RESPONSE = 0x13AC, SMSG_QUERY_TIME_RESPONSE = 0x1DB0, SMSG_QUESTGIVER_INVALID_QUEST = 0x15E1, @@ -1621,6 +1660,7 @@ enum OpcodeServer : uint32 SMSG_REQUEST_PVP_REWARDS_RESPONSE = 0x1DAE, SMSG_RESEARCH_COMPLETE = 0x0B3A, SMSG_RESEARCH_SETUP_HISTORY = 0x0A25, + SMSG_RESET_AREA_TRIGGER = 0x1B54, SMSG_RESET_COMPRESSION_CONTEXT = 0xBADD, SMSG_RESET_FAILED_NOTIFY = 0x1B01, SMSG_RESET_RANGED_COMBAT_TIMER = 0x0106, @@ -1655,6 +1695,7 @@ enum OpcodeServer : uint32 SMSG_SCENE_OBJECT_PET_BATTLE_REPLACEMENTS_MADE = 0x098A, SMSG_SCENE_OBJECT_PET_BATTLE_ROUND_RESULT = 0x0130, SMSG_SCRIPT_CAST = 0x1B1C, + SMSG_SCRIPT_MESSAGE = 0x1119, SMSG_SELL_ITEM = 0xBADD, SMSG_SELL_RESPONSE = 0x1352, SMSG_SEND_ITEM_PASSIVES = 0x059F, @@ -1664,11 +1705,14 @@ enum OpcodeServer : uint32 SMSG_SEND_SPELL_CHARGES = 0x1A82, SMSG_SEND_SPELL_HISTORY = 0x1933, SMSG_SEND_UNLEARN_SPELLS = 0x0BCB, + SMSG_SERVER_BUCK_DATA = 0x1EC9, + SMSG_SERVER_BUCK_DATA_START = 0x11E4, SMSG_SERVER_FIRST_ACHIEVEMENT = 0x1413, SMSG_SERVER_FIRST_ACHIEVEMENTS = 0x0BAC, - SMSG_SERVER_INFO_RESPONSE = 0xBADD, + SMSG_SERVER_INFO_QUERY_RESPONSE = 0x11A4, + SMSG_SERVER_INFO_RESPONSE = 0x018B, SMSG_SERVER_MESSAGE = 0x0683, - SMSG_SERVER_PERF = 0xBADD, + SMSG_SERVER_PERF = 0x0319, SMSG_SERVER_TIME = 0x0339, SMSG_SETUP_CURRENCY = 0x0B06, SMSG_SETUP_TROPHY = 0x0B63, @@ -1696,8 +1740,10 @@ enum OpcodeServer : uint32 SMSG_SET_PLAY_HOVER_ANIM = 0x02D4, SMSG_SET_PROFICIENCY = 0x00D3, SMSG_SET_PROJECTILE_POSITION = 0xBADD, + SMSG_SET_PROMOTION_RESPONSE = 0x0A07, SMSG_SET_QUEST_COMPLETED_BIT = 0x15D3, SMSG_SET_RAID_DIFFICULTY = 0x051F, + SMSG_SET_SERVER_WOW_TIME = 0x0312, SMSG_SET_SPELL_CHARGES = 0x18AB, SMSG_SET_TASK_COMPLETE = 0x0B24, SMSG_SET_TIME_ZONE_INFORMATION = 0x153E, @@ -1706,9 +1752,10 @@ enum OpcodeServer : uint32 SMSG_SHOW_BANK = 0x1B51, SMSG_SHOW_MAILBOX = 0x152F, SMSG_SHOW_NEUTRAL_PLAYER_FACTION_SELECT_UI = 0x053D, - SMSG_SHOW_RATINGS = 0xBADD, + SMSG_SHOW_RATINGS = 0x0F9A, SMSG_SHOW_TAXI_NODES = 0x12A1, SMSG_SHOW_TRADE_SKILL_RESPONSE = 0x15C0, + SMSG_SHOW_ZONES_CHEAT_RESULT = 0x1123, SMSG_SOCKET_GEMS = 0x1302, SMSG_SORT_BAGS_ACK = 0x09E4, SMSG_SOR_START_EXPERIENCE_INCOMPLETE = 0x198B, @@ -1798,7 +1845,7 @@ enum OpcodeServer : uint32 SMSG_UPDATE_INSTANCE_OWNERSHIP = 0x093E, SMSG_UPDATE_LAST_INSTANCE = 0x13A4, SMSG_UPDATE_OBJECT = 0x122C, - SMSG_UPDATE_SERVER_PLAYER_POSITION = 0xBADD, + SMSG_UPDATE_SERVER_PLAYER_POSITION = 0x01AC, SMSG_UPDATE_TASK_PROGRESS = 0x1209, SMSG_UPDATE_WORLD_STATE = 0x03EC, SMSG_USERLIST_ADD = 0x0441, diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h index 42d2ec72c8e..112893fbc75 100644 --- a/src/server/game/Spells/Auras/SpellAuraDefines.h +++ b/src/server/game/Spells/Auras/SpellAuraDefines.h @@ -468,7 +468,7 @@ enum AuraType SPELL_AURA_408 = 408, SPELL_AURA_409 = 409, SPELL_AURA_410 = 410, - SPELL_AURA_MOD_CHARGES = 411, // NYI + SPELL_AURA_MOD_MAX_CHARGES = 411, SPELL_AURA_412 = 412, SPELL_AURA_413 = 413, SPELL_AURA_414 = 414, @@ -508,19 +508,19 @@ enum AuraType SPELL_AURA_448 = 448, SPELL_AURA_449 = 449, SPELL_AURA_450 = 450, - SPELL_AURA_451 = 451, + SPELL_AURA_OVERRIDE_PET_SPECS = 451, // NYI SPELL_AURA_452 = 452, - SPELL_AURA_453 = 453, - SPELL_AURA_454 = 454, + SPELL_AURA_CHARGE_RECOVERY_MOD = 453, + SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER = 454, SPELL_AURA_455 = 455, - SPELL_AURA_456 = 456, - SPELL_AURA_457 = 457, - SPELL_AURA_458 = 458, + SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE = 456, + SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE_REGEN = 457, + SPELL_AURA_IGNORE_DUAL_WIELD_HIT_PENALTY = 458, // NYI SPELL_AURA_459 = 459, SPELL_AURA_460 = 460, SPELL_AURA_461 = 461, SPELL_AURA_462 = 462, - SPELL_AURA_463 = 463, + SPELL_AURA_CONVER_CRIT_RATING_PCT_TO_PARRY_RATING = 463, // NYI SPELL_AURA_464 = 464, SPELL_AURA_465 = 465, SPELL_AURA_466 = 466, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index b4c27f0214b..6ce9fec1ed4 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -42,6 +42,7 @@ #include "Pet.h" #include "ReputationMgr.h" #include "MiscPackets.h" +#include "SpellHistory.h" class Aura; // @@ -470,7 +471,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNULL, //408 &AuraEffect::HandleNULL, //409 &AuraEffect::HandleNULL, //410 - &AuraEffect::HandleNULL, //411 SPELL_AURA_MOD_CHARGES + &AuraEffect::HandleNoImmediateEffect, //411 SPELL_AURA_MOD_MAX_CHARGES implemented in SpellHistory::GetMaxCharges &AuraEffect::HandleNULL, //412 &AuraEffect::HandleNULL, //413 &AuraEffect::HandleNULL, //414 @@ -510,19 +511,19 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNULL, //448 &AuraEffect::HandleNULL, //449 &AuraEffect::HandleNULL, //450 - &AuraEffect::HandleNULL, //451 + &AuraEffect::HandleNULL, //451 SPELL_AURA_OVERRIDE_PET_SPECS &AuraEffect::HandleNULL, //452 - &AuraEffect::HandleNULL, //453 - &AuraEffect::HandleNULL, //454 + &AuraEffect::HandleNoImmediateEffect, //453 SPELL_AURA_CHARGE_RECOVERY_MOD implemented in SpellHistory::GetChargeRecoveryTime + &AuraEffect::HandleNoImmediateEffect, //454 SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER implemented in SpellHistory::GetChargeRecoveryTime &AuraEffect::HandleNULL, //455 - &AuraEffect::HandleNULL, //456 - &AuraEffect::HandleNULL, //457 - &AuraEffect::HandleNULL, //458 + &AuraEffect::HandleNoImmediateEffect, //456 SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE implemented in SpellHistory::GetChargeRecoveryTime + &AuraEffect::HandleNoImmediateEffect, //457 SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE_REGEN implemented in SpellHistory::GetChargeRecoveryTime + &AuraEffect::HandleNULL, //458 SPELL_AURA_IGNORE_DUAL_WIELD_HIT_PENALTY &AuraEffect::HandleNULL, //459 &AuraEffect::HandleNULL, //460 &AuraEffect::HandleNULL, //461 &AuraEffect::HandleNULL, //462 - &AuraEffect::HandleNULL, //463 + &AuraEffect::HandleNULL, //463 SPELL_AURA_CRIT_RATING_AFFECTS_PARRY used by Riposte &AuraEffect::HandleNULL, //464 &AuraEffect::HandleNULL, //465 &AuraEffect::HandleNULL, //466 @@ -665,12 +666,18 @@ int32 AuraEffect::CalculateAmount(Unit* caster) m_canBeRecalculated = false; break; case SPELL_AURA_MOUNTED: - if (MountCapabilityEntry const* mountCapability = GetBase()->GetUnitOwner()->GetMountCapability(uint32(GetMiscValueB()))) + { + uint32 mountType = uint32(GetMiscValueB()); + if (MountEntry const* mountEntry = sDB2Manager.GetMount(GetId())) + mountType = mountEntry->MountTypeId; + + if (MountCapabilityEntry const* mountCapability = GetBase()->GetUnitOwner()->GetMountCapability(mountType)) { amount = mountCapability->ID; m_canBeRecalculated = false; } break; + } case SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE: { if (caster) @@ -1321,15 +1328,13 @@ void AuraEffect::HandleShapeshiftBoosts(Unit* target, bool apply) const // Remove cooldown of spells triggered on stance change - they may share cooldown with stance spell if (spellId) { - if (target->GetTypeId() == TYPEID_PLAYER) - target->ToPlayer()->RemoveSpellCooldown(spellId); + target->GetSpellHistory()->ResetCooldown(spellId); target->CastSpell(target, spellId, true, NULL, this); } if (spellId2) { - if (target->GetTypeId() == TYPEID_PLAYER) - target->ToPlayer()->RemoveSpellCooldown(spellId2); + target->GetSpellHistory()->ResetCooldown(spellId2); target->CastSpell(target, spellId2, true, NULL, this); } @@ -2661,22 +2666,24 @@ void AuraEffect::HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bo uint32 displayId = 0; uint32 vehicleId = 0; - // Festive Holiday Mount - if (target->HasAura(62061)) + if (MountEntry const* mountEntry = sDB2Manager.GetMount(GetId())) { - if (GetBase()->HasEffectType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) - creatureEntry = 24906; - else - creatureEntry = 15665; + displayId = mountEntry->DisplayId; + // TODO: CREATE TABLE mount_vehicle (mountId, vehicleCreatureId) for future mounts that are vehicles (new mounts no longer have proper data in MiscValue) + //if (MountVehicle const* mountVehicle = sObjectMgr->GetMountVehicle(mountEntry->Id)) + // creatureEntry = mountVehicle->VehicleCreatureId; } if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry)) { - displayId = ObjectMgr::ChooseDisplayId(creatureInfo); - sObjectMgr->GetCreatureModelRandomGender(&displayId); - vehicleId = creatureInfo->VehicleId; + if (!displayId || vehicleId) + { + displayId = ObjectMgr::ChooseDisplayId(creatureInfo); + sObjectMgr->GetCreatureModelRandomGender(&displayId); + } + //some spell has one aura of mount and one of vehicle for (SpellEffectInfo const* effect : GetBase()->GetSpellEffectInfos()) if (effect && effect->Effect == SPELL_EFFECT_SUMMON && effect->MiscValue == GetMiscValue()) @@ -5021,29 +5028,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool target->PlayDirectSound(14972, target->ToPlayer()); } break; - case 62061: // Festive Holiday Mount - if (target->HasAuraType(SPELL_AURA_MOUNTED)) - { - uint32 creatureEntry = 0; - if (apply) - { - if (target->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) - creatureEntry = 24906; - else - creatureEntry = 15665; - } - else - creatureEntry = target->GetAuraEffectsByType(SPELL_AURA_MOUNTED).front()->GetMiscValue(); - - if (CreatureTemplate const* creatureInfo = sObjectMgr->GetCreatureTemplate(creatureEntry)) - { - uint32 displayID = ObjectMgr::ChooseDisplayId(creatureInfo); - sObjectMgr->GetCreatureModelRandomGender(&displayID); - - target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, displayID); - } - } - break; } break; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 4e239f8a373..cd3b22dfa11 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -26,6 +26,7 @@ #include "Unit.h" #include "Spell.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "SpellPackets.h" #include "DynamicObject.h" #include "ObjectAccessor.h" @@ -117,7 +118,7 @@ void AuraApplication::_Remove() void AuraApplication::_InitFlags(Unit* caster, uint32 effMask) { // mark as selfcast if needed - _flags |= (GetBase()->GetCasterGUID() == GetTarget()->GetGUID()) ? AFLAG_NONE : AFLAG_NOCASTER; + _flags |= (GetBase()->GetCasterGUID() == GetTarget()->GetGUID()) ? AFLAG_NOCASTER : AFLAG_NONE; // aura is cast by self or an enemy // one negative effect and we know aura is negative @@ -150,7 +151,10 @@ void AuraApplication::_InitFlags(Unit* caster, uint32 effMask) _flags |= positiveFound ? AFLAG_POSITIVE : AFLAG_NEGATIVE; } - if (GetBase()->GetSpellInfo()->AttributesEx8 & SPELL_ATTR8_AURA_SEND_AMOUNT) + if (GetBase()->GetSpellInfo()->AttributesEx8 & SPELL_ATTR8_AURA_SEND_AMOUNT || + GetBase()->HasEffectType(SPELL_AURA_MOD_MAX_CHARGES) || + GetBase()->HasEffectType(SPELL_AURA_CHARGE_RECOVERY_MOD) || + GetBase()->HasEffectType(SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER)) _flags |= AFLAG_SCALABLE; } @@ -182,6 +186,7 @@ void AuraApplication::_HandleEffect(uint8 effIndex, bool apply) // Remove all triggered by aura spells vs unlimited duration aurEff->CleanupTriggeredSpells(GetTarget()); } + SetNeedClientUpdate(); } @@ -218,10 +223,10 @@ void AuraApplication::BuildUpdatePacket(WorldPackets::Spells::AuraInfo& auraInfo if (auraData.Flags & AFLAG_SCALABLE) { - auraData.Points.reserve(aura->GetAuraEffects().size()); + auraData.Points.resize(aura->GetAuraEffects().size(), 0.0f); for (AuraEffect const* effect : GetBase()->GetAuraEffects()) if (effect && HasEffect(effect->GetEffIndex())) // Not all of aura's effects have to be applied on every target - auraData.Points.push_back(float(effect->GetAmount())); + auraData.Points[effect->GetEffIndex()] = float(effect->GetAmount()); } auraInfo.AuraData.Set(auraData); @@ -462,7 +467,7 @@ void Aura::_ApplyForTarget(Unit* target, Unit* caster, AuraApplication * auraApp if (m_spellInfo->IsCooldownStartedOnEvent()) { Item* castItem = !m_castItemGuid.IsEmpty() ? caster->ToPlayer()->GetItemByGuid(m_castItemGuid) : NULL; - caster->ToPlayer()->AddSpellAndCategoryCooldowns(m_spellInfo, castItem ? castItem->GetEntry() : 0, NULL, true); + caster->GetSpellHistory()->StartCooldown(m_spellInfo, castItem ? castItem->GetEntry() : 0, nullptr, true); } } } @@ -490,12 +495,9 @@ void Aura::_UnapplyForTarget(Unit* target, Unit* caster, AuraApplication * auraA m_removedApplications.push_back(auraApp); // reset cooldown state for spells - if (caster && caster->GetTypeId() == TYPEID_PLAYER) - { - if (GetSpellInfo()->IsCooldownStartedOnEvent()) - // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) - caster->ToPlayer()->SendCooldownEvent(GetSpellInfo()); - } + if (caster && GetSpellInfo()->IsCooldownStartedOnEvent()) + // note: item based cooldowns and cooldown spell mods with charges ignored (unknown existed cases) + caster->GetSpellHistory()->SendCooldownEvent(GetSpellInfo()); } // removes aura from all targets @@ -1055,7 +1057,12 @@ bool Aura::CanBeSaved() const bool Aura::CanBeSentToClient() const { - return !IsPassive() || GetSpellInfo()->HasAreaAuraEffect(GetOwner() ? GetOwner()->GetMap()->GetDifficultyID() : DIFFICULTY_NONE) || HasEffectType(SPELL_AURA_ABILITY_IGNORE_AURASTATE) || HasEffectType(SPELL_AURA_CAST_WHILE_WALKING); + return !IsPassive() || GetSpellInfo()->HasAreaAuraEffect(GetOwner() ? GetOwner()->GetMap()->GetDifficultyID() : DIFFICULTY_NONE) + || HasEffectType(SPELL_AURA_ABILITY_IGNORE_AURASTATE) + || HasEffectType(SPELL_AURA_CAST_WHILE_WALKING) + || HasEffectType(SPELL_AURA_MOD_MAX_CHARGES) + || HasEffectType(SPELL_AURA_CHARGE_RECOVERY_MOD) + || HasEffectType(SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER); } bool Aura::IsSingleTargetWith(Aura const* aura) const @@ -1310,7 +1317,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b break; case 60970: // Heroic Fury (remove Intercept cooldown) if (target->GetTypeId() == TYPEID_PLAYER) - target->ToPlayer()->RemoveSpellCooldown(20252, true); + target->GetSpellHistory()->ResetCooldown(20252, true); break; } break; @@ -1470,15 +1477,15 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b // check cooldown if (caster->GetTypeId() == TYPEID_PLAYER) { - if (caster->ToPlayer()->HasSpellCooldown(aura->GetId())) + if (caster->GetSpellHistory()->HasCooldown(aura->GetId())) { // 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->ToPlayer()->GetSpellCooldownDelay(aura->GetId()) <= 11) + if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetId()) <= 11) break; } else // and add if needed - caster->ToPlayer()->AddSpellCooldown(aura->GetId(), 0, uint32(time(NULL) + 12)); + caster->GetSpellHistory()->AddCooldown(aura->GetId(), 0, std::chrono::seconds(12)); } // effect on caster diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 3a05da9cd1e..8710f312dda 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -79,7 +79,7 @@ class AuraApplication uint32 GetEffectMask() const { return _effectMask; } bool HasEffect(uint8 effect) const { ASSERT(effect < MAX_SPELL_EFFECTS); return (_effectMask & (1 << effect)) != 0; } bool IsPositive() const { return (_flags & AFLAG_POSITIVE) != 0; } - bool IsSelfcast() const { return (_flags & AFLAG_NOCASTER) == 0; } + bool IsSelfcast() const { return (_flags & AFLAG_NOCASTER) != 0; } uint8 GetEffectsToApply() const { return _effectsToApply; } void SetRemoveMode(AuraRemoveMode mode) { _removeMode = mode; } diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 760ae42bbfd..ed7ba3b788c 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -58,6 +58,7 @@ #include "Battlefield.h" #include "BattlefieldMgr.h" #include "SpellPackets.h" +#include "SpellHistory.h" extern pEffect SpellEffects[TOTAL_SPELL_EFFECTS]; @@ -3318,7 +3319,7 @@ void Spell::cast(bool skipCheck) //Clear spell cooldowns after every spell is cast if .cheat cooldown is enabled. if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN)) - m_caster->ToPlayer()->RemoveSpellCooldown(m_spellInfo->Id, true); + m_caster->GetSpellHistory()->ResetCooldown(m_spellInfo->Id, true); } SetExecutedCurrently(false); @@ -3521,30 +3522,7 @@ void Spell::_handle_finish_phase() void Spell::SendSpellCooldown() { - Player* _player = m_caster->ToPlayer(); - if (!_player) - { - // Handle pet cooldowns here if needed instead of in PetAI to avoid hidden cooldown restarts - Creature* _creature = m_caster->ToCreature(); - if (_creature && (_creature->IsPet() || _creature->IsGuardian())) - _creature->AddCreatureSpellCooldown(m_spellInfo->Id); - - return; - } - - // mana/health/etc potions, disabled by client (until combat out as declarate) - if (m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) - { - // need in some way provided data for Spell::finish SendCooldownEvent - _player->SetLastPotionId(m_CastItem->GetEntry()); - return; - } - - // have infinity cooldown but set at aura apply // do not set cooldown for triggered spells (needed by reincarnation) - if (m_spellInfo->IsCooldownStartedOnEvent() || m_spellInfo->IsPassive() || (_triggeredCastFlags & TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD)) - return; - - _player->AddSpellAndCategoryCooldowns(m_spellInfo, m_CastItem ? m_CastItem->GetEntry() : 0, this); + m_caster->GetSpellHistory()->HandleCooldowns(m_spellInfo, m_CastItem, this); } void Spell::update(uint32 difftime) @@ -4420,12 +4398,12 @@ void Spell::TakeCastItem() bool expendable = false; bool withoutCharges = false; - for (uint8 i = 0; i < proto->Effects.size(); ++i) + for (uint8 i = 0; i < proto->Effects.size() && i < 5; ++i) { // item has limited charges - if (proto->Effects[i].Charges) + if (proto->Effects[i]->Charges) { - if (proto->Effects[i].Charges < 0) + if (proto->Effects[i]->Charges < 0) expendable = true; int32 charges = m_CastItem->GetSpellCharges(i); @@ -4662,11 +4640,11 @@ void Spell::TakeReagents() // if CastItem is also spell reagent if (castItemTemplate && castItemTemplate->GetId() == itemid) { - for (uint8 s = 0; s < castItemTemplate->Effects.size(); ++s) + for (uint8 s = 0; s < castItemTemplate->Effects.size() && s < 5; ++s) { // CastItem will be used up and does not count as reagent int32 charges = m_CastItem->GetSpellCharges(s); - if (castItemTemplate->Effects[s].Charges < 0 && abs(charges) < 2) + if (castItemTemplate->Effects[s]->Charges < 0 && abs(charges) < 2) { ++itemcount; break; @@ -4809,23 +4787,26 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_CASTER_DEAD; // check cooldowns to prevent cheating - if (m_caster->GetTypeId() == TYPEID_PLAYER && !m_spellInfo->HasAttribute(SPELL_ATTR0_PASSIVE)) + if (!m_spellInfo->IsPassive()) { - //can cast triggered (by aura only?) spells while have this flag - if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE) && m_caster->ToPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY)) - return SPELL_FAILED_SPELL_IN_PROGRESS; + if (m_caster->GetTypeId() == TYPEID_PLAYER) + { + //can cast triggered (by aura only?) spells while have this flag + if (!(_triggeredCastFlags & TRIGGERED_IGNORE_CASTER_AURASTATE) && m_caster->ToPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY)) + return SPELL_FAILED_SPELL_IN_PROGRESS; + + // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat + if (m_caster->ToPlayer()->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) + return SPELL_FAILED_NOT_READY; + } - if (m_caster->ToPlayer()->HasSpellCooldown(m_spellInfo->Id)) + if (!m_caster->GetSpellHistory()->IsReady(m_spellInfo)) { if (m_triggeredByAuraSpell) return SPELL_FAILED_DONT_REPORT; else return SPELL_FAILED_NOT_READY; } - - // check if we are using a potion in combat for the 2nd+ time. Cooldown is added only after caster gets out of combat - if (m_caster->ToPlayer()->GetLastPotionId() && m_CastItem && (m_CastItem->IsPotion() || m_spellInfo->IsCooldownStartedOnEvent())) - return SPELL_FAILED_NOT_READY; } if (m_spellInfo->HasAttribute(SPELL_ATTR7_IS_CHEAT_SPELL) && !m_caster->HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_ALLOW_CHEAT_SPELLS)) @@ -5510,7 +5491,7 @@ SpellCastResult Spell::CheckCast(bool strict) TalentEntry const* talent = sTalentStore.LookupEntry(m_misc.TalentId); if (!talent) return SPELL_FAILED_DONT_REPORT; - if (m_caster->ToPlayer()->HasSpellCooldown(talent->SpellID)) + if (m_caster->GetSpellHistory()->HasCooldown(talent->SpellID)) return SPELL_FAILED_CANT_UNTALENT; break; } @@ -5695,13 +5676,13 @@ SpellCastResult Spell::CheckPetCast(Unit* target) } // cooldown - if (Creature const* creatureCaster = m_caster->ToCreature()) - if (creatureCaster->HasSpellCooldown(m_spellInfo->Id)) + if (Creature* creatureCaster = m_caster->ToCreature()) + if (!creatureCaster->GetSpellHistory()->IsReady(m_spellInfo)) return SPELL_FAILED_NOT_READY; // Check if spell is affected by GCD if (m_spellInfo->StartRecoveryCategory > 0) - if (m_caster->GetCharmInfo() && m_caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo)) + if (m_caster->GetCharmInfo() && m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo)) return SPELL_FAILED_NOT_READY; return CheckCast(true); @@ -6033,8 +6014,8 @@ SpellCastResult Spell::CheckItems() if (!proto) return SPELL_FAILED_ITEM_NOT_READY; - for (uint8 i = 0; i < proto->Effects.size(); ++i) - if (proto->Effects[i].Charges) + for (uint8 i = 0; i < proto->Effects.size() && i < 5; ++i) + if (proto->Effects[i]->Charges) if (m_CastItem->GetSpellCharges(i) == 0) return SPELL_FAILED_NO_CHARGES_REMAIN; @@ -6134,11 +6115,11 @@ SpellCastResult Spell::CheckItems() ItemTemplate const* proto = m_CastItem->GetTemplate(); if (!proto) return SPELL_FAILED_ITEM_NOT_READY; - for (uint8 s = 0; s < proto->Effects.size(); ++s) + for (uint8 s = 0; s < proto->Effects.size() && s < 5; ++s) { // CastItem will be used up and does not count as reagent int32 charges = m_CastItem->GetSpellCharges(s); - if (proto->Effects[s].Charges < 0 && abs(charges) < 2) + if (proto->Effects[s]->Charges < 0 && abs(charges) < 2) { ++itemcount; break; @@ -6165,8 +6146,28 @@ SpellCastResult Spell::CheckItems() else totems -= 1; } + if (totems != 0) return SPELL_FAILED_TOTEMS; + + // Check items for TotemCategory (items presence in inventory) + uint32 totemCategory = 2; + for (uint8 i = 0; i < 2; ++i) + { + if (m_spellInfo->TotemCategory[i] != 0) + { + if (player->HasItemTotemCategory(m_spellInfo->TotemCategory[i])) + { + totemCategory -= 1; + continue; + } + } + else + totemCategory -= 1; + } + + if (totemCategory != 0) + return SPELL_FAILED_TOTEM_CATEGORY; } // special checks for spell effects @@ -6239,9 +6240,9 @@ SpellCastResult Spell::CheckItems() ItemTemplate const* proto = targetItem->GetTemplate(); for (uint8 e = 0; e < proto->Effects.size(); ++e) { - if (proto->Effects[e].SpellID && ( - proto->Effects[e].Trigger == ITEM_SPELLTRIGGER_ON_USE || - proto->Effects[e].Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)) + if (proto->Effects[e]->SpellID && ( + proto->Effects[e]->Trigger == ITEM_SPELLTRIGGER_ON_USE || + proto->Effects[e]->Trigger == ITEM_SPELLTRIGGER_ON_NO_DELAY_USE)) { isItemUsable = true; break; @@ -6418,8 +6419,8 @@ SpellCastResult Spell::CheckItems() if (Item* pitem = player->GetItemByEntry(item_id)) { - for (uint8 x = 0; x < pProto->Effects.size(); ++x) - if (pProto->Effects[x].Charges != 0 && pitem->GetSpellCharges(x) == pProto->Effects[x].Charges) + for (uint8 x = 0; x < pProto->Effects.size() && x < 5; ++x) + if (pProto->Effects[x]->Charges != 0 && pitem->GetSpellCharges(x) == pProto->Effects[x]->Charges) return SPELL_FAILED_ITEM_AT_MAX_CHARGES; } break; @@ -7415,13 +7416,11 @@ enum GCDLimits bool Spell::HasGlobalCooldown() const { - // Only player or controlled units have global cooldown - if (m_caster->GetCharmInfo()) - return m_caster->GetCharmInfo()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - return m_caster->ToPlayer()->GetGlobalCooldownMgr().HasGlobalCooldown(m_spellInfo); - else + // Only players or controlled units have global cooldown + if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) return false; + + return m_caster->GetSpellHistory()->HasGlobalCooldown(m_spellInfo); } void Spell::TriggerGlobalCooldown() @@ -7430,6 +7429,10 @@ void Spell::TriggerGlobalCooldown() if (!gcd) return; + // Only players or controlled units have global cooldown + if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + return; + if (m_caster->GetTypeId() == TYPEID_PLAYER) if (m_caster->ToPlayer()->GetCommandStatus(CHEAT_COOLDOWN)) return; @@ -7451,11 +7454,7 @@ void Spell::TriggerGlobalCooldown() gcd = MAX_GCD; } - // Only players or controlled units have global cooldown - if (m_caster->GetCharmInfo()) - m_caster->GetCharmInfo()->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->GetGlobalCooldownMgr().AddGlobalCooldown(m_spellInfo, gcd); + m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd); } void Spell::CancelGlobalCooldown() @@ -7468,10 +7467,10 @@ void Spell::CancelGlobalCooldown() return; // Only players or controlled units have global cooldown - if (m_caster->GetCharmInfo()) - m_caster->GetCharmInfo()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo); - else if (m_caster->GetTypeId() == TYPEID_PLAYER) - m_caster->ToPlayer()->GetGlobalCooldownMgr().CancelGlobalCooldown(m_spellInfo); + if (m_caster->GetTypeId() != TYPEID_PLAYER && !m_caster->GetCharmInfo()) + return; + + m_caster->GetSpellHistory()->CancelGlobalCooldown(m_spellInfo); } bool Spell::HasEffect(SpellEffectName effect) const diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index e751f9915cd..42c5258f4f8 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -498,6 +498,7 @@ class Spell void ReSetTimer() { m_timer = m_casttime > 0 ? m_casttime : 0; } bool IsNextMeleeSwingSpell() const; bool IsTriggered() const { return (_triggeredCastFlags & TRIGGERED_FULL_MASK) != 0; } + bool IsIgnoringCooldowns() const { return (_triggeredCastFlags & TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD) != 0; } bool IsChannelActive() const { return m_caster->GetUInt32Value(UNIT_CHANNEL_SPELL) != 0; } bool IsAutoActionResetSpell() const; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 73217e6a9e3..db332f2fc49 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -32,6 +32,7 @@ #include "DynamicObject.h" #include "SpellAuras.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "Group.h" #include "UpdateData.h" #include "MapManager.h" @@ -672,9 +673,7 @@ void Spell::EffectTriggerSpell(SpellEffIndex /*effIndex*/) return; // Reset cooldown on stealth if needed - if (unitTarget->ToPlayer()->HasSpellCooldown(1784)) - unitTarget->ToPlayer()->RemoveSpellCooldown(1784); - + unitTarget->GetSpellHistory()->ResetCooldown(1784); unitTarget->CastSpell(unitTarget, 1784, true); return; } @@ -778,7 +777,7 @@ 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()) - m_caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id); + m_caster->GetSpellHistory()->ResetCooldown(spellInfo->Id); // original caster guid only for GO cast m_caster->CastSpell(targets, spellInfo, &values, TRIGGERED_FULL_MASK, NULL, NULL, m_originalCasterGUID); @@ -831,7 +830,7 @@ 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()) - m_caster->ToPlayer()->RemoveSpellCooldown(spellInfo->Id); + m_caster->GetSpellHistory()->ResetCooldown(spellInfo->Id); // original caster guid only for GO cast m_caster->CastSpell(targets, spellInfo, &values, TRIGGERED_FULL_MASK, NULL, NULL, m_originalCasterGUID); @@ -3175,7 +3174,7 @@ void Spell::EffectInterruptCast(SpellEffIndex effIndex) if (m_originalCaster) { int32 duration = m_spellInfo->GetDuration(); - unitTarget->ProhibitSpellSchool(curSpellInfo->GetSchoolMask(), unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effIndex)); + unitTarget->GetSpellHistory()->LockSpellSchool(curSpellInfo->GetSchoolMask(), unitTarget->ModSpellDuration(m_spellInfo, unitTarget, duration, false, 1 << effIndex)); } ExecuteLogEffectInterruptCast(effIndex, unitTarget, curSpellInfo->Id); unitTarget->InterruptSpell(CurrentSpellTypes(i), false); @@ -3944,7 +3943,7 @@ void Spell::EffectStuck(SpellEffIndex /*effIndex*/) } // the player dies if hearthstone is in cooldown, else the player is teleported to home - if (player->HasSpellCooldown(8690)) + if (player->GetSpellHistory()->HasCooldown(8690)) { player->Kill(player); return; @@ -5616,7 +5615,7 @@ void Spell::EffectCastButtons(SpellEffIndex /*effIndex*/) if (!spellInfo) continue; - if (!p_caster->HasSpell(spell_id) || p_caster->HasSpellCooldown(spell_id)) + if (!p_caster->HasSpell(spell_id) || p_caster->GetSpellHistory()->HasCooldown(spell_id)) continue; if (!spellInfo->HasAttribute(SPELL_ATTR9_SUMMON_PLAYER_TOTEM)) @@ -5656,8 +5655,8 @@ void Spell::EffectRechargeManaGem(SpellEffIndex /*effIndex*/) if (Item* pItem = player->GetItemByEntry(item_id)) { - for (size_t x = 0; x < pProto->Effects.size(); ++x) - pItem->SetSpellCharges(x, pProto->Effects[x].Charges); + for (size_t x = 0; x < pProto->Effects.size() && x < 5; ++x) + pItem->SetSpellCharges(x, pProto->Effects[x]->Charges); pItem->SetState(ITEM_CHANGED, player); } } diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp new file mode 100644 index 00000000000..658498e7455 --- /dev/null +++ b/src/server/game/Spells/SpellHistory.cpp @@ -0,0 +1,854 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "SpellHistory.h" +#include "Pet.h" +#include "Player.h" +#include "SpellInfo.h" +#include "SpellPackets.h" +#include "World.h" + +SpellHistory::Clock::duration const SpellHistory::InfinityCooldownDelay = std::chrono::duration_cast<SpellHistory::Clock::duration>(std::chrono::seconds(MONTH)); + +template<> +struct SpellHistory::PersistenceHelper<Player> +{ + static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_CHAR_SPELL_COOLDOWNS; + static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_CHAR_SPELL_COOLDOWN; + static CharacterDatabaseStatements const ChargesDeleteStatement = CHAR_DEL_CHAR_SPELL_CHARGES; + static CharacterDatabaseStatements const ChargesInsertStatement = CHAR_INS_CHAR_SPELL_CHARGES; + + static void SetIdentifier(PreparedStatement* stmt, uint8 index, Unit* owner) { stmt->setUInt64(index, owner->GetGUID().GetCounter()); } + + static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry) + { + *spellId = fields[0].GetUInt32(); + if (!sSpellMgr->GetSpellInfo(*spellId)) + return false; + + cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[2].GetUInt32())); + cooldownEntry->ItemId = fields[1].GetUInt32(); + return true; + } + + static bool ReadCharge(Field* fields, uint32* categoryId, ChargeEntry* chargeEntry) + { + *categoryId = fields[0].GetUInt32(); + if (!sSpellCategoryStore.LookupEntry(*categoryId)) + return false; + + chargeEntry->RechargeStart = Clock::from_time_t(time_t(fields[1].GetUInt32())); + chargeEntry->RechargeEnd = Clock::from_time_t(time_t(fields[2].GetUInt32())); + return true; + } + + static void WriteCooldown(PreparedStatement* stmt, uint8& index, CooldownStorageType::value_type const& cooldown) + { + stmt->setUInt32(index++, cooldown.first); + stmt->setUInt32(index++, cooldown.second.ItemId); + stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd))); + } + + static void WriteCharge(PreparedStatement* stmt, uint8& index, uint32 chargeCategory, ChargeEntry const& charge) + { + stmt->setUInt32(index++, chargeCategory); + stmt->setUInt32(index++, uint32(Clock::to_time_t(charge.RechargeStart))); + stmt->setUInt32(index++, uint32(Clock::to_time_t(charge.RechargeEnd))); + } +}; + +template<> +struct SpellHistory::PersistenceHelper<Pet> +{ + static CharacterDatabaseStatements const CooldownsDeleteStatement = CHAR_DEL_PET_SPELL_COOLDOWNS; + static CharacterDatabaseStatements const CooldownsInsertStatement = CHAR_INS_PET_SPELL_COOLDOWN; + static CharacterDatabaseStatements const ChargesDeleteStatement = CHAR_DEL_PET_SPELL_CHARGES; + static CharacterDatabaseStatements const ChargesInsertStatement = CHAR_INS_PET_SPELL_CHARGES; + + static void SetIdentifier(PreparedStatement* stmt, uint8 index, Unit* owner) { stmt->setUInt32(index, owner->GetCharmInfo()->GetPetNumber()); } + + static bool ReadCooldown(Field* fields, uint32* spellId, CooldownEntry* cooldownEntry) + { + *spellId = fields[0].GetUInt32(); + if (!sSpellMgr->GetSpellInfo(*spellId)) + return false; + + cooldownEntry->CooldownEnd = Clock::from_time_t(time_t(fields[1].GetUInt32())); + cooldownEntry->ItemId = 0; + return true; + } + + static bool ReadCharge(Field* fields, uint32* categoryId, ChargeEntry* chargeEntry) + { + *categoryId = fields[0].GetUInt32(); + if (!sSpellCategoryStore.LookupEntry(*categoryId)) + return false; + + chargeEntry->RechargeStart = Clock::from_time_t(time_t(fields[1].GetUInt32())); + chargeEntry->RechargeEnd = Clock::from_time_t(time_t(fields[2].GetUInt32())); + return true; + } + + static void WriteCooldown(PreparedStatement* stmt, uint8& index, CooldownStorageType::value_type const& cooldown) + { + stmt->setUInt32(index++, cooldown.first); + stmt->setUInt32(index++, uint32(Clock::to_time_t(cooldown.second.CooldownEnd))); + } + + static void WriteCharge(PreparedStatement* stmt, uint8& index, uint32 chargeCategory, ChargeEntry const& charge) + { + stmt->setUInt32(index++, chargeCategory); + stmt->setUInt32(index++, uint32(Clock::to_time_t(charge.RechargeStart))); + stmt->setUInt32(index++, uint32(Clock::to_time_t(charge.RechargeEnd))); + } +}; + +template<class OwnerType> +void SpellHistory::LoadFromDB(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult) +{ + typedef PersistenceHelper<OwnerType> StatementInfo; + + if (cooldownsResult) + { + do + { + uint32 spellId; + CooldownEntry cooldown; + if (StatementInfo::ReadCooldown(cooldownsResult->Fetch(), &spellId, &cooldown)) + _spellCooldowns[spellId] = cooldown; + + } while (cooldownsResult->NextRow()); + } + + if (chargesResult) + { + do + { + Field* fields = chargesResult->Fetch(); + uint32 categoryId = 0; + ChargeEntry charges; + if (StatementInfo::ReadCharge(fields, &categoryId, &charges)) + _categoryCharges[categoryId].push_back(charges); + + } while (chargesResult->NextRow()); + } +} + +template<class OwnerType> +void SpellHistory::SaveToDB(SQLTransaction& trans) +{ + typedef PersistenceHelper<OwnerType> StatementInfo; + + uint8 index = 0; + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsDeleteStatement); + StatementInfo::SetIdentifier(stmt, index++, _owner); + trans->Append(stmt); + + for (auto const& p : _spellCooldowns) + { + if (!p.second.OnHold) + { + index = 0; + stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::CooldownsInsertStatement); + StatementInfo::SetIdentifier(stmt, index++, _owner); + StatementInfo::WriteCooldown(stmt, index, p); + trans->Append(stmt); + } + } + + stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::ChargesDeleteStatement); + StatementInfo::SetIdentifier(stmt, 0, _owner); + trans->Append(stmt); + + for (auto const& p : _categoryCharges) + { + for (ChargeEntry const& charge : p.second) + { + index = 0; + stmt = CharacterDatabase.GetPreparedStatement(StatementInfo::ChargesInsertStatement); + StatementInfo::SetIdentifier(stmt, index++, _owner); + StatementInfo::WriteCharge(stmt, index, p.first, charge); + trans->Append(stmt); + } + } +} + +void SpellHistory::Update() +{ + SQLTransaction t; + Clock::time_point now = Clock::now(); + for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();) + { + if (itr->second.CooldownEnd < now) + itr = _spellCooldowns.erase(itr); + else + ++itr; + } + + for (auto& p : _categoryCharges) + { + std::deque<ChargeEntry>& chargeRefreshTimes = p.second; + while (!chargeRefreshTimes.empty() && chargeRefreshTimes.front().RechargeEnd <= now) + chargeRefreshTimes.pop_front(); + } +} + +void SpellHistory::HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell /*= nullptr*/) +{ + if (ConsumeCharge(spellInfo->ChargeCategoryEntry)) + return; + + if (Player* player = _owner->ToPlayer()) + { + // potions start cooldown until exiting combat + if (item && (item->IsPotion() || spellInfo->IsCooldownStartedOnEvent())) + { + player->SetLastPotionId(item->GetEntry()); + return; + } + } + + if (spellInfo->IsCooldownStartedOnEvent() || spellInfo->IsPassive() || (spell && spell->IsIgnoringCooldowns())) + return; + + StartCooldown(spellInfo, item ? item->GetEntry() : 0, spell); +} + +bool SpellHistory::IsReady(SpellInfo const* spellInfo) const +{ + if (spellInfo->PreventionType == SPELL_PREVENTION_TYPE_SILENCE) + if (IsSchoolLocked(spellInfo->GetSchoolMask())) + return false; + + if (HasCooldown(spellInfo->Id)) + return false; + + if (!HasCharge(spellInfo->ChargeCategoryEntry)) + return false; + + return true; +} + +template<class PacketType> +void SpellHistory::WritePacket(PacketType* packet) const +{ + static_assert(!std::is_same<PacketType, PacketType>::value /*static_assert(false)*/, "This packet is not supported."); +} + +template<> +void SpellHistory::WritePacket(WorldPackets::Spells::SendSpellHistory* sendSpellHistory) const +{ + sendSpellHistory->Entries.reserve(_spellCooldowns.size()); + + Clock::time_point now = Clock::now(); + for (auto const& p : _spellCooldowns) + { + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(p.first); + WorldPackets::Spells::SpellHistoryEntry historyEntry; + historyEntry.SpellID = p.first; + historyEntry.ItemID = p.second.ItemId; + historyEntry.Category = spellInfo->GetCategory(); + + if (p.second.OnHold) + historyEntry.OnHold = true; + else + { + std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(p.second.CooldownEnd - now); + if (cooldownDuration.count() <= 0) + continue; + + if (spellInfo->GetCategory()) + historyEntry.CategoryRecoveryTime = uint32(cooldownDuration.count()); + else + historyEntry.RecoveryTime = uint32(cooldownDuration.count()); + } + + sendSpellHistory->Entries.push_back(historyEntry); + } +} + +template<> +void SpellHistory::WritePacket(WorldPackets::Spells::SendSpellCharges* sendSpellCharges) const +{ + sendSpellCharges->Entries.reserve(_categoryCharges.size()); + + Clock::time_point now = Clock::now(); + for (auto const& p : _categoryCharges) + { + if (!p.second.empty()) + { + std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(p.second.front().RechargeEnd - now); + if (cooldownDuration.count() <= 0) + continue; + + WorldPackets::Spells::SpellChargeEntry chargeEntry; + chargeEntry.Category = p.first; + chargeEntry.NextRecoveryTime = uint32(cooldownDuration.count()); + chargeEntry.ConsumedCharges = p.second.size(); + sendSpellCharges->Entries.push_back(chargeEntry); + } + } +} + +/* +template<> +void SpellHistory::WritePacket(WorldPackets::Pet::PetSpells* petSpells) +{ + Clock::time_point now = Clock::now(); + + petSpells->Cooldowns.reserve(_spellCooldowns.size()); + for (auto const& p : _spellCooldowns) + { + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(p.first); + WorldPackets::Pet::PetSpellCooldown petSpellCooldown; + petSpellCooldown.SpellID = p.first; + petSpellCooldown.Category = spellInfo->GetCategory(); + + if (!p.second.OnHold) + { + std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(p.second.CooldownEnd - now); + if (cooldownDuration.count() <= 0) + continue; + + if (spellInfo->GetCategory()) + petSpellCooldown.CategoryDuration = uint32(cooldownDuration.count()); + else + petSpellCooldown.Duration = uint32(cooldownDuration.count()); + } + + petSpells->Cooldowns.push_back(historyEntry); + } + + petSpells->SpellHistory.reserve(_categoryCharges.size()); + for (auto const& p : _categoryCharges) + { + if (!p.second.empty()) + { + std::chrono::milliseconds cooldownDuration = std::chrono::duration_cast<std::chrono::milliseconds>(p.second.front().RechargeEnd - now); + if (cooldownDuration.count() <= 0) + continue; + + WorldPackets::Pet::PetSpellHistory petChargeEntry; + petChargeEntry.CategoryID = p.first; + petChargeEntry.RecoveryTime = uint32(cooldownDuration.count()); + petChargeEntry.ConsumedCharges = p.second.size(); + + petSpells->SpellHistory.push_back(petChargeEntry); + } + } +} +*/ + +void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spell* spell /*= nullptr*/, bool onHold /*= false*/) +{ + // init cooldown values + uint32 categoryId = 0; + int32 cooldown = -1; + int32 categoryCooldown = -1; + + // some special item spells without correct cooldown in SpellInfo + // cooldown information stored in item prototype + if (itemId) + { + if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemId)) + { + for (ItemEffectEntry const* itemEffect : proto->Effects) + { + if (itemEffect->SpellID == spellInfo->Id) + { + categoryId = itemEffect->Category; + cooldown = itemEffect->Cooldown; + categoryCooldown = itemEffect->CategoryCooldown; + break; + } + } + } + } + + // if no cooldown found above then base at DBC data + if (cooldown < 0 && categoryCooldown < 0) + { + categoryId = spellInfo->GetCategory(); + cooldown = spellInfo->RecoveryTime; + categoryCooldown = spellInfo->CategoryRecoveryTime; + } + + Clock::time_point curTime = Clock::now(); + Clock::time_point catrecTime; + Clock::time_point recTime; + bool needsCooldownPacket = false; + + // 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; + } + else + { + // shoot spells used equipped item cooldown values already assigned in GetAttackTime(RANGED_ATTACK) + // prevent 0 cooldowns set by another way + if (cooldown <= 0 && categoryCooldown <= 0 && (categoryId == 76 || (spellInfo->IsAutoRepeatRangedSpell() && spellInfo->Id != 75))) + cooldown = _owner->GetAttackTime(RANGED_ATTACK); + + // Now we have cooldown data (if found any), time to apply mods + if (Player* modOwner = _owner->GetSpellModOwner()) + { + if (cooldown > 0) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, cooldown, spell); + + if (categoryCooldown > 0 && !spellInfo->HasAttribute(SPELL_ATTR6_IGNORE_CATEGORY_COOLDOWN_MODS)) + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell); + } + + 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 += cooldownMod * IN_MILLISECONDS; // 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 > 0) + cooldown += categoryModifier; + + if (categoryCooldown > 0) + categoryCooldown += categoryModifier; + } + + SpellCategoryEntry const* categoryEntry = sSpellCategoryStore.AssertEntry(categoryId); + if (categoryEntry->Flags & SPELL_CATEGORY_FLAG_COOLDOWN_EXPIRES_AT_DAILY_RESET) + categoryCooldown = int32(std::chrono::duration_cast<std::chrono::milliseconds>(Clock::from_time_t(sWorld->GetNextDailyQuestsResetTime()) - Clock::now()).count()); + } + + // replace negative cooldowns by 0 + if (cooldown < 0) + cooldown = 0; + + if (categoryCooldown < 0) + categoryCooldown = 0; + + // no cooldown after applying spell mods + if (cooldown == 0 && categoryCooldown == 0) + return; + + catrecTime = categoryCooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(categoryCooldown)) : curTime; + recTime = cooldown ? curTime + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldown)) : catrecTime; + } + + // self spell cooldown + if (recTime != curTime) + { + AddCooldown(spellInfo->Id, itemId, recTime, onHold); + + if (needsCooldownPacket) + { + if (Player* playerOwner = GetPlayerOwner()) + { + WorldPackets::Spells::SpellCooldown spellCooldown; + spellCooldown.Caster = _owner->GetGUID(); + spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; + spellCooldown.SpellCooldowns.emplace_back(spellInfo->Id, uint32(cooldown)); + playerOwner->SendDirectMessage(spellCooldown.Write()); + } + } + } + + // category spells + if (categoryId && catrecTime != curTime) + { + SpellCategoryStore::const_iterator i_scstore = sSpellsByCategoryStore.find(categoryId); + 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; + + AddCooldown(*i_scset, itemId, catrecTime, onHold); + } + } + } +} + +void SpellHistory::SendCooldownEvent(SpellInfo const* spellInfo, uint32 itemId /*= 0*/, Spell* spell /*= nullptr*/, bool startCooldown /*= true*/) +{ + // start cooldowns at server side, if any + if (startCooldown) + StartCooldown(spellInfo, itemId, spell); + + if (Player* player = GetPlayerOwner()) + { + // Send activate cooldown timer (possible 0) at client side + player->SendDirectMessage(WorldPackets::Spells::CooldownEvent(_owner->GetGUID(), spellInfo->Id).Write()); + + uint32 category = spellInfo->GetCategory(); + if (category && spellInfo->CategoryRecoveryTime) + { + SpellCategoryStore::const_iterator ct = sSpellsByCategoryStore.find(category); + if (ct != sSpellsByCategoryStore.end()) + { + for (auto const& cooldownPair : _spellCooldowns) + { + uint32 categorySpell = cooldownPair.first; + if (!ct->second.count(categorySpell)) + continue; + + if (categorySpell == spellInfo->Id) // skip main spell, already handled above + continue; + + SpellInfo const* spellInfo2 = sSpellMgr->AssertSpellInfo(categorySpell); + if (!spellInfo2->IsCooldownStartedOnEvent()) + continue; + + player->SendDirectMessage(WorldPackets::Spells::CooldownEvent(_owner->GetGUID(), categorySpell).Write()); + } + } + } + } +} + +void SpellHistory::AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, bool onHold /*= false*/) +{ + CooldownEntry& cooldownEntry = _spellCooldowns[spellId]; + cooldownEntry.CooldownEnd = cooldownEnd; + cooldownEntry.ItemId = itemId; + cooldownEntry.OnHold = onHold; +} + +void SpellHistory::ModifyCooldown(uint32 spellId, int32 cooldownModMs) +{ + auto itr = _spellCooldowns.find(spellId); + if (!cooldownModMs || itr == _spellCooldowns.end()) + return; + + Clock::time_point now = Clock::now(); + Clock::duration offset = std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(cooldownModMs)); + if (itr->second.CooldownEnd + offset > now) + itr->second.CooldownEnd += offset; + else + _spellCooldowns.erase(itr); + + if (Player* playerOwner = GetPlayerOwner()) + { + WorldPackets::Spells::ModifyCooldown modifyCooldown; + modifyCooldown.UnitGUID = _owner->GetGUID(); + modifyCooldown.SpellID = spellId; + modifyCooldown.DeltaTime = cooldownModMs; + playerOwner->SendDirectMessage(modifyCooldown.Write()); + } +} + +void SpellHistory::ResetCooldown(uint32 spellId, bool update /*= false*/) +{ + auto itr = _spellCooldowns.find(spellId); + if (itr == _spellCooldowns.end()) + return; + + ResetCooldown(itr, update); +} + +void SpellHistory::ResetCooldown(CooldownStorageType::iterator& itr, bool update /*= false*/) +{ + if (update) + { + if (Player* playerOwner = GetPlayerOwner()) + { + WorldPackets::Spells::ClearCooldown clearCooldown; + clearCooldown.CasterGUID = _owner->GetGUID(); + clearCooldown.SpellID = itr->first; + clearCooldown.ClearOnHold = false; + playerOwner->SendDirectMessage(clearCooldown.Write()); + } + } + + itr = _spellCooldowns.erase(itr); +} + +void SpellHistory::ResetAllCooldowns() +{ + if (Player* playerOwner = GetPlayerOwner()) + { + std::vector<int32> cooldowns; + cooldowns.reserve(_spellCooldowns.size()); + for (auto const& p : _spellCooldowns) + cooldowns.push_back(p.first); + + SendClearCooldowns(cooldowns); + } + + _spellCooldowns.clear(); +} + +bool SpellHistory::HasCooldown(uint32 spellId) const +{ + return _spellCooldowns.count(spellId) != 0; +} + +uint32 SpellHistory::GetRemainingCooldown(uint32 spellId) const +{ + auto itr = _spellCooldowns.find(spellId); + if (itr == _spellCooldowns.end()) + return 0; + + Clock::time_point now = Clock::now(); + if (itr->second.CooldownEnd < now) + return 0; + + Clock::duration remaining = itr->second.CooldownEnd - now; + return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(remaining).count()); +} + +void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime) +{ + Clock::time_point lockoutEnd = Clock::now() + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(lockoutTime)); + for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i) + if (SpellSchoolMask(1 << i) & schoolMask) + _schoolLockouts[i] = lockoutEnd; + + std::set<uint32> knownSpells; + if (Player* plrOwner = _owner->ToPlayer()) + { + for (auto const& p : plrOwner->GetSpellMap()) + if (p.second->state != PLAYERSPELL_REMOVED) + knownSpells.insert(p.first); + } + else if (Pet* petOwner = _owner->ToPet()) + { + for (auto const& p : petOwner->m_spells) + if (p.second.state != PETSPELL_REMOVED) + knownSpells.insert(p.first); + } + else + { + Creature* creatureOwner = _owner->ToCreature(); + for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) + if (creatureOwner->m_spells[i]) + knownSpells.insert(creatureOwner->m_spells[i]); + } + + WorldPackets::Spells::SpellCooldown spellCooldown; + spellCooldown.Caster = _owner->GetGUID(); + spellCooldown.Flags = SPELL_COOLDOWN_FLAG_NONE; + for (uint32 spellId : knownSpells) + { + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(spellId); + if (spellInfo->IsCooldownStartedOnEvent()) + continue; + + if (spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE) + continue; + + if ((schoolMask & spellInfo->GetSchoolMask()) && GetRemainingCooldown(spellId) < lockoutTime) + { + spellCooldown.SpellCooldowns.emplace_back(spellId, lockoutTime); + AddCooldown(spellId, 0, lockoutEnd); + } + } + + if (Player* player = GetPlayerOwner()) + if (!spellCooldown.SpellCooldowns.empty()) + player->SendDirectMessage(spellCooldown.Write()); +} + +bool SpellHistory::IsSchoolLocked(SpellSchoolMask schoolMask) const +{ + Clock::time_point now = Clock::now(); + for (uint32 i = 0; i < MAX_SPELL_SCHOOL; ++i) + if (SpellSchoolMask(1 << i) & schoolMask) + if (_schoolLockouts[i] > now) + return true; + + return false; +} + +bool SpellHistory::ConsumeCharge(SpellCategoryEntry const* chargeCategoryEntry) +{ + if (!chargeCategoryEntry) + return false; + + int32 chargeRecovery = GetChargeRecoveryTime(chargeCategoryEntry); + if (chargeRecovery > 0 && GetMaxCharges(chargeCategoryEntry) > 0) + { + Clock::time_point recoveryStart; + std::deque<ChargeEntry>& charges = _categoryCharges[chargeCategoryEntry->ID]; + if (charges.empty()) + recoveryStart = Clock::now(); + else + recoveryStart = charges.back().RechargeEnd; + + charges.emplace_back(recoveryStart, std::chrono::milliseconds(chargeRecovery)); + return true; + } + + return false; +} + +void SpellHistory::RestoreCharge(SpellCategoryEntry const* chargeCategoryEntry) +{ + if (!chargeCategoryEntry) + return; + + auto itr = _categoryCharges.find(chargeCategoryEntry->ID); + if (itr != _categoryCharges.end() && !itr->second.empty()) + { + itr->second.pop_back(); + + if (Player* player = GetPlayerOwner()) + { + int32 maxCharges = GetMaxCharges(chargeCategoryEntry); + int32 usedCharges = itr->second.size(); + float count = float(maxCharges - usedCharges); + if (usedCharges) + { + ChargeEntry& charge = itr->second.front(); + std::chrono::milliseconds remaining = std::chrono::duration_cast<std::chrono::milliseconds>(charge.RechargeEnd - Clock::now()); + std::chrono::milliseconds recharge = std::chrono::duration_cast<std::chrono::milliseconds>(charge.RechargeEnd - charge.RechargeStart); + count += 1.0f - float(remaining.count()) / float(recharge.count()); + } + + WorldPackets::Spells::SetSpellCharges setSpellCharges; + setSpellCharges.IsPet = player == _owner; + setSpellCharges.Count = count; + setSpellCharges.Category = chargeCategoryEntry->ID; + player->SendDirectMessage(setSpellCharges.Write()); + } + } +} + +void SpellHistory::ResetCharges(SpellCategoryEntry const* chargeCategoryEntry) +{ + if (!chargeCategoryEntry) + return; + + auto itr = _categoryCharges.find(chargeCategoryEntry->ID); + if (itr != _categoryCharges.end()) + { + _categoryCharges.erase(itr); + + if (Player* player = GetPlayerOwner()) + { + WorldPackets::Spells::ClearSpellCharges clearSpellCharges; + clearSpellCharges.Unit = _owner->GetGUID(); + clearSpellCharges.Category = chargeCategoryEntry->ID; + player->SendDirectMessage(clearSpellCharges.Write()); + } + } +} + +void SpellHistory::ResetAllCharges() +{ + _categoryCharges.clear(); + + if (Player* player = GetPlayerOwner()) + { + WorldPackets::Spells::ClearAllSpellCharges clearAllSpellCharges; + clearAllSpellCharges.Unit = _owner->GetGUID(); + player->SendDirectMessage(clearAllSpellCharges.Write()); + } +} + +bool SpellHistory::HasCharge(SpellCategoryEntry const* chargeCategoryEntry) const +{ + if (!chargeCategoryEntry) + return true; + + // Check if the spell is currently using charges (untalented warlock Dark Soul) + int32 maxCharges = GetMaxCharges(chargeCategoryEntry); + if (maxCharges <= 0) + return true; + + auto itr = _categoryCharges.find(chargeCategoryEntry->ID); + return itr == _categoryCharges.end() || itr->second.size() < maxCharges; +} + +int32 SpellHistory::GetMaxCharges(SpellCategoryEntry const* chargeCategoryEntry) const +{ + if (!chargeCategoryEntry) + return 0; + + uint32 charges = chargeCategoryEntry->MaxCharges; + charges += _owner->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MAX_CHARGES, chargeCategoryEntry->ID); + return charges; +} + +int32 SpellHistory::GetChargeRecoveryTime(SpellCategoryEntry const* chargeCategoryEntry) const +{ + if (!chargeCategoryEntry) + return 0; + + int32 recoveryTime = chargeCategoryEntry->ChargeRecoveryTime; + recoveryTime += _owner->GetTotalAuraModifierByMiscValue(SPELL_AURA_CHARGE_RECOVERY_MOD, chargeCategoryEntry->ID); + + float recoveryTimeF = recoveryTime; + recoveryTimeF *= _owner->GetTotalAuraMultiplierByMiscValue(SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER, chargeCategoryEntry->ID); + + if (_owner->HasAuraType(SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE)) + recoveryTimeF *= _owner->GetFloatValue(UNIT_MOD_CAST_HASTE); + + if (_owner->HasAuraType(SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE_REGEN)) + recoveryTimeF *= _owner->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN); + + return int32(std::floor(recoveryTimeF)); +} + +bool SpellHistory::HasGlobalCooldown(SpellInfo const* spellInfo) const +{ + auto itr = _globalCooldowns.find(spellInfo->StartRecoveryCategory); + return itr != _globalCooldowns.end() && itr->second > Clock::now(); +} + +void SpellHistory::AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration) +{ + _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::now() + std::chrono::duration_cast<Clock::duration>(std::chrono::milliseconds(duration)); +} + +void SpellHistory::CancelGlobalCooldown(SpellInfo const* spellInfo) +{ + _globalCooldowns[spellInfo->StartRecoveryCategory] = Clock::time_point(Clock::duration(0)); +} + +Player* SpellHistory::GetPlayerOwner() const +{ + return _owner->GetCharmerOrOwnerPlayerOrPlayerItself(); +} + +void SpellHistory::SendClearCooldowns(std::vector<int32> const& cooldowns) const +{ + if (Player const* playerOwner = GetPlayerOwner()) + { + WorldPackets::Spells::ClearCooldowns clearCooldowns; + clearCooldowns.Guid = _owner->GetGUID(); + clearCooldowns.SpellID = cooldowns; + playerOwner->SendDirectMessage(clearCooldowns.Write()); + } +} + +template void SpellHistory::LoadFromDB<Player>(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult); +template void SpellHistory::LoadFromDB<Pet>(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult); +template void SpellHistory::SaveToDB<Player>(SQLTransaction& trans); +template void SpellHistory::SaveToDB<Pet>(SQLTransaction& trans); diff --git a/src/server/game/Spells/SpellHistory.h b/src/server/game/Spells/SpellHistory.h new file mode 100644 index 00000000000..a55f57aa9f4 --- /dev/null +++ b/src/server/game/Spells/SpellHistory.h @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2008-2015 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef SpellHistory_h__ +#define SpellHistory_h__ + +#include "SharedDefines.h" +#include "QueryResult.h" +#include "Transaction.h" +#include <chrono> +#include <deque> + +class Item; +class Player; +class Spell; +class SpellInfo; +class Unit; +struct SpellCategoryEntry; + +/// Spell cooldown flags sent in SMSG_SPELL_COOLDOWN +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 +}; + +class SpellHistory +{ +public: + typedef std::chrono::system_clock Clock; + + struct CooldownEntry + { + CooldownEntry() : ItemId(0), OnHold(false) { } + CooldownEntry(Clock::time_point endTime, uint32 itemId) : CooldownEnd(endTime), ItemId(itemId), OnHold(false) { } + + Clock::time_point CooldownEnd; + uint32 ItemId; + bool OnHold; + }; + + struct ChargeEntry + { + ChargeEntry() { } + ChargeEntry(Clock::time_point startTime, std::chrono::milliseconds rechargeTime) : RechargeStart(startTime), RechargeEnd(startTime + rechargeTime) { } + ChargeEntry(Clock::time_point startTime, Clock::time_point endTime) : RechargeStart(startTime), RechargeEnd(endTime) { } + + Clock::time_point RechargeStart; + Clock::time_point RechargeEnd; + }; + + typedef std::unordered_map<uint32 /*spellId*/, CooldownEntry> CooldownStorageType; + typedef std::unordered_map<uint32 /*categoryId*/, std::deque<ChargeEntry>> ChargeStorageType; + typedef std::unordered_map<uint32 /*categoryId*/, Clock::time_point> GlobalCooldownStorageType; + + explicit SpellHistory(Unit* owner) : _owner(owner), _schoolLockouts() { } + + template<class OwnerType> + void LoadFromDB(PreparedQueryResult cooldownsResult, PreparedQueryResult chargesResult); + + template<class OwnerType> + void SaveToDB(SQLTransaction& trans); + + void Update(); + + void HandleCooldowns(SpellInfo const* spellInfo, Item const* item, Spell* spell = nullptr); + bool IsReady(SpellInfo const* spellInfo) const; + template<class PacketType> + void WritePacket(PacketType* packet) const; + + // Cooldowns + static Clock::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 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) + { + AddCooldown(spellId, itemId, Clock::now() + std::chrono::duration_cast<Clock::duration>(cooldownDuration)); + } + + void AddCooldown(uint32 spellId, uint32 itemId, Clock::time_point cooldownEnd, bool onHold = false); + void ModifyCooldown(uint32 spellId, int32 cooldownModMs); + void ResetCooldown(uint32 spellId, bool update = false); + void ResetCooldown(CooldownStorageType::iterator& itr, bool update = false); + template<typename Predicate> + void ResetCooldowns(Predicate predicate, bool update = false) + { + std::vector<int32> resetCooldowns; + resetCooldowns.reserve(_spellCooldowns.size()); + for (auto itr = _spellCooldowns.begin(); itr != _spellCooldowns.end();) + { + if (predicate(itr)) + { + resetCooldowns.push_back(int32(itr->first)); + ResetCooldown(itr, false); + } + else + ++itr; + } + + if (update && !resetCooldowns.empty()) + SendClearCooldowns(resetCooldowns); + } + + void ResetAllCooldowns(); + bool HasCooldown(uint32 spellId) const; + uint32 GetRemainingCooldown(uint32 spellId) const; + + // School lockouts + void LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTime); + bool IsSchoolLocked(SpellSchoolMask schoolMask) const; + + // Charges + bool ConsumeCharge(SpellCategoryEntry const* chargeCategoryEntry); + void RestoreCharge(SpellCategoryEntry const* chargeCategoryEntry); + void ResetCharges(SpellCategoryEntry const* chargeCategoryEntry); + void ResetAllCharges(); + bool HasCharge(SpellCategoryEntry const* chargeCategoryEntry) const; + int32 GetMaxCharges(SpellCategoryEntry const* chargeCategoryEntry) const; + int32 GetChargeRecoveryTime(SpellCategoryEntry const* chargeCategoryEntry) const; + + // Global cooldown + bool HasGlobalCooldown(SpellInfo const* spellInfo) const; + void AddGlobalCooldown(SpellInfo const* spellInfo, uint32 duration); + void CancelGlobalCooldown(SpellInfo const* spellInfo); + +private: + Player* GetPlayerOwner() const; + void SendClearCooldowns(std::vector<int32> const& cooldowns) const; + + Unit* _owner; + CooldownStorageType _spellCooldowns; + Clock::time_point _schoolLockouts[MAX_SPELL_SCHOOL]; + ChargeStorageType _categoryCharges; + GlobalCooldownStorageType _globalCooldowns; + + template<class T> + struct PersistenceHelper { }; +}; + +#endif // SpellHistory_h__ diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index a70c73de6df..46b7c2f92e4 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -942,23 +942,20 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] = {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 244 SPELL_EFFECT_244 }; -SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap effects) +SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap const& effectsMap) { Id = spellEntry->ID; // SpellDifficultyEntry - for (SpellEffectEntryMap::const_iterator itr = effects.begin(); itr != effects.end(); ++itr) + for (SpellEffectEntryMap::value_type const& itr : effectsMap) { - SpellEffectEntryVector effects = itr->second; - _effects[itr->first].resize(effects.size()); + SpellEffectEntryVector const& effects = itr.second; + _effects[itr.first].resize(effects.size()); - for (uint32 i = effects.size(); i > 0; --i) + for (size_t i = 0; i < effects.size(); ++i) { - SpellEffectEntry const* effect = effects[i - 1]; - if (!effect) - continue; - - _effects[itr->first][effect->EffectIndex] = new SpellEffectInfo(spellEntry, this, effect->EffectIndex, effect); + if (SpellEffectEntry const* effect = effects[i]) + _effects[itr.first][effect->EffectIndex] = new SpellEffectInfo(spellEntry, this, effect->EffectIndex, effect); } } @@ -1054,6 +1051,7 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap effects) StartRecoveryCategory = _categorie ? _categorie->StartRecoveryCategory : 0; DmgClass = _categorie ? _categorie->DefenseType : 0; PreventionType = _categorie ? _categorie->PreventionType : 0; + ChargeCategoryEntry = _categorie ? sSpellCategoryStore.LookupEntry(_categorie->ChargeCategory) : 0; // SpellClassOptionsEntry SpellClassOptionsEntry const* _class = GetSpellClassOptions(); @@ -1127,6 +1125,15 @@ SpellInfo::SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap effects) SpellInfo::~SpellInfo() { _UnloadImplicitTargetConditionLists(); + _UnloadSpellEffects(); +} + +void SpellInfo::_UnloadSpellEffects() +{ + for (SpellEffectInfoMap::value_type& i : _effects) + for (size_t j = 0; j < i.second.size(); ++j) + delete i.second[j]; + _effects.clear(); } uint32 SpellInfo::GetCategory() const @@ -1816,7 +1823,10 @@ SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 a } case SPELL_AURA_MOUNTED: { - if (effect->MiscValueB && !player->GetMountCapability(effect->MiscValueB)) + uint32 mountType = effect->MiscValueB; + if (MountEntry const* mountEntry = sDB2Manager.GetMount(Id)) + mountType = mountEntry->MountTypeId; + if (mountType && !player->GetMountCapability(mountType)) return SPELL_FAILED_NOT_HERE; break; } diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index b585588726e..9e39e88d9d3 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -399,6 +399,7 @@ public: uint32 PreventionType; int32 RequiredAreasID; uint32 SchoolMask; + SpellCategoryEntry const* ChargeCategoryEntry; uint32 SpellDifficultyId; uint32 SpellScalingId; uint32 SpellAuraOptionsId; @@ -449,7 +450,7 @@ public: SpellTotemsEntry const* GetSpellTotems() const; SpellMiscEntry const* GetSpellMisc() const; - SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap effects); + SpellInfo(SpellEntry const* spellEntry, SpellEffectEntryMap const& effectsMap); ~SpellInfo(); uint32 GetCategory() const; @@ -571,6 +572,7 @@ public: // unloading helpers void _UnloadImplicitTargetConditionLists(); + void _UnloadSpellEffects(); SpellEffectInfoVector GetEffectsForDifficulty(uint32 difficulty) const; SpellEffectInfo const* GetEffect(uint32 difficulty, uint32 index) const; diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp index 41bd04c5dca..9119fdbe76d 100644 --- a/src/server/scripts/Commands/cs_lookup.cpp +++ b/src/server/scripts/Commands/cs_lookup.cpp @@ -946,6 +946,7 @@ public: bool found = false; uint32 count = 0; uint32 maxResults = sWorld->getIntConfig(CONFIG_MAX_RESULTS_LOOKUP_COMMANDS); + int32 locale = handler->GetSessionDbcLocale(); // Search in TaxiNodes.dbc for (uint32 id = 0; id < sTaxiNodesStore.GetNumRows(); id++) @@ -953,7 +954,7 @@ public: TaxiNodesEntry const* nodeEntry = sTaxiNodesStore.LookupEntry(id); if (nodeEntry) { - std::string name = nodeEntry->Name_lang; + std::string name = nodeEntry->Name_lang->Str[locale]; if (name.empty()) continue; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 889b24480e6..65b2d20c9d2 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -36,6 +36,7 @@ #include "GroupMgr.h" #include "MMapFactory.h" #include "DisableMgr.h" +#include "SpellHistory.h" class misc_commandscript : public CommandScript { @@ -713,7 +714,8 @@ public: if (!*args) { - target->RemoveAllSpellCooldown(); + target->GetSpellHistory()->ResetAllCooldowns(); + target->GetSpellHistory()->ResetAllCharges(); handler->PSendSysMessage(LANG_REMOVEALL_COOLDOWN, nameLink.c_str()); } else @@ -723,14 +725,16 @@ public: if (!spellIid) return false; - if (!sSpellMgr->GetSpellInfo(spellIid)) + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellIid); + if (!spellInfo) { handler->PSendSysMessage(LANG_UNKNOWN_SPELL, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); handler->SetSentErrorMessage(true); return false; } - target->RemoveSpellCooldown(spellIid, true); + target->GetSpellHistory()->ResetCooldown(spellIid, true); + target->GetSpellHistory()->ResetCharges(spellInfo->ChargeCategoryEntry); handler->PSendSysMessage(LANG_REMOVE_COOLDOWN, spellIid, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); } return true; 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 754b22620d0..511a2044f6a 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -25,6 +25,7 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "SpellScript.h" #include "Transport.h" #include "TransportMgr.h" @@ -644,10 +645,10 @@ protected: { if (Instance->GetBossState(DATA_ICECROWN_GUNSHIP_BATTLE) == IN_PROGRESS && !me->HasUnitState(UNIT_STATE_CASTING) && !me->HasReactState(REACT_PASSIVE) && - !me->HasSpellCooldown(BurningPitchId)) + !me->GetSpellHistory()->HasCooldown(BurningPitchId)) { DoCastAOE(BurningPitchId, true); - me->_AddCreatureSpellCooldown(BurningPitchId, time(NULL) + urand(3000, 4000) / IN_MILLISECONDS); + me->GetSpellHistory()->AddCooldown(BurningPitchId, 0, std::chrono::milliseconds(urand(3000, 4000))); } } @@ -1469,7 +1470,7 @@ struct npc_gunship_boarding_addAI : public gunship_npc_AI DoCast(me, SPELL_BATTLE_EXPERIENCE, true); DoCast(me, SPELL_TELEPORT_TO_ENEMY_SHIP, true); DoCast(me, Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_MELEE_TARGETING_ON_ORGRIMS_HAMMER : SPELL_MELEE_TARGETING_ON_SKYBREAKER, true); - me->_AddCreatureSpellCooldown(BurningPitchId, time(NULL) + 3); + me->GetSpellHistory()->AddCooldown(BurningPitchId, 0, std::chrono::seconds(3)); std::list<Player*> players; Trinity::UnitAuraCheck check(true, Instance->GetData(DATA_TEAM_IN_INSTANCE) == HORDE ? SPELL_ON_ORGRIMS_HAMMER_DECK : SPELL_ON_SKYBREAKER_DECK); @@ -1698,11 +1699,11 @@ class npc_gunship_rocketeer : public CreatureScript return; uint32 spellId = me->GetEntry() == NPC_SKYBREAKER_MORTAR_SOLDIER ? SPELL_ROCKET_ARTILLERY_A : SPELL_ROCKET_ARTILLERY_H; - if (me->HasSpellCooldown(spellId)) + if (me->GetSpellHistory()->HasCooldown(spellId)) return; DoCastAOE(spellId, true); - me->_AddCreatureSpellCooldown(spellId, time(NULL) + 9); + me->GetSpellHistory()->AddCooldown(spellId, 0, std::chrono::seconds(9)); } }; diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp index d653ec9e57d..e967f4aa8c5 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp @@ -29,7 +29,7 @@ EndScriptData */ #include "Player.h" #include "SpellInfo.h" -enum Enums +enum Texts { SAY_AGGRO = 0, SAY_FORGE = 1, @@ -38,29 +38,50 @@ enum Enums SAY_DEATH = 4, EMOTE_TO_ANVIL = 5, EMOTE_SHATTER = 6, +}; +enum Spells +{ SPELL_HEAT = 52387, SPELL_SHATTERING_STOMP = 52237, - SPELL_TEMPER = 52238, SPELL_TEMPER_DUMMY = 52654, - SPELL_SUMMON_MOLTEN_GOLEM = 52405, + SPELL_FORGE_VISUAL = 52654, // Molten Golem SPELL_BLAST_WAVE = 23113, SPELL_IMMOLATION_STRIKE = 52433, SPELL_SHATTER = 52429, +}; +enum Events +{ + EVENT_PAUSE = 1, + EVENT_SHATTERING_STOMP = 2, + EVENT_SHATTER = 3, + EVENT_FORGE_CAST = 4, + + // Molten Golem + EVENT_BLAST = 5, + EVENT_IMMOLATION = 6 +}; + +enum Npcs +{ NPC_VOLKHAN_ANVIL = 28823, NPC_MOLTEN_GOLEM = 28695, NPC_BRITTLE_GOLEM = 28681, - MAX_GOLEM = 2, - DATA_SHATTER_RESISTANT = 2042 }; +enum Phases +{ + PHASE_INTRO = 1, + PHASE_NORMAL +}; + /*###### ## Boss Volkhan ######*/ @@ -68,68 +89,44 @@ class boss_volkhan : public CreatureScript { public: boss_volkhan() : CreatureScript("boss_volkhan") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_volkhanAI>(creature); - } - - struct boss_volkhanAI : public ScriptedAI + + struct boss_volkhanAI : public BossAI { - boss_volkhanAI(Creature* creature) : ScriptedAI(creature) + boss_volkhanAI(Creature* creature) : BossAI(creature, DATA_VOLKHAN) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() { - m_bIsStriking = false; - m_bHasTemper = false; + m_bIsStriking = false; + m_bHasTemper = false; m_bCanShatterGolem = false; - - m_uiPause_Timer = 3500; - m_uiShatteringStomp_Timer = 0; - m_uiShatter_Timer = 5000; - m_uiDelay_Timer = 1000; - m_uiSummonPhase = 0; - GolemsShattered = 0; + m_uiDelay_Timer = 1000; + m_uiSummonPhase = 0; + GolemsShattered = 0; m_uiHealthAmountModifier = 1; } - InstanceScript* instance; - - GuidList m_lGolemGUIDList; - - bool m_bHasTemper; - bool m_bIsStriking; - bool m_bCanShatterGolem; - - uint8 GolemsShattered; - uint32 m_uiPause_Timer; - uint32 m_uiShatteringStomp_Timer; - uint32 m_uiShatter_Timer; - uint32 m_uiDelay_Timer; - uint32 m_uiSummonPhase; - - uint32 m_uiHealthAmountModifier; - void Reset() override { Initialize(); - + _Reset(); DespawnGolem(); m_lGolemGUIDList.clear(); - - instance->SetBossState(DATA_VOLKHAN, NOT_STARTED); + events.SetPhase(PHASE_INTRO); + events.ScheduleEvent(EVENT_FORGE_CAST, 2 * IN_MILLISECONDS, 0, PHASE_INTRO); } void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); - - instance->SetBossState(DATA_VOLKHAN, IN_PROGRESS); + events.SetPhase(PHASE_NORMAL); + events.ScheduleEvent(EVENT_PAUSE, 3.5 * IN_MILLISECONDS, 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SHATTERING_STOMP, 0 * IN_MILLISECONDS, 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_SHATTER, 5 * IN_MILLISECONDS, 0, PHASE_NORMAL); + _EnterCombat(); } void AttackStart(Unit* who) override @@ -150,7 +147,7 @@ public: Talk(SAY_DEATH); DespawnGolem(); - instance->SetBossState(DATA_VOLKHAN, DONE); + _JustDied(); } void KilledUnit(Unit* who) override @@ -224,59 +221,61 @@ public: return 0; } - void UpdateAI(uint32 uiDiff) override + void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) + // Return since we have no target and are in CombatPhase + if (events.IsInPhase(PHASE_NORMAL) && !UpdateVictim()) return; - if (m_bIsStriking) - { - if (m_uiPause_Timer <= uiDiff) - { - if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) - if (me->GetVictim()) - me->GetMotionMaster()->MoveChase(me->GetVictim()); - - m_bHasTemper = false; - m_bIsStriking = false; - m_uiPause_Timer = 3500; - } - else - m_uiPause_Timer -= uiDiff; + events.Update(diff); + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - } - // When to start shatter? After 60, 40 or 20% hp? - if (!m_bHasTemper && m_uiHealthAmountModifier >= 3) + while (uint32 eventId = events.ExecuteEvent()) { - if (m_uiShatteringStomp_Timer <= uiDiff) + switch (eventId) { - // Should he stomp even if he has no brittle golem to shatter? - Talk(SAY_STOMP); - - DoCast(me, SPELL_SHATTERING_STOMP); + case EVENT_PAUSE: + if (m_bIsStriking) + { + if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() != CHASE_MOTION_TYPE) + if (me->GetVictim()) + me->GetMotionMaster()->MoveChase(me->GetVictim()); - Talk(EMOTE_SHATTER); + m_bHasTemper = false; + m_bIsStriking = false; + events.ScheduleEvent(EVENT_PAUSE, 3.5 * IN_MILLISECONDS, 0, PHASE_NORMAL); + } + break; + case EVENT_SHATTERING_STOMP: + if (!m_bHasTemper && m_uiHealthAmountModifier >= 3) + { + // Should he stomp even if he has no brittle golem to shatter? + Talk(SAY_STOMP); - m_uiShatteringStomp_Timer = 30000; - m_bCanShatterGolem = true; - } - else - m_uiShatteringStomp_Timer -= uiDiff; - } + DoCast(me, SPELL_SHATTERING_STOMP); - // Shatter Golems 3 seconds after Shattering Stomp - if (m_bCanShatterGolem) - { - if (m_uiShatter_Timer <= uiDiff) - { - ShatterGolem(); - m_uiShatter_Timer = 3000; - m_bCanShatterGolem = false; + Talk(EMOTE_SHATTER); + events.ScheduleEvent(EVENT_SHATTERING_STOMP, 30 * IN_MILLISECONDS, 0, PHASE_NORMAL); + m_bCanShatterGolem = true; + } + break; + case EVENT_SHATTER: + if (m_bCanShatterGolem) + { + ShatterGolem(); + events.ScheduleEvent(EVENT_SHATTER, 3 * IN_MILLISECONDS, 0, PHASE_NORMAL); + m_bCanShatterGolem = false; + } + break; + case EVENT_FORGE_CAST: + DoCast(me, SPELL_FORGE_VISUAL); + events.ScheduleEvent(EVENT_FORGE_CAST, 15 * IN_MILLISECONDS, 0, PHASE_INTRO); + break; + default: + break; } - else - m_uiShatter_Timer -= uiDiff; } // Health check @@ -302,12 +301,10 @@ public: me->GetMotionMaster()->MoveTargetedHome(); m_uiSummonPhase = 2; // Set Next Phase break; - case 2: // 2 - Check if reached Anvil // This is handled in: void JustReachedHome() override break; - case 3: // 3 - Cast Temper on the Anvil if (Unit* target = GetClosestCreatureWithEntry(me, NPC_VOLKHAN_ANVIL, 1000.0f, true)) @@ -319,10 +316,9 @@ public: m_uiDelay_Timer = 1000; // Delay 2 seconds before next phase can begin m_uiSummonPhase = 4; // Set Next Phase break; - case 4: // 4 - Wait for delay to expire - if (m_uiDelay_Timer <= uiDiff) + if (m_uiDelay_Timer <= diff) { if (Unit* target = SelectTarget(SELECT_TARGET_TOPAGGRO, 0)) { @@ -333,9 +329,8 @@ public: m_uiSummonPhase = 5; } else - m_uiDelay_Timer -= uiDiff; + m_uiDelay_Timer -= diff; break; - case 5: // 5 - Spawn the Golems if (Creature* creatureTarget = GetClosestCreatureWithEntry(me, NPC_VOLKHAN_ANVIL, 1000.0f, true)) @@ -349,13 +344,30 @@ public: DoMeleeAttackIfReady(); } + + private: + GuidList m_lGolemGUIDList; + uint32 m_uiHealthAmountModifier; + uint8 GolemsShattered; + uint32 m_uiDelay_Timer; + uint32 m_uiSummonPhase; + + bool m_bHasTemper; + bool m_bIsStriking; + bool m_bCanShatterGolem; }; + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_volkhanAI>(creature); + } + }; /*###### ## npc_molten_golem ######*/ + class npc_molten_golem : public CreatureScript { public: @@ -376,18 +388,12 @@ public: void Initialize() { m_bIsFrozen = false; - - m_uiBlast_Timer = 20000; - m_uiDeathDelay_Timer = 0; - m_uiImmolation_Timer = 5000; + events.ScheduleEvent(EVENT_BLAST, 20 * IN_MILLISECONDS); + events.ScheduleEvent(EVENT_IMMOLATION, 5 * IN_MILLISECONDS); } bool m_bIsFrozen; - uint32 m_uiBlast_Timer; - uint32 m_uiDeathDelay_Timer; - uint32 m_uiImmolation_Timer; - void Reset() override { Initialize(); @@ -433,30 +439,39 @@ public: me->DespawnOrUnsummon(); } - void UpdateAI(uint32 uiDiff) override + void UpdateAI(uint32 diff) override { // Return since we have no target or if we are frozen if (!UpdateVictim() || m_bIsFrozen) return; - if (m_uiBlast_Timer <= uiDiff) - { - DoCast(me, SPELL_BLAST_WAVE); - m_uiBlast_Timer = 20000; - } - else - m_uiBlast_Timer -= uiDiff; + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - if (m_uiImmolation_Timer <= uiDiff) + while (uint32 eventId = events.ExecuteEvent()) { - DoCastVictim(SPELL_IMMOLATION_STRIKE); - m_uiImmolation_Timer = 5000; + switch (eventId) + { + case EVENT_BLAST: + DoCast(me, SPELL_BLAST_WAVE); + events.ScheduleEvent(EVENT_BLAST, 20 * IN_MILLISECONDS); + break; + case EVENT_IMMOLATION: + DoCastVictim(SPELL_IMMOLATION_STRIKE); + events.ScheduleEvent(EVENT_BLAST, 5 * IN_MILLISECONDS); + break; + default: + break; + } } - else - m_uiImmolation_Timer -= uiDiff; DoMeleeAttackIfReady(); } + + private: + EventMap events; }; }; diff --git a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp index f44170870cd..1043c1db29a 100644 --- a/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp +++ b/src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp @@ -26,9 +26,6 @@ enum Spells SPELL_SUMMON_VOID_SENTRY = 54369, SPELL_VOID_SHIFT = 54361, H_SPELL_VOID_SHIFT = 59743, - - SPELL_ZURAMAT_ADD_2 = 54342, - H_SPELL_ZURAMAT_ADD_2 = 59747 }; enum Creatures @@ -191,14 +188,6 @@ public: Talk(SAY_SLAY); } - - void JustSummoned(Creature* summon) override - { - summon->AI()->AttackStart(me->GetVictim()); - summon->CastSpell((Unit*)NULL, SPELL_ZURAMAT_ADD_2); - summon->SetInPhase(169, true, true); // Normal phase - summon->SetInPhase(173, true, true); // Void phase - } }; }; diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index 728b164cc04..e4369f0348d 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -93,15 +93,12 @@ enum Factions FACTION_COMBAT = 1868 }; -enum SetData +enum Actions { - SETDATA_DATA = 1, - SETDATA_RESET = 1, - SETDATA_CHANNELER_DIED = 2, - SETDATA_START_SPAWNING = 3, - SETDATA_STOP_SPAWNING = 4, - SETDATA_DESPAWN_ALL_SPAWNS = 5, - SETDATA_START_ATTACK_AKAMA = 6 + ACTION_CHANNELER_DIED = 1, + ACTION_START_SPAWNING = 2, + ACTION_STOP_SPAWNING = 3, + ACTION_DESPAWN_ALL_SPAWNS = 4, }; enum Events @@ -158,12 +155,11 @@ class boss_shade_of_akama : public CreatureScript public: boss_shade_of_akama() : CreatureScript("boss_shade_of_akama") { } - struct boss_shade_of_akamaAI : public ScriptedAI + struct boss_shade_of_akamaAI : public BossAI { - boss_shade_of_akamaAI(Creature* creature) : ScriptedAI(creature) + boss_shade_of_akamaAI(Creature* creature) : BossAI(creature, DATA_SHADE_OF_AKAMA) { Initialize(); - instance = creature->GetInstanceScript(); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } @@ -177,6 +173,7 @@ public: void Reset() override { + _Reset(); if (!HasKilledAkamaAndReseting) { for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) @@ -185,7 +182,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_DESPAWN_ALL_SPAWNS); + Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 3000); events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 5000); @@ -197,11 +194,6 @@ public: Initialize(); } - void JustDied(Unit* /*killer*/) override - { - instance->SetBossState(DATA_SHADE_OF_AKAMA, DONE); - } - void EnterCombat(Unit* /*who*/) override { } void AttackStart(Unit* who) override @@ -216,9 +208,9 @@ public: ScriptedAI::AttackStart(who); } - void SetData(uint32 data, uint32 value) override + void DoAction(int32 actionId) override { - if (data == SETDATA_DATA && value == SETDATA_CHANNELER_DIED) + if (actionId == ACTION_CHANNELER_DIED) me->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); UpdateSpeed(); @@ -314,7 +306,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_START_SPAWNING); + Spawner->AI()->DoAction(ACTION_START_SPAWNING); break; } case EVENT_START_ATTACK_AKAMA: @@ -353,7 +345,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_DESPAWN_ALL_SPAWNS); + Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 10000); events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 20000); @@ -378,7 +370,7 @@ public: for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->SetData(SETDATA_DATA, SETDATA_STOP_SPAWNING); + Spawner->AI()->DoAction(ACTION_STOP_SPAWNING); } } } @@ -390,8 +382,6 @@ public: public: bool HasKilledAkama; private: - InstanceScript* instance; - EventMap events; GuidList Channelers; GuidList Spawners; bool akamaReached; @@ -433,9 +423,11 @@ public: void Reset() override { me->setFaction(FACTION_FRIENDLY); - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); DoCast(me, SPELL_STEALTH); Initialize(); + + if (instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } void JustDied(Unit* /*killer*/) override @@ -581,7 +573,7 @@ public: void JustDied(Unit* /*killer*/) override { if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); } void EnterCombat(Unit* /*who*/) override { } @@ -603,7 +595,7 @@ public: else { me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); } } events.ScheduleEvent(EVENT_CHANNEL, 2000); @@ -663,36 +655,33 @@ public: Summons.Summon(summon); } - void SetData(uint32 data, uint32 value) override + void DoAction(int32 actionId) override { - if (data == SETDATA_DATA) - { - doSpawning = true; + doSpawning = true; - switch (value) - { - case SETDATA_START_SPAWNING: - if (leftSide) - { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000)); - } - else - { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000)); - } - break; - case SETDATA_STOP_SPAWNING: - doSpawning = false; - break; - case SETDATA_DESPAWN_ALL_SPAWNS: - doSpawning = false; - Summons.DespawnAll(); - break; - default: - break; - } + switch (actionId) + { + case ACTION_START_SPAWNING: + if (leftSide) + { + events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100); + events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000)); + } + else + { + events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000); + events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000)); + } + break; + case ACTION_STOP_SPAWNING: + doSpawning = false; + break; + case ACTION_DESPAWN_ALL_SPAWNS: + doSpawning = false; + Summons.DespawnAll(); + break; + default: + break; } } @@ -778,23 +767,16 @@ public: } } - summonerGuid.Clear(); Initialize(); } void JustDied(Unit* /*killer*/) override { if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { } void AttackStart(Unit* who) override @@ -824,7 +806,7 @@ public: else { me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->SetData(SETDATA_DATA, SETDATA_CHANNELER_DIED); + Shade->AI()->DoAction(ACTION_CHANNELER_DIED); switchToCombat = true; if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); @@ -855,7 +837,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; bool startedBanishing; bool switchToCombat; }; @@ -884,8 +865,6 @@ public: void Reset() override { - summonerGuid.Clear(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); } @@ -895,12 +874,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_HEROIC_STRIKE, 5000); @@ -947,7 +920,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; }; CreatureAI* GetAI(Creature* creature) const override @@ -974,8 +946,6 @@ public: void Reset() override { - summonerGuid.Clear(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); } @@ -985,12 +955,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(500, 2000)); @@ -1027,7 +991,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; }; CreatureAI* GetAI(Creature* creature) const override @@ -1054,8 +1017,6 @@ public: void Reset() override { - summonerGuid.Clear(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) AttackStart(target); } @@ -1065,12 +1026,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 18000); @@ -1107,7 +1062,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; }; CreatureAI* GetAI(Creature* creature) const override @@ -1137,7 +1091,6 @@ public: { spiritMend = false; chainHeal = false; - summonerGuid.Clear(); } void Reset() override @@ -1153,12 +1106,6 @@ public: me->DespawnOrUnsummon(5000); } - void IsSummonedBy(Unit* /*summoner*/) override - { - if (Creature* summoner = (ObjectAccessor::GetCreature((*me), summonerGuid))) - ENSURE_AI(npc_creature_generator_akama::npc_creature_generator_akamaAI, summoner->AI())->JustSummoned(me); - } - void EnterCombat(Unit* /*who*/) override { events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (5000, 6000)); @@ -1208,7 +1155,6 @@ public: private: InstanceScript* instance; EventMap events; - ObjectGuid summonerGuid; bool spiritMend; bool chainHeal; }; diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp index 2407f3980de..9ffc151b3b9 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp @@ -338,6 +338,13 @@ class npc_warden_mellichar : public CreatureScript IsRunning = true; } + void JustSummoned(Creature* summon) override + { + DoZoneInCombat(summon); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true)) + summon->AI()->AttackStart(target); + } + bool CanProgress() { if (Phase == 7 && instance->GetData(DATA_WARDEN_4) == DONE) diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 9edf3f42b88..98c6781e866 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -25,6 +25,7 @@ #include "ScriptMgr.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "Containers.h" enum DeathKnightSpells @@ -148,7 +149,7 @@ class spell_dk_anti_magic_shell : public SpellScriptLoader { // Cannot reduce cooldown by more than 50% int32 val = std::min(glyph->GetAmount(), int32(absorbedAmount) * 100 / maxHealth); - player->ModifySpellCooldown(GetId(), -int32(player->GetSpellCooldownDelay(GetId()) * val / 100)); + player->GetSpellHistory()->ModifyCooldown(GetId(), -int32(player->GetSpellHistory()->GetRemainingCooldown(GetId()) * val / 100)); } } diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index dd002dedee5..2418e75d22c 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -25,6 +25,7 @@ #include "ScriptMgr.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "Containers.h" enum DruidSpells @@ -122,8 +123,8 @@ class spell_dru_eclipse : public SpellScriptLoader if (!caster) return; - if (caster->ToPlayer()->GetAuraOfRankedSpell(SPELL_DRUID_NATURES_GRACE)) - caster->ToPlayer()->RemoveSpellCooldown(SPELL_DRUID_NATURES_GRACE_TRIGGER, true); + if (caster->GetAuraOfRankedSpell(SPELL_DRUID_NATURES_GRACE)) + caster->GetSpellHistory()->ResetCooldown(SPELL_DRUID_NATURES_GRACE_TRIGGER, true); } void Register() override diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index a05eab1cd08..31779c4df22 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -36,6 +36,7 @@ #include "SkillDiscovery.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "Vehicle.h" class spell_gen_absorb0_hitlimit1 : public SpellScriptLoader @@ -1313,9 +1314,7 @@ class spell_gen_divine_storm_cd_reset : public SpellScriptLoader void HandleScript(SpellEffIndex /*effIndex*/) { - Player* caster = GetCaster()->ToPlayer(); - if (caster->HasSpellCooldown(SPELL_DIVINE_STORM)) - caster->RemoveSpellCooldown(SPELL_DIVINE_STORM, true); + GetCaster()->GetSpellHistory()->ResetCooldown(SPELL_DIVINE_STORM, true); } void Register() override @@ -3324,7 +3323,7 @@ class spell_pvp_trinket_wotf_shared_cd : public SpellScriptLoader { // This is only needed because spells cast from spell_linked_spell are triggered by default // Spell::SendSpellCooldown() skips all spells with TRIGGERED_IGNORE_SPELL_AND_CATEGORY_CD - GetCaster()->ToPlayer()->AddSpellAndCategoryCooldowns(GetSpellInfo(), GetCastItem() ? GetCastItem()->GetEntry() : 0, GetSpell()); + GetCaster()->GetSpellHistory()->StartCooldown(GetSpellInfo(), 0, GetSpell()); } void Register() override diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 205965910c1..a0c01479cdb 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -29,6 +29,7 @@ #include "GridNotifiersImpl.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" enum HunterSpells { @@ -641,24 +642,20 @@ class spell_hun_readiness : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - Player* caster = GetCaster()->ToPlayer(); // immediately finishes the cooldown on your other Hunter abilities except Bestial Wrath - const SpellCooldowns& cm = caster->ToPlayer()->GetSpellCooldownMap(); - for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) + GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) { - SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(itr->first); + SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); ///! If spellId in cooldown map isn't valid, the above will return a null pointer. - if (spellInfo && - spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && + if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != SPELL_HUNTER_READINESS && spellInfo->Id != SPELL_HUNTER_BESTIAL_WRATH && spellInfo->Id != SPELL_DRAENEI_GIFT_OF_THE_NAARU && spellInfo->GetRecoveryTime() > 0) - caster->RemoveSpellCooldown((itr++)->first, true); - else - ++itr; - } + return true; + return false; + }, true); } void Register() override diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index eb5c4c147a8..66c90c85e55 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -26,6 +26,7 @@ #include "ScriptedCreature.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "SkillDiscovery.h" #include "Battleground.h" @@ -1355,7 +1356,7 @@ class spell_item_red_rider_air_rifle : public SpellScriptLoader caster->CastSpell(caster, SPELL_AIR_RIFLE_HOLD_VISUAL, true); // needed because this spell shares GCD with its triggered spells (which must not be cast with triggered flag) if (Player* player = caster->ToPlayer()) - player->GetGlobalCooldownMgr().CancelGlobalCooldown(GetSpellInfo()); + player->GetSpellHistory()->CancelGlobalCooldown(GetSpellInfo()); if (urand(0, 4)) caster->CastSpell(target, SPELL_AIR_RIFLE_SHOOT, false); else @@ -2374,7 +2375,7 @@ class spell_item_rocket_boots : public SpellScriptLoader if (Battleground* bg = caster->GetBattleground()) bg->EventPlayerDroppedFlag(caster); - caster->RemoveSpellCooldown(SPELL_ROCKET_BOOTS_PROC); + caster->GetSpellHistory()->ResetCooldown(SPELL_ROCKET_BOOTS_PROC); caster->CastSpell(caster, SPELL_ROCKET_BOOTS_PROC, true, NULL); } diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index ea8630acc59..16e819430d2 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -24,6 +24,7 @@ #include "Player.h" #include "ScriptMgr.h" #include "SpellScript.h" +#include "SpellHistory.h" #include "SpellAuraEffects.h" #include "Pet.h" #include "GridNotifiers.h" @@ -326,22 +327,12 @@ class spell_mage_cold_snap : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - Player* caster = GetCaster()->ToPlayer(); - // immediately finishes the cooldown on Frost spells - const SpellCooldowns& cm = caster->GetSpellCooldownMap(); - for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) + GetCaster()->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); - - if (spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && - (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) && - spellInfo->Id != SPELL_MAGE_COLD_SNAP && spellInfo->GetRecoveryTime() > 0) - { - caster->RemoveSpellCooldown((itr++)->first, true); - } - else - ++itr; - } + return spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (spellInfo->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) && + spellInfo->Id != SPELL_MAGE_COLD_SNAP && spellInfo->GetRecoveryTime() > 0; + }, true); } void Register() override @@ -703,7 +694,7 @@ class spell_mage_glyph_of_ice_block : public SpellScriptLoader { PreventDefaultAction(); // Remove Frost Nova cooldown - GetTarget()->ToPlayer()->RemoveSpellCooldown(SPELL_MAGE_FROST_NOVA, true); + GetTarget()->GetSpellHistory()->ResetCooldown(SPELL_MAGE_FROST_NOVA, true); } void Register() override diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index 305f1cce9e3..2af1b8c14f2 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -25,6 +25,7 @@ #include "ScriptMgr.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "Group.h" enum PaladinSpells @@ -600,7 +601,7 @@ class spell_pal_grand_crusader : public SpellScriptLoader void HandleEffectProc(AuraEffect const* /*aurEff*/, ProcEventInfo& /*eventInfo*/) { - GetTarget()->ToPlayer()->RemoveSpellCooldown(SPELL_PALADIN_AVENGERS_SHIELD, true); + GetTarget()->GetSpellHistory()->ResetCooldown(SPELL_PALADIN_AVENGERS_SHIELD, true); } void Register() override diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index 902e89f6c56..b70ae177928 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -25,6 +25,7 @@ #include "ScriptMgr.h" #include "SpellScript.h" #include "SpellAuraEffects.h" +#include "SpellHistory.h" #include "Containers.h" enum RogueSpells @@ -159,11 +160,11 @@ class spell_rog_cheat_death : public SpellScriptLoader void Absorb(AuraEffect* /*aurEff*/, DamageInfo & dmgInfo, uint32 & absorbAmount) { Player* target = GetTarget()->ToPlayer(); - if (dmgInfo.GetDamage() < target->GetHealth() || target->HasSpellCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN) || !roll_chance_i(absorbChance)) + if (dmgInfo.GetDamage() < target->GetHealth() || target->GetSpellHistory()->HasCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN) || !roll_chance_i(absorbChance)) return; target->CastSpell(target, SPELL_ROGUE_CHEAT_DEATH_COOLDOWN, true); - target->AddSpellCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN, 0, time(NULL) + 60); + target->GetSpellHistory()->AddCooldown(SPELL_ROGUE_CHEAT_DEATH_COOLDOWN, 0, std::chrono::minutes(1)); uint32 health10 = target->CountPctFromMaxHealth(10); @@ -550,31 +551,21 @@ class spell_rog_preparation : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - Player* caster = GetCaster()->ToPlayer(); - - // immediately finishes the cooldown on certain Rogue abilities - SpellCooldowns const& cm = caster->GetSpellCooldownMap(); - for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();) + Unit* caster = GetCaster(); + caster->GetSpellHistory()->ResetCooldowns([caster](SpellHistory::CooldownStorageType::iterator itr) { SpellInfo const* spellInfo = sSpellMgr->AssertSpellInfo(itr->first); if (spellInfo->SpellFamilyName != SPELLFAMILY_ROGUE) - { - ++itr; - continue; - } + return false; - if ((spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_SHADOWSTEP || // Shadowstep + return (spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_SHADOWSTEP || // Shadowstep spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG0_ROGUE_VAN_SPRINT) || // Vanish, Sprint // Glyph of Preparation (caster->HasAura(SPELL_ROGUE_GLYPH_OF_PREPARATION) && (spellInfo->SpellFamilyFlags[1] & SPELLFAMILYFLAG1_ROGUE_DISMANTLE_SMOKE_BOMB || // Dismantle, Smoke Bomb - spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG0_ROGUE_KICK))) // Kick - { - caster->RemoveSpellCooldown((itr++)->first, true); - } - else - ++itr; - } + spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG0_ROGUE_KICK)); // Kick + + }, true); } void Register() override diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index b5c9c23fa5c..589f67fc39b 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -26,6 +26,7 @@ #include "GridNotifiers.h" #include "Unit.h" #include "SpellScript.h" +#include "SpellHistory.h" #include "SpellAuraEffects.h" enum ShamanSpells @@ -310,7 +311,7 @@ class spell_sha_earth_shield : public SpellScriptLoader { //! HACK due to currenct proc system implementation if (Player* player = GetTarget()->ToPlayer()) - if (player->HasSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL)) + if (player->GetSpellHistory()->HasCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL)) return false; return true; } @@ -323,7 +324,7 @@ class spell_sha_earth_shield : public SpellScriptLoader /// @hack: due to currenct proc system implementation if (Player* player = GetTarget()->ToPlayer()) - player->AddSpellCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL, 0, time(NULL) + 3); + player->GetSpellHistory()->AddCooldown(SPELL_SHAMAN_EARTH_SHIELD_HEAL, 0, std::chrono::seconds(3)); } void Register() override @@ -462,7 +463,7 @@ class spell_sha_feedback : public SpellScriptLoader { PreventDefaultAction(); // will prevent default effect execution if (Player* target = GetTarget()->ToPlayer()) - target->ModifySpellCooldown(SPELL_SHAMAN_ELEMENTAL_MASTERY, aurEff->GetBaseAmount()); + target->GetSpellHistory()->ModifyCooldown(SPELL_SHAMAN_ELEMENTAL_MASTERY, aurEff->GetBaseAmount()); } void Register() override @@ -832,7 +833,7 @@ class spell_sha_item_t10_elemental_2p_bonus : public SpellScriptLoader { PreventDefaultAction(); if (Player* target = GetTarget()->ToPlayer()) - target->ModifySpellCooldown(SPELL_SHAMAN_ELEMENTAL_MASTERY, -aurEff->GetAmount()); + target->GetSpellHistory()->ModifyCooldown(SPELL_SHAMAN_ELEMENTAL_MASTERY, -aurEff->GetAmount()); } void Register() override @@ -949,7 +950,7 @@ class spell_sha_lava_surge_proc : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { - GetCaster()->ToPlayer()->RemoveSpellCooldown(SPELL_SHAMAN_LAVA_BURST, true); + GetCaster()->GetSpellHistory()->ResetCooldown(SPELL_SHAMAN_LAVA_BURST, true); } void Register() override @@ -1017,7 +1018,7 @@ class spell_sha_nature_guardian : public SpellScriptLoader { //! HACK due to currenct proc system implementation if (Player* player = GetTarget()->ToPlayer()) - if (player->HasSpellCooldown(GetSpellInfo()->Id)) + if (player->GetSpellHistory()->HasCooldown(GetSpellInfo()->Id)) return false; return GetTarget()->HealthBelowPctDamaged(30, eventInfo.GetDamageInfo()->GetDamage()); @@ -1034,7 +1035,7 @@ class spell_sha_nature_guardian : public SpellScriptLoader eventInfo.GetProcTarget()->getThreatManager().modifyThreatPercent(GetTarget(), -10); if (Player* player = GetTarget()->ToPlayer()) - player->AddSpellCooldown(GetSpellInfo()->Id, 0, time(NULL) + aurEff->GetSpellInfo()->GetEffect(EFFECT_1)->CalcValue()); + player->GetSpellHistory()->AddCooldown(GetSpellInfo()->Id, 0, std::chrono::seconds(aurEff->GetSpellInfo()->GetEffect(EFFECT_1)->CalcValue())); } void Register() override diff --git a/src/server/scripts/Spells/spell_warrior.cpp b/src/server/scripts/Spells/spell_warrior.cpp index b3caff679df..80e738c7442 100644 --- a/src/server/scripts/Spells/spell_warrior.cpp +++ b/src/server/scripts/Spells/spell_warrior.cpp @@ -23,6 +23,7 @@ #include "Player.h" #include "ScriptMgr.h" +#include "SpellHistory.h" #include "SpellScript.h" #include "SpellAuraEffects.h" @@ -697,7 +698,7 @@ class spell_warr_sudden_death : public SpellScriptLoader { // Remove cooldown on Colossus Smash if (Player* player = GetTarget()->ToPlayer()) - player->RemoveSpellCooldown(SPELL_WARRIOR_COLOSSUS_SMASH, true); + player->GetSpellHistory()->ResetCooldown(SPELL_WARRIOR_COLOSSUS_SMASH, true); } void Register() override @@ -799,7 +800,7 @@ class spell_warr_sword_and_board : public SpellScriptLoader { // Remove cooldown on Shield Slam if (Player* player = GetTarget()->ToPlayer()) - player->RemoveSpellCooldown(SPELL_WARRIOR_SHIELD_SLAM, true); + player->GetSpellHistory()->ResetCooldown(SPELL_WARRIOR_SHIELD_SLAM, true); } void Register() override @@ -932,7 +933,7 @@ class spell_warr_vigilance_trigger : public SpellScriptLoader // Remove Taunt cooldown if (Player* target = GetHitPlayer()) - target->RemoveSpellCooldown(SPELL_WARRIOR_TAUNT, true); + target->GetSpellHistory()->ResetCooldown(SPELL_WARRIOR_TAUNT, true); } void Register() override diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index d4a73ab161f..121417717a4 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -56,6 +56,7 @@ EndContentData */ #include "SpellAuras.h" #include "Pet.h" #include "CreatureTextMgr.h" +#include "SpellHistory.h" /*######## # npc_air_force_bots @@ -1208,14 +1209,14 @@ public: if (creature->IsQuestGiver()) player->PrepareQuestMenu(creature->GetGUID()); - if (player->HasSpellCooldown(SPELL_INT) || - player->HasSpellCooldown(SPELL_ARM) || - player->HasSpellCooldown(SPELL_DMG) || - player->HasSpellCooldown(SPELL_RES) || - player->HasSpellCooldown(SPELL_STR) || - player->HasSpellCooldown(SPELL_AGI) || - player->HasSpellCooldown(SPELL_STM) || - player->HasSpellCooldown(SPELL_SPI)) + if (player->GetSpellHistory()->HasCooldown(SPELL_INT) || + player->GetSpellHistory()->HasCooldown(SPELL_ARM) || + player->GetSpellHistory()->HasCooldown(SPELL_DMG) || + player->GetSpellHistory()->HasCooldown(SPELL_RES) || + player->GetSpellHistory()->HasCooldown(SPELL_STR) || + player->GetSpellHistory()->HasCooldown(SPELL_AGI) || + player->GetSpellHistory()->HasCooldown(SPELL_STM) || + player->GetSpellHistory()->HasCooldown(SPELL_SPI)) player->SEND_GOSSIP_MENU(7393, creature->GetGUID()); else { @@ -1282,42 +1283,42 @@ public: break; case GOSSIP_SENDER_MAIN + 1: creature->CastSpell(player, SPELL_DMG, false); - player->AddSpellCooldown(SPELL_DMG, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_DMG, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 2: creature->CastSpell(player, SPELL_RES, false); - player->AddSpellCooldown(SPELL_RES, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_RES, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 3: creature->CastSpell(player, SPELL_ARM, false); - player->AddSpellCooldown(SPELL_ARM, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_ARM, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 4: creature->CastSpell(player, SPELL_SPI, false); - player->AddSpellCooldown(SPELL_SPI, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_SPI, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 5: creature->CastSpell(player, SPELL_INT, false); - player->AddSpellCooldown(SPELL_INT, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_INT, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 6: creature->CastSpell(player, SPELL_STM, false); - player->AddSpellCooldown(SPELL_STM, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_STM, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 7: creature->CastSpell(player, SPELL_STR, false); - player->AddSpellCooldown(SPELL_STR, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_STR, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 8: creature->CastSpell(player, SPELL_AGI, false); - player->AddSpellCooldown(SPELL_AGI, 0, time(NULL) + 7200); + player->GetSpellHistory()->AddCooldown(SPELL_AGI, 0, std::chrono::hours(2)); SendAction(player, creature, action); break; } diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.cpp b/src/server/shared/Database/Implementation/CharacterDatabase.cpp index 4cd4803997c..e95b05bc418 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/shared/Database/Implementation/CharacterDatabase.cpp @@ -112,7 +112,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_MAIL_COUNT, "SELECT COUNT(*) FROM mail WHERE receiver = ?", CONNECTION_SYNCH); 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 FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, "SELECT spell, item, time FROM character_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_SPELL_CHARGES, "SELECT categoryId, rechargeStart, rechargeEnd FROM character_spell_charges WHERE guid = ? AND rechargeEnd > UNIX_TIMESTAMP() ORDER BY rechargeEnd", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_DECLINEDNAMES, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_declinedname WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GUILD_MEMBER, "SELECT guildid, rank FROM guild_member WHERE guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_SEL_GUILD_MEMBER_EXTENDED, "SELECT g.guildid, g.name, gr.rname, gr.rid, gm.pnote, gm.offnote " @@ -511,7 +512,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_CHAR_REP_FACTION_CHANGE, "UPDATE character_reputation SET faction = ?, standing = ? WHERE faction = ? AND guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET knownTitles = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_RES_CHAR_TITLES_FACTION_CHANGE, "UPDATE characters SET chosenTitle = 0 WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CHAR_SPELL_COOLDOWN, "DELETE FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHAR_SPELL_COOLDOWNS, "DELETE FROM character_spell_cooldown WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_SPELL_COOLDOWN, "INSERT INTO character_spell_cooldown (guid, spell, item, time) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_CHAR_SPELL_CHARGES, "DELETE FROM character_spell_charges WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_SPELL_CHARGES, "INSERT INTO character_spell_charges (guid, categoryId, rechargeStart, rechargeEnd) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER, "DELETE FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_ACTION, "DELETE FROM character_action WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_AURA, "DELETE FROM character_aura WHERE guid = ?", CONNECTION_ASYNC); @@ -608,13 +612,16 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_PET_AURA, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, maxDuration, remainTime, remainCharges FROM pet_aura WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_AURA_EFFECT, "SELECT casterGuid, spell, effectMask, effectIndex, amount, baseAmount FROM pet_aura_effect WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_SPELL, "SELECT spell, active FROM pet_spell WHERE guid = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_PET_SPELL_COOLDOWN, "SELECT spell, time FROM pet_spell_cooldown WHERE guid = ? AND time > UNIX_TIMESTAMP()", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_PET_DECLINED_NAME, "SELECT genitive, dative, accusative, instrumental, prepositional FROM character_pet_declinedname WHERE owner = ? AND id = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_PET_AURAS, "DELETE FROM pet_aura WHERE guid = ?", CONNECTION_BOTH); PrepareStatement(CHAR_DEL_PET_AURA_EFFECTS, "DELETE FROM pet_aura_effect WHERE guid = ?", CONNECTION_BOTH); 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_BOTH); PrepareStatement(CHAR_INS_PET_SPELL_COOLDOWN, "INSERT INTO pet_spell_cooldown (guid, spell, time) VALUES (?, ?, ?)", CONNECTION_BOTH); + PrepareStatement(CHAR_SEL_PET_SPELL_CHARGES, "SELECT categoryId, rechargeStart, rechargeEnd FROM pet_spell_charges WHERE guid = ? AND rechargeEnd > UNIX_TIMESTAMP() ORDER BY rechargeEnd", CONNECTION_SYNCH); + PrepareStatement(CHAR_DEL_PET_SPELL_CHARGES, "DELETE FROM pet_spell_charges WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_PET_SPELL_CHARGES, "INSERT INTO pet_spell_charges (guid, categoryId, rechargeStart, rechargeEnd) 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_BOTH); PrepareStatement(CHAR_INS_PET_AURA, "INSERT INTO pet_aura (guid, casterGuid, spell, effectMask, recalculateMask, stackCount, maxDuration, remainTime, remainCharges) " diff --git a/src/server/shared/Database/Implementation/CharacterDatabase.h b/src/server/shared/Database/Implementation/CharacterDatabase.h index 3c232c52a6d..045a4c1fde6 100644 --- a/src/server/shared/Database/Implementation/CharacterDatabase.h +++ b/src/server/shared/Database/Implementation/CharacterDatabase.h @@ -111,6 +111,7 @@ enum CharacterDatabaseStatements CHAR_SEL_CHARACTER_SOCIALLIST, CHAR_SEL_CHARACTER_HOMEBIND, CHAR_SEL_CHARACTER_SPELLCOOLDOWNS, + CHAR_SEL_CHARACTER_SPELL_CHARGES, CHAR_SEL_CHARACTER_DECLINEDNAMES, CHAR_SEL_GUILD_MEMBER, CHAR_SEL_GUILD_MEMBER_EXTENDED, @@ -441,7 +442,10 @@ enum CharacterDatabaseStatements CHAR_UPD_CHAR_REP_FACTION_CHANGE, CHAR_UPD_CHAR_TITLES_FACTION_CHANGE, CHAR_RES_CHAR_TITLES_FACTION_CHANGE, - CHAR_DEL_CHAR_SPELL_COOLDOWN, + CHAR_DEL_CHAR_SPELL_COOLDOWNS, + CHAR_INS_CHAR_SPELL_COOLDOWN, + CHAR_DEL_CHAR_SPELL_CHARGES, + CHAR_INS_CHAR_SPELL_CHARGES, CHAR_DEL_CHARACTER, CHAR_DEL_CHAR_ACTION, CHAR_DEL_CHAR_AURA, @@ -518,6 +522,9 @@ enum CharacterDatabaseStatements CHAR_DEL_PET_AURA_EFFECTS, CHAR_DEL_PET_SPELL_COOLDOWNS, CHAR_INS_PET_SPELL_COOLDOWN, + CHAR_SEL_PET_SPELL_CHARGES, + CHAR_DEL_PET_SPELL_CHARGES, + CHAR_INS_PET_SPELL_CHARGES, CHAR_DEL_PET_SPELL_BY_SPELL, CHAR_INS_PET_SPELL, CHAR_INS_PET_AURA, |