diff options
Diffstat (limited to 'src')
238 files changed, 9196 insertions, 5306 deletions
diff --git a/src/common/Collision/DynamicTree.cpp b/src/common/Collision/DynamicTree.cpp index 5d2e18b1a2e..111606644de 100644 --- a/src/common/Collision/DynamicTree.cpp +++ b/src/common/Collision/DynamicTree.cpp @@ -248,7 +248,7 @@ bool DynamicMapTree::isInLineOfSight(float x1, float y1, float z1, float x2, flo float DynamicMapTree::getHeight(float x, float y, float z, float maxSearchDist, uint32 phasemask) const { - G3D::Vector3 v(x, y, z); + G3D::Vector3 v(x, y, z + 0.5f); G3D::Ray r(v, G3D::Vector3(0, 0, -1)); DynamicTreeIntersectionCallback callback(phasemask); impl->intersectZAllignedRay(r, callback, maxSearchDist); diff --git a/src/common/Collision/Management/MMapFactory.h b/src/common/Collision/Management/MMapFactory.h index 6dda7a40a22..f89f44ba4df 100644 --- a/src/common/Collision/Management/MMapFactory.h +++ b/src/common/Collision/Management/MMapFactory.h @@ -43,7 +43,6 @@ namespace MMAP public: static MMapManager* createOrGetMMapManager(); static void clear(); - static bool IsPathfindingEnabled(uint32 mapId); }; } diff --git a/src/common/Collision/Maps/MapTree.cpp b/src/common/Collision/Maps/MapTree.cpp index 4d0996e1961..5907971efb9 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/Debugging/WheatyExceptionReport.cpp b/src/common/Debugging/WheatyExceptionReport.cpp index 5b9a1b1bd6c..72abec161b9 100644 --- a/src/common/Debugging/WheatyExceptionReport.cpp +++ b/src/common/Debugging/WheatyExceptionReport.cpp @@ -901,7 +901,7 @@ unsigned /*cbBuffer*/) // will return true. bool bHandled; pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, pSym->ModBase, pSym->TypeIndex, - 0, pVariable, bHandled, pSym->Name, "", false, true); + pVariable, bHandled, pSym->Name, "", false, true); if (!bHandled) { @@ -934,7 +934,6 @@ char * WheatyExceptionReport::DumpTypeIndex( char * pszCurrBuffer, DWORD64 modBase, DWORD dwTypeIndex, -unsigned nestingLevel, DWORD_PTR offset, bool & bHandled, const char* Name, @@ -1022,14 +1021,14 @@ bool logChildren) FormatOutputValue(buffer, btVoid, sizeof(PVOID), (PVOID)offset, sizeof(buffer)); symbolDetails.top().Value = buffer; - if (nestingLevel >= WER_MAX_NESTING_LEVEL) + if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL) logChildren = false; // no need to log any children since the address is invalid anyway if (address == NULL || address == DWORD_PTR(-1)) logChildren = false; - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, address, bHandled, Name, addressStr, false, logChildren); if (!bHandled) @@ -1074,19 +1073,19 @@ bool logChildren) switch (innerTypeTag) { case SymTagUDT: - if (nestingLevel >= WER_MAX_NESTING_LEVEL) + if (symbolDetails.size() >= WER_MAX_NESTING_LEVEL) logChildren = false; - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); break; case SymTagPointerType: if (Name != NULL && Name[0] != '\0') symbolDetails.top().Name = Name; - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); break; case SymTagArrayType: - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, offset, bHandled, symbolDetails.top().Name.c_str(), "", false, logChildren); break; default: @@ -1100,7 +1099,7 @@ bool logChildren) symbolDetails.top().HasChildren = true; BasicType basicType = btNoType; - pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, nestingLevel + 1, + pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, innerTypeID, offset, bHandled, Name, "", false, false); // Set Value back to an empty string since the Array object itself has no value, only its elements have @@ -1222,7 +1221,7 @@ bool logChildren) DWORD_PTR dwFinalOffset = offset + dwMemberOffset; pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase, - children.ChildId[i], nestingLevel+1, + children.ChildId[i], dwFinalOffset, bHandled2, ""/*Name */, "", true, true); // If the child wasn't a UDT, format it appropriately diff --git a/src/common/Debugging/WheatyExceptionReport.h b/src/common/Debugging/WheatyExceptionReport.h index eb62d8bceef..34919b19e01 100644 --- a/src/common/Debugging/WheatyExceptionReport.h +++ b/src/common/Debugging/WheatyExceptionReport.h @@ -13,7 +13,7 @@ #define countof _countof #define WER_MAX_ARRAY_ELEMENTS_COUNT 10 -#define WER_MAX_NESTING_LEVEL 5 +#define WER_MAX_NESTING_LEVEL 4 #define WER_LARGE_BUFFER_SIZE 1024 * 128 enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK @@ -173,7 +173,7 @@ class WheatyExceptionReport static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME64 *, char * pszBuffer, unsigned cbBuffer); - static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool &, const char*, char*, bool, bool); + static char * DumpTypeIndex(char *, DWORD64, DWORD, DWORD_PTR, bool &, const char*, char*, bool, bool); static void FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress, size_t bufferSize, size_t countOverride = 0); diff --git a/src/common/GitRevision.cpp b/src/common/GitRevision.cpp index 2162e36847f..702cd01d84b 100644 --- a/src/common/GitRevision.cpp +++ b/src/common/GitRevision.cpp @@ -41,19 +41,30 @@ char const* GitRevision::GetFullDatabase() return _FULL_DATABASE; } -#define _PACKAGENAME "TrinityCore" - -char const* GitRevision::GetFullVersion() -{ #if PLATFORM == PLATFORM_WINDOWS -# ifdef _WIN64 - return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Win64, " _BUILD_DIRECTIVE ")"; -# else - return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Win32, " _BUILD_DIRECTIVE ")"; -# endif +# ifdef _WIN64 +# define TRINITY_PLATFORM_STR "Win64" +# else +# define TRINITY_PLATFORM_STR "Win32" +# endif +#elif PLATFORM == PLATFORM_APPLE +# define TRINITY_PLATFORM_STR "MacOSX" +#elif PLATFORM == PLATFORM_INTEL +# define TRINITY_PLATFORM_STR "Intel" +#else // PLATFORM_UNIX +# define TRINITY_PLATFORM_STR "Unix" +#endif + +#ifndef TRINITY_API_USE_DYNAMIC_LINKING +# define TRINITY_LINKAGE_TYPE_STR "Static" #else - return _PACKAGENAME " rev. " VER_PRODUCTVERSION_STR " (Unix, " _BUILD_DIRECTIVE ")"; +# define TRINITY_LINKAGE_TYPE_STR "Dynamic" #endif + +char const* GitRevision::GetFullVersion() +{ + return "TrinityCore rev. " VER_PRODUCTVERSION_STR + " (" TRINITY_PLATFORM_STR ", " _BUILD_DIRECTIVE ", " TRINITY_LINKAGE_TYPE_STR ")"; } char const* GitRevision::GetCompanyNameStr() 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 0f83b52f9d0..5edb245fd87 100644 --- a/src/common/Utilities/Containers.h +++ b/src/common/Utilities/Containers.h @@ -31,9 +31,9 @@ namespace Trinity namespace Containers { template<class T> - void RandomResizeList(std::list<T> &list, uint32 size) + void RandomResizeList(std::list<T>& list, uint32 size) { - size_t list_size = list.size(); + uint32 list_size = uint32(list.size()); while (list_size > size) { @@ -56,7 +56,7 @@ namespace Trinity if (size) RandomResizeList(listCopy, size); - list = listCopy; + list = std::move(listCopy); } /* @@ -68,7 +68,7 @@ namespace Trinity typename C::value_type const& SelectRandomContainerElement(C const& container) { typename C::const_iterator it = container.begin(); - std::advance(it, urand(0, container.size() - 1)); + std::advance(it, urand(0, uint32(container.size()) - 1)); return *it; } @@ -170,7 +170,6 @@ namespace Trinity ++itr; } } - } //! namespace Containers } diff --git a/src/common/Utilities/EventProcessor.cpp b/src/common/Utilities/EventProcessor.cpp index be74d58b790..2341d0a0872 100644 --- a/src/common/Utilities/EventProcessor.cpp +++ b/src/common/Utilities/EventProcessor.cpp @@ -17,11 +17,20 @@ */ #include "EventProcessor.h" +#include "Errors.h" -EventProcessor::EventProcessor() +void BasicEvent::ScheduleAbort() { - m_time = 0; - m_aborting = false; + ASSERT(IsRunning() + && "Tried to scheduled the abortion of an event twice!"); + m_abortState = AbortState::STATE_ABORT_SCHEDULED; +} + +void BasicEvent::SetAborted() +{ + ASSERT(!IsAborted() + && "Tried to abort an already aborted event!"); + m_abortState = AbortState::STATE_ABORTED; } EventProcessor::~EventProcessor() @@ -39,55 +48,73 @@ void EventProcessor::Update(uint32 p_time) while (((i = m_events.begin()) != m_events.end()) && i->first <= m_time) { // get and remove event from queue - BasicEvent* Event = i->second; + BasicEvent* event = i->second; m_events.erase(i); - if (!Event->to_Abort) + if (event->IsRunning()) { - if (Event->Execute(m_time, p_time)) + if (event->Execute(m_time, p_time)) { // completely destroy event if it is not re-added - delete Event; + delete event; } + continue; } - else + + if (event->IsAbortScheduled()) { - Event->Abort(m_time); - delete Event; + event->Abort(m_time); + // Mark the event as aborted + event->SetAborted(); } + + if (event->IsDeletable()) + { + delete event; + continue; + } + + // Reschedule non deletable events to be checked at + // the next update tick + AddEvent(event, CalculateTime(1), false); } } void EventProcessor::KillAllEvents(bool force) { - // prevent event insertions - m_aborting = true; - - // first, abort all existing events - for (EventList::iterator i = m_events.begin(); i != m_events.end();) + for (auto itr = m_events.begin(); itr != m_events.end();) { - EventList::iterator i_old = i; - ++i; - - i_old->second->to_Abort = true; - i_old->second->Abort(m_time); - if (force || i_old->second->IsDeletable()) + // Abort events which weren't aborted already + if (!itr->second->IsAborted()) { - delete i_old->second; + itr->second->SetAborted(); + itr->second->Abort(m_time); + } - if (!force) // need per-element cleanup - m_events.erase (i_old); + // Skip non-deletable events when we are + // not forcing the event cancellation. + if (!force && !itr->second->IsDeletable()) + { + ++itr; + continue; } + + delete itr->second; + + if (force) + ++itr; // Clear the whole container when forcing + else + itr = m_events.erase(itr); } - // fast clear event list (in force case) if (force) m_events.clear(); } void EventProcessor::AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime) { - if (set_addtime) Event->m_addTime = m_time; + if (set_addtime) + Event->m_addTime = m_time; Event->m_execTime = e_time; m_events.insert(std::pair<uint64, BasicEvent*>(e_time, Event)); } @@ -96,4 +123,3 @@ uint64 EventProcessor::CalculateTime(uint64 t_offset) const { return(m_time + t_offset); } - diff --git a/src/common/Utilities/EventProcessor.h b/src/common/Utilities/EventProcessor.h index e10558e6a21..57f3065f323 100644 --- a/src/common/Utilities/EventProcessor.h +++ b/src/common/Utilities/EventProcessor.h @@ -20,20 +20,27 @@ #define __EVENTPROCESSOR_H #include "Define.h" - #include <map> +class EventProcessor; + // Note. All times are in milliseconds here. class TC_COMMON_API BasicEvent { + friend class EventProcessor; + + enum class AbortState : uint8 + { + STATE_RUNNING, + STATE_ABORT_SCHEDULED, + STATE_ABORTED + }; + public: BasicEvent() - { - to_Abort = false; - m_addTime = 0; - m_execTime = 0; - } + : m_abortState(AbortState::STATE_RUNNING), m_addTime(0), m_execTime(0) { } + virtual ~BasicEvent() { } // override destructor to perform some actions on event removal // this method executes when the event is triggered @@ -45,8 +52,16 @@ class TC_COMMON_API BasicEvent virtual void Abort(uint64 /*e_time*/) { } // this method executes when the event is aborted - bool to_Abort; // set by externals when the event is aborted, aborted events don't execute - // and get Abort call when deleted + // Aborts the event at the next update tick + void ScheduleAbort(); + + private: + void SetAborted(); + bool IsRunning() const { return (m_abortState == AbortState::STATE_RUNNING); } + bool IsAbortScheduled() const { return (m_abortState == AbortState::STATE_ABORT_SCHEDULED); } + bool IsAborted() const { return (m_abortState == AbortState::STATE_ABORTED); } + + AbortState m_abortState; // set by externals when the event is aborted, aborted events don't execute // these can be used for time offset control uint64 m_addTime; // time when the event was added to queue, filled by event handler @@ -58,16 +73,17 @@ typedef std::multimap<uint64, BasicEvent*> EventList; class TC_COMMON_API EventProcessor { public: - EventProcessor(); + EventProcessor() : m_time(0) { } ~EventProcessor(); void Update(uint32 p_time); void KillAllEvents(bool force); void AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime = true); uint64 CalculateTime(uint64 t_offset) const; + protected: uint64 m_time; EventList m_events; - bool m_aborting; }; + #endif diff --git a/src/server/database/Database/DatabaseWorkerPool.cpp b/src/server/database/Database/DatabaseWorkerPool.cpp index ba2a4256919..6fc4dc21a51 100644 --- a/src/server/database/Database/DatabaseWorkerPool.cpp +++ b/src/server/database/Database/DatabaseWorkerPool.cpp @@ -28,7 +28,7 @@ DatabaseWorkerPool<T>::DatabaseWorkerPool() { WPFatal(mysql_thread_safe(), "Used MySQL library isn't thread-safe."); WPFatal(mysql_get_client_version() >= MIN_MYSQL_CLIENT_VERSION, "TrinityCore does not support MySQL versions below 5.1"); - WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s).", + WPFatal(mysql_get_client_version() == MYSQL_VERSION_ID, "Used MySQL library version (%s) does not match the version used to compile TrinityCore (%s). Search on forum for TCE00011.", mysql_get_client_info(), MYSQL_SERVER_VERSION); } diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index ce01b0eb245..bb0e2bb09d4 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -56,7 +56,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_RACE, "SELECT race FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_LEVEL, "SELECT level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_ZONE, "SELECT zone FROM characters WHERE guid = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level FROM characters WHERE guid = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_CHARACTER_NAME_DATA, "SELECT race, class, gender, level, name FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_POSITION_XYZ, "SELECT map, position_x, position_y, position_z FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_POSITION, "SELECT position_x, position_y, position_z, orientation, map, taxi_path FROM characters WHERE guid = ?", CONNECTION_SYNCH); @@ -157,11 +157,9 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_DEL_GIFT, "DELETE FROM character_gifts WHERE item_guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_GIFT_BY_ITEM, "SELECT entry, flags FROM character_gifts WHERE item_guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_ACCOUNT_BY_NAME, "SELECT account FROM characters WHERE name = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHARACTER_DATA_BY_GUID, "SELECT account, name, level FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, "DELETE FROM account_instance_times WHERE accountId = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, "INSERT INTO account_instance_times (accountId, instanceId, releaseTime) VALUES (?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_NAME_CLASS, "SELECT name, class FROM characters WHERE guid = ?", CONNECTION_SYNCH); - PrepareStatement(CHAR_SEL_CHARACTER_NAME, "SELECT name FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_MATCH_MAKER_RATING, "SELECT matchMakerRating FROM character_arena_stats WHERE guid = ? AND slot = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHARACTER_COUNT, "SELECT account, COUNT(guid) FROM characters WHERE account = ? GROUP BY account", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_NAME, "UPDATE characters set name = ?, at_login = at_login & ~ ? WHERE guid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index 430243a8f1e..0cac6d35b55 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -146,13 +146,11 @@ enum CharacterDatabaseStatements CHAR_DEL_ACCOUNT_INSTANCE_LOCK_TIMES, CHAR_INS_ACCOUNT_INSTANCE_LOCK_TIMES, CHAR_SEL_CHARACTER_NAME_CLASS, - CHAR_SEL_CHARACTER_NAME, CHAR_SEL_MATCH_MAKER_RATING, CHAR_SEL_CHARACTER_COUNT, CHAR_UPD_NAME, CHAR_UPD_NAME_BY_GUID, CHAR_DEL_DECLINED_NAME, - CHAR_SEL_CHARACTER_DATA_BY_GUID, CHAR_INS_GUILD, CHAR_DEL_GUILD, diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 7a183d5bf78..83720c1a996 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -30,8 +30,8 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_SMARTAI_WP, "SELECT entry, pointid, position_x, position_y, position_z FROM waypoints ORDER BY entry, pointid", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_GAMEOBJECT, "DELETE FROM gameobject WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_EVENT_GAMEOBJECT, "DELETE FROM game_event_gameobject WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO game_graveyard_zone (id, ghost_zone, faction) VALUES (?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM game_graveyard_zone WHERE id = ? AND ghost_zone = ? AND faction = ?", CONNECTION_ASYNC); + PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO graveyard_zone (ID, GhostZone, Faction) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM graveyard_zone WHERE ID = ? AND GhostZone = ? AND Faction = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_INS_GAME_TELE, "INSERT INTO game_tele (id, position_x, position_y, position_z, orientation, map, name) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_GAME_TELE, "DELETE FROM game_tele WHERE name = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_INS_NPC_VENDOR, "INSERT INTO npc_vendor (entry, item, maxcount, incrtime, extendedcost) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC); diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp index 0d51f8ed060..8515da9f6f8 100644 --- a/src/server/database/Updater/DBUpdater.cpp +++ b/src/server/database/Updater/DBUpdater.cpp @@ -55,7 +55,7 @@ bool DBUpdaterUtil::CheckExecutable() } } - TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").", + TC_LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").", absolute(exe).generic_string().c_str()); return false; @@ -178,7 +178,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool) // Path of temp file static Path const temp("create_table.sql"); - // Create temporary query to use external mysql cli + // Create temporary query to use external MySQL CLi std::ofstream file(temp.generic_string()); if (!file.is_open()) { @@ -197,7 +197,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool) } catch (UpdateException&) { - TC_LOG_FATAL("sql.updates", "Failed to create database %s! Has the user `CREATE` priviliges?", pool.GetConnectionInfo()->database.c_str()); + TC_LOG_FATAL("sql.updates", "Failed to create database %s! Does the user (named in *.conf) have `CREATE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str()); boost::filesystem::remove(temp); return false; } @@ -280,7 +280,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool) { case LOCATION_REPOSITORY: { - TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing, try to clone the source again.", + TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing. Try fixing it by cloning the source again.", base.generic_string().c_str()); break; @@ -288,7 +288,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool) case LOCATION_DOWNLOAD: { TC_LOG_ERROR("sql.updates", ">> File \"%s\" is missing, download it from \"https://github.com/TrinityCore/TrinityCore/releases\"" \ - " and place it in your server directory.", base.filename().generic_string().c_str()); + " uncompress it and place the file TDB_full_world_(a_variable_name).sql in your worldserver directory.", base.filename().generic_string().c_str()); break; } } @@ -355,7 +355,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos if (!std::isdigit(port_or_socket[0])) { - // We can't check here if host == "." because is named localhost if socket option is enabled + // We can't check if host == "." here, because it is named localhost if socket option is enabled args.push_back("-P0"); args.push_back("--protocol=SOCKET"); args.push_back("-S" + port_or_socket); @@ -383,7 +383,10 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos if (ret != EXIT_SUCCESS) { TC_LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \ - " If you are an user pull the latest revision from the repository. If you are a developer fix your sql query.", + " If you are a user, please pull the latest revision from the repository. " + "Also make sure you have not applied any of the databases with your sql client. " + "You cannot use auto-update system and import sql files from TrinityCore repository with your sql client. " + "If you are a developer, please fix your sql query.", path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str()); throw UpdateException("update failed"); diff --git a/src/server/game/AI/CoreAI/UnitAI.cpp b/src/server/game/AI/CoreAI/UnitAI.cpp index 844bd45ffeb..037d9e59cf2 100644 --- a/src/server/game/AI/CoreAI/UnitAI.cpp +++ b/src/server/game/AI/CoreAI/UnitAI.cpp @@ -267,35 +267,59 @@ SpellTargetSelector::SpellTargetSelector(Unit* caster, uint32 spellId) : bool SpellTargetSelector::operator()(Unit const* target) const { - if (!target) - return false; - - if (_spellInfo->CheckTarget(_caster, target) != SPELL_CAST_OK) + if (!target || _spellInfo->CheckTarget(_caster, target) != SPELL_CAST_OK) return false; // copypasta from Spell::CheckRange - uint32 range_type = _spellInfo->RangeEntry ? _spellInfo->RangeEntry->type : 0; - float max_range = _caster->GetSpellMaxRangeForTarget(target, _spellInfo); - float min_range = _caster->GetSpellMinRangeForTarget(target, _spellInfo); - - - if (target && target != _caster) + float minRange = 0.0f; + float maxRange = 0.0f; + float rangeMod = 0.0f; + if (_spellInfo->RangeEntry) { - if (range_type == SPELL_RANGE_MELEE) + if (_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE) { - // Because of lag, we can not check too strictly here. - if (!_caster->IsWithinMeleeRange(target, max_range)) - return false; - } - else if (!_caster->IsWithinCombatRange(target, max_range)) - return false; + rangeMod = _caster->GetCombatReach() + 4.0f / 3.0f; + rangeMod += target->GetCombatReach(); - if (range_type == SPELL_RANGE_RANGED) + rangeMod = std::max(rangeMod, NOMINAL_MELEE_RANGE); + } + else { - if (_caster->IsWithinMeleeRange(target)) - return false; + float meleeRange = 0.0f; + if (_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED) + { + meleeRange = _caster->GetCombatReach() + 4.0f / 3.0f; + meleeRange += target->GetCombatReach(); + + meleeRange = std::max(meleeRange, NOMINAL_MELEE_RANGE); + } + + minRange = _caster->GetSpellMinRangeForTarget(target, _spellInfo) + meleeRange; + maxRange = _caster->GetSpellMaxRangeForTarget(target, _spellInfo); + + rangeMod = _caster->GetCombatReach(); + rangeMod += target->GetCombatReach(); + + if (minRange > 0.0f && !(_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED)) + minRange += rangeMod; } - else if (min_range && _caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0 + + if (_caster->isMoving() && target->isMoving() && !_caster->IsWalking() && !target->IsWalking() && + (_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + rangeMod += 5.0f / 3.0f; + } + + maxRange += rangeMod; + + minRange *= minRange; + maxRange *= maxRange; + + if (target != _caster) + { + if (_caster->GetExactDistSq(target) > maxRange) + return false; + + if (minRange > 0.0f && _caster->GetExactDistSq(target) < minRange) return false; } diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 344ccc06249..0dd09bc8051 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -148,15 +148,29 @@ class TC_GAME_API UnitAI { ThreatContainer::StorageType const& threatlist = me->getThreatManager().getThreatList(); if (position >= threatlist.size()) - return NULL; + return nullptr; std::list<Unit*> targetList; + Unit* currentVictim = nullptr; + if (auto currentVictimReference = me->getThreatManager().getCurrentVictim()) + { + currentVictim = currentVictimReference->getTarget(); + + // Current victim always goes first + if (currentVictim && predicate(currentVictim)) + targetList.push_back(currentVictim); + } + for (ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr) - if (predicate((*itr)->getTarget())) + { + if (currentVictim != nullptr && (*itr)->getTarget() != currentVictim && predicate((*itr)->getTarget())) targetList.push_back((*itr)->getTarget()); + else if (currentVictim == nullptr && predicate((*itr)->getTarget())) + targetList.push_back((*itr)->getTarget()); + } if (position >= targetList.size()) - return NULL; + return nullptr; if (targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST) targetList.sort(Trinity::ObjectDistanceOrderPred(me)); @@ -187,7 +201,7 @@ class TC_GAME_API UnitAI break; } - return NULL; + return nullptr; } void SelectTargetList(std::list<Unit*>& targetList, uint32 num, SelectAggroTarget targetType, float dist = 0.0f, bool playerOnly = false, int32 aura = 0); @@ -242,6 +256,7 @@ class TC_GAME_API UnitAI void DoAddAuraToAllHostilePlayers(uint32 spellid); void DoCast(uint32 spellId); void DoCast(Unit* victim, uint32 spellId, bool triggered = false); + void DoCastSelf(uint32 spellId, bool triggered = false) { DoCast(me, spellId, triggered); } void DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false); void DoCastVictim(uint32 spellId, bool triggered = false); void DoCastAOE(uint32 spellId, bool triggered = false); diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index d52306b8a2e..15bbff2793f 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -46,7 +46,7 @@ void CreatureAI::Talk(uint8 id, WorldObject const* whisperTarget /*= nullptr*/) sCreatureTextMgr->SendChat(me, id, whisperTarget); } -void CreatureAI::DoZoneInCombat(Creature* creature /*= NULL*/, float maxRangeToNearestTarget /* = 50.0f*/) +void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRangeToNearestTarget /* = 250.0f*/) { if (!creature) creature = me; @@ -265,7 +265,7 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) me->DeleteThreatList(); me->CombatStop(true); me->LoadCreaturesAddon(); - me->SetLootRecipient(NULL); + me->SetLootRecipient(nullptr); me->ResetPlayerDamageReq(); me->SetLastDamagedTime(0); me->SetCannotReachTarget(false); diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index 31b53b888a6..f8fa6ba532b 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -112,7 +112,7 @@ class TC_GAME_API CreatureAI : public UnitAI // Called for reaction at stopping attack at no attackers or targets virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER); - // Called for reaction at enter to combat if not in combat yet (enemy can be NULL) + // Called for reaction at enter to combat if not in combat yet (enemy can be nullptr) virtual void EnterCombat(Unit* /*victim*/) { } // Called when the creature is killed @@ -149,7 +149,7 @@ class TC_GAME_API CreatureAI : public UnitAI // Called at reaching home after evade virtual void JustReachedHome() { } - void DoZoneInCombat(Creature* creature = NULL, float maxRangeToNearestTarget = 50.0f); + void DoZoneInCombat(Creature* creature = nullptr, float maxRangeToNearestTarget = 250.0f); // Called at text emote receive from player virtual void ReceiveEmote(Player* /*player*/, uint32 /*emoteId*/) { } diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp index 12bfb86251b..d5c17cce3ad 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.cpp +++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp @@ -19,6 +19,51 @@ #include "SpellAuras.h" #include "SpellAuraEffects.h" +static const uint8 NUM_TALENT_TREES = 3; +static const uint8 NUM_SPEC_ICONICS = 3; + +enum Specs +{ + SPEC_WARRIOR_ARMS = 0, + SPEC_WARRIOR_FURY = 1, + SPEC_WARRIOR_PROTECTION = 2, + + SPEC_PALADIN_HOLY = 0, + SPEC_PALADIN_PROTECTION = 1, + SPEC_PALADIN_RETRIBUTION = 2, + + SPEC_HUNTER_BEAST_MASTERY = 0, + SPEC_HUNTER_MARKSMANSHIP = 1, + SPEC_HUNTER_SURVIVAL = 2, + + SPEC_ROGUE_ASSASSINATION = 0, + SPEC_ROGUE_COMBAT = 1, + SPEC_ROGUE_SUBLETY = 2, + + SPEC_PRIEST_DISCIPLINE = 0, + SPEC_PRIEST_HOLY = 1, + SPEC_PRIEST_SHADOW = 2, + + SPEC_DEATH_KNIGHT_BLOOD = 0, + SPEC_DEATH_KNIGHT_FROST = 1, + SPEC_DEATH_KNIGHT_UNHOLY = 2, + + SPEC_SHAMAN_ELEMENTAL = 0, + SPEC_SHAMAN_ENHANCEMENT = 1, + SPEC_SHAMAN_RESTORATION = 2, + + SPEC_MAGE_ARCANE = 0, + SPEC_MAGE_FIRE = 1, + SPEC_MAGE_FROST = 2, + + SPEC_WARLOCK_AFFLICTION = 0, + SPEC_WARLOCK_DEMONOLOGY = 1, + SPEC_WARLOCK_DESTRUCTION = 2, + + SPEC_DRUID_BALANCE = 0, + SPEC_DRUID_FERAL = 1, + SPEC_DRUID_RESTORATION = 2 +}; enum Spells { /* Generic */ @@ -27,29 +72,449 @@ enum Spells SPELL_THROW = 2764, SPELL_SHOOT_WAND = 5019, - /* Paladin */ - PASSIVE_ILLUMINATION = 20215, + /* Warrior - Generic */ + SPELL_BATTLE_STANCE = 2457, + SPELL_BERSERKER_STANCE = 2458, + SPELL_DEFENSIVE_STANCE = 71, + SPELL_CHARGE = 11578, + SPELL_INTERCEPT = 20252, + SPELL_ENRAGED_REGEN = 55694, + SPELL_INTIMIDATING_SHOUT= 5246, + SPELL_PUMMEL = 6552, + SPELL_SHIELD_BASH = 72, + SPELL_BLOODRAGE = 2687, + + /* Warrior - Arms */ + SPELL_SWEEPING_STRIKES = 12328, + SPELL_MORTAL_STRIKE = 12294, + SPELL_BLADESTORM = 46924, + SPELL_REND = 47465, + SPELL_RETALIATION = 20230, + SPELL_SHATTERING_THROW = 64382, + SPELL_THUNDER_CLAP = 47502, + + /* Warrior - Fury */ + SPELL_DEATH_WISH = 12292, + SPELL_BLOODTHIRST = 23881, + PASSIVE_TITANS_GRIP = 46917, + SPELL_DEMO_SHOUT = 47437, + SPELL_EXECUTE = 47471, + SPELL_HEROIC_FURY = 60970, + SPELL_RECKLESSNESS = 1719, + SPELL_PIERCING_HOWL = 12323, + + /* Warrior - Protection */ + SPELL_VIGILANCE = 50720, + SPELL_DEVASTATE = 20243, + SPELL_SHOCKWAVE = 46968, + SPELL_CONCUSSION_BLOW = 12809, + SPELL_DISARM = 676, + SPELL_LAST_STAND = 12975, + SPELL_SHIELD_BLOCK = 2565, + SPELL_SHIELD_SLAM = 47488, + SPELL_SHIELD_WALL = 871, + SPELL_SPELL_REFLECTION = 23920, + + /* Paladin - Generic */ + SPELL_AURA_MASTERY = 31821, + SPELL_LAY_ON_HANDS = 48788, + SPELL_BLESSING_OF_MIGHT = 48932, + SPELL_AVENGING_WRATH = 31884, + SPELL_DIVINE_PROTECTION = 498, + SPELL_DIVINE_SHIELD = 642, + SPELL_HAMMER_OF_JUSTICE = 10308, + SPELL_HAND_OF_FREEDOM = 1044, + SPELL_HAND_OF_PROTECTION = 10278, + SPELL_HAND_OF_SACRIFICE = 6940, + + /* Paladin - Holy*/ + PASSIVE_ILLUMINATION = 20215, + SPELL_HOLY_SHOCK = 20473, + SPELL_BEACON_OF_LIGHT = 53563, + SPELL_CONSECRATION = 48819, + SPELL_FLASH_OF_LIGHT = 48785, + SPELL_HOLY_LIGHT = 48782, + SPELL_DIVINE_FAVOR = 20216, + SPELL_DIVINE_ILLUMINATION = 31842, + + /* Paladin - Protection */ + SPELL_BLESS_OF_SANC = 20911, + SPELL_HOLY_SHIELD = 20925, + SPELL_AVENGERS_SHIELD = 48827, + SPELL_DIVINE_SACRIFICE = 64205, + SPELL_HAMMER_OF_RIGHTEOUS = 53595, + SPELL_RIGHTEOUS_FURY = 25780, + SPELL_SHIELD_OF_RIGHTEOUS = 61411, + + /* Paladin - Retribution */ + SPELL_SEAL_OF_COMMAND = 20375, + SPELL_CRUSADER_STRIKE = 35395, + SPELL_DIVINE_STORM = 53385, + SPELL_JUDGEMENT = 20271, + SPELL_HAMMER_OF_WRATH = 48806, + + /* Hunter - Generic */ + SPELL_DETERRENCE = 19263, + SPELL_EXPLOSIVE_TRAP = 49067, + SPELL_FREEZING_ARROW = 60192, + SPELL_RAPID_FIRE = 3045, + SPELL_KILL_SHOT = 61006, + SPELL_MULTI_SHOT = 49048, + SPELL_VIPER_STING = 3034, + + /* Hunter - Beast Mastery */ + SPELL_BESTIAL_WRATH = 19574, + PASSIVE_BEAST_WITHIN = 34692, + PASSIVE_BEAST_MASTERY = 53270, + + /* Hunter - Marksmanship */ + SPELL_AIMED_SHOT = 19434, + PASSIVE_TRUESHOT_AURA = 19506, + SPELL_CHIMERA_SHOT = 53209, + SPELL_ARCANE_SHOT = 49045, + SPELL_STEADY_SHOT = 49052, + SPELL_READINESS = 23989, + SPELL_SILENCING_SHOT = 34490, - /* Priest */ - SPELL_SOUL_WARDING = 63574, - SPELL_SPIRIT_REDEMPTION = 20711, - SPELL_SHADOWFORM = 15473, + /* Hunter - Survival */ + PASSIVE_LOCK_AND_LOAD = 56344, + SPELL_WYVERN_STING = 19386, + SPELL_EXPLOSIVE_SHOT = 53301, + SPELL_BLACK_ARROW = 3674, - /* Shaman */ - SPELL_TIDAL_FORCE = 582, - SPELL_MANA_TIDE_TOTEM = 590, - SPELL_SHA_NATURE_SWIFT = 591, + /* Rogue - Generic */ + SPELL_DISMANTLE = 51722, + SPELL_EVASION = 26669, + SPELL_KICK = 1766, + SPELL_VANISH = 26889, + SPELL_BLIND = 2094, + SPELL_CLOAK_OF_SHADOWS = 31224, + + /* Rogue - Assassination */ + SPELL_COLD_BLOOD = 14177, + SPELL_MUTILATE = 1329, + SPELL_HUNGER_FOR_BLOOD = 51662, + SPELL_ENVENOM = 57993, + + /* Rogue - Combat */ + SPELL_SINISTER_STRIKE = 48637, + SPELL_BLADE_FLURRY = 13877, + SPELL_ADRENALINE_RUSH = 13750, + SPELL_KILLING_SPREE = 51690, + SPELL_EVISCERATE = 48668, + + /* Rogue - Sublety */ + SPELL_HEMORRHAGE = 16511, + SPELL_PREMEDITATION = 14183, + SPELL_SHADOW_DANCE = 51713, + SPELL_PREPARATION = 14185, + SPELL_SHADOWSTEP = 36554, + + /* Priest - Generic */ + SPELL_FEAR_WARD = 6346, + SPELL_POWER_WORD_FORT = 48161, + SPELL_DIVINE_SPIRIT = 48073, + SPELL_SHADOW_PROTECTION = 48169, + SPELL_DIVINE_HYMN = 64843, + SPELL_HYMN_OF_HOPE = 64901, + SPELL_SHADOW_WORD_DEATH = 48158, + SPELL_PSYCHIC_SCREAM = 10890, + + /* Priest - Discipline */ + PASSIVE_SOUL_WARDING = 63574, + SPELL_POWER_INFUSION = 10060, + SPELL_PENANCE = 47540, + SPELL_PAIN_SUPPRESSION = 33206, + SPELL_INNER_FOCUS = 14751, + SPELL_POWER_WORD_SHIELD = 48066, + + /* Priest - Holy */ + PASSIVE_SPIRIT_REDEMPTION = 20711, + SPELL_DESPERATE_PRAYER = 19236, + SPELL_GUARDIAN_SPIRIT = 47788, + SPELL_FLASH_HEAL = 48071, + SPELL_RENEW = 48068, + + /* Priest - Shadow */ + SPELL_VAMPIRIC_EMBRACE = 15286, + SPELL_SHADOWFORM = 15473, + SPELL_VAMPIRIC_TOUCH = 34914, + SPELL_MIND_FLAY = 15407, + SPELL_MIND_BLAST = 48127, + SPELL_SHADOW_WORD_PAIN = 48125, + SPELL_DEVOURING_PLAGUE = 48300, + SPELL_DISPERSION = 47585, + + /* Death Knight - Generic */ + SPELL_DEATH_GRIP = 49576, + SPELL_STRANGULATE = 47476, + SPELL_EMPOWER_RUNE_WEAP = 47568, + SPELL_ICEBORN_FORTITUDE = 48792, + SPELL_ANTI_MAGIC_SHELL = 48707, + SPELL_DEATH_COIL_DK = 49895, + SPELL_MIND_FREEZE = 47528, + SPELL_ICY_TOUCH = 49909, + AURA_FROST_FEVER = 55095, + SPELL_PLAGUE_STRIKE = 49921, + AURA_BLOOD_PLAGUE = 55078, + SPELL_PESTILENCE = 50842, + + /* Death Knight - Blood */ + SPELL_RUNE_TAP = 48982, + SPELL_HYSTERIA = 49016, + SPELL_HEART_STRIKE = 55050, + SPELL_DEATH_STRIKE = 49924, + SPELL_BLOOD_STRIKE = 49930, + SPELL_MARK_OF_BLOOD = 49005, + SPELL_VAMPIRIC_BLOOD = 55233, + + /* Death Knight - Frost */ + PASSIVE_ICY_TALONS = 50887, + SPELL_FROST_STRIKE = 49143, + SPELL_HOWLING_BLAST = 49184, + SPELL_UNBREAKABLE_ARMOR = 51271, + SPELL_OBLITERATE = 51425, + SPELL_DEATHCHILL = 49796, + + /* Death Knight - Unholy */ + PASSIVE_UNHOLY_BLIGHT = 49194, + PASSIVE_MASTER_OF_GHOUL = 52143, + SPELL_SCOURGE_STRIKE = 55090, + SPELL_DEATH_AND_DECAY = 49938, + SPELL_ANTI_MAGIC_ZONE = 51052, + SPELL_SUMMON_GARGOYLE = 49206, + + /* Shaman - Generic */ + SPELL_HEROISM = 32182, + SPELL_BLOODLUST = 2825, + SPELL_GROUNDING_TOTEM = 8177, + + /* Shaman - Elemental*/ + PASSIVE_ELEMENTAL_FOCUS = 16164, + SPELL_TOTEM_OF_WRATH = 30706, + SPELL_THUNDERSTORM = 51490, + SPELL_LIGHTNING_BOLT = 49238, + SPELL_EARTH_SHOCK = 49231, + SPELL_FLAME_SHOCK = 49233, + SPELL_LAVA_BURST = 60043, + SPELL_CHAIN_LIGHTNING = 49271, + SPELL_ELEMENTAL_MASTERY = 16166, + + /* Shaman - Enhancement */ + PASSIVE_SPIRIT_WEAPONS = 16268, + SPELL_LAVA_LASH = 60103, + SPELL_FERAL_SPIRIT = 51533, + AURA_MAELSTROM_WEAPON = 53817, SPELL_STORMSTRIKE = 17364, + SPELL_SHAMANISTIC_RAGE = 30823, + + /* Shaman - Restoration*/ + SPELL_SHA_NATURE_SWIFT = 591, + SPELL_MANA_TIDE_TOTEM = 590, + SPELL_EARTH_SHIELD = 49284, + SPELL_RIPTIDE = 61295, + SPELL_HEALING_WAVE = 49273, + SPELL_LESSER_HEAL_WAVE = 49276, + SPELL_TIDAL_FORCE = 55198, + + /* Mage - Generic */ + SPELL_DAMPEN_MAGIC = 43015, + SPELL_EVOCATION = 12051, + SPELL_MANA_SHIELD = 43020, + SPELL_MIRROR_IMAGE = 55342, + SPELL_SPELLSTEAL = 30449, + SPELL_COUNTERSPELL = 2139, + SPELL_ICE_BLOCK = 45438, + + /* Mage - Arcane */ + SPELL_FOCUS_MAGIC = 54646, + SPELL_ARCANE_POWER = 12042, + SPELL_ARCANE_BARRAGE = 44425, + SPELL_ARCANE_BLAST = 42897, + AURA_ARCANE_BLAST = 36032, + SPELL_ARCANE_MISSILES = 42846, + SPELL_PRESENCE_OF_MIND = 12043, + + /* Mage - Fire */ + SPELL_PYROBLAST = 11366, + SPELL_COMBUSTION = 11129, + SPELL_LIVING_BOMB = 44457, + SPELL_FIREBALL = 42833, + SPELL_FIRE_BLAST = 42873, + SPELL_DRAGONS_BREATH = 31661, + SPELL_BLAST_WAVE = 11113, + + /* Mage - Frost */ + SPELL_ICY_VEINS = 12472, + SPELL_ICE_BARRIER = 11426, + SPELL_DEEP_FREEZE = 44572, + SPELL_FROST_NOVA = 42917, + SPELL_FROSTBOLT = 42842, + SPELL_COLD_SNAP = 11958, + SPELL_ICE_LANCE = 42914, + + /* Warlock - Generic */ + SPELL_FEAR = 6215, + SPELL_HOWL_OF_TERROR = 17928, + SPELL_CORRUPTION = 47813, + SPELL_DEATH_COIL_W = 47860, + SPELL_SHADOW_BOLT = 47809, + SPELL_INCINERATE = 47838, + SPELL_IMMOLATE = 47811, + SPELL_SEED_OF_CORRUPTION = 47836, + + /* Warlock - Affliction */ + PASSIVE_SIPHON_LIFE = 63108, + SPELL_UNSTABLE_AFFLICTION = 30108, + SPELL_HAUNT = 48181, + SPELL_CURSE_OF_AGONY = 47864, + SPELL_DRAIN_SOUL = 47855, - /* Druid */ - SPELL_MOONKIN_FORM = 24858, - SPELL_SWIFTMEND = 18562, - SPELL_DRU_NATURE_SWIFT = 17116, - SPELL_TREE_OF_LIFE = 33891 + /* Warlock - Demonology */ + SPELL_SOUL_LINK = 19028, + SPELL_DEMONIC_EMPOWERMENT = 47193, + SPELL_METAMORPHOSIS = 59672, + SPELL_IMMOLATION_AURA = 50589, + SPELL_DEMON_CHARGE = 54785, + AURA_DECIMATION = 63167, + AURA_MOLTEN_CORE = 71165, + SPELL_SOUL_FIRE = 47825, + + /* Warlock - Destruction */ + SPELL_SHADOWBURN = 17877, + SPELL_CONFLAGRATE = 17962, + SPELL_CHAOS_BOLT = 50796, + SPELL_SHADOWFURY = 47847, + + /* Druid - Generic */ + SPELL_BARKSKIN = 22812, + SPELL_INNERVATE = 29166, + + /* Druid - Balance */ + SPELL_INSECT_SWARM = 5570, + SPELL_MOONKIN_FORM = 24858, + SPELL_STARFALL = 48505, + SPELL_TYPHOON = 61384, + AURA_ECLIPSE_LUNAR = 48518, + SPELL_MOONFIRE = 48463, + SPELL_STARFIRE = 48465, + SPELL_WRATH = 48461, + + /* Druid - Feral */ + SPELL_CAT_FORM = 768, + SPELL_SURVIVAL_INSTINCTS = 61336, + SPELL_MANGLE = 33917, + SPELL_BERSERK = 50334, + SPELL_MANGLE_CAT = 48566, + SPELL_FERAL_CHARGE_CAT = 49376, + SPELL_RAKE = 48574, + SPELL_RIP = 49800, + SPELL_SAVAGE_ROAR = 52610, + SPELL_TIGER_FURY = 50213, + SPELL_CLAW = 48570, + SPELL_DASH = 33357, + SPELL_MAIM = 49802, + + /* Druid - Restoration */ + SPELL_SWIFTMEND = 18562, + SPELL_TREE_OF_LIFE = 33891, + SPELL_WILD_GROWTH = 48438, + SPELL_NATURE_SWIFTNESS = 17116, + SPELL_TRANQUILITY = 48447, + SPELL_NOURISH = 50464, + SPELL_HEALING_TOUCH = 48378, + SPELL_REJUVENATION = 48441, + SPELL_REGROWTH = 48443, + SPELL_LIFEBLOOM = 48451 }; +// As it turns out, finding out "how many points does the player have in spec X" is actually really expensive to do frequently +// So instead, we just check for a handful of spells that, realistically, no spec is gonna go without (and "has spell" is cheap!) +// Can players deliberately trick this check? Yes. +// Is it worth doing? No. +// Close enough. +static const uint32 SPEC_ICONICS[MAX_CLASSES][NUM_TALENT_TREES][NUM_SPEC_ICONICS] = { + { // CLASS_NONE + {0,0,0}, + {0,0,0}, + {0,0,0} + }, + { // CLASS_WARRIOR + {SPELL_BLADESTORM, SPELL_MORTAL_STRIKE, SPELL_SWEEPING_STRIKES}, // Arms + {PASSIVE_TITANS_GRIP, SPELL_BLOODTHIRST, SPELL_DEATH_WISH}, // Fury + {SPELL_SHOCKWAVE, SPELL_DEVASTATE, SPELL_VIGILANCE} // Protection + }, + { // CLASS_PALADIN + {SPELL_BEACON_OF_LIGHT, SPELL_HOLY_SHOCK, PASSIVE_ILLUMINATION}, // Holy + {SPELL_HAMMER_OF_RIGHTEOUS, SPELL_HOLY_SHIELD, SPELL_BLESS_OF_SANC}, // Protection + {SPELL_DIVINE_STORM, SPELL_CRUSADER_STRIKE, SPELL_SEAL_OF_COMMAND} // Retribution + }, + { // CLASS_HUNTER + {PASSIVE_BEAST_MASTERY, PASSIVE_BEAST_WITHIN, SPELL_BESTIAL_WRATH}, // Beast Mastery + {SPELL_CHIMERA_SHOT, PASSIVE_TRUESHOT_AURA, SPELL_AIMED_SHOT}, // Marksmanship + {SPELL_EXPLOSIVE_SHOT, SPELL_WYVERN_STING, PASSIVE_LOCK_AND_LOAD} // Survival + }, + { // CLASS_ROGUE + {SPELL_HUNGER_FOR_BLOOD, SPELL_MUTILATE, SPELL_COLD_BLOOD}, // Assassination + {SPELL_KILLING_SPREE, SPELL_ADRENALINE_RUSH, SPELL_BLADE_FLURRY}, // Combat + {SPELL_SHADOW_DANCE, SPELL_PREMEDITATION, SPELL_HEMORRHAGE} // Sublety + }, + { // CLASS_PRIEST + {SPELL_PENANCE, SPELL_POWER_INFUSION, PASSIVE_SOUL_WARDING}, // Discipline + {SPELL_GUARDIAN_SPIRIT, PASSIVE_SPIRIT_REDEMPTION, SPELL_DESPERATE_PRAYER}, // Holy + {SPELL_VAMPIRIC_TOUCH, SPELL_SHADOWFORM, SPELL_VAMPIRIC_EMBRACE} // Shadow + }, + { // CLASS_DEATH_KNIGHT + {SPELL_HEART_STRIKE, SPELL_HYSTERIA, SPELL_RUNE_TAP}, // Blood + {SPELL_HOWLING_BLAST, SPELL_FROST_STRIKE, PASSIVE_ICY_TALONS}, // Frost + {SPELL_SCOURGE_STRIKE, PASSIVE_MASTER_OF_GHOUL, PASSIVE_UNHOLY_BLIGHT} // Unholy + }, + { // CLASS_SHAMAN + {SPELL_THUNDERSTORM, SPELL_TOTEM_OF_WRATH, PASSIVE_ELEMENTAL_FOCUS}, // Elemental + {SPELL_FERAL_SPIRIT, SPELL_LAVA_LASH, PASSIVE_SPIRIT_WEAPONS}, // Enhancement + {SPELL_RIPTIDE, SPELL_MANA_TIDE_TOTEM, SPELL_SHA_NATURE_SWIFT} // Restoration + }, + { // CLASS_MAGE + {SPELL_ARCANE_BARRAGE, SPELL_ARCANE_POWER, SPELL_FOCUS_MAGIC}, // Arcane + {SPELL_LIVING_BOMB, SPELL_COMBUSTION, SPELL_PYROBLAST}, // Fire + {SPELL_DEEP_FREEZE, SPELL_ICE_BARRIER, SPELL_ICY_VEINS} // Frost + }, + { // CLASS_WARLOCK + {SPELL_HAUNT, SPELL_UNSTABLE_AFFLICTION, PASSIVE_SIPHON_LIFE}, // Affliction + {SPELL_METAMORPHOSIS, SPELL_DEMONIC_EMPOWERMENT, SPELL_SOUL_LINK}, // Demonology + {SPELL_CHAOS_BOLT, SPELL_CONFLAGRATE, SPELL_SHADOWBURN} // Destruction + }, + { // CLASS_UNK + {0,0,0}, + {0,0,0}, + {0,0,0} + }, + { // CLASS_DRUID + {SPELL_STARFALL, SPELL_MOONKIN_FORM, SPELL_INSECT_SWARM}, // Balance + {SPELL_BERSERK, SPELL_MANGLE, SPELL_SURVIVAL_INSTINCTS}, // Feral + {SPELL_WILD_GROWTH, SPELL_TREE_OF_LIFE, SPELL_SWIFTMEND} // Restoration + } +}; + +uint8 PlayerAI::GetPlayerSpec(Player const* who) +{ + if (!who) + return 0; + + uint8 wClass = who->getClass(); + for (uint8 tier = 0; tier < NUM_SPEC_ICONICS; ++tier) + for (uint8 tree = 0; tree < NUM_TALENT_TREES; ++tree) + if (SPEC_ICONICS[wClass][tree][tier] && who->HasSpell(SPEC_ICONICS[wClass][tree][tier])) + return tree; + + return 0; +} + bool PlayerAI::IsPlayerHealer(Player const* who) { + if (!who) + return false; + switch (who->getClass()) { case CLASS_WARRIOR: @@ -61,18 +526,21 @@ bool PlayerAI::IsPlayerHealer(Player const* who) default: return false; case CLASS_PALADIN: - return who->HasSpell(PASSIVE_ILLUMINATION); + return (PlayerAI::GetPlayerSpec(who) == SPEC_PALADIN_HOLY); case CLASS_PRIEST: - return who->HasSpell(SPELL_SOUL_WARDING) || who->HasSpell(SPELL_SPIRIT_REDEMPTION); + return (PlayerAI::GetPlayerSpec(who) != SPEC_PRIEST_SHADOW); case CLASS_SHAMAN: - return who->HasSpell(SPELL_MANA_TIDE_TOTEM) || who->HasSpell(SPELL_SHA_NATURE_SWIFT) || who->HasSpell(SPELL_TIDAL_FORCE); + return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_RESTORATION); case CLASS_DRUID: - return who->HasSpell(SPELL_SWIFTMEND) || who->HasSpell(SPELL_DRU_NATURE_SWIFT) || who->HasSpell(SPELL_TREE_OF_LIFE); + return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_RESTORATION); } } bool PlayerAI::IsPlayerRangedAttacker(Player const* who) { + if (!who) + return false; + switch (who->getClass()) { case CLASS_WARRIOR: @@ -94,12 +562,106 @@ bool PlayerAI::IsPlayerRangedAttacker(Player const* who) return false; } case CLASS_PRIEST: - return who->HasSpell(SPELL_SHADOWFORM); + return (PlayerAI::GetPlayerSpec(who) == SPEC_PRIEST_SHADOW); case CLASS_SHAMAN: - return !who->HasSpell(SPELL_STORMSTRIKE); + return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_ELEMENTAL); case CLASS_DRUID: - return who->HasSpell(SPELL_MOONKIN_FORM); + return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_BALANCE); + } +} + +PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, Unit* target) +{ + // Find highest spell rank that we know + uint32 knownRank, nextRank; + if (me->HasSpell(spellId)) + { + // this will save us some lookups if the player has the highest rank (expected case) + knownRank = spellId; + nextRank = sSpellMgr->GetNextSpellInChain(spellId); + } + else + { + knownRank = 0; + nextRank = sSpellMgr->GetFirstSpellInChain(spellId); + } + + while (nextRank && me->HasSpell(nextRank)) + { + knownRank = nextRank; + nextRank = sSpellMgr->GetNextSpellInChain(knownRank); + } + + if (!knownRank) + return {}; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(knownRank); + if (!spellInfo) + return {}; + + if (me->GetSpellHistory()->HasGlobalCooldown(spellInfo)) + return {}; + + Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE); + if (spell->CanAutoCast(target)) + return{ spell, target }; + + delete spell; + return {}; +} + +PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, SpellTarget target) +{ + Unit* pTarget = nullptr; + switch (target) + { + case TARGET_NONE: + break; + case TARGET_VICTIM: + pTarget = me->GetVictim(); + if (!pTarget) + return {}; + break; + case TARGET_CHARMER: + pTarget = me->GetCharmer(); + if (!pTarget) + return {}; + break; + case TARGET_SELF: + pTarget = me; + break; + } + + return VerifySpellCast(spellId, pTarget); +} + +PlayerAI::TargetedSpell PlayerAI::SelectSpellCast(PossibleSpellVector& spells) +{ + uint32 totalWeights = 0; + for (PossibleSpell const& wSpell : spells) + totalWeights += wSpell.second; + + TargetedSpell selected; + uint32 randNum = urand(0, totalWeights - 1); + for (PossibleSpell const& wSpell : spells) + { + if (selected) + { + delete wSpell.first.first; + continue; + } + + if (randNum < wSpell.second) + selected = wSpell.first; + else + { + randNum -= wSpell.second; + delete wSpell.first.first; + } } + + spells.clear(); + return selected; } void PlayerAI::DoRangedAttackIfReady() @@ -150,6 +712,29 @@ void PlayerAI::DoAutoAttackIfReady() DoMeleeAttackIfReady(); } +void PlayerAI::CancelAllShapeshifts() +{ + std::list<AuraEffect*> const& shapeshiftAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); + std::set<Aura*> removableShapeshifts; + for (AuraEffect* auraEff : shapeshiftAuras) + { + Aura* aura = auraEff->GetBase(); + if (!aura) + continue; + SpellInfo const* auraInfo = aura->GetSpellInfo(); + if (!auraInfo) + continue; + if (auraInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL)) + continue; + if (!auraInfo->IsPositive() || auraInfo->IsPassive()) + continue; + removableShapeshifts.insert(aura); + } + + for (Aura* aura : removableShapeshifts) + me->RemoveOwnedAura(aura, AURA_REMOVE_BY_CANCEL); +} + struct UncontrolledTargetSelectPredicate : public std::unary_function<Unit*, bool> { bool operator()(Unit const* target) const @@ -164,7 +749,516 @@ Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const return nullptr; } -void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/) +PlayerAI::TargetedSpell SimpleCharmedPlayerAI::SelectAppropriateCastForSpec() +{ + PossibleSpellVector spells; + + switch (me->getClass()) + { + case CLASS_WARRIOR: + if (!me->IsWithinMeleeRange(me->GetVictim())) + { + VerifyAndPushSpellCast(spells, SPELL_CHARGE, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_INTERCEPT, TARGET_VICTIM, 10); + } + VerifyAndPushSpellCast(spells, SPELL_ENRAGED_REGEN, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_INTIMIDATING_SHOUT, TARGET_VICTIM, 4); + if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING)) + { + VerifyAndPushSpellCast(spells, SPELL_PUMMEL, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_BASH, TARGET_VICTIM, 15); + } + VerifyAndPushSpellCast(spells, SPELL_BLOODRAGE, TARGET_NONE, 5); + switch (GetSpec()) + { + case SPEC_WARRIOR_PROTECTION: + VerifyAndPushSpellCast(spells, SPELL_SHOCKWAVE, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_CONCUSSION_BLOW, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_DISARM, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_LAST_STAND, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_BLOCK, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_SLAM, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_WALL, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_SPELL_REFLECTION, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_DEVASTATE, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 1); + break; + case SPEC_WARRIOR_ARMS: + VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_MORTAL_STRIKE, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_BLADESTORM, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_RETALIATION, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_SHATTERING_THROW, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15); + break; + case SPEC_WARRIOR_FURY: + VerifyAndPushSpellCast(spells, SPELL_DEATH_WISH, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_BLOODTHIRST, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_HEROIC_FURY, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_RECKLESSNESS, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_PIERCING_HOWL, TARGET_VICTIM, 2); + break; + } + break; + case CLASS_PALADIN: + VerifyAndPushSpellCast(spells, SPELL_AURA_MASTERY, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_LAY_ON_HANDS, TARGET_CHARMER, 8); + VerifyAndPushSpellCast(spells, SPELL_BLESSING_OF_MIGHT, TARGET_CHARMER, 8); + VerifyAndPushSpellCast(spells, SPELL_AVENGING_WRATH, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_PROTECTION, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_SHIELD, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_JUSTICE, TARGET_VICTIM, 6); + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_FREEDOM, TARGET_SELF, 3); + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, TARGET_SELF, 1); + if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID())) + { + if (creatureCharmer->IsDungeonBoss() || creatureCharmer->isWorldBoss()) + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_SACRIFICE, creatureCharmer, 10); + else + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, creatureCharmer, 3); + } + + switch (GetSpec()) + { + case SPEC_PALADIN_PROTECTION: + VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_RIGHTEOUS, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_SACRIFICE, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_OF_RIGHTEOUS, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_CONSECRATION, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_HOLY_SHIELD, TARGET_NONE, 1); + break; + case SPEC_PALADIN_HOLY: + VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_FLASH_OF_LIGHT, TARGET_CHARMER, 4); + VerifyAndPushSpellCast(spells, SPELL_HOLY_LIGHT, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_FAVOR, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_ILLUMINATION, TARGET_NONE, 3); + break; + case SPEC_PALADIN_RETRIBUTION: + VerifyAndPushSpellCast(spells, SPELL_CRUSADER_STRIKE, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_STORM, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_WRATH, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_RIGHTEOUS_FURY, TARGET_NONE, 2); + break; + } + break; + case CLASS_HUNTER: + VerifyAndPushSpellCast(spells, SPELL_DETERRENCE, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_TRAP, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_FREEZING_ARROW, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_RAPID_FIRE, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_KILL_SHOT, TARGET_VICTIM, 10); + if (me->GetVictim() && me->GetVictim()->getPowerType() == POWER_MANA && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_VIPER_STING, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_VIPER_STING, TARGET_VICTIM, 5); + + switch (GetSpec()) + { + case SPEC_HUNTER_BEAST_MASTERY: + VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 2); + break; + case SPEC_HUNTER_MARKSMANSHIP: + VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_CHIMERA_SHOT, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_READINESS, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_SILENCING_SHOT, TARGET_VICTIM, 5); + break; + case SPEC_HUNTER_SURVIVAL: + VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_SHOT, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_BLACK_ARROW, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 1); + break; + } + break; + case CLASS_ROGUE: + { + VerifyAndPushSpellCast(spells, SPELL_DISMANTLE, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_EVASION, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_VANISH, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_BLIND, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_CLOAK_OF_SHADOWS, TARGET_NONE, 2); + + uint32 builder = 0, finisher = 0; + switch (GetSpec()) + { + case SPEC_ROGUE_ASSASSINATION: + builder = SPELL_MUTILATE, finisher = SPELL_ENVENOM; + VerifyAndPushSpellCast(spells, SPELL_COLD_BLOOD, TARGET_NONE, 20); + break; + case SPEC_ROGUE_COMBAT: + builder = SPELL_SINISTER_STRIKE, finisher = SPELL_EVISCERATE; + VerifyAndPushSpellCast(spells, SPELL_ADRENALINE_RUSH, TARGET_NONE, 6); + VerifyAndPushSpellCast(spells, SPELL_BLADE_FLURRY, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_KILLING_SPREE, TARGET_NONE, 25); + break; + case SPEC_ROGUE_SUBLETY: + builder = SPELL_HEMORRHAGE, finisher = SPELL_EVISCERATE; + VerifyAndPushSpellCast(spells, SPELL_PREPARATION, TARGET_NONE, 10); + if (!me->IsWithinMeleeRange(me->GetVictim())) + VerifyAndPushSpellCast(spells, SPELL_SHADOWSTEP, TARGET_VICTIM, 25); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_DANCE, TARGET_NONE, 10); + break; + } + + if (Unit* victim = me->GetVictim()) + { + if (victim->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_KICK, TARGET_VICTIM, 25); + + uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0; + if (cp >= 4) + VerifyAndPushSpellCast(spells, finisher, TARGET_VICTIM, 10); + if (cp <= 4) + VerifyAndPushSpellCast(spells, builder, TARGET_VICTIM, 5); + } + break; + } + case CLASS_PRIEST: + VerifyAndPushSpellCast(spells, SPELL_FEAR_WARD, TARGET_SELF, 2); + VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_FORT, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_SPIRIT, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_PROTECTION, TARGET_CHARMER, 2); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_HYMN, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_HYMN_OF_HOPE, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_DEATH, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_PSYCHIC_SCREAM, TARGET_VICTIM, 3); + switch (GetSpec()) + { + case SPEC_PRIEST_DISCIPLINE: + VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_SHIELD, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_INNER_FOCUS, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_PAIN_SUPPRESSION, TARGET_CHARMER, 15); + VerifyAndPushSpellCast(spells, SPELL_POWER_INFUSION, TARGET_CHARMER, 10); + VerifyAndPushSpellCast(spells, SPELL_PENANCE, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1); + break; + case SPEC_PRIEST_HOLY: + VerifyAndPushSpellCast(spells, SPELL_DESPERATE_PRAYER, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_GUARDIAN_SPIRIT, TARGET_CHARMER, 5); + VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_RENEW, TARGET_CHARMER, 3); + break; + case SPEC_PRIEST_SHADOW: + if (!me->HasAura(SPELL_SHADOWFORM)) + { + VerifyAndPushSpellCast(spells, SPELL_SHADOWFORM, TARGET_NONE, 100); + break; + } + if (Unit* victim = me->GetVictim()) + { + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_VAMPIRIC_TOUCH, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_TOUCH, TARGET_VICTIM, 4); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_SHADOW_WORD_PAIN, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_PAIN, TARGET_VICTIM, 3); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_DEVOURING_PLAGUE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_DEVOURING_PLAGUE, TARGET_VICTIM, 4); + } + VerifyAndPushSpellCast(spells, SPELL_MIND_BLAST, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_MIND_FLAY, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_DISPERSION, TARGET_NONE, 10); + break; + } + break; + case CLASS_DEATH_KNIGHT: + { + if (!me->IsWithinMeleeRange(me->GetVictim())) + VerifyAndPushSpellCast(spells, SPELL_DEATH_GRIP, TARGET_VICTIM, 25); + VerifyAndPushSpellCast(spells, SPELL_STRANGULATE, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_EMPOWER_RUNE_WEAP, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_ICEBORN_FORTITUDE, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_SHELL, TARGET_NONE, 10); + + bool hasFF = false, hasBP = false; + if (Unit* victim = me->GetVictim()) + { + if (victim->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_MIND_FREEZE, TARGET_VICTIM, 25); + + hasFF = !!victim->GetAuraApplicationOfRankedSpell(AURA_FROST_FEVER, me->GetGUID()), hasBP = !!victim->GetAuraApplicationOfRankedSpell(AURA_BLOOD_PLAGUE, me->GetGUID()); + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_PESTILENCE, TARGET_VICTIM, 3); + if (!hasFF) + VerifyAndPushSpellCast(spells, SPELL_ICY_TOUCH, TARGET_VICTIM, 4); + if (!hasBP) + VerifyAndPushSpellCast(spells, SPELL_PLAGUE_STRIKE, TARGET_VICTIM, 4); + } + switch (GetSpec()) + { + case SPEC_DEATH_KNIGHT_BLOOD: + VerifyAndPushSpellCast(spells, SPELL_RUNE_TAP, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, TARGET_SELF, 5); + if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID())) + if (!creatureCharmer->IsDungeonBoss() && !creatureCharmer->isWorldBoss()) + VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, creatureCharmer, 15); + VerifyAndPushSpellCast(spells, SPELL_HEART_STRIKE, TARGET_VICTIM, 2); + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_DEATH_STRIKE, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_MARK_OF_BLOOD, TARGET_VICTIM, 20); + VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_BLOOD, TARGET_NONE, 10); + break; + case SPEC_DEATH_KNIGHT_FROST: + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_OBLITERATE, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_HOWLING_BLAST, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_UNBREAKABLE_ARMOR, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_DEATHCHILL, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_FROST_STRIKE, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1); + break; + case SPEC_DEATH_KNIGHT_UNHOLY: + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_SCOURGE_STRIKE, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_DEATH_AND_DECAY, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_ZONE, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_SUMMON_GARGOYLE, TARGET_VICTIM, 7); + VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 3); + break; + } + break; + } + case CLASS_SHAMAN: + VerifyAndPushSpellCast(spells, SPELL_HEROISM, TARGET_NONE, 25); + VerifyAndPushSpellCast(spells, SPELL_BLOODLUST, TARGET_NONE, 25); + VerifyAndPushSpellCast(spells, SPELL_GROUNDING_TOTEM, TARGET_NONE, 2); + switch (GetSpec()) + { + case SPEC_SHAMAN_RESTORATION: + if (Unit* charmer = me->GetCharmer()) + if (!charmer->GetAuraApplicationOfRankedSpell(SPELL_EARTH_SHIELD, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_EARTH_SHIELD, charmer, 2); + if (me->HasAura(SPELL_SHA_NATURE_SWIFT)) + VerifyAndPushSpellCast(spells, SPELL_HEALING_WAVE, TARGET_CHARMER, 20); + else + VerifyAndPushSpellCast(spells, SPELL_LESSER_HEAL_WAVE, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_TIDAL_FORCE, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_SHA_NATURE_SWIFT, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_MANA_TIDE_TOTEM, TARGET_NONE, 3); + break; + case SPEC_SHAMAN_ELEMENTAL: + if (Unit* victim = me->GetVictim()) + { + if (victim->GetAuraOfRankedSpell(SPELL_FLAME_SHOCK, GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_LAVA_BURST, TARGET_VICTIM, 5); + else + VerifyAndPushSpellCast(spells, SPELL_FLAME_SHOCK, TARGET_VICTIM, 3); + } + VerifyAndPushSpellCast(spells, SPELL_CHAIN_LIGHTNING, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_ELEMENTAL_MASTERY, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_THUNDERSTORM, TARGET_NONE, 3); + break; + case SPEC_SHAMAN_ENHANCEMENT: + if (Aura const* maelstrom = me->GetAura(AURA_MAELSTROM_WEAPON)) + if (maelstrom->GetStackAmount() == 5) + VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_STORMSTRIKE, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_EARTH_SHOCK, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_LAVA_LASH, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_SHAMANISTIC_RAGE, TARGET_NONE, 10); + break; + } + break; + case CLASS_MAGE: + if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_COUNTERSPELL, TARGET_VICTIM, 25); + VerifyAndPushSpellCast(spells, SPELL_DAMPEN_MAGIC, TARGET_CHARMER, 2); + VerifyAndPushSpellCast(spells, SPELL_EVOCATION, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_MANA_SHIELD, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_MIRROR_IMAGE, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_SPELLSTEAL, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_ICE_BLOCK, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_ICY_VEINS, TARGET_NONE, 3); + switch (GetSpec()) + { + case SPEC_MAGE_ARCANE: + if (Aura* abAura = me->GetAura(AURA_ARCANE_BLAST)) + if (abAura->GetStackAmount() >= 3) + VerifyAndPushSpellCast(spells, SPELL_ARCANE_MISSILES, TARGET_VICTIM, 7); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_BLAST, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_BARRAGE, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_POWER, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_PRESENCE_OF_MIND, TARGET_NONE, 7); + break; + case SPEC_MAGE_FIRE: + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_LIVING_BOMB)) + VerifyAndPushSpellCast(spells, SPELL_LIVING_BOMB, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_COMBUSTION, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_FIREBALL, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_FIRE_BLAST, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_DRAGONS_BREATH, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_BLAST_WAVE, TARGET_VICTIM, 1); + break; + case SPEC_MAGE_FROST: + VerifyAndPushSpellCast(spells, SPELL_DEEP_FREEZE, TARGET_VICTIM, 10); + VerifyAndPushSpellCast(spells, SPELL_FROST_NOVA, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_FROSTBOLT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_COLD_SNAP, TARGET_VICTIM, 5); + if (me->GetVictim() && me->GetVictim()->HasAuraState(AURA_STATE_FROZEN, nullptr, me)) + VerifyAndPushSpellCast(spells, SPELL_ICE_LANCE, TARGET_VICTIM, 5); + break; + } + break; + case CLASS_WARLOCK: + VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_W, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_FEAR, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_SEED_OF_CORRUPTION, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_HOWL_OF_TERROR, TARGET_NONE, 2); + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_CORRUPTION, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_CORRUPTION, TARGET_VICTIM, 10); + switch (GetSpec()) + { + case SPEC_WARLOCK_AFFLICTION: + if (Unit* victim = me->GetVictim()) + { + VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_UNSTABLE_AFFLICTION, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_UNSTABLE_AFFLICTION, TARGET_VICTIM, 8); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_HAUNT, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_HAUNT, TARGET_VICTIM, 8); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_CURSE_OF_AGONY, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_CURSE_OF_AGONY, TARGET_VICTIM, 4); + if (victim->HealthBelowPct(25)) + VerifyAndPushSpellCast(spells, SPELL_DRAIN_SOUL, TARGET_VICTIM, 100); + } + break; + case SPEC_WARLOCK_DEMONOLOGY: + VerifyAndPushSpellCast(spells, SPELL_METAMORPHOSIS, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7); + if (me->HasAura(AURA_DECIMATION)) + VerifyAndPushSpellCast(spells, SPELL_SOUL_FIRE, TARGET_VICTIM, 100); + if (me->HasAura(SPELL_METAMORPHOSIS)) + { + VerifyAndPushSpellCast(spells, SPELL_IMMOLATION_AURA, TARGET_NONE, 30); + if (!me->IsWithinMeleeRange(me->GetVictim())) + VerifyAndPushSpellCast(spells, SPELL_DEMON_CHARGE, TARGET_VICTIM, 20); + } + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 5); + if (me->HasAura(AURA_MOLTEN_CORE)) + VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 10); + break; + case SPEC_WARLOCK_DESTRUCTION: + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 8); + if (me->GetVictim() && me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_CONFLAGRATE, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_SHADOWFURY, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_CHAOS_BOLT, TARGET_VICTIM, 10); + VerifyAndPushSpellCast(spells, SPELL_SHADOWBURN, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 7); + break; + } + break; + case CLASS_DRUID: + VerifyAndPushSpellCast(spells, SPELL_INNERVATE, TARGET_CHARMER, 5); + VerifyAndPushSpellCast(spells, SPELL_BARKSKIN, TARGET_NONE, 5); + switch (GetSpec()) + { + case SPEC_DRUID_RESTORATION: + if (!me->HasAura(SPELL_TREE_OF_LIFE)) + { + CancelAllShapeshifts(); + VerifyAndPushSpellCast(spells, SPELL_TREE_OF_LIFE, TARGET_NONE, 100); + break; + } + VerifyAndPushSpellCast(spells, SPELL_TRANQUILITY, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_NATURE_SWIFTNESS, TARGET_NONE, 7); + if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID())) + { + VerifyAndPushSpellCast(spells, SPELL_NOURISH, creatureCharmer, 5); + VerifyAndPushSpellCast(spells, SPELL_WILD_GROWTH, creatureCharmer, 5); + if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_REJUVENATION, creatureCharmer, 8); + if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_REGROWTH, creatureCharmer, 8); + uint8 lifebloomStacks = 0; + if (Aura const* lifebloom = creatureCharmer->GetAura(SPELL_LIFEBLOOM, me->GetGUID())) + lifebloomStacks = lifebloom->GetStackAmount(); + if (lifebloomStacks < 3) + VerifyAndPushSpellCast(spells, SPELL_LIFEBLOOM, creatureCharmer, 5); + if (creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION) || + creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH)) + VerifyAndPushSpellCast(spells, SPELL_SWIFTMEND, creatureCharmer, 10); + if (me->HasAura(SPELL_NATURE_SWIFTNESS)) + VerifyAndPushSpellCast(spells, SPELL_HEALING_TOUCH, creatureCharmer, 100); + } + break; + case SPEC_DRUID_BALANCE: + { + if (!me->HasAura(SPELL_MOONKIN_FORM)) + { + CancelAllShapeshifts(); + VerifyAndPushSpellCast(spells, SPELL_MOONKIN_FORM, TARGET_NONE, 100); + break; + } + uint32 const mainAttackSpell = me->HasAura(AURA_ECLIPSE_LUNAR) ? SPELL_STARFIRE : SPELL_WRATH; + VerifyAndPushSpellCast(spells, SPELL_STARFALL, TARGET_NONE, 20); + VerifyAndPushSpellCast(spells, mainAttackSpell, TARGET_VICTIM, 10); + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_INSECT_SWARM, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_INSECT_SWARM, TARGET_VICTIM, 7); + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_MOONFIRE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_MOONFIRE, TARGET_VICTIM, 5); + if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_TYPHOON, TARGET_NONE, 15); + break; + } + case SPEC_DRUID_FERAL: + if (!me->HasAura(SPELL_CAT_FORM)) + { + CancelAllShapeshifts(); + VerifyAndPushSpellCast(spells, SPELL_CAT_FORM, TARGET_NONE, 100); + break; + } + VerifyAndPushSpellCast(spells, SPELL_BERSERK, TARGET_NONE, 20); + VerifyAndPushSpellCast(spells, SPELL_SURVIVAL_INSTINCTS, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_TIGER_FURY, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_DASH, TARGET_NONE, 5); + if (Unit* victim = me->GetVictim()) + { + uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0; + if (victim->HasUnitState(UNIT_STATE_CASTING) && cp >= 1) + VerifyAndPushSpellCast(spells, SPELL_MAIM, TARGET_VICTIM, 25); + if (!me->IsWithinMeleeRange(victim)) + VerifyAndPushSpellCast(spells, SPELL_FERAL_CHARGE_CAT, TARGET_VICTIM, 25); + if (cp >= 4) + VerifyAndPushSpellCast(spells, SPELL_RIP, TARGET_VICTIM, 50); + if (cp <= 4) + { + VerifyAndPushSpellCast(spells, SPELL_MANGLE_CAT, TARGET_VICTIM, 10); + VerifyAndPushSpellCast(spells, SPELL_CLAW, TARGET_VICTIM, 5); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_RAKE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_RAKE, TARGET_VICTIM, 8); + if (!me->HasAura(SPELL_SAVAGE_ROAR)) + VerifyAndPushSpellCast(spells, SPELL_SAVAGE_ROAR, TARGET_NONE, 15); + } + } + break; + } + break; + } + + return SelectSpellCast(spells); +} + +static const float CASTER_CHASE_DISTANCE = 28.0f; +void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff) { Creature* charmer = me->GetCharmer() ? me->GetCharmer()->ToCreature() : nullptr; if (!charmer) @@ -192,10 +1286,54 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/) return; if (IsRangedAttacker()) - AttackStartCaster(target, 28.0f); + { + _chaseCloser = !me->IsWithinLOSInMap(target); + if (_chaseCloser) + AttackStart(target); + else + AttackStartCaster(target, CASTER_CHASE_DISTANCE); + } else AttackStart(target); + _forceFacing = true; } + + if (me->IsStopped() && !me->HasUnitState(UNIT_STATE_CANNOT_TURN)) + { + float targetAngle = me->GetAngle(target); + if (_forceFacing || fabs(me->GetOrientation() - targetAngle) > 0.4f) + { + me->SetFacingTo(targetAngle); + _forceFacing = false; + } + } + + if (_castCheckTimer <= diff) + { + if (me->HasUnitState(UNIT_STATE_CASTING)) + _castCheckTimer = 0; + else + { + if (IsRangedAttacker()) + { // chase to zero if the target isn't in line of sight + bool inLOS = me->IsWithinLOSInMap(target); + if (_chaseCloser != !inLOS) + { + _chaseCloser = !inLOS; + if (_chaseCloser) + AttackStart(target); + else + AttackStartCaster(target, CASTER_CHASE_DISTANCE); + } + } + if (TargetedSpell shouldCast = SelectAppropriateCastForSpec()) + DoCastAtTarget(shouldCast); + _castCheckTimer = 500; + } + } + else + _castCheckTimer -= diff; + DoAutoAttackIfReady(); } else @@ -214,6 +1352,9 @@ void SimpleCharmedPlayerAI::OnCharmed(bool apply) { me->CastStop(); me->AttackStop(); + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(0, me->GetPosition(), false); // force re-sync of current position for all clients } else { diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h index b717816f9a3..18f65485161 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.h +++ b/src/server/game/AI/PlayerAI/PlayerAI.h @@ -20,41 +20,99 @@ #include "UnitAI.h" #include "Player.h" +#include "Spell.h" #include "Creature.h" class TC_GAME_API PlayerAI : public UnitAI { public: - explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { } + explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _selfSpec(PlayerAI::GetPlayerSpec(player)), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { } void OnCharmed(bool /*apply*/) override { } // charm AI application for players is handled by Unit::SetCharmedBy / Unit::RemoveCharmedBy // helper functions to determine player info + // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned. + static uint8 GetPlayerSpec(Player const* who); + // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned. + uint8 GetSpec(Player const* who = nullptr) const { return (!who || who == me) ? _selfSpec : GetPlayerSpec(who); } static bool IsPlayerHealer(Player const* who); bool IsHealer(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfHealer : IsPlayerHealer(who); } static bool IsPlayerRangedAttacker(Player const* who); bool IsRangedAttacker(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); } protected: + struct TargetedSpell : public std::pair<Spell*, Unit*> + { + TargetedSpell() : pair<Spell*, Unit*>() { } + TargetedSpell(Spell* first, Unit* second) : pair<Spell*, Unit*>(first, second) { } + explicit operator bool() { return !!first; } + }; + typedef std::pair<TargetedSpell, uint32> PossibleSpell; + typedef std::vector<PossibleSpell> PossibleSpellVector; + Player* const me; void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection + enum SpellTarget + { + TARGET_NONE, + TARGET_VICTIM, + TARGET_CHARMER, + TARGET_SELF + }; + /* Check if the specified spell can be cast on that target. + Caller is responsible for cleaning up created Spell object from pointer. */ + TargetedSpell VerifySpellCast(uint32 spellId, Unit* target); + /* Check if the specified spell can be cast on that target. + Caller is responsible for cleaning up created Spell object from pointer. */ + TargetedSpell VerifySpellCast(uint32 spellId, SpellTarget target); + + /* Helper method - checks spell cast, then pushes it onto provided vector if valid. */ + template<typename T> inline void VerifyAndPushSpellCast(PossibleSpellVector& spells, uint32 spellId, T target, uint32 weight) + { + if (TargetedSpell spell = VerifySpellCast(spellId, target)) + spells.push_back({ spell,weight }); + } + + /* Helper method - selects one spell from the vector and returns it, while deleting everything else. + This invalidates the vector, and empties it to prevent accidental misuse. */ + TargetedSpell SelectSpellCast(PossibleSpellVector& spells); + /* Helper method - casts the included spell at the included target */ + inline void DoCastAtTarget(TargetedSpell spell) + { + SpellCastTargets targets; + targets.SetUnitTarget(spell.second); + spell.first->prepare(&targets); + } + virtual Unit* SelectAttackTarget() const { return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr; } void DoRangedAttackIfReady(); void DoAutoAttackIfReady(); + // Cancels all shapeshifts that the player could voluntarily cancel + void CancelAllShapeshifts(); + private: - bool _isSelfHealer; + uint8 const _selfSpec; + bool const _isSelfHealer; bool _isSelfRangedAttacker; }; class SimpleCharmedPlayerAI : public PlayerAI { public: - SimpleCharmedPlayerAI(Player* player) : PlayerAI(player) { } + SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(500), _chaseCloser(false), _forceFacing(true) { } void UpdateAI(uint32 diff) override; void OnCharmed(bool apply) override; + + protected: Unit* SelectAttackTarget() const override; + + private: + TargetedSpell SelectAppropriateCastForSpec(); + uint32 _castCheckTimer; + bool _chaseCloser; + bool _forceFacing; }; #endif diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 9ce37fd0c32..316a9704cac 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -32,7 +32,7 @@ struct TSpellSummary uint8 Effects; // set of enum SelectEffect } extern* SpellSummary; -void SummonList::DoZoneInCombat(uint32 entry) +void SummonList::DoZoneInCombat(uint32 entry, float maxRangeToNearestTarget) { for (StorageType::iterator i = storage_.begin(); i != storage_.end();) { @@ -41,7 +41,7 @@ void SummonList::DoZoneInCombat(uint32 entry) if (summon && summon->IsAIEnabled && (!entry || summon->GetEntry() == entry)) { - summon->AI()->DoZoneInCombat(); + summon->AI()->DoZoneInCombat(nullptr, maxRangeToNearestTarget); } } } @@ -183,11 +183,11 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec { //No target so we can't cast if (!target) - return NULL; + return nullptr; //Silenced so we can't cast if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) - return NULL; + return nullptr; //Using the extended script system we first create a list of viable spells SpellInfo const* apSpell[MAX_CREATURE_SPELLS]; @@ -195,7 +195,7 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec uint32 spellCount = 0; - SpellInfo const* tempSpell = NULL; + SpellInfo const* tempSpell = nullptr; //Check if each spell is viable(set it to null if not) for (uint32 i = 0; i < MAX_CREATURE_SPELLS; i++) @@ -251,7 +251,7 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec //We got our usable spells so now lets randomly pick one if (!spellCount) - return NULL; + return nullptr; return apSpell[urand(0, spellCount - 1)]; } @@ -327,7 +327,7 @@ void ScriptedAI::DoTeleportAll(float x, float y, float z, float o) Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 minHPDiff) { - Unit* unit = NULL; + Unit* unit = nullptr; Trinity::MostHPMissingInRange u_check(me, range, minHPDiff); Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check); me->VisitNearbyObject(range, searcher); @@ -357,7 +357,7 @@ std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 u Player* ScriptedAI::GetPlayerAtMinimumRange(float minimumRange) { - Player* player = NULL; + Player* player = nullptr; CellCoord pair(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY())); Cell cell(pair); @@ -507,6 +507,14 @@ void BossAI::_EnterCombat() ScheduleTasks(); } +bool BossAI::CanRespawn() +{ + if (instance && instance->GetBossState(_bossId) == DONE) + return false; + + return true; +} + void BossAI::TeleportCheaters() { float x, y, z; @@ -547,7 +555,7 @@ void BossAI::UpdateAI(uint32 diff) DoMeleeAttackIfReady(); } -void BossAI::_DespawnAtEvade(uint32 delayToRespawn) +void BossAI::_DespawnAtEvade(uint32 delayToRespawn, Creature* who) { if (delayToRespawn < 2) { @@ -555,18 +563,28 @@ void BossAI::_DespawnAtEvade(uint32 delayToRespawn) delayToRespawn = 2; } - uint32 corpseDelay = me->GetCorpseDelay(); - uint32 respawnDelay = me->GetRespawnDelay(); + if (!who) + who = me; - me->SetCorpseDelay(1); - me->SetRespawnDelay(delayToRespawn - 1); + if (TempSummon* whoSummon = who->ToTempSummon()) + { + TC_LOG_WARN("scripts", "_DespawnAtEvade called on a temporary summon."); + whoSummon->UnSummon(); + return; + } - me->DespawnOrUnsummon(); + uint32 corpseDelay = who->GetCorpseDelay(); + uint32 respawnDelay = who->GetRespawnDelay(); - me->SetCorpseDelay(corpseDelay); - me->SetRespawnDelay(respawnDelay); + who->SetCorpseDelay(1); + who->SetRespawnDelay(delayToRespawn - 1); - if (instance) + who->DespawnOrUnsummon(); + + who->SetCorpseDelay(corpseDelay); + who->SetRespawnDelay(respawnDelay); + + if (instance && who == me) instance->SetBossState(_bossId, FAIL); } diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 42befa26a23..bbc7e8beadb 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -114,7 +114,7 @@ public: } } - void DoZoneInCombat(uint32 entry = 0); + void DoZoneInCombat(uint32 entry = 0, float maxRangeToNearestTarget = 250.0f); void RemoveNotExisting(); bool HasEntry(uint32 entry) const; @@ -361,13 +361,15 @@ class TC_GAME_API BossAI : public ScriptedAI void JustReachedHome() override { _JustReachedHome(); } bool CanAIAttack(Unit const* target) const override { return CheckBoundary(target); } + bool CanRespawn() override; protected: void _Reset(); void _EnterCombat(); void _JustDied(); void _JustReachedHome() { me->setActive(false); } - void _DespawnAtEvade(uint32 delayToRespawn = 30); + void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr); + void _DespawnAtEvade(Milliseconds const& time, Creature* who = nullptr) { _DespawnAtEvade(uint32(time.count()), who); } void TeleportCheaters(); diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index fa80634e0f0..1b8b472b805 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -72,7 +72,7 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* who) return false; //experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS)) + if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; //not a player diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index 778edf8cab5..bd07e688fb0 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -69,7 +69,7 @@ bool FollowerAI::AssistPlayerInCombat(Unit* who) return false; //experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS)) + if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; //not a player diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 60df4fe6d5a..e21f59fe582 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -468,7 +468,7 @@ bool SmartAI::AssistPlayerInCombat(Unit* who) return false; //experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS)) + if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; //not a player diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index a18f0a6574b..75b9752a193 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -929,9 +929,21 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u break; } - instance->SetData(e.action.setInstanceData.field, e.action.setInstanceData.data); - TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: Field: %u, data: %u", - e.action.setInstanceData.field, e.action.setInstanceData.data); + switch (e.action.setInstanceData.type) + { + case 0: + instance->SetData(e.action.setInstanceData.field, e.action.setInstanceData.data); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetData Field: %u, data: %u", + e.action.setInstanceData.field, e.action.setInstanceData.data); + break; + case 1: + instance->SetBossState(e.action.setInstanceData.field, static_cast<EncounterState>(e.action.setInstanceData.data)); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction: SMART_ACTION_SET_INST_DATA: SetBossState BossId: %u, State: %u (%s)", + e.action.setInstanceData.field, e.action.setInstanceData.data, InstanceScript::GetBossStateName(e.action.setInstanceData.data).c_str()); + break; + default: // Static analysis + break; + } break; } case SMART_ACTION_SET_INST_DATA64: @@ -1037,10 +1049,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u target->DespawnOrUnsummon(e.action.forceDespawn.delay); } else if (GameObject* goTarget = (*itr)->ToGameObject()) - { - if (IsSmartGO(goTarget)) - goTarget->SetRespawnTime(e.action.forceDespawn.delay + 1); - } + goTarget->SetRespawnTime(e.action.forceDespawn.delay + 1); } delete targets; @@ -1126,22 +1135,29 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u delete targets; break; } - case SMART_ACTION_MOVE_FORWARD: + case SMART_ACTION_MOVE_OFFSET: { - if (!me) - break; + if (ObjectList* targets = GetTargets(e, unit)) + { + for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) + { + if (!IsCreature(*itr)) + continue; - float x, y, z; - me->GetClosePoint(x, y, z, me->GetObjectSize() / 3, (float)e.action.moveRandom.distance); - me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z); - break; - } - case SMART_ACTION_RISE_UP: - { - if (!me) - break; + Position pos = (*itr)->GetPosition(); + + // Use forward/backward/left/right cartesian plane movement + float x, y, z, o; + o = pos.GetOrientation(); + x = pos.GetPositionX() + (std::cos(o - (M_PI / 2))*e.target.x) + (std::cos(o)*e.target.y); + y = pos.GetPositionY() + (std::sin(o - (M_PI / 2))*e.target.x) + (std::sin(o)*e.target.y); + z = pos.GetPositionZ() + e.target.z; + (*itr)->ToCreature()->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, x, y, z); + } + + delete targets; + } - me->GetMotionMaster()->MovePoint(SMART_RANDOM_POINT, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + (float)e.action.moveRandom.distance); break; } case SMART_ACTION_SET_VISIBILITY: @@ -1215,18 +1231,13 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u ObjectList* targets = GetTargets(e, unit); if (targets) { - float x, y, z, o; for (ObjectList::const_iterator itr = targets->begin(); itr != targets->end(); ++itr) { if (!IsUnit(*itr)) continue; - (*itr)->GetPosition(x, y, z, o); - x += e.target.x; - y += e.target.y; - z += e.target.z; - o += e.target.o; - GetBaseObject()->SummonGameObject(e.action.summonGO.entry, x, y, z, o, 0, 0, 0, 0, e.action.summonGO.despawnTime); + Position pos = (*itr)->GetPositionWithOffset(Position(e.target.x, e.target.y, e.target.z, e.target.o)); + GetBaseObject()->SummonGameObject(e.action.summonGO.entry, pos, G3D::Quat(), e.action.summonGO.despawnTime); } delete targets; @@ -1235,7 +1246,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (e.GetTargetType() != SMART_TARGET_POSITION) break; - GetBaseObject()->SummonGameObject(e.action.summonGO.entry, e.target.x, e.target.y, e.target.z, e.target.o, 0, 0, 0, 0, e.action.summonGO.despawnTime); + GetBaseObject()->SummonGameObject(e.action.summonGO.entry, Position(e.target.x, e.target.y, e.target.z, e.target.o), G3D::Quat(), e.action.summonGO.despawnTime); break; } case SMART_ACTION_KILL_UNIT: @@ -1483,10 +1494,10 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (TransportBase* trans = me->GetDirectTransport()) trans->CalculatePassengerPosition(dest.x, dest.y, dest.z); - me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, dest.x, dest.y, dest.z); + me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, dest.x, dest.y, dest.z, e.action.MoveToPos.disablePathfinding == 0); } else - me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()); + me->GetMotionMaster()->MovePoint(e.action.MoveToPos.pointId, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), e.action.MoveToPos.disablePathfinding == 0); break; } case SMART_ACTION_RESPAWN_TARGET: @@ -2409,7 +2420,7 @@ void SmartScript::InstallTemplate(SmartScriptHolder const& e) AddEvent(SMART_EVENT_DATA_SET, 0, 0, 0, 0, 0, SMART_ACTION_SET_RUN, e.action.installTtemplate.param3, 0, 0, 0, 0, 0, SMART_TARGET_NONE, 0, 0, 0, 0); AddEvent(SMART_EVENT_DATA_SET, 0, 0, 0, 0, 0, SMART_ACTION_SET_EVENT_PHASE, 1, 0, 0, 0, 0, 0, SMART_TARGET_NONE, 0, 0, 0, 0); - AddEvent(SMART_EVENT_UPDATE, SMART_EVENT_FLAG_NOT_REPEATABLE, 1000, 1000, 0, 0, SMART_ACTION_MOVE_FORWARD, e.action.installTtemplate.param4, 0, 0, 0, 0, 0, SMART_TARGET_NONE, 0, 0, 0, 1); + AddEvent(SMART_EVENT_UPDATE, SMART_EVENT_FLAG_NOT_REPEATABLE, 1000, 1000, 0, 0, SMART_ACTION_MOVE_OFFSET, 0, 0, 0, 0, 0, 0, SMART_TARGET_SELF, 0, e.action.installTtemplate.param4, 0, 1); //phase 1: give quest credit on movepoint reached AddEvent(SMART_EVENT_MOVEMENTINFORM, 0, POINT_MOTION_TYPE, SMART_RANDOM_POINT, 0, 0, SMART_ACTION_SET_DATA, 0, 0, 0, 0, 0, 0, SMART_TARGET_STORED, 1, 0, 0, 1); //phase 1: despawn after time on movepoint reached @@ -3359,6 +3370,16 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff) } } + // Delay flee for assist event if stunned or rooted + if (e.GetActionType() == SMART_ACTION_FLEE_FOR_ASSIST) + { + if (me && me->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) + { + e.timer = 1; + return; + } + } + e.active = true;//activate events with cooldown switch (e.GetEventType())//process ONLY timed events { diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index 86aaf45af88..f0709eb9216 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -19,6 +19,7 @@ #include "ObjectMgr.h" #include "GridDefines.h" #include "GridNotifiers.h" +#include "InstanceScript.h" #include "SpellMgr.h" #include "Cell.h" #include "GameEventMgr.h" @@ -1156,6 +1157,23 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) } break; } + case SMART_ACTION_SET_INST_DATA: + { + if (e.action.setInstanceData.type > 1) + { + TC_LOG_ERROR("sql.sql", "Entry %u SourceType %u Event %u Action %u uses invalid data type %u (value range 0-1), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setInstanceData.type); + return false; + } + else if (e.action.setInstanceData.type == 1) + { + if (e.action.setInstanceData.data > TO_BE_DECIDED) + { + TC_LOG_ERROR("sql.sql", "Entry %u SourceType %u Event %u Action %u uses invalid boss state %u (value range 0-5), skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.action.setInstanceData.data); + return false; + } + } + break; + } case SMART_ACTION_START_CLOSEST_WAYPOINT: case SMART_ACTION_FOLLOW: case SMART_ACTION_SET_ORIENTATION: @@ -1173,13 +1191,11 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_ACTION_ATTACK_START: case SMART_ACTION_THREAT_ALL_PCT: case SMART_ACTION_THREAT_SINGLE_PCT: - case SMART_ACTION_SET_INST_DATA: case SMART_ACTION_SET_INST_DATA64: case SMART_ACTION_AUTO_ATTACK: case SMART_ACTION_ALLOW_COMBAT_MOVEMENT: case SMART_ACTION_CALL_FOR_HELP: case SMART_ACTION_SET_DATA: - case SMART_ACTION_MOVE_FORWARD: case SMART_ACTION_SET_VISIBILITY: case SMART_ACTION_WP_PAUSE: case SMART_ACTION_SET_FLY: @@ -1225,7 +1241,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) case SMART_ACTION_ADD_GO_FLAG: case SMART_ACTION_REMOVE_GO_FLAG: case SMART_ACTION_SUMMON_CREATURE_GROUP: - case SMART_ACTION_RISE_UP: + case SMART_ACTION_MOVE_OFFSET: case SMART_ACTION_SET_CORPSE_DELAY: break; default: diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 3105f087bf7..8b55c3e138c 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -469,7 +469,7 @@ enum SMART_ACTION SMART_ACTION_RANDOM_PHASE_RANGE = 31, // PhaseMin, PhaseMax SMART_ACTION_RESET_GOBJECT = 32, // SMART_ACTION_CALL_KILLEDMONSTER = 33, // CreatureId, - SMART_ACTION_SET_INST_DATA = 34, // Field, Data + SMART_ACTION_SET_INST_DATA = 34, // Field, Data, Type (0 = SetData, 1 = SetBossState) SMART_ACTION_SET_INST_DATA64 = 35, // Field, SMART_ACTION_UPDATE_TEMPLATE = 36, // Entry SMART_ACTION_DIE = 37, // No Params @@ -481,7 +481,6 @@ enum SMART_ACTION SMART_ACTION_MOUNT_TO_ENTRY_OR_MODEL = 43, // Creature_template entry(param1) OR ModelId (param2) (or 0 for both to dismount) SMART_ACTION_SET_INGAME_PHASE_MASK = 44, // mask SMART_ACTION_SET_DATA = 45, // Field, Data (only creature @todo) - SMART_ACTION_MOVE_FORWARD = 46, // distance SMART_ACTION_SET_VISIBILITY = 47, // on/off SMART_ACTION_SET_ACTIVE = 48, // on/off SMART_ACTION_ATTACK_START = 49, // @@ -504,7 +503,7 @@ enum SMART_ACTION SMART_ACTION_SET_ORIENTATION = 66, // SMART_ACTION_CREATE_TIMED_EVENT = 67, // id, InitialMin, InitialMax, RepeatMin(only if it repeats), RepeatMax(only if it repeats), chance SMART_ACTION_PLAYMOVIE = 68, // entry - SMART_ACTION_MOVE_TO_POS = 69, // PointId, xyz + SMART_ACTION_MOVE_TO_POS = 69, // PointId, transport, disablePathfinding SMART_ACTION_RESPAWN_TARGET = 70, // SMART_ACTION_EQUIP = 71, // entry, slotmask slot1, slot2, slot3 , only slots with mask set will be sent to client, bits are 1, 2, 4, leaving mask 0 is defaulted to mask 7 (send all), slots1-3 are only used if no entry is set SMART_ACTION_CLOSE_GOSSIP = 72, // none @@ -549,7 +548,7 @@ enum SMART_ACTION SMART_ACTION_GAME_EVENT_STOP = 111, // GameEventId SMART_ACTION_GAME_EVENT_START = 112, // GameEventId SMART_ACTION_START_CLOSEST_WAYPOINT = 113, // wp1, wp2, wp3, wp4, wp5, wp6, wp7 - SMART_ACTION_RISE_UP = 114, // distance + SMART_ACTION_MOVE_OFFSET = 114, SMART_ACTION_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf SMART_ACTION_SET_CORPSE_DELAY = 116, // timer @@ -716,6 +715,7 @@ struct SmartAction { uint32 field; uint32 data; + uint32 type; } setInstanceData; struct @@ -958,6 +958,7 @@ struct SmartAction { uint32 pointId; uint32 transport; + uint32 disablePathfinding; } MoveToPos; struct diff --git a/src/server/game/Accounts/AccountMgr.cpp b/src/server/game/Accounts/AccountMgr.cpp index 1b93d072fb6..fa3e8818fe0 100644 --- a/src/server/game/Accounts/AccountMgr.cpp +++ b/src/server/game/Accounts/AccountMgr.cpp @@ -487,19 +487,20 @@ void AccountMgr::UpdateAccountAccess(rbac::RBACData* rbac, uint32 accountId, uin if (rbac && securityLevel == rbac->GetSecurityLevel()) rbac->SetSecurityLevel(securityLevel); + SQLTransaction trans = LoginDatabase.BeginTransaction(); // Delete old security level from DB if (realmId == -1) { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS); stmt->setUInt32(0, accountId); - LoginDatabase.Execute(stmt); + trans->Append(stmt); } else { PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_ACCOUNT_ACCESS_BY_REALM); stmt->setUInt32(0, accountId); stmt->setUInt32(1, realmId); - LoginDatabase.Execute(stmt); + trans->Append(stmt); } // Add new security level @@ -509,8 +510,10 @@ void AccountMgr::UpdateAccountAccess(rbac::RBACData* rbac, uint32 accountId, uin stmt->setUInt32(0, accountId); stmt->setUInt8(1, securityLevel); stmt->setInt32(2, realmId); - LoginDatabase.Execute(stmt); + trans->Append(stmt); } + + LoginDatabase.CommitTransaction(trans); } rbac::RBACPermission const* AccountMgr::GetRBACPermission(uint32 permissionId) const diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 9dd56ec8180..741983431dd 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -59,9 +59,9 @@ enum RBACPermissions // 7 - reuse // 8 - reuse // 9 - reuse - // 10 - reuse + // 10 - 7.x only RBAC_PERM_LOG_GM_TRADE = 11, - // 12 - reuse + // 12 - 7.x only RBAC_PERM_SKIP_CHECK_INSTANCE_REQUIRED_BOSSES = 13, RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_TEAMMASK = 14, RBAC_PERM_SKIP_CHECK_CHARACTER_CREATION_CLASSMASK = 15, @@ -540,7 +540,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE = 642, RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS = 643, RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE = 644, - RBAC_PERM_COMMAND_RELOAD_GAME_GRAVEYARD_ZONE = 645, + RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE = 645, RBAC_PERM_COMMAND_RELOAD_GAME_TELE = 646, RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER = 647, RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE = 648, @@ -701,6 +701,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_PET_LEVEL = 838, RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE = 839, RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840, + RBAC_PERM_COMMAND_NEARGRAVEYARD = 841, // custom permissions 1000+ RBAC_PERM_MAX diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp index 7f05cd2efd7..6b1dcb85bec 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotBuyer.cpp @@ -17,7 +17,7 @@ #include "Log.h" #include "Item.h" -#include "ItemPrototype.h" +#include "ItemTemplate.h" #include "AuctionHouseBotBuyer.h" AuctionBotBuyer::AuctionBotBuyer() : _checkInterval(20 * MINUTE) diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h index 563c2cda04a..3e6f263707f 100644 --- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h +++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.h @@ -19,7 +19,7 @@ #define AUCTION_HOUSE_BOT_SELLER_H #include "Define.h" -#include "ItemPrototype.h" +#include "ItemTemplate.h" #include "AuctionHouseBot.h" struct ItemToSell diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index e69d0f3085c..f44099f6037 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -797,18 +797,21 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl GameObject* Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z, float o) { // Get map object - Map* map = sMapMgr->CreateBaseMap(571); // *vomits* + Map* map = sMapMgr->CreateBaseMap(m_MapId); if (!map) return 0; + // Calculate rotation + G3D::Quat rot = G3D::Matrix3::fromEulerAnglesZYX(o, 0.f, 0.f); + // 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(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, PHASEMASK_NORMAL, Position(x, y, z, o), rot, 255, GO_STATE_READY)) { TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u could not be found in the database! Battlefield has not been created!", entry); TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Could not create gameobject template %u! Battlefield has not been created!", entry); delete go; - return NULL; + return nullptr; } // Add to world diff --git a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp index 5dd0b6f29c3..37e26d7e448 100644 --- a/src/server/game/Battlegrounds/ArenaTeamMgr.cpp +++ b/src/server/game/Battlegrounds/ArenaTeamMgr.cpp @@ -99,7 +99,7 @@ void ArenaTeamMgr::LoadArenaTeams() uint32 oldMSTime = getMSTime(); // Clean out the trash before loading anything - CharacterDatabase.Execute("DELETE FROM arena_team_member WHERE arenaTeamId NOT IN (SELECT arenaTeamId FROM arena_team)"); // One-time query + CharacterDatabase.DirectExecute("DELETE FROM arena_team_member WHERE arenaTeamId NOT IN (SELECT arenaTeamId FROM arena_team)"); // One-time query // 0 1 2 3 4 5 6 7 8 QueryResult result = CharacterDatabase.Query("SELECT arenaTeamId, name, captainGuid, type, backgroundColor, emblemStyle, emblemColor, borderStyle, borderColor, " diff --git a/src/server/game/Battlegrounds/Battleground.cpp b/src/server/game/Battlegrounds/Battleground.cpp index 100e2e6c7cc..2e66c587e15 100644 --- a/src/server/game/Battlegrounds/Battleground.cpp +++ b/src/server/game/Battlegrounds/Battleground.cpp @@ -283,8 +283,12 @@ inline void Battleground::_CheckSafePositions(uint32 diff) m_ValidStartPositionTimer = 0; for (BattlegroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr) + { if (Player* player = ObjectAccessor::FindPlayer(itr->first)) { + if (player->IsGameMaster()) + continue; + Position pos = player->GetPosition(); Position const* startPos = GetTeamStartPosition(Battleground::GetTeamIndexByTeamId(player->GetBGTeam())); if (pos.GetExactDistSq(startPos) > maxDist) @@ -293,6 +297,7 @@ inline void Battleground::_CheckSafePositions(uint32 diff) player->TeleportTo(GetMapId(), startPos->GetPositionX(), startPos->GetPositionY(), startPos->GetPositionZ(), startPos->GetOrientation()); } } + } } } @@ -1345,12 +1350,22 @@ bool Battleground::AddObject(uint32 type, uint32 entry, float x, float y, float Map* map = FindBgMap(); if (!map) return false; + + G3D::Quat rot(rotation0, rotation1, rotation2, rotation3); + // Temporally add safety check for bad spawns and send log (object rotations need to be rechecked in sniff) + if (!rotation0 && !rotation1 && !rotation2 && !rotation3) + { + TC_LOG_DEBUG("bg.battleground", "Battleground::AddObject: gameoobject [entry: %u, object type: %u] for BG (map: %u) has zeroed rotation fields, " + "orientation used temporally, but please fix the spawn", entry, type, m_MapId); + + rot = G3D::Matrix3::fromEulerAnglesZYX(o, 0.f, 0.f); + } + // Must be created this way, adding to godatamap would add it to the base map of the instance // 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(GetBgMap()->GenerateLowGuid<HighGuid::GameObject>(), entry, GetBgMap(), PHASEMASK_NORMAL, Position(x, y, z, o), rot, 255, 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/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index 52e0deaf86f..8c144cbc8be 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -142,8 +142,8 @@ void BattlegroundIC::PostUpdateImpl(uint32 diff) { if (siege->IsAlive()) { - if (siege->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_UNK_14|UNIT_FLAG_IMMUNE_TO_PC)) - // following sniffs the vehicle always has UNIT_FLAG_UNK_14 + if (siege->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_CANNOT_SWIM|UNIT_FLAG_IMMUNE_TO_PC)) + // following sniffs the vehicle always has UNIT_FLAG_CANNOT_SWIM siege->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_IMMUNE_TO_PC); else siege->SetHealth(siege->GetMaxHealth()); @@ -762,7 +762,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* node, bool recapture) if (Creature* siegeEngine = GetBGCreature(siegeType)) { - siegeEngine->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_UNK_14|UNIT_FLAG_IMMUNE_TO_PC); + siegeEngine->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_CANNOT_SWIM|UNIT_FLAG_IMMUNE_TO_PC); siegeEngine->setFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index 1942ac9d648..450f5cd9144 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -151,8 +151,8 @@ bool BattlegroundSA::ResetObjs() } // MAD props for Kiper for discovering those values - 4 hours of his work. - GetBGObject(BG_SA_BOAT_ONE)->UpdateRotationFields(1.0f, 0.0002f); - GetBGObject(BG_SA_BOAT_TWO)->UpdateRotationFields(1.0f, 0.00001f); + GetBGObject(BG_SA_BOAT_ONE)->SetParentRotation(G3D::Quat(0.f, 0.f, 1.0f, 0.0002f)); + GetBGObject(BG_SA_BOAT_TWO)->SetParentRotation(G3D::Quat(0.f, 0.f, 1.0f, 0.00001f)); SpawnBGObject(BG_SA_BOAT_ONE, RESPAWN_IMMEDIATELY); SpawnBGObject(BG_SA_BOAT_TWO, RESPAWN_IMMEDIATELY); diff --git a/src/server/game/Chat/Channels/Channel.cpp b/src/server/game/Chat/Channels/Channel.cpp index 0875ceefff5..a34f0f4d9a2 100644 --- a/src/server/game/Chat/Channels/Channel.cpp +++ b/src/server/game/Chat/Channels/Channel.cpp @@ -25,66 +25,66 @@ #include "AccountMgr.h" #include "Player.h" -Channel::Channel(std::string const& name, uint32 channelId, uint32 team): - _announce(true), - _ownership(true), - _IsSaved(false), - _flags(0), +Channel::Channel(std::string const& name, uint32 channelId, uint32 team /*= 0*/): + _announceEnabled(true), + _ownershipEnabled(true), + _persistentChannel(false), + _channelFlags(0), _channelId(channelId), - _Team(team), - _ownerGUID(), - _name(name), - _password("") + _channelTeam(team), + _ownerGuid(), + _channelName(name), + _channelPassword() { // set special flags if built-in channel if (ChatChannelsEntry const* ch = sChatChannelsStore.LookupEntry(channelId)) // check whether it's a built-in channel { - _announce = false; // no join/leave announces - _ownership = false; // no ownership handout + _announceEnabled = false; // no join/leave announces + _ownershipEnabled = false; // no ownership handout - _flags |= CHANNEL_FLAG_GENERAL; // for all built-in channels + _channelFlags |= CHANNEL_FLAG_GENERAL; // for all built-in channels if (ch->flags & CHANNEL_DBC_FLAG_TRADE) // for trade channel - _flags |= CHANNEL_FLAG_TRADE; + _channelFlags |= CHANNEL_FLAG_TRADE; if (ch->flags & CHANNEL_DBC_FLAG_CITY_ONLY2) // for city only channels - _flags |= CHANNEL_FLAG_CITY; + _channelFlags |= CHANNEL_FLAG_CITY; if (ch->flags & CHANNEL_DBC_FLAG_LFG) // for LFG channel - _flags |= CHANNEL_FLAG_LFG; + _channelFlags |= CHANNEL_FLAG_LFG; else // for all other channels - _flags |= CHANNEL_FLAG_NOT_LFG; + _channelFlags |= CHANNEL_FLAG_NOT_LFG; } else // it's custom channel { - _flags |= CHANNEL_FLAG_CUSTOM; + _channelFlags |= CHANNEL_FLAG_CUSTOM; // If storing custom channels in the db is enabled either load or save the channel if (sWorld->getBoolConfig(CONFIG_PRESERVE_CUSTOM_CHANNELS)) { PreparedStatement *stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHANNEL); stmt->setString(0, name); - stmt->setUInt32(1, _Team); + stmt->setUInt32(1, _channelTeam); PreparedQueryResult result = CharacterDatabase.Query(stmt); if (result) //load { Field* fields = result->Fetch(); - _announce = fields[0].GetBool(); - _ownership = fields[1].GetBool(); - _password = fields[2].GetString(); - const char* db_BannedList = fields[3].GetCString(); + _announceEnabled = fields[0].GetBool(); + _ownershipEnabled = fields[1].GetBool(); + _channelPassword = fields[2].GetString(); + char const* db_BannedList = fields[3].GetCString(); if (db_BannedList) { Tokenizer tokens(db_BannedList, ' '); for (Tokenizer::const_iterator i = tokens.begin(); i != tokens.end(); ++i) { - ObjectGuid banned_guid(uint64(strtoull(*i, NULL, 10))); + ObjectGuid banned_guid(uint64(atoull(*i))); if (banned_guid) { TC_LOG_DEBUG("chat.system", "Channel(%s) loaded bannedStore %s", name.c_str(), banned_guid.ToString().c_str()); - bannedStore.insert(banned_guid); + _bannedStore.insert(banned_guid); } } } @@ -93,45 +93,44 @@ Channel::Channel(std::string const& name, uint32 channelId, uint32 team): { stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHANNEL); stmt->setString(0, name); - stmt->setUInt32(1, _Team); + stmt->setUInt32(1, _channelTeam); CharacterDatabase.Execute(stmt); TC_LOG_DEBUG("chat.system", "Channel(%s) saved in database", name.c_str()); } - _IsSaved = true; + _persistentChannel = true; } } } void Channel::UpdateChannelInDB() const { - if (_IsSaved) + if (_persistentChannel) { std::ostringstream banlist; - BannedContainer::const_iterator iter; - for (iter = bannedStore.begin(); iter != bannedStore.end(); ++iter) + for (BannedContainer::const_iterator iter = _bannedStore.begin(); iter != _bannedStore.end(); ++iter) banlist << iter->GetRawValue() << ' '; std::string banListStr = banlist.str(); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL); - stmt->setBool(0, _announce); - stmt->setBool(1, _ownership); - stmt->setString(2, _password); + stmt->setBool(0, _announceEnabled); + stmt->setBool(1, _ownershipEnabled); + stmt->setString(2, _channelPassword); stmt->setString(3, banListStr); - stmt->setString(4, _name); - stmt->setUInt32(5, _Team); + stmt->setString(4, _channelName); + stmt->setUInt32(5, _channelTeam); CharacterDatabase.Execute(stmt); - TC_LOG_DEBUG("chat.system", "Channel(%s) updated in database", _name.c_str()); + TC_LOG_DEBUG("chat.system", "Channel(%s) updated in database", _channelName.c_str()); } } void Channel::UpdateChannelUseageInDB() const { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHANNEL_USAGE); - stmt->setString(0, _name); - stmt->setUInt32(1, _Team); + stmt->setString(0, _channelName); + stmt->setUInt32(1, _channelTeam); CharacterDatabase.Execute(stmt); } @@ -170,7 +169,7 @@ void Channel::JoinChannel(Player* player, std::string const& pass) return; } - if (!_password.empty() && pass != _password) + if (!_channelPassword.empty() && pass != _channelPassword) { WorldPacket data; MakeWrongPassword(&data); @@ -191,17 +190,15 @@ void Channel::JoinChannel(Player* player, std::string const& pass) player->JoinedChannel(this); - if (_announce && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) + if (_announceEnabled && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) { WorldPacket data; MakeJoined(&data, guid); SendToAll(&data); } - PlayerInfo pinfo; - pinfo.player = guid; + PlayerInfo& pinfo = _playersStore[guid]; pinfo.flags = MEMBER_FLAG_NONE; - playersStore[guid] = pinfo; WorldPacket data; MakeYouJoined(&data); @@ -213,14 +210,13 @@ void Channel::JoinChannel(Player* player, std::string const& pass) if (!IsConstant()) { // Update last_used timestamp in db - if (!playersStore.empty()) - UpdateChannelUseageInDB(); + UpdateChannelUseageInDB(); // If the channel has no owner yet and ownership is allowed, set the new owner. - if (!_ownerGUID && _ownership) + if (!_ownerGuid && _ownershipEnabled) { - SetOwner(guid, playersStore.size() > 1); - playersStore[guid].SetModerator(true); + SetOwner(guid, _playersStore.size() > 1); + pinfo.SetModerator(true); } } } @@ -248,11 +244,11 @@ void Channel::LeaveChannel(Player* player, bool send) data.clear(); } - bool changeowner = playersStore[guid].IsOwner(); - - playersStore.erase(guid); + PlayerInfo& info = _playersStore.at(guid); + bool changeowner = info.IsOwner(); + _playersStore.erase(guid); - if (_announce && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) + if (_announceEnabled && !player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) { WorldPacket data; MakeLeft(&data, guid); @@ -266,11 +262,12 @@ void Channel::LeaveChannel(Player* player, bool send) // Update last_used timestamp in db UpdateChannelUseageInDB(); - // If the channel owner left and there are still playersStore inside, pick a new owner - if (changeowner && _ownership && !playersStore.empty()) + // If the channel owner left and there are still players inside, pick a new owner + if (changeowner && _ownershipEnabled && !_playersStore.empty()) { - ObjectGuid newowner = playersStore.begin()->second.player; - playersStore[newowner].SetModerator(true); + auto itr = _playersStore.begin(); + ObjectGuid newowner = itr->first; + itr->second.SetModerator(true); SetOwner(newowner); } } @@ -288,7 +285,8 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b return; } - if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) + PlayerInfo& info = _playersStore.at(good); + if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -306,9 +304,9 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b return; } - bool changeowner = _ownerGUID == victim; + bool changeowner = _ownerGuid == victim; - if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && changeowner && good != _ownerGUID) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && changeowner && good != _ownerGuid) { WorldPacket data; MakeNotOwner(&data); @@ -318,7 +316,7 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b if (ban && !IsBanned(victim)) { - bannedStore.insert(victim); + _bannedStore.insert(victim); UpdateChannelInDB(); if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_SILENTLY_JOIN_CHANNEL)) @@ -335,13 +333,13 @@ void Channel::KickOrBan(Player const* player, std::string const& badname, bool b SendToAll(&data); } - playersStore.erase(victim); + _playersStore.erase(victim); bad->LeftChannel(this); - if (changeowner && _ownership && !playersStore.empty()) + if (changeowner && _ownershipEnabled && !_playersStore.empty()) { ObjectGuid newowner = good; - playersStore[newowner].SetModerator(true); + info.SetModerator(true); SetOwner(newowner); } } @@ -358,7 +356,8 @@ void Channel::UnBan(Player const* player, std::string const& badname) return; } - if (!playersStore[good].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) + PlayerInfo& info = _playersStore.at(good); + if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -377,7 +376,7 @@ void Channel::UnBan(Player const* player, std::string const& badname) return; } - bannedStore.erase(victim); + _bannedStore.erase(victim); WorldPacket data; MakePlayerUnbanned(&data, victim, good); @@ -399,7 +398,8 @@ void Channel::Password(Player const* player, std::string const& pass) return; } - if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) + PlayerInfo& info = _playersStore.at(guid); + if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -407,7 +407,7 @@ void Channel::Password(Player const* player, std::string const& pass) return; } - _password = pass; + _channelPassword = pass; WorldPacket data; MakePasswordChanged(&data, guid); @@ -428,7 +428,8 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo return; } - if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) + PlayerInfo& info = _playersStore.at(guid); + if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -436,7 +437,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo return; } - if (guid == _ownerGUID && std::string(p2n) == player->GetName() && mod) + if (guid == _ownerGuid && std::string(p2n) == player->GetName() && mod) return; Player* newp = ObjectAccessor::FindConnectedPlayerByName(p2n); @@ -453,7 +454,7 @@ void Channel::SetMode(Player const* player, std::string const& p2n, bool mod, bo return; } - if (_ownerGUID == victim && _ownerGUID != guid) + if (_ownerGuid == victim && _ownerGuid != guid) { WorldPacket data; MakeNotOwner(&data); @@ -479,7 +480,7 @@ void Channel::SetOwner(Player const* player, std::string const& newname) return; } - if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && guid != _ownerGUID) + if (!player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR) && guid != _ownerGuid) { WorldPacket data; MakeNotOwner(&data); @@ -501,7 +502,8 @@ void Channel::SetOwner(Player const* player, std::string const& newname) return; } - playersStore[victim].SetModerator(true); + PlayerInfo& info = _playersStore.at(victim); + info.SetModerator(true); SetOwner(victim); } @@ -515,7 +517,7 @@ void Channel::SendWhoOwner(ObjectGuid guid) SendToOne(&data, guid); } -void Channel::List(Player const* player) +void Channel::List(Player const* player) const { ObjectGuid guid = player->GetGUID(); @@ -530,7 +532,7 @@ void Channel::List(Player const* player) TC_LOG_DEBUG("chat.system", "SMSG_CHANNEL_LIST %s Channel: %s", player->GetSession()->GetPlayerInfo().c_str(), GetName().c_str()); - WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+playersStore.size()*(8+1)); + WorldPacket data(SMSG_CHANNEL_LIST, 1+(GetName().size()+1)+1+4+_playersStore.size()*(8+1)); data << uint8(1); // channel type? data << GetName(); // channel name data << uint8(GetFlags()); // channel flags? @@ -541,7 +543,7 @@ void Channel::List(Player const* player) uint32 gmLevelInWhoList = sWorld->getIntConfig(CONFIG_GM_LEVEL_IN_WHO_LIST); uint32 count = 0; - for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i) + for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i) { Player* member = ObjectAccessor::FindConnectedPlayer(i->first); @@ -575,7 +577,8 @@ void Channel::Announce(Player const* player) return; } - if (!playersStore[guid].IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) + PlayerInfo& info = _playersStore.at(guid); + if (!info.IsModerator() && !player->GetSession()->HasPermission(rbac::RBAC_PERM_CHANGE_CHANNEL_NOT_MODERATOR)) { WorldPacket data; MakeNotModerator(&data); @@ -583,10 +586,10 @@ void Channel::Announce(Player const* player) return; } - _announce = !_announce; + _announceEnabled = !_announceEnabled; WorldPacket data; - if (_announce) + if (_announceEnabled) MakeAnnouncementsOn(&data, guid); else MakeAnnouncementsOff(&data, guid); @@ -595,7 +598,7 @@ void Channel::Announce(Player const* player) UpdateChannelInDB(); } -void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang) +void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang) const { if (what.empty()) return; @@ -612,7 +615,8 @@ void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang) return; } - if (playersStore[guid].IsMuted()) + PlayerInfo const& info = _playersStore.at(guid); + if (info.IsMuted()) { WorldPacket data; MakeMuted(&data); @@ -622,11 +626,11 @@ void Channel::Say(ObjectGuid guid, std::string const& what, uint32 lang) WorldPacket data; if (Player* player = ObjectAccessor::FindConnectedPlayer(guid)) - ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), player, player, what, 0, _name); + ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), player, player, what, 0, _channelName); else - ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), guid, guid, what, 0, "", "", 0, false, _name); + ChatHandler::BuildChatPacket(data, CHAT_MSG_CHANNEL, Language(lang), guid, guid, what, 0, "", "", 0, false, _channelName); - SendToAll(&data, !playersStore[guid].IsModerator() ? guid : ObjectGuid::Empty); + SendToAll(&data, !info.IsModerator() ? guid : ObjectGuid::Empty); } void Channel::Invite(Player const* player, std::string const& newname) @@ -691,28 +695,31 @@ void Channel::Invite(Player const* player, std::string const& newname) void Channel::SetOwner(ObjectGuid guid, bool exclaim) { - if (_ownerGUID) + if (_ownerGuid) { - // [] will re-add player after it possible removed - PlayerContainer::iterator p_itr = playersStore.find(_ownerGUID); - if (p_itr != playersStore.end()) - p_itr->second.SetOwner(false); + auto itr = _playersStore.find(_ownerGuid); + if (itr != _playersStore.end()) + itr->second.SetOwner(false); } - _ownerGUID = guid; - if (_ownerGUID) + _ownerGuid = guid; + if (_ownerGuid) { - uint8 oldFlag = GetPlayerFlags(_ownerGUID); - playersStore[_ownerGUID].SetModerator(true); - playersStore[_ownerGUID].SetOwner(true); + uint8 oldFlag = GetPlayerFlags(_ownerGuid); + auto itr = _playersStore.find(_ownerGuid); + if (itr == _playersStore.end()) + return; + + itr->second.SetModerator(true); + itr->second.SetOwner(true); WorldPacket data; - MakeModeChange(&data, _ownerGUID, oldFlag); + MakeModeChange(&data, _ownerGuid, oldFlag); SendToAll(&data); if (exclaim) { - MakeOwnerChanged(&data, _ownerGUID); + MakeOwnerChanged(&data, _ownerGuid); SendToAll(&data); } @@ -720,58 +727,58 @@ void Channel::SetOwner(ObjectGuid guid, bool exclaim) } } -void Channel::SendToAll(WorldPacket* data, ObjectGuid guid) +void Channel::SendToAll(WorldPacket* data, ObjectGuid guid) const { - for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i) + for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i) if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first)) if (!guid || !player->GetSocial()->HasIgnore(guid.GetCounter())) player->GetSession()->SendPacket(data); } -void Channel::SendToAllButOne(WorldPacket* data, ObjectGuid who) +void Channel::SendToAllButOne(WorldPacket* data, ObjectGuid who) const { - for (PlayerContainer::const_iterator i = playersStore.begin(); i != playersStore.end(); ++i) + for (PlayerContainer::const_iterator i = _playersStore.begin(); i != _playersStore.end(); ++i) if (i->first != who) if (Player* player = ObjectAccessor::FindConnectedPlayer(i->first)) player->GetSession()->SendPacket(data); } -void Channel::SendToOne(WorldPacket* data, ObjectGuid who) +void Channel::SendToOne(WorldPacket* data, ObjectGuid who) const { if (Player* player = ObjectAccessor::FindConnectedPlayer(who)) player->GetSession()->SendPacket(data); } -void Channel::Voice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) +void Channel::Voice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) const { } -void Channel::DeVoice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) +void Channel::DeVoice(ObjectGuid /*guid1*/, ObjectGuid /*guid2*/) const { } -void Channel::MakeNotifyPacket(WorldPacket* data, uint8 notify_type) +void Channel::MakeNotifyPacket(WorldPacket* data, uint8 notify_type) const { - data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + _name.size()); + data->Initialize(SMSG_CHANNEL_NOTIFY, 1 + _channelName.size()); *data << uint8(notify_type); - *data << _name; + *data << _channelName; } -void Channel::MakeJoined(WorldPacket* data, ObjectGuid guid) +void Channel::MakeJoined(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_JOINED_NOTICE); *data << uint64(guid); } -void Channel::MakeLeft(WorldPacket* data, ObjectGuid guid) +void Channel::MakeLeft(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_LEFT_NOTICE); *data << uint64(guid); } -void Channel::MakeYouJoined(WorldPacket* data) +void Channel::MakeYouJoined(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_YOU_JOINED_NOTICE); *data << uint8(GetFlags()); @@ -779,63 +786,66 @@ void Channel::MakeYouJoined(WorldPacket* data) *data << uint32(0); } -void Channel::MakeYouLeft(WorldPacket* data) +void Channel::MakeYouLeft(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_YOU_LEFT_NOTICE); *data << uint32(GetChannelId()); *data << uint8(IsConstant()); } -void Channel::MakeWrongPassword(WorldPacket* data) +void Channel::MakeWrongPassword(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_WRONG_PASSWORD_NOTICE); } -void Channel::MakeNotMember(WorldPacket* data) +void Channel::MakeNotMember(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_NOT_MEMBER_NOTICE); } -void Channel::MakeNotModerator(WorldPacket* data) +void Channel::MakeNotModerator(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_NOT_MODERATOR_NOTICE); } -void Channel::MakePasswordChanged(WorldPacket* data, ObjectGuid guid) +void Channel::MakePasswordChanged(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_PASSWORD_CHANGED_NOTICE); *data << uint64(guid); } -void Channel::MakeOwnerChanged(WorldPacket* data, ObjectGuid guid) +void Channel::MakeOwnerChanged(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_OWNER_CHANGED_NOTICE); *data << uint64(guid); } -void Channel::MakePlayerNotFound(WorldPacket* data, std::string const& name) +void Channel::MakePlayerNotFound(WorldPacket* data, std::string const& name) const { MakeNotifyPacket(data, CHAT_PLAYER_NOT_FOUND_NOTICE); *data << name; } -void Channel::MakeNotOwner(WorldPacket* data) +void Channel::MakeNotOwner(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_NOT_OWNER_NOTICE); } -void Channel::MakeChannelOwner(WorldPacket* data) +void Channel::MakeChannelOwner(WorldPacket* data) const { - std::string name = ""; + std::string name; - if (!sObjectMgr->GetPlayerNameByGUID(_ownerGUID, name) || name.empty()) + CharacterInfo const* cInfo = sWorld->GetCharacterInfo(_ownerGuid); + if (!cInfo || cInfo->Name.empty()) name = "PLAYER_NOT_FOUND"; + else + name = cInfo->Name; MakeNotifyPacket(data, CHAT_CHANNEL_OWNER_NOTICE); - *data << ((IsConstant() || !_ownerGUID) ? "Nobody" : name); + *data << ((IsConstant() || !_ownerGuid) ? "Nobody" : name); } -void Channel::MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) +void Channel::MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) const { MakeNotifyPacket(data, CHAT_MODE_CHANGE_NOTICE); *data << uint64(guid); @@ -843,127 +853,127 @@ void Channel::MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) *data << uint8(GetPlayerFlags(guid)); } -void Channel::MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid) +void Channel::MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_ON_NOTICE); *data << uint64(guid); } -void Channel::MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid) +void Channel::MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_ANNOUNCEMENTS_OFF_NOTICE); *data << uint64(guid); } -void Channel::MakeMuted(WorldPacket* data) +void Channel::MakeMuted(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_MUTED_NOTICE); } -void Channel::MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good) +void Channel::MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const { MakeNotifyPacket(data, CHAT_PLAYER_KICKED_NOTICE); *data << uint64(bad); *data << uint64(good); } -void Channel::MakeBanned(WorldPacket* data) +void Channel::MakeBanned(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_BANNED_NOTICE); } -void Channel::MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) +void Channel::MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const { MakeNotifyPacket(data, CHAT_PLAYER_BANNED_NOTICE); *data << uint64(bad); *data << uint64(good); } -void Channel::MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) +void Channel::MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const { MakeNotifyPacket(data, CHAT_PLAYER_UNBANNED_NOTICE); *data << uint64(bad); *data << uint64(good); } -void Channel::MakePlayerNotBanned(WorldPacket* data, const std::string &name) +void Channel::MakePlayerNotBanned(WorldPacket* data, const std::string &name) const { MakeNotifyPacket(data, CHAT_PLAYER_NOT_BANNED_NOTICE); *data << name; } -void Channel::MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid) +void Channel::MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_PLAYER_ALREADY_MEMBER_NOTICE); *data << uint64(guid); } -void Channel::MakeInvite(WorldPacket* data, ObjectGuid guid) +void Channel::MakeInvite(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_INVITE_NOTICE); *data << uint64(guid); } -void Channel::MakeInviteWrongFaction(WorldPacket* data) +void Channel::MakeInviteWrongFaction(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_INVITE_WRONG_FACTION_NOTICE); } -void Channel::MakeWrongFaction(WorldPacket* data) +void Channel::MakeWrongFaction(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_WRONG_FACTION_NOTICE); } -void Channel::MakeInvalidName(WorldPacket* data) +void Channel::MakeInvalidName(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_INVALID_NAME_NOTICE); } -void Channel::MakeNotModerated(WorldPacket* data) +void Channel::MakeNotModerated(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_NOT_MODERATED_NOTICE); } -void Channel::MakePlayerInvited(WorldPacket* data, const std::string& name) +void Channel::MakePlayerInvited(WorldPacket* data, std::string const& name) const { MakeNotifyPacket(data, CHAT_PLAYER_INVITED_NOTICE); *data << name; } -void Channel::MakePlayerInviteBanned(WorldPacket* data, const std::string& name) +void Channel::MakePlayerInviteBanned(WorldPacket* data, std::string const& name) const { MakeNotifyPacket(data, CHAT_PLAYER_INVITE_BANNED_NOTICE); *data << name; } -void Channel::MakeThrottled(WorldPacket* data) +void Channel::MakeThrottled(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_THROTTLED_NOTICE); } -void Channel::MakeNotInArea(WorldPacket* data) +void Channel::MakeNotInArea(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_NOT_IN_AREA_NOTICE); } -void Channel::MakeNotInLfg(WorldPacket* data) +void Channel::MakeNotInLfg(WorldPacket* data) const { MakeNotifyPacket(data, CHAT_NOT_IN_LFG_NOTICE); } -void Channel::MakeVoiceOn(WorldPacket* data, ObjectGuid guid) +void Channel::MakeVoiceOn(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_VOICE_ON_NOTICE); *data << uint64(guid); } -void Channel::MakeVoiceOff(WorldPacket* data, ObjectGuid guid) +void Channel::MakeVoiceOff(WorldPacket* data, ObjectGuid guid) const { MakeNotifyPacket(data, CHAT_VOICE_OFF_NOTICE); *data << uint64(guid); } -void Channel::JoinNotify(ObjectGuid guid) +void Channel::JoinNotify(ObjectGuid guid) const { WorldPacket data(IsConstant() ? SMSG_USERLIST_ADD : SMSG_USERLIST_UPDATE, 8 + 1 + 1 + 4 + GetName().size()); data << uint64(guid); @@ -978,7 +988,7 @@ void Channel::JoinNotify(ObjectGuid guid) SendToAll(&data); } -void Channel::LeaveNotify(ObjectGuid guid) +void Channel::LeaveNotify(ObjectGuid guid) const { WorldPacket data(SMSG_USERLIST_REMOVE, 8 + 1 + 4 + GetName().size()); data << uint64(guid); diff --git a/src/server/game/Chat/Channels/Channel.h b/src/server/game/Chat/Channels/Channel.h index 319105d2a8d..23f9e5ae28f 100644 --- a/src/server/game/Chat/Channels/Channel.h +++ b/src/server/game/Chat/Channels/Channel.h @@ -19,10 +19,6 @@ #ifndef _CHANNEL_H #define _CHANNEL_H -#include <list> -#include <map> -#include <string> - #include "Common.h" #include "WorldSession.h" @@ -53,10 +49,10 @@ enum ChatNotify CHAT_MODERATION_OFF_NOTICE = 0x10, //+ "[%s] Channel moderation disabled by %s."; CHAT_MUTED_NOTICE = 0x11, //+ "[%s] You do not have permission to speak."; CHAT_PLAYER_KICKED_NOTICE = 0x12, //? "[%s] Player %s kicked by %s."; - CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are bannedStore from that channel."; - CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s bannedStore by %s."; + CHAT_BANNED_NOTICE = 0x13, //+ "[%s] You are banned from that channel."; + CHAT_PLAYER_BANNED_NOTICE = 0x14, //? "[%s] Player %s banned by %s."; CHAT_PLAYER_UNBANNED_NOTICE = 0x15, //? "[%s] Player %s unbanned by %s."; - CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not bannedStore."; + CHAT_PLAYER_NOT_BANNED_NOTICE = 0x16, //+ "[%s] Player %s is not banned."; CHAT_PLAYER_ALREADY_MEMBER_NOTICE = 0x17, //+ "[%s] Player %s is already on the channel."; CHAT_INVITE_NOTICE = 0x18, //+ "%2$s has invited you to join the channel '%1$s'."; CHAT_INVITE_WRONG_FACTION_NOTICE = 0x19, //+ "Target is in the wrong alliance for %s."; @@ -64,7 +60,7 @@ enum ChatNotify CHAT_INVALID_NAME_NOTICE = 0x1B, //+ "Invalid channel name"; CHAT_NOT_MODERATED_NOTICE = 0x1C, //+ "%s is not moderated"; CHAT_PLAYER_INVITED_NOTICE = 0x1D, //+ "[%s] You invited %s to join the channel"; - CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been bannedStore."; + CHAT_PLAYER_INVITE_BANNED_NOTICE = 0x1E, //+ "[%s] %s has been banned."; CHAT_THROTTLED_NOTICE = 0x1F, //+ "[%s] The number of messages that can be sent to this channel is limited, please wait to send another message."; CHAT_NOT_IN_AREA_NOTICE = 0x20, //+ "[%s] You are not in the correct area for this channel."; -- The user is trying to send a chat to a zone specific channel, and they're not physically in that zone. CHAT_NOT_IN_LFG_NOTICE = 0x21, //+ "[%s] You must be queued in looking for group before joining this channel."; -- The user must be in the looking for group system to join LFG chat channels. @@ -85,7 +81,7 @@ enum ChannelFlags CHANNEL_FLAG_VOICE = 0x80 // General 0x18 = 0x10 | 0x08 // Trade 0x3C = 0x20 | 0x10 | 0x08 | 0x04 - // LocalDefence 0x18 = 0x10 | 0x08 + // LocalDefense 0x18 = 0x10 | 0x08 // GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08 // LookingForGroup 0x50 = 0x40 | 0x10 }; @@ -122,23 +118,25 @@ class TC_GAME_API Channel { struct PlayerInfo { - ObjectGuid player; uint8 flags; bool HasFlag(uint8 flag) const { return (flags & flag) != 0; } void SetFlag(uint8 flag) { flags |= flag; } + bool IsOwner() const { return (flags & MEMBER_FLAG_OWNER) != 0; } void SetOwner(bool state) { if (state) flags |= MEMBER_FLAG_OWNER; else flags &= ~MEMBER_FLAG_OWNER; } + bool IsModerator() const { return (flags & MEMBER_FLAG_MODERATOR) != 0; } void SetModerator(bool state) { if (state) flags |= MEMBER_FLAG_MODERATOR; else flags &= ~MEMBER_FLAG_MODERATOR; } + bool IsMuted() const { return (flags & MEMBER_FLAG_MUTED) != 0; } void SetMuted(bool state) { @@ -148,106 +146,121 @@ class TC_GAME_API Channel }; public: - Channel(std::string const& name, uint32 channel_id, uint32 Team = 0); - std::string const& GetName() const { return _name; } + Channel(std::string const& name, uint32 channel_id, uint32 team = 0); + + std::string const& GetName() const { return _channelName; } + uint32 GetChannelId() const { return _channelId; } bool IsConstant() const { return _channelId != 0; } - bool IsAnnounce() const { return _announce; } bool IsLFG() const { return (GetFlags() & CHANNEL_FLAG_LFG) != 0; } - std::string const& GetPassword() const { return _password; } - void SetPassword(std::string const& npassword) { _password = npassword; } - void SetAnnounce(bool nannounce) { _announce = nannounce; } - uint32 GetNumPlayers() const { return playersStore.size(); } - uint8 GetFlags() const { return _flags; } - bool HasFlag(uint8 flag) const { return (_flags & flag) != 0; } + + bool IsAnnounce() const { return _announceEnabled; } + void SetAnnounce(bool nannounce) { _announceEnabled = nannounce; } + + std::string const& GetPassword() const { return _channelPassword; } + void SetPassword(std::string const& npassword) { _channelPassword = npassword; } + + uint32 GetNumPlayers() const { return _playersStore.size(); } + + uint8 GetFlags() const { return _channelFlags; } + bool HasFlag(uint8 flag) const { return (_channelFlags & flag) != 0; } void JoinChannel(Player* player, std::string const& pass); void LeaveChannel(Player* player, bool send = true); + void KickOrBan(Player const* player, std::string const& badname, bool ban); void Kick(Player const* player, std::string const& badname) { KickOrBan(player, badname, false); } void Ban(Player const* player, std::string const& badname) { KickOrBan(player, badname, true); } + void UnBan(Player const* player, std::string const& badname); void Password(Player const* player, std::string const& pass); void SetMode(Player const* player, std::string const& p2n, bool mod, bool set); - void SetOwner(ObjectGuid guid, bool exclaim = true); - void SetOwner(Player const* player, std::string const& name); - void SendWhoOwner(ObjectGuid guid); + void SetModerator(Player const* player, std::string const& newname) { SetMode(player, newname, true, true); } void UnsetModerator(Player const* player, std::string const& newname) { SetMode(player, newname, true, false); } void SetMute(Player const* player, std::string const& newname) { SetMode(player, newname, false, true); } void UnsetMute(Player const* player, std::string const& newname) { SetMode(player, newname, false, false); } - void List(Player const* player); + + void SetOwner(ObjectGuid guid, bool exclaim = true); + void SetOwner(Player const* player, std::string const& name); + void SendWhoOwner(ObjectGuid guid); + + void List(Player const* player) const; void Announce(Player const* player); - void Say(ObjectGuid guid, std::string const& what, uint32 lang); + void Say(ObjectGuid guid, std::string const& what, uint32 lang) const; void Invite(Player const* player, std::string const& newp); - void Voice(ObjectGuid guid1, ObjectGuid guid2); - void DeVoice(ObjectGuid guid1, ObjectGuid guid2); - void JoinNotify(ObjectGuid guid); // invisible notify - void LeaveNotify(ObjectGuid guid); // invisible notify - void SetOwnership(bool ownership) { _ownership = ownership; } + void Voice(ObjectGuid guid1, ObjectGuid guid2) const; + void DeVoice(ObjectGuid guid1, ObjectGuid guid2) const; + void JoinNotify(ObjectGuid guid) const; // invisible notify + void LeaveNotify(ObjectGuid guid) const; // invisible notify + void SetOwnership(bool ownership) { _ownershipEnabled = ownership; } static void CleanOldChannelsInDB(); private: // initial packet data (notify type and channel name) - void MakeNotifyPacket(WorldPacket* data, uint8 notify_type); + void MakeNotifyPacket(WorldPacket* data, uint8 notify_type) const; // type specific packet data - void MakeJoined(WorldPacket* data, ObjectGuid guid); //+ 0x00 - void MakeLeft(WorldPacket* data, ObjectGuid guid); //+ 0x01 - void MakeYouJoined(WorldPacket* data); //+ 0x02 - void MakeYouLeft(WorldPacket* data); //+ 0x03 - void MakeWrongPassword(WorldPacket* data); //? 0x04 - void MakeNotMember(WorldPacket* data); //? 0x05 - void MakeNotModerator(WorldPacket* data); //? 0x06 - void MakePasswordChanged(WorldPacket* data, ObjectGuid guid); //+ 0x07 - void MakeOwnerChanged(WorldPacket* data, ObjectGuid guid); //? 0x08 - void MakePlayerNotFound(WorldPacket* data, std::string const& name); //+ 0x09 - void MakeNotOwner(WorldPacket* data); //? 0x0A - void MakeChannelOwner(WorldPacket* data); //? 0x0B - void MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags);//+ 0x0C - void MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid); //+ 0x0D - void MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid); //+ 0x0E - void MakeMuted(WorldPacket* data); //? 0x11 - void MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good);//? 0x12 - void MakeBanned(WorldPacket* data); //? 0x13 - void MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good);//? 0x14 - void MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good);//? 0x15 - void MakePlayerNotBanned(WorldPacket* data, std::string const& name); //? 0x16 - void MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid); //+ 0x17 - void MakeInvite(WorldPacket* data, ObjectGuid guid); //? 0x18 - void MakeInviteWrongFaction(WorldPacket* data); //? 0x19 - void MakeWrongFaction(WorldPacket* data); //? 0x1A - void MakeInvalidName(WorldPacket* data); //? 0x1B - void MakeNotModerated(WorldPacket* data); //? 0x1C - void MakePlayerInvited(WorldPacket* data, std::string const& name); //+ 0x1D - void MakePlayerInviteBanned(WorldPacket* data, std::string const& name);//? 0x1E - void MakeThrottled(WorldPacket* data); //? 0x1F - void MakeNotInArea(WorldPacket* data); //? 0x20 - void MakeNotInLfg(WorldPacket* data); //? 0x21 - void MakeVoiceOn(WorldPacket* data, ObjectGuid guid); //+ 0x22 - void MakeVoiceOff(WorldPacket* data, ObjectGuid guid); //+ 0x23 - - void SendToAll(WorldPacket* data, ObjectGuid guid = ObjectGuid::Empty); - void SendToAllButOne(WorldPacket* data, ObjectGuid who); - void SendToOne(WorldPacket* data, ObjectGuid who); - - bool IsOn(ObjectGuid who) const { return playersStore.find(who) != playersStore.end(); } - bool IsBanned(ObjectGuid guid) const { return bannedStore.find(guid) != bannedStore.end(); } + void MakeJoined(WorldPacket* data, ObjectGuid guid) const; //+ 0x00 + void MakeLeft(WorldPacket* data, ObjectGuid guid) const; //+ 0x01 + void MakeYouJoined(WorldPacket* data) const; //+ 0x02 + void MakeYouLeft(WorldPacket* data) const; //+ 0x03 + void MakeWrongPassword(WorldPacket* data) const; //? 0x04 + void MakeNotMember(WorldPacket* data) const; //? 0x05 + void MakeNotModerator(WorldPacket* data) const; //? 0x06 + void MakePasswordChanged(WorldPacket* data, ObjectGuid guid) const; //+ 0x07 + void MakeOwnerChanged(WorldPacket* data, ObjectGuid guid) const; //? 0x08 + void MakePlayerNotFound(WorldPacket* data, std::string const& name) const; //+ 0x09 + void MakeNotOwner(WorldPacket* data) const; //? 0x0A + void MakeChannelOwner(WorldPacket* data) const; //? 0x0B + void MakeModeChange(WorldPacket* data, ObjectGuid guid, uint8 oldflags) const; //+ 0x0C + void MakeAnnouncementsOn(WorldPacket* data, ObjectGuid guid) const; //+ 0x0D + void MakeAnnouncementsOff(WorldPacket* data, ObjectGuid guid) const; //+ 0x0E + void MakeMuted(WorldPacket* data) const; //? 0x11 + void MakePlayerKicked(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x12 + void MakeBanned(WorldPacket* data) const; //? 0x13 + void MakePlayerBanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x14 + void MakePlayerUnbanned(WorldPacket* data, ObjectGuid bad, ObjectGuid good) const; //? 0x15 + void MakePlayerNotBanned(WorldPacket* data, std::string const& name) const; //? 0x16 + void MakePlayerAlreadyMember(WorldPacket* data, ObjectGuid guid) const; //+ 0x17 + void MakeInvite(WorldPacket* data, ObjectGuid guid) const; //? 0x18 + void MakeInviteWrongFaction(WorldPacket* data) const; //? 0x19 + void MakeWrongFaction(WorldPacket* data) const; //? 0x1A + void MakeInvalidName(WorldPacket* data) const; //? 0x1B + void MakeNotModerated(WorldPacket* data) const; //? 0x1C + void MakePlayerInvited(WorldPacket* data, std::string const& name) const; //+ 0x1D + void MakePlayerInviteBanned(WorldPacket* data, std::string const& name) const; //? 0x1E + void MakeThrottled(WorldPacket* data) const; //? 0x1F + void MakeNotInArea(WorldPacket* data) const; //? 0x20 + void MakeNotInLfg(WorldPacket* data) const; //? 0x21 + void MakeVoiceOn(WorldPacket* data, ObjectGuid guid) const; //+ 0x22 + void MakeVoiceOff(WorldPacket* data, ObjectGuid guid) const; //+ 0x23 + + void SendToAll(WorldPacket* data, ObjectGuid guid = ObjectGuid::Empty) const; + void SendToAllButOne(WorldPacket* data, ObjectGuid who) const; + void SendToOne(WorldPacket* data, ObjectGuid who) const; + + bool IsOn(ObjectGuid who) const { return _playersStore.count(who) != 0; } + bool IsBanned(ObjectGuid guid) const { return _bannedStore.count(guid) != 0; } void UpdateChannelInDB() const; void UpdateChannelUseageInDB() const; uint8 GetPlayerFlags(ObjectGuid guid) const { - PlayerContainer::const_iterator itr = playersStore.find(guid); - return itr != playersStore.end() ? itr->second.flags : 0; + PlayerContainer::const_iterator itr = _playersStore.find(guid); + return itr != _playersStore.end() ? itr->second.flags : 0; } void SetModerator(ObjectGuid guid, bool set) { - if (playersStore[guid].IsModerator() != set) + if (!IsOn(guid)) + return; + + PlayerInfo& playerInfo = _playersStore.at(guid); + if (playerInfo.IsModerator() != set) { uint8 oldFlag = GetPlayerFlags(guid); - playersStore[guid].SetModerator(set); + playerInfo.SetModerator(set); WorldPacket data; MakeModeChange(&data, guid, oldFlag); @@ -257,10 +270,14 @@ class TC_GAME_API Channel void SetMute(ObjectGuid guid, bool set) { - if (playersStore[guid].IsMuted() != set) + if (!IsOn(guid)) + return; + + PlayerInfo& playerInfo = _playersStore.at(guid); + if (playerInfo.IsMuted() != set) { uint8 oldFlag = GetPlayerFlags(guid); - playersStore[guid].SetMuted(set); + playerInfo.SetMuted(set); WorldPacket data; MakeModeChange(&data, guid, oldFlag); @@ -269,19 +286,20 @@ class TC_GAME_API Channel } typedef std::map<ObjectGuid, PlayerInfo> PlayerContainer; - typedef GuidSet BannedContainer; + typedef GuidUnorderedSet BannedContainer; + + bool _announceEnabled; //< Whether we should broadcast a packet whenever a player joins/exits the channel + bool _ownershipEnabled; //< Whether the channel has to maintain an owner + bool _persistentChannel; //< Whether the channel is saved to DB - bool _announce; - bool _ownership; - bool _IsSaved; - uint8 _flags; + uint8 _channelFlags; uint32 _channelId; - uint32 _Team; - ObjectGuid _ownerGUID; - std::string _name; - std::string _password; - PlayerContainer playersStore; - BannedContainer bannedStore; + uint32 _channelTeam; + ObjectGuid _ownerGuid; + std::string _channelName; + std::string _channelPassword; + PlayerContainer _playersStore; + BannedContainer _bannedStore; }; #endif diff --git a/src/server/game/Chat/Channels/ChannelMgr.cpp b/src/server/game/Chat/Channels/ChannelMgr.cpp index 3e9a633729a..043d4bdc2bc 100644 --- a/src/server/game/Chat/Channels/ChannelMgr.cpp +++ b/src/server/game/Chat/Channels/ChannelMgr.cpp @@ -39,14 +39,14 @@ ChannelMgr* ChannelMgr::forTeam(uint32 team) if (team == HORDE) return &hordeChannelMgr; - return NULL; + return nullptr; } Channel* ChannelMgr::GetJoinChannel(std::string const& name, uint32 channelId) { std::wstring wname; if (!Utf8toWStr(name, wname)) - return NULL; + return nullptr; wstrToLower(wname); @@ -66,7 +66,7 @@ Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pk { std::wstring wname; if (!Utf8toWStr(name, wname)) - return NULL; + return nullptr; wstrToLower(wname); @@ -81,7 +81,7 @@ Channel* ChannelMgr::GetChannel(std::string const& name, Player* player, bool pk player->GetSession()->SendPacket(&data); } - return NULL; + return nullptr; } return i->second; diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index ec90a5f7efb..27fa7ffb409 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -81,7 +81,7 @@ bool ChatHandler::isAvailable(ChatCommand const& cmd) const bool ChatHandler::HasLowerSecurity(Player* target, ObjectGuid guid, bool strong) { - WorldSession* target_session = NULL; + WorldSession* target_session = nullptr; uint32 target_account = 0; if (target) @@ -116,7 +116,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac else if (target_account) target_sec = AccountMgr::GetSecurity(target_account, realm.Id.Realm); else - return true; // caller must report error for (target == NULL && target_account == 0) + return true; // caller must report error for (target == nullptr && target_account == 0) AccountTypes target_ac_sec = AccountTypes(target_sec); if (m_session->GetSecurity() < target_ac_sec || (strong && m_session->GetSecurity() <= target_ac_sec)) @@ -129,7 +129,7 @@ bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_ac return false; } -bool ChatHandler::hasStringAbbr(const char* name, const char* part) +bool ChatHandler::hasStringAbbr(char const* name, char const* part) { // non "" command if (*name) @@ -166,7 +166,7 @@ void ChatHandler::SendSysMessage(const char *str, bool escapeCharacters) { size_t startPos = 0; std::ostringstream o; - while (const char* charPos = strchr(str + startPos, '|')) + while (char const* charPos = strchr(str + startPos, '|')) { o.write(str + startPos, charPos - str - startPos); o << "||"; @@ -184,7 +184,7 @@ void ChatHandler::SendSysMessage(const char *str, bool escapeCharacters) while (char* line = LineFromMessage(pos)) { - BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, NULL, line); + BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line); m_session->SendPacket(&data); } @@ -202,7 +202,7 @@ void ChatHandler::SendGlobalSysMessage(const char *str) while (char* line = LineFromMessage(pos)) { - BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, NULL, line); + BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line); sWorld->SendGlobalMessage(&data); } @@ -220,7 +220,7 @@ void ChatHandler::SendGlobalGMSysMessage(const char *str) while (char* line = LineFromMessage(pos)) { - BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, NULL, line); + BuildChatPacket(data, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, nullptr, nullptr, line); sWorld->SendGlobalGMMessage(&data); } @@ -232,7 +232,7 @@ void ChatHandler::SendSysMessage(uint32 entry) SendSysMessage(GetTrinityString(entry)); } -bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, const char* text, std::string const& fullcmd) +bool ChatHandler::ExecuteCommandInTable(std::vector<ChatCommand> const& table, char const* text, std::string const& fullcmd) { char const* oldtext = text; std::string cmd = ""; @@ -450,7 +450,7 @@ Valid examples: if (sWorld->getIntConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3) { const char validSequence[6] = "cHhhr"; - const char* validSequenceIterator = validSequence; + char const* validSequenceIterator = validSequence; const std::string validCommands = "cHhr|"; while (*message) @@ -525,7 +525,7 @@ bool ChatHandler::ShowHelpForSubCommands(std::vector<ChatCommand> const& table, return true; } -bool ChatHandler::ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd) +bool ChatHandler::ShowHelpForCommand(std::vector<ChatCommand> const& table, char const* cmd) { if (*cmd) { @@ -539,7 +539,7 @@ bool ChatHandler::ShowHelpForCommand(std::vector<ChatCommand> const& table, cons continue; // have subcommand - char const* subcmd = (*cmd) ? strtok(NULL, " ") : ""; + char const* subcmd = (*cmd) ? strtok(nullptr, " ") : ""; if (!table[i].ChildCommands.empty() && subcmd && *subcmd) { @@ -694,7 +694,7 @@ size_t ChatHandler::BuildChatPacket(WorldPacket& data, ChatMsg chatType, Languag Player* ChatHandler::getSelectedPlayer() { if (!m_session) - return NULL; + return nullptr; ObjectGuid selected = m_session->GetPlayer()->GetTarget(); if (!selected) @@ -706,7 +706,7 @@ Player* ChatHandler::getSelectedPlayer() Unit* ChatHandler::getSelectedUnit() { if (!m_session) - return NULL; + return nullptr; if (Unit* selected = m_session->GetPlayer()->GetSelectedUnit()) return selected; @@ -717,7 +717,7 @@ Unit* ChatHandler::getSelectedUnit() WorldObject* ChatHandler::getSelectedObject() { if (!m_session) - return NULL; + return nullptr; ObjectGuid guid = m_session->GetPlayer()->GetTarget(); @@ -730,7 +730,7 @@ WorldObject* ChatHandler::getSelectedObject() Creature* ChatHandler::getSelectedCreature() { if (!m_session) - return NULL; + return nullptr; return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(), m_session->GetPlayer()->GetTarget()); } @@ -738,7 +738,7 @@ Creature* ChatHandler::getSelectedCreature() Player* ChatHandler::getSelectedPlayerOrSelf() { if (!m_session) - return NULL; + return nullptr; ObjectGuid selected = m_session->GetPlayer()->GetTarget(); if (!selected) @@ -757,14 +757,14 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** s { // skip empty if (!text) - return NULL; + return nullptr; // skip spaces while (*text == ' '||*text == '\t'||*text == '\b') ++text; if (!*text) - return NULL; + return nullptr; // return non link case if (text[0] != '|') @@ -776,28 +776,28 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** s char* check = strtok(text, "|"); // skip color if (!check) - return NULL; // end of data + return nullptr; // end of data - char* cLinkType = strtok(NULL, ":"); // linktype + char* cLinkType = strtok(nullptr, ":"); // linktype if (!cLinkType) - return NULL; // end of data + return nullptr; // end of data if (strcmp(cLinkType, linkType) != 0) { - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after retturn from function + strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after retturn from function SendSysMessage(LANG_WRONG_LINK_TYPE); - return NULL; + return nullptr; } - char* cKeys = strtok(NULL, "|"); // extract keys and values - char* cKeysTail = strtok(NULL, ""); + char* cKeys = strtok(nullptr, "|"); // extract keys and values + char* cKeysTail = strtok(nullptr, ""); char* cKey = strtok(cKeys, ":|"); // extract key if (something1) - *something1 = strtok(NULL, ":|"); // extract something + *something1 = strtok(nullptr, ":|"); // extract something strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after return from function + strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after return from function return cKey; } @@ -805,14 +805,14 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, { // skip empty if (!text) - return NULL; + return nullptr; // skip spaces while (*text == ' '||*text == '\t'||*text == '\b') ++text; if (!*text) - return NULL; + return nullptr; // return non link case if (text[0] != '|') @@ -830,48 +830,48 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes, { char* check = strtok(text, "|"); // skip color if (!check) - return NULL; // end of data + return nullptr; // end of data - tail = strtok(NULL, ""); // tail + tail = strtok(nullptr, ""); // tail } else tail = text+1; // skip first | char* cLinkType = strtok(tail, ":"); // linktype if (!cLinkType) - return NULL; // end of data + return nullptr; // end of data for (int i = 0; linkTypes[i]; ++i) { if (strcmp(cLinkType, linkTypes[i]) == 0) { - char* cKeys = strtok(NULL, "|"); // extract keys and values - char* cKeysTail = strtok(NULL, ""); + char* cKeys = strtok(nullptr, "|"); // extract keys and values + char* cKeysTail = strtok(nullptr, ""); char* cKey = strtok(cKeys, ":|"); // extract key if (something1) - *something1 = strtok(NULL, ":|"); // extract something + *something1 = strtok(nullptr, ":|"); // extract something strtok(cKeysTail, "]"); // restart scan tail and skip name with possible spaces - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after return from function + strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after return from function if (found_idx) *found_idx = i; return cKey; } } - strtok(NULL, " "); // skip link tail (to allow continue strtok(NULL, s) use after return from function + strtok(nullptr, " "); // skip link tail (to allow continue strtok(nullptr, s) use after return from function SendSysMessage(LANG_WRONG_LINK_TYPE); - return NULL; + return nullptr; } GameObject* ChatHandler::GetNearbyGameObject() { if (!m_session) - return NULL; + return nullptr; Player* pl = m_session->GetPlayer(); - GameObject* obj = NULL; + GameObject* obj = nullptr; Trinity::NearestGameObjectCheck check(*pl); Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectCheck> searcher(pl, obj, check); pl->VisitNearbyGridObject(SIZE_OF_GRIDS, searcher); @@ -881,7 +881,7 @@ GameObject* ChatHandler::GetNearbyGameObject() GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(ObjectGuid::LowType lowguid, uint32 entry) { if (!m_session) - return NULL; + return nullptr; Player* pl = m_session->GetPlayer(); @@ -926,7 +926,7 @@ uint32 ChatHandler::extractSpellIdFromLink(char* text) // number or [name] Shift-click form |color|Htalent:talent_id, rank|h[name]|h|r // number or [name] Shift-click form |color|Htrade:spell_id, skill_id, max_value, cur_value|h[name]|h|r int type = 0; - char* param1_str = NULL; + char* param1_str = nullptr; char* idS = extractKeyFromLink(text, spellKeys, &type, ¶m1_str); if (!idS) return 0; @@ -974,7 +974,7 @@ GameTele const* ChatHandler::extractGameTeleFromLink(char* text) // id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r char* cId = extractKeyFromLink(text, "Htele"); if (!cId) - return NULL; + return nullptr; // id case (explicit or from shift link) if (cId[0] >= '0' || cId[0] >= '9') @@ -1064,7 +1064,7 @@ std::string ChatHandler::extractPlayerNameFromLink(char* text) return name; } -bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid /*=NULL*/, std::string* player_name /*= NULL*/) +bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid /*=nullptr*/, std::string* player_name /*= nullptr*/) { if (args && *args) { @@ -1120,12 +1120,12 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* p void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2) { char* p1 = strtok(args, " "); - char* p2 = strtok(NULL, " "); + char* p2 = strtok(nullptr, " "); if (!p2) { p2 = p1; - p1 = NULL; + p1 = nullptr; } if (arg1) @@ -1138,7 +1138,7 @@ void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2) char* ChatHandler::extractQuotedArg(char* args) { if (!args || !*args) - return NULL; + return nullptr; if (*args == '"') return strtok(args+1, "\""); @@ -1151,9 +1151,9 @@ char* ChatHandler::extractQuotedArg(char* args) continue; } - // return NULL if we reached the end of the string + // return nullptr if we reached the end of the string if (!*args) - return NULL; + return nullptr; // since we skipped all spaces, we expect another token now if (*args == '"') @@ -1171,7 +1171,7 @@ char* ChatHandler::extractQuotedArg(char* args) return strtok(args + 1, "\""); } else - return NULL; + return nullptr; } } @@ -1223,9 +1223,9 @@ bool CliHandler::needReportToTarget(Player* /*chr*/) const return true; } -bool ChatHandler::GetPlayerGroupAndGUIDByName(const char* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline) +bool ChatHandler::GetPlayerGroupAndGUIDByName(char const* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline) { - player = NULL; + player = nullptr; guid.Clear(); if (cname) diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h index 1c9368275ad..a700afdf97d 100644 --- a/src/server/game/Chat/Chat.h +++ b/src/server/game/Chat/Chat.h @@ -67,7 +67,7 @@ class TC_GAME_API ChatHandler // Builds chat packet and returns receiver guid position in the packet to substitute in whisper builders static size_t BuildChatPacket(WorldPacket& data, ChatMsg chatType, Language language, WorldObject const* sender, WorldObject const* receiver, std::string const& message, uint32 achievementId = 0, std::string const& channelName = "", LocaleConstant locale = DEFAULT_LOCALE); - static char* LineFromMessage(char*& pos) { char* start = strtok(pos, "\n"); pos = NULL; return start; } + static char* LineFromMessage(char*& pos) { char* start = strtok(pos, "\n"); pos = nullptr; return start; } // function with different implementation for chat/console virtual char const* GetTrinityString(uint32 entry) const; @@ -76,7 +76,7 @@ class TC_GAME_API ChatHandler void SendSysMessage(uint32 entry); template<typename... Args> - void PSendSysMessage(const char* fmt, Args&&... args) + void PSendSysMessage(char const* fmt, Args&&... args) { SendSysMessage(Trinity::StringFormat(fmt, std::forward<Args>(args)...).c_str()); } @@ -93,15 +93,15 @@ class TC_GAME_API ChatHandler return Trinity::StringFormat(GetTrinityString(entry), std::forward<Args>(args)...); } - bool ParseCommands(const char* text); + bool ParseCommands(char const* text); static std::vector<ChatCommand> const& getCommandTable(); static void invalidateCommandTable(); - bool isValidChatMessage(const char* msg); + bool isValidChatMessage(char const* msg); void SendGlobalSysMessage(const char *str); - bool hasStringAbbr(const char* name, const char* part); + bool hasStringAbbr(char const* name, char const* part); // function with different implementation for chat/console virtual bool isAvailable(ChatCommand const& cmd) const; @@ -122,20 +122,20 @@ class TC_GAME_API ChatHandler // Returns either the selected player or self if there is no selected player Player* getSelectedPlayerOrSelf(); - char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL); - char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL); + char* extractKeyFromLink(char* text, char const* linkType, char** something1 = nullptr); + char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = nullptr); - // if args have single value then it return in arg2 and arg1 == NULL + // if args have single value then it return in arg2 and arg1 == nullptr void extractOptFirstArg(char* args, char** arg1, char** arg2); char* extractQuotedArg(char* args); uint32 extractSpellIdFromLink(char* text); ObjectGuid extractGuidFromLink(char* text); GameTele const* extractGameTeleFromLink(char* text); - bool GetPlayerGroupAndGUIDByName(const char* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline = false); + bool GetPlayerGroupAndGUIDByName(char const* cname, Player*& player, Group*& group, ObjectGuid& guid, bool offline = false); std::string extractPlayerNameFromLink(char* text); // select by arg (name/link) or in-game selection online/offline player or self if a creature is selected - bool extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid = NULL, std::string* player_name = NULL); + bool extractPlayerTarget(char* args, Player** player, ObjectGuid* player_guid = nullptr, std::string* player_name = nullptr); std::string playerLink(std::string const& name) const { return m_session ? "|cffffffff|Hplayer:"+name+"|h["+name+"]|h|r" : name; } std::string GetNameLink(Player* chr) const; @@ -145,15 +145,15 @@ class TC_GAME_API ChatHandler bool HasSentErrorMessage() const { return sentErrorMessage; } void SetSentErrorMessage(bool val){ sentErrorMessage = val; } - bool ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd); + bool ShowHelpForCommand(std::vector<ChatCommand> const& table, char const* cmd); protected: - explicit ChatHandler() : m_session(NULL), sentErrorMessage(false) { } // for CLI subclass - static bool SetDataForCommandInTable(std::vector<ChatCommand>& table, const char* text, uint32 permission, std::string const& help, std::string const& fullcommand); - bool ExecuteCommandInTable(std::vector<ChatCommand> const& table, const char* text, std::string const& fullcmd); + explicit ChatHandler() : m_session(nullptr), sentErrorMessage(false) { } // for CLI subclass + static bool SetDataForCommandInTable(std::vector<ChatCommand>& table, char const* text, uint32 permission, std::string const& help, std::string const& fullcommand); + bool ExecuteCommandInTable(std::vector<ChatCommand> const& table, char const* text, std::string const& fullcmd); bool ShowHelpForSubCommands(std::vector<ChatCommand> const& table, char const* cmd, char const* subcmd); private: - WorldSession* m_session; // != NULL for chat command call and NULL for CLI command + WorldSession* m_session; // != nullptr for chat command call and nullptr for CLI command // common global flag bool sentErrorMessage; diff --git a/src/server/game/Chat/ChatLink.cpp b/src/server/game/Chat/ChatLink.cpp index 3602b54bc44..92d69edb5e8 100644 --- a/src/server/game/Chat/ChatLink.cpp +++ b/src/server/game/Chat/ChatLink.cpp @@ -71,7 +71,7 @@ inline std::string ReadSkip(std::istringstream& iss, char term) return res; } -inline bool CheckDelimiter(std::istringstream& iss, char delimiter, const char* context) +inline bool CheckDelimiter(std::istringstream& iss, char delimiter, char const* context) { char c = iss.peek(); if (c != delimiter) @@ -96,7 +96,7 @@ inline bool ReadHex(std::istringstream& iss, uint32& res, uint32 length) #define DELIMITER ':' #define PIPE_CHAR '|' -bool ChatLink::ValidateName(char* buffer, const char* /*context*/) +bool ChatLink::ValidateName(char* buffer, char const* /*context*/) { _name = buffer; return true; @@ -170,7 +170,7 @@ bool ItemChatLink::Initialize(std::istringstream& iss) inline std::string ItemChatLink::FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const { std::stringstream ss; - if (locale == NULL || index >= locale->Name.size()) + if (locale == nullptr || index >= locale->Name.size()) ss << _item->Name1; else ss << locale->Name[index]; @@ -179,13 +179,13 @@ inline std::string ItemChatLink::FormatName(uint8 index, ItemLocale const* local return ss.str(); } -bool ItemChatLink::ValidateName(char* buffer, const char* context) +bool ItemChatLink::ValidateName(char* buffer, char const* context) { ChatLink::ValidateName(buffer, context); - char* const* suffixStrings = _suffix ? _suffix->nameSuffix : (_property ? _property->nameSuffix : NULL); + char* const* suffixStrings = _suffix ? _suffix->nameSuffix : (_property ? _property->nameSuffix : nullptr); - bool res = (FormatName(LOCALE_enUS, NULL, suffixStrings) == buffer); + bool res = (FormatName(LOCALE_enUS, nullptr, suffixStrings) == buffer); if (!res) { ItemLocale const* il = sObjectMgr->GetItemLocale(_item->ItemId); @@ -239,7 +239,7 @@ bool QuestChatLink::Initialize(std::istringstream& iss) return true; } -bool QuestChatLink::ValidateName(char* buffer, const char* context) +bool QuestChatLink::ValidateName(char* buffer, char const* context) { ChatLink::ValidateName(buffer, context); @@ -280,7 +280,7 @@ bool SpellChatLink::Initialize(std::istringstream& iss) return true; } -bool SpellChatLink::ValidateName(char* buffer, const char* context) +bool SpellChatLink::ValidateName(char* buffer, char const* context) { ChatLink::ValidateName(buffer, context); @@ -373,7 +373,7 @@ bool AchievementChatLink::Initialize(std::istringstream& iss) return true; } -bool AchievementChatLink::ValidateName(char* buffer, const char* context) +bool AchievementChatLink::ValidateName(char* buffer, char const* context) { ChatLink::ValidateName(buffer, context); @@ -537,7 +537,7 @@ bool GlyphChatLink::Initialize(std::istringstream& iss) return true; } -LinkExtractor::LinkExtractor(const char* msg) : _iss(msg) { } +LinkExtractor::LinkExtractor(char const* msg) : _iss(msg) { } LinkExtractor::~LinkExtractor() { @@ -549,19 +549,19 @@ LinkExtractor::~LinkExtractor() bool LinkExtractor::IsValidMessage() { const char validSequence[6] = "cHhhr"; - const char* validSequenceIterator = validSequence; + char const* validSequenceIterator = validSequence; char buffer[256]; std::istringstream::pos_type startPos = 0; uint32 color = 0; - ChatLink* link = NULL; + ChatLink* link = nullptr; while (!_iss.eof()) { if (validSequence == validSequenceIterator) { - link = NULL; + link = nullptr; _iss.ignore(255, PIPE_CHAR); startPos = _iss.tellg() - std::istringstream::pos_type(1); } diff --git a/src/server/game/Chat/ChatLink.h b/src/server/game/Chat/ChatLink.h index 0d413ce49df..32ea4b73698 100644 --- a/src/server/game/Chat/ChatLink.h +++ b/src/server/game/Chat/ChatLink.h @@ -44,7 +44,7 @@ public: void SetBounds(std::istringstream::pos_type startPos, std::istringstream::pos_type endPos) { _startPos = startPos; _endPos = endPos; } virtual bool Initialize(std::istringstream& iss) = 0; - virtual bool ValidateName(char* buffer, const char* context) = 0; + virtual bool ValidateName(char* buffer, char const* context) = 0; protected: uint32 _color; @@ -57,12 +57,12 @@ protected: class TC_GAME_API ItemChatLink : public ChatLink { public: - ItemChatLink() : ChatLink(), _item(NULL), _suffix(NULL), _property(NULL) + ItemChatLink() : ChatLink(), _item(nullptr), _suffix(nullptr), _property(nullptr) { memset(_data, 0, sizeof(_data)); } virtual bool Initialize(std::istringstream& iss) override; - virtual bool ValidateName(char* buffer, const char* context) override; + virtual bool ValidateName(char* buffer, char const* context) override; protected: std::string FormatName(uint8 index, ItemLocale const* locale, char* const* suffixStrings) const; @@ -79,7 +79,7 @@ class TC_GAME_API QuestChatLink : public ChatLink public: QuestChatLink() : ChatLink(), _quest(nullptr), _questLevel(0) { } virtual bool Initialize(std::istringstream& iss) override; - virtual bool ValidateName(char* buffer, const char* context) override; + virtual bool ValidateName(char* buffer, char const* context) override; protected: Quest const* _quest; @@ -92,7 +92,7 @@ class TC_GAME_API SpellChatLink : public ChatLink public: SpellChatLink() : ChatLink(), _spell(nullptr) { } virtual bool Initialize(std::istringstream& iss) override; - virtual bool ValidateName(char* buffer, const char* context) override; + virtual bool ValidateName(char* buffer, char const* context) override; protected: SpellInfo const* _spell; @@ -102,12 +102,12 @@ protected: class TC_GAME_API AchievementChatLink : public ChatLink { public: - AchievementChatLink() : ChatLink(), _guid(0), _achievement(NULL) + AchievementChatLink() : ChatLink(), _guid(0), _achievement(nullptr) { memset(_data, 0, sizeof(_data)); } virtual bool Initialize(std::istringstream& iss) override; - virtual bool ValidateName(char* buffer, const char* context) override; + virtual bool ValidateName(char* buffer, char const* context) override; protected: uint32 _guid; @@ -152,7 +152,7 @@ public: class TC_GAME_API GlyphChatLink : public SpellChatLink { public: - GlyphChatLink() : SpellChatLink(), _slotId(0), _glyph(NULL) { } + GlyphChatLink() : SpellChatLink(), _slotId(0), _glyph(nullptr) { } virtual bool Initialize(std::istringstream& iss) override; private: uint32 _slotId; @@ -162,7 +162,7 @@ private: class TC_GAME_API LinkExtractor { public: - explicit LinkExtractor(const char* msg); + explicit LinkExtractor(char const* msg); ~LinkExtractor(); bool IsValidMessage(); diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index afb6255079b..6317fae2767 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -103,7 +103,8 @@ ConditionMgr::ConditionTypeInfo const ConditionMgr::StaticConditionTypeData[COND { "Realm Achievement", true, false, false }, { "In Water", false, false, false }, { "Terrain Swap", false, false, false }, - { "Sit/stand state", true, true, false } + { "Sit/stand state", true, true, false }, + { "Daily Quest Completed",true, false, false } }; // Checks if object meets the condition @@ -448,6 +449,12 @@ bool Condition::Meets(ConditionSourceInfo& sourceInfo) const } break; } + case CONDITION_DAILY_QUEST_DONE: + { + if (Player* player = object->ToPlayer()) + condMeets = player->IsDailyQuestDone(ConditionValue1); + break; + } default: condMeets = false; break; @@ -621,6 +628,9 @@ uint32 Condition::GetSearcherTypeMaskForCondition() const case CONDITION_STAND_STATE: mask |= GRID_MAP_TYPE_MASK_CREATURE | GRID_MAP_TYPE_MASK_PLAYER; break; + case CONDITION_DAILY_QUEST_DONE: + mask |= GRID_MAP_TYPE_MASK_PLAYER; + break; default: ASSERT(false && "Condition::GetSearcherTypeMaskForCondition - missing condition handling!"); break; @@ -738,7 +748,7 @@ bool ConditionMgr::IsObjectMeetToConditionList(ConditionSourceInfo& sourceInfo, //! If not found, add an entry in the store and set to true (placeholder) if (itr == elseGroupStore.end()) elseGroupStore[condition->ElseGroup] = true; - else if (!(*itr).second) + else if (!(*itr).second) //! If another condition in this group was unmatched before this, don't bother checking (the group is false anyway) continue; if (condition->ReferenceId)//handle reference @@ -1791,6 +1801,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const case CONDITION_QUESTTAKEN: case CONDITION_QUEST_NONE: case CONDITION_QUEST_COMPLETE: + case CONDITION_DAILY_QUEST_DONE: { if (!sObjectMgr->GetQuestTemplate(cond->ConditionValue1)) { diff --git a/src/server/game/Conditions/ConditionMgr.h b/src/server/game/Conditions/ConditionMgr.h index 83e714781c3..4ec2be0753a 100644 --- a/src/server/game/Conditions/ConditionMgr.h +++ b/src/server/game/Conditions/ConditionMgr.h @@ -73,7 +73,8 @@ enum ConditionTypes CONDITION_IN_WATER = 40, // 0 0 0 true if unit in water CONDITION_TERRAIN_SWAP = 41, // only for 6.x CONDITION_STAND_STATE = 42, // stateType state 0 true if unit matches specified sitstate (0,x: has exactly state x; 1,0: any standing state; 1,1: any sitting state;) - CONDITION_MAX = 43 // MAX + CONDITION_DAILY_QUEST_DONE = 43, // quest id 0 0 true if daily quest has been completed for the day + CONDITION_MAX = 44 // MAX }; /*! Documentation on implementing a new ConditionSourceType: diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 36ec418ed56..051b5e824bf 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -25,7 +25,7 @@ #include "Timer.h" #include "ObjectDefines.h" -#include <boost/regex.hpp> +#include <regex> #include <map> typedef std::map<uint16, uint32> AreaFlagByAreaID; @@ -145,7 +145,7 @@ DBCStorage <MovieEntry> sMovieStore(MovieEntryfmt); DBCStorage<NamesProfanityEntry> sNamesProfanityStore(NamesProfanityEntryfmt); DBCStorage<NamesReservedEntry> sNamesReservedStore(NamesReservedEntryfmt); -typedef std::array<std::vector<boost::regex>, TOTAL_LOCALES> NameValidationRegexContainer; +typedef std::array<std::vector<std::wregex>, TOTAL_LOCALES> NameValidationRegexContainer; NameValidationRegexContainer NamesProfaneValidators; NameValidationRegexContainer NamesReservedValidators; @@ -264,7 +264,7 @@ inline void LoadDBC(uint32& availableDbcLocales, StoreProblemList& errors, DBCSt if (FILE* f = fopen(dbcFilename.c_str(), "rb")) { std::ostringstream stream; - stream << dbcFilename << " exists, and has " << storage.GetFieldCount() << " field(s) (expected " << strlen(storage.GetFormat()) << "). Extracted file might be from wrong client version or a database-update has been forgotten."; + stream << dbcFilename << " exists, and has " << storage.GetFieldCount() << " field(s) (expected " << strlen(storage.GetFormat()) << "). Extracted file might be from wrong client version or a database-update has been forgotten. Search on forum for TCE00008 for more info."; std::string buf = stream.str(); errors.push_back(buf); fclose(f); @@ -410,11 +410,14 @@ void LoadDBCStores(const std::string& dataPath) continue; ASSERT(namesProfanity->Language < TOTAL_LOCALES || namesProfanity->Language == -1); + std::wstring wname; + ASSERT(Utf8toWStr(namesProfanity->Name, wname)); + if (namesProfanity->Language != -1) - NamesProfaneValidators[namesProfanity->Language].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + NamesProfaneValidators[namesProfanity->Language].emplace_back(wname, std::regex::icase | std::regex::optimize); else for (uint32 i = 0; i < TOTAL_LOCALES; ++i) - NamesProfaneValidators[i].emplace_back(namesProfanity->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + NamesProfaneValidators[i].emplace_back(wname, std::regex::icase | std::regex::optimize); } for (uint32 i = 0; i < sNamesReservedStore.GetNumRows(); ++i) @@ -424,11 +427,14 @@ void LoadDBCStores(const std::string& dataPath) continue; ASSERT(namesReserved->Language < TOTAL_LOCALES || namesReserved->Language == -1); + std::wstring wname; + ASSERT(Utf8toWStr(namesReserved->Name, wname)); + if (namesReserved->Language != -1) - NamesReservedValidators[namesReserved->Language].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + NamesReservedValidators[namesReserved->Language].emplace_back(wname, std::regex::icase | std::regex::optimize); else for (uint32 i = 0; i < TOTAL_LOCALES; ++i) - NamesReservedValidators[i].emplace_back(namesReserved->Name, boost::regex::perl | boost::regex::icase | boost::regex::optimize); + NamesReservedValidators[i].emplace_back(wname, std::regex::icase | std::regex::optimize); } @@ -1000,18 +1006,18 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u return NULL; } -ResponseCodes ValidateName(std::string const& name, LocaleConstant locale) +ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale) { if (locale >= TOTAL_LOCALES) return RESPONSE_FAILURE; - for (boost::regex const& regex : NamesProfaneValidators[locale]) - if (boost::regex_search(name, regex)) + for (std::wregex const& regex : NamesProfaneValidators[locale]) + if (std::regex_search(name, regex)) return CHAR_NAME_PROFANE; // regexes at TOTAL_LOCALES are loaded from NamesReserved which is not locale specific - for (boost::regex const& regex : NamesReservedValidators[locale]) - if (boost::regex_search(name, regex)) + for (std::wregex const& regex : NamesReservedValidators[locale]) + if (std::regex_search(name, regex)) return CHAR_NAME_RESERVED; return CHAR_NAME_SUCCESS; diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index 00b4141555f..128ff7ad1b8 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -75,7 +75,7 @@ typedef std::unordered_multimap<uint32, SkillRaceClassInfoEntry const*> SkillRac typedef std::pair<SkillRaceClassInfoMap::iterator, SkillRaceClassInfoMap::iterator> SkillRaceClassInfoBounds; TC_GAME_API SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, uint8 class_); -TC_GAME_API ResponseCodes ValidateName(std::string const& name, LocaleConstant locale); +TC_GAME_API ResponseCodes ValidateName(std::wstring const& name, LocaleConstant locale); TC_GAME_API EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender); diff --git a/src/server/game/DataStores/M2Stores.cpp b/src/server/game/DataStores/M2Stores.cpp index 5cff66e6107..69581f16549 100644 --- a/src/server/game/DataStores/M2Stores.cpp +++ b/src/server/game/DataStores/M2Stores.cpp @@ -193,7 +193,7 @@ void LoadM2Cameras(std::string const& dataPath) { if (CinematicCameraEntry const* dbcentry = sCinematicCameraStore.LookupEntry(i)) { - std::string filenameWork = dataPath.c_str(); + std::string filenameWork = dataPath; filenameWork.append(dbcentry->Model); // Replace slashes (always to forward slash, because boost!) diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp index e72859c23e7..4ca1a68c048 100644 --- a/src/server/game/DungeonFinding/LFGMgr.cpp +++ b/src/server/game/DungeonFinding/LFGMgr.cpp @@ -82,19 +82,19 @@ void LFGMgr::_SaveToDB(ObjectGuid guid, uint32 db_guid) if (!guid.IsGroup()) return; - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_LFG_DATA); + SQLTransaction trans = CharacterDatabase.BeginTransaction(); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_LFG_DATA); stmt->setUInt32(0, db_guid); - - CharacterDatabase.Execute(stmt); + trans->Append(stmt); stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_LFG_DATA); stmt->setUInt32(0, db_guid); - stmt->setUInt32(1, GetDungeon(guid)); stmt->setUInt32(2, GetState(guid)); + trans->Append(stmt); - CharacterDatabase.Execute(stmt); + CharacterDatabase.CommitTransaction(trans); } /// Load rewards for completing dungeons @@ -1904,8 +1904,16 @@ bool LFGMgr::AllQueued(GuidList const& check) return false; for (GuidList::const_iterator it = check.begin(); it != check.end(); ++it) - if (GetState(*it) != LFG_STATE_QUEUED) + { + LfgState state = GetState(*it); + if (state != LFG_STATE_QUEUED) + { + if (state != LFG_STATE_PROPOSAL) + TC_LOG_DEBUG("lfg.allqueued", "Unexpected state found while trying to form new group. Guid: %s, State: %s", (*it).ToString().c_str(), GetStateString(state).c_str()); + return false; + } + } return true; } diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 450cf2396a8..4a5f70165fc 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -367,13 +367,13 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender); // Load creature equipment - if (data && data->equipmentId != 0) + if (!data || data->equipmentId == 0) + LoadEquipment(); // use default equipment (if available) + else if (data && data->equipmentId != 0) // override, 0 means no equipment { m_originalEquipmentId = data->equipmentId; LoadEquipment(data->equipmentId); } - else - LoadEquipment(0, true); SetName(normalInfo->Name); // at normal entry always @@ -481,6 +481,9 @@ bool Creature::UpdateEntry(uint32 entry, CreatureData const* data /*= nullptr*/) ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); } + if (cInfo->InhabitType & INHABIT_ROOT) + SetControlled(true, UNIT_STATE_ROOT); + UpdateMovementFlags(); LoadCreaturesAddon(); return true; @@ -863,15 +866,17 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u //! returning correct zone id for selecting OutdoorPvP/Battlefield script Relocate(x, y, z, ang); - if (!CreateFromProto(guidlow, entry, data, vehId)) - return false; - + // Check if the position is valid before calling CreateFromProto(), otherwise we might add Auras to Creatures at + // invalid position, triggering a crash about Auras not removed in the destructor if (!IsPositionValid()) { TC_LOG_ERROR("entities.unit", "Creature::Create(): given coordinates for creature (guidlow %d, entry %d) are not valid (X: %f, Y: %f, Z: %f, O: %f)", guidlow, entry, x, y, z, ang); return false; } + if (!CreateFromProto(guidlow, entry, data, vehId)) + return false; + switch (GetCreatureTemplate()->rank) { case CREATURE_ELITE_RARE: @@ -1823,7 +1828,7 @@ bool Creature::isWorldBoss() const if (IsPet()) return false; - return (GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_BOSS) != 0; + return (GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0; } SpellInfo const* Creature::reachWithSpellAttack(Unit* victim) diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index bb43bcb5ff1..cfe637c4b21 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -22,7 +22,7 @@ #include "Common.h" #include "Unit.h" #include "UpdateMask.h" -#include "ItemPrototype.h" +#include "ItemTemplate.h" #include "LootMgr.h" #include "DatabaseEnv.h" #include "Cell.h" @@ -107,7 +107,7 @@ struct TC_GAME_API CreatureTemplate uint32 unit_flags; // enum UnitFlags mask values uint32 unit_flags2; // enum UnitFlags2 mask values uint32 dynamicflags; - uint32 family; // enum CreatureFamily values (optional) + CreatureFamily family; // enum CreatureFamily values (optional) uint32 trainer_type; uint32 trainer_spell; uint32 trainer_class; @@ -146,11 +146,11 @@ struct TC_GAME_API CreatureTemplate // helpers SkillType GetRequiredLootSkill() const { - if (type_flags & CREATURE_TYPEFLAGS_HERBLOOT) + if (type_flags & CREATURE_TYPE_FLAG_HERB_SKINNING_SKILL) return SKILL_HERBALISM; - else if (type_flags & CREATURE_TYPEFLAGS_MININGLOOT) + else if (type_flags & CREATURE_TYPE_FLAG_MINING_SKINNING_SKILL) return SKILL_MINING; - else if (type_flags & CREATURE_TYPEFLAGS_ENGINEERLOOT) + else if (type_flags & CREATURE_TYPE_FLAG_ENGINEERING_SKINNING_SKILL) return SKILL_ENGINEERING; else return SKILL_SKINNING; // normal case @@ -158,12 +158,12 @@ struct TC_GAME_API CreatureTemplate bool IsExotic() const { - return (type_flags & CREATURE_TYPEFLAGS_EXOTIC) != 0; + return (type_flags & CREATURE_TYPE_FLAG_EXOTIC_PET) != 0; } bool IsTameable(bool canTameExotic) const { - if (type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPEFLAGS_TAMEABLE) == 0) + if (type != CREATURE_TYPE_BEAST || family == CREATURE_FAMILY_NONE || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_PET) == 0) return false; // if can tame exotic then can tame any tameable @@ -294,7 +294,8 @@ enum InhabitTypeValues INHABIT_GROUND = 1, INHABIT_WATER = 2, INHABIT_AIR = 4, - INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR + INHABIT_ROOT = 8, + INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR | INHABIT_ROOT }; // Enums used by StringTextData::Type (CreatureEventAI) @@ -597,6 +598,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma void RemoveCorpse(bool setSpawnTime = true); void DespawnOrUnsummon(uint32 msTimeToDespawn = 0); + void DespawnOrUnsummon(Milliseconds const& time) { DespawnOrUnsummon(uint32(time.count())); } time_t const& GetRespawnTime() const { return m_respawnTime; } time_t GetRespawnTimeEx() const; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 7f922f89572..ba336303a35 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -34,7 +34,7 @@ #include "Transport.h" GameObject::GameObject() : WorldObject(false), MapObject(), - m_model(NULL), m_goValue(), m_AI(NULL) + m_model(nullptr), m_goValue(), m_AI(nullptr) { m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; @@ -49,11 +49,11 @@ GameObject::GameObject() : WorldObject(false), MapObject(), m_usetimes = 0; m_spellId = 0; m_cooldownTime = 0; - m_goInfo = NULL; - m_goData = NULL; + m_goInfo = nullptr; + m_goData = nullptr; + m_packedRotation = 0; m_spawnId = 0; - m_rotation = 0; m_lootRecipientGroup = 0; m_groupLootTimer = 0; @@ -173,16 +173,16 @@ 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(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, G3D::Quat const& rotation, uint32 animprogress, GOState go_state, uint32 artKit /*= 0*/) { ASSERT(map); SetMap(map); - Relocate(x, y, z, ang); - m_stationaryPosition.Relocate(x, y, z, ang); + Relocate(pos); + m_stationaryPosition.Relocate(pos); if (!IsPositionValid()) { - TC_LOG_ERROR("misc", "Gameobject (GUID: %u Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, name_id, x, y); + TC_LOG_ERROR("misc", "Gameobject (GUID: %u Entry: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)", guidlow, name_id, pos.GetPositionX(), pos.GetPositionY()); return false; } @@ -199,7 +199,7 @@ 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: %u Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", guidlow, name_id, map->GetId(), x, y, z); + TC_LOG_ERROR("sql.sql", "Gameobject (GUID: %u Entry: %u) not created: non-existing entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f)", guidlow, name_id, map->GetId(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ()); return false; } @@ -222,10 +222,15 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u return false; } - SetFloatValue(GAMEOBJECT_PARENTROTATION+0, rotation0); - SetFloatValue(GAMEOBJECT_PARENTROTATION+1, rotation1); + SetWorldRotation(rotation); + GameObjectAddon const* gameObjectAddon = sObjectMgr->GetGameObjectAddon(GetSpawnId()); - UpdateRotationFields(rotation2, rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3 + // For most of gameobjects is (0, 0, 0, 1) quaternion, there are only some transports with not standard rotation + G3D::Quat parentRotation; + if (gameObjectAddon) + parentRotation = gameObjectAddon->ParentRotation; + + SetParentRotation(parentRotation); SetObjectScale(goinfo->size); @@ -285,13 +290,10 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, u break; } - if (GameObjectAddon const* addon = sObjectMgr->GetGameObjectAddon(GetSpawnId())) + if (gameObjectAddon && gameObjectAddon->InvisibilityValue) { - if (addon->InvisibilityValue) - { - m_invisibility.AddFlag(addon->invisibilityType); - m_invisibility.AddValue(addon->invisibilityType, addon->InvisibilityValue); - } + m_invisibility.AddFlag(gameObjectAddon->invisibilityType); + m_invisibility.AddValue(gameObjectAddon->invisibilityType, gameObjectAddon->InvisibilityValue); } LastUsedScriptID = GetGOInfo()->ScriptId; @@ -496,7 +498,7 @@ void GameObject::Update(uint32 diff) radius = goInfo->trap.diameter / 2.f; // Pointer to appropriate target if found any - Unit* target = NULL; + Unit* target = nullptr; /// @todo this hack with search required until GO casting not implemented if (Unit* owner = GetOwner()) @@ -511,7 +513,7 @@ void GameObject::Update(uint32 diff) else { // Environmental trap: Any player - Player* player = NULL; + Player* player = nullptr; Trinity::AnyPlayerInObjectRangeCheck checker(this, radius); Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, player, checker); VisitNearbyWorldObject(radius, searcher); @@ -571,8 +573,8 @@ void GameObject::Update(uint32 diff) GameObjectTemplate const* goInfo = GetGOInfo(); if (goInfo->trap.type == 2 && goInfo->trap.spellId) { - /// @todo NULL target won't work for target type 1 - CastSpell(NULL, goInfo->trap.spellId); + /// @todo nullptr target won't work for target type 1 + CastSpell(nullptr, goInfo->trap.spellId); SetLootState(GO_JUST_DEACTIVATED); } else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID)) @@ -780,10 +782,7 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) data.posY = GetPositionY(); data.posZ = GetPositionZ(); data.orientation = GetOrientation(); - data.rotation0 = GetFloatValue(GAMEOBJECT_PARENTROTATION+0); - data.rotation1 = GetFloatValue(GAMEOBJECT_PARENTROTATION+1); - data.rotation2 = GetFloatValue(GAMEOBJECT_PARENTROTATION+2); - data.rotation3 = GetFloatValue(GAMEOBJECT_PARENTROTATION+3); + data.rotation = m_worldRotation; data.spawntimesecs = m_spawnedByDefault ? m_respawnDelayTime : -(int32)m_respawnDelayTime; data.animprogress = GetGoAnimProgress(); data.go_state = GetGoState(); @@ -809,10 +808,10 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask) stmt->setFloat(index++, GetPositionY()); stmt->setFloat(index++, GetPositionZ()); stmt->setFloat(index++, GetOrientation()); - stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION)); - stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+1)); - stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+2)); - stmt->setFloat(index++, GetFloatValue(GAMEOBJECT_PARENTROTATION+3)); + stmt->setFloat(index++, m_worldRotation.x); + stmt->setFloat(index++, m_worldRotation.y); + stmt->setFloat(index++, m_worldRotation.z); + stmt->setFloat(index++, m_worldRotation.w); stmt->setInt32(index++, int32(m_respawnDelayTime)); stmt->setUInt8(index++, GetGoAnimProgress()); stmt->setUInt8(index++, uint8(GetGoState())); @@ -834,22 +833,14 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo uint32 entry = data->id; //uint32 map_id = data->mapid; // already used before call uint32 phaseMask = data->phaseMask; - float x = data->posX; - float y = data->posY; - float z = data->posZ; - float ang = data->orientation; - - float rotation0 = data->rotation0; - float rotation1 = data->rotation1; - float rotation2 = data->rotation2; - float rotation3 = data->rotation3; + Position pos(data->posX, data->posY, data->posZ, data->orientation); uint32 animprogress = data->animprogress; GOState go_state = data->go_state; 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(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, phaseMask, pos, data->rotation, animprogress, go_state, artKit)) return false; if (data->spawntimesecs >= 0) @@ -868,7 +859,7 @@ bool GameObject::LoadGameObjectFromDB(ObjectGuid::LowType spawnId, Map* map, boo m_respawnTime = GetMap()->GetGORespawnTime(m_spawnId); // ready to respawn - if (m_respawnTime && m_respawnTime <= time(NULL)) + if (m_respawnTime && m_respawnTime <= time(nullptr)) { m_respawnTime = 0; GetMap()->RemoveGORespawnTime(m_spawnId); @@ -1093,7 +1084,7 @@ void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target) float range = float(target->GetSpellMaxRangeForTarget(GetOwner(), trapSpell)); // search nearest linked GO - GameObject* trapGO = NULL; + GameObject* trapGO = nullptr; { // using original GO distance CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); @@ -1113,7 +1104,7 @@ void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target) GameObject* GameObject::LookupFishingHoleAround(float range) { - GameObject* ok = NULL; + GameObject* ok = nullptr; CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); Cell cell(p); @@ -1136,7 +1127,7 @@ void GameObject::ResetDoorOrButton() m_cooldownTime = 0; } -void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=NULL*/) +void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=nullptr*/) { if (m_lootState != GO_READY) return; @@ -1160,7 +1151,7 @@ void GameObject::SetGoArtKit(uint8 kit) void GameObject::SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType lowguid) { - const GameObjectData* data = NULL; + const GameObjectData* data = nullptr; if (go) { go->SetGoArtKit(artkit); @@ -1386,7 +1377,7 @@ void GameObject::Use(Unit* user) // cast this spell later if provided spellId = info->goober.spellId; - spellCaster = NULL; + spellCaster = nullptr; break; } @@ -1453,11 +1444,11 @@ void GameObject::Use(Unit* user) TC_LOG_DEBUG("misc", "Fishing check (skill: %i zone min skill: %i chance %i roll: %i", skill, zone_skill, chance, roll); - // but you will likely cause junk in areas that require a high fishing skill (not yet implemented) + player->UpdateFishingSkill(); + + // but you will likely cause junk in areas that require a high fishing skill if (chance >= roll) { - player->UpdateFishingSkill(); - /// @todo I do not understand this hack. Need some explanation. // prevent removing GO at spell cancel RemoveFromOwner(); @@ -1505,7 +1496,7 @@ void GameObject::Use(Unit* user) GameObjectTemplate const* info = GetGOInfo(); - Player* m_ritualOwner = NULL; + Player* m_ritualOwner = nullptr; if (m_ritualOwnerGUID) m_ritualOwner = ObjectAccessor::FindPlayer(m_ritualOwnerGUID); @@ -1869,7 +1860,7 @@ bool GameObject::IsInRange(float x, float y, float z, float radius) const && dz < info->maxZ + radius && dz > info->minZ - radius; } -void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= NULL*/) +void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= nullptr*/) { if (!eventId) return; @@ -1899,37 +1890,41 @@ std::string const & GameObject::GetNameForLocaleIdx(LocaleConstant loc_idx) cons return GetName(); } -void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/) +void GameObject::UpdatePackedRotation() { - static double const atan_pow = atan(pow(2.0f, -20.0f)); - - double f_rot1 = std::sin(GetOrientation() / 2.0f); - double f_rot2 = std::cos(GetOrientation() / 2.0f); + static const int32 PACK_YZ = 1 << 20; + static const int32 PACK_X = PACK_YZ << 1; - int64 i_rot1 = int64(f_rot1 / atan_pow *(f_rot2 >= 0 ? 1.0f : -1.0f)); - int64 rotation = (i_rot1 << 43 >> 43) & 0x00000000001FFFFF; + static const int32 PACK_YZ_MASK = (PACK_YZ << 1) - 1; + static const int32 PACK_X_MASK = (PACK_X << 1) - 1; - //float f_rot2 = std::sin(0.0f / 2.0f); - //int64 i_rot2 = f_rot2 / atan(pow(2.0f, -20.0f)); - //rotation |= (((i_rot2 << 22) >> 32) >> 11) & 0x000003FFFFE00000; - - //float f_rot3 = std::sin(0.0f / 2.0f); - //int64 i_rot3 = f_rot3 / atan(pow(2.0f, -21.0f)); - //rotation |= (i_rot3 >> 42) & 0x7FFFFC0000000000; + int8 w_sign = (m_worldRotation.w >= 0.f ? 1 : -1); + int64 x = int32(m_worldRotation.x * PACK_X) * w_sign & PACK_X_MASK; + int64 y = int32(m_worldRotation.y * PACK_YZ) * w_sign & PACK_YZ_MASK; + int64 z = int32(m_worldRotation.z * PACK_YZ) * w_sign & PACK_YZ_MASK; + m_packedRotation = z | (y << 21) | (x << 42); +} - m_rotation = rotation; +void GameObject::SetWorldRotation(G3D::Quat const& rot) +{ + m_worldRotation = rot.toUnit(); + UpdatePackedRotation(); +} - if (rotation2 == 0.0f && rotation3 == 0.0f) - { - rotation2 = (float)f_rot1; - rotation3 = (float)f_rot2; - } +void GameObject::SetParentRotation(G3D::Quat const& rotation) +{ + SetFloatValue(GAMEOBJECT_PARENTROTATION + 0, rotation.x); + SetFloatValue(GAMEOBJECT_PARENTROTATION + 1, rotation.y); + SetFloatValue(GAMEOBJECT_PARENTROTATION + 2, rotation.z); + SetFloatValue(GAMEOBJECT_PARENTROTATION + 3, rotation.w); +} - SetFloatValue(GAMEOBJECT_PARENTROTATION+2, rotation2); - SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3); +void GameObject::SetWorldRotationAngles(float z_rot, float y_rot, float x_rot) +{ + SetWorldRotation(G3D::Quat(G3D::Matrix3::fromEulerAnglesZYX(z_rot, y_rot, x_rot))); } -void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, uint32 spellId /*= 0*/) +void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/) { if (!m_goValue.Building.MaxHealth || !change) return; @@ -1948,7 +1943,7 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, u // Set the health bar, value = 255 * healthPct; SetGoAnimProgress(m_goValue.Building.Health * 255 / m_goValue.Building.MaxHealth); - Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : NULL; + Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr; // dealing damage, send packet if (player) @@ -1979,7 +1974,7 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, u SetDestructibleState(newState, player, false); } -void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= NULL*/, bool setHealth /*= false*/) +void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= nullptr*/, bool setHealth /*= false*/) { // the user calling this must know he is already operating on destructible gameobject ASSERT(GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING); @@ -2004,7 +1999,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); - uint32 modelId = m_goInfo->building.damagedDisplayId; + uint32 modelId = m_goInfo->displayId; if (DestructibleModelDataEntry const* modelData = sDestructibleModelDataStore.LookupEntry(m_goInfo->building.destructibleData)) if (modelData->DamagedDisplayId) modelId = modelData->DamagedDisplayId; @@ -2032,7 +2027,7 @@ void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED); SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED); - uint32 modelId = m_goInfo->building.destroyedDisplayId; + uint32 modelId = m_goInfo->displayId; if (DestructibleModelDataEntry const* modelData = sDestructibleModelDataStore.LookupEntry(m_goInfo->building.destructibleData)) if (modelData->DestroyedDisplayId) modelId = modelData->DestroyedDisplayId; @@ -2161,14 +2156,14 @@ void GameObject::UpdateModel() Player* GameObject::GetLootRecipient() const { if (!m_lootRecipient) - return NULL; + return nullptr; return ObjectAccessor::FindConnectedPlayer(m_lootRecipient); } Group* GameObject::GetLootRecipientGroup() const { if (!m_lootRecipientGroup) - return NULL; + return nullptr; return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup); } @@ -2176,7 +2171,7 @@ void GameObject::SetLootRecipient(Unit* unit) { // set the player whose group should receive the right // to loot the creature after it dies - // should be set to NULL after the loot disappears + // should be set to nullptr after the loot disappears if (!unit) { @@ -2295,7 +2290,7 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t data->append(fieldBuffer); } -void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = NULL*/) const +void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const { if (m_spawnId) { diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 2b092427178..a271512629b 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -25,6 +25,7 @@ #include "Object.h" #include "LootMgr.h" #include "DatabaseEnv.h" +#include <G3D/Quat.h> class GameObjectAI; class Group; @@ -57,6 +58,7 @@ struct GameObjectTemplate uint32 openTextID; //4 can be used to replace castBarCaption? uint32 closeTextID; //5 uint32 ignoredByPathing; //6 + uint32 conditionID1; //7 } door; //1 GAMEOBJECT_TYPE_BUTTON struct @@ -70,6 +72,7 @@ struct GameObjectTemplate uint32 openTextID; //6 can be used to replace castBarCaption? uint32 closeTextID; //7 uint32 losOK; //8 + uint32 conditionID1; //9 } button; //2 GAMEOBJECT_TYPE_QUESTGIVER struct @@ -84,6 +87,7 @@ struct GameObjectTemplate uint32 losOK; //7 uint32 allowMounted; //8 Is usable while on mount/vehicle. (0/1) uint32 large; //9 + uint32 conditionID1; //10 } questgiver; //3 GAMEOBJECT_TYPE_CHEST struct @@ -105,6 +109,7 @@ struct GameObjectTemplate uint32 openTextID; //14 can be used to replace castBarCaption? uint32 groupLootRules; //15 uint32 floatingTooltip; //16 + uint32 conditionID1; //17 } chest; //4 GAMEOBJECT_TYPE_BINDER - empty //5 GAMEOBJECT_TYPE_GENERIC @@ -116,6 +121,7 @@ struct GameObjectTemplate uint32 large; //3 uint32 floatOnWater; //4 int32 questID; //5 + uint32 conditionID1; //6 } _generic; //6 GAMEOBJECT_TYPE_TRAP struct @@ -135,6 +141,7 @@ struct GameObjectTemplate uint32 openTextID; //12 can be used to replace castBarCaption? uint32 closeTextID; //13 uint32 ignoreTotems; //14 + uint32 conditionID1; //15 } trap; //7 GAMEOBJECT_TYPE_CHAIR struct @@ -143,6 +150,7 @@ struct GameObjectTemplate uint32 height; //1 uint32 onlyCreatorUse; //2 uint32 triggeredEvent; //3 + uint32 conditionID1; //4 } chair; //8 GAMEOBJECT_TYPE_SPELL_FOCUS struct @@ -154,6 +162,8 @@ struct GameObjectTemplate uint32 questID; //4 uint32 large; //5 uint32 floatingTooltip; //6 + uint32 floatOnWater; //7 + uint32 conditionID1; //8 } spellFocus; //9 GAMEOBJECT_TYPE_TEXT struct @@ -162,6 +172,7 @@ struct GameObjectTemplate uint32 language; //1 uint32 pageMaterial; //2 uint32 allowMounted; //3 Is usable while on mount/vehicle. (0/1) + uint32 conditionID1; //4 } text; //10 GAMEOBJECT_TYPE_GOOBER struct @@ -187,6 +198,8 @@ struct GameObjectTemplate uint32 floatingTooltip; //18 uint32 gossipID; //19 uint32 WorldStateSetsState; //20 + uint32 floatOnWater; //21 + uint32 conditionID1; //22 } goober; //11 GAMEOBJECT_TYPE_TRANSPORT struct @@ -196,6 +209,7 @@ struct GameObjectTemplate uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000 uint32 pause1EventID; //3 uint32 pause2EventID; //4 + uint32 mapID; //5 } transport; //12 GAMEOBJECT_TYPE_AREADAMAGE struct @@ -216,6 +230,7 @@ struct GameObjectTemplate uint32 cinematicId; //1 uint32 eventID; //2 uint32 openTextID; //3 can be used to replace castBarCaption? + uint32 conditionID1; //4 } camera; //14 GAMEOBJECT_TYPE_MAPOBJECT - empty //15 GAMEOBJECT_TYPE_MO_TRANSPORT @@ -244,9 +259,14 @@ struct GameObjectTemplate uint32 casterTargetSpellTargets; //5 uint32 castersGrouped; //6 uint32 ritualNoTargetCheck; //7 + uint32 conditionID1; //8 } summoningRitual; - //19 GAMEOBJECT_TYPE_MAILBOX - empty - //20 GAMEOBJECT_TYPE_DONOTUSE - empty + //19 GAMEOBJECT_TYPE_MAILBOX + struct + { + uint32 conditionID1; //0 + } mailbox; + //20 GAMEOBJECT_TYPE_DO_NOT_USE - empty //21 GAMEOBJECT_TYPE_GUARDPOST struct { @@ -261,6 +281,7 @@ struct GameObjectTemplate uint32 partyOnly; //2 uint32 allowMounted; //3 Is usable while on mount/vehicle. (0/1) uint32 large; //4 + uint32 conditionID1; //5 } spellcaster; //23 GAMEOBJECT_TYPE_MEETINGSTONE struct @@ -280,6 +301,7 @@ struct GameObjectTemplate uint32 noDamageImmune; //5 uint32 openTextID; //6 uint32 losOK; //7 + uint32 conditionID1; //8 } flagstand; //25 GAMEOBJECT_TYPE_FISHINGHOLE struct @@ -358,21 +380,21 @@ struct GameObjectTemplate { uint32 intactNumHits; //0 uint32 creditProxyCreature; //1 - uint32 state1Name; //2 + uint32 empty1; //2 uint32 intactEvent; //3 - uint32 damagedDisplayId; //4 + uint32 empty2; //4 uint32 damagedNumHits; //5 uint32 empty3; //6 uint32 empty4; //7 uint32 empty5; //8 uint32 damagedEvent; //9 - uint32 destroyedDisplayId; //10 + uint32 empty6; //10 uint32 empty7; //11 uint32 empty8; //12 uint32 empty9; //13 uint32 destroyedEvent; //14 uint32 empty10; //15 - uint32 debuildingTimeSecs; //16 + uint32 rebuildingTimeSecs; //16 uint32 empty11; //17 uint32 destructibleData; //18 uint32 rebuildingEvent; //19 @@ -381,7 +403,11 @@ struct GameObjectTemplate uint32 damageEvent; //22 uint32 empty14; //23 } building; - //34 GAMEOBJECT_TYPE_GUILDBANK - empty + //34 GAMEOBJECT_TYPE_GUILDBANK + struct + { + uint32 conditionID1; //0 + } guildbank; //35 GAMEOBJECT_TYPE_TRAPDOOR struct { @@ -578,6 +604,7 @@ struct GameObjectLocale // `gameobject_addon` table struct GameObjectAddon { + G3D::Quat ParentRotation; InvisibilityType invisibilityType; uint32 InvisibilityValue; }; @@ -597,8 +624,7 @@ enum GOState // from `gameobject` struct GameObjectData { - explicit GameObjectData() : id(0), mapid(0), phaseMask(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), - rotation0(0.0f), rotation1(0.0f), rotation2(0.0f), rotation3(0.0f), spawntimesecs(0), + explicit GameObjectData() : id(0), mapid(0), phaseMask(0), posX(0.0f), posY(0.0f), posZ(0.0f), orientation(0.0f), spawntimesecs(0), animprogress(0), go_state(GO_STATE_ACTIVE), spawnMask(0), artKit(0), dbData(true) { } uint32 id; // entry in gamobject_template uint16 mapid; @@ -607,10 +633,7 @@ struct GameObjectData float posY; float posZ; float orientation; - float rotation0; - float rotation1; - float rotation2; - float rotation3; + G3D::Quat rotation; int32 spawntimesecs; uint32 animprogress; GOState go_state; @@ -652,7 +675,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(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, uint32 phaseMask, Position const& pos, G3D::Quat const& rotation, 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; } @@ -664,7 +687,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> ObjectGuid::LowType GetSpawnId() const { return m_spawnId; } - void UpdateRotationFields(float rotation2 = 0.0f, float rotation3 = 0.0f); + // z_rot, y_rot, x_rot - rotation angles around z, y and x axes + void SetWorldRotationAngles(float z_rot, float y_rot, float x_rot); + void SetWorldRotation(G3D::Quat const& rot); + void SetParentRotation(G3D::Quat const& rotation); // transforms(rotates) transport's path + int64 GetPackedWorldRotation() const { return m_packedRotation; } // overwrite WorldObject function for proper name localization std::string const& GetNameForLocaleIdx(LocaleConstant locale_idx) const override; @@ -822,7 +849,6 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void EventInform(uint32 eventId, WorldObject* invoker = NULL); - uint64 GetRotation() const { return m_rotation; } virtual uint32 GetScriptId() const { return GetGOInfo()->ScriptId; } GameObjectAI* AI() const { return m_AI; } @@ -877,7 +903,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> GameObjectData const* m_goData; GameObjectValue m_goValue; - uint64 m_rotation; + int64 m_packedRotation; + G3D::Quat m_worldRotation; Position m_stationaryPosition; ObjectGuid m_lootRecipient; @@ -886,6 +913,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> private: void RemoveFromOwner(); void SwitchDoorOrButton(bool activate, bool alternative = false); + void UpdatePackedRotation(); //! Object distance/size - overridden from Object::_IsWithinDist. Needs to take in account proper GO size. bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool /*is3D*/) const override diff --git a/src/server/game/Entities/Item/Container/Bag.h b/src/server/game/Entities/Item/Container/Bag.h index 89615f94e52..8f84dc3e5b3 100644 --- a/src/server/game/Entities/Item/Container/Bag.h +++ b/src/server/game/Entities/Item/Container/Bag.h @@ -23,7 +23,7 @@ #define MAX_BAG_SIZE 36 // 2.0.12 #include "Item.h" -#include "ItemPrototype.h" +#include "ItemTemplate.h" class TC_GAME_API Bag : public Item { diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 8658200c28c..5e00a816cab 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -22,7 +22,7 @@ #include "Common.h" #include "Object.h" #include "LootMgr.h" -#include "ItemPrototype.h" +#include "ItemTemplate.h" #include "DatabaseEnv.h" class SpellInfo; diff --git a/src/server/game/Entities/Item/ItemPrototype.cpp b/src/server/game/Entities/Item/ItemTemplate.cpp index 3bb7b128b08..1a8bc2291ae 100644 --- a/src/server/game/Entities/Item/ItemPrototype.cpp +++ b/src/server/game/Entities/Item/ItemTemplate.cpp @@ -15,23 +15,23 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "ItemPrototype.h" +#include "ItemTemplate.h" bool ItemTemplate::CanChangeEquipStateInCombat() const { switch (InventoryType) { - case INVTYPE_RELIC: - case INVTYPE_SHIELD: - case INVTYPE_HOLDABLE: - return true; + case INVTYPE_RELIC: + case INVTYPE_SHIELD: + case INVTYPE_HOLDABLE: + return true; } switch (Class) { - case ITEM_CLASS_WEAPON: - case ITEM_CLASS_PROJECTILE: - return true; + case ITEM_CLASS_WEAPON: + case ITEM_CLASS_PROJECTILE: + return true; } return false; @@ -40,82 +40,85 @@ bool ItemTemplate::CanChangeEquipStateInCombat() const float ItemTemplate::getDPS() const { - if (Delay == 0) - return 0; - float temp = 0; - for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) + if (!Delay) + return 0.f; + + float temp = 0.f; + for (uint8 i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) temp += Damage[i].DamageMin + Damage[i].DamageMax; - return temp * 500 / Delay; -} + return temp * 500.f / Delay; +} int32 ItemTemplate::getFeralBonus(int32 extraDPS /*= 0*/) const { // 0x02A5F3 - is mask for Melee weapon from ItemSubClassMask.dbc if (Class == ITEM_CLASS_WEAPON && (1 << SubClass) & 0x02A5F3) { - int32 bonus = int32((extraDPS + getDPS())*14.0f) - 767; + int32 bonus = int32((extraDPS + getDPS()) * 14.0f) - 767; if (bonus < 0) return 0; return bonus; } + return 0; } float ItemTemplate::GetItemLevelIncludingQuality() const { - float itemLevel = (float)ItemLevel; + float itemLevel(ItemLevel); switch (Quality) { - case ITEM_QUALITY_POOR: - case ITEM_QUALITY_NORMAL: - case ITEM_QUALITY_UNCOMMON: - case ITEM_QUALITY_ARTIFACT: - case ITEM_QUALITY_HEIRLOOM: - itemLevel -= 13; // leaving this as a separate statement since we do not know the real behavior in this case - break; - case ITEM_QUALITY_RARE: - itemLevel -= 13; - break; - case ITEM_QUALITY_EPIC: - case ITEM_QUALITY_LEGENDARY: - default: - break; + case ITEM_QUALITY_POOR: + case ITEM_QUALITY_NORMAL: + case ITEM_QUALITY_UNCOMMON: + case ITEM_QUALITY_ARTIFACT: + case ITEM_QUALITY_HEIRLOOM: + itemLevel -= 13.f; // leaving this as a separate statement since we do not know the real behavior in this case + break; + case ITEM_QUALITY_RARE: + itemLevel -= 13.f; + break; + case ITEM_QUALITY_EPIC: + case ITEM_QUALITY_LEGENDARY: + default: + break; } + return std::max<float>(0.f, itemLevel); } uint32 ItemTemplate::GetSkill() const { - const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] = + static uint32 const itemWeaponSkills[MAX_ITEM_SUBCLASS_WEAPON] = { - SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES, - SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0, - SKILL_STAVES, 0, 0, SKILL_FIST_WEAPONS, 0, - SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS, + SKILL_AXES, SKILL_2H_AXES, SKILL_BOWS, SKILL_GUNS, SKILL_MACES, + SKILL_2H_MACES, SKILL_POLEARMS, SKILL_SWORDS, SKILL_2H_SWORDS, 0, + SKILL_STAVES, 0, 0, SKILL_FIST_WEAPONS, 0, + SKILL_DAGGERS, SKILL_THROWN, SKILL_ASSASSINATION, SKILL_CROSSBOWS, SKILL_WANDS, SKILL_FISHING }; - const static uint32 item_armor_skills[MAX_ITEM_SUBCLASS_ARMOR] = + static uint32 const itemArmorSkills[MAX_ITEM_SUBCLASS_ARMOR] = { 0, SKILL_CLOTH, SKILL_LEATHER, SKILL_MAIL, SKILL_PLATE_MAIL, 0, SKILL_SHIELD, 0, 0, 0, 0 }; switch (Class) { - case ITEM_CLASS_WEAPON: - if (SubClass >= MAX_ITEM_SUBCLASS_WEAPON) - return 0; - else - return item_weapon_skills[SubClass]; + case ITEM_CLASS_WEAPON: + if (SubClass >= MAX_ITEM_SUBCLASS_WEAPON) + return 0; + else + return itemWeaponSkills[SubClass]; - case ITEM_CLASS_ARMOR: - if (SubClass >= MAX_ITEM_SUBCLASS_ARMOR) - return 0; - else - return item_armor_skills[SubClass]; + case ITEM_CLASS_ARMOR: + if (SubClass >= MAX_ITEM_SUBCLASS_ARMOR) + return 0; + else + return itemArmorSkills[SubClass]; - default: - return 0; + default: + return 0; } } diff --git a/src/server/game/Entities/Item/ItemPrototype.h b/src/server/game/Entities/Item/ItemTemplate.h index 9b0f4628364..0ff3f00a08b 100644 --- a/src/server/game/Entities/Item/ItemPrototype.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -107,20 +107,20 @@ enum ItemBondingType enum ItemProtoFlags { - ITEM_PROTO_FLAG_UNK1 = 0x00000001, // ? + ITEM_PROTO_FLAG_NO_PICKUP = 0x00000001, // ? ITEM_PROTO_FLAG_CONJURED = 0x00000002, // Conjured item - ITEM_PROTO_FLAG_OPENABLE = 0x00000004, // Item can be right clicked to open for loot + ITEM_PROTO_FLAG_HAS_LOOT = 0x00000004, // Item can be right clicked to open for loot ITEM_PROTO_FLAG_HEROIC = 0x00000008, // Makes green "Heroic" text appear on item ITEM_PROTO_FLAG_DEPRECATED = 0x00000010, // Cannot equip or use ITEM_PROTO_FLAG_INDESTRUCTIBLE = 0x00000020, // Item can not be destroyed, except by using spell (item can be reagent for spell) - ITEM_PROTO_FLAG_UNK2 = 0x00000040, // ? + ITEM_PROTO_FLAG_PLAYER_CAST = 0x00000040, // Item's spells are castable by players ITEM_PROTO_FLAG_NO_EQUIP_COOLDOWN = 0x00000080, // No default 30 seconds cooldown when equipped - ITEM_PROTO_FLAG_UNK3 = 0x00000100, // ? - ITEM_PROTO_FLAG_WRAPPER = 0x00000200, // Item can wrap other items - ITEM_PROTO_FLAG_UNK4 = 0x00000400, // ? - ITEM_PROTO_FLAG_PARTY_LOOT = 0x00000800, // Looting this item does not remove it from available loot + ITEM_PROTO_FLAG_INT_BONUS_INSTEAD = 0x00000100, // ? + ITEM_PROTO_FLAG_IS_WRAPPER = 0x00000200, // Item can wrap other items + ITEM_PROTO_FLAG_USES_RESOURCES = 0x00000400, // ? + ITEM_PROTO_FLAG_MULTI_DROP = 0x00000800, // Looting this item does not remove it from available loot ITEM_PROTO_FLAG_REFUNDABLE = 0x00001000, // Item can be returned to vendor for its original cost (extended cost) - ITEM_PROTO_FLAG_CHARTER = 0x00002000, // Item is guild or arena charter + ITEM_PROTO_FLAG_PETITION = 0x00002000, // Item is guild or arena charter ITEM_PROTO_FLAG_UNK5 = 0x00004000, // Only readable items have this (but not all) ITEM_PROTO_FLAG_UNK6 = 0x00008000, // ? ITEM_PROTO_FLAG_UNK7 = 0x00010000, // ? @@ -537,12 +537,7 @@ inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemS return 0; } -// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push, N), also any gcc version not support it at some platform -#if defined(__GNUC__) -#pragma pack(1) -#else #pragma pack(push, 1) -#endif struct _Damage { @@ -573,12 +568,7 @@ struct _Socket uint32 Content; }; -// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform -#if defined(__GNUC__) -#pragma pack() -#else #pragma pack(pop) -#endif #define MAX_ITEM_PROTO_DAMAGES 2 // changed in 3.1.0 #define MAX_ITEM_PROTO_SOCKETS 3 diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index a2f519a681c..20b88c5e3a9 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -480,7 +480,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const // 0x200 if (flags & UPDATEFLAG_ROTATION) - *data << int64(ToGameObject()->GetRotation()); + *data << int64(ToGameObject()->GetPackedWorldRotation()); } void Object::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const @@ -1154,9 +1154,13 @@ bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const if (!IsInMap(obj)) return false; - float ox, oy, oz; - obj->GetPosition(ox, oy, oz); - return IsWithinLOS(ox, oy, oz); + float x, y, z; + if (obj->GetTypeId() == TYPEID_PLAYER) + obj->GetPosition(x, y, z); + else + obj->GetHitSpherePointFor(GetPosition(), x, y, z); + + return IsWithinLOS(x, y, z); } float WorldObject::GetDistance(const WorldObject* obj) const @@ -1240,11 +1244,36 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const VMAP::IVMapManager* vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);*/ if (IsInWorld()) - return GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ()+2.f, ox, oy, oz+2.f, GetPhaseMask()); + { + float x, y, z; + if (GetTypeId() == TYPEID_PLAYER) + GetPosition(x, y, z); + else + GetHitSpherePointFor({ ox, oy, oz }, x, y, z); + + return GetMap()->isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhaseMask()); + } return true; } +Position WorldObject::GetHitSpherePointFor(Position const& dest) const +{ + G3D::Vector3 vThis(GetPositionX(), GetPositionY(), GetPositionZ()); + G3D::Vector3 vObj(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ()); + G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * GetObjectSize(); + + return Position(contactPoint.x, contactPoint.y, contactPoint.z, GetAngle(contactPoint.x, contactPoint.y)); +} + +void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const +{ + Position pos = GetHitSpherePointFor(dest); + x = pos.GetPositionX(); + y = pos.GetPositionY(); + z = pos.GetPositionZ(); +} + bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D /* = true */) const { float dx1 = GetPositionX() - obj1->GetPositionX(); @@ -1374,7 +1403,8 @@ void WorldObject::GetRandomPoint(const Position &pos, float distance, float &ran // angle to face `obj` to `this` float angle = (float)rand_norm()*static_cast<float>(2*M_PI); - float new_dist = (float)rand_norm()*static_cast<float>(distance); + float new_dist = (float)rand_norm() + (float)rand_norm(); + new_dist = distance * (new_dist > 1 ? new_dist - 2 : new_dist); rand_x = pos.m_positionX + new_dist * std::cos(angle); rand_y = pos.m_positionY + new_dist * std::sin(angle); @@ -1416,7 +1446,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const bool canSwim = ToCreature()->CanSwim(); float ground_z = z; float max_z = canSwim - ? GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)) + ? GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)) : ((ground_z = GetMap()->GetHeight(GetPhaseMask(), x, y, z, true))); if (max_z > INVALID_HEIGHT) { @@ -1440,7 +1470,7 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const if (!ToPlayer()->CanFly()) { float ground_z = z; - float max_z = GetMap()->GetWaterOrGroundLevel(x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)); + float max_z = GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), x, y, z, &ground_z, !ToUnit()->HasAuraType(SPELL_AURA_WATER_WALK)); if (max_z > INVALID_HEIGHT) { if (z > max_z) @@ -1951,7 +1981,7 @@ void WorldObject::ClearZoneScript() m_zoneScript = NULL; } -TempSummon* WorldObject::SummonCreature(uint32 entry, const Position &pos, TempSummonType spwtype, uint32 duration, uint32 /*vehId*/) const +TempSummon* WorldObject::SummonCreature(uint32 entry, Position const& pos, TempSummonType spwtype /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 duration /*= 0*/, uint32 /*vehId = 0*/) const { if (Map* map = FindMap()) { @@ -1962,7 +1992,7 @@ TempSummon* WorldObject::SummonCreature(uint32 entry, const Position &pos, TempS } } - return NULL; + return nullptr; } TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, float ang /*= 0*/, TempSummonType spwtype /*= TEMPSUMMON_MANUAL_DESPAWN*/, uint32 despwtime /*= 0*/) const @@ -1972,29 +2002,30 @@ TempSummon* WorldObject::SummonCreature(uint32 id, float x, float y, float z, fl GetClosePoint(x, y, z, GetObjectSize()); ang = GetOrientation(); } + Position pos; pos.Relocate(x, y, z, ang); return SummonCreature(id, pos, spwtype, despwtime, 0); } -GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime) +GameObject* WorldObject::SummonGameObject(uint32 entry, Position const& pos, G3D::Quat const& rot, uint32 respawnTime) { if (!IsInWorld()) - return NULL; + return nullptr; GameObjectTemplate const* goinfo = sObjectMgr->GetGameObjectTemplate(entry); if (!goinfo) { TC_LOG_ERROR("sql.sql", "Gameobject template %u not found in database!", entry); - return NULL; + return nullptr; } 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(map->GenerateLowGuid<HighGuid::GameObject>(), entry, map, GetPhaseMask(), pos, rot, 255, GO_STATE_READY)) { delete go; - return NULL; + return nullptr; } go->SetRespawnTime(respawnTime); @@ -2007,6 +2038,18 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float return go; } +GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float z, float ang, G3D::Quat const& rot, uint32 respawnTime) +{ + if (!x && !y && !z) + { + GetClosePoint(x, y, z, GetObjectSize()); + ang = GetOrientation(); + } + + Position pos(x, y, z, ang); + return SummonGameObject(entry, pos, rot, respawnTime); +} + Creature* WorldObject::SummonTrigger(float x, float y, float z, float ang, uint32 duration, CreatureAI* (*GetAI)(Creature*)) { TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN; @@ -2038,7 +2081,10 @@ void WorldObject::SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list std::vector<TempSummonData> const* data = sObjectMgr->GetSummonGroup(GetEntry(), GetTypeId() == TYPEID_GAMEOBJECT ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE, group); if (!data) + { + TC_LOG_WARN("scripts", "%s (%s) tried to summon non-existing summon group %u.", GetName().c_str(), GetGUID().ToString().c_str(), group); return; + } for (std::vector<TempSummonData>::const_iterator itr = data->begin(); itr != data->end(); ++itr) if (TempSummon* summon = SummonCreature(itr->entry, itr->pos, itr->type, itr->time)) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index f406b354293..87b158ab81a 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -488,6 +488,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true) const; bool IsWithinLOS(float x, float y, float z) const; bool IsWithinLOSInMap(WorldObject const* obj) const; + Position GetHitSpherePointFor(Position const& dest) const; + void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const; bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const; bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const; bool IsInRange2d(float x, float y, float minRange, float maxRange) const; @@ -540,9 +542,10 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation void ClearZoneScript(); ZoneScript* GetZoneScript() const { return m_zoneScript; } - TempSummon* SummonCreature(uint32 id, Position const &pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0) const; + TempSummon* SummonCreature(uint32 id, Position const& pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0) const; TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0) const; - GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime /* s */); + GameObject* SummonGameObject(uint32 entry, Position const& pos, G3D::Quat const& rot, uint32 respawnTime /* s */); + GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, G3D::Quat const& rot, uint32 respawnTime /* s */); Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, CreatureAI* (*GetAI)(Creature*) = NULL); void SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list = NULL); @@ -550,8 +553,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation GameObject* FindNearestGameObject(uint32 entry, float range) const; GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const; - void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange) const; - void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange) const; + void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const; + void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const; void GetPlayerListInGrid(std::list<Player*>& lList, float fMaxSearchRange) const; void DestroyForNearbyPlayers(); diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp index 37fb23b38f5..482fcb315f2 100644 --- a/src/server/game/Entities/Pet/Pet.cpp +++ b/src/server/game/Entities/Pet/Pet.cpp @@ -329,6 +329,9 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c _LoadSpellCooldowns(); LearnPetPassives(); InitLevelupSpellsForLevel(); + if (map->IsBattleArena()) + RemoveArenaAuras(); + CastPetAuras(current); } @@ -748,7 +751,7 @@ void Pet::GivePetLevel(uint8 level) if (!level || level == getLevel()) return; - if (getPetType()==HUNTER_PET) + if (getPetType() == HUNTER_PET) { SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, uint32(sObjectMgr->GetXPForLevel(level)*PET_XP_FACTOR)); diff --git a/src/server/game/Entities/Player/CinematicMgr.cpp b/src/server/game/Entities/Player/CinematicMgr.cpp index 07bf733c9ff..cc5a62300ad 100644 --- a/src/server/game/Entities/Player/CinematicMgr.cpp +++ b/src/server/game/Entities/Player/CinematicMgr.cpp @@ -27,6 +27,7 @@ CinematicMgr::CinematicMgr(Player* playerref) m_cinematicDiff = 0; m_lastCinematicCheck = 0; m_activeCinematicCameraId = 0; + m_cinematicLength = 0; m_cinematicCamera = nullptr; m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f); m_CinematicObject = nullptr; @@ -70,6 +71,8 @@ void CinematicMgr::BeginCinematic() FlyByCameraCollection::const_reverse_iterator camrevitr = m_cinematicCamera->rbegin(); if (camrevitr != m_cinematicCamera->rend()) m_cinematicLength = camrevitr->timeStamp; + else + m_cinematicLength = 0; } } } diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 9ed101bf52e..bc95ea1107e 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -567,6 +567,7 @@ Player::~Player() delete m_runes; delete m_achievementMgr; delete m_reputationMgr; + delete _cinematicMgr; sWorld->DecreasePlayerCount(); } @@ -1710,7 +1711,7 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) // Pets info uint32 petDisplayId = 0; uint32 petLevel = 0; - uint32 petFamily = 0; + CreatureFamily petFamily = CREATURE_FAMILY_NONE; // show pet at selection character in character list only for non-ghost character if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (plrClass == CLASS_WARLOCK || plrClass == CLASS_HUNTER || plrClass == CLASS_DEATH_KNIGHT)) @@ -2418,11 +2419,11 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflag return nullptr; // Deathstate checks - if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_GHOST)) + if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_GHOST_VISIBLE)) return nullptr; // alive or spirit healer - if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_DEAD_INTERACT)) + if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD)) return nullptr; // appropriate npc type @@ -2793,6 +2794,8 @@ void Player::GiveLevel(uint8 level) if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up UpdateSkillsToMaxSkillsForLevel(); + _ApplyAllLevelScaleItemMods(true); + // set current level health and mana/energy to maximum after applying all mods. SetFullHealth(); SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); @@ -2802,8 +2805,6 @@ void Player::GiveLevel(uint8 level) SetPower(POWER_FOCUS, 0); SetPower(POWER_HAPPINESS, 0); - _ApplyAllLevelScaleItemMods(true); - // update level to hunter/summon pet if (Pet* pet = GetPet()) pet->SynchronizeLevelWithOwner(); @@ -2996,7 +2997,7 @@ void Player::InitStatsForLevel(bool reapplyMods) // cleanup unit flags (will be re-applied if need at aura load). RemoveFlag(UNIT_FIELD_FLAGS, - UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 | + UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_LOOTING | UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED | @@ -3201,10 +3202,9 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) } } - PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; PlayerTalent* newtalent = new PlayerTalent(); - newtalent->state = state; + newtalent->state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; newtalent->spec = spec; (*m_talents[spec])[spellId] = newtalent; @@ -4211,7 +4211,7 @@ void Player::DeleteFromDB(ObjectGuid playerguid, uint32 accountId, bool updateRe // close player ticket if any GmTicket* ticket = sTicketMgr->GetTicketByPlayer(playerguid); if (ticket) - ticket->SetClosedBy(playerguid); + sTicketMgr->CloseTicket(ticket->GetId(), playerguid); // remove from arena teams LeaveAllArenaTeams(playerguid); @@ -4940,6 +4940,9 @@ void Player::DurabilityPointsLossAll(int32 points, bool inventory) void Player::DurabilityPointsLoss(Item* item, int32 points) { + if (HasAuraType(SPELL_AURA_PREVENT_DURABILITY_LOSS)) + return; + int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY); int32 pNewDurability = pOldDurability - points; @@ -5173,71 +5176,67 @@ void Player::UpdateLocalChannels(uint32 newZone) return; std::string current_zone_name = current_zone->area_name[GetSession()->GetSessionDbcLocale()]; - for (uint32 i = 0; i < sChatChannelsStore.GetNumRows(); ++i) { - if (ChatChannelsEntry const* channel = sChatChannelsStore.LookupEntry(i)) - { - Channel* usedChannel = nullptr; + ChatChannelsEntry const* channelEntry = sChatChannelsStore.LookupEntry(i); + if (!channelEntry) + continue; - for (JoinedChannelsList::iterator itr = m_channels.begin(); itr != m_channels.end(); ++itr) + Channel* usedChannel = nullptr; + for (Channel* channel : m_channels) + { + if (channel->GetChannelId() == i) { - if ((*itr)->GetChannelId() == i) - { - usedChannel = *itr; - break; - } + usedChannel = channel; + break; } + } - Channel* removeChannel = nullptr; - Channel* joinChannel = nullptr; - bool sendRemove = true; + Channel* removeChannel = nullptr; + Channel* joinChannel = nullptr; + bool sendRemove = true; - if (CanJoinConstantChannelInZone(channel, current_zone)) + if (CanJoinConstantChannelInZone(channelEntry, current_zone)) + { + if (!(channelEntry->flags & CHANNEL_DBC_FLAG_GLOBAL)) { - if (!(channel->flags & CHANNEL_DBC_FLAG_GLOBAL)) - { - if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY && usedChannel) - continue; // Already on the channel, as city channel names are not changing - - char new_channel_name_buf[100]; - char const* currentNameExt; - - if (channel->flags & CHANNEL_DBC_FLAG_CITY_ONLY) - currentNameExt = sObjectMgr->GetTrinityStringForDBCLocale(LANG_CHANNEL_CITY); - else - currentNameExt = current_zone_name.c_str(); + if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY && usedChannel) + continue; // Already on the channel, as city channel names are not changing - snprintf(new_channel_name_buf, 100, channel->pattern[m_session->GetSessionDbcLocale()], currentNameExt); + std::string currentNameExt; + if (channelEntry->flags & CHANNEL_DBC_FLAG_CITY_ONLY) + currentNameExt = sObjectMgr->GetTrinityStringForDBCLocale(LANG_CHANNEL_CITY); + else + currentNameExt = current_zone_name; - joinChannel = cMgr->GetJoinChannel(new_channel_name_buf, channel->ChannelID); - if (usedChannel) + std::string newChannelName = Trinity::StringFormat(channelEntry->pattern[m_session->GetSessionDbcLocale()], currentNameExt.c_str()); + joinChannel = cMgr->GetJoinChannel(newChannelName, channelEntry->ChannelID); + if (usedChannel) + { + if (joinChannel != usedChannel) { - if (joinChannel != usedChannel) - { - removeChannel = usedChannel; - sendRemove = false; // Do not send leave channel, it already replaced at client - } - else - joinChannel = nullptr; + removeChannel = usedChannel; + sendRemove = false; // Do not send leave channel, it already replaced at client } + else + joinChannel = nullptr; } - else - joinChannel = cMgr->GetJoinChannel(channel->pattern[m_session->GetSessionDbcLocale()], channel->ChannelID); } else - removeChannel = usedChannel; + joinChannel = cMgr->GetJoinChannel(channelEntry->pattern[m_session->GetSessionDbcLocale()], channelEntry->ChannelID); + } + else + removeChannel = usedChannel; - if (joinChannel) - joinChannel->JoinChannel(this, ""); // Changed Channel: ... or Joined Channel: ... + if (joinChannel) + joinChannel->JoinChannel(this, ""); // Changed Channel: ... or Joined Channel: ... - if (removeChannel) - { - removeChannel->LeaveChannel(this, sendRemove); // Leave old channel - std::string name = removeChannel->GetName(); // Store name, (*i)erase in LeftChannel - LeftChannel(removeChannel); // Remove from player's channel list - cMgr->LeftChannel(name); // Delete if empty - } + if (removeChannel) + { + removeChannel->LeaveChannel(this, sendRemove); // Leave old channel + std::string name = removeChannel->GetName(); // Store name, (*i)erase in LeftChannel + LeftChannel(removeChannel); // Remove from player's channel list + cMgr->LeftChannel(name); // Delete if empty } } } @@ -6254,14 +6253,14 @@ bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) co case ACTION_BUTTON_SPELL: if (!sSpellMgr->GetSpellInfo(action)) { - TC_LOG_ERROR("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): spell not exist", + TC_LOG_DEBUG("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): spell does not exist. This can be due to a character imported from a different expansion", action, button, GetName().c_str(), GetGUID().ToString().c_str()); return false; } if (!HasSpell(action)) { - TC_LOG_ERROR("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): player don't known this spell", + TC_LOG_DEBUG("entities.player", "Player::IsActionButtonDataValid: Spell action %u not added into button %u for player %s (%s): player does not known this spell, this can be due to a player changing their talents", action, button, GetName().c_str(), GetGUID().ToString().c_str()); return false; } @@ -7207,6 +7206,13 @@ void Player::DuelComplete(DuelCompleteType type) if (!duel) return; + // Check if DuelComplete() has been called already up in the stack and in that case don't do anything else here + if (duel->isCompleted || ASSERT_NOTNULL(duel->opponent->duel)->isCompleted) + return; + + duel->isCompleted = true; + duel->opponent->duel->isCompleted = true; + TC_LOG_DEBUG("entities.unit", "Player::DuelComplete: Player '%s' (%s), Opponent: '%s' (%s)", GetName().c_str(), GetGUID().ToString().c_str(), duel->opponent->GetName().c_str(), duel->opponent->GetGUID().ToString().c_str()); @@ -12141,7 +12147,8 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update) // remove item dependent auras and casts (only weapon and armor slots) if (slot < EQUIPMENT_SLOT_END) { - RemoveItemDependentAurasAndCasts(pItem); + if (update) + RemoveItemDependentAurasAndCasts(pItem); // remove held enchantments, update expertise if (slot == EQUIPMENT_SLOT_MAINHAND) @@ -12320,7 +12327,7 @@ void Player::DestroyItem(uint8 bag, uint8 slot, bool update) // Delete rolled money / loot from db. // MUST be done before RemoveFromWorld() or GetTemplate() fails if (ItemTemplate const* pTmp = pItem->GetTemplate()) - if (pTmp->Flags & ITEM_PROTO_FLAG_OPENABLE) + if (pTmp->Flags & ITEM_PROTO_FLAG_HAS_LOOT) pItem->ItemContainerDeleteLootMoneyAndLootItemsFromDB(); if (IsInWorld() && update) @@ -14003,8 +14010,11 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool break; case GOSSIP_OPTION_TRAINER: if (getClass() != creature->GetCreatureTemplate()->trainer_class && creature->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS) + { TC_LOG_ERROR("sql.sql", "GOSSIP_OPTION_TRAINER:: Player %s (GUID: %u) requested wrong gossip menu: %u with wrong class: %u at Creature: %s (Entry: %u, Trainer Class: %u)", - GetName().c_str(), GetGUID().GetCounter(), menu->GetGossipMenu().GetMenuId(), getClass(), creature->GetName().c_str(), creature->GetEntry(), creature->GetCreatureTemplate()->trainer_class); + GetName().c_str(), GetGUID().GetCounter(), menu->GetGossipMenu().GetMenuId(), getClass(), creature->GetName().c_str(), creature->GetEntry(), creature->GetCreatureTemplate()->trainer_class); + canTalk = false; + } // no break; case GOSSIP_OPTION_GOSSIP: case GOSSIP_OPTION_SPIRITGUIDE: @@ -14808,7 +14818,7 @@ void Player::AddQuest(Quest const* quest, Object* questGiver) StartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, quest_id); - SendQuestUpdate(quest_id); + SendQuestUpdate(); if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled { @@ -14835,12 +14845,8 @@ void Player::CompleteQuest(uint32 quest_id) SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE); if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id)) - { if (qInfo->HasFlag(QUEST_FLAGS_TRACKING)) RewardQuest(qInfo, 0, this, false); - else - SendQuestComplete(quest_id); - } } if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled @@ -14876,15 +14882,23 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, uint32 quest_id = quest->GetQuestId(); for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) - if (quest->RequiredItemId[i]) - DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true); - + { + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i])) + { + if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->RequiredItemId[i], 9999, true, true); + else + DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true); + } + } for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) { - if (quest->ItemDrop[i]) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) { - uint32 count = quest->ItemDropQuantity[i]; - DestroyItemCount(quest->ItemDrop[i], count ? count : 9999, true); + if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->ItemDrop[i], 9999, true, true); + else + DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true); } } @@ -14980,7 +14994,10 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, { /// @todo Poor design of mail system SQLTransaction trans = CharacterDatabase.BeginTransaction(); - MailDraft(mail_template_id).SendMailTo(trans, this, questGiver, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs()); + if (uint32 questMailSender = quest->GetRewMailSenderEntry()) + MailDraft(mail_template_id).SendMailTo(trans, this, questMailSender, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs()); + else + MailDraft(mail_template_id).SendMailTo(trans, this, questGiver, MAIL_CHECK_MASK_HAS_BODY, quest->GetRewMailDelaySecs()); CharacterDatabase.CommitTransaction(trans); } @@ -15001,8 +15018,11 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, SetSeasonalQuestStatus(quest_id); RemoveActiveQuest(quest_id, false); - m_RewardedQuests.insert(quest_id); - m_RewardedQuestsSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE; + if (quest->CanIncreaseRewardedQuestCounters()) + { + m_RewardedQuests.insert(quest_id); + m_RewardedQuestsSave[quest_id] = QUEST_DEFAULT_SAVE_TYPE; + } // StoreNewItem, mail reward, etc. save data directly to the database // to prevent exploitable data desynchronisation we save the quest status to the database too @@ -15048,7 +15068,9 @@ void Player::RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, UpdatePvPState(); } - SendQuestUpdate(quest_id); + SendQuestUpdate(); + + SendQuestGiverStatusMultiple(); //lets remove flag for delayed teleports SetCanDelayTeleport(false); @@ -15086,14 +15108,32 @@ void Player::FailQuest(uint32 questId) SendQuestFailed(questId); // Destroy quest items on quest failure. - for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i) - if (quest->RequiredItemId[i] > 0 && quest->RequiredItemCount[i] > 0) - // Destroy items received on starting the quest. - DestroyItemCount(quest->RequiredItemId[i], quest->RequiredItemCount[i], true, true); + for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i])) + if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->RequiredItemId[i], 9999, true, true); + for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) - if (quest->ItemDrop[i] > 0 && quest->ItemDropQuantity[i] > 0) - // Destroy items received during the quest. - DestroyItemCount(quest->ItemDrop[i], quest->ItemDropQuantity[i], true, true); + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) + if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->ItemDrop[i], 9999, true, true); + } +} + +void Player::AbandonQuest(uint32 questId) +{ + if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId)) + { + // Destroy quest items on quest abandon. + for (uint8 i = 0; i < QUEST_ITEM_OBJECTIVES_COUNT; ++i) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->RequiredItemId[i])) + if (quest->RequiredItemCount[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->RequiredItemId[i], 9999, true, true); + + for (uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i) + if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(quest->ItemDrop[i])) + if (quest->ItemDropQuantity[i] > 0 && (itemTemplate->Bonding == BIND_QUEST_ITEM || itemTemplate->Bonding == BIND_QUEST_ITEM1)) + DestroyItemCount(quest->ItemDrop[i], 9999, true, true); } } @@ -15683,14 +15723,16 @@ bool Player::CanShareQuest(uint32 quest_id) const void Player::SetQuestStatus(uint32 questId, QuestStatus status, bool update /*= true*/) { - if (sObjectMgr->GetQuestTemplate(questId)) + if (Quest const* quest = sObjectMgr->GetQuestTemplate(questId)) { m_QuestStatus[questId].Status = status; - m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE; + + if (!quest->IsAutoComplete()) + m_QuestStatusSave[questId] = QUEST_DEFAULT_SAVE_TYPE; } if (update) - SendQuestUpdate(questId); + SendQuestUpdate(); sScriptMgr->OnQuestStatusChange(this, questId, status); } @@ -15705,7 +15747,7 @@ void Player::RemoveActiveQuest(uint32 questId, bool update /*= true*/) } if (update) - SendQuestUpdate(questId); + SendQuestUpdate(); } void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/) @@ -15718,33 +15760,26 @@ void Player::RemoveRewardedQuest(uint32 questId, bool update /*= true*/) } if (update) - SendQuestUpdate(questId); + SendQuestUpdate(); } -void Player::SendQuestUpdate(uint32 questId) +void Player::SendQuestUpdate() { uint32 zone = 0, area = 0; + GetZoneAndAreaId(zone, area); - SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForQuestMapBounds(questId); - if (saBounds.first != saBounds.second) - { - GetZoneAndAreaId(zone, area); - - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) - if (itr->second->autocast && itr->second->IsFitToRequirements(this, zone, area)) - if (!HasAura(itr->second->spellId)) - CastSpell(this, itr->second->spellId, true); - } + SpellAreaForQuestMapBounds saBounds = sSpellMgr->GetSpellAreaForAreaMapBounds(area); - saBounds = sSpellMgr->GetSpellAreaForQuestEndMapBounds(questId); if (saBounds.first != saBounds.second) { - if (!zone || !area) - GetZoneAndAreaId(zone, area); - for (SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr) + { if (!itr->second->IsFitToRequirements(this, zone, area)) RemoveAurasDueToSpell(itr->second->spellId); + else if (itr->second->autocast) + if (!HasAura(itr->second->spellId)) + CastSpell(this, itr->second->spellId, true); + } } UpdateForQuestWorldObjects(); @@ -16593,6 +16628,50 @@ void Player::SendQuestUpdateAddPlayer(Quest const* quest, uint16 old_count, uint SetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT, GetQuestSlotCounter(log_slot, QUEST_PVP_KILL_SLOT) + add_count); } +void Player::SendQuestGiverStatusMultiple() +{ + uint32 count = 0; + + WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4); + data << uint32(count); // placeholder + + for (auto itr = m_clientGUIDs.begin(); itr != m_clientGUIDs.end(); ++itr) + { + uint32 questStatus = DIALOG_STATUS_NONE; + + if (itr->IsAnyTypeCreature()) + { + // need also pet quests case support + Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr); + if (!questgiver || questgiver->IsHostileTo(this)) + continue; + if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)) + continue; + + questStatus = GetQuestDialogStatus(questgiver); + + data << uint64(questgiver->GetGUID()); + data << uint8(questStatus); + ++count; + } + else if (itr->IsGameObject()) + { + GameObject* questgiver = GetMap()->GetGameObject(*itr); + if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER) + continue; + + questStatus = GetQuestDialogStatus(questgiver); + + data << uint64(questgiver->GetGUID()); + data << uint8(questStatus); + ++count; + } + } + + data.put<uint32>(0, count); // write real count + GetSession()->SendPacket(&data); +} + bool Player::HasPvPForcingQuest() const { for (uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i) @@ -17518,6 +17597,8 @@ bool Player::isAllowedToLoot(const Creature* creature) const Loot* loot = &creature->loot; if (loot->isLooted()) // nothing to loot or everything looted. return false; + if (!loot->hasItemForAll() && !loot->hasItemFor(this)) // no loot in creature for this player + return false; if (loot->loot_type == LOOT_SKINNING) return creature->GetSkinner() == GetGUID(); @@ -17574,7 +17655,7 @@ void Player::_LoadActions(PreparedQueryResult result) ab->uState = ACTIONBUTTON_UNCHANGED; else { - TC_LOG_ERROR("entities.player", "Player::_LoadActions: Player '%s' (%s) has an invalid action button (Button: %u, Action: %u, Type: %u). It will be deleted at next save.", + TC_LOG_DEBUG("entities.player", "Player::_LoadActions: Player '%s' (%s) has an invalid action button (Button: %u, Action: %u, Type: %u). It will be deleted at next save. This can be due to a player changing their talents.", GetName().c_str(), GetGUID().ToString().c_str(), button, action, type); // Will be deleted in DB at next save (it can create data until save but marked as deleted). @@ -18219,9 +18300,10 @@ void Player::_LoadQuestStatusRewarded(PreparedQueryResult result) if (quest->GetBonusTalents()) m_questRewardTalentCount += quest->GetBonusTalents(); - } - m_RewardedQuests.insert(quest_id); + if (quest->CanIncreaseRewardedQuestCounters()) + m_RewardedQuests.insert(quest_id); + } } while (result->NextRow()); } @@ -18780,7 +18862,7 @@ bool Player::CheckInstanceValidity(bool /*isLogin*/) return true; // non-instances are always valid - Map* map = GetMap(); + Map* map = FindMap(); if (!map || !map->IsDungeon()) return true; @@ -20940,7 +21022,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc return false; } - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) + if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL)) return false; // taximaster case @@ -21139,7 +21221,7 @@ void Player::CleanupAfterTaxiFlight() { m_taxi.ClearTaxiDestinations(); // not destinations, clear source node Dismount(); - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT); getHostileRefManager().setOnlineOfflineState(true); } @@ -21944,8 +22026,8 @@ void Player::ReportedAfkBy(Player* reporter) if (m_bgData.bgAfkReporter.find(reporter->GetGUID().GetCounter()) == m_bgData.bgAfkReporter.end() && !HasAura(43680) && !HasAura(43681) && reporter->CanReportAfkDueToLimit()) { m_bgData.bgAfkReporter.insert(reporter->GetGUID().GetCounter()); - // 3 players have to complain to apply debuff - if (m_bgData.bgAfkReporter.size() >= 3) + // by default 3 players have to complain to apply debuff + if (m_bgData.bgAfkReporter.size() >= sWorld->getIntConfig(CONFIG_BATTLEGROUND_REPORT_AFK)) { // cast 'Idle' spell CastSpell(this, 43680, true); @@ -22870,6 +22952,24 @@ void Player::SetDailyQuestStatus(uint32 quest_id) } } +bool Player::IsDailyQuestDone(uint32 quest_id) +{ + bool found = false; + if (sObjectMgr->GetQuestTemplate(quest_id)) + { + for (uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx) + { + if (GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1 + quest_daily_idx) == quest_id) + { + found = true; + break; + } + } + } + + return found; +} + void Player::SetWeeklyQuestStatus(uint32 quest_id) { m_weeklyquests.insert(quest_id); @@ -24016,6 +24116,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply) if (target->isType(TYPEMASK_UNIT) && target != GetVehicleBase()) static_cast<Unit*>(target)->AddPlayerToVision(this); + SetSeer(target); } else { @@ -24031,7 +24132,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply) static_cast<Unit*>(target)->RemovePlayerFromVision(this); //must immediately set seer back otherwise may crash - m_seer = this; + SetSeer(this); //WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0); //GetSession()->SendPacket(&data); @@ -26319,7 +26420,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, getFaction()); pet->setPowerType(POWER_MANA); - pet->SetUInt32Value(UNIT_NPC_FLAGS, 0); + pet->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); pet->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); pet->InitStatsForLevel(getLevel()); diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 372a49b4f9d..38fff9b8d83 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -269,6 +269,7 @@ struct PlayerInfo uint16 displayId_f; PlayerCreateInfoItems item; PlayerCreateInfoSpells customSpells; + PlayerCreateInfoSpells castSpells; PlayerCreateInfoActions action; PlayerCreateInfoSkills skills; @@ -288,7 +289,7 @@ struct PvPInfo struct DuelInfo { - DuelInfo() : initiator(nullptr), opponent(nullptr), startTimer(0), startTime(0), outOfBound(0), isMounted(false) { } + DuelInfo() : initiator(nullptr), opponent(nullptr), startTimer(0), startTime(0), outOfBound(0), isMounted(false), isCompleted(false) { } Player* initiator; Player* opponent; @@ -296,6 +297,7 @@ struct DuelInfo time_t startTime; time_t outOfBound; bool isMounted; + bool isCompleted; }; struct Areas @@ -1333,6 +1335,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool CanRewardQuest(Quest const* quest, uint32 reward, bool msg); void AddQuestAndCheckCompletion(Quest const* quest, Object* questGiver); void AddQuest(Quest const* quest, Object* questGiver); + void AbandonQuest(uint32 quest_id); void CompleteQuest(uint32 quest_id); void IncompleteQuest(uint32 quest_id); void RewardQuest(Quest const* quest, uint32 reward, Object* questGiver, bool announce = true); @@ -1361,10 +1364,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SetQuestStatus(uint32 questId, QuestStatus status, bool update = true); void RemoveActiveQuest(uint32 questId, bool update = true); void RemoveRewardedQuest(uint32 questId, bool update = true); - void SendQuestUpdate(uint32 questId); + void SendQuestUpdate(); QuestGiverStatus GetQuestDialogStatus(Object* questGiver); void SetDailyQuestStatus(uint32 quest_id); + bool IsDailyQuestDone(uint32 quest_id); void SetWeeklyQuestStatus(uint32 quest_id); void SetMonthlyQuestStatus(uint32 quest_id); void SetSeasonalQuestStatus(uint32 quest_id); @@ -1413,6 +1417,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void SendQuestUpdateAddItem(Quest const* quest, uint32 itemIdx, uint16 count) const; void SendQuestUpdateAddCreatureOrGo(Quest const* quest, ObjectGuid guid, uint32 creatureOrGOIdx, uint16 oldCount, uint16 addCount); void SendQuestUpdateAddPlayer(Quest const* quest, uint16 oldCount, uint16 addCount); + void SendQuestGiverStatusMultiple(); ObjectGuid GetDivider() const { return m_divider; } void SetDivider(ObjectGuid guid) { m_divider = guid; } @@ -1596,7 +1601,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void AddSpellMod(SpellModifier* mod, bool apply); bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr) const; - template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell = nullptr); + template <class T> + void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr); void RemoveSpellMods(Spell* spell); void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr); void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr); @@ -2604,11 +2610,13 @@ TC_GAME_API void AddItemsSetItem(Player* player, Item* item); TC_GAME_API void RemoveItemsSetItem(Player* player, ItemTemplate const* proto); // "the bodies of template functions must be made available in a header file" -template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell) +template <class T> +void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) - return 0; + return; + float totalmul = 1.0f; int32 totalflat = 0; @@ -2644,9 +2652,8 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &bas DropModCharge(mod, spell); } - float diff = (float)basevalue * (totalmul - 1.0f) + (float)totalflat; - basevalue = T((float)basevalue + diff); - return T(diff); + + basevalue = T(float(basevalue + totalflat) * totalmul); } #endif diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 5e5c6901471..0d97e120fff 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -92,7 +92,8 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, SetGoType(GAMEOBJECT_TYPE_MO_TRANSPORT); SetGoAnimProgress(animprogress); SetName(goinfo->name); - UpdateRotationFields(0.0f, 1.0f); + SetWorldRotation(G3D::Quat()); + SetParentRotation(G3D::Quat()); m_model = CreateModel(); return true; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ee815f199bd..ac924f39a03 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -459,12 +459,6 @@ void Unit::resetAttackTimer(WeaponAttackType type) m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]); } -float Unit::GetMeleeReach() const -{ - float reach = m_floatValues[UNIT_FIELD_COMBATREACH]; - return reach > MIN_MELEE_REACH ? reach : MIN_MELEE_REACH; -} - bool Unit::IsWithinCombatRange(const Unit* obj, float dist2compare) const { if (!obj || !IsInMap(obj) || !InSamePhase(obj)) @@ -481,7 +475,7 @@ bool Unit::IsWithinCombatRange(const Unit* obj, float dist2compare) const return distsq < maxdist * maxdist; } -bool Unit::IsWithinMeleeRange(const Unit* obj, float dist) const +bool Unit::IsWithinMeleeRange(Unit const* obj) const { if (!obj || !IsInMap(obj) || !InSamePhase(obj)) return false; @@ -491,10 +485,9 @@ bool Unit::IsWithinMeleeRange(const Unit* obj, float dist) const float dz = GetPositionZMinusOffset() - obj->GetPositionZMinusOffset(); float distsq = dx*dx + dy*dy + dz*dz; - float sizefactor = GetMeleeReach() + obj->GetMeleeReach(); - float maxdist = dist + sizefactor; + float maxdist = GetCombatReach() + obj->GetCombatReach() + 4.0f / 3.0f; - return distsq < maxdist * maxdist; + return distsq <= maxdist * maxdist; } void Unit::GetRandomContactPoint(const Unit* obj, float &x, float &y, float &z, float distance2dMin, float distance2dMax) const @@ -599,11 +592,14 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam if (victim->GetTypeId() == TYPEID_PLAYER && this != victim) { - // Signal to pets that their owner was attacked - Pet* pet = victim->ToPlayer()->GetPet(); + // Signal to pets that their owner was attacked - except when DOT. + if (damagetype != DOT) + { + Pet* pet = victim->ToPlayer()->GetPet(); - if (pet && pet->IsAlive()) - pet->AI()->OwnerAttackedBy(this); + if (pet && pet->IsAlive()) + pet->AI()->OwnerAttackedBy(this); + } if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD)) return 0; @@ -624,6 +620,17 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam else victim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, 0); + // interrupt spells with SPELL_INTERRUPT_FLAG_ABORT_ON_DMG on absorbed damage (no dots) + if (!damage && damagetype != DOT && cleanDamage && cleanDamage->absorbed_damage) + if (victim != this && victim->GetTypeId() == TYPEID_PLAYER) + if (Spell* spell = victim->m_currentSpells[CURRENT_GENERIC_SPELL]) + if (spell->getState() == SPELL_STATE_PREPARING) + { + uint32 interruptFlags = spell->m_spellInfo->InterruptFlags; + if ((interruptFlags & SPELL_INTERRUPT_FLAG_ABORT_ON_DMG) != 0) + victim->InterruptNonMeleeSpells(false); + } + // We're going to call functions which can modify content of the list during iteration over it's elements // Let's copy the list so we can prevent iterator invalidation AuraEffectList vCopyDamageCopy(victim->GetAuraEffectsByType(SPELL_AURA_SHARE_DAMAGE_PCT)); @@ -1355,7 +1362,8 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) if (damageInfo->blocked_amount && damageInfo->TargetState != VICTIMSTATE_BLOCKS) victim->HandleEmoteCommand(EMOTE_ONESHOT_PARRY_SHIELD); - if (damageInfo->TargetState == VICTIMSTATE_PARRY) + if (damageInfo->TargetState == VICTIMSTATE_PARRY && + (GetTypeId() != TYPEID_UNIT || (ToCreature()->GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN) == 0)) { // Get attack timers float offtime = float(victim->getAttackTimer(OFF_ATTACK)); @@ -2253,7 +2261,7 @@ float Unit::CalculateLevelPenalty(SpellInfo const* spellProto) const float LvlPenalty = 0.0f; if (spellProto->SpellLevel < 20) - LvlPenalty = 20.0f - spellProto->SpellLevel * 3.75f; + LvlPenalty = (20.0f - spellProto->SpellLevel) * 3.75f; float LvlFactor = (float(spellProto->SpellLevel) + 6.0f) / float(getLevel()); if (LvlFactor > 1.0f) LvlFactor = 1.0f; @@ -2387,37 +2395,19 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit* victim, SpellInfo const* spellInfo if (roll < tmp) return SPELL_MISS_MISS; - // Chance resist mechanic (select max value from every mechanic spell effect) - int32 resist_mech = 0; - // Get effects mechanic and chance - for (uint8 eff = 0; eff < MAX_SPELL_EFFECTS; ++eff) - { - int32 effect_mech = spellInfo->GetEffectMechanic(eff); - if (effect_mech) - { - int32 temp = victim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_MECHANIC_RESISTANCE, effect_mech); - if (resist_mech < temp * 100) - resist_mech = temp * 100; - } - } - // Roll chance - tmp += resist_mech; + // Chance resist mechanic + int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100; + tmp += resist_chance; if (roll < tmp) return SPELL_MISS_RESIST; - bool canDodge = true; - bool canParry = true; - bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL); - - // Same spells cannot be parry/dodge + // Same spells cannot be parried/dodged if (spellInfo->HasAttribute(SPELL_ATTR0_IMPOSSIBLE_DODGE_PARRY_BLOCK)) return SPELL_MISS_NONE; - // Chance resist mechanic - int32 resist_chance = victim->GetMechanicResistChance(spellInfo) * 100; - tmp += resist_chance; - if (roll < tmp) - return SPELL_MISS_RESIST; + bool canDodge = true; + bool canParry = true; + bool canBlock = spellInfo->HasAttribute(SPELL_ATTR3_BLOCKABLE_SPELL); // Ranged attacks can only miss, resist and deflect if (attType == RANGED_ATTACK) @@ -2854,6 +2844,15 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit* victi else crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_CHANCE); + AuraEffectList const& critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER); + for (AuraEffect const* aurEff : critChanceForCaster) + { + if (aurEff->GetCasterGUID() != GetGUID()) + continue; + + crit += aurEff->GetAmount(); + } + crit += victim->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_SPELL_AND_WEAPON_CRIT_CHANCE); // reduce crit chance from Rating for players @@ -3189,6 +3188,15 @@ int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const return 0; } +bool Unit::CanMoveDuringChannel() const +{ + if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) + if (spell->getState() != SPELL_STATE_FINISHED) + return spell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING) && spell->IsChannelActive(); + + return false; +} + bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const { return IsWithinDistInMap(target, distance) && HasInArc(arc, target); @@ -5287,7 +5295,7 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType } //victim may be NULL -bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) +bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown) { SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); uint32 effIndex = triggeredByAura->GetEffIndex(); @@ -5297,8 +5305,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; uint32 triggered_spell_id = 0; - uint32 cooldown_spell_id = 0; // for random trigger, will be one of the triggered spell to avoid repeatable triggers - // otherwise, it's the triggered_spell_id by default Unit* target = victim; int32 basepoints0 = 0; ObjectGuid originalCaster; @@ -5374,24 +5380,20 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere case CLASS_PALADIN: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 case CLASS_DRUID: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 triggered_spell_id = RAND(39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409); - cooldown_spell_id = 39511; break; case CLASS_ROGUE: // 39511, 40997, 40998, 41002, 41005, 41011 case CLASS_WARRIOR: // 39511, 40997, 40998, 41002, 41005, 41011 case CLASS_DEATH_KNIGHT: triggered_spell_id = RAND(39511, 40997, 40998, 41002, 41005, 41011); - cooldown_spell_id = 39511; break; case CLASS_PRIEST: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 case CLASS_SHAMAN: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 case CLASS_MAGE: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 case CLASS_WARLOCK: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 triggered_spell_id = RAND(40999, 41002, 41005, 41009, 41011, 41406, 41409); - cooldown_spell_id = 40999; break; case CLASS_HUNTER: // 40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409 triggered_spell_id = RAND(40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409); - cooldown_spell_id = 40997; break; default: return false; @@ -5621,11 +5623,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) - { - if (!GetSpellHistory()->HasCooldown(*itr)) - GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); - } break; } case 71562: // Deathbringer's Will Heroic @@ -5667,11 +5664,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) - { - if (!GetSpellHistory()->HasCooldown(*itr)) - GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); - } break; } case 65032: // Boom aura (321 Boombot) @@ -6309,24 +6301,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere return true; } } - // Eclipse - if (dummySpell->SpellIconID == 2856 && GetTypeId() == TYPEID_PLAYER) - { - if (!procSpell || effIndex != 0) - return false; - - bool isWrathSpell = (procSpell->SpellFamilyFlags[0] & 1); - - if (!roll_chance_f(dummySpell->ProcChance * (isWrathSpell ? 0.6f : 1.0f))) - return false; - - target = this; - if (target->HasAura(isWrathSpell ? 48517 : 48518)) - return false; - - triggered_spell_id = isWrathSpell ? 48518 : 48517; - break; - } break; } case SPELLFAMILY_ROGUE: @@ -6491,6 +6465,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // 4 healing tick basepoints0 = triggerAmount * damage / 400; triggered_spell_id = 54203; + // Add remaining ticks to healing done + basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), triggered_spell_id, SPELL_AURA_PERIODIC_HEAL); break; } switch (dummySpell->Id) @@ -6507,7 +6483,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // Item - Paladin T8 Holy 4P Bonus if (Unit* caster = triggeredByAura->GetCaster()) if (AuraEffect const* aurEff = caster->GetAuraEffect(64895, 0)) - cooldown = aurEff->GetAmount(); + cooldown = Milliseconds(aurEff->GetAmount()); target = this; break; @@ -6822,10 +6798,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere if (!player || !castItem || !castItem->IsEquipped() || !victim || !victim->IsAlive()) return false; - // custom cooldown processing case - if (cooldown && GetSpellHistory()->HasCooldown(dummySpell->Id)) - return false; - if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID()) return false; @@ -6888,8 +6860,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere triggered_spell_id = 33750; // apply cooldown before cast to prevent processing itself - if (cooldown) - player->GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown)); + triggeredByAura->GetBase()->AddProcCooldown(std::chrono::steady_clock::now() + cooldown); + cooldown = Milliseconds::zero(); // Attack Twice for (uint32 i = 0; i < 2; ++i) @@ -7132,10 +7104,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere if (!procSpell || GetTypeId() != TYPEID_PLAYER || !victim) return false; - // custom cooldown processing case - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(dummySpell->Id)) - return false; - uint32 spellId = 0; // Every Lightning Bolt and Chain Lightning spell have duplicate vs half damage and zero cost switch (procSpell->Id) @@ -7181,10 +7149,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } CastSpell(victim, spellId, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown)); - return true; } // Static Shock @@ -7486,26 +7450,17 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere return false; } - if (cooldown_spell_id == 0) - cooldown_spell_id = triggered_spell_id; - - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(cooldown_spell_id)) - return false; - if (basepoints0) CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura, originalCaster); else CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster); - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(cooldown_spell_id, 0, std::chrono::seconds(cooldown)); - return true; } // Used in case when access to whole aura is needed // All procs should be handled like this... -bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, uint32 cooldown, bool * handled) +bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, bool* handled) { SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); @@ -7717,12 +7672,6 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp case 49222: { *handled = true; - if (cooldown && GetTypeId() == TYPEID_PLAYER) - { - if (GetSpellHistory()->HasCooldown(100000)) - return false; - GetSpellHistory()->AddCooldown(100000, 0, std::chrono::seconds(cooldown)); - } return true; } // Hungering Cold aura drop @@ -7765,7 +7714,7 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp return false; } -bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown) +bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx) { // Get triggered aura spell info SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo(); @@ -7791,95 +7740,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg { switch (auraSpellInfo->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - switch (auraSpellInfo->Id) - { - case 43820: // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket) - // Pct value stored in dummy - basepoints0 = victim->GetCreateHealth() * auraSpellInfo->Effects[1].CalcValue() / 100; - target = victim; - break; - case 57345: // Darkmoon Card: Greatness - { - float stat = 0.0f; - // strength - if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 60229;stat = GetStat(STAT_STRENGTH); } - // agility - if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 60233;stat = GetStat(STAT_AGILITY); } - // intellect - if (GetStat(STAT_INTELLECT)> stat) { trigger_spell_id = 60234;stat = GetStat(STAT_INTELLECT);} - // spirit - if (GetStat(STAT_SPIRIT) > stat) { trigger_spell_id = 60235; } - break; - } - case 64568: // Blood Reserve - { - if (HealthBelowPctDamaged(35, damage)) - { - CastCustomSpell(this, 64569, &triggerAmount, NULL, NULL, true); - RemoveAura(64568); - } - return false; - } - case 67702: // Death's Choice, Item - Coliseum 25 Normal Melee Trinket - { - float stat = 0.0f; - // strength - if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67708;stat = GetStat(STAT_STRENGTH); } - // agility - if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67703; } - break; - } - case 67771: // Death's Choice (heroic), Item - Coliseum 25 Heroic Melee Trinket - { - float stat = 0.0f; - // strength - if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67773;stat = GetStat(STAT_STRENGTH); } - // agility - if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67772; } - break; - } - // Mana Drain Trigger - case 27522: - case 40336: - { - // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target. - if (IsAlive()) - CastSpell(this, 29471, true, castItem, triggeredByAura); - if (victim && victim->IsAlive()) - CastSpell(victim, 27526, true, castItem, triggeredByAura); - return true; - } - // Evasive Maneuvers - case 50240: - { - // Remove a Evasive Charge - Aura* charge = GetAura(50241); - if (charge && charge->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL)) - RemoveAurasDueToSpell(50240); - break; - } - // Battle Experience - // already handled in gunship battle script - case 71201: - return false; - } - break; - case SPELLFAMILY_MAGE: - if (auraSpellInfo->SpellIconID == 2127) // Blazing Speed - { - switch (auraSpellInfo->Id) - { - case 31641: // Rank 1 - case 31642: // Rank 2 - trigger_spell_id = 31643; - break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u miss posibly Blazing Speed", auraSpellInfo->Id); - return false; - } - } - break; case SPELLFAMILY_WARLOCK: { // Drain Soul @@ -7902,325 +7762,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg // Need for correct work Drain Soul SPELL_AURA_CHANNEL_DEATH_ITEM aura return false; } - // Nether Protection - else if (auraSpellInfo->SpellIconID == 1985) - { - if (!procSpell) - return false; - switch (GetFirstSchoolInMask(procSpell->GetSchoolMask())) - { - case SPELL_SCHOOL_NORMAL: - return false; // ignore - case SPELL_SCHOOL_HOLY: trigger_spell_id = 54370; break; - case SPELL_SCHOOL_FIRE: trigger_spell_id = 54371; break; - case SPELL_SCHOOL_NATURE: trigger_spell_id = 54375; break; - case SPELL_SCHOOL_FROST: trigger_spell_id = 54372; break; - case SPELL_SCHOOL_SHADOW: trigger_spell_id = 54374; break; - case SPELL_SCHOOL_ARCANE: trigger_spell_id = 54373; break; - default: - return false; - } - } - break; - } - case SPELLFAMILY_PRIEST: - { - // Blessed Recovery - if (auraSpellInfo->SpellIconID == 1875) - { - switch (auraSpellInfo->Id) - { - case 27811: trigger_spell_id = 27813; break; - case 27815: trigger_spell_id = 27817; break; - case 27816: trigger_spell_id = 27818; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u not handled in BR", auraSpellInfo->Id); - return false; - } - basepoints0 = CalculatePct(int32(damage), triggerAmount) / 3; - target = this; - // Add remaining ticks to healing done - basepoints0 += GetRemainingPeriodicAmount(GetGUID(), trigger_spell_id, SPELL_AURA_PERIODIC_HEAL); - } - break; - } - case SPELLFAMILY_DRUID: - { - switch (auraSpellInfo->Id) - { - // Druid Forms Trinket - case 37336: - { - switch (GetShapeshiftForm()) - { - case FORM_NONE: trigger_spell_id = 37344; break; - case FORM_CAT: trigger_spell_id = 37341; break; - case FORM_BEAR: - case FORM_DIREBEAR: trigger_spell_id = 37340; break; - case FORM_TREE: trigger_spell_id = 37342; break; - case FORM_MOONKIN: trigger_spell_id = 37343; break; - default: - return false; - } - break; - } - // Druid T9 Feral Relic (Lacerate, Swipe, Mangle, and Shred) - case 67353: - { - switch (GetShapeshiftForm()) - { - case FORM_CAT: trigger_spell_id = 67355; break; - case FORM_BEAR: - case FORM_DIREBEAR: trigger_spell_id = 67354; break; - default: - return false; - } - break; - } - default: - break; - } - break; - } - case SPELLFAMILY_HUNTER: - { - if (auraSpellInfo->SpellIconID == 3247) // Piercing Shots - { - switch (auraSpellInfo->Id) - { - case 53234: // Rank 1 - case 53237: // Rank 2 - case 53238: // Rank 3 - trigger_spell_id = 63468; - break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u miss posibly Piercing Shots", auraSpellInfo->Id); - return false; - } - SpellInfo const* TriggerPS = sSpellMgr->GetSpellInfo(trigger_spell_id); - if (!TriggerPS) - return false; - - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (TriggerPS->GetMaxDuration() / TriggerPS->Effects[0].Amplitude); - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), trigger_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - break; - } - // Item - Hunter T9 4P Bonus - if (auraSpellInfo->Id == 67151) - { - trigger_spell_id = 68130; - target = this; - break; - } - break; - } - case SPELLFAMILY_PALADIN: - { - switch (auraSpellInfo->Id) - { - // Soul Preserver - case 60510: - { - switch (getClass()) - { - case CLASS_DRUID: - trigger_spell_id = 60512; - break; - case CLASS_PALADIN: - trigger_spell_id = 60513; - break; - case CLASS_PRIEST: - trigger_spell_id = 60514; - break; - case CLASS_SHAMAN: - trigger_spell_id = 60515; - break; - } - - target = this; - break; - } - case 37657: // Lightning Capacitor - case 54841: // Thunder Capacitor - case 67712: // Item - Coliseum 25 Normal Caster Trinket - case 67758: // Item - Coliseum 25 Heroic Caster Trinket - { - if (!victim || !victim->IsAlive() || GetTypeId() != TYPEID_PLAYER) - return false; - - uint32 stack_spell_id = 0; - switch (auraSpellInfo->Id) - { - case 37657: - stack_spell_id = 37658; - trigger_spell_id = 37661; - break; - case 54841: - stack_spell_id = 54842; - trigger_spell_id = 54843; - break; - case 67712: - stack_spell_id = 67713; - trigger_spell_id = 67714; - break; - case 67758: - stack_spell_id = 67759; - trigger_spell_id = 67760; - break; - } - - CastSpell(this, stack_spell_id, true, NULL, triggeredByAura); - - Aura* dummy = GetAura(stack_spell_id); - if (!dummy || dummy->GetStackAmount() < triggerAmount) - return false; - - RemoveAurasDueToSpell(stack_spell_id); - target = victim; - break; - } - default: - // Illumination - if (auraSpellInfo->SpellIconID == 241) - { - if (!procSpell) - return false; - // procspell is triggered spell but we need mana cost of original cast spell - uint32 originalSpellId = procSpell->Id; - // Holy Shock heal - if (procSpell->SpellFamilyFlags[1] & 0x00010000) - { - switch (procSpell->Id) - { - case 25914: originalSpellId = 20473; break; - case 25913: originalSpellId = 20929; break; - case 25903: originalSpellId = 20930; break; - case 27175: originalSpellId = 27174; break; - case 33074: originalSpellId = 33072; break; - case 48820: originalSpellId = 48824; break; - case 48821: originalSpellId = 48825; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u not handled in HShock", procSpell->Id); - return false; - } - } - SpellInfo const* originalSpell = sSpellMgr->GetSpellInfo(originalSpellId); - if (!originalSpell) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu", originalSpellId); - return false; - } - // percent stored in effect 1 (class scripts) base points - int32 cost = int32(originalSpell->ManaCost + CalculatePct(GetCreateMana(), originalSpell->ManaCostPercentage)); - basepoints0 = CalculatePct(cost, auraSpellInfo->Effects[1].CalcValue()); - trigger_spell_id = 20272; - target = this; - } - break; - } - break; - } - case SPELLFAMILY_SHAMAN: - { - switch (auraSpellInfo->Id) - { - case 30881: // Nature's Guardian Rank 1 - case 30883: // Nature's Guardian Rank 2 - case 30884: // Nature's Guardian Rank 3 - case 30885: // Nature's Guardian Rank 4 - case 30886: // Nature's Guardian Rank 5 - { - if (HealthBelowPct(30)) - { - basepoints0 = int32(auraSpellInfo->Effects[EFFECT_0].CalcValue() * GetMaxHealth() / 100.0f); - target = this; - trigger_spell_id = 31616; - /// @todo Threat part - } - else - return false; - break; - } - default: - { - // Lightning Shield (overwrite non existing triggered spell call in spell.dbc - if (auraSpellInfo->SpellFamilyFlags[0] & 0x400) - { - trigger_spell_id = sSpellMgr->GetSpellWithRank(26364, auraSpellInfo->GetRank()); - } - // Nature's Guardian - else if (auraSpellInfo->SpellIconID == 2013) - { - // Check health condition - should drop to less 30% (damage deal after this!) - if (!HealthBelowPctDamaged(30, damage)) - return false; - - if (victim && victim->IsAlive()) - victim->getThreatManager().modifyThreatPercent(this, -10); - - basepoints0 = int32(CountPctFromMaxHealth(triggerAmount)); - trigger_spell_id = 31616; - target = this; - } - } - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Acclimation - if (auraSpellInfo->SpellIconID == 1930) - { - if (!procSpell) - return false; - switch (GetFirstSchoolInMask(procSpell->GetSchoolMask())) - { - case SPELL_SCHOOL_NORMAL: - return false; // ignore - case SPELL_SCHOOL_HOLY: trigger_spell_id = 50490; break; - case SPELL_SCHOOL_FIRE: trigger_spell_id = 50362; break; - case SPELL_SCHOOL_NATURE: trigger_spell_id = 50488; break; - case SPELL_SCHOOL_FROST: trigger_spell_id = 50485; break; - case SPELL_SCHOOL_SHADOW: trigger_spell_id = 50489; break; - case SPELL_SCHOOL_ARCANE: trigger_spell_id = 50486; break; - default: - return false; - } - } - // Blood Presence (Improved) - else if (auraSpellInfo->Id == 63611) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - trigger_spell_id = 50475; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - } - // Item - Death Knight T10 Melee 4P Bonus - else if (auraSpellInfo->Id == 70656) - { - if (GetTypeId() != TYPEID_PLAYER || getClass() != CLASS_DEATH_KNIGHT) - return false; - - for (uint8 i = 0; i < MAX_RUNES; ++i) - if (ToPlayer()->GetRuneCooldown(i) == 0) - return false; - } - break; - } - case SPELLFAMILY_ROGUE: - { - switch (auraSpellInfo->Id) - { - // Rogue T10 2P bonus, should only proc on caster - case 70805: - { - if (victim != this) - return false; - break; - } - } - break; } default: break; @@ -8254,7 +7795,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg float averageDmg = 0; // now compute approximate weapon damage by formula from wowwiki.com - if (procFlags & PROC_FLAG_DONE_OFFHAND_ATTACK) + if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) averageDmg = (GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2.f; else averageDmg = (GetFloatValue(UNIT_FIELD_MINDAMAGE) + GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2.f; @@ -8331,7 +7872,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg case 53232: { // This effect only from Rapid Fire (ability cast) - if (!(procSpell->SpellFamilyFlags[0] & 0x20)) + if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x20)) return false; break; } @@ -8349,7 +7890,9 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg case 15337: // Improved Spirit Tap (Rank 1) case 15338: // Improved Spirit Tap (Rank 2) { - ASSERT(procSpell); + if (!procSpell) + return false; + if (procSpell->SpellFamilyFlags[0] & 0x800000) if ((procSpell->Id != 58381) || !roll_chance_i(50)) return false; @@ -8416,13 +7959,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg if (!target) return false; - if (cooldown && target->GetTypeId() == TYPEID_PLAYER && target->GetSpellHistory()->HasCooldown(trigger_spell_id)) - return false; - target->CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown)); return true; } // Cast positive spell on enemy target @@ -8531,7 +8068,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg // Astral Shift case 52179: { - if (procSpell == 0 || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) + if (!procSpell || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == victim) return false; // Need stun, fear or silence mechanic @@ -8553,7 +8090,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg case 56453: { // Proc only from Frost/Freezing trap activation or from Freezing Arrow (the periodic dmg proc handled elsewhere) - if (!(procFlags & PROC_FLAG_DONE_TRAP_ACTIVATION) || !procSpell || !(procSpell->SchoolMask & SPELL_SCHOOL_MASK_FROST) || !roll_chance_i(triggerAmount)) + if (!(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION) || !procSpell || !(procSpell->SchoolMask & SPELL_SCHOOL_MASK_FROST) || !roll_chance_i(triggerAmount)) return false; break; } @@ -8583,13 +8120,15 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg case 65081: { // Proc only from PW:S cast - if (!(procSpell->SpellFamilyFlags[0] & 0x00000001)) + if (!procSpell || !(procSpell->SpellFamilyFlags[0] & 0x00000001)) return false; break; } // Culling the Herd case 70893: { + if (!procSpell) + return false; // check if we're doing a critical hit if (!(procSpell->SpellFamilyFlags[1] & 0x10000000) && (procEx != PROC_EX_CRITICAL_HIT)) return false; @@ -8600,29 +8139,23 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg } } - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(trigger_spell_id)) - return false; - // extra attack should hit same target if (triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) target = victim; // try detect target manually if not set if (target == NULL) - target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim; + target = !(procFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim; if (basepoints0) CastCustomSpell(target, trigger_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); else CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown)); - return true; } -bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown) +bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell) { int32 scriptId = triggeredByAura->GetMiscValue(); @@ -8713,14 +8246,7 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, Au return false; } - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(triggered_spell_id)) - return false; - CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(triggered_spell_id, 0, std::chrono::seconds(cooldown)); - return true; } @@ -9073,6 +8599,9 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) ToCreature()->SendAIReaction(AI_REACTION_HOSTILE); ToCreature()->CallAssistance(); + + // Remove emote state - will be restored on creature reset + SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); } // delay offhand weapon attack to next attack time @@ -10739,6 +10268,19 @@ float Unit::GetUnitSpellCriticalChance(Unit* victim, SpellInfo const* spellProto if (Player* modOwner = GetSpellModOwner()) modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CRITICAL_CHANCE, crit_chance); + // for this types the bonus was already added in GetUnitCriticalChance, do not add twice + if (spellProto->DmgClass != SPELL_DAMAGE_CLASS_MELEE && spellProto->DmgClass != SPELL_DAMAGE_CLASS_RANGED) + { + AuraEffectList const& critChanceForCaster = victim->GetAuraEffectsByType(SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER); + for (AuraEffect const* aurEff : critChanceForCaster) + { + if (aurEff->GetCasterGUID() != GetGUID() || !aurEff->IsAffectedOnSpell(spellProto)) + continue; + + crit_chance += aurEff->GetAmount(); + } + } + return crit_chance > 0.0f ? crit_chance : 0.0f; } @@ -11874,10 +11416,8 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy) if (enemy) { if (IsAIEnabled) - { creature->AI()->EnterCombat(enemy); - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); // unit has engaged in combat, remove immunity so players can fight back - } + if (creature->GetFormation()) creature->GetFormation()->MemberAttackStart(creature, enemy); } @@ -11889,7 +11429,7 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy) UpdateSpeed(MOVE_FLIGHT); } - if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_MOUNTED_COMBAT)) + if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED)) Dismount(); } @@ -11918,9 +11458,6 @@ void Unit::ClearInCombat() // Player's state will be cleared in Player::UpdateContestedPvP if (Creature* creature = ToCreature()) { - if (creature->GetCreatureTemplate() && creature->GetCreatureTemplate()->unit_flags & UNIT_FLAG_IMMUNE_TO_PC) - SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); // set immunity state to the one from db on evade - ClearUnitState(UNIT_STATE_ATTACK_PLAYER); if (HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED)) SetUInt32Value(UNIT_DYNAMIC_FLAGS, creature->GetCreatureTemplate()->dynamicflags); @@ -11948,7 +11485,7 @@ bool Unit::isTargetableForAttack(bool checkFakeDeath) const return false; if (HasFlag(UNIT_FIELD_FLAGS, - UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC)) + UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE)) return false; if (GetTypeId() == TYPEID_PLAYER && ToPlayer()->IsGameMaster()) @@ -12042,7 +11579,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo } Creature const* creatureAttacker = ToCreature(); - if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) + if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) return false; // check duel - before sanctuary checks @@ -12126,11 +11663,14 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co // can't assist non-friendly targets if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL - && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER))) + && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))) return false; + // Controlled player case, we can assist creatures (reaction already checked above, our faction == charmer faction) + if (GetTypeId() == TYPEID_PLAYER && IsCharmed() && GetCharmerGUID().IsCreature()) + return true; // PvP case - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) + else if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { Player const* targetPlayerOwner = target->GetAffectingPlayer(); if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) @@ -12160,7 +11700,7 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co && !((target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP))) { if (Creature const* creatureTarget = target->ToCreature()) - return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS; + return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST; } return true; } @@ -12511,7 +12051,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate) pet->SetSpeedRate(mtype, m_speed_rate[mtype]); } - if (m_movedPlayer) // unit controlled by a player. + if (Player* playerMover = GetPlayerMover()) // unit controlled by a player. { // Send notification to self. this packet is only sent to one client (the client of the player concerned by the change). WorldPacket self; @@ -12521,7 +12061,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate) if (mtype == MOVE_RUN) self << uint8(1); // unknown byte added in 2.1.0 self << float(GetSpeed(mtype)); - m_movedPlayer->GetSession()->SendPacket(&self); + playerMover->GetSession()->SendPacket(&self); // Send notification to other players. sent to every clients (if in range) except one: the client of the player concerned by the change. WorldPacket data; @@ -12529,7 +12069,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate) data << GetPackGUID(); BuildMovementPacket(&data); data << float(GetSpeed(mtype)); - SendMessageToSet(&data, false); + playerMover->SendMessageToSet(&data, false); } else // unit controlled by AI. { @@ -12649,7 +12189,7 @@ float Unit::ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask) void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) { // Only mobs can manage threat lists - if (CanHaveThreatList()) + if (CanHaveThreatList() && !HasUnitState(UNIT_STATE_EVADE)) m_ThreatManager.addThreat(victim, fThreat, schoolMask, threatSpell); } @@ -13757,6 +13297,7 @@ void Unit::UpdateCharmAI() if (!newAI) // otherwise, we default to the generic one newAI = new SimpleCharmedPlayerAI(ToPlayer()); i_AI = newAI; + newAI->OnCharmed(true); } else { @@ -14054,6 +13595,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); @@ -14254,6 +13809,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo); + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); ProcTriggeredList procTriggered; // Fill procTriggered list for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) @@ -14261,6 +13817,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u // Do not allow auras to proc from effect triggered by itself if (procAura && procAura->Id == itr->first) continue; + + if (itr->second->GetBase()->IsProcOnCooldown(now)) + continue; + ProcTriggeredData triggerData(itr->second->GetBase()); // Defensive procs are active on absorbs (so absorption effects are not a hindrance) bool active = damage || (procExtra & PROC_EX_BLOCK && isVictim); @@ -14285,6 +13845,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) continue; + bool procSuccess = RollProcResult(target, triggerData.aura, attType, isVictim, triggerData.spellProcEvent); + if (!procSuccess) + continue; + // Triggered spells not triggering additional spells bool triggered = !spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ? (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false; @@ -14337,10 +13901,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo); - // For players set spell cooldown if need - uint32 cooldown = 0; - if (prepare && GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown) - cooldown = i->spellProcEvent->cooldown; + Milliseconds cooldown = Milliseconds::zero(); + if (prepare && i->spellProcEvent && i->spellProcEvent->cooldown) + cooldown = Seconds(i->spellProcEvent->cooldown); // Note: must SetCantProc(false) before return if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) @@ -14349,9 +13912,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo); // "handled" is needed as long as proc can be handled in multiple places - if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled)) + if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, &handled)) { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), Id); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), Id); takeCharges = true; } @@ -14359,7 +13922,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u { for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) { - if (!(i->effMask & (1<<effIndex))) + if (!(i->effMask & (1 << effIndex))) continue; AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex); @@ -14376,9 +13939,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u { case SPELL_AURA_PROC_TRIGGER_SPELL: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); // Don`t drop charge or add cooldown for not started trigger - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) takeCharges = true; break; } @@ -14395,7 +13959,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u case SPELL_AURA_MANA_SHIELD: case SPELL_AURA_DUMMY: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) takeCharges = true; break; @@ -14404,20 +13969,22 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: case SPELL_AURA_MOD_MELEE_HASTE: - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", + spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); takeCharges = true; break; case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell, cooldown)) + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); + if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell)) takeCharges = true; break; } case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: { TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); HandleAuraRaidProcFromChargeWithValue(triggeredByAura); takeCharges = true; @@ -14426,7 +13993,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u case SPELL_AURA_RAID_PROC_FROM_CHARGE: { TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); HandleAuraRaidProcFromCharge(triggeredByAura); takeCharges = true; @@ -14434,9 +14001,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) takeCharges = true; break; } @@ -14521,6 +14089,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) } // if (!handled) + if (prepare && takeCharges && cooldown != Milliseconds::zero()) + i->aura->AddProcCooldown(now + cooldown); + // Remove charge (aura can be removed by triggers) if (prepare && useCharges && takeCharges) { @@ -14549,6 +14120,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo) { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + // use provided list of auras which can proc if (procAuras) { @@ -14556,9 +14129,9 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge { ASSERT((*itr)->GetTarget() == this); if (!(*itr)->GetRemoveMode()) - if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo)) + if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo, now)) { - (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo); + (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo, now); aurasTriggeringProc.push_back(*itr); } } @@ -14568,9 +14141,9 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge { for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) { - if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo)) + if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now)) { - itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo); + itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo, now); aurasTriggeringProc.push_back(itr->second); } } @@ -15192,23 +14765,23 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) return true; } -bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent) +bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent) { - SpellInfo const* spellProto = aura->GetSpellInfo(); + SpellInfo const* spellInfo = aura->GetSpellInfo(); // let the aura be handled by new proc system if it has new entry - if (sSpellMgr->GetSpellProcEntry(spellProto->Id)) + if (sSpellMgr->GetSpellProcEntry(spellInfo->Id)) return false; // Get proc Event Entry - spellProcEvent = sSpellMgr->GetSpellProcEvent(spellProto->Id); + spellProcEvent = sSpellMgr->GetSpellProcEvent(spellInfo->Id); // Get EventProcFlag uint32 EventProcFlag; if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags EventProcFlag = spellProcEvent->procFlags; else - EventProcFlag = spellProto->ProcFlags; // else get from spell proto + EventProcFlag = spellInfo->ProcFlags; // else get from spell proto // Continue if no trigger exist if (!EventProcFlag) return false; @@ -15216,12 +14789,12 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const // Additional checks for triggered spells (ignore trap casts) if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) { - if (!spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) + if (!spellInfo->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) return false; } // Check spellProcEvent data requirements - if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellProto, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) + if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellInfo, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) return false; // In most cases req get honor or XP from kill if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) @@ -15239,15 +14812,15 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const } // Aura added by spell can`t trigger from self (prevent drop charges/do triggers) // But except periodic and kill triggers (can triggered from self) - if (procSpell && procSpell->Id == spellProto->Id - && !(spellProto->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) + if (procSpell && procSpell->Id == spellInfo->Id + && !(spellInfo->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) return false; // Check if current equipment allows aura to proc if (!isVictim && GetTypeId() == TYPEID_PLAYER) { Player* player = ToPlayer(); - if (spellProto->EquippedItemClass == ITEM_CLASS_WEAPON) + if (spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON) { Item* item = NULL; if (attType == BASE_ATTACK) @@ -15260,19 +14833,26 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const if (player->IsInFeralForm()) return false; - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellProto->EquippedItemSubClassMask)) + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) return false; } - else if (spellProto->EquippedItemClass == ITEM_CLASS_ARMOR) + else if (spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR) { // Check if player is wearing shield Item* item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellProto->EquippedItemSubClassMask)) + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) return false; } } + + return true; +} + +bool Unit::RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent) +{ + SpellInfo const* spellInfo = aura->GetSpellInfo(); // Get chance from spell - float chance = float(spellProto->ProcChance); + float chance = float(spellInfo->ProcChance); // If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance; if (spellProcEvent && spellProcEvent->customChance) chance = spellProcEvent->customChance; @@ -15282,19 +14862,18 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const if (!isVictim) { uint32 weaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellProto); + chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); } else if (victim) { uint32 weaponSpeed = victim->GetAttackTime(attType); - chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellProto); + chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); } } // Apply chance modifer aura if (Player* modOwner = GetSpellModOwner()) - { - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - } + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); + return roll_chance_f(chance); } @@ -16025,7 +15604,11 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au { // change AI to charmed AI on next Update tick NeedChangeAI = true; - IsAIEnabled = false; + if (IsAIEnabled) + { + IsAIEnabled = false; + player->AI()->OnCharmed(true); + } } player->SetClientControl(this, false); } @@ -16057,7 +15640,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au case CHARM_TYPE_POSSESS: AddUnitState(UNIT_STATE_POSSESSED); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); playerCharmer->SetClientControl(this, true); playerCharmer->PossessSpellInitialize(); break; @@ -16160,7 +15743,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) case CHARM_TYPE_POSSESS: playerCharmer->SetClientControl(this, false); playerCharmer->SetClientControl(charmer, true); - charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); ClearUnitState(UNIT_STATE_POSSESSED); break; @@ -16300,8 +15883,8 @@ bool Unit::IsInPartyWith(Unit const* unit) const if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER) return u1->ToPlayer()->IsInSameGroupWith(u2->ToPlayer()); - else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) || - (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER)) + else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || + (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; else return false; @@ -16319,8 +15902,8 @@ bool Unit::IsInRaidWith(Unit const* unit) const if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER) return u1->ToPlayer()->IsInSameRaidWith(u2->ToPlayer()); - else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) || - (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER)) + else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || + (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; else return false; @@ -17202,7 +16785,7 @@ void Unit::_ExitVehicle(Position const* exitPosition) Movement::MoveSplineInit init(this); // Creatures without inhabit type air should begin falling after exiting the vehicle - if (GetTypeId() == TYPEID_UNIT && !CanFly() && height > GetMap()->GetWaterOrGroundLevel(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), &height) + 0.1f) + if (GetTypeId() == TYPEID_UNIT && !CanFly() && height > GetMap()->GetWaterOrGroundLevel(GetPhaseMask(), pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), &height) + 0.1f) init.SetFall(); init.MoveTo(pos.GetPositionX(), pos.GetPositionY(), height, false); @@ -17679,7 +17262,6 @@ bool Unit::SetWalk(bool enable) AddUnitMovementFlag(MOVEMENTFLAG_WALKING); else RemoveUnitMovementFlag(MOVEMENTFLAG_WALKING); - return true; } @@ -17694,15 +17276,7 @@ bool Unit::SetDisableGravity(bool disable, bool /*packetOnly = false*/) RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING); } else - { RemoveUnitMovementFlag(MOVEMENTFLAG_DISABLE_GRAVITY); - if (!HasUnitMovementFlag(MOVEMENTFLAG_CAN_FLY)) - { - m_movementInfo.SetFallTime(0); - AddUnitMovementFlag(MOVEMENTFLAG_FALLING); - } - } - return true; } @@ -17715,7 +17289,6 @@ bool Unit::SetSwim(bool enable) AddUnitMovementFlag(MOVEMENTFLAG_SWIMMING); else RemoveUnitMovementFlag(MOVEMENTFLAG_SWIMMING); - return true; } @@ -17730,15 +17303,7 @@ bool Unit::SetCanFly(bool enable, bool /*packetOnly = false */) RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING); } else - { RemoveUnitMovementFlag(MOVEMENTFLAG_CAN_FLY | MOVEMENTFLAG_MASK_MOVING_FLY); - if (!IsLevitating()) - { - m_movementInfo.SetFallTime(0); - AddUnitMovementFlag(MOVEMENTFLAG_FALLING); - } - } - return true; } @@ -17751,7 +17316,6 @@ bool Unit::SetWaterWalking(bool enable, bool /*packetOnly = false */) AddUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); else RemoveUnitMovementFlag(MOVEMENTFLAG_WATERWALKING); - return true; } @@ -17764,7 +17328,6 @@ bool Unit::SetFeatherFall(bool enable, bool /*packetOnly = false */) AddUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW); else RemoveUnitMovementFlag(MOVEMENTFLAG_FALLING_SLOW); - return true; } @@ -17792,7 +17355,6 @@ bool Unit::SetHover(bool enable, bool /*packetOnly = false*/) UpdateHeight(newZ); } } - return true; } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index d49d2fd9842..0f48f31b9c0 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -615,7 +615,7 @@ enum UnitFlags : uint32 { UNIT_FLAG_SERVER_CONTROLLED = 0x00000001, // set only when unit movement is controlled by server - by SPLINE/MONSTER_MOVE packets, together with UNIT_FLAG_STUNNED; only set to units controlled by client; client function CGUnit_C::IsClientControlled returns false when set for owner UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable - UNIT_FLAG_DISABLE_MOVE = 0x00000004, + UNIT_FLAG_REMOVE_CLIENT_CONTROL = 0x00000004, // This is a legacy flag used to disable movement player's movement while controlling other units, SMSG_CLIENT_CONTROL replaces this functionality clientside now. CONFUSED and FLEEING flags have the same effect on client movement asDISABLE_MOVE_CONTROL in addition to preventing spell casts/autoattack (they all allow climbing steeper hills and emotes while moving) UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, // allow apply pvp rules to attackable state in addition to faction dependent state UNIT_FLAG_RENAME = 0x00000010, UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP @@ -627,7 +627,7 @@ enum UnitFlags : uint32 UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8 UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3 UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1 - UNIT_FLAG_UNK_14 = 0x00004000, // 2.0.8 + UNIT_FLAG_CANNOT_SWIM = 0x00004000, // 2.0.8 UNIT_FLAG_UNK_15 = 0x00008000, UNIT_FLAG_UNK_16 = 0x00010000, UNIT_FLAG_PACIFIED = 0x00020000, // 3.0.3 ok @@ -739,14 +739,13 @@ enum MovementFlags MOVEMENTFLAG_FALLING_SLOW = 0x20000000, // active rogue safe fall spell (passive) MOVEMENTFLAG_HOVER = 0x40000000, // hover, cannot jump - /// @todo Check if PITCH_UP and PITCH_DOWN really belong here.. MOVEMENTFLAG_MASK_MOVING = MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD | MOVEMENTFLAG_STRAFE_LEFT | MOVEMENTFLAG_STRAFE_RIGHT | - MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN | MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR | MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING | + MOVEMENTFLAG_FALLING | MOVEMENTFLAG_FALLING_FAR | MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING | MOVEMENTFLAG_SPLINE_ELEVATION, MOVEMENTFLAG_MASK_TURNING = - MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT, + MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT | MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN, MOVEMENTFLAG_MASK_MOVING_FLY = MOVEMENTFLAG_FLYING | MOVEMENTFLAG_ASCENDING | MOVEMENTFLAG_DESCENDING, @@ -1290,9 +1289,8 @@ class TC_GAME_API Unit : public WorldObject bool CanDualWield() const { return m_canDualWield; } virtual void SetCanDualWield(bool value) { m_canDualWield = value; } float GetCombatReach() const { return m_floatValues[UNIT_FIELD_COMBATREACH]; } - float GetMeleeReach() const; bool IsWithinCombatRange(const Unit* obj, float dist2compare) const; - bool IsWithinMeleeRange(const Unit* obj, float dist = MELEE_RANGE) const; + bool IsWithinMeleeRange(Unit const* obj) const; void GetRandomContactPoint(const Unit* target, float &x, float &y, float &z, float distance2dMin, float distance2dMax) const; uint32 m_extraAttacks; bool m_canDualWield; @@ -1685,7 +1683,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); @@ -1858,6 +1857,9 @@ class TC_GAME_API Unit : public WorldObject Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; int32 GetCurrentSpellCastTime(uint32 spell_id) const; + // Check if our current channel spell has attribute SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING + bool CanMoveDuringChannel() const; + SpellHistory* GetSpellHistory() { return m_spellHistory; } SpellHistory const* GetSpellHistory() const { return m_spellHistory; } @@ -2252,11 +2254,12 @@ class TC_GAME_API Unit : public WorldObject void DisableSpline(); private: - bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent); - bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool * handled); - bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown); + bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent); + bool RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent); + bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown); + bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, bool* handled); + bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx); + bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell); bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura); bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura); diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 9cf485322f2..1c5299f4798 100755 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -238,7 +238,7 @@ void Vehicle::RemoveAllPassengers() while (!_pendingJoinEvents.empty()) { VehicleJoinEvent* e = _pendingJoinEvents.front(); - e->to_Abort = true; + e->ScheduleAbort(); e->Target = eventVehicle; _pendingJoinEvents.pop_front(); } @@ -429,7 +429,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) if (seat == Seats.end()) // no available seat { - e->to_Abort = true; + e->ScheduleAbort(); return false; } @@ -441,7 +441,7 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) seat = Seats.find(seatId); if (seat == Seats.end()) { - e->to_Abort = true; + e->ScheduleAbort(); return false; } @@ -532,6 +532,9 @@ void Vehicle::RelocatePassengers() { ASSERT(_me->GetMap()); + std::vector<std::pair<Unit*, Position>> seatRelocation; + seatRelocation.reserve(Seats.size()); + // not sure that absolute position calculation is correct, it must depend on vehicle pitch angle for (SeatMap::const_iterator itr = Seats.begin(); itr != Seats.end(); ++itr) { @@ -542,9 +545,12 @@ void Vehicle::RelocatePassengers() float px, py, pz, po; passenger->m_movementInfo.transport.pos.GetPosition(px, py, pz, po); CalculatePassengerPosition(px, py, pz, &po); - passenger->UpdatePosition(px, py, pz, po); + seatRelocation.emplace_back(passenger, Position(px, py, pz, po)); } } + + for (auto const& pair : seatRelocation) + pair.first->UpdatePosition(pair.second); } /** @@ -701,7 +707,7 @@ void Vehicle::RemovePendingEventsForSeat(int8 seatId) { if ((*itr)->Seat->first == seatId) { - (*itr)->to_Abort = true; + (*itr)->ScheduleAbort(); _pendingJoinEvents.erase(itr++); } else @@ -726,7 +732,7 @@ void Vehicle::RemovePendingEventsForPassenger(Unit* passenger) { if ((*itr)->Passenger == passenger) { - (*itr)->to_Abort = true; + (*itr)->ScheduleAbort(); _pendingJoinEvents.erase(itr++); } else diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index fc0abf5a8c7..bbd0cee2a51 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -473,7 +473,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.unit_flags = fields[29].GetUInt32(); creatureTemplate.unit_flags2 = fields[30].GetUInt32(); creatureTemplate.dynamicflags = fields[31].GetUInt32(); - creatureTemplate.family = fields[32].GetUInt8(); + creatureTemplate.family = CreatureFamily(fields[32].GetUInt8()); creatureTemplate.trainer_type = fields[33].GetUInt8(); creatureTemplate.trainer_spell = fields[34].GetUInt32(); creatureTemplate.trainer_class = fields[35].GetUInt8(); @@ -888,7 +888,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM) { TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid creature family (%u) in `family`.", cInfo->Entry, cInfo->family); - const_cast<CreatureTemplate*>(cInfo)->family = 0; + const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE; } if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE) @@ -1050,8 +1050,8 @@ void ObjectMgr::LoadGameObjectAddons() { uint32 oldMSTime = getMSTime(); - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT guid, invisibilityType, invisibilityValue FROM gameobject_addon"); + // 0 1 2 3 4 5 6 + QueryResult result = WorldDatabase.Query("SELECT guid, parent_rotation0, parent_rotation1, parent_rotation2, parent_rotation3, invisibilityType, invisibilityValue FROM gameobject_addon"); if (!result) { @@ -1066,7 +1066,7 @@ void ObjectMgr::LoadGameObjectAddons() ObjectGuid::LowType guid = fields[0].GetUInt32(); - const GameObjectData* goData = GetGOData(guid); + GameObjectData const* goData = GetGOData(guid); if (!goData) { TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) does not exist but has a record in `gameobject_addon`", guid); @@ -1074,12 +1074,13 @@ void ObjectMgr::LoadGameObjectAddons() } GameObjectAddon& gameObjectAddon = _gameObjectAddonStore[guid]; - gameObjectAddon.invisibilityType = InvisibilityType(fields[1].GetUInt8()); - gameObjectAddon.InvisibilityValue = fields[2].GetUInt32(); + gameObjectAddon.ParentRotation = G3D::Quat(fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat()); + gameObjectAddon.invisibilityType = InvisibilityType(fields[5].GetUInt8()); + gameObjectAddon.InvisibilityValue = fields[6].GetUInt32(); if (gameObjectAddon.invisibilityType >= TOTAL_INVISIBILITY_TYPES) { - TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid InvisibilityType in `gameobject_addon`", guid); + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid InvisibilityType in `gameobject_addon`, disabled invisibility", guid); gameObjectAddon.invisibilityType = INVISIBILITY_GENERAL; gameObjectAddon.InvisibilityValue = 0; } @@ -1090,6 +1091,12 @@ void ObjectMgr::LoadGameObjectAddons() gameObjectAddon.InvisibilityValue = 1; } + if (!gameObjectAddon.ParentRotation.isUnit()) + { + TC_LOG_ERROR("sql.sql", "GameObject (GUID: %u) has invalid path rotation, set to default", guid); + gameObjectAddon.ParentRotation = G3D::Quat(); + } + ++count; } while (result->NextRow()); @@ -1883,10 +1890,10 @@ ObjectGuid::LowType ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, fl data.posY = y; data.posZ = z; data.orientation = o; - data.rotation0 = rotation0; - data.rotation1 = rotation1; - data.rotation2 = rotation2; - data.rotation3 = rotation3; + data.rotation.x = rotation0; + data.rotation.y = rotation1; + data.rotation.z = rotation2; + data.rotation.w = rotation3; data.spawntimesecs = spawntimedelay; data.animprogress = 100; data.spawnMask = 1; @@ -2036,10 +2043,10 @@ void ObjectMgr::LoadGameobjects() data.posY = fields[4].GetFloat(); data.posZ = fields[5].GetFloat(); data.orientation = fields[6].GetFloat(); - data.rotation0 = fields[7].GetFloat(); - data.rotation1 = fields[8].GetFloat(); - data.rotation2 = fields[9].GetFloat(); - data.rotation3 = fields[10].GetFloat(); + data.rotation.x = fields[7].GetFloat(); + data.rotation.y = fields[8].GetFloat(); + data.rotation.z = fields[9].GetFloat(); + data.rotation.w = fields[10].GetFloat(); data.spawntimesecs = fields[11].GetInt32(); MapEntry const* mapEntry = sMapStore.LookupEntry(data.mapid); @@ -2080,15 +2087,27 @@ void ObjectMgr::LoadGameobjects() data.orientation = Position::NormalizeOrientation(data.orientation); } - if (data.rotation2 < -1.0f || data.rotation2 > 1.0f) + if (data.rotation.x < -1.0f || data.rotation.x > 1.0f) + { + TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationX (%f) value, skip", guid, data.id, data.rotation.x); + continue; + } + + if (data.rotation.y < -1.0f || data.rotation.y > 1.0f) + { + TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationY (%f) value, skip", guid, data.id, data.rotation.y); + continue; + } + + if (data.rotation.z < -1.0f || data.rotation.z > 1.0f) { - TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip", guid, data.id, data.rotation2); + TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationZ (%f) value, skip", guid, data.id, data.rotation.z); continue; } - if (data.rotation3 < -1.0f || data.rotation3 > 1.0f) + if (data.rotation.w < -1.0f || data.rotation.w > 1.0f) { - TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip", guid, data.id, data.rotation3); + TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid rotationW (%f) value, skip", guid, data.id, data.rotation.w); continue; } @@ -2178,26 +2197,12 @@ ObjectGuid ObjectMgr::GetPlayerGUIDByName(std::string const& name) const bool ObjectMgr::GetPlayerNameByGUID(ObjectGuid guid, std::string& name) const { - // prevent DB access for online player - if (Player* player = ObjectAccessor::FindConnectedPlayer(guid)) - { - name = player->GetName(); - return true; - } - - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME); - - stmt->setUInt32(0, guid.GetCounter()); - - PreparedQueryResult result = CharacterDatabase.Query(stmt); - - if (result) - { - name = (*result)[0].GetString(); - return true; - } + CharacterInfo const* characterInfo = sWorld->GetCharacterInfo(guid); + if (!characterInfo) + return false; - return false; + name = characterInfo->Name; + return true; } uint32 ObjectMgr::GetPlayerTeamByGUID(ObjectGuid guid) const @@ -3529,6 +3534,61 @@ void ObjectMgr::LoadPlayerInfo() } } + // Load playercreate cast spell + TC_LOG_INFO("server.loading", "Loading Player Create Cast Spell Data..."); + { + uint32 oldMSTime = getMSTime(); + + QueryResult result = WorldDatabase.PQuery("SELECT raceMask, classMask, spell FROM playercreateinfo_cast_spell"); + + if (!result) + TC_LOG_ERROR("server.loading", ">> Loaded 0 player create cast spells. DB table `playercreateinfo_cast_spell` is empty."); + else + { + uint32 count = 0; + + do + { + Field* fields = result->Fetch(); + uint32 raceMask = fields[0].GetUInt32(); + uint32 classMask = fields[1].GetUInt32(); + uint32 spellId = fields[2].GetUInt32(); + + if (raceMask != 0 && !(raceMask & RACEMASK_ALL_PLAYABLE)) + { + TC_LOG_ERROR("sql.sql", "Wrong race mask %u in `playercreateinfo_cast_spell` table, ignoring.", raceMask); + continue; + } + + if (classMask != 0 && !(classMask & CLASSMASK_ALL_PLAYABLE)) + { + TC_LOG_ERROR("sql.sql", "Wrong class mask %u in `playercreateinfo_cast_spell` table, ignoring.", classMask); + continue; + } + + for (uint32 raceIndex = RACE_HUMAN; raceIndex < MAX_RACES; ++raceIndex) + { + if (raceMask == 0 || ((1 << (raceIndex - 1)) & raceMask)) + { + for (uint32 classIndex = CLASS_WARRIOR; classIndex < MAX_CLASSES; ++classIndex) + { + if (classMask == 0 || ((1 << (classIndex - 1)) & classMask)) + { + if (PlayerInfo* info = _playerInfo[raceIndex][classIndex]) + { + info->castSpells.push_back(spellId); + ++count; + } + } + } + } + } + } while (result->NextRow()); + + TC_LOG_INFO("server.loading", ">> Loaded %u player create cast spells in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + } + } + // Load playercreate actions TC_LOG_INFO("server.loading", "Loading Player Create Action Data..."); { @@ -4056,8 +4116,8 @@ void ObjectMgr::LoadQuests() // Load `quest_template_addon` // 0 1 2 3 4 5 6 7 8 result = WorldDatabase.Query("SELECT ID, MaxLevel, AllowableClasses, SourceSpellID, PrevQuestID, NextQuestID, ExclusiveGroup, RewardMailTemplateID, RewardMailDelay, " - //9 10 11 12 13 14 15 16 - "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, SpecialFlags FROM quest_template_addon"); + //9 10 11 12 13 14 15 16 17 + "RequiredSkillID, RequiredSkillPoints, RequiredMinRepFaction, RequiredMaxRepFaction, RequiredMinRepValue, RequiredMaxRepValue, ProvidedItemCount, RewardMailSenderEntry, SpecialFlags FROM quest_template_addon LEFT JOIN quest_mail_sender ON Id=QuestId"); if (!result) { @@ -4561,6 +4621,7 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId); qinfo->RewardMailTemplateId = 0; // no mail will send to player qinfo->RewardMailDelay = 0; // no mail will send to player + qinfo->RewardMailSenderEntry = 0; } else if (usedMailTemplates.find(qinfo->RewardMailTemplateId) != usedMailTemplates.end()) { @@ -4569,6 +4630,7 @@ void ObjectMgr::LoadQuests() qinfo->GetQuestId(), qinfo->RewardMailTemplateId, qinfo->RewardMailTemplateId, used_mt_itr->second); qinfo->RewardMailTemplateId = 0; // no mail will send to player qinfo->RewardMailDelay = 0; // no mail will send to player + qinfo->RewardMailSenderEntry = 0; } else usedMailTemplates[qinfo->RewardMailTemplateId] = qinfo->GetQuestId(); @@ -5946,14 +6008,14 @@ void ObjectMgr::LoadGraveyardZones() { uint32 oldMSTime = getMSTime(); - GraveYardStore.clear(); // need for reload case + GraveYardStore.clear(); // need for reload case - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT id, ghost_zone, faction FROM game_graveyard_zone"); + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone"); if (!result) { - TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `game_graveyard_zone` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty."); return; } @@ -5972,31 +6034,31 @@ void ObjectMgr::LoadGraveyardZones() WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId); if (!entry) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: %u), skipped.", safeLocId); continue; } AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId); if (!areaEntry) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing zone id (%u), skipped.", zoneId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: %u), skipped.", zoneId); continue; } if (areaEntry->zone != 0) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for subzone id (%u) instead of zone, skipped.", zoneId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for SubZone (ID: %u) instead of zone, skipped.", zoneId); continue; } if (team != 0 && team != HORDE && team != ALLIANCE) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for non player faction (%u), skipped.", team); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non player faction (%u), skipped.", team); continue; } if (!AddGraveYardLink(safeLocId, zoneId, team, false)) - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId); } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u graveyard-zone links in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); @@ -6045,7 +6107,7 @@ WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float if (range.first == range.second && !map->IsBattlegroundOrArena()) { if (zoneId != 0) // zone == 0 can't be fixed, used by bliz for bugged zones - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); return GetDefaultGraveYard(team); } @@ -6071,7 +6133,7 @@ WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId); if (!entry) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", data.safeLocId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing graveyard (WorldSafeLocsID %u), skipped.", data.safeLocId); continue; } @@ -6186,7 +6248,7 @@ void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool GraveYardMapBoundsNonConst range = GraveYardStore.equal_range(zoneId); if (range.first == range.second) { - //TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); + //TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); return; } @@ -6494,7 +6556,7 @@ uint32 ObjectMgr::GenerateAuctionID() { if (_auctionId >= 0xFFFFFFFE) { - TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. "); + TC_LOG_ERROR("misc", "Auctions ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. "); World::StopNow(ERROR_EXIT_CODE); } return _auctionId++; @@ -6504,7 +6566,7 @@ uint64 ObjectMgr::GenerateEquipmentSetGuid() { if (_equipmentSetGuid >= uint64(0xFFFFFFFFFFFFFFFELL)) { - TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. "); + TC_LOG_ERROR("misc", "EquipmentSet guid overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. "); World::StopNow(ERROR_EXIT_CODE); } return _equipmentSetGuid++; @@ -6514,7 +6576,7 @@ uint32 ObjectMgr::GenerateMailID() { if (_mailId >= 0xFFFFFFFE) { - TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. "); + TC_LOG_ERROR("misc", "Mail ids overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. "); World::StopNow(ERROR_EXIT_CODE); } return _mailId++; @@ -6524,7 +6586,7 @@ uint32 ObjectMgr::GeneratePetNumber() { if (_hiPetNumber >= 0xFFFFFFFE) { - TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. "); + TC_LOG_ERROR("misc", "_hiPetNumber Id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info."); World::StopNow(ERROR_EXIT_CODE); } return _hiPetNumber++; @@ -6534,7 +6596,7 @@ uint32 ObjectMgr::GenerateCreatureSpawnId() { if (_creatureSpawnId >= uint32(0xFFFFFF)) { - TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. "); + TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info."); World::StopNow(ERROR_EXIT_CODE); } return _creatureSpawnId++; @@ -6544,7 +6606,7 @@ uint32 ObjectMgr::GenerateGameObjectSpawnId() { if (_gameObjectSpawnId >= uint32(0xFFFFFF)) { - TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. "); + TC_LOG_ERROR("misc", "Creature spawn id overflow!! Can't continue, shutting down server. Search on forum for TCE00007 for more info. "); World::StopNow(ERROR_EXIT_CODE); } return _gameObjectSpawnId++; @@ -7639,7 +7701,7 @@ ResponseCodes ObjectMgr::CheckPlayerName(std::string const& name, LocaleConstant if (wname[i] == wname[i-1] && wname[i] == wname[i-2]) return CHAR_NAME_THREE_CONSECUTIVE; - return ValidateName(name, locale); + return ValidateName(wname, locale); } bool ObjectMgr::IsValidCharterName(const std::string& name) @@ -7677,7 +7739,7 @@ PetNameInvalidReason ObjectMgr::CheckPetName(const std::string& name, LocaleCons if (!isValidString(wname, strictMask, false)) return PET_NAME_MIXED_LANGUAGES; - switch (ValidateName(name, locale)) + switch (ValidateName(wname, locale)) { case CHAR_NAME_PROFANE: return PET_NAME_PROFANE; @@ -7763,7 +7825,7 @@ bool ObjectMgr::LoadTrinityStrings() QueryResult result = WorldDatabase.Query("SELECT entry, content_default, content_loc1, content_loc2, content_loc3, content_loc4, content_loc5, content_loc6, content_loc7, content_loc8 FROM trinity_string"); if (!result) { - TC_LOG_ERROR("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty."); + TC_LOG_ERROR("server.loading", ">> Loaded 0 trinity strings. DB table `trinity_string` is empty. You have imported an incorrect database for more info search for TCE00003 on forum."); return false; } diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h index 576a8d2ccac..f06c9faf58b 100644 --- a/src/server/game/Globals/ObjectMgr.h +++ b/src/server/game/Globals/ObjectMgr.h @@ -28,7 +28,7 @@ #include "TemporarySummon.h" #include "Corpse.h" #include "QuestDef.h" -#include "ItemPrototype.h" +#include "ItemTemplate.h" #include "NPCHandler.h" #include "DatabaseEnv.h" #include "Mail.h" diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 84aa29f96b7..5283805c59d 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -620,6 +620,9 @@ namespace Trinity if (go->GetGOInfo()->spellFocus.focusId != i_focusId) return false; + if (!go->isSpawned()) + return false; + float dist = go->GetGOInfo()->spellFocus.dist / 2.f; return go->IsWithinDistInMap(i_unit, dist); @@ -1237,7 +1240,7 @@ namespace Trinity AllGameObjectsWithEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { } bool operator() (GameObject* go) { - if (go->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(go, m_fRange, false)) + if ((!m_uiEntry || go->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(go, m_fRange, false)) return true; return false; @@ -1254,7 +1257,7 @@ namespace Trinity AllCreaturesOfEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { } bool operator() (Unit* unit) { - if (unit->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(unit, m_fRange, false)) + if ((!m_uiEntry || unit->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(unit, m_fRange, false)) return true; return false; diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 87494e78a28..121afc014ce 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -1443,17 +1443,17 @@ void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string _BroadcastEvent(GE_BANK_TAB_UPDATED, ObjectGuid::Empty, aux, name.c_str(), icon.c_str()); } -void Guild::HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool isPublic) +void Guild::HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool officer) { // Player must have rights to set public/officer note - if (!_HasRankRight(session->GetPlayer(), isPublic ? GR_RIGHT_EPNOTE : GR_RIGHT_EOFFNOTE)) + if (!_HasRankRight(session->GetPlayer(), officer ? GR_RIGHT_EOFFNOTE : GR_RIGHT_EPNOTE)) SendCommandResult(session, GUILD_COMMAND_PUBLIC_NOTE, ERR_GUILD_PERMISSIONS); else if (Member* member = GetMember(name)) { - if (isPublic) - member->SetPublicNote(note); - else + if (officer) member->SetOfficerNote(note); + else + member->SetPublicNote(note); HandleRoster(session); } diff --git a/src/server/game/Handlers/BattleGroundHandler.cpp b/src/server/game/Handlers/BattleGroundHandler.cpp index e91da35c06b..55bfb93c1c4 100644 --- a/src/server/game/Handlers/BattleGroundHandler.cpp +++ b/src/server/game/Handlers/BattleGroundHandler.cpp @@ -777,7 +777,7 @@ void WorldSession::HandleReportPvPAFK(WorldPacket& recvData) if (!reportedPlayer) { - TC_LOG_DEBUG("bg.battleground", "WorldSession::HandleReportPvPAFK: player not found"); + TC_LOG_INFO("bg.reportpvpafk", "WorldSession::HandleReportPvPAFK: %s [IP: %s] reported %s [IP: %s]", _player->GetName().c_str(), _player->GetSession()->GetRemoteAddress().c_str(), reportedPlayer->GetName().c_str(), reportedPlayer->GetSession()->GetRemoteAddress().c_str()); return; } diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp index 19638ec1bf8..812a828ddce 100644 --- a/src/server/game/Handlers/CharacterHandler.cpp +++ b/src/server/game/Handlers/CharacterHandler.cpp @@ -45,6 +45,7 @@ #include "World.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "Metric.h" class LoginQueryHolder : public SQLQueryHolder @@ -686,17 +687,17 @@ void WorldSession::HandleCharDeleteOpcode(WorldPacket& recvData) return; } - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_DATA_BY_GUID); - stmt->setUInt32(0, guid.GetCounter()); - - if (PreparedQueryResult result = CharacterDatabase.Query(stmt)) + CharacterInfo const* characterInfo = sWorld->GetCharacterInfo(guid); + if (!characterInfo) { - Field* fields = result->Fetch(); - accountId = fields[0].GetUInt32(); - name = fields[1].GetString(); - level = fields[2].GetUInt8(); + sScriptMgr->OnPlayerFailedDelete(guid, initAccountId); + return; } + accountId = characterInfo->AccountId; + name = characterInfo->Name; + level = characterInfo->Level; + // prevent deleting other players' characters using cheating tools if (accountId != initAccountId) { @@ -969,8 +970,14 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) bool firstLogin = pCurrChar->HasAtLoginFlag(AT_LOGIN_FIRST); if (firstLogin) + { pCurrChar->RemoveAtLoginFlag(AT_LOGIN_FIRST); + PlayerInfo const* info = sObjectMgr->GetPlayerInfo(pCurrChar->getRace(), pCurrChar->getClass()); + for (uint32 spellId : info->castSpells) + pCurrChar->CastSpell(pCurrChar, spellId, true); + } + // show time before shutdown if shutdown planned. if (sWorld->IsShuttingDown()) sWorld->ShutdownMsg(true, pCurrChar); @@ -995,6 +1002,8 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder* holder) sScriptMgr->OnPlayerLogin(pCurrChar, firstLogin); + TC_METRIC_EVENT("player_events", "Login", pCurrChar->GetName()); + delete holder; } @@ -1361,6 +1370,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) uint8 plrRace = fields[0].GetUInt8(); uint8 plrClass = fields[1].GetUInt8(); uint8 plrGender = fields[2].GetUInt8(); + std::string oldName = fields[4].GetString(); if (!Player::ValidateAppearance(plrRace, plrClass, plrGender, customizeInfo.HairStyle, customizeInfo.HairColor, customizeInfo.Face, customizeInfo.FacialHair, customizeInfo.Skin, true)) { @@ -1389,6 +1399,13 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) return; } + // prevent character rename + if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (customizeInfo.Name != oldName)) + { + SendCharCustomize(CHAR_NAME_FAILURE, customizeInfo); + return; + } + // prevent character rename to invalid name if (!normalizePlayerName(customizeInfo.Name)) { @@ -1420,17 +1437,6 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) } } - stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_CHARACTER_NAME); - stmt->setUInt32(0, customizeInfo.Guid.GetCounter()); - result = CharacterDatabase.Query(stmt); - - if (result) - { - std::string oldname = result->Fetch()[0].GetString(); - TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s), Character[%s] (%s) Customized to: %s", - GetAccountId(), GetRemoteAddress().c_str(), oldname.c_str(), customizeInfo.Guid.ToString().c_str(), customizeInfo.Name.c_str()); - } - SQLTransaction trans = CharacterDatabase.BeginTransaction(); Player::Customize(&customizeInfo, trans); @@ -1452,6 +1458,9 @@ void WorldSession::HandleCharCustomize(WorldPacket& recvData) sWorld->UpdateCharacterInfo(customizeInfo.Guid, customizeInfo.Name, customizeInfo.Gender); SendCharCustomize(RESPONSE_SUCCESS, customizeInfo); + + TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s), Character[%s] (%s) Customized to: %s", + GetAccountId(), GetRemoteAddress().c_str(), oldName.c_str(), customizeInfo.Guid.ToString().c_str(), customizeInfo.Name.c_str()); } void WorldSession::HandleEquipmentSetSave(WorldPacket& recvData) @@ -1605,6 +1614,7 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) return; } + std::string oldName = nameData->Name; uint8 oldRace = nameData->Race; uint8 playerClass = nameData->Class; uint8 level = nameData->Level; @@ -1647,6 +1657,13 @@ void WorldSession::HandleCharFactionOrRaceChange(WorldPacket& recvData) } } + // prevent character rename + if (sWorld->getBoolConfig(CONFIG_PREVENT_RENAME_CUSTOMIZATION) && (factionChangeInfo.Name != oldName)) + { + SendCharFactionChange(CHAR_NAME_FAILURE, factionChangeInfo); + return; + } + // prevent character rename to invalid name if (!normalizePlayerName(factionChangeInfo.Name)) { diff --git a/src/server/game/Handlers/ChatHandler.cpp b/src/server/game/Handlers/ChatHandler.cpp index 346adf6f091..2d94d423142 100644 --- a/src/server/game/Handlers/ChatHandler.cpp +++ b/src/server/game/Handlers/ChatHandler.cpp @@ -239,8 +239,6 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) switch (type) { case CHAT_MSG_SAY: - case CHAT_MSG_EMOTE: - case CHAT_MSG_YELL: { // Prevent cheating if (!sender->IsAlive()) @@ -252,13 +250,39 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) return; } - if (type == CHAT_MSG_SAY) - sender->Say(msg, Language(lang)); - else if (type == CHAT_MSG_EMOTE) - sender->TextEmote(msg); - else if (type == CHAT_MSG_YELL) - sender->Yell(msg, Language(lang)); - } break; + sender->Say(msg, Language(lang)); + break; + } + case CHAT_MSG_EMOTE: + { + // Prevent cheating + if (!sender->IsAlive()) + return; + + if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_EMOTE_LEVEL_REQ)) + { + SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld->getIntConfig(CONFIG_CHAT_EMOTE_LEVEL_REQ)); + return; + } + + sender->TextEmote(msg); + break; + } + case CHAT_MSG_YELL: + { + // Prevent cheating + if (!sender->IsAlive()) + return; + + if (sender->getLevel() < sWorld->getIntConfig(CONFIG_CHAT_YELL_LEVEL_REQ)) + { + SendNotification(GetTrinityString(LANG_SAY_REQ), sWorld->getIntConfig(CONFIG_CHAT_YELL_LEVEL_REQ)); + return; + } + + sender->Yell(msg, Language(lang)); + break; + } case CHAT_MSG_WHISPER: { if (!normalizePlayerName(to)) @@ -298,7 +322,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) sender->AddWhisperWhiteList(receiver->GetGUID()); GetPlayer()->Whisper(msg, Language(lang), receiver); - } break; + break; + } case CHAT_MSG_PARTY: case CHAT_MSG_PARTY_LEADER: { @@ -319,7 +344,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) WorldPacket data; ChatHandler::BuildChatPacket(data, ChatMsg(type), Language(lang), sender, NULL, msg); group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID())); - } break; + break; + } case CHAT_MSG_GUILD: { if (GetPlayer()->GetGuildId()) @@ -331,7 +357,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) guild->BroadcastToGuild(this, false, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); } } - } break; + break; + } case CHAT_MSG_OFFICER: { if (GetPlayer()->GetGuildId()) @@ -343,7 +370,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) guild->BroadcastToGuild(this, true, msg, lang == LANG_ADDON ? LANG_ADDON : LANG_UNIVERSAL); } } - } break; + break; + } case CHAT_MSG_RAID: { // if player is in battleground, he cannot say to battleground members by /ra @@ -360,7 +388,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID, Language(lang), sender, NULL, msg); group->BroadcastPacket(&data, false); - } break; + break; + } case CHAT_MSG_RAID_LEADER: { // if player is in battleground, he cannot say to battleground members by /ra @@ -377,7 +406,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_LEADER, Language(lang), sender, NULL, msg); group->BroadcastPacket(&data, false); - } break; + break; + } case CHAT_MSG_RAID_WARNING: { Group* group = GetPlayer()->GetGroup(); @@ -390,7 +420,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) //in battleground, raid warning is sent only to players in battleground - code is ok ChatHandler::BuildChatPacket(data, CHAT_MSG_RAID_WARNING, Language(lang), sender, NULL, msg); group->BroadcastPacket(&data, false); - } break; + break; + } case CHAT_MSG_BATTLEGROUND: { //battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() @@ -403,7 +434,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_BATTLEGROUND, Language(lang), sender, NULL, msg); group->BroadcastPacket(&data, false); - } break; + break; + } case CHAT_MSG_BATTLEGROUND_LEADER: { // battleground raid is always in Player->GetGroup(), never in GetOriginalGroup() @@ -416,7 +448,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) WorldPacket data; ChatHandler::BuildChatPacket(data, CHAT_MSG_BATTLEGROUND_LEADER, Language(lang), sender, NULL, msg);; group->BroadcastPacket(&data, false); - } break; + break; + } case CHAT_MSG_CHANNEL: { if (!HasPermission(rbac::RBAC_PERM_SKIP_CHECK_CHAT_CHANNEL_REQ)) @@ -436,7 +469,8 @@ void WorldSession::HandleMessagechatOpcode(WorldPacket& recvData) chn->Say(sender->GetGUID(), msg.c_str(), lang); } } - } break; + break; + } case CHAT_MSG_AFK: { if (!sender->IsInCombat()) diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index 1c1982600ca..5ea81718a5d 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -82,6 +82,13 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData) return; } + // player trying to invite himself (most likely cheating) + if (player == GetPlayer()) + { + SendPartyResult(PARTY_OP_INVITE, membername, ERR_BAD_PLAYER_NAME_S); + return; + } + // restrict invite to GMs if (!sWorld->getBoolConfig(CONFIG_ALLOW_GM_GROUP) && !GetPlayer()->IsGameMaster() && player->IsGameMaster()) { @@ -113,6 +120,12 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData) return; } + if (!player->GetSocial()->HasFriend(GetPlayer()->GetGUID().GetCounter()) && GetPlayer()->getLevel() < sWorld->getIntConfig(CONFIG_PARTY_LEVEL_REQ)) + { + SendPartyResult(PARTY_OP_INVITE, membername, ERR_INVITE_RESTRICTED); + return; + } + Group* group = GetPlayer()->GetGroup(); if (group && group->isBGGroup()) group = GetPlayer()->GetOriginalGroup(); @@ -170,6 +183,7 @@ void WorldSession::HandleGroupInviteOpcode(WorldPacket& recvData) } if (!group->AddInvite(player)) { + group->RemoveAllInvites(); delete group; return; } diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp index e8f8372d679..3801e974dba 100644 --- a/src/server/game/Handlers/GuildHandler.cpp +++ b/src/server/game/Handlers/GuildHandler.cpp @@ -178,7 +178,7 @@ void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket) if (normalizePlayerName(playerName)) if (Guild* guild = GetPlayer()->GetGuild()) - guild->HandleSetMemberNote(this, playerName, note, true); + guild->HandleSetMemberNote(this, playerName, note, false); } void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket) @@ -192,7 +192,7 @@ void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket) if (normalizePlayerName(playerName)) if (Guild* guild = GetPlayer()->GetGuild()) - guild->HandleSetMemberNote(this, playerName, note, false); + guild->HandleSetMemberNote(this, playerName, note, true); } void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket) diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 7f5d882a912..42ce01708eb 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -1095,7 +1095,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) return; } - if (!(gift->GetTemplate()->Flags & ITEM_PROTO_FLAG_WRAPPER)) // cheating: non-wrapper wrapper + if (!(gift->GetTemplate()->Flags & ITEM_PROTO_FLAG_IS_WRAPPER)) // cheating: non-wrapper wrapper { _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL); return; diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp index 82fdc2e54c7..b0b06b517c8 100644 --- a/src/server/game/Handlers/LootHandler.cpp +++ b/src/server/game/Handlers/LootHandler.cpp @@ -338,7 +338,7 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) else { // Only delete item if no loot or money (unlooted loot is saved to db) or if it isn't an openable item - if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_OPENABLE)) + if (pItem->loot.isLooted() || !(proto->Flags & ITEM_PROTO_FLAG_HAS_LOOT)) player->DestroyItem(pItem->GetBagSlot(), pItem->GetSlot(), true); } return; // item can be looted only single player @@ -370,13 +370,10 @@ void WorldSession::DoLootRelease(ObjectGuid lguid) loot->roundRobinPlayer.Clear(); if (Group* group = player->GetGroup()) - { group->SendLooter(creature, NULL); - - // force update of dynamic flags, otherwise other group's players still not able to loot. - creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS); - } } + // force dynflag update to update looter and lootable info + creature->ForceValuesUpdateAtIndex(UNIT_DYNAMIC_FLAGS); } } diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index 1e00c25a0c3..d02611971cc 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -236,8 +236,8 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData) recvData >> guid >> spellId; TC_LOG_DEBUG("network", "WORLD: Received CMSG_TRAINER_BUY_SPELL %s, learn spell id is: %u", guid.ToString().c_str(), spellId); - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER); - if (!unit) + Creature* trainer = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_TRAINER); + if (!trainer) { TC_LOG_DEBUG("network", "WORLD: HandleTrainerBuySpellOpcode - %s not found or you can not interact with him.", guid.ToString().c_str()); return; @@ -247,8 +247,20 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData) if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); + // check race for mount trainers + if (trainer->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_MOUNTS) + { + if (uint32 trainerRace = trainer->GetCreatureTemplate()->trainer_race) + if (_player->getRace() != trainerRace) + return; + } + + // check class for class trainers + if (_player->getClass() != trainer->GetCreatureTemplate()->trainer_class && trainer->GetCreatureTemplate()->trainer_type == TRAINER_TYPE_CLASS) + return; + // check present spell in trainer spell list - TrainerSpellData const* trainer_spells = unit->GetTrainerSpells(); + TrainerSpellData const* trainer_spells = trainer->GetTrainerSpells(); if (!trainer_spells) return; @@ -262,7 +274,7 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData) return; // apply reputation discount - uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit))); + uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(trainer))); // check money requirement if (!_player->HasEnoughMoney(nSpellCost)) @@ -270,8 +282,8 @@ void WorldSession::HandleTrainerBuySpellOpcode(WorldPacket& recvData) _player->ModifyMoney(-int32(nSpellCost)); - unit->SendPlaySpellVisual(179); // 53 SpellCastDirected - unit->SendPlaySpellImpact(_player->GetGUID(), 362); // 113 EmoteSalute + trainer->SendPlaySpellVisual(179); // 53 SpellCastDirected + trainer->SendPlaySpellImpact(_player->GetGUID(), 362); // 113 EmoteSalute // learn explicitly or cast explicitly if (trainer_spell->IsCastable()) diff --git a/src/server/game/Handlers/PetHandler.cpp b/src/server/game/Handlers/PetHandler.cpp index 8bfb0070605..17c9c4a58d7 100644 --- a/src/server/game/Handlers/PetHandler.cpp +++ b/src/server/game/Handlers/PetHandler.cpp @@ -595,7 +595,7 @@ void WorldSession::HandlePetRename(WorldPacket& recvData) Pet* pet = ObjectAccessor::GetPet(*_player, petguid); // check it! - if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType()!= HUNTER_PET || + if (!pet || !pet->IsPet() || ((Pet*)pet)->getPetType() != HUNTER_PET || !pet->HasByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED) || pet->GetOwnerGUID() != _player->GetGUID() || !pet->GetCharmInfo()) return; diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index a7db18deddb..b7aee1778a9 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -426,6 +426,7 @@ void WorldSession::HandleQuestLogRemoveQuest(WorldPacket& recvData) } _player->TakeQuestSourceItem(questId, true); // remove quest src item from player + _player->AbandonQuest(questId); // remove all quest items player received before abandoning quest. Note, this does not remove normal drop items that happen to be quest requirements. _player->RemoveActiveQuest(questId); _player->RemoveTimedAchievement(ACHIEVEMENT_TIMED_TYPE_QUEST, questId); @@ -640,46 +641,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket { TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_STATUS_MULTIPLE_QUERY"); - uint32 count = 0; - - WorldPacket data(SMSG_QUESTGIVER_STATUS_MULTIPLE, 4); - data << uint32(count); // placeholder - - for (auto itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr) - { - uint32 questStatus = DIALOG_STATUS_NONE; - - if (itr->IsAnyTypeCreature()) - { - // need also pet quests case support - Creature* questgiver = ObjectAccessor::GetCreatureOrPetOrVehicle(*GetPlayer(), *itr); - if (!questgiver || questgiver->IsHostileTo(_player)) - continue; - if (!questgiver->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER)) - continue; - - questStatus = _player->GetQuestDialogStatus(questgiver); - - data << uint64(questgiver->GetGUID()); - data << uint8(questStatus); - ++count; - } - else if (itr->IsGameObject()) - { - GameObject* questgiver = GetPlayer()->GetMap()->GetGameObject(*itr); - if (!questgiver || questgiver->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER) - continue; - - questStatus = _player->GetQuestDialogStatus(questgiver); - - data << uint64(questgiver->GetGUID()); - data << uint8(questStatus); - ++count; - } - } - - data.put<uint32>(0, count); // write real count - SendPacket(&data); + _player->SendQuestGiverStatusMultiple(); } void WorldSession::HandleQueryQuestsCompleted(WorldPacket & /*recvData*/) diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index e07e10ab00c..93e986b0339 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -196,7 +196,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket) } // Verify that the bag is an actual bag or wrapped item that can be used "normally" - if (!(proto->Flags & ITEM_PROTO_FLAG_OPENABLE) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) + if (!(proto->Flags & ITEM_PROTO_FLAG_HAS_LOOT) && !item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED)) { pUser->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW, item, NULL); TC_LOG_ERROR("network", "Possible hacking attempt: Player %s [guid: %u] tried to open item [guid: %u, entry: %u] which is not openable!", diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 5cede9a32d5..260e7ff464f 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -353,7 +353,7 @@ LootItem::LootItem(LootStoreItem const& li) conditions = li.conditions; ItemTemplate const* proto = sObjectMgr->GetItemTemplate(itemid); - freeforall = proto && (proto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT); + freeforall = proto && (proto->Flags & ITEM_PROTO_FLAG_MULTI_DROP); follow_loot_rules = proto && (proto->FlagsCu & ITEM_FLAGS_CU_FOLLOW_LOOT_RULES); needs_quest = li.needs_quest; @@ -429,7 +429,7 @@ void Loot::AddItem(LootStoreItem const& item) // non-conditional one-player only items are counted here, // free for all items are counted in FillFFALoot(), // non-ffa conditionals are counted in FillNonQuestNonFFAConditionalLoot() - if (!item.needs_quest && item.conditions.empty() && !(proto->Flags & ITEM_PROTO_FLAG_PARTY_LOOT)) + if (!item.needs_quest && item.conditions.empty() && !(proto->Flags & ITEM_PROTO_FLAG_MULTI_DROP)) ++unlootedCount; } } @@ -704,7 +704,7 @@ void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID) if (_itr->itemid != itemID) continue; - _itr->canSave = true; + _itr->canSave = false; break; } } @@ -786,6 +786,19 @@ uint32 Loot::GetMaxSlotInLootFor(Player* player) const return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0); } +// return true if there is any item that is lootable for any player (not quest item, FFA or conditional) +bool Loot::hasItemForAll() const +{ + // Gold is always lootable + if (gold) + return true; + + for (LootItem const& item : items) + if (!item.is_looted && !item.freeforall && item.conditions.empty()) + return true; + return false; +} + // return true if there is any FFA, quest or conditional item for the player. bool Loot::hasItemFor(Player* player) const { @@ -1640,7 +1653,7 @@ void LoadLootTemplates_Item() // remove real entries and check existence loot ItemTemplateContainer const* its = sObjectMgr->GetItemTemplateStore(); for (ItemTemplateContainer::const_iterator itr = its->begin(); itr != its->end(); ++itr) - if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end() && itr->second.Flags & ITEM_PROTO_FLAG_OPENABLE) + if (lootIdSet.find(itr->second.ItemId) != lootIdSet.end() && itr->second.Flags & ITEM_PROTO_FLAG_HAS_LOOT) lootIdSet.erase(itr->second.ItemId); // output error for any still listed (not referenced from appropriate table) ids diff --git a/src/server/game/Loot/LootMgr.h b/src/server/game/Loot/LootMgr.h index 0afb327d7f0..a66b8f0b4c5 100644 --- a/src/server/game/Loot/LootMgr.h +++ b/src/server/game/Loot/LootMgr.h @@ -382,6 +382,7 @@ struct TC_GAME_API Loot LootItem* LootItemInSlot(uint32 lootslot, Player* player, QuestItem** qitem = NULL, QuestItem** ffaitem = NULL, QuestItem** conditem = NULL); uint32 GetMaxSlotInLootFor(Player* player) const; + bool hasItemForAll() const; bool hasItemFor(Player* player) const; bool hasOverThresholdItem() const; diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index bc31a3c8c06..0fc49eba05e 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -70,6 +70,13 @@ MailSender::MailSender(Player* sender) m_senderId = sender->GetGUID().GetCounter(); } +MailSender::MailSender(uint32 senderEntry) +{ + m_messageType = MAIL_CREATURE; + m_senderId = senderEntry; + m_stationery = MAIL_STATIONERY_DEFAULT; +} + MailReceiver::MailReceiver(Player* receiver) : m_receiver(receiver), m_receiver_lowguid(receiver->GetGUID().GetCounter()) { } MailReceiver::MailReceiver(Player* receiver, ObjectGuid::LowType receiver_lowguid) : m_receiver(receiver), m_receiver_lowguid(receiver_lowguid) diff --git a/src/server/game/Mails/Mail.h b/src/server/game/Mails/Mail.h index ee1a0aae2c9..ad14e3cecf9 100644 --- a/src/server/game/Mails/Mail.h +++ b/src/server/game/Mails/Mail.h @@ -90,6 +90,7 @@ class TC_GAME_API MailSender MailSender(CalendarEvent* sender); MailSender(AuctionEntry* sender); MailSender(Player* sender); + MailSender(uint32 senderEntry); public: // Accessors MailMessageType GetMailMessageType() const { return m_messageType; } ObjectGuid::LowType GetSenderId() const { return m_senderId; } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 794be12ee7c..23ca8cbba1a 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -90,7 +90,7 @@ bool Map::ExistMap(uint32 mapid, int gx, int gy) if (fread(&header, sizeof(header), 1, pf) == 1) { if (header.mapMagic.asUInt != MapMagic.asUInt || header.versionMagic.asUInt != MapVersionMagic.asUInt) - TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please recreate using the mapextractor.", + TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please pull your source, recompile tools and recreate maps using the updated mapextractor, then replace your old map files with new files. If you still have problems search on forum for error TCE00018.", fileName, 4, header.mapMagic.asChar, 4, header.versionMagic.asChar, 4, MapMagic.asChar, 4, MapVersionMagic.asChar); else ret = true; @@ -1731,7 +1731,7 @@ bool GridMap::loadData(const char* filename) return true; } - TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please recreate using the mapextractor.", + TC_LOG_ERROR("maps", "Map file '%s' is from an incompatible map version (%.*s %.*s), %.*s %.*s is expected. Please pull your source, recompile tools and recreate maps using the updated mapextractor, then replace your old map files with new files. If you still have problems search on forum for error TCE00018.", filename, 4, header.mapMagic.asChar, 4, header.versionMagic.asChar, 4, MapMagic.asChar, 4, MapVersionMagic.asChar); fclose(in); return false; @@ -2299,12 +2299,12 @@ inline GridMap* Map::GetGrid(float x, float y) return GridMaps[gx][gy]; } -float Map::GetWaterOrGroundLevel(float x, float y, float z, float* ground /*= NULL*/, bool /*swim = false*/) const +float Map::GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground /*= NULL*/, bool /*swim = false*/) const { if (const_cast<Map*>(this)->GetGrid(x, y)) { // we need ground level (including grid height version) for proper return water level in point - float ground_z = GetHeight(PHASEMASK_NORMAL, x, y, z, true, 50.0f); + float ground_z = GetHeight(phasemask, x, y, z, true, 50.0f); if (ground) *ground = ground_z; diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index 5f134613e85..48864180b84 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -494,7 +494,7 @@ class TC_GAME_API Map : public GridRefManager<NGridType> BattlegroundMap* ToBattlegroundMap() { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap*>(this); else return NULL; } BattlegroundMap const* ToBattlegroundMap() const { if (IsBattlegroundOrArena()) return reinterpret_cast<BattlegroundMap const*>(this); return NULL; } - float GetWaterOrGroundLevel(float x, float y, float z, float* ground = NULL, bool swim = false) const; + float GetWaterOrGroundLevel(uint32 phasemask, float x, float y, float z, float* ground = NULL, bool swim = false) const; float GetHeight(uint32 phasemask, float x, float y, float z, bool vmap = true, float maxSearchDist = DEFAULT_HEIGHT_SEARCH) const; bool isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const; void Balance() { _dynamicTree.balance(); } diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index a7fdc37d324..91a26c29150 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -126,9 +126,9 @@ class TC_GAME_API MapManager template<typename Worker> void DoForAllMapsWithMapId(uint32 mapId, Worker&& worker); - uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; } - uint32 DecreaseScheduledScriptCount() { return --_scheduledScripts; } - uint32 DecreaseScheduledScriptCount(size_t count) { return _scheduledScripts -= count; } + void IncreaseScheduledScriptsCount() { ++_scheduledScripts; } + void DecreaseScheduledScriptCount() { --_scheduledScripts; } + void DecreaseScheduledScriptCount(std::size_t count) { _scheduledScripts -= count; } bool IsScriptScheduled() const { return _scheduledScripts > 0; } private: @@ -157,7 +157,7 @@ class TC_GAME_API MapManager MapUpdater m_updater; // atomic op counter for active scripts amount - std::atomic<uint32> _scheduledScripts; + std::atomic<std::size_t> _scheduledScripts; }; template<typename Worker> diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index d6af5d81432..143f535ddf5 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -112,7 +112,9 @@ enum TrinityStrings LANG_RBAC_PERM_REVOKED_NOT_IN_LIST = 79, LANG_PVPSTATS = 80, LANG_PVPSTATS_DISABLED = 81, - // Free 82 - 95 + LANG_COMMAND_NEARGRAVEYARD = 82, + LANG_COMMAND_NEARGRAVEYARD_NOTFOUND = 83, + // Free 84 - 95 LANG_GUILD_RENAME_ALREADY_EXISTS = 96, diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index f9a98cffd0e..7f77453d00b 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -456,7 +456,7 @@ enum SpellAttr4 enum SpellAttr5 { - SPELL_ATTR5_UNK0 = 0x00000001, // 0 + SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING = 0x00000001, // 0 available casting channel spell when moving SPELL_ATTR5_NO_REAGENT_WHILE_PREP = 0x00000002, // 1 not need reagents if UNIT_FLAG_PREPARATION SPELL_ATTR5_UNK2 = 0x00000004, // 2 SPELL_ATTR5_USABLE_WHILE_STUNNED = 0x00000008, // 3 usable while stunned @@ -1490,11 +1490,12 @@ 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) + 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_TRIGGERED = 0x00000040, // typically, summoned objects. Triggered by spell or other events + 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 }; @@ -2531,6 +2532,7 @@ uint32 const CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL = (1 << (CREATURE_TYPE_ME // CreatureFamily.dbc enum CreatureFamily { + CREATURE_FAMILY_NONE = 0, CREATURE_FAMILY_WOLF = 1, CREATURE_FAMILY_CAT = 2, CREATURE_FAMILY_SPIDER = 3, @@ -2576,38 +2578,38 @@ enum CreatureFamily enum CreatureTypeFlags { - CREATURE_TYPEFLAGS_TAMEABLE = 0x00000001, // Tameable by any hunter - CREATURE_TYPEFLAGS_GHOST = 0x00000002, // Creature are also visible for not alive player. Allow gossip interaction if npcflag allow? - CREATURE_TYPEFLAGS_BOSS = 0x00000004, - CREATURE_TYPEFLAGS_UNK3 = 0x00000008, - CREATURE_TYPEFLAGS_UNK4 = 0x00000010, - CREATURE_TYPEFLAGS_UNK5 = 0x00000020, - CREATURE_TYPEFLAGS_UNK6 = 0x00000040, - CREATURE_TYPEFLAGS_DEAD_INTERACT = 0x00000080, // Player can interact with the creature if its dead (not player dead) - CREATURE_TYPEFLAGS_HERBLOOT = 0x00000100, // Can be looted by herbalist - CREATURE_TYPEFLAGS_MININGLOOT = 0x00000200, // Can be looted by miner - CREATURE_TYPEFLAGS_DONT_LOG_DEATH = 0x00000400, // Death event will not show up in combat log - CREATURE_TYPEFLAGS_MOUNTED_COMBAT = 0x00000800, // Creature can remain mounted when entering combat - CREATURE_TYPEFLAGS_AID_PLAYERS = 0x00001000, // ? Can aid any player in combat if in range? - CREATURE_TYPEFLAGS_UNK13 = 0x00002000, - CREATURE_TYPEFLAGS_UNK14 = 0x00004000, // ? Possibly not in use - CREATURE_TYPEFLAGS_ENGINEERLOOT = 0x00008000, // Can be looted by engineer - CREATURE_TYPEFLAGS_EXOTIC = 0x00010000, // Can be tamed by hunter as exotic pet - CREATURE_TYPEFLAGS_UNK17 = 0x00020000, // ? Related to vehicles/pvp? - CREATURE_TYPEFLAGS_UNK18 = 0x00040000, // ? Related to vehicle/siege weapons? - CREATURE_TYPEFLAGS_PROJECTILE_COLLISION = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ - CREATURE_TYPEFLAGS_UNK20 = 0x00100000, - CREATURE_TYPEFLAGS_UNK21 = 0x00200000, - CREATURE_TYPEFLAGS_UNK22 = 0x00400000, - CREATURE_TYPEFLAGS_UNK23 = 0x00800000, // ? First seen in 3.2.2. Related to banner/backpack of creature/companion? - CREATURE_TYPEFLAGS_UNK24 = 0x01000000, - CREATURE_TYPEFLAGS_UNK25 = 0x02000000, - CREATURE_TYPEFLAGS_PARTY_MEMBER = 0x04000000, //! Creature can be targeted by spells that require target to be in caster's party/raid - CREATURE_TYPEFLAGS_UNK27 = 0x08000000, - CREATURE_TYPEFLAGS_UNK28 = 0x10000000, - CREATURE_TYPEFLAGS_UNK29 = 0x20000000, - CREATURE_TYPEFLAGS_UNK30 = 0x40000000, - CREATURE_TYPEFLAGS_UNK31 = 0x80000000 + CREATURE_TYPE_FLAG_TAMEABLE_PET = 0x00000001, // Makes the mob tameable (must also be a beast and have family set) + CREATURE_TYPE_FLAG_GHOST_VISIBLE = 0x00000002, // Creature are also visible for not alive player. Allow gossip interaction if npcflag allow? + CREATURE_TYPE_FLAG_BOSS_MOB = 0x00000004, // Changes creature's visible level to "??" in the creature's portrait - Immune Knockback. + CREATURE_TYPE_FLAG_DO_NOT_PLAY_WOUND_PARRY_ANIMATION = 0x00000008, + CREATURE_TYPE_FLAG_HIDE_FACTION_TOOLTIP = 0x00000010, + CREATURE_TYPE_FLAG_UNK5 = 0x00000020, // Sound related + CREATURE_TYPE_FLAG_SPELL_ATTACKABLE = 0x00000040, + CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD = 0x00000080, // Player can interact with the creature if its dead (not player dead) + CREATURE_TYPE_FLAG_HERB_SKINNING_SKILL = 0x00000100, // Can be looted by herbalist + CREATURE_TYPE_FLAG_MINING_SKINNING_SKILL = 0x00000200, // Can be looted by miner + CREATURE_TYPE_FLAG_DO_NOT_LOG_DEATH = 0x00000400, // Death event will not show up in combat log + CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED = 0x00000800, // Creature can remain mounted when entering combat + CREATURE_TYPE_FLAG_CAN_ASSIST = 0x00001000, // ? Can aid any player in combat if in range? + CREATURE_TYPE_FLAG_IS_PET_BAR_USED = 0x00002000, + CREATURE_TYPE_FLAG_MASK_UID = 0x00004000, + CREATURE_TYPE_FLAG_ENGINEERING_SKINNING_SKILL = 0x00008000, // Can be looted by engineer + CREATURE_TYPE_FLAG_EXOTIC_PET = 0x00010000, // Can be tamed by hunter as exotic pet + CREATURE_TYPE_FLAG_USE_DEFAULT_COLLISION_BOX = 0x00020000, // Collision related. (always using default collision box?) + CREATURE_TYPE_FLAG_IS_SIEGE_WEAPON = 0x00040000, + CREATURE_TYPE_FLAG_CAN_COLLIDE_WITH_MISSILES = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ + CREATURE_TYPE_FLAG_HIDE_NAME_PLATE = 0x00100000, + CREATURE_TYPE_FLAG_DO_NOT_PLAY_MOUNTED_ANIMATIONS = 0x00200000, + CREATURE_TYPE_FLAG_IS_LINK_ALL = 0x00400000, + CREATURE_TYPE_FLAG_INTERACT_ONLY_WITH_CREATOR = 0x00800000, + CREATURE_TYPE_FLAG_DO_NOT_PLAY_UNIT_EVENT_SOUNDS = 0x01000000, + CREATURE_TYPE_FLAG_HAS_NO_SHADOW_BLOB = 0x02000000, + CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT = 0x04000000, // ! Creature can be targeted by spells that require target to be in caster's party/raid + CREATURE_TYPE_FLAG_FORCE_GOSSIP = 0x08000000, // Allows the creature to display a single gossip option. + CREATURE_TYPE_FLAG_DO_NOT_SHEATHE = 0x10000000, + CREATURE_TYPE_FLAG_DO_NOT_TARGET_ON_INTERACTION = 0x20000000, + CREATURE_TYPE_FLAG_DO_NOT_RENDER_OBJECT_NAME = 0x40000000, + CREATURE_TYPE_FLAG_UNIT_IS_QUEST_BOSS = 0x80000000 // Not verified }; enum CreatureEliteType diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index f27e47fba21..26873451649 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -241,7 +241,7 @@ void MotionMaster::MoveConfused() void MotionMaster::MoveChase(Unit* target, float dist, float angle) { // ignore movement request if target not exist - if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) + if (!target || target == _owner) return; //_owner->ClearUnitState(UNIT_STATE_FOLLOW); @@ -266,7 +266,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle) void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot) { // ignore movement request if target not exist - if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) + if (!target || target == _owner) return; //_owner->AddUnitState(UNIT_STATE_FOLLOW); @@ -479,11 +479,12 @@ void MotionMaster::MoveFall(uint32 id /*=0*/) if (std::fabs(_owner->GetPositionZ() - tz) < 0.1f) return; + _owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING); + _owner->m_movementInfo.SetFallTime(0); + + // don't run spline movement for players if (_owner->GetTypeId() == TYPEID_PLAYER) - { - _owner->AddUnitMovementFlag(MOVEMENTFLAG_FALLING); - _owner->m_movementInfo.SetFallTime(0); - } + return; Movement::MoveSplineInit init(_owner); init.MoveTo(_owner->GetPositionX(), _owner->GetPositionY(), tz, false); @@ -534,6 +535,7 @@ void MotionMaster::MoveSeekAssistance(float x, float y, float z) TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) seek assistance (X: %f Y: %f Z: %f)", _owner->GetEntry(), _owner->GetGUID().GetCounter(), x, y, z); _owner->AttackStop(); + _owner->CastStop(); _owner->ToCreature()->SetReactState(REACT_PASSIVE); Mutate(new AssistanceMovementGenerator(x, y, z), MOTION_SLOT_ACTIVE); } diff --git a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp index 6cdd29986bb..4cf896c6d98 100644 --- a/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/FleeingMovementGenerator.cpp @@ -38,6 +38,12 @@ void FleeingMovementGenerator<T>::_setTargetLocation(T* owner) if (owner->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED)) return; + if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel()) + { + owner->CastStop(); + return; + } + owner->AddUnitState(UNIT_STATE_FLEEING_MOVE); float x, y, z; diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 421678ded17..feea0ce734d 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -29,6 +29,12 @@ template<> void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature) { + if (creature->HasUnitState(UNIT_STATE_CASTING) && !creature->CanMoveDuringChannel()) + { + creature->CastStop(); + return; + } + float respX, respY, respZ, respO, destX, destY, destZ, travelDistZ; creature->GetHomePosition(respX, respY, respZ, respO); Map const* map = creature->GetBaseMap(); @@ -57,7 +63,7 @@ void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature) // Limit height change const float distanceZ = float(rand_norm()) * travelDistZ/2.0f; destZ = respZ + distanceZ; - float levelZ = map->GetWaterOrGroundLevel(destX, destY, destZ-2.0f); + float levelZ = map->GetWaterOrGroundLevel(creature->GetPhaseMask(), destX, destY, destZ-2.5f); // Problem here, we must fly above the ground and water, not under. Let's try on next tick if (levelZ >= destZ) @@ -141,6 +147,9 @@ void RandomMovementGenerator<Creature>::DoFinalize(Creature* creature) template<> bool RandomMovementGenerator<Creature>::DoUpdate(Creature* creature, const uint32 diff) { + if (!creature || !creature->IsAlive()) + return false; + if (creature->HasUnitState(UNIT_STATE_ROOT | UNIT_STATE_STUNNED | UNIT_STATE_DISTRACTED)) { i_nextMoveTime.Reset(0); // Expire the timer diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index 403ccf684e7..2a95952d793 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -36,6 +36,9 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up if (owner->HasUnitState(UNIT_STATE_NOT_MOVE)) return; + if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel()) + return; + if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature())) { owner->ToCreature()->SetCannotReachTarget(true); @@ -70,8 +73,8 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up // doing a "dance" while fighting if (owner->IsPet() && i_target->GetTypeId() == TYPEID_PLAYER) { - dist = i_target->GetCombatReach(); - size = i_target->GetCombatReach() - i_target->GetObjectSize(); + dist = 1.0f; //i_target->GetCombatReach(); + size = 1.0f; //i_target->GetCombatReach() - i_target->GetObjectSize(); } else { @@ -146,7 +149,7 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff) } // prevent movement while casting spells with cast time or channel time - if (owner->HasUnitState(UNIT_STATE_CASTING)) + if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel()) { if (!owner->IsStopped()) owner->StopMoving(); @@ -165,8 +168,15 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff) if (i_recheckDistance.Passed()) { i_recheckDistance.Reset(100); + //More distance let have better performance, less distance let have more sensitive reaction at target move. - float allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE); + float allowed_dist = 0.0f; + + if (owner->IsPet() && (owner->GetCharmerOrOwnerGUID() == i_target->GetGUID())) + allowed_dist = 1.0f; // pet following owner + else + allowed_dist = owner->GetCombatReach() + sWorld->getRate(RATE_TARGET_POS_RECALCULATION_RANGE); + G3D::Vector3 dest = owner->movespline->FinalDestination(); if (owner->movespline->onTransport) if (TransportBase* transport = owner->GetDirectTransport()) diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index dd1cf494325..2ffa1a644eb 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -315,7 +315,7 @@ void FlightPathMovementGenerator::DoFinalize(Player* player) player->ClearUnitState(UNIT_STATE_IN_FLIGHT); player->Dismount(); - player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT); if (player->m_taxi.empty()) { @@ -335,7 +335,7 @@ void FlightPathMovementGenerator::DoReset(Player* player) { player->getHostileRefManager().setOnlineOfflineState(false); player->AddUnitState(UNIT_STATE_IN_FLIGHT); - player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT); Movement::MoveSplineInit init(player); uint32 end = GetPathAtMapEnd(); diff --git a/src/server/game/Movement/PathGenerator.cpp b/src/server/game/Movement/PathGenerator.cpp index 3b4f19adb0b..3f8e7d44ff2 100644 --- a/src/server/game/Movement/PathGenerator.cpp +++ b/src/server/game/Movement/PathGenerator.cpp @@ -25,6 +25,7 @@ #include "DisableMgr.h" #include "DetourCommon.h" #include "DetourNavMeshQuery.h" +#include "Metric.h" ////////////////// PathGenerator ////////////////// PathGenerator::PathGenerator(const Unit* owner) : @@ -61,6 +62,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); @@ -939,3 +942,8 @@ void PathGenerator::ReducePathLenghtByDist(float dist) nextVec = currVec; // we're going backwards } } + +bool PathGenerator::IsInvalidDestinationZ(Unit const* target) const +{ + return (target->GetPositionZ() - GetActualEndPosition().z) > 5.0f; +} diff --git a/src/server/game/Movement/PathGenerator.h b/src/server/game/Movement/PathGenerator.h index 3cad62abf25..a3e88b8a705 100644 --- a/src/server/game/Movement/PathGenerator.h +++ b/src/server/game/Movement/PathGenerator.h @@ -58,6 +58,7 @@ class TC_GAME_API PathGenerator // Calculate the path from owner to given destination // return: true if new path was calculated, false otherwise (no change needed) bool CalculatePath(float destX, float destY, float destZ, bool forceDest = false, bool straightLine = false); + bool IsInvalidDestinationZ(Unit const* target) const; // option setters - use optional void SetUseStraightPath(bool useStraightPath) { _useStraightPath = useStraightPath; } diff --git a/src/server/game/Movement/Spline/MoveSpline.cpp b/src/server/game/Movement/Spline/MoveSpline.cpp index d8173aab331..991a4eacd29 100644 --- a/src/server/game/Movement/Spline/MoveSpline.cpp +++ b/src/server/game/Movement/Spline/MoveSpline.cpp @@ -206,7 +206,7 @@ bool MoveSplineInitArgs::Validate(Unit* unit) const return false;\ } CHECK(path.size() > 1); - CHECK(velocity > 0.1f); + CHECK(velocity > 0.01f); CHECK(time_perc >= 0.f && time_perc <= 1.f); //CHECK(_checkPathBounds()); return true; diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp index d329ab27de9..1343e0966c1 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -95,7 +95,7 @@ void OPvPCapturePoint::AddGO(uint32 type, ObjectGuid::LowType guid, uint32 entry entry = data->id; } - m_Objects[type] = ObjectGuid(HighGuid::GameObject, entry, guid); + m_Objects[type] = guid; m_ObjectTypes[m_Objects[type]] = type; } @@ -109,7 +109,7 @@ void OPvPCapturePoint::AddCre(uint32 type, ObjectGuid::LowType guid, uint32 entr entry = data->id; } - m_Creatures[type] = ObjectGuid(HighGuid::Unit, entry, guid); + m_Creatures[type] = guid; m_CreatureTypes[m_Creatures[type]] = type; } @@ -190,7 +190,8 @@ bool OPvPCapturePoint::DelCreature(uint32 type) // delete respawn time for this creature PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CREATURE_RESPAWN); stmt->setUInt32(0, spawnId); - stmt->setUInt16(1, m_PvP->GetMap()->GetId()); stmt->setUInt32(2, 0); // instance id, always 0 for world maps + stmt->setUInt16(1, m_PvP->GetMap()->GetId()); + stmt->setUInt32(2, 0); // instance id, always 0 for world maps CharacterDatabase.Execute(stmt); sObjectMgr->DeleteCreatureData(spawnId); @@ -236,9 +237,9 @@ bool OPvPCapturePoint::DelCapturePoint() void OPvPCapturePoint::DeleteSpawns() { - for (std::map<uint32, uint32>::iterator i = m_Objects.begin(); i != m_Objects.end(); ++i) + for (std::map<uint32, ObjectGuid::LowType>::iterator i = m_Objects.begin(); i != m_Objects.end(); ++i) DelObject(i->first); - for (std::map<uint32, uint32>::iterator i = m_Creatures.begin(); i != m_Creatures.end(); ++i) + for (std::map<uint32, ObjectGuid::LowType>::iterator i = m_Creatures.begin(); i != m_Creatures.end(); ++i) DelCreature(i->first); DelCapturePoint(); } @@ -581,7 +582,7 @@ bool OPvPCapturePoint::HandleDropFlag(Player* /*player*/, uint32 /*id*/) int32 OPvPCapturePoint::HandleOpenGo(Player* /*player*/, GameObject* go) { - std::map<uint32, uint32>::iterator itr = m_ObjectTypes.find(go->GetSpawnId()); + std::map<ObjectGuid::LowType, uint32>::iterator itr = m_ObjectTypes.find(go->GetSpawnId()); if (itr != m_ObjectTypes.end()) return itr->second; @@ -596,7 +597,7 @@ bool OutdoorPvP::HandleAreaTrigger(Player* /*player*/, uint32 /*trigger*/) void OutdoorPvP::BroadcastPacket(WorldPacket &data) const { // This is faster than sWorld->SendZoneMessage - for (uint32 team = 0; team < 2; ++team) + for (uint32 team = 0; team < BG_TEAMS_COUNT; ++team) for (GuidSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr) if (Player* const player = ObjectAccessor::FindPlayer(*itr)) player->GetSession()->SendPacket(&data); diff --git a/src/server/game/Quests/QuestDef.cpp b/src/server/game/Quests/QuestDef.cpp index 4a4ee9bff9c..91a7b87eec7 100644 --- a/src/server/game/Quests/QuestDef.cpp +++ b/src/server/game/Quests/QuestDef.cpp @@ -132,7 +132,15 @@ Quest::Quest(Field* questRecord) void Quest::LoadQuestDetails(Field* fields) { for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) + { + if (!sEmotesStore.LookupEntry(fields[1+i].GetUInt16())) + { + TC_LOG_ERROR("sql.sql", "Table `quest_details` has non-existing Emote%i (%u) set for quest %u. Skipped.", 1+i, fields[1+i].GetUInt16(), fields[0].GetUInt32()); + continue; + } + DetailsEmote[i] = fields[1+i].GetUInt16(); + } for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) DetailsEmoteDelay[i] = fields[5+i].GetUInt32(); @@ -142,13 +150,28 @@ void Quest::LoadQuestRequestItems(Field* fields) { EmoteOnComplete = fields[1].GetUInt16(); EmoteOnIncomplete = fields[2].GetUInt16(); + + if (!sEmotesStore.LookupEntry(EmoteOnComplete)) + TC_LOG_ERROR("sql.sql", "Table `quest_request_items` has non-existing EmoteOnComplete (%u) set for quest %u.", EmoteOnComplete, fields[0].GetUInt32()); + + if (!sEmotesStore.LookupEntry(EmoteOnIncomplete)) + TC_LOG_ERROR("sql.sql", "Table `quest_request_items` has non-existing EmoteOnIncomplete (%u) set for quest %u.", EmoteOnIncomplete, fields[0].GetUInt32()); + RequestItemsText = fields[3].GetString(); } void Quest::LoadQuestOfferReward(Field* fields) { for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) + { + if (!sEmotesStore.LookupEntry(fields[1+i].GetUInt16())) + { + TC_LOG_ERROR("sql.sql", "Table `quest_offer_reward` has non-existing Emote%i (%u) set for quest %u. Skipped.", 1+i, fields[1+i].GetUInt16(), fields[0].GetUInt32()); + continue; + } + OfferRewardEmote[i] = fields[1+i].GetUInt16(); + } for (int i = 0; i < QUEST_EMOTE_COUNT; ++i) OfferRewardEmoteDelay[i] = fields[5+i].GetUInt32(); @@ -173,7 +196,8 @@ void Quest::LoadQuestTemplateAddon(Field* fields) RequiredMinRepValue = fields[13].GetInt32(); RequiredMaxRepValue = fields[14].GetInt32(); StartItemCount = fields[15].GetUInt8(); - SpecialFlags = fields[16].GetUInt8(); + RewardMailSenderEntry = fields[16].GetUInt32(); + SpecialFlags = fields[17].GetUInt8(); if (SpecialFlags & QUEST_SPECIAL_FLAGS_AUTO_ACCEPT) Flags |= QUEST_FLAGS_AUTO_ACCEPT; @@ -254,6 +278,9 @@ bool Quest::IsRaidQuest(Difficulty difficulty) const break; } + if ((Flags & QUEST_FLAGS_RAID) != 0) + return true; + return false; } @@ -285,3 +312,10 @@ uint32 Quest::CalculateHonorGain(uint8 level) const return honor; } + +bool Quest::CanIncreaseRewardedQuestCounters() const +{ + // Dungeon Finder/Daily/Repeatable (if not weekly, monthly or seasonal) quests are never considered rewarded serverside. + // This affects counters and client requests for completed quests. + return (!IsDFQuest() && !IsDaily() && (!IsRepeatable() || IsWeekly() || IsMonthly() || IsSeasonal())); +} diff --git a/src/server/game/Quests/QuestDef.h b/src/server/game/Quests/QuestDef.h index 5e3bb4889ab..dc6d22deab0 100644 --- a/src/server/game/Quests/QuestDef.h +++ b/src/server/game/Quests/QuestDef.h @@ -134,7 +134,7 @@ enum QuestFlags QUEST_FLAGS_SHARABLE = 0x00000008, // Can be shared: Player::CanShareQuest() QUEST_FLAGS_HAS_CONDITION = 0x00000010, // Not used currently QUEST_FLAGS_HIDE_REWARD_POI = 0x00000020, // Not used currently: Unsure of content - QUEST_FLAGS_RAID = 0x00000040, // Not used currently + QUEST_FLAGS_RAID = 0x00000040, // Can be completed while in raid QUEST_FLAGS_TBC = 0x00000080, // Not used currently: Available if TBC expansion enabled only QUEST_FLAGS_NO_MONEY_FROM_XP = 0x00000100, // Not used currently: Experience is not converted to gold at max level QUEST_FLAGS_HIDDEN_REWARDS = 0x00000200, // Items and money rewarded only sent in SMSG_QUESTGIVER_OFFER_REWARD (not in SMSG_QUESTGIVER_QUEST_DETAILS or in client quest log(SMSG_QUEST_QUERY_RESPONSE)) @@ -257,6 +257,7 @@ class TC_GAME_API Quest int32 GetRewSpellCast() const { return RewardSpell; } uint32 GetRewMailTemplateId() const { return RewardMailTemplateId; } uint32 GetRewMailDelaySecs() const { return RewardMailDelay; } + uint32 GetRewMailSenderEntry() const { return RewardMailSenderEntry; } uint32 GetPOIContinent() const { return POIContinent; } float GetPOIx() const { return POIx; } float GetPOIy() const { return POIy; } @@ -276,6 +277,7 @@ class TC_GAME_API Quest bool IsAllowedInRaid(Difficulty difficulty) const; bool IsDFQuest() const { return (SpecialFlags & QUEST_SPECIAL_FLAGS_DF_QUEST) != 0; } uint32 CalculateHonorGain(uint8 level) const; + bool CanIncreaseRewardedQuestCounters() const; // multiple values std::string ObjectiveText[QUEST_OBJECTIVES_COUNT]; @@ -373,6 +375,7 @@ class TC_GAME_API Quest uint32 RequiredMaxRepFaction = 0; int32 RequiredMaxRepValue = 0; uint32 StartItemCount = 0; + uint32 RewardMailSenderEntry = 0; uint32 SpecialFlags = 0; // custom flags, not sniffed/WDB }; diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index ac440da3f24..bd3f9cf2bfe 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -368,7 +368,9 @@ class CreatureGameObjectScriptRegistrySwapHooks // Hook which is called before a creature is swapped static void UnloadStage1(Creature* creature) { - creature->m_Events.KillAllEvents(true); + // Remove deletable events only, + // otherwise it causes crashes with non-deletable spell events. + creature->m_Events.KillAllEvents(false); if (creature->IsCharmed()) creature->RemoveCharmedBy(nullptr); @@ -985,7 +987,7 @@ void ScriptMgr::Initialize() FillSpellSummary(); // Load core scripts - SetScriptContext("___static___"); + SetScriptContext(GetNameOfStaticContext()); // SmartAI AddSC_SmartScripts(); @@ -1040,6 +1042,12 @@ void ScriptMgr::SwapScriptContext(bool initialize) _currentContext.clear(); } +std::string const& ScriptMgr::GetNameOfStaticContext() +{ + static std::string const name = "___static___"; + return name; +} + void ScriptMgr::ReleaseScriptContext(std::string const& context) { sScriptRegistryCompositum->ReleaseContext(context); diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index cc1b65fa593..233e56aadb2 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -876,6 +876,9 @@ class TC_GAME_API ScriptMgr /// calls for better performance (bulk changes). void SwapScriptContext(bool initialize = false); + /// Returns the context name of the static context provided by the worldserver + static std::string const& GetNameOfStaticContext(); + /// Acquires a strong module reference to the module containing the given script name, /// which prevents the shared library which contains the script from unloading. /// The shared library is lazy unloaded as soon as all references to it are released. diff --git a/src/server/game/Scripting/ScriptReloadMgr.cpp b/src/server/game/Scripting/ScriptReloadMgr.cpp index d13fa9c30f0..b0c9b6821d2 100644 --- a/src/server/game/Scripting/ScriptReloadMgr.cpp +++ b/src/server/game/Scripting/ScriptReloadMgr.cpp @@ -194,6 +194,8 @@ public: static Optional<std::shared_ptr<ScriptModule>> CreateFromPath(fs::path const& path, Optional<fs::path> cache_path); + static void ScheduleDelayedDelete(ScriptModule* module); + char const* GetScriptModuleRevisionHash() const override { return _getScriptModuleRevisionHash(); @@ -287,8 +289,13 @@ Optional<std::shared_ptr<ScriptModule>> GetFunctionFromSharedLibrary(handle, "AddScripts", addScripts) && GetFunctionFromSharedLibrary(handle, "GetScriptModule", getScriptModule) && GetFunctionFromSharedLibrary(handle, "GetBuildDirective", getBuildDirective)) - return std::make_shared<ScriptModule>(std::move(holder), getScriptModuleRevisionHash, + { + auto module = new ScriptModule(std::move(holder), getScriptModuleRevisionHash, addScripts, getScriptModule, getBuildDirective, path); + + // Unload the module at the next update tick as soon as all references are removed + return std::shared_ptr<ScriptModule>(module, ScheduleDelayedDelete); + } else { TC_LOG_ERROR("scripts.hotswap", "Could not extract all required functions from the shared library \"%s\"!", @@ -937,13 +944,6 @@ private: } } - sScriptMgr->SetScriptContext(module_name); - (*module)->AddScripts(); - TC_LOG_TRACE("scripts.hotswap", ">> Registered all scripts of module %s.", module_name.c_str()); - - if (swap_context) - sScriptMgr->SwapScriptContext(); - // Create the source listener auto listener = Trinity::make_unique<SourceUpdateListener>( sScriptReloadMgr->GetSourceDirectory() / module_name, @@ -952,8 +952,16 @@ private: // Store the module _known_modules_build_directives.insert(std::make_pair(module_name, (*module)->GetBuildDirective())); _running_script_modules.insert(std::make_pair(module_name, - std::make_pair(std::move(*module), std::move(listener)))); + std::make_pair(*module, std::move(listener)))); _running_script_module_names.insert(std::make_pair(path, module_name)); + + // Process the script loading after the module was registered correctly (#17557). + sScriptMgr->SetScriptContext(module_name); + (*module)->AddScripts(); + TC_LOG_TRACE("scripts.hotswap", ">> Registered all scripts of module %s.", module_name.c_str()); + + if (swap_context) + sScriptMgr->SwapScriptContext(); } void ProcessReloadScriptModule(fs::path const& path) @@ -1435,6 +1443,26 @@ private: fs::path temporary_cache_path_; }; +class ScriptModuleDeleteMessage +{ +public: + explicit ScriptModuleDeleteMessage(ScriptModule* module) + : module_(module) { } + + void operator() (HotSwapScriptReloadMgr*) + { + module_.reset(); + } + +private: + std::unique_ptr<ScriptModule> module_; +}; + +void ScriptModule::ScheduleDelayedDelete(ScriptModule* module) +{ + sScriptReloadMgr->QueueMessage(ScriptModuleDeleteMessage(module)); +} + /// Maps efsw actions to strings static char const* ActionToString(efsw::Action action) { @@ -1592,11 +1620,15 @@ void SourceUpdateListener::handleFileAction(efsw::WatchID watchid, std::string c std::shared_ptr<ModuleReference> ScriptReloadMgr::AcquireModuleReferenceOfContext(std::string const& context) { - auto const itr = sScriptReloadMgr->_running_script_modules.find(context); - if (itr != sScriptReloadMgr->_running_script_modules.end()) - return itr->second.first; - else + // Return empty references for the static context exported by the worldserver + if (context == ScriptMgr::GetNameOfStaticContext()) return { }; + + auto const itr = sScriptReloadMgr->_running_script_modules.find(context); + ASSERT(itr != sScriptReloadMgr->_running_script_modules.end() + && "Requested a reference to a non existent script context!"); + + return itr->second.first; } // Returns the full hot swap implemented ScriptReloadMgr diff --git a/src/server/game/Server/WorldSession.cpp b/src/server/game/Server/WorldSession.cpp index ddcc10b53dd..f65cd750ea9 100644 --- a/src/server/game/Server/WorldSession.cpp +++ b/src/server/game/Server/WorldSession.cpp @@ -43,6 +43,7 @@ #include "WardenWin.h" #include "MoveSpline.h" #include "WardenMac.h" +#include "Metric.h" #include <zlib.h> @@ -380,6 +381,8 @@ bool WorldSession::Update(uint32 diff, PacketFilter& updater) break; } + TC_METRIC_VALUE("processed_packets", processedPackets); + _recvQueue.readd(requeuePackets.begin(), requeuePackets.end()); if (m_Socket && m_Socket->IsOpen() && _warden) @@ -536,6 +539,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 @@ -823,32 +828,15 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) mi->RemoveMovementFlag((maskToRemove)); #endif + if (!GetPlayer()->GetVehicleBase() || !(GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION)) + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), MOVEMENTFLAG_ROOT); + /*! This must be a packet spoofing attempt. MOVEMENTFLAG_ROOT sent from the client is not valid in conjunction with any of the moving movement flags such as MOVEMENTFLAG_FORWARD. It will freeze clients that receive this player's movement info. */ - // Only adjust movement flag removal for vehicles with the VEHICLE_FLAG_FIXED_POSITION flag, or the hard coded exceptions below: - // 30236 | Argent Cannon - // 39759 | Tankbuster Cannon - if (GetPlayer()->GetVehicleBase() && ((GetPlayer()->GetVehicle()->GetVehicleInfo()->m_flags & VEHICLE_FLAG_FIXED_POSITION) || GetPlayer()->GetVehicleBase()->GetEntry() == 30236 || GetPlayer()->GetVehicleBase()->GetEntry() == 39759)) - { - // Actually players in rooted vehicles still send commands, don't clear root for these! - // Check specifically for the following conditions: - // MOVEMENTFLAG_ROOT + no other flags (0x800) - // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_LEFT (0x810) - // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_RIGHT (0x820) - // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_UP (0x840) - // MOVEMENTFLAG_ROOT + MOVEMENTFLAG_PITCH_DOWN (0x880) - // If none of these are true, clear the root - REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT) && mi->HasMovementFlag(MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT | MOVEMENTFLAG_PITCH_UP | MOVEMENTFLAG_PITCH_DOWN), - MOVEMENTFLAG_MASK_MOVING); - } - else - { - // Only remove here for non vehicles - REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT), - MOVEMENTFLAG_ROOT); - } + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_ROOT) && mi->HasMovementFlag(MOVEMENTFLAG_MASK_MOVING), + MOVEMENTFLAG_MASK_MOVING); //! Cannot hover without SPELL_AURA_HOVER REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_HOVER) && !GetPlayer()->HasAuraType(SPELL_AURA_HOVER), @@ -874,8 +862,10 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo* mi) REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_FORWARD) && mi->HasMovementFlag(MOVEMENTFLAG_BACKWARD), MOVEMENTFLAG_FORWARD | MOVEMENTFLAG_BACKWARD); - //! Cannot walk on water without SPELL_AURA_WATER_WALK - REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && !GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK), + //! Cannot walk on water without SPELL_AURA_WATER_WALK except for ghosts + REMOVE_VIOLATING_FLAGS(mi->HasMovementFlag(MOVEMENTFLAG_WATERWALKING) && + !GetPlayer()->HasAuraType(SPELL_AURA_WATER_WALK) && + !GetPlayer()->HasAuraType(SPELL_AURA_GHOST), MOVEMENTFLAG_WATERWALKING); //! Cannot feather fall without SPELL_AURA_FEATHER_FALL diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index 1c77283d812..a8a639d9b7f 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -324,9 +324,20 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() switch (opcode) { case CMSG_PING: + { LogOpcodeText(opcode, sessionGuard); - return HandlePing(packet) ? ReadDataHandlerResult::Ok : ReadDataHandlerResult::Error; + try + { + return HandlePing(packet) ? ReadDataHandlerResult::Ok : ReadDataHandlerResult::Error; + } + catch (ByteBufferPositionException const&) + { + } + TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_PING", GetRemoteIpAddress().to_string().c_str()); + return ReadDataHandlerResult::Error; + } case CMSG_AUTH_SESSION: + { LogOpcodeText(opcode, sessionGuard); if (_authed) { @@ -336,8 +347,17 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() return ReadDataHandlerResult::Error; } - HandleAuthSession(packet); - return ReadDataHandlerResult::WaitingForQuery; + try + { + HandleAuthSession(packet); + return ReadDataHandlerResult::WaitingForQuery; + } + catch (ByteBufferPositionException const&) + { + } + TC_LOG_ERROR("network", "WorldSocket::ReadDataHandler(): client %s sent malformed CMSG_AUTH_SESSION", GetRemoteIpAddress().to_string().c_str()); + return ReadDataHandlerResult::Error; + } case CMSG_KEEP_ALIVE: LogOpcodeText(opcode, sessionGuard); break; diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h index e0daf59fcc2..43f6b51031a 100644 --- a/src/server/game/Spells/Auras/SpellAuraDefines.h +++ b/src/server/game/Spells/Auras/SpellAuraDefines.h @@ -348,7 +348,7 @@ enum AuraType SPELL_AURA_ABILITY_PERIODIC_CRIT = 286, SPELL_AURA_DEFLECT_SPELLS = 287, SPELL_AURA_IGNORE_HIT_DIRECTION = 288, - SPELL_AURA_289 = 289, + SPELL_AURA_PREVENT_DURABILITY_LOSS = 289, SPELL_AURA_MOD_CRIT_PCT = 290, SPELL_AURA_MOD_XP_QUEST_PCT = 291, SPELL_AURA_OPEN_STABLE = 292, @@ -367,7 +367,7 @@ enum AuraType SPELL_AURA_MOD_MINIMUM_SPEED = 305, SPELL_AURA_306 = 306, SPELL_AURA_HEAL_ABSORB_TEST = 307, - SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER = 308, // NYI + SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER = 308, SPELL_AURA_309 = 309, SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE = 310, SPELL_AURA_311 = 311, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index ffe79293430..94fe6778ba1 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -346,7 +346,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //286 SPELL_AURA_ABILITY_PERIODIC_CRIT implemented in AuraEffect::PeriodicTick &AuraEffect::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult &AuraEffect::HandleNoImmediateEffect, //288 SPELL_AURA_IGNORE_HIT_DIRECTION implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult Unit::RollMeleeOutcomeAgainst - &AuraEffect::HandleNULL, //289 unused (3.2.0) + &AuraEffect::HandleNoImmediateEffect, //289 SPELL_AURA_PREVENT_DURABILITY_LOSS implemented in Player::DurabilityPointsLoss &AuraEffect::HandleAuraModCritPct, //290 SPELL_AURA_MOD_CRIT_PCT &AuraEffect::HandleNoImmediateEffect, //291 SPELL_AURA_MOD_XP_QUEST_PCT implemented in Player::RewardQuest &AuraEffect::HandleAuraOpenStable, //292 SPELL_AURA_OPEN_STABLE @@ -365,7 +365,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleAuraModIncreaseSpeed, //305 SPELL_AURA_MOD_MINIMUM_SPEED &AuraEffect::HandleUnused, //306 0 spells in 3.3.5 &AuraEffect::HandleUnused, //307 0 spells in 3.3.5 - &AuraEffect::HandleNULL, //308 new aura for hunter traps + &AuraEffect::HandleNoImmediateEffect, //308 SPELL_AURA_MOD_CRIT_CHANCE_FOR_CASTER implemented in Unit::GetUnitCriticalChance and Unit::GetUnitSpellCriticalChance &AuraEffect::HandleUnused, //309 0 spells in 3.3.5 &AuraEffect::HandleNoImmediateEffect, //310 SPELL_AURA_MOD_CREATURE_AOE_DAMAGE_AVOIDANCE implemented in Spell::CalculateDamageDone &AuraEffect::HandleNULL, //311 0 spells in 3.3.5 @@ -846,16 +846,6 @@ void AuraEffect::UpdatePeriodic(Unit* caster) case 59911: // Tenacity (vehicle) GetBase()->RefreshDuration(); break; - case 66823: case 67618: case 67619: case 67620: // Paralytic Toxin - // Get 0 effect aura - if (AuraEffect* slow = GetBase()->GetEffect(0)) - { - int32 newAmount = slow->GetAmount() - 10; - if (newAmount < -100) - newAmount = -100; - slow->ChangeAmount(newAmount); - } - break; default: break; } @@ -2545,10 +2535,9 @@ void AuraEffect::HandleAuraAllowFlight(AuraApplication const* aurApp, uint8 mode return; } - target->SetCanFly(apply); - - if (!apply && target->GetTypeId() == TYPEID_UNIT && !target->IsLevitating()) - target->GetMotionMaster()->MoveFall(); + if (target->SetCanFly(apply)) + if (!apply && !target->IsLevitating()) + target->GetMotionMaster()->MoveFall(); } void AuraEffect::HandleAuraWaterWalk(AuraApplication const* aurApp, uint8 mode, bool apply) const @@ -2938,10 +2927,9 @@ void AuraEffect::HandleAuraModIncreaseFlightSpeed(AuraApplication const* aurApp, // do not remove unit flag if there are more than this auraEffect of that kind on unit on unit if (mode & AURA_EFFECT_HANDLE_SEND_FOR_CLIENT_MASK && (apply || (!target->HasAuraType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED) && !target->HasAuraType(SPELL_AURA_FLY)))) { - target->SetCanFly(apply); - - if (!apply && target->GetTypeId() == TYPEID_UNIT && !target->IsLevitating()) - target->GetMotionMaster()->MoveFall(); + if (target->SetCanFly(apply)) + if (!apply && !target->IsLevitating()) + target->GetMotionMaster()->MoveFall(); } //! Someone should clean up these hacks and remove it from this function. It doesn't even belong here. diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 1ca5df6b327..35606989240 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -337,7 +337,8 @@ m_spellInfo(spellproto), m_casterGuid(casterGUID ? casterGUID : caster->GetGUID( m_castItemGuid(castItem ? castItem->GetGUID() : ObjectGuid::Empty), m_applyTime(time(NULL)), m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), -m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr) +m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), +m_procCooldown(std::chrono::steady_clock::time_point::min()) { if (m_spellInfo->ManaPerSecond || m_spellInfo->ManaPerSecondPerLevel) m_timeCla = 1 * IN_MILLISECONDS; @@ -469,7 +470,7 @@ void Aura::_Remove(AuraRemoveMode removeMode) if (m_dropEvent) { - m_dropEvent->to_Abort = true; + m_dropEvent->ScheduleAbort(); m_dropEvent = nullptr; } } @@ -789,7 +790,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const { uint32 maxProcCharges = m_spellInfo->ProcCharges; if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId())) - maxProcCharges = procEntry->charges; + maxProcCharges = procEntry->Charges; if (caster) if (Player* modOwner = caster->GetSpellModOwner()) @@ -1851,22 +1852,17 @@ bool Aura::CanStackWith(Aura const* existingAura) const return true; } -bool Aura::IsProcOnCooldown() const +bool Aura::IsProcOnCooldown(std::chrono::steady_clock::time_point now) const { - /*if (m_procCooldown) - { - if (m_procCooldown > time(NULL)) - return true; - }*/ - return false; + return m_procCooldown > now; } -void Aura::AddProcCooldown(uint32 /*msec*/) +void Aura::AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd) { - //m_procCooldown = time(NULL) + msec; + m_procCooldown = cooldownEnd; } -void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo) +void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) { bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo); if (!prepare) @@ -1884,10 +1880,10 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf ASSERT(procEntry); // cooldowns should be added to the whole aura (see 51698 area aura) - AddProcCooldown(procEntry->cooldown); + AddProcCooldown(now + procEntry->Cooldown); } -bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const +bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const { SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); // only auras with spell proc entry can trigger proc @@ -1899,7 +1895,7 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI return false; // check proc cooldown - if (IsProcOnCooldown()) + if (IsProcOnCooldown(now)) return false; /// @todo @@ -1963,16 +1959,16 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const { - float chance = procEntry.chance; + float chance = procEntry.Chance; // calculate chances depending on unit with caster's data // so talents modifying chances and judgements will have properly calculated proc chance if (Unit* caster = GetCaster()) { // calculate ppm chance if present and we're using weapon - if (eventInfo.GetDamageInfo() && procEntry.ratePerMinute != 0) + if (eventInfo.GetDamageInfo() && procEntry.ProcsPerMinute != 0) { uint32 WeaponSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType()); - chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ratePerMinute, GetSpellInfo()); + chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ProcsPerMinute, GetSpellInfo()); } // apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell) if (Player* modOwner = caster->GetSpellModOwner()) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 750110dc146..a147957f258 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -203,12 +203,12 @@ class TC_GAME_API Aura // this subsystem is not yet in use - the core of it is functional, but still some research has to be done // and some dependant problems fixed before it can replace old proc system (for example cooldown handling) // currently proc system functionality is implemented in Unit::ProcDamageAndSpell - bool IsProcOnCooldown() const; - void AddProcCooldown(uint32 msec); + bool IsProcOnCooldown(std::chrono::steady_clock::time_point now) const; + void AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } - void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo); - bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; + void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now); + bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); @@ -269,6 +269,8 @@ class TC_GAME_API Aura ChargeDropEvent* m_dropEvent; + std::chrono::steady_clock::time_point m_procCooldown; + private: Unit::AuraApplicationList m_removedApplications; }; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 4a36e6d3d72..5e58a602a43 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -1564,7 +1564,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex) if (Creature* creatureTarget = unit->ToCreature()) { - if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PROJECTILE_COLLISION)) + if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_COLLIDE_WITH_MISSILES)) continue; } } @@ -2320,8 +2320,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) } } + bool enablePvP = false; // need to check PvP state before spell effects, but act on it afterwards + if (spellHitTarget) { + // if target is flagged for pvp also flag caster if a player + if (unit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER) + enablePvP = true; // Decide on PvP flagging now, but act on it later. + SpellMissInfo missInfo2 = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura); if (missInfo2 != SPELL_MISS_NONE) { @@ -2461,6 +2467,10 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) unit->SetStandState(UNIT_STAND_STATE_STAND); } + // Check for SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER + if (m_spellInfo->HasAttribute(SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER) && unit->GetTypeId() != TYPEID_PLAYER) + caster->CastSpell(unit, SPELL_INTERRUPT_NONPLAYER, true); + if (spellHitTarget) { //AI functions @@ -2474,8 +2484,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Needs to be called after dealing damage/healing to not remove breaking on damage auras DoTriggersOnSpellHit(spellHitTarget, mask); - // if target is fallged for pvp also flag caster if a player - if (unit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER) + if (enablePvP) m_caster->ToPlayer()->UpdatePvP(true); CallScriptAfterHitHandlers(); @@ -2910,7 +2919,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->SetSpellModTakingSpell(this, true); - // Fill cost data (not use power for item casts + // Fill cost data (do not use power for item casts) m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask); if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); @@ -2986,12 +2995,17 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered } // don't allow channeled spells / spells with cast time to be cast while moving + // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) - if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) + if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && !(m_caster->IsCharmed() && m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT)) { - SendCastResult(SPELL_FAILED_MOVING); - finish(false); - return; + // 1. Is a channel spell, 2. Has no casttime, 3. And has flag to allow movement during channel + if (!(m_spellInfo->IsChanneled() && !m_casttime && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING))) + { + SendCastResult(SPELL_FAILED_MOVING); + finish(false); + return; + } } // set timer base at cast time @@ -3219,6 +3233,10 @@ void Spell::cast(bool skipCheck) return; } + if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) + if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, m_caster->GetPetGUID())) + pet->DespawnOrUnsummon(); + PrepareTriggersExecutedOnHit(); CallScriptOnCastHandlers(); @@ -3528,12 +3546,18 @@ void Spell::update(uint32 difftime) // check if the player caster has moved before the spell finished if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && - (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))) + m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && + (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))) { // don't cancel for melee, autorepeat, triggered and instant spells - if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered()) - cancel(); + if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING))) + { + // if charmed by creature, trust the AI not to cheat and allow the cast to proceed + // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now + // however, checking what type of movement the spline is for every single spline would be really expensive + if (!m_caster->GetCharmerGUID().IsCreature()) + cancel(); + } } switch (m_spellState) @@ -3840,7 +3864,7 @@ void Spell::SendSpellStart() if (schoolImmunityMask || mechanicImmunityMask) castFlags |= CAST_FLAG_IMMUNITY; - if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) + if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count) castFlags |= CAST_FLAG_PENDING; if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) @@ -3893,7 +3917,7 @@ void Spell::SendSpellGo() uint32 castFlags = CAST_FLAG_UNKNOWN_9; // triggered spells with spell visual != 0 - if ((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) + if (((IsTriggered() && !m_spellInfo->IsAutoRepeatRangedSpell()) || m_triggeredByAuraSpell) && !m_cast_count) castFlags |= CAST_FLAG_PENDING; if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) || m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NEEDS_AMMO_DATA)) @@ -4835,7 +4859,7 @@ SpellCastResult Spell::CheckCast(bool strict) // cancel autorepeat spells if cast start when moving // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature())) { // skip stuck spell to allow use it in falling case and apply spell limitations at movement if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) && @@ -5173,7 +5197,11 @@ SpellCastResult Spell::CheckCast(bool strict) return SPELL_FAILED_OUT_OF_RANGE; else if (!result || m_preGeneratedPath.GetPathType() & (PATHFIND_NOPATH | PATHFIND_INCOMPLETE)) return SPELL_FAILED_NOPATH; + else if (m_preGeneratedPath.IsInvalidDestinationZ(target)) // Check position z, if not in a straight line + return SPELL_FAILED_NOPATH; } + else if (m_preGeneratedPath.IsInvalidDestinationZ(target)) // Check position z, if in a straight line + return SPELL_FAILED_NOPATH; m_preGeneratedPath.ReducePathLenghtByDist(objSize); // move back } @@ -5289,7 +5317,7 @@ SpellCastResult Spell::CheckCast(bool strict) switch (SummonProperties->Category) { case SUMMON_CATEGORY_PET: - if (m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET case SUMMON_CATEGORY_PUPPET: @@ -5305,7 +5333,7 @@ SpellCastResult Spell::CheckCast(bool strict) { if (m_targets.GetUnitTarget()->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; - if (m_targets.GetUnitTarget()->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_targets.GetUnitTarget()->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; } break; @@ -5314,13 +5342,13 @@ SpellCastResult Spell::CheckCast(bool strict) { if (m_caster->GetPetGUID()) //let warlock do a replacement summon { - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) if (Pet* pet = m_caster->ToPlayer()->GetPet()) pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID()); } - else + else if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) return SPELL_FAILED_ALREADY_HAVE_SUMMON; } @@ -5447,7 +5475,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_CHARM || m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_POSSESS) { - if (m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; if (m_caster->GetCharmGUID()) @@ -5477,7 +5505,7 @@ SpellCastResult Spell::CheckCast(bool strict) } case SPELL_AURA_MOUNTED: { - if (m_caster->IsInWater()) + if (m_caster->IsInWater() && m_spellInfo->HasAura(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)) return SPELL_FAILED_ONLY_ABOVEWATER; // Ignore map check if spell have AreaId. AreaId already checked and this prevent special mount spells @@ -5593,7 +5621,14 @@ SpellCastResult Spell::CheckPetCast(Unit* target) m_targets.SetUnitTarget(target); } - // cooldown + // check power requirement + // this would be zero until ::prepare normally, we set it here (it gets reset in ::prepare) + m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask); + SpellCastResult failReason = CheckPower(); + if (failReason != SPELL_CAST_OK) + return failReason; + + // check cooldown if (Creature* creatureCaster = m_caster->ToCreature()) if (!creatureCaster->GetSpellHistory()->IsReady(m_spellInfo)) return SPELL_FAILED_NOT_READY; @@ -5736,6 +5771,9 @@ SpellCastResult Spell::CheckCasterAuras() const bool Spell::CanAutoCast(Unit* target) { + if (!target) + return (CheckPetCast(target) == SPELL_CAST_OK); + ObjectGuid targetguid = target->GetGUID(); // check if target already has the same or a more powerful aura @@ -5772,16 +5810,19 @@ bool Spell::CanAutoCast(Unit* target) } SpellCastResult result = CheckPetCast(target); - if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) { + // do not check targets for ground-targeted spells (we target them on top of the intended target anyway) + if (GetSpellInfo()->ExplicitTargetMask & TARGET_FLAG_DEST_LOCATION) + return true; SelectSpellTargets(); //check if among target units, our WANTED target is as well (->only self cast spells return false) - for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) if (ihit->targetGUID == targetguid) return true; } - return false; //target invalid + // either the cast failed or the intended target wouldn't be hit + return false; } SpellCastResult Spell::CheckRange(bool strict) @@ -5790,43 +5831,76 @@ SpellCastResult Spell::CheckRange(bool strict) if (!strict && m_casttime == 0) return SPELL_CAST_OK; - uint32 range_type = 0; + Unit* target = m_targets.GetUnitTarget(); + float minRange = 0.0f; + float maxRange = 0.0f; + float rangeMod = 0.0f; + if (strict && IsNextMeleeSwingSpell()) + maxRange = 100.0f; + else if (m_spellInfo->RangeEntry) + { + if (m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE) + { + rangeMod = m_caster->GetCombatReach() + 4.0f / 3.0f; + if (target) + rangeMod += target->GetCombatReach(); + else + rangeMod += m_caster->GetCombatReach(); - if (m_spellInfo->RangeEntry) - { - // check needed by 68766 51693 - both spells are cast on enemies and have 0 max range - // these are triggered by other spells - possibly we should omit range check in that case? - if (m_spellInfo->RangeEntry->ID == 1) - return SPELL_CAST_OK; + rangeMod = std::max(rangeMod, NOMINAL_MELEE_RANGE); + } + else + { + float meleeRange = 0.0f; + if (m_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED) + { + meleeRange = m_caster->GetCombatReach() + 4.0f / 3.0f; + if (target) + meleeRange += target->GetCombatReach(); + else + meleeRange += m_caster->GetCombatReach(); + + meleeRange = std::max(meleeRange, NOMINAL_MELEE_RANGE); + } + + minRange = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo) + meleeRange; + maxRange = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo); + + if (target || m_targets.GetCorpseTarget()) + { + rangeMod = m_caster->GetCombatReach(); + if (target) + rangeMod += target->GetCombatReach(); - range_type = m_spellInfo->RangeEntry->type; + if (minRange > 0.0f && !(m_spellInfo->RangeEntry->type & SPELL_RANGE_RANGED)) + minRange += rangeMod; + } + } + + if (target && m_caster->isMoving() && target->isMoving() && !m_caster->IsWalking() && !target->IsWalking() && + (m_spellInfo->RangeEntry->type & SPELL_RANGE_MELEE || target->GetTypeId() == TYPEID_PLAYER)) + rangeMod += 5.0f / 3.0f; } - Unit* target = m_targets.GetUnitTarget(); - float max_range = m_caster->GetSpellMaxRangeForTarget(target, m_spellInfo); - float min_range = m_caster->GetSpellMinRangeForTarget(target, m_spellInfo); + if (m_spellInfo->HasAttribute(SPELL_ATTR0_REQ_AMMO) && m_caster->GetTypeId() == TYPEID_PLAYER) + if (Item* ranged = m_caster->ToPlayer()->GetWeaponForAttack(RANGED_ATTACK, true)) + maxRange *= ranged->GetTemplate()->RangedModRange * 0.01f; if (Player* modOwner = m_caster->GetSpellModOwner()) - modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this); + modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, maxRange, this); + + maxRange += rangeMod; + + minRange *= minRange; + maxRange *= maxRange; if (target && target != m_caster) { - if (range_type == SPELL_RANGE_MELEE) - { - // Because of lag, we can not check too strictly here. - if (!m_caster->IsWithinMeleeRange(target, max_range)) - return SPELL_FAILED_OUT_OF_RANGE; - } - else if (!m_caster->IsWithinCombatRange(target, max_range)) - return SPELL_FAILED_OUT_OF_RANGE; //0x5A; + if (m_caster->GetExactDistSq(target) > maxRange) + return SPELL_FAILED_OUT_OF_RANGE; - if (range_type == SPELL_RANGE_RANGED) - { - if (m_caster->IsWithinMeleeRange(target)) - return SPELL_FAILED_TOO_CLOSE; - } - else if (min_range && m_caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0 - return SPELL_FAILED_TOO_CLOSE; + if (minRange > 0.0f && m_caster->GetExactDistSq(target) < minRange) + return SPELL_FAILED_OUT_OF_RANGE; if (m_caster->GetTypeId() == TYPEID_PLAYER && (m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc(static_cast<float>(M_PI), target)) @@ -5835,10 +5909,10 @@ SpellCastResult Spell::CheckRange(bool strict) if (m_targets.HasDst() && !m_targets.HasTraj()) { - if (!m_caster->IsWithinDist3d(m_targets.GetDstPos(), max_range)) - return SPELL_FAILED_OUT_OF_RANGE; - if (min_range && m_caster->IsWithinDist3d(m_targets.GetDstPos(), min_range)) - return SPELL_FAILED_TOO_CLOSE; + if (m_caster->GetExactDistSq(m_targets.GetDstPos()) > maxRange) + return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; + if (minRange > 0.0f && m_caster->GetExactDistSq(m_targets.GetDstPos()) < minRange) + return !(_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_OUT_OF_RANGE : SPELL_FAILED_DONT_REPORT; } return SPELL_CAST_OK; @@ -6928,7 +7002,7 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk reqSkillValue = lockInfo->Skill[j]; // castitem check: rogue using skeleton keys. the skill values should not be added in this case. - skillValue = m_CastItem || m_caster->GetTypeId()!= TYPEID_PLAYER ? + skillValue = m_CastItem || m_caster->GetTypeId() != TYPEID_PLAYER ? 0 : m_caster->ToPlayer()->GetSkillValue(skillId); // skill bonus provided by casting spell (mostly item spells) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index b7134283ccb..46384fc54ec 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -218,6 +218,8 @@ enum SpellEffectHandleMode typedef std::list<std::pair<uint32, ObjectGuid>> DispelList; +static const uint32 SPELL_INTERRUPT_NONPLAYER = 32747; + class TC_GAME_API Spell { friend void Unit::SetCurrentCastSpell(Spell* pSpell); diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 51b6db197db..b0e3b6deeee 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -556,7 +556,7 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) if (uint32 combo = player->GetComboPoints()) { float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK); - damage += irand(int32(ap * combo * 0.03f), int32(ap * combo * 0.07f)); + damage += std::lroundf(ap * combo * 0.07f); // Eviscerate and Envenom Bonus Damage (item set effect) if (m_caster->HasAura(37169)) @@ -596,17 +596,13 @@ void Spell::EffectSchoolDMG(SpellEffIndex effIndex) if (Player* caster = m_caster->ToPlayer()) { // Add Ammo and Weapon damage plus RAP * 0.1 - if (Item* item = caster->GetWeaponForAttack(RANGED_ATTACK)) - { - ItemTemplate const* weaponTemplate = item->GetTemplate(); - float dmg_min = weaponTemplate->Damage[0].DamageMin; - float dmg_max = weaponTemplate->Damage[0].DamageMax; - if (dmg_max == 0.0f && dmg_min > dmg_max) - damage += int32(dmg_min); - else - damage += irand(int32(dmg_min), int32(dmg_max)); - damage += int32(caster->GetAmmoDPS() * weaponTemplate->Delay * 0.001f); - } + float dmg_min = caster->GetWeaponDamageRange(RANGED_ATTACK, MINDAMAGE); + float dmg_max = caster->GetWeaponDamageRange(RANGED_ATTACK, MAXDAMAGE); + if (dmg_max == 0.0f && dmg_min > dmg_max) + damage += int32(dmg_min); + else + damage += irand(int32(dmg_min), int32(dmg_max)); + damage += int32(caster->GetAmmoDPS() * caster->GetAttackTime(RANGED_ATTACK) * 0.001f); } } break; @@ -1683,6 +1679,8 @@ void Spell::EffectCreateItem2(SpellEffIndex effIndex) } else player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell); // create some random items + + player->UpdateCraftSkill(m_spellInfo->Id); } /// @todo ExecuteLogEffectCreateItem(i, m_spellInfo->Effects[i].ItemType); } @@ -3019,6 +3017,12 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) //OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation()); //OldSummon->SetMap(owner->GetMap()); //owner->GetMap()->Add(OldSummon->ToCreature()); + if (OldSummon->getPetType() == SUMMON_PET) + { + OldSummon->SetHealth(OldSummon->GetMaxHealth()); + OldSummon->SetPower(OldSummon->getPowerType(), + OldSummon->GetMaxPower(OldSummon->getPowerType())); + } if (owner->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled()) owner->ToPlayer()->PetSpellInitialize(); @@ -3505,7 +3509,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)) + m_caster->GetPhaseMask(), Position(x, y, z, target->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY)) { delete pGameObj; return; @@ -3530,7 +3534,7 @@ void Spell::EffectSummonObjectWild(SpellEffIndex effIndex) { 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)) + m_caster->GetPhaseMask(), Position(x, y, z, target->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY)) { linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); linkedGO->SetSpellId(m_spellInfo->Id); @@ -4119,13 +4123,16 @@ void Spell::EffectDuel(SpellEffIndex effIndex) uint32 gameobject_id = m_spellInfo->Effects[effIndex].MiscValue; - Map* map = m_caster->GetMap(); - if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id, - map, m_caster->GetPhaseMask(), - m_caster->GetPositionX()+(unitTarget->GetPositionX()-m_caster->GetPositionX())/2, - m_caster->GetPositionY()+(unitTarget->GetPositionY()-m_caster->GetPositionY())/2, + Position const pos = + { + m_caster->GetPositionX() + (unitTarget->GetPositionX() - m_caster->GetPositionX()) / 2, + m_caster->GetPositionY() + (unitTarget->GetPositionY() - m_caster->GetPositionY()) / 2, m_caster->GetPositionZ(), - m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY)) + m_caster->GetOrientation() + }; + + Map* map = m_caster->GetMap(); + if (!pGameObj->Create(map->GenerateLowGuid<HighGuid::GameObject>(), gameobject_id, map, m_caster->GetPhaseMask(), pos, G3D::Quat(), 0, GO_STATE_READY)) { delete pGameObj; return; @@ -4458,7 +4465,7 @@ void Spell::EffectSummonObject(SpellEffIndex effIndex) Map* map = m_caster->GetMap(); if (!go->Create(map->GenerateLowGuid<HighGuid::GameObject>(), go_id, map, - m_caster->GetPhaseMask(), x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY)) + m_caster->GetPhaseMask(), Position(x, y, z, m_caster->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY)) { delete go; return; @@ -5087,7 +5094,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) GameObject* pGameObj = new GameObject; if (!pGameObj->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), name_id, cMap, - m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY)) + m_caster->GetPhaseMask(), Position(fx, fy, fz, m_caster->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY)) { delete pGameObj; return; @@ -5153,7 +5160,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) { GameObject* linkedGO = new GameObject; if (linkedGO->Create(cMap->GenerateLowGuid<HighGuid::GameObject>(), linkedEntry, cMap, - m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY)) + m_caster->GetPhaseMask(), Position(fx, fy, fz, m_caster->GetOrientation()), G3D::Quat(), 255, GO_STATE_READY)) { linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILLISECONDS : 0); //linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel()); @@ -5475,7 +5482,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 l = 0; l + 1 < MAX_RUNES && count > 0; ++l) { // Check if both runes are on cd as that is the only time when this needs to come into effect - if ((player->GetRuneCooldown(l) && player->GetCurrentRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetCurrentRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB))) + if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetBaseRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB))) { // Should always update the rune with the lowest cd if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l+1)) diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index f6078bd063f..ac157b48783 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -525,10 +525,10 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const { - float multiplier = DamageMultiplier; + float multiplierPercent = DamageMultiplier * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplier, spell); - return multiplier; + modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplierPercent, spell); + return multiplierPercent / 100.0f; } bool SpellEffectInfo::HasRadius() const diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 50f750f2ca5..ed0d95f0a58 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -955,11 +955,11 @@ SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const { // proc type doesn't match - if (!(eventInfo.GetTypeMask() & procEntry.typeMask)) + if (!(eventInfo.GetTypeMask() & procEntry.ProcFlags)) return false; // check XP or honor target requirement - if (procEntry.attributesMask & PROC_ATTR_REQ_EXP_OR_HONOR) + if (procEntry.AttributesMask & PROC_ATTR_REQ_EXP_OR_HONOR) if (Player* actor = eventInfo.GetActor()->ToPlayer()) if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget())) return false; @@ -969,7 +969,7 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE return true; // check school mask (if set) for other trigger types - if (procEntry.schoolMask && !(eventInfo.GetSchoolMask() & procEntry.schoolMask)) + if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask)) return false; // check spell family name/flags (if set) for spells @@ -977,31 +977,31 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE { SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo(); - if (procEntry.spellFamilyName && eventSpellInfo && (procEntry.spellFamilyName != eventSpellInfo->SpellFamilyName)) + if (procEntry.SpellFamilyName && eventSpellInfo && (procEntry.SpellFamilyName != eventSpellInfo->SpellFamilyName)) return false; - if (procEntry.spellFamilyMask && eventSpellInfo && !(procEntry.spellFamilyMask & eventSpellInfo->SpellFamilyFlags)) + if (procEntry.SpellFamilyMask && eventSpellInfo && !(procEntry.SpellFamilyMask & eventSpellInfo->SpellFamilyFlags)) return false; } // check spell type mask (if set) if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)) { - if (procEntry.spellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.spellTypeMask)) + if (procEntry.SpellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.SpellTypeMask)) return false; } // check spell phase mask if (eventInfo.GetTypeMask() & REQ_SPELL_PHASE_PROC_FLAG_MASK) { - if (!(eventInfo.GetSpellPhaseMask() & procEntry.spellPhaseMask)) + if (!(eventInfo.GetSpellPhaseMask() & procEntry.SpellPhaseMask)) return false; } // check hit mask (on taken hit or on done hit, but not on spell cast phase) if ((eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) || ((eventInfo.GetTypeMask() & DONE_HIT_PROC_FLAG_MASK) && !(eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST))) { - uint32 hitMask = procEntry.hitMask; + uint32 hitMask = procEntry.HitMask; // get default values if hit mask not set if (!hitMask) { @@ -1929,8 +1929,11 @@ void SpellMgr::LoadSpellProcs() mSpellProcMap.clear(); // need for reload case - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - QueryResult result = WorldDatabase.Query("SELECT spellId, schoolMask, spellFamilyName, spellFamilyMask0, spellFamilyMask1, spellFamilyMask2, typeMask, spellTypeMask, spellPhaseMask, hitMask, attributesMask, ratePerMinute, chance, cooldown, charges FROM spell_proc"); + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, " + // 6 7 8 9 10 11 12 13 14 + "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc"); + if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty."); @@ -1972,21 +1975,20 @@ void SpellMgr::LoadSpellProcs() SpellProcEntry baseProcEntry; - baseProcEntry.schoolMask = fields[1].GetInt8(); - baseProcEntry.spellFamilyName = fields[2].GetUInt16(); - baseProcEntry.spellFamilyMask[0] = fields[3].GetUInt32(); - baseProcEntry.spellFamilyMask[1] = fields[4].GetUInt32(); - baseProcEntry.spellFamilyMask[2] = fields[5].GetUInt32(); - baseProcEntry.typeMask = fields[6].GetUInt32(); - baseProcEntry.spellTypeMask = fields[7].GetUInt32(); - baseProcEntry.spellPhaseMask = fields[8].GetUInt32(); - baseProcEntry.hitMask = fields[9].GetUInt32(); - baseProcEntry.attributesMask = fields[10].GetUInt32(); - baseProcEntry.ratePerMinute = fields[11].GetFloat(); - baseProcEntry.chance = fields[12].GetFloat(); - float cooldown = fields[13].GetFloat(); - baseProcEntry.cooldown = uint32(cooldown); - baseProcEntry.charges = fields[14].GetUInt32(); + baseProcEntry.SchoolMask = fields[1].GetInt8(); + baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); + baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); + baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); + baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); + baseProcEntry.ProcFlags = fields[6].GetUInt32(); + baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); + baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); + baseProcEntry.HitMask = fields[9].GetUInt32(); + baseProcEntry.AttributesMask = fields[10].GetUInt32(); + baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); + baseProcEntry.Chance = fields[12].GetFloat(); + baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); + baseProcEntry.Charges = fields[14].GetUInt8(); while (spellInfo) { @@ -1999,56 +2001,46 @@ void SpellMgr::LoadSpellProcs() SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); // take defaults from dbcs - if (!procEntry.typeMask) - procEntry.typeMask = spellInfo->ProcFlags; - if (!procEntry.charges) - procEntry.charges = spellInfo->ProcCharges; - if (!procEntry.chance && !procEntry.ratePerMinute) - procEntry.chance = float(spellInfo->ProcChance); + if (!procEntry.ProcFlags) + procEntry.ProcFlags = spellInfo->ProcFlags; + if (!procEntry.Charges) + procEntry.Charges = spellInfo->ProcCharges; + if (!procEntry.Chance && !procEntry.ProcsPerMinute) + procEntry.Chance = float(spellInfo->ProcChance); // validate data - if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `schoolMask` set: %u", spellInfo->Id, procEntry.schoolMask); - if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16)) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `spellFamilyName` set: %u", spellInfo->Id, procEntry.spellFamilyName); - if (procEntry.chance < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `chance` field", spellInfo->Id); - procEntry.chance = 0; - } - if (procEntry.ratePerMinute < 0) + if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); + if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); + if (procEntry.Chance < 0) { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ratePerMinute` field", spellInfo->Id); - procEntry.ratePerMinute = 0; + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); + procEntry.Chance = 0; } - if (cooldown < 0) + if (procEntry.ProcsPerMinute < 0) { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `cooldown` field", spellInfo->Id); - procEntry.cooldown = 0; + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); + procEntry.ProcsPerMinute = 0; } - if (procEntry.chance == 0 && procEntry.ratePerMinute == 0) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `chance` and `ratePerMinute` values defined, proc will not be triggered", spellInfo->Id); - if (procEntry.charges > 99) - { - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a too big `charges` field value.", spellInfo->Id); - procEntry.charges = 99; - } - if (!procEntry.typeMask) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `typeMask` value defined, proc will not be triggered.", spellInfo->Id); - if (procEntry.spellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellInfo->Id, procEntry.spellTypeMask); - if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `spellTypeMask` value defined, but it will not be used for the defined `typeMask` value.", spellInfo->Id); - if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `spellPhaseMask` value defined, but it is required for the defined `typeMask` value. Proc will not be triggered.", spellInfo->Id); - if (procEntry.spellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellInfo->Id, procEntry.spellPhaseMask); - if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK)) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `spellPhaseMask` value defined, but it will not be used for the defined `typeMask` value.", spellInfo->Id); - if (procEntry.hitMask & ~PROC_HIT_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", spellInfo->Id, procEntry.hitMask); - if (procEntry.hitMask && !(procEntry.typeMask & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.typeMask & DONE_HIT_PROC_FLAG_MASK && (!procEntry.spellPhaseMask || procEntry.spellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `hitMask` value defined, but it will not be used for defined `typeMask` and `spellPhaseMask` values.", spellInfo->Id); + if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellInfo->Id); + if (!procEntry.ProcFlags) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); + if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); + if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); + if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); mSpellProcMap[spellInfo->Id] = procEntry; @@ -3081,6 +3073,13 @@ void SpellMgr::LoadSpellInfoCorrections() case 53385: // Divine Storm (Damage) spellInfo->MaxAffectedTargets = 4; break; + case 53480: // Roar of Sacrifice + // missing spell effect 2 data, taken from 4.3.4 + spellInfo->Effects[EFFECT_1].Effect = SPELL_EFFECT_APPLY_AURA; + spellInfo->Effects[EFFECT_1].ApplyAuraName = SPELL_AURA_DUMMY; + spellInfo->Effects[EFFECT_1].MiscValue = 127; + spellInfo->Effects[EFFECT_1].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_TARGET_ALLY); + break; case 42005: // Bloodboil case 38296: // Spitfire Totem case 37676: // Insidious Whisper @@ -3346,6 +3345,24 @@ void SpellMgr::LoadSpellInfoCorrections() //! HACK: This spell break quest complete for alliance and on retail not used °_O spellInfo->Effects[EFFECT_0].Effect = 0; break; + case 47476: // Deathknight - Strangulate + case 15487: // Priest - Silence + case 5211: // Druid - Bash - R1 + case 6798: // Druid - Bash - R2 + case 8983: // Druid - Bash - R3 + spellInfo->AttributesEx7 |= SPELL_ATTR7_INTERRUPT_ONLY_NONPLAYER; + break; + case 42490: // Energized! + case 42492: // Cast Energized + spellInfo->AttributesEx |= SPELL_ATTR1_NO_THREAT; + break; + case 46842: // Flame Ring + case 46836: // Flame Patch + spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(); + break; + case 29726: // Test Ribbon Pole Channel + spellInfo->InterruptFlags &= ~AURA_INTERRUPT_FLAG_CAST; + break; // VIOLET HOLD SPELLS // case 54258: // Water Globule (Ichoron) @@ -3704,9 +3721,17 @@ void SpellMgr::LoadSpellInfoCorrections() spellInfo->AttributesEx6 |= SPELL_ATTR6_CAN_TARGET_INVISIBLE; spellInfo->AttributesEx2 |= SPELL_ATTR2_CAN_TARGET_NOT_IN_LOS; break; - case 75888: // Awaken Flames - case 75889: // Awaken Flames - spellInfo->AttributesEx |= SPELL_ATTR1_CANT_TARGET_SELF; + case 75875: // Combustion and Consumption Heroic versions lacks radius data + spellInfo->Effects[EFFECT_0].Mechanic = MECHANIC_NONE; + spellInfo->Effects[EFFECT_1].Mechanic = MECHANIC_SNARE; + spellInfo->Effects[EFFECT_1].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_6_YARDS); + break; + case 75884: + spellInfo->Effects[EFFECT_0].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_6_YARDS); + // No break + case 75883: + case 75876: + spellInfo->Effects[EFFECT_1].RadiusEntry = sSpellRadiusStore.LookupEntry(EFFECT_RADIUS_6_YARDS); break; // ENDOF RUBY SANCTUM SPELLS // @@ -3755,12 +3780,6 @@ void SpellMgr::LoadSpellInfoCorrections() case 24314: // Threatening Gaze spellInfo->AuraInterruptFlags |= AURA_INTERRUPT_FLAG_CAST | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_JUMP; break; - case 45257: // Using Steam Tonk Controller - case 45440: // Steam Tonk Controller - case 60256: // Collect Sample - // Crashes client on pressing ESC - spellInfo->AttributesEx4 &= ~SPELL_ATTR4_CAN_CAST_WHILE_CASTING; - break; // ISLE OF CONQUEST SPELLS // case 66551: // Teleport diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index fea7513b092..75da933636c 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -266,7 +266,7 @@ enum ProcFlagsHit PROC_HIT_REFLECT = 0x0000800, PROC_HIT_INTERRUPT = 0x0001000, // (not used atm) PROC_HIT_FULL_BLOCK = 0x0002000, - PROC_HIT_MASK_ALL = 0x2FFF + PROC_HIT_MASK_ALL = 0x0002FFF }; enum ProcAttributes @@ -290,18 +290,18 @@ typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap; struct SpellProcEntry { - uint32 schoolMask; // if nonzero - bitmask for matching proc condition based on spell's school - uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName - flag96 spellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags - uint32 typeMask; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags - uint32 spellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType - uint32 spellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase - uint32 hitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit - uint32 attributesMask; // bitmask, see ProcAttributes - float ratePerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60 - float chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if perMinuteRate set - uint32 cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura - uint32 charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite + uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school + uint32 SpellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName + flag96 SpellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags + uint32 ProcFlags; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags + uint32 SpellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType + uint32 SpellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase + uint32 HitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit + uint32 AttributesMask; // bitmask, see ProcAttributes + float ProcsPerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60 + float Chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if ProcsPerMinute set + Milliseconds Cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura + uint32 Charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite }; typedef std::unordered_map<uint32, SpellProcEntry> SpellProcMap; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index d84fe11383f..19abfbe3152 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -58,6 +58,7 @@ #include "SkillDiscovery.h" #include "SkillExtraItems.h" #include "SmartAI.h" +#include "Metric.h" #include "TicketMgr.h" #include "TransportMgr.h" #include "Unit.h" @@ -396,6 +397,7 @@ void World::LoadConfigSettings(bool reload) return; } sLog->LoadFromConfig(); + sMetric->LoadFromConfigs(); } ///- Read the player limit and the Message of the day from the config file @@ -593,7 +595,10 @@ void World::LoadConfigSettings(bool reload) } m_int_configs[CONFIG_CHAT_CHANNEL_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Channel", 1); m_int_configs[CONFIG_CHAT_WHISPER_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Whisper", 1); + m_int_configs[CONFIG_CHAT_EMOTE_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Emote", 1); m_int_configs[CONFIG_CHAT_SAY_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Say", 1); + m_int_configs[CONFIG_CHAT_YELL_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Yell", 1); + m_int_configs[CONFIG_PARTY_LEVEL_REQ] = sConfigMgr->GetIntDefault("PartyLevelReq", 1); m_int_configs[CONFIG_TRADE_LEVEL_REQ] = sConfigMgr->GetIntDefault("LevelReq.Trade", 1); m_int_configs[CONFIG_TICKET_LEVEL_REQ] = sConfigMgr->GetIntDefault("LevelReq.Ticket", 1); m_int_configs[CONFIG_AUCTION_LEVEL_REQ] = sConfigMgr->GetIntDefault("LevelReq.Auction", 1); @@ -659,9 +664,8 @@ void World::LoadConfigSettings(bool reload) m_float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetFloatDefault("MaxGroupXPDistance", 74.0f); m_float_configs[CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE] = sConfigMgr->GetFloatDefault("MaxRecruitAFriendBonusDistance", 100.0f); - /// @todo Add MonsterSight and GuarderSight (with meaning) in worldserver.conf or put them as define + /// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define m_float_configs[CONFIG_SIGHT_MONSTER] = sConfigMgr->GetFloatDefault("MonsterSight", 50.0f); - m_float_configs[CONFIG_SIGHT_GUARDER] = sConfigMgr->GetFloatDefault("GuarderSight", 50.0f); if (reload) { @@ -1059,6 +1063,17 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfigMgr->GetBoolDefault("Battleground.QueueAnnouncer.PlayerOnly", false); m_bool_configs[CONFIG_BATTLEGROUND_STORE_STATISTICS_ENABLE] = sConfigMgr->GetBoolDefault("Battleground.StoreStatistics.Enable", false); m_bool_configs[CONFIG_BATTLEGROUND_TRACK_DESERTERS] = sConfigMgr->GetBoolDefault("Battleground.TrackDeserters.Enable", false); + m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = sConfigMgr->GetIntDefault("Battleground.ReportAFK", 3); + if (m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] < 1) + { + TC_LOG_ERROR("server.loading", "Battleground.ReportAFK (%d) must be >0. Using 3 instead.", m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK]); + m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = 3; + } + if (m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] > 9) + { + TC_LOG_ERROR("server.loading", "Battleground.ReportAFK (%d) must be <10. Using 3 instead.", m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK]); + m_int_configs[CONFIG_BATTLEGROUND_REPORT_AFK] = 3; + } m_int_configs[CONFIG_BATTLEGROUND_INVITATION_TYPE] = sConfigMgr->GetIntDefault ("Battleground.InvitationType", 0); m_int_configs[CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER] = sConfigMgr->GetIntDefault ("Battleground.PrematureFinishTimer", 5 * MINUTE * IN_MILLISECONDS); m_int_configs[CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH] = sConfigMgr->GetIntDefault ("Battleground.PremadeGroupWaitForMatch", 30 * MINUTE * IN_MILLISECONDS); @@ -1330,6 +1345,9 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_HOTSWAP_INSTALL_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableInstall", true); m_bool_configs[CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnablePrefixCorrection", true); + // prevent character rename on character customization + m_bool_configs[CONFIG_PREVENT_RENAME_CUSTOMIZATION] = sConfigMgr->GetBoolDefault("PreventRenameCharacterOnCustomization", false); + // call ScriptMgr if we're reloading the configuration if (reload) sScriptMgr->OnConfigLoad(reload); @@ -1938,6 +1956,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); } @@ -2252,6 +2272,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() @@ -2877,16 +2901,20 @@ void World::_UpdateRealmCharCount(PreparedQueryResult resultCharCount) uint32 accountId = fields[0].GetUInt32(); uint8 charCount = uint8(fields[1].GetUInt64()); + SQLTransaction trans = LoginDatabase.BeginTransaction(); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_DEL_REALM_CHARACTERS_BY_REALM); stmt->setUInt32(0, accountId); stmt->setUInt32(1, realm.Id.Realm); - LoginDatabase.Execute(stmt); + trans->Append(stmt); stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_REALM_CHARACTERS); stmt->setUInt8(0, charCount); stmt->setUInt32(1, accountId); stmt->setUInt32(2, realm.Id.Realm); - LoginDatabase.Execute(stmt); + trans->Append(stmt); + + LoginDatabase.CommitTransaction(trans); } } diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index 330e78cf510..5850063bdc8 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -175,6 +175,7 @@ enum WorldBoolConfigs CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED, CONFIG_HOTSWAP_INSTALL_ENABLED, CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED, + CONFIG_PREVENT_RENAME_CUSTOMIZATION, BOOL_CONFIG_VALUE_COUNT }; @@ -183,7 +184,6 @@ enum WorldFloatConfigs CONFIG_GROUP_XP_DISTANCE = 0, CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE, CONFIG_SIGHT_MONSTER, - CONFIG_SIGHT_GUARDER, CONFIG_LISTEN_RANGE_SAY, CONFIG_LISTEN_RANGE_TEXTEMOTE, CONFIG_LISTEN_RANGE_YELL, @@ -281,7 +281,10 @@ enum WorldIntConfigs CONFIG_CHAT_STRICT_LINK_CHECKING_KICK, CONFIG_CHAT_CHANNEL_LEVEL_REQ, CONFIG_CHAT_WHISPER_LEVEL_REQ, + CONFIG_CHAT_EMOTE_LEVEL_REQ, CONFIG_CHAT_SAY_LEVEL_REQ, + CONFIG_CHAT_YELL_LEVEL_REQ, + CONFIG_PARTY_LEVEL_REQ, CONFIG_TRADE_LEVEL_REQ, CONFIG_TICKET_LEVEL_REQ, CONFIG_AUCTION_LEVEL_REQ, @@ -297,6 +300,7 @@ enum WorldIntConfigs CONFIG_BATTLEGROUND_INVITATION_TYPE, CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER, CONFIG_BATTLEGROUND_PREMADE_GROUP_WAIT_FOR_MATCH, + CONFIG_BATTLEGROUND_REPORT_AFK, CONFIG_ARENA_MAX_RATING_DIFFERENCE, CONFIG_ARENA_RATING_DISCARD_TIMER, CONFIG_ARENA_RATED_UPDATE_TIMER, diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index ca4dd814e01..570f4587ae8 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -35,8 +35,10 @@ EndScriptData */ #include "Language.h" #include "MapManager.h" #include "M2Stores.h" +#include "BattlefieldMgr.h" #include <fstream> +#include <limits> class debug_commandscript : public CommandScript { @@ -95,7 +97,8 @@ public: { "transport", rbac::RBAC_PERM_COMMAND_DEBUG_TRANSPORT, false, &HandleDebugTransportCommand, "" }, { "loadcells", rbac::RBAC_PERM_COMMAND_DEBUG_LOADCELLS, false, &HandleDebugLoadCellsCommand, "" }, { "boundary", rbac::RBAC_PERM_COMMAND_DEBUG_BOUNDARY, false, &HandleDebugBoundaryCommand, "" }, - { "raidreset", rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, false, &HandleDebugRaidResetCommand, "" } + { "raidreset", rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, false, &HandleDebugRaidResetCommand, "" }, + { "neargraveyard", rbac::RBAC_PERM_COMMAND_NEARGRAVEYARD, false, &HandleDebugNearGraveyard, "" }, }; static std::vector<ChatCommand> commandTable = { @@ -1485,6 +1488,53 @@ public: sInstanceSaveMgr->ForceGlobalReset(mEntry->MapID, Difficulty(difficulty)); return true; } + + static bool HandleDebugNearGraveyard(ChatHandler* handler, char const* args) + { + Player* player = handler->GetSession()->GetPlayer(); + const WorldSafeLocsEntry* nearestLoc = nullptr; + + if (stricmp(args, "linked")) + { + if (Battleground* bg = player->GetBattleground()) + nearestLoc = bg->GetClosestGraveYard(player); + else + { + if (Battlefield* bf = sBattlefieldMgr->GetBattlefieldToZoneId(player->GetZoneId())) + nearestLoc = bf->GetClosestGraveYard(player); + else + nearestLoc = sObjectMgr->GetClosestGraveYard(player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam()); + } + } + else + { + float x = player->GetPositionX(); + float y = player->GetPositionY(); + float z = player->GetPositionZ(); + float distNearest = std::numeric_limits<float>::max(); + + for (uint32 i = 0; i < sWorldSafeLocsStore.GetNumRows(); ++i) + { + const WorldSafeLocsEntry* loc = sWorldSafeLocsStore.LookupEntry(i); + if (loc && loc->map_id == player->GetMapId()) + { + float dist = (loc->x - x) * (loc->x - x) + (loc->y - y) * (loc->y - y) + (loc->z - z) * (loc->z - z); + if (dist < distNearest) + { + distNearest = dist; + nearestLoc = loc; + } + } + } + } + + if (nearestLoc) + handler->PSendSysMessage(LANG_COMMAND_NEARGRAVEYARD, nearestLoc->ID, nearestLoc->x, nearestLoc->y, nearestLoc->z); + else + handler->PSendSysMessage(LANG_COMMAND_NEARGRAVEYARD_NOTFOUND); + + return true; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_gobject.cpp b/src/server/scripts/Commands/cs_gobject.cpp index 729dac0ad6f..c4ed6f650b9 100644 --- a/src/server/scripts/Commands/cs_gobject.cpp +++ b/src/server/scripts/Commands/cs_gobject.cpp @@ -139,16 +139,12 @@ public: } Player* player = handler->GetSession()->GetPlayer(); - float x = float(player->GetPositionX()); - float y = float(player->GetPositionY()); - float z = float(player->GetPositionZ()); - float o = float(player->GetOrientation()); Map* map = player->GetMap(); GameObject* object = new GameObject; ObjectGuid::LowType guidLow = map->GenerateLowGuid<HighGuid::GameObject>(); - if (!object->Create(guidLow, objectInfo->entry, map, player->GetPhaseMaskForSpawn(), x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY)) + if (!object->Create(guidLow, objectInfo->entry, map, player->GetPhaseMaskForSpawn(), *player, G3D::Quat(), 255, GO_STATE_READY)) { delete object; return false; @@ -179,7 +175,7 @@ public: /// @todo is it really necessary to add both the real and DB table guid here ? sObjectMgr->AddGameobjectToGrid(guidLow, sObjectMgr->GetGOData(guidLow)); - handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, x, y, z); + handler->PSendSysMessage(LANG_GAMEOBJECT_ADD, objectId, objectInfo->name.c_str(), guidLow, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); return true; } @@ -201,14 +197,7 @@ public: if (spawntime) spawntm = atoi((char*)spawntime); - float x = player->GetPositionX(); - float y = player->GetPositionY(); - float z = player->GetPositionZ(); - float ang = player->GetOrientation(); - - float rot2 = std::sin(ang/2); - float rot3 = std::cos(ang/2); - + G3D::Quat rotation = G3D::Matrix3::fromEulerAnglesZYX(player->GetOrientation(), 0.f, 0.f); uint32 objectId = atoi(id); if (!sObjectMgr->GetGameObjectTemplate(objectId)) @@ -218,7 +207,7 @@ public: return false; } - player->SummonGameObject(objectId, x, y, z, ang, 0, 0, rot2, rot3, spawntm); + player->SummonGameObject(objectId, *player, rotation, spawntm); return true; } @@ -415,20 +404,30 @@ public: } char* orientation = strtok(NULL, " "); - float o; + float oz = 0.f, oy = 0.f, ox = 0.f; if (orientation) - o = (float)atof(orientation); + { + oz = float(atof(orientation)); + + orientation = strtok(NULL, " "); + if (orientation) + { + oy = float(atof(orientation)); + orientation = strtok(NULL, " "); + if (orientation) + ox = float(atof(orientation)); + } + } else { Player* player = handler->GetSession()->GetPlayer(); - o = player->GetOrientation(); + oz = player->GetOrientation(); } Map* map = object->GetMap(); - - object->Relocate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), o); - object->UpdateRotationFields(); + object->Relocate(object->GetPositionX(), object->GetPositionY(), object->GetPositionZ()); + object->SetWorldRotationAngles(oz, oy, ox); object->SaveToDB(); // Generate a completely new spawn with new guid diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index a98f9f4bf9c..16217fbaea6 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -703,7 +703,7 @@ public: static bool HandleCooldownCommand(ChatHandler* handler, char const* args) { - Player* target = handler->getSelectedPlayerOrSelf(); + Unit* target = handler->getSelectedUnit(); if (!target) { handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); @@ -711,7 +711,14 @@ public: return false; } - std::string nameLink = handler->GetNameLink(target); + Player* owner = target->GetCharmerOrOwnerPlayerOrPlayerItself(); + if (!owner) + { + owner = handler->GetSession()->GetPlayer(); + target = owner; + } + + std::string nameLink = handler->GetNameLink(owner); if (!*args) { @@ -728,13 +735,13 @@ public: SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellIid); if (!spellInfo) { - handler->PSendSysMessage(LANG_UNKNOWN_SPELL, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); + handler->PSendSysMessage(LANG_UNKNOWN_SPELL, owner == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); handler->SetSentErrorMessage(true); return false; } target->GetSpellHistory()->ResetCooldown(spellIid, true); - handler->PSendSysMessage(LANG_REMOVE_COOLDOWN, spellIid, target == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); + handler->PSendSysMessage(LANG_REMOVE_COOLDOWN, spellIid, owner == handler->GetSession()->GetPlayer() ? handler->GetTrinityString(LANG_YOU) : nameLink.c_str()); } return true; } @@ -2124,17 +2131,9 @@ public: } return true; } - /* - ComeToMe command REQUIRED for 3rd party scripting library to have access to PointMovementGenerator - Without this function 3rd party scripting library will get linking errors (unresolved external) - when attempting to use the PointMovementGenerator - */ - static bool HandleComeToMeCommand(ChatHandler* handler, char const* args) - { - char const* newFlagStr = strtok((char*)args, " "); - if (!newFlagStr) - return false; + static bool HandleComeToMeCommand(ChatHandler* handler, char const* /*args*/) + { Creature* caster = handler->getSelectedCreature(); if (!caster) { diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 438d05c5687..38aa96a6a66 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -113,7 +113,7 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] = { CREATE_NAMED_ENUM(UNIT_FLAG_SERVER_CONTROLLED), CREATE_NAMED_ENUM(UNIT_FLAG_NON_ATTACKABLE), - CREATE_NAMED_ENUM(UNIT_FLAG_DISABLE_MOVE), + CREATE_NAMED_ENUM(UNIT_FLAG_REMOVE_CLIENT_CONTROL), CREATE_NAMED_ENUM(UNIT_FLAG_PVP_ATTACKABLE), CREATE_NAMED_ENUM(UNIT_FLAG_RENAME), CREATE_NAMED_ENUM(UNIT_FLAG_PREPARATION), @@ -125,7 +125,7 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] = CREATE_NAMED_ENUM(UNIT_FLAG_PET_IN_COMBAT), CREATE_NAMED_ENUM(UNIT_FLAG_PVP), CREATE_NAMED_ENUM(UNIT_FLAG_SILENCED), - CREATE_NAMED_ENUM(UNIT_FLAG_UNK_14), + CREATE_NAMED_ENUM(UNIT_FLAG_CANNOT_SWIM), CREATE_NAMED_ENUM(UNIT_FLAG_UNK_15), CREATE_NAMED_ENUM(UNIT_FLAG_UNK_16), CREATE_NAMED_ENUM(UNIT_FLAG_PACIFIED), diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp index 787265cc19b..a86de766117 100644 --- a/src/server/scripts/Commands/cs_pet.cpp +++ b/src/server/scripts/Commands/cs_pet.cpp @@ -69,10 +69,10 @@ public: } CreatureTemplate const* creatureTemplate = creatureTarget->GetCreatureTemplate(); - // Creatures with family 0 crashes the server - if (!creatureTemplate->family) + // Creatures with family CREATURE_FAMILY_NONE crashes the server + if (creatureTemplate->family == CREATURE_FAMILY_NONE) { - handler->PSendSysMessage("This creature cannot be tamed. (family id: 0)."); + handler->PSendSysMessage("This creature cannot be tamed. Family id: 0 (CREATURE_FAMILY_NONE)."); handler->SetSentErrorMessage(true); return false; } diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index eb28a8adae4..4470fa7de42 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -91,7 +91,7 @@ public: { "disenchant_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesDisenchantCommand, "" }, { "event_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS, true, &HandleReloadEventScriptsCommand, "" }, { "fishing_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesFishingCommand, "" }, - { "game_graveyard_zone", rbac::RBAC_PERM_COMMAND_RELOAD_GAME_GRAVEYARD_ZONE, true, &HandleReloadGameGraveyardZoneCommand, "" }, + { "graveyard_zone", rbac::RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE, true, &HandleReloadGameGraveyardZoneCommand, "" }, { "game_tele", rbac::RBAC_PERM_COMMAND_RELOAD_GAME_TELE, true, &HandleReloadGameTeleCommand, "" }, { "gameobject_questender", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER, true, &HandleReloadGOQuestEnderCommand, "" }, { "gameobject_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesGameobjectCommand, "" }, diff --git a/src/server/scripts/Commands/cs_ticket.cpp b/src/server/scripts/Commands/cs_ticket.cpp index 899c5615206..26f712eb8ef 100644 --- a/src/server/scripts/Commands/cs_ticket.cpp +++ b/src/server/scripts/Commands/cs_ticket.cpp @@ -199,7 +199,12 @@ public: ticket->SaveToDB(trans); sTicketMgr->UpdateLastChange(); - std::string msg = ticket->FormatMessageString(*handler, NULL, ticket->GetAssignedToName().c_str(), NULL, NULL, NULL); + std::string msg = [&] { + std::string const assignedName = ticket->GetAssignedToName(); + return ticket->FormatMessageString(*handler, nullptr, + assignedName.empty() ? nullptr : assignedName.c_str(), nullptr, nullptr, nullptr); + }(); + msg += handler->PGetParseString(LANG_COMMAND_TICKETLISTADDCOMMENT, player ? player->GetName().c_str() : "Console", comment); handler->SendGlobalGMSysMessage(msg.c_str()); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp index 817aaf0a253..f962a019da6 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp @@ -187,6 +187,12 @@ public: events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(17000, 19000)); } + void IsSummonedBy(Unit* /*summoner*/) override + { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); + DoZoneInCombat(); + } + void JustDied(Unit* /*killer*/) override { _JustDied(); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp index 4a6fee12098..995fc44abd0 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_nefarian.cpp @@ -206,9 +206,10 @@ public: Talk(SAY_GAMESBEGIN_2); me->setFaction(103); - me->SetUInt32Value(UNIT_NPC_FLAGS, 0); + me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); DoCast(me, SPELL_NEFARIANS_BARRIER); me->SetStandState(UNIT_STAND_STATE_STAND); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); AttackStart(target); events.ScheduleEvent(EVENT_SHADOW_BOLT, urand(3000, 10000)); events.ScheduleEvent(EVENT_FEAR, urand(10000, 20000)); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp index e58bded801b..e97b7cba388 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingLair/boss_razorgore.cpp @@ -32,6 +32,7 @@ enum Say enum Spells { + // @todo orb uses the wrong spell, this needs sniffs SPELL_MINDCONTROL = 42013, SPELL_CHANNEL = 45537, SPELL_EGG_DESTROY = 19873, @@ -103,7 +104,7 @@ public: secondPhase = true; me->RemoveAllAuras(); - me->SetHealth(me->GetMaxHealth()); + me->SetFullHealth(); } void DoAction(int32 action) override @@ -114,6 +115,7 @@ public: void DamageTaken(Unit* /*who*/, uint32& damage) override { + // @todo this is wrong - razorgore should still take damage, he should just nuke the whole room and respawn if he dies during P1 if (!secondPhase) damage = 0; } @@ -146,6 +148,7 @@ public: break; case EVENT_CONFLAGRATION: DoCastVictim(SPELL_CONFLAGRATION); + // @todo is this even necessary? pretty sure AI ignores targets with disorient by default if (me->GetVictim() && me->EnsureVictim()->HasAura(SPELL_CONFLAGRATION)) if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100, true)) me->TauntApply(target); @@ -175,10 +178,10 @@ public: { if (InstanceScript* instance = go->GetInstanceScript()) if (instance->GetData(DATA_EGG_EVENT) != DONE) - if (Creature* razor = instance->GetCreature(DATA_RAZORGORE_THE_UNTAMED)) + if (Creature* razorgore = instance->GetCreature(DATA_RAZORGORE_THE_UNTAMED)) { - razor->Attack(player, true); - player->CastSpell(razor, SPELL_MINDCONTROL); + razorgore->Attack(player, true); + player->CastSpell(razorgore, SPELL_MINDCONTROL); } return true; } diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp index 9b487f7b5f9..8cff67d9f28 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_majordomo_executus.cpp @@ -44,16 +44,20 @@ enum Texts enum Spells { - SPELL_MAGIC_REFLECTION = 20619, - SPELL_DAMAGE_REFLECTION = 21075, + SPELL_SUMMON_RAGNAROS = 19774, SPELL_BLAST_WAVE = 20229, - SPELL_AEGIS_OF_RAGNAROS = 20620, SPELL_TELEPORT = 20618, - SPELL_SUMMON_RAGNAROS = 19774, + SPELL_MAGIC_REFLECTION = 20619, + SPELL_AEGIS_OF_RAGNAROS = 20620, + SPELL_DAMAGE_REFLECTION = 21075 }; -#define GOSSIP_HELLO 4995 -#define GOSSIP_SELECT "Tell me more." +enum Extras +{ + OPTION_ID_YOU_CHALLENGED_US = 0, + FACTION_FRIENDLY = 35, + MENU_OPTION_YOU_CHALLENGED_US = 4108 +}; enum Events { @@ -64,7 +68,7 @@ enum Events EVENT_OUTRO_1 = 5, EVENT_OUTRO_2 = 6, - EVENT_OUTRO_3 = 7, + EVENT_OUTRO_3 = 7 }; class boss_majordomo : public CreatureScript @@ -106,7 +110,7 @@ class boss_majordomo : public CreatureScript if (!me->FindNearestCreature(NPC_FLAMEWAKER_HEALER, 100.0f) && !me->FindNearestCreature(NPC_FLAMEWAKER_ELITE, 100.0f)) { instance->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, me->GetEntry(), me); - me->setFaction(35); + me->setFaction(FACTION_FRIENDLY); EnterEvadeMode(); Talk(SAY_DEFEAT); _JustDied(); @@ -184,25 +188,20 @@ class boss_majordomo : public CreatureScript } else if (action == ACTION_START_RAGNAROS_ALT) { - me->setFaction(35); + me->setFaction(FACTION_FRIENDLY); me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } } - }; - bool OnGossipHello(Player* player, Creature* creature) override - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SELECT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->SEND_GOSSIP_MENU(GOSSIP_HELLO, creature->GetGUID()); - return true; - } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 /*action*/) override - { - player->CLOSE_GOSSIP_MENU(); - creature->AI()->DoAction(ACTION_START_RAGNAROS); - return true; - } + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId == MENU_OPTION_YOU_CHALLENGED_US && gossipListId == OPTION_ID_YOU_CHALLENGED_US) + { + player->CLOSE_GOSSIP_MENU(); + DoAction(ACTION_START_RAGNAROS); + } + } + }; CreatureAI* GetAI(Creature* creature) const override { diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp index e0cae87051f..a89a6b491e8 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_ragnaros.cpp @@ -159,7 +159,7 @@ class boss_ragnaros : public CreatureScript break; case EVENT_INTRO_5: me->SetReactState(REACT_AGGRESSIVE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); _introState = 2; break; default: diff --git a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp index 05d964124b1..c8eb645845b 100644 --- a/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp +++ b/src/server/scripts/EasternKingdoms/Gnomeregan/gnomeregan.cpp @@ -305,7 +305,7 @@ public: me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[9], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); break; case 2: - if (GameObject* go = me->SummonGameObject(183410, -533.140f, -105.322f, -156.016f, 0, 0, 0, 0, 0, 1)) + if (GameObject* go = me->SummonGameObject(183410, -533.140f, -105.322f, -156.016f, 0.f, G3D::Quat(), 1)) { GoSummonList.push_back(go->GetGUID()); go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it! @@ -320,7 +320,7 @@ public: Talk(SAY_BLASTMASTER_7); break; case 4: - if (GameObject* go = me->SummonGameObject(183410, -542.199f, -96.854f, -155.790f, 0, 0, 0, 0, 0, 1)) + if (GameObject* go = me->SummonGameObject(183410, -542.199f, -96.854f, -155.790f, 0.f, G3D::Quat(), 1)) { GoSummonList.push_back(go->GetGUID()); go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); @@ -334,7 +334,7 @@ public: me->SummonCreature(NPC_CAVERNDEEP_AMBUSHER, SpawnPosition[14], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); break; case 6: - if (GameObject* go = me->SummonGameObject(183410, -507.820f, -103.333f, -151.353f, 0, 0, 0, 0, 0, 1)) + if (GameObject* go = me->SummonGameObject(183410, -507.820f, -103.333f, -151.353f, 0.f, G3D::Quat(), 1)) { GoSummonList.push_back(go->GetGUID()); go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it! @@ -342,7 +342,7 @@ public: } break; case 7: - if (GameObject* go = me->SummonGameObject(183410, -511.829f, -86.249f, -151.431f, 0, 0, 0, 0, 0, 1)) + if (GameObject* go = me->SummonGameObject(183410, -511.829f, -86.249f, -151.431f, 0.f, G3D::Quat(), 1)) { GoSummonList.push_back(go->GetGUID()); go->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); //We can't use it! @@ -354,9 +354,9 @@ public: me->SummonCreature(NPC_CHOMPER, SpawnPosition[16], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 1800000); break; case 9: - me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[17].GetPositionX(), SpawnPosition[17].GetPositionY(), SpawnPosition[17].GetPositionZ(), SpawnPosition[17].GetOrientation(), 0, 0, 0, 0, 7200); - me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[18].GetPositionX(), SpawnPosition[18].GetPositionY(), SpawnPosition[18].GetPositionZ(), SpawnPosition[18].GetOrientation(), 0, 0, 0, 0, 7200); - me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[19].GetPositionX(), SpawnPosition[19].GetPositionY(), SpawnPosition[19].GetPositionZ(), SpawnPosition[19].GetOrientation(), 0, 0, 0, 0, 7200); + me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[17], G3D::Quat(), 7200); + me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[18], G3D::Quat(), 7200); + me->SummonGameObject(GO_RED_ROCKET, SpawnPosition[19], G3D::Quat(), 7200); break; } } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp index 43ef7e006ef..7e7edfb7832 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp @@ -28,25 +28,42 @@ EndScriptData */ #include "SpellInfo.h" #include "karazhan.h" -enum Midnight +enum Texts { - SAY_MIDNIGHT_KILL = 0, - SAY_APPEAR = 1, - SAY_MOUNT = 2, - - SAY_KILL = 0, - SAY_DISARMED = 1, - SAY_DEATH = 2, - SAY_RANDOM = 3, - - SPELL_SHADOWCLEAVE = 29832, - SPELL_INTANGIBLE_PRESENCE = 29833, - SPELL_BERSERKER_CHARGE = 26561, //Only when mounted + SAY_KILL = 0, + SAY_RANDOM = 1, + SAY_DISARMED = 2, + SAY_MIDNIGHT_KILL = 3, + SAY_APPEAR = 4, + SAY_MOUNT = 5, + + SAY_DEATH = 3, + + // Midnight + EMOTE_CALL_ATTUMEN = 0, + EMOTE_MOUNT_UP = 1 +}; - MOUNTED_DISPLAYID = 16040, +enum Spells +{ + // Attumen + SPELL_SHADOWCLEAVE = 29832, + SPELL_INTANGIBLE_PRESENCE = 29833, + SPELL_SPAWN_SMOKE = 10389, + SPELL_CHARGE = 29847, + + // Midnight + SPELL_KNOCKDOWN = 29711, + SPELL_SUMMON_ATTUMEN = 29714, + SPELL_MOUNT = 29770, + SPELL_SUMMON_ATTUMEN_MOUNTED = 29799 +}; - //Attumen (@todo Use the summoning spell instead of Creature id. It works, but is not convenient for us) - SUMMON_ATTUMEN = 15550, +enum Phases +{ + PHASE_NONE, + PHASE_ATTUMEN_ENGAGES, + PHASE_MOUNTED }; class boss_attumen : public CreatureScript @@ -54,75 +71,212 @@ class boss_attumen : public CreatureScript public: boss_attumen() : CreatureScript("boss_attumen") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_attumenAI(creature); - } - - struct boss_attumenAI : public ScriptedAI + struct boss_attumenAI : public BossAI { - boss_attumenAI(Creature* creature) : ScriptedAI(creature) + boss_attumenAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN) { Initialize(); - - Phase = 1; - - CleaveTimer = urand(10000, 15000); - CurseTimer = 30000; - RandomYellTimer = urand(30000, 60000); //Occasionally yell - ChargeTimer = 20000; - } + } void Initialize() { - ResetTimer = 0; - Midnight.Clear(); + _midnightGUID.Clear(); + _phase = PHASE_NONE; } - ObjectGuid Midnight; - uint8 Phase; - uint32 CleaveTimer; - uint32 CurseTimer; - uint32 RandomYellTimer; - uint32 ChargeTimer; //only when mounted - uint32 ResetTimer; - void Reset() override { Initialize(); + BossAI::Reset(); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + BossAI::_DespawnAtEvade(10, midnight); + + me->DespawnOrUnsummon(); } - void EnterEvadeMode(EvadeReason why) override + void ScheduleTasks() override { - ScriptedAI::EnterEvadeMode(why); - ResetTimer = 2000; + scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task) + { + DoCastVictim(SPELL_SHADOWCLEAVE); + task.Repeat(Seconds(15), Seconds(25)); + }); + + scheduler.Schedule(Seconds(25), Seconds(45), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target,SPELL_INTANGIBLE_PRESENCE); + + task.Repeat(Seconds(25), Seconds(45)); + }); + + scheduler.Schedule(Seconds(30), Seconds(60), [this](TaskContext task) + { + Talk(SAY_RANDOM); + task.Repeat(Seconds(30), Seconds(60)); + }); } - void EnterCombat(Unit* /*who*/) override { } + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override + { + // Attumen does not die until he mounts Midnight, let health fall to 1 and prevent further damage. + if (damage >= me->GetHealth() && _phase != PHASE_MOUNTED) + damage = me->GetHealth() - 1; + + if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) + { + _phase = PHASE_NONE; + + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + midnight->AI()->DoCastAOE(SPELL_MOUNT, true); + } + } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_KILL); } - void JustDied(Unit* /*killer*/) override + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_ATTUMEN_MOUNTED) + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + { + if (midnight->GetHealth() > me->GetHealth()) + summon->SetHealth(midnight->GetHealth()); + else + summon->SetHealth(me->GetHealth()); + + summon->AI()->DoZoneInCombat(); + summon->AI()->SetGUID(_midnightGUID, NPC_MIDNIGHT); + } + + BossAI::JustSummoned(summon); + } + + void IsSummonedBy(Unit* summoner) override + { + if (summoner->GetEntry() == NPC_MIDNIGHT) + _phase = PHASE_ATTUMEN_ENGAGES; + + if (summoner->GetEntry() == NPC_ATTUMEN_UNMOUNTED) + { + _phase = PHASE_MOUNTED; + DoCastSelf(SPELL_SPAWN_SMOKE); + + scheduler.Schedule(Seconds(10), Seconds(25), [this](TaskContext task) + { + Unit* target = nullptr; + ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList(); + std::vector<Unit*> target_list; + + for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) + { + target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); + if (target && !target->IsWithinDist(me, 8.00f, false) && target->IsWithinDist(me, 25.0f, false)) + target_list.push_back(target); + + target = nullptr; + } + + if (!target_list.empty()) + target = Trinity::Containers::SelectRandomContainerElement(target_list); + + DoCast(target, SPELL_CHARGE); + task.Repeat(Seconds(10), Seconds(25)); + }); + + scheduler.Schedule(Seconds(25), Seconds(35), [this](TaskContext task) + { + DoCastVictim(SPELL_KNOCKDOWN); + task.Repeat(Seconds(25), Seconds(35)); + }); + } + } + + void JustDied(Unit* killer) override { Talk(SAY_DEATH); - if (Unit* midnight = ObjectAccessor::GetUnit(*me, Midnight)) + if (Unit* midnight = ObjectAccessor::GetUnit(*me, _midnightGUID)) midnight->KillSelf(); - if (InstanceScript* instance = me->GetInstanceScript()) - instance->SetBossState(DATA_ATTUMEN, DONE); + BossAI::JustDied(killer); } - void UpdateAI(uint32 diff) override; + void SetGUID(ObjectGuid guid, int32 data) override + { + if (data == NPC_MIDNIGHT) + _midnightGUID = guid; + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && _phase != PHASE_NONE) + return; + + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } void SpellHit(Unit* /*source*/, const SpellInfo* spell) override { if (spell->Mechanic == MECHANIC_DISARM) Talk(SAY_DISARMED); + + if (spell->Id == SPELL_MOUNT) + { + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + { + _phase = PHASE_NONE; + scheduler.CancelAll(); + + midnight->AttackStop(); + midnight->RemoveAllAttackers(); + midnight->SetReactState(REACT_PASSIVE); + midnight->GetMotionMaster()->MoveChase(me); + midnight->AI()->Talk(EMOTE_MOUNT_UP); + + me->AttackStop(); + me->RemoveAllAttackers(); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MoveChase(midnight); + Talk(SAY_MOUNT); + + scheduler.Schedule(Seconds(3), [this](TaskContext task) + { + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + { + if (me->IsWithinMeleeRange(midnight)) + { + DoCastAOE(SPELL_SUMMON_ATTUMEN_MOUNTED); + me->SetVisible(false); + midnight->SetVisible(false); + } + else + { + midnight->GetMotionMaster()->MoveChase(me); + me->GetMotionMaster()->MoveChase(midnight); + task.Repeat(Seconds(3)); + } + } + }); + } + } } + + private: + ObjectGuid _midnightGUID; + uint8 _phase; }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new boss_attumenAI(creature); + } }; class boss_midnight : public CreatureScript @@ -130,214 +284,102 @@ class boss_midnight : public CreatureScript public: boss_midnight() : CreatureScript("boss_midnight") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_midnightAI(creature); - } - - struct boss_midnightAI : public ScriptedAI + struct boss_midnightAI : public BossAI { - boss_midnightAI(Creature* creature) : ScriptedAI(creature) + boss_midnightAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN) { Initialize(); } void Initialize() { - Phase = 1; - Attumen.Clear(); - Mount_Timer = 0; + _phase = PHASE_NONE; } - ObjectGuid Attumen; - uint8 Phase; - uint32 Mount_Timer; - void Reset() override { Initialize(); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + BossAI::Reset(); me->SetVisible(true); + me->SetReactState(REACT_DEFENSIVE); } - void EnterCombat(Unit* /*who*/) override + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override { - if (InstanceScript* instance = me->GetInstanceScript()) - instance->SetBossState(DATA_ATTUMEN, IN_PROGRESS); - } + // Midnight never dies, let health fall to 1 and prevent further damage. + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; - void KilledUnit(Unit* /*victim*/) override - { - if (Phase == 2) + if (_phase == PHASE_NONE && me->HealthBelowPctDamaged(95, damage)) { - if (Unit* unit = ObjectAccessor::GetUnit(*me, Attumen)) - Talk(SAY_MIDNIGHT_KILL, unit); + _phase = PHASE_ATTUMEN_ENGAGES; + Talk(EMOTE_CALL_ATTUMEN); + DoCastAOE(SPELL_SUMMON_ATTUMEN); + } + else if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) + { + _phase = PHASE_MOUNTED; + DoCastAOE(SPELL_MOUNT, true); } } - void UpdateAI(uint32 diff) override + void JustSummoned(Creature* summon) override { - if (!UpdateVictim()) - return; - - if (Phase == 1 && HealthBelowPct(95)) + if (summon->GetEntry() == NPC_ATTUMEN_UNMOUNTED) { - Phase = 2; - if (Creature* attumen = me->SummonCreature(SUMMON_ATTUMEN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000)) - { - Attumen = attumen->GetGUID(); - attumen->AI()->AttackStart(me->GetVictim()); - SetMidnight(attumen, me->GetGUID()); - Talk(SAY_APPEAR, attumen); - } - } - else if (Phase == 2 && HealthBelowPct(25)) - { - if (Unit* pAttumen = ObjectAccessor::GetUnit(*me, Attumen)) - Mount(pAttumen); - } - else if (Phase == 3) - { - if (Mount_Timer) - { - if (Mount_Timer <= diff) - { - Mount_Timer = 0; - me->SetVisible(false); - me->GetMotionMaster()->MoveIdle(); - if (Unit* pAttumen = ObjectAccessor::GetUnit(*me, Attumen)) - { - pAttumen->SetDisplayId(MOUNTED_DISPLAYID); - pAttumen->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - if (pAttumen->GetVictim()) - { - pAttumen->GetMotionMaster()->MoveChase(pAttumen->GetVictim()); - pAttumen->SetTarget(pAttumen->EnsureVictim()->GetGUID()); - } - pAttumen->SetObjectScale(1); - } - } else Mount_Timer -= diff; - } + _attumenGUID = summon->GetGUID(); + summon->AI()->SetGUID(me->GetGUID(), NPC_MIDNIGHT); + summon->AI()->AttackStart(me->GetVictim()); + summon->AI()->Talk(SAY_APPEAR); } - if (Phase != 3) - DoMeleeAttackIfReady(); + BossAI::JustSummoned(summon); } - void Mount(Unit* pAttumen) + void EnterCombat(Unit* who) override { - Talk(SAY_MOUNT, pAttumen); - Phase = 3; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - pAttumen->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - float angle = me->GetAngle(pAttumen); - float distance = me->GetDistance2d(pAttumen); - float newX = me->GetPositionX() + std::cos(angle)*(distance/2); - float newY = me->GetPositionY() + std::sin(angle)*(distance/2); - float newZ = 50; - //me->Relocate(newX, newY, newZ, angle); - //me->SendMonsterMove(newX, newY, newZ, 0, true, 1000); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(0, newX, newY, newZ); - distance += 10; - newX = me->GetPositionX() + std::cos(angle)*(distance/2); - newY = me->GetPositionY() + std::sin(angle)*(distance/2); - pAttumen->GetMotionMaster()->Clear(); - pAttumen->GetMotionMaster()->MovePoint(0, newX, newY, newZ); - //pAttumen->Relocate(newX, newY, newZ, -angle); - //pAttumen->SendMonsterMove(newX, newY, newZ, 0, true, 1000); - Mount_Timer = 1000; + BossAI::EnterCombat(who); + + scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task) + { + DoCastVictim(SPELL_KNOCKDOWN); + task.Repeat(Seconds(15), Seconds(25)); + }); } - void SetMidnight(Creature* pAttumen, ObjectGuid value) + void EnterEvadeMode(EvadeReason /*why*/) override { - ENSURE_AI(boss_attumen::boss_attumenAI, pAttumen->AI())->Midnight = value; + BossAI::_DespawnAtEvade(10); } - }; -}; -void boss_attumen::boss_attumenAI::UpdateAI(uint32 diff) -{ - if (ResetTimer) - { - if (ResetTimer <= diff) + void KilledUnit(Unit* /*victim*/) override { - ResetTimer = 0; - Unit* pMidnight = ObjectAccessor::GetUnit(*me, Midnight); - if (pMidnight) + if (_phase == PHASE_ATTUMEN_ENGAGES) { - pMidnight->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - pMidnight->SetVisible(true); + if (Unit* unit = ObjectAccessor::GetUnit(*me, _attumenGUID)) + Talk(SAY_MIDNIGHT_KILL, unit); } - Midnight.Clear(); - me->SetVisible(false); - me->KillSelf(); - } else ResetTimer -= diff; - } - - //Return since we have no target - if (!UpdateVictim()) - return; - - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE)) - return; - - if (CleaveTimer <= diff) - { - DoCastVictim(SPELL_SHADOWCLEAVE); - CleaveTimer = urand(10000, 15000); - } else CleaveTimer -= diff; + } - if (CurseTimer <= diff) - { - DoCastVictim(SPELL_INTANGIBLE_PRESENCE); - CurseTimer = 30000; - } else CurseTimer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || _phase == PHASE_MOUNTED) + return; - if (RandomYellTimer <= diff) - { - Talk(SAY_RANDOM); - RandomYellTimer = urand(30000, 60000); - } else RandomYellTimer -= diff; + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } - if (me->GetUInt32Value(UNIT_FIELD_DISPLAYID) == MOUNTED_DISPLAYID) - { - if (ChargeTimer <= diff) - { - Unit* target = NULL; - ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList(); - std::vector<Unit*> target_list; - for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) - { - target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - if (target && !target->IsWithinDist(me, ATTACK_DISTANCE, false)) - target_list.push_back(target); - target = NULL; - } - if (!target_list.empty()) - target = *(target_list.begin() + rand32() % target_list.size()); + private: + ObjectGuid _attumenGUID; + uint8 _phase; + }; - DoCast(target, SPELL_BERSERKER_CHARGE); - ChargeTimer = 20000; - } else ChargeTimer -= diff; - } - else + CreatureAI* GetAI(Creature* creature) const override { - if (HealthBelowPct(25)) - { - Creature* pMidnight = ObjectAccessor::GetCreature(*me, Midnight); - if (pMidnight && pMidnight->GetTypeId() == TYPEID_UNIT) - { - ENSURE_AI(boss_midnight::boss_midnightAI, (pMidnight->AI()))->Mount(me); - me->SetHealth(pMidnight->GetHealth()); - DoResetThreat(); - } - } + return new boss_midnightAI(creature); } - - DoMeleeAttackIfReady(); -} +}; void AddSC_boss_attumen() { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp index e667141fa29..29c78d3b388 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_moroes.cpp @@ -298,7 +298,7 @@ public: if (Blind_Timer <= diff) { std::list<Unit*> targets; - SelectTargetList(targets, 5, SELECT_TARGET_RANDOM, me->GetMeleeReach()*5, true); + SelectTargetList(targets, 5, SELECT_TARGET_RANDOM, me->GetCombatReach()*5, true); for (std::list<Unit*>::const_iterator i = targets.begin(); i != targets.end(); ++i) if (!me->IsWithinMeleeRange(*i)) { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp index 3f236c060d7..2e4ac11d3fa 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/bosses_opera.cpp @@ -797,46 +797,43 @@ enum RedRidingHood SAY_WOLF_AGGRO = 0, SAY_WOLF_SLAY = 1, SAY_WOLF_HOOD = 2, + OPTION_WHAT_PHAT_LEWTS_YOU_HAVE = 7443, SOUND_WOLF_DEATH = 9275, SPELL_LITTLE_RED_RIDING_HOOD = 30768, SPELL_TERRIFYING_HOWL = 30752, SPELL_WIDE_SWIPE = 30761, - CREATURE_BIG_BAD_WOLF = 17521, + CREATURE_BIG_BAD_WOLF = 17521 }; - -#define GOSSIP_GRANDMA "What phat lewtz you have grandmother?" - - - class npc_grandmother : public CreatureScript { -public: - npc_grandmother() : CreatureScript("npc_grandmother") { } + public: + npc_grandmother() : CreatureScript("npc_grandmother") { } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF) + struct npc_grandmotherAI : public ScriptedAI { - if (Creature* pBigBadWolf = creature->SummonCreature(CREATURE_BIG_BAD_WOLF, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR*2*IN_MILLISECONDS)) - pBigBadWolf->AI()->AttackStart(player); + npc_grandmotherAI(Creature* creature) : ScriptedAI(creature) { } - creature->DespawnOrUnsummon(); - } + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId == OPTION_WHAT_PHAT_LEWTS_YOU_HAVE && gossipListId == 0) + { + player->CLOSE_GOSSIP_MENU(); - return true; - } + if (Creature* pBigBadWolf = me->SummonCreature(CREATURE_BIG_BAD_WOLF, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR*2*IN_MILLISECONDS)) + pBigBadWolf->AI()->AttackStart(player); - bool OnGossipHello(Player* player, Creature* creature) override - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_GRANDMA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - player->SEND_GOSSIP_MENU(8990, creature->GetGUID()); + me->DespawnOrUnsummon(); + } + } + }; - return true; - } + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_grandmotherAI(creature); + } }; class boss_bigbadwolf : public CreatureScript diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h index 05de9e43a91..2dc3750dc5b 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h @@ -70,6 +70,9 @@ enum MiscCreatures NPC_SHADIKITH_THE_GLIDER = 16180, NPC_TERESTIAN_ILLHOOF = 15688, NPC_MOROES = 15687, + NPC_ATTUMEN_UNMOUNTED = 15550, + NPC_ATTUMEN_MOUNTED = 16152, + NPC_MIDNIGHT = 16151, // Trash NPC_COLDMIST_WIDOW = 16171, diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp index d25a225717a..ea6d2cb0b6f 100644 --- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp @@ -1116,7 +1116,7 @@ public: case 48: // Show the cleansing effect (dawn of light) //if (GameObject* go = me->GetMap()->GetGameObject(uiDawnofLightGUID)) // go->SetPhaseMask(128, true); - me->SummonGameObject(GO_LIGHT_OF_DAWN, 2283.896f, -5287.914f, 83.066f, 0, 0, 0, 0, 0, 30); + me->SummonGameObject(GO_LIGHT_OF_DAWN, 2283.896f, -5287.914f, 83.066f, 0.f, G3D::Quat(), 30); if (Creature* temp = ObjectAccessor::GetCreature(*me, uiTirionGUID)) { if (temp->HasAura(SPELL_REBIRTH_OF_THE_ASHBRINGER)) diff --git a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp index 6a6a0b0994b..241710627b0 100644 --- a/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp +++ b/src/server/scripts/EasternKingdoms/SunkenTemple/instance_sunken_temple.cpp @@ -176,14 +176,14 @@ public: void UseStatue(GameObject* go) { - go->SummonGameObject(GO_ATALAI_LIGHT1, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ(), 0, 0, 0, 0, 0, 0); + go->SummonGameObject(GO_ATALAI_LIGHT1, *go, G3D::Quat(), 0); go->SetUInt32Value(GAMEOBJECT_FLAGS, 4); } void UseLastStatue(GameObject* go) { for (uint8 i = 0; i < nStatues; ++i) - go->SummonGameObject(GO_ATALAI_LIGHT2, statuePositions[i].GetPositionX(), statuePositions[i].GetPositionY(), statuePositions[i].GetPositionZ(), statuePositions[i].GetOrientation(), 0, 0, 0, 0, 0); + go->SummonGameObject(GO_ATALAI_LIGHT2, statuePositions[i], G3D::Quat(), 0); go->SummonCreature(NPC_ATALALARION, atalalarianPos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 7200); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp index 7563a880f0a..040d8cb2a37 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp @@ -1361,7 +1361,7 @@ public: DoCastVictim(SPELL_SR_SHOOT, false); uiTimer[2] = urand(4000, 6000); } - if (me->IsWithinMeleeRange(me->GetVictim(), 6)) + if (me->IsWithinMeleeRange(me->GetVictim())) { if (uiTimer[0] <= diff) { diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp index a5fed30a6c6..68391c65655 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp @@ -98,7 +98,7 @@ class boss_archaedas : public CreatureScript instance->SetData(0, 5); // respawn any dead minions me->setFaction(35); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->AddAura(SPELL_FREEZE_ANIM, me); } @@ -111,7 +111,7 @@ class boss_archaedas : public CreatureScript DoCast(minion, SPELL_AWAKEN_VAULT_WALKER, flag); minion->CastSpell(minion, SPELL_ARCHAEDAS_AWAKEN, true); minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); minion->setFaction(14); minion->RemoveAura(SPELL_MINION_FREEZE_ANIM); } @@ -121,7 +121,7 @@ class boss_archaedas : public CreatureScript { me->setFaction(14); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); } void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override @@ -261,7 +261,7 @@ class npc_archaedas_minions : public CreatureScript me->setFaction(35); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->RemoveAllAuras(); me->AddAura(SPELL_MINION_FREEZE_ANIM, me); } @@ -271,7 +271,7 @@ class npc_archaedas_minions : public CreatureScript me->setFaction (14); me->RemoveAllAuras(); me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); bAmIAwake = true; } @@ -350,7 +350,7 @@ class npc_stonekeepers : public CreatureScript { me->setFaction(35); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->RemoveAllAuras(); me->AddAura(SPELL_MINION_FREEZE_ANIM, me); } @@ -359,7 +359,7 @@ class npc_stonekeepers : public CreatureScript { me->setFaction(14); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); } void UpdateAI(uint32 /*diff*/) override diff --git a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp index 8c78fb7ff1b..28a3a4eb4e0 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp @@ -147,7 +147,7 @@ class instance_uldaman : public InstanceMapScript creature->setFaction(35); creature->RemoveAllAuras(); creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); creature->AddAura(SPELL_MINION_FREEZE_ANIM, creature); } @@ -178,7 +178,7 @@ class instance_uldaman : public InstanceMapScript Creature* target = instance->GetCreature(*i); if (!target || !target->IsAlive()) continue; - target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); target->setFaction(14); target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); target->RemoveAura(SPELL_MINION_FREEZE_ANIM); @@ -202,7 +202,7 @@ class instance_uldaman : public InstanceMapScript Creature* target = instance->GetCreature(*i); if (!target || !target->IsAlive() || target->getFaction() == 14) continue; - target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); target->setFaction(14); target->RemoveAura(SPELL_MINION_FREEZE_ANIM); @@ -268,7 +268,7 @@ class instance_uldaman : public InstanceMapScript return; ironaya->setFaction(415); - ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); ironaya->GetMotionMaster()->Clear(); diff --git a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp index 972acdc3f9f..9b668169dca 100644 --- a/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp +++ b/src/server/scripts/EasternKingdoms/ZulAman/zulaman.cpp @@ -162,7 +162,7 @@ class npc_zulaman_hostage : public CreatureScript { if (HostageEntry[i] == entry) { - creature->SummonGameObject(ChestEntry[i], x-2, y, z, 0, 0, 0, 0, 0, 0); + creature->SummonGameObject(ChestEntry[i], Position(x - 2, y, z, 0.f), G3D::Quat(), 0); break; } } diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp index b794a653791..adcb4f9fc9a 100644 --- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp +++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp @@ -179,7 +179,6 @@ void AddSC_eastern_plaguelands(); void AddSC_ghostlands(); void AddSC_hinterlands(); void AddSC_isle_of_queldanas(); -void AddSC_loch_modan(); void AddSC_redridge_mountains(); void AddSC_silverpine_forest(); void AddSC_stormwind_city(); @@ -357,7 +356,6 @@ void AddEasternKingdomsScripts() AddSC_ghostlands(); AddSC_hinterlands(); AddSC_isle_of_queldanas(); - AddSC_loch_modan(); AddSC_redridge_mountains(); AddSC_silverpine_forest(); AddSC_stormwind_city(); diff --git a/src/server/scripts/EasternKingdoms/zone_loch_modan.cpp b/src/server/scripts/EasternKingdoms/zone_loch_modan.cpp deleted file mode 100644 index 151f8270c47..00000000000 --- a/src/server/scripts/EasternKingdoms/zone_loch_modan.cpp +++ /dev/null @@ -1,106 +0,0 @@ -/* - * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> - * - * 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/>. - */ - -/* ScriptData -SDName: Loch_Modan -SD%Complete: 100 -SDComment: Quest support: 3181 -SDCategory: Loch Modan -EndScriptData */ - -/* ContentData -npc_mountaineer_pebblebitty -EndContentData */ - -#include "ScriptMgr.h" -#include "ScriptedCreature.h" -#include "ScriptedGossip.h" -#include "Player.h" - -/*###### -## npc_mountaineer_pebblebitty -######*/ - -#define GOSSIP_MP "Open the gate please, i need to get to Searing Gorge" - -#define GOSSIP_MP1 "But i need to get there, now open the gate!" -#define GOSSIP_MP2 "Ok, so what is this other way?" -#define GOSSIP_MP3 "Doesn't matter, i'm invulnerable." -#define GOSSIP_MP4 "Yes..." -#define GOSSIP_MP5 "Ok, i'll try to remember that." -#define GOSSIP_MP6 "A key? Ok!" - -class npc_mountaineer_pebblebitty : public CreatureScript -{ -public: - npc_mountaineer_pebblebitty() : CreatureScript("npc_mountaineer_pebblebitty") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF+1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - player->SEND_GOSSIP_MENU(1833, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+2: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - player->SEND_GOSSIP_MENU(1834, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+3: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); - player->SEND_GOSSIP_MENU(1835, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+4: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - player->SEND_GOSSIP_MENU(1836, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+5: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); - player->SEND_GOSSIP_MENU(1837, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+6: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); - player->SEND_GOSSIP_MENU(1838, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+7: - player->CLOSE_GOSSIP_MENU(); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (!player->GetQuestRewardStatus(3181) == 1) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - -void AddSC_loch_modan() -{ - new npc_mountaineer_pebblebitty(); -} diff --git a/src/server/scripts/Events/childrens_week.cpp b/src/server/scripts/Events/childrens_week.cpp index 4b93f5b4fbb..f61e175b8fc 100644 --- a/src/server/scripts/Events/childrens_week.cpp +++ b/src/server/scripts/Events/childrens_week.cpp @@ -639,6 +639,12 @@ class npc_elder_kekek : public CreatureScript } }; +enum TheEtymidian +{ + SAY_ACTIVATION = 0, + QUEST_THE_ACTIVATION_RUNE = 12547 +}; + /*###### ## npc_the_etymidian ## @todo A red crystal as a gift for the great one should be spawned during the event. @@ -668,10 +674,20 @@ class npc_the_etymidian : public CreatureScript Initialize(); } + void sQuestReward(Player* /*player*/, Quest const* quest, uint32 /*opt*/) override + { + if (quest->GetQuestId() != QUEST_THE_ACTIVATION_RUNE) + return; + + Talk(SAY_ACTIVATION); + } + void MoveInLineOfSight(Unit* who) override { if (!phase && who && who->GetDistance2d(me) < 10.0f) + { if (Player* player = who->ToPlayer()) + { if (player->GetQuestStatus(QUEST_MEETING_A_GREAT_ONE) == QUEST_STATUS_INCOMPLETE) { playerGUID = player->GetGUID(); @@ -679,6 +695,8 @@ class npc_the_etymidian : public CreatureScript if (orphanGUID) phase = 1; } + } + } } void UpdateAI(uint32 diff) override @@ -734,7 +752,6 @@ class npc_the_etymidian : public CreatureScript int8 phase; ObjectGuid playerGUID; ObjectGuid orphanGUID; - }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp index 6b2142a8095..c326710e820 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/BattleForMountHyjal/hyjalAI.cpp @@ -650,7 +650,7 @@ void hyjalAI::SpawnVeins() return; for (uint8 i = 0; i < 7; ++i) { - GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7], 0); + GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], G3D::Quat(VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7]), 0); if (gem) VeinGUID[i]=gem->GetGUID(); } @@ -662,7 +662,7 @@ void hyjalAI::SpawnVeins() return; for (uint8 i = 7; i < 14; ++i) { - GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7], 0); + GameObject* gem = me->SummonGameObject(GO_ANCIENT_VEIN, VeinPos[i][0], VeinPos[i][1], VeinPos[i][2], VeinPos[i][3], G3D::Quat(VeinPos[i][4], VeinPos[i][5], VeinPos[i][6], VeinPos[i][7]), 0); if (gem) VeinGUID[i] = gem->GetGUID(); } @@ -725,7 +725,7 @@ void hyjalAI::UpdateAI(uint32 diff) HideNearPos(me->GetPositionX(), me->GetPositionY()); HideNearPos(5037.76f, -1889.71f); for (uint8 i = 0; i < 92; ++i)//summon fires - me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7], 0); + me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], G3D::Quat(AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7]), 0); } else me->SetVisible(true); @@ -738,7 +738,7 @@ void hyjalAI::UpdateAI(uint32 diff) HideNearPos(5563, -2763.19f); HideNearPos(5542.2f, -2629.36f); for (uint8 i = 0; i < 65; ++i)//summon fires - me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7], 0); + me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], G3D::Quat(HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7]), 0); } else me->SetVisible(true); @@ -1042,7 +1042,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff) { case 0://alliance for (uint8 i = 0; i < 92; ++i)//summon fires - me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7], 0); + me->SummonGameObject(GO_ROARING_FLAME, AllianceFirePos[i][0], AllianceFirePos[i][1], AllianceFirePos[i][2], AllianceFirePos[i][3], G3D::Quat(AllianceFirePos[i][4], AllianceFirePos[i][5], AllianceFirePos[i][6], AllianceFirePos[i][7]), 0); for (uint8 i = 0; i < 25; ++i)//summon 25 ghouls { @@ -1083,7 +1083,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff) break; case 1://horde for (uint8 i = 0; i < 65; ++i)//summon fires - me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7], 0); + me->SummonGameObject(GO_ROARING_FLAME, HordeFirePos[i][0], HordeFirePos[i][1], HordeFirePos[i][2], HordeFirePos[i][3], G3D::Quat(HordeFirePos[i][4], HordeFirePos[i][5], HordeFirePos[i][6], HordeFirePos[i][7]), 0); for (uint8 i = 0; i < 26; ++i)//summon infernals { diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp index 074ea781838..f76e65c3423 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/CullingOfStratholme/culling_of_stratholme.cpp @@ -1256,7 +1256,7 @@ class npc_crate_helper : public CreatureScript instance->SetData(DATA_CRATE_COUNT, instance->GetData(DATA_CRATE_COUNT) + 1); if (GameObject* crate = me->FindNearestGameObject(GO_SUSPICIOUS_CRATE, 5.0f)) { - crate->SummonGameObject(GO_PLAGUED_CRATE, crate->GetPositionX(), crate->GetPositionY(), crate->GetPositionZ(), crate->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, DAY); + crate->SummonGameObject(GO_PLAGUED_CRATE, *crate, G3D::Quat(), DAY); crate->Delete(); } } diff --git a/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp b/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp index 00a622f1319..558b6c10a61 100644 --- a/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp +++ b/src/server/scripts/Kalimdor/DireMaul/instance_dire_maul.cpp @@ -25,20 +25,49 @@ gets instead the deserter debuff. #include "ScriptMgr.h" #include "InstanceScript.h" +// Bosses (East) +// 0 - Pusillin +// 1 - Lethtendris +// 2 - Hydrospawn +// 3 - Zevrim Thornhoof +// 4 - Alzzin the Wildshaper + +// West +// 5 - Tendris Warpwood +// 6 - Magister Kalendris +// 7 - Tsu'zee +// 8 - Illyanna Ravenoak +// 9 - Immol'thar +// 10 - Prince Tortheldrin + +// North +// 11 - Guard Mol'dar +// 12 - Stomper Kreeg +// 13 - Guard Fengus +// 14 - Guard Slip'kik +// 15 - Captain Kromcrush +// 16 - King Gordok + +uint8 const EncounterCount = 17; + class instance_dire_maul : public InstanceMapScript { public: instance_dire_maul() : InstanceMapScript("instance_dire_maul", 429) { } + struct instance_dire_maul_InstanceMapScript : public InstanceScript + { + instance_dire_maul_InstanceMapScript(Map* map) : InstanceScript(map) + { + SetBossNumber(EncounterCount); + } + }; + InstanceScript* GetInstanceScript(InstanceMap* map) const override { return new instance_dire_maul_InstanceMapScript(map); } - struct instance_dire_maul_InstanceMapScript : public InstanceScript - { - instance_dire_maul_InstanceMapScript(Map* map) : InstanceScript(map) { } - }; }; void AddSC_instance_dire_maul() diff --git a/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp b/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp index b579f9fc608..e8b101b5057 100644 --- a/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp +++ b/src/server/scripts/Kalimdor/RazorfenDowns/razorfen_downs.cpp @@ -204,7 +204,7 @@ public: case EVENT_COMPLETE: { DoCast(me, SPELL_IDOM_ROOM_CAMERA_SHAKE); - me->SummonGameObject(GO_BELNISTRASZS_BRAZIER, 2577.196f, 947.0781f, 53.16757f, 2.356195f, 0, 0, 0.9238796f, 0.3826832f, 3600); + me->SummonGameObject(GO_BELNISTRASZS_BRAZIER, 2577.196f, 947.0781f, 53.16757f, 2.356195f, G3D::Quat(0.f, 0.f, 0.9238796f, 0.3826832f), 3600); std::list<WorldObject*> ClusterList; Trinity::AllWorldObjectsInRange objects(me, 50.0f); Trinity::WorldObjectListSearcher<Trinity::AllWorldObjectsInRange> searcher(me, ClusterList, objects); diff --git a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp index b192ff8ef4d..a220468e513 100644 --- a/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp +++ b/src/server/scripts/Kalimdor/RuinsOfAhnQiraj/boss_ossirian.cpp @@ -190,11 +190,7 @@ class boss_ossirian : public CreatureScript if (Creature* Trigger = me->GetMap()->SummonCreature(NPC_OSSIRIAN_TRIGGER, CrystalCoordinates[CrystalIterator])) { TriggerGUID = Trigger->GetGUID(); - if (GameObject* Crystal = Trigger->SummonGameObject(GO_OSSIRIAN_CRYSTAL, - CrystalCoordinates[CrystalIterator].GetPositionX(), - CrystalCoordinates[CrystalIterator].GetPositionY(), - CrystalCoordinates[CrystalIterator].GetPositionZ(), - 0, 0, 0, 0, 0, uint32(-1))) + if (GameObject* Crystal = Trigger->SummonGameObject(GO_OSSIRIAN_CRYSTAL, CrystalCoordinates[CrystalIterator], G3D::Quat(), uint32(-1))) { CrystalGUID = Crystal->GetGUID(); ++CrystalIterator; diff --git a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp index 6063b9fe5c6..a7b2b156128 100644 --- a/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp +++ b/src/server/scripts/Kalimdor/zone_bloodmyst_isle.cpp @@ -508,7 +508,7 @@ public: _explosivesGuids.clear(); for (uint8 i = 0; i != MAX_EXPLOSIVES; ++i) { - if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_1, ExplosivesPos[0][i].m_positionX, ExplosivesPos[0][i].m_positionY, ExplosivesPos[0][i].m_positionZ, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_1, ExplosivesPos[0][i], G3D::Quat(), 0)) _explosivesGuids.push_back(explosive->GetGUID()); } me->HandleEmoteCommand(EMOTE_ONESHOT_NONE); // reset anim state @@ -604,7 +604,7 @@ public: _explosivesGuids.clear(); for (uint8 i = 0; i != MAX_EXPLOSIVES; ++i) { - if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_2, ExplosivesPos[1][i].m_positionX, ExplosivesPos[1][i].m_positionY, ExplosivesPos[1][i].m_positionZ, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) + if (GameObject* explosive = me->SummonGameObject(GO_DRAENEI_EXPLOSIVES_2, ExplosivesPos[1][i], G3D::Quat(), 0)) _explosivesGuids.push_back(explosive->GetGUID()); } Talk(SAY_LEGOSO_15); diff --git a/src/server/scripts/Kalimdor/zone_felwood.cpp b/src/server/scripts/Kalimdor/zone_felwood.cpp index bce9d62105c..2b42db4b697 100644 --- a/src/server/scripts/Kalimdor/zone_felwood.cpp +++ b/src/server/scripts/Kalimdor/zone_felwood.cpp @@ -19,12 +19,12 @@ /* ScriptData SDName: Felwood SD%Complete: 95 -SDComment: Quest support: 4101, 4102 +SDComment: Quest support: 7632 SDCategory: Felwood EndScriptData */ /* ContentData -npcs_riverbreeze_and_silversky +at_ancient_leaf EndContentData */ #include "ScriptMgr.h" @@ -33,74 +33,6 @@ EndContentData */ #include "Player.h" /*###### -## npcs_riverbreeze_and_silversky -######*/ - -#define GOSSIP_ITEM_BEACON "Please make me a Cenarion Beacon" - -enum RiverbreezeAndSilversky -{ - SPELL_CENARION_BEACON = 15120, - - NPC_ARATHANDRIS_SILVERSKY = 9528, - NPC_MAYBESS_RIVERBREEZE = 9529, - - QUEST_CLEASING_FELWOOD_A = 4101, - QUEST_CLEASING_FELWOOD_H = 4102 -}; - -class npcs_riverbreeze_and_silversky : public CreatureScript -{ -public: - npcs_riverbreeze_and_silversky() : CreatureScript("npcs_riverbreeze_and_silversky") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF+1) - { - player->CLOSE_GOSSIP_MENU(); - creature->CastSpell(player, SPELL_CENARION_BEACON, false); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - uint32 creatureId = creature->GetEntry(); - - if (creatureId == NPC_ARATHANDRIS_SILVERSKY) - { - if (player->GetQuestRewardStatus(QUEST_CLEASING_FELWOOD_A)) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->SEND_GOSSIP_MENU(2848, creature->GetGUID()); - } else if (player->GetTeam() == HORDE) - player->SEND_GOSSIP_MENU(2845, creature->GetGUID()); - else - player->SEND_GOSSIP_MENU(2844, creature->GetGUID()); - } - - if (creatureId == NPC_MAYBESS_RIVERBREEZE) - { - if (player->GetQuestRewardStatus(QUEST_CLEASING_FELWOOD_H)) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_BEACON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->SEND_GOSSIP_MENU(2849, creature->GetGUID()); - } else if (player->GetTeam() == ALLIANCE) - player->SEND_GOSSIP_MENU(2843, creature->GetGUID()); - else - player->SEND_GOSSIP_MENU(2842, creature->GetGUID()); - } - - return true; - } -}; - -/*###### ## at_ancient_leaf ######*/ @@ -138,6 +70,5 @@ class at_ancient_leaf : public AreaTriggerScript void AddSC_felwood() { - new npcs_riverbreeze_and_silversky(); new at_ancient_leaf(); } diff --git a/src/server/scripts/Kalimdor/zone_feralas.cpp b/src/server/scripts/Kalimdor/zone_feralas.cpp index 3e67a95b503..3c6ab633f66 100644 --- a/src/server/scripts/Kalimdor/zone_feralas.cpp +++ b/src/server/scripts/Kalimdor/zone_feralas.cpp @@ -19,10 +19,15 @@ /* ScriptData SDName: Feralas SD%Complete: 100 -SDComment: Quest support: 2767, Special vendor Gregan Brewspewer +SDComment: Quest support: 2767, 2987 SDCategory: Feralas EndScriptData */ +/* ContentData +npc_oox22fe +spell_gordunni_trap +EndContentData */ + #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" @@ -32,44 +37,6 @@ EndScriptData */ #include "WorldSession.h" /*###### -## npc_gregan_brewspewer -######*/ - -#define GOSSIP_HELLO "Buy somethin', will ya?" - -class npc_gregan_brewspewer : public CreatureScript -{ -public: - npc_gregan_brewspewer() : CreatureScript("npc_gregan_brewspewer") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF+1) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - player->SEND_GOSSIP_MENU(2434, creature->GetGUID()); - } - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (creature->IsVendor() && player->GetQuestStatus(3909) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - - player->SEND_GOSSIP_MENU(2433, creature->GetGUID()); - return true; - } - -}; - -/*###### ## npc_oox22fe ######*/ @@ -183,6 +150,10 @@ public: }; +/*###### +## spell_gordunni_trap +######*/ + enum GordunniTrap { GO_GORDUNNI_DIRT_MOUND = 144064, @@ -199,12 +170,12 @@ class spell_gordunni_trap : public SpellScriptLoader void HandleDummy() { - if (Unit* caster = GetCaster()) - if (GameObject* chest = caster->SummonGameObject(GO_GORDUNNI_DIRT_MOUND, caster->GetPositionX(), caster->GetPositionY(), caster->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0)) - { - chest->SetSpellId(GetSpellInfo()->Id); - caster->RemoveGameObject(chest, false); - } + Unit* caster = GetCaster(); + if (GameObject* chest = caster->SummonGameObject(GO_GORDUNNI_DIRT_MOUND, *caster, G3D::Quat(), 0)) + { + chest->SetSpellId(GetSpellInfo()->Id); + caster->RemoveGameObject(chest, false); + } } void Register() override @@ -225,7 +196,6 @@ class spell_gordunni_trap : public SpellScriptLoader void AddSC_feralas() { - new npc_gregan_brewspewer(); new npc_oox22fe(); new spell_gordunni_trap(); } diff --git a/src/server/scripts/Kalimdor/zone_moonglade.cpp b/src/server/scripts/Kalimdor/zone_moonglade.cpp index 99a209b5f0a..531c657e1cb 100644 --- a/src/server/scripts/Kalimdor/zone_moonglade.cpp +++ b/src/server/scripts/Kalimdor/zone_moonglade.cpp @@ -19,15 +19,14 @@ /* ScriptData SDName: Moonglade SD%Complete: 100 -SDComment: Quest support: 30, 272, 5929, 5930, 10965. Special Flight Paths for Druid class. +SDComment: Quest support: 10965 SDCategory: Moonglade EndScriptData */ /* ContentData -npc_bunthen_plainswind -npc_silva_filnaveth npc_clintar_spirit -npc_clintar_dreamwalker +npc_omen +npc_giant_spotlight EndContentData */ #include "ScriptMgr.h" @@ -42,128 +41,6 @@ EndContentData */ #include "CellImpl.h" /*###### -## npc_bunthen_plainswind -######*/ - -enum Bunthen -{ - QUEST_SEA_LION_HORDE = 30, - QUEST_SEA_LION_ALLY = 272, - TAXI_PATH_ID_ALLY = 315, - TAXI_PATH_ID_HORDE = 316 -}; - -#define GOSSIP_ITEM_THUNDER "I'd like to fly to Thunder Bluff." -#define GOSSIP_ITEM_AQ_END "Do you know where I can find Half Pendant of Aquatic Endurance?" - -class npc_bunthen_plainswind : public CreatureScript -{ -public: - npc_bunthen_plainswind() : CreatureScript("npc_bunthen_plainswind") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - player->CLOSE_GOSSIP_MENU(); - if (player->getClass() == CLASS_DRUID && player->GetTeam() == HORDE) - player->ActivateTaxiPathTo(TAXI_PATH_ID_HORDE); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - player->SEND_GOSSIP_MENU(5373, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - player->SEND_GOSSIP_MENU(5376, creature->GetGUID()); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->getClass() != CLASS_DRUID) - player->SEND_GOSSIP_MENU(4916, creature->GetGUID()); - else if (player->GetTeam() != HORDE) - { - if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_END, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - - player->SEND_GOSSIP_MENU(4917, creature->GetGUID()); - } - else if (player->getClass() == CLASS_DRUID && player->GetTeam() == HORDE) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_THUNDER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_END, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - - player->SEND_GOSSIP_MENU(4918, creature->GetGUID()); - } - return true; - } - -}; - -/*###### -## npc_silva_filnaveth -######*/ - -#define GOSSIP_ITEM_RUTHERAN "I'd like to fly to Rut'theran Village." -#define GOSSIP_ITEM_AQ_AGI "Do you know where I can find Half Pendant of Aquatic Agility?" - -class npc_silva_filnaveth : public CreatureScript -{ -public: - npc_silva_filnaveth() : CreatureScript("npc_silva_filnaveth") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - player->CLOSE_GOSSIP_MENU(); - if (player->getClass() == CLASS_DRUID && player->GetTeam() == ALLIANCE) - player->ActivateTaxiPathTo(TAXI_PATH_ID_ALLY); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - player->SEND_GOSSIP_MENU(5374, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - player->SEND_GOSSIP_MENU(5375, creature->GetGUID()); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->getClass() != CLASS_DRUID) - player->SEND_GOSSIP_MENU(4913, creature->GetGUID()); - else if (player->GetTeam() != ALLIANCE) - { - if (player->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_AGI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - - player->SEND_GOSSIP_MENU(4915, creature->GetGUID()); - } - else if (player->getClass() == CLASS_DRUID && player->GetTeam() == ALLIANCE) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_RUTHERAN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - if (player->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_AGI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - - player->SEND_GOSSIP_MENU(4914, creature->GetGUID()); - } - return true; - } - -}; - -/*###### ## npc_clintar_spirit ######*/ @@ -652,8 +529,6 @@ public: void AddSC_moonglade() { - new npc_bunthen_plainswind(); - new npc_silva_filnaveth(); new npc_clintar_spirit(); new npc_omen(); new npc_giant_spotlight(); diff --git a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp index adb6439272a..9c531c1f1be 100644 --- a/src/server/scripts/Kalimdor/zone_orgrimmar.cpp +++ b/src/server/scripts/Kalimdor/zone_orgrimmar.cpp @@ -32,6 +32,9 @@ EndContentData */ #include "ScriptedCreature.h" #include "ScriptedGossip.h" #include "Player.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" /*###### ## npc_shenthul @@ -273,8 +276,555 @@ public: }; +/* --------- Herald of War ------------- */ +enum CitizenEntries +{ + NPC_GRYSHKA = 31433, + NPC_OLVIA = 31425, + NPC_SANA = 31429, + NPC_FELIKA = 31427, + NPC_THATHUNG = 31430, + NPC_KAJA = 31423 +}; + +enum SceneEvents +{ + EVENT_SCENE_1 = 1, + EVENT_SCENE_2 = 2, + EVENT_SCENE_3 = 3, + EVENT_SCENE_4 = 4, + EVENT_SCENE_5 = 5, + EVENT_SCENE_6 = 6, + EVENT_SCENE_7 = 7, + EVENT_SCENE_8 = 8, + EVENT_SCENE_9 = 9, + EVENT_SCENE_10 = 10, + EVENT_SCENE_11 = 11, + EVENT_SCENE_12 = 12, + EVENT_SCENE_13 = 13, + EVENT_RESET = 14 +}; + +enum CitizenTalk +{ + SAY_GRYSHKA_1 = 0, // When can we reopen our shops? I'm losing gold here! + SAY_GRYSHKA_2 = 1, // "This is an outrage!" + SAY_OLVIA_1 = 0, // Where is the Warchief? + SAY_OLVIA_2 = 1, // What are all these Forsaken doing here? + SAY_FELIKA_1 = 0, // What is going on? + SAY_FELIKA_2 = 1, // This is an outrage! + SAY_THATHUNG = 0, // What is going on? + SAY_KAJA = 0, // Why is Thrall allowing this to happen to our city? + SAY_SANA = 0, // We demand answers! + SAY_RUNTHAK_1 = 0, // SILENCE! + SAY_RUNTHAK_2 = 1, // We are on the brink of all out war with the Alliance! + SAY_RUNTHAK_3 = 2, // Tragic events have unfolded in Northrend. The Warchief is doing all that he can to keep us safe. + SAY_RUNTHAK_4 = 3 // All services and shops are to remain closed until further notice! That is all! +}; + +class npc_overlord_runthak_orgrimmar : public CreatureScript +{ +public: + npc_overlord_runthak_orgrimmar() : CreatureScript("npc_overlord_runthak_orgrimmar") { } + + struct npc_overlord_runthak_orgrimmarAI : public ScriptedAI + { + npc_overlord_runthak_orgrimmarAI(Creature* creature) : ScriptedAI(creature) + { + inProgress = false; + } + + void Reset() override + { + inProgress = false; + me->GetMotionMaster()->MovePath(me->GetSpawnId() * 10, true); + events.Reset(); + } + + void MoveInLineOfSight(Unit* who) override + { + if (who->GetTypeId() == TYPEID_PLAYER && who->IsWithinDist(me, 20.0f) && !inProgress) + { + inProgress = true; + events.ScheduleEvent(EVENT_SCENE_1, 2000); + } + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + while (uint32 eventID = events.ExecuteEvent()) + { + switch (eventID) + { + case EVENT_SCENE_1: + GetCitizenGuids(); + me->GetMotionMaster()->MoveIdle(); + if (Creature* gryshka = ObjectAccessor::GetCreature(*me, gryshkaGUID)) + { + me->SetFacingTo(me->GetAngle(gryshka->GetPositionX(), gryshka->GetPositionY())); + gryshka->AI()->Talk(SAY_GRYSHKA_1); + } + events.ScheduleEvent(EVENT_SCENE_2, 4500); + break; + case EVENT_SCENE_2: + if (Creature* olvia = ObjectAccessor::GetCreature(*me, olviaGUID)) + olvia->AI()->Talk(SAY_OLVIA_1); + events.ScheduleEvent(EVENT_SCENE_3, 4500); + break; + case EVENT_SCENE_3: + if (Creature* felika = ObjectAccessor::GetCreature(*me, felikaGUID)) + felika->AI()->Talk(SAY_FELIKA_1); + events.ScheduleEvent(EVENT_SCENE_4, 4500); + break; + case EVENT_SCENE_4: + if (Creature* thathung = ObjectAccessor::GetCreature(*me, thungGUID)) + thathung->AI()->Talk(SAY_THATHUNG); + events.ScheduleEvent(EVENT_SCENE_5, 4500); + break; + case EVENT_SCENE_5: + if (Creature* sana = ObjectAccessor::GetCreature(*me, sanaGUID)) + sana->AI()->Talk(SAY_SANA); + events.ScheduleEvent(EVENT_SCENE_6, 4500); + break; + case EVENT_SCENE_6: + if (Creature* gryshka = ObjectAccessor::GetCreature(*me, gryshkaGUID)) + gryshka->AI()->Talk(SAY_GRYSHKA_2); + events.ScheduleEvent(EVENT_SCENE_7, 4500); + break; + case EVENT_SCENE_7: + if (Creature* kaja = ObjectAccessor::GetCreature(*me, kajaGUID)) + kaja->AI()->Talk(SAY_KAJA); + events.ScheduleEvent(EVENT_SCENE_8, 4500); + break; + case EVENT_SCENE_8: + if (Creature* felika = ObjectAccessor::GetCreature(*me, felikaGUID)) + felika->AI()->Talk(SAY_FELIKA_2); + events.ScheduleEvent(EVENT_SCENE_9, 4500); + break; + case EVENT_SCENE_9: + if (Creature* olvia = ObjectAccessor::GetCreature(*me, olviaGUID)) + olvia->AI()->Talk(SAY_OLVIA_2); + events.ScheduleEvent(EVENT_SCENE_10, 4500); + break; + case EVENT_SCENE_10: + Talk(SAY_RUNTHAK_1); + events.ScheduleEvent(EVENT_SCENE_11, 1500); + break; + case EVENT_SCENE_11: + Talk(SAY_RUNTHAK_2); + events.ScheduleEvent(EVENT_SCENE_12, 4500); + break; + case EVENT_SCENE_12: + Talk(SAY_RUNTHAK_3); + events.ScheduleEvent(EVENT_SCENE_13, 4500); + break; + case EVENT_SCENE_13: + Talk(SAY_RUNTHAK_4); + events.ScheduleEvent(EVENT_RESET, 25000); + break; + case EVENT_RESET: + Reset(); + break; + default: + break; + } + } + } + + void GetCitizenGuids() + { + // if one GUID is empty it means all the others are empty as well so we should store them + // otherwise do not call for grid search since someone else already activated event once before and guids are stored + if (gryshkaGUID.IsEmpty()) + { + std::list<Unit*> citizenList; + Trinity::AnyFriendlyUnitInObjectRangeCheck checker(me, me, 25.0f); + Trinity::UnitListSearcher<Trinity::AnyFriendlyUnitInObjectRangeCheck> searcher(me, citizenList, checker); + me->VisitNearbyObject(20.0f, searcher); + for (Unit* target : citizenList) + { + switch (target->GetEntry()) + { + case NPC_GRYSHKA: + gryshkaGUID = target->GetGUID(); + break; + case NPC_OLVIA: + olviaGUID = target->GetGUID(); + break; + case NPC_SANA: + sanaGUID = target->GetGUID(); + break; + case NPC_FELIKA: + felikaGUID = target->GetGUID(); + break; + case NPC_THATHUNG: + thungGUID = target->GetGUID(); + break; + case NPC_KAJA: + kajaGUID = target->GetGUID(); + break; + default: + break; + } + } + } + } + + private: + EventMap events; + bool inProgress; + ObjectGuid gryshkaGUID; + ObjectGuid olviaGUID; + ObjectGuid sanaGUID; + ObjectGuid felikaGUID; + ObjectGuid thungGUID; + ObjectGuid kajaGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_overlord_runthak_orgrimmarAI(creature); + } +}; + +// Phased out thrall during herald of war chain quest +enum HeraldEntries +{ + NPC_PORTAL_STORMWIND = 31640, + NPC_JAINA_PROUDMOORE = 31418, + NPC_BANSHEE_SYLVANAS = 31419, + NPC_KORKRON_GUARD = 31417, + NPC_THRALL_HERALD = 31412, + + GO_PORTAL_UNDERCITY = 193425 +}; + +enum HeraldMisc +{ + QUEST_HERALD_OF_WAR = 13257, + GUARDS_SIZE = 4 +}; + +enum HeraldActions +{ + ACTION_START_SCENE = 0 +}; + +enum HeraldSpell +{ + SPELL_JAINA_SPAWNIN = 55761 +}; + +enum HeraldTalk +{ + SAY_THRALL_0 = 0, // Kor'kron, stand down! + SAY_THRALL_1 = 1, // Jaina... + SAY_THRALL_2 = 2, // Jaina, what happened at the Wrathgate. It was a betrayal from within... + SAY_THRALL_3 = 3, // The Horde has lost the Undercity. + SAY_THRALL_4 = 4, // We now prepare to lay siege to the city and bring the perpetrators of this unforgivable crime to justice. + SAY_THRALL_5 = 5, // If we are forced into a conflict, the Lich King will destroy our divided forces in Northrend. + SAY_THRALL_6 = 6, // We will make this right, Jaina. Tell your king all that you have learned here. + SAY_THRALL_7 = 7, // Kor'kron, prepare transport to the Undercity. + + SAY_SYLVANAS_0 = 0, // Lady Proudmoore, the Warchief speaks the truth. This subterfuge was set in motion by Varimathras and Grand Apothecary Putress. It was not the Horde's doing. + SAY_SYLVANAS_1 = 1, // As the combined Horde and Alliance forces began their assault upon the Wrath Gate, an uprising broke out in the Undercity. Varimathras and hordes of his demonic brethren attacked. Hundreds of my people were slain in the coup. I barely managed to escape with my life. + + SAY_JAINA_0 = 0, // Thrall, what has happened? The King is preparing for war... + SAY_JAINA_1 = 1, // I will deliver this information to King Wrynn, Thrall, but... + SAY_JAINA_2 = 2, // Bolvar was like a brother to him. In the King's absence, Bolvar kept the Alliance united. He found strength for our people in our darkest hours. He watched over Anduin, raising him as his own. + SAY_JAINA_3 = 3, // I fear that the rage will consume him, Thrall. I remain hopeful that reason will prevail, but we must prepare for the worst... for war. + SAY_JAINA_4 = 4 // Farewell, Warchief. I pray that the next time we meet it will be as allies. +}; + +enum HeraldEvents +{ + EVENT_HERALD_SCENE1 = 1, + EVENT_HERALD_SCENE2 = 2, + EVENT_HERALD_SCENE3 = 3, + EVENT_HERALD_SCENE4 = 4, + EVENT_HERALD_SCENE5 = 5, + EVENT_HERALD_SCENE6 = 6, + EVENT_HERALD_SCENE7 = 7, + EVENT_HERALD_SCENE8 = 8, + EVENT_HERALD_SCENE9 = 9, + EVENT_HERALD_SCENE10 = 10, + EVENT_HERALD_SCENE11 = 11, + EVENT_HERALD_SCENE12 = 12, + EVENT_HERALD_SCENE13 = 13, + EVENT_HERALD_SCENE14 = 14, + EVENT_HERALD_SCENE15 = 15, + EVENT_HERALD_RESET = 16 +}; + +Position const GuardsSpawnPosition[GUARDS_SIZE] = +{ + { 1909.39f, -4144.21f, 40.6368f, 0.042239f }, + { 1910.73f, -4155.26f, 40.6316f, 0.615577f }, + { 1934.01f, -4141.40f, 40.6375f, 3.61109f }, + { 1931.11f, -4156.38f, 40.6130f, 2.19737f } +}; + +Position const GuardsMovePosition[GUARDS_SIZE] = +{ + { 1917.461670f, -4147.514160f, 40.636799f, 5.89346f }, + { 1916.181274f, -4152.295898f, 40.629120f, 0.497757f }, + { 1926.435425f, -4146.397461f, 40.618534f, 3.846709f }, + { 1926.519165f, -4153.216797f, 40.614975f, 2.570434f } +}; + +Position const MiscMovePositions[3] = +{ + { 1921.719604f, -4143.051270f, 40.623356f, 1.657789f }, // jaina move + { 1921.151855f, -4139.343750f, 40.583084f, 4.732627f }, // thrall move + { 1918.732422f, -4139.619629f, 40.607685f, 4.803311f } // sylvanas move +}; + +Position const PortalSpawnPosition = { 1921.752441f, -4151.148438f, 40.623848f, 1.714324f }; + +class npc_thrall_herald_of_war : public CreatureScript +{ +public: + npc_thrall_herald_of_war() : CreatureScript("npc_thrall_herald_of_war") { } + + struct npc_thrall_herald_of_warAI : public ScriptedAI + { + npc_thrall_herald_of_warAI(Creature* creature) : ScriptedAI(creature) + { + spawnedGuards = false; + sceneInProgress = false; + } + + void Reset() override + { + events.Reset(); + me->GetMotionMaster()->MovePath(me->GetSpawnId() * 10, true); + sceneInProgress = false; + + if (!spawnedGuards) + { + spawnedGuards = true; + for (uint8 i = 0; i < GUARDS_SIZE; i++) + { + if (Creature* korkronGuard = me->SummonCreature(NPC_KORKRON_GUARD, GuardsSpawnPosition[i])) + guardsGUIDs[i] = korkronGuard->GetGUID(); + } + } + } + + void MovementInform(uint32 type, uint32 pointId) override + { + if (type != POINT_MOTION_TYPE) + return; + + if (pointId == 2) + me->GetMotionMaster()->MovePath(me->GetSpawnId() * 10, true); + } + + void DoAction(int32 actionId) override + { + if (actionId == ACTION_START_SCENE && !sceneInProgress) + { + me->GetMotionMaster()->MoveIdle(); + sceneInProgress = true; + + if (Creature* jaina = me->SummonCreature(NPC_JAINA_PROUDMOORE, PortalSpawnPosition)) + { + me->SetFacingTo(me->GetAngle(jaina->GetPositionX(), jaina->GetPositionY())); + jainaGUID = jaina->GetGUID(); + jaina->CastSpell(jaina, SPELL_JAINA_SPAWNIN); + } + + if (Creature* stormwindPortal = me->SummonCreature(NPC_PORTAL_STORMWIND, PortalSpawnPosition)) + { + stormwindPortal->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + stormwindPortalGUID = stormwindPortal->GetGUID(); + } + + for (uint8 i = 0; i < GUARDS_SIZE; i++) + { + if (Creature* guards = ObjectAccessor::GetCreature(*me, guardsGUIDs[i])) + { + guards->SetWalk(false); + guards->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H); + guards->GetMotionMaster()->MovePoint(1, GuardsMovePosition[i]); + } + } + + if (Creature* sylvanas = me->FindNearestCreature(NPC_BANSHEE_SYLVANAS, 25.0f)) + sylvanasGUID = sylvanas->GetGUID(); + + events.ScheduleEvent(EVENT_HERALD_SCENE1, 4000); + } + } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + while (uint32 eventID = events.ExecuteEvent()) + { + switch (eventID) + { + case EVENT_HERALD_SCENE1: + Talk(SAY_THRALL_0); + me->GetMotionMaster()->MovePoint(1, MiscMovePositions[1]); + if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) + { + jaina->SetWalk(true); + jaina->GetMotionMaster()->MovePoint(1, MiscMovePositions[0]); + } + + for (uint8 i = 0; i < GUARDS_SIZE; i++) + { + if (Creature* guard = ObjectAccessor::GetCreature(*me, guardsGUIDs[i])) + { + guard->GetMotionMaster()->MoveTargetedHome(); + guard->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); + } + } + events.ScheduleEvent(EVENT_HERALD_SCENE2, 3000); + break; + case EVENT_HERALD_SCENE2: + Talk(SAY_THRALL_1); + if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) + me->SetFacingTo(me->GetAngle(jaina->GetPositionX(), jaina->GetPositionY())); + events.ScheduleEvent(EVENT_HERALD_SCENE3, 3500); + break; + case EVENT_HERALD_SCENE3: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) + jaina->AI()->Talk(SAY_JAINA_0); + events.ScheduleEvent(EVENT_HERALD_SCENE4, 5500); + break; + case EVENT_HERALD_SCENE4: + Talk(SAY_THRALL_2); + if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID)) + { + sylvanas->SetStandState(UNIT_STAND_STATE_STAND); + sylvanas->SetWalk(true); + sylvanas->GetMotionMaster()->MovePoint(1, MiscMovePositions[2]); + } + events.ScheduleEvent(EVENT_HERALD_SCENE5, 6500); + break; + case EVENT_HERALD_SCENE5: + if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID)) + sylvanas->AI()->Talk(SAY_SYLVANAS_0); + events.ScheduleEvent(EVENT_HERALD_SCENE6, 10000); + break; + case EVENT_HERALD_SCENE6: + if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID)) + sylvanas->AI()->Talk(SAY_SYLVANAS_1); + events.ScheduleEvent(EVENT_HERALD_SCENE7, 20000); + break; + case EVENT_HERALD_SCENE7: + Talk(SAY_THRALL_3); + events.ScheduleEvent(EVENT_HERALD_SCENE8, 4500); + break; + case EVENT_HERALD_SCENE8: + Talk(SAY_THRALL_4); + events.ScheduleEvent(EVENT_HERALD_SCENE9, 10000); + break; + case EVENT_HERALD_SCENE9: + Talk(SAY_THRALL_5); + events.ScheduleEvent(EVENT_HERALD_SCENE10, 8000); + break; + case EVENT_HERALD_SCENE10: + Talk(SAY_THRALL_6); + events.ScheduleEvent(EVENT_HERALD_SCENE11, 9000); + break; + case EVENT_HERALD_SCENE11: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) + jaina->AI()->Talk(SAY_JAINA_1); + events.ScheduleEvent(EVENT_HERALD_SCENE12, 6000); + break; + case EVENT_HERALD_SCENE12: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) + jaina->AI()->Talk(SAY_JAINA_2); + events.ScheduleEvent(EVENT_HERALD_SCENE13, 14000); + break; + case EVENT_HERALD_SCENE13: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) + jaina->AI()->Talk(SAY_JAINA_3); + events.ScheduleEvent(EVENT_HERALD_SCENE14, 10000); + break; + case EVENT_HERALD_SCENE14: + if (Creature* jaina = ObjectAccessor::GetCreature(*me, jainaGUID)) + { + jaina->AI()->Talk(SAY_JAINA_4); + jaina->SetWalk(true); + jaina->GetMotionMaster()->MovePoint(2, jaina->GetHomePosition()); + jaina->DespawnOrUnsummon(5000); + } + events.ScheduleEvent(EVENT_HERALD_SCENE15, 7000); + break; + case EVENT_HERALD_SCENE15: + { + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(2, me->GetHomePosition()); + Talk(SAY_THRALL_7); + + if (Creature* sylvanas = ObjectAccessor::GetCreature(*me, sylvanasGUID)) + { + sylvanas->SetWalk(true); + sylvanas->GetMotionMaster()->MovePoint(2, sylvanas->GetHomePosition()); + } + + if (Creature* portal = ObjectAccessor::GetCreature(*me, stormwindPortalGUID)) + portal->DespawnOrUnsummon(); + + events.ScheduleEvent(EVENT_HERALD_RESET, 60000); + break; + } + case EVENT_HERALD_RESET: + Reset(); + break; + default: + break; + } + } + } + + private: + EventMap events; + ObjectGuid guardsGUIDs[GUARDS_SIZE]; + ObjectGuid jainaGUID; + ObjectGuid sylvanasGUID; + ObjectGuid stormwindPortalGUID; + bool spawnedGuards; + bool sceneInProgress; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_thrall_herald_of_warAI(creature); + } +}; + +class areatrigger_orgrimmar_herald_of_war : public AreaTriggerScript +{ +public: + areatrigger_orgrimmar_herald_of_war() : AreaTriggerScript("areatrigger_orgrimmar_herald_of_war") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*trigger*/) override + { + if (player->IsAlive() && player->GetQuestStatus(QUEST_HERALD_OF_WAR) == QUEST_STATUS_COMPLETE) + { + if (Creature* thrall = player->FindNearestCreature(NPC_THRALL_HERALD, 50.0f)) + { + thrall->AI()->DoAction(ACTION_START_SCENE); + return true; + } + } + return false; + } +}; + void AddSC_orgrimmar() { new npc_shenthul(); new npc_thrall_warchief(); + new npc_overlord_runthak_orgrimmar(); + new npc_thrall_herald_of_war(); + new areatrigger_orgrimmar_herald_of_war(); } diff --git a/src/server/scripts/Kalimdor/zone_silithus.cpp b/src/server/scripts/Kalimdor/zone_silithus.cpp index cc285ab991e..db7bd62c5d1 100644 --- a/src/server/scripts/Kalimdor/zone_silithus.cpp +++ b/src/server/scripts/Kalimdor/zone_silithus.cpp @@ -19,13 +19,20 @@ /* ScriptData SDName: Silithus SD%Complete: 100 -SDComment: Quest support: 8304, 8507. +SDComment: Quest support: 8348,8352,8361,8519 SDCategory: Silithus EndScriptData */ /* ContentData -npcs_rutgar_and_frankal -quest_a_pawn_on_the_eternal_pawn +A Pawn on the Eternal Board - creatures, gameobjects and defines +quest_a_pawn_on_the_eternal_board +npc_qiraj_war_spawn : Adds that are summoned in the Qiraj gates battle. +npc_anachronos_the_ancient : Creature that controls the event. +npc_anachronos_quest_trigger: controls the spawning of the BG War mobs. +go_crystalline_tear : GameObject that begins the event and hands out quest +TO DO: get correct spell IDs and timings for spells cast upon dragon transformations +TO DO: Dragons should use the HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF) after transformation, + but for some unknown reason it doesn't work. EndContentData */ #include "ScriptMgr.h" @@ -34,195 +41,66 @@ EndContentData */ #include "Group.h" #include "Player.h" -/*### -## npcs_rutgar_and_frankal -###*/ - -//gossip item text best guess -#define GOSSIP_ITEM1 "I seek information about Natalia" - -#define GOSSIP_ITEM2 "That sounds dangerous!" -#define GOSSIP_ITEM3 "What did you do?" -#define GOSSIP_ITEM4 "Who?" -#define GOSSIP_ITEM5 "Women do that. What did she demand?" -#define GOSSIP_ITEM6 "What do you mean?" -#define GOSSIP_ITEM7 "What happened next?" - -#define GOSSIP_ITEM11 "Yes, please continue" -#define GOSSIP_ITEM12 "What language?" -#define GOSSIP_ITEM13 "The Priestess attacked you?!" -#define GOSSIP_ITEM14 "I should ask the monkey about this" -#define GOSSIP_ITEM15 "Then what..." - -enum RutgarAndFrankal //trigger creatures to kill -{ - TRIGGER_FRANKAL = 15221, - TRIGGER_RUTGAR = 15222 -}; - -class npcs_rutgar_and_frankal : public CreatureScript -{ -public: - npcs_rutgar_and_frankal() : CreatureScript("npcs_rutgar_and_frankal") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - player->SEND_GOSSIP_MENU(7755, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - player->SEND_GOSSIP_MENU(7756, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - player->SEND_GOSSIP_MENU(7757, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); - player->SEND_GOSSIP_MENU(7758, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 4: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - player->SEND_GOSSIP_MENU(7759, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 5: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM7, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); - player->SEND_GOSSIP_MENU(7760, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 6: - player->SEND_GOSSIP_MENU(7761, creature->GetGUID()); - //'kill' our trigger to update quest status - player->KilledMonsterCredit(TRIGGER_RUTGAR); - break; - - case GOSSIP_ACTION_INFO_DEF + 9: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM11, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); - player->SEND_GOSSIP_MENU(7762, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 10: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM12, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); - player->SEND_GOSSIP_MENU(7763, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 11: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM13, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); - player->SEND_GOSSIP_MENU(7764, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 12: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM14, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); - player->SEND_GOSSIP_MENU(7765, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 13: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM15, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14); - player->SEND_GOSSIP_MENU(7766, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF + 14: - player->SEND_GOSSIP_MENU(7767, creature->GetGUID()); - //'kill' our trigger to update quest status - player->KilledMonsterCredit(TRIGGER_FRANKAL); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE && - creature->GetEntry() == 15170 && - !player->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - - if (player->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE && - creature->GetEntry() == 15171 && - player->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+9); - - player->SEND_GOSSIP_MENU(7754, creature->GetGUID()); - - return true; - } - -}; - -/*#### -# quest_a_pawn_on_the_eternal_board (Defines) -####*/ -enum EternalBoard -{ - QUEST_A_PAWN_ON_THE_ETERNAL_BOARD = 8519, - - FACTION_HOSTILE = 14, - FACTION_FRIENDLY = 35, - - C_ANACHRONOS = 15381, - C_FANDRAL_STAGHELM = 15382, - C_ARYGOS = 15380, - C_MERITHRA = 15378, - C_CAELESTRASZ = 15379, - - ANACHRONOS_SAY_1 = 0, - ANACHRONOS_SAY_2 = 1, - ANACHRONOS_SAY_3 = 2, - ANACHRONOS_SAY_4 = 3, - ANACHRONOS_SAY_5 = 4, - ANACHRONOS_SAY_6 = 5, - ANACHRONOS_SAY_7 = 6, - ANACHRONOS_SAY_8 = 7, - ANACHRONOS_SAY_9 = 8, - ANACHRONOS_SAY_10 = 9, - ANACHRONOS_EMOTE_1 = 10, - ANACHRONOS_EMOTE_2 = 11, - ANACHRONOS_EMOTE_3 = 12, - - FANDRAL_SAY_1 = 0, - FANDRAL_SAY_2 = 1, - FANDRAL_SAY_3 = 2, - FANDRAL_SAY_4 = 3, - FANDRAL_SAY_5 = 4, - FANDRAL_SAY_6 = 5, - FANDRAL_EMOTE_1 = 6, - FANDRAL_EMOTE_2 = 7, - - CAELESTRASZ_SAY_1 = 0, - CAELESTRASZ_SAY_2 = 1, - CAELESTRASZ_YELL_1 = 2, - - ARYGOS_SAY_1 = 0, - ARYGOS_YELL_1 = 1, - ARYGOS_EMOTE_1 = 2, - - MERITHRA_SAY_1 = 0, - MERITHRA_SAY_2 = 1, - MERITHRA_YELL_1 = 2, - MERITHRA_EMOTE_1 = 3, - - GO_GATE_OF_AHN_QIRAJ = 176146, - GO_GLYPH_OF_AHN_QIRAJ = 176148, - GO_ROOTS_OF_AHN_QIRAJ = 176147 -}; /*##### # Quest: A Pawn on the Eternal Board #####*/ -/* ContentData -A Pawn on the Eternal Board - creatures, gameobjects and defines -npc_qiraj_war_spawn : Adds that are summoned in the Qiraj gates battle. -npc_anachronos_the_ancient : Creature that controls the event. -npc_anachronos_quest_trigger: controls the spawning of the BG War mobs. -go_crystalline_tear : GameObject that begins the event and hands out quest -TO DO: get correct spell IDs and timings for spells cast upon dragon transformations -TO DO: Dragons should use the HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF) after transformation, but for some unknown reason it doesnt work. -EndContentData */ - -#define EVENT_AREA_RADIUS 65 //65yds -#define EVENT_COOLDOWN 500000 //in ms. appear after event completed or failed (should be = Adds despawn time) +enum EternalBoard +{ + QUEST_A_PAWN_ON_THE_ETERNAL_BOARD = 8519, + + FACTION_HOSTILE = 14, + FACTION_FRIENDLY = 35, + + EVENT_AREA_RADIUS = 65, // 65yds + EVENT_COOLDOWN = 500000, // in ms. appears after event completed or failed (should be = Adds despawn time) + + NPC_ANACHRONOS = 15381, + NPC_FANDRAL_STAGHELM = 15382, + NPC_ARYGOS = 15380, + NPC_MERITHRA_OF_THE_DREAM = 15378, + NPC_CAELESTRASZ = 15379, + + ANACHRONOS_SAY_1 = 0, + ANACHRONOS_SAY_2 = 1, + ANACHRONOS_SAY_3 = 2, + ANACHRONOS_SAY_4 = 3, + ANACHRONOS_SAY_5 = 4, + ANACHRONOS_SAY_6 = 5, + ANACHRONOS_SAY_7 = 6, + ANACHRONOS_SAY_8 = 7, + ANACHRONOS_SAY_9 = 8, + ANACHRONOS_SAY_10 = 9, + ANACHRONOS_EMOTE_1 = 10, + ANACHRONOS_EMOTE_2 = 11, + ANACHRONOS_EMOTE_3 = 12, + + FANDRAL_SAY_1 = 0, + FANDRAL_SAY_2 = 1, + FANDRAL_SAY_3 = 2, + FANDRAL_SAY_4 = 3, + FANDRAL_SAY_5 = 4, + FANDRAL_SAY_6 = 5, + FANDRAL_EMOTE_1 = 6, + FANDRAL_EMOTE_2 = 7, + + CAELESTRASZ_SAY_1 = 0, + CAELESTRASZ_SAY_2 = 1, + CAELESTRASZ_YELL_1 = 2, + + ARYGOS_SAY_1 = 0, + ARYGOS_YELL_1 = 1, + ARYGOS_EMOTE_1 = 2, + + MERITHRA_SAY_1 = 0, + MERITHRA_SAY_2 = 1, + MERITHRA_YELL_1 = 2, + MERITHRA_EMOTE_1 = 3, + + GO_GATE_OF_AHN_QIRAJ = 176146, + GO_GLYPH_OF_AHN_QIRAJ = 176148, + GO_ROOTS_OF_AHN_QIRAJ = 176147 +}; struct QuestCinematic { @@ -233,59 +111,59 @@ struct QuestCinematic // Creature 0 - Anachronos, 1 - Fandral, 2 - Arygos, 3 - Merithra, 4 - Caelestrasz static QuestCinematic EventAnim[]= { - {ANACHRONOS_SAY_1, 0, 2000}, - {FANDRAL_SAY_1, 1, 4000}, - {MERITHRA_EMOTE_1, 3, 500}, - {MERITHRA_SAY_1, 3, 500}, - {ARYGOS_EMOTE_1, 2, 2000}, + {ANACHRONOS_SAY_1, 0, 2000}, + {FANDRAL_SAY_1, 1, 4000}, + {MERITHRA_EMOTE_1, 3, 500}, + {MERITHRA_SAY_1, 3, 500}, + {ARYGOS_EMOTE_1, 2, 2000}, {CAELESTRASZ_SAY_1, 4, 8000}, - {MERITHRA_SAY_2, 3, 6000}, + {MERITHRA_SAY_2, 3, 6000}, {0, 3, 2000}, - {MERITHRA_YELL_1, 3, 2500}, - {0, 3, 3000}, //Morph - {0, 3, 4000}, //EmoteLiftoff - {0, 3, 4000}, // spell - {0, 3, 1250}, //fly - {0, 3, 250}, //remove flags + {MERITHRA_YELL_1, 3, 2500}, + {0, 3, 3000}, //Morph + {0, 3, 4000}, //EmoteLiftoff + {0, 3, 4000}, // spell + {0, 3, 1250}, //fly + {0, 3, 250}, //remove flags {ARYGOS_SAY_1, 2, 3000}, {0, 3, 2000}, {ARYGOS_YELL_1, 2, 3000}, - {0, 3, 3000}, //Morph - {0, 3, 4000}, //EmoteLiftoff - {0, 3, 4000}, // spell - {0, 3, 1000}, //fly - {0, 3, 1000}, //remove flags + {0, 3, 3000}, //Morph + {0, 3, 4000}, //EmoteLiftoff + {0, 3, 4000}, // spell + {0, 3, 1000}, //fly + {0, 3, 1000}, //remove flags {CAELESTRASZ_SAY_2, 4, 5000}, {0, 3, 3000}, {CAELESTRASZ_YELL_1, 4, 3000}, - {0, 3, 3000}, //Morph - {0, 3, 4000}, //EmoteLiftoff - {0, 3, 2500}, // spell + {0, 3, 3000}, //Morph + {0, 3, 4000}, //EmoteLiftoff + {0, 3, 2500}, // spell {ANACHRONOS_SAY_2, 0, 2000}, - {0, 3, 250}, //fly - {0, 3, 25}, //remove flags + {0, 3, 250}, //fly + {0, 3, 25}, //remove flags {FANDRAL_SAY_2, 1, 3000}, - {ANACHRONOS_SAY_3, 0, 10000}, //Both run through the armies - {0, 3, 2000}, // Sands will stop - {0, 3, 8000}, // Summon Gate + {ANACHRONOS_SAY_3, 0, 10000}, //Both run through the armies + {0, 3, 2000}, // Sands will stop + {0, 3, 8000}, // Summon Gate {ANACHRONOS_SAY_4, 0, 4000}, - {0, 0, 2000}, //spell 1-> Arcane cosmetic (Mobs freeze) - {0, 0, 5000}, //Spell 2-> Arcane long cosmetic (barrier appears) (Barrier -> Glyphs) - {0, 0, 7000}, //BarrieR - {0, 0, 4000}, //Glyphs + {0, 0, 2000}, //spell 1-> Arcane cosmetic (Mobs freeze) + {0, 0, 5000}, //Spell 2-> Arcane long cosmetic (barrier appears) (Barrier -> Glyphs) + {0, 0, 7000}, //BarrieR + {0, 0, 4000}, //Glyphs {ANACHRONOS_SAY_5, 0, 2000}, - {0, 0, 4000}, // Roots - {FANDRAL_SAY_3, 1, 3000}, //Root Text - {FANDRAL_EMOTE_1, 1, 3000}, //falls knee + {0, 0, 4000}, // Roots + {FANDRAL_SAY_3, 1, 3000}, //Root Text + {FANDRAL_EMOTE_1, 1, 3000}, //falls knee {ANACHRONOS_SAY_6, 0, 3000}, {ANACHRONOS_SAY_7, 0, 3000}, {ANACHRONOS_SAY_8, 0, 8000}, - {ANACHRONOS_EMOTE_1, 0, 1000}, //Give Scepter + {ANACHRONOS_EMOTE_1, 0, 1000}, //Give Scepter {FANDRAL_SAY_4, 1, 3000}, - {FANDRAL_SAY_5, 1, 3000}, //->Equip hammer~Scepter, throw it at door - {FANDRAL_EMOTE_2, 1, 3000}, //Throw hammer at door. + {FANDRAL_SAY_5, 1, 3000}, //->Equip hammer~Scepter, throw it at door + {FANDRAL_EMOTE_2, 1, 3000}, //Throw hammer at door. {ANACHRONOS_SAY_9, 0, 3000}, - {FANDRAL_SAY_6, 1, 3000}, //fandral goes away + {FANDRAL_SAY_6, 1, 3000}, //fandral goes away {ANACHRONOS_EMOTE_2, 0, 3000}, {ANACHRONOS_EMOTE_3, 0, 3000}, {0, 0, 2000}, @@ -348,30 +226,30 @@ Position const SpawnLocation[] = {-8078.0f, 1518.0f, 2.61f, 3.141592f}, //Kaldorei Infantry {-8082.0f, 1516.0f, 2.61f, 3.141592f}, //Kaldorei Infantry - {-8088.0f, 1510.0f, 2.61f, 0.0f}, //Anubisath Conqueror - {-8084.0f, 1520.0f, 2.61f, 0.0f}, //Anubisath Conqueror - {-8088.0f, 1530.0f, 2.61f, 0.0f}, //Anubisath Conqueror - - {-8080.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8082.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8085.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8082.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8085.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8080.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Wasp - - {-8082.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8079.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8080.0f, 1531.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8079.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp - {-8080.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp - - {-8081.0f, 1514.0f, 2.61f, 0.0f}, //Qiraj Tank - {-8081.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank - {-8081.0f, 1526.0f, 2.61f, 0.0f}, //Qiraj Tank - {-8081.0f, 1512.0f, 2.61f, 0.0f}, //Qiraj Tank - {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank - {-8081.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Tank + {-8088.0f, 1510.0f, 2.61f, 0.0f}, //Anubisath Conqueror + {-8084.0f, 1520.0f, 2.61f, 0.0f}, //Anubisath Conqueror + {-8088.0f, 1530.0f, 2.61f, 0.0f}, //Anubisath Conqueror + + {-8080.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8082.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8085.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8082.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8085.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8080.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Wasp + + {-8082.0f, 1513.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8079.0f, 1523.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8080.0f, 1531.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8079.0f, 1516.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Wasp + {-8080.0f, 1518.0f, 2.61f, 0.0f}, //Qiraj Wasp + + {-8081.0f, 1514.0f, 2.61f, 0.0f}, //Qiraj Tank + {-8081.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank + {-8081.0f, 1526.0f, 2.61f, 0.0f}, //Qiraj Tank + {-8081.0f, 1512.0f, 2.61f, 0.0f}, //Qiraj Tank + {-8082.0f, 1520.0f, 2.61f, 0.0f}, //Qiraj Tank + {-8081.0f, 1528.0f, 2.61f, 0.0f}, //Qiraj Tank {-8082.0f, 1513.0f, 2.61f, 3.141592f}, //Anubisath Conqueror {-8082.0f, 1520.0f, 2.61f, 3.141592f}, //Anubisath Conqueror @@ -402,14 +280,36 @@ struct SpawnSpells static SpawnSpells SpawnCast[4] = { - {100000, 2000, 33652}, // Stop Time + {100000, 2000, 33652}, // Stoned {38500, 300000, 28528}, // Poison Cloud {58000, 300000, 35871}, // Frost Debuff (need correct spell) {80950, 300000, 42075}, // Fire Explosion (need correct spell however this one looks cool) }; + /*##### # npc_anachronos_the_ancient ######*/ + +enum AnachronosTheAncient +{ + NPC_QIRAJI_WASP = 15414, + NPC_QIRAJI_TANK = 15422, + NPC_KALDOREI_INFANTRY = 15423, + NPC_ANUBISATH_CONQUEROR = 15424, + SPELL_ARCANE_CHANNELING = 23017, + SPELL_NOXIOUS_BREATH = 24818, + SPELL_GREEN_DRAGON_TRANSFORM_DND = 25105, + SPELL_RED_DRAGON_TRANSFORM_DND = 25106, + SPELL_BLUE_DRAGON_TRANSFORM_DND = 25107, + SPELL_TIME_STOP = 25158, + SPELL_CALL_PRISMATIC_BARRIER = 25159, + SPELL_CALL_GLYPHS_OF_WARDING = 25166, + SPELL_CALL_ANCIENTS = 25167, + SPELL_THROW_HAMMER = 33806, + SPELL_FROST_BREATH = 50505, + SPELL_FLAME_BREATH = 54293 +}; + class npc_anachronos_the_ancient : public CreatureScript { public: @@ -464,10 +364,10 @@ public: if (!player) return; - Creature* Fandral = player->FindNearestCreature(C_FANDRAL_STAGHELM, 100.0f); - Creature* Arygos = player->FindNearestCreature(C_ARYGOS, 100.0f); - Creature* Caelestrasz = player->FindNearestCreature(C_CAELESTRASZ, 100.0f); - Creature* Merithra = player->FindNearestCreature(C_MERITHRA, 100.0f); + Creature* Fandral = player->FindNearestCreature(NPC_FANDRAL_STAGHELM, 100.0f); + Creature* Arygos = player->FindNearestCreature(NPC_ARYGOS, 100.0f); + Creature* Caelestrasz = player->FindNearestCreature(NPC_CAELESTRASZ, 100.0f); + Creature* Merithra = player->FindNearestCreature(NPC_MERITHRA_OF_THE_DREAM, 100.0f); if (!Fandral || !Arygos || !Caelestrasz || !Merithra) return; @@ -509,7 +409,7 @@ public: Merithra->AI()->Talk(MERITHRA_YELL_1); break; case 9: - Merithra->CastSpell(Merithra, 25105, true); + Merithra->CastSpell(Merithra, SPELL_GREEN_DRAGON_TRANSFORM_DND, true); break; case 10: Merithra->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); @@ -517,7 +417,7 @@ public: Merithra->GetMotionMaster()->MoveCharge(-8065, 1530, 6.61f, 3); break; case 11: - Merithra->CastSpell(Merithra, 24818, false); + Merithra->CastSpell(Merithra, SPELL_NOXIOUS_BREATH, false); break; case 12: Merithra->GetMotionMaster()->MoveCharge(-8100, 1530, 50, 42); @@ -536,7 +436,7 @@ public: Arygos->AI()->Talk(ARYGOS_YELL_1); break; case 17: - Arygos->CastSpell(Arygos, 25107, true); + Arygos->CastSpell(Arygos, SPELL_BLUE_DRAGON_TRANSFORM_DND, true); break; case 18: Arygos->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); @@ -544,7 +444,7 @@ public: Arygos->GetMotionMaster()->MoveCharge(-8065, 1530, 6.61f, 42); break; case 19: - Arygos->CastSpell(Arygos, 50505, false); + Arygos->CastSpell(Arygos, SPELL_FROST_BREATH, false); break; case 20: Arygos->GetMotionMaster()->MoveCharge(-8095, 1530, 50, 42); @@ -563,15 +463,15 @@ public: Caelestrasz->AI()->Talk(CAELESTRASZ_YELL_1); break; case 25: - Caelestrasz->CastSpell(Caelestrasz, 25106, true); + Caelestrasz->CastSpell(Caelestrasz, SPELL_RED_DRAGON_TRANSFORM_DND, true); break; case 26: - Caelestrasz->HandleEmoteCommand(254); + Caelestrasz->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); Caelestrasz->SetDisableGravity(true); Caelestrasz->GetMotionMaster()->MoveCharge(-8065, 1530, 7.61f, 4); break; case 27: - Caelestrasz->CastSpell(Caelestrasz, 54293, false); + Caelestrasz->CastSpell(Caelestrasz, SPELL_FLAME_BREATH, false); break; case 28: Talk(ANACHRONOS_SAY_2, Fandral); @@ -596,29 +496,29 @@ public: Caelestrasz->GetMotionMaster()->MoveCharge(-8050, 1473, 65, 15); break; //Text: sands will stop case 34: - DoCast(player, 23017, true);//Arcane Channeling + DoCast(player, SPELL_ARCANE_CHANNELING, true);//Arcane Channeling break; case 35: - me->CastSpell(-8088, 1520.43f, 2.67f, 25158, true); + me->CastSpell(-8088, 1520.43f, 2.67f, SPELL_TIME_STOP, true); break; case 36: - DoCast(player, 25159, true); + DoCast(player, SPELL_CALL_PRISMATIC_BARRIER, true); break; case 37: - me->SummonGameObject(GO_GATE_OF_AHN_QIRAJ, -8130, 1525, 17.5f, 0, 0, 0, 0, 0, 0); + me->SummonGameObject(GO_GATE_OF_AHN_QIRAJ, Position(-8130.f, 1525.f, 17.5f, 0.f), G3D::Quat(), 0); break; case 38: - DoCast(player, 25166, true); - me->SummonGameObject(GO_GLYPH_OF_AHN_QIRAJ, -8130, 1525, 17.5f, 0, 0, 0, 0, 0, 0); + DoCast(player, SPELL_CALL_GLYPHS_OF_WARDING, true); + me->SummonGameObject(GO_GLYPH_OF_AHN_QIRAJ, Position(-8130.f, 1525.f, 17.5f, 0.f), G3D::Quat(), 0); break; case 39: Talk(ANACHRONOS_SAY_5, Fandral); break; case 40: - Fandral->CastSpell(me, 25167, true); + Fandral->CastSpell(me, SPELL_CALL_ANCIENTS, true); break; case 41: - Fandral->SummonGameObject(GO_ROOTS_OF_AHN_QIRAJ, -8130, 1525, 17.5f, 0, 0, 0, 0, 0, 0); + Fandral->SummonGameObject(GO_ROOTS_OF_AHN_QIRAJ, Position(-8130.f, 1525.f, 17.5f, 0.f), G3D::Quat(), 0); Fandral->AI()->Talk(FANDRAL_SAY_3); break; case 42: @@ -649,11 +549,11 @@ public: break; case 50: Fandral->AI()->Talk(FANDRAL_EMOTE_2); - Fandral->CastSpell(-8127, 1525, 17.5f, 33806, true); + Fandral->CastSpell(-8127, 1525, 17.5f, SPELL_THROW_HAMMER, true); break; case 51: { - uint32 entries[4] = { 15423, 15424, 15414, 15422 }; + uint32 entries[4] = { NPC_KALDOREI_INFANTRY, NPC_ANUBISATH_CONQUEROR, NPC_QIRAJI_WASP, NPC_QIRAJI_TANK }; Unit* mob = NULL; for (uint8 i = 0; i < 4; ++i) { @@ -661,7 +561,7 @@ public: while (mob) { mob->RemoveFromWorld(); - mob = player->FindNearestCreature(15423, 50); + mob = player->FindNearestCreature(NPC_KALDOREI_INFANTRY, 50); } } break; @@ -706,7 +606,7 @@ public: me->SetDisplayId(15500); break; case 63: - me->HandleEmoteCommand(254); + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); me->SetDisableGravity(true); break; case 64: @@ -746,6 +646,13 @@ public: # npc_qiraj_war_spawn ######*/ +enum QirajWarSpawn +{ + SPELL_STONED_CHANNEL_CAST_VISUAL = 15533, + SPELL_SUMMON_POISON_CLOUD = 24319, + SPELL_STONED = 33652 +}; + class npc_qiraj_war_spawn : public CreatureScript { public: @@ -793,22 +700,22 @@ public: { if (!Timers) { - if (me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414) //all but Kaldorei Soldiers + if (me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP) //all but Kaldorei Soldiers { SpellTimer1 = SpawnCast[1].Timer1; SpellTimer2 = SpawnCast[2].Timer1; SpellTimer3 = SpawnCast[3].Timer1; } - if (me->GetEntry() == 15423 || me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414) + if (me->GetEntry() == NPC_KALDOREI_INFANTRY || me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP) SpellTimer4 = SpawnCast[0].Timer1; Timers = true; } - if (me->GetEntry() == 15424 || me->GetEntry() == 15422|| me->GetEntry() == 15414) + if (me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP) { if (SpellTimer1 <= diff) { DoCast(me, SpawnCast[1].SpellId); - DoCast(me, 24319); + DoCast(me, SPELL_SUMMON_POISON_CLOUD); SpellTimer1 = SpawnCast[1].Timer2; } else SpellTimer1 -= diff; if (SpellTimer2 <= diff) @@ -822,38 +729,38 @@ public: SpellTimer3 = SpawnCast[3].Timer2; } else SpellTimer3 -= diff; } - if (me->GetEntry() == 15423 || me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414) + if (me->GetEntry() == NPC_KALDOREI_INFANTRY || me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP) { if (SpellTimer4 <= diff) { me->RemoveAllAttackers(); me->AttackStop(); - DoCast(me, 15533); + DoCast(me, SPELL_STONED_CHANNEL_CAST_VISUAL); SpellTimer4 = SpawnCast[0].Timer2; } else SpellTimer4 -= diff; } if (!hasTarget) { Unit* target = NULL; - if (me->GetEntry() == 15424 || me->GetEntry() == 15422 || me->GetEntry() == 15414) - target = me->FindNearestCreature(15423, 20, true); - if (me->GetEntry() == 15423) + if (me->GetEntry() == NPC_ANUBISATH_CONQUEROR || me->GetEntry() == NPC_QIRAJI_TANK || me->GetEntry() == NPC_QIRAJI_WASP) + target = me->FindNearestCreature(NPC_KALDOREI_INFANTRY, 20, true); + if (me->GetEntry() == NPC_KALDOREI_INFANTRY) { uint8 tar = urand(0, 2); if (tar == 0) - target = me->FindNearestCreature(15422, 20, true); + target = me->FindNearestCreature(NPC_QIRAJI_TANK, 20, true); else if (tar == 1) - target = me->FindNearestCreature(15424, 20, true); + target = me->FindNearestCreature(NPC_ANUBISATH_CONQUEROR, 20, true); else if (tar == 2) - target = me->FindNearestCreature(15414, 20, true); + target = me->FindNearestCreature(NPC_QIRAJI_WASP, 20, true); } hasTarget = true; if (target) AttackStart(target); } - if (!(me->FindNearestCreature(15379, 60))) - DoCast(me, 33652); + if (!(me->FindNearestCreature(NPC_CAELESTRASZ, 60))) + DoCast(me, SPELL_STONED); if (!UpdateVictim()) { @@ -932,7 +839,7 @@ public: if (Creature* spawn = me->SummonCreature(WavesInfo[WaveCount].CreatureId, SpawnLocation[i], TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, desptimer)) { - if (spawn->GetEntry() == 15423) + if (spawn->GetEntry() == NPC_KALDOREI_INFANTRY) spawn->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15427 + rand32() % 4); if (i >= 30) WaveCount = 1; if (i >= 33) WaveCount = 2; @@ -1040,6 +947,14 @@ void npc_qiraj_war_spawn::npc_qiraj_war_spawnAI::JustDied(Unit* /*slayer*/) # go_crystalline_tear ######*/ +enum CrystallineTear +{ + ARYGOS_GNOME_FORM = 15418, + CAELESTRASZ_NIGHT_ELF_FORM = 15419, + MERITHRA_NIGHT_ELF_FORM = 15420, + ANACHRONOS_QUEST_TRIGGER_INVISIBLE = 15454 +}; + class go_crystalline_tear : public GameObjectScript { public: @@ -1049,35 +964,35 @@ public: { if (quest->GetQuestId() == QUEST_A_PAWN_ON_THE_ETERNAL_BOARD) { - if (Creature* trigger = go->FindNearestCreature(15454, 100)) + if (Creature* trigger = go->FindNearestCreature(ANACHRONOS_QUEST_TRIGGER_INVISIBLE, 100)) { - Unit* Merithra = trigger->SummonCreature(15378, -8034.535f, 1535.14f, 2.61f, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); - Unit* Caelestrasz = trigger->SummonCreature(15379, -8032.767f, 1533.148f, 2.61f, 1.5f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); - Unit* Arygos = trigger->SummonCreature(15380, -8034.52f, 1537.843f, 2.61f, 5.7f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); - /* Unit* Fandral = */ trigger->SummonCreature(15382, -8028.462f, 1535.843f, 2.61f, 3.141592f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); - Creature* Anachronos = trigger->SummonCreature(15381, -8028.75f, 1538.795f, 2.61f, 4, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); + Unit* Merithra = trigger->SummonCreature(NPC_MERITHRA_OF_THE_DREAM, -8034.535f, 1535.14f, 2.61f, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); + Unit* Caelestrasz = trigger->SummonCreature(NPC_CAELESTRASZ, -8032.767f, 1533.148f, 2.61f, 1.5f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); + Unit* Arygos = trigger->SummonCreature(NPC_ARYGOS, -8034.52f, 1537.843f, 2.61f, 5.7f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); + /* Unit* Fandral = */ trigger->SummonCreature(NPC_FANDRAL_STAGHELM, -8028.462f, 1535.843f, 2.61f, 3.141592f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); + Creature* Anachronos = trigger->SummonCreature(NPC_ANACHRONOS, -8028.75f, 1538.795f, 2.61f, 4, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 220000); if (Merithra) { - Merithra->SetUInt32Value(UNIT_NPC_FLAGS, 0); + Merithra->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); Merithra->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - Merithra->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15420); + Merithra->SetUInt32Value(UNIT_FIELD_DISPLAYID, MERITHRA_NIGHT_ELF_FORM); Merithra->setFaction(35); } if (Caelestrasz) { - Caelestrasz->SetUInt32Value(UNIT_NPC_FLAGS, 0); + Caelestrasz->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); Caelestrasz->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - Caelestrasz->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15419); + Caelestrasz->SetUInt32Value(UNIT_FIELD_DISPLAYID, CAELESTRASZ_NIGHT_ELF_FORM); Caelestrasz->setFaction(35); } if (Arygos) { - Arygos->SetUInt32Value(UNIT_NPC_FLAGS, 0); + Arygos->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); Arygos->SetUInt32Value(UNIT_FIELD_BYTES_1, 0); - Arygos->SetUInt32Value(UNIT_FIELD_DISPLAYID, 15418); + Arygos->SetUInt32Value(UNIT_FIELD_DISPLAYID, ARYGOS_GNOME_FORM); Arygos->setFaction(35); } @@ -1107,112 +1022,107 @@ public: enum WSSpells { - SPELL_PUNISHMENT = 24803, - SPELL_SPAWN_IN = 25035, - - AURA_TWILIGHT_SET = 24746, - AURA_MEDALLION = 24748, - AURA_RING = 24782, - - SPELL_TEMPLAR_RANDOM = 24745, - SPELL_TEMPLAR_FIRE = 24747, - SPELL_TEMPLAR_AIR = 24757, - SPELL_TEMPLAR_EARTH = 24759, - SPELL_TEMPLAR_WATER = 24761, - - SPELL_DUKE_RANDOM = 24762, - SPELL_DUKE_FIRE = 24766, - SPELL_DUKE_AIR = 24769, - SPELL_DUKE_EARTH = 24771, - SPELL_DUKE_WATER = 24773, - - SPELL_ROYAL_RANDOM = 24785, - SPELL_ROYAL_FIRE = 24787, - SPELL_ROYAL_AIR = 24791, - SPELL_ROYAL_EARTH = 24792, - SPELL_ROYAL_WATER = 24793 + AURA_TWILIGHT_SET = 24746, + AURA_MEDALLION = 24748, + AURA_RING = 24782, + + SPELL_TEMPLAR_RANDOM = 24745, + SPELL_TEMPLAR_FIRE = 24747, + SPELL_TEMPLAR_AIR = 24757, + SPELL_TEMPLAR_EARTH = 24759, + SPELL_TEMPLAR_WATER = 24761, + + SPELL_DUKE_RANDOM = 24762, + SPELL_DUKE_FIRE = 24766, + SPELL_DUKE_AIR = 24769, + SPELL_DUKE_EARTH = 24771, + SPELL_DUKE_WATER = 24773, + + SPELL_ROYAL_RANDOM = 24785, + SPELL_ROYAL_FIRE = 24787, + SPELL_ROYAL_AIR = 24791, + SPELL_ROYAL_EARTH = 24792, + SPELL_ROYAL_WATER = 24793, + + SPELL_PUNISHMENT = 24803, + SPELL_SPAWN_IN = 25035 }; enum WSGossip { - GOSSIPID_LESSER_WS = 6540, - GOSSIPID_WS = 6542, - GOSSIPID_GREATER_WS = 6543 + OPTION_ID_WS_RANDOM = 0, + OPTION_ID_1_CRIMSON = 1, + OPTION_ID_2_AZURE = 2, + OPTION_ID_3_EARTHEN = 3, + OPTION_ID_4_HOARY = 4, + OPTION_ID_1_CYNDERS = 1, + OPTION_ID_2_FATHOMS = 2, + OPTION_ID_3_SHARDS = 3, + OPTION_ID_4_ZEPHYRS = 4, + OPTION_ID_1_SKALDRENOX = 1, + OPTION_ID_2_SKWOL = 2, + OPTION_ID_3_KAZUM = 3, + OPTION_ID_4_WHIRLAXIS = 4, + GOSSIP_ID_LESSER_WS = 6540, + GOSSIP_ID_WIND_STONE = 6542, + GOSSIP_ID_GREATER_WS = 6543 }; enum WSCreatures { - NPC_TEMPLAR_FIRE = 15209, - NPC_TEMPLAR_WATER = 15211, - NPC_TEMPLAR_AIR = 15212, - NPC_TEMPLAR_EARTH = 15307, - - NPC_DUKE_FIRE = 15206, - NPC_DUKE_WATER = 15207, - NPC_DUKE_EARTH = 15208, - NPC_DUKE_AIR = 15220, - - NPC_ROYAL_FIRE = 15203, - NPC_ROYAL_AIR = 15204, - NPC_ROYAL_EARTH = 15205, - NPC_ROYAL_WATER = 15305 + NPC_TEMPLAR_FIRE = 15209, + NPC_TEMPLAR_WATER = 15211, + NPC_TEMPLAR_AIR = 15212, + NPC_TEMPLAR_EARTH = 15307, + + NPC_DUKE_FIRE = 15206, + NPC_DUKE_WATER = 15207, + NPC_DUKE_EARTH = 15208, + NPC_DUKE_AIR = 15220, + + NPC_ROYAL_FIRE = 15203, + NPC_ROYAL_AIR = 15204, + NPC_ROYAL_EARTH = 15205, + NPC_ROYAL_WATER = 15305 }; enum WSItems { - ITEM_TEMPLAR_FIRE = 20416, - ITEM_TEMPLAR_EARTH = 20419, - ITEM_TEMPLAR_WATER = 20420, - ITEM_TEMPLAR_AIR = 20418, - - ITEM_DUKE_FIRE = 20432, - ITEM_DUKE_EARTH = 20435, - ITEM_DUKE_WATER = 20436, - ITEM_DUKE_AIR = 20433, - - ITEM_ROYAL_FIRE = 20447, - ITEM_ROYAL_EARTH = 20449, - ITEM_ROYAL_WATER = 20450, - ITEM_ROYAL_AIR = 20448, + ITEM_TEMPLAR_FIRE = 20416, + ITEM_TEMPLAR_AIR = 20418, + ITEM_TEMPLAR_EARTH = 20419, + ITEM_TEMPLAR_WATER = 20420, + + ITEM_DUKE_FIRE = 20432, + ITEM_DUKE_AIR = 20433, + ITEM_DUKE_EARTH = 20435, + ITEM_DUKE_WATER = 20436, + + ITEM_ROYAL_FIRE = 20447, + ITEM_ROYAL_AIR = 20448, + ITEM_ROYAL_EARTH = 20449, + ITEM_ROYAL_WATER = 20450 }; enum WS { - TEMPLAR = 0, - DUKE = 1, - ROYAL = 2, - - FIRE = 0x1, - WATER = 0x2, - EARTH = 0x4, - AIR = 0x8 -}; + TEMPLAR = 0, + DUKE = 1, + ROYAL = 2, + + FIRE = 0x1, + WATER = 0x2, + EARTH = 0x4, + AIR = 0x8 +}; enum WSTexts { - SAY_TEMPLAR_AGGRO = 0, - SAY_DUKE_AGGRO = 0, - YELL_ROYAL_AGGRO = 0 + SAY_TEMPLAR_AGGRO = 0, + SAY_DUKE_AGGRO = 0, + YELL_ROYAL_AGGRO = 0 }; -#define GOSSIP_TEMPLAR_RANDOM "I am no cultist, you monster! Come to me and face your destruction!" -#define GOSSIP_TEMPLAR_FIRE "Crimson Templar! I hold your signet! Heed my call!" -#define GOSSIP_TEMPLAR_EARTH "Earthen Templar! I hold your signet! Heed my call!" -#define GOSSIP_TEMPLAR_AIR "Hoary Templar! I hold your signet! Heed my call!" -#define GOSSIP_TEMPLAR_WATER "Azure Templar! I hold your signet! Heed my call!" - -#define GOSSIP_DUKE_RANDOM "You will listen to this, vile duke! I am not your Twilight's Hammer lapdog! I am here to challenge you! Come! Come, and meet your death..." -#define GOSSIP_DUKE_FIRE "Duke of Cynders! I hold your signet! Heed my call!" -#define GOSSIP_DUKE_EARTH "The Duke of Shards! I hold your signet! Heed my call!" -#define GOSSIP_DUKE_AIR "The Duke of Zephyrs! I hold your signet! Heed my call!" -#define GOSSIP_DUKE_WATER "The Duke of Fathoms! I hold your signet! Heed my call!" - -#define GOSSIP_ROYAL_RANDOM "The day of the judgement has come, fiend! I challenge you to battle!" -#define GOSSIP_ROYAL_FIRE "Prince Skaldrenox! I hold your signet! Heed my call!" -#define GOSSIP_ROYAL_EARTH "Baron Kazum! I hold your signet! Heed my call!" -#define GOSSIP_ROYAL_AIR "High Marshal Whirlaxis! I hold your signet! Heed my call!" -#define GOSSIP_ROYAL_WATER "Lord Skwol! I hold your signet! Heed my call!" - class go_wind_stone : public GameObjectScript { public: @@ -1294,7 +1204,7 @@ class go_wind_stone : public GameObjectScript case NPC_TEMPLAR_WATER: case NPC_TEMPLAR_AIR: case NPC_TEMPLAR_EARTH: - summons->AI()->Talk(SAY_TEMPLAR_AGGRO); + summons->AI()->Talk(SAY_TEMPLAR_AGGRO, player); break; case NPC_DUKE_FIRE: @@ -1323,10 +1233,10 @@ class go_wind_stone : public GameObjectScript uint32 gossipId = go->GetGOInfo()->GetGossipMenuId(); switch (gossipId) { - case GOSSIPID_LESSER_WS: + case GOSSIP_ID_LESSER_WS: { if (rank >= 1) // 1 or 2 or 3 - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_WS_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); else { go->CastSpell(player, SPELL_PUNISHMENT); @@ -1335,19 +1245,19 @@ class go_wind_stone : public GameObjectScript uint8 item = GetItems(player, TEMPLAR); if (item & FIRE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_FIRE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_1_CRIMSON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); if (item & WATER) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_WATER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_2_AZURE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); if (item & EARTH) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_EARTH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_3_EARTHEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); if (item & AIR) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEMPLAR_AIR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_LESSER_WS, OPTION_ID_4_HOARY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); break; } - case GOSSIPID_WS: + case GOSSIP_ID_WIND_STONE: { if (rank >= 2) // 2 or 3 - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_WS_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); else { go->CastSpell(player, SPELL_PUNISHMENT); @@ -1356,19 +1266,19 @@ class go_wind_stone : public GameObjectScript uint8 item = GetItems(player, DUKE); if (item & FIRE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_FIRE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_1_CYNDERS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); if (item & WATER) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_WATER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_2_FATHOMS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); if (item & EARTH) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_EARTH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_3_SHARDS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); if (item & AIR) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DUKE_AIR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_WIND_STONE, OPTION_ID_4_ZEPHYRS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10); break; } - case GOSSIPID_GREATER_WS: + case GOSSIP_ID_GREATER_WS: { if (rank == 3) // 3 - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_WS_RANDOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11); else { go->CastSpell(player, SPELL_PUNISHMENT); @@ -1377,13 +1287,13 @@ class go_wind_stone : public GameObjectScript uint8 item = GetItems(player, ROYAL); if (item & FIRE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_FIRE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_1_SKALDRENOX, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12); if (item & WATER) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_WATER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_2_SKWOL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13); if (item & EARTH) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_EARTH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_3_KAZUM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 14); if (item & AIR) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ROYAL_AIR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 15); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_ID_GREATER_WS, OPTION_ID_4_WHIRLAXIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 15); break; } default: @@ -1462,6 +1372,5 @@ void AddSC_silithus() new npc_anachronos_quest_trigger(); new npc_anachronos_the_ancient(); new npc_qiraj_war_spawn(); - new npcs_rutgar_and_frankal(); new go_wind_stone(); } diff --git a/src/server/scripts/Kalimdor/zone_tanaris.cpp b/src/server/scripts/Kalimdor/zone_tanaris.cpp index 22b4c86cbcd..966bbaec1da 100644 --- a/src/server/scripts/Kalimdor/zone_tanaris.cpp +++ b/src/server/scripts/Kalimdor/zone_tanaris.cpp @@ -19,15 +19,13 @@ /* ScriptData SDName: Tanaris SD%Complete: 80 -SDComment: Quest support: 648, 1560, 2954, 4005, 10277, 10279(Special flight path). +SDComment: Quest support: 648, 1560, 4005, 10277 SDCategory: Tanaris EndScriptData */ /* ContentData npc_aquementas npc_custodian_of_time -npc_steward_of_time -npc_stone_watcher_of_norgannon npc_OOX17 npc_tooga EndContentData */ @@ -298,116 +296,6 @@ public: }; /*###### -## npc_steward_of_time -######*/ - -#define GOSSIP_ITEM_FLIGHT "Please take me to the master's lair." - -class npc_steward_of_time : public CreatureScript -{ -public: - npc_steward_of_time() : CreatureScript("npc_steward_of_time") { } - - bool OnQuestAccept(Player* player, Creature* /*creature*/, Quest const* quest) override - { - if (quest->GetQuestId() == 10279) //Quest: To The Master's Lair - player->CastSpell(player, 34891, true); //(Flight through Caverns) - - return false; - } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF + 1) - player->CastSpell(player, 34891, true); //(Flight through Caverns) - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(10279) == QUEST_STATUS_INCOMPLETE || player->GetQuestRewardStatus(10279)) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FLIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - player->SEND_GOSSIP_MENU(9978, creature->GetGUID()); - } - else - player->SEND_GOSSIP_MENU(9977, creature->GetGUID()); - - return true; - } - -}; - -/*###### -## npc_stone_watcher_of_norgannon -######*/ - -#define GOSSIP_ITEM_NORGANNON_1 "What function do you serve?" -#define GOSSIP_ITEM_NORGANNON_2 "What are the Plates of Uldum?" -#define GOSSIP_ITEM_NORGANNON_3 "Where are the Plates of Uldum?" -#define GOSSIP_ITEM_NORGANNON_4 "Excuse me? We've been \"reschedueled for visitations\"? What does that mean?!" -#define GOSSIP_ITEM_NORGANNON_5 "So, what's inside Uldum?" -#define GOSSIP_ITEM_NORGANNON_6 "I will return when i have the Plates of Uldum." - -class npc_stone_watcher_of_norgannon : public CreatureScript -{ -public: - npc_stone_watcher_of_norgannon() : CreatureScript("npc_stone_watcher_of_norgannon") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->SEND_GOSSIP_MENU(1675, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - player->SEND_GOSSIP_MENU(1676, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+2: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); - player->SEND_GOSSIP_MENU(1677, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+3: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); - player->SEND_GOSSIP_MENU(1678, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+4: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); - player->SEND_GOSSIP_MENU(1679, creature->GetGUID()); - break; - case GOSSIP_ACTION_INFO_DEF+5: - player->CLOSE_GOSSIP_MENU(); - player->AreaExploredOrEventHappens(2954); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(2954) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF); - - player->SEND_GOSSIP_MENU(1674, creature->GetGUID()); - - return true; - } - -}; - -/*###### ## npc_OOX17 ######*/ @@ -672,8 +560,6 @@ void AddSC_tanaris() { new npc_aquementas(); new npc_custodian_of_time(); - new npc_steward_of_time(); - new npc_stone_watcher_of_norgannon(); new npc_OOX17(); new npc_tooga(); } diff --git a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp index 85c3a621239..2a606856af6 100644 --- a/src/server/scripts/Kalimdor/zone_thousand_needles.cpp +++ b/src/server/scripts/Kalimdor/zone_thousand_needles.cpp @@ -19,7 +19,7 @@ /* ScriptData SDName: Thousand Needles SD%Complete: 100 -SDComment: Support for Quest: 1950, 4770, 4904, 4966, 5151. +SDComment: Support for Quest: 4770, 4904, 4966, 5151. SDCategory: Thousand Needles EndScriptData */ @@ -27,7 +27,6 @@ EndScriptData */ npc_kanati npc_lakota_windsong npc_swiftmountain -npc_plucky npc_enraged_panther go_panther_cage EndContentData */ @@ -44,10 +43,9 @@ EndContentData */ enum Kanati { - SAY_KAN_START = 0, - - QUEST_PROTECT_KANATI = 4966, - NPC_GALAK_ASS = 10720 + SAY_KAN_START = 0, + QUEST_PROTECT_KANATI = 4966, + NPC_GALAK_ASS = 10720 }; Position const GalakLoc = {-4867.387695f, -1357.353760f, -48.226f, 0.0f}; @@ -112,19 +110,19 @@ public: enum Lakota { - SAY_LAKO_START = 0, - SAY_LAKO_LOOK_OUT = 1, - SAY_LAKO_HERE_COME = 2, - SAY_LAKO_MORE = 3, - SAY_LAKO_END = 4, - - QUEST_FREE_AT_LAST = 4904, - NPC_GRIM_BANDIT = 10758, - FACTION_ESCORTEE_LAKO = 232, //guessed - - ID_AMBUSH_1 = 0, - ID_AMBUSH_2 = 2, - ID_AMBUSH_3 = 4 + SAY_LAKO_START = 0, + SAY_LAKO_LOOK_OUT = 1, + SAY_LAKO_HERE_COME = 2, + SAY_LAKO_MORE = 3, + SAY_LAKO_END = 4, + + QUEST_FREE_AT_LAST = 4904, + NPC_GRIM_BANDIT = 10758, + FACTION_ESCORTEE_LAKO = 232, //guessed + + ID_AMBUSH_1 = 0, + ID_AMBUSH_2 = 2, + ID_AMBUSH_3 = 4 }; Position const BanditLoc[6] = @@ -183,6 +181,7 @@ public: DoSpawnBandits(ID_AMBUSH_3); break; case 45: + Talk(SAY_LAKO_END); if (Player* player = GetPlayerForEscort()) player->GroupEventHappens(QUEST_FREE_AT_LAST, me); break; @@ -195,7 +194,6 @@ public: me->SummonCreature(NPC_GRIM_BANDIT, BanditLoc[i+AmbushId], TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000); } }; - }; /*###### @@ -204,13 +202,13 @@ public: enum Packa { - SAY_START = 0, - SAY_WYVERN = 1, - SAY_COMPLETE = 2, + SAY_START = 0, + SAY_WYVERN = 1, + SAY_COMPLETE = 2, - QUEST_HOMEWARD = 4770, - NPC_WYVERN = 4107, - FACTION_ESCORTEE = 232 //guessed + QUEST_HOMEWARD = 4770, + NPC_WYVERN = 4107, + FACTION_ESCORTEE = 232 //guessed }; Position const WyvernLoc[3] = @@ -275,137 +273,10 @@ public: }; }; -/*##### -# npc_plucky -######*/ - -#define GOSSIP_P "Please tell me the Phrase.." - -enum Plucky -{ - FACTION_FRIENDLY = 35, - QUEST_SCOOP = 1950, - SPELL_PLUCKY_HUMAN = 9192, - SPELL_PLUCKY_CHICKEN = 9220 -}; - -class npc_plucky : public CreatureScript -{ -public: - npc_plucky() : CreatureScript("npc_plucky") { } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF+1: - player->CLOSE_GOSSIP_MENU(); - player->CompleteQuest(QUEST_SCOOP); - break; - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_P, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - - player->SEND_GOSSIP_MENU(738, creature->GetGUID()); - - return true; - } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_pluckyAI(creature); - } - - struct npc_pluckyAI : public ScriptedAI - { - npc_pluckyAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - NormFaction = creature->getFaction(); - } - - void Initialize() - { - ResetTimer = 120000; - } - - uint32 NormFaction; - uint32 ResetTimer; - - void Reset() override - { - Initialize(); - - if (me->getFaction() != NormFaction) - me->setFaction(NormFaction); - - if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP)) - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - - DoCast(me, SPELL_PLUCKY_CHICKEN, false); - } - - void ReceiveEmote(Player* player, uint32 TextEmote) override - { - if (player->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE) - { - if (TextEmote == TEXT_EMOTE_BECKON) - { - me->setFaction(FACTION_FRIENDLY); - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - DoCast(me, SPELL_PLUCKY_HUMAN, false); - } - } - - if (TextEmote == TEXT_EMOTE_CHICKEN) - { - if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP)) - return; - else - { - me->setFaction(FACTION_FRIENDLY); - me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - DoCast(me, SPELL_PLUCKY_HUMAN, false); - me->HandleEmoteCommand(EMOTE_ONESHOT_WAVE); - } - } - } - - void UpdateAI(uint32 Diff) override - { - if (me->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP)) - { - if (ResetTimer <= Diff) - { - if (!me->GetVictim()) - EnterEvadeMode(); - else - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - - return; - } - else - ResetTimer -= Diff; - } - - if (!UpdateVictim()) - return; - - DoMeleeAttackIfReady(); - } - }; - -}; - enum PantherCage { - ENRAGED_PANTHER = 10992 + QUEST_HYPERCAPACITOR_GIZMO = 5151, + ENRAGED_PANTHER = 10992 }; class go_panther_cage : public GameObjectScript @@ -416,7 +287,7 @@ public: bool OnGossipHello(Player* player, GameObject* go) override { go->UseDoorOrButton(); - if (player->GetQuestStatus(5151) == QUEST_STATUS_INCOMPLETE) + if (player->GetQuestStatus(QUEST_HYPERCAPACITOR_GIZMO) == QUEST_STATUS_INCOMPLETE) { if (Creature* panther = go->FindNearestCreature(ENRAGED_PANTHER, 5, true)) { @@ -466,7 +337,6 @@ void AddSC_thousand_needles() new npc_kanati(); new npc_lakota_windsong(); new npc_paoka_swiftmountain(); - new npc_plucky(); new npc_enraged_panther(); new go_panther_cage(); } diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp index 3c11fd5d9a6..2f97b5a8055 100644 --- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp @@ -16,7 +16,8 @@ */ /* - * Comment: Complete - BUT THE TRIGGER NEEDS DATA WHETHER THE PRISON OF TALDARAM IS OFFLINE ! + * Comment: Visuals missing, de-germanize code, wow 3.3.5a-ize + * Patch 3.3.2 (2010-01-02): Jedoga Shadowseeker now only ascends once during the encounter. */ #include "ScriptMgr.h" diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp index 16cfb30e2dc..2df0fceab9c 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp @@ -156,7 +156,7 @@ public: ImpaleTarget = impaleTarget->GetGUID(); impaleTarget->SetReactState(REACT_PASSIVE); impaleTarget->SetDisplayId(DISPLAY_INVISIBLE); - impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); return impaleTarget; } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp index 7b8ca41772f..d6682e272fd 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_halion.cpp @@ -137,7 +137,8 @@ enum Events EVENT_CHECK_CORPOREALITY = 13, EVENT_SHADOW_PULSARS_SHOOT = 14, EVENT_TRIGGER_BERSERK = 15, - EVENT_TWILIGHT_MENDING = 16 + EVENT_TWILIGHT_MENDING = 16, + EVENT_ACTIVATE_EMBERS = 17 }; enum Actions @@ -150,7 +151,9 @@ enum Actions ACTION_MONITOR_CORPOREALITY = 3, // Orb Carrier - ACTION_SHOOT = 4 + ACTION_WARNING_SHOOT = 4, + ACTION_SHOOT = 5, + ACTION_ACTIVATE_EMBERS = 6 }; enum Phases @@ -168,8 +171,7 @@ enum Misc DATA_MATERIAL_DAMAGE_TAKEN = 2, DATA_STACKS_DISPELLED = 3, DATA_FIGHT_PHASE = 4, - DATA_EVADE_METHOD = 5, - DATA_SPAWNED_FLAMES = 6, + DATA_SPAWNED_FLAMES = 5, }; enum OrbCarrierSeats @@ -189,6 +191,7 @@ enum CorporealityEvent }; Position const HalionSpawnPos = {3156.67f, 533.8108f, 72.98822f, 3.159046f}; +Position const HalionRespawnPos = {3156.625f, 533.2674f, 72.97205f, 0.0f}; uint8 const MAX_CORPOREALITY_STATE = 11; @@ -212,132 +215,39 @@ CorporealityEntry const _corporealityReference[MAX_CORPOREALITY_STATE] = { {74831, 74836} }; -struct generic_halionAI : public BossAI -{ - generic_halionAI(Creature* creature, uint32 bossId) : BossAI(creature, bossId), _canEvade(false) { } - - void EnterCombat(Unit* /*who*/) override - { - _EnterCombat(); - me->AddAura(SPELL_TWILIGHT_PRECISION, me); - _canEvade = false; - events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000)); - events.ScheduleEvent(EVENT_TAIL_LASH, 10000); - events.ScheduleEvent(EVENT_BREATH, urand(10000, 15000)); - } - - void Reset() override - { - _canEvade = false; - _Reset(); - } - - void JustReachedHome() override - { - instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); - _JustReachedHome(); - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_CLEAVE: - DoCastVictim(SPELL_CLEAVE); - events.ScheduleEvent(EVENT_CLEAVE, urand(8000, 10000)); - break; - case EVENT_TAIL_LASH: - DoCastAOE(SPELL_TAIL_LASH); - events.ScheduleEvent(EVENT_TAIL_LASH, 10000); - break; - case EVENT_BREATH: - DoCast(me, me->GetEntry() == NPC_HALION ? SPELL_FLAME_BREATH : SPELL_DARK_BREATH); - events.ScheduleEvent(EVENT_BREATH, urand(10000, 12000)); - break; - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) - return; - - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - ExecuteEvent(eventId); - - DoMeleeAttackIfReady(); - } - - void SetData(uint32 index, uint32 dataValue) override - { - switch (index) - { - case DATA_EVADE_METHOD: - _canEvade = (dataValue == 1); - break; - default: - break; - } - } - - void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo) override - { - if (spellInfo->Id == SPELL_TWILIGHT_MENDING) - Talk(SAY_REGENERATE); - } - -protected: - bool _canEvade; -}; - class boss_halion : public CreatureScript { public: boss_halion() : CreatureScript("boss_halion") { } - struct boss_halionAI : public generic_halionAI + struct boss_halionAI : public BossAI { - boss_halionAI(Creature* creature) : generic_halionAI(creature, DATA_HALION) - { - me->SetHomePosition(HalionSpawnPos); - } - - void Reset() override - { - generic_halionAI::Reset(); - me->SetReactState(REACT_DEFENSIVE); - me->RemoveAurasDueToSpell(SPELL_TWILIGHT_PHASING); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - } + boss_halionAI(Creature* creature) : BossAI(creature, DATA_HALION) { } void EnterEvadeMode(EvadeReason why) override { - if (why == EVADE_REASON_BOUNDARY) + if (why == EVADE_REASON_BOUNDARY || events.IsInPhase(PHASE_ONE)) if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) - controller->AI()->EnterEvadeMode(); - - // Phase 1: We always can evade. Phase 2 & 3: We can evade if and only if the controller tells us to. - if (events.IsInPhase(PHASE_ONE) || _canEvade) - generic_halionAI::EnterEvadeMode(why); + controller->AI()->EnterEvadeMode(why); } - void EnterCombat(Unit* who) override + void EnterCombat(Unit* /*who*/) override { Talk(SAY_AGGRO); events.Reset(); events.SetPhase(PHASE_ONE); - generic_halionAI::EnterCombat(who); + _EnterCombat(); + me->AddAura(SPELL_TWILIGHT_PRECISION, me); + events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, Seconds(5)); + events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(5), Seconds(15))); + events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(6), Seconds(10))); + events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(7), Seconds(12))); + events.ScheduleEvent(EVENT_FIERY_COMBUSTION, randtime(Seconds(15), Seconds(18))); + events.ScheduleEvent(EVENT_METEOR_STRIKE, Seconds(18)); instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); - instance->SetBossState(DATA_HALION, IN_PROGRESS); - - events.ScheduleEvent(EVENT_ACTIVATE_FIREWALL, 5000); - events.ScheduleEvent(EVENT_METEOR_STRIKE, urand(20000, 25000)); - events.ScheduleEvent(EVENT_FIERY_COMBUSTION, urand(15000, 18000)); if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) controller->AI()->SetData(DATA_FIGHT_PHASE, PHASE_ONE); @@ -388,58 +298,76 @@ class boss_halion : public CreatureScript } } + void SpellHit(Unit* /*who*/, SpellInfo const* spellInfo) override + { + if (spellInfo->Id == SPELL_TWILIGHT_MENDING) + Talk(SAY_REGENERATE); + } + void UpdateAI(uint32 diff) override { if (events.IsInPhase(PHASE_TWO)) return; - generic_halionAI::UpdateAI(diff); - } + if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) + return; - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - case EVENT_ACTIVATE_FIREWALL: - // Flame ring is activated 5 seconds after starting encounter, DOOR_TYPE_ROOM is only instant. - for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i) - if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(i))) - instance->HandleGameObject(instance->GetGuidData(DATA_FLAME_RING), false, flameRing); - break; - case EVENT_METEOR_STRIKE: + switch (eventId) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM)) + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(8), Seconds(10))); + break; + case EVENT_TAIL_LASH: + DoCastAOE(SPELL_TAIL_LASH); + events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(11), Seconds(16))); + break; + case EVENT_BREATH: + DoCast(me, SPELL_FLAME_BREATH); + events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(16), Seconds(25))); + break; + case EVENT_ACTIVATE_FIREWALL: + // Flame ring is activated 5 seconds after starting encounter, DOOR_TYPE_ROOM is only instant. + for (uint8 i = DATA_FLAME_RING; i <= DATA_TWILIGHT_FLAME_RING; ++i) + if (GameObject* flameRing = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(i))) + instance->HandleGameObject(instance->GetGuidData(DATA_FLAME_RING), false, flameRing); + break; + case EVENT_METEOR_STRIKE: { - _meteorStrikePos = target->GetPosition(); - me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, NULL, NULL, me->GetGUID()); - Talk(SAY_METEOR_STRIKE); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true, -SPELL_TWILIGHT_REALM)) + { + _meteorStrikePos = target->GetPosition(); + me->CastSpell(_meteorStrikePos.GetPositionX(), _meteorStrikePos.GetPositionY(), _meteorStrikePos.GetPositionZ(), SPELL_METEOR_STRIKE, true, nullptr, nullptr, me->GetGUID()); + Talk(SAY_METEOR_STRIKE); + } + events.ScheduleEvent(EVENT_METEOR_STRIKE, Seconds(38)); + break; } - events.ScheduleEvent(EVENT_METEOR_STRIKE, 40000); - break; - } - case EVENT_FIERY_COMBUSTION: - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM)) - DoCast(target, SPELL_FIERY_COMBUSTION); - events.ScheduleEvent(EVENT_FIERY_COMBUSTION, 25000); - break; + case EVENT_FIERY_COMBUSTION: + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, -SPELL_TWILIGHT_REALM)) + me->CastSpell(target, SPELL_FIERY_COMBUSTION, TRIGGERED_IGNORE_SET_FACING); + events.ScheduleEvent(EVENT_FIERY_COMBUSTION, Seconds(25)); + break; + } + default: + break; } - default: - generic_halionAI::ExecuteEvent(eventId); - break; } + + DoMeleeAttackIfReady(); } void SetData(uint32 index, uint32 value) override { - switch (index) - { - case DATA_FIGHT_PHASE: - events.SetPhase(value); - break; - default: - generic_halionAI::SetData(index, value); - } + if (index != DATA_FIGHT_PHASE) + return; + + events.SetPhase(value); } private: @@ -459,9 +387,9 @@ class boss_twilight_halion : public CreatureScript public: boss_twilight_halion() : CreatureScript("boss_twilight_halion") { } - struct boss_twilight_halionAI : public generic_halionAI + struct boss_twilight_halionAI : public BossAI { - boss_twilight_halionAI(Creature* creature) : generic_halionAI(creature, DATA_TWILIGHT_HALION) + boss_twilight_halionAI(Creature* creature) : BossAI(creature, DATA_TWILIGHT_HALION) { Creature* halion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION)); if (!halion) @@ -470,26 +398,29 @@ class boss_twilight_halion : public CreatureScript // Using AddAura because no spell cast packet in sniffs. halion->AddAura(SPELL_COPY_DAMAGE, me); // We use explicit targeting here to avoid conditions + SPELL_ATTR6_CANT_TARGET_SELF. me->AddAura(SPELL_COPY_DAMAGE, halion); - me->AddAura(SPELL_DUSK_SHROUD, me); + DoCast(me, SPELL_DUSK_SHROUD, true); me->SetHealth(halion->GetHealth()); me->SetPhaseMask(0x20, true); - me->SetReactState(REACT_AGGRESSIVE); + me->SetReactState(REACT_DEFENSIVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + events.ScheduleEvent(EVENT_TAIL_LASH, Seconds(12)); + events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, Seconds(15)); } - void EnterCombat(Unit* who) override + void EnterCombat(Unit* /*who*/) override { - events.Reset(); events.SetPhase(PHASE_TWO); - generic_halionAI::EnterCombat(who); - - events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000); + _EnterCombat(); + me->AddAura(SPELL_TWILIGHT_PRECISION, me); + events.ScheduleEvent(EVENT_CLEAVE, Seconds(3)); + events.ScheduleEvent(EVENT_BREATH, Seconds(12)); instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 2); } - // Never evade + void Reset() override { } void EnterEvadeMode(EvadeReason /*why*/) override { } void KilledUnit(Unit* victim) override @@ -522,6 +453,10 @@ class boss_twilight_halion : public CreatureScript void DamageTaken(Unit* attacker, uint32& damage) override { + //Needed because we already have UNIT_FLAG_IN_COMBAT, otherwise EnterCombat won't ever be called + if (!events.IsInPhase(PHASE_TWO) && !events.IsInPhase(PHASE_THREE)) + EnterCombat(attacker); + if (me->HealthBelowPctDamaged(50, damage) && events.IsInPhase(PHASE_TWO)) { events.SetPhase(PHASE_THREE); @@ -542,7 +477,7 @@ class boss_twilight_halion : public CreatureScript } } - void SpellHit(Unit* who, SpellInfo const* spell) override + void SpellHit(Unit* /*who*/, SpellInfo const* spell) override { switch (spell->Id) { @@ -550,25 +485,51 @@ class boss_twilight_halion : public CreatureScript if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) controller->AI()->DoAction(ACTION_MONITOR_CORPOREALITY); break; + case SPELL_TWILIGHT_MENDING: + Talk(SAY_REGENERATE); + break; default: - generic_halionAI::SpellHit(who, spell); break; } } - void ExecuteEvent(uint32 eventId) override + void UpdateAI(uint32 diff) override { - switch (eventId) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (!UpdateVictim()) + return; + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - case EVENT_SOUL_CONSUMPTION: - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM)) - DoCast(target, SPELL_SOUL_CONSUMPTION); - events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, 20000); - break; - default: - generic_halionAI::ExecuteEvent(eventId); - break; + switch (eventId) + { + case EVENT_CLEAVE: + DoCastVictim(SPELL_CLEAVE); + events.ScheduleEvent(EVENT_CLEAVE, randtime(Seconds(7), Seconds(10))); + break; + case EVENT_TAIL_LASH: + DoCastAOE(SPELL_TAIL_LASH); + events.ScheduleEvent(EVENT_TAIL_LASH, randtime(Seconds(12), Seconds(16))); + break; + case EVENT_BREATH: + DoCast(me, SPELL_DARK_BREATH); + events.ScheduleEvent(EVENT_BREATH, randtime(Seconds(10), Seconds(14))); + break; + case EVENT_SOUL_CONSUMPTION: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0.0f, true, SPELL_TWILIGHT_REALM)) + me->CastSpell(target, SPELL_SOUL_CONSUMPTION, TRIGGERED_IGNORE_SET_FACING); + events.ScheduleEvent(EVENT_SOUL_CONSUMPTION, Seconds(20)); + break; + default: + break; + } } + + DoMeleeAttackIfReady(); } }; @@ -589,7 +550,6 @@ class npc_halion_controller : public CreatureScript _instance(creature->GetInstanceScript()), _summons(me) { Initialize(); - me->SetPhaseMask(me->GetPhaseMask() | 0x20, true); } void Initialize() @@ -599,6 +559,15 @@ class npc_halion_controller : public CreatureScript _twilightDamageTaken = 0; } + void JustRespawned() override + { + if (_instance->GetGuidData(DATA_HALION)) + return; + + Reset(); + me->GetMap()->SummonCreature(NPC_HALION, HalionRespawnPos); + } + void Reset() override { _summons.DespawnAll(); @@ -626,21 +595,36 @@ class npc_halion_controller : public CreatureScript _twilightDamageTaken = 0; _materialDamageTaken = 0; - _events.ScheduleEvent(EVENT_TRIGGER_BERSERK, 8 * MINUTE * IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_TRIGGER_BERSERK, Minutes(8)); } - void JustReachedHome() override + void EnterEvadeMode(EvadeReason /*why*/) override { if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION))) + { twilightHalion->DespawnOrUnsummon(); + _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, twilightHalion); + } if (Creature* halion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION))) { - halion->AI()->SetData(DATA_EVADE_METHOD, 1); - halion->AI()->EnterEvadeMode(); + halion->DespawnOrUnsummon(); + _instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, halion); } _instance->SetBossState(DATA_HALION, FAIL); + _summons.DespawnAll(); + + uint32 corpseDelay = me->GetCorpseDelay(); + uint32 respawnDelay = me->GetRespawnDelay(); + + me->SetCorpseDelay(1); + me->SetRespawnDelay(30); + + me->DespawnOrUnsummon(); + + me->SetCorpseDelay(corpseDelay); + me->SetRespawnDelay(respawnDelay); } void DoAction(int32 action) override @@ -650,7 +634,16 @@ class npc_halion_controller : public CreatureScript case ACTION_INTRO_HALION: _events.Reset(); _events.SetPhase(PHASE_INTRO); - _events.ScheduleEvent(EVENT_START_INTRO, 2000); + _events.ScheduleEvent(EVENT_START_INTRO, Seconds(2)); + break; + case ACTION_INTRO_HALION_2: + if (_instance->GetGuidData(DATA_HALION)) + return; + + for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_4; ++i) + if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i))) + _instance->HandleGameObject(_instance->GetGuidData(i), true, tree); + me->GetMap()->SummonCreature(NPC_HALION, HalionRespawnPos); break; case ACTION_MONITOR_CORPOREALITY: { @@ -678,8 +671,12 @@ class npc_halion_controller : public CreatureScript _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_MATERIAL, 50); _instance->DoUpdateWorldState(WORLDSTATE_CORPOREALITY_TWILIGHT, 50); - _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 7500); + _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, Seconds(7)); + break; } + case ACTION_ACTIVATE_EMBERS: + _events.ScheduleEvent(EVENT_ACTIVATE_EMBERS, Seconds(6)); + break; default: break; } @@ -692,7 +689,7 @@ class npc_halion_controller : public CreatureScript // combat state. if (!_events.IsInPhase(PHASE_INTRO) && me->IsInCombat() && !UpdateVictim()) { - EnterEvadeMode(); + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); return; } @@ -704,29 +701,31 @@ class npc_halion_controller : public CreatureScript { case EVENT_START_INTRO: DoCast(me, SPELL_COSMETIC_FIRE_PILLAR, true); - _events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, 4000); + _events.ScheduleEvent(EVENT_INTRO_PROGRESS_1, Seconds(4)); break; case EVENT_INTRO_PROGRESS_1: for (uint8 i = DATA_BURNING_TREE_3; i <= DATA_BURNING_TREE_4; ++i) if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i))) _instance->HandleGameObject(_instance->GetGuidData(i), true, tree); - _events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, 4000); + _events.ScheduleEvent(EVENT_INTRO_PROGRESS_2, Seconds(4)); break; case EVENT_INTRO_PROGRESS_2: for (uint8 i = DATA_BURNING_TREE_1; i <= DATA_BURNING_TREE_2; ++i) if (GameObject* tree = ObjectAccessor::GetGameObject(*me, _instance->GetGuidData(i))) _instance->HandleGameObject(_instance->GetGuidData(i), true, tree); - _events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, 4000); + _events.ScheduleEvent(EVENT_INTRO_PROGRESS_3, Seconds(4)); break; case EVENT_INTRO_PROGRESS_3: DoCast(me, SPELL_FIERY_EXPLOSION); + if (_instance->GetGuidData(DATA_HALION)) + return; if (Creature* halion = me->GetMap()->SummonCreature(NPC_HALION, HalionSpawnPos)) halion->AI()->Talk(SAY_INTRO); break; case EVENT_TWILIGHT_MENDING: if (ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_HALION))) // Just check if physical Halion is spawned if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION))) - twilightHalion->CastSpell((Unit*)NULL, SPELL_TWILIGHT_MENDING, true); + twilightHalion->CastSpell((Unit*)nullptr, SPELL_TWILIGHT_MENDING, true); break; case EVENT_TRIGGER_BERSERK: for (uint8 i = DATA_HALION; i <= DATA_TWILIGHT_HALION; i++) @@ -734,17 +733,16 @@ class npc_halion_controller : public CreatureScript halion->CastSpell(halion, SPELL_BERSERK, true); break; case EVENT_SHADOW_PULSARS_SHOOT: - if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION))) - twilightHalion->AI()->Talk(SAY_SPHERE_PULSE); - if (Creature* orbCarrier = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ORB_CARRIER))) - orbCarrier->AI()->DoAction(ACTION_SHOOT); - - _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000); + orbCarrier->AI()->DoAction(ACTION_WARNING_SHOOT); + _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, Seconds(30)); break; case EVENT_CHECK_CORPOREALITY: UpdateCorporeality(); - _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, 5000); + _events.ScheduleEvent(EVENT_CHECK_CORPOREALITY, Seconds(5)); + break; + case EVENT_ACTIVATE_EMBERS: + _summons.DoZoneInCombat(NPC_LIVING_EMBER); break; default: break; @@ -770,7 +768,7 @@ class npc_halion_controller : public CreatureScript DoZoneInCombat(); break; case PHASE_TWO: - _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, 29000); + _events.ScheduleEvent(EVENT_SHADOW_PULSARS_SHOOT, Seconds(35)); break; default: break; @@ -793,7 +791,7 @@ class npc_halion_controller : public CreatureScript uint8 oldValue = _materialCorporealityValue; if (_twilightDamageTaken == 0 || _materialDamageTaken == 0) { - _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100); + _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, Milliseconds(100)); _twilightDamageTaken = 0; _materialDamageTaken = 0; return; @@ -833,7 +831,7 @@ class npc_halion_controller : public CreatureScript } case CORPOREALITY_TWILIGHT_MENDING: { - _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, 100); + _events.ScheduleEvent(EVENT_TWILIGHT_MENDING, Milliseconds(100)); _materialDamageTaken = 0; _twilightDamageTaken = 0; return; @@ -889,52 +887,73 @@ class npc_orb_carrier : public CreatureScript struct npc_orb_carrierAI : public ScriptedAI { npc_orb_carrierAI(Creature* creature) : ScriptedAI(creature), - instance(creature->GetInstanceScript()) + _instance(creature->GetInstanceScript()) { ASSERT(creature->GetVehicleKit()); } - void UpdateAI(uint32 /*diff*/) override + void UpdateAI(uint32 diff) override { /// According to sniffs this spell is cast every 1 or 2 seconds. /// However, refreshing it looks bad, so just cast the spell if /// we are not channeling it. if (!me->HasUnitState(UNIT_STATE_CASTING)) - me->CastSpell((Unit*)NULL, SPELL_TRACK_ROTATION, false); + me->CastSpell((Unit*)nullptr, SPELL_TRACK_ROTATION, false); + + scheduler.Update(diff); /// Workaround: This is here because even though the above spell has SPELL_ATTR1_CHANNEL_TRACK_TARGET, /// we are having two creatures involded here. This attribute is handled clientside, meaning the client /// sends orientation update itself. Here, no packet is sent, and the creature does not rotate. By /// forcing the carrier to always be facing the rotation focus, we ensure everything works as it should. - if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ORB_ROTATION_FOCUS))) + if (Creature* rotationFocus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ORB_ROTATION_FOCUS))) me->SetFacingToObject(rotationFocus); // setInFront } void DoAction(int32 action) override { - if (action == ACTION_SHOOT) + switch (action) { - Vehicle* vehicle = me->GetVehicleKit(); - Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH); - Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH); - if (southOrb && northOrb) + case ACTION_WARNING_SHOOT: { - if (northOrb->GetTypeId() == TYPEID_UNIT) + Vehicle* vehicle = me->GetVehicleKit(); + Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH); + if (northOrb && northOrb->GetTypeId() == TYPEID_UNIT) northOrb->ToCreature()->AI()->Talk(EMOTE_WARN_LASER); - TriggerCutter(northOrb, southOrb); + + scheduler.Schedule(Seconds(5), [this](TaskContext /*context*/) + { + DoAction(ACTION_SHOOT); + }); + break; } + case ACTION_SHOOT: + { + Vehicle* vehicle = me->GetVehicleKit(); + Unit* southOrb = vehicle->GetPassenger(SEAT_SOUTH); + Unit* northOrb = vehicle->GetPassenger(SEAT_NORTH); + if (southOrb && northOrb) + TriggerCutter(northOrb, southOrb); - if (!IsHeroic()) - return; + if (Creature* twilightHalion = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_TWILIGHT_HALION))) + twilightHalion->AI()->Talk(SAY_SPHERE_PULSE); + + if (!IsHeroic()) + return; - Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST); - Unit* westOrb = vehicle->GetPassenger(SEAT_WEST); - if (eastOrb && westOrb) - TriggerCutter(eastOrb, westOrb); + Unit* eastOrb = vehicle->GetPassenger(SEAT_EAST); + Unit* westOrb = vehicle->GetPassenger(SEAT_WEST); + if (eastOrb && westOrb) + TriggerCutter(eastOrb, westOrb); + break; + } + default: + break; } } private: - InstanceScript* instance; + InstanceScript* _instance; + TaskScheduler scheduler; void TriggerCutter(Unit* caster, Unit* target) { @@ -987,12 +1006,12 @@ class npc_meteor_strike_initial : public CreatureScript controller->AI()->JustSummoned(me); DoCast(me, SPELL_METEOR_STRIKE_COUNTDOWN); - DoCast(me, SPELL_BIRTH_NO_VISUAL); // Unknown purpose + DoCast(me, SPELL_BIRTH_NO_VISUAL); if (HalionAI* halionAI = CAST_AI(HalionAI, owner->AI())) { Position const* ownerPos = halionAI->GetMeteorStrikePosition(); - float randomAdjustment = frand(0.0f, static_cast<float>(M_PI / 5.0f)); + float randomAdjustment = frand(static_cast<float>(M_PI / 5.0f), static_cast<float>(M_PI / 2.0f)); float angle[4]; angle[0] = me->GetAngle(ownerPos); angle[1] = angle[0] + randomAdjustment; @@ -1006,10 +1025,7 @@ class npc_meteor_strike_initial : public CreatureScript me->SetOrientation(angle[i]); Position newPos = me->GetNearPosition(10.0f, 0.0f); // Exact distance if (Creature* meteor = me->SummonCreature(NPC_METEOR_STRIKE_NORTH + i, newPos, TEMPSUMMON_TIMED_DESPAWN, 30000)) - { - meteor->SetOrientation(angle[i]); _meteorList.push_back(meteor); - } } } } @@ -1046,7 +1062,7 @@ class npc_meteor_strike : public CreatureScript { DoCast(me, SPELL_METEOR_STRIKE_FIRE_AURA_2, true); me->setActive(true); - _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 500); + _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, Milliseconds(500)); } } @@ -1110,7 +1126,7 @@ class npc_meteor_strike_flame : public CreatureScript void SetGUID(ObjectGuid guid, int32 /*id = 0 */) override { _rootOwnerGuid = guid; - _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, 800); + _events.ScheduleEvent(EVENT_SPAWN_METEOR_FLAME, Milliseconds(800)); } void IsSummonedBy(Unit* /*summoner*/) override @@ -1202,7 +1218,7 @@ class npc_combustion_consumption : public CreatureScript if (type != DATA_STACKS_DISPELLED || !_damageSpell || !_explosionSpell || !summoner) return; - me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount, me); + me->CastCustomSpell(SPELL_SCALE_AURA, SPELLVALUE_AURA_STACK, stackAmount + 1, me); DoCast(me, _damageSpell); int32 damage = 1200 + (stackAmount * 1290); // Needs more research. @@ -1240,17 +1256,32 @@ class npc_living_inferno : public CreatureScript // SMSG_SPELL_GO for the living ember stuff isn't even sent to the client - Blizzard on drugs. if (me->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC) - me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true); + scheduler.Schedule(Seconds(3), [this](TaskContext /*context*/) + { + me->CastSpell(me, SPELL_SPAWN_LIVING_EMBERS, true); + }); if (InstanceScript* instance = me->GetInstanceScript()) if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HALION_CONTROLLER))) + { + controller->AI()->DoAction(ACTION_ACTIVATE_EMBERS); controller->AI()->JustSummoned(me); + } } void JustDied(Unit* /*killer*/) override { me->DespawnOrUnsummon(1); } + + void UpdateAI(uint32 diff) override + { + scheduler.Update(diff); + ScriptedAI::UpdateAI(diff); + } + + private: + TaskScheduler scheduler; }; CreatureAI* GetAI(Creature* creature) const override @@ -1266,27 +1297,7 @@ class npc_living_ember : public CreatureScript struct npc_living_emberAI : public ScriptedAI { - npc_living_emberAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - _enrageTimer = 0; - } - - void Initialize() - { - _hasEnraged = false; - } - - void Reset() override - { - Initialize(); - } - - void EnterCombat(Unit* /*who*/) override - { - _enrageTimer = 20000; - _hasEnraged = false; - } + npc_living_emberAI(Creature* creature) : ScriptedAI(creature) { } void IsSummonedBy(Unit* /*summoner*/) override { @@ -1299,25 +1310,6 @@ class npc_living_ember : public CreatureScript { me->DespawnOrUnsummon(1); } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim() || me->HasUnitState(UNIT_STATE_CASTING)) - return; - - if (!_hasEnraged && _enrageTimer <= diff) - { - _hasEnraged = true; - DoCast(me, SPELL_BERSERK); - } - else _enrageTimer -= diff; - - DoMeleeAttackIfReady(); - } - - private: - uint32 _enrageTimer; - bool _hasEnraged; }; CreatureAI* GetAI(Creature* creature) const override @@ -1473,6 +1465,47 @@ class spell_halion_combustion_consumption : public SpellScriptLoader uint32 _spellID; }; +class spell_halion_combustion_consumption_periodic : public SpellScriptLoader +{ + public: + spell_halion_combustion_consumption_periodic() : SpellScriptLoader("spell_halion_combustion_consumption_periodic") { } + + class spell_halion_combustion_consumption_periodic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_halion_combustion_consumption_periodic_AuraScript); + + bool Validate(SpellInfo const* spellInfo) override + { + if (!sSpellMgr->GetSpellInfo(spellInfo->Effects[EFFECT_0].TriggerSpell)) + return false; + return true; + } + + void HandleTick(AuraEffect const* aurEff) + { + PreventDefaultAction(); + Unit* caster = GetCaster(); + if (!caster) + return; + + uint32 triggerSpell = GetSpellInfo()->Effects[aurEff->GetEffIndex()].TriggerSpell; + int32 radius = caster->GetObjectScale() * M_PI * 10000 / 3; + + caster->CastCustomSpell(triggerSpell, SPELLVALUE_RADIUS_MOD, radius, (Unit*)nullptr, TRIGGERED_FULL_MASK, nullptr, aurEff, caster->GetGUID()); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_halion_combustion_consumption_periodic_AuraScript::HandleTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_halion_combustion_consumption_periodic_AuraScript(); + } +}; + class spell_halion_marks : public SpellScriptLoader { public: @@ -1511,7 +1544,7 @@ class spell_halion_marks : public SpellScriptLoader return; // Stacks marker - GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, NULL, NULL, GetCasterGUID()); + GetTarget()->CastCustomSpell(_summonSpellId, SPELLVALUE_BASE_POINT1, aurEff->GetBase()->GetStackAmount(), GetTarget(), TRIGGERED_FULL_MASK, nullptr, nullptr, GetCasterGUID()); } void Register() override @@ -1819,6 +1852,35 @@ class spell_halion_spawn_living_embers : public SpellScriptLoader } }; +class spell_halion_blazing_aura : public SpellScriptLoader +{ + public: + spell_halion_blazing_aura() : SpellScriptLoader("spell_halion_blazing_aura") { } + + class spell_halion_blazing_aura_SpellScript : public SpellScript + { + PrepareSpellScript(spell_halion_blazing_aura_SpellScript); + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + GetHitUnit()->CastSpell(GetHitUnit(), GetSpellInfo()->Effects[EFFECT_1].TriggerSpell); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_halion_blazing_aura_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_FORCE_CAST); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_halion_blazing_aura_SpellScript(); + } +}; + + + void AddSC_boss_halion() { new boss_halion(); @@ -1840,6 +1902,7 @@ void AddSC_boss_halion() new spell_halion_combustion_consumption("spell_halion_fiery_combustion", SPELL_MARK_OF_COMBUSTION); new spell_halion_marks("spell_halion_mark_of_combustion", SPELL_FIERY_COMBUSTION_SUMMON, SPELL_FIERY_COMBUSTION); new spell_halion_marks("spell_halion_mark_of_consumption", SPELL_SOUL_CONSUMPTION_SUMMON, SPELL_SOUL_CONSUMPTION); + new spell_halion_combustion_consumption_periodic(); new spell_halion_damage_aoe_summon(); new spell_halion_twilight_realm_handlers("spell_halion_leave_twilight_realm", SPELL_SOUL_CONSUMPTION, false); new spell_halion_twilight_realm_handlers("spell_halion_enter_twilight_realm", SPELL_FIERY_COMBUSTION, true); @@ -1848,4 +1911,5 @@ void AddSC_boss_halion() new spell_halion_twilight_cutter(); new spell_halion_clear_debuffs(); new spell_halion_spawn_living_embers(); + new spell_halion_blazing_aura(); } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp index 739c3602f44..be85d3dc99c 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/instance_ruby_sanctum.cpp @@ -53,11 +53,11 @@ class instance_ruby_sanctum : public InstanceMapScript void OnPlayerEnter(Player* /*player*/) override { - if (!GetGuidData(DATA_HALION_CONTROLLER) && GetBossState(DATA_HALION) != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE) + if (!GetGuidData(DATA_HALION) && GetBossState(DATA_HALION) != DONE && GetBossState(DATA_GENERAL_ZARITHRIAN) == DONE) { instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY()); - if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos)) - halionController->AI()->DoAction(ACTION_INTRO_HALION); + if (Creature* halionController = instance->GetCreature(GetGuidData(DATA_HALION_CONTROLLER))) + halionController->AI()->DoAction(ACTION_INTRO_HALION_2); } } @@ -161,6 +161,12 @@ class instance_ruby_sanctum : public InstanceMapScript } } + void OnCreatureRemove(Creature* creature) override + { + if (creature->GetEntry() == NPC_HALION) + HalionGUID = ObjectGuid::Empty; + } + void OnUnitDeath(Unit* unit) override { Creature* creature = unit->ToCreature(); @@ -170,7 +176,7 @@ class instance_ruby_sanctum : public InstanceMapScript if (creature->GetEntry() == NPC_GENERAL_ZARITHRIAN && GetBossState(DATA_HALION) != DONE) { instance->LoadGrid(HalionControllerSpawnPos.GetPositionX(), HalionControllerSpawnPos.GetPositionY()); - if (Creature* halionController = instance->SummonCreature(NPC_HALION_CONTROLLER, HalionControllerSpawnPos)) + if (Creature* halionController = instance->GetCreature(GetGuidData(DATA_HALION_CONTROLLER))) halionController->AI()->DoAction(ACTION_INTRO_HALION); } } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h index 69c27a22ea0..333fef1dc85 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/ruby_sanctum.h @@ -56,6 +56,7 @@ enum SharedActions ACTION_INTRO_BALTHARUS = -3975101, ACTION_BALTHARUS_DEATH = -3975102, ACTION_INTRO_HALION = -4014601, + ACTION_INTRO_HALION_2 = -4014602, }; enum CreaturesIds diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp index 00900d440c9..f6726d60db3 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheChampion/instance_trial_of_the_champion.cpp @@ -180,7 +180,7 @@ public: { pAnnouncer->GetMotionMaster()->MovePoint(0, 748.309f, 619.487f, 411.171f); pAnnouncer->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_CHAMPIONS_LOOT_H : GO_CHAMPIONS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, 0, 0, 0, 0, 90000); + pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_CHAMPIONS_LOOT_H : GO_CHAMPIONS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, G3D::Quat(), 90000); } } } @@ -203,7 +203,7 @@ public: { pAnnouncer->GetMotionMaster()->MovePoint(0, 748.309f, 619.487f, 411.171f); pAnnouncer->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_EADRIC_LOOT_H : GO_EADRIC_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, 0, 0, 0, 0, 90000); + pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_EADRIC_LOOT_H : GO_EADRIC_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, G3D::Quat(), 90000); } break; case BOSS_ARGENT_CHALLENGE_P: @@ -212,7 +212,7 @@ public: { pAnnouncer->GetMotionMaster()->MovePoint(0, 748.309f, 619.487f, 411.171f); pAnnouncer->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_PALETRESS_LOOT_H : GO_PALETRESS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, 0, 0, 0, 0, 90000); + pAnnouncer->SummonGameObject(instance->IsHeroic()? GO_PALETRESS_LOOT_H : GO_PALETRESS_LOOT, 746.59f, 618.49f, 411.09f, 1.42f, G3D::Quat(), 90000); } break; } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp index 7440984d7c5..dfe0cacdef7 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp @@ -222,10 +222,10 @@ class boss_anubarak_trial : public CreatureScript instance->SetBossState(BOSS_ANUBARAK, FAIL); //Summon Scarab Swarms neutral at random places for (int i = 0; i < 10; i++) - if (Creature* temp = me->SummonCreature(NPC_SCARAB, AnubarakLoc[1].GetPositionX()+urand(0, 50)-25, AnubarakLoc[1].GetPositionY()+urand(0, 50)-25, AnubarakLoc[1].GetPositionZ())) + if (Creature* scarab = me->SummonCreature(NPC_SCARAB, AnubarakLoc[1].GetPositionX()+urand(0, 50)-25, AnubarakLoc[1].GetPositionY()+urand(0, 50)-25, AnubarakLoc[1].GetPositionZ())) { - temp->setFaction(31); - temp->GetMotionMaster()->MoveRandom(10); + scarab->setFaction(31); + scarab->GetMotionMaster()->MoveRandom(10); } } @@ -881,9 +881,9 @@ class spell_anubarak_leeching_swarm : public SpellScriptLoader if (lifeLeeched < 250) lifeLeeched = 250; // Damage - caster->CastCustomSpell(target, SPELL_LEECHING_SWARM_DMG, &lifeLeeched, 0, 0, false); + caster->CastCustomSpell(target, SPELL_LEECHING_SWARM_DMG, &lifeLeeched, 0, 0, true); // Heal - caster->CastCustomSpell(caster, SPELL_LEECHING_SWARM_HEAL, &lifeLeeched, 0, 0, false); + caster->CastCustomSpell(caster, SPELL_LEECHING_SWARM_HEAL, &lifeLeeched, 0, 0, true); } } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp index 6c1b516c7de..ee44e1391b4 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp @@ -453,22 +453,22 @@ class boss_toc_champion_controller : public CreatureScript for (uint8 i = 0; i < vChampionEntries.size(); ++i) { uint8 pos = urand(0, vChampionJumpTarget.size()-1); - if (Creature* temp = me->SummonCreature(vChampionEntries[i], vChampionJumpOrigin[urand(0, vChampionJumpOrigin.size()-1)], TEMPSUMMON_MANUAL_DESPAWN)) + if (Creature* champion = me->SummonCreature(vChampionEntries[i], vChampionJumpOrigin[urand(0, vChampionJumpOrigin.size()-1)], TEMPSUMMON_MANUAL_DESPAWN)) { - _summons.Summon(temp); - temp->SetReactState(REACT_PASSIVE); - temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); + _summons.Summon(champion); + champion->SetReactState(REACT_PASSIVE); + champion->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); if (playerTeam == ALLIANCE) { - temp->SetHomePosition(vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 0); - temp->GetMotionMaster()->MoveJump(vChampionJumpTarget[pos], 20.0f, 20.0f); - temp->SetOrientation(0); + champion->SetHomePosition(vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 0); + champion->GetMotionMaster()->MoveJump(vChampionJumpTarget[pos], 20.0f, 20.0f); + champion->SetOrientation(0); } else { - temp->SetHomePosition((ToCCommonLoc[1].GetPositionX()*2)-vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 3); - temp->GetMotionMaster()->MoveJump((ToCCommonLoc[1].GetPositionX() * 2) - vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), vChampionJumpTarget[pos].GetOrientation(), 20.0f, 20.0f); - temp->SetOrientation(3); + champion->SetHomePosition((ToCCommonLoc[1].GetPositionX()*2)-vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 3); + champion->GetMotionMaster()->MoveJump((ToCCommonLoc[1].GetPositionX() * 2) - vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), vChampionJumpTarget[pos].GetOrientation(), 20.0f, 20.0f); + champion->SetOrientation(3); } } vChampionJumpTarget.erase(vChampionJumpTarget.begin()+pos); @@ -485,10 +485,10 @@ class boss_toc_champion_controller : public CreatureScript case 1: for (SummonList::iterator i = _summons.begin(); i != _summons.end(); ++i) { - if (Creature* temp = ObjectAccessor::GetCreature(*me, *i)) + if (Creature* summon = ObjectAccessor::GetCreature(*me, *i)) { - temp->SetReactState(REACT_AGGRESSIVE); - temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); + summon->SetReactState(REACT_AGGRESSIVE); + summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); } } break; @@ -640,12 +640,12 @@ struct boss_faction_championsAI : public BossAI if (TeamInInstance == ALLIANCE) { - if (Creature* temp = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_VARIAN))) - temp->AI()->Talk(SAY_KILL_PLAYER); + if (Creature* varian = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_VARIAN))) + varian->AI()->Talk(SAY_KILL_PLAYER); } else - if (Creature* temp = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GARROSH))) - temp->AI()->Talk(SAY_KILL_PLAYER); + if (Creature* garrosh = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_GARROSH))) + garrosh->AI()->Talk(SAY_KILL_PLAYER); } } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp index 2fba0c2af42..93823987d78 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_lord_jaraxxus.cpp @@ -67,10 +67,12 @@ enum BossSpells SPELL_SHIVAN_SLASH = 67098, SPELL_SPINNING_STRIKE = 66283, SPELL_MISTRESS_KISS = 66336, - SPELL_FEL_INFERNO = 67047, - SPELL_FEL_STREAK = 66494, SPELL_LORD_HITTIN = 66326, // special effect preventing more specific spells be cast on the same player within 10 seconds - SPELL_MISTRESS_KISS_DAMAGE_SILENCE = 66359 + SPELL_MISTRESS_KISS_DAMAGE_SILENCE = 66359, + + // Felflame Infernal + SPELL_FEL_STREAK_VISUAL = 66493, + SPELL_FEL_STREAK = 66494, }; enum Events @@ -116,7 +118,7 @@ class boss_jaraxxus : public CreatureScript _JustReachedHome(); instance->SetBossState(BOSS_JARAXXUS, FAIL); DoCast(me, SPELL_JARAXXUS_CHAINS); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); } void KilledUnit(Unit* who) override @@ -305,18 +307,17 @@ class npc_fel_infernal : public CreatureScript { npc_fel_infernalAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); _instance = creature->GetInstanceScript(); } - void Initialize() - { - _felStreakTimer = 30 * IN_MILLISECONDS; - } - void Reset() override { - Initialize(); + _scheduler.Schedule(Seconds(2), [this](TaskContext context) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) + DoCast(target, SPELL_FEL_STREAK_VISUAL); + context.Repeat(Seconds(15)); + }); me->SetInCombatWithZone(); } @@ -328,23 +329,20 @@ class npc_fel_infernal : public CreatureScript return; } + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + if (!UpdateVictim()) return; - if (_felStreakTimer <= diff) + _scheduler.Update(diff, [this] { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) - DoCast(target, SPELL_FEL_STREAK); - _felStreakTimer = 30*IN_MILLISECONDS; - } - else - _felStreakTimer -= diff; - - DoMeleeAttackIfReady(); + DoMeleeAttackIfReady(); + }); } private: - uint32 _felStreakTimer; InstanceScript* _instance; + TaskScheduler _scheduler; }; CreatureAI* GetAI(Creature* creature) const override @@ -579,6 +577,39 @@ class spell_mistress_kiss_area : public SpellScriptLoader } }; +class spell_fel_streak_visual : public SpellScriptLoader +{ +public: + spell_fel_streak_visual() : SpellScriptLoader("spell_fel_streak_visual") { } + + class spell_fel_streak_visual_SpellScript : public SpellScript + { + PrepareSpellScript(spell_fel_streak_visual_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_FEL_STREAK)) + return false; + return true; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetHitUnit(), uint32(GetEffectValue()), true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_fel_streak_visual_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_fel_streak_visual_SpellScript(); + } +}; + void AddSC_boss_jaraxxus() { new boss_jaraxxus(); @@ -590,4 +621,5 @@ void AddSC_boss_jaraxxus() new spell_mistress_kiss(); new spell_mistress_kiss_area(); + new spell_fel_streak_visual(); } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp index e3720503d0a..b2283ec6fde 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp @@ -25,6 +25,7 @@ #include "Vehicle.h" #include "Player.h" #include "SpellScript.h" +#include "SpellAuraEffects.h" enum Yells { @@ -33,6 +34,7 @@ enum Yells // Acidmaw & Dreadscale EMOTE_ENRAGE = 0, + SAY_SPECIAL = 1, // Icehowl EMOTE_TRAMPLE_START = 0, @@ -78,22 +80,29 @@ enum BossSpells SPELL_FIRE_BOMB_DOT = 66318, SPELL_HEAD_CRACK = 66407, - //Acidmaw & Dreadscale + //Acidmaw & Dreadscale Generic + SPELL_SWEEP = 66794, + SUMMON_SLIME_POOL = 66883, + SPELL_EMERGE = 66947, + SPELL_SUBMERGE = 66948, + SPELL_ENRAGE = 68335, + SPELL_SLIME_POOL_EFFECT = 66882, //In 60s it diameter grows from 10y to 40y (r=r+0.25 per second) + SPELL_GROUND_VISUAL_0 = 66969, + SPELL_GROUND_VISUAL_1 = 68302, + SPELL_HATE_TO_ZERO = 63984, + //Acidmaw SPELL_ACID_SPIT = 66880, SPELL_PARALYTIC_SPRAY = 66901, - SPELL_ACID_SPEW = 66819, - SPELL_PARALYTIC_BITE = 66824, - SPELL_SWEEP_0 = 66794, - SUMMON_SLIME_POOL = 66883, - SPELL_FIRE_SPIT = 66796, + SPELL_PARALYTIC_BITE = 66824, //Paralytic Toxin + SPELL_ACID_SPEW = 66818, + SPELL_PARALYSIS = 66830, + SPELL_PARALYTIC_TOXIN = 66823, + //Dreadscale + SPELL_BURNING_BITE = 66879, // Burning Bile SPELL_MOLTEN_SPEW = 66821, - SPELL_BURNING_BITE = 66879, + SPELL_FIRE_SPIT = 66796, SPELL_BURNING_SPRAY = 66902, - SPELL_SWEEP_1 = 67646, - SPELL_EMERGE_0 = 66947, - SPELL_SUBMERGE_0 = 66948, - SPELL_ENRAGE = 68335, - SPELL_SLIME_POOL_EFFECT = 66882, //In 60s it diameter grows from 10y to 40y (r=r+0.25 per second) + SPELL_BURNING_BILE = 66869, //Icehowl SPELL_FEROCIOUS_BUTT = 66770, @@ -182,7 +191,7 @@ class boss_gormok : public CreatureScript { case 0: instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); me->SetReactState(REACT_AGGRESSIVE); me->SetInCombatWithZone(); break; @@ -549,7 +558,7 @@ struct boss_jormungarAI : public BossAI if (!Enraged && instance->GetData(TYPE_NORTHREND_BEASTS) == SNAKES_SPECIAL) { - me->RemoveAurasDueToSpell(SPELL_SUBMERGE_0); + me->RemoveAurasDueToSpell(SPELL_SUBMERGE); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); DoCast(SPELL_ENRAGE); Enraged = true; @@ -586,10 +595,11 @@ struct boss_jormungarAI : public BossAI case EVENT_SUMMON_ACIDMAW: if (Creature* acidmaw = me->SummonCreature(NPC_ACIDMAW, ToCCommonLoc[9].GetPositionX(), ToCCommonLoc[9].GetPositionY(), ToCCommonLoc[9].GetPositionZ(), 5, TEMPSUMMON_MANUAL_DESPAWN)) { - acidmaw->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + acidmaw->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); acidmaw->SetReactState(REACT_AGGRESSIVE); acidmaw->SetInCombatWithZone(); - acidmaw->CastSpell(acidmaw, SPELL_EMERGE_0); + acidmaw->CastSpell(acidmaw, SPELL_EMERGE); + acidmaw->CastSpell(acidmaw, SPELL_GROUND_VISUAL_1, true); } return; case EVENT_SPRAY: @@ -598,7 +608,7 @@ struct boss_jormungarAI : public BossAI events.ScheduleEvent(EVENT_SPRAY, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_STATIONARY); return; case EVENT_SWEEP: - DoCastAOE(SPELL_SWEEP_0); + DoCastAOE(SPELL_SWEEP); events.ScheduleEvent(EVENT_SWEEP, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_STATIONARY); return; default: @@ -608,13 +618,14 @@ struct boss_jormungarAI : public BossAI if (events.IsInPhase(PHASE_MOBILE)) DoMeleeAttackIfReady(); if (events.IsInPhase(PHASE_STATIONARY)) - DoSpellAttackIfReady(SpitSpell); + DoCastVictim(SpitSpell); } void Submerge() { - DoCast(me, SPELL_SUBMERGE_0); - me->RemoveAurasDueToSpell(SPELL_EMERGE_0); + DoCast(me, SPELL_SUBMERGE); + DoCast(me, SPELL_GROUND_VISUAL_0, true); + me->RemoveAurasDueToSpell(SPELL_EMERGE); me->SetInCombatWithZone(); events.SetPhase(PHASE_SUBMERGED); events.ScheduleEvent(EVENT_EMERGE, 5*IN_MILLISECONDS, 0, PHASE_SUBMERGED); @@ -625,17 +636,20 @@ struct boss_jormungarAI : public BossAI void Emerge() { - DoCast(me, SPELL_EMERGE_0); + DoCast(me, SPELL_EMERGE); + DoCastAOE(SPELL_HATE_TO_ZERO, true); me->SetDisplayId(ModelMobile); - me->RemoveAurasDueToSpell(SPELL_SUBMERGE_0); + me->RemoveAurasDueToSpell(SPELL_SUBMERGE); + me->RemoveAurasDueToSpell(SPELL_GROUND_VISUAL_0); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); // if the worm was mobile before submerging, make him stationary now if (WasMobile) { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); SetCombatMovement(false); me->SetDisplayId(ModelStationary); + me->CastSpell(me, SPELL_GROUND_VISUAL_1, true); events.SetPhase(PHASE_STATIONARY); events.ScheduleEvent(EVENT_SUBMERGE, 45*IN_MILLISECONDS, 0, PHASE_STATIONARY); events.ScheduleEvent(EVENT_SPIT, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_STATIONARY); @@ -644,10 +658,11 @@ struct boss_jormungarAI : public BossAI } else { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); SetCombatMovement(true); me->GetMotionMaster()->MoveChase(me->GetVictim()); me->SetDisplayId(ModelMobile); + me->RemoveAurasDueToSpell(SPELL_GROUND_VISUAL_1); events.SetPhase(PHASE_MOBILE); events.ScheduleEvent(EVENT_SUBMERGE, 45*IN_MILLISECONDS, 0, PHASE_MOBILE); events.ScheduleEvent(EVENT_BITE, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS), 0, PHASE_MOBILE); @@ -737,7 +752,7 @@ class boss_dreadscale : public CreatureScript { case 0: instance->DoCloseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); me->SetReactState(REACT_AGGRESSIVE); me->SetInCombatWithZone(); break; @@ -910,7 +925,7 @@ class boss_icehowl : public CreatureScript break; case 2: instance->DoUseDoorOrButton(instance->GetGuidData(GO_MAIN_GATE_DOOR)); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); me->SetReactState(REACT_AGGRESSIVE); me->SetInCombatWithZone(); break; @@ -1008,7 +1023,7 @@ class boss_icehowl : public CreatureScript me->SetTarget(_trampleTargetGUID); _trampleCast = false; SetCombatMovement(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->GetMotionMaster()->Clear(); me->GetMotionMaster()->MoveIdle(); events.ScheduleEvent(EVENT_TRAMPLE, 4*IN_MILLISECONDS); @@ -1092,7 +1107,7 @@ class boss_icehowl : public CreatureScript Talk(EMOTE_TRAMPLE_FAIL); } _movementStarted = false; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); SetCombatMovement(true); me->GetMotionMaster()->MovementExpired(); me->GetMotionMaster()->Clear(); @@ -1122,6 +1137,148 @@ class boss_icehowl : public CreatureScript } }; +class spell_jormungars_paralytic_toxin : public SpellScriptLoader +{ +public: + spell_jormungars_paralytic_toxin() : SpellScriptLoader("spell_jormungars_paralytic_toxin") { } + + class spell_jormungars_paralytic_toxin_AuraScript : public AuraScript + { + PrepareAuraScript(spell_jormungars_paralytic_toxin_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PARALYSIS)) + return false; + return true; + } + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + Unit* caster = GetCaster(); + if (caster && caster->GetEntry() == NPC_ACIDMAW) + { + if (Creature* acidMaw = caster->ToCreature()) + acidMaw->AI()->Talk(SAY_SPECIAL, GetTarget()); + } + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAurasDueToSpell(SPELL_PARALYSIS); + } + + void CalculateAmount(AuraEffect const* aurEff, int32& amount, bool& canBeRecalculated) + { + if (!canBeRecalculated) + amount = aurEff->GetAmount(); + + canBeRecalculated = false; + } + + void HandleDummy(AuraEffect const* /*aurEff*/) + { + if (AuraEffect* slowEff = GetEffect(EFFECT_0)) + { + int32 newAmount = slowEff->GetAmount() - 10; + if (newAmount < -100) + newAmount = -100; + slowEff->ChangeAmount(newAmount); + + if (newAmount <= -100 && !GetTarget()->HasAura(SPELL_PARALYSIS)) + GetTarget()->CastSpell(GetTarget(), SPELL_PARALYSIS, true, NULL, slowEff, GetCasterGUID()); + } + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_jormungars_paralytic_toxin_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_jormungars_paralytic_toxin_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL); + DoEffectCalcAmount += AuraEffectCalcAmountFn(spell_jormungars_paralytic_toxin_AuraScript::CalculateAmount, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_jormungars_paralytic_toxin_AuraScript::HandleDummy, EFFECT_2, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_jormungars_paralytic_toxin_AuraScript(); + } +}; + +class spell_jormungars_snakes_spray : public SpellScriptLoader +{ +public: + spell_jormungars_snakes_spray(const char* name, uint32 spellId) : SpellScriptLoader(name), _spellId(spellId) { } + + class spell_jormungars_snakes_spray_SpellScript : public SpellScript + { + PrepareSpellScript(spell_jormungars_snakes_spray_SpellScript); + + public: + spell_jormungars_snakes_spray_SpellScript(uint32 spellId) : SpellScript(), _spellId(spellId) { } + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(_spellId)) + return false; + return true; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + if (Player* target = GetHitPlayer()) + GetCaster()->CastSpell(target, _spellId, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_jormungars_snakes_spray_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } + + private: + uint32 _spellId; + }; + + SpellScript* GetSpellScript() const override + { + return new spell_jormungars_snakes_spray_SpellScript(_spellId); + } + +private: + uint32 _spellId; +}; + +class spell_jormungars_paralysis : public SpellScriptLoader +{ +public: + spell_jormungars_paralysis() : SpellScriptLoader("spell_jormungars_paralysis") { } + + class spell_jormungars_paralysis_AuraScript : public AuraScript + { + PrepareAuraScript(spell_jormungars_paralysis_AuraScript); + + void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* caster = GetCaster()) + if (InstanceScript* instance = caster->GetInstanceScript()) + if (instance->GetData(TYPE_NORTHREND_BEASTS) == SNAKES_IN_PROGRESS || instance->GetData(TYPE_NORTHREND_BEASTS) == SNAKES_SPECIAL) + return; + + Remove(); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_jormungars_paralysis_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_STUN, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_jormungars_paralysis_AuraScript(); + } +}; + void AddSC_boss_northrend_beasts() { new boss_gormok(); @@ -1132,6 +1289,10 @@ void AddSC_boss_northrend_beasts() new boss_acidmaw(); new boss_dreadscale(); new npc_slime_pool(); + new spell_jormungars_paralytic_toxin(); + new spell_jormungars_snakes_spray("spell_jormungars_burning_spray", SPELL_BURNING_BILE); + new spell_jormungars_snakes_spray("spell_jormungars_paralytic_spray", SPELL_PARALYTIC_TOXIN); + new spell_jormungars_paralysis(); new boss_icehowl(); } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp index 278f6a7ab4f..c6ac59218ea 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_twin_valkyr.cpp @@ -440,10 +440,8 @@ class boss_fjola : public CreatureScript // Allocate an unique random stage to each position in the array. for (int i = 0; i < MAX_STAGES - 1; ++i) { - int random = i + (rand32() % (MAX_STAGES - i)); - int temp = Stage[i]; - Stage[i] = Stage[random]; - Stage[random] = temp; + int random = i + urand(0, MAX_STAGES - i); + std::swap(Stage[i], Stage[random]); } } private: @@ -804,8 +802,8 @@ class spell_valkyr_essences : public SpellScriptLoader else { owner->CastSpell(owner, poweringUp, true); - if (Aura* pTemp = owner->GetAura(poweringUp)) - pTemp->ModStackAmount(stacksCount); + if ((pAura = owner->GetAura(poweringUp))) + pAura->ModStackAmount(stacksCount); } } } @@ -829,8 +827,8 @@ class spell_valkyr_essences : public SpellScriptLoader else { owner->CastSpell(owner, poweringUp, true); - if (Aura* pTemp = owner->GetAura(poweringUp)) - pTemp->ModStackAmount(stacksCount); + if ((pAura = owner->GetAura(poweringUp))) + pAura->ModStackAmount(stacksCount); } } } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp index b1a0f0217c4..eb4840f3b8b 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/instance_trial_of_the_crusader.cpp @@ -314,7 +314,7 @@ class instance_trial_of_the_crusader : public InstanceMapScript if (tributeChest) if (Creature* tirion = instance->GetCreature(TirionGUID)) - if (GameObject* chest = tirion->SummonGameObject(tributeChest, 805.62f, 134.87f, 142.16f, 3.27f, 0, 0, 0, 0, WEEK)) + if (GameObject* chest = tirion->SummonGameObject(tributeChest, 805.62f, 134.87f, 142.16f, 3.27f, G3D::Quat(), WEEK)) chest->SetRespawnTime(chest->GetRespawnDelay()); break; } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp index 0ffe74932e0..8ea292d1de5 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.cpp @@ -174,7 +174,7 @@ class npc_announcer_toc10 : public CreatureScript if (Creature* jaraxxus = ObjectAccessor::GetCreature(*player, instance->GetGuidData(NPC_JARAXXUS))) { jaraxxus->RemoveAurasDueToSpell(SPELL_JARAXXUS_CHAINS); - jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); jaraxxus->SetReactState(REACT_DEFENSIVE); jaraxxus->SetInCombatWithZone(); } @@ -360,11 +360,11 @@ class npc_fizzlebang_toc : public CreatureScript { Talk(SAY_STAGE_1_06, killer); _instance->SetData(TYPE_EVENT, 1180); - if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) { - temp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - temp->SetReactState(REACT_AGGRESSIVE); - temp->SetInCombatWithZone(); + jaraxxus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); + jaraxxus->SetReactState(REACT_AGGRESSIVE); + jaraxxus->SetInCombatWithZone(); } } @@ -457,18 +457,18 @@ class npc_fizzlebang_toc : public CreatureScript break; case 1140: Talk(SAY_STAGE_1_04); - if (Creature* temp = me->SummonCreature(NPC_JARAXXUS, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY(), ToCCommonLoc[1].GetPositionZ(), 5.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) + if (Creature* jaraxxus = me->SummonCreature(NPC_JARAXXUS, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY(), ToCCommonLoc[1].GetPositionZ(), 5.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) { - temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - temp->SetReactState(REACT_PASSIVE); - temp->GetMotionMaster()->MovePoint(0, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY()-10, ToCCommonLoc[1].GetPositionZ()); + jaraxxus->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC); + jaraxxus->SetReactState(REACT_PASSIVE); + jaraxxus->GetMotionMaster()->MovePoint(0, ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY()-10, ToCCommonLoc[1].GetPositionZ()); } _instance->SetData(TYPE_EVENT, 1142); _updateTimer = 5*IN_MILLISECONDS; break; case 1142: - if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) - temp->SetTarget(me->GetGUID()); + if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + jaraxxus->SetTarget(me->GetGUID()); if (Creature* pTrigger = ObjectAccessor::GetCreature(*me, _triggerGUID)) pTrigger->DespawnOrUnsummon(); if (Creature* pPortal = ObjectAccessor::GetCreature(*me, _portalGUID)) @@ -477,19 +477,19 @@ class npc_fizzlebang_toc : public CreatureScript _updateTimer = 10*IN_MILLISECONDS; break; case 1144: - if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) - temp->AI()->Talk(SAY_STAGE_1_05); + if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + jaraxxus->AI()->Talk(SAY_STAGE_1_05); _instance->SetData(TYPE_EVENT, 1150); _updateTimer = 5*IN_MILLISECONDS; break; case 1150: - if (Creature* temp = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) + if (Creature* jaraxxus = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(NPC_JARAXXUS))) { //1-shot Fizzlebang - temp->CastSpell(me, 67888, false); - me->SetInCombatWith(temp); - temp->AddThreat(me, 1000.0f); - temp->AI()->AttackStart(me); + jaraxxus->CastSpell(me, 67888, false); // 67888 - Fel Lightning + me->SetInCombatWith(jaraxxus); + jaraxxus->AddThreat(me, 1000.0f); + jaraxxus->AI()->AttackStart(me); } _instance->SetData(TYPE_EVENT, 1160); _updateTimer = 3*IN_MILLISECONDS; @@ -561,11 +561,11 @@ class npc_tirion_toc : public CreatureScript { _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); - if (Creature* temp = me->SummonCreature(NPC_GORMOK, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30*IN_MILLISECONDS)) + if (Creature* gormok = me->SummonCreature(NPC_GORMOK, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30*IN_MILLISECONDS)) { - temp->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); - temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - temp->SetReactState(REACT_PASSIVE); + gormok->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); + gormok->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + gormok->SetReactState(REACT_PASSIVE); } } _updateTimer = 3*IN_MILLISECONDS; @@ -582,11 +582,11 @@ class npc_tirion_toc : public CreatureScript if (_instance->GetBossState(BOSS_BEASTS) != DONE) { _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); - if (Creature* temp = me->SummonCreature(NPC_DREADSCALE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_MANUAL_DESPAWN)) + if (Creature* dreadscale = me->SummonCreature(NPC_DREADSCALE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_MANUAL_DESPAWN)) { - temp->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); - temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - temp->SetReactState(REACT_PASSIVE); + dreadscale->GetMotionMaster()->MovePoint(0, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); + dreadscale->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + dreadscale->SetReactState(REACT_PASSIVE); } } _updateTimer = 5*IN_MILLISECONDS; @@ -600,9 +600,9 @@ class npc_tirion_toc : public CreatureScript if (_instance->GetBossState(BOSS_BEASTS) != DONE) { _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); - if (Creature* temp = me->SummonCreature(NPC_ICEHOWL, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_DEAD_DESPAWN)) + if (Creature* icehowl = me->SummonCreature(NPC_ICEHOWL, ToCSpawnLoc[0].GetPositionX(), ToCSpawnLoc[0].GetPositionY(), ToCSpawnLoc[0].GetPositionZ(), 5, TEMPSUMMON_DEAD_DESPAWN)) { - temp->GetMotionMaster()->MovePoint(2, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); + icehowl->GetMotionMaster()->MovePoint(2, ToCCommonLoc[5].GetPositionX(), ToCCommonLoc[5].GetPositionY(), ToCCommonLoc[5].GetPositionZ()); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); me->SetReactState(REACT_PASSIVE); } @@ -698,34 +698,34 @@ class npc_tirion_toc : public CreatureScript break; case 4010: Talk(SAY_STAGE_3_02); - if (Creature* temp = me->SummonCreature(NPC_LIGHTBANE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) + if (Creature* lightbane = me->SummonCreature(NPC_LIGHTBANE, ToCSpawnLoc[1].GetPositionX(), ToCSpawnLoc[1].GetPositionY(), ToCSpawnLoc[1].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) { - temp->SetVisible(false); - temp->SetReactState(REACT_PASSIVE); - temp->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[0].GetPositionX(), TwinValkyrsLoc[0].GetPositionY(), TwinValkyrsLoc[0].GetPositionZ()); - temp->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[1].GetPositionX(), TwinValkyrsLoc[1].GetPositionY(), TwinValkyrsLoc[1].GetPositionZ()); + lightbane->SetVisible(false); + lightbane->SetReactState(REACT_PASSIVE); + lightbane->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[0].GetPositionX(), TwinValkyrsLoc[0].GetPositionY(), TwinValkyrsLoc[0].GetPositionZ()); + lightbane->SummonCreature(NPC_LIGHT_ESSENCE, TwinValkyrsLoc[1].GetPositionX(), TwinValkyrsLoc[1].GetPositionY(), TwinValkyrsLoc[1].GetPositionZ()); } - if (Creature* temp = me->SummonCreature(NPC_DARKBANE, ToCSpawnLoc[2].GetPositionX(), ToCSpawnLoc[2].GetPositionY(), ToCSpawnLoc[2].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) + if (Creature* darkbane = me->SummonCreature(NPC_DARKBANE, ToCSpawnLoc[2].GetPositionX(), ToCSpawnLoc[2].GetPositionY(), ToCSpawnLoc[2].GetPositionZ(), 5, TEMPSUMMON_CORPSE_TIMED_DESPAWN, DESPAWN_TIME)) { - temp->SetVisible(false); - temp->SetReactState(REACT_PASSIVE); - temp->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[2].GetPositionX(), TwinValkyrsLoc[2].GetPositionY(), TwinValkyrsLoc[2].GetPositionZ()); - temp->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[3].GetPositionX(), TwinValkyrsLoc[3].GetPositionY(), TwinValkyrsLoc[3].GetPositionZ()); + darkbane->SetVisible(false); + darkbane->SetReactState(REACT_PASSIVE); + darkbane->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[2].GetPositionX(), TwinValkyrsLoc[2].GetPositionY(), TwinValkyrsLoc[2].GetPositionZ()); + darkbane->SummonCreature(NPC_DARK_ESSENCE, TwinValkyrsLoc[3].GetPositionX(), TwinValkyrsLoc[3].GetPositionY(), TwinValkyrsLoc[3].GetPositionZ()); } _updateTimer = 3*IN_MILLISECONDS; _instance->SetData(TYPE_EVENT, 4015); break; case 4015: _instance->DoUseDoorOrButton(_instance->GetGuidData(GO_MAIN_GATE_DOOR)); - if (Creature* temp = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_LIGHTBANE))) + if (Creature* lightbane = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_LIGHTBANE))) { - temp->GetMotionMaster()->MovePoint(1, ToCCommonLoc[8].GetPositionX(), ToCCommonLoc[8].GetPositionY(), ToCCommonLoc[8].GetPositionZ()); - temp->SetVisible(true); + lightbane->GetMotionMaster()->MovePoint(1, ToCCommonLoc[8].GetPositionX(), ToCCommonLoc[8].GetPositionY(), ToCCommonLoc[8].GetPositionZ()); + lightbane->SetVisible(true); } - if (Creature* temp = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_DARKBANE))) + if (Creature* darkbane = ObjectAccessor::GetCreature((*me), _instance->GetGuidData(NPC_DARKBANE))) { - temp->GetMotionMaster()->MovePoint(1, ToCCommonLoc[9].GetPositionX(), ToCCommonLoc[9].GetPositionY(), ToCCommonLoc[9].GetPositionZ()); - temp->SetVisible(true); + darkbane->GetMotionMaster()->MovePoint(1, ToCCommonLoc[9].GetPositionX(), ToCCommonLoc[9].GetPositionY(), ToCCommonLoc[9].GetPositionZ()); + darkbane->SetVisible(true); } _updateTimer = 10*IN_MILLISECONDS; _instance->SetData(TYPE_EVENT, 4016); @@ -754,9 +754,9 @@ class npc_tirion_toc : public CreatureScript _instance->SetData(TYPE_EVENT, 0); break; case 6000: - me->SummonCreature(NPC_TIRION_FORDRING, EndSpawnLoc[0].GetPositionX(), EndSpawnLoc[0].GetPositionY(), EndSpawnLoc[0].GetPositionZ()); - me->SummonCreature(NPC_ARGENT_MAGE, EndSpawnLoc[1].GetPositionX(), EndSpawnLoc[1].GetPositionY(), EndSpawnLoc[1].GetPositionZ()); - me->SummonGameObject(GO_PORTAL_TO_DALARAN, EndSpawnLoc[2].GetPositionX(), EndSpawnLoc[2].GetPositionY(), EndSpawnLoc[2].GetPositionZ(), 5, 0, 0, 0, 0, 0); + me->SummonCreature(NPC_TIRION_FORDRING, EndSpawnLoc[0]); + me->SummonCreature(NPC_ARGENT_MAGE, EndSpawnLoc[1]); + me->SummonGameObject(GO_PORTAL_TO_DALARAN, EndSpawnLoc[2], G3D::Quat(), 0); _updateTimer = 20*IN_MILLISECONDS; _instance->SetData(TYPE_EVENT, 6005); break; diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h index 90b9781954f..d53d6705400 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/trial_of_the_crusader.h @@ -140,9 +140,9 @@ const Position AnubarakLoc[]= const Position EndSpawnLoc[]= { - {648.9167f, 131.0208f, 141.6161f, 0}, // 0 - Highlord Tirion Fordring - {649.1614f, 142.0399f, 141.3057f, 0}, // 1 - Argent Mage - {644.6250f, 149.2743f, 140.6015f, 0} // 2 - Portal to Dalaran + {648.9167f, 131.0208f, 141.6161f, 0.f}, // 0 - Highlord Tirion Fordring + {649.1614f, 142.0399f, 141.3057f, 0.f}, // 1 - Argent Mage + {644.6250f, 149.2743f, 140.6015f, 5.f} // 2 - Portal to Dalaran }; enum WorldStateIds diff --git a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp index ce722e391d1..292f1d7074f 100644 --- a/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp +++ b/src/server/scripts/Northrend/DraktharonKeep/boss_novos.cpp @@ -198,8 +198,6 @@ public: { if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC)) - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); if (me->HasUnitState(UNIT_STATE_CASTING)) me->CastStop(); } @@ -207,8 +205,6 @@ public: { if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE)) me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - if (!me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC)) - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); DoCast(SPELL_ARCANE_FIELD); } } diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp index 055d0a07f41..94d1d93225c 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp @@ -144,7 +144,7 @@ class boss_devourer_of_souls : public CreatureScript void Reset() override { _Reset(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->SetDisplayId(DISPLAY_ANGER); me->SetReactState(REACT_AGGRESSIVE); @@ -297,7 +297,7 @@ class boss_devourer_of_souls : public CreatureScript me->SetTarget(ObjectGuid::Empty); me->GetMotionMaster()->Clear(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); wailingSoulTick = 15; events.DelayEvents(18000); // no other events during wailing souls @@ -317,7 +317,7 @@ class boss_devourer_of_souls : public CreatureScript { me->SetReactState(REACT_AGGRESSIVE); me->SetDisplayId(DISPLAY_ANGER); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->GetMotionMaster()->MoveChase(me->GetVictim()); events.ScheduleEvent(EVENT_WAILING_SOULS, urand(60000, 70000)); } 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 3ac85809fa2..6b20ae6a373 100644 --- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp @@ -1921,6 +1921,7 @@ class npc_frostsworn_general : public CreatureScript { if (Creature* reflection = me->SummonCreature(NPC_REFLECTION, *target, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 3000)) { + reflection->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); target->CastSpell(reflection, SPELL_CLONE, true); target->CastSpell(reflection, SPELL_GHOST_VISUAL, true); reflection->AI()->AttackStart(target); @@ -2155,6 +2156,7 @@ struct npc_escape_event_trash : public ScriptedAI DoZoneInCombat(me, 0.0f); if (Creature* leader = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_ESCAPE_LEADER))) { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); me->SetInCombatWith(leader); leader->SetInCombatWith(me); me->AddThreat(leader, 0.0f); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index d77842fff0a..73a7de36580 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -2461,6 +2461,33 @@ class spell_igb_teleport_players_on_victory : public SpellScriptLoader } }; +// 71201 - Battle Experience - proc should never happen, handled in script +class spell_igb_battle_experience_check : public SpellScriptLoader +{ +public: + spell_igb_battle_experience_check() : SpellScriptLoader("spell_igb_battle_experience_check") { } + + class spell_igb_battle_experience_check_AuraScript : public AuraScript + { + PrepareAuraScript(spell_igb_battle_experience_check_AuraScript); + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_igb_battle_experience_check_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_igb_battle_experience_check_AuraScript(); + } +}; + class achievement_im_on_a_boat : public AchievementCriteriaScript { public: @@ -2500,5 +2527,6 @@ void AddSC_boss_icecrown_gunship_battle() new spell_igb_gunship_fall_teleport(); new spell_igb_check_for_players(); new spell_igb_teleport_players_on_victory(); + new spell_igb_battle_experience_check(); new achievement_im_on_a_boat(); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index 1a37d5238d2..aa5060e83b4 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -1312,7 +1312,7 @@ class spell_sindragosa_ice_tomb : public SpellScriptLoader { summon->AI()->SetGUID(GetTarget()->GetGUID(), DATA_TRAPPED_PLAYER); GetTarget()->CastSpell(GetTarget(), SPELL_ICE_TOMB_UNTARGETABLE); - if (GameObject* go = summon->SummonGameObject(GO_ICE_BLOCK, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0)) + if (GameObject* go = summon->SummonGameObject(GO_ICE_BLOCK, pos, G3D::Quat(), 0)) { go->SetSpellId(SPELL_ICE_TOMB_DAMAGE); summon->AddGameObject(go); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp index 4a0a8217af8..de8d65693b9 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -528,6 +528,7 @@ class boss_the_lich_king : public CreatureScript void SetupEncounter() { _Reset(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); me->SetReactState(REACT_PASSIVE); events.SetPhase(PHASE_INTRO); Initialize(); diff --git a/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp index 11cc645f0cb..f7e83ff1f42 100644 --- a/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp +++ b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp @@ -259,6 +259,58 @@ class spell_ioc_launch : public SpellScriptLoader } }; +enum SeaforiumBombSpells +{ + SPELL_SEAFORIUM_BLAST = 66676, + SPELL_HUGE_SEAFORIUM_BLAST = 66672, + SPELL_A_BOMB_INABLE_CREDIT = 68366, + SPELL_A_BOMB_INATION_CREDIT = 68367 +}; + +class spell_ioc_seaforium_blast_credit : public SpellScriptLoader +{ + public: + spell_ioc_seaforium_blast_credit() : SpellScriptLoader("spell_ioc_seaforium_blast_credit") { } + + class spell_ioc_seaforium_blast_credit_SpellScript : public SpellScript + { + PrepareSpellScript(spell_ioc_seaforium_blast_credit_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_A_BOMB_INABLE_CREDIT) || !sSpellMgr->GetSpellInfo(SPELL_A_BOMB_INATION_CREDIT)) + return false; + return true; + } + + void HandleAchievementCredit(SpellEffIndex /*effIndex*/) + { + uint32 _creditSpell = 0; + Unit* caster = GetOriginalCaster(); + if (!caster) + return; + + if (GetSpellInfo()->Id == SPELL_SEAFORIUM_BLAST) + _creditSpell = SPELL_A_BOMB_INABLE_CREDIT; + else if (GetSpellInfo()->Id == SPELL_HUGE_SEAFORIUM_BLAST) + _creditSpell = SPELL_A_BOMB_INATION_CREDIT; + + if (GetHitGObj() && GetHitGObj()->IsDestructibleBuilding()) + caster->CastSpell(caster, _creditSpell, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_ioc_seaforium_blast_credit_SpellScript::HandleAchievementCredit, EFFECT_1, SPELL_EFFECT_GAMEOBJECT_DAMAGE); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_ioc_seaforium_blast_credit_SpellScript(); + } +}; + void AddSC_isle_of_conquest() { new npc_four_car_garage(); @@ -266,4 +318,5 @@ void AddSC_isle_of_conquest() new spell_ioc_gunship_portal(); new spell_ioc_parachute_ic(); new spell_ioc_launch(); + new spell_ioc_seaforium_blast_credit(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp index ef6ccf5bf4b..6ec9af68723 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp @@ -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; - } + // 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 (timer > 1600 && me->GetExactDist2d(gluth) > 10.0f && 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.0f) + me->StopMoving(); + } } else if (state == STATE_ZOMBIE_NORMAL) DoMeleeAttackIfReady(); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index 47807ec28cb..42efdfbfe67 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -309,7 +309,7 @@ public: _Reset(); me->setFaction(35); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE); ResetPlayerScale(); spawns.DespawnAll(); @@ -356,7 +356,7 @@ public: } DoCast(me, SPELL_KELTHUZAD_CHANNEL, false); Talk(SAY_SUMMON_MINIONS); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE); me->SetFloatValue(UNIT_FIELD_COMBATREACH, 4); me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 4); events.SetPhase(PHASE_ONE); @@ -426,7 +426,7 @@ public: Talk(SAY_AGGRO); Talk(EMOTE_PHASE_TWO); spawns.DespawnAll(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE); me->CastStop(); DoStartMovement(me->GetVictim()); diff --git a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp index 4596b17bc23..0c9a236e196 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_sapphiron.cpp @@ -485,7 +485,7 @@ class spell_sapphiron_icebolt : public SpellScriptLoader return; float x, y, z; GetTarget()->GetPosition(x, y, z); - if (GameObject* block = GetTarget()->SummonGameObject(GO_ICEBLOCK, x, y, z, 0, 0, 0, 0, 0, 25)) + if (GameObject* block = GetTarget()->SummonGameObject(GO_ICEBLOCK, x, y, z, 0.f, G3D::Quat(), 25)) _block = block->GetGUID(); } diff --git a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp index e3971248513..720549de0b2 100644 --- a/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp +++ b/src/server/scripts/Northrend/Naxxramas/instance_naxxramas.cpp @@ -123,6 +123,9 @@ class instance_naxxramas : public InstanceMapScript CurrentWingTaunt = SAY_KELTHUZAD_FIRST_WING_TAUNT; playerDied = 0; + + nextFroggerWave = 0; + events.ScheduleEvent(EVENT_SUMMON_FROGGER_WAVE, Seconds(1)); } void OnCreatureCreate(Creature* creature) override @@ -477,6 +480,16 @@ class instance_naxxramas : public InstanceMapScript kelthuzad->AI()->Talk(CurrentWingTaunt); ++CurrentWingTaunt; break; + case EVENT_SUMMON_FROGGER_WAVE: + { + std::list<TempSummon*> spawns; + instance->SummonCreatureGroup(nextFroggerWave, &spawns); + if (!spawns.empty()) + (*spawns.begin())->GetMotionMaster()->MovePath(10 * NPC_FROGGER + nextFroggerWave, false); + events.Repeat(Seconds(1) + Milliseconds(666)); + nextFroggerWave = (nextFroggerWave+1) % 3; + break; + } case EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD: if (Creature* kelthuzad = instance->GetCreature(KelthuzadGUID)) kelthuzad->AI()->Talk(SAY_DIALOGUE_SAPPHIRON_KELTHUZAD); @@ -613,6 +626,8 @@ class instance_naxxramas : public InstanceMapScript /* The Immortal / The Undying */ uint32 playerDied; + int8 nextFroggerWave; + EventMap events; }; diff --git a/src/server/scripts/Northrend/Naxxramas/naxxramas.h b/src/server/scripts/Northrend/Naxxramas/naxxramas.h index a3fedf5aa40..75e7314c5d0 100644 --- a/src/server/scripts/Northrend/Naxxramas/naxxramas.h +++ b/src/server/scripts/Northrend/Naxxramas/naxxramas.h @@ -107,7 +107,8 @@ enum CreaturesIds NPC_DK_UNDERSTUDY = 16803, NPC_BIGGLESWORTH = 16998, NPC_LICH_KING = 16980, - NPC_OLD_WORLD_TRIGGER = 15384 + NPC_OLD_WORLD_TRIGGER = 15384, + NPC_FROGGER = 16027 }; enum GameObjectsIds @@ -175,6 +176,9 @@ enum InstanceEvents // Dialogue that happens after each wing. EVENT_KELTHUZAD_WING_TAUNT, + // Periodic Frogger summon + EVENT_SUMMON_FROGGER_WAVE, + // Dialogue that happens after Sapphiron's death. EVENT_DIALOGUE_SAPPHIRON_KELTHUZAD, EVENT_DIALOGUE_SAPPHIRON_LICHKING, diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index 3d5a6ee8dfb..914a1a8bbb0 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -376,6 +376,8 @@ public: me->SetDisableGravity(true); me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); // TO DO: find what in core is making boss slower than in retail (when correct speed data) or find missing movement flag update or forced spline change me->SetSpeedRate(MOVE_FLIGHT, _flySpeed * 0.25f); if (_despawned) @@ -464,7 +466,7 @@ public: pos.m_positionZ = alexstraszaBunny->GetPositionZ(); alexstraszaBunny->GetNearPoint2D(pos.m_positionX, pos.m_positionY, 30.0f, alexstraszaBunny->GetAngle(me)); me->GetMotionMaster()->MoveLand(POINT_LAND_P_ONE, pos); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_NPC); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); me->SetReactState(REACT_AGGRESSIVE); me->SetInCombatWithZone(); events.ScheduleEvent(EVENT_LAND_START_ENCOUNTER, 7*IN_MILLISECONDS, 1, PHASE_NOT_STARTED); @@ -604,8 +606,6 @@ public: // Set speed to normal value me->SetSpeedRate(MOVE_FLIGHT, _flySpeed); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->RemoveAllAuras(); me->CombatStop(); // Sometimes threat can remain, so it's a safety measure @@ -1001,14 +1001,7 @@ public: _JustDied(); Talk(SAY_DEATH); if (Creature* alexstraszaGiftBoxBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_GIFT_BOX_BUNNY_GUID))) - { - if (GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) - alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_10, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(), - HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); - else if (GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) - alexstraszaGiftBoxBunny->SummonGameObject(GO_HEART_OF_MAGIC_25, HeartOfMagicSpawnPos.GetPositionX(), HeartOfMagicSpawnPos.GetPositionY(), - HeartOfMagicSpawnPos.GetPositionZ(), HeartOfMagicSpawnPos.GetOrientation(), 0.0f, 0.0f, 0.0f, 1.0f, 0); - } + alexstraszaGiftBoxBunny->SummonGameObject(RAID_MODE(GO_HEART_OF_MAGIC_10, GO_HEART_OF_MAGIC_25), HeartOfMagicSpawnPos, G3D::Quat(), 0); me->SummonCreature(NPC_ALEXSTRASZA, AlexstraszaSpawnPos, TEMPSUMMON_MANUAL_DESPAWN); me->DespawnOrUnsummon(5*IN_MILLISECONDS); @@ -2439,9 +2432,9 @@ class spell_alexstrasza_gift_beam_visual : public SpellScriptLoader if (Creature* target = GetTarget()->ToCreature()) { if (target->GetMap()->GetDifficulty() == RAID_DIFFICULTY_10MAN_NORMAL) - _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_10, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0); + _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_10, *target, G3D::Quat(), 0); else if (target->GetMap()->GetDifficulty() == RAID_DIFFICULTY_25MAN_NORMAL) - _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_25, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0); + _alexstraszaGift = target->SummonGameObject(GO_ALEXSTRASZA_S_GIFT_25, *target, G3D::Quat(), 0); } } 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 e87115dd8e2..eb8a92f7b28 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,9 +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(), - 0, 0, 0, 0, 120, GO_STATE_READY)) + if (!go->Create(instance->GenerateLowGuid<HighGuid::GameObject>(), entry, instance, PHASEMASK_NORMAL, pos, G3D::Quat(), 255, GO_STATE_READY)) { delete go; return; diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp index 8f7687d0fca..55295a534e1 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp @@ -113,7 +113,7 @@ public: Initialize(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL); if (!me->IsVisible()) me->SetVisible(true); @@ -152,7 +152,7 @@ public: me->AttackStop(); me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->GetMotionMaster()->Clear(); me->GetMotionMaster()->MoveIdle(); @@ -236,7 +236,7 @@ public: else if (lSparkList.empty()) { me->SetVisible(true); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL); DoCast(me, SPELL_SPARK_DESPAWN, false); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index 05beacca638..fa59c021cad 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -171,8 +171,7 @@ enum FreyaNpcs enum FreyaActions { - ACTION_ELDER_DEATH = 1, - ACTION_ELDER_FREYA_KILLED = 2 + ACTION_ELDER_FREYA_KILLED = 1 }; enum FreyaEvents @@ -619,7 +618,7 @@ class boss_freya : public CreatureScript Elder->AttackStop(); Elder->CombatStop(true); Elder->DeleteThreatList(); - Elder->GetAI()->DoAction(ACTION_ELDER_FREYA_KILLED); + Elder->AI()->DoAction(ACTION_ELDER_FREYA_KILLED); } } } @@ -705,19 +704,10 @@ class boss_elder_brightleaf : public CreatureScript Talk(SAY_ELDER_SLAY); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { _JustDied(); Talk(SAY_ELDER_DEATH); - - if (killer->GetTypeId() == TYPEID_PLAYER) - { - if (Creature* Ironbranch = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_IRONBRANCH))) - Ironbranch->AI()->DoAction(ACTION_ELDER_DEATH); - - if (Creature* Stonebark = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_STONEBARK))) - Stonebark->AI()->DoAction(ACTION_ELDER_DEATH); - } } void EnterCombat(Unit* /*who*/) override @@ -812,19 +802,10 @@ class boss_elder_stonebark : public CreatureScript Talk(SAY_ELDER_SLAY); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { _JustDied(); Talk(SAY_ELDER_DEATH); - - if (killer->GetTypeId() == TYPEID_PLAYER) - { - if (Creature* Ironbranch = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_IRONBRANCH))) - Ironbranch->AI()->DoAction(ACTION_ELDER_DEATH); - - if (Creature* Brightleaf = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_BRIGHTLEAF))) - Brightleaf->AI()->DoAction(ACTION_ELDER_DEATH); - } } void EnterCombat(Unit* /*who*/) override @@ -925,19 +906,10 @@ class boss_elder_ironbranch : public CreatureScript Talk(SAY_ELDER_SLAY); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { _JustDied(); Talk(SAY_ELDER_DEATH); - - if (killer->GetTypeId() == TYPEID_PLAYER) - { - if (Creature* Brightleaf = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_BRIGHTLEAF))) - Brightleaf->AI()->DoAction(ACTION_ELDER_DEATH); - - if (Creature* Stonebark = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_STONEBARK))) - Stonebark->AI()->DoAction(ACTION_ELDER_DEATH); - } } void EnterCombat(Unit* /*who*/) override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp index 09d95b34521..cbd24141bdf 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp @@ -415,7 +415,7 @@ class npc_saronite_vapors : public CreatureScript if (damage >= me->GetHealth()) { damage = 0; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->SetStandState(UNIT_STAND_STATE_DEAD); me->SetHealth(me->GetMaxHealth()); me->RemoveAllAuras(); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp index c35a5936822..c03a1c6fbc1 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp @@ -30,6 +30,8 @@ Destroying of Toasty Fires */ +/* @todo Hodir aggro behavior is wonky. He gets set to _PASSIVE, but never to _AGGRESSIVE unless you kill an ice block which doesn't spawn unless you have*/ + enum HodirYells { SAY_AGGRO = 0, @@ -184,7 +186,7 @@ class npc_flash_freeze : public CreatureScript Initialize(); instance = me->GetInstanceScript(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); } void Initialize() @@ -259,7 +261,7 @@ class npc_ice_block : public CreatureScript { instance = me->GetInstanceScript(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); } InstanceScript* instance; @@ -269,7 +271,7 @@ class npc_ice_block : public CreatureScript void IsSummonedBy(Unit* summoner) override { targetGUID = summoner->GetGUID(); - summoner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + summoner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); me->SetInCombatWith(summoner); me->AddThreat(summoner, 250.0f); summoner->AddThreat(me, 250.0f); @@ -285,7 +287,7 @@ class npc_ice_block : public CreatureScript { if (Creature* Helper = ObjectAccessor::GetCreature(*me, targetGUID)) { - Helper->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + Helper->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); if (Creature* Hodir = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_HODIR))) { @@ -375,7 +377,7 @@ class boss_hodir : public CreatureScript Talk(SAY_SLAY); } - void DamageTaken(Unit* /*who*/, uint32& damage) override + void DamageTaken(Unit* who, uint32& damage) override { if (damage >= me->GetHealth()) { @@ -388,7 +390,7 @@ class boss_hodir : public CreatureScript me->RemoveAllAttackers(); me->AttackStop(); me->SetReactState(REACT_PASSIVE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->InterruptNonMeleeSpells(true); me->StopMoving(); me->GetMotionMaster()->Clear(); @@ -403,6 +405,12 @@ class boss_hodir : public CreatureScript _JustDied(); } + else if (!me->IsInCombat()) + { + me->SetReactState(REACT_AGGRESSIVE); + me->AI()->DoZoneInCombat(); + me->AI()->AttackStart(who); + } } void UpdateAI(uint32 diff) override @@ -539,7 +547,7 @@ class npc_icicle : public CreatureScript { Initialize(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid1); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE); me->SetReactState(REACT_PASSIVE); } @@ -593,7 +601,7 @@ class npc_snowpacked_icicle : public CreatureScript { Initialize(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); me->SetReactState(REACT_PASSIVE); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp index cd214a0114f..a50643c5deb 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp @@ -178,7 +178,7 @@ class boss_ignis : public CreatureScript { summon->setFaction(16); summon->SetReactState(REACT_AGGRESSIVE); - summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_DISABLE_MOVE); + summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_IMMUNE_TO_PC); } summon->AI()->AttackStart(me->GetVictim()); @@ -375,7 +375,7 @@ class npc_scorch_ground : public CreatureScript npc_scorch_groundAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE |UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL |UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); creature->SetDisplayId(16925); //model 2 in db cannot overwrite wdb fields } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp index 470ad388fff..45d4fa58a03 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp @@ -104,7 +104,7 @@ class boss_kologarn : public CreatureScript left(false), right(false) { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); DoCast(SPELL_KOLOGARN_REDUCE_PARRY); SetCombatMovement(false); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 8aa443cba3f..a965f8b39ff 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -644,10 +644,10 @@ class boss_mimiron : public CreatureScript { if (Creature* computer = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_COMPUTER))) computer->AI()->DoAction(DO_DEACTIVATE_COMPUTER); - me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION_FIREFIGHTER, GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, 0.f, 0.f, 0.9999619f, 0.008734641f, 604800); + me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION_FIREFIGHTER, GO_CACHE_OF_INNOVATION_FIREFIGHTER_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, G3D::Quat(0.f, 0.f, 0.9999619f, 0.008734641f), 604800); } else - me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION, GO_CACHE_OF_INNOVATION_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, 0.f, 0.f, 0.9999619f, 0.008734641f, 604800); + me->SummonGameObject(RAID_MODE(GO_CACHE_OF_INNOVATION, GO_CACHE_OF_INNOVATION_HERO), 2744.040f, 2569.352f, 364.3135f, 3.124123f, G3D::Quat(0.f, 0.f, 0.9999619f, 0.008734641f), 604800); events.ScheduleEvent(EVENT_OUTTRO_3, 11000); break; case EVENT_OUTTRO_3: @@ -972,7 +972,7 @@ class boss_vx_001 : public CreatureScript events.ScheduleEvent(EVENT_FLAME_SUPPRESSANT_VX, 6000); // Missing break intended. case DO_START_VX001: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); me->RemoveAurasDueToSpell(SPELL_FREEZE_ANIM); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); // Remove emotestate. //me->SetUInt32Value(UNIT_FIELD_BYTES_1, 33554432); Blizzard handles hover animation like this it seems. @@ -1145,7 +1145,7 @@ class boss_aerial_command_unit : public CreatureScript events.ScheduleEvent(EVENT_SUMMON_FIRE_BOTS, 1000, 0, PHASE_AERIAL_COMMAND_UNIT); // Missing break intended. case DO_START_AERIAL: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); me->SetReactState(REACT_AGGRESSIVE); events.SetPhase(PHASE_AERIAL_COMMAND_UNIT); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp index 363f5907048..a0ea2bd3d57 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp @@ -242,7 +242,7 @@ class boss_razorscale_controller : public CreatureScript break; case ACTION_PLACE_BROKEN_HARPOON: for (uint8 n = 0; n < RAID_MODE(2, 4); n++) - me->SummonGameObject(GO_RAZOR_BROKEN_HARPOON, PosHarpoon[n].GetPositionX(), PosHarpoon[n].GetPositionY(), PosHarpoon[n].GetPositionZ(), 2.286f, 0, 0, 0, 0, 180); + me->SummonGameObject(GO_RAZOR_BROKEN_HARPOON, PosHarpoon[n].GetPositionX(), PosHarpoon[n].GetPositionY(), PosHarpoon[n].GetPositionZ(), 2.286f, G3D::Quat(), 180); break; } } @@ -257,7 +257,7 @@ class boss_razorscale_controller : public CreatureScript { case EVENT_BUILD_HARPOON_1: Talk(EMOTE_HARPOON); - if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_1, PosHarpoon[0].GetPositionX(), PosHarpoon[0].GetPositionY(), PosHarpoon[0].GetPositionZ(), 4.790f, 0.0f, 0.0f, 0.0f, 0.0f, uint32(me->GetRespawnTime()))) + if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_1, PosHarpoon[0].GetPositionX(), PosHarpoon[0].GetPositionY(), PosHarpoon[0].GetPositionZ(), 4.790f, G3D::Quat(), uint32(me->GetRespawnTime()))) { if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) //only nearest broken harpoon BrokenHarpoon->RemoveFromWorld(); @@ -267,7 +267,7 @@ class boss_razorscale_controller : public CreatureScript return; case EVENT_BUILD_HARPOON_2: Talk(EMOTE_HARPOON); - if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_2, PosHarpoon[1].GetPositionX(), PosHarpoon[1].GetPositionY(), PosHarpoon[1].GetPositionZ(), 4.659f, 0, 0, 0, 0, uint32(me->GetRespawnTime()))) + if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_2, PosHarpoon[1].GetPositionX(), PosHarpoon[1].GetPositionY(), PosHarpoon[1].GetPositionZ(), 4.659f, G3D::Quat(), uint32(me->GetRespawnTime()))) { if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) BrokenHarpoon->RemoveFromWorld(); @@ -276,7 +276,7 @@ class boss_razorscale_controller : public CreatureScript return; case EVENT_BUILD_HARPOON_3: Talk(EMOTE_HARPOON); - if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_3, PosHarpoon[2].GetPositionX(), PosHarpoon[2].GetPositionY(), PosHarpoon[2].GetPositionZ(), 5.382f, 0, 0, 0, 0, uint32(me->GetRespawnTime()))) + if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_3, PosHarpoon[2].GetPositionX(), PosHarpoon[2].GetPositionY(), PosHarpoon[2].GetPositionZ(), 5.382f, G3D::Quat(), uint32(me->GetRespawnTime()))) { if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) BrokenHarpoon->RemoveFromWorld(); @@ -286,7 +286,7 @@ class boss_razorscale_controller : public CreatureScript return; case EVENT_BUILD_HARPOON_4: Talk(EMOTE_HARPOON); - if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_4, PosHarpoon[3].GetPositionX(), PosHarpoon[3].GetPositionY(), PosHarpoon[3].GetPositionZ(), 4.266f, 0, 0, 0, 0, uint32(me->GetRespawnTime()))) + if (GameObject* Harpoon = me->SummonGameObject(GO_RAZOR_HARPOON_4, PosHarpoon[3].GetPositionX(), PosHarpoon[3].GetPositionY(), PosHarpoon[3].GetPositionZ(), 4.266f, G3D::Quat(), uint32(me->GetRespawnTime()))) { if (GameObject* BrokenHarpoon = Harpoon->FindNearestGameObject(GO_RAZOR_BROKEN_HARPOON, 5.0f)) BrokenHarpoon->RemoveFromWorld(); diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp index 547dc681ac6..28ca7f4ef3b 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/instance_utgarde_pinnacle.cpp @@ -21,7 +21,7 @@ BossBoundaryData const boundaries = { - { DATA_KING_YMIRON, new RectangleBoundary(340.0f, 450.0f, -412.0f, -275.0f) } + { DATA_KING_YMIRON, new RectangleBoundary(340.0f, 443.0f, -412.0f, -275.0f) } }; DoorData const doorData[] = diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp index b89c11147b6..df3fa266191 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_archavon.cpp @@ -115,6 +115,7 @@ class boss_archavon : public CreatureScript DoCastVictim(SPELL_STOMP); events.ScheduleEvent(EVENT_IMPALE, 3000); events.ScheduleEvent(EVENT_STOMP, 45000); + Talk(EMOTE_LEAP, me->GetVictim()); break; case EVENT_IMPALE: DoCastVictim(SPELL_IMPALE); diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp index 06ebcbdc2fb..e7305e53808 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp @@ -247,7 +247,7 @@ class npc_frozen_orb_stalker : public CreatureScript npc_frozen_orb_stalkerAI(Creature* creature) : ScriptedAI(creature) { creature->SetVisible(false); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); creature->SetReactState(REACT_PASSIVE); instance = creature->GetInstanceScript(); diff --git a/src/server/scripts/Northrend/zone_borean_tundra.cpp b/src/server/scripts/Northrend/zone_borean_tundra.cpp index 698fd510411..d9315315c58 100644 --- a/src/server/scripts/Northrend/zone_borean_tundra.cpp +++ b/src/server/scripts/Northrend/zone_borean_tundra.cpp @@ -2274,7 +2274,8 @@ public: void DoAction(int32 /*iParam*/) override { me->StopMoving(); - me->SetUInt32Value(UNIT_NPC_FLAGS, 0); + me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE); if (Player* player = ObjectAccessor::GetPlayer(*me, uiPlayerGUID)) me->SetFacingToObject(player); uiEventTimer = 3000; @@ -2303,7 +2304,6 @@ public: switch (me->GetEntry()) { case NPC_SALTY_JOHN_THORPE: - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0); Talk(SAY_HIDDEN_CULTIST_1); uiEventTimer = 5000; uiEventPhase = 2; @@ -2314,7 +2314,8 @@ public: uiEventPhase = 2; break; case NPC_TOM_HEGGER: - Talk(SAY_HIDDEN_CULTIST_3); + if (Player* player = ObjectAccessor::GetPlayer(*me, uiPlayerGUID)) + Talk(SAY_HIDDEN_CULTIST_3, player); uiEventTimer = 5000; uiEventPhase = 2; break; diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp index 4eafc1cd94e..adade245c2b 100644 --- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp +++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp @@ -1044,6 +1044,12 @@ public: { PrepareAuraScript(spell_z_check_AuraScript); + public: + spell_z_check_AuraScript() + { + _posZ = 0.0f; + } + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { _posZ = GetTarget()->GetPositionZ(); diff --git a/src/server/scripts/Northrend/zone_sholazar_basin.cpp b/src/server/scripts/Northrend/zone_sholazar_basin.cpp index f2edccd99b5..41785f76d9a 100644 --- a/src/server/scripts/Northrend/zone_sholazar_basin.cpp +++ b/src/server/scripts/Northrend/zone_sholazar_basin.cpp @@ -253,7 +253,7 @@ public: case 1: Talk(SAY_WP_3); me->CastSpell(5918.33f, 5372.91f, -98.770f, SPELL_EXPLODE_CRYSTAL, true); - me->SummonGameObject(184743, 5918.33f, 5372.91f, -98.770f, 0, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds + me->SummonGameObject(184743, 5918.33f, 5372.91f, -98.770f, 0, G3D::Quat(), TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH); break; case 2: @@ -264,7 +264,7 @@ public: break; case 8: me->CastSpell(5887.37f, 5379.39f, -91.289f, SPELL_EXPLODE_CRYSTAL, true); - me->SummonGameObject(184743, 5887.37f, 5379.39f, -91.289f, 0, 0, 0, 0, 0, TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds + me->SummonGameObject(184743, 5887.37f, 5379.39f, -91.289f, 0, G3D::Quat(), TEMPSUMMON_MANUAL_DESPAWN); //approx 3 to 4 seconds me->HandleEmoteCommand(EMOTE_ONESHOT_LAUGH); break; case 9: @@ -780,7 +780,7 @@ public: bird->KillSelf(); crunchy->GetMotionMaster()->MovePoint(0, bird->GetPositionX(), bird->GetPositionY(), - bird->GetMap()->GetWaterOrGroundLevel(bird->GetPositionX(), bird->GetPositionY(), bird->GetPositionZ())); + bird->GetMap()->GetWaterOrGroundLevel(bird->GetPhaseMask(), bird->GetPositionX(), bird->GetPositionY(), bird->GetPositionZ())); /// @todo Make crunchy perform emote eat when he reaches the bird break; diff --git a/src/server/scripts/Northrend/zone_storm_peaks.cpp b/src/server/scripts/Northrend/zone_storm_peaks.cpp index e5263a8630a..490c72c5cba 100644 --- a/src/server/scripts/Northrend/zone_storm_peaks.cpp +++ b/src/server/scripts/Northrend/zone_storm_peaks.cpp @@ -516,7 +516,7 @@ public: if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID)) voice->AI()->Talk(SAY_VOICE_1, player); } - if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_1, 7860.273f, -1383.622f, 1538.302f, -1.658062f, 0, 0, -0.737277f, 0.6755905f, 0)) + if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_1, 7860.273f, -1383.622f, 1538.302f, -1.658062f, G3D::Quat(0.f, 0.f, -0.737277f, 0.6755905f), 0)) objectGUID[objectCounter++] = go->GetGUID(); events.ScheduleEvent(EVENT_SCRIPT_5, 6000); break; @@ -524,7 +524,7 @@ public: if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID)) if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID)) voice->AI()->Talk(SAY_VOICE_2, player); - if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_2, 7875.67f, -1387.266f, 1538.323f, -2.373644f, 0, 0, -0.9271832f, 0.3746083f, 0)) + if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_2, 7875.67f, -1387.266f, 1538.323f, -2.373644f, G3D::Quat(0.f, 0.f, -0.9271832f, 0.3746083f), 0)) objectGUID[objectCounter++] = go->GetGUID(); events.ScheduleEvent(EVENT_SCRIPT_6, 6000); break; @@ -532,7 +532,7 @@ public: if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID)) if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID)) voice->AI()->Talk(SAY_VOICE_3, player); - if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_3, 7879.212f, -1401.175f, 1538.279f, 2.967041f, 0, 0, 0.9961939f, 0.08716504f, 0)) + if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_3, 7879.212f, -1401.175f, 1538.279f, 2.967041f, G3D::Quat(0.f, 0.f, 0.9961939f, 0.08716504f), 0)) objectGUID[objectCounter++] = go->GetGUID(); events.ScheduleEvent(EVENT_SCRIPT_7, 6000); break; @@ -540,7 +540,7 @@ public: if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID)) if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID)) voice->AI()->Talk(SAY_VOICE_4, player); - if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_4, 7868.944f, -1411.18f, 1538.213f, 2.111848f, 0, 0, 0.8703556f, 0.4924237f, 0)) + if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_4, 7868.944f, -1411.18f, 1538.213f, 2.111848f, G3D::Quat(0.f, 0.f, 0.8703556f, 0.4924237f), 0)) objectGUID[objectCounter++] = go->GetGUID(); events.ScheduleEvent(EVENT_SCRIPT_8, 6000); break; @@ -548,7 +548,7 @@ public: if (Player* player = ObjectAccessor::GetPlayer(*me, playerGUID)) if (Creature* voice = ObjectAccessor::GetCreature(*me, voiceGUID)) voice->AI()->Talk(SAY_VOICE_5, player); - if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_5, 7855.11f, -1406.839f, 1538.42f, 1.151916f, 0, 0, 0.5446386f, 0.8386708f, 0)) + if (GameObject* go = me->SummonGameObject(OBJECT_TOL_SIGNAL_5, 7855.11f, -1406.839f, 1538.42f, 1.151916f, G3D::Quat(0.f, 0.f, 0.5446386f, 0.8386708f), 0)) objectGUID[objectCounter] = go->GetGUID(); events.ScheduleEvent(EVENT_SCRIPT_9, 6000); break; diff --git a/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp b/src/server/scripts/OutdoorPvP/OutdoorPvPSI.cpp index a0b9351fb75..05ed405cc6e 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(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), *player, G3D::Quat(), 255, GO_STATE_READY)) { delete go; return true; @@ -192,7 +192,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(map->GenerateLowGuid<HighGuid::GameObject>(), SI_SILITHYST_MOUND, map, player->GetPhaseMask(), *player, G3D::Quat(), 255, GO_STATE_READY)) { delete go; return true; diff --git a/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp index 9c16379c9f1..236f5bac403 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_gurtogg_bloodboil.cpp @@ -64,12 +64,11 @@ public: return GetInstanceAI<boss_gurtogg_bloodboilAI>(creature); } - struct boss_gurtogg_bloodboilAI : public ScriptedAI + struct boss_gurtogg_bloodboilAI : public BossAI { - boss_gurtogg_bloodboilAI(Creature* creature) : ScriptedAI(creature) + boss_gurtogg_bloodboilAI(Creature* creature) : BossAI(creature, DATA_GURTOGG_BLOODBOIL) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() @@ -91,8 +90,6 @@ public: Phase1 = true; } - InstanceScript* instance; - ObjectGuid TargetGUID; float TargetThreat; @@ -112,8 +109,7 @@ public: void Reset() override { - instance->SetBossState(DATA_GURTOGG_BLOODBOIL, NOT_STARTED); - + _Reset(); Initialize(); me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false); @@ -122,9 +118,8 @@ public: void EnterCombat(Unit* /*who*/) override { - DoZoneInCombat(); Talk(SAY_AGGRO); - instance->SetBossState(DATA_GURTOGG_BLOODBOIL, IN_PROGRESS); + _EnterCombat(); } void KilledUnit(Unit* /*victim*/) override @@ -134,9 +129,8 @@ public: void JustDied(Unit* /*killer*/) override { - instance->SetBossState(DATA_GURTOGG_BLOODBOIL, DONE); - Talk(SAY_DEATH); + _JustDied(); } void RevertThreatOnTarget(ObjectGuid guid) diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index 14aeda04a7e..a63d984d849 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -473,12 +473,11 @@ class boss_illidan_stormrage : public CreatureScript public: boss_illidan_stormrage() : CreatureScript("boss_illidan_stormrage") { } - struct boss_illidan_stormrageAI : public ScriptedAI + struct boss_illidan_stormrageAI : public BossAI { - boss_illidan_stormrageAI(Creature* creature) : ScriptedAI(creature), Summons(me) + boss_illidan_stormrageAI(Creature* creature) : BossAI(creature, DATA_ILLIDAN_STORMRAGE) { Initialize(); - instance = creature->GetInstanceScript(); DoCast(me, SPELL_DUAL_WIELD, true); } @@ -519,7 +518,7 @@ public: EnterPhase(PHASE_FLIGHT_SEQUENCE); } } - Summons.Despawn(summon); + summons.Despawn(summon); } void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) override @@ -539,6 +538,7 @@ public: void EnterCombat(Unit* /*who*/) override { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); me->setActive(true); DoZoneInCombat(); } @@ -561,10 +561,10 @@ public: { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - instance->SetBossState(DATA_ILLIDAN_STORMRAGE, DONE); - for (uint8 i = DATA_GO_ILLIDAN_DOOR_R; i < DATA_GO_ILLIDAN_DOOR_L + 1; ++i) instance->HandleGameObject(instance->GetGuidData(i), true); + + _JustDied(); } void KilledUnit(Unit* victim) override @@ -585,7 +585,7 @@ public: void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override { - if (spell->Id == SPELL_GLAIVE_RETURNS) // Re-equip our warblades! + if (spell->Id == SPELL_GLAIVE_RETURNS) // Re-equip our warglaives! { if (!me->GetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID)) SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); @@ -673,7 +673,7 @@ public: Timer[EVENT_TALK_SEQUENCE] = 100; me->RemoveAllAuras(); me->InterruptNonMeleeSpells(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); me->GetMotionMaster()->Clear(false); me->AttackStop(); break; @@ -791,99 +791,99 @@ public: { switch (FlightCount) { - case 1: // lift off - me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); - me->SetDisableGravity(true); - me->StopMoving(); - Talk(SAY_ILLIDAN_TAKEOFF); - Timer[EVENT_FLIGHT_SEQUENCE] = 3000; - break; - case 2: // move to center - me->GetMotionMaster()->MovePoint(0, CENTER_X + 5, CENTER_Y, CENTER_Z); // +5, for SPELL_THROW_GLAIVE bug - Timer[EVENT_FLIGHT_SEQUENCE] = 0; - break; - case 3: // throw one glaive - { - uint8 i=1; - Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); - if (Glaive) + case 1: // lift off + me->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF); + me->SetDisableGravity(true); + me->StopMoving(); + Talk(SAY_ILLIDAN_TAKEOFF); + Timer[EVENT_FLIGHT_SEQUENCE] = 3000; + break; + case 2: // move to center + me->GetMotionMaster()->MovePoint(0, CENTER_X + 5, CENTER_Y, CENTER_Z); // +5, for SPELL_THROW_GLAIVE bug + Timer[EVENT_FLIGHT_SEQUENCE] = 0; + break; + case 3: // throw one glaive { - GlaiveGUID[i] = Glaive->GetGUID(); - Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - Glaive->SetDisplayId(MODEL_INVISIBLE); - Glaive->setFaction(me->getFaction()); - DoCast(Glaive, SPELL_THROW_GLAIVE2); + uint8 i=1; + Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + if (Glaive) + { + GlaiveGUID[i] = Glaive->GetGUID(); + Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Glaive->SetDisplayId(MODEL_INVISIBLE); + Glaive->setFaction(me->getFaction()); + DoCast(Glaive, SPELL_THROW_GLAIVE2); + } } - } - Timer[EVENT_FLIGHT_SEQUENCE] = 700; - break; - case 4: // throw another - SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); - { - uint8 i=0; - Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); - if (Glaive) + Timer[EVENT_FLIGHT_SEQUENCE] = 700; + break; + case 4: // throw another + SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); { - GlaiveGUID[i] = Glaive->GetGUID(); - Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - Glaive->SetDisplayId(MODEL_INVISIBLE); - Glaive->setFaction(me->getFaction()); - DoCast(Glaive, SPELL_THROW_GLAIVE, true); + uint8 i=0; + Creature* Glaive = me->SummonCreature(BLADE_OF_AZZINOTH, GlaivePosition[i].x, GlaivePosition[i].y, GlaivePosition[i].z, 0, TEMPSUMMON_CORPSE_DESPAWN, 0); + if (Glaive) + { + GlaiveGUID[i] = Glaive->GetGUID(); + Glaive->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + Glaive->SetDisplayId(MODEL_INVISIBLE); + Glaive->setFaction(me->getFaction()); + DoCast(Glaive, SPELL_THROW_GLAIVE, true); + } } - } - Timer[EVENT_FLIGHT_SEQUENCE] = 5000; - break; - case 5: // summon flames - SummonFlamesOfAzzinoth(); - Timer[EVENT_FLIGHT_SEQUENCE] = 3000; - break; - case 6: // fly to hover point - me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z); - Timer[EVENT_FLIGHT_SEQUENCE] = 0; - break; - case 7: // return to center - me->GetMotionMaster()->MovePoint(0, CENTER_X, CENTER_Y, CENTER_Z); - Timer[EVENT_FLIGHT_SEQUENCE] = 0; - break; - case 8: // glaive return - for (uint8 i = 0; i < 2; ++i) - { - if (GlaiveGUID[i]) + Timer[EVENT_FLIGHT_SEQUENCE] = 5000; + break; + case 5: // summon flames + SummonFlamesOfAzzinoth(); + Timer[EVENT_FLIGHT_SEQUENCE] = 3000; + break; + case 6: // fly to hover point + me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z); + Timer[EVENT_FLIGHT_SEQUENCE] = 0; + break; + case 7: // return to center + me->GetMotionMaster()->MovePoint(0, CENTER_X, CENTER_Y, CENTER_Z); + Timer[EVENT_FLIGHT_SEQUENCE] = 0; + break; + case 8: // glaive return + for (uint8 i = 0; i < 2; ++i) { - Unit* Glaive = ObjectAccessor::GetUnit(*me, GlaiveGUID[i]); - if (Glaive) + if (GlaiveGUID[i]) { - Glaive->CastSpell(me, SPELL_GLAIVE_RETURNS, false); // Make it look like the Glaive flies back up to us - Glaive->SetDisplayId(MODEL_INVISIBLE); // disappear but not die for now + Unit* Glaive = ObjectAccessor::GetUnit(*me, GlaiveGUID[i]); + if (Glaive) + { + Glaive->CastSpell(me, SPELL_GLAIVE_RETURNS, false); // Make it look like the Glaive flies back up to us + Glaive->SetDisplayId(MODEL_INVISIBLE); // disappear but not die for now + } } } - } - Timer[EVENT_FLIGHT_SEQUENCE] = 2000; - break; - case 9: // land - me->SetDisableGravity(false); - me->StopMoving(); - me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); - for (uint8 i = 0; i < 2; ++i) - { - if (GlaiveGUID[i]) + Timer[EVENT_FLIGHT_SEQUENCE] = 2000; + break; + case 9: // land + me->SetDisableGravity(false); + me->StopMoving(); + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + for (uint8 i = 0; i < 2; ++i) { - if (Creature* glaive = ObjectAccessor::GetCreature(*me, GlaiveGUID[i])) - glaive->DespawnOrUnsummon(); + if (GlaiveGUID[i]) + { + if (Creature* glaive = ObjectAccessor::GetCreature(*me, GlaiveGUID[i])) + glaive->DespawnOrUnsummon(); - GlaiveGUID[i].Clear(); + GlaiveGUID[i].Clear(); + } } - } - Timer[EVENT_FLIGHT_SEQUENCE] = 2000; - break; - case 10: // attack - DoResetThreat(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - me->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE); - EnterPhase(PHASE_NORMAL_2); - break; - default: - break; + Timer[EVENT_FLIGHT_SEQUENCE] = 2000; + break; + case 10: // attack + DoResetThreat(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); + me->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE); + EnterPhase(PHASE_NORMAL_2); + break; + default: + break; } ++FlightCount; } @@ -913,23 +913,23 @@ public: switch (TransformCount) { - case 2: - DoResetThreat(); - break; - case 4: - EnterPhase(PHASE_DEMON); - break; - case 7: - DoResetThreat(); - break; - case 9: - if (MaievGUID) - EnterPhase(PHASE_NORMAL_MAIEV); // Depending on whether we summoned Maiev, we switch to either phase 5 or 3 - else - EnterPhase(PHASE_NORMAL_2); - break; - default: - break; + case 2: + DoResetThreat(); + break; + case 4: + EnterPhase(PHASE_DEMON); + break; + case 7: + DoResetThreat(); + break; + case 9: + if (MaievGUID) + EnterPhase(PHASE_NORMAL_MAIEV); // Depending on whether we summoned Maiev, we switch to either phase 5 or 3 + else + EnterPhase(PHASE_NORMAL_2); + break; + default: + break; } if (Phase == PHASE_TRANSFORM_SEQUENCE) Timer[EVENT_TRANSFORM_SEQUENCE] = DemonTransformation[TransformCount].timer; @@ -957,37 +957,37 @@ public: switch (Phase) { - case PHASE_NORMAL: - if (HealthBelowPct(65)) - EnterPhase(PHASE_FLIGHT_SEQUENCE); - break; + case PHASE_NORMAL: + if (HealthBelowPct(65)) + EnterPhase(PHASE_FLIGHT_SEQUENCE); + break; - case PHASE_NORMAL_2: - if (HealthBelowPct(30)) - EnterPhase(PHASE_TALK_SEQUENCE); - break; + case PHASE_NORMAL_2: + if (HealthBelowPct(30)) + EnterPhase(PHASE_TALK_SEQUENCE); + break; - case PHASE_NORMAL_MAIEV: - if (HealthBelowPct(1)) - EnterPhase(PHASE_TALK_SEQUENCE); - break; + case PHASE_NORMAL_MAIEV: + if (HealthBelowPct(1)) + EnterPhase(PHASE_TALK_SEQUENCE); + break; - case PHASE_TALK_SEQUENCE: - if (Event == EVENT_TALK_SEQUENCE) - HandleTalkSequence(); - break; + case PHASE_TALK_SEQUENCE: + if (Event == EVENT_TALK_SEQUENCE) + HandleTalkSequence(); + break; - case PHASE_FLIGHT_SEQUENCE: - if (Event == EVENT_FLIGHT_SEQUENCE) - HandleFlightSequence(); - break; + case PHASE_FLIGHT_SEQUENCE: + if (Event == EVENT_FLIGHT_SEQUENCE) + HandleFlightSequence(); + break; - case PHASE_TRANSFORM_SEQUENCE: - if (Event == EVENT_TRANSFORM_SEQUENCE) - HandleTransformSequence(); - break; - default: - break; + case PHASE_TRANSFORM_SEQUENCE: + if (Event == EVENT_TRANSFORM_SEQUENCE) + HandleTransformSequence(); + break; + default: + break; } if (me->IsNonMeleeSpellCast(false)) @@ -998,63 +998,63 @@ public: switch (Event) { // PHASE_NORMAL - case EVENT_BERSERK: - Talk(SAY_ILLIDAN_ENRAGE); - DoCast(me, SPELL_BERSERK, true); - Timer[EVENT_BERSERK] = 5000; // The buff actually lasts forever. - break; - - case EVENT_TAUNT: - Talk(SAY_ILLIDAN_TAUNT); - Timer[EVENT_TAUNT] = urand(25000, 35000); - break; - - case EVENT_SHEAR: - // no longer exists in 3.0f.2 - // DoCastVictim(SPELL_SHEAR); - Timer[EVENT_SHEAR] = 25000 + (rand32() % 16 * 1000); - break; - - case EVENT_FLAME_CRASH: - DoCastVictim(SPELL_FLAME_CRASH); - Timer[EVENT_FLAME_CRASH] = urand(30000, 40000); - break; - - case EVENT_PARASITIC_SHADOWFIEND: - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200, true)) - DoCast(target, SPELL_PARASITIC_SHADOWFIEND, true); - Timer[EVENT_PARASITIC_SHADOWFIEND] = urand(35000, 45000); - } - break; - - case EVENT_PARASITE_CHECK: - Timer[EVENT_PARASITE_CHECK] = 0; - break; - - case EVENT_DRAW_SOUL: - DoCastVictim(SPELL_DRAW_SOUL); - Timer[EVENT_DRAW_SOUL] = urand(50000, 60000); - break; - - // PHASE_NORMAL_2 - case EVENT_AGONIZING_FLAMES: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_AGONIZING_FLAMES); - Timer[EVENT_AGONIZING_FLAMES] = 0; - break; - - case EVENT_TRANSFORM_NORMAL: - EnterPhase(PHASE_TRANSFORM_SEQUENCE); - break; - - // PHASE_NORMAL_MAIEV - case EVENT_ENRAGE: - DoCast(me, SPELL_ENRAGE); - Timer[EVENT_ENRAGE] = 0; - break; - - default: - break; + case EVENT_BERSERK: + Talk(SAY_ILLIDAN_ENRAGE); + DoCast(me, SPELL_BERSERK, true); + Timer[EVENT_BERSERK] = 5000; // The buff actually lasts forever. + break; + + case EVENT_TAUNT: + Talk(SAY_ILLIDAN_TAUNT); + Timer[EVENT_TAUNT] = urand(25000, 35000); + break; + + case EVENT_SHEAR: + // no longer exists in 3.0f.2 + // DoCastVictim(SPELL_SHEAR); + Timer[EVENT_SHEAR] = 25000 + (rand32() % 16 * 1000); + break; + + case EVENT_FLAME_CRASH: + DoCastVictim(SPELL_FLAME_CRASH); + Timer[EVENT_FLAME_CRASH] = urand(30000, 40000); + break; + + case EVENT_PARASITIC_SHADOWFIEND: + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200, true)) + DoCast(target, SPELL_PARASITIC_SHADOWFIEND, true); + Timer[EVENT_PARASITIC_SHADOWFIEND] = urand(35000, 45000); + } + break; + + case EVENT_PARASITE_CHECK: + Timer[EVENT_PARASITE_CHECK] = 0; + break; + + case EVENT_DRAW_SOUL: + DoCastVictim(SPELL_DRAW_SOUL); + Timer[EVENT_DRAW_SOUL] = urand(50000, 60000); + break; + + // PHASE_NORMAL_2 + case EVENT_AGONIZING_FLAMES: + DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_AGONIZING_FLAMES); + Timer[EVENT_AGONIZING_FLAMES] = 0; + break; + + case EVENT_TRANSFORM_NORMAL: + EnterPhase(PHASE_TRANSFORM_SEQUENCE); + break; + + // PHASE_NORMAL_MAIEV + case EVENT_ENRAGE: + DoCast(me, SPELL_ENRAGE); + Timer[EVENT_ENRAGE] = 0; + break; + + default: + break; } DoMeleeAttackIfReady(); } @@ -1063,32 +1063,32 @@ public: { switch (Event) { - case EVENT_FIREBALL: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL); - Timer[EVENT_FIREBALL] = 3000; - break; - - case EVENT_DARK_BARRAGE: - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE); - Timer[EVENT_DARK_BARRAGE] = 0; - break; - - case EVENT_EYE_BLAST: - CastEyeBlast(); - Timer[EVENT_EYE_BLAST] = 0; - break; - - case EVENT_MOVE_POINT: - Phase = PHASE_FLIGHT_SEQUENCE; - Timer[EVENT_FLIGHT_SEQUENCE] = 0; // do not start Event when changing hover point - HoverPoint += (rand32() % 3 + 1); - if (HoverPoint > 3) - HoverPoint -= 4; - me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z); - break; - - default: - break; + case EVENT_FIREBALL: + DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL); + Timer[EVENT_FIREBALL] = 3000; + break; + + case EVENT_DARK_BARRAGE: + DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE); + Timer[EVENT_DARK_BARRAGE] = 0; + break; + + case EVENT_EYE_BLAST: + CastEyeBlast(); + Timer[EVENT_EYE_BLAST] = 0; + break; + + case EVENT_MOVE_POINT: + Phase = PHASE_FLIGHT_SEQUENCE; + Timer[EVENT_FLIGHT_SEQUENCE] = 0; // do not start Event when changing hover point + HoverPoint += (rand32() % 3 + 1); + if (HoverPoint > 3) + HoverPoint -= 4; + me->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z); + break; + + default: + break; } } @@ -1096,29 +1096,29 @@ public: { switch (Event) { - case EVENT_SHADOW_BLAST: - me->GetMotionMaster()->Clear(false); - if (me->GetVictim() && (!me->IsWithinDistInMap(me->GetVictim(), 50) || !me->IsWithinLOSInMap(me->GetVictim()))) - me->GetMotionMaster()->MoveChase(me->GetVictim(), 30); - else - me->GetMotionMaster()->MoveIdle(); - DoCastVictim(SPELL_SHADOW_BLAST); - Timer[EVENT_SHADOW_BLAST] = 4000; - break; - case EVENT_SHADOWDEMON: - DoCast(me, SPELL_SUMMON_SHADOWDEMON); - Timer[EVENT_SHADOWDEMON] = 0; - Timer[EVENT_FLAME_BURST] += 10000; - break; - case EVENT_FLAME_BURST: - DoCast(me, SPELL_FLAME_BURST); - Timer[EVENT_FLAME_BURST] = 15000; - break; - case EVENT_TRANSFORM_DEMON: - EnterPhase(PHASE_TRANSFORM_SEQUENCE); - break; - default: - break; + case EVENT_SHADOW_BLAST: + me->GetMotionMaster()->Clear(false); + if (me->GetVictim() && (!me->IsWithinDistInMap(me->GetVictim(), 50) || !me->IsWithinLOSInMap(me->GetVictim()))) + me->GetMotionMaster()->MoveChase(me->GetVictim(), 30); + else + me->GetMotionMaster()->MoveIdle(); + DoCastVictim(SPELL_SHADOW_BLAST); + Timer[EVENT_SHADOW_BLAST] = 4000; + break; + case EVENT_SHADOWDEMON: + DoCast(me, SPELL_SUMMON_SHADOWDEMON); + Timer[EVENT_SHADOWDEMON] = 0; + Timer[EVENT_FLAME_BURST] += 10000; + break; + case EVENT_FLAME_BURST: + DoCast(me, SPELL_FLAME_BURST); + Timer[EVENT_FLAME_BURST] = 15000; + break; + case EVENT_TRANSFORM_DEMON: + EnterPhase(PHASE_TRANSFORM_SEQUENCE); + break; + default: + break; } } } @@ -1128,7 +1128,6 @@ public: uint32 Timer[EVENT_ENRAGE + 1]; PhaseIllidan Phase; private: - InstanceScript* instance; EventIllidan Event; uint32 TalkCount; uint32 TransformCount; @@ -1137,7 +1136,6 @@ public: ObjectGuid MaievGUID; ObjectGuid FlameGUID[2]; ObjectGuid GlaiveGUID[2]; - SummonList Summons; }; CreatureAI* GetAI(Creature* creature) const override @@ -1148,7 +1146,7 @@ public: /********************************** End of Illidan AI* *****************************************/ -/******* Functions and vars for Akama's AI* *****/ +/******* Functions and vars for Maiev's AI* *****/ class boss_maiev_shadowsong : public CreatureScript { public: @@ -1377,6 +1375,7 @@ public: } }; +/******* Functions and vars for Akama's AI* *****/ class npc_akama_illidan : public CreatureScript { public: @@ -1434,7 +1433,7 @@ public: KillAllElites(); - me->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Database sometimes has strange values.. + me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); // Database sometimes has strange values.. me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); me->setActive(false); me->SetVisible(false); @@ -1811,7 +1810,7 @@ public: void boss_illidan_stormrage::boss_illidan_stormrageAI::Reset() { - instance->SetBossState(DATA_ILLIDAN_STORMRAGE, NOT_STARTED); + _Reset(); if (Creature* akama = ObjectAccessor::GetCreature(*me, AkamaGUID)) { @@ -1830,12 +1829,11 @@ void boss_illidan_stormrage::boss_illidan_stormrageAI::Reset() SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); me->SetDisableGravity(false); me->setActive(false); - Summons.DespawnAll(); } void boss_illidan_stormrage::boss_illidan_stormrageAI::JustSummoned(Creature* summon) { - Summons.Summon(summon); + summons.Summon(summon); switch (summon->GetEntry()) { case PARASITIC_SHADOWFIEND: @@ -1928,7 +1926,7 @@ void boss_illidan_stormrage::boss_illidan_stormrageAI::HandleTalkSequence() break; case 15: DoCast(me, SPELL_DEATH); // Animate his kneeling + stun him - Summons.DespawnAll(); + summons.DespawnAll(); break; case 17: if (Creature* akama = ObjectAccessor::GetCreature(*me, AkamaGUID)) diff --git a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp index aaf72512ae9..9412067f77e 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_mother_shahraz.cpp @@ -149,7 +149,7 @@ public: void TeleportPlayers() { - uint32 random = urand(0, 7); + uint32 random = urand(0, 6); float X = TeleportPoint[random].x; float Y = TeleportPoint[random].y; float Z = TeleportPoint[random].z; @@ -210,7 +210,7 @@ public: break; case EVENT_PRISMATIC_SHIELD: // Random Prismatic Shield every 15 seconds. - DoCast(me, PrismaticAuras[urand(0, 6)]); + DoCast(me, PrismaticAuras[urand(0, 5)]); events.ScheduleEvent(EVENT_PRISMATIC_SHIELD, 15000); break; case EVENT_FATAL_ATTRACTION: diff --git a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp index afc7d3d0fd7..d89437c2816 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_reliquary_of_souls.cpp @@ -135,12 +135,11 @@ public: return GetInstanceAI<boss_reliquary_of_soulsAI>(creature); } - struct boss_reliquary_of_soulsAI : public ScriptedAI + struct boss_reliquary_of_soulsAI : public BossAI { - boss_reliquary_of_soulsAI(Creature* creature) : ScriptedAI(creature) + boss_reliquary_of_soulsAI(Creature* creature) : BossAI(creature, DATA_RELIQUARY_OF_SOULS) { Initialize(); - instance = creature->GetInstanceScript(); Counter = 0; Timer = 0; SoulCount = 0; @@ -152,8 +151,6 @@ public: Phase = 0; } - InstanceScript* instance; - ObjectGuid EssenceGUID; uint32 Phase; @@ -165,7 +162,7 @@ public: void Reset() override { - instance->SetBossState(DATA_RELIQUARY_OF_SOULS, NOT_STARTED); + _Reset(); if (EssenceGUID) { @@ -202,8 +199,7 @@ public: void EnterCombat(Unit* who) override { me->AddThreat(who, 10000.0f); - DoZoneInCombat(); - instance->SetBossState(DATA_RELIQUARY_OF_SOULS, IN_PROGRESS); + _EnterCombat(); Phase = 1; Counter = 0; @@ -246,11 +242,6 @@ public: } } - void JustDied(Unit* /*killer*/) override - { - instance->SetBossState(DATA_RELIQUARY_OF_SOULS, DONE); - } - void UpdateAI(uint32 diff) override { if (!Phase) diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp index 1a9074c2464..c9e08176ad6 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp @@ -15,45 +15,45 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* -Name: Boss_Shade_of_Akama -%Complete: 80 -Comment: WIP A few more adds to script, ending script, and bugs. -Category: Black Temple -*/ - #include "ObjectMgr.h" #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "PassiveAI.h" #include "ScriptedGossip.h" #include "GridNotifiers.h" #include "black_temple.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" enum Says { - // Akama Ending cinematic text + // Akama SAY_BROKEN_FREE_0 = 0, SAY_BROKEN_FREE_1 = 1, - SAY_BROKEN_FREE_2 = 2 + SAY_BROKEN_FREE_2 = 2, + SAY_LOW_HEALTH = 3, + SAY_DEAD = 4, + // Ashtongue Broken + SAY_BROKEN_SPECIAL = 0, + SAY_BROKEN_HAIL = 1 }; enum Spells { // Akama - SPELL_STEALTH = 34189, // On Spawn - SPELL_AKAMA_SOUL_CHANNEL = 40447, // Cast on self hits Shade - SPELL_FIXATE = 40607, // Cast on self hits Shade - SPELL_CHAIN_LIGHTNING = 39945, // Combat - SPELL_DESTRUCTIVE_POISON = 40874, // Combat + SPELL_STEALTH = 34189, + SPELL_AKAMA_SOUL_CHANNEL = 40447, + SPELL_FIXATE = 40607, + SPELL_CHAIN_LIGHTNING = 39945, + SPELL_DESTRUCTIVE_POISON = 40874, + SPELL_AKAMA_SOUL_EXPEL = 40902, // Shade - SPELL_THREAT = 41602, // self cast hits Akama - SPELL_SHADE_OF_AKAMA_TRIGGER = 40955, // Cast on death - SPELL_AKAMA_SOUL_EXPEL_CHANNEL = 40927, // must hit shade - SPELL_AKAMA_SOUL_EXPEL = 40902, // the one he cast + SPELL_THREAT = 41602, + SPELL_SHADE_OF_AKAMA_TRIGGER = 40955, + SPELL_AKAMA_SOUL_EXPEL_CHANNEL = 40927, // Ashtongue Channeler SPELL_SHADE_SOUL_CHANNEL = 40401, SPELL_SHADE_SOUL_CHANNEL_2 = 40520, - SPELL_SHADOWFORM = 40973, // Cast on Shade // Creature Spawner SPELL_ASHTONGUE_WAVE_B = 42035, SPELL_SUMMON_ASHTONGUE_SORCERER = 40476, @@ -78,11 +78,6 @@ enum Spells enum Creatures { NPC_ASHTONGUE_CHANNELER = 23421, - NPC_ASHTONGUE_SORCERER = 23215, - NPC_ASHTONGUE_DEFENDER = 23216, - NPC_ASHTONGUE_ELEMENTALIST = 23523, - NPC_ASHTONGUE_ROGUE = 23318, - NPC_ASHTONGUE_SPIRITBINDER = 23524, NPC_ASHTONGUE_BROKEN = 23319, NPC_CREATURE_SPAWNER_AKAMA = 23210 }; @@ -95,60 +90,114 @@ enum Factions enum Actions { - ACTION_CHANNELER_DIED = 1, - ACTION_START_SPAWNING = 2, - ACTION_STOP_SPAWNING = 3, - ACTION_DESPAWN_ALL_SPAWNS = 4, + ACTION_START_SPAWNING = 0, + ACTION_STOP_SPAWNING = 1, + ACTION_DESPAWN_ALL_SPAWNS = 2, + ACTION_SHADE_OF_AKAMA_DEAD = 3, + ACTION_BROKEN_SPECIAL = 4, + ACTION_BROKEN_EMOTE = 5, + ACTION_BROKEN_HAIL = 6 }; enum Events { // Akama - EVENT_SHADE_START = 1, - EVENT_SHADE_CHANNEL = 2, - EVENT_FIXATE = 3, - EVENT_CHAIN_LIGHTNING = 4, - EVENT_DESTRUCTIVE_POISON = 5, - // Shade - EVENT_RESET_ENCOUNTER = 6, - EVENT_FIND_CHANNELERS_SPAWNERS = 7, - EVENT_SET_CHANNELERS_SPAWNERS = 8, - EVENT_START_ATTACK_AKAMA = 9, - EVENT_ADD_THREAT = 10, + EVENT_SHADE_START = 1, + EVENT_SHADE_CHANNEL = 2, + EVENT_FIXATE = 3, + EVENT_CHAIN_LIGHTNING = 4, + EVENT_DESTRUCTIVE_POISON = 5, + EVENT_START_BROKEN_FREE = 6, + EVENT_START_SOUL_EXPEL = 7, + EVENT_EVADE_CHECK = 8, + EVENT_BROKEN_FREE_1 = 9, + EVENT_BROKEN_FREE_2 = 10, + EVENT_BROKEN_FREE_3 = 11, + EVENT_BROKEN_FREE_4 = 12, + // Shade of Akama + EVENT_INITIALIZE_SPAWNERS = 13, + EVENT_START_CHANNELERS_AND_SPAWNERS = 14, + EVENT_ADD_THREAT = 15, // Creature spawner - EVENT_SPAWN_WAVE_B = 11, - EVENT_SUMMON_ASHTONGUE_SORCERER = 12, - EVENT_SUMMON_ASHTONGUE_DEFENDER = 13, - // Channeler - EVENT_CHANNEL = 14, - // Ashtongue Sorcerer - EVENT_SORCERER_CHANNEL = 15, + EVENT_SPAWN_WAVE_B = 16, + EVENT_SUMMON_ASHTONGUE_SORCERER = 17, + EVENT_SUMMON_ASHTONGUE_DEFENDER = 18, // Ashtongue Defender - EVENT_DEBILITATING_STRIKE = 16, - EVENT_HEROIC_STRIKE = 17, - EVENT_SHIELD_BASH = 18, - EVENT_WINDFURY = 29, + EVENT_DEBILITATING_STRIKE = 19, + EVENT_HEROIC_STRIKE = 20, + EVENT_SHIELD_BASH = 21, + EVENT_WINDFURY = 22, // Ashtongue Rogue - EVENT_DEBILITATING_POISON = 20, - EVENT_EVISCERATE = 21, + EVENT_DEBILITATING_POISON = 23, + EVENT_EVISCERATE = 24, // Ashtongue Elementalist - EVENT_RAIN_OF_FIRE = 22, - EVENT_LIGHTNING_BOLT = 23, + EVENT_RAIN_OF_FIRE = 25, + EVENT_LIGHTNING_BOLT = 26, // Ashtongue Spiritbinder - EVENT_SPIRIT_HEAL = 24 + EVENT_SPIRIT_HEAL = 27, + EVENT_SPIRIT_MEND_RESET = 28, + EVENT_CHAIN_HEAL_RESET = 29 }; -G3D::Vector3 const ShadeWP = { 512.4877f, 400.7993f, 112.7837f }; +enum Misc +{ + AKAMA_CHANNEL_WAYPOINT = 0, + AKAMA_INTRO_WAYPOINT = 1, + + SUMMON_GROUP_RESET = 1 +}; -G3D::Vector3 const AkamaWP[] = +Position const AkamaWP[2] = { { 517.4877f, 400.7993f, 112.7837f }, { 468.4435f, 401.1062f, 118.5379f } }; -// ######################################################## -// Shade of Akama -// ######################################################## +Position const BrokenPos[18] = +{ + { 495.5628f, 462.7089f, 112.8169f, 4.1808090f }, + { 498.3421f, 463.8384f, 112.8673f, 4.5634810f }, + { 501.6708f, 463.8806f, 112.8673f, 3.7157850f }, + { 532.4264f, 448.4718f, 112.8563f, 3.9813020f }, + { 532.9113f, 451.6227f, 112.8671f, 4.6479530f }, + { 532.8243f, 453.9475f, 112.8671f, 4.7032810f }, + { 521.5317f, 402.3790f, 112.8671f, 3.1138120f }, + { 521.9184f, 404.6848f, 112.8671f, 4.0787760f }, + { 522.4290f, 406.5160f, 112.8671f, 3.3869470f }, + { 521.0833f, 393.1852f, 112.8611f, 3.0750830f }, + { 521.9014f, 395.6381f, 112.8671f, 4.0157140f }, + { 522.2610f, 397.7423f, 112.8671f, 3.4417790f }, + { 532.4565f, 345.3987f, 112.8585f, 1.7232640f }, + { 532.5565f, 346.8792f, 112.8671f, 1.8325960f }, + { 532.5491f, 348.6840f, 112.8671f, 0.2054047f }, + { 501.4669f, 338.5967f, 112.8504f, 1.7038430f }, + { 499.0937f, 337.9894f, 112.8673f, 1.8586250f }, + { 496.8722f, 338.0152f, 112.8673f, 0.5428222f } +}; + +Position const BrokenWP[18] = +{ + { 479.1884f, 434.8635f, 112.7838f }, + { 479.7349f, 435.9843f, 112.7838f }, + { 480.5328f, 436.8310f, 112.7838f }, + { 493.1714f, 420.1136f, 112.7838f }, + { 494.7830f, 417.4830f, 112.7838f }, + { 492.9280f, 423.1891f, 112.7838f }, + { 491.8618f, 403.2035f, 112.7838f }, + { 491.7784f, 400.2046f, 112.7838f }, + { 491.9451f, 406.2023f, 112.7838f }, + { 488.3535f, 395.3652f, 112.7838f }, + { 488.8324f, 392.3267f, 112.7838f }, + { 489.2300f, 398.3135f, 112.7838f }, + { 491.9286f, 383.0433f, 112.7838f }, + { 491.1526f, 380.0966f, 112.7839f }, + { 493.6747f, 385.5407f, 112.7838f }, + { 476.2499f, 369.0865f, 112.7839f }, + { 473.7637f, 367.8766f, 112.7839f }, + { 478.8986f, 370.1895f, 112.7839f } +}; + +static float const MIDDLE_OF_ROOM = 400.0f; class boss_shade_of_akama : public CreatureScript { @@ -160,233 +209,144 @@ public: boss_shade_of_akamaAI(Creature* creature) : BossAI(creature, DATA_SHADE_OF_AKAMA) { Initialize(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } void Initialize() { - combatStarted = false; - akamaReached = false; - HasKilledAkama = false; - HasKilledAkamaAndReseting = false; + _spawners.clear(); + _isInPhaseOne = true; } void Reset() override { - _Reset(); - if (!HasKilledAkamaAndReseting) - { - for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr)) - Channeler->DespawnOrUnsummon(); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - - events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 3000); - events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 5000); - } - + Initialize(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); me->SetWalk(true); - Initialize(); + events.ScheduleEvent(EVENT_INITIALIZE_SPAWNERS, Seconds(1)); + me->SummonCreatureGroup(SUMMON_GROUP_RESET); } - void EnterCombat(Unit* /*who*/) override { } - - void AttackStart(Unit* who) override + void EnterEvadeMode(EvadeReason /*why*/) override { - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - { - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - if (Akama->IsAlive()) - ScriptedAI::AttackStart(Akama); - } - else - ScriptedAI::AttackStart(who); - } + _Reset(); - void DoAction(int32 actionId) override - { - if (actionId == ACTION_CHANNELER_DIED) - me->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - UpdateSpeed(); + _DespawnAtEvade(); } void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { if (spell->Id == SPELL_AKAMA_SOUL_CHANNEL) { - combatStarted = true; - events.ScheduleEvent(EVENT_START_ATTACK_AKAMA, 500); - events.ScheduleEvent(EVENT_SET_CHANNELERS_SPAWNERS, 1000); + events.ScheduleEvent(EVENT_START_CHANNELERS_AND_SPAWNERS, Seconds(1)); me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - me->AddThreat(Akama, 10000000.0f); + events.ScheduleEvent(EVENT_EVADE_CHECK, Seconds(10)); + if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } - else if (spell->Id == SPELL_SHADE_SOUL_CHANNEL_2) - UpdateSpeed(); + + if (spell->Id == SPELL_AKAMA_SOUL_EXPEL) + DoCastSelf(SPELL_AKAMA_SOUL_EXPEL_CHANNEL); } - void UpdateSpeed() + void MovementInform(uint32 motionType, uint32 /*pointId*/) override { - float moveSpeed = 0.2f; - - if (me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2) <= 3) + if (_isInPhaseOne && motionType == CHASE_MOTION_TYPE) { - moveSpeed = (2.0f - (0.6f * me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2))); - me->SetSpeedRate(MOVE_WALK, moveSpeed / 2.5f); - me->SetSpeedRate(MOVE_RUN, (moveSpeed * 2) / 7); - me->ClearUnitState(UNIT_STATE_ROOT); + _isInPhaseOne = false; + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetWalk(false); + events.ScheduleEvent(EVENT_ADD_THREAT, Milliseconds(100)); + + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_STOP_SPAWNING); } - else - me->AddUnitState(UNIT_STATE_ROOT); } - void UpdateAI(uint32 diff) override + void JustDied(Unit* /*killer*/) override { - if (HasKilledAkamaAndReseting) - return; + DoCastSelf(SPELL_SHADE_OF_AKAMA_TRIGGER); + + if (Creature* akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + akama->AI()->DoAction(ACTION_SHADE_OF_AKAMA_DEAD); + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); + + events.Reset(); + summons.DespawnEntry(NPC_ASHTONGUE_CHANNELER); + instance->SetBossState(DATA_SHADE_OF_AKAMA, DONE); + } + + void EnterEvadeModeIfNeeded() + { + Map::PlayerList const &players = me->GetMap()->GetPlayers(); + for (Map::PlayerList::const_iterator i = players.begin(); i != players.end(); ++i) + if (Player* player = i->GetSource()) + if (player->IsAlive() && !player->IsGameMaster() && CheckBoundary(player)) + return; + + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + } + + void UpdateAI(uint32 diff) override + { events.Update(diff); - if (!combatStarted) - { - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_RESET_ENCOUNTER: - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - if (!Akama->IsAlive()) - Akama->Respawn(); - break; - case EVENT_FIND_CHANNELERS_SPAWNERS: - { - std::list<Creature*> ChannelerList; - me->GetCreatureListWithEntryInGrid(ChannelerList, NPC_ASHTONGUE_CHANNELER, 15.0f); - - if (!ChannelerList.empty()) - for (std::list<Creature*>::const_iterator itr = ChannelerList.begin(); itr != ChannelerList.end(); ++itr) - { - Channelers.push_back((*itr)->GetGUID()); - if ((*itr)->isDead()) - (*itr)->Respawn(); - } - - std::list<Creature*> SpawnerList; - me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA, 90.0f); - - if (!SpawnerList.empty()) - for (std::list<Creature*>::const_iterator itr = SpawnerList.begin(); itr != SpawnerList.end(); ++itr) - Spawners.push_back((*itr)->GetGUID()); - - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN); - break; - } - default: - break; - } - } - } - else + if (!UpdateVictim()) + return; + + while (uint32 eventId = events.ExecuteEvent()) { - while (uint32 eventId = events.ExecuteEvent()) + switch (eventId) { - switch (eventId) + case EVENT_INITIALIZE_SPAWNERS: { - case EVENT_SET_CHANNELERS_SPAWNERS: - { - for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr)) - Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_START_SPAWNING); - break; - } - case EVENT_START_ATTACK_AKAMA: - me->GetMotionMaster()->MovePoint(0, ShadeWP.x, ShadeWP.y, ShadeWP.z, false); - events.ScheduleEvent(EVENT_START_ATTACK_AKAMA, 1000); - break; - case EVENT_ADD_THREAT: - DoCast(SPELL_THREAT); - events.ScheduleEvent(EVENT_ADD_THREAT, 3500); - break; - default: - break; - } - } + std::list<Creature*> SpawnerList; + me->GetCreatureListWithEntryInGrid(SpawnerList, NPC_CREATURE_SPAWNER_AKAMA); + for (Creature* spawner : SpawnerList) + _spawners.push_back(spawner->GetGUID()); - if (HasKilledAkama) - { - if (!HasKilledAkamaAndReseting) - { - HasKilledAkamaAndReseting = true; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - instance->SetBossState(DATA_SHADE_OF_AKAMA, NOT_STARTED); - me->RemoveAllAurasExceptType(SPELL_AURA_DUMMY); - me->DeleteThreatList(); - me->CombatStop(); - me->GetMotionMaster()->MoveTargetedHome(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC); - combatStarted = false; - - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - Akama->DespawnOrUnsummon(); - - for (GuidList::const_iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr) - if (Creature* Channeler = ObjectAccessor::GetCreature(*me, *itr)) - Channeler->DespawnOrUnsummon(); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_DESPAWN_ALL_SPAWNS); - - events.ScheduleEvent(EVENT_FIND_CHANNELERS_SPAWNERS, 10000); - events.ScheduleEvent(EVENT_RESET_ENCOUNTER, 20000); + break; } - } - - if (!akamaReached) - { - if (Creature* Akama = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) + case EVENT_START_CHANNELERS_AND_SPAWNERS: { - if (me->IsWithinDist(Akama, 2.0f, false)) - { - akamaReached = true; - me->GetMotionMaster()->Clear(true); - me->GetMotionMaster()->MoveIdle(); - me->SetWalk(false); + for (ObjectGuid const& summonGuid : summons) + if (Creature* channeler = ObjectAccessor::GetCreature(*me, summonGuid)) + channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + for (ObjectGuid const& spawnerGuid : _spawners) + if (Creature* spawner = ObjectAccessor::GetCreature(*me, spawnerGuid)) + spawner->AI()->DoAction(ACTION_START_SPAWNING); - events.CancelEvent(EVENT_START_ATTACK_AKAMA); - events.ScheduleEvent(EVENT_ADD_THREAT, 100); - - for (GuidList::const_iterator itr = Spawners.begin(); itr != Spawners.end(); ++itr) - if (Creature* Spawner = ObjectAccessor::GetCreature(*me, *itr)) - Spawner->AI()->DoAction(ACTION_STOP_SPAWNING); - } + break; } + case EVENT_ADD_THREAT: + DoCast(SPELL_THREAT); + events.Repeat(Seconds(3) + Milliseconds(500)); + break; + case EVENT_EVADE_CHECK: + EnterEvadeModeIfNeeded(); + events.Repeat(Seconds(10)); + break; + default: + break; } - else - DoMeleeAttackIfReady(); } + + DoMeleeAttackIfReady(); } - public: - bool HasKilledAkama; private: - GuidList Channelers; - GuidList Spawners; - bool akamaReached; - bool combatStarted; - bool HasKilledAkamaAndReseting; + GuidVector _spawners; + bool _isInPhaseOne; }; CreatureAI* GetAI(Creature* creature) const override @@ -395,10 +355,6 @@ public: } }; -// ######################################################## -// Akama -// ######################################################## - class npc_akama_shade : public CreatureScript { public: @@ -406,117 +362,188 @@ public: struct npc_akamaAI : public ScriptedAI { - npc_akamaAI(Creature* creature) : ScriptedAI(creature) + npc_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me) { Initialize(); - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Initialize() { - StartChannel = false; - StartCombat = false; - HasYelledOnce = false; - ShadeHasDied = false; + _isInCombat = false; + _hasYelledOnce = false; + _chosen.Clear(); + _summons.DespawnAll(); + _events.Reset(); } void Reset() override { - me->setFaction(FACTION_FRIENDLY); - DoCast(me, SPELL_STEALTH); Initialize(); + me->setFaction(FACTION_FRIENDLY); + DoCastSelf(SPELL_STEALTH); - if (instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) + if (_instance->GetBossState(DATA_SHADE_OF_AKAMA) != DONE) me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); } - void JustDied(Unit* /*killer*/) override + void JustSummoned(Creature* summon) override { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - if (Shade->IsAlive()) - ENSURE_AI(boss_shade_of_akama::boss_shade_of_akamaAI, Shade->AI())->HasKilledAkama = true; - me->GetMotionMaster()->Clear(true); - me->GetMotionMaster()->MoveIdle(); + _summons.Summon(summon); } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - if (spell->Id == SPELL_THREAT && !StartCombat) + if (spell->Id == SPELL_THREAT && !_isInCombat) { - me->ClearUnitState(UNIT_STATE_ROOT); - me->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->RemoveAura(SPELL_AKAMA_SOUL_CHANNEL); - StartCombat = true; + _isInCombat = true; + me->SetWalk(false); + me->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + { + shade->RemoveAurasDueToSpell(SPELL_AKAMA_SOUL_CHANNEL); + AttackStart(shade); + _events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, Seconds(2)); + _events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, Seconds(5)); + } } } - void EnterCombat(Unit* /*who*/) override + void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override { - events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 2000); - events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, 5000); + if (me->HealthBelowPct(20) && !_hasYelledOnce) + { + _hasYelledOnce = true; + Talk(SAY_LOW_HEALTH); + } } - void UpdateAI(uint32 diff) override + void DoAction(int32 actionId) override { - if (StartChannel) + if (actionId == ACTION_SHADE_OF_AKAMA_DEAD) { - events.Update(diff); + _isInCombat = false; + me->CombatStop(true); + me->setFaction(FACTION_FRIENDLY); + me->SetWalk(true); + _events.Reset(); + me->GetMotionMaster()->MovePoint(AKAMA_INTRO_WAYPOINT, AkamaWP[1]); + } + } - while (uint32 eventId = events.ExecuteEvent()) + void MovementInform(uint32 motionType, uint32 pointId) override + { + if (motionType != POINT_MOTION_TYPE) + return; + + if (pointId == AKAMA_CHANNEL_WAYPOINT) + _events.ScheduleEvent(EVENT_SHADE_CHANNEL, Seconds(1)); + + else if (pointId == AKAMA_INTRO_WAYPOINT) + { + me->SetWalk(false); + me->SetFacingTo(0.08726646f); + _events.ScheduleEvent(EVENT_START_SOUL_EXPEL, Seconds(1)); + } + } + + void SummonBrokens() + { + for (uint8 i = 0; i < 18; i++) + { + if (TempSummon* summoned = me->SummonCreature(NPC_ASHTONGUE_BROKEN, BrokenPos[i])) { - switch (eventId) - { - case EVENT_SHADE_START: - instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS); - me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->RemoveAura(SPELL_STEALTH); - me->SetWalk(true); - me->GetMotionMaster()->MovePoint(0, AkamaWP[0].x, AkamaWP[0].y, AkamaWP[0].z, false); - events.ScheduleEvent(EVENT_SHADE_CHANNEL, 10000); - break; - case EVENT_SHADE_CHANNEL: - me->AddUnitState(UNIT_STATE_ROOT); - me->SetFacingTo(3.118662f); - DoCast(me, SPELL_AKAMA_SOUL_CHANNEL); - me->setFaction(FACTION_COMBAT); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); - events.ScheduleEvent(EVENT_FIXATE, 5000); - break; - case EVENT_FIXATE: - DoCast(SPELL_FIXATE); - StartChannel = false; - break; - default: - break; - } + summoned->SetWalk(true); + summoned->GetMotionMaster()->MovePoint(0, BrokenWP[i]); + if (i == 9) //On Sniffs, npc that Yell "Special" is the tenth to be created + _chosen = summoned->GetGUID(); } } + } - if (!UpdateVictim()) - return; - - events.Update(diff); + void UpdateAI(uint32 diff) override + { + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { + case EVENT_SHADE_START: + _instance->SetBossState(DATA_SHADE_OF_AKAMA, IN_PROGRESS); + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + me->RemoveAurasDueToSpell(SPELL_STEALTH); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(AKAMA_CHANNEL_WAYPOINT, AkamaWP[0], false); + break; + case EVENT_SHADE_CHANNEL: + me->SetFacingTo(3.118662f); + DoCastSelf(SPELL_AKAMA_SOUL_CHANNEL); + me->setFaction(FACTION_COMBAT); + _events.ScheduleEvent(EVENT_FIXATE, Seconds(5)); + break; + case EVENT_FIXATE: + DoCast(SPELL_FIXATE); + break; case EVENT_CHAIN_LIGHTNING: DoCastVictim(SPELL_CHAIN_LIGHTNING); - events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, urand(10000, 15000)); + _events.Repeat(randtime(Seconds(8), Seconds(15))); break; case EVENT_DESTRUCTIVE_POISON: - DoCast(me, SPELL_DESTRUCTIVE_POISON); - events.ScheduleEvent(EVENT_DESTRUCTIVE_POISON, urand(4000, 5000)); + DoCastSelf(SPELL_DESTRUCTIVE_POISON); + _events.Repeat(randtime(Seconds(3), Seconds(7))); + break; + case EVENT_START_SOUL_EXPEL: + DoCast(SPELL_AKAMA_SOUL_EXPEL); + _events.ScheduleEvent(EVENT_START_BROKEN_FREE, Seconds(15)); + break; + case EVENT_START_BROKEN_FREE: + me->HandleEmoteCommand(EMOTE_ONESHOT_ROAR); + Talk(SAY_BROKEN_FREE_0); + SummonBrokens(); + _events.ScheduleEvent(EVENT_BROKEN_FREE_1, Seconds(10)); + break; + case EVENT_BROKEN_FREE_1: + Talk(SAY_BROKEN_FREE_1); + _events.ScheduleEvent(EVENT_BROKEN_FREE_2, Seconds(12)); + break; + case EVENT_BROKEN_FREE_2: + Talk(SAY_BROKEN_FREE_2); + _events.ScheduleEvent(EVENT_BROKEN_FREE_3, Seconds(15)); + break; + case EVENT_BROKEN_FREE_3: + if (Creature* special = ObjectAccessor::GetCreature(*me, _chosen)) + special->AI()->Talk(SAY_BROKEN_SPECIAL); + + _summons.DoAction(ACTION_BROKEN_EMOTE, _pred); + _events.ScheduleEvent(EVENT_BROKEN_FREE_4, Seconds(5)); + break; + case EVENT_BROKEN_FREE_4: + _summons.DoAction(ACTION_BROKEN_HAIL, _pred); break; default: break; } } - DoMeleeAttackIfReady(); + if (me->getFaction() == FACTION_COMBAT) + { + if (!UpdateVictim()) + return; + + DoMeleeAttackIfReady(); + } + } + + void JustDied(Unit* /*killer*/) override + { + _summons.DespawnAll(); + Talk(SAY_DEAD); + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (shade->IsAlive()) + shade->AI()->EnterEvadeMode(EVADE_REASON_OTHER); } void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override @@ -524,19 +551,18 @@ public: if (gossipListId == 0) { player->CLOSE_GOSSIP_MENU(); - StartChannel = true; - events.ScheduleEvent(EVENT_SHADE_START, 500); + _events.ScheduleEvent(EVENT_SHADE_START, Milliseconds(500)); } } private: - InstanceScript* instance; - EventMap events; - bool StartChannel; - bool ShadeHasDied; - bool StartCombat; - bool HasYelledOnce; - + InstanceScript* _instance; + EventMap _events; + SummonList _summons; + DummyEntryCheckPredicate _pred; + ObjectGuid _chosen; //Creature that should yell the speech special. + bool _isInCombat; + bool _hasYelledOnce; }; CreatureAI* GetAI(Creature* creature) const override @@ -545,70 +571,44 @@ public: } }; -// ######################################################## -// Ashtongue Channeler -// ######################################################## - class npc_ashtongue_channeler : public CreatureScript { public: npc_ashtongue_channeler() : CreatureScript("npc_ashtongue_channeler") { } - struct npc_ashtongue_channelerAI : public ScriptedAI + struct npc_ashtongue_channelerAI : public PassiveAI { - npc_ashtongue_channelerAI(Creature* creature) : ScriptedAI(creature) + npc_ashtongue_channelerAI(Creature* creature) : PassiveAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true); + _scheduler.Schedule(Seconds(2), [this](TaskContext channel) + { + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + { + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_CHANNEL, 2000); - } + else + me->DespawnOrUnsummon(Seconds(3)); + } - void JustDied(Unit* /*killer*/) override - { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); + channel.Repeat(Seconds(2)); + }); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } - void EnterCombat(Unit* /*who*/) override { } - void AttackStart(Unit* /*who*/) override { } - void UpdateAI(uint32 diff) override { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) - { - switch (eventId) - { - case EVENT_CHANNEL: - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - { - if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - DoCast(me, SPELL_SHADE_SOUL_CHANNEL); - else - { - me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); - } - } - events.ScheduleEvent(EVENT_CHANNEL, 2000); - break; - default: - break; - } - } + _scheduler.Update(diff); } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + TaskScheduler _scheduler; }; CreatureAI* GetAI(Creature* creature) const override @@ -617,10 +617,6 @@ public: } }; -// ######################################################## -// Creature Generator Akama -// ######################################################## - class npc_creature_generator_akama : public CreatureScript { public: @@ -628,57 +624,53 @@ public: struct npc_creature_generator_akamaAI : public ScriptedAI { - npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), Summons(me) + npc_creature_generator_akamaAI(Creature* creature) : ScriptedAI(creature), _summons(me) { Initialize(); - instance = creature->GetInstanceScript(); } void Initialize() { - doSpawning = false; - leftSide = false; + _leftSide = false; + _events.Reset(); + _summons.DespawnAll(); } void Reset() override { - Summons.DespawnAll(); - Initialize(); - if (me->GetPositionY() < 400.0f) - leftSide = true; + if (me->GetPositionY() < MIDDLE_OF_ROOM) + _leftSide = true; } void JustSummoned(Creature* summon) override { - Summons.Summon(summon); + _summons.Summon(summon); } void DoAction(int32 actionId) override { - doSpawning = true; - switch (actionId) { case ACTION_START_SPAWNING: - if (leftSide) + if (_leftSide) { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 100); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(2000, 5000)); + _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Milliseconds(100)); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, randtime(Seconds(2), Seconds(5))); } else { - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, 10000); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(2000, 5000)); + _events.ScheduleEvent(EVENT_SPAWN_WAVE_B, Seconds(10)); + _events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, randtime(Seconds(2), Seconds(5))); } break; case ACTION_STOP_SPAWNING: - doSpawning = false; + _events.Reset(); break; case ACTION_DESPAWN_ALL_SPAWNS: - doSpawning = false; - Summons.DespawnAll(); + _events.Reset(); + _summons.DespawnAll(); break; default: break; @@ -687,39 +679,34 @@ public: void UpdateAI(uint32 diff) override { - if (doSpawning) - { - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_SPAWN_WAVE_B: - DoCast(me, SPELL_ASHTONGUE_WAVE_B); - events.ScheduleEvent(EVENT_SPAWN_WAVE_B, urand(45000, 50000)); - break; - case EVENT_SUMMON_ASHTONGUE_SORCERER: // left - DoCast(me, SPELL_SUMMON_ASHTONGUE_SORCERER); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_SORCERER, urand(30000, 35000)); - break; - case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right - DoCast(me, SPELL_SUMMON_ASHTONGUE_DEFENDER); - events.ScheduleEvent(EVENT_SUMMON_ASHTONGUE_DEFENDER, urand(30000, 35000)); - break; - default: - break; - } + case EVENT_SPAWN_WAVE_B: + DoCastSelf(SPELL_ASHTONGUE_WAVE_B); + _events.Repeat(randtime(Seconds(50), Seconds(60))); + break; + case EVENT_SUMMON_ASHTONGUE_SORCERER: // left + DoCastSelf(SPELL_SUMMON_ASHTONGUE_SORCERER); + _events.Repeat(randtime(Seconds(30), Seconds(35))); + break; + case EVENT_SUMMON_ASHTONGUE_DEFENDER: // right + DoCastSelf(SPELL_SUMMON_ASHTONGUE_DEFENDER); + _events.Repeat(randtime(Seconds(30), Seconds(40))); + break; + default: + break; } } } private: - InstanceScript* instance; - EventMap events; - SummonList Summons; - bool leftSide; - bool doSpawning; + EventMap _events; + SummonList _summons; + bool _leftSide; }; CreatureAI* GetAI(Creature* creature) const override @@ -728,10 +715,6 @@ public: } }; -// ######################################################## -// Ashtongue Sorcerer -// ######################################################## - class npc_ashtongue_sorcerer : public CreatureScript { public: @@ -742,103 +725,96 @@ public: npc_ashtongue_sorcererAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Initialize() { - startedBanishing = false; - switchToCombat = false; + _switchToCombat = false; + _inBanish = false; } void Reset() override { - if (!startedBanishing) + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) + me->GetMotionMaster()->MovePoint(0, shade->GetPosition()); + + else { - if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - me->GetMotionMaster()->MovePoint(0, Shade->GetPositionX(), Shade->GetPositionY(), Shade->GetPositionZ(), false); - else - { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); - } + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } } - Initialize(); } void JustDied(Unit* /*killer*/) override { - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } + void EnterEvadeMode(EvadeReason /*why*/) override { } void EnterCombat(Unit* /*who*/) override { } void AttackStart(Unit* who) override { - if (!switchToCombat) + if (!_switchToCombat) return; + ScriptedAI::AttackStart(who); } - void UpdateAI(uint32 diff) override + void MoveInLineOfSight(Unit* who) override { - events.Update(diff); - - while (uint32 eventId = events.ExecuteEvent()) + if (!_inBanish && who->GetGUID() == _instance->GetGuidData(DATA_SHADE_OF_AKAMA) && me->IsWithinDist(who, 20.0f, false)) { - switch (eventId) + _inBanish = true; + me->StopMoving(); + me->GetMotionMaster()->Clear(false); + me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand(-8.0f, 8.0f), me->GetPositionY() + frand(-8.0f, 8.0f), me->GetPositionZ()); + + _scheduler.Schedule(Seconds(1) + Milliseconds(500), [this](TaskContext sorcer_channel) { - case EVENT_SORCERER_CHANNEL: - if (Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + if (Creature* shade = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_SHADE_OF_AKAMA))) + { + if (shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) { - if (Shade->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE)) - { - me->SetFacingToObject(Shade); - DoCast(me, SPELL_SHADE_SOUL_CHANNEL); - events.ScheduleEvent(EVENT_SORCERER_CHANNEL, 2000); - } - else - { - me->InterruptSpell(CURRENT_CHANNELED_SPELL); - Shade->AI()->DoAction(ACTION_CHANNELER_DIED); - switchToCombat = true; - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); - } + me->SetFacingToObject(shade); + DoCastSelf(SPELL_SHADE_SOUL_CHANNEL); + sorcer_channel.Repeat(Seconds(2)); } - break; - default: - break; - } + else + { + me->InterruptSpell(CURRENT_CHANNELED_SPELL); + _switchToCombat = true; + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); + } + } + }); } + } - if (!startedBanishing) - { - Creature* Shade = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_SHADE_OF_AKAMA)); - if (me->IsWithinDist(Shade, 20.0f, false)) - { - me->StopMoving(); - me->GetMotionMaster()->Clear(false); - me->GetMotionMaster()->MovePoint(1, me->GetPositionX() + frand (-8.0f, 8.0f), me->GetPositionY() + frand (-8.0f, 8.0f), me->GetPositionZ(), false); - events.ScheduleEvent(EVENT_SORCERER_CHANNEL, 1500); - startedBanishing = true; - } - } + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + if (!UpdateVictim()) + return; DoMeleeAttackIfReady(); } private: - InstanceScript* instance; - EventMap events; - bool startedBanishing; - bool switchToCombat; + InstanceScript* _instance; + TaskScheduler _scheduler; + bool _switchToCombat; + bool _inBanish; }; CreatureAI* GetAI(Creature* creature) const override @@ -847,10 +823,6 @@ public: } }; -// ######################################################## -// Ashtongue Defender -// ######################################################## - class npc_ashtongue_defender : public CreatureScript { public: @@ -860,54 +832,55 @@ public: { npc_ashtongue_defenderAI(Creature* creature) : ScriptedAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_HEROIC_STRIKE, 5000); - events.ScheduleEvent(EVENT_SHIELD_BASH, urand(10000, 16000)); - events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, urand(10000, 16000)); - events.ScheduleEvent(EVENT_WINDFURY, urand(8000, 12000)); + _events.ScheduleEvent(EVENT_HEROIC_STRIKE, Seconds(5)); + _events.ScheduleEvent(EVENT_SHIELD_BASH, randtime(Seconds(10), Seconds(16))); + _events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, randtime(Seconds(10), Seconds(16))); + _events.ScheduleEvent(EVENT_WINDFURY, randtime(Seconds(8), Seconds(12))); } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_DEBILITATING_STRIKE: DoCastVictim(SPELL_DEBILITATING_STRIKE); - events.ScheduleEvent(EVENT_DEBILITATING_STRIKE, urand(8000, 16000)); + _events.Repeat(randtime(Seconds(20), Seconds(25))); break; case EVENT_HEROIC_STRIKE: - DoCast(me, SPELL_HEROIC_STRIKE); - events.ScheduleEvent(EVENT_HEROIC_STRIKE, urand(50000, 60000)); + DoCastSelf(SPELL_HEROIC_STRIKE); + _events.Repeat(randtime(Seconds(5), Seconds(15))); break; case EVENT_SHIELD_BASH: DoCastVictim(SPELL_SHIELD_BASH); - events.ScheduleEvent(EVENT_SHIELD_BASH, urand(8000, 16000)); + _events.Repeat(randtime(Seconds(10), Seconds(20))); break; case EVENT_WINDFURY: DoCastVictim(SPELL_WINDFURY); - events.ScheduleEvent(EVENT_WINDFURY, urand(6000 , 8000)); + _events.Repeat(randtime(Seconds(6), Seconds(8))); break; default: break; @@ -918,8 +891,8 @@ public: } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override @@ -928,10 +901,6 @@ public: } }; -// ######################################################## -// Ashtongue Rogue -// ######################################################## - class npc_ashtongue_rogue : public CreatureScript { public: @@ -941,44 +910,46 @@ public: { npc_ashtongue_rogueAI(Creature* creature) : ScriptedAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(500, 2000)); - events.ScheduleEvent(EVENT_EVISCERATE, urand(2000, 5000)); + _events.ScheduleEvent(EVENT_DEBILITATING_POISON, randtime(Milliseconds(500), Seconds(2))); + _events.ScheduleEvent(EVENT_EVISCERATE, randtime(Seconds(2), Seconds(5))); } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_DEBILITATING_POISON: DoCastVictim(SPELL_DEBILITATING_POISON); - events.ScheduleEvent(EVENT_DEBILITATING_POISON, urand(14000, 18000)); + _events.Repeat(randtime(Seconds(15), Seconds(20))); break; case EVENT_EVISCERATE: DoCastVictim(SPELL_EVISCERATE); - events.ScheduleEvent(EVENT_EVISCERATE, urand(12000, 16000)); + _events.Repeat(randtime(Seconds(12), Seconds(20))); break; default: break; @@ -989,8 +960,8 @@ public: } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override @@ -999,10 +970,6 @@ public: } }; -// ######################################################## -// Ashtongue Elementalist -// ######################################################## - class npc_ashtongue_elementalist : public CreatureScript { public: @@ -1012,44 +979,46 @@ public: { npc_ashtongue_elementalistAI(Creature* creature) : ScriptedAI(creature) { - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Reset() override { - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 18000); - events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 6000); + _events.ScheduleEvent(EVENT_RAIN_OF_FIRE, Seconds(18)); + _events.ScheduleEvent(EVENT_LIGHTNING_BOLT, Seconds(6)); } + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_RAIN_OF_FIRE: DoCastVictim(SPELL_RAIN_OF_FIRE); - events.ScheduleEvent(EVENT_RAIN_OF_FIRE, 20000); + _events.Repeat(randtime(Seconds(15), Seconds(20))); break; case EVENT_LIGHTNING_BOLT: DoCastVictim(SPELL_LIGHTNING_BOLT); - events.ScheduleEvent(EVENT_LIGHTNING_BOLT, 15000); + _events.Repeat(randtime(Seconds(8), Seconds(15))); break; default: break; @@ -1060,8 +1029,8 @@ public: } private: - InstanceScript* instance; - EventMap events; + InstanceScript* _instance; + EventMap _events; }; CreatureAI* GetAI(Creature* creature) const override @@ -1070,10 +1039,6 @@ public: } }; -// ######################################################## -// Ashtongue Spiritbinder -// ######################################################## - class npc_ashtongue_spiritbinder : public CreatureScript { public: @@ -1084,44 +1049,72 @@ public: npc_ashtongue_spiritbinderAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } void Initialize() { - spiritMend = false; - chainHeal = false; + _spiritMend = false; + _chainHeal = false; } void Reset() override { Initialize(); - if (Unit* target = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AKAMA_SHADE))) - AttackStart(target); + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + AttackStart(akama); } void JustDied(Unit* /*killer*/) override { - me->DespawnOrUnsummon(5000); + me->DespawnOrUnsummon(Seconds(5)); } void EnterCombat(Unit* /*who*/) override { - events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (5000, 6000)); + _events.ScheduleEvent(EVENT_SPIRIT_HEAL, randtime(Seconds(5), Seconds(6))); } + void DamageTaken(Unit* /*who*/, uint32& /*damage*/) override + { + if (!_spiritMend) + if (HealthBelowPct(30)) + { + DoCastSelf(SPELL_SPIRIT_MEND); + _spiritMend = true; + _events.ScheduleEvent(EVENT_SPIRIT_MEND_RESET, randtime(Seconds(10),Seconds(15))); + } + + if (!_chainHeal) + if (HealthBelowPct(50)) + { + DoCastSelf(SPELL_CHAIN_HEAL); + _chainHeal = true; + _events.ScheduleEvent(EVENT_CHAIN_HEAL_RESET, randtime(Seconds(10), Seconds(15))); + } + + } + + void EnterEvadeMode(EvadeReason /*why*/) override { } + void UpdateAI(uint32 diff) override { - events.Update(diff); + _events.Update(diff); - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = _events.ExecuteEvent()) { switch (eventId) { case EVENT_SPIRIT_HEAL: - DoCast(me, SPELL_SPIRITBINDER_SPIRIT_HEAL); - events.ScheduleEvent(EVENT_SPIRIT_HEAL, urand (13000, 16000)); + DoCastSelf(SPELL_SPIRITBINDER_SPIRIT_HEAL); + _events.Repeat(randtime(Seconds(13), Seconds(16))); + break; + case EVENT_SPIRIT_MEND_RESET: + _spiritMend = false; + break; + case EVENT_CHAIN_HEAL_RESET: + _chainHeal = false; break; default: break; @@ -1131,32 +1124,14 @@ public: if (!UpdateVictim()) return; - if (!spiritMend) - { - if (HealthBelowPct(25)) - { - DoCast(me, SPELL_SPIRIT_MEND); - spiritMend = true; - } - } - - if (!chainHeal) - { - if (HealthBelowPct(40)) - { - DoCast(me, SPELL_CHAIN_HEAL); - chainHeal = true; - } - } - DoMeleeAttackIfReady(); } private: - InstanceScript* instance; - EventMap events; - bool spiritMend; - bool chainHeal; + InstanceScript* _instance; + EventMap _events; + bool _spiritMend; + bool _chainHeal; }; CreatureAI* GetAI(Creature* creature) const override @@ -1165,6 +1140,119 @@ public: } }; +class npc_ashtongue_broken : public CreatureScript +{ +public: + npc_ashtongue_broken() : CreatureScript("npc_ashtongue_broken") { } + + struct npc_ashtongue_brokenAI : public ScriptedAI + { + npc_ashtongue_brokenAI(Creature* creature) : ScriptedAI(creature) + { + _instance = me->GetInstanceScript(); + } + + void MovementInform(uint32 motionType, uint32 /*pointId*/) override + { + if (motionType != POINT_MOTION_TYPE) + return; + + if (Creature* akama = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AKAMA_SHADE))) + me->SetFacingToObject(akama); + } + + void DoAction(int32 actionId) override + { + switch (actionId) + { + case ACTION_BROKEN_SPECIAL: + Talk(SAY_BROKEN_SPECIAL); + break; + case ACTION_BROKEN_HAIL: + me->setFaction(FACTION_FRIENDLY); + Talk(SAY_BROKEN_HAIL); + break; + case ACTION_BROKEN_EMOTE: + me->SetByteValue(UNIT_FIELD_BYTES_1, UNIT_BYTES_1_OFFSET_STAND_STATE, UNIT_STAND_STATE_KNEEL); + break; + default: + break; + } + } + + private: + InstanceScript* _instance; + }; + + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_ashtongue_brokenAI>(creature); + } +}; + +class spell_shade_soul_channel_serverside : public SpellScriptLoader +{ +public: + spell_shade_soul_channel_serverside() : SpellScriptLoader("spell_shade_soul_channel_serverside") { } + + class spell_shade_soul_channel_serverside_AuraScript : public AuraScript + { + PrepareAuraScript(spell_shade_soul_channel_serverside_AuraScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHADE_SOUL_CHANNEL_2)) + return false; + return true; + } + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + GetTarget()->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2); + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_shade_soul_channel_serverside_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_shade_soul_channel_serverside_AuraScript(); + } +}; + +class spell_shade_soul_channel : public SpellScriptLoader +{ +public: + spell_shade_soul_channel() : SpellScriptLoader("spell_shade_soul_channel") { } + + class spell_shade_soul_channel_AuraScript : public AuraScript + { + PrepareAuraScript(spell_shade_soul_channel_AuraScript); + + void OnApply(AuraEffect const* aurEff, AuraEffectHandleModes /*mode*/) + { + int32 const maxSlowEff = -99; + if (aurEff->GetAmount() < maxSlowEff) + if (AuraEffect* slowEff = GetEffect(EFFECT_0)) + slowEff->ChangeAmount(maxSlowEff); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_shade_soul_channel_AuraScript::OnApply, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL_OR_REAPPLY_MASK); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_shade_soul_channel_AuraScript(); + } +}; + void AddSC_boss_shade_of_akama() { new boss_shade_of_akama(); @@ -1176,4 +1264,7 @@ void AddSC_boss_shade_of_akama() new npc_ashtongue_rogue(); new npc_ashtongue_elementalist(); new npc_ashtongue_spiritbinder(); + new npc_ashtongue_broken(); + new spell_shade_soul_channel_serverside(); + new spell_shade_soul_channel(); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp index 72eb99bd7dc..56d333b2dda 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp @@ -91,7 +91,7 @@ public: void Despawn() { - me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); me->RemoveCorpse(); } @@ -218,7 +218,7 @@ public: { Creature* Teron = (ObjectAccessor::GetCreature((*me), TeronGUID)); if (!Teron || !Teron->IsAlive() || Teron->IsInEvadeMode()) - me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); CheckTeronTimer = 5000; } else CheckTeronTimer -= diff; @@ -236,9 +236,9 @@ public: return GetInstanceAI<boss_teron_gorefiendAI>(creature); } - struct boss_teron_gorefiendAI : public ScriptedAI + struct boss_teron_gorefiendAI : public BossAI { - boss_teron_gorefiendAI(Creature* creature) : ScriptedAI(creature) + boss_teron_gorefiendAI(Creature* creature) : BossAI(creature, DATA_TERON_GOREFIEND) { Initialize(); instance = creature->GetInstanceScript(); @@ -277,8 +277,7 @@ public: void Reset() override { - instance->SetBossState(DATA_TERON_GOREFIEND, NOT_STARTED); - + _Reset(); Initialize(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); @@ -316,9 +315,8 @@ public: void JustDied(Unit* /*killer*/) override { - instance->SetBossState(DATA_TERON_GOREFIEND, DONE); - Talk(SAY_DEATH); + _JustDied(); } float CalculateRandomLocation(float Loc, uint32 radius) @@ -363,7 +361,7 @@ public: /** WHAT IS FULLY NECESSARY FOR GOREFIEND TO BE 100% COMPLETE *****/ /************************************************************************/ - Unit* ghost = NULL; + Unit* ghost = nullptr; if (GhostGUID) ghost = ObjectAccessor::GetUnit(*me, GhostGUID); if (ghost && ghost->IsAlive() && ghost->HasAura(SPELL_SHADOW_OF_DEATH)) @@ -379,7 +377,7 @@ public: }*/ for (uint8 i = 0; i < 4; ++i) { - Creature* Construct = NULL; + Creature* Construct = nullptr; float X = CalculateRandomLocation(ghost->GetPositionX(), 10); float Y = CalculateRandomLocation(ghost->GetPositionY(), 10); Construct = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, Y, ghost->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); @@ -435,17 +433,13 @@ public: for (uint8 i = 0; i < 2; ++i) { - Creature* Shadow = NULL; float X = CalculateRandomLocation(me->GetPositionX(), 10); - Shadow = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 0); - if (Shadow) + if (Creature* shadow = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 0)) { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1); - if (!target) - target = me->GetVictim(); - - if (target) - Shadow->AI()->AttackStart(target); + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) + shadow->AI()->AttackStart(target); + else if (Unit* victim = me->GetVictim()) + shadow->AI()->AttackStart(victim); } } SummonShadowsTimer = 60000; diff --git a/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp b/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp index 9b33ea88ca7..b9627856ae6 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_warlord_najentus.cpp @@ -150,7 +150,7 @@ public: DoCast(target, SPELL_IMPALING_SPINE, true); SpineTargetGUID = target->GetGUID(); //must let target summon, otherwise you cannot click the spine - target->SummonGameObject(GO_NAJENTUS_SPINE, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), me->GetOrientation(), 0, 0, 0, 0, 30); + target->SummonGameObject(GO_NAJENTUS_SPINE, *target, G3D::Quat(), 30); Talk(SAY_NEEDLE); events.DelayEvents(1500, GCD_CAST); events.DelayEvents(15000, GCD_YELL); diff --git a/src/server/scripts/Outland/BlackTemple/illidari_council.cpp b/src/server/scripts/Outland/BlackTemple/illidari_council.cpp index d883a48b630..06b492e32d7 100644 --- a/src/server/scripts/Outland/BlackTemple/illidari_council.cpp +++ b/src/server/scripts/Outland/BlackTemple/illidari_council.cpp @@ -90,8 +90,6 @@ enum IllidariCouncil SPELL_BERSERK = 45078 }; -#define ERROR_INST_DATA "SD2 ERROR: Instance Data for Black Temple not set properly; Illidari Council event will not function properly." - struct CouncilYells { int32 entry; @@ -165,7 +163,7 @@ public: Council[1] = instance->GetGuidData(DATA_VERAS_DARKSHADOW); Council[2] = instance->GetGuidData(DATA_LADY_MALANDE); Council[3] = instance->GetGuidData(DATA_HIGH_NETHERMANCER_ZEREVOR); - } else TC_LOG_ERROR("scripts", ERROR_INST_DATA); + } } void EnterCombat(Unit* /*who*/) override { } @@ -231,6 +229,7 @@ public: { Initialize(); instance = creature->GetInstanceScript(); + SetBoundary(instance->GetBossBoundary(DATA_ILLIDARI_COUNCIL)); } void Initialize() @@ -257,7 +256,7 @@ public: { Initialize(); - Creature* pMember = NULL; + Creature* pMember = nullptr; for (uint8 i = 0; i < 4; ++i) { pMember = ObjectAccessor::GetCreature((*me), Council[i]); @@ -330,16 +329,16 @@ public: if (DeathCount > 3) { if (Creature* VoiceTrigger = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_BLOOD_ELF_COUNCIL_VOICE))) - VoiceTrigger->DealDamage(VoiceTrigger, VoiceTrigger->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + VoiceTrigger->DealDamage(VoiceTrigger, VoiceTrigger->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); instance->SetBossState(DATA_ILLIDARI_COUNCIL, DONE); //me->SummonCreature(AKAMAID, 746.466980f, 304.394989f, 311.90208f, 6.272870f, TEMPSUMMON_DEAD_DESPAWN, 0); - me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); return; } Creature* pMember = (ObjectAccessor::GetCreature(*me, Council[DeathCount])); if (pMember && pMember->IsAlive()) - pMember->DealDamage(pMember, pMember->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); + pMember->DealDamage(pMember, pMember->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); ++DeathCount; EndEventTimer = 1500; } else EndEventTimer -= diff; @@ -922,7 +921,7 @@ public: if (dmgInfo.GetAttacker() == target) return; int32 bp = absorbAmount / 2; - target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_T, &bp, NULL, NULL, true, NULL, aurEff); + target->CastCustomSpell(dmgInfo.GetAttacker(), SPELL_REFLECTIVE_SHIELD_T, &bp, nullptr, nullptr, true, nullptr, aurEff); } void Register() override diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp index 86ad7958957..bac996918ac 100644 --- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp @@ -35,6 +35,20 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM } // END }; +BossBoundaryData const boundaries = +{ + { DATA_HIGH_WARLORD_NAJENTUS, new RectangleBoundary(394.0f, 479.4f, 707.8f, 859.1f) }, + { DATA_SUPREMUS, new RectangleBoundary(556.1f, 850.2f, 542.0f, 1001.0f) }, + { DATA_SHADE_OF_AKAMA, new RectangleBoundary(406.8f, 564.0f, 327.9f, 473.5f) }, + { DATA_TERON_GOREFIEND, new RectangleBoundary(512.5f, 613.3f, 373.2f, 432.0f) }, + { DATA_TERON_GOREFIEND, new ZRangeBoundary(179.5f, 223.6f) }, + { DATA_GURTOGG_BLOODBOIL, new RectangleBoundary(720.5f, 864.5f, 159.3f, 316.0f) }, + { DATA_RELIQUARY_OF_SOULS, new RectangleBoundary(435.9f, 558.8f, 113.3f, 229.6f) }, + { DATA_MOTHER_SHAHRAZ, new RectangleBoundary(903.4f, 982.1f, 92.4f, 476.7f) }, + { DATA_ILLIDARI_COUNCIL, new EllipseBoundary(Position(696.6f, 305.0f), 70.0 , 85.0) }, + { DATA_ILLIDAN_STORMRAGE, new EllipseBoundary(Position(694.8f, 309.0f), 70.0 , 85.0) } +}; + class instance_black_temple : public InstanceMapScript { public: @@ -47,6 +61,7 @@ class instance_black_temple : public InstanceMapScript SetHeaders(DataHeader); SetBossNumber(EncounterCount); LoadDoorData(doorData); + LoadBossBoundaries(boundaries); } void OnCreatureCreate(Creature* creature) override diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp index 89b7a8cbd46..62552a3bf61 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp @@ -73,6 +73,7 @@ enum Spells SPELL_SLIPPERY_FLOOR_AMBIENT = 46314, SPELL_SLIPPERY_FLOOR_PERIODIC = 46320, SPELL_SLIPPERY_FLOOR_SLIP = 45947, + SPELL_SLIPPERY_FLOOR_YOU_SLIPPED = 45946, // Frozen Core SPELL_SUICIDE = 45254, @@ -101,19 +102,18 @@ enum Events EVENT_EMERGE = 1, EVENT_INITIAL_EMERGE = 2, EVENT_SYNCH_HEALTH = 3, - EVENT_FOUND_OPENING = 4, - EVENT_LOOKFOROPENING_0 = 5, - EVENT_LOOKFOROPENING_1 = 6, - EVENT_LOOKFOROPENING_2 = 7, - EVENT_SUMMON_HAILSTONE = 8, - EVENT_SUMMON_COLDWEAVE = 9, - EVENT_SUMMON_FROSTWIND = 10, - EVENT_SUMMON_AHUNE = 11, - EVENT_CLOSE_OPENING = 12, - EVENT_AHUNE_PHASE_ONE = 13, - EVENT_AHUNE_PHASE_TWO = 14, - EVENT_START_LOOKING_FOR_OPENING = 15, - EVENT_STOP_LOOKING_FOR_OPENING = 16 + EVENT_LOOKFOROPENING_0 = 4, + EVENT_LOOKFOROPENING_1 = 5, + EVENT_LOOKFOROPENING_2 = 6, + EVENT_SUMMON_HAILSTONE = 7, + EVENT_SUMMON_COLDWEAVE = 8, + EVENT_SUMMON_FROSTWIND = 9, + EVENT_SUMMON_AHUNE = 10, + EVENT_CLOSE_OPENING = 11, + EVENT_AHUNE_PHASE_ONE = 12, + EVENT_AHUNE_PHASE_TWO = 13, + EVENT_START_LOOKING_FOR_OPENING = 14, + EVENT_STOP_LOOKING_FOR_OPENING = 15 }; enum Actions @@ -169,19 +169,14 @@ public: { boss_ahuneAI(Creature* creature) : BossAI(creature, DATA_AHUNE) { - Initialize(); - } - - void Initialize() - { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetControlled(true, UNIT_STATE_ROOT); } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - events.ScheduleEvent(EVENT_INITIAL_EMERGE, 4); - events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000); + events.ScheduleEvent(EVENT_INITIAL_EMERGE, Milliseconds(4)); + events.ScheduleEvent(EVENT_SYNCH_HEALTH, Seconds(3)); } void EnterEvadeMode(EvadeReason /*why*/) override @@ -194,7 +189,6 @@ public: void JustDied(Unit* /*killer*/) override { - _JustDied(); instance->DoCastSpellOnPlayers(SPELL_AHUNE_ACHIEVEMENT); if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AHUNE_BUNNY))) @@ -209,11 +203,8 @@ public: if (group->isLFGGroup()) sLFGMgr->FinishDungeon(group->GetGUID(), 286); } - } - void JustSummoned(Creature* summon) override - { - BossAI::JustSummoned(summon); + _JustDied(); } void DoAction(int32 action) override @@ -221,7 +212,7 @@ public: if (action == ACTION_AHUNE_RETREAT) { Submerge(); - events.ScheduleEvent(EVENT_EMERGE, 35000); + events.ScheduleEvent(EVENT_EMERGE, Seconds(35)); } } @@ -246,10 +237,10 @@ public: break; case EVENT_SYNCH_HEALTH: if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE))) - DoCast(frozenCore, SPELL_SYNCH_HEALTH); + DoCast(frozenCore, SPELL_SYNCH_HEALTH, true); else DoCast(me, SPELL_SUICIDE); - events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000); + events.Repeat(Seconds(3)); break; default: break; @@ -268,8 +259,8 @@ public: me->RemoveAurasDueToSpell(SPELL_STAY_SUBMERGED); DoCast(me, SPELL_STAND); DoCast(me, SPELL_RESURFACE, true); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + events.ScheduleEvent(EVENT_SYNCH_HEALTH, Seconds(3)); } void Submerge() @@ -277,7 +268,7 @@ public: if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE))) frozenCore->AI()->DoAction(ACTION_AHUNE_RETREAT); me->RemoveAurasDueToSpell(SPELL_AHUNES_SHIELD); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_31); DoCast(me, SPELL_SUBMERGED, true); DoCast(me, SPELL_AHUNE_SELF_STUN, true); DoCast(me, SPELL_STAY_SUBMERGED, true); @@ -308,18 +299,11 @@ public: void Initialize() { me->SetReactState(REACT_PASSIVE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->setRegeneratingHealth(false); DoCast(me, SPELL_FROZEN_CORE_GETS_HIT); DoCast(me, SPELL_ICE_SPEAR_AURA); } - void EnterEvadeMode(EvadeReason /*why*/) override - { - DoCast(SPELL_MINION_DESPAWNER); - if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE))) - ahune->AI()->EnterEvadeMode(); - } - void JustDied(Unit* /*killer*/) override { if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE))) @@ -333,23 +317,20 @@ public: { if (action == ACTION_AHUNE_RETREAT) { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); me->RemoveAurasDueToSpell(SPELL_ICE_SPEAR_AURA); - _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000, 0, PHASE_TWO); + _events.ScheduleEvent(EVENT_SYNCH_HEALTH, Seconds(3), 0, PHASE_TWO); } else if (action == ACTION_AHUNE_RESURFACE) { _events.Reset(); DoCast(me, SPELL_ICE_SPEAR_AURA); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC); } } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) - return; - _events.Update(diff); while (uint32 eventId = _events.ExecuteEvent()) @@ -358,10 +339,10 @@ public: { case EVENT_SYNCH_HEALTH: if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE))) - DoCast(ahune, SPELL_SYNCH_HEALTH); + DoCast(ahune, SPELL_SYNCH_HEALTH, true); else DoCast(me, SPELL_SUICIDE); - _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000); + _events.Repeat(Seconds(3)); break; default: break; @@ -414,7 +395,7 @@ public: _summons.DespawnAll(); ResetFlameCallers(); - me->SummonGameObject(GO_ICE_STONE, -69.90455f, -162.2449f, -2.366563f, 2.426008f, 0.0f, 0.0f, 0.9366722f, 0.3502074f, 0); + me->SummonGameObject(GO_ICE_STONE, -69.90455f, -162.2449f, -2.366563f, 2.426008f, G3D::Quat(0.0f, 0.0f, 0.9366722f, 0.3502074f), 0); } void DoAction(int32 action) override @@ -435,11 +416,11 @@ public: _submerged = false; _events.Reset(); _events.SetPhase(PHASE_ONE); - _events.ScheduleEvent(EVENT_SUMMON_AHUNE, 10000); - _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, 14000, 0, PHASE_ONE); - _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 22000, 0, PHASE_ONE); - _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, 14000, 0, PHASE_ONE); - _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, 108000, 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_SUMMON_AHUNE, Seconds(10)); + _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, Seconds(14), 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, Seconds(22), 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, Seconds(14), 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, Seconds(108), 0, PHASE_ONE); } } @@ -466,9 +447,9 @@ public: case EVENT_SUMMON_COLDWEAVE: DoCast(SPELL_SUMMON_COLDWEAVE); DoCast(SPELL_SUMMON_COLDWEAVE); - _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 8000, 0, PHASE_ONE); + _events.Repeat(Seconds(8)); if (_submerged) - _events.ScheduleEvent(EVENT_SUMMON_FROSTWIND, 4000, 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_SUMMON_FROSTWIND, Seconds(4), 0, PHASE_ONE); break; case EVENT_SUMMON_FROSTWIND: DoCast(SPELL_SUMMON_FROSTWIND); @@ -494,17 +475,17 @@ public: ahune->AI()->DoAction(ACTION_AHUNE_RETREAT); _events.Reset(); _events.SetPhase(PHASE_TWO); - _events.ScheduleEvent(EVENT_CLOSE_OPENING, 25000, 0, PHASE_TWO); - _events.ScheduleEvent(EVENT_AHUNE_PHASE_ONE, 35000, 0, PHASE_TWO); + _events.ScheduleEvent(EVENT_CLOSE_OPENING, Seconds(25), 0, PHASE_TWO); + _events.ScheduleEvent(EVENT_AHUNE_PHASE_ONE, Seconds(35), 0, PHASE_TWO); break; case EVENT_AHUNE_PHASE_ONE: _submerged = true; _events.Reset(); _events.SetPhase(PHASE_ONE); - _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 8000, 0, PHASE_ONE); - _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, 5000, 0, PHASE_ONE); - _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, 5000, 0, PHASE_ONE); - _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, 100000, 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, Seconds(8), 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, Seconds(5), 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, Seconds(5), 0, PHASE_ONE); + _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, Seconds(100), 0, PHASE_ONE); break; default: break; @@ -581,10 +562,10 @@ public: switch (spellInfo->Id) { case SPELL_SHAMANS_LOOK_FOR_OPENING: - _events.ScheduleEvent(EVENT_LOOKFOROPENING_0, 17000); + _events.ScheduleEvent(EVENT_LOOKFOROPENING_0, Seconds(17)); break; case SPELL_FOUND_OPENING: - _events.ScheduleEvent(EVENT_FOUND_OPENING, 0); + Talk(EMOTE_RETREAT); break; default: break; @@ -607,21 +588,19 @@ public: { case EVENT_LOOKFOROPENING_0: LookOpening(true, 0); - _events.ScheduleEvent(EVENT_LOOKFOROPENING_1, 26000); + _events.ScheduleEvent(EVENT_LOOKFOROPENING_1, Seconds(26)); break; case EVENT_LOOKFOROPENING_1: LookOpening(true, 1); - _events.ScheduleEvent(EVENT_LOOKFOROPENING_2, 25000); + _events.ScheduleEvent(EVENT_LOOKFOROPENING_2, Seconds(25)); break; case EVENT_LOOKFOROPENING_2: LookOpening(true, 2); - _events.ScheduleEvent(EVENT_STOP_LOOKING_FOR_OPENING, 27000); + _events.ScheduleEvent(EVENT_STOP_LOOKING_FOR_OPENING, Seconds(27)); break; case EVENT_STOP_LOOKING_FOR_OPENING: LookOpening(false, _mySpot); break; - case EVENT_FOUND_OPENING: - Talk(EMOTE_RETREAT); default: break; } @@ -799,25 +778,26 @@ public: void PeriodicTick(AuraEffect const* aurEff) { - if (Creature* caster = GetCaster()->ToCreature()) - switch (aurEff->GetTickNumber()) - { - case 1: - caster->CastSpell(caster, SPELL_SUMMON_ICE_SPEAR_GO); - break; - case 3: - if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f)) - spike->UseDoorOrButton(); - caster->AI()->DoCastAOE(SPELL_ICE_SPEAR_KNOCKBACK, true); - break; - case 5: - if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f)) - spike->Delete(); - caster->DespawnOrUnsummon(); - break; - default: - break; - } + if (Unit* tmpCaster = GetCaster()) + if (Creature* caster = tmpCaster->ToCreature()) + switch (aurEff->GetTickNumber()) + { + case 1: + caster->CastSpell(caster, SPELL_SUMMON_ICE_SPEAR_GO); + break; + case 3: + if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f)) + spike->UseDoorOrButton(); + caster->AI()->DoCastAOE(SPELL_ICE_SPEAR_KNOCKBACK, true); + break; + case 5: + if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f)) + spike->Delete(); + caster->DespawnOrUnsummon(); + break; + default: + break; + } } void Register() override @@ -884,6 +864,16 @@ public: return true; } + void FilterTargets(std::list<WorldObject*>& targets) + { + if (targets.empty()) + return; + + WorldObject* target = Trinity::Containers::SelectRandomContainerElement(targets); + targets.clear(); + targets.push_back(target); + } + void HandleDummy(SpellEffIndex /*effIndex*/) { GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ICE_SPEAR_BUNNY, true); @@ -891,6 +881,7 @@ public: void Register() override { + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_ice_spear_target_picker_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY); OnEffectHitTarget += SpellEffectFn(spell_ice_spear_target_picker_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } }; @@ -922,7 +913,10 @@ public: { if (Unit* target = GetHitUnit()) if (target->isMoving()) + { target->CastSpell(target, SPELL_SLIPPERY_FLOOR_SLIP, true); + target->CastSpell(target, SPELL_SLIPPERY_FLOOR_YOU_SLIPPED, true); + } } void Register() override diff --git a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp index cab5b9efbc3..f4ff9f7084c 100644 --- a/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp +++ b/src/server/scripts/Outland/TempestKeep/arcatraz/arcatraz.cpp @@ -537,7 +537,7 @@ class npc_zerekethvoidzone : public CreatureScript void Reset() override { - me->SetUInt32Value(UNIT_NPC_FLAGS, 0); + me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE); me->setFaction(16); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); diff --git a/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp b/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp index e64c0fe9f5e..b9e7fb0034d 100644 --- a/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp +++ b/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp @@ -103,7 +103,7 @@ class npc_warp_splinter_treant : public CreatureScript { if (Unit* Warp = ObjectAccessor::GetUnit(*me, WarpGuid)) { - if (me->IsWithinMeleeRange(Warp, 2.5f)) + if (me->IsWithinMeleeRange(Warp)) { int32 CurrentHP_Treant = (int32)me->GetHealth(); Warp->CastCustomSpell(Warp, SPELL_HEAL_FATHER, &CurrentHP_Treant, 0, 0, true, 0, 0, me->GetGUID()); diff --git a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp index 30bf085be43..9074f1ea373 100644 --- a/src/server/scripts/Outland/zone_blades_edge_mountains.cpp +++ b/src/server/scripts/Outland/zone_blades_edge_mountains.cpp @@ -959,7 +959,7 @@ public: { // Spell 37392 does not exist in dbc, manually spawning me->SummonCreature(NPC_OSCILLATING_FREQUENCY_SCANNER_TOP_BUNNY, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ() + 0.5f, me->GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 50000); - me->SummonGameObject(GO_OSCILLATING_FREQUENCY_SCANNER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), 0, 0, 0, 0, 50); + me->SummonGameObject(GO_OSCILLATING_FREQUENCY_SCANNER, *me, G3D::Quat(), 50); me->DespawnOrUnsummon(50000); } diff --git a/src/server/scripts/Outland/zone_netherstorm.cpp b/src/server/scripts/Outland/zone_netherstorm.cpp index d35dedb5d27..8803ce9d9ed 100644 --- a/src/server/scripts/Outland/zone_netherstorm.cpp +++ b/src/server/scripts/Outland/zone_netherstorm.cpp @@ -19,7 +19,7 @@ /* ScriptData SDName: Netherstorm SD%Complete: 80 -SDComment: Quest support: 10337, 10438, 10652 (special flight paths), 10198, 10191 +SDComment: Quest support: 10337, 10652 (special flight paths), 10198, 10191 SDCategory: Netherstorm EndScriptData */ @@ -335,56 +335,6 @@ public: }; /*###### -## npc_professor_dabiri -######*/ -enum ProfessorDabiriData -{ - SPELL_PHASE_DISTRUPTOR = 35780, - - //WHISPER_DABIRI = 0, not existing in database - - QUEST_DIMENSIUS = 10439, - QUEST_ON_NETHERY_WINGS = 10438, -}; - -#define GOSSIP_ITEM "I need a new phase distruptor, Professor" - -class npc_professor_dabiri : public CreatureScript -{ -public: - npc_professor_dabiri() : CreatureScript("npc_professor_dabiri") { } - - //OnQuestAccept: - //if (quest->GetQuestId() == QUEST_DIMENSIUS) - //creature->AI()->Talk(WHISPER_DABIRI, player); - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF+1) - { - creature->CastSpell(player, SPELL_PHASE_DISTRUPTOR, false); - player->CLOSE_GOSSIP_MENU(); - } - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(QUEST_ON_NETHERY_WINGS) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(29778)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - -/*###### ## npc_phase_hunter ######*/ @@ -752,7 +702,6 @@ void AddSC_netherstorm() { new npc_commander_dawnforge(); new at_commander_dawnforge(); - new npc_professor_dabiri(); new npc_phase_hunter(); new npc_bessy(); new npc_maxx_a_million_escort(); diff --git a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp index 7ea87a3c0c4..c91e5c94ca0 100644 --- a/src/server/scripts/Outland/zone_shadowmoon_valley.cpp +++ b/src/server/scripts/Outland/zone_shadowmoon_valley.cpp @@ -19,7 +19,7 @@ /* ScriptData SDName: Shadowmoon_Valley SD%Complete: 100 -SDComment: Quest support: 10583, 10601, 10804, 10854, 10458, 10481, 10480, 10781, 10451. Vendor Drake Dealer Hurlunk. +SDComment: Quest support: 10804, 10854, 10458, 10481, 10480, 10781, 10451. SDCategory: Shadowmoon Valley EndScriptData */ @@ -28,9 +28,6 @@ npc_invis_infernal_caster npc_infernal_attacker npc_mature_netherwing_drake npc_enslaved_netherwing_drake -npc_drake_dealer_hurlunk -npcs_flanis_swiftwing_and_kagrosh -npc_karynaku npc_earthmender_wilda npc_torloth_the_magnificent npc_illidari_spawn @@ -90,8 +87,10 @@ public: void SummonInfernal() { - Creature* infernal = me->SummonCreature(NPC_INFERNAL_ATTACKER, me->GetPositionX(), me->GetPositionY(), ground + 0.05f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000); - infernalGUID = infernal->GetGUID(); + if (Creature* infernal = me->SummonCreature(NPC_INFERNAL_ATTACKER, me->GetPositionX(), me->GetPositionY(), ground + 0.05f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000)) + infernalGUID = infernal->GetGUID(); + else + infernalGUID = ObjectGuid::Empty; } void UpdateAI(uint32 diff) override @@ -568,117 +567,6 @@ public: }; }; -/*###### -## npc_drake_dealer_hurlunk -######*/ - -class npc_drake_dealer_hurlunk : public CreatureScript -{ -public: - npc_drake_dealer_hurlunk() : CreatureScript("npc_drake_dealer_hurlunk") { } - - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_TRADE) - player->GetSession()->SendListInventory(creature->GetGUID()); - - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsVendor() && player->GetReputationRank(1015) == REP_EXALTED) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - -/*###### -## npc_flanis_swiftwing_and_kagrosh -######*/ - -#define GOSSIP_HSK1 "Take Flanis's Pack" -#define GOSSIP_HSK2 "Take Kagrosh's Pack" - -class npcs_flanis_swiftwing_and_kagrosh : public CreatureScript -{ -public: - npcs_flanis_swiftwing_and_kagrosh() : CreatureScript("npcs_flanis_swiftwing_and_kagrosh") { } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF+1) - { - ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 30658, 1, NULL); - if (msg == EQUIP_ERR_OK) - { - player->StoreNewItem(dest, 30658, true); - player->PlayerTalkClass->ClearMenus(); - } - } - if (action == GOSSIP_ACTION_INFO_DEF+2) - { - ItemPosCountVec dest; - uint8 msg = player->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 30659, 1, NULL); - if (msg == EQUIP_ERR_OK) - { - player->StoreNewItem(dest, 30659, true); - player->PlayerTalkClass->ClearMenus(); - } - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetQuestStatus(10583) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30658, 1, true)) - player->ADD_GOSSIP_ITEM(0, GOSSIP_HSK1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - if (player->GetQuestStatus(10601) == QUEST_STATUS_INCOMPLETE && !player->HasItemCount(30659, 1, true)) - player->ADD_GOSSIP_ITEM(0, GOSSIP_HSK2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - -/*###### -# npc_karynaku -####*/ - -enum Karynaku -{ - QUEST_ALLY_OF_NETHER = 10870, - QUEST_ZUHULED_THE_WACK = 10866, - - NPC_ZUHULED_THE_WACKED = 11980, - - TAXI_PATH_ID = 649, -}; - -class npc_karynaku : public CreatureScript -{ - public: - npc_karynaku() : CreatureScript("npc_karynaku") { } - - bool OnQuestAccept(Player* player, Creature* creature, Quest const* quest) override - { - if (quest->GetQuestId() == QUEST_ALLY_OF_NETHER) - player->ActivateTaxiPathTo(TAXI_PATH_ID); - - if (quest->GetQuestId() == QUEST_ZUHULED_THE_WACK) - creature->SummonCreature(NPC_ZUHULED_THE_WACKED, -4204.94f, 316.397f, 122.508f, 1.309f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300000); - - return true; - } -}; - /*#### # npc_earthmender_wilda ####*/ @@ -1701,9 +1589,6 @@ void AddSC_shadowmoon_valley() new npc_mature_netherwing_drake(); new npc_enslaved_netherwing_drake(); new npc_dragonmaw_peon(); - new npc_drake_dealer_hurlunk(); - new npcs_flanis_swiftwing_and_kagrosh(); - new npc_karynaku(); new npc_earthmender_wilda(); new npc_lord_illidan_stormrage(); new go_crystal_prison(); diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 724019a1b19..980c0db19cc 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -30,6 +30,13 @@ enum DeathKnightSpells { + SPELL_DK_ACCLIMATION_HOLY = 50490, + SPELL_DK_ACCLIMATION_FIRE = 50362, + SPELL_DK_ACCLIMATION_FROST = 50485, + SPELL_DK_ACCLIMATION_ARCANE = 50486, + SPELL_DK_ACCLIMATION_SHADOW = 50489, + SPELL_DK_ACCLIMATION_NATURE = 50488, + SPELL_DK_ADVANTAGE_T10_4P_MELEE = 70657, SPELL_DK_ANTI_MAGIC_SHELL_TALENT = 51052, SPELL_DK_BLACK_ICE_R1 = 49140, SPELL_DK_BLOOD_BOIL_TRIGGERED = 65658, @@ -51,6 +58,7 @@ enum DeathKnightSpells SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1 = 50365, SPELL_DK_IMPROVED_FROST_PRESENCE_R1 = 50384, SPELL_DK_IMPROVED_UNHOLY_PRESENCE_R1 = 50391, + SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL = 50475, SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED = 63611, SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED = 63622, SPELL_DK_ITEM_SIGIL_VENGEFUL_HEART = 64962, @@ -78,6 +86,141 @@ enum Misc NPC_DK_GHOUL = 26125 }; +// -49200 - Acclimation +class spell_dk_acclimation : public SpellScriptLoader +{ +public: + spell_dk_acclimation() : SpellScriptLoader("spell_dk_acclimation") { } + + class spell_dk_acclimation_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_acclimation_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_HOLY) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_FIRE) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_FROST) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_NATURE) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_SHADOW) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_ARCANE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetDamageInfo()) + { + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + case SPELL_SCHOOL_FIRE: + case SPELL_SCHOOL_NATURE: + case SPELL_SCHOOL_FROST: + case SPELL_SCHOOL_SHADOW: + case SPELL_SCHOOL_ARCANE: + return true; + default: + break; + } + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerspell = 0; + + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + triggerspell = SPELL_DK_ACCLIMATION_HOLY; + break; + case SPELL_SCHOOL_FIRE: + triggerspell = SPELL_DK_ACCLIMATION_FIRE; + break; + case SPELL_SCHOOL_NATURE: + triggerspell = SPELL_DK_ACCLIMATION_NATURE; + break; + case SPELL_SCHOOL_FROST: + triggerspell = SPELL_DK_ACCLIMATION_FROST; + break; + case SPELL_SCHOOL_SHADOW: + triggerspell = SPELL_DK_ACCLIMATION_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + triggerspell = SPELL_DK_ACCLIMATION_ARCANE; + break; + default: + return; + } + + if (Unit* target = eventInfo.GetActionTarget()) + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_acclimation_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dk_acclimation_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_acclimation_AuraScript(); + } +}; + +// 70656 - Advantage (T10 4P Melee Bonus) +class spell_dk_advantage_t10_4p : public SpellScriptLoader +{ +public: + spell_dk_advantage_t10_4p() : SpellScriptLoader("spell_dk_advantage_t10_4p") { } + + class spell_dk_advantage_t10_4p_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_advantage_t10_4p_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_ADVANTAGE_T10_4P_MELEE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (Unit* caster = eventInfo.GetActor()) + { + if (caster->GetTypeId() != TYPEID_PLAYER || caster->getClass() != CLASS_DEATH_KNIGHT) + return false; + + for (uint8 i = 0; i < MAX_RUNES; ++i) + if (caster->ToPlayer()->GetRuneCooldown(i) == 0) + return false; + + return true; + } + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_advantage_t10_4p_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_advantage_t10_4p_AuraScript(); + } +}; + // 50462 - Anti-Magic Shell (on raid member) class spell_dk_anti_magic_shell_raid : public SpellScriptLoader { @@ -919,6 +1062,52 @@ class spell_dk_improved_blood_presence : public SpellScriptLoader } }; +// 63611 - Improved Blood Presence Triggered +class spell_dk_improved_blood_presence_triggered : public SpellScriptLoader +{ +public: + spell_dk_improved_blood_presence_triggered() : SpellScriptLoader("spell_dk_improved_blood_presence_triggered") { } + + class spell_dk_improved_blood_presence_triggered_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_improved_blood_presence_triggered_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActor()->GetTypeId() == TYPEID_PLAYER) + return true; + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + eventInfo.GetActor()->CastCustomSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL, SPELLVALUE_BASE_POINT0, CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()), + eventInfo.GetActor(), true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_improved_blood_presence_triggered_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dk_improved_blood_presence_triggered_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_improved_blood_presence_triggered_AuraScript(); + } +}; + // -50384 - Improved Frost Presence class spell_dk_improved_frost_presence : public SpellScriptLoader { @@ -2006,6 +2195,8 @@ public: void AddSC_deathknight_spell_scripts() { + new spell_dk_acclimation(); + new spell_dk_advantage_t10_4p(); new spell_dk_anti_magic_shell_raid(); new spell_dk_anti_magic_shell_self(); new spell_dk_anti_magic_zone(); @@ -2022,6 +2213,7 @@ void AddSC_deathknight_spell_scripts() new spell_dk_ghoul_explode(); new spell_dk_icebound_fortitude(); new spell_dk_improved_blood_presence(); + new spell_dk_improved_blood_presence_triggered(); new spell_dk_improved_frost_presence(); new spell_dk_improved_unholy_presence(); new spell_dk_pestilence(); diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index c088ae07e66..0bf5ab01f45 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -31,6 +31,13 @@ enum DruidSpells { SPELL_DRUID_BEAR_FORM_PASSIVE = 1178, SPELL_DRUID_DIRE_BEAR_FORM_PASSIVE = 9635, + SPELL_DRUID_ECLIPSE_LUNAR_PROC = 48518, + SPELL_DRUID_ECLIPSE_SOLAR_PROC = 48517, + SPELL_DRUID_FORMS_TRINKET_BEAR = 37340, + SPELL_DRUID_FORMS_TRINKET_CAT = 37341, + SPELL_DRUID_FORMS_TRINKET_MOONKIN = 37343, + SPELL_DRUID_FORMS_TRINKET_NONE = 37344, + SPELL_DRUID_FORMS_TRINKET_TREE = 37342, SPELL_DRUID_ENRAGE = 5229, SPELL_DRUID_ENRAGE_MOD_DAMAGE = 51185, SPELL_DRUID_ENRAGED_DEFENSE = 70725, @@ -48,6 +55,8 @@ enum DruidSpells SPELL_DRUID_NATURES_SPLENDOR = 57865, SPELL_DRUID_SURVIVAL_INSTINCTS = 50322, SPELL_DRUID_SAVAGE_ROAR = 62071, + SPELL_DRUID_T9_FERAL_RELIC_BEAR = 67354, + SPELL_DRUID_T9_FERAL_RELIC_CAT = 67355, SPELL_DRUID_TIGER_S_FURY_ENERGIZE = 51178 }; @@ -131,6 +140,69 @@ class spell_dru_dash : public SpellScriptLoader } }; +class spell_dru_eclipse : public SpellScriptLoader +{ +public: + spell_dru_eclipse() : SpellScriptLoader("spell_dru_eclipse") { } + + class spell_dru_eclipse_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_eclipse_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_LUNAR_PROC)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (!eventInfo.GetSpellInfo()) + return false; + + if (eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_LUNAR_PROC) || eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) + return false; + + // Triggered by Wrath? + if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1) + return roll_chance_f(GetSpellInfo()->ProcChance * 0.6f) && _lunarProcCooldownEnd <= std::chrono::steady_clock::now(); + + return _solarProcCooldownEnd <= std::chrono::steady_clock::now(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1) + { + _lunarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount()); + eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_LUNAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff); + } + else + { + _solarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount()); + eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_SOLAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff); + } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_eclipse_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_eclipse_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + + std::chrono::steady_clock::time_point _lunarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); + std::chrono::steady_clock::time_point _solarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_eclipse_AuraScript(); + } +}; + // 5229 - Enrage class spell_dru_enrage : public SpellScriptLoader { @@ -197,6 +269,91 @@ class spell_dru_enrage : public SpellScriptLoader } }; +// 37336 - Druid Forms Trinket +class spell_dru_forms_trinket : public SpellScriptLoader +{ +public: + spell_dru_forms_trinket() : SpellScriptLoader("spell_dru_forms_trinket") { } + + class spell_dru_forms_trinket_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_forms_trinket_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_BEAR) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_CAT) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_MOONKIN) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_NONE) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_TREE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + Unit* target = eventInfo.GetActor(); + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + case FORM_CAT: + case FORM_MOONKIN: + case FORM_NONE: + case FORM_TREE: + return true; + default: + break; + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* target = eventInfo.GetActor(); + uint32 triggerspell = 0; + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + triggerspell = SPELL_DRUID_FORMS_TRINKET_BEAR; + break; + case FORM_CAT: + triggerspell = SPELL_DRUID_FORMS_TRINKET_CAT; + break; + case FORM_MOONKIN: + triggerspell = SPELL_DRUID_FORMS_TRINKET_MOONKIN; + break; + case FORM_NONE: + triggerspell = SPELL_DRUID_FORMS_TRINKET_NONE; + break; + case FORM_TREE: + triggerspell = SPELL_DRUID_FORMS_TRINKET_TREE; + break; + default: + return; + } + + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_forms_trinket_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_forms_trinket_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_forms_trinket_AuraScript(); + } +}; + // 54846 - Glyph of Starfire class spell_dru_glyph_of_starfire : public SpellScriptLoader { @@ -1079,6 +1236,77 @@ class spell_dru_typhoon : public SpellScriptLoader } }; +// 67353 - T9 Feral Relic (Idol of Mutilation) +class spell_dru_t9_feral_relic : public SpellScriptLoader +{ +public: + spell_dru_t9_feral_relic() : SpellScriptLoader("spell_dru_t9_feral_relic") { } + + class spell_dru_t9_feral_relic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t9_feral_relic_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_T9_FERAL_RELIC_BEAR) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_T9_FERAL_RELIC_CAT)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + Unit* target = eventInfo.GetActor(); + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + case FORM_CAT: + return true; + default: + break; + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerspell = 0; + + Unit* target = eventInfo.GetActor(); + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + triggerspell = SPELL_DRUID_T9_FERAL_RELIC_BEAR; + break; + case FORM_CAT: + triggerspell = SPELL_DRUID_T9_FERAL_RELIC_CAT; + break; + default: + return; + } + + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_t9_feral_relic_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_t9_feral_relic_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t9_feral_relic_AuraScript(); + } +}; + // 70691 - Item T10 Restoration 4P Bonus class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader { @@ -1208,7 +1436,9 @@ void AddSC_druid_spell_scripts() { new spell_dru_bear_form_passive(); new spell_dru_dash(); + new spell_dru_eclipse(); new spell_dru_enrage(); + new spell_dru_forms_trinket(); new spell_dru_glyph_of_starfire(); new spell_dru_idol_lifebloom(); new spell_dru_innervate(); @@ -1230,6 +1460,7 @@ void AddSC_druid_spell_scripts() new spell_dru_flight_form(); new spell_dru_tiger_s_fury(); new spell_dru_typhoon(); + new spell_dru_t9_feral_relic(); new spell_dru_t10_restoration_4p_bonus(); new spell_dru_wild_growth(); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 8b8c5300a9e..e8ad73ceadb 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -430,6 +430,61 @@ class spell_gen_bandage : public SpellScriptLoader } }; +// Blood Reserve - 64568 +enum BloodReserve +{ + SPELL_GEN_BLOOD_RESERVE_AURA = 64568, + SPELL_GEN_BLOOD_RESERVE_HEAL = 64569 +}; + +class spell_gen_blood_reserve : public SpellScriptLoader +{ + public: + spell_gen_blood_reserve() : SpellScriptLoader("spell_gen_blood_reserve") { } + + class spell_gen_blood_reserve_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_blood_reserve_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_GEN_BLOOD_RESERVE_HEAL)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + if (Unit* caster = eventInfo.GetActionTarget()) + if (caster->HealthBelowPctDamaged(35, dmgInfo->GetDamage())) + return true; + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActionTarget(); + caster->CastCustomSpell(SPELL_GEN_BLOOD_RESERVE_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), caster, TRIGGERED_FULL_MASK, nullptr, aurEff); + caster->RemoveAura(SPELL_GEN_BLOOD_RESERVE_AURA); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_gen_blood_reserve_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_gen_blood_reserve_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gen_blood_reserve_AuraScript(); + } +}; + enum Bonked { SPELL_BONKED = 62991, @@ -2085,7 +2140,7 @@ class spell_gen_mounted_charge: public SpellScriptLoader } // If target isn't a training dummy there's a chance of failing the charge - if (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE) && roll_chance_f(12.5f)) + if (!target->IsCharmedOwnedByPlayerOrPlayer() && roll_chance_f(12.5f)) spellId = SPELL_CHARGE_MISS_EFFECT; if (Unit* vehicle = GetCaster()->GetVehicleBase()) @@ -4199,6 +4254,7 @@ void AddSC_generic_spell_scripts() new spell_gen_aura_service_uniform(); new spell_gen_av_drekthar_presence(); new spell_gen_bandage(); + new spell_gen_blood_reserve(); new spell_gen_bonked(); new spell_gen_break_shield("spell_gen_break_shield"); new spell_gen_break_shield("spell_gen_tournament_counterattack"); diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index 6442eb8acca..bd36e3cb765 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -113,6 +113,105 @@ class spell_love_is_in_the_air_romantic_picnic : public SpellScriptLoader } }; +enum HallowEndCandysSpells +{ + SPELL_HALLOWS_END_CANDY_ORANGE_GIANT = 24924, // Effect 1: Apply Aura: Mod Size, Value: 30% + SPELL_HALLOWS_END_CANDY_SKELETON = 24925, // Effect 1: Apply Aura: Change Model (Skeleton). Effect 2: Apply Aura: Underwater Breathing + SPELL_HALLOWS_END_CANDY_PIRATE = 24926, // Effect 1: Apply Aura: Increase Swim Speed, Value: 50% + SPELL_HALLOWS_END_CANDY_GHOST = 24927, // Effect 1: Apply Aura: Levitate / Hover. Effect 2: Apply Aura: Slow Fall, Effect 3: Apply Aura: Water Walking + SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE = 44742, // Effect 1: Apply Aura: Change Model (Defias Pirate, Female). Effect 2: Increase Swim Speed, Value: 50% + SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE = 44743 // Effect 1: Apply Aura: Change Model (Defias Pirate, Male). Effect 2: Increase Swim Speed, Value: 50% +}; + +// 24930 - Hallow's End Candy +class spell_hallow_end_candy : public SpellScriptLoader +{ + public: + spell_hallow_end_candy() : SpellScriptLoader("spell_hallow_end_candy") { } + + class spell_hallow_end_candy_SpellScript : public SpellScript + { + PrepareSpellScript(spell_hallow_end_candy_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + for (uint32 spellId : spells) + if (!sSpellMgr->GetSpellInfo(spellId)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + GetCaster()->CastSpell(GetCaster(), spells[urand(0, 3)], true); + } + + void Register() override + { + OnEffectHit += SpellEffectFn(spell_hallow_end_candy_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + + private: + static uint32 const spells[4]; + }; + + SpellScript* GetSpellScript() const override + { + return new spell_hallow_end_candy_SpellScript(); + } +}; + +uint32 const spell_hallow_end_candy::spell_hallow_end_candy_SpellScript::spells[4] = +{ + SPELL_HALLOWS_END_CANDY_ORANGE_GIANT, + SPELL_HALLOWS_END_CANDY_SKELETON, + SPELL_HALLOWS_END_CANDY_PIRATE, + SPELL_HALLOWS_END_CANDY_GHOST +}; + +// 24926 - Hallow's End Candy +class spell_hallow_end_candy_pirate : public SpellScriptLoader +{ + public: + spell_hallow_end_candy_pirate() : SpellScriptLoader("spell_hallow_end_candy_pirate") { } + + class spell_hallow_end_candy_pirate_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hallow_end_candy_pirate_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE) + || !sSpellMgr->GetSpellInfo(SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE)) + return false; + return true; + } + + void HandleApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + uint32 spell = GetTarget()->getGender() == GENDER_FEMALE ? SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE : SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE; + GetTarget()->CastSpell(GetTarget(), spell, true); + } + + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + uint32 spell = GetTarget()->getGender() == GENDER_FEMALE ? SPELL_HALLOWS_END_CANDY_FEMALE_DEFIAS_PIRATE : SPELL_HALLOWS_END_CANDY_MALE_DEFIAS_PIRATE; + GetTarget()->RemoveAurasDueToSpell(spell); + } + + void Register() override + { + AfterEffectApply += AuraEffectApplyFn(spell_hallow_end_candy_pirate_AuraScript::HandleApply, EFFECT_0, SPELL_AURA_MOD_INCREASE_SWIM_SPEED, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_hallow_end_candy_pirate_AuraScript::HandleRemove, EFFECT_0, SPELL_AURA_MOD_INCREASE_SWIM_SPEED, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hallow_end_candy_pirate_AuraScript(); + } +}; + // 24750 Trick enum TrickSpells { @@ -1183,11 +1282,79 @@ class spell_midsummer_braziers_hit : public SpellScriptLoader } }; +enum RibbonPoleData +{ + SPELL_HAS_FULL_MIDSUMMER_SET = 58933, + SPELL_BURNING_HOT_POLE_DANCE = 58934, + SPELL_RIBBON_DANCE_COSMETIC = 29726, + SPELL_RIBBON_DANCE = 29175, + GO_RIBBON_POLE = 181605, +}; + +class spell_gen_ribbon_pole_dancer_check : public SpellScriptLoader +{ + public: + spell_gen_ribbon_pole_dancer_check() : SpellScriptLoader("spell_gen_ribbon_pole_dancer_check") { } + + class spell_gen_ribbon_pole_dancer_check_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_ribbon_pole_dancer_check_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HAS_FULL_MIDSUMMER_SET) + || !sSpellMgr->GetSpellInfo(SPELL_RIBBON_DANCE) + || !sSpellMgr->GetSpellInfo(SPELL_BURNING_HOT_POLE_DANCE)) + return false; + return true; + } + + void PeriodicTick(AuraEffect const* /*aurEff*/) + { + Unit* target = GetTarget(); + + // check if aura needs to be removed + if (!target->FindNearestGameObject(GO_RIBBON_POLE, 8.0f) || !target->HasUnitState(UNIT_STATE_CASTING)) + { + target->InterruptNonMeleeSpells(false); + target->RemoveAurasDueToSpell(GetId()); + target->RemoveAura(SPELL_RIBBON_DANCE_COSMETIC); + return; + } + + // set xp buff duration + if (Aura* aur = target->GetAura(SPELL_RIBBON_DANCE)) + { + aur->SetMaxDuration(std::min(3600000, aur->GetMaxDuration() + 180000)); + aur->RefreshDuration(); + + // reward achievement criteria + if (aur->GetMaxDuration() == 3600000 && target->HasAura(SPELL_HAS_FULL_MIDSUMMER_SET)) + target->CastSpell(target, SPELL_BURNING_HOT_POLE_DANCE, true); + } + else + target->AddAura(SPELL_RIBBON_DANCE, target); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_gen_ribbon_pole_dancer_check_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gen_ribbon_pole_dancer_check_AuraScript(); + } +}; + void AddSC_holiday_spell_scripts() { // Love is in the Air new spell_love_is_in_the_air_romantic_picnic(); // Hallow's End + new spell_hallow_end_candy(); + new spell_hallow_end_candy_pirate(); new spell_hallow_end_trick(); new spell_hallow_end_trick_or_treat(); new spell_hallow_end_tricky_treat(); @@ -1219,4 +1386,5 @@ void AddSC_holiday_spell_scripts() new spell_brewfest_barker_bunny(); // Midsummer Fire Festival new spell_midsummer_braziers_hit(); + new spell_gen_ribbon_pole_dancer_check(); } diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 82d9d134445..a75294ad6e0 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -51,12 +51,15 @@ enum HunterSpells SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED = 54114, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF = 55711, SPELL_HUNTER_PET_CARRION_FEEDER_TRIGGERED = 54045, + SPELL_HUNTER_PIERCING_SHOTS = 63468, SPELL_HUNTER_READINESS = 23989, SPELL_HUNTER_SNIPER_TRAINING_R1 = 53302, SPELL_HUNTER_SNIPER_TRAINING_BUFF_R1 = 64418, + SPELL_HUNTER_T9_4P_GREATNESS = 68130, SPELL_HUNTER_VICIOUS_VIPER = 61609, SPELL_HUNTER_VIPER_ATTACK_SPEED = 60144, - SPELL_DRAENEI_GIFT_OF_THE_NAARU = 59543 + SPELL_DRAENEI_GIFT_OF_THE_NAARU = 59543, + SPELL_ROAR_OF_SACRIFICE_TRIGGERED = 67481 }; // 13161 - Aspect of the Beast @@ -704,6 +707,63 @@ class spell_hun_pet_heart_of_the_phoenix : public SpellScriptLoader } }; +// -53234 - Piercing Shots +class spell_hun_piercing_shots : public SpellScriptLoader +{ +public: + spell_hun_piercing_shots() : SpellScriptLoader("spell_hun_piercing_shots") { } + + class spell_hun_piercing_shots_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_piercing_shots_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_PIERCING_SHOTS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActionTarget()) + return true; + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); + + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + { + SpellInfo const* piercingShots = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_PIERCING_SHOTS); + int32 duration = piercingShots->GetMaxDuration(); + uint32 amplitude = piercingShots->Effects[EFFECT_0].Amplitude; + uint32 dmg = dmgInfo->GetDamage(); + + uint32 bp = CalculatePct(int32(dmg), aurEff->GetAmount()) / (duration / int32(amplitude)); + bp += target->GetRemainingPeriodicAmount(target->GetGUID(), SPELL_HUNTER_PIERCING_SHOTS, SPELL_AURA_PERIODIC_DAMAGE); + + caster->CastCustomSpell(SPELL_HUNTER_PIERCING_SHOTS, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_piercing_shots_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_piercing_shots_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_piercing_shots_AuraScript(); + } +}; + // 56654, 58882 - Rapid Recuperation class spell_hun_rapid_recuperation : public SpellScriptLoader { @@ -788,6 +848,49 @@ class spell_hun_readiness : public SpellScriptLoader } }; +// 53480 - Roar of Sacrifice +class spell_hun_roar_of_sacrifice : public SpellScriptLoader +{ + public: + spell_hun_roar_of_sacrifice() : SpellScriptLoader("spell_hun_roar_of_sacrifice") { } + + class spell_hun_roar_of_sacrifice_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_roar_of_sacrifice_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROAR_OF_SACRIFICE_TRIGGERED)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return GetCaster() && (eventInfo.GetDamageInfo()->GetSchoolMask() & GetEffect(EFFECT_1)->GetMiscValue()) != 0; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + uint32 damage = CalculatePct(eventInfo.GetDamageInfo()->GetDamage(), aurEff->GetAmount()); + eventInfo.GetActor()->CastCustomSpell(SPELL_ROAR_OF_SACRIFICE_TRIGGERED, SPELLVALUE_BASE_POINT0, damage, GetCaster(), TRIGGERED_FULL_MASK, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_roar_of_sacrifice_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_roar_of_sacrifice_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_roar_of_sacrifice_AuraScript(); + } +}; + // 37506 - Scatter Shot class spell_hun_scatter_shot : public SpellScriptLoader { @@ -967,6 +1070,51 @@ class spell_hun_target_only_pet_and_owner : public SpellScriptLoader } }; +// 67151 - T9 4P Bonus +class spell_hun_t9_4p_bonus : public SpellScriptLoader +{ +public: + spell_hun_t9_4p_bonus() : SpellScriptLoader("spell_hun_t9_4p_bonus") { } + + class spell_hun_t9_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_t9_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_T9_4P_GREATNESS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActor()->GetTypeId() == TYPEID_PLAYER && eventInfo.GetActor()->ToPlayer()->GetPet()) + return true; + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + + caster->CastSpell(caster->ToPlayer()->GetPet(), SPELL_HUNTER_T9_4P_GREATNESS, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_t9_4p_bonus_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_t9_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_t9_4p_bonus_AuraScript(); + } +}; + // 60144 - Viper Attack Speed class spell_hun_viper_attack_speed : public SpellScriptLoader { @@ -1025,11 +1173,14 @@ void AddSC_hunter_spell_scripts() new spell_hun_misdirection_proc(); new spell_hun_pet_carrion_feeder(); new spell_hun_pet_heart_of_the_phoenix(); + new spell_hun_piercing_shots(); new spell_hun_rapid_recuperation(); new spell_hun_readiness(); + new spell_hun_roar_of_sacrifice(); new spell_hun_scatter_shot(); new spell_hun_sniper_training(); new spell_hun_tame_beast(); new spell_hun_target_only_pet_and_owner(); + new spell_hun_t9_4p_bonus(); new spell_hun_viper_attack_speed(); } diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 84c67bad63b..5a6bb78eb10 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -1903,7 +1903,7 @@ class spell_item_crystal_prison_dummy_dnd : public SpellScriptLoader if (Creature* target = GetHitCreature()) if (target->isDead() && !target->IsPet()) { - GetCaster()->SummonGameObject(OBJECT_IMPRISONED_DOOMGUARD, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), target->GetOrientation(), 0, 0, 0, 0, uint32(target->GetRespawnTime()-time(NULL))); + GetCaster()->SummonGameObject(OBJECT_IMPRISONED_DOOMGUARD, *target, G3D::Quat(), uint32(target->GetRespawnTime()-time(NULL))); target->DespawnOrUnsummon(); } } @@ -2731,6 +2731,70 @@ public: } }; +enum SoulPreserver +{ + SPELL_SOUL_PRESERVER_DRUID = 60512, + SPELL_SOUL_PRESERVER_PALADIN = 60513, + SPELL_SOUL_PRESERVER_PRIEST = 60514, + SPELL_SOUL_PRESERVER_SHAMAN = 60515, +}; + +class spell_item_soul_preserver : public SpellScriptLoader +{ +public: + spell_item_soul_preserver() : SpellScriptLoader("spell_item_soul_preserver") { } + + class spell_item_soul_preserver_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_soul_preserver_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_DRUID) || + !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_PALADIN) || + !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_PRIEST) || + !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_SHAMAN)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + + switch (caster->getClass()) + { + case CLASS_DRUID: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_DRUID, true, nullptr, aurEff); + break; + case CLASS_PALADIN: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PALADIN, true, nullptr, aurEff); + break; + case CLASS_PRIEST: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PRIEST, true, nullptr, aurEff); + break; + case CLASS_SHAMAN: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_SHAMAN, true, nullptr, aurEff); + break; + default: + break; + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_soul_preserver_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_soul_preserver_AuraScript(); + } +}; + class spell_item_toy_train_set_pulse : public SpellScriptLoader { public: @@ -2768,6 +2832,336 @@ public: } }; +enum DeathChoiceSpells +{ + SPELL_DEATH_CHOICE_NORMAL_AURA = 67702, + SPELL_DEATH_CHOICE_NORMAL_AGILITY = 67703, + SPELL_DEATH_CHOICE_NORMAL_STRENGTH = 67708, + SPELL_DEATH_CHOICE_HEROIC_AURA = 67771, + SPELL_DEATH_CHOICE_HEROIC_AGILITY = 67772, + SPELL_DEATH_CHOICE_HEROIC_STRENGTH = 67773 +}; + +class spell_item_death_choice : public SpellScriptLoader +{ +public: + spell_item_death_choice() : SpellScriptLoader("spell_item_death_choice") { } + + class spell_item_death_choice_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_death_choice_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_NORMAL_STRENGTH) || + !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_NORMAL_AGILITY) || + !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_HEROIC_STRENGTH) || + !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_HEROIC_AGILITY)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + float str = caster->GetStat(STAT_STRENGTH); + float agi = caster->GetStat(STAT_AGILITY); + + switch (aurEff->GetId()) + { + case SPELL_DEATH_CHOICE_NORMAL_AURA: + { + if (str > agi) + caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_STRENGTH, true, nullptr, aurEff); + else + caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_AGILITY, true, nullptr, aurEff); + break; + } + case SPELL_DEATH_CHOICE_HEROIC_AURA: + { + if (str > agi) + caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_STRENGTH, true, nullptr, aurEff); + else + caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_AGILITY, true, nullptr, aurEff); + break; + } + default: + break; + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_death_choice_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_death_choice_AuraScript(); + } +}; + +enum TrinketStackSpells +{ + SPELL_LIGHTNING_CAPACITOR_AURA = 37657, // Lightning Capacitor + SPELL_LIGHTNING_CAPACITOR_STACK = 37658, + SPELL_LIGHTNING_CAPACITOR_TRIGGER = 37661, + SPELL_THUNDER_CAPACITOR_AURA = 54841, // Thunder Capacitor + SPELL_THUNDER_CAPACITOR_STACK = 54842, + SPELL_THUNDER_CAPACITOR_TRIGGER = 54843, + SPELL_TOC25_CASTER_TRINKET_NORMAL_AURA = 67712, // Item - Coliseum 25 Normal Caster Trinket + SPELL_TOC25_CASTER_TRINKET_NORMAL_STACK = 67713, + SPELL_TOC25_CASTER_TRINKET_NORMAL_TRIGGER = 67714, + SPELL_TOC25_CASTER_TRINKET_HEROIC_AURA = 67758, // Item - Coliseum 25 Heroic Caster Trinket + SPELL_TOC25_CASTER_TRINKET_HEROIC_STACK = 67759, + SPELL_TOC25_CASTER_TRINKET_HEROIC_TRIGGER = 67760, +}; + +class spell_item_trinket_stack : public SpellScriptLoader +{ +public: + spell_item_trinket_stack(char const* scriptName, uint32 stackSpell, uint32 triggerSpell) : SpellScriptLoader(scriptName), + _stackSpell(stackSpell), _triggerSpell(triggerSpell) + { + } + + class spell_item_trinket_stack_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_trinket_stack_AuraScript); + + public: + spell_item_trinket_stack_AuraScript(uint32 stackSpell, uint32 triggerSpell) : _stackSpell(stackSpell), _triggerSpell(triggerSpell) + { + } + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(_stackSpell) || !sSpellMgr->GetSpellInfo(_triggerSpell)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + + caster->CastSpell(caster, _stackSpell, true, nullptr, aurEff); // cast the stack + + Aura* dummy = caster->GetAura(_stackSpell); // retrieve aura + + //dont do anything if it's not the right amount of stacks; + if (!dummy || dummy->GetStackAmount() < aurEff->GetAmount()) + return; + + // if right amount, remove the aura and cast real trigger + caster->RemoveAurasDueToSpell(_stackSpell); + if (Unit* target = eventInfo.GetActionTarget()) + caster->CastSpell(target, _triggerSpell, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_trinket_stack_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + + private: + uint32 _stackSpell; + uint32 _triggerSpell; + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_trinket_stack_AuraScript(_stackSpell, _triggerSpell); + } + +private: + uint32 _stackSpell; + uint32 _triggerSpell; +}; + +// 57345 - Darkmoon Card: Greatness +enum DarkmoonCardSpells +{ + SPELL_DARKMOON_CARD_STRENGHT = 60229, + SPELL_DARKMOON_CARD_AGILITY = 60233, + SPELL_DARKMOON_CARD_INTELLECT = 60234, + SPELL_DARKMOON_CARD_SPIRIT = 60235, +}; + +class spell_item_darkmoon_card_greatness : public SpellScriptLoader +{ +public: + spell_item_darkmoon_card_greatness() : SpellScriptLoader("spell_item_darkmoon_card_greatness") { } + + class spell_item_darkmoon_card_greatness_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_darkmoon_card_greatness_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_STRENGHT) || + !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_AGILITY) || + !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_INTELLECT) || + !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_SPIRIT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + float str = caster->GetStat(STAT_STRENGTH); + float agi = caster->GetStat(STAT_AGILITY); + float intl = caster->GetStat(STAT_INTELLECT); + float spi = caster->GetStat(STAT_SPIRIT); + float stat = 0.0f; + + uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGHT; + + if (str > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_STRENGHT; + stat = str; + } + + if (agi > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_AGILITY; + stat = agi; + } + + if (intl > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_INTELLECT; + stat = intl; + } + + if (spi > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_SPIRIT; + stat = spi; + } + + caster->CastSpell(caster, spellTrigger, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_darkmoon_card_greatness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_darkmoon_card_greatness_AuraScript(); + } +}; + +// 43820 - Amani Charm of the Witch Doctor +enum CharmWitchDoctor +{ + SPELL_CHARM_WITCH_DOCTOR_PROC = 43821 +}; + +class spell_item_charm_witch_doctor : public SpellScriptLoader +{ +public: + spell_item_charm_witch_doctor() : SpellScriptLoader("spell_item_charm_witch_doctor") { } + + class spell_item_charm_witch_doctor_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_charm_witch_doctor_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_CHARM_WITCH_DOCTOR_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); + + if (target) + { + int32 bp = CalculatePct(target->GetCreateHealth(),aurEff->GetSpellInfo()->Effects[1].CalcValue()); + caster->CastCustomSpell(target, SPELL_CHARM_WITCH_DOCTOR_PROC, &bp, nullptr, nullptr, true, nullptr, aurEff); + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_charm_witch_doctor_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_charm_witch_doctor_AuraScript(); + } +}; + +// 27522,40336 - Mana Drain +enum ManaDrainSpells +{ + SPELL_MANA_DRAIN_ENERGIZE = 29471, + SPELL_MANA_DRAIN_LEECH = 27526 +}; + +class spell_item_mana_drain : public SpellScriptLoader +{ +public: + spell_item_mana_drain() : SpellScriptLoader("spell_item_mana_drain") { } + + class spell_item_mana_drain_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_mana_drain_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MANA_DRAIN_ENERGIZE) + || !sSpellMgr->GetSpellInfo(SPELL_MANA_DRAIN_LEECH)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); + + if (caster->IsAlive()) + caster->CastSpell(caster, SPELL_MANA_DRAIN_ENERGIZE, true, nullptr, aurEff); + + if (target && target->IsAlive()) + caster->CastSpell(target, SPELL_MANA_DRAIN_LEECH, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_mana_drain_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_mana_drain_AuraScript(); + } +}; + void AddSC_item_spell_scripts() { // 23074 Arcanite Dragonling @@ -2838,5 +3232,14 @@ void AddSC_item_spell_scripts() new spell_item_chicken_cover(); new spell_item_muisek_vessel(); new spell_item_greatmothers_soulcatcher(); + new spell_item_soul_preserver(); new spell_item_toy_train_set_pulse(); + new spell_item_death_choice(); + new spell_item_trinket_stack("spell_item_lightning_capacitor", SPELL_LIGHTNING_CAPACITOR_STACK, SPELL_LIGHTNING_CAPACITOR_TRIGGER); + new spell_item_trinket_stack("spell_item_thunder_capacitor", SPELL_THUNDER_CAPACITOR_STACK, SPELL_THUNDER_CAPACITOR_TRIGGER); + new spell_item_trinket_stack("spell_item_toc25_normal_caster_trinket", SPELL_TOC25_CASTER_TRINKET_NORMAL_STACK, SPELL_TOC25_CASTER_TRINKET_NORMAL_TRIGGER); + new spell_item_trinket_stack("spell_item_toc25_heroic_caster_trinket", SPELL_TOC25_CASTER_TRINKET_HEROIC_STACK, SPELL_TOC25_CASTER_TRINKET_HEROIC_TRIGGER); + new spell_item_darkmoon_card_greatness(); + new spell_item_charm_witch_doctor(); + new spell_item_mana_drain(); } diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index 2f4e4fa6f44..bacbe31630c 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -29,6 +29,7 @@ enum MageSpells { + SPELL_MAGE_BLAZING_SPEED = 31643, SPELL_MAGE_BURNOUT = 29077, SPELL_MAGE_COLD_SNAP = 11958, SPELL_MAGE_FOCUS_MAGIC_PROC = 54648, @@ -116,6 +117,42 @@ class spell_mage_blast_wave : public SpellScriptLoader } }; +// -31641 - Blazing Speed +class spell_mage_blazing_speed : public SpellScriptLoader +{ +public: + spell_mage_blazing_speed() : SpellScriptLoader("spell_mage_blazing_speed") { } + + class spell_mage_blazing_speed_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_blazing_speed_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_BLAZING_SPEED)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (Unit* target = eventInfo.GetActionTarget()) + target->CastSpell(target, SPELL_MAGE_BLAZING_SPEED, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_blazing_speed_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_blazing_speed_AuraScript(); + } +}; + // -44449 - Burnout class spell_mage_burnout : public SpellScriptLoader { @@ -647,6 +684,7 @@ class spell_mage_summon_water_elemental : public SpellScriptLoader void AddSC_mage_spell_scripts() { new spell_mage_blast_wave(); + new spell_mage_blazing_speed(); new spell_mage_burnout(); new spell_mage_cold_snap(); new spell_mage_fire_frost_ward(); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index d9fd36f5fd4..6de95af8d8f 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -37,6 +37,7 @@ enum PaladinSpells SPELL_PALADIN_HOLY_SHOCK_R1 = 20473, SPELL_PALADIN_HOLY_SHOCK_R1_DAMAGE = 25912, SPELL_PALADIN_HOLY_SHOCK_R1_HEALING = 25914, + SPELL_PALADIN_ILLUMINATION_ENERGIZE = 20272, SPELL_PALADIN_BLESSING_OF_LOWER_CITY_DRUID = 37878, SPELL_PALADIN_BLESSING_OF_LOWER_CITY_PALADIN = 37879, @@ -871,6 +872,61 @@ class spell_pal_holy_shock : public SpellScriptLoader } }; +// -20210 - Illumination +class spell_pal_illumination : public SpellScriptLoader +{ +public: + spell_pal_illumination() : SpellScriptLoader("spell_pal_illumination") { } + + class spell_pal_illumination_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_illumination_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_SHOCK_R1_HEALING) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_ILLUMINATION_ENERGIZE) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_SHOCK_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + // this script is valid only for the Holy Shock procs of illumination + if (eventInfo.GetHealInfo() && eventInfo.GetHealInfo()->GetSpellInfo()) + { + SpellInfo const* originalSpell = nullptr; + + // if proc comes from the Holy Shock heal, need to get mana cost of original spell - else it's the original heal itself + if (eventInfo.GetHealInfo()->GetSpellInfo()->SpellFamilyFlags[1] & 0x00010000) + originalSpell = sSpellMgr->GetSpellInfo(sSpellMgr->GetSpellWithRank(SPELL_PALADIN_HOLY_SHOCK_R1, eventInfo.GetHealInfo()->GetSpellInfo()->GetRank())); + else + originalSpell = eventInfo.GetHealInfo()->GetSpellInfo(); + + if (originalSpell && aurEff->GetSpellInfo()) + { + Unit* target = eventInfo.GetActor(); // Paladin is the target of the energize + uint32 bp = CalculatePct(originalSpell->CalcPowerCost(target, originalSpell->GetSchoolMask()), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue()); + target->CastCustomSpell(SPELL_PALADIN_ILLUMINATION_ENERGIZE, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + } + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_illumination_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_illumination_AuraScript(); + } +}; + // Maybe this is incorrect // These spells should always be cast on login, regardless of whether the player has the talent or not @@ -1393,6 +1449,7 @@ void AddSC_paladin_spell_scripts() new spell_pal_hand_of_sacrifice(); new spell_pal_hand_of_salvation(); new spell_pal_holy_shock(); + new spell_pal_illumination(); new spell_pal_improved_aura("spell_pal_improved_concentraction_aura", SPELL_PALADIN_IMPROVED_CONCENTRACTION_AURA); new spell_pal_improved_aura("spell_pal_improved_devotion_aura", SPELL_PALADIN_IMPROVED_DEVOTION_AURA); new spell_pal_improved_aura("spell_pal_sanctified_retribution", SPELL_PALADIN_SANCTIFIED_RETRIBUTION_AURA); diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 51f03346df1..9e2d265aa9c 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -29,6 +29,7 @@ enum PriestSpells { + SPELL_PRIEST_BLESSED_RECOVERY_R1 = 27813, SPELL_PRIEST_DIVINE_AEGIS = 47753, SPELL_PRIEST_EMPOWERED_RENEW = 63544, SPELL_PRIEST_GLYPH_OF_CIRCLE_OF_HEALING = 55675, @@ -88,6 +89,50 @@ class RaidCheck Unit const* _caster; }; +// -27811 - Blessed Recovery +class spell_pri_blessed_recovery : public SpellScriptLoader +{ +public: + spell_pri_blessed_recovery() : SpellScriptLoader("spell_pri_blessed_recovery") { } + + class spell_pri_blessed_recovery_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_blessed_recovery_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_BLESSED_RECOVERY_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + { + if (Unit* target = eventInfo.GetActionTarget()) + { + uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_PRIEST_BLESSED_RECOVERY_R1, aurEff->GetSpellInfo()->GetRank()); + uint32 bp = CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()) / 3; + bp += target->GetRemainingPeriodicAmount(target->GetGUID(), triggerSpell, SPELL_AURA_PERIODIC_HEAL); + target->CastCustomSpell(triggerSpell, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + } + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_blessed_recovery_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_blessed_recovery_AuraScript(); + } +}; + // -34861 - Circle of Healing class spell_pri_circle_of_healing : public SpellScriptLoader { @@ -869,6 +914,7 @@ class spell_pri_vampiric_touch : public SpellScriptLoader void AddSC_priest_spell_scripts() { + new spell_pri_blessed_recovery(); new spell_pri_circle_of_healing(); new spell_pri_divine_aegis(); new spell_pri_divine_hymn(); diff --git a/src/server/scripts/Spells/spell_quest.cpp b/src/server/scripts/Spells/spell_quest.cpp index ae761413ca5..715e4d4ed2d 100644 --- a/src/server/scripts/Spells/spell_quest.cpp +++ b/src/server/scripts/Spells/spell_quest.cpp @@ -1080,9 +1080,7 @@ enum RedSnapperVeryTasty ITEM_RED_SNAPPER = 23614, SPELL_CAST_NET = 29866, - SPELL_NEW_SUMMON_TEST = 49214, - - GO_SCHOOL_OF_RED_SNAPPER = 181616 + SPELL_NEW_SUMMON_TEST = 49214 }; class spell_q9452_cast_net: public SpellScriptLoader @@ -1099,15 +1097,6 @@ class spell_q9452_cast_net: public SpellScriptLoader return GetCaster()->GetTypeId() == TYPEID_PLAYER; } - SpellCastResult CheckCast() - { - GameObject* go = GetCaster()->FindNearestGameObject(GO_SCHOOL_OF_RED_SNAPPER, 3.0f); - if (!go || go->GetRespawnTime()) - return SPELL_FAILED_REQUIRES_SPELL_FOCUS; - - return SPELL_CAST_OK; - } - void HandleDummy(SpellEffIndex /*effIndex*/) { Player* caster = GetCaster()->ToPlayer(); @@ -1127,7 +1116,6 @@ class spell_q9452_cast_net: public SpellScriptLoader void Register() override { - OnCheckCast += SpellCheckCastFn(spell_q9452_cast_net_SpellScript::CheckCast); OnEffectHit += SpellEffectFn(spell_q9452_cast_net_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); OnEffectHitTarget += SpellEffectFn(spell_q9452_cast_net_SpellScript::HandleActiveObject, EFFECT_1, SPELL_EFFECT_ACTIVATE_OBJECT); } @@ -2482,6 +2470,33 @@ class spell_q13665_q13790_bested_trigger : public SpellScriptLoader } }; +// herald of war and life without regret portal spells +class spell_59064_59439_portals : public SpellScriptLoader +{ +public: + spell_59064_59439_portals() : SpellScriptLoader("spell_59064_59439_portals") { } + + class spell_59064_59439_portals_SpellScript : public SpellScript + { + PrepareSpellScript(spell_59064_59439_portals_SpellScript); + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetHitUnit()->CastSpell(GetHitUnit(), uint32(GetEffectValue())); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_59064_59439_portals_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_59064_59439_portals_SpellScript(); + } +}; + void AddSC_quest_spell_scripts() { new spell_q55_sacred_cleansing(); @@ -2542,4 +2557,5 @@ void AddSC_quest_spell_scripts() new spell_q10929_fumping(); new spell_q12414_hand_over_reins(); new spell_q13665_q13790_bested_trigger(); + new spell_59064_59439_portals(); } diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index affc4d1c26c..1abb6741e0d 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -43,7 +43,8 @@ enum RogueSpells SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC = 59628, SPELL_ROGUE_HONOR_AMONG_THIEVES = 51698, SPELL_ROGUE_HONOR_AMONG_THIEVES_PROC = 52916, - SPELL_ROGUE_HONOR_AMONG_THIEVES_2 = 51699 + SPELL_ROGUE_HONOR_AMONG_THIEVES_2 = 51699, + SPELL_ROGUE_T10_2P_BONUS = 70804, }; // 13877, 33735, (check 51211, 65956) - Blade Flurry @@ -843,6 +844,40 @@ public: } }; +// 70805 - Rogue T10 2P Bonus -- THIS SHOULD BE REMOVED WITH NEW PROC SYSTEM. +class spell_rog_t10_2p_bonus : public SpellScriptLoader +{ +public: + spell_rog_t10_2p_bonus() : SpellScriptLoader("spell_rog_t10_2p_bonus") { } + + class spell_rog_t10_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_t10_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_T10_2P_BONUS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetActor() == eventInfo.GetActionTarget(); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_rog_t10_2p_bonus_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_rog_t10_2p_bonus_AuraScript(); + } +}; + void AddSC_rogue_spell_scripts() { new spell_rog_blade_flurry(); @@ -858,4 +893,5 @@ void AddSC_rogue_spell_scripts() new spell_rog_tricks_of_the_trade_proc(); new spell_rog_honor_among_thieves(); new spell_rog_honor_among_thieves_proc(); + new spell_rog_t10_2p_bonus(); } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 41e72b1388b..ad65c7c6ec7 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -48,8 +48,10 @@ enum ShamanSpells SPELL_SHAMAN_ITEM_MANA_SURGE = 23571, SPELL_SHAMAN_LAVA_FLOWS_R1 = 51480, SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1 = 64694, + SPELL_SHAMAN_LIGHTNING_SHIELD_R1 = 26364, SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE = 52032, SPELL_SHAMAN_MANA_TIDE_TOTEM = 39609, + SPELL_SHAMAN_NATURE_GUARDIAN = 31616, SPELL_SHAMAN_SATED = 57724, SPELL_SHAMAN_STORM_EARTH_AND_FIRE = 51483, SPELL_SHAMAN_TOTEM_EARTHBIND_EARTHGRAB = 64695, @@ -671,7 +673,7 @@ class spell_sha_heroism : public SpellScriptLoader } }; -// 23551 - Lightning Shield +// 23551 - Lightning Shield T2 Bonus class spell_sha_item_lightning_shield : public SpellScriptLoader { public: @@ -706,7 +708,7 @@ class spell_sha_item_lightning_shield : public SpellScriptLoader } }; -// 23552 - Lightning Shield +// 23552 - Lightning Shield T2 Bonus class spell_sha_item_lightning_shield_trigger : public SpellScriptLoader { public: @@ -718,7 +720,7 @@ class spell_sha_item_lightning_shield_trigger : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_MANA_SURGE)) + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE)) return false; return true; } @@ -753,7 +755,7 @@ class spell_sha_item_mana_surge : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE)) + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_MANA_SURGE)) return false; return true; } @@ -865,6 +867,51 @@ class spell_sha_lava_lash : public SpellScriptLoader } }; +// -324 - Lightning Shield +class spell_sha_lightning_shield : public SpellScriptLoader +{ +public: + spell_sha_lightning_shield() : SpellScriptLoader("spell_sha_lightning_shield") { } + + class spell_sha_lightning_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_lightning_shield_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LIGHTNING_SHIELD_R1)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActionTarget()) + return true; + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_LIGHTNING_SHIELD_R1, aurEff->GetSpellInfo()->GetRank()); + + eventInfo.GetActionTarget()->CastSpell(eventInfo.GetActor(), triggerSpell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_lightning_shield_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_sha_lightning_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_lightning_shield_AuraScript(); + } +}; + // 52031, 52033, 52034, 52035, 52036, 58778, 58779, 58780 - Mana Spring Totem class spell_sha_mana_spring_totem : public SpellScriptLoader { @@ -924,6 +971,7 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { if (Unit* caster = GetCaster()) + { if (Unit* unitTarget = GetHitUnit()) { if (unitTarget->getPowerType() == POWER_MANA) @@ -938,6 +986,7 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader caster->CastCustomSpell(unitTarget, SPELL_SHAMAN_MANA_TIDE_TOTEM, &effBasePoints0, NULL, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID()); } } + } } void Register() override @@ -952,6 +1001,56 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader } }; +// -30881 - Nature's Guardian +class spell_sha_nature_guardian : public SpellScriptLoader +{ +public: + spell_sha_nature_guardian() : SpellScriptLoader("spell_sha_nature_guardian") { } + + class spell_sha_nature_guardian_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_nature_guardian_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_NATURE_GUARDIAN)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 healthpct = aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); // %s2 - the 30% threshold for health + + if (Unit* target = eventInfo.GetActionTarget()) + { + if (target->HealthBelowPctDamaged(healthpct, eventInfo.GetDamageInfo()->GetDamage())) + { + + uint32 bp = CalculatePct(target->GetMaxHealth(), aurEff->GetAmount()); + target->CastCustomSpell(SPELL_SHAMAN_NATURE_GUARDIAN, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + + // Threat reduction is around 10% confirmed in retail and from wiki + Unit* attacker = eventInfo.GetActor(); + if (attacker->IsAlive()) + attacker->getThreatManager().modifyThreatPercent(target, -10); + } + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_nature_guardian_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_nature_guardian_AuraScript(); + } +}; + // 6495 - Sentry Totem class spell_sha_sentry_totem : public SpellScriptLoader { @@ -1085,8 +1184,10 @@ void AddSC_shaman_spell_scripts() new spell_sha_item_mana_surge(); new spell_sha_item_t10_elemental_2p_bonus(); new spell_sha_lava_lash(); + new spell_sha_lightning_shield(); new spell_sha_mana_spring_totem(); new spell_sha_mana_tide_totem(); + new spell_sha_nature_guardian(); new spell_sha_sentry_totem(); new spell_sha_thunderstorm(); new spell_sha_totemic_mastery(); diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index a0a6189cbe2..5e0074bf9f7 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -50,6 +50,12 @@ enum WarlockSpells SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2 = 60956, SPELL_WARLOCK_LIFE_TAP_ENERGIZE = 31818, SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2 = 32553, + SPELL_WARLOCK_NETHER_PROTECTION_HOLY = 54370, + SPELL_WARLOCK_NETHER_PROTECTION_FIRE = 54371, + SPELL_WARLOCK_NETHER_PROTECTION_FROST = 54372, + SPELL_WARLOCK_NETHER_PROTECTION_ARCANE = 54373, + SPELL_WARLOCK_NETHER_PROTECTION_SHADOW = 54374, + SPELL_WARLOCK_NETHER_PROTECTION_NATURE = 54375, SPELL_WARLOCK_SOULSHATTER = 32835, SPELL_WARLOCK_SIPHON_LIFE_HEAL = 63106, SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117 @@ -377,6 +383,8 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader case CREATURE_FAMILY_IMP: targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP, true); break; + default: + break; } } } @@ -682,6 +690,95 @@ class spell_warl_life_tap : public SpellScriptLoader } }; +// -30299 - Nether Protection +class spell_warl_nether_protection : public SpellScriptLoader +{ +public: + spell_warl_nether_protection() : SpellScriptLoader("spell_warl_nether_protection") { } + + class spell_warl_nether_protection_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_nether_protection_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_HOLY) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_FIRE) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_FROST) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_ARCANE) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_SHADOW) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_NATURE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetDamageInfo()) + { + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + case SPELL_SCHOOL_FIRE: + case SPELL_SCHOOL_NATURE: + case SPELL_SCHOOL_FROST: + case SPELL_SCHOOL_SHADOW: + case SPELL_SCHOOL_ARCANE: + return true; + default: + break; + } + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerspell = 0; + + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_HOLY; + break; + case SPELL_SCHOOL_FIRE: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_FIRE; + break; + case SPELL_SCHOOL_NATURE: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_NATURE; + break; + case SPELL_SCHOOL_FROST: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_FROST; + break; + case SPELL_SCHOOL_SHADOW: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_ARCANE; + break; + default: + return; + } + + if (Unit* target = eventInfo.GetActionTarget()) + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_warl_nether_protection_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warl_nether_protection_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_nether_protection_AuraScript(); + } +}; + // 18541 - Ritual of Doom Effect class spell_warl_ritual_of_doom_effect : public SpellScriptLoader { @@ -917,6 +1014,7 @@ void AddSC_warlock_spell_scripts() new spell_warl_haunt(); new spell_warl_health_funnel(); new spell_warl_life_tap(); + new spell_warl_nether_protection(); new spell_warl_ritual_of_doom_effect(); new spell_warl_seed_of_corruption(); new spell_warl_shadow_ward(); diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 8f2ea5887d2..3db4f41706d 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -1197,6 +1197,29 @@ public: } }; +enum MidsummerPoleRibbon +{ + SPELL_POLE_DANCE = 29726, + SPELL_BLUE_FIRE_RING = 46842, + NPC_POLE_RIBBON_BUNNY = 17066, + ACTION_COSMETIC_FIRES = 0 +}; + +class go_midsummer_ribbon_pole : public GameObjectScript +{ +public: + go_midsummer_ribbon_pole() : GameObjectScript("go_midsummer_ribbon_pole") { } + + bool OnGossipHello(Player* player, GameObject* go) override + { + if (Creature* creature = go->FindNearestCreature(NPC_POLE_RIBBON_BUNNY, 10.0f)) + { + creature->GetAI()->DoAction(ACTION_COSMETIC_FIRES); + player->CastSpell(creature, SPELL_POLE_DANCE, true); + } + return true; + } +}; enum ToyTrainSpells { @@ -1274,5 +1297,6 @@ void AddSC_go_scripts() new go_veil_skith_cage(); new go_frostblade_shrine(); new go_midsummer_bonfire(); + new go_midsummer_ribbon_pole(); new go_toy_train_set(); } diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp index 52174e1b012..ff15698e579 100644 --- a/src/server/scripts/World/item_scripts.cpp +++ b/src/server/scripts/World/item_scripts.cpp @@ -241,7 +241,7 @@ public: float x, y, z; go->GetClosePoint(x, y, z, go->GetObjectSize() / 3, 7.0f); - go->SummonGameObject(GO_HIGH_QUALITY_FUR, go->GetPositionX(), go->GetPositionY(), go->GetPositionZ(), 0, 0, 0, 0, 0, 1); + go->SummonGameObject(GO_HIGH_QUALITY_FUR, *go, G3D::Quat(), 1); if (TempSummon* summon = player->SummonCreature(NPC_NESINGWARY_TRAPPER, x, y, z, go->GetOrientation(), TEMPSUMMON_DEAD_DESPAWN, 1000)) { summon->SetVisible(false); diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index 159faa38c62..277bc9ac804 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -36,7 +36,6 @@ npc_doctor 100% Gustaf Vanhowzen and Gregory Victor, quest 6622 npc_sayge 100% Darkmoon event fortune teller, buff player based on answers given npc_snake_trap_serpents 80% AI for snakes that summoned by Snake Trap npc_shadowfiend 100% restore 5% of owner's mana when shadowfiend die from damage -npc_locksmith 75% list of keys needs to be confirmed npc_firework 100% NPC's summoned by rockets and rocket clusters, for making them cast visual npc_train_wrecker 100% Wind-Up Train Wrecker that kills train set EndContentData */ @@ -581,6 +580,115 @@ public: }; /*###### +## npc_midsummer_bunny_pole +######*/ + +enum RibbonPoleData +{ + GO_RIBBON_POLE = 181605, + SPELL_RIBBON_DANCE_COSMETIC = 29726, + SPELL_RED_FIRE_RING = 46836, + SPELL_BLUE_FIRE_RING = 46842, + EVENT_CAST_RED_FIRE_RING = 1, + EVENT_CAST_BLUE_FIRE_RING = 2 +}; + +class npc_midsummer_bunny_pole : public CreatureScript +{ +public: + npc_midsummer_bunny_pole() : CreatureScript("npc_midsummer_bunny_pole") { } + + struct npc_midsummer_bunny_poleAI : public ScriptedAI + { + npc_midsummer_bunny_poleAI(Creature* creature) : ScriptedAI(creature) + { + Initialize(); + } + + void Initialize() + { + events.Reset(); + running = false; + } + + void Reset() override + { + Initialize(); + } + + void DoAction(int32 /*action*/) override + { + // Don't start event if it's already running. + if (running) + return; + + running = true; + events.ScheduleEvent(EVENT_CAST_RED_FIRE_RING, 1); + } + + bool checkNearbyPlayers() + { + // Returns true if no nearby player has aura "Test Ribbon Pole Channel". + std::list<Player*> players; + Trinity::UnitAuraCheck check(true, SPELL_RIBBON_DANCE_COSMETIC); + Trinity::PlayerListSearcher<Trinity::UnitAuraCheck> searcher(me, players, check); + me->VisitNearbyWorldObject(10.0f, searcher); + + return players.empty(); + } + + void UpdateAI(uint32 diff) override + { + if (!running) + return; + + events.Update(diff); + + switch (events.ExecuteEvent()) + { + case EVENT_CAST_RED_FIRE_RING: + { + if (checkNearbyPlayers()) + { + Reset(); + return; + } + + if (GameObject* go = me->FindNearestGameObject(GO_RIBBON_POLE, 10.0f)) + me->CastSpell(go, SPELL_RED_FIRE_RING, true); + + events.ScheduleEvent(EVENT_CAST_BLUE_FIRE_RING, Seconds(5)); + } + break; + case EVENT_CAST_BLUE_FIRE_RING: + { + if (checkNearbyPlayers()) + { + Reset(); + return; + } + + if (GameObject* go = me->FindNearestGameObject(GO_RIBBON_POLE, 10.0f)) + me->CastSpell(go, SPELL_BLUE_FIRE_RING, true); + + events.ScheduleEvent(EVENT_CAST_RED_FIRE_RING, Seconds(5)); + } + break; + } + } + + private: + EventMap events; + bool running; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_midsummer_bunny_poleAI(creature); + } +}; + +/*###### ## Triage quest ######*/ @@ -1188,36 +1296,49 @@ public: enum Sayge { - SPELL_DMG = 23768, // dmg - SPELL_RES = 23769, // res - SPELL_ARM = 23767, // arm - SPELL_SPI = 23738, // spi - SPELL_INT = 23766, // int - SPELL_STM = 23737, // stm - SPELL_STR = 23735, // str - SPELL_AGI = 23736, // agi - SPELL_FORTUNE = 23765 // faire fortune + GOSSIP_MENU_OPTION_ID_ANSWER_1 = 0, + GOSSIP_MENU_OPTION_ID_ANSWER_2 = 1, + GOSSIP_MENU_OPTION_ID_ANSWER_3 = 2, + GOSSIP_MENU_OPTION_ID_ANSWER_4 = 3, + GOSSIP_I_AM_READY_TO_DISCOVER = 6186, + GOSSIP_MENU_OPTION_SAYGE1 = 6185, + GOSSIP_MENU_OPTION_SAYGE2 = 6185, + GOSSIP_MENU_OPTION_SAYGE3 = 6185, + GOSSIP_MENU_OPTION_SAYGE4 = 6185, + GOSSIP_MENU_OPTION_SAYGE5 = 6187, + GOSSIP_MENU_OPTION_SAYGE6 = 6187, + GOSSIP_MENU_OPTION_SAYGE7 = 6187, + GOSSIP_MENU_OPTION_SAYGE8 = 6208, + GOSSIP_MENU_OPTION_SAYGE9 = 6208, + GOSSIP_MENU_OPTION_SAYGE10 = 6208, + GOSSIP_MENU_OPTION_SAYGE11 = 6209, + GOSSIP_MENU_OPTION_SAYGE12 = 6209, + GOSSIP_MENU_OPTION_SAYGE13 = 6209, + GOSSIP_MENU_OPTION_SAYGE14 = 6210, + GOSSIP_MENU_OPTION_SAYGE15 = 6210, + GOSSIP_MENU_OPTION_SAYGE16 = 6210, + GOSSIP_MENU_OPTION_SAYGE17 = 6211, + GOSSIP_MENU_I_HAVE_LONG_KNOWN = 7339, + GOSSIP_MENU_YOU_HAVE_BEEN_TASKED = 7340, + GOSSIP_MENU_SWORN_EXECUTIONER = 7341, + GOSSIP_MENU_DIPLOMATIC_MISSION = 7361, + GOSSIP_MENU_YOUR_BROTHER_SEEKS = 7362, + GOSSIP_MENU_A_TERRIBLE_BEAST = 7363, + GOSSIP_MENU_YOUR_FORTUNE_IS_CAST = 7364, + GOSSIP_MENU_HERE_IS_YOUR_FORTUNE = 7365, + GOSSIP_MENU_CANT_GIVE_YOU_YOUR = 7393, + + SPELL_STRENGTH = 23735, // +10% Strength + SPELL_AGILITY = 23736, // +10% Agility + SPELL_STAMINA = 23737, // +10% Stamina + SPELL_SPIRIT = 23738, // +10% Spirit + SPELL_INTELLECT = 23766, // +10% Intellect + SPELL_ARMOR = 23767, // +10% Armor + SPELL_DAMAGE = 23768, // +10% Damage + SPELL_RESISTANCE = 23769, // +25 Magic Resistance (All) + SPELL_FORTUNE = 23765 // Darkmoon Faire Fortune }; -#define GOSSIP_HELLO_SAYGE "Yes" -#define GOSSIP_SENDACTION_SAYGE1 "Slay the Man" -#define GOSSIP_SENDACTION_SAYGE2 "Turn him over to liege" -#define GOSSIP_SENDACTION_SAYGE3 "Confiscate the corn" -#define GOSSIP_SENDACTION_SAYGE4 "Let him go and have the corn" -#define GOSSIP_SENDACTION_SAYGE5 "Execute your friend painfully" -#define GOSSIP_SENDACTION_SAYGE6 "Execute your friend painlessly" -#define GOSSIP_SENDACTION_SAYGE7 "Let your friend go" -#define GOSSIP_SENDACTION_SAYGE8 "Confront the diplomat" -#define GOSSIP_SENDACTION_SAYGE9 "Show not so quiet defiance" -#define GOSSIP_SENDACTION_SAYGE10 "Remain quiet" -#define GOSSIP_SENDACTION_SAYGE11 "Speak against your brother openly" -#define GOSSIP_SENDACTION_SAYGE12 "Help your brother in" -#define GOSSIP_SENDACTION_SAYGE13 "Keep your brother out without letting him know" -#define GOSSIP_SENDACTION_SAYGE14 "Take credit, keep gold" -#define GOSSIP_SENDACTION_SAYGE15 "Take credit, share the gold" -#define GOSSIP_SENDACTION_SAYGE16 "Let the knight take credit" -#define GOSSIP_SENDACTION_SAYGE17 "Thanks" - class npc_sayge : public CreatureScript { public: @@ -1228,19 +1349,19 @@ public: if (creature->IsQuestGiver()) player->PrepareQuestMenu(creature->GetGUID()); - if (player->GetSpellHistory()->HasCooldown(SPELL_INT) || - player->GetSpellHistory()->HasCooldown(SPELL_ARM) || - player->GetSpellHistory()->HasCooldown(SPELL_DMG) || - player->GetSpellHistory()->HasCooldown(SPELL_RES) || - player->GetSpellHistory()->HasCooldown(SPELL_STR) || - player->GetSpellHistory()->HasCooldown(SPELL_AGI) || - player->GetSpellHistory()->HasCooldown(SPELL_STM) || - player->GetSpellHistory()->HasCooldown(SPELL_SPI)) - player->SEND_GOSSIP_MENU(7393, creature->GetGUID()); + if (player->GetSpellHistory()->HasCooldown(SPELL_STRENGTH) || + player->GetSpellHistory()->HasCooldown(SPELL_AGILITY) || + player->GetSpellHistory()->HasCooldown(SPELL_STAMINA) || + player->GetSpellHistory()->HasCooldown(SPELL_SPIRIT) || + player->GetSpellHistory()->HasCooldown(SPELL_INTELLECT) || + player->GetSpellHistory()->HasCooldown(SPELL_ARMOR) || + player->GetSpellHistory()->HasCooldown(SPELL_DAMAGE) || + player->GetSpellHistory()->HasCooldown(SPELL_RESISTANCE)) + player->SEND_GOSSIP_MENU(GOSSIP_MENU_CANT_GIVE_YOU_YOUR, creature->GetGUID()); else { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_SAYGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - player->SEND_GOSSIP_MENU(7339, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_I_AM_READY_TO_DISCOVER, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_I_HAVE_LONG_KNOWN, creature->GetGUID()); } return true; @@ -1251,43 +1372,43 @@ public: switch (action) { case GOSSIP_ACTION_INFO_DEF + 1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - player->SEND_GOSSIP_MENU(7340, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE1, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE2, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE3, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE4, GOSSIP_MENU_OPTION_ID_ANSWER_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_YOU_HAVE_BEEN_TASKED, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF + 2: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE5, GOSSIP_SENDER_MAIN + 1, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE6, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE7, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF); - player->SEND_GOSSIP_MENU(7341, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE5, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 1, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE6, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE7, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_SWORN_EXECUTIONER, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF + 3: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE8, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE9, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE10, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF); - player->SEND_GOSSIP_MENU(7361, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE8, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE9, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE10,GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 2, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_DIPLOMATIC_MISSION, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF + 4: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE11, GOSSIP_SENDER_MAIN + 6, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE12, GOSSIP_SENDER_MAIN + 7, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE13, GOSSIP_SENDER_MAIN + 8, GOSSIP_ACTION_INFO_DEF); - player->SEND_GOSSIP_MENU(7362, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE11, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 6, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE12, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 7, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE13, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 8, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_YOUR_BROTHER_SEEKS, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF + 5: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE14, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE15, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE16, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF); - player->SEND_GOSSIP_MENU(7363, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE14, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN + 5, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE15, GOSSIP_MENU_OPTION_ID_ANSWER_2, GOSSIP_SENDER_MAIN + 4, GOSSIP_ACTION_INFO_DEF); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE16, GOSSIP_MENU_OPTION_ID_ANSWER_3, GOSSIP_SENDER_MAIN + 3, GOSSIP_ACTION_INFO_DEF); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_A_TERRIBLE_BEAST, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SENDACTION_SAYGE17, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); - player->SEND_GOSSIP_MENU(7364, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_OPTION_SAYGE17, GOSSIP_MENU_OPTION_ID_ANSWER_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_YOUR_FORTUNE_IS_CAST, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF + 6: creature->CastSpell(player, SPELL_FORTUNE, false); - player->SEND_GOSSIP_MENU(7365, creature->GetGUID()); + player->SEND_GOSSIP_MENU(GOSSIP_MENU_HERE_IS_YOUR_FORTUNE, creature->GetGUID()); break; } } @@ -1302,28 +1423,28 @@ public: SendAction(player, creature, action); break; case GOSSIP_SENDER_MAIN + 1: - spellId = SPELL_DMG; + spellId = SPELL_DAMAGE; break; case GOSSIP_SENDER_MAIN + 2: - spellId = SPELL_RES; + spellId = SPELL_RESISTANCE; break; case GOSSIP_SENDER_MAIN + 3: - spellId = SPELL_ARM; + spellId = SPELL_ARMOR; break; case GOSSIP_SENDER_MAIN + 4: - spellId = SPELL_SPI; + spellId = SPELL_SPIRIT; break; case GOSSIP_SENDER_MAIN + 5: - spellId = SPELL_INT; + spellId = SPELL_INTELLECT; break; case GOSSIP_SENDER_MAIN + 6: - spellId = SPELL_STM; + spellId = SPELL_STAMINA; break; case GOSSIP_SENDER_MAIN + 7: - spellId = SPELL_STR; + spellId = SPELL_STRENGTH; break; case GOSSIP_SENDER_MAIN + 8: - spellId = SPELL_AGI; + spellId = SPELL_AGILITY; break; } @@ -1743,149 +1864,6 @@ public: }; /*###### -## npc_locksmith -######*/ - -enum LockSmith -{ - QUEST_HOW_TO_BRAKE_IN_TO_THE_ARCATRAZ = 10704, - QUEST_DARK_IRON_LEGACY = 3802, - QUEST_THE_KEY_TO_SCHOLOMANCE_A = 5505, - QUEST_THE_KEY_TO_SCHOLOMANCE_H = 5511, - QUEST_HOTTER_THAN_HELL_A = 10758, - QUEST_HOTTER_THAN_HELL_H = 10764, - QUEST_RETURN_TO_KHAGDAR = 9837, - QUEST_CONTAINMENT = 13159, - QUEST_ETERNAL_VIGILANCE = 11011, - QUEST_KEY_TO_THE_FOCUSING_IRIS = 13372, - QUEST_HC_KEY_TO_THE_FOCUSING_IRIS = 13375, - - ITEM_ARCATRAZ_KEY = 31084, - ITEM_SHADOWFORGE_KEY = 11000, - ITEM_SKELETON_KEY = 13704, - ITEM_SHATTERED_HALLS_KEY = 28395, - ITEM_THE_MASTERS_KEY = 24490, - ITEM_VIOLET_HOLD_KEY = 42482, - ITEM_ESSENCE_INFUSED_MOONSTONE = 32449, - ITEM_KEY_TO_THE_FOCUSING_IRIS = 44582, - ITEM_HC_KEY_TO_THE_FOCUSING_IRIS = 44581, - - SPELL_ARCATRAZ_KEY = 54881, - SPELL_SHADOWFORGE_KEY = 54882, - SPELL_SKELETON_KEY = 54883, - SPELL_SHATTERED_HALLS_KEY = 54884, - SPELL_THE_MASTERS_KEY = 54885, - SPELL_VIOLET_HOLD_KEY = 67253, - SPELL_ESSENCE_INFUSED_MOONSTONE = 40173, -}; - -#define GOSSIP_LOST_ARCATRAZ_KEY "I've lost my key to the Arcatraz." -#define GOSSIP_LOST_SHADOWFORGE_KEY "I've lost my key to the Blackrock Depths." -#define GOSSIP_LOST_SKELETON_KEY "I've lost my key to the Scholomance." -#define GOSSIP_LOST_SHATTERED_HALLS_KEY "I've lost my key to the Shattered Halls." -#define GOSSIP_LOST_THE_MASTERS_KEY "I've lost my key to the Karazhan." -#define GOSSIP_LOST_VIOLET_HOLD_KEY "I've lost my key to the Violet Hold." -#define GOSSIP_LOST_ESSENCE_INFUSED_MOONSTONE "I've lost my Essence-Infused Moonstone." -#define GOSSIP_LOST_KEY_TO_THE_FOCUSING_IRIS "I've lost my Key to the Focusing Iris." -#define GOSSIP_LOST_HC_KEY_TO_THE_FOCUSING_IRIS "I've lost my Heroic Key to the Focusing Iris." - -class npc_locksmith : public CreatureScript -{ -public: - npc_locksmith() : CreatureScript("npc_locksmith") { } - - bool OnGossipHello(Player* player, Creature* creature) override - { - // Arcatraz Key - if (player->GetQuestRewardStatus(QUEST_HOW_TO_BRAKE_IN_TO_THE_ARCATRAZ) && !player->HasItemCount(ITEM_ARCATRAZ_KEY, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_ARCATRAZ_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - - // Shadowforge Key - if (player->GetQuestRewardStatus(QUEST_DARK_IRON_LEGACY) && !player->HasItemCount(ITEM_SHADOWFORGE_KEY, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_SHADOWFORGE_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2); - - // Skeleton Key - if ((player->GetQuestRewardStatus(QUEST_THE_KEY_TO_SCHOLOMANCE_A) || player->GetQuestRewardStatus(QUEST_THE_KEY_TO_SCHOLOMANCE_H)) && - !player->HasItemCount(ITEM_SKELETON_KEY, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_SKELETON_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3); - - // Shatered Halls Key - if ((player->GetQuestRewardStatus(QUEST_HOTTER_THAN_HELL_A) || player->GetQuestRewardStatus(QUEST_HOTTER_THAN_HELL_H)) && - !player->HasItemCount(ITEM_SHATTERED_HALLS_KEY, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_SHATTERED_HALLS_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4); - - // Master's Key - if (player->GetQuestRewardStatus(QUEST_RETURN_TO_KHAGDAR) && !player->HasItemCount(ITEM_THE_MASTERS_KEY, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_THE_MASTERS_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5); - - // Violet Hold Key - if (player->GetQuestRewardStatus(QUEST_CONTAINMENT) && !player->HasItemCount(ITEM_VIOLET_HOLD_KEY, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_VIOLET_HOLD_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6); - - // Essence-Infused Moonstone - if (player->GetQuestRewardStatus(QUEST_ETERNAL_VIGILANCE) && !player->HasItemCount(ITEM_ESSENCE_INFUSED_MOONSTONE, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_ESSENCE_INFUSED_MOONSTONE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7); - - // Key to the Focusing Iris - if (player->GetQuestRewardStatus(QUEST_KEY_TO_THE_FOCUSING_IRIS) && !player->HasItemCount(ITEM_KEY_TO_THE_FOCUSING_IRIS, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_KEY_TO_THE_FOCUSING_IRIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8); - - // Heroic Key to the Focusing Iris - if (player->GetQuestRewardStatus(QUEST_HC_KEY_TO_THE_FOCUSING_IRIS) && !player->HasItemCount(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS, 1, true)) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_LOST_HC_KEY_TO_THE_FOCUSING_IRIS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9); - - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } - - bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - switch (action) - { - case GOSSIP_ACTION_INFO_DEF + 1: - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_ARCATRAZ_KEY, false); - break; - case GOSSIP_ACTION_INFO_DEF + 2: - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_SHADOWFORGE_KEY, false); - break; - case GOSSIP_ACTION_INFO_DEF + 3: - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_SKELETON_KEY, false); - break; - case GOSSIP_ACTION_INFO_DEF + 4: - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_SHATTERED_HALLS_KEY, false); - break; - case GOSSIP_ACTION_INFO_DEF + 5: - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_THE_MASTERS_KEY, false); - break; - case GOSSIP_ACTION_INFO_DEF + 6: - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_VIOLET_HOLD_KEY, false); - break; - case GOSSIP_ACTION_INFO_DEF + 7: - player->CLOSE_GOSSIP_MENU(); - player->CastSpell(player, SPELL_ESSENCE_INFUSED_MOONSTONE, false); - break; - case GOSSIP_ACTION_INFO_DEF + 8: - player->CLOSE_GOSSIP_MENU(); - player->AddItem(ITEM_KEY_TO_THE_FOCUSING_IRIS, 1); - break; - case GOSSIP_ACTION_INFO_DEF + 9: - player->CLOSE_GOSSIP_MENU(); - player->AddItem(ITEM_HC_KEY_TO_THE_FOCUSING_IRIS, 1); - break; - } - return true; - } -}; - -/*###### ## npc_experience ######*/ @@ -2213,7 +2191,7 @@ public: float displacement = 0.7f; for (uint8 i = 0; i < 4; i++) - me->SummonGameObject(GetFireworkGameObjectId(), me->GetPositionX() + (i%2 == 0 ? displacement : -displacement), me->GetPositionY() + (i > 1 ? displacement : -displacement), me->GetPositionZ() + 4.0f, me->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 1); + me->SummonGameObject(GetFireworkGameObjectId(), me->GetPositionX() + (i % 2 == 0 ? displacement : -displacement), me->GetPositionY() + (i > 1 ? displacement : -displacement), me->GetPositionZ() + 4.0f, me->GetOrientation(), G3D::Quat(), 1); } else //me->CastSpell(me, GetFireworkSpell(me->GetEntry()), true); @@ -2578,6 +2556,7 @@ void AddSC_npcs_special() new npc_chicken_cluck(); new npc_dancing_flames(); new npc_torch_tossing_target_bunny_controller(); + new npc_midsummer_bunny_pole(); new npc_doctor(); new npc_injured_patient(); new npc_garments_of_quests(); @@ -2589,7 +2568,6 @@ void AddSC_npcs_special() new npc_training_dummy(); new npc_wormhole(); new npc_pet_trainer(); - new npc_locksmith(); new npc_experience(); new npc_firework(); new npc_spring_rabbit(); diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index f4d736ac675..0241221a2ac 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -52,6 +52,7 @@ #include "Realm/Realm.h" #include "DatabaseLoader.h" #include "AppenderDB.h" +#include "Metric.h" using namespace boost::program_options; namespace fs = boost::filesystem; @@ -196,6 +197,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(); @@ -295,6 +303,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 ccb784f8c2b..0513f2b0ab4 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -34,6 +34,7 @@ # AUCTION HOUSE BOT BUYER CONFIG # LOGGING SYSTEM SETTINGS # PACKET SPOOF PROTECTION SETTINGS +# METRIC SETTINGS # ################################################################################################### @@ -94,6 +95,12 @@ LogsDir = "" # Default: "127.0.0.1;3306;trinity;trinity;auth" - (LoginDatabaseInfo) # "127.0.0.1;3306;trinity;trinity;world" - (WorldDatabaseInfo) # "127.0.0.1;3306;trinity;trinity;characters" - (CharacterDatabaseInfo) +# +# Don't change hostname unless you are hosting mysql on a different machine, if you need help +# with configuration allowing to connect from diferent machine than the one running server +# search for TCE00016 on forum. +# Don't open port on firewall to external connections (it belongs to mysql, not to wow server). +# The username you choose must have permisions to create/alter/rename tables. LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth" WorldDatabaseInfo = "127.0.0.1;3306;trinity;trinity;world" @@ -1734,24 +1741,27 @@ Channel.RestrictedLfg = 1 # # ChatLevelReq.Channel -# Description: Level requirement for characters to be able to write in chat channels. -# Default: 1 - -ChatLevelReq.Channel = 1 - -# # ChatLevelReq.Whisper -# Description: Level requirement for characters to be able to whisper other characters. +# ChatLevelReq.Emote +# ChatLevelReq.Say +# ChatLevelReq.Yell +# Description: Level requirement for characters to be able to use chats. # Default: 1 +ChatLevelReq.Channel = 1 ChatLevelReq.Whisper = 1 +ChatLevelReq.Emote = 1 +ChatLevelReq.Say = 1 +ChatLevelReq.Yell = 1 # -# ChatLevelReq.Say -# Description: Level requirement for characters to be able to use say/yell/emote. +# PartyLevelReq +# Description: Minimum level at which players can invite to group, even if they aren't on +# the invitee friends list. (Players who are on that friend list can always +# invite despite having lower level) # Default: 1 -ChatLevelReq.Say = 1 +PartyLevelReq = 1 # # PreserveCustomChannels @@ -2444,6 +2454,14 @@ Battleground.RewardLoserHonorFirst = 5 Battleground.RewardLoserHonorLast = 5 # +# Battleground.ReportAFK +# Description: Number of reports needed to kick someone AFK from Battleground. +# Range: 1-9 +# Default: 3 + +Battleground.ReportAFK = 3 + +# ################################################################################################### ################################################################################################### @@ -3028,6 +3046,16 @@ NoGrayAggro.Above = 0 NoGrayAggro.Below = 0 # +# PreventRenameCharacterOnCustomization +# Description: If option is set to 1, player can not rename the character in character customization. +# Applies to all character customization commands. +# Default: 0 - (Disabled, character can be renamed in Character Customization) +# 1 - (Enabled, character can not be renamed in Character Customization) +# + +PreventRenameCharacterOnCustomization = 0 + +# ################################################################################################### ################################################################################################### @@ -3283,7 +3311,7 @@ AuctionHouseBot.Items.Amount.Yellow = 0 # Armor: 8 # Reagent: 1 # Projectile: 2 -# TradeGod: 10 +# TradeGood: 10 # Generic: 1 # Recipe: 6 # Quiver: 1 @@ -3608,6 +3636,7 @@ Logger.mmaps=3,Server #Logger.bg.arena=3,Console Server #Logger.bg.battlefield=3,Console Server #Logger.bg.battleground=3,Console Server +#Logger.bg.reportpvpafk=3,Console Server #Logger.chat.log=3,Console Server #Logger.calendar=3,Console Server #Logger.chat.system=3,Console Server @@ -3700,3 +3729,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 + +# +################################################################################################### |
