aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/Configuration/Config.cpp6
-rw-r--r--src/common/Configuration/Config.h6
-rw-r--r--src/common/Cryptography/SHA1.cpp8
-rw-r--r--src/common/Cryptography/SHA1.h5
-rw-r--r--src/common/Utilities/Containers.h22
-rw-r--r--src/common/Utilities/StartProcess.cpp12
-rw-r--r--src/common/Utilities/Timer.h4
-rw-r--r--src/server/authserver/Main.cpp17
-rw-r--r--src/server/authserver/Server/AuthSession.cpp35
-rw-r--r--src/server/authserver/Server/AuthSession.h2
-rw-r--r--src/server/authserver/authserver.conf.dist1
-rw-r--r--src/server/database/Database/DatabaseWorkerPool.h6
-rw-r--r--src/server/database/Database/Implementation/WorldDatabase.cpp4
-rw-r--r--src/server/database/Database/QueryCallback.h (renamed from src/common/Threading/Callback.h)6
-rw-r--r--src/server/database/Updater/DBUpdater.cpp14
-rw-r--r--src/server/database/Updater/UpdateFetcher.cpp16
-rw-r--r--src/server/database/Updater/UpdateFetcher.h1
-rw-r--r--src/server/game/AI/CoreAI/CombatAI.cpp2
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.cpp6
-rw-r--r--src/server/game/AI/CoreAI/PassiveAI.h2
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp6
-rw-r--r--src/server/game/AI/CoreAI/PetAI.h2
-rw-r--r--src/server/game/AI/CoreAI/UnitAI.h1
-rw-r--r--src/server/game/AI/CreatureAI.cpp37
-rw-r--r--src/server/game/AI/CreatureAI.h5
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.cpp1191
-rw-r--r--src/server/game/AI/PlayerAI/PlayerAI.h64
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.cpp60
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedCreature.h4
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedEscortAI.cpp2
-rw-r--r--src/server/game/AI/ScriptedAI/ScriptedFollowerAI.cpp2
-rw-r--r--src/server/game/AI/SmartScripts/SmartAI.cpp15
-rw-r--r--src/server/game/AI/SmartScripts/SmartScript.cpp23
-rw-r--r--src/server/game/Accounts/RBAC.h6
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp11
-rw-r--r--src/server/game/Battlegrounds/Zones/BattlegroundIC.cpp6
-rw-r--r--src/server/game/Conditions/ConditionMgr.cpp2
-rw-r--r--src/server/game/DataStores/DBCEnums.h16
-rw-r--r--src/server/game/DataStores/DBCStores.cpp245
-rw-r--r--src/server/game/DataStores/DBCStores.h4
-rw-r--r--src/server/game/DataStores/DBCStructure.h12
-rw-r--r--src/server/game/DataStores/M2Stores.cpp266
-rw-r--r--src/server/game/DataStores/M2Stores.h36
-rw-r--r--src/server/game/DataStores/M2Structure.h133
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp65
-rw-r--r--src/server/game/Entities/Creature/Creature.h39
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp42
-rw-r--r--src/server/game/Entities/Item/ItemEnchantmentMgr.cpp6
-rw-r--r--src/server/game/Entities/Object/Object.cpp44
-rw-r--r--src/server/game/Entities/Object/Object.h14
-rw-r--r--src/server/game/Entities/Player/CinematicMgr.cpp174
-rw-r--r--src/server/game/Entities/Player/CinematicMgr.h60
-rw-r--r--src/server/game/Entities/Player/Player.cpp276
-rw-r--r--src/server/game/Entities/Player/Player.h67
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp691
-rw-r--r--src/server/game/Entities/Unit/Unit.h30
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp34
-rw-r--r--src/server/game/Grids/Notifiers/GridNotifiers.h4
-rw-r--r--src/server/game/Guilds/Guild.cpp10
-rw-r--r--src/server/game/Handlers/GroupHandler.cpp19
-rw-r--r--src/server/game/Handlers/GuildHandler.cpp4
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp18
-rw-r--r--src/server/game/Handlers/QueryHandler.cpp4
-rw-r--r--src/server/game/Loot/LootMgr.cpp2
-rw-r--r--src/server/game/Maps/MapManager.h8
-rw-r--r--src/server/game/Miscellaneous/Language.h6
-rw-r--r--src/server/game/Miscellaneous/SharedDefines.h69
-rw-r--r--src/server/game/Movement/MotionMaster.cpp4
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/ConfusedMovementGenerator.cpp5
-rw-r--r--src/server/game/Movement/MovementGenerators/RandomMovementGenerator.cpp4
-rw-r--r--src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp19
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp6
-rwxr-xr-xsrc/server/game/Movement/MovementGenerators/WaypointMovementGenerator.h2
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp2
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.cpp204
-rw-r--r--src/server/game/Server/Protocol/PacketLog.cpp4
-rw-r--r--src/server/game/Server/WorldSocket.cpp3
-rw-r--r--src/server/game/Spells/Auras/SpellAuraDefines.h2
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp7
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.cpp32
-rw-r--r--src/server/game/Spells/Auras/SpellAuras.h10
-rw-r--r--src/server/game/Spells/Spell.cpp131
-rw-r--r--src/server/game/Spells/Spell.h8
-rw-r--r--src/server/game/Spells/SpellEffects.cpp47
-rw-r--r--src/server/game/Spells/SpellHistory.cpp2
-rw-r--r--src/server/game/Spells/SpellInfo.cpp11
-rw-r--r--src/server/game/Spells/SpellMgr.cpp132
-rw-r--r--src/server/game/Spells/SpellMgr.h26
-rw-r--r--src/server/game/World/World.cpp10
-rw-r--r--src/server/game/World/World.h8
-rw-r--r--src/server/scripts/Commands/cs_character.cpp2
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp3
-rw-r--r--src/server/scripts/Commands/cs_misc.cpp2
-rw-r--r--src/server/scripts/Commands/cs_modify.cpp456
-rw-r--r--src/server/scripts/Commands/cs_npc.cpp121
-rw-r--r--src/server/scripts/Commands/cs_pet.cpp63
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp2
-rw-r--r--src/server/scripts/Commands/cs_server.cpp78
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp12
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_kiljaeden.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_muru.cpp833
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/sunwell_plateau.h3
-rw-r--r--src/server/scripts/EasternKingdoms/Uldaman/boss_archaedas.cpp14
-rw-r--r--src/server/scripts/EasternKingdoms/Uldaman/instance_uldaman.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/ZulGurub/boss_mandokir.cpp9
-rw-r--r--src/server/scripts/Kalimdor/BlackfathomDeeps/instance_blackfathom_deeps.cpp2
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp12
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_aeonus.cpp46
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/TheBlackMorass/boss_temporus.cpp52
-rw-r--r--src/server/scripts/Kalimdor/zone_the_barrens.cpp43
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/AzjolNerub/boss_anubarak.cpp2
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_northrend_beasts.cpp8
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/ForgeOfSouls/boss_devourer_of_souls.cpp6
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp28
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp49
-rw-r--r--src/server/scripts/Northrend/IsleOfConquest/boss_ioc_horde_alliance.cpp132
-rw-r--r--src/server/scripts/Northrend/IsleOfConquest/isle_of_conquest.cpp (renamed from src/server/scripts/Northrend/isle_of_conquest.cpp)53
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp5
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_kelthuzad.cpp6
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp6
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_freya.cpp38
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_general_vezax.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_hodir.cpp14
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_ignis.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_kologarn.cpp2
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp4
-rw-r--r--src/server/scripts/Northrend/VaultOfArchavon/boss_toravon.cpp2
-rw-r--r--src/server/scripts/Northrend/northrend_script_loader.cpp6
-rw-r--r--src/server/scripts/Northrend/zone_grizzly_hills.cpp260
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_illidan.cpp32
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp9
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp2
-rw-r--r--src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_nethekurse.cpp19
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_astromancer.cpp151
-rw-r--r--src/server/scripts/Spells/spell_dk.cpp192
-rw-r--r--src/server/scripts/Spells/spell_druid.cpp231
-rw-r--r--src/server/scripts/Spells/spell_generic.cpp58
-rw-r--r--src/server/scripts/Spells/spell_hunter.cpp106
-rw-r--r--src/server/scripts/Spells/spell_item.cpp505
-rw-r--r--src/server/scripts/Spells/spell_mage.cpp38
-rw-r--r--src/server/scripts/Spells/spell_paladin.cpp57
-rw-r--r--src/server/scripts/Spells/spell_priest.cpp46
-rw-r--r--src/server/scripts/Spells/spell_rogue.cpp38
-rw-r--r--src/server/scripts/Spells/spell_shaman.cpp109
-rw-r--r--src/server/scripts/Spells/spell_warlock.cpp98
-rw-r--r--src/server/scripts/World/go_scripts.cpp2
-rw-r--r--src/server/scripts/World/item_scripts.cpp14
-rw-r--r--src/server/shared/DataStores/DBCStore.h115
-rw-r--r--src/server/worldserver/Main.cpp17
-rw-r--r--src/server/worldserver/worldserver.conf.dist17
-rw-r--r--src/tools/map_extractor/System.cpp2
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");