aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp5
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp2
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp21
-rw-r--r--src/server/game/DataStores/DB2Stores.h3
-rw-r--r--src/server/game/DataStores/DB2Structure.h32
-rw-r--r--src/server/game/DataStores/DB2fmt.h1
-rw-r--r--src/server/game/DataStores/DBCEnums.h15
-rw-r--r--src/server/game/DataStores/DBCStores.cpp2
-rw-r--r--src/server/game/DataStores/DBCStores.h2
-rw-r--r--src/server/game/DataStores/DBCStructure.h6
-rw-r--r--src/server/game/DataStores/DBCfmt.h4
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp79
-rw-r--r--src/server/game/Entities/Creature/Creature.h10
-rw-r--r--src/server/game/Entities/Item/Item.cpp4
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h17
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp111
-rw-r--r--src/server/game/Entities/Pet/Pet.h2
-rw-r--r--src/server/game/Entities/Player/Player.cpp652
-rw-r--r--src/server/game/Entities/Player/Player.h36
-rw-r--r--src/server/game/Entities/Totem/Totem.cpp3
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp149
-rw-r--r--src/server/game/Entities/Unit/Unit.h47
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp9
-rw-r--r--src/server/game/Handlers/CalendarHandler.cpp2
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp4
-rw-r--r--src/server/game/Handlers/CombatHandler.cpp17
-rw-r--r--src/server/game/Handlers/PetHandler.cpp20
-rw-r--r--src/server/game/Handlers/PetitionsHandler.cpp13
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp4
-rw-r--r--src/server/game/Loot/LootMgr.cpp2
-rw-r--r--src/server/game/Loot/LootMgr.h8
-rw-r--r--src/server/game/Movement/MotionMaster.cpp2
-rw-r--r--src/server/game/Reputation/ReputationMgr.cpp2
-rw-r--r--src/server/game/Server/Packets/CharacterPackets.cpp2
-rw-r--r--src/server/game/Server/Packets/CombatPackets.cpp12
-rw-r--r--src/server/game/Server/Packets/CombatPackets.h8
-rw-r--r--src/server/game/Server/Packets/ItemPackets.cpp14
-rw-r--r--src/server/game/Server/Packets/NPCPackets.cpp1
-rw-r--r--src/server/game/Server/Packets/NPCPackets.h4
-rw-r--r--src/server/game/Server/Packets/SpellPackets.cpp119
-rw-r--r--src/server/game/Server/Packets/SpellPackets.h130
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp31
-rw-r--r--src/server/game/Server/Protocol/Opcodes.h67
-rw-r--r--src/server/game/Spells/Auras/SpellAuraDefines.h16
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp74
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp39
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h2
-rw-r--r--src/server/game/Spells/Spell.cpp133
-rw-r--r--src/server/game/Spells/Spell.h1
-rw-r--r--src/server/game/Spells/SpellEffects.cpp19
-rw-r--r--src/server/game/Spells/SpellHistory.cpp854
-rw-r--r--src/server/game/Spells/SpellHistory.h158
-rw-r--r--src/server/game/Spells/SpellInfo.cpp32
-rw-r--r--src/server/game/Spells/SpellInfo.h4
-rw-r--r--src/server/scripts/Commands/cs_lookup.cpp3
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp10
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp11
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_volkhan.cpp243
-rw-r--r--src/server/scripts/Northrend/VioletHold/boss_zuramat.cpp11
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp146
-rw-r--r--src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp7
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp3
-rw-r--r--src/server/scripts/Spells/spell_druid.cpp5
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp7
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp17
-rw-r--r--src/server/scripts/Spells/spell_item.cpp5
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp21
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp3
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp29
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp15
-rw-r--r--src/server/scripts/Spells/spell_warrior.cpp7
-rw-r--r--src/server/scripts/World/npcs_special.cpp33
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.cpp13
-rw-r--r--src/server/shared/Database/Implementation/CharacterDatabase.h9
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,