aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2016-06-13 21:00:25 +0200
committerShauren <shauren.trinity@gmail.com>2016-06-13 21:00:25 +0200
commitd2733eb6f1f9a550ec6511b5fa696b67b11044b3 (patch)
tree92e101dba7e48661de4a02c93bfba5700a6f1bdf /src
parent3903482eb810625fce64c616a4edca3f06975e94 (diff)
parent2fe6fc63d79655a96ee2135a6b380ce353729088 (diff)
Merge branch '6.x' of https://github.com/TrinityCore/TrinityCore into legion
Diffstat (limited to 'src')
-rw-r--r--src/common/Collision/Maps/MapTree.cpp5
-rw-r--r--src/common/Metric/Metric.cpp235
-rw-r--r--src/common/Metric/Metric.h141
-rw-r--r--src/common/Utilities/Containers.h4
-rw-r--r--src/server/bnetserver/REST/LoginRESTService.h2
-rw-r--r--src/server/bnetserver/Server/Session.cpp2
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.cpp12
-rw-r--r--src/server/database/Database/Implementation/CharacterDatabase.h3
-rw-r--r--src/server/database/Database/Implementation/LoginDatabase.cpp2
-rw-r--r--src/server/database/Updater/UpdateFetcher.cpp46
-rw-r--r--src/server/game/AI/CoreAI/GameObjectAI.h2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.h2
-rw-r--r--src/server/game/AuctionHouse/AuctionHouseMgr.cpp6
-rw-r--r--src/server/game/Battlefield/Battlefield.cpp2
-rw-r--r--src/server/game/Battlegrounds/Battleground.cpp9
-rw-r--r--src/server/game/Battlegrounds/Battleground.h4
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp13
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAB.h7
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp13
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundAV.h7
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp15
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundEY.h7
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp12
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundIC.h7
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp13
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundWS.h7
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp6
-rw-r--r--src/server/game/Conditions/ConditionMgr.h2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp26
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h2
-rw-r--r--src/server/game/Entities/Object/Object.cpp2
-rw-r--r--src/server/game/Entities/Object/ObjectGuid.h16
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp379
-rw-r--r--src/server/game/Entities/Pet/Pet.h18
-rw-r--r--src/server/game/Entities/Player/Player.cpp42
-rw-r--r--src/server/game/Entities/Player/Player.h4
-rw-r--r--src/server/game/Entities/Taxi/TaxiPathGraph.cpp5
-rw-r--r--src/server/game/Entities/Unit/StatSystem.cpp2
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp132
-rw-r--r--src/server/game/Entities/Unit/Unit.h4
-rw-r--r--src/server/game/Garrison/Garrison.cpp5
-rw-r--r--src/server/game/Globals/ObjectAccessor.cpp13
-rw-r--r--src/server/game/Globals/ObjectAccessor.h3
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp19
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp5
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp32
-rw-r--r--src/server/game/Handlers/PetHandler.cpp17
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp2
-rw-r--r--src/server/game/Maps/Map.cpp2
-rw-r--r--src/server/game/Maps/MapScripts.cpp8
-rw-r--r--src/server/game/Maps/TransportMgr.cpp2
-rw-r--r--src/server/game/Maps/ZoneScript.h2
-rw-r--r--src/server/game/Miscellaneous/Language.h2
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h264
-rw-r--r--src/server/game/Movement/PathGenerator.cpp3
-rw-r--r--src/server/game/Server/Packets/BattlenetPackets.h4
-rw-r--r--src/server/game/Server/Packets/BlackMarketPackets.h4
-rw-r--r--src/server/game/Server/Packets/GuildFinderPackets.cpp2
-rw-r--r--src/server/game/Server/Packets/MovementPackets.cpp23
-rw-r--r--src/server/game/Server/Packets/MovementPackets.h32
-rw-r--r--src/server/game/Server/Packets/PetPackets.cpp21
-rw-r--r--src/server/game/Server/Packets/PetPackets.h13
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp10
-rw-r--r--src/server/game/Server/WorldSession.cpp10
-rw-r--r--src/server/game/Server/WorldSession.h2
-rw-r--r--src/server/game/Spells/Auras/SpellAuraDefines.h31
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp107
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.h4
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp4
-rw-r--r--src/server/game/Spells/Spell.cpp24
-rw-r--r--src/server/game/Spells/SpellEffects.cpp26
-rw-r--r--src/server/game/Spells/SpellHistory.cpp25
-rw-r--r--src/server/game/Spells/SpellInfo.cpp5
-rw-r--r--src/server/game/World/World.cpp8
-rw-r--r--src/server/scripts/Commands/cs_gobject.cpp14
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp12
-rw-r--r--src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp4
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp4
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp2
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp30
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp3
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp5
-rw-r--r--src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp4
-rw-r--r--src/server/scripts/Pet/pet_mage.cpp4
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp2
-rw-r--r--src/server/scripts/World/go_scripts.cpp9
-rw-r--r--src/server/worldserver/Main.cpp11
-rw-r--r--src/server/worldserver/worldserver.conf.dist42
89 files changed, 1416 insertions, 684 deletions
diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp
index faabdbdffb8..b7b98199502 100644
--- a/src/common/Collision/Maps/MapTree.cpp
+++ b/src/common/Collision/Maps/MapTree.cpp
@@ -22,6 +22,7 @@
#include "VMapDefinitions.h"
#include "Log.h"
#include "Errors.h"
+#include "Metric.h"
#include <string>
#include <sstream>
@@ -415,6 +416,8 @@ namespace VMAP
}
else
iLoadedTiles[packTileID(tileX, tileY)] = false;
+ TC_METRIC_EVENT("map_events", "LoadMapTile",
+ "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
return result;
}
@@ -473,6 +476,8 @@ namespace VMAP
}
}
iLoadedTiles.erase(tile);
+ TC_METRIC_EVENT("map_events", "UnloadMapTile",
+ "Map: " + std::to_string(iMapID) + " TileX: " + std::to_string(tileX) + " TileY: " + std::to_string(tileY));
}
void StaticMapTree::getModelInstances(ModelInstance* &models, uint32 &count)
diff --git a/src/common/Metric/Metric.cpp b/src/common/Metric/Metric.cpp
new file mode 100644
index 00000000000..9484cebcc72
--- /dev/null
+++ b/src/common/Metric/Metric.cpp
@@ -0,0 +1,235 @@
+/*
+* Copyright (C) 2008-2016 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 "Metric.h"
+#include "Log.h"
+#include "Config.h"
+#include "Util.h"
+
+void Metric::Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger)
+{
+ _realmName = realmName;
+ _batchTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
+ _overallStatusTimer = Trinity::make_unique<boost::asio::deadline_timer>(ioService);
+ _overallStatusLogger = overallStatusLogger;
+ LoadFromConfigs();
+}
+
+bool Metric::Connect()
+{
+ _dataStream.connect(_hostname, _port);
+ auto error = _dataStream.error();
+ if (error)
+ {
+ TC_LOG_ERROR("metric", "Error connecting to '%s:%s', disabling Metric. Error message : %s",
+ _hostname.c_str(), _port.c_str(), error.message().c_str());
+ _enabled = false;
+ return false;
+ }
+ _dataStream.clear();
+ return true;
+}
+
+void Metric::LoadFromConfigs()
+{
+ bool previousValue = _enabled;
+ _enabled = sConfigMgr->GetBoolDefault("Metric.Enable", false);
+ _updateInterval = sConfigMgr->GetIntDefault("Metric.Interval", 10);
+ if (_updateInterval < 1)
+ {
+ TC_LOG_ERROR("metric", "'Metric.Interval' config set to %d, overriding to 1.", _updateInterval);
+ _updateInterval = 1;
+ }
+
+ _overallStatusTimerInterval = sConfigMgr->GetIntDefault("Metric.OverallStatusInterval", 1);
+ if (_overallStatusTimerInterval < 1)
+ {
+ TC_LOG_ERROR("metric", "'Metric.OverallStatusInterval' config set to %d, overriding to 1.", _overallStatusTimerInterval);
+ _overallStatusTimerInterval = 1;
+ }
+
+ // Schedule a send at this point only if the config changed from Disabled to Enabled.
+ // Cancel any scheduled operation if the config changed from Enabled to Disabled.
+ if (_enabled && !previousValue)
+ {
+ std::string connectionInfo = sConfigMgr->GetStringDefault("Metric.ConnectionInfo", "");
+ if (connectionInfo.empty())
+ {
+ TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' not specified in configuration file.");
+ return;
+ }
+
+ Tokenizer tokens(connectionInfo, ';');
+ if (tokens.size() != 3)
+ {
+ TC_LOG_ERROR("metric", "'Metric.ConnectionInfo' specified with wrong format in configuration file.");
+ return;
+ }
+
+ _hostname.assign(tokens[0]);
+ _port.assign(tokens[1]);
+ _databaseName.assign(tokens[2]);
+ Connect();
+
+ ScheduleSend();
+ ScheduleOverallStatusLog();
+ }
+}
+
+void Metric::Update()
+{
+ if (_overallStatusTimerTriggered)
+ {
+ _overallStatusTimerTriggered = false;
+ _overallStatusLogger();
+ }
+}
+
+void Metric::LogEvent(std::string const& category, std::string const& title, std::string const& description)
+{
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_EVENT;
+ data->Title = title;
+ data->Text = description;
+
+ _queuedData.Enqueue(data);
+}
+
+void Metric::SendBatch()
+{
+ using namespace std::chrono;
+
+ std::stringstream batchedData;
+ MetricData* data;
+ bool firstLoop = true;
+ while (_queuedData.Dequeue(data))
+ {
+ if (!firstLoop)
+ batchedData << "\n";
+
+ batchedData << data->Category;
+ if (!_realmName.empty())
+ batchedData << ",realm=" << _realmName;
+
+ batchedData << " ";
+
+ switch (data->Type)
+ {
+ case METRIC_DATA_VALUE:
+ batchedData << "value=" << data->Value;
+ break;
+ case METRIC_DATA_EVENT:
+ batchedData << "title=\"" << data->Title << "\",text=\"" << data->Text << "\"";
+ break;
+ }
+
+ batchedData << " ";
+
+ batchedData << std::to_string(duration_cast<nanoseconds>(data->Timestamp.time_since_epoch()).count());
+
+ firstLoop = false;
+ delete data;
+ }
+
+ // Check if there's any data to send
+ if (batchedData.tellp() == std::streampos(0))
+ {
+ ScheduleSend();
+ return;
+ }
+
+ if (!_dataStream.good() && !Connect())
+ return;
+
+ _dataStream << "POST " << "/write?db=" << _databaseName << " HTTP/1.1\r\n";
+ _dataStream << "Host: " << _hostname << ":" << _port << "\r\n";
+ _dataStream << "Accept: */*\r\n";
+ _dataStream << "Content-Type: application/octet-stream\r\n";
+ _dataStream << "Content-Transfer-Encoding: binary\r\n";
+
+ _dataStream << "Content-Length: " << std::to_string(batchedData.tellp()) << "\r\n\r\n";
+ _dataStream << batchedData.rdbuf();
+
+ std::string http_version;
+ _dataStream >> http_version;
+ unsigned int status_code = 0;
+ _dataStream >> status_code;
+ if (status_code != 204)
+ {
+ TC_LOG_ERROR("metric", "Error sending data, returned HTTP code: %u", status_code);
+ }
+
+ // Read and ignore the status description
+ std::string status_description;
+ std::getline(_dataStream, status_description);
+ // Read headers
+ std::string header;
+ while (std::getline(_dataStream, header) && header != "\r")
+ {
+ if (header == "Connection: close\r")
+ _dataStream.close();
+ }
+
+ ScheduleSend();
+}
+
+void Metric::ScheduleSend()
+{
+ if (_enabled)
+ {
+ _batchTimer->expires_from_now(boost::posix_time::seconds(_updateInterval));
+ _batchTimer->async_wait(std::bind(&Metric::SendBatch, this));
+ }
+ else
+ {
+ _dataStream.close();
+ MetricData* data;
+ // Clear the queue
+ while (_queuedData.Dequeue(data))
+ ;
+ }
+}
+
+void Metric::ForceSend()
+{
+ // Send what's queued only if io_service is stopped (so only on shutdown)
+ if (_enabled && _batchTimer->get_io_service().stopped())
+ SendBatch();
+}
+
+void Metric::ScheduleOverallStatusLog()
+{
+ if (_enabled)
+ {
+ _overallStatusTimer->expires_from_now(boost::posix_time::seconds(_overallStatusTimerInterval));
+ _overallStatusTimer->async_wait([this](const boost::system::error_code&)
+ {
+ _overallStatusTimerTriggered = true;
+ ScheduleOverallStatusLog();
+ });
+ }
+}
+
+Metric* Metric::instance()
+{
+ static Metric instance;
+ return &instance;
+}
diff --git a/src/common/Metric/Metric.h b/src/common/Metric/Metric.h
new file mode 100644
index 00000000000..1855e1d0098
--- /dev/null
+++ b/src/common/Metric/Metric.h
@@ -0,0 +1,141 @@
+/*
+* Copyright (C) 2008-2016 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 METRIC_H__
+#define METRIC_H__
+
+#include "Common.h"
+#include "Threading/MPSCQueue.h"
+#include <boost/asio/ip/tcp.hpp>
+#include <boost/algorithm/string.hpp>
+#include <type_traits>
+
+enum MetricDataType
+{
+ METRIC_DATA_VALUE,
+ METRIC_DATA_EVENT
+};
+
+struct MetricData
+{
+ std::string Category;
+ std::chrono::time_point<std::chrono::system_clock> Timestamp;
+ MetricDataType Type;
+
+ // LogValue-specific fields
+ std::string Value;
+
+ // LogEvent-specific fields
+ std::string Title;
+ std::string Text;
+};
+
+class TC_COMMON_API Metric
+{
+private:
+ boost::asio::ip::tcp::iostream _dataStream;
+ MPSCQueue<MetricData> _queuedData;
+ std::unique_ptr<boost::asio::deadline_timer> _batchTimer;
+ std::unique_ptr<boost::asio::deadline_timer> _overallStatusTimer;
+ int32 _updateInterval = 0;
+ int32 _overallStatusTimerInterval = 0;
+ bool _enabled = false;
+ bool _overallStatusTimerTriggered = false;
+ std::string _hostname;
+ std::string _port;
+ std::string _databaseName;
+ std::function<void()> _overallStatusLogger;
+ std::string _realmName;
+
+ bool Connect();
+ void SendBatch();
+ void ScheduleSend();
+ void ScheduleOverallStatusLog();
+
+ template<class T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr>
+ static std::string FormatInfluxDBValue(T value) { return std::to_string(value) + 'i'; }
+
+ static std::string FormatInfluxDBValue(std::string const& value)
+ {
+ return '"' + boost::replace_all_copy(value, "\"", "\\\"") + '"';
+ }
+
+ static std::string FormatInfluxDBValue(bool value) { return value ? "t" : "f"; }
+ static std::string FormatInfluxDBValue(const char* value) { return FormatInfluxDBValue(std::string(value)); }
+ static std::string FormatInfluxDBValue(double value) { return std::to_string(value); }
+ static std::string FormatInfluxDBValue(float value) { return FormatInfluxDBValue(double(value)); }
+
+public:
+ static Metric* instance();
+
+ void Initialize(std::string const& realmName, boost::asio::io_service& ioService, std::function<void()> overallStatusLogger = [](){});
+ void LoadFromConfigs();
+ void Update();
+
+ template<class T>
+ void LogValue(std::string const& category, T value)
+ {
+ using namespace std::chrono;
+
+ MetricData* data = new MetricData;
+ data->Category = category;
+ data->Timestamp = system_clock::now();
+ data->Type = METRIC_DATA_VALUE;
+ data->Value = FormatInfluxDBValue(value);
+
+ _queuedData.Enqueue(data);
+ }
+
+ void LogEvent(std::string const& category, std::string const& title, std::string const& description);
+
+ void ForceSend();
+ bool IsEnabled() const { return _enabled; }
+};
+
+#define sMetric Metric::instance()
+
+#if PLATFORM != PLATFORM_WINDOWS
+#define TC_METRIC_EVENT(category, title, description) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogEvent(category, title, description); \
+ } while (0)
+#define TC_METRIC_VALUE(category, value) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogValue(category, value); \
+ } while (0)
+#else
+#define TC_METRIC_EVENT(category, title, description) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogEvent(category, title, description); \
+ } while (0) \
+ __pragma(warning(pop))
+#define TC_METRIC_VALUE(category, value) \
+ __pragma(warning(push)) \
+ __pragma(warning(disable:4127)) \
+ do { \
+ if (sMetric->IsEnabled()) \
+ sMetric->LogValue(category, value); \
+ } while (0) \
+ __pragma(warning(pop))
+#endif
+
+#endif // METRIC_H__
diff --git a/src/common/Utilities/Containers.h b/src/common/Utilities/Containers.h
index 554dcb1b3de..5edb245fd87 100644
--- a/src/common/Utilities/Containers.h
+++ b/src/common/Utilities/Containers.h
@@ -31,7 +31,7 @@ namespace Trinity
namespace Containers
{
template<class T>
- void RandomResizeList(std::list<T> &list, uint32 size)
+ void RandomResizeList(std::list<T>& list, uint32 size)
{
uint32 list_size = uint32(list.size());
@@ -56,7 +56,7 @@ namespace Trinity
if (size)
RandomResizeList(listCopy, size);
- list = listCopy;
+ list = std::move(listCopy);
}
/*
diff --git a/src/server/bnetserver/REST/LoginRESTService.h b/src/server/bnetserver/REST/LoginRESTService.h
index 5c78ef3e09d..881c656f15e 100644
--- a/src/server/bnetserver/REST/LoginRESTService.h
+++ b/src/server/bnetserver/REST/LoginRESTService.h
@@ -35,7 +35,7 @@ struct soap_plugin;
class LoginRESTService
{
public:
- LoginRESTService() : _stopped(false), _port(0) { }
+ LoginRESTService() : _stopped(false), _port(0), _loginTicketCleanupTimer(nullptr) { }
static LoginRESTService& Instance();
diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp
index 66d55480796..844f80cba06 100644
--- a/src/server/bnetserver/Server/Session.cpp
+++ b/src/server/bnetserver/Server/Session.cpp
@@ -647,7 +647,7 @@ bool Battlenet::Session::ReadHeaderHandler()
bool Battlenet::Session::ReadDataHandler()
{
Header header;
- header.ParseFromArray(_headerBuffer.GetReadPointer(), _headerBuffer.GetActiveSize());
+ ASSERT(header.ParseFromArray(_headerBuffer.GetReadPointer(), _headerBuffer.GetActiveSize()));
if (header.service_id() != 0xFE)
{
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp
index 7d8023fa272..14e7ac54bab 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.cpp
+++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp
@@ -648,10 +648,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);
PrepareStatement(CHAR_INS_PET_AURA_EFFECT, "INSERT INTO pet_aura_effect (guid, casterGuid, spell, effectMask, effectIndex, amount, baseAmount) "
"VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_BOTH);
- PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH);
- PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND id = ?", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT_2, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND entry = ? AND (slot = ? OR slot > ?)", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?) ", CONNECTION_SYNCH);
+ PrepareStatement(CHAR_SEL_CHAR_PET_BY_ENTRY_AND_SLOT, "SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization FROM character_pet WHERE owner = ? AND slot = ?", CONNECTION_SYNCH);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_OWNER, "DELETE FROM character_pet WHERE owner = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_NAME, "UPDATE character_pet SET name = ?, renamed = 1 WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_SLOT_EXCLUDE_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND slot = ? AND id <> ?", CONNECTION_ASYNC);
@@ -659,6 +659,10 @@ void CharacterDatabaseConnection::DoPrepareStatements()
PrepareStatement(CHAR_UPD_CHAR_PET_SLOT_BY_ID, "UPDATE character_pet SET slot = ? WHERE owner = ? AND id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_ID, "DELETE FROM character_pet WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_DEL_CHAR_PET_BY_SLOT, "DELETE FROM character_pet WHERE owner = ? AND (slot = ? OR slot > ?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_DEL_ALL_PET_SPELLS_BY_OWNER, "DELETE FROM pet_spell WHERE guid in (SELECT id FROM character_pet WHERE owner=?)", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_UPD_PET_SPECS_BY_OWNER, "UPDATE character_pet SET specialization = 0 WHERE owner=?", CONNECTION_ASYNC);
+ PrepareStatement(CHAR_INS_PET, "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType, specialization) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC);
// PvPstats
PrepareStatement(CHAR_SEL_PVPSTATS_MAXID, "SELECT MAX(id) FROM pvpstats_battlegrounds", CONNECTION_SYNCH);
diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h
index 1941f221eb3..ea6d9bd186f 100644
--- a/src/server/database/Database/Implementation/CharacterDatabase.h
+++ b/src/server/database/Database/Implementation/CharacterDatabase.h
@@ -558,6 +558,9 @@ enum CharacterDatabaseStatements
CHAR_UPD_CHAR_PET_SLOT_BY_ID,
CHAR_DEL_CHAR_PET_BY_ID,
CHAR_DEL_CHAR_PET_BY_SLOT,
+ CHAR_DEL_ALL_PET_SPELLS_BY_OWNER,
+ CHAR_UPD_PET_SPECS_BY_OWNER,
+ CHAR_INS_PET,
CHAR_SEL_ITEMCONTAINER_ITEMS,
CHAR_DEL_ITEMCONTAINER_ITEMS,
diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp
index e37258001ea..249839c079b 100644
--- a/src/server/database/Database/Implementation/LoginDatabase.cpp
+++ b/src/server/database/Database/Implementation/LoginDatabase.cpp
@@ -119,7 +119,7 @@ void LoginDatabaseConnection::DoPrepareStatements()
PrepareStatement(LOGIN_SEL_BNET_ACCOUNT_INFO, "SELECT " BnetAccountInfo ", " BnetGameAccountInfo
" FROM battlenet_accounts ba LEFT JOIN battlenet_account_bans bab ON ba.id = bab.id LEFT JOIN account a ON ba.id = a.battlenet_account"
- " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? AND ba.sha_pass_hash = ?", CONNECTION_SYNCH);
+ " LEFT JOIN account_banned ab ON a.id = ab.id AND ab.active = 1 LEFT JOIN account_access aa ON a.id = aa.id AND aa.RealmID = -1 WHERE ba.email = ? AND ba.sha_pass_hash = ? ORDER BY a.id", CONNECTION_SYNCH);
PrepareStatement(LOGIN_UPD_BNET_LAST_LOGIN_INFO, "UPDATE battlenet_accounts SET last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE id = ?", CONNECTION_ASYNC);
PrepareStatement(LOGIN_UPD_BNET_GAME_ACCOUNT_LOGIN_INFO, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH);
PrepareStatement(LOGIN_SEL_BNET_CHARACTER_COUNTS_BY_ACCOUNT_ID, "SELECT rc.acctid, rc.numchars, r.id, r.Region, r.Battlegroup FROM realmcharacters rc INNER JOIN realmlist r ON rc.realmid = r.id WHERE rc.acctid = ?", CONNECTION_ASYNC);
diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp
index 7dc0a307ca2..3d81b9e035e 100644
--- a/src/server/database/Updater/UpdateFetcher.cpp
+++ b/src/server/database/Updater/UpdateFetcher.cpp
@@ -67,11 +67,11 @@ void UpdateFetcher::FillFileListRecursively(Path const& path, LocaleFileStorage&
LocaleFileEntry const entry = { itr->path(), state };
// Check for doubled filenames
- // Since elements are only compared through their filenames this is ok
+ // Because elements are only compared by their filenames, this is ok
if (storage.find(entry) != storage.end())
{
- TC_LOG_FATAL("sql.updates", "Duplicated filename occurred \"%s\", since updates are ordered " \
- "through its filename every name needs to be unique!", itr->path().generic_string().c_str());
+ TC_LOG_FATAL("sql.updates", "Duplicate filename \"%s\" occurred. Because updates are ordered " \
+ "by their filenames, every name needs to be unique!", itr->path().generic_string().c_str());
throw UpdateException("Updating failed, see the log for details.");
}
@@ -101,7 +101,7 @@ UpdateFetcher::DirectoryStorage UpdateFetcher::ReceiveIncludedDirectories() cons
if (!is_directory(p))
{
- TC_LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"%s\" isn't existing, skipped!", p.generic_string().c_str());
+ TC_LOG_WARN("sql.updates", "DBUpdater: Given update include directory \"%s\" does not exist, skipped!", p.generic_string().c_str());
continue;
}
@@ -144,7 +144,7 @@ std::string UpdateFetcher::ReadSQLUpdate(boost::filesystem::path const& file) co
{
TC_LOG_FATAL("sql.updates", "Failed to open the sql update \"%s\" for reading! "
"Stopping the server to keep the database integrity, "
- "try to identify and solve the issue or disabled the database updater.",
+ "try to identify and solve the issue or disable the database updater.",
file.generic_string().c_str());
throw UpdateException("Opening the sql update failed!");
@@ -192,7 +192,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
AppliedFileStorage::const_iterator iter = applied.find(availableQuery.first.filename().string());
if (iter != applied.end())
{
- // If redundancy is disabled skip it since the update is already applied.
+ // If redundancy is disabled, skip it, because the update is already applied.
if (!redundancyChecks)
{
TC_LOG_DEBUG("sql.updates", ">> Update is already applied, skipping redundancy checks.");
@@ -200,7 +200,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
continue;
}
- // If the update is in an archived directory and is marked as archived in our database skip redundancy checks (archived updates never change).
+ // If the update is in an archived directory and is marked as archived in our database, skip redundancy checks (archived updates never change).
if (!archivedRedundancy && (iter->second.state == ARCHIVED) && (availableQuery.second == ARCHIVED))
{
TC_LOG_DEBUG("sql.updates", ">> Update is archived and marked as archived in database, skipping redundancy checks.");
@@ -217,11 +217,11 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
// Update is not in our applied list
if (iter == applied.end())
{
- // Catch renames (different filename but same hash)
+ // Catch renames (different filename, but same hash)
HashToFileNameStorage::const_iterator const hashIter = hashToName.find(hash);
if (hashIter != hashToName.end())
{
- // Check if the original file was removed if not we've got a problem.
+ // Check if the original file was removed. If not, we've got a problem.
LocaleFileStorage::const_iterator localeIter;
// Push localeIter forward
for (localeIter = available.begin(); (localeIter != available.end()) &&
@@ -230,12 +230,12 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
// Conflict!
if (localeIter != available.end())
{
- TC_LOG_WARN("sql.updates", ">> Seems like update \"%s\" \'%s\' was renamed, but the old file is still there! " \
- "Trade it as a new file! (Probably its an unmodified copy of file \"%s\")",
+ TC_LOG_WARN("sql.updates", ">> It seems like the update \"%s\" \'%s\' was renamed, but the old file is still there! " \
+ "Treating it as a new file! (It is probably an unmodified copy of the file \"%s\")",
availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str(),
localeIter->first.filename().string().c_str());
}
- // Its save to trade the file as renamed here
+ // It is safe to treat the file as renamed here
else
{
TC_LOG_INFO("sql.updates", ">> Renaming update \"%s\" to \"%s\" \'%s\'.",
@@ -253,7 +253,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
availableQuery.first.filename().string().c_str(), hash.substr(0, 7).c_str());
}
}
- // Rehash the update entry if it is contained in our database but with an empty hash.
+ // Rehash the update entry if it exists in our database with an empty hash.
else if (allowRehash && iter->second.hash.empty())
{
mode = MODE_REHASH;
@@ -263,7 +263,7 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
}
else
{
- // If the hash of the files differs from the one stored in our database reapply the update (because it was changed).
+ // If the hash of the files differs from the one stored in our database, reapply the update (because it changed).
if (iter->second.hash != hash)
{
TC_LOG_INFO("sql.updates", ">> Reapplying update \"%s\" \'%s\' -> \'%s\' (it changed)...", availableQuery.first.filename().string().c_str(),
@@ -271,16 +271,16 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
}
else
{
- // If the file wasn't changed and just moved update its state if necessary.
+ // If the file wasn't changed and just moved, update its state (if necessary).
if (iter->second.state != availableQuery.second)
{
- TC_LOG_DEBUG("sql.updates", ">> Updating state of \"%s\" to \'%s\'...",
+ TC_LOG_DEBUG("sql.updates", ">> Updating the state of \"%s\" to \'%s\'...",
availableQuery.first.filename().string().c_str(), AppliedFileEntry::StateConvert(availableQuery.second).c_str());
UpdateState(availableQuery.first.filename().string(), availableQuery.second);
}
- TC_LOG_DEBUG("sql.updates", ">> Update is already applied and is matching hash \'%s\'.", hash.substr(0, 7).c_str());
+ TC_LOG_DEBUG("sql.updates", ">> Update is already applied and matches the hash \'%s\'.", hash.substr(0, 7).c_str());
applied.erase(iter);
continue;
@@ -307,14 +307,14 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
++importedUpdates;
}
- // Cleanup up orphaned entries if enabled
+ // Cleanup up orphaned entries (if enabled)
if (!applied.empty())
{
bool const doCleanup = (cleanDeadReferencesMaxCount < 0) || (applied.size() <= static_cast<size_t>(cleanDeadReferencesMaxCount));
for (auto const& entry : applied)
{
- TC_LOG_WARN("sql.updates", ">> File \'%s\' was applied to the database but is missing in" \
+ TC_LOG_WARN("sql.updates", ">> The file \'%s\' was applied to the database, but is missing in" \
" your update directory now!", entry.first.c_str());
if (doCleanup)
@@ -325,8 +325,8 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks,
CleanUp(applied);
else
{
- TC_LOG_ERROR("sql.updates", "Cleanup is disabled! There are " SZFMTD " dirty files that were applied to your database " \
- "but are now missing in your source directory!", applied.size());
+ TC_LOG_ERROR("sql.updates", "Cleanup is disabled! There were " SZFMTD " dirty files applied to your database, " \
+ "but they are now missing in your source directory!", applied.size());
}
}
@@ -343,7 +343,7 @@ uint32 UpdateFetcher::Apply(Path const& path) const
// Update database
_applyFile(path);
- // Return time the query took to apply
+ // Return the time it took the query to apply
return uint32(std::chrono::duration_cast<std::chrono::milliseconds>(Time::now() - begin).count());
}
@@ -358,7 +358,7 @@ void UpdateFetcher::UpdateEntry(AppliedFileEntry const& entry, uint32 const spee
void UpdateFetcher::RenameEntry(std::string const& from, std::string const& to) const
{
- // Delete target if it exists
+ // Delete the target if it exists
{
std::string const update = "DELETE FROM `updates` WHERE `name`=\"" + to + "\"";
diff --git a/src/server/game/AI/CoreAI/GameObjectAI.h b/src/server/game/AI/CoreAI/GameObjectAI.h
index 7a2f23ac804..6d02f97e0ec 100644
--- a/src/server/game/AI/CoreAI/GameObjectAI.h
+++ b/src/server/game/AI/CoreAI/GameObjectAI.h
@@ -47,7 +47,7 @@ class TC_GAME_API GameObjectAI
static int Permissible(GameObject const* go);
- virtual bool GossipHello(Player* /*player*/) { return false; }
+ virtual bool GossipHello(Player* /*player*/, bool /*isUse*/) { return false; }
virtual bool GossipSelect(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/) { return false; }
virtual bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, char const* /*code*/) { return false; }
virtual bool QuestAccept(Player* /*player*/, Quest const* /*quest*/) { return false; }
diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp
index 914a476cf7d..5cda5fce674 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.cpp
+++ b/src/server/game/AI/SmartScripts/SmartAI.cpp
@@ -840,7 +840,7 @@ void SmartGameObjectAI::Reset()
}
// Called when a player opens a gossip dialog with the gameobject.
-bool SmartGameObjectAI::GossipHello(Player* player)
+bool SmartGameObjectAI::GossipHello(Player* player, bool /*isUse*/)
{
TC_LOG_DEBUG("scripts.ai", "SmartGameObjectAI::GossipHello");
GetScript()->ProcessEventsFor(SMART_EVENT_GOSSIP_HELLO, player, 0, 0, false, NULL, go);
diff --git a/src/server/game/AI/SmartScripts/SmartAI.h b/src/server/game/AI/SmartScripts/SmartAI.h
index aa7c9ace0b3..67a7bd4c330 100644
--- a/src/server/game/AI/SmartScripts/SmartAI.h
+++ b/src/server/game/AI/SmartScripts/SmartAI.h
@@ -242,7 +242,7 @@ class TC_GAME_API SmartGameObjectAI : public GameObjectAI
SmartScript* GetScript() { return &mScript; }
static int Permissible(const GameObject* g);
- bool GossipHello(Player* player) override;
+ bool GossipHello(Player* player, bool isUse) override;
bool GossipSelect(Player* player, uint32 sender, uint32 action) override;
bool GossipSelectCode(Player* /*player*/, uint32 /*sender*/, uint32 /*action*/, const char* /*code*/) override;
bool QuestAccept(Player* player, Quest const* quest) override;
diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
index 53cc4d57033..3b3be37b0b3 100644
--- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
+++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp
@@ -442,11 +442,11 @@ void AuctionHouseMgr::PendingAuctionProcess(Player* player)
totaldeposit = GetAuctionDeposit(itr->auctionHouseEntry, itr->etime, item, totalItems);
uint32 depositremain = totaldeposit;
- for (auto itr = thisAH->begin(); itr != thisAH->end(); ++itr)
+ for (auto itrAH = thisAH->begin(); itrAH != thisAH->end(); ++itrAH)
{
- AuctionEntry* AH = (*itr);
+ AuctionEntry* AH = (*itrAH);
- if (next(itr) == thisAH->end())
+ if (next(itrAH) == thisAH->end())
AH->deposit = depositremain;
else
{
diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp
index 58874e394e5..0db3ed09ef2 100644
--- a/src/server/game/Battlefield/Battlefield.cpp
+++ b/src/server/game/Battlefield/Battlefield.cpp
@@ -828,7 +828,7 @@ GameObject* Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z
// Create gameobject
GameObject* go = new GameObject;
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, PHASEMASK_NORMAL, x, y, z, o, 0, 0, 0, 0, 100, GO_STATE_READY))
+ if (!go->Create(entry, map, PHASEMASK_NORMAL, x, y, z, o, 0, 0, 0, 0, 100, GO_STATE_READY))
{
TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry);
delete go;
diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp
index 825a56820f2..449e614a900 100644
--- a/src/server/game/Battlegrounds/Battleground.cpp
+++ b/src/server/game/Battlegrounds/Battleground.cpp
@@ -1076,6 +1076,12 @@ void Battleground::StartBattleground()
TC_LOG_DEBUG("bg.arena", "Arena match type: %u for Team1Id: %u - Team2Id: %u started.", m_ArenaType, m_ArenaTeamIds[TEAM_ALLIANCE], m_ArenaTeamIds[TEAM_HORDE]);
}
+void Battleground::TeleportPlayerToExploitLocation(Player* player)
+{
+ if (WorldSafeLocsEntry const* loc = GetExploitTeleportLocation(Team(player->GetBGTeam())))
+ player->TeleportTo(loc->MapID, loc->Loc.X, loc->Loc.Y, loc->Loc.Z, loc->Facing);
+}
+
void Battleground::AddPlayer(Player* player)
{
// remove afk from player
@@ -1455,8 +1461,7 @@ bool Battleground::AddObject(uint32 type, uint32 entry, float x, float y, float
// and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
// So we must create it specific for this instance
GameObject* go = new GameObject;
- if (!go->Create(GetBgMap()->GenerateLowGuid<HighGuid::GameObject>(), entry, GetBgMap(),
- PHASEMASK_NORMAL, x, y, z, o, rotation0, rotation1, rotation2, rotation3, 100, goState))
+ if (!go->Create(entry, GetBgMap(), PHASEMASK_NORMAL, x, y, z, o, rotation0, rotation1, rotation2, rotation3, 100, goState))
{
TC_LOG_ERROR("bg.battleground", "Battleground::AddObject: cannot create gameobject (entry: %u) for BG (map: %u, instance id: %u)!",
entry, m_MapId, m_InstanceID);
diff --git a/src/server/game/Battlegrounds/Battleground.h b/src/server/game/Battlegrounds/Battleground.h
index 2ef7eca4aa1..4d2f5da1a24 100644
--- a/src/server/game/Battlegrounds/Battleground.h
+++ b/src/server/game/Battlegrounds/Battleground.h
@@ -438,6 +438,10 @@ class TC_GAME_API Battleground
// Death related
virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player);
+ virtual WorldSafeLocsEntry const* GetExploitTeleportLocation(Team /*team*/) { return nullptr; }
+ // GetExploitTeleportLocation(TeamId) must be implemented in the battleground subclass.
+ void TeleportPlayerToExploitLocation(Player* player);
+
virtual void AddPlayer(Player* player); // must be implemented in BG subclass
void AddOrSetPlayerToCorrectBgGroup(Player* player, uint32 team);
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp
index 911fe259fc3..60e0a5f8db8 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.cpp
@@ -231,11 +231,13 @@ void BattlegroundAB::RemovePlayer(Player* /*player*/, ObjectGuid /*guid*/, uint3
void BattlegroundAB::HandleAreaTrigger(Player* player, uint32 trigger, bool entered)
{
- if (GetStatus() != STATUS_IN_PROGRESS)
- return;
-
switch (trigger)
{
+ case 6635: // Horde Start
+ case 6634: // Alliance Start
+ if (GetStatus() == STATUS_WAIT_JOIN && !entered)
+ TeleportPlayerToExploitLocation(player);
+ break;
case 3948: // Arathi Basin Alliance Exit.
if (player->GetTeam() != ALLIANCE)
player->GetSession()->SendNotification("Only The Alliance can use that portal");
@@ -690,6 +692,11 @@ WorldSafeLocsEntry const* BattlegroundAB::GetClosestGraveYard(Player* player)
return good_entry;
}
+WorldSafeLocsEntry const* BattlegroundAB::GetExploitTeleportLocation(Team team)
+{
+ return sWorldSafeLocsStore.LookupEntry(team == ALLIANCE ? AB_EXPLOIT_TELEPORT_LOCATION_ALLIANCE : AB_EXPLOIT_TELEPORT_LOCATION_HORDE);
+}
+
bool BattlegroundAB::UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor)
{
if (!Battleground::UpdatePlayerScore(player, type, value, doAddHonor))
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
index e4bdf2f16d6..c452ca6fa2f 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAB.h
@@ -179,6 +179,12 @@ enum BG_AB_Objectives
AB_OBJECTIVE_DEFEND_BASE = 123
};
+enum BG_AB_ExploitTeleportLocations
+{
+ AB_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3705,
+ AB_EXPLOIT_TELEPORT_LOCATION_HORDE = 3706
+};
+
#define BG_AB_NotABBGWeekendHonorTicks 260
#define BG_AB_ABBGWeekendHonorTicks 160
#define BG_AB_NotABBGWeekendReputationTicks 160
@@ -288,6 +294,7 @@ class BattlegroundAB : public Battleground
void Reset() override;
void EndBattleground(uint32 winner) override;
WorldSafeLocsEntry const* GetClosestGraveYard(Player* player) override;
+ WorldSafeLocsEntry const* GetExploitTeleportLocation(Team team) override;
/* Scorekeeping */
bool UpdatePlayerScore(Player* player, uint32 type, uint32 value, bool doAddHonor = true) override;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
index 59d01df8237..031228013ef 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.cpp
@@ -493,11 +493,13 @@ void BattlegroundAV::RemovePlayer(Player* player, ObjectGuid /*guid*/, uint32 /*
void BattlegroundAV::HandleAreaTrigger(Player* player, uint32 trigger, bool entered)
{
- if (GetStatus() != STATUS_IN_PROGRESS)
- return;
-
switch (trigger)
{
+ case 6633: // Horde Start
+ case 6632: // Alliance Start
+ if (GetStatus() == STATUS_WAIT_JOIN && entered)
+ TeleportPlayerToExploitLocation(player);
+ break;
case 95:
case 2608:
if (player->GetTeam() != ALLIANCE)
@@ -1124,6 +1126,11 @@ WorldSafeLocsEntry const* BattlegroundAV::GetClosestGraveYard(Player* player)
return pGraveyard;
}
+WorldSafeLocsEntry const* BattlegroundAV::GetExploitTeleportLocation(Team team)
+{
+ return sWorldSafeLocsStore.LookupEntry(team == ALLIANCE ? AV_EXPLOIT_TELEPORT_LOCATION_ALLIANCE: AV_EXPLOIT_TELEPORT_LOCATION_HORDE);
+}
+
bool BattlegroundAV::SetupBattleground()
{
// Create starting objects
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
index 8bf97822426..eae03d83bc1 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundAV.h
@@ -1536,6 +1536,12 @@ enum Texts
TEXT_SNIVVLE_RANDOM = 0
};
+enum BG_AV_ExploitTeleportLocations
+{
+ AV_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3664,
+ AV_EXPLOIT_TELEPORT_LOCATION_HORDE = 3665
+};
+
struct BG_AV_NodeInfo
{
BG_AV_States State;
@@ -1633,6 +1639,7 @@ class BattlegroundAV : public Battleground
void EndBattleground(uint32 winner) override;
WorldSafeLocsEntry const* GetClosestGraveYard(Player* player) override;
+ WorldSafeLocsEntry const* GetExploitTeleportLocation(Team team) override;
// Achievement: Av perfection and Everything counts
bool CheckAchievementCriteriaMeet(uint32 criteriaId, Player const* source, Unit const* target = nullptr, uint32 miscvalue1 = 0) override;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
index 461420fe962..8bcba2ca204 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp
@@ -406,14 +406,16 @@ void BattlegroundEY::RemovePlayer(Player* player, ObjectGuid guid, uint32 /*team
void BattlegroundEY::HandleAreaTrigger(Player* player, uint32 trigger, bool entered)
{
- if (GetStatus() != STATUS_IN_PROGRESS)
- return;
-
if (!player->IsAlive()) //hack code, must be removed later
return;
switch (trigger)
{
+ case 4530: // Horde Start
+ case 4531: // Alliance Start
+ if (GetStatus() == STATUS_WAIT_JOIN && !entered)
+ TeleportPlayerToExploitLocation(player);
+ break;
case TR_BLOOD_ELF_POINT:
if (m_PointState[BLOOD_ELF] == EY_POINT_UNDER_CONTROL && m_PointOwnedByTeam[BLOOD_ELF] == player->GetTeam())
if (m_FlagState && GetFlagPickerGUID() == player->GetGUID())
@@ -438,8 +440,6 @@ void BattlegroundEY::HandleAreaTrigger(Player* player, uint32 trigger, bool ente
case 4515:
case 4517:
case 4519:
- case 4530:
- case 4531:
case 4568:
case 4569:
case 4570:
@@ -950,6 +950,11 @@ WorldSafeLocsEntry const* BattlegroundEY::GetClosestGraveYard(Player* player)
return nearestEntry;
}
+WorldSafeLocsEntry const* BattlegroundEY::GetExploitTeleportLocation(Team team)
+{
+ return sWorldSafeLocsStore.LookupEntry(team == ALLIANCE ? EY_EXPLOIT_TELEPORT_LOCATION_ALLIANCE : EY_EXPLOIT_TELEPORT_LOCATION_HORDE);
+}
+
bool BattlegroundEY::IsAllNodesControlledByTeam(uint32 team) const
{
uint32 count = 0;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.h b/src/server/game/Battlegrounds/Zones/BattlegroundEY.h
index e25601f51b9..a1e63e356b8 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.h
@@ -248,6 +248,12 @@ enum BG_EY_Objectives
EY_OBJECTIVE_CAPTURE_FLAG = 183
};
+enum BG_EY_ExploitTeleportLocations
+{
+ EY_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3773,
+ EY_EXPLOIT_TELEPORT_LOCATION_HORDE = 3772
+};
+
struct BattlegroundEYPointIconsStruct
{
BattlegroundEYPointIconsStruct(uint32 _WorldStateControlIndex, uint32 _WorldStateAllianceControlledIndex, uint32 _WorldStateHordeControlledIndex)
@@ -376,6 +382,7 @@ class BattlegroundEY : public Battleground
void HandleAreaTrigger(Player* source, uint32 trigger, bool entered) override;
void HandleKillPlayer(Player* player, Player* killer) override;
WorldSafeLocsEntry const* GetClosestGraveYard(Player* player) override;
+ WorldSafeLocsEntry const* GetExploitTeleportLocation(Team team) override;
bool SetupBattleground() override;
void Reset() override;
void UpdateTeamScore(uint32 Team);
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
index d31fd47819f..085da3d6b5a 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp
@@ -263,11 +263,12 @@ void BattlegroundIC::RemovePlayer(Player* player, ObjectGuid /*guid*/, uint32 /*
}
}
-void BattlegroundIC::HandleAreaTrigger(Player* player, uint32 trigger, bool /*entered*/)
+void BattlegroundIC::HandleAreaTrigger(Player* player, uint32 trigger, bool entered)
{
// this is wrong way to implement these things. On official it done by gameobject spell cast.
- if (GetStatus() != STATUS_IN_PROGRESS)
- return;
+ if (GetStatus() == STATUS_WAIT_JOIN && !entered)
+ if (trigger == 9176 || trigger == 9178)
+ TeleportPlayerToExploitLocation(player);
/// @hack: this spell should be cast by npc 22515 (World Trigger) and not by the player
if (trigger == 5555 && player->GetTeamId() == TEAM_HORDE)
@@ -884,6 +885,11 @@ WorldSafeLocsEntry const* BattlegroundIC::GetClosestGraveYard(Player* player)
return good_entry;
}
+WorldSafeLocsEntry const * BattlegroundIC::GetExploitTeleportLocation(Team team)
+{
+ return sWorldSafeLocsStore.LookupEntry(team == ALLIANCE ? IC_EXPLOIT_TELEPORT_LOCATION_ALLIANCE : IC_EXPLOIT_TELEPORT_LOCATION_HORDE);
+}
+
bool BattlegroundIC::IsAllNodesControlledByTeam(uint32 team) const
{
uint32 count = 0;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h
index f60d9324e48..d65b69e62f4 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.h
@@ -468,6 +468,12 @@ enum BG_IC_MaxSpawns
MAX_CAPTAIN_SPAWNS_PER_FACTION = 2,
};
+enum BG_IC_ExploitTeleportLocations
+{
+ IC_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3986,
+ IC_EXPLOIT_TELEPORT_LOCATION_HORDE = 3983
+};
+
const ICNpc BG_IC_NpcSpawnlocs[MAX_NORMAL_NPCS_SPAWNS] =
{
{BG_IC_NPC_OVERLORD_AGMAR, NPC_OVERLORD_AGMAR, TEAM_HORDE, 1295.44f, -765.733f, 70.0541f, 0.0f}, //Overlord Agmar 1
@@ -940,6 +946,7 @@ class BattlegroundIC : public Battleground
void DestroyGate(Player* player, GameObject* go) override;
WorldSafeLocsEntry const* GetClosestGraveYard(Player* player) override;
+ WorldSafeLocsEntry const* GetExploitTeleportLocation(Team team) override;
/* Scorekeeping */
void FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet) override;
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp
index 0df4fd6f88e..37b39043e42 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundWS.cpp
@@ -650,13 +650,15 @@ void BattlegroundWS::UpdateTeamScore(uint32 team)
void BattlegroundWS::HandleAreaTrigger(Player* player, uint32 trigger, bool entered)
{
- if (GetStatus() != STATUS_IN_PROGRESS)
- return;
-
//uint32 SpellId = 0;
//uint64 buff_guid = 0;
switch (trigger)
{
+ case 8965: // Horde Start
+ case 8966: // Alliance Start
+ if (GetStatus() == STATUS_WAIT_JOIN && !entered)
+ TeleportPlayerToExploitLocation(player);
+ break;
case 3686: // Alliance elixir of speed spawn. Trigger not working, because located inside other areatrigger, can be replaced by IsWithinDist(object, dist) in Battleground::Update().
//buff_guid = BgObjects[BG_WS_OBJECT_SPEEDBUFF_1];
break;
@@ -851,6 +853,11 @@ WorldSafeLocsEntry const* BattlegroundWS::GetClosestGraveYard(Player* player)
}
}
+WorldSafeLocsEntry const* BattlegroundWS::GetExploitTeleportLocation(Team team)
+{
+ return sWorldSafeLocsStore.LookupEntry(team == ALLIANCE ? WS_EXPLOIT_TELEPORT_LOCATION_ALLIANCE : WS_EXPLOIT_TELEPORT_LOCATION_HORDE);
+}
+
void BattlegroundWS::FillInitialWorldStates(WorldPackets::WorldState::InitWorldStates& packet)
{
packet.Worldstates.emplace_back(uint32(BG_WS_FLAG_CAPTURES_ALLIANCE), int32(GetTeamScore(TEAM_ALLIANCE)));
diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundWS.h b/src/server/game/Battlegrounds/Zones/BattlegroundWS.h
index 3561d6f2fc3..8731255cd32 100644
--- a/src/server/game/Battlegrounds/Zones/BattlegroundWS.h
+++ b/src/server/game/Battlegrounds/Zones/BattlegroundWS.h
@@ -125,6 +125,12 @@ enum BG_WS_Graveyards
WS_GRAVEYARD_MAIN_HORDE = 772
};
+enum BG_WS_ExploitTeleportLocations
+{
+ WS_EXPLOIT_TELEPORT_LOCATION_ALLIANCE = 3784,
+ WS_EXPLOIT_TELEPORT_LOCATION_HORDE = 3785
+};
+
enum BG_WS_CreatureTypes
{
WS_SPIRIT_MAIN_ALLIANCE = 0,
@@ -222,6 +228,7 @@ class BattlegroundWS : public Battleground
void Reset() override;
void EndBattleground(uint32 winner) override;
WorldSafeLocsEntry const* GetClosestGraveYard(Player* player) override;
+ WorldSafeLocsEntry const* GetExploitTeleportLocation(Team team) override;
void UpdateFlagState(uint32 team, uint32 value);
void SetLastFlagCapture(uint32 team) { _lastFlagCaptureTeam = team; }
diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp
index a052d4d44d4..dcc18fdf64a 100644
--- a/src/server/game/Conditions/ConditionMgr.cpp
+++ b/src/server/game/Conditions/ConditionMgr.cpp
@@ -446,7 +446,7 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const
if (Unit* unit = object->ToUnit())
{
if (ConditionValue1 == 0)
- condMeets = (unit->GetStandState() == ConditionValue2);
+ condMeets = (unit->GetStandState() == UnitStandStateType(ConditionValue2));
else if (ConditionValue2 == 0)
condMeets = unit->IsStandState();
else if (ConditionValue2 == 1)
@@ -2368,7 +2368,7 @@ inline bool PlayerConditionLogic(uint32 logic, std::array<bool, N>& results)
return result;
}
-bool ConditionMgr::IsPlayerMeetingCondition(Player* player, PlayerConditionEntry const* condition)
+bool ConditionMgr::IsPlayerMeetingCondition(Player const* player, PlayerConditionEntry const* condition)
{
if (condition->MinLevel && player->getLevel() < condition->MinLevel)
return false;
@@ -2489,7 +2489,7 @@ bool ConditionMgr::IsPlayerMeetingCondition(Player* player, PlayerConditionEntry
if (condition->PartyStatus)
{
- Group* group = player->GetGroup();
+ Group const* group = player->GetGroup();
switch (condition->PartyStatus)
{
case 1:
diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h
index 7c61b4d2955..fb754ab5349 100644
--- a/src/server/game/Conditions/ConditionMgr.h
+++ b/src/server/game/Conditions/ConditionMgr.h
@@ -271,7 +271,7 @@ class TC_GAME_API ConditionMgr
bool IsObjectMeetingSmartEventConditions(int64 entryOrGuid, uint32 eventId, uint32 sourceType, Unit* unit, WorldObject* baseObject) const;
bool IsObjectMeetingVendorItemConditions(uint32 creatureId, uint32 itemId, Player* player, Creature* vendor) const;
- static bool IsPlayerMeetingCondition(Player* player, PlayerConditionEntry const* condition);
+ static bool IsPlayerMeetingCondition(Player const* player, PlayerConditionEntry const* condition);
struct ConditionTypeInfo
{
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index c13c4f775ca..6ea2dfef591 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -176,7 +176,7 @@ void GameObject::RemoveFromWorld()
}
}
-bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 /*phaseMask*/, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit)
+bool GameObject::Create(uint32 name_id, Map* map, uint32 /*phaseMask*/, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit)
{
ASSERT(map);
SetMap(map);
@@ -185,14 +185,14 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
m_stationaryPosition.Relocate(x, y, z, ang);
if (!IsPositionValid())
{
- TC_LOG_ERROR("misc", "Gameobject (GUID: " UI64FMTD " Spawn id: " UI64FMTD " Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, GetSpawnId(), name_id, x, y);
+ TC_LOG_ERROR("misc", "Gameobject (Spawn id: " UI64FMTD " Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", GetSpawnId(), name_id, x, y);
return false;
}
SetZoneScript();
if (m_zoneScript)
{
- name_id = m_zoneScript->GetGameObjectEntry(guidlow, name_id);
+ name_id = m_zoneScript->GetGameObjectEntry(m_spawnId, name_id);
if (!name_id)
return false;
}
@@ -200,26 +200,32 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u
GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(name_id);
if (!goinfo)
{
- TC_LOG_ERROR("sql.sql", "Gameobject (GUID: " UI64FMTD " Spawn id: " UI64FMTD " Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", guidlow, GetSpawnId(), name_id, map->GetId(), x, y, z);
+ TC_LOG_ERROR("sql.sql", "Gameobject (Spawn id: " UI64FMTD " Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", GetSpawnId(), name_id, map->GetId(), x, y, z);
return false;
}
if (goinfo->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT)
{
- TC_LOG_ERROR("sql.sql", "Gameobject (GUID: " UI64FMTD " Spawn id: " UI64FMTD " Entry: %u) not created: gameobject type GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT cannot be manually created.", guidlow, GetSpawnId(), name_id);
+ TC_LOG_ERROR("sql.sql", "Gameobject (Spawn id: " UI64FMTD " Entry: %u) not created: gameobject type GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT cannot be manually created.", GetSpawnId(), name_id);
return false;
}
- if (goinfo->type == GAMEOBJECT_TYPE_TRANSPORT)
+ ObjectGuid guid;
+ if (goinfo->type != GAMEOBJECT_TYPE_TRANSPORT)
+ guid = ObjectGuid::Create<HighGuid::GameObject>(map->GetId(), goinfo->entry, map->GenerateLowGuid<HighGuid::GameObject>());
+ else
+ {
+ guid = ObjectGuid::Create<HighGuid::Transport>(map->GenerateLowGuid<HighGuid::Transport>());
m_updateFlag |= UPDATEFLAG_TRANSPORT;
+ }
- Object::_Create(ObjectGuid::Create<HighGuid::GameObject>(map->GetId(), goinfo->entry, guidlow));
+ Object::_Create(guid);
m_goInfo = goinfo;
if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
{
- TC_LOG_ERROR("sql.sql", "Gameobject (GUID: " UI64FMTD " Spawn id: " UI64FMTD " Entry: %u) not created: non-existing GO type '%u' in `gameobject_template`. It will crash client if created.", guidlow, GetSpawnId(), name_id, goinfo->type);
+ TC_LOG_ERROR("sql.sql", "Gameobject (%s Spawn id: " UI64FMTD " Entry: %u) not created: non-existing GO type '%u' in `gameobject_template`. It will crash client if created.", guid.ToString().c_str(), GetSpawnId(), name_id, goinfo->type);
return false;
}
@@ -888,7 +894,7 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo
uint32 artKit = data->artKit;
m_spawnId = spawnId;
- if (!Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, artKit))
+ if (!Create(entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, artKit))
return false;
if (data->phaseid)
@@ -1250,7 +1256,7 @@ void GameObject::Use(Unit* user)
if (sScriptMgr->OnGossipHello(playerUser, this))
return;
- if (AI()->GossipHello(playerUser))
+ if (AI()->GossipHello(playerUser, true))
return;
}
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index a83b04e2a58..40e00531497 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -933,7 +933,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void RemoveFromWorld() override;
void CleanupsBeforeDelete(bool finalCleanup = true) override;
- bool Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit = 0);
+ bool Create(uint32 name_id, Map* map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit = 0);
void Update(uint32 p_time) override;
GameObjectTemplate const* GetGOInfo() const { return m_goInfo; }
GameObjectData const* GetGOData() const { return m_goData; }
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 47751548922..0522ef13095 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -2406,7 +2406,7 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float
Map* map = GetMap();
GameObject* go = new GameObject();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, GetPhaseMask(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3, 100, GO_STATE_READY))
+ if (!go->Create(entry, map, GetPhaseMask(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3, 100, GO_STATE_READY))
{
delete go;
return NULL;
diff --git a/src/server/game/Entities/Object/ObjectGuid.h b/src/server/game/Entities/Object/ObjectGuid.h
index 000028ef852..aa7b4a4a23e 100644
--- a/src/server/game/Entities/Object/ObjectGuid.h
+++ b/src/server/game/Entities/Object/ObjectGuid.h
@@ -160,7 +160,6 @@ GUID_TRAIT_GLOBAL(HighGuid::CommerceObj)
GUID_TRAIT_GLOBAL(HighGuid::ClientSession)
GUID_TRAIT_REALM_SPECIFIC(HighGuid::Player)
GUID_TRAIT_REALM_SPECIFIC(HighGuid::Item) // This is not exactly correct, there are 2 more unknown parts in highguid: (high >> 10 & 0xFF), (high >> 18 & 0xFFFFFF)
-GUID_TRAIT_REALM_SPECIFIC(HighGuid::Transport)
GUID_TRAIT_REALM_SPECIFIC(HighGuid::Guild)
GUID_TRAIT_MAP_SPECIFIC(HighGuid::WorldTransaction)
GUID_TRAIT_MAP_SPECIFIC(HighGuid::Conversation)
@@ -183,6 +182,19 @@ GUID_TRAIT_MAP_SPECIFIC(HighGuid::AILock)
GUID_TRAIT_MAP_SPECIFIC(HighGuid::AILockTicket)
GUID_TRAIT_MAP_SPECIFIC(HighGuid::Cast)
+// Special case
+// Global transports are loaded from `transports` table, RealmSpecific part is used for them.
+// after worldserver finishes loading, no more global transports can be created, only the ones existing within instances that never change maps
+// here is where MapSpecific comes into play - each map takes over the responsibility to generate transport guids
+// on top of this, regular elevators (GAMEOBJECT_TYPE_TRANSPORT) must also use Transport highguid type, otherwise client will reject seeing other players on them
+template<>
+struct ObjectGuidTraits<HighGuid::Transport>
+{
+ static bool const Global = false;
+ static bool const RealmSpecific = true;
+ static bool const MapSpecific = true;
+};
+
class ObjectGuid;
class PackedGuid;
@@ -207,7 +219,7 @@ class TC_GAME_API ObjectGuid
static typename std::enable_if<ObjectGuidTraits<type>::RealmSpecific, ObjectGuid>::type Create(LowType counter) { return RealmSpecific(type, counter); }
template<HighGuid type>
- static typename std::enable_if<ObjectGuidTraits<type>::MapSpecific, ObjectGuid>::type Create(uint16 mapId, uint32 entry, LowType counter) { return MapSpecific(type, 0, mapId, 0, entry, counter); }
+ static typename std::enable_if<ObjectGuidTraits<type>::MapSpecific && type != HighGuid::Transport, ObjectGuid>::type Create(uint16 mapId, uint32 entry, LowType counter) { return MapSpecific(type, 0, mapId, 0, entry, counter); }
template<HighGuid type>
static typename std::enable_if<ObjectGuidTraits<type>::MapSpecific, ObjectGuid>::type Create(uint8 subType, uint16 mapId, uint32 entry, LowType counter) { return MapSpecific(type, subType, mapId, 0, entry, counter); }
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index fb7ba8f22b7..06036f5cbf7 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -37,9 +37,9 @@
#define PET_XP_FACTOR 0.05f
Pet::Pet(Player* owner, PetType type) :
- Guardian(NULL, owner, true), m_usedTalentCount(0), m_removed(false),
+ Guardian(NULL, owner, true), m_removed(false),
m_petType(type), m_duration(0), m_loading(false), m_groupUpdateMask(0),
- m_declinedname(NULL)
+ m_declinedname(NULL), m_petSpecialization(0)
{
ASSERT(GetOwner());
@@ -312,8 +312,6 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
owner->SetMinion(this, true);
map->AddToMap(this->ToCreature());
- InitTalentForLevel(); // set original talents points before spell loading
-
uint32 timediff = uint32(time(NULL) - fields[13].GetUInt32());
_LoadAuras(timediff);
@@ -323,23 +321,28 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
m_charmInfo->LoadPetActionBar(fields[12].GetString());
_LoadSpells();
- InitTalentForLevel(); // re-init to check talent count
_LoadSpellCooldowns();
LearnPetPassives();
InitLevelupSpellsForLevel();
CastPetAuras(current);
}
- CleanupActionBar(); // remove unknown spells from action bar after load
-
TC_LOG_DEBUG("entities.pet", "New Pet has %s", GetGUID().ToString().c_str());
- owner->PetSpellInitialize();
+ uint16 specId = fields[16].GetUInt16();
+ if (ChrSpecializationEntry const* petSpec = sChrSpecializationStore.LookupEntry(specId))
+ specId = sDB2Manager.GetChrSpecializationByIndex(owner->HasAuraType(SPELL_AURA_OVERRIDE_PET_SPECS) ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0, petSpec->OrderIndex)->ID;
- SetGroupUpdateFlag(GROUP_UPDATE_PET_FULL);
+ SetSpecialization(specId);
- // TODO: 6.x remove/update pet talents
- //owner->SendTalentsInfoData(true);
+ // The SetSpecialization function will run these functions if the pet's spec is not 0
+ if (!GetSpecialization())
+ {
+ CleanupActionBar(); // remove unknown spells from action bar after load
+ owner->PetSpellInitialize();
+ }
+
+ SetGroupUpdateFlag(GROUP_UPDATE_PET_FULL);
if (getPetType() == HUNTER_PET)
{
@@ -415,8 +418,6 @@ void Pet::SavePetToDB(PetSaveMode mode)
if (mode >= PET_SAVE_AS_CURRENT)
{
ObjectGuid::LowType ownerLowGUID = GetOwnerGUID().GetCounter();
- std::string name = m_name;
- CharacterDatabase.EscapeString(name);
trans = CharacterDatabase.BeginTransaction();
// remove current data
@@ -445,34 +446,28 @@ void Pet::SavePetToDB(PetSaveMode mode)
}
// save pet
- std::ostringstream ss;
- ss << "INSERT INTO character_pet (id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, abdata, savetime, CreatedBySpell, PetType) "
- << "VALUES ("
- << m_charmInfo->GetPetNumber() << ','
- << GetEntry() << ','
- << ownerLowGUID << ','
- << GetNativeDisplayId() << ','
- << uint32(getLevel()) << ','
- << GetUInt32Value(UNIT_FIELD_PETEXPERIENCE) << ','
- << uint32(GetReactState()) << ','
- << uint32(mode) << ", '"
- << name.c_str() << "', "
- << uint32(HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1) << ','
- << curhealth << ','
- << curmana << ", '";
-
- for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
- {
- ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' '
- << uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' ';
- };
-
- ss << "', "
- << time(NULL) << ','
- << GetUInt32Value(UNIT_CREATED_BY_SPELL) << ','
- << uint32(getPetType()) << ')';
+ stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_PET);
+ stmt->setUInt32(0, m_charmInfo->GetPetNumber());
+ stmt->setUInt32(1, GetEntry());
+ stmt->setUInt64(2, ownerLowGUID);
+ stmt->setUInt32(3, GetNativeDisplayId());
+ stmt->setUInt8(4, getLevel());
+ stmt->setUInt32(5, GetUInt32Value(UNIT_FIELD_PETEXPERIENCE));
+ stmt->setUInt8(6, GetReactState());
+ stmt->setInt16(7, mode);
+ stmt->setString(8, m_name);
+ stmt->setUInt8(9, HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) ? 0 : 1);
+ stmt->setUInt32(10, curhealth);
+ stmt->setUInt32(11, curmana);
+
+ stmt->setString(12, GenerateActionBarData());
+
+ stmt->setUInt32(13, time(NULL));
+ stmt->setUInt32(14, GetUInt32Value(UNIT_CREATED_BY_SPELL));
+ stmt->setUInt8(15, getPetType());
+ stmt->setUInt16(16, m_petSpecialization);
+ trans->Append(stmt);
- trans->Append(ss.str().c_str());
CharacterDatabase.CommitTransaction(trans);
}
// delete
@@ -724,7 +719,6 @@ void Pet::GivePetLevel(uint8 level)
InitStatsForLevel(level);
InitLevelupSpellsForLevel();
- InitTalentForLevel();
}
bool Pet::CreateBaseAtCreature(Creature* creature)
@@ -1436,6 +1430,22 @@ bool Pet::learnSpell(uint32 spell_id)
return true;
}
+void Pet::learnSpells(std::vector<uint32> const& spellIds)
+{
+ WorldPackets::Pet::PetLearnedSpells packet;
+
+ for (uint32 spell : spellIds)
+ {
+ if (!addSpell(spell))
+ continue;
+
+ packet.Spells.push_back(spell);
+ }
+
+ if (!m_loading)
+ GetOwner()->GetSession()->SendPacket(packet.Write());
+}
+
void Pet::InitLevelupSpellsForLevel()
{
uint8 level = getLevel();
@@ -1488,6 +1498,22 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
return false;
}
+void Pet::unlearnSpells(std::vector<uint32> const& spellIds, bool learn_prev, bool clear_ab)
+{
+ WorldPackets::Pet::PetUnlearnedSpells packet;
+
+ for (uint32 spell : spellIds)
+ {
+ if (!removeSpell(spell, learn_prev, clear_ab))
+ continue;
+
+ packet.Spells.push_back(spell);
+ }
+
+ if (!m_loading)
+ GetOwner()->GetSession()->SendPacket(packet.Write());
+}
+
bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
{
PetSpellMap::iterator itr = m_spells.find(spell_id);
@@ -1513,7 +1539,7 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
}
// if remove last rank or non-ranked then update action bar at server and client if need
- if (clear_ab && !learn_prev && m_charmInfo->RemoveSpellFromActionBar(spell_id))
+ if (m_charmInfo->RemoveSpellFromActionBar(spell_id) && !learn_prev && clear_ab)
{
if (!m_loading)
GetOwner()->PetSpellInitialize(); // need update action bar for last removed rank
@@ -1549,182 +1575,6 @@ void Pet::InitPetCreateSpells()
CastPetAuras(false);
}
-bool Pet::resetTalents()
-{
- /* TODO: 6.x remove pet talents
- Player* player = GetOwner();
-
- // not need after this call
- if (player->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
- player->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS, true);
-
- CreatureTemplate const* ci = GetCreatureTemplate();
- if (!ci)
- return false;
- // Check pet talent type
- CreatureFamilyEntry const* pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
- if (!pet_family || pet_family->PetTalentType < 0)
- return false;
-
- uint8 level = getLevel();
- uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level);
-
- if (m_usedTalentCount == 0)
- {
- SetFreeTalentPoints(talentPointsForLevel);
- return false;
- }
-
- for (uint32 i = 0; i < sTalentStore.GetNumRows(); ++i)
- {
- TalentEntry const* talentInfo = sTalentStore.LookupEntry(i);
-
- if (!talentInfo)
- continue;
-
- TalentTabEntry const* talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
-
- if (!talentTabInfo)
- continue;
-
- // unlearn only talents for pets family talent type
- if (!((1 << pet_family->PetTalentType) & talentTabInfo->petTalentMask))
- continue;
-
- for (uint8 j = 0; j < MAX_TALENT_RANK; ++j)
- {
- for (PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end();)
- {
- if (itr->second.state == PETSPELL_REMOVED)
- {
- ++itr;
- continue;
- }
- // remove learned spells (all ranks)
- uint32 itrFirstId = sSpellMgr->GetFirstSpellInChain(itr->first);
-
- // unlearn if first rank is talent or learned by talent
- if (itrFirstId == talentInfo->RankID[j] || sSpellMgr->IsSpellLearnToSpell(talentInfo->RankID[j], itrFirstId))
- {
- unlearnSpell(itr->first, false);
- itr = m_spells.begin();
- continue;
- }
- else
- ++itr;
- }
- }
- }
-
- SetFreeTalentPoints(talentPointsForLevel);
-
- if (!m_loading)
- player->PetSpellInitialize();*/
- return true;
-}
-
-void Pet::resetTalentsForAllPetsOf(Player* /*owner*/, Pet* /*onlinePet*/ /*= NULL*/)
-{
- /* TODO: 6.x remove pet talents
- // not need after this call
- if (owner->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
- owner->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS, true);
-
- // reset for online
- if (onlinePet)
- onlinePet->resetTalents();
-
- // now need only reset for offline pets (all pets except online case)
- uint32 exceptPetNumber = onlinePet ? onlinePet->GetCharmInfo()->GetPetNumber() : 0;
-
- PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHAR_PET);
- stmt->setUInt64(0, owner->GetGUID().GetCounter());
- stmt->setUInt32(1, exceptPetNumber);
- PreparedQueryResult resultPets = CharacterDatabase.Query(stmt);
-
- // no offline pets
- if (!resultPets)
- return;
-
- stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PET_SPELL_LIST);
- stmt->setUInt64(0, owner->GetGUID().GetCounter());
- stmt->setUInt32(1, exceptPetNumber);
- PreparedQueryResult result = CharacterDatabase.Query(stmt);
-
- if (!result)
- return;
-
- bool need_comma = false;
- std::ostringstream ss;
- ss << "DELETE FROM pet_spell WHERE guid IN (";
-
- do
- {
- Field* fields = resultPets->Fetch();
-
- uint32 id = fields[0].GetUInt32();
-
- if (need_comma)
- ss << ',';
-
- ss << id;
-
- need_comma = true;
- } while (resultPets->NextRow());
-
- ss << ") AND spell IN (";
-
- bool need_execute = false;
- do
- {
- Field* fields = result->Fetch();
-
- uint32 spell = fields[0].GetUInt32();
-
- if (!GetTalentSpellCost(spell))
- continue;
-
- if (need_execute)
- ss << ',';
-
- ss << spell;
-
- need_execute = true;
- }
- while (result->NextRow());
-
- if (!need_execute)
- return;
-
- ss << ')';
-
- CharacterDatabase.Execute(ss.str().c_str());*/
-}
-
-void Pet::InitTalentForLevel()
-{
- /* TODO: 6.x remove/update pet talents
- uint8 level = getLevel();
- uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level);
- // Reset talents in case low level (on level down) or wrong points for level (hunter can unlearn TP increase talent)
- if (talentPointsForLevel == 0 || m_usedTalentCount > talentPointsForLevel)
- resetTalents(); // Remove all talent points
-
- SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount);
-
- if (!m_loading)
- GetOwner()->SendTalentsInfoData(true);
- */
-}
-
-uint8 Pet::GetMaxTalentPointsForLevel(uint8 level) const
-{
- uint8 points = (level >= 20) ? ((level - 16) / 4) : 0;
- // Mod points from owner SPELL_AURA_MOD_PET_TALENT_POINTS
- points += GetOwner()->GetTotalAuraModifier(SPELL_AURA_MOD_PET_TALENT_POINTS);
- return points;
-}
-
void Pet::ToggleAutocast(SpellInfo const* spellInfo, bool apply)
{
ASSERT(spellInfo);
@@ -1940,3 +1790,96 @@ void Pet::ResetGroupUpdateFlag()
if (GetOwner()->GetGroup())
GetOwner()->RemoveGroupUpdateFlag(GROUP_UPDATE_FLAG_PET);
}
+
+void Pet::LearnSpecializationSpells()
+{
+ std::vector<uint32> learnedSpells;
+
+ if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(m_petSpecialization))
+ {
+ for (size_t j = 0; j < specSpells->size(); ++j)
+ {
+ SpecializationSpellsEntry const* specSpell = specSpells->at(j);
+ SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(specSpell->SpellID);
+ if (!spellInfo || spellInfo->SpellLevel > getLevel())
+ continue;
+
+ learnedSpells.push_back(specSpell->SpellID);
+ }
+ }
+
+ learnSpells(learnedSpells);
+}
+
+void Pet::RemoveSpecializationSpells(bool clearActionBar)
+{
+ std::vector<uint32> unlearnedSpells;
+
+ for (uint32 i = 0; i < MAX_SPECIALIZATIONS; ++i)
+ {
+ if (ChrSpecializationEntry const* specialization = sDB2Manager.GetChrSpecializationByIndex(0, i))
+ {
+ if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(specialization->ID))
+ {
+ for (size_t j = 0; j < specSpells->size(); ++j)
+ {
+ SpecializationSpellsEntry const* specSpell = specSpells->at(j);
+ unlearnedSpells.push_back(specSpell->SpellID);
+ }
+ }
+ }
+
+ if (ChrSpecializationEntry const* specialization = sDB2Manager.GetChrSpecializationByIndex(PET_SPEC_OVERRIDE_CLASS_INDEX, i))
+ {
+ if (std::vector<SpecializationSpellsEntry const*> const* specSpells = sDB2Manager.GetSpecializationSpells(specialization->ID))
+ {
+ for (size_t j = 0; j < specSpells->size(); ++j)
+ {
+ SpecializationSpellsEntry const* specSpell = specSpells->at(j);
+ unlearnedSpells.push_back(specSpell->SpellID);
+ }
+ }
+ }
+ }
+
+ unlearnSpells(unlearnedSpells, true, clearActionBar);
+}
+
+void Pet::SetSpecialization(uint16 spec)
+{
+ if (m_petSpecialization == spec)
+ return;
+
+ // remove all the old spec's specalization spells, set the new spec, then add the new spec's spells
+ // clearActionBars is false because we'll be updating the pet actionbar later so we don't have to do it now
+ RemoveSpecializationSpells(false);
+ if (!sChrSpecializationStore.LookupEntry(spec))
+ {
+ m_petSpecialization = 0;
+ return;
+ }
+
+ m_petSpecialization = spec;
+ LearnSpecializationSpells();
+
+ // resend SMSG_PET_SPELLS_MESSAGE to remove old specialization spells from the pet action bar
+ CleanupActionBar();
+ GetOwner()->PetSpellInitialize();
+
+ WorldPackets::Pet::SetPetSpecialization setPetSpecialization;
+ setPetSpecialization.SpecID = m_petSpecialization;
+ GetOwner()->GetSession()->SendPacket(setPetSpecialization.Write());
+}
+
+std::string Pet::GenerateActionBarData() const
+{
+ std::ostringstream ss;
+
+ for (uint32 i = ACTION_BAR_INDEX_START; i < ACTION_BAR_INDEX_END; ++i)
+ {
+ ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << ' '
+ << uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << ' ';
+ }
+
+ return ss.str();
+}
diff --git a/src/server/game/Entities/Pet/Pet.h b/src/server/game/Entities/Pet/Pet.h
index 170b881fa50..38d78ec181b 100644
--- a/src/server/game/Entities/Pet/Pet.h
+++ b/src/server/game/Entities/Pet/Pet.h
@@ -119,26 +119,24 @@ class TC_GAME_API Pet : public Guardian
bool addSpell(uint32 spellId, ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL);
bool learnSpell(uint32 spell_id);
+ void learnSpells(std::vector<uint32> const& spellIds);
void learnSpellHighRank(uint32 spellid);
void InitLevelupSpellsForLevel();
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
+ void unlearnSpells(std::vector<uint32> const& spellIds, bool learn_prev, bool clear_ab = true);
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
void CleanupActionBar();
+ std::string GenerateActionBarData() const;
PetSpellMap m_spells;
AutoSpellList m_autospells;
void InitPetCreateSpells();
- bool resetTalents();
- static void resetTalentsForAllPetsOf(Player* owner, Pet* online_pet = nullptr);
- void InitTalentForLevel();
-
- uint8 GetMaxTalentPointsForLevel(uint8 level) const;
- uint8 GetFreeTalentPoints() const { return GetByteValue(UNIT_FIELD_BYTES_1, 1); }
- void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); }
-
- uint32 m_usedTalentCount;
+ uint16 GetSpecialization() { return m_petSpecialization; }
+ void SetSpecialization(uint16 spec);
+ void LearnSpecializationSpells();
+ void RemoveSpecializationSpells(bool clearActionBar);
uint32 GetGroupUpdateFlag() const { return m_groupUpdateMask; }
void SetGroupUpdateFlag(uint32 flag);
@@ -159,6 +157,8 @@ class TC_GAME_API Pet : public Guardian
DeclinedName *m_declinedname;
+ uint16 m_petSpecialization;
+
private:
void SaveToDB(uint32, uint32, uint32) override // override of Creature::SaveToDB - must not be called
{
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index e119c1d8199..c35e9bc99ca 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -1220,7 +1220,7 @@ void Player::Update(uint32 p_time)
m_zoneUpdateTimer -= p_time;
}
- if (m_timeSyncTimer > 0)
+ if (m_timeSyncTimer > 0 && !IsBeingTeleportedFar())
{
if (p_time >= m_timeSyncTimer)
SendTimeSync();
@@ -1635,18 +1635,10 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
if (!GetSession()->PlayerLogout())
{
- if (mEntry->IsDungeon())
- {
- WorldPackets::Instance::UpdateLastInstance updateLastInstance;
- updateLastInstance.MapID = mapid;
- SendDirectMessage(updateLastInstance.Write());
- }
-
- WorldPackets::Movement::NewWorld packet;
- packet.MapID = mapid;
- packet.Pos = static_cast<Position>(m_teleport_dest);
- packet.Reason = !(options & TELE_TO_SEAMLESS) ? NEW_WORLD_NORMAL : NEW_WORLD_SEAMLESS;
- SendDirectMessage(packet.Write());
+ WorldPackets::Movement::SuspendToken suspendToken;
+ suspendToken.SequenceIndex = m_movementCounter; // not incrementing
+ suspendToken.Reason = options & TELE_TO_SEAMLESS ? 2 : 1;
+ SendDirectMessage(suspendToken.Write());
}
// move packet sent by client always after far teleport
@@ -20140,7 +20132,7 @@ void Player::PetSpellInitialize()
WorldPackets::Pet::PetSpells petSpellsPacket;
petSpellsPacket.PetGUID = pet->GetGUID();
petSpellsPacket._CreatureFamily = pet->GetCreatureTemplate()->family; // creature family (required for pet talents)
- //petSpellsPacket.Specialization = pet->GetSpecialization(); NYI
+ petSpellsPacket.Specialization = pet->GetSpecialization();
petSpellsPacket.TimeLimit = pet->GetDuration();
petSpellsPacket.ReactState = pet->GetReactState();
petSpellsPacket.CommandState = charmInfo->GetCommandState();
@@ -21968,7 +21960,7 @@ void Player::UpdateTriggerVisibility()
creature->BuildValuesUpdateBlockForPlayer(&udata, this);
creature->RemoveFieldNotifyFlag(UF_FLAG_PUBLIC);
}
- else if (itr->IsGameObject())
+ else if (itr->IsAnyTypeGameObject())
{
GameObject* go = GetMap()->GetGameObject(*itr);
if (!go)
@@ -22186,6 +22178,14 @@ void Player::SetGroup(Group* group, int8 subgroup)
void Player::SendInitialPacketsBeforeAddToMap()
{
+ if (!(m_teleport_options & TELE_TO_SEAMLESS))
+ {
+ m_movementCounter = 0;
+ ResetTimeSync();
+ }
+
+ SendTimeSync();
+
/// Pass 'this' as argument because we're not stored in ObjectAccessor yet
GetSocial()->SendSocialList(this, SOCIAL_FLAG_ALL);
@@ -22275,9 +22275,6 @@ void Player::SendInitialPacketsAfterAddToMap()
GetZoneAndAreaId(newzone, newarea);
UpdateZone(newzone, newarea); // also call SendInitWorldStates();
- ResetTimeSync();
- SendTimeSync();
-
GetSession()->SendLoadCUFProfiles();
CastSpell(this, 836, true); // LOGINEFFECT
@@ -25581,14 +25578,6 @@ void Player::DeleteGarrison()
}
}
-void Player::SendMovementSetCanTransitionBetweenSwimAndFly(bool apply)
-{
- WorldPackets::Movement::MoveSetFlag packet(apply ? SMSG_MOVE_ENABLE_TRANSITION_BETWEEN_SWIM_AND_FLY : SMSG_MOVE_DISABLE_TRANSITION_BETWEEN_SWIM_AND_FLY);
- packet.MoverGUID = GetGUID();
- packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
-}
-
void Player::SendMovementSetCollisionHeight(float height)
{
WorldPackets::Movement::MoveSetCollisionHeight setCollisionHeight;
@@ -25746,7 +25735,6 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
{
case SUMMON_PET:
pet->InitPetCreateSpells();
- pet->InitTalentForLevel();
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
PetSpellInitialize();
break;
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index e46712d0580..4e4e116f65e 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -2417,10 +2417,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void ValidateMovementInfo(MovementInfo* mi);
- /*! These methods send different packets to the client in apply and unapply case.
- These methods are only sent to the current unit.
- */
- void SendMovementSetCanTransitionBetweenSwimAndFly(bool apply);
void SendMovementSetCollisionHeight(float height);
bool CanFly() const override { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_CAN_FLY); }
diff --git a/src/server/game/Entities/Taxi/TaxiPathGraph.cpp b/src/server/game/Entities/Taxi/TaxiPathGraph.cpp
index 709a5debf4d..3ff288b0c50 100644
--- a/src/server/game/Entities/Taxi/TaxiPathGraph.cpp
+++ b/src/server/game/Entities/Taxi/TaxiPathGraph.cpp
@@ -184,8 +184,9 @@ uint32 TaxiPathGraph::EdgeCost::EvaluateDistance(Player const* player) const
if (!(To->Flags & requireFlag))
return std::numeric_limits<uint16>::max();
- //if (To->ConditionID && !player->MeetsCondition(To->ConditionID))
- // return std::numeric_limits<uint16>::max();
+ if (PlayerConditionEntry const* condition = sPlayerConditionStore.LookupEntry(To->ConditionID))
+ if (!sConditionMgr->IsPlayerMeetingCondition(player, condition))
+ return std::numeric_limits<uint16>::max();
return Distance;
}
diff --git a/src/server/game/Entities/Unit/StatSystem.cpp b/src/server/game/Entities/Unit/StatSystem.cpp
index c3b8ef8e07e..035bc4d8703 100644
--- a/src/server/game/Entities/Unit/StatSystem.cpp
+++ b/src/server/game/Entities/Unit/StatSystem.cpp
@@ -301,7 +301,7 @@ void Player::UpdateAttackPowerAndDamage(bool ranged)
float val2 = 0.0f;
float level = float(getLevel());
- ChrClassesEntry const* entry = sChrClassesStore.LookupEntry(getClass());
+ ChrClassesEntry const* entry = sChrClassesStore.AssertEntry(getClass());
UnitMods unitMod = ranged ? UNIT_MOD_ATTACK_POWER_RANGED : UNIT_MOD_ATTACK_POWER;
uint16 index = UNIT_FIELD_ATTACK_POWER;
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index c473e4fb535..ab16b8275c9 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -9952,20 +9952,20 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
pet->SetSpeedRate(mtype, m_speed_rate[mtype]);
}
- if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER)
+ if (Player* playerMover = GetPlayerMover()) // unit controlled by a player.
{
// Send notification to self
WorldPackets::Movement::MoveSetSpeed selfpacket(moveTypeToOpcode[mtype][1]);
selfpacket.MoverGUID = GetGUID();
selfpacket.SequenceIndex = m_movementCounter++;
selfpacket.Speed = GetSpeed(mtype);
- ToPlayer()->GetSession()->SendPacket(selfpacket.Write());
+ playerMover->GetSession()->SendPacket(selfpacket.Write());
// Send notification to other players
WorldPackets::Movement::MoveUpdateSpeed packet(moveTypeToOpcode[mtype][2]);
packet.movementInfo = &m_movementInfo;
packet.Speed = GetSpeed(mtype);
- SendMessageToSet(packet.Write(), false);
+ playerMover->SendMessageToSet(packet.Write(), false);
}
else
{
@@ -11535,6 +11535,20 @@ void CharmInfo::SetSpellAutocast(SpellInfo const* spellInfo, bool state)
}
}
+Unit* Unit::GetMover() const
+{
+ if (Player const* player = ToPlayer())
+ return player->m_mover;
+ return nullptr;
+}
+
+Player* Unit::GetPlayerMover() const
+{
+ if (Unit* mover = GetMover())
+ return mover->ToPlayer();
+ return nullptr;
+}
+
bool Unit::isFrozen() const
{
return HasAuraState(AURA_STATE_FROZEN);
@@ -13417,12 +13431,16 @@ void Unit::SetRooted(bool apply, bool packetOnly /*= false*/)
{ SMSG_MOVE_SPLINE_ROOT, SMSG_MOVE_ROOT }
};
- if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER)
+ if (Player* playerMover = GetPlayerMover()) // unit controlled by a player.
{
WorldPackets::Movement::MoveSetFlag packet(rootOpcodeTable[apply][1]);
packet.MoverGUID = GetGUID();
packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
}
else
{
@@ -14903,8 +14921,9 @@ void Unit::SendTeleportPacket(Position& pos)
WorldPackets::Movement::MoveUpdateTeleport moveUpdateTeleport;
moveUpdateTeleport.movementInfo = &m_movementInfo;
+ Unit* broadcastSource = this;
- if (GetTypeId() == TYPEID_PLAYER)
+ if (Player* playerMover = GetPlayerMover())
{
WorldPackets::Movement::MoveTeleport moveTeleport;
moveTeleport.MoverGUID = GetGUID();
@@ -14914,7 +14933,9 @@ void Unit::SendTeleportPacket(Position& pos)
moveTeleport.TransportGUID = GetTransGUID();
moveTeleport.Facing = pos.GetOrientation();
moveTeleport.SequenceIndex = m_movementCounter++;
- ToPlayer()->SendDirectMessage(moveTeleport.Write());
+ playerMover->SendDirectMessage(moveTeleport.Write());
+
+ broadcastSource = playerMover;
}
else
{
@@ -14926,7 +14947,7 @@ void Unit::SendTeleportPacket(Position& pos)
}
// Broadcast the packet to everyone except self.
- SendMessageToSet(moveUpdateTeleport.Write(), false);
+ broadcastSource->SendMessageToSet(moveUpdateTeleport.Write(), false);
}
bool Unit::UpdatePosition(float x, float y, float z, float orientation, bool teleport)
@@ -15307,14 +15328,16 @@ bool Unit::SetDisableGravity(bool disable, bool packetOnly /*= false*/)
{ SMSG_MOVE_SPLINE_DISABLE_GRAVITY, SMSG_MOVE_DISABLE_GRAVITY }
};
- bool player = GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER;
-
- if (player)
+ if (Player* playerMover = GetPlayerMover())
{
WorldPackets::Movement::MoveSetFlag packet(gravityOpcodeTable[disable][1]);
packet.MoverGUID = GetGUID();
packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
}
else
{
@@ -15385,16 +15408,19 @@ bool Unit::SetCanFly(bool enable)
{ SMSG_MOVE_SPLINE_SET_FLYING, SMSG_MOVE_SET_CAN_FLY }
};
- bool player = GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER;
- if (!enable && player)
+ if (!enable && GetTypeId() == TYPEID_PLAYER)
ToPlayer()->SetFallInformation(0, GetPositionZ());
- if (player)
+ if (Player* playerMover = GetPlayerMover())
{
WorldPackets::Movement::MoveSetFlag packet(flyOpcodeTable[enable][1]);
packet.MoverGUID = GetGUID();
packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
}
else
{
@@ -15425,14 +15451,16 @@ bool Unit::SetWaterWalking(bool enable, bool packetOnly /*= false */)
{ SMSG_MOVE_SPLINE_SET_WATER_WALK, SMSG_MOVE_SET_WATER_WALK }
};
- bool player = GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER;
-
- if (player)
+ if (Player* playerMover = GetPlayerMover())
{
WorldPackets::Movement::MoveSetFlag packet(waterWalkingOpcodeTable[enable][1]);
packet.MoverGUID = GetGUID();
packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
}
else
{
@@ -15463,14 +15491,16 @@ bool Unit::SetFeatherFall(bool enable, bool packetOnly /*= false */)
{ SMSG_MOVE_SPLINE_SET_FEATHER_FALL, SMSG_MOVE_SET_FEATHER_FALL }
};
- bool player = GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER;
-
- if (player)
+ if (Player* playerMover = GetPlayerMover())
{
WorldPackets::Movement::MoveSetFlag packet(featherFallOpcodeTable[enable][1]);
packet.MoverGUID = GetGUID();
packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
}
else
{
@@ -15516,14 +15546,16 @@ bool Unit::SetHover(bool enable, bool packetOnly /*= false*/)
{ SMSG_MOVE_SPLINE_SET_HOVER, SMSG_MOVE_SET_HOVERING }
};
- bool player = GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER;
-
- if (player)
+ if (Player* playerMover = GetPlayerMover())
{
WorldPackets::Movement::MoveSetFlag packet(hoverOpcodeTable[enable][1]);
packet.MoverGUID = GetGUID();
packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
}
else
{
@@ -15551,14 +15583,16 @@ bool Unit::SetCollision(bool disable)
{ SMSG_MOVE_SPLINE_DISABLE_COLLISION, SMSG_MOVE_DISABLE_COLLISION }
};
- bool player = GetTypeId() == TYPEID_PLAYER && ToPlayer()->m_mover->GetTypeId() == TYPEID_PLAYER;
-
- if (player)
+ if (Player* playerMover = GetPlayerMover())
{
WorldPackets::Movement::MoveSetFlag packet(collisionOpcodeTable[disable][1]);
packet.MoverGUID = GetGUID();
packet.SequenceIndex = m_movementCounter++;
- SendMessageToSet(packet.Write(), true);
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
}
else
{
@@ -15570,6 +15604,40 @@ bool Unit::SetCollision(bool disable)
return true;
}
+bool Unit::SetCanTransitionBetweenSwimAndFly(bool enable)
+{
+ if (GetTypeId() != TYPEID_PLAYER)
+ return false;
+
+ if (enable == HasExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_SWIM_TO_FLY_TRANS))
+ return false;
+
+ if (enable)
+ AddExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_SWIM_TO_FLY_TRANS);
+ else
+ RemoveExtraUnitMovementFlag(MOVEMENTFLAG2_CAN_SWIM_TO_FLY_TRANS);
+
+ static OpcodeServer const swimToFlyTransOpcodeTable[2] =
+ {
+ SMSG_MOVE_ENABLE_TRANSITION_BETWEEN_SWIM_AND_FLY,
+ SMSG_MOVE_DISABLE_TRANSITION_BETWEEN_SWIM_AND_FLY
+ };
+
+ if (Player* playerMover = GetPlayerMover())
+ {
+ WorldPackets::Movement::MoveSetFlag packet(swimToFlyTransOpcodeTable[enable]);
+ packet.MoverGUID = GetGUID();
+ packet.SequenceIndex = m_movementCounter++;
+ playerMover->SendDirectMessage(packet.Write());
+
+ WorldPackets::Movement::MoveUpdate moveUpdate;
+ moveUpdate.movementInfo = &m_movementInfo;
+ SendMessageToSet(moveUpdate.Write(), playerMover);
+ }
+
+ return true;
+}
+
void Unit::SendSetVehicleRecId(uint32 vehicleId)
{
if (Player* player = ToPlayer())
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index 2032b8210d3..15792d4f543 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -1686,6 +1686,7 @@ class TC_GAME_API Unit : public WorldObject
bool SetFeatherFall(bool enable, bool packetOnly = false);
bool SetHover(bool enable, bool packetOnly = false);
bool SetCollision(bool disable);
+ bool SetCanTransitionBetweenSwimAndFly(bool enable);
void SendSetVehicleRecId(uint32 vehicleId);
void SetInFront(WorldObject const* target);
@@ -1759,7 +1760,8 @@ class TC_GAME_API Unit : public WorldObject
CharmInfo* InitCharmInfo();
void DeleteCharmInfo();
void UpdateCharmAI();
- //Player* GetMoverSource() const;
+ Unit* GetMover() const;
+ Player* GetPlayerMover() const;
Player* m_movedPlayer;
SharedVisionList const& GetSharedVisionList() { return m_sharedVision; }
void AddPlayerToVision(Player* player);
diff --git a/src/server/game/Garrison/Garrison.cpp b/src/server/game/Garrison/Garrison.cpp
index 59315b7ffc7..233fc1fa311 100644
--- a/src/server/game/Garrison/Garrison.cpp
+++ b/src/server/game/Garrison/Garrison.cpp
@@ -715,8 +715,7 @@ GameObject* Garrison::Plot::CreateGameObject(Map* map, GarrisonFactionIndex fact
Position const& pos = PacketInfo.PlotPos;
GameObject* building = new GameObject();
- if (!building->Create(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, 0, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(),
- 0.0f, 0.0f, 0.0f, 0.0f, 255, GO_STATE_READY))
+ if (!building->Create(entry, map, 0, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 255, GO_STATE_READY))
{
delete building;
return nullptr;
@@ -728,7 +727,7 @@ GameObject* Garrison::Plot::CreateGameObject(Map* map, GarrisonFactionIndex fact
{
Position const& pos2 = finalizeInfo->FactionInfo[faction].Pos;
GameObject* finalizer = new GameObject();
- if (finalizer->Create(map->GenerateLowGuid<HighGuid::GameObject>(), finalizeInfo->FactionInfo[faction].GameObjectId, map, 0, pos2.GetPositionX(), pos2.GetPositionY(),
+ if (finalizer->Create(finalizeInfo->FactionInfo[faction].GameObjectId, map, 0, pos2.GetPositionX(), pos2.GetPositionY(),
pos2.GetPositionZ(), pos2.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 255, GO_STATE_READY))
{
// set some spell id to make the object delete itself after use
diff --git a/src/server/game/Globals/ObjectAccessor.cpp b/src/server/game/Globals/ObjectAccessor.cpp
index 7cb21321f04..553cf67cc3c 100644
--- a/src/server/game/Globals/ObjectAccessor.cpp
+++ b/src/server/game/Globals/ObjectAccessor.cpp
@@ -84,12 +84,12 @@ WorldObject* ObjectAccessor::GetWorldObject(WorldObject const& p, ObjectGuid con
case HighGuid::Transport:
case HighGuid::GameObject: return GetGameObject(p, guid);
case HighGuid::Vehicle:
- case HighGuid::Creature: return GetCreature(p, guid);
+ case HighGuid::Creature: return GetCreature(p, guid);
case HighGuid::Pet: return GetPet(p, guid);
case HighGuid::DynamicObject: return GetDynamicObject(p, guid);
case HighGuid::AreaTrigger: return GetAreaTrigger(p, guid);
case HighGuid::Corpse: return GetCorpse(p, guid);
- default: return NULL;
+ default: return nullptr;
}
}
@@ -132,7 +132,7 @@ Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const& p, ObjectGuid con
break;
}
- return NULL;
+ return nullptr;
}
Corpse* ObjectAccessor::GetCorpse(WorldObject const& u, ObjectGuid const& guid)
@@ -145,11 +145,16 @@ GameObject* ObjectAccessor::GetGameObject(WorldObject const& u, ObjectGuid const
return u.GetMap()->GetGameObject(guid);
}
-Transport* ObjectAccessor::GetTransport(WorldObject const& u, ObjectGuid const& guid)
+Transport* ObjectAccessor::GetTransportOnMap(WorldObject const& u, ObjectGuid const& guid)
{
return u.GetMap()->GetTransport(guid);
}
+Transport* ObjectAccessor::GetTransport(ObjectGuid const& guid)
+{
+ return HashMapHolder<Transport>::Find(guid);
+}
+
DynamicObject* ObjectAccessor::GetDynamicObject(WorldObject const& u, ObjectGuid const& guid)
{
return u.GetMap()->GetDynamicObject(guid);
diff --git a/src/server/game/Globals/ObjectAccessor.h b/src/server/game/Globals/ObjectAccessor.h
index 83bbdf42239..4ac3b7a79d1 100644
--- a/src/server/game/Globals/ObjectAccessor.h
+++ b/src/server/game/Globals/ObjectAccessor.h
@@ -72,7 +72,8 @@ namespace ObjectAccessor
TC_GAME_API Object* GetObjectByTypeMask(WorldObject const&, ObjectGuid const&, uint32 typemask);
TC_GAME_API Corpse* GetCorpse(WorldObject const& u, ObjectGuid const& guid);
TC_GAME_API GameObject* GetGameObject(WorldObject const& u, ObjectGuid const& guid);
- TC_GAME_API Transport* GetTransport(WorldObject const& u, ObjectGuid const& guid);
+ TC_GAME_API Transport* GetTransportOnMap(WorldObject const& u, ObjectGuid const& guid);
+ TC_GAME_API Transport* GetTransport(ObjectGuid const& guid);
TC_GAME_API DynamicObject* GetDynamicObject(WorldObject const& u, ObjectGuid const& guid);
TC_GAME_API AreaTrigger* GetAreaTrigger(WorldObject const& u, ObjectGuid const& guid);
TC_GAME_API Unit* GetUnit(WorldObject const&, ObjectGuid const& guid);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index 63431e7e4ad..b8c6072f60e 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -54,6 +54,7 @@
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
+#include "Metric.h"
class LoginQueryHolder : public SQLQueryHolder
{
@@ -1079,7 +1080,17 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
// reset for all pets before pet loading
if (pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
- Pet::resetTalentsForAllPetsOf(pCurrChar);
+ {
+ // Delete all of the player's pet spells
+ PreparedStatement* stmtSpells = CharacterDatabase.GetPreparedStatement(CHAR_DEL_ALL_PET_SPELLS_BY_OWNER);
+ stmtSpells->setUInt64(0, pCurrChar->GetGUID().GetCounter());
+ CharacterDatabase.Execute(stmtSpells);
+
+ // Then reset all of the player's pet specualizations
+ PreparedStatement* stmtSpec = CharacterDatabase.GetPreparedStatement(CHAR_UPD_PET_SPECS_BY_OWNER);
+ stmtSpec->setUInt64(0, pCurrChar->GetGUID().GetCounter());
+ CharacterDatabase.Execute(stmtSpec);
+ }
// Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
pCurrChar->LoadPet();
@@ -1139,6 +1150,8 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder)
sScriptMgr->OnPlayerLogin(pCurrChar, firstLogin);
+ TC_METRIC_EVENT("player_events", "Login", pCurrChar->GetName());
+
delete holder;
}
@@ -2166,8 +2179,8 @@ void WorldSession::HandleCharRaceOrFactionChangeCallback(PreparedQueryResult res
uint32 title_alliance = it->first;
uint32 title_horde = it->second;
- CharTitlesEntry const* atitleInfo = sCharTitlesStore.LookupEntry(title_alliance);
- CharTitlesEntry const* htitleInfo = sCharTitlesStore.LookupEntry(title_horde);
+ CharTitlesEntry const* atitleInfo = sCharTitlesStore.AssertEntry(title_alliance);
+ CharTitlesEntry const* htitleInfo = sCharTitlesStore.AssertEntry(title_horde);
// new team
if (newTeamId == TEAM_ALLIANCE)
{
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 0d37806d170..afcced346df 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -485,7 +485,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::Misc::AreaTrigger& pack
return;
}
- if (!player->IsInAreaTriggerRadius(atEntry))
+ if (packet.Entered && !player->IsInAreaTriggerRadius(atEntry))
{
TC_LOG_DEBUG("network", "HandleAreaTriggerOpcode: Player '%s' (%s) too far, ignore Area Trigger ID: %u",
player->GetName().c_str(), player->GetGUID().ToString().c_str(), packet.AreaTriggerID);
@@ -535,8 +535,7 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPackets::Misc::AreaTrigger& pack
}
if (Battleground* bg = player->GetBattleground())
- if (bg->GetStatus() == STATUS_IN_PROGRESS)
- bg->HandleAreaTrigger(player, packet.AreaTriggerID, packet.Entered);
+ bg->HandleAreaTrigger(player, packet.AreaTriggerID, packet.Entered);
if (OutdoorPvP* pvp = player->GetOutdoorPvP())
if (pvp->HandleAreaTrigger(_player, packet.AreaTriggerID, packet.Entered))
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 0349fad420a..9e11c044f3e 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -31,6 +31,7 @@
#include "InstanceSaveMgr.h"
#include "ObjectMgr.h"
#include "Vehicle.h"
+#include "InstancePackets.h"
#include "MovementPackets.h"
#define MOVEMENT_PACKET_TIME_DELAY 0
@@ -95,6 +96,11 @@ void WorldSession::HandleMoveWorldportAckOpcode()
GetPlayer()->ResetMap();
GetPlayer()->SetMap(newMap);
+ WorldPackets::Movement::ResumeToken resumeToken;
+ resumeToken.SequenceIndex = _player->m_movementCounter;
+ resumeToken.Reason = seamlessTeleport ? 2 : 1;
+ SendPacket(resumeToken.Write());
+
if (!seamlessTeleport)
GetPlayer()->SendInitialPacketsBeforeAddToMap();
@@ -214,6 +220,30 @@ void WorldSession::HandleMoveWorldportAckOpcode()
GetPlayer()->ProcessDelayedOperations();
}
+void WorldSession::HandleSuspendTokenResponse(WorldPackets::Movement::SuspendTokenResponse& /*suspendTokenResponse*/)
+{
+ if (!_player->IsBeingTeleportedFar())
+ return;
+
+ WorldLocation const& loc = GetPlayer()->GetTeleportDest();
+
+ if (sMapStore.AssertEntry(loc.GetMapId())->IsDungeon())
+ {
+ WorldPackets::Instance::UpdateLastInstance updateLastInstance;
+ updateLastInstance.MapID = loc.GetMapId();
+ SendPacket(updateLastInstance.Write());
+ }
+
+ WorldPackets::Movement::NewWorld packet;
+ packet.MapID = loc.GetMapId();
+ packet.Pos.Relocate(loc);
+ packet.Reason = !_player->IsBeingTeleportedSeamlessly() ? NEW_WORLD_NORMAL : NEW_WORLD_SEAMLESS;
+ SendPacket(packet.Write());
+
+ if (_player->IsBeingTeleportedSeamlessly())
+ HandleMoveWorldportAckOpcode();
+}
+
void WorldSession::HandleMoveTeleportAck(WorldPackets::Movement::MoveTeleportAck& packet)
{
TC_LOG_DEBUG("network", "CMSG_MOVE_TELEPORT_ACK: Guid: %s, Sequence: %u, Time: %u", packet.MoverGUID.ToString().c_str(), packet.AckIndex, packet.MoveTime);
@@ -330,7 +360,7 @@ void WorldSession::HandleMovementOpcodes(WorldPackets::Movement::ClientPlayerMov
{
GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid);
if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT)
- movementInfo.transport.guid.Clear();
+ movementInfo.transport.Reset();
}
}
else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave
diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp
index faf13940eff..416ff57a6ab 100644
--- a/src/server/game/Handlers/PetHandler.cpp
+++ b/src/server/game/Handlers/PetHandler.cpp
@@ -515,10 +515,9 @@ void WorldSession::HandlePetSetAction(WorldPackets::Pet::PetSetAction& packet)
void WorldSession::HandlePetRename(WorldPackets::Pet::PetRename& packet)
{
ObjectGuid petguid = packet.RenameData.PetGUID;
- bool isdeclined = packet.RenameData.HasDeclinedNames;
std::string name = packet.RenameData.NewName;
- DeclinedName declinedname = packet.RenameData.DeclinedNames;
+ DeclinedName* declinedname = packet.RenameData.DeclinedNames.get_ptr();
Pet* pet = ObjectAccessor::GetPet(*_player, petguid);
// check it!
@@ -546,21 +545,21 @@ void WorldSession::HandlePetRename(WorldPackets::Pet::PetRename& packet)
pet->RemoveByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED);
- if (isdeclined)
+ if (declinedname)
{
std::wstring wname;
if (!Utf8toWStr(name, wname))
return;
- if (!ObjectMgr::CheckDeclinedNames(wname, declinedname))
+ if (!ObjectMgr::CheckDeclinedNames(wname, *declinedname))
{
- SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, &declinedname);
+ SendPetNameInvalid(PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME, name, declinedname);
return;
}
}
SQLTransaction trans = CharacterDatabase.BeginTransaction();
- if (isdeclined)
+ if (declinedname)
{
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_PET_DECLINEDNAME);
stmt->setUInt32(0, pet->GetCharmInfo()->GetPetNumber());
@@ -571,7 +570,7 @@ void WorldSession::HandlePetRename(WorldPackets::Pet::PetRename& packet)
stmt->setUInt64(1, _player->GetGUID().GetCounter());
for (uint8 i = 0; i < 5; i++)
- stmt->setString(i + 2, declinedname.name[i]);
+ stmt->setString(i + 2, declinedname->name[i]);
trans->Append(stmt);
}
@@ -721,8 +720,8 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec
WorldPackets::Pet::PetNameInvalid petNameInvalid;
petNameInvalid.Result = error;
petNameInvalid.RenameData.NewName = name;
- for (int i = 0; i < MAX_DECLINED_NAME_CASES; i++)
- petNameInvalid.RenameData.DeclinedNames.name[i] = declinedName[i].name[i];
+ if (declinedName)
+ petNameInvalid.RenameData.DeclinedNames = *declinedName;
SendPacket(petNameInvalid.Write());
}
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 323dec31e5a..8ce4a88ef5a 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -234,7 +234,7 @@ void WorldSession::HandleGameobjectReportUse(WorldPackets::GameObject::GameObjRe
if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(packet.Guid))
{
- if (go->AI()->GossipHello(_player))
+ if (go->AI()->GossipHello(_player, false))
return;
_player->UpdateCriteria(CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 37321344782..0b0868eddf4 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -255,6 +255,8 @@ i_scriptLock(false), _defaultLight(DB2Manager::GetDefaultMapLight(id))
//lets initialize visibility distance for map
Map::InitVisibilityDistance();
+ GetGuidSequenceGenerator<HighGuid::Transport>().Set(sObjectMgr->GetGenerator<HighGuid::Transport>().GetNextAfterMaxUsed());
+
sScriptMgr->OnCreateMap(this);
}
diff --git a/src/server/game/Maps/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp
index fd1f798d2d0..c60b4d8096d 100644
--- a/src/server/game/Maps/MapScripts.cpp
+++ b/src/server/game/Maps/MapScripts.cpp
@@ -322,14 +322,12 @@ void Map::ScriptsProcess()
source = HashMapHolder<Player>::Find(step.sourceGUID);
break;
case HighGuid::GameObject:
+ case HighGuid::Transport:
source = GetGameObject(step.sourceGUID);
break;
case HighGuid::Corpse:
source = GetCorpse(step.sourceGUID);
break;
- case HighGuid::Transport:
- source = GetTransport(step.sourceGUID);
- break;
default:
TC_LOG_ERROR("scripts", "%s source with unsupported high guid %s.",
step.script->GetDebugInfo().c_str(), step.sourceGUID.ToString().c_str());
@@ -353,14 +351,12 @@ void Map::ScriptsProcess()
target = HashMapHolder<Player>::Find(step.targetGUID);
break;
case HighGuid::GameObject:
+ case HighGuid::Transport:
target = GetGameObject(step.targetGUID);
break;
case HighGuid::Corpse:
target = GetCorpse(step.targetGUID);
break;
- case HighGuid::Transport:
- target = GetTransport(step.targetGUID);
- break;
default:
TC_LOG_ERROR("scripts", "%s target with unsupported high guid %s.",
step.script->GetDebugInfo().c_str(), step.targetGUID.ToString().c_str());
diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp
index 5de0133c342..4e695547c42 100644
--- a/src/server/game/Maps/TransportMgr.cpp
+++ b/src/server/game/Maps/TransportMgr.cpp
@@ -389,7 +389,7 @@ Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid
float o = tInfo->keyFrames.begin()->InitialOrientation;
// initialize the gameobject base
- ObjectGuid::LowType guidLow = guid ? guid : sObjectMgr->GetGenerator<HighGuid::Transport>().Generate();
+ ObjectGuid::LowType guidLow = guid ? guid : ASSERT_NOTNULL(map)->GenerateLowGuid<HighGuid::Transport>();
if (!trans->Create(guidLow, entry, mapId, x, y, z, o, 255))
{
delete trans;
diff --git a/src/server/game/Maps/ZoneScript.h b/src/server/game/Maps/ZoneScript.h
index e54c96dbb11..e83c6654912 100644
--- a/src/server/game/Maps/ZoneScript.h
+++ b/src/server/game/Maps/ZoneScript.h
@@ -30,7 +30,7 @@ class ZoneScript
virtual ~ZoneScript() { }
virtual uint32 GetCreatureEntry(ObjectGuid::LowType /*guidLow*/, CreatureData const* data) { return data->id; }
- virtual uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) { return entry; }
+ virtual uint32 GetGameObjectEntry(ObjectGuid::LowType /*spawnId*/, uint32 entry) { return entry; }
virtual void OnCreatureCreate(Creature* ) { }
virtual void OnCreatureRemove(Creature* ) { }
diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h
index a4051c7e865..6f1f4cb2409 100644
--- a/src/server/game/Miscellaneous/Language.h
+++ b/src/server/game/Miscellaneous/Language.h
@@ -1069,7 +1069,7 @@ enum TrinityStrings
LANG_COMMAND_NO_FROZEN_PLAYERS = 5004,
LANG_COMMAND_LIST_FREEZE = 5005,
LANG_COMMAND_PERMA_FROZEN_PLAYER = 5006,
- // = 5007, unused
+ LANG_PHASE_NOTFOUND = 5007,
LANG_INSTANCE_CLOSED = 5008,
LANG_COMMAND_PLAYED_TO_ALL = 5009,
LANG_NPCINFO_LINKGUID = 5010,
diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h
index a1114c0e28c..7320119f739 100644
--- a/src/server/game/Miscellaneous/SharedDefines.h
+++ b/src/server/game/Miscellaneous/SharedDefines.h
@@ -717,138 +717,138 @@ enum SpellAttr9
enum SpellAttr10
{
- SPELL_ATTR10_UNK0 = 0x00000001, // 0
- SPELL_ATTR10_UNK1 = 0x00000002, // 1
- SPELL_ATTR10_UNK2 = 0x00000004, // 2
- SPELL_ATTR10_UNK3 = 0x00000008, // 3
- SPELL_ATTR10_WATER_SPOUT = 0x00000010, // 4
- SPELL_ATTR10_UNK5 = 0x00000020, // 5
- SPELL_ATTR10_UNK6 = 0x00000040, // 6
- SPELL_ATTR10_TELEPORT_PLAYER = 0x00000080, // 7 4 Teleport Player spells
- SPELL_ATTR10_UNK8 = 0x00000100, // 8
- SPELL_ATTR10_UNK9 = 0x00000200, // 9
- SPELL_ATTR10_UNK10 = 0x00000400, // 10
- SPELL_ATTR10_HERB_GATHERING_MINING = 0x00000800, // 11 Only Herb Gathering and Mining
- SPELL_ATTR10_USE_SPELL_BASE_LEVEL_FOR_SCALING = 0x00001000, // 12
- SPELL_ATTR10_UNK13 = 0x00002000, // 13
- SPELL_ATTR10_UNK14 = 0x00004000, // 14
- SPELL_ATTR10_UNK15 = 0x00008000, // 15
- SPELL_ATTR10_UNK16 = 0x00010000, // 16
- SPELL_ATTR10_UNK17 = 0x00020000, // 17
- SPELL_ATTR10_UNK18 = 0x00040000, // 18
- SPELL_ATTR10_UNK19 = 0x00080000, // 19
- SPELL_ATTR10_UNK20 = 0x00100000, // 20
- SPELL_ATTR10_UNK21 = 0x00200000, // 21
- SPELL_ATTR10_UNK22 = 0x00400000, // 22
- SPELL_ATTR10_UNK23 = 0x00800000, // 23
- SPELL_ATTR10_UNK24 = 0x01000000, // 24
- SPELL_ATTR10_UNK25 = 0x02000000, // 25
- SPELL_ATTR10_UNK26 = 0x04000000, // 26
- SPELL_ATTR10_UNK27 = 0x08000000, // 27
- SPELL_ATTR10_UNK28 = 0x10000000, // 28
- SPELL_ATTR10_MOUNT_IS_NOT_ACCOUNT_WIDE = 0x20000000, // 29 This mount is stored per-character
- SPELL_ATTR10_UNK30 = 0x40000000, // 30
- SPELL_ATTR10_UNK31 = 0x80000000 // 31
+ SPELL_ATTR10_UNK0 = 0x00000001, // 0
+ SPELL_ATTR10_UNK1 = 0x00000002, // 1
+ SPELL_ATTR10_UNK2 = 0x00000004, // 2
+ SPELL_ATTR10_UNK3 = 0x00000008, // 3
+ SPELL_ATTR10_WATER_SPOUT = 0x00000010, // 4
+ SPELL_ATTR10_UNK5 = 0x00000020, // 5
+ SPELL_ATTR10_UNK6 = 0x00000040, // 6
+ SPELL_ATTR10_TELEPORT_PLAYER = 0x00000080, // 7 4 Teleport Player spells
+ SPELL_ATTR10_UNK8 = 0x00000100, // 8
+ SPELL_ATTR10_UNK9 = 0x00000200, // 9
+ SPELL_ATTR10_UNK10 = 0x00000400, // 10
+ SPELL_ATTR10_HERB_GATHERING_MINING = 0x00000800, // 11 Only Herb Gathering and Mining
+ SPELL_ATTR10_USE_SPELL_BASE_LEVEL_FOR_SCALING= 0x00001000, // 12
+ SPELL_ATTR10_UNK13 = 0x00002000, // 13
+ SPELL_ATTR10_UNK14 = 0x00004000, // 14
+ SPELL_ATTR10_UNK15 = 0x00008000, // 15
+ SPELL_ATTR10_UNK16 = 0x00010000, // 16
+ SPELL_ATTR10_UNK17 = 0x00020000, // 17
+ SPELL_ATTR10_UNK18 = 0x00040000, // 18
+ SPELL_ATTR10_UNK19 = 0x00080000, // 19
+ SPELL_ATTR10_UNK20 = 0x00100000, // 20
+ SPELL_ATTR10_UNK21 = 0x00200000, // 21
+ SPELL_ATTR10_UNK22 = 0x00400000, // 22
+ SPELL_ATTR10_UNK23 = 0x00800000, // 23
+ SPELL_ATTR10_UNK24 = 0x01000000, // 24
+ SPELL_ATTR10_UNK25 = 0x02000000, // 25
+ SPELL_ATTR10_UNK26 = 0x04000000, // 26
+ SPELL_ATTR10_UNK27 = 0x08000000, // 27
+ SPELL_ATTR10_UNK28 = 0x10000000, // 28
+ SPELL_ATTR10_MOUNT_IS_NOT_ACCOUNT_WIDE = 0x20000000, // 29 This mount is stored per-character
+ SPELL_ATTR10_UNK30 = 0x40000000, // 30
+ SPELL_ATTR10_UNK31 = 0x80000000 // 31
};
enum SpellAttr11
{
- SPELL_ATTR11_UNK0 = 0x00000001, // 0
- SPELL_ATTR11_UNK1 = 0x00000002, // 1
- SPELL_ATTR11_SCALES_WITH_ITEM_LEVEL = 0x00000004, // 2
- SPELL_ATTR11_UNK3 = 0x00000008, // 3
- SPELL_ATTR11_UNK4 = 0x00000010, // 4
- SPELL_ATTR11_UNK5 = 0x00000020, // 5
- SPELL_ATTR11_UNK6 = 0x00000040, // 6
- SPELL_ATTR11_RANK_IGNORES_CASTER_LEVEL = 0x00000080, // 7 Spell_C_GetSpellRank returns SpellLevels->MaxLevel * 5 instead of std::min(SpellLevels->MaxLevel, caster->Level) * 5
- SPELL_ATTR11_UNK8 = 0x00000100, // 8
- SPELL_ATTR11_UNK9 = 0x00000200, // 9
- SPELL_ATTR11_UNK10 = 0x00000400, // 10
- SPELL_ATTR11_UNK11 = 0x00000800, // 11
- SPELL_ATTR11_UNK12 = 0x00001000, // 12
- SPELL_ATTR11_UNK13 = 0x00002000, // 13
- SPELL_ATTR11_UNK14 = 0x00004000, // 14
- SPELL_ATTR11_UNK15 = 0x00008000, // 15
- SPELL_ATTR11_NOT_USABLE_IN_CHALLENGE_MODE = 0x00010000, // 16
- SPELL_ATTR11_UNK17 = 0x00020000, // 17
- SPELL_ATTR11_UNK18 = 0x00040000, // 18
- SPELL_ATTR11_UNK19 = 0x00080000, // 19
- SPELL_ATTR11_UNK20 = 0x00100000, // 20
- SPELL_ATTR11_UNK21 = 0x00200000, // 21
- SPELL_ATTR11_UNK22 = 0x00400000, // 22
- SPELL_ATTR11_UNK23 = 0x00800000, // 23
- SPELL_ATTR11_UNK24 = 0x01000000, // 24
- SPELL_ATTR11_UNK25 = 0x02000000, // 25
- SPELL_ATTR11_UNK26 = 0x04000000, // 26
- SPELL_ATTR11_UNK27 = 0x08000000, // 27
- SPELL_ATTR11_UNK28 = 0x10000000, // 28
- SPELL_ATTR11_UNK29 = 0x20000000, // 29
- SPELL_ATTR11_UNK30 = 0x40000000, // 30
- SPELL_ATTR11_UNK31 = 0x80000000 // 31
+ SPELL_ATTR11_UNK0 = 0x00000001, // 0
+ SPELL_ATTR11_UNK1 = 0x00000002, // 1
+ SPELL_ATTR11_SCALES_WITH_ITEM_LEVEL = 0x00000004, // 2
+ SPELL_ATTR11_UNK3 = 0x00000008, // 3
+ SPELL_ATTR11_UNK4 = 0x00000010, // 4
+ SPELL_ATTR11_UNK5 = 0x00000020, // 5
+ SPELL_ATTR11_UNK6 = 0x00000040, // 6
+ SPELL_ATTR11_RANK_IGNORES_CASTER_LEVEL = 0x00000080, // 7 Spell_C_GetSpellRank returns SpellLevels->MaxLevel * 5 instead of std::min(SpellLevels->MaxLevel, caster->Level) * 5
+ SPELL_ATTR11_UNK8 = 0x00000100, // 8
+ SPELL_ATTR11_UNK9 = 0x00000200, // 9
+ SPELL_ATTR11_UNK10 = 0x00000400, // 10
+ SPELL_ATTR11_UNK11 = 0x00000800, // 11
+ SPELL_ATTR11_UNK12 = 0x00001000, // 12
+ SPELL_ATTR11_UNK13 = 0x00002000, // 13
+ SPELL_ATTR11_UNK14 = 0x00004000, // 14
+ SPELL_ATTR11_UNK15 = 0x00008000, // 15
+ SPELL_ATTR11_NOT_USABLE_IN_CHALLENGE_MODE = 0x00010000, // 16
+ SPELL_ATTR11_UNK17 = 0x00020000, // 17
+ SPELL_ATTR11_UNK18 = 0x00040000, // 18
+ SPELL_ATTR11_UNK19 = 0x00080000, // 19
+ SPELL_ATTR11_UNK20 = 0x00100000, // 20
+ SPELL_ATTR11_UNK21 = 0x00200000, // 21
+ SPELL_ATTR11_UNK22 = 0x00400000, // 22
+ SPELL_ATTR11_UNK23 = 0x00800000, // 23
+ SPELL_ATTR11_UNK24 = 0x01000000, // 24
+ SPELL_ATTR11_UNK25 = 0x02000000, // 25
+ SPELL_ATTR11_UNK26 = 0x04000000, // 26
+ SPELL_ATTR11_UNK27 = 0x08000000, // 27
+ SPELL_ATTR11_UNK28 = 0x10000000, // 28
+ SPELL_ATTR11_UNK29 = 0x20000000, // 29
+ SPELL_ATTR11_UNK30 = 0x40000000, // 30
+ SPELL_ATTR11_UNK31 = 0x80000000 // 31
};
enum SpellAttr12
{
- SPELL_ATTR12_UNK0 = 0x00000001, // 0
- SPELL_ATTR12_UNK1 = 0x00000002, // 1
- SPELL_ATTR12_UNK2 = 0x00000004, // 2
- SPELL_ATTR12_UNK3 = 0x00000008, // 3
- SPELL_ATTR12_UNK4 = 0x00000010, // 4
- SPELL_ATTR12_UNK5 = 0x00000020, // 5
- SPELL_ATTR12_UNK6 = 0x00000040, // 6
- SPELL_ATTR12_UNK7 = 0x00000080, // 7
- SPELL_ATTR12_UNK8 = 0x00000100, // 8
- SPELL_ATTR12_UNK9 = 0x00000200, // 9
- SPELL_ATTR12_UNK10 = 0x00000400, // 10
- SPELL_ATTR12_UNK11 = 0x00000800, // 11
- SPELL_ATTR12_UNK12 = 0x00001000, // 12
- SPELL_ATTR12_UNK13 = 0x00002000, // 13
- SPELL_ATTR12_UNK14 = 0x00004000, // 14
- SPELL_ATTR12_UNK15 = 0x00008000, // 15
- SPELL_ATTR12_UNK16 = 0x00010000, // 16
- SPELL_ATTR12_UNK17 = 0x00020000, // 17
- SPELL_ATTR12_UNK18 = 0x00040000, // 18
- SPELL_ATTR12_UNK19 = 0x00080000, // 19
- SPELL_ATTR12_UNK20 = 0x00100000, // 20
- SPELL_ATTR12_UNK21 = 0x00200000, // 21
- SPELL_ATTR12_UNK22 = 0x00400000, // 22
- SPELL_ATTR12_UNK23 = 0x00800000, // 23
- SPELL_ATTR12_UNK24 = 0x01000000, // 24
- SPELL_ATTR12_UNK25 = 0x02000000, // 25
- SPELL_ATTR12_UNK26 = 0x04000000, // 26
- SPELL_ATTR12_IS_READINESS_SPELL = 0x08000000, // 27
- SPELL_ATTR12_UNK28 = 0x10000000, // 28
- SPELL_ATTR12_UNK29 = 0x20000000, // 29
- SPELL_ATTR12_UNK30 = 0x40000000, // 30
- SPELL_ATTR12_UNK31 = 0x80000000 // 31
+ SPELL_ATTR12_UNK0 = 0x00000001, // 0
+ SPELL_ATTR12_UNK1 = 0x00000002, // 1
+ SPELL_ATTR12_UNK2 = 0x00000004, // 2
+ SPELL_ATTR12_UNK3 = 0x00000008, // 3
+ SPELL_ATTR12_UNK4 = 0x00000010, // 4
+ SPELL_ATTR12_UNK5 = 0x00000020, // 5
+ SPELL_ATTR12_UNK6 = 0x00000040, // 6
+ SPELL_ATTR12_UNK7 = 0x00000080, // 7
+ SPELL_ATTR12_UNK8 = 0x00000100, // 8
+ SPELL_ATTR12_UNK9 = 0x00000200, // 9
+ SPELL_ATTR12_UNK10 = 0x00000400, // 10
+ SPELL_ATTR12_UNK11 = 0x00000800, // 11
+ SPELL_ATTR12_UNK12 = 0x00001000, // 12
+ SPELL_ATTR12_UNK13 = 0x00002000, // 13
+ SPELL_ATTR12_UNK14 = 0x00004000, // 14
+ SPELL_ATTR12_UNK15 = 0x00008000, // 15
+ SPELL_ATTR12_UNK16 = 0x00010000, // 16
+ SPELL_ATTR12_UNK17 = 0x00020000, // 17
+ SPELL_ATTR12_UNK18 = 0x00040000, // 18
+ SPELL_ATTR12_UNK19 = 0x00080000, // 19
+ SPELL_ATTR12_UNK20 = 0x00100000, // 20
+ SPELL_ATTR12_UNK21 = 0x00200000, // 21
+ SPELL_ATTR12_UNK22 = 0x00400000, // 22
+ SPELL_ATTR12_UNK23 = 0x00800000, // 23
+ SPELL_ATTR12_IS_GARRISON_BUFF = 0x01000000, // 24
+ SPELL_ATTR12_UNK25 = 0x02000000, // 25
+ SPELL_ATTR12_UNK26 = 0x04000000, // 26
+ SPELL_ATTR12_IS_READINESS_SPELL = 0x08000000, // 27
+ SPELL_ATTR12_UNK28 = 0x10000000, // 28
+ SPELL_ATTR12_UNK29 = 0x20000000, // 29
+ SPELL_ATTR12_UNK30 = 0x40000000, // 30
+ SPELL_ATTR12_UNK31 = 0x80000000 // 31
};
enum SpellAttr13
{
- SPELL_ATTR13_UNK0 = 0x00000001, // 0
- SPELL_ATTR13_UNK1 = 0x00000002, // 1
- SPELL_ATTR13_UNK2 = 0x00000004, // 2
- SPELL_ATTR13_UNK3 = 0x00000008, // 3
- SPELL_ATTR13_UNK4 = 0x00000010, // 4
- SPELL_ATTR13_UNK5 = 0x00000020, // 5
- SPELL_ATTR13_UNK6 = 0x00000040, // 6
- SPELL_ATTR13_UNK7 = 0x00000080, // 7
- SPELL_ATTR13_UNK8 = 0x00000100, // 8
- SPELL_ATTR13_UNK9 = 0x00000200, // 9
- SPELL_ATTR13_UNK10 = 0x00000400, // 10
- SPELL_ATTR13_UNK11 = 0x00000800, // 11
- SPELL_ATTR13_UNK12 = 0x00001000, // 12
- SPELL_ATTR13_UNK13 = 0x00002000, // 13
- SPELL_ATTR13_UNK14 = 0x00004000, // 14
- SPELL_ATTR13_UNK15 = 0x00008000, // 15
- SPELL_ATTR13_UNK16 = 0x00010000, // 16
- SPELL_ATTR13_UNK17 = 0x00020000, // 17
- SPELL_ATTR13_ACTIVATES_REQUIRED_SHAPESHIFT = 0x00040000, // 18
- SPELL_ATTR13_UNK19 = 0x00080000, // 19
- SPELL_ATTR13_UNK20 = 0x00100000, // 20
- SPELL_ATTR13_UNK21 = 0x00200000, // 21
- SPELL_ATTR13_UNK22 = 0x00400000, // 22
- SPELL_ATTR13_UNK23 = 0x00800000 // 23
+ SPELL_ATTR13_UNK0 = 0x00000001, // 0
+ SPELL_ATTR13_UNK1 = 0x00000002, // 1
+ SPELL_ATTR13_UNK2 = 0x00000004, // 2
+ SPELL_ATTR13_UNK3 = 0x00000008, // 3
+ SPELL_ATTR13_UNK4 = 0x00000010, // 4
+ SPELL_ATTR13_UNK5 = 0x00000020, // 5
+ SPELL_ATTR13_UNK6 = 0x00000040, // 6
+ SPELL_ATTR13_UNK7 = 0x00000080, // 7
+ SPELL_ATTR13_UNK8 = 0x00000100, // 8
+ SPELL_ATTR13_UNK9 = 0x00000200, // 9
+ SPELL_ATTR13_UNK10 = 0x00000400, // 10
+ SPELL_ATTR13_UNK11 = 0x00000800, // 11
+ SPELL_ATTR13_UNK12 = 0x00001000, // 12
+ SPELL_ATTR13_UNK13 = 0x00002000, // 13
+ SPELL_ATTR13_UNK14 = 0x00004000, // 14
+ SPELL_ATTR13_UNK15 = 0x00008000, // 15
+ SPELL_ATTR13_UNK16 = 0x00010000, // 16
+ SPELL_ATTR13_UNK17 = 0x00020000, // 17
+ SPELL_ATTR13_ACTIVATES_REQUIRED_SHAPESHIFT = 0x00040000, // 18
+ SPELL_ATTR13_UNK19 = 0x00080000, // 19
+ SPELL_ATTR13_UNK20 = 0x00100000, // 20
+ SPELL_ATTR13_UNK21 = 0x00200000, // 21
+ SPELL_ATTR13_UNK22 = 0x00400000, // 22
+ SPELL_ATTR13_UNK23 = 0x00800000 // 23
};
#define MIN_TALENT_GROUP 0
@@ -1229,7 +1229,7 @@ enum SpellEffectName
SPELL_EFFECT_203 = 203,
SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY = 204,
SPELL_EFFECT_LAUNCH_QUEST_CHOICE = 205,
- SPELL_EFFECT_206 = 206,
+ SPELL_EFFECT_ALTER_ITEM = 206, // NYI
SPELL_EFFECT_LAUNCH_QUEST_TASK = 207, // Starts one of the "progress bar" quests
SPELL_EFFECT_208 = 208,
SPELL_EFFECT_209 = 209,
@@ -2322,18 +2322,18 @@ enum GameobjectTypes : uint8 // (6.0.3.19103)
enum GameObjectFlags
{
- GO_FLAG_IN_USE = 0x00000001, // disables interaction while animated
- GO_FLAG_LOCKED = 0x00000002, // require key, spell, event, etc to be opened. Makes "Locked" appear in tooltip
- GO_FLAG_INTERACT_COND = 0x00000004, // cannot interact (condition to interact - requires GO_DYNFLAG_LO_ACTIVATE to enable interaction clientside)
- GO_FLAG_TRANSPORT = 0x00000008, // any kind of transport? Object can transport (elevator, boat, car)
- GO_FLAG_NOT_SELECTABLE = 0x00000010, // not selectable even in GM mode
- GO_FLAG_NODESPAWN = 0x00000020, // never despawn, typically for doors, they just change state
- GO_FLAG_AI_OBSTACLE = 0x00000040,
- GO_FLAG_FREEZE_ANIMATION = 0x00000080,
- GO_FLAG_DAMAGED = 0x00000200,
- GO_FLAG_DESTROYED = 0x00000400,
+ GO_FLAG_IN_USE = 0x00000001, // disables interaction while animated
+ GO_FLAG_LOCKED = 0x00000002, // require key, spell, event, etc to be opened. Makes "Locked" appear in tooltip
+ GO_FLAG_INTERACT_COND = 0x00000004, // cannot interact (condition to interact - requires GO_DYNFLAG_LO_ACTIVATE to enable interaction clientside)
+ GO_FLAG_TRANSPORT = 0x00000008, // any kind of transport? Object can transport (elevator, boat, car)
+ GO_FLAG_NOT_SELECTABLE = 0x00000010, // not selectable even in GM mode
+ GO_FLAG_NODESPAWN = 0x00000020, // never despawn, typically for doors, they just change state
+ GO_FLAG_AI_OBSTACLE = 0x00000040, // makes the client register the object in something called AIObstacleMgr, unknown what it does
+ GO_FLAG_FREEZE_ANIMATION = 0x00000080,
+ GO_FLAG_DAMAGED = 0x00000200,
+ GO_FLAG_DESTROYED = 0x00000400,
GO_FLAG_INTERACT_DISTANCE_USES_TEMPLATE_MODEL = 0x00080000, // client checks interaction distance from model sent in SMSG_QUERY_GAMEOBJECT_RESPONSE instead of GAMEOBJECT_DISPLAYID
- GO_FLAG_MAP_OBJECT = 0x00100000 // pre-7.0 model loading used to be controlled by file extension (wmo vs m2)
+ GO_FLAG_MAP_OBJECT = 0x00100000 // pre-7.0 model loading used to be controlled by file extension (wmo vs m2)
};
enum GameObjectDynamicLowFlags
diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp
index fe09aa26af2..39087269e54 100644
--- a/src/server/game/Movement/PathGenerator.cpp
+++ b/src/server/game/Movement/PathGenerator.cpp
@@ -26,6 +26,7 @@
#include "DisableMgr.h"
#include "DetourCommon.h"
#include "DetourNavMeshQuery.h"
+#include "Metric.h"
////////////////// PathGenerator //////////////////
PathGenerator::PathGenerator(const Unit* owner) :
@@ -62,6 +63,8 @@ bool PathGenerator::CalculatePath(float destX, float destY, float destZ, bool fo
if (!Trinity::IsValidMapCoord(destX, destY, destZ) || !Trinity::IsValidMapCoord(x, y, z))
return false;
+ TC_METRIC_EVENT("mmap_events", "CalculatePath", "");
+
G3D::Vector3 dest(destX, destY, destZ);
SetEndPosition(dest);
diff --git a/src/server/game/Server/Packets/BattlenetPackets.h b/src/server/game/Server/Packets/BattlenetPackets.h
index f059b39b75f..76aa499799c 100644
--- a/src/server/game/Server/Packets/BattlenetPackets.h
+++ b/src/server/game/Server/Packets/BattlenetPackets.h
@@ -76,8 +76,8 @@ namespace WorldPackets
WorldPacket const* Write() override;
- uint32 Token;
- bool Allow;
+ uint32 Token = 0;
+ bool Allow = false;
ByteBuffer Ticket;
};
diff --git a/src/server/game/Server/Packets/BlackMarketPackets.h b/src/server/game/Server/Packets/BlackMarketPackets.h
index 1193a9f0147..19853a212a5 100644
--- a/src/server/game/Server/Packets/BlackMarketPackets.h
+++ b/src/server/game/Server/Packets/BlackMarketPackets.h
@@ -122,7 +122,7 @@ namespace WorldPackets
int32 MarketID = 0;
Item::ItemInstance Item;
- int32 RandomPropertiesID;
+ int32 RandomPropertiesID = 0;
};
class BlackMarketWon final : public ServerPacket
@@ -134,7 +134,7 @@ namespace WorldPackets
int32 MarketID = 0;
Item::ItemInstance Item;
- int32 RandomPropertiesID;
+ int32 RandomPropertiesID = 0;
};
}
}
diff --git a/src/server/game/Server/Packets/GuildFinderPackets.cpp b/src/server/game/Server/Packets/GuildFinderPackets.cpp
index 137b5f29986..eb2f1178925 100644
--- a/src/server/game/Server/Packets/GuildFinderPackets.cpp
+++ b/src/server/game/Server/Packets/GuildFinderPackets.cpp
@@ -92,8 +92,8 @@ ByteBuffer& operator<<(ByteBuffer& data, WorldPackets::GuildFinder::LFGuildAppli
WorldPacket const* WorldPackets::GuildFinder::LFGuildApplications::Write()
{
- _worldPacket << uint32(Application.size());
_worldPacket << int32(NumRemaining);
+ _worldPacket << uint32(Application.size());
for (LFGuildApplicationData const& application : Application)
_worldPacket << application;
diff --git a/src/server/game/Server/Packets/MovementPackets.cpp b/src/server/game/Server/Packets/MovementPackets.cpp
index 16b3b98646f..ff8337c88c4 100644
--- a/src/server/game/Server/Packets/MovementPackets.cpp
+++ b/src/server/game/Server/Packets/MovementPackets.cpp
@@ -736,3 +736,26 @@ WorldPacket const* WorldPackets::Movement::SummonRequest::Write()
return &_worldPacket;
}
+
+WorldPacket const* WorldPackets::Movement::SuspendToken::Write()
+{
+ _worldPacket << uint32(SequenceIndex);
+ _worldPacket.WriteBits(Reason, 2);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
+
+void WorldPackets::Movement::SuspendTokenResponse::Read()
+{
+ _worldPacket >> SequenceIndex;
+}
+
+WorldPacket const* WorldPackets::Movement::ResumeToken::Write()
+{
+ _worldPacket << uint32(SequenceIndex);
+ _worldPacket.WriteBits(Reason, 2);
+ _worldPacket.FlushBits();
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/MovementPackets.h b/src/server/game/Server/Packets/MovementPackets.h
index 7b56074db28..1c21c034c86 100644
--- a/src/server/game/Server/Packets/MovementPackets.h
+++ b/src/server/game/Server/Packets/MovementPackets.h
@@ -504,6 +504,38 @@ namespace WorldPackets
SummonReason Reason = SPELL;
bool SkipStartingArea = false;
};
+
+ class SuspendToken final : public ServerPacket
+ {
+ public:
+ SuspendToken() : ServerPacket(SMSG_SUSPEND_TOKEN, 4 + 1) { }
+
+ WorldPacket const* Write() override;
+
+ uint32 SequenceIndex = 1;
+ uint32 Reason = 1;
+ };
+
+ class SuspendTokenResponse final : public ClientPacket
+ {
+ public:
+ SuspendTokenResponse(WorldPacket&& packet) : ClientPacket(CMSG_SUSPEND_TOKEN_RESPONSE, std::move(packet)) { }
+
+ void Read() override;
+
+ uint32 SequenceIndex = 0;
+ };
+
+ class ResumeToken final : public ServerPacket
+ {
+ public:
+ ResumeToken() : ServerPacket(SMSG_RESUME_TOKEN, 4 + 1) { }
+
+ WorldPacket const* Write() override;
+
+ uint32 SequenceIndex = 1;
+ uint32 Reason = 1;
+ };
}
ByteBuffer& operator<<(ByteBuffer& data, Movement::MonsterSplineFilterKey const& monsterSplineFilterKey);
diff --git a/src/server/game/Server/Packets/PetPackets.cpp b/src/server/game/Server/Packets/PetPackets.cpp
index b07a74dfb10..cb5593167f0 100644
--- a/src/server/game/Server/Packets/PetPackets.cpp
+++ b/src/server/game/Server/Packets/PetPackets.cpp
@@ -99,19 +99,19 @@ WorldPacket const* WorldPackets::Pet::PetNameInvalid::Write()
_worldPacket << uint8(RenameData.NewName.length());
- _worldPacket.WriteBit(RenameData.HasDeclinedNames);
+ _worldPacket.WriteBit(RenameData.DeclinedNames.is_initialized());
_worldPacket.FlushBits();
- if (RenameData.HasDeclinedNames)
+ if (RenameData.DeclinedNames)
{
for (int32 i = 0; i < MAX_DECLINED_NAME_CASES; i++)
{
- _worldPacket.WriteBits(RenameData.DeclinedNames.name[i].length(), 7);
+ _worldPacket.WriteBits(RenameData.DeclinedNames->name[i].length(), 7);
_worldPacket.FlushBits();
}
for (int32 i = 0; i < MAX_DECLINED_NAME_CASES; i++)
- _worldPacket << RenameData.DeclinedNames.name[i];
+ _worldPacket << RenameData.DeclinedNames->name[i];
}
_worldPacket.WriteString(RenameData.NewName);
@@ -126,15 +126,15 @@ void WorldPackets::Pet::PetRename::Read()
int8 nameLen = 0;
_worldPacket >> nameLen;
- RenameData.HasDeclinedNames = _worldPacket.ReadBit();
- if (RenameData.HasDeclinedNames)
+ if (_worldPacket.ReadBit())
{
+ RenameData.DeclinedNames = boost::in_place();
int32 count[MAX_DECLINED_NAME_CASES];
for (int32 i = 0; i < MAX_DECLINED_NAME_CASES; i++)
count[i] = _worldPacket.ReadBits(7);
for (int32 i = 0; i < MAX_DECLINED_NAME_CASES; i++)
- RenameData.DeclinedNames.name[i] = _worldPacket.ReadString(count[i]);
+ RenameData.DeclinedNames->name[i] = _worldPacket.ReadString(count[i]);
}
RenameData.NewName = _worldPacket.ReadString(nameLen);
@@ -185,3 +185,10 @@ void WorldPackets::Pet::PetCancelAura::Read()
_worldPacket >> PetGUID;
_worldPacket >> SpellID;
}
+
+WorldPacket const* WorldPackets::Pet::SetPetSpecialization::Write()
+{
+ _worldPacket << uint16(SpecID);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/PetPackets.h b/src/server/game/Server/Packets/PetPackets.h
index 500fef3a249..813d82d4827 100644
--- a/src/server/game/Server/Packets/PetPackets.h
+++ b/src/server/game/Server/Packets/PetPackets.h
@@ -163,8 +163,7 @@ namespace WorldPackets
ObjectGuid PetGUID;
int32 PetNumber = 0;
std::string NewName;
- bool HasDeclinedNames = false;
- DeclinedName DeclinedNames;
+ Optional<DeclinedName> DeclinedNames;
};
class PetNameInvalid final : public ServerPacket
@@ -226,6 +225,16 @@ namespace WorldPackets
int32 SpellID = 0;
};
+ class SetPetSpecialization final : public ServerPacket
+ {
+ public:
+ SetPetSpecialization() : ServerPacket(SMSG_SET_PET_SPECIALIZATION, 2) { }
+
+ WorldPacket const* Write() override;
+
+ uint16 SpecID = 0;
+ };
+
}
}
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 8efd98c19f9..1f9badfbdef 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -714,7 +714,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_SET_PARTY_ASSIGNMENT, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Party::SetPartyAssignment, &WorldSession::HandleSetPartyAssignment);
DEFINE_HANDLER(CMSG_SET_PARTY_LEADER, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Party::SetPartyLeader, &WorldSession::HandleSetPartyLeaderOpcode);
DEFINE_HANDLER(CMSG_SET_PET_SLOT, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_SET_PLAYER_DECLINED_NAMES, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Character::SetPlayerDeclinedNames, &WorldSession::HandleSetPlayerDeclinedNames);
+ DEFINE_HANDLER(CMSG_SET_PLAYER_DECLINED_NAMES, STATUS_AUTHED, PROCESS_THREADUNSAFE, WorldPackets::Character::SetPlayerDeclinedNames, &WorldSession::HandleSetPlayerDeclinedNames);
DEFINE_HANDLER(CMSG_SET_PREFERRED_CEMETERY, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
DEFINE_HANDLER(CMSG_SET_PVP, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::SetPvP, &WorldSession::HandleSetPvP);
DEFINE_HANDLER(CMSG_SET_RAID_DIFFICULTY, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Misc::SetRaidDifficulty, &WorldSession::HandleSetRaidDifficultyOpcode);
@@ -749,7 +749,7 @@ void OpcodeTable::Initialize()
DEFINE_HANDLER(CMSG_SUPPORT_TICKET_SUBMIT_COMPLAINT, STATUS_UNHANDLED, PROCESS_THREADUNSAFE, WorldPackets::Ticket::SupportTicketSubmitComplaint, &WorldSession::HandleSupportTicketSubmitComplaint);
DEFINE_HANDLER(CMSG_SUPPORT_TICKET_SUBMIT_SUGGESTION, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Ticket::SupportTicketSubmitSuggestion, &WorldSession::HandleSupportTicketSubmitSuggestion);
DEFINE_HANDLER(CMSG_SUSPEND_COMMS_ACK, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
- DEFINE_HANDLER(CMSG_SUSPEND_TOKEN_RESPONSE, STATUS_UNHANDLED, PROCESS_INPLACE, WorldPackets::Null, &WorldSession::Handle_NULL);
+ DEFINE_HANDLER(CMSG_SUSPEND_TOKEN_RESPONSE, STATUS_TRANSFER, PROCESS_THREADUNSAFE, WorldPackets::Movement::SuspendTokenResponse, &WorldSession::HandleSuspendTokenResponse);
DEFINE_HANDLER(CMSG_SWAP_INV_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::SwapInvItem, &WorldSession::HandleSwapInvItemOpcode);
DEFINE_HANDLER(CMSG_SWAP_ITEM, STATUS_LOGGEDIN, PROCESS_THREADUNSAFE, WorldPackets::Item::SwapItem, &WorldSession::HandleSwapItem);
DEFINE_HANDLER(CMSG_SWAP_SUB_GROUPS, STATUS_LOGGEDIN, PROCESS_INPLACE, WorldPackets::Party::SwapSubGroups, &WorldSession::HandleSwapSubGroupsOpcode);
@@ -1576,7 +1576,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESPOND_INSPECT_ACHIEVEMENTS, STATUS_UNHANDLED, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESUME_CAST_BAR, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESUME_COMMS, STATUS_NEVER, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESUME_TOKEN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESUME_TOKEN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESURRECT_REQUEST, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_RESYNC_RUNES, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_ROLE_CHANGED_INFORM, STATUS_NEVER, CONNECTION_TYPE_REALM);
@@ -1628,7 +1628,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_MELEE_ANIM_KIT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_MOVEMENT_ANIM_KIT, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PCT_SPELL_MODIFIER, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PET_SPECIALIZATION, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PET_SPECIALIZATION, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PLAY_HOVER_ANIM, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SET_PROFICIENCY, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
@@ -1687,7 +1687,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SUMMON_REQUEST, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SUPERCEDED_SPELLS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_SUSPEND_COMMS, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_SUSPEND_TOKEN, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_SUSPEND_TOKEN, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TALENTS_INVOLUNTARILY_RESET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TAXI_NODE_STATUS, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_TEXT_EMOTE, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp
index 5203cdc1f9b..937961ad571 100644
--- a/src/server/game/Server/WorldSession.cpp
+++ b/src/server/game/Server/WorldSession.cpp
@@ -51,6 +51,7 @@
#include "BattlePetMgr.h"
#include "PacketUtilities.h"
#include "CollectionMgr.h"
+#include "Metric.h"
#include <zlib.h>
@@ -117,6 +118,7 @@ WorldSession::WorldSession(uint32 id, std::string&& name, uint32 battlenetAccoun
_battlenetAccountId(battlenetAccountId),
m_expansion(expansion),
_os(os),
+ _battlenetRequestToken(0),
_warden(NULL),
_logoutTime(0),
m_inQueue(false),
@@ -334,10 +336,6 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
if (IsConnectionIdle())
m_Socket[CONNECTION_TYPE_REALM]->CloseSocket();
- if (updater.ProcessUnsafe())
- while (_player && _player->IsBeingTeleportedSeamlessly())
- HandleMoveWorldportAckOpcode();
-
///- Retrieve packets from the receive queue and call the appropriate handlers
/// not process packets if socket already closed
WorldPacket* packet = NULL;
@@ -456,6 +454,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater)
break;
}
+ TC_METRIC_VALUE("processed_packets", processedPackets);
+
_recvQueue.readd(requeuePackets.begin(), requeuePackets.end());
if (m_Socket[CONNECTION_TYPE_REALM] && m_Socket[CONNECTION_TYPE_REALM]->IsOpen() && _warden)
@@ -614,6 +614,8 @@ void WorldSession::LogoutPlayer(bool save)
//! Call script hook before deletion
sScriptMgr->OnPlayerLogout(_player);
+ TC_METRIC_EVENT("player_events", "Logout", _player->GetName());
+
//! Remove the player from the world
// the player may not be in the world when logging out
// e.g if he got disconnected during a transfer to another map
diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h
index 5b06c9ce03f..3093b05163c 100644
--- a/src/server/game/Server/WorldSession.h
+++ b/src/server/game/Server/WorldSession.h
@@ -439,6 +439,7 @@ namespace WorldPackets
class MoveTimeSkipped;
class SummonResponse;
class MoveSplineDone;
+ class SuspendTokenResponse;
}
namespace NPC
@@ -1232,6 +1233,7 @@ class TC_GAME_API WorldSession
void HandleMoveWorldportAckOpcode(WorldPackets::Movement::WorldPortResponse& packet);
void HandleMoveWorldportAckOpcode(); // for server-side calls
+ void HandleSuspendTokenResponse(WorldPackets::Movement::SuspendTokenResponse& suspendTokenResponse);
void HandleMovementOpcodes(WorldPackets::Movement::ClientPlayerMovement& packet);
void HandleSetActiveMoverOpcode(WorldPackets::Movement::SetActiveMover& packet);
diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h
index 5130ebd7876..9eb6ad5c864 100644
--- a/src/server/game/Spells/Auras/SpellAuraDefines.h
+++ b/src/server/game/Spells/Auras/SpellAuraDefines.h
@@ -404,7 +404,7 @@ enum AuraType
SPELL_AURA_MOD_AUTOATTACK_DAMAGE = 344, // NYI
SPELL_AURA_BYPASS_ARMOR_FOR_CASTER = 345,
SPELL_AURA_ENABLE_ALT_POWER = 346, // NYI
- SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE = 347, // NYI
+ SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE = 347,
SPELL_AURA_DEPOSIT_BONUS_MONEY_IN_GUILD_BANK_ON_LOOT = 348,
SPELL_AURA_MOD_CURRENCY_GAIN = 349,
SPELL_AURA_MOD_GATHERING_ITEMS_GAINED_PERCENT = 350, // NYI
@@ -433,11 +433,11 @@ enum AuraType
SPELL_AURA_373 = 373,
SPELL_AURA_MODIFY_FALL_DAMAGE_PCT = 374, // NYI
SPELL_AURA_375 = 375,
- SPELL_AURA_MOD_CURRENCY_GAIN_2 = 376, // NYI
+ SPELL_AURA_MOD_CURRENCY_GAIN_FROM_SOURCE = 376, // NYI
SPELL_AURA_CAST_WHILE_WALKING_2 = 377, // NYI
SPELL_AURA_378 = 378,
SPELL_AURA_379 = 379,
- SPELL_AURA_380 = 380,
+ SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE = 380, // Allows melee abilities to benefit from haste GCD reduction
SPELL_AURA_381 = 381,
SPELL_AURA_MOD_PET_STAT_PCT = 382, // NYI
SPELL_AURA_IGNORE_SPELL_COOLDOWN = 383, // NYI
@@ -451,7 +451,7 @@ enum AuraType
SPELL_AURA_391 = 391,
SPELL_AURA_392 = 392,
SPELL_AURA_393 = 393,
- SPELL_AURA_394 = 394,
+ SPELL_AURA_SHOW_CONFIRMATION_PROMPT = 394,
SPELL_AURA_AREA_TRIGGER = 395, // NYI
SPELL_AURA_396 = 396,
SPELL_AURA_397 = 397,
@@ -473,8 +473,8 @@ enum AuraType
SPELL_AURA_413 = 413,
SPELL_AURA_414 = 414,
SPELL_AURA_415 = 415,
- SPELL_AURA_MOD_COOLDOWN_BY_HASTE_EFFECTS = 416, // NYI
- SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_EFFECTS = 417, // NYI
+ SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN = 416,
+ SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN = 417,
SPELL_AURA_MOD_MAX_POWER = 418, // NYI
SPELL_AURA_MOD_BASE_MANA_PCT = 419,
SPELL_AURA_MOD_BATTLE_PET_XP_PCT = 420, // NYI
@@ -497,10 +497,10 @@ enum AuraType
SPELL_AURA_437 = 437,
SPELL_AURA_438 = 438,
SPELL_AURA_439 = 439,
- SPELL_AURA_440 = 440,
- SPELL_AURA_441 = 441,
- SPELL_AURA_442 = 442,
- SPELL_AURA_443 = 443,
+ SPELL_AURA_MOD_MULTISTRIKE_DAMAGE = 440, // NYI
+ SPELL_AURA_MOD_MULTISTRIKE_CHANCE = 441, // NYI
+ SPELL_AURA_MOD_READINESS = 442, // NYI
+ SPELL_AURA_MOD_LEECH = 443, // NYI
SPELL_AURA_444 = 444,
SPELL_AURA_445 = 445,
SPELL_AURA_446 = 446,
@@ -508,7 +508,7 @@ enum AuraType
SPELL_AURA_448 = 448,
SPELL_AURA_449 = 449,
SPELL_AURA_450 = 450,
- SPELL_AURA_OVERRIDE_PET_SPECS = 451, // NYI
+ SPELL_AURA_OVERRIDE_PET_SPECS = 451,
SPELL_AURA_452 = 452,
SPELL_AURA_CHARGE_RECOVERY_MOD = 453,
SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER = 454,
@@ -526,13 +526,14 @@ enum AuraType
SPELL_AURA_MOD_BONUS_ARMOR_PCT = 466, // Affects bonus armor gain from all sources except base stats
SPELL_AURA_MOD_STAT_BONUS_PCT = 467, // Affects stat gain from all sources except base stats
SPELL_AURA_468 = 468,
- SPELL_AURA_469 = 469,
- SPELL_AURA_471 = 471,
+ SPELL_AURA_SHOW_CONFIRMATION_PROMPT_WITH_DIFFICULTY = 469,
+ SPELL_AURA_470 = 470,
+ SPELL_AURA_MOD_VERSATILITY = 471, // NYI
SPELL_AURA_472 = 472,
SPELL_AURA_PREVENT_DURABILITY_LOSS_FROM_COMBAT = 473, // Prevents durability loss from dealing/taking damage
SPELL_AURA_474 = 474,
- SPELL_AURA_475 = 475,
- SPELL_AURA_476 = 476,
+ SPELL_AURA_ALLOW_USING_GAMEOBJECTS_WHILE_MOUNTED = 475,
+ SPELL_AURA_MOD_CURRENCY_GAIN_LOOTED = 476,
SPELL_AURA_477 = 477,
SPELL_AURA_478 = 478,
SPELL_AURA_479 = 479,
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index 9962a214f97..58375e3f0df 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -204,7 +204,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleAuraModBaseResistancePCT, //142 SPELL_AURA_MOD_BASE_RESISTANCE_PCT
&AuraEffect::HandleAuraModResistanceExclusive, //143 SPELL_AURA_MOD_RESISTANCE_EXCLUSIVE
&AuraEffect::HandleNoImmediateEffect, //144 SPELL_AURA_SAFE_FALL implemented in WorldSession::HandleMovementOpcodes
- &AuraEffect::HandleAuraModPetTalentsPoints, //145 SPELL_AURA_MOD_PET_TALENT_POINTS
+ &AuraEffect::HandleNULL, //145 used by 5 spells in 6.2.4 dbc but the meaning of this aura changed (it's used by mind control spells but isn't the control itself)
&AuraEffect::HandleNoImmediateEffect, //146 SPELL_AURA_ALLOW_TAME_PET_TYPE
&AuraEffect::HandleModStateImmunityMask, //147 SPELL_AURA_MECHANIC_IMMUNITY_MASK
&AuraEffect::HandleAuraRetainComboPoints, //148 SPELL_AURA_RETAIN_COMBO_POINTS
@@ -406,7 +406,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //344 SPELL_AURA_MOD_AUTOATTACK_DAMAGE
&AuraEffect::HandleNoImmediateEffect, //345 SPELL_AURA_BYPASS_ARMOR_FOR_CASTER
&AuraEffect::HandleEnableAltPower, //346 SPELL_AURA_ENABLE_ALT_POWER
- &AuraEffect::HandleNULL, //347 SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE
+ &AuraEffect::HandleNoImmediateEffect, //347 SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE implemented in SpellHistory::StartCooldown
&AuraEffect::HandleNoImmediateEffect, //348 SPELL_AURA_DEPOSIT_BONUS_MONEY_IN_GUILD_BANK_ON_LOOT implemented in WorldSession::HandleLootMoneyOpcode
&AuraEffect::HandleNoImmediateEffect, //349 SPELL_AURA_MOD_CURRENCY_GAIN implemented in Player::ModifyCurrency
&AuraEffect::HandleNULL, //350 SPELL_AURA_MOD_GATHERING_ITEMS_GAINED_PERCENT
@@ -435,11 +435,11 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //373
&AuraEffect::HandleNULL, //374 SPELL_AURA_MODIFY_FALL_DAMAGE_PCT
&AuraEffect::HandleNULL, //375
- &AuraEffect::HandleNULL, //376 SPELL_AURA_MOD_CURRENCY_GAIN_2
+ &AuraEffect::HandleNULL, //376 SPELL_AURA_MOD_CURRENCY_GAIN_FROM_SOURCE
&AuraEffect::HandleNULL, //377 SPELL_AURA_CAST_WHILE_WALKING_2
&AuraEffect::HandleNULL, //378
&AuraEffect::HandleNULL, //379
- &AuraEffect::HandleNULL, //380
+ &AuraEffect::HandleNoImmediateEffect, //380 SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE implemented in Spell::TriggerGlobalCooldown
&AuraEffect::HandleNULL, //381
&AuraEffect::HandleNULL, //382 SPELL_AURA_MOD_PET_STAT_PCT
&AuraEffect::HandleNULL, //383 SPELL_AURA_IGNORE_SPELL_COOLDOWN
@@ -453,7 +453,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //391
&AuraEffect::HandleNULL, //392
&AuraEffect::HandleNULL, //393
- &AuraEffect::HandleNULL, //394
+ &AuraEffect::HandleShowConfirmationPrompt, //394 SPELL_AURA_SHOW_CONFIRMATION_PROMPT
&AuraEffect::HandleNULL, //395 SPELL_AURA_AREA_TRIGGER
&AuraEffect::HandleNULL, //396
&AuraEffect::HandleNULL, //397
@@ -475,8 +475,8 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //413
&AuraEffect::HandleNULL, //414
&AuraEffect::HandleNULL, //415
- &AuraEffect::HandleNULL, //416 SPELL_AURA_MOD_COOLDOWN_BY_HASTE_EFFECTS
- &AuraEffect::HandleNULL, //417 SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_EFFECTS
+ &AuraEffect::HandleNoImmediateEffect, //416 SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN implemented in SpellHistory::StartCooldown
+ &AuraEffect::HandleNoImmediateEffect, //417 SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN implemented in Spell::TriggerGlobalCooldown
&AuraEffect::HandleNULL, //418 SPELL_AURA_MOD_MAX_POWER
&AuraEffect::HandleAuraModIncreaseBaseManaPercent, //419 SPELL_AURA_MOD_BASE_MANA_PCT
&AuraEffect::HandleNULL, //420 SPELL_AURA_MOD_BATTLE_PET_XP_PCT
@@ -499,10 +499,10 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //437
&AuraEffect::HandleNULL, //438
&AuraEffect::HandleNULL, //439
- &AuraEffect::HandleNULL, //440
- &AuraEffect::HandleNULL, //441
- &AuraEffect::HandleNULL, //442
- &AuraEffect::HandleNULL, //443
+ &AuraEffect::HandleNULL, //440 SPELL_AURA_MOD_MULTISTRIKE_DAMAGE
+ &AuraEffect::HandleNULL, //441 SPELL_AURA_MOD_MULTISTRIKE_CHANCE
+ &AuraEffect::HandleNULL, //442 SPELL_AURA_MOD_READINESS
+ &AuraEffect::HandleNULL, //443 SPELL_AURA_MOD_LEECH
&AuraEffect::HandleNULL, //444
&AuraEffect::HandleNULL, //445
&AuraEffect::HandleNULL, //446
@@ -510,7 +510,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //448
&AuraEffect::HandleNULL, //449
&AuraEffect::HandleNULL, //450
- &AuraEffect::HandleNULL, //451 SPELL_AURA_OVERRIDE_PET_SPECS
+ &AuraEffect::HandleOverridePetSpecs, //451 SPELL_AURA_OVERRIDE_PET_SPECS
&AuraEffect::HandleNULL, //452
&AuraEffect::HandleNoImmediateEffect, //453 SPELL_AURA_CHARGE_RECOVERY_MOD implemented in SpellHistory::GetChargeRecoveryTime
&AuraEffect::HandleNoImmediateEffect, //454 SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER implemented in SpellHistory::GetChargeRecoveryTime
@@ -528,14 +528,14 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]=
&AuraEffect::HandleNULL, //466 SPELL_AURA_MOD_BONUS_ARMOR_PCT
&AuraEffect::HandleModStatBonusPercent, //467 SPELL_AURA_MOD_STAT_BONUS_PCT
&AuraEffect::HandleNULL, //468
- &AuraEffect::HandleNULL, //469
+ &AuraEffect::HandleShowConfirmationPrompt, //469 SPELL_AURA_SHOW_CONFIRMATION_PROMPT_WITH_DIFFICULTY
&AuraEffect::HandleNULL, //470
- &AuraEffect::HandleNULL, //471
+ &AuraEffect::HandleNULL, //471 SPELL_AURA_MOD_VERSATILITY
&AuraEffect::HandleNULL, //472
&AuraEffect::HandleNoImmediateEffect, //473 SPELL_AURA_PREVENT_DURABILITY_LOSS_FROM_COMBAT implemented in Player::DurabilityPointLossForEquipSlot
&AuraEffect::HandleNULL, //474
- &AuraEffect::HandleNULL, //475
- &AuraEffect::HandleNULL, //476
+ &AuraEffect::HandleAllowUsingGameobjectsWhileMounted, //475 SPELL_AURA_ALLOW_USING_GAMEOBJECTS_WHILE_MOUNTED
+ &AuraEffect::HandleNULL, //476 SPELL_AURA_MOD_CURRENCY_GAIN_LOOTED
&AuraEffect::HandleNULL, //477
&AuraEffect::HandleNULL, //478
&AuraEffect::HandleNULL, //479
@@ -706,6 +706,11 @@ int32 AuraEffect::CalculateAmount(Unit* caster)
break;
}
}
+ case SPELL_AURA_SHOW_CONFIRMATION_PROMPT_WITH_DIFFICULTY:
+ if (caster)
+ amount = caster->GetMap()->GetDifficultyID();
+ m_canBeRecalculated = false;
+ break;
default:
break;
}
@@ -846,6 +851,9 @@ void AuraEffect::ChangeAmount(int32 newAmount, bool mark, bool onStackOrReapply)
for (std::list<AuraApplication*>::const_iterator apptItr = effectApplications.begin(); apptItr != effectApplications.end(); ++apptItr)
if ((*apptItr)->HasEffect(GetEffIndex()))
HandleEffect(*apptItr, handleMask, true);
+
+ if (GetSpellInfo()->HasAttribute(SPELL_ATTR8_AURA_SEND_AMOUNT))
+ GetBase()->SetNeedClientUpdateForTargets();
}
void AuraEffect::HandleEffect(AuraApplication * aurApp, uint8 mode, bool apply)
@@ -2584,21 +2592,6 @@ void AuraEffect::HandleAuraUntrackable(AuraApplication const* aurApp, uint8 mode
/*** SKILLS & TALENTS ***/
/****************************/
-void AuraEffect::HandleAuraModPetTalentsPoints(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
-{
- if (!(mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK))
- return;
-
- Unit* target = aurApp->GetTarget();
-
- if (target->GetTypeId() != TYPEID_PLAYER)
- return;
-
- // Recalculate pet talent points
- if (Pet* pet = target->ToPlayer()->GetPet())
- pet->InitTalentForLevel();
-}
-
void AuraEffect::HandleAuraModSkill(AuraApplication const* aurApp, uint8 mode, bool apply) const
{
if (!(mode & (AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK | AURA_EFFECT_HANDLE_SKILL)))
@@ -6603,3 +6596,55 @@ void AuraEffect::HandleModSpellCategoryCooldown(AuraApplication const* aurApp, u
if (Player* player = aurApp->GetTarget()->ToPlayer())
player->SendSpellCategoryCooldowns();
}
+
+void AuraEffect::HandleShowConfirmationPrompt(AuraApplication const* aurApp, uint8 mode, bool apply) const
+{
+ if (!(mode & AURA_EFFECT_HANDLE_REAL))
+ return;
+
+ Player* player = aurApp->GetTarget()->ToPlayer();
+ if (!player)
+ return;
+
+ if (apply)
+ player->AddTemporarySpell(_effectInfo->TriggerSpell);
+ else
+ player->RemoveTemporarySpell(_effectInfo->TriggerSpell);
+}
+
+void AuraEffect::HandleOverridePetSpecs(AuraApplication const* aurApp, uint8 mode, bool apply) const
+{
+ if (!(mode & AURA_EFFECT_HANDLE_REAL))
+ return;
+
+ Player* player = aurApp->GetTarget()->ToPlayer();
+ if (!player)
+ return;
+
+ if (player->getClass() != CLASS_HUNTER)
+ return;
+
+ Pet* pet = player->GetPet();
+ if (!pet)
+ return;
+
+ ChrSpecializationEntry const* currSpec = sChrSpecializationStore.LookupEntry(pet->GetSpecialization());
+ if (!currSpec)
+ return;
+
+ pet->SetSpecialization(sDB2Manager.GetChrSpecializationByIndex(apply ? PET_SPEC_OVERRIDE_CLASS_INDEX : 0, currSpec->OrderIndex)->ID);
+}
+
+void AuraEffect::HandleAllowUsingGameobjectsWhileMounted(AuraApplication const* aurApp, uint8 mode, bool apply) const
+{
+ if (!(mode & AURA_EFFECT_HANDLE_REAL))
+ return;
+
+ if (aurApp->GetTarget()->GetTypeId() != TYPEID_PLAYER)
+ return;
+
+ if (apply)
+ aurApp->GetTarget()->SetFlag(PLAYER_FIELD_LOCAL_FLAGS, PLAYER_LOCAL_FLAG_CAN_USE_OBJECTS_MOUNTED);
+ else if (!aurApp->GetTarget()->HasAuraType(SPELL_AURA_ALLOW_USING_GAMEOBJECTS_WHILE_MOUNTED))
+ aurApp->GetTarget()->RemoveFlag(PLAYER_FIELD_LOCAL_FLAGS, PLAYER_LOCAL_FLAG_CAN_USE_OBJECTS_MOUNTED);
+}
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.h b/src/server/game/Spells/Auras/SpellAuraEffects.h
index 46a0ccdabc6..8df069df7f3 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.h
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.h
@@ -174,7 +174,6 @@ class TC_GAME_API AuraEffect
void HandleAuraModStalked(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraUntrackable(AuraApplication const* aurApp, uint8 mode, bool apply) const;
// skills & talents
- void HandleAuraModPetTalentsPoints(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleAuraModSkill(AuraApplication const* aurApp, uint8 mode, bool apply) const;
// movement
void HandleAuraMounted(AuraApplication const* aurApp, uint8 mode, bool apply) const;
@@ -304,6 +303,9 @@ class TC_GAME_API AuraEffect
void HandleAuraForceWeather(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleEnableAltPower(AuraApplication const* aurApp, uint8 mode, bool apply) const;
void HandleModSpellCategoryCooldown(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+ void HandleShowConfirmationPrompt(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+ void HandleOverridePetSpecs(AuraApplication const* aurApp, uint8 mode, bool apply) const;
+ void HandleAllowUsingGameobjectsWhileMounted(AuraApplication const* aurApp, uint8 mode, bool apply) const;
// aura effect periodic tick handlers
void HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const;
diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp
index ec060f253b9..d0af2af8f97 100644
--- a/src/server/game/Spells/Auras/SpellAuras.cpp
+++ b/src/server/game/Spells/Auras/SpellAuras.cpp
@@ -1760,6 +1760,10 @@ bool Aura::CanStackWith(Aura const* existingAura) const
return true; // Empty seat available (skip rest)
}
+ if (HasEffectType(SPELL_AURA_SHOW_CONFIRMATION_PROMPT) || HasEffectType(SPELL_AURA_SHOW_CONFIRMATION_PROMPT_WITH_DIFFICULTY))
+ if (existingAura->HasEffectType(SPELL_AURA_SHOW_CONFIRMATION_PROMPT) || existingAura->HasEffectType(SPELL_AURA_SHOW_CONFIRMATION_PROMPT_WITH_DIFFICULTY))
+ return false;
+
// spell of same spell rank chain
if (m_spellInfo->IsRankOf(existingSpellInfo))
{
diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp
index 005d15d8291..d109667cd85 100644
--- a/src/server/game/Spells/Spell.cpp
+++ b/src/server/game/Spells/Spell.cpp
@@ -4187,13 +4187,11 @@ void Spell::UpdateSpellCastDataAmmo(WorldPackets::Spells::SpellAmmo& ammo)
void Spell::SendSpellExecuteLog()
{
WorldPackets::CombatLog::SpellExecuteLog spellExecuteLog;
-
spellExecuteLog.Caster = m_caster->GetGUID();
spellExecuteLog.SpellID = m_spellInfo->Id;
for (SpellEffectInfo const* effect : GetEffects())
{
- WorldPackets::CombatLog::SpellExecuteLog::SpellLogEffect spellLogEffect;
if (!effect)
continue;
@@ -4202,6 +4200,9 @@ void Spell::SendSpellExecuteLog()
_tradeSkillTargets[effect->EffectIndex].empty() && _feedPetTargets[effect->EffectIndex].empty())
continue;
+ spellExecuteLog.Effects.emplace_back();
+
+ WorldPackets::CombatLog::SpellExecuteLog::SpellLogEffect& spellLogEffect = spellExecuteLog.Effects.back();
spellLogEffect.Effect = effect->Effect;
spellLogEffect.PowerDrainTargets = std::move(_powerDrainTargets[effect->EffectIndex]);
spellLogEffect.ExtraAttacksTargets = std::move(_extraAttacksTargets[effect->EffectIndex]);
@@ -4209,8 +4210,6 @@ void Spell::SendSpellExecuteLog()
spellLogEffect.GenericVictimTargets = std::move(_genericVictimTargets[effect->EffectIndex]);
spellLogEffect.TradeSkillTargets = std::move(_tradeSkillTargets[effect->EffectIndex]);
spellLogEffect.FeedPetTargets = std::move(_feedPetTargets[effect->EffectIndex]);
-
- spellExecuteLog.Effects.push_back(spellLogEffect);
}
if (!spellExecuteLog.Effects.empty())
@@ -7346,7 +7345,7 @@ bool Spell::HasGlobalCooldown() const
void Spell::TriggerGlobalCooldown()
{
int32 gcd = m_spellInfo->StartRecoveryTime;
- if (!gcd)
+ if (!gcd || !m_spellInfo->StartRecoveryCategory)
return;
// Only players or controlled units have global cooldown
@@ -7366,12 +7365,17 @@ void Spell::TriggerGlobalCooldown()
if (m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->ToPlayer()->ApplySpellMod(m_spellInfo->Id, SPELLMOD_GLOBAL_COOLDOWN, gcd, this);
+ bool isMeleeOrRangedSpell = m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE ||
+ m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED ||
+ m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) ||
+ m_spellInfo->HasAttribute(SPELL_ATTR0_ABILITY);
+
// Apply haste rating
- gcd = int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_SPEED));
- if (gcd < MIN_GCD)
- gcd = MIN_GCD;
- else if (gcd > MAX_GCD)
- gcd = MAX_GCD;
+ if (gcd > MIN_GCD && ((m_spellInfo->StartRecoveryCategory == 133 && !isMeleeOrRangedSpell) || m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE, m_spellInfo)))
+ gcd = std::min<int32>(std::max<int32>(int32(float(gcd) * m_caster->GetFloatValue(UNIT_MOD_CAST_HASTE)), MIN_GCD), MAX_GCD);
+
+ if (gcd > MIN_GCD && m_caster->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_GLOBAL_COOLDOWN_BY_HASTE_REGEN, m_spellInfo))
+ gcd = std::min<int32>(std::max<int32>(int32(float(gcd) * m_caster->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN)), MIN_GCD), MAX_GCD);
}
m_caster->GetSpellHistory()->AddGlobalCooldown(m_spellInfo, gcd);
diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp
index 3e9dec9f485..11a92fd67b8 100644
--- a/src/server/game/Spells/SpellEffects.cpp
+++ b/src/server/game/Spells/SpellEffects.cpp
@@ -271,7 +271,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectNULL, //203 SPELL_EFFECT_203
&Spell::EffectNULL, //204 SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY
&Spell::EffectNULL, //205 SPELL_EFFECT_LAUNCH_QUEST_CHOICE
- &Spell::EffectNULL, //206 SPELL_EFFECT_206
+ &Spell::EffectNULL, //206 SPELL_EFFECT_ALTER_ITEM
&Spell::EffectNULL, //207 SPELL_EFFECT_LAUNCH_QUEST_TASK
&Spell::EffectNULL, //208 SPELL_EFFECT_208
&Spell::EffectNULL, //209 SPELL_EFFECT_209
@@ -1725,7 +1725,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype)
if (sScriptMgr->OnGossipHello(player, gameObjTarget))
return;
- if (gameObjTarget->AI()->GossipHello(player))
+ if (gameObjTarget->AI()->GossipHello(player, true))
return;
switch (gameObjTarget->GetGoType())
@@ -2728,8 +2728,6 @@ void Spell::EffectTameCreature(SpellEffIndex /*effIndex*/)
// caster have pet now
m_caster->SetMinion(pet, true);
- pet->InitTalentForLevel();
-
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
@@ -3170,8 +3168,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex)
Map* map = target->GetMap();
- if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id, map,
- m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ if (!pGameObj->Create(gameobject_id, map, m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
{
delete pGameObj;
return;
@@ -3197,8 +3194,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex)
if (uint32 linkedEntry = pGameObj->GetGOInfo()->GetLinkedGameObjectEntry())
{
GameObject* linkedGO = new GameObject;
- if (linkedGO->Create(map->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, map,
- m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ if (linkedGO->Create(linkedEntry, map, m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
{
linkedGO->CopyPhaseFrom(m_caster);
@@ -3791,8 +3787,7 @@ void Spell::EffectDuel(SpellEffIndex effIndex)
uint32 gameobject_id = effectInfo->MiscValue;
Map* map = m_caster->GetMap();
- if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id,
- map, 0,
+ if (!pGameObj->Create(gameobject_id, map, 0,
m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2,
m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2,
m_caster->GetPositionZ(),
@@ -4097,8 +4092,7 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex)
m_caster->GetClosePoint(x, y, z, DEFAULT_WORLD_OBJECT_SIZE);
Map* map = m_caster->GetMap();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), go_id, map,
- 0, x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
+ if (!go->Create(go_id, map, 0, x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
{
delete go;
return;
@@ -4756,8 +4750,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex)
GameObject* pGameObj = new GameObject;
- if (!pGameObj->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), name_id, cMap,
- 0, fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ if (!pGameObj->Create(name_id, cMap, 0, fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
{
delete pGameObj;
return;
@@ -4824,8 +4817,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex)
if (uint32 linkedEntry = pGameObj->GetGOInfo()->GetLinkedGameObjectEntry())
{
GameObject* linkedGO = new GameObject;
- if (linkedGO->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, cMap,
- 0, fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
+ if (linkedGO->Create(linkedEntry, cMap, 0, fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
{
linkedGO->CopyPhaseFrom(m_caster);
@@ -5178,8 +5170,6 @@ void Spell::EffectCreateTamedPet(SpellEffIndex /*effIndex*/)
// unitTarget has pet now
unitTarget->SetMinion(pet, true);
- pet->InitTalentForLevel();
-
if (unitTarget->GetTypeId() == TYPEID_PLAYER)
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp
index bb991b66cf9..b6c7fdc17ec 100644
--- a/src/server/game/Spells/SpellHistory.cpp
+++ b/src/server/game/Spells/SpellHistory.cpp
@@ -330,7 +330,7 @@ void SpellHistory::WritePacket(WorldPackets::Spells::SendSpellCharges* sendSpell
WorldPackets::Spells::SpellChargeEntry chargeEntry;
chargeEntry.Category = p.first;
chargeEntry.NextRecoveryTime = uint32(cooldownDuration.count());
- chargeEntry.ConsumedCharges = p.second.size();
+ chargeEntry.ConsumedCharges = uint8(p.second.size());
sendSpellCharges->Entries.push_back(chargeEntry);
}
}
@@ -359,6 +359,8 @@ void SpellHistory::WritePacket(WorldPackets::Pet::PetSpells* petSpells) const
if (categoryDuration.count() > 0)
petSpellCooldown.CategoryDuration = uint32(categoryDuration.count());
}
+ else
+ petSpellCooldown.CategoryDuration = 0x80000000;
petSpells->Cooldowns.push_back(petSpellCooldown);
}
@@ -375,14 +377,13 @@ void SpellHistory::WritePacket(WorldPackets::Pet::PetSpells* petSpells) const
WorldPackets::Pet::PetSpellHistory petChargeEntry;
petChargeEntry.CategoryID = p.first;
petChargeEntry.RecoveryTime = uint32(cooldownDuration.count());
- petChargeEntry.ConsumedCharges = p.second.size();
+ petChargeEntry.ConsumedCharges = int8(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
@@ -421,6 +422,18 @@ void SpellHistory::StartCooldown(SpellInfo const* spellInfo, uint32 itemId, Spel
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, categoryCooldown, spell);
}
+ if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_SPELL_COOLDOWN_BY_HASTE, spellInfo))
+ {
+ cooldown = int32(cooldown * _owner->GetFloatValue(UNIT_MOD_CAST_HASTE));
+ categoryCooldown = int32(categoryCooldown * _owner->GetFloatValue(UNIT_MOD_CAST_HASTE));
+ }
+
+ if (_owner->HasAuraTypeWithAffectMask(SPELL_AURA_MOD_COOLDOWN_BY_HASTE_REGEN, spellInfo))
+ {
+ cooldown = int32(cooldown * _owner->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN));
+ categoryCooldown = int32(categoryCooldown * _owner->GetFloatValue(UNIT_FIELD_MOD_HASTE_REGEN));
+ }
+
if (int32 cooldownMod = _owner->GetTotalAuraModifier(SPELL_AURA_MOD_COOLDOWN))
{
// Apply SPELL_AURA_MOD_COOLDOWN only to own spells
@@ -731,7 +744,7 @@ void SpellHistory::RestoreCharge(uint32 chargeCategoryId)
setSpellCharges.Category = chargeCategoryId;
if (!itr->second.empty())
setSpellCharges.NextRecoveryTime = uint32(std::chrono::duration_cast<std::chrono::milliseconds>(itr->second.front().RechargeEnd - Clock::now()).count());
- setSpellCharges.ConsumedCharges = itr->second.size();
+ setSpellCharges.ConsumedCharges = uint8(itr->second.size());
setSpellCharges.IsPet = player != _owner;
player->SendDirectMessage(setSpellCharges.Write());
@@ -802,7 +815,7 @@ int32 SpellHistory::GetChargeRecoveryTime(uint32 chargeCategoryId) const
int32 recoveryTime = chargeCategoryEntry->ChargeRecoveryTime;
recoveryTime += _owner->GetTotalAuraModifierByMiscValue(SPELL_AURA_CHARGE_RECOVERY_MOD, chargeCategoryId);
- float recoveryTimeF = recoveryTime;
+ float recoveryTimeF = float(recoveryTime);
recoveryTimeF *= _owner->GetTotalAuraMultiplierByMiscValue(SPELL_AURA_CHARGE_RECOVERY_MULTIPLIER, chargeCategoryId);
if (_owner->HasAuraType(SPELL_AURA_CHARGE_RECOVERY_AFFECTED_BY_HASTE))
@@ -923,7 +936,7 @@ void SpellHistory::RestoreCooldownStateAfterDuel()
for (auto const& c : _spellCooldowns)
{
Clock::time_point now = Clock::now();
- uint32 cooldownDuration = c.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(c.second.CooldownEnd - now).count() : 0;
+ uint32 cooldownDuration = uint32(c.second.CooldownEnd > now ? std::chrono::duration_cast<std::chrono::milliseconds>(c.second.CooldownEnd - now).count() : 0);
// cooldownDuration must be between 0 and 10 minutes in order to avoid any visual bugs
if (cooldownDuration <= 0 || cooldownDuration > 10 * MINUTE * IN_MILLISECONDS || c.second.OnHold)
diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp
index 6e8db741a3d..362d5e8df35 100644
--- a/src/server/game/Spells/SpellInfo.cpp
+++ b/src/server/game/Spells/SpellInfo.cpp
@@ -926,7 +926,7 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] =
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 203 SPELL_EFFECT_203
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 204 SPELL_EFFECT_CHANGE_BATTLEPET_QUALITY
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 205 SPELL_EFFECT_LAUNCH_QUEST_CHOICE
- {EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 206 SPELL_EFFECT_206
+ {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 206 SPELL_EFFECT_ALTER_IETM
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 207 SPELL_EFFECT_LAUNCH_QUEST_TASK
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 208 SPELL_EFFECT_208
{EFFECT_IMPLICIT_TARGET_NONE, TARGET_OBJECT_TYPE_NONE}, // 209 SPELL_EFFECT_209
@@ -2820,6 +2820,9 @@ inline float CalcPPMItemLevelMod(SpellProcsPerMinuteModEntry const* mod, int32 i
float SpellInfo::CalcProcPPM(Unit* caster, int32 itemLevel) const
{
float ppm = ProcBasePPM;
+ if (!caster)
+ return ppm;
+
for (SpellProcsPerMinuteModEntry const* mod : ProcPPMMods)
{
switch (mod->Type)
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 4262cf11e27..bf536765901 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -64,6 +64,7 @@
#include "SkillDiscovery.h"
#include "SkillExtraItems.h"
#include "SmartAI.h"
+#include "Metric.h"
#include "SupportMgr.h"
#include "TaxiPathGraph.h"
#include "TransportMgr.h"
@@ -438,6 +439,7 @@ void World::LoadConfigSettings(bool reload)
return;
}
sLog->LoadFromConfig();
+ sMetric->LoadFromConfigs();
}
m_defaultDbcLocale = LocaleConstant(sConfigMgr->GetIntDefault("DBC.Locale", 0));
@@ -2124,6 +2126,8 @@ void World::SetInitialWorldSettings()
TC_LOG_INFO("server.worldserver", "World initialized in %u minutes %u seconds", (startupDuration / 60000), ((startupDuration % 60000) / 1000));
+ TC_METRIC_EVENT("events", "World initialized", "World initialized in " + std::to_string(startupDuration / 60000) + " minutes " + std::to_string((startupDuration % 60000) / 1000) + " seconds");
+
if (uint32 realmId = sConfigMgr->GetIntDefault("RealmID", 0)) // 0 reserved for auth
sLog->SetRealmId(realmId);
}
@@ -2426,6 +2430,10 @@ void World::Update(uint32 diff)
ProcessCliCommands();
sScriptMgr->OnWorldUpdate(diff);
+
+ // Stats logger update
+ sMetric->Update();
+ TC_METRIC_VALUE("update_time_diff", diff);
}
void World::ForceGameEventUpdate()
diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp
index 22e9bfe704a..982a15e314d 100644
--- a/src/server/scripts/Commands/cs_gobject.cpp
+++ b/src/server/scripts/Commands/cs_gobject.cpp
@@ -146,9 +146,7 @@ public:
Map* map = player->GetMap();
GameObject* object = new GameObject;
- ObjectGuid::LowType guidLow = map->GenerateLowGuid<HighGuid::GameObject>();
-
- if (!object->Create(guidLow, objectInfo->entry, map, 0, x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
+ if (!object->Create(objectInfo->entry, map, 0, x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
{
delete object;
return false;
@@ -164,7 +162,7 @@ public:
// fill the gameobject data and save to the db
object->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), player->GetPhaseMask());
- guidLow = object->GetSpawnId();
+ ObjectGuid::LowType spawnId = object->GetSpawnId();
// delete the old object and do a clean load from DB with a fresh new GameObject instance.
// this is required to avoid weird behavior and memory leaks
@@ -172,16 +170,16 @@ public:
object = new GameObject();
// this will generate a new guid if the object is in an instance
- if (!object->LoadGameObjectFromDB(guidLow, map))
+ if (!object->LoadGameObjectFromDB(spawnId, map))
{
delete object;
return false;
}
/// @todo is it really necessary to add both the real and DB table guid here ?
- sObjectMgr->AddGameobjectToGrid(guidLow, ASSERT_NOTNULL(sObjectMgr->GetGOData(guidLow)));
+ sObjectMgr->AddGameobjectToGrid(spawnId, ASSERT_NOTNULL(sObjectMgr->GetGOData(spawnId)));
- handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, x, y, z);
+ handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), spawnId, x, y, z);
return true;
}
@@ -669,7 +667,7 @@ public:
int32 objectState = atoi(state);
if (objectType < 4)
- object->SetByteValue(GAMEOBJECT_BYTES_1, objectType, objectState);
+ object->SetByteValue(GAMEOBJECT_BYTES_1, uint8(objectType), uint8(objectState));
else if (objectType == 4)
object->SendCustomAnim(objectState);
diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp
index 5fd9ff8028a..96acb89f1cb 100644
--- a/src/server/scripts/Commands/cs_npc.cpp
+++ b/src/server/scripts/Commands/cs_npc.cpp
@@ -1106,7 +1106,13 @@ public:
if (!*args)
return false;
- uint32 phase = (uint32) atoi((char*)args);
+ uint32 phaseID = uint32(atoi((char*)args));
+ if (!sPhaseStore.LookupEntry(phaseID))
+ {
+ handler->SendSysMessage(LANG_PHASE_NOTFOUND);
+ handler->SetSentErrorMessage(true);
+ return false;
+ }
Creature* creature = handler->getSelectedCreature();
if (!creature || creature->IsPet())
@@ -1117,8 +1123,8 @@ public:
}
creature->ClearPhases();
- creature->SetInPhase(phase, true, true);
- creature->SetDBPhase(phase);
+ creature->SetInPhase(phaseID, true, true);
+ creature->SetDBPhase(phaseID);
creature->SaveToDB();
diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
index 8c68fe4cc18..9d7c41ffd7b 100644
--- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
+++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp
@@ -1353,8 +1353,10 @@ class go_twilight_portal : public GameObjectScript
}
}
- bool GossipHello(Player* player) override
+ bool GossipHello(Player* player, bool isUse) override
{
+ if (!isUse)
+ return true;
if (_spellId != 0)
player->CastSpell(player, _spellId, true);
return true;
diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
index 106c567b24e..d755fa15d23 100644
--- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
@@ -1113,7 +1113,7 @@ class npc_jaina_or_sylvanas_escape_hor : public CreatureScript
me->RemoveAurasDueToSpell(SPELL_HARVEST_SOUL);
if (_instance->GetData(DATA_TEAM_IN_INSTANCE) == ALLIANCE)
Talk(SAY_JAINA_ESCAPE_9);
- if (Transport* gunship = ObjectAccessor::GetTransport(*me, _instance->GetGuidData(DATA_GUNSHIP)))
+ if (Transport* gunship = ObjectAccessor::GetTransportOnMap(*me, _instance->GetGuidData(DATA_GUNSHIP)))
gunship->EnableMovement(true);
_instance->SetBossState(DATA_THE_LICH_KING_ESCAPE, DONE);
break;
@@ -1185,7 +1185,7 @@ class npc_the_lich_king_escape_hor : public CreatureScript
if (Creature* target = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ESCAPE_LEADER)))
DoCast(target, SPELL_HARVEST_SOUL);
- if (Transport* gunship = ObjectAccessor::GetTransport(*me, _instance->GetGuidData(DATA_GUNSHIP)))
+ if (Transport* gunship = ObjectAccessor::GetTransportOnMap(*me, _instance->GetGuidData(DATA_GUNSHIP)))
gunship->EnableMovement(true);
break;
default:
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index 037b6a2988e..1a165780224 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -400,7 +400,7 @@ class instance_icecrown_citadel : public InstanceMapScript
return entry;
}
- uint32 GetGameObjectEntry(ObjectGuid::LowType /*guidLow*/, uint32 entry) override
+ uint32 GetGameObjectEntry(ObjectGuid::LowType /*spawnId*/, uint32 entry) override
{
switch (entry)
{
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
index ef6ccf5bf4b..69dbc7bf9e6 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
@@ -200,7 +200,7 @@ public:
if (zombie)
{
zombieToBeEatenGUID = zombie->GetGUID(); // save for later use
-
+
// the soon-to-be-eaten zombie should stop moving and stop attacking
zombie->AI()->SetData(DATA_ZOMBIE_STATE, STATE_ZOMBIE_TOBE_EATEN);
@@ -225,7 +225,7 @@ public:
case EVENT_KILL_ZOMBIE_SINGLE:
{
Creature* zombieToBeEaten = ObjectAccessor::GetCreature(*me, zombieToBeEatenGUID);
- if (zombieToBeEaten && zombieToBeEaten->IsAlive() && zombieToBeEaten->IsWithinDistInMap(me, 10.0))
+ if (zombieToBeEaten && zombieToBeEaten->IsAlive() && zombieToBeEaten->IsWithinDistInMap(me, 10.0))
DoCast(zombieToBeEaten, SPELL_ZOMBIE_CHOW_SEARCH_SINGLE); // do the killing + healing in done inside by spell script see below.
zombieToBeEatenGUID = ObjectGuid::Empty;
@@ -249,11 +249,11 @@ public:
if (zombie && zombie->IsAlive() && zombie->GetExactDist2d(me) > 18.0)
zombie = nullptr;
}
-
+
if (zombie) // cast the aoe spell only if at least one zombie is found nearby
{
Talk(EMOTE_DEVOURS_ALL);
- DoCastAOE(SPELL_ZOMBIE_CHOW_SEARCH_MULTI);
+ DoCastAOE(SPELL_ZOMBIE_CHOW_SEARCH_MULTI);
}
break;
}
@@ -269,7 +269,7 @@ public:
me->GetMotionMaster()->MoveIdle();
events.ScheduleEvent(EVENT_KILL_ZOMBIE_SINGLE, Seconds(1));
}
-
+
}
void DoAction(int32 action) override
@@ -405,17 +405,19 @@ public:
if (state == STATE_ZOMBIE_DECIMATED)
{
timer += diff;
- Creature* gluth = ObjectAccessor::GetCreature(*me, GluthGUID);
// Putting this in the UpdateAI loop fixes an issue where death gripping a decimated zombie would make the zombie stand still until the rest of the fight.
// Also fix the issue where if one or more zombie is rooted when decimates hits (and MovePoint() is called), the zombie teleport to the boss. pretty weird behavior.
- if (gluth && timer>1600 && me->GetExactDist2d(gluth) > 10.0 && me->CanFreeMove()) // it takes about 1600 ms for the animation to cycle. This way, the animation looks relatively smooth.
+ if (Creature* gluth = ObjectAccessor::GetCreature(*me, GluthGUID))
{
- me->GetMotionMaster()->MovePoint(0, gluth->GetPosition()); // isn't dynamic. So, to take into account Gluth's movement, it must be called periodicly.
- timer = 0;
- }
+ if (timer > 1600 && me->GetExactDist2d(gluth) > 10.0 && me->CanFreeMove()) // it takes about 1600 ms for the animation to cycle. This way, the animation looks relatively smooth.
+ {
+ me->GetMotionMaster()->MovePoint(0, gluth->GetPosition()); // isn't dynamic. So, to take into account Gluth's movement, it must be called periodicly.
+ timer = 0;
+ }
- if (me->GetExactDist2d(gluth) <= 10.0)
- me->StopMoving();
+ if (me->GetExactDist2d(gluth) <= 10.0)
+ me->StopMoving();
+ }
}
else if (state == STATE_ZOMBIE_NORMAL)
DoMeleeAttackIfReady();
@@ -430,10 +432,10 @@ public:
{
me->SetReactState(ReactStates::REACT_PASSIVE);
me->AttackStop();
- me->SetTarget(ObjectGuid::Empty);
+ me->SetTarget(ObjectGuid::Empty);
// at this point, the zombie should be non attacking and non moving.
- me->SetWalk(true); // it doesnt seem to work with MoveFollow() (but it does work with MovePoint()).
+ me->SetWalk(true); // it doesnt seem to work with MoveFollow() (but it does work with MovePoint()).
timer = 1000;
}
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp
index e5944841ad5..f6c829590b9 100644
--- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp
+++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/instance_eye_of_eternity.cpp
@@ -86,8 +86,7 @@ public:
void SpawnGameObject(uint32 entry, Position const& pos)
{
GameObject* go = new GameObject();
- if (!go->Create(instance->GenerateLowGuid<HighGuid::GameObject>(), entry, instance,
- PHASEMASK_NORMAL, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(),
+ if (!go->Create(entry, instance, PHASEMASK_NORMAL, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(),
0, 0, 0, 0, 120, GO_STATE_READY))
{
delete go;
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
index 0d8f9a6bce1..33afada15fd 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp
@@ -977,8 +977,11 @@ class go_celestial_planetarium_access : public GameObjectScript
{
}
- bool GossipHello(Player* player) override
+ bool GossipHello(Player* player, bool isUse) override
{
+ if (!isUse)
+ return true;
+
if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE))
return true;
diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp
index 59ff06ea66b..c7f49a67328 100644
--- a/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp
+++ b/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp
@@ -163,7 +163,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player* player, uint32 spellId)
GameObject* go = new GameObject;
Map* map = player->GetMap();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY))
+ if (!go->Create(SI_SILITHYST_MOUND, map, player->GetPhaseMask(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY))
{
delete go;
return true;
@@ -194,7 +194,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player* player, uint32 spellId)
GameObject* go = new GameObject;
Map* map = player->GetMap();
- if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY))
+ if (!go->Create(SI_SILITHYST_MOUND, map, player->GetPhaseMask(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetOrientation(), 0, 0, 0, 0, 100, GO_STATE_READY))
{
delete go;
return true;
diff --git a/src/server/scripts/Pet/pet_mage.cpp b/src/server/scripts/Pet/pet_mage.cpp
index 9ec779703d3..659a24773b7 100644
--- a/src/server/scripts/Pet/pet_mage.cpp
+++ b/src/server/scripts/Pet/pet_mage.cpp
@@ -198,8 +198,8 @@ class npc_pet_mage_mirror_image : public CreatureScript
if (!target || me->GetVictim() != target)
{
Unit* ownerTarget = nullptr;
- if (Player* owner = me->GetCharmerOrOwner()->ToPlayer())
- ownerTarget = owner->GetSelectedUnit();
+ if (Player* ownerPlayer = me->GetCharmerOrOwner()->ToPlayer())
+ ownerTarget = ownerPlayer->GetSelectedUnit();
// recognize which victim will be choosen
if (ownerTarget && ownerTarget->GetTypeId() == TYPEID_PLAYER)
diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp
index 0829441e17a..ad43ed92d3e 100644
--- a/src/server/scripts/Spells/spell_shaman.cpp
+++ b/src/server/scripts/Spells/spell_shaman.cpp
@@ -442,7 +442,7 @@ class spell_sha_fulmination : public SpellScriptLoader
uint32 stacks = aura->GetCharges();
if (stacks > 1)
{
- SpellInfo const* triggerSpell = sSpellMgr->GetSpellInfo(aura->GetSpellEffectInfo(EFFECT_0)->TriggerSpell);
+ SpellInfo const* triggerSpell = sSpellMgr->AssertSpellInfo(aura->GetSpellEffectInfo(EFFECT_0)->TriggerSpell);
SpellEffectInfo const* triggerEffect = triggerSpell->GetEffect(EFFECT_0);
uint32 damage;
diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp
index 0e9a972a3fb..c9481f2e6b8 100644
--- a/src/server/scripts/World/go_scripts.cpp
+++ b/src/server/scripts/World/go_scripts.cpp
@@ -834,12 +834,11 @@ class go_soulwell : public GameObjectScript
{
}
- /// Due to the fact that this GameObject triggers CMSG_GAMEOBJECT_USE
- /// _and_ CMSG_GAMEOBJECT_REPORT_USE, this GossipHello hook is called
- /// twice. The script's handling is fine as it won't remove two charges
- /// on the well. We have to find how to segregate REPORT_USE and USE.
- bool GossipHello(Player* player) override
+ bool GossipHello(Player* player, bool isUse) override
{
+ if (!isUse)
+ return true;
+
Unit* owner = go->GetOwner();
if (!owner || owner->GetTypeId() != TYPEID_PLAYER || !player->IsInSameRaidWith(owner->ToPlayer()))
return true;
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index 5d8d0819102..2d14e3301a3 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -45,6 +45,7 @@
#include "RealmList.h"
#include "DatabaseLoader.h"
#include "AppenderDB.h"
+#include "Metric.h"
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
#include <boost/asio/io_service.hpp>
@@ -200,6 +201,13 @@ extern int main(int argc, char** argv)
LoadRealmInfo();
+ sMetric->Initialize(realm.Name, _ioService, []()
+ {
+ TC_METRIC_VALUE("online_players", sWorld->GetPlayerCount());
+ });
+
+ TC_METRIC_EVENT("events", "Worldserver started", "");
+
// Initialize the World
sScriptMgr->SetScriptLoader(AddScripts);
sWorld->SetInitialWorldSettings();
@@ -300,6 +308,9 @@ extern int main(int argc, char** argv)
StopDB();
+ TC_METRIC_EVENT("events", "Worldserver shutdown", "");
+ sMetric->ForceSend();
+
TC_LOG_INFO("server.worldserver", "Halting process...");
ShutdownCLIThread(cliThread);
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 3cd2222339c..73b5719ec18 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -37,6 +37,7 @@
# LOGGING SYSTEM SETTINGS
# CURRENCIES SETTINGS
# PACKET SPOOF PROTECTION SETTINGS
+# METRIC SETTINGS
#
###################################################################################################
@@ -3301,7 +3302,7 @@ AuctionHouseBot.Items.Amount.Yellow = 0
# Armor: 8
# Reagent: 1
# Projectile: 2
-# TradeGod: 10
+# TradeGood: 10
# Generic: 1
# Recipe: 6
# Quiver: 1
@@ -3853,3 +3854,42 @@ PacketSpoof.BanDuration = 86400
#
###################################################################################################
+
+###################################################################################################
+# METRIC SETTINGS
+#
+# These settings control the statistics sent to the metric database (currently InfluxDB)
+#
+# Metric.Enable
+# Description: Enables statistics sent to the metric database.
+# Default: 0 - (Disabled)
+# 1 - (Enabled)
+
+Metric.Enable = 0
+
+#
+# Metric.Interval
+# Description: Interval between every batch of data sent in seconds
+# Default: 10 seconds
+#
+
+Metric.Interval = 10
+
+#
+# Metric.ConnectionInfo
+# Description: Connection settings for metric database (currently InfluxDB).
+# Example: "hostname;port;database"
+# Default: "127.0.0.1;8086;worldserver"
+
+Metric.ConnectionInfo = "127.0.0.1;8086;worldserver"
+
+#
+# Metric.OverallStatusInterval
+# Description: Interval between every gathering of overall worldserver status data in seconds
+# Default: 1 second
+#
+
+Metric.OverallStatusInterval = 1
+
+#
+###################################################################################################