aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/Common.h6
-rw-r--r--src/common/Utilities/StartProcess.cpp267
-rw-r--r--src/common/Utilities/StartProcess.h67
-rw-r--r--src/server/database/CMakeLists.txt1
-rw-r--r--src/server/database/Updater/DBUpdater.cpp70
-rw-r--r--src/server/game/AI/CoreAI/PetAI.cpp6
-rw-r--r--src/server/game/Achievements/AchievementMgr.cpp2
-rw-r--r--src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp45
-rw-r--r--src/server/game/CMakeLists.txt27
-rw-r--r--src/server/game/Chat/Chat.cpp26
-rw-r--r--src/server/game/Chat/Chat.h4
-rw-r--r--src/server/game/DataStores/DBCStores.cpp247
-rw-r--r--src/server/game/DataStores/DBCStores.h5
-rw-r--r--src/server/game/DataStores/DBCStructure.h12
-rw-r--r--src/server/game/DataStores/DBCfmt.h3
-rw-r--r--src/server/game/DungeonFinding/LFGMgr.cpp2
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.cpp6
-rw-r--r--src/server/game/DungeonFinding/LFGScripts.h2
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp44
-rw-r--r--src/server/game/Entities/Creature/Creature.h1
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp8
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h4
-rw-r--r--src/server/game/Entities/Object/Object.cpp23
-rw-r--r--src/server/game/Entities/Object/Object.h1
-rw-r--r--src/server/game/Entities/Pet/Pet.cpp11
-rw-r--r--src/server/game/Entities/Player/Player.cpp291
-rw-r--r--src/server/game/Entities/Player/Player.h113
-rw-r--r--src/server/game/Entities/Unit/Unit.cpp204
-rw-r--r--src/server/game/Entities/Unit/Unit.h37
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp100
-rw-r--r--src/server/game/Globals/ObjectMgr.h3
-rw-r--r--src/server/game/Handlers/CharacterHandler.cpp23
-rw-r--r--src/server/game/Handlers/MiscHandler.cpp10
-rw-r--r--src/server/game/Handlers/MovementHandler.cpp2
-rw-r--r--src/server/game/Handlers/SpellHandler.cpp10
-rw-r--r--src/server/game/Instances/InstanceScript.cpp14
-rw-r--r--src/server/game/Instances/InstanceScript.h8
-rw-r--r--src/server/game/Maps/Map.cpp20
-rw-r--r--src/server/game/Maps/MapManager.cpp2
-rw-r--r--src/server/game/Maps/MapManager.h8
-rw-r--r--src/server/game/Maps/MapScripts.cpp (renamed from src/server/game/Scripting/MapScripts.cpp)7
-rw-r--r--src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp6
-rw-r--r--src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp6
-rw-r--r--src/server/game/Scripting/ScriptMgr.cpp1124
-rw-r--r--src/server/game/Scripting/ScriptMgr.h72
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.cpp1520
-rw-r--r--src/server/game/Scripting/ScriptReloadMgr.h81
-rw-r--r--src/server/game/Spells/Auras/SpellAuraEffects.cpp48
-rw-r--r--src/server/game/Spells/SpellScript.cpp7
-rw-r--r--src/server/game/Spells/SpellScript.h11
-rw-r--r--src/server/game/World/World.cpp18
-rw-r--r--src/server/game/World/World.h7
-rw-r--r--src/server/scripts/CMakeLists.txt253
-rw-r--r--src/server/scripts/Commands/cs_debug.cpp17
-rw-r--r--src/server/scripts/Commands/cs_modify.cpp22
-rw-r--r--src/server/scripts/Commands/cs_reload.cpp10
-rw-r--r--src/server/scripts/Commands/cs_reset.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp34
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp8
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp32
-rw-r--r--src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp4
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp2
-rw-r--r--src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp10
-rw-r--r--src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp2
-rw-r--r--src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp4
-rw-r--r--src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp6
-rw-r--r--src/server/scripts/Kalimdor/kalimdor_script_loader.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_azshara.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_desolace.cpp2
-rw-r--r--src/server/scripts/Kalimdor/zone_durotar.cpp4
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp6
-rw-r--r--src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp4
-rw-r--r--src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp2
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp2
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp2
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp6
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp12
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp8
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp4
-rw-r--r--src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp31
-rw-r--r--src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp10
-rw-r--r--src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp4
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp2
-rw-r--r--src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp6
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp2
-rw-r--r--src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp2
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_illidan.cpp4
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp4
-rw-r--r--src/server/scripts/Outland/BlackTemple/boss_supremus.cpp4
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp4
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp2
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp1051
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/instance_the_slave_pens.cpp120
-rw-r--r--src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h38
-rw-r--r--src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp2
-rw-r--r--src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp6
-rw-r--r--src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp6
-rw-r--r--src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp2
-rw-r--r--src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp2
-rw-r--r--src/server/scripts/Outland/outland_script_loader.cpp2
-rw-r--r--src/server/scripts/Outland/zone_hellfire_peninsula.cpp8
-rw-r--r--src/server/scripts/Pet/pet_dk.cpp4
-rw-r--r--src/server/scripts/ScriptLoader.cpp51
-rw-r--r--src/server/scripts/ScriptLoader.cpp.in.cmake64
-rw-r--r--src/server/scripts/ScriptPCH.cpp (renamed from src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp)0
-rw-r--r--src/server/scripts/ScriptPCH.h (renamed from src/server/scripts/PrecompiledHeaders/ScriptPCH.h)0
-rw-r--r--src/server/scripts/World/boss_emerald_dragons.cpp2
-rw-r--r--src/server/shared/DataStores/DBCStore.h115
-rw-r--r--src/server/worldserver/CMakeLists.txt6
-rw-r--r--src/server/worldserver/Main.cpp2
-rw-r--r--src/server/worldserver/worldserver.conf.dist75
-rw-r--r--src/tools/map_extractor/System.cpp74
120 files changed, 5854 insertions, 975 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 436620e2900..0428738f2dd 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -71,7 +71,9 @@ target_link_libraries(common
openssl
valgrind
threads
- jemalloc)
+ jemalloc
+ PRIVATE
+ process)
add_dependencies(common revision_data.h)
diff --git a/src/common/Common.h b/src/common/Common.h
index 8216c519746..aa04abacd30 100644
--- a/src/common/Common.h
+++ b/src/common/Common.h
@@ -44,6 +44,8 @@
#include <cerrno>
#include <csignal>
+#include <boost/optional.hpp>
+#include <boost/utility/in_place_factory.hpp>
#include <boost/functional/hash.hpp>
#include "Debugging/Errors.h"
@@ -152,6 +154,10 @@ typedef std::vector<std::string> StringVector;
#define MAX_QUERY_LEN 32*1024
+//! Optional helper class to wrap optional values within.
+template <typename T>
+using Optional = boost::optional<T>;
+
namespace Trinity
{
//! std::make_unique implementation (TODO: remove this once C++14 is supported)
diff --git a/src/common/Utilities/StartProcess.cpp b/src/common/Utilities/StartProcess.cpp
new file mode 100644
index 00000000000..c47c02bbe87
--- /dev/null
+++ b/src/common/Utilities/StartProcess.cpp
@@ -0,0 +1,267 @@
+/*
+ * 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 "StartProcess.h"
+
+#include <atomic>
+#include <thread>
+#include <functional>
+
+#include <boost/algorithm/string/join.hpp>
+#include <boost/iostreams/stream.hpp>
+#include <boost/iostreams/copy.hpp>
+#include <boost/iostreams/concepts.hpp>
+#include <boost/iostreams/device/file_descriptor.hpp>
+#include <boost/process.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "Common.h"
+#include "Log.h"
+
+using namespace boost::process;
+using namespace boost::process::initializers;
+using namespace boost::iostreams;
+
+namespace Trinity {
+
+template<typename T>
+class TCLogSink
+{
+ T callback_;
+
+public:
+ typedef char char_type;
+ typedef sink_tag category;
+
+ // Requires a callback type which has a void(std::string) signature
+ TCLogSink(T callback)
+ : callback_(std::move(callback)) { }
+
+ std::streamsize write(const char* str, std::streamsize size)
+ {
+ callback_(std::string(str, size));
+ return size;
+ }
+};
+
+template<typename T>
+auto MakeTCLogSink(T&& callback)
+ -> TCLogSink<typename std::decay<T>::type>
+{
+ return { std::forward<T>(callback) };
+}
+
+template<typename T>
+static int CreateChildProcess(T waiter, std::string const& executable,
+ std::vector<std::string> const& args,
+ std::string const& logger, std::string const& input,
+ bool secure)
+{
+ auto outPipe = create_pipe();
+ auto errPipe = create_pipe();
+
+ Optional<file_descriptor_source> inputSource;
+
+ if (!secure)
+ {
+ TC_LOG_TRACE(logger.c_str(), "Starting process \"%s\" with arguments: \"%s\".",
+ executable.c_str(), boost::algorithm::join(args, " ").c_str());
+ }
+
+ // Start the child process
+ child c = [&]
+ {
+ if (!input.empty())
+ {
+ inputSource = file_descriptor_source(input);
+
+ // With binding stdin
+ return execute(run_exe(boost::filesystem::absolute(executable)),
+ set_args(args),
+ bind_stdin(*inputSource),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+ }
+ else
+ {
+ // Without binding stdin
+ return execute(run_exe(boost::filesystem::absolute(executable)),
+ set_args(args),
+ bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
+ bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
+ }
+ }();
+
+ file_descriptor_source outFd(outPipe.source, close_handle);
+ file_descriptor_source errFd(errPipe.source, close_handle);
+
+ auto outInfo = MakeTCLogSink([&](std::string msg)
+ {
+ TC_LOG_INFO(logger.c_str(), "%s", msg.c_str());
+ });
+
+ auto outError = MakeTCLogSink([&](std::string msg)
+ {
+ TC_LOG_ERROR(logger.c_str(), "%s", msg.c_str());
+ });
+
+ copy(outFd, outInfo);
+ copy(errFd, outError);
+
+ // Call the waiter in the current scope to prevent
+ // the streams from closing too early on leaving the scope.
+ int const result = waiter(c);
+
+ if (!secure)
+ {
+ TC_LOG_TRACE(logger.c_str(), ">> Process \"%s\" finished with return value %i.",
+ executable.c_str(), result);
+ }
+
+ if (inputSource)
+ inputSource->close();
+
+ return result;
+}
+
+int StartProcess(std::string const& executable, std::vector<std::string> const& args,
+ std::string const& logger, std::string input_file, bool secure)
+{
+ return CreateChildProcess([](child& c) -> int
+ {
+ try
+ {
+ return wait_for_exit(c);
+ }
+ catch (...)
+ {
+ return EXIT_FAILURE;
+ }
+ }, executable, args, logger, input_file, secure);
+}
+
+class AsyncProcessResultImplementation
+ : public AsyncProcessResult
+{
+ std::string const executable;
+ std::vector<std::string> const args;
+ std::string const logger;
+ std::string const input_file;
+ bool const is_secure;
+
+ std::atomic<bool> was_terminated;
+
+ // Workaround for missing move support in boost < 1.57
+ Optional<std::shared_ptr<std::future<int>>> result;
+ Optional<std::reference_wrapper<child>> my_child;
+
+public:
+ explicit AsyncProcessResultImplementation(std::string executable_, std::vector<std::string> args_,
+ std::string logger_, std::string input_file_,
+ bool secure)
+ : executable(std::move(executable_)), args(std::move(args_)),
+ logger(std::move(logger_)), input_file(input_file_),
+ is_secure(secure), was_terminated(false) { }
+
+ AsyncProcessResultImplementation(AsyncProcessResultImplementation const&) = delete;
+ AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation const&) = delete;
+ AsyncProcessResultImplementation(AsyncProcessResultImplementation&&) = delete;
+ AsyncProcessResultImplementation& operator= (AsyncProcessResultImplementation&&) = delete;
+
+ int StartProcess()
+ {
+ ASSERT(!my_child, "Process started already!");
+
+ return CreateChildProcess([&](child& c) -> int
+ {
+ int result;
+ my_child = std::reference_wrapper<child>(c);
+
+ try
+ {
+ result = wait_for_exit(c);
+ }
+ catch (...)
+ {
+ result = EXIT_FAILURE;
+ }
+
+ my_child.reset();
+ return was_terminated ? EXIT_FAILURE : result;
+
+ }, executable, args, logger, input_file, is_secure);
+ }
+
+ void SetFuture(std::future<int> result_)
+ {
+ result = std::make_shared<std::future<int>>(std::move(result_));
+ }
+
+ /// Returns the future which contains the result of the process
+ /// as soon it is finished.
+ std::future<int>& GetFutureResult() override
+ {
+ ASSERT(*result, "The process wasn't started!");
+ return **result;
+ }
+
+ /// Tries to terminate the process
+ void Terminate() override
+ {
+ if (!my_child)
+ {
+ was_terminated = true;
+ try
+ {
+ terminate(my_child->get());
+ }
+ catch(...)
+ {
+ // Do nothing
+ }
+ }
+ }
+};
+
+TC_COMMON_API std::shared_ptr<AsyncProcessResult>
+ StartAsyncProcess(std::string executable, std::vector<std::string> args,
+ std::string logger, std::string input_file, bool secure)
+{
+ auto handle = std::make_shared<AsyncProcessResultImplementation>(
+ std::move(executable), std::move(args), std::move(logger), std::move(input_file), secure);
+
+ handle->SetFuture(std::async(std::launch::async, [handle] { return handle->StartProcess(); }));
+ return handle;
+}
+
+Optional<std::string> SearchExecutableInPath(std::string const& filename)
+{
+ try
+ {
+ auto result = search_path(filename);
+ if (result.empty())
+ return boost::none;
+ else
+ return result;
+ }
+ catch (...)
+ {
+ return boost::none;
+ }
+}
+
+} // namespace Trinity
diff --git a/src/common/Utilities/StartProcess.h b/src/common/Utilities/StartProcess.h
new file mode 100644
index 00000000000..3b380bd4f4e
--- /dev/null
+++ b/src/common/Utilities/StartProcess.h
@@ -0,0 +1,67 @@
+/*
+ * 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 Process_h__
+#define Process_h__
+
+#include <future>
+#include <memory>
+#include "Common.h"
+
+namespace Trinity {
+
+/// Starts a process with the given arguments and parameters and will block
+/// until the process is finished.
+/// When an input path is given, the file will be routed to the processes stdin.
+/// When the process is marked as secure no arguments are leaked to logs.
+/// Note that most executables expect it's name as the first argument.
+TC_COMMON_API int StartProcess(std::string const& executable, std::vector<std::string> const& args,
+ std::string const& logger, std::string input_file = "",
+ bool secure = false);
+
+/// Platform and library independent representation
+/// of asynchronous process results
+class AsyncProcessResult
+{
+public:
+ virtual ~AsyncProcessResult() { }
+
+ /// Returns the future which contains the result of the process
+ /// as soon it is finished.
+ virtual std::future<int>& GetFutureResult() = 0;
+
+ /// Tries to terminate the process
+ virtual void Terminate() = 0;
+};
+
+/// Starts a process asynchronously with the given arguments and parameters and
+/// returns an AsyncProcessResult immediately which is set, when the process exits.
+/// When an input path is given, the file will be routed to the processes stdin.
+/// When the process is marked as secure no arguments are leaked to logs.
+/// Note that most executables expect it's name as the first argument.
+TC_COMMON_API std::shared_ptr<AsyncProcessResult>
+ StartAsyncProcess(std::string executable, std::vector<std::string> args,
+ std::string logger, std::string input_file = "",
+ bool secure = false);
+
+/// Searches for the given executable in the PATH variable
+/// and returns a present optional when it was found.
+TC_COMMON_API Optional<std::string> SearchExecutableInPath(std::string const& filename);
+
+} // namespace Trinity
+
+#endif // Process_h__
diff --git a/src/server/database/CMakeLists.txt b/src/server/database/CMakeLists.txt
index 236586075ee..bd2fa280ad6 100644
--- a/src/server/database/CMakeLists.txt
+++ b/src/server/database/CMakeLists.txt
@@ -52,7 +52,6 @@ add_definitions(-DTRINITY_API_EXPORT_DATABASE)
target_link_libraries(database
PUBLIC
common
- process
mysql)
set_target_properties(database
diff --git a/src/server/database/Updater/DBUpdater.cpp b/src/server/database/Updater/DBUpdater.cpp
index 5f5b519d2b3..3be81e85715 100644
--- a/src/server/database/Updater/DBUpdater.cpp
+++ b/src/server/database/Updater/DBUpdater.cpp
@@ -22,19 +22,11 @@
#include "DatabaseLoader.h"
#include "Config.h"
#include "BuiltInConfig.h"
+#include "StartProcess.h"
#include <fstream>
#include <iostream>
#include <unordered_map>
-#include <boost/process.hpp>
-#include <boost/iostreams/stream.hpp>
-#include <boost/iostreams/copy.hpp>
-#include <boost/iostreams/device/file_descriptor.hpp>
-#include <boost/system/system_error.hpp>
-
-using namespace boost::process;
-using namespace boost::process::initializers;
-using namespace boost::iostreams;
std::string DBUpdaterUtil::GetCorrectedMySQLExecutable()
{
@@ -51,19 +43,16 @@ bool DBUpdaterUtil::CheckExecutable()
{
exe.clear();
- try
- {
- exe = search_path("mysql");
- }
- catch (std::runtime_error&)
+ if (auto path = Trinity::SearchExecutableInPath("mysql"))
{
- }
+ exe = std::move(*path);
- if (!exe.empty() && exists(exe))
- {
- // Correct the path to the cli
- corrected_path() = absolute(exe).generic_string();
- return true;
+ if (!exe.empty() && exists(exe))
+ {
+ // Correct the path to the cli
+ corrected_path() = absolute(exe).generic_string();
+ return true;
+ }
}
TC_LOG_FATAL("sql.updates", "Didn't find executeable mysql binary at \'%s\' or in path, correct the path in the *.conf (\"Updates.MySqlCLIPath\").",
@@ -387,44 +376,9 @@ void DBUpdater<T>::ApplyFile(DatabaseWorkerPool<T>& pool, std::string const& hos
if (!database.empty())
args.push_back(database);
- // ToDo: use the existing query in memory as virtual file if possible
- file_descriptor_source source(path);
-
- uint32 ret;
- try
- {
- boost::process::pipe outPipe = create_pipe();
- boost::process::pipe errPipe = create_pipe();
-
- child c = execute(run_exe(
- boost::filesystem::absolute(DBUpdaterUtil::GetCorrectedMySQLExecutable()).generic_string()),
- set_args(args), bind_stdin(source), throw_on_error(),
- bind_stdout(file_descriptor_sink(outPipe.sink, close_handle)),
- bind_stderr(file_descriptor_sink(errPipe.sink, close_handle)));
-
- file_descriptor_source mysqlOutfd(outPipe.source, close_handle);
- file_descriptor_source mysqlErrfd(errPipe.source, close_handle);
-
- stream<file_descriptor_source> mysqlOutStream(mysqlOutfd);
- stream<file_descriptor_source> mysqlErrStream(mysqlErrfd);
-
- std::stringstream out;
- std::stringstream err;
-
- copy(mysqlOutStream, out);
- copy(mysqlErrStream, err);
-
- TC_LOG_INFO("sql.updates", "%s", out.str().c_str());
- TC_LOG_ERROR("sql.updates", "%s", err.str().c_str());
-
- ret = wait_for_exit(c);
- }
- catch (boost::system::system_error&)
- {
- ret = EXIT_FAILURE;
- }
-
- source.close();
+ // Invokes a mysql process which doesn't leak credentials to logs
+ int const ret = Trinity::StartProcess(DBUpdaterUtil::GetCorrectedMySQLExecutable(), args,
+ "sql.updates", path.generic_string(), true);
if (ret != EXIT_SUCCESS)
{
diff --git a/src/server/game/AI/CoreAI/PetAI.cpp b/src/server/game/AI/CoreAI/PetAI.cpp
index bbf4f4e02ce..68554722db6 100644
--- a/src/server/game/AI/CoreAI/PetAI.cpp
+++ b/src/server/game/AI/CoreAI/PetAI.cpp
@@ -245,9 +245,9 @@ void PetAI::UpdateAI(uint32 diff)
}
// Update speed as needed to prevent dropping too far behind and despawning
- me->UpdateSpeed(MOVE_RUN, true);
- me->UpdateSpeed(MOVE_WALK, true);
- me->UpdateSpeed(MOVE_FLIGHT, true);
+ me->UpdateSpeed(MOVE_RUN);
+ me->UpdateSpeed(MOVE_WALK);
+ me->UpdateSpeed(MOVE_FLIGHT);
}
diff --git a/src/server/game/Achievements/AchievementMgr.cpp b/src/server/game/Achievements/AchievementMgr.cpp
index ebc814405bf..87f5ff6ce5c 100644
--- a/src/server/game/Achievements/AchievementMgr.cpp
+++ b/src/server/game/Achievements/AchievementMgr.cpp
@@ -1533,7 +1533,7 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
//! Since no common attributes were found, (not even in titleRewardFlags field)
//! we explicitly check by ID. Maybe in the future we could move the achievement_reward
//! condition fields to the condition system.
- if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->GetByteValue(PLAYER_BYTES_3, 0) : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
+ if (uint32 titleId = reward->titleId[achievement->ID == 1793 ? GetPlayer()->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) : (GetPlayer()->GetTeam() == ALLIANCE ? 0 : 1)])
if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
GetPlayer()->SetTitle(titleEntry);
diff --git a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
index 5acb56b5173..fd105c5b25f 100644
--- a/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
+++ b/src/server/game/AuctionHouseBot/AuctionHouseBotSeller.cpp
@@ -582,13 +582,13 @@ void AuctionBotSeller::LoadSellerValues(SellerConfiguration& config)
break;
}
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_GRAY, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_WHITE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_GREEN, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_BLUE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_PURPLE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_ORANGE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_PRICE_RATIO) / 100);
- config.SetPriceRatioPerQuality(AUCTION_QUALITY_YELLOW, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_PRICE_RATIO) / 100);
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_GRAY, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GRAY_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_WHITE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_WHITE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_GREEN, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_GREEN_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_BLUE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_BLUE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_PURPLE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_PURPLE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_ORANGE, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_ORANGE_PRICE_RATIO));
+ config.SetPriceRatioPerQuality(AUCTION_QUALITY_YELLOW, PriceRatio * sAuctionBotConfig->GetConfig(CONFIG_AHBOT_ITEM_YELLOW_PRICE_RATIO));
config.SetPriceRatioPerClass(ITEM_CLASS_CONSUMABLE, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONSUMABLE_PRICE_RATIO));
config.SetPriceRatioPerClass(ITEM_CLASS_CONTAINER, sAuctionBotConfig->GetConfig(CONFIG_AHBOT_CLASS_CONTAINER_PRICE_RATIO));
@@ -701,10 +701,10 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
{
uint32 classRatio = config.GetPriceRatioPerClass(ItemClass(itemProto->Class));
uint32 qualityRatio = config.GetPriceRatioPerQuality(AuctionQuality(itemProto->Quality));
- uint32 priceRatio = (classRatio * qualityRatio) / 100;
+ float priceRatio = (classRatio * qualityRatio) / 10000.0f;
- uint32 buyPrice = itemProto->BuyPrice;
- uint32 sellPrice = itemProto->SellPrice;
+ float buyPrice = itemProto->BuyPrice;
+ float sellPrice = itemProto->SellPrice;
if (buyPrice == 0)
{
@@ -712,11 +712,11 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
buyPrice = sellPrice * GetSellModifier(itemProto);
else
{
- uint32 divisor = ((itemProto->Class == 2 || itemProto->Class == 4) ? 284 : 80);
- uint32 tempLevel = (itemProto->ItemLevel == 0 ? 1 : itemProto->ItemLevel);
- uint32 tempQuality = (itemProto->Quality == 0 ? 1 : itemProto->Quality);
+ float divisor = ((itemProto->Class == ITEM_CLASS_WEAPON || itemProto->Class == ITEM_CLASS_ARMOR) ? 284.0f : 80.0f);
+ float tempLevel = (itemProto->ItemLevel == 0 ? 1.0f : itemProto->ItemLevel);
+ float tempQuality = (itemProto->Quality == 0 ? 1.0f : itemProto->Quality);
- buyPrice = tempLevel * tempQuality * GetBuyModifier(itemProto)* tempLevel / divisor;
+ buyPrice = tempLevel * tempQuality * static_cast<float>(GetBuyModifier(itemProto))* tempLevel / divisor;
}
}
@@ -725,15 +725,16 @@ void AuctionBotSeller::SetPricesOfItem(ItemTemplate const* itemProto, SellerConf
if (sAuctionBotConfig->GetConfig(CONFIG_AHBOT_BUYPRICE_SELLER))
buyPrice = sellPrice;
-
- uint32 basePrice = (buyPrice * stackCount * priceRatio) / (itemProto->Class == 6 ? 200 : itemProto->BuyCount) / 100;
- uint32 range = basePrice * 0.04;
-
- buyp = urand(basePrice - range, basePrice + range) + 1;
-
- basePrice = buyp * .5;
+
+ float basePriceFloat = (buyPrice * stackCount * priceRatio) / (itemProto->Class == 6 ? 200.0f : static_cast<float>(itemProto->BuyCount)) / 100.0f;
+ float range = basePriceFloat * 0.04f;
+
+ buyp = static_cast<uint32>(frand(basePriceFloat - range, basePriceFloat + range) + 0.5f);
+ if (buyp == 0)
+ buyp = 1;
+ uint32 basePrice = buyp * .5;
range = buyp * .4;
- bidp = urand(basePrice - range, basePrice + range) + 1;
+ bidp = urand(static_cast<uint32>(basePrice - range + 0.5f), static_cast<uint32>(basePrice + range + 0.5f)) + 1;
}
// Determines the stack size to use for the item
diff --git a/src/server/game/CMakeLists.txt b/src/server/game/CMakeLists.txt
index 60e86093f89..239e59d6dbb 100644
--- a/src/server/game/CMakeLists.txt
+++ b/src/server/game/CMakeLists.txt
@@ -23,27 +23,36 @@ GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
add_definitions(-DTRINITY_API_EXPORT_GAME)
-add_library(game
- ${PRIVATE_PCH_SOURCE}
- ${PRIVATE_SOURCES}
-)
-
CollectIncludeDirectories(
${CMAKE_CURRENT_SOURCE_DIR}
PUBLIC_INCLUDES
# Exclude
${CMAKE_CURRENT_SOURCE_DIR}/PrecompiledHeaders)
+# Provide an interface target for the game project to allow
+# dependent projects to build meanwhile.
+add_library(game-interface INTERFACE)
+
+target_include_directories(game-interface
+ INTERFACE
+ ${PUBLIC_INCLUDES})
+
+target_link_libraries(game-interface
+ INTERFACE
+ shared
+ Detour)
+
+add_library(game
+ ${PRIVATE_PCH_SOURCE}
+ ${PRIVATE_SOURCES})
+
target_include_directories(game
- PUBLIC
- ${PUBLIC_INCLUDES}
PRIVATE
${CMAKE_CURRENT_BINARY_DIR})
target_link_libraries(game
PUBLIC
- shared
- Detour
+ game-interface
PRIVATE
efsw)
diff --git a/src/server/game/Chat/Chat.cpp b/src/server/game/Chat/Chat.cpp
index f0e98ee8c1f..ec90a5f7efb 100644
--- a/src/server/game/Chat/Chat.cpp
+++ b/src/server/game/Chat/Chat.cpp
@@ -32,18 +32,19 @@
#include "ScriptMgr.h"
#include "ChatLink.h"
-bool ChatHandler::load_command_table = true;
+// Lazy loading of the command table cache from commands and the
+// ScriptMgr should be thread safe since the player commands,
+// cli commands and ScriptMgr updates are all dispatched one after
+// one inside the world update loop.
+static Optional<std::vector<ChatCommand>> commandTableCache;
std::vector<ChatCommand> const& ChatHandler::getCommandTable()
{
- static std::vector<ChatCommand> commandTableCache;
-
- if (LoadCommandTable())
+ if (!commandTableCache)
{
- SetLoadCommandTable(false);
-
- std::vector<ChatCommand> cmds = sScriptMgr->GetChatCommands();
- commandTableCache.swap(cmds);
+ // We need to initialize this at top since SetDataForCommandInTable
+ // calls getCommandTable() recursively.
+ commandTableCache = sScriptMgr->GetChatCommands();
PreparedStatement* stmt = WorldDatabase.GetPreparedStatement(WORLD_SEL_COMMANDS);
PreparedQueryResult result = WorldDatabase.Query(stmt);
@@ -54,13 +55,18 @@ std::vector<ChatCommand> const& ChatHandler::getCommandTable()
Field* fields = result->Fetch();
std::string name = fields[0].GetString();
- SetDataForCommandInTable(commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
+ SetDataForCommandInTable(*commandTableCache, name.c_str(), fields[1].GetUInt16(), fields[2].GetString(), name);
}
while (result->NextRow());
}
}
- return commandTableCache;
+ return *commandTableCache;
+}
+
+void ChatHandler::invalidateCommandTable()
+{
+ commandTableCache.reset();
}
char const* ChatHandler::GetTrinityString(uint32 entry) const
diff --git a/src/server/game/Chat/Chat.h b/src/server/game/Chat/Chat.h
index a061ff63b00..1c9368275ad 100644
--- a/src/server/game/Chat/Chat.h
+++ b/src/server/game/Chat/Chat.h
@@ -96,6 +96,7 @@ class TC_GAME_API ChatHandler
bool ParseCommands(const char* text);
static std::vector<ChatCommand> const& getCommandTable();
+ static void invalidateCommandTable();
bool isValidChatMessage(const char* msg);
void SendGlobalSysMessage(const char *str);
@@ -143,8 +144,6 @@ class TC_GAME_API ChatHandler
GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(ObjectGuid::LowType lowguid, uint32 entry);
bool HasSentErrorMessage() const { return sentErrorMessage; }
void SetSentErrorMessage(bool val){ sentErrorMessage = val; }
- static bool LoadCommandTable() { return load_command_table; }
- static void SetLoadCommandTable(bool val) { load_command_table = val; }
bool ShowHelpForCommand(std::vector<ChatCommand> const& table, const char* cmd);
protected:
@@ -157,7 +156,6 @@ class TC_GAME_API ChatHandler
WorldSession* m_session; // != NULL for chat command call and NULL for CLI command
// common global flag
- static bool load_command_table;
bool sentErrorMessage;
};
diff --git a/src/server/game/DataStores/DBCStores.cpp b/src/server/game/DataStores/DBCStores.cpp
index 0e9b50d64c5..5ede70da2a3 100644
--- a/src/server/game/DataStores/DBCStores.cpp
+++ b/src/server/game/DataStores/DBCStores.cpp
@@ -27,6 +27,9 @@
#include <boost/regex.hpp>
#include <map>
+#include <fstream>
+#include <iostream>
+#include <iomanip>
typedef std::map<uint16, uint32> AreaFlagByAreaID;
typedef std::map<uint32, uint32> AreaFlagByMapID;
@@ -73,6 +76,7 @@ DBCStorage <CharTitlesEntry> sCharTitlesStore(CharTitlesEntryfmt);
DBCStorage <ChatChannelsEntry> sChatChannelsStore(ChatChannelsEntryfmt);
DBCStorage <ChrClassesEntry> sChrClassesStore(ChrClassesEntryfmt);
DBCStorage <ChrRacesEntry> sChrRacesStore(ChrRacesEntryfmt);
+DBCStorage <CinematicCameraEntry> sCinematicCameraStore(CinematicCameraEntryfmt);
DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore(CinematicSequencesEntryfmt);
DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore(CreatureDisplayInfofmt);
DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore(CreatureDisplayInfoExtrafmt);
@@ -217,6 +221,8 @@ 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;
@@ -311,6 +317,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales, bad_dbc_files, sChatChannelsStore, dbcPath, "ChatChannels.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChrClassesStore, dbcPath, "ChrClasses.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sChrRacesStore, dbcPath, "ChrRaces.dbc");
+ LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicCameraStore, dbcPath, "CinematicCamera.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCinematicSequencesStore, dbcPath, "CinematicSequences.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoStore, dbcPath, "CreatureDisplayInfo.dbc");
LoadDBC(availableDbcLocales, bad_dbc_files, sCreatureDisplayInfoExtraStore, dbcPath, "CreatureDisplayInfoExtra.dbc");
@@ -717,10 +724,250 @@ 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 beb0bb514cc..6cad13fdf9a 100644
--- a/src/server/game/DataStores/DBCStores.h
+++ b/src/server/game/DataStores/DBCStores.h
@@ -27,6 +27,8 @@
#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);
@@ -96,6 +98,7 @@ TC_GAME_API extern DBCStorage <CharSectionsEntry> sCharSectionsStore;
TC_GAME_API extern DBCStorage <CharTitlesEntry> sCharTitlesStore;
TC_GAME_API extern DBCStorage <ChrClassesEntry> sChrClassesStore;
TC_GAME_API extern DBCStorage <ChrRacesEntry> sChrRacesStore;
+TC_GAME_API extern DBCStorage <CinematicCameraEntry> sCinematicCameraStore;
TC_GAME_API extern DBCStorage <CinematicSequencesEntry> sCinematicSequencesStore;
TC_GAME_API extern DBCStorage <CreatureDisplayInfoEntry> sCreatureDisplayInfoStore;
TC_GAME_API extern DBCStorage <CreatureDisplayInfoExtraEntry> sCreatureDisplayInfoExtraStore;
@@ -193,7 +196,9 @@ 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 b5dc4489148..51b3dcbd38b 100644
--- a/src/server/game/DataStores/DBCStructure.h
+++ b/src/server/game/DataStores/DBCStructure.h
@@ -725,24 +725,22 @@ struct ChrRacesEntry
uint32 expansion; // 68 (0 - original race, 1 - tbc addon, ...)
};
-/* not used
struct CinematicCameraEntry
{
uint32 id; // 0 index
char* filename; // 1
uint32 soundid; // 2 in SoundEntries.dbc or 0
- float start_x; // 3
- float start_y; // 4
- float start_z; // 5
- float unk6; // 6 speed?
+ float base_x; // 3
+ float base_y; // 4
+ float base_z; // 5
+ float base_o; // 6
};
-*/
struct CinematicSequencesEntry
{
uint32 Id; // 0 index
//uint32 unk1; // 1 always 0
- //uint32 cinematicCamera; // 2 id in CinematicCamera.dbc
+ uint32 cinematicCamera; // 2 id in CinematicCamera.dbc
// 3-9 always 0
};
diff --git a/src/server/game/DataStores/DBCfmt.h b/src/server/game/DataStores/DBCfmt.h
index c61ec997bc2..1accc81714b 100644
--- a/src/server/game/DataStores/DBCfmt.h
+++ b/src/server/game/DataStores/DBCfmt.h
@@ -38,7 +38,8 @@ char const CharTitlesEntryfmt[] = "nxssssssssssssssssxssssssssssssssssxi";
char const ChatChannelsEntryfmt[] = "nixssssssssssssssssxxxxxxxxxxxxxxxxxx";
char const ChrClassesEntryfmt[] = "nxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxixii";
char const ChrRacesEntryfmt[] = "niixiixixxxxixssssssssssssssssxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxi";
-char const CinematicSequencesEntryfmt[] = "nxxxxxxxxx";
+char const CinematicCameraEntryfmt[] = "nsiffff";
+char const CinematicSequencesEntryfmt[] = "nxixxxxxxx";
char const CreatureDisplayInfofmt[] = "nixifxxxxxxxxxxx";
char const CreatureDisplayInfoExtrafmt[] = "diixxxxxxxxxxxxxxxxxx";
char const CreatureFamilyfmt[] = "nfifiiiiixssssssssssssssssxx";
diff --git a/src/server/game/DungeonFinding/LFGMgr.cpp b/src/server/game/DungeonFinding/LFGMgr.cpp
index 02fc69b8365..e72859c23e7 100644
--- a/src/server/game/DungeonFinding/LFGMgr.cpp
+++ b/src/server/game/DungeonFinding/LFGMgr.cpp
@@ -40,8 +40,6 @@ namespace lfg
LFGMgr::LFGMgr(): m_QueueTimer(0), m_lfgProposalId(1),
m_options(sWorld->getIntConfig(CONFIG_LFG_OPTIONSMASK))
{
- new LFGPlayerScript();
- new LFGGroupScript();
}
LFGMgr::~LFGMgr()
diff --git a/src/server/game/DungeonFinding/LFGScripts.cpp b/src/server/game/DungeonFinding/LFGScripts.cpp
index 79d36055870..1d2b963b254 100644
--- a/src/server/game/DungeonFinding/LFGScripts.cpp
+++ b/src/server/game/DungeonFinding/LFGScripts.cpp
@@ -241,4 +241,10 @@ void LFGGroupScript::OnInviteMember(Group* group, ObjectGuid guid)
sLFGMgr->LeaveLfg(leader);
}
+void AddSC_LFGScripts()
+{
+ new LFGPlayerScript();
+ new LFGGroupScript();
+}
+
} // namespace lfg
diff --git a/src/server/game/DungeonFinding/LFGScripts.h b/src/server/game/DungeonFinding/LFGScripts.h
index ec64604a282..9f52668ea61 100644
--- a/src/server/game/DungeonFinding/LFGScripts.h
+++ b/src/server/game/DungeonFinding/LFGScripts.h
@@ -53,4 +53,6 @@ class TC_GAME_API LFGGroupScript : public GroupScript
void OnInviteMember(Group* group, ObjectGuid guid) override;
};
+/*keep private*/ void AddSC_LFGScripts();
+
} // namespace lfg
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index 61d5d7d9459..d3843c86aa4 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -342,10 +342,10 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
m_creatureInfo = cinfo; // map mode related always
// equal to player Race field, but creature does not have race
- SetByteValue(UNIT_FIELD_BYTES_0, 0, 0);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, 0);
// known valid are: CLASS_WARRIOR, CLASS_PALADIN, CLASS_ROGUE, CLASS_MAGE
- SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(cinfo->unit_class));
// Cancel load if no model defined
if (!(cinfo->GetFirstValidModelId()))
@@ -364,7 +364,7 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
// Load creature equipment
if (data && data->equipmentId != 0)
@@ -379,10 +379,10 @@ bool Creature::InitEntry(uint32 entry, CreatureData const* data /*= nullptr*/)
SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f);
- SetSpeed(MOVE_WALK, cinfo->speed_walk);
- SetSpeed(MOVE_RUN, cinfo->speed_run);
- SetSpeed(MOVE_SWIM, 1.0f); // using 1.0 rate
- SetSpeed(MOVE_FLIGHT, 1.0f); // using 1.0 rate
+ SetSpeedRate(MOVE_WALK, cinfo->speed_walk);
+ SetSpeedRate(MOVE_RUN, cinfo->speed_run);
+ SetSpeedRate(MOVE_SWIM, 1.0f); // using 1.0 rate
+ SetSpeedRate(MOVE_FLIGHT, 1.0f); // using 1.0 rate
// Will set UNIT_FIELD_BOUNDINGRADIUS and UNIT_FIELD_COMBATREACH
SetObjectScale(cinfo->scale);
@@ -780,7 +780,7 @@ void Creature::DoFleeToGetAssistance()
cell.Visit(p, grid_creature_searcher, *GetMap(), *this, radius);
SetNoSearchAssistance(true);
- UpdateSpeed(MOVE_RUN, false);
+ UpdateSpeed(MOVE_RUN);
if (!creature)
//SetFeared(true, EnsureVictim()->GetGUID(), 0, sWorld->getIntConfig(CONFIG_CREATURE_FAMILY_FLEE_DELAY));
@@ -791,6 +791,24 @@ void Creature::DoFleeToGetAssistance()
}
}
+bool Creature::AIM_Destroy()
+{
+ if (m_AI_locked)
+ {
+ TC_LOG_DEBUG("scripts", "AIM_Destroy: failed to destroy, locked.");
+ return false;
+ }
+
+ ASSERT(!i_disabledAI,
+ "The disabled AI wasn't cleared!");
+
+ delete i_AI;
+ i_AI = nullptr;
+
+ IsAIEnabled = false;
+ return true;
+}
+
bool Creature::AIM_Initialize(CreatureAI* ai)
{
// make sure nothing can change the AI during AI update
@@ -800,12 +818,12 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
return false;
}
- UnitAI* oldAI = i_AI;
+ AIM_Destroy();
Motion_Initialize();
i_AI = ai ? ai : FactorySelector::selectAI(this);
- delete oldAI;
+
IsAIEnabled = true;
i_AI->InitializeAI();
// Initialize vehicle
@@ -889,7 +907,7 @@ bool Creature::Create(ObjectGuid::LowType guidlow, Map* map, uint32 phaseMask, u
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
LastUsedScriptID = GetCreatureTemplate()->ScriptID;
@@ -1618,7 +1636,7 @@ void Creature::setDeathState(DeathState s)
if (HasSearchedAssistance())
{
SetNoSearchAssistance(false);
- UpdateSpeed(MOVE_RUN, false);
+ UpdateSpeed(MOVE_RUN);
}
//Dismiss group if is leader
@@ -1705,7 +1723,7 @@ void Creature::Respawn(bool force)
{
SetDisplayId(displayID);
SetNativeDisplayId(displayID);
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
GetMotionMaster()->InitDefault();
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index c6b19dbb939..8466dad9e95 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -476,6 +476,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
bool IsInEvadeMode() const { return HasUnitState(UNIT_STATE_EVADE); }
+ bool AIM_Destroy();
bool AIM_Initialize(CreatureAI* ai = NULL);
void Motion_Initialize();
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 3fcd249c9be..7f922f89572 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -71,9 +71,15 @@ GameObject::~GameObject()
// CleanupsBeforeDelete();
}
-bool GameObject::AIM_Initialize()
+void GameObject::AIM_Destroy()
{
delete m_AI;
+ m_AI = nullptr;
+}
+
+bool GameObject::AIM_Initialize()
+{
+ AIM_Destroy();
m_AI = FactorySelector::SelectGameObjectAI(this);
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 49f94013ac2..2b092427178 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -849,8 +849,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void UpdateModelPosition();
- protected:
+ void AIM_Destroy();
bool AIM_Initialize();
+
+ protected:
GameObjectModel* CreateModel();
void UpdateModel(); // updates model in case displayId were changed
uint32 m_spellId;
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 824f42ad671..45952ba51ac 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -928,6 +928,11 @@ bool Object::HasByteFlag(uint16 index, uint8 offset, uint8 flag) const
return (((uint8*)&m_uint32Values[index])[offset] & flag) != 0;
}
+void Object::ApplyModByteFlag(uint16 index, uint8 offset, uint8 flag, bool apply)
+{
+ if (apply) SetByteFlag(index, offset, flag); else RemoveByteFlag(index, offset, flag);
+}
+
void Object::SetFlag64(uint16 index, uint64 newFlag)
{
uint64 oldval = GetUInt64Value(index);
@@ -1465,9 +1470,20 @@ void WorldObject::UpdateAllowedPositionZ(float x, float y, float &z) const
float WorldObject::GetGridActivationRange() const
{
if (ToPlayer())
+ {
+ if (ToPlayer()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
return GetMap()->GetVisibilityRange();
+ }
else if (ToCreature())
return ToCreature()->m_SightDistance;
+ else if (ToDynObject())
+ {
+ if (isActiveObject())
+ return GetMap()->GetVisibilityRange();
+ else
+ return 0.0f;
+ }
else
return 0.0f;
}
@@ -1488,6 +1504,8 @@ float WorldObject::GetSightRange(const WorldObject* target) const
{
if (target && target->isActiveObject() && !target->ToPlayer())
return MAX_VISIBILITY_DISTANCE;
+ else if (ToPlayer()->IsOnCinematic())
+ return DEFAULT_VISIBILITY_INSTANCE;
else
return GetMap()->GetVisibilityRange();
}
@@ -1497,6 +1515,11 @@ float WorldObject::GetSightRange(const WorldObject* target) const
return SIGHT_RANGE_UNIT;
}
+ if (ToDynObject() && isActiveObject())
+ {
+ return GetMap()->GetVisibilityRange();
+ }
+
return 0.0f;
}
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 029a8c08fd7..d4c8fc35451 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -160,6 +160,7 @@ class TC_GAME_API Object
void RemoveByteFlag(uint16 index, uint8 offset, uint8 newFlag);
void ToggleByteFlag(uint16 index, uint8 offset, uint8 flag);
bool HasByteFlag(uint16 index, uint8 offset, uint8 flag) const;
+ void ApplyModByteFlag(uint16 index, uint8 offset, uint8 flag, bool apply);
void SetFlag64(uint16 index, uint64 newFlag);
void RemoveFlag64(uint16 index, uint64 oldFlag);
diff --git a/src/server/game/Entities/Pet/Pet.cpp b/src/server/game/Entities/Pet/Pet.cpp
index 0da63c5e500..37fb23b38f5 100644
--- a/src/server/game/Entities/Pet/Pet.cpp
+++ b/src/server/game/Entities/Pet/Pet.cpp
@@ -213,12 +213,14 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
case SUMMON_PET:
petlevel = owner->getLevel();
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x800); // class = mage
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(CLASS_MAGE));
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
// this enables popup window (pet dismiss, cancel)
break;
case HUNTER_PET:
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); // class = warrior, gender = none, power = focus
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_WARRIOR);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, GENDER_NONE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, POWER_FOCUS);
SetSheath(SHEATH_STATE_MELEE);
SetByteFlag(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_CAN_BE_ABANDONED : UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
@@ -226,7 +228,6 @@ bool Pet::LoadPetFromDB(Player* owner, uint32 petEntry, uint32 petnumber, bool c
// this enables popup window (pet abandon, cancel)
SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS));
SetPower(POWER_HAPPINESS, fields[12].GetUInt32());
- setPowerType(POWER_FOCUS);
break;
default:
if (!IsPetGhoul())
@@ -822,7 +823,9 @@ bool Pet::CreateBaseAtTamed(CreatureTemplate const* cinfo, Map* map, uint32 phas
if (cinfo->type == CREATURE_TYPE_BEAST)
{
- SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_WARRIOR);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, GENDER_NONE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, POWER_FOCUS);
SetSheath(SHEATH_STATE_MELEE);
SetByteFlag(UNIT_FIELD_BYTES_2, 2, UNIT_CAN_BE_RENAMED | UNIT_CAN_BE_ABANDONED);
}
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 0af1fc87f6b..c58f2c940fc 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -96,6 +96,9 @@
#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,
@@ -535,6 +538,14 @@ Player::Player(WorldSession* session): Unit(true)
_activeCheats = CHEAT_NONE;
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;
+
m_achievementMgr = new AchievementMgr(this);
m_reputationMgr = new ReputationMgr(this);
}
@@ -643,9 +654,10 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
return false;
}
- uint32 RaceClassGender = (createInfo->Race) | (createInfo->Class << 8) | (createInfo->Gender << 16);
-
- SetUInt32Value(UNIT_FIELD_BYTES_0, (RaceClassGender | (powertype << 24)));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, createInfo->Race);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, createInfo->Class);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, createInfo->Gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, powertype);
InitDisplayIds();
if (sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld->getIntConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
{
@@ -658,13 +670,14 @@ bool Player::Create(ObjectGuid::LowType guidlow, CharacterCreateInfo* createInfo
SetInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, uint32(-1)); // -1 is default value
- SetUInt32Value(PLAYER_BYTES, (createInfo->Skin | (createInfo->Face << 8) | (createInfo->HairStyle << 16) | (createInfo->HairColor << 24)));
- SetUInt32Value(PLAYER_BYTES_2, (createInfo->FacialHair |
- (0x00 << 8) |
- (0x00 << 16) |
- (((GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED) << 24)));
- SetByteValue(PLAYER_BYTES_3, 0, createInfo->Gender);
- SetByteValue(PLAYER_BYTES_3, 3, 0); // BattlefieldArenaFaction (0 or 1)
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, createInfo->Skin);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID, createInfo->Face);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, createInfo->HairStyle);
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, createInfo->HairColor);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, createInfo->FacialHair);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0) ? REST_STATE_RAF_LINKED : REST_STATE_NOT_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, createInfo->Gender);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_ARENA_FACTION, 0);
SetUInt32Value(PLAYER_GUILDID, 0);
SetUInt32Value(PLAYER_GUILDRANK, 0);
@@ -1179,7 +1192,7 @@ void Player::SetDrunkValue(uint8 newDrunkValue, uint32 itemId /*= 0*/)
m_invisibilityDetect.DelFlag(INVISIBILITY_DRUNK);
uint32 newDrunkenState = Player::GetDrunkenstateByValue(newDrunkValue);
- SetByteValue(PLAYER_BYTES_3, 1, newDrunkValue);
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, newDrunkValue);
UpdateObjectVisibility();
if (!isSobering)
@@ -1222,7 +1235,15 @@ void Player::Update(uint32 p_time)
m_spellModTakingSpell = nullptr;
}
- //used to implement delayed far teleport
+ // 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)
+ {
+ m_lastCinematicCheck = getMSTime();
+ UpdateCinematicLocation(p_time);
+ }
+
+ //used to implement delayed far teleports
SetCanDelayTeleport(true);
Unit::Update(p_time);
SetCanDelayTeleport(false);
@@ -2822,8 +2843,8 @@ void Player::GiveLevel(uint8 level)
{
++m_grantableLevels;
- if (!HasByteFlag(PLAYER_FIELD_BYTES, 1, 0x01))
- SetByteFlag(PLAYER_FIELD_BYTES, 1, 0x01);
+ if (!HasByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01))
+ SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01);
}
sScriptMgr->OnPlayerLevelChanged(this, oldLevel);
@@ -4789,7 +4810,7 @@ Corpse* Player::CreateCorpse()
// prevent the existence of 2 corpses for one player
SpawnCorpseBones();
- uint32 _pb, _pb2, _cfb1, _cfb2;
+ uint32 _cfb1, _cfb2;
Corpse* corpse = new Corpse((m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH) ? CORPSE_RESURRECTABLE_PVP : CORPSE_RESURRECTABLE_PVE);
SetPvPDeath(false);
@@ -4802,16 +4823,13 @@ Corpse* Player::CreateCorpse()
_corpseLocation.WorldRelocate(*this);
- _pb = GetUInt32Value(PLAYER_BYTES);
- _pb2 = GetUInt32Value(PLAYER_BYTES_2);
+ uint8 skin = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
+ uint8 face = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID);
+ uint8 hairstyle = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID);
+ uint8 haircolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
+ uint8 facialhair = GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE);
- uint8 skin = (uint8)(_pb);
- uint8 face = (uint8)(_pb >> 8);
- uint8 hairstyle = (uint8)(_pb >> 16);
- uint8 haircolor = (uint8)(_pb >> 24);
- uint8 facialhair = (uint8)(_pb2);
-
- _cfb1 = ((0x00) | (getRace() << 8) | (GetByteValue(PLAYER_BYTES_3, 0) << 16) | (skin << 24));
+ _cfb1 = ((0x00) | (getRace() << 8) | (GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER) << 16) | (skin << 24));
_cfb2 = ((face) | (hairstyle << 8) | (haircolor << 16) | (facialhair << 24));
corpse->SetUInt32Value(CORPSE_FIELD_BYTES_1, _cfb1);
@@ -6399,11 +6417,13 @@ void Player::SendDirectMessage(WorldPacket const* data) const
m_session->SendPacket(data);
}
-void Player::SendCinematicStart(uint32 CinematicSequenceId) const
+void Player::SendCinematicStart(uint32 CinematicSequenceId)
{
WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(CinematicSequenceId);
SendDirectMessage(&data);
+ if (const CinematicSequencesEntry* sequence = sCinematicSequencesStore.LookupEntry(CinematicSequenceId))
+ SetActiveCinematicCamera(sequence->cinematicCamera);
}
void Player::SendMovieStart(uint32 MovieId) const
@@ -16859,12 +16879,9 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
return false;
}
- // overwrite some data fields
- uint32 bytes0 = 0;
- bytes0 |= fields[3].GetUInt8(); // race
- bytes0 |= fields[4].GetUInt8() << 8; // class
- bytes0 |= gender << 16; // gender
- SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE, fields[3].GetUInt8());
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, fields[4].GetUInt8());
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, gender);
// check if race/class combination is valid
PlayerInfo const* info = sObjectMgr->GetPlayerInfo(getRace(), getClass());
@@ -16891,25 +16908,25 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
money = MAX_MONEY_AMOUNT;
SetMoney(money);
- SetByteValue(PLAYER_BYTES, 0, fields[9].GetUInt8());
- SetByteValue(PLAYER_BYTES, 1, fields[10].GetUInt8());
- SetByteValue(PLAYER_BYTES, 2, fields[11].GetUInt8());
- SetByteValue(PLAYER_BYTES, 3, fields[12].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 0, fields[13].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 2, fields[14].GetUInt8());
- SetByteValue(PLAYER_BYTES_2, 3, fields[15].GetUInt8());
- SetByteValue(PLAYER_BYTES_3, 0, fields[5].GetUInt8());
- SetByteValue(PLAYER_BYTES_3, 1, fields[54].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, fields[9].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID, fields[10].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, fields[11].GetUInt8());
+ SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, fields[12].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, fields[13].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS, fields[14].GetUInt8());
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, fields[15].GetUInt8());
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, fields[5].GetUInt8());
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION, fields[54].GetUInt8());
if (!ValidateAppearance(
fields[3].GetUInt8(), // race
fields[4].GetUInt8(), // class
gender,
- GetByteValue(PLAYER_BYTES, 2),
- GetByteValue(PLAYER_BYTES, 3),
- GetByteValue(PLAYER_BYTES, 1),
- GetByteValue(PLAYER_BYTES_2, 0),
- GetByteValue(PLAYER_BYTES, 0)))
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID),
+ GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE),
+ GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID)))
{
TC_LOG_ERROR("entities.player", "Player::LoadFromDB: Player (%s) has wrong Appearance values (Hair/Skin/Color), can't load.", guid.ToString().c_str());
return false;
@@ -16923,7 +16940,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetUInt32Value(PLAYER_AMMO_ID, fields[68].GetUInt32());
// set which actionbars the client has active - DO NOT REMOVE EVER AGAIN (can be changed though, if it does change fieldwise)
- SetByteValue(PLAYER_FIELD_BYTES, 2, fields[70].GetUInt8());
+ SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, fields[70].GetUInt8());
InitDisplayIds();
@@ -17506,7 +17523,7 @@ bool Player::LoadFromDB(ObjectGuid guid, SQLQueryHolder *holder)
SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_REFER_A_FRIEND);
if (m_grantableLevels > 0)
- SetByteValue(PLAYER_FIELD_BYTES, 1, 0x01);
+ SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL, 0x01);
_LoadDeclinedNames(holder->GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_DECLINED_NAMES));
@@ -17713,7 +17730,7 @@ void Player::LoadCorpse(PreparedQueryResult result)
{
Field* fields = result->Fetch();
_corpseLocation.WorldRelocate(fields[0].GetUInt16(), fields[1].GetFloat(), fields[2].GetFloat(), fields[3].GetFloat(), fields[4].GetFloat());
- ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable());
+ ApplyModByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER, !sMapStore.LookupEntry(_corpseLocation.GetMapId())->Instanceable());
}
else
ResurrectPlayer(0.5f);
@@ -18947,17 +18964,17 @@ void Player::SaveToDB(bool create /*=false*/)
stmt->setString(index++, GetName());
stmt->setUInt8(index++, getRace());
stmt->setUInt8(index++, getClass());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, 0)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
stmt->setUInt8(index++, getLevel());
stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
stmt->setUInt32(index++, GetMoney());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 1));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 3));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 3));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE));
stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
stmt->setUInt16(index++, (uint16)GetMapId());
stmt->setUInt32(index++, (uint32)GetInstanceId());
@@ -19047,7 +19064,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
stmt->setString(index++, ss.str());
- stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES));
stmt->setUInt32(index, m_grantableLevels);
}
else
@@ -19057,17 +19074,17 @@ void Player::SaveToDB(bool create /*=false*/)
stmt->setString(index++, GetName());
stmt->setUInt8(index++, getRace());
stmt->setUInt8(index++, getClass());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, 0)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)); // save gender from PLAYER_BYTES_3, UNIT_BYTES_0 changes with every transform effect
stmt->setUInt8(index++, getLevel());
stmt->setUInt32(index++, GetUInt32Value(PLAYER_XP));
stmt->setUInt32(index++, GetMoney());
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 1));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, 3));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 0));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 2));
- stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, 3));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE));
stmt->setUInt32(index++, GetUInt32Value(PLAYER_FLAGS));
if (!IsBeingTeleported())
@@ -19172,7 +19189,7 @@ void Player::SaveToDB(bool create /*=false*/)
ss << GetUInt32Value(PLAYER__FIELD_KNOWN_TITLES + i) << ' ';
stmt->setString(index++, ss.str());
- stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, 2));
+ stmt->setUInt8(index++, GetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES));
stmt->setUInt32(index++, m_grantableLevels);
stmt->setUInt8(index++, IsInWorld() && !GetSession()->PlayerLogout() ? 1 : 0);
@@ -20892,13 +20909,13 @@ void Player::SetRestBonus(float rest_bonus_new)
// update data for client
if ((GetsRecruitAFriendBonus(true) && (GetSession()->IsARecruiter() || GetSession()->GetRecruiterId() != 0)))
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_RAF_LINKED);
else
{
if (m_rest_bonus > 10)
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_RESTED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_RESTED);
else if (m_rest_bonus <= 1)
- SetByteValue(PLAYER_BYTES_2, 3, REST_STATE_NOT_RAF_LINKED);
+ SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_REST_STATE, REST_STATE_NOT_RAF_LINKED);
}
//RestTickUpdate
@@ -21232,7 +21249,7 @@ void Player::InitDisplayIds()
return;
}
- uint8 gender = GetByteValue(PLAYER_BYTES_3, 0);
+ uint8 gender = GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER);
switch (gender)
{
case GENDER_FEMALE:
@@ -21840,7 +21857,7 @@ void Player::SetBattlegroundEntryPoint()
void Player::SetBGTeam(uint32 team)
{
m_bgData.bgTeam = team;
- SetByteValue(PLAYER_BYTES_3, 3, uint8(team == ALLIANCE ? 1 : 0));
+ SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_ARENA_FACTION, uint8(team == ALLIANCE ? 1 : 0));
}
uint32 Player::GetBGTeam() const
@@ -24061,10 +24078,10 @@ uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 n
if (level > GT_MAX_LEVEL)
level = GT_MAX_LEVEL; // max level in this dbc
- uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
- uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
- uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
- uint8 skincolor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 hairstyle = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID);
+ uint8 haircolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
+ uint8 facialhair = GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE);
+ uint8 skincolor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
if ((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair) && (!newSkin || (newSkin->hair_id == skincolor)))
return 0;
@@ -26094,6 +26111,9 @@ bool Player::SetCanFly(bool apply, bool packetOnly /*= false*/)
if (!packetOnly && !Unit::SetCanFly(apply))
return false;
+ if (!apply)
+ SetFallInformation(0, GetPositionZ());
+
WorldPacket data(apply ? SMSG_MOVE_SET_CAN_FLY : SMSG_MOVE_UNSET_CAN_FLY, 12);
data << GetPackGUID();
data << uint32(0); //! movement counter
@@ -26190,6 +26210,125 @@ 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();
@@ -26285,7 +26424,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
case SUMMON_PET:
// this enables pet details window (Shift+P)
pet->GetCharmInfo()->SetPetNumber(pet_number, true);
- pet->SetUInt32Value(UNIT_FIELD_BYTES_0, 2048);
+ pet->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, CLASS_MAGE);
pet->SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
pet->SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, 1000);
pet->SetFullHealth();
diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h
index d817969f6da..fd0fac69674 100644
--- a/src/server/game/Entities/Player/Player.h
+++ b/src/server/game/Entities/Player/Player.h
@@ -415,50 +415,48 @@ enum PlayerFlags
PLAYER_FLAGS_UNK31 = 0x80000000
};
-// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
-// can't use enum for uint64 values
-#define PLAYER_TITLE_DISABLED UI64LIT(0x0000000000000000)
-#define PLAYER_TITLE_NONE UI64LIT(0x0000000000000001)
-#define PLAYER_TITLE_PRIVATE UI64LIT(0x0000000000000002) // 1
-#define PLAYER_TITLE_CORPORAL UI64LIT(0x0000000000000004) // 2
-#define PLAYER_TITLE_SERGEANT_A UI64LIT(0x0000000000000008) // 3
-#define PLAYER_TITLE_MASTER_SERGEANT UI64LIT(0x0000000000000010) // 4
-#define PLAYER_TITLE_SERGEANT_MAJOR UI64LIT(0x0000000000000020) // 5
-#define PLAYER_TITLE_KNIGHT UI64LIT(0x0000000000000040) // 6
-#define PLAYER_TITLE_KNIGHT_LIEUTENANT UI64LIT(0x0000000000000080) // 7
-#define PLAYER_TITLE_KNIGHT_CAPTAIN UI64LIT(0x0000000000000100) // 8
-#define PLAYER_TITLE_KNIGHT_CHAMPION UI64LIT(0x0000000000000200) // 9
-#define PLAYER_TITLE_LIEUTENANT_COMMANDER UI64LIT(0x0000000000000400) // 10
-#define PLAYER_TITLE_COMMANDER UI64LIT(0x0000000000000800) // 11
-#define PLAYER_TITLE_MARSHAL UI64LIT(0x0000000000001000) // 12
-#define PLAYER_TITLE_FIELD_MARSHAL UI64LIT(0x0000000000002000) // 13
-#define PLAYER_TITLE_GRAND_MARSHAL UI64LIT(0x0000000000004000) // 14
-#define PLAYER_TITLE_SCOUT UI64LIT(0x0000000000008000) // 15
-#define PLAYER_TITLE_GRUNT UI64LIT(0x0000000000010000) // 16
-#define PLAYER_TITLE_SERGEANT_H UI64LIT(0x0000000000020000) // 17
-#define PLAYER_TITLE_SENIOR_SERGEANT UI64LIT(0x0000000000040000) // 18
-#define PLAYER_TITLE_FIRST_SERGEANT UI64LIT(0x0000000000080000) // 19
-#define PLAYER_TITLE_STONE_GUARD UI64LIT(0x0000000000100000) // 20
-#define PLAYER_TITLE_BLOOD_GUARD UI64LIT(0x0000000000200000) // 21
-#define PLAYER_TITLE_LEGIONNAIRE UI64LIT(0x0000000000400000) // 22
-#define PLAYER_TITLE_CENTURION UI64LIT(0x0000000000800000) // 23
-#define PLAYER_TITLE_CHAMPION UI64LIT(0x0000000001000000) // 24
-#define PLAYER_TITLE_LIEUTENANT_GENERAL UI64LIT(0x0000000002000000) // 25
-#define PLAYER_TITLE_GENERAL UI64LIT(0x0000000004000000) // 26
-#define PLAYER_TITLE_WARLORD UI64LIT(0x0000000008000000) // 27
-#define PLAYER_TITLE_HIGH_WARLORD UI64LIT(0x0000000010000000) // 28
-#define PLAYER_TITLE_GLADIATOR UI64LIT(0x0000000020000000) // 29
-#define PLAYER_TITLE_DUELIST UI64LIT(0x0000000040000000) // 30
-#define PLAYER_TITLE_RIVAL UI64LIT(0x0000000080000000) // 31
-#define PLAYER_TITLE_CHALLENGER UI64LIT(0x0000000100000000) // 32
-#define PLAYER_TITLE_SCARAB_LORD UI64LIT(0x0000000200000000) // 33
-#define PLAYER_TITLE_CONQUEROR UI64LIT(0x0000000400000000) // 34
-#define PLAYER_TITLE_JUSTICAR UI64LIT(0x0000000800000000) // 35
-#define PLAYER_TITLE_CHAMPION_OF_THE_NAARU UI64LIT(0x0000001000000000) // 36
-#define PLAYER_TITLE_MERCILESS_GLADIATOR UI64LIT(0x0000002000000000) // 37
-#define PLAYER_TITLE_OF_THE_SHATTERED_SUN UI64LIT(0x0000004000000000) // 38
-#define PLAYER_TITLE_HAND_OF_ADAL UI64LIT(0x0000008000000000) // 39
-#define PLAYER_TITLE_VENGEFUL_GLADIATOR UI64LIT(0x0000010000000000) // 40
+enum PlayerBytesOffsets
+{
+ PLAYER_BYTES_OFFSET_SKIN_ID = 0,
+ PLAYER_BYTES_OFFSET_FACE_ID = 1,
+ PLAYER_BYTES_OFFSET_HAIR_STYLE_ID = 2,
+ PLAYER_BYTES_OFFSET_HAIR_COLOR_ID = 3
+};
+
+enum PlayerBytes2Offsets
+{
+ PLAYER_BYTES_2_OFFSET_FACIAL_STYLE = 0,
+ PLAYER_BYTES_2_OFFSET_PARTY_TYPE = 1,
+ PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS = 2,
+ PLAYER_BYTES_2_OFFSET_REST_STATE = 3
+};
+
+enum PlayerBytes3Offsets
+{
+ PLAYER_BYTES_3_OFFSET_GENDER = 0,
+ PLAYER_BYTES_3_OFFSET_INEBRIATION = 1,
+ PLAYER_BYTES_3_OFFSET_PVP_TITLE = 2,
+ PLAYER_BYTES_3_OFFSET_ARENA_FACTION = 3
+};
+
+enum PlayerFieldBytesOffsets
+{
+ PLAYER_FIELD_BYTES_OFFSET_FLAGS = 0,
+ PLAYER_FIELD_BYTES_OFFSET_RAF_GRANTABLE_LEVEL = 1,
+ PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES = 2,
+ PLAYER_FIELD_BYTES_OFFSET_LIFETIME_MAX_PVP_RANK = 3
+};
+
+enum PlayerFieldBytes2Offsets
+{
+ PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID = 0, // uint16!
+ PLAYER_FIELD_BYTES_2_OFFSET_IGNORE_POWER_REGEN_PREDICTION_MASK = 2,
+ PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION = 3
+};
+
+static_assert((PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID & 1) == 0, "PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID must be aligned to 2 byte boundary");
+
+#define PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET (PLAYER_FIELD_BYTES_2_OFFSET_OVERRIDE_SPELLS_ID / 2)
#define KNOWN_TITLES_SIZE 3
#define MAX_TITLE_INDEX (KNOWN_TITLES_SIZE*64) // 3 uint64 fields
@@ -1185,8 +1183,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
static bool IsBankPos(uint8 bag, uint8 slot);
bool IsValidPos(uint16 pos, bool explicit_pos) const { return IsValidPos(pos >> 8, pos & 255, explicit_pos); }
bool IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const;
- uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, 2); }
- void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, 2, count); }
+ uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS); }
+ void SetBankBagSlotCount(uint8 count) { SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_BANK_BAG_SLOTS, count); }
bool HasItemCount(uint32 item, uint32 count = 1, bool inBankAlso = false) const;
bool HasItemFitToSpellRequirements(SpellInfo const* spellInfo, Item const* ignoreItem = nullptr) const;
bool CanNoReagentCast(SpellInfo const* spellInfo) const;
@@ -1911,7 +1909,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
//End of PvP System
void SetDrunkValue(uint8 newDrunkValue, uint32 itemId = 0);
- uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, 1); }
+ uint8 GetDrunkValue() const { return GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_INEBRIATION); }
static DrunkenState GetDrunkenstateByValue(uint8 value);
uint32 GetDeathTimer() const { return m_deathTimer; }
@@ -2131,7 +2129,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player>
void ResummonPetTemporaryUnSummonedIfAny();
bool IsPetNeedBeTemporaryUnsummoned() const;
- void SendCinematicStart(uint32 CinematicSequenceId) const;
+ void SendCinematicStart(uint32 CinematicSequenceId);
void SendMovieStart(uint32 MovieId) const;
/*********************************************************/
@@ -2267,6 +2265,17 @@ 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;
@@ -2592,6 +2601,14 @@ 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);
diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp
index 319b939d899..b7f05a7fefa 100644
--- a/src/server/game/Entities/Unit/Unit.cpp
+++ b/src/server/game/Entities/Unit/Unit.cpp
@@ -8715,7 +8715,7 @@ void Unit::setPowerType(Powers new_powertype)
if (getPowerType() == new_powertype)
return;
- SetByteValue(UNIT_FIELD_BYTES_0, 3, new_powertype);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, new_powertype);
if (GetTypeId() == TYPEID_PLAYER)
{
@@ -9106,7 +9106,7 @@ bool Unit::AttackStop()
if (creature->HasSearchedAssistance())
{
creature->SetNoSearchAssistance(false);
- UpdateSpeed(MOVE_RUN, false);
+ UpdateSpeed(MOVE_RUN);
}
}
@@ -9440,7 +9440,7 @@ void Unit::SetMinion(Minion *minion, bool apply)
// FIXME: hack, speed must be set only at follow
if (GetTypeId() == TYPEID_PLAYER && minion->IsPet())
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
- minion->SetSpeed(UnitMoveType(i), m_speed_rate[i], true);
+ minion->SetSpeedRate(UnitMoveType(i), m_speed_rate[i]);
// Ghoul pets have energy instead of mana (is anywhere better place for this code?)
if (minion->IsPetGhoul() || minion->IsRisenAlly())
@@ -11870,9 +11870,9 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy)
if (IsPet())
{
- UpdateSpeed(MOVE_RUN, true);
- UpdateSpeed(MOVE_SWIM, true);
- UpdateSpeed(MOVE_FLIGHT, true);
+ UpdateSpeed(MOVE_RUN);
+ UpdateSpeed(MOVE_SWIM);
+ UpdateSpeed(MOVE_FLIGHT);
}
if (!(creature->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_MOUNTED_COMBAT))
@@ -11916,7 +11916,7 @@ void Unit::ClearInCombat()
if (Unit* owner = GetOwner())
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
if (owner->GetSpeedRate(UnitMoveType(i)) > GetSpeedRate(UnitMoveType(i)))
- SetSpeed(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)), true);
+ SetSpeedRate(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)));
}
else if (!IsCharmed())
return;
@@ -12003,25 +12003,26 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
|| target->GetReactionTo(this) > REP_NEUTRAL)
return false;
+ Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : nullptr;
+ Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : nullptr;
+
// Not all neutral creatures can be attacked (even some unfriendly faction does not react aggresive to you, like Sporaggar)
- if (GetReactionTo(target) == REP_NEUTRAL &&
- target->GetReactionTo(this) <= REP_NEUTRAL)
+ if (
+ (playerAffectingAttacker && !playerAffectingTarget) ||
+ (!playerAffectingAttacker && playerAffectingTarget)
+ )
{
- if (!(target->GetTypeId() == TYPEID_PLAYER && GetTypeId() == TYPEID_PLAYER) &&
- !(target->GetTypeId() == TYPEID_UNIT && GetTypeId() == TYPEID_UNIT))
- {
- Player const* player = target->GetTypeId() == TYPEID_PLAYER ? target->ToPlayer() : ToPlayer();
- Unit const* creature = target->GetTypeId() == TYPEID_UNIT ? target : this;
+ Player const* player = playerAffectingAttacker ? playerAffectingAttacker : playerAffectingTarget;
+ Unit const* creature = playerAffectingAttacker ? target : this;
- if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
- {
- if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
- if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction))
- if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
- if (!(repState->Flags & FACTION_FLAG_AT_WAR))
- return false;
+ if (FactionTemplateEntry const* factionTemplate = creature->GetFactionTemplateEntry())
+ {
+ if (!(player->GetReputationMgr().GetForcedRankIfAny(factionTemplate)))
+ if (FactionEntry const* factionEntry = sFactionStore.LookupEntry(factionTemplate->faction))
+ if (FactionState const* repState = player->GetReputationMgr().GetState(factionEntry))
+ if (!(repState->Flags & FACTION_FLAG_AT_WAR))
+ return false;
- }
}
}
@@ -12029,9 +12030,6 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
if (creatureAttacker && creatureAttacker->GetCreatureTemplate()->type_flags & CREATURE_TYPEFLAGS_PARTY_MEMBER)
return false;
- Player const* playerAffectingAttacker = HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? GetAffectingPlayer() : NULL;
- Player const* playerAffectingTarget = target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE) ? target->GetAffectingPlayer() : NULL;
-
// check duel - before sanctuary checks
if (playerAffectingAttacker && playerAffectingTarget)
if (playerAffectingAttacker->duel && playerAffectingAttacker->duel->opponent == playerAffectingTarget && playerAffectingAttacker->duel->startTime != 0)
@@ -12302,7 +12300,7 @@ void Unit::SetVisible(bool x)
UpdateObjectVisibility();
}
-void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
+void Unit::UpdateSpeed(UnitMoveType mtype)
{
int32 main_speed_mod = 0;
float stack_bonus = 1.0f;
@@ -12365,7 +12363,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
// Update speed for vehicle if available
if (GetTypeId() == TYPEID_PLAYER && GetVehicle())
- GetVehicleBase()->UpdateSpeed(MOVE_FLIGHT, true);
+ GetVehicleBase()->UpdateSpeed(MOVE_FLIGHT);
break;
}
default:
@@ -12447,7 +12445,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
speed = min_speed;
}
- SetSpeed(mtype, speed, forced);
+ SetSpeedRate(mtype, speed);
}
float Unit::GetSpeed(UnitMoveType mtype) const
@@ -12455,7 +12453,12 @@ float Unit::GetSpeed(UnitMoveType mtype) const
return m_speed_rate[mtype]*(IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]);
}
-void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
+void Unit::SetSpeed(UnitMoveType mtype, float newValue)
+{
+ SetSpeedRate(mtype, newValue / (IsControlledByPlayer() ? playerBaseMoveSpeed[mtype] : baseMoveSpeed[mtype]));
+}
+
+void Unit::SetSpeedRate(UnitMoveType mtype, float rate)
{
if (rate < 0)
rate = 0.0f;
@@ -12468,100 +12471,59 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
propagateSpeedChange();
- WorldPacket data;
- if (!forced)
+ // Spline packets are for units controlled by AI. "Force speed change" (wrongly named opcodes) and "move set speed" packets are for units controlled by a player.
+ static Opcodes const moveTypeToOpcode[MAX_MOVE_TYPE][3] =
+ {
+ {SMSG_SPLINE_SET_WALK_SPEED, SMSG_FORCE_WALK_SPEED_CHANGE, MSG_MOVE_SET_WALK_SPEED },
+ {SMSG_SPLINE_SET_RUN_SPEED, SMSG_FORCE_RUN_SPEED_CHANGE, MSG_MOVE_SET_RUN_SPEED },
+ {SMSG_SPLINE_SET_RUN_BACK_SPEED, SMSG_FORCE_RUN_BACK_SPEED_CHANGE, MSG_MOVE_SET_RUN_BACK_SPEED },
+ {SMSG_SPLINE_SET_SWIM_SPEED, SMSG_FORCE_SWIM_SPEED_CHANGE, MSG_MOVE_SET_SWIM_SPEED },
+ {SMSG_SPLINE_SET_SWIM_BACK_SPEED, SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, MSG_MOVE_SET_SWIM_BACK_SPEED },
+ {SMSG_SPLINE_SET_TURN_RATE, SMSG_FORCE_TURN_RATE_CHANGE, MSG_MOVE_SET_TURN_RATE },
+ {SMSG_SPLINE_SET_FLIGHT_SPEED, SMSG_FORCE_FLIGHT_SPEED_CHANGE, MSG_MOVE_SET_FLIGHT_SPEED },
+ {SMSG_SPLINE_SET_FLIGHT_BACK_SPEED, SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, MSG_MOVE_SET_FLIGHT_BACK_SPEED },
+ {SMSG_SPLINE_SET_PITCH_RATE, SMSG_FORCE_PITCH_RATE_CHANGE, MSG_MOVE_SET_PITCH_RATE },
+ };
+
+ if (GetTypeId() == TYPEID_PLAYER)
{
- switch (mtype)
- {
- case MOVE_WALK:
- data.Initialize(MSG_MOVE_SET_WALK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_RUN:
- data.Initialize(MSG_MOVE_SET_RUN_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_RUN_BACK:
- data.Initialize(MSG_MOVE_SET_RUN_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_SWIM:
- data.Initialize(MSG_MOVE_SET_SWIM_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_SWIM_BACK:
- data.Initialize(MSG_MOVE_SET_SWIM_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_TURN_RATE:
- data.Initialize(MSG_MOVE_SET_TURN_RATE, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_FLIGHT:
- data.Initialize(MSG_MOVE_SET_FLIGHT_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_FLIGHT_BACK:
- data.Initialize(MSG_MOVE_SET_FLIGHT_BACK_SPEED, 8+4+2+4+4+4+4+4+4+4);
- break;
- case MOVE_PITCH_RATE:
- data.Initialize(MSG_MOVE_SET_PITCH_RATE, 8+4+2+4+4+4+4+4+4+4);
- break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::SetSpeed: Unsupported move type (%d), data not sent to client.", mtype);
- return;
- }
+ // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
+ // and do it only for real sent packets and use run for run/mounted as client expected
+ ++ToPlayer()->m_forced_speed_changes[mtype];
+ if (!IsInCombat())
+ if (Pet* pet = ToPlayer()->GetPet())
+ pet->SetSpeedRate(mtype, m_speed_rate[mtype]);
+ }
+
+ if (m_movedPlayer) // unit controlled by a player.
+ {
+ // Send notification to self. this packet is only sent to one client (the client of the player concerned by the change).
+ WorldPacket self;
+ 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.
+ if (mtype == MOVE_RUN)
+ self << uint8(1); // unknown byte added in 2.1.0
+ self << float(GetSpeed(mtype));
+ m_movedPlayer->GetSession()->SendPacket(&self);
+
+ // Send notification to other players. sent to every clients (if in range) except one: the client of the player concerned by the change.
+ WorldPacket data;
+ data.Initialize(moveTypeToOpcode[mtype][2], 8 + 30 + 4);
data << GetPackGUID();
BuildMovementPacket(&data);
data << float(GetSpeed(mtype));
- SendMessageToSet(&data, true);
+ SendMessageToSet(&data, false);
}
- else
+ else // unit controlled by AI.
{
- if (GetTypeId() == TYPEID_PLAYER)
- {
- // register forced speed changes for WorldSession::HandleForceSpeedChangeAck
- // and do it only for real sent packets and use run for run/mounted as client expected
- ++ToPlayer()->m_forced_speed_changes[mtype];
-
- if (!IsInCombat())
- if (Pet* pet = ToPlayer()->GetPet())
- pet->SetSpeed(mtype, m_speed_rate[mtype], forced);
- }
-
- switch (mtype)
- {
- case MOVE_WALK:
- data.Initialize(SMSG_FORCE_WALK_SPEED_CHANGE, 16);
- break;
- case MOVE_RUN:
- data.Initialize(SMSG_FORCE_RUN_SPEED_CHANGE, 17);
- break;
- case MOVE_RUN_BACK:
- data.Initialize(SMSG_FORCE_RUN_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_SWIM:
- data.Initialize(SMSG_FORCE_SWIM_SPEED_CHANGE, 16);
- break;
- case MOVE_SWIM_BACK:
- data.Initialize(SMSG_FORCE_SWIM_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_TURN_RATE:
- data.Initialize(SMSG_FORCE_TURN_RATE_CHANGE, 16);
- break;
- case MOVE_FLIGHT:
- data.Initialize(SMSG_FORCE_FLIGHT_SPEED_CHANGE, 16);
- break;
- case MOVE_FLIGHT_BACK:
- data.Initialize(SMSG_FORCE_FLIGHT_BACK_SPEED_CHANGE, 16);
- break;
- case MOVE_PITCH_RATE:
- data.Initialize(SMSG_FORCE_PITCH_RATE_CHANGE, 16);
- break;
- default:
- TC_LOG_ERROR("entities.unit", "Unit::SetSpeed: Unsupported move type (%d), data not sent to client.", mtype);
- return;
- }
+ // send notification to every clients.
+ WorldPacket data;
+ data.Initialize(moveTypeToOpcode[mtype][0], 8 + 4);
data << GetPackGUID();
- data << (uint32)0; // moveEvent, NUM_PMOVE_EVTS = 0x39
- if (mtype == MOVE_RUN)
- data << uint8(0); // new 2.1.0
data << float(GetSpeed(mtype));
- SendMessageToSet(&data, true);
+ SendMessageToSet(&data, false);
}
}
@@ -14769,7 +14731,7 @@ void Unit::SetDisplayId(uint32 modelId)
SetUInt32Value(UNIT_FIELD_DISPLAYID, modelId);
// Set Gender by modelId
if (CreatureModelInfo const* minfo = sObjectMgr->GetCreatureModelInfo(modelId))
- SetByteValue(UNIT_FIELD_BYTES_0, 2, minfo->gender);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, minfo->gender);
}
void Unit::RestoreDisplayId()
@@ -16080,7 +16042,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type, AuraApplication const* au
if (cinfo && cinfo->type == CREATURE_TYPE_DEMON)
{
// to prevent client crash
- SetByteValue(UNIT_FIELD_BYTES_0, 1, (uint8)CLASS_MAGE);
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, (uint8)CLASS_MAGE);
// just to enable stat window
if (GetCharmInfo())
@@ -16182,7 +16144,7 @@ void Unit::RemoveCharmedBy(Unit* charmer)
CreatureTemplate const* cinfo = ToCreature()->GetCreatureTemplate();
if (cinfo && cinfo->type == CREATURE_TYPE_DEMON)
{
- SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
+ SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS, uint8(cinfo->unit_class));
if (GetCharmInfo())
GetCharmInfo()->SetPetNumber(0, true);
else
@@ -16700,7 +16662,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
- uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
+ uint8 hairColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
switch (hairColor)
{
case 7: // Violet
@@ -16721,7 +16683,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
- uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 skinColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
// Male
if (getGender() == GENDER_MALE)
{
@@ -16780,7 +16742,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Hair color
if (getRace() == RACE_NIGHTELF)
{
- uint8 hairColor = GetByteValue(PLAYER_BYTES, 3);
+ uint8 hairColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID);
switch (hairColor)
{
case 0: // Green
@@ -16800,7 +16762,7 @@ uint32 Unit::GetModelForForm(ShapeshiftForm form) const
// Based on Skin color
else if (getRace() == RACE_TAUREN)
{
- uint8 skinColor = GetByteValue(PLAYER_BYTES, 0);
+ uint8 skinColor = GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID);
// Male
if (getGender() == GENDER_MALE)
{
diff --git a/src/server/game/Entities/Unit/Unit.h b/src/server/game/Entities/Unit/Unit.h
index cc1f6465766..a0973aac279 100644
--- a/src/server/game/Entities/Unit/Unit.h
+++ b/src/server/game/Entities/Unit/Unit.h
@@ -190,6 +190,30 @@ enum UnitStandFlags
UNIT_STAND_FLAGS_ALL = 0xFF
};
+enum UnitBytes0Offsets
+{
+ UNIT_BYTES_0_OFFSET_RACE = 0,
+ UNIT_BYTES_0_OFFSET_CLASS = 1,
+ UNIT_BYTES_0_OFFSET_GENDER = 2,
+ UNIT_BYTES_0_OFFSET_POWER_TYPE = 3,
+};
+
+enum UnitBytes1Offsets
+{
+ UNIT_BYTES_1_OFFSET_STAND_STATE = 0,
+ UNIT_BYTES_1_OFFSET_PET_TALENTS = 1,
+ UNIT_BYTES_1_OFFSET_VIS_FLAG = 2,
+ UNIT_BYTES_1_OFFSET_ANIM_TIER = 3
+};
+
+enum UnitBytes2Offsets
+{
+ UNIT_BYTES_2_OFFSET_SHEATH_STATE = 0,
+ UNIT_BYTES_2_OFFSET_PVP_FLAG = 1,
+ UNIT_BYTES_2_OFFSET_PET_FLAG = 2,
+ UNIT_BYTES_2_OFFSET_SHAPESHIFT = 3,
+};
+
// byte flags value (UNIT_FIELD_BYTES_1, 3)
enum UnitBytes1_Flags
{
@@ -1314,11 +1338,11 @@ class TC_GAME_API Unit : public WorldObject
uint8 getLevel() const { return uint8(GetUInt32Value(UNIT_FIELD_LEVEL)); }
uint8 getLevelForTarget(WorldObject const* /*target*/) const override { return getLevel(); }
void SetLevel(uint8 lvl);
- uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, 0); }
+ uint8 getRace() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_RACE); }
uint32 getRaceMask() const { return 1 << (getRace()-1); }
- uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, 1); }
+ uint8 getClass() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_CLASS); }
uint32 getClassMask() const { return 1 << (getClass()-1); }
- uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, 2); }
+ uint8 getGender() const { return GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER); }
float GetStat(Stats stat) const { return float(GetUInt32Value(UNIT_FIELD_STAT0+stat)); }
void SetStat(Stats stat, int32 val) { SetStatInt32Value(UNIT_FIELD_STAT0+stat, val); }
@@ -1347,7 +1371,7 @@ class TC_GAME_API Unit : public WorldObject
int32 ModifyHealth(int32 val);
int32 GetHealthGain(int32 dVal);
- Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, 3)); }
+ Powers getPowerType() const { return Powers(GetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE)); }
void setPowerType(Powers power);
uint32 GetPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_POWER1 +power); }
uint32 GetMaxPower(Powers power) const { return GetUInt32Value(UNIT_FIELD_MAXPOWER1+power); }
@@ -1994,10 +2018,11 @@ class TC_GAME_API Unit : public WorldObject
void CalcAbsorbResist(Unit* victim, SpellSchoolMask schoolMask, DamageEffectType damagetype, uint32 const damage, uint32* absorb, uint32* resist, SpellInfo const* spellInfo = NULL);
void CalcHealAbsorb(Unit* victim, SpellInfo const* spellInfo, uint32& healAmount, uint32& absorb);
- void UpdateSpeed(UnitMoveType mtype, bool forced);
+ void UpdateSpeed(UnitMoveType mtype);
float GetSpeed(UnitMoveType mtype) const;
float GetSpeedRate(UnitMoveType mtype) const { return m_speed_rate[mtype]; }
- void SetSpeed(UnitMoveType mtype, float rate, bool forced = false);
+ void SetSpeed(UnitMoveType mtype, float newValue);
+ void SetSpeedRate(UnitMoveType mtype, float rate);
float ApplyEffectModifiers(SpellInfo const* spellProto, uint8 effect_index, float value) const;
int32 CalculateSpellDamage(Unit const* target, SpellInfo const* spellProto, uint8 effect_index, int32 const* basePoints = NULL) const;
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index fc7d668ae0b..3efeb1ca273 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -4715,7 +4715,7 @@ void ObjectMgr::LoadScripts(ScriptsType type)
if (tableName.empty())
return;
- if (sScriptMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
+ if (sMapMgr->IsScriptScheduled()) // function cannot be called when scripts are in use.
return;
TC_LOG_INFO("server.loading", "Loading %s...", tableName.c_str());
@@ -5162,7 +5162,7 @@ void ObjectMgr::LoadSpellScriptNames()
while (spellInfo)
{
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
spellInfo = spellInfo->GetNextRankSpell();
}
}
@@ -5171,7 +5171,7 @@ void ObjectMgr::LoadSpellScriptNames()
if (spellInfo->IsRanked())
TC_LOG_ERROR("sql.sql", "Scriptname: `%s` spell (Id: %d) is ranked spell. Perhaps not all ranks are assigned to this script.", scriptName.c_str(), spellId);
- _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, GetScriptId(scriptName)));
+ _spellScriptsStore.insert(SpellScriptsContainer::value_type(spellInfo->Id, std::make_pair(GetScriptId(scriptName), true)));
}
++count;
@@ -5193,45 +5193,59 @@ void ObjectMgr::ValidateSpellScripts()
uint32 count = 0;
- for (SpellScriptsContainer::iterator itr = _spellScriptsStore.begin(); itr != _spellScriptsStore.end();)
+ for (auto spell : _spellScriptsStore)
{
- SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(itr->first);
- std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> > SpellScriptLoaders;
- sScriptMgr->CreateSpellScriptLoaders(itr->first, SpellScriptLoaders);
- itr = _spellScriptsStore.upper_bound(itr->first);
+ SpellInfo const* spellEntry = sSpellMgr->GetSpellInfo(spell.first);
- for (std::vector<std::pair<SpellScriptLoader *, SpellScriptsContainer::iterator> >::iterator sitr = SpellScriptLoaders.begin(); sitr != SpellScriptLoaders.end(); ++sitr)
+ auto const bounds = sObjectMgr->GetSpellScriptsBounds(spell.first);
+
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScript* spellScript = sitr->first->GetSpellScript();
- AuraScript* auraScript = sitr->first->GetAuraScript();
- bool valid = true;
- if (!spellScript && !auraScript)
- {
- TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(sitr->second->second).c_str());
- valid = false;
- }
- if (spellScript)
- {
- spellScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- spellScript->_Register();
- if (!spellScript->_Validate(spellEntry))
- valid = false;
- delete spellScript;
- }
- if (auraScript)
+ if (SpellScriptLoader* spellScriptLoader = sScriptMgr->GetSpellScriptLoader(itr->second.first))
{
- auraScript->_Init(&sitr->first->GetName(), spellEntry->Id);
- auraScript->_Register();
- if (!auraScript->_Validate(spellEntry))
- valid = false;
- delete auraScript;
- }
- if (!valid)
- {
- _spellScriptsStore.erase(sitr->second);
+ ++count;
+
+ std::unique_ptr<SpellScript> spellScript(spellScriptLoader->GetSpellScript());
+ std::unique_ptr<AuraScript> auraScript(spellScriptLoader->GetAuraScript());
+
+ if (!spellScript && !auraScript)
+ {
+ TC_LOG_ERROR("scripts", "Functions GetSpellScript() and GetAuraScript() of script `%s` do not return objects - script skipped", GetScriptName(itr->second.first).c_str());
+
+ itr->second.second = false;
+ continue;
+ }
+
+ if (spellScript)
+ {
+ spellScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ spellScript->_Register();
+
+ if (!spellScript->_Validate(spellEntry))
+ {
+ itr->second.second = false;
+ continue;
+ }
+ }
+
+ if (auraScript)
+ {
+ auraScript->_Init(&spellScriptLoader->GetName(), spellEntry->Id);
+ auraScript->_Register();
+
+ if (!auraScript->_Validate(spellEntry))
+ {
+ itr->second.second = false;
+ continue;
+ }
+ }
+
+ // Enable the script when all checks passed
+ itr->second.second = true;
}
+ else
+ itr->second.second = false;
}
- ++count;
}
TC_LOG_INFO("server.loading", ">> Validated %u scripts in %u ms", count, GetMSTimeDiffToNow(oldMSTime));
@@ -8571,6 +8585,8 @@ void ObjectMgr::LoadScriptNames()
{
uint32 oldMSTime = getMSTime();
+ // We insert an empty placeholder here so we can use the
+ // script id 0 as dummy for "no script found".
_scriptNamesStore.emplace_back("");
QueryResult result = WorldDatabase.Query(
@@ -8612,18 +8628,18 @@ void ObjectMgr::LoadScriptNames()
std::sort(_scriptNamesStore.begin(), _scriptNamesStore.end());
-#ifdef SCRIPTS
- for (size_t i = 1; i < _scriptNamesStore.size(); ++i)
- UnusedScriptNames.push_back(_scriptNamesStore[i]);
-#endif
-
TC_LOG_INFO("server.loading", ">> Loaded " SZFMTD " ScriptNames in %u ms", _scriptNamesStore.size(), GetMSTimeDiffToNow(oldMSTime));
}
+ObjectMgr::ScriptNameContainer const& ObjectMgr::GetAllScriptNames() const
+{
+ return _scriptNamesStore;
+}
+
std::string const& ObjectMgr::GetScriptName(uint32 id) const
{
static std::string const empty = "";
- return id < _scriptNamesStore.size() ? _scriptNamesStore[id] : empty;
+ return (id < _scriptNamesStore.size()) ? _scriptNamesStore[id] : empty;
}
diff --git a/src/server/game/Globals/ObjectMgr.h b/src/server/game/Globals/ObjectMgr.h
index 73c03849699..576a8d2ccac 100644
--- a/src/server/game/Globals/ObjectMgr.h
+++ b/src/server/game/Globals/ObjectMgr.h
@@ -370,7 +370,7 @@ struct ScriptInfo
typedef std::multimap<uint32, ScriptInfo> ScriptMap;
typedef std::map<uint32, ScriptMap > ScriptMapMap;
-typedef std::multimap<uint32, uint32> SpellScriptsContainer;
+typedef std::multimap<uint32 /*spell id*/, std::pair<uint32 /*script id*/, bool /*enabled*/>> SpellScriptsContainer;
typedef std::pair<SpellScriptsContainer::iterator, SpellScriptsContainer::iterator> SpellScriptsBounds;
TC_GAME_API extern ScriptMapMap sSpellScripts;
TC_GAME_API extern ScriptMapMap sEventScripts;
@@ -1272,6 +1272,7 @@ class TC_GAME_API ObjectMgr
bool IsVendorItemValid(uint32 vendor_entry, uint32 item, int32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* player = NULL, std::set<uint32>* skip_vendors = NULL, uint32 ORnpcflag = 0) const;
void LoadScriptNames();
+ ScriptNameContainer const& GetAllScriptNames() const;
std::string const& GetScriptName(uint32 id) const;
uint32 GetScriptId(std::string const& name);
diff --git a/src/server/game/Handlers/CharacterHandler.cpp b/src/server/game/Handlers/CharacterHandler.cpp
index c3c78a4cf66..19638ec1bf8 100644
--- a/src/server/game/Handlers/CharacterHandler.cpp
+++ b/src/server/game/Handlers/CharacterHandler.cpp
@@ -642,7 +642,7 @@ void WorldSession::HandleCharCreateCallback(PreparedQueryResult result, Characte
TC_LOG_INFO("entities.player.character", "Account: %d (IP: %s) Create Character:[%s] (GUID: %u)", GetAccountId(), GetRemoteAddress().c_str(), createInfo->Name.c_str(), newChar.GetGUID().GetCounter());
sScriptMgr->OnPlayerCreate(&newChar);
- sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, 0), newChar.getRace(), newChar.getClass(), newChar.getLevel());
+ sWorld->AddCharacterInfo(newChar.GetGUID(), GetAccountId(), newChar.GetName(), newChar.GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER), newChar.getRace(), newChar.getClass(), newChar.getLevel());
newChar.CleanupsBeforeDelete();
delete createInfo;
@@ -1242,20 +1242,25 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair);
- if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
+ if (!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER))
return;
BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair);
- if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->GetByteValue(PLAYER_BYTES_3, 0))
+ if (!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER))
return;
BarberShopStyleEntry const* bs_skinColor = sBarberShopStyleStore.LookupEntry(SkinColor);
- if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->GetByteValue(PLAYER_BYTES_3, 0)))
+ if (bs_skinColor && (bs_skinColor->type != 3 || bs_skinColor->race != _player->getRace() || bs_skinColor->gender != _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER)))
return;
- if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->GetByteValue(PLAYER_BYTES_3, 0), bs_hair->hair_id, Color, _player->GetByteValue(PLAYER_BYTES, 1), bs_facialHair->hair_id, bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, 0)))
+ if (!Player::ValidateAppearance(_player->getRace(), _player->getClass(), _player->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER),
+ bs_hair->hair_id,
+ Color,
+ _player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID),
+ bs_facialHair->hair_id,
+ bs_skinColor ? bs_skinColor->hair_id : _player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID)))
return;
GameObject* go = _player->FindNearestGameObjectOfType(GAMEOBJECT_TYPE_BARBER_CHAIR, 5.0f);
@@ -1287,11 +1292,11 @@ void WorldSession::HandleAlterAppearance(WorldPacket& recvData)
_player->ModifyMoney(-int32(cost)); // it isn't free
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, cost);
- _player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id));
- _player->SetByteValue(PLAYER_BYTES, 3, uint8(Color));
- _player->SetByteValue(PLAYER_BYTES_2, 0, uint8(bs_facialHair->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID, uint8(bs_hair->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID, uint8(Color));
+ _player->SetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE, uint8(bs_facialHair->hair_id));
if (bs_skinColor)
- _player->SetByteValue(PLAYER_BYTES, 0, uint8(bs_skinColor->hair_id));
+ _player->SetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID, uint8(bs_skinColor->hair_id));
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1);
diff --git a/src/server/game/Handlers/MiscHandler.cpp b/src/server/game/Handlers/MiscHandler.cpp
index 8ae37065726..467d3730ab2 100644
--- a/src/server/game/Handlers/MiscHandler.cpp
+++ b/src/server/game/Handlers/MiscHandler.cpp
@@ -284,7 +284,7 @@ void WorldSession::HandleWhoOpcode(WorldPacket& recvData)
continue;
uint32 pzoneid = target->GetZoneId();
- uint8 gender = target->GetByteValue(PLAYER_BYTES_3, 0);
+ uint8 gender = target->GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER);
bool z_show = true;
for (uint32 i = 0; i < zones_count; ++i)
@@ -1056,12 +1056,14 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recvData)
void WorldSession::HandleCompleteCinematic(WorldPacket& /*recvData*/)
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_COMPLETE_CINEMATIC");
+ // If player has sight bound to visual waypoint NPC we should remove it
+ GetPlayer()->EndCinematic();
}
void WorldSession::HandleNextCinematicCamera(WorldPacket& /*recvData*/)
{
- TC_LOG_DEBUG("network", "WORLD: Received CMSG_NEXT_CINEMATIC_CAMERA");
+ // Sent by client when cinematic actually begun. So we begin the server side process
+ GetPlayer()->BeginCinematic();
}
void WorldSession::HandleMoveTimeSkippedOpcode(WorldPacket& recvData)
@@ -1157,7 +1159,7 @@ void WorldSession::HandleSetActionBarToggles(WorldPacket& recvData)
return;
}
- GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, actionBar);
+ GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_ACTION_BAR_TOGGLES, actionBar);
}
void WorldSession::HandlePlayedTime(WorldPacket& recvData)
diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp
index 02702fc5622..64f927a987f 100644
--- a/src/server/game/Handlers/MovementHandler.cpp
+++ b/src/server/game/Handlers/MovementHandler.cpp
@@ -473,7 +473,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recvData)
{
TC_LOG_ERROR("network", "%sSpeedChange player %s is NOT correct (must be %f instead %f), force set to correct value",
move_type_name[move_type], _player->GetName().c_str(), _player->GetSpeed(move_type), newspeed);
- _player->SetSpeed(move_type, _player->GetSpeedRate(move_type), true);
+ _player->SetSpeedRate(move_type, _player->GetSpeedRate(move_type));
}
else // must be lesser - cheating
{
diff --git a/src/server/game/Handlers/SpellHandler.cpp b/src/server/game/Handlers/SpellHandler.cpp
index 6be1fd30ae3..e07e10ab00c 100644
--- a/src/server/game/Handlers/SpellHandler.cpp
+++ b/src/server/game/Handlers/SpellHandler.cpp
@@ -594,11 +594,11 @@ void WorldSession::HandleMirrorImageDataRequest(WorldPacket& recvData)
if (creator->GetTypeId() == TYPEID_PLAYER)
{
Player* player = creator->ToPlayer();
- data << uint8(player->GetByteValue(PLAYER_BYTES, 0)); // skin
- data << uint8(player->GetByteValue(PLAYER_BYTES, 1)); // face
- data << uint8(player->GetByteValue(PLAYER_BYTES, 2)); // hair
- data << uint8(player->GetByteValue(PLAYER_BYTES, 3)); // haircolor
- data << uint8(player->GetByteValue(PLAYER_BYTES_2, 0)); // facialhair
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_SKIN_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_FACE_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_STYLE_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES, PLAYER_BYTES_OFFSET_HAIR_COLOR_ID));
+ data << uint8(player->GetByteValue(PLAYER_BYTES_2, PLAYER_BYTES_2_OFFSET_FACIAL_STYLE));
data << uint32(player->GetGuildId()); // unk
static EquipmentSlots const itemSlots[] =
diff --git a/src/server/game/Instances/InstanceScript.cpp b/src/server/game/Instances/InstanceScript.cpp
index 1575b50098f..0887d183a8b 100644
--- a/src/server/game/Instances/InstanceScript.cpp
+++ b/src/server/game/Instances/InstanceScript.cpp
@@ -29,6 +29,8 @@
#include "Pet.h"
#include "WorldSession.h"
#include "Opcodes.h"
+#include "ScriptReloadMgr.h"
+#include "ScriptMgr.h"
BossBoundaryData::~BossBoundaryData()
{
@@ -36,6 +38,18 @@ BossBoundaryData::~BossBoundaryData()
delete it->Boundary;
}
+InstanceScript::InstanceScript(Map* map) : instance(map), completedEncounters(0)
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ uint32 scriptId = sObjectMgr->GetInstanceTemplate(map->GetId())->ScriptId;
+ auto const scriptname = sObjectMgr->GetScriptName(scriptId);
+ ASSERT(!scriptname.empty());
+ // Acquire a strong reference from the script module
+ // to keep it loaded until this object is destroyed.
+ module_reference = sScriptMgr->AcquireModuleReferenceOfScriptName(scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+}
+
void InstanceScript::SaveToDB()
{
std::string data = GetSaveData();
diff --git a/src/server/game/Instances/InstanceScript.h b/src/server/game/Instances/InstanceScript.h
index 949cdf5abcb..3814afe2f4b 100644
--- a/src/server/game/Instances/InstanceScript.h
+++ b/src/server/game/Instances/InstanceScript.h
@@ -36,6 +36,7 @@ class Unit;
class Player;
class GameObject;
class Creature;
+class ModuleReference;
enum EncounterFrameType
{
@@ -137,7 +138,7 @@ typedef std::map<uint32 /*entry*/, uint32 /*type*/> ObjectInfoMap;
class TC_GAME_API InstanceScript : public ZoneScript
{
public:
- explicit InstanceScript(Map* map) : instance(map), completedEncounters(0) { }
+ explicit InstanceScript(Map* map);
virtual ~InstanceScript() { }
@@ -289,6 +290,11 @@ class TC_GAME_API InstanceScript : public ZoneScript
ObjectInfoMap _gameObjectInfo;
ObjectGuidMap _objectGuids;
uint32 completedEncounters; // completed encounter mask, bit indexes are DungeonEncounter.dbc boss numbers, used for packets
+
+ #ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Strong reference to the associated script module
+ std::shared_ptr<ModuleReference> module_reference;
+ #endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
};
template<class AI, class T>
diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp
index 8640150d86b..794be12ee7c 100644
--- a/src/server/game/Maps/Map.cpp
+++ b/src/server/game/Maps/Map.cpp
@@ -17,6 +17,7 @@
*/
#include "Map.h"
+#include "MapManager.h"
#include "Battleground.h"
#include "MMapFactory.h"
#include "CellImpl.h"
@@ -64,7 +65,7 @@ Map::~Map()
}
if (!m_scriptSchedule.empty())
- sScriptMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
+ sMapMgr->DecreaseScheduledScriptCount(m_scriptSchedule.size());
MMAP::MMapFactory::createOrGetMMapManager()->unloadMapInstance(GetId(), i_InstanceId);
}
@@ -713,6 +714,15 @@ void Map::Update(const uint32 t_diff)
VisitNearbyCellsOf(player, grid_object_update, world_object_update);
+ // If player is using far sight, visit that object too
+ if (WorldObject* viewPoint = player->GetViewpoint())
+ {
+ if (Creature* viewCreature = viewPoint->ToCreature())
+ VisitNearbyCellsOf(viewCreature, grid_object_update, world_object_update);
+ else if (DynamicObject* viewObject = viewPoint->ToDynObject())
+ VisitNearbyCellsOf(viewObject, grid_object_update, world_object_update);
+ }
+
// Handle updates for creatures in combat with player and are more than 60 yards away
if (player->IsInCombat())
{
@@ -2335,9 +2345,9 @@ float Map::GetHeight(float x, float y, float z, bool checkVMap /*= true*/, float
{
// we have mapheight and vmapheight and must select more appropriate
- // we are already under the surface or vmap height above map heigt
+ // vmap height above map height
// or if the distance of the vmap height is less the land height distance
- if (z < mapHeight || vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z))
+ if (vmapHeight > mapHeight || std::fabs(mapHeight - z) > std::fabs(vmapHeight - z))
return vmapHeight;
else
return mapHeight; // better use .map surface height
@@ -2657,8 +2667,8 @@ void Map::UpdateObjectsVisibilityFor(Player* player, Cell cell, CellCoord cellpa
cell.SetNoCreate();
TypeContainerVisitor<Trinity::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
TypeContainerVisitor<Trinity::VisibleNotifier, GridTypeMapContainer > grid_notifier(notifier);
- cell.Visit(cellpair, world_notifier, *this, *player, player->GetSightRange());
- cell.Visit(cellpair, grid_notifier, *this, *player, player->GetSightRange());
+ cell.Visit(cellpair, world_notifier, *this, *player->m_seer, player->GetSightRange());
+ cell.Visit(cellpair, grid_notifier, *this, *player->m_seer, player->GetSightRange());
// send data
notifier.SendToSelf();
diff --git a/src/server/game/Maps/MapManager.cpp b/src/server/game/Maps/MapManager.cpp
index c1882b3dc75..79a8b3855e8 100644
--- a/src/server/game/Maps/MapManager.cpp
+++ b/src/server/game/Maps/MapManager.cpp
@@ -38,10 +38,10 @@
#include "AchievementMgr.h"
MapManager::MapManager()
+ : _nextInstanceId(0), _scheduledScripts(0)
{
i_gridCleanUpDelay = sWorld->getIntConfig(CONFIG_INTERVAL_GRIDCLEAN);
i_timer.SetInterval(sWorld->getIntConfig(CONFIG_INTERVAL_MAPUPDATE));
- _nextInstanceId = 0;
}
MapManager::~MapManager() { }
diff --git a/src/server/game/Maps/MapManager.h b/src/server/game/Maps/MapManager.h
index e74453a1196..a7fdc37d324 100644
--- a/src/server/game/Maps/MapManager.h
+++ b/src/server/game/Maps/MapManager.h
@@ -126,6 +126,11 @@ 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; }
+ bool IsScriptScheduled() const { return _scheduledScripts > 0; }
+
private:
typedef std::unordered_map<uint32, Map*> MapMapType;
typedef std::vector<bool> InstanceIds;
@@ -150,6 +155,9 @@ class TC_GAME_API MapManager
InstanceIds _instanceIds;
uint32 _nextInstanceId;
MapUpdater m_updater;
+
+ // atomic op counter for active scripts amount
+ std::atomic<uint32> _scheduledScripts;
};
template<typename Worker>
diff --git a/src/server/game/Scripting/MapScripts.cpp b/src/server/game/Maps/MapScripts.cpp
index a01c109b9ca..c5d7bdc64ce 100644
--- a/src/server/game/Scripting/MapScripts.cpp
+++ b/src/server/game/Maps/MapScripts.cpp
@@ -21,6 +21,7 @@
#include "GridNotifiersImpl.h"
#include "GossipDef.h"
#include "Map.h"
+#include "MapManager.h"
#include "ObjectMgr.h"
#include "Pet.h"
#include "Item.h"
@@ -58,7 +59,7 @@ void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, O
if (iter->first == 0)
immedScript = true;
- sScriptMgr->IncreaseScheduledScriptsCount();
+ sMapMgr->IncreaseScheduledScriptsCount();
}
///- If one of the effects should be immediate, launch the script execution
if (/*start &&*/ immedScript && !i_scriptLock)
@@ -86,7 +87,7 @@ void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* sou
sa.script = &script;
m_scriptSchedule.insert(ScriptScheduleMap::value_type(time_t(sWorld->GetGameTime() + delay), sa));
- sScriptMgr->IncreaseScheduledScriptsCount();
+ sMapMgr->IncreaseScheduledScriptsCount();
///- If effects should be immediate, launch the script execution
if (delay == 0 && !i_scriptLock)
@@ -878,6 +879,6 @@ void Map::ScriptsProcess()
m_scriptSchedule.erase(iter);
iter = m_scriptSchedule.begin();
- sScriptMgr->DecreaseScheduledScriptCount();
+ sMapMgr->DecreaseScheduledScriptCount();
}
}
diff --git a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
index b595059557a..d3c6c70c4ed 100644
--- a/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
+++ b/src/server/game/Movement/MovementGenerators/TargetedMovementGenerator.cpp
@@ -265,9 +265,9 @@ void FollowMovementGenerator<Creature>::_updateSpeed(Creature* owner)
if (!owner->IsPet() || !owner->IsInWorld() || !i_target.isValid() || i_target->GetGUID() != owner->GetOwnerGUID())
return;
- owner->UpdateSpeed(MOVE_RUN, true);
- owner->UpdateSpeed(MOVE_WALK, true);
- owner->UpdateSpeed(MOVE_SWIM, true);
+ owner->UpdateSpeed(MOVE_RUN);
+ owner->UpdateSpeed(MOVE_WALK);
+ owner->UpdateSpeed(MOVE_SWIM);
}
template<>
diff --git a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
index 7f0695e16b3..aa00d211d6e 100644
--- a/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
+++ b/src/server/game/OutdoorPvP/OutdoorPvPMgr.cpp
@@ -33,8 +33,14 @@ void OutdoorPvPMgr::Die()
for (OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
delete *itr;
+ m_OutdoorPvPSet.clear();
+
for (OutdoorPvPDataMap::iterator itr = m_OutdoorPvPDatas.begin(); itr != m_OutdoorPvPDatas.end(); ++itr)
delete itr->second;
+
+ m_OutdoorPvPDatas.clear();
+
+ m_OutdoorPvPMap.clear();
}
OutdoorPvPMgr* OutdoorPvPMgr::instance()
diff --git a/src/server/game/Scripting/ScriptMgr.cpp b/src/server/game/Scripting/ScriptMgr.cpp
index 4218746893f..ca1cd71363e 100644
--- a/src/server/game/Scripting/ScriptMgr.cpp
+++ b/src/server/game/Scripting/ScriptMgr.cpp
@@ -17,6 +17,7 @@
*/
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "Config.h"
#include "DatabaseEnv.h"
#include "DBCStores.h"
@@ -34,11 +35,9 @@
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Chat.h"
-
-// namespace
-// {
- UnusedScriptNamesContainer UnusedScriptNames;
-// }
+#include "MapManager.h"
+#include "LFGScripts.h"
+#include "InstanceScript.h"
// Trait which indicates whether this script type
// must be assigned in the database.
@@ -67,6 +66,10 @@ struct is_script_database_bound<GameObjectScript>
: std::true_type { };
template<>
+struct is_script_database_bound<VehicleScript>
+ : std::true_type { };
+
+template<>
struct is_script_database_bound<AreaTriggerScript>
: std::true_type { };
@@ -94,143 +97,846 @@ template<>
struct is_script_database_bound<AchievementCriteriaScript>
: std::true_type { };
+enum Spells
+{
+ SPELL_HOTSWAP_VISUAL_SPELL_EFFECT = 40162 // 59084
+};
+
+class ScriptRegistryInterface
+{
+public:
+ ScriptRegistryInterface() { }
+ virtual ~ScriptRegistryInterface() { }
+
+ ScriptRegistryInterface(ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface(ScriptRegistryInterface&&) = delete;
+
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface const&) = delete;
+ ScriptRegistryInterface& operator= (ScriptRegistryInterface&&) = delete;
+
+ /// Removes all scripts associated with the given script context.
+ /// Requires ScriptRegistryBase::SwapContext to be called after all transfers have finished.
+ virtual void ReleaseContext(std::string const& context) = 0;
+
+ /// Injects and updates the changed script objects.
+ virtual void SwapContext(bool initialize) = 0;
+
+ /// Removes the scripts used by this registry from the given container.
+ /// Used to find unused script names.
+ virtual void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) = 0;
+
+ /// Unloads the script registry.
+ virtual void Unload() = 0;
+};
+
+template<class>
+class ScriptRegistry;
+
+class ScriptRegistryCompositum
+ : public ScriptRegistryInterface
+{
+ ScriptRegistryCompositum() { }
+
+ template<class>
+ friend class ScriptRegistry;
+
+ /// Type erasure wrapper for objects
+ class DeleteableObjectBase
+ {
+ public:
+ DeleteableObjectBase() { }
+ virtual ~DeleteableObjectBase() { }
+
+ DeleteableObjectBase(DeleteableObjectBase const&) = delete;
+ DeleteableObjectBase& operator= (DeleteableObjectBase const&) = delete;
+ };
+
+ template<typename T>
+ class DeleteableObject
+ : public DeleteableObjectBase
+ {
+ public:
+ DeleteableObject(T&& object)
+ : _object(std::forward<T>(object)) { }
+
+ private:
+ T _object;
+ };
+
+public:
+ void SetScriptNameInContext(std::string const& scriptname, std::string const& context)
+ {
+ ASSERT(_scriptnames_to_context.find(scriptname) == _scriptnames_to_context.end(),
+ "Scriptname was assigned to this context already!");
+ _scriptnames_to_context.insert(std::make_pair(scriptname, context));
+ }
+
+ std::string const& GetScriptContextOfScriptName(std::string const& scriptname) const
+ {
+ auto itr = _scriptnames_to_context.find(scriptname);
+ ASSERT(itr != _scriptnames_to_context.end() &&
+ "Given scriptname doesn't exist!");
+ return itr->second;
+ }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ for (auto const registry : _registries)
+ registry->ReleaseContext(context);
+
+ // Clear the script names in context after calling the release hooks
+ // since it's possible that new references to a shared library
+ // are acquired when releasing.
+ for (auto itr = _scriptnames_to_context.begin();
+ itr != _scriptnames_to_context.end();)
+ if (itr->second == context)
+ itr = _scriptnames_to_context.erase(itr);
+ else
+ ++itr;
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ for (auto const registry : _registries)
+ registry->SwapContext(initialize);
+
+ DoDelayedDelete();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const registry : _registries)
+ registry->RemoveUsedScriptsFromContainer(scripts);
+ }
+
+ void Unload() final override
+ {
+ for (auto const registry : _registries)
+ registry->Unload();
+ }
+
+ template<typename T>
+ void QueueForDelayedDelete(T&& any)
+ {
+ _delayed_delete_queue.push_back(
+ Trinity::make_unique<
+ DeleteableObject<typename std::decay<T>::type>
+ >(std::forward<T>(any))
+ );
+ }
+
+ static ScriptRegistryCompositum* Instance()
+ {
+ static ScriptRegistryCompositum instance;
+ return &instance;
+ }
+
+private:
+ void Register(ScriptRegistryInterface* registry)
+ {
+ _registries.insert(registry);
+ }
+
+ void DoDelayedDelete()
+ {
+ _delayed_delete_queue.clear();
+ }
+
+ std::unordered_set<ScriptRegistryInterface*> _registries;
+
+ std::vector<std::unique_ptr<DeleteableObjectBase>> _delayed_delete_queue;
+
+ std::unordered_map<
+ std::string /*script name*/,
+ std::string /*context*/
+ > _scriptnames_to_context;
+};
+
+#define sScriptRegistryCompositum ScriptRegistryCompositum::Instance()
+
+template<typename /*ScriptType*/, bool /*IsDatabaseBound*/>
+class SpecializedScriptRegistry;
+
// This is the global static registry of scripts.
-template<class TScript>
-class ScriptRegistry
+template<class ScriptType>
+class ScriptRegistry final
+ : public SpecializedScriptRegistry<
+ ScriptType, is_script_database_bound<ScriptType>::value>
+{
+ ScriptRegistry()
+ {
+ sScriptRegistryCompositum->Register(this);
+ }
+
+public:
+ static ScriptRegistry* Instance()
+ {
+ static ScriptRegistry instance;
+ return &instance;
+ }
+
+ void LogDuplicatedScriptPointerError(ScriptType const* first, ScriptType const* second)
+ {
+ // See if the script is using the same memory as another script. If this happens, it means that
+ // someone forgot to allocate new memory for a script.
+ TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
+ first->GetName().c_str(), second->GetName().c_str());
+ }
+};
+
+class ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHookBase() { }
+ virtual ~ScriptRegistrySwapHookBase() { }
+
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase(ScriptRegistrySwapHookBase&&) = delete;
+
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase const&) = delete;
+ ScriptRegistrySwapHookBase& operator= (ScriptRegistrySwapHookBase&&) = delete;
+
+ /// Called before the actual context release happens
+ virtual void BeforeReleaseContext(std::string const& /*context*/) { }
+
+ /// Called before SwapContext
+ virtual void BeforeSwapContext(bool /*initialize*/) { }
+
+ /// Called before Unload
+ virtual void BeforeUnload() { }
+};
+
+template<typename ScriptType, typename Base>
+class ScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+};
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class UnsupportedScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ ASSERT(bounds.first == bounds.second);
+ }
+};
+
+/// This hook is responsible for swapping Creature and GameObject AI's
+template<typename ObjectType, typename ScriptType, typename Base>
+class CreatureGameObjectScriptRegistrySwapHooks
+ : public ScriptRegistrySwapHookBase
{
+ template<typename W>
+ class AIFunctionMapWorker
+ {
+ public:
+ template<typename T>
+ AIFunctionMapWorker(T&& worker)
+ : _worker(std::forward<T>(worker)) { }
+
+ void Visit(std::unordered_map<ObjectGuid, ObjectType*>& objects)
+ {
+ _worker(objects);
+ }
+
+ template<typename O>
+ void Visit(std::unordered_map<ObjectGuid, O*>&) { }
+
+ private:
+ W _worker;
+ };
+
+ class AsyncCastHotswapEffectEvent : public BasicEvent
+ {
public:
+ explicit AsyncCastHotswapEffectEvent(Unit* owner) : owner_(owner) { }
+
+ bool Execute(uint64 /*e_time*/, uint32 /*p_time*/) override
+ {
+ owner_->CastSpell(owner_, SPELL_HOTSWAP_VISUAL_SPELL_EFFECT, true);
+ return true;
+ }
+
+ private:
+ Unit* owner_;
+ };
+
+ // Hook which is called before a creature is swapped
+ static void UnloadStage1(Creature* creature)
+ {
+ if (creature->IsCharmed())
+ creature->RemoveCharmedBy(nullptr);
+
+ ASSERT(!creature->IsCharmed(),
+ "There is a disabled AI which is still loaded.");
- typedef std::map<uint32, TScript*> ScriptMap;
- typedef typename ScriptMap::iterator ScriptMapIterator;
+ creature->AI()->EnterEvadeMode();
+ }
+
+ static void UnloadStage2(Creature* creature)
+ {
+ bool const destroyed = creature->AIM_Destroy();
+ ASSERT(destroyed,
+ "Destroying the AI should never fail here!");
+ (void)destroyed;
- // The actual list of scripts. This will be accessed concurrently, so it must not be modified
- // after server startup.
- static ScriptMap ScriptPointerList;
- static std::vector<TScript*> Scripts;
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+ }
- static void AddScript(TScript* const script, bool addToDeleteContainer = true)
+ // Hook which is called before a gameobject is swapped
+ static void UnloadStage1(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
+
+ static void UnloadStage2(GameObject* gameobject)
+ {
+ gameobject->AIM_Destroy();
+
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+ }
+
+ // Hook which is called after a creature was swapped
+ static void LoadStage1(Creature* creature)
+ {
+ ASSERT(!creature->AI(),
+ "The AI should be null here!");
+
+ if (creature->IsAlive())
+ creature->ClearUnitState(UNIT_STATE_EVADE);
+
+ bool const created = creature->AIM_Initialize();
+ ASSERT(created,
+ "Creating the AI should never fail here!");
+ (void)created;
+ }
+
+ static void LoadStage2(Creature* creature)
+ {
+ if (!creature->IsAlive())
+ return;
+
+ creature->AI()->EnterEvadeMode();
+
+ // Cast a dummy visual spell asynchronously here to signal
+ // that the AI was hot swapped
+ creature->m_Events.AddEvent(new AsyncCastHotswapEffectEvent(creature),
+ creature->m_Events.CalculateTime(0));
+ }
+
+ // Hook which is called after a gameobject was swapped
+ static void LoadStage1(GameObject* gameobject)
+ {
+ ASSERT(!gameobject->AI(),
+ "The AI should be null here!");
+
+ gameobject->AIM_Initialize();
+ }
+
+ static void LoadStage2(GameObject* gameobject)
+ {
+ gameobject->AI()->Reset();
+ }
+
+ template<typename T>
+ void RunOverAllEntities(T fn)
+ {
+ auto evaluator = [&](std::unordered_map<ObjectGuid, ObjectType*>& objects)
{
- ASSERT(script);
+ for (auto object : objects)
+ fn(object.second);
+ };
- // See if the script is using the same memory as another script. If this happens, it means that
- // someone forgot to allocate new memory for a script.
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
+ AIFunctionMapWorker<typename std::decay<decltype(evaluator)>::type> worker(std::move(evaluator));
+ TypeContainerVisitor<decltype(worker), MapStoredObjectTypesContainer> visitor(worker);
+
+ sMapMgr->DoForAllMaps([&](Map* map)
+ {
+ // Run the worker over all maps
+ visitor.Visit(map->GetObjectsStore());
+ });
+ }
+
+public:
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto ids_to_remove = static_cast<Base*>(this)->GetScriptIDsToRemove(context);
+
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_to_remove.find(object->GetScriptId()) != ids_to_remove.end())
{
- if (it->second == script)
- {
- TC_LOG_ERROR("scripts", "Script '%s' has same memory pointer as '%s'.",
- script->GetName().c_str(), it->second->GetName().c_str());
+ UnloadStage1(object);
+ stage2.push_back(object);
+ }
+ });
- return;
+ for (auto object : stage2)
+ UnloadStage2(object);
+
+ // Add the new ids which are removed to the global ids to remove set
+ ids_removed_.insert(ids_to_remove.begin(), ids_to_remove.end());
+ }
+
+ void BeforeSwapContext(bool initialize) override
+ {
+ // Never swap creature or gameobject scripts when initializing
+ if (initialize)
+ return;
+
+ // Add the recently added scripts to the deleted scripts to replace
+ // default AI's with recently added core scripts.
+ ids_removed_.insert(static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().begin(),
+ static_cast<Base*>(this)->GetRecentlyAddedScriptIDs().end());
+
+ std::vector<ObjectType*> remove;
+ std::vector<ObjectType*> stage2;
+
+ RunOverAllEntities([&](ObjectType* object)
+ {
+ if (ids_removed_.find(object->GetScriptId()) != ids_removed_.end())
+ {
+ if (object->AI())
+ {
+ // Overwrite existing (default) AI's which are replaced by a new script
+ UnloadStage1(object);
+ remove.push_back(object);
}
+
+ stage2.push_back(object);
}
+ });
+
+ for (auto object : remove)
+ UnloadStage2(object);
- AddScript(is_script_database_bound<TScript>{}, script);
- if (addToDeleteContainer)
- Scripts.push_back(script);
+ for (auto object : stage2)
+ LoadStage1(object);
+
+ for (auto object : stage2)
+ LoadStage2(object);
+
+ ids_removed_.clear();
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(ids_removed_.empty());
+ }
+
+private:
+ std::unordered_set<uint32> ids_removed_;
+};
+
+// This hook is responsible for swapping CreatureAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<CreatureScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ Creature, CreatureScript, Base
+ > { };
+
+// This hook is responsible for swapping GameObjectAI's
+template<typename Base>
+class ScriptRegistrySwapHooks<GameObjectScript, Base>
+ : public CreatureGameObjectScriptRegistrySwapHooks<
+ GameObject, GameObjectScript, Base
+ > { };
+
+/// This hook is responsible for swapping BattlegroundScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<BattlegroundScript, Base>
+ : public UnsupportedScriptRegistrySwapHooks<Base> { };
+
+/// This hook is responsible for swapping OutdoorPvP's
+template<typename Base>
+class ScriptRegistrySwapHooks<OutdoorPvPScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if ((!swapped) && (bounds.first != bounds.second))
+ {
+ swapped = true;
+ sOutdoorPvPMgr->Die();
}
+ }
- // Gets a script by its ID (assigned by ObjectMgr).
- static TScript* GetScriptById(uint32 id)
+ void BeforeSwapContext(bool initialize) override
+ {
+ // Never swap outdoor pvp scripts when initializing
+ if ((!initialize) && swapped)
{
- ScriptMapIterator it = ScriptPointerList.find(id);
- if (it != ScriptPointerList.end())
- return it->second;
+ sOutdoorPvPMgr->InitOutdoorPvP();
+ swapped = false;
+ }
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+/// This hook is responsible for swapping InstanceMapScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<InstanceMapScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
- return NULL;
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ swapped = false;
+ }
+
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+/// This hook is responsible for swapping SpellScriptLoader's
+template<typename Base>
+class ScriptRegistrySwapHooks<SpellScriptLoader, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ ScriptRegistrySwapHooks() : swapped(false) { }
+
+ void BeforeReleaseContext(std::string const& context) final override
+ {
+ auto const bounds = static_cast<Base*>(this)->_ids_of_contexts.equal_range(context);
+
+ if (bounds.first != bounds.second)
+ swapped = true;
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ if (swapped)
+ {
+ sObjectMgr->ValidateSpellScripts();
+ swapped = false;
}
+ }
- private:
+ void BeforeUnload() final override
+ {
+ ASSERT(!swapped);
+ }
+
+private:
+ bool swapped;
+};
+
+// Database bound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, true>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename>
+ friend class UnsupportedScriptRegistrySwapHooks;
+
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+ template<typename, typename, typename>
+ friend class CreatureGameObjectScriptRegistrySwapHooks;
+
+public:
+ SpecializedScriptRegistry() { }
+
+ typedef std::unordered_map<
+ uint32 /*script id*/,
+ std::unique_ptr<ScriptType>
+ > ScriptStoreType;
+
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ _scripts.erase(itr->second);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ this->BeforeSwapContext(initialize);
+
+ _recently_added_ids.clear();
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ ASSERT(_recently_added_ids.empty(),
+ "Recently added script ids should be empty here!");
+
+ _scripts.clear();
+ _ids_of_contexts.clear();
+ }
+
+ // Adds a database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
- // Adds a database bound script
- static void AddScript(std::true_type, TScript* const script)
+ std::unique_ptr<ScriptType> script_ptr(script);
+
+ // Get an ID for the script. An ID only exists if it's a script that is assigned in the database
+ // through a script name (or similar).
+ if (uint32 const id = sObjectMgr->GetScriptId(script->GetName()))
{
- // Get an ID for the script. An ID only exists if it's a script that is assigned in the database
- // through a script name (or similar).
- uint32 id = sObjectMgr->GetScriptId(script->GetName());
- if (id)
+ // Try to find an existing script.
+ for (auto const& stored_script : _scripts)
{
- // Try to find an existing script.
- bool existing = false;
- for (ScriptMapIterator it = ScriptPointerList.begin(); it != ScriptPointerList.end(); ++it)
- {
- // If the script names match...
- if (it->second->GetName() == script->GetName())
- {
- // ... It exists.
- existing = true;
- break;
- }
- }
-
- // If the script isn't assigned -> assign it!
- if (!existing)
- {
- ScriptPointerList[id] = script;
- sScriptMgr->IncrementScriptCount();
-
- #ifdef SCRIPTS
- UnusedScriptNamesContainer::iterator itr = std::lower_bound(UnusedScriptNames.begin(), UnusedScriptNames.end(), script->GetName());
- if (itr != UnusedScriptNames.end() && *itr == script->GetName())
- UnusedScriptNames.erase(itr);
- #endif
- }
- else
+ // If the script names match...
+ if (stored_script.second->GetName() == script->GetName())
{
// If the script is already assigned -> delete it!
- TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, so the script can't work.",
- script->GetName().c_str());
+ TC_LOG_ERROR("scripts", "Script '%s' already assigned with the same script name, "
+ "so the script can't work.", script->GetName().c_str());
- ABORT(); // Error that should be fixed ASAP.
+ // Error that should be fixed ASAP.
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ ABORT();
+ return;
}
}
- else
- {
- // The script uses a script name from database, but isn't assigned to anything.
- TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.", script->GetName().c_str());
- }
- }
- // Adds a non database bound script
- static void AddScript(std::false_type, TScript* const script)
+ // If the script isn't assigned -> assign it!
+ _scripts.insert(std::make_pair(id, std::move(script_ptr)));
+ _ids_of_contexts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), id));
+ _recently_added_ids.insert(id);
+
+ sScriptRegistryCompositum->SetScriptNameInContext(script->GetName(),
+ sScriptMgr->GetCurrentScriptContext());
+ }
+ else
{
- // We're dealing with a code-only script; just add it.
- ScriptPointerList[_scriptIdCounter++] = script;
- sScriptMgr->IncrementScriptCount();
+ // The script uses a script name from database, but isn't assigned to anything.
+ TC_LOG_ERROR("sql.sql", "Script named '%s' does not have a script name assigned in database.",
+ script->GetName().c_str());
+
+ // Avoid calling "delete script;" because we are currently in the script constructor
+ // In a valid scenario this will not happen because every script has a name assigned in the database
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ return;
}
+ }
+
+ // Gets a script by its ID (assigned by ObjectMgr).
+ ScriptType* GetScriptById(uint32 id)
+ {
+ auto const itr = _scripts.find(id);
+ if (itr != _scripts.end())
+ return itr->second.get();
+
+ return nullptr;
+ }
+
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
- // Counter used for code-only scripts.
- static uint32 _scriptIdCounter;
+protected:
+ // Returns the script id's which are registered to a certain context
+ std::unordered_set<uint32> GetScriptIDsToRemove(std::string const& context) const
+ {
+ // Create a set of all ids which are removed
+ std::unordered_set<uint32> scripts_to_remove;
+
+ auto const bounds = _ids_of_contexts.equal_range(context);
+ for (auto itr = bounds.first; itr != bounds.second; ++itr)
+ scripts_to_remove.insert(itr->second);
+
+ return scripts_to_remove;
+ }
+
+ std::unordered_set<uint32> const& GetRecentlyAddedScriptIDs() const
+ {
+ return _recently_added_ids;
+ }
+
+private:
+ ScriptStoreType _scripts;
+
+ // Scripts of a specific context
+ std::unordered_multimap<std::string /*context*/, uint32 /*id*/> _ids_of_contexts;
+
+ // Script id's which were registered recently
+ std::unordered_set<uint32> _recently_added_ids;
+};
+
+/// This hook is responsible for swapping CommandScript's
+template<typename Base>
+class ScriptRegistrySwapHooks<CommandScript, Base>
+ : public ScriptRegistrySwapHookBase
+{
+public:
+ void BeforeReleaseContext(std::string const& /*context*/) final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeSwapContext(bool /*initialize*/) override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+
+ void BeforeUnload() final override
+ {
+ ChatHandler::invalidateCommandTable();
+ }
+};
+
+// Database unbound script registry
+template<typename ScriptType>
+class SpecializedScriptRegistry<ScriptType, false>
+ : public ScriptRegistryInterface,
+ public ScriptRegistrySwapHooks<ScriptType, ScriptRegistry<ScriptType>>
+{
+ template<typename, typename>
+ friend class ScriptRegistrySwapHooks;
+
+public:
+ typedef std::unordered_multimap<std::string /*context*/, std::unique_ptr<ScriptType>> ScriptStoreType;
+ typedef typename ScriptStoreType::iterator ScriptStoreIteratorType;
+
+ SpecializedScriptRegistry() { }
+
+ void ReleaseContext(std::string const& context) final override
+ {
+ this->BeforeReleaseContext(context);
+
+ _scripts.erase(context);
+ }
+
+ void SwapContext(bool initialize) final override
+ {
+ this->BeforeSwapContext(initialize);
+ }
+
+ void RemoveUsedScriptsFromContainer(std::unordered_set<std::string>& scripts) final override
+ {
+ for (auto const& script : _scripts)
+ scripts.erase(script.second->GetName());
+ }
+
+ void Unload() final override
+ {
+ this->BeforeUnload();
+
+ _scripts.clear();
+ }
+
+ // Adds a non database bound script
+ void AddScript(ScriptType* script)
+ {
+ ASSERT(script,
+ "Tried to call AddScript with a nullpointer!");
+ ASSERT(!sScriptMgr->GetCurrentScriptContext().empty(),
+ "Tried to register a script without being in a valid script context!");
+
+ std::unique_ptr<ScriptType> script_ptr(script);
+
+ for (auto const& entry : _scripts)
+ if (entry.second.get() == script)
+ {
+ static_cast<ScriptRegistry<ScriptType>*>(this)->
+ LogDuplicatedScriptPointerError(script, entry.second.get());
+
+ sScriptRegistryCompositum->QueueForDelayedDelete(std::move(script_ptr));
+ return;
+ }
+
+ // We're dealing with a code-only script, just add it.
+ _scripts.insert(std::make_pair(sScriptMgr->GetCurrentScriptContext(), std::move(script_ptr)));
+ }
+
+ ScriptStoreType& GetScripts()
+ {
+ return _scripts;
+ }
+
+private:
+ ScriptStoreType _scripts;
};
// Utility macros to refer to the script registry.
-#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptMap
-#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptMapIterator
-#define SCR_REG_LST(T) ScriptRegistry<T>::ScriptPointerList
-#define SCR_REG_VEC(T) ScriptRegistry<T>::Scripts
+#define SCR_REG_MAP(T) ScriptRegistry<T>::ScriptStoreType
+#define SCR_REG_ITR(T) ScriptRegistry<T>::ScriptStoreIteratorType
+#define SCR_REG_LST(T) ScriptRegistry<T>::Instance()->GetScripts()
// Utility macros for looping over scripts.
#define FOR_SCRIPTS(T, C, E) \
if (SCR_REG_LST(T).empty()) \
return; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOR_SCRIPTS_RET(T, C, E, R) \
if (SCR_REG_LST(T).empty()) \
return R; \
+ \
for (SCR_REG_ITR(T) C = SCR_REG_LST(T).begin(); \
C != SCR_REG_LST(T).end(); ++C)
+
#define FOREACH_SCRIPT(T) \
FOR_SCRIPTS(T, itr, end) \
- itr->second
+ itr->second
// Utility macros for finding specific scripts.
#define GET_SCRIPT(T, I, V) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return;
+
#define GET_SCRIPT_RET(T, I, V, R) \
- T* V = ScriptRegistry<T>::GetScriptById(I); \
+ T* V = ScriptRegistry<T>::Instance()->GetScriptById(I); \
if (!V) \
return R;
@@ -240,8 +946,18 @@ struct TSpellSummary
uint8 Effects; // set of enum SelectEffect
} *SpellSummary;
+ScriptObject::ScriptObject(const char* name) : _name(name)
+{
+ sScriptMgr->IncreaseScriptCount();
+}
+
+ScriptObject::~ScriptObject()
+{
+ sScriptMgr->DecreaseScriptCount();
+}
+
ScriptMgr::ScriptMgr()
- : _scriptCount(0), _scheduledScripts(0), _script_loader_callback(nullptr)
+ : _scriptCount(0), _script_loader_callback(nullptr)
{
}
@@ -255,6 +971,9 @@ ScriptMgr* ScriptMgr::instance()
void ScriptMgr::Initialize()
{
+ ASSERT(sSpellMgr->GetSpellInfo(SPELL_HOTSWAP_VISUAL_SPELL_EFFECT)
+ && "Reload hotswap spell effect for creatures isn't valid!");
+
uint32 oldMSTime = getMSTime();
LoadDatabase();
@@ -263,59 +982,85 @@ void ScriptMgr::Initialize()
FillSpellSummary();
+ // Load core scripts
+ SetScriptContext("___static___");
+
+ // SmartAI
AddSC_SmartScripts();
+ // LFGScripts
+ lfg::AddSC_LFGScripts();
+
+ // Load all static linked scripts through the script loader function.
ASSERT(_script_loader_callback,
"Script loader callback wasn't registered!");
-
_script_loader_callback();
-#ifdef SCRIPTS
- for (std::string const& scriptName : UnusedScriptNames)
+ // Initialize all dynamic scripts
+ // and finishes the context switch to do
+ // bulk loading
+ sScriptReloadMgr->Initialize();
+
+ // Loads all scripts from the current context
+ sScriptMgr->SwapScriptContext(true);
+
+ // Print unused script names.
+ std::unordered_set<std::string> unusedScriptNames(
+ sObjectMgr->GetAllScriptNames().begin(),
+ sObjectMgr->GetAllScriptNames().end());
+
+ // Remove the used scripts from the given container.
+ sScriptRegistryCompositum->RemoveUsedScriptsFromContainer(unusedScriptNames);
+
+ for (std::string const& scriptName : unusedScriptNames)
{
- TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, but no core script found!", scriptName.c_str());
+ // Avoid complaining about empty script names since the
+ // script name container contains a placeholder as the 0 element.
+ if (scriptName.empty())
+ continue;
+
+ TC_LOG_ERROR("sql.sql", "ScriptName '%s' exists in database, "
+ "but no core script found!", scriptName.c_str());
}
-#endif
- TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms", GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
+ TC_LOG_INFO("server.loading", ">> Loaded %u C++ scripts in %u ms",
+ GetScriptCount(), GetMSTimeDiffToNow(oldMSTime));
+}
+
+void ScriptMgr::SetScriptContext(std::string const& context)
+{
+ _currentContext = context;
+}
+
+void ScriptMgr::SwapScriptContext(bool initialize)
+{
+ sScriptRegistryCompositum->SwapContext(initialize);
+ _currentContext.clear();
+}
+
+void ScriptMgr::ReleaseScriptContext(std::string const& context)
+{
+ sScriptRegistryCompositum->ReleaseContext(context);
+}
+
+std::shared_ptr<ModuleReference>
+ ScriptMgr::AcquireModuleReferenceOfScriptName(std::string const& scriptname) const
+{
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Returns the reference to the module of the given scriptname
+ return ScriptReloadMgr::AcquireModuleReferenceOfContext(
+ sScriptRegistryCompositum->GetScriptContextOfScriptName(scriptname));
+#else
+ (void)scriptname;
+ // Something went wrong when this function is used in
+ // a static linked context.
+ WPAbort();
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
void ScriptMgr::Unload()
{
- #define SCR_CLEAR(T) \
- for (T* scr : SCR_REG_VEC(T)) \
- delete scr; \
- SCR_REG_VEC(T).clear();
-
- // Clear scripts for every script type.
- SCR_CLEAR(SpellScriptLoader);
- SCR_CLEAR(ServerScript);
- SCR_CLEAR(WorldScript);
- SCR_CLEAR(FormulaScript);
- SCR_CLEAR(WorldMapScript);
- SCR_CLEAR(InstanceMapScript);
- SCR_CLEAR(BattlegroundMapScript);
- SCR_CLEAR(ItemScript);
- SCR_CLEAR(CreatureScript);
- SCR_CLEAR(GameObjectScript);
- SCR_CLEAR(AreaTriggerScript);
- SCR_CLEAR(BattlegroundScript);
- SCR_CLEAR(OutdoorPvPScript);
- SCR_CLEAR(CommandScript);
- SCR_CLEAR(WeatherScript);
- SCR_CLEAR(AuctionHouseScript);
- SCR_CLEAR(ConditionScript);
- SCR_CLEAR(VehicleScript);
- SCR_CLEAR(DynamicObjectScript);
- SCR_CLEAR(TransportScript);
- SCR_CLEAR(AchievementCriteriaScript);
- SCR_CLEAR(PlayerScript);
- SCR_CLEAR(AccountScript);
- SCR_CLEAR(GuildScript);
- SCR_CLEAR(GroupScript);
- SCR_CLEAR(UnitScript);
-
- #undef SCR_CLEAR
+ sScriptRegistryCompositum->Unload();
delete[] SpellSummary;
delete[] UnitAI::AISpellInfo;
@@ -413,38 +1158,22 @@ void ScriptMgr::FillSpellSummary()
}
}
-void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
+template<typename T, typename F>
+void CreateSpellOrAuraScripts(uint32 spellId, std::list<T*>& scriptVector, F&& extractor)
{
SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
{
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
+ // When the script is disabled continue with the next one
+ if (!itr->second.second)
continue;
- SpellScript* script = tmpscript->GetSpellScript();
-
- if (!script)
- continue;
-
- script->_Init(&tmpscript->GetName(), spellId);
-
- scriptVector.push_back(script);
- }
-}
-
-void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
-{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
-
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
+ SpellScriptLoader* tmpscript = sScriptMgr->GetSpellScriptLoader(itr->second.first);
if (!tmpscript)
continue;
- AuraScript* script = tmpscript->GetAuraScript();
+ T* script = (*tmpscript.*extractor)();
if (!script)
continue;
@@ -455,19 +1184,19 @@ void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& script
}
}
-void ScriptMgr::CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, SpellScriptsContainer::iterator> >& scriptVector)
+void ScriptMgr::CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector)
{
- SpellScriptsBounds bounds = sObjectMgr->GetSpellScriptsBounds(spellId);
- scriptVector.reserve(std::distance(bounds.first, bounds.second));
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetSpellScript);
+}
- for (SpellScriptsContainer::iterator itr = bounds.first; itr != bounds.second; ++itr)
- {
- SpellScriptLoader* tmpscript = ScriptRegistry<SpellScriptLoader>::GetScriptById(itr->second);
- if (!tmpscript)
- continue;
+void ScriptMgr::CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector)
+{
+ CreateSpellOrAuraScripts(spellId, scriptVector, &SpellScriptLoader::GetAuraScript);
+}
- scriptVector.push_back(std::make_pair(tmpscript, itr));
- }
+SpellScriptLoader* ScriptMgr::GetSpellScriptLoader(uint32 scriptId)
+{
+ return ScriptRegistry<SpellScriptLoader>::Instance()->GetScriptById(scriptId);
}
void ScriptMgr::OnNetworkStart()
@@ -1495,56 +2224,62 @@ void ScriptMgr::OnGroupDisband(Group* group)
void ScriptMgr::OnHeal(Unit* healer, Unit* reciever, uint32& gain)
{
FOREACH_SCRIPT(UnitScript)->OnHeal(healer, reciever, gain);
+ FOREACH_SCRIPT(PlayerScript)->OnHeal(healer, reciever, gain);
}
void ScriptMgr::OnDamage(Unit* attacker, Unit* victim, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->OnDamage(attacker, victim, damage);
+ FOREACH_SCRIPT(PlayerScript)->OnDamage(attacker, victim, damage);
}
void ScriptMgr::ModifyPeriodicDamageAurasTick(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyPeriodicDamageAurasTick(target, attacker, damage);
}
void ScriptMgr::ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifyMeleeDamage(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifyMeleeDamage(target, attacker, damage);
}
void ScriptMgr::ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage)
{
FOREACH_SCRIPT(UnitScript)->ModifySpellDamageTaken(target, attacker, damage);
+ FOREACH_SCRIPT(PlayerScript)->ModifySpellDamageTaken(target, attacker, damage);
}
SpellScriptLoader::SpellScriptLoader(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<SpellScriptLoader>::AddScript(this);
+ ScriptRegistry<SpellScriptLoader>::Instance()->AddScript(this);
}
ServerScript::ServerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ServerScript>::AddScript(this);
+ ScriptRegistry<ServerScript>::Instance()->AddScript(this);
}
WorldScript::WorldScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WorldScript>::AddScript(this);
+ ScriptRegistry<WorldScript>::Instance()->AddScript(this);
}
FormulaScript::FormulaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<FormulaScript>::AddScript(this);
+ ScriptRegistry<FormulaScript>::Instance()->AddScript(this);
}
UnitScript::UnitScript(const char* name, bool addToScripts)
: ScriptObject(name)
{
- ScriptRegistry<UnitScript>::AddScript(this, addToScripts);
+ if (addToScripts)
+ ScriptRegistry<UnitScript>::Instance()->AddScript(this);
}
WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
@@ -1553,7 +2288,7 @@ WorldMapScript::WorldMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsWorldMap())
TC_LOG_ERROR("scripts", "WorldMapScript for map %u is invalid.", mapId);
- ScriptRegistry<WorldMapScript>::AddScript(this);
+ ScriptRegistry<WorldMapScript>::Instance()->AddScript(this);
}
InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
@@ -1562,7 +2297,7 @@ InstanceMapScript::InstanceMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsDungeon())
TC_LOG_ERROR("scripts", "InstanceMapScript for map %u is invalid.", mapId);
- ScriptRegistry<InstanceMapScript>::AddScript(this);
+ ScriptRegistry<InstanceMapScript>::Instance()->AddScript(this);
}
BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
@@ -1571,122 +2306,117 @@ BattlegroundMapScript::BattlegroundMapScript(const char* name, uint32 mapId)
if (GetEntry() && !GetEntry()->IsBattleground())
TC_LOG_ERROR("scripts", "BattlegroundMapScript for map %u is invalid.", mapId);
- ScriptRegistry<BattlegroundMapScript>::AddScript(this);
+ ScriptRegistry<BattlegroundMapScript>::Instance()->AddScript(this);
}
ItemScript::ItemScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ItemScript>::AddScript(this);
+ ScriptRegistry<ItemScript>::Instance()->AddScript(this);
}
CreatureScript::CreatureScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<CreatureScript>::AddScript(this);
+ ScriptRegistry<CreatureScript>::Instance()->AddScript(this);
}
GameObjectScript::GameObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GameObjectScript>::AddScript(this);
+ ScriptRegistry<GameObjectScript>::Instance()->AddScript(this);
}
AreaTriggerScript::AreaTriggerScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AreaTriggerScript>::AddScript(this);
+ ScriptRegistry<AreaTriggerScript>::Instance()->AddScript(this);
}
BattlegroundScript::BattlegroundScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<BattlegroundScript>::AddScript(this);
+ ScriptRegistry<BattlegroundScript>::Instance()->AddScript(this);
}
OutdoorPvPScript::OutdoorPvPScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<OutdoorPvPScript>::AddScript(this);
+ ScriptRegistry<OutdoorPvPScript>::Instance()->AddScript(this);
}
CommandScript::CommandScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<CommandScript>::AddScript(this);
+ ScriptRegistry<CommandScript>::Instance()->AddScript(this);
}
WeatherScript::WeatherScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<WeatherScript>::AddScript(this);
+ ScriptRegistry<WeatherScript>::Instance()->AddScript(this);
}
AuctionHouseScript::AuctionHouseScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AuctionHouseScript>::AddScript(this);
+ ScriptRegistry<AuctionHouseScript>::Instance()->AddScript(this);
}
ConditionScript::ConditionScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<ConditionScript>::AddScript(this);
+ ScriptRegistry<ConditionScript>::Instance()->AddScript(this);
}
VehicleScript::VehicleScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<VehicleScript>::AddScript(this);
+ ScriptRegistry<VehicleScript>::Instance()->AddScript(this);
}
DynamicObjectScript::DynamicObjectScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<DynamicObjectScript>::AddScript(this);
+ ScriptRegistry<DynamicObjectScript>::Instance()->AddScript(this);
}
TransportScript::TransportScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<TransportScript>::AddScript(this);
+ ScriptRegistry<TransportScript>::Instance()->AddScript(this);
}
AchievementCriteriaScript::AchievementCriteriaScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AchievementCriteriaScript>::AddScript(this);
+ ScriptRegistry<AchievementCriteriaScript>::Instance()->AddScript(this);
}
PlayerScript::PlayerScript(const char* name)
: UnitScript(name, false)
{
- ScriptRegistry<PlayerScript>::AddScript(this);
+ ScriptRegistry<PlayerScript>::Instance()->AddScript(this);
}
AccountScript::AccountScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<AccountScript>::AddScript(this);
+ ScriptRegistry<AccountScript>::Instance()->AddScript(this);
}
GuildScript::GuildScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GuildScript>::AddScript(this);
+ ScriptRegistry<GuildScript>::Instance()->AddScript(this);
}
GroupScript::GroupScript(const char* name)
: ScriptObject(name)
{
- ScriptRegistry<GroupScript>::AddScript(this);
+ ScriptRegistry<GroupScript>::Instance()->AddScript(this);
}
-// Instantiate static members of ScriptRegistry.
-template<class TScript> std::map<uint32, TScript*> ScriptRegistry<TScript>::ScriptPointerList;
-template<class TScript> std::vector<TScript*> ScriptRegistry<TScript>::Scripts;
-template<class TScript> uint32 ScriptRegistry<TScript>::_scriptIdCounter = 0;
-
// Specialize for each script type class like so:
template class TC_GAME_API ScriptRegistry<SpellScriptLoader>;
template class TC_GAME_API ScriptRegistry<ServerScript>;
@@ -1714,13 +2444,3 @@ template class TC_GAME_API ScriptRegistry<GuildScript>;
template class TC_GAME_API ScriptRegistry<GroupScript>;
template class TC_GAME_API ScriptRegistry<UnitScript>;
template class TC_GAME_API ScriptRegistry<AccountScript>;
-
-// Undefine utility macros.
-#undef GET_SCRIPT_RET
-#undef GET_SCRIPT
-#undef FOREACH_SCRIPT
-#undef FOR_SCRIPTS_RET
-#undef FOR_SCRIPTS
-#undef SCR_REG_LST
-#undef SCR_REG_ITR
-#undef SCR_REG_MAP
diff --git a/src/server/game/Scripting/ScriptMgr.h b/src/server/game/Scripting/ScriptMgr.h
index 337e7de4861..cc1b65fa593 100644
--- a/src/server/game/Scripting/ScriptMgr.h
+++ b/src/server/game/Scripting/ScriptMgr.h
@@ -46,6 +46,7 @@ class InstanceMap;
class InstanceScript;
class Item;
class Map;
+class ModuleReference;
class OutdoorPvP;
class Player;
class Quest;
@@ -157,14 +158,8 @@ class TC_GAME_API ScriptObject
protected:
- ScriptObject(const char* name)
- : _name(name)
- {
- }
-
- virtual ~ScriptObject()
- {
- }
+ ScriptObject(const char* name);
+ virtual ~ScriptObject();
private:
@@ -338,7 +333,8 @@ class TC_GAME_API WorldMapScript : public ScriptObject, public MapScript<Map>
WorldMapScript(const char* name, uint32 mapId);
};
-class TC_GAME_API InstanceMapScript : public ScriptObject, public MapScript<InstanceMap>
+class TC_GAME_API InstanceMapScript
+ : public ScriptObject, public MapScript<InstanceMap>
{
protected:
@@ -834,15 +830,6 @@ class TC_GAME_API GroupScript : public ScriptObject
virtual void OnDisband(Group* /*group*/) { }
};
-// Placed here due to ScriptRegistry::AddScript dependency.
-#define sScriptMgr ScriptMgr::instance()
-
-// namespace
-// {
- typedef std::list<std::string> UnusedScriptNamesContainer;
- TC_GAME_API extern UnusedScriptNamesContainer UnusedScriptNames;
-// }
-
// Manages registration, loading, and execution of scripts.
class TC_GAME_API ScriptMgr
{
@@ -852,16 +839,17 @@ class TC_GAME_API ScriptMgr
ScriptMgr();
virtual ~ScriptMgr();
+ void FillSpellSummary();
+ void LoadDatabase();
+
+ void IncreaseScriptCount() { ++_scriptCount; }
+ void DecreaseScriptCount() { --_scriptCount; }
+
public: /* Initialization */
static ScriptMgr* instance();
void Initialize();
- void LoadDatabase();
- void FillSpellSummary();
-
- const char* ScriptsVersion() const { return "Integrated Trinity Scripts"; }
- void IncrementScriptCount() { ++_scriptCount; }
uint32 GetScriptCount() const { return _scriptCount; }
typedef void(*ScriptLoaderCallbackType)();
@@ -873,6 +861,27 @@ class TC_GAME_API ScriptMgr
_script_loader_callback = script_loader_callback;
}
+ public: /* Script contexts */
+ /// Set the current script context, which allows the ScriptMgr
+ /// to accept new scripts in this context.
+ /// Requires a SwapScriptContext() call afterwards to load the new scripts.
+ void SetScriptContext(std::string const& context);
+ /// Returns the current script context.
+ std::string const& GetCurrentScriptContext() const { return _currentContext; }
+ /// Releases all scripts associated with the given script context immediately.
+ /// Requires a SwapScriptContext() call afterwards to finish the unloading.
+ void ReleaseScriptContext(std::string const& context);
+ /// Executes all changed introduced by SetScriptContext and ReleaseScriptContext.
+ /// It is possible to combine multiple SetScriptContext and ReleaseScriptContext
+ /// calls for better performance (bulk changes).
+ void SwapScriptContext(bool initialize = false);
+
+ /// Acquires a strong module reference to the module containing the given script name,
+ /// which prevents the shared library which contains the script from unloading.
+ /// The shared library is lazy unloaded as soon as all references to it are released.
+ std::shared_ptr<ModuleReference> AcquireModuleReferenceOfScriptName(
+ std::string const& scriptname) const;
+
public: /* Unloading */
void Unload();
@@ -881,7 +890,7 @@ class TC_GAME_API ScriptMgr
void CreateSpellScripts(uint32 spellId, std::list<SpellScript*>& scriptVector);
void CreateAuraScripts(uint32 spellId, std::list<AuraScript*>& scriptVector);
- void CreateSpellScriptLoaders(uint32 spellId, std::vector<std::pair<SpellScriptLoader*, std::multimap<uint32, uint32>::iterator> >& scriptVector);
+ SpellScriptLoader* GetSpellScriptLoader(uint32 scriptId);
public: /* ServerScript */
@@ -1094,21 +1103,14 @@ class TC_GAME_API ScriptMgr
void ModifyMeleeDamage(Unit* target, Unit* attacker, uint32& damage);
void ModifySpellDamageTaken(Unit* target, Unit* attacker, int32& damage);
- public: /* Scheduled scripts */
-
- uint32 IncreaseScheduledScriptsCount() { return ++_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount() { return --_scheduledScripts; }
- uint32 DecreaseScheduledScriptCount(size_t count) { return _scheduledScripts -= count; }
- bool IsScriptScheduled() const { return _scheduledScripts > 0; }
-
private:
-
uint32 _scriptCount;
- //atomic op counter for active scripts amount
- std::atomic<uint32> _scheduledScripts;
-
ScriptLoaderCallbackType _script_loader_callback;
+
+ std::string _currentContext;
};
+#define sScriptMgr ScriptMgr::instance()
+
#endif
diff --git a/src/server/game/Scripting/ScriptReloadMgr.cpp b/src/server/game/Scripting/ScriptReloadMgr.cpp
new file mode 100644
index 00000000000..aac0e26f02b
--- /dev/null
+++ b/src/server/game/Scripting/ScriptReloadMgr.cpp
@@ -0,0 +1,1520 @@
+/*
+ * 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 "ScriptReloadMgr.h"
+#include "Errors.h"
+
+#ifndef TRINITY_API_USE_DYNAMIC_LINKING
+
+// This method should never be called
+std::shared_ptr<ModuleReference>
+ ScriptReloadMgr::AcquireModuleReferenceOfContext(std::string const& /*context*/)
+{
+ WPAbort();
+}
+
+// Returns the empty implemented ScriptReloadMgr
+ScriptReloadMgr* ScriptReloadMgr::instance()
+{
+ static ScriptReloadMgr instance;
+ return &instance;
+}
+
+#else
+
+#include <algorithm>
+#include <regex>
+#include <vector>
+#include <future>
+#include <memory>
+#include <fstream>
+#include <type_traits>
+#include <unordered_set>
+#include <unordered_map>
+
+#include <boost/algorithm/string/replace.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/system/system_error.hpp>
+
+#include "efsw/efsw.hpp"
+
+#include "Log.h"
+#include "Config.h"
+#include "BuiltInConfig.h"
+#include "ScriptMgr.h"
+#include "StartProcess.h"
+#include "MPSCQueue.h"
+#include "GitRevision.h"
+
+namespace fs = boost::filesystem;
+
+#ifdef _WIN32
+ #include <windows.h>
+#else // Posix
+ #include <dlfcn.h>
+#endif
+
+// Promote the sScriptReloadMgr to a HotSwapScriptReloadMgr
+// in this compilation unit.
+#undef sScriptReloadMgr
+#define sScriptReloadMgr static_cast<HotSwapScriptReloadMgr*>(ScriptReloadMgr::instance())
+
+// Returns "" on Windows and "lib" on posix.
+static char const* GetSharedLibraryPrefix()
+{
+#ifdef _WIN32
+ return "";
+#else // Posix
+ return "lib";
+#endif
+}
+
+// Returns "dll" on Windows and "so" on posix.
+static char const* GetSharedLibraryExtension()
+{
+#ifdef _WIN32
+ return "dll";
+#else // Posix
+ return "so";
+#endif
+}
+
+#ifdef _WIN32
+typedef HMODULE HandleType;
+#else // Posix
+typedef void* HandleType;
+#endif
+
+class SharedLibraryUnloader
+{
+public:
+ SharedLibraryUnloader()
+ : _path() { }
+ explicit SharedLibraryUnloader(fs::path const& path)
+ : _path(path) { }
+
+ void operator() (HandleType handle) const
+ {
+ // Unload the associated shared library.
+#ifdef _WIN32
+ bool success = (FreeLibrary(handle) != 0);
+#else // Posix
+ bool success = (dlclose(handle) == 0);
+#endif
+
+ if (!success)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to unload (syscall) the shared library \"%s\".",
+ _path.generic_string().c_str());
+
+ return;
+ }
+
+ boost::system::error_code error;
+ if (fs::remove(_path, error))
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Lazy unloaded and deleted the shared library \"%s\".",
+ _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());
+ }
+ }
+
+private:
+ fs::path _path;
+};
+
+typedef std::unique_ptr<typename std::remove_pointer<HandleType>::type, SharedLibraryUnloader> HandleHolder;
+
+typedef char const* (*GetScriptModuleRevisionHashType)();
+typedef void (*AddScriptsType)();
+typedef char const* (*GetScriptModuleType)();
+typedef char const* (*GetBuildDirectiveType)();
+
+class ScriptModule
+ : public ModuleReference
+{
+public:
+ explicit ScriptModule(HandleHolder handle, GetScriptModuleRevisionHashType getScriptModuleRevisionHash,
+ AddScriptsType addScripts, GetScriptModuleType getScriptModule,
+ GetBuildDirectiveType getBuildDirective, fs::path const& path)
+ : _handle(std::forward<HandleHolder>(handle)), _getScriptModuleRevisionHash(getScriptModuleRevisionHash),
+ _addScripts(addScripts), _getScriptModule(getScriptModule),
+ _getBuildDirective(getBuildDirective), _path(path) { }
+
+ ScriptModule(ScriptModule const&) = delete;
+ ScriptModule(ScriptModule&& right) = delete;
+
+ ScriptModule& operator= (ScriptModule const&) = delete;
+ ScriptModule& operator= (ScriptModule&& right) = delete;
+
+ static Optional<std::shared_ptr<ScriptModule>> CreateFromPath(fs::path const& path);
+
+ char const* GetScriptModuleRevisionHash() const override
+ {
+ return _getScriptModuleRevisionHash();
+ }
+
+ void AddScripts() const
+ {
+ return _addScripts();
+ }
+
+ char const* GetScriptModule() const override
+ {
+ return _getScriptModule();
+ }
+
+ char const* GetBuildDirective() const
+ {
+ return _getBuildDirective();
+ }
+
+ fs::path const& GetModulePath() const override
+ {
+ return _path;
+ }
+
+private:
+ HandleHolder _handle;
+
+ GetScriptModuleRevisionHashType _getScriptModuleRevisionHash;
+ AddScriptsType _addScripts;
+ GetScriptModuleType _getScriptModule;
+ GetBuildDirectiveType _getBuildDirective;
+
+ fs::path _path;
+};
+
+template<typename Fn>
+static bool GetFunctionFromSharedLibrary(HandleType handle, std::string const& name, Fn& fn)
+{
+#ifdef _WIN32
+ fn = reinterpret_cast<Fn>(GetProcAddress(handle, name.c_str()));
+#else // Posix
+ fn = reinterpret_cast<Fn>(dlsym(handle, name.c_str()));
+#endif
+ return fn != nullptr;
+}
+
+// Load a shared library from the given path.
+Optional<std::shared_ptr<ScriptModule>> ScriptModule::CreateFromPath(fs::path const& path)
+{
+#ifdef _WIN32
+ HandleType handle = LoadLibrary(path.generic_string().c_str());
+#else // Posix
+ HandleType handle = dlopen(path.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());
+ return boost::none;
+ }
+
+ // Use RAII to release the library on failure.
+ HandleHolder holder(handle, SharedLibraryUnloader(path));
+
+ GetScriptModuleRevisionHashType getScriptModuleRevisionHash;
+ AddScriptsType addScripts;
+ GetScriptModuleType getScriptModule;
+ GetBuildDirectiveType getBuildDirective;
+
+ if (GetFunctionFromSharedLibrary(handle, "GetScriptModuleRevisionHash", getScriptModuleRevisionHash) &&
+ GetFunctionFromSharedLibrary(handle, "AddScripts", addScripts) &&
+ GetFunctionFromSharedLibrary(handle, "GetScriptModule", getScriptModule) &&
+ GetFunctionFromSharedLibrary(handle, "GetBuildDirective", getBuildDirective))
+ return std::make_shared<ScriptModule>(std::move(holder), getScriptModuleRevisionHash,
+ addScripts, getScriptModule, getBuildDirective, path);
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Could not extract all required functions from the shared library \"%s\"!",
+ path.generic_string().c_str());
+
+ return boost::none;
+ }
+}
+
+static bool HasValidScriptModuleName(std::string const& name)
+{
+ // Detects scripts_NAME.dll's / .so's
+ static std::regex const regex(
+ Trinity::StringFormat("^%s[sS]cripts_[a-zA-Z0-9_]+\\.%s$",
+ GetSharedLibraryPrefix(),
+ GetSharedLibraryExtension()));
+
+ return std::regex_match(name, regex);
+}
+
+/// File watcher responsible for watching shared libraries
+class LibraryUpdateListener : public efsw::FileWatchListener
+{
+public:
+ LibraryUpdateListener() { }
+ virtual ~LibraryUpdateListener() { }
+
+ void handleFileAction(efsw::WatchID /*watchid*/, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename = "") final override;
+};
+
+static LibraryUpdateListener libraryUpdateListener;
+
+/// File watcher responsible for watching source files
+class SourceUpdateListener : public efsw::FileWatchListener
+{
+ fs::path const path_;
+
+ std::string const script_module_name_;
+
+ efsw::WatchID const watcher_id_;
+
+public:
+ explicit SourceUpdateListener(fs::path path, std::string script_module_name);
+
+ virtual ~SourceUpdateListener();
+
+ void handleFileAction(efsw::WatchID /*watchid*/, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename = "") final override;
+};
+
+namespace std
+{
+ template <>
+ struct hash<fs::path>
+ {
+ hash<string> hasher;
+
+ std::size_t operator()(fs::path const& key) const
+ {
+ return hasher(key.generic_string());
+ }
+ };
+}
+
+/// Invokes a synchronous CMake process with the given arguments
+template<typename... T>
+static int InvokeCMakeCommand(T&&... args)
+{
+ auto const executable = BuiltInConfig::GetCMakeCommand();
+ return Trinity::StartProcess(executable, {
+ executable,
+ std::forward<T>(args)...
+ }, "scripts.hotswap");
+}
+
+/// Invokes an asynchronous CMake process with the given arguments
+template<typename... T>
+static std::shared_ptr<Trinity::AsyncProcessResult> InvokeAsyncCMakeCommand(T&&... args)
+{
+ auto const executable = BuiltInConfig::GetCMakeCommand();
+ return Trinity::StartAsyncProcess(executable, {
+ executable,
+ std::forward<T>(args)...
+ }, "scripts.hotswap");
+}
+
+/// Calculates the C++ project name of the given module which is
+/// the lowercase string of scripts_${module}.
+static std::string CalculateScriptModuleProjectName(std::string const& module)
+{
+ std::string module_project = "scripts_" + module;
+ std::transform(module_project.begin(), module_project.end(),
+ module_project.begin(), ::tolower);
+
+ return module_project;
+}
+
+/// Returns false when there isn't any attached debugger to the process which
+/// could block the rebuild of new shared libraries.
+static bool IsDebuggerBlockingRebuild()
+{
+#ifdef _WIN32
+ if (IsDebuggerPresent())
+ return true;
+#endif
+ return false;
+}
+
+/// ScriptReloadMgr which is used when dynamic linking is enabled
+///
+/// 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
+/// 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
+/// to serve scripts of different versions to entities and spells.
+///
+/// Also this class invokes rebuilds as soon as the source of loaded
+/// scripts change and installs the modules correctly through CMake.
+class HotSwapScriptReloadMgr final
+ : public ScriptReloadMgr
+{
+ friend class ScriptReloadMgr;
+ friend class SourceUpdateListener;
+
+ /// Reflects a queued change on a shared library or shared library
+ /// which is waiting for processing
+ enum class ChangeStateRequest : uint8
+ {
+ CHANGE_REQUEST_ADDED,
+ CHANGE_REQUEST_MODIFIED,
+ CHANGE_REQUEST_REMOVED
+ };
+
+ /// Reflects a running job of an invoked asynchronous external process
+ enum class BuildJobType : uint8
+ {
+ BUILD_JOB_NONE,
+ BUILD_JOB_RERUN_CMAKE,
+ BUILD_JOB_COMPILE,
+ BUILD_JOB_INSTALL,
+ };
+
+ // Represents a job which was invoked through a source or shared library change
+ class BuildJob
+ {
+ // Script module which is processed in the current running job
+ std::string script_module_name_;
+ // The C++ project name of the module which is processed
+ std::string script_module_project_name_;
+ // The build directive of the current module which is processed
+ // like "Release" or "Debug". The build directive from the
+ // previous same module is used if there was any.
+ std::string script_module_build_directive_;
+
+ // Type of the current running job
+ BuildJobType type_;
+ // The async process result of the current job
+ std::shared_ptr<Trinity::AsyncProcessResult> async_result_;
+
+ public:
+ explicit BuildJob(std::string script_module_name, std::string script_module_project_name,
+ std::string script_module_build_directive)
+ : 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) { }
+
+ bool IsValid() const
+ {
+ return type_ != BuildJobType::BUILD_JOB_NONE;
+ }
+
+ std::string const& GetModuleName() const { return script_module_name_; }
+
+ std::string const& GetProjectName() const { return script_module_project_name_; }
+
+ std::string const& GetBuildDirective() const { return script_module_build_directive_; }
+
+ BuildJobType GetType() const { return type_; }
+
+ std::shared_ptr<Trinity::AsyncProcessResult> const& GetProcess() const
+ {
+ ASSERT(async_result_, "Tried to access an empty process handle!");
+ return async_result_;
+ }
+
+ /// Updates the current running job with the given type and async result
+ void UpdateCurrentJob(BuildJobType type,
+ std::shared_ptr<Trinity::AsyncProcessResult> async_result)
+ {
+ ASSERT(type != BuildJobType::BUILD_JOB_NONE, "None isn't allowed here!");
+ ASSERT(async_result, "The async result must not be empty!");
+
+ type_ = type;
+ async_result_ = std::move(async_result);
+ }
+ };
+
+ /// Base class for lockfree asynchronous messages to the script reloader
+ class ScriptReloaderMessage
+ {
+ public:
+ virtual ~ScriptReloaderMessage() { }
+
+ /// Invoke this function to run a message thread safe on the reloader
+ virtual void operator() (HotSwapScriptReloadMgr* reloader) = 0;
+ };
+
+ /// Implementation class which wraps functional types and dispatches
+ /// it in the overwritten implementation of the reloader messages.
+ template<typename T>
+ class ScriptReloaderMessageImplementation
+ : public ScriptReloaderMessage
+ {
+ T dispatcher_;
+
+ public:
+ explicit ScriptReloaderMessageImplementation(T dispatcher)
+ : dispatcher_(std::move(dispatcher)) { }
+
+ void operator() (HotSwapScriptReloadMgr* reloader) final override
+ {
+ dispatcher_(reloader);
+ }
+ };
+
+ /// Uses the given functional type and creates a asynchronous reloader
+ /// message on the heap, which requires deletion.
+ template<typename T>
+ auto MakeMessage(T&& dispatcher)
+ -> ScriptReloaderMessageImplementation<typename std::decay<T>::type>*
+ {
+ return new ScriptReloaderMessageImplementation<typename std::decay<T>::type>
+ (std::forward<T>(dispatcher));
+ }
+
+public:
+ HotSwapScriptReloadMgr()
+ : _libraryWatcher(-1), _unique_library_name_counter(0),
+ _last_time_library_changed(0), _last_time_sources_changed(0),
+ _last_time_user_informed(0), terminate_early(false) { }
+
+ virtual ~HotSwapScriptReloadMgr()
+ {
+ // Delete all messages
+ ScriptReloaderMessage* message;
+ while (_messages.Dequeue(message))
+ delete message;
+ }
+
+ /// Returns the absolute path to the script module directory
+ static fs::path GetLibraryDirectory()
+ {
+ return fs::absolute(sConfigMgr->GetStringDefault("HotSwap.ScriptDir", "scripts"));
+ }
+
+ /// Returns the absolute path to the scripts directory in the source tree.
+ static fs::path GetSourceDirectory()
+ {
+ fs::path dir = BuiltInConfig::GetSourceDirectory();
+ dir /= "src";
+ dir /= "server";
+ dir /= "scripts";
+ return dir;
+ }
+
+ /// Initializes the file watchers and loads all existing shared libraries
+ /// into the running server.
+ void Initialize() final override
+ {
+ if (!sWorld->getBoolConfig(CONFIG_HOTSWAP_ENABLED))
+ return;
+
+ if (BuiltInConfig::GetBuildDirectory().find(" ") != std::string::npos)
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Your build directory path \"%s\" "
+ "contains spaces, which isn't allowed for compatibility reasons! "
+ "You need to create a build directory which doesn't contain any space character "
+ "in it's path!",
+ BuiltInConfig::GetBuildDirectory().c_str());
+
+ return;
+ }
+
+ {
+ auto const library_directory = GetLibraryDirectory();
+ if (!fs::exists(library_directory) || !fs::is_directory(library_directory))
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Library directory \"%s\" doesn't exist!.",
+ library_directory.generic_string().c_str());
+ return;
+ }
+ }
+
+ // Get the cache directory path
+ fs::path const cache_path = []
+ {
+ auto path = fs::absolute(sScriptReloadMgr->GetLibraryDirectory());
+ path /= ".cache";
+ return path;
+ }();
+
+ // 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))
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Couldn't create the cache directory \"%s\".",
+ cache_path.generic_string().c_str());
+ return;
+ }
+
+ // Used to silent compiler warnings
+ (void)code;
+
+ // Correct the CMake prefix when needed
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED))
+ DoCMakePrefixCorrectionIfNeeded();
+
+ InitializeDefaultLibraries();
+ InitializeFileWatchers();
+ }
+
+ /// Needs to be called periodically from the worldserver loop
+ /// to invoke queued actions like module loading/unloading and script
+ /// compilation.
+ /// This method should be invoked from a thread safe point to
+ /// prevent misbehavior.
+ void Update() final override
+ {
+ // Consume all messages
+ ScriptReloaderMessage* message;
+ while (_messages.Dequeue(message))
+ {
+ (*message)(this);
+ delete message;
+ }
+
+ DispatchRunningBuildJobs();
+ DispatchModuleChanges();
+ }
+
+ /// Unloads the manager and cancels all runnings jobs immediately
+ void Unload() final override
+ {
+ if (_libraryWatcher >= 0)
+ {
+ _fileWatcher.removeWatch(_libraryWatcher);
+ _libraryWatcher = -1;
+ }
+
+ // If a build is in progress cancel it
+ if (_build_job)
+ {
+ _build_job->GetProcess()->Terminate();
+ _build_job.reset();
+ }
+
+ // Release all strong references to script modules
+ // to trigger unload actions as early as possible,
+ // otherwise the worldserver will crash on exit.
+ _running_script_modules.clear();
+ }
+
+ /// Queue's a thread safe message to the reloader which is executed on
+ /// the next world server update tick.
+ template<typename T>
+ void QueueMessage(T&& message)
+ {
+ _messages.Enqueue(MakeMessage(std::forward<T>(message)));
+ }
+
+ /// Queues an action which marks the given shared library as changed
+ /// which will add, unload or reload it at the next world update tick.
+ /// This method is thread safe.
+ void QueueSharedLibraryChanged(fs::path const& path)
+ {
+ _last_time_library_changed = getMSTime();
+ _libraries_changed.insert(path);
+ }
+
+ /// Queues a notification that a source file was added
+ /// This method is thread unsafe.
+ void QueueAddSourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_ADDED);
+ }
+
+ /// Queues a notification that a source file was modified
+ /// This method is thread unsafe.
+ void QueueModifySourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_MODIFIED);
+ }
+
+ /// Queues a notification that a source file was removed
+ /// This method is thread unsafe.
+ void QueueRemoveSourceFile(std::string const& module_name, fs::path const& path)
+ {
+ UpdateSourceChangeRequest(module_name, path, ChangeStateRequest::CHANGE_REQUEST_REMOVED);
+ }
+
+private:
+ // Loads all shared libraries which are contained in the
+ // scripts directory on startup.
+ void InitializeDefaultLibraries()
+ {
+ fs::path const libraryDirectory(GetLibraryDirectory());
+ fs::directory_iterator const dir_end;
+
+ uint32 count = 0;
+
+ // Iterate through all shared libraries in the script directory and load it
+ for (fs::directory_iterator dir_itr(libraryDirectory); dir_itr != dir_end ; ++dir_itr)
+ if (fs::is_regular_file(dir_itr->path()) && HasValidScriptModuleName(dir_itr->path().filename().generic_string()))
+ {
+ TC_LOG_INFO("scripts.hotswap", "Loading script module \"%s\"...",
+ dir_itr->path().filename().generic_string().c_str());
+
+ // Don't swap the script context to do bulk loading
+ ProcessLoadScriptModule(dir_itr->path(), false);
+ ++count;
+ }
+
+ TC_LOG_INFO("scripts.hotswap", ">> Loaded %u script modules.", count);
+ }
+
+ // Initialize all enabled file watchers.
+ // Needs to be called after InitializeDefaultLibraries()!
+ void InitializeFileWatchers()
+ {
+ _libraryWatcher = _fileWatcher.addWatch(GetLibraryDirectory().generic_string(), &libraryUpdateListener, false);
+ if (_libraryWatcher >= 0)
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Library reloader is listening on \"%s\".",
+ GetLibraryDirectory().generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to initialize the library reloader on \"%s\".",
+ GetLibraryDirectory().generic_string().c_str());
+ }
+
+ _fileWatcher.watch();
+ }
+
+ /// Updates the current state of the given source path
+ void UpdateSourceChangeRequest(std::string const& module_name,
+ fs::path const& path,
+ ChangeStateRequest state)
+ {
+ _last_time_sources_changed = getMSTime();
+
+ // Write when there is no module with the given name known
+ auto module_itr = _sources_changed.find(module_name);
+
+ // When the file was modified it's enough to mark the module as
+ // dirty by initializing the associated map.
+ if (module_itr == _sources_changed.end())
+ module_itr = _sources_changed.insert(std::make_pair(
+ module_name, decltype(_sources_changed)::mapped_type{})).first;
+
+ // Leave when the file was just modified as explained above
+ if (state == ChangeStateRequest::CHANGE_REQUEST_MODIFIED)
+ return;
+
+ // Insert when the given path isn't existent
+ auto const itr = module_itr->second.find(path);
+ if (itr == module_itr->second.end())
+ {
+ module_itr->second.insert(std::make_pair(path, state));
+ return;
+ }
+
+ ASSERT((itr->second == ChangeStateRequest::CHANGE_REQUEST_ADDED)
+ || (itr->second == ChangeStateRequest::CHANGE_REQUEST_REMOVED),
+ "Stored value is invalid!");
+
+ ASSERT((state == ChangeStateRequest::CHANGE_REQUEST_ADDED)
+ || (state == ChangeStateRequest::CHANGE_REQUEST_REMOVED),
+ "The given state is invalid!");
+
+ ASSERT(state != itr->second,
+ "Tried to apply a state which is stored already!");
+
+ module_itr->second.erase(itr);
+ }
+
+ /// Called periodically on the worldserver tick to process all
+ /// load/unload/reload requests of shared libraries.
+ void DispatchModuleChanges()
+ {
+ // When there are no libraries to change return
+ if (_libraries_changed.empty())
+ return;
+
+ // Wait some time after changes to catch bulk changes
+ if (GetMSTimeDiffToNow(_last_time_library_changed) < 500)
+ return;
+
+ for (auto const& path : _libraries_changed)
+ {
+ bool const is_running =
+ _running_script_module_names.find(path) != _running_script_module_names.end();
+
+ bool const exists = fs::exists(path);
+
+ if (is_running)
+ {
+ if (exists)
+ ProcessReloadScriptModule(path);
+ else
+ ProcessUnloadScriptModule(path);
+ }
+ else if (exists)
+ ProcessLoadScriptModule(path);
+ }
+
+ _libraries_changed.clear();
+ }
+
+ void ProcessLoadScriptModule(fs::path const& path, bool swap_context = true)
+ {
+ 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());
+
+ if ([&]
+ {
+ boost::system::error_code code;
+ fs::copy_file(path, cache_path, fs::copy_option::fail_if_exists, code);
+ return code;
+ }())
+ {
+ TC_LOG_FATAL("scripts.hotswap", ">> Failed to create cache entry for module \"%s\"!",
+ path.filename().generic_string().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;
+ }
+
+ auto module = ScriptModule::CreateFromPath(cache_path);
+ if (!module)
+ {
+ TC_LOG_FATAL("scripts.hotswap", ">> Failed to load script module \"%s\"!",
+ path.filename().generic_string().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;
+ }
+
+ // Limit the git revision hash to 7 characters.
+ std::string module_revision((*module)->GetScriptModuleRevisionHash());
+ if (module_revision.size() >= 7)
+ module_revision = module_revision.substr(0, 7);
+
+ std::string const module_name = (*module)->GetScriptModule();
+ TC_LOG_INFO("scripts.hotswap", ">> Loaded script module \"%s\" (\"%s\" - %s).",
+ path.filename().generic_string().c_str(), module_name.c_str(), module_revision.c_str());
+
+ if (module_revision.empty())
+ {
+ TC_LOG_WARN("scripts.hotswap", ">> Script module \"%s\" has an empty revision hash!",
+ path.filename().generic_string().c_str());
+ }
+ else
+ {
+ // Trim the revision hash
+ std::string my_revision_hash = GitRevision::GetHash();
+ std::size_t const trim = std::min(module_revision.size(), my_revision_hash.size());
+ my_revision_hash = my_revision_hash.substr(0, trim);
+ module_revision = module_revision.substr(0, trim);
+
+ if (my_revision_hash != module_revision)
+ {
+ TC_LOG_WARN("scripts.hotswap", ">> Script module \"%s\" has a different revision hash! "
+ "Binary incompatibility could lead to unknown behaviour!", path.filename().generic_string().c_str());
+ }
+ }
+
+ {
+ auto const itr = _running_script_modules.find(module_name);
+ if (itr != _running_script_modules.end())
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Attempt to load a module twice \"%s\" (loaded module is at %s)!",
+ path.generic_string().c_str(), itr->second.first->GetModulePath().generic_string().c_str());
+
+ return;
+ }
+ }
+
+ sScriptMgr->SetScriptContext(module_name);
+ (*module)->AddScripts();
+ TC_LOG_TRACE("scripts.hotswap", ">> Registered all scripts of module %s.", module_name.c_str());
+
+ if (swap_context)
+ sScriptMgr->SwapScriptContext();
+
+ // Create the source listener
+ auto listener = Trinity::make_unique<SourceUpdateListener>(
+ sScriptReloadMgr->GetSourceDirectory() / module_name,
+ module_name);
+
+ // Store the module
+ _known_modules_build_directives.insert(std::make_pair(module_name, (*module)->GetBuildDirective()));
+ _running_script_modules.insert(std::make_pair(module_name,
+ std::make_pair(std::move(*module), std::move(listener))));
+ _running_script_module_names.insert(std::make_pair(path, module_name));
+ }
+
+ void ProcessReloadScriptModule(fs::path const& path)
+ {
+ ProcessUnloadScriptModule(path, false);
+ ProcessLoadScriptModule(path);
+ }
+
+ void ProcessUnloadScriptModule(fs::path const& path, bool finish = true)
+ {
+ auto const itr = _running_script_module_names.find(path);
+
+ ASSERT(itr != _running_script_module_names.end(),
+ "Can't unload a module which isn't running!");
+
+ // Unload the script context
+ sScriptMgr->ReleaseScriptContext(itr->second);
+
+ if (finish)
+ sScriptMgr->SwapScriptContext();
+
+ TC_LOG_INFO("scripts.hotswap", "Released script module \"%s\" (\"%s\")...",
+ path.filename().generic_string().c_str(), itr->second.c_str());
+
+ // Unload the script module
+ auto ref = _running_script_modules.find(itr->second);
+ ASSERT(ref != _running_script_modules.end() &&
+ "Expected the script reference to be present!");
+
+ // Yield a message when there are other owning references to
+ // the module which prevents it from unloading.
+ // The module will be unloaded once all scripts provided from the module
+ // are destroyed.
+ if (!ref->second.first.unique())
+ {
+ TC_LOG_INFO("scripts.hotswap",
+ "Script module %s is still used by %lu spell, aura or instance scripts. "
+ "Will lazy unload the module once all scripts stopped using it, "
+ "to use the latest version of an edited script unbind yourself from "
+ "the instance or re-cast the spell.",
+ ref->second.first->GetScriptModule(), ref->second.first.use_count() - 1);
+ }
+
+ // Remove the owning reference from the reloader
+ _running_script_modules.erase(ref);
+ _running_script_module_names.erase(itr);
+ }
+
+ /// Called periodically on the worldserver tick to process all recompile
+ /// requests. This method invokes one build or install job at the time
+ void DispatchRunningBuildJobs()
+ {
+ if (_build_job)
+ {
+ // Terminate the current build job when an associated source was changed
+ // while compiling and the terminate early option is enabled.
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED))
+ {
+ if (!terminate_early && _sources_changed.find(_build_job->GetModuleName()) != _sources_changed.end())
+ {
+ /*
+ FIXME: Currently crashes the server
+ TC_LOG_INFO("scripts.hotswap", "Terminating the running build of module \"%s\"...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->GetProcess()->Terminate();
+ _build_job.reset();
+
+ // Continue with the default execution path
+ DispatchRunningBuildJobs();
+ return;
+ */
+
+ terminate_early = true;
+ return;
+ }
+ }
+
+ // Wait for the current build job to finish, if the job finishes in time
+ // evaluate it and continue with the next one.
+ if (_build_job->GetProcess()->GetFutureResult().
+ wait_for(std::chrono::seconds(0)) == std::future_status::ready)
+ ProcessReadyBuildJob();
+ else
+ return; // Return when the job didn't finish in time
+
+ // Skip this cycle when the previous job scheduled a new one
+ if (_build_job)
+ return;
+ }
+
+ // Avoid burst updates through waiting for a short time after changes
+ if ((_last_time_sources_changed != 0) &&
+ (GetMSTimeDiffToNow(_last_time_sources_changed) < 500))
+ return;
+
+ // If the changed sources are empty do nothing
+ if (_sources_changed.empty())
+ return;
+
+ // Wait until are attached debugger were detached.
+ if (IsDebuggerBlockingRebuild())
+ {
+ if ((_last_time_user_informed == 0) ||
+ (GetMSTimeDiffToNow(_last_time_user_informed) > 7500))
+ {
+ _last_time_user_informed = getMSTime();
+
+ // Informs the user that the attached debugger is blocking the automatic script rebuild.
+ TC_LOG_INFO("scripts.hotswap", "Your attached debugger is blocking the TrinityCore "
+ "automatic script rebuild, please detach it!");
+ }
+
+ return;
+ }
+
+ // Find all source files of a changed script module and removes
+ // it from the changed source list, invoke the build afterwards.
+ bool rebuild_buildfiles;
+ auto module_name = [&]
+ {
+ auto itr = _sources_changed.begin();
+ auto name = itr->first;
+ rebuild_buildfiles = !itr->second.empty();
+
+ if (sLog->ShouldLog("scripts.hotswap", LogLevel::LOG_LEVEL_TRACE))
+ for (auto const& entry : itr->second)
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s was %s.",
+ entry.first.generic_string().c_str(),
+ ((entry.second == ChangeStateRequest::CHANGE_REQUEST_ADDED) ?
+ "added" : "removed"));
+ }
+
+ _sources_changed.erase(itr);
+ return name;
+ }();
+
+ // Erase the added delete history all modules when we
+ // invoke a cmake rebuild since we add all
+ // added files of other modules to the build as well
+ if (rebuild_buildfiles)
+ {
+ for (auto& entry : _sources_changed)
+ entry.second.clear();
+ }
+
+ ASSERT(!module_name.empty(),
+ "The current module name is invalid!");
+
+ TC_LOG_INFO("scripts.hotswap", "Recompiling Module \"%s\"...",
+ module_name.c_str());
+
+ // Calculate the project name of the script module
+ auto project_name = CalculateScriptModuleProjectName(module_name);
+
+ // Find the best build directive for the module
+ auto build_directive = [&] () -> std::string
+ {
+ auto directive = sConfigMgr->GetStringDefault("HotSwap.ReCompilerBuildType", "");
+ if (!directive.empty())
+ return directive;
+
+ auto const itr = _known_modules_build_directives.find(module_name);
+ if (itr != _known_modules_build_directives.end())
+ return itr->second;
+ else // If no build directive of the module was found use the one from the game library
+ return _BUILD_DIRECTIVE;
+ }();
+
+ // Initiate the new build job
+ _build_job = BuildJob(std::move(module_name),
+ std::move(project_name), std::move(build_directive));
+
+ // Rerun CMake when we need to recreate the build files
+ if (rebuild_buildfiles
+ && sWorld->getBoolConfig(CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED))
+ DoRerunCMake();
+ else
+ DoCompileCurrentProcessedModule();
+ }
+
+ void ProcessReadyBuildJob()
+ {
+ ASSERT(_build_job->IsValid(), "Invalid build job!");
+
+ // Retrieve the result
+ auto const error = _build_job->GetProcess()->GetFutureResult().get();
+
+ if (terminate_early)
+ {
+ _build_job.reset();
+ terminate_early = false;
+ return;
+ }
+
+ switch (_build_job->GetType())
+ {
+ case BuildJobType::BUILD_JOB_RERUN_CMAKE:
+ {
+ if (!error)
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Successfully updated the build files!");
+ }
+ else
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> Failed to update the build files at \"%s\", "
+ "it's possible that recently added sources are not included "
+ "in your next builds, rerun CMake manually.",
+ BuiltInConfig::GetBuildDirectory().c_str());
+ }
+ // Continue with building the changes sources
+ DoCompileCurrentProcessedModule();
+ return;
+ }
+ case BuildJobType::BUILD_JOB_COMPILE:
+ {
+ if (!error) // Build was successful
+ {
+ if (sWorld->getBoolConfig(CONFIG_HOTSWAP_INSTALL_ENABLED))
+ {
+ // Continue with the installation when it's enabled
+ TC_LOG_INFO("scripts.hotswap",
+ ">> Successfully build module %s, continue with installing...",
+ _build_job->GetModuleName().c_str());
+
+ DoInstallCurrentProcessedModule();
+ return;
+ }
+
+ // Skip the installation because it's disabled in config
+ TC_LOG_INFO("scripts.hotswap",
+ ">> Successfully build module %s, skipped the installation.",
+ _build_job->GetModuleName().c_str());
+ }
+ else // Build wasn't successful
+ {
+ TC_LOG_ERROR("scripts.hotswap",
+ ">> The build of module %s failed! See the log for details.",
+ _build_job->GetModuleName().c_str());
+ }
+ break;
+ }
+ case BuildJobType::BUILD_JOB_INSTALL:
+ {
+ if (!error)
+ {
+ // Installation was successful
+ TC_LOG_INFO("scripts.hotswap", ">> Successfully installed module %s.",
+ _build_job->GetModuleName().c_str());
+ }
+ else
+ {
+ // Installation wasn't successful
+ TC_LOG_INFO("scripts.hotswap",
+ ">> The installation of module %s failed! See the log for details.",
+ _build_job->GetModuleName().c_str());
+ }
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Clear the current job
+ _build_job.reset();
+ }
+
+ /// Reruns CMake asynchronously over the build directory
+ void DoRerunCMake()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Rerunning CMake because there were sources added or removed...");
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_RERUN_CMAKE,
+ InvokeAsyncCMakeCommand(BuiltInConfig::GetBuildDirectory()));
+ }
+
+ /// Invokes a new build of the current active module job
+ void DoCompileCurrentProcessedModule()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Starting asynchronous build job for module %s...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_COMPILE,
+ InvokeAsyncCMakeCommand(
+ "--build", BuiltInConfig::GetBuildDirectory(),
+ "--target", _build_job->GetProjectName(),
+ "--config", _build_job->GetBuildDirective()));
+ }
+
+ /// Invokes a new asynchronous install of the current active module job
+ void DoInstallCurrentProcessedModule()
+ {
+ ASSERT(_build_job, "There isn't any active build job!");
+
+ TC_LOG_INFO("scripts.hotswap", "Starting asynchronous install job for module %s...",
+ _build_job->GetModuleName().c_str());
+
+ _build_job->UpdateCurrentJob(BuildJobType::BUILD_JOB_INSTALL,
+ InvokeAsyncCMakeCommand(
+ "-DCOMPONENT=" + _build_job->GetProjectName(),
+ "-DBUILD_TYPE=" + _build_job->GetBuildDirective(),
+ "-P", fs::absolute("cmake_install.cmake",
+ BuiltInConfig::GetBuildDirectory()).generic_string()));
+ }
+
+ /// Sets the CMAKE_INSTALL_PREFIX variable in the CMake cache
+ /// to point to the current worldserver position,
+ /// since most users will forget this.
+ void DoCMakePrefixCorrectionIfNeeded()
+ {
+ TC_LOG_INFO("scripts.hotswap", "Correcting your CMAKE_INSTALL_PREFIX in \"%s\"...",
+ BuiltInConfig::GetBuildDirectory().c_str());
+
+ auto const cmake_cache_path = fs::absolute("CMakeCache.txt",
+ BuiltInConfig::GetBuildDirectory());
+
+ // Stop when the CMakeCache wasn't found
+ if (![&]
+ {
+ boost::system::error_code error;
+ if (!fs::exists(cmake_cache_path, error))
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> CMake cache \"%s\" doesn't exist, "
+ "set the \"BuildDirectory\" option in your worldserver.conf to point"
+ "to your build directory!",
+ cmake_cache_path.generic_string().c_str());
+
+ return false;
+ }
+ else
+ return true;
+ }())
+ return;
+
+ TC_LOG_TRACE("scripts.hotswap", "Checking CMake cache (\"%s\") "
+ "for the correct CMAKE_INSTALL_PREFIX location...",
+ cmake_cache_path.generic_string().c_str());
+
+ std::string cmake_cache_content;
+ {
+ std::ifstream in(cmake_cache_path.generic_string());
+ if (!in.is_open())
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Failed to read the CMake cache at \"%s\"!",
+ cmake_cache_path.generic_string().c_str());
+
+ return;
+ }
+
+ std::ostringstream ss;
+ ss << in.rdbuf();
+ cmake_cache_content = ss.str();
+
+ in.close();
+ }
+
+ static std::string const prefix_key = "CMAKE_INSTALL_PREFIX:PATH=";
+
+ // Extract the value of CMAKE_INSTALL_PREFIX
+ auto begin = cmake_cache_content.find(prefix_key);
+ if (begin != std::string::npos)
+ {
+ begin += prefix_key.length();
+ auto const end = cmake_cache_content.find("\n", begin);
+ if (end != std::string::npos)
+ {
+ fs::path value = cmake_cache_content.substr(begin, end - begin);
+
+ auto current_path = fs::current_path();
+
+ #ifndef _WIN32
+ // The worldserver location is ${CMAKE_INSTALL_PREFIX}/bin
+ // on all other platforms then windows
+ current_path = current_path.remove_leaf();
+ #endif
+
+ if (value != current_path)
+ {
+ // Prevent correction of the install prefix
+ // when we are starting the core from inside the build tree
+ bool const is_in_path = [&]
+ {
+ fs::path base = BuiltInConfig::GetBuildDirectory();
+ fs::path branch = value;
+ while (!branch.empty())
+ {
+ if (base == branch)
+ return true;
+
+ branch = branch.remove_leaf();
+ }
+
+ return false;
+ }();
+
+ if (is_in_path)
+ return;
+
+ TC_LOG_INFO("scripts.hotswap", ">> Found outdated CMAKE_INSTALL_PREFIX (\"%s\"), "
+ "worldserver is currently installed at %s...",
+ value.generic_string().c_str(), current_path.generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_INFO("scripts.hotswap", ">> CMAKE_INSTALL_PREFIX is equal to the current path of execution.");
+ return;
+ }
+ }
+ }
+
+ TC_LOG_INFO("scripts.hotswap", "Invoking CMake cache correction...");
+
+ auto const error = InvokeCMakeCommand(
+ "-DCMAKE_INSTALL_PREFIX:PATH=" + fs::current_path().generic_string(),
+ BuiltInConfig::GetBuildDirectory());
+
+ if (error)
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Failed to update the CMAKE_INSTALL_PREFIX! "
+ "This could lead to unexpected behaviour!");
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", ">> Successfully corrected your CMAKE_INSTALL_PREFIX variable"
+ "to point at your current path of execution.");
+ }
+ }
+
+ // File watcher instance and watcher ID's
+ efsw::FileWatcher _fileWatcher;
+ efsw::WatchID _libraryWatcher;
+
+ // Unique library name counter which is used to
+ // generate unique names for every shared library version.
+ uint32 _unique_library_name_counter;
+
+ // Queue which is used for thread safe message processing
+ MPSCQueue<ScriptReloaderMessage> _messages;
+
+ // Change requests to load or unload shared libraries
+ std::unordered_set<fs::path /*path*/> _libraries_changed;
+ // The timestamp which indicates the last time a library was changed
+ uint32 _last_time_library_changed;
+
+ // Contains all running script modules
+ // The associated shared libraries are unloaded immediately
+ // on loosing ownership through RAII.
+ std::unordered_map<std::string /*module name*/,
+ std::pair<std::shared_ptr<ScriptModule>, std::unique_ptr<SourceUpdateListener>>
+ > _running_script_modules;
+ // Container which maps the path of a shared library to it's module name
+ std::unordered_map<fs::path, std::string /*module name*/> _running_script_module_names;
+ // Container which maps the module name to it's last known build directive
+ std::unordered_map<std::string /*module name*/, std::string /*build directive*/> _known_modules_build_directives;
+
+ // Modules which were changed and are queued for recompilation
+ std::unordered_map<std::string /*module*/,
+ std::unordered_map<fs::path /*path*/, ChangeStateRequest /*state*/>> _sources_changed;
+ // Tracks the time since the last module has changed to avoid burst updates
+ uint32 _last_time_sources_changed;
+
+ // Tracks the last timestamp the user was informed about a certain repeating event.
+ uint32 _last_time_user_informed;
+
+ // Represents the current build job which is in progress
+ Optional<BuildJob> _build_job;
+
+ // Is true when the build job dispatcher should stop after
+ // the current job has finished
+ bool terminate_early;
+};
+
+/// Maps efsw actions to strings
+static char const* ActionToString(efsw::Action action)
+{
+ switch (action)
+ {
+ case efsw::Action::Add:
+ return "added";
+ case efsw::Action::Delete:
+ return "deleted";
+ case efsw::Action::Moved:
+ return "moved";
+ default:
+ return "modified";
+ }
+}
+
+void LibraryUpdateListener::handleFileAction(efsw::WatchID watchid, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename)
+{
+ // TC_LOG_TRACE("scripts.hotswap", "Library listener detected change on possible module \"%s\ (%s)".", filename.c_str(), ActionToString(action));
+
+ // Split moved actions into a delete and an add action
+ if (action == efsw::Action::Moved)
+ {
+ ASSERT(!oldFilename.empty(), "Old filename doesn't exist!");
+ handleFileAction(watchid, dir, oldFilename, efsw::Action::Delete);
+ handleFileAction(watchid, dir, filename, efsw::Action::Add);
+ return;
+ }
+
+ sScriptReloadMgr->QueueMessage([=](HotSwapScriptReloadMgr* reloader) mutable
+ {
+ auto const path = fs::absolute(
+ filename,
+ sScriptReloadMgr->GetLibraryDirectory());
+
+ if (!HasValidScriptModuleName(filename))
+ return;
+
+ switch (action)
+ {
+ case efsw::Actions::Add:
+ TC_LOG_TRACE("scripts.hotswap", ">> Loading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ case efsw::Actions::Delete:
+ TC_LOG_TRACE("scripts.hotswap", ">> Unloading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ case efsw::Actions::Modified:
+ TC_LOG_TRACE("scripts.hotswap", ">> Reloading \"%s\" (%s)...",
+ path.generic_string().c_str(), ActionToString(action));
+ reloader->QueueSharedLibraryChanged(path);
+ break;
+ default:
+ WPAbort();
+ break;
+ }
+ });
+}
+
+/// Returns true when the given path has a known C++ file extension
+static bool HasCXXSourceFileExtension(fs::path const& path)
+{
+ static std::regex const regex("^\\.(h|hpp|c|cc|cpp)$");
+ return std::regex_match(path.extension().generic_string(), regex);
+}
+
+SourceUpdateListener::SourceUpdateListener(fs::path path, std::string script_module_name)
+ : path_(std::move(path)), script_module_name_(std::move(script_module_name)),
+ watcher_id_(sScriptReloadMgr->_fileWatcher.addWatch(path_.generic_string(), this, true))
+{
+ if (watcher_id_ >= 0)
+ {
+ TC_LOG_TRACE("scripts.hotswap", ">> Attached the source recompiler to \"%s\".",
+ path_.generic_string().c_str());
+ }
+ else
+ {
+ TC_LOG_ERROR("scripts.hotswap", "Failed to initialize thesource recompiler on \"%s\".",
+ path_.generic_string().c_str());
+ }
+}
+
+SourceUpdateListener::~SourceUpdateListener()
+{
+ if (watcher_id_ >= 0)
+ {
+ sScriptReloadMgr->_fileWatcher.removeWatch(watcher_id_);
+
+ TC_LOG_TRACE("scripts.hotswap", ">> Detached the source recompiler from \"%s\".",
+ path_.generic_string().c_str());
+ }
+}
+
+void SourceUpdateListener::handleFileAction(efsw::WatchID watchid, std::string const& dir,
+ std::string const& filename, efsw::Action action, std::string oldFilename)
+{
+ // TC_LOG_TRACE("scripts.hotswap", "Source listener detected change on possible file \"%s/%s\" (%s).", dir.c_str(), filename.c_str(), ActionToString(action));
+
+ // Skip the file change notification if the recompiler is disabled
+ if (!sWorld->getBoolConfig(CONFIG_HOTSWAP_RECOMPILER_ENABLED))
+ return;
+
+ // Split moved actions into a delete and an add action
+ if (action == efsw::Action::Moved)
+ {
+ ASSERT(!oldFilename.empty(), "Old filename doesn't exist!");
+ handleFileAction(watchid, dir, oldFilename, efsw::Action::Delete);
+ handleFileAction(watchid, dir, filename, efsw::Action::Add);
+ return;
+ }
+
+ auto const path = fs::absolute(
+ filename,
+ dir);
+
+ // Check if the file is a C/C++ source file.
+ if (!path.has_extension() || !HasCXXSourceFileExtension(path))
+ return;
+
+ /// Thread safe part
+ sScriptReloadMgr->QueueMessage([=](HotSwapScriptReloadMgr* reloader)
+ {
+ TC_LOG_TRACE("scripts.hotswap", "Detected source change on module \"%s\", "
+ "queued for recompilation...", script_module_name_.c_str());
+
+ switch (action)
+ {
+ case efsw::Actions::Add:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was added.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueAddSourceFile(script_module_name_, path);
+ break;
+ case efsw::Actions::Delete:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was deleted.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueRemoveSourceFile(script_module_name_, path);
+ break;
+ case efsw::Actions::Modified:
+ TC_LOG_TRACE("scripts.hotswap", "Source file %s of module %s was modified.",
+ path.generic_string().c_str(), script_module_name_.c_str());
+ reloader->QueueModifySourceFile(script_module_name_, path);
+ break;
+ default:
+ WPAbort();
+ break;
+ }
+ });
+}
+
+// Returns the module reference of the given context
+std::shared_ptr<ModuleReference>
+ ScriptReloadMgr::AcquireModuleReferenceOfContext(std::string const& context)
+{
+ auto const itr = sScriptReloadMgr->_running_script_modules.find(context);
+ if (itr != sScriptReloadMgr->_running_script_modules.end())
+ return itr->second.first;
+ else
+ return { };
+}
+
+// Returns the full hot swap implemented ScriptReloadMgr
+ScriptReloadMgr* ScriptReloadMgr::instance()
+{
+ static HotSwapScriptReloadMgr instance;
+ return &instance;
+}
+
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
diff --git a/src/server/game/Scripting/ScriptReloadMgr.h b/src/server/game/Scripting/ScriptReloadMgr.h
new file mode 100644
index 00000000000..f9b388f8eb0
--- /dev/null
+++ b/src/server/game/Scripting/ScriptReloadMgr.h
@@ -0,0 +1,81 @@
+/*
+ * 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 SCRIPT_RELOADER_H
+#define SCRIPT_RELOADER_H
+
+#include <memory>
+#include <string>
+#include "Define.h"
+#include <boost/filesystem/path.hpp>
+
+/// Represents a strong reference to a dynamic library which
+/// provides C++ scripts. As long as one reference to the library exists
+/// the library is kept loaded in the server, which makes it possible to lazy
+/// unload several script types on demand (like SpellScripts), and to
+/// provide multiple versions of the same script to the script factories.
+///
+/// Acquire a new reference through using:
+/// `ScriptReloadMgr::AcquireModuleReferenceOfContext`
+class ModuleReference
+{
+public:
+ virtual ~ModuleReference() { }
+
+ /// Returns the git revision hash of the referenced script module
+ virtual char const* GetScriptModuleRevisionHash() const = 0;
+ /// Returns the name of the referenced script module
+ virtual char const* GetScriptModule() const = 0;
+ /// Returns the path to the script module
+ virtual boost::filesystem::path const& GetModulePath() const = 0;
+};
+
+/// Provides the whole physical dynamic library unloading capability.
+/// Loads, Reloads and Unloads dynamic libraries on changes and
+/// informs the ScriptMgr about changes which were made.
+/// The ScriptReloadMgr is also responsible for watching the source directory
+/// and to invoke a build on changes.
+class TC_GAME_API ScriptReloadMgr
+{
+protected:
+ ScriptReloadMgr() { }
+
+public:
+ virtual ~ScriptReloadMgr() { }
+
+ /// Initializes the ScriptReloadMgr
+ virtual void Initialize() { }
+
+ /// Needs to be called periodically to check for updates on script modules.
+ /// Expects to be invoked in a thread safe way which means it's required that
+ /// the current thread is the only one which accesses the world data.
+ virtual void Update() { }
+
+ /// Unloads the ScriptReloadMgr
+ virtual void Unload() { }
+
+ /// Returns an owning reference to the current module of the given context
+ static std::shared_ptr<ModuleReference> AcquireModuleReferenceOfContext(
+ std::string const& context);
+
+ /// Returns the unique ScriptReloadMgr singleton instance
+ static ScriptReloadMgr* instance();
+};
+
+#define sScriptReloadMgr ScriptReloadMgr::instance()
+
+#endif // SCRIPT_RELOADER_H
diff --git a/src/server/game/Spells/Auras/SpellAuraEffects.cpp b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
index d6cdb99e75c..a8adda3ad58 100644
--- a/src/server/game/Spells/Auras/SpellAuraEffects.cpp
+++ b/src/server/game/Spells/Auras/SpellAuraEffects.cpp
@@ -1325,7 +1325,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
{
// apply glow vision
if (target->GetTypeId() == TYPEID_PLAYER)
- target->SetByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+ target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
target->m_invisibility.AddFlag(type);
target->m_invisibility.AddValue(type, GetAmount());
@@ -1337,7 +1337,7 @@ void AuraEffect::HandleModInvisibility(AuraApplication const* aurApp, uint8 mode
// if not have different invisibility auras.
// remove glow vision
if (target->GetTypeId() == TYPEID_PLAYER)
- target->RemoveByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+ target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
target->m_invisibility.DelFlag(type);
}
@@ -1409,7 +1409,7 @@ void AuraEffect::HandleModStealth(AuraApplication const* aurApp, uint8 mode, boo
target->SetStandFlags(UNIT_STAND_FLAGS_CREEP);
if (target->GetTypeId() == TYPEID_PLAYER)
- target->SetByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_STEALTH);
+ target->SetByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_STEALTH);
}
else
{
@@ -1421,7 +1421,7 @@ void AuraEffect::HandleModStealth(AuraApplication const* aurApp, uint8 mode, boo
target->RemoveStandFlags(UNIT_STAND_FLAGS_CREEP);
if (target->GetTypeId() == TYPEID_PLAYER)
- target->RemoveByteFlag(PLAYER_FIELD_BYTES2, 3, PLAYER_FIELD_BYTE2_STEALTH);
+ target->RemoveByteFlag(PLAYER_FIELD_BYTES2, PLAYER_FIELD_BYTES_2_OFFSET_AURA_VISION, PLAYER_FIELD_BYTE2_STEALTH);
}
}
@@ -2400,7 +2400,7 @@ void AuraEffect::HandleAuraTrackStealthed(AuraApplication const* aurApp, uint8 m
if (target->HasAuraType(GetAuraType()))
return;
}
- target->ApplyModFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTE_TRACK_STEALTHED, apply);
+ target->ApplyModByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_TRACK_STEALTHED, apply);
}
void AuraEffect::HandleAuraModStalked(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2915,7 +2915,7 @@ void AuraEffect::HandleAuraModIncreaseSpeed(AuraApplication const* aurApp, uint8
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_RUN, true);
+ target->UpdateSpeed(MOVE_RUN);
}
void AuraEffect::HandleAuraModIncreaseMountedSpeed(AuraApplication const* aurApp, uint8 mode, bool apply) const
@@ -2930,7 +2930,7 @@ void AuraEffect::HandleAuraModIncreaseFlightSpeed(AuraApplication const* aurApp,
Unit* target = aurApp->GetTarget();
if (mode & AURA_EFFECT_HANDLE_CHANGE_AMOUNT_MASK)
- target->UpdateSpeed(MOVE_FLIGHT, true);
+ target->UpdateSpeed(MOVE_FLIGHT);
//! Update ability to fly
if (GetAuraType() == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)
@@ -2965,7 +2965,7 @@ void AuraEffect::HandleAuraModIncreaseSwimSpeed(AuraApplication const* aurApp, u
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_SWIM, true);
+ target->UpdateSpeed(MOVE_SWIM);
}
void AuraEffect::HandleAuraModDecreaseSpeed(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
@@ -2975,12 +2975,12 @@ void AuraEffect::HandleAuraModDecreaseSpeed(AuraApplication const* aurApp, uint8
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_RUN, true);
- target->UpdateSpeed(MOVE_SWIM, true);
- target->UpdateSpeed(MOVE_FLIGHT, true);
- target->UpdateSpeed(MOVE_RUN_BACK, true);
- target->UpdateSpeed(MOVE_SWIM_BACK, true);
- target->UpdateSpeed(MOVE_FLIGHT_BACK, true);
+ target->UpdateSpeed(MOVE_RUN);
+ target->UpdateSpeed(MOVE_SWIM);
+ target->UpdateSpeed(MOVE_FLIGHT);
+ target->UpdateSpeed(MOVE_RUN_BACK);
+ target->UpdateSpeed(MOVE_SWIM_BACK);
+ target->UpdateSpeed(MOVE_FLIGHT_BACK);
}
void AuraEffect::HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint8 mode, bool /*apply*/) const
@@ -2990,9 +2990,9 @@ void AuraEffect::HandleAuraModUseNormalSpeed(AuraApplication const* aurApp, uint
Unit* target = aurApp->GetTarget();
- target->UpdateSpeed(MOVE_RUN, true);
- target->UpdateSpeed(MOVE_SWIM, true);
- target->UpdateSpeed(MOVE_FLIGHT, true);
+ target->UpdateSpeed(MOVE_RUN);
+ target->UpdateSpeed(MOVE_SWIM);
+ target->UpdateSpeed(MOVE_FLIGHT);
}
/*********************************************************/
@@ -5248,7 +5248,7 @@ void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 m
if (apply)
{
- target->SetUInt16Value(PLAYER_FIELD_BYTES2, 0, overrideId);
+ target->SetUInt16Value(PLAYER_FIELD_BYTES2, PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET, overrideId);
if (OverrideSpellDataEntry const* overrideSpells = sOverrideSpellDataStore.LookupEntry(overrideId))
for (uint8 i = 0; i < MAX_OVERRIDE_SPELL; ++i)
if (uint32 spellId = overrideSpells->spellId[i])
@@ -5256,7 +5256,7 @@ void AuraEffect::HandleAuraOverrideSpells(AuraApplication const* aurApp, uint8 m
}
else
{
- target->SetUInt16Value(PLAYER_FIELD_BYTES2, 0, 0);
+ target->SetUInt16Value(PLAYER_FIELD_BYTES2, PLAYER_BYTES_2_OVERRIDE_SPELLS_UINT16_OFFSET, 0);
if (OverrideSpellDataEntry const* overrideSpells = sOverrideSpellDataStore.LookupEntry(overrideId))
for (uint8 i = 0; i < MAX_OVERRIDE_SPELL; ++i)
if (uint32 spellId = overrideSpells->spellId[i])
@@ -5302,9 +5302,9 @@ void AuraEffect::HandlePreventResurrection(AuraApplication const* aurApp, uint8
return;
if (apply)
- aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
+ aurApp->GetTarget()->RemoveByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER);
else if (!aurApp->GetTarget()->GetBaseMap()->Instanceable())
- aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, 0, PLAYER_FIELD_BYTE_RELEASE_TIMER);
+ aurApp->GetTarget()->SetByteFlag(PLAYER_FIELD_BYTES, PLAYER_FIELD_BYTES_OFFSET_FLAGS, PLAYER_FIELD_BYTE_RELEASE_TIMER);
}
void AuraEffect::HandlePeriodicDummyAuraTick(Unit* target, Unit* caster) const
@@ -5974,14 +5974,14 @@ void AuraEffect::HandlePeriodicHealthLeechAuraTick(Unit* target, Unit* caster) c
caster->CalcAbsorbResist(target, GetSpellInfo()->GetSchoolMask(), DOT, damage, &absorb, &resist, m_spellInfo);
- if (target->GetHealth() < damage)
- damage = uint32(target->GetHealth());
-
TC_LOG_DEBUG("spells.periodic", "PeriodicTick: %s health leech of %s for %u dmg inflicted by %u abs is %u",
GetCasterGUID().ToString().c_str(), target->GetGUID().ToString().c_str(), damage, GetId(), absorb);
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/SpellScript.cpp b/src/server/game/Spells/SpellScript.cpp
index 6876f8fa7ef..e2598386466 100644
--- a/src/server/game/Spells/SpellScript.cpp
+++ b/src/server/game/Spells/SpellScript.cpp
@@ -16,6 +16,7 @@
*/
#include "Spell.h"
+#include "ScriptMgr.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "SpellMgr.h"
@@ -50,6 +51,12 @@ void _SpellScript::_Init(std::string const* scriptname, uint32 spellId)
m_currentScriptState = SPELL_SCRIPT_STATE_NONE;
m_scriptName = scriptname;
m_scriptSpellId = spellId;
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+ // Acquire a strong reference to the binary code
+ // to keep it loaded until all spells are destroyed.
+ m_moduleReference = sScriptMgr->AcquireModuleReferenceOfScriptName(*scriptname);
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
}
std::string const* _SpellScript::_GetScriptName() const
diff --git a/src/server/game/Spells/SpellScript.h b/src/server/game/Spells/SpellScript.h
index 1d4efd8073d..539bc54cc94 100644
--- a/src/server/game/Spells/SpellScript.h
+++ b/src/server/game/Spells/SpellScript.h
@@ -22,6 +22,7 @@
#include "SharedDefines.h"
#include "SpellAuraDefines.h"
#include "Spell.h"
+#include "ScriptReloadMgr.h"
#include <stack>
class Unit;
@@ -105,6 +106,16 @@ class TC_GAME_API _SpellScript
uint8 m_currentScriptState;
std::string const* m_scriptName;
uint32 m_scriptSpellId;
+
+ private:
+
+#ifdef TRINITY_API_USE_DYNAMIC_LINKING
+
+ // Strong reference to keep the binary code loaded
+ std::shared_ptr<ModuleReference> m_moduleReference;
+
+#endif // #ifndef TRINITY_API_USE_DYNAMIC_LINKING
+
public:
//
// SpellScript/AuraScript interface base
diff --git a/src/server/game/World/World.cpp b/src/server/game/World/World.cpp
index 6380aece941..4b64ef0bbd8 100644
--- a/src/server/game/World/World.cpp
+++ b/src/server/game/World/World.cpp
@@ -54,6 +54,7 @@
#include "PoolMgr.h"
#include "GitRevision.h"
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "SkillDiscovery.h"
#include "SkillExtraItems.h"
#include "SmartAI.h"
@@ -1319,6 +1320,14 @@ void World::LoadConfigSettings(bool reload)
m_bool_configs[CONFIG_CALCULATE_CREATURE_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Creature.Zone.Area.Data", false);
m_bool_configs[CONFIG_CALCULATE_GAMEOBJECT_ZONE_AREA_DATA] = sConfigMgr->GetBoolDefault("Calculate.Gameoject.Zone.Area.Data", false);
+ // HotSwap
+ m_bool_configs[CONFIG_HOTSWAP_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.Enabled", true);
+ m_bool_configs[CONFIG_HOTSWAP_RECOMPILER_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableReCompiler", true);
+ m_bool_configs[CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableEarlyTermination", true);
+ m_bool_configs[CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableBuildFileRecreation", true);
+ m_bool_configs[CONFIG_HOTSWAP_INSTALL_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnableInstall", true);
+ m_bool_configs[CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED] = sConfigMgr->GetBoolDefault("HotSwap.EnablePrefixCorrection", true);
+
// call ScriptMgr if we're reloading the configuration
if (reload)
sScriptMgr->OnConfigLoad(reload);
@@ -1826,6 +1835,8 @@ void World::SetInitialWorldSettings()
m_timers[WUPDATE_PINGDB].SetInterval(getIntConfig(CONFIG_DB_PING_INTERVAL)*MINUTE*IN_MILLISECONDS); // Mysql ping time in minutes
+ m_timers[WUPDATE_CHECK_FILECHANGES].SetInterval(500);
+
//to set mailtimer to return mails every day between 4 and 5 am
//mailtimer is increased when updating auctions
//one second is 1000 -(tested on win system)
@@ -2109,6 +2120,13 @@ void World::Update(uint32 diff)
m_timers[WUPDATE_AHBOT].Reset();
}
+ /// <li> Handle file changes
+ if (m_timers[WUPDATE_CHECK_FILECHANGES].Passed())
+ {
+ sScriptReloadMgr->Update();
+ m_timers[WUPDATE_CHECK_FILECHANGES].Reset();
+ }
+
/// <li> Handle session updates when the timer has passed
ResetTimeDiffRecord();
UpdateSessions(diff);
diff --git a/src/server/game/World/World.h b/src/server/game/World/World.h
index a59d9ef882a..abc0ea452ac 100644
--- a/src/server/game/World/World.h
+++ b/src/server/game/World/World.h
@@ -81,6 +81,7 @@ enum WorldTimers
WUPDATE_DELETECHARS,
WUPDATE_AHBOT,
WUPDATE_PINGDB,
+ WUPDATE_CHECK_FILECHANGES,
WUPDATE_COUNT
};
@@ -167,6 +168,12 @@ enum WorldBoolConfigs
CONFIG_RESET_DUEL_HEALTH_MANA,
CONFIG_BASEMAP_LOAD_GRIDS,
CONFIG_INSTANCEMAP_LOAD_GRIDS,
+ CONFIG_HOTSWAP_ENABLED,
+ CONFIG_HOTSWAP_RECOMPILER_ENABLED,
+ CONFIG_HOTSWAP_EARLY_TERMINATION_ENABLED,
+ CONFIG_HOTSWAP_BUILD_FILE_RECREATION_ENABLED,
+ CONFIG_HOTSWAP_INSTALL_ENABLED,
+ CONFIG_HOTSWAP_PREFIX_CORRECTION_ENABLED,
BOOL_CONFIG_VALUE_COUNT
};
diff --git a/src/server/scripts/CMakeLists.txt b/src/server/scripts/CMakeLists.txt
index 8c0adc4c1e1..31ba073e77d 100644
--- a/src/server/scripts/CMakeLists.txt
+++ b/src/server/scripts/CMakeLists.txt
@@ -8,69 +8,232 @@
# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-# Enable precompiled headers when using the GCC compiler.
-
-message(STATUS "SCRIPT PREPARATIONS")
-
-macro(PrepareScripts name out)
- file(GLOB_RECURSE found
- ${name}/*.h
- ${name}/*.cpp
- )
- list(APPEND ${out} ${found})
- message(STATUS " -> Prepared: ${name}")
-endmacro(PrepareScripts)
-
-PrepareScripts(Spells PRIVATE_SOURCES)
-PrepareScripts(Commands PRIVATE_SOURCES)
-
-if(SCRIPTS)
- PrepareScripts(Custom PRIVATE_SOURCES)
- PrepareScripts(World PRIVATE_SOURCES)
- PrepareScripts(OutdoorPvP PRIVATE_SOURCES)
- PrepareScripts(EasternKingdoms PRIVATE_SOURCES)
- PrepareScripts(Kalimdor PRIVATE_SOURCES)
- PrepareScripts(Outland PRIVATE_SOURCES)
- PrepareScripts(Northrend PRIVATE_SOURCES)
- PrepareScripts(Events PRIVATE_SOURCES)
- PrepareScripts(Pet PRIVATE_SOURCES)
+message("")
+
+# Make the script module list available in the current scope
+GetScriptModuleList(SCRIPT_MODULE_LIST)
+
+# Make the native install offset available in this scope
+GetInstallOffset(INSTALL_OFFSET)
+
+# Sets the SCRIPTS_${SCRIPT_MODULE} variables
+# when using predefined templates for script building
+# like dynamic, static, minimal-static...
+# Sets SCRIPTS_DEFAULT_LINKAGE
+if (SCRIPTS MATCHES "dynamic")
+ set(SCRIPTS_DEFAULT_LINKAGE "dynamic")
+elseif(SCRIPTS MATCHES "static")
+ set(SCRIPTS_DEFAULT_LINKAGE "static")
+else()
+ set(SCRIPTS_DEFAULT_LINKAGE "disabled")
+endif()
+# Sets SCRIPTS_USE_WHITELIST
+# Sets SCRIPTS_WHITELIST
+if (SCRIPTS MATCHES "minimal")
+ set(SCRIPTS_USE_WHITELIST ON)
+ # Whitelist which is used when minimal is selected
+ list(APPEND SCRIPTS_WHITELIST Commands Spells)
endif()
-message(STATUS "SCRIPT PREPARATION COMPLETE")
-message("")
+# Set the SCRIPTS_${SCRIPT_MODULE} variables from the
+# variables set above
+foreach(SCRIPT_MODULE ${SCRIPT_MODULE_LIST})
+ ScriptModuleNameToVariable(${SCRIPT_MODULE} SCRIPT_MODULE_VARIABLE)
+ if (${SCRIPT_MODULE_VARIABLE} STREQUAL "default")
+ if(SCRIPTS_USE_WHITELIST)
+ list(FIND SCRIPTS_WHITELIST "${SCRIPT_MODULE}" INDEX)
+ if (${INDEX} GREATER -1)
+ set(${SCRIPT_MODULE_VARIABLE} ${SCRIPTS_DEFAULT_LINKAGE})
+ else()
+ set(${SCRIPT_MODULE_VARIABLE} "disabled")
+ endif()
+ else()
+ set(${SCRIPT_MODULE_VARIABLE} ${SCRIPTS_DEFAULT_LINKAGE})
+ endif()
+ endif()
+ # Build the Graph values
+ if (${SCRIPT_MODULE_VARIABLE} MATCHES "dynamic")
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ GetNativeSharedLibraryName(${SCRIPT_MODULE_PROJECT_NAME} SCRIPT_PROJECT_LIBRARY)
+ list(APPEND GRAPH_KEYS ${SCRIPT_MODULE_PROJECT_NAME})
+ set(GRAPH_VALUE_DISPLAY_${SCRIPT_MODULE_PROJECT_NAME} ${SCRIPT_PROJECT_LIBRARY})
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_${SCRIPT_MODULE_PROJECT_NAME} ${SCRIPT_MODULE})
+ elseif(${SCRIPT_MODULE_VARIABLE} MATCHES "static")
+ list(APPEND GRAPH_KEYS worldserver)
+ set(GRAPH_VALUE_DISPLAY_worldserver worldserver)
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_worldserver ${SCRIPT_MODULE})
+ else()
+ list(APPEND GRAPH_KEYS disabled)
+ set(GRAPH_VALUE_DISPLAY_disabled disabled)
+ list(APPEND GRAPH_VALUE_CONTAINS_MODULES_disabled ${SCRIPT_MODULE})
+ endif()
+endforeach()
-list(APPEND PRIVATE_SOURCES
- ${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.h
- ${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.cpp)
+list(SORT GRAPH_KEYS)
+list(REMOVE_DUPLICATES GRAPH_KEYS)
+# Display the script graph
+message("* Script configuration (${SCRIPTS}):
+ |")
+
+foreach(GRAPH_KEY ${GRAPH_KEYS})
+ if (NOT GRAPH_KEY STREQUAL "disabled")
+ message(" +- ${GRAPH_VALUE_DISPLAY_${GRAPH_KEY}}")
+ else()
+ message(" | ${GRAPH_VALUE_DISPLAY_${GRAPH_KEY}}")
+ endif()
+ foreach(GRAPH_PROJECT_ENTRY ${GRAPH_VALUE_CONTAINS_MODULES_${GRAPH_KEY}})
+ message(" | +- ${GRAPH_PROJECT_ENTRY}")
+ endforeach()
+ message(" |")
+endforeach()
+
+# Base sources which are used by every script project
if (USE_SCRIPTPCH)
- set(PRIVATE_PCH_HEADER PrecompiledHeaders/ScriptPCH.h)
- set(PRIVATE_PCH_SOURCE PrecompiledHeaders/ScriptPCH.cpp)
+ set(PRIVATE_PCH_HEADER ScriptPCH.h)
+ set(PRIVATE_PCH_SOURCE ScriptPCH.cpp)
endif ()
GroupSources(${CMAKE_CURRENT_SOURCE_DIR})
+# Configures the scriptloader with the given name and stores the output in the LOADER_OUT variable.
+# It is possible to expose multiple subdirectories from the same scriptloader through passing
+# it to the variable arguments
+function(ConfigureScriptLoader SCRIPTLOADER_NAME LOADER_OUT IS_DYNAMIC_SCRIPTLOADER)
+ # Deduces following variables which are referenced by thge template:
+ # TRINITY_IS_DYNAMIC_SCRIPTLOADER
+ # TRINITY_SCRIPTS_FORWARD_DECL
+ # TRINITY_SCRIPTS_INVOKE
+ # TRINITY_CURRENT_SCRIPT_PROJECT
+
+ # To generate export macros
+ set(TRINITY_IS_DYNAMIC_SCRIPTLOADER ${IS_DYNAMIC_SCRIPTLOADER})
+ # To generate forward declarations of the loading functions
+ unset(TRINITY_SCRIPTS_FORWARD_DECL)
+ unset(TRINITY_SCRIPTS_INVOKE)
+ # The current script project which is built in
+ set(TRINITY_CURRENT_SCRIPT_PROJECT ${SCRIPTLOADER_NAME})
+ foreach(LOCALE_SCRIPT_MODULE ${ARGN})
+ # Determine the loader function ("Add##${NameOfDirectory}##Scripts()")
+ set(LOADER_FUNCTION
+ "Add${LOCALE_SCRIPT_MODULE}Scripts()")
+ # Generate the funciton call and the forward declarations
+ set(TRINITY_SCRIPTS_FORWARD_DECL
+ "${TRINITY_SCRIPTS_FORWARD_DECL}void ${LOADER_FUNCTION};\n")
+ set(TRINITY_SCRIPTS_INVOKE
+ "${TRINITY_SCRIPTS_INVOKE} ${LOADER_FUNCTION};\n")
+ endforeach()
+ set(GENERATED_LOADER ${CMAKE_CURRENT_BINARY_DIR}/gen_scriptloader/${SCRIPTLOADER_NAME}/ScriptLoader.cpp)
+ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/ScriptLoader.cpp.in.cmake ${GENERATED_LOADER})
+ set(${LOADER_OUT} ${GENERATED_LOADER} PARENT_SCOPE)
+endfunction()
+
+# Generates the actual script projects
+# Fills the STATIC_SCRIPT_MODULES and DYNAMIC_SCRIPT_MODULE_PROJECTS variables
+# which contain the names which scripts are linked statically/dynamically and
+# adds the sources of the static modules to the PRIVATE_SOURCES variable.
+foreach(SCRIPT_MODULE ${SCRIPT_MODULE_LIST})
+ GetPathToScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PATH)
+ ScriptModuleNameToVariable(${SCRIPT_MODULE} SCRIPT_MODULE_VARIABLE)
+
+ if ((${SCRIPT_MODULE_VARIABLE} STREQUAL "disabled") OR
+ (${SCRIPT_MODULE_VARIABLE} STREQUAL "static"))
+ # Uninstall disabled modules
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ GetNativeSharedLibraryName(${SCRIPT_MODULE_PROJECT_NAME} SCRIPT_MODULE_OUTPUT_NAME)
+ list(APPEND DISABLED_SCRIPT_MODULE_PROJECTS ${INSTALL_OFFSET}/${SCRIPT_MODULE_OUTPUT_NAME})
+ if (${SCRIPT_MODULE_VARIABLE} STREQUAL "static")
+ # Add the module name to STATIC_SCRIPT_MODULES
+ list(APPEND STATIC_SCRIPT_MODULES ${SCRIPT_MODULE})
+ # Add the module content to the whole static module
+ CollectSourceFiles(${SCRIPT_MODULE_PATH} PRIVATE_SOURCES)
+ endif()
+ elseif(${SCRIPT_MODULE_VARIABLE} STREQUAL "dynamic")
+ # Generate an own dynamic module which is loadable on runtime
+ # Add the module content to the whole static module
+ unset(SCRIPT_MODULE_PRIVATE_SOURCES)
+ CollectSourceFiles(${SCRIPT_MODULE_PATH} SCRIPT_MODULE_PRIVATE_SOURCES)
+ # Configure the scriptloader
+ ConfigureScriptLoader(${SCRIPT_MODULE} SCRIPT_MODULE_PRIVATE_SCRIPTLOADER ON ${SCRIPT_MODULE})
+ GetProjectNameOfScriptModule(${SCRIPT_MODULE} SCRIPT_MODULE_PROJECT_NAME)
+ # Add the module name to DYNAMIC_SCRIPT_MODULES
+ list(APPEND DYNAMIC_SCRIPT_MODULE_PROJECTS ${SCRIPT_MODULE_PROJECT_NAME})
+ # Create the script module project
+ add_library(${SCRIPT_MODULE_PROJECT_NAME} SHARED
+ ${PRIVATE_PCH_SOURCE}
+ ${SCRIPT_MODULE_PRIVATE_SOURCES}
+ ${SCRIPT_MODULE_PRIVATE_SCRIPTLOADER})
+ target_link_libraries(${SCRIPT_MODULE_PROJECT_NAME}
+ PUBLIC
+ game)
+ set_target_properties(${SCRIPT_MODULE_PROJECT_NAME}
+ PROPERTIES
+ FOLDER
+ "scripts")
+
+ if(UNIX)
+ install(TARGETS ${SCRIPT_MODULE_PROJECT_NAME}
+ DESTINATION ${INSTALL_OFFSET}
+ COMPONENT ${SCRIPT_MODULE_PROJECT_NAME})
+ elseif(WIN32)
+ install(TARGETS ${SCRIPT_MODULE_PROJECT_NAME}
+ RUNTIME DESTINATION ${INSTALL_OFFSET}
+ COMPONENT ${SCRIPT_MODULE_PROJECT_NAME})
+ if(MSVC)
+ # Place the script modules in the script subdirectory
+ set_target_properties(${SCRIPT_MODULE_PROJECT_NAME} PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/Debug/scripts
+ RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/Release/scripts
+ RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/bin/RelWithDebInfo/scripts
+ RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/bin/MinSizeRel/scripts)
+ endif()
+ endif()
+ else()
+ message(FATAL_ERROR "Unknown value \"${${SCRIPT_MODULE_VARIABLE}}\"!")
+ endif()
+endforeach()
+
+# Add the dynamic script modules to the worldserver as dependency
+set(WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES ${DYNAMIC_SCRIPT_MODULE_PROJECTS} PARENT_SCOPE)
+
+ConfigureScriptLoader("static" SCRIPT_MODULE_PRIVATE_SCRIPTLOADER OFF ${STATIC_SCRIPT_MODULES})
+
add_library(scripts STATIC
+ ScriptLoader.h
${PRIVATE_PCH_SOURCE}
- ${PRIVATE_SOURCES}
-)
+ ${SCRIPT_MODULE_PRIVATE_SCRIPTLOADER}
+ ${PRIVATE_SOURCES})
-target_include_directories(scripts
+target_link_libraries(scripts
PUBLIC
- ${CMAKE_CURRENT_SOURCE_DIR}
- PRIVATE
- ${CMAKE_CURRENT_BINARY_DIR})
+ game-interface)
-target_link_libraries(scripts
+target_include_directories(scripts
PUBLIC
- game)
+ ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(scripts
- PROPERTIES
- FOLDER
- "server")
+ PROPERTIES
+ FOLDER
+ "scripts")
# Generate precompiled header
if (USE_SCRIPTPCH)
- add_cxx_pch(scripts ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
+ list(APPEND ALL_SCRIPT_PROJECTS scripts ${DYNAMIC_SCRIPT_MODULE_PROJECTS})
+ add_cxx_pch("${ALL_SCRIPT_PROJECTS}" ${PRIVATE_PCH_HEADER} ${PRIVATE_PCH_SOURCE})
+endif()
+
+# Remove all shared libraries in the installl directory which
+# are contained in the static library already.
+if (DISABLED_SCRIPT_MODULE_PROJECTS)
+ install(CODE "
+ foreach(SCRIPT_TO_UNINSTALL ${DISABLED_SCRIPT_MODULE_PROJECTS})
+ if (EXISTS \"\${SCRIPT_TO_UNINSTALL}\")
+ message(STATUS \"Uninstalling: \${SCRIPT_TO_UNINSTALL}\")
+ file(REMOVE \"\${SCRIPT_TO_UNINSTALL}\")
+ endif()
+ endforeach()
+ ")
endif()
+
+message("")
diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp
index 01a048c8795..1d8094885d4 100644
--- a/src/server/scripts/Commands/cs_debug.cpp
+++ b/src/server/scripts/Commands/cs_debug.cpp
@@ -124,6 +124,23 @@ public:
return false;
}
+ // Dump camera locations
+ if (CinematicSequencesEntry const* cineSeq = sCinematicSequencesStore.LookupEntry(id))
+ {
+ std::unordered_map<uint32, FlyByCameraCollection>::const_iterator itr = sFlyByCameraStore.find(cineSeq->cinematicCamera);
+ if (itr != sFlyByCameraStore.end())
+ {
+ handler->PSendSysMessage("Waypoints for sequence %u, camera %u", id, cineSeq->cinematicCamera);
+ uint32 count = 1 ;
+ for (FlyByCamera cam : itr->second)
+ {
+ handler->PSendSysMessage("%02u - %7ums [%f, %f, %f] Facing %f (%f degrees)", count, cam.timeStamp, cam.locations.x, cam.locations.y, cam.locations.z, cam.locations.w, cam.locations.w * (180 / M_PI));
+ count++;
+ }
+ handler->PSendSysMessage("%u waypoints dumped", itr->second.size());
+ }
+ }
+
handler->GetSession()->GetPlayer()->SendCinematicStart(id);
return true;
}
diff --git a/src/server/scripts/Commands/cs_modify.cpp b/src/server/scripts/Commands/cs_modify.cpp
index 3a062b21c3e..f1ddb448b35 100644
--- a/src/server/scripts/Commands/cs_modify.cpp
+++ b/src/server/scripts/Commands/cs_modify.cpp
@@ -477,11 +477,11 @@ public:
if (handler->needReportToTarget(target))
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, handler->GetNameLink().c_str(), ASpeed);
- target->SetSpeed(MOVE_WALK, ASpeed, true);
- target->SetSpeed(MOVE_RUN, ASpeed, true);
- target->SetSpeed(MOVE_SWIM, ASpeed, true);
- //target->SetSpeed(MOVE_TURN, ASpeed, true);
- target->SetSpeed(MOVE_FLIGHT, ASpeed, true);
+ 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);
return true;
}
@@ -525,7 +525,7 @@ public:
if (handler->needReportToTarget(target))
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, handler->GetNameLink().c_str(), Speed);
- target->SetSpeed(MOVE_RUN, Speed, true);
+ target->SetSpeedRate(MOVE_RUN, Speed);
return true;
}
@@ -570,7 +570,7 @@ public:
if (handler->needReportToTarget(target))
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, handler->GetNameLink().c_str(), Swim);
- target->SetSpeed(MOVE_SWIM, Swim, true);
+ target->SetSpeedRate(MOVE_SWIM, Swim);
return true;
}
@@ -615,7 +615,7 @@ public:
if (handler->needReportToTarget(target))
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, handler->GetNameLink().c_str(), BSpeed);
- target->SetSpeed(MOVE_RUN_BACK, BSpeed, true);
+ target->SetSpeedRate(MOVE_RUN_BACK, BSpeed);
return true;
}
@@ -651,7 +651,7 @@ public:
if (handler->needReportToTarget(target))
ChatHandler(target->GetSession()).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, handler->GetNameLink().c_str(), FSpeed);
- target->SetSpeed(MOVE_FLIGHT, FSpeed, true);
+ target->SetSpeedRate(MOVE_FLIGHT, FSpeed);
return true;
}
@@ -1339,8 +1339,8 @@ public:
}
// Set gender
- target->SetByteValue(UNIT_FIELD_BYTES_0, 2, gender);
- target->SetByteValue(PLAYER_BYTES_3, 0, gender);
+ target->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_GENDER, gender);
+ target->SetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_GENDER, gender);
// Change display ID
target->InitDisplayIds();
diff --git a/src/server/scripts/Commands/cs_reload.cpp b/src/server/scripts/Commands/cs_reload.cpp
index 56b0dbf43d0..eb28a8adae4 100644
--- a/src/server/scripts/Commands/cs_reload.cpp
+++ b/src/server/scripts/Commands/cs_reload.cpp
@@ -251,7 +251,7 @@ public:
static bool HandleReloadAllScriptsCommand(ChatHandler* handler, const char* /*args*/)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->PSendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -393,7 +393,7 @@ public:
static bool HandleReloadCommandCommand(ChatHandler* handler, const char* /*args*/)
{
- handler->SetLoadCommandTable(true);
+ ChatHandler::invalidateCommandTable();
handler->SendGlobalGMSysMessage("DB table `command` will be reloaded at next chat command use.");
return true;
}
@@ -893,7 +893,7 @@ public:
static bool HandleReloadEventScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -913,7 +913,7 @@ public:
static bool HandleReloadWpScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
@@ -946,7 +946,7 @@ public:
static bool HandleReloadSpellScriptsCommand(ChatHandler* handler, const char* args)
{
- if (sScriptMgr->IsScriptScheduled())
+ if (sMapMgr->IsScriptScheduled())
{
handler->SendSysMessage("DB scripts used currently, please attempt reload later.");
handler->SetSentErrorMessage(true);
diff --git a/src/server/scripts/Commands/cs_reset.cpp b/src/server/scripts/Commands/cs_reset.cpp
index 05941120423..ba1dab28350 100644
--- a/src/server/scripts/Commands/cs_reset.cpp
+++ b/src/server/scripts/Commands/cs_reset.cpp
@@ -102,7 +102,7 @@ public:
player->setFactionForRace(player->getRace());
- player->SetUInt32Value(UNIT_FIELD_BYTES_0, ((player->getRace()) | (player->getClass() << 8) | (player->getGender() << 16) | (powerType << 24)));
+ player->SetByteValue(UNIT_FIELD_BYTES_0, UNIT_BYTES_0_OFFSET_POWER_TYPE, powerType);
// reset only if player not in some form;
if (player->GetShapeshiftForm() == FORM_NONE)
diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp
index 51bb314968d..ffec32c0619 100644
--- a/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp
+++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/MoltenCore/boss_baron_geddon.cpp
@@ -27,6 +27,8 @@ EndScriptData */
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "molten_core.h"
+#include "SpellAuraEffects.h"
+#include "SpellScript.h"
enum Emotes
{
@@ -36,9 +38,10 @@ enum Emotes
enum Spells
{
SPELL_INFERNO = 19695,
+ SPELL_INFERNO_DMG = 19698,
SPELL_IGNITE_MANA = 19659,
SPELL_LIVING_BOMB = 20475,
- SPELL_ARMAGEDDON = 20479,
+ SPELL_ARMAGEDDON = 20478,
};
enum Events
@@ -119,7 +122,36 @@ class boss_baron_geddon : public CreatureScript
}
};
+class spell_baron_geddon_inferno : public SpellScriptLoader
+{
+ public:
+ spell_baron_geddon_inferno() : SpellScriptLoader("spell_baron_geddon_inferno") { }
+
+ class spell_baron_geddon_inferno_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_baron_geddon_inferno_AuraScript);
+
+ void OnPeriodic(AuraEffect const* aurEff)
+ {
+ PreventDefaultAction();
+ int32 damageForTick[8] = { 500, 500, 1000, 1000, 2000, 2000, 3000, 5000 };
+ GetTarget()->CastCustomSpell(SPELL_INFERNO_DMG, SPELLVALUE_BASE_POINT0, damageForTick[aurEff->GetTickNumber() - 1], (Unit*)nullptr, TRIGGERED_FULL_MASK, nullptr, aurEff);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_baron_geddon_inferno_AuraScript::OnPeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_baron_geddon_inferno_AuraScript();
+ }
+};
+
void AddSC_boss_baron_geddon()
{
new boss_baron_geddon();
+ new spell_baron_geddon_inferno();
}
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp
index 7d7c60ac419..6ff20e66f7f 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_nightbane.cpp
@@ -138,7 +138,7 @@ public:
{
Initialize();
- me->SetSpeed(MOVE_RUN, 2.0f);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
me->SetDisableGravity(true);
me->SetWalk(false);
me->setActive(true);
diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
index 4aef3c8d4a3..ace15cc2a7e 100644
--- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
+++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.cpp
@@ -562,7 +562,7 @@ public:
arca->GetMotionMaster()->MovePoint(0, -11010.82f, -1761.18f, 156.47f);
arca->setActive(true);
arca->InterruptNonMeleeSpells(true);
- arca->SetSpeed(MOVE_FLIGHT, 2.0f);
+ arca->SetSpeedRate(MOVE_FLIGHT, 2.0f);
}
return 10000;
case 13:
diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
index b809128e731..ebe406c8909 100644
--- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
+++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_felblood_kaelthas.cpp
@@ -405,7 +405,7 @@ public:
Creature* Orb = DoSpawnCreature(CREATURE_ARCANE_SPHERE, 5, 5, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
if (Orb && target)
{
- Orb->SetSpeed(MOVE_RUN, 0.5f);
+ Orb->SetSpeedRate(MOVE_RUN, 0.5f);
Orb->AddThreat(target, 1000000.0f);
Orb->AI()->AttackStart(target);
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
index 81044a0dbb1..3cf78b79f67 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter1.cpp
@@ -396,7 +396,7 @@ class npc_eye_of_acherus : public CreatureScript
if (Player* owner = me->GetCharmerOrOwner()->ToPlayer())
{
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
- me->SetSpeed(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)), true);
+ me->SetSpeedRate(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)));
Talk(TALK_MOVE_START, owner);
}
me->GetMotionMaster()->MovePath(me->GetEntry() * 100, false);
@@ -419,7 +419,7 @@ class npc_eye_of_acherus : public CreatureScript
{
owner->RemoveAura(SPELL_EYE_FLIGHT_BOOST);
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
- me->SetSpeed(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)), true);
+ me->SetSpeedRate(UnitMoveType(i), owner->GetSpeedRate(UnitMoveType(i)));
Talk(TALK_CONTROL, owner);
}
@@ -703,7 +703,7 @@ class npc_dark_rider_of_acherus : public CreatureScript
TargetGUID = who->GetGUID();
me->SetWalk(true);
- me->SetSpeed(MOVE_RUN, 0.4f);
+ me->SetSpeedRate(MOVE_RUN, 0.4f);
me->GetMotionMaster()->MoveChase(who);
me->SetTarget(TargetGUID);
Intro = true;
@@ -1050,7 +1050,7 @@ class npc_scarlet_miner_cart : public CreatureScript
// Not 100% correct, but movement is smooth. Sometimes miner walks faster
// than normal, this speed is fast enough to keep up at those times.
- me->SetSpeed(MOVE_RUN, 1.25f);
+ me->SetSpeedRate(MOVE_RUN, 1.25f);
me->GetMotionMaster()->MoveFollow(miner, 1.0f, 0);
}
diff --git a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
index e336ff24382..d25a225717a 100644
--- a/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletEnclave/chapter5.cpp
@@ -923,7 +923,7 @@ public:
if (ObjectAccessor::GetCreature(*me, uiLichKingGUID))
DoCast(me, SPELL_MOGRAINE_CHARGE); // jumping charge
// doesn't make it looks well, so workarounds, Darion charges, looks better
- me->SetSpeed(MOVE_RUN, 3.0f);
+ me->SetSpeedRate(MOVE_RUN, 3.0f);
me->SetWalk(false);
SetHoldState(false);
JumpToNextStep(0);
@@ -935,7 +935,7 @@ public:
temp->HandleEmoteCommand(EMOTE_ONESHOT_KICK);
temp->AI()->Talk(SAY_LIGHT_OF_DAWN46);
}
- me->SetSpeed(MOVE_RUN, 6.0f);
+ me->SetSpeedRate(MOVE_RUN, 6.0f);
me->SetStandState(UNIT_STAND_STATE_DEAD);
SetHoldState(false); // Darion got kicked by lich king
JumpToNextStep(0);
@@ -997,7 +997,7 @@ public:
Unit* temp = me->SummonCreature(NPC_DEFENDER_OF_THE_LIGHT, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->setFaction(me->getFaction());
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
uiDefenderGUID[0] = temp->GetGUID();
@@ -1005,7 +1005,7 @@ public:
temp = me->SummonCreature(NPC_RIMBLAT_EARTHSHATTER, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }), TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->setFaction(me->getFaction());
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
uiEarthshatterGUID[0] = temp->GetGUID();
@@ -1014,7 +1014,7 @@ public:
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
temp->AI()->Talk(SAY_LIGHT_OF_DAWN50);
}
@@ -1022,7 +1022,7 @@ public:
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->HandleEmoteCommand(EMOTE_STATE_ATTACK_UNARMED);
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
}
@@ -1030,7 +1030,7 @@ public:
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
temp->SetWalk(false);
- temp->SetSpeed(MOVE_RUN, 2.0f);
+ temp->SetSpeedRate(MOVE_RUN, 2.0f);
temp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
}
}
@@ -1044,33 +1044,33 @@ public:
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiMaxwellGUID))
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[14]);
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiKorfaxGUID))
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[11]);
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiEligorGUID))
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[17]);
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiDefenderGUID[0]))
{
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }));
}
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiEarthshatterGUID[0]))
{
- temp->SetSpeed(MOVE_RUN, 6.0f);
+ temp->SetSpeedRate(MOVE_RUN, 6.0f);
temp->SetStandState(UNIT_STAND_STATE_DEAD);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].GetPositionWithOffset({ float(rand32() % 10), float(rand32() % 10), 0.0f, 0.0f }));
}
@@ -1093,7 +1093,7 @@ public:
break;
case 46: // Darion stand up, "not today"
- me->SetSpeed(MOVE_RUN, 1.0f);
+ me->SetSpeedRate(MOVE_RUN, 1.0f);
me->SetWalk(true);
me->SetStandState(UNIT_STAND_STATE_STAND);
Talk(SAY_LIGHT_OF_DAWN53);
@@ -1153,7 +1153,7 @@ public:
temp->AI()->Talk(EMOTE_LIGHT_OF_DAWN16);
temp->CastSpell(temp, SPELL_TIRION_CHARGE, false); // jumping charge
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_READY2H);
- temp->SetSpeed(MOVE_RUN, 3.0f); // workarounds, make Tirion still running
+ temp->SetSpeedRate(MOVE_RUN, 3.0f); // workarounds, make Tirion still running
temp->SetWalk(false);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[2]);
if (Creature* lktemp = ObjectAccessor::GetCreature(*me, uiLichKingGUID))
@@ -1171,7 +1171,7 @@ public:
case 54:
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiLichKingGUID))
{
- temp->SetSpeed(MOVE_RUN, 1.0f);
+ temp->SetSpeedRate(MOVE_RUN, 1.0f);
me->SetWalk(true);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[29]); // 26
}
@@ -1208,7 +1208,7 @@ public:
if (Creature* temp = ObjectAccessor::GetCreature(*me, uiTirionGUID)) // Tirion runs to Darion
{
temp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
- temp->SetSpeed(MOVE_RUN, 1.0f);
+ temp->SetSpeedRate(MOVE_RUN, 1.0f);
temp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[6]);
}
JumpToNextStep(2500);
diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
index 74b300919d8..f7a1c18c234 100644
--- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
+++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_headless_horseman.cpp
@@ -450,7 +450,7 @@ public:
me->SetVisible(false);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->SetDisableGravity(true);
- me->SetSpeed(MOVE_WALK, 5.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 5.0f);
wp_reached = false;
count = 0;
say_timer = 3000;
diff --git a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp
index d9b481d7b6d..fded249f9ca 100644
--- a/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp
+++ b/src/server/scripts/EasternKingdoms/SunwellPlateau/boss_felmyst.cpp
@@ -249,7 +249,7 @@ public:
me->CastStop(SPELL_FOG_BREATH);
me->RemoveAurasDueToSpell(SPELL_FOG_BREATH);
me->StopMoving();
- me->SetSpeed(MOVE_RUN, 2.0f);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
events.ScheduleEvent(EVENT_CLEAVE, urand(5000, 10000));
events.ScheduleEvent(EVENT_CORROSION, urand(10000, 20000));
@@ -530,7 +530,7 @@ public:
npc_felmyst_vaporAI(Creature* creature) : ScriptedAI(creature)
{
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- me->SetSpeed(MOVE_RUN, 0.8f);
+ me->SetSpeedRate(MOVE_RUN, 0.8f);
}
void Reset() override { }
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
index 791f68bae31..72e76ba52bf 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_akilzon.cpp
@@ -425,7 +425,7 @@ class npc_akilzon_eagle : public CreatureScript
if (Unit* target = ObjectAccessor::GetUnit(*me, TargetGUID))
DoCast(target, SPELL_EAGLE_SWOOP, true);
TargetGUID.Clear();
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
EagleSwoop_Timer = urand(5000, 10000);
}
}
@@ -454,7 +454,7 @@ class npc_akilzon_eagle : public CreatureScript
{
target->GetContactPoint(me, x, y, z);
z += 2;
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
TargetGUID = target->GetGUID();
}
me->GetMotionMaster()->MovePoint(0, x, y, z);
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp
index 0407cb6cd7c..188f9d0cc03 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_nalorakk.cpp
@@ -142,7 +142,7 @@ class boss_nalorakk : public CreatureScript
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
inMove = false;
waitTimer = 0;
- me->SetSpeed(MOVE_RUN, 2);
+ me->SetSpeedRate(MOVE_RUN, 2);
me->SetWalk(false);
}else
{
diff --git a/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp b/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
index 96c0798ae05..a1c2369cf66 100644
--- a/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
+++ b/src/server/scripts/EasternKingdoms/ZulAman/boss_zuljin.cpp
@@ -347,7 +347,7 @@ class boss_zuljin : public CreatureScript
Vortex->CastSpell(Vortex, SPELL_CYCLONE_PASSIVE, true);
Vortex->CastSpell(Vortex, SPELL_CYCLONE_VISUAL, true);
Vortex->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
- Vortex->SetSpeed(MOVE_RUN, 1.0f);
+ Vortex->SetSpeedRate(MOVE_RUN, 1.0f);
Vortex->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM, 0));
DoZoneInCombat(Vortex);
}
@@ -438,7 +438,7 @@ class boss_zuljin : public CreatureScript
{
if (me->GetVictim())
TankGUID = me->EnsureVictim()->GetGUID();
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
AttackStart(target); // change victim
Claw_Rage_Timer = 0;
Claw_Loop_Timer = 500;
@@ -462,7 +462,7 @@ class boss_zuljin : public CreatureScript
if (Claw_Counter == 12)
{
Claw_Rage_Timer = urand(15000, 20000);
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
AttackStart(ObjectAccessor::GetUnit(*me, TankGUID));
TankGUID.Clear();
return;
@@ -487,7 +487,7 @@ class boss_zuljin : public CreatureScript
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0))
{
TankGUID = me->EnsureVictim()->GetGUID();
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
AttackStart(target); // change victim
Lynx_Rush_Timer = 0;
Claw_Counter = 0;
@@ -510,7 +510,7 @@ class boss_zuljin : public CreatureScript
if (Claw_Counter == 9)
{
Lynx_Rush_Timer = urand(15000, 20000);
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
AttackStart(ObjectAccessor::GetUnit(*me, TankGUID));
TankGUID.Clear();
}
diff --git a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
index 8c781bb9001..b794a653791 100644
--- a/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
+++ b/src/server/scripts/EasternKingdoms/eastern_kingdoms_script_loader.cpp
@@ -89,7 +89,6 @@ void AddSC_boss_sulfuron();
void AddSC_boss_majordomo();
void AddSC_boss_ragnaros();
void AddSC_instance_molten_core();
-void AddSC_instance_ragefire_chasm(); //Ragefire Chasm
void AddSC_the_scarlet_enclave(); //Scarlet Enclave
void AddSC_the_scarlet_enclave_c1();
void AddSC_the_scarlet_enclave_c2();
@@ -268,7 +267,6 @@ void AddEasternKingdomsScripts()
AddSC_boss_majordomo();
AddSC_boss_ragnaros();
AddSC_instance_molten_core();
- AddSC_instance_ragefire_chasm(); //Ragefire Chasm
AddSC_the_scarlet_enclave(); //Scarlet Enclave
AddSC_the_scarlet_enclave_c1();
AddSC_the_scarlet_enclave_c2();
diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp
index 44cbdec2cb5..0526dcfa630 100644
--- a/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp
+++ b/src/server/scripts/Kalimdor/CavernsOfTime/EscapeFromDurnholdeKeep/old_hillsbrad.cpp
@@ -479,12 +479,12 @@ public:
void DoMount()
{
me->Mount(SKARLOC_MOUNT_MODEL);
- me->SetSpeed(MOVE_RUN, SPEED_MOUNT);
+ me->SetSpeedRate(MOVE_RUN, SPEED_MOUNT);
}
void DoUnmount()
{
me->Dismount();
- me->SetSpeed(MOVE_RUN, SPEED_RUN);
+ me->SetSpeedRate(MOVE_RUN, SPEED_RUN);
}
void EnterCombat(Unit* /*who*/) override
{
diff --git a/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp b/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp
index 7da15b1fdce..71ebe870e3d 100644
--- a/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp
+++ b/src/server/scripts/Kalimdor/OnyxiasLair/boss_onyxia.cpp
@@ -205,7 +205,7 @@ public:
PointData = GetMoveData();
MovePoint = PointData->LocIdEnd;
- me->SetSpeed(MOVE_FLIGHT, 1.5f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.5f);
me->GetMotionMaster()->MovePoint(8, MiddleRoomLocation);
}
}
@@ -220,7 +220,7 @@ public:
PointData = GetMoveData();
if (PointData)
{
- me->SetSpeed(MOVE_FLIGHT, 1.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
me->GetMotionMaster()->MovePoint(PointData->LocId, PointData->fX, PointData->fY, PointData->fZ);
}
break;
@@ -250,7 +250,7 @@ public:
if (Creature * trigger = me->SummonCreature(NPC_TRIGGER, MiddleRoomLocation, TEMPSUMMON_CORPSE_DESPAWN))
triggerGUID = trigger->GetGUID();
me->GetMotionMaster()->MoveTakeoff(11, Phase2Floating);
- me->SetSpeed(MOVE_FLIGHT, 1.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
Talk(SAY_PHASE_2_TRANS);
instance->SetData(DATA_ONYXIA_PHASE, Phase);
events.ScheduleEvent(EVENT_WHELP_SPAWN, 5000);
diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
index f1969a063d6..87edf4781d4 100644
--- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
+++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp
@@ -47,6 +47,7 @@ void AddSC_boss_meathook();
void AddSC_culling_of_stratholme();
void AddSC_instance_culling_of_stratholme();
void AddSC_instance_dire_maul(); //Dire Maul
+void AddSC_instance_ragefire_chasm(); //Ragefire Chasm
void AddSC_boss_celebras_the_cursed(); //Maraudon
void AddSC_boss_landslide();
void AddSC_boss_noxxion();
@@ -143,6 +144,7 @@ void AddKalimdorScripts()
AddSC_culling_of_stratholme();
AddSC_instance_culling_of_stratholme();
AddSC_instance_dire_maul(); //Dire Maul
+ AddSC_instance_ragefire_chasm(); //Ragefire Chasm
AddSC_boss_celebras_the_cursed(); //Maraudon
AddSC_boss_landslide();
AddSC_boss_noxxion();
diff --git a/src/server/scripts/Kalimdor/zone_azshara.cpp b/src/server/scripts/Kalimdor/zone_azshara.cpp
index 1ed95c16a0d..4847ac46542 100644
--- a/src/server/scripts/Kalimdor/zone_azshara.cpp
+++ b/src/server/scripts/Kalimdor/zone_azshara.cpp
@@ -404,7 +404,7 @@ public:
DoCast(me, SPELL_PERIODIC_DEPTH_CHARGE);
me->SetHover(true);
me->SetSwim(true);
- me->SetSpeed(MOVE_RUN, 0.85f, true);
+ me->SetSpeedRate(MOVE_RUN, 0.85f);
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MovePoint(CurrWP, WPs[CurrWP]);
Escape = true;
diff --git a/src/server/scripts/Kalimdor/zone_desolace.cpp b/src/server/scripts/Kalimdor/zone_desolace.cpp
index 6880d8ee38f..3f4a905147f 100644
--- a/src/server/scripts/Kalimdor/zone_desolace.cpp
+++ b/src/server/scripts/Kalimdor/zone_desolace.cpp
@@ -89,7 +89,7 @@ public:
me->UpdateEntry(NPC_TAMED_KODO);
me->CombatStop();
me->DeleteThreatList();
- me->SetSpeed(MOVE_RUN, 0.6f, true);
+ me->SetSpeedRate(MOVE_RUN, 0.6f);
me->GetMotionMaster()->MoveFollow(caster, PET_FOLLOW_DIST, me->GetFollowAngle());
me->setActive(true);
}
diff --git a/src/server/scripts/Kalimdor/zone_durotar.cpp b/src/server/scripts/Kalimdor/zone_durotar.cpp
index 05caf9cca3a..62a041e7798 100644
--- a/src/server/scripts/Kalimdor/zone_durotar.cpp
+++ b/src/server/scripts/Kalimdor/zone_durotar.cpp
@@ -483,8 +483,8 @@ class spell_mount_check : public SpellScriptLoader
else if (!owner->IsMounted() && target->IsMounted())
target->Dismount();
- target->SetSpeed(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN));
- target->SetSpeed(MOVE_WALK, owner->GetSpeedRate(MOVE_WALK));
+ target->SetSpeedRate(MOVE_RUN, owner->GetSpeedRate(MOVE_RUN));
+ target->SetSpeedRate(MOVE_WALK, owner->GetSpeedRate(MOVE_WALK));
}
void Register() override
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
index ab09dd45710..3c11fd5d9a6 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_jedoga_shadowseeker.cpp
@@ -454,11 +454,11 @@ public:
float distance = me->GetDistance(JedogaPosition[1]);
if (distance < 9.0f)
- me->SetSpeed(MOVE_WALK, 0.5f, true);
+ me->SetSpeedRate(MOVE_WALK, 0.5f);
else if (distance < 15.0f)
- me->SetSpeed(MOVE_WALK, 0.75f, true);
+ me->SetSpeedRate(MOVE_WALK, 0.75f);
else if (distance < 20.0f)
- me->SetSpeed(MOVE_WALK, 1.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 1.0f);
me->GetMotionMaster()->Clear(false);
me->GetMotionMaster()->MovePoint(1, JedogaPosition[1]);
diff --git a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
index e25f64f61aa..04b62f77e9a 100644
--- a/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
+++ b/src/server/scripts/Northrend/AzjolNerub/Ahnkahet/boss_prince_taldaram.cpp
@@ -178,7 +178,7 @@ class boss_prince_taldaram : public CreatureScript
if (Unit* embraceTarget = GetEmbraceTarget())
{
me->GetMotionMaster()->Clear();
- me->SetSpeed(MOVE_WALK, 2.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 2.0f);
me->GetMotionMaster()->MoveChase(embraceTarget);
}
events.ScheduleEvent(EVENT_VANISHED, 1300);
@@ -188,7 +188,7 @@ class boss_prince_taldaram : public CreatureScript
DoCast(embraceTarget, SPELL_EMBRACE_OF_THE_VAMPYR);
Talk(SAY_FEED);
me->GetMotionMaster()->Clear();
- me->SetSpeed(MOVE_WALK, 1.0f, true);
+ me->SetSpeedRate(MOVE_WALK, 1.0f);
me->GetMotionMaster()->MoveChase(me->GetVictim());
events.ScheduleEvent(EVENT_FEEDING, 20000);
break;
diff --git a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
index c89510211b9..7440984d7c5 100644
--- a/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
+++ b/src/server/scripts/Northrend/CrusadersColiseum/TrialOfTheCrusader/boss_anubarak_trial.cpp
@@ -804,7 +804,7 @@ class npc_anubarak_spike : public CreatureScript
void StartChase(Unit* who)
{
DoCast(who, SPELL_MARK);
- me->SetSpeed(MOVE_RUN, 0.5f);
+ me->SetSpeedRate(MOVE_RUN, 0.5f);
// make sure the Spine will really follow the one he should
me->getThreatManager().clearReferences();
me->SetInCombatWithZone();
diff --git a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
index 4c2b92da0ea..3ac85809fa2 100644
--- a/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/HallsOfReflection/halls_of_reflection.cpp
@@ -2580,7 +2580,7 @@ class npc_quel_delar_sword : public CreatureScript
void Reset() override
{
_events.Reset();
- me->SetSpeed(MOVE_FLIGHT, 4.5f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 4.5f);
DoCast(SPELL_WHIRLWIND_VISUAL);
if (_intro)
_events.ScheduleEvent(EVENT_QUEL_DELAR_INIT, 0);
diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp
index 03f12e46bc3..b24a36da3fb 100644
--- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_krickandick.cpp
@@ -418,7 +418,7 @@ class boss_krick : public CreatureScript
case EVENT_OUTRO_6:
if (Creature* tyrannus = ObjectAccessor::GetCreature(*me, _instanceScript->GetGuidData(DATA_TYRANNUS_EVENT)))
{
- tyrannus->SetSpeed(MOVE_FLIGHT, 3.5f, true);
+ tyrannus->SetSpeedRate(MOVE_FLIGHT, 3.5f);
tyrannus->GetMotionMaster()->MovePoint(1, outroPos[4]);
_tyrannusGUID = tyrannus->GetGUID();
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
index 166e9739a95..51711f9ded5 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_blood_prince_council.cpp
@@ -1069,7 +1069,7 @@ class npc_blood_queen_lana_thel : public CreatureScript
if (Creature* summon = DoSummon(NPC_FLOATING_TRIGGER, triggerPos, 15000, TEMPSUMMON_TIMED_DESPAWN))
{
summon->CastSpell(summon, SPELL_OOC_INVOCATION_VISUAL, true);
- summon->SetSpeed(MOVE_FLIGHT, 0.15f, true); // todo: creature is swimming, check if this is blizzlike or not.
+ summon->SetSpeedRate(MOVE_FLIGHT, 0.15f); // todo: creature is swimming, check if this is blizzlike or not.
summon->GetMotionMaster()->MovePoint(0, triggerEndPos);
}
}
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
index 056231285e2..9f20799df82 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_lord_marrowgar.cpp
@@ -142,7 +142,7 @@ class boss_lord_marrowgar : public CreatureScript
void Reset() override
{
_Reset();
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
me->RemoveAurasDueToSpell(SPELL_BONE_STORM);
me->RemoveAurasDueToSpell(SPELL_BERSERK);
events.ScheduleEvent(EVENT_ENABLE_BONE_SLICE, 10000);
@@ -224,7 +224,7 @@ class boss_lord_marrowgar : public CreatureScript
case EVENT_BONE_STORM_BEGIN:
if (Aura* pStorm = me->GetAura(SPELL_BONE_STORM))
pStorm->SetDuration(int32(_boneStormDuration));
- me->SetSpeed(MOVE_RUN, _baseSpeed*3.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*3.0f);
Talk(SAY_BONE_STORM);
events.ScheduleEvent(EVENT_BONE_STORM_END, _boneStormDuration+1);
// no break here
@@ -242,7 +242,7 @@ class boss_lord_marrowgar : public CreatureScript
if (me->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
me->GetMotionMaster()->MovementExpired();
me->GetMotionMaster()->MoveChase(me->GetVictim());
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
events.CancelEvent(EVENT_BONE_STORM_MOVE);
events.ScheduleEvent(EVENT_ENABLE_BONE_SLICE, 10000);
if (!IsHeroic())
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
index 280a0aaa13a..fe6e4081326 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_professor_putricide.cpp
@@ -369,21 +369,21 @@ class boss_professor_putricide : public CreatureScript
{
case POINT_FESTERGUT:
instance->SetBossState(DATA_FESTERGUT, IN_PROGRESS); // needed here for delayed gate close
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
DoAction(ACTION_FESTERGUT_GAS);
if (Creature* festergut = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FESTERGUT)))
festergut->CastSpell(festergut, SPELL_GASEOUS_BLIGHT_LARGE, false, NULL, NULL, festergut->GetGUID());
break;
case POINT_ROTFACE:
instance->SetBossState(DATA_ROTFACE, IN_PROGRESS); // needed here for delayed gate close
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
DoAction(ACTION_ROTFACE_OOZE);
events.ScheduleEvent(EVENT_ROTFACE_OOZE_FLOOD, 25000, 0, PHASE_ROTFACE);
break;
case POINT_TABLE:
// stop attack
me->GetMotionMaster()->MoveIdle();
- me->SetSpeed(MOVE_RUN, _baseSpeed, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed);
if (GameObject* table = ObjectAccessor::GetGameObject(*me, instance->GetGuidData(DATA_PUTRICIDE_TABLE)))
me->SetFacingToObject(table);
// operating on new phase already
@@ -418,7 +418,7 @@ class boss_professor_putricide : public CreatureScript
{
case ACTION_FESTERGUT_COMBAT:
SetPhase(PHASE_FESTERGUT);
- me->SetSpeed(MOVE_RUN, _baseSpeed*2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*2.0f);
me->GetMotionMaster()->MovePoint(POINT_FESTERGUT, festergutWatchPos);
me->SetReactState(REACT_PASSIVE);
DoZoneInCombat(me);
@@ -435,7 +435,7 @@ class boss_professor_putricide : public CreatureScript
case ACTION_ROTFACE_COMBAT:
{
SetPhase(PHASE_ROTFACE);
- me->SetSpeed(MOVE_RUN, _baseSpeed*2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*2.0f);
me->GetMotionMaster()->MovePoint(POINT_ROTFACE, rotfaceWatchPos);
me->SetReactState(REACT_PASSIVE);
_oozeFloodStage = 0;
@@ -477,7 +477,7 @@ class boss_professor_putricide : public CreatureScript
events.ScheduleEvent(EVENT_ROTFACE_DIES, 4500, 0, PHASE_ROTFACE);
break;
case ACTION_CHANGE_PHASE:
- me->SetSpeed(MOVE_RUN, _baseSpeed*2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, _baseSpeed*2.0f);
events.DelayEvents(30000);
me->AttackStop();
if (!IsHeroic())
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
index 0b129f3aa89..caa37465165 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_sindragosa.cpp
@@ -315,7 +315,7 @@ class boss_sindragosa : public CreatureScript
me->SetCanFly(true);
me->SetDisableGravity(true);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER);
- me->SetSpeed(MOVE_FLIGHT, 4.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 4.0f);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
float moveTime = me->GetExactDist(&SindragosaFlyPos) / (me->GetSpeed(MOVE_FLIGHT) * 0.001f);
me->m_Events.AddEvent(new FrostwyrmLandEvent(*me, SindragosaLandPos), me->m_Events.CalculateTime(uint64(moveTime) + 250));
@@ -351,7 +351,7 @@ class boss_sindragosa : public CreatureScript
me->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER);
me->SetHomePosition(SindragosaLandPos);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- me->SetSpeed(MOVE_FLIGHT, 2.5f);
+ me->SetSpeedRate(MOVE_FLIGHT, 2.5f);
// Sindragosa enters combat as soon as she lands
DoZoneInCombat();
@@ -697,7 +697,7 @@ class npc_spinestalker : public CreatureScript
return;
me->setActive(true);
- me->SetSpeed(MOVE_FLIGHT, 2.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 2.0f);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
float moveTime = me->GetExactDist(&SpinestalkerFlyPos) / (me->GetSpeed(MOVE_FLIGHT) * 0.001f);
me->m_Events.AddEvent(new FrostwyrmLandEvent(*me, SpinestalkerLandPos), me->m_Events.CalculateTime(uint64(moveTime) + 250));
@@ -834,7 +834,7 @@ class npc_rimefang : public CreatureScript
return;
me->setActive(true);
- me->SetSpeed(MOVE_FLIGHT, 2.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 2.0f);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
float moveTime = me->GetExactDist(&RimefangFlyPos) / (me->GetSpeed(MOVE_FLIGHT) * 0.001f);
me->m_Events.AddEvent(new FrostwyrmLandEvent(*me, RimefangLandPos), me->m_Events.CalculateTime(uint64(moveTime) + 250));
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
index 4675989228a..4a0a8217af8 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_the_lich_king.cpp
@@ -763,7 +763,7 @@ class boss_the_lich_king : public CreatureScript
{
summons.Summon(summon);
summon->SetReactState(REACT_PASSIVE);
- summon->SetSpeed(MOVE_FLIGHT, 0.5f);
+ summon->SetSpeedRate(MOVE_FLIGHT, 0.5f);
summon->GetMotionMaster()->MoveRandom(10.0f);
if (!events.IsInPhase(PHASE_FROSTMOURNE))
summon->m_Events.AddEvent(new VileSpiritActivateEvent(summon), summon->m_Events.CalculateTime(15000));
@@ -1448,7 +1448,7 @@ class npc_valkyr_shadowguard : public CreatureScript
_events.Reset();
me->SetReactState(REACT_PASSIVE);
DoCast(me, SPELL_WINGS_OF_THE_DAMNED, false);
- me->SetSpeed(MOVE_FLIGHT, 0.642857f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.642857f);
}
void IsSummonedBy(Unit* /*summoner*/) override
diff --git a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
index 412615afad4..1e2eb06a2d5 100644
--- a/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
+++ b/src/server/scripts/Northrend/Naxxramas/boss_gluth.cpp
@@ -74,9 +74,14 @@ enum States
STATE_ZOMBIE_TOBE_EATEN = 3
};
+enum SummonGroups
+{
+ SUMMON_GROUP_CHOW_10MAN = 1,
+ SUMMON_GROUP_CHOW_25MAN = 2
+};
+
enum Misc
{
- NPC_ZOMBIE_CHOW = 16360,
EVENT_GLUTH_ZOMBIE_BEHAVIOR = 10495, // unused. event handled by spell_gluth_decimate_SpellScript::HandleEvent
DATA_ZOMBIE_STATE = 1,
ACTION_DECIMATE_EVENT = 2,
@@ -103,7 +108,7 @@ public:
zombieToBeEatenGUID.Clear();
state = STATE_GLUTH_NORMAL;
me->SetReactState(REACT_AGGRESSIVE);
- me->SetSpeed(UnitMoveType::MOVE_RUN, 12.0f / baseMoveSpeed[MOVE_RUN]);
+ me->SetSpeed(UnitMoveType::MOVE_RUN, 12.0f);
}
void EnterCombat(Unit* /*who*/) override
@@ -177,12 +182,9 @@ public:
break;
case EVENT_SUMMON:
if (Is25ManRaid()) // one wave each 10s. one wave=1 zombie in 10man and 2 zombies in 25man.
- {
- DoSummon(NPC_ZOMBIE_CHOW, PosSummon[1]);
- DoSummon(NPC_ZOMBIE_CHOW, PosSummon[2]);
- }
+ me->SummonCreatureGroup(SUMMON_GROUP_CHOW_25MAN);
else
- DoSummon(NPC_ZOMBIE_CHOW, PosSummon[0]);
+ me->SummonCreatureGroup(SUMMON_GROUP_CHOW_10MAN);
events.Repeat(Seconds(10));
break;
case EVENT_SEARCH_ZOMBIE_SINGLE:
@@ -191,19 +193,19 @@ public:
for (SummonList::const_iterator itr = summons.begin(); !zombie && itr != summons.end(); ++itr)
{
zombie=ObjectAccessor::GetCreature(*me, *itr);
- if (zombie == nullptr || !zombie->IsAlive() || !zombie->IsWithinDistInMap(me, 10.0))
+ if (!zombie || !zombie->IsAlive() || !zombie->IsWithinDistInMap(me, 10.0))
zombie = nullptr;
}
if (zombie)
{
- zombieToBeEatenGUID = zombie->GetGUID(); // save for later use// the soon-to-be-eaten zombie should stop moving and stop attacking
+ zombieToBeEatenGUID = zombie->GetGUID(); // save for later use
// the soon-to-be-eaten zombie should stop moving and stop attacking
zombie->AI()->SetData(DATA_ZOMBIE_STATE, STATE_ZOMBIE_TOBE_EATEN);
// gluth should stop AAs on his primary target and turn toward the zombie (2 yards away). He then pauses for a few seconds.
- me->SetSpeed(MOVE_RUN, 36.0f / baseMoveSpeed[MOVE_RUN]);
+ me->SetSpeed(MOVE_RUN, 36.0f);
me->SetReactState(ReactStates::REACT_PASSIVE);
me->AttackStop();
@@ -228,7 +230,7 @@ public:
zombieToBeEatenGUID = ObjectGuid::Empty;
state = STATE_GLUTH_NORMAL;
- me->SetSpeed(UnitMoveType::MOVE_RUN, 12.0f / baseMoveSpeed[MOVE_RUN]);
+ me->SetSpeed(UnitMoveType::MOVE_RUN, 12.0f);
// and then return on primary target
me->SetReactState(REACT_AGGRESSIVE);
@@ -321,10 +323,7 @@ public:
bool Validate(SpellInfo const* /*spellInfo*/) override
{
- if (sSpellMgr->GetSpellInfo(SPELL_DECIMATE_DMG))
- return true;
- else
- return false;
+ return (sSpellMgr->GetSpellInfo(SPELL_DECIMATE_DMG) != nullptr);
}
void Register() override
@@ -411,7 +410,6 @@ public:
if (gluth && timer>1600 && me->GetExactDist2d(gluth) > 10.0 && me->CanFreeMove()) // it takes about 1600 ms for the animation to cycle. This way, the animation looks relatively smooth.
{
me->GetMotionMaster()->MovePoint(0, gluth->GetPosition()); // isn't dynamic. So, to take into account Gluth's movement, it must be called periodicly.
- //me->GetMotionMaster()->MoveFollow(gluth, 0, 0); // the zombies will stand still too far away from Gluth at the end of their movement. Also doesnt seem to work with SetWalk(true)
timer = 0;
}
@@ -435,7 +433,6 @@ public:
// at this point, the zombie should be non attacking and non moving.
me->SetWalk(true); // it doesnt seem to work with MoveFollow() (but it does work with MovePoint()).
- //me->SetSpeed(UnitMoveType::MOVE_RUN, baseMoveSpeed[UnitMoveType::MOVE_WALK] / baseMoveSpeed[UnitMoveType::MOVE_RUN]); // Hack to stay in run mode but with walk speed in case using MoveFollow()
timer = 1000;
}
diff --git a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
index 277ca793a8f..f000d7a3ef7 100644
--- a/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
+++ b/src/server/scripts/Northrend/Nexus/EyeOfEternity/boss_malygos.cpp
@@ -377,7 +377,7 @@ public:
me->SetDisableGravity(true);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND | UNIT_BYTE1_FLAG_HOVER);
// TO DO: find what in core is making boss slower than in retail (when correct speed data) or find missing movement flag update or forced spline change
- me->SetSpeed(MOVE_FLIGHT, _flySpeed * 0.25f);
+ me->SetSpeedRate(MOVE_FLIGHT, _flySpeed * 0.25f);
if (_despawned)
DoAction(ACTION_HANDLE_RESPAWN);
@@ -603,7 +603,7 @@ public:
me->SetRespawnDelay(respawnDelay);
// Set speed to normal value
- me->SetSpeed(MOVE_FLIGHT, _flySpeed);
+ me->SetSpeedRate(MOVE_FLIGHT, _flySpeed);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->RemoveAllAuras();
@@ -1165,7 +1165,7 @@ public:
_instance = creature->GetInstanceScript();
me->SetReactState(REACT_PASSIVE);
// TO DO: These were a bit faster than what they should be. Not sure what is the reason.
- me->SetSpeed(MOVE_FLIGHT, 1.25f);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.25f);
}
void Initialize()
@@ -1274,7 +1274,7 @@ public:
me->SetReactState(REACT_PASSIVE);
// TO DO: Something is wrong with calculations for flying creatures that are on WP/Cyclic path.
// They should get the same difference as to when at ground from run creature switch to walk.
- me->SetSpeed(MOVE_FLIGHT, 0.45f);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.45f);
}
void Reset() override
@@ -1566,7 +1566,7 @@ public:
{
me->DespawnOrUnsummon(2050);
me->SetOrientation(2.5f);
- me->SetSpeed(MOVE_FLIGHT, 1.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
Position pos = me->GetPosition();
pos.m_positionX += 10.0f;
pos.m_positionY += 10.0f;
diff --git a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
index e1a4a6ed7b8..3d9ea97b136 100644
--- a/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
+++ b/src/server/scripts/Northrend/Nexus/Oculus/oculus.cpp
@@ -375,7 +375,7 @@ class npc_ruby_emerald_amber_drake : public CreatureScript
{
me->DespawnOrUnsummon(2050);
me->SetOrientation(2.5f);
- me->SetSpeed(MOVE_FLIGHT, 1.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
Talk(SAY_DRAKES_TAKEOFF);
Position pos = me->GetPosition();
Position offset = { 10.0f, 10.0f, 12.0f, 0.0f };
diff --git a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
index a98da2c6e3d..8f7687d0fca 100644
--- a/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
+++ b/src/server/scripts/Northrend/Ulduar/HallsOfLightning/boss_ionar.cpp
@@ -174,7 +174,7 @@ public:
{
if (pSpark->IsAlive())
{
- pSpark->SetSpeed(MOVE_RUN, 2.0f);
+ pSpark->SetSpeedRate(MOVE_RUN, 2.0f);
pSpark->GetMotionMaster()->Clear();
pSpark->GetMotionMaster()->MovePoint(DATA_POINT_CALLBACK, pos);
}
@@ -355,7 +355,7 @@ public:
{
Position pos = ionar->GetPosition();
- me->SetSpeed(MOVE_RUN, 2.0f);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(DATA_POINT_CALLBACK, pos);
}
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
index 0714f2426bc..8aa443cba3f 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_mimiron.cpp
@@ -703,7 +703,7 @@ class boss_leviathan_mk_ii : public CreatureScript
if (Unit* turret = me->GetVehicleKit()->GetPassenger(3))
turret->KillSelf();
- me->SetSpeed(MOVE_RUN, 1.5f, true);
+ me->SetSpeedRate(MOVE_RUN, 1.5f);
me->GetMotionMaster()->MovePoint(WP_MKII_P1_IDLE, VehicleRelocation[WP_MKII_P1_IDLE]);
}
else if (events.IsInPhase(PHASE_VOL7RON))
diff --git a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
index f5337b2dca5..363f5907048 100644
--- a/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
+++ b/src/server/scripts/Northrend/Ulduar/Ulduar/boss_razorscale.cpp
@@ -366,7 +366,7 @@ class boss_razorscale : public CreatureScript
_EnterCombat();
if (Creature* controller = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_RAZORSCALE_CONTROL)))
controller->AI()->DoAction(ACTION_HARPOON_BUILD);
- me->SetSpeed(MOVE_FLIGHT, 3.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 3.0f);
me->SetReactState(REACT_PASSIVE);
phase = PHASE_GROUND;
events.SetPhase(PHASE_GROUND);
@@ -550,7 +550,7 @@ class boss_razorscale : public CreatureScript
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED | UNIT_FLAG_PACIFIED);
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveAurasDueToSpell(SPELL_HARPOON_TRIGGER);
- me->SetSpeed(MOVE_FLIGHT, 1.0f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 1.0f);
PermaGround = true;
DoCastAOE(SPELL_FLAMEBREATH);
events.ScheduleEvent(EVENT_FLAME, 15000, 0, PHASE_PERMAGROUND);
@@ -677,7 +677,7 @@ class npc_expedition_commander : public CreatureScript
if (Creature* summonedEngineer = me->SummonCreature(NPC_ENGINEER, PosEngSpawn, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000))
{
summonedEngineer->SetWalk(false);
- summonedEngineer->SetSpeed(MOVE_RUN, 0.5f);
+ summonedEngineer->SetSpeedRate(MOVE_RUN, 0.5f);
summonedEngineer->SetHomePosition(PosEngRepair[n]);
summonedEngineer->GetMotionMaster()->MoveTargetedHome();
Engineer[n] = summonedEngineer->GetGUID();
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
index 1af45f3031a..ee71c696d7c 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_palehoof.cpp
@@ -746,7 +746,7 @@ public:
//! HACK: Creature's can't have MOVEMENTFLAG_FLYING
me->AddUnitMovementFlag(MOVEMENTFLAG_FLYING);
me->RemoveAurasDueToSpell(SPELL_ORB_VISUAL);
- me->SetSpeed(MOVE_FLIGHT, 0.5f);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.5f);
}
void UpdateAI(uint32 diff) override
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp
index d7b65093898..7615217a794 100644
--- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp
+++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_skadi.cpp
@@ -209,7 +209,7 @@ public:
Initialize();
Summons.DespawnAll();
- me->SetSpeed(MOVE_FLIGHT, 3.0f);
+ me->SetSpeedRate(MOVE_FLIGHT, 3.0f);
if ((ObjectAccessor::GetCreature(*me, m_uiGraufGUID) == NULL) && !me->IsMounted())
me->SummonCreature(NPC_GRAUF, Location[0].GetPositionX(), Location[0].GetPositionY(), Location[0].GetPositionZ(), 3.0f);
instance->SetBossState(DATA_SKADI_THE_RUTHLESS, NOT_STARTED);
diff --git a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
index 7902c585509..3c651e10a1e 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_illidan.cpp
@@ -745,7 +745,7 @@ public:
if (!Trigger)
return;
- Trigger->SetSpeed(MOVE_WALK, 3);
+ Trigger->SetSpeedRate(MOVE_WALK, 3);
Trigger->SetWalk(true);
Trigger->GetMotionMaster()->MovePoint(0, final.x, final.y, final.z);
@@ -1524,7 +1524,7 @@ public:
void BeginWalk()
{
me->SetWalk(false);
- me->SetSpeed(MOVE_RUN, 1.0f);
+ me->SetSpeedRate(MOVE_RUN, 1.0f);
me->GetMotionMaster()->MovePoint(0, AkamaWP[WalkCount].x, AkamaWP[WalkCount].y, AkamaWP[WalkCount].z);
}
diff --git a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
index 7e1215488e1..1a9074c2464 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_shade_of_akama.cpp
@@ -238,8 +238,8 @@ public:
if (me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2) <= 3)
{
moveSpeed = (2.0f - (0.6f * me->GetAuraCount(SPELL_SHADE_SOUL_CHANNEL_2)));
- me->SetSpeed(MOVE_WALK, moveSpeed / 2.5f);
- me->SetSpeed(MOVE_RUN, (moveSpeed * 2) / 7);
+ me->SetSpeedRate(MOVE_WALK, moveSpeed / 2.5f);
+ me->SetSpeedRate(MOVE_RUN, (moveSpeed * 2) / 7);
me->ClearUnitState(UNIT_STATE_ROOT);
}
else
diff --git a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
index ae4d17bdad4..0999f1fcd6b 100644
--- a/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
+++ b/src/server/scripts/Outland/BlackTemple/boss_supremus.cpp
@@ -117,7 +117,7 @@ public:
DummyEntryCheckPredicate pred;
summons.DoAction(EVENT_VOLCANO, pred);
events.ScheduleEvent(EVENT_HATEFUL_STRIKE, 5000, GCD_CAST, PHASE_STRIKE);
- me->SetSpeed(MOVE_RUN, 1.2f);
+ me->SetSpeedRate(MOVE_RUN, 1.2f);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, false);
}
@@ -126,7 +126,7 @@ public:
phase = PHASE_CHASE;
events.ScheduleEvent(EVENT_VOLCANO, 5000, GCD_CAST, PHASE_CHASE);
events.ScheduleEvent(EVENT_SWITCH_TARGET, 10000, 0, PHASE_CHASE);
- me->SetSpeed(MOVE_RUN, 0.9f);
+ me->SetSpeedRate(MOVE_RUN, 0.9f);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_ATTACK_ME, true);
}
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
index e24499c3aee..3ddf0fec416 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_lady_vashj.cpp
@@ -585,8 +585,8 @@ public:
void Reset() override
{
- me->SetSpeed(MOVE_WALK, 0.6f); // walk
- me->SetSpeed(MOVE_RUN, 0.6f); // run
+ me->SetSpeedRate(MOVE_WALK, 0.6f); // walk
+ me->SetSpeedRate(MOVE_RUN, 0.6f); // run
Initialize();
//search for nearest waypoint (up on stairs)
diff --git a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp
index 8d117f7c3ef..f72b9a8d2ca 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp
+++ b/src/server/scripts/Outland/CoilfangReservoir/SerpentShrine/boss_leotheras_the_blind.cpp
@@ -244,7 +244,7 @@ public:
CheckChannelers();
Initialize();
me->SetCanDualWield(true);
- me->SetSpeed(MOVE_RUN, 2.0f, true);
+ me->SetSpeedRate(MOVE_RUN, 2.0f);
me->SetDisplayId(MODEL_NIGHTELF);
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID , 0);
me->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID+1, 0);
diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp
new file mode 100644
index 00000000000..9b53b62cdee
--- /dev/null
+++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/boss_ahune.cpp
@@ -0,0 +1,1051 @@
+/*
+ * 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 "CreatureTextMgr.h"
+#include "LFGMgr.h"
+#include "ScriptedGossip.h"
+#include "ScriptedCreature.h"
+#include "ScriptMgr.h"
+#include "SpellAuraEffects.h"
+#include "SpellScript.h"
+#include "the_slave_pens.h"
+
+enum Spells
+{
+ // Ahune
+ SPELL_SYNCH_HEALTH = 46430,
+ SPELL_AHUNES_SHIELD = 45954,
+ SPELL_STAY_SUBMERGED = 46981,
+ SPELL_AHUNE_SELF_STUN = 46416,
+ SPELL_AHUNE_ACHIEVEMENT = 62043,
+ SPELL_AHUNE_SPANKY_HANDS = 46146,
+ SPELL_COLD_SLAP = 46145,
+ SPELL_RESURFACE = 46402,
+ SPELL_SUBMERGED = 37751,
+ SPELL_STAND = 37752,
+
+ //Earther Ring Flamecaller
+ SPELL_FIND_OPENING_VISUAL = 45964,
+ SPELL_FIND_OPENING_BEAM_END = 46333,
+ SPELL_FIND_OPENING_TRIGGER = 46341,
+ SPELL_FIND_OPENING_CHANNEL = 46345,
+ SPELL_BONFIRE_VISUAL = 46339,
+ SPELL_FOUND_OPENING = 46421,
+
+ //Ahune Bunny
+ SPELL_SUMMON_COLDWEAVE = 46143,
+ SPELL_SUMMON_FROSTWIND = 46382,
+ SPELL_SUMMON_HAILSTONE = 46176,
+ SPELL_SUMMONING_VISUAL_1 = 45937,
+ SPELL_SUMMONING_RHYME_AURA = 45926,
+ SPELL_SUMMONING_RHYME_BONFIRE = 45930,
+ SPELL_FORCE_WHISP_FLIGHT = 46603,
+ SPELL_SHAMANS_LOOK_FOR_OPENING = 46422,
+ SPELL_CLOSE_OPENING_VISUAL = 46236,
+ SPELL_ICE_BOMBARD = 46397,
+ SPELL_ICE_BOMBARDMENT_DEST_PICKER = 46398,
+ SPELL_ICE_BOMBARDMENT = 46396,
+
+ // Ice Spear
+ SPELL_SUMMON_ICE_SPEAR_BUNNY = 46359,
+ SPELL_ICE_SPEAR_KNOCKBACK = 46360,
+ SPELL_SUMMON_ICE_SPEAR_GO = 46369,
+ SPELL_ICE_SPEAR_AURA = 46371,
+ SPELL_ICE_SPEAR_TARGET_PICKER = 46372,
+ SPELL_ICE_SPEAR_DELAY = 46878,
+ SPELL_ICE_SPEAR_VISUAL = 75498,
+
+ // Slippery Floor
+ SPELL_SLIPPERY_FLOOR_AMBIENT = 46314,
+ SPELL_SLIPPERY_FLOOR_PERIODIC = 46320,
+ SPELL_SLIPPERY_FLOOR_SLIP = 45947,
+
+ // Frozen Core
+ SPELL_SUICIDE = 45254,
+ SPELL_SUMMON_LOOT_MISSILE = 45941,
+ SPELL_FROZEN_CORE_GETS_HIT = 46810,
+ SPELL_MINION_DESPAWNER = 46843,
+ SPELL_GHOST_DISGUISE = 46786
+};
+
+enum Emotes
+{
+ EMOTE_EARTHEN_ASSAULT = 0,
+ EMOTE_RETREAT = 0,
+ EMOTE_RESURFACE = 1
+};
+
+enum Says
+{
+ SAY_PLAYER_TEXT_1 = 0,
+ SAY_PLAYER_TEXT_2 = 1,
+ SAY_PLAYER_TEXT_3 = 2
+};
+
+enum Events
+{
+ EVENT_EMERGE = 1,
+ EVENT_INITIAL_EMERGE = 2,
+ EVENT_SYNCH_HEALTH = 3,
+ EVENT_FOUND_OPENING = 4,
+ EVENT_LOOKFOROPENING_0 = 5,
+ EVENT_LOOKFOROPENING_1 = 6,
+ EVENT_LOOKFOROPENING_2 = 7,
+ EVENT_SUMMON_HAILSTONE = 8,
+ EVENT_SUMMON_COLDWEAVE = 9,
+ EVENT_SUMMON_FROSTWIND = 10,
+ EVENT_SUMMON_AHUNE = 11,
+ EVENT_CLOSE_OPENING = 12,
+ EVENT_AHUNE_PHASE_ONE = 13,
+ EVENT_AHUNE_PHASE_TWO = 14,
+ EVENT_START_LOOKING_FOR_OPENING = 15,
+ EVENT_STOP_LOOKING_FOR_OPENING = 16
+};
+
+enum Actions
+{
+ ACTION_START_EVENT = -2574500,
+ ACTION_AHUNE_RETREAT = -2586500,
+ ACTION_AHUNE_RESURFACE = -2586501,
+ ACTION_EMOTE_RESURFACE = -2575400
+};
+
+enum Phases
+{
+ PHASE_ONE = 0,
+ PHASE_TWO = 1
+};
+
+enum Points
+{
+ POINT_FLAMECALLER_000,
+ POINT_FLAMECALLER_001,
+ POINT_FLAMECALLER_002
+};
+
+enum Misc
+{
+ MAX_FLAMECALLERS = 3
+};
+
+Position const SummonPositions[] =
+{
+ { -99.1021f, -233.7526f, -1.22307f, 1.588250f }, // Ahune
+ { -98.0151f, -230.4555f, -1.21089f, 1.797689f }, // Frozen Core
+ { -143.172f, -147.6801f, -3.16113f, 4.852015f }, // Bonfire Bunny 000
+ { -134.304f, -145.7803f, -1.70332f, 4.677482f }, // Bonfire Bunny 001
+ { -125.036f, -144.2065f, -1.91660f, 4.991642f }, // Bonfire Bunny 002
+ { -69.8121f, -162.4954f, -2.30451f, 1.710423f }, // Wisp Source Bunny
+ { -98.1029f, -230.7864f, -10.8085f, 1.448623f } // Wisp Dest Bunny
+};
+
+Position const FlameCallerSpots[] =
+{
+ { -145.2233f, -137.5543f, -1.59056f, 5.427049f },
+ { -137.4383f, -136.4050f, -1.72384f, 5.336747f },
+ { -129.0413f, -132.1494f, -2.09285f, 5.460842f }
+};
+
+class boss_ahune : public CreatureScript
+{
+public:
+ boss_ahune() : CreatureScript("boss_ahune") { }
+
+ struct boss_ahuneAI : public BossAI
+ {
+ boss_ahuneAI(Creature* creature) : BossAI(creature, DATA_AHUNE)
+ {
+ Initialize();
+ }
+
+ void Initialize()
+ {
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+ }
+
+ void EnterCombat(Unit* /*who*/) override
+ {
+ _EnterCombat();
+ events.ScheduleEvent(EVENT_INITIAL_EMERGE, 4);
+ events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AHUNE_BUNNY)))
+ ahuneBunny->AI()->EnterEvadeMode();
+ summons.DespawnAll();
+ me->DespawnOrUnsummon();
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _JustDied();
+ instance->DoCastSpellOnPlayers(SPELL_AHUNE_ACHIEVEMENT);
+
+ if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_AHUNE_BUNNY)))
+ me->Kill(ahuneBunny);
+ if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
+ me->Kill(frozenCore);
+
+ Map::PlayerList const& players = me->GetMap()->GetPlayers();
+ if (!players.isEmpty())
+ {
+ if (Group* group = players.begin()->GetSource()->GetGroup())
+ if (group->isLFGGroup())
+ sLFGMgr->FinishDungeon(group->GetGUID(), 286);
+ }
+ }
+
+ void JustSummoned(Creature* summon) override
+ {
+ BossAI::JustSummoned(summon);
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_AHUNE_RETREAT)
+ {
+ Submerge();
+ events.ScheduleEvent(EVENT_EMERGE, 35000);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ events.Update(diff);
+
+ while (uint32 eventId = events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_INITIAL_EMERGE:
+ DoCast(me, SPELL_STAND);
+ DoCast(me, SPELL_AHUNE_SPANKY_HANDS);
+ DoCast(me, SPELL_AHUNES_SHIELD);
+ break;
+ case EVENT_EMERGE:
+ Emerge();
+ break;
+ case EVENT_SYNCH_HEALTH:
+ if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
+ DoCast(frozenCore, SPELL_SYNCH_HEALTH);
+ else
+ DoCast(me, SPELL_SUICIDE);
+ events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ break;
+ default:
+ break;
+ }
+ }
+ DoMeleeAttackIfReady();
+ }
+
+ void Emerge()
+ {
+ if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
+ frozenCore->AI()->DoAction(ACTION_AHUNE_RESURFACE);
+
+ DoCast(me, SPELL_AHUNES_SHIELD);
+ me->RemoveAurasDueToSpell(SPELL_AHUNE_SELF_STUN);
+ me->RemoveAurasDueToSpell(SPELL_STAY_SUBMERGED);
+ DoCast(me, SPELL_STAND);
+ DoCast(me, SPELL_RESURFACE, true);
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ }
+
+ void Submerge()
+ {
+ if (Creature* frozenCore = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_FROZEN_CORE)))
+ frozenCore->AI()->DoAction(ACTION_AHUNE_RETREAT);
+ me->RemoveAurasDueToSpell(SPELL_AHUNES_SHIELD);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
+ DoCast(me, SPELL_SUBMERGED, true);
+ DoCast(me, SPELL_AHUNE_SELF_STUN, true);
+ DoCast(me, SPELL_STAY_SUBMERGED, true);
+ me->HandleEmoteCommand(EMOTE_ONESHOT_SUBMERGE);
+ events.Reset();
+ }
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<boss_ahuneAI>(creature);
+ }
+};
+
+class npc_frozen_core : public CreatureScript
+{
+public:
+ npc_frozen_core() : CreatureScript("npc_frozen_core") { }
+
+ struct npc_frozen_coreAI : public ScriptedAI
+ {
+ npc_frozen_coreAI(Creature* creature) : ScriptedAI(creature)
+ {
+ _instance = me->GetInstanceScript();
+ Initialize();
+ }
+
+ void Initialize()
+ {
+ me->SetReactState(REACT_PASSIVE);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_DISABLE_MOVE);
+ DoCast(me, SPELL_FROZEN_CORE_GETS_HIT);
+ DoCast(me, SPELL_ICE_SPEAR_AURA);
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ DoCast(SPELL_MINION_DESPAWNER);
+ if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
+ ahune->AI()->EnterEvadeMode();
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
+ me->Kill(ahune);
+
+ DoCast(SPELL_SUMMON_LOOT_MISSILE);
+ DoCast(SPELL_MINION_DESPAWNER);
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_AHUNE_RETREAT)
+ {
+ me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC);
+ me->RemoveAurasDueToSpell(SPELL_ICE_SPEAR_AURA);
+ _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000, 0, PHASE_TWO);
+ }
+ else if (action == ACTION_AHUNE_RESURFACE)
+ {
+ _events.Reset();
+ DoCast(me, SPELL_ICE_SPEAR_AURA);
+ me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC | UNIT_FLAG_DISABLE_MOVE);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_SYNCH_HEALTH:
+ if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
+ DoCast(ahune, SPELL_SYNCH_HEALTH);
+ else
+ DoCast(me, SPELL_SUICIDE);
+ _events.ScheduleEvent(EVENT_SYNCH_HEALTH, 3000);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private:
+ InstanceScript* _instance;
+ EventMap _events;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_frozen_coreAI>(creature);
+ }
+};
+
+class npc_ahune_bunny : public CreatureScript
+{
+public:
+ npc_ahune_bunny() : CreatureScript("npc_ahune_bunny") { }
+
+ struct npc_ahune_bunnyAI : public ScriptedAI
+ {
+ npc_ahune_bunnyAI(Creature* creature) : ScriptedAI(creature), _summons(me)
+ {
+ _instance = me->GetInstanceScript();
+ _submerged = false;
+ }
+
+ void JustSummoned(Creature* summon) override
+ {
+ if (summon->GetEntry() == NPC_AHUNE)
+ return;
+
+ summon->SetInCombatWithZone();
+ _summons.Summon(summon);
+ }
+
+ void JustDied(Unit* /*killer*/) override
+ {
+ _summons.DespawnAll();
+ ResetFlameCallers();
+ }
+
+ void EnterEvadeMode(EvadeReason /*why*/) override
+ {
+ _EnterEvadeMode();
+ _summons.DespawnAll();
+ ResetFlameCallers();
+
+ me->SummonGameObject(GO_ICE_STONE, -69.90455f, -162.2449f, -2.366563f, 2.426008f, 0.0f, 0.0f, 0.9366722f, 0.3502074f, 0);
+ }
+
+ void DoAction(int32 action) override
+ {
+ if (action == ACTION_START_EVENT)
+ {
+ DoCast(me, SPELL_SUMMONING_VISUAL_1);
+ me->SummonCreature(NPC_WHISP_SOURCE_BUNNY, SummonPositions[5], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_WHISP_DEST_BUNNY, SummonPositions[6], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_SHAMAN_BONFIRE_BUNNY_000, SummonPositions[2], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_SHAMAN_BONFIRE_BUNNY_001, SummonPositions[3], TEMPSUMMON_MANUAL_DESPAWN);
+ me->SummonCreature(NPC_SHAMAN_BONFIRE_BUNNY_002, SummonPositions[4], TEMPSUMMON_MANUAL_DESPAWN);
+
+ for (uint8 counter = 0; counter < MAX_FLAMECALLERS; ++counter)
+ if (Creature* flameCaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000 + counter)))
+ flameCaller->GetMotionMaster()->MovePoint(counter, FlameCallerSpots[counter].GetPosition());
+
+ _submerged = false;
+ _events.Reset();
+ _events.SetPhase(PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_AHUNE, 10000);
+ _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, 14000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 22000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, 14000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, 108000, 0, PHASE_ONE);
+ }
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ if (!UpdateVictim())
+ return;
+
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_START_LOOKING_FOR_OPENING:
+ Talk(EMOTE_EARTHEN_ASSAULT);
+ for (uint8 counter = 0; counter < MAX_FLAMECALLERS; ++counter)
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000 + counter)))
+ DoCast(flamecaller, SPELL_SHAMANS_LOOK_FOR_OPENING, true);
+ break;
+ case EVENT_SUMMON_HAILSTONE:
+ DoCast(SPELL_SUMMON_HAILSTONE);
+ break;
+ case EVENT_SUMMON_COLDWEAVE:
+ DoCast(SPELL_SUMMON_COLDWEAVE);
+ DoCast(SPELL_SUMMON_COLDWEAVE);
+ _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 8000, 0, PHASE_ONE);
+ if (_submerged)
+ _events.ScheduleEvent(EVENT_SUMMON_FROSTWIND, 4000, 0, PHASE_ONE);
+ break;
+ case EVENT_SUMMON_FROSTWIND:
+ DoCast(SPELL_SUMMON_FROSTWIND);
+ break;
+ case EVENT_SUMMON_AHUNE:
+ if (TempSummon* ahune = me->SummonCreature(NPC_AHUNE, SummonPositions[0], TEMPSUMMON_DEAD_DESPAWN))
+ {
+ ahune->SummonCreature(NPC_FROZEN_CORE, SummonPositions[1], TEMPSUMMON_CORPSE_DESPAWN);
+ ahune->SetInCombatWithZone();
+ DoCast(ahune, SPELL_RESURFACE);
+ }
+ break;
+ case EVENT_CLOSE_OPENING:
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000)))
+ flamecaller->AI()->DoAction(ACTION_EMOTE_RESURFACE);
+ DoCast(SPELL_CLOSE_OPENING_VISUAL);
+ DoCast(me, SPELL_ICE_BOMBARD);
+ break;
+ case EVENT_AHUNE_PHASE_TWO:
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000)))
+ DoCast(flamecaller, SPELL_FOUND_OPENING);
+ if (Creature* ahune = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_AHUNE)))
+ ahune->AI()->DoAction(ACTION_AHUNE_RETREAT);
+ _events.Reset();
+ _events.SetPhase(PHASE_TWO);
+ _events.ScheduleEvent(EVENT_CLOSE_OPENING, 25000, 0, PHASE_TWO);
+ _events.ScheduleEvent(EVENT_AHUNE_PHASE_ONE, 35000, 0, PHASE_TWO);
+ break;
+ case EVENT_AHUNE_PHASE_ONE:
+ _submerged = true;
+ _events.Reset();
+ _events.SetPhase(PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_COLDWEAVE, 8000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_SUMMON_HAILSTONE, 5000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_START_LOOKING_FOR_OPENING, 5000, 0, PHASE_ONE);
+ _events.ScheduleEvent(EVENT_AHUNE_PHASE_TWO, 100000, 0, PHASE_ONE);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ void ResetFlameCallers()
+ {
+ for (uint8 counter = 0; counter < MAX_FLAMECALLERS; ++counter)
+ if (Creature* flamecaller = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_FLAMECALLER_000 + counter)))
+ flamecaller->AI()->EnterEvadeMode();
+ }
+
+ private:
+ InstanceScript* _instance;
+ EventMap _events;
+ SummonList _summons;
+ bool _submerged;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_ahune_bunnyAI>(creature);
+ }
+};
+
+class npc_earthen_ring_flamecaller : public CreatureScript
+{
+public:
+ npc_earthen_ring_flamecaller() : CreatureScript("npc_earthen_ring_flamecaller") { }
+
+ struct npc_earthen_ring_flamecallerAI : public ScriptedAI
+ {
+ npc_earthen_ring_flamecallerAI(Creature* creature) : ScriptedAI(creature)
+ {
+ _instance = me->GetInstanceScript();
+ }
+
+ void Reset() override
+ {
+ _events.Reset();
+ }
+
+ void MovementInform(uint32 motionType, uint32 pointId) override
+ {
+ if (motionType != POINT_MOTION_TYPE)
+ return;
+
+ switch (pointId)
+ {
+ case POINT_FLAMECALLER_000:
+ _mySpot = POINT_FLAMECALLER_000;
+ me->SetOrientation(FlameCallerSpots[_mySpot].GetOrientation());
+ break;
+ case POINT_FLAMECALLER_001:
+ _mySpot = POINT_FLAMECALLER_001;
+ me->SetOrientation(FlameCallerSpots[_mySpot].GetOrientation());
+ break;
+ case POINT_FLAMECALLER_002:
+ _mySpot = POINT_FLAMECALLER_002;
+ me->SetOrientation(FlameCallerSpots[_mySpot].GetOrientation());
+ break;
+ default:
+ break;
+ }
+
+ DoCast(me, SPELL_FIND_OPENING_CHANNEL);
+ }
+
+ void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override
+ {
+ switch (spellInfo->Id)
+ {
+ case SPELL_SHAMANS_LOOK_FOR_OPENING:
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_0, 17000);
+ break;
+ case SPELL_FOUND_OPENING:
+ _events.ScheduleEvent(EVENT_FOUND_OPENING, 0);
+ break;
+ default:
+ break;
+ }
+ }
+
+ void DoAction(int action) override
+ {
+ if (action == ACTION_EMOTE_RESURFACE)
+ Talk(EMOTE_RESURFACE);
+ }
+
+ void UpdateAI(uint32 diff) override
+ {
+ _events.Update(diff);
+
+ while (uint32 eventId = _events.ExecuteEvent())
+ {
+ switch (eventId)
+ {
+ case EVENT_LOOKFOROPENING_0:
+ LookOpening(true, 0);
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_1, 26000);
+ break;
+ case EVENT_LOOKFOROPENING_1:
+ LookOpening(true, 1);
+ _events.ScheduleEvent(EVENT_LOOKFOROPENING_2, 25000);
+ break;
+ case EVENT_LOOKFOROPENING_2:
+ LookOpening(true, 2);
+ _events.ScheduleEvent(EVENT_STOP_LOOKING_FOR_OPENING, 27000);
+ break;
+ case EVENT_STOP_LOOKING_FOR_OPENING:
+ LookOpening(false, _mySpot);
+ break;
+ case EVENT_FOUND_OPENING:
+ Talk(EMOTE_RETREAT);
+ default:
+ break;
+ }
+ }
+ }
+
+ void LookOpening(bool activate, uint8 spot)
+ {
+ if (_mySpot != spot)
+ return;
+
+ if (Creature* bonfireBunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_BONFIRE_BUNNY_000 + spot)))
+ if (Creature* beamBunny = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_BEAM_BUNNY_000 + spot)))
+ {
+ if (activate)
+ {
+ DoCast(bonfireBunny, SPELL_FIND_OPENING_TRIGGER);
+ bonfireBunny->CastSpell(beamBunny, SPELL_FIND_OPENING_VISUAL, true);
+ }
+ else
+ {
+ DoCast(me, SPELL_FIND_OPENING_CHANNEL);
+ bonfireBunny->CastStop();
+ beamBunny->RemoveAurasDueToSpell(SPELL_FIND_OPENING_BEAM_END);
+ }
+ }
+ }
+
+ private:
+ EventMap _events;
+ InstanceScript* _instance;
+ uint8 _mySpot;
+ };
+
+ CreatureAI* GetAI(Creature* creature) const override
+ {
+ return GetInstanceAI<npc_earthen_ring_flamecallerAI>(creature);
+ }
+};
+
+class go_ahune_ice_stone : public GameObjectScript
+{
+public:
+ go_ahune_ice_stone() : GameObjectScript("go_ahune_ice_stone") { }
+
+ bool OnGossipSelect(Player* player, GameObject* go, uint32 /*sender*/, uint32 /*action*/)
+ {
+ InstanceScript* instance = go->GetInstanceScript();
+ if (!instance)
+ return false;
+
+ player->PlayerTalkClass->ClearMenus();
+
+ if (Creature* ahuneBunny = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_AHUNE_BUNNY)))
+ {
+ ahuneBunny->AI()->DoAction(ACTION_START_EVENT);
+ ahuneBunny->SetInCombatWithZone();
+ }
+ if (Creature* luma = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_LUMA_SKYMOTHER)))
+ luma->CastSpell(player, SPELL_SUMMONING_RHYME_AURA, true);
+ player->CLOSE_GOSSIP_MENU();
+ go->Delete();
+
+ return true;
+ }
+};
+
+// 46430 - Synch Health
+class spell_ahune_synch_health : public SpellScriptLoader
+{
+public:
+ spell_ahune_synch_health() : SpellScriptLoader("spell_ahune_synch_health") { }
+
+ class spell_ahune_synch_health_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ahune_synch_health_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SYNCH_HEALTH))
+ return false;
+ return true;
+ }
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ if (Unit* caster = GetCaster())
+ target->SetHealth(caster->GetHealth());
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ahune_synch_health_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ahune_synch_health_SpellScript();
+ }
+};
+
+// 45926 - Summoning Rhyme Aura
+class spell_summoning_rhyme_aura : public SpellScriptLoader
+{
+public:
+ spell_summoning_rhyme_aura() : SpellScriptLoader("spell_summoning_rhyme_aura") { }
+
+ class spell_summoning_rhyme_aura_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_summoning_rhyme_aura_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_FORCE_WHISP_FLIGHT) || !sSpellMgr->GetSpellInfo(SPELL_SUMMONING_RHYME_BONFIRE))
+ return false;
+ return true;
+ }
+
+ void PeriodicTick(AuraEffect const* aurEff)
+ {
+ Creature* caster = GetCaster()->ToCreature();
+ Player* player = GetTarget()->ToPlayer();
+
+ if (!caster || !player)
+ return;
+
+ player->CastSpell(player, SPELL_FORCE_WHISP_FLIGHT);
+
+ switch (aurEff->GetTickNumber())
+ {
+ case 1:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_TEXT_1, NULL, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, player);
+ player->CastSpell(player, SPELL_SUMMONING_RHYME_BONFIRE, true);
+ break;
+ case 2:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_TEXT_2, NULL, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, player);
+ break;
+ case 3:
+ sCreatureTextMgr->SendChat(caster, SAY_PLAYER_TEXT_3, NULL, CHAT_MSG_SAY, LANG_UNIVERSAL, TEXT_RANGE_NORMAL, 0, TEAM_OTHER, false, player);
+ Remove();
+ break;
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_summoning_rhyme_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_summoning_rhyme_aura_AuraScript();
+ }
+};
+
+// 46878 - Summon Ice Spear Delayer
+class spell_summon_ice_spear_delayer : public SpellScriptLoader
+{
+public:
+ spell_summon_ice_spear_delayer() : SpellScriptLoader("spell_summon_ice_spear_delayer") { }
+
+ class spell_summon_ice_spear_delayer_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_summon_ice_spear_delayer_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ICE_SPEAR_GO) || !sSpellMgr->GetSpellInfo(SPELL_ICE_SPEAR_KNOCKBACK))
+ return false;
+ return true;
+ }
+
+ void PeriodicTick(AuraEffect const* aurEff)
+ {
+ if (Creature* caster = GetCaster()->ToCreature())
+ switch (aurEff->GetTickNumber())
+ {
+ case 1:
+ caster->CastSpell(caster, SPELL_SUMMON_ICE_SPEAR_GO);
+ break;
+ case 3:
+ if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f))
+ spike->UseDoorOrButton();
+ caster->AI()->DoCastAOE(SPELL_ICE_SPEAR_KNOCKBACK, true);
+ break;
+ case 5:
+ if (GameObject* spike = caster->FindNearestGameObject(GO_ICE_SPEAR, 3.0f))
+ spike->Delete();
+ caster->DespawnOrUnsummon();
+ break;
+ default:
+ break;
+ }
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_summon_ice_spear_delayer_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_summon_ice_spear_delayer_AuraScript();
+ }
+};
+
+// 46371 - Ice Spear Control Aura
+class spell_ice_spear_control_aura : public SpellScriptLoader
+{
+public:
+ spell_ice_spear_control_aura() : SpellScriptLoader("spell_ice_spear_control_aura") { }
+
+ class spell_ice_spear_control_aura_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_ice_spear_control_aura_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ICE_SPEAR_TARGET_PICKER))
+ return false;
+ return true;
+ }
+
+ void PeriodicTick(AuraEffect const* /*aurEff*/)
+ {
+ if (Unit* caster = GetCaster())
+ caster->CastSpell(caster, SPELL_ICE_SPEAR_TARGET_PICKER);
+ }
+
+ void Register() override
+ {
+ OnEffectPeriodic += AuraEffectPeriodicFn(spell_ice_spear_control_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_ice_spear_control_aura_AuraScript();
+ }
+};
+
+// 46372 - Ice Spear Target Picker
+class spell_ice_spear_target_picker : public SpellScriptLoader
+{
+public:
+ spell_ice_spear_target_picker() : SpellScriptLoader("spell_ice_spear_target_picker") { }
+
+ class spell_ice_spear_target_picker_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ice_spear_target_picker_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SUMMON_ICE_SPEAR_BUNNY))
+ return false;
+ return true;
+ }
+
+ void HandleDummy(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetHitUnit(), SPELL_SUMMON_ICE_SPEAR_BUNNY, true);
+ }
+
+ void Register()
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ice_spear_target_picker_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ice_spear_target_picker_SpellScript();
+ }
+};
+
+// 46320 - Spell Slippery Floor Periodic
+class spell_slippery_floor_periodic : public SpellScriptLoader
+{
+public:
+ spell_slippery_floor_periodic() : SpellScriptLoader("spell_slippery_floor_periodic") { }
+
+ class spell_slippery_floor_periodic_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_slippery_floor_periodic_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_SLIPPERY_FLOOR_SLIP))
+ return false;
+ return true;
+ }
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ if (Unit* target = GetHitUnit())
+ if (target->isMoving())
+ target->CastSpell(target, SPELL_SLIPPERY_FLOOR_SLIP, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_slippery_floor_periodic_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_slippery_floor_periodic_SpellScript();
+ }
+};
+
+// 46146 - Ahune Spanky Hands
+class spell_ahune_spanky_hands : public SpellScriptLoader
+{
+public:
+ spell_ahune_spanky_hands() : SpellScriptLoader("spell_ahune_spanky_hands") { }
+
+ class spell_ahune_spanky_hands_AuraScript : public AuraScript
+ {
+ PrepareAuraScript(spell_ahune_spanky_hands_AuraScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_COLD_SLAP))
+ return false;
+ return true;
+ }
+
+ void HandleProc(AuraEffect const* /*aurEff*/, ProcEventInfo& eventInfo)
+ {
+ PreventDefaultAction();
+ GetTarget()->CastSpell(eventInfo.GetProcTarget(), SPELL_COLD_SLAP, true);
+ }
+
+ void Register() override
+ {
+ OnEffectProc += AuraEffectProcFn(spell_ahune_spanky_hands_AuraScript::HandleProc, EFFECT_0, SPELL_AURA_PROC_TRIGGER_SPELL);
+ }
+ };
+
+ AuraScript* GetAuraScript() const override
+ {
+ return new spell_ahune_spanky_hands_AuraScript();
+ }
+};
+
+class spell_ahune_minion_despawner : public SpellScriptLoader
+{
+public:
+ spell_ahune_minion_despawner() : SpellScriptLoader("spell_ahune_minion_despawner") { }
+
+ class spell_ahune_minion_despawner_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ahune_minion_despawner_SpellScript);
+
+ void HandleScript(SpellEffIndex /*effIndex*/)
+ {
+ if (GetHitCreature())
+ GetHitCreature()->DespawnOrUnsummon();
+ }
+
+ void Register() override
+ {
+ OnEffectHitTarget += SpellEffectFn(spell_ahune_minion_despawner_SpellScript::HandleScript, EFFECT_0, SPELL_EFFECT_APPLY_AURA);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ahune_minion_despawner_SpellScript();
+ }
+};
+
+// 46398 - Spell Ice Bombardment Dest Picker
+class spell_ice_bombardment_dest_picker : public SpellScriptLoader
+{
+public:
+ spell_ice_bombardment_dest_picker() : SpellScriptLoader("spell_ice_bombardment_dest_picker") { }
+
+ class spell_ice_bombardment_dest_picker_SpellScript : public SpellScript
+ {
+ PrepareSpellScript(spell_ice_bombardment_dest_picker_SpellScript);
+
+ bool Validate(SpellInfo const* /*spellInfo*/) override
+ {
+ if (!sSpellMgr->GetSpellInfo(SPELL_ICE_BOMBARDMENT))
+ return false;
+ return true;
+ }
+
+ void HandleScriptEffect(SpellEffIndex /*effIndex*/)
+ {
+ GetCaster()->CastSpell(GetHitDest()->GetPositionX(), GetHitDest()->GetPositionY(), GetHitDest()->GetPositionZ(), SPELL_ICE_BOMBARDMENT, true);
+ }
+
+ void Register() override
+ {
+ OnEffectHit += SpellEffectFn(spell_ice_bombardment_dest_picker_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
+ }
+ };
+
+ SpellScript* GetSpellScript() const override
+ {
+ return new spell_ice_bombardment_dest_picker_SpellScript();
+ }
+};
+
+void AddSC_boss_ahune()
+{
+ new boss_ahune();
+ new npc_frozen_core();
+ new npc_earthen_ring_flamecaller();
+ new npc_ahune_bunny();
+ new go_ahune_ice_stone();
+ new spell_ahune_synch_health();
+ new spell_summoning_rhyme_aura();
+ new spell_summon_ice_spear_delayer();
+ new spell_ice_spear_control_aura();
+ new spell_slippery_floor_periodic();
+ new spell_ahune_spanky_hands();
+ new spell_ahune_minion_despawner();
+ new spell_ice_spear_target_picker();
+ new spell_ice_bombardment_dest_picker();
+}
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 87a07cd1e5e..0d85a8f0d86 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
@@ -31,15 +31,125 @@ class instance_the_slave_pens : public InstanceMapScript
public:
instance_the_slave_pens() : InstanceMapScript(SPScriptName, 547) { }
+ struct instance_the_slave_pens_InstanceMapScript : public InstanceScript
+ {
+ instance_the_slave_pens_InstanceMapScript(Map* map) : InstanceScript(map)
+ {
+ counter = DATA_FLAMECALLER_000;
+ }
+
+ void OnCreatureCreate(Creature* creature) override
+ {
+ switch (creature->GetEntry())
+ {
+ case NPC_AHUNE:
+ AhuneGUID = creature->GetGUID();
+ break;
+ case NPC_FROZEN_CORE:
+ FrozenCoreGUID = creature->GetGUID();
+ break;
+ case NPC_AHUNE_LOC_BUNNY:
+ AhuneBunnyGUID = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BONFIRE_BUNNY_000:
+ BonfireBunnyGUIDs[0] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BONFIRE_BUNNY_001:
+ BonfireBunnyGUIDs[1] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BONFIRE_BUNNY_002:
+ BonfireBunnyGUIDs[2] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BEAM_BUNNY_000:
+ BeamBunnyGUIDs[0] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BEAM_BUNNY_001:
+ BeamBunnyGUIDs[1] = creature->GetGUID();
+ break;
+ case NPC_SHAMAN_BEAM_BUNNY_002:
+ BeamBunnyGUIDs[2] = creature->GetGUID();
+ break;
+ case NPC_LUMA_SKYMOTHER:
+ LumaGUID = creature->GetGUID();
+ break;
+ case NPC_EARTHEN_RING_FLAMECALLER:
+ SetGuidData(counter, creature->GetGUID());
+ ++counter;
+ break;
+ default:
+ break;
+ }
+ }
+
+ void SetGuidData(uint32 data, ObjectGuid guid)
+ {
+ switch (data)
+ {
+ case DATA_FLAMECALLER_000:
+ FlameCallerGUIDs[0] = guid;
+ break;
+ case DATA_FLAMECALLER_001:
+ FlameCallerGUIDs[1] = guid;
+ break;
+ case DATA_FLAMECALLER_002:
+ FlameCallerGUIDs[2] = guid;
+ break;
+ default:
+ break;
+ }
+ }
+
+ ObjectGuid GetGuidData(uint32 type) const override
+ {
+ switch (type)
+ {
+ case DATA_AHUNE:
+ return AhuneGUID;
+ case DATA_AHUNE_BUNNY:
+ return AhuneBunnyGUID;
+ case DATA_FROZEN_CORE:
+ return FrozenCoreGUID;
+ case DATA_FLAMECALLER_000:
+ return FlameCallerGUIDs[0];
+ case DATA_FLAMECALLER_001:
+ return FlameCallerGUIDs[1];
+ case DATA_FLAMECALLER_002:
+ return FlameCallerGUIDs[2];
+ case DATA_BONFIRE_BUNNY_000:
+ return BonfireBunnyGUIDs[0];
+ case DATA_BONFIRE_BUNNY_001:
+ return BonfireBunnyGUIDs[1];
+ case DATA_BONFIRE_BUNNY_002:
+ return BonfireBunnyGUIDs[2];
+ case DATA_BEAM_BUNNY_000:
+ return BeamBunnyGUIDs[0];
+ case DATA_BEAM_BUNNY_001:
+ return BeamBunnyGUIDs[1];
+ case DATA_BEAM_BUNNY_002:
+ return BeamBunnyGUIDs[2];
+ case DATA_LUMA_SKYMOTHER:
+ return LumaGUID;
+ default:
+ break;
+ }
+ return ObjectGuid::Empty;
+ }
+
+ protected:
+ ObjectGuid AhuneGUID;
+ ObjectGuid AhuneBunnyGUID;
+ ObjectGuid FrozenCoreGUID;
+ ObjectGuid LumaGUID;
+ ObjectGuid FlameCallerGUIDs[3];
+ ObjectGuid BonfireBunnyGUIDs[3];
+ ObjectGuid BeamBunnyGUIDs[3];
+ uint8 counter;
+ };
+
InstanceScript* GetInstanceScript(InstanceMap* map) const override
{
return new instance_the_slave_pens_InstanceMapScript(map);
}
-
- struct instance_the_slave_pens_InstanceMapScript : public InstanceScript
- {
- instance_the_slave_pens_InstanceMapScript(Map* map) : InstanceScript(map) { }
- };
};
void AddSC_instance_the_slave_pens()
diff --git a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h
index 95e6e44121e..544e98fd206 100644
--- a/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h
+++ b/src/server/scripts/Outland/CoilfangReservoir/TheSlavePens/the_slave_pens.h
@@ -27,7 +27,43 @@ enum DataTypes
{
DATA_MENNU_THE_BETRAYER = 1,
DATA_ROKMAR_THE_CRACKLER = 2,
- DATA_QUAGMIRRAN = 3
+ DATA_QUAGMIRRAN = 3,
+ DATA_AHUNE = 4,
+ DATA_AHUNE_BUNNY = 5,
+ DATA_FROZEN_CORE = 6,
+ DATA_FLAMECALLER_000 = 7,
+ DATA_FLAMECALLER_001 = 8,
+ DATA_FLAMECALLER_002 = 9,
+ DATA_BONFIRE_BUNNY_000 = 10,
+ DATA_BONFIRE_BUNNY_001 = 11,
+ DATA_BONFIRE_BUNNY_002 = 12,
+ DATA_BEAM_BUNNY_000 = 13,
+ DATA_BEAM_BUNNY_001 = 14,
+ DATA_BEAM_BUNNY_002 = 15,
+ DATA_LUMA_SKYMOTHER = 16
+};
+
+enum CreaturesIds
+{
+ NPC_AHUNE = 25740,
+ NPC_FROZEN_CORE = 25865,
+ NPC_LUMA_SKYMOTHER = 25697,
+ NPC_AHUNE_LOC_BUNNY = 25745,
+ NPC_EARTHEN_RING_FLAMECALLER = 25754,
+ NPC_SHAMAN_BONFIRE_BUNNY_000 = 25971,
+ NPC_SHAMAN_BONFIRE_BUNNY_001 = 25972,
+ NPC_SHAMAN_BONFIRE_BUNNY_002 = 25973,
+ NPC_SHAMAN_BEAM_BUNNY_000 = 25964,
+ NPC_SHAMAN_BEAM_BUNNY_001 = 25965,
+ NPC_SHAMAN_BEAM_BUNNY_002 = 25966,
+ NPC_WHISP_DEST_BUNNY = 26120,
+ NPC_WHISP_SOURCE_BUNNY = 26121
+};
+
+enum GameObjectIds
+{
+ GO_ICE_SPEAR = 188077,
+ GO_ICE_STONE = 187882
};
#endif // SLAVE_PENS_H
diff --git a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp
index 4e20e6b0953..9b8220596c9 100644
--- a/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp
+++ b/src/server/scripts/Outland/HellfireCitadel/HellfireRamparts/boss_vazruden_the_herald.cpp
@@ -372,7 +372,7 @@ class boss_vazruden_the_herald : public CreatureScript
if (summon->GetEntry() == NPC_NAZAN)
{
summon->SetDisableGravity(true);
- summon->SetSpeed(MOVE_FLIGHT, 2.5f);
+ summon->SetSpeedRate(MOVE_FLIGHT, 2.5f);
if (victim)
AttackStartNoMove(victim);
}
diff --git a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp
index 4faab709e16..c29d560529d 100644
--- a/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp
+++ b/src/server/scripts/Outland/HellfireCitadel/ShatteredHalls/boss_warchief_kargath_bladefist.cpp
@@ -109,7 +109,7 @@ class boss_warchief_kargath_bladefist : public CreatureScript
{
removeAdds();
_Reset();
- me->SetSpeed(MOVE_RUN, 2);
+ me->SetSpeedRate(MOVE_RUN, 2);
me->SetWalk(false);
Initialize();
@@ -231,7 +231,7 @@ class boss_warchief_kargath_bladefist : public CreatureScript
{
// stop bladedance
InBlade = false;
- me->SetSpeed(MOVE_RUN, 2);
+ me->SetSpeedRate(MOVE_RUN, 2);
me->GetMotionMaster()->MoveChase(me->GetVictim());
Blade_Dance_Timer = 30000;
Wait_Timer = 0;
@@ -264,7 +264,7 @@ class boss_warchief_kargath_bladefist : public CreatureScript
Wait_Timer = 1;
InBlade = true;
Blade_Dance_Timer = 0;
- me->SetSpeed(MOVE_RUN, 4);
+ me->SetSpeedRate(MOVE_RUN, 4);
return;
}
else
diff --git a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp
index 56010c09897..20d96ecd29c 100644
--- a/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Eye/boss_alar.cpp
@@ -130,7 +130,7 @@ class boss_alar : public CreatureScript
_Reset();
me->SetDisplayId(me->GetNativeDisplayId());
- me->SetSpeed(MOVE_RUN, DefaultMoveSpeedRate);
+ me->SetSpeedRate(MOVE_RUN, DefaultMoveSpeedRate);
//me->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 10);
//me->SetFloatValue(UNIT_FIELD_COMBATREACH, 10);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true);
@@ -178,7 +178,7 @@ class boss_alar : public CreatureScript
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->AttackStop();
me->SetTarget(ObjectGuid::Empty);
- me->SetSpeed(MOVE_RUN, 5.0f);
+ me->SetSpeedRate(MOVE_RUN, 5.0f);
me->GetMotionMaster()->Clear();
me->GetMotionMaster()->MovePoint(0, waypoint[5][0], waypoint[5][1], waypoint[5][2]);
}
@@ -261,7 +261,7 @@ class boss_alar : public CreatureScript
case WE_REVIVE:
me->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_STAND);
me->SetFullHealth();
- me->SetSpeed(MOVE_RUN, DefaultMoveSpeedRate);
+ me->SetSpeedRate(MOVE_RUN, DefaultMoveSpeedRate);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
DoZoneInCombat();
DoCast(me, SPELL_REBIRTH, true);
diff --git a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
index 3aa2674aec6..d45e17bd28d 100644
--- a/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
+++ b/src/server/scripts/Outland/TempestKeep/Mechanar/boss_nethermancer_sepethrea.cpp
@@ -176,7 +176,7 @@ class npc_ragin_flames : public CreatureScript
Initialize();
me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true);
me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true);
- me->SetSpeed(MOVE_RUN, DUNGEON_MODE(0.5f, 0.7f));
+ me->SetSpeedRate(MOVE_RUN, DUNGEON_MODE(0.5f, 0.7f));
}
void EnterCombat(Unit* /*who*/) override
diff --git a/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp b/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
index 856649c6c5e..e64c0fe9f5e 100644
--- a/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
+++ b/src/server/scripts/Outland/TempestKeep/botanica/boss_warp_splinter.cpp
@@ -167,7 +167,7 @@ class boss_warp_splinter : public CreatureScript
{
Initialize();
- me->SetSpeed(MOVE_RUN, 0.7f, true);
+ me->SetSpeedRate(MOVE_RUN, 0.7f);
}
void EnterCombat(Unit* /*who*/) override
diff --git a/src/server/scripts/Outland/outland_script_loader.cpp b/src/server/scripts/Outland/outland_script_loader.cpp
index 91ba4e5689f..ed2c21da6c3 100644
--- a/src/server/scripts/Outland/outland_script_loader.cpp
+++ b/src/server/scripts/Outland/outland_script_loader.cpp
@@ -72,6 +72,7 @@ void AddSC_instance_the_slave_pens();
void AddSC_boss_mennu_the_betrayer();
void AddSC_boss_rokmar_the_crackler();
void AddSC_boss_quagmirran();
+void AddSC_boss_ahune();
// Coilfang Reservoir - The Underbog
void AddSC_instance_the_underbog();
@@ -193,6 +194,7 @@ void AddOutlandScripts()
AddSC_boss_mennu_the_betrayer();
AddSC_boss_rokmar_the_crackler();
AddSC_boss_quagmirran();
+ AddSC_boss_ahune();
// Coilfang Reservoir - The Underbog
AddSC_instance_the_underbog();
diff --git a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp
index 404cdc7ceb2..668c484dd63 100644
--- a/src/server/scripts/Outland/zone_hellfire_peninsula.cpp
+++ b/src/server/scripts/Outland/zone_hellfire_peninsula.cpp
@@ -159,7 +159,7 @@ public:
else
TC_LOG_ERROR("scripts", "TRINITY: npc_ancestral_wolf can not obtain owner or owner is not a player.");
- creature->SetSpeed(MOVE_WALK, 1.5f);
+ creature->SetSpeedRate(MOVE_WALK, 1.5f);
Reset();
}
@@ -192,7 +192,7 @@ public:
if (ryga->IsAlive() && !ryga->IsInCombat())
{
ryga->SetWalk(true);
- ryga->SetSpeed(MOVE_WALK, 1.5f);
+ ryga->SetSpeedRate(MOVE_WALK, 1.5f);
ryga->GetMotionMaster()->MovePoint(0, 517.340698f, 3885.03975f, 190.455978f, true);
Reset();
}
@@ -778,7 +778,7 @@ public:
me->AddAura(SPELL_JULES_THREATENS_AURA, me);
me->SetCanFly(true);
- me->SetSpeed(MOVE_RUN, 0.2f);
+ me->SetSpeedRate(MOVE_RUN, 0.2f);
me->SetFacingTo(3.207566f);
me->GetMotionMaster()->MoveJump(exorcismPos[2], 2.0f, 2.0f);
@@ -798,7 +798,7 @@ public:
break;
case ACTION_JULES_MOVE_HOME:
wpreached = false;
- me->SetSpeed(MOVE_RUN, 1.0f);
+ me->SetSpeedRate(MOVE_RUN, 1.0f);
me->GetMotionMaster()->MovePoint(11, exorcismPos[2]);
events.CancelEvent(EVENT_SUMMON_SKULL);
diff --git a/src/server/scripts/Pet/pet_dk.cpp b/src/server/scripts/Pet/pet_dk.cpp
index 80b3a00774b..113b14a0d54 100644
--- a/src/server/scripts/Pet/pet_dk.cpp
+++ b/src/server/scripts/Pet/pet_dk.cpp
@@ -103,8 +103,8 @@ class npc_pet_dk_ebon_gargoyle : public CreatureScript
//! HACK: Creature's can't have MOVEMENTFLAG_FLYING
// Fly Away
me->SetCanFly(true);
- me->SetSpeed(MOVE_FLIGHT, 0.75f, true);
- me->SetSpeed(MOVE_RUN, 0.75f, true);
+ me->SetSpeedRate(MOVE_FLIGHT, 0.75f);
+ me->SetSpeedRate(MOVE_RUN, 0.75f);
float x = me->GetPositionX() + 20 * std::cos(me->GetOrientation());
float y = me->GetPositionY() + 20 * std::sin(me->GetOrientation());
float z = me->GetPositionZ() + 40;
diff --git a/src/server/scripts/ScriptLoader.cpp b/src/server/scripts/ScriptLoader.cpp
deleted file mode 100644
index 57c848d5981..00000000000
--- a/src/server/scripts/ScriptLoader.cpp
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * 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 "ScriptLoader.h"
-#include "World.h"
-
-void AddSpellsScripts();
-void AddCommandsScripts();
-
-#ifdef SCRIPTS
-void AddWorldScripts();
-void AddEasternKingdomsScripts();
-void AddKalimdorScripts();
-void AddOutlandScripts();
-void AddNorthrendScripts();
-void AddEventsScripts();
-void AddPetScripts();
-void AddOutdoorPvPScripts();
-void AddCustomScripts();
-#endif
-
-void AddScripts()
-{
- AddSpellsScripts();
- AddCommandsScripts();
-#ifdef SCRIPTS
- AddWorldScripts();
- AddEasternKingdomsScripts();
- AddKalimdorScripts();
- AddOutlandScripts();
- AddNorthrendScripts();
- AddEventsScripts();
- AddPetScripts();
- AddOutdoorPvPScripts();
- AddCustomScripts();
-#endif
-}
diff --git a/src/server/scripts/ScriptLoader.cpp.in.cmake b/src/server/scripts/ScriptLoader.cpp.in.cmake
new file mode 100644
index 00000000000..33c336a9a93
--- /dev/null
+++ b/src/server/scripts/ScriptLoader.cpp.in.cmake
@@ -0,0 +1,64 @@
+/*
+ * 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/>.
+ */
+
+// This file was created automatically from your script configuration!
+// Use CMake to reconfigure this file, never change it on your own!
+
+#cmakedefine TRINITY_IS_DYNAMIC_SCRIPTLOADER
+
+#include "Define.h"
+#include <vector>
+#include <string>
+
+@TRINITY_SCRIPTS_FORWARD_DECL@
+#ifdef TRINITY_IS_DYNAMIC_SCRIPTLOADER
+# include "revision_data.h"
+# define TC_SCRIPT_API TC_API_EXPORT
+extern "C" {
+
+/// Exposed in script modules to return the script module revision hash.
+TC_SCRIPT_API char const* GetScriptModuleRevisionHash()
+{
+ return _HASH;
+}
+
+/// Exposed in script module to return the name of the script module
+/// contained in this shared library.
+TC_SCRIPT_API char const* GetScriptModule()
+{
+ return "@TRINITY_CURRENT_SCRIPT_PROJECT@";
+}
+
+#else
+# include "ScriptLoader.h"
+# define TC_SCRIPT_API
+#endif
+
+/// Exposed in script modules to register all scripts to the ScriptMgr.
+TC_SCRIPT_API void AddScripts()
+{
+@TRINITY_SCRIPTS_INVOKE@}
+
+/// Exposed in script modules to get the build directive of the module.
+TC_SCRIPT_API char const* GetBuildDirective()
+{
+ return _BUILD_DIRECTIVE;
+}
+
+#ifdef TRINITY_IS_DYNAMIC_SCRIPTLOADER
+} // extern "C"
+#endif
diff --git a/src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp b/src/server/scripts/ScriptPCH.cpp
index 41fecf3c60d..41fecf3c60d 100644
--- a/src/server/scripts/PrecompiledHeaders/ScriptPCH.cpp
+++ b/src/server/scripts/ScriptPCH.cpp
diff --git a/src/server/scripts/PrecompiledHeaders/ScriptPCH.h b/src/server/scripts/ScriptPCH.h
index 1cd25309055..1cd25309055 100644
--- a/src/server/scripts/PrecompiledHeaders/ScriptPCH.h
+++ b/src/server/scripts/ScriptPCH.h
diff --git a/src/server/scripts/World/boss_emerald_dragons.cpp b/src/server/scripts/World/boss_emerald_dragons.cpp
index 1f89720803d..65dec74414b 100644
--- a/src/server/scripts/World/boss_emerald_dragons.cpp
+++ b/src/server/scripts/World/boss_emerald_dragons.cpp
@@ -205,7 +205,7 @@ class npc_dream_fog : public CreatureScript
}
// Seeping fog movement is slow enough for a player to be able to walk backwards and still outpace it
me->SetWalk(true);
- me->SetSpeed(MOVE_WALK, 0.75f);
+ me->SetSpeedRate(MOVE_WALK, 0.75f);
}
else
_roamTimer -= diff;
diff --git a/src/server/shared/DataStores/DBCStore.h b/src/server/shared/DataStores/DBCStore.h
index b93bbdaea12..7c2cab1e36a 100644
--- a/src/server/shared/DataStores/DBCStore.h
+++ b/src/server/shared/DataStores/DBCStore.h
@@ -25,6 +25,121 @@
#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/CMakeLists.txt b/src/server/worldserver/CMakeLists.txt
index 7923b239854..0de8d6054f3 100644
--- a/src/server/worldserver/CMakeLists.txt
+++ b/src/server/worldserver/CMakeLists.txt
@@ -48,6 +48,7 @@ set_target_properties(worldserver PROPERTIES LINK_FLAGS "${worldserver_LINK_FLAG
target_link_libraries(worldserver
PUBLIC
scripts
+ game
gsoap
readline)
@@ -68,6 +69,11 @@ set_target_properties(worldserver
FOLDER
"server")
+# Add all dynamic projects as dependency to the worldserver
+if (WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES)
+ add_dependencies(worldserver ${WORLDSERVER_DYNAMIC_SCRIPT_MODULES_DEPENDENCIES})
+endif()
+
if( WIN32 )
if ( MSVC )
add_custom_command(TARGET worldserver
diff --git a/src/server/worldserver/Main.cpp b/src/server/worldserver/Main.cpp
index c047c1fafc3..2b5553b4bb0 100644
--- a/src/server/worldserver/Main.cpp
+++ b/src/server/worldserver/Main.cpp
@@ -39,6 +39,7 @@
#include "InstanceSaveMgr.h"
#include "ObjectAccessor.h"
#include "ScriptMgr.h"
+#include "ScriptReloadMgr.h"
#include "ScriptLoader.h"
#include "OutdoorPvP/OutdoorPvPMgr.h"
#include "BattlegroundMgr.h"
@@ -271,6 +272,7 @@ extern int main(int argc, char** argv)
sOutdoorPvPMgr->Die();
sMapMgr->UnloadAll(); // unload all grids (including locked in memory)
sScriptMgr->Unload();
+ sScriptReloadMgr->Unload();
// set server offline
LoginDatabase.DirectPExecute("UPDATE realmlist SET flag = flag | %u WHERE id = '%d'", REALM_FLAG_OFFLINE, realm.Id.Realm);
diff --git a/src/server/worldserver/worldserver.conf.dist b/src/server/worldserver/worldserver.conf.dist
index 7c512d8ae6e..c2694c640c5 100644
--- a/src/server/worldserver/worldserver.conf.dist
+++ b/src/server/worldserver/worldserver.conf.dist
@@ -12,6 +12,7 @@
# SERVER LOGGING
# SERVER SETTINGS
# UPDATE SETTINGS
+# HOTSWAP SETTINGS
# WARDEN SETTINGS
# PLAYER INTERACTION
# CREATURE SETTINGS
@@ -1280,6 +1281,78 @@ Updates.CleanDeadRefMaxCount = 3
###################################################################################################
###################################################################################################
+# HOTSWAP SETTINGS
+#
+# HotSwap.Enabled (Requires compilation with DYNAMIC_LINKING=1)
+# Description: Enables dynamic script hotswapping.
+# Reloads scripts on changes.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.Enabled = 1
+
+#
+# HotSwap.ScriptDir
+# Description: Directory containing the script shared libraries (.dll/.so).
+# Example: "/usr/local/scripts"
+# Default: "scripts"
+
+HotSwap.ScriptDir = "scripts"
+
+# HotSwap.EnableReCompiler
+# Description: Enables the dynamic script recompiler.
+# Watches your script source directories and recompiles the
+# script modules on changes.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableReCompiler = 1
+
+# HotSwap.EnableEarlyTermination
+# Description: Terminate the build of a module when an associated
+# source file was changed meanwhile.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableEarlyTermination = 1
+
+# HotSwap.EnableBuildFileRecreation
+# Description: Recreate build files when sources to a module
+# were added or removed.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableBuildFileRecreation = 1
+
+#
+# HotSwap.EnableInstall
+# Description: Enables cmake install after automatic builds have finished
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnableInstall = 1
+
+#
+# HotSwap.EnablePrefixCorrection
+# Description: Allows the core to automatic set the CMAKE_INSTALL_PREFIX
+# to it's current location in the filesystem.
+# Default: 1 - (Enabled)
+# 0 - (Disabled)
+
+HotSwap.EnablePrefixCorrection = 1
+
+# HotSwap.ReCompilerBuildType
+# Description: Defines the build type of the builds invoked by the recompiler.
+# Default: "" - Built-in build type of the module is used.
+# "Release" - Release builds only
+# "Debug" - Debug builds only
+
+HotSwap.ReCompilerBuildType = ""
+
+#
+###################################################################################################
+
+###################################################################################################
# WARDEN SETTINGS
#
# Warden.Enabled
@@ -3220,7 +3293,6 @@ AuctionHouseBot.Class.Key = 1
AuctionHouseBot.Class.Misc = 5
AuctionHouseBot.Class.Glyph = 3
-
#
###################################################################################################
@@ -3551,6 +3623,7 @@ Logger.mmaps=3,Server
#Logger.rbac=3,Console Server
#Logger.scripts=3,Console Server
#Logger.scripts.ai=3,Console Server
+#Logger.scripts.hotswap=3,Console Server
#Logger.server.authserver=3,Console Server
#Logger.spells=3,Console Server
#Logger.spells.periodic=3,Console Server
diff --git a/src/tools/map_extractor/System.cpp b/src/tools/map_extractor/System.cpp
index f3a761fd437..1d84fc75d27 100644
--- a/src/tools/map_extractor/System.cpp
+++ b/src/tools/map_extractor/System.cpp
@@ -53,12 +53,13 @@ char input_path[MAX_PATH_LENGTH] = ".";
// **************************************************
enum Extract
{
- EXTRACT_MAP = 1,
- EXTRACT_DBC = 2
+ EXTRACT_MAP = 1,
+ EXTRACT_DBC = 2,
+ EXTRACT_CAMERA = 4
};
// Select data for extract
-int CONF_extract = EXTRACT_MAP | EXTRACT_DBC;
+int CONF_extract = EXTRACT_MAP | EXTRACT_DBC | EXTRACT_CAMERA;
// This option allow limit minimum height to some value (Allow save some memory)
bool CONF_allow_height_limit = true;
float CONF_use_minHeight = -500.0f;
@@ -103,7 +104,7 @@ void Usage(char* prg)
"%s -[var] [value]\n"\
"-i set input path (max %d characters)\n"\
"-o set output path (max %d characters)\n"\
- "-e extract only MAP(1)/DBC(2) - standard: both(3)\n"\
+ "-e extract only MAP(1)/DBC(2)/Camera(4) - standard: all(7)\n"\
"-f height stored as int (less map size but lost some accuracy) 1 by default\n"\
"Example: %s -f 0 -i \"c:\\games\\game\"", prg, MAX_PATH_LENGTH - 1, MAX_PATH_LENGTH - 1, prg);
exit(1);
@@ -151,7 +152,7 @@ void HandleArgs(int argc, char * arg[])
if(c + 1 < argc) // all ok
{
CONF_extract=atoi(arg[(c++) + 1]);
- if(!(CONF_extract > 0 && CONF_extract < 4))
+ if(!(CONF_extract > 0 && CONF_extract < 8))
Usage(arg[0]);
}
else
@@ -1025,6 +1026,56 @@ void ExtractDBCFiles(int locale, bool basicLocale)
printf("Extracted %u DBC files\n\n", count);
}
+void ExtractCameraFiles(int locale, bool basicLocale)
+{
+ printf("Extracting camera files...\n");
+ DBCFile camdbc("DBFilesClient\\CinematicCamera.dbc");
+
+ if (!camdbc.open())
+ {
+ printf("Unable to open CinematicCamera.dbc. Camera extract aborted.\n");
+ return;
+ }
+
+ // get camera file list from DBC
+ std::vector<std::string> camerafiles;
+ size_t cam_count = camdbc.getRecordCount();
+
+ for (uint32 i = 0; i < cam_count; ++i)
+ {
+ std::string camFile(camdbc.getRecord(i).getString(1));
+ size_t loc = camFile.find(".mdx");
+ if (loc != std::string::npos)
+ camFile.replace(loc, 4, ".m2");
+ camerafiles.push_back(std::string(camFile));
+ }
+
+ std::string path = output_path;
+ path += "/Cameras/";
+ CreateDir(path);
+ if (!basicLocale)
+ {
+ path += langs[locale];
+ path += "/";
+ CreateDir(path);
+ }
+
+ // extract M2s
+ uint32 count = 0;
+ for (std::string thisFile : camerafiles)
+ {
+ std::string filename = path;
+ filename += (thisFile.c_str() + strlen("Cameras\\"));
+
+ if (boost::filesystem::exists(filename))
+ continue;
+
+ if (ExtractFile(thisFile.c_str(), filename))
+ ++count;
+ }
+ printf("Extracted %u camera files\n", count);
+}
+
void LoadLocaleMPQFiles(int const locale)
{
std::string fileName = Trinity::StringFormat("%s/Data/%s/locale-%s.MPQ", input_path, langs[locale], langs[locale]);
@@ -1111,6 +1162,19 @@ int main(int argc, char * arg[])
return 0;
}
+ if (CONF_extract & EXTRACT_CAMERA)
+ {
+ printf("Using locale: %s\n", langs[FirstLocale]);
+
+ // Open MPQs
+ LoadLocaleMPQFiles(FirstLocale);
+ LoadCommonMPQFiles();
+
+ ExtractCameraFiles(FirstLocale, true);
+ // Close MPQs
+ CloseMPQFiles();
+ }
+
if (CONF_extract & EXTRACT_MAP)
{
printf("Using locale: %s\n", langs[FirstLocale]);