diff options
Diffstat (limited to 'src')
152 files changed, 6058 insertions, 2885 deletions
diff --git a/src/common/Configuration/Config.cpp b/src/common/Configuration/Config.cpp index ddaab7dee61..888aa6ecef3 100644 --- a/src/common/Configuration/Config.cpp +++ b/src/common/Configuration/Config.cpp @@ -25,11 +25,13 @@ using namespace boost::property_tree; -bool ConfigMgr::LoadInitial(std::string const& file, std::string& error) +bool ConfigMgr::LoadInitial(std::string const& file, std::vector<std::string> args, + std::string& error) { std::lock_guard<std::mutex> lock(_configLock); _filename = file; + _args = args; try { @@ -65,7 +67,7 @@ ConfigMgr* ConfigMgr::instance() bool ConfigMgr::Reload(std::string& error) { - return LoadInitial(_filename, error); + return LoadInitial(_filename, std::move(_args), error); } template<class T> diff --git a/src/common/Configuration/Config.h b/src/common/Configuration/Config.h index 10113cd88d3..573bc7b4a15 100644 --- a/src/common/Configuration/Config.h +++ b/src/common/Configuration/Config.h @@ -23,6 +23,7 @@ #include <string> #include <list> +#include <vector> #include <mutex> #include <boost/property_tree/ptree.hpp> @@ -35,7 +36,8 @@ class TC_COMMON_API ConfigMgr public: /// Method used only for loading main configuration files (authserver.conf and worldserver.conf) - bool LoadInitial(std::string const& file, std::string& error); + bool LoadInitial(std::string const& file, std::vector<std::string> args, + std::string& error); static ConfigMgr* instance(); @@ -47,10 +49,12 @@ public: float GetFloatDefault(std::string const& name, float def) const; std::string const& GetFilename(); + std::vector<std::string> const& GetArguments() const { return _args; } std::list<std::string> GetKeysByString(std::string const& name); private: std::string _filename; + std::vector<std::string> _args; boost::property_tree::ptree _config; std::mutex _configLock; diff --git a/src/common/Cryptography/SHA1.cpp b/src/common/Cryptography/SHA1.cpp index a01bd7844ee..aed4a069827 100644 --- a/src/common/Cryptography/SHA1.cpp +++ b/src/common/Cryptography/SHA1.cpp @@ -18,6 +18,7 @@ #include "SHA1.h" #include "BigNumber.h" +#include "Util.h" #include <cstring> #include <stdarg.h> @@ -67,3 +68,10 @@ void SHA1Hash::Finalize(void) SHA1_Final(mDigest, &mC); } +std::string CalculateSHA1Hash(std::string const& content) +{ + unsigned char digest[SHA_DIGEST_LENGTH]; + SHA1((unsigned char*)content.c_str(), content.length(), (unsigned char*)&digest); + + return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH); +} diff --git a/src/common/Cryptography/SHA1.h b/src/common/Cryptography/SHA1.h index 970ab5c0cb9..37ac2cc0166 100644 --- a/src/common/Cryptography/SHA1.h +++ b/src/common/Cryptography/SHA1.h @@ -46,5 +46,8 @@ class TC_COMMON_API SHA1Hash SHA_CTX mC; uint8 mDigest[SHA_DIGEST_LENGTH]; }; -#endif +/// Returns the SHA1 hash of the given content as hex string. +TC_COMMON_API std::string CalculateSHA1Hash(std::string const& content); + +#endif diff --git a/src/common/Utilities/Containers.h b/src/common/Utilities/Containers.h index 7c889068011..5edb245fd87 100644 --- a/src/common/Utilities/Containers.h +++ b/src/common/Utilities/Containers.h @@ -31,9 +31,9 @@ namespace Trinity namespace Containers { template<class T> - void RandomResizeList(std::list<T> &list, uint32 size) + void RandomResizeList(std::list<T>& list, uint32 size) { - size_t list_size = list.size(); + uint32 list_size = uint32(list.size()); while (list_size > size) { @@ -56,7 +56,7 @@ namespace Trinity if (size) RandomResizeList(listCopy, size); - list = listCopy; + list = std::move(listCopy); } /* @@ -68,7 +68,7 @@ namespace Trinity typename C::value_type const& SelectRandomContainerElement(C const& container) { typename C::const_iterator it = container.begin(); - std::advance(it, urand(0, container.size() - 1)); + std::advance(it, urand(0, uint32(container.size()) - 1)); return *it; } @@ -118,6 +118,19 @@ namespace Trinity } /** + * @fn void Trinity::Containers::RandomShuffle(C& container) + * + * @brief Reorder the elements of the container randomly. + * + * @param container Container to reorder + */ + template <class C> + void RandomShuffle(C& container) + { + std::shuffle(container.begin(), container.end(), SFMTEngine::Instance()); + } + + /** * @fn bool Trinity::Containers::Intersects(Iterator first1, Iterator last1, Iterator first2, Iterator last2) * * @brief Checks if two SORTED containers have a common element @@ -157,7 +170,6 @@ namespace Trinity ++itr; } } - } //! namespace Containers } diff --git a/src/common/Utilities/StartProcess.cpp b/src/common/Utilities/StartProcess.cpp index c47c02bbe87..f35c6de3b5c 100644 --- a/src/common/Utilities/StartProcess.cpp +++ b/src/common/Utilities/StartProcess.cpp @@ -78,7 +78,7 @@ static int CreateChildProcess(T waiter, std::string const& executable, if (!secure) { - TC_LOG_TRACE(logger.c_str(), "Starting process \"%s\" with arguments: \"%s\".", + TC_LOG_TRACE(logger, "Starting process \"%s\" with arguments: \"%s\".", executable.c_str(), boost::algorithm::join(args, " ").c_str()); } @@ -92,6 +92,7 @@ static int CreateChildProcess(T waiter, std::string const& executable, // With binding stdin return execute(run_exe(boost::filesystem::absolute(executable)), set_args(args), + inherit_env(), bind_stdin(*inputSource), bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)), bind_stderr(file_descriptor_sink(errPipe.sink, close_handle))); @@ -101,6 +102,7 @@ static int CreateChildProcess(T waiter, std::string const& executable, // Without binding stdin return execute(run_exe(boost::filesystem::absolute(executable)), set_args(args), + inherit_env(), bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)), bind_stderr(file_descriptor_sink(errPipe.sink, close_handle))); } @@ -111,12 +113,12 @@ static int CreateChildProcess(T waiter, std::string const& executable, auto outInfo = MakeTCLogSink([&](std::string msg) { - TC_LOG_INFO(logger.c_str(), "%s", msg.c_str()); + TC_LOG_INFO(logger, "%s", msg.c_str()); }); auto outError = MakeTCLogSink([&](std::string msg) { - TC_LOG_ERROR(logger.c_str(), "%s", msg.c_str()); + TC_LOG_ERROR(logger, "%s", msg.c_str()); }); copy(outFd, outInfo); @@ -128,7 +130,7 @@ static int CreateChildProcess(T waiter, std::string const& executable, if (!secure) { - TC_LOG_TRACE(logger.c_str(), ">> Process \"%s\" finished with return value %i.", + TC_LOG_TRACE(logger, ">> Process \"%s\" finished with return value %i.", executable.c_str(), result); } @@ -237,7 +239,7 @@ public: } }; -TC_COMMON_API std::shared_ptr<AsyncProcessResult> +std::shared_ptr<AsyncProcessResult> StartAsyncProcess(std::string executable, std::vector<std::string> args, std::string logger, std::string input_file, bool secure) { diff --git a/src/common/Utilities/Timer.h b/src/common/Utilities/Timer.h index cdce08caaf0..f66bb90c98e 100644 --- a/src/common/Utilities/Timer.h +++ b/src/common/Utilities/Timer.h @@ -25,9 +25,9 @@ inline uint32 getMSTime() { using namespace std::chrono; - static const system_clock::time_point ApplicationStartTime = system_clock::now(); + static const steady_clock::time_point ApplicationStartTime = steady_clock::now(); - return uint32(duration_cast<milliseconds>(system_clock::now() - ApplicationStartTime).count()); + return uint32(duration_cast<milliseconds>(steady_clock::now() - ApplicationStartTime).count()); } inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime) diff --git a/src/server/authserver/Main.cpp b/src/server/authserver/Main.cpp index a53187ad737..1392900fb9a 100644 --- a/src/server/authserver/Main.cpp +++ b/src/server/authserver/Main.cpp @@ -36,12 +36,14 @@ #include "GitRevision.h" #include "Util.h" #include <iostream> +#include <boost/filesystem/path.hpp> #include <boost/program_options.hpp> #include <openssl/opensslv.h> #include <openssl/crypto.h> using boost::asio::ip::tcp; using namespace boost::program_options; +namespace fs = boost::filesystem; #ifndef _TRINITY_REALM_CONFIG # define _TRINITY_REALM_CONFIG "authserver.conf" @@ -69,7 +71,7 @@ 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); +variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService); boost::asio::io_service* _ioService; boost::asio::deadline_timer* _dbPingTimer; @@ -81,7 +83,7 @@ int main(int argc, char** argv) { signal(SIGABRT, &Trinity::AbortHandler); - std::string configFile = _TRINITY_REALM_CONFIG; + auto configFile = fs::absolute(_TRINITY_REALM_CONFIG); std::string configService; auto vm = GetConsoleArguments(argc, argv, configFile, configService); // exit if help or version is enabled @@ -98,7 +100,9 @@ int main(int argc, char** argv) #endif std::string configError; - if (!sConfigMgr->LoadInitial(configFile, configError)) + if (!sConfigMgr->LoadInitial(configFile.generic_string(), + std::vector<std::string>(argv, argv + argc), + configError)) { printf("Error in config file: %s\n", configError.c_str()); return 1; @@ -109,7 +113,7 @@ int main(int argc, char** argv) TC_LOG_INFO("server.authserver", "%s (authserver)", GitRevision::GetFullVersion()); TC_LOG_INFO("server.authserver", "<Ctrl-C> to stop.\n"); - TC_LOG_INFO("server.authserver", "Using configuration file %s.", configFile.c_str()); + TC_LOG_INFO("server.authserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str()); TC_LOG_INFO("server.authserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); TC_LOG_INFO("server.authserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); @@ -286,13 +290,14 @@ void ServiceStatusWatcher(boost::system::error_code const& error) } #endif -variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService) +variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService) { options_description all("Allowed options"); all.add_options() ("help,h", "print usage message") ("version,v", "print version build info") - ("config,c", value<std::string>(&configFile)->default_value(_TRINITY_REALM_CONFIG), "use <arg> as configuration file") + ("config,c", value<fs::path>(&configFile)->default_value(fs::absolute(_TRINITY_REALM_CONFIG)), + "use <arg> as configuration file") ; #if PLATFORM == PLATFORM_WINDOWS options_description win("Windows platform specific options"); diff --git a/src/server/authserver/Server/AuthSession.cpp b/src/server/authserver/Server/AuthSession.cpp index f044e0cea94..43c327ffda1 100644 --- a/src/server/authserver/Server/AuthSession.cpp +++ b/src/server/authserver/Server/AuthSession.cpp @@ -820,41 +820,6 @@ bool AuthSession::HandleReconnectProof() } } -tcp::endpoint const GetAddressForClient(Realm const& realm, ip::address const& clientAddr) -{ - ip::address realmIp; - - // Attempt to send best address for client - if (clientAddr.is_loopback()) - { - // Try guessing if realm is also connected locally - if (realm.LocalAddress.is_loopback() || realm.ExternalAddress.is_loopback()) - realmIp = clientAddr; - else - { - // Assume that user connecting from the machine that authserver is located on - // has all realms available in his local network - realmIp = realm.LocalAddress; - } - } - else - { - if (clientAddr.is_v4() && - (clientAddr.to_v4().to_ulong() & realm.LocalSubnetMask.to_v4().to_ulong()) == - (realm.LocalAddress.to_v4().to_ulong() & realm.LocalSubnetMask.to_v4().to_ulong())) - { - realmIp = realm.LocalAddress; - } - else - realmIp = realm.ExternalAddress; - } - - tcp::endpoint endpoint(realmIp, realm.Port); - - // Return external IP - return endpoint; -} - bool AuthSession::HandleRealmList() { TC_LOG_DEBUG("server.authserver", "Entering _HandleRealmList"); diff --git a/src/server/authserver/Server/AuthSession.h b/src/server/authserver/Server/AuthSession.h index 1babb7407a9..027011629eb 100644 --- a/src/server/authserver/Server/AuthSession.h +++ b/src/server/authserver/Server/AuthSession.h @@ -23,7 +23,7 @@ #include "ByteBuffer.h" #include "Socket.h" #include "BigNumber.h" -#include "Callback.h" +#include "QueryCallback.h" #include <memory> #include <boost/asio/ip/tcp.hpp> diff --git a/src/server/authserver/authserver.conf.dist b/src/server/authserver/authserver.conf.dist index db60e3b3ece..1d3b48839a8 100644 --- a/src/server/authserver/authserver.conf.dist +++ b/src/server/authserver/authserver.conf.dist @@ -179,6 +179,7 @@ LoginDatabaseInfo = "127.0.0.1;3306;trinity;trinity;auth" # LoginDatabase.WorkerThreads # Description: The amount of worker threads spawned to handle asynchronous (delayed) MySQL # statements. Each worker thread is mirrored with its own connection to the +# MySQL server and their own thread on the MySQL server. # Default: 1 LoginDatabase.WorkerThreads = 1 diff --git a/src/server/database/Database/DatabaseWorkerPool.h b/src/server/database/Database/DatabaseWorkerPool.h index d883366237f..ffdde91c0a6 100644 --- a/src/server/database/Database/DatabaseWorkerPool.h +++ b/src/server/database/Database/DatabaseWorkerPool.h @@ -19,7 +19,7 @@ #define _DATABASEWORKERPOOL_H #include "Common.h" -#include "Callback.h" +#include "QueryCallback.h" #include "MySQLConnection.h" #include "Transaction.h" #include "DatabaseWorker.h" @@ -120,7 +120,7 @@ class DatabaseWorkerPool //! This method should only be used for queries that are only executed once, e.g during startup. void DirectExecute(const char* sql) { - if (!sql) + if (Trinity::IsFormatEmptyOrNull(sql)) return; T* connection = GetFreeConnection(); @@ -175,7 +175,7 @@ class DatabaseWorkerPool template<typename Format, typename... Args> QueryResult PQuery(Format&& sql, Args&&... args) { - if (!sql) + if (Trinity::IsFormatEmptyOrNull(sql)) return QueryResult(nullptr); return Query(Trinity::StringFormat(std::forward<Format>(sql), std::forward<Args>(args)...).c_str()); diff --git a/src/server/database/Database/Implementation/WorldDatabase.cpp b/src/server/database/Database/Implementation/WorldDatabase.cpp index 7a183d5bf78..83720c1a996 100644 --- a/src/server/database/Database/Implementation/WorldDatabase.cpp +++ b/src/server/database/Database/Implementation/WorldDatabase.cpp @@ -30,8 +30,8 @@ void WorldDatabaseConnection::DoPrepareStatements() PrepareStatement(WORLD_SEL_SMARTAI_WP, "SELECT entry, pointid, position_x, position_y, position_z FROM waypoints ORDER BY entry, pointid", CONNECTION_SYNCH); PrepareStatement(WORLD_DEL_GAMEOBJECT, "DELETE FROM gameobject WHERE guid = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_EVENT_GAMEOBJECT, "DELETE FROM game_event_gameobject WHERE guid = ?", CONNECTION_ASYNC); - PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO game_graveyard_zone (id, ghost_zone, faction) VALUES (?, ?, ?)", CONNECTION_ASYNC); - PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM game_graveyard_zone WHERE id = ? AND ghost_zone = ? AND faction = ?", CONNECTION_ASYNC); + PrepareStatement(WORLD_INS_GRAVEYARD_ZONE, "INSERT INTO graveyard_zone (ID, GhostZone, Faction) VALUES (?, ?, ?)", CONNECTION_ASYNC); + PrepareStatement(WORLD_DEL_GRAVEYARD_ZONE, "DELETE FROM graveyard_zone WHERE ID = ? AND GhostZone = ? AND Faction = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_INS_GAME_TELE, "INSERT INTO game_tele (id, position_x, position_y, position_z, orientation, map, name) VALUES (?, ?, ?, ?, ?, ?, ?)", CONNECTION_ASYNC); PrepareStatement(WORLD_DEL_GAME_TELE, "DELETE FROM game_tele WHERE name = ?", CONNECTION_ASYNC); PrepareStatement(WORLD_INS_NPC_VENDOR, "INSERT INTO npc_vendor (entry, item, maxcount, incrtime, extendedcost) VALUES(?, ?, ?, ?, ?)", CONNECTION_ASYNC); diff --git a/src/common/Threading/Callback.h b/src/server/database/Database/QueryCallback.h index f7eab57ddda..5f6ae74da4f 100644 --- a/src/common/Threading/Callback.h +++ b/src/server/database/Database/QueryCallback.h @@ -15,8 +15,8 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef _CALLBACK_H -#define _CALLBACK_H +#ifndef _QUERY_CALLBACK_H +#define _QUERY_CALLBACK_H #include <future> #include "QueryResult.h" @@ -206,4 +206,4 @@ class QueryCallback_2 QueryCallback_2& operator=(QueryCallback_2 const& right) = delete; }; -#endif +#endif // _QUERY_CALLBACK_H diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp index 3be81e85715..85213a7f709 100644 --- a/src/server/database/Updater/DBUpdater.cpp +++ b/src/server/database/Updater/DBUpdater.cpp @@ -55,7 +55,7 @@ bool DBUpdaterUtil::CheckExecutable() } } - TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").", + TC_LOG_FATAL("sql.updates", "Didn't find any executable MySQL binary at \'%s\' or in path, correct the path in the *.conf (\"MySQLExecutable\").", absolute(exe).generic_string().c_str()); return false; @@ -178,7 +178,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool) // Path of temp file static Path const temp("create_table.sql"); - // Create temporary query to use external mysql cli + // Create temporary query to use external MySQL CLi std::ofstream file(temp.generic_string()); if (!file.is_open()) { @@ -197,7 +197,7 @@ bool DBUpdater<T>::Create(DatabaseWorkerPool<T>& pool) } catch (UpdateException&) { - TC_LOG_FATAL("sql.updates", "Failed to create database %s! Has the user `CREATE` priviliges?", pool.GetConnectionInfo()->database.c_str()); + TC_LOG_FATAL("sql.updates", "Failed to create database %s! Does the user (named in *.conf) have `CREATE` privileges on the MySQL server?", pool.GetConnectionInfo()->database.c_str()); boost::filesystem::remove(temp); return false; } @@ -280,7 +280,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool) { case LOCATION_REPOSITORY: { - TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing, try to clone the source again.", + TC_LOG_ERROR("sql.updates", ">> Base file \"%s\" is missing. Try fixing it by cloning the source again.", base.generic_string().c_str()); break; @@ -288,7 +288,7 @@ bool DBUpdater<T>::Populate(DatabaseWorkerPool<T>& pool) case LOCATION_DOWNLOAD: { TC_LOG_ERROR("sql.updates", ">> File \"%s\" is missing, download it from \"https://github.com/TrinityCore/TrinityCore/releases\"" \ - " and place it in your server directory.", base.filename().generic_string().c_str()); + " and place it in your worldserver directory.", base.filename().generic_string().c_str()); break; } } @@ -355,7 +355,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos if (!std::isdigit(port_or_socket[0])) { - // We can't check here if host == "." because is named localhost if socket option is enabled + // We can't check if host == "." here, because it is named localhost if socket option is enabled args.push_back("-P0"); args.push_back("--protocol=SOCKET"); args.push_back("-S" + port_or_socket); @@ -383,7 +383,7 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos if (ret != EXIT_SUCCESS) { TC_LOG_FATAL("sql.updates", "Applying of file \'%s\' to database \'%s\' failed!" \ - " If you are an user pull the latest revision from the repository. If you are a developer fix your sql query.", + " If you are a user, pull the latest revision from the repository. If you are a developer, fix your sql query.", path.generic_string().c_str(), pool.GetConnectionInfo()->database.c_str()); throw UpdateException("update failed"); diff --git a/src/server/database/Updater/UpdateFetcher.cpp b/src/server/database/Updater/UpdateFetcher.cpp index 6f67867c52b..7dc0a307ca2 100644 --- a/src/server/database/Updater/UpdateFetcher.cpp +++ b/src/server/database/Updater/UpdateFetcher.cpp @@ -18,6 +18,7 @@ #include "UpdateFetcher.h" #include "Log.h" #include "Util.h" +#include "SHA1.h" #include <fstream> #include <chrono> @@ -25,7 +26,6 @@ #include <sstream> #include <exception> #include <unordered_map> -#include <openssl/sha.h> using namespace boost::filesystem; @@ -209,9 +209,8 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, } } - // Calculate hash - std::string const hash = - CalculateHash(ReadSQLUpdate(availableQuery.first)); + // Calculate a Sha1 hash based on query content. + std::string const hash = CalculateSHA1Hash(ReadSQLUpdate(availableQuery.first)); UpdateMode mode = MODE_APPLY; @@ -334,15 +333,6 @@ UpdateResult UpdateFetcher::Update(bool const redundancyChecks, return UpdateResult(importedUpdates, countRecentUpdates, countArchivedUpdates); } -std::string UpdateFetcher::CalculateHash(std::string const& query) const -{ - // Calculate a Sha1 hash based on query content. - unsigned char digest[SHA_DIGEST_LENGTH]; - SHA1((unsigned char*)query.c_str(), query.length(), (unsigned char*)&digest); - - return ByteArrayToHexStr(digest, SHA_DIGEST_LENGTH); -} - uint32 UpdateFetcher::Apply(Path const& path) const { using Time = std::chrono::high_resolution_clock; diff --git a/src/server/database/Updater/UpdateFetcher.h b/src/server/database/Updater/UpdateFetcher.h index a17658818ce..cabc3c2fce3 100644 --- a/src/server/database/Updater/UpdateFetcher.h +++ b/src/server/database/Updater/UpdateFetcher.h @@ -112,7 +112,6 @@ private: AppliedFileStorage ReceiveAppliedFiles() const; std::string ReadSQLUpdate(Path const& file) const; - std::string CalculateHash(std::string const& query) const; uint32 Apply(Path const& path) const; diff --git a/src/server/game/AI/CoreAI/CombatAI.cpp b/src/server/game/AI/CoreAI/CombatAI.cpp index 716ac13c666..4d0247d9fb5 100644 --- a/src/server/game/AI/CoreAI/CombatAI.cpp +++ b/src/server/game/AI/CoreAI/CombatAI.cpp @@ -50,7 +50,7 @@ void AggressorAI::UpdateAI(uint32 /*diff*/) void CombatAI::InitializeAI() { - for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for (uint32 i = 0; i < MAX_CREATURE_SPELLS; ++i) if (me->m_spells[i] && sSpellMgr->GetSpellInfo(me->m_spells[i])) spells.push_back(me->m_spells[i]); diff --git a/src/server/game/AI/CoreAI/PassiveAI.cpp b/src/server/game/AI/CoreAI/PassiveAI.cpp index aafde3c1d9a..3ed2f927645 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.cpp +++ b/src/server/game/AI/CoreAI/PassiveAI.cpp @@ -58,6 +58,12 @@ void PossessedAI::KilledUnit(Unit* victim) victim->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE); } +void PossessedAI::OnCharmed(bool /*apply*/) +{ + me->NeedChangeAI = true; + me->IsAIEnabled = false; +} + void CritterAI::DamageTaken(Unit* /*done_by*/, uint32&) { if (!me->HasUnitState(UNIT_STATE_FLEEING)) diff --git a/src/server/game/AI/CoreAI/PassiveAI.h b/src/server/game/AI/CoreAI/PassiveAI.h index 5a6dba7046d..9ca9e75bd9f 100644 --- a/src/server/game/AI/CoreAI/PassiveAI.h +++ b/src/server/game/AI/CoreAI/PassiveAI.h @@ -46,6 +46,8 @@ class TC_GAME_API PossessedAI : public CreatureAI void JustDied(Unit*) override; void KilledUnit(Unit* victim) override; + void OnCharmed(bool /*apply*/) override; + static int Permissible(const Creature*) { return PERMIT_BASE_IDLE; } }; diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp index 68554722db6..2abe20f0ae6 100644 --- a/src/server/game/AI/CoreAI/PetAI.cpp +++ b/src/server/game/AI/CoreAI/PetAI.cpp @@ -588,6 +588,12 @@ void PetAI::ReceiveEmote(Player* player, uint32 emote) } } +void PetAI::OnCharmed(bool /*apply*/) +{ + me->NeedChangeAI = true; + me->IsAIEnabled = false; +} + void PetAI::ClearCharmInfoFlags() { // Quick access to set all flags to FALSE diff --git a/src/server/game/AI/CoreAI/PetAI.h b/src/server/game/AI/CoreAI/PetAI.h index 3ad34047b01..93ee6c41ece 100644 --- a/src/server/game/AI/CoreAI/PetAI.h +++ b/src/server/game/AI/CoreAI/PetAI.h @@ -49,6 +49,8 @@ class TC_GAME_API PetAI : public CreatureAI void MoveInLineOfSight_Safe(Unit* /*who*/) { } // CreatureAI interferes with returning pets void EnterEvadeMode(EvadeReason /*why*/) override { } // For fleeing, pets don't use this type of Evade mechanic + void OnCharmed(bool /*apply*/) override; + private: bool _isVisible(Unit*) const; bool _needToStop(void); diff --git a/src/server/game/AI/CoreAI/UnitAI.h b/src/server/game/AI/CoreAI/UnitAI.h index 344ccc06249..20a986c0483 100644 --- a/src/server/game/AI/CoreAI/UnitAI.h +++ b/src/server/game/AI/CoreAI/UnitAI.h @@ -242,6 +242,7 @@ class TC_GAME_API UnitAI void DoAddAuraToAllHostilePlayers(uint32 spellid); void DoCast(uint32 spellId); void DoCast(Unit* victim, uint32 spellId, bool triggered = false); + void DoCastSelf(uint32 spellId, bool triggered = false) { DoCast(me, spellId, triggered); } void DoCastToAllHostilePlayers(uint32 spellid, bool triggered = false); void DoCastVictim(uint32 spellId, bool triggered = false); void DoCastAOE(uint32 spellId, bool triggered = false); diff --git a/src/server/game/AI/CreatureAI.cpp b/src/server/game/AI/CreatureAI.cpp index 5b76b583d24..15bbff2793f 100644 --- a/src/server/game/AI/CreatureAI.cpp +++ b/src/server/game/AI/CreatureAI.cpp @@ -29,11 +29,13 @@ #include "Language.h" //Disable CreatureAI when charmed -void CreatureAI::OnCharmed(bool /*apply*/) +void CreatureAI::OnCharmed(bool apply) { - //me->IsAIEnabled = !apply;*/ - me->NeedChangeAI = true; - me->IsAIEnabled = false; + if (apply) + { + me->NeedChangeAI = true; + me->IsAIEnabled = false; + } } AISpellInfoType* UnitAI::AISpellInfo; @@ -44,7 +46,7 @@ void CreatureAI::Talk(uint8 id, WorldObject const* whisperTarget /*= nullptr*/) sCreatureTextMgr->SendChat(me, id, whisperTarget); } -void CreatureAI::DoZoneInCombat(Creature* creature /*= NULL*/, float maxRangeToNearestTarget /* = 50.0f*/) +void CreatureAI::DoZoneInCombat(Creature* creature /*= nullptr*/, float maxRangeToNearestTarget /* = 250.0f*/) { if (!creature) creature = me; @@ -257,17 +259,16 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) if (!me->IsAlive()) return false; - // don't remove vehicle auras, passengers aren't supposed to drop off the vehicle - // don't remove clone caster on evade (to be verified) - me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_CLONE_CASTER); + me->RemoveAurasOnEvade(); // sometimes bosses stuck in combat? me->DeleteThreatList(); me->CombatStop(true); me->LoadCreaturesAddon(); - me->SetLootRecipient(NULL); + me->SetLootRecipient(nullptr); me->ResetPlayerDamageReq(); me->SetLastDamagedTime(0); + me->SetCannotReachTarget(false); if (me->IsInEvadeMode()) return false; @@ -275,11 +276,11 @@ bool CreatureAI::_EnterEvadeMode(EvadeReason /*why*/) return true; } -#define BOUNDARY_VISUALIZE_CREATURE 15425 -#define BOUNDARY_VISUALIZE_CREATURE_SCALE 0.25f -#define BOUNDARY_VISUALIZE_STEP_SIZE 1 -#define BOUNDARY_VISUALIZE_FAILSAFE_LIMIT 750 -#define BOUNDARY_VISUALIZE_SPAWN_HEIGHT 5 +static const uint32 BOUNDARY_VISUALIZE_CREATURE = 15425; +static const float BOUNDARY_VISUALIZE_CREATURE_SCALE = 0.25f; +static const int8 BOUNDARY_VISUALIZE_STEP_SIZE = 1; +static const int32 BOUNDARY_VISUALIZE_FAILSAFE_LIMIT = 750; +static const float BOUNDARY_VISUALIZE_SPAWN_HEIGHT = 5.0f; int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) const { typedef std::pair<int32, int32> coordinate; @@ -295,13 +296,13 @@ int32 CreatureAI::VisualizeBoundary(uint32 duration, Unit* owner, bool fill) con std::unordered_set<coordinate> outOfBounds; Position startPosition = owner->GetPosition(); - if (!CheckBoundary(&startPosition)) // fall back to creature position - { + if (!CheckBoundary(&startPosition)) + { // fall back to creature position startPosition = me->GetPosition(); if (!CheckBoundary(&startPosition)) - { + { // fall back to creature home position startPosition = me->GetHomePosition(); - if (!CheckBoundary(&startPosition)) // fall back to creature home position + if (!CheckBoundary(&startPosition)) return LANG_CREATURE_NO_INTERIOR_POINT_FOUND; } } diff --git a/src/server/game/AI/CreatureAI.h b/src/server/game/AI/CreatureAI.h index f175050e107..f8fa6ba532b 100644 --- a/src/server/game/AI/CreatureAI.h +++ b/src/server/game/AI/CreatureAI.h @@ -87,6 +87,7 @@ class TC_GAME_API CreatureAI : public UnitAI { EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty EVADE_REASON_BOUNDARY, // the creature has moved outside its evade boundary + EVADE_REASON_NO_PATH, // the creature was unable to reach its target for over 5 seconds EVADE_REASON_SEQUENCE_BREAK, // this is a boss and the pre-requisite encounters for engaging it are not defeated yet EVADE_REASON_OTHER }; @@ -111,7 +112,7 @@ class TC_GAME_API CreatureAI : public UnitAI // Called for reaction at stopping attack at no attackers or targets virtual void EnterEvadeMode(EvadeReason why = EVADE_REASON_OTHER); - // Called for reaction at enter to combat if not in combat yet (enemy can be NULL) + // Called for reaction at enter to combat if not in combat yet (enemy can be nullptr) virtual void EnterCombat(Unit* /*victim*/) { } // Called when the creature is killed @@ -148,7 +149,7 @@ class TC_GAME_API CreatureAI : public UnitAI // Called at reaching home after evade virtual void JustReachedHome() { } - void DoZoneInCombat(Creature* creature = NULL, float maxRangeToNearestTarget = 50.0f); + void DoZoneInCombat(Creature* creature = nullptr, float maxRangeToNearestTarget = 250.0f); // Called at text emote receive from player virtual void ReceiveEmote(Player* /*player*/, uint32 /*emoteId*/) { } diff --git a/src/server/game/AI/PlayerAI/PlayerAI.cpp b/src/server/game/AI/PlayerAI/PlayerAI.cpp index 680ecfb9414..68cb268ac6c 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.cpp +++ b/src/server/game/AI/PlayerAI/PlayerAI.cpp @@ -19,6 +19,51 @@ #include "SpellAuras.h" #include "SpellAuraEffects.h" +static const uint8 NUM_TALENT_TREES = 3; +static const uint8 NUM_SPEC_ICONICS = 3; + +enum Specs +{ + SPEC_WARRIOR_ARMS = 0, + SPEC_WARRIOR_FURY = 1, + SPEC_WARRIOR_PROTECTION = 2, + + SPEC_PALADIN_HOLY = 0, + SPEC_PALADIN_PROTECTION = 1, + SPEC_PALADIN_RETRIBUTION = 2, + + SPEC_HUNTER_BEAST_MASTERY = 0, + SPEC_HUNTER_MARKSMANSHIP = 1, + SPEC_HUNTER_SURVIVAL = 2, + + SPEC_ROGUE_ASSASSINATION = 0, + SPEC_ROGUE_COMBAT = 1, + SPEC_ROGUE_SUBLETY = 2, + + SPEC_PRIEST_DISCIPLINE = 0, + SPEC_PRIEST_HOLY = 1, + SPEC_PRIEST_SHADOW = 2, + + SPEC_DEATH_KNIGHT_BLOOD = 0, + SPEC_DEATH_KNIGHT_FROST = 1, + SPEC_DEATH_KNIGHT_UNHOLY = 2, + + SPEC_SHAMAN_ELEMENTAL = 0, + SPEC_SHAMAN_ENHANCEMENT = 1, + SPEC_SHAMAN_RESTORATION = 2, + + SPEC_MAGE_ARCANE = 0, + SPEC_MAGE_FIRE = 1, + SPEC_MAGE_FROST = 2, + + SPEC_WARLOCK_AFFLICTION = 0, + SPEC_WARLOCK_DEMONOLOGY = 1, + SPEC_WARLOCK_DESTRUCTION = 2, + + SPEC_DRUID_BALANCE = 0, + SPEC_DRUID_FERAL = 1, + SPEC_DRUID_RESTORATION = 2 +}; enum Spells { /* Generic */ @@ -27,29 +72,449 @@ enum Spells SPELL_THROW = 2764, SPELL_SHOOT_WAND = 5019, - /* Paladin */ - PASSIVE_ILLUMINATION = 20215, + /* Warrior - Generic */ + SPELL_BATTLE_STANCE = 2457, + SPELL_BERSERKER_STANCE = 2458, + SPELL_DEFENSIVE_STANCE = 71, + SPELL_CHARGE = 11578, + SPELL_INTERCEPT = 20252, + SPELL_ENRAGED_REGEN = 55694, + SPELL_INTIMIDATING_SHOUT= 5246, + SPELL_PUMMEL = 6552, + SPELL_SHIELD_BASH = 72, + SPELL_BLOODRAGE = 2687, + + /* Warrior - Arms */ + SPELL_SWEEPING_STRIKES = 12328, + SPELL_MORTAL_STRIKE = 12294, + SPELL_BLADESTORM = 46924, + SPELL_REND = 47465, + SPELL_RETALIATION = 20230, + SPELL_SHATTERING_THROW = 64382, + SPELL_THUNDER_CLAP = 47502, + + /* Warrior - Fury */ + SPELL_DEATH_WISH = 12292, + SPELL_BLOODTHIRST = 23881, + PASSIVE_TITANS_GRIP = 46917, + SPELL_DEMO_SHOUT = 47437, + SPELL_EXECUTE = 47471, + SPELL_HEROIC_FURY = 60970, + SPELL_RECKLESSNESS = 1719, + SPELL_PIERCING_HOWL = 12323, + + /* Warrior - Protection */ + SPELL_VIGILANCE = 50720, + SPELL_DEVASTATE = 20243, + SPELL_SHOCKWAVE = 46968, + SPELL_CONCUSSION_BLOW = 12809, + SPELL_DISARM = 676, + SPELL_LAST_STAND = 12975, + SPELL_SHIELD_BLOCK = 2565, + SPELL_SHIELD_SLAM = 47488, + SPELL_SHIELD_WALL = 871, + SPELL_SPELL_REFLECTION = 23920, + + /* Paladin - Generic */ + SPELL_AURA_MASTERY = 31821, + SPELL_LAY_ON_HANDS = 48788, + SPELL_BLESSING_OF_MIGHT = 48932, + SPELL_AVENGING_WRATH = 31884, + SPELL_DIVINE_PROTECTION = 498, + SPELL_DIVINE_SHIELD = 642, + SPELL_HAMMER_OF_JUSTICE = 10308, + SPELL_HAND_OF_FREEDOM = 1044, + SPELL_HAND_OF_PROTECTION = 10278, + SPELL_HAND_OF_SACRIFICE = 6940, + + /* Paladin - Holy*/ + PASSIVE_ILLUMINATION = 20215, + SPELL_HOLY_SHOCK = 20473, + SPELL_BEACON_OF_LIGHT = 53563, + SPELL_CONSECRATION = 48819, + SPELL_FLASH_OF_LIGHT = 48785, + SPELL_HOLY_LIGHT = 48782, + SPELL_DIVINE_FAVOR = 20216, + SPELL_DIVINE_ILLUMINATION = 31842, + + /* Paladin - Protection */ + SPELL_BLESS_OF_SANC = 20911, + SPELL_HOLY_SHIELD = 20925, + SPELL_AVENGERS_SHIELD = 48827, + SPELL_DIVINE_SACRIFICE = 64205, + SPELL_HAMMER_OF_RIGHTEOUS = 53595, + SPELL_RIGHTEOUS_FURY = 25780, + SPELL_SHIELD_OF_RIGHTEOUS = 61411, + + /* Paladin - Retribution */ + SPELL_SEAL_OF_COMMAND = 20375, + SPELL_CRUSADER_STRIKE = 35395, + SPELL_DIVINE_STORM = 53385, + SPELL_JUDGEMENT = 20271, + SPELL_HAMMER_OF_WRATH = 48806, + + /* Hunter - Generic */ + SPELL_DETERRENCE = 19263, + SPELL_EXPLOSIVE_TRAP = 49067, + SPELL_FREEZING_ARROW = 60192, + SPELL_RAPID_FIRE = 3045, + SPELL_KILL_SHOT = 61006, + SPELL_MULTI_SHOT = 49048, + SPELL_VIPER_STING = 3034, + + /* Hunter - Beast Mastery */ + SPELL_BESTIAL_WRATH = 19574, + PASSIVE_BEAST_WITHIN = 34692, + PASSIVE_BEAST_MASTERY = 53270, + + /* Hunter - Marksmanship */ + SPELL_AIMED_SHOT = 19434, + PASSIVE_TRUESHOT_AURA = 19506, + SPELL_CHIMERA_SHOT = 53209, + SPELL_ARCANE_SHOT = 49045, + SPELL_STEADY_SHOT = 49052, + SPELL_READINESS = 23989, + SPELL_SILENCING_SHOT = 34490, - /* Priest */ - SPELL_SOUL_WARDING = 63574, - SPELL_SPIRIT_REDEMPTION = 20711, - SPELL_SHADOWFORM = 15473, + /* Hunter - Survival */ + PASSIVE_LOCK_AND_LOAD = 56344, + SPELL_WYVERN_STING = 19386, + SPELL_EXPLOSIVE_SHOT = 53301, + SPELL_BLACK_ARROW = 3674, - /* Shaman */ - SPELL_TIDAL_FORCE = 582, - SPELL_MANA_TIDE_TOTEM = 590, - SPELL_SHA_NATURE_SWIFT = 591, + /* Rogue - Generic */ + SPELL_DISMANTLE = 51722, + SPELL_EVASION = 26669, + SPELL_KICK = 1766, + SPELL_VANISH = 26889, + SPELL_BLIND = 2094, + SPELL_CLOAK_OF_SHADOWS = 31224, + + /* Rogue - Assassination */ + SPELL_COLD_BLOOD = 14177, + SPELL_MUTILATE = 1329, + SPELL_HUNGER_FOR_BLOOD = 51662, + SPELL_ENVENOM = 57993, + + /* Rogue - Combat */ + SPELL_SINISTER_STRIKE = 48637, + SPELL_BLADE_FLURRY = 13877, + SPELL_ADRENALINE_RUSH = 13750, + SPELL_KILLING_SPREE = 51690, + SPELL_EVISCERATE = 48668, + + /* Rogue - Sublety */ + SPELL_HEMORRHAGE = 16511, + SPELL_PREMEDITATION = 14183, + SPELL_SHADOW_DANCE = 51713, + SPELL_PREPARATION = 14185, + SPELL_SHADOWSTEP = 36554, + + /* Priest - Generic */ + SPELL_FEAR_WARD = 6346, + SPELL_POWER_WORD_FORT = 48161, + SPELL_DIVINE_SPIRIT = 48073, + SPELL_SHADOW_PROTECTION = 48169, + SPELL_DIVINE_HYMN = 64843, + SPELL_HYMN_OF_HOPE = 64901, + SPELL_SHADOW_WORD_DEATH = 48158, + SPELL_PSYCHIC_SCREAM = 10890, + + /* Priest - Discipline */ + PASSIVE_SOUL_WARDING = 63574, + SPELL_POWER_INFUSION = 10060, + SPELL_PENANCE = 47540, + SPELL_PAIN_SUPPRESSION = 33206, + SPELL_INNER_FOCUS = 14751, + SPELL_POWER_WORD_SHIELD = 48066, + + /* Priest - Holy */ + PASSIVE_SPIRIT_REDEMPTION = 20711, + SPELL_DESPERATE_PRAYER = 19236, + SPELL_GUARDIAN_SPIRIT = 47788, + SPELL_FLASH_HEAL = 48071, + SPELL_RENEW = 48068, + + /* Priest - Shadow */ + SPELL_VAMPIRIC_EMBRACE = 15286, + SPELL_SHADOWFORM = 15473, + SPELL_VAMPIRIC_TOUCH = 34914, + SPELL_MIND_FLAY = 15407, + SPELL_MIND_BLAST = 48127, + SPELL_SHADOW_WORD_PAIN = 48125, + SPELL_DEVOURING_PLAGUE = 48300, + SPELL_DISPERSION = 47585, + + /* Death Knight - Generic */ + SPELL_DEATH_GRIP = 49576, + SPELL_STRANGULATE = 47476, + SPELL_EMPOWER_RUNE_WEAP = 47568, + SPELL_ICEBORN_FORTITUDE = 48792, + SPELL_ANTI_MAGIC_SHELL = 48707, + SPELL_DEATH_COIL_DK = 49895, + SPELL_MIND_FREEZE = 47528, + SPELL_ICY_TOUCH = 49909, + AURA_FROST_FEVER = 55095, + SPELL_PLAGUE_STRIKE = 49921, + AURA_BLOOD_PLAGUE = 55078, + SPELL_PESTILENCE = 50842, + + /* Death Knight - Blood */ + SPELL_RUNE_TAP = 48982, + SPELL_HYSTERIA = 49016, + SPELL_HEART_STRIKE = 55050, + SPELL_DEATH_STRIKE = 49924, + SPELL_BLOOD_STRIKE = 49930, + SPELL_MARK_OF_BLOOD = 49005, + SPELL_VAMPIRIC_BLOOD = 55233, + + /* Death Knight - Frost */ + PASSIVE_ICY_TALONS = 50887, + SPELL_FROST_STRIKE = 49143, + SPELL_HOWLING_BLAST = 49184, + SPELL_UNBREAKABLE_ARMOR = 51271, + SPELL_OBLITERATE = 51425, + SPELL_DEATHCHILL = 49796, + + /* Death Knight - Unholy */ + PASSIVE_UNHOLY_BLIGHT = 49194, + PASSIVE_MASTER_OF_GHOUL = 52143, + SPELL_SCOURGE_STRIKE = 55090, + SPELL_DEATH_AND_DECAY = 49938, + SPELL_ANTI_MAGIC_ZONE = 51052, + SPELL_SUMMON_GARGOYLE = 49206, + + /* Shaman - Generic */ + SPELL_HEROISM = 32182, + SPELL_BLOODLUST = 2825, + SPELL_GROUNDING_TOTEM = 8177, + + /* Shaman - Elemental*/ + PASSIVE_ELEMENTAL_FOCUS = 16164, + SPELL_TOTEM_OF_WRATH = 30706, + SPELL_THUNDERSTORM = 51490, + SPELL_LIGHTNING_BOLT = 49238, + SPELL_EARTH_SHOCK = 49231, + SPELL_FLAME_SHOCK = 49233, + SPELL_LAVA_BURST = 60043, + SPELL_CHAIN_LIGHTNING = 49271, + SPELL_ELEMENTAL_MASTERY = 16166, + + /* Shaman - Enhancement */ + PASSIVE_SPIRIT_WEAPONS = 16268, + SPELL_LAVA_LASH = 60103, + SPELL_FERAL_SPIRIT = 51533, + AURA_MAELSTROM_WEAPON = 53817, SPELL_STORMSTRIKE = 17364, + SPELL_SHAMANISTIC_RAGE = 30823, + + /* Shaman - Restoration*/ + SPELL_SHA_NATURE_SWIFT = 591, + SPELL_MANA_TIDE_TOTEM = 590, + SPELL_EARTH_SHIELD = 49284, + SPELL_RIPTIDE = 61295, + SPELL_HEALING_WAVE = 49273, + SPELL_LESSER_HEAL_WAVE = 49276, + SPELL_TIDAL_FORCE = 55198, + + /* Mage - Generic */ + SPELL_DAMPEN_MAGIC = 43015, + SPELL_EVOCATION = 12051, + SPELL_MANA_SHIELD = 43020, + SPELL_MIRROR_IMAGE = 55342, + SPELL_SPELLSTEAL = 30449, + SPELL_COUNTERSPELL = 2139, + SPELL_ICE_BLOCK = 45438, + + /* Mage - Arcane */ + SPELL_FOCUS_MAGIC = 54646, + SPELL_ARCANE_POWER = 12042, + SPELL_ARCANE_BARRAGE = 44425, + SPELL_ARCANE_BLAST = 42897, + AURA_ARCANE_BLAST = 36032, + SPELL_ARCANE_MISSILES = 42846, + SPELL_PRESENCE_OF_MIND = 12043, + + /* Mage - Fire */ + SPELL_PYROBLAST = 11366, + SPELL_COMBUSTION = 11129, + SPELL_LIVING_BOMB = 44457, + SPELL_FIREBALL = 42833, + SPELL_FIRE_BLAST = 42873, + SPELL_DRAGONS_BREATH = 31661, + SPELL_BLAST_WAVE = 11113, + + /* Mage - Frost */ + SPELL_ICY_VEINS = 12472, + SPELL_ICE_BARRIER = 11426, + SPELL_DEEP_FREEZE = 44572, + SPELL_FROST_NOVA = 42917, + SPELL_FROSTBOLT = 42842, + SPELL_COLD_SNAP = 11958, + SPELL_ICE_LANCE = 42914, + + /* Warlock - Generic */ + SPELL_FEAR = 6215, + SPELL_HOWL_OF_TERROR = 17928, + SPELL_CORRUPTION = 47813, + SPELL_DEATH_COIL_W = 47860, + SPELL_SHADOW_BOLT = 47809, + SPELL_INCINERATE = 47838, + SPELL_IMMOLATE = 47811, + SPELL_SEED_OF_CORRUPTION = 47836, + + /* Warlock - Affliction */ + PASSIVE_SIPHON_LIFE = 63108, + SPELL_UNSTABLE_AFFLICTION = 30108, + SPELL_HAUNT = 48181, + SPELL_CURSE_OF_AGONY = 47864, + SPELL_DRAIN_SOUL = 47855, - /* Druid */ - SPELL_MOONKIN_FORM = 24858, - SPELL_SWIFTMEND = 18562, - SPELL_DRU_NATURE_SWIFT = 17116, - SPELL_TREE_OF_LIFE = 33891 + /* Warlock - Demonology */ + SPELL_SOUL_LINK = 19028, + SPELL_DEMONIC_EMPOWERMENT = 47193, + SPELL_METAMORPHOSIS = 59672, + SPELL_IMMOLATION_AURA = 50589, + SPELL_DEMON_CHARGE = 54785, + AURA_DECIMATION = 63167, + AURA_MOLTEN_CORE = 71165, + SPELL_SOUL_FIRE = 47825, + + /* Warlock - Destruction */ + SPELL_SHADOWBURN = 17877, + SPELL_CONFLAGRATE = 17962, + SPELL_CHAOS_BOLT = 50796, + SPELL_SHADOWFURY = 47847, + + /* Druid - Generic */ + SPELL_BARKSKIN = 22812, + SPELL_INNERVATE = 29166, + + /* Druid - Balance */ + SPELL_INSECT_SWARM = 5570, + SPELL_MOONKIN_FORM = 24858, + SPELL_STARFALL = 48505, + SPELL_TYPHOON = 61384, + AURA_ECLIPSE_LUNAR = 48518, + SPELL_MOONFIRE = 48463, + SPELL_STARFIRE = 48465, + SPELL_WRATH = 48461, + + /* Druid - Feral */ + SPELL_CAT_FORM = 768, + SPELL_SURVIVAL_INSTINCTS = 61336, + SPELL_MANGLE = 33917, + SPELL_BERSERK = 50334, + SPELL_MANGLE_CAT = 48566, + SPELL_FERAL_CHARGE_CAT = 49376, + SPELL_RAKE = 48574, + SPELL_RIP = 49800, + SPELL_SAVAGE_ROAR = 52610, + SPELL_TIGER_FURY = 50213, + SPELL_CLAW = 48570, + SPELL_DASH = 33357, + SPELL_MAIM = 49802, + + /* Druid - Restoration */ + SPELL_SWIFTMEND = 18562, + SPELL_TREE_OF_LIFE = 33891, + SPELL_WILD_GROWTH = 48438, + SPELL_NATURE_SWIFTNESS = 17116, + SPELL_TRANQUILITY = 48447, + SPELL_NOURISH = 50464, + SPELL_HEALING_TOUCH = 48378, + SPELL_REJUVENATION = 48441, + SPELL_REGROWTH = 48443, + SPELL_LIFEBLOOM = 48451 }; +// As it turns out, finding out "how many points does the player have in spec X" is actually really expensive to do frequently +// So instead, we just check for a handful of spells that, realistically, no spec is gonna go without (and "has spell" is cheap!) +// Can players deliberately trick this check? Yes. +// Is it worth doing? No. +// Close enough. +static const uint32 SPEC_ICONICS[MAX_CLASSES][NUM_TALENT_TREES][NUM_SPEC_ICONICS] = { + { // CLASS_NONE + {0,0,0}, + {0,0,0}, + {0,0,0} + }, + { // CLASS_WARRIOR + {SPELL_BLADESTORM, SPELL_MORTAL_STRIKE, SPELL_SWEEPING_STRIKES}, // Arms + {PASSIVE_TITANS_GRIP, SPELL_BLOODTHIRST, SPELL_DEATH_WISH}, // Fury + {SPELL_SHOCKWAVE, SPELL_DEVASTATE, SPELL_VIGILANCE} // Protection + }, + { // CLASS_PALADIN + {SPELL_BEACON_OF_LIGHT, SPELL_HOLY_SHOCK, PASSIVE_ILLUMINATION}, // Holy + {SPELL_HAMMER_OF_RIGHTEOUS, SPELL_HOLY_SHIELD, SPELL_BLESS_OF_SANC}, // Protection + {SPELL_DIVINE_STORM, SPELL_CRUSADER_STRIKE, SPELL_SEAL_OF_COMMAND} // Retribution + }, + { // CLASS_HUNTER + {PASSIVE_BEAST_MASTERY, PASSIVE_BEAST_WITHIN, SPELL_BESTIAL_WRATH}, // Beast Mastery + {SPELL_CHIMERA_SHOT, PASSIVE_TRUESHOT_AURA, SPELL_AIMED_SHOT}, // Marksmanship + {SPELL_EXPLOSIVE_SHOT, SPELL_WYVERN_STING, PASSIVE_LOCK_AND_LOAD} // Survival + }, + { // CLASS_ROGUE + {SPELL_HUNGER_FOR_BLOOD, SPELL_MUTILATE, SPELL_COLD_BLOOD}, // Assassination + {SPELL_KILLING_SPREE, SPELL_ADRENALINE_RUSH, SPELL_BLADE_FLURRY}, // Combat + {SPELL_SHADOW_DANCE, SPELL_PREMEDITATION, SPELL_HEMORRHAGE} // Sublety + }, + { // CLASS_PRIEST + {SPELL_PENANCE, SPELL_POWER_INFUSION, PASSIVE_SOUL_WARDING}, // Discipline + {SPELL_GUARDIAN_SPIRIT, PASSIVE_SPIRIT_REDEMPTION, SPELL_DESPERATE_PRAYER}, // Holy + {SPELL_VAMPIRIC_TOUCH, SPELL_SHADOWFORM, SPELL_VAMPIRIC_EMBRACE} // Shadow + }, + { // CLASS_DEATH_KNIGHT + {SPELL_HEART_STRIKE, SPELL_HYSTERIA, SPELL_RUNE_TAP}, // Blood + {SPELL_HOWLING_BLAST, SPELL_FROST_STRIKE, PASSIVE_ICY_TALONS}, // Frost + {SPELL_SCOURGE_STRIKE, PASSIVE_MASTER_OF_GHOUL, PASSIVE_UNHOLY_BLIGHT} // Unholy + }, + { // CLASS_SHAMAN + {SPELL_THUNDERSTORM, SPELL_TOTEM_OF_WRATH, PASSIVE_ELEMENTAL_FOCUS}, // Elemental + {SPELL_FERAL_SPIRIT, SPELL_LAVA_LASH, PASSIVE_SPIRIT_WEAPONS}, // Enhancement + {SPELL_RIPTIDE, SPELL_MANA_TIDE_TOTEM, SPELL_SHA_NATURE_SWIFT} // Restoration + }, + { // CLASS_MAGE + {SPELL_ARCANE_BARRAGE, SPELL_ARCANE_POWER, SPELL_FOCUS_MAGIC}, // Arcane + {SPELL_LIVING_BOMB, SPELL_COMBUSTION, SPELL_PYROBLAST}, // Fire + {SPELL_DEEP_FREEZE, SPELL_ICE_BARRIER, SPELL_ICY_VEINS} // Frost + }, + { // CLASS_WARLOCK + {SPELL_HAUNT, SPELL_UNSTABLE_AFFLICTION, PASSIVE_SIPHON_LIFE}, // Affliction + {SPELL_METAMORPHOSIS, SPELL_DEMONIC_EMPOWERMENT, SPELL_SOUL_LINK}, // Demonology + {SPELL_CHAOS_BOLT, SPELL_CONFLAGRATE, SPELL_SHADOWBURN} // Destruction + }, + { // CLASS_UNK + {0,0,0}, + {0,0,0}, + {0,0,0} + }, + { // CLASS_DRUID + {SPELL_STARFALL, SPELL_MOONKIN_FORM, SPELL_INSECT_SWARM}, // Balance + {SPELL_BERSERK, SPELL_MANGLE, SPELL_SURVIVAL_INSTINCTS}, // Feral + {SPELL_WILD_GROWTH, SPELL_TREE_OF_LIFE, SPELL_SWIFTMEND} // Restoration + } +}; + +uint8 PlayerAI::GetPlayerSpec(Player const* who) +{ + if (!who) + return 0; + + uint8 wClass = who->getClass(); + for (uint8 tier = 0; tier < NUM_SPEC_ICONICS; ++tier) + for (uint8 tree = 0; tree < NUM_TALENT_TREES; ++tree) + if (SPEC_ICONICS[wClass][tree][tier] && who->HasSpell(SPEC_ICONICS[wClass][tree][tier])) + return tree; + + return 0; +} + bool PlayerAI::IsPlayerHealer(Player const* who) { + if (!who) + return false; + switch (who->getClass()) { case CLASS_WARRIOR: @@ -61,18 +526,21 @@ bool PlayerAI::IsPlayerHealer(Player const* who) default: return false; case CLASS_PALADIN: - return who->HasSpell(PASSIVE_ILLUMINATION); + return (PlayerAI::GetPlayerSpec(who) == SPEC_PALADIN_HOLY); case CLASS_PRIEST: - return who->HasSpell(SPELL_SOUL_WARDING) || who->HasSpell(SPELL_SPIRIT_REDEMPTION); + return (PlayerAI::GetPlayerSpec(who) != SPEC_PRIEST_SHADOW); case CLASS_SHAMAN: - return who->HasSpell(SPELL_MANA_TIDE_TOTEM) || who->HasSpell(SPELL_SHA_NATURE_SWIFT) || who->HasSpell(SPELL_TIDAL_FORCE); + return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_RESTORATION); case CLASS_DRUID: - return who->HasSpell(SPELL_SWIFTMEND) || who->HasSpell(SPELL_DRU_NATURE_SWIFT) || who->HasSpell(SPELL_TREE_OF_LIFE); + return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_RESTORATION); } } bool PlayerAI::IsPlayerRangedAttacker(Player const* who) { + if (!who) + return false; + switch (who->getClass()) { case CLASS_WARRIOR: @@ -94,12 +562,106 @@ bool PlayerAI::IsPlayerRangedAttacker(Player const* who) return false; } case CLASS_PRIEST: - return who->HasSpell(SPELL_SHADOWFORM); + return (PlayerAI::GetPlayerSpec(who) == SPEC_PRIEST_SHADOW); case CLASS_SHAMAN: - return !who->HasSpell(SPELL_STORMSTRIKE); + return (PlayerAI::GetPlayerSpec(who) == SPEC_SHAMAN_ELEMENTAL); case CLASS_DRUID: - return who->HasSpell(SPELL_MOONKIN_FORM); + return (PlayerAI::GetPlayerSpec(who) == SPEC_DRUID_BALANCE); + } +} + +PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, Unit* target) +{ + // Find highest spell rank that we know + uint32 knownRank, nextRank; + if (me->HasSpell(spellId)) + { + // this will save us some lookups if the player has the highest rank (expected case) + knownRank = spellId; + nextRank = sSpellMgr->GetNextSpellInChain(spellId); + } + else + { + knownRank = 0; + nextRank = sSpellMgr->GetFirstSpellInChain(spellId); + } + + while (nextRank && me->HasSpell(nextRank)) + { + knownRank = nextRank; + nextRank = sSpellMgr->GetNextSpellInChain(knownRank); + } + + if (!knownRank) + return {}; + + SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(knownRank); + if (!spellInfo) + return {}; + + if (me->GetSpellHistory()->HasGlobalCooldown(spellInfo)) + return {}; + + Spell* spell = new Spell(me, spellInfo, TRIGGERED_NONE); + if (spell->CanAutoCast(target)) + return{ spell, target }; + + delete spell; + return {}; +} + +PlayerAI::TargetedSpell PlayerAI::VerifySpellCast(uint32 spellId, SpellTarget target) +{ + Unit* pTarget = nullptr; + switch (target) + { + case TARGET_NONE: + break; + case TARGET_VICTIM: + pTarget = me->GetVictim(); + if (!pTarget) + return {}; + break; + case TARGET_CHARMER: + pTarget = me->GetCharmer(); + if (!pTarget) + return {}; + break; + case TARGET_SELF: + pTarget = me; + break; + } + + return VerifySpellCast(spellId, pTarget); +} + +PlayerAI::TargetedSpell PlayerAI::SelectSpellCast(PossibleSpellVector& spells) +{ + uint32 totalWeights = 0; + for (PossibleSpell const& wSpell : spells) + totalWeights += wSpell.second; + + TargetedSpell selected; + uint32 randNum = urand(0, totalWeights - 1); + for (PossibleSpell const& wSpell : spells) + { + if (selected) + { + delete wSpell.first.first; + continue; + } + + if (randNum < wSpell.second) + selected = wSpell.first; + else + { + randNum -= wSpell.second; + delete wSpell.first.first; + } } + + spells.clear(); + return selected; } void PlayerAI::DoRangedAttackIfReady() @@ -150,6 +712,29 @@ void PlayerAI::DoAutoAttackIfReady() DoMeleeAttackIfReady(); } +void PlayerAI::CancelAllShapeshifts() +{ + std::list<AuraEffect*> const& shapeshiftAuras = me->GetAuraEffectsByType(SPELL_AURA_MOD_SHAPESHIFT); + std::set<Aura*> removableShapeshifts; + for (AuraEffect* auraEff : shapeshiftAuras) + { + Aura* aura = auraEff->GetBase(); + if (!aura) + continue; + SpellInfo const* auraInfo = aura->GetSpellInfo(); + if (!auraInfo) + continue; + if (auraInfo->HasAttribute(SPELL_ATTR0_CANT_CANCEL)) + continue; + if (!auraInfo->IsPositive() || auraInfo->IsPassive()) + continue; + removableShapeshifts.insert(aura); + } + + for (Aura* aura : removableShapeshifts) + me->RemoveOwnedAura(aura, AURA_REMOVE_BY_CANCEL); +} + struct UncontrolledTargetSelectPredicate : public std::unary_function<Unit*, bool> { bool operator()(Unit const* target) const @@ -164,7 +749,516 @@ Unit* SimpleCharmedPlayerAI::SelectAttackTarget() const return nullptr; } -void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/) +PlayerAI::TargetedSpell SimpleCharmedPlayerAI::SelectAppropriateCastForSpec() +{ + PossibleSpellVector spells; + + switch (me->getClass()) + { + case CLASS_WARRIOR: + if (!me->IsWithinMeleeRange(me->GetVictim())) + { + VerifyAndPushSpellCast(spells, SPELL_CHARGE, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_INTERCEPT, TARGET_VICTIM, 10); + } + VerifyAndPushSpellCast(spells, SPELL_ENRAGED_REGEN, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_INTIMIDATING_SHOUT, TARGET_VICTIM, 4); + if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING)) + { + VerifyAndPushSpellCast(spells, SPELL_PUMMEL, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_BASH, TARGET_VICTIM, 15); + } + VerifyAndPushSpellCast(spells, SPELL_BLOODRAGE, TARGET_NONE, 5); + switch (GetSpec()) + { + case SPEC_WARRIOR_PROTECTION: + VerifyAndPushSpellCast(spells, SPELL_SHOCKWAVE, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_CONCUSSION_BLOW, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_DISARM, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_LAST_STAND, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_BLOCK, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_SLAM, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_WALL, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_SPELL_REFLECTION, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_DEVASTATE, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 1); + break; + case SPEC_WARRIOR_ARMS: + VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_MORTAL_STRIKE, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_BLADESTORM, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_REND, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_RETALIATION, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_SHATTERING_THROW, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_SWEEPING_STRIKES, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_THUNDER_CLAP, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15); + break; + case SPEC_WARRIOR_FURY: + VerifyAndPushSpellCast(spells, SPELL_DEATH_WISH, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_BLOODTHIRST, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_DEMO_SHOUT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_EXECUTE, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_HEROIC_FURY, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_RECKLESSNESS, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_PIERCING_HOWL, TARGET_VICTIM, 2); + break; + } + break; + case CLASS_PALADIN: + VerifyAndPushSpellCast(spells, SPELL_AURA_MASTERY, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_LAY_ON_HANDS, TARGET_CHARMER, 8); + VerifyAndPushSpellCast(spells, SPELL_BLESSING_OF_MIGHT, TARGET_CHARMER, 8); + VerifyAndPushSpellCast(spells, SPELL_AVENGING_WRATH, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_PROTECTION, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_SHIELD, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_JUSTICE, TARGET_VICTIM, 6); + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_FREEDOM, TARGET_SELF, 3); + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, TARGET_SELF, 1); + if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID())) + { + if (creatureCharmer->IsDungeonBoss() || creatureCharmer->isWorldBoss()) + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_SACRIFICE, creatureCharmer, 10); + else + VerifyAndPushSpellCast(spells, SPELL_HAND_OF_PROTECTION, creatureCharmer, 3); + } + + switch (GetSpec()) + { + case SPEC_PALADIN_PROTECTION: + VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_RIGHTEOUS, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_SACRIFICE, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_SHIELD_OF_RIGHTEOUS, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_CONSECRATION, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_HOLY_SHIELD, TARGET_NONE, 1); + break; + case SPEC_PALADIN_HOLY: + VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_HOLY_SHOCK, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_FLASH_OF_LIGHT, TARGET_CHARMER, 4); + VerifyAndPushSpellCast(spells, SPELL_HOLY_LIGHT, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_FAVOR, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_ILLUMINATION, TARGET_NONE, 3); + break; + case SPEC_PALADIN_RETRIBUTION: + VerifyAndPushSpellCast(spells, SPELL_CRUSADER_STRIKE, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_STORM, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_JUDGEMENT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_HAMMER_OF_WRATH, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_RIGHTEOUS_FURY, TARGET_NONE, 2); + break; + } + break; + case CLASS_HUNTER: + VerifyAndPushSpellCast(spells, SPELL_DETERRENCE, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_TRAP, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_FREEZING_ARROW, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_RAPID_FIRE, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_KILL_SHOT, TARGET_VICTIM, 10); + if (me->GetVictim() && me->GetVictim()->getPowerType() == POWER_MANA && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_VIPER_STING, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_VIPER_STING, TARGET_VICTIM, 5); + + switch (GetSpec()) + { + case SPEC_HUNTER_BEAST_MASTERY: + VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 2); + break; + case SPEC_HUNTER_MARKSMANSHIP: + VerifyAndPushSpellCast(spells, SPELL_AIMED_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_CHIMERA_SHOT, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_SHOT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_READINESS, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_SILENCING_SHOT, TARGET_VICTIM, 5); + break; + case SPEC_HUNTER_SURVIVAL: + VerifyAndPushSpellCast(spells, SPELL_EXPLOSIVE_SHOT, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_BLACK_ARROW, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_MULTI_SHOT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_STEADY_SHOT, TARGET_VICTIM, 1); + break; + } + break; + case CLASS_ROGUE: + { + VerifyAndPushSpellCast(spells, SPELL_DISMANTLE, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_EVASION, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_VANISH, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_BLIND, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_CLOAK_OF_SHADOWS, TARGET_NONE, 2); + + uint32 builder, finisher; + switch (GetSpec()) + { + case SPEC_ROGUE_ASSASSINATION: + builder = SPELL_MUTILATE, finisher = SPELL_ENVENOM; + VerifyAndPushSpellCast(spells, SPELL_COLD_BLOOD, TARGET_NONE, 20); + break; + case SPEC_ROGUE_COMBAT: + builder = SPELL_SINISTER_STRIKE, finisher = SPELL_EVISCERATE; + VerifyAndPushSpellCast(spells, SPELL_ADRENALINE_RUSH, TARGET_NONE, 6); + VerifyAndPushSpellCast(spells, SPELL_BLADE_FLURRY, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_KILLING_SPREE, TARGET_NONE, 25); + break; + case SPEC_ROGUE_SUBLETY: + builder = SPELL_HEMORRHAGE, finisher = SPELL_EVISCERATE; + VerifyAndPushSpellCast(spells, SPELL_PREPARATION, TARGET_NONE, 10); + if (!me->IsWithinMeleeRange(me->GetVictim())) + VerifyAndPushSpellCast(spells, SPELL_SHADOWSTEP, TARGET_VICTIM, 25); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_DANCE, TARGET_NONE, 10); + break; + } + + if (Unit* victim = me->GetVictim()) + { + if (victim->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_KICK, TARGET_VICTIM, 25); + + uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0; + if (cp >= 4) + VerifyAndPushSpellCast(spells, finisher, TARGET_VICTIM, 10); + if (cp <= 4) + VerifyAndPushSpellCast(spells, builder, TARGET_VICTIM, 5); + } + break; + } + case CLASS_PRIEST: + VerifyAndPushSpellCast(spells, SPELL_FEAR_WARD, TARGET_SELF, 2); + VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_FORT, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_SPIRIT, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_PROTECTION, TARGET_CHARMER, 2); + VerifyAndPushSpellCast(spells, SPELL_DIVINE_HYMN, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_HYMN_OF_HOPE, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_DEATH, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_PSYCHIC_SCREAM, TARGET_VICTIM, 3); + switch (GetSpec()) + { + case SPEC_PRIEST_DISCIPLINE: + VerifyAndPushSpellCast(spells, SPELL_POWER_WORD_SHIELD, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_INNER_FOCUS, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_PAIN_SUPPRESSION, TARGET_CHARMER, 15); + VerifyAndPushSpellCast(spells, SPELL_POWER_INFUSION, TARGET_CHARMER, 10); + VerifyAndPushSpellCast(spells, SPELL_PENANCE, TARGET_CHARMER, 3); + VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1); + break; + case SPEC_PRIEST_HOLY: + VerifyAndPushSpellCast(spells, SPELL_DESPERATE_PRAYER, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_GUARDIAN_SPIRIT, TARGET_CHARMER, 5); + VerifyAndPushSpellCast(spells, SPELL_FLASH_HEAL, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_RENEW, TARGET_CHARMER, 3); + break; + case SPEC_PRIEST_SHADOW: + if (!me->HasAura(SPELL_SHADOWFORM)) + { + VerifyAndPushSpellCast(spells, SPELL_SHADOWFORM, TARGET_NONE, 100); + break; + } + if (Unit* victim = me->GetVictim()) + { + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_VAMPIRIC_TOUCH, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_TOUCH, TARGET_VICTIM, 4); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_SHADOW_WORD_PAIN, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_SHADOW_WORD_PAIN, TARGET_VICTIM, 3); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_DEVOURING_PLAGUE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_DEVOURING_PLAGUE, TARGET_VICTIM, 4); + } + VerifyAndPushSpellCast(spells, SPELL_MIND_BLAST, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_MIND_FLAY, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_DISPERSION, TARGET_NONE, 10); + break; + } + break; + case CLASS_DEATH_KNIGHT: + { + if (!me->IsWithinMeleeRange(me->GetVictim())) + VerifyAndPushSpellCast(spells, SPELL_DEATH_GRIP, TARGET_VICTIM, 25); + VerifyAndPushSpellCast(spells, SPELL_STRANGULATE, TARGET_VICTIM, 15); + VerifyAndPushSpellCast(spells, SPELL_EMPOWER_RUNE_WEAP, TARGET_NONE, 5); + VerifyAndPushSpellCast(spells, SPELL_ICEBORN_FORTITUDE, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_SHELL, TARGET_NONE, 10); + + bool hasFF = false, hasBP = false; + if (Unit* victim = me->GetVictim()) + { + if (victim->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_MIND_FREEZE, TARGET_VICTIM, 25); + + hasFF = !!victim->GetAuraApplicationOfRankedSpell(AURA_FROST_FEVER, me->GetGUID()), hasBP = !!victim->GetAuraApplicationOfRankedSpell(AURA_BLOOD_PLAGUE, me->GetGUID()); + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_PESTILENCE, TARGET_VICTIM, 3); + if (!hasFF) + VerifyAndPushSpellCast(spells, SPELL_ICY_TOUCH, TARGET_VICTIM, 4); + if (!hasBP) + VerifyAndPushSpellCast(spells, SPELL_PLAGUE_STRIKE, TARGET_VICTIM, 4); + } + switch (GetSpec()) + { + case SPEC_DEATH_KNIGHT_BLOOD: + VerifyAndPushSpellCast(spells, SPELL_RUNE_TAP, TARGET_NONE, 2); + VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, TARGET_SELF, 5); + if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID())) + if (!creatureCharmer->IsDungeonBoss() && !creatureCharmer->isWorldBoss()) + VerifyAndPushSpellCast(spells, SPELL_HYSTERIA, creatureCharmer, 15); + VerifyAndPushSpellCast(spells, SPELL_HEART_STRIKE, TARGET_VICTIM, 2); + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_DEATH_STRIKE, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_MARK_OF_BLOOD, TARGET_VICTIM, 20); + VerifyAndPushSpellCast(spells, SPELL_VAMPIRIC_BLOOD, TARGET_NONE, 10); + break; + case SPEC_DEATH_KNIGHT_FROST: + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_OBLITERATE, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_HOWLING_BLAST, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_UNBREAKABLE_ARMOR, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_DEATHCHILL, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_FROST_STRIKE, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1); + break; + case SPEC_DEATH_KNIGHT_UNHOLY: + if (hasFF && hasBP) + VerifyAndPushSpellCast(spells, SPELL_SCOURGE_STRIKE, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_DEATH_AND_DECAY, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_ANTI_MAGIC_ZONE, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_SUMMON_GARGOYLE, TARGET_VICTIM, 7); + VerifyAndPushSpellCast(spells, SPELL_BLOOD_STRIKE, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_DK, TARGET_VICTIM, 3); + break; + } + break; + } + case CLASS_SHAMAN: + VerifyAndPushSpellCast(spells, SPELL_HEROISM, TARGET_NONE, 25); + VerifyAndPushSpellCast(spells, SPELL_BLOODLUST, TARGET_NONE, 25); + VerifyAndPushSpellCast(spells, SPELL_GROUNDING_TOTEM, TARGET_NONE, 2); + switch (GetSpec()) + { + case SPEC_SHAMAN_RESTORATION: + if (Unit* charmer = me->GetCharmer()) + if (!charmer->GetAuraApplicationOfRankedSpell(SPELL_EARTH_SHIELD, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_EARTH_SHIELD, charmer, 2); + if (me->HasAura(SPELL_SHA_NATURE_SWIFT)) + VerifyAndPushSpellCast(spells, SPELL_HEALING_WAVE, TARGET_CHARMER, 20); + else + VerifyAndPushSpellCast(spells, SPELL_LESSER_HEAL_WAVE, TARGET_CHARMER, 1); + VerifyAndPushSpellCast(spells, SPELL_TIDAL_FORCE, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_SHA_NATURE_SWIFT, TARGET_NONE, 4); + VerifyAndPushSpellCast(spells, SPELL_MANA_TIDE_TOTEM, TARGET_NONE, 3); + break; + case SPEC_SHAMAN_ELEMENTAL: + if (Unit* victim = me->GetVictim()) + { + if (victim->GetAuraOfRankedSpell(SPELL_FLAME_SHOCK, GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_LAVA_BURST, TARGET_VICTIM, 5); + else + VerifyAndPushSpellCast(spells, SPELL_FLAME_SHOCK, TARGET_VICTIM, 3); + } + VerifyAndPushSpellCast(spells, SPELL_CHAIN_LIGHTNING, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_ELEMENTAL_MASTERY, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_THUNDERSTORM, TARGET_NONE, 3); + break; + case SPEC_SHAMAN_ENHANCEMENT: + if (Aura const* maelstrom = me->GetAura(AURA_MAELSTROM_WEAPON)) + if (maelstrom->GetStackAmount() == 5) + VerifyAndPushSpellCast(spells, SPELL_LIGHTNING_BOLT, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_STORMSTRIKE, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_EARTH_SHOCK, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_LAVA_LASH, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_SHAMANISTIC_RAGE, TARGET_NONE, 10); + break; + } + break; + case CLASS_MAGE: + if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_COUNTERSPELL, TARGET_VICTIM, 25); + VerifyAndPushSpellCast(spells, SPELL_DAMPEN_MAGIC, TARGET_CHARMER, 2); + VerifyAndPushSpellCast(spells, SPELL_EVOCATION, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_MANA_SHIELD, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_MIRROR_IMAGE, TARGET_NONE, 3); + VerifyAndPushSpellCast(spells, SPELL_SPELLSTEAL, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_ICE_BLOCK, TARGET_NONE, 1); + VerifyAndPushSpellCast(spells, SPELL_ICY_VEINS, TARGET_NONE, 3); + switch (GetSpec()) + { + case SPEC_MAGE_ARCANE: + if (Aura* abAura = me->GetAura(AURA_ARCANE_BLAST)) + if (abAura->GetStackAmount() >= 3) + VerifyAndPushSpellCast(spells, SPELL_ARCANE_MISSILES, TARGET_VICTIM, 7); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_BLAST, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_BARRAGE, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_ARCANE_POWER, TARGET_NONE, 8); + VerifyAndPushSpellCast(spells, SPELL_PRESENCE_OF_MIND, TARGET_NONE, 7); + break; + case SPEC_MAGE_FIRE: + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_LIVING_BOMB)) + VerifyAndPushSpellCast(spells, SPELL_LIVING_BOMB, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_COMBUSTION, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_FIREBALL, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_FIRE_BLAST, TARGET_VICTIM, 1); + VerifyAndPushSpellCast(spells, SPELL_DRAGONS_BREATH, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_BLAST_WAVE, TARGET_VICTIM, 1); + break; + case SPEC_MAGE_FROST: + VerifyAndPushSpellCast(spells, SPELL_DEEP_FREEZE, TARGET_VICTIM, 10); + VerifyAndPushSpellCast(spells, SPELL_FROST_NOVA, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_FROSTBOLT, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_COLD_SNAP, TARGET_VICTIM, 5); + if (me->GetVictim() && me->GetVictim()->HasAuraState(AURA_STATE_FROZEN, nullptr, me)) + VerifyAndPushSpellCast(spells, SPELL_ICE_LANCE, TARGET_VICTIM, 5); + break; + } + break; + case CLASS_WARLOCK: + VerifyAndPushSpellCast(spells, SPELL_DEATH_COIL_W, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_FEAR, TARGET_VICTIM, 2); + VerifyAndPushSpellCast(spells, SPELL_SEED_OF_CORRUPTION, TARGET_VICTIM, 4); + VerifyAndPushSpellCast(spells, SPELL_HOWL_OF_TERROR, TARGET_NONE, 2); + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_CORRUPTION, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_CORRUPTION, TARGET_VICTIM, 10); + switch (GetSpec()) + { + case SPEC_WARLOCK_AFFLICTION: + if (Unit* victim = me->GetVictim()) + { + VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_UNSTABLE_AFFLICTION, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_UNSTABLE_AFFLICTION, TARGET_VICTIM, 8); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_HAUNT, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_HAUNT, TARGET_VICTIM, 8); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_CURSE_OF_AGONY, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_CURSE_OF_AGONY, TARGET_VICTIM, 4); + if (victim->HealthBelowPct(25)) + VerifyAndPushSpellCast(spells, SPELL_DRAIN_SOUL, TARGET_VICTIM, 100); + } + break; + case SPEC_WARLOCK_DEMONOLOGY: + VerifyAndPushSpellCast(spells, SPELL_METAMORPHOSIS, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_SHADOW_BOLT, TARGET_VICTIM, 7); + if (me->HasAura(AURA_DECIMATION)) + VerifyAndPushSpellCast(spells, SPELL_SOUL_FIRE, TARGET_VICTIM, 100); + if (me->HasAura(SPELL_METAMORPHOSIS)) + { + VerifyAndPushSpellCast(spells, SPELL_IMMOLATION_AURA, TARGET_NONE, 30); + if (!me->IsWithinMeleeRange(me->GetVictim())) + VerifyAndPushSpellCast(spells, SPELL_DEMON_CHARGE, TARGET_VICTIM, 20); + } + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 5); + if (me->HasAura(AURA_MOLTEN_CORE)) + VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 10); + break; + case SPEC_WARLOCK_DESTRUCTION: + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_IMMOLATE, TARGET_VICTIM, 8); + if (me->GetVictim() && me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_IMMOLATE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_CONFLAGRATE, TARGET_VICTIM, 8); + VerifyAndPushSpellCast(spells, SPELL_SHADOWFURY, TARGET_VICTIM, 5); + VerifyAndPushSpellCast(spells, SPELL_CHAOS_BOLT, TARGET_VICTIM, 10); + VerifyAndPushSpellCast(spells, SPELL_SHADOWBURN, TARGET_VICTIM, 3); + VerifyAndPushSpellCast(spells, SPELL_INCINERATE, TARGET_VICTIM, 7); + break; + } + break; + case CLASS_DRUID: + VerifyAndPushSpellCast(spells, SPELL_INNERVATE, TARGET_CHARMER, 5); + VerifyAndPushSpellCast(spells, SPELL_BARKSKIN, TARGET_NONE, 5); + switch (GetSpec()) + { + case SPEC_DRUID_RESTORATION: + if (!me->HasAura(SPELL_TREE_OF_LIFE)) + { + CancelAllShapeshifts(); + VerifyAndPushSpellCast(spells, SPELL_TREE_OF_LIFE, TARGET_NONE, 100); + break; + } + VerifyAndPushSpellCast(spells, SPELL_TRANQUILITY, TARGET_NONE, 10); + VerifyAndPushSpellCast(spells, SPELL_NATURE_SWIFTNESS, TARGET_NONE, 7); + if (Creature* creatureCharmer = ObjectAccessor::GetCreature(*me, me->GetCharmerGUID())) + { + VerifyAndPushSpellCast(spells, SPELL_NOURISH, creatureCharmer, 5); + VerifyAndPushSpellCast(spells, SPELL_WILD_GROWTH, creatureCharmer, 5); + if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_REJUVENATION, creatureCharmer, 8); + if (!creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_REGROWTH, creatureCharmer, 8); + uint8 lifebloomStacks = 0; + if (Aura const* lifebloom = creatureCharmer->GetAura(SPELL_LIFEBLOOM, me->GetGUID())) + lifebloomStacks = lifebloom->GetStackAmount(); + if (lifebloomStacks < 3) + VerifyAndPushSpellCast(spells, SPELL_LIFEBLOOM, creatureCharmer, 5); + if (creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REJUVENATION) || + creatureCharmer->GetAuraApplicationOfRankedSpell(SPELL_REGROWTH)) + VerifyAndPushSpellCast(spells, SPELL_SWIFTMEND, creatureCharmer, 10); + if (me->HasAura(SPELL_NATURE_SWIFTNESS)) + VerifyAndPushSpellCast(spells, SPELL_HEALING_TOUCH, creatureCharmer, 100); + } + break; + case SPEC_DRUID_BALANCE: + { + if (!me->HasAura(SPELL_MOONKIN_FORM)) + { + CancelAllShapeshifts(); + VerifyAndPushSpellCast(spells, SPELL_MOONKIN_FORM, TARGET_NONE, 100); + break; + } + uint32 const mainAttackSpell = me->HasAura(AURA_ECLIPSE_LUNAR) ? SPELL_STARFIRE : SPELL_WRATH; + VerifyAndPushSpellCast(spells, SPELL_STARFALL, TARGET_NONE, 20); + VerifyAndPushSpellCast(spells, mainAttackSpell, TARGET_VICTIM, 10); + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_INSECT_SWARM, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_INSECT_SWARM, TARGET_VICTIM, 7); + if (me->GetVictim() && !me->GetVictim()->GetAuraApplicationOfRankedSpell(SPELL_MOONFIRE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_MOONFIRE, TARGET_VICTIM, 5); + if (me->GetVictim() && me->GetVictim()->HasUnitState(UNIT_STATE_CASTING)) + VerifyAndPushSpellCast(spells, SPELL_TYPHOON, TARGET_NONE, 15); + break; + } + case SPEC_DRUID_FERAL: + if (!me->HasAura(SPELL_CAT_FORM)) + { + CancelAllShapeshifts(); + VerifyAndPushSpellCast(spells, SPELL_CAT_FORM, TARGET_NONE, 100); + break; + } + VerifyAndPushSpellCast(spells, SPELL_BERSERK, TARGET_NONE, 20); + VerifyAndPushSpellCast(spells, SPELL_SURVIVAL_INSTINCTS, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_TIGER_FURY, TARGET_NONE, 15); + VerifyAndPushSpellCast(spells, SPELL_DASH, TARGET_NONE, 5); + if (Unit* victim = me->GetVictim()) + { + uint8 const cp = (me->GetComboTarget() == victim->GetGUID()) ? me->GetComboPoints() : 0; + if (victim->HasUnitState(UNIT_STATE_CASTING) && cp >= 1) + VerifyAndPushSpellCast(spells, SPELL_MAIM, TARGET_VICTIM, 25); + if (!me->IsWithinMeleeRange(victim)) + VerifyAndPushSpellCast(spells, SPELL_FERAL_CHARGE_CAT, TARGET_VICTIM, 25); + if (cp >= 4) + VerifyAndPushSpellCast(spells, SPELL_RIP, TARGET_VICTIM, 50); + if (cp <= 4) + { + VerifyAndPushSpellCast(spells, SPELL_MANGLE_CAT, TARGET_VICTIM, 10); + VerifyAndPushSpellCast(spells, SPELL_CLAW, TARGET_VICTIM, 5); + if (!victim->GetAuraApplicationOfRankedSpell(SPELL_RAKE, me->GetGUID())) + VerifyAndPushSpellCast(spells, SPELL_RAKE, TARGET_VICTIM, 8); + if (!me->HasAura(SPELL_SAVAGE_ROAR)) + VerifyAndPushSpellCast(spells, SPELL_SAVAGE_ROAR, TARGET_NONE, 15); + } + } + break; + } + break; + } + + return SelectSpellCast(spells); +} + +static const float CASTER_CHASE_DISTANCE = 28.0f; +void SimpleCharmedPlayerAI::UpdateAI(const uint32 diff) { Creature* charmer = me->GetCharmer() ? me->GetCharmer()->ToCreature() : nullptr; if (!charmer) @@ -177,7 +1271,7 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/) for (Player::AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter) if ((*iter)->GetCasterGUID() == charmer->GetGUID() && (*iter)->GetBase()->IsPermanent()) { - me->Kill(me); + me->KillSelf(); return; } } @@ -192,10 +1286,54 @@ void SimpleCharmedPlayerAI::UpdateAI(const uint32 /*diff*/) return; if (IsRangedAttacker()) - AttackStartCaster(target, 28.0f); + { + _chaseCloser = !me->IsWithinLOSInMap(target); + if (_chaseCloser) + AttackStart(target); + else + AttackStartCaster(target, CASTER_CHASE_DISTANCE); + } else AttackStart(target); + _forceFacing = true; } + + if (me->IsStopped() && !me->HasUnitState(UNIT_STATE_CANNOT_TURN)) + { + float targetAngle = me->GetAngle(target); + if (_forceFacing || fabs(me->GetOrientation() - targetAngle) > 0.4f) + { + me->SetFacingTo(targetAngle); + _forceFacing = false; + } + } + + if (_castCheckTimer <= diff) + { + if (me->HasUnitState(UNIT_STATE_CASTING)) + _castCheckTimer = 0; + else + { + if (IsRangedAttacker()) + { // chase to zero if the target isn't in line of sight + bool inLOS = me->IsWithinLOSInMap(target); + if (_chaseCloser != !inLOS) + { + _chaseCloser = !inLOS; + if (_chaseCloser) + AttackStart(target); + else + AttackStartCaster(target, CASTER_CHASE_DISTANCE); + } + } + if (TargetedSpell shouldCast = SelectAppropriateCastForSpec()) + DoCastAtTarget(shouldCast); + _castCheckTimer = 500; + } + } + else + _castCheckTimer -= diff; + DoAutoAttackIfReady(); } else @@ -214,6 +1352,9 @@ void SimpleCharmedPlayerAI::OnCharmed(bool apply) { me->CastStop(); me->AttackStop(); + me->StopMoving(); + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MovePoint(0, me->GetPosition(), false); // force re-sync of current position for all clients } else { diff --git a/src/server/game/AI/PlayerAI/PlayerAI.h b/src/server/game/AI/PlayerAI/PlayerAI.h index b717816f9a3..18f65485161 100644 --- a/src/server/game/AI/PlayerAI/PlayerAI.h +++ b/src/server/game/AI/PlayerAI/PlayerAI.h @@ -20,41 +20,99 @@ #include "UnitAI.h" #include "Player.h" +#include "Spell.h" #include "Creature.h" class TC_GAME_API PlayerAI : public UnitAI { public: - explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { } + explicit PlayerAI(Player* player) : UnitAI(static_cast<Unit*>(player)), me(player), _selfSpec(PlayerAI::GetPlayerSpec(player)), _isSelfHealer(PlayerAI::IsPlayerHealer(player)), _isSelfRangedAttacker(PlayerAI::IsPlayerRangedAttacker(player)) { } void OnCharmed(bool /*apply*/) override { } // charm AI application for players is handled by Unit::SetCharmedBy / Unit::RemoveCharmedBy // helper functions to determine player info + // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned. + static uint8 GetPlayerSpec(Player const* who); + // Return values range from 0 (left-most spec) to 2 (right-most spec). If two specs have the same number of talent points, the left-most of those specs is returned. + uint8 GetSpec(Player const* who = nullptr) const { return (!who || who == me) ? _selfSpec : GetPlayerSpec(who); } static bool IsPlayerHealer(Player const* who); bool IsHealer(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfHealer : IsPlayerHealer(who); } static bool IsPlayerRangedAttacker(Player const* who); bool IsRangedAttacker(Player const* who = nullptr) const { return (!who || who == me) ? _isSelfRangedAttacker : IsPlayerRangedAttacker(who); } protected: + struct TargetedSpell : public std::pair<Spell*, Unit*> + { + TargetedSpell() : pair<Spell*, Unit*>() { } + TargetedSpell(Spell* first, Unit* second) : pair<Spell*, Unit*>(first, second) { } + explicit operator bool() { return !!first; } + }; + typedef std::pair<TargetedSpell, uint32> PossibleSpell; + typedef std::vector<PossibleSpell> PossibleSpellVector; + Player* const me; void SetIsRangedAttacker(bool state) { _isSelfRangedAttacker = state; } // this allows overriding of the default ranged attacker detection + enum SpellTarget + { + TARGET_NONE, + TARGET_VICTIM, + TARGET_CHARMER, + TARGET_SELF + }; + /* Check if the specified spell can be cast on that target. + Caller is responsible for cleaning up created Spell object from pointer. */ + TargetedSpell VerifySpellCast(uint32 spellId, Unit* target); + /* Check if the specified spell can be cast on that target. + Caller is responsible for cleaning up created Spell object from pointer. */ + TargetedSpell VerifySpellCast(uint32 spellId, SpellTarget target); + + /* Helper method - checks spell cast, then pushes it onto provided vector if valid. */ + template<typename T> inline void VerifyAndPushSpellCast(PossibleSpellVector& spells, uint32 spellId, T target, uint32 weight) + { + if (TargetedSpell spell = VerifySpellCast(spellId, target)) + spells.push_back({ spell,weight }); + } + + /* Helper method - selects one spell from the vector and returns it, while deleting everything else. + This invalidates the vector, and empties it to prevent accidental misuse. */ + TargetedSpell SelectSpellCast(PossibleSpellVector& spells); + /* Helper method - casts the included spell at the included target */ + inline void DoCastAtTarget(TargetedSpell spell) + { + SpellCastTargets targets; + targets.SetUnitTarget(spell.second); + spell.first->prepare(&targets); + } + virtual Unit* SelectAttackTarget() const { return me->GetCharmer() ? me->GetCharmer()->GetVictim() : nullptr; } void DoRangedAttackIfReady(); void DoAutoAttackIfReady(); + // Cancels all shapeshifts that the player could voluntarily cancel + void CancelAllShapeshifts(); + private: - bool _isSelfHealer; + uint8 const _selfSpec; + bool const _isSelfHealer; bool _isSelfRangedAttacker; }; class SimpleCharmedPlayerAI : public PlayerAI { public: - SimpleCharmedPlayerAI(Player* player) : PlayerAI(player) { } + SimpleCharmedPlayerAI(Player* player) : PlayerAI(player), _castCheckTimer(500), _chaseCloser(false), _forceFacing(true) { } void UpdateAI(uint32 diff) override; void OnCharmed(bool apply) override; + + protected: Unit* SelectAttackTarget() const override; + + private: + TargetedSpell SelectAppropriateCastForSpec(); + uint32 _castCheckTimer; + bool _chaseCloser; + bool _forceFacing; }; #endif diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 6f04a852b61..6475cc8117e 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -32,7 +32,7 @@ struct TSpellSummary uint8 Effects; // set of enum SelectEffect } extern* SpellSummary; -void SummonList::DoZoneInCombat(uint32 entry) +void SummonList::DoZoneInCombat(uint32 entry, float maxRangeToNearestTarget) { for (StorageType::iterator i = storage_.begin(); i != storage_.end();) { @@ -41,7 +41,7 @@ void SummonList::DoZoneInCombat(uint32 entry) if (summon && summon->IsAIEnabled && (!entry || summon->GetEntry() == entry)) { - summon->AI()->DoZoneInCombat(); + summon->AI()->DoZoneInCombat(nullptr, maxRangeToNearestTarget); } } } @@ -183,22 +183,22 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec { //No target so we can't cast if (!target) - return NULL; + return nullptr; //Silenced so we can't cast if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED)) - return NULL; + return nullptr; //Using the extended script system we first create a list of viable spells - SpellInfo const* apSpell[CREATURE_MAX_SPELLS]; - memset(apSpell, 0, CREATURE_MAX_SPELLS * sizeof(SpellInfo*)); + SpellInfo const* apSpell[MAX_CREATURE_SPELLS]; + memset(apSpell, 0, MAX_CREATURE_SPELLS * sizeof(SpellInfo*)); uint32 spellCount = 0; - SpellInfo const* tempSpell = NULL; + SpellInfo const* tempSpell = nullptr; //Check if each spell is viable(set it to null if not) - for (uint32 i = 0; i < CREATURE_MAX_SPELLS; i++) + for (uint32 i = 0; i < MAX_CREATURE_SPELLS; i++) { tempSpell = sSpellMgr->GetSpellInfo(me->m_spells[i]); @@ -251,7 +251,7 @@ SpellInfo const* ScriptedAI::SelectSpell(Unit* target, uint32 school, uint32 mec //We got our usable spells so now lets randomly pick one if (!spellCount) - return NULL; + return nullptr; return apSpell[urand(0, spellCount - 1)]; } @@ -327,7 +327,7 @@ void ScriptedAI::DoTeleportAll(float x, float y, float z, float o) Unit* ScriptedAI::DoSelectLowestHpFriendly(float range, uint32 minHPDiff) { - Unit* unit = NULL; + Unit* unit = nullptr; Trinity::MostHPMissingInRange u_check(me, range, minHPDiff); Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(me, unit, u_check); me->VisitNearbyObject(range, searcher); @@ -357,7 +357,7 @@ std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float range, uint32 u Player* ScriptedAI::GetPlayerAtMinimumRange(float minimumRange) { - Player* player = NULL; + Player* player = nullptr; CellCoord pair(Trinity::ComputeCellCoord(me->GetPositionX(), me->GetPositionY())); Cell cell(pair); @@ -547,7 +547,7 @@ void BossAI::UpdateAI(uint32 diff) DoMeleeAttackIfReady(); } -void BossAI::_DespawnAtEvade(uint32 delayToRespawn) +void BossAI::_DespawnAtEvade(uint32 delayToRespawn, Creature* who) { if (delayToRespawn < 2) { @@ -555,18 +555,28 @@ void BossAI::_DespawnAtEvade(uint32 delayToRespawn) delayToRespawn = 2; } - uint32 corpseDelay = me->GetCorpseDelay(); - uint32 respawnDelay = me->GetRespawnDelay(); + if (!who) + who = me; - me->SetCorpseDelay(1); - me->SetRespawnDelay(delayToRespawn - 1); + if (TempSummon* whoSummon = who->ToTempSummon()) + { + TC_LOG_WARN("scripts", "_DespawnAtEvade called on a temporary summon."); + whoSummon->UnSummon(); + return; + } - me->DespawnOrUnsummon(); + uint32 corpseDelay = who->GetCorpseDelay(); + uint32 respawnDelay = who->GetRespawnDelay(); - me->SetCorpseDelay(corpseDelay); - me->SetRespawnDelay(respawnDelay); + who->SetCorpseDelay(1); + who->SetRespawnDelay(delayToRespawn - 1); - if (instance) + who->DespawnOrUnsummon(); + + who->SetCorpseDelay(corpseDelay); + who->SetRespawnDelay(respawnDelay); + + if (instance && who == me) instance->SetBossState(_bossId, FAIL); } @@ -628,27 +638,27 @@ void WorldBossAI::UpdateAI(uint32 diff) } // SD2 grid searchers. -TC_GAME_API Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive /*= true*/) +Creature* GetClosestCreatureWithEntry(WorldObject* source, uint32 entry, float maxSearchRange, bool alive /*= true*/) { return source->FindNearestCreature(entry, maxSearchRange, alive); } -TC_GAME_API GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange) +GameObject* GetClosestGameObjectWithEntry(WorldObject* source, uint32 entry, float maxSearchRange) { return source->FindNearestGameObject(entry, maxSearchRange); } -TC_GAME_API void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange) +void GetCreatureListWithEntryInGrid(std::list<Creature*>& list, WorldObject* source, uint32 entry, float maxSearchRange) { source->GetCreatureListWithEntryInGrid(list, entry, maxSearchRange); } -TC_GAME_API void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange) +void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& list, WorldObject* source, uint32 entry, float maxSearchRange) { source->GetGameObjectListWithEntryInGrid(list, entry, maxSearchRange); } -TC_GAME_API void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange) +void GetPlayerListInGrid(std::list<Player*>& list, WorldObject* source, float maxSearchRange) { source->GetPlayerListInGrid(list, maxSearchRange); } diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 42befa26a23..6144a4e5203 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -114,7 +114,7 @@ public: } } - void DoZoneInCombat(uint32 entry = 0); + void DoZoneInCombat(uint32 entry = 0, float maxRangeToNearestTarget = 250.0f); void RemoveNotExisting(); bool HasEntry(uint32 entry) const; @@ -367,7 +367,7 @@ class TC_GAME_API BossAI : public ScriptedAI void _EnterCombat(); void _JustDied(); void _JustReachedHome() { me->setActive(false); } - void _DespawnAtEvade(uint32 delayToRespawn = 30); + void _DespawnAtEvade(uint32 delayToRespawn = 30, Creature* who = nullptr); void TeleportCheaters(); diff --git a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp index fa80634e0f0..1b8b472b805 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp @@ -72,7 +72,7 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* who) return false; //experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS)) + if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; //not a player diff --git a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp index 778edf8cab5..bd07e688fb0 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp @@ -69,7 +69,7 @@ bool FollowerAI::AssistPlayerInCombat(Unit* who) return false; //experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS)) + if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; //not a player diff --git a/src/server/game/AI/SmartScripts/SmartAI.cpp b/src/server/game/AI/SmartScripts/SmartAI.cpp index 361bb1a5b1d..e21f59fe582 100644 --- a/src/server/game/AI/SmartScripts/SmartAI.cpp +++ b/src/server/game/AI/SmartScripts/SmartAI.cpp @@ -413,15 +413,10 @@ void SmartAI::EnterEvadeMode(EvadeReason /*why*/) if (!me->IsAlive() || me->IsInEvadeMode()) return; - me->RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_CLONE_CASTER); + me->RemoveAurasOnEvade(); me->AddUnitState(UNIT_STATE_EVADE); - me->DeleteThreatList(); - me->CombatStop(true); - me->LoadCreaturesAddon(); - me->SetLootRecipient(NULL); - me->ResetPlayerDamageReq(); - me->SetLastDamagedTime(0); + _EnterEvadeMode(); GetScript()->ProcessEventsFor(SMART_EVENT_EVADE);//must be after aura clear so we can cast spells from db @@ -473,7 +468,7 @@ bool SmartAI::AssistPlayerInCombat(Unit* who) return false; //experimental (unknown) flag not present - if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS)) + if (!(me->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST)) return false; //not a player @@ -653,8 +648,8 @@ void SmartAI::OnCharmed(bool apply) { GetScript()->ProcessEventsFor(SMART_EVENT_CHARMED, NULL, 0, 0, apply); - if (!apply && !me->IsInEvadeMode() && me->GetCharmerGUID()) - if (Unit* charmer = ObjectAccessor::GetUnit(*me, me->GetCharmerGUID())) + if (!apply && !me->IsInEvadeMode()) + if (Unit* charmer = me->GetCharmer()) AttackStart(charmer); } diff --git a/src/server/game/AI/SmartScripts/SmartScript.cpp b/src/server/game/AI/SmartScripts/SmartScript.cpp index 9db36c76bb4..7c258e09b09 100644 --- a/src/server/game/AI/SmartScripts/SmartScript.cpp +++ b/src/server/game/AI/SmartScripts/SmartScript.cpp @@ -1037,10 +1037,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u target->DespawnOrUnsummon(e.action.forceDespawn.delay); } else if (GameObject* goTarget = (*itr)->ToGameObject()) - { - if (IsSmartGO(goTarget)) - goTarget->SetRespawnTime(e.action.forceDespawn.delay + 1); - } + goTarget->SetRespawnTime(e.action.forceDespawn.delay + 1); } delete targets; @@ -3239,29 +3236,28 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui if (!me) return; - WorldObject* creature = NULL; + Creature* creature = nullptr; if (e.event.distance.guid != 0) { creature = FindCreatureNear(me, e.event.distance.guid); - if (!creature) return; - if (!me->IsInRange(creature, 0, (float)e.event.distance.dist)) + if (!me->IsInRange(creature, 0, static_cast<float>(e.event.distance.dist))) return; } else if (e.event.distance.entry != 0) { std::list<Creature*> list; - me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist); + me->GetCreatureListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist)); if (!list.empty()) creature = list.front(); } if (creature) - ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat); + ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat, creature); break; } @@ -3270,29 +3266,28 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui if (!me) return; - WorldObject* gameobject = NULL; + GameObject* gameobject = nullptr; if (e.event.distance.guid != 0) { gameobject = FindGameObjectNear(me, e.event.distance.guid); - if (!gameobject) return; - if (!me->IsInRange(gameobject, 0, (float)e.event.distance.dist)) + if (!me->IsInRange(gameobject, 0, static_cast<float>(e.event.distance.dist))) return; } else if (e.event.distance.entry != 0) { std::list<GameObject*> list; - me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, (float)e.event.distance.dist); + me->GetGameObjectListWithEntryInGrid(list, e.event.distance.entry, static_cast<float>(e.event.distance.dist)); if (!list.empty()) gameobject = list.front(); } if (gameobject) - ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat); + ProcessTimedAction(e, e.event.distance.repeat, e.event.distance.repeat, nullptr, 0, 0, false, nullptr, gameobject); break; } diff --git a/src/server/game/Accounts/RBAC.h b/src/server/game/Accounts/RBAC.h index 5f883175157..d1d418b0931 100644 --- a/src/server/game/Accounts/RBAC.h +++ b/src/server/game/Accounts/RBAC.h @@ -540,7 +540,7 @@ enum RBACPermissions RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE = 642, RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS = 643, RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE = 644, - RBAC_PERM_COMMAND_RELOAD_GAME_GRAVEYARD_ZONE = 645, + RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE = 645, RBAC_PERM_COMMAND_RELOAD_GAME_TELE = 646, RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER = 647, RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE = 648, @@ -697,6 +697,10 @@ enum RBACPermissions // 799 - 834 6.x only RBAC_PERM_COMMAND_DEBUG_LOADCELLS = 835, RBAC_PERM_COMMAND_DEBUG_BOUNDARY = 836, + RBAC_PERM_COMMAND_NPC_EVADE = 837, + RBAC_PERM_COMMAND_PET_LEVEL = 838, + RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE = 839, + RBAC_PERM_COMMAND_SERVER_RESTART_FORCE = 840, // custom permissions 1000+ RBAC_PERM_MAX diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp index 87f5ff6ce5c..11c2635da33 100644 --- a/src/server/game/Achievements/AchievementMgr.cpp +++ b/src/server/game/Achievements/AchievementMgr.cpp @@ -646,13 +646,20 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement) if (achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL | ACHIEVEMENT_FLAG_REALM_FIRST_REACH)) { + uint32 team = GetPlayer()->GetTeam(); + // broadcast realm first reached WorldPacket data(SMSG_SERVER_FIRST_ACHIEVEMENT, GetPlayer()->GetName().size() + 1 + 8 + 4 + 4); data << GetPlayer()->GetName(); data << uint64(GetPlayer()->GetGUID()); data << uint32(achievement->ID); - data << uint32(0); // 1=link supplied string as player name, 0=display plain string - sWorld->SendGlobalMessage(&data); + + std::size_t linkTypePos = data.wpos(); + data << uint32(1); // display name as clickable link in chat + sWorld->SendGlobalMessage(&data, nullptr, team); + + data.put<uint32>(linkTypePos, 0); // display name as plain string in chat + sWorld->SendGlobalMessage(&data, nullptr, team == ALLIANCE ? HORDE : ALLIANCE); } // if player is in world he can tell his friends about new achievement else if (GetPlayer()->IsInWorld()) diff --git a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp index 52e0deaf86f..8c144cbc8be 100644 --- a/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp +++ b/src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp @@ -142,8 +142,8 @@ void BattlegroundIC::PostUpdateImpl(uint32 diff) { if (siege->IsAlive()) { - if (siege->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_UNK_14|UNIT_FLAG_IMMUNE_TO_PC)) - // following sniffs the vehicle always has UNIT_FLAG_UNK_14 + if (siege->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_CANNOT_SWIM|UNIT_FLAG_IMMUNE_TO_PC)) + // following sniffs the vehicle always has UNIT_FLAG_CANNOT_SWIM siege->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_IMMUNE_TO_PC); else siege->SetHealth(siege->GetMaxHealth()); @@ -762,7 +762,7 @@ void BattlegroundIC::HandleCapturedNodes(ICNodePoint* node, bool recapture) if (Creature* siegeEngine = GetBGCreature(siegeType)) { - siegeEngine->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_UNK_14|UNIT_FLAG_IMMUNE_TO_PC); + siegeEngine->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_CANNOT_SWIM|UNIT_FLAG_IMMUNE_TO_PC); siegeEngine->setFaction(BG_IC_Factions[(node->faction == TEAM_ALLIANCE ? 0 : 1)]); } } diff --git a/src/server/game/Conditions/ConditionMgr.cpp b/src/server/game/Conditions/ConditionMgr.cpp index afb6255079b..74e94135b86 100644 --- a/src/server/game/Conditions/ConditionMgr.cpp +++ b/src/server/game/Conditions/ConditionMgr.cpp @@ -738,7 +738,7 @@ bool ConditionMgr::IsObjectMeetToConditionList(ConditionSourceInfo& sourceInfo, //! If not found, add an entry in the store and set to true (placeholder) if (itr == elseGroupStore.end()) elseGroupStore[condition->ElseGroup] = true; - else if (!(*itr).second) + else if (!(*itr).second) //! If another condition in this group was unmatched before this, don't bother checking (the group is false anyway) continue; if (condition->ReferenceId)//handle reference diff --git a/src/server/game/DataStores/DBCEnums.h b/src/server/game/DataStores/DBCEnums.h index f0ea5b4a5f1..cb2f26e567f 100644 --- a/src/server/game/DataStores/DBCEnums.h +++ b/src/server/game/DataStores/DBCEnums.h @@ -19,6 +19,22 @@ #ifndef DBCENUMS_H #define DBCENUMS_H +#pragma pack(push, 1) + +struct DBCPosition2D +{ + float X; + float Y; +}; + +struct DBCPosition3D +{ + float X; + float Y; + float Z; +}; + +#pragma pack(pop) enum LevelLimit { // Client expected level limitation, like as used in DBC item max levels for "until max player level" diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp index 5ede70da2a3..36ec418ed56 100644 --- a/src/server/game/DataStores/DBCStores.cpp +++ b/src/server/game/DataStores/DBCStores.cpp @@ -27,9 +27,6 @@ #include <boost/regex.hpp> #include <map> -#include <fstream> -#include <iostream> -#include <iomanip> typedef std::map<uint16, uint32> AreaFlagByAreaID; typedef std::map<uint32, uint32> AreaFlagByMapID; @@ -221,8 +218,6 @@ DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt); DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore(WorldMapOverlayEntryfmt); DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt); -std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore; - typedef std::list<std::string> StoreProblemList; uint32 DBCFileCount = 0; @@ -724,250 +719,10 @@ void LoadDBCStores(const std::string& dataPath) exit(1); } - LoadM2Cameras(dataPath); - TC_LOG_INFO("server.loading", ">> Initialized %d data stores in %u ms", DBCFileCount, GetMSTimeDiffToNow(oldMSTime)); } -// Convert the geomoetry from a spline value, to an actual WoW XYZ -G3D::Vector3 TranslateLocation(G3D::Vector4 const* DBCPosition, G3D::Vector3 const* basePosition, G3D::Vector3 const* splineVector) -{ - G3D::Vector3 work; - float x = basePosition->x + splineVector->x; - float y = basePosition->y + splineVector->y; - float z = basePosition->z + splineVector->z; - float const distance = sqrt((x * x) + (y * y)); - float angle = std::atan2(x, y) - DBCPosition->w; - - if (angle < 0) - angle += 2 * float(M_PI); - - work.x = DBCPosition->x + (distance * sin(angle)); - work.y = DBCPosition->y + (distance * cos(angle)); - work.z = DBCPosition->z + z; - return work; -} - -// Number of cameras not used. Multiple cameras never used in 3.3.5 -bool readCamera(M2Camera const* cam, uint32 buffSize, M2Header const* header, CinematicCameraEntry const* dbcentry) -{ - char const* buffer = reinterpret_cast<char const*>(header); - - FlyByCameraCollection cameras; - FlyByCameraCollection targetcam; - - G3D::Vector4 DBCData; - DBCData.x = dbcentry->base_x; - DBCData.y = dbcentry->base_y; - DBCData.z = dbcentry->base_z; - DBCData.w = dbcentry->base_o; - - // Read target locations, only so that we can calculate orientation - for (uint32 k = 0; k < cam->target_positions.timestamps.number; ++k) - { - // Extract Target positions - if (cam->target_positions.timestamps.offset_elements + sizeof(M2Array) > buffSize) - return false; - M2Array const* targTsArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.timestamps.offset_elements); - if (targTsArray->offset_elements + sizeof(uint32) > buffSize || cam->target_positions.values.offset_elements + sizeof(M2Array) > buffSize) - return false; - uint32 const* targTimestamps = reinterpret_cast<uint32 const*>(buffer + targTsArray->offset_elements); - M2Array const* targArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.values.offset_elements); - - if (targArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) - return false; - M2SplineKey<G3D::Vector3> const* targPositions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + targArray->offset_elements); - - // Read the data for this set - uint32 currPos = targArray->offset_elements; - for (uint32 i = 0; i < targTsArray->number; ++i) - { - if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) - return false; - // Translate co-ordinates - G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->target_position_base, &targPositions->p0); - - // Add to vector - FlyByCamera thisCam; - thisCam.timeStamp = targTimestamps[i]; - thisCam.locations.x = newPos.x; - thisCam.locations.y = newPos.y; - thisCam.locations.z = newPos.z; - thisCam.locations.w = 0.0f; - targetcam.push_back(thisCam); - targPositions++; - currPos += sizeof(M2SplineKey<G3D::Vector3>); - } - } - - // Read camera positions and timestamps (translating first position of 3 only, we don't need to translate the whole spline) - for (uint32 k = 0; k < cam->positions.timestamps.number; ++k) - { - // Extract Camera positions for this set - if (cam->positions.timestamps.offset_elements + sizeof(M2Array) > buffSize) - return false; - M2Array const* posTsArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.timestamps.offset_elements); - if (posTsArray->offset_elements + sizeof(uint32) > buffSize || cam->positions.values.offset_elements + sizeof(M2Array) > buffSize) - return false; - uint32 const* posTimestamps = reinterpret_cast<uint32 const*>(buffer + posTsArray->offset_elements); - M2Array const* posArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.values.offset_elements); - if (posArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) - return false; - M2SplineKey<G3D::Vector3> const* positions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + posArray->offset_elements); - - // Read the data for this set - uint32 currPos = posArray->offset_elements; - for (uint32 i = 0; i < posTsArray->number; ++i) - { - if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) - return false; - // Translate co-ordinates - G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->position_base, &positions->p0); - - // Add to vector - FlyByCamera thisCam; - thisCam.timeStamp = posTimestamps[i]; - thisCam.locations.x = newPos.x; - thisCam.locations.y = newPos.y; - thisCam.locations.z = newPos.z; - - if (targetcam.size() > 0) - { - // Find the target camera before and after this camera - FlyByCamera lastTarget; - FlyByCamera nextTarget; - - // Pre-load first item - lastTarget = targetcam[0]; - nextTarget = targetcam[0]; - for (uint32 j = 0; j < targetcam.size(); ++j) - { - nextTarget = targetcam[j]; - if (targetcam[j].timeStamp > posTimestamps[i]) - break; - - lastTarget = targetcam[j]; - } - - float x = lastTarget.locations.x; - float y = lastTarget.locations.y; - float z = lastTarget.locations.z; - - // Now, the timestamps for target cam and position can be different. So, if they differ we interpolate - if (lastTarget.timeStamp != posTimestamps[i]) - { - uint32 timeDiffTarget = nextTarget.timeStamp - lastTarget.timeStamp; - uint32 timeDiffThis = posTimestamps[i] - lastTarget.timeStamp; - float xDiff = nextTarget.locations.x - lastTarget.locations.x; - float yDiff = nextTarget.locations.y - lastTarget.locations.y; - float zDiff = nextTarget.locations.z - lastTarget.locations.z; - x = lastTarget.locations.x + (xDiff * (float(timeDiffThis) / float(timeDiffTarget))); - y = lastTarget.locations.y + (yDiff * (float(timeDiffThis) / float(timeDiffTarget))); - z = lastTarget.locations.z + (zDiff * (float(timeDiffThis) / float(timeDiffTarget))); - } - float xDiff = x - thisCam.locations.x; - float yDiff = y - thisCam.locations.y; - thisCam.locations.w = std::atan2(yDiff, xDiff); - - if (thisCam.locations.w < 0) - thisCam.locations.w += 2 * float(M_PI); - } - - cameras.push_back(thisCam); - positions++; - currPos += sizeof(M2SplineKey<G3D::Vector3>); - } - } - - sFlyByCameraStore[dbcentry->id] = cameras; - return true; -} - -void LoadM2Cameras(const std::string& dataPath) -{ - sFlyByCameraStore.clear(); - TC_LOG_INFO("server.loading", ">> Loading Cinematic Camera files"); - - uint32 oldMSTime = getMSTime(); - for (uint32 i = 0; i < sCinematicCameraStore.GetNumRows(); ++i) - { - if (CinematicCameraEntry const* dbcentry = sCinematicCameraStore.LookupEntry(i)) - { - std::string filename = dataPath.c_str(); - filename.append(dbcentry->filename); - - // Replace slashes - size_t loc = filename.find("\\"); - while (loc != std::string::npos) - { - filename.replace(loc, 1, "/"); - loc = filename.find("\\"); - } - - // Replace mdx to .m2 - loc = filename.find(".mdx"); - if (loc != std::string::npos) - filename.replace(loc, 4, ".m2"); - - std::ifstream m2file(filename.c_str(), std::ios::in | std::ios::binary); - if (!m2file.is_open()) - continue; - - // Get file size - m2file.seekg(0, std::ios::end); - std::streamoff const fileSize = m2file.tellg(); - - // Reject if not at least the size of the header - if (static_cast<uint32 const>(fileSize) < sizeof(M2Header)) - { - TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File is smaller than header size", filename.c_str()); - m2file.close(); - continue; - } - - // Read 4 bytes (signature) - m2file.seekg(0, std::ios::beg); - char fileCheck[5]; - m2file.read(fileCheck, 4); - fileCheck[4] = 0; - - // Check file has correct magic (MD20) - if (strcmp(fileCheck, "MD20")) - { - TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File identifier not found", filename.c_str()); - m2file.close(); - continue; - } - - // Now we have a good file, read it all into a vector of char's, then close the file. - std::vector<char> buffer(fileSize); - m2file.seekg(0, std::ios::beg); - if (!m2file.read(buffer.data(), fileSize)) - { - m2file.close(); - continue; - } - m2file.close(); - - // Read header - M2Header const* header = reinterpret_cast<M2Header const*>(buffer.data()); - - if (header->ofsCameras + sizeof(M2Camera) > static_cast<uint32 const>(fileSize)) - { - TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.c_str()); - continue; - } - - // Get camera(s) - Main header, then dump them. - M2Camera const* cam = reinterpret_cast<M2Camera const*>(buffer.data() + header->ofsCameras); - if (!readCamera(cam, fileSize, header, dbcentry)) - TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.c_str()); - } - } - TC_LOG_INFO("server.loading", ">> Loaded %u cinematic waypoint sets in %u ms", (uint32)sFlyByCameraStore.size(), GetMSTimeDiffToNow(oldMSTime)); -} - SimpleFactionsList const* GetFactionTeamList(uint32 faction) { FactionTeamMap::const_iterator itr = sFactionTeamMap.find(faction); diff --git a/src/server/game/DataStores/DBCStores.h b/src/server/game/DataStores/DBCStores.h index 6cad13fdf9a..00b4141555f 100644 --- a/src/server/game/DataStores/DBCStores.h +++ b/src/server/game/DataStores/DBCStores.h @@ -27,8 +27,6 @@ #include <list> typedef std::list<uint32> SimpleFactionsList; -typedef std::vector<FlyByCamera> FlyByCameraCollection; - TC_GAME_API SimpleFactionsList const* GetFactionTeamList(uint32 faction); TC_GAME_API char* GetPetName(uint32 petfamily, uint32 dbclang); @@ -196,9 +194,7 @@ TC_GAME_API extern DBCStorage <WMOAreaTableEntry> sWMOAreaTableStore; //TC_GAME_API extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates TC_GAME_API extern DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore; TC_GAME_API extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore; -TC_GAME_API extern std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore; TC_GAME_API void LoadDBCStores(const std::string& dataPath); -TC_GAME_API void LoadM2Cameras(const std::string& dataPath); #endif diff --git a/src/server/game/DataStores/DBCStructure.h b/src/server/game/DataStores/DBCStructure.h index 51b3dcbd38b..13f0198a5e7 100644 --- a/src/server/game/DataStores/DBCStructure.h +++ b/src/server/game/DataStores/DBCStructure.h @@ -727,13 +727,11 @@ struct ChrRacesEntry struct CinematicCameraEntry { - uint32 id; // 0 index - char* filename; // 1 - uint32 soundid; // 2 in SoundEntries.dbc or 0 - float base_x; // 3 - float base_y; // 4 - float base_z; // 5 - float base_o; // 6 + uint32 ID; // 0 + char const* Model; // 1 Model filename (translate .mdx to .m2) + uint32 SoundID; // 2 Sound ID (voiceover for cinematic) + DBCPosition3D Origin; // 3-5 Position in map used for basis for M2 co-ordinates + float OriginFacing; // 6 Orientation in map used for basis for M2 co-ordinates }; struct CinematicSequencesEntry diff --git a/src/server/game/DataStores/M2Stores.cpp b/src/server/game/DataStores/M2Stores.cpp new file mode 100644 index 00000000000..69581f16549 --- /dev/null +++ b/src/server/game/DataStores/M2Stores.cpp @@ -0,0 +1,266 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "DBCStores.h" +#include "M2Structure.h" +#include "M2Stores.h" +#include "Common.h" +#include "Containers.h" +#include "Log.h" +#include "World.h" +#include <fstream> +#include <iostream> +#include <iomanip> +#include <boost/filesystem/path.hpp> + +std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore; + +// Convert the geomoetry from a spline value, to an actual WoW XYZ +G3D::Vector3 TranslateLocation(G3D::Vector4 const* DBCPosition, G3D::Vector3 const* basePosition, G3D::Vector3 const* splineVector) +{ + G3D::Vector3 work; + float x = basePosition->x + splineVector->x; + float y = basePosition->y + splineVector->y; + float z = basePosition->z + splineVector->z; + float const distance = sqrt((x * x) + (y * y)); + float angle = std::atan2(x, y) - DBCPosition->w; + + if (angle < 0) + angle += 2 * float(M_PI); + + work.x = DBCPosition->x + (distance * sin(angle)); + work.y = DBCPosition->y + (distance * cos(angle)); + work.z = DBCPosition->z + z; + return work; +} + +// Number of cameras not used. Multiple cameras never used in 3.3.5 +bool readCamera(M2Camera const* cam, uint32 buffSize, M2Header const* header, CinematicCameraEntry const* dbcentry) +{ + char const* buffer = reinterpret_cast<char const*>(header); + + FlyByCameraCollection cameras; + FlyByCameraCollection targetcam; + + G3D::Vector4 DBCData; + DBCData.x = dbcentry->Origin.X; + DBCData.y = dbcentry->Origin.Y; + DBCData.z = dbcentry->Origin.Z; + DBCData.w = dbcentry->OriginFacing; + + // Read target locations, only so that we can calculate orientation + for (uint32 k = 0; k < cam->target_positions.timestamps.number; ++k) + { + // Extract Target positions + if (cam->target_positions.timestamps.offset_elements + sizeof(M2Array) > buffSize) + return false; + M2Array const* targTsArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.timestamps.offset_elements); + if (targTsArray->offset_elements + sizeof(uint32) > buffSize || cam->target_positions.values.offset_elements + sizeof(M2Array) > buffSize) + return false; + uint32 const* targTimestamps = reinterpret_cast<uint32 const*>(buffer + targTsArray->offset_elements); + M2Array const* targArray = reinterpret_cast<M2Array const*>(buffer + cam->target_positions.values.offset_elements); + + if (targArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) + return false; + M2SplineKey<G3D::Vector3> const* targPositions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + targArray->offset_elements); + + // Read the data for this set + uint32 currPos = targArray->offset_elements; + for (uint32 i = 0; i < targTsArray->number; ++i) + { + if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) + return false; + // Translate co-ordinates + G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->target_position_base, &targPositions->p0); + + // Add to vector + FlyByCamera thisCam; + thisCam.timeStamp = targTimestamps[i]; + thisCam.locations.x = newPos.x; + thisCam.locations.y = newPos.y; + thisCam.locations.z = newPos.z; + thisCam.locations.w = 0.0f; + targetcam.push_back(thisCam); + targPositions++; + currPos += sizeof(M2SplineKey<G3D::Vector3>); + } + } + + // Read camera positions and timestamps (translating first position of 3 only, we don't need to translate the whole spline) + for (uint32 k = 0; k < cam->positions.timestamps.number; ++k) + { + // Extract Camera positions for this set + if (cam->positions.timestamps.offset_elements + sizeof(M2Array) > buffSize) + return false; + M2Array const* posTsArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.timestamps.offset_elements); + if (posTsArray->offset_elements + sizeof(uint32) > buffSize || cam->positions.values.offset_elements + sizeof(M2Array) > buffSize) + return false; + uint32 const* posTimestamps = reinterpret_cast<uint32 const*>(buffer + posTsArray->offset_elements); + M2Array const* posArray = reinterpret_cast<M2Array const*>(buffer + cam->positions.values.offset_elements); + if (posArray->offset_elements + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) + return false; + M2SplineKey<G3D::Vector3> const* positions = reinterpret_cast<M2SplineKey<G3D::Vector3> const*>(buffer + posArray->offset_elements); + + // Read the data for this set + uint32 currPos = posArray->offset_elements; + for (uint32 i = 0; i < posTsArray->number; ++i) + { + if (currPos + sizeof(M2SplineKey<G3D::Vector3>) > buffSize) + return false; + // Translate co-ordinates + G3D::Vector3 newPos = TranslateLocation(&DBCData, &cam->position_base, &positions->p0); + + // Add to vector + FlyByCamera thisCam; + thisCam.timeStamp = posTimestamps[i]; + thisCam.locations.x = newPos.x; + thisCam.locations.y = newPos.y; + thisCam.locations.z = newPos.z; + + if (targetcam.size() > 0) + { + // Find the target camera before and after this camera + FlyByCamera lastTarget; + FlyByCamera nextTarget; + + // Pre-load first item + lastTarget = targetcam[0]; + nextTarget = targetcam[0]; + for (uint32 j = 0; j < targetcam.size(); ++j) + { + nextTarget = targetcam[j]; + if (targetcam[j].timeStamp > posTimestamps[i]) + break; + + lastTarget = targetcam[j]; + } + + float x = lastTarget.locations.x; + float y = lastTarget.locations.y; + float z = lastTarget.locations.z; + + // Now, the timestamps for target cam and position can be different. So, if they differ we interpolate + if (lastTarget.timeStamp != posTimestamps[i]) + { + uint32 timeDiffTarget = nextTarget.timeStamp - lastTarget.timeStamp; + uint32 timeDiffThis = posTimestamps[i] - lastTarget.timeStamp; + float xDiff = nextTarget.locations.x - lastTarget.locations.x; + float yDiff = nextTarget.locations.y - lastTarget.locations.y; + float zDiff = nextTarget.locations.z - lastTarget.locations.z; + x = lastTarget.locations.x + (xDiff * (float(timeDiffThis) / float(timeDiffTarget))); + y = lastTarget.locations.y + (yDiff * (float(timeDiffThis) / float(timeDiffTarget))); + z = lastTarget.locations.z + (zDiff * (float(timeDiffThis) / float(timeDiffTarget))); + } + float xDiff = x - thisCam.locations.x; + float yDiff = y - thisCam.locations.y; + thisCam.locations.w = std::atan2(yDiff, xDiff); + + if (thisCam.locations.w < 0) + thisCam.locations.w += 2 * float(M_PI); + } + + cameras.push_back(thisCam); + positions++; + currPos += sizeof(M2SplineKey<G3D::Vector3>); + } + } + + sFlyByCameraStore[dbcentry->ID] = cameras; + return true; +} + +void LoadM2Cameras(std::string const& dataPath) +{ + sFlyByCameraStore.clear(); + TC_LOG_INFO("server.loading", ">> Loading Cinematic Camera files"); + + uint32 oldMSTime = getMSTime(); + for (uint32 i = 0; i < sCinematicCameraStore.GetNumRows(); ++i) + { + if (CinematicCameraEntry const* dbcentry = sCinematicCameraStore.LookupEntry(i)) + { + std::string filenameWork = dataPath; + filenameWork.append(dbcentry->Model); + + // Replace slashes (always to forward slash, because boost!) + std::replace(filenameWork.begin(), filenameWork.end(), '\\', '/'); + + boost::filesystem::path filename = filenameWork; + + // Convert to native format + filename.make_preferred(); + + // Replace mdx to .m2 + filename.replace_extension("m2"); + + std::ifstream m2file(filename.string().c_str(), std::ios::in | std::ios::binary); + if (!m2file.is_open()) + continue; + + // Get file size + m2file.seekg(0, std::ios::end); + std::streamoff const fileSize = m2file.tellg(); + + // Reject if not at least the size of the header + if (static_cast<uint32 const>(fileSize) < sizeof(M2Header)) + { + TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File is smaller than header size", filename.string().c_str()); + m2file.close(); + continue; + } + + // Read 4 bytes (signature) + m2file.seekg(0, std::ios::beg); + char fileCheck[5]; + m2file.read(fileCheck, 4); + fileCheck[4] = 0; + + // Check file has correct magic (MD20) + if (strcmp(fileCheck, "MD20")) + { + TC_LOG_ERROR("server.loading", "Camera file %s is damaged. File identifier not found", filename.string().c_str()); + m2file.close(); + continue; + } + + // Now we have a good file, read it all into a vector of char's, then close the file. + std::vector<char> buffer(fileSize); + m2file.seekg(0, std::ios::beg); + if (!m2file.read(buffer.data(), fileSize)) + { + m2file.close(); + continue; + } + m2file.close(); + + // Read header + M2Header const* header = reinterpret_cast<M2Header const*>(buffer.data()); + + if (header->ofsCameras + sizeof(M2Camera) > static_cast<uint32 const>(fileSize)) + { + TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.string().c_str()); + continue; + } + + // Get camera(s) - Main header, then dump them. + M2Camera const* cam = reinterpret_cast<M2Camera const*>(buffer.data() + header->ofsCameras); + if (!readCamera(cam, fileSize, header, dbcentry)) + TC_LOG_ERROR("server.loading", "Camera file %s is damaged. Camera references position beyond file end", filename.string().c_str()); + } + } + TC_LOG_INFO("server.loading", ">> Loaded %u cinematic waypoint sets in %u ms", (uint32)sFlyByCameraStore.size(), GetMSTimeDiffToNow(oldMSTime)); +} diff --git a/src/server/game/DataStores/M2Stores.h b/src/server/game/DataStores/M2Stores.h new file mode 100644 index 00000000000..97224475e5d --- /dev/null +++ b/src/server/game/DataStores/M2Stores.h @@ -0,0 +1,36 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef TRINITY_M2STORES_H +#define TRINITY_M2STORES_H + +#include "SharedDefines.h" +#include "Common.h" + +struct FlyByCamera +{ + uint32 timeStamp; + G3D::Vector4 locations; +}; + +typedef std::vector<FlyByCamera> FlyByCameraCollection; + +TC_GAME_API extern std::unordered_map<uint32, FlyByCameraCollection> sFlyByCameraStore; + +TC_GAME_API void LoadM2Cameras(std::string const& dataPath); + +#endif
\ No newline at end of file diff --git a/src/server/game/DataStores/M2Structure.h b/src/server/game/DataStores/M2Structure.h new file mode 100644 index 00000000000..43e8d008b9f --- /dev/null +++ b/src/server/game/DataStores/M2Structure.h @@ -0,0 +1,133 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef TRINITY_M2STRUCTURE_H +#define TRINITY_M2STRUCTURE_H + +#include <G3D/Vector3.h> +#include <G3D/AABox.h> + +// Structures for M2 file. Source: https://wowdev.wiki +#pragma pack(push, 1) +template<typename T> +struct M2SplineKey +{ + T p0; + T p1; + T p2; +}; + +struct M2Header +{ + char Magic[4]; // "MD20" + uint32 Version; // The version of the format. + uint32 lName; // Length of the model's name including the trailing \0 + uint32 ofsName; // Offset to the name, it seems like models can get reloaded by this name.should be unique, i guess. + uint32 GlobalModelFlags; // 0x0001: tilt x, 0x0002: tilt y, 0x0008: add 2 fields in header, 0x0020: load .phys data (MoP+), 0x0080: has _lod .skin files (MoP?+), 0x0100: is camera related. + uint32 nGlobalSequences; + uint32 ofsGlobalSequences; // A list of timestamps. + uint32 nAnimations; + uint32 ofsAnimations; // Information about the animations in the model. + uint32 nAnimationLookup; + uint32 ofsAnimationLookup; // Mapping of global IDs to the entries in the Animation sequences block. + uint32 nBones; // MAX_BONES = 0x100 + uint32 ofsBones; // Information about the bones in this model. + uint32 nKeyBoneLookup; + uint32 ofsKeyBoneLookup; // Lookup table for key skeletal bones. + uint32 nVertices; + uint32 ofsVertices; // Vertices of the model. + uint32 nViews; // Views (LOD) are now in .skins. + uint32 nSubmeshAnimations; + uint32 ofsSubmeshAnimations; // Submesh color and alpha animations definitions. + uint32 nTextures; + uint32 ofsTextures; // Textures of this model. + uint32 nTransparency; + uint32 ofsTransparency; // Transparency of textures. + uint32 nUVAnimation; + uint32 ofsUVAnimation; + uint32 nTexReplace; + uint32 ofsTexReplace; // Replaceable Textures. + uint32 nRenderFlags; + uint32 ofsRenderFlags; // Blending modes / render flags. + uint32 nBoneLookupTable; + uint32 ofsBoneLookupTable; // A bone lookup table. + uint32 nTexLookup; + uint32 ofsTexLookup; // The same for textures. + uint32 nTexUnits; // possibly removed with cata?! + uint32 ofsTexUnits; // And texture units. Somewhere they have to be too. + uint32 nTransLookup; + uint32 ofsTransLookup; // Everything needs its lookup. Here are the transparencies. + uint32 nUVAnimLookup; + uint32 ofsUVAnimLookup; + G3D::AABox BoundingBox; // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height + float BoundingSphereRadius; + G3D::AABox CollisionBox; + float CollisionSphereRadius; + uint32 nBoundingTriangles; + uint32 ofsBoundingTriangles; // Our bounding volumes. Similar structure like in the old ofsViews. + uint32 nBoundingVertices; + uint32 ofsBoundingVertices; + uint32 nBoundingNormals; + uint32 ofsBoundingNormals; + uint32 nAttachments; + uint32 ofsAttachments; // Attachments are for weapons etc. + uint32 nAttachLookup; + uint32 ofsAttachLookup; // Of course with a lookup. + uint32 nEvents; + uint32 ofsEvents; // Used for playing sounds when dying and a lot else. + uint32 nLights; + uint32 ofsLights; // Lights are mainly used in loginscreens but in wands and some doodads too. + uint32 nCameras; // Format of Cameras changed with version 271! + uint32 ofsCameras; // The cameras are present in most models for having a model in the Character-Tab. + uint32 nCameraLookup; + uint32 ofsCameraLookup; // And lookup-time again. + uint32 nRibbonEmitters; + uint32 ofsRibbonEmitters; // Things swirling around. See the CoT-entrance for light-trails. + uint32 nParticleEmitters; + uint32 ofsParticleEmitters; // Spells and weapons, doodads and loginscreens use them. Blood dripping of a blade? Particles. + uint32 nBlendMaps; // This has to deal with blending. Exists IFF (flags & 0x8) != 0. When set, textures blending is overriden by the associated array. See M2/WotLK#Blend_mode_overrides + uint32 ofsBlendMaps; // Same as above. Points to an array of uint16 of nBlendMaps entries -- From WoD information.}; +}; + +struct M2Array +{ + uint32_t number; + uint32 offset_elements; +}; +struct M2Track +{ + uint16_t interpolation_type; + uint16_t global_sequence; + M2Array timestamps; + M2Array values; +}; + +struct M2Camera +{ + uint32_t type; // 0: portrait, 1: characterinfo; -1: else (flyby etc.); referenced backwards in the lookup table. + float fov; // No radians, no degrees. Multiply by 35 to get degrees. + float far_clip; + float near_clip; + M2Track positions; // How the camera's position moves. Should be 3*3 floats. + G3D::Vector3 position_base; + M2Track target_positions; // How the target moves. Should be 3*3 floats. + G3D::Vector3 target_position_base; + M2Track rolldata; // The camera can have some roll-effect. Its 0 to 2*Pi. +}; +#pragma pack(pop) + +#endif
\ No newline at end of file diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index d3843c86aa4..a11f4131dcf 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -182,13 +182,13 @@ m_groupLootTimer(0), lootingGroupLowGUID(0), m_PlayerDamageReq(0), m_lootRecipient(), m_lootRecipientGroup(0), _skinner(), _pickpocketLootRestore(0), m_corpseRemoveTime(0), m_respawnTime(0), m_respawnDelay(300), m_corpseDelay(60), m_respawnradius(0.0f), m_boundaryCheckTime(2500), m_combatPulseTime(0), m_combatPulseDelay(0), m_reactState(REACT_AGGRESSIVE), m_defaultMovementType(IDLE_MOTION_TYPE), m_spawnId(0), m_equipmentId(0), m_originalEquipmentId(0), m_AlreadyCallAssistance(false), -m_AlreadySearchedAssistance(false), m_regenHealth(true), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), +m_AlreadySearchedAssistance(false), m_regenHealth(true), m_cannotReachTarget(false), m_cannotReachTimer(0), m_AI_locked(false), m_meleeDamageSchoolMask(SPELL_SCHOOL_MASK_NORMAL), m_originalEntry(0), m_homePosition(), m_transportHomePosition(), m_creatureInfo(nullptr), m_creatureData(nullptr), m_waypointID(0), m_path_id(0), m_formation(nullptr), m_focusSpell(nullptr), m_focusDelay(0) { m_regenTimer = CREATURE_REGEN_INTERVAL; m_valuesCount = UNIT_END; - for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) m_spells[i] = 0; DisableReputationGain = false; @@ -367,13 +367,13 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender); // Load creature equipment - if (data && data->equipmentId != 0) + if (!data || data->equipmentId == 0) + LoadEquipment(); // use default equipment (if available) + else if (data && data->equipmentId != 0) // override, 0 means no equipment { m_originalEquipmentId = data->equipmentId; LoadEquipment(data->equipmentId); } - else - LoadEquipment(0, true); SetName(normalInfo->Name); // at normal entry always @@ -394,7 +394,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/) if (!m_respawnradius && m_defaultMovementType == RANDOM_MOTION_TYPE) m_defaultMovementType = IDLE_MOTION_TYPE; - for (uint8 i=0; i < CREATURE_MAX_SPELLS; ++i) + for (uint8 i=0; i < MAX_CREATURE_SPELLS; ++i) m_spells[i] = GetCreatureTemplate()->spells[i]; return true; @@ -648,33 +648,32 @@ void Creature::Update(uint32 diff) m_regenTimer -= diff; } - if (m_regenTimer != 0) - break; - - bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim - !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player - !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster + if (m_regenTimer == 0) + { + bool bInCombat = IsInCombat() && (!GetVictim() || // if IsInCombat() is true and this has no victim + !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player + !EnsureVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->IsGameMaster()); // or the victim/owner/charmer is not a GameMaster - /*if (m_regenTimer <= diff) - {*/ - if (!IsInEvadeMode() && (!bInCombat || IsPolymorphed())) // regenerate health if not in combat or if polymorphed - RegenerateHealth(); + if (!IsInEvadeMode() && (!bInCombat || IsPolymorphed() || CanNotReachTarget())) // regenerate health if not in combat or if polymorphed + RegenerateHealth(); - if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER)) - { - if (getPowerType() == POWER_ENERGY) - Regenerate(POWER_ENERGY); - else - RegenerateMana(); + if (HasFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER)) + { + if (getPowerType() == POWER_ENERGY) + Regenerate(POWER_ENERGY); + else + RegenerateMana(); + } + m_regenTimer = CREATURE_REGEN_INTERVAL; } - /*if (!bIsPolymorphed) // only increase the timer if not polymorphed - m_regenTimer += CREATURE_REGEN_INTERVAL - diff; + if (CanNotReachTarget() && !IsInEvadeMode() && !GetMap()->IsRaid()) + { + m_cannotReachTimer += diff; + if (m_cannotReachTimer >= CREATURE_NOPATH_EVADE_TIME) + if (IsAIEnabled) + AI()->EnterEvadeMode(CreatureAI::EVADE_REASON_NO_PATH); } - else - if (!bIsPolymorphed) // if polymorphed, skip the timer - m_regenTimer -= diff;*/ - m_regenTimer = CREATURE_REGEN_INTERVAL; break; } default: @@ -1824,7 +1823,7 @@ bool Creature::isWorldBoss() const if (IsPet()) return false; - return (GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_BOSS) != 0; + return (GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_BOSS_MOB) != 0; } SpellInfo const* Creature::reachWithSpellAttack(Unit* victim) @@ -1832,7 +1831,7 @@ SpellInfo const* Creature::reachWithSpellAttack(Unit* victim) if (!victim) return nullptr; - for (uint32 i=0; i < CREATURE_MAX_SPELLS; ++i) + for (uint32 i=0; i < MAX_CREATURE_SPELLS; ++i) { if (!m_spells[i]) continue; @@ -1880,7 +1879,7 @@ SpellInfo const* Creature::reachWithSpellCure(Unit* victim) if (!victim) return nullptr; - for (uint32 i=0; i < CREATURE_MAX_SPELLS; ++i) + for (uint32 i=0; i < MAX_CREATURE_SPELLS; ++i) { if (!m_spells[i]) continue; @@ -2325,10 +2324,10 @@ uint32 Creature::GetShieldBlockValue() const //dunno mob block bool Creature::HasSpell(uint32 spellID) const { uint8 i; - for (i = 0; i < CREATURE_MAX_SPELLS; ++i) + for (i = 0; i < MAX_CREATURE_SPELLS; ++i) if (spellID == m_spells[i]) break; - return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells + return i < MAX_CREATURE_SPELLS; //broke before end of iteration of known spells } time_t Creature::GetRespawnTimeEx() const diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h index 8466dad9e95..cc0e0559d28 100644 --- a/src/server/game/Entities/Creature/Creature.h +++ b/src/server/game/Entities/Creature/Creature.h @@ -67,12 +67,13 @@ enum CreatureFlagsExtra 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_NO_PLAYER_DAMAGE_REQ | CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK) -#define CREATURE_REGEN_INTERVAL 2 * IN_MILLISECONDS +static const uint32 CREATURE_REGEN_INTERVAL = 2 * IN_MILLISECONDS; +static const uint32 CREATURE_NOPATH_EVADE_TIME = 5 * IN_MILLISECONDS; -#define MAX_KILL_CREDIT 2 -#define MAX_CREATURE_MODELS 4 -#define MAX_CREATURE_QUEST_ITEMS 6 -#define CREATURE_MAX_SPELLS 8 +static const uint8 MAX_KILL_CREDIT = 2; +static const uint32 MAX_CREATURE_MODELS = 4; +static const uint32 MAX_CREATURE_QUEST_ITEMS = 6; +static const uint32 MAX_CREATURE_SPELLS = 8; // from `creature_template` table struct TC_GAME_API CreatureTemplate @@ -106,7 +107,7 @@ struct TC_GAME_API CreatureTemplate uint32 unit_flags; // enum UnitFlags mask values uint32 unit_flags2; // enum UnitFlags2 mask values uint32 dynamicflags; - uint32 family; // enum CreatureFamily values (optional) + CreatureFamily family; // enum CreatureFamily values (optional) uint32 trainer_type; uint32 trainer_spell; uint32 trainer_class; @@ -117,7 +118,7 @@ struct TC_GAME_API CreatureTemplate uint32 pickpocketLootId; uint32 SkinLootId; int32 resistance[MAX_SPELL_SCHOOL]; - uint32 spells[CREATURE_MAX_SPELLS]; + uint32 spells[MAX_CREATURE_SPELLS]; uint32 PetSpellDataId; uint32 VehicleId; uint32 mingold; @@ -145,11 +146,11 @@ struct TC_GAME_API CreatureTemplate // helpers SkillType GetRequiredLootSkill() const { - if (type_flags & CREATURE_TYPEFLAGS_HERBLOOT) + if (type_flags & CREATURE_TYPE_FLAG_HERB_SKINNING_SKILL) return SKILL_HERBALISM; - else if (type_flags & CREATURE_TYPEFLAGS_MININGLOOT) + else if (type_flags & CREATURE_TYPE_FLAG_MINING_SKINNING_SKILL) return SKILL_MINING; - else if (type_flags & CREATURE_TYPEFLAGS_ENGINEERLOOT) + else if (type_flags & CREATURE_TYPE_FLAG_ENGINEERING_SKINNING_SKILL) return SKILL_ENGINEERING; else return SKILL_SKINNING; // normal case @@ -157,12 +158,12 @@ struct TC_GAME_API CreatureTemplate bool IsExotic() const { - return (type_flags & CREATURE_TYPEFLAGS_EXOTIC) != 0; + return (type_flags & CREATURE_TYPE_FLAG_EXOTIC_PET) != 0; } bool IsTameable(bool canTameExotic) const { - if (type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPEFLAGS_TAMEABLE) == 0) + if (type != CREATURE_TYPE_BEAST || family == CREATURE_FAMILY_NONE || (type_flags & CREATURE_TYPE_FLAG_TAMEABLE_PET) == 0) return false; // if can tame exotic then can tame any tameable @@ -475,6 +476,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma uint8 getLevelForTarget(WorldObject const* target) const override; // overwrite Unit::getLevelForTarget for boss level support bool IsInEvadeMode() const { return HasUnitState(UNIT_STATE_EVADE); } + bool IsEvadingAttacks() const { return IsInEvadeMode() || CanNotReachTarget(); } bool AIM_Destroy(); bool AIM_Initialize(CreatureAI* ai = NULL); @@ -567,7 +569,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma SpellInfo const* reachWithSpellAttack(Unit* victim); SpellInfo const* reachWithSpellCure(Unit* victim); - uint32 m_spells[CREATURE_MAX_SPELLS]; + uint32 m_spells[MAX_CREATURE_SPELLS]; bool CanStartAttack(Unit const* u, bool force) const; float GetAttackDistance(Unit const* player) const; @@ -632,6 +634,15 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma virtual uint8 GetPetAutoSpellSize() const { return MAX_SPELL_CHARM; } virtual uint32 GetPetAutoSpellOnPos(uint8 pos) const; + void SetCannotReachTarget(bool cannotReach) + { + if (cannotReach == m_cannotReachTarget) + return; + m_cannotReachTarget = cannotReach; + m_cannotReachTimer = 0; + } + bool CanNotReachTarget() const { return m_cannotReachTarget; } + void SetPosition(float x, float y, float z, float o); void SetPosition(const Position &pos) { SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()); } @@ -719,6 +730,8 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma bool m_AlreadyCallAssistance; bool m_AlreadySearchedAssistance; bool m_regenHealth; + bool m_cannotReachTarget; + uint32 m_cannotReachTimer; bool m_AI_locked; SpellSchoolMask m_meleeDamageSchoolMask; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 7f922f89572..d7e478bb4e5 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -34,7 +34,7 @@ #include "Transport.h" GameObject::GameObject() : WorldObject(false), MapObject(), - m_model(NULL), m_goValue(), m_AI(NULL) + m_model(nullptr), m_goValue(), m_AI(nullptr) { m_objectType |= TYPEMASK_GAMEOBJECT; m_objectTypeId = TYPEID_GAMEOBJECT; @@ -49,8 +49,8 @@ GameObject::GameObject() : WorldObject(false), MapObject(), m_usetimes = 0; m_spellId = 0; m_cooldownTime = 0; - m_goInfo = NULL; - m_goData = NULL; + m_goInfo = nullptr; + m_goData = nullptr; m_spawnId = 0; m_rotation = 0; @@ -496,7 +496,7 @@ void GameObject::Update(uint32 diff) radius = goInfo->trap.diameter / 2.f; // Pointer to appropriate target if found any - Unit* target = NULL; + Unit* target = nullptr; /// @todo this hack with search required until GO casting not implemented if (Unit* owner = GetOwner()) @@ -511,7 +511,7 @@ void GameObject::Update(uint32 diff) else { // Environmental trap: Any player - Player* player = NULL; + Player* player = nullptr; Trinity::AnyPlayerInObjectRangeCheck checker(this, radius); Trinity::PlayerSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(this, player, checker); VisitNearbyWorldObject(radius, searcher); @@ -571,8 +571,8 @@ void GameObject::Update(uint32 diff) GameObjectTemplate const* goInfo = GetGOInfo(); if (goInfo->trap.type == 2 && goInfo->trap.spellId) { - /// @todo NULL target won't work for target type 1 - CastSpell(NULL, goInfo->trap.spellId); + /// @todo nullptr target won't work for target type 1 + CastSpell(nullptr, goInfo->trap.spellId); SetLootState(GO_JUST_DEACTIVATED); } else if (Unit* target = ObjectAccessor::GetUnit(*this, m_lootStateUnitGUID)) @@ -1093,7 +1093,7 @@ void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target) float range = float(target->GetSpellMaxRangeForTarget(GetOwner(), trapSpell)); // search nearest linked GO - GameObject* trapGO = NULL; + GameObject* trapGO = nullptr; { // using original GO distance CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); @@ -1113,7 +1113,7 @@ void GameObject::TriggeringLinkedGameObject(uint32 trapEntry, Unit* target) GameObject* GameObject::LookupFishingHoleAround(float range) { - GameObject* ok = NULL; + GameObject* ok = nullptr; CellCoord p(Trinity::ComputeCellCoord(GetPositionX(), GetPositionY())); Cell cell(p); @@ -1136,7 +1136,7 @@ void GameObject::ResetDoorOrButton() m_cooldownTime = 0; } -void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=NULL*/) +void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */, Unit* user /*=nullptr*/) { if (m_lootState != GO_READY) return; @@ -1160,7 +1160,7 @@ void GameObject::SetGoArtKit(uint8 kit) void GameObject::SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType lowguid) { - const GameObjectData* data = NULL; + const GameObjectData* data = nullptr; if (go) { go->SetGoArtKit(artkit); @@ -1386,7 +1386,7 @@ void GameObject::Use(Unit* user) // cast this spell later if provided spellId = info->goober.spellId; - spellCaster = NULL; + spellCaster = nullptr; break; } @@ -1505,7 +1505,7 @@ void GameObject::Use(Unit* user) GameObjectTemplate const* info = GetGOInfo(); - Player* m_ritualOwner = NULL; + Player* m_ritualOwner = nullptr; if (m_ritualOwnerGUID) m_ritualOwner = ObjectAccessor::FindPlayer(m_ritualOwnerGUID); @@ -1869,7 +1869,7 @@ bool GameObject::IsInRange(float x, float y, float z, float radius) const && dz < info->maxZ + radius && dz > info->minZ - radius; } -void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= NULL*/) +void GameObject::EventInform(uint32 eventId, WorldObject* invoker /*= nullptr*/) { if (!eventId) return; @@ -1929,7 +1929,7 @@ void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3); } -void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, uint32 spellId /*= 0*/) +void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= nullptr*/, uint32 spellId /*= 0*/) { if (!m_goValue.Building.MaxHealth || !change) return; @@ -1948,7 +1948,7 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, u // Set the health bar, value = 255 * healthPct; SetGoAnimProgress(m_goValue.Building.Health * 255 / m_goValue.Building.MaxHealth); - Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : NULL; + Player* player = attackerOrHealer ? attackerOrHealer->GetCharmerOrOwnerPlayerOrPlayerItself() : nullptr; // dealing damage, send packet if (player) @@ -1979,7 +1979,7 @@ void GameObject::ModifyHealth(int32 change, Unit* attackerOrHealer /*= NULL*/, u SetDestructibleState(newState, player, false); } -void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= NULL*/, bool setHealth /*= false*/) +void GameObject::SetDestructibleState(GameObjectDestructibleState state, Player* eventInvoker /*= nullptr*/, bool setHealth /*= false*/) { // the user calling this must know he is already operating on destructible gameobject ASSERT(GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING); @@ -2161,14 +2161,14 @@ void GameObject::UpdateModel() Player* GameObject::GetLootRecipient() const { if (!m_lootRecipient) - return NULL; + return nullptr; return ObjectAccessor::FindConnectedPlayer(m_lootRecipient); } Group* GameObject::GetLootRecipientGroup() const { if (!m_lootRecipientGroup) - return NULL; + return nullptr; return sGroupMgr->GetGroupByGUID(m_lootRecipientGroup); } @@ -2176,7 +2176,7 @@ void GameObject::SetLootRecipient(Unit* unit) { // set the player whose group should receive the right // to loot the creature after it dies - // should be set to NULL after the loot disappears + // should be set to nullptr after the loot disappears if (!unit) { @@ -2295,7 +2295,7 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t data->append(fieldBuffer); } -void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = NULL*/) const +void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const { if (m_spawnId) { diff --git a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp index b44a3e7ad7b..a7b410bc04b 100644 --- a/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp +++ b/src/server/game/Entities/Item/ItemEnchantmentMgr.cpp @@ -44,7 +44,7 @@ typedef std::unordered_map<uint32, EnchStoreList> EnchantmentStore; static EnchantmentStore RandomItemEnch; -TC_GAME_API void LoadRandomEnchantmentsTable() +void LoadRandomEnchantmentsTable() { uint32 oldMSTime = getMSTime(); @@ -77,7 +77,7 @@ TC_GAME_API void LoadRandomEnchantmentsTable() TC_LOG_ERROR("server.loading", ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty."); } -TC_GAME_API uint32 GetItemEnchantMod(int32 entry) +uint32 GetItemEnchantMod(int32 entry) { if (!entry) return 0; @@ -118,7 +118,7 @@ TC_GAME_API uint32 GetItemEnchantMod(int32 entry) return 0; } -TC_GAME_API uint32 GenerateEnchSuffixFactor(uint32 item_id) +uint32 GenerateEnchSuffixFactor(uint32 item_id) { ItemTemplate const* itemProto = sObjectMgr->GetItemTemplate(item_id); diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 45952ba51ac..daad0c9d958 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -1154,9 +1154,13 @@ bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const if (!IsInMap(obj)) return false; - float ox, oy, oz; - obj->GetPosition(ox, oy, oz); - return IsWithinLOS(ox, oy, oz); + float x, y, z; + if (obj->GetTypeId() == TYPEID_PLAYER) + obj->GetPosition(x, y, z); + else + obj->GetHitSpherePointFor(GetPosition(), x, y, z); + + return IsWithinLOS(x, y, z); } float WorldObject::GetDistance(const WorldObject* obj) const @@ -1240,11 +1244,36 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const VMAP::IVMapManager* vMapManager = VMAP::VMapFactory::createOrGetVMapManager(); return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);*/ if (IsInWorld()) - return GetMap()->isInLineOfSight(GetPositionX(), GetPositionY(), GetPositionZ()+2.f, ox, oy, oz+2.f, GetPhaseMask()); + { + float x, y, z; + if (GetTypeId() == TYPEID_PLAYER) + GetPosition(x, y, z); + else + GetHitSpherePointFor({ ox, oy, oz }, x, y, z); + + return GetMap()->isInLineOfSight(x, y, z + 2.0f, ox, oy, oz + 2.0f, GetPhaseMask()); + } return true; } +Position WorldObject::GetHitSpherePointFor(Position const& dest) const +{ + G3D::Vector3 vThis(GetPositionX(), GetPositionY(), GetPositionZ()); + G3D::Vector3 vObj(dest.GetPositionX(), dest.GetPositionY(), dest.GetPositionZ()); + G3D::Vector3 contactPoint = vThis + (vObj - vThis).directionOrZero() * GetObjectSize(); + + return Position(contactPoint.x, contactPoint.y, contactPoint.z, GetAngle(contactPoint.x, contactPoint.y)); +} + +void WorldObject::GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const +{ + Position pos = GetHitSpherePointFor(dest); + x = pos.GetPositionX(); + y = pos.GetPositionY(); + z = pos.GetPositionZ(); +} + bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D /* = true */) const { float dx1 = GetPositionX() - obj1->GetPositionX(); @@ -1471,7 +1500,7 @@ float WorldObject::GetGridActivationRange() const { if (ToPlayer()) { - if (ToPlayer()->IsOnCinematic()) + if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) return DEFAULT_VISIBILITY_INSTANCE; return GetMap()->GetVisibilityRange(); } @@ -1504,7 +1533,7 @@ float WorldObject::GetSightRange(const WorldObject* target) const { if (target && target->isActiveObject() && !target->ToPlayer()) return MAX_VISIBILITY_DISTANCE; - else if (ToPlayer()->IsOnCinematic()) + else if (ToPlayer()->GetCinematicMgr()->IsOnCinematic()) return DEFAULT_VISIBILITY_INSTANCE; else return GetMap()->GetVisibilityRange(); @@ -2038,7 +2067,10 @@ void WorldObject::SummonCreatureGroup(uint8 group, std::list<TempSummon*>* list std::vector<TempSummonData> const* data = sObjectMgr->GetSummonGroup(GetEntry(), GetTypeId() == TYPEID_GAMEOBJECT ? SUMMONER_TYPE_GAMEOBJECT : SUMMONER_TYPE_CREATURE, group); if (!data) + { + TC_LOG_WARN("scripts", "%s (%s) tried to summon non-existing summon group %u.", GetName().c_str(), GetGUID().ToString().c_str(), group); return; + } for (std::vector<TempSummonData>::const_iterator itr = data->begin(); itr != data->end(); ++itr) if (TempSummon* summon = SummonCreature(itr->entry, itr->pos, itr->type, itr->time)) diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index d4c8fc35451..47b28d8ea3a 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -488,6 +488,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation bool IsWithinDistInMap(WorldObject const* obj, float dist2compare, bool is3D = true) const; bool IsWithinLOS(float x, float y, float z) const; bool IsWithinLOSInMap(WorldObject const* obj) const; + Position GetHitSpherePointFor(Position const& dest) const; + void GetHitSpherePointFor(Position const& dest, float& x, float& y, float& z) const; bool GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D = true) const; bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const; bool IsInRange2d(float x, float y, float minRange, float maxRange) const; @@ -550,8 +552,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation GameObject* FindNearestGameObject(uint32 entry, float range) const; GameObject* FindNearestGameObjectOfType(GameobjectTypes type, float range) const; - void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange) const; - void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange) const; + void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const; + void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry = 0, float fMaxSearchRange = 250.0f) const; void GetPlayerListInGrid(std::list<Player*>& lList, float fMaxSearchRange) const; void DestroyForNearbyPlayers(); @@ -584,14 +586,6 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation template<class NOTIFIER> void VisitNearbyGridObject(float const& radius, NOTIFIER& notifier) const { if (IsInWorld()) GetMap()->VisitGrid(GetPositionX(), GetPositionY(), radius, notifier); } template<class NOTIFIER> void VisitNearbyWorldObject(float const& radius, NOTIFIER& notifier) const { if (IsInWorld()) GetMap()->VisitWorld(GetPositionX(), GetPositionY(), radius, notifier); } -#ifdef MAP_BASED_RAND_GEN - int32 irand(int32 min, int32 max) const { return int32 (GetMap()->mtRand.randInt(max - min)) + min; } - uint32 urand(uint32 min, uint32 max) const { return GetMap()->mtRand.randInt(max - min) + min;} - int32 rand32() const { return GetMap()->mtRand.randInt();} - double rand_norm() const { return GetMap()->mtRand.randExc();} - double rand_chance() const { return GetMap()->mtRand.randExc(100.0);} -#endif - uint32 LastUsedScriptID; // Transports diff --git a/src/server/game/Entities/Player/CinematicMgr.cpp b/src/server/game/Entities/Player/CinematicMgr.cpp new file mode 100644 index 00000000000..cc5a62300ad --- /dev/null +++ b/src/server/game/Entities/Player/CinematicMgr.cpp @@ -0,0 +1,174 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "CinematicMgr.h" +#include "Creature.h" +#include "Player.h" +#include "TemporarySummon.h" + +CinematicMgr::CinematicMgr(Player* playerref) +{ + player = playerref; + m_cinematicDiff = 0; + m_lastCinematicCheck = 0; + m_activeCinematicCameraId = 0; + m_cinematicLength = 0; + m_cinematicCamera = nullptr; + m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f); + m_CinematicObject = nullptr; +} + +CinematicMgr::~CinematicMgr() +{ + if (m_cinematicCamera && m_activeCinematicCameraId) + EndCinematic(); +} + +void CinematicMgr::BeginCinematic() +{ + // Sanity check for active camera set + if (m_activeCinematicCameraId == 0) + return; + + auto itr = sFlyByCameraStore.find(m_activeCinematicCameraId); + if (itr != sFlyByCameraStore.end()) + { + // Initialize diff, and set camera + m_cinematicDiff = 0; + m_cinematicCamera = &itr->second; + + auto camitr = m_cinematicCamera->begin(); + if (camitr != m_cinematicCamera->end()) + { + Position pos(camitr->locations.x, camitr->locations.y, camitr->locations.z, camitr->locations.w); + if (!pos.IsPositionValid()) + return; + + player->GetMap()->LoadGrid(camitr->locations.x, camitr->locations.y); + m_CinematicObject = player->SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 5 * MINUTE * IN_MILLISECONDS); + if (m_CinematicObject) + { + m_CinematicObject->setActive(true); + player->SetViewpoint(m_CinematicObject, true); + } + + // Get cinematic length + FlyByCameraCollection::const_reverse_iterator camrevitr = m_cinematicCamera->rbegin(); + if (camrevitr != m_cinematicCamera->rend()) + m_cinematicLength = camrevitr->timeStamp; + else + m_cinematicLength = 0; + } + } +} + +void CinematicMgr::EndCinematic() +{ + if (m_activeCinematicCameraId == 0) + return; + + m_cinematicDiff = 0; + m_cinematicCamera = nullptr; + m_activeCinematicCameraId = 0; + if (m_CinematicObject) + { + if (WorldObject* vpObject = player->GetViewpoint()) + if (vpObject == m_CinematicObject) + player->SetViewpoint(m_CinematicObject, false); + + m_CinematicObject->AddObjectToRemoveList(); + } +} + +void CinematicMgr::UpdateCinematicLocation(uint32 /*diff*/) +{ + if (m_activeCinematicCameraId == 0 || !m_cinematicCamera || m_cinematicCamera->size() == 0) + return; + + Position lastPosition; + uint32 lastTimestamp = 0; + Position nextPosition; + uint32 nextTimestamp = 0; + + // Obtain direction of travel + for (FlyByCamera cam : *m_cinematicCamera) + { + if (cam.timeStamp > m_cinematicDiff) + { + nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); + nextTimestamp = cam.timeStamp; + break; + } + lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); + lastTimestamp = cam.timeStamp; + } + float angle = lastPosition.GetAngle(&nextPosition); + angle -= lastPosition.GetOrientation(); + if (angle < 0) + angle += 2 * float(M_PI); + + // Look for position around 2 second ahead of us. + int32 workDiff = m_cinematicDiff; + + // Modify result based on camera direction (Humans for example, have the camera point behind) + workDiff += static_cast<int32>(float(CINEMATIC_LOOKAHEAD) * cos(angle)); + + // Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end + FlyByCameraCollection::const_reverse_iterator endItr = m_cinematicCamera->rbegin(); + if (endItr != m_cinematicCamera->rend() && workDiff > static_cast<int32>(endItr->timeStamp)) + workDiff = endItr->timeStamp; + + // Never try to go back in time before the start of cinematic! + if (workDiff < 0) + workDiff = m_cinematicDiff; + + // Obtain the previous and next waypoint based on timestamp + for (FlyByCamera cam : *m_cinematicCamera) + { + if (static_cast<int32>(cam.timeStamp) >= workDiff) + { + nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); + nextTimestamp = cam.timeStamp; + break; + } + lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); + lastTimestamp = cam.timeStamp; + } + + // Never try to go beyond the end of the cinematic + if (workDiff > static_cast<int32>(nextTimestamp)) + workDiff = static_cast<int32>(nextTimestamp); + + // Interpolate the position for this moment in time (or the adjusted moment in time) + uint32 timeDiff = nextTimestamp - lastTimestamp; + uint32 interDiff = workDiff - lastTimestamp; + float xDiff = nextPosition.m_positionX - lastPosition.m_positionX; + float yDiff = nextPosition.m_positionY - lastPosition.m_positionY; + float zDiff = nextPosition.m_positionZ - lastPosition.m_positionZ; + Position interPosition(lastPosition.m_positionX + (xDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionY + + (yDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionZ + (zDiff * (float(interDiff) / float(timeDiff)))); + + // Advance (at speed) to this position. The remote sight object is used + // to send update information to player in cinematic + if (m_CinematicObject && interPosition.IsPositionValid()) + m_CinematicObject->MonsterMoveWithSpeed(interPosition.m_positionX, interPosition.m_positionY, interPosition.m_positionZ, 500.0f, false, true); + + // If we never received an end packet 10 seconds after the final timestamp then force an end + if (m_cinematicDiff > m_cinematicLength + 10 * IN_MILLISECONDS) + EndCinematic(); +} diff --git a/src/server/game/Entities/Player/CinematicMgr.h b/src/server/game/Entities/Player/CinematicMgr.h new file mode 100644 index 00000000000..ab067afa042 --- /dev/null +++ b/src/server/game/Entities/Player/CinematicMgr.h @@ -0,0 +1,60 @@ +/* +* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> +* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/> +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU General Public License as published by the +* Free Software Foundation; either version 2 of the License, or (at your +* option) any later version. +* +* This program is distributed in the hope that it will be useful, but WITHOUT +* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +* more details. +* +* You should have received a copy of the GNU General Public License along +* with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#ifndef CinematicMgr_h__ +#define CinematicMgr_h__ + +#include "Define.h" +#include "Object.h" +#include "M2Stores.h" + +#define CINEMATIC_LOOKAHEAD (2 * IN_MILLISECONDS) +#define CINEMATIC_UPDATEDIFF 500 + +class Player; + +class TC_GAME_API CinematicMgr +{ + friend class Player; +public: + explicit CinematicMgr(Player* playerref); + ~CinematicMgr(); + + // Cinematic camera data and remote sight functions + uint32 GetActiveCinematicCamera() const { return m_activeCinematicCameraId; } + void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; } + bool IsOnCinematic() const { return (m_cinematicCamera != nullptr); } + void BeginCinematic(); + void EndCinematic(); + void UpdateCinematicLocation(uint32 diff); + +private: + // Remote location information + Player* player; + +protected: + uint32 m_cinematicDiff; + uint32 m_lastCinematicCheck; + uint32 m_activeCinematicCameraId; + uint32 m_cinematicLength; + FlyByCameraCollection* m_cinematicCamera; + Position m_remoteSightPosition; + TempSummon* m_CinematicObject; +}; + +#endif
\ No newline at end of file diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index c58f2c940fc..ec86e8fe4c3 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -96,9 +96,6 @@ #define SKILL_PERM_BONUS(x) int16(PAIR32_HIPART(x)) #define MAKE_SKILL_BONUS(t, p) MAKE_PAIR32(t, p) -#define CINEMATIC_LOOKAHEAD (2 * IN_MILLISECONDS) -#define CINEMATIC_UPDATEDIFF 500 - enum CharacterFlags { CHARACTER_FLAG_NONE = 0x00000000, @@ -477,21 +474,11 @@ Player::Player(WorldSession* session): Unit(true) // Player summoning m_summon_expire = 0; - m_summon_mapid = 0; - m_summon_x = 0.0f; - m_summon_y = 0.0f; - m_summon_z = 0.0f; m_mover = this; m_movedPlayer = this; m_seer = this; - m_recallMap = 0; - m_recallX = 0; - m_recallY = 0; - m_recallZ = 0; - m_recallO = 0; - m_homebindMapId = 0; m_homebindAreaId = 0; m_homebindX = 0; @@ -539,12 +526,7 @@ Player::Player(WorldSession* session): Unit(true) healthBeforeDuel = 0; manaBeforeDuel = 0; - m_cinematicDiff = 0; - m_lastCinematicCheck = 0; - m_activeCinematicCameraId = 0; - m_cinematicCamera = nullptr; - m_remoteSightPosition = Position(0.0f, 0.0f, 0.0f); - m_CinematicObject = nullptr; + _cinematicMgr = new CinematicMgr(this); m_achievementMgr = new AchievementMgr(this); m_reputationMgr = new ReputationMgr(this); @@ -585,6 +567,7 @@ Player::~Player() delete m_runes; delete m_achievementMgr; delete m_reputationMgr; + delete _cinematicMgr; sWorld->DecreasePlayerCount(); } @@ -1236,11 +1219,11 @@ void Player::Update(uint32 p_time) } // Update cinematic location, if 500ms have passed and we're doing a cinematic now. - m_cinematicDiff += p_time; - if (m_cinematicCamera && m_activeCinematicCameraId && GetMSTimeDiffToNow(m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF) + _cinematicMgr->m_cinematicDiff += p_time; + if (_cinematicMgr->m_cinematicCamera && _cinematicMgr->m_activeCinematicCameraId && GetMSTimeDiffToNow(_cinematicMgr->m_lastCinematicCheck) > CINEMATIC_UPDATEDIFF) { - m_lastCinematicCheck = getMSTime(); - UpdateCinematicLocation(p_time); + _cinematicMgr->m_lastCinematicCheck = getMSTime(); + _cinematicMgr->UpdateCinematicLocation(p_time); } //used to implement delayed far teleports @@ -1728,7 +1711,7 @@ bool Player::BuildEnumData(PreparedQueryResult result, WorldPacket* data) // Pets info uint32 petDisplayId = 0; uint32 petLevel = 0; - uint32 petFamily = 0; + CreatureFamily petFamily = CREATURE_FAMILY_NONE; // show pet at selection character in character list only for non-ghost character if (result && !(playerFlags & PLAYER_FLAGS_GHOST) && (plrClass == CLASS_WARLOCK || plrClass == CLASS_HUNTER || plrClass == CLASS_DEATH_KNIGHT)) @@ -2436,11 +2419,11 @@ Creature* Player::GetNPCIfCanInteractWith(ObjectGuid const& guid, uint32 npcflag return nullptr; // Deathstate checks - if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_GHOST)) + if (!IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_GHOST_VISIBLE)) return nullptr; // alive or spirit healer - if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_DEAD_INTERACT)) + if (!creature->IsAlive() && !(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD)) return nullptr; // appropriate npc type @@ -2811,6 +2794,8 @@ void Player::GiveLevel(uint8 level) if (sWorld->getBoolConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up UpdateSkillsToMaxSkillsForLevel(); + _ApplyAllLevelScaleItemMods(true); + // set current level health and mana/energy to maximum after applying all mods. SetFullHealth(); SetPower(POWER_MANA, GetMaxPower(POWER_MANA)); @@ -2820,8 +2805,6 @@ void Player::GiveLevel(uint8 level) SetPower(POWER_FOCUS, 0); SetPower(POWER_HAPPINESS, 0); - _ApplyAllLevelScaleItemMods(true); - // update level to hunter/summon pet if (Pet* pet = GetPet()) pet->SynchronizeLevelWithOwner(); @@ -3014,7 +2997,7 @@ void Player::InitStatsForLevel(bool reapplyMods) // cleanup unit flags (will be re-applied if need at aura load). RemoveFlag(UNIT_FIELD_FLAGS, - UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_ATTACKABLE_1 | + UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_ATTACKABLE_1 | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_LOOTING | UNIT_FLAG_PET_IN_COMBAT | UNIT_FLAG_SILENCED | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_IN_COMBAT | UNIT_FLAG_DISARMED | @@ -3219,10 +3202,9 @@ bool Player::AddTalent(uint32 spellId, uint8 spec, bool learning) } } - PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; PlayerTalent* newtalent = new PlayerTalent(); - newtalent->state = state; + newtalent->state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED; newtalent->spec = spec; (*m_talents[spec])[spellId] = newtalent; @@ -4958,6 +4940,9 @@ void Player::DurabilityPointsLossAll(int32 points, bool inventory) void Player::DurabilityPointsLoss(Item* item, int32 points) { + if (HasAuraType(SPELL_AURA_PREVENT_DURABILITY_LOSS)) + return; + int32 pMaxDurability = item->GetUInt32Value(ITEM_FIELD_MAXDURABILITY); int32 pOldDurability = item->GetUInt32Value(ITEM_FIELD_DURABILITY); int32 pNewDurability = pOldDurability - points; @@ -6374,15 +6359,6 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, bool t return true; } -void Player::SaveRecallPosition() -{ - m_recallMap = GetMapId(); - m_recallX = GetPositionX(); - m_recallY = GetPositionY(); - m_recallZ = GetPositionZ(); - m_recallO = GetOrientation(); -} - void Player::SendMessageToSetInRange(WorldPacket* data, float dist, bool self) { if (self) @@ -6417,13 +6393,13 @@ void Player::SendDirectMessage(WorldPacket const* data) const m_session->SendPacket(data); } -void Player::SendCinematicStart(uint32 CinematicSequenceId) +void Player::SendCinematicStart(uint32 CinematicSequenceId) const { WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4); data << uint32(CinematicSequenceId); SendDirectMessage(&data); - if (const CinematicSequencesEntry* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId)) - SetActiveCinematicCamera(sequence->cinematicCamera); + if (CinematicSequencesEntry const* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId)) + _cinematicMgr->SetActiveCinematicCamera(sequence->cinematicCamera); } void Player::SendMovieStart(uint32 MovieId) const @@ -14862,12 +14838,8 @@ void Player::CompleteQuest(uint32 quest_id) SetQuestSlotState(log_slot, QUEST_STATE_COMPLETE); if (Quest const* qInfo = sObjectMgr->GetQuestTemplate(quest_id)) - { if (qInfo->HasFlag(QUEST_FLAGS_TRACKING)) RewardQuest(qInfo, 0, this, false); - else - SendQuestComplete(quest_id); - } } if (sWorld->getBoolConfig(CONFIG_QUEST_ENABLE_QUEST_TRACKER)) // check if Quest Tracker is enabled @@ -20328,6 +20300,11 @@ void Player::Say(std::string const& text, Language language, WorldObject const* SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), true); } +void Player::Say(uint32 textId, WorldObject const* target /*= nullptr*/) +{ + Talk(textId, CHAT_MSG_SAY, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_SAY), target); +} + void Player::Yell(std::string const& text, Language language, WorldObject const* /*= nullptr*/) { std::string _text(text); @@ -20338,6 +20315,11 @@ void Player::Yell(std::string const& text, Language language, WorldObject const* SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), true); } +void Player::Yell(uint32 textId, WorldObject const* target /*= nullptr*/) +{ + Talk(textId, CHAT_MSG_YELL, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_YELL), target); +} + void Player::TextEmote(std::string const& text, WorldObject const* /*= nullptr*/, bool /*= false*/) { std::string _text(text); @@ -20348,6 +20330,11 @@ void Player::TextEmote(std::string const& text, WorldObject const* /*= nullptr*/ SendMessageToSetInRange(&data, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), true, !GetSession()->HasPermission(rbac::RBAC_PERM_TWO_SIDE_INTERACTION_CHAT)); } +void Player::TextEmote(uint32 textId, WorldObject const* target /*= nullptr*/, bool /*isBossEmote = false*/) +{ + Talk(textId, CHAT_MSG_EMOTE, sWorld->getFloatConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE), target); +} + void Player::Whisper(std::string const& text, Language language, Player* target, bool /*= false*/) { ASSERT(target); @@ -20384,6 +20371,24 @@ void Player::Whisper(std::string const& text, Language language, Player* target, ChatHandler(GetSession()).PSendSysMessage(LANG_PLAYER_DND, target->GetName().c_str(), target->autoReplyMsg.c_str()); } +void Player::Whisper(uint32 textId, Player* target, bool /*isBossWhisper = false*/) +{ + if (!target) + return; + + BroadcastText const* bct = sObjectMgr->GetBroadcastText(textId); + if (!bct) + { + TC_LOG_ERROR("entities.unit", "WorldObject::MonsterWhisper: `broadcast_text` was not %u found", textId); + return; + } + + LocaleConstant locale = target->GetSession()->GetSessionDbLocaleIndex(); + WorldPacket data; + ChatHandler::BuildChatPacket(data, CHAT_MSG_WHISPER, LANG_UNIVERSAL, this, target, bct->GetText(locale, getGender()), 0, "", locale); + target->SendDirectMessage(&data); +} + Item* Player::GetMItem(uint32 id) { ItemMap::const_iterator itr = mMitems.find(id); @@ -20502,7 +20507,7 @@ void Player::VehicleSpellInitialize() data << uint8(0); // Command State data << uint16(0x800); // DisableActions (set for all vehicles) - for (uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for (uint32 i = 0; i < MAX_CREATURE_SPELLS; ++i) { uint32 spellId = vehicle->m_spells[i]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); @@ -20526,7 +20531,7 @@ void Player::VehicleSpellInitialize() data << uint32(MAKE_UNIT_ACTION_BUTTON(spellId, i+8)); } - for (uint32 i = CREATURE_MAX_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i) + for (uint32 i = MAX_CREATURE_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i) data << uint32(0); data << uint8(0); // Auras? @@ -20934,7 +20939,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc return false; } - if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) + if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL)) return false; // taximaster case @@ -21133,7 +21138,7 @@ void Player::CleanupAfterTaxiFlight() { m_taxi.ClearTaxiDestinations(); // not destinations, clear source node Dismount(); - RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT); getHostileRefManager().setOnlineOfflineState(true); } @@ -23176,13 +23181,32 @@ void Player::UpdateForQuestWorldObjects() GetSession()->SendPacket(&packet); } -void Player::SetSummonPoint(uint32 mapid, float x, float y, float z) +bool Player::HasSummonPending() const +{ + return m_summon_expire >= time(nullptr); +} + +void Player::SendSummonRequestFrom(Unit* summoner) { + if (!summoner) + return; + + // Player already has active summon request + if (HasSummonPending()) + return; + + // Evil Twin (ignore player summon, but hide this for summoner) + if (HasAura(23445)) + return; + m_summon_expire = time(nullptr) + MAX_PLAYER_SUMMON_DELAY; - m_summon_mapid = mapid; - m_summon_x = x; - m_summon_y = y; - m_summon_z = z; + m_summon_location.WorldRelocate(*summoner); + + WorldPacket data(SMSG_SUMMON_REQUEST, 8 + 4 + 4); + data << uint64(summoner->GetGUID()); // summoner guid + data << uint32(summoner->GetZoneId()); // summoner zone + data << uint32(MAX_PLAYER_SUMMON_DELAY*IN_MILLISECONDS); // auto decline after msecs + GetSession()->SendPacket(&data); } void Player::SummonIfPossible(bool agree) @@ -23213,7 +23237,7 @@ void Player::SummonIfPossible(bool agree) UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1); - TeleportTo(m_summon_mapid, m_summon_x, m_summon_y, m_summon_z, GetOrientation()); + TeleportTo(m_summon_location); } void Player::RemoveItemDurations(Item* item) @@ -26210,125 +26234,6 @@ float Player::GetCollisionHeight(bool mounted) const } } -void Player::BeginCinematic() -{ - // Sanity check for active camera set - if (m_activeCinematicCameraId == 0) - return; - - auto itr = sFlyByCameraStore.find(m_activeCinematicCameraId); - if (itr != sFlyByCameraStore.end()) - { - // Initialize diff, and set camera - m_cinematicDiff = 0; - m_cinematicCamera = &itr->second; - - auto camitr = m_cinematicCamera->begin(); - if (camitr != m_cinematicCamera->end()) - { - Position pos(camitr->locations.x, camitr->locations.y, camitr->locations.z, camitr->locations.w); - if (!pos.IsPositionValid()) - return; - - m_mapRef->LoadGrid(camitr->locations.x, camitr->locations.y); - m_CinematicObject = SummonCreature(VISUAL_WAYPOINT, pos.m_positionX, pos.m_positionY, pos.m_positionZ, 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 120000); - if (m_CinematicObject) - { - m_CinematicObject->setActive(true); - SetViewpoint(m_CinematicObject, true); - } - } - } -} - -void Player::EndCinematic() -{ - m_cinematicDiff = 0; - m_cinematicCamera = nullptr; - m_activeCinematicCameraId = 0; - if (m_CinematicObject) - { - if (m_seer && m_seer == m_CinematicObject) - SetViewpoint(m_CinematicObject, false); - m_CinematicObject->AddObjectToRemoveList(); - } -} - -void Player::UpdateCinematicLocation(uint32 /*diff*/) -{ - Position lastPosition; - uint32 lastTimestamp = 0; - Position nextPosition; - uint32 nextTimestamp = 0; - - if (m_cinematicCamera->size() == 0) - return; - - // Obtain direction of travel - for (FlyByCamera cam : *m_cinematicCamera) - { - if (cam.timeStamp > m_cinematicDiff) - { - nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); - nextTimestamp = cam.timeStamp; - break; - } - lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); - lastTimestamp = cam.timeStamp; - } - float angle = lastPosition.GetAngle(&nextPosition); - angle -= lastPosition.GetOrientation(); - if (angle < 0) - angle += 2 * float(M_PI); - - // Look for position around 2 second ahead of us. - int32 workDiff = m_cinematicDiff; - - // Modify result based on camera direction (Humans for example, have the camera point behind) - workDiff += static_cast<int32>(float(CINEMATIC_LOOKAHEAD) * cos(angle)); - - // Get an iterator to the last entry in the cameras, to make sure we don't go beyond the end - FlyByCameraCollection::const_reverse_iterator endItr = m_cinematicCamera->rbegin(); - if (endItr != m_cinematicCamera->rend() && workDiff > static_cast<int32>(endItr->timeStamp)) - workDiff = endItr->timeStamp; - - // Never try to go back in time before the start of cinematic! - if (workDiff < 0) - workDiff = m_cinematicDiff; - - // Obtain the previous and next waypoint based on timestamp - for (FlyByCamera cam : *m_cinematicCamera) - { - if (static_cast<int32>(cam.timeStamp) >= workDiff) - { - nextPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); - nextTimestamp = cam.timeStamp; - break; - } - lastPosition = Position(cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w); - lastTimestamp = cam.timeStamp; - } - - // Never try to go beyond the end of the cinematic - if (workDiff > static_cast<int32>(nextTimestamp)) - workDiff = static_cast<int32>(nextTimestamp); - - // Interpolate the position for this moment in time (or the adjusted moment in time) - uint32 timeDiff = nextTimestamp - lastTimestamp; - uint32 interDiff = workDiff - lastTimestamp; - float xDiff = nextPosition.m_positionX - lastPosition.m_positionX; - float yDiff = nextPosition.m_positionY - lastPosition.m_positionY; - float zDiff = nextPosition.m_positionZ - lastPosition.m_positionZ; - Position interPosition(lastPosition.m_positionX + (xDiff * (float(interDiff)/float(timeDiff))), lastPosition.m_positionY + - (yDiff * (float(interDiff) / float(timeDiff))), lastPosition.m_positionZ + (zDiff * (float(interDiff) / float(timeDiff)))); - - // Advance (at speed) to this position. The remote sight object is used - // to send update information to player in cinematic - if (m_CinematicObject && interPosition.IsPositionValid()) - m_CinematicObject->MonsterMoveWithSpeed(interPosition.m_positionX, interPosition.m_positionY, interPosition.m_positionZ, 200.0f, false, true); -} - - std::string Player::GetMapAreaAndZoneString() const { uint32 areaId = GetAreaId(); @@ -26561,3 +26466,22 @@ void Player::RemoveRestFlag(RestFlag restFlag) RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING); } } + +uint32 Player::DoRandomRoll(uint32 minimum, uint32 maximum) +{ + ASSERT(maximum <= 10000); + + uint32 roll = urand(minimum, maximum); + + WorldPacket data(MSG_RANDOM_ROLL, 4 + 4 + 4 + 8); + data << uint32(minimum); + data << uint32(maximum); + data << uint32(roll); + data << GetGUID(); + if (Group* group = GetGroup()) + group->BroadcastPacket(&data, false); + else + SendDirectMessage(&data); + + return roll; +} diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index fd0fac69674..6a38cd3b9fe 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -30,6 +30,7 @@ #include "SpellHistory.h" #include "Unit.h" #include "TradeData.h" +#include "CinematicMgr.h" #include <limits> #include <string> @@ -1026,6 +1027,7 @@ struct ResurrectionData class TC_GAME_API Player : public Unit, public GridObject<Player> { friend class WorldSession; + friend class CinematicMgr; friend void Item::AddToUpdateQueueOf(Player* player); friend void Item::RemoveFromUpdateQueueOf(Player* player); public: @@ -1050,7 +1052,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> bool TeleportTo(WorldLocation const &loc, uint32 options = 0); bool TeleportToBGEntryPoint(); - void SetSummonPoint(uint32 mapid, float x, float y, float z); + bool HasSummonPending() const; + void SendSummonRequestFrom(Unit* summoner); void SummonIfPossible(bool agree); bool Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo); @@ -1148,12 +1151,16 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> /// Handles said message in regular chat based on declared language and in config pre-defined Range. void Say(std::string const& text, Language language, WorldObject const* = nullptr) override; + void Say(uint32 textId, WorldObject const* target = nullptr) override; /// Handles yelled message in regular chat based on declared language and in config pre-defined Range. void Yell(std::string const& text, Language language, WorldObject const* = nullptr) override; + void Yell(uint32 textId, WorldObject const* target = nullptr) override; /// Outputs an universal text which is supposed to be an action. void TextEmote(std::string const& text, WorldObject const* = nullptr, bool = false) override; + void TextEmote(uint32 textId, WorldObject const* target = nullptr, bool isBossEmote = false) override; /// Handles whispers from Addons and players based on sender, receiver's guid and language. void Whisper(std::string const& text, Language language, Player* receiver, bool = false) override; + void Whisper(uint32 textId, Player* target, bool isBossWhisper = false) override; /*********************************************************/ /*** STORAGE SYSTEM ***/ @@ -1269,6 +1276,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> TradeData* GetTradeData() const { return m_trade; } void TradeCancel(bool sendback); + CinematicMgr* GetCinematicMgr() const { return _cinematicMgr; } + void UpdateEnchantTime(uint32 time); void UpdateSoulboundTradeItems(); void AddTradeableItem(Item* item); @@ -1587,7 +1596,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void AddSpellMod(SpellModifier* mod, bool apply); bool IsAffectedBySpellmod(SpellInfo const* spellInfo, SpellModifier* mod, Spell* spell = nullptr) const; - template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell = nullptr); + template <class T> + void ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell = nullptr); void RemoveSpellMods(Spell* spell); void RestoreSpellMods(Spell* spell, uint32 ownerAuraId = 0, Aura* aura = nullptr); void RestoreAllSpellMods(uint32 ownerAuraId = 0, Aura* aura = nullptr); @@ -2073,13 +2083,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint32 GetSaveTimer() const { return m_nextSave; } void SetSaveTimer(uint32 timer) { m_nextSave = timer; } - // Recall position - uint32 m_recallMap; - float m_recallX; - float m_recallY; - float m_recallZ; - float m_recallO; - void SaveRecallPosition(); + void SaveRecallPosition() { m_recall_location.WorldRelocate(*this); } + void Recall() { TeleportTo(m_recall_location); } void SetHomebind(WorldLocation const& loc, uint32 areaId); @@ -2129,9 +2134,11 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> void ResummonPetTemporaryUnSummonedIfAny(); bool IsPetNeedBeTemporaryUnsummoned() const; - void SendCinematicStart(uint32 CinematicSequenceId); + void SendCinematicStart(uint32 CinematicSequenceId) const; void SendMovieStart(uint32 MovieId) const; + uint32 DoRandomRoll(uint32 minimum, uint32 maximum); + /*********************************************************/ /*** INSTANCE SYSTEM ***/ /*********************************************************/ @@ -2265,17 +2272,6 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> std::string GetMapAreaAndZoneString() const; std::string GetCoordsMapAreaAndZoneString() const; - // Cinematic camera data and remote sight functions - uint32 GetActiveCinematicCamera() const { return m_activeCinematicCameraId; } - void SetActiveCinematicCamera(uint32 cinematicCameraId = 0) { m_activeCinematicCameraId = cinematicCameraId; } - bool IsOnCinematic() const { return (m_cinematicCamera != nullptr); } - void BeginCinematic(); - void EndCinematic(); - void UpdateCinematicLocation(uint32 diff); - - std::string GetMapAreaAndZoneString(); - std::string GetCoordsMapAreaAndZoneString(); - protected: // Gamemaster whisper whitelist GuidList WhisperList; @@ -2510,10 +2506,10 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> // Player summoning time_t m_summon_expire; - uint32 m_summon_mapid; - float m_summon_x; - float m_summon_y; - float m_summon_z; + WorldLocation m_summon_location; + + // Recall position + WorldLocation m_recall_location; DeclinedName *m_declinedname; Runes *m_runes; @@ -2535,6 +2531,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> Item* _StoreItem(uint16 pos, Item* pItem, uint32 count, bool clone, bool update); Item* _LoadItem(SQLTransaction& trans, uint32 zoneId, uint32 timeDiff, Field* fields); + CinematicMgr* _cinematicMgr; + GuidSet m_refundableItems; void SendRefundInfo(Item* item); void RefundItem(Item* item); @@ -2601,25 +2599,19 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> uint32 manaBeforeDuel; WorldLocation _corpseLocation; - - // Remote location information - uint32 m_cinematicDiff; - uint32 m_lastCinematicCheck; - uint32 m_activeCinematicCameraId; - FlyByCameraCollection* m_cinematicCamera; - Position m_remoteSightPosition; - Creature* m_CinematicObject; }; TC_GAME_API void AddItemsSetItem(Player* player, Item* item); TC_GAME_API void RemoveItemsSetItem(Player* player, ItemTemplate const* proto); // "the bodies of template functions must be made available in a header file" -template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell* spell) +template <class T> +void Player::ApplySpellMod(uint32 spellId, SpellModOp op, T& basevalue, Spell* spell /*= nullptr*/) { SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); if (!spellInfo) - return 0; + return; + float totalmul = 1.0f; int32 totalflat = 0; @@ -2655,9 +2647,8 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &bas DropModCharge(mod, spell); } - float diff = (float)basevalue * (totalmul - 1.0f) + (float)totalflat; - basevalue = T((float)basevalue + diff); - return T(diff); + + basevalue = T(float(basevalue + totalflat) * totalmul); } #endif diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index ee8a0b093e7..2aa32374087 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -578,7 +578,7 @@ bool Unit::HasBreakableByDamageCrowdControlAura(Unit* excludeCasterChannel) cons void Unit::DealDamageMods(Unit* victim, uint32 &damage, uint32* absorb) { - if (!victim || !victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())) + if (!victim || !victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())) { if (absorb) *absorb += damage; @@ -599,11 +599,14 @@ uint32 Unit::DealDamage(Unit* victim, uint32 damage, CleanDamage const* cleanDam if (victim->GetTypeId() == TYPEID_PLAYER && this != victim) { - // Signal to pets that their owner was attacked - Pet* pet = victim->ToPlayer()->GetPet(); + // Signal to pets that their owner was attacked - except when DOT. + if (damagetype != DOT) + { + Pet* pet = victim->ToPlayer()->GetPet(); - if (pet && pet->IsAlive()) - pet->AI()->OwnerAttackedBy(this); + if (pet && pet->IsAlive()) + pet->AI()->OwnerAttackedBy(this); + } if (victim->ToPlayer()->GetCommandStatus(CHEAT_GOD)) return 0; @@ -1120,7 +1123,7 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage* damageInfo, bool durabilityLoss) if (!victim) return; - if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())) + if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())) return; SpellInfo const* spellProto = sSpellMgr->GetSpellInfo(damageInfo->SpellID); @@ -1346,7 +1349,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo* damageInfo, bool durabilityLoss) { Unit* victim = damageInfo->target; - if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode())) + if (!victim->IsAlive() || victim->HasUnitState(UNIT_STATE_IN_FLIGHT) || (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks())) return; // Hmmmm dont like this emotes client must by self do all animations @@ -2044,7 +2047,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit* victim, WeaponAttackTy MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit* victim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) const { - if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode()) + if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()) return MELEE_HIT_EVADE; int32 attackerMaxSkillValueForLevel = GetMaxSkillValueForLevel(victim); @@ -2656,7 +2659,7 @@ SpellMissInfo Unit::SpellHitResult(Unit* victim, SpellInfo const* spellInfo, boo return SPELL_MISS_NONE; // Return evade for units in evade mode - if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsInEvadeMode()) + if (victim->GetTypeId() == TYPEID_UNIT && victim->ToCreature()->IsEvadingAttacks()) return SPELL_MISS_EVADE; // Try victim reflect spell @@ -3189,6 +3192,15 @@ int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const return 0; } +bool Unit::CanMoveDuringChannel() const +{ + if (Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL]) + if (spell->getState() != SPELL_STATE_FINISHED) + return spell->GetSpellInfo()->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING) && spell->IsChannelActive(); + + return false; +} + bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const { return IsWithinDistInMap(target, distance) && HasInArc(arc, target); @@ -4185,6 +4197,16 @@ void Unit::RemoveArenaAuras() } } +void Unit::RemoveAurasOnEvade() +{ + if (IsCharmedOwnedByPlayerOrPlayer()) // if it is a player owned creature it should not remove the aura + return; + + // don't remove vehicle auras, passengers aren't supposed to drop off the vehicle + // don't remove clone caster on evade (to be verified) + RemoveAllAurasExceptType(SPELL_AURA_CONTROL_VEHICLE, SPELL_AURA_CLONE_CASTER); +} + void Unit::RemoveAllAurasOnDeath() { // used just after dieing to remove all visible auras @@ -5277,7 +5299,7 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit* target, uint8 /*SwingType } //victim may be NULL -bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown) +bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown) { SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); uint32 effIndex = triggeredByAura->GetEffIndex(); @@ -5287,8 +5309,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere ? ToPlayer()->GetItemByGuid(triggeredByAura->GetBase()->GetCastItemGUID()) : NULL; uint32 triggered_spell_id = 0; - uint32 cooldown_spell_id = 0; // for random trigger, will be one of the triggered spell to avoid repeatable triggers - // otherwise, it's the triggered_spell_id by default Unit* target = victim; int32 basepoints0 = 0; ObjectGuid originalCaster; @@ -5364,24 +5384,20 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere case CLASS_PALADIN: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 case CLASS_DRUID: // 39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409 triggered_spell_id = RAND(39511, 40997, 40998, 40999, 41002, 41005, 41009, 41011, 41409); - cooldown_spell_id = 39511; break; case CLASS_ROGUE: // 39511, 40997, 40998, 41002, 41005, 41011 case CLASS_WARRIOR: // 39511, 40997, 40998, 41002, 41005, 41011 case CLASS_DEATH_KNIGHT: triggered_spell_id = RAND(39511, 40997, 40998, 41002, 41005, 41011); - cooldown_spell_id = 39511; break; case CLASS_PRIEST: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 case CLASS_SHAMAN: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 case CLASS_MAGE: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 case CLASS_WARLOCK: // 40999, 41002, 41005, 41009, 41011, 41406, 41409 triggered_spell_id = RAND(40999, 41002, 41005, 41009, 41011, 41406, 41409); - cooldown_spell_id = 40999; break; case CLASS_HUNTER: // 40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409 triggered_spell_id = RAND(40997, 40999, 41002, 41005, 41009, 41011, 41406, 41409); - cooldown_spell_id = 40997; break; default: return false; @@ -5611,11 +5627,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) - { - if (!GetSpellHistory()->HasCooldown(*itr)) - GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); - } break; } case 71562: // Deathbringer's Will Heroic @@ -5657,11 +5668,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere uint8 rand_spell = urand(0, (RandomSpells.size() - 1)); CastSpell(target, RandomSpells[rand_spell], true, castItem, triggeredByAura, originalCaster); - for (std::vector<uint32>::iterator itr = RandomSpells.begin(); itr != RandomSpells.end(); ++itr) - { - if (!GetSpellHistory()->HasCooldown(*itr)) - GetSpellHistory()->AddCooldown(*itr, 0, std::chrono::seconds(cooldown)); - } break; } case 65032: // Boom aura (321 Boombot) @@ -6299,24 +6305,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere return true; } } - // Eclipse - if (dummySpell->SpellIconID == 2856 && GetTypeId() == TYPEID_PLAYER) - { - if (!procSpell || effIndex != 0) - return false; - - bool isWrathSpell = (procSpell->SpellFamilyFlags[0] & 1); - - if (!roll_chance_f(dummySpell->ProcChance * (isWrathSpell ? 0.6f : 1.0f))) - return false; - - target = this; - if (target->HasAura(isWrathSpell ? 48517 : 48518)) - return false; - - triggered_spell_id = isWrathSpell ? 48518 : 48517; - break; - } break; } case SPELLFAMILY_ROGUE: @@ -6497,7 +6485,7 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere // Item - Paladin T8 Holy 4P Bonus if (Unit* caster = triggeredByAura->GetCaster()) if (AuraEffect const* aurEff = caster->GetAuraEffect(64895, 0)) - cooldown = aurEff->GetAmount(); + cooldown = Milliseconds(aurEff->GetAmount()); target = this; break; @@ -6812,10 +6800,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere if (!player || !castItem || !castItem->IsEquipped() || !victim || !victim->IsAlive()) return false; - // custom cooldown processing case - if (cooldown && GetSpellHistory()->HasCooldown(dummySpell->Id)) - return false; - if (triggeredByAura->GetBase() && castItem->GetGUID() != triggeredByAura->GetBase()->GetCastItemGUID()) return false; @@ -6878,8 +6862,8 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere triggered_spell_id = 33750; // apply cooldown before cast to prevent processing itself - if (cooldown) - player->GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown)); + triggeredByAura->GetBase()->AddProcCooldown(std::chrono::steady_clock::now() + cooldown); + cooldown = Milliseconds::zero(); // Attack Twice for (uint32 i = 0; i < 2; ++i) @@ -7122,10 +7106,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere if (!procSpell || GetTypeId() != TYPEID_PLAYER || !victim) return false; - // custom cooldown processing case - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(dummySpell->Id)) - return false; - uint32 spellId = 0; // Every Lightning Bolt and Chain Lightning spell have duplicate vs half damage and zero cost switch (procSpell->Id) @@ -7171,10 +7151,6 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere } CastSpell(victim, spellId, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(dummySpell->Id, 0, std::chrono::seconds(cooldown)); - return true; } // Static Shock @@ -7476,26 +7452,17 @@ bool Unit::HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggere return false; } - if (cooldown_spell_id == 0) - cooldown_spell_id = triggered_spell_id; - - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(cooldown_spell_id)) - return false; - if (basepoints0) CastCustomSpell(target, triggered_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura, originalCaster); else CastSpell(target, triggered_spell_id, true, castItem, triggeredByAura, originalCaster); - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(cooldown_spell_id, 0, std::chrono::seconds(cooldown)); - return true; } // Used in case when access to whole aura is needed // All procs should be handled like this... -bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, uint32 cooldown, bool * handled) +bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 /*procFlag*/, uint32 procEx, bool* handled) { SpellInfo const* dummySpell = triggeredByAura->GetSpellInfo(); @@ -7707,12 +7674,6 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp case 49222: { *handled = true; - if (cooldown && GetTypeId() == TYPEID_PLAYER) - { - if (GetSpellHistory()->HasCooldown(100000)) - return false; - GetSpellHistory()->AddCooldown(100000, 0, std::chrono::seconds(cooldown)); - } return true; } // Hungering Cold aura drop @@ -7755,7 +7716,7 @@ bool Unit::HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, Sp return false; } -bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown) +bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx) { // Get triggered aura spell info SpellInfo const* auraSpellInfo = triggeredByAura->GetSpellInfo(); @@ -7781,91 +7742,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg { switch (auraSpellInfo->SpellFamilyName) { - case SPELLFAMILY_GENERIC: - switch (auraSpellInfo->Id) - { - case 43820: // Charm of the Witch Doctor (Amani Charm of the Witch Doctor trinket) - // Pct value stored in dummy - basepoints0 = victim->GetCreateHealth() * auraSpellInfo->Effects[1].CalcValue() / 100; - target = victim; - break; - case 57345: // Darkmoon Card: Greatness - { - float stat = 0.0f; - // strength - if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 60229;stat = GetStat(STAT_STRENGTH); } - // agility - if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 60233;stat = GetStat(STAT_AGILITY); } - // intellect - if (GetStat(STAT_INTELLECT)> stat) { trigger_spell_id = 60234;stat = GetStat(STAT_INTELLECT);} - // spirit - if (GetStat(STAT_SPIRIT) > stat) { trigger_spell_id = 60235; } - break; - } - case 64568: // Blood Reserve - { - if (HealthBelowPctDamaged(35, damage)) - { - CastCustomSpell(this, 64569, &triggerAmount, NULL, NULL, true); - RemoveAura(64568); - } - return false; - } - case 67702: // Death's Choice, Item - Coliseum 25 Normal Melee Trinket - { - float stat = 0.0f; - // strength - if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67708;stat = GetStat(STAT_STRENGTH); } - // agility - if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67703; } - break; - } - case 67771: // Death's Choice (heroic), Item - Coliseum 25 Heroic Melee Trinket - { - float stat = 0.0f; - // strength - if (GetStat(STAT_STRENGTH) > stat) { trigger_spell_id = 67773;stat = GetStat(STAT_STRENGTH); } - // agility - if (GetStat(STAT_AGILITY) > stat) { trigger_spell_id = 67772; } - break; - } - // Mana Drain Trigger - case 27522: - case 40336: - { - // On successful melee or ranged attack gain $29471s1 mana and if possible drain $27526s1 mana from the target. - if (IsAlive()) - CastSpell(this, 29471, true, castItem, triggeredByAura); - if (victim && victim->IsAlive()) - CastSpell(victim, 27526, true, castItem, triggeredByAura); - return true; - } - // Evasive Maneuvers - case 50240: - { - // Remove a Evasive Charge - Aura* charge = GetAura(50241); - if (charge && charge->ModStackAmount(-1, AURA_REMOVE_BY_ENEMY_SPELL)) - RemoveAurasDueToSpell(50240); - break; - } - } - break; - case SPELLFAMILY_MAGE: - if (auraSpellInfo->SpellIconID == 2127) // Blazing Speed - { - switch (auraSpellInfo->Id) - { - case 31641: // Rank 1 - case 31642: // Rank 2 - trigger_spell_id = 31643; - break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u miss posibly Blazing Speed", auraSpellInfo->Id); - return false; - } - } - break; case SPELLFAMILY_WARLOCK: { // Drain Soul @@ -7888,325 +7764,6 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg // Need for correct work Drain Soul SPELL_AURA_CHANNEL_DEATH_ITEM aura return false; } - // Nether Protection - else if (auraSpellInfo->SpellIconID == 1985) - { - if (!procSpell) - return false; - switch (GetFirstSchoolInMask(procSpell->GetSchoolMask())) - { - case SPELL_SCHOOL_NORMAL: - return false; // ignore - case SPELL_SCHOOL_HOLY: trigger_spell_id = 54370; break; - case SPELL_SCHOOL_FIRE: trigger_spell_id = 54371; break; - case SPELL_SCHOOL_NATURE: trigger_spell_id = 54375; break; - case SPELL_SCHOOL_FROST: trigger_spell_id = 54372; break; - case SPELL_SCHOOL_SHADOW: trigger_spell_id = 54374; break; - case SPELL_SCHOOL_ARCANE: trigger_spell_id = 54373; break; - default: - return false; - } - } - break; - } - case SPELLFAMILY_PRIEST: - { - // Blessed Recovery - if (auraSpellInfo->SpellIconID == 1875) - { - switch (auraSpellInfo->Id) - { - case 27811: trigger_spell_id = 27813; break; - case 27815: trigger_spell_id = 27817; break; - case 27816: trigger_spell_id = 27818; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u not handled in BR", auraSpellInfo->Id); - return false; - } - basepoints0 = CalculatePct(int32(damage), triggerAmount) / 3; - target = this; - // Add remaining ticks to healing done - basepoints0 += GetRemainingPeriodicAmount(GetGUID(), trigger_spell_id, SPELL_AURA_PERIODIC_HEAL); - } - break; - } - case SPELLFAMILY_DRUID: - { - switch (auraSpellInfo->Id) - { - // Druid Forms Trinket - case 37336: - { - switch (GetShapeshiftForm()) - { - case FORM_NONE: trigger_spell_id = 37344; break; - case FORM_CAT: trigger_spell_id = 37341; break; - case FORM_BEAR: - case FORM_DIREBEAR: trigger_spell_id = 37340; break; - case FORM_TREE: trigger_spell_id = 37342; break; - case FORM_MOONKIN: trigger_spell_id = 37343; break; - default: - return false; - } - break; - } - // Druid T9 Feral Relic (Lacerate, Swipe, Mangle, and Shred) - case 67353: - { - switch (GetShapeshiftForm()) - { - case FORM_CAT: trigger_spell_id = 67355; break; - case FORM_BEAR: - case FORM_DIREBEAR: trigger_spell_id = 67354; break; - default: - return false; - } - break; - } - default: - break; - } - break; - } - case SPELLFAMILY_HUNTER: - { - if (auraSpellInfo->SpellIconID == 3247) // Piercing Shots - { - switch (auraSpellInfo->Id) - { - case 53234: // Rank 1 - case 53237: // Rank 2 - case 53238: // Rank 3 - trigger_spell_id = 63468; - break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u miss posibly Piercing Shots", auraSpellInfo->Id); - return false; - } - SpellInfo const* TriggerPS = sSpellMgr->GetSpellInfo(trigger_spell_id); - if (!TriggerPS) - return false; - - basepoints0 = CalculatePct(int32(damage), triggerAmount) / (TriggerPS->GetMaxDuration() / TriggerPS->Effects[0].Amplitude); - basepoints0 += victim->GetRemainingPeriodicAmount(GetGUID(), trigger_spell_id, SPELL_AURA_PERIODIC_DAMAGE); - break; - } - // Item - Hunter T9 4P Bonus - if (auraSpellInfo->Id == 67151) - { - trigger_spell_id = 68130; - target = this; - break; - } - break; - } - case SPELLFAMILY_PALADIN: - { - switch (auraSpellInfo->Id) - { - // Soul Preserver - case 60510: - { - switch (getClass()) - { - case CLASS_DRUID: - trigger_spell_id = 60512; - break; - case CLASS_PALADIN: - trigger_spell_id = 60513; - break; - case CLASS_PRIEST: - trigger_spell_id = 60514; - break; - case CLASS_SHAMAN: - trigger_spell_id = 60515; - break; - } - - target = this; - break; - } - case 37657: // Lightning Capacitor - case 54841: // Thunder Capacitor - case 67712: // Item - Coliseum 25 Normal Caster Trinket - case 67758: // Item - Coliseum 25 Heroic Caster Trinket - { - if (!victim || !victim->IsAlive() || GetTypeId() != TYPEID_PLAYER) - return false; - - uint32 stack_spell_id = 0; - switch (auraSpellInfo->Id) - { - case 37657: - stack_spell_id = 37658; - trigger_spell_id = 37661; - break; - case 54841: - stack_spell_id = 54842; - trigger_spell_id = 54843; - break; - case 67712: - stack_spell_id = 67713; - trigger_spell_id = 67714; - break; - case 67758: - stack_spell_id = 67759; - trigger_spell_id = 67760; - break; - } - - CastSpell(this, stack_spell_id, true, NULL, triggeredByAura); - - Aura* dummy = GetAura(stack_spell_id); - if (!dummy || dummy->GetStackAmount() < triggerAmount) - return false; - - RemoveAurasDueToSpell(stack_spell_id); - target = victim; - break; - } - default: - // Illumination - if (auraSpellInfo->SpellIconID == 241) - { - if (!procSpell) - return false; - // procspell is triggered spell but we need mana cost of original cast spell - uint32 originalSpellId = procSpell->Id; - // Holy Shock heal - if (procSpell->SpellFamilyFlags[1] & 0x00010000) - { - switch (procSpell->Id) - { - case 25914: originalSpellId = 20473; break; - case 25913: originalSpellId = 20929; break; - case 25903: originalSpellId = 20930; break; - case 27175: originalSpellId = 27174; break; - case 33074: originalSpellId = 33072; break; - case 48820: originalSpellId = 48824; break; - case 48821: originalSpellId = 48825; break; - default: - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u not handled in HShock", procSpell->Id); - return false; - } - } - SpellInfo const* originalSpell = sSpellMgr->GetSpellInfo(originalSpellId); - if (!originalSpell) - { - TC_LOG_ERROR("entities.unit", "Unit::HandleProcTriggerSpell: Spell %u unknown but selected as original in Illu", originalSpellId); - return false; - } - // percent stored in effect 1 (class scripts) base points - int32 cost = int32(originalSpell->ManaCost + CalculatePct(GetCreateMana(), originalSpell->ManaCostPercentage)); - basepoints0 = CalculatePct(cost, auraSpellInfo->Effects[1].CalcValue()); - trigger_spell_id = 20272; - target = this; - } - break; - } - break; - } - case SPELLFAMILY_SHAMAN: - { - switch (auraSpellInfo->Id) - { - case 30881: // Nature's Guardian Rank 1 - case 30883: // Nature's Guardian Rank 2 - case 30884: // Nature's Guardian Rank 3 - case 30885: // Nature's Guardian Rank 4 - case 30886: // Nature's Guardian Rank 5 - { - if (HealthBelowPct(30)) - { - basepoints0 = int32(auraSpellInfo->Effects[EFFECT_0].CalcValue() * GetMaxHealth() / 100.0f); - target = this; - trigger_spell_id = 31616; - /// @todo Threat part - } - else - return false; - break; - } - default: - { - // Lightning Shield (overwrite non existing triggered spell call in spell.dbc - if (auraSpellInfo->SpellFamilyFlags[0] & 0x400) - { - trigger_spell_id = sSpellMgr->GetSpellWithRank(26364, auraSpellInfo->GetRank()); - } - // Nature's Guardian - else if (auraSpellInfo->SpellIconID == 2013) - { - // Check health condition - should drop to less 30% (damage deal after this!) - if (!HealthBelowPctDamaged(30, damage)) - return false; - - if (victim && victim->IsAlive()) - victim->getThreatManager().modifyThreatPercent(this, -10); - - basepoints0 = int32(CountPctFromMaxHealth(triggerAmount)); - trigger_spell_id = 31616; - target = this; - } - } - } - break; - } - case SPELLFAMILY_DEATHKNIGHT: - { - // Acclimation - if (auraSpellInfo->SpellIconID == 1930) - { - if (!procSpell) - return false; - switch (GetFirstSchoolInMask(procSpell->GetSchoolMask())) - { - case SPELL_SCHOOL_NORMAL: - return false; // ignore - case SPELL_SCHOOL_HOLY: trigger_spell_id = 50490; break; - case SPELL_SCHOOL_FIRE: trigger_spell_id = 50362; break; - case SPELL_SCHOOL_NATURE: trigger_spell_id = 50488; break; - case SPELL_SCHOOL_FROST: trigger_spell_id = 50485; break; - case SPELL_SCHOOL_SHADOW: trigger_spell_id = 50489; break; - case SPELL_SCHOOL_ARCANE: trigger_spell_id = 50486; break; - default: - return false; - } - } - // Blood Presence (Improved) - else if (auraSpellInfo->Id == 63611) - { - if (GetTypeId() != TYPEID_PLAYER) - return false; - - trigger_spell_id = 50475; - basepoints0 = CalculatePct(int32(damage), triggerAmount); - } - // Item - Death Knight T10 Melee 4P Bonus - else if (auraSpellInfo->Id == 70656) - { - if (GetTypeId() != TYPEID_PLAYER || getClass() != CLASS_DEATH_KNIGHT) - return false; - - for (uint8 i = 0; i < MAX_RUNES; ++i) - if (ToPlayer()->GetRuneCooldown(i) == 0) - return false; - } - break; - } - case SPELLFAMILY_ROGUE: - { - switch (auraSpellInfo->Id) - { - // Rogue T10 2P bonus, should only proc on caster - case 70805: - { - if (victim != this) - return false; - break; - } - } - break; } default: break; @@ -8240,7 +7797,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg float averageDmg = 0; // now compute approximate weapon damage by formula from wowwiki.com - if (procFlags & PROC_FLAG_DONE_OFFHAND_ATTACK) + if (procFlag & PROC_FLAG_DONE_OFFHAND_ATTACK) averageDmg = (GetFloatValue(UNIT_FIELD_MINOFFHANDDAMAGE) + GetFloatValue(UNIT_FIELD_MAXOFFHANDDAMAGE)) / 2.f; else averageDmg = (GetFloatValue(UNIT_FIELD_MINDAMAGE) + GetFloatValue(UNIT_FIELD_MAXDAMAGE)) / 2.f; @@ -8402,13 +7959,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg if (!target) return false; - if (cooldown && target->GetTypeId() == TYPEID_PLAYER && target->GetSpellHistory()->HasCooldown(trigger_spell_id)) - return false; - target->CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown)); return true; } // Cast positive spell on enemy target @@ -8539,7 +8090,7 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg case 56453: { // Proc only from Frost/Freezing trap activation or from Freezing Arrow (the periodic dmg proc handled elsewhere) - if (!(procFlags & PROC_FLAG_DONE_TRAP_ACTIVATION) || !procSpell || !(procSpell->SchoolMask & SPELL_SCHOOL_MASK_FROST) || !roll_chance_i(triggerAmount)) + if (!(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION) || !procSpell || !(procSpell->SchoolMask & SPELL_SCHOOL_MASK_FROST) || !roll_chance_i(triggerAmount)) return false; break; } @@ -8586,29 +8137,23 @@ bool Unit::HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* trigg } } - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(trigger_spell_id)) - return false; - // extra attack should hit same target if (triggerEntry->HasEffect(SPELL_EFFECT_ADD_EXTRA_ATTACKS)) target = victim; // try detect target manually if not set if (target == NULL) - target = !(procFlags & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim; + target = !(procFlag & (PROC_FLAG_DONE_SPELL_MAGIC_DMG_CLASS_POS | PROC_FLAG_DONE_SPELL_NONE_DMG_CLASS_POS)) && triggerEntry->IsPositive() ? this : victim; if (basepoints0) CastCustomSpell(target, trigger_spell_id, &basepoints0, NULL, NULL, true, castItem, triggeredByAura); else CastSpell(target, trigger_spell_id, true, castItem, triggeredByAura); - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(trigger_spell_id, 0, std::chrono::seconds(cooldown)); - return true; } -bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown) +bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellInfo const* procSpell) { int32 scriptId = triggeredByAura->GetMiscValue(); @@ -8699,14 +8244,7 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit* victim, uint32 /*damage*/, Au return false; } - if (cooldown && GetTypeId() == TYPEID_PLAYER && GetSpellHistory()->HasCooldown(triggered_spell_id)) - return false; - CastSpell(victim, triggered_spell_id, true, castItem, triggeredByAura); - - if (cooldown && GetTypeId() == TYPEID_PLAYER) - GetSpellHistory()->AddCooldown(triggered_spell_id, 0, std::chrono::seconds(cooldown)); - return true; } @@ -8990,7 +8528,7 @@ bool Unit::Attack(Unit* victim, bool meleeAttack) } else { - if (victim->ToCreature()->IsInEvadeMode()) + if (victim->ToCreature()->IsEvadingAttacks()) return false; } @@ -11875,7 +11413,7 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy) UpdateSpeed(MOVE_FLIGHT); } - if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_MOUNTED_COMBAT)) + if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED)) Dismount(); } @@ -11925,6 +11463,7 @@ void Unit::ClearInCombat() ToPlayer()->UpdatePotionCooldown(); RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT); + RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_LEAVE_COMBAT); } bool Unit::isTargetableForAttack(bool checkFakeDeath) const @@ -12027,7 +11566,7 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo } Creature const* creatureAttacker = ToCreature(); - if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) + if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) return false; // check duel - before sanctuary checks @@ -12111,11 +11650,14 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co // can't assist non-friendly targets if (GetReactionTo(target) < REP_NEUTRAL && target->GetReactionTo(this) < REP_NEUTRAL - && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER))) + && (!ToCreature() || !(ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT))) return false; + // Controlled player case, we can assist creatures (reaction already checked above, our faction == charmer faction) + if (GetTypeId() == TYPEID_PLAYER && IsCharmed() && GetCharmerGUID().IsCreature()) + return true; // PvP case - if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) + else if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) { Player const* targetPlayerOwner = target->GetAffectingPlayer(); if (HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE)) @@ -12145,7 +11687,7 @@ bool Unit::_IsValidAssistTarget(Unit const* target, SpellInfo const* bySpell) co && !((target->GetByteValue(UNIT_FIELD_BYTES_2, 1) & UNIT_BYTE2_FLAG_PVP))) { if (Creature const* creatureTarget = target->ToCreature()) - return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_AID_PLAYERS; + return creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT || creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_ASSIST; } return true; } @@ -12502,7 +12044,7 @@ void Unit::SetSpeedRate(UnitMoveType mtype, float rate) WorldPacket self; self.Initialize(moveTypeToOpcode[mtype][1], mtype != MOVE_RUN ? 8 + 4 + 4 : 8 + 4 + 1 + 4); self << GetPackGUID(); - self << (uint32)0; // Movement counter. Unimplemented at the moment! NUM_PMOVE_EVTS = 0x39Z. + self << (uint32)0; // Movement counter. Unimplemented at the moment! NUM_PMOVE_EVTS = 0x39Z. if (mtype == MOVE_RUN) self << uint8(1); // unknown byte added in 2.1.0 self << float(GetSpeed(mtype)); @@ -12634,7 +12176,7 @@ float Unit::ApplyTotalThreatModifier(float fThreat, SpellSchoolMask schoolMask) void Unit::AddThreat(Unit* victim, float fThreat, SpellSchoolMask schoolMask, SpellInfo const* threatSpell) { // Only mobs can manage threat lists - if (CanHaveThreatList()) + if (CanHaveThreatList() && !HasUnitState(UNIT_STATE_EVADE)) m_ThreatManager.addThreat(victim, fThreat, schoolMask, threatSpell); } @@ -13702,6 +13244,9 @@ void Unit::UpdateCharmAI() delete i_AI; i_AI = i_disabledAI; i_disabledAI = nullptr; + + if (GetTypeId() == TYPEID_UNIT) + ToCreature()->AI()->OnCharmed(false); } } else @@ -13739,6 +13284,7 @@ void Unit::UpdateCharmAI() if (!newAI) // otherwise, we default to the generic one newAI = new SimpleCharmedPlayerAI(ToPlayer()); i_AI = newAI; + newAI->OnCharmed(true); } else { @@ -13849,7 +13395,7 @@ void CharmInfo::InitPossessCreateSpells() break; } - for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) { uint32 spellId = _unit->ToCreature()->m_spells[i]; SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(spellId); @@ -14236,6 +13782,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u HealInfo healInfo = HealInfo(actor, actionTarget, damage, procSpell, procSpell ? SpellSchoolMask(procSpell->SchoolMask) : SPELL_SCHOOL_MASK_NORMAL); ProcEventInfo eventInfo = ProcEventInfo(actor, actionTarget, target, procFlag, 0, 0, procExtra, nullptr, &damageInfo, &healInfo); + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); ProcTriggeredList procTriggered; // Fill procTriggered list for (AuraApplicationMap::const_iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) @@ -14243,6 +13790,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u // Do not allow auras to proc from effect triggered by itself if (procAura && procAura->Id == itr->first) continue; + + if (itr->second->GetBase()->IsProcOnCooldown(now)) + continue; + ProcTriggeredData triggerData(itr->second->GetBase()); // Defensive procs are active on absorbs (so absorption effects are not a hindrance) bool active = damage || (procExtra & PROC_EX_BLOCK && isVictim); @@ -14267,6 +13818,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u if (!triggerData.aura->CallScriptCheckProcHandlers(itr->second, eventInfo)) continue; + bool procSuccess = RollProcResult(target, triggerData.aura, attType, isVictim, triggerData.spellProcEvent); + if (!procSuccess) + continue; + // Triggered spells not triggering additional spells bool triggered = !spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED) ? (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) : false; @@ -14320,9 +13875,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u bool prepare = i->aura->CallScriptPrepareProcHandlers(aurApp, eventInfo); // For players set spell cooldown if need - uint32 cooldown = 0; + Milliseconds cooldown = Milliseconds::zero(); if (prepare && GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown) - cooldown = i->spellProcEvent->cooldown; + cooldown = Seconds(i->spellProcEvent->cooldown); // Note: must SetCantProc(false) before return if (spellInfo->HasAttribute(SPELL_ATTR3_DISABLE_PROC)) @@ -14331,9 +13886,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u bool handled = i->aura->CallScriptProcHandlers(aurApp, eventInfo); // "handled" is needed as long as proc can be handled in multiple places - if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled)) + if (!handled && HandleAuraProc(target, damage, i->aura, procSpell, procFlag, procExtra, &handled)) { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), Id); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), Id); takeCharges = true; } @@ -14341,7 +13896,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u { for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) { - if (!(i->effMask & (1<<effIndex))) + if (!(i->effMask & (1 << effIndex))) continue; AuraEffect* triggeredByAura = i->aura->GetEffect(effIndex); @@ -14358,9 +13913,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u { case SPELL_AURA_PROC_TRIGGER_SPELL: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered by %s aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); // Don`t drop charge or add cooldown for not started trigger - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) takeCharges = true; break; } @@ -14377,7 +13933,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u case SPELL_AURA_MANA_SHIELD: case SPELL_AURA_DUMMY: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s dummy aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); if (HandleDummyAuraProc(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) takeCharges = true; break; @@ -14386,20 +13943,22 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u case SPELL_AURA_MOD_SPELL_CRIT_CHANCE: case SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN: case SPELL_AURA_MOD_MELEE_HASTE: - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", + spellInfo->Id, isVictim ? "a victim's" : "an attacker's", triggeredByAura->GetId()); takeCharges = true; break; case SPELL_AURA_OVERRIDE_CLASS_SCRIPTS: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); - if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell, cooldown)) + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell id %u (triggered by %s aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); + if (HandleOverrideClassScriptAuraProc(target, damage, triggeredByAura, procSpell)) takeCharges = true; break; } case SPELL_AURA_RAID_PROC_FROM_CHARGE_WITH_VALUE: { TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); HandleAuraRaidProcFromChargeWithValue(triggeredByAura); takeCharges = true; @@ -14408,7 +13967,7 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u case SPELL_AURA_RAID_PROC_FROM_CHARGE: { TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)", - (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); HandleAuraRaidProcFromCharge(triggeredByAura); takeCharges = true; @@ -14416,9 +13975,10 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE: { - TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id, (isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId()); + TC_LOG_DEBUG("spells", "ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", + spellInfo->Id, (isVictim ? "a victim's" : "an attacker's"), triggeredByAura->GetId()); - if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown)) + if (HandleProcTriggerSpell(target, damage, triggeredByAura, procSpell, procFlag, procExtra)) takeCharges = true; break; } @@ -14503,6 +14063,9 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u } // for (uint8 effIndex = 0; effIndex < MAX_SPELL_EFFECTS; ++effIndex) } // if (!handled) + if (prepare && takeCharges && cooldown != Milliseconds::zero()) + i->aura->AddProcCooldown(now + cooldown); + // Remove charge (aura can be removed by triggers) if (prepare && useCharges && takeCharges) { @@ -14531,6 +14094,8 @@ void Unit::ProcDamageAndSpellFor(bool isVictim, Unit* target, uint32 procFlag, u void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTriggeringProc, std::list<AuraApplication*>* procAuras, ProcEventInfo eventInfo) { + std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + // use provided list of auras which can proc if (procAuras) { @@ -14538,9 +14103,9 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge { ASSERT((*itr)->GetTarget() == this); if (!(*itr)->GetRemoveMode()) - if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo)) + if ((*itr)->GetBase()->IsProcTriggeredOnEvent(*itr, eventInfo, now)) { - (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo); + (*itr)->GetBase()->PrepareProcToTrigger(*itr, eventInfo, now); aurasTriggeringProc.push_back(*itr); } } @@ -14550,9 +14115,9 @@ void Unit::GetProcAurasTriggeredOnEvent(std::list<AuraApplication*>& aurasTrigge { for (AuraApplicationMap::iterator itr = GetAppliedAuras().begin(); itr!= GetAppliedAuras().end(); ++itr) { - if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo)) + if (itr->second->GetBase()->IsProcTriggeredOnEvent(itr->second, eventInfo, now)) { - itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo); + itr->second->GetBase()->PrepareProcToTrigger(itr->second, eventInfo, now); aurasTriggeringProc.push_back(itr->second); } } @@ -15174,23 +14739,23 @@ bool Unit::InitTamedPet(Pet* pet, uint8 level, uint32 spell_id) return true; } -bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent) +bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent) { - SpellInfo const* spellProto = aura->GetSpellInfo(); + SpellInfo const* spellInfo = aura->GetSpellInfo(); // let the aura be handled by new proc system if it has new entry - if (sSpellMgr->GetSpellProcEntry(spellProto->Id)) + if (sSpellMgr->GetSpellProcEntry(spellInfo->Id)) return false; // Get proc Event Entry - spellProcEvent = sSpellMgr->GetSpellProcEvent(spellProto->Id); + spellProcEvent = sSpellMgr->GetSpellProcEvent(spellInfo->Id); // Get EventProcFlag uint32 EventProcFlag; if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags EventProcFlag = spellProcEvent->procFlags; else - EventProcFlag = spellProto->ProcFlags; // else get from spell proto + EventProcFlag = spellInfo->ProcFlags; // else get from spell proto // Continue if no trigger exist if (!EventProcFlag) return false; @@ -15198,12 +14763,12 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const // Additional checks for triggered spells (ignore trap casts) if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_DONE_TRAP_ACTIVATION)) { - if (!spellProto->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) + if (!spellInfo->HasAttribute(SPELL_ATTR3_CAN_PROC_WITH_TRIGGERED)) return false; } // Check spellProcEvent data requirements - if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellProto, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) + if (!sSpellMgr->IsSpellProcEventCanTriggeredBy(spellInfo, spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active)) return false; // In most cases req get honor or XP from kill if (EventProcFlag & PROC_FLAG_KILL && GetTypeId() == TYPEID_PLAYER) @@ -15221,15 +14786,15 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const } // Aura added by spell can`t trigger from self (prevent drop charges/do triggers) // But except periodic and kill triggers (can triggered from self) - if (procSpell && procSpell->Id == spellProto->Id - && !(spellProto->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) + if (procSpell && procSpell->Id == spellInfo->Id + && !(spellInfo->ProcFlags&(PROC_FLAG_TAKEN_PERIODIC | PROC_FLAG_KILL))) return false; // Check if current equipment allows aura to proc if (!isVictim && GetTypeId() == TYPEID_PLAYER) { Player* player = ToPlayer(); - if (spellProto->EquippedItemClass == ITEM_CLASS_WEAPON) + if (spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON) { Item* item = NULL; if (attType == BASE_ATTACK) @@ -15242,19 +14807,26 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const if (player->IsInFeralForm()) return false; - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellProto->EquippedItemSubClassMask)) + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) return false; } - else if (spellProto->EquippedItemClass == ITEM_CLASS_ARMOR) + else if (spellInfo->EquippedItemClass == ITEM_CLASS_ARMOR) { // Check if player is wearing shield Item* item = player->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND); - if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellProto->EquippedItemSubClassMask)) + if (!item || item->IsBroken() || item->GetTemplate()->Class != ITEM_CLASS_ARMOR || !((1<<item->GetTemplate()->SubClass) & spellInfo->EquippedItemSubClassMask)) return false; } } + + return true; +} + +bool Unit::RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent) +{ + SpellInfo const* spellInfo = aura->GetSpellInfo(); // Get chance from spell - float chance = float(spellProto->ProcChance); + float chance = float(spellInfo->ProcChance); // If in spellProcEvent exist custom chance, chance = spellProcEvent->customChance; if (spellProcEvent && spellProcEvent->customChance) chance = spellProcEvent->customChance; @@ -15264,19 +14836,18 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const if (!isVictim) { uint32 weaponSpeed = GetAttackTime(attType); - chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellProto); + chance = GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); } else if (victim) { uint32 weaponSpeed = victim->GetAttackTime(attType); - chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellProto); + chance = victim->GetPPMProcChance(weaponSpeed, spellProcEvent->ppmRate, spellInfo); } } // Apply chance modifer aura if (Player* modOwner = GetSpellModOwner()) - { - modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); - } + modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CHANCE_OF_SUCCESS, chance); + return roll_chance_f(chance); } @@ -16007,7 +15578,11 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au { // change AI to charmed AI on next Update tick NeedChangeAI = true; - IsAIEnabled = false; + if (IsAIEnabled) + { + IsAIEnabled = false; + player->AI()->OnCharmed(true); + } } player->SetClientControl(this, false); } @@ -16039,7 +15614,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au case CHARM_TYPE_POSSESS: AddUnitState(UNIT_STATE_POSSESSED); SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); - charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + charmer->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); playerCharmer->SetClientControl(this, true); playerCharmer->PossessSpellInitialize(); break; @@ -16142,7 +15717,7 @@ void Unit::RemoveCharmedBy(Unit* charmer) case CHARM_TYPE_POSSESS: playerCharmer->SetClientControl(this, false); playerCharmer->SetClientControl(charmer, true); - charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + charmer->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED); ClearUnitState(UNIT_STATE_POSSESSED); break; @@ -16282,8 +15857,8 @@ bool Unit::IsInPartyWith(Unit const* unit) const if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER) return u1->ToPlayer()->IsInSameGroupWith(u2->ToPlayer()); - else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) || - (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER)) + else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || + (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; else return false; @@ -16301,8 +15876,8 @@ bool Unit::IsInRaidWith(Unit const* unit) const if (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER) return u1->ToPlayer()->IsInSameRaidWith(u2->ToPlayer()); - else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER) || - (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER)) + else if ((u2->GetTypeId() == TYPEID_PLAYER && u1->GetTypeId() == TYPEID_UNIT && u1->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT) || + (u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_UNIT && u2->ToCreature()->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT)) return true; else return false; @@ -17647,7 +17222,7 @@ void Unit::SetFacingToObject(WorldObject const* object) /// @todo figure out under what conditions creature will move towards object instead of facing it where it currently is. Movement::MoveSplineInit init(this); - init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset()); + init.MoveTo(GetPositionX(), GetPositionY(), GetPositionZMinusOffset(), false); init.SetFacing(GetAngle(object)); // when on transport, GetAngle will still return global coordinates (and angle) that needs transforming init.Launch(); } diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h index a0973aac279..b23aff3ed8e 100644 --- a/src/server/game/Entities/Unit/Unit.h +++ b/src/server/game/Entities/Unit/Unit.h @@ -76,6 +76,7 @@ enum SpellAuraInterruptFlags AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT = 0x00800000, // 23 removed by entering pvp combat AURA_INTERRUPT_FLAG_DIRECT_DAMAGE = 0x01000000, // 24 removed by any direct damage AURA_INTERRUPT_FLAG_LANDING = 0x02000000, // 25 removed by hitting the ground + AURA_INTERRUPT_FLAG_LEAVE_COMBAT = 0x80000000, // 31 removed by leaving combat AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE) }; @@ -614,7 +615,7 @@ enum UnitFlags : uint32 { UNIT_FLAG_SERVER_CONTROLLED = 0x00000001, // set only when unit movement is controlled by server - by SPLINE/MONSTER_MOVE packets, together with UNIT_FLAG_STUNNED; only set to units controlled by client; client function CGUnit_C::IsClientControlled returns false when set for owner UNIT_FLAG_NON_ATTACKABLE = 0x00000002, // not attackable - UNIT_FLAG_DISABLE_MOVE = 0x00000004, + UNIT_FLAG_REMOVE_CLIENT_CONTROL = 0x00000004, // This is a legacy flag used to disable movement player's movement while controlling other units, SMSG_CLIENT_CONTROL replaces this functionality clientside now. CONFUSED and FLEEING flags have the same effect on client movement asDISABLE_MOVE_CONTROL in addition to preventing spell casts/autoattack (they all allow climbing steeper hills and emotes while moving) UNIT_FLAG_PVP_ATTACKABLE = 0x00000008, // allow apply pvp rules to attackable state in addition to faction dependent state UNIT_FLAG_RENAME = 0x00000010, UNIT_FLAG_PREPARATION = 0x00000020, // don't take reagents for spells with SPELL_ATTR5_NO_REAGENT_WHILE_PREP @@ -626,7 +627,7 @@ enum UnitFlags : uint32 UNIT_FLAG_PET_IN_COMBAT = 0x00000800, // in combat?, 2.0.8 UNIT_FLAG_PVP = 0x00001000, // changed in 3.0.3 UNIT_FLAG_SILENCED = 0x00002000, // silenced, 2.1.1 - UNIT_FLAG_UNK_14 = 0x00004000, // 2.0.8 + UNIT_FLAG_CANNOT_SWIM = 0x00004000, // 2.0.8 UNIT_FLAG_UNK_15 = 0x00008000, UNIT_FLAG_UNK_16 = 0x00010000, UNIT_FLAG_PACIFIED = 0x00020000, // 3.0.3 ok @@ -1755,6 +1756,7 @@ class TC_GAME_API Unit : public WorldObject void RemoveAreaAurasDueToLeaveWorld(); void RemoveAllAuras(); void RemoveArenaAuras(); + void RemoveAurasOnEvade(); void RemoveAllAurasOnDeath(); void RemoveAllAurasRequiringDeadTarget(); void RemoveAllAurasExceptType(AuraType type); @@ -1856,6 +1858,9 @@ class TC_GAME_API Unit : public WorldObject Spell* FindCurrentSpellBySpellId(uint32 spell_id) const; int32 GetCurrentSpellCastTime(uint32 spell_id) const; + // Check if our current channel spell has attribute SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING + bool CanMoveDuringChannel() const; + SpellHistory* GetSpellHistory() { return m_spellHistory; } SpellHistory const* GetSpellHistory() const { return m_spellHistory; } @@ -2166,11 +2171,11 @@ class TC_GAME_API Unit : public WorldObject virtual void Yell(std::string const& text, Language language, WorldObject const* target = nullptr); virtual void TextEmote(std::string const& text, WorldObject const* target = nullptr, bool isBossEmote = false); virtual void Whisper(std::string const& text, Language language, Player* target, bool isBossWhisper = false); - void Talk(uint32 textId, ChatMsg msgType, float textRange, WorldObject const* target); - void Say(uint32 textId, WorldObject const* target = nullptr); - void Yell(uint32 textId, WorldObject const* target = nullptr); - void TextEmote(uint32 textId, WorldObject const* target = nullptr, bool isBossEmote = false); - void Whisper(uint32 textId, Player* target, bool isBossWhisper = false); + virtual void Talk(uint32 textId, ChatMsg msgType, float textRange, WorldObject const* target); + virtual void Say(uint32 textId, WorldObject const* target = nullptr); + virtual void Yell(uint32 textId, WorldObject const* target = nullptr); + virtual void TextEmote(uint32 textId, WorldObject const* target = nullptr, bool isBossEmote = false); + virtual void Whisper(uint32 textId, Player* target, bool isBossWhisper = false); protected: explicit Unit (bool isWorldObject); @@ -2250,11 +2255,12 @@ class TC_GAME_API Unit : public WorldObject void DisableSpline(); private: - bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const* & spellProcEvent); - bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool * handled); - bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown); - bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 cooldown); + bool IsTriggeredAtSpellProcEvent(Unit* victim, Aura* aura, SpellInfo const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const*& spellProcEvent); + bool RollProcResult(Unit* victim, Aura* aura, WeaponAttackType attType, bool isVictim, SpellProcEventEntry const* spellProcEvent); + bool HandleDummyAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, Milliseconds& cooldown); + bool HandleAuraProc(Unit* victim, uint32 damage, Aura* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx, bool* handled); + bool HandleProcTriggerSpell(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell, uint32 procFlag, uint32 procEx); + bool HandleOverrideClassScriptAuraProc(Unit* victim, uint32 damage, AuraEffect* triggeredByAura, SpellInfo const* procSpell); bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura); bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura); diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp index 3efeb1ca273..33a0325851a 100644 --- a/src/server/game/Globals/ObjectMgr.cpp +++ b/src/server/game/Globals/ObjectMgr.cpp @@ -473,7 +473,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) creatureTemplate.unit_flags = fields[29].GetUInt32(); creatureTemplate.unit_flags2 = fields[30].GetUInt32(); creatureTemplate.dynamicflags = fields[31].GetUInt32(); - creatureTemplate.family = fields[32].GetUInt8(); + creatureTemplate.family = CreatureFamily(fields[32].GetUInt8()); creatureTemplate.trainer_type = fields[33].GetUInt8(); creatureTemplate.trainer_spell = fields[34].GetUInt32(); creatureTemplate.trainer_class = fields[35].GetUInt8(); @@ -487,7 +487,7 @@ void ObjectMgr::LoadCreatureTemplate(Field* fields) for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i) creatureTemplate.resistance[i] = fields[42 + i - 1].GetInt16(); - for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) creatureTemplate.spells[i] = fields[48 + i].GetUInt32(); creatureTemplate.PetSpellDataId = fields[56].GetUInt32(); @@ -833,7 +833,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if (!displayScaleEntry) TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) does not have any existing display id in Modelid1/Modelid2/Modelid3/Modelid4.", cInfo->Entry); - for (int k = 0; k < MAX_KILL_CREDIT; ++k) + for (uint8 k = 0; k < MAX_KILL_CREDIT; ++k) { if (cInfo->KillCredit[k]) { @@ -888,7 +888,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) if (cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM) { TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has invalid creature family (%u) in `family`.", cInfo->Entry, cInfo->family); - const_cast<CreatureTemplate*>(cInfo)->family = 0; + const_cast<CreatureTemplate*>(cInfo)->family = CREATURE_FAMILY_NONE; } if (cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE) @@ -920,7 +920,7 @@ void ObjectMgr::CheckCreatureTemplate(CreatureTemplate const* cInfo) TC_LOG_ERROR("sql.sql", "Creature (Entry: %u) has non-existing PetSpellDataId (%u).", cInfo->Entry, cInfo->PetSpellDataId); } - for (uint8 j = 0; j < CREATURE_MAX_SPELLS; ++j) + for (uint8 j = 0; j < MAX_CREATURE_SPELLS; ++j) { if (cInfo->spells[j] && !sSpellMgr->GetSpellInfo(cInfo->spells[j])) { @@ -5946,14 +5946,14 @@ void ObjectMgr::LoadGraveyardZones() { uint32 oldMSTime = getMSTime(); - GraveYardStore.clear(); // need for reload case + GraveYardStore.clear(); // need for reload case - // 0 1 2 - QueryResult result = WorldDatabase.Query("SELECT id, ghost_zone, faction FROM game_graveyard_zone"); + // 0 1 2 + QueryResult result = WorldDatabase.Query("SELECT ID, GhostZone, Faction FROM graveyard_zone"); if (!result) { - TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `game_graveyard_zone` is empty."); + TC_LOG_INFO("server.loading", ">> Loaded 0 graveyard-zone links. DB table `graveyard_zone` is empty."); return; } @@ -5972,31 +5972,31 @@ void ObjectMgr::LoadGraveyardZones() WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(safeLocId); if (!entry) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", safeLocId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing graveyard (WorldSafeLocsID: %u), skipped.", safeLocId); continue; } AreaTableEntry const* areaEntry = sAreaTableStore.LookupEntry(zoneId); if (!areaEntry) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for not existing zone id (%u), skipped.", zoneId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non-existing Zone (ID: %u), skipped.", zoneId); continue; } if (areaEntry->zone != 0) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for subzone id (%u) instead of zone, skipped.", zoneId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for SubZone (ID: %u) instead of zone, skipped.", zoneId); continue; } if (team != 0 && team != HORDE && team != ALLIANCE) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a record for non player faction (%u), skipped.", team); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a record for non player faction (%u), skipped.", team); continue; } if (!AddGraveYardLink(safeLocId, zoneId, team, false)) - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has a duplicate record for Graveyard (ID: %u) and Zone (ID: %u), skipped.", safeLocId, zoneId); } while (result->NextRow()); TC_LOG_INFO("server.loading", ">> Loaded %u graveyard-zone links in %u ms", count, GetMSTimeDiffToNow(oldMSTime)); @@ -6045,7 +6045,7 @@ WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float if (range.first == range.second && !map->IsBattlegroundOrArena()) { if (zoneId != 0) // zone == 0 can't be fixed, used by bliz for bugged zones - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); return GetDefaultGraveYard(team); } @@ -6071,7 +6071,7 @@ WorldSafeLocsEntry const* ObjectMgr::GetClosestGraveYard(float x, float y, float WorldSafeLocsEntry const* entry = sWorldSafeLocsStore.LookupEntry(data.safeLocId); if (!entry) { - TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` has record for not existing graveyard (WorldSafeLocs.dbc id) %u, skipped.", data.safeLocId); + TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` has record for not existing graveyard (WorldSafeLocsID %u), skipped.", data.safeLocId); continue; } @@ -6186,7 +6186,7 @@ void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool GraveYardMapBoundsNonConst range = GraveYardStore.equal_range(zoneId); if (range.first == range.second) { - //TC_LOG_ERROR("sql.sql", "Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); + //TC_LOG_ERROR("sql.sql", "Table `graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.", zoneId, team); return; } diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.h b/src/server/game/Grids/Notifiers/GridNotifiers.h index 84aa29f96b7..1d2b0bd33cf 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.h +++ b/src/server/game/Grids/Notifiers/GridNotifiers.h @@ -1237,7 +1237,7 @@ namespace Trinity AllGameObjectsWithEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { } bool operator() (GameObject* go) { - if (go->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(go, m_fRange, false)) + if ((!m_uiEntry || go->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(go, m_fRange, false)) return true; return false; @@ -1254,7 +1254,7 @@ namespace Trinity AllCreaturesOfEntryInRange(const WorldObject* object, uint32 entry, float maxRange) : m_pObject(object), m_uiEntry(entry), m_fRange(maxRange) { } bool operator() (Unit* unit) { - if (unit->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(unit, m_fRange, false)) + if ((!m_uiEntry || unit->GetEntry() == m_uiEntry) && m_pObject->IsWithinDist(unit, m_fRange, false)) return true; return false; diff --git a/src/server/game/Guilds/Guild.cpp b/src/server/game/Guilds/Guild.cpp index 87494e78a28..121afc014ce 100644 --- a/src/server/game/Guilds/Guild.cpp +++ b/src/server/game/Guilds/Guild.cpp @@ -1443,17 +1443,17 @@ void Guild::HandleSetBankTabInfo(WorldSession* session, uint8 tabId, std::string _BroadcastEvent(GE_BANK_TAB_UPDATED, ObjectGuid::Empty, aux, name.c_str(), icon.c_str()); } -void Guild::HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool isPublic) +void Guild::HandleSetMemberNote(WorldSession* session, std::string const& name, std::string const& note, bool officer) { // Player must have rights to set public/officer note - if (!_HasRankRight(session->GetPlayer(), isPublic ? GR_RIGHT_EPNOTE : GR_RIGHT_EOFFNOTE)) + if (!_HasRankRight(session->GetPlayer(), officer ? GR_RIGHT_EOFFNOTE : GR_RIGHT_EPNOTE)) SendCommandResult(session, GUILD_COMMAND_PUBLIC_NOTE, ERR_GUILD_PERMISSIONS); else if (Member* member = GetMember(name)) { - if (isPublic) - member->SetPublicNote(note); - else + if (officer) member->SetOfficerNote(note); + else + member->SetPublicNote(note); HandleRoster(session); } diff --git a/src/server/game/Handlers/GroupHandler.cpp b/src/server/game/Handlers/GroupHandler.cpp index 84cbb86345d..1c1982600ca 100644 --- a/src/server/game/Handlers/GroupHandler.cpp +++ b/src/server/game/Handlers/GroupHandler.cpp @@ -488,9 +488,7 @@ void WorldSession::HandleMinimapPingOpcode(WorldPacket& recvData) void WorldSession::HandleRandomRollOpcode(WorldPacket& recvData) { - TC_LOG_DEBUG("network", "WORLD: Received MSG_RANDOM_ROLL"); - - uint32 minimum, maximum, roll; + uint32 minimum, maximum; recvData >> minimum; recvData >> maximum; @@ -499,20 +497,7 @@ void WorldSession::HandleRandomRollOpcode(WorldPacket& recvData) return; /********************/ - // everything's fine, do it - roll = urand(minimum, maximum); - - //TC_LOG_DEBUG("ROLL: MIN: %u, MAX: %u, ROLL: %u", minimum, maximum, roll); - - WorldPacket data(MSG_RANDOM_ROLL, 4+4+4+8); - data << uint32(minimum); - data << uint32(maximum); - data << uint32(roll); - data << uint64(GetPlayer()->GetGUID()); - if (GetPlayer()->GetGroup()) - GetPlayer()->GetGroup()->BroadcastPacket(&data, false); - else - SendPacket(&data); + GetPlayer()->DoRandomRoll(minimum, maximum); } void WorldSession::HandleRaidTargetUpdateOpcode(WorldPacket& recvData) diff --git a/src/server/game/Handlers/GuildHandler.cpp b/src/server/game/Handlers/GuildHandler.cpp index e8f8372d679..3801e974dba 100644 --- a/src/server/game/Handlers/GuildHandler.cpp +++ b/src/server/game/Handlers/GuildHandler.cpp @@ -178,7 +178,7 @@ void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket) if (normalizePlayerName(playerName)) if (Guild* guild = GetPlayer()->GetGuild()) - guild->HandleSetMemberNote(this, playerName, note, true); + guild->HandleSetMemberNote(this, playerName, note, false); } void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket) @@ -192,7 +192,7 @@ void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket) if (normalizePlayerName(playerName)) if (Guild* guild = GetPlayer()->GetGuild()) - guild->HandleSetMemberNote(this, playerName, note, false); + guild->HandleSetMemberNote(this, playerName, note, true); } void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket) diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp index 467d3730ab2..52b36d80202 100644 --- a/src/server/game/Handlers/MiscHandler.cpp +++ b/src/server/game/Handlers/MiscHandler.cpp @@ -884,15 +884,15 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket& recvData) player->SendTransferAborted(entry->MapID, TRANSFER_ABORT_DIFFICULTY, player->GetDifficulty(entry->IsRaid())); break; case Map::CANNOT_ENTER_NOT_IN_RAID: - if (MapEntry const* entry = sMapStore.LookupEntry(at->target_mapId)) - { - char const* mapName = entry->name[player->GetSession()->GetSessionDbcLocale()]; - TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance '%s'", player->GetName().c_str(), mapName); - // probably there must be special opcode, because client has this string constant in GlobalStrings.lua - player->GetSession()->SendAreaTriggerMessage(player->GetSession()->GetTrinityString(LANG_INSTANCE_RAID_GROUP_ONLY), mapName); - } + { + WorldPacket data(SMSG_RAID_GROUP_ONLY, 4 + 4); + data << uint32(0); + data << uint32(2); // You must be in a raid group to enter this instance. + player->GetSession()->SendPacket(&data); + TC_LOG_DEBUG("maps", "MAP: Player '%s' must be in a raid group to enter instance map %d", player->GetName().c_str(), at->target_mapId); reviveAtTrigger = true; break; + } case Map::CANNOT_ENTER_CORPSE_IN_DIFFERENT_INSTANCE: { WorldPacket data(SMSG_CORPSE_NOT_IN_INSTANCE); @@ -1057,13 +1057,13 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recvData) void WorldSession::HandleCompleteCinematic(WorldPacket& /*recvData*/) { // If player has sight bound to visual waypoint NPC we should remove it - GetPlayer()->EndCinematic(); + GetPlayer()->GetCinematicMgr()->EndCinematic(); } void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recvData*/) { // Sent by client when cinematic actually begun. So we begin the server side process - GetPlayer()->BeginCinematic(); + GetPlayer()->GetCinematicMgr()->BeginCinematic(); } void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData) diff --git a/src/server/game/Handlers/QueryHandler.cpp b/src/server/game/Handlers/QueryHandler.cpp index ebc9ebde994..1eee9e0d9fe 100644 --- a/src/server/game/Handlers/QueryHandler.cpp +++ b/src/server/game/Handlers/QueryHandler.cpp @@ -133,10 +133,10 @@ void WorldSession::HandleCreatureQueryOpcode(WorldPacket& recvData) CreatureQuestItemList const* items = sObjectMgr->GetCreatureQuestItemList(entry); if (items) - for (size_t i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) + for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) data << (i < items->size() ? uint32((*items)[i]) : uint32(0)); else - for (size_t i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) + for (uint32 i = 0; i < MAX_CREATURE_QUEST_ITEMS; ++i) data << uint32(0); data << uint32(ci->movementId); // CreatureMovementInfo.dbc diff --git a/src/server/game/Loot/LootMgr.cpp b/src/server/game/Loot/LootMgr.cpp index 5cede9a32d5..390b636bb5e 100644 --- a/src/server/game/Loot/LootMgr.cpp +++ b/src/server/game/Loot/LootMgr.cpp @@ -704,7 +704,7 @@ void Loot::DeleteLootItemFromContainerItemDB(uint32 itemID) if (_itr->itemid != itemID) continue; - _itr->canSave = true; + _itr->canSave = false; break; } } diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h index a7fdc37d324..91a26c29150 100644 --- a/src/server/game/Maps/MapManager.h +++ b/src/server/game/Maps/MapManager.h @@ -126,9 +126,9 @@ class TC_GAME_API MapManager template<typename Worker> void DoForAllMapsWithMapId(uint32 mapId, Worker&& worker); - uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; } - uint32 DecreaseScheduledScriptCount() { return --_scheduledScripts; } - uint32 DecreaseScheduledScriptCount(size_t count) { return _scheduledScripts -= count; } + void IncreaseScheduledScriptsCount() { ++_scheduledScripts; } + void DecreaseScheduledScriptCount() { --_scheduledScripts; } + void DecreaseScheduledScriptCount(std::size_t count) { _scheduledScripts -= count; } bool IsScriptScheduled() const { return _scheduledScripts > 0; } private: @@ -157,7 +157,7 @@ class TC_GAME_API MapManager MapUpdater m_updater; // atomic op counter for active scripts amount - std::atomic<uint32> _scheduledScripts; + std::atomic<std::size_t> _scheduledScripts; }; template<typename Worker> diff --git a/src/server/game/Miscellaneous/Language.h b/src/server/game/Miscellaneous/Language.h index ca63137ac93..d6af5d81432 100644 --- a/src/server/game/Miscellaneous/Language.h +++ b/src/server/game/Miscellaneous/Language.h @@ -1063,7 +1063,7 @@ enum TrinityStrings LANG_COMMAND_NO_FROZEN_PLAYERS = 5004, LANG_COMMAND_LIST_FREEZE = 5005, LANG_COMMAND_PERMA_FROZEN_PLAYER = 5006, - LANG_INSTANCE_RAID_GROUP_ONLY = 5007, + // = 5007, unused LANG_INSTANCE_CLOSED = 5008, LANG_COMMAND_PLAYED_TO_ALL = 5009, LANG_NPCINFO_LINKGUID = 5010, @@ -1212,6 +1212,8 @@ enum TrinityStrings LANG_CREATURE_NO_INTERIOR_POINT_FOUND = 11011, LANG_CREATURE_MOVEMENT_NOT_BOUNDED = 11012, LANG_CREATURE_MOVEMENT_MAYBE_UNBOUNDED = 11013, - LANG_INSTANCE_BIND_MISMATCH = 11014 + LANG_INSTANCE_BIND_MISMATCH = 11014, + LANG_CREATURE_NOT_AI_ENABLED = 11015, + LANG_SELECT_PLAYER_OR_PET = 11016, }; #endif diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index aa45c5024e6..b9df9e3accc 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -456,7 +456,7 @@ enum SpellAttr4 enum SpellAttr5 { - SPELL_ATTR5_UNK0 = 0x00000001, // 0 + SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING = 0x00000001, // 0 available casting channel spell when moving SPELL_ATTR5_NO_REAGENT_WHILE_PREP = 0x00000002, // 1 not need reagents if UNIT_FLAG_PREPARATION SPELL_ATTR5_UNK2 = 0x00000004, // 2 SPELL_ATTR5_USABLE_WHILE_STUNNED = 0x00000008, // 3 usable while stunned @@ -828,7 +828,7 @@ enum SpellEffects SPELL_EFFECT_CREATE_ITEM_2 = 157, SPELL_EFFECT_MILLING = 158, SPELL_EFFECT_ALLOW_RENAME_PET = 159, - SPELL_EFFECT_160 = 160, + SPELL_EFFECT_FORCE_CAST_2 = 160, SPELL_EFFECT_TALENT_SPEC_COUNT = 161, SPELL_EFFECT_TALENT_SPEC_SELECT = 162, SPELL_EFFECT_163 = 163, @@ -2531,6 +2531,7 @@ uint32 const CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL = (1 << (CREATURE_TYPE_ME // CreatureFamily.dbc enum CreatureFamily { + CREATURE_FAMILY_NONE = 0, CREATURE_FAMILY_WOLF = 1, CREATURE_FAMILY_CAT = 2, CREATURE_FAMILY_SPIDER = 3, @@ -2576,38 +2577,38 @@ enum CreatureFamily enum CreatureTypeFlags { - CREATURE_TYPEFLAGS_TAMEABLE = 0x00000001, // Tameable by any hunter - CREATURE_TYPEFLAGS_GHOST = 0x00000002, // Creature are also visible for not alive player. Allow gossip interaction if npcflag allow? - CREATURE_TYPEFLAGS_BOSS = 0x00000004, - CREATURE_TYPEFLAGS_UNK3 = 0x00000008, - CREATURE_TYPEFLAGS_UNK4 = 0x00000010, - CREATURE_TYPEFLAGS_UNK5 = 0x00000020, - CREATURE_TYPEFLAGS_UNK6 = 0x00000040, - CREATURE_TYPEFLAGS_DEAD_INTERACT = 0x00000080, // Player can interact with the creature if its dead (not player dead) - CREATURE_TYPEFLAGS_HERBLOOT = 0x00000100, // Can be looted by herbalist - CREATURE_TYPEFLAGS_MININGLOOT = 0x00000200, // Can be looted by miner - CREATURE_TYPEFLAGS_DONT_LOG_DEATH = 0x00000400, // Death event will not show up in combat log - CREATURE_TYPEFLAGS_MOUNTED_COMBAT = 0x00000800, // Creature can remain mounted when entering combat - CREATURE_TYPEFLAGS_AID_PLAYERS = 0x00001000, // ? Can aid any player in combat if in range? - CREATURE_TYPEFLAGS_UNK13 = 0x00002000, - CREATURE_TYPEFLAGS_UNK14 = 0x00004000, // ? Possibly not in use - CREATURE_TYPEFLAGS_ENGINEERLOOT = 0x00008000, // Can be looted by engineer - CREATURE_TYPEFLAGS_EXOTIC = 0x00010000, // Can be tamed by hunter as exotic pet - CREATURE_TYPEFLAGS_UNK17 = 0x00020000, // ? Related to vehicles/pvp? - CREATURE_TYPEFLAGS_UNK18 = 0x00040000, // ? Related to vehicle/siege weapons? - CREATURE_TYPEFLAGS_PROJECTILE_COLLISION = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ - CREATURE_TYPEFLAGS_UNK20 = 0x00100000, - CREATURE_TYPEFLAGS_UNK21 = 0x00200000, - CREATURE_TYPEFLAGS_UNK22 = 0x00400000, - CREATURE_TYPEFLAGS_UNK23 = 0x00800000, // ? First seen in 3.2.2. Related to banner/backpack of creature/companion? - CREATURE_TYPEFLAGS_UNK24 = 0x01000000, - CREATURE_TYPEFLAGS_UNK25 = 0x02000000, - CREATURE_TYPEFLAGS_PARTY_MEMBER = 0x04000000, //! Creature can be targeted by spells that require target to be in caster's party/raid - CREATURE_TYPEFLAGS_UNK27 = 0x08000000, - CREATURE_TYPEFLAGS_UNK28 = 0x10000000, - CREATURE_TYPEFLAGS_UNK29 = 0x20000000, - CREATURE_TYPEFLAGS_UNK30 = 0x40000000, - CREATURE_TYPEFLAGS_UNK31 = 0x80000000 + CREATURE_TYPE_FLAG_TAMEABLE_PET = 0x00000001, // Makes the mob tameable (must also be a beast and have family set) + CREATURE_TYPE_FLAG_GHOST_VISIBLE = 0x00000002, // Creature are also visible for not alive player. Allow gossip interaction if npcflag allow? + CREATURE_TYPE_FLAG_BOSS_MOB = 0x00000004, // Changes creature's visible level to "??" in the creature's portrait - Immune Knockback. + CREATURE_TYPE_FLAG_DO_NOT_PLAY_WOUND_PARRY_ANIMATION = 0x00000008, + CREATURE_TYPE_FLAG_HIDE_FACTION_TOOLTIP = 0x00000010, + CREATURE_TYPE_FLAG_UNK5 = 0x00000020, // Sound related + CREATURE_TYPE_FLAG_SPELL_ATTACKABLE = 0x00000040, + CREATURE_TYPE_FLAG_CAN_INTERACT_WHILE_DEAD = 0x00000080, // Player can interact with the creature if its dead (not player dead) + CREATURE_TYPE_FLAG_HERB_SKINNING_SKILL = 0x00000100, // Can be looted by herbalist + CREATURE_TYPE_FLAG_MINING_SKINNING_SKILL = 0x00000200, // Can be looted by miner + CREATURE_TYPE_FLAG_DO_NOT_LOG_DEATH = 0x00000400, // Death event will not show up in combat log + CREATURE_TYPE_FLAG_MOUNTED_COMBAT_ALLOWED = 0x00000800, // Creature can remain mounted when entering combat + CREATURE_TYPE_FLAG_CAN_ASSIST = 0x00001000, // ? Can aid any player in combat if in range? + CREATURE_TYPE_FLAG_IS_PET_BAR_USED = 0x00002000, + CREATURE_TYPE_FLAG_MASK_UID = 0x00004000, + CREATURE_TYPE_FLAG_ENGINEERING_SKINNING_SKILL = 0x00008000, // Can be looted by engineer + CREATURE_TYPE_FLAG_EXOTIC_PET = 0x00010000, // Can be tamed by hunter as exotic pet + CREATURE_TYPE_FLAG_USE_DEFAULT_COLLISION_BOX = 0x00020000, // Collision related. (always using default collision box?) + CREATURE_TYPE_FLAG_IS_SIEGE_WEAPON = 0x00040000, + CREATURE_TYPE_FLAG_CAN_COLLIDE_WITH_MISSILES = 0x00080000, // Projectiles can collide with this creature - interacts with TARGET_DEST_TRAJ + CREATURE_TYPE_FLAG_HIDE_NAME_PLATE = 0x00100000, + CREATURE_TYPE_FLAG_DO_NOT_PLAY_MOUNTED_ANIMATIONS = 0x00200000, + CREATURE_TYPE_FLAG_IS_LINK_ALL = 0x00400000, + CREATURE_TYPE_FLAG_INTERACT_ONLY_WITH_CREATOR = 0x00800000, + CREATURE_TYPE_FLAG_DO_NOT_PLAY_UNIT_EVENT_SOUNDS = 0x01000000, + CREATURE_TYPE_FLAG_HAS_NO_SHADOW_BLOB = 0x02000000, + CREATURE_TYPE_FLAG_TREAT_AS_RAID_UNIT = 0x04000000, // ! Creature can be targeted by spells that require target to be in caster's party/raid + CREATURE_TYPE_FLAG_FORCE_GOSSIP = 0x08000000, // Allows the creature to display a single gossip option. + CREATURE_TYPE_FLAG_DO_NOT_SHEATHE = 0x10000000, + CREATURE_TYPE_FLAG_DO_NOT_TARGET_ON_INTERACTION = 0x20000000, + CREATURE_TYPE_FLAG_DO_NOT_RENDER_OBJECT_NAME = 0x40000000, + CREATURE_TYPE_FLAG_UNIT_IS_QUEST_BOSS = 0x80000000 // Not verified }; enum CreatureEliteType diff --git a/src/server/game/Movement/MotionMaster.cpp b/src/server/game/Movement/MotionMaster.cpp index f27e47fba21..6f7dae1d4f2 100644 --- a/src/server/game/Movement/MotionMaster.cpp +++ b/src/server/game/Movement/MotionMaster.cpp @@ -241,7 +241,7 @@ void MotionMaster::MoveConfused() void MotionMaster::MoveChase(Unit* target, float dist, float angle) { // ignore movement request if target not exist - if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) + if (!target || target == _owner) return; //_owner->ClearUnitState(UNIT_STATE_FOLLOW); @@ -266,7 +266,7 @@ void MotionMaster::MoveChase(Unit* target, float dist, float angle) void MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot) { // ignore movement request if target not exist - if (!target || target == _owner || _owner->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE)) + if (!target || target == _owner) return; //_owner->AddUnitState(UNIT_STATE_FOLLOW); diff --git a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp index 108276c951a..6f6a8037d47 100755 --- a/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp @@ -23,11 +23,6 @@ #include "MoveSpline.h" #include "Player.h" -#ifdef MAP_BASED_RAND_GEN -#define rand_norm() unit.rand_norm() -#define urand(a, b) unit.urand(a, b) -#endif - template<class T> void ConfusedMovementGenerator<T>::DoInitialize(T* unit) { diff --git a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp index 2e013c44ae8..421678ded17 100644 --- a/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp @@ -26,10 +26,6 @@ #define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV" -#ifdef MAP_BASED_RAND_GEN -#define rand_norm() creature.rand_norm() -#endif - template<> void RandomMovementGenerator<Creature>::_setRandomLocation(Creature* creature) { diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp index d3c6c70c4ed..3fd3b702ba5 100644 --- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp @@ -36,8 +36,14 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up if (owner->HasUnitState(UNIT_STATE_NOT_MOVE)) return; + if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel()) + return; + if (owner->GetTypeId() == TYPEID_UNIT && !i_target->isInAccessiblePlaceFor(owner->ToCreature())) + { + owner->ToCreature()->SetCannotReachTarget(true); return; + } if (owner->GetTypeId() == TYPEID_UNIT && owner->ToCreature()->IsFocusing(nullptr, true)) return; @@ -48,6 +54,9 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up { if (!i_offset) { + if (i_target->IsWithinDistInMap(owner, CONTACT_DISTANCE)) + return; + // to nearest contact position i_target->GetContactPoint(owner, x, y, z); } @@ -99,8 +108,10 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up bool result = i_path->CalculatePath(x, y, z, forceDest); if (!result || (i_path->GetPathType() & PATHFIND_NOPATH)) { - // Cant reach target + // can't reach target i_recalculateTravel = true; + if (owner->GetTypeId() == TYPEID_UNIT) + owner->ToCreature()->SetCannotReachTarget(true); return; } @@ -108,6 +119,8 @@ void TargetedMovementGeneratorMedium<T, D>::_setTargetLocation(T* owner, bool up i_targetReached = false; i_recalculateTravel = false; owner->AddUnitState(UNIT_STATE_CHASE); + if (owner->GetTypeId() == TYPEID_UNIT) + owner->ToCreature()->SetCannotReachTarget(false); Movement::MoveSplineInit init(owner); init.MovebyPath(i_path->GetPath()); @@ -136,7 +149,7 @@ bool TargetedMovementGeneratorMedium<T, D>::DoUpdate(T* owner, uint32 time_diff) } // prevent movement while casting spells with cast time or channel time - if (owner->HasUnitState(UNIT_STATE_CASTING)) + if (owner->HasUnitState(UNIT_STATE_CASTING) && !owner->CanMoveDuringChannel()) { if (!owner->IsStopped()) owner->StopMoving(); @@ -198,6 +211,8 @@ void ChaseMovementGenerator<T>::_reachTarget(T* owner) { if (owner->IsWithinMeleeRange(this->i_target.getTarget())) owner->Attack(this->i_target.getTarget(), true); + if (owner->GetTypeId() == TYPEID_UNIT) + owner->ToCreature()->SetCannotReachTarget(false); } template<> diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 48d29b3259b..2ffa1a644eb 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -213,7 +213,7 @@ bool WaypointMovementGenerator<Creature>::DoUpdate(Creature* creature, uint32 di creature->SetHomePosition(creature->GetPosition()); if (creature->IsStopped()) - Stop(STOP_TIME_FOR_PLAYER); + Stop(sWorld->getIntConfig(CONFIG_CREATURE_STOP_FOR_PLAYER)); else if (creature->movespline->Finalized()) { OnArrived(creature); @@ -315,7 +315,7 @@ void FlightPathMovementGenerator::DoFinalize(Player* player) player->ClearUnitState(UNIT_STATE_IN_FLIGHT); player->Dismount(); - player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + player->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT); if (player->m_taxi.empty()) { @@ -335,7 +335,7 @@ void FlightPathMovementGenerator::DoReset(Player* player) { player->getHostileRefManager().setOnlineOfflineState(false); player->AddUnitState(UNIT_STATE_IN_FLIGHT); - player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT); + player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_TAXI_FLIGHT); Movement::MoveSplineInit init(player); uint32 end = GetPathAtMapEnd(); diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h index 1dd4611d53b..72309822dab 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h @@ -28,9 +28,9 @@ #include "MovementGenerator.h" #include "WaypointManager.h" #include "Player.h" +#include "World.h" #define FLIGHT_TRAVEL_UPDATE 100 -#define STOP_TIME_FOR_PLAYER 3 * MINUTE * IN_MILLISECONDS // 3 Minutes #define TIMEDIFF_NEXT_WP 250 template<class T, class P> diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp index ca1cd71363e..ac440da3f24 100644 --- a/src/server/game/Scripting/ScriptMgr.cpp +++ b/src/server/game/Scripting/ScriptMgr.cpp @@ -368,6 +368,8 @@ class CreatureGameObjectScriptRegistrySwapHooks // Hook which is called before a creature is swapped static void UnloadStage1(Creature* creature) { + creature->m_Events.KillAllEvents(true); + if (creature->IsCharmed()) creature->RemoveCharmedBy(nullptr); diff --git a/src/server/game/Scripting/ScriptReloadMgr.cpp b/src/server/game/Scripting/ScriptReloadMgr.cpp index 8b14ca8aeeb..d13fa9c30f0 100644 --- a/src/server/game/Scripting/ScriptReloadMgr.cpp +++ b/src/server/game/Scripting/ScriptReloadMgr.cpp @@ -56,6 +56,7 @@ ScriptReloadMgr* ScriptReloadMgr::instance() #include "Config.h" #include "BuiltInConfig.h" #include "ScriptMgr.h" +#include "SHA1.h" #include "StartProcess.h" #include "MPSCQueue.h" #include "GitRevision.h" @@ -64,8 +65,10 @@ namespace fs = boost::filesystem; #ifdef _WIN32 #include <windows.h> + #define HOTSWAP_PLATFORM_REQUIRES_CACHING #else // Posix #include <dlfcn.h> + // #define HOTSWAP_PLATFORM_REQUIRES_CACHING #endif // Promote the sScriptReloadMgr to a HotSwapScriptReloadMgr @@ -99,13 +102,25 @@ typedef HMODULE HandleType; typedef void* HandleType; #endif +static fs::path GetDirectoryOfExecutable() +{ + ASSERT((!sConfigMgr->GetArguments().empty()), + "Expected the arguments to contain at least 1 element!"); + + fs::path path(sConfigMgr->GetArguments()[0]); + if (path.is_absolute()) + return path.parent_path(); + else + return fs::absolute(path).parent_path(); +} + class SharedLibraryUnloader { public: - SharedLibraryUnloader() - : _path() { } - explicit SharedLibraryUnloader(fs::path const& path) - : _path(path) { } + explicit SharedLibraryUnloader(fs::path path) + : path_(std::move(path)) { } + SharedLibraryUnloader(fs::path path, Optional<fs::path> cache_path) + : path_(std::move(path)), cache_path_(std::move(cache_path)) { } void operator() (HandleType handle) const { @@ -119,26 +134,37 @@ public: if (!success) { TC_LOG_ERROR("scripts.hotswap", "Failed to unload (syscall) the shared library \"%s\".", - _path.generic_string().c_str()); + path_.generic_string().c_str()); return; } - boost::system::error_code error; - if (fs::remove(_path, error)) + /// When the shared library was cached delete it's shared version + if (cache_path_) { - TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded and deleted the shared library \"%s\".", - _path.generic_string().c_str()); + boost::system::error_code error; + if (!fs::remove(*cache_path_, error)) + { + TC_LOG_ERROR("scripts.hotswap", "Failed to delete the cached shared library \"%s\" (%s).", + cache_path_->generic_string().c_str(), error.message().c_str()); + + return; + } + + TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded the shared library \"%s\" " + "and deleted it's cached version at \"%s\"", + path_.generic_string().c_str(), cache_path_->generic_string().c_str()); } else { - TC_LOG_ERROR("scripts.hotswap", "Failed to delete the shared library \"%s\" (%s).", - _path.generic_string().c_str(), error.message().c_str()); + TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded the shared library \"%s\".", + path_.generic_string().c_str()); } } private: - fs::path _path; + fs::path const path_; + Optional<fs::path> const cache_path_; }; typedef std::unique_ptr<typename std::remove_pointer<HandleType>::type, SharedLibraryUnloader> HandleHolder; @@ -165,7 +191,8 @@ public: ScriptModule& operator= (ScriptModule const&) = delete; ScriptModule& operator= (ScriptModule&& right) = delete; - static Optional<std::shared_ptr<ScriptModule>> CreateFromPath(fs::path const& path); + static Optional<std::shared_ptr<ScriptModule>> + CreateFromPath(fs::path const& path, Optional<fs::path> cache_path); char const* GetScriptModuleRevisionHash() const override { @@ -215,23 +242,41 @@ static bool GetFunctionFromSharedLibrary(HandleType handle, std::string const& n } // Load a shared library from the given path. -Optional<std::shared_ptr<ScriptModule>> ScriptModule::CreateFromPath(fs::path const& path) +Optional<std::shared_ptr<ScriptModule>> + ScriptModule::CreateFromPath(fs::path const& path, Optional<fs::path> cache_path) { + auto const load_path = [&] () -> fs::path { + if (cache_path) + return *cache_path; + else + return path; + }(); + #ifdef _WIN32 - HandleType handle = LoadLibrary(path.generic_string().c_str()); + HandleType handle = LoadLibrary(load_path.generic_string().c_str()); #else // Posix - HandleType handle = dlopen(path.c_str(), RTLD_LAZY); + HandleType handle = dlopen(load_path.generic_string().c_str(), RTLD_LAZY); #endif if (!handle) { - TC_LOG_ERROR("scripts.hotswap", "Could not load the shared library \"%s\" for reading.", - path.generic_string().c_str()); + if (cache_path) + { + TC_LOG_ERROR("scripts.hotswap", "Could not dynamic load the shared library \"%s\" " + "(the library is cached at %s)", + path.generic_string().c_str(), cache_path->generic_string().c_str()); + } + else + { + TC_LOG_ERROR("scripts.hotswap", "Could not dynamic load the shared library \"%s\".", + path.generic_string().c_str()); + } + return boost::none; } // Use RAII to release the library on failure. - HandleHolder holder(handle, SharedLibraryUnloader(path)); + HandleHolder holder(handle, SharedLibraryUnloader(path, std::move(cache_path))); GetScriptModuleRevisionHashType getScriptModuleRevisionHash; AddScriptsType addScripts; @@ -357,7 +402,7 @@ static bool IsDebuggerBlockingRebuild() /// /// This class manages shared library loading/unloading through watching /// the script module directory. Loaded shared libraries are mirrored -/// into a .cache subdirectory to allow lazy unloading as long as +/// into a cache subdirectory to allow lazy unloading as long as /// the shared library is still used which is useful for scripts /// which can't be instantly replaced like spells or instances. /// Several modules which reference different versions can be kept loaded @@ -400,6 +445,8 @@ class HotSwapScriptReloadMgr final // like "Release" or "Debug". The build directive from the // previous same module is used if there was any. std::string script_module_build_directive_; + // The time where the build job started + uint32 start_time_; // Type of the current running job BuildJobType type_; @@ -412,7 +459,7 @@ class HotSwapScriptReloadMgr final : script_module_name_(std::move(script_module_name)), script_module_project_name_(std::move(script_module_project_name)), script_module_build_directive_(std::move(script_module_build_directive)), - type_(BuildJobType::BUILD_JOB_NONE) { } + start_time_(getMSTime()), type_(BuildJobType::BUILD_JOB_NONE) { } bool IsValid() const { @@ -425,6 +472,8 @@ class HotSwapScriptReloadMgr final std::string const& GetBuildDirective() const { return script_module_build_directive_; } + uint32 GetTimeFromStart() const { return GetMSTimeDiffToNow(start_time_); } + BuildJobType GetType() const { return type_; } std::shared_ptr<Trinity::AsyncProcessResult> const& GetProcess() const @@ -500,7 +549,13 @@ public: /// Returns the absolute path to the script module directory static fs::path GetLibraryDirectory() { - return fs::absolute(sConfigMgr->GetStringDefault("HotSwap.ScriptDir", "scripts")); + // When an absolute path is given in the config use it, + // otherwise interpret paths relative to the executable. + fs::path path(sConfigMgr->GetStringDefault("HotSwap.ScriptDir", "scripts")); + if (path.is_absolute()) + return path; + else + return fs::absolute(path, GetDirectoryOfExecutable()); } /// Returns the absolute path to the scripts directory in the source tree. @@ -541,28 +596,28 @@ public: } } - // Get the cache directory path - fs::path const cache_path = [] - { - auto path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory()); - path /= ".cache"; - return path; - }(); + #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING + + temporary_cache_path_ = CalculateTemporaryCachePath(); // We use the boost filesystem function versions which accept // an error code to prevent it from throwing exceptions. boost::system::error_code code; - if ((!fs::exists(cache_path, code) || (fs::remove_all(cache_path, code) > 0)) && - !fs::create_directory(cache_path, code)) + if ((!fs::exists(temporary_cache_path_, code) + || (fs::remove_all(temporary_cache_path_, code) > 0)) && + !fs::create_directory(temporary_cache_path_, code)) { - TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory \"%s\".", - cache_path.generic_string().c_str()); + TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory at \"%s\".", + temporary_cache_path_.generic_string().c_str()); + return; } // Used to silent compiler warnings (void)code; + #endif // #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING + // Correct the CMake prefix when needed if (sWorld->getBoolConfig(CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED)) DoCMakePrefixCorrectionIfNeeded(); @@ -694,6 +749,31 @@ private: _fileWatcher.watch(); } + static fs::path CalculateTemporaryCachePath() + { + auto path = fs::temp_directory_path(); + path /= Trinity::StringFormat("tc_script_cache_%s_%s", + GitRevision::GetBranch(), + CalculateSHA1Hash(sConfigMgr->GetFilename()).c_str()); + + return path; + } + + fs::path GenerateUniquePathForLibraryInCache(fs::path path) + { + ASSERT(!temporary_cache_path_.empty(), + "The temporary cache path wasn't set!"); + + // Create the cache path and increment the library counter to use an unique name for each library + auto cache_path = temporary_cache_path_; + cache_path /= Trinity::StringFormat("%s.%u%s", + path.stem().generic_string().c_str(), + _unique_library_name_counter++, + path.extension().generic_string().c_str()); + + return cache_path; + } + /// Updates the current state of the given source path void UpdateSourceChangeRequest(std::string const& module_name, fs::path const& path, @@ -774,31 +854,37 @@ private: ASSERT(_running_script_module_names.find(path) == _running_script_module_names.end(), "Can't load a module which is running already!"); - // Create the cache path and increment the library counter to use an unique name for each library - fs::path cache_path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory()); - cache_path /= ".cache"; - cache_path /= Trinity::StringFormat("%s.%u%s", - path.stem().generic_string().c_str(), - _unique_library_name_counter++, - path.extension().generic_string().c_str()); + Optional<fs::path> cache_path; + + #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING + + // Copy the shared library into a cache on platforms which lock files on use (windows). + cache_path = GenerateUniquePathForLibraryInCache(path); - boost::system::error_code code; - fs::copy_file(path, cache_path, fs::copy_option::fail_if_exists, code); - if (code) { - TC_LOG_FATAL("scripts.hotswap", ">> Failed to create cache entry for module " - "\"%s\" at \"%s\" with reason (\"%s\")!", - path.filename().generic_string().c_str(), cache_path.generic_string().c_str(), - code.message().c_str()); + boost::system::error_code code; + fs::copy_file(path, *cache_path, fs::copy_option::fail_if_exists, code); + if (code) + { + TC_LOG_FATAL("scripts.hotswap", ">> Failed to create cache entry for module " + "\"%s\" at \"%s\" with reason (\"%s\")!", + path.filename().generic_string().c_str(), cache_path->generic_string().c_str(), + code.message().c_str()); + + // Find a better solution for this but it's much better + // to start the core without scripts + std::this_thread::sleep_for(std::chrono::seconds(5)); + ABORT(); + return; + } - // Find a better solution for this but it's much better - // to start the core without scripts - std::this_thread::sleep_for(std::chrono::seconds(5)); - ABORT(); - return; + TC_LOG_TRACE("scripts.hotswap", ">> Copied the shared library \"%s\" to \"%s\" for caching.", + path.filename().generic_string().c_str(), cache_path->generic_string().c_str()); } - auto module = ScriptModule::CreateFromPath(cache_path); + #endif // #ifdef HOTSWAP_PLATFORM_REQUIRES_CACHING + + auto module = ScriptModule::CreateFromPath(path, cache_path); if (!module) { TC_LOG_FATAL("scripts.hotswap", ">> Failed to load script module \"%s\"!", @@ -1116,8 +1202,9 @@ private: if (!error) { // Installation was successful - TC_LOG_INFO("scripts.hotswap", ">> Successfully installed module %s.", - _build_job->GetModuleName().c_str()); + TC_LOG_INFO("scripts.hotswap", ">> Successfully installed module %s in %us", + _build_job->GetModuleName().c_str(), + _build_job->GetTimeFromStart() / IN_MILLISECONDS); } else { @@ -1246,7 +1333,7 @@ private: #ifndef _WIN32 // The worldserver location is ${CMAKE_INSTALL_PREFIX}/bin // on all other platforms then windows - current_path = current_path.remove_leaf(); + current_path = current_path.parent_path(); #endif if (value != current_path) @@ -1262,7 +1349,7 @@ private: if (base == branch) return true; - branch = branch.remove_leaf(); + branch = branch.parent_path(); } return false; @@ -1343,6 +1430,9 @@ private: // Is true when the build job dispatcher should stop after // the current job has finished bool terminate_early; + + // The path to the tc_scripts temporary cache + fs::path temporary_cache_path_; }; /// Maps efsw actions to strings diff --git a/src/server/game/Server/Protocol/PacketLog.cpp b/src/server/game/Server/Protocol/PacketLog.cpp index 11a02828998..114b2ae0ecc 100644 --- a/src/server/game/Server/Protocol/PacketLog.cpp +++ b/src/server/game/Server/Protocol/PacketLog.cpp @@ -45,7 +45,7 @@ struct PacketHeader uint32 SocketPort; }; - char Direction[4]; + uint32 Direction; uint32 ConnectionId; uint32 ArrivalTicks; uint32 OptionalDataSize; @@ -109,7 +109,7 @@ void PacketLog::LogPacket(WorldPacket const& packet, Direction direction, boost: std::lock_guard<std::mutex> lock(_logPacketLock); PacketHeader header; - *reinterpret_cast<uint32*>(header.Direction) = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53; + header.Direction = direction == CLIENT_TO_SERVER ? 0x47534d43 : 0x47534d53; header.ConnectionId = 0; header.ArrivalTicks = getMSTime(); diff --git a/src/server/game/Server/WorldSocket.cpp b/src/server/game/Server/WorldSocket.cpp index e3c6179a34a..1c77283d812 100644 --- a/src/server/game/Server/WorldSocket.cpp +++ b/src/server/game/Server/WorldSocket.cpp @@ -344,11 +344,12 @@ WorldSocket::ReadDataHandlerResult WorldSocket::ReadDataHandler() default: { sessionGuard.lock(); + LogOpcodeText(opcode, sessionGuard); + if (!_worldSession) { TC_LOG_ERROR("network.opcode", "ProcessIncoming: Client not authed opcode = %u", uint32(opcode)); - CloseSocket(); return ReadDataHandlerResult::Error; } diff --git a/src/server/game/Spells/Auras/SpellAuraDefines.h b/src/server/game/Spells/Auras/SpellAuraDefines.h index e0daf59fcc2..02dd88e582a 100644 --- a/src/server/game/Spells/Auras/SpellAuraDefines.h +++ b/src/server/game/Spells/Auras/SpellAuraDefines.h @@ -348,7 +348,7 @@ enum AuraType SPELL_AURA_ABILITY_PERIODIC_CRIT = 286, SPELL_AURA_DEFLECT_SPELLS = 287, SPELL_AURA_IGNORE_HIT_DIRECTION = 288, - SPELL_AURA_289 = 289, + SPELL_AURA_PREVENT_DURABILITY_LOSS = 289, SPELL_AURA_MOD_CRIT_PCT = 290, SPELL_AURA_MOD_XP_QUEST_PCT = 291, SPELL_AURA_OPEN_STABLE = 292, diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp index 459f273b790..9b23caef9ab 100644 --- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp +++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp @@ -346,7 +346,7 @@ pAuraEffectHandler AuraEffectHandler[TOTAL_AURAS]= &AuraEffect::HandleNoImmediateEffect, //286 SPELL_AURA_ABILITY_PERIODIC_CRIT implemented in AuraEffect::PeriodicTick &AuraEffect::HandleNoImmediateEffect, //287 SPELL_AURA_DEFLECT_SPELLS implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult &AuraEffect::HandleNoImmediateEffect, //288 SPELL_AURA_IGNORE_HIT_DIRECTION implemented in Unit::MagicSpellHitResult and Unit::MeleeSpellHitResult Unit::RollMeleeOutcomeAgainst - &AuraEffect::HandleNULL, //289 unused (3.2.0) + &AuraEffect::HandleNoImmediateEffect, //289 SPELL_AURA_PREVENT_DURABILITY_LOSS implemented in Player::DurabilityPointsLoss &AuraEffect::HandleAuraModCritPct, //290 SPELL_AURA_MOD_CRIT_PCT &AuraEffect::HandleNoImmediateEffect, //291 SPELL_AURA_MOD_XP_QUEST_PCT implemented in Player::RewardQuest &AuraEffect::HandleAuraOpenStable, //292 SPELL_AURA_OPEN_STABLE @@ -4676,7 +4676,7 @@ void AuraEffect::HandleAuraDummy(AuraApplication const* aurApp, uint8 mode, bool case 71563: if (Aura* newAura = target->AddAura(71564, target)) newAura->SetStackAmount(newAura->GetSpellInfo()->StackAmount); - break; + break; } } // AT REMOVE @@ -5969,9 +5969,6 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c caster->SendSpellNonMeleeDamageLog(target, GetId(), damage, GetSpellInfo()->GetSchoolMask(), absorb, resist, false, 0, crit); - if (target->GetHealth() < damage) - damage = uint32(target->GetHealth()); - // Set trigger flag uint32 procAttacker = PROC_FLAG_DONE_PERIODIC; uint32 procVictim = PROC_FLAG_TAKEN_PERIODIC; diff --git a/src/server/game/Spells/Auras/SpellAuras.cpp b/src/server/game/Spells/Auras/SpellAuras.cpp index 1ca5df6b327..3d35e4039ba 100644 --- a/src/server/game/Spells/Auras/SpellAuras.cpp +++ b/src/server/game/Spells/Auras/SpellAuras.cpp @@ -337,7 +337,8 @@ m_spellInfo(spellproto), m_casterGuid(casterGUID ? casterGUID : caster->GetGUID( m_castItemGuid(castItem ? castItem->GetGUID() : ObjectGuid::Empty), m_applyTime(time(NULL)), m_owner(owner), m_timeCla(0), m_updateTargetMapInterval(0), m_casterLevel(caster ? caster->getLevel() : m_spellInfo->SpellLevel), m_procCharges(0), m_stackAmount(1), -m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr) +m_isRemoved(false), m_isSingleTarget(false), m_isUsingCharges(false), m_dropEvent(nullptr), +m_procCooldown(std::chrono::steady_clock::time_point::min()) { if (m_spellInfo->ManaPerSecond || m_spellInfo->ManaPerSecondPerLevel) m_timeCla = 1 * IN_MILLISECONDS; @@ -789,7 +790,7 @@ uint8 Aura::CalcMaxCharges(Unit* caster) const { uint32 maxProcCharges = m_spellInfo->ProcCharges; if (SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId())) - maxProcCharges = procEntry->charges; + maxProcCharges = procEntry->Charges; if (caster) if (Player* modOwner = caster->GetSpellModOwner()) @@ -1851,22 +1852,17 @@ bool Aura::CanStackWith(Aura const* existingAura) const return true; } -bool Aura::IsProcOnCooldown() const +bool Aura::IsProcOnCooldown(std::chrono::steady_clock::time_point now) const { - /*if (m_procCooldown) - { - if (m_procCooldown > time(NULL)) - return true; - }*/ - return false; + return m_procCooldown > now; } -void Aura::AddProcCooldown(uint32 /*msec*/) +void Aura::AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd) { - //m_procCooldown = time(NULL) + msec; + m_procCooldown = cooldownEnd; } -void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo) +void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) { bool prepare = CallScriptPrepareProcHandlers(aurApp, eventInfo); if (!prepare) @@ -1884,10 +1880,10 @@ void Aura::PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInf ASSERT(procEntry); // cooldowns should be added to the whole aura (see 51698 area aura) - AddProcCooldown(procEntry->cooldown); + AddProcCooldown(now + procEntry->Cooldown); } -bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const +bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const { SpellProcEntry const* procEntry = sSpellMgr->GetSpellProcEntry(GetId()); // only auras with spell proc entry can trigger proc @@ -1899,7 +1895,7 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI return false; // check proc cooldown - if (IsProcOnCooldown()) + if (IsProcOnCooldown(now)) return false; /// @todo @@ -1963,16 +1959,16 @@ bool Aura::IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventI float Aura::CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const { - float chance = procEntry.chance; + float chance = procEntry.Chance; // calculate chances depending on unit with caster's data // so talents modifying chances and judgements will have properly calculated proc chance if (Unit* caster = GetCaster()) { // calculate ppm chance if present and we're using weapon - if (eventInfo.GetDamageInfo() && procEntry.ratePerMinute != 0) + if (eventInfo.GetDamageInfo() && procEntry.ProcsPerMinute != 0) { uint32 WeaponSpeed = caster->GetAttackTime(eventInfo.GetDamageInfo()->GetAttackType()); - chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ratePerMinute, GetSpellInfo()); + chance = caster->GetPPMProcChance(WeaponSpeed, procEntry.ProcsPerMinute, GetSpellInfo()); } // apply chance modifer aura, applies also to ppm chance (see improved judgement of light spell) if (Player* modOwner = caster->GetSpellModOwner()) diff --git a/src/server/game/Spells/Auras/SpellAuras.h b/src/server/game/Spells/Auras/SpellAuras.h index 750110dc146..a147957f258 100644 --- a/src/server/game/Spells/Auras/SpellAuras.h +++ b/src/server/game/Spells/Auras/SpellAuras.h @@ -203,12 +203,12 @@ class TC_GAME_API Aura // this subsystem is not yet in use - the core of it is functional, but still some research has to be done // and some dependant problems fixed before it can replace old proc system (for example cooldown handling) // currently proc system functionality is implemented in Unit::ProcDamageAndSpell - bool IsProcOnCooldown() const; - void AddProcCooldown(uint32 msec); + bool IsProcOnCooldown(std::chrono::steady_clock::time_point now) const; + void AddProcCooldown(std::chrono::steady_clock::time_point cooldownEnd); bool IsUsingCharges() const { return m_isUsingCharges; } void SetUsingCharges(bool val) { m_isUsingCharges = val; } - void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo); - bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo) const; + void PrepareProcToTrigger(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now); + bool IsProcTriggeredOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo, std::chrono::steady_clock::time_point now) const; float CalcProcChance(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const; void TriggerProcOnEvent(AuraApplication* aurApp, ProcEventInfo& eventInfo); @@ -269,6 +269,8 @@ class TC_GAME_API Aura ChargeDropEvent* m_dropEvent; + std::chrono::steady_clock::time_point m_procCooldown; + private: Unit::AuraApplicationList m_removedApplications; }; diff --git a/src/server/game/Spells/Spell.cpp b/src/server/game/Spells/Spell.cpp index fff69ae48df..5d96de1def9 100644 --- a/src/server/game/Spells/Spell.cpp +++ b/src/server/game/Spells/Spell.cpp @@ -979,14 +979,22 @@ void Spell::SelectImplicitChannelTargets(SpellEffIndex effIndex, SpellImplicitTa { CallScriptObjectTargetSelectHandlers(target, effIndex, targetType); if (target) - m_targets.SetDst(*target); + { + SpellDestination dest(*target); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); + m_targets.SetDst(dest); + } } else TC_LOG_DEBUG("spells", "SPELL: cannot find channel spell destination for spell ID %u, effect %u", m_spellInfo->Id, effIndex); break; case TARGET_DEST_CHANNEL_CASTER: - m_targets.SetDst(*channeledSpell->GetCaster()); + { + SpellDestination dest(*channeledSpell->GetCaster()); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); + m_targets.SetDst(dest); break; + } default: ASSERT(false && "Spell::SelectImplicitChannelTargets: received not implemented target type"); break; @@ -1042,7 +1050,11 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar if (m_spellInfo->RequiresSpellFocus) { if (focusObject) - m_targets.SetDst(*focusObject); + { + SpellDestination dest(*focusObject); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); + m_targets.SetDst(dest); + } return; } break; @@ -1068,7 +1080,6 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar switch (targetType.GetObjectType()) { case TARGET_OBJECT_TYPE_UNIT: - { if (Unit* unit = target->ToUnit()) AddUnitTarget(unit, effMask, true, false); else @@ -1077,7 +1088,6 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar return; } break; - } case TARGET_OBJECT_TYPE_GOBJ: if (GameObject* gobjTarget = target->ToGameObject()) AddGOTarget(gobjTarget, effMask); @@ -1088,8 +1098,12 @@ void Spell::SelectImplicitNearbyTargets(SpellEffIndex effIndex, SpellImplicitTar } break; case TARGET_OBJECT_TYPE_DEST: - m_targets.SetDst(*target); + { + SpellDestination dest(*target); + CallScriptDestinationTargetSelectHandlers(dest, effIndex, targetType); + m_targets.SetDst(dest); break; + } default: ASSERT(false && "Spell::SelectImplicitNearbyTargets: received not implemented target object type"); break; @@ -1288,18 +1302,30 @@ void Spell::SelectImplicitCasterDestTargets(SpellEffIndex effIndex, SpellImplici } default: { - float dist; + float dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); float angle = targetType.CalcDirectionAngle(); float objSize = m_caster->GetObjectSize(); - if (targetType.GetTarget() == TARGET_DEST_CASTER_SUMMON) - dist = PET_FOLLOW_DIST; - else - dist = m_spellInfo->Effects[effIndex].CalcRadius(m_caster); if (dist < objSize) dist = objSize; - else if (targetType.GetTarget() == TARGET_DEST_CASTER_RANDOM) - dist = objSize + (dist - objSize) * float(rand_norm()); + + switch (targetType.GetTarget()) + { + case TARGET_DEST_CASTER_SUMMON: + dist = PET_FOLLOW_DIST; + break; + case TARGET_DEST_CASTER_RANDOM: + dist = objSize + (dist - objSize) * float(rand_norm()); + break; + case TARGET_DEST_CASTER_FRONT_LEFT: + case TARGET_DEST_CASTER_BACK_LEFT: + case TARGET_DEST_CASTER_FRONT_RIGHT: + case TARGET_DEST_CASTER_BACK_RIGHT: + dist = dist + objSize; + break; + default: + break; + } Position pos = dest._position; m_caster->MovePositionToFirstCollision(pos, dist, angle); @@ -1538,7 +1564,7 @@ void Spell::SelectImplicitTrajTargets(SpellEffIndex effIndex) if (Creature* creatureTarget = unit->ToCreature()) { - if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PROJECTILE_COLLISION)) + if (!(creatureTarget->GetCreatureTemplate()->type_flags & CREATURE_TYPE_FLAG_CAN_COLLIDE_WITH_MISSILES)) continue; } } @@ -2294,8 +2320,13 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) } } + bool enablePvP = false; // need to check PvP state before spell effects, but act on it afterwards + if (spellHitTarget) { + // if target is flagged for pvp also flag caster if a player + if (unit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER) + enablePvP = true; // Decide on PvP flagging now, but act on it later. SpellMissInfo missInfo2 = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura); if (missInfo2 != SPELL_MISS_NONE) { @@ -2448,8 +2479,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo* target) // Needs to be called after dealing damage/healing to not remove breaking on damage auras DoTriggersOnSpellHit(spellHitTarget, mask); - // if target is fallged for pvp also flag caster if a player - if (unit->IsPvP() && m_caster->GetTypeId() == TYPEID_PLAYER) + if (enablePvP) m_caster->ToPlayer()->UpdatePvP(true); CallScriptAfterHitHandlers(); @@ -2884,7 +2914,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->SetSpellModTakingSpell(this, true); - // Fill cost data (not use power for item casts + // Fill cost data (do not use power for item casts) m_powerCost = m_CastItem ? 0 : m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask); if (m_caster->GetTypeId() == TYPEID_PLAYER) m_caster->ToPlayer()->SetSpellModTakingSpell(this, false); @@ -2960,12 +2990,17 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect const* triggered } // don't allow channeled spells / spells with cast time to be cast while moving + // exception are only channeled spells that have no casttime and SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING // (even if they are interrupted on moving, spells with almost immediate effect get to have their effect processed before movement interrupter kicks in) - if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) + if ((m_spellInfo->IsChanneled() || m_casttime) && m_caster->GetTypeId() == TYPEID_PLAYER && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature()) && m_caster->isMoving() && m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) { - SendCastResult(SPELL_FAILED_MOVING); - finish(false); - return; + // 1. Is a channel spell, 2. Has no casttime, 3. And has flag to allow movement during channel + if (!(m_spellInfo->IsChanneled() && !m_casttime && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING))) + { + SendCastResult(SPELL_FAILED_MOVING); + finish(false); + return; + } } // set timer base at cast time @@ -3193,6 +3228,10 @@ void Spell::cast(bool skipCheck) return; } + if (m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) + if (Creature* pet = ObjectAccessor::GetCreature(*m_caster, m_caster->GetPetGUID())) + pet->DespawnOrUnsummon(); + PrepareTriggersExecutedOnHit(); CallScriptOnCastHandlers(); @@ -3502,12 +3541,18 @@ void Spell::update(uint32 difftime) // check if the player caster has moved before the spell finished if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) && - m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) && - (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR))) + m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT && + (m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK || !m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR)))) { // don't cancel for melee, autorepeat, triggered and instant spells - if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered()) - cancel(); + if (!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !IsTriggered() && !(IsChannelActive() && m_spellInfo->HasAttribute(SPELL_ATTR5_CAN_CHANNEL_WHEN_MOVING))) + { + // if charmed by creature, trust the AI not to cheat and allow the cast to proceed + // @todo this is a hack, "creature" movesplines don't differentiate turning/moving right now + // however, checking what type of movement the spline is for every single spline would be really expensive + if (!m_caster->GetCharmerGUID().IsCreature()) + cancel(); + } } switch (m_spellState) @@ -4803,7 +4848,7 @@ SpellCastResult Spell::CheckCast(bool strict) // cancel autorepeat spells if cast start when moving // (not wand currently autorepeat cast delayed to moving stop anyway in spell update code) - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving()) + if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->ToPlayer()->isMoving() && (!m_caster->IsCharmed() || !m_caster->GetCharmerGUID().IsCreature())) { // skip stuck spell to allow use it in falling case and apply spell limitations at movement if ((!m_caster->HasUnitMovementFlag(MOVEMENTFLAG_FALLING_FAR) || m_spellInfo->Effects[0].Effect != SPELL_EFFECT_STUCK) && @@ -5260,7 +5305,7 @@ SpellCastResult Spell::CheckCast(bool strict) switch (SummonProperties->Category) { case SUMMON_CATEGORY_PET: - if (m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; // intentional missing break, check both GetPetGUID() and GetCharmGUID for SUMMON_CATEGORY_PET case SUMMON_CATEGORY_PUPPET: @@ -5276,7 +5321,7 @@ SpellCastResult Spell::CheckCast(bool strict) { if (m_targets.GetUnitTarget()->GetTypeId() != TYPEID_PLAYER) return SPELL_FAILED_BAD_TARGETS; - if (m_targets.GetUnitTarget()->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_targets.GetUnitTarget()->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; } break; @@ -5285,13 +5330,13 @@ SpellCastResult Spell::CheckCast(bool strict) { if (m_caster->GetPetGUID()) //let warlock do a replacement summon { - if (m_caster->GetTypeId() == TYPEID_PLAYER && m_caster->getClass() == CLASS_WARLOCK) + if (m_caster->GetTypeId() == TYPEID_PLAYER) { if (strict) //starting cast, trigger pet stun (cast by pet so it doesn't attack player) if (Pet* pet = m_caster->ToPlayer()->GetPet()) pet->CastSpell(pet, 32752, true, NULL, NULL, pet->GetGUID()); } - else + else if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET)) return SPELL_FAILED_ALREADY_HAVE_SUMMON; } @@ -5310,6 +5355,9 @@ SpellCastResult Spell::CheckCast(bool strict) if (!target || m_caster->ToPlayer() == target || (!target->IsInSameRaidWith(m_caster->ToPlayer()) && m_spellInfo->Id != 48955)) // refer-a-friend spell return SPELL_FAILED_BAD_TARGETS; + if (target->HasSummonPending()) + return SPELL_FAILED_SUMMON_PENDING; + // check if our map is dungeon MapEntry const* map = sMapStore.LookupEntry(m_caster->GetMapId()); if (map->IsDungeon()) @@ -5415,7 +5463,7 @@ SpellCastResult Spell::CheckCast(bool strict) if (m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_CHARM || m_spellInfo->Effects[i].ApplyAuraName == SPELL_AURA_MOD_POSSESS) { - if (m_caster->GetPetGUID()) + if (!m_spellInfo->HasAttribute(SPELL_ATTR1_DISMISS_PET) && m_caster->GetPetGUID()) return SPELL_FAILED_ALREADY_HAVE_SUMMON; if (m_caster->GetCharmGUID()) @@ -5561,7 +5609,14 @@ SpellCastResult Spell::CheckPetCast(Unit* target) m_targets.SetUnitTarget(target); } - // cooldown + // check power requirement + // this would be zero until ::prepare normally, we set it here (it gets reset in ::prepare) + m_powerCost = m_spellInfo->CalcPowerCost(m_caster, m_spellSchoolMask); + SpellCastResult failReason = CheckPower(); + if (failReason != SPELL_CAST_OK) + return failReason; + + // check cooldown if (Creature* creatureCaster = m_caster->ToCreature()) if (!creatureCaster->GetSpellHistory()->IsReady(m_spellInfo)) return SPELL_FAILED_NOT_READY; @@ -5704,6 +5759,9 @@ SpellCastResult Spell::CheckCasterAuras() const bool Spell::CanAutoCast(Unit* target) { + if (!target) + return (CheckPetCast(target) == SPELL_CAST_OK); + ObjectGuid targetguid = target->GetGUID(); // check if target already has the same or a more powerful aura @@ -5740,16 +5798,19 @@ bool Spell::CanAutoCast(Unit* target) } SpellCastResult result = CheckPetCast(target); - if (result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT) { + // do not check targets for ground-targeted spells (we target them on top of the intended target anyway) + if (GetSpellInfo()->ExplicitTargetMask & TARGET_FLAG_DEST_LOCATION) + return true; SelectSpellTargets(); //check if among target units, our WANTED target is as well (->only self cast spells return false) - for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) + for (std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit) if (ihit->targetGUID == targetguid) return true; } - return false; //target invalid + // either the cast failed or the intended target wouldn't be hit + return false; } SpellCastResult Spell::CheckRange(bool strict) diff --git a/src/server/game/Spells/Spell.h b/src/server/game/Spells/Spell.h index e634aa62a1c..b7134283ccb 100644 --- a/src/server/game/Spells/Spell.h +++ b/src/server/game/Spells/Spell.h @@ -694,14 +694,6 @@ class TC_GAME_API Spell ByteBuffer * m_effectExecuteData[MAX_SPELL_EFFECTS]; -#ifdef MAP_BASED_RAND_GEN - int32 irand(int32 min, int32 max) { return int32 (m_caster->GetMap()->mtRand.randInt(max - min)) + min; } - uint32 urand(uint32 min, uint32 max) { return m_caster->GetMap()->mtRand.randInt(max - min) + min; } - int32 rand32() { return m_caster->GetMap()->mtRand.randInt(); } - double rand_norm() { return m_caster->GetMap()->mtRand.randExc(); } - double rand_chance() { return m_caster->GetMap()->mtRand.randExc(100.0); } -#endif - Spell(Spell const& right) = delete; Spell& operator=(Spell const& right) = delete; }; diff --git a/src/server/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 5b1def554e7..8a16812034c 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -217,7 +217,7 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]= &Spell::EffectCreateItem2, //157 SPELL_EFFECT_CREATE_ITEM_2 create item or create item template and replace by some randon spell loot item &Spell::EffectMilling, //158 SPELL_EFFECT_MILLING milling &Spell::EffectRenamePet, //159 SPELL_EFFECT_ALLOW_RENAME_PET allow rename pet once again - &Spell::EffectNULL, //160 SPELL_EFFECT_160 1 spell - 45534 + &Spell::EffectForceCast, //160 SPELL_EFFECT_FORCE_CAST_2 &Spell::EffectSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert) &Spell::EffectActivateSpec, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec &Spell::EffectNULL, //163 unused @@ -3019,6 +3019,12 @@ void Spell::EffectSummonPet(SpellEffIndex effIndex) //OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation()); //OldSummon->SetMap(owner->GetMap()); //owner->GetMap()->Add(OldSummon->ToCreature()); + if (OldSummon->getPetType() == SUMMON_PET) + { + OldSummon->SetHealth(OldSummon->GetMaxHealth()); + OldSummon->SetPower(OldSummon->getPowerType(), + OldSummon->GetMaxPower(OldSummon->getPowerType())); + } if (owner->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled()) owner->ToPlayer()->PetSpellInitialize(); @@ -3771,28 +3777,6 @@ void Spell::EffectScriptEffect(SpellEffIndex effIndex) break; } - // Roll Dice - Decahedral Dwarven Dice - case 47770: - { - char buf[128]; - const char *gender = "his"; - if (m_caster->getGender() > 0) - gender = "her"; - sprintf(buf, "%s rubs %s [Decahedral Dwarven Dice] between %s hands and rolls. One %u and one %u.", m_caster->GetName().c_str(), gender, gender, urand(1, 10), urand(1, 10)); - m_caster->TextEmote(buf); - break; - } - // Roll 'dem Bones - Worn Troll Dice - case 47776: - { - char buf[128]; - const char *gender = "his"; - if (m_caster->getGender() > 0) - gender = "her"; - sprintf(buf, "%s causually tosses %s [Worn Troll Dice]. One %u and one %u.", m_caster->GetName().c_str(), gender, urand(1, 6), urand(1, 6)); - m_caster->TextEmote(buf); - break; - } // Death Knight Initiate Visual case 51519: { @@ -4234,20 +4218,7 @@ void Spell::EffectSummonPlayer(SpellEffIndex /*effIndex*/) if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER) return; - // Evil Twin (ignore player summon, but hide this for summoner) - if (unitTarget->HasAura(23445)) - return; - - float x, y, z; - m_caster->GetPosition(x, y, z); - - unitTarget->ToPlayer()->SetSummonPoint(m_caster->GetMapId(), x, y, z); - - WorldPacket data(SMSG_SUMMON_REQUEST, 8+4+4); - data << uint64(m_caster->GetGUID()); // summoner guid - data << uint32(m_caster->GetZoneId()); // summoner zone - data << uint32(MAX_PLAYER_SUMMON_DELAY*IN_MILLISECONDS); // auto decline after msecs - unitTarget->ToPlayer()->GetSession()->SendPacket(&data); + unitTarget->ToPlayer()->SendSummonRequestFrom(m_caster); } void Spell::EffectActivateObject(SpellEffIndex /*effIndex*/) @@ -5510,7 +5481,7 @@ void Spell::EffectActivateRune(SpellEffIndex effIndex) for (uint32 l = 0; l + 1 < MAX_RUNES && count > 0; ++l) { // Check if both runes are on cd as that is the only time when this needs to come into effect - if ((player->GetRuneCooldown(l) && player->GetCurrentRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetCurrentRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB))) + if ((player->GetRuneCooldown(l) && player->GetBaseRune(l) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB)) && (player->GetRuneCooldown(l+1) && player->GetBaseRune(l+1) == RuneType(m_spellInfo->Effects[effIndex].MiscValueB))) { // Should always update the rune with the lowest cd if (l + 1 < MAX_RUNES && player->GetRuneCooldown(l) >= player->GetRuneCooldown(l+1)) diff --git a/src/server/game/Spells/SpellHistory.cpp b/src/server/game/Spells/SpellHistory.cpp index 4f74197fed2..31490bea29b 100644 --- a/src/server/game/Spells/SpellHistory.cpp +++ b/src/server/game/Spells/SpellHistory.cpp @@ -533,7 +533,7 @@ void SpellHistory::LockSpellSchool(SpellSchoolMask schoolMask, uint32 lockoutTim else { Creature* creatureOwner = _owner->ToCreature(); - for (uint8 i = 0; i < CREATURE_MAX_SPELLS; ++i) + for (uint8 i = 0; i < MAX_CREATURE_SPELLS; ++i) if (creatureOwner->m_spells[i]) knownSpells.insert(creatureOwner->m_spells[i]); } diff --git a/src/server/game/Spells/SpellInfo.cpp b/src/server/game/Spells/SpellInfo.cpp index 069c794ca8b..ac157b48783 100644 --- a/src/server/game/Spells/SpellInfo.cpp +++ b/src/server/game/Spells/SpellInfo.cpp @@ -418,7 +418,8 @@ int32 SpellEffectInfo::CalcValue(Unit const* caster, int32 const* bp, Unit const level = int32(_spellInfo->MaxLevel); else if (level < int32(_spellInfo->BaseLevel)) level = int32(_spellInfo->BaseLevel); - level -= int32(_spellInfo->SpellLevel); + if (!_spellInfo->IsPassive()) + level -= int32(_spellInfo->SpellLevel); basePoints += int32(level * basePointsPerLevel); } @@ -524,10 +525,10 @@ float SpellEffectInfo::CalcValueMultiplier(Unit* caster, Spell* spell) const float SpellEffectInfo::CalcDamageMultiplier(Unit* caster, Spell* spell) const { - float multiplier = DamageMultiplier; + float multiplierPercent = DamageMultiplier * 100.0f; if (Player* modOwner = (caster ? caster->GetSpellModOwner() : NULL)) - modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplier, spell); - return multiplier; + modOwner->ApplySpellMod(_spellInfo->Id, SPELLMOD_DAMAGE_MULTIPLIER, multiplierPercent, spell); + return multiplierPercent / 100.0f; } bool SpellEffectInfo::HasRadius() const @@ -754,7 +755,7 @@ SpellEffectInfo::StaticData SpellEffectInfo::_data[TOTAL_SPELL_EFFECTS] = {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 157 SPELL_EFFECT_CREATE_ITEM_2 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_ITEM}, // 158 SPELL_EFFECT_MILLING {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 159 SPELL_EFFECT_ALLOW_RENAME_PET - {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 160 SPELL_EFFECT_160 + {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 160 SPELL_EFFECT_FORCE_CAST_2 {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 161 SPELL_EFFECT_TALENT_SPEC_COUNT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 162 SPELL_EFFECT_TALENT_SPEC_SELECT {EFFECT_IMPLICIT_TARGET_EXPLICIT, TARGET_OBJECT_TYPE_UNIT}, // 163 SPELL_EFFECT_163 diff --git a/src/server/game/Spells/SpellMgr.cpp b/src/server/game/Spells/SpellMgr.cpp index 50f750f2ca5..70eb7ad76bf 100644 --- a/src/server/game/Spells/SpellMgr.cpp +++ b/src/server/game/Spells/SpellMgr.cpp @@ -955,11 +955,11 @@ SpellProcEntry const* SpellMgr::GetSpellProcEntry(uint32 spellId) const bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcEventInfo& eventInfo) const { // proc type doesn't match - if (!(eventInfo.GetTypeMask() & procEntry.typeMask)) + if (!(eventInfo.GetTypeMask() & procEntry.ProcFlags)) return false; // check XP or honor target requirement - if (procEntry.attributesMask & PROC_ATTR_REQ_EXP_OR_HONOR) + if (procEntry.AttributesMask & PROC_ATTR_REQ_EXP_OR_HONOR) if (Player* actor = eventInfo.GetActor()->ToPlayer()) if (eventInfo.GetActionTarget() && !actor->isHonorOrXPTarget(eventInfo.GetActionTarget())) return false; @@ -969,7 +969,7 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE return true; // check school mask (if set) for other trigger types - if (procEntry.schoolMask && !(eventInfo.GetSchoolMask() & procEntry.schoolMask)) + if (procEntry.SchoolMask && !(eventInfo.GetSchoolMask() & procEntry.SchoolMask)) return false; // check spell family name/flags (if set) for spells @@ -977,31 +977,31 @@ bool SpellMgr::CanSpellTriggerProcOnEvent(SpellProcEntry const& procEntry, ProcE { SpellInfo const* eventSpellInfo = eventInfo.GetSpellInfo(); - if (procEntry.spellFamilyName && eventSpellInfo && (procEntry.spellFamilyName != eventSpellInfo->SpellFamilyName)) + if (procEntry.SpellFamilyName && eventSpellInfo && (procEntry.SpellFamilyName != eventSpellInfo->SpellFamilyName)) return false; - if (procEntry.spellFamilyMask && eventSpellInfo && !(procEntry.spellFamilyMask & eventSpellInfo->SpellFamilyFlags)) + if (procEntry.SpellFamilyMask && eventSpellInfo && !(procEntry.SpellFamilyMask & eventSpellInfo->SpellFamilyFlags)) return false; } // check spell type mask (if set) if (eventInfo.GetTypeMask() & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK)) { - if (procEntry.spellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.spellTypeMask)) + if (procEntry.SpellTypeMask && !(eventInfo.GetSpellTypeMask() & procEntry.SpellTypeMask)) return false; } // check spell phase mask if (eventInfo.GetTypeMask() & REQ_SPELL_PHASE_PROC_FLAG_MASK) { - if (!(eventInfo.GetSpellPhaseMask() & procEntry.spellPhaseMask)) + if (!(eventInfo.GetSpellPhaseMask() & procEntry.SpellPhaseMask)) return false; } // check hit mask (on taken hit or on done hit, but not on spell cast phase) if ((eventInfo.GetTypeMask() & TAKEN_HIT_PROC_FLAG_MASK) || ((eventInfo.GetTypeMask() & DONE_HIT_PROC_FLAG_MASK) && !(eventInfo.GetSpellPhaseMask() & PROC_SPELL_PHASE_CAST))) { - uint32 hitMask = procEntry.hitMask; + uint32 hitMask = procEntry.HitMask; // get default values if hit mask not set if (!hitMask) { @@ -1929,8 +1929,11 @@ void SpellMgr::LoadSpellProcs() mSpellProcMap.clear(); // need for reload case - // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 - QueryResult result = WorldDatabase.Query("SELECT spellId, schoolMask, spellFamilyName, spellFamilyMask0, spellFamilyMask1, spellFamilyMask2, typeMask, spellTypeMask, spellPhaseMask, hitMask, attributesMask, ratePerMinute, chance, cooldown, charges FROM spell_proc"); + // 0 1 2 3 4 5 + QueryResult result = WorldDatabase.Query("SELECT SpellId, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, " + // 6 7 8 9 10 11 12 13 14 + "ProcFlags, SpellTypeMask, SpellPhaseMask, HitMask, AttributesMask, ProcsPerMinute, Chance, Cooldown, Charges FROM spell_proc"); + if (!result) { TC_LOG_INFO("server.loading", ">> Loaded 0 spell proc conditions and data. DB table `spell_proc` is empty."); @@ -1972,21 +1975,20 @@ void SpellMgr::LoadSpellProcs() SpellProcEntry baseProcEntry; - baseProcEntry.schoolMask = fields[1].GetInt8(); - baseProcEntry.spellFamilyName = fields[2].GetUInt16(); - baseProcEntry.spellFamilyMask[0] = fields[3].GetUInt32(); - baseProcEntry.spellFamilyMask[1] = fields[4].GetUInt32(); - baseProcEntry.spellFamilyMask[2] = fields[5].GetUInt32(); - baseProcEntry.typeMask = fields[6].GetUInt32(); - baseProcEntry.spellTypeMask = fields[7].GetUInt32(); - baseProcEntry.spellPhaseMask = fields[8].GetUInt32(); - baseProcEntry.hitMask = fields[9].GetUInt32(); - baseProcEntry.attributesMask = fields[10].GetUInt32(); - baseProcEntry.ratePerMinute = fields[11].GetFloat(); - baseProcEntry.chance = fields[12].GetFloat(); - float cooldown = fields[13].GetFloat(); - baseProcEntry.cooldown = uint32(cooldown); - baseProcEntry.charges = fields[14].GetUInt32(); + baseProcEntry.SchoolMask = fields[1].GetInt8(); + baseProcEntry.SpellFamilyName = fields[2].GetUInt16(); + baseProcEntry.SpellFamilyMask[0] = fields[3].GetUInt32(); + baseProcEntry.SpellFamilyMask[1] = fields[4].GetUInt32(); + baseProcEntry.SpellFamilyMask[2] = fields[5].GetUInt32(); + baseProcEntry.ProcFlags = fields[6].GetUInt32(); + baseProcEntry.SpellTypeMask = fields[7].GetUInt32(); + baseProcEntry.SpellPhaseMask = fields[8].GetUInt32(); + baseProcEntry.HitMask = fields[9].GetUInt32(); + baseProcEntry.AttributesMask = fields[10].GetUInt32(); + baseProcEntry.ProcsPerMinute = fields[11].GetFloat(); + baseProcEntry.Chance = fields[12].GetFloat(); + baseProcEntry.Cooldown = Milliseconds(fields[13].GetUInt32()); + baseProcEntry.Charges = fields[14].GetUInt8(); while (spellInfo) { @@ -1999,56 +2001,46 @@ void SpellMgr::LoadSpellProcs() SpellProcEntry procEntry = SpellProcEntry(baseProcEntry); // take defaults from dbcs - if (!procEntry.typeMask) - procEntry.typeMask = spellInfo->ProcFlags; - if (!procEntry.charges) - procEntry.charges = spellInfo->ProcCharges; - if (!procEntry.chance && !procEntry.ratePerMinute) - procEntry.chance = float(spellInfo->ProcChance); + if (!procEntry.ProcFlags) + procEntry.ProcFlags = spellInfo->ProcFlags; + if (!procEntry.Charges) + procEntry.Charges = spellInfo->ProcCharges; + if (!procEntry.Chance && !procEntry.ProcsPerMinute) + procEntry.Chance = float(spellInfo->ProcChance); // validate data - if (procEntry.schoolMask & ~SPELL_SCHOOL_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `schoolMask` set: %u", spellInfo->Id, procEntry.schoolMask); - if (procEntry.spellFamilyName && (procEntry.spellFamilyName < 3 || procEntry.spellFamilyName > 17 || procEntry.spellFamilyName == 14 || procEntry.spellFamilyName == 16)) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `spellFamilyName` set: %u", spellInfo->Id, procEntry.spellFamilyName); - if (procEntry.chance < 0) - { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `chance` field", spellInfo->Id); - procEntry.chance = 0; - } - if (procEntry.ratePerMinute < 0) - { - 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) + if (procEntry.SchoolMask & ~SPELL_SCHOOL_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SchoolMask` set: %u", spellInfo->Id, procEntry.SchoolMask); + if (procEntry.SpellFamilyName && (procEntry.SpellFamilyName < 3 || procEntry.SpellFamilyName > 17 || procEntry.SpellFamilyName == 14 || procEntry.SpellFamilyName == 16)) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellFamilyName` set: %u", spellInfo->Id, procEntry.SpellFamilyName); + if (procEntry.Chance < 0) { - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `cooldown` field", spellInfo->Id); - procEntry.cooldown = 0; + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `Chance` field", spellInfo->Id); + procEntry.Chance = 0; } - if (procEntry.chance == 0 && procEntry.ratePerMinute == 0) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `chance` and `ratePerMinute` values defined, proc will not be triggered", spellInfo->Id); - if (procEntry.charges > 99) + if (procEntry.ProcsPerMinute < 0) { - 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; + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has negative value in the `ProcsPerMinute` field", spellInfo->Id); + procEntry.ProcsPerMinute = 0; } - if (!procEntry.typeMask) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `typeMask` value defined, proc will not be triggered.", spellInfo->Id); - if (procEntry.spellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `spellTypeMask` set: %u", spellInfo->Id, procEntry.spellTypeMask); - if (procEntry.spellTypeMask && !(procEntry.typeMask & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `spellTypeMask` value defined, but it will not be used for the defined `typeMask` value.", spellInfo->Id); - if (!procEntry.spellPhaseMask && procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `spellPhaseMask` value defined, but it is required for the defined `typeMask` value. Proc will not be triggered.", spellInfo->Id); - if (procEntry.spellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `spellPhaseMask` set: %u", spellInfo->Id, procEntry.spellPhaseMask); - if (procEntry.spellPhaseMask && !(procEntry.typeMask & REQ_SPELL_PHASE_PROC_FLAG_MASK)) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `spellPhaseMask` value defined, but it will not be used for the defined `typeMask` value.", spellInfo->Id); - if (procEntry.hitMask & ~PROC_HIT_MASK_ALL) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `hitMask` set: %u", spellInfo->Id, procEntry.hitMask); - if (procEntry.hitMask && !(procEntry.typeMask & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.typeMask & DONE_HIT_PROC_FLAG_MASK && (!procEntry.spellPhaseMask || procEntry.spellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) - TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `hitMask` value defined, but it will not be used for defined `typeMask` and `spellPhaseMask` values.", spellInfo->Id); + if (procEntry.Chance == 0 && procEntry.ProcsPerMinute == 0) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u doesn't have any `Chance` and `ProcsPerMinute` values defined, proc will not be triggered", spellInfo->Id); + if (!procEntry.ProcFlags) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `ProcFlags` value defined, proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellTypeMask & ~PROC_SPELL_TYPE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "`spell_proc` table entry for spellId %u has wrong `SpellTypeMask` set: %u", spellInfo->Id, procEntry.SpellTypeMask); + if (procEntry.SpellTypeMask && !(procEntry.ProcFlags & (SPELL_PROC_FLAG_MASK | PERIODIC_PROC_FLAG_MASK))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `SpellTypeMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (!procEntry.SpellPhaseMask && procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u doesn't have any `SpellPhaseMask` value defined, but it is required for the defined `ProcFlags` value. Proc will not be triggered.", spellInfo->Id); + if (procEntry.SpellPhaseMask & ~PROC_SPELL_PHASE_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `SpellPhaseMask` set: %u", spellInfo->Id, procEntry.SpellPhaseMask); + if (procEntry.SpellPhaseMask && !(procEntry.ProcFlags & REQ_SPELL_PHASE_PROC_FLAG_MASK)) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has a `SpellPhaseMask` value defined, but it will not be used for the defined `ProcFlags` value.", spellInfo->Id); + if (procEntry.HitMask & ~PROC_HIT_MASK_ALL) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has wrong `HitMask` set: %u", spellInfo->Id, procEntry.HitMask); + if (procEntry.HitMask && !(procEntry.ProcFlags & TAKEN_HIT_PROC_FLAG_MASK || (procEntry.ProcFlags & DONE_HIT_PROC_FLAG_MASK && (!procEntry.SpellPhaseMask || procEntry.SpellPhaseMask & (PROC_SPELL_PHASE_HIT | PROC_SPELL_PHASE_FINISH))))) + TC_LOG_ERROR("sql.sql", "The `spell_proc` table entry for spellId %u has `HitMask` value defined, but it will not be used for defined `ProcFlags` and `SpellPhaseMask` values.", spellInfo->Id); mSpellProcMap[spellInfo->Id] = procEntry; @@ -3758,6 +3750,8 @@ void SpellMgr::LoadSpellInfoCorrections() case 45257: // Using Steam Tonk Controller case 45440: // Steam Tonk Controller case 60256: // Collect Sample + case 45634: // Neural Needle + case 54897: // Flaming Arrow // Crashes client on pressing ESC spellInfo->AttributesEx4 &= ~SPELL_ATTR4_CAN_CAST_WHILE_CASTING; break; diff --git a/src/server/game/Spells/SpellMgr.h b/src/server/game/Spells/SpellMgr.h index fea7513b092..75da933636c 100644 --- a/src/server/game/Spells/SpellMgr.h +++ b/src/server/game/Spells/SpellMgr.h @@ -266,7 +266,7 @@ enum ProcFlagsHit PROC_HIT_REFLECT = 0x0000800, PROC_HIT_INTERRUPT = 0x0001000, // (not used atm) PROC_HIT_FULL_BLOCK = 0x0002000, - PROC_HIT_MASK_ALL = 0x2FFF + PROC_HIT_MASK_ALL = 0x0002FFF }; enum ProcAttributes @@ -290,18 +290,18 @@ typedef std::unordered_map<uint32, SpellProcEventEntry> SpellProcEventMap; struct SpellProcEntry { - uint32 schoolMask; // if nonzero - bitmask for matching proc condition based on spell's school - uint32 spellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName - flag96 spellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags - uint32 typeMask; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags - uint32 spellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType - uint32 spellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase - uint32 hitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit - uint32 attributesMask; // bitmask, see ProcAttributes - float ratePerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60 - float chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if perMinuteRate set - uint32 cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura - uint32 charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite + uint32 SchoolMask; // if nonzero - bitmask for matching proc condition based on spell's school + uint32 SpellFamilyName; // if nonzero - for matching proc condition based on candidate spell's SpellFamilyName + flag96 SpellFamilyMask; // if nonzero - bitmask for matching proc condition based on candidate spell's SpellFamilyFlags + uint32 ProcFlags; // if nonzero - owerwrite procFlags field for given Spell.dbc entry, bitmask for matching proc condition, see enum ProcFlags + uint32 SpellTypeMask; // if nonzero - bitmask for matching proc condition based on candidate spell's damage/heal effects, see enum ProcFlagsSpellType + uint32 SpellPhaseMask; // if nonzero - bitmask for matching phase of a spellcast on which proc occurs, see enum ProcFlagsSpellPhase + uint32 HitMask; // if nonzero - bitmask for matching proc condition based on hit result, see enum ProcFlagsHit + uint32 AttributesMask; // bitmask, see ProcAttributes + float ProcsPerMinute; // if nonzero - chance to proc is equal to value * aura caster's weapon speed / 60 + float Chance; // if nonzero - owerwrite procChance field for given Spell.dbc entry, defines chance of proc to occur, not used if ProcsPerMinute set + Milliseconds Cooldown; // if nonzero - cooldown in secs for aura proc, applied to aura + uint32 Charges; // if nonzero - owerwrite procCharges field for given Spell.dbc entry, defines how many times proc can occur before aura remove, 0 - infinite }; typedef std::unordered_map<uint32, SpellProcEntry> SpellProcMap; diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp index 4b64ef0bbd8..37457f36145 100644 --- a/src/server/game/World/World.cpp +++ b/src/server/game/World/World.cpp @@ -66,7 +66,7 @@ #include "WaypointMovementGenerator.h" #include "WeatherMgr.h" #include "WorldSession.h" - +#include "M2Stores.h" TC_GAME_API std::atomic<bool> World::m_stopEvent(false); TC_GAME_API uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE; @@ -659,9 +659,8 @@ void World::LoadConfigSettings(bool reload) m_float_configs[CONFIG_GROUP_XP_DISTANCE] = sConfigMgr->GetFloatDefault("MaxGroupXPDistance", 74.0f); m_float_configs[CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE] = sConfigMgr->GetFloatDefault("MaxRecruitAFriendBonusDistance", 100.0f); - /// @todo Add MonsterSight and GuarderSight (with meaning) in worldserver.conf or put them as define + /// @todo Add MonsterSight (with meaning) in worldserver.conf or put them as define m_float_configs[CONFIG_SIGHT_MONSTER] = sConfigMgr->GetFloatDefault("MonsterSight", 50.0f); - m_float_configs[CONFIG_SIGHT_GUARDER] = sConfigMgr->GetFloatDefault("GuarderSight", 50.0f); if (reload) { @@ -900,6 +899,7 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_ALLOW_GM_GROUP] = sConfigMgr->GetBoolDefault("GM.AllowInvite", false); m_bool_configs[CONFIG_GM_LOWER_SECURITY] = sConfigMgr->GetBoolDefault("GM.LowerSecurity", false); m_float_configs[CONFIG_CHANCE_OF_GM_SURVEY] = sConfigMgr->GetFloatDefault("GM.TicketSystem.ChanceOfGMSurvey", 50.0f); + m_int_configs[CONFIG_FORCE_SHUTDOWN_THRESHOLD] = sConfigMgr->GetIntDefault("GM.ForceShutdownThreshold", 30); m_int_configs[CONFIG_GROUP_VISIBILITY] = sConfigMgr->GetIntDefault("Visibility.GroupMode", 1); @@ -1082,6 +1082,7 @@ void World::LoadConfigSettings(bool reload) m_bool_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfigMgr->GetBoolDefault("OffhandCheckAtSpellUnlearn", true); m_int_configs[CONFIG_CREATURE_PICKPOCKET_REFILL] = sConfigMgr->GetIntDefault("Creature.PickPocketRefillDelay", 10 * MINUTE); + m_int_configs[CONFIG_CREATURE_STOP_FOR_PLAYER] = sConfigMgr->GetIntDefault("Creature.MovingStopTimeForPlayer", 3 * MINUTE * IN_MILLISECONDS); if (int32 clientCacheId = sConfigMgr->GetIntDefault("ClientCacheVersion", 0)) { @@ -1402,6 +1403,9 @@ void World::SetInitialWorldSettings() LoadDBCStores(m_dataPath); DetectDBCLang(); + // Load cinematic cameras + LoadM2Cameras(m_dataPath); + std::vector<uint32> mapIds; for (uint32 mapId = 0; mapId < sMapStore.GetNumRows(); mapId++) if (sMapStore.LookupEntry(mapId)) diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h index abc0ea452ac..4456b7a553e 100644 --- a/src/server/game/World/World.h +++ b/src/server/game/World/World.h @@ -28,7 +28,7 @@ #include "Timer.h" #include "SharedDefines.h" #include "QueryResult.h" -#include "Callback.h" +#include "QueryCallback.h" #include "Realm/Realm.h" #include <atomic> @@ -56,7 +56,8 @@ enum ServerMessageType enum ShutdownMask { SHUTDOWN_MASK_RESTART = 1, - SHUTDOWN_MASK_IDLE = 2 + SHUTDOWN_MASK_IDLE = 2, + SHUTDOWN_MASK_FORCE = 4 }; enum ShutdownExitCode @@ -182,7 +183,6 @@ enum WorldFloatConfigs CONFIG_GROUP_XP_DISTANCE = 0, CONFIG_MAX_RECRUIT_A_FRIEND_DISTANCE, CONFIG_SIGHT_MONSTER, - CONFIG_SIGHT_GUARDER, CONFIG_LISTEN_RANGE_SAY, CONFIG_LISTEN_RANGE_TEXTEMOTE, CONFIG_LISTEN_RANGE_YELL, @@ -252,6 +252,7 @@ enum WorldIntConfigs CONFIG_GM_LEVEL_IN_GM_LIST, CONFIG_GM_LEVEL_IN_WHO_LIST, CONFIG_START_GM_LEVEL, + CONFIG_FORCE_SHUTDOWN_THRESHOLD, CONFIG_GROUP_VISIBILITY, CONFIG_MAIL_DELIVERY_DELAY, CONFIG_UPTIME_UPDATE, @@ -357,6 +358,7 @@ enum WorldIntConfigs CONFIG_BG_REWARD_LOSER_HONOR_LAST, CONFIG_BIRTHDAY_TIME, CONFIG_CREATURE_PICKPOCKET_REFILL, + CONFIG_CREATURE_STOP_FOR_PLAYER, CONFIG_AHBOT_UPDATE_INTERVAL, CONFIG_CHARTER_COST_GUILD, CONFIG_CHARTER_COST_ARENA_2v2, diff --git a/src/server/scripts/Commands/cs_character.cpp b/src/server/scripts/Commands/cs_character.cpp index 9557d182df1..01602e2f24c 100644 --- a/src/server/scripts/Commands/cs_character.cpp +++ b/src/server/scripts/Commands/cs_character.cpp @@ -436,7 +436,7 @@ public: if (isalpha(levelStr[0])) { nameStr = levelStr; - levelStr = NULL; // current level will used + levelStr = nullptr; // current level will used } Player* target; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index 1d8094885d4..ca4dd814e01 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -34,6 +34,7 @@ EndScriptData */ #include "Transport.h" #include "Language.h" #include "MapManager.h" +#include "M2Stores.h" #include <fstream> @@ -847,7 +848,7 @@ public: if (Unit* unit = ref->GetSource()->GetOwner()) { ++count; - handler->PSendSysMessage(" %u. %s (guid %u) - threat %f", count, unit->GetName().c_str(), unit->GetGUID().GetCounter(), ref->getThreat()); + handler->PSendSysMessage(" %u. %s (current guid %u, DB guid %u) - threat %f", count, unit->GetName().c_str(), unit->GetGUID().GetCounter(), unit->GetTypeId() == TYPEID_UNIT ? unit->ToCreature()->GetSpawnId() : 0, ref->getThreat()); } ref = ref->next(); } diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index 3e35a721162..a98f9f4bf9c 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -796,7 +796,7 @@ public: target->CleanupAfterTaxiFlight(); } - target->TeleportTo(target->m_recallMap, target->m_recallX, target->m_recallY, target->m_recallZ, target->m_recallO); + target->Recall(); return true; } diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp index f1ddb448b35..e25018cf1bd 100644 --- a/src/server/scripts/Commands/cs_modify.cpp +++ b/src/server/scripts/Commands/cs_modify.cpp @@ -80,23 +80,32 @@ public: return commandTable; } - //Edit Player HP - static bool HandleModifyHPCommand(ChatHandler* handler, const char* args) + template<typename... Args> + static void NotifyModification(ChatHandler* handler, Unit* target, TrinityStrings resourceMessage, TrinityStrings resourceReportMessage, Args&&... args) + { + if (Player* player = target->ToPlayer()) + { + handler->PSendSysMessage(resourceMessage, handler->GetNameLink(player).c_str(), args...); + if (handler->needReportToTarget(player)) + ChatHandler(player->GetSession()).PSendSysMessage(resourceReportMessage, handler->GetNameLink().c_str(), std::forward<Args>(args)...); + } + } + + static bool CheckModifyResources(ChatHandler* handler, const char* args, Player* target, int32& res, int32& resmax, int8 const multiplier = 1) { if (!*args) return false; - int32 hp = atoi((char*)args); - int32 hpm = atoi((char*)args); + res = atoi((char*)args) * multiplier; + resmax = atoi((char*)args) * multiplier; - if (hp < 1 || hpm < 1 || hpm < hp) + if (res < 1 || resmax < 1 || resmax < res) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } - Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -107,164 +116,87 @@ public: if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) return false; - handler->PSendSysMessage(LANG_YOU_CHANGE_HP, handler->GetNameLink(target).c_str(), hp, hpm); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_HP_CHANGED, handler->GetNameLink().c_str(), hp, hpm); - - target->SetMaxHealth(hpm); - target->SetHealth(hp); - return true; } - //Edit Player Mana - static bool HandleModifyManaCommand(ChatHandler* handler, const char* args) + //Edit Player HP + static bool HandleModifyHPCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - int32 mana = atoi((char*)args); - int32 manam = atoi((char*)args); - - if (mana <= 0 || manam <= 0 || manam < mana) + int32 hp, hpmax; + Player* target = handler->getSelectedPlayerOrSelf(); + if (CheckModifyResources(handler, args, target, hp, hpmax)) { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_HP, LANG_YOURS_HP_CHANGED, hp, hpmax); + target->SetMaxHealth(hpmax); + target->SetHealth(hp); + return true; } + return false; + } + //Edit Player Mana + static bool HandleModifyManaCommand(ChatHandler* handler, const char* args) + { + int32 mana, manamax; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + + if (CheckModifyResources(handler, args, target, mana, manamax)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_MANA, LANG_YOURS_MANA_CHANGED, mana, manamax); + target->SetMaxPower(POWER_MANA, manamax); + target->SetPower(POWER_MANA, mana); + return true; } - - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - handler->PSendSysMessage(LANG_YOU_CHANGE_MANA, handler->GetNameLink(target).c_str(), mana, manam); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_MANA_CHANGED, handler->GetNameLink().c_str(), mana, manam); - - target->SetMaxPower(POWER_MANA, manam); - target->SetPower(POWER_MANA, mana); - - return true; + return false; } //Edit Player Energy static bool HandleModifyEnergyCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - int32 energy = atoi((char*)args)*10; - int32 energym = atoi((char*)args)*10; - - if (energy <= 0 || energym <= 0 || energym < energy) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + int32 energy, energymax; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + int8 const energyMultiplier = 10; + if (CheckModifyResources(handler, args, target, energy, energymax, energyMultiplier)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_ENERGY, LANG_YOURS_ENERGY_CHANGED, energy / energyMultiplier, energymax / energyMultiplier); + target->SetMaxPower(POWER_ENERGY, energymax); + target->SetPower(POWER_ENERGY, energy); + TC_LOG_DEBUG("misc", handler->GetTrinityString(LANG_CURRENT_ENERGY), target->GetMaxPower(POWER_ENERGY)); + return true; } - - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - handler->PSendSysMessage(LANG_YOU_CHANGE_ENERGY, handler->GetNameLink(target).c_str(), energy/10, energym/10); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, handler->GetNameLink().c_str(), energy/10, energym/10); - - target->SetMaxPower(POWER_ENERGY, energym); - target->SetPower(POWER_ENERGY, energy); - - TC_LOG_DEBUG("misc", handler->GetTrinityString(LANG_CURRENT_ENERGY), target->GetMaxPower(POWER_ENERGY)); - - return true; + return false; } //Edit Player Rage static bool HandleModifyRageCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - int32 rage = atoi((char*)args)*10; - int32 ragem = atoi((char*)args)*10; - - if (rage <= 0 || ragem <= 0 || ragem < rage) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + int32 rage, ragemax; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + int8 const rageMultiplier = 10; + if (CheckModifyResources(handler, args, target, rage, ragemax, rageMultiplier)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_RAGE, LANG_YOURS_RAGE_CHANGED, rage / rageMultiplier, ragemax / rageMultiplier); + target->SetMaxPower(POWER_RAGE, ragemax); + target->SetPower(POWER_RAGE, rage); + return true; } - - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - handler->PSendSysMessage(LANG_YOU_CHANGE_RAGE, handler->GetNameLink(target).c_str(), rage/10, ragem/10); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_RAGE_CHANGED, handler->GetNameLink().c_str(), rage/10, ragem/10); - - target->SetMaxPower(POWER_RAGE, ragem); - target->SetPower(POWER_RAGE, rage); - - return true; + return false; } // Edit Player Runic Power static bool HandleModifyRunicPowerCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - int32 rune = atoi((char*)args)*10; - int32 runem = atoi((char*)args)*10; - - if (rune <= 0 || runem <= 0 || runem < rune) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + int32 rune, runemax; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + int8 const runeMultiplier = 10; + if (CheckModifyResources(handler, args, target, rune, runemax, runeMultiplier)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_RUNIC_POWER, LANG_YOURS_RUNIC_POWER_CHANGED, rune / runeMultiplier, runemax / runeMultiplier); + target->SetMaxPower(POWER_RUNIC_POWER, runemax); + target->SetPower(POWER_RUNIC_POWER, rune); + return true; } - - handler->PSendSysMessage(LANG_YOU_CHANGE_RUNIC_POWER, handler->GetNameLink(target).c_str(), rune/10, runem/10); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_RUNIC_POWER_CHANGED, handler->GetNameLink().c_str(), rune/10, runem/10); - - target->SetMaxPower(POWER_RUNIC_POWER, runem); - target->SetPower(POWER_RUNIC_POWER, rune); - - return true; + return false; } //Edit Player Faction @@ -437,22 +369,20 @@ public: return false; } - //Edit Player Aspeed - static bool HandleModifyASpeedCommand(ChatHandler* handler, const char* args) + static bool CheckModifySpeed(ChatHandler* handler, const char* args, Unit* target, float& speed, float minimumBound, float maximumBound, bool checkInFlight = true) { if (!*args) return false; - float ASpeed = (float)atof((char*)args); + speed = (float)atof((char*)args); - if (ASpeed > 50.0f || ASpeed < 0.1f) + if (speed > maximumBound || speed < minimumBound) { handler->SendSysMessage(LANG_BAD_VALUE); handler->SetSentErrorMessage(true); return false; } - Player* target = handler->getSelectedPlayerOrSelf(); if (!target) { handler->SendSysMessage(LANG_NO_CHAR_SELECTED); @@ -460,238 +390,107 @@ public: return false; } - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - std::string targetNameLink = handler->GetNameLink(target); - - if (target->IsInFlight()) + if (Player* player = target->ToPlayer()) { - handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str()); - handler->SetSentErrorMessage(true); - return false; - } - - handler->PSendSysMessage(LANG_YOU_CHANGE_ASPEED, targetNameLink.c_str(), ASpeed); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, handler->GetNameLink().c_str(), ASpeed); + // check online security + if (handler->HasLowerSecurity(player, ObjectGuid::Empty)) + return false; - target->SetSpeedRate(MOVE_WALK, ASpeed); - target->SetSpeedRate(MOVE_RUN, ASpeed); - target->SetSpeedRate(MOVE_SWIM, ASpeed); - //target->SetSpeedRate(MOVE_TURN, ASpeed); - target->SetSpeedRate(MOVE_FLIGHT, ASpeed); + if (player->IsInFlight() && checkInFlight) + { + handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, handler->GetNameLink(player).c_str()); + handler->SetSentErrorMessage(true); + return false; + } + } return true; } - //Edit Player Speed - static bool HandleModifySpeedCommand(ChatHandler* handler, const char* args) + //Edit Player Aspeed + static bool HandleModifyASpeedCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - float Speed = (float)atof((char*)args); - - if (Speed > 50.0f || Speed < 0.1f) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + float allSpeed; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + if (CheckModifySpeed(handler, args, target, allSpeed, 0.1f, 50.0f)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_ASPEED, LANG_YOURS_ASPEED_CHANGED, allSpeed); + target->SetSpeedRate(MOVE_WALK, allSpeed); + target->SetSpeedRate(MOVE_RUN, allSpeed); + target->SetSpeedRate(MOVE_SWIM, allSpeed); + target->SetSpeedRate(MOVE_FLIGHT, allSpeed); + return true; } + return false; + } - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - std::string targetNameLink = handler->GetNameLink(target); - - if (target->IsInFlight()) + //Edit Player Speed + static bool HandleModifySpeedCommand(ChatHandler* handler, const char* args) + { + float Speed; + Player* target = handler->getSelectedPlayerOrSelf(); + if (CheckModifySpeed(handler, args, target, Speed, 0.1f, 50.0f)) { - handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str()); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_SPEED, LANG_YOURS_SPEED_CHANGED, Speed); + target->SetSpeedRate(MOVE_RUN, Speed); + return true; } - - handler->PSendSysMessage(LANG_YOU_CHANGE_SPEED, targetNameLink.c_str(), Speed); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, handler->GetNameLink().c_str(), Speed); - - target->SetSpeedRate(MOVE_RUN, Speed); - - return true; + return false; } //Edit Player Swim Speed static bool HandleModifySwimCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - float Swim = (float)atof((char*)args); - - if (Swim > 50.0f || Swim < 0.1f) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + float swimSpeed; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + if (CheckModifySpeed(handler, args, target, swimSpeed, 0.1f, 50.0f)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; - } - - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - std::string targetNameLink = handler->GetNameLink(target); - - if (target->IsInFlight()) - { - handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str()); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_SWIM_SPEED, LANG_YOURS_SWIM_SPEED_CHANGED, swimSpeed); + target->SetSpeedRate(MOVE_SWIM, swimSpeed); + return true; } - - handler->PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, targetNameLink.c_str(), Swim); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, handler->GetNameLink().c_str(), Swim); - - target->SetSpeedRate(MOVE_SWIM, Swim); - - return true; + return false; } - //Edit Player Walk Speed + //Edit Player Backwards Walk Speed static bool HandleModifyBWalkCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - float BSpeed = (float)atof((char*)args); - - if (BSpeed > 50.0f || BSpeed < 0.1f) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + float backSpeed; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + if (CheckModifySpeed(handler, args, target, backSpeed, 0.1f, 50.0f)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; - } - - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - std::string targetNameLink = handler->GetNameLink(target); - - if (target->IsInFlight()) - { - handler->PSendSysMessage(LANG_CHAR_IN_FLIGHT, targetNameLink.c_str()); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_BACK_SPEED, LANG_YOURS_BACK_SPEED_CHANGED, backSpeed); + target->SetSpeedRate(MOVE_RUN_BACK, backSpeed); + return true; } - - handler->PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, targetNameLink.c_str(), BSpeed); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, handler->GetNameLink().c_str(), BSpeed); - - target->SetSpeedRate(MOVE_RUN_BACK, BSpeed); - - return true; + return false; } //Edit Player Fly static bool HandleModifyFlyCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - float FSpeed = (float)atof((char*)args); - - if (FSpeed > 50.0f || FSpeed < 0.1f) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + float flySpeed; Player* target = handler->getSelectedPlayerOrSelf(); - if (!target) + if (CheckModifySpeed(handler, args, target, flySpeed, 0.1f, 50.0f, false)) { - handler->SendSysMessage(LANG_NO_CHAR_SELECTED); - handler->SetSentErrorMessage(true); - return false; + NotifyModification(handler, target, LANG_YOU_CHANGE_FLY_SPEED, LANG_YOURS_FLY_SPEED_CHANGED, flySpeed); + target->SetSpeedRate(MOVE_FLIGHT, flySpeed); + return true; } - - // check online security - if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) - return false; - - handler->PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, handler->GetNameLink(target).c_str(), FSpeed); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, handler->GetNameLink().c_str(), FSpeed); - - target->SetSpeedRate(MOVE_FLIGHT, FSpeed); - - return true; + return false; } //Edit Player or Creature Scale static bool HandleModifyScaleCommand(ChatHandler* handler, const char* args) { - if (!*args) - return false; - - float Scale = (float)atof((char*)args); - if (Scale > 10.0f || Scale < 0.1f) - { - handler->SendSysMessage(LANG_BAD_VALUE); - handler->SetSentErrorMessage(true); - return false; - } - + float Scale; Unit* target = handler->getSelectedUnit(); - if (!target) + if (CheckModifySpeed(handler, args, target, Scale, 0.1f, 10.0f, false)) { - handler->SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE); - handler->SetSentErrorMessage(true); - return false; - } - - if (Player* player = target->ToPlayer()) - { - // check online security - if (handler->HasLowerSecurity(player, ObjectGuid::Empty)) - return false; - - handler->PSendSysMessage(LANG_YOU_CHANGE_SIZE, handler->GetNameLink(player).c_str(), Scale); - if (handler->needReportToTarget(player)) - ChatHandler(player->GetSession()).PSendSysMessage(LANG_YOURS_SIZE_CHANGED, handler->GetNameLink().c_str(), Scale); + NotifyModification(handler, target, LANG_YOU_CHANGE_SIZE, LANG_YOURS_SIZE_CHANGED, Scale); + target->SetObjectScale(Scale); + return true; } - - target->SetObjectScale(Scale); - - return true; + return false; } //Enable Player mount @@ -932,9 +731,7 @@ public: if (handler->HasLowerSecurity(target, ObjectGuid::Empty)) return false; - handler->PSendSysMessage(LANG_YOU_GIVE_MOUNT, handler->GetNameLink(target).c_str()); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_MOUNT_GIVED, handler->GetNameLink().c_str()); + NotifyModification(handler, target, LANG_YOU_GIVE_MOUNT, LANG_MOUNT_GIVED); target->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP); target->Mount(mId); @@ -988,10 +785,7 @@ public: TC_LOG_DEBUG("misc", handler->GetTrinityString(LANG_CURRENT_MONEY), targetMoney, moneyToAdd, newmoney); if (newmoney <= 0) { - handler->PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, handler->GetNameLink(target).c_str()); - if (handler->needReportToTarget(target)) - ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, handler->GetNameLink().c_str()); - + NotifyModification(handler, target, LANG_YOU_TAKE_ALL_MONEY, LANG_YOURS_ALL_MONEY_GONE); target->SetMoney(0); } else diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index fbd199b99db..38aa96a6a66 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -113,7 +113,7 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] = { CREATE_NAMED_ENUM(UNIT_FLAG_SERVER_CONTROLLED), CREATE_NAMED_ENUM(UNIT_FLAG_NON_ATTACKABLE), - CREATE_NAMED_ENUM(UNIT_FLAG_DISABLE_MOVE), + CREATE_NAMED_ENUM(UNIT_FLAG_REMOVE_CLIENT_CONTROL), CREATE_NAMED_ENUM(UNIT_FLAG_PVP_ATTACKABLE), CREATE_NAMED_ENUM(UNIT_FLAG_RENAME), CREATE_NAMED_ENUM(UNIT_FLAG_PREPARATION), @@ -125,7 +125,7 @@ EnumName<UnitFlags> const unitFlags[MAX_UNIT_FLAGS] = CREATE_NAMED_ENUM(UNIT_FLAG_PET_IN_COMBAT), CREATE_NAMED_ENUM(UNIT_FLAG_PVP), CREATE_NAMED_ENUM(UNIT_FLAG_SILENCED), - CREATE_NAMED_ENUM(UNIT_FLAG_UNK_14), + CREATE_NAMED_ENUM(UNIT_FLAG_CANNOT_SWIM), CREATE_NAMED_ENUM(UNIT_FLAG_UNK_15), CREATE_NAMED_ENUM(UNIT_FLAG_UNK_16), CREATE_NAMED_ENUM(UNIT_FLAG_PACIFIED), @@ -220,14 +220,15 @@ public: { "whisper", rbac::RBAC_PERM_COMMAND_NPC_WHISPER, false, &HandleNpcWhisperCommand, "" }, { "yell", rbac::RBAC_PERM_COMMAND_NPC_YELL, false, &HandleNpcYellCommand, "" }, { "tame", rbac::RBAC_PERM_COMMAND_NPC_TAME, false, &HandleNpcTameCommand, "" }, - { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, NULL, "", npcAddCommandTable }, - { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, NULL, "", npcDeleteCommandTable }, - { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, NULL, "", npcFollowCommandTable }, - { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, NULL, "", npcSetCommandTable }, + { "add", rbac::RBAC_PERM_COMMAND_NPC_ADD, false, nullptr, "", npcAddCommandTable }, + { "delete", rbac::RBAC_PERM_COMMAND_NPC_DELETE, false, nullptr, "", npcDeleteCommandTable }, + { "follow", rbac::RBAC_PERM_COMMAND_NPC_FOLLOW, false, nullptr, "", npcFollowCommandTable }, + { "set", rbac::RBAC_PERM_COMMAND_NPC_SET, false, nullptr, "", npcSetCommandTable }, + { "evade", rbac::RBAC_PERM_COMMAND_NPC_EVADE, false, &HandleNpcEvadeCommand, "" }, }; static std::vector<ChatCommand> commandTable = { - { "npc", rbac::RBAC_PERM_COMMAND_NPC, false, NULL, "", npcCommandTable }, + { "npc", rbac::RBAC_PERM_COMMAND_NPC, false, nullptr, "", npcCommandTable }, }; return commandTable; } @@ -318,17 +319,17 @@ public: uint32 itemId = item_int; - char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0 + char* fmaxcount = strtok(nullptr, " "); //add maxcount, default: 0 uint32 maxcount = 0; if (fmaxcount) maxcount = atoul(fmaxcount); - char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0 + char* fincrtime = strtok(nullptr, " "); //add incrtime, default: 0 uint32 incrtime = 0; if (fincrtime) incrtime = atoul(fincrtime); - char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0 + char* fextendedcost = strtok(nullptr, " "); //add ExtendedCost, default: 0 uint32 extendedcost = fextendedcost ? atoul(fextendedcost) : 0; Creature* vendor = handler->getSelectedCreature(); if (!vendor) @@ -361,7 +362,7 @@ public: return false; char* guidStr = strtok((char*)args, " "); - char* waitStr = strtok((char*)NULL, " "); + char* waitStr = strtok((char*)nullptr, " "); ObjectGuid::LowType lowGuid = atoi((char*)guidStr); @@ -446,36 +447,24 @@ public: } Creature* creature = handler->getSelectedCreature(); - if (!creature) + if (!creature || creature->IsPet()) { handler->SendSysMessage(LANG_SELECT_CREATURE); handler->SetSentErrorMessage(true); return false; } - if (creature->IsPet()) - { - if (((Pet*)creature)->getPetType() == HUNTER_PET) - { - creature->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, sObjectMgr->GetXPForLevel(lvl)/4); - creature->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0); - } - ((Pet*)creature)->GivePetLevel(lvl); - } - else - { - creature->SetMaxHealth(100 + 30*lvl); - creature->SetHealth(100 + 30*lvl); - creature->SetLevel(lvl); - creature->SaveToDB(); - } + creature->SetMaxHealth(100 + 30*lvl); + creature->SetHealth(100 + 30*lvl); + creature->SetLevel(lvl); + creature->SaveToDB(); return true; } static bool HandleNpcDeleteCommand(ChatHandler* handler, char const* args) { - Creature* unit = NULL; + Creature* unit = nullptr; if (*args) { @@ -628,7 +617,7 @@ public: return false; char* arg1 = strtok((char*)args, " "); - char* arg2 = strtok((char*)NULL, ""); + char* arg2 = strtok((char*)nullptr, ""); if (!arg1 || !arg2) return false; @@ -926,8 +915,8 @@ public: // later switched on/off according to special events (like escort // quests, etc) char* guid_str = strtok((char*)args, " "); - char* type_str = strtok((char*)NULL, " "); - char* dontdel_str = strtok((char*)NULL, " "); + char* type_str = strtok((char*)nullptr, " "); + char* dontdel_str = strtok((char*)nullptr, " "); bool doNotDelete = false; @@ -935,7 +924,7 @@ public: return false; ObjectGuid::LowType lowguid = 0; - Creature* creature = NULL; + Creature* creature = nullptr; if (dontdel_str) { @@ -961,7 +950,7 @@ public: { //TC_LOG_ERROR("misc", "DEBUG: type_str, NODEL "); doNotDelete = true; - type_str = NULL; + type_str = nullptr; } } } @@ -1001,7 +990,7 @@ public: } // now lowguid is low guid really existed creature - // and creature point (maybe) to this creature or NULL + // and creature point (maybe) to this creature or nullptr MovementGeneratorType move_type; @@ -1260,7 +1249,7 @@ public: } char* receiver_str = strtok((char*)args, " "); - char* text = strtok(NULL, ""); + char* text = strtok(nullptr, ""); if (!receiver_str || !text) { @@ -1319,7 +1308,16 @@ public: if (!*args) return false; - char* charID = handler->extractKeyFromLink((char*)args, "Hcreature_entry"); + bool loot = false; + char const* spawntype_str = strtok((char*)args, " "); + char const* entry_str = strtok(nullptr, ""); + if (stricmp(spawntype_str, "LOOT") == 0) + loot = true; + else if (stricmp(spawntype_str, "NOLOOT") == 0) + loot = false; + else + entry_str = args; + char* charID = handler->extractKeyFromLink((char*)entry_str, "Hcreature_entry"); if (!charID) return false; @@ -1332,7 +1330,7 @@ public: if (!sObjectMgr->GetCreatureTemplate(id)) return false; - chr->SummonCreature(id, *chr, TEMPSUMMON_CORPSE_DESPAWN, 120); + chr->SummonCreature(id, *chr, loot ? TEMPSUMMON_CORPSE_TIMED_DESPAWN : TEMPSUMMON_CORPSE_DESPAWN, 30 * IN_MILLISECONDS); return true; } @@ -1404,6 +1402,51 @@ public: return true; } + static bool HandleNpcEvadeCommand(ChatHandler* handler, char const* args) + { + Creature* creatureTarget = handler->getSelectedCreature(); + if (!creatureTarget || creatureTarget->IsPet()) + { + handler->PSendSysMessage(LANG_SELECT_CREATURE); + handler->SetSentErrorMessage(true); + return false; + } + + if (!creatureTarget->IsAIEnabled) + { + handler->PSendSysMessage(LANG_CREATURE_NOT_AI_ENABLED); + handler->SetSentErrorMessage(true); + return false; + } + + char* type_str = args ? strtok((char*)args, " ") : nullptr; + char* force_str = args ? strtok(nullptr, " ") : nullptr; + + CreatureAI::EvadeReason why = CreatureAI::EVADE_REASON_OTHER; + bool force = false; + if (type_str) + { + if (stricmp(type_str, "NO_HOSTILES") == 0 || stricmp(type_str, "EVADE_REASON_NO_HOSTILES") == 0) + why = CreatureAI::EVADE_REASON_NO_HOSTILES; + else if (stricmp(type_str, "BOUNDARY") == 0 || stricmp(type_str, "EVADE_REASON_BOUNDARY") == 0) + why = CreatureAI::EVADE_REASON_BOUNDARY; + else if (stricmp(type_str, "SEQUENCE_BREAK") == 0 || stricmp(type_str, "EVADE_REASON_SEQUENCE_BREAK") == 0) + why = CreatureAI::EVADE_REASON_SEQUENCE_BREAK; + else if (stricmp(type_str, "FORCE") == 0) + force = true; + + if (!force && force_str) + if (stricmp(force_str, "FORCE") == 0) + force = true; + } + + if (force) + creatureTarget->ClearUnitState(UNIT_STATE_EVADE); + creatureTarget->AI()->EnterEvadeMode(why); + + return true; + } + static bool HandleNpcAddFormationCommand(ChatHandler* handler, char const* args) { if (!*args) @@ -1515,7 +1558,7 @@ public: if (!pSlotID) return false; - char* pItemID = strtok(NULL, " "); + char* pItemID = strtok(nullptr, " "); if (!pItemID) return false; diff --git a/src/server/scripts/Commands/cs_pet.cpp b/src/server/scripts/Commands/cs_pet.cpp index 4f0a179142d..a86de766117 100644 --- a/src/server/scripts/Commands/cs_pet.cpp +++ b/src/server/scripts/Commands/cs_pet.cpp @@ -22,6 +22,19 @@ #include "ObjectMgr.h" #include "ScriptMgr.h" +static inline Pet* GetSelectedPlayerPetOrOwn(ChatHandler* handler) +{ + if (Unit* target = handler->getSelectedUnit()) + { + if (target->GetTypeId() == TYPEID_PLAYER) + return target->ToPlayer()->GetPet(); + if (target->IsPet()) + return target->ToPet(); + return nullptr; + } + Player* player = handler->GetSession()->GetPlayer(); + return player ? player->GetPet() : nullptr; +} class pet_commandscript : public CommandScript { public: @@ -34,6 +47,7 @@ public: { "create", rbac::RBAC_PERM_COMMAND_PET_CREATE, false, &HandlePetCreateCommand, "" }, { "learn", rbac::RBAC_PERM_COMMAND_PET_LEARN, false, &HandlePetLearnCommand, "" }, { "unlearn", rbac::RBAC_PERM_COMMAND_PET_UNLEARN, false, &HandlePetUnlearnCommand, "" }, + { "level", rbac::RBAC_PERM_COMMAND_PET_LEVEL, false, &HandlePetLevelCommand, "" }, }; static std::vector<ChatCommand> commandTable = @@ -54,11 +68,11 @@ public: return false; } - CreatureTemplate const* creatrueTemplate = creatureTarget->GetCreatureTemplate(); - // Creatures with family 0 crashes the server - if (!creatrueTemplate->family) + CreatureTemplate const* creatureTemplate = creatureTarget->GetCreatureTemplate(); + // Creatures with family CREATURE_FAMILY_NONE crashes the server + if (creatureTemplate->family == CREATURE_FAMILY_NONE) { - handler->PSendSysMessage("This creature cannot be tamed. (family id: 0)."); + handler->PSendSysMessage("This creature cannot be tamed. Family id: 0 (CREATURE_FAMILY_NONE)."); handler->SetSentErrorMessage(true); return false; } @@ -119,12 +133,11 @@ public: if (!*args) return false; - Player* player = handler->GetSession()->GetPlayer(); - Pet* pet = player->GetPet(); + Pet* pet = GetSelectedPlayerPetOrOwn(handler); if (!pet) { - handler->PSendSysMessage("You have no pet"); + handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET); handler->SetSentErrorMessage(true); return false; } @@ -162,11 +175,10 @@ public: if (!*args) return false; - Player* player = handler->GetSession()->GetPlayer(); - Pet* pet = player->GetPet(); + Pet* pet = GetSelectedPlayerPetOrOwn(handler); if (!pet) { - handler->PSendSysMessage("You have no pet"); + handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET); handler->SetSentErrorMessage(true); return false; } @@ -180,6 +192,37 @@ public: return true; } + + static bool HandlePetLevelCommand(ChatHandler* handler, char const* args) + { + Pet* pet = GetSelectedPlayerPetOrOwn(handler); + Player* owner = pet ? pet->GetOwner() : nullptr; + if (!pet || !owner) + { + handler->SendSysMessage(LANG_SELECT_PLAYER_OR_PET); + handler->SetSentErrorMessage(true); + return false; + } + + int32 level = args ? atoi(args) : 0; + if (level == 0) + level = owner->getLevel() - pet->getLevel(); + if (level == 0 || level < -STRONG_MAX_LEVEL || level > STRONG_MAX_LEVEL) + { + handler->SendSysMessage(LANG_BAD_VALUE); + handler->SetSentErrorMessage(true); + return false; + } + + int32 newLevel = pet->getLevel() + level; + if (newLevel < 1) + newLevel = 1; + else if (newLevel > owner->getLevel()) + newLevel = owner->getLevel(); + + pet->GivePetLevel(newLevel); + return true; + } }; void AddSC_pet_commandscript() diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp index eb28a8adae4..4470fa7de42 100644 --- a/src/server/scripts/Commands/cs_reload.cpp +++ b/src/server/scripts/Commands/cs_reload.cpp @@ -91,7 +91,7 @@ public: { "disenchant_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_DISENCHANT_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesDisenchantCommand, "" }, { "event_scripts", rbac::RBAC_PERM_COMMAND_RELOAD_EVENT_SCRIPTS, true, &HandleReloadEventScriptsCommand, "" }, { "fishing_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_FISHING_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesFishingCommand, "" }, - { "game_graveyard_zone", rbac::RBAC_PERM_COMMAND_RELOAD_GAME_GRAVEYARD_ZONE, true, &HandleReloadGameGraveyardZoneCommand, "" }, + { "graveyard_zone", rbac::RBAC_PERM_COMMAND_RELOAD_GRAVEYARD_ZONE, true, &HandleReloadGameGraveyardZoneCommand, "" }, { "game_tele", rbac::RBAC_PERM_COMMAND_RELOAD_GAME_TELE, true, &HandleReloadGameTeleCommand, "" }, { "gameobject_questender", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUESTENDER, true, &HandleReloadGOQuestEnderCommand, "" }, { "gameobject_loot_template", rbac::RBAC_PERM_COMMAND_RELOAD_GAMEOBJECT_QUEST_LOOT_TEMPLATE, true, &HandleReloadLootTemplatesGameobjectCommand, "" }, diff --git a/src/server/scripts/Commands/cs_server.cpp b/src/server/scripts/Commands/cs_server.cpp index 83bc2e47674..e0e52d4e1f5 100644 --- a/src/server/scripts/Commands/cs_server.cpp +++ b/src/server/scripts/Commands/cs_server.cpp @@ -29,6 +29,7 @@ EndScriptData */ #include "Player.h" #include "ScriptMgr.h" #include "GitRevision.h" +#include "Util.h" class server_commandscript : public CommandScript { @@ -52,12 +53,14 @@ public: static std::vector<ChatCommand> serverRestartCommandTable = { { "cancel", rbac::RBAC_PERM_COMMAND_SERVER_RESTART_CANCEL, true, &HandleServerShutDownCancelCommand, "" }, + { "force", rbac::RBAC_PERM_COMMAND_SERVER_RESTART_FORCE, true, &HandleServerForceRestartCommand, "" }, { "" , rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, &HandleServerRestartCommand, "" }, }; static std::vector<ChatCommand> serverShutdownCommandTable = { { "cancel", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN_CANCEL, true, &HandleServerShutDownCancelCommand, "" }, + { "force", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN_FORCE, true, &HandleServerForceShutDownCommand, "" }, { "" , rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, &HandleServerShutDownCommand, "" }, }; @@ -73,19 +76,19 @@ public: { { "corpses", rbac::RBAC_PERM_COMMAND_SERVER_CORPSES, true, &HandleServerCorpsesCommand, "" }, { "exit", rbac::RBAC_PERM_COMMAND_SERVER_EXIT, true, &HandleServerExitCommand, "" }, - { "idlerestart", rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART, true, NULL, "", serverIdleRestartCommandTable }, - { "idleshutdown", rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN, true, NULL, "", serverIdleShutdownCommandTable }, + { "idlerestart", rbac::RBAC_PERM_COMMAND_SERVER_IDLERESTART, true, nullptr, "", serverIdleRestartCommandTable }, + { "idleshutdown", rbac::RBAC_PERM_COMMAND_SERVER_IDLESHUTDOWN, true, nullptr, "", serverIdleShutdownCommandTable }, { "info", rbac::RBAC_PERM_COMMAND_SERVER_INFO, true, &HandleServerInfoCommand, "" }, { "motd", rbac::RBAC_PERM_COMMAND_SERVER_MOTD, true, &HandleServerMotdCommand, "" }, { "plimit", rbac::RBAC_PERM_COMMAND_SERVER_PLIMIT, true, &HandleServerPLimitCommand, "" }, - { "restart", rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, NULL, "", serverRestartCommandTable }, - { "shutdown", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, NULL, "", serverShutdownCommandTable }, - { "set", rbac::RBAC_PERM_COMMAND_SERVER_SET, true, NULL, "", serverSetCommandTable }, + { "restart", rbac::RBAC_PERM_COMMAND_SERVER_RESTART, true, nullptr, "", serverRestartCommandTable }, + { "shutdown", rbac::RBAC_PERM_COMMAND_SERVER_SHUTDOWN, true, nullptr, "", serverShutdownCommandTable }, + { "set", rbac::RBAC_PERM_COMMAND_SERVER_SET, true, nullptr, "", serverSetCommandTable }, }; static std::vector<ChatCommand> commandTable = { - { "server", rbac::RBAC_PERM_COMMAND_SERVER, true, NULL, "", serverCommandTable }, + { "server", rbac::RBAC_PERM_COMMAND_SERVER, true, nullptr, "", serverCommandTable }, }; return commandTable; } @@ -192,19 +195,34 @@ public: return true; } - static bool HandleServerShutDownCommand(ChatHandler* /*handler*/, char const* args) + static inline bool IsOnlyUser(WorldSession* mySession) { - return ShutdownServer(args, 0, SHUTDOWN_EXIT_CODE); + // check if there is any session connected from a different address + std::string myAddr = mySession ? mySession->GetRemoteAddress() : ""; + SessionMap const& sessions = sWorld->GetAllSessions(); + for (SessionMap::value_type const& session : sessions) + if (session.second && myAddr != session.second->GetRemoteAddress()) + return false; + return true; + } + static bool HandleServerShutDownCommand(ChatHandler* handler, char const* args) + { + return ShutdownServer(args, IsOnlyUser(handler->GetSession()) ? SHUTDOWN_MASK_FORCE : 0, SHUTDOWN_EXIT_CODE); } - static bool HandleServerRestartCommand(ChatHandler* /*handler*/, char const* args) + static bool HandleServerRestartCommand(ChatHandler* handler, char const* args) { - return ShutdownServer(args, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE); + return ShutdownServer(args, IsOnlyUser(handler->GetSession()) ? (SHUTDOWN_MASK_FORCE | SHUTDOWN_MASK_RESTART) : SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE); } - static bool HandleServerIdleRestartCommand(ChatHandler* /*handler*/, char const* args) + static bool HandleServerForceShutDownCommand(ChatHandler* /*handler*/, char const* args) { - return ShutdownServer(args, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, RESTART_EXIT_CODE); + return ShutdownServer(args, SHUTDOWN_MASK_FORCE, SHUTDOWN_EXIT_CODE); + } + + static bool HandleServerForceRestartCommand(ChatHandler* /*handler*/, char const* args) + { + return ShutdownServer(args, SHUTDOWN_MASK_FORCE | SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE); } static bool HandleServerIdleShutDownCommand(ChatHandler* /*handler*/, char const* args) @@ -212,6 +230,11 @@ public: return ShutdownServer(args, SHUTDOWN_MASK_IDLE, SHUTDOWN_EXIT_CODE); } + static bool HandleServerIdleRestartCommand(ChatHandler* /*handler*/, char const* args) + { + return ShutdownServer(args, SHUTDOWN_MASK_RESTART | SHUTDOWN_MASK_IDLE, RESTART_EXIT_CODE); + } + // Exit the realm static bool HandleServerExitCommand(ChatHandler* handler, char const* /*args*/) { @@ -256,8 +279,8 @@ public: return false; char* type = strtok((char*)args, " "); - char* name = strtok(NULL, " "); - char* level = strtok(NULL, " "); + char* name = strtok(nullptr, " "); + char* level = strtok(nullptr, " "); if (!type || !name || !level || *name == '\0' || *level == '\0' || (*type != 'a' && *type != 'l')) return false; @@ -313,10 +336,26 @@ private: return false; // #delay [#exit_code] [reason] + int32 delay = 0; char* delayStr = strtok((char*)args, " "); - if (!delayStr || !isNumeric(delayStr)) + if (!delayStr) return false; + if (isNumeric(delayStr)) + { + delay = atoi(delayStr); + // Prevent interpret wrong arg value as 0 secs shutdown time + if ((delay == 0 && (delayStr[0] != '0' || delayStr[1] != '\0')) || delay < 0) + return false; + } + else + { + delay = TimeStringToSecs(std::string(delayStr)); + + if (delay == 0) + return false; + } + char* exitCodeStr = nullptr; char reason[256] = { 0 }; @@ -337,17 +376,14 @@ private: } } - int32 delay = atoi(delayStr); - - // Prevent interpret wrong arg value as 0 secs shutdown time - if ((delay == 0 && (delayStr[0] != '0' || delayStr[1] != '\0')) || delay < 0) - return false; - int32 exitCode = defaultExitCode; if (exitCodeStr) if (!ParseExitCode(exitCodeStr, exitCode)) return false; + if (delay < (int32)sWorld->getIntConfig(CONFIG_FORCE_SHUTDOWN_THRESHOLD) && !(shutdownMask & SHUTDOWN_MASK_FORCE)) + return false; + sWorld->ShutdownServ(delay, shutdownMask, static_cast<uint8>(exitCode), std::string(reason)); return true; diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp index f7a1c18c234..734b70933aa 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp @@ -894,12 +894,12 @@ public: if (instance->GetBossState(DATA_HORSEMAN_EVENT) == IN_PROGRESS) return false; - player->AreaExploredOrEventHappens(11405); - if (Creature* horseman = go->SummonCreature(HH_MOUNTED, FlightPoint[20].x, FlightPoint[20].y, FlightPoint[20].z, 0, TEMPSUMMON_MANUAL_DESPAWN, 0)) - { - ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->PlayerGUID = player->GetGUID(); - ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->FlyMode(); - } + player->AreaExploredOrEventHappens(11405); + if (Creature* horseman = go->SummonCreature(HH_MOUNTED, FlightPoint[20].x, FlightPoint[20].y, FlightPoint[20].z, 0, TEMPSUMMON_MANUAL_DESPAWN, 0)) + { + ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->PlayerGUID = player->GetGUID(); + ENSURE_AI(boss_headless_horseman::boss_headless_horsemanAI, horseman->AI())->FlyMode(); + } return true; } }; diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp index d86b3707606..7563a880f0a 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp @@ -143,11 +143,7 @@ enum Spells SPELL_RING_OF_BLUE_FLAMES = 45825 //Cast this spell when the go is activated }; -/*** Error messages ***/ -#define ERROR_KJ_NOT_SUMMONED "TSCR ERROR: Unable to summon Kil'Jaeden for some reason" - /*** Others ***/ -#define FLOOR_Z 28.050388f #define SHIELD_ORB_Z 45.000f enum Phase diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp index 04d23cd7d4e..6c84677708e 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp @@ -15,88 +15,119 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Muru -SD%Complete: 80 -SDComment: all sounds, black hole effect triggers to often (46228) -*/ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "sunwell_plateau.h" -#include "Player.h" -#include "SpellInfo.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" -// Muru & Entropius's spells enum Spells { - SPELL_ENRAGE = 26662, - // Muru's spells - SPELL_NEGATIVE_ENERGY = 46009, //(this trigger 46008) - SPELL_DARKNESS = 45999, - SPELL_OPEN_ALL_PORTALS = 46177, - SPELL_OPEN_PORTAL = 45977, - SPELL_OPEN_PORTAL_2 = 45976, - SPELL_SUMMON_BERSERKER = 46037, - SPELL_SUMNON_FURY_MAGE = 46038, - SPELL_SUMMON_VOID_SENTINEL = 45988, - SPELL_SUMMON_ENTROPIUS = 46217, + SPELL_OPEN_PORTAL_PERIODIC = 45994, + SPELL_DARKNESS_PERIODIC = 45998, + SPELL_NEGATIVE_ENERGY_PERIODIC = 46009, + SPELL_SUMMON_VOID_SPAWN = 46071, + SPELL_SUMMON_BLOOD_ELVES_SCRIPT = 46050, + SPELL_SUMMON_BLOOD_ELVES_PERIODIC = 46041, + SPELL_OPEN_ALL_PORTALS = 46177, + SPELL_SUMMON_ENTROPIUS = 46217, + SPELL_ENRAGE = 26662, + SPELL_SUMMON_DARK_FIEND_0 = 46000, + SPELL_SUMMON_DARK_FIEND_1 = 46001, + SPELL_SUMMON_DARK_FIEND_2 = 46002, + SPELL_SUMMON_DARK_FIEND_3 = 46003, + SPELL_SUMMON_DARK_FIEND_4 = 46004, + SPELL_SUMMON_DARK_FIEND_5 = 46005, + SPELL_SUMMON_DARK_FIEND_6 = 46006, + SPELL_SUMMON_DARK_FIEND_7 = 46007, + SPELL_SUMMON_BERSERKER = 46037, + SPELL_SUMMON_BERSERKER_2 = 46040, + SPELL_SUMMON_FURY_MAGE = 46038, + SPELL_SUMMON_FURY_MAGE_2 = 46039, // Entropius's spells - SPELL_DARKNESS_E = 46269, - SPELL_BLACKHOLE = 46282, - SPELL_NEGATIVE_ENERGY_E = 46284, - SPELL_ENTROPIUS_SPAWN = 46223, - - // Shadowsword Berserker's spells - SPELL_FLURRY = 46160, - SPELL_DUAL_WIELD = 29651, + SPELL_ENTROPIUS_COSMETIC_SPAWN = 46223, + SPELL_DARKNESS_E = 46269, + SPELL_NEGATIVE_ENERGY_PERIODIC_E = 46284, + SPELL_BLACKHOLE = 46282, + SPELL_SUMMON_DARKFIEND_E = 46263, + + // Myruu's Portal Target + SPELL_SUMMON_VOID_SENTINEL_SUMMONER = 45978, + SPELL_SUMMON_VOID_SENTINEL_SUMMONER_VISUAL = 45989, + SPELL_SUMMON_VOID_SENTINEL = 45988, + SPELL_TRANSFORM_VISUAL_MISSILE = 46205, + TRANSFORM_VISUAL_MISSILE_1 = 46208, + TRANSFORM_VISUAL_MISSILE_2 = 46178, + SPELL_OPEN_PORTAL = 45977, + SPELL_OPEN_PORTAL_2 = 45976, - // Shadowsword Fury Mage's spells - SPELL_FEL_FIREBALL = 46101, - SPELL_SPELL_FURY = 46102, + //Dark Fiend Spells + SPELL_DARKFIEND_DAMAGE = 45944, + SPELL_DARKFIEND_VISUAL = 45936, + SPELL_DARKFIEND_SKIN = 45934, // Void Sentinel's spells - SPELL_SHADOW_PULSE = 46087, - SPELL_VOID_BLAST = 46161, + SPELL_SHADOW_PULSE_PERIODIC = 46086, + SPELL_VOID_BLAST = 46161, - // Void Spawn's spells - SPELL_SHADOW_BOLT_VOLLEY = 46082, + //Black Hole Spells + SPELL_BLACKHOLE_SUMMON_VISUAL = 46242, + SPELL_BLACKHOLE_SUMMON_VISUAL_2 = 46247, + SPELL_BLACKHOLE_PASSIVE = 46228, + SPELL_BLACK_HOLE_VISUAL_2 = 46235 +}; - //Dark Fiend Spells - SPELL_DARKFIEND_AOE = 45944, - SPELL_DARKFIEND_VISUAL = 45936, - SPELL_DARKFIEND_SKIN = 45934, +enum Phases +{ + PHASE_ONE = 1, + PHASE_TWO = 2 +}; - //Black Hole Spells - SPELL_BLACKHOLE_SPAWN = 46242, - SPELL_BLACKHOLE_GROW = 46228 +enum Misc +{ + MAX_VOID_SPAWNS = 6, + MAX_SUMMON_BLOOD_ELVES = 4, + MAX_SUMMON_DARK_FIEND = 8 }; -enum Events +uint32 const SummonDarkFiendSpells[MAX_SUMMON_DARK_FIEND] = { - // M'uru - EVENT_DARKNESS = 1, - EVENT_SUMMON_HUMANOIDS, - EVENT_SUMMON_SENTINEL, - EVENT_PHASE_TRANSITION, // Delayed phase transition. - EVENT_ENRAGE, - - // Entropius - EVENT_SUMMON_BLACK_HOLE + SPELL_SUMMON_DARK_FIEND_0, + SPELL_SUMMON_DARK_FIEND_1, + SPELL_SUMMON_DARK_FIEND_2, + SPELL_SUMMON_DARK_FIEND_3, + SPELL_SUMMON_DARK_FIEND_4, + SPELL_SUMMON_DARK_FIEND_5, + SPELL_SUMMON_DARK_FIEND_6, + SPELL_SUMMON_DARK_FIEND_7 }; -enum Phases +uint32 const SummonBloodElvesSpells[MAX_SUMMON_BLOOD_ELVES] = { - PHASE_ONE = 1, - PHASE_TWO, + SPELL_SUMMON_BERSERKER, + SPELL_SUMMON_BERSERKER_2, + SPELL_SUMMON_FURY_MAGE, + SPELL_SUMMON_FURY_MAGE_2 }; -enum CreatureGroups +class VoidSpawnSummon : public BasicEvent { - CREATURE_GROUP_HUMANOIDS, - CREATURE_GROUP_DARKFIENDS + public: + explicit VoidSpawnSummon(Creature* owner) + : _owner(owner) + { + } + + bool Execute(uint64 /*time*/, uint32 /*diff*/) + { + _owner->CastSpell((Unit*)nullptr, SPELL_SUMMON_VOID_SENTINEL, true); + return true; + } + + private: + Creature* _owner; }; class boss_entropius : public CreatureScript @@ -110,53 +141,78 @@ public: void Reset() override { - DoCastAOE(SPELL_NEGATIVE_ENERGY_E); + _Reset(); + DoCast(me, SPELL_ENTROPIUS_COSMETIC_SPAWN, true); } - void EnterCombat(Unit* /*who*/) override + void ScheduleTasks() override { - _EnterCombat(); - DoCastAOE(SPELL_NEGATIVE_ENERGY_E, true); - DoCast(me, SPELL_ENTROPIUS_SPAWN); - events.ScheduleEvent(EVENT_SUMMON_BLACK_HOLE, 15000); + scheduler.Schedule(Milliseconds(2000), [this](TaskContext /*context*/) + { + DoResetPortals(); + DoCastAOE(SPELL_NEGATIVE_ENERGY_PERIODIC_E, true); + }); + + scheduler.Schedule(Seconds(15), [this](TaskContext context) + { + DoCastAOE(SPELL_DARKNESS_E, true); + DoCastAOE(SPELL_BLACKHOLE, true); + + context.Repeat(); + }); } - void JustSummoned(Creature* summoned) override + void JustSummoned(Creature* summon) override { - switch (summoned->GetEntry()) + switch (summon->GetEntry()) { case NPC_DARK_FIENDS: - summoned->CastSpell(summoned, SPELL_DARKFIEND_VISUAL); + summon->CastSpell(summon, SPELL_DARKFIEND_VISUAL); break; case NPC_DARKNESS: - summoned->AddUnitState(UNIT_STATE_STUNNED); - float x, y, z, o; - summoned->GetHomePosition(x, y, z, o); - me->SummonCreature(NPC_DARK_FIENDS, x, y, z, o, TEMPSUMMON_CORPSE_DESPAWN, 0); + summon->SetReactState(REACT_PASSIVE); + summon->CastSpell(summon, SPELL_BLACKHOLE); + summon->CastSpell(summon, SPELL_SUMMON_DARKFIEND_E, true); break; } - summoned->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0, 50, true)); - summons.Summon(summoned); + summons.Summon(summon); } - void ExecuteEvent(uint32 eventId) override + void EnterEvadeMode(EvadeReason /*why*/) override { - if (eventId == EVENT_SUMMON_BLACK_HOLE) - { - if (Unit* random = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - DoCast(random, SPELL_DARKNESS_E); - if (Unit* random = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)) - random->CastSpell(random, SPELL_BLACKHOLE); - events.ScheduleEvent(EVENT_SUMMON_BLACK_HOLE, 15000); - } + if (Creature* muru = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MURU))) + muru->AI()->EnterEvadeMode(); + + DoResetPortals(); + summons.DespawnAll(); + me->DespawnOrUnsummon(); } - void EnterEvadeMode(EvadeReason /*why*/) override + void JustDied(Unit* /*killer*/) override { + _JustDied(); + if (Creature* muru = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_MURU))) - muru->AI()->Reset(); // Reset encounter. - me->DisappearAndDie(); - summons.DespawnAll(); + muru->DisappearAndDie(); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + scheduler.Update(diff, [this] + { + DoMeleeAttackIfReady(); + }); + } + + void DoResetPortals() + { + std::list<Creature*> portals; + me->GetCreatureListWithEntryInGrid(portals, NPC_MURU_PORTAL_TARGET, 100.0f); + for (Creature* portal : portals) + portal->RemoveAllAuras(); } }; @@ -181,101 +237,96 @@ public: void Initialize() { - DarkFiend = false; - HasEnraged = false; - EntropiusGUID.Clear(); + _hasEnraged = false; + _phase = PHASE_ONE; + _entropiusGUID.Clear(); } void Reset() override { - Initialize(); _Reset(); + Initialize(); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); me->SetVisible(true); } + void EnterEvadeMode(EvadeReason /*why*/) override + { + BossAI::EnterEvadeMode(); + if (Creature* entropius = ObjectAccessor::GetCreature(*me, _entropiusGUID)) + entropius->AI()->EnterEvadeMode(); + } + + void ScheduleTasks() override + { + scheduler.Schedule(Minutes(10), [this](TaskContext /*context*/) + { + if (Creature* entropius = ObjectAccessor::GetCreature(*me, _entropiusGUID)) + entropius->CastSpell(entropius, SPELL_ENRAGE); + DoCast(me, SPELL_ENRAGE); + _hasEnraged = true; + }); + + scheduler.Schedule(Seconds(10), [this](TaskContext /*context*/) + { + DoCast(me, SPELL_SUMMON_BLOOD_ELVES_SCRIPT, true); + DoCast(me, SPELL_SUMMON_BLOOD_ELVES_PERIODIC, true); + }); + } + void EnterCombat(Unit* /*who*/) override { _EnterCombat(); - events.SetPhase(PHASE_ONE); - events.ScheduleEvent(EVENT_ENRAGE, 600000); - events.ScheduleEvent(EVENT_DARKNESS, 45000, 0, PHASE_ONE); - events.ScheduleEvent(EVENT_SUMMON_HUMANOIDS, 10000, 0, PHASE_ONE); - events.ScheduleEvent(EVENT_SUMMON_SENTINEL, 31500, 0, PHASE_ONE); - DoCastAOE(SPELL_NEGATIVE_ENERGY); + DoCast(me, SPELL_OPEN_PORTAL_PERIODIC, true); + DoCast(me, SPELL_DARKNESS_PERIODIC, true); + DoCast(me, SPELL_NEGATIVE_ENERGY_PERIODIC, true); } void DamageTaken(Unit* /*done_by*/, uint32 &damage) override { - if (damage >= me->GetHealth() && events.IsInPhase(PHASE_ONE)) + if (damage >= me->GetHealth()) { - damage = 0; + damage = me->GetHealth() - 1; + if (_phase != PHASE_ONE) + return; + + _phase = PHASE_TWO; me->RemoveAllAuras(); - DoCast(me, SPELL_OPEN_ALL_PORTALS); + DoCast(me, SPELL_OPEN_ALL_PORTALS, true); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - events.SetPhase(PHASE_TWO); - events.ScheduleEvent(EVENT_PHASE_TRANSITION, 2000); + + scheduler.Schedule(Seconds(6), [this](TaskContext /*context*/) + { + DoCast(me, SPELL_SUMMON_ENTROPIUS, true); + }); } } - void JustSummoned(Creature* summoned) override + void JustSummoned(Creature* summon) override { - switch (summoned->GetEntry()) + if (summon->GetEntry() == NPC_ENTROPIUS) { - case NPC_ENTROPIUS: - me->SetVisible(false); - EntropiusGUID = summoned->GetGUID(); - if (HasEnraged) // If we hit phase transition while enraged, enrage Entropius as well. - summoned->CastSpell(summoned, SPELL_ENRAGE); - break; - case NPC_DARK_FIENDS: - summoned->CastSpell(summoned, SPELL_DARKFIEND_VISUAL); - break; + me->SetVisible(false); + _entropiusGUID = summon->GetGUID(); + if (_hasEnraged) + summon->CastSpell(summon, SPELL_ENRAGE, true); + return; } - summoned->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0, 50, true)); - summons.Summon(summoned); + BossAI::JustSummoned(summon); } - void ExecuteEvent(uint32 eventId) override + void UpdateAI(uint32 diff) override { - switch (eventId) - { - case EVENT_DARKNESS: - if (!DarkFiend) - { - DarkFiend = true; - DoCastAOE(SPELL_DARKNESS); - } - else - me->SummonCreatureGroup(CREATURE_GROUP_DARKFIENDS); - events.ScheduleEvent(EVENT_DARKNESS, DarkFiend ? 3000 : 42000, 0, PHASE_ONE); - break; - case EVENT_SUMMON_HUMANOIDS: - me->SummonCreatureGroup(CREATURE_GROUP_HUMANOIDS); - events.ScheduleEvent(EVENT_SUMMON_HUMANOIDS, 60000, 0, PHASE_ONE); - break; - case EVENT_SUMMON_SENTINEL: - DoCastAOE(SPELL_OPEN_PORTAL_2); - events.ScheduleEvent(EVENT_SUMMON_SENTINEL, 30000, 0, PHASE_ONE); - break; - case EVENT_PHASE_TRANSITION: - DoCast(me, SPELL_SUMMON_ENTROPIUS); - break; - case EVENT_ENRAGE: - if (Creature* entropius = ObjectAccessor::GetCreature(*me, EntropiusGUID)) - entropius->CastSpell(entropius, SPELL_ENRAGE); - DoCast(me, SPELL_ENRAGE); - HasEnraged = true; - break; - default: - break; - } + if (!UpdateVictim()) + return; + + scheduler.Update(diff); } private: - bool DarkFiend; - bool HasEnraged; - ObjectGuid EntropiusGUID; + ObjectGuid _entropiusGUID; + bool _hasEnraged; + uint8 _phase; }; CreatureAI* GetAI(Creature* creature) const override @@ -289,89 +340,50 @@ class npc_muru_portal : public CreatureScript public: npc_muru_portal() : CreatureScript("npc_muru_portal") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_muru_portalAI>(creature); - } - struct npc_muru_portalAI : public ScriptedAI { - npc_muru_portalAI(Creature* creature) : ScriptedAI(creature), Summons(creature) - { - Initialize(); - SetCombatMovement(false); - instance = creature->GetInstanceScript(); - } - - void Initialize() - { - SummonTimer = 5000; - - InAction = false; - SummonSentinel = false; - } - - InstanceScript* instance; - - SummonList Summons; - - bool SummonSentinel; - bool InAction; - - uint32 SummonTimer; - - void Reset() override - { - Initialize(); + npc_muru_portalAI(Creature* creature) : ScriptedAI(creature) { } - me->AddUnitState(UNIT_STATE_STUNNED); - - Summons.DespawnAll(); - } - - void JustSummoned(Creature* summoned) override + void JustSummoned(Creature* summon) override { - if (Player* target = ObjectAccessor::GetPlayer(*me, instance->GetGuidData(DATA_PLAYER_GUID))) - summoned->AI()->AttackStart(target); + DoCast(summon, SPELL_SUMMON_VOID_SENTINEL_SUMMONER_VISUAL, true); - Summons.Summon(summoned); + summon->m_Events.AddEvent(new VoidSpawnSummon(summon), summon->m_Events.CalculateTime(1500)); } - void SpellHit(Unit* /*caster*/, const SpellInfo* Spell) override + void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->NearTeleportTo(x, y, z, o); - InAction = true; - switch (Spell->Id) + switch (spell->Id) { case SPELL_OPEN_ALL_PORTALS: - DoCastAOE(SPELL_OPEN_PORTAL); + DoCastAOE(SPELL_OPEN_PORTAL, true); + DoCastAOE(SPELL_TRANSFORM_VISUAL_MISSILE, true); break; case SPELL_OPEN_PORTAL_2: - DoCastAOE(SPELL_OPEN_PORTAL); - SummonSentinel = true; + DoCastAOE(SPELL_OPEN_PORTAL, true); + _scheduler.Schedule(Seconds(6), [this](TaskContext /*context*/) + { + DoCastAOE(SPELL_SUMMON_VOID_SENTINEL_SUMMONER, true); + }); + break; + default: break; } } void UpdateAI(uint32 diff) override { - if (!SummonSentinel) - { - if (InAction && instance->GetBossState(DATA_MURU) == NOT_STARTED) - Reset(); - return; - } - - if (SummonTimer <= diff) - { - DoCastAOE(SPELL_SUMMON_VOID_SENTINEL, false); - SummonTimer = 5000; - SummonSentinel = false; - } else SummonTimer -= diff; + _scheduler.Update(diff); } + + private: + TaskScheduler _scheduler; }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetSunwellPlateauAI<npc_muru_portalAI>(creature); + } }; class npc_dark_fiend : public CreatureScript @@ -379,11 +391,6 @@ class npc_dark_fiend : public CreatureScript public: npc_dark_fiend() : CreatureScript("npc_dark_fiend") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_dark_fiendAI(creature); - } - struct npc_dark_fiendAI : public ScriptedAI { npc_dark_fiendAI(Creature* creature) : ScriptedAI(creature) @@ -393,54 +400,56 @@ public: void Initialize() { - WaitTimer = 2000; - InAction = false; - } + me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); + me->SetReactState(REACT_PASSIVE); + DoCast(me, SPELL_DARKFIEND_SKIN, true); - uint32 WaitTimer; - bool InAction; + _scheduler.Schedule(Seconds(2), [this](TaskContext /*context*/) + { + me->SetReactState(REACT_AGGRESSIVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - void Reset() override - { - Initialize(); + if (Creature* _summoner = ObjectAccessor::GetCreature(*me, _summonerGUID)) + if (Unit* target = _summoner->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) + AttackStart(target); + }); - me->AddUnitState(UNIT_STATE_STUNNED); + _scheduler.Schedule(Seconds(3), [this](TaskContext context) + { + if (me->IsWithinDist(me->GetVictim(), 5.0f) && me->HasAura(SPELL_DARKFIEND_SKIN)) + { + DoCastAOE(SPELL_DARKFIEND_DAMAGE, false); + me->DisappearAndDie(); + } + + context.Repeat(Milliseconds(500)); + }); } - void SpellHit(Unit* /*caster*/, const SpellInfo* Spell) override + void IsSummonedBy(Unit* summoner) override { - for (uint8 i = 0; i < 3; ++i) - if (Spell->Effects[i].Effect == 38) - me->DisappearAndDie(); + _summonerGUID = summoner->GetGUID(); } - void UpdateAI(uint32 diff) override + bool CanAIAttack(Unit const* /*target*/) const override { - if (!UpdateVictim()) - return; + return me->HasAura(SPELL_DARKFIEND_SKIN); + } - if (WaitTimer <= diff) - { - if (!InAction) - { - me->ClearUnitState(UNIT_STATE_STUNNED); - DoCastAOE(SPELL_DARKFIEND_SKIN, false); - AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true)); - InAction = true; - WaitTimer = 500; - } - else - { - if (me->IsWithinDist(me->GetVictim(), 5)) - { - DoCastAOE(SPELL_DARKFIEND_AOE, false); - me->DisappearAndDie(); - } - WaitTimer = 500; - } - } else WaitTimer -= diff; + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); } + + private: + TaskScheduler _scheduler; + ObjectGuid _summonerGUID; }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetSunwellPlateauAI<npc_dark_fiendAI>(creature); + } }; class npc_void_sentinel : public CreatureScript @@ -448,63 +457,54 @@ class npc_void_sentinel : public CreatureScript public: npc_void_sentinel() : CreatureScript("npc_void_sentinel") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_void_sentinelAI(creature); - } - struct npc_void_sentinelAI : public ScriptedAI { npc_void_sentinelAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); + _instance = me->GetInstanceScript(); } - void Initialize() + void IsSummonedBy(Unit* /*summoner*/) override { - PulseTimer = 3000; - VoidBlastTimer = 45000; //is this a correct timer? + if (Creature* muru = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_MURU))) + muru->AI()->JustSummoned(me); } - uint32 PulseTimer; - uint32 VoidBlastTimer; - - void Reset() override + void EnterCombat(Unit* /*who*/) override { - Initialize(); + DoCast(me, SPELL_SHADOW_PULSE_PERIODIC, true); - float x, y, z, o; - me->GetHomePosition(x, y, z, o); - me->NearTeleportTo(x, y, 71, o); + _scheduler.Schedule(Seconds(45), [this](TaskContext context) + { + DoCastVictim(SPELL_VOID_BLAST, false); + + context.Repeat(); + }); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { - for (uint8 i = 0; i < 8; ++i) - if (Creature* temp = me->SummonCreature(NPC_VOID_SPAWN, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), float(rand32() % 6), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 180000)) - temp->AI()->AttackStart(killer); + for (uint8 i = 0; i < MAX_VOID_SPAWNS; ++i) + DoCastAOE(SPELL_SUMMON_VOID_SPAWN, true); } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) - return; - - if (PulseTimer <= diff) - { - DoCastAOE(SPELL_SHADOW_PULSE, true); - PulseTimer = 3000; - } else PulseTimer -= diff; - - if (VoidBlastTimer <= diff) + _scheduler.Update(diff, [this] { - DoCastVictim(SPELL_VOID_BLAST, false); - VoidBlastTimer = 45000; - } else VoidBlastTimer -= diff; - - DoMeleeAttackIfReady(); + DoMeleeAttackIfReady(); + }); } + + private: + TaskScheduler _scheduler; + InstanceScript* _instance; }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetSunwellPlateauAI<npc_void_sentinelAI>(creature); + } }; class npc_blackhole : public CreatureScript @@ -512,84 +512,220 @@ class npc_blackhole : public CreatureScript public: npc_blackhole() : CreatureScript("npc_blackhole") { } - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<npc_blackholeAI>(creature); - } - struct npc_blackholeAI : public ScriptedAI { npc_blackholeAI(Creature* creature) : ScriptedAI(creature) { - Initialize(); - instance = creature->GetInstanceScript(); + _instance = creature->GetInstanceScript(); } - void Initialize() - { - DespawnTimer = 15000; - SpellTimer = 5000; - Phase = 0; - NeedForAHack = 0; - } - - InstanceScript* instance; - - uint32 DespawnTimer; - uint32 SpellTimer; - uint8 Phase; - uint8 NeedForAHack; - void Reset() override { - Initialize(); + me->SetReactState(REACT_PASSIVE); + DoCast(SPELL_BLACKHOLE_SUMMON_VISUAL); - me->AddUnitState(UNIT_STATE_STUNNED); - DoCastAOE(SPELL_BLACKHOLE_SPAWN, true); - } + _scheduler.Schedule(Seconds(15), [this](TaskContext /*context*/) + { + me->DisappearAndDie(); + }); - void UpdateAI(uint32 diff) override - { - if (SpellTimer <= diff) + _scheduler.Schedule(Seconds(1), [this](TaskContext context) { - Unit* Victim = ObjectAccessor::GetUnit(*me, instance->GetGuidData(DATA_PLAYER_GUID)); - switch (NeedForAHack) + switch (context.GetRepeatCounter()) { case 0: - me->ClearUnitState(UNIT_STATE_STUNNED); - DoCastAOE(SPELL_BLACKHOLE_GROW, false); - if (Victim) - AttackStart(Victim); - SpellTimer = 700; - NeedForAHack = 2; + me->SetReactState(REACT_AGGRESSIVE); + DoCast(SPELL_BLACKHOLE_SUMMON_VISUAL_2); + if (Unit* victim = ObjectAccessor::GetUnit(*me, _instance->GetGuidData(DATA_PLAYER_GUID))) + AttackStart(victim); + context.Repeat(Milliseconds(1200)); break; case 1: - me->AddAura(SPELL_BLACKHOLE_GROW, me); - NeedForAHack = 2; - SpellTimer = 600; + DoCast(SPELL_BLACKHOLE_SUMMON_VISUAL); + context.Repeat(Seconds(2)); break; case 2: - SpellTimer = 400; - NeedForAHack = 3; - me->RemoveAura(SPELL_BLACKHOLE_GROW); + DoCast(SPELL_BLACKHOLE_PASSIVE); + DoCast(SPELL_BLACK_HOLE_VISUAL_2); + break; + default: break; - case 3: - SpellTimer = urand(400, 900); - NeedForAHack = 1; - if (Unit* Temp = me->GetVictim()) - { - if (Temp->GetPositionZ() > 73 && Victim) - AttackStart(Victim); - } else - return; } - } else SpellTimer -= diff; + }); + } - if (DespawnTimer <= diff) - me->DisappearAndDie(); - else DespawnTimer -= diff; + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); } + + private: + TaskScheduler _scheduler; + InstanceScript* _instance; }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetSunwellPlateauAI<npc_blackholeAI>(creature); + } +}; + +class spell_summon_blood_elves_script : SpellScriptLoader +{ + public: + spell_summon_blood_elves_script() : SpellScriptLoader("spell_summon_blood_elves_script") { } + + class spell_summon_blood_elves_script_SpellScript : public SpellScript + { + PrepareSpellScript(spell_summon_blood_elves_script_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + for (uint8 i = 0; i < MAX_SUMMON_BLOOD_ELVES; ++i) + if (!sSpellMgr->GetSpellInfo(SummonBloodElvesSpells[i])) + return false; + return true; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + for (uint8 i = 0; i < MAX_SUMMON_BLOOD_ELVES; ++i) + GetCaster()->CastSpell((Unit*)nullptr, SummonBloodElvesSpells[urand(0,3)], true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_summon_blood_elves_script_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_summon_blood_elves_script_SpellScript(); + } +}; + +class spell_muru_darkness : SpellScriptLoader +{ + public: + spell_muru_darkness() : SpellScriptLoader("spell_muru_darkness") { } + + class spell_muru_darkness_SpellScript : public SpellScript + { + PrepareSpellScript(spell_muru_darkness_SpellScript); + + bool Validate(SpellInfo const* /*spell*/) override + { + for (uint8 i = 0; i < MAX_SUMMON_DARK_FIEND; ++i) + if (!sSpellMgr->GetSpellInfo(SummonDarkFiendSpells[i])) + return false; + return true; + } + + void HandleAfterCast() + { + for (uint8 i = 0; i < MAX_SUMMON_DARK_FIEND; ++i) + GetCaster()->CastSpell((Unit*)nullptr, SummonDarkFiendSpells[i], true); + } + + void Register() override + { + AfterCast += SpellCastFn(spell_muru_darkness_SpellScript::HandleAfterCast); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_muru_darkness_SpellScript(); + } +}; + +class spell_dark_fiend_skin : public SpellScriptLoader +{ + public: + spell_dark_fiend_skin() : SpellScriptLoader("spell_dark_fiend_skin") { } + + class spell_dark_fiend_skin_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dark_fiend_skin_AuraScript); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL) + return; + + if (Creature* target = GetTarget()->ToCreature()) + { + target->SetReactState(REACT_PASSIVE); + target->AttackStop(); + target->StopMoving(); + target->CastSpell(target, SPELL_DARKFIEND_VISUAL, true); + target->DespawnOrUnsummon(3000); + } + } + + void Register() override + { + AfterEffectRemove += AuraEffectRemoveFn(spell_dark_fiend_skin_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dark_fiend_skin_AuraScript(); + } +}; + +class spell_transform_visual_missile_periodic : public SpellScriptLoader +{ + public: + spell_transform_visual_missile_periodic() : SpellScriptLoader("spell_transform_visual_missile_periodic") { } + + class spell_transform_visual_missile_periodic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_transform_visual_missile_periodic_AuraScript); + + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + GetTarget()->CastSpell((Unit*)nullptr, RAND(TRANSFORM_VISUAL_MISSILE_1, TRANSFORM_VISUAL_MISSILE_2), true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_transform_visual_missile_periodic_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_transform_visual_missile_periodic_AuraScript(); + } +}; + +class spell_summon_blood_elves_periodic : public SpellScriptLoader +{ + public: + spell_summon_blood_elves_periodic() : SpellScriptLoader("spell_summon_blood_elves_periodic") { } + + class spell_summon_blood_elves_periodic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_summon_blood_elves_periodic_AuraScript); + + void OnPeriodic(AuraEffect const* /*aurEff*/) + { + GetTarget()->CastSpell((Unit*)nullptr, SPELL_SUMMON_BLOOD_ELVES_SCRIPT, true); + } + + void Register() override + { + OnEffectPeriodic += AuraEffectPeriodicFn(spell_summon_blood_elves_periodic_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_summon_blood_elves_periodic_AuraScript(); + } }; void AddSC_boss_muru() @@ -600,4 +736,9 @@ void AddSC_boss_muru() new npc_dark_fiend(); new npc_void_sentinel(); new npc_blackhole(); + new spell_summon_blood_elves_script(); + new spell_muru_darkness(); + new spell_dark_fiend_skin(); + new spell_transform_visual_missile_periodic(); + new spell_summon_blood_elves_periodic(); } diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h index c6b4ae753a5..7ea0fc4bc7d 100644 --- a/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h +++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h @@ -97,7 +97,8 @@ enum CreatureIds NPC_FURY_MAGE = 25799, NPC_VOID_SENTINEL = 25772, NPC_VOID_SPAWN = 25824, - NPC_BLACK_HOLE = 25855 + NPC_BLACK_HOLE = 25855, + NPC_MURU_PORTAL_TARGET = 25770 }; enum GameObjectIds diff --git a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp index a5fed30a6c6..68391c65655 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp @@ -98,7 +98,7 @@ class boss_archaedas : public CreatureScript instance->SetData(0, 5); // respawn any dead minions me->setFaction(35); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->AddAura(SPELL_FREEZE_ANIM, me); } @@ -111,7 +111,7 @@ class boss_archaedas : public CreatureScript DoCast(minion, SPELL_AWAKEN_VAULT_WALKER, flag); minion->CastSpell(minion, SPELL_ARCHAEDAS_AWAKEN, true); minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + minion->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); minion->setFaction(14); minion->RemoveAura(SPELL_MINION_FREEZE_ANIM); } @@ -121,7 +121,7 @@ class boss_archaedas : public CreatureScript { me->setFaction(14); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); } void SpellHit(Unit* /*caster*/, const SpellInfo* spell) override @@ -261,7 +261,7 @@ class npc_archaedas_minions : public CreatureScript me->setFaction(35); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->RemoveAllAuras(); me->AddAura(SPELL_MINION_FREEZE_ANIM, me); } @@ -271,7 +271,7 @@ class npc_archaedas_minions : public CreatureScript me->setFaction (14); me->RemoveAllAuras(); me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); bAmIAwake = true; } @@ -350,7 +350,7 @@ class npc_stonekeepers : public CreatureScript { me->setFaction(35); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->RemoveAllAuras(); me->AddAura(SPELL_MINION_FREEZE_ANIM, me); } @@ -359,7 +359,7 @@ class npc_stonekeepers : public CreatureScript { me->setFaction(14); me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); } void UpdateAI(uint32 /*diff*/) override diff --git a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp index 8c78fb7ff1b..28a3a4eb4e0 100644 --- a/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp +++ b/src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp @@ -147,7 +147,7 @@ class instance_uldaman : public InstanceMapScript creature->setFaction(35); creature->RemoveAllAuras(); creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); creature->AddAura(SPELL_MINION_FREEZE_ANIM, creature); } @@ -178,7 +178,7 @@ class instance_uldaman : public InstanceMapScript Creature* target = instance->GetCreature(*i); if (!target || !target->IsAlive()) continue; - target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); target->setFaction(14); target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); target->RemoveAura(SPELL_MINION_FREEZE_ANIM); @@ -202,7 +202,7 @@ class instance_uldaman : public InstanceMapScript Creature* target = instance->GetCreature(*i); if (!target || !target->IsAlive() || target->getFaction() == 14) continue; - target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); target->setFaction(14); target->RemoveAura(SPELL_MINION_FREEZE_ANIM); @@ -268,7 +268,7 @@ class instance_uldaman : public InstanceMapScript return; ironaya->setFaction(415); - ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); ironaya->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); ironaya->GetMotionMaster()->Clear(); diff --git a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp index 8c179af2adf..3697b0876c8 100644 --- a/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp +++ b/src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp @@ -148,6 +148,11 @@ class boss_mandokir : public CreatureScript instance->SaveToDB(); } + void JustReachedHome() override + { + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_IMMUNE_TO_NPC); + } + void EnterCombat(Unit* /*who*/) override { _EnterCombat(); @@ -194,9 +199,9 @@ class boss_mandokir : public CreatureScript me->SetWalk(false); if (id == POINT_MANDOKIR_END) { - me->SetHomePosition(PosMandokir[0]); + me->SetHomePosition(PosMandokir[1]); + me->GetMotionMaster()->MoveTargetedHome(); instance->SetBossState(DATA_MANDOKIR, NOT_STARTED); - me->DespawnOrUnsummon(6000); // No idea how to respawn on wipe. } } } diff --git a/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp b/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp index b0d6e782052..03c957fa221 100644 --- a/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp +++ b/src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp @@ -174,7 +174,7 @@ public: if (state == DONE) if (GameObject* go = instance->GetGameObject(shrineOfGelihastGUID)) go->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_NOT_SELECTABLE); - break; + break; case DATA_AKU_MAI: if (state == DONE) if (GameObject* go = instance->GetGameObject(altarOfTheDeepsGUID)) diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp index 0526dcfa630..8e53ab06ca5 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp @@ -535,12 +535,12 @@ public: if (!UpdateVictim()) return; - /// @todo add his abilities'n-crap here - if (!LowHp && HealthBelowPct(20)) - { - Talk(SAY_TH_RANDOM_LOW_HP); - LowHp = true; - } + /// @todo add his abilities'n-crap here + if (!LowHp && HealthBelowPct(20)) + { + Talk(SAY_TH_RANDOM_LOW_HP); + LowHp = true; + } } }; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp index 8715f3ca0f6..4d0f80d502d 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp @@ -106,33 +106,33 @@ public: if (!UpdateVictim()) return; - events.Update(diff); + events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_SANDBREATH: - DoCastVictim(SPELL_SAND_BREATH); - events.ScheduleEvent(EVENT_SANDBREATH, urand(15000, 25000)); - break; - case EVENT_TIMESTOP: - DoCastVictim(SPELL_TIME_STOP); - events.ScheduleEvent(EVENT_TIMESTOP, urand(20000, 35000)); - break; - case EVENT_FRENZY: - Talk(EMOTE_FRENZY); - DoCast(me, SPELL_ENRAGE); - events.ScheduleEvent(EVENT_FRENZY, urand(20000, 35000)); - break; - default: - break; - } + case EVENT_SANDBREATH: + DoCastVictim(SPELL_SAND_BREATH); + events.ScheduleEvent(EVENT_SANDBREATH, urand(15000, 25000)); + break; + case EVENT_TIMESTOP: + DoCastVictim(SPELL_TIME_STOP); + events.ScheduleEvent(EVENT_TIMESTOP, urand(20000, 35000)); + break; + case EVENT_FRENZY: + Talk(EMOTE_FRENZY); + DoCast(me, SPELL_ENRAGE); + events.ScheduleEvent(EVENT_FRENZY, urand(20000, 35000)); + break; + default: + break; } - DoMeleeAttackIfReady(); + } + DoMeleeAttackIfReady(); } }; diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp index 2befdabe550..844ce239bdf 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp @@ -107,36 +107,36 @@ public: if (!UpdateVictim()) return; - events.Update(diff); + events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - while (uint32 eventId = events.ExecuteEvent()) + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - switch (eventId) - { - case EVENT_HASTE: - DoCast(me, SPELL_HASTE); - events.ScheduleEvent(EVENT_HASTE, urand(20000, 25000)); - break; - case EVENT_MORTAL_WOUND: - DoCast(me, SPELL_MORTAL_WOUND); - events.ScheduleEvent(EVENT_MORTAL_WOUND, urand(10000, 20000)); - break; - case EVENT_WING_BUFFET: - DoCast(me, SPELL_WING_BUFFET); - events.ScheduleEvent(EVENT_WING_BUFFET, urand(20000, 30000)); - break; - case EVENT_SPELL_REFLECTION: // Only in Heroic - DoCast(me, SPELL_REFLECT); - events.ScheduleEvent(EVENT_SPELL_REFLECTION, urand(25000, 35000)); - break; - default: - break; - } + case EVENT_HASTE: + DoCast(me, SPELL_HASTE); + events.ScheduleEvent(EVENT_HASTE, urand(20000, 25000)); + break; + case EVENT_MORTAL_WOUND: + DoCast(me, SPELL_MORTAL_WOUND); + events.ScheduleEvent(EVENT_MORTAL_WOUND, urand(10000, 20000)); + break; + case EVENT_WING_BUFFET: + DoCast(me, SPELL_WING_BUFFET); + events.ScheduleEvent(EVENT_WING_BUFFET, urand(20000, 30000)); + break; + case EVENT_SPELL_REFLECTION: // Only in Heroic + DoCast(me, SPELL_REFLECT); + events.ScheduleEvent(EVENT_SPELL_REFLECTION, urand(25000, 35000)); + break; + default: + break; } - DoMeleeAttackIfReady(); + } + DoMeleeAttackIfReady(); } }; diff --git a/src/server/scripts/Kalimdor/zone_the_barrens.cpp b/src/server/scripts/Kalimdor/zone_the_barrens.cpp index b113615ca50..7a963d066b6 100644 --- a/src/server/scripts/Kalimdor/zone_the_barrens.cpp +++ b/src/server/scripts/Kalimdor/zone_the_barrens.cpp @@ -43,38 +43,37 @@ EndContentData */ ## npc_beaten_corpse ######*/ -#define GOSSIP_CORPSE "Examine corpse in detail..." - enum BeatenCorpse { - QUEST_LOST_IN_BATTLE = 4921 + GOSSIP_OPTION_ID_BEATEN_CORPSE = 0, + GOSSIP_MENU_OPTION_INSPECT_BODY = 2871 }; class npc_beaten_corpse : public CreatureScript { -public: - npc_beaten_corpse() : CreatureScript("npc_beaten_corpse") { } + public: + npc_beaten_corpse() : CreatureScript("npc_beaten_corpse") { } - bool OnGossipSelect(Player* player, Creature* creature, uint32 /*sender*/, uint32 action) override - { - player->PlayerTalkClass->ClearMenus(); - if (action == GOSSIP_ACTION_INFO_DEF +1) + struct npc_beaten_corpseAI : public ScriptedAI { - player->SEND_GOSSIP_MENU(3558, creature->GetGUID()); - player->TalkedToCreature(creature->GetEntry(), creature->GetGUID()); - } - return true; - } - - bool OnGossipHello(Player* player, Creature* creature) override - { - if (player->GetQuestStatus(QUEST_LOST_IN_BATTLE) == QUEST_STATUS_INCOMPLETE || player->GetQuestStatus(QUEST_LOST_IN_BATTLE) == QUEST_STATUS_COMPLETE) - player->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_CORPSE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1); + npc_beaten_corpseAI(Creature* creature) : ScriptedAI(creature) + { + } - player->SEND_GOSSIP_MENU(3557, creature->GetGUID()); - return true; - } + void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override + { + if (menuId == GOSSIP_MENU_OPTION_INSPECT_BODY && gossipListId == GOSSIP_OPTION_ID_BEATEN_CORPSE) + { + player->CLOSE_GOSSIP_MENU(); + player->TalkedToCreature(me->GetEntry(), me->GetGUID()); + } + } + }; + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_beaten_corpseAI(creature); + } }; /*###### diff --git a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp index 16cfb30e2dc..2df0fceab9c 100644 --- a/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp +++ b/src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp @@ -156,7 +156,7 @@ public: ImpaleTarget = impaleTarget->GetGUID(); impaleTarget->SetReactState(REACT_PASSIVE); impaleTarget->SetDisplayId(DISPLAY_INVISIBLE); - impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); + impaleTarget->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL|UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE); return impaleTarget; } diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp index abca9a42d45..7f4a2158d25 100644 --- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp +++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp @@ -646,7 +646,7 @@ struct boss_jormungarAI : public BossAI // if the worm was mobile before submerging, make him stationary now if (WasMobile) { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); SetCombatMovement(false); me->SetDisplayId(ModelStationary); me->CastSpell(me, SPELL_GROUND_VISUAL_1, true); @@ -658,7 +658,7 @@ struct boss_jormungarAI : public BossAI } else { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); SetCombatMovement(true); me->GetMotionMaster()->MoveChase(me->GetVictim()); me->SetDisplayId(ModelMobile); @@ -1023,7 +1023,7 @@ class boss_icehowl : public CreatureScript me->SetTarget(_trampleTargetGUID); _trampleCast = false; SetCombatMovement(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->GetMotionMaster()->Clear(); me->GetMotionMaster()->MoveIdle(); events.ScheduleEvent(EVENT_TRAMPLE, 4*IN_MILLISECONDS); @@ -1107,7 +1107,7 @@ class boss_icehowl : public CreatureScript Talk(EMOTE_TRAMPLE_FAIL); } _movementStarted = false; - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); SetCombatMovement(true); me->GetMotionMaster()->MovementExpired(); me->GetMotionMaster()->Clear(); diff --git a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp index 055d0a07f41..94d1d93225c 100644 --- a/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp +++ b/src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp @@ -144,7 +144,7 @@ class boss_devourer_of_souls : public CreatureScript void Reset() override { _Reset(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->SetDisplayId(DISPLAY_ANGER); me->SetReactState(REACT_AGGRESSIVE); @@ -297,7 +297,7 @@ class boss_devourer_of_souls : public CreatureScript me->SetTarget(ObjectGuid::Empty); me->GetMotionMaster()->Clear(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); wailingSoulTick = 15; events.DelayEvents(18000); // no other events during wailing souls @@ -317,7 +317,7 @@ class boss_devourer_of_souls : public CreatureScript { me->SetReactState(REACT_AGGRESSIVE); me->SetDisplayId(DISPLAY_ANGER); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->GetMotionMaster()->MoveChase(me->GetVictim()); events.ScheduleEvent(EVENT_WAILING_SOULS, urand(60000, 70000)); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index d77842fff0a..73a7de36580 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -2461,6 +2461,33 @@ class spell_igb_teleport_players_on_victory : public SpellScriptLoader } }; +// 71201 - Battle Experience - proc should never happen, handled in script +class spell_igb_battle_experience_check : public SpellScriptLoader +{ +public: + spell_igb_battle_experience_check() : SpellScriptLoader("spell_igb_battle_experience_check") { } + + class spell_igb_battle_experience_check_AuraScript : public AuraScript + { + PrepareAuraScript(spell_igb_battle_experience_check_AuraScript); + + bool CheckProc(ProcEventInfo& /*eventInfo*/) + { + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_igb_battle_experience_check_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_igb_battle_experience_check_AuraScript(); + } +}; + class achievement_im_on_a_boat : public AchievementCriteriaScript { public: @@ -2500,5 +2527,6 @@ void AddSC_boss_icecrown_gunship_battle() new spell_igb_gunship_fall_teleport(); new spell_igb_check_for_players(); new spell_igb_teleport_players_on_victory(); + new spell_igb_battle_experience_check(); new achievement_im_on_a_boat(); } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp index caa37465165..1a37d5238d2 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp @@ -604,6 +604,7 @@ class npc_ice_tomb : public CreatureScript _trappedPlayerGUID.Clear(); player->RemoveAurasDueToSpell(SPELL_ICE_TOMB_DAMAGE); player->RemoveAurasDueToSpell(SPELL_ASPHYXIATION); + player->RemoveAurasDueToSpell(SPELL_ICE_TOMB_UNTARGETABLE); } } @@ -1284,9 +1285,9 @@ class spell_sindragosa_ice_tomb : public SpellScriptLoader public: spell_sindragosa_ice_tomb() : SpellScriptLoader("spell_sindragosa_ice_tomb_trap") { } - class spell_sindragosa_ice_tomb_SpellScript : public SpellScript + class spell_sindragosa_ice_tomb_AuraScript : public AuraScript { - PrepareSpellScript(spell_sindragosa_ice_tomb_SpellScript); + PrepareAuraScript(spell_sindragosa_ice_tomb_AuraScript); bool Validate(SpellInfo const* /*spell*/) override { @@ -1297,46 +1298,42 @@ class spell_sindragosa_ice_tomb : public SpellScriptLoader return true; } - void SummonTomb() + void PeriodicTick(AuraEffect const* aurEff) { - Position pos = GetHitUnit()->GetPosition(); - if (TempSummon* summon = GetCaster()->SummonCreature(NPC_ICE_TOMB, pos)) + PreventDefaultAction(); + + if (aurEff->GetTickNumber() == 1) { - summon->AI()->SetGUID(GetHitUnit()->GetGUID(), DATA_TRAPPED_PLAYER); - if (GameObject* go = summon->SummonGameObject(GO_ICE_BLOCK, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0)) + if (Unit* caster = GetCaster()) { - go->SetSpellId(SPELL_ICE_TOMB_DAMAGE); - summon->AddGameObject(go); + Position pos = GetTarget()->GetPosition(); + + if (TempSummon* summon = caster->SummonCreature(NPC_ICE_TOMB, pos)) + { + summon->AI()->SetGUID(GetTarget()->GetGUID(), DATA_TRAPPED_PLAYER); + GetTarget()->CastSpell(GetTarget(), SPELL_ICE_TOMB_UNTARGETABLE); + if (GameObject* go = summon->SummonGameObject(GO_ICE_BLOCK, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0)) + { + go->SetSpellId(SPELL_ICE_TOMB_DAMAGE); + summon->AddGameObject(go); + } + } } } } - void Register() override - { - AfterHit += SpellHitFn(spell_sindragosa_ice_tomb_SpellScript::SummonTomb); - } - }; - - class spell_sindragosa_ice_tomb_AuraScript : public AuraScript - { - PrepareAuraScript(spell_sindragosa_ice_tomb_AuraScript); - - void PeriodicTick(AuraEffect const* /*aurEff*/) + void HandleRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - PreventDefaultAction(); + GetTarget()->RemoveAurasDueToSpell(SPELL_ICE_TOMB_UNTARGETABLE); } void Register() override { OnEffectPeriodic += AuraEffectPeriodicFn(spell_sindragosa_ice_tomb_AuraScript::PeriodicTick, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + AfterEffectRemove += AuraEffectRemoveFn(spell_sindragosa_ice_tomb_AuraScript::HandleRemove, EFFECT_2, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); } }; - SpellScript* GetSpellScript() const override - { - return new spell_sindragosa_ice_tomb_SpellScript(); - } - AuraScript* GetAuraScript() const override { return new spell_sindragosa_ice_tomb_AuraScript(); diff --git a/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp b/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp new file mode 100644 index 00000000000..71d90da21a1 --- /dev/null +++ b/src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "ScriptMgr.h" +#include "ScriptedCreature.h" +#include "BattlegroundIC.h" + +enum BossSpells +{ + SPELL_BRUTAL_STRIKE = 58460, + SPELL_DAGGER_THROW = 67280, + SPELL_CRUSHING_LEAP = 68506, + SPELL_RAGE = 66776 +}; + +enum BossEvents +{ + EVENT_BRUTAL_STRIKE = 1, + EVENT_DAGGER_THROW = 2, + EVENT_CRUSHING_LEAP = 3, + EVENT_CHECK_RANGE = 4 +}; + +class boss_ioc_horde_alliance : public CreatureScript +{ +public: + boss_ioc_horde_alliance() : CreatureScript("boss_ioc_horde_alliance") { } + + struct boss_ioc_horde_allianceAI : public ScriptedAI + { + boss_ioc_horde_allianceAI(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + _events.Reset(); + + uint32 _npcGuard; + if (me->GetEntry() == NPC_HIGH_COMMANDER_HALFORD_WYRMBANE) + _npcGuard = NPC_SEVEN_TH_LEGION_INFANTRY; + else + _npcGuard = NPC_KOR_KRON_GUARD; + + std::list<Creature*> guardsList; + me->GetCreatureListWithEntryInGrid(guardsList, _npcGuard, 100.0f); + for (std::list<Creature*>::const_iterator itr = guardsList.begin(); itr != guardsList.end(); ++itr) + (*itr)->Respawn(); + }; + + void EnterCombat(Unit* /*who*/) override + { + _events.ScheduleEvent(EVENT_BRUTAL_STRIKE, 5 * IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_DAGGER_THROW, 7 * IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_CHECK_RANGE, 1 * IN_MILLISECONDS); + _events.ScheduleEvent(EVENT_CRUSHING_LEAP, 15 * IN_MILLISECONDS); + } + + void SpellHit(Unit* caster, SpellInfo const* /*spell*/) override + { + if (caster->IsVehicle()) + me->Kill(caster); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_BRUTAL_STRIKE: + DoCastVictim(SPELL_BRUTAL_STRIKE); + _events.ScheduleEvent(EVENT_BRUTAL_STRIKE, 5 * IN_MILLISECONDS); + break; + case EVENT_DAGGER_THROW: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) + DoCast(target, SPELL_DAGGER_THROW); + _events.ScheduleEvent(EVENT_DAGGER_THROW, 7 * IN_MILLISECONDS); + break; + case EVENT_CRUSHING_LEAP: + DoCastVictim(SPELL_CRUSHING_LEAP); + _events.ScheduleEvent(EVENT_CRUSHING_LEAP, 25 * IN_MILLISECONDS); + break; + case EVENT_CHECK_RANGE: + if (me->GetDistance(me->GetHomePosition()) > 25.0f) + DoCast(me, SPELL_RAGE); + else + me->RemoveAurasDueToSpell(SPELL_RAGE); + _events.ScheduleEvent(EVENT_CHECK_RANGE, 1 * IN_MILLISECONDS); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } + + private: + EventMap _events; + }; + + CreatureAI* GetAI(Creature* creature) const + { + return new boss_ioc_horde_allianceAI(creature); + } +}; + +void AddSC_boss_ioc_horde_alliance() +{ + new boss_ioc_horde_alliance(); +} diff --git a/src/server/scripts/Northrend/isle_of_conquest.cpp b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp index 11cc645f0cb..f7e83ff1f42 100644 --- a/src/server/scripts/Northrend/isle_of_conquest.cpp +++ b/src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp @@ -259,6 +259,58 @@ class spell_ioc_launch : public SpellScriptLoader } }; +enum SeaforiumBombSpells +{ + SPELL_SEAFORIUM_BLAST = 66676, + SPELL_HUGE_SEAFORIUM_BLAST = 66672, + SPELL_A_BOMB_INABLE_CREDIT = 68366, + SPELL_A_BOMB_INATION_CREDIT = 68367 +}; + +class spell_ioc_seaforium_blast_credit : public SpellScriptLoader +{ + public: + spell_ioc_seaforium_blast_credit() : SpellScriptLoader("spell_ioc_seaforium_blast_credit") { } + + class spell_ioc_seaforium_blast_credit_SpellScript : public SpellScript + { + PrepareSpellScript(spell_ioc_seaforium_blast_credit_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_A_BOMB_INABLE_CREDIT) || !sSpellMgr->GetSpellInfo(SPELL_A_BOMB_INATION_CREDIT)) + return false; + return true; + } + + void HandleAchievementCredit(SpellEffIndex /*effIndex*/) + { + uint32 _creditSpell = 0; + Unit* caster = GetOriginalCaster(); + if (!caster) + return; + + if (GetSpellInfo()->Id == SPELL_SEAFORIUM_BLAST) + _creditSpell = SPELL_A_BOMB_INABLE_CREDIT; + else if (GetSpellInfo()->Id == SPELL_HUGE_SEAFORIUM_BLAST) + _creditSpell = SPELL_A_BOMB_INATION_CREDIT; + + if (GetHitGObj() && GetHitGObj()->IsDestructibleBuilding()) + caster->CastSpell(caster, _creditSpell, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_ioc_seaforium_blast_credit_SpellScript::HandleAchievementCredit, EFFECT_1, SPELL_EFFECT_GAMEOBJECT_DAMAGE); + } + }; + + SpellScript* GetSpellScript() const + { + return new spell_ioc_seaforium_blast_credit_SpellScript(); + } +}; + void AddSC_isle_of_conquest() { new npc_four_car_garage(); @@ -266,4 +318,5 @@ void AddSC_isle_of_conquest() new spell_ioc_gunship_portal(); new spell_ioc_parachute_ic(); new spell_ioc_launch(); + new spell_ioc_seaforium_blast_credit(); } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp index 1e2eb06a2d5..ef6ccf5bf4b 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp @@ -332,7 +332,7 @@ public: OnEffectHit += SpellEffectFn(spell_gluth_decimate_SpellScript::HandleEvent, EFFECT_2, SPELL_EFFECT_SEND_EVENT); } - bool Load() + bool Load() override { return GetCaster() && GetCaster()->GetEntry() == NPC_GLUTH; } @@ -366,7 +366,7 @@ public: AfterHit += SpellHitFn(spell_gluth_zombiechow_search_SpellScript::HealForEachTargetHit); } - bool Load() + bool Load() override { return GetCaster() && GetCaster()->GetEntry() == NPC_GLUTH; } @@ -393,6 +393,7 @@ public: GluthGUID = creature->GetInstanceScript()->GetGuidData(DATA_GLUTH); DoCast(me, SPELL_INFECTED_WOUND); + timer = 0; state = STATE_ZOMBIE_NORMAL; } diff --git a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp index 47807ec28cb..42efdfbfe67 100644 --- a/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp +++ b/src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp @@ -309,7 +309,7 @@ public: _Reset(); me->setFaction(35); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE); ResetPlayerScale(); spawns.DespawnAll(); @@ -356,7 +356,7 @@ public: } DoCast(me, SPELL_KELTHUZAD_CHANNEL, false); Talk(SAY_SUMMON_MINIONS); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE); me->SetFloatValue(UNIT_FIELD_COMBATREACH, 4); me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 4); events.SetPhase(PHASE_ONE); @@ -426,7 +426,7 @@ public: Talk(SAY_AGGRO); Talk(EMOTE_PHASE_TWO); spawns.DespawnAll(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NOT_SELECTABLE); me->CastStop(); DoStartMovement(me->GetVictim()); diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp index f000d7a3ef7..3d5a6ee8dfb 100644 --- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp +++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp @@ -1224,13 +1224,15 @@ public: void DoAction(int32 /*action*/) override { if (Vehicle* vehicleTemp = me->GetVehicleKit()) + { if (vehicleTemp->GetPassenger(0) && vehicleTemp->GetPassenger(0)->GetTypeId() == TYPEID_PLAYER) { vehicleTemp->RemoveAllPassengers(); me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } + } - me->DespawnOrUnsummon(3*IN_MILLISECONDS); + me->DespawnOrUnsummon(3*IN_MILLISECONDS); } void MovementInform(uint32 type, uint32 id) override diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp index 8f7687d0fca..55295a534e1 100644 --- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp +++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp @@ -113,7 +113,7 @@ public: Initialize(); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL); if (!me->IsVisible()) me->SetVisible(true); @@ -152,7 +152,7 @@ public: me->AttackStop(); me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->GetMotionMaster()->Clear(); me->GetMotionMaster()->MoveIdle(); @@ -236,7 +236,7 @@ public: else if (lSparkList.empty()) { me->SetVisible(true); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_DISABLE_MOVE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE|UNIT_FLAG_NOT_SELECTABLE|UNIT_FLAG_REMOVE_CLIENT_CONTROL); DoCast(me, SPELL_SPARK_DESPAWN, false); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp index 05beacca638..fa59c021cad 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp @@ -171,8 +171,7 @@ enum FreyaNpcs enum FreyaActions { - ACTION_ELDER_DEATH = 1, - ACTION_ELDER_FREYA_KILLED = 2 + ACTION_ELDER_FREYA_KILLED = 1 }; enum FreyaEvents @@ -619,7 +618,7 @@ class boss_freya : public CreatureScript Elder->AttackStop(); Elder->CombatStop(true); Elder->DeleteThreatList(); - Elder->GetAI()->DoAction(ACTION_ELDER_FREYA_KILLED); + Elder->AI()->DoAction(ACTION_ELDER_FREYA_KILLED); } } } @@ -705,19 +704,10 @@ class boss_elder_brightleaf : public CreatureScript Talk(SAY_ELDER_SLAY); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { _JustDied(); Talk(SAY_ELDER_DEATH); - - if (killer->GetTypeId() == TYPEID_PLAYER) - { - if (Creature* Ironbranch = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_IRONBRANCH))) - Ironbranch->AI()->DoAction(ACTION_ELDER_DEATH); - - if (Creature* Stonebark = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_STONEBARK))) - Stonebark->AI()->DoAction(ACTION_ELDER_DEATH); - } } void EnterCombat(Unit* /*who*/) override @@ -812,19 +802,10 @@ class boss_elder_stonebark : public CreatureScript Talk(SAY_ELDER_SLAY); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { _JustDied(); Talk(SAY_ELDER_DEATH); - - if (killer->GetTypeId() == TYPEID_PLAYER) - { - if (Creature* Ironbranch = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_IRONBRANCH))) - Ironbranch->AI()->DoAction(ACTION_ELDER_DEATH); - - if (Creature* Brightleaf = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_BRIGHTLEAF))) - Brightleaf->AI()->DoAction(ACTION_ELDER_DEATH); - } } void EnterCombat(Unit* /*who*/) override @@ -925,19 +906,10 @@ class boss_elder_ironbranch : public CreatureScript Talk(SAY_ELDER_SLAY); } - void JustDied(Unit* killer) override + void JustDied(Unit* /*killer*/) override { _JustDied(); Talk(SAY_ELDER_DEATH); - - if (killer->GetTypeId() == TYPEID_PLAYER) - { - if (Creature* Brightleaf = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_BRIGHTLEAF))) - Brightleaf->AI()->DoAction(ACTION_ELDER_DEATH); - - if (Creature* Stonebark = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_STONEBARK))) - Stonebark->AI()->DoAction(ACTION_ELDER_DEATH); - } } void EnterCombat(Unit* /*who*/) override diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp index 09d95b34521..cbd24141bdf 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp @@ -415,7 +415,7 @@ class npc_saronite_vapors : public CreatureScript if (damage >= me->GetHealth()) { damage = 0; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->SetStandState(UNIT_STAND_STATE_DEAD); me->SetHealth(me->GetMaxHealth()); me->RemoveAllAuras(); diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp index c35a5936822..86068a1ecba 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp @@ -184,7 +184,7 @@ class npc_flash_freeze : public CreatureScript Initialize(); instance = me->GetInstanceScript(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); } void Initialize() @@ -259,7 +259,7 @@ class npc_ice_block : public CreatureScript { instance = me->GetInstanceScript(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); } InstanceScript* instance; @@ -269,7 +269,7 @@ class npc_ice_block : public CreatureScript void IsSummonedBy(Unit* summoner) override { targetGUID = summoner->GetGUID(); - summoner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + summoner->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); me->SetInCombatWith(summoner); me->AddThreat(summoner, 250.0f); summoner->AddThreat(me, 250.0f); @@ -285,7 +285,7 @@ class npc_ice_block : public CreatureScript { if (Creature* Helper = ObjectAccessor::GetCreature(*me, targetGUID)) { - Helper->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); + Helper->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED); if (Creature* Hodir = ObjectAccessor::GetCreature(*me, instance->GetGuidData(BOSS_HODIR))) { @@ -388,7 +388,7 @@ class boss_hodir : public CreatureScript me->RemoveAllAttackers(); me->AttackStop(); me->SetReactState(REACT_PASSIVE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); me->InterruptNonMeleeSpells(true); me->StopMoving(); me->GetMotionMaster()->Clear(); @@ -539,7 +539,7 @@ class npc_icicle : public CreatureScript { Initialize(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid1); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_NOT_SELECTABLE); me->SetReactState(REACT_PASSIVE); } @@ -593,7 +593,7 @@ class npc_snowpacked_icicle : public CreatureScript { Initialize(); me->SetDisplayId(me->GetCreatureTemplate()->Modelid2); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); me->SetReactState(REACT_PASSIVE); } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp index cd214a0114f..7106e9e4ebe 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp @@ -178,7 +178,7 @@ class boss_ignis : public CreatureScript { summon->setFaction(16); summon->SetReactState(REACT_AGGRESSIVE); - summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_DISABLE_MOVE); + summon->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED | UNIT_FLAG_STUNNED | UNIT_FLAG_REMOVE_CLIENT_CONTROL); } summon->AI()->AttackStart(me->GetVictim()); @@ -375,7 +375,7 @@ class npc_scorch_ground : public CreatureScript npc_scorch_groundAI(Creature* creature) : ScriptedAI(creature) { Initialize(); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE |UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL |UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_PACIFIED); creature->SetDisplayId(16925); //model 2 in db cannot overwrite wdb fields } diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp index 470ad388fff..45d4fa58a03 100644 --- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp +++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp @@ -104,7 +104,7 @@ class boss_kologarn : public CreatureScript left(false), right(false) { me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); DoCast(SPELL_KOLOGARN_REDUCE_PARRY); SetCombatMovement(false); diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp index ee71c696d7c..3dec0c60991 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp @@ -102,7 +102,7 @@ public: Sequence[i] = Phase(i); /// This ensures a random order and only executes each phase once. - std::random_shuffle(Sequence, Sequence + PHASE_GORTOK_PALEHOOF); + Trinity::Containers::RandomShuffle(Sequence); uiArcingSmashTimer = 15000; uiImpaleTimer = 12000; @@ -118,7 +118,7 @@ public: uint32 uiWhiteringRoarTimer; Phase currentPhase; uint8 AddCount; - Phase Sequence[4]; + std::array<Phase, 4> Sequence; void Reset() override { diff --git a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp index 06ebcbdc2fb..e7305e53808 100644 --- a/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp +++ b/src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp @@ -247,7 +247,7 @@ class npc_frozen_orb_stalker : public CreatureScript npc_frozen_orb_stalkerAI(Creature* creature) : ScriptedAI(creature) { creature->SetVisible(false); - creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); creature->SetReactState(REACT_PASSIVE); instance = creature->GetInstanceScript(); diff --git a/src/server/scripts/Northrend/northrend_script_loader.cpp b/src/server/scripts/Northrend/northrend_script_loader.cpp index 7d104b85f6d..d84bb1c4072 100644 --- a/src/server/scripts/Northrend/northrend_script_loader.cpp +++ b/src/server/scripts/Northrend/northrend_script_loader.cpp @@ -178,6 +178,8 @@ void AddSC_boss_baltharus_the_warborn(); void AddSC_boss_saviana_ragefire(); void AddSC_boss_general_zarithrian(); void AddSC_boss_halion(); +void AddSC_isle_of_conquest(); // Isle of Conquest +void AddSC_boss_ioc_horde_alliance(); void AddSC_dalaran(); void AddSC_borean_tundra(); @@ -190,7 +192,6 @@ void AddSC_storm_peaks(); void AddSC_wintergrasp(); void AddSC_zuldrak(); void AddSC_crystalsong_forest(); -void AddSC_isle_of_conquest(); // The name of this function should match: // void Add${NameOfDirectory}Scripts() @@ -358,6 +359,8 @@ void AddNorthrendScripts() AddSC_boss_saviana_ragefire(); AddSC_boss_general_zarithrian(); AddSC_boss_halion(); + AddSC_isle_of_conquest(); // Isle of Conquest + AddSC_boss_ioc_horde_alliance(); AddSC_dalaran(); AddSC_borean_tundra(); @@ -370,5 +373,4 @@ void AddNorthrendScripts() AddSC_wintergrasp(); AddSC_zuldrak(); AddSC_crystalsong_forest(); - AddSC_isle_of_conquest(); } diff --git a/src/server/scripts/Northrend/zone_grizzly_hills.cpp b/src/server/scripts/Northrend/zone_grizzly_hills.cpp index 59802165a94..adade245c2b 100644 --- a/src/server/scripts/Northrend/zone_grizzly_hills.cpp +++ b/src/server/scripts/Northrend/zone_grizzly_hills.cpp @@ -22,6 +22,7 @@ #include "Player.h" #include "SpellScript.h" #include "CreatureTextMgr.h" +#include "CombatAI.h" /*###### ## Quest 12027: Mr. Floppy's Perilous Adventure @@ -854,6 +855,260 @@ class spell_infected_worgen_bite : public SpellScriptLoader } }; +/*###### +## Quest: Riding the Red Rocket +######*/ + +enum RedRocket +{ + SPELL_VEHICLE_WARHEAD_FUSE = 49107, + SPELL_ALLIANCE_KILL_CREDIT_TORPEDO = 49510, + SPELL_HORDE_KILL_CREDIT_TORPEDO = 49340, + NPC_HORDE_LUMBERBOAT = 27702, + NPC_ALLIANCE_LUMBERBOAT = 27688, + SPELL_DETONATE = 49250 +}; + +class npc_rocket_propelled_warhead : public CreatureScript +{ +public: + npc_rocket_propelled_warhead() : CreatureScript("npc_rocket_propelled_warhead") { } + + struct npc_rocket_propelled_warheadAI : public VehicleAI + { + npc_rocket_propelled_warheadAI(Creature* creature) : VehicleAI(creature) + { + _finished = false; + _faction = ALLIANCE; + } + + void PassengerBoarded(Unit* who, int8 /*seatId*/, bool apply) override + { + if (apply && who->ToPlayer()) + { + DoCast(me, SPELL_VEHICLE_WARHEAD_FUSE); + _faction = who->ToPlayer()->GetTeam(); + } + } + + void JustReachedHome() override + { + _finished = false; + me->SetVisible(true); + me->GetMotionMaster()->Clear(true); + } + + void DoAction(int32 /*action*/) override + { + FinishQuest(false, _faction); + } + + void SpellHit(Unit* caster, SpellInfo const* /*spellInfo*/) override + { + if (caster->GetEntry() == NPC_HORDE_LUMBERBOAT || caster->GetEntry() == NPC_ALLIANCE_LUMBERBOAT) + FinishQuest(true, _faction); + } + + void FinishQuest(bool success, uint32 faction) + { + if (_finished) + return; + + _finished = true; + + if (success) + DoCast(me, faction == ALLIANCE ? SPELL_ALLIANCE_KILL_CREDIT_TORPEDO : SPELL_HORDE_KILL_CREDIT_TORPEDO); + + DoCast(me, SPELL_DETONATE); + me->RemoveAllAuras(); + me->SetVisible(false); + me->GetMotionMaster()->MoveTargetedHome(); + } + + private: + uint32 _faction; + bool _finished; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new npc_rocket_propelled_warheadAI(creature); + } +}; + +enum WarheadSpells +{ + SPELL_WARHEAD_Z_CHECK = 61678, + SPELL_WARHEAD_SEEKING_LUMBERSHIP = 49331, + SPELL_WARHEAD_FUSE = 49181 +}; +// 49107 - Vehicle: Warhead Fuse +class spell_vehicle_warhead_fuse : public SpellScriptLoader +{ +public: + spell_vehicle_warhead_fuse() : SpellScriptLoader("spell_vehicle_warhead_fuse") { } + + class spell_vehicle_warhead_fuse_SpellScript : public SpellScript + { + PrepareSpellScript(spell_vehicle_warhead_fuse_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARHEAD_Z_CHECK) || !sSpellMgr->GetSpellInfo(SPELL_WARHEAD_SEEKING_LUMBERSHIP) || !sSpellMgr->GetSpellInfo(SPELL_WARHEAD_FUSE)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + + caster->CastSpell(caster, SPELL_WARHEAD_Z_CHECK, true); + caster->CastSpell(caster, SPELL_WARHEAD_SEEKING_LUMBERSHIP, true); + caster->CastSpell(caster, SPELL_WARHEAD_FUSE, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_vehicle_warhead_fuse_SpellScript::HandleDummy, EFFECT_1, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_vehicle_warhead_fuse_SpellScript(); + } +}; + +enum WarheadDenonate +{ + SPELL_PARACHUTE = 66154, + SPELL_TORPEDO_EXPLOSION = 49290, + NPC_ALLIANCE_LUMBERBOAT_EXPLOSIONS = 27689 +}; +// 49250 - Detonate +class spell_warhead_detonate : public SpellScriptLoader +{ +public: + spell_warhead_detonate() : SpellScriptLoader("spell_warhead_detonate") { } + + class spell_warhead_detonate_SpellScript : public SpellScript + { + PrepareSpellScript(spell_warhead_detonate_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PARACHUTE) || !sSpellMgr->GetSpellInfo(SPELL_TORPEDO_EXPLOSION)) + return false; + return true; + } + + void HandleDummy(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + Player* player = GetHitPlayer(); + if (!player) + return; + + player->ExitVehicle(); + float horizontalSpeed = 3.0f; + float verticalSpeed = 40.0f; + player->KnockbackFrom(caster->GetPositionX(), caster->GetPositionY(), horizontalSpeed, verticalSpeed); + caster->CastSpell(player, SPELL_PARACHUTE, true); + + std::list<Creature*> explosionBunnys; + caster->GetCreatureListWithEntryInGrid(explosionBunnys, NPC_ALLIANCE_LUMBERBOAT_EXPLOSIONS, 90.0f); + for (std::list<Creature*>::const_iterator itr = explosionBunnys.begin(); itr != explosionBunnys.end(); ++itr) + (*itr)->CastSpell((*itr), SPELL_TORPEDO_EXPLOSION, true); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_warhead_detonate_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_warhead_detonate_SpellScript(); + } +}; + +// 61678 - Z Check +class spell_z_check : public SpellScriptLoader +{ +public: + spell_z_check() : SpellScriptLoader("spell_z_check") { } + + class spell_z_check_AuraScript : public AuraScript + { + PrepareAuraScript(spell_z_check_AuraScript); + + public: + spell_z_check_AuraScript() + { + _posZ = 0.0f; + } + + void HandleEffectApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + _posZ = GetTarget()->GetPositionZ(); + } + + void HandleEffectPeriodic(AuraEffect const* /*aurEff*/) + { + PreventDefaultAction(); + + if (_posZ != GetTarget()->GetPositionZ()) + if (Creature* target = GetTarget()->ToCreature()) + target->AI()->DoAction(0); + } + + private: + float _posZ; + + void Register() override + { + OnEffectApply += AuraEffectApplyFn(spell_z_check_AuraScript::HandleEffectApply, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + OnEffectPeriodic += AuraEffectPeriodicFn(spell_z_check_AuraScript::HandleEffectPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_z_check_AuraScript(); + } +}; + +// 49181 - Warhead Fuse +class spell_warhead_fuse : public SpellScriptLoader +{ +public: + spell_warhead_fuse() : SpellScriptLoader("spell_warhead_fuse") { } + + class spell_warhead_fuse_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warhead_fuse_AuraScript); + + void HandleOnEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (Unit* rocketUnit = GetTarget()->GetVehicleBase()) + if (Creature* rocketCrea = rocketUnit->ToCreature()) + rocketCrea->AI()->DoAction(0); + } + + void Register() override + { + OnEffectRemove += AuraEffectRemoveFn(spell_warhead_fuse_AuraScript::HandleOnEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warhead_fuse_AuraScript(); + } +}; + void AddSC_grizzly_hills() { new npc_emily(); @@ -866,4 +1121,9 @@ void AddSC_grizzly_hills() new npc_lake_frog(); new spell_shredder_delivery(); new spell_infected_worgen_bite(); + new npc_rocket_propelled_warhead(); + new spell_z_check(); + new spell_warhead_detonate(); + new spell_vehicle_warhead_fuse(); + new spell_warhead_fuse(); } diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp index 3c651e10a1e..14aeda04a7e 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp @@ -1301,15 +1301,17 @@ public: EventMaiev Event = EVENT_MAIEV_NULL; for (uint8 i = 1; i <= MaxTimer; ++i) + { if (Timer[i]) { if (Timer[i] <= diff) Event = (EventMaiev)i; else Timer[i] -= diff; } + } - switch (Event) - { + switch (Event) + { case EVENT_MAIEV_STEALTH: { me->SetFullHealth(); @@ -1345,21 +1347,21 @@ public: break; default: break; - } + } - if (HealthBelowPct(50)) - { - me->SetVisible(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) - ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->DeleteFromThreatList(me->GetGUID()); - me->AttackStop(); - Timer[EVENT_MAIEV_STEALTH] = 60000; // reappear after 1 minute - MaxTimer = 1; - } + if (HealthBelowPct(50)) + { + me->SetVisible(false); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + if (Creature* illidan = ObjectAccessor::GetCreature(*me, IllidanGUID)) + ENSURE_AI(boss_illidan_stormrage::boss_illidan_stormrageAI, illidan->AI())->DeleteFromThreatList(me->GetGUID()); + me->AttackStop(); + Timer[EVENT_MAIEV_STEALTH] = 60000; // reappear after 1 minute + MaxTimer = 1; + } - if (Phase == PHASE_NORMAL_MAIEV) - DoMeleeAttackIfReady(); + if (Phase == PHASE_NORMAL_MAIEV) + DoMeleeAttackIfReady(); } private: diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp index 9b53b62cdee..5a7a7786c6a 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp @@ -174,7 +174,7 @@ public: void Initialize() { - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_REMOVE_CLIENT_CONTROL); } void EnterCombat(Unit* /*who*/) override @@ -308,7 +308,7 @@ public: void Initialize() { me->SetReactState(REACT_PASSIVE); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_REMOVE_CLIENT_CONTROL); DoCast(me, SPELL_FROZEN_CORE_GETS_HIT); DoCast(me, SPELL_ICE_SPEAR_AURA); } @@ -341,7 +341,7 @@ public: { _events.Reset(); DoCast(me, SPELL_ICE_SPEAR_AURA); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_DISABLE_MOVE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_REMOVE_CLIENT_CONTROL); } } @@ -542,6 +542,7 @@ public: npc_earthen_ring_flamecallerAI(Creature* creature) : ScriptedAI(creature) { _instance = me->GetInstanceScript(); + _mySpot = 0; } void Reset() override @@ -888,7 +889,7 @@ public: GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ICE_SPEAR_BUNNY, true); } - void Register() + void Register() override { OnEffectHitTarget += SpellEffectFn(spell_ice_spear_target_picker_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY); } diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp index 0d85a8f0d86..0fac7b5a39d 100644 --- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp +++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp @@ -81,7 +81,7 @@ public: } } - void SetGuidData(uint32 data, ObjectGuid guid) + void SetGuidData(uint32 data, ObjectGuid guid) override { switch (data) { diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp index 499550945c6..2592ed3b262 100644 --- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp +++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp @@ -180,24 +180,23 @@ class boss_grand_warlock_nethekurse : public CreatureScript } void MoveInLineOfSight(Unit* who) override - { if (!IntroOnce && me->IsWithinDistInMap(who, 30.0f)) - { + { if (who->GetTypeId() != TYPEID_PLAYER) return; - Talk(SAY_INTRO); - IntroOnce = true; - IsIntroEvent = true; + Talk(SAY_INTRO); + IntroOnce = true; + IsIntroEvent = true; - instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS); - } + instance->SetBossState(DATA_NETHEKURSE, IN_PROGRESS); + } - if (IsIntroEvent || !IsMainEvent) - return; + if (IsIntroEvent || !IsMainEvent) + return; - ScriptedAI::MoveInLineOfSight(who); + ScriptedAI::MoveInLineOfSight(who); } void EnterCombat(Unit* /*who*/) override diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp index 9fd1c5c7388..30b3fd67687 100644 --- a/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp +++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp @@ -325,86 +325,85 @@ class boss_high_astromancer_solarian : public CreatureScript else Phase1_Timer-=diff; } - else - if (Phase == 2) + else if (Phase == 2) + { + //10 seconds after Solarian disappears, 12 mobs spawn out of the three portals. + me->AttackStop(); + me->StopMoving(); + if (Phase2_Timer <= diff) { - //10 seconds after Solarian disappears, 12 mobs spawn out of the three portals. - me->AttackStop(); - me->StopMoving(); - if (Phase2_Timer <= diff) - { - Phase = 3; - for (int i=0; i <= 2; ++i) - for (int j=1; j <= 4; j++) - SummonMinion(NPC_SOLARIUM_AGENT, Portals[i][0], Portals[i][1], Portals[i][2]); + Phase = 3; + for (int i=0; i <= 2; ++i) + for (int j=1; j <= 4; j++) + SummonMinion(NPC_SOLARIUM_AGENT, Portals[i][0], Portals[i][1], Portals[i][2]); - Talk(SAY_SUMMON1); - Phase2_Timer = 10000; - } - else - Phase2_Timer -= diff; + Talk(SAY_SUMMON1); + Phase2_Timer = 10000; } else - if (Phase == 3) - { - me->AttackStop(); - me->StopMoving(); - //Check Phase3_Timer - if (Phase3_Timer <= diff) - { - Phase = 1; - //15 seconds later Solarian reappears out of one of the 3 portals. Simultaneously, 2 healers appear in the two other portals. - int i = rand32() % 3; - me->GetMotionMaster()->Clear(); - me->SetPosition(Portals[i][0], Portals[i][1], Portals[i][2], CENTER_O); - - for (int j=0; j <= 2; j++) - if (j != i) - SummonMinion(NPC_SOLARIUM_PRIEST, Portals[j][0], Portals[j][1], Portals[j][2]); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetVisible(true); - - Talk(SAY_SUMMON2); - AppearDelay = true; - Phase3_Timer = 15000; - } - else - Phase3_Timer -= diff; - } - else - if (Phase == 4) - { - //Fear_Timer - if (Fear_Timer <= diff) - { - DoCast(me, SPELL_FEAR); - Fear_Timer = 20000; - } - else - Fear_Timer -= diff; - //VoidBolt_Timer - if (VoidBolt_Timer <= diff) - { - DoCastVictim(SPELL_VOID_BOLT); - VoidBolt_Timer = 10000; - } - else - VoidBolt_Timer -= diff; - } - //When Solarian reaches 20% she will transform into a huge void walker. - if (Phase != 4 && me->HealthBelowPct(20)) - { - Phase = 4; - //To make sure she wont be invisible or not selecatble - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->SetVisible(true); - Talk(SAY_VOIDA); - Talk(SAY_VOIDB); - me->SetArmor(WV_ARMOR); - me->SetDisplayId(MODEL_VOIDWALKER); - me->SetObjectScale(defaultsize*2.5f); - } + Phase2_Timer -= diff; + } + else if (Phase == 3) + { + me->AttackStop(); + me->StopMoving(); + //Check Phase3_Timer + if (Phase3_Timer <= diff) + { + Phase = 1; + //15 seconds later Solarian reappears out of one of the 3 portals. Simultaneously, 2 healers appear in the two other portals. + int i = rand32() % 3; + me->GetMotionMaster()->Clear(); + me->SetPosition(Portals[i][0], Portals[i][1], Portals[i][2], CENTER_O); + + for (int j=0; j <= 2; j++) + if (j != i) + SummonMinion(NPC_SOLARIUM_PRIEST, Portals[j][0], Portals[j][1], Portals[j][2]); + + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetVisible(true); + + Talk(SAY_SUMMON2); + AppearDelay = true; + Phase3_Timer = 15000; + } + else + Phase3_Timer -= diff; + } + else if (Phase == 4) + { + //Fear_Timer + if (Fear_Timer <= diff) + { + DoCast(me, SPELL_FEAR); + Fear_Timer = 20000; + } + else + Fear_Timer -= diff; + //VoidBolt_Timer + if (VoidBolt_Timer <= diff) + { + DoCastVictim(SPELL_VOID_BOLT); + VoidBolt_Timer = 10000; + } + else + VoidBolt_Timer -= diff; + } + + //When Solarian reaches 20% she will transform into a huge void walker. + if (Phase != 4 && me->HealthBelowPct(20)) + { + Phase = 4; + //To make sure she wont be invisible or not selecatble + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetVisible(true); + Talk(SAY_VOIDA); + Talk(SAY_VOIDB); + me->SetArmor(WV_ARMOR); + me->SetDisplayId(MODEL_VOIDWALKER); + me->SetObjectScale(defaultsize*2.5f); + } + DoMeleeAttackIfReady(); } }; diff --git a/src/server/scripts/Spells/spell_dk.cpp b/src/server/scripts/Spells/spell_dk.cpp index 724019a1b19..980c0db19cc 100644 --- a/src/server/scripts/Spells/spell_dk.cpp +++ b/src/server/scripts/Spells/spell_dk.cpp @@ -30,6 +30,13 @@ enum DeathKnightSpells { + SPELL_DK_ACCLIMATION_HOLY = 50490, + SPELL_DK_ACCLIMATION_FIRE = 50362, + SPELL_DK_ACCLIMATION_FROST = 50485, + SPELL_DK_ACCLIMATION_ARCANE = 50486, + SPELL_DK_ACCLIMATION_SHADOW = 50489, + SPELL_DK_ACCLIMATION_NATURE = 50488, + SPELL_DK_ADVANTAGE_T10_4P_MELEE = 70657, SPELL_DK_ANTI_MAGIC_SHELL_TALENT = 51052, SPELL_DK_BLACK_ICE_R1 = 49140, SPELL_DK_BLOOD_BOIL_TRIGGERED = 65658, @@ -51,6 +58,7 @@ enum DeathKnightSpells SPELL_DK_IMPROVED_BLOOD_PRESENCE_R1 = 50365, SPELL_DK_IMPROVED_FROST_PRESENCE_R1 = 50384, SPELL_DK_IMPROVED_UNHOLY_PRESENCE_R1 = 50391, + SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL = 50475, SPELL_DK_IMPROVED_BLOOD_PRESENCE_TRIGGERED = 63611, SPELL_DK_IMPROVED_UNHOLY_PRESENCE_TRIGGERED = 63622, SPELL_DK_ITEM_SIGIL_VENGEFUL_HEART = 64962, @@ -78,6 +86,141 @@ enum Misc NPC_DK_GHOUL = 26125 }; +// -49200 - Acclimation +class spell_dk_acclimation : public SpellScriptLoader +{ +public: + spell_dk_acclimation() : SpellScriptLoader("spell_dk_acclimation") { } + + class spell_dk_acclimation_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_acclimation_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_HOLY) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_FIRE) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_FROST) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_NATURE) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_SHADOW) || + !sSpellMgr->GetSpellInfo(SPELL_DK_ACCLIMATION_ARCANE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetDamageInfo()) + { + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + case SPELL_SCHOOL_FIRE: + case SPELL_SCHOOL_NATURE: + case SPELL_SCHOOL_FROST: + case SPELL_SCHOOL_SHADOW: + case SPELL_SCHOOL_ARCANE: + return true; + default: + break; + } + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerspell = 0; + + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + triggerspell = SPELL_DK_ACCLIMATION_HOLY; + break; + case SPELL_SCHOOL_FIRE: + triggerspell = SPELL_DK_ACCLIMATION_FIRE; + break; + case SPELL_SCHOOL_NATURE: + triggerspell = SPELL_DK_ACCLIMATION_NATURE; + break; + case SPELL_SCHOOL_FROST: + triggerspell = SPELL_DK_ACCLIMATION_FROST; + break; + case SPELL_SCHOOL_SHADOW: + triggerspell = SPELL_DK_ACCLIMATION_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + triggerspell = SPELL_DK_ACCLIMATION_ARCANE; + break; + default: + return; + } + + if (Unit* target = eventInfo.GetActionTarget()) + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_acclimation_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dk_acclimation_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_acclimation_AuraScript(); + } +}; + +// 70656 - Advantage (T10 4P Melee Bonus) +class spell_dk_advantage_t10_4p : public SpellScriptLoader +{ +public: + spell_dk_advantage_t10_4p() : SpellScriptLoader("spell_dk_advantage_t10_4p") { } + + class spell_dk_advantage_t10_4p_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_advantage_t10_4p_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_ADVANTAGE_T10_4P_MELEE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (Unit* caster = eventInfo.GetActor()) + { + if (caster->GetTypeId() != TYPEID_PLAYER || caster->getClass() != CLASS_DEATH_KNIGHT) + return false; + + for (uint8 i = 0; i < MAX_RUNES; ++i) + if (caster->ToPlayer()->GetRuneCooldown(i) == 0) + return false; + + return true; + } + + return false; + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_advantage_t10_4p_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_advantage_t10_4p_AuraScript(); + } +}; + // 50462 - Anti-Magic Shell (on raid member) class spell_dk_anti_magic_shell_raid : public SpellScriptLoader { @@ -919,6 +1062,52 @@ class spell_dk_improved_blood_presence : public SpellScriptLoader } }; +// 63611 - Improved Blood Presence Triggered +class spell_dk_improved_blood_presence_triggered : public SpellScriptLoader +{ +public: + spell_dk_improved_blood_presence_triggered() : SpellScriptLoader("spell_dk_improved_blood_presence_triggered") { } + + class spell_dk_improved_blood_presence_triggered_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dk_improved_blood_presence_triggered_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActor()->GetTypeId() == TYPEID_PLAYER) + return true; + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + eventInfo.GetActor()->CastCustomSpell(SPELL_DK_IMPROVED_BLOOD_PRESENCE_HEAL, SPELLVALUE_BASE_POINT0, CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()), + eventInfo.GetActor(), true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dk_improved_blood_presence_triggered_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dk_improved_blood_presence_triggered_AuraScript::HandleProc, EFFECT_1, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dk_improved_blood_presence_triggered_AuraScript(); + } +}; + // -50384 - Improved Frost Presence class spell_dk_improved_frost_presence : public SpellScriptLoader { @@ -2006,6 +2195,8 @@ public: void AddSC_deathknight_spell_scripts() { + new spell_dk_acclimation(); + new spell_dk_advantage_t10_4p(); new spell_dk_anti_magic_shell_raid(); new spell_dk_anti_magic_shell_self(); new spell_dk_anti_magic_zone(); @@ -2022,6 +2213,7 @@ void AddSC_deathknight_spell_scripts() new spell_dk_ghoul_explode(); new spell_dk_icebound_fortitude(); new spell_dk_improved_blood_presence(); + new spell_dk_improved_blood_presence_triggered(); new spell_dk_improved_frost_presence(); new spell_dk_improved_unholy_presence(); new spell_dk_pestilence(); diff --git a/src/server/scripts/Spells/spell_druid.cpp b/src/server/scripts/Spells/spell_druid.cpp index c088ae07e66..0bf5ab01f45 100644 --- a/src/server/scripts/Spells/spell_druid.cpp +++ b/src/server/scripts/Spells/spell_druid.cpp @@ -31,6 +31,13 @@ enum DruidSpells { SPELL_DRUID_BEAR_FORM_PASSIVE = 1178, SPELL_DRUID_DIRE_BEAR_FORM_PASSIVE = 9635, + SPELL_DRUID_ECLIPSE_LUNAR_PROC = 48518, + SPELL_DRUID_ECLIPSE_SOLAR_PROC = 48517, + SPELL_DRUID_FORMS_TRINKET_BEAR = 37340, + SPELL_DRUID_FORMS_TRINKET_CAT = 37341, + SPELL_DRUID_FORMS_TRINKET_MOONKIN = 37343, + SPELL_DRUID_FORMS_TRINKET_NONE = 37344, + SPELL_DRUID_FORMS_TRINKET_TREE = 37342, SPELL_DRUID_ENRAGE = 5229, SPELL_DRUID_ENRAGE_MOD_DAMAGE = 51185, SPELL_DRUID_ENRAGED_DEFENSE = 70725, @@ -48,6 +55,8 @@ enum DruidSpells SPELL_DRUID_NATURES_SPLENDOR = 57865, SPELL_DRUID_SURVIVAL_INSTINCTS = 50322, SPELL_DRUID_SAVAGE_ROAR = 62071, + SPELL_DRUID_T9_FERAL_RELIC_BEAR = 67354, + SPELL_DRUID_T9_FERAL_RELIC_CAT = 67355, SPELL_DRUID_TIGER_S_FURY_ENERGIZE = 51178 }; @@ -131,6 +140,69 @@ class spell_dru_dash : public SpellScriptLoader } }; +class spell_dru_eclipse : public SpellScriptLoader +{ +public: + spell_dru_eclipse() : SpellScriptLoader("spell_dru_eclipse") { } + + class spell_dru_eclipse_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_eclipse_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_LUNAR_PROC)) + return false; + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (!eventInfo.GetSpellInfo()) + return false; + + if (eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_LUNAR_PROC) || eventInfo.GetActor()->HasAura(SPELL_DRUID_ECLIPSE_SOLAR_PROC)) + return false; + + // Triggered by Wrath? + if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1) + return roll_chance_f(GetSpellInfo()->ProcChance * 0.6f) && _lunarProcCooldownEnd <= std::chrono::steady_clock::now(); + + return _solarProcCooldownEnd <= std::chrono::steady_clock::now(); + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + if (eventInfo.GetSpellInfo()->SpellFamilyFlags[0] & 1) + { + _lunarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount()); + eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_LUNAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff); + } + else + { + _solarProcCooldownEnd = std::chrono::steady_clock::now() + Seconds(aurEff->GetAmount()); + eventInfo.GetActor()->CastSpell(eventInfo.GetActor(), SPELL_DRUID_ECLIPSE_SOLAR_PROC, TRIGGERED_FULL_MASK, nullptr, aurEff); + } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_eclipse_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_eclipse_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_DUMMY); + } + + std::chrono::steady_clock::time_point _lunarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); + std::chrono::steady_clock::time_point _solarProcCooldownEnd = std::chrono::steady_clock::time_point::min(); + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_eclipse_AuraScript(); + } +}; + // 5229 - Enrage class spell_dru_enrage : public SpellScriptLoader { @@ -197,6 +269,91 @@ class spell_dru_enrage : public SpellScriptLoader } }; +// 37336 - Druid Forms Trinket +class spell_dru_forms_trinket : public SpellScriptLoader +{ +public: + spell_dru_forms_trinket() : SpellScriptLoader("spell_dru_forms_trinket") { } + + class spell_dru_forms_trinket_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_forms_trinket_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_BEAR) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_CAT) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_MOONKIN) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_NONE) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_FORMS_TRINKET_TREE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + Unit* target = eventInfo.GetActor(); + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + case FORM_CAT: + case FORM_MOONKIN: + case FORM_NONE: + case FORM_TREE: + return true; + default: + break; + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* target = eventInfo.GetActor(); + uint32 triggerspell = 0; + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + triggerspell = SPELL_DRUID_FORMS_TRINKET_BEAR; + break; + case FORM_CAT: + triggerspell = SPELL_DRUID_FORMS_TRINKET_CAT; + break; + case FORM_MOONKIN: + triggerspell = SPELL_DRUID_FORMS_TRINKET_MOONKIN; + break; + case FORM_NONE: + triggerspell = SPELL_DRUID_FORMS_TRINKET_NONE; + break; + case FORM_TREE: + triggerspell = SPELL_DRUID_FORMS_TRINKET_TREE; + break; + default: + return; + } + + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_forms_trinket_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_forms_trinket_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_forms_trinket_AuraScript(); + } +}; + // 54846 - Glyph of Starfire class spell_dru_glyph_of_starfire : public SpellScriptLoader { @@ -1079,6 +1236,77 @@ class spell_dru_typhoon : public SpellScriptLoader } }; +// 67353 - T9 Feral Relic (Idol of Mutilation) +class spell_dru_t9_feral_relic : public SpellScriptLoader +{ +public: + spell_dru_t9_feral_relic() : SpellScriptLoader("spell_dru_t9_feral_relic") { } + + class spell_dru_t9_feral_relic_AuraScript : public AuraScript + { + PrepareAuraScript(spell_dru_t9_feral_relic_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DRUID_T9_FERAL_RELIC_BEAR) || + !sSpellMgr->GetSpellInfo(SPELL_DRUID_T9_FERAL_RELIC_CAT)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + Unit* target = eventInfo.GetActor(); + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + case FORM_CAT: + return true; + default: + break; + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerspell = 0; + + Unit* target = eventInfo.GetActor(); + + switch (target->GetShapeshiftForm()) + { + case FORM_BEAR: + case FORM_DIREBEAR: + triggerspell = SPELL_DRUID_T9_FERAL_RELIC_BEAR; + break; + case FORM_CAT: + triggerspell = SPELL_DRUID_T9_FERAL_RELIC_CAT; + break; + default: + return; + } + + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_dru_t9_feral_relic_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_dru_t9_feral_relic_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_dru_t9_feral_relic_AuraScript(); + } +}; + // 70691 - Item T10 Restoration 4P Bonus class spell_dru_t10_restoration_4p_bonus : public SpellScriptLoader { @@ -1208,7 +1436,9 @@ void AddSC_druid_spell_scripts() { new spell_dru_bear_form_passive(); new spell_dru_dash(); + new spell_dru_eclipse(); new spell_dru_enrage(); + new spell_dru_forms_trinket(); new spell_dru_glyph_of_starfire(); new spell_dru_idol_lifebloom(); new spell_dru_innervate(); @@ -1230,6 +1460,7 @@ void AddSC_druid_spell_scripts() new spell_dru_flight_form(); new spell_dru_tiger_s_fury(); new spell_dru_typhoon(); + new spell_dru_t9_feral_relic(); new spell_dru_t10_restoration_4p_bonus(); new spell_dru_wild_growth(); } diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index 8b8c5300a9e..e8ad73ceadb 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -430,6 +430,61 @@ class spell_gen_bandage : public SpellScriptLoader } }; +// Blood Reserve - 64568 +enum BloodReserve +{ + SPELL_GEN_BLOOD_RESERVE_AURA = 64568, + SPELL_GEN_BLOOD_RESERVE_HEAL = 64569 +}; + +class spell_gen_blood_reserve : public SpellScriptLoader +{ + public: + spell_gen_blood_reserve() : SpellScriptLoader("spell_gen_blood_reserve") { } + + class spell_gen_blood_reserve_AuraScript : public AuraScript + { + PrepareAuraScript(spell_gen_blood_reserve_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_GEN_BLOOD_RESERVE_HEAL)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + if (Unit* caster = eventInfo.GetActionTarget()) + if (caster->HealthBelowPctDamaged(35, dmgInfo->GetDamage())) + return true; + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActionTarget(); + caster->CastCustomSpell(SPELL_GEN_BLOOD_RESERVE_HEAL, SPELLVALUE_BASE_POINT0, aurEff->GetAmount(), caster, TRIGGERED_FULL_MASK, nullptr, aurEff); + caster->RemoveAura(SPELL_GEN_BLOOD_RESERVE_AURA); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_gen_blood_reserve_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_gen_blood_reserve_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_gen_blood_reserve_AuraScript(); + } +}; + enum Bonked { SPELL_BONKED = 62991, @@ -2085,7 +2140,7 @@ class spell_gen_mounted_charge: public SpellScriptLoader } // If target isn't a training dummy there's a chance of failing the charge - if (!target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE) && roll_chance_f(12.5f)) + if (!target->IsCharmedOwnedByPlayerOrPlayer() && roll_chance_f(12.5f)) spellId = SPELL_CHARGE_MISS_EFFECT; if (Unit* vehicle = GetCaster()->GetVehicleBase()) @@ -4199,6 +4254,7 @@ void AddSC_generic_spell_scripts() new spell_gen_aura_service_uniform(); new spell_gen_av_drekthar_presence(); new spell_gen_bandage(); + new spell_gen_blood_reserve(); new spell_gen_bonked(); new spell_gen_break_shield("spell_gen_break_shield"); new spell_gen_break_shield("spell_gen_tournament_counterattack"); diff --git a/src/server/scripts/Spells/spell_hunter.cpp b/src/server/scripts/Spells/spell_hunter.cpp index 82d9d134445..0e952818a2f 100644 --- a/src/server/scripts/Spells/spell_hunter.cpp +++ b/src/server/scripts/Spells/spell_hunter.cpp @@ -51,9 +51,11 @@ enum HunterSpells SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_TRIGGERED = 54114, SPELL_HUNTER_PET_HEART_OF_THE_PHOENIX_DEBUFF = 55711, SPELL_HUNTER_PET_CARRION_FEEDER_TRIGGERED = 54045, + SPELL_HUNTER_PIERCING_SHOTS = 63468, SPELL_HUNTER_READINESS = 23989, SPELL_HUNTER_SNIPER_TRAINING_R1 = 53302, SPELL_HUNTER_SNIPER_TRAINING_BUFF_R1 = 64418, + SPELL_HUNTER_T9_4P_GREATNESS = 68130, SPELL_HUNTER_VICIOUS_VIPER = 61609, SPELL_HUNTER_VIPER_ATTACK_SPEED = 60144, SPELL_DRAENEI_GIFT_OF_THE_NAARU = 59543 @@ -704,6 +706,63 @@ class spell_hun_pet_heart_of_the_phoenix : public SpellScriptLoader } }; +// -53234 - Piercing Shots +class spell_hun_piercing_shots : public SpellScriptLoader +{ +public: + spell_hun_piercing_shots() : SpellScriptLoader("spell_hun_piercing_shots") { } + + class spell_hun_piercing_shots_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_piercing_shots_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_PIERCING_SHOTS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActionTarget()) + return true; + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); + + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + { + SpellInfo const* piercingShots = sSpellMgr->AssertSpellInfo(SPELL_HUNTER_PIERCING_SHOTS); + int32 duration = piercingShots->GetMaxDuration(); + uint32 amplitude = piercingShots->Effects[EFFECT_0].Amplitude; + uint32 dmg = dmgInfo->GetDamage(); + + uint32 bp = CalculatePct(int32(dmg), aurEff->GetAmount()) / (duration / int32(amplitude)); + bp += target->GetRemainingPeriodicAmount(target->GetGUID(), SPELL_HUNTER_PIERCING_SHOTS, SPELL_AURA_PERIODIC_DAMAGE); + + caster->CastCustomSpell(SPELL_HUNTER_PIERCING_SHOTS, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + } + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_piercing_shots_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_piercing_shots_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_piercing_shots_AuraScript(); + } +}; + // 56654, 58882 - Rapid Recuperation class spell_hun_rapid_recuperation : public SpellScriptLoader { @@ -967,6 +1026,51 @@ class spell_hun_target_only_pet_and_owner : public SpellScriptLoader } }; +// 67151 - T9 4P Bonus +class spell_hun_t9_4p_bonus : public SpellScriptLoader +{ +public: + spell_hun_t9_4p_bonus() : SpellScriptLoader("spell_hun_t9_4p_bonus") { } + + class spell_hun_t9_4p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_hun_t9_4p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_HUNTER_T9_4P_GREATNESS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActor()->GetTypeId() == TYPEID_PLAYER && eventInfo.GetActor()->ToPlayer()->GetPet()) + return true; + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + Unit* caster = eventInfo.GetActor(); + + caster->CastSpell(caster->ToPlayer()->GetPet(), SPELL_HUNTER_T9_4P_GREATNESS, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_hun_t9_4p_bonus_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_hun_t9_4p_bonus_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_hun_t9_4p_bonus_AuraScript(); + } +}; + // 60144 - Viper Attack Speed class spell_hun_viper_attack_speed : public SpellScriptLoader { @@ -1025,11 +1129,13 @@ void AddSC_hunter_spell_scripts() new spell_hun_misdirection_proc(); new spell_hun_pet_carrion_feeder(); new spell_hun_pet_heart_of_the_phoenix(); + new spell_hun_piercing_shots(); new spell_hun_rapid_recuperation(); new spell_hun_readiness(); new spell_hun_scatter_shot(); new spell_hun_sniper_training(); new spell_hun_tame_beast(); new spell_hun_target_only_pet_and_owner(); + new spell_hun_t9_4p_bonus(); new spell_hun_viper_attack_speed(); } diff --git a/src/server/scripts/Spells/spell_item.cpp b/src/server/scripts/Spells/spell_item.cpp index 0abff255e4b..f7c2cefa202 100644 --- a/src/server/scripts/Spells/spell_item.cpp +++ b/src/server/scripts/Spells/spell_item.cpp @@ -201,6 +201,55 @@ class spell_item_blessing_of_ancient_kings : public SpellScriptLoader } }; +// 47770 - Roll Dice +class spell_item_decahedral_dwarven_dice : public SpellScriptLoader +{ + public: + spell_item_decahedral_dwarven_dice() : SpellScriptLoader("spell_item_decahedral_dwarven_dice") { } + + class spell_item_decahedral_dwarven_dice_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_decahedral_dwarven_dice_SpellScript); + + enum + { + TEXT_DECAHEDRAL_DWARVEN_DICE = 26147 + }; + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sObjectMgr->GetBroadcastText(TEXT_DECAHEDRAL_DWARVEN_DICE)) + return false; + return true; + } + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->TextEmote(TEXT_DECAHEDRAL_DWARVEN_DICE, GetHitUnit()); + + static uint32 const minimum = 1; + static uint32 const maximum = 100; + + GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_decahedral_dwarven_dice_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_decahedral_dwarven_dice_SpellScript(); + } +}; + // 8342 - Defibrillate (Goblin Jumper Cables) have 33% chance on success // 22999 - Defibrillate (Goblin Jumper Cables XL) have 50% chance on success // 54732 - Defibrillate (Gnomish Army Knife) have 67% chance on success @@ -1319,6 +1368,57 @@ class spell_item_underbelly_elixir : public SpellScriptLoader } }; +// 47776 - Roll 'dem Bones +class spell_item_worn_troll_dice : public SpellScriptLoader +{ + public: + spell_item_worn_troll_dice() : SpellScriptLoader("spell_item_worn_troll_dice") { } + + class spell_item_worn_troll_dice_SpellScript : public SpellScript + { + PrepareSpellScript(spell_item_worn_troll_dice_SpellScript); + + enum + { + TEXT_WORN_TROLL_DICE = 26152 + }; + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sObjectMgr->GetBroadcastText(TEXT_WORN_TROLL_DICE)) + return false; + return true; + } + + bool Load() override + { + return GetCaster()->GetTypeId() == TYPEID_PLAYER; + } + + void HandleScript(SpellEffIndex /*effIndex*/) + { + GetCaster()->TextEmote(TEXT_WORN_TROLL_DICE, GetHitUnit()); + + static uint32 const minimum = 1; + static uint32 const maximum = 6; + + // roll twice + GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); + GetCaster()->ToPlayer()->DoRandomRoll(minimum, maximum); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_item_worn_troll_dice_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_item_worn_troll_dice_SpellScript(); + } +}; + enum AirRifleSpells { SPELL_AIR_RIFLE_HOLD_VISUAL = 65582, @@ -2631,6 +2731,70 @@ public: } }; +enum SoulPreserver +{ + SPELL_SOUL_PRESERVER_DRUID = 60512, + SPELL_SOUL_PRESERVER_PALADIN = 60513, + SPELL_SOUL_PRESERVER_PRIEST = 60514, + SPELL_SOUL_PRESERVER_SHAMAN = 60515, +}; + +class spell_item_soul_preserver : public SpellScriptLoader +{ +public: + spell_item_soul_preserver() : SpellScriptLoader("spell_item_soul_preserver") { } + + class spell_item_soul_preserver_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_soul_preserver_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_DRUID) || + !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_PALADIN) || + !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_PRIEST) || + !sSpellMgr->GetSpellInfo(SPELL_SOUL_PRESERVER_SHAMAN)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + + switch (caster->getClass()) + { + case CLASS_DRUID: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_DRUID, true, nullptr, aurEff); + break; + case CLASS_PALADIN: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PALADIN, true, nullptr, aurEff); + break; + case CLASS_PRIEST: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_PRIEST, true, nullptr, aurEff); + break; + case CLASS_SHAMAN: + caster->CastSpell(caster, SPELL_SOUL_PRESERVER_SHAMAN, true, nullptr, aurEff); + break; + default: + break; + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_soul_preserver_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_soul_preserver_AuraScript(); + } +}; + class spell_item_toy_train_set_pulse : public SpellScriptLoader { public: @@ -2668,6 +2832,336 @@ public: } }; +enum DeathChoiceSpells +{ + SPELL_DEATH_CHOICE_NORMAL_AURA = 67702, + SPELL_DEATH_CHOICE_NORMAL_AGILITY = 67703, + SPELL_DEATH_CHOICE_NORMAL_STRENGTH = 67708, + SPELL_DEATH_CHOICE_HEROIC_AURA = 67771, + SPELL_DEATH_CHOICE_HEROIC_AGILITY = 67772, + SPELL_DEATH_CHOICE_HEROIC_STRENGTH = 67773 +}; + +class spell_item_death_choice : public SpellScriptLoader +{ +public: + spell_item_death_choice() : SpellScriptLoader("spell_item_death_choice") { } + + class spell_item_death_choice_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_death_choice_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_NORMAL_STRENGTH) || + !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_NORMAL_AGILITY) || + !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_HEROIC_STRENGTH) || + !sSpellMgr->GetSpellInfo(SPELL_DEATH_CHOICE_HEROIC_AGILITY)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + float str = caster->GetStat(STAT_STRENGTH); + float agi = caster->GetStat(STAT_AGILITY); + + switch (aurEff->GetId()) + { + case SPELL_DEATH_CHOICE_NORMAL_AURA: + { + if (str > agi) + caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_STRENGTH, true, nullptr, aurEff); + else + caster->CastSpell(caster, SPELL_DEATH_CHOICE_NORMAL_AGILITY, true, nullptr, aurEff); + break; + } + case SPELL_DEATH_CHOICE_HEROIC_AURA: + { + if (str > agi) + caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_STRENGTH, true, nullptr, aurEff); + else + caster->CastSpell(caster, SPELL_DEATH_CHOICE_HEROIC_AGILITY, true, nullptr, aurEff); + break; + } + default: + break; + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_death_choice_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_death_choice_AuraScript(); + } +}; + +enum TrinketStackSpells +{ + SPELL_LIGHTNING_CAPACITOR_AURA = 37657, // Lightning Capacitor + SPELL_LIGHTNING_CAPACITOR_STACK = 37658, + SPELL_LIGHTNING_CAPACITOR_TRIGGER = 37661, + SPELL_THUNDER_CAPACITOR_AURA = 54841, // Thunder Capacitor + SPELL_THUNDER_CAPACITOR_STACK = 54842, + SPELL_THUNDER_CAPACITOR_TRIGGER = 54843, + SPELL_TOC25_CASTER_TRINKET_NORMAL_AURA = 67712, // Item - Coliseum 25 Normal Caster Trinket + SPELL_TOC25_CASTER_TRINKET_NORMAL_STACK = 67713, + SPELL_TOC25_CASTER_TRINKET_NORMAL_TRIGGER = 67714, + SPELL_TOC25_CASTER_TRINKET_HEROIC_AURA = 67758, // Item - Coliseum 25 Heroic Caster Trinket + SPELL_TOC25_CASTER_TRINKET_HEROIC_STACK = 67759, + SPELL_TOC25_CASTER_TRINKET_HEROIC_TRIGGER = 67760, +}; + +class spell_item_trinket_stack : public SpellScriptLoader +{ +public: + spell_item_trinket_stack(char const* scriptName, uint32 stackSpell, uint32 triggerSpell) : SpellScriptLoader(scriptName), + _stackSpell(stackSpell), _triggerSpell(triggerSpell) + { + } + + class spell_item_trinket_stack_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_trinket_stack_AuraScript); + + public: + spell_item_trinket_stack_AuraScript(uint32 stackSpell, uint32 triggerSpell) : _stackSpell(stackSpell), _triggerSpell(triggerSpell) + { + } + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(_stackSpell) || !sSpellMgr->GetSpellInfo(_triggerSpell)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + + caster->CastSpell(caster, _stackSpell, true, nullptr, aurEff); // cast the stack + + Aura* dummy = caster->GetAura(_stackSpell); // retrieve aura + + //dont do anything if it's not the right amount of stacks; + if (!dummy || dummy->GetStackAmount() < aurEff->GetAmount()) + return; + + // if right amount, remove the aura and cast real trigger + caster->RemoveAurasDueToSpell(_stackSpell); + if (Unit* target = eventInfo.GetActionTarget()) + caster->CastSpell(target, _triggerSpell, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_trinket_stack_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + + private: + uint32 _stackSpell; + uint32 _triggerSpell; + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_trinket_stack_AuraScript(_stackSpell, _triggerSpell); + } + +private: + uint32 _stackSpell; + uint32 _triggerSpell; +}; + +// 57345 - Darkmoon Card: Greatness +enum DarkmoonCardSpells +{ + SPELL_DARKMOON_CARD_STRENGHT = 60229, + SPELL_DARKMOON_CARD_AGILITY = 60233, + SPELL_DARKMOON_CARD_INTELLECT = 60234, + SPELL_DARKMOON_CARD_SPIRIT = 60235, +}; + +class spell_item_darkmoon_card_greatness : public SpellScriptLoader +{ +public: + spell_item_darkmoon_card_greatness() : SpellScriptLoader("spell_item_darkmoon_card_greatness") { } + + class spell_item_darkmoon_card_greatness_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_darkmoon_card_greatness_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_STRENGHT) || + !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_AGILITY) || + !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_INTELLECT) || + !sSpellMgr->GetSpellInfo(SPELL_DARKMOON_CARD_SPIRIT)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + float str = caster->GetStat(STAT_STRENGTH); + float agi = caster->GetStat(STAT_AGILITY); + float intl = caster->GetStat(STAT_INTELLECT); + float spi = caster->GetStat(STAT_SPIRIT); + float stat = 0.0f; + + uint32 spellTrigger = SPELL_DARKMOON_CARD_STRENGHT; + + if (str > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_STRENGHT; + stat = str; + } + + if (agi > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_AGILITY; + stat = agi; + } + + if (intl > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_INTELLECT; + stat = intl; + } + + if (spi > stat) + { + spellTrigger = SPELL_DARKMOON_CARD_SPIRIT; + stat = spi; + } + + caster->CastSpell(caster, spellTrigger, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_darkmoon_card_greatness_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_darkmoon_card_greatness_AuraScript(); + } +}; + +// 43820 - Amani Charm of the Witch Doctor +enum CharmWitchDoctor +{ + SPELL_CHARM_WITCH_DOCTOR_PROC = 43821 +}; + +class spell_item_charm_witch_doctor : public SpellScriptLoader +{ +public: + spell_item_charm_witch_doctor() : SpellScriptLoader("spell_item_charm_witch_doctor") { } + + class spell_item_charm_witch_doctor_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_charm_witch_doctor_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_CHARM_WITCH_DOCTOR_PROC)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); + + if (target) + { + int32 bp = CalculatePct(target->GetCreateHealth(),aurEff->GetSpellInfo()->Effects[1].CalcValue()); + caster->CastCustomSpell(target, SPELL_CHARM_WITCH_DOCTOR_PROC, &bp, nullptr, nullptr, true, nullptr, aurEff); + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_charm_witch_doctor_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_charm_witch_doctor_AuraScript(); + } +}; + +// 27522,40336 - Mana Drain +enum ManaDrainSpells +{ + SPELL_MANA_DRAIN_ENERGIZE = 29471, + SPELL_MANA_DRAIN_LEECH = 27526 +}; + +class spell_item_mana_drain : public SpellScriptLoader +{ +public: + spell_item_mana_drain() : SpellScriptLoader("spell_item_mana_drain") { } + + class spell_item_mana_drain_AuraScript : public AuraScript + { + PrepareAuraScript(spell_item_mana_drain_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MANA_DRAIN_ENERGIZE) + || !sSpellMgr->GetSpellInfo(SPELL_MANA_DRAIN_LEECH)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + Unit* caster = eventInfo.GetActor(); + Unit* target = eventInfo.GetActionTarget(); + + if (caster->IsAlive()) + caster->CastSpell(caster, SPELL_MANA_DRAIN_ENERGIZE, true, nullptr, aurEff); + + if (target && target->IsAlive()) + caster->CastSpell(target, SPELL_MANA_DRAIN_LEECH, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_item_mana_drain_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_item_mana_drain_AuraScript(); + } +}; + void AddSC_item_spell_scripts() { // 23074 Arcanite Dragonling @@ -2682,6 +3176,7 @@ void AddSC_item_spell_scripts() new spell_item_aegis_of_preservation(); new spell_item_arcane_shroud(); new spell_item_blessing_of_ancient_kings(); + new spell_item_decahedral_dwarven_dice(); new spell_item_defibrillate("spell_item_goblin_jumper_cables", 67, SPELL_GOBLIN_JUMPER_CABLES_FAIL); new spell_item_defibrillate("spell_item_goblin_jumper_cables_xl", 50, SPELL_GOBLIN_JUMPER_CABLES_XL_FAIL); new spell_item_defibrillate("spell_item_gnomish_army_knife", 33); @@ -2706,6 +3201,7 @@ void AddSC_item_spell_scripts() new spell_item_six_demon_bag(); new spell_item_the_eye_of_diminution(); new spell_item_underbelly_elixir(); + new spell_item_worn_troll_dice(); new spell_item_red_rider_air_rifle(); new spell_item_create_heart_candy(); @@ -2736,5 +3232,14 @@ void AddSC_item_spell_scripts() new spell_item_chicken_cover(); new spell_item_muisek_vessel(); new spell_item_greatmothers_soulcatcher(); + new spell_item_soul_preserver(); new spell_item_toy_train_set_pulse(); + new spell_item_death_choice(); + new spell_item_trinket_stack("spell_item_lightning_capacitor", SPELL_LIGHTNING_CAPACITOR_STACK, SPELL_LIGHTNING_CAPACITOR_TRIGGER); + new spell_item_trinket_stack("spell_item_thunder_capacitor", SPELL_THUNDER_CAPACITOR_STACK, SPELL_THUNDER_CAPACITOR_TRIGGER); + new spell_item_trinket_stack("spell_item_toc25_normal_caster_trinket", SPELL_TOC25_CASTER_TRINKET_NORMAL_STACK, SPELL_TOC25_CASTER_TRINKET_NORMAL_TRIGGER); + new spell_item_trinket_stack("spell_item_toc25_heroic_caster_trinket", SPELL_TOC25_CASTER_TRINKET_HEROIC_STACK, SPELL_TOC25_CASTER_TRINKET_HEROIC_TRIGGER); + new spell_item_darkmoon_card_greatness(); + new spell_item_charm_witch_doctor(); + new spell_item_mana_drain(); } diff --git a/src/server/scripts/Spells/spell_mage.cpp b/src/server/scripts/Spells/spell_mage.cpp index 2f4e4fa6f44..bacbe31630c 100644 --- a/src/server/scripts/Spells/spell_mage.cpp +++ b/src/server/scripts/Spells/spell_mage.cpp @@ -29,6 +29,7 @@ enum MageSpells { + SPELL_MAGE_BLAZING_SPEED = 31643, SPELL_MAGE_BURNOUT = 29077, SPELL_MAGE_COLD_SNAP = 11958, SPELL_MAGE_FOCUS_MAGIC_PROC = 54648, @@ -116,6 +117,42 @@ class spell_mage_blast_wave : public SpellScriptLoader } }; +// -31641 - Blazing Speed +class spell_mage_blazing_speed : public SpellScriptLoader +{ +public: + spell_mage_blazing_speed() : SpellScriptLoader("spell_mage_blazing_speed") { } + + class spell_mage_blazing_speed_AuraScript : public AuraScript + { + PrepareAuraScript(spell_mage_blazing_speed_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_MAGE_BLAZING_SPEED)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (Unit* target = eventInfo.GetActionTarget()) + target->CastSpell(target, SPELL_MAGE_BLAZING_SPEED, true, nullptr, aurEff); + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_mage_blazing_speed_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_mage_blazing_speed_AuraScript(); + } +}; + // -44449 - Burnout class spell_mage_burnout : public SpellScriptLoader { @@ -647,6 +684,7 @@ class spell_mage_summon_water_elemental : public SpellScriptLoader void AddSC_mage_spell_scripts() { new spell_mage_blast_wave(); + new spell_mage_blazing_speed(); new spell_mage_burnout(); new spell_mage_cold_snap(); new spell_mage_fire_frost_ward(); diff --git a/src/server/scripts/Spells/spell_paladin.cpp b/src/server/scripts/Spells/spell_paladin.cpp index d9fd36f5fd4..eed84d4ff6b 100644 --- a/src/server/scripts/Spells/spell_paladin.cpp +++ b/src/server/scripts/Spells/spell_paladin.cpp @@ -37,6 +37,7 @@ enum PaladinSpells SPELL_PALADIN_HOLY_SHOCK_R1 = 20473, SPELL_PALADIN_HOLY_SHOCK_R1_DAMAGE = 25912, SPELL_PALADIN_HOLY_SHOCK_R1_HEALING = 25914, + SPELL_PALADIN_ILLUMINATION_ENERGIZE = 20272, SPELL_PALADIN_BLESSING_OF_LOWER_CITY_DRUID = 37878, SPELL_PALADIN_BLESSING_OF_LOWER_CITY_PALADIN = 37879, @@ -871,6 +872,61 @@ class spell_pal_holy_shock : public SpellScriptLoader } }; +// -20210 - Illumination +class spell_pal_illumination : public SpellScriptLoader +{ +public: + spell_pal_illumination() : SpellScriptLoader("spell_pal_illumination") { } + + class spell_pal_illumination_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pal_illumination_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_SHOCK_R1_HEALING) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_ILLUMINATION_ENERGIZE) || + !sSpellMgr->GetSpellInfo(SPELL_PALADIN_HOLY_SHOCK_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + + // this script is valid only for the Holy Shock procs of illumination + if (eventInfo.GetHealInfo() && eventInfo.GetHealInfo()->GetSpellInfo()) + { + if (eventInfo.GetHealInfo()->GetSpellInfo()->SpellFamilyFlags[1] & 0x00010000) + { + PreventDefaultAction(); + Unit* target = eventInfo.GetActor(); // Paladin is the target of the energize + + // proc comes from the Holy Shock heal, need to get mana cost of original spell + uint32 originalspellid = sSpellMgr->GetSpellWithRank(SPELL_PALADIN_HOLY_SHOCK_R1, eventInfo.GetHealInfo()->GetSpellInfo()->GetRank()); + SpellInfo const* originalSpell = sSpellMgr->GetSpellInfo(originalspellid); + if (originalSpell && aurEff->GetSpellInfo()) + { + uint32 bp = CalculatePct(originalSpell->CalcPowerCost(target, originalSpell->GetSchoolMask()), aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue()); + target->CastCustomSpell(SPELL_PALADIN_ILLUMINATION_ENERGIZE, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + } + } + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pal_illumination_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pal_illumination_AuraScript(); + } +}; + // Maybe this is incorrect // These spells should always be cast on login, regardless of whether the player has the talent or not @@ -1393,6 +1449,7 @@ void AddSC_paladin_spell_scripts() new spell_pal_hand_of_sacrifice(); new spell_pal_hand_of_salvation(); new spell_pal_holy_shock(); + new spell_pal_illumination(); new spell_pal_improved_aura("spell_pal_improved_concentraction_aura", SPELL_PALADIN_IMPROVED_CONCENTRACTION_AURA); new spell_pal_improved_aura("spell_pal_improved_devotion_aura", SPELL_PALADIN_IMPROVED_DEVOTION_AURA); new spell_pal_improved_aura("spell_pal_sanctified_retribution", SPELL_PALADIN_SANCTIFIED_RETRIBUTION_AURA); diff --git a/src/server/scripts/Spells/spell_priest.cpp b/src/server/scripts/Spells/spell_priest.cpp index 51f03346df1..9e2d265aa9c 100644 --- a/src/server/scripts/Spells/spell_priest.cpp +++ b/src/server/scripts/Spells/spell_priest.cpp @@ -29,6 +29,7 @@ enum PriestSpells { + SPELL_PRIEST_BLESSED_RECOVERY_R1 = 27813, SPELL_PRIEST_DIVINE_AEGIS = 47753, SPELL_PRIEST_EMPOWERED_RENEW = 63544, SPELL_PRIEST_GLYPH_OF_CIRCLE_OF_HEALING = 55675, @@ -88,6 +89,50 @@ class RaidCheck Unit const* _caster; }; +// -27811 - Blessed Recovery +class spell_pri_blessed_recovery : public SpellScriptLoader +{ +public: + spell_pri_blessed_recovery() : SpellScriptLoader("spell_pri_blessed_recovery") { } + + class spell_pri_blessed_recovery_AuraScript : public AuraScript + { + PrepareAuraScript(spell_pri_blessed_recovery_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_PRIEST_BLESSED_RECOVERY_R1)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + if (DamageInfo* dmgInfo = eventInfo.GetDamageInfo()) + { + if (Unit* target = eventInfo.GetActionTarget()) + { + uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_PRIEST_BLESSED_RECOVERY_R1, aurEff->GetSpellInfo()->GetRank()); + uint32 bp = CalculatePct(int32(dmgInfo->GetDamage()), aurEff->GetAmount()) / 3; + bp += target->GetRemainingPeriodicAmount(target->GetGUID(), triggerSpell, SPELL_AURA_PERIODIC_HEAL); + target->CastCustomSpell(triggerSpell, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + } + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_pri_blessed_recovery_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_pri_blessed_recovery_AuraScript(); + } +}; + // -34861 - Circle of Healing class spell_pri_circle_of_healing : public SpellScriptLoader { @@ -869,6 +914,7 @@ class spell_pri_vampiric_touch : public SpellScriptLoader void AddSC_priest_spell_scripts() { + new spell_pri_blessed_recovery(); new spell_pri_circle_of_healing(); new spell_pri_divine_aegis(); new spell_pri_divine_hymn(); diff --git a/src/server/scripts/Spells/spell_rogue.cpp b/src/server/scripts/Spells/spell_rogue.cpp index affc4d1c26c..1abb6741e0d 100644 --- a/src/server/scripts/Spells/spell_rogue.cpp +++ b/src/server/scripts/Spells/spell_rogue.cpp @@ -43,7 +43,8 @@ enum RogueSpells SPELL_ROGUE_TRICKS_OF_THE_TRADE_PROC = 59628, SPELL_ROGUE_HONOR_AMONG_THIEVES = 51698, SPELL_ROGUE_HONOR_AMONG_THIEVES_PROC = 52916, - SPELL_ROGUE_HONOR_AMONG_THIEVES_2 = 51699 + SPELL_ROGUE_HONOR_AMONG_THIEVES_2 = 51699, + SPELL_ROGUE_T10_2P_BONUS = 70804, }; // 13877, 33735, (check 51211, 65956) - Blade Flurry @@ -843,6 +844,40 @@ public: } }; +// 70805 - Rogue T10 2P Bonus -- THIS SHOULD BE REMOVED WITH NEW PROC SYSTEM. +class spell_rog_t10_2p_bonus : public SpellScriptLoader +{ +public: + spell_rog_t10_2p_bonus() : SpellScriptLoader("spell_rog_t10_2p_bonus") { } + + class spell_rog_t10_2p_bonus_AuraScript : public AuraScript + { + PrepareAuraScript(spell_rog_t10_2p_bonus_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_ROGUE_T10_2P_BONUS)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + return eventInfo.GetActor() == eventInfo.GetActionTarget(); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_rog_t10_2p_bonus_AuraScript::CheckProc); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_rog_t10_2p_bonus_AuraScript(); + } +}; + void AddSC_rogue_spell_scripts() { new spell_rog_blade_flurry(); @@ -858,4 +893,5 @@ void AddSC_rogue_spell_scripts() new spell_rog_tricks_of_the_trade_proc(); new spell_rog_honor_among_thieves(); new spell_rog_honor_among_thieves_proc(); + new spell_rog_t10_2p_bonus(); } diff --git a/src/server/scripts/Spells/spell_shaman.cpp b/src/server/scripts/Spells/spell_shaman.cpp index 41e72b1388b..ad65c7c6ec7 100644 --- a/src/server/scripts/Spells/spell_shaman.cpp +++ b/src/server/scripts/Spells/spell_shaman.cpp @@ -48,8 +48,10 @@ enum ShamanSpells SPELL_SHAMAN_ITEM_MANA_SURGE = 23571, SPELL_SHAMAN_LAVA_FLOWS_R1 = 51480, SPELL_SHAMAN_LAVA_FLOWS_TRIGGERED_R1 = 64694, + SPELL_SHAMAN_LIGHTNING_SHIELD_R1 = 26364, SPELL_SHAMAN_MANA_SPRING_TOTEM_ENERGIZE = 52032, SPELL_SHAMAN_MANA_TIDE_TOTEM = 39609, + SPELL_SHAMAN_NATURE_GUARDIAN = 31616, SPELL_SHAMAN_SATED = 57724, SPELL_SHAMAN_STORM_EARTH_AND_FIRE = 51483, SPELL_SHAMAN_TOTEM_EARTHBIND_EARTHGRAB = 64695, @@ -671,7 +673,7 @@ class spell_sha_heroism : public SpellScriptLoader } }; -// 23551 - Lightning Shield +// 23551 - Lightning Shield T2 Bonus class spell_sha_item_lightning_shield : public SpellScriptLoader { public: @@ -706,7 +708,7 @@ class spell_sha_item_lightning_shield : public SpellScriptLoader } }; -// 23552 - Lightning Shield +// 23552 - Lightning Shield T2 Bonus class spell_sha_item_lightning_shield_trigger : public SpellScriptLoader { public: @@ -718,7 +720,7 @@ class spell_sha_item_lightning_shield_trigger : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_MANA_SURGE)) + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE)) return false; return true; } @@ -753,7 +755,7 @@ class spell_sha_item_mana_surge : public SpellScriptLoader bool Validate(SpellInfo const* /*spellInfo*/) override { - if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_LIGHTNING_SHIELD_DAMAGE)) + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_ITEM_MANA_SURGE)) return false; return true; } @@ -865,6 +867,51 @@ class spell_sha_lava_lash : public SpellScriptLoader } }; +// -324 - Lightning Shield +class spell_sha_lightning_shield : public SpellScriptLoader +{ +public: + spell_sha_lightning_shield() : SpellScriptLoader("spell_sha_lightning_shield") { } + + class spell_sha_lightning_shield_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_lightning_shield_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_LIGHTNING_SHIELD_R1)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetActionTarget()) + return true; + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerSpell = sSpellMgr->GetSpellWithRank(SPELL_SHAMAN_LIGHTNING_SHIELD_R1, aurEff->GetSpellInfo()->GetRank()); + + eventInfo.GetActionTarget()->CastSpell(eventInfo.GetActor(), triggerSpell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_sha_lightning_shield_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_sha_lightning_shield_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_lightning_shield_AuraScript(); + } +}; + // 52031, 52033, 52034, 52035, 52036, 58778, 58779, 58780 - Mana Spring Totem class spell_sha_mana_spring_totem : public SpellScriptLoader { @@ -924,6 +971,7 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader void HandleDummy(SpellEffIndex /*effIndex*/) { if (Unit* caster = GetCaster()) + { if (Unit* unitTarget = GetHitUnit()) { if (unitTarget->getPowerType() == POWER_MANA) @@ -938,6 +986,7 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader caster->CastCustomSpell(unitTarget, SPELL_SHAMAN_MANA_TIDE_TOTEM, &effBasePoints0, NULL, NULL, true, NULL, NULL, GetOriginalCaster()->GetGUID()); } } + } } void Register() override @@ -952,6 +1001,56 @@ class spell_sha_mana_tide_totem : public SpellScriptLoader } }; +// -30881 - Nature's Guardian +class spell_sha_nature_guardian : public SpellScriptLoader +{ +public: + spell_sha_nature_guardian() : SpellScriptLoader("spell_sha_nature_guardian") { } + + class spell_sha_nature_guardian_AuraScript : public AuraScript + { + PrepareAuraScript(spell_sha_nature_guardian_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_SHAMAN_NATURE_GUARDIAN)) + return false; + return true; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + int32 healthpct = aurEff->GetSpellInfo()->Effects[EFFECT_1].CalcValue(); // %s2 - the 30% threshold for health + + if (Unit* target = eventInfo.GetActionTarget()) + { + if (target->HealthBelowPctDamaged(healthpct, eventInfo.GetDamageInfo()->GetDamage())) + { + + uint32 bp = CalculatePct(target->GetMaxHealth(), aurEff->GetAmount()); + target->CastCustomSpell(SPELL_SHAMAN_NATURE_GUARDIAN, SPELLVALUE_BASE_POINT0, bp, target, true, nullptr, aurEff); + + // Threat reduction is around 10% confirmed in retail and from wiki + Unit* attacker = eventInfo.GetActor(); + if (attacker->IsAlive()) + attacker->getThreatManager().modifyThreatPercent(target, -10); + } + } + } + + void Register() override + { + OnEffectProc += AuraEffectProcFn(spell_sha_nature_guardian_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_sha_nature_guardian_AuraScript(); + } +}; + // 6495 - Sentry Totem class spell_sha_sentry_totem : public SpellScriptLoader { @@ -1085,8 +1184,10 @@ void AddSC_shaman_spell_scripts() new spell_sha_item_mana_surge(); new spell_sha_item_t10_elemental_2p_bonus(); new spell_sha_lava_lash(); + new spell_sha_lightning_shield(); new spell_sha_mana_spring_totem(); new spell_sha_mana_tide_totem(); + new spell_sha_nature_guardian(); new spell_sha_sentry_totem(); new spell_sha_thunderstorm(); new spell_sha_totemic_mastery(); diff --git a/src/server/scripts/Spells/spell_warlock.cpp b/src/server/scripts/Spells/spell_warlock.cpp index a0a6189cbe2..5e0074bf9f7 100644 --- a/src/server/scripts/Spells/spell_warlock.cpp +++ b/src/server/scripts/Spells/spell_warlock.cpp @@ -50,6 +50,12 @@ enum WarlockSpells SPELL_WARLOCK_IMPROVED_HEALTH_FUNNEL_BUFF_R2 = 60956, SPELL_WARLOCK_LIFE_TAP_ENERGIZE = 31818, SPELL_WARLOCK_LIFE_TAP_ENERGIZE_2 = 32553, + SPELL_WARLOCK_NETHER_PROTECTION_HOLY = 54370, + SPELL_WARLOCK_NETHER_PROTECTION_FIRE = 54371, + SPELL_WARLOCK_NETHER_PROTECTION_FROST = 54372, + SPELL_WARLOCK_NETHER_PROTECTION_ARCANE = 54373, + SPELL_WARLOCK_NETHER_PROTECTION_SHADOW = 54374, + SPELL_WARLOCK_NETHER_PROTECTION_NATURE = 54375, SPELL_WARLOCK_SOULSHATTER = 32835, SPELL_WARLOCK_SIPHON_LIFE_HEAL = 63106, SPELL_WARLOCK_UNSTABLE_AFFLICTION_DISPEL = 31117 @@ -377,6 +383,8 @@ class spell_warl_demonic_empowerment : public SpellScriptLoader case CREATURE_FAMILY_IMP: targetCreature->CastSpell(targetCreature, SPELL_WARLOCK_DEMONIC_EMPOWERMENT_IMP, true); break; + default: + break; } } } @@ -682,6 +690,95 @@ class spell_warl_life_tap : public SpellScriptLoader } }; +// -30299 - Nether Protection +class spell_warl_nether_protection : public SpellScriptLoader +{ +public: + spell_warl_nether_protection() : SpellScriptLoader("spell_warl_nether_protection") { } + + class spell_warl_nether_protection_AuraScript : public AuraScript + { + PrepareAuraScript(spell_warl_nether_protection_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override + { + if (!sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_HOLY) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_FIRE) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_FROST) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_ARCANE) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_SHADOW) || + !sSpellMgr->GetSpellInfo(SPELL_WARLOCK_NETHER_PROTECTION_NATURE)) + return false; + return true; + } + + bool CheckProc(ProcEventInfo& eventInfo) + { + if (eventInfo.GetDamageInfo()) + { + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + case SPELL_SCHOOL_FIRE: + case SPELL_SCHOOL_NATURE: + case SPELL_SCHOOL_FROST: + case SPELL_SCHOOL_SHADOW: + case SPELL_SCHOOL_ARCANE: + return true; + default: + break; + } + } + + return false; + } + + void HandleProc(AuraEffect const* aurEff, ProcEventInfo& eventInfo) + { + PreventDefaultAction(); + uint32 triggerspell = 0; + + switch (GetFirstSchoolInMask(eventInfo.GetDamageInfo()->GetSchoolMask())) + { + case SPELL_SCHOOL_HOLY: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_HOLY; + break; + case SPELL_SCHOOL_FIRE: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_FIRE; + break; + case SPELL_SCHOOL_NATURE: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_NATURE; + break; + case SPELL_SCHOOL_FROST: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_FROST; + break; + case SPELL_SCHOOL_SHADOW: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_SHADOW; + break; + case SPELL_SCHOOL_ARCANE: + triggerspell = SPELL_WARLOCK_NETHER_PROTECTION_ARCANE; + break; + default: + return; + } + + if (Unit* target = eventInfo.GetActionTarget()) + target->CastSpell(target, triggerspell, true, nullptr, aurEff); + } + + void Register() override + { + DoCheckProc += AuraCheckProcFn(spell_warl_nether_protection_AuraScript::CheckProc); + OnEffectProc += AuraEffectProcFn(spell_warl_nether_protection_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL); + } + }; + + AuraScript* GetAuraScript() const override + { + return new spell_warl_nether_protection_AuraScript(); + } +}; + // 18541 - Ritual of Doom Effect class spell_warl_ritual_of_doom_effect : public SpellScriptLoader { @@ -917,6 +1014,7 @@ void AddSC_warlock_spell_scripts() new spell_warl_haunt(); new spell_warl_health_funnel(); new spell_warl_life_tap(); + new spell_warl_nether_protection(); new spell_warl_ritual_of_doom_effect(); new spell_warl_seed_of_corruption(); new spell_warl_shadow_ward(); diff --git a/src/server/scripts/World/go_scripts.cpp b/src/server/scripts/World/go_scripts.cpp index 3094ecd660a..8f2ea5887d2 100644 --- a/src/server/scripts/World/go_scripts.cpp +++ b/src/server/scripts/World/go_scripts.cpp @@ -1171,7 +1171,7 @@ public: player->CastSpell(player, SPELL_CLEANSING_SOUL); player->SetStandState(UNIT_STAND_STATE_SIT); } - return true; + return true; } }; diff --git a/src/server/scripts/World/item_scripts.cpp b/src/server/scripts/World/item_scripts.cpp index f4241ba0819..52174e1b012 100644 --- a/src/server/scripts/World/item_scripts.cpp +++ b/src/server/scripts/World/item_scripts.cpp @@ -57,18 +57,18 @@ public: //for special scripts switch (itemId) { - case 24538: + case 24538: if (player->GetAreaId() != 3628) disabled = true; - break; - case 34489: + break; + case 34489: if (player->GetZoneId() != 4080) disabled = true; - break; - case 34475: - if (const SpellInfo* spellInfo = sSpellMgr->GetSpellInfo(SPELL_ARCANE_CHARGES)) + break; + case 34475: + if (SpellInfo const* spellInfo = sSpellMgr->GetSpellInfo(SPELL_ARCANE_CHARGES)) Spell::SendCastResult(player, spellInfo, 1, SPELL_FAILED_NOT_ON_GROUND); - break; + break; } // allow use in flight only diff --git a/src/server/shared/DataStores/DBCStore.h b/src/server/shared/DataStores/DBCStore.h index 7c2cab1e36a..b93bbdaea12 100644 --- a/src/server/shared/DataStores/DBCStore.h +++ b/src/server/shared/DataStores/DBCStore.h @@ -25,121 +25,6 @@ #include "DatabaseWorkerPool.h" #include "Implementation/WorldDatabase.h" #include "DatabaseEnv.h" -#include <G3D/Vector3.h> -#include <G3D/AABox.h> - - // Structures for M4 file. Source: https://wowdev.wiki -template<typename T> -struct M2SplineKey -{ - T p0; - T p1; - T p2; -}; - -struct M2Header -{ - char Magic[4]; // "MD20" - uint32 Version; // The version of the format. - uint32 lName; // Length of the model's name including the trailing \0 - uint32 ofsName; // Offset to the name, it seems like models can get reloaded by this name.should be unique, i guess. - uint32 GlobalModelFlags; // 0x0001: tilt x, 0x0002: tilt y, 0x0008: add 2 fields in header, 0x0020: load .phys data (MoP+), 0x0080: has _lod .skin files (MoP?+), 0x0100: is camera related. - uint32 nGlobalSequences; - uint32 ofsGlobalSequences; // A list of timestamps. - uint32 nAnimations; - uint32 ofsAnimations; // Information about the animations in the model. - uint32 nAnimationLookup; - uint32 ofsAnimationLookup; // Mapping of global IDs to the entries in the Animation sequences block. - uint32 nBones; // MAX_BONES = 0x100 - uint32 ofsBones; // Information about the bones in this model. - uint32 nKeyBoneLookup; - uint32 ofsKeyBoneLookup; // Lookup table for key skeletal bones. - uint32 nVertices; - uint32 ofsVertices; // Vertices of the model. - uint32 nViews; // Views (LOD) are now in .skins. - uint32 nSubmeshAnimations; - uint32 ofsSubmeshAnimations; // Submesh color and alpha animations definitions. - uint32 nTextures; - uint32 ofsTextures; // Textures of this model. - uint32 nTransparency; - uint32 ofsTransparency; // Transparency of textures. - uint32 nUVAnimation; - uint32 ofsUVAnimation; - uint32 nTexReplace; - uint32 ofsTexReplace; // Replaceable Textures. - uint32 nRenderFlags; - uint32 ofsRenderFlags; // Blending modes / render flags. - uint32 nBoneLookupTable; - uint32 ofsBoneLookupTable; // A bone lookup table. - uint32 nTexLookup; - uint32 ofsTexLookup; // The same for textures. - uint32 nTexUnits; // possibly removed with cata?! - uint32 ofsTexUnits; // And texture units. Somewhere they have to be too. - uint32 nTransLookup; - uint32 ofsTransLookup; // Everything needs its lookup. Here are the transparencies. - uint32 nUVAnimLookup; - uint32 ofsUVAnimLookup; - G3D::AABox BoundingBox; // min/max( [1].z, 2.0277779f ) - 0.16f seems to be the maximum camera height - float BoundingSphereRadius; - G3D::AABox CollisionBox; - float CollisionSphereRadius; - uint32 nBoundingTriangles; - uint32 ofsBoundingTriangles; // Our bounding volumes. Similar structure like in the old ofsViews. - uint32 nBoundingVertices; - uint32 ofsBoundingVertices; - uint32 nBoundingNormals; - uint32 ofsBoundingNormals; - uint32 nAttachments; - uint32 ofsAttachments; // Attachments are for weapons etc. - uint32 nAttachLookup; - uint32 ofsAttachLookup; // Of course with a lookup. - uint32 nEvents; - uint32 ofsEvents; // Used for playing sounds when dying and a lot else. - uint32 nLights; - uint32 ofsLights; // Lights are mainly used in loginscreens but in wands and some doodads too. - uint32 nCameras; // Format of Cameras changed with version 271! - uint32 ofsCameras; // The cameras are present in most models for having a model in the Character-Tab. - uint32 nCameraLookup; - uint32 ofsCameraLookup; // And lookup-time again. - uint32 nRibbonEmitters; - uint32 ofsRibbonEmitters; // Things swirling around. See the CoT-entrance for light-trails. - uint32 nParticleEmitters; - uint32 ofsParticleEmitters; // Spells and weapons, doodads and loginscreens use them. Blood dripping of a blade? Particles. - uint32 nBlendMaps; // This has to deal with blending. Exists IFF (flags & 0x8) != 0. When set, textures blending is overriden by the associated array. See M2/WotLK#Blend_mode_overrides - uint32 ofsBlendMaps; // Same as above. Points to an array of uint16 of nBlendMaps entries -- From WoD information.}; -}; - -struct M2Array -{ - uint32_t number; - uint32 offset_elements; -}; -struct M2Track -{ - uint16_t interpolation_type; - uint16_t global_sequence; - M2Array timestamps; - M2Array values; -}; - -struct M2Camera -{ - uint32_t type; // 0: portrait, 1: characterinfo; -1: else (flyby etc.); referenced backwards in the lookup table. - float fov; // No radians, no degrees. Multiply by 35 to get degrees. - float far_clip; - float near_clip; - M2Track positions; // How the camera's position moves. Should be 3*3 floats. - G3D::Vector3 position_base; - M2Track target_positions; // How the target moves. Should be 3*3 floats. - G3D::Vector3 target_position_base; - M2Track rolldata; // The camera can have some roll-effect. Its 0 to 2*Pi. -}; - -struct FlyByCamera -{ - uint32 timeStamp; - G3D::Vector4 locations; -}; struct SqlDbc { diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp index 2b5553b4bb0..f4d736ac675 100644 --- a/src/server/worldserver/Main.cpp +++ b/src/server/worldserver/Main.cpp @@ -24,6 +24,7 @@ #include <openssl/crypto.h> #include <boost/asio/io_service.hpp> #include <boost/asio/deadline_timer.hpp> +#include <boost/filesystem/path.hpp> #include <boost/program_options.hpp> #include "Common.h" @@ -53,6 +54,7 @@ #include "AppenderDB.h" using namespace boost::program_options; +namespace fs = boost::filesystem; #ifndef _TRINITY_CORE_CONFIG #define _TRINITY_CORE_CONFIG "worldserver.conf" @@ -90,14 +92,14 @@ void ClearOnlineAccounts(); void ShutdownCLIThread(std::thread* cliThread); void ShutdownThreadPool(std::vector<std::thread>& threadPool); bool LoadRealmInfo(); -variables_map GetConsoleArguments(int argc, char** argv, std::string& cfg_file, std::string& cfg_service); +variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& cfg_service); /// Launch the Trinity server extern int main(int argc, char** argv) { signal(SIGABRT, &Trinity::AbortHandler); - std::string configFile = _TRINITY_CORE_CONFIG; + auto configFile = fs::absolute(_TRINITY_CORE_CONFIG); std::string configService; auto vm = GetConsoleArguments(argc, argv, configFile, configService); @@ -115,7 +117,9 @@ extern int main(int argc, char** argv) #endif std::string configError; - if (!sConfigMgr->LoadInitial(configFile, configError)) + if (!sConfigMgr->LoadInitial(configFile.generic_string(), + std::vector<std::string>(argv, argv + argc), + configError)) { printf("Error in config file: %s\n", configError.c_str()); return 1; @@ -136,7 +140,7 @@ extern int main(int argc, char** argv) TC_LOG_INFO("server.worldserver", " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\"); TC_LOG_INFO("server.worldserver", " C O R E /\\___/"); TC_LOG_INFO("server.worldserver", "http://TrinityCore.org \\/__/\n"); - TC_LOG_INFO("server.worldserver", "Using configuration file %s.", configFile.c_str()); + TC_LOG_INFO("server.worldserver", "Using configuration file %s.", sConfigMgr->GetFilename().c_str()); TC_LOG_INFO("server.worldserver", "Using SSL version: %s (library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION)); TC_LOG_INFO("server.worldserver", "Using Boost version: %i.%i.%i", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100); @@ -571,7 +575,7 @@ void ClearOnlineAccounts() /// @} -variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile, std::string& configService) +variables_map GetConsoleArguments(int argc, char** argv, fs::path& configFile, std::string& configService) { // Silences warning about configService not be used if the OS is not Windows (void)configService; @@ -580,7 +584,8 @@ variables_map GetConsoleArguments(int argc, char** argv, std::string& configFile all.add_options() ("help,h", "print usage message") ("version,v", "print version build info") - ("config,c", value<std::string>(&configFile)->default_value(_TRINITY_CORE_CONFIG), "use <arg> as configuration file") + ("config,c", value<fs::path>(&configFile)->default_value(fs::absolute(_TRINITY_CORE_CONFIG)), + "use <arg> as configuration file") ; #ifdef _WIN32 options_description win("Windows platform specific options"); diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist index 1d425e68246..ccb784f8c2b 100644 --- a/src/server/worldserver/worldserver.conf.dist +++ b/src/server/worldserver/worldserver.conf.dist @@ -725,7 +725,7 @@ CharacterCreating.Disabled.RaceMask = 0 # 2 - (Disabled, Paladin) # 4 - (Disabled, Hunter) # 8 - (Disabled, Rogue) -# 16 - (Disabled, Undead) +# 16 - (Disabled, Priest) # 32 - (Disabled, Death Knight) # 64 - (Disabled, Shaman) # 128 - (Disabled, Mage) @@ -1658,6 +1658,14 @@ ListenRange.TextEmote = 40 ListenRange.Yell = 300 # +# Creature.MovingStopTimeForPlayer +# Description: Time (in milliseconds) during which creature will not move after +# interaction with player. +# Default: 180000 + +Creature.MovingStopTimeForPlayer = 180000 + +# ################################################################################################### ################################################################################################### @@ -1869,6 +1877,13 @@ GM.LowerSecurity = 0 GM.TicketSystem.ChanceOfGMSurvey = 50 # +# GM.ForceShutdownThreshold +# Description: Minimum shutdown time in seconds before 'force' is required if other players are connected. +# Default: 30 + +GM.ForceShutdownThreshold = 30 + +# ################################################################################################### ################################################################################################### diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp index 1d84fc75d27..9d3dc47bce0 100644 --- a/src/tools/map_extractor/System.cpp +++ b/src/tools/map_extractor/System.cpp @@ -1041,7 +1041,7 @@ void ExtractCameraFiles(int locale, bool basicLocale) std::vector<std::string> camerafiles; size_t cam_count = camdbc.getRecordCount(); - for (uint32 i = 0; i < cam_count; ++i) + for (size_t i = 0; i < cam_count; ++i) { std::string camFile(camdbc.getRecord(i).getString(1)); size_t loc = camFile.find(".mdx"); |