diff --git a/issue_template.md b/issue_template.md new file mode 100644 index 00000000000..87f2e21af41 --- /dev/null +++ b/issue_template.md @@ -0,0 +1,23 @@ +**Description**: + +**Current behaviour**: Tell us what happens + +**Expected behaviour**: Tell us what should happen instead + +**Steps to reproduce the problem**: + +1. +2. +3. + +**Branch(es)**: 335/6x + +**TC hash/commit**: + +**TDB version**: + +**Operating system**: + + +[//]: # (This template is for problem reports, for other type of reports edit it accordingly) +[//]: # (If this is a crash report, include the crashlog with https://gist.github.com/) diff --git a/pull_request_template.md b/pull_request_template.md new file mode 100644 index 00000000000..7438ed561cf --- /dev/null +++ b/pull_request_template.md @@ -0,0 +1,13 @@ +**Changes proposed**: + +- +- +- + +**Target branch(es)**: 335/6x + +**Issues addressed**: Fixes # + +**Tests performed**: (Does it build? Tested in-game?) + +**Known issues and TODO list**: diff --git a/sql/base/characters_database.sql b/sql/base/characters_database.sql index 99dd6378ab7..afac4485173 100644 --- a/sql/base/characters_database.sql +++ b/sql/base/characters_database.sql @@ -847,6 +847,7 @@ CREATE TABLE `character_instance` ( `guid` int(10) unsigned NOT NULL DEFAULT '0', `instance` int(10) unsigned NOT NULL DEFAULT '0', `permanent` tinyint(3) unsigned NOT NULL DEFAULT '0', + `extendState` TINYINT(2) UNSIGNED NOT NULL DEFAULT '1', PRIMARY KEY (`guid`,`instance`), KEY `instance` (`instance`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; @@ -2786,7 +2787,7 @@ CREATE TABLE `updates` ( LOCK TABLES `updates` WRITE; /*!40000 ALTER TABLE `updates` DISABLE KEYS */; -INSERT INTO `updates` VALUES ('2015_03_20_00_characters.sql','B761760804EA73BD297F296C5C1919687DF7191C','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_01_characters.sql','894F08B70449A5481FFAF394EE5571D7FC4D8A3A','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_02_characters.sql','97D7BE0CAADC79F3F11B9FD296B8C6CD40FE593B','ARCHIVED','2015-03-21 21:44:51',0),('2015_06_26_00_characters_335.sql','f8230a59a9e878a6f54f421d6621f1595bd93861','ARCHIVED','2015-03-21 21:44:51',0),('2015_09_28_00_characters_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-28 21:00:00',0),('2016_01_11_00_characters_from_335.sql','AAECE4BA6FDCF0C34A57FADD11DB68DAFD8AE9CD','ARCHIVED','2016-01-10 21:57:56',0),('2016_01_11_01_characters_from_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-29 22:23:32',0),('2016_01_11_02_characters_from_335.sql','4555A7F35C107E54C13D74D20F141039ED42943E','ARCHIVED','2015-10-29 20:32:37',0),('2016_01_14_from_335_2015_10_06_00_characters.sql','16842FDD7E8547F2260D3312F53EFF8761EFAB35','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_07_00_characters.sql','E15AB463CEBE321001D7BFDEA4B662FF618728FD','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_12_00_characters.sql','D6F9927BDED72AD0A81D6EC2C6500CBC34A39FA2','ARCHIVED','2015-10-13 01:06:25',0),('2016_01_14_from_335_2015_10_28_00_characters.sql','622A9CA8FCE690429EBE23BA071A37C7A007BF8B','ARCHIVED','2015-10-28 21:49:52',0),('2016_01_14_from_335_2015_11_03_00_characters.sql','CC045717B8FDD9733351E52A5302560CD08AAD57','ARCHIVED','2015-11-03 22:56:17',0); +INSERT INTO `updates` VALUES ('2015_03_20_00_characters.sql','B761760804EA73BD297F296C5C1919687DF7191C','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_01_characters.sql','894F08B70449A5481FFAF394EE5571D7FC4D8A3A','ARCHIVED','2015-03-21 21:44:15',0),('2015_03_20_02_characters.sql','97D7BE0CAADC79F3F11B9FD296B8C6CD40FE593B','ARCHIVED','2015-03-21 21:44:51',0),('2015_06_26_00_characters_335.sql','f8230a59a9e878a6f54f421d6621f1595bd93861','ARCHIVED','2015-03-21 21:44:51',0),('2015_09_28_00_characters_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-28 21:00:00',0),('2016_01_11_00_characters_from_335.sql','AAECE4BA6FDCF0C34A57FADD11DB68DAFD8AE9CD','ARCHIVED','2016-01-10 21:57:56',0),('2016_01_11_01_characters_from_335.sql','F8682A431D50E54BDC4AC0E7DBED21AE8AAB6AD4','ARCHIVED','2015-09-29 22:23:32',0),('2016_01_11_02_characters_from_335.sql','4555A7F35C107E54C13D74D20F141039ED42943E','ARCHIVED','2015-10-29 20:32:37',0),('2016_01_14_from_335_2015_10_06_00_characters.sql','16842FDD7E8547F2260D3312F53EFF8761EFAB35','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_07_00_characters.sql','E15AB463CEBE321001D7BFDEA4B662FF618728FD','ARCHIVED','2015-10-11 19:47:41',0),('2016_01_14_from_335_2015_10_12_00_characters.sql','D6F9927BDED72AD0A81D6EC2C6500CBC34A39FA2','ARCHIVED','2015-10-13 01:06:25',0),('2016_01_14_from_335_2015_10_28_00_characters.sql','622A9CA8FCE690429EBE23BA071A37C7A007BF8B','ARCHIVED','2015-10-28 21:49:52',0),('2016_01_14_from_335_2015_11_03_00_characters.sql','CC045717B8FDD9733351E52A5302560CD08AAD57','ARCHIVED','2015-11-03 22:56:17',0),('2016_02_21_from_335_2016_02_10_00_characters.sql','F1B4DA202819CABC7319A4470A2D224A34609E97','RELEASED','2016-02-10 00:00:00',0); /*!40000 ALTER TABLE `updates` ENABLE KEYS */; UNLOCK TABLES; diff --git a/sql/updates/characters/2016_02_21_from_335_2016_02_10_00_characters.sql b/sql/updates/characters/2016_02_21_from_335_2016_02_10_00_characters.sql new file mode 100644 index 00000000000..d49ed155bc8 --- /dev/null +++ b/sql/updates/characters/2016_02_21_from_335_2016_02_10_00_characters.sql @@ -0,0 +1,2 @@ +-- extend=0 is expired, extend=1 is normal, extend=2 is extended +ALTER TABLE `character_instance` ADD COLUMN `extendState` TINYINT(2) UNSIGNED NOT NULL DEFAULT '1'; diff --git a/src/common/Define.h b/src/common/Define.h index dd10298f65c..106d07f61a5 100644 --- a/src/common/Define.h +++ b/src/common/Define.h @@ -83,10 +83,16 @@ # define ATTR_NORETURN __attribute__((__noreturn__)) # define ATTR_PRINTF(F, V) __attribute__ ((__format__ (__printf__, F, V))) # define ATTR_DEPRECATED __attribute__((__deprecated__)) +# define TRINITY_CONSTEXPR constexpr #else //COMPILER != COMPILER_GNU # define ATTR_NORETURN # define ATTR_PRINTF(F, V) # define ATTR_DEPRECATED +#if _MSC_VER >= 1900 +# define TRINITY_CONSTEXPR constexpr +#else +# define TRINITY_CONSTEXPR +#endif #endif //COMPILER == COMPILER_GNU #define UI64FMTD "%" PRIu64 diff --git a/src/common/Threading/MPSCQueue.h b/src/common/Threading/MPSCQueue.h new file mode 100644 index 00000000000..09648b844be --- /dev/null +++ b/src/common/Threading/MPSCQueue.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008-2016 TrinityCore + * + * 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 . + */ + +#ifndef MPSCQueue_h__ +#define MPSCQueue_h__ + +#include +#include + +// C++ implementation of Dmitry Vyukov's lock free MPSC queue +// http://www.1024cores.net/home/lock-free-algorithms/queues/non-intrusive-mpsc-node-based-queue +template +class MPSCQueue +{ +public: + MPSCQueue() : _head(new Node()), _tail(_head.load(std::memory_order_relaxed)) + { + Node* front = _head.load(std::memory_order_relaxed); + front->Next.store(nullptr, std::memory_order_relaxed); + } + + ~MPSCQueue() + { + T* output; + while (this->Dequeue(output)) + ; + + Node* front = _head.load(std::memory_order_relaxed); + delete front; + } + + void Enqueue(T* input) + { + Node* node = new Node(input); + Node* prevHead = _head.exchange(node, std::memory_order_acq_rel); + prevHead->Next.store(node, std::memory_order_release); + } + + bool Dequeue(T*& result) + { + Node* tail = _tail.load(std::memory_order_relaxed); + Node* next = tail->Next.load(std::memory_order_acquire); + if (!next) + return false; + + result = next->Data; + _tail.store(next, std::memory_order_release); + delete tail; + return true; + } + +private: + struct Node + { + Node() = default; + explicit Node(T* data) : Data(data) { Next.store(nullptr, std::memory_order_relaxed); } + + T* Data; + std::atomic Next; + }; + + std::atomic _head; + std::atomic _tail; + + MPSCQueue(MPSCQueue const&) = delete; + MPSCQueue& operator=(MPSCQueue const&) = delete; +}; + +#endif // MPSCQueue_h__ diff --git a/src/server/shared/Containers.h b/src/common/Utilities/Containers.h similarity index 60% rename from src/server/shared/Containers.h rename to src/common/Utilities/Containers.h index 76708845442..7177582cf62 100644 --- a/src/server/shared/Containers.h +++ b/src/common/Utilities/Containers.h @@ -19,10 +19,11 @@ #define TRINITY_CONTAINERS_H #include "Define.h" +#include "Random.h" +#include +#include #include - -//! Because circular includes are bad -extern uint32 urand(uint32 min, uint32 max); +#include namespace Trinity { @@ -57,14 +58,64 @@ namespace Trinity list = listCopy; } - /* Select a random element from a container. Note: make sure you explicitly empty check the container */ - template typename C::value_type const& SelectRandomContainerElement(C const& container) + /* + * Select a random element from a container. + * + * Note: container cannot be empty + */ + template + typename C::value_type const& SelectRandomContainerElement(C const& container) { typename C::const_iterator it = container.begin(); std::advance(it, urand(0, container.size() - 1)); return *it; } + /* + * Select a random element from a container where each element has a different chance to be selected. + * + * @param container Container to select an element from + * @param weights Chances of each element to be selected, must be in the same order as elements in container. + * Caller is responsible for checking that sum of all weights is greater than 0. + * + * Note: container cannot be empty + */ + template + typename C::const_iterator SelectRandomWeightedContainerElement(C const& container, std::vector weights) + { + Trinity::discrete_distribution_param ddParam(weights.begin(), weights.end()); + std::discrete_distribution dd(ddParam); + typename C::const_iterator it = container.begin(); + std::advance(it, dd(SFMTEngine::Instance())); + return it; + } + + /* + * Select a random element from a container where each element has a different chance to be selected. + * + * @param container Container to select an element from + * @param weightExtractor Function retrieving chance of each element in container, expected to take an element of the container and returning a double + * + * Note: container cannot be empty + */ + template + typename C::const_iterator SelectRandomWeightedContainerElement(C const& container, Fn weightExtractor) + { + std::vector weights; + weights.reserve(container.size()); + double weightSum = 0.0; + for (auto itr = container.begin(); itr != container.end(); ++itr) + { + double weight = weightExtractor(*itr); + weights.push_back(weight); + weightSum += weight; + } + if (weightSum <= 0.0) + weights.assign(container.size(), 1.0); + + return SelectRandomWeightedContainerElement(container, weights); + } + /** * @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2) * diff --git a/src/common/Utilities/Random.cpp b/src/common/Utilities/Random.cpp new file mode 100644 index 00000000000..cc013110b01 --- /dev/null +++ b/src/common/Utilities/Random.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008-2015 TrinityCore + * + * 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 . + */ + +#include "Random.h" +#include "Common.h" +#include "Errors.h" +#include "SFMT.h" +#include + +static boost::thread_specific_ptr sfmtRand; + +static SFMTRand* GetRng() +{ + SFMTRand* rand = sfmtRand.get(); + + if (!rand) + { + rand = new SFMTRand(); + sfmtRand.reset(rand); + } + + return rand; +} + +int32 irand(int32 min, int32 max) +{ + ASSERT(max >= min); + return int32(GetRng()->IRandom(min, max)); +} + +uint32 urand(uint32 min, uint32 max) +{ + ASSERT(max >= min); + return GetRng()->URandom(min, max); +} + +uint32 urandms(uint32 min, uint32 max) +{ + ASSERT(max >= min); + ASSERT(INT_MAX / IN_MILLISECONDS >= max); + return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS); +} + +float frand(float min, float max) +{ + ASSERT(max >= min); + return float(GetRng()->Random() * (max - min) + min); +} + +uint32 rand32() +{ + return GetRng()->BRandom(); +} + +double rand_norm() +{ + return GetRng()->Random(); +} + +double rand_chance() +{ + return GetRng()->Random() * 100.0; +} + +SFMTEngine& SFMTEngine::Instance() +{ + static SFMTEngine engine; + return engine; +} diff --git a/src/common/Utilities/Random.h b/src/common/Utilities/Random.h new file mode 100644 index 00000000000..5610651a83b --- /dev/null +++ b/src/common/Utilities/Random.h @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2008-2015 TrinityCore + * + * 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 . + */ + +#ifndef Random_h__ +#define Random_h__ + +#include "Define.h" +#include +#include + +/* Return a random number in the range min..max. */ +int32 irand(int32 min, int32 max); + +/* Return a random number in the range min..max (inclusive). */ +uint32 urand(uint32 min, uint32 max); + +/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */ +uint32 urandms(uint32 min, uint32 max); + +/* Return a random number in the range 0 .. UINT32_MAX. */ +uint32 rand32(); + +/* Return a random number in the range min..max */ +float frand(float min, float max); + +/* Return a random double from 0.0 to 1.0 (exclusive). */ +double rand_norm(); + +/* Return a random double from 0.0 to 100.0 (exclusive). */ +double rand_chance(); + +/* Return true if a random roll fits in the specified chance (range 0-100). */ +inline bool roll_chance_f(float chance) +{ + return chance > rand_chance(); +} + +/* Return true if a random roll fits in the specified chance (range 0-100). */ +inline bool roll_chance_i(int chance) +{ + return chance > irand(0, 99); +} + +/* +* SFMT wrapper satisfying UniformRandomNumberGenerator concept for use in algorithms +*/ +class SFMTEngine +{ +public: + typedef uint32 result_type; + + static TRINITY_CONSTEXPR result_type min() { return std::numeric_limits::min(); } + static TRINITY_CONSTEXPR result_type max() { return std::numeric_limits::max(); } + result_type operator()() const { return rand32(); } + + static SFMTEngine& Instance(); +}; + +// Ugly, horrible, i don't even..., hack for VS2013 to work around missing discrete_distribution(iterator, iterator) constructor +namespace Trinity +{ +#if COMPILER == COMPILER_MICROSOFT && _MSC_VER <= 1800 + template + struct discrete_distribution_param : public std::discrete_distribution::param_type + { + typedef typename std::discrete_distribution::param_type base; + + template + discrete_distribution_param(InIt begin, InIt end) : base(_Noinit()) + { + this->_Pvec.assign(begin, end); + this->_Init(); + } + }; +#else + template + using discrete_distribution_param = typename std::discrete_distribution::param_type; +#endif +} + +#endif // Random_h__ diff --git a/src/common/Utilities/Util.cpp b/src/common/Utilities/Util.cpp index 388e05a9bbb..f36dcbadbd0 100644 --- a/src/common/Utilities/Util.cpp +++ b/src/common/Utilities/Util.cpp @@ -20,10 +20,8 @@ #include "Common.h" #include "CompilerDefs.h" #include "utf8.h" -#include "SFMT.h" #include "Errors.h" // for ASSERT #include -#include #if COMPILER == COMPILER_GNU #include @@ -31,61 +29,6 @@ #include #endif -static boost::thread_specific_ptr sfmtRand; - -static SFMTRand* GetRng() -{ - SFMTRand* rand = sfmtRand.get(); - - if (!rand) - { - rand = new SFMTRand(); - sfmtRand.reset(rand); - } - - return rand; -} - -int32 irand(int32 min, int32 max) -{ - ASSERT(max >= min); - return int32(GetRng()->IRandom(min, max)); -} - -uint32 urand(uint32 min, uint32 max) -{ - ASSERT(max >= min); - return GetRng()->URandom(min, max); -} - -uint32 urandms(uint32 min, uint32 max) -{ - ASSERT(max >= min); - ASSERT(INT_MAX/IN_MILLISECONDS >= max); - return GetRng()->URandom(min * IN_MILLISECONDS, max * IN_MILLISECONDS); -} - -float frand(float min, float max) -{ - ASSERT(max >= min); - return float(GetRng()->Random() * (max - min) + min); -} - -uint32 rand32() -{ - return GetRng()->BRandom(); -} - -double rand_norm() -{ - return GetRng()->Random(); -} - -double rand_chance() -{ - return GetRng()->Random() * 100.0; -} - Tokenizer::Tokenizer(const std::string &src, const char sep, uint32 vectorReserve) { m_str = new char[src.length() + 1]; diff --git a/src/common/Utilities/Util.h b/src/common/Utilities/Util.h index 60f5a00e59d..b28938e55ea 100644 --- a/src/common/Utilities/Util.h +++ b/src/common/Utilities/Util.h @@ -21,6 +21,7 @@ #include "Define.h" #include "Errors.h" +#include "Random.h" #include #include @@ -77,39 +78,6 @@ std::string secsToTimeString(uint64 timeInSecs, bool shortText = false, bool hou uint32 TimeStringToSecs(const std::string& timestring); std::string TimeToTimestampStr(time_t t); -/* Return a random number in the range min..max. */ -int32 irand(int32 min, int32 max); - -/* Return a random number in the range min..max (inclusive). */ -uint32 urand(uint32 min, uint32 max); - -/* Return a random millisecond value between min and max seconds. Functionally equivalent to urand(min*IN_MILLISECONDS, max*IN_MILLISECONDS). */ -uint32 urandms(uint32 min, uint32 max); - -/* Return a random number in the range 0 .. UINT32_MAX. */ -uint32 rand32(); - -/* Return a random number in the range min..max */ -float frand(float min, float max); - -/* Return a random double from 0.0 to 1.0 (exclusive). */ -double rand_norm(); - -/* Return a random double from 0.0 to 100.0 (exclusive). */ -double rand_chance(); - -/* Return true if a random roll fits in the specified chance (range 0-100). */ -inline bool roll_chance_f(float chance) -{ - return chance > rand_chance(); -} - -/* Return true if a random roll fits in the specified chance (range 0-100). */ -inline bool roll_chance_i(int chance) -{ - return chance > irand(0, 99); -} - inline void ApplyPercentModFloatVar(float& var, float val, bool apply) { if (val == -100.0f) // prevent set var to zero diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index 0c812ebd494..0618ec437b6 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -68,11 +68,14 @@ bool StartDB(); void StopDB(); void SignalHandler(const boost::system::error_code& error, int signalNumber); void KeepDatabaseAliveHandler(const boost::system::error_code& error); +void BanExpiryHandler(boost::system::error_code const& error); variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService); boost::asio::io_service* _ioService; boost::asio::deadline_timer* _dbPingTimer; uint32 _dbPingInterval; +boost::asio::deadline_timer* _banExpiryCheckTimer; +uint32 _banExpiryCheckInterval; LoginDatabaseWorkerPool LoginDatabase; int main(int argc, char** argv) @@ -169,6 +172,11 @@ int main(int argc, char** argv) _dbPingTimer->expires_from_now(boost::posix_time::minutes(_dbPingInterval)); _dbPingTimer->async_wait(KeepDatabaseAliveHandler); + _banExpiryCheckInterval = sConfigMgr->GetIntDefault("BanExpiryCheckInterval", 60); + _banExpiryCheckTimer = new boost::asio::deadline_timer(*_ioService); + _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval)); + _banExpiryCheckTimer->async_wait(BanExpiryHandler); + #if PLATFORM == PLATFORM_WINDOWS if (m_ServiceStatus != -1) { @@ -181,6 +189,7 @@ int main(int argc, char** argv) // Start the io service worker loop _ioService->run(); + _banExpiryCheckTimer->cancel(); _dbPingTimer->cancel(); sAuthSocketMgr.StopNetwork(); @@ -192,6 +201,7 @@ int main(int argc, char** argv) signals.cancel(); + delete _banExpiryCheckTimer; delete _dbPingTimer; delete _ioService; return 0; @@ -242,6 +252,18 @@ void KeepDatabaseAliveHandler(const boost::system::error_code& error) } } +void BanExpiryHandler(boost::system::error_code const& error) +{ + if (!error) + { + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); + LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); + + _banExpiryCheckTimer->expires_from_now(boost::posix_time::seconds(_banExpiryCheckInterval)); + _banExpiryCheckTimer->async_wait(BanExpiryHandler); + } +} + #if PLATFORM == PLATFORM_WINDOWS void ServiceStatusWatcher(boost::system::error_code const& error) { diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index f3725383065..b909694c878 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -43,12 +43,6 @@ enum eAuthCmd XFER_CANCEL = 0x34 }; -enum eStatus -{ - STATUS_CONNECTED = 0, - STATUS_AUTHED -}; - #pragma pack(push, 1) typedef struct AUTH_LOGON_CHALLENGE_C @@ -115,11 +109,10 @@ enum class BufferSizes : uint32 SRP_6_S = 0x20, }; +#define MAX_ACCEPTED_CHALLENGE_SIZE (sizeof(AUTH_LOGON_CHALLENGE_C) + 16) + #define AUTH_LOGON_CHALLENGE_INITIAL_SIZE 4 #define REALM_LIST_PACKET_SIZE 5 -#define XFER_ACCEPT_SIZE 1 -#define XFER_RESUME_SIZE 9 -#define XFER_CANCEL_SIZE 1 std::unordered_map AuthSession::InitHandlers() { @@ -130,15 +123,98 @@ std::unordered_map AuthSession::InitHandlers() handlers[AUTH_RECONNECT_CHALLENGE] = { STATUS_CONNECTED, AUTH_LOGON_CHALLENGE_INITIAL_SIZE, &AuthSession::HandleReconnectChallenge }; handlers[AUTH_RECONNECT_PROOF] = { STATUS_CONNECTED, sizeof(AUTH_RECONNECT_PROOF_C), &AuthSession::HandleReconnectProof }; handlers[REALM_LIST] = { STATUS_AUTHED, REALM_LIST_PACKET_SIZE, &AuthSession::HandleRealmList }; - handlers[XFER_ACCEPT] = { STATUS_AUTHED, XFER_ACCEPT_SIZE, &AuthSession::HandleXferAccept }; - handlers[XFER_RESUME] = { STATUS_AUTHED, XFER_RESUME_SIZE, &AuthSession::HandleXferResume }; - handlers[XFER_CANCEL] = { STATUS_AUTHED, XFER_CANCEL_SIZE, &AuthSession::HandleXferCancel }; return handlers; } std::unordered_map const Handlers = AuthSession::InitHandlers(); +void AccountInfo::LoadResult(Field* fields) +{ + // 0 1 2 3 4 5 6 + //SELECT a.id, a.username, a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, + // 7 8 9 10 11 12 + // ab.unbandate = ab.bandate, aa.gmlevel, a.token_key, a.sha_pass_hash, a.v, a.s + //FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ? + + Id = fields[0].GetUInt32(); + Login = fields[1].GetString(); + IsLockedToIP = fields[2].GetBool(); + LockCountry = fields[3].GetString(); + LastIP = fields[4].GetString(); + FailedLogins = fields[5].GetUInt32(); + IsBanned = fields[6].GetUInt64() != 0; + IsPermanenetlyBanned = fields[7].GetUInt64() != 0; + SecurityLevel = AccountTypes(fields[8].GetUInt8()); +} + +AuthSession::AuthSession(tcp::socket&& socket) : Socket(std::move(socket)), +_sentChallenge(false), _sentProof(false), +_status(STATUS_CONNECTED), _build(0), _expversion(0) +{ + N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); + g.SetDword(7); +} + +void AuthSession::Start() +{ + std::string ip_address = GetRemoteIpAddress().to_string(); + TC_LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str()); + + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); + stmt->setString(0, ip_address); + stmt->setUInt32(1, inet_addr(ip_address.c_str())); + + _queryCallback = std::bind(&AuthSession::CheckIpCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); +} + +bool AuthSession::Update() +{ + if (!AuthSocket::Update()) + return false; + + if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) + { + auto callback = _queryCallback; + _queryCallback = nullptr; + callback(_queryFuture.get()); + } + + return true; +} + +void AuthSession::CheckIpCallback(PreparedQueryResult result) +{ + if (result) + { + bool banned = false; + do + { + Field* fields = result->Fetch(); + if (fields[0].GetUInt64() != 0) + banned = true; + + if (!fields[1].GetString().empty()) + _ipCountry = fields[1].GetString(); + + } while (result->NextRow()); + + if (banned) + { + ByteBuffer pkt; + pkt << uint8(AUTH_LOGON_CHALLENGE); + pkt << uint8(0x00); + pkt << uint8(WOW_FAIL_BANNED); + SendPacket(pkt); + TC_LOG_DEBUG("session", "[AuthSession::CheckIpCallback] Banned ip '%s:%d' tries to login!", GetRemoteIpAddress().to_string().c_str(), GetRemotePort()); + return; + } + } + + AsyncRead(); +} + void AuthSession::ReadHandler() { MessageBuffer& packet = GetReadBuffer(); @@ -153,6 +229,12 @@ void AuthSession::ReadHandler() break; } + if (_status != itr->second.status) + { + CloseSocket(); + return; + } + uint16 size = uint16(itr->second.packetSize); if (packet.GetActiveSize() < size) break; @@ -161,12 +243,17 @@ void AuthSession::ReadHandler() { sAuthLogonChallenge_C* challenge = reinterpret_cast(packet.GetReadPointer()); size += challenge->size; + if (size > MAX_ACCEPTED_CHALLENGE_SIZE) + { + CloseSocket(); + return; + } } if (packet.GetActiveSize() < size) break; - if (!(*this.*Handlers.at(cmd).handler)()) + if (!(*this.*itr->second.handler)()) { CloseSocket(); return; @@ -187,235 +274,211 @@ void AuthSession::SendPacket(ByteBuffer& packet) { MessageBuffer buffer; buffer.Write(packet.contents(), packet.size()); - - std::unique_lock guard(_writeLock); - - QueuePacket(std::move(buffer), guard); + QueuePacket(std::move(buffer)); } } bool AuthSession::HandleLogonChallenge() { + if (_sentChallenge) + return false; + + _sentChallenge = true; + sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer()); + if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len) + return false; - //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I); + std::string login((const char*)challenge->I, challenge->I_len); + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] '%s'", login.c_str()); - ByteBuffer pkt; + if (_queryCallback) + { + ByteBuffer pkt; + pkt << uint8(AUTH_LOGON_CHALLENGE); + pkt << uint8(0x00); + pkt << uint8(WOW_FAIL_DB_BUSY); + SendPacket(pkt); + + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] %s attempted to log too quick after previous attempt!", login.c_str()); + return true; + } - _login.assign((const char*)challenge->I, challenge->I_len); _build = challenge->build; _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); - _os = (const char*)challenge->os; - - if (_os.size() > 4) - return false; + std::array os; + os.fill('\0'); + memcpy(os.data(), challenge->os, sizeof(challenge->os)); + _os = os.data(); // Restore string order as its byte order is reversed std::reverse(_os.begin(), _os.end()); + _localizationName.resize(4); + for (int i = 0; i < 4; ++i) + _localizationName[i] = challenge->country[4 - i - 1]; + + // Get the account details from the account table + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE); + stmt->setString(0, login); + + _queryCallback = std::bind(&AuthSession::LogonChallengeCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); + return true; +} + +void AuthSession::LogonChallengeCallback(PreparedQueryResult result) +{ + ByteBuffer pkt; pkt << uint8(AUTH_LOGON_CHALLENGE); pkt << uint8(0x00); - // Verify that this IP is not in the ip_banned table - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); + if (!result) + { + pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); + SendPacket(pkt); + return; + } + + Field* fields = result->Fetch(); + + _accountInfo.LoadResult(fields); std::string ipAddress = GetRemoteIpAddress().to_string(); uint16 port = GetRemotePort(); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_BANNED); - stmt->setString(0, ipAddress); - PreparedQueryResult result = LoginDatabase.Query(stmt); - if (result) + // If the IP is 'locked', check that the player comes indeed from the correct IP address + if (_accountInfo.IsLockedToIP) { - pkt << uint8(WOW_FAIL_BANNED); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned ip tries to login!", ipAddress.c_str(), port); + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountInfo.Login.c_str(), _accountInfo.LastIP.c_str(), ipAddress.c_str()); + if (_accountInfo.LastIP != ipAddress) + { + pkt << uint8(WOW_FAIL_LOCKED_ENFORCED); + SendPacket(pkt); + return; + } } else { - // Get the account details from the account table - // No SQL injection (prepared statement) - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGONCHALLENGE); - stmt->setString(0, _login); - - PreparedQueryResult res2 = LoginDatabase.Query(stmt); - if (res2) + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _accountInfo.Login.c_str()); + if (_accountInfo.LockCountry.empty() || _accountInfo.LockCountry == "00") + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _accountInfo.Login.c_str()); + else if (!_accountInfo.LockCountry.empty() && !_ipCountry.empty()) { - Field* fields = res2->Fetch(); - - // If the IP is 'locked', check that the player comes indeed from the correct IP address - bool locked = false; - if (fields[2].GetUInt8() == 1) // if ip is locked + TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _accountInfo.Login.c_str(), _accountInfo.LockCountry.c_str(), _ipCountry.c_str()); + if (_ipCountry != _accountInfo.LockCountry) { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to IP - '%s'", _login.c_str(), fields[4].GetCString()); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Player address is '%s'", ipAddress.c_str()); - - if (strcmp(fields[4].GetCString(), ipAddress.c_str()) != 0) - { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP differs"); - pkt << uint8(WOW_FAIL_LOCKED_ENFORCED); - locked = true; - } - else - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account IP matches"); - } - else - { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to ip", _login.c_str()); - std::string accountCountry = fields[3].GetString(); - if (accountCountry.empty() || accountCountry == "00") - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is not locked to country", _login.c_str()); - else if (!accountCountry.empty()) - { - uint32 ip = inet_addr(ipAddress.c_str()); - EndianConvertReverse(ip); - - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_LOGON_COUNTRY); - stmt->setUInt32(0, ip); - if (PreparedQueryResult sessionCountryQuery = LoginDatabase.Query(stmt)) - { - std::string loginCountry = (*sessionCountryQuery)[0].GetString(); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account '%s' is locked to country: '%s' Player country is '%s'", _login.c_str(), - accountCountry.c_str(), loginCountry.c_str()); - - if (loginCountry != accountCountry) - { - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country differs."); - pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK); - locked = true; - } - else - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] Account country matches"); - } - else - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] IP2NATION Table empty"); - } - } - - if (!locked) - { - //set expired bans to inactive - LoginDatabase.DirectExecute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); - - // If the account is banned, reject the logon attempt - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_BANNED); - stmt->setUInt32(0, fields[1].GetUInt32()); - PreparedQueryResult banresult = LoginDatabase.Query(stmt); - if (banresult) - { - if ((*banresult)[0].GetUInt32() == (*banresult)[1].GetUInt32()) - { - pkt << uint8(WOW_FAIL_BANNED); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(), - port, _login.c_str()); - } - else - { - pkt << uint8(WOW_FAIL_SUSPENDED); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", - ipAddress.c_str(), port, _login.c_str()); - } - } - else - { - // Get the password from the account table, upper it, and make the SRP6 calculation - std::string rI = fields[0].GetString(); - - // Don't calculate (v, s) if there are already some in the database - std::string databaseV = fields[6].GetString(); - std::string databaseS = fields[7].GetString(); - - TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str()); - - // multiply with 2 since bytes are stored as hexstring - if (databaseV.size() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 2) - SetVSFields(rI); - else - { - s.SetHexStr(databaseS.c_str()); - v.SetHexStr(databaseV.c_str()); - } - - b.SetRand(19 * 8); - BigNumber gmod = g.ModExp(b, N); - B = ((v * 3) + gmod) % N; - - ASSERT(gmod.GetNumBytes() <= 32); - - BigNumber unk3; - unk3.SetRand(16 * 8); - - // Fill the response packet with the result - if (fields[9].GetUInt32() && AuthHelper::IsBuildSupportingBattlenet(_build)) - pkt << uint8(WOW_FAIL_USE_BATTLENET); - else if (AuthHelper::IsAcceptedClientBuild(_build)) - pkt << uint8(WOW_SUCCESS); - else - pkt << uint8(WOW_FAIL_VERSION_INVALID); - - // B may be calculated < 32B so we force minimal length to 32B - pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes - pkt << uint8(1); - pkt.append(g.AsByteArray(1).get(), 1); - pkt << uint8(32); - pkt.append(N.AsByteArray(32).get(), 32); - pkt.append(s.AsByteArray(int32(BufferSizes::SRP_6_S)).get(), size_t(BufferSizes::SRP_6_S)); // 32 bytes - pkt.append(unk3.AsByteArray(16).get(), 16); - uint8 securityFlags = 0; - - // Check if token is used - _tokenKey = fields[8].GetString(); - if (!_tokenKey.empty()) - securityFlags = 4; - - pkt << uint8(securityFlags); // security flags (0x0...0x04) - - if (securityFlags & 0x01) // PIN input - { - pkt << uint32(0); - pkt << uint64(0) << uint64(0); // 16 bytes hash? - } - - if (securityFlags & 0x02) // Matrix input - { - pkt << uint8(0); - pkt << uint8(0); - pkt << uint8(0); - pkt << uint8(0); - pkt << uint64(0); - } - - if (securityFlags & 0x04) // Security token input - pkt << uint8(1); - - uint8 secLevel = fields[5].GetUInt8(); - _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; - - _localizationName.resize(4); - for (int i = 0; i < 4; ++i) - _localizationName[i] = challenge->country[4 - i - 1]; - - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", - ipAddress.c_str(), port, _login.c_str(), - challenge->country[3], challenge->country[2], challenge->country[1], challenge->country[0], - GetLocaleByName(_localizationName) - ); - } + pkt << uint8(WOW_FAIL_UNLOCKABLE_LOCK); + SendPacket(pkt); + return; } } - else //no account - pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); } + // If the account is banned, reject the logon attempt + if (_accountInfo.IsBanned) + { + if (_accountInfo.IsPermanenetlyBanned) + { + pkt << uint8(WOW_FAIL_BANNED); + SendPacket(pkt); + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str()); + return; + } + else + { + pkt << uint8(WOW_FAIL_SUSPENDED); + SendPacket(pkt); + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] Temporarily banned account %s tried to login!", ipAddress.c_str(), port, _accountInfo.Login.c_str()); + return; + } + } + + // Get the password from the account table, upper it, and make the SRP6 calculation + std::string rI = fields[10].GetString(); + + // Don't calculate (v, s) if there are already some in the database + std::string databaseV = fields[11].GetString(); + std::string databaseS = fields[12].GetString(); + + TC_LOG_DEBUG("network", "database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str()); + + // multiply with 2 since bytes are stored as hexstring + if (databaseV.size() != size_t(BufferSizes::SRP_6_V) * 2 || databaseS.size() != size_t(BufferSizes::SRP_6_S) * 2) + SetVSFields(rI); + else + { + s.SetHexStr(databaseS.c_str()); + v.SetHexStr(databaseV.c_str()); + } + + b.SetRand(19 * 8); + BigNumber gmod = g.ModExp(b, N); + B = ((v * 3) + gmod) % N; + + ASSERT(gmod.GetNumBytes() <= 32); + + BigNumber unk3; + unk3.SetRand(16 * 8); + + // Fill the response packet with the result + if (fields[13].GetUInt32() && AuthHelper::IsBuildSupportingBattlenet(_build)) + pkt << uint8(WOW_FAIL_USE_BATTLENET); + else if (AuthHelper::IsAcceptedClientBuild(_build)) + pkt << uint8(WOW_SUCCESS); + else + pkt << uint8(WOW_FAIL_VERSION_INVALID); + + // B may be calculated < 32B so we force minimal length to 32B + pkt.append(B.AsByteArray(32).get(), 32); // 32 bytes + pkt << uint8(1); + pkt.append(g.AsByteArray(1).get(), 1); + pkt << uint8(32); + pkt.append(N.AsByteArray(32).get(), 32); + pkt.append(s.AsByteArray(int32(BufferSizes::SRP_6_S)).get(), size_t(BufferSizes::SRP_6_S)); // 32 bytes + pkt.append(unk3.AsByteArray(16).get(), 16); + uint8 securityFlags = 0; + + // Check if token is used + _tokenKey = fields[9].GetString(); + if (!_tokenKey.empty()) + securityFlags = 4; + + pkt << uint8(securityFlags); // security flags (0x0...0x04) + + if (securityFlags & 0x01) // PIN input + { + pkt << uint32(0); + pkt << uint64(0) << uint64(0); // 16 bytes hash? + } + + if (securityFlags & 0x02) // Matrix input + { + pkt << uint8(0); + pkt << uint8(0); + pkt << uint8(0); + pkt << uint8(0); + pkt << uint64(0); + } + + if (securityFlags & 0x04) // Security token input + pkt << uint8(1); + + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s is using '%s' locale (%u)", + ipAddress.c_str(), port, _accountInfo.Login.c_str(), _localizationName.c_str(), GetLocaleByName(_localizationName)); + SendPacket(pkt); - return true; } // Logon Proof command handler bool AuthSession::HandleLogonProof() { - TC_LOG_DEBUG("server.authserver", "Entering _HandleLogonProof"); + if (_sentProof) + return false; + + _sentProof = true; + // Read the packet sAuthLogonProof_C *logonProof = reinterpret_cast(GetReadBuffer().GetReadPointer()); @@ -489,7 +552,7 @@ bool AuthSession::HandleLogonProof() t3.SetBinary(hash, 20); sha.Initialize(); - sha.UpdateData(_login); + sha.UpdateData(_accountInfo.Login); sha.Finalize(); uint8 t4[SHA_DIGEST_LENGTH]; memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH); @@ -505,7 +568,7 @@ bool AuthSession::HandleLogonProof() // Check if SRP6 results match (password is correct), else send an error if (!memcmp(M.AsByteArray(sha.GetLength()).get(), logonProof->M1, 20)) { - TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str()); + TC_LOG_DEBUG("server.authserver", "'%s:%d' User '%s' successfully authenticated", GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str()); // Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account PreparedStatement *stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_LOGONPROOF); @@ -513,7 +576,7 @@ bool AuthSession::HandleLogonProof() stmt->setString(1, GetRemoteIpAddress().to_string().c_str()); stmt->setUInt32(2, GetLocaleByName(_localizationName)); stmt->setString(3, _os); - stmt->setString(4, _login); + stmt->setString(4, _accountInfo.Login); LoginDatabase.DirectExecute(stmt); // Finish SRP6 and send the final result to the client @@ -528,6 +591,7 @@ bool AuthSession::HandleLogonProof() std::string token(reinterpret_cast(GetReadBuffer().GetReadPointer() + sizeof(sAuthLogonProof_C) + sizeof(size)), size); GetReadBuffer().ReadCompleted(sizeof(size) + size); uint32 validToken = TOTP::GenerateToken(_tokenKey.c_str()); + _tokenKey.clear(); uint32 incomingToken = atoi(token.c_str()); if (validToken != incomingToken) { @@ -568,7 +632,7 @@ bool AuthSession::HandleLogonProof() } SendPacket(packet); - _isAuthenticated = true; + _status = STATUS_AUTHED; } else { @@ -580,7 +644,7 @@ bool AuthSession::HandleLogonProof() SendPacket(packet); TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s tried to login with invalid password!", - GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str()); + GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str()); uint32 MaxWrongPassCount = sConfigMgr->GetIntDefault("WrongPass.MaxCount", 0); @@ -588,7 +652,7 @@ bool AuthSession::HandleLogonProof() if (sConfigMgr->GetBoolDefault("WrongPass.Logging", false)) { PreparedStatement* logstmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_FALP_IP_LOGGING); - logstmt->setString(0, _login); + logstmt->setString(0, _accountInfo.Login); logstmt->setString(1, GetRemoteIpAddress().to_string()); logstmt->setString(2, "Logged on failed AccountLogin due wrong password"); @@ -599,42 +663,33 @@ bool AuthSession::HandleLogonProof() { //Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_FAILEDLOGINS); - stmt->setString(0, _login); + stmt->setString(0, _accountInfo.Login); LoginDatabase.Execute(stmt); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_FAILEDLOGINS); - stmt->setString(0, _login); - - if (PreparedQueryResult loginfail = LoginDatabase.Query(stmt)) + if (_accountInfo.FailedLogins >= MaxWrongPassCount) { - uint32 failed_logins = (*loginfail)[1].GetUInt32(); + uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600); + bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false); - if (failed_logins >= MaxWrongPassCount) + if (WrongPassBanType) { - uint32 WrongPassBanTime = sConfigMgr->GetIntDefault("WrongPass.BanTime", 600); - bool WrongPassBanType = sConfigMgr->GetBoolDefault("WrongPass.BanType", false); + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED); + stmt->setUInt32(0, _accountInfo.Id); + stmt->setUInt32(1, WrongPassBanTime); + LoginDatabase.Execute(stmt); - if (WrongPassBanType) - { - uint32 acc_id = (*loginfail)[0].GetUInt32(); - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED); - stmt->setUInt32(0, acc_id); - stmt->setUInt32(1, WrongPassBanTime); - LoginDatabase.Execute(stmt); + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", + GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _accountInfo.Login.c_str(), WrongPassBanTime, _accountInfo.FailedLogins); + } + else + { + stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED); + stmt->setString(0, GetRemoteIpAddress().to_string()); + stmt->setUInt32(1, WrongPassBanTime); + LoginDatabase.Execute(stmt); - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] account %s got banned for '%u' seconds because it failed to authenticate '%u' times", - GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str(), WrongPassBanTime, failed_logins); - } - else - { - stmt = LoginDatabase.GetPreparedStatement(LOGIN_INS_IP_AUTO_BANNED); - stmt->setString(0, GetRemoteIpAddress().to_string()); - stmt->setUInt32(1, WrongPassBanTime); - LoginDatabase.Execute(stmt); - - TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP got banned for '%u' seconds because account %s failed to authenticate '%u' times", - GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), WrongPassBanTime, _login.c_str(), failed_logins); - } + TC_LOG_DEBUG("server.authserver", "'%s:%d' [AuthChallenge] IP got banned for '%u' seconds because account %s failed to authenticate '%u' times", + GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), WrongPassBanTime, _accountInfo.Login.c_str(), _accountInfo.FailedLogins); } } } @@ -645,61 +700,88 @@ bool AuthSession::HandleLogonProof() bool AuthSession::HandleReconnectChallenge() { - TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectChallenge"); - sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer()); - - //TC_LOG_DEBUG("server.authserver", "[AuthChallenge] got full packet, %#04x bytes", challenge->size); - TC_LOG_DEBUG("server.authserver", "[AuthChallenge] name(%d): '%s'", challenge->I_len, challenge->I); - - _login.assign((const char*)challenge->I, challenge->I_len); - - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_SESSIONKEY); - stmt->setString(0, _login); - PreparedQueryResult result = LoginDatabase.Query(stmt); - - // Stop if the account is not found - if (!result) - { - TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login and we cannot find his session key in the database.", - GetRemoteIpAddress().to_string().c_str(), GetRemotePort(), _login.c_str()); + if (_sentChallenge) return false; + + _sentChallenge = true; + + sAuthLogonChallenge_C* challenge = reinterpret_cast(GetReadBuffer().GetReadPointer()); + if (challenge->size - (sizeof(sAuthLogonChallenge_C) - AUTH_LOGON_CHALLENGE_INITIAL_SIZE - 1) != challenge->I_len) + return false; + + std::string login((const char*)challenge->I, challenge->I_len); + TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] '%s'", login.c_str()); + + if (_queryCallback) + { + ByteBuffer pkt; + pkt << uint8(AUTH_RECONNECT_CHALLENGE); + pkt << uint8(WOW_FAIL_DB_BUSY); + SendPacket(pkt); + + TC_LOG_DEBUG("server.authserver", "[ReconnectChallenge] %s attempted to log too quick after previous attempt!", login.c_str()); + return true; } - // Reinitialize build, expansion and the account securitylevel _build = challenge->build; _expversion = uint8(AuthHelper::IsPostBCAcceptedClientBuild(_build) ? POST_BC_EXP_FLAG : (AuthHelper::IsPreBCAcceptedClientBuild(_build) ? PRE_BC_EXP_FLAG : NO_VALID_EXP_FLAG)); - _os = (const char*)challenge->os; - - if (_os.size() > 4) - return false; + std::array os; + os.fill('\0'); + memcpy(os.data(), challenge->os, sizeof(challenge->os)); + _os = os.data(); // Restore string order as its byte order is reversed std::reverse(_os.begin(), _os.end()); - Field* fields = result->Fetch(); - uint8 secLevel = fields[2].GetUInt8(); - _accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR; + _localizationName.resize(4); + for (int i = 0; i < 4; ++i) + _localizationName[i] = challenge->country[4 - i - 1]; - K.SetHexStr((*result)[0].GetCString()); + // Get the account details from the account table + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_RECONNECTCHALLENGE); + stmt->setString(0, login); - // Sending response + _queryCallback = std::bind(&AuthSession::ReconnectChallengeCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); + return true; +} + +void AuthSession::ReconnectChallengeCallback(PreparedQueryResult result) +{ ByteBuffer pkt; pkt << uint8(AUTH_RECONNECT_CHALLENGE); - pkt << uint8(0x00); + + if (!result) + { + pkt << uint8(WOW_FAIL_UNKNOWN_ACCOUNT); + SendPacket(pkt); + return; + } + + Field* fields = result->Fetch(); + + _accountInfo.LoadResult(fields); + K.SetHexStr(fields[9].GetCString()); _reconnectProof.SetRand(16 * 8); - pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random + + pkt << uint8(WOW_SUCCESS); + pkt.append(_reconnectProof.AsByteArray(16).get(), 16); // 16 bytes random pkt << uint64(0x00) << uint64(0x00); // 16 bytes zeros SendPacket(pkt); - - return true; } + bool AuthSession::HandleReconnectProof() { TC_LOG_DEBUG("server.authserver", "Entering _HandleReconnectProof"); + if (_sentProof) + return false; + + _sentProof = true; + sAuthReconnectProof_C *reconnectProof = reinterpret_cast(GetReadBuffer().GetReadPointer()); - if (_login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) + if (_accountInfo.Login.empty() || !_reconnectProof.GetNumBytes() || !K.GetNumBytes()) return false; BigNumber t1; @@ -707,7 +789,7 @@ bool AuthSession::HandleReconnectProof() SHA1Hash sha; sha.Initialize(); - sha.UpdateData(_login); + sha.UpdateData(_accountInfo.Login); sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL); sha.Finalize(); @@ -719,13 +801,13 @@ bool AuthSession::HandleReconnectProof() pkt << uint8(0x00); pkt << uint16(0x00); // 2 bytes zeros SendPacket(pkt); - _isAuthenticated = true; + _status = STATUS_AUTHED; return true; } else { TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login, but session is invalid.", GetRemoteIpAddress().to_string().c_str(), - GetRemotePort(), _login.c_str()); + GetRemotePort(), _accountInfo.Login.c_str()); return false; } } @@ -734,20 +816,31 @@ bool AuthSession::HandleRealmList() { TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList"); - // Get the user id (else close the connection) - // No SQL injection (prepared statement) - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME); - stmt->setString(0, _login); - PreparedQueryResult result = LoginDatabase.Query(stmt); - if (!result) + if (_queryCallback) { - TC_LOG_ERROR("server.authserver", "'%s:%d' [ERROR] user %s tried to login but we cannot find him in the database.", GetRemoteIpAddress().to_string().c_str(), - GetRemotePort(), _login.c_str()); + TC_LOG_DEBUG("server.authserver", "[RealmList] %s attempted to get realmlist too quick after previous attempt!", _accountInfo.Login.c_str()); return false; } - Field* fields = result->Fetch(); - uint32 id = fields[0].GetUInt32(); + PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS); + stmt->setUInt32(0, _accountInfo.Id); + + _queryCallback = std::bind(&AuthSession::RealmListCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); + return true; +} + +void AuthSession::RealmListCallback(PreparedQueryResult result) +{ + std::map characterCounts; + if (result) + { + do + { + Field* fields = result->Fetch(); + characterCounts[fields[0].GetUInt32()] = fields[1].GetUInt8(); + } while (result->NextRow()); + } // Update realm list if need sRealmList->UpdateIfNeed(); @@ -784,25 +877,17 @@ bool AuthSession::HandleRealmList() name = ss.str(); } - uint8 lock = (realm.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0; + uint8 lock = (realm.allowedSecurityLevel > _accountInfo.SecurityLevel) ? 1 : 0; - uint8 AmountOfCharacters = 0; - stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_NUM_CHARS_ON_REALM); - stmt->setUInt32(0, realm.m_ID); - stmt->setUInt32(1, id); - result = LoginDatabase.Query(stmt); - if (result) - AmountOfCharacters = (*result)[0].GetUInt8(); - - pkt << realm.icon; // realm type + pkt << uint8(realm.icon); // realm type if (_expversion & POST_BC_EXP_FLAG) // only 2.x and 3.x clients - pkt << lock; // if 1, then realm locked + pkt << uint8(lock); // if 1, then realm locked pkt << uint8(flag); // RealmFlags pkt << name; pkt << boost::lexical_cast(realm.GetAddressForClient(GetRemoteIpAddress())); - pkt << realm.populationLevel; - pkt << AmountOfCharacters; - pkt << realm.timezone; // realm category + pkt << float(realm.populationLevel); + pkt << uint8(characterCounts[realm.m_ID]); + pkt << uint8(realm.timezone); // realm category if (_expversion & POST_BC_EXP_FLAG) // 2.x and 3.x clients pkt << uint8(realm.m_ID); else @@ -844,32 +929,6 @@ bool AuthSession::HandleRealmList() hdr.append(RealmListSizeBuffer); // append RealmList's size buffer hdr.append(pkt); // append realms in the realmlist SendPacket(hdr); - return true; -} - -// Resume patch transfer -bool AuthSession::HandleXferResume() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleXferResume"); - //uint8 - //uint64 - return true; -} - -// Cancel patch transfer -bool AuthSession::HandleXferCancel() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleXferCancel"); - //uint8 - return false; -} - -// Accept patch transfer -bool AuthSession::HandleXferAccept() -{ - TC_LOG_DEBUG("server.authserver", "Entering _HandleXferAccept"); - //uint8 - return true; } // Make the SRP6 calculation from hash in dB @@ -897,6 +956,6 @@ void AuthSession::SetVSFields(const std::string& rI) PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_VS); stmt->setString(0, v.AsHexStr()); stmt->setString(1, s.AsHexStr()); - stmt->setString(2, _login); + stmt->setString(2, _accountInfo.Login); LoginDatabase.Execute(stmt); } diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h index d40e0852b57..1babb7407a9 100644 --- a/src/server/authserver/Server/AuthSession.h +++ b/src/server/authserver/Server/AuthSession.h @@ -23,29 +23,48 @@ #include "ByteBuffer.h" #include "Socket.h" #include "BigNumber.h" +#include "Callback.h" #include #include using boost::asio::ip::tcp; +class Field; struct AuthHandler; +enum AuthStatus +{ + STATUS_CONNECTED = 0, + STATUS_AUTHED +}; + +struct AccountInfo +{ + void LoadResult(Field* fields); + + uint32 Id = 0; + std::string Login; + bool IsLockedToIP = false; + std::string LockCountry; + std::string LastIP; + uint32 FailedLogins = 0; + bool IsBanned = false; + bool IsPermanenetlyBanned = false; + AccountTypes SecurityLevel = SEC_PLAYER; + std::string TokenKey; +}; + class AuthSession : public Socket { + typedef Socket AuthSocket; + public: static std::unordered_map InitHandlers(); - AuthSession(tcp::socket&& socket) : Socket(std::move(socket)), - _isAuthenticated(false), _build(0), _expversion(0), _accountSecurityLevel(SEC_PLAYER) - { - N.SetHexStr("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7"); - g.SetDword(7); - } + AuthSession(tcp::socket&& socket); - void Start() override - { - AsyncRead(); - } + void Start() override; + bool Update() override; void SendPacket(ByteBuffer& packet); @@ -59,10 +78,10 @@ private: bool HandleReconnectProof(); bool HandleRealmList(); - //data transfer handle for patch - bool HandleXferResume(); - bool HandleXferCancel(); - bool HandleXferAccept(); + void CheckIpCallback(PreparedQueryResult result); + void LogonChallengeCallback(PreparedQueryResult result); + void ReconnectChallengeCallback(PreparedQueryResult result); + void RealmListCallback(PreparedQueryResult result); void SetVSFields(const std::string& rI); @@ -71,22 +90,27 @@ private: BigNumber K; BigNumber _reconnectProof; - bool _isAuthenticated; + bool _sentChallenge; + bool _sentProof; + + AuthStatus _status; + AccountInfo _accountInfo; std::string _tokenKey; - std::string _login; std::string _localizationName; std::string _os; + std::string _ipCountry; uint16 _build; uint8 _expversion; - AccountTypes _accountSecurityLevel; + PreparedQueryResultFuture _queryFuture; + std::function _queryCallback; }; #pragma pack(push, 1) struct AuthHandler { - uint32 status; + AuthStatus status; size_t packetSize; bool (AuthSession::*handler)(); }; diff --git a/src/server/authserver/Server/AuthSocketMgr.h b/src/server/authserver/Server/AuthSocketMgr.h index fa96502663f..a16b7d405b9 100644 --- a/src/server/authserver/Server/AuthSocketMgr.h +++ b/src/server/authserver/Server/AuthSocketMgr.h @@ -21,8 +21,6 @@ #include "SocketMgr.h" #include "AuthSession.h" -void OnSocketAccept(tcp::socket&& sock); - class AuthSocketMgr : public SocketMgr { typedef SocketMgr BaseSocketMgr; @@ -39,7 +37,7 @@ public: if (!BaseSocketMgr::StartNetwork(service, bindIp, port)) return false; - _acceptor->AsyncAcceptManaged(&OnSocketAccept); + _acceptor->AsyncAcceptWithCallback<&AuthSocketMgr::OnSocketAccept>(); return true; } @@ -48,14 +46,13 @@ protected: { return new NetworkThread[1]; } + + static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex) + { + Instance().OnSocketOpen(std::forward(sock), threadIndex); + } }; #define sAuthSocketMgr AuthSocketMgr::Instance() -void OnSocketAccept(tcp::socket&& sock) -{ - sAuthSocketMgr.OnSocketOpen(std::forward(sock)); -} - - #endif // AuthSocketMgr_h__ diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index 604988d62e5..82c3cd47148 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -130,6 +130,13 @@ WrongPass.BanType = 0 WrongPass.Logging = 0 +# +# BanExpiryCheckInterval +# Description: Time (in seconds) between checks for expired bans +# Default: 60 + +BanExpiryCheckInterval = 60 + # ################################################################################################### diff --git a/src/server/bnetserver/Packets/BitStream.h b/src/server/bnetserver/Packets/BitStream.h index 4594cccc74f..7ad996607e2 100644 --- a/src/server/bnetserver/Packets/BitStream.h +++ b/src/server/bnetserver/Packets/BitStream.h @@ -217,6 +217,7 @@ namespace Battlenet uint8* GetBuffer() { return _buffer.data(); } uint8 const* GetBuffer() const { return _buffer.data(); } + uint32 GetReadPos() const { return _readPos; } size_t GetSize() const { return ((_writePos + 7) & ~7) / 8; } // These methods are meant to only be used when their corresponding actions in the client ignore the value completely diff --git a/src/server/bnetserver/Packets/ConnectionPackets.h b/src/server/bnetserver/Packets/ConnectionPackets.h index bc6193f53de..c07063d5293 100644 --- a/src/server/bnetserver/Packets/ConnectionPackets.h +++ b/src/server/bnetserver/Packets/ConnectionPackets.h @@ -63,6 +63,8 @@ namespace Battlenet void Read() override { } std::string ToString() const override; void CallHandler(Session* session) override; + uint8* GetRemainingData() { return _stream.GetBuffer() + (((_stream.GetReadPos() + 7) & ~7) / 8); } + size_t GetRemainingSize() const { return _stream.GetSize() - (((_stream.GetReadPos() + 7) & ~7) / 8); } }; class LogoutRequest final : public ClientPacket diff --git a/src/server/bnetserver/Server/Session.cpp b/src/server/bnetserver/Server/Session.cpp index 2fed7fc3572..5506634f38b 100644 --- a/src/server/bnetserver/Server/Session.cpp +++ b/src/server/bnetserver/Server/Session.cpp @@ -141,6 +141,7 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(AUTH_INVALID_PROGRAM); AsyncWrite(logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s attempted to log in with game other than WoW (using %s)!", GetClientInfo().c_str(), logonRequest.Program.c_str()); return; } @@ -149,6 +150,7 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(AUTH_INVALID_OS); AsyncWrite(logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s attempted to log in from an unsupported platform (using %s)!", GetClientInfo().c_str(), logonRequest.Platform.c_str()); return; } @@ -157,6 +159,7 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(AUTH_UNSUPPORTED_LANGUAGE); AsyncWrite(logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s attempted to log in with unsupported locale (using %s)!", GetClientInfo().c_str(), logonRequest.Locale.c_str()); return; } @@ -166,15 +169,23 @@ void Battlenet::Session::HandleLogonRequest(Authentication::LogonRequest const& { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); if (!sComponentMgr->HasProgram(component.Program)) + { logonResponse->SetAuthResult(AUTH_INVALID_PROGRAM); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is using unsupported component program %s!", GetClientInfo().c_str(), component.Program.c_str()); + } else if (!sComponentMgr->HasPlatform(component.Platform)) + { logonResponse->SetAuthResult(AUTH_INVALID_OS); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is using unsupported component platform %s!", GetClientInfo().c_str(), component.Platform.c_str()); + } else { if (component.Program != "WoW" || AuthHelper::IsBuildSupportingBattlenet(component.Build)) logonResponse->SetAuthResult(AUTH_REGION_BAD_VERSION); else logonResponse->SetAuthResult(AUTH_USE_GRUNT_LOGON); + + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is using unsupported component version %u!", GetClientInfo().c_str(), component.Build); } AsyncWrite(logonResponse); @@ -204,6 +215,7 @@ void Battlenet::Session::HandleLogonRequestCallback(PreparedQueryResult result) Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(AUTH_UNKNOWN_ACCOUNT); AsyncWrite(logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] %s is trying to log in from unknown account!", GetClientInfo().c_str()); return; } @@ -224,7 +236,7 @@ void Battlenet::Session::HandleLogonRequestCallback(PreparedQueryResult result) std::string ip_address = GetRemoteIpAddress().to_string(); // If the IP is 'locked', check that the player comes indeed from the correct IP address - if (_accountInfo->IsLockedToIP) // if ip is locked + if (_accountInfo->IsLockedToIP) { TC_LOG_DEBUG("session", "[Battlenet::LogonRequest] Account '%s' is locked to IP - '%s' is logging in from '%s'", _accountInfo->Login.c_str(), _accountInfo->LastIP.c_str(), ip_address.c_str()); @@ -417,9 +429,10 @@ void Battlenet::Session::HandlePing(Connection::Ping const& /*ping*/) AsyncWrite(new Connection::Pong()); } -void Battlenet::Session::HandleEnableEncryption(Connection::EnableEncryption const& /*enableEncryption*/) +void Battlenet::Session::HandleEnableEncryption(Connection::EnableEncryption& enableEncryption) { _crypt.Init(&K); + _crypt.DecryptRecv(enableEncryption.GetRemainingData(), enableEncryption.GetRemainingSize()); } void Battlenet::Session::HandleLogoutRequest(Connection::LogoutRequest const& /*logoutRequest*/) @@ -461,12 +474,13 @@ void Battlenet::Session::HandleListSubscribeRequestCallback(PreparedQueryResult } while (result->NextRow()); } - for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms()) - listSubscribeResponse->RealmData.push_back(BuildListUpdate(&i.second)); - - listSubscribeResponse->RealmData.push_back(new WoWRealm::ListComplete()); - AsyncWrite(listSubscribeResponse); + + for (RealmList::RealmMap::value_type const& i : sRealmList->GetRealms()) + AsyncWrite(BuildListUpdate(&i.second)); + + AsyncWrite(new WoWRealm::ListComplete()); + _subscribedToRealmListUpdates = true; } @@ -486,7 +500,7 @@ void Battlenet::Session::HandleJoinRequestV2(WoWRealm::JoinRequestV2 const& join return; } - joinResponse->ServerSeed = uint32(rand32()); + joinResponse->ServerSeed = rand32(); uint8 sessionKey[40]; HmacSha1 hmac(K.GetNumBytes(), K.AsByteArray().get()); @@ -563,7 +577,7 @@ void Battlenet::Session::ReadHandler() if (stream.Read(1)) header.Channel = stream.Read(4); - if (header.Channel != AUTHENTICATION && !_authed) + if (header.Channel != AUTHENTICATION && (header.Channel != CONNECTION || header.Opcode != Connection::CMSG_PING) && !_authed) { TC_LOG_DEBUG("session.packets", "%s Received not allowed %s. Client has not authed yet.", GetClientInfo().c_str(), header.ToString().c_str()); CloseSocket(); @@ -608,18 +622,6 @@ void Battlenet::Session::Start() std::string ip_address = GetRemoteIpAddress().to_string(); TC_LOG_TRACE("session", "Accepted connection from %s", ip_address.c_str()); - if (_queryCallback) - { - Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - logonResponse->SetAuthResult(AUTH_LOGON_TOO_FAST); - AsyncWrite(logonResponse); - TC_LOG_DEBUG("session", "[Session::Start] %s attempted to log too quick after previous attempt!", GetClientInfo().c_str()); - return; - } - - // Verify that this IP is not in the ip_banned table - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_DEL_EXPIRED_IP_BANS)); - PreparedStatement* stmt = LoginDatabase.GetPreparedStatement(LOGIN_SEL_IP_INFO); stmt->setString(0, ip_address); stmt->setUInt32(1, inet_addr(ip_address.c_str())); @@ -659,12 +661,37 @@ void Battlenet::Session::CheckIpCallback(PreparedQueryResult result) bool Battlenet::Session::Update() { + EncryptableBuffer* queued; + MessageBuffer buffer((std::size_t(BufferSizes::Read))); + while (_bufferQueue.Dequeue(queued)) + { + std::size_t packetSize = queued->Buffer.GetActiveSize(); + if (queued->Encrypt) + _crypt.EncryptSend(queued->Buffer.GetReadPointer(), packetSize); + + if (buffer.GetRemainingSpace() < packetSize) + { + QueuePacket(std::move(buffer)); + buffer.Resize(std::size_t(BufferSizes::Read)); + } + + if (buffer.GetRemainingSpace() >= packetSize) + buffer.Write(queued->Buffer.GetReadPointer(), packetSize); + else // single packet larger than 16384 bytes - client will reject. + QueuePacket(std::move(queued->Buffer)); + + delete queued; + } + + if (buffer.GetActiveSize() > 0) + QueuePacket(std::move(buffer)); + if (!BattlenetSocket::Update()) return false; if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - auto callback = std::move(_queryCallback); + auto callback = _queryCallback; _queryCallback = nullptr; callback(_queryFuture.get()); } @@ -684,15 +711,12 @@ void Battlenet::Session::AsyncWrite(ServerPacket* packet) packet->Write(); - MessageBuffer buffer; - buffer.Write(packet->GetData(), packet->GetSize()); + EncryptableBuffer* buffer = new EncryptableBuffer(); + buffer->Buffer.Write(packet->GetData(), packet->GetSize()); + buffer->Encrypt = _crypt.IsInitialized(); delete packet; - std::unique_lock guard(_writeLock); - - _crypt.EncryptSend(buffer.GetReadPointer(), buffer.GetActiveSize()); - - QueuePacket(std::move(buffer), guard); + _bufferQueue.Enqueue(buffer); } inline void ReplaceResponse(Battlenet::ServerPacket** oldResponse, Battlenet::ServerPacket* newResponse) @@ -807,6 +831,7 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(AUTH_UNKNOWN_ACCOUNT); ReplaceResponse(response, logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::Password] %s attempted to log in with invalid password!", GetClientInfo().c_str()); return false; } @@ -815,12 +840,10 @@ bool Battlenet::Session::HandlePasswordModule(BitStream* dataStream, ServerPacke Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); logonResponse->SetAuthResult(LOGIN_NO_GAME_ACCOUNT); ReplaceResponse(response, logonResponse); + TC_LOG_DEBUG("session", "[Battlenet::Password] %s does not have any linked game accounts!", GetClientInfo().c_str()); return false; } - //set expired game account bans to inactive - LoginDatabase.Execute(LoginDatabase.GetPreparedStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS)); - BigNumber M; sha.Initialize(); sha.UpdateBigNumbers(&A, &M1, &K, NULL); @@ -929,6 +952,7 @@ bool Battlenet::Session::HandleSelectGameAccountModule(BitStream* dataStream, Se Authentication::LogonResponse* complete = new Authentication::LogonResponse(); complete->SetAuthResult(LOGIN_NO_GAME_ACCOUNT); ReplaceResponse(response, complete); + TC_LOG_DEBUG("session", "[Battlenet::SelectGameAccount] %s attempted to log in with invalid game account name %s!", GetClientInfo().c_str(), account.c_str()); return false; } @@ -961,7 +985,7 @@ bool Battlenet::Session::HandleSelectGameAccountModule(BitStream* dataStream, Se bool Battlenet::Session::HandleRiskFingerprintModule(BitStream* dataStream, ServerPacket** response) { Authentication::LogonResponse* logonResponse = new Authentication::LogonResponse(); - if (dataStream->Read(8) == 1 && _accountInfo && _gameAccountInfo) + if (dataStream->Read(8) == 1 && _accountInfo && _gameAccountInfo) { logonResponse->AccountId = _accountInfo->Id; logonResponse->GameAccountName = _gameAccountInfo->Name; @@ -1043,7 +1067,7 @@ bool Battlenet::Session::HandleResumeModule(BitStream* dataStream, ServerPacket* stmt->setString(0, _accountInfo->Login); LoginDatabase.Execute(stmt); - TC_LOG_DEBUG("session", "[Battlenet::Resume] Invalid proof!"); + TC_LOG_DEBUG("session", "[Battlenet::Resume] %s attempted to reconnect with invalid password!", GetClientInfo().c_str()); Authentication::ResumeResponse* resumeResponse = new Authentication::ResumeResponse(); resumeResponse->SetAuthResult(AUTH_UNKNOWN_ACCOUNT); ReplaceResponse(response, resumeResponse); diff --git a/src/server/bnetserver/Server/Session.h b/src/server/bnetserver/Server/Session.h index d909c68dc24..e747e2b7407 100644 --- a/src/server/bnetserver/Server/Session.h +++ b/src/server/bnetserver/Server/Session.h @@ -22,7 +22,8 @@ #include "BattlenetPacketCrypt.h" #include "Socket.h" #include "BigNumber.h" -#include "Database/DatabaseEnv.h" +#include "Callback.h" +#include "MPSCQueue.h" #include #include @@ -96,7 +97,7 @@ namespace Battlenet // Connection void HandlePing(Connection::Ping const& ping); - void HandleEnableEncryption(Connection::EnableEncryption const& enableEncryption); + void HandleEnableEncryption(Connection::EnableEncryption& enableEncryption); void HandleLogoutRequest(Connection::LogoutRequest const& logoutRequest); void HandleConnectionClosing(Connection::ConnectionClosing const& connectionClosing); @@ -172,10 +173,18 @@ namespace Battlenet std::queue _modulesWaitingForData; + struct EncryptableBuffer + { + MessageBuffer Buffer; + bool Encrypt; + }; + + MPSCQueue _bufferQueue; + PacketCrypt _crypt; bool _authed; bool _subscribedToRealmListUpdates; - + PreparedQueryResultFuture _queryFuture; std::function _queryCallback; }; diff --git a/src/server/bnetserver/Server/SessionManager.cpp b/src/server/bnetserver/Server/SessionManager.cpp index b0e2ceca508..c53214495d4 100644 --- a/src/server/bnetserver/Server/SessionManager.cpp +++ b/src/server/bnetserver/Server/SessionManager.cpp @@ -22,7 +22,8 @@ bool Battlenet::SessionManager::StartNetwork(boost::asio::io_service& service, s if (!BaseSocketMgr::StartNetwork(service, bindIp, port)) return false; - _acceptor->AsyncAcceptManaged(&OnSocketAccept); + _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); + _acceptor->AsyncAcceptWithCallback<&OnSocketAccept>(); return true; } @@ -31,9 +32,9 @@ NetworkThread* Battlenet::SessionManager::CreateThreads() co return new NetworkThread[GetNetworkThreadCount()]; } -void Battlenet::SessionManager::OnSocketAccept(tcp::socket&& sock) +void Battlenet::SessionManager::OnSocketAccept(tcp::socket&& sock, uint32 threadIndex) { - sSessionMgr.OnSocketOpen(std::forward(sock)); + sSessionMgr.OnSocketOpen(std::forward(sock), threadIndex); } void Battlenet::SessionManager::AddSession(Session* session) @@ -46,7 +47,11 @@ void Battlenet::SessionManager::AddSession(Session* session) void Battlenet::SessionManager::RemoveSession(Session* session) { std::unique_lock lock(_sessionMutex); - _sessions.erase({ session->GetAccountId(), session->GetGameAccountId() }); + auto itr = _sessions.find({ session->GetAccountId(), session->GetGameAccountId() }); + // Remove old session only if it was not overwritten by reconnecting + if (itr != _sessions.end() && itr->second == session) + _sessions.erase(itr); + _sessionsByAccountId[session->GetAccountId()].remove(session); } diff --git a/src/server/bnetserver/Server/SessionManager.h b/src/server/bnetserver/Server/SessionManager.h index 014d5a4bd98..5cf0b199f15 100644 --- a/src/server/bnetserver/Server/SessionManager.h +++ b/src/server/bnetserver/Server/SessionManager.h @@ -40,7 +40,7 @@ namespace Battlenet #pragma pack(pop) - class SessionManager : SocketMgr + class SessionManager : public SocketMgr { typedef SocketMgr BaseSocketMgr; typedef std::map SessionMap; @@ -75,7 +75,7 @@ namespace Battlenet NetworkThread* CreateThreads() const override; private: - static void OnSocketAccept(tcp::socket&& sock); + static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex); SessionMap _sessions; SessionByAccountMap _sessionsByAccountId; diff --git a/src/server/database/Database/Implementation/CharacterDatabase.cpp b/src/server/database/Database/Implementation/CharacterDatabase.cpp index 3885aa098b7..7367f6ea02b 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.cpp +++ b/src/server/database/Database/Implementation/CharacterDatabase.cpp @@ -72,7 +72,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() "FROM characters WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_GROUP_MEMBER, "SELECT guid FROM group_member WHERE memberGuid = ?", CONNECTION_BOTH); - PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_SEL_CHARACTER_INSTANCE, "SELECT id, permanent, map, difficulty, extendState, resettime FROM character_instance LEFT JOIN instance ON instance = id WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, " "base_amount0, base_amount1, base_amount2, maxduration, remaintime, remaincharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_SEL_CHARACTER_SPELL, "SELECT spell, active, disabled FROM character_spell WHERE guid = ?", CONNECTION_ASYNC); @@ -426,8 +426,8 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_UPD_WORLDSTATE, "UPDATE worldstates SET value = ? WHERE entry = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_WORLDSTATE, "INSERT INTO worldstates (entry, value) VALUES (?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE_GUID, "DELETE FROM character_instance WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_CHAR_INSTANCE, "UPDATE character_instance SET instance = ?, permanent = ?, extendState = ? WHERE guid = ? AND instance = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_INS_CHAR_INSTANCE, "INSERT INTO character_instance (guid, instance, permanent, extendState) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_GENDER_PLAYERBYTES, "UPDATE characters SET gender = ?, playerBytes = ?, playerBytes2 = ? WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHARACTER_SKILL, "DELETE FROM character_skills WHERE guid = ? AND skill = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_UPD_ADD_CHARACTER_SOCIAL_FLAGS, "UPDATE character_social SET flags = flags | ? WHERE guid = ? AND friend = ?", CONNECTION_ASYNC); @@ -455,6 +455,7 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, "SELECT class, level, at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_AT_LOGIN_TITLES, "SELECT at_login, knownTitles FROM characters WHERE guid = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_INSTANCE, "SELECT data, completedEncounters FROM instance WHERE map = ? AND id = ?", CONNECTION_SYNCH); + PrepareStatement(CHAR_SEL_PERM_BIND_BY_INSTANCE, "SELECT guid FROM character_instance WHERE instance = ? and permanent = 1", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_COD_ITEM_MAIL, "SELECT id, messageType, mailTemplateId, sender, subject, body, money, has_items FROM mail WHERE receiver = ? AND has_items <> 0 AND cod <> 0", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_SOCIAL, "SELECT DISTINCT guid FROM character_social WHERE friend = ?", CONNECTION_SYNCH); PrepareStatement(CHAR_SEL_CHAR_OLD_CHARS, "SELECT guid, deleteInfos_Account FROM characters WHERE deleteDate IS NOT NULL AND deleteDate < ?", CONNECTION_SYNCH); @@ -490,9 +491,10 @@ void CharacterDatabaseConnection::DoPrepareStatements() PrepareStatement(CHAR_INS_CHAR_GIFT, "INSERT INTO character_gifts (guid, item_guid, entry, flags) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_INSTANCE_BY_INSTANCE, "DELETE FROM instance WHERE id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, "DELETE FROM character_instance WHERE instance = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF, "DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF, "DELETE FROM character_instance USING character_instance LEFT JOIN instance ON character_instance.instance = id WHERE (extendState = 0 or permanent = 0) and map = ? and difficulty = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF, "DELETE FROM group_instance USING group_instance LEFT JOIN instance ON group_instance.instance = id WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); - PrepareStatement(CHAR_DEL_INSTANCE_BY_MAP_DIFF, "DELETE FROM instance WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); + PrepareStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF, "DELETE FROM instance WHERE map = ? and difficulty = ? and (SELECT guid FROM character_instance WHERE extendState != 0 AND instance = id LIMIT 1) IS NULL", CONNECTION_ASYNC); + PrepareStatement(CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF, "UPDATE character_instance LEFT JOIN instance ON character_instance.instance = id SET extendState = extendState-1 WHERE map = ? and difficulty = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_MAIL_ITEM_BY_ID, "DELETE FROM mail_items WHERE mail_id = ?", CONNECTION_ASYNC); PrepareStatement(CHAR_INS_PETITION, "INSERT INTO petition (ownerguid, petitionguid, name, type) VALUES (?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(CHAR_DEL_PETITION_BY_GUID, "DELETE FROM petition WHERE petitionguid = ?", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/CharacterDatabase.h b/src/server/database/Database/Implementation/CharacterDatabase.h index c6e9f01b897..bb169628df0 100644 --- a/src/server/database/Database/Implementation/CharacterDatabase.h +++ b/src/server/database/Database/Implementation/CharacterDatabase.h @@ -384,6 +384,7 @@ enum CharacterDatabaseStatements CHAR_SEL_CHAR_CLASS_LVL_AT_LOGIN, CHAR_SEL_CHAR_AT_LOGIN_TITLES, CHAR_SEL_INSTANCE, + CHAR_SEL_PERM_BIND_BY_INSTANCE, CHAR_SEL_CHAR_COD_ITEM_MAIL, CHAR_SEL_CHAR_SOCIAL, CHAR_SEL_CHAR_OLD_CHARS, @@ -414,9 +415,10 @@ enum CharacterDatabaseStatements CHAR_INS_CHAR_GIFT, CHAR_DEL_INSTANCE_BY_INSTANCE, CHAR_DEL_CHAR_INSTANCE_BY_INSTANCE, - CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF, + CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF, CHAR_DEL_GROUP_INSTANCE_BY_MAP_DIFF, - CHAR_DEL_INSTANCE_BY_MAP_DIFF, + CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF, + CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF, CHAR_DEL_MAIL_ITEM_BY_ID, CHAR_INS_PETITION, CHAR_DEL_PETITION_BY_GUID, diff --git a/src/server/database/Database/Implementation/LoginDatabase.cpp b/src/server/database/Database/Implementation/LoginDatabase.cpp index 270d68693e3..bcf551f4737 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.cpp +++ b/src/server/database/Database/Implementation/LoginDatabase.cpp @@ -24,26 +24,27 @@ void LoginDatabaseConnection::DoPrepareStatements() PrepareStatement(LOGIN_SEL_REALMLIST, "SELECT id, name, address, localAddress, localSubnetMask, port, icon, flag, timezone, allowedSecurityLevel, population, gamebuild, Region, Battlegroup FROM realmlist WHERE flag <> 3 ORDER BY name", CONNECTION_SYNCH); PrepareStatement(LOGIN_DEL_EXPIRED_IP_BANS, "DELETE FROM ip_banned WHERE unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC); - PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_IP_BANNED, "SELECT * FROM ip_banned WHERE ip = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_UPD_EXPIRED_ACCOUNT_BANS, "UPDATE account_banned SET active = 0 WHERE active = 1 AND unbandate<>bandate AND unbandate<=UNIX_TIMESTAMP()", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_IP_INFO, "(SELECT unbandate > UNIX_TIMESTAMP() OR unbandate = bandate AS banned, NULL as country FROM ip_banned WHERE ip = ?) " "UNION " "(SELECT NULL AS banned, country FROM ip2nation WHERE INET_NTOA(ip) = ?)", CONNECTION_ASYNC); PrepareStatement(LOGIN_INS_IP_AUTO_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban')", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_IP_BANNED_ALL, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) ORDER BY unbandate", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_IP_BANNED_BY_IP, "SELECT ip, bandate, unbandate, bannedby, banreason FROM ip_banned WHERE (bandate = unbandate OR unbandate > UNIX_TIMESTAMP()) AND ip LIKE CONCAT('%%', ?, '%%') ORDER BY unbandate", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED, "SELECT bandate, unbandate FROM account_banned WHERE id = ? AND active = 1", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_ALL, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 GROUP BY account.id", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, "SELECT account.id, username FROM account, account_banned WHERE account.id = account_banned.id AND active = 1 AND username LIKE CONCAT('%%', ?, '%%') GROUP BY account.id", CONNECTION_SYNCH); PrepareStatement(LOGIN_INS_ACCOUNT_AUTO_BANNED, "INSERT INTO account_banned VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, 'Trinity Auth', 'Failed login autoban', 1)", CONNECTION_ASYNC); PrepareStatement(LOGIN_DEL_ACCOUNT_BANNED, "DELETE FROM account_banned WHERE id = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_SEL_SESSIONKEY, "SELECT a.sessionkey, a.id, aa.gmlevel FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_UPD_VS, "UPDATE account SET v = ?, s = ? WHERE username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_UPD_LOGONPROOF, "UPDATE account SET sessionkey = ?, last_ip = ?, last_login = NOW(), locale = ?, failed_logins = 0, os = ? WHERE username = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.sha_pass_hash, a.id, a.locked, a.lock_country, a.last_ip, aa.gmlevel, a.v, a.s, a.token_key, a.battlenet_account FROM account a LEFT JOIN account_access aa ON (a.id = aa.id) WHERE a.username = ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_LOGONCHALLENGE, "SELECT a.id, UPPER(a.username), a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, " + "ab.unbandate = ab.bandate, aa.gmlevel, a.token_key, a.sha_pass_hash, a.v, a.s, a.battlenet_account " + "FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC); + PrepareStatement(LOGIN_SEL_RECONNECTCHALLENGE, "SELECT a.id, UPPER(a.username), a.locked, a.lock_country, a.last_ip, a.failed_logins, ab.unbandate > UNIX_TIMESTAMP() OR ab.unbandate = ab.bandate, " + "ab.unbandate = ab.bandate, aa.gmlevel, a.sessionKey " + "FROM account a LEFT JOIN account_access aa ON a.id = aa.id LEFT JOIN account_banned ab ON ab.id = a.id AND ab.active = 1 WHERE a.username = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_LOGON_COUNTRY, "SELECT country FROM ip2nation WHERE ip < ? ORDER BY ip DESC LIMIT 0,1", CONNECTION_SYNCH); PrepareStatement(LOGIN_UPD_FAILEDLOGINS, "UPDATE account SET failed_logins = failed_logins + 1 WHERE username = ?", CONNECTION_ASYNC); - PrepareStatement(LOGIN_SEL_FAILEDLOGINS, "SELECT id, failed_logins FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_ID_BY_NAME, "SELECT id FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_NAME, "SELECT id, username FROM account WHERE username = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_INFO_BY_NAME, "SELECT a.id, a.sessionkey, ba.last_ip, ba.locked, ba.lock_country, a.expansion, a.mutetime, ba.locale, a.recruiter, ba.os, ba.id, aa.gmLevel, " @@ -53,7 +54,7 @@ void LoginDatabaseConnection::DoPrepareStatements() "WHERE a.username = ? ORDER BY aa.RealmID DESC LIMIT 1", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, "SELECT id, username FROM account WHERE email = ?", CONNECTION_SYNCH); - PrepareStatement(LOGIN_SEL_NUM_CHARS_ON_REALM, "SELECT numchars FROM realmcharacters WHERE realmid = ? AND acctid= ?", CONNECTION_SYNCH); + PrepareStatement(LOGIN_SEL_REALM_CHARACTER_COUNTS, "SELECT realmid, numchars FROM realmcharacters WHERE acctid = ?", CONNECTION_ASYNC); PrepareStatement(LOGIN_SEL_ACCOUNT_BY_IP, "SELECT id, username FROM account WHERE last_ip = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_SEL_ACCOUNT_BY_ID, "SELECT 1 FROM account WHERE id = ?", CONNECTION_SYNCH); PrepareStatement(LOGIN_INS_IP_BANNED, "INSERT INTO ip_banned (ip, bandate, unbandate, bannedby, banreason) VALUES (?, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()+?, ?, ?)", CONNECTION_ASYNC); diff --git a/src/server/database/Database/Implementation/LoginDatabase.h b/src/server/database/Database/Implementation/LoginDatabase.h index e966b7a3ac6..34efaf86fd0 100644 --- a/src/server/database/Database/Implementation/LoginDatabase.h +++ b/src/server/database/Database/Implementation/LoginDatabase.h @@ -32,26 +32,23 @@ enum LoginDatabaseStatements LOGIN_SEL_REALMLIST, LOGIN_DEL_EXPIRED_IP_BANS, LOGIN_UPD_EXPIRED_ACCOUNT_BANS, - LOGIN_SEL_IP_BANNED, LOGIN_SEL_IP_INFO, LOGIN_INS_IP_AUTO_BANNED, - LOGIN_SEL_ACCOUNT_BANNED, LOGIN_SEL_ACCOUNT_BANNED_ALL, LOGIN_SEL_ACCOUNT_BANNED_BY_USERNAME, LOGIN_INS_ACCOUNT_AUTO_BANNED, LOGIN_DEL_ACCOUNT_BANNED, - LOGIN_SEL_SESSIONKEY, LOGIN_UPD_VS, LOGIN_UPD_LOGONPROOF, LOGIN_SEL_LOGONCHALLENGE, + LOGIN_SEL_RECONNECTCHALLENGE, LOGIN_SEL_LOGON_COUNTRY, LOGIN_UPD_FAILEDLOGINS, - LOGIN_SEL_FAILEDLOGINS, LOGIN_SEL_ACCOUNT_ID_BY_NAME, LOGIN_SEL_ACCOUNT_LIST_BY_NAME, LOGIN_SEL_ACCOUNT_INFO_BY_NAME, LOGIN_SEL_ACCOUNT_LIST_BY_EMAIL, - LOGIN_SEL_NUM_CHARS_ON_REALM, + LOGIN_SEL_REALM_CHARACTER_COUNTS, LOGIN_SEL_ACCOUNT_BY_IP, LOGIN_INS_IP_BANNED, LOGIN_DEL_IP_NOT_BANNED, diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp index fd0dbdd4b5a..001fdf20610 100644 --- a/src/server/database/Updater/UpdateFetcher.cpp +++ b/src/server/database/Updater/UpdateFetcher.cpp @@ -142,14 +142,12 @@ UpdateFetcher::SQLUpdate UpdateFetcher::ReadSQLUpdate(boost::filesystem::path co std::ifstream in(file.c_str()); WPFatal(in.is_open(), "Could not read an update file."); - auto const start_pos = in.tellg(); - in.ignore(std::numeric_limits::max()); - auto const char_count = in.gcount(); - in.seekg(start_pos); + auto update = [&in] { + std::ostringstream ss; + ss << in.rdbuf(); + return Trinity::make_unique(ss.str()); + }(); - SQLUpdate const update(new std::string(char_count, char{})); - - in.read(&(*update)[0], update->size()); in.close(); return update; } diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h index 22a0d08c7f8..32f8516413d 100644 --- a/src/server/database/Updater/UpdateFetcher.h +++ b/src/server/database/Updater/UpdateFetcher.h @@ -103,7 +103,7 @@ private: typedef std::unordered_map HashToFileNameStorage; typedef std::unordered_map AppliedFileStorage; typedef std::vector DirectoryStorage; - typedef std::shared_ptr SQLUpdate; + typedef std::unique_ptr SQLUpdate; LocaleFileStorage GetFileList() const; void FillFileListRecursively(Path const& path, LocaleFileStorage& storage, State const state, uint32 const depth) const; diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index c254a9124c1..352ae635878 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -133,7 +133,7 @@ void CreatureAI::MoveInLineOfSight(Unit* who) if (me->GetCreatureType() == CREATURE_TYPE_NON_COMBAT_PET) // non-combat pets should just stand there and look good;) return; - if (me->CanStartAttack(who, false)) + if (me->HasReactState(REACT_AGGRESSIVE) && me->CanStartAttack(who, false)) AttackStart(who); //else if (who->GetVictim() && me->IsFriendlyTo(who) // && me->IsWithinDistInMap(who, sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS)) @@ -350,7 +350,7 @@ int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) con return boundsWarning ? LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED : 0; } -bool CreatureAI::CheckBoundary(Position* who) const +bool CreatureAI::CheckBoundary(Position const* who) const { if (!who) who = me; diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index f250a79ea25..239fda577a7 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -79,7 +79,7 @@ class CreatureAI : public UnitAI Creature* DoSummon(uint32 entry, WorldObject* obj, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); Creature* DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN); - bool CheckBoundary(Position* who = nullptr) const; + bool CheckBoundary(Position const* who = nullptr) const; void SetBoundary(CreatureBoundary const* boundary) { _boundary = boundary; me->DoImmediateBoundaryCheck(); } public: enum EvadeReason diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index fe957abcc75..608534f634e 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -547,18 +547,27 @@ void BossAI::UpdateAI(uint32 diff) DoMeleeAttackIfReady(); } -void BossAI::_DespawnAtEvade() +void BossAI::_DespawnAtEvade(uint32 delayToRespawn) { + if (delayToRespawn < 2) + { + TC_LOG_ERROR("scripts", "_DespawnAtEvade called with delay of %u seconds, defaulting to 2.", delayToRespawn); + delayToRespawn = 2; + } + uint32 corpseDelay = me->GetCorpseDelay(); uint32 respawnDelay = me->GetRespawnDelay(); me->SetCorpseDelay(1); - me->SetRespawnDelay(29); + me->SetRespawnDelay(delayToRespawn - 1); me->DespawnOrUnsummon(); me->SetCorpseDelay(corpseDelay); me->SetRespawnDelay(respawnDelay); + + if(instance) + instance->SetBossState(_bossId, FAIL); } // WorldBossAI - for non-instanced bosses diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index ff0e7d3d7f9..5452a033a17 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -360,12 +360,14 @@ class BossAI : public ScriptedAI void JustDied(Unit* /*killer*/) override { _JustDied(); } void JustReachedHome() override { _JustReachedHome(); } + bool CanAIAttack(Unit const* target) const override { return CheckBoundary(target); } + protected: void _Reset(); void _EnterCombat(); void _JustDied(); void _JustReachedHome() { me->setActive(false); } - void _DespawnAtEvade(); + void _DespawnAtEvade(uint32 delayToRespawn = 30); void TeleportCheaters(); diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index 68cb8d346d0..fa80634e0f0 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -105,7 +105,7 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* who) void npc_escortAI::MoveInLineOfSight(Unit* who) { - if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me)) + if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me)) { if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(who)) return; diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index dc9f6d2681e..778edf8cab5 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -102,7 +102,7 @@ bool FollowerAI::AssistPlayerInCombat(Unit* who) void FollowerAI::MoveInLineOfSight(Unit* who) { - if (!me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me)) + if (me->HasReactState(REACT_AGGRESSIVE) && !me->HasUnitState(UNIT_STATE_STUNNED) && who->isTargetableForAttack() && who->isInAccessiblePlaceFor(me)) { if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(who)) return; diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index eca327e770e..361bb1a5b1d 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -466,6 +466,9 @@ bool SmartAI::CanAIAttack(const Unit* /*who*/) const bool SmartAI::AssistPlayerInCombat(Unit* who) { + if (me->HasReactState(REACT_PASSIVE)) + return false; + if (!who || !who->GetVictim()) return false; diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 206f8d121da..0d5ed677b9c 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -2008,7 +2008,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u if (Creature* creature = (*itr)->ToCreature()) { creature->GetMotionMaster()->Clear(); - creature->GetMotionMaster()->MoveJump(e.target.x, e.target.y, e.target.z, (float)e.action.jump.speedxy, (float)e.action.jump.speedz); + creature->GetMotionMaster()->MoveJump(e.target.x, e.target.y, e.target.z, 0.0f, (float)e.action.jump.speedxy, (float)e.action.jump.speedz); // @todo add optional jump orientation support? } } /// @todo Resume path when reached jump location @@ -2315,6 +2315,31 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u } break; } + case SMART_ACTION_RANDOM_SOUND: + { + std::vector sounds; + std::copy_if(e.action.randomSound.sounds.begin(), e.action.randomSound.sounds.end(), + std::back_inserter(sounds), [](uint32 sound) { return sound != 0; }); + + bool onlySelf = e.action.randomSound.onlySelf != 0; + + if (ObjectList* targets = GetTargets(e, unit)) + { + for (WorldObject* const obj : *targets) + { + if (IsUnit(obj)) + { + uint32 sound = Trinity::Containers::SelectRandomContainerElement(sounds); + obj->PlayDirectSound(sound, onlySelf ? obj->ToPlayer() : nullptr); + TC_LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_RANDOM_SOUND: target: %s (%s), sound: %u, onlyself: %s", + obj->GetName().c_str(), obj->GetGUID().ToString().c_str(), sound, onlySelf ? "true" : "false"); + } + } + + delete targets; + break; + } + } default: TC_LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry %d SourceType %u, Event %u, Unhandled Action type %u", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); break; diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp index e7782f46020..feede2deb59 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.cpp @@ -468,7 +468,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Map entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.map); return false; } - if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !GetAreaEntryByAreaID(e.event.respawn.area)) + if (e.event.respawn.type == SMART_SCRIPT_RESPAWN_CONDITION_AREA && !sAreaTableStore.LookupEntry(e.event.respawn.area)) { TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u uses non-existent Area entry %u, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.event.respawn.area); return false; @@ -827,6 +827,21 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e) if (e.action.randomEmote.emote6 && !IsEmoteValid(e, e.action.randomEmote.emote6)) return false; break; + case SMART_ACTION_RANDOM_SOUND: + { + if (std::all_of(e.action.randomSound.sounds.begin(), e.action.randomSound.sounds.end(), [](uint32 sound) { return sound == 0; })) + { + TC_LOG_ERROR("sql.sql", "SmartAIMgr: Entry %d SourceType %u Event %u Action %u does not have any non-zero sound", + e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType()); + return false; + } + + for (uint32 sound : e.action.randomSound.sounds) + if (sound && !IsSoundValid(e, sound)) + return false; + + break; + } case SMART_ACTION_CAST: { if (!IsSpellValid(e, e.action.cast.spell)) diff --git a/src/server/game/AI/SmartScripts/SmartScriptMgr.h b/src/server/game/AI/SmartScripts/SmartScriptMgr.h index 324c04d87ce..6e1fb5d81bb 100644 --- a/src/server/game/AI/SmartScripts/SmartScriptMgr.h +++ b/src/server/game/AI/SmartScripts/SmartScriptMgr.h @@ -44,6 +44,16 @@ struct WayPoint float z; }; +enum eSmartAI +{ + SMART_EVENT_PARAM_COUNT = 4, + SMART_ACTION_PARAM_COUNT = 6, + SMART_SUMMON_COUNTER = 0xFFFFFF, + SMART_ESCORT_LAST_OOC_POINT = 0xFFFFFF, + SMART_RANDOM_POINT = 0xFFFFFE, + SMART_ESCORT_TARGETS = 0xFFFFFF +}; + enum SMART_EVENT_PHASE { SMART_EVENT_PHASE_ALWAYS = 0, @@ -541,8 +551,9 @@ enum SMART_ACTION 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_RANDOM_SOUND = 115, // soundId1, soundId2, soundId3, soundId4, soundId5, onlySelf - SMART_ACTION_END = 115 + SMART_ACTION_END = 116 }; struct SmartAction @@ -1025,6 +1036,12 @@ struct SmartAction uint32 wp6; } closestWaypointFromList; + struct + { + std::array sounds; + uint32 onlySelf; + } randomSound; + //! Note for any new future actions //! All parameters must have type uint32 @@ -1188,16 +1205,6 @@ struct SmartTarget }; }; -enum eSmartAI -{ - SMART_EVENT_PARAM_COUNT = 4, - SMART_ACTION_PARAM_COUNT = 6, - SMART_SUMMON_COUNTER = 0xFFFFFF, - SMART_ESCORT_LAST_OOC_POINT = 0xFFFFFF, - SMART_RANDOM_POINT = 0xFFFFFE, - SMART_ESCORT_TARGETS = 0xFFFFFF -}; - enum SmartScriptType { SMART_SCRIPT_TYPE_CREATURE = 0, //done diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 961b99b71a8..c8ca970d193 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -47,7 +47,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) { if (dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` for criteria (Entry: %u) has wrong data type (%u), ignored.", criteria->ID, dataType); + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` for criteria (Entry: %u) contains a wrong data type (%u), ignored.", criteria->ID, dataType); return false; } @@ -82,7 +82,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) default: if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has data for non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->type); + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains data for a non-supported criteria type (Entry: %u Type: %u), ignored.", criteria->ID, criteria->type); return false; } break; @@ -96,7 +96,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_CREATURE: if (!creature.id || !sObjectMgr->GetCreatureTemplate(creature.id)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) has non-existing creature id in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_CREATURE (%u) contains a non-existing creature id in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, creature.id); return false; } @@ -110,13 +110,13 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) } if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) has non-existing class in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) contains a non-existing class in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, classRace.class_id); return false; } if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) has non-existing race in value2 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_CLASS_RACE (%u) contains a non-existing race in value2 (%u), ignored.", criteria->ID, criteria->type, dataType, classRace.race_id); return false; } @@ -124,7 +124,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_PLAYER_LESS_HEALTH: if (health.percent < 1 || health.percent > 100) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) has wrong percent value in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_PLAYER_LESS_HEALTH (%u) contains a wrong percent value in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, health.percent); return false; } @@ -135,19 +135,19 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(aura.spell_id); if (!spellEntry) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell id in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a wrong spell id in value1 (%u), ignored.", criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.spell_id); return false; } if (aura.effect_idx >= 3) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has wrong spell effect index in value2 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a wrong spell effect index in value2 (%u), ignored.", criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.effect_idx); return false; } if (!spellEntry->Effects[aura.effect_idx].ApplyAuraName) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) has non-aura spell effect (ID: %u Effect: %u), ignores.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type %s (%u) contains a non-aura spell effect (ID: %u Effect: %u), ignored.", criteria->ID, criteria->type, (dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA?"ACHIEVEMENT_CRITERIA_DATA_TYPE_S_AURA":"ACHIEVEMENT_CRITERIA_DATA_TYPE_T_AURA"), dataType, aura.spell_id, aura.effect_idx); return false; } @@ -156,7 +156,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE: if (value.compType >= COMP_TYPE_MAX) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE (%u) has wrong ComparisionType in value2 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_VALUE (%u) contains a wrong ComparisionType in value2 (%u), ignored.", criteria->ID, criteria->type, dataType, value.compType); return false; } @@ -164,7 +164,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL: if (level.minlevel > STRONG_MAX_LEVEL) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) has wrong minlevel in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_LEVEL (%u) contains a wrong minlevel in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, level.minlevel); return false; } @@ -172,7 +172,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER: if (gender.gender > GENDER_NONE) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) has wrong gender in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_GENDER (%u) contains a wrong gender value in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, gender.gender); return false; } @@ -180,7 +180,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT: if (!ScriptId) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT (%u) does not have ScriptName set, ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT (%u) does not have a ScriptName set, ignored.", criteria->ID, criteria->type, dataType); return false; } @@ -188,7 +188,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT: if (map_players.maxcount <= 0) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) has wrong max players count in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_MAP_PLAYER_COUNT (%u) contains a wrong max players count in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, map_players.maxcount); return false; } @@ -196,7 +196,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM: if (team.team != ALLIANCE && team.team != HORDE) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) has unknown team in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_T_TEAM (%u) contains an unknown team value in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, team.team); return false; } @@ -204,7 +204,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK: if (drunk.state >= MAX_DRUNKEN) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) has unknown drunken state in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_DRUNK (%u) contains an unknown drunken state value in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, drunk.state); return false; } @@ -212,7 +212,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY: if (!sHolidaysStore.LookupEntry(holiday.id)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) has unknown holiday in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY (%u) contains an unknown holiday entry in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, holiday.id); return false; } @@ -233,7 +233,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM: if (equipped_item.item_quality >= MAX_ITEM_QUALITY) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM (%u) has unknown quality state in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_EQUIPED_ITEM (%u) contains an unknown quality state value in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, equipped_item.item_quality); return false; } @@ -241,19 +241,19 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE: if (!classRace.class_id && !classRace.race_id) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) must not have 0 in either value field, ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) should not have 0 in either value field. Ignored.", criteria->ID, criteria->type, dataType); return false; } if (classRace.class_id && ((1 << (classRace.class_id-1)) & CLASSMASK_ALL_PLAYABLE) == 0) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) has non-existing class in value1 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) contains a non-existing class entry in value1 (%u), ignored.", criteria->ID, criteria->type, dataType, classRace.class_id); return false; } if (classRace.race_id && ((1 << (classRace.race_id-1)) & RACEMASK_ALL_PLAYABLE) == 0) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) has non-existing race in value2 (%u), ignored.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_PLAYER_CLASS_RACE (%u) contains a non-existing race entry in value2 (%u), ignored.", criteria->ID, criteria->type, dataType, classRace.race_id); return false; } @@ -261,13 +261,13 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria) case ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE: if (!sCharTitlesStore.LookupEntry(known_title.title_id)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE (%u) have unknown title_id in value1 (%u), ignore.", + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) for data type ACHIEVEMENT_CRITERIA_DATA_TYPE_S_KNOWN_TITLE (%u) contains an unknown title_id in value1 (%u), ignore.", criteria->ID, criteria->type, dataType, known_title.title_id); return false; } return true; default: - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) has data for non-supported data type (%u), ignored.", criteria->ID, criteria->type, dataType); + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` (Entry: %u Type: %u) contains data of a non-supported data type (%u), ignored.", criteria->ID, criteria->type, dataType); return false; } } @@ -346,14 +346,14 @@ bool AchievementCriteriaData::Meets(uint32 criteria_id, Player const* source, Un Map* map = source->GetMap(); if (!map->IsDungeon()) { - TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u for non-dungeon/non-raid map %u", + TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u in a non-dungeon/non-raid map %u", dataType, criteria_id, map->GetId()); return false; } InstanceScript* instance = map->ToInstanceMap()->GetInstanceScript(); if (!instance) { - TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u for map %u but map does not have a instance script", + TC_LOG_ERROR("achievement", "Achievement system call ACHIEVEMENT_CRITERIA_DATA_TYPE_INSTANCE_SCRIPT (%u) for achievement criteria %u in map %u, but the map does not have an instance script.", dataType, criteria_id, map->GetId()); return false; } @@ -695,8 +695,8 @@ void AchievementMgr::LoadFromDB(PreparedQueryResult achievementResult, P AchievementCriteriaEntry const* criteria = sAchievementMgr->GetAchievementCriteria(id); if (!criteria) { - // we will remove not existed criteria for all characters - TC_LOG_ERROR("achievement", "Non-existing achievement criteria %u data removed from table `character_achievement_progress`.", id); + // Removing non-existing criteria data for all characters + TC_LOG_ERROR("achievement", "Non-existing achievement criteria %u data has been removed from the table `character_achievement_progress`.", id); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEV_PROGRESS_CRITERIA); stmt->setUInt16(0, uint16(id)); @@ -2636,17 +2636,15 @@ bool AchievementMgr::RequirementsSatisfied(AchievementCriteriaEntry const* ac bool matchFound = false; for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j) { - uint32 area_id = worldOverlayEntry->areatableID[j]; - if (!area_id) // array have 0 only in empty tail + AreaTableEntry const* area = sAreaTableStore.LookupEntry(worldOverlayEntry->areatableID[j]); + if (!area) break; - int32 exploreFlag = GetAreaFlagByAreaID(area_id); - if (exploreFlag < 0) + uint32 playerIndexOffset = uint32(area->exploreFlag) / 32; + if (playerIndexOffset >= PLAYER_EXPLORED_ZONES_SIZE) continue; - uint32 playerIndexOffset = uint32(exploreFlag) / 32; - uint32 mask = 1 << (uint32(exploreFlag) % 32); - + uint32 mask = 1 << (uint32(area->exploreFlag) % 32); if (referencePlayer->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask) { matchFound = true; @@ -3165,7 +3163,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaList() m_AchievementCriteriasByTimedType[criteria->timedCriteriaStartType].push_back(criteria); } - TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u achievement criteria and %u guild achievement crieteria in %u ms.", criterias, guildCriterias, GetMSTimeDiffToNow(oldMSTime)); } void AchievementGlobalMgr::LoadAchievementReferenceList() @@ -3194,7 +3192,7 @@ void AchievementGlobalMgr::LoadAchievementReferenceList() if (AchievementEntry const* achievement = sAchievementMgr->GetAchievement(4539)) const_cast(achievement)->mapID = 631; // Correct map requirement (currently has Ulduar) - TC_LOG_INFO("server.loading", ">> Loaded %u achievement references in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u achievement references in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } void AchievementGlobalMgr::LoadAchievementCriteriaData() @@ -3222,7 +3220,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() if (!criteria) { - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has data for non-existing criteria (Entry: %u), ignore.", criteria_id); + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains data for non-existing criteria (Entry: %u). Ignored.", criteria_id); continue; } @@ -3232,7 +3230,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData() if (scriptName.length()) // not empty { if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT) - TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` has ScriptName set for non-scripted data type (Entry: %u, type %u), useless data.", criteria_id, dataType); + TC_LOG_ERROR("sql.sql", "Table `achievement_criteria_data` contains a ScriptName for non-scripted data type (Entry: %u, type %u), useless data.", criteria_id, dataType); else scriptId = sObjectMgr->GetScriptId(scriptName); } @@ -3278,8 +3276,8 @@ void AchievementGlobalMgr::LoadCompletedAchievements() const AchievementEntry* achievement = sAchievementMgr->GetAchievement(achievementId); if (!achievement) { - // Remove non existent achievements from all characters - TC_LOG_ERROR("achievement", "Non-existing achievement %u data removed from table `character_achievement`.", achievementId); + // Remove non-existing achievements from all characters + TC_LOG_ERROR("achievement", "Non-existing achievement %u data has been removed from the table `character_achievement`.", achievementId); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_ACHIEVMENT); stmt->setUInt16(0, uint16(achievementId)); @@ -3292,7 +3290,7 @@ void AchievementGlobalMgr::LoadCompletedAchievements() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms", (unsigned long)m_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %lu realm first completed achievements in %u ms.", (unsigned long)m_allCompletedAchievements.size(), GetMSTimeDiffToNow(oldMSTime)); } void AchievementGlobalMgr::LoadRewards() @@ -3319,7 +3317,7 @@ void AchievementGlobalMgr::LoadRewards() AchievementEntry const* achievement = GetAchievement(entry); if (!achievement) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` has wrong achievement (Entry: %u), ignored.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` contains a wrong achievement entry (Entry: %u), ignored.", entry); continue; } @@ -3335,19 +3333,19 @@ void AchievementGlobalMgr::LoadRewards() // must be title or mail at least if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have title or item reward data, ignored.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not contain title or item reward data. Ignored.", entry); continue; } if (achievement->requiredFaction == ACHIEVEMENT_FACTION_ANY && (!reward.titleId[0] ^ !reward.titleId[1])) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains the title (A: %u H: %u) for only one team.", entry, reward.titleId[0], reward.titleId[1]); if (reward.titleId[0]) { CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]); if (!titleEntry) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_A`, set to 0", entry, reward.titleId[0]); reward.titleId[0] = 0; } } @@ -3357,7 +3355,7 @@ void AchievementGlobalMgr::LoadRewards() CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]); if (!titleEntry) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid title id (%u) in `title_H`, set to 0", entry, reward.titleId[1]); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid title id (%u) in `title_H`, set to 0", entry, reward.titleId[1]); reward.titleId[1] = 0; } } @@ -3367,41 +3365,41 @@ void AchievementGlobalMgr::LoadRewards() { if (!sObjectMgr->GetCreatureTemplate(reward.sender)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid creature entry %u as sender, mail reward skipped.", entry, reward.sender); reward.sender = 0; } } else { if (reward.itemId) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has item reward, item will not be rewarded.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains an item reward. Item will not be rewarded.", entry); if (!reward.subject.empty()) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mail subject.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains a mail subject.", entry); if (!reward.text.empty()) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mail text.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but contains mail text.", entry); if (reward.mailTemplate) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data but has mailTemplate.", entry); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) does not have sender data, but has a mailTemplate.", entry); } if (reward.mailTemplate) { if (!sMailTemplateStore.LookupEntry(reward.mailTemplate)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid mailTemplate (%u).", entry, reward.mailTemplate); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using an invalid mailTemplate (%u).", entry, reward.mailTemplate); reward.mailTemplate = 0; } else if (!reward.subject.empty() || !reward.text.empty()) - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has mailTemplate (%u) and mail subject/text.", entry, reward.mailTemplate); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) is using mailTemplate (%u) and mail subject/text.", entry, reward.mailTemplate); } if (reward.itemId) { if (!sObjectMgr->GetItemTemplate(reward.itemId)) { - TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) has invalid item id %u, reward mail will not contain item.", entry, reward.itemId); + TC_LOG_ERROR("sql.sql", "Table `achievement_reward` (Entry: %u) contains an invalid item id %u, reward mail will not contain the rewarded item.", entry, reward.itemId); reward.itemId = 0; } } @@ -3411,7 +3409,7 @@ void AchievementGlobalMgr::LoadRewards() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u achievement rewards in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u achievement rewards in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } void AchievementGlobalMgr::LoadRewardLocales() @@ -3426,7 +3424,7 @@ void AchievementGlobalMgr::LoadRewardLocales() if (!result) { - TC_LOG_INFO("server.loading", ">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty."); return; } @@ -3438,7 +3436,7 @@ void AchievementGlobalMgr::LoadRewardLocales() if (m_achievementRewards.find(entry) == m_achievementRewards.end()) { - TC_LOG_ERROR("sql.sql", "Table `locales_achievement_reward` (Entry: %u) has locale strings for non-existing achievement reward.", entry); + TC_LOG_ERROR("sql.sql", "Table `locales_achievement_reward` (Entry: %u) contains locale strings for a non-existing achievement reward.", entry); continue; } @@ -3453,7 +3451,7 @@ void AchievementGlobalMgr::LoadRewardLocales() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u achievement reward locale strings in %u ms", uint32(m_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u achievement reward locale strings in %u ms.", uint32(m_achievementRewardLocales.size()), GetMSTimeDiffToNow(oldMSTime)); } AchievementEntry const* AchievementGlobalMgr::GetAchievement(uint32 achievementId) const diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp index 04cbf41d810..16c65df4e7c 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.cpp +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.cpp @@ -576,6 +576,14 @@ void AuctionHouseObject::Update() if (AuctionsMap.empty()) return; + // Clear expired throttled players + for (PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.begin(); itr != GetAllThrottleMap.end();) + { + if (itr->second <= curTime) + itr = GetAllThrottleMap.erase(itr); + else + ++itr; + } SQLTransaction trans = CharacterDatabase.BeginTransaction(); @@ -651,13 +659,40 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player, std::wstring const& wsearchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount) + uint32& count, uint32& totalcount, bool getall) { int loc_idx = player->GetSession()->GetSessionDbLocaleIndex(); int locdbc_idx = player->GetSession()->GetSessionDbcLocale(); time_t curTime = sWorld->GetGameTime(); + PlayerGetAllThrottleMap::const_iterator itr = GetAllThrottleMap.find(player->GetGUID()); + time_t throttleTime = itr != GetAllThrottleMap.end() ? itr->second : curTime; + + if (getall && throttleTime <= curTime) + { + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) + { + AuctionEntry* Aentry = itr->second; + // Skip expired auctions + if (Aentry->expire_time < curTime) + continue; + + Item* item = sAuctionMgr->GetAItem(Aentry->itemGUIDLow); + if (!item) + continue; + + ++count; + ++totalcount; + Aentry->BuildAuctionInfo(data, item); + + if (count >= MAX_GETALL_RETURN) + break; + } + GetAllThrottleMap[player->GetGUID()] = curTime + sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY); + return; + } + for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr) { AuctionEntry* Aentry = itr->second; @@ -747,16 +782,16 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player if (count < 50 && totalcount >= listfrom) { ++count; - Aentry->BuildAuctionInfo(data); + Aentry->BuildAuctionInfo(data, item); } ++totalcount; } } //this function inserts to WorldPacket auction's data -bool AuctionEntry::BuildAuctionInfo(WorldPacket& data) const +bool AuctionEntry::BuildAuctionInfo(WorldPacket& data, Item* sourceItem) const { - Item* item = sAuctionMgr->GetAItem(itemGUIDLow); + Item* item = (sourceItem) ? sourceItem : sAuctionMgr->GetAItem(itemGUIDLow); if (!item) { TC_LOG_ERROR("misc", "AuctionEntry::BuildAuctionInfo: Auction %u has a non-existent item: %u", Id, itemGUIDLow); diff --git a/src/server/game/AuctionHouse/AuctionHouseMgr.h b/src/server/game/AuctionHouse/AuctionHouseMgr.h index 874cef189f6..7e31db01a22 100644 --- a/src/server/game/AuctionHouse/AuctionHouseMgr.h +++ b/src/server/game/AuctionHouse/AuctionHouseMgr.h @@ -30,6 +30,7 @@ class WorldPacket; #define MIN_AUCTION_TIME (12*HOUR) #define MAX_AUCTION_ITEMS 160 +#define MAX_GETALL_RETURN 55000 enum AuctionError { @@ -90,7 +91,7 @@ struct AuctionEntry uint8 GetHouseId() const { return houseId; } uint32 GetAuctionCut() const; uint32 GetAuctionOutBid() const; - bool BuildAuctionInfo(WorldPacket & data) const; + bool BuildAuctionInfo(WorldPacket & data, Item* sourceItem = nullptr) const; void DeleteFromDB(SQLTransaction& trans) const; void SaveToDB(SQLTransaction& trans) const; bool LoadFromDB(Field* fields); @@ -110,6 +111,7 @@ class AuctionHouseObject } typedef std::map AuctionEntryMap; + typedef std::unordered_map PlayerGetAllThrottleMap; uint32 Getcount() const { return AuctionsMap.size(); } @@ -133,10 +135,15 @@ class AuctionHouseObject void BuildListAuctionItems(WorldPacket& data, Player* player, std::wstring const& searchedname, uint32 listfrom, uint8 levelmin, uint8 levelmax, uint8 usable, uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality, - uint32& count, uint32& totalcount); + uint32& count, uint32& totalcount, bool getall = false); private: AuctionEntryMap AuctionsMap; + + // Map of throttled players for GetAll, and throttle expiry time + // Stored here, rather than player object to maintain persistence after logout + PlayerGetAllThrottleMap GetAllThrottleMap; + }; class AuctionHouseMgr diff --git a/src/server/game/Battlefield/Battlefield.cpp b/src/server/game/Battlefield/Battlefield.cpp index 2aae8f0138d..b9d39abfbf7 100644 --- a/src/server/game/Battlefield/Battlefield.cpp +++ b/src/server/game/Battlefield/Battlefield.cpp @@ -285,7 +285,7 @@ void Battlefield::InitStalker(uint32 entry, Position const& pos) if (Creature* creature = SpawnCreature(entry, pos, TEAM_NEUTRAL)) StalkerGuid = creature->GetGUID(); else - TC_LOG_ERROR("bg.battlefield", "Battlefield::InitStalker: could not spawn Stalker (Creature entry %u), zone messeges will be un-available", entry); + TC_LOG_ERROR("bg.battlefield", "Battlefield::InitStalker: Could not spawn Stalker (Creature entry %u), zone messages will be unavailable!", entry); } void Battlefield::KickAfkPlayers() @@ -543,10 +543,10 @@ BfGraveyard* Battlefield::GetGraveyardById(uint32 id) const if (BfGraveyard* graveyard = m_GraveyardList.at(id)) return graveyard; else - TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u not existed", id); + TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u does not exist.", id); } else - TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u cant be found", id); + TC_LOG_ERROR("bg.battlefield", "Battlefield::GetGraveyardById Id:%u could not be found.", id); return NULL; } @@ -764,7 +764,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl Map* map = sMapMgr->CreateBaseMap(m_MapId); if (!map) { - TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u map not found", entry); + TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Can't create creature entry: %u, map not found.", entry); return nullptr; } @@ -780,7 +780,7 @@ Creature* Battlefield::SpawnCreature(uint32 entry, float x, float y, float z, fl CreatureTemplate const* cinfo = sObjectMgr->GetCreatureTemplate(entry); if (!cinfo) { - TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: entry %u does not exist.", entry); + TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnCreature: Entry %u does not exist.", entry); return nullptr; } @@ -803,8 +803,8 @@ GameObject* Battlefield::SpawnGameObject(uint32 entry, float x, float y, float z GameObject* go = new GameObject; if (!go->Create(map->GenerateLowGuid(), entry, map, PHASEMASK_NORMAL, x, y, z, o, 0, 0, 0, 0, 100, GO_STATE_READY)) { - TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Gameobject template %u not found in database! Battlefield not created!", entry); - TC_LOG_ERROR("bg.battlefield", "Battlefield::SpawnGameObject: Cannot create gameobject template %u! Battlefield not created!", entry); + 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; } @@ -905,7 +905,7 @@ bool BfCapturePoint::SetCapturePointData(GameObject* capturePoint) GameObjectTemplate const* goinfo = capturePoint->GetGOInfo(); if (goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT) { - TC_LOG_ERROR("misc", "OutdoorPvP: GO %u is not capture point!", capturePoint->GetEntry()); + TC_LOG_ERROR("misc", "OutdoorPvP: GO %u is not a capture point!", capturePoint->GetEntry()); return false; } diff --git a/src/server/game/Battlegrounds/BattlegroundMgr.cpp b/src/server/game/Battlegrounds/BattlegroundMgr.cpp index 067083b64eb..d8697c62d88 100644 --- a/src/server/game/Battlegrounds/BattlegroundMgr.cpp +++ b/src/server/game/Battlegrounds/BattlegroundMgr.cpp @@ -790,7 +790,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates() BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId); if (!bl) { - TC_LOG_ERROR("bg.battleground", "Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeId); + TC_LOG_ERROR("bg.battleground", "Battleground ID %u could not be found in BattlemasterList.dbc. The battleground was not created.", bgTypeId); continue; } @@ -808,14 +808,14 @@ void BattlegroundMgr::LoadBattlegroundTemplates() if (bgTemplate.MaxPlayersPerTeam == 0 || bgTemplate.MinPlayersPerTeam > bgTemplate.MaxPlayersPerTeam) { - TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has bad values for MinPlayersPerTeam (%u) and MaxPlayersPerTeam(%u)", + TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains bad values for MinPlayersPerTeam (%u) and MaxPlayersPerTeam(%u).", bgTemplate.Id, bgTemplate.MinPlayersPerTeam, bgTemplate.MaxPlayersPerTeam); continue; } if (bgTemplate.MinLevel == 0 || bgTemplate.MaxLevel == 0 || bgTemplate.MinLevel > bgTemplate.MaxLevel) { - TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has bad values for MinLevel (%u) and MaxLevel (%u)", + TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains bad values for MinLevel (%u) and MaxLevel (%u).", bgTemplate.Id, bgTemplate.MinLevel, bgTemplate.MaxLevel); continue; } @@ -829,7 +829,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates() } else { - TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has non-existed WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTemplate.Id, startId); + TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains a non-existing WorldSafeLocs.dbc id %u in field `AllianceStartLoc`. BG not created.", bgTemplate.Id, startId); continue; } @@ -840,7 +840,7 @@ void BattlegroundMgr::LoadBattlegroundTemplates() } else { - TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u has non-existed WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTemplate.Id, startId); + TC_LOG_ERROR("sql.sql", "Table `battleground_template` for id %u contains a non-existing WorldSafeLocs.dbc id %u in field `HordeStartLoc`. BG not created.", bgTemplate.Id, startId); continue; } } @@ -1036,7 +1036,7 @@ BattlegroundTypeId BattlegroundMgr::BGTemplateId(BattlegroundQueueTypeId bgQueue case BATTLEGROUND_QUEUE_5v5: return BATTLEGROUND_AA; default: - return BattlegroundTypeId(0); // used for unknown template (it existed and do nothing) + return BattlegroundTypeId(0); // used for unknown template (it exists and does nothing) } } @@ -1140,7 +1140,7 @@ void BattlegroundMgr::LoadBattleMastersEntry() uint32 bgTypeId = fields[1].GetUInt32(); if (!sBattlemasterListStore.LookupEntry(bgTypeId)) { - TC_LOG_ERROR("sql.sql", "Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.", entry, bgTypeId); + TC_LOG_ERROR("sql.sql", "Table `battlemaster_entry` contains entry %u for a non-existing battleground type %u, ignored.", entry, bgTypeId); continue; } @@ -1160,7 +1160,7 @@ void BattlegroundMgr::CheckBattleMasters() { if ((itr->second.npcflag & UNIT_NPC_FLAG_BATTLEMASTER) && mBattleMastersMap.find(itr->second.Entry) == mBattleMastersMap.end()) { - TC_LOG_ERROR("sql.sql", "CreatureTemplate (Entry: %u) has UNIT_NPC_FLAG_BATTLEMASTER but no data in `battlemaster_entry` table. Removing flag!", itr->second.Entry); + TC_LOG_ERROR("sql.sql", "Creature_Template Entry: %u has UNIT_NPC_FLAG_BATTLEMASTER, but no data in the `battlemaster_entry` table. Removing flag.", itr->second.Entry); const_cast(&itr->second)->npcflag &= ~UNIT_NPC_FLAG_BATTLEMASTER; } } diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp index c6efdfcd1fd..af8c4f439f6 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundEY.cpp @@ -171,7 +171,7 @@ void BattlegroundEY::CheckSomeoneJoinedPoint() Player* player = ObjectAccessor::FindPlayer(m_PlayersNearPoint[EY_POINTS_MAX][j]); if (!player) { - TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneJoinedPoint: Player (%s) not found!", m_PlayersNearPoint[EY_POINTS_MAX][j].ToString().c_str()); + TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneJoinedPoint: Player (%s) could not be found!", m_PlayersNearPoint[EY_POINTS_MAX][j].ToString().c_str()); ++j; continue; } @@ -212,8 +212,8 @@ void BattlegroundEY::CheckSomeoneLeftPoint() Player* player = ObjectAccessor::FindPlayer(m_PlayersNearPoint[i][j]); if (!player) { - TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneLeftPoint Player (%s) not found!", m_PlayersNearPoint[i][j].ToString().c_str()); - //move not existed player to "free space" - this will cause many error showing in log, but it is a very important bug + TC_LOG_ERROR("bg.battleground", "BattlegroundEY:CheckSomeoneLeftPoint Player (%s) could not be found!", m_PlayersNearPoint[i][j].ToString().c_str()); + //move non-existing players to "free space" - this will cause many errors showing in log, but it is a very important bug m_PlayersNearPoint[EY_POINTS_MAX].push_back(m_PlayersNearPoint[i][j]); m_PlayersNearPoint[i].erase(m_PlayersNearPoint[i].begin() + j); continue; @@ -498,7 +498,7 @@ bool BattlegroundEY::SetupBattleground() || !AddObject(BG_EY_OBJECT_TOWER_CAP_MAGE_TOWER, BG_OBJECT_HU_TOWER_CAP_EY_ENTRY, 2282.121582f, 1760.006958f, 1189.707153f, 1.919862f, 0, 0, 0.819152f, 0.573576f, RESPAWN_ONE_DAY) ) { - TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn some object Battleground not created!"); + TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn some objects. The battleground was not created."); return false; } @@ -515,21 +515,21 @@ bool BattlegroundEY::SetupBattleground() || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REAVER + i * 3 + 1, Buff_Entries[1], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) || !AddObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REAVER + i * 3 + 2, Buff_Entries[2], at->x, at->y, at->z, 0.907571f, 0, 0, 0.438371f, 0.898794f, RESPAWN_ONE_DAY) ) - TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Cannot spawn buff"); + TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Could not spawn Speedbuff Fel Reaver."); } WorldSafeLocsEntry const* sg = NULL; sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_ALLIANCE); if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_ALLIANCE, sg->x, sg->y, sg->z, 3.124139f, TEAM_ALLIANCE)) { - TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide! Battleground not created!"); + TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide. The battleground was not created."); return false; } sg = sWorldSafeLocsStore.LookupEntry(EY_GRAVEYARD_MAIN_HORDE); if (!sg || !AddSpiritGuide(EY_SPIRIT_MAIN_HORDE, sg->x, sg->y, sg->z, 3.193953f, TEAM_HORDE)) { - TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide! Battleground not created!"); + TC_LOG_ERROR("sql.sql", "BatteGroundEY: Failed to spawn spirit guide. The battleground was not created."); return false; } @@ -595,7 +595,7 @@ void BattlegroundEY::RespawnFlagAfterDrop() if (obj) obj->Delete(); else - TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Unknown dropped flag (%s)", GetDroppedFlagGUID().ToString().c_str()); + TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Unknown dropped flag (%s).", GetDroppedFlagGUID().ToString().c_str()); SetDroppedFlagGUID(ObjectGuid::Empty); } @@ -767,7 +767,7 @@ void BattlegroundEY::EventTeamCapturedPoint(Player* player, uint32 Point) WorldSafeLocsEntry const* sg = NULL; sg = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[Point].GraveYardId); if (!sg || !AddSpiritGuide(Point, sg->x, sg->y, sg->z, 3.124139f, GetTeamIndexByTeamId(Team))) - TC_LOG_ERROR("bg.battleground", "BatteGroundEY: Failed to spawn spirit guide! point: %u, team: %u, graveyard_id: %u", + TC_LOG_ERROR("bg.battleground", "BatteGroundEY: Failed to spawn spirit guide. point: %u, team: %u, graveyard_id: %u", Point, Team, m_CapturingPointTypes[Point].GraveYardId); // SpawnBGCreature(Point, RESPAWN_IMMEDIATELY); @@ -917,7 +917,7 @@ WorldSafeLocsEntry const* BattlegroundEY::GetClosestGraveYard(Player* player) if (!entry) { - TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Not found the main team graveyard. Graveyard system isn't working!"); + TC_LOG_ERROR("bg.battleground", "BattlegroundEY: The main team graveyard could not be found. The graveyard system will not be operational!"); return NULL; } @@ -934,7 +934,7 @@ WorldSafeLocsEntry const* BattlegroundEY::GetClosestGraveYard(Player* player) { entry = sWorldSafeLocsStore.LookupEntry(m_CapturingPointTypes[i].GraveYardId); if (!entry) - TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Not found graveyard: %u", m_CapturingPointTypes[i].GraveYardId); + TC_LOG_ERROR("bg.battleground", "BattlegroundEY: Graveyard %u could not be found.", m_CapturingPointTypes[i].GraveYardId); else { distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z); diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp index 8176ddc8012..2cdbf3fb3ae 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundSA.cpp @@ -389,6 +389,8 @@ void BattlegroundSA::PostUpdateImpl(uint32 diff) { if (TotalTime >= BG_SA_ROUNDLENGTH) { + CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE); + CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE); RoundScores[0].winner = Attackers; RoundScores[0].time = BG_SA_ROUNDLENGTH; TotalTime = 0; @@ -401,8 +403,6 @@ void BattlegroundSA::PostUpdateImpl(uint32 diff) ToggleTimer(); ResetObjs(); GetBgMap()->UpdateAreaDependentAuras(); - CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE); - CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE); return; } } @@ -410,6 +410,8 @@ void BattlegroundSA::PostUpdateImpl(uint32 diff) { if (TotalTime >= EndRoundTimer) { + CastSpellOnTeam(SPELL_END_OF_ROUND, ALLIANCE); + CastSpellOnTeam(SPELL_END_OF_ROUND, HORDE); RoundScores[1].time = BG_SA_ROUNDLENGTH; RoundScores[1].winner = (Attackers == TEAM_ALLIANCE) ? TEAM_HORDE : TEAM_ALLIANCE; if (RoundScores[0].time == RoundScores[1].time) diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp index 4675fda810e..7c1e371f933 100644 --- a/src/server/game/Chat/Chat.cpp +++ b/src/server/game/Chat/Chat.cpp @@ -297,10 +297,10 @@ bool ChatHandler::ExecuteCommandInTable(std::vector const& table, c uint32 areaId = player->GetAreaId(); std::string areaName = "Unknown"; std::string zoneName = "Unknown"; - if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) { areaName = area->area_name; - if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone)) + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone)) zoneName = zone->area_name; } @@ -360,7 +360,7 @@ bool ChatHandler::SetDataForCommandInTable(std::vector& table, char // expected subcommand by full name DB content else if (*text) { - TC_LOG_ERROR("sql.sql", "Table `command` have unexpected subcommand '%s' in command '%s', skip.", text, fullcommand.c_str()); + TC_LOG_ERROR("sql.sql", "Table `command` contains an unexpected subcommand '%s' in command '%s', skipped.", text, fullcommand.c_str()); return false; } @@ -376,9 +376,9 @@ bool ChatHandler::SetDataForCommandInTable(std::vector& table, char if (!cmd.empty()) { if (&table == &getCommandTable()) - TC_LOG_ERROR("sql.sql", "Table `command` have not existed command '%s', skip.", cmd.c_str()); + TC_LOG_ERROR("sql.sql", "Table `command` contains a non-existing command '%s', skipped.", cmd.c_str()); else - TC_LOG_ERROR("sql.sql", "Table `command` have not existed subcommand '%s' in command '%s', skip.", cmd.c_str(), fullcommand.c_str()); + TC_LOG_ERROR("sql.sql", "Table `command` contains a non-existing subcommand '%s' in command '%s', skipped.", cmd.c_str(), fullcommand.c_str()); } return false; @@ -486,7 +486,7 @@ bool ChatHandler::ShowHelpForSubCommands(std::vector const& table, std::string list; for (uint32 i = 0; i < table.size(); ++i) { - // must be available (ignore handler existence for show command with possible available subcommands) + // must be available (ignore handler existence to show command with possible available subcommands) if (!isAvailable(table[i])) continue; @@ -525,7 +525,7 @@ bool ChatHandler::ShowHelpForCommand(std::vector const& table, cons { for (uint32 i = 0; i < table.size(); ++i) { - // must be available (ignore handler existence for show command with possible available subcommands) + // must be available (ignore handler existence to show command with possible available subcommands) if (!isAvailable(table[i])) continue; @@ -555,7 +555,7 @@ bool ChatHandler::ShowHelpForCommand(std::vector const& table, cons { for (uint32 i = 0; i < table.size(); ++i) { - // must be available (ignore handler existence for show command with possible available subcommands) + // must be available (ignore handler existence to show command with possible available subcommands) if (!isAvailable(table[i])) continue; @@ -1116,7 +1116,7 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, ObjectGuid* p *player_name = pl ? pl->GetName() : ""; } - // some from req. data must be provided (note: name is empty if player not exist) + // some from req. data must be provided (note: name is empty if player does not exist) if ((!player || !*player) && (!player_guid || !*player_guid) && (!player_name || player_name->empty())) { SendSysMessage(LANG_PLAYER_NOT_FOUND); diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index e2fad683aad..b56f9f1c70d 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -1749,7 +1749,7 @@ bool ConditionMgr::isSourceTypeValid(Condition* cond) const } case CONDITION_SOURCE_TYPE_PHASE: { - if (cond->SourceEntry && !GetAreaEntryByAreaID(cond->SourceEntry)) + if (cond->SourceEntry && !sAreaTableStore.LookupEntry(cond->SourceEntry)) { TC_LOG_ERROR("sql.sql", "%s SourceEntry in `condition` table, does not exist in AreaTable.dbc, ignoring.", cond->ToString().c_str()); return false; @@ -1826,7 +1826,7 @@ bool ConditionMgr::isConditionTypeValid(Condition* cond) const } case CONDITION_ZONEID: { - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(cond->ConditionValue1); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(cond->ConditionValue1); if (!areaEntry) { TC_LOG_ERROR("sql.sql", "%s Area (%u) does not exist, skipped.", cond->ToString(true).c_str(), cond->ConditionValue1); diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 4891b17d71b..7df533d4c59 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -52,11 +52,9 @@ struct WMOAreaTableTripple typedef std::map WMOAreaInfoByTripple; typedef std::multimap CharSectionsMap; -DBCStorage sAreaStore(AreaTableEntryfmt); +DBCStorage sAreaTableStore(AreaTableEntryfmt); DBCStorage sAreaGroupStore(AreaGroupEntryfmt); DBCStorage sAreaPOIStore(AreaPOIEntryfmt); -static AreaFlagByAreaID sAreaFlagByAreaID; -static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files static WMOAreaInfoByTripple sWMOAreaInfoByTripple; @@ -95,6 +93,9 @@ DBCStorage sDurabilityCostsStore(DurabilityCostsfmt); DBCStorage sEmotesStore(EmotesEntryfmt); DBCStorage sEmotesTextStore(EmotesTextEntryfmt); +typedef std::tuple EmotesTextSoundKey; +static std::map sEmotesTextSoundMap; +DBCStorage sEmotesTextSoundStore(EmotesTextSoundEntryfmt); typedef std::map FactionTeamMap; static FactionTeamMap sFactionTeamMap; @@ -337,21 +338,7 @@ void LoadDBCStores(const std::string& dataPath) StoreProblemList bad_dbc_files; uint32 availableDbcLocales = 0xFFFFFFFF; - LoadDBC(availableDbcLocales, bad_dbc_files, sAreaStore, dbcPath, "AreaTable.dbc"); - - // must be after sAreaStore loading - for (uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0 - { - if (AreaTableEntry const* area = sAreaStore.LookupEntry(i)) - { - // fill AreaId->DBC records - sAreaFlagByAreaID.insert(AreaFlagByAreaID::value_type(uint16(area->ID), area->exploreFlag)); - - // fill MapId->DBC records (skip sub zones and continents) - if (area->zone == 0 && area->mapid != 0 && area->mapid != 1 && area->mapid != 530 && area->mapid != 571) - sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid, area->exploreFlag)); - } - } + LoadDBC(availableDbcLocales, bad_dbc_files, sAreaTableStore, dbcPath, "AreaTable.dbc"); LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementStore, dbcPath, "Achievement.dbc", &CustomAchievementfmt, &CustomAchievementIndex);//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sAchievementCriteriaStore, dbcPath, "Achievement_Criteria.dbc");//15595 @@ -397,6 +384,10 @@ void LoadDBCStores(const std::string& dataPath) } } + LoadDBC(availableDbcLocales, bad_dbc_files, sEmotesTextSoundStore, dbcPath, "EmotesTextSound.dbc"); + for (uint32 i = 0; i < sEmotesTextSoundStore.GetNumRows(); ++i) + if (EmotesTextSoundEntry const* entry = sEmotesTextSoundStore.LookupEntry(i)) + sEmotesTextSoundMap[EmotesTextSoundKey(entry->EmotesTextId, entry->RaceId, entry->SexId)] = entry; LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");//15595 LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoExtraStore, dbcPath, "CreatureDisplayInfoExtra.dbc");//15595 @@ -854,7 +845,7 @@ void LoadDBCStores(const std::string& dataPath) } // Check loaded DBC files proper version - if (!sAreaStore.LookupEntry(4713) || // last area (areaflag) added in 4.3.4 (15595) + if (!sAreaTableStore.LookupEntry(5995) || // last area (areaflag) added in 4.3.4 (15595) !sCharTitlesStore.LookupEntry(287) || // last char title added in 4.3.4 (15595) !sGemPropertiesStore.LookupEntry(2250) || // last gem property added in 4.3.4 (15595) !sMapStore.LookupEntry(980) || // last map added in 4.3.4 (15595) @@ -911,41 +902,13 @@ uint32 GetTalentSpellCost(uint32 spellId) return 0; } -int32 GetAreaFlagByAreaID(uint32 area_id) -{ - AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id); - if (i == sAreaFlagByAreaID.end()) - return -1; - - return i->second; -} WMOAreaTableEntry const* GetWMOAreaTableEntryByTripple(int32 rootid, int32 adtid, int32 groupid) { WMOAreaInfoByTripple::iterator i = sWMOAreaInfoByTripple.find(WMOAreaTableTripple(rootid, adtid, groupid)); - if (i == sWMOAreaInfoByTripple.end()) - return NULL; - return i->second; -} - -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id) -{ - int32 areaflag = GetAreaFlagByAreaID(area_id); - if (areaflag < 0) + if (i == sWMOAreaInfoByTripple.end()) return NULL; - - return sAreaStore.LookupEntry(areaflag); -} - -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id) -{ - if (area_flag) - return sAreaStore.LookupEntry(area_flag); - - if (MapEntry const* mapEntry = sMapStore.LookupEntry(map_id)) - return GetAreaEntryByAreaID(mapEntry->linked_zone); - - return NULL; + return i->second; } char const* GetRaceName(uint8 race, uint8 /*locale*/) @@ -960,15 +923,6 @@ char const* GetClassName(uint8 class_, uint8 /*locale*/) return classEntry ? classEntry->name : NULL; } -uint32 GetAreaFlagByMapId(uint32 mapid) -{ - AreaFlagByMapID::iterator i = sAreaFlagByMapID.find(mapid); - if (i == sAreaFlagByMapID.end()) - return 0; - else - return i->second; -} - uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId) { if (mapid != 530 && mapid != 571 && mapid != 732) // speed for most cases @@ -1404,3 +1358,8 @@ ResponseCodes ValidateName(std::string const& name, LocaleConstant locale) return CHAR_NAME_SUCCESS; } + +EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender) +{ + return sEmotesTextSoundMap[EmotesTextSoundKey(emote, race, gender)]; +} diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index 647a659e8ae..a1f259b2cc8 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -33,11 +33,6 @@ char const* GetPetName(uint32 petfamily, uint32 dbclang); uint32 GetTalentSpellCost(uint32 spellId); TalentSpellPos const* GetTalentSpellPos(uint32 spellId); -int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found -AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id); -AreaTableEntry const* GetAreaEntryByAreaFlagAndMap(uint32 area_flag, uint32 map_id); -uint32 GetAreaFlagByMapId(uint32 mapid); - char const* GetRaceName(uint8 race, uint8 locale); char const* GetClassName(uint8 class_, uint8 locale); @@ -93,9 +88,11 @@ SkillRaceClassInfoEntry const* GetSkillRaceClassInfo(uint32 skill, uint8 race, u ResponseCodes ValidateName(std::string const& name, LocaleConstant locale); +EmotesTextSoundEntry const* FindTextSoundEmoteFor(uint32 emote, uint32 race, uint32 gender); + extern DBCStorage sAchievementStore; extern DBCStorage sAchievementCriteriaStore; -extern DBCStorage sAreaStore;// recommend access using functions +extern DBCStorage sAreaTableStore; extern DBCStorage sAreaGroupStore; extern DBCStorage sAreaPOIStore; extern DBCStorage sAreaTriggerStore; @@ -126,6 +123,7 @@ extern DBCStorage sDurabilityCostsStore; extern DBCStorage sDurabilityQualityStore; extern DBCStorage sEmotesStore; extern DBCStorage sEmotesTextStore; +extern DBCStorage sEmotesTextSoundStore; extern DBCStorage sFactionStore; extern DBCStorage sFactionTemplateStore; extern DBCStorage sGameObjectDisplayInfoStore; diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 3cc660e0175..e01ca3268c2 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -525,7 +525,7 @@ struct AreaTableEntry uint32 ID; // 0 uint32 mapid; // 1 uint32 zone; // 2 if 0 then it's zone, else it's zone id of this area - uint32 exploreFlag; // 3, main index + uint32 exploreFlag; // 3 uint32 flags; // 4, //uint32 unk5; // 5, //uint32 unk6; // 6, @@ -980,6 +980,15 @@ struct EmotesTextEntry uint32 textid; }; +struct EmotesTextSoundEntry +{ + uint32 Id; // 0 + uint32 EmotesTextId; // 1 + uint32 RaceId; // 2 + uint32 SexId; // 3, 0 male / 1 female + uint32 SoundId; // 4 +}; + struct FactionEntry { uint32 ID; // 0 m_ID diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h index bcc6734bbe1..d36cfa0e3e7 100644 --- a/src/server/game/DataStores/DBCfmt.h +++ b/src/server/game/DataStores/DBCfmt.h @@ -26,7 +26,7 @@ char const Achievementfmt[] = "niixsxiixixxii"; const std::string CustomAchievementfmt = "pppaaaapapaapp"; const std::string CustomAchievementIndex = "ID"; char const AchievementCriteriafmt[] = "niiiliiiisiiiiixxiiiiii"; -char const AreaTableEntryfmt[] = "iiinixxxxxisiiiiiffixxxxxx"; +char const AreaTableEntryfmt[] = "niiiixxxxxisiiiiiffixxxxxx"; char const AreaGroupEntryfmt[] = "niiiiiii"; char const AreaPOIEntryfmt[] = "niiiiiiiiiiiffixixxixx"; char const AreaTriggerEntryfmt[] = "nifffxxxfffff"; @@ -57,6 +57,7 @@ char const DurabilityCostsfmt[] = "niiiiiiiiiiiiiiiiiiiiiiiiiiiii"; char const DurabilityQualityfmt[] = "nf"; char const EmotesEntryfmt[] = "nxxiiixx"; char const EmotesTextEntryfmt[] = "nxixxxxxxxxxxxxxxxx"; +char const EmotesTextSoundEntryfmt[] = "niiii"; char const FactionEntryfmt[] = "niiiiiiiiiiiiiiiiiiffixsxi"; char const FactionTemplateEntryfmt[] = "niiiiiiiiiiiii"; char const GameObjectDisplayInfofmt[] = "nsxxxxxxxxxxffffffxxx"; diff --git a/src/server/game/Entities/Corpse/Corpse.cpp b/src/server/game/Entities/Corpse/Corpse.cpp index d1e6f1153ea..5abe223e2f6 100644 --- a/src/server/game/Entities/Corpse/Corpse.cpp +++ b/src/server/game/Entities/Corpse/Corpse.cpp @@ -158,7 +158,7 @@ bool Corpse::LoadCorpseFromDB(ObjectGuid::LowType guid, Field* fields) SetObjectScale(1.0f); SetUInt32Value(CORPSE_FIELD_DISPLAY_ID, fields[5].GetUInt32()); - _LoadIntoDataField(fields[6].GetCString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END); + _LoadIntoDataField(fields[6].GetString(), CORPSE_FIELD_ITEM, EQUIPMENT_SLOT_END); SetUInt32Value(CORPSE_FIELD_BYTES_1, fields[7].GetUInt32()); SetUInt32Value(CORPSE_FIELD_BYTES_2, fields[8].GetUInt32()); SetUInt32Value(CORPSE_FIELD_FLAGS, fields[9].GetUInt8()); diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d8991dc9dca..4c3f55ec5fc 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -369,13 +369,13 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender); // Load creature equipment - if (!data || data->equipmentId == 0) - LoadEquipment(); // use default equipment (if available) - else if (data && data->equipmentId != 0) // override, 0 means no equipment + if (data && data->equipmentId != 0) { m_originalEquipmentId = data->equipmentId; LoadEquipment(data->equipmentId); } + else + LoadEquipment(0, true); SetName(normalInfo->Name); // at normal entry always @@ -911,6 +911,12 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 /*phaseMask* if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING) AddUnitState(UNIT_STATE_IGNORE_PATHFINDING); + if (GetCreatureTemplate()->flags_extra & CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK) + { + ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); + ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK_DEST, true); + } + return true; } @@ -1312,8 +1318,38 @@ bool Creature::CreateFromProto(ObjectGuid::LowType guidlow, uint32 entry, Creatu return true; } -bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap) +bool Creature::LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap, bool allowDuplicate) { + if (!allowDuplicate) + { + // If an alive instance of this spawnId is already found, skip creation + // If only dead instance(s) exist, despawn them and spawn a new (maybe also dead) version + const auto creatureBounds = map->GetCreatureBySpawnIdStore().equal_range(spawnId); + std::vector despawnList; + + if (creatureBounds.first != creatureBounds.second) + { + for (auto itr = creatureBounds.first; itr != creatureBounds.second; ++itr) + { + if (itr->second->IsAlive()) + { + TC_LOG_DEBUG("maps", "Would have spawned %u but %s already exists", spawnId, creatureBounds.first->second->GetGUID().ToString().c_str()); + return false; + } + else + { + despawnList.push_back(itr->second); + TC_LOG_DEBUG("maps", "Despawned dead instance of spawn %u (%s)", spawnId, itr->second->GetGUID().ToString().c_str()); + } + } + + for (Creature* despawnCreature : despawnList) + { + despawnCreature->AddObjectToRemoveList(); + } + } + } + CreatureData const* data = sObjectMgr->GetCreatureData(spawnId); if (!data) @@ -1393,6 +1429,7 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/) SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, 0); m_equipmentId = 0; } + return; } @@ -1401,7 +1438,7 @@ void Creature::LoadEquipment(int8 id, bool force /*= true*/) return; m_equipmentId = id; - for (uint8 i = 0; i < 3; ++i) + for (uint8 i = 0; i < MAX_EQUIPMENT_ITEMS; ++i) SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + i, einfo->ItemEntry[i]); } @@ -1624,10 +1661,28 @@ void Creature::setDeathState(DeathState s) UpdateMovementFlags(); - CreatureTemplate const* cinfo = GetCreatureTemplate(); - SetUInt32Value(UNIT_NPC_FLAGS, cinfo->npcflag); ClearUnitState(uint32(UNIT_STATE_ALL_STATE & ~UNIT_STATE_IGNORE_PATHFINDING)); - SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); + + if (!IsPet()) + { + CreatureData const* creatureData = GetCreatureData(); + CreatureTemplate const* cinfo = GetCreatureTemplate(); + + uint32 npcflag, unit_flags, dynamicflags; + ObjectMgr::ChooseCreatureFlags(cinfo, npcflag, unit_flags, dynamicflags, creatureData); + + SetUInt32Value(UNIT_NPC_FLAGS, npcflag); + SetUInt32Value(UNIT_FIELD_FLAGS, unit_flags); + SetUInt32Value(UNIT_DYNAMIC_FLAGS, dynamicflags); + + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT); + + SetMeleeDamageSchool(SpellSchools(cinfo->dmgschool)); + + if (creatureData && GetPhaseMask() != creatureData->phaseMask) + SetPhaseMask(creatureData->phaseMask, false); + } + Motion_Initialize(); Unit::setDeathState(ALIVE); LoadCreaturesAddon(); @@ -2114,6 +2169,11 @@ bool Creature::CanCreatureAttack(Unit const* victim, bool /*force*/) const if (GetMap()->IsDungeon()) return true; + // if the mob is actively being damaged, do not reset due to distance unless it's a world boss + if (!isWorldBoss()) + if (time(NULL) - GetLastDamagedTime() <= MAX_AGGRO_RESET_TIME) + return true; + //Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and out of combat every update tick. float dist = std::max(GetAttackDistance(victim), sWorld->getFloatConfig(CONFIG_THREAT_RADIUS)) + m_CombatDistance; diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 9022444caca..fbf68e69152 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -39,23 +39,25 @@ class WorldSession; enum CreatureFlagsExtra { - CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group - CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility) - CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry - CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry - CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block - CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks - CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP - CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature - CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me - CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) - CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard - CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes - CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills - CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre - CREATURE_FLAG_EXTRA_ALL_DIMINISH = 0x00100000, // Creature is subject to all diminishing returns as player are - CREATURE_FLAG_EXTRA_DUNGEON_BOSS = 0x10000000, // creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB) - CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING = 0x20000000 // creature ignore pathfinding + CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group + CREATURE_FLAG_EXTRA_CIVILIAN = 0x00000002, // not aggro (ignore faction/reputation hostility) + CREATURE_FLAG_EXTRA_NO_PARRY = 0x00000004, // creature can't parry + CREATURE_FLAG_EXTRA_NO_PARRY_HASTEN = 0x00000008, // creature can't counter-attack at parry + CREATURE_FLAG_EXTRA_NO_BLOCK = 0x00000010, // creature can't block + CREATURE_FLAG_EXTRA_NO_CRUSH = 0x00000020, // creature can't do crush attacks + CREATURE_FLAG_EXTRA_NO_XP_AT_KILL = 0x00000040, // creature kill not provide XP + CREATURE_FLAG_EXTRA_TRIGGER = 0x00000080, // trigger creature + CREATURE_FLAG_EXTRA_NO_TAUNT = 0x00000100, // creature is immune to taunt auras and effect attack me + CREATURE_FLAG_EXTRA_WORLDEVENT = 0x00004000, // custom flag for world event creatures (left room for merging) + CREATURE_FLAG_EXTRA_GUARD = 0x00008000, // Creature is guard + CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes + CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills + CREATURE_FLAG_EXTRA_TAUNT_DIMINISH = 0x00080000, // Taunt is a subject to diminishing returns on this creautre + CREATURE_FLAG_EXTRA_ALL_DIMINISH = 0x00100000, // creature is subject to all diminishing returns as player are + CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ = 0x00200000, // creature does not need to take player damage for kill credit + CREATURE_FLAG_EXTRA_DUNGEON_BOSS = 0x10000000, // creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB) + CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING = 0x20000000, // creature ignore pathfinding + CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK = 0x40000000 // creature is immune to knockback effects }; #define CREATURE_FLAG_EXTRA_DB_ALLOWED (CREATURE_FLAG_EXTRA_INSTANCE_BIND | CREATURE_FLAG_EXTRA_CIVILIAN | \ @@ -63,7 +65,7 @@ enum CreatureFlagsExtra CREATURE_FLAG_EXTRA_NO_CRUSH | CREATURE_FLAG_EXTRA_NO_XP_AT_KILL | CREATURE_FLAG_EXTRA_TRIGGER | \ CREATURE_FLAG_EXTRA_NO_TAUNT | CREATURE_FLAG_EXTRA_WORLDEVENT | CREATURE_FLAG_EXTRA_NO_CRIT | \ CREATURE_FLAG_EXTRA_NO_SKILLGAIN | CREATURE_FLAG_EXTRA_TAUNT_DIMINISH | CREATURE_FLAG_EXTRA_ALL_DIMINISH | \ - CREATURE_FLAG_EXTRA_GUARD | CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING) + CREATURE_FLAG_EXTRA_GUARD | CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING | CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ | CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK) #define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS @@ -532,7 +534,7 @@ class Creature : public Unit, public GridObject, public MapObject void setDeathState(DeathState s) override; // override virtual Unit::setDeathState bool LoadFromDB(ObjectGuid::LowType spawnId, Map* map) { return LoadCreatureFromDB(spawnId, map, false); } - bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true); + bool LoadCreatureFromDB(ObjectGuid::LowType spawnId, Map* map, bool addToMap = true, bool allowDuplicate = false); void SaveToDB(); // overriden in Pet virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask); @@ -654,7 +656,7 @@ class Creature : public Unit, public GridObject, public MapObject void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; } bool IsReputationGainDisabled() const { return DisableReputationGain; } - bool IsDamageEnoughForLootingAndReward() const { return m_PlayerDamageReq == 0; } + bool IsDamageEnoughForLootingAndReward() const { return (m_creatureInfo->flags_extra & CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ) || (m_PlayerDamageReq == 0); } void LowerPlayerDamageReq(uint32 unDamage); void ResetPlayerDamageReq() { m_PlayerDamageReq = GetHealth() / 2; } uint32 m_PlayerDamageReq; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index c3d793a7553..fd2302ef4c5 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -1392,7 +1392,15 @@ void GameObject::Use(Unit* user) break; } - player->KillCreditGO(info->entry, GetGUID()); + if (Group* group = player->GetGroup()) + { + for (GroupReference const* itr = group->GetFirstMember(); itr != nullptr; itr = itr->next()) + if (Player* member = itr->GetSource()) + if (member->IsAtGroupRewardDistance(this)) + member->KillCreditGO(info->entry, GetGUID()); + } + else + player->KillCreditGO(info->entry, GetGUID()); } if (uint32 trapEntry = info->goober.linkedTrapId) @@ -1430,7 +1438,10 @@ void GameObject::Use(Unit* user) player->SendCinematicStart(info->camera.cinematicId); if (info->camera.eventID) + { GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this); + EventInform(info->camera.eventID, user); + } return; } diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 78b99d692db..effd616f847 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -452,8 +452,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid owner_guid, Field* fi need_save = true; } - std::string enchants = fields[6].GetString(); - _LoadIntoDataField(enchants.c_str(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET); + _LoadIntoDataField(fields[6].GetString(), ITEM_FIELD_ENCHANTMENT_1_1, MAX_ENCHANTMENT_SLOT * MAX_ENCHANTMENT_OFFSET); SetInt32Value(ITEM_FIELD_RANDOM_PROPERTIES_ID, fields[7].GetInt16()); // recalculate suffix factor if (GetItemRandomPropertyId() < 0) diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 946fe37eaf8..b2dcd7fbd8a 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -222,7 +222,7 @@ void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all) if (all) { for (uint8 i = 0; i < TaxiMaskSize; ++i) - data << uint8(sTaxiNodesMask[i]); // all existed nodes + data << uint8(sTaxiNodesMask[i]); // all existing nodes } else { @@ -607,7 +607,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo PlayerInfo const* info = sObjectMgr->GetPlayerInfo(createInfo->Race, createInfo->Class); if (!info) { - TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid race/class pair (%u/%u) - refusing to do so.", + TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid race/class pair (%u/%u) - refusing to do so.", GetSession()->GetAccountId(), m_name.c_str(), createInfo->Race, createInfo->Class); return false; } @@ -620,7 +620,7 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(createInfo->Class); if (!cEntry) { - TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid character class (%u) - refusing to do so (wrong DBC-files?)", + TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid character class (%u) - refusing to do so (wrong DBC-files?)", GetSession()->GetAccountId(), m_name.c_str(), createInfo->Class); return false; } @@ -635,14 +635,14 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo if (!IsValidGender(createInfo->Gender)) { - TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with an invalid gender (%u) - refusing to do so", + TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with an invalid gender (%u) - refusing to do so", GetSession()->GetAccountId(), m_name.c_str(), createInfo->Gender); return false; } if (!ValidateAppearance(createInfo->Race, createInfo->Class, createInfo->Gender, createInfo->HairStyle, createInfo->HairColor, createInfo->Face, createInfo->FacialHair, createInfo->Skin, true)) { - TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking-attempt: Account %u tried creating a character named '%s' with invalid appearance attributes - refusing to do so", + TC_LOG_ERROR("entities.player", "Player::Create: Possible hacking attempt: Account %u tried to create a character named '%s' with invalid appearance attributes - refusing to do so", GetSession()->GetAccountId(), m_name.c_str()); return false; } @@ -959,9 +959,9 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage) if (!IsAlive()) { - if (type == DAMAGE_FALL) // DealDamage not apply item durability loss at self damage + if (type == DAMAGE_FALL) // DealDamage does not apply item durability loss from self-induced damage. { - TC_LOG_DEBUG("entities.player", "We are fall to death, loosing 10 percents durability"); + TC_LOG_DEBUG("entities.player", "You have died from falling, losing 10 percent total armor durability."); DurabilityLossAll(0.10f, false); // durability lost message SendDurabilityLoss(this, 10); @@ -1544,7 +1544,7 @@ void Player::setDeathState(DeathState s) { if (!cur) { - TC_LOG_ERROR("entities.player", "setDeathState: attempt to kill a dead player %s(%d)", GetName().c_str(), GetGUID().GetCounter()); + TC_LOG_ERROR("entities.player", "setDeathState: Attempted to kill a dead player %s(%d)", GetName().c_str(), GetGUID().GetCounter()); return; } @@ -1831,7 +1831,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // client without expansion support if (GetSession()->Expansion() < mEntry->Expansion()) { - TC_LOG_DEBUG("maps", "Player %s using client without required expansion tried teleport to non accessible map %u", GetName().c_str(), mapid); + TC_LOG_DEBUG("maps", "Player %s using client without required expansion tried teleporting to non accessible map %u", GetName().c_str(), mapid); if (Transport* transport = GetTransport()) { @@ -2474,7 +2474,7 @@ bool Player::CanInteractWithQuestGiver(Object* questGiver) return false; } -Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask) +Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflagmask) { // unit checks if (!guid) @@ -2518,7 +2518,21 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask) return creature; } -GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const +GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const +{ + if (GameObject* go = GetMap()->GetGameObject(guid)) + { + if (go->IsWithinDistInMap(this, go->GetInteractionDistance())) + return go; + + TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)", go->GetGOInfo()->name.c_str(), + go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this), go->GetInteractionDistance()); + } + + return nullptr; +} + +GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const { if (GameObject* go = GetMap()->GetGameObject(guid)) { @@ -2527,12 +2541,12 @@ GameObject* Player::GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTy if (go->IsWithinDistInMap(this, go->GetInteractionDistance())) return go; - TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name.c_str(), - go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this)); + TC_LOG_DEBUG("maps", "GetGameObjectIfCanInteractWith: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal %f is allowed)", go->GetGOInfo()->name.c_str(), + go->GetGUID().GetCounter(), GetName().c_str(), GetGUID().GetCounter(), go->GetDistance(this), go->GetInteractionDistance()); } } - return NULL; + return nullptr; } bool Player::IsUnderWater() const @@ -3213,12 +3227,12 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) // do character spell book cleanup (all characters) if (!IsInWorld() && !learning) // spell load case { - TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore. Deleting spell for all characters in `character_spell`.", spellId); DeleteSpellFromAllPlayers(spellId); } else - TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore.", spellId); return false; } @@ -3228,12 +3242,12 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) // do character spell book cleanup (all characters) if (!IsInWorld() && !learning) // spell load case { - TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed, deleting for all characters in `character_talent`.", spellId); + TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u, learning this spell is not allowed. Deleting this spell for all characters in `character_talent`.", spellId); DeleteSpellFromAllPlayers(spellId); } else - TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u learning not allowed.", spellId); + TC_LOG_ERROR("spells", "Player::addTalent: Broken spell #%u, learning this spell is not allowed.", spellId); return false; } @@ -3278,12 +3292,12 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent // do character spell book cleanup (all characters) if (!IsInWorld() && !learning) // spell load case { - TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request, deleting for all characters in `character_spell`.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore. Deleting for all characters in `character_spell`.", spellId); DeleteSpellFromAllPlayers(spellId); } else - TC_LOG_ERROR("spells", "Player::addSpell: Non-existed in SpellStore spell #%u request.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Non-existing spell #%u requested from SpellStore.", spellId); return false; } @@ -3293,12 +3307,12 @@ bool Player::AddSpell(uint32 spellId, bool active, bool learning, bool dependent // do character spell book cleanup (all characters) if (!IsInWorld() && !learning) // spell load case { - TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u learning not allowed, deleting for all characters in `character_spell`.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u, learning this spell is not allowed. Deleting this spell for all characters in `character_spell`.", spellId); DeleteSpellFromAllPlayers(spellId); } else - TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u learning not allowed.", spellId); + TC_LOG_ERROR("spells", "Player::addSpell: Broken spell #%u, learning this spell is not allowed.", spellId); return false; } @@ -3968,7 +3982,7 @@ bool Player::ResetTalents(bool no_cost) for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) { - // skip non-existant talent ranks + // skip non-existing talent ranks if (talentInfo->RankID[rank] == 0) continue; const SpellInfo* _spellEntry = sSpellMgr->GetSpellInfo(talentInfo->RankID[rank]); @@ -4662,6 +4676,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness) // remove death flag + set aura SetByteValue(UNIT_FIELD_BYTES_1, 3, 0x00); + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); // This must be called always even on Players with race != RACE_NIGHTELF in case of faction change RemoveAurasDueToSpell(20584); // RACE_NIGHTELF speed bonuses @@ -4773,7 +4788,7 @@ void Player::OfflineResurrect(ObjectGuid const& guid, SQLTransaction& trans) Corpse* Player::CreateCorpse() { - // prevent existence 2 corpse for player + // prevent the existence of 2 corpses for one player SpawnCorpseBones(); uint32 _pb, _pb2, _cfb1, _cfb2; @@ -4996,7 +5011,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g DurabilityCostsEntry const* dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel); if (!dcost) { - TC_LOG_ERROR("entities.player.items", "RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel); + TC_LOG_ERROR("entities.player.items", "RepairDurability: Wrong item level %u", ditemProto->ItemLevel); return TotalCost; } @@ -5020,7 +5035,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g { if (GetGuildId() == 0) { - TC_LOG_DEBUG("entities.player.items", "You are not member of a guild"); + TC_LOG_DEBUG("entities.player.items", "You are not member of a guild."); return TotalCost; } @@ -5035,7 +5050,7 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g } else if (!HasEnoughMoney(uint64(costs))) { - TC_LOG_DEBUG("entities.player.items", "You do not have enough money"); + TC_LOG_DEBUG("entities.player.items", "You do not have enough money."); return TotalCost; } else @@ -5057,7 +5072,7 @@ void Player::RepopAtGraveyard() // note: this can be called also when the player is alive // for example from WorldSession::HandleMovementOpcodes - AreaTableEntry const* zone = GetAreaEntryByAreaID(GetAreaId()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetAreaId()); // Such zones are considered unreachable as a ghost and the player must be automatically revived if ((!IsAlive() && zone && zone->flags & AREA_FLAG_NEED_FLY) || GetTransport() || GetPositionZ() < (zone ? zone->MaxDepth : -500.0f)) @@ -5100,6 +5115,8 @@ void Player::RepopAtGraveyard() } else if (GetPositionZ() < zone->MaxDepth) TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation()); + + RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); } bool Player::CanJoinConstantChannelInZone(ChatChannelsEntry const* channel, AreaTableEntry const* zone) @@ -5144,7 +5161,7 @@ void Player::UpdateLocalChannels(uint32 newZone) if (GetSession()->PlayerLoading() && !IsBeingTeleportedFar()) return; // The client handles it automatically after loading, but not after teleporting - AreaTableEntry const* current_zone = GetAreaEntryByAreaID(newZone); + AreaTableEntry const* current_zone = sAreaTableStore.LookupEntry(newZone); if (!current_zone) return; @@ -5238,7 +5255,7 @@ void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, floa { if (modGroup >= BASEMOD_END || modType >= MOD_END) { - TC_LOG_ERROR("spells", "ERROR in HandleBaseModValue(): non existed BaseModGroup of wrong BaseModType!"); + TC_LOG_ERROR("spells", "ERROR in HandleBaseModValue(): Non-existing BaseModGroup or wrong BaseModType!"); return; } @@ -5263,7 +5280,7 @@ float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const { if (modGroup >= BASEMOD_END || modType >= MOD_END) { - TC_LOG_ERROR("spells", "trial to access non existed BaseModGroup or wrong BaseModType!"); + TC_LOG_ERROR("spells", "Attempt to access non-existing BaseModGroup or wrong BaseModType!"); return 0.0f; } @@ -5277,7 +5294,7 @@ float Player::GetTotalBaseModValue(BaseModGroup modGroup) const { if (modGroup >= BASEMOD_END) { - TC_LOG_ERROR("spells", "wrong BaseModGroup in GetTotalBaseModValue()!"); + TC_LOG_ERROR("spells", "Wrong BaseModGroup in GetTotalBaseModValue()!"); return 0.0f; } @@ -6155,20 +6172,20 @@ bool Player::IsActionButtonDataValid(uint8 button, uint32 action, uint8 type) case ACTION_BUTTON_SPELL: if (!sSpellMgr->GetSpellInfo(action)) { - TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): spell not exist", action, button, GetName().c_str(), GetGUID().GetCounter()); + TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): spell does not exist", action, button, GetName().c_str(), GetGUID().GetCounter()); return false; } if (!HasSpell(action)) { - TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): player don't known this spell", action, button, GetName().c_str(), GetGUID().GetCounter()); + TC_LOG_DEBUG("entities.player", "Spell action %u not added into button %u for player %s (GUID: %u): player does not know this spell", action, button, GetName().c_str(), GetGUID().GetCounter()); return false; } break; case ACTION_BUTTON_ITEM: if (!sObjectMgr->GetItemTemplate(action)) { - TC_LOG_DEBUG("entities.player", "Item action %u not added into button %u for player %s (GUID: %u): item not exist", action, button, GetName().c_str(), GetGUID().GetCounter()); + TC_LOG_DEBUG("entities.player", "Item action %u not added into button %u for player %s (GUID: %u): item does not exist", action, button, GetName().c_str(), GetGUID().GetCounter()); return false; } break; @@ -6191,7 +6208,7 @@ ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type) if (!IsActionButtonDataValid(button, action, type)) return NULL; - // it create new button (NEW state) if need or return existed + // it create new button (NEW state) if need or return existing ActionButton& ab = m_actionButtons[button]; // set data and update to CHANGED if not NEW @@ -6319,22 +6336,32 @@ void Player::CheckAreaExploreAndOutdoor() return; bool isOutdoor; - uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); + uint32 areaId = GetBaseMap()->GetAreaId(GetPositionX(), GetPositionY(), GetPositionZ(), &isOutdoor); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); if (sWorld->getBoolConfig(CONFIG_VMAP_INDOOR_CHECK) && !isOutdoor) RemoveAurasWithAttribute(SPELL_ATTR0_OUTDOORS_ONLY); - if (areaFlag == 0xffff) + if (!areaId) return; - int offset = areaFlag / 32; - if (offset >= PLAYER_EXPLORED_ZONES_SIZE) + if (!areaEntry) { - TC_LOG_ERROR("entities.player", "Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", areaFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE); + TC_LOG_ERROR("entities.player", "Player '%s' (%s) discovered unknown area (x: %f y: %f z: %f map: %u)", + GetName().c_str(), GetGUID().ToString().c_str(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId()); return; } - uint32 val = (uint32)(1 << (areaFlag % 32)); + uint32 offset = areaEntry->exploreFlag / 32; + + if (offset >= PLAYER_EXPLORED_ZONES_SIZE) + { + TC_LOG_ERROR("entities.player", "Player::CheckAreaExploreAndOutdoor: Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < %u ).", + areaEntry->exploreFlag, GetPositionX(), GetPositionY(), offset, offset, PLAYER_EXPLORED_ZONES_SIZE); + return; + } + + uint32 val = (uint32)(1 << (areaEntry->exploreFlag % 32)); uint32 currFields = GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); if (!(currFields & val)) @@ -6343,19 +6370,11 @@ void Player::CheckAreaExploreAndOutdoor() UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaFlagAndMap(areaFlag, GetMapId()); - if (!areaEntry) - { - TC_LOG_ERROR("entities.player", "Player %u discovered unknown area (x: %f y: %f z: %f map: %u", GetGUID().GetCounter(), GetPositionX(), GetPositionY(), GetPositionZ(), GetMapId()); - return; - } - if (areaEntry->area_level > 0) { - uint32 area = areaEntry->ID; if (getLevel() >= sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)) { - SendExplorationExperience(area, 0); + SendExplorationExperience(areaId, 0); } else { @@ -6379,9 +6398,9 @@ void Player::CheckAreaExploreAndOutdoor() } GiveXP(XP, NULL); - SendExplorationExperience(area, XP); + SendExplorationExperience(areaId, XP); } - TC_LOG_DEBUG("entities.player", "Player %u discovered a new area: %u", GetGUID().GetCounter(), area); + TC_LOG_DEBUG("entities.player", "Player '%s' (%s) discovered a new area: %u", GetName().c_str(),GetGUID().ToString().c_str(), areaId); } } } @@ -7300,7 +7319,7 @@ void Player::UpdateArea(uint32 newArea) // so apply them accordingly m_areaUpdateId = newArea; - AreaTableEntry const* area = GetAreaEntryByAreaID(newArea); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(newArea); pvpInfo.IsInFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA); UpdatePvPState(true); @@ -7348,7 +7367,7 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea) // zone changed, so area changed as well, update it UpdateArea(newArea); - AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(newZone); if (!zone) return; @@ -8146,7 +8165,7 @@ void Player::CastItemCombatSpell(Unit* target, WeaponAttackType attType, uint32 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellData.SpellId); if (!spellInfo) { - TC_LOG_ERROR("entities.player.items", "WORLD: unknown Item spellid %i", spellData.SpellId); + TC_LOG_ERROR("entities.player.items", "WORLD: Unknown item spellid %i", spellData.SpellId); continue; } @@ -8249,7 +8268,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(learn_spell_id); if (!spellInfo) { - TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring ", proto->ItemId, learn_spell_id); + TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring.", proto->ItemId, learn_spell_id); SendEquipError(EQUIP_ERR_INTERNAL_BAG_ERROR, item, NULL); return; } @@ -8278,7 +8297,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellData.SpellId); if (!spellInfo) { - TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring", proto->ItemId, spellData.SpellId); + TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell: Item (Entry: %u) has wrong spell id %u, ignoring.", proto->ItemId, spellData.SpellId); continue; } @@ -8308,7 +8327,7 @@ void Player::CastItemUseSpell(Item* item, SpellCastTargets const& targets, uint8 SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(pEnchant->spellid[s]); if (!spellInfo) { - TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]); + TC_LOG_ERROR("entities.player", "Player::CastItemUseSpell Enchant %i casts unknown spell %i.", pEnchant->ID, pEnchant->spellid[s]); continue; } @@ -9493,7 +9512,7 @@ uint32 Player::GetXPRestBonus(uint32 xp) SetRestBonus(GetRestBonus() - rested_bonus); - TC_LOG_DEBUG("entities.player", "GetXPRestBonus: Player %s (%u) gain %u xp (+%u Rested Bonus). Rested points=%f", GetName().c_str(), GetGUID().GetCounter(), xp+rested_bonus, rested_bonus, GetRestBonus()); + TC_LOG_DEBUG("entities.player", "GetXPRestBonus: Player %s (%u) gains %u xp (+%u Rested Bonus). Rested points=%f", GetName().c_str(), GetGUID().GetCounter(), xp+rested_bonus, rested_bonus, GetRestBonus()); return rested_bonus; } @@ -9525,7 +9544,7 @@ void Player::ResetPetTalents() CharmInfo* charmInfo = pet->GetCharmInfo(); if (!charmInfo) { - TC_LOG_ERROR("entities.player", "Object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUID().GetCounter(), pet->GetTypeId()); + TC_LOG_ERROR("entities.player", "Object (GUID: %u TypeId: %u) is considered pet-like, but doesn't have charm info!", pet->GetGUID().GetCounter(), pet->GetTypeId()); return; } pet->resetTalents(); @@ -10372,7 +10391,7 @@ InventoryResult Player::CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, Ite if (bag == skip_bag) return EQUIP_ERR_WRONG_BAG_TYPE; - // skip not existed bag or self targeted bag + // skip non-existing bag or self targeted bag Bag* pBag = GetBagByPos(bag); if (!pBag || pBag == pSrcItem) return EQUIP_ERR_WRONG_BAG_TYPE; @@ -11141,7 +11160,7 @@ InventoryResult Player::CanUnequipItem(uint16 pos, bool swap) const Item* pItem = GetItemByPos(pos); - // Applied only to existed equipped item + // Applied only to existing equipped item if (!pItem) return EQUIP_ERR_OK; @@ -11751,7 +11770,7 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(cooldownSpell); if (!spellProto) - TC_LOG_ERROR("entities.player", "Weapon switch cooldown spell %u couldn't be found in Spell.dbc", cooldownSpell); + TC_LOG_ERROR("entities.player", "Weapon switch cooldown spell %u could not be found in Spell.dbc", cooldownSpell); else { m_weaponChangeTimer = spellProto->StartRecoveryTime; @@ -11999,7 +12018,7 @@ void Player::MoveItemToInventory(ItemPosCountVec const& dest, Item* pItem, bool // store item Item* pLastItem = StoreItem(dest, pItem, update); - // only set if not merged to existed stack (pItem can be deleted already but we can compare pointers any way) + // only set if not merged to existing stack (pItem can be deleted already but we can compare pointers any way) if (pLastItem == pItem) { // update owner for last item (this can be original item with wrong owner @@ -12407,7 +12426,7 @@ void Player::SplitItem(uint16 src, uint16 dst, uint32 count) return; } - // not let split more existed items (can be only at cheating) + // not let split more existing items (can be only at cheating) if (pSrcItem->GetCount() < count) { SendEquipError(EQUIP_ERR_TOO_FEW_TO_SPLIT, pSrcItem, NULL); @@ -13957,7 +13976,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool VendorItemData const* vendorItems = creature->GetVendorItems(); if (!vendorItems || vendorItems->Empty()) { - TC_LOG_ERROR("sql.sql", "Creature %s (Entry: %u GUID: %u DB GUID: %u) has UNIT_NPC_FLAG_VENDOR set but has an empty trading item list.", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId()); + TC_LOG_ERROR("sql.sql", "Creature %s (Entry: %u GUID: %u DB GUID: %u) has UNIT_NPC_FLAG_VENDOR set, but has an empty trading item list.", creature->GetName().c_str(), creature->GetEntry(), creature->GetGUID().GetCounter(), creature->GetSpawnId()); canTalk = false; } break; @@ -13991,7 +14010,7 @@ 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) request wrong gossip menu: %u with wrong class: %u at Creature: %s (Entry: %u, Trainer Class: %u)", + 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); // no break; case GOSSIP_OPTION_GOSSIP: @@ -14007,7 +14026,7 @@ void Player::PrepareGossipMenu(WorldObject* source, uint32 menuId /*= 0*/, bool canTalk = false; break; default: - TC_LOG_ERROR("sql.sql", "Creature entry %u has unknown gossip option %u for menu %u", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId); + TC_LOG_ERROR("sql.sql", "Creature entry %u has unknown gossip option %u for menu %u.", creature->GetEntry(), itr->second.OptionType, itr->second.MenuId); canTalk = false; break; } @@ -14120,7 +14139,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men { if (gossipOptionId > GOSSIP_OPTION_QUESTGIVER) { - TC_LOG_ERROR("entities.player", "Player guid %u request invalid gossip option for GameObject entry %u", GetGUID().GetCounter(), source->GetEntry()); + TC_LOG_ERROR("entities.player", "Player guid %u requested invalid gossip option for GameObject entry %u.", GetGUID().GetCounter(), source->GetEntry()); return; } } @@ -14224,7 +14243,7 @@ void Player::OnGossipSelect(WorldObject* source, uint32 gossipListId, uint32 men if (bgTypeId == BATTLEGROUND_TYPE_NONE) { - TC_LOG_ERROR("entities.player", "a user (guid %u) requested battlegroundlist from a npc who is no battlemaster", GetGUID().GetCounter()); + TC_LOG_ERROR("entities.player", "User (guid %u) requested battlegroundlist from an NPC who is not a battlemaster.", GetGUID().GetCounter()); return; } @@ -15214,7 +15233,7 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (1).", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required quest(s) (1).", qInfo->GetQuestId()); } return false; } @@ -15247,7 +15266,7 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (2).", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required quest(s) (2).", qInfo->GetQuestId()); } return false; @@ -15263,7 +15282,7 @@ bool Player::SatisfyQuestPreviousQuest(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required quest (3).", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestPreviousQuest: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required quest(s) (3).", qInfo->GetQuestId()); } return false; @@ -15281,7 +15300,7 @@ bool Player::SatisfyQuestClass(Quest const* qInfo, bool msg) const if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestClass: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required class.", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestClass: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player is not (one of) the required class(es).", qInfo->GetQuestId()); } return false; @@ -15300,7 +15319,7 @@ bool Player::SatisfyQuestRace(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_QUEST_FAILED_WRONG_RACE); - TC_LOG_DEBUG("misc", "SatisfyQuestRace: Sent INVALIDREASON_QUEST_FAILED_WRONG_RACE (questId: %u) because player does not have required race.", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestRace: Sent INVALIDREASON_QUEST_FAILED_WRONG_RACE (questId: %u) because player is not (one of) the required race(s).", qInfo->GetQuestId()); } return false; @@ -15316,7 +15335,7 @@ bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (min).", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required reputation (min).", qInfo->GetQuestId()); } return false; } @@ -15327,7 +15346,7 @@ bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (max).", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required reputation (max).", qInfo->GetQuestId()); } return false; } @@ -15340,7 +15359,7 @@ bool Player::SatisfyQuestReputation(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have required reputation (ReputationObjective2).", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestReputation: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not have the required reputation (ReputationObjective2).", qInfo->GetQuestId()); } return false; } @@ -15369,7 +15388,7 @@ bool Player::SatisfyQuestConditions(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestConditions: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not meet conditions.", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestConditions: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player does not meet the conditions.", qInfo->GetQuestId()); } TC_LOG_DEBUG("condition", "Player::SatisfyQuestConditions: conditions not met for quest %u", qInfo->GetQuestId()); return false; @@ -15415,7 +15434,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did daily quests in exclusive group.", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did all daily quests in exclusive group.", qInfo->GetQuestId()); } return false; @@ -15427,7 +15446,7 @@ bool Player::SatisfyQuestExclusiveGroup(Quest const* qInfo, bool msg) if (msg) { SendCanTakeQuestResponse(INVALIDREASON_DONT_HAVE_REQ); - TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player already did quest in exclusive group.", qInfo->GetQuestId()); + TC_LOG_DEBUG("misc", "SatisfyQuestExclusiveGroup: Sent INVALIDREASON_DONT_HAVE_REQ (questId: %u) because player has already taken one or more quests in the exclusive group.", qInfo->GetQuestId()); } return false; } @@ -15496,7 +15515,7 @@ bool Player::SatisfyQuestDay(Quest const* qInfo, bool msg) if (qInfo->IsDFQuest()) { - if (!m_DFQuests.empty()) + if (m_DFQuests.find(qInfo->GetQuestId()) != m_DFQuests.end()) return false; return true; @@ -16818,7 +16837,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) { std::string name = ""; sObjectMgr->GetPlayerNameByGUID(guid, name); - TC_LOG_ERROR("entities.player", "Player %s %s not found in table `characters`, can't load. ", name.c_str(), guid.ToString().c_str()); + TC_LOG_ERROR("entities.player", "Player %s %s not found in table `characters`, can't load.", name.c_str(), guid.ToString().c_str()); return false; } @@ -16830,7 +16849,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) // player should be able to load/delete character only with correct account! if (dbAccountId != GetSession()->GetAccountId()) { - TC_LOG_ERROR("entities.player", "Player %s loading from wrong account (is: %u, should be: %u)", guid.ToString().c_str(), GetSession()->GetAccountId(), dbAccountId); + TC_LOG_ERROR("entities.player", "Player %s attempts to load from wrong account (current: %u, should be: %u)", guid.ToString().c_str(), GetSession()->GetAccountId(), dbAccountId); return false; } @@ -16862,7 +16881,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) uint8 gender = fields[5].GetUInt8(); if (!IsValidGender(gender)) { - TC_LOG_ERROR("entities.player", "Player %s has wrong gender (%u), can't be loaded.", guid.ToString().c_str(), gender); + TC_LOG_ERROR("entities.player", "Player %s is the wrong gender (%u) and can't be loaded.", guid.ToString().c_str(), gender); return false; } @@ -16877,15 +16896,15 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass()); if (!info) { - TC_LOG_ERROR("entities.player", "Player %s has wrong race/class (%u/%u), can't be loaded.", guid.ToString().c_str(), getRace(), getClass()); + TC_LOG_ERROR("entities.player", "Player %s has an invalid race/class combination (%u/%u) and can't be loaded.", guid.ToString().c_str(), getRace(), getClass()); return false; } SetUInt32Value(UNIT_FIELD_LEVEL, fields[6].GetUInt8()); SetUInt32Value(PLAYER_XP, fields[7].GetUInt32()); - _LoadIntoDataField(fields[55].GetCString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); - _LoadIntoDataField(fields[57].GetCString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2); + _LoadIntoDataField(fields[55].GetString(), PLAYER_EXPLORED_ZONES_1, PLAYER_EXPLORED_ZONES_SIZE); + _LoadIntoDataField(fields[57].GetString(), PLAYER__FIELD_KNOWN_TITLES, KNOWN_TITLES_SIZE*2); SetObjectScale(1.0f); SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); @@ -16912,7 +16931,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) GetByteValue(PLAYER_BYTES_2, 0), // facial hair GetByteValue(PLAYER_BYTES, 0))) // skin color { - TC_LOG_ERROR("entities.player", "Player %s has wrong Appearance values (Hair/Skin/Color), can't be loaded.", guid.ToString().c_str()); + TC_LOG_ERROR("entities.player", "Player %s has wrong Appearance values (Hair/Skin/Color) and can't be loaded.", guid.ToString().c_str()); return false; } @@ -16924,7 +16943,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) InitDisplayIds(); - // cleanup inventory related item value fields (its will be filled correctly in _LoadInventory) + // cleanup inventory related item value fields (it will be filled correctly in _LoadInventory) for (uint8 slot = EQUIPMENT_SLOT_START; slot < EQUIPMENT_SLOT_END; ++slot) { SetGuidValue(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), ObjectGuid::Empty); @@ -16934,7 +16953,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) m_items[slot] = NULL; } - TC_LOG_DEBUG("entities.player.loading", "Load Basic value of player %s is: ", m_name.c_str()); + TC_LOG_DEBUG("entities.player.loading", "Load Basic values of player %s: ", m_name.c_str()); outDebugValues(); //Need to call it to initialize m_team (m_team can be calculated from race) @@ -17001,7 +17020,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); if (!mapEntry || !IsPositionValid()) { - TC_LOG_ERROR("entities.player", "Player %s have invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleport to default race/class locations.", + TC_LOG_ERROR("entities.player", "Player %s has invalid coordinates (MapId: %u X: %f Y: %f Z: %f O: %f). Teleporting player to default race/class location.", guid.ToString().c_str(), mapId, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); RelocateToHomebind(); } @@ -17042,7 +17061,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) //if (mapId == MAPID_INVALID) -- code kept for reference if (int16(mapId) == int16(-1)) // Battleground Entry Point not found (???) { - TC_LOG_ERROR("entities.player", "Player %s was in BG in database, but BG was not found, and entry point was invalid! Teleport to default race/class locations.", + TC_LOG_ERROR("entities.player", "Player %s was in BG in database, but BG was not found and entry point was invalid! Teleporting to default race/class locations.", guid.ToString().c_str()); RelocateToHomebind(); } @@ -17074,7 +17093,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) std::fabs(m_movementInfo.transport.pos.GetPositionY()) > 250.0f || std::fabs(m_movementInfo.transport.pos.GetPositionZ()) > 250.0f) { - TC_LOG_ERROR("entities.player", "Player %s have invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleport to bind location.", + TC_LOG_ERROR("entities.player", "Player %s has invalid transport coordinates (X: %f Y: %f Z: %f O: %f). Teleporting player to bind location.", guid.ToString().c_str(), x, y, z, o); m_movementInfo.transport.Reset(); @@ -17091,7 +17110,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) } else { - TC_LOG_ERROR("entities.player", "Player (%s) have problems with transport guid (%u). Teleport to bind location.", + TC_LOG_ERROR("entities.player", "Player %s has problems with transport guid (%u). Teleporting to bind location.", guid.ToString().c_str(), transLowGUID); RelocateToHomebind(); @@ -17115,14 +17134,14 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) if (uint32 node_id = m_taxi.GetTaxiSource()) nodeEntry = sTaxiNodesStore.LookupEntry(node_id); - if (!nodeEntry) // don't know taxi start node, to homebind + if (!nodeEntry) // don't know taxi start node, teleport to homebind { - TC_LOG_ERROR("entities.player", "Character %u have wrong data in taxi destination list, teleport to homebind.", GetGUID().GetCounter()); + TC_LOG_ERROR("entities.player", "Character %u has wrong data in taxi destination list. Teleporting player to homebind.", GetGUID().GetCounter()); RelocateToHomebind(); } - else // have start node, to it + else // has start node, teleport to it { - TC_LOG_ERROR("entities.player", "Character %u have too short taxi destination list, teleport to original node.", GetGUID().GetCounter()); + TC_LOG_ERROR("entities.player", "Character %u has too short taxi destination list. Teleporting player to original node.", GetGUID().GetCounter()); mapId = nodeEntry->map_id; Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z, 0.0f); } @@ -17151,7 +17170,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) { if (GetSession()->Expansion() < mapEntry->Expansion()) { - TC_LOG_DEBUG("entities.player.loading", "Player %s using client without required expansion tried login at non accessible map %u", GetName().c_str(), mapId); + TC_LOG_DEBUG("entities.player.loading", "Player %s is using client without required expansion, tried to log in to inaccessible map %u.", GetName().c_str(), mapId); RelocateToHomebind(); } @@ -17281,7 +17300,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) m_stableSlots = fields[33].GetUInt8(); if (m_stableSlots > MAX_PET_STABLES) { - TC_LOG_ERROR("entities.player", "Player can have not more %u stable slots, but have in DB %u", MAX_PET_STABLES, uint32(m_stableSlots)); + TC_LOG_ERROR("entities.player", "Player can not have more than %u stable slots, but has %u in DB.", MAX_PET_STABLES, uint32(m_stableSlots)); m_stableSlots = MAX_PET_STABLES; } @@ -17289,7 +17308,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder) if (HasAtLoginFlag(AT_LOGIN_RENAME)) { - TC_LOG_ERROR("entities.player", "Player (GUID: %u) tried to login while forced to rename, can't load.'", GetGUID().GetCounter()); + TC_LOG_ERROR("entities.player", "Player (GUID: %u) tried to login while forced to rename, could not load player.", GetGUID().GetCounter()); return false; } @@ -17651,9 +17670,9 @@ void Player::_LoadActions(PreparedQueryResult result) ab->uState = ACTIONBUTTON_UNCHANGED; else { - TC_LOG_ERROR("entities.player", " ...at loading, and will deleted in DB also"); + TC_LOG_ERROR("entities.player", " ...at loading, and will also be deleted in DB."); - // Will deleted in DB at next save (it can create data until save but marked as deleted) + // Will be deleted in DB at next save (it can create data until save but marked as deleted). m_actionButtons[button].uState = ACTIONBUTTON_DELETED; } } while (result->NextRow()); @@ -17662,7 +17681,7 @@ void Player::_LoadActions(PreparedQueryResult result) void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) { - TC_LOG_DEBUG("entities.player.loading", "Loading auras for player %u", GetGUID().GetCounter()); + TC_LOG_DEBUG("entities.player.loading", "Loading auras for player %u.", GetGUID().GetCounter()); /* 0 1 2 3 4 5 6 7 8 9 10 QueryResult* result = CharacterDatabase.PQuery("SELECT casterGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, base_amount0, base_amount1, base_amount2, @@ -17695,7 +17714,7 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellid); if (!spellInfo) { - TC_LOG_ERROR("entities.player", "Unknown aura (spellid %u), ignore.", spellid); + TC_LOG_ERROR("entities.player", "Unknown aura (spellid %u), ignored.", spellid); continue; } @@ -17729,7 +17748,7 @@ void Player::_LoadAuras(PreparedQueryResult result, uint32 timediff) aura->SetLoadedState(maxduration, remaintime, remaincharges, stackcount, recalculatemask, &damage[0]); aura->ApplyForTargets(); - TC_LOG_DEBUG("entities.player", "Added aura spellid %u, effectmask %u", spellInfo->Id, effmask); + TC_LOG_DEBUG("entities.player", "Added aura spellid %u, effectmask %u.", spellInfo->Id, effmask); } } while (result->NextRow()); @@ -17755,10 +17774,10 @@ void Player::_LoadGlyphAuras() TC_LOG_ERROR("entities.player", "Player %s has glyph with typeflags %u in slot with typeflags %u, removing.", m_name.c_str(), gp->TypeFlags, gs->TypeFlags); } else - TC_LOG_ERROR("entities.player", "Player %s has not existing glyph slot entry %u on index %u", m_name.c_str(), GetGlyphSlot(i), i); + TC_LOG_ERROR("entities.player", "Player %s has non-existing glyph slot entry %u on index %u.", m_name.c_str(), GetGlyphSlot(i), i); } else - TC_LOG_ERROR("entities.player", "Player %s has not existing glyph entry %u on index %u", m_name.c_str(), glyph, i); + TC_LOG_ERROR("entities.player", "Player %s has non-existing glyph entry %u on index %u.", m_name.c_str(), glyph, i); // On any error remove glyph SetGlyph(i, 0); @@ -17888,7 +17907,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) item->SetState(ITEM_UNCHANGED, this); else { - TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) which can't be loaded into inventory (Bag GUID: %u, slot: %u) by reason %u. Item will be sent by mail.", + TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has item (GUID: %u, entry: %u) which can't be loaded into inventory (Bag GUID: %u, slot: %u) for reason %u. Item will be sent by mail.", GetGUID().GetCounter(), GetName().c_str(), item->GetGUID().GetCounter(), item->GetEntry(), bagGuid, slot, err); item->DeleteFromInventoryDB(trans); problematicItems.push_back(item); @@ -18068,7 +18087,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F } else { - TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has broken item (GUID: %u, entry: %u) in inventory. Deleting item.", + TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has a broken item (GUID: %u, entry: %u) in inventory. Deleting item.", GetGUID().GetCounter(), GetName().c_str(), itemGuid, itemEntry); remove = true; } @@ -18083,7 +18102,7 @@ Item* Player::_LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, F } else { - TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has unknown item (entry: %u) in inventory. Deleting item.", + TC_LOG_ERROR("entities.player", "Player::_LoadInventory: player (GUID: %u, name: '%s') has an unknown item (entry: %u) in inventory. Deleting item.", GetGUID().GetCounter(), GetName().c_str(), itemEntry); Item::DeleteFromInventoryDB(trans, itemGuid); Item::DeleteFromDB(trans, itemGuid); @@ -18114,7 +18133,7 @@ void Player::_LoadMailedItems(Mail* mail) if (!proto) { - TC_LOG_ERROR("entities.player", "Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUID().GetCounter(), itemGuid, itemTemplate, mail->messageID); + TC_LOG_ERROR("entities.player", "Player %u has an unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUID().GetCounter(), itemGuid, itemTemplate, mail->messageID); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INVALID_MAIL_ITEM); stmt->setUInt32(0, itemGuid); @@ -18130,7 +18149,7 @@ void Player::_LoadMailedItems(Mail* mail) if (!item->LoadFromDB(itemGuid, ObjectGuid(HighGuid::Player, fields[13].GetUInt32()), fields, itemTemplate)) { - TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, itemGuid); + TC_LOG_ERROR("entities.player", "Player::_LoadMailedItems - Item in mail (%u) doesn't exist!!! - item guid: %u, deleted from mail.", mail->messageID, itemGuid); stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_MAIL_ITEM); stmt->setUInt32(0, itemGuid); @@ -18193,7 +18212,7 @@ void Player::_LoadMail() if (m->mailTemplateId && !sMailTemplateStore.LookupEntry(m->mailTemplateId)) { - TC_LOG_ERROR("entities.player", "Player::_LoadMail - Mail (%u) have not existed MailTemplateId (%u), remove at load", m->messageID, m->mailTemplateId); + TC_LOG_ERROR("entities.player", "Player::_LoadMail - Mail (%u) contains a non-existing MailTemplateId (%u), removing at load.", m->messageID, m->mailTemplateId); m->mailTemplateId = 0; } @@ -18374,7 +18393,7 @@ void Player::_LoadDailyQuestStatus(PreparedQueryResult result) if (quest_daily_idx >= PLAYER_MAX_DAILY_QUESTS) // max amount with exist data in query { - TC_LOG_ERROR("entities.player", "Player (GUID: %u) have more 25 daily quest records in `charcter_queststatus_daily`", GetGUID().GetCounter()); + TC_LOG_ERROR("entities.player", "Player (GUID: %u) has more than 25 daily quest records in `charcter_queststatus_daily`", GetGUID().GetCounter()); break; } @@ -18523,8 +18542,9 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) uint32 mapId = fields[2].GetUInt16(); uint32 instanceId = fields[0].GetUInt32(); uint8 difficulty = fields[3].GetUInt8(); + BindExtensionState extendState = BindExtensionState(fields[4].GetUInt8()); - time_t resetTime = time_t(fields[4].GetUInt32()); + time_t resetTime = time_t(fields[5].GetUInt32()); // the resettime for normal instances is only saved when the InstanceSave is unloaded // so the value read from the DB may be wrong here but only if the InstanceSave is loaded // and in that case it is not used @@ -18536,12 +18556,12 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) if (!mapEntry || !mapEntry->IsDungeon()) { - TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed or not dungeon map %d (%s)", GetName().c_str(), GetGUID().GetCounter(), mapId, mapname.c_str()); + TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to a non-existing or non-dungeon map %d (%s).", GetName().c_str(), GetGUID().GetCounter(), mapId, mapname.c_str()); deleteInstance = true; } else if (difficulty >= MAX_DIFFICULTY) { - TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str()); + TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has a bind to a non-existing difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str()); deleteInstance = true; } else @@ -18549,12 +18569,12 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) MapDifficulty const* mapDiff = GetMapDifficultyData(mapId, Difficulty(difficulty)); if (!mapDiff) { - TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has bind to not existed difficulty %d instance for map %u (%s)", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str()); + TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) has a bind to a non-existing difficulty %d instance for map %u (%s).", GetName().c_str(), GetGUID().GetCounter(), difficulty, mapId, mapname.c_str()); deleteInstance = true; } else if (!perm && group) { - TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) is in group %d but has a non-permanent character bind to map %d (%s), %d, %d", GetName().c_str(), GetGUID().GetCounter(), group->GetLowGUID(), mapId, mapname.c_str(), instanceId, difficulty); + TC_LOG_ERROR("entities.player", "_LoadBoundInstances: player %s(%d) is in group %d, but has a non-permanent character bind to map %d (%s), %d, %d.", GetName().c_str(), GetGUID().GetCounter(), group->GetLowGUID(), mapId, mapname.c_str(), instanceId, difficulty); deleteInstance = true; } } @@ -18573,13 +18593,13 @@ void Player::_LoadBoundInstances(PreparedQueryResult result) // since non permanent binds are always solo bind, they can always be reset if (InstanceSave* save = sInstanceSaveMgr->AddInstanceSave(mapId, instanceId, Difficulty(difficulty), resetTime, !perm, true)) - BindToInstance(save, perm, true); + BindToInstance(save, perm, extendState, true); } while (result->NextRow()); } } -InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty) +InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty, bool withExpired) { // some instances only have one difficulty MapDifficulty const* mapDiff = GetDownscaledMapDifficultyData(mapid, difficulty); @@ -18588,9 +18608,9 @@ InstancePlayerBind* Player::GetBoundInstance(uint32 mapid, Difficulty difficulty BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid); if (itr != m_boundInstances[difficulty].end()) - return &itr->second; - else - return NULL; + if (itr->second.extendState || withExpired) + return &itr->second; + return nullptr; } InstanceSave* Player::GetInstanceSave(uint32 mapid, bool raid) @@ -18633,24 +18653,32 @@ void Player::UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficu } } -InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, bool load) +InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, BindExtensionState extendState, bool load) { if (save) { InstancePlayerBind& bind = m_boundInstances[save->GetDifficulty()][save->GetMapId()]; + if (extendState == EXTEND_STATE_KEEP) // special flag, keep the player's current extend state when updating for new boss down + { + if (save == bind.save) + extendState = bind.extendState; + else + extendState = EXTEND_STATE_NORMAL; + } if (!load) { if (bind.save) { // update the save when the group kills a boss - if (permanent != bind.perm || save != bind.save) + if (permanent != bind.perm || save != bind.save || extendState != bind.extendState) { PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_CHAR_INSTANCE); stmt->setUInt32(0, save->GetInstanceId()); stmt->setBool(1, permanent); - stmt->setUInt32(2, GetGUID().GetCounter()); - stmt->setUInt32(3, bind.save->GetInstanceId()); + stmt->setUInt8(2, extendState); + stmt->setUInt32(3, GetGUID().GetCounter()); + stmt->setUInt32(4, bind.save->GetInstanceId()); CharacterDatabase.Execute(stmt); } @@ -18662,6 +18690,7 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, b stmt->setUInt32(0, GetGUID().GetCounter()); stmt->setUInt32(1, save->GetInstanceId()); stmt->setBool(2, permanent); + stmt->setUInt8(3, extendState); CharacterDatabase.Execute(stmt); } @@ -18679,9 +18708,10 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave* save, bool permanent, b bind.save = save; bind.perm = permanent; + bind.extendState = extendState; if (!load) TC_LOG_DEBUG("maps", "Player::BindToInstance: %s(%d) is now bound to map %d, instance %d, difficulty %d", GetName().c_str(), GetGUID().GetCounter(), save->GetMapId(), save->GetInstanceId(), save->GetDifficulty()); - sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficulty(), save->GetMapId(), permanent); + sScriptMgr->OnPlayerBindToInstance(this, save->GetDifficulty(), save->GetMapId(), permanent, uint8(extendState)); return &bind; } @@ -18699,7 +18729,7 @@ void Player::BindToInstance() GetSession()->SendPacket(&data); if (!IsGameMaster()) { - BindToInstance(mapSave, true); + BindToInstance(mapSave, true, EXTEND_STATE_KEEP); GetSession()->SendCalendarRaidLockout(mapSave, true); } } @@ -18725,23 +18755,27 @@ void Player::SendRaidInfo() { for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr) { - if (itr->second.perm) + InstancePlayerBind const& bind = itr->second; + if (bind.perm) { - InstanceSave* save = itr->second.save; + InstanceSave* save = bind.save; bool isHeroic = save->GetDifficulty() == RAID_DIFFICULTY_10MAN_HEROIC || save->GetDifficulty() == RAID_DIFFICULTY_25MAN_HEROIC; uint32 completedEncounters = 0; if (Map* map = sMapMgr->FindMap(save->GetMapId(), save->GetInstanceId())) if (InstanceScript* instanceScript = ((InstanceMap*)map)->GetInstanceScript()) completedEncounters = instanceScript->GetCompletedEncounterMask(); - data << uint32(save->GetMapId()); // map id - data << uint32(save->GetDifficulty()); // difficulty + data << uint32(save->GetMapId()); // map id + data << uint32(save->GetDifficulty()); // difficulty data << uint32(isHeroic); // heroic - data << uint64(save->GetInstanceId()); // instance id - data << uint8(1); // expired = 0 - data << uint8(0); // extended = 1 - data << uint32(save->GetResetTime() - now); // reset time - data << uint32(completedEncounters); // completed encounters mask + data << uint64(save->GetInstanceId()); // instance id + data << uint8(bind.extendState != EXTEND_STATE_EXPIRED); // expired = 0 + data << uint8(bind.extendState == EXTEND_STATE_EXTENDED); // extended = 1 + time_t nextReset = save->GetResetTime(); + if (bind.extendState == EXTEND_STATE_EXTENDED) + nextReset = sInstanceSaveMgr->GetSubsequentResetTime(save->GetMapId(), save->GetDifficulty(), save->GetResetTime()); + data << uint32(nextReset - now); // reset time + data << uint32(completedEncounters); // completed encounters mask ++counter; } } @@ -19499,7 +19533,7 @@ void Player::_SaveInventory(SQLTransaction& trans) } else { - TC_LOG_ERROR("entities.player", "Can't find %s but is in refundable storage for player %u ! Removing.", itr->ToString().c_str(), GetGUID().GetCounter()); + TC_LOG_ERROR("entities.player", "Can't find %s, but is in refundable storage for player %u ! Removing.", itr->ToString().c_str(), GetGUID().GetCounter()); m_refundableItems.erase(itr); } } @@ -19548,7 +19582,7 @@ void Player::_SaveInventory(SQLTransaction& trans) { TC_LOG_ERROR("entities.player", "Player(GUID: %u Name: %s)::_SaveInventory - the bag(%u) and slot(%u) values for the item with guid %u are incorrect, the item with guid %u is there instead!", lowGuid, GetName().c_str(), item->GetBagSlot(), item->GetSlot(), item->GetGUID().GetCounter(), test->GetGUID().GetCounter()); // save all changes to the item... - if (item->GetState() != ITEM_NEW) // only for existing items, no dupes + if (item->GetState() != ITEM_NEW) // only for existing items, no duplicates item->SaveToDB(trans); // ...but do not save position in inventory continue; @@ -20376,8 +20410,8 @@ Pet* Player::GetPet() const if (IsInWorld() && pet) return pet; - //there may be a guardian in slot - //TC_LOG_ERROR("entities.player", "Player::GetPet: Pet %u not exist.", GUID_LOPART(pet_guid)); + // there may be a guardian in this slot + //TC_LOG_ERROR("entities.player", "Player::GetPet: Pet %u does not exist.", GUID_LOPART(pet_guid)); //const_cast(this)->SetPetGUID(0); } @@ -20479,7 +20513,7 @@ void Player::StopCastingCharm() if (GetCharmGUID()) { - TC_LOG_FATAL("entities.player", "Player %s (%s) is not able to uncharm unit (%s)", GetName().c_str(), GetGUID().ToString().c_str(), GetCharmGUID().ToString().c_str()); + TC_LOG_FATAL("entities.player", "Player %s (%s) is not able to uncharm unit (%s)!", GetName().c_str(), GetGUID().ToString().c_str(), GetCharmGUID().ToString().c_str()); if (charm->GetCharmerGUID()) { TC_LOG_FATAL("entities.player", "Charmed unit has charmer %s", charm->GetCharmerGUID().ToString().c_str()); @@ -21771,7 +21805,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost); if (!iece) { - TC_LOG_ERROR("entities.player", "Item %u have wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost); + TC_LOG_ERROR("entities.player", "Item %u has wrong ExtendedCost field value %u", pProto->ItemId, crItem->ExtendedCost); return false; } @@ -21847,7 +21881,7 @@ bool Player::BuyItemFromVendorSlot(ObjectGuid vendorguid, uint32 vendorslot, uin uint32 maxCount = MAX_MONEY_AMOUNT / pProto->BuyPrice; if ((uint32)count > maxCount) { - TC_LOG_ERROR("entities.player", "Player %s tried to buy %u item id %u, causing overflow", GetName().c_str(), (uint32)count, pProto->ItemId); + TC_LOG_ERROR("entities.player", "Player %s tried to buy %u item id %u, causing overflow.", GetName().c_str(), (uint32)count, pProto->ItemId); count = (uint8)maxCount; } price = pProto->BuyPrice * count; //it should not exceed MAX_MONEY_AMOUNT @@ -21952,7 +21986,7 @@ void Player::UpdateHomebindTime(uint32 time) data << uint32(m_HomebindTimer); data << uint32(1); GetSession()->SendPacket(&data); - TC_LOG_DEBUG("maps", "PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName().c_str(), GetGUID().GetCounter()); + TC_LOG_DEBUG("maps", "PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds.", GetName().c_str(), GetGUID().GetCounter()); } } @@ -22022,7 +22056,7 @@ void Player::UpdatePotionCooldown(Spell* spell) // Call not from spell cast, send cooldown event for item spells if no in combat if (!spell) { - // spell/item pair let set proper cooldown (except not existed charged spell cooldown spellmods for potions) + // spell/item pair let set proper cooldown (except non-existing charged spell cooldown spellmods for potions) if (ItemTemplate const* proto = sObjectMgr->GetItemTemplate(m_lastPotionId)) for (uint8 idx = 0; idx < MAX_ITEM_PROTO_SPELLS; ++idx) if (proto->Spells[idx].SpellId && proto->Spells[idx].SpellTrigger == ITEM_SPELLTRIGGER_ON_USE) @@ -23714,7 +23748,7 @@ bool Player::HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item cons break; } default: - TC_LOG_ERROR("entities.player", "HasItemFitToSpellRequirements: Not handled spell requirement for item class %u", spellInfo->EquippedItemClass); + TC_LOG_ERROR("entities.player", "HasItemFitToSpellRequirements: Spell requirement not handled for item class %u", spellInfo->EquippedItemClass); break; } @@ -24380,7 +24414,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply) { if (apply) { - TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName().c_str(), target->GetEntry(), target->GetTypeId()); + TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s created seer %u (TypeId: %u).", GetName().c_str(), target->GetEntry(), target->GetTypeId()); if (!AddGuidValue(PLAYER_FARSIGHT, target->GetGUID())) { @@ -24396,7 +24430,7 @@ void Player::SetViewpoint(WorldObject* target, bool apply) } else { - TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s remove seer", GetName().c_str()); + TC_LOG_DEBUG("maps", "Player::CreateViewpoint: Player %s removed seer", GetName().c_str()); if (!RemoveGuidValue(PLAYER_FARSIGHT, target->GetGUID())) { @@ -24975,7 +25009,7 @@ void Player::_LoadSkills(PreparedQueryResult result) if (value == 0) { - TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Will be deleted.", GetGUID().GetCounter(), skill); + TC_LOG_ERROR("entities.player", "Character %u has skill %u with value 0. Skill will be deleted.", GetGUID().GetCounter(), skill); PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHARACTER_SKILL); @@ -25326,7 +25360,7 @@ bool Player::LearnTalent(uint32 talentId, uint32 talentRank) uint32 spellid = talentInfo->RankID[talentRank]; if (spellid == 0) { - TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank); + TC_LOG_ERROR("entities.player", "Talent.dbc contains talent: %u Rank: %u spell id = 0", talentId, talentRank); return false; } @@ -25480,7 +25514,7 @@ void Player::LearnPetTalent(ObjectGuid petGuid, uint32 talentId, uint32 talentRa uint32 spellid = talentInfo->RankID[talentRank]; if (spellid == 0) { - TC_LOG_ERROR("entities.player", "Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank); + TC_LOG_ERROR("entities.player", "Talent.dbc contains talent: %u Rank: %u spell id = 0", talentId, talentRank); return; } @@ -25808,7 +25842,7 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset) if (!found) // something wrong... { - TC_LOG_ERROR("entities.player", "Player %s tried to save equipment set " UI64FMTD " (index %u), but that equipment set not found!", GetName().c_str(), eqset.Guid, index); + TC_LOG_ERROR("entities.player", "Player %s tried to save equipment set " UI64FMTD " (index %u), but that equipment set was not found!", GetName().c_str(), eqset.Guid, index); return; } } @@ -26146,7 +26180,7 @@ void Player::ActivateSpec(uint8 spec) for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) { - // skip non-existant talent ranks + // skip non-existing talent ranks if (talentInfo->RankID[rank] == 0) continue; RemoveSpell(talentInfo->RankID[rank], true); // removes the talent, and all dependant, learned, and chained spells.. @@ -26205,7 +26239,7 @@ void Player::ActivateSpec(uint8 spec) // learn highest talent rank that exists in newly activated spec for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank) { - // skip non-existant talent ranks + // skip non-existing talent ranks if (talentInfo->RankID[rank] == 0) continue; // if the talent can be found in the newly activated PlayerTalentMap @@ -26823,11 +26857,11 @@ std::string Player::GetMapAreaAndZoneString() uint32 areaId = GetAreaId(); std::string areaName = "Unknown"; std::string zoneName = "Unknown"; - if (AreaTableEntry const* area = GetAreaEntryByAreaID(areaId)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) { int locale = GetSession()->GetSessionDbcLocale(); areaName = area->area_name[locale]; - if (AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone)) + if (AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone)) zoneName = zone->area_name[locale]; } @@ -26884,7 +26918,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy pet->Relocate(x, y, z, ang); if (!pet->IsPositionValid()) { - TC_LOG_ERROR("misc", "Pet (guidlow %d, entry %d) not summoned. Suggested coordinates isn't valid (X: %f Y: %f)", pet->GetGUID().GetCounter(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY()); + TC_LOG_ERROR("misc", "Pet (guidlow %d, entry %d) not summoned. Suggested coordinates are not valid (X: %f Y: %f)", pet->GetGUID().GetCounter(), pet->GetEntry(), pet->GetPositionX(), pet->GetPositionY()); delete pet; return NULL; } @@ -26893,7 +26927,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy uint32 pet_number = sObjectMgr->GeneratePetNumber(); if (!pet->Create(map->GenerateLowGuid(), map, entry, pet_number)) { - TC_LOG_ERROR("misc", "no such creature entry %u", entry); + TC_LOG_ERROR("misc", "No such creature entry %u", entry); delete pet; return NULL; } diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 6d6f1408a73..12c3c033fe8 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -909,14 +909,27 @@ enum PlayerDelayedOperations // Maximum money amount : 2^31 - 1 extern uint64 const MAX_MONEY_AMOUNT; +enum BindExtensionState +{ + EXTEND_STATE_EXPIRED = 0, + EXTEND_STATE_NORMAL = 1, + EXTEND_STATE_EXTENDED = 2, + EXTEND_STATE_KEEP = 255 // special state: keep current save type +}; struct InstancePlayerBind { InstanceSave* save; - bool perm; /* permanent PlayerInstanceBinds are created in Raid/Heroic instances for players - that aren't already permanently bound when they are inside when a boss is killed - or when they enter an instance that the group leader is permanently bound to. */ - InstancePlayerBind() : save(NULL), perm(false) { } + that aren't already permanently bound when they are inside when a boss is killed + or when they enter an instance that the group leader is permanently bound to. */ + bool perm; + /* extend state listing: + EXPIRED - doesn't affect anything unless manually re-extended by player + NORMAL - standard state + EXTENDED - won't be promoted to EXPIRED at next reset period, will instead be promoted to NORMAL */ + BindExtensionState extendState; + + InstancePlayerBind() : save(NULL), perm(false), extendState(EXTEND_STATE_NORMAL) { } }; struct AccessRequirement @@ -1198,8 +1211,9 @@ class Player : public Unit, public GridObject void SendInstanceResetWarning(uint32 mapid, Difficulty difficulty, uint32 time, bool welcome); bool CanInteractWithQuestGiver(Object* questGiver); - Creature* GetNPCIfCanInteractWith(ObjectGuid guid, uint32 npcflagmask); - GameObject* GetGameObjectIfCanInteractWith(ObjectGuid guid, GameobjectTypes type) const; + Creature* GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflagmask); + GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid) const; + GameObject* GetGameObjectIfCanInteractWith(ObjectGuid const& guid, GameobjectTypes type) const; void ToggleAFK(); void ToggleDND(); @@ -2316,12 +2330,12 @@ class Player : public Unit, public GridObject bool m_InstanceValid; // permanent binds and solo binds by difficulty BoundInstancesMap m_boundInstances[MAX_DIFFICULTY]; - InstancePlayerBind* GetBoundInstance(uint32 mapid, Difficulty difficulty); + InstancePlayerBind* GetBoundInstance(uint32 mapid, Difficulty difficulty, bool withExpired = false); BoundInstancesMap& GetBoundInstances(Difficulty difficulty) { return m_boundInstances[difficulty]; } InstanceSave* GetInstanceSave(uint32 mapid, bool raid); void UnbindInstance(uint32 mapid, Difficulty difficulty, bool unload = false); void UnbindInstance(BoundInstancesMap::iterator &itr, Difficulty difficulty, bool unload = false); - InstancePlayerBind* BindToInstance(InstanceSave* save, bool permanent, bool load = false); + InstancePlayerBind* BindToInstance(InstanceSave* save, bool permanent, BindExtensionState extendState = EXTEND_STATE_NORMAL, bool load = false); void BindToInstance(); void SetPendingBind(uint32 instanceId, uint32 bindTimer); bool HasPendingBind() const { return _pendingBindId > 0; } diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index db084fdd606..f9f91147e6a 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -1633,6 +1633,9 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe RoundToInterval(auraAbsorbMod, 0.0f, 100.0f); + int32 absorbIgnoringDamage = CalculatePct(dmgInfo.GetDamage(), auraAbsorbMod); + dmgInfo.ModifyDamage(-absorbIgnoringDamage); + // 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 vSchoolAbsorbCopy(victim->GetAuraEffectsByType(SPELL_AURA_SCHOOL_ABSORB)); @@ -1665,9 +1668,6 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe if (defaultPrevented) continue; - // Apply absorb mod auras - AddPct(currentAbsorb, -auraAbsorbMod); - // absorb must be smaller than the damage itself currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(dmgInfo.GetDamage())); @@ -1716,8 +1716,6 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe if (defaultPrevented) continue; - AddPct(currentAbsorb, -auraAbsorbMod); - // absorb must be smaller than the damage itself currentAbsorb = RoundToInterval(currentAbsorb, 0, int32(dmgInfo.GetDamage())); @@ -1746,6 +1744,8 @@ void Unit::CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffe } } + dmgInfo.ModifyDamage(absorbIgnoringDamage); + // split damage auras - only when not damaging self if (victim != this) { @@ -4130,13 +4130,10 @@ void Unit::GetDispellableAuraList(Unit* caster, uint32 dispelMask, DispelCharges if (aura->GetSpellInfo()->GetDispelMask() & dispelMask) { - if (aura->GetSpellInfo()->Dispel == DISPEL_MAGIC) - { - // do not remove positive auras if friendly target - // negative auras if non-friendly target - if (aurApp->IsPositive() == IsFriendlyTo(caster)) - continue; - } + // do not remove positive auras if friendly target + // negative auras if non-friendly target + if (aurApp->IsPositive() == IsFriendlyTo(caster)) + continue; // The charges / stack amounts don't count towards the total number of auras that can be dispelled. // Ie: A dispel on a target with 5 stacks of Winters Chill and a Polymorph has 1 / (1 + 1) -> 50% chance to dispell @@ -11140,7 +11137,7 @@ Unit* Creature::SelectVictim() } } else - return NULL; + return nullptr; if (target && _IsTargetAcceptable(target) && CanCreatureAttack(target)) { @@ -11149,14 +11146,6 @@ Unit* Creature::SelectVictim() return target; } - // Case where mob is being kited. - // Mob may not be in range to attack or may have dropped target. In any case, - // don't evade if damage received within the last 10 seconds - // Does not apply to world bosses to prevent kiting to cities - if (!isWorldBoss() && !GetInstanceId()) - if (time(NULL) - GetLastDamagedTime() <= MAX_AGGRO_RESET_TIME) - return target; - // last case when creature must not go to evade mode: // it in combat but attacker not make any damage and not enter to aggro radius to have record in threat list // for example at owner command to pet attack some far away creature @@ -11165,12 +11154,12 @@ Unit* Creature::SelectVictim() { if ((*itr) && !CanCreatureAttack(*itr) && (*itr)->GetTypeId() != TYPEID_PLAYER && !(*itr)->ToCreature()->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN)) - return NULL; + return nullptr; } /// @todo a vehicle may eat some mob, so mob should not evade if (GetVehicle()) - return NULL; + return nullptr; // search nearby enemy before enter evade mode if (HasReactState(REACT_AGGRESSIVE)) @@ -11188,17 +11177,17 @@ Unit* Creature::SelectVictim() { if ((*itr)->GetBase()->IsPermanent()) { - AI()->EnterEvadeMode(); + AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_OTHER); break; } } - return NULL; + return nullptr; } // enter in evade mode in other case AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_HOSTILES); - return NULL; + return nullptr; } //====================================================================== @@ -15458,12 +15447,12 @@ void Unit::JumpTo(float speedXY, float speedZ, bool forward) } } -void Unit::JumpTo(WorldObject* obj, float speedZ) +void Unit::JumpTo(WorldObject* obj, float speedZ, bool withOrientation) { float x, y, z; obj->GetContactPoint(this, x, y, z); float speedXY = GetExactDist2d(x, y) * 10.0f / speedZ; - GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ); + GetMotionMaster()->MoveJump(x, y, z, GetAngle(obj), speedXY, speedZ, EVENT_JUMP, withOrientation); } bool Unit::HandleSpellClick(Unit* clicker, int8 seatId) diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index 8171243ce96..4700a483a74 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -1583,7 +1583,7 @@ class Unit : public WorldObject void SendMoveKnockBack(Player* player, float speedXY, float speedZ, float vcos, float vsin); void KnockbackFrom(float x, float y, float speedXY, float speedZ); void JumpTo(float speedXY, float speedZ, bool forward = true); - void JumpTo(WorldObject* obj, float speedZ); + void JumpTo(WorldObject* obj, float speedZ, bool withOrientation = false); void MonsterMoveWithSpeed(float x, float y, float z, float speed, bool generatePath = false, bool forceDestination = false); diff --git a/src/server/game/Events/GameEventMgr.cpp b/src/server/game/Events/GameEventMgr.cpp index d9afebae12e..26b3da2a0e4 100644 --- a/src/server/game/Events/GameEventMgr.cpp +++ b/src/server/game/Events/GameEventMgr.cpp @@ -221,7 +221,7 @@ void GameEventMgr::LoadFromDB() uint8 event_id = fields[0].GetUInt8(); if (event_id == 0) { - TC_LOG_ERROR("sql.sql", "`game_event` game event entry 0 is reserved and can't be used."); + TC_LOG_ERROR("sql.sql", "`game_event`: game event entry 0 is reserved and can't be used."); continue; } @@ -240,7 +240,7 @@ void GameEventMgr::LoadFromDB() if (pGameEvent.length == 0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check { - TC_LOG_ERROR("sql.sql", "`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.", event_id); + TC_LOG_ERROR("sql.sql", "`game_event`: game event id (%i) is not a world event and has length = 0, thus cannot be used.", event_id); continue; } @@ -248,7 +248,7 @@ void GameEventMgr::LoadFromDB() { if (!sHolidaysStore.LookupEntry(pGameEvent.holiday_id)) { - TC_LOG_ERROR("sql.sql", "`game_event` game event id (%i) have not existed holiday id %u.", event_id, pGameEvent.holiday_id); + TC_LOG_ERROR("sql.sql", "`game_event`: game event id (%i) contains nonexisting holiday id %u.", event_id, pGameEvent.holiday_id); pGameEvent.holiday_id = HOLIDAY_NONE; } } @@ -259,7 +259,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } @@ -283,7 +283,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_save` game event entry (%i) is out of range compared to max event entry in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_save`: game event entry (%i) is out of range compared to max event entry in `game_event`.", event_id); continue; } @@ -294,7 +294,7 @@ void GameEventMgr::LoadFromDB() } else { - TC_LOG_ERROR("sql.sql", "game_event_save includes event save for non-worldevent id %u", event_id); + TC_LOG_ERROR("sql.sql", "game_event_save includes event save for non-worldevent id %u.", event_id); continue; } @@ -302,7 +302,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u game event saves in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u game event saves in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -326,7 +326,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_prerequisite` game event id (%i) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_prerequisite`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -335,14 +335,14 @@ void GameEventMgr::LoadFromDB() uint16 prerequisite_event = fields[1].GetUInt32(); if (prerequisite_event >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_prerequisite` game event prerequisite id (%i) is out of range compared to max event id in `game_event`", prerequisite_event); + TC_LOG_ERROR("sql.sql", "`game_event_prerequisite`: game event prerequisite id (%i) is out of range compared to max event id in `game_event`.", prerequisite_event); continue; } mGameEvent[event_id].prerequisite_events.insert(prerequisite_event); } else { - TC_LOG_ERROR("sql.sql", "game_event_prerequisiste includes event entry for non-worldevent id %u", event_id); + TC_LOG_ERROR("sql.sql", "game_event_prerequisiste includes event entry for non-worldevent id %u.", event_id); continue; } @@ -350,7 +350,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u game event prerequisites in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u game event prerequisites in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -363,7 +363,7 @@ void GameEventMgr::LoadFromDB() QueryResult result = WorldDatabase.Query("SELECT guid, eventEntry FROM game_event_creature"); if (!result) - TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in game events. DB table `game_event_creature` is empty"); + TC_LOG_INFO("server.loading", ">> Loaded 0 creatures in game events. DB table `game_event_creature` is empty."); else { uint32 count = 0; @@ -385,7 +385,7 @@ void GameEventMgr::LoadFromDB() if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size())) { - TC_LOG_ERROR("sql.sql", "`game_event_creature` game event id (%i) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_creature`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -396,7 +396,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u creatures in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u creatures in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -431,7 +431,7 @@ void GameEventMgr::LoadFromDB() if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size())) { - TC_LOG_ERROR("sql.sql", "`game_event_gameobject` game event id (%i) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_gameobject`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -442,7 +442,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u gameobjects in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u gameobjects in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -469,7 +469,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEventModelEquip.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_model_equip` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_model_equip`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -485,7 +485,7 @@ void GameEventMgr::LoadFromDB() int8 equipId = static_cast(newModelEquipSet.equipment_id); if (!sObjectMgr->GetEquipmentInfo(entry, equipId)) { - TC_LOG_ERROR("sql.sql", "Table `game_event_model_equip` have creature (Guid: %u, entry: %u) with equipment_id %u not found in table `creature_equip_template`, set to no equipment.", + TC_LOG_ERROR("sql.sql", "Table `game_event_model_equip` contains creature (Guid: %u, entry: %u) with equipment_id %u not found in table `creature_equip_template`. Setting entry to no equipment.", guid, entry, newModelEquipSet.equipment_id); continue; } @@ -497,7 +497,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u model/equipment changes in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u model/equipment changes in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -523,7 +523,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEventCreatureQuests.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_creature_quest` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_creature_quest`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -534,7 +534,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -560,7 +560,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEventGameObjectQuests.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_gameobject_quest` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_gameobject_quest`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -571,7 +571,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -598,7 +598,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_quest_condition` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_quest_condition`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -610,7 +610,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u quest event conditions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u quest event conditions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -635,7 +635,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_condition` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_condition`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -648,7 +648,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u conditions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u conditions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -673,7 +673,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_condition_save` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_condition_save`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -684,7 +684,7 @@ void GameEventMgr::LoadFromDB() } else { - TC_LOG_ERROR("sql.sql", "game_event_condition_save contains not present condition evt id %u cond id %u", event_id, condition); + TC_LOG_ERROR("sql.sql", "game_event_condition_save contains not present condition event id %u condition id %u.", event_id, condition); continue; } @@ -692,7 +692,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u condition saves in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u condition saves in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -718,7 +718,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_npcflag` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_npcflag`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -728,7 +728,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u npcflags in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u npcflags in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -753,13 +753,13 @@ void GameEventMgr::LoadFromDB() if (!sObjectMgr->GetQuestTemplate(questId)) { - TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation` quest id (%u) does not exist in `quest_template`", questId); + TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation`: quest id (%u) does not exist in `quest_template`.", questId); continue; } if (eventEntry >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation` event id (%u) is out of range compared to max event in `game_event`", eventEntry); + TC_LOG_ERROR("sql.sql", "`game_event_seasonal_questrelation`: event id (%u) is out of range compared to max event in `game_event`.", eventEntry); continue; } @@ -768,7 +768,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u quests additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -792,7 +792,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEventVendors.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_npc_vendor` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_npc_vendor`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -831,7 +831,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u vendor additions in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u vendor additions in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -855,7 +855,7 @@ void GameEventMgr::LoadFromDB() if (event_id >= mGameEvent.size()) { - TC_LOG_ERROR("sql.sql", "`game_event_battleground_holiday` game event id (%u) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_battleground_holiday`: game event id (%u) is out of range compared to max event id in `game_event`.", event_id); continue; } @@ -865,7 +865,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u battleground holidays in game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u battleground holidays in game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } @@ -893,13 +893,13 @@ void GameEventMgr::LoadFromDB() if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size())) { - TC_LOG_ERROR("sql.sql", "`game_event_pool` game event id (%i) is out of range compared to max event id in `game_event`", event_id); + TC_LOG_ERROR("sql.sql", "`game_event_pool`: game event id (%i) is out of range compared to max event id in `game_event`.", event_id); continue; } if (!sPoolMgr->CheckPool(entry)) { - TC_LOG_ERROR("sql.sql", "Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system cannot pick one to spawn.", entry); + TC_LOG_ERROR("sql.sql", "Pool Id (%u) has all creatures or gameobjects with explicit chance sum <> 100 and no equal chance defined. The pool system cannot pick one to spawn.", entry); continue; } @@ -910,7 +910,7 @@ void GameEventMgr::LoadFromDB() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u pools for game events in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u pools for game events in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } } } @@ -972,7 +972,7 @@ void GameEventMgr::StartArenaSeason() if (!result) { - TC_LOG_ERROR("gameevent", "ArenaSeason (%u) must be an existant Arena Season", season); + TC_LOG_ERROR("gameevent", "ArenaSeason (%u) must be an existing Arena Season.", season); return; } @@ -981,7 +981,7 @@ void GameEventMgr::StartArenaSeason() if (eventId >= mGameEvent.size()) { - TC_LOG_ERROR("gameevent", "EventEntry %u for ArenaSeason (%u) does not exists", eventId, season); + TC_LOG_ERROR("gameevent", "EventEntry %u for ArenaSeason (%u) does not exist.", eventId, season); return; } @@ -1172,7 +1172,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size())) { - TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: %zu)", + TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventCreatureGuids element %i (size: %zu).", internal_event_id, mGameEventCreatureGuids.size()); return; } @@ -1199,7 +1199,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size())) { - TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %zu)", + TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventGameobjectGuids element %i (size: %zu).", internal_event_id, mGameEventGameobjectGuids.size()); return; } @@ -1232,7 +1232,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id) if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size())) { - TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempt access to out of range mGameEventPoolIds element %u (size: %zu)", + TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventSpawn attempted access to out of range mGameEventPoolIds element %u (size: %zu).", internal_event_id, mGameEventPoolIds.size()); return; } @@ -1247,7 +1247,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) if (internal_event_id < 0 || internal_event_id >= int32(mGameEventCreatureGuids.size())) { - TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: %zu)", + TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventCreatureGuids element %i (size: %zu).", internal_event_id, mGameEventCreatureGuids.size()); return; } @@ -1277,7 +1277,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) if (internal_event_id < 0 || internal_event_id >= int32(mGameEventGameobjectGuids.size())) { - TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: %zu)", + TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventGameobjectGuids element %i (size: %zu).", internal_event_id, mGameEventGameobjectGuids.size()); return; } @@ -1306,7 +1306,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id) } if (internal_event_id < 0 || internal_event_id >= int32(mGameEventPoolIds.size())) { - TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventPoolIds element %u (size: %zu)", internal_event_id, mGameEventPoolIds.size()); + TC_LOG_ERROR("gameevent", "GameEventMgr::GameEventUnspawn attempted access to out of range mGameEventPoolIds element %u (size: %zu).", internal_event_id, mGameEventPoolIds.size()); return; } diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 2f4c510d44d..0490e21eb9d 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -1786,7 +1786,7 @@ void ObjectMgr::LoadCreatures() if (!ok) continue; - // -1 random, 0 no equipment, + // -1 random, 0 no equipment if (data.equipmentId != 0) { if (!GetEquipmentInfo(data.id, data.equipmentId)) @@ -4069,7 +4069,7 @@ void ObjectMgr::LoadQuests() // client quest log visual (area case) if (qinfo->ZoneOrSort > 0) { - if (!GetAreaEntryByAreaID(qinfo->ZoneOrSort)) + if (!sAreaTableStore.LookupEntry(qinfo->ZoneOrSort)) { TC_LOG_ERROR("sql.sql", "Quest %u has `ZoneOrSort` = %u (zone case) but zone with this id does not exist.", qinfo->GetQuestId(), qinfo->ZoneOrSort); @@ -6025,7 +6025,7 @@ void ObjectMgr::LoadGraveyardZones() continue; } - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId); + 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); @@ -7885,7 +7885,7 @@ void ObjectMgr::LoadFishingBaseSkillLevel() uint32 entry = fields[0].GetUInt32(); int32 skill = fields[1].GetInt16(); - AreaTableEntry const* fArea = GetAreaEntryByAreaID(entry); + AreaTableEntry const* fArea = sAreaTableStore.LookupEntry(entry); if (!fArea) { TC_LOG_ERROR("sql.sql", "AreaId %u defined in `skill_fishing_base_level` does not exist", entry); diff --git a/src/server/game/Grids/GridDefines.h b/src/server/game/Grids/GridDefines.h index 8ad9fdce954..a13aa38f228 100644 --- a/src/server/game/Grids/GridDefines.h +++ b/src/server/game/Grids/GridDefines.h @@ -229,7 +229,7 @@ namespace Trinity inline bool IsValidMapCoord(float x, float y, float z) { - return IsValidMapCoord(x, y) && std::isfinite(z); + return IsValidMapCoord(x, y) && IsValidMapCoord(z); } inline bool IsValidMapCoord(float x, float y, float z, float o) diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index a5713ed8bfd..a7201ea3744 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -131,7 +131,7 @@ inline void CreatureUnitRelocationWorker(Creature* c, Unit* u) if (!u->IsAlive() || !c->IsAlive() || c == u || u->IsInFlight()) return; - if (c->HasReactState(REACT_AGGRESSIVE) && !c->HasUnitState(UNIT_STATE_SIGHTLESS)) + if (!c->HasUnitState(UNIT_STATE_SIGHTLESS)) { if (c->IsAIEnabled && c->CanSeeOrDetect(u, false, true)) c->AI()->MoveInLineOfSight_Safe(u); diff --git a/src/server/game/Groups/Group.cpp b/src/server/game/Groups/Group.cpp index 04ed24db847..aa3672815d6 100644 --- a/src/server/game/Groups/Group.cpp +++ b/src/server/game/Groups/Group.cpp @@ -718,7 +718,8 @@ void Group::ConvertLeaderInstancesToGroup(Player* player, Group* group, bool swi for (Player::BoundInstancesMap::iterator itr = player->m_boundInstances[i].begin(); itr != player->m_boundInstances[i].end();) { if (!switchLeader || !group->GetBoundInstance(itr->second.save->GetDifficulty(), itr->first)) - group->BindToInstance(itr->second.save, itr->second.perm, false); + if (itr->second.extendState) // not expired + group->BindToInstance(itr->second.save, itr->second.perm, false); // permanent binds are not removed if (switchLeader && !itr->second.perm) @@ -2322,6 +2323,8 @@ LootMethod Group::GetLootMethod() const ObjectGuid Group::GetLooterGuid() const { + if (GetLootMethod() == FREE_FOR_ALL) + return ObjectGuid::Empty; return m_looterGuid; } diff --git a/src/server/game/Handlers/AuctionHouseHandler.cpp b/src/server/game/Handlers/AuctionHouseHandler.cpp index 529a77a929d..77f2c462dad 100644 --- a/src/server/game/Handlers/AuctionHouseHandler.cpp +++ b/src/server/game/Handlers/AuctionHouseHandler.cpp @@ -731,7 +731,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) TC_LOG_DEBUG("network", "WORLD: Received CMSG_AUCTION_LIST_ITEMS"); std::string searchedname; - uint8 levelmin, levelmax, usable; + uint8 levelmin, levelmax, usable, getAll; uint32 listfrom, auctionSlotID, auctionMainCategory, auctionSubCategory, quality; ObjectGuid guid; @@ -743,7 +743,7 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) recvData >> auctionSlotID >> auctionMainCategory >> auctionSubCategory; recvData >> quality >> usable; - recvData.read_skip(); // unk + recvData >> getAll; recvData.read_skip(); // unk // this block looks like it uses some lame byte packing or similar... @@ -781,11 +781,11 @@ void WorldSession::HandleAuctionListItems(WorldPacket& recvData) auctionHouse->BuildListAuctionItems(data, _player, wsearchedname, listfrom, levelmin, levelmax, usable, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, - count, totalcount); + count, totalcount, (getAll != 0 && sWorld->getIntConfig(CONFIG_AUCTION_GETALL_DELAY) != 0)); data.put(0, count); data << uint32(totalcount); - data << uint32(300); //unk 2.3.0 + data << (uint32) sWorld->getIntConfig(CONFIG_AUCTION_SEARCH_DELAY); SendPacket(&data); } diff --git a/src/server/game/Handlers/CalendarHandler.cpp b/src/server/game/Handlers/CalendarHandler.cpp index 5acafc006fe..8e895c0ba2d 100644 --- a/src/server/game/Handlers/CalendarHandler.cpp +++ b/src/server/game/Handlers/CalendarHandler.cpp @@ -700,6 +700,21 @@ void WorldSession::HandleSetSavedInstanceExtend(WorldPacket& recvData) recvData >> mapId >> difficulty>> toggleExtend; TC_LOG_DEBUG("network", "CMSG_SET_SAVED_INSTANCE_EXTEND - MapId: %u, Difficulty: %u, ToggleExtend: %s", mapId, difficulty, toggleExtend ? "On" : "Off"); + if (Player* player = GetPlayer()) + { + InstancePlayerBind* instanceBind = player->GetBoundInstance(mapId, Difficulty(difficulty), toggleExtend == 1); // include expired instances if we are toggling extend on + if (!instanceBind || !instanceBind->save || !instanceBind->perm) + return; + + BindExtensionState newState; + if (!toggleExtend || instanceBind->extendState == EXTEND_STATE_EXPIRED) + newState = EXTEND_STATE_NORMAL; + else + newState = EXTEND_STATE_EXTENDED; + + player->BindToInstance(instanceBind->save, true, newState, false); + } + /* InstancePlayerBind* instanceBind = _player->GetBoundInstance(mapId, Difficulty(difficulty)); if (!instanceBind || !instanceBind->save) diff --git a/src/server/game/Handlers/ChannelHandler.cpp b/src/server/game/Handlers/ChannelHandler.cpp index 8b01cf235ae..a5a5b0ba7e3 100644 --- a/src/server/game/Handlers/ChannelHandler.cpp +++ b/src/server/game/Handlers/ChannelHandler.cpp @@ -46,7 +46,7 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket) if (!channel) return; - AreaTableEntry const* zone = GetAreaEntryByAreaID(GetPlayer()->GetZoneId()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(GetPlayer()->GetZoneId()); if (!zone || !GetPlayer()->CanJoinConstantChannelInZone(channel, zone)) return; } diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 8b3c39418bb..3c747619e75 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -37,7 +37,7 @@ bool WorldSession::CanOpenMailBox(ObjectGuid guid) { if (!HasPermission(rbac::RBAC_PERM_COMMAND_MAILBOX)) { - TC_LOG_WARN("cheat", "%s attempt open mailbox in cheating way.", _player->GetName().c_str()); + TC_LOG_WARN("cheat", "%s attempted to open mailbox by using a cheat.", _player->GetName().c_str()); return false; } } @@ -158,7 +158,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) if (!receiverGuid) { - TC_LOG_INFO("network", "Player %u is sending mail to %s (GUID: not existed!) with subject %s " + TC_LOG_INFO("network", "Player %u is sending mail to %s (GUID: non-existing!) with subject %s " "and body %s includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", player->GetGUID().GetCounter(), receiverName.c_str(), subject.c_str(), body.c_str(), items_count, money, COD, stationery, package); @@ -167,7 +167,7 @@ void WorldSession::HandleSendMail(WorldPacket& recvData) } TC_LOG_INFO("network", "Player %u is sending mail to %s (%s) with subject %s and body %s " - "includes %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", + "including %u items, " UI64FMTD " copper and " UI64FMTD " COD copper with unk1 = %u, unk2 = %u", player->GetGUID().GetCounter(), receiverName.c_str(), receiverGuid.ToString().c_str(), subject.c_str(), body.c_str(), items_count, money, COD, stationery, package); diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 1f9fb1e1a41..13216c6d489 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -105,7 +105,7 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) GameObject* go = NULL; if (guid.IsCreatureOrVehicle()) { - unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); + unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP); if (!unit) { TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with him.", guid.ToString().c_str()); @@ -114,10 +114,10 @@ void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) } else if (guid.IsGameObject()) { - go = _player->GetMap()->GetGameObject(guid); + go = _player->GetGameObjectIfCanInteractWith(guid); if (!go) { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found.", guid.ToString().c_str()); + TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - %s not found or you can't interact with it.", guid.ToString().c_str()); return; } } @@ -319,7 +319,7 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData) continue; std::string aname; - if (AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(pzoneid)) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(pzoneid)) aname = areaEntry->area_name[GetSessionDbcLocale()]; bool s_show = true; @@ -1255,7 +1255,7 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recvData) WorldPacket data(SMSG_INSPECT_TALENT, 8 + 4 + 1 + 1 + talent_points + 8 + 4 + 8 + 4); data << player->GetGUID(); - if (sWorld->getBoolConfig(CONFIG_TALENTS_INSPECTING) || _player->IsGameMaster()) + if (GetPlayer()->CanBeGameMaster() || sWorld->getIntConfig(CONFIG_TALENTS_INSPECTING) + (GetPlayer()->GetTeamId() == player->GetTeamId()) > 1) player->BuildPlayerTalentsInfoData(&data); else { @@ -1886,7 +1886,7 @@ void WorldSession::HandleHearthAndResurrect(WorldPacket& /*recvData*/) return; } - AreaTableEntry const* atEntry = GetAreaEntryByAreaID(_player->GetAreaId()); + AreaTableEntry const* atEntry = sAreaTableStore.LookupEntry(_player->GetAreaId()); if (!atEntry || !(atEntry->flags & AREA_FLAG_WINTERGRASP_2)) return; diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index ba061d0330e..989723f8382 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -402,7 +402,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) plrMover->UpdateFallInformationIfNeed(movementInfo, opcode); - AreaTableEntry const* zone = GetAreaEntryByAreaID(plrMover->GetAreaId()); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(plrMover->GetAreaId()); float depth = zone ? zone->MaxDepth : -500.0f; if (movementInfo.pos.GetPositionZ() < depth) { @@ -413,6 +413,7 @@ void WorldSession::HandleMovementOpcodes(WorldPacket& recvPacket) /// @todo discard movement packets after the player is rooted if (plrMover->IsAlive()) { + plrMover->SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_IS_OUT_OF_BOUNDS); plrMover->EnvironmentalDamage(DAMAGE_FALL_TO_VOID, GetPlayer()->GetMaxHealth()); // player can be alive if GM/etc // change the death state to CORPSE to prevent the death timer from diff --git a/src/server/game/Handlers/NPCHandler.cpp b/src/server/game/Handlers/NPCHandler.cpp index ea57f156aa0..7e45de8a6d6 100644 --- a/src/server/game/Handlers/NPCHandler.cpp +++ b/src/server/game/Handlers/NPCHandler.cpp @@ -317,7 +317,7 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData) ObjectGuid guid; recvData >> guid; - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); + Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_GOSSIP); if (!unit) { TC_LOG_DEBUG("network", "WORLD: HandleGossipHelloOpcode - %s not found or you can not interact with him.", guid.ToString().c_str()); @@ -357,47 +357,6 @@ void WorldSession::HandleGossipHelloOpcode(WorldPacket& recvData) unit->AI()->sGossipHello(_player); } -/*void WorldSession::HandleGossipSelectOptionOpcode(WorldPacket& recvData) -{ - TC_LOG_DEBUG("network", "WORLD: CMSG_GOSSIP_SELECT_OPTION"); - - uint32 option; - uint32 unk; - uint64 guid; - std::string code = ""; - - recvData >> guid >> unk >> option; - - if (_player->PlayerTalkClass->GossipOptionCoded(option)) - { - TC_LOG_DEBUG("network", "reading string"); - recvData >> code; - TC_LOG_DEBUG("network", "string read: %s", code.c_str()); - } - - Creature* unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); - if (!unit) - { - TC_LOG_DEBUG("network", "WORLD: HandleGossipSelectOptionOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid))); - return; - } - - // remove fake death - if (GetPlayer()->HasUnitState(UNIT_STATE_DIED)) - GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH); - - if (!code.empty()) - { - if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction(option), code.c_str())) - unit->OnGossipSelect (_player, option); - } - else - { - if (!Script->OnGossipSelect (_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction (option))) - unit->OnGossipSelect (_player, option); - } -}*/ - void WorldSession::HandleSpiritHealerActivateOpcode(WorldPacket& recvData) { TC_LOG_DEBUG("network", "WORLD: CMSG_SPIRIT_HEALER_ACTIVATE"); diff --git a/src/server/game/Handlers/QuestHandler.cpp b/src/server/game/Handlers/QuestHandler.cpp index f103ab24951..2aa2d4e200e 100644 --- a/src/server/game/Handlers/QuestHandler.cpp +++ b/src/server/game/Handlers/QuestHandler.cpp @@ -75,7 +75,7 @@ void WorldSession::HandleQuestgiverHelloOpcode(WorldPacket& recvData) TC_LOG_DEBUG("network", "WORLD: Received CMSG_QUESTGIVER_HELLO %s", guid.ToString().c_str()); - Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_NONE); + Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_QUESTGIVER); if (!creature) { TC_LOG_DEBUG("network", "WORLD: HandleQuestgiverHelloOpcode - %s not found or you can't interact with him.", diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp index f8914d3c8d7..9541cacc91c 100644 --- a/src/server/game/Handlers/SpellHandler.cpp +++ b/src/server/game/Handlers/SpellHandler.cpp @@ -287,11 +287,8 @@ void WorldSession::HandleGameObjectUseOpcode(WorldPacket& recvData) TC_LOG_DEBUG("network", "WORLD: Recvd CMSG_GAMEOBJ_USE Message [%s]", guid.ToString().c_str()); - if (GameObject* obj = GetPlayer()->GetMap()->GetGameObject(guid)) + if (GameObject* obj = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) { - if (!obj->IsWithinDistInMap(GetPlayer(), obj->GetInteractionDistance())) - return; - // ignore for remote control state if (GetPlayer()->m_mover != GetPlayer()) if (!(GetPlayer()->IsOnVehicle(GetPlayer()->m_mover) || GetPlayer()->IsMounted()) && !obj->GetGOInfo()->IsUsableMounted()) @@ -312,17 +309,13 @@ void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket) if (_player->m_mover != _player) return; - GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid); - if (!go) - return; + if (GameObject* go = GetPlayer()->GetGameObjectIfCanInteractWith(guid)) + { + if (go->AI()->GossipHello(_player)) + return; - if (!go->IsWithinDistInMap(_player, INTERACTION_DISTANCE)) - return; - - if (go->AI()->GossipHello(_player)) - return; - - _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); + _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry()); + } } void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket) diff --git a/src/server/game/Instances/InstanceSaveMgr.cpp b/src/server/game/Instances/InstanceSaveMgr.cpp index 51a41176f31..432889e1e22 100644 --- a/src/server/game/Instances/InstanceSaveMgr.cpp +++ b/src/server/game/Instances/InstanceSaveMgr.cpp @@ -435,6 +435,23 @@ void InstanceSaveManager::LoadResetTimes() } } +time_t InstanceSaveManager::GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const +{ + MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty); + if (!mapDiff || !mapDiff->resetTime) + { + TC_LOG_ERROR("misc", "InstanceSaveManager::GetSubsequentResetTime: not valid difficulty or no reset delay for map %u", mapid); + return 0; + } + + time_t diff = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; + time_t period = uint32(((mapDiff->resetTime * sWorld->getRate(RATE_INSTANCE_RESET_TIME)) / DAY) * DAY); + if (period < DAY) + period = DAY; + + return ((resetTime + MINUTE) / DAY * DAY) + period + diff; +} + void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent event) { if (!add) @@ -472,6 +489,17 @@ void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent ev m_resetTimeQueue.insert(std::pair(time, event)); } +void InstanceSaveManager::ForceGlobalReset(uint32 mapId, Difficulty difficulty) +{ + if (!GetDownscaledMapDifficultyData(mapId, difficulty)) + return; + // remove currently scheduled reset times + ScheduleReset(false, 0, InstResetEvent(1, mapId, difficulty, 0)); + ScheduleReset(false, 0, InstResetEvent(4, mapId, difficulty, 0)); + // force global reset on the instance + _ResetOrWarnAll(mapId, difficulty, false, time(nullptr)); +} + void InstanceSaveManager::Update() { time_t now = time(NULL); @@ -512,10 +540,26 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr) // do not allow UnbindInstance to automatically unload the InstanceSaves lock_instLists = true; + bool shouldDelete = true; InstanceSave::PlayerListType &pList = itr->second->m_playerList; - while (!pList.empty()) + std::vector temp; // list of expired binds that should be unbound + for (Player* player : pList) + { + if (InstancePlayerBind* bind = player->GetBoundInstance(itr->second->GetMapId(), itr->second->GetDifficulty())) + { + ASSERT(bind->save == itr->second); + if (bind->perm && bind->extendState) // permanent and not already expired + { + // actual promotion in DB already happened in caller + bind->extendState = bind->extendState == EXTEND_STATE_EXTENDED ? EXTEND_STATE_NORMAL : EXTEND_STATE_EXPIRED; + shouldDelete = false; + continue; + } + } + temp.push_back(player); + } + for (Player* player : temp) { - Player* player = *(pList.begin()); player->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true); } @@ -526,8 +570,13 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr) group->UnbindInstance(itr->second->GetMapId(), itr->second->GetDifficulty(), true); } - delete itr->second; - m_instanceSaveById.erase(itr++); + if (shouldDelete) + { + delete itr->second; + itr = m_instanceSaveById.erase(itr); + } + else + ++itr; lock_instLists = false; } @@ -568,31 +617,21 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b MapEntry const* mapEntry = sMapStore.LookupEntry(mapid); if (!mapEntry->Instanceable()) return; + TC_LOG_DEBUG("misc", "InstanceSaveManager::ResetOrWarnAll: Processing map %s (%u) on difficulty %u (warn? %u)", mapEntry->name[0], mapid, uint8(difficulty), warn); time_t now = time(NULL); if (!warn) { - MapDifficulty const* mapDiff = GetMapDifficultyData(mapid, difficulty); - if (!mapDiff || !mapDiff->resetTime) - { - TC_LOG_ERROR("misc", "InstanceSaveManager::ResetOrWarnAll: not valid difficulty or no reset delay for map %d", mapid); + // calculate the next reset time + time_t next_reset = GetSubsequentResetTime(mapid, difficulty, resetTime); + if (!next_reset) return; - } - // remove all binds to instances of the given map - for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();) - { - if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty) - _ResetSave(itr); - else - ++itr; - } - - // delete them from the DB, even if not loaded + // delete/promote instance binds from the DB, even if not loaded SQLTransaction trans = CharacterDatabase.BeginTransaction(); - PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_CHAR_INSTANCE_BY_MAP_DIFF); + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_CHAR_INSTANCE_BY_MAP_DIFF); stmt->setUInt16(0, uint16(mapid)); stmt->setUInt8(1, uint8(difficulty)); trans->Append(stmt); @@ -602,21 +641,26 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, Difficulty difficulty, b stmt->setUInt8(1, uint8(difficulty)); trans->Append(stmt); - stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_INSTANCE_BY_MAP_DIFF); + stmt = CharacterDatabase.GetPreparedStatement(CHAR_DEL_EXPIRED_INSTANCE_BY_MAP_DIFF); + stmt->setUInt16(0, uint16(mapid)); + stmt->setUInt8(1, uint8(difficulty)); + trans->Append(stmt); + + stmt = CharacterDatabase.GetPreparedStatement(CHAR_UPD_EXPIRE_CHAR_INSTANCE_BY_MAP_DIFF); stmt->setUInt16(0, uint16(mapid)); stmt->setUInt8(1, uint8(difficulty)); trans->Append(stmt); CharacterDatabase.CommitTransaction(trans); - // calculate the next reset time - uint32 diff = sWorld->getIntConfig(CONFIG_INSTANCE_RESET_TIME_HOUR) * HOUR; - - uint32 period = uint32(((mapDiff->resetTime * sWorld->getRate(RATE_INSTANCE_RESET_TIME))/DAY) * DAY); - if (period < DAY) - period = DAY; - - uint32 next_reset = uint32(((resetTime + MINUTE) / DAY * DAY) + period + diff); + // promote loaded binds to instances of the given map + for (InstanceSaveHashMap::iterator itr = m_instanceSaveById.begin(); itr != m_instanceSaveById.end();) + { + if (itr->second->GetMapId() == mapid && itr->second->GetDifficulty() == difficulty) + _ResetSave(itr); + else + ++itr; + } SetResetTimeFor(mapid, difficulty, next_reset); ScheduleReset(true, time_t(next_reset-3600), InstResetEvent(1, mapid, difficulty, 0)); diff --git a/src/server/game/Instances/InstanceSaveMgr.h b/src/server/game/Instances/InstanceSaveMgr.h index 956513e9010..6c75fb7e5b2 100644 --- a/src/server/game/Instances/InstanceSaveMgr.h +++ b/src/server/game/Instances/InstanceSaveMgr.h @@ -190,6 +190,7 @@ class InstanceSaveManager ResetTimeByMapDifficultyMap::const_iterator itr = m_resetTimeByMapDifficulty.find(MAKE_PAIR32(mapid, d)); return itr != m_resetTimeByMapDifficulty.end() ? itr->second : 0; } + time_t GetSubsequentResetTime(uint32 mapid, Difficulty difficulty, time_t resetTime) const; // Use this on startup when initializing reset times void InitializeResetTimeFor(uint32 mapid, Difficulty d, time_t t) @@ -210,6 +211,7 @@ class InstanceSaveManager return m_resetTimeByMapDifficulty; } void ScheduleReset(bool add, time_t time, InstResetEvent event); + void ForceGlobalReset(uint32 mapId, Difficulty difficulty); void Update(); diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 95d78689c82..8dc15c25b3a 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -1591,8 +1591,8 @@ void LoadLootTemplates_Fishing() uint32 count = LootTemplates_Fishing.LoadAndCollectLootIds(lootIdSet); // remove real entries and check existence loot - for (uint32 i = 1; i < sAreaStore.GetNumRows(); ++i) - if (AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(i)) + for (uint32 i = 1; i < sAreaTableStore.GetNumRows(); ++i) + if (AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(i)) if (lootIdSet.find(areaEntry->ID) != lootIdSet.end()) lootIdSet.erase(areaEntry->ID); diff --git a/src/server/game/Mails/Mail.cpp b/src/server/game/Mails/Mail.cpp index b0e8a1ebe02..bc31a3c8c06 100644 --- a/src/server/game/Mails/Mail.cpp +++ b/src/server/game/Mails/Mail.cpp @@ -49,8 +49,8 @@ MailSender::MailSender(Object* sender, MailStationery stationery) : m_stationery break; default: m_messageType = MAIL_NORMAL; - m_senderId = 0; // will show mail from not existed player - TC_LOG_ERROR("misc", "MailSender::MailSender - Mail have unexpected sender typeid (%u)", sender->GetTypeId()); + m_senderId = 0; // will show mail from non-existing player + TC_LOG_ERROR("misc", "MailSender::MailSender - Mail message contains unexpected sender typeid (%u).", sender->GetTypeId()); break; } } diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 728fdc59f6f..9f044a47cab 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1640,13 +1640,13 @@ GridMap::GridMap() _flags = 0; // Area data _gridArea = 0; - _areaMap = NULL; + _areaMap = nullptr; // Height level data _gridHeight = INVALID_HEIGHT; _gridGetHeight = &GridMap::getHeightFromFlat; _gridIntHeightMultiplier = 0; - m_V9 = NULL; - m_V8 = NULL; + m_V9 = nullptr; + m_V8 = nullptr; // Liquid data _liquidType = 0; _liquidOffX = 0; @@ -1654,9 +1654,9 @@ GridMap::GridMap() _liquidWidth = 0; _liquidHeight = 0; _liquidLevel = INVALID_HEIGHT; - _liquidEntry = NULL; - _liquidFlags = NULL; - _liquidMap = NULL; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; } GridMap::~GridMap() @@ -1722,12 +1722,12 @@ void GridMap::unloadData() delete[] _liquidEntry; delete[] _liquidFlags; delete[] _liquidMap; - _areaMap = NULL; - m_V9 = NULL; - m_V8 = NULL; - _liquidEntry = NULL; - _liquidFlags = NULL; - _liquidMap = NULL; + _areaMap = nullptr; + m_V9 = nullptr; + m_V8 = nullptr; + _liquidEntry = nullptr; + _liquidFlags = nullptr; + _liquidMap = nullptr; _gridGetHeight = &GridMap::getHeightFromFlat; } @@ -1742,7 +1742,7 @@ bool GridMap::loadAreaData(FILE* in, uint32 offset, uint32 /*size*/) _gridArea = header.gridArea; if (!(header.flags & MAP_AREA_NO_AREA)) { - _areaMap = new uint16 [16*16]; + _areaMap = new uint16[16 * 16]; if (fread(_areaMap, sizeof(uint16), 16*16, in) != 16*16) return false; } @@ -1792,6 +1792,7 @@ bool GridMap::loadHeightData(FILE* in, uint32 offset, uint32 /*size*/) } else _gridGetHeight = &GridMap::getHeightFromFlat; + return true; } @@ -2121,12 +2122,12 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R uint32 liqTypeIdx = liquidEntry->Type; if (entry < 21) { - if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(getArea(x, y), MAPID_INVALID)) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(getArea(x, y))) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; if (!overrideLiquid && area->zone) { - area = GetAreaEntryByAreaID(area->zone); + area = sAreaTableStore.LookupEntry(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidEntry->Type]; } @@ -2300,7 +2301,7 @@ bool Map::IsOutdoors(float x, float y, float z) const if (wmoEntry) { TC_LOG_DEBUG("maps", "Got WMOAreaTableEntry! flag %u, areaid %u", wmoEntry->Flags, wmoEntry->areaId); - atEntry = GetAreaEntryByAreaID(wmoEntry->areaId); + atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); } return IsOutdoorWMO(mogpFlags, adtId, rootId, groupId, wmoEntry, atEntry); } @@ -2324,7 +2325,7 @@ bool Map::GetAreaInfo(float x, float y, float z, uint32 &flags, int32 &adtId, in return false; } -uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const +uint32 Map::GetAreaId(float x, float y, float z, bool *isOutdoors) const { uint32 mogpFlags; int32 adtId, rootId, groupId; @@ -2337,20 +2338,21 @@ uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const haveAreaInfo = true; wmoEntry = GetWMOAreaTableEntryByTripple(rootId, adtId, groupId); if (wmoEntry) - atEntry = GetAreaEntryByAreaID(wmoEntry->areaId); + atEntry = sAreaTableStore.LookupEntry(wmoEntry->areaId); } - uint16 areaflag; + uint32 areaId = 0; if (atEntry) - areaflag = atEntry->exploreFlag; + areaId = atEntry->ID; else { if (GridMap* gmap = const_cast(this)->GetGrid(x, y)) - areaflag = gmap->getArea(x, y); + areaId = gmap->getArea(x, y); + // this used while not all *.map files generated (instances) - else - areaflag = GetAreaFlagByMapId(i_mapEntry->MapID); + if (!areaId) + areaId = i_mapEntry->linked_zone; } if (isOutdoors) @@ -2360,8 +2362,31 @@ uint16 Map::GetAreaFlag(float x, float y, float z, bool *isOutdoors) const else *isOutdoors = true; } - return areaflag; - } + return areaId; +} + +uint32 Map::GetAreaId(float x, float y, float z) const +{ + return GetAreaId(x, y, z, nullptr); +} + +uint32 Map::GetZoneId(float x, float y, float z) const +{ + uint32 areaId = GetAreaId(x, y, z); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId)) + if (area->zone) + return area->zone; + + return areaId; +} + +void Map::GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const +{ + areaid = zoneid = GetAreaId(x, y, z); + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaid)) + if (area->zone) + zoneid = area->zone; +} uint8 Map::GetTerrainType(float x, float y) const { @@ -2397,12 +2422,12 @@ ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidTyp if (liquid_type && liquid_type < 21) { - if (AreaTableEntry const* area = GetAreaEntryByAreaFlagAndMap(GetAreaFlag(x, y, z), GetId())) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(GetAreaId(x, y, z))) { uint32 overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; if (!overrideLiquid && area->zone) { - area = GetAreaEntryByAreaID(area->zone); + area = sAreaTableStore.LookupEntry(area->zone); if (area) overrideLiquid = area->LiquidTypeOverride[liquidFlagType]; } @@ -2464,34 +2489,6 @@ float Map::GetWaterLevel(float x, float y) const return 0; } -uint32 Map::GetAreaIdByAreaFlag(uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - if (entry) - return entry->ID; - else - return 0; -} - -uint32 Map::GetZoneIdByAreaFlag(uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - if (entry) - return (entry->zone != 0) ? entry->zone : entry->ID; - else - return 0; -} - -void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id) -{ - AreaTableEntry const* entry = GetAreaEntryByAreaFlagAndMap(areaflag, map_id); - - areaid = entry ? entry->ID : 0; - zoneid = entry ? ((entry->zone != 0) ? entry->zone : entry->ID) : 0; -} - bool Map::isInLineOfSight(float x1, float y1, float z1, float x2, float y2, float z2, uint32 phasemask) const { return VMAP::VMapFactory::createOrGetVMapManager()->isInLineOfSight(GetId(), x1, y1, z1, x2, y2, z2) @@ -3200,22 +3197,37 @@ bool InstanceMap::Reset(uint8 method) } else { + bool doUnload = true; if (method == INSTANCE_RESET_GLOBAL) + { // set the homebind timer for players inside (1 minute) for (MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr) - itr->GetSource()->m_InstanceValid = false; + { + InstancePlayerBind* bind = itr->GetSource()->GetBoundInstance(GetId(), GetDifficulty()); + if (bind && bind->extendState && bind->save->GetInstanceId() == GetInstanceId()) + doUnload = false; + else + itr->GetSource()->m_InstanceValid = false; + } - // the unload timer is not started - // instead the map will unload immediately after the players have left - m_unloadWhenEmpty = true; - m_resetAfterUnload = true; + if (doUnload && HasPermBoundPlayers()) // check if any unloaded players have a nonexpired save to this + doUnload = false; + } + + if (doUnload) + { + // the unload timer is not started + // instead the map will unload immediately after the players have left + m_unloadWhenEmpty = true; + m_resetAfterUnload = true; + } } } else { // unloaded at next update m_unloadTimer = MIN_UNLOAD_DELAY; - m_resetAfterUnload = true; + m_resetAfterUnload = !(method == INSTANCE_RESET_GLOBAL && HasPermBoundPlayers()); } return m_mapRefManager.isEmpty(); @@ -3296,6 +3308,13 @@ MapDifficulty const* Map::GetMapDifficulty() const return GetMapDifficultyData(GetId(), GetDifficulty()); } +bool InstanceMap::HasPermBoundPlayers() const +{ + PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_SEL_PERM_BIND_BY_INSTANCE); + stmt->setUInt16(0,GetInstanceId()); + return !!CharacterDatabase.Query(stmt); +} + uint32 InstanceMap::GetMaxPlayers() const { MapDifficulty const* mapDiff = GetMapDifficulty(); diff --git a/src/server/game/Maps/Map.h b/src/server/game/Maps/Map.h index dc180685e9b..ae916ae8602 100644 --- a/src/server/game/Maps/Map.h +++ b/src/server/game/Maps/Map.h @@ -98,9 +98,9 @@ struct map_areaHeader uint16 gridArea; }; -#define MAP_HEIGHT_NO_HEIGHT 0x0001 -#define MAP_HEIGHT_AS_INT16 0x0002 -#define MAP_HEIGHT_AS_INT8 0x0004 +#define MAP_HEIGHT_NO_HEIGHT 0x0001 +#define MAP_HEIGHT_AS_INT16 0x0002 +#define MAP_HEIGHT_AS_INT8 0x0004 struct map_heightHeader { @@ -329,8 +329,11 @@ class Map : public GridRefManager ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData* data = nullptr) const; - uint16 GetAreaFlag(float x, float y, float z, bool *isOutdoors=nullptr) const; - bool GetAreaInfo(float x, float y, float z, uint32 &mogpflags, int32 &adtId, int32 &rootId, int32 &groupId) const; + uint32 GetAreaId(float x, float y, float z, bool *isOutdoors) const; + bool GetAreaInfo(float x, float y, float z, uint32& mogpflags, int32& adtId, int32& rootId, int32& groupId) const; + uint32 GetAreaId(float x, float y, float z) const; + uint32 GetZoneId(float x, float y, float z) const; + void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const; bool IsOutdoors(float x, float y, float z) const; @@ -339,25 +342,6 @@ class Map : public GridRefManager bool IsInWater(float x, float y, float z, LiquidData* data = nullptr) const; bool IsUnderWater(float x, float y, float z) const; - static uint32 GetAreaIdByAreaFlag(uint16 areaflag, uint32 map_id); - static uint32 GetZoneIdByAreaFlag(uint16 areaflag, uint32 map_id); - static void GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag, uint32 map_id); - - uint32 GetAreaId(float x, float y, float z) const - { - return GetAreaIdByAreaFlag(GetAreaFlag(x, y, z), GetId()); - } - - uint32 GetZoneId(float x, float y, float z) const - { - return GetZoneIdByAreaFlag(GetAreaFlag(x, y, z), GetId()); - } - - void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const - { - GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(x, y, z), GetId()); - } - void MoveAllCreaturesInMoveList(); void MoveAllGameObjectsInMoveList(); void MoveAllDynamicObjectsInMoveList(); @@ -785,6 +769,9 @@ class InstanceMap : public Map void SendResetWarnings(uint32 timeLeft) const; void SetResetSchedule(bool on); + /* this checks if any players have a permanent bind (included reactivatable expired binds) to the instance ID + it needs a DB query, so use sparingly */ + bool HasPermBoundPlayers() const; uint32 GetMaxPlayers() const; uint32 GetMaxResetDelay() const; diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index 32a9835664b..f9c2012fe11 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -42,22 +42,20 @@ class MapManager Map* CreateMap(uint32 mapId, Player* player, uint32 loginInstanceId=0); Map* FindMap(uint32 mapId, uint32 instanceId) const; - uint16 GetAreaFlag(uint32 mapid, float x, float y, float z) const - { - Map const* m = const_cast(this)->CreateBaseMap(mapid); - return m->GetAreaFlag(x, y, z); - } uint32 GetAreaId(uint32 mapid, float x, float y, float z) const { - return Map::GetAreaIdByAreaFlag(GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast(this)->CreateBaseMap(mapid); + return m->GetAreaId(x, y, z); } uint32 GetZoneId(uint32 mapid, float x, float y, float z) const { - return Map::GetZoneIdByAreaFlag(GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast(this)->CreateBaseMap(mapid); + return m->GetZoneId(x, y, z); } void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, uint32 mapid, float x, float y, float z) { - Map::GetZoneAndAreaIdByAreaFlag(zoneid, areaid, GetAreaFlag(mapid, x, y, z), mapid); + Map const* m = const_cast(this)->CreateBaseMap(mapid); + m->GetZoneAndAreaId(zoneid, areaid, x, y, z); } void Initialize(void); diff --git a/src/server/game/Miscellaneous/Formulas.h b/src/server/game/Miscellaneous/Formulas.h index 9eaf8c5c1ab..54e280647fd 100644 --- a/src/server/game/Miscellaneous/Formulas.h +++ b/src/server/game/Miscellaneous/Formulas.h @@ -188,6 +188,9 @@ namespace Trinity } xpMod *= isBattleGround ? sWorld->getRate(RATE_XP_BG_KILL) : sWorld->getRate(RATE_XP_KILL); + if (creature && creature->m_PlayerDamageReq) // if players dealt less than 50% of the damage and were credited anyway (due to CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ), scale XP gained appropriately (linear scaling) + xpMod *= 1.0f - 2.0f*creature->m_PlayerDamageReq / creature->GetMaxHealth(); + gain = uint32(gain * xpMod); } diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index b818b5be8bc..97c74ab5ee8 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -193,7 +193,7 @@ void MotionMaster::MoveRandom(float spawndist) { if (_owner->GetTypeId() == TYPEID_UNIT) { - TC_LOG_DEBUG("misc", "Creature (GUID: %u) start moving random", _owner->GetGUID().GetCounter()); + TC_LOG_DEBUG("misc", "Creature (GUID: %u) started random movement.", _owner->GetGUID().GetCounter()); Mutate(new RandomMovementGenerator(spawndist), MOTION_SLOT_IDLE); } } @@ -204,22 +204,22 @@ void MotionMaster::MoveTargetedHome() if (_owner->GetTypeId() == TYPEID_UNIT && !_owner->ToCreature()->GetCharmerOrOwnerGUID()) { - TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) targeted home", _owner->GetEntry(), _owner->GetGUID().GetCounter()); + TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) targeted home.", _owner->GetEntry(), _owner->GetGUID().GetCounter()); Mutate(new HomeMovementGenerator(), MOTION_SLOT_ACTIVE); } else if (_owner->GetTypeId() == TYPEID_UNIT && _owner->ToCreature()->GetCharmerOrOwnerGUID()) { - TC_LOG_DEBUG("misc", "Pet or controlled creature (Entry: %u GUID: %u) targeting home", _owner->GetEntry(), _owner->GetGUID().GetCounter()); + TC_LOG_DEBUG("misc", "Pet or controlled creature (Entry: %u GUID: %u) is targeting home.", _owner->GetEntry(), _owner->GetGUID().GetCounter()); Unit* target = _owner->ToCreature()->GetCharmerOrOwner(); if (target) { - TC_LOG_DEBUG("misc", "Following %s (GUID: %u)", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : ((Creature*)target)->GetSpawnId()); + TC_LOG_DEBUG("misc", "Following %s (GUID: %u).", target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : ((Creature*)target)->GetSpawnId()); Mutate(new FollowMovementGenerator(target, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE), MOTION_SLOT_ACTIVE); } } else { - TC_LOG_ERROR("misc", "Player (GUID: %u) attempt targeted home", _owner->GetGUID().GetCounter()); + TC_LOG_ERROR("misc", "Player (GUID: %u) attempted to move towards target home.", _owner->GetGUID().GetCounter()); } } @@ -272,14 +272,14 @@ void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlo //_owner->AddUnitState(UNIT_STATE_FOLLOW); if (_owner->GetTypeId() == TYPEID_PLAYER) { - TC_LOG_DEBUG("misc", "Player (GUID: %u) follow to %s (GUID: %u)", _owner->GetGUID().GetCounter(), + TC_LOG_DEBUG("misc", "Player (GUID: %u) follows %s (GUID: %u).", _owner->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : target->ToCreature()->GetSpawnId()); Mutate(new FollowMovementGenerator(target, dist, angle), slot); } else { - TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) follow to %s (GUID: %u)", + TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) follows %s (GUID: %u).", _owner->GetEntry(), _owner->GetGUID().GetCounter(), target->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", target->GetTypeId() == TYPEID_PLAYER ? target->GetGUID().GetCounter() : target->ToCreature()->GetSpawnId()); @@ -291,12 +291,12 @@ void MotionMaster::MovePoint(uint32 id, float x, float y, float z, bool generate { if (_owner->GetTypeId() == TYPEID_PLAYER) { - TC_LOG_DEBUG("misc", "Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f)", _owner->GetGUID().GetCounter(), id, x, y, z); + TC_LOG_DEBUG("misc", "Player (GUID: %u) targeted point (Id: %u X: %f Y: %f Z: %f).", _owner->GetGUID().GetCounter(), id, x, y, z); Mutate(new PointMovementGenerator(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE); } else { - TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f)", + TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) targeted point (ID: %u X: %f Y: %f Z: %f).", _owner->GetEntry(), _owner->GetGUID().GetCounter(), id, x, y, z); Mutate(new PointMovementGenerator(id, x, y, z, generatePath), MOTION_SLOT_ACTIVE); } @@ -307,7 +307,7 @@ void MotionMaster::MoveLand(uint32 id, Position const& pos) float x, y, z; pos.GetPosition(x, y, z); - TC_LOG_DEBUG("misc", "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z); + TC_LOG_DEBUG("misc", "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f).", _owner->GetEntry(), id, x, y, z); Movement::MoveSplineInit init(_owner); init.MoveTo(x, y, z); @@ -321,7 +321,7 @@ void MotionMaster::MoveTakeoff(uint32 id, Position const& pos) float x, y, z; pos.GetPosition(x, y, z); - TC_LOG_DEBUG("misc", "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f)", _owner->GetEntry(), id, x, y, z); + TC_LOG_DEBUG("misc", "Creature (Entry: %u) landing point (ID: %u X: %f Y: %f Z: %f).", _owner->GetEntry(), id, x, y, z); Movement::MoveSplineInit init(_owner); init.MoveTo(x, y, z); @@ -366,12 +366,12 @@ void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ) float moveTimeHalf = speedZ / Movement::gravity; float dist = 2 * moveTimeHalf * speedXY; _owner->GetClosePoint(x, y, z, _owner->GetObjectSize(), dist, angle); - MoveJump(x, y, z, speedXY, speedZ); + MoveJump(x, y, z, 0.0f, speedXY, speedZ); } -void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id) +void MotionMaster::MoveJump(float x, float y, float z, float o, float speedXY, float speedZ, uint32 id, bool hasOrientation /* = false*/) { - TC_LOG_DEBUG("misc", "Unit (GUID: %u) jump to point (X: %f Y: %f Z: %f)", _owner->GetGUID().GetCounter(), x, y, z); + TC_LOG_DEBUG("misc", "Unit (GUID: %u) jumps to point (X: %f Y: %f Z: %f).", _owner->GetGUID().GetCounter(), x, y, z); if (speedXY <= 0.1f) return; @@ -382,6 +382,8 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee init.MoveTo(x, y, z, false); init.SetParabolic(max_height, 0); init.SetVelocity(speedXY); + if (hasOrientation) + init.SetFacing(o); init.Launch(); Mutate(new EffectMovementGenerator(id), MOTION_SLOT_CONTROLLED); } @@ -447,7 +449,7 @@ void MotionMaster::MoveFall(uint32 id /*=0*/) float tz = _owner->GetMap()->GetHeight(_owner->GetPhaseMask(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ(), true, MAX_FALL_DISTANCE); if (tz <= INVALID_HEIGHT) { - TC_LOG_DEBUG("misc", "MotionMaster::MoveFall: unable retrive a proper height at map %u (x: %f, y: %f, z: %f).", + TC_LOG_DEBUG("misc", "MotionMaster::MoveFall: unable to retrieve a proper height at map %u (x: %f, y: %f, z: %f).", _owner->GetMap()->GetId(), _owner->GetPositionX(), _owner->GetPositionY(), _owner->GetPositionZ()); return; } @@ -473,12 +475,12 @@ void MotionMaster::MoveCharge(float x, float y, float z, float speed /*= SPEED_C if (_owner->GetTypeId() == TYPEID_PLAYER) { - TC_LOG_DEBUG("misc", "Player (GUID: %u) charge point (X: %f Y: %f Z: %f)", _owner->GetGUID().GetCounter(), x, y, z); + TC_LOG_DEBUG("misc", "Player (GUID: %u) charged point (X: %f Y: %f Z: %f).", _owner->GetGUID().GetCounter(), x, y, z); Mutate(new PointMovementGenerator(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED); } else { - TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) charge point (X: %f Y: %f Z: %f)", + TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) charged point (X: %f Y: %f Z: %f).", _owner->GetEntry(), _owner->GetGUID().GetCounter(), x, y, z); Mutate(new PointMovementGenerator(id, x, y, z, generatePath, speed), MOTION_SLOT_CONTROLLED); } @@ -501,7 +503,7 @@ void MotionMaster::MoveSeekAssistance(float x, float y, float z) { if (_owner->GetTypeId() == TYPEID_PLAYER) { - TC_LOG_ERROR("misc", "Player (GUID: %u) attempt to seek assistance", _owner->GetGUID().GetCounter()); + TC_LOG_ERROR("misc", "Player (GUID: %u) attempted to seek assistance.", _owner->GetGUID().GetCounter()); } else { @@ -517,11 +519,11 @@ void MotionMaster::MoveSeekAssistanceDistract(uint32 time) { if (_owner->GetTypeId() == TYPEID_PLAYER) { - TC_LOG_ERROR("misc", "Player (GUID: %u) attempt to call distract after assistance", _owner->GetGUID().GetCounter()); + TC_LOG_ERROR("misc", "Player (GUID: %u) attempted to call distract assistance.", _owner->GetGUID().GetCounter()); } else { - TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) is distracted after assistance call (Time: %u)", + TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) is distracted after assistance call (Time: %u).", _owner->GetEntry(), _owner->GetGUID().GetCounter(), time); Mutate(new AssistanceDistractMovementGenerator(time), MOTION_SLOT_ACTIVE); } @@ -534,14 +536,14 @@ void MotionMaster::MoveFleeing(Unit* enemy, uint32 time) if (_owner->GetTypeId() == TYPEID_PLAYER) { - TC_LOG_DEBUG("misc", "Player (GUID: %u) flee from %s (GUID: %u)", _owner->GetGUID().GetCounter(), + TC_LOG_DEBUG("misc", "Player (GUID: %u) flees from %s (GUID: %u).", _owner->GetGUID().GetCounter(), enemy->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", enemy->GetTypeId() == TYPEID_PLAYER ? enemy->GetGUID().GetCounter() : enemy->ToCreature()->GetSpawnId()); Mutate(new FleeingMovementGenerator(enemy->GetGUID()), MOTION_SLOT_CONTROLLED); } else { - TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) flee from %s (GUID: %u)%s", + TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) flees from %s (GUID: %u)%s.", _owner->GetEntry(), _owner->GetGUID().GetCounter(), enemy->GetTypeId() == TYPEID_PLAYER ? "player" : "creature", enemy->GetTypeId() == TYPEID_PLAYER ? enemy->GetGUID().GetCounter() : enemy->ToCreature()->GetSpawnId(), @@ -559,20 +561,20 @@ void MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode) { if (path < sTaxiPathNodesByPath.size()) { - TC_LOG_DEBUG("misc", "%s taxi to (Path %u node %u)", _owner->GetName().c_str(), path, pathnode); + TC_LOG_DEBUG("misc", "%s taxi to (Path %u node %u).", _owner->GetName().c_str(), path, pathnode); FlightPathMovementGenerator* mgen = new FlightPathMovementGenerator(pathnode); mgen->LoadPath(_owner->ToPlayer()); Mutate(mgen, MOTION_SLOT_CONTROLLED); } else { - TC_LOG_ERROR("misc", "%s attempt taxi to (not existed Path %u node %u)", + TC_LOG_ERROR("misc", "%s attempted taxi to (non-existing Path %u node %u).", _owner->GetName().c_str(), path, pathnode); } } else { - TC_LOG_ERROR("misc", "Creature (Entry: %u GUID: %u) attempt taxi to (Path %u node %u)", + TC_LOG_ERROR("misc", "Creature (Entry: %u GUID: %u) attempted taxi to (Path %u node %u).", _owner->GetEntry(), _owner->GetGUID().GetCounter(), path, pathnode); } } @@ -584,11 +586,11 @@ void MotionMaster::MoveDistract(uint32 timer) if (_owner->GetTypeId() == TYPEID_PLAYER) { - TC_LOG_DEBUG("misc", "Player (GUID: %u) distracted (timer: %u)", _owner->GetGUID().GetCounter(), timer); + TC_LOG_DEBUG("misc", "Player (GUID: %u) distracted (timer: %u).", _owner->GetGUID().GetCounter(), timer); } else { - TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) (timer: %u)", + TC_LOG_DEBUG("misc", "Creature (Entry: %u GUID: %u) distracted (timer: %u)", _owner->GetEntry(), _owner->GetGUID().GetCounter(), timer); } @@ -640,7 +642,7 @@ void MotionMaster::MovePath(uint32 path_id, bool repeatable) //Mutate(new WaypointMovementGenerator(path_id, repeatable)): Mutate(new WaypointMovementGenerator(path_id, repeatable), MOTION_SLOT_IDLE); - TC_LOG_DEBUG("misc", "%s (GUID: %u) start moving over path(Id:%u, repeatable: %s)", + TC_LOG_DEBUG("misc", "%s (GUID: %u) starts moving over path(Id:%u, repeatable: %s).", _owner->GetTypeId() == TYPEID_PLAYER ? "Player" : "Creature", _owner->GetGUID().GetCounter(), path_id, repeatable ? "YES" : "NO"); } diff --git a/src/server/game/Movement/MotionMaster.h b/src/server/game/Movement/MotionMaster.h index ce0f1f7836e..76ae12986d5 100644 --- a/src/server/game/Movement/MotionMaster.h +++ b/src/server/game/Movement/MotionMaster.h @@ -181,9 +181,11 @@ class MotionMaster //: private std::stack void MoveCharge(PathGenerator const& path, float speed = SPEED_CHARGE); void MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ); void MoveJumpTo(float angle, float speedXY, float speedZ); - void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP) - { MoveJump(pos.m_positionX, pos.m_positionY, pos.m_positionZ, speedXY, speedZ, id); } - void MoveJump(float x, float y, float z, float speedXY, float speedZ, uint32 id = EVENT_JUMP); + void MoveJump(Position const& pos, float speedXY, float speedZ, uint32 id = EVENT_JUMP, bool hasOrientation = false) + { + MoveJump(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), speedXY, speedZ, id, hasOrientation); + } + void MoveJump(float x, float y, float z, float o, float speedXY, float speedZ, uint32 id = EVENT_JUMP, bool hasOrientation = false); void MoveCirclePath(float x, float y, float z, float radius, bool clockwise, uint8 stepCount); void MoveSmoothPath(uint32 pointId, G3D::Vector3 const* pathPoints, size_t pathSize, bool walk); void MoveFall(uint32 id = 0); diff --git a/src/server/game/OutdoorPvP/OutdoorPvP.cpp b/src/server/game/OutdoorPvP/OutdoorPvP.cpp index 3f02fdedddf..9e1c6a94e88 100644 --- a/src/server/game/OutdoorPvP/OutdoorPvP.cpp +++ b/src/server/game/OutdoorPvP/OutdoorPvP.cpp @@ -689,7 +689,7 @@ void OutdoorPvP::BroadcastWorker(Worker& _worker, uint32 zoneId) void OutdoorPvP::SetMapFromZone(uint32 zone) { - AreaTableEntry const* areaTable = GetAreaEntryByAreaID(zone); + AreaTableEntry const* areaTable = sAreaTableStore.LookupEntry(zone); ASSERT(areaTable); Map* map = sMapMgr->CreateBaseMap(areaTable->mapid); ASSERT(!map->Instanceable()); diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index c1de9b654e8..04af0929818 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -37,7 +37,6 @@ // namespace // { - UnusedScriptContainer UnusedScripts; UnusedScriptNamesContainer UnusedScriptNames; // } @@ -107,8 +106,9 @@ class ScriptRegistry // The actual list of scripts. This will be accessed concurrently, so it must not be modified // after server startup. static ScriptMap ScriptPointerList; + static std::vector Scripts; - static void AddScript(TScript* const script) + static void AddScript(TScript* const script, bool addToDeleteContainer = true) { ASSERT(script); @@ -126,6 +126,8 @@ class ScriptRegistry } AddScript(is_script_database_bound{}, script); + if (addToDeleteContainer) + Scripts.push_back(script); } // Gets a script by its ID (assigned by ObjectMgr). @@ -186,11 +188,6 @@ class ScriptRegistry { // The script uses a script name from database, but isn't assigned to anything. TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", script->GetName().c_str()); - - // Avoid calling "delete script;" because we are currently in the script constructor - // In a valid scenario this will not happen because every script has a name assigned in the database - UnusedScripts.push_back(script); - return; } } @@ -210,6 +207,7 @@ class ScriptRegistry #define SCR_REG_MAP(T) ScriptRegistry::ScriptMap #define SCR_REG_ITR(T) ScriptRegistry::ScriptMapIterator #define SCR_REG_LST(T) ScriptRegistry::ScriptPointerList +#define SCR_REG_VEC(T) ScriptRegistry::Scripts // Utility macros for looping over scripts. #define FOR_SCRIPTS(T, C, E) \ @@ -266,17 +264,15 @@ void ScriptMgr::Initialize() } #endif - UnloadUnusedScripts(); - TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime)); } void ScriptMgr::Unload() { #define SCR_CLEAR(T) \ - for (SCR_REG_ITR(T) itr = SCR_REG_LST(T).begin(); itr != SCR_REG_LST(T).end(); ++itr) \ - delete itr->second; \ - SCR_REG_LST(T).clear(); + for (T* scr : SCR_REG_VEC(T)) \ + delete scr; \ + SCR_REG_VEC(T).clear(); // Clear scripts for every script type. SCR_CLEAR(SpellScriptLoader); @@ -308,19 +304,10 @@ void ScriptMgr::Unload() #undef SCR_CLEAR - UnloadUnusedScripts(); - delete[] SpellSummary; delete[] UnitAI::AISpellInfo; } -void ScriptMgr::UnloadUnusedScripts() -{ - for (size_t i = 0; i < UnusedScripts.size(); ++i) - delete UnusedScripts[i]; - UnusedScripts.clear(); -} - void ScriptMgr::LoadDatabase() { sScriptSystemMgr->LoadScriptWaypoints(); @@ -1368,9 +1355,9 @@ void ScriptMgr::OnPlayerSave(Player* player) FOREACH_SCRIPT(PlayerScript)->OnSave(player); } -void ScriptMgr::OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent) +void ScriptMgr::OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent, uint8 extendState) { - FOREACH_SCRIPT(PlayerScript)->OnBindToInstance(player, difficulty, mapid, permanent); + FOREACH_SCRIPT(PlayerScript)->OnBindToInstance(player, difficulty, mapid, permanent, extendState); } void ScriptMgr::OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea) @@ -1555,8 +1542,7 @@ FormulaScript::FormulaScript(const char* name) UnitScript::UnitScript(const char* name, bool addToScripts) : ScriptObject(name) { - if (addToScripts) - ScriptRegistry::AddScript(this); + ScriptRegistry::AddScript(this, addToScripts); } WorldMapScript::WorldMapScript(const char* name, uint32 mapId) @@ -1696,6 +1682,7 @@ GroupScript::GroupScript(const char* name) // Instantiate static members of ScriptRegistry. template std::map ScriptRegistry::ScriptPointerList; +template std::vector ScriptRegistry::Scripts; template uint32 ScriptRegistry::_scriptIdCounter = 0; // Specialize for each script type class like so: diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h index 367ee9cdb93..4ec0d9a9606 100644 --- a/src/server/game/Scripting/ScriptMgr.h +++ b/src/server/game/Scripting/ScriptMgr.h @@ -733,7 +733,7 @@ class PlayerScript : public UnitScript virtual void OnSave(Player* /*player*/) { } // Called when a player is bound to an instance - virtual void OnBindToInstance(Player* /*player*/, Difficulty /*difficulty*/, uint32 /*mapId*/, bool /*permanent*/) { } + virtual void OnBindToInstance(Player* /*player*/, Difficulty /*difficulty*/, uint32 /*mapId*/, bool /*permanent*/, uint8 /*extendState*/) { } // Called when a player switches to a new zone virtual void OnUpdateZone(Player* /*player*/, uint32 /*newZone*/, uint32 /*newArea*/) { } @@ -842,10 +842,7 @@ class GroupScript : public ScriptObject // namespace // { - typedef std::vector UnusedScriptContainer; typedef std::list UnusedScriptNamesContainer; - - extern UnusedScriptContainer UnusedScripts; extern UnusedScriptNamesContainer UnusedScriptNames; // } @@ -877,7 +874,6 @@ class ScriptMgr public: /* Unloading */ void Unload(); - void UnloadUnusedScripts(); public: /* SpellScriptLoader */ @@ -1053,7 +1049,7 @@ class ScriptMgr void OnPlayerDelete(ObjectGuid guid, uint32 accountId); void OnPlayerFailedDelete(ObjectGuid guid, uint32 accountId); void OnPlayerSave(Player* player); - void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent); + void OnPlayerBindToInstance(Player* player, Difficulty difficulty, uint32 mapid, bool permanent, uint8 extendState); void OnPlayerUpdateZone(Player* player, uint32 newZone, uint32 newArea); void OnQuestStatusChange(Player* player, uint32 questId, QuestStatus status); diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index e0b6eac81e7..ebab1955d24 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -25,6 +25,17 @@ #include "BattlenetAccountMgr.h" #include +class EncryptablePacket : public WorldPacket +{ +public: + EncryptablePacket(WorldPacket const& packet, bool encrypt) : WorldPacket(packet), _encrypt(encrypt) { } + + bool NeedsEncryption() const { return _encrypt; } + +private: + bool _encrypt; +}; + using boost::asio::ip::tcp; std::string const WorldSocket::ServerConnectionInitialize("WORLD OF WARCRAFT CONNECTION - SERVER TO CLIENT"); @@ -45,11 +56,8 @@ void WorldSocket::Start() stmt->setString(0, ip_address); stmt->setUInt32(1, inet_addr(ip_address.c_str())); - { - std::lock_guard guard(_queryLock); - _queryCallback = io_service().wrap(std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1)); - _queryFuture = LoginDatabase.AsyncQuery(stmt); - } + _queryCallback = std::bind(&WorldSocket::CheckIpCallback, this, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); } void WorldSocket::CheckIpCallback(PreparedQueryResult result) @@ -84,23 +92,59 @@ void WorldSocket::CheckIpCallback(PreparedQueryResult result) initializer.Write(header.header, header.getHeaderLength() - 2); initializer.Write(ServerConnectionInitialize.c_str(), ServerConnectionInitialize.length()); - std::unique_lock dummy(_writeLock, std::defer_lock); - QueuePacket(std::move(initializer), dummy); + // - io_service.run thread, safe. + QueuePacket(std::move(initializer)); } bool WorldSocket::Update() { + EncryptablePacket* queued; + MessageBuffer buffer; + while (_bufferQueue.Dequeue(queued)) + { + if (_worldSession && queued->size() > 0x400 && !queued->IsCompressed()) + queued->Compress(_worldSession->GetCompressionStream()); + + ServerPktHeader header(queued->size() + 2, queued->GetOpcode()); + if (queued->NeedsEncryption()) + _authCrypt.EncryptSend(header.header, header.getHeaderLength()); + + if (buffer.GetRemainingSpace() < queued->size() + header.getHeaderLength()) + { + QueuePacket(std::move(buffer)); + buffer.Resize(4096); + } + + if (buffer.GetRemainingSpace() >= queued->size() + header.getHeaderLength()) + { + buffer.Write(header.header, header.getHeaderLength()); + if (!queued->empty()) + buffer.Write(queued->contents(), queued->size()); + } + else // single packet larger than 4096 bytes + { + MessageBuffer packetBuffer(queued->size() + header.getHeaderLength()); + packetBuffer.Write(header.header, header.getHeaderLength()); + if (!queued->empty()) + packetBuffer.Write(queued->contents(), queued->size()); + + QueuePacket(std::move(packetBuffer)); + } + + delete queued; + } + + if (buffer.GetActiveSize() > 0) + QueuePacket(std::move(buffer)); + if (!BaseSocket::Update()) return false; + if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - std::lock_guard guard(_queryLock); - if (_queryFuture.valid() && _queryFuture.wait_for(std::chrono::seconds(0)) == std::future_status::ready) - { - auto callback = std::move(_queryCallback); - _queryCallback = nullptr; - callback(_queryFuture.get()); - } + auto callback = _queryCallback; + _queryCallback = nullptr; + callback(_queryFuture.get()); } return true; @@ -418,32 +462,7 @@ void WorldSocket::SendPacket(WorldPacket& packet) if (sPacketLog->CanLogPacket()) sPacketLog->LogPacket(packet, SERVER_TO_CLIENT, GetRemoteIpAddress(), GetRemotePort()); - if (_worldSession && packet.size() > 0x400 && !packet.IsCompressed()) - packet.Compress(_worldSession->GetCompressionStream()); - - ServerPktHeader header(packet.size() + 2, packet.GetOpcode()); - - std::unique_lock guard(_writeLock); - - _authCrypt.EncryptSend(header.header, header.getHeaderLength()); - -#ifndef TC_SOCKET_USE_IOCP - if (_writeQueue.empty() && _writeBuffer.GetRemainingSpace() >= header.getHeaderLength() + packet.size()) - { - _writeBuffer.Write(header.header, header.getHeaderLength()); - if (!packet.empty()) - _writeBuffer.Write(packet.contents(), packet.size()); - } - else -#endif - { - MessageBuffer buffer(header.getHeaderLength() + packet.size()); - buffer.Write(header.header, header.getHeaderLength()); - if (!packet.empty()) - buffer.Write(packet.contents(), packet.size()); - - QueuePacket(std::move(buffer), guard); - } + _bufferQueue.Enqueue(new EncryptablePacket(packet, _authCrypt.IsInitialized())); } void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) @@ -497,11 +516,8 @@ void WorldSocket::HandleAuthSession(WorldPacket& recvPacket) stmt->setInt32(0, realmHandle.Index); stmt->setString(1, authSession->Account); - { - std::lock_guard guard(_queryLock); - _queryCallback = io_service().wrap(std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1)); - _queryFuture = LoginDatabase.AsyncQuery(stmt); - } + _queryCallback = std::bind(&WorldSocket::HandleAuthSessionCallback, this, authSession, std::placeholders::_1); + _queryFuture = LoginDatabase.AsyncQuery(stmt); } void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSession, PreparedQueryResult result) @@ -655,7 +671,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr authSes if (wardenActive) _worldSession->InitWarden(&account.Game.SessionKey, account.BattleNet.OS); - _queryCallback = io_service().wrap(std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1)); + _queryCallback = std::bind(&WorldSocket::LoadSessionPermissionsCallback, this, std::placeholders::_1); _queryFuture = _worldSession->LoadPermissionsAsync(); AsyncRead(); } diff --git a/src/server/game/Server/WorldSocket.h b/src/server/game/Server/WorldSocket.h index 91a6c697932..a81f0d83773 100644 --- a/src/server/game/Server/WorldSocket.h +++ b/src/server/game/Server/WorldSocket.h @@ -26,11 +26,13 @@ #include "Util.h" #include "WorldPacket.h" #include "WorldSession.h" +#include "MPSCQueue.h" #include #include #include using boost::asio::ip::tcp; +class EncryptablePacket; #pragma pack(push, 1) @@ -107,10 +109,10 @@ private: MessageBuffer _headerBuffer; MessageBuffer _packetBuffer; + MPSCQueue _bufferQueue; bool _initialized; - std::mutex _queryLock; PreparedQueryResultFuture _queryFuture; std::function _queryCallback; std::string _ipCountry; diff --git a/src/server/game/Server/WorldSocketMgr.cpp b/src/server/game/Server/WorldSocketMgr.cpp index 529396b3966..e8f8c59f4af 100644 --- a/src/server/game/Server/WorldSocketMgr.cpp +++ b/src/server/game/Server/WorldSocketMgr.cpp @@ -24,9 +24,9 @@ #include -static void OnSocketAccept(tcp::socket&& sock) +static void OnSocketAccept(tcp::socket&& sock, uint32 threadIndex) { - sWorldSocketMgr.OnSocketOpen(std::forward(sock)); + sWorldSocketMgr.OnSocketOpen(std::forward(sock), threadIndex); } class WorldSocketThread : public NetworkThread @@ -67,7 +67,9 @@ bool WorldSocketMgr::StartNetwork(boost::asio::io_service& service, std::string BaseSocketMgr::StartNetwork(service, bindIp, port); - _acceptor->AsyncAcceptManaged(&OnSocketAccept); + _acceptor->SetSocketFactory(std::bind(&BaseSocketMgr::GetSocketForAccept, this)); + + _acceptor->AsyncAcceptWithCallback<&OnSocketAccept>(); sScriptMgr->OnNetworkStart(); return true; @@ -80,7 +82,7 @@ void WorldSocketMgr::StopNetwork() sScriptMgr->OnNetworkStop(); } -void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock) +void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) { // set some options here if (_socketSendBufferSize >= 0) @@ -108,7 +110,7 @@ void WorldSocketMgr::OnSocketOpen(tcp::socket&& sock) //sock->m_OutBufferSize = static_cast (m_SockOutUBuff); - BaseSocketMgr::OnSocketOpen(std::forward(sock)); + BaseSocketMgr::OnSocketOpen(std::forward(sock), threadIndex); } NetworkThread* WorldSocketMgr::CreateThreads() const diff --git a/src/server/game/Server/WorldSocketMgr.h b/src/server/game/Server/WorldSocketMgr.h index 92a28d0c135..38e2e7abb69 100644 --- a/src/server/game/Server/WorldSocketMgr.h +++ b/src/server/game/Server/WorldSocketMgr.h @@ -47,7 +47,7 @@ public: /// Stops all network threads, It will wait for all running threads . void StopNetwork() override; - void OnSocketOpen(tcp::socket&& sock) override; + void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) override; protected: WorldSocketMgr(); diff --git a/src/server/game/Skills/SkillDiscovery.cpp b/src/server/game/Skills/SkillDiscovery.cpp index 36a7d147192..8860b391f48 100644 --- a/src/server/game/Skills/SkillDiscovery.cpp +++ b/src/server/game/Skills/SkillDiscovery.cpp @@ -88,7 +88,7 @@ void LoadSkillDiscoveryTable() { if (reportedReqSpells.find(absReqSkillOrSpell) == reportedReqSpells.end()) { - TC_LOG_ERROR("sql.sql", "Spell (ID: %u) have not existed spell (ID: %i) in `reqSpell` field in `skill_discovery_template` table", spellId, reqSkillOrSpell); + TC_LOG_ERROR("sql.sql", "Spell (ID: %u) has a non-existing spell (ID: %i) in `reqSpell` field in the `skill_discovery_template` table.", spellId, reqSkillOrSpell); reportedReqSpells.insert(absReqSkillOrSpell); } continue; @@ -101,8 +101,8 @@ void LoadSkillDiscoveryTable() { if (reportedReqSpells.find(absReqSkillOrSpell) == reportedReqSpells.end()) { - TC_LOG_ERROR("sql.sql", "Spell (ID: %u) not have MECHANIC_DISCOVERY (28) value in Mechanic field in spell.dbc" - " and not 100%% chance random discovery ability but listed for spellId %u (and maybe more) in `skill_discovery_template` table", + TC_LOG_ERROR("sql.sql", "Spell (ID: %u) does not have any MECHANIC_DISCOVERY (28) value in the Mechanic field in spell.dbc" + " nor 100%% chance random discovery ability, but is listed for spellId %u (and maybe more) in the `skill_discovery_template` table.", absReqSkillOrSpell, spellId); reportedReqSpells.insert(absReqSkillOrSpell); } @@ -117,7 +117,7 @@ void LoadSkillDiscoveryTable() if (bounds.first == bounds.second) { - TC_LOG_ERROR("sql.sql", "Spell (ID: %u) not listed in `SkillLineAbility.dbc` but listed with `reqSpell`=0 in `skill_discovery_template` table", spellId); + TC_LOG_ERROR("sql.sql", "Spell (ID: %u) is not listed in `SkillLineAbility.dbc`, but listed with `reqSpell`= 0 in the `skill_discovery_template` table.", spellId); continue; } @@ -126,7 +126,7 @@ void LoadSkillDiscoveryTable() } else { - TC_LOG_ERROR("sql.sql", "Spell (ID: %u) have negative value in `reqSpell` field in `skill_discovery_template` table", spellId); + TC_LOG_ERROR("sql.sql", "Spell (ID: %u) has a negative value in `reqSpell` field in the `skill_discovery_template` table.", spellId); continue; } @@ -135,7 +135,7 @@ void LoadSkillDiscoveryTable() while (result->NextRow()); if (!ssNonDiscoverableEntries.str().empty()) - TC_LOG_ERROR("sql.sql", "Some items can't be successfully discovered: have in chance field value < 0.000001 in `skill_discovery_template` DB table . List:\n%s", ssNonDiscoverableEntries.str().c_str()); + TC_LOG_ERROR("sql.sql", "Some items can't be successfully discovered, their chance field value is < 0.000001 in the `skill_discovery_template` DB table. List:\n%s", ssNonDiscoverableEntries.str().c_str()); // report about empty data for explicit discovery spells for (uint32 spell_id = 1; spell_id < sSpellMgr->GetSpellInfoStoreSize(); ++spell_id) @@ -149,10 +149,10 @@ void LoadSkillDiscoveryTable() continue; if (SkillDiscoveryStore.find(int32(spell_id)) == SkillDiscoveryStore.end()) - TC_LOG_ERROR("sql.sql", "Spell (ID: %u) is 100%% chance random discovery ability but not have data in `skill_discovery_template` table", spell_id); + TC_LOG_ERROR("sql.sql", "Spell (ID: %u) has got 100%% chance random discovery ability, but does not have data in the `skill_discovery_template` table.", spell_id); } - TC_LOG_INFO("server.loading", ">> Loaded %u skill discovery definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u skill discovery definitions in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player) diff --git a/src/server/game/Skills/SkillExtraItems.cpp b/src/server/game/Skills/SkillExtraItems.cpp index 5213944cc90..f76e4623137 100644 --- a/src/server/game/Skills/SkillExtraItems.cpp +++ b/src/server/game/Skills/SkillExtraItems.cpp @@ -74,28 +74,28 @@ void LoadSkillPerfectItemTable() if (!sSpellMgr->GetSpellInfo(spellId)) { - TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has non-existent spell id in `skill_perfect_item_template`!", spellId); + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has a non-existing spell id in the `skill_perfect_item_template`!", spellId); continue; } uint32 requiredSpecialization = fields[1].GetUInt32(); if (!sSpellMgr->GetSpellInfo(requiredSpecialization)) { - TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has non-existent required specialization spell id %u in `skill_perfect_item_template`!", spellId, requiredSpecialization); + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has a non-existing required specialization spell id %u in the `skill_perfect_item_template`!", spellId, requiredSpecialization); continue; } float perfectCreateChance = fields[2].GetFloat(); if (perfectCreateChance <= 0.0f) { - TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has impossibly low proc chance in `skill_perfect_item_template`!", spellId); + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u has impossibly low proc chance in the `skill_perfect_item_template`!", spellId); continue; } uint32 perfectItemType = fields[3].GetUInt32(); if (!sObjectMgr->GetItemTemplate(perfectItemType)) { - TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u references non-existent perfect item id %u in `skill_perfect_item_template`!", spellId, perfectItemType); + TC_LOG_ERROR("sql.sql", "Skill perfection data for spell %u references a non-existing perfect item id %u in the `skill_perfect_item_template`!", spellId, perfectItemType); continue; } @@ -109,7 +109,7 @@ void LoadSkillPerfectItemTable() } while (result->NextRow()); - TC_LOG_INFO("server.loading", ">> Loaded %u spell perfection definitions in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); + TC_LOG_INFO("server.loading", ">> Loaded %u spell perfection definitions in %u ms.", count, GetMSTimeDiffToNow(oldMSTime)); } // struct to store information about extra item creation @@ -161,28 +161,28 @@ void LoadSkillExtraItemTable() if (!sSpellMgr->GetSpellInfo(spellId)) { - TC_LOG_ERROR("sql.sql", "Skill specialization %u has non-existent spell id in `skill_extra_item_template`!", spellId); + TC_LOG_ERROR("sql.sql", "Skill specialization %u has a non-existing spell id in the `skill_extra_item_template`!", spellId); continue; } uint32 requiredSpecialization = fields[1].GetUInt32(); if (!sSpellMgr->GetSpellInfo(requiredSpecialization)) { - TC_LOG_ERROR("sql.sql", "Skill specialization %u have not existed required specialization spell id %u in `skill_extra_item_template`!", spellId, requiredSpecialization); + TC_LOG_ERROR("sql.sql", "Skill specialization %u has a non-existing required specialization spell id %u in the `skill_extra_item_template`!", spellId, requiredSpecialization); continue; } float additionalCreateChance = fields[2].GetFloat(); if (additionalCreateChance <= 0.0f) { - TC_LOG_ERROR("sql.sql", "Skill specialization %u has too low additional create chance in `skill_extra_item_template`!", spellId); + TC_LOG_ERROR("sql.sql", "Skill specialization %u has too low additional create chance in the `skill_extra_item_template`!", spellId); continue; } uint8 additionalMaxNum = fields[3].GetUInt8(); if (!additionalMaxNum) { - TC_LOG_ERROR("sql.sql", "Skill specialization %u has 0 max number of extra items in `skill_extra_item_template`!", spellId); + TC_LOG_ERROR("sql.sql", "Skill specialization %u has 0 max number of extra items in the `skill_extra_item_template`!", spellId); continue; } diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 30aa889db22..fdfe45a1c2c 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -4702,11 +4702,6 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool if (caster) target->GetMotionMaster()->MoveFall(); break; - case 52916: // Honor Among Thieves - if (target->GetTypeId() == TYPEID_PLAYER) - if (Unit* spellTarget = ObjectAccessor::GetUnit(*target, target->ToPlayer()->GetComboTarget())) - target->CastSpell(spellTarget, 51699, true); - break; case 71563: if (Aura* newAura = target->AddAura(71564, target)) newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount); @@ -5626,25 +5621,6 @@ void AuraEffect::HandlePeriodicTriggerSpellAuraTick(Unit* target, Unit* caster) target->RemoveAurasDueToSpell(28820); return; } - // Totemic Mastery (Skyshatter Regalia (Shaman Tier 6) - bonus) - case 38443: - { - bool all = true; - for (int i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i) - { - if (!target->m_SummonSlot[i]) - { - all = false; - break; - } - } - - if (all) - target->CastSpell(target, 38437, true, NULL, this); - else - target->RemoveAurasDueToSpell(38437); - return; - } } break; } diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 0346bdcdc56..51cfdac8754 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -1404,7 +1404,7 @@ void Aura::HandleAuraSpecificMods(AuraApplication const* aurApp, Unit* caster, b { // This additional check is needed to add a minimal delay before cooldown in in effect // to allow all bubbles broken by a single damage source proc mana return - if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetSpellInfo()) <= 11) + if (caster->GetSpellHistory()->GetRemainingCooldown(aura->GetSpellInfo()) <= 11 * IN_MILLISECONDS) break; } else // and add if needed diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index 19c835b4b8b..76c5afb48bb 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -2435,7 +2435,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) if (missInfo != SPELL_MISS_EVADE && !m_caster->IsFriendlyTo(unit) && (!m_spellInfo->IsPositive() || m_spellInfo->HasEffect(SPELL_EFFECT_DISPEL))) { - m_caster->CombatStart(unit, !m_spellInfo->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)); + m_caster->CombatStart(unit, m_spellInfo->HasInitialAggro()); if (!unit->IsStandState()) unit->SetStandState(UNIT_STAND_STATE_STAND); @@ -2525,7 +2525,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit* unit, uint32 effectMask, bool scaleA if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->UpdatePvP(true); } - if (unit->IsInCombat() && !m_spellInfo->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)) + if (unit->IsInCombat() && m_spellInfo->HasInitialAggro()) { m_caster->SetInCombatState(unit->GetCombatTimer() > 0, unit); unit->getHostileRefManager().threatAssist(m_caster, 0.0f); @@ -4649,8 +4649,7 @@ void Spell::HandleThreatSpells() if (m_UniqueTargetInfo.empty()) return; - if (m_spellInfo->HasAttribute(SPELL_ATTR1_NO_THREAT) || - m_spellInfo->HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)) + if (!m_spellInfo->HasInitialAggro()) return; float threat = 0.0f; @@ -4661,7 +4660,7 @@ void Spell::HandleThreatSpells() threat += threatEntry->flatMod; } - else if (m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NO_INITIAL_THREAT) == 0) + else if (!m_spellInfo->HasAttribute(SPELL_ATTR0_CU_NO_INITIAL_THREAT)) threat += m_spellInfo->SpellLevel; // past this point only multiplicative effects occur @@ -5548,7 +5547,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_originalCaster && m_originalCaster->GetTypeId() == TYPEID_PLAYER && m_originalCaster->IsAlive()) { Battlefield* Bf = sBattlefieldMgr->GetBattlefieldToZoneId(m_originalCaster->GetZoneId()); - if (AreaTableEntry const* area = GetAreaEntryByAreaID(m_originalCaster->GetAreaId())) + if (AreaTableEntry const* area = sAreaTableStore.LookupEntry(m_originalCaster->GetAreaId())) if (area->flags & AREA_FLAG_NO_FLY_ZONE || (Bf && !Bf->CanFlyIn())) return (_triggeredCastFlags & TRIGGERED_DONT_REPORT_CAST_ERROR) ? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_NOT_HERE; } diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index d5926cf5589..7a2c0c7d496 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -751,7 +751,7 @@ void Spell::EffectTriggerMissileSpell(SpellEffIndex effIndex) SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(triggered_spell_id); if (!spellInfo) { - TC_LOG_ERROR("spells", "Spell::EffectTriggerMissileSpell spell %u tried to trigger unknown spell %u", m_spellInfo->Id, triggered_spell_id); + TC_LOG_ERROR("spells", "Spell::EffectTriggerMissileSpell spell %u tried to trigger unknown spell %u.", m_spellInfo->Id, triggered_spell_id); return; } @@ -802,7 +802,7 @@ void Spell::EffectForceCast(SpellEffIndex effIndex) if (!spellInfo) { - TC_LOG_ERROR("spells", "Spell::EffectForceCast of spell %u: triggering unknown spell id %i", m_spellInfo->Id, triggered_spell_id); + TC_LOG_ERROR("spells", "Spell::EffectForceCast of spell %u: triggering unknown spell id %i.", m_spellInfo->Id, triggered_spell_id); return; } @@ -854,7 +854,7 @@ void Spell::EffectTriggerRitualOfSummoning(SpellEffIndex effIndex) if (!spellInfo) { - TC_LOG_ERROR("spells", "EffectTriggerRitualOfSummoning of spell %u: triggering unknown spell id %i", m_spellInfo->Id, triggered_spell_id); + TC_LOG_ERROR("spells", "EffectTriggerRitualOfSummoning of spell %u: triggering unknown spell id %i.", m_spellInfo->Id, triggered_spell_id); return; } @@ -879,7 +879,7 @@ void Spell::EffectJump(SpellEffIndex effIndex) float speedXY, speedZ; CalculateJumpSpeeds(effIndex, m_caster->GetExactDist2d(x, y), speedXY, speedZ); - m_caster->GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ); + m_caster->GetMotionMaster()->MoveJump(x, y, z, 0.0f, speedXY, speedZ, EVENT_JUMP, false); } void Spell::EffectJumpDest(SpellEffIndex effIndex) @@ -893,13 +893,9 @@ void Spell::EffectJumpDest(SpellEffIndex effIndex) if (!m_targets.HasDst()) return; - // Init dest coordinates - float x, y, z; - destTarget->GetPosition(x, y, z); - float speedXY, speedZ; - CalculateJumpSpeeds(effIndex, m_caster->GetExactDist2d(x, y), speedXY, speedZ); - m_caster->GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ); + CalculateJumpSpeeds(effIndex, m_caster->GetExactDist2d(destTarget), speedXY, speedZ); + m_caster->GetMotionMaster()->MoveJump(*destTarget, speedXY, speedZ, EVENT_JUMP, true); } void Spell::CalculateJumpSpeeds(uint8 i, float dist, float & speedXY, float & speedZ) @@ -924,7 +920,7 @@ void Spell::EffectTeleportUnits(SpellEffIndex /*effIndex*/) // If not exist data for dest location - return if (!m_targets.HasDst()) { - TC_LOG_ERROR("spells", "Spell::EffectTeleportUnits - does not have destination for spellId %u.", m_spellInfo->Id); + TC_LOG_ERROR("spells", "Spell::EffectTeleportUnits - does not have a destination for spellId %u.", m_spellInfo->Id); return; } @@ -1245,7 +1241,7 @@ void Spell::EffectHeal(SpellEffIndex /*effIndex*/) if (!targetAura) { - TC_LOG_ERROR("spells", "Target (%s) has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUID().ToString().c_str()); + TC_LOG_ERROR("spells", "Target (%s) has the aurastate AURA_STATE_SWIFTMEND, but no matching aura.", unitTarget->GetGUID().ToString().c_str()); return; } @@ -1721,7 +1717,7 @@ void Spell::SendLoot(ObjectGuid guid, LootType loottype) // Players shouldn't be able to loot gameobjects that are currently despawned if (!gameObjTarget->isSpawned() && !player->IsGameMaster()) { - TC_LOG_ERROR("spells", "Possible hacking attempt: Player %s [guid: %u] tried to loot a gameobject [entry: %u id: %u] which is on respawn time without being in GM mode!", + TC_LOG_ERROR("spells", "Possible hacking attempt: Player %s [guid: %u] tried to loot a gameobject [entry: %u id: %u] which is on respawn timer without being in GM mode!", player->GetName().c_str(), player->GetGUID().GetCounter(), gameObjTarget->GetEntry(), gameObjTarget->GetGUID().GetCounter()); return; } @@ -2026,7 +2022,7 @@ void Spell::EffectSummonType(SpellEffIndex effIndex) SummonPropertiesEntry const* properties = sSummonPropertiesStore.LookupEntry(m_spellInfo->Effects[effIndex].MiscValueB); if (!properties) { - TC_LOG_ERROR("spells", "EffectSummonType: Unhandled summon type %u", m_spellInfo->Effects[effIndex].MiscValueB); + TC_LOG_ERROR("spells", "EffectSummonType: Unhandled summon type %u.", m_spellInfo->Effects[effIndex].MiscValueB); return; } @@ -2214,7 +2210,7 @@ void Spell::EffectLearnSpell(SpellEffIndex effIndex) uint32 spellToLearn = (m_spellInfo->Id == 483 || m_spellInfo->Id == 55884) ? damage : m_spellInfo->Effects[effIndex].TriggerSpell; player->LearnSpell(spellToLearn, false); - TC_LOG_DEBUG("spells", "Spell: Player %u has learned spell %u from NpcGUID=%u", player->GetGUID().GetCounter(), spellToLearn, m_caster->GetGUID().GetCounter()); + TC_LOG_DEBUG("spells", "Spell: Player %u has learned spell %u from NpcGUID: %u", player->GetGUID().GetCounter(), spellToLearn, m_caster->GetGUID().GetCounter()); } void Spell::EffectDispel(SpellEffIndex effIndex) @@ -2577,7 +2573,7 @@ void Spell::EffectEnchantItemPrismatic(SpellEffIndex effIndex) } if (!add_socket) { - TC_LOG_ERROR("spells", "Spell::EffectEnchantItemPrismatic: attempt apply enchant spell %u with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC (%u) but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET (%u), not suppoted yet.", + TC_LOG_ERROR("spells", "Spell::EffectEnchantItemPrismatic: attempt to apply the enchant spell %u with SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC (%u), but without ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET (%u), not supported yet.", m_spellInfo->Id, SPELL_EFFECT_ENCHANT_ITEM_PRISMATIC, ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET); return; } @@ -2624,14 +2620,14 @@ void Spell::EffectEnchantItemTmp(SpellEffIndex effIndex) if (!enchant_id) { - TC_LOG_ERROR("spells", "Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have 0 as enchanting id", m_spellInfo->Id, effIndex); + TC_LOG_ERROR("spells", "Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) has enchanting id 0.", m_spellInfo->Id, effIndex); return; } SpellItemEnchantmentEntry const* pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id); if (!pEnchant) { - TC_LOG_ERROR("spells", "Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) have not existed enchanting id %u ", m_spellInfo->Id, effIndex, enchant_id); + TC_LOG_ERROR("spells", "Spell %u Effect %u (SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY) has a non-existing enchanting id %u ", m_spellInfo->Id, effIndex, enchant_id); return; } @@ -3681,7 +3677,7 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) case 31893: spell_heal = 48084; break; case 31883: spell_heal = 48085; break; default: - TC_LOG_ERROR("spells", "Unknown Lightwell spell caster %u", m_caster->GetEntry()); + TC_LOG_ERROR("spells", "Unknown Lightwell spell caster %u.", m_caster->GetEntry()); return; } @@ -3834,14 +3830,14 @@ void Spell::EffectDuel(SpellEffIndex effIndex) return; // Players can only fight a duel in zones with this flag - AreaTableEntry const* casterAreaEntry = GetAreaEntryByAreaID(caster->GetAreaId()); + AreaTableEntry const* casterAreaEntry = sAreaTableStore.LookupEntry(caster->GetAreaId()); if (casterAreaEntry && !(casterAreaEntry->flags & AREA_FLAG_ALLOW_DUELS)) { SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here return; } - AreaTableEntry const* targetAreaEntry = GetAreaEntryByAreaID(target->GetAreaId()); + AreaTableEntry const* targetAreaEntry = sAreaTableStore.LookupEntry(target->GetAreaId()); if (targetAreaEntry && !(targetAreaEntry->flags & AREA_FLAG_ALLOW_DUELS)) { SendCastResult(SPELL_FAILED_NO_DUELING); // Dueling isn't allowed here @@ -3922,7 +3918,7 @@ void Spell::EffectStuck(SpellEffIndex /*effIndex*/) return; TC_LOG_DEBUG("spells", "Spell Effect: Stuck"); - TC_LOG_DEBUG("spells", "Player %s (guid %u) used auto-unstuck future at map %u (%f, %f, %f)", player->GetName().c_str(), player->GetGUID().GetCounter(), player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); + TC_LOG_DEBUG("spells", "Player %s (guid %u) used the auto-unstuck feature at map %u (%f, %f, %f).", player->GetName().c_str(), player->GetGUID().GetCounter(), player->GetMapId(), player->GetPositionX(), player->GetPositionY(), player->GetPositionZ()); if (player->IsInFlight()) return; @@ -4632,7 +4628,7 @@ void Spell::EffectPullTowards(SpellEffIndex effIndex) float speedXY = float(m_spellInfo->Effects[effIndex].MiscValue) * 0.1f; float speedZ = unitTarget->GetDistance(pos) / speedXY * 0.5f * Movement::gravity; - unitTarget->GetMotionMaster()->MoveJump(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), speedXY, speedZ); + unitTarget->GetMotionMaster()->MoveJump(pos, speedXY, speedZ); } void Spell::EffectDispelMechanic(SpellEffIndex effIndex) @@ -4834,7 +4830,7 @@ void Spell::EffectTransmitted(SpellEffIndex effIndex) if (!goinfo) { - TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u) not exist and not created at spell (ID: %u) cast", name_id, m_spellInfo->Id); + TC_LOG_ERROR("sql.sql", "Gameobject (Entry: %u) does not exist and is not created by spell (ID: %u) cast.", name_id, m_spellInfo->Id); return; } @@ -5484,7 +5480,7 @@ void Spell::EffectPlayMusic(SpellEffIndex effIndex) if (!sSoundEntriesStore.LookupEntry(soundid)) { - TC_LOG_ERROR("spells", "EffectPlayMusic: Sound (Id: %u) not exist in spell %u.", soundid, m_spellInfo->Id); + TC_LOG_ERROR("spells", "EffectPlayMusic: Sound (Id: %u) does not exist in spell %u.", soundid, m_spellInfo->Id); return; } @@ -5541,7 +5537,7 @@ void Spell::EffectPlaySound(SpellEffIndex effIndex) if (!sSoundEntriesStore.LookupEntry(soundId)) { - TC_LOG_ERROR("spells", "EffectPlaySound: Sound (Id: %u) not exist in spell %u.", soundId, m_spellInfo->Id); + TC_LOG_ERROR("spells", "EffectPlaySound: Sound (Id: %u) does not exist in spell %u.", soundId, m_spellInfo->Id); return; } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 6a7a205a237..22f5f0a1c59 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -1349,6 +1349,11 @@ bool SpellInfo::IsAutoRepeatRangedSpell() const return HasAttribute(SPELL_ATTR2_AUTOREPEAT_FLAG); } +bool SpellInfo::HasInitialAggro() const +{ + return !(HasAttribute(SPELL_ATTR1_NO_THREAT) || HasAttribute(SPELL_ATTR3_NO_INITIAL_AGGRO)); +} + bool SpellInfo::IsAffectedBySpellMods() const { return !HasAttribute(SPELL_ATTR3_NO_DONE_BONUS); diff --git a/src/server/game/Spells/SpellInfo.h b/src/server/game/Spells/SpellInfo.h index e9db0fca1e3..e2e25a6b0fb 100644 --- a/src/server/game/Spells/SpellInfo.h +++ b/src/server/game/Spells/SpellInfo.h @@ -474,6 +474,7 @@ public: bool IsBreakingStealth() const; bool IsRangedWeaponSpell() const; bool IsAutoRepeatRangedSpell() const; + bool HasInitialAggro() const; bool IsAffectedBySpellMods() const; bool IsAffectedBySpellMod(SpellModifier const* mod) const; diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 32847f87e49..49423b7ce6e 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -379,29 +379,29 @@ bool SpellMgr::IsSpellValid(SpellInfo const* spellInfo, Player* player, bool msg { if (spellInfo->Effects[i].ItemType == 0) { - // skip auto-loot crafting spells, its not need explicit item info (but have special fake items sometime) + // skip auto-loot crafting spells, it does not need explicit item info (but has special fake items sometimes). if (!spellInfo->IsLootCrafting()) { if (msg) { if (player) - ChatHandler(player->GetSession()).PSendSysMessage("Craft spell %u not have create item entry.", spellInfo->Id); + ChatHandler(player->GetSession()).PSendSysMessage("The craft spell %u does not have a create item entry.", spellInfo->Id); else - TC_LOG_ERROR("sql.sql", "Craft spell %u not have create item entry.", spellInfo->Id); + TC_LOG_ERROR("sql.sql", "The craft spell %u does not have a create item entry.", spellInfo->Id); } return false; } } - // also possible IsLootCrafting case but fake item must exist anyway + // also possible IsLootCrafting case but fake items must exist anyway else if (!sObjectMgr->GetItemTemplate(spellInfo->Effects[i].ItemType)) { if (msg) { if (player) - ChatHandler(player->GetSession()).PSendSysMessage("Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType); + ChatHandler(player->GetSession()).PSendSysMessage("Craft spell %u has created a non-existing item in DB (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType); else - TC_LOG_ERROR("sql.sql", "Craft spell %u create not-exist in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType); + TC_LOG_ERROR("sql.sql", "Craft spell %u has created a non-existing item in DB (Entry: %u) and then...", spellInfo->Id, spellInfo->Effects[i].ItemType); } return false; } @@ -437,9 +437,9 @@ bool SpellMgr::IsSpellValid(SpellInfo const* spellInfo, Player* player, bool msg if (msg) { if (player) - ChatHandler(player->GetSession()).PSendSysMessage("Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]); + ChatHandler(player->GetSession()).PSendSysMessage("Craft spell %u refers a non-existing reagent in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]); else - TC_LOG_ERROR("sql.sql", "Craft spell %u have not-exist reagent in DB item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]); + TC_LOG_ERROR("sql.sql", "Craft spell %u refers to a non-existing reagent in DB, item (Entry: %u) and then...", spellInfo->Id, spellInfo->Reagent[j]); } return false; } @@ -458,7 +458,7 @@ uint32 SpellMgr::GetSpellDifficultyId(uint32 spellId) const void SpellMgr::SetSpellDifficultyId(uint32 spellId, uint32 id) { if (uint32 i = GetSpellDifficultyId(spellId)) - TC_LOG_ERROR("spells", "SpellMgr::SetSpellDifficultyId: Spell %u has already spellDifficultyId %u. Will override with spellDifficultyId %u.", spellId, i, id); + TC_LOG_ERROR("spells", "SpellMgr::SetSpellDifficultyId: The spell %u already has spellDifficultyId %u. Will override with spellDifficultyId %u.", spellId, i, id); mSpellDifficultySearcherMap[spellId] = id; } @@ -473,7 +473,7 @@ uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit const* caster) con uint32 mode = uint32(caster->GetMap()->GetSpawnMode()); if (mode >= MAX_DIFFICULTY) { - TC_LOG_ERROR("spells", "SpellMgr::GetSpellIdForDifficulty: Incorrect Difficulty for spell %u.", spellId); + TC_LOG_ERROR("spells", "SpellMgr::GetSpellIdForDifficulty: Incorrect difficulty for spell %u.", spellId); return spellId; //return source spell } @@ -484,7 +484,7 @@ uint32 SpellMgr::GetSpellIdForDifficulty(uint32 spellId, Unit const* caster) con SpellDifficultyEntry const* difficultyEntry = sSpellDifficultyStore.LookupEntry(difficultyId); if (!difficultyEntry) { - TC_LOG_ERROR("spells", "SpellMgr::GetSpellIdForDifficulty: SpellDifficultyEntry not found for spell %u. This should never happen.", spellId); + TC_LOG_ERROR("spells", "SpellMgr::GetSpellIdForDifficulty: SpellDifficultyEntry was not found for spell %u. This should never happen.", spellId); return spellId; //return source spell } @@ -877,7 +877,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, Spell // For melee triggers if (procSpell == NULL) { - // Check (if set) for school (melee attack have Normal school) + // Check (if set) for school (melee attack has Normal school) if (spellProcEvent->schoolMask && (spellProcEvent->schoolMask & SPELL_SCHOOL_MASK_NORMAL) == 0) return false; } @@ -897,7 +897,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellInfo const* spellProto, Spell if (!(spellProcEvent->spellFamilyMask & procSpell->SpellFamilyFlags)) return false; hasFamilyMask = true; - // Some spells are not considered as active even with have spellfamilyflags + // Some spells are not considered as active even with spellfamilyflags set if (!(procEvent_procEx & PROC_EX_ONLY_ACTIVE_SPELL)) active = true; } @@ -1124,27 +1124,27 @@ SpellAreaForAreaMapBounds SpellMgr::GetSpellAreaForAreaMapBounds(uint32 area_id) bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const { - if (gender != GENDER_NONE) // not in expected gender + if (gender != GENDER_NONE) // is not expected gender if (!player || gender != player->getGender()) return false; - if (raceMask) // not in expected race + if (raceMask) // is not expected race if (!player || !(raceMask & player->getRaceMask())) return false; - if (areaId) // not in expected zone + if (areaId) // is not in expected zone if (newZone != areaId && newArea != areaId) return false; - if (questStart) // not in expected required quest state + if (questStart) // is not in expected required quest state if (!player || (((1 << player->GetQuestStatus(questStart)) & questStartStatus) == 0)) return false; - if (questEnd) // not in expected forbidden quest state + if (questEnd) // is not in expected forbidden quest state if (!player || (((1 << player->GetQuestStatus(questEnd)) & questEndStatus) == 0)) return false; - if (auraSpell) // not have expected aura + if (auraSpell) // does not have expected aura if (!player || (auraSpell > 0 && !player->HasAura(auraSpell)) || (auraSpell < 0 && player->HasAura(-auraSpell))) return false; @@ -1332,7 +1332,7 @@ void SpellMgr::LoadSpellRanks() SpellInfo const* first = GetSpellInfo(lastSpell); if (!first) { - TC_LOG_ERROR("sql.sql", "Spell rank identifier(first_spell_id) %u listed in `spell_ranks` does not exist!", lastSpell); + TC_LOG_ERROR("sql.sql", "The spell rank identifier(first_spell_id) %u listed in `spell_ranks` does not exist!", lastSpell); continue; } // check if chain is long enough @@ -1349,14 +1349,14 @@ void SpellMgr::LoadSpellRanks() SpellInfo const* spell = GetSpellInfo(itr->first); if (!spell) { - TC_LOG_ERROR("sql.sql", "Spell %u (rank %u) listed in `spell_ranks` for chain %u does not exist!", itr->first, itr->second, lastSpell); + TC_LOG_ERROR("sql.sql", "The spell %u (rank %u) listed in `spell_ranks` for chain %u does not exist!", itr->first, itr->second, lastSpell); valid = false; break; } ++curRank; if (itr->second != curRank) { - TC_LOG_ERROR("sql.sql", "Spell %u (rank %u) listed in `spell_ranks` for chain %u does not have proper rank value(should be %u)!", itr->first, itr->second, lastSpell, curRank); + TC_LOG_ERROR("sql.sql", "The spell %u (rank %u) listed in `spell_ranks` for chain %u does not have a proper rank value (should be %u)!", itr->first, itr->second, lastSpell, curRank); valid = false; break; } @@ -1372,7 +1372,7 @@ void SpellMgr::LoadSpellRanks() int32 addedSpell = itr->first; if (mSpellInfoMap[addedSpell]->ChainEntry) - TC_LOG_ERROR("sql.sql", "Spell %u (rank: %u, first: %u) listed in `spell_ranks` has already ChainEntry from dbc.", addedSpell, itr->second, lastSpell); + TC_LOG_ERROR("sql.sql", "The spell %u (rank: %u, first: %u) listed in `spell_ranks` already has ChainEntry from dbc.", addedSpell, itr->second, lastSpell); mSpellChains[addedSpell].first = GetSpellInfo(lastSpell); mSpellChains[addedSpell].last = GetSpellInfo(rankChain.back().first); @@ -1426,26 +1426,26 @@ void SpellMgr::LoadSpellRequired() SpellInfo const* spell = GetSpellInfo(spell_id); if (!spell) { - TC_LOG_ERROR("sql.sql", "spell_id %u in `spell_required` table is not found in dbcs, skipped", spell_id); + TC_LOG_ERROR("sql.sql", "spell_id %u in `spell_required` table could not be found in dbc, skipped.", spell_id); continue; } SpellInfo const* reqSpell = GetSpellInfo(spell_req); if (!reqSpell) { - TC_LOG_ERROR("sql.sql", "req_spell %u in `spell_required` table is not found in dbcs, skipped", spell_req); + TC_LOG_ERROR("sql.sql", "req_spell %u in `spell_required` table could not be found in dbc, skipped.", spell_req); continue; } if (spell->IsRankOf(reqSpell)) { - TC_LOG_ERROR("sql.sql", "req_spell %u and spell_id %u in `spell_required` table are ranks of the same spell, entry not needed, skipped", spell_req, spell_id); + TC_LOG_ERROR("sql.sql", "req_spell %u and spell_id %u in `spell_required` table are ranks of the same spell, entry not needed, skipped.", spell_req, spell_id); continue; } if (IsSpellRequiringSpell(spell_id, spell_req)) { - TC_LOG_ERROR("sql.sql", "duplicated entry of req_spell %u and spell_id %u in `spell_required`, skipped", spell_req, spell_id); + TC_LOG_ERROR("sql.sql", "Duplicate entry of req_spell %u and spell_id %u in `spell_required`, skipped.", spell_req, spell_id); continue; } @@ -1523,19 +1523,19 @@ void SpellMgr::LoadSpellLearnSpells() if (!GetSpellInfo(spell_id)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_learn_spell` does not exist", spell_id); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_learn_spell` does not exist.", spell_id); continue; } if (!GetSpellInfo(node.spell)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_learn_spell` learning not existed spell %u", spell_id, node.spell); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_learn_spell` learning non-existing spell %u.", spell_id, node.spell); continue; } if (GetTalentSpellCost(node.spell)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped", spell_id, node.spell); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_learn_spell` attempts learning talent spell %u, skipped.", spell_id, node.spell); continue; } @@ -1580,7 +1580,7 @@ void SpellMgr::LoadSpellLearnSpells() { if (itr->second.spell == dbc_node.spell) { - TC_LOG_ERROR("sql.sql", "Spell %u auto-learn spell %u in spell.dbc then the record in `spell_learn_spell` is redundant, please fix DB.", + TC_LOG_ERROR("sql.sql", "The spell %u is an auto-learn spell %u in spell.dbc and the record in `spell_learn_spell` is redundant. Please update your DB.", spell, dbc_node.spell); found = true; break; @@ -1718,7 +1718,7 @@ void SpellMgr::LoadSpellTargetPositions() } else { - TC_LOG_ERROR("sql.sql", "Spell (Id: %u, effIndex: %u) listed in `spell_target_position` does not have target TARGET_DEST_DB (17).", Spell_ID, effIndex); + TC_LOG_ERROR("sql.sql", "Spell (Id: %u, effIndex: %u) listed in `spell_target_position` does not have a target TARGET_DEST_DB (17).", Spell_ID, effIndex); continue; } @@ -1755,7 +1755,7 @@ void SpellMgr::LoadSpellTargetPositions() if (found) { if (!sSpellMgr->GetSpellTargetPosition(i)) - TC_LOG_DEBUG("spells", "Spell (ID: %u) does not have record in `spell_target_position`", i); + TC_LOG_DEBUG("spells", "Spell (ID: %u) does not have a record in `spell_target_position`.", i); } }*/ @@ -1814,12 +1814,12 @@ void SpellMgr::LoadSpellGroups() if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_group` does not exist", itr->second); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_group` does not exist", itr->second); mSpellGroupSpell.erase(itr++); } else if (spellInfo->GetRank() > 1) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_group` is not first rank of spell", itr->second); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_group` is not the first rank of the spell.", itr->second); mSpellGroupSpell.erase(itr++); } else @@ -1865,7 +1865,7 @@ void SpellMgr::LoadSpellGroupStackRules() uint8 stack_rule = fields[1].GetInt8(); if (stack_rule >= SPELL_GROUP_STACK_RULE_MAX) { - TC_LOG_ERROR("sql.sql", "SpellGroupStackRule %u listed in `spell_group_stack_rules` does not exist", stack_rule); + TC_LOG_ERROR("sql.sql", "SpellGroupStackRule %u listed in `spell_group_stack_rules` does not exist.", stack_rule); continue; } @@ -1873,7 +1873,7 @@ void SpellMgr::LoadSpellGroupStackRules() if (spellGroup.first == spellGroup.second) { - TC_LOG_ERROR("sql.sql", "SpellGroup id %u listed in `spell_group_stack_rules` does not exist", group_id); + TC_LOG_ERROR("sql.sql", "SpellGroup id %u listed in `spell_group_stack_rules` does not exist.", group_id); continue; } @@ -1917,18 +1917,18 @@ void SpellMgr::LoadSpellProcEvents() SpellInfo const* spellInfo = GetSpellInfo(spellId); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc_event` does not exist", spellId); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` does not exist.", spellId); continue; } if (allRanks) { if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId); + TC_LOG_ERROR("sql.sql", "The spell %u is listed in `spell_proc_event` with all ranks, but spell has no ranks.", spellId); if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc_event` is not first rank of spell.", spellId); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is not first rank of spell.", spellId); continue; } } @@ -1950,12 +1950,12 @@ void SpellMgr::LoadSpellProcEvents() { if (mSpellProcEventMap.find(spellInfo->Id) != mSpellProcEventMap.end()) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc_event` already has its first rank in table.", spellInfo->Id); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` already has its first rank in table.", spellInfo->Id); break; } if (!spellInfo->ProcFlags && !spellProcEvent.procFlags) - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc_event` probably not triggered spell", spellInfo->Id); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc_event` is probably not a triggered spell.", spellInfo->Id); mSpellProcEventMap[spellInfo->Id] = spellProcEvent; @@ -2003,18 +2003,18 @@ void SpellMgr::LoadSpellProcs() SpellInfo const* spellInfo = GetSpellInfo(spellId); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc` does not exist", spellId); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` does not exist", spellId); continue; } if (allRanks) { if (!spellInfo->IsRanked()) - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` with all ranks, but spell has no ranks.", spellId); if (spellInfo->GetFirstRankSpell()->Id != uint32(spellId)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc` is not first rank of spell.", spellId); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` is not the first rank of the spell.", spellId); continue; } } @@ -2041,7 +2041,7 @@ void SpellMgr::LoadSpellProcs() { if (mSpellProcMap.find(spellInfo->Id) != mSpellProcMap.end()) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_proc` already has its first rank in table.", spellInfo->Id); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_proc` already has its first rank in the table.", spellInfo->Id); break; } @@ -2062,42 +2062,42 @@ void SpellMgr::LoadSpellProcs() 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 `chance` field", spellInfo->Id); + 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) { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in `ratePerMinute` field", spellInfo->Id); + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ratePerMinute` field", spellInfo->Id); procEntry.ratePerMinute = 0; } if (cooldown < 0) { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in `cooldown` field", spellInfo->Id); + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `cooldown` field", spellInfo->Id); procEntry.cooldown = 0; } if (procEntry.chance == 0 && procEntry.ratePerMinute == 0) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have `chance` and `ratePerMinute` values defined, proc will not be triggered", spellInfo->Id); + 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", "`spell_proc` table entry for spellId %u has too big value in `charges` field", spellInfo->Id); + 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", "`spell_proc` table entry for spellId %u doesn't have `typeMask` value defined, proc will not be triggered", spellInfo->Id); + 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", "`spell_proc` table entry for spellId %u has `spellTypeMask` value defined, but it won't be used for defined `typeMask` value", spellInfo->Id); + 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", "`spell_proc` table entry for spellId %u doesn't have `spellPhaseMask` value defined, but it's required for defined `typeMask` value, proc will not be triggered", spellInfo->Id); + 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", "`spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellInfo->Id, procEntry.spellPhaseMask); + 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", "`spell_proc` table entry for spellId %u has `spellPhaseMask` value defined, but it won't be used for defined `typeMask` value", spellInfo->Id); + 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", "`spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", spellInfo->Id, procEntry.hitMask); + 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", "`spell_proc` table entry for spellId %u has `hitMask` value defined, but it won't be used for defined `typeMask` and `spellPhaseMask` values", spellInfo->Id); + 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); mSpellProcMap[spellInfo->Id] = procEntry; @@ -2136,7 +2136,7 @@ void SpellMgr::LoadSpellBonusess() SpellInfo const* spell = GetSpellInfo(entry); if (!spell) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_bonus_data` does not exist", entry); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_bonus_data` does not exist.", entry); continue; } @@ -2175,7 +2175,7 @@ void SpellMgr::LoadSpellThreats() if (!GetSpellInfo(entry)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_threat` does not exist", entry); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_threat` does not exist.", entry); continue; } @@ -2244,21 +2244,21 @@ void SpellMgr::LoadSpellPetAuras() SpellInfo const* spellInfo = GetSpellInfo(spell); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_pet_auras` does not exist", spell); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_pet_auras` does not exist.", spell); continue; } if (spellInfo->Effects[eff].Effect != SPELL_EFFECT_DUMMY && (spellInfo->Effects[eff].Effect != SPELL_EFFECT_APPLY_AURA || spellInfo->Effects[eff].ApplyAuraName != SPELL_AURA_DUMMY)) { - TC_LOG_ERROR("spells", "Spell %u listed in `spell_pet_auras` does not have dummy aura or dummy effect", spell); + TC_LOG_ERROR("spells", "The spell %u listed in `spell_pet_auras` does not have any dummy aura or dummy effect.", spell); continue; } SpellInfo const* spellInfo2 = GetSpellInfo(aura); if (!spellInfo2) { - TC_LOG_ERROR("sql.sql", "Aura %u listed in `spell_pet_auras` does not exist", aura); + TC_LOG_ERROR("sql.sql", "The aura %u listed in `spell_pet_auras` does not exist.", aura); continue; } @@ -2336,7 +2336,7 @@ void SpellMgr::LoadSpellEnchantProcData() SpellItemEnchantmentEntry const* ench = sSpellItemEnchantmentStore.LookupEntry(enchantId); if (!ench) { - TC_LOG_ERROR("sql.sql", "Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId); + TC_LOG_ERROR("sql.sql", "The enchancment %u listed in `spell_enchant_proc_data` does not exist.", enchantId); continue; } @@ -2380,7 +2380,7 @@ void SpellMgr::LoadSpellLinked() SpellInfo const* spellInfo = GetSpellInfo(abs(trigger)); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_linked_spell` does not exist", abs(trigger)); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_linked_spell` does not exist.", abs(trigger)); continue; } @@ -2388,13 +2388,13 @@ void SpellMgr::LoadSpellLinked() for (uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j) { if (spellInfo->Effects[j].CalcValue() == abs(effect)) - TC_LOG_ERROR("sql.sql", "Spell %u Effect: %u listed in `spell_linked_spell` has same bp%u like effect (possible hack)", abs(trigger), abs(effect), j); + TC_LOG_ERROR("sql.sql", "The spell %u Effect: %u listed in `spell_linked_spell` has same bp%u like effect (possible hack).", abs(trigger), abs(effect), j); } spellInfo = GetSpellInfo(abs(effect)); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_linked_spell` does not exist", abs(effect)); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_linked_spell` does not exist.", abs(effect)); continue; } @@ -2639,7 +2639,7 @@ void SpellMgr::LoadSpellAreas() } else { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` does not exist", spell); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` does not exist", spell); continue; } @@ -2668,20 +2668,20 @@ void SpellMgr::LoadSpellAreas() if (!ok) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` already listed with similar requirements.", spell); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` is already listed with similar requirements.", spell); continue; } } - if (spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId)) + if (spellArea.areaId && !sAreaTableStore.LookupEntry(spellArea.areaId)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have wrong area (%u) requirement", spell, spellArea.areaId); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has a wrong area (%u) requirement.", spell, spellArea.areaId); continue; } if (spellArea.questStart && !sObjectMgr->GetQuestTemplate(spellArea.questStart)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell, spellArea.questStart); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has a wrong start quest (%u) requirement.", spell, spellArea.questStart); continue; } @@ -2689,7 +2689,7 @@ void SpellMgr::LoadSpellAreas() { if (!sObjectMgr->GetQuestTemplate(spellArea.questEnd)) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell, spellArea.questEnd); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has a wrong ending quest (%u) requirement.", spell, spellArea.questEnd); continue; } } @@ -2699,13 +2699,13 @@ void SpellMgr::LoadSpellAreas() SpellInfo const* spellInfo = GetSpellInfo(abs(spellArea.auraSpell)); if (!spellInfo) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell, abs(spellArea.auraSpell)); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has wrong aura spell (%u) requirement", spell, abs(spellArea.auraSpell)); continue; } if (uint32(abs(spellArea.auraSpell)) == spellArea.spellId) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell, abs(spellArea.auraSpell)); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has aura spell (%u) requirement for itself", spell, abs(spellArea.auraSpell)); continue; } @@ -2725,7 +2725,7 @@ void SpellMgr::LoadSpellAreas() if (chain) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell, spellArea.auraSpell); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has the aura spell (%u) requirement that it autocasts itself from the aura.", spell, spellArea.auraSpell); continue; } @@ -2741,7 +2741,7 @@ void SpellMgr::LoadSpellAreas() if (chain) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell, spellArea.auraSpell); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has the aura spell (%u) requirement that the spell itself autocasts from the aura.", spell, spellArea.auraSpell); continue; } } @@ -2749,13 +2749,13 @@ void SpellMgr::LoadSpellAreas() if (spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE) == 0) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have wrong race mask (%u) requirement", spell, spellArea.raceMask); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has wrong race mask (%u) requirement.", spell, spellArea.raceMask); continue; } if (spellArea.gender != GENDER_NONE && spellArea.gender != GENDER_FEMALE && spellArea.gender != GENDER_MALE) { - TC_LOG_ERROR("sql.sql", "Spell %u listed in `spell_area` have wrong gender (%u) requirement", spell, spellArea.gender); + TC_LOG_ERROR("sql.sql", "The spell %u listed in `spell_area` has wrong gender (%u) requirement.", spell, spellArea.gender); continue; } @@ -3022,6 +3022,7 @@ void SpellMgr::LoadSpellInfoCorrections() break; case 70743: // AoD Special case 70614: // AoD Special - Vegard + case 4020: // Safirdrang's Chill case 42436: // Drink! (Brewfest) spellInfo->Effects[EFFECT_0].TargetA = SpellImplicitTargetInfo(TARGET_UNIT_TARGET_ANY); break; @@ -3061,13 +3062,6 @@ void SpellMgr::LoadSpellInfoCorrections() case 36350: // They Must Burn Bomb Aura (self) spellInfo->Effects[EFFECT_0].TriggerSpell = 36325; // They Must Burn Bomb Drop (DND) break; - case 49838: // Stop Time - case 69438: // Sample Satisfaction - case 69445: // Perfume Spritz - case 69489: // Chocolate Sample - case 69563: // Cologne Spritz - spellInfo->AttributesEx3 |= SPELL_ATTR3_NO_INITIAL_AGGRO; - break; case 61407: // Energize Cores case 62136: // Energize Cores case 54069: // Energize Cores diff --git a/src/server/game/Texts/CreatureTextMgr.cpp b/src/server/game/Texts/CreatureTextMgr.cpp index 9e6b4989733..bbfa501f4aa 100644 --- a/src/server/game/Texts/CreatureTextMgr.cpp +++ b/src/server/game/Texts/CreatureTextMgr.cpp @@ -191,7 +191,6 @@ void CreatureTextMgr::LoadCreatureTextLocales() } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u creature localized texts in %u ms", textCount, GetMSTimeDiffToNow(oldMSTime)); - } uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject const* whisperTarget /*= nullptr*/, ChatMsg msgType /*= CHAT_MSG_ADDON*/, Language language /*= LANG_ADDON*/, CreatureTextRange range /*= TEXT_RANGE_NORMAL*/, uint32 sound /*= 0*/, Team team /*= TEAM_OTHER*/, bool gmOnly /*= false*/, Player* srcPlr /*= nullptr*/) @@ -228,42 +227,10 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject tempGroup = textGroupContainer; } - uint8 count = 0; - float lastChance = -1; - bool isEqualChanced = true; - - float totalChance = 0; - - for (CreatureTextGroup::const_iterator iter = tempGroup.begin(); iter != tempGroup.end(); ++iter) + auto iter = Trinity::Containers::SelectRandomWeightedContainerElement(tempGroup, [](CreatureTextEntry const& t) -> double { - if (lastChance >= 0 && lastChance != iter->probability) - isEqualChanced = false; - - lastChance = iter->probability; - totalChance += iter->probability; - ++count; - } - - int32 offset = -1; - if (!isEqualChanced) - { - for (CreatureTextGroup::const_iterator iter = tempGroup.begin(); iter != tempGroup.end(); ++iter) - { - uint32 chance = uint32(iter->probability); - uint32 r = urand(0, 100); - ++offset; - if (r <= chance) - break; - } - } - - uint32 pos = 0; - if (isEqualChanced || offset < 0) - pos = urand(0, count - 1); - else if (offset >= 0) - pos = offset; - - CreatureTextGroup::const_iterator iter = tempGroup.begin() + pos; + return t.probability; + }); ChatMsg finalType = (msgType == CHAT_MSG_ADDON) ? iter->type : msgType; Language finalLang = (language == LANG_ADDON) ? iter->lang : language; @@ -293,9 +260,7 @@ uint32 CreatureTextMgr::SendChat(Creature* source, uint8 textGroup, WorldObject SendChatPacket(finalSource, builder, finalType, whisperTarget, range, team, gmOnly); } - if (isEqualChanced || totalChance == 100.0f) - SetRepeatId(source, textGroup, iter->id); - + SetRepeatId(source, textGroup, iter->id); return iter->duration; } diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 9d096088132..516dc8e5b47 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -586,6 +586,13 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_ADDON_CHANNEL] = sConfigMgr->GetBoolDefault("AddonChannel", true); m_bool_configs[CONFIG_CLEAN_CHARACTER_DB] = sConfigMgr->GetBoolDefault("CleanCharacterDB", false); m_int_configs[CONFIG_PERSISTENT_CHARACTER_CLEAN_FLAGS] = sConfigMgr->GetIntDefault("PersistentCharacterCleanFlags", 0); + m_int_configs[CONFIG_AUCTION_GETALL_DELAY] = sConfigMgr->GetIntDefault("Auction.GetAllScanDelay", 900); + m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] = sConfigMgr->GetIntDefault("Auction.SearchDelay", 300); + if (m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] < 100 || m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] > 10000) + { + TC_LOG_ERROR("server.loading", "Auction.SearchDelay (%i) must be between 100 and 10000. Using default of 300ms", m_int_configs[CONFIG_AUCTION_SEARCH_DELAY]); + m_int_configs[CONFIG_AUCTION_SEARCH_DELAY] = 300; + } 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_SAY_LEVEL_REQ] = sConfigMgr->GetIntDefault("ChatLevelReq.Say", 1); @@ -1029,7 +1036,7 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_DETECT_POS_COLLISION] = sConfigMgr->GetBoolDefault("DetectPosCollision", true); m_bool_configs[CONFIG_RESTRICTED_LFG_CHANNEL] = sConfigMgr->GetBoolDefault("Channel.RestrictedLfg", true); - m_bool_configs[CONFIG_TALENTS_INSPECTING] = sConfigMgr->GetBoolDefault("TalentsInspecting", true); + m_int_configs[CONFIG_TALENTS_INSPECTING] = sConfigMgr->GetIntDefault("TalentsInspecting", 1); m_bool_configs[CONFIG_CHAT_FAKE_MESSAGE_PREVENTING] = sConfigMgr->GetBoolDefault("ChatFakeMessagePreventing", false); m_int_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY] = sConfigMgr->GetIntDefault("ChatStrictLinkChecking.Severity", 0); m_int_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_KICK] = sConfigMgr->GetIntDefault("ChatStrictLinkChecking.Kick", 0); diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index d2931af07cd..57f190612df 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -122,7 +122,6 @@ enum WorldBoolConfigs CONFIG_QUEST_IGNORE_RAID, CONFIG_DETECT_POS_COLLISION, CONFIG_RESTRICTED_LFG_CHANNEL, - CONFIG_TALENTS_INSPECTING, CONFIG_CHAT_FAKE_MESSAGE_PREVENTING, CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP, CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE, @@ -373,6 +372,9 @@ enum WorldIntConfigs CONFIG_CHARTER_COST_ARENA_5v5, CONFIG_NO_GRAY_AGGRO_ABOVE, CONFIG_NO_GRAY_AGGRO_BELOW, + CONFIG_AUCTION_GETALL_DELAY, + CONFIG_AUCTION_SEARCH_DELAY, + CONFIG_TALENTS_INSPECTING, INT_CONFIG_VALUE_COUNT }; diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index 62a52d1a870..e69ef285103 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -133,7 +133,7 @@ public: info.name = fields[1].GetString(); info.accountId = fields[2].GetUInt32(); - // account name will be empty for not existed account + // account name will be empty for nonexisting account AccountMgr::GetName(info.accountId, info.accountName); info.deleteDate = time_t(fields[3].GetUInt32()); foundList.push_back(info); @@ -169,11 +169,11 @@ public: if (!handler->GetSession()) handler->PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CONSOLE, - itr->guid.GetCounter(), itr->name.c_str(), itr->accountName.empty() ? "" : itr->accountName.c_str(), + itr->guid.GetCounter(), itr->name.c_str(), itr->accountName.empty() ? "" : itr->accountName.c_str(), itr->accountId, dateStr.c_str()); else handler->PSendSysMessage(LANG_CHARACTER_DELETED_LIST_LINE_CHAT, - itr->guid.GetCounter(), itr->name.c_str(), itr->accountName.empty() ? "" : itr->accountName.c_str(), + itr->guid.GetCounter(), itr->name.c_str(), itr->accountName.empty() ? "" : itr->accountName.c_str(), itr->accountId, dateStr.c_str()); } @@ -193,7 +193,7 @@ public: */ static void HandleCharacterDeletedRestoreHelper(DeletedInfo const& delInfo, ChatHandler* handler) { - if (delInfo.accountName.empty()) // account not exist + if (delInfo.accountName.empty()) // account does not exist { handler->PSendSysMessage(LANG_CHARACTER_DELETED_SKIP_ACCOUNT, delInfo.name.c_str(), delInfo.guid.GetCounter(), delInfo.accountId); return; @@ -660,7 +660,7 @@ public: if (newCharName.empty()) { - // Drop not existed account cases + // Drop nonexisting account cases for (DeletedInfoList::iterator itr = foundList.begin(); itr != foundList.end(); ++itr) HandleCharacterDeletedRestoreHelper(*itr, handler); } @@ -810,7 +810,7 @@ public: if (levelStr && isalpha(levelStr[0])) { nameStr = levelStr; - levelStr = NULL; // current level will used + levelStr = NULL; // current level will be used } Player* target; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index de994a7c73a..cf1031c27df 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -94,7 +94,8 @@ public: { "transport", rbac::RBAC_PERM_COMMAND_DEBUG_TRANSPORT, false, &HandleDebugTransportCommand, "" }, { "phase", rbac::RBAC_PERM_COMMAND_DEBUG_PHASE, false, &HandleDebugPhaseCommand, "" }, { "loadcells", rbac::RBAC_PERM_COMMAND_DEBUG_LOADCELLS, false, &HandleDebugLoadCellsCommand, "" }, - { "boundary", rbac::RBAC_PERM_COMMAND_DEBUG_BOUNDARY, false, &HandleDebugBoundaryCommand, "" } + { "boundary", rbac::RBAC_PERM_COMMAND_DEBUG_BOUNDARY, false, &HandleDebugBoundaryCommand, "" }, + { "raidreset", rbac::RBAC_PERM_COMMAND_INSTANCE_UNBIND, false, &HandleDebugRaidResetCommand, "" } }; static std::vector commandTable = { @@ -1470,6 +1471,32 @@ public: return true; } + + static bool HandleDebugRaidResetCommand(ChatHandler* /*handler*/, char const* args) + { + char* map_str = args ? strtok((char*)args, " ") : nullptr; + char* difficulty_str = args ? strtok(nullptr, " ") : nullptr; + + int32 map = map_str ? atoi(map_str) : -1; + if (map <= 0) + return false; + MapEntry const* mEntry = sMapStore.LookupEntry(map); + if (!mEntry || !mEntry->IsRaid()) + return false; + int32 difficulty = difficulty_str ? atoi(difficulty_str) : -1; + if (difficulty >= MAX_RAID_DIFFICULTY || difficulty < -1) + return false; + + if (difficulty == -1) + for (uint8 diff = 0; diff < MAX_RAID_DIFFICULTY; ++diff) + { + if (GetMapDifficultyData(mEntry->MapID, Difficulty(diff))) + sInstanceSaveMgr->ForceGlobalReset(mEntry->MapID, Difficulty(diff)); + } + else + sInstanceSaveMgr->ForceGlobalReset(mEntry->MapID, Difficulty(difficulty)); + return true; + } }; void AddSC_debug_commandscript() diff --git a/src/server/scripts/Commands/cs_go.cpp b/src/server/scripts/Commands/cs_go.cpp index 27d8c9f35f8..a97dd62436d 100644 --- a/src/server/scripts/Commands/cs_go.cpp +++ b/src/server/scripts/Commands/cs_go.cpp @@ -424,7 +424,7 @@ public: uint32 areaId = id ? (uint32)atoi(id) : player->GetZoneId(); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); if (x < 0 || x > 100 || y < 0 || y > 100 || !areaEntry) { @@ -434,7 +434,7 @@ public: } // update to parent zone if exist (client map show only zones without parents) - AreaTableEntry const* zoneEntry = areaEntry->zone ? GetAreaEntryByAreaID(areaEntry->zone) : areaEntry; + AreaTableEntry const* zoneEntry = areaEntry->zone ? sAreaTableStore.LookupEntry(areaEntry->zone) : areaEntry; ASSERT(zoneEntry); Map const* map = sMapMgr->CreateBaseMap(zoneEntry->mapid); diff --git a/src/server/scripts/Commands/cs_group.cpp b/src/server/scripts/Commands/cs_group.cpp index 5e8952ce881..9f2bc86f9c0 100644 --- a/src/server/scripts/Commands/cs_group.cpp +++ b/src/server/scripts/Commands/cs_group.cpp @@ -348,10 +348,10 @@ public: phase = (!p->IsGameMaster() ? p->GetPhaseMask() : -1); uint32 locale = handler->GetSessionDbcLocale(); - AreaTableEntry const* area = GetAreaEntryByAreaID(p->GetAreaId()); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(p->GetAreaId()); if (area) { - AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone); if (zone) zoneName = zone->area_name[locale]; } diff --git a/src/server/scripts/Commands/cs_instance.cpp b/src/server/scripts/Commands/cs_instance.cpp index a53d1f00b54..d0325a317db 100644 --- a/src/server/scripts/Commands/cs_instance.cpp +++ b/src/server/scripts/Commands/cs_instance.cpp @@ -82,7 +82,7 @@ public: { InstanceSave* save = itr->second.save; std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL)); - handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", itr->second.extendState == EXTEND_STATE_EXPIRED ? "expired" : itr->second.extendState == EXTEND_STATE_EXTENDED ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); counter++; } } @@ -98,7 +98,7 @@ public: { InstanceSave* save = itr->second.save; std::string timeleft = GetTimeString(save->GetResetTime() - time(NULL)); - handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); + handler->PSendSysMessage(LANG_COMMAND_LIST_BIND_INFO, itr->first, save->GetInstanceId(), itr->second.perm ? "yes" : "no", "-", save->GetDifficulty(), save->CanReset() ? "yes" : "no", timeleft.c_str()); counter++; } } diff --git a/src/server/scripts/Commands/cs_lookup.cpp b/src/server/scripts/Commands/cs_lookup.cpp index 92ac0c36eed..f19bff9c8b7 100644 --- a/src/server/scripts/Commands/cs_lookup.cpp +++ b/src/server/scripts/Commands/cs_lookup.cpp @@ -97,9 +97,9 @@ public: wstrToLower(wNamePart); // Search in AreaTable.dbc - for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows(); ++areaflag) + for (uint32 i = 0; i < sAreaTableStore.GetNumRows(); ++i) { - AreaTableEntry const* areaEntry = sAreaStore.LookupEntry(areaflag); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(i); if (areaEntry) { std::string name = areaEntry->area_name; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 13d7f2f0215..69ddc5c3204 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -197,8 +197,8 @@ public: uint32 mapId = object->GetMapId(); MapEntry const* mapEntry = sMapStore.LookupEntry(mapId); - AreaTableEntry const* zoneEntry = GetAreaEntryByAreaID(zoneId); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* zoneEntry = sAreaTableStore.LookupEntry(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(areaId); float zoneX = object->GetPositionX(); float zoneY = object->GetPositionY(); @@ -976,7 +976,7 @@ public: uint32 zoneId = player->GetZoneId(); - AreaTableEntry const* areaEntry = GetAreaEntryByAreaID(zoneId); + AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId); if (!areaEntry || areaEntry->zone !=0) { handler->PSendSysMessage(LANG_COMMAND_GRAVEYARDWRONGZONE, graveyardId, zoneId); @@ -1067,17 +1067,23 @@ public: return false; } - int32 area = GetAreaFlagByAreaID(atoi((char*)args)); - int32 offset = area / 32; - - if (area<0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + AreaTableEntry const* area = sAreaTableStore.LookupEntry(atoi(args)); + if (!area) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } - uint32 val = uint32((1 << (area % 32))); + int32 offset = area->exploreFlag / 32; + if (offset >= PLAYER_EXPLORED_ZONES_SIZE) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + + uint32 val = uint32((1 << (area->exploreFlag % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields | val))); @@ -1098,17 +1104,23 @@ public: return false; } - int32 area = GetAreaFlagByAreaID(atoi((char*)args)); - int32 offset = area / 32; - - if (area < 0 || offset >= PLAYER_EXPLORED_ZONES_SIZE) + AreaTableEntry const* area = sAreaTableStore.LookupEntry(atoi(args)); + if (!area) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } - uint32 val = uint32((1 << (area % 32))); + int32 offset = area->exploreFlag / 32; + if (offset >= PLAYER_EXPLORED_ZONES_SIZE) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + + uint32 val = uint32((1 << (area->exploreFlag % 32))); uint32 currFields = playerTarget->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset); playerTarget->SetUInt32Value(PLAYER_EXPLORED_ZONES_1 + offset, uint32((currFields ^ val))); @@ -1752,12 +1764,12 @@ public: // Position data MapEntry const* map = sMapStore.LookupEntry(mapId); - AreaTableEntry const* area = GetAreaEntryByAreaID(areaId); + AreaTableEntry const* area = sAreaTableStore.LookupEntry(areaId); if (area) { areaName = area->area_name; - AreaTableEntry const* zone = GetAreaEntryByAreaID(area->zone); + AreaTableEntry const* zone = sAreaTableStore.LookupEntry(area->zone); if (zone) zoneName = zone->area_name; } diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index 273a2ae288f..54a10c876f8 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -43,7 +43,7 @@ struct EnumName #define CREATE_NAMED_ENUM(VALUE) { VALUE, STRINGIZE(VALUE) } #define NPCFLAG_COUNT 24 -#define FLAGS_EXTRA_COUNT 16 +#define FLAGS_EXTRA_COUNT 19 EnumName const npcFlagTexts[NPCFLAG_COUNT] = { @@ -163,7 +163,10 @@ EnumName const flagsExtra[FLAGS_EXTRA_COUNT] = CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_SKILLGAIN), CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_TAUNT_DIMINISH), CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_ALL_DIMINISH), - CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_DUNGEON_BOSS) + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_DUNGEON_BOSS), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING), + CREATE_NAMED_ENUM(CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK) }; class npc_commandscript : public CommandScript diff --git a/src/server/scripts/Commands/cs_send.cpp b/src/server/scripts/Commands/cs_send.cpp index 672db3a3ab0..31544543426 100644 --- a/src/server/scripts/Commands/cs_send.cpp +++ b/src/server/scripts/Commands/cs_send.cpp @@ -74,7 +74,7 @@ public: std::string subject = msgSubject; std::string text = msgText; - // from console show not existed sender + // from console, use non-existing sender MailSender sender(MAIL_NORMAL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetGUID().GetCounter() : 0, MAIL_STATIONERY_GM); /// @todo Fix poor design @@ -173,7 +173,7 @@ public: } } - // from console show not existed sender + // from console show nonexisting sender MailSender sender(MAIL_NORMAL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetGUID().GetCounter() : 0, MAIL_STATIONERY_GM); // fill mail @@ -185,7 +185,7 @@ public: { if (Item* item = Item::CreateItem(itr->first, itr->second, handler->GetSession() ? handler->GetSession()->GetPlayer() : 0)) { - item->SaveToDB(trans); // save for prevent lost at next mail load, if send fail then item will deleted + item->SaveToDB(trans); // Save to prevent being lost at next mail load. If send fails, the item will be deleted. draft.AddItem(item); } } @@ -233,7 +233,7 @@ public: std::string subject = msgSubject; std::string text = msgText; - // from console show not existed sender + // from console show nonexisting sender MailSender sender(MAIL_NORMAL, handler->GetSession() ? handler->GetSession()->GetPlayer()->GetGUID().GetCounter() : 0, MAIL_STATIONERY_GM); SQLTransaction trans = CharacterDatabase.BeginTransaction(); @@ -260,7 +260,7 @@ public: if (!msgStr) return false; - ///- Check that he is not logging out. + /// - Check if player is logging out. if (player->GetSession()->isLogingOut()) { handler->SendSysMessage(LANG_PLAYER_NOT_FOUND); diff --git a/src/server/scripts/Commands/cs_titles.cpp b/src/server/scripts/Commands/cs_titles.cpp index 3bcabd1c28e..8a87f385c57 100644 --- a/src/server/scripts/Commands/cs_titles.cpp +++ b/src/server/scripts/Commands/cs_titles.cpp @@ -224,7 +224,7 @@ public: if (CharTitlesEntry const* tEntry = sCharTitlesStore.LookupEntry(i)) titles2 &= ~(uint64(1) << tEntry->bit_index); - titles &= ~titles2; // remove not existed titles + titles &= ~titles2; // remove non-existing titles target->SetUInt64Value(PLAYER__FIELD_KNOWN_TITLES, titles); handler->SendSysMessage(LANG_DONE); diff --git a/src/server/scripts/EasternKingdoms/Deadmines/boss_mr_smite.cpp b/src/server/scripts/EasternKingdoms/Deadmines/boss_mr_smite.cpp index 541ddc0e1c8..8f246ab9bf0 100644 --- a/src/server/scripts/EasternKingdoms/Deadmines/boss_mr_smite.cpp +++ b/src/server/scripts/EasternKingdoms/Deadmines/boss_mr_smite.cpp @@ -25,17 +25,25 @@ EndScriptData */ #include "ScriptedCreature.h" #include "deadmines.h" -enum Spels +enum Spells { SPELL_TRASH = 3391, SPELL_SMITE_STOMP = 6432, SPELL_SMITE_SLAM = 6435, - SPELL_NIMBLE_REFLEXES = 6264, + SPELL_NIMBLE_REFLEXES = 6264 +}; +enum Equips +{ EQUIP_SWORD = 5191, - EQUIP_MACE = 7230, + EQUIP_AXE = 5196, + EQUIP_MACE = 7230 +}; - SAY_AGGRO = 0, +enum Texts +{ + SAY_PHASE_1 = 2, + SAY_PHASE_2 = 3 }; class boss_mr_smite : public CreatureScript @@ -66,6 +74,8 @@ public: uiPhase = 0; uiTimer = 0; + + uiIsMoving = false; } InstanceScript* instance; @@ -79,16 +89,19 @@ public: uint32 uiPhase; uint32 uiTimer; + bool uiIsMoving; + void Reset() override { Initialize(); SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); + me->SetStandState(UNIT_STAND_STATE_STAND); + me->SetReactState(REACT_AGGRESSIVE); } void EnterCombat(Unit* /*who*/) override { - Talk(SAY_AGGRO); } bool bCheckChances() @@ -105,38 +118,52 @@ public: if (!UpdateVictim()) return; - /*START ACID-AI*/ - if (uiTrashTimer <= uiDiff) + if (!uiIsMoving) // halt abilities in between phases { - if (bCheckChances()) - DoCast(me, SPELL_TRASH); - uiTrashTimer = urand(6000, 15500); - } else uiTrashTimer -= uiDiff; + if (uiTrashTimer <= uiDiff) + { + if (bCheckChances()) + DoCast(me, SPELL_TRASH); + uiTrashTimer = urand(6000, 15500); + } + else uiTrashTimer -= uiDiff; - if (uiSlamTimer <= uiDiff) - { - if (bCheckChances()) - DoCastVictim(SPELL_SMITE_SLAM); - uiSlamTimer = 11000; - } else uiSlamTimer -= uiDiff; + if (uiSlamTimer <= uiDiff) + { + if (bCheckChances()) + DoCastVictim(SPELL_SMITE_SLAM); + uiSlamTimer = 11000; + } + else uiSlamTimer -= uiDiff; - if (uiNimbleReflexesTimer <= uiDiff) - { - if (bCheckChances()) - DoCast(me, SPELL_NIMBLE_REFLEXES); - uiNimbleReflexesTimer = urand(27300, 60100); - } else uiNimbleReflexesTimer -= uiDiff; - /*END ACID-AI*/ + if (uiNimbleReflexesTimer <= uiDiff) + { + if (bCheckChances()) + DoCast(me, SPELL_NIMBLE_REFLEXES); + uiNimbleReflexesTimer = urand(27300, 60100); + } + else uiNimbleReflexesTimer -= uiDiff; + } if ((uiHealth == 0 && !HealthAbovePct(66)) || (uiHealth == 1 && !HealthAbovePct(33))) { ++uiHealth; DoCastAOE(SPELL_SMITE_STOMP, false); SetCombatMovement(false); - if (GameObject* go = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_SMITE_CHEST))) + me->AttackStop(); + me->InterruptNonMeleeSpells(false); + me->SetReactState(REACT_PASSIVE); + uiTimer = 2500; + uiPhase = 1; + + switch (uiHealth) { - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(1, go->GetPositionX() - 3.0f, go->GetPositionY(), go->GetPositionZ()); + case 1: + Talk(SAY_PHASE_1); + break; + case 2: + Talk(SAY_PHASE_2); + break; } } @@ -147,21 +174,36 @@ public: switch (uiPhase) { case 1: - me->HandleEmoteCommand(EMOTE_STATE_KNEEL); //dosen't work? - uiTimer = 1000; - uiPhase = 2; + { + if (uiIsMoving) + break; + + if (GameObject* go = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_SMITE_CHEST))) + { + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(1, go->GetPositionX() - 1.5f, go->GetPositionY() + 1.4f, go->GetPositionZ()); + uiIsMoving = true; + } break; + } case 2: if (uiHealth == 1) - SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_SWORD, EQUIP_NO_CHANGE); + SetEquipmentSlots(false, EQUIP_AXE, EQUIP_AXE, EQUIP_NO_CHANGE); else SetEquipmentSlots(false, EQUIP_MACE, EQUIP_UNEQUIP, EQUIP_NO_CHANGE); uiTimer = 500; uiPhase = 3; break; case 3: + me->SetStandState(UNIT_STAND_STATE_STAND); + uiTimer = 750; + uiPhase = 4; + break; + case 4: + me->SetReactState(REACT_AGGRESSIVE); SetCombatMovement(true); me->GetMotionMaster()->MoveChase(me->GetVictim(), me->m_CombatDistance); + uiIsMoving = false; uiPhase = 0; break; } @@ -176,8 +218,11 @@ public: if (uiType != POINT_MOTION_TYPE) return; - uiTimer = 1500; - uiPhase = 1; + me->SetFacingTo(5.47f); + me->SetStandState(UNIT_STAND_STATE_KNEEL); + + uiTimer = 2000; + uiPhase = 2; } }; }; diff --git a/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h b/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h index dff4243617e..01ebabb160e 100644 --- a/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h +++ b/src/server/scripts/EasternKingdoms/Deadmines/deadmines.h @@ -26,6 +26,7 @@ enum CannonState CANNON_GUNPOWDER_USED, CANNON_BLAST_INITIATED, PIRATES_ATTACK, + SMITE_ALARMED, EVENT_DONE }; @@ -48,4 +49,16 @@ enum GameObjects GO_DOOR_LEVER = 101833, GO_MR_SMITE_CHEST = 144111 }; + +enum CreaturesIds +{ + NPC_MR_SMITE = 646 +}; + +enum InstanceTexts +{ + SAY_ALARM1 = 0, + SAY_ALARM2 = 1 +}; + #endif diff --git a/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp b/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp index 80391ab2873..b827fdf7e8b 100644 --- a/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp +++ b/src/server/scripts/EasternKingdoms/Deadmines/instance_deadmines.cpp @@ -32,18 +32,14 @@ EndScriptData */ enum Sounds { SOUND_CANNONFIRE = 1400, - SOUND_DESTROYDOOR = 3079, - SOUND_MR_SMITE_ALARM1 = 5775, - SOUND_MR_SMITE_ALARM2 = 5777 + SOUND_DESTROYDOOR = 3079 }; -#define SAY_MR_SMITE_ALARM1 "You there, check out that noise!" -#define SAY_MR_SMITE_ALARM2 "We're under attack! A vast, ye swabs! Repel the invaders!" - enum Misc { DATA_CANNON_BLAST_TIMER = 3000, - DATA_PIRATES_DELAY_TIMER = 1000 + DATA_PIRATES_DELAY_TIMER = 1000, + DATA_SMITE_ALARM_DELAY_TIMER = 5000 }; class instance_deadmines : public InstanceMapScript @@ -61,6 +57,8 @@ class instance_deadmines : public InstanceMapScript SetHeaders(DataHeader); State = CANNON_NOT_USED; + CannonBlast_Timer = 0; + PiratesDelay_Timer = 0; } ObjectGuid FactoryDoorGUID; @@ -70,10 +68,12 @@ class instance_deadmines : public InstanceMapScript ObjectGuid DefiasPirate1GUID; ObjectGuid DefiasPirate2GUID; ObjectGuid DefiasCompanionGUID; + ObjectGuid MrSmiteGUID; uint32 State; uint32 CannonBlast_Timer; uint32 PiratesDelay_Timer; + uint32 SmiteAlarmDelay_Timer; ObjectGuid uiSmiteChestGUID; virtual void Update(uint32 diff) override @@ -89,22 +89,20 @@ class instance_deadmines : public InstanceMapScript { case CANNON_GUNPOWDER_USED: CannonBlast_Timer = DATA_CANNON_BLAST_TIMER; - // it's a hack - Mr. Smite should do that but his too far away - //pIronCladDoor->SetName("Mr. Smite"); - //pIronCladDoor->MonsterYell(SAY_MR_SMITE_ALARM1, LANG_UNIVERSAL, NULL); - pIronCladDoor->PlayDirectSound(SOUND_MR_SMITE_ALARM1); State = CANNON_BLAST_INITIATED; break; case CANNON_BLAST_INITIATED: PiratesDelay_Timer = DATA_PIRATES_DELAY_TIMER; + SmiteAlarmDelay_Timer = DATA_SMITE_ALARM_DELAY_TIMER; if (CannonBlast_Timer <= diff) { SummonCreatures(); ShootCannon(); BlastOutDoor(); LeverStucked(); - //pIronCladDoor->MonsterYell(SAY_MR_SMITE_ALARM2, LANG_UNIVERSAL, NULL); - pIronCladDoor->PlayDirectSound(SOUND_MR_SMITE_ALARM2); + instance->LoadGrid(-22.8f, -797.24f); // Loads Mr. Smite's grid. + if (Creature* smite = instance->GetCreature(MrSmiteGUID)) // goes off when door blows up + smite->AI()->Talk(SAY_ALARM1); State = PIRATES_ATTACK; } else CannonBlast_Timer -= diff; break; @@ -112,9 +110,17 @@ class instance_deadmines : public InstanceMapScript if (PiratesDelay_Timer <= diff) { MoveCreaturesInside(); - State = EVENT_DONE; + State = SMITE_ALARMED; } else PiratesDelay_Timer -= diff; break; + case SMITE_ALARMED: + if (SmiteAlarmDelay_Timer <= diff) + { + if (Creature* smite = instance->GetCreature(MrSmiteGUID)) + smite->AI()->Talk(SAY_ALARM2); + State = EVENT_DONE; + } else SmiteAlarmDelay_Timer -= diff; + break; } } @@ -178,6 +184,18 @@ class instance_deadmines : public InstanceMapScript pDoorLever->SetUInt32Value(GAMEOBJECT_FLAGS, 4); } + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_MR_SMITE: + MrSmiteGUID = creature->GetGUID(); + break; + default: + break; + } + } + void OnGameObjectCreate(GameObject* go) override { switch (go->GetEntry()) diff --git a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp index b23b9e645d0..b725f421c8f 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp @@ -44,6 +44,13 @@ EndScriptData */ 11 - Nightbane */ +const Position OptionalSpawn[] = +{ + { -10960.981445f, -1940.138428f, 46.178097f, 4.12f }, // Hyakiss the Lurker + { -10899.903320f, -2085.573730f, 49.474449f, 1.38f }, // Rokad the Ravager + { -10945.769531f, -2040.153320f, 49.474438f, 0.077f } // Shadikith the Glider +}; + class instance_karazhan : public InstanceMapScript { public: @@ -64,6 +71,7 @@ public: // 1 - OZ, 2 - HOOD, 3 - RAJ, this never gets altered. m_uiOperaEvent = urand(1, 3); m_uiOzDeathCount = 0; + OptionalBossCount = 0; } uint32 m_auiEncounter[MAX_ENCOUNTER]; @@ -71,6 +79,7 @@ public: uint32 m_uiOperaEvent; uint32 m_uiOzDeathCount; + uint32 OptionalBossCount; ObjectGuid m_uiCurtainGUID; ObjectGuid m_uiStageDoorLeftGUID; @@ -107,6 +116,29 @@ public: } } + void OnUnitDeath(Unit* unit) override + { + Creature* creature = unit->ToCreature(); + if (!creature) + return; + + switch (creature->GetEntry()) + { + case NPC_COLDMIST_WIDOW: + case NPC_COLDMIST_STALKER: + case NPC_SHADOWBAT: + case NPC_VAMPIRIC_SHADOWBAT: + case NPC_GREATER_SHADOWBAT: + case NPC_PHASE_HOUND: + case NPC_DREADBEAST: + case NPC_SHADOWBEAST: + SetData(TYPE_OPTIONAL_BOSS, NOT_STARTED); + break; + default: + break; + } + } + void SetData(uint32 type, uint32 uiData) override { switch (type) @@ -118,7 +150,28 @@ public: m_auiEncounter[1] = uiData; break; case TYPE_MAIDEN: m_auiEncounter[2] = uiData; break; - case TYPE_OPTIONAL_BOSS: m_auiEncounter[3] = uiData; break; + case TYPE_OPTIONAL_BOSS: + m_auiEncounter[3] = uiData; + if (uiData == NOT_STARTED) + { + ++OptionalBossCount; + if (OptionalBossCount == 50) + { + switch (urand(0, 2)) + { + case 0: + instance->SummonCreature(NPC_HYAKISS_THE_LURKER, OptionalSpawn[0]); + break; + case 1: + instance->SummonCreature(NPC_ROKAD_THE_RAVAGER, OptionalSpawn[1]); + break; + case 2: + instance->SummonCreature(NPC_SHADIKITH_THE_GLIDER, OptionalSpawn[2]); + break; + } + } + } + break; case TYPE_OPERA: m_auiEncounter[4] = uiData; if (uiData == DONE) diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h index db68484c4a8..4d86492257c 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h @@ -64,4 +64,20 @@ enum OperaEvents EVENT_RAJ = 3 }; +enum MiscCreatures +{ + NPC_HYAKISS_THE_LURKER = 16179, + NPC_ROKAD_THE_RAVAGER = 16181, + NPC_SHADIKITH_THE_GLIDER = 16180, + + // Trash + NPC_COLDMIST_WIDOW = 16171, + NPC_COLDMIST_STALKER = 16170, + NPC_SHADOWBAT = 16173, + NPC_VAMPIRIC_SHADOWBAT = 16175, + NPC_GREATER_SHADOWBAT = 16174, + NPC_PHASE_HOUND = 16178, + NPC_DREADBEAST = 16177, + NPC_SHADOWBEAST = 16176 +}; #endif diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index b48f0edec25..6386bb50e1a 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -16,8 +16,10 @@ */ #include "ScriptMgr.h" +#include "ScriptedCreature.h" #include "InstanceScript.h" #include "magisters_terrace.h" +#include "EventMap.h" /* 0 - Selin Fireheart @@ -36,6 +38,8 @@ DoorData const doorData[] = { 0, 0, DOOR_TYPE_ROOM } // END }; +Position const KalecgosSpawnPos = { 164.3747f, -397.1197f, 2.151798f, 1.66219f }; + class instance_magisters_terrace : public InstanceMapScript { public: @@ -93,6 +97,10 @@ class instance_magisters_terrace : public InstanceMapScript case NPC_DELRISSA: DelrissaGUID = creature->GetGUID(); break; + case NPC_KALECGOS: + case NPC_HUMAN_KALECGOS: + KalecgosGUID = creature->GetGUID(); + break; default: break; } @@ -139,6 +147,25 @@ class instance_magisters_terrace : public InstanceMapScript } } + void ProcessEvent(WorldObject* obj, uint32 eventId) override + { + if (eventId == EVENT_SPAWN_KALECGOS) + if (!ObjectAccessor::GetCreature(*obj, KalecgosGUID) && Events.Empty()) + Events.ScheduleEvent(EVENT_SPAWN_KALECGOS, Minutes(1)); + } + + void Update(uint32 diff) override + { + Events.Update(diff); + + if (Events.ExecuteEvent() == EVENT_SPAWN_KALECGOS) + if (Creature* kalecgos = instance->SummonCreature(NPC_KALECGOS, KalecgosSpawnPos)) + { + kalecgos->GetMotionMaster()->MovePath(PATH_KALECGOS_FLIGHT, false); + kalecgos->AI()->Talk(SAY_KALECGOS_SPAWN); + } + } + bool SetBossState(uint32 type, EncounterState state) override { if (!InstanceScript::SetBossState(type, state)) @@ -177,10 +204,12 @@ class instance_magisters_terrace : public InstanceMapScript } protected: + EventMap Events; ObjectGuid SelinGUID; ObjectGuid DelrissaGUID; ObjectGuid KaelStatue[2]; ObjectGuid EscapeOrbGUID; + ObjectGuid KalecgosGUID; uint32 DelrissaDeathCount; }; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp index e216a024468..5b90ac8ccf4 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.cpp @@ -31,7 +31,8 @@ EndContentData */ #include "ScriptedCreature.h" #include "ScriptedGossip.h" #include "Player.h" -#include "SpellInfo.h" +#include "magisters_terrace.h" +#include "EventMap.h" /*###### ## npc_kalecgos @@ -39,30 +40,29 @@ EndContentData */ enum Spells { - SPELL_TRANSFORM_TO_KAEL = 44670, + SPELL_KALECGOS_TRANSFORM = 44670, + SPELL_TRANSFORM_VISUAL = 24085, + SPELL_CAMERA_SHAKE = 44762, SPELL_ORB_KILL_CREDIT = 46307 }; -enum Creatures +enum MovementPoints { - NPC_KAEL = 24848 //human form entry + POINT_ID_PREPARE_LANDING = 6 }; -enum Misc +enum EventIds { - POINT_ID_LAND = 1 + EVENT_KALECGOS_TRANSFORM = 1, + EVENT_KALECGOS_LANDING = 2 }; -const float afKaelLandPoint[] = {225.045f, -276.236f, -5.434f}; - #define GOSSIP_ITEM_KAEL_1 "Who are you?" #define GOSSIP_ITEM_KAEL_2 "What can we do to assist you?" #define GOSSIP_ITEM_KAEL_3 "What brings you to the Sunwell?" #define GOSSIP_ITEM_KAEL_4 "You're not alone here?" #define GOSSIP_ITEM_KAEL_5 "What would Kil'jaeden want with a mortal woman?" -// This is friendly keal that appear after used Orb. -// If we assume DB handle summon, summon appear somewhere outside the platform where Orb is class npc_kalecgos : public CreatureScript { public: @@ -115,52 +115,46 @@ public: struct npc_kalecgosAI : public ScriptedAI { - npc_kalecgosAI(Creature* creature) : ScriptedAI(creature) + npc_kalecgosAI(Creature* creature) : ScriptedAI(creature) { } + + void MovementInform(uint32 type, uint32 pointId) override { - Initialize(); - } - - void Initialize() - { - m_uiTransformTimer = 0; - } - - uint32 m_uiTransformTimer; - - void Reset() override - { - Initialize(); - - // we must assume he appear as dragon somewhere outside the platform of orb, and then move directly to here - if (me->GetEntry() != NPC_KAEL) - me->GetMotionMaster()->MovePoint(POINT_ID_LAND, afKaelLandPoint[0], afKaelLandPoint[1], afKaelLandPoint[2]); - } - - void MovementInform(uint32 uiType, uint32 uiPointId) override - { - if (uiType != POINT_MOTION_TYPE) + if (type != WAYPOINT_MOTION_TYPE) return; - if (uiPointId == POINT_ID_LAND) - m_uiTransformTimer = MINUTE*IN_MILLISECONDS; - } - - void UpdateAI(uint32 uiDiff) override - { - if (m_uiTransformTimer) + if (pointId == POINT_ID_PREPARE_LANDING) { - if (m_uiTransformTimer <= uiDiff) - { - DoCast(me, SPELL_ORB_KILL_CREDIT, true); - - // Transform and update entry, now ready for quest/read gossip - DoCast(me, SPELL_TRANSFORM_TO_KAEL, false); - me->UpdateEntry(NPC_KAEL); - - m_uiTransformTimer = 0; - } else m_uiTransformTimer -= uiDiff; + me->HandleEmoteCommand(EMOTE_ONESHOT_LAND); + me->SetDisableGravity(false); + me->SetHover(false); + events.ScheduleEvent(EVENT_KALECGOS_LANDING, Seconds(2)); } } + + void UpdateAI(uint32 diff) override + { + events.Update(diff); + + switch (events.ExecuteEvent()) + { + case EVENT_KALECGOS_LANDING: + DoCastAOE(SPELL_CAMERA_SHAKE); + me->SetObjectScale(0.6f); + events.ScheduleEvent(EVENT_KALECGOS_TRANSFORM, Seconds(1)); + break; + case EVENT_KALECGOS_TRANSFORM: + DoCast(me, SPELL_ORB_KILL_CREDIT, true); + DoCast(me, SPELL_TRANSFORM_VISUAL, false); + DoCast(me, SPELL_KALECGOS_TRANSFORM, false); + me->UpdateEntry(NPC_HUMAN_KALECGOS); + break; + default: + break; + } + } + + private: + EventMap events; }; }; diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h index 917ad0eb50b..05718dfc1dd 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h @@ -42,7 +42,9 @@ enum CreatureIds { NPC_SELIN = 24723, NPC_DELRISSA = 24560, - NPC_FEL_CRYSTAL = 24722 + NPC_FEL_CRYSTAL = 24722, + NPC_KALECGOS = 24844, + NPC_HUMAN_KALECGOS = 24848 }; enum GameObjectIds @@ -57,4 +59,19 @@ enum GameObjectIds GO_ESCAPE_ORB = 188173 }; +enum InstanceEventIds +{ + EVENT_SPAWN_KALECGOS = 16547 +}; + +enum InstanceText +{ + SAY_KALECGOS_SPAWN = 0 +}; + +enum MovementData +{ + PATH_KALECGOS_FLIGHT = 248440 +}; + #endif diff --git a/src/server/scripts/EasternKingdoms/Uldaman/uldaman.cpp b/src/server/scripts/EasternKingdoms/Uldaman/uldaman.cpp index 622c8f0de01..447dbcd67f9 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/uldaman.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/uldaman.cpp @@ -132,7 +132,10 @@ public: ## at_map_chamber ######*/ -#define QUEST_HIDDEN_CHAMBER 2240 +enum MapChamber +{ + QUEST_HIDDEN_CHAMBER = 2240 +}; class AreaTrigger_at_map_chamber : public AreaTriggerScript { diff --git a/src/server/scripts/EasternKingdoms/zone_undercity.cpp b/src/server/scripts/EasternKingdoms/zone_undercity.cpp index 43143748661..99556587e65 100644 --- a/src/server/scripts/EasternKingdoms/zone_undercity.cpp +++ b/src/server/scripts/EasternKingdoms/zone_undercity.cpp @@ -157,7 +157,7 @@ public: { if (Creature* target = ObjectAccessor::GetCreature(*summoned, targetGUID)) { - target->GetMotionMaster()->MoveJump(target->GetPositionX(), target->GetPositionY(), me->GetPositionZ() + 15.0f, 0); + target->GetMotionMaster()->MoveJump(target->GetPositionX(), target->GetPositionY(), me->GetPositionZ() + 15.0f, me->GetOrientation(), 0); target->SetPosition(target->GetPositionX(), target->GetPositionY(), me->GetPositionZ()+15.0f, 0.0f); summoned->CastSpell(target, SPELL_RIBBON_OF_SOULS, false); } @@ -316,59 +316,6 @@ public: }; }; -/*###### -## npc_parqual_fintallas -######*/ - -enum ParqualFintallas -{ - SPELL_MARK_OF_SHAME = 6767 -}; - -#define GOSSIP_HPF1 "Gul'dan" -#define GOSSIP_HPF2 "Kel'Thuzad" -#define GOSSIP_HPF3 "Ner'zhul" - -class npc_parqual_fintallas : public CreatureScript -{ -public: - npc_parqual_fintallas() : CreatureScript("npc_parqual_fintallas") { } - - 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_MARK_OF_SHAME, false); - } - if (action == GOSSIP_ACTION_INFO_DEF+2) - { - player->CLOSE_GOSSIP_MENU(); - player->AreaExploredOrEventHappens(6628); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(6628) == QUEST_STATUS_INCOMPLETE && !player->HasAura(SPELL_MARK_OF_SHAME)) - { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HPF1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HPF2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HPF3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - player->SEND_GOSSIP_MENU(5822, creature->GetGUID()); - } - else - player->SEND_GOSSIP_MENU(5821, creature->GetGUID()); - - return true; - } -}; - /*###### ## AddSC ######*/ @@ -377,5 +324,4 @@ void AddSC_undercity() { new npc_lady_sylvanas_windrunner(); new npc_highborne_lamenter(); - new npc_parqual_fintallas(); } diff --git a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp index 6cf86c3069f..a01b93a140b 100644 --- a/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp +++ b/src/server/scripts/EasternKingdoms/zone_western_plaguelands.cpp @@ -25,7 +25,6 @@ EndScriptData */ /* ContentData npcs_dithers_and_arbington -npc_myranda_the_hag npc_the_scourge_cauldron npc_andorhal_tower EndContentData */ @@ -33,7 +32,6 @@ EndContentData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" -#include "ScriptedEscortAI.h" #include "Player.h" #include "WorldSession.h" @@ -41,12 +39,23 @@ EndContentData */ ## npcs_dithers_and_arbington ######*/ -#define GOSSIP_HDA1 "What does the Felstone Field Cauldron need?" -#define GOSSIP_HDA2 "What does the Dalson's Tears Cauldron need?" -#define GOSSIP_HDA3 "What does the Writhing Haunt Cauldron need?" -#define GOSSIP_HDA4 "What does the Gahrron's Withering Cauldron need?" - -#define GOSSIP_SDA1 "Thanks, i need a Vitreous Focuser" +enum DithersAndArbington +{ + GOSSIP_ITEM_ID_FELSTONE_FIELD = 0, + GOSSIP_ITEM_ID_DALSON_S_TEARS = 1, + GOSSIP_ITEM_ID_WRITHING_HAUNT = 2, + GOSSIP_ITEM_ID_GAHRRON_S_WITH = 3, + GOSSIP_MENU_ID_LETS_GET_TO_WORK = 3223, + GOSSIP_MENU_ID_VITREOUS_FOCUSER = 3229, + NPC_TEXT_OSSEOUS_AGITATORS = 3980, + NPC_TEXT_SOMATIC_INTENSIFIERS_1 = 3981, + NPC_TEXT_SOMATIC_INTENSIFIERS_2 = 3982, + NPC_TEXT_ECTOPLASMIC_RESONATORS = 3983, + NPC_TEXT_LET_S_GET_TO_WORK = 3985, + QUEST_MISSION_ACCOMPLISHED_H = 5237, + QUEST_MISSION_ACCOMPLISHED_A = 5238, + CREATE_ITEM_VITREOUS_FOCUSER = 17529 +}; class npcs_dithers_and_arbington : public CreatureScript { @@ -62,24 +71,24 @@ public: player->GetSession()->SendListInventory(creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF+1: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SDA1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); - player->SEND_GOSSIP_MENU(3980, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(NPC_TEXT_OSSEOUS_AGITATORS, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF+2: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SDA1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); - player->SEND_GOSSIP_MENU(3981, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(NPC_TEXT_SOMATIC_INTENSIFIERS_1, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF+3: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SDA1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); - player->SEND_GOSSIP_MENU(3982, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(NPC_TEXT_SOMATIC_INTENSIFIERS_2, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF+4: - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SDA1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); - player->SEND_GOSSIP_MENU(3983, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_VITREOUS_FOCUSER, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+5); + player->SEND_GOSSIP_MENU(NPC_TEXT_ECTOPLASMIC_RESONATORS, creature->GetGUID()); break; case GOSSIP_ACTION_INFO_DEF+5: player->CLOSE_GOSSIP_MENU(); - creature->CastSpell(player, 17529, false); + creature->CastSpell(player, CREATE_ITEM_VITREOUS_FOCUSER, false); break; } return true; @@ -93,61 +102,13 @@ public: if (creature->IsVendor()) player->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE); - if (player->GetQuestRewardStatus(5237) || player->GetQuestRewardStatus(5238)) + if (player->GetQuestRewardStatus(QUEST_MISSION_ACCOMPLISHED_H) || player->GetQuestRewardStatus(QUEST_MISSION_ACCOMPLISHED_A)) { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HDA1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HDA2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HDA3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HDA4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); - player->SEND_GOSSIP_MENU(3985, creature->GetGUID()); - } - else - player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); - - return true; - } -}; - -/*###### -## npc_myranda_the_hag -######*/ - -enum Myranda -{ - ILLUSION_GOSSIP = 4773, - QUEST_SUBTERFUGE = 5862, - QUEST_IN_DREAMS = 5944, - SPELL_SCARLET_ILLUSION = 17961 -}; - -class npc_myranda_the_hag : public CreatureScript -{ -public: - npc_myranda_the_hag() : CreatureScript("npc_myranda_the_hag") { } - - 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(); - player->CastSpell(player, SPELL_SCARLET_ILLUSION, false); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (creature->IsQuestGiver()) - player->PrepareQuestMenu(creature->GetGUID()); - - if (player->GetQuestStatus(QUEST_SUBTERFUGE) == QUEST_STATUS_COMPLETE && - player->GetQuestStatus(QUEST_IN_DREAMS) != QUEST_STATUS_COMPLETE && - !player->HasAura(SPELL_SCARLET_ILLUSION)) - { - player->ADD_GOSSIP_ITEM_DB(Player::GetDefaultGossipMenuForSource(creature), 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - player->SEND_GOSSIP_MENU(ILLUSION_GOSSIP, creature->GetGUID()); - return true; + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_FELSTONE_FIELD, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_DALSON_S_TEARS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_WRITHING_HAUNT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_MENU_ID_LETS_GET_TO_WORK, GOSSIP_ITEM_ID_GAHRRON_S_WITH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4); + player->SEND_GOSSIP_MENU(NPC_TEXT_LET_S_GET_TO_WORK, creature->GetGUID()); } else player->SEND_GOSSIP_MENU(player->GetGossipTextId(creature), creature->GetGUID()); @@ -184,7 +145,7 @@ public: me->DealDamage(me, me->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false); //override any database `spawntimesecs` to prevent duplicated summons uint32 rTime = me->GetRespawnDelay(); - if (rTime<600) + if (rTime < 600) me->SetRespawnDelay(600); } @@ -263,7 +224,6 @@ public: } void MoveInLineOfSight(Unit* who) override - { if (!who || who->GetTypeId() != TYPEID_PLAYER) return; @@ -275,14 +235,9 @@ public: }; }; -/*###### -## -######*/ - void AddSC_western_plaguelands() { new npcs_dithers_and_arbington(); - new npc_myranda_the_hag(); new npc_the_scourge_cauldron(); new npc_andorhal_tower(); } diff --git a/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp b/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp index 63c56e29414..29a754d5895 100644 --- a/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp +++ b/src/server/scripts/Kalimdor/WailingCaverns/wailing_caverns.cpp @@ -58,6 +58,10 @@ enum Enums SAY_FAREWELL = 5, SAY_ATTACKED = 11, + GOSSIP_OPTION_LET_EVENT_BEGIN = 201, + NPC_TEXT_NARALEX_SLEEPS_AGAIN = 698, + NPC_TEXT_FANGLORDS_ARE_DEAD = 699, + SPELL_MARK_OF_THE_WILD_RANK_2 = 5232, SPELL_SERPENTINE_CLEANSING = 6270, SPELL_NARALEXS_AWAKENING = 6271, @@ -70,10 +74,6 @@ enum Enums NPC_MUTANUS_THE_DEVOURER = 3654, }; -#define GOSSIP_ID_START_1 698 //Naralex sleeps again! -#define GOSSIP_ID_START_2 699 //The fanglords are dead! -#define GOSSIP_ITEM_NARALEX "Let the event begin!" - class npc_disciple_of_naralex : public CreatureScript { public: @@ -116,8 +116,8 @@ public: if ((instance->GetData(TYPE_LORD_COBRAHN) == DONE) && (instance->GetData(TYPE_LORD_PYTHAS) == DONE) && (instance->GetData(TYPE_LADY_ANACONDRA) == DONE) && (instance->GetData(TYPE_LORD_SERPENTIS) == DONE)) { - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NARALEX, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); - player->SEND_GOSSIP_MENU(GOSSIP_ID_START_2, creature->GetGUID()); + player->ADD_GOSSIP_ITEM_DB(GOSSIP_OPTION_LET_EVENT_BEGIN, 0, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + player->SEND_GOSSIP_MENU(NPC_TEXT_FANGLORDS_ARE_DEAD, creature->GetGUID()); if (!instance->GetData(TYPE_NARALEX_YELLED)) { @@ -127,7 +127,7 @@ public: } else { - player->SEND_GOSSIP_MENU(GOSSIP_ID_START_1, creature->GetGUID()); + player->SEND_GOSSIP_MENU(NPC_TEXT_NARALEX_SLEEPS_AGAIN, creature->GetGUID()); } } return true; diff --git a/src/server/scripts/Kalimdor/zone_ashenvale.cpp b/src/server/scripts/Kalimdor/zone_ashenvale.cpp index 52a83c02a8a..50730507b8f 100644 --- a/src/server/scripts/Kalimdor/zone_ashenvale.cpp +++ b/src/server/scripts/Kalimdor/zone_ashenvale.cpp @@ -31,6 +31,7 @@ EndContentData */ #include "ScriptedCreature.h" #include "ScriptedEscortAI.h" #include "Player.h" +#include "SpellScript.h" /*#### # npc_ruul_snowhoof @@ -344,9 +345,42 @@ class go_naga_brazier : public GameObjectScript } }; +enum KingoftheFoulwealdMisc +{ + GO_BANNER = 178205 +}; + +class spell_destroy_karangs_banner : public SpellScriptLoader +{ + public: + spell_destroy_karangs_banner() : SpellScriptLoader("spell_destroy_karangs_banner") { } + + class spell_destroy_karangs_banner_SpellScript : public SpellScript + { + PrepareSpellScript(spell_destroy_karangs_banner_SpellScript); + + void HandleAfterCast() + { + if (GameObject* banner = GetCaster()->FindNearestGameObject(GO_BANNER, GetSpellInfo()->GetMaxRange(true))) + banner->Delete(); + } + + void Register() override + { + AfterCast += SpellCastFn(spell_destroy_karangs_banner_SpellScript::HandleAfterCast); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_destroy_karangs_banner_SpellScript(); + } +}; + void AddSC_ashenvale() { new npc_ruul_snowhoof(); new npc_muglash(); new go_naga_brazier(); + new spell_destroy_karangs_banner(); } diff --git a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp index fb5642c6f3b..84d7d92acfe 100644 --- a/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp +++ b/src/server/scripts/Northrend/ChamberOfAspects/RubySanctum/boss_general_zarithrian.cpp @@ -135,9 +135,9 @@ class boss_general_zarithrian : public CreatureScript Talk(SAY_KILL); } - bool CanAIAttack(Unit const* /*target*/) const override + bool CanAIAttack(Unit const* target) const override { - return (instance->GetBossState(DATA_SAVIANA_RAGEFIRE) == DONE && instance->GetBossState(DATA_BALTHARUS_THE_WARBORN) == DONE); + return (instance->GetBossState(DATA_SAVIANA_RAGEFIRE) == DONE && instance->GetBossState(DATA_BALTHARUS_THE_WARBORN) == DONE && BossAI::CanAIAttack(target)); } void UpdateAI(uint32 diff) override 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 1e860fb6ec5..6c1b516c7de 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_faction_champions.cpp @@ -461,13 +461,13 @@ class boss_toc_champion_controller : public CreatureScript if (playerTeam == ALLIANCE) { temp->SetHomePosition(vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 0); - temp->GetMotionMaster()->MoveJump(vChampionJumpTarget[pos].GetPositionX(), vChampionJumpTarget[pos].GetPositionY(), vChampionJumpTarget[pos].GetPositionZ(), 20.0f, 20.0f); + temp->GetMotionMaster()->MoveJump(vChampionJumpTarget[pos], 20.0f, 20.0f); temp->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(), 20.0f, 20.0f); + 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); } } 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 83daa6e35a2..e3720503d0a 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp @@ -390,7 +390,7 @@ class npc_snobold_vassal : public CreatureScript else if (Unit* target2 = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, true)) { _targetGUID = target2->GetGUID(); - me->GetMotionMaster()->MoveJump(target2->GetPositionX(), target2->GetPositionY(), target2->GetPositionZ(), 15.0f, 15.0f); + me->GetMotionMaster()->MoveJump(*target2, 15.0f, 15.0f); } } } @@ -981,7 +981,7 @@ class boss_icehowl : public CreatureScript events.ScheduleEvent(EVENT_WHIRL, urand(15*IN_MILLISECONDS, 30*IN_MILLISECONDS)); return; case EVENT_MASSIVE_CRASH: - me->GetMotionMaster()->MoveJump(ToCCommonLoc[1].GetPositionX(), ToCCommonLoc[1].GetPositionY(), ToCCommonLoc[1].GetPositionZ(), 20.0f, 20.0f, 0); // 1: Middle of the room + me->GetMotionMaster()->MoveJump(ToCCommonLoc[1], 20.0f, 20.0f, 0); // 1: Middle of the room SetCombatMovement(false); me->AttackStop(); _stage = 7; //Invalid (Do nothing more than move) @@ -1034,7 +1034,7 @@ class boss_icehowl : public CreatureScript _trampleTargetY = target->GetPositionY(); _trampleTargetZ = target->GetPositionZ(); // 2: Hop Backwards - me->GetMotionMaster()->MoveJump(2*me->GetPositionX() - _trampleTargetX, 2*me->GetPositionY() - _trampleTargetY, me->GetPositionZ(), 30.0f, 20.0f, 0); + me->GetMotionMaster()->MoveJump(2*me->GetPositionX() - _trampleTargetX, 2*me->GetPositionY() - _trampleTargetY, me->GetPositionZ(), me->GetOrientation(), 30.0f, 20.0f, 0); _stage = 7; //Invalid (Do nothing more than move) } else diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp index 7e9083115b8..3ea0de31764 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_deathbringer_saurfang.cpp @@ -598,7 +598,7 @@ class boss_deathbringer_saurfang : public CreatureScript if (target->GetTransport()) return false; - return true; + return BossAI::CanAIAttack(target); } static uint32 const FightWonValue; diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp index 0f721148b72..2db9d206a00 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp @@ -317,14 +317,12 @@ class boss_professor_putricide : public CreatureScript // no possible aura seen in sniff adding the aurastate summon->ModifyAuraState(AURA_STATE_UNKNOWN22, true); summon->CastSpell(summon, SPELL_GASEOUS_BLOAT_PROC, true); - summon->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); summon->SetReactState(REACT_PASSIVE); break; case NPC_VOLATILE_OOZE: // no possible aura seen in sniff adding the aurastate summon->ModifyAuraState(AURA_STATE_UNKNOWN19, true); summon->CastSpell(summon, SPELL_OOZE_ERUPTION_SEARCH_PERIODIC, true); - summon->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); summon->SetReactState(REACT_PASSIVE); break; case NPC_CHOKING_GAS_BOMB: 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 535c2293ce1..2a859c99ff9 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp @@ -285,6 +285,7 @@ enum Phases #define PHASE_TWO_THREE (events.IsInPhase(PHASE_TWO) ? PHASE_TWO : PHASE_THREE) Position const CenterPosition = {503.6282f, -2124.655f, 840.8569f, 0.0f}; +Position const TirionSpawn = {505.2118f, -2124.353f, 840.9403f, 3.141593f}; Position const TirionIntro = {489.2970f, -2124.840f, 840.8569f, 0.0f}; Position const TirionCharge = {482.9019f, -2124.479f, 840.8570f, 0.0f}; Position const LichKingIntro[3] = @@ -493,13 +494,33 @@ class boss_the_lich_king : public CreatureScript _vileSpiritExplosions = 0; } - void Reset() override + void InitializeAI() override + { + SetupEncounter(); + } + + void JustRespawned() override + { + SetupEncounter(); + } + + void SetupEncounter() { _Reset(); me->SetReactState(REACT_PASSIVE); events.SetPhase(PHASE_INTRO); Initialize(); SetEquipmentSlots(true); + + // Reset The Frozen Throne gameobjects + FrozenThroneResetWorker reset; + Trinity::GameObjectWorker worker(me, reset); + me->VisitNearbyGridObject(333.0f, worker); + + // Reset any light override + me->GetMap()->SetZoneOverrideLight(AREA_THE_FROZEN_THRONE, 0, 5000); + + me->SummonCreature(NPC_HIGHLORD_TIRION_FORDRING_LK, TirionSpawn, TEMPSUMMON_MANUAL_DESPAWN); } void JustDied(Unit* /*killer*/) override @@ -535,40 +556,21 @@ class boss_the_lich_king : public CreatureScript events.ScheduleEvent(EVENT_SHADOW_TRAP, 15500, 0, PHASE_ONE); } - void JustReachedHome() override - { - _JustReachedHome(); - instance->SetBossState(DATA_THE_LICH_KING, NOT_STARTED); - - // Reset The Frozen Throne gameobjects - FrozenThroneResetWorker reset; - Trinity::GameObjectWorker worker(me, reset); - me->VisitNearbyGridObject(333.0f, worker); - - // Restore Tirion's gossip only after The Lich King fully resets to prevent - // restarting the encounter while LK still runs back to spawn point - if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HIGHLORD_TIRION_FORDRING))) - tirion->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - - // Reset any light override - me->GetMap()->SetZoneOverrideLight(AREA_THE_FROZEN_THRONE, 0, 5000); - } - bool CanAIAttack(Unit const* target) const override { // The Lich King must not select targets in frostmourne room if he killed everyone outside - return !target->HasAura(SPELL_IN_FROSTMOURNE_ROOM); + return !target->HasAura(SPELL_IN_FROSTMOURNE_ROOM) && BossAI::CanAIAttack(target); } - void EnterEvadeMode(EvadeReason why) override + void EnterEvadeMode(EvadeReason /*why*/) override { - instance->SetBossState(DATA_THE_LICH_KING, FAIL); - BossAI::EnterEvadeMode(why); if (Creature* tirion = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_HIGHLORD_TIRION_FORDRING))) - tirion->AI()->EnterEvadeMode(); + tirion->DespawnOrUnsummon(); DoCastAOE(SPELL_KILL_FROSTMOURNE_PLAYERS); EntryCheckPredicate pred(NPC_STRANGULATE_VEHICLE); summons.DoAction(ACTION_TELEPORT_BACK, pred); + summons.DespawnAll(); + _DespawnAtEvade(); } void KilledUnit(Unit* victim) override @@ -749,6 +751,8 @@ class boss_the_lich_king : public CreatureScript case NPC_STRANGULATE_VEHICLE: summons.Summon(summon); return; + case NPC_HIGHLORD_TIRION_FORDRING_LK: + return; default: break; } @@ -1132,6 +1136,7 @@ class npc_tirion_fordring_tft : public CreatureScript _events.Reset(); if (_instance->GetBossState(DATA_THE_LICH_KING) == DONE) me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + me->LoadEquipment(1); } void MovementInform(uint32 type, uint32 id) override diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp index 81f1e071da0..be12894ebea 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gothik.cpp @@ -375,8 +375,12 @@ class boss_gothik : public CreatureScript _gateIsOpen = true; for (ObjectGuid summonGuid : summons) + { if (Creature* summon = ObjectAccessor::GetCreature(*me, summonGuid)) summon->AI()->DoAction(ACTION_GATE_OPENED); + if (summons.empty()) // ACTION_GATE_OPENED may cause an evade, despawning summons and invalidating our iterator + break; + } } void DamageTaken(Unit* /*who*/, uint32& damage) override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp index 2caa6dfc960..d0fd8d5544e 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_algalon_the_observer.cpp @@ -979,6 +979,9 @@ class go_celestial_planetarium_access : public GameObjectScript bool GossipHello(Player* player) override { + if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE)) + return true; + bool hasKey = true; if (LockEntry const* lock = sLockStore.LookupEntry(go->GetGOInfo()->goober.lockId)) { diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp index 6e4b6ea22c9..fcf129a9b07 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_flame_leviathan.cpp @@ -1186,6 +1186,7 @@ class npc_brann_bronzebeard_ulduar_intro : public CreatureScript { if (menuId == GOSSIP_MENU_BRANN_BRONZEBEARD && gossipListId == GOSSIP_OPTION_BRANN_BRONZEBEARD) { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); player->PlayerTalkClass->SendCloseGossip(); if (Creature* loreKeeper = _instance->GetCreature(DATA_LORE_KEEPER_OF_NORGANNON)) loreKeeper->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); @@ -1238,6 +1239,7 @@ class npc_lorekeeper : public CreatureScript { if (menuId == GOSSIP_MENU_LORE_KEEPER && gossipListId == GOSSIP_OPTION_LORE_KEEPER) { + me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); player->PlayerTalkClass->SendCloseGossip(); _instance->instance->LoadGrid(364, -16); // make sure leviathan is loaded @@ -1250,6 +1252,7 @@ class npc_lorekeeper : public CreatureScript { if (Creature* brann = _instance->GetCreature(DATA_BRANN_BRONZEBEARD_INTRO)) { + brann->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); delorah->GetMotionMaster()->MovePoint(0, brann->GetPositionX() - 4, brann->GetPositionY(), brann->GetPositionZ()); /// @todo delorah->AI()->Talk(xxxx, brann->GetGUID()); when reached at branz } @@ -1765,9 +1768,7 @@ class spell_vehicle_throw_passenger : public SpellScriptLoader else { passenger->ExitVehicle(); - float x, y, z; - targets.GetDstPos()->GetPosition(x, y, z); - passenger->GetMotionMaster()->MoveJump(x, y, z, targets.GetSpeedXY(), targets.GetSpeedZ()); + passenger->GetMotionMaster()->MoveJump(*targets.GetDstPos(), targets.GetSpeedXY(), targets.GetSpeedZ()); } } } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index df5877d9220..05beacca638 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -222,7 +222,6 @@ class npc_iron_roots : public CreatureScript { SetCombatMovement(false); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); me->ApplySpellImmune(0, IMMUNITY_ID, 49560, true); // Death Grip me->setFaction(14); me->SetReactState(REACT_PASSIVE); 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 385f7d6a69d..09d95b34521 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp @@ -380,7 +380,6 @@ class npc_saronite_vapors : public CreatureScript { Talk(EMOTE_VAPORS); instance = me->GetInstanceScript(); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); me->ApplySpellImmune(0, IMMUNITY_ID, 49560, true); // Death Grip jump effect me->SetReactState(REACT_PASSIVE); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp index 820332791c8..e694433c614 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp @@ -444,7 +444,7 @@ class boss_mimiron : public CreatureScript DoCastAOE(SPELL_DESPAWN_ASSAULT_BOTS); me->ExitVehicle(); // ExitVehicle() offset position is not implemented, so we make up for that with MoveJump()... - me->GetMotionMaster()->MoveJump(me->GetPositionX() + (10.f * std::cos(me->GetOrientation())), me->GetPositionY() + (10.f * std::sin(me->GetOrientation())), me->GetPositionZ(), 10.f, 5.f); + me->GetMotionMaster()->MoveJump(me->GetPositionX() + (10.f * std::cos(me->GetOrientation())), me->GetPositionY() + (10.f * std::sin(me->GetOrientation())), me->GetPositionZ(), me->GetOrientation(), 10.f, 5.f); events.ScheduleEvent(EVENT_OUTTRO_1, 7000); } @@ -1634,8 +1634,11 @@ class go_mimiron_hardmode_button : public GameObjectScript public: go_mimiron_hardmode_button() : GameObjectScript("go_mimiron_hardmode_button") { } - bool OnGossipHello(Player* /*player*/, GameObject* go) + bool OnGossipHello(Player* /*player*/, GameObject* go) override { + if (go->HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE)) + return true; + InstanceScript* instance = go->GetInstanceScript(); if (!instance) return false; diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp index b72bcbecdac..86a4a9caf3a 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardeKeep/boss_ingvar_the_plunderer.cpp @@ -110,30 +110,24 @@ class boss_ingvar_the_plunderer : public CreatureScript { if (me->GetEntry() != NPC_INGVAR) me->UpdateEntry(NPC_INGVAR); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); _Reset(); - events.SetPhase(PHASE_HUMAN); - - events.ScheduleEvent(EVENT_CLEAVE, urand(6, 12)*IN_MILLISECONDS, 0, PHASE_HUMAN); - events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18, 21)*IN_MILLISECONDS, 0, PHASE_HUMAN); - events.ScheduleEvent(EVENT_ENRAGE, urand(7, 14)*IN_MILLISECONDS, 0, PHASE_HUMAN); - events.ScheduleEvent(EVENT_SMASH, urand(12, 17)*IN_MILLISECONDS, 0, PHASE_HUMAN); } void DamageTaken(Unit* /*doneBy*/, uint32& damage) override { if (damage >= me->GetHealth() && events.IsInPhase(PHASE_HUMAN)) { + events.SetPhase(PHASE_EVENT); + events.ScheduleEvent(EVENT_SUMMON_BANSHEE, 3 * IN_MILLISECONDS, 0, PHASE_EVENT); + me->RemoveAllAuras(); + me->StopMoving(); DoCast(me, SPELL_INGVAR_FEIGN_DEATH, true); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); - events.SetPhase(PHASE_EVENT); - events.ScheduleEvent(EVENT_SUMMON_BANSHEE, 3 * IN_MILLISECONDS, 0, PHASE_EVENT); - Talk(SAY_DEATH); } @@ -152,13 +146,28 @@ class boss_ingvar_the_plunderer : public CreatureScript me->RemoveAura(SPELL_INGVAR_FEIGN_DEATH); DoCast(me, SPELL_INGVAR_TRANSFORM, true); me->UpdateEntry(NPC_INGVAR_UNDEAD); - events.ScheduleEvent(EVENT_JUST_TRANSFORMED, 2 * IN_MILLISECONDS, 0, PHASE_EVENT); + events.ScheduleEvent(EVENT_JUST_TRANSFORMED, IN_MILLISECONDS / 2, 0, PHASE_EVENT); } void EnterCombat(Unit* /*who*/) override { + if (events.IsInPhase(PHASE_EVENT) || events.IsInPhase(PHASE_UNDEAD)) // ingvar gets multiple EnterCombat calls + return; _EnterCombat(); + Talk(SAY_AGGRO); + events.SetPhase(PHASE_HUMAN); + events.ScheduleEvent(EVENT_CLEAVE, urand(6, 12)*IN_MILLISECONDS, 0, PHASE_HUMAN); + events.ScheduleEvent(EVENT_STAGGERING_ROAR, urand(18, 21)*IN_MILLISECONDS, 0, PHASE_HUMAN); + events.ScheduleEvent(EVENT_ENRAGE, urand(7, 14)*IN_MILLISECONDS, 0, PHASE_HUMAN); + events.ScheduleEvent(EVENT_SMASH, urand(12, 17)*IN_MILLISECONDS, 0, PHASE_HUMAN); + } + + void AttackStart(Unit* who) override + { + if (events.IsInPhase(PHASE_EVENT)) // prevent ingvar from beginning to attack/chase during transition + return; + BossAI::AttackStart(who); } void JustDied(Unit* /*killer*/) override @@ -171,7 +180,7 @@ class boss_ingvar_the_plunderer : public CreatureScript { events.SetPhase(PHASE_UNDEAD); events.ScheduleEvent(EVENT_DARK_SMASH, urand(14, 18)*IN_MILLISECONDS, 0, PHASE_UNDEAD); - events.ScheduleEvent(EVENT_DREADFUL_ROAR, urand(18, 22)*IN_MILLISECONDS, 0, PHASE_UNDEAD); + events.ScheduleEvent(EVENT_DREADFUL_ROAR, 0, 0, PHASE_UNDEAD); events.ScheduleEvent(EVENT_WOE_STRIKE, urand(10, 14)*IN_MILLISECONDS, 0, PHASE_UNDEAD); events.ScheduleEvent(EVENT_SHADOW_AXE, 30*IN_MILLISECONDS, 0, PHASE_UNDEAD); } @@ -214,9 +223,17 @@ class boss_ingvar_the_plunderer : public CreatureScript events.ScheduleEvent(EVENT_SMASH, urand(12, 16)*IN_MILLISECONDS, 0, PHASE_HUMAN); break; case EVENT_JUST_TRANSFORMED: - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); - DoZoneInCombat(); ScheduleSecondPhase(); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_NOT_SELECTABLE); + if (Unit* target = me->getThreatManager().getHostilTarget()) + AttackStart(target); + else + { + EnterEvadeMode(EVADE_REASON_NO_HOSTILES); + return; + } + Talk(SAY_AGGRO); + DoZoneInCombat(); return; case EVENT_SUMMON_BANSHEE: DoCast(me, SPELL_SUMMON_BANSHEE); diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp index 77b376bf7d5..d7b65093898 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp @@ -238,7 +238,7 @@ public: me->SetInCombatWithZone(); instance->SetBossState(DATA_SKADI_THE_RUTHLESS, IN_PROGRESS); instance->DoStartTimedAchievement(ACHIEVEMENT_TIMED_TYPE_EVENT, ACHIEV_TIMED_START_EVENT); - me->GetMotionMaster()->MoveJump(Location[0].GetPositionX(), Location[0].GetPositionY(), Location[0].GetPositionZ(), 5.0f, 10.0f); + me->GetMotionMaster()->MoveJump(Location[0], 5.0f, 10.0f); me->SetWalk(false); m_uiMountTimer = 1000; Summons.DespawnEntry(NPC_GRAUF); @@ -289,7 +289,7 @@ public: pGrauf->GetMotionMaster()->MoveFall(); pGrauf->HandleEmoteCommand(EMOTE_ONESHOT_FLYDEATH); } - me->GetMotionMaster()->MoveJump(Location[4].GetPositionX(), Location[4].GetPositionY(), Location[4].GetPositionZ(), 5.0f, 10.0f); + me->GetMotionMaster()->MoveJump(Location[4], 5.0f, 10.0f); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE); Talk(SAY_DRAKE_DEATH); m_uiCrushTimer = 8000; diff --git a/src/server/scripts/Northrend/isle_of_conquest.cpp b/src/server/scripts/Northrend/isle_of_conquest.cpp index 9648097f7c8..c99977de841 100644 --- a/src/server/scripts/Northrend/isle_of_conquest.cpp +++ b/src/server/scripts/Northrend/isle_of_conquest.cpp @@ -206,7 +206,7 @@ class spell_ioc_parachute_ic : public SpellScriptLoader class StartLaunchEvent : public BasicEvent { public: - StartLaunchEvent(float x, float y, float z, ObjectGuid::LowType lowGuid) : _x(x), _y(y), _z(z), _lowGuid(lowGuid) + StartLaunchEvent(Position const& pos, ObjectGuid::LowType lowGuid) : _pos(pos), _lowGuid(lowGuid) { } @@ -218,15 +218,15 @@ class StartLaunchEvent : public BasicEvent player->AddAura(SPELL_LAUNCH_NO_FALLING_DAMAGE, player); // prevents falling damage float speedZ = 10.0f; - float dist = player->GetExactDist2d(_x, _y); + float dist = player->GetExactDist2d(&_pos); player->ExitVehicle(); - player->GetMotionMaster()->MoveJump(_x, _y, _z, dist, speedZ); + player->GetMotionMaster()->MoveJump(_pos, dist, speedZ, EVENT_JUMP, true); return true; } private: - float _x, _y, _z; + Position _pos; ObjectGuid::LowType _lowGuid; }; @@ -244,11 +244,7 @@ class spell_ioc_launch : public SpellScriptLoader if (!GetCaster()->ToCreature() || !GetExplTargetDest()) return; - float x, y, z; - x = GetExplTargetDest()->GetPositionX(); - y = GetExplTargetDest()->GetPositionY(); - z = GetExplTargetDest()->GetPositionZ(); - GetCaster()->ToCreature()->m_Events.AddEvent(new StartLaunchEvent(x, y, z, GetHitPlayer()->GetGUID().GetCounter()), GetCaster()->ToCreature()->m_Events.CalculateTime(2500)); + GetCaster()->ToCreature()->m_Events.AddEvent(new StartLaunchEvent(*GetExplTargetDest(), GetHitPlayer()->GetGUID().GetCounter()), GetCaster()->ToCreature()->m_Events.CalculateTime(2500)); } void Register() override diff --git a/src/server/scripts/Northrend/zone_icecrown.cpp b/src/server/scripts/Northrend/zone_icecrown.cpp index 88217cb384e..1e020edd10a 100644 --- a/src/server/scripts/Northrend/zone_icecrown.cpp +++ b/src/server/scripts/Northrend/zone_icecrown.cpp @@ -213,7 +213,6 @@ class npc_tournament_training_dummy : public CreatureScript void Reset() override { me->SetControlled(true, UNIT_STATE_STUNNED); - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true); Initialize(); // Cast Defend spells to max stack size diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp index e5812390bd2..be27932e6b4 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_kaelthas.cpp @@ -144,6 +144,7 @@ enum Spells // Thaladred the Darkener spells SPELL_PSYCHIC_BLOW = 10689, SPELL_SILENCE = 30225, + SPELL_REND = 36965, // Lord Sanguinar spells SPELL_BELLOWING_ROAR = 40636, // Grand Astromancer Capernian spells @@ -881,11 +882,13 @@ class boss_thaladred_the_darkener : public CreatureScript { Gaze_Timer = 100; Silence_Timer = 20000; + Rend_Timer = 4000; PsychicBlow_Timer = 10000; } uint32 Gaze_Timer; uint32 Silence_Timer; + uint32 Rend_Timer; uint32 PsychicBlow_Timer; void Reset() override @@ -939,6 +942,15 @@ class boss_thaladred_the_darkener : public CreatureScript else Silence_Timer -= diff; + //Rend_Timer + if (Rend_Timer <= diff) + { + DoCastVictim(SPELL_REND); + Rend_Timer = 4000; + } + else + Rend_Timer -= diff; + //PsychicBlow_Timer if (PsychicBlow_Timer <= diff) { diff --git a/src/server/scripts/Pet/pet_generic.cpp b/src/server/scripts/Pet/pet_generic.cpp index 35455bc474b..0ec6f08ae58 100644 --- a/src/server/scripts/Pet/pet_generic.cpp +++ b/src/server/scripts/Pet/pet_generic.cpp @@ -20,10 +20,181 @@ * Scriptnames of files in this file should be prefixed with "npc_pet_gen_". */ + /* ContentData + npc_pet_gen_egbert 100% Egbert run's around + npc_pet_gen_pandaren_monk 100% Pandaren Monk drinks and bows with you + npc_pet_gen_mojo 100% Mojo follows you when you kiss it + EndContentData */ + #include "ScriptMgr.h" #include "ScriptedCreature.h" +#include "PassiveAI.h" #include "Player.h" +enum EgbertMisc +{ + SPELL_EGBERT = 40669, + EVENT_RETURN = 3 +}; + +class npc_pet_gen_egbert : public CreatureScript +{ +public: + npc_pet_gen_egbert() : CreatureScript("npc_pet_gen_egbert") {} + + struct npc_pet_gen_egbertAI : public NullCreatureAI + { + npc_pet_gen_egbertAI(Creature* creature) : NullCreatureAI(creature) + { + if (Unit* owner = me->GetCharmerOrOwner()) + if (owner->GetMap()->GetEntry()->addon > 1) + me->SetCanFly(true); + } + + void Reset() override + { + _events.Reset(); + if (Unit* owner = me->GetCharmerOrOwner()) + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle()); + } + + void EnterEvadeMode(EvadeReason why) override + { + if (!_EnterEvadeMode(why)) + return; + + Reset(); + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (Unit* owner = me->GetCharmerOrOwner()) + { + if (!me->IsWithinDist(owner, 40.f)) + { + me->RemoveAura(SPELL_EGBERT); + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, me->GetFollowAngle()); + } + } + + if (me->HasAura(SPELL_EGBERT)) + _events.ScheduleEvent(EVENT_RETURN, urandms(5, 20)); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_RETURN: + me->RemoveAura(SPELL_EGBERT); + break; + default: + break; + } + } + } + private: + EventMap _events; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_pet_gen_egbertAI(creature); + } +}; + +enum PandarenMonkMisc +{ + SPELL_PANDAREN_MONK = 69800, + EVENT_FOCUS = 1, + EVENT_EMOTE = 2, + EVENT_FOLLOW = 3, + EVENT_DRINK = 4 +}; + +class npc_pet_gen_pandaren_monk : public CreatureScript +{ +public: + npc_pet_gen_pandaren_monk() : CreatureScript("npc_pet_gen_pandaren_monk") {} + + struct npc_pet_gen_pandaren_monkAI : public NullCreatureAI + { + npc_pet_gen_pandaren_monkAI(Creature* creature) : NullCreatureAI(creature) { } + + void Reset() override + { + _events.Reset(); + _events.ScheduleEvent(EVENT_FOCUS, 1000); + } + + void EnterEvadeMode(EvadeReason why) override + { + if (!_EnterEvadeMode(why)) + return; + + Reset(); + } + + void ReceiveEmote(Player* /*player*/, uint32 emote) override + { + me->InterruptSpell(CURRENT_CHANNELED_SPELL); + me->StopMoving(); + + switch (emote) + { + case TEXT_EMOTE_BOW: + _events.ScheduleEvent(EVENT_FOCUS, 1000); + break; + case TEXT_EMOTE_DRINK: + _events.ScheduleEvent(EVENT_DRINK, 1000); + break; + } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + if (Unit* owner = me->GetCharmerOrOwner()) + if (!me->IsWithinDist(owner, 30.f)) + me->InterruptSpell(CURRENT_CHANNELED_SPELL); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_FOCUS: + if (Unit* owner = me->GetCharmerOrOwner()) + me->SetFacingToObject(owner); + _events.ScheduleEvent(EVENT_EMOTE, 1000); + break; + case EVENT_EMOTE: + me->HandleEmoteCommand(EMOTE_ONESHOT_BOW); + _events.ScheduleEvent(EVENT_FOLLOW, 1000); + break; + case EVENT_FOLLOW: + if (Unit* owner = me->GetCharmerOrOwner()) + me->GetMotionMaster()->MoveFollow(owner, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE); + break; + case EVENT_DRINK: + me->CastSpell(me, SPELL_PANDAREN_MONK, false); + break; + default: + break; + } + } + } + private: + EventMap _events; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new npc_pet_gen_pandaren_monkAI(creature); + } +}; + enum Mojo { SAY_MOJO = 0, @@ -89,5 +260,7 @@ class npc_pet_gen_mojo : public CreatureScript void AddSC_generic_pet_scripts() { + new npc_pet_gen_egbert(); + new npc_pet_gen_pandaren_monk(); new npc_pet_gen_mojo(); } diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index 5e2d1b64eac..01c8c6efc6e 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -1083,6 +1083,37 @@ class spell_dru_swift_flight_passive : public SpellScriptLoader } }; +// -33943 - Flight Form +class spell_dru_flight_form : public SpellScriptLoader +{ + public: + spell_dru_flight_form() : SpellScriptLoader("spell_dru_flight_form") { } + + class spell_dru_flight_form_SpellScript : public SpellScript + { + PrepareSpellScript(spell_dru_flight_form_SpellScript); + + SpellCastResult CheckCast() + { + Unit* caster = GetCaster(); + if (caster->IsInDisallowedMountForm()) + return SPELL_FAILED_NOT_SHAPESHIFT; + + return SPELL_CAST_OK; + } + + void Register() override + { + OnCheckCast += SpellCheckCastFn(spell_dru_flight_form_SpellScript::CheckCast); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_dru_flight_form_SpellScript(); + } +}; + // 5217 - Tiger's Fury class spell_dru_tiger_s_fury : public SpellScriptLoader { @@ -1289,6 +1320,7 @@ void AddSC_druid_spell_scripts() new spell_dru_stampede(); new spell_dru_survival_instincts(); new spell_dru_swift_flight_passive(); + new spell_dru_flight_form(); new spell_dru_tiger_s_fury(); new spell_dru_typhoon(); new spell_dru_t10_restoration_4p_bonus(); diff --git a/src/server/scripts/Spells/spell_holiday.cpp b/src/server/scripts/Spells/spell_holiday.cpp index 7fe1f54a594..a3359fdf6f9 100644 --- a/src/server/scripts/Spells/spell_holiday.cpp +++ b/src/server/scripts/Spells/spell_holiday.cpp @@ -29,6 +29,7 @@ #include "GridNotifiers.h" #include "GridNotifiersImpl.h" #include "CellImpl.h" +#include "Vehicle.h" // 45102 Romantic Picnic enum SpellsPicnic @@ -410,6 +411,84 @@ class spell_pilgrims_bounty_buff_food : public SpellScriptLoader } }; +enum FeastOnSpells +{ + FEAST_ON_TURKEY = 61784, + FEAST_ON_CRANBERRIES = 61785, + FEAST_ON_SWEET_POTATOES = 61786, + FEAST_ON_PIE = 61787, + FEAST_ON_STUFFING = 61788, + SPELL_CRANBERRY_HELPINS = 61841, + SPELL_TURKEY_HELPINS = 61842, + SPELL_STUFFING_HELPINS = 61843, + SPELL_SWEET_POTATO_HELPINS = 61844, + SPELL_PIE_HELPINS = 61845, + SPELL_ON_PLATE_EAT_VISUAL = 61826 +}; + +class spell_pilgrims_bounty_feast_on : public SpellScriptLoader +{ + public: + spell_pilgrims_bounty_feast_on() : SpellScriptLoader("spell_pilgrims_bounty_feast_on") { } + + class spell_pilgrims_bounty_feast_on_SpellScript : public SpellScript + { + PrepareSpellScript(spell_pilgrims_bounty_feast_on_SpellScript); + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + + uint32 _spellId = 0; + switch (GetSpellInfo()->Id) + { + case FEAST_ON_TURKEY: + _spellId = SPELL_TURKEY_HELPINS; + break; + case FEAST_ON_CRANBERRIES: + _spellId = SPELL_CRANBERRY_HELPINS; + break; + case FEAST_ON_SWEET_POTATOES: + _spellId = SPELL_SWEET_POTATO_HELPINS; + break; + case FEAST_ON_PIE: + _spellId = SPELL_PIE_HELPINS; + break; + case FEAST_ON_STUFFING: + _spellId = SPELL_STUFFING_HELPINS; + break; + default: + return; + } + + if (Vehicle* vehicle = caster->GetVehicleKit()) + if (Unit* target = vehicle->GetPassenger(0)) + if (Player* player = target->ToPlayer()) + { + player->CastSpell(player, SPELL_ON_PLATE_EAT_VISUAL, true); + caster->CastSpell(player, _spellId, true, NULL, NULL, player->GetGUID()); + } + + if (Aura* aura = caster->GetAura(GetEffectValue())) + { + if (aura->GetStackAmount() == 1) + caster->RemoveAurasDueToSpell(aura->GetSpellInfo()->Effects[EFFECT_0].CalcValue()); + aura->ModStackAmount(-1); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_pilgrims_bounty_feast_on_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_pilgrims_bounty_feast_on_SpellScript(); + } +}; + enum TheTurkinator { SPELL_KILL_COUNTER_VISUAL = 62015, @@ -429,7 +508,7 @@ class spell_pilgrims_bounty_turkey_tracker : public SpellScriptLoader { PrepareSpellScript(spell_pilgrims_bounty_turkey_tracker_SpellScript); - bool Validate(SpellInfo const* /*spell*/) + bool Validate(SpellInfo const* /*spell*/) override { if (!sSpellMgr->GetSpellInfo(SPELL_KILL_COUNTER_VISUAL) || !sSpellMgr->GetSpellInfo(SPELL_KILL_COUNTER_VISUAL_MAX)) return false; @@ -472,18 +551,90 @@ class spell_pilgrims_bounty_turkey_tracker : public SpellScriptLoader } } - void Register() + void Register() override { OnEffectHitTarget += SpellEffectFn(spell_pilgrims_bounty_turkey_tracker_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); } }; - SpellScript* GetSpellScript() const + SpellScript* GetSpellScript() const override { return new spell_pilgrims_bounty_turkey_tracker_SpellScript(); } }; +enum SpiritOfSharing +{ + SPELL_THE_SPIRIT_OF_SHARING = 61849 +}; + +class spell_pilgrims_bounty_well_fed : public SpellScriptLoader +{ + private: + uint32 _triggeredSpellId; + + public: + spell_pilgrims_bounty_well_fed(const char* name, uint32 triggeredSpellId) : SpellScriptLoader(name), _triggeredSpellId(triggeredSpellId) { } + + class spell_pilgrims_bounty_well_fed_SpellScript : public SpellScript + { + PrepareSpellScript(spell_pilgrims_bounty_well_fed_SpellScript); + private: + uint32 _triggeredSpellId; + + public: + spell_pilgrims_bounty_well_fed_SpellScript(uint32 triggeredSpellId) : SpellScript(), _triggeredSpellId(triggeredSpellId) { } + + bool Validate(SpellInfo const* /*spell*/) override + { + if (!sSpellMgr->GetSpellInfo(_triggeredSpellId)) + return false; + return true; + } + + void HandleScript(SpellEffIndex effIndex) + { + PreventHitDefaultEffect(effIndex); + Player* target = GetHitPlayer(); + if (!target) + return; + + if (Aura const* aura = target->GetAura(GetSpellInfo()->Id)) + { + if (aura->GetStackAmount() == 5) + target->CastSpell(target, _triggeredSpellId, true); + } + + Aura const* turkey = target->GetAura(SPELL_TURKEY_HELPINS); + Aura const* cranberies = target->GetAura(SPELL_CRANBERRY_HELPINS); + Aura const* stuffing = target->GetAura(SPELL_STUFFING_HELPINS); + Aura const* sweetPotatoes = target->GetAura(SPELL_SWEET_POTATO_HELPINS); + Aura const* pie = target->GetAura(SPELL_PIE_HELPINS); + + if ((turkey && turkey->GetStackAmount() == 5) && (cranberies && cranberies->GetStackAmount() == 5) && (stuffing && stuffing->GetStackAmount() == 5) + && (sweetPotatoes && sweetPotatoes->GetStackAmount() == 5) && (pie && pie->GetStackAmount() == 5)) + { + target->CastSpell(target, SPELL_THE_SPIRIT_OF_SHARING, true); + target->RemoveAurasDueToSpell(SPELL_TURKEY_HELPINS); + target->RemoveAurasDueToSpell(SPELL_CRANBERRY_HELPINS); + target->RemoveAurasDueToSpell(SPELL_STUFFING_HELPINS); + target->RemoveAurasDueToSpell(SPELL_SWEET_POTATO_HELPINS); + target->RemoveAurasDueToSpell(SPELL_PIE_HELPINS); + } + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_pilgrims_bounty_well_fed_SpellScript::HandleScript, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_pilgrims_bounty_well_fed_SpellScript(_triggeredSpellId); + } +}; + enum Mistletoe { SPELL_CREATE_MISTLETOE = 26206, @@ -1047,6 +1198,12 @@ void AddSC_holiday_spell_scripts() new spell_pilgrims_bounty_buff_food("spell_gen_spice_bread_stuffing", SPELL_WELL_FED_HIT_TRIGGER); new spell_pilgrims_bounty_buff_food("spell_gen_pumpkin_pie", SPELL_WELL_FED_SPIRIT_TRIGGER); new spell_pilgrims_bounty_buff_food("spell_gen_candied_sweet_potato", SPELL_WELL_FED_HASTE_TRIGGER); + new spell_pilgrims_bounty_feast_on(); + new spell_pilgrims_bounty_well_fed("spell_pilgrims_bounty_well_fed_turkey", SPELL_WELL_FED_AP_TRIGGER); + new spell_pilgrims_bounty_well_fed("spell_pilgrims_bounty_well_fed_cranberry", SPELL_WELL_FED_ZM_TRIGGER); + new spell_pilgrims_bounty_well_fed("spell_pilgrims_bounty_well_fed_stuffing", SPELL_WELL_FED_HIT_TRIGGER); + new spell_pilgrims_bounty_well_fed("spell_pilgrims_bounty_well_fed_sweet_potatoes", SPELL_WELL_FED_HASTE_TRIGGER); + new spell_pilgrims_bounty_well_fed("spell_pilgrims_bounty_well_fed_pie", SPELL_WELL_FED_SPIRIT_TRIGGER); new spell_pilgrims_bounty_turkey_tracker(); // Winter Veil new spell_winter_veil_mistletoe(); diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 8c255a7a48e..a372adead55 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -29,6 +29,7 @@ #include "SpellAuraEffects.h" #include "SkillDiscovery.h" #include "Battleground.h" +#include "DBCStores.h" // Generic script for handling item dummy effects which trigger another spell. class spell_item_trigger_spell : public SpellScriptLoader @@ -2585,6 +2586,43 @@ public: } }; +class spell_item_toy_train_set_pulse : public SpellScriptLoader +{ +public: + spell_item_toy_train_set_pulse() : SpellScriptLoader("spell_item_toy_train_set_pulse") { } + + class spell_item_toy_train_set_pulse_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_toy_train_set_pulse_SpellScript); + + void HandleDummy(SpellEffIndex /*index*/) + { + if (Player* target = GetHitUnit()->ToPlayer()) + { + target->HandleEmoteCommand(EMOTE_ONESHOT_TRAIN); + if (EmotesTextSoundEntry const* soundEntry = FindTextSoundEmoteFor(TEXT_EMOTE_TRAIN, target->getRace(), target->getGender())) + target->PlayDistanceSound(soundEntry->SoundId); + } + } + + void HandleTargets(std::list& targetList) + { + targetList.remove_if([](WorldObject const* obj) { return obj->GetTypeId() != TYPEID_PLAYER; }); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_toy_train_set_pulse_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_item_toy_train_set_pulse_SpellScript::HandleTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ALLY); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_toy_train_set_pulse_SpellScript(); + } +}; + void AddSC_item_spell_scripts() { // 23074 Arcanite Dragonling @@ -2652,4 +2690,5 @@ void AddSC_item_spell_scripts() new spell_item_chicken_cover(); new spell_item_muisek_vessel(); new spell_item_greatmothers_soulcatcher(); + new spell_item_toy_train_set_pulse(); } diff --git a/src/server/scripts/World/duel_reset.cpp b/src/server/scripts/World/duel_reset.cpp index c5b53368bc8..3c46255a1bf 100644 --- a/src/server/scripts/World/duel_reset.cpp +++ b/src/server/scripts/World/duel_reset.cpp @@ -34,9 +34,8 @@ class DuelResetScript : public PlayerScript player1->GetSpellHistory()->SaveCooldownStateBeforeDuel(); player2->GetSpellHistory()->SaveCooldownStateBeforeDuel(); - - ResetSpellCooldowns(player1); - ResetSpellCooldowns(player2); + ResetSpellCooldowns(player1, true); + ResetSpellCooldowns(player2, true); } // Health and mana reset @@ -73,9 +72,8 @@ class DuelResetScript : public PlayerScript // Cooldown restore if (sWorld->getBoolConfig(CONFIG_RESET_DUEL_COOLDOWNS)) { - - ResetSpellCooldowns(winner); - ResetSpellCooldowns(loser); + ResetSpellCooldowns(winner, false); + ResetSpellCooldowns(loser, false); winner->GetSpellHistory()->RestoreCooldownStateAfterDuel(); loser->GetSpellHistory()->RestoreCooldownStateAfterDuel(); @@ -98,14 +96,35 @@ class DuelResetScript : public PlayerScript } } - static void ResetSpellCooldowns(Player* player) + static void ResetSpellCooldowns(Player* player, bool onStartDuel) { - // remove cooldowns on spells that have < 10 min CD and has no onHold - player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool + if (onStartDuel) { - SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); - return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS && !itr->second.OnHold; - }, true); + // remove cooldowns on spells that have < 10 min CD > 30 sec and has no onHold + player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool + { + SpellHistory::Clock::time_point now = SpellHistory::Clock::now(); + uint32 cooldownDuration = itr->second.CooldownEnd > now ? std::chrono::duration_cast(itr->second.CooldownEnd - now).count() : 0; + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); + return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS + && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS + && !itr->second.OnHold + && cooldownDuration > 0 + && ( spellInfo->RecoveryTime - cooldownDuration ) > (MINUTE / 2) * IN_MILLISECONDS + && ( spellInfo->CategoryRecoveryTime - cooldownDuration ) > (MINUTE / 2) * IN_MILLISECONDS; + }, true); + } + else + { + // remove cooldowns on spells that have < 10 min CD and has no onHold + player->GetSpellHistory()->ResetCooldowns([](SpellHistory::CooldownStorageType::iterator itr) -> bool + { + SpellInfo const* spellInfo = sSpellMgr->EnsureSpellInfo(itr->first); + return spellInfo->RecoveryTime < 10 * MINUTE * IN_MILLISECONDS + && spellInfo->CategoryRecoveryTime < 10 * MINUTE * IN_MILLISECONDS + && !itr->second.OnHold; + }, true); + } // pet cooldowns if (Pet* pet = player->GetPet()) diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 3a36ad9dfad..b79bd482a02 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -41,6 +41,7 @@ go_tadpole_cage go_amberpine_outhouse go_hive_pod go_veil_skith_cage +go_toy_train_set EndContentData */ #include "ScriptMgr.h" @@ -1125,6 +1126,48 @@ public: } }; + +enum ToyTrainSpells +{ + SPELL_TOY_TRAIN_PULSE = 61551, +}; + +class go_toy_train_set : public GameObjectScript +{ + public: + go_toy_train_set() : GameObjectScript("go_toy_train_set") { } + + struct go_toy_train_setAI : public GameObjectAI + { + go_toy_train_setAI(GameObject* go) : GameObjectAI(go), _pulseTimer(3 * IN_MILLISECONDS) { } + + void UpdateAI(uint32 diff) override + { + if (diff < _pulseTimer) + _pulseTimer -= diff; + else + { + go->CastSpell(nullptr, SPELL_TOY_TRAIN_PULSE, true); + _pulseTimer = 6 * IN_MILLISECONDS; + } + } + + // triggered on wrecker'd + void DoAction(int32 /*action*/) override + { + go->Delete(); + } + + private: + uint32 _pulseTimer; + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_toy_train_setAI(go); + } +}; + void AddSC_go_scripts() { new go_cat_figurine(); @@ -1160,4 +1203,5 @@ void AddSC_go_scripts() new go_veil_skith_cage(); new go_frostblade_shrine(); new go_midsummer_bonfire(); + new go_toy_train_set(); } diff --git a/src/server/scripts/World/npc_professions.cpp b/src/server/scripts/World/npc_professions.cpp index dbdd23c2703..cc7e5c208c5 100644 --- a/src/server/scripts/World/npc_professions.cpp +++ b/src/server/scripts/World/npc_professions.cpp @@ -18,27 +18,26 @@ /* ScriptData SDName: Npc_Professions -SD%Complete: 80 -SDComment: Provides learn/unlearn/relearn-options for professions. Not supported: Unlearn engineering, re-learn engineering, re-learn leatherworking. -SDCategory: NPCs +SD%Complete: 100 +SDComment: Provides learn/unlearn/relearn-options for professions. +SDCategory: NPCs/GOBs EndScriptData */ #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" +#include "GameObjectAI.h" #include "Player.h" #include "SpellInfo.h" #include "WorldSession.h" /* A few notes for future developement: -- A full implementation of gossip for GO's is required. They must have the same scripting capabilities as creatures. Basically, -there is no difference here (except that default text is chosen with `gameobject_template`.`data3` (for GO type2, different dataN for a few others) - It's possible blacksmithing still require some tweaks and adjustments due to the way we _have_ to use reputation. */ /*### -# to be removed from here (->ncp_text). This is data for database projects. +# to be removed from here (->npc_text). This is data for database projects. ###*/ #define TALK_MUST_UNLEARN_WEAPON "You must forget your weapon type specialty before I can help you. Go to Everlook in Winterspring and seek help there." @@ -153,6 +152,9 @@ enum ProfessionSpells S_LEARN_GOBLIN = 20221, S_LEARN_GNOMISH = 20220, + S_UNLEARN_GOBLIN = 68334, + S_UNLEARN_GNOMISH = 68333, + S_SPELLFIRE = 26797, S_MOONCLOTH = 26798, S_SHADOWEAVE = 26801, @@ -376,6 +378,27 @@ void ProfessionUnlearnSpells(Player* player, uint32 type) player->RemoveSpell(36075); // Wildfeather Leggings player->RemoveSpell(36078); // Living Crystal Breastplate break; + case S_UNLEARN_GOBLIN: // S_UNLEARN_GOBLIN + player->RemoveSpell(30565); // Foreman's Enchanted Helmet + player->RemoveSpell(30566); // Foreman's Reinforced Helmet + player->RemoveSpell(30563); // Goblin Rocket Launcher + player->RemoveSpell(56514); // Global Thermal Sapper Charge + player->RemoveSpell(36954); // Dimensional Ripper - Area 52 + player->RemoveSpell(23486); // Dimensional Ripper - Everlook + player->RemoveSpell(23078); // Goblin Jumper Cables XL + player->RemoveSpell(72952); // Shatter Rounds + break; + case S_UNLEARN_GNOMISH: // S_UNLEARN_GNOMISH + player->RemoveSpell(30575); // Gnomish Battle Goggles + player->RemoveSpell(30574); // Gnomish Power Goggles + player->RemoveSpell(56473); // Gnomish X-Ray Specs + player->RemoveSpell(30569); // Gnomish Poultryizer + player->RemoveSpell(30563); // Ultrasafe Transporter - Toshley's Station + player->RemoveSpell(23489); // Ultrasafe Transporter - Gadgetzan + player->RemoveSpell(23129); // World Enlarger + player->RemoveSpell(23096); // Gnomish Alarm-o-Bot + player->RemoveSpell(72953); // Iceblade Arrow + break; case S_UNLEARN_SPELLFIRE: // S_UNLEARN_SPELLFIRE player->RemoveSpell(26752); // Spellfire Belt player->RemoveSpell(26753); // Spellfire Gloves @@ -923,6 +946,76 @@ public: } }; +// Object ID - 177226 +// Book "Soothsaying for dummies" +enum SoothsayingForDummies +{ + GOSSIP_ID = 7058, + + // Engineering + OPTION_UNLEARN_GNOMISH = 0, + OPTION_UNLEARN_GOBLIN = 1, + OPTION_LEARN_GNOMISH = 2, + OPTION_LEARN_GOBLIN = 3, + + // Leatherworking + OPTION_LEARN_DRAGONSCALE = 4, + OPTION_LEARN_ELEMENTAL = 5, + OPTION_LEARN_TRIBAL = 6 +}; + +class go_soothsaying_for_dummies : public GameObjectScript +{ + public: + go_soothsaying_for_dummies() : GameObjectScript("go_soothsaying_for_dummies") { } + + struct go_soothsaying_for_dummiesAI : public GameObjectAI + { + go_soothsaying_for_dummiesAI(GameObject* go) : GameObjectAI(go) { } + + bool GossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId != GOSSIP_ID) + return false; + + switch (gossipListId) + { + case OPTION_UNLEARN_GNOMISH: + ProcessUnlearnAction(player, nullptr, S_UNLEARN_GNOMISH, 0, 0); // cost is handled by gossip code + break; + case OPTION_UNLEARN_GOBLIN: + ProcessUnlearnAction(player, nullptr, S_UNLEARN_GOBLIN, 0, 0); + break; + case OPTION_LEARN_GNOMISH: + player->CastSpell(player, S_LEARN_GNOMISH, true); + break; + case OPTION_LEARN_GOBLIN: + player->CastSpell(player, S_LEARN_GOBLIN, true); + break; + case OPTION_LEARN_DRAGONSCALE: + player->CastSpell(player, S_LEARN_DRAGON, true); + break; + case OPTION_LEARN_ELEMENTAL: + player->CastSpell(player, S_LEARN_ELEMENTAL, true); + break; + case OPTION_LEARN_TRIBAL: + player->CastSpell(player, S_LEARN_TRIBAL, true); + break; + default: + return false; + } + + player->CLOSE_GOSSIP_MENU(); + return true; // prevent further processing + } + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return new go_soothsaying_for_dummiesAI(go); + } +}; + /*### # start menues leatherworking ###*/ @@ -1212,6 +1305,7 @@ void AddSC_npc_professions() new npc_prof_alchemy(); new npc_prof_blacksmith(); new npc_engineering_tele_trinket(); + new go_soothsaying_for_dummies(); new npc_prof_leather(); new npc_prof_tailor(); } diff --git a/src/server/scripts/World/npcs_special.cpp b/src/server/scripts/World/npcs_special.cpp index d532f91b874..e975a4f70e9 100644 --- a/src/server/scripts/World/npcs_special.cpp +++ b/src/server/scripts/World/npcs_special.cpp @@ -38,6 +38,7 @@ 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 */ #include "ScriptMgr.h" @@ -512,6 +513,67 @@ public: } }; +/*###### +## npc_torch_tossing_target_bunny_controller +######*/ + +enum TorchTossingTarget +{ + NPC_TORCH_TOSSING_TARGET_BUNNY = 25535, + SPELL_TARGET_INDICATOR = 45723 +}; + +class npc_torch_tossing_target_bunny_controller : public CreatureScript +{ +public: + npc_torch_tossing_target_bunny_controller() : CreatureScript("npc_torch_tossing_target_bunny_controller") { } + + struct npc_torch_tossing_target_bunny_controllerAI : public ScriptedAI + { + npc_torch_tossing_target_bunny_controllerAI(Creature* creature) : ScriptedAI(creature) + { + _targetTimer = 3000; + } + + ObjectGuid DoSearchForTargets(ObjectGuid lastTargetGUID) + { + std::list targets; + me->GetCreatureListWithEntryInGrid(targets, NPC_TORCH_TOSSING_TARGET_BUNNY, 60.0f); + targets.remove_if([lastTargetGUID](Creature* creature) { return creature->GetGUID() == lastTargetGUID; }); + + if (!targets.empty()) + { + _lastTargetGUID = Trinity::Containers::SelectRandomContainerElement(targets)->GetGUID(); + + return _lastTargetGUID; + } + return ObjectGuid::Empty; + } + + void UpdateAI(uint32 diff) override + { + if (_targetTimer < diff) + { + if (Unit* target = ObjectAccessor::GetUnit(*me, DoSearchForTargets(_lastTargetGUID))) + target->CastSpell(target, SPELL_TARGET_INDICATOR, true); + + _targetTimer = 3000; + } + else + _targetTimer -= diff; + } + + private: + uint32 _targetTimer; + ObjectGuid _lastTargetGUID; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_torch_tossing_target_bunny_controllerAI(creature); + } +}; + /*###### ## Triage quest ######*/ @@ -1416,7 +1478,6 @@ public: void Reset() override { me->SetControlled(true, UNIT_STATE_STUNNED);//disable rotate - me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_KNOCK_BACK, true);//imune to knock aways like blast wave _events.Reset(); _damageTimes.clear(); @@ -2124,7 +2185,7 @@ public: // Check if we are near Elune'ara lake south, if so try to summon Omen or a minion if (me->GetZoneId() == ZONE_MOONGLADE) { - if (!me->FindNearestCreature(NPC_OMEN, 100.0f, false) && me->GetDistance2d(omenSummonPos.GetPositionX(), omenSummonPos.GetPositionY()) <= 100.0f) + if (!me->FindNearestCreature(NPC_OMEN, 100.0f) && me->GetDistance2d(omenSummonPos.GetPositionX(), omenSummonPos.GetPositionY()) <= 100.0f) { switch (urand(0, 9)) { @@ -2381,12 +2442,136 @@ class npc_stable_master : public CreatureScript } }; +enum TrainWrecker +{ + GO_TOY_TRAIN = 193963, + SPELL_TOY_TRAIN_PULSE = 61551, + SPELL_WRECK_TRAIN = 62943, + ACTION_WRECKED = 1, + EVENT_DO_JUMP = 1, + EVENT_DO_FACING = 2, + EVENT_DO_WRECK = 3, + EVENT_DO_DANCE = 4, + MOVEID_CHASE = 1, + MOVEID_JUMP = 2 +}; +class npc_train_wrecker : public CreatureScript +{ + public: + npc_train_wrecker() : CreatureScript("npc_train_wrecker") { } + + struct npc_train_wreckerAI : public NullCreatureAI + { + npc_train_wreckerAI(Creature* creature) : NullCreatureAI(creature), _isSearching(true), _nextAction(0), _timer(1 * IN_MILLISECONDS) { } + + GameObject* VerifyTarget() const + { + if (GameObject* target = ObjectAccessor::GetGameObject(*me, _target)) + return target; + me->HandleEmoteCommand(EMOTE_ONESHOT_RUDE); + me->DespawnOrUnsummon(3 * IN_MILLISECONDS); + return nullptr; + } + + void UpdateAI(uint32 diff) override + { + if (_isSearching) + { + if (diff < _timer) + _timer -= diff; + else + { + if (GameObject* target = me->FindNearestGameObject(GO_TOY_TRAIN, 15.0f)) + { + _isSearching = false; + _target = target->GetGUID(); + me->SetWalk(true); + me->GetMotionMaster()->MovePoint(MOVEID_CHASE, target->GetNearPosition(3.0f, target->GetAngle(me))); + } + else + _timer = 3 * IN_MILLISECONDS; + } + } + else + { + switch (_nextAction) + { + case EVENT_DO_JUMP: + if (GameObject* target = VerifyTarget()) + me->GetMotionMaster()->MoveJump(*target, 5.0, 10.0, MOVEID_JUMP); + _nextAction = 0; + break; + case EVENT_DO_FACING: + if (GameObject* target = VerifyTarget()) + { + me->SetFacingTo(target->GetOrientation()); + me->HandleEmoteCommand(EMOTE_ONESHOT_ATTACK1H); + _timer = 1.5 * IN_MILLISECONDS; + _nextAction = EVENT_DO_WRECK; + } + else + _nextAction = 0; + break; + case EVENT_DO_WRECK: + if (diff < _timer) + { + _timer -= diff; + break; + } + if (GameObject* target = VerifyTarget()) + { + me->CastSpell(target, SPELL_WRECK_TRAIN, false); + target->AI()->DoAction(ACTION_WRECKED); + _timer = 2 * IN_MILLISECONDS; + _nextAction = EVENT_DO_DANCE; + } + else + _nextAction = 0; + break; + case EVENT_DO_DANCE: + if (diff < _timer) + { + _timer -= diff; + break; + } + me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_DANCE); + me->DespawnOrUnsummon(5 * IN_MILLISECONDS); + _nextAction = 0; + break; + default: + break; + } + } + } + + void MovementInform(uint32 /*type*/, uint32 id) override + { + if (id == MOVEID_CHASE) + _nextAction = EVENT_DO_JUMP; + else if (id == MOVEID_JUMP) + _nextAction = EVENT_DO_FACING; + } + + private: + bool _isSearching; + uint8 _nextAction; + uint32 _timer; + ObjectGuid _target; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_train_wreckerAI(creature); + } +}; + void AddSC_npcs_special() { new npc_air_force_bots(); new npc_lunaclaw_spirit(); new npc_chicken_cluck(); new npc_dancing_flames(); + new npc_torch_tossing_target_bunny_controller(); new npc_doctor(); new npc_injured_patient(); new npc_garments_of_quests(); @@ -2404,4 +2589,5 @@ void AddSC_npcs_special() new npc_spring_rabbit(); new npc_imp_in_a_ball(); new npc_stable_master(); + new npc_train_wrecker(); } diff --git a/src/server/shared/Networking/AsyncAcceptor.h b/src/server/shared/Networking/AsyncAcceptor.h index d02284d3f28..e54854eac81 100644 --- a/src/server/shared/Networking/AsyncAcceptor.h +++ b/src/server/shared/Networking/AsyncAcceptor.h @@ -20,34 +20,39 @@ #include "Log.h" #include +#include using boost::asio::ip::tcp; class AsyncAcceptor { public: - typedef void(*ManagerAcceptHandler)(tcp::socket&& newSocket); + typedef void(*AcceptCallback)(tcp::socket&& newSocket, uint32 threadIndex); AsyncAcceptor(boost::asio::io_service& ioService, std::string const& bindIp, uint16 port) : _acceptor(ioService, tcp::endpoint(boost::asio::ip::address::from_string(bindIp), port)), - _socket(ioService), _closed(false) + _socket(ioService), _closed(false), _socketFactory(std::bind(&AsyncAcceptor::DefeaultSocketFactory, this)) { } - template + template void AsyncAccept(); - void AsyncAcceptManaged(ManagerAcceptHandler mgrHandler) + template + void AsyncAcceptWithCallback() { - _acceptor.async_accept(_socket, [this, mgrHandler](boost::system::error_code error) + tcp::socket* socket; + uint32 threadIndex; + std::tie(socket, threadIndex) = _socketFactory(); + _acceptor.async_accept(*socket, [this, socket, threadIndex](boost::system::error_code error) { if (!error) { try { - _socket.non_blocking(true); + socket->non_blocking(true); - mgrHandler(std::move(_socket)); + acceptCallback(std::move(*socket), threadIndex); } catch (boost::system::system_error const& err) { @@ -55,7 +60,7 @@ public: } } if (!_closed) - AsyncAcceptManaged(mgrHandler); + this->AsyncAcceptWithCallback(); }); } @@ -68,10 +73,15 @@ public: _acceptor.close(err); } + void SetSocketFactory(std::function()> func) { _socketFactory = func; } + private: + std::pair DefeaultSocketFactory() { return std::make_pair(&_socket, 0); } + tcp::acceptor _acceptor; tcp::socket _socket; std::atomic _closed; + std::function()> _socketFactory; }; template diff --git a/src/server/shared/Networking/MessageBuffer.h b/src/server/shared/Networking/MessageBuffer.h index 189a56f18b6..d68bee181b1 100644 --- a/src/server/shared/Networking/MessageBuffer.h +++ b/src/server/shared/Networking/MessageBuffer.h @@ -105,7 +105,7 @@ public: return std::move(_storage); } - MessageBuffer& operator=(MessageBuffer& right) + MessageBuffer& operator=(MessageBuffer const& right) { if (this != &right) { diff --git a/src/server/shared/Networking/NetworkThread.h b/src/server/shared/Networking/NetworkThread.h index ac216838bce..5eb2fcb2f6a 100644 --- a/src/server/shared/Networking/NetworkThread.h +++ b/src/server/shared/Networking/NetworkThread.h @@ -22,6 +22,8 @@ #include "Errors.h" #include "Log.h" #include "Timer.h" +#include +#include #include #include #include @@ -29,11 +31,14 @@ #include #include +using boost::asio::ip::tcp; + template class NetworkThread { public: - NetworkThread() : _connections(0), _stopped(false), _thread(nullptr) + NetworkThread() : _connections(0), _stopped(false), _thread(nullptr), + _acceptSocket(_io_service), _updateTimer(_io_service) { } @@ -50,6 +55,7 @@ public: void Stop() { _stopped = true; + _io_service.stop(); } bool Start() @@ -80,10 +86,12 @@ public: std::lock_guard lock(_newSocketsLock); ++_connections; - _newSockets.insert(sock); + _newSockets.push_back(sock); SocketAdded(sock); } + tcp::socket* GetSocketForAccept() { return &_acceptSocket; } + protected: virtual void SocketAdded(std::shared_ptr /*sock*/) { } virtual void SocketRemoved(std::shared_ptr /*sock*/) { } @@ -95,16 +103,15 @@ protected: if (_newSockets.empty()) return; - for (typename SocketSet::const_iterator i = _newSockets.begin(); i != _newSockets.end(); ++i) + for (std::shared_ptr sock : _newSockets) { - if (!(*i)->IsOpen()) + if (!sock->IsOpen()) { - SocketRemoved(*i); - + SocketRemoved(sock); --_connections; } else - _Sockets.insert(*i); + _sockets.push_back(sock); } _newSockets.clear(); @@ -114,53 +121,58 @@ protected: { TC_LOG_DEBUG("misc", "Network Thread Starting"); - typename SocketSet::iterator i, t; - - uint32 sleepTime = 10; - uint32 tickStart = 0, diff = 0; - while (!_stopped) - { - std::this_thread::sleep_for(std::chrono::milliseconds(sleepTime)); - - tickStart = getMSTime(); - - AddNewSockets(); - - for (i = _Sockets.begin(); i != _Sockets.end();) - { - if (!(*i)->Update()) - { - if ((*i)->IsOpen()) - (*i)->CloseSocket(); - - SocketRemoved(*i); - - --_connections; - _Sockets.erase(i++); - } - else - ++i; - } - - diff = GetMSTimeDiffToNow(tickStart); - sleepTime = diff > 10 ? 0 : 10 - diff; - } + _updateTimer.expires_from_now(boost::posix_time::milliseconds(10)); + _updateTimer.async_wait(std::bind(&NetworkThread::Update, this)); + _io_service.run(); TC_LOG_DEBUG("misc", "Network Thread exits"); + _newSockets.clear(); + _sockets.clear(); + } + + void Update() + { + if (_stopped) + return; + + _updateTimer.expires_from_now(boost::posix_time::milliseconds(10)); + _updateTimer.async_wait(std::bind(&NetworkThread::Update, this)); + + AddNewSockets(); + + _sockets.erase(std::remove_if(_sockets.begin(), _sockets.end(), [this](std::shared_ptr sock) + { + if (!sock->Update()) + { + if (sock->IsOpen()) + sock->CloseSocket(); + + SocketRemoved(sock); + + --_connections; + return true; + } + + return false; + }), _sockets.end()); } private: - typedef std::set > SocketSet; + typedef std::vector> SocketContainer; std::atomic _connections; std::atomic _stopped; std::thread* _thread; - SocketSet _Sockets; + SocketContainer _sockets; std::mutex _newSocketsLock; - SocketSet _newSockets; + SocketContainer _newSockets; + + boost::asio::io_service _io_service; + tcp::socket _acceptSocket; + boost::asio::deadline_timer _updateTimer; }; #endif // NetworkThread_h__ diff --git a/src/server/shared/Networking/Socket.h b/src/server/shared/Networking/Socket.h index a2f57b5029e..d1ba7f49aa4 100644 --- a/src/server/shared/Networking/Socket.h +++ b/src/server/shared/Networking/Socket.h @@ -21,15 +21,11 @@ #include "MessageBuffer.h" #include "Log.h" #include -#include -#include #include #include #include #include #include -#include -#include using boost::asio::ip::tcp; @@ -63,14 +59,10 @@ public: return false; #ifndef TC_SOCKET_USE_IOCP - std::unique_lock guard(_writeLock); - if (!guard) + if (_isWritingAsync || _writeQueue.empty()) return true; - if (_isWritingAsync || (!_writeBuffer.GetActiveSize() && _writeQueue.empty())) - return true; - - for (; WriteHandler(guard);) + for (; HandleQueue();) ; #endif @@ -98,14 +90,12 @@ public: std::bind(&Socket::ReadHandlerInternal, this->shared_from_this(), std::placeholders::_1, std::placeholders::_2)); } - void QueuePacket(MessageBuffer&& buffer, std::unique_lock& guard) + void QueuePacket(MessageBuffer&& buffer) { _writeQueue.push(std::move(buffer)); #ifdef TC_SOCKET_USE_IOCP - AsyncProcessQueue(guard); -#else - (void)guard; + AsyncProcessQueue(); #endif } @@ -135,7 +125,7 @@ protected: virtual void ReadHandler() = 0; - bool AsyncProcessQueue(std::unique_lock&) + bool AsyncProcessQueue() { if (_isWritingAsync) return false; @@ -154,14 +144,6 @@ protected: return false; } - std::mutex _writeLock; - std::queue _writeQueue; -#ifndef TC_SOCKET_USE_IOCP - MessageBuffer _writeBuffer; -#endif - - boost::asio::io_service& io_service() { return _socket.get_io_service(); } - private: void ReadHandlerInternal(boost::system::error_code error, size_t transferredBytes) { @@ -181,15 +163,13 @@ private: { if (!error) { - std::unique_lock deleteGuard(_writeLock); - _isWritingAsync = false; _writeQueue.front().ReadCompleted(transferedBytes); if (!_writeQueue.front().GetActiveSize()) _writeQueue.pop(); if (!_writeQueue.empty()) - AsyncProcessQueue(deleteGuard); + AsyncProcessQueue(); else if (_closing) CloseSocket(); } @@ -201,48 +181,15 @@ private: void WriteHandlerWrapper(boost::system::error_code /*error*/, std::size_t /*transferedBytes*/) { - std::unique_lock guard(_writeLock); _isWritingAsync = false; - WriteHandler(guard); + HandleQueue(); } - bool WriteHandler(std::unique_lock& guard) + bool HandleQueue() { if (!IsOpen()) return false; - std::size_t bytesToSend = _writeBuffer.GetActiveSize(); - - if (bytesToSend == 0) - return HandleQueue(guard); - - boost::system::error_code error; - std::size_t bytesWritten = _socket.write_some(boost::asio::buffer(_writeBuffer.GetReadPointer(), bytesToSend), error); - - if (error) - { - if (error == boost::asio::error::would_block || error == boost::asio::error::try_again) - return AsyncProcessQueue(guard); - - return false; - } - else if (bytesWritten == 0) - return false; - else if (bytesWritten < bytesToSend) - { - _writeBuffer.ReadCompleted(bytesWritten); - _writeBuffer.Normalize(); - return AsyncProcessQueue(guard); - } - - // now bytesWritten == bytesToSend - _writeBuffer.Reset(); - - return HandleQueue(guard); - } - - bool HandleQueue(std::unique_lock& guard) - { if (_writeQueue.empty()) return false; @@ -256,7 +203,7 @@ private: if (error) { if (error == boost::asio::error::would_block || error == boost::asio::error::try_again) - return AsyncProcessQueue(guard); + return AsyncProcessQueue(); _writeQueue.pop(); return false; @@ -269,7 +216,7 @@ private: else if (bytesSent < bytesToSend) // now n > 0 { queuedMessage.ReadCompleted(bytesSent); - return AsyncProcessQueue(guard); + return AsyncProcessQueue(); } _writeQueue.pop(); @@ -284,6 +231,7 @@ private: uint16 _remotePort; MessageBuffer _readBuffer; + std::queue _writeQueue; std::atomic _closed; std::atomic _closing; diff --git a/src/server/shared/Networking/SocketMgr.h b/src/server/shared/Networking/SocketMgr.h index d92d2ea4154..acefa46a6de 100644 --- a/src/server/shared/Networking/SocketMgr.h +++ b/src/server/shared/Networking/SocketMgr.h @@ -81,6 +81,12 @@ public: _threadCount = 0; Wait(); + + delete _acceptor; + _acceptor = nullptr; + delete[] _threads; + _threads = nullptr; + _threadCount = 0; } void Wait() @@ -90,20 +96,14 @@ public: _threads[i].Wait(); } - virtual void OnSocketOpen(tcp::socket&& sock) + virtual void OnSocketOpen(tcp::socket&& sock, uint32 threadIndex) { - size_t min = 0; - - for (int32 i = 1; i < _threadCount; ++i) - if (_threads[i].GetConnectionCount() < _threads[min].GetConnectionCount()) - min = i; - try { std::shared_ptr newSocket = std::make_shared(std::move(sock)); newSocket->Start(); - _threads[min].AddSocket(newSocket); + _threads[threadIndex].AddSocket(newSocket); } catch (boost::system::system_error const& err) { @@ -113,6 +113,23 @@ public: int32 GetNetworkThreadCount() const { return _threadCount; } + uint32 SelectThreadWithMinConnections() const + { + uint32 min = 0; + + for (int32 i = 1; i < _threadCount; ++i) + if (_threads[i].GetConnectionCount() < _threads[min].GetConnectionCount()) + min = i; + + return min; + } + + std::pair GetSocketForAccept() + { + uint32 threadIndex = SelectThreadWithMinConnections(); + return std::make_pair(_threads[threadIndex].GetSocketForAccept(), threadIndex); + } + protected: SocketMgr() : _acceptor(nullptr), _threads(nullptr), _threadCount(1) { diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index cb5407b9750..cf6ad103b25 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -406,6 +406,24 @@ CleanCharacterDB = 0 PersistentCharacterCleanFlags = 0 +# +# Auction.GetAllScanDelay +# Description: Sets the minimum time in seconds, a single player character can perform a getall scan. +# The value is only held in memory so a server restart will clear it. +# Setting this to zero, will disable GetAll functions completely. +# Default: 900 - (GetAll scan limited to once every 15mins per player character) + +Auction.GetAllScanDelay = 900 + +# +# Auction.SearchDelay +# Description: Sets the minimum time in milliseconds (seconds x 1000), that the client must wait between +# auction search operations. This can be increased if somehow Auction House activity is causing +# too much load. +# Default: 300 - (Time delay between auction searches set to 0.3secs) + +Auction.SearchDelay = 300 + # ################################################################################################### @@ -1312,10 +1330,11 @@ AllowTwoSide.Trade = 0 # # TalentsInspecting -# Description: Allow inspecting characters from the opposing faction. -# Doesn't affect characters in gamemaster mode. -# Default: 1 - (Enabled) -# 0 - (Disabled) +# Description: Allow/disallow inspecting other characters' talents. +# Doesn't affect game master accounts. +# 2 - (Enabled for all characters) +# Default: 1 - (Enabled for characters of the same faction) +# 0 - (Talent inspecting is disabled) TalentsInspecting = 1 diff --git a/src/tools/map_extractor/CMakeLists.txt b/src/tools/map_extractor/CMakeLists.txt index 8ecc9b72173..c4ea3a9435a 100644 --- a/src/tools/map_extractor/CMakeLists.txt +++ b/src/tools/map_extractor/CMakeLists.txt @@ -16,6 +16,7 @@ include_directories ( ${CMAKE_SOURCE_DIR}/src/server/shared ${CMAKE_SOURCE_DIR}/dep/cppformat ${CMAKE_SOURCE_DIR}/dep/StormLib/src + ${CMAKE_SOURCE_DIR}/dep/g3dlite/include ${CMAKE_SOURCE_DIR}/src/common ${CMAKE_SOURCE_DIR}/src/common/Utilities ${CMAKE_SOURCE_DIR}/src/server/shared @@ -31,6 +32,7 @@ add_executable(mapextractor target_link_libraries(mapextractor common format + g3dlib ${BZIP2_LIBRARIES} ${ZLIB_LIBRARIES} ${Boost_LIBRARIES} diff --git a/src/tools/map_extractor/adt.cpp b/src/tools/map_extractor/adt.cpp index c8aa7adf9ba..7a900640d61 100644 --- a/src/tools/map_extractor/adt.cpp +++ b/src/tools/map_extractor/adt.cpp @@ -22,15 +22,15 @@ #include // Helper -int holetab_h[4] = {0x1111, 0x2222, 0x4444, 0x8888}; -int holetab_v[4] = {0x000F, 0x00F0, 0x0F00, 0xF000}; +int holetab_h[4] = { 0x1111, 0x2222, 0x4444, 0x8888 }; +int holetab_v[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 }; -u_map_fcc MHDRMagic = { {'R','D','H','M'} }; -u_map_fcc MCINMagic = { {'N','I','C','M'} }; -u_map_fcc MH2OMagic = { {'O','2','H','M'} }; -u_map_fcc MCNKMagic = { {'K','N','C','M'} }; -u_map_fcc MCVTMagic = { {'T','V','C','M'} }; -u_map_fcc MCLQMagic = { {'Q','L','C','M'} }; +u_map_fcc MHDRMagic = { { 'R','D','H','M' } }; +u_map_fcc MCINMagic = { { 'N','I','C','M' } }; +u_map_fcc MH2OMagic = { { 'O','2','H','M' } }; +u_map_fcc MCNKMagic = { { 'K','N','C','M' } }; +u_map_fcc MCVTMagic = { { 'T','V','C','M' } }; +u_map_fcc MCLQMagic = { { 'Q','L','C','M' } }; bool isHole(int holes, int i, int j) { @@ -103,7 +103,7 @@ bool adt_MHDR::prepareLoadedData() if (fcc != MHDRMagic.fcc) return false; - if (size!=sizeof(adt_MHDR)-8) + if (size != sizeof(adt_MHDR) - 8) return false; // Check and prepare MCIN diff --git a/src/tools/map_extractor/adt.h b/src/tools/map_extractor/adt.h index e31a57d10cc..5a698794074 100644 --- a/src/tools/map_extractor/adt.h +++ b/src/tools/map_extractor/adt.h @@ -279,10 +279,10 @@ public: uint32 flags; uint32 offsMCIN; // MCIN - uint32 offsTex; // MTEX - uint32 offsModels; // MMDX - uint32 offsModelsIds; // MMID - uint32 offsMapObejcts; // MWMO + uint32 offsTex; // MTEX + uint32 offsModels; // MMDX + uint32 offsModelsIds; // MMID + uint32 offsMapObejcts; // MWMO uint32 offsMapObejctsIds; // MWID uint32 offsDoodsDef; // MDDF uint32 offsObjectsDef; // MODF diff --git a/src/tools/mmaps_generator/MapBuilder.cpp b/src/tools/mmaps_generator/MapBuilder.cpp index 13e5e16228e..64903fe51ff 100644 --- a/src/tools/mmaps_generator/MapBuilder.cpp +++ b/src/tools/mmaps_generator/MapBuilder.cpp @@ -696,7 +696,7 @@ namespace MMAP iv.polyMesh = rcAllocPolyMesh(); if (!iv.polyMesh) { - printf("%s alloc iv.polyMesh FIALED!\n", tileString.c_str()); + printf("%s alloc iv.polyMesh FAILED!\n", tileString.c_str()); delete[] pmmerge; delete[] dmmerge; delete[] tiles; @@ -707,7 +707,7 @@ namespace MMAP iv.polyMeshDetail = rcAllocPolyMeshDetail(); if (!iv.polyMeshDetail) { - printf("%s alloc m_dmesh FIALED!\n", tileString.c_str()); + printf("%s alloc m_dmesh FAILED!\n", tileString.c_str()); delete[] pmmerge; delete[] dmmerge; delete[] tiles;