Merge branch 'master' of github.com:azerothcore/azerothcore-wotlk into Playerbot

This commit is contained in:
Yunfan Li
2023-09-23 22:44:15 +08:00
154 changed files with 7913 additions and 4858 deletions
+1 -1
View File
@@ -575,7 +575,7 @@ if(OPENSSL_FOUND)
include(EnsureVersion)
ENSURE_VERSION("${OPENSSL_EXPECTED_VERSION}" "${OPENSSL_VERSION}" OPENSSL_VERSION_OK)
if(NOT OPENSSL_VERSION_OK)
message(FATAL_ERROR "TrinityCore needs OpenSSL version ${OPENSSL_EXPECTED_VERSION} but found too new version ${OPENSSL_VERSION}. TrinityCore needs OpenSSL 1.0.x or 1.1.x to work properly. If you still have problems please install OpenSSL 1.0.x if you still have problems search on forum for TCE00022")
message(FATAL_ERROR "AzerothCore needs OpenSSL version ${OPENSSL_EXPECTED_VERSION} but found too new version ${OPENSSL_VERSION}. AzerothCore needs OpenSSL 1.0.x or 1.1.x to work properly. If you still have problems please install OpenSSL 1.0.x if you still have problems search on forum for TCE00022")
endif()
if(NOT TARGET OpenSSL::Crypto AND
+14 -2
View File
@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008+ TrinityCore <http://www.trinitycore.org/>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 _MAPDEFINES_H
-7
View File
@@ -41,13 +41,6 @@
#include <unistd.h>
#endif
#if AC_COMPILER == AC_COMPILER_MICROSOFT
#define atoll _atoi64
#define llabs _abs64
#else
#define stricmp strcasecmp
#endif
#define STRINGIZE(a) #a
#define MAX_NETCLIENT_PACKET_SIZE (32767 - 1) // Client hardcap: int16 with trailing zero space otherwise crash on memory free
+65 -31
View File
@@ -32,6 +32,7 @@ namespace
std::vector<std::string> _additonalFiles;
std::vector<std::string> _args;
std::unordered_map<std::string /*name*/, std::string /*value*/> _configOptions;
std::unordered_map<std::string /*name*/, std::string /*value*/> _envVarCache;
std::mutex _configLock;
// Check system configs like *server.conf*
@@ -39,8 +40,9 @@ namespace
{
size_t foundAuth = fileName.find("authserver.conf");
size_t foundWorld = fileName.find("worldserver.conf");
size_t foundImport = fileName.find("dbimport.conf");
return foundAuth != std::string_view::npos || foundWorld != std::string_view::npos;
return foundAuth != std::string_view::npos || foundWorld != std::string_view::npos || foundImport != std::string_view::npos;
}
// Check logging system configs like Appender.* and Logger.*
@@ -119,7 +121,7 @@ namespace
auto const& itr = fileConfigs.find(confOption);
if (itr != fileConfigs.end())
{
PrintError(file, "> Config::LoadFile: Dublicate key name '{}' in config file '{}'", confOption, file);
PrintError(file, "> Config::LoadFile: Duplicate key name '{}' in config file '{}'", confOption, file);
return true;
}
@@ -282,9 +284,14 @@ namespace
return result;
}
std::string GetEnvVarName(std::string const& configName)
{
return "AC_" + IniKeyToEnvVarKey(configName);
}
Optional<std::string> EnvVarForIniKey(std::string const& key)
{
std::string envKey = "AC_" + IniKeyToEnvVarKey(key);
std::string envKey = GetEnvVarName(key);
char* val = std::getenv(envKey.c_str());
if (!val)
return std::nullopt;
@@ -329,6 +336,29 @@ bool ConfigMgr::Reload()
return true;
}
// Check the _envVarCache if the env var is there
// if not, check the env for the value
Optional<std::string> GetEnvFromCache(std::string const& configName, std::string const& envVarName)
{
auto foundInCache = _envVarCache.find(envVarName);
Optional<std::string> foundInEnv;
// If it's not in the cache
if (foundInCache == _envVarCache.end())
{
// Check the env itself
foundInEnv = EnvVarForIniKey(configName);
if (foundInEnv)
{
// If it's found in the env, put it in the cache
_envVarCache.emplace(envVarName, *foundInEnv);
}
// Return the result of checking env
return foundInEnv;
}
return foundInCache->second;
}
std::vector<std::string> ConfigMgr::OverrideWithEnvVariablesIfAny()
{
std::lock_guard<std::mutex> lock(_configLock);
@@ -356,29 +386,31 @@ template<class T>
T ConfigMgr::GetValueDefault(std::string const& name, T const& def, bool showLogs /*= true*/) const
{
std::string strValue;
auto const& itr = _configOptions.find(name);
if (itr == _configOptions.end())
bool notFound = itr == _configOptions.end();
auto envVarName = GetEnvVarName(name);
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
if (envVar)
{
Optional<std::string> envVar = EnvVarForIniKey(name);
if (!envVar)
// If showLogs and this key/value pair wasn't found in the currently saved config
if (showLogs && (notFound || itr->second != envVar->c_str()))
{
if (showLogs)
{
LOG_ERROR("server.loading", "> Config: Missing property {} in config file {}, add \"{} = {}\" to this file.",
name, _filename, name, Acore::ToString(def));
}
return def;
}
if (showLogs)
{
LOG_WARN("server.loading", "Missing property {} in config file {}, recovered with environment '{}' value.",
name, _filename, envVar->c_str());
LOG_INFO("server.loading", "> Config: Found config value '{}' from environment variable '{}'.", name, envVarName );
AddKey(name, envVar->c_str(), "ENVIRONMENT", false, false);
}
strValue = *envVar;
}
else if (notFound)
{
if (showLogs)
{
LOG_ERROR("server.loading", "> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
name, _filename, name, Acore::ToString(def), envVarName);
}
return def;
}
else
{
strValue = itr->second;
@@ -403,24 +435,26 @@ template<>
std::string ConfigMgr::GetValueDefault<std::string>(std::string const& name, std::string const& def, bool showLogs /*= true*/) const
{
auto const& itr = _configOptions.find(name);
if (itr == _configOptions.end())
bool notFound = itr == _configOptions.end();
auto envVarName = GetEnvVarName(name);
Optional<std::string> envVar = GetEnvFromCache(name, envVarName);
if (envVar)
{
Optional<std::string> envVar = EnvVarForIniKey(name);
if (envVar)
// If showLogs and this key/value pair wasn't found in the currently saved config
if (showLogs && (notFound || itr->second != envVar->c_str()))
{
if (showLogs)
{
LOG_WARN("server.loading", "Missing property {} in config file {}, recovered with environment '{}' value.",
name, _filename, envVar->c_str());
}
return *envVar;
LOG_INFO("server.loading", "> Config: Found config value '{}' from environment variable '{}'.", name, envVarName);
AddKey(name, *envVar, "ENVIRONMENT", false, false);
}
return *envVar;
}
else if (notFound)
{
if (showLogs)
{
LOG_ERROR("server.loading", "> Config: Missing property {} in config file {}, add \"{} = {}\" to this file.",
name, _filename, name, def);
LOG_ERROR("server.loading", "> Config: Missing property {} in config file {}, add \"{} = {}\" to this file or define '{}' as an environment variable.",
name, _filename, name, def, envVarName);
}
return def;
+15 -3
View File
@@ -1,7 +1,19 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
*/
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "GitRevision.h"
#include "revision.h"
+15 -3
View File
@@ -1,7 +1,19 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2018 TrinityCore <http://www.trinitycore.org/>
*/
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 __GITREVISION_H__
#define __GITREVISION_H__
-2
View File
@@ -398,6 +398,4 @@ void Log::LoadFromConfig()
ReadAppendersFromConfig();
ReadLoggersFromConfig();
_debugLogMask = DebugLogFilters(sConfigMgr->GetOption<uint32>("DebugLogMask", LOG_FILTER_NONE, false));
}
-2
View File
@@ -121,8 +121,6 @@ private:
Acore::Asio::IoContext* _ioContext;
Acore::Asio::Strand* _strand;
// Deprecated debug filter logs
DebugLogFilters _debugLogMask;
};
#define sLog Log::instance()
+1 -33
View File
@@ -46,6 +46,7 @@ enum AppenderType : uint8
APPENDER_INVALID = 0xFF // SKIP
};
// EnumUtils: DESCRIBE THIS
enum AppenderFlags : uint8
{
APPENDER_FLAGS_NONE = 0x00,
@@ -56,37 +57,4 @@ enum AppenderFlags : uint8
APPENDER_FLAGS_MAKE_FILE_BACKUP = 0x10
};
// Dprecated debug log filters need delte later
enum DebugLogFilters
{
LOG_FILTER_NONE = 0x00000000,
LOG_FILTER_UNITS = 0x00000001, // Anything related to units that doesn't fit in other categories. ie. creature formations
LOG_FILTER_PETS = 0x00000002,
LOG_FILTER_VEHICLES = 0x00000004,
LOG_FILTER_TSCR = 0x00000008, // C++ AI, instance scripts, etc.
LOG_FILTER_DATABASE_AI = 0x00000010, // SmartAI, EventAI, CreatureAI
LOG_FILTER_MAPSCRIPTS = 0x00000020,
LOG_FILTER_NETWORKIO = 0x00000040, // Anything packet/netcode related
LOG_FILTER_SPELLS_AURAS = 0x00000080,
LOG_FILTER_ACHIEVEMENTSYS = 0x00000100,
LOG_FILTER_CONDITIONSYS = 0x00000200,
LOG_FILTER_POOLSYS = 0x00000400,
LOG_FILTER_AUCTIONHOUSE = 0x00000800,
LOG_FILTER_BATTLEGROUND = 0x00001000, // Anything related to arena's and battlegrounds
LOG_FILTER_OUTDOORPVP = 0x00002000,
LOG_FILTER_CHATSYS = 0x00004000,
LOG_FILTER_LFG = 0x00008000,
LOG_FILTER_MAPS = 0x00010000, // Maps, instances, grids, cells, visibility
LOG_FILTER_PLAYER_LOADING = 0x00020000, // Debug output from Player::_Load functions
LOG_FILTER_PLAYER_ITEMS = 0x00040000, // Anything item related
LOG_FILTER_PLAYER_SKILLS = 0x00080000, // Skills related
LOG_FILTER_LOOT = 0x00100000, // Loot related
LOG_FILTER_GUILD = 0x00200000, // Guild related
LOG_FILTER_TRANSPORTS = 0x00400000, // Transport related
LOG_FILTER_WARDEN = 0x00800000, // Warden related
LOG_FILTER_BATTLEFIELD = 0x01000000, // Battlefield related
LOG_FILTER_MODULES = 0x02000000, // Modules debug
LOG_FILTER_CLOSE_SOCKET = 0x04000000, // Whenever KickPlayer() or CloseSocket() are called
};
#endif // LogCommon_h__
+51
View File
@@ -121,4 +121,55 @@ AC_API_EXPORT size_t EnumUtils<AppenderType>::ToIndex(AppenderType value)
default: throw std::out_of_range("value");
}
}
/*****************************************************************\
|* data for enum 'AppenderFlags' in 'LogCommon.h' auto-generated *|
\*****************************************************************/
template <>
AC_API_EXPORT EnumText EnumUtils<AppenderFlags>::ToString(AppenderFlags value)
{
switch (value)
{
case APPENDER_FLAGS_NONE: return { "APPENDER_FLAGS_NONE", "APPENDER_FLAGS_NONE", "" };
case APPENDER_FLAGS_PREFIX_TIMESTAMP: return { "APPENDER_FLAGS_PREFIX_TIMESTAMP", "APPENDER_FLAGS_PREFIX_TIMESTAMP", "" };
case APPENDER_FLAGS_PREFIX_LOGLEVEL: return { "APPENDER_FLAGS_PREFIX_LOGLEVEL", "APPENDER_FLAGS_PREFIX_LOGLEVEL", "" };
case APPENDER_FLAGS_PREFIX_LOGFILTERTYPE: return { "APPENDER_FLAGS_PREFIX_LOGFILTERTYPE", "APPENDER_FLAGS_PREFIX_LOGFILTERTYPE", "" };
case APPENDER_FLAGS_USE_TIMESTAMP: return { "APPENDER_FLAGS_USE_TIMESTAMP", "APPENDER_FLAGS_USE_TIMESTAMP", "" };
case APPENDER_FLAGS_MAKE_FILE_BACKUP: return { "APPENDER_FLAGS_MAKE_FILE_BACKUP", "APPENDER_FLAGS_MAKE_FILE_BACKUP", "" };
default: throw std::out_of_range("value");
}
}
template <>
AC_API_EXPORT size_t EnumUtils<AppenderFlags>::Count() { return 6; }
template <>
AC_API_EXPORT AppenderFlags EnumUtils<AppenderFlags>::FromIndex(size_t index)
{
switch (index)
{
case 0: return APPENDER_FLAGS_NONE;
case 1: return APPENDER_FLAGS_PREFIX_TIMESTAMP;
case 2: return APPENDER_FLAGS_PREFIX_LOGLEVEL;
case 3: return APPENDER_FLAGS_PREFIX_LOGFILTERTYPE;
case 4: return APPENDER_FLAGS_USE_TIMESTAMP;
case 5: return APPENDER_FLAGS_MAKE_FILE_BACKUP;
default: throw std::out_of_range("index");
}
}
template <>
AC_API_EXPORT size_t EnumUtils<AppenderFlags>::ToIndex(AppenderFlags value)
{
switch (value)
{
case APPENDER_FLAGS_NONE: return 0;
case APPENDER_FLAGS_PREFIX_TIMESTAMP: return 1;
case APPENDER_FLAGS_PREFIX_LOGLEVEL: return 2;
case APPENDER_FLAGS_PREFIX_LOGFILTERTYPE: return 3;
case APPENDER_FLAGS_USE_TIMESTAMP: return 4;
case APPENDER_FLAGS_MAKE_FILE_BACKUP: return 5;
default: throw std::out_of_range("value");
}
}
}
+14 -2
View File
@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2021 TrinityCore <http://www.trinitycore.org/>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 EnumFlag_h__
+3 -3
View File
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITY_SMARTENUM_H
#define TRINITY_SMARTENUM_H
#ifndef AC_SMARTENUM_H
#define AC_SMARTENUM_H
#include "IteratorPair.h"
#include <iterator>
@@ -130,4 +130,4 @@ public:
static char const* ToDescription(Enum value) { return ToString(value).Description; }
};
#endif
#endif // AC_SMART_ENUM_H
+14
View File
@@ -72,8 +72,22 @@ namespace Acore
std::string const& logger, std::string const& input,
bool secure)
{
#if AC_COMPILER == AC_COMPILER_MICROSOFT
#pragma warning(push)
#pragma warning(disable:4297)
/*
Silence warning with boost 1.83
boost/process/pipe.hpp(132,5): warning C4297: 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf': function assumed not to throw an exception but does
boost/process/pipe.hpp(132,5): message : destructor or deallocator has a (possibly implicit) non-throwing exception specification
boost/process/pipe.hpp(124,6): message : while compiling class template member function 'boost::process::basic_pipebuf<char,std::char_traits<char>>::~basic_pipebuf(void)'
boost/process/pipe.hpp(304,42): message : see reference to class template instantiation 'boost::process::basic_pipebuf<char,std::char_traits<char>>' being compiled
*/
#endif
ipstream outStream;
ipstream errStream;
#if AC_COMPILER == AC_COMPILER_MICROSOFT
#pragma warning(pop)
#endif
if (!secure)
{
+14 -2
View File
@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008+ TrinityCore <http://www.trinitycore.org/>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "TaskScheduler.h"
+17 -5
View File
@@ -1,6 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
* Copyright (C) 2008+ TrinityCore <http://www.trinitycore.org/>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 _TASK_SCHEDULER_H_
@@ -432,11 +444,11 @@ public:
: _task(right._task), _owner(right._owner), _consumed(right._consumed) { }
// Move construct
TaskContext(TaskContext&& right)
TaskContext(TaskContext&& right) noexcept
: _task(std::move(right._task)), _owner(std::move(right._owner)), _consumed(std::move(right._consumed)) { }
// Copy assign
TaskContext& operator= (TaskContext const& right)
TaskContext& operator= (TaskContext const& right) noexcept
{
_task = right._task;
_owner = right._owner;
@@ -445,7 +457,7 @@ public:
}
// Move assign
TaskContext& operator= (TaskContext&& right)
TaskContext& operator= (TaskContext&& right) noexcept
{
_task = std::move(right._task);
_owner = std::move(right._owner);
-5
View File
@@ -85,8 +85,6 @@ int main(int argc, char** argv)
if (!sConfigMgr->LoadAppConfigs())
return 1;
std::vector<std::string> overriddenKeys = sConfigMgr->OverrideWithEnvVariablesIfAny();
// Init logging
sLog->RegisterAppender<AppenderDB>();
sLog->Initialize(nullptr);
@@ -103,9 +101,6 @@ int main(int argc, char** argv)
LOG_INFO("server.authserver", "> Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
});
for (std::string const& key : overriddenKeys)
LOG_INFO("server.authserver", "Configuration field {} was overridden with environment variable.", key);
OpenSSLCrypto::threadsSetup();
std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
@@ -45,6 +45,7 @@ enum AuthStatus
STATUS_CLOSED
};
// cppcheck-suppress ctuOneDefinitionRuleViolation
struct AccountInfo
{
void LoadResult(Field* fields);
-5
View File
@@ -184,8 +184,6 @@ int main(int argc, char** argv)
if (!sConfigMgr->LoadAppConfigs())
return 1;
std::vector<std::string> overriddenKeys = sConfigMgr->OverrideWithEnvVariablesIfAny();
std::shared_ptr<Acore::Asio::IoContext> ioContext = std::make_shared<Acore::Asio::IoContext>();
// Init all logs
@@ -205,9 +203,6 @@ int main(int argc, char** argv)
LOG_INFO("server.worldserver", "> Using Boost version: {}.{}.{}", BOOST_VERSION / 100000, BOOST_VERSION / 100 % 1000, BOOST_VERSION % 100);
});
for (std::string const& key : overriddenKeys)
LOG_INFO("server.worldserver", "Configuration field {} was overridden with environment variable.", key);
OpenSSLCrypto::threadsSetup();
std::shared_ptr<void> opensslHandle(nullptr, [](void*) { OpenSSLCrypto::threadsCleanup(); });
File diff suppressed because it is too large Load Diff
@@ -71,7 +71,7 @@ void CharacterDatabaseConnection::DoPrepareStatements()
"resettalents_time, trans_x, trans_y, trans_z, trans_o, transguid, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, instance_mode_mask, "
"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, "
"knownTitles, actionBars, grantableLevels, innTriggerId, extraBonusTalentCount FROM characters WHERE guid = ?", CONNECTION_ASYNC);
"knownTitles, actionBars, grantableLevels, innTriggerId, extraBonusTalentCount, UNIX_TIMESTAMP(creation_date) FROM characters WHERE guid = ?", CONNECTION_ASYNC);
PrepareStatement(CHAR_SEL_CHARACTER_AURAS, "SELECT casterGuid, itemGuid, spell, effectMask, recalculateMask, stackCount, amount0, amount1, amount2, "
"base_amount0, base_amount1, base_amount2, maxDuration, remainTime, remainCharges FROM character_aura WHERE guid = ?", CONNECTION_ASYNC);
+1 -1
View File
@@ -82,7 +82,7 @@ protected:
Creature* DoSummon(uint32 entry, WorldObject* obj, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature* DoSummonFlyer(uint32 entry, WorldObject* obj, float flightZ, float radius = 5.0f, uint32 despawnTime = 30000, TempSummonType summonType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
public:
// EnumUtils: DESCRIBE THIS
// EnumUtils: DESCRIBE THIS (in CreatureAI::)
enum EvadeReason
{
EVADE_REASON_NO_HOSTILES, // the creature's threat list is empty
+1 -1
View File
@@ -601,7 +601,7 @@ void SmartAI::MovepointReached(uint32 id)
if (mLastWP)
{
me->SetPosition(mLastWP->x, mLastWP->y, mLastWP->z, me->GetOrientation());
me->SetHomePosition(me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
me->SetHomePosition(me->GetPosition());
}
if (HasEscortState(SMART_ESCORT_PAUSED))
+92 -73
View File
@@ -593,9 +593,7 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
// may be nullptr
if (go)
{
go->CastSpell(target->ToUnit(), e.action.cast.spell);
}
if (!IsUnit(target))
continue;
@@ -604,29 +602,24 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
{
caster->CastSpell(target->ToUnit(), e.action.cast.spell, (e.action.cast.castFlags & SMARTCAST_TRIGGERED));
}
else if (me && (!(e.action.cast.castFlags & SMARTCAST_AURA_NOT_PRESENT) || !target->ToUnit()->HasAura(e.action.cast.spell)))
else if (me)
{
if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
{
me->InterruptNonMeleeSpells(false);
}
// If target has the aura, skip
if ((e.action.cast.castFlags & SMARTCAST_AURA_NOT_PRESENT) && target->ToUnit()->HasAura(e.action.cast.spell))
continue;
// If the threatlist is a singleton, cancel
if (e.action.cast.castFlags & SMARTCAST_THREATLIST_NOT_SINGLE)
if (me->GetThreatMgr().GetThreatListSize() <= 1)
break;
TriggerCastFlags triggerFlags = TRIGGERED_NONE;
if (e.action.cast.castFlags & SMARTCAST_TRIGGERED)
{
if (e.action.cast.triggerFlags)
{
triggerFlags = TriggerCastFlags(e.action.cast.triggerFlags);
}
else
{
triggerFlags = TRIGGERED_FULL_MASK;
}
}
// If target does not use mana, skip
if ((e.action.cast.castFlags & SMARTCAST_TARGET_POWER_MANA) && !target->ToUnit()->GetPower(POWER_MANA))
continue;
// Interrupts current spellcast
if (e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS)
me->InterruptNonMeleeSpells(false);
// Flag usable only if caster has max dist set.
if ((e.action.cast.castFlags & SMARTCAST_COMBAT_MOVE) && GetCasterMaxDist() > 0.0f && me->GetMaxPower(GetCasterPowerType()) > 0)
@@ -647,15 +640,19 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
}
}
TriggerCastFlags triggerFlags = TRIGGERED_NONE;
if (e.action.cast.castFlags & SMARTCAST_TRIGGERED)
{
if (e.action.cast.triggerFlags)
triggerFlags = TriggerCastFlags(e.action.cast.triggerFlags);
else
triggerFlags = TRIGGERED_FULL_MASK;
}
me->CastSpell(target->ToUnit(), e.action.cast.spell, triggerFlags);
LOG_DEBUG("scripts.ai", "SmartScript::ProcessAction:: SMART_ACTION_CAST: Unit {} casts spell {} on target {} with castflags {}",
me->GetGUID().ToString(), e.action.cast.spell, target->GetGUID().ToString(), e.action.cast.castFlags);
}
else
{
LOG_DEBUG("scripts.ai", "Spell {} not cast because it has flag SMARTCAST_AURA_NOT_PRESENT and the target {} already has the aura",
e.action.cast.spell, target->GetGUID().ToString());
}
}
break;
}
@@ -2991,6 +2988,47 @@ void SmartScript::ProcessAction(SmartScriptHolder& e, Unit* unit, uint32 var0, u
break;
}
case SMART_ACTION_SET_ORIENTATION_TARGET:
{
switch (e.action.orientationTarget.type)
{
case 0: // Reset
{
for (WorldObject* target : targets)
target->ToCreature()->SetFacingTo((target->ToCreature()->HasUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT) && target->ToCreature()->GetTransGUID() ? target->ToCreature()->GetTransportHomePosition() : target->ToCreature()->GetHomePosition()).GetOrientation());
break;
}
case 1: // Target target.o
{
for (WorldObject* target : targets)
target->ToCreature()->SetFacingTo(e.target.o);
break;
}
case 2: // Target source
{
for (WorldObject* target : targets)
target->ToCreature()->SetFacingToObject(me);
break;
}
case 3: // Target parameters
{
ObjectVector facingTargets;
GetTargets(facingTargets, CreateSmartEvent(SMART_EVENT_UPDATE_IC, 0, 0, 0, 0, 0, 0, 0, SMART_ACTION_NONE, 0, 0, 0, 0, 0, 0, (SMARTAI_TARGETS)e.action.orientationTarget.targetType, e.action.orientationTarget.targetParam1, e.action.orientationTarget.targetParam2, e.action.orientationTarget.targetParam3, e.action.orientationTarget.targetParam4, 0), unit);
for (WorldObject* facingTarget : facingTargets)
for (WorldObject* target : targets)
target->ToCreature()->SetFacingToObject(facingTarget);
break;
}
default:
break;
}
break;
}
default:
LOG_ERROR("sql.sql", "SmartScript::ProcessAction: Entry {} SourceType {}, Event {}, Unhandled Action type {}", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
break;
@@ -3162,7 +3200,7 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MaxThreat, 1, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
targets.push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MaxThreat, 1, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MaxThreat, 1, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly, true, -e.target.hostileRandom.aura))
targets.push_back(u);
}
break;
@@ -3174,7 +3212,7 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinThreat, 0, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
targets.push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinThreat, 0, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::MinThreat, 0, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly, true, -e.target.hostileRandom.aura))
targets.push_back(u);
}
break;
@@ -3186,7 +3224,7 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 0, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
targets.push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 0, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 0, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly, true, -e.target.hostileRandom.aura))
targets.push_back(u);
}
break;
@@ -3198,7 +3236,7 @@ void SmartScript::GetTargets(ObjectVector& targets, SmartScriptHolder const& e,
if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 1, PowerUsersSelector(me, Powers(e.target.hostileRandom.powerType - 1), (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly)))
targets.push_back(u);
}
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 1, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly))
else if (Unit* u = me->AI()->SelectTarget(SelectTargetMethod::Random, 1, (float)e.target.hostileRandom.maxDist, e.target.hostileRandom.playerOnly, true, -e.target.hostileRandom.aura))
targets.push_back(u);
}
break;
@@ -3785,16 +3823,8 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (!me || !me->IsEngaged() || !me->GetVictim())
return;
if (me->IsInRange(me->GetVictim(), (float)e.event.rangeRepeat.minRange, (float)e.event.rangeRepeat.maxRange))
{
if (e.event.rangeRepeat.onlyFireOnRepeat == 2)
{
e.event.rangeRepeat.onlyFireOnRepeat = 1;
RecalcTimer(e, e.event.rangeRepeat.repeatMin, e.event.rangeRepeat.repeatMax);
}
else
ProcessTimedAction(e, e.event.rangeRepeat.repeatMin, e.event.rangeRepeat.repeatMax, me->GetVictim());
}
if (me->IsInRange(me->GetVictim(), (float)e.event.minMaxRepeat.rangeMin, (float)e.event.minMaxRepeat.rangeMax))
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, me->GetVictim());
else
RecalcTimer(e, 1200, 1200); // make it predictable
@@ -3947,9 +3977,11 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (Unit* victim = me->GetVictim())
{
if (!victim->HasInArc(static_cast<float>(M_PI), me))
ProcessTimedAction(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax, victim);
if (e.event.minMaxRepeat.rangeMax && (me->IsInRange(victim, (float)e.event.minMaxRepeat.rangeMin, (float)e.event.minMaxRepeat.rangeMax)))
if (!victim->HasInArc(static_cast<float>(M_PI), me))
ProcessTimedAction(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax, victim);
}
break;
}
case SMART_EVENT_RECEIVE_EMOTE:
@@ -4229,7 +4261,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
if (IsUnit(target) && me->IsFriendlyTo(target->ToUnit()) && target->ToUnit()->IsAlive() && target->ToUnit()->IsInCombat())
{
uint32 healthPct = uint32(target->ToUnit()->GetHealthPct());
if (healthPct > e.event.friendlyHealthPct.maxHpPct || healthPct < e.event.friendlyHealthPct.minHpPct)
if (healthPct > e.event.friendlyHealthPct.hpPct)
{
continue;
}
@@ -4243,7 +4275,7 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
}
case SMART_TARGET_SELF:
case SMART_TARGET_ACTION_INVOKER:
unitTarget = DoSelectLowestHpPercentFriendly((float)e.event.friendlyHealthPct.radius, e.event.friendlyHealthPct.minHpPct, e.event.friendlyHealthPct.maxHpPct);
unitTarget = DoSelectLowestHpPercentFriendly((float)e.event.friendlyHealthPct.radius, 0, e.event.friendlyHealthPct.hpPct);
break;
default:
return;
@@ -4435,14 +4467,14 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
{
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
{
if (!target || target->IsPet() || target->IsTotem() || !target->IsNonMeleeSpellCast(false, false, true))
if (!target || !IsPlayer(target) || !target->IsNonMeleeSpellCast(false, false, true))
continue;
if (e.event.areaCasting.rangeMin && !(me->IsInRange(target, (float)e.event.areaCasting.rangeMin, (float)e.event.areaCasting.rangeMax)))
if (!(me->IsInRange(target, (float)e.event.minMaxRepeat.rangeMin, (float)e.event.minMaxRepeat.rangeMax)))
continue;
ProcessAction(e, target);
RecalcTimer(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax);
RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
return;
}
@@ -4463,11 +4495,11 @@ void SmartScript::ProcessEvent(SmartScriptHolder& e, Unit* unit, uint32 var0, ui
{
if (Unit* target = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()))
{
if (!(me->IsInRange(target, (float)e.event.areaRange.rangeMin, (float)e.event.areaRange.rangeMax)))
if (!(me->IsInRange(target, (float)e.event.minMaxRepeat.rangeMin, (float)e.event.minMaxRepeat.rangeMax)))
continue;
ProcessAction(e, target);
RecalcTimer(e, e.event.areaRange.repeatMin, e.event.areaRange.repeatMax);
RecalcTimer(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax);
return;
}
}
@@ -4487,16 +4519,6 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
switch (e.GetEventType())
{
//set only events which have initial timers
case SMART_EVENT_RANGE:
// If onlyFireOnRepeat is true set to 2 before entering combat. Will be set back to 1 after entering combat to ignore initial firing.
if (e.event.rangeRepeat.onlyFireOnRepeat == 1)
e.event.rangeRepeat.onlyFireOnRepeat = 2;
// make it predictable
RecalcTimer(e, 1200, 1200);
break;
case SMART_EVENT_AREA_RANGE:
RecalcTimer(e, e.event.areaRange.min, e.event.areaRange.max);
break;
case SMART_EVENT_NEAR_PLAYERS:
case SMART_EVENT_NEAR_PLAYERS_NEGATION:
RecalcTimer(e, e.event.nearPlayer.firstTimer, e.event.nearPlayer.firstTimer);
@@ -4504,13 +4526,13 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
case SMART_EVENT_UPDATE:
case SMART_EVENT_UPDATE_IC:
case SMART_EVENT_UPDATE_OOC:
case SMART_EVENT_RANGE:
case SMART_EVENT_AREA_RANGE:
case SMART_EVENT_AREA_CASTING:
case SMART_EVENT_IS_BEHIND_TARGET:
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
RecalcTimer(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max);
break;
case SMART_EVENT_OOC_LOS:
case SMART_EVENT_IC_LOS:
// Xinef: cooldown should be processed AFTER action is done, not before...
//RecalcTimer(e, e.event.los.cooldownMin, e.event.los.cooldownMax);
//break;
case SMART_EVENT_DISTANCE_CREATURE:
case SMART_EVENT_DISTANCE_GAMEOBJECT:
RecalcTimer(e, e.event.distance.repeat, e.event.distance.repeat);
@@ -4519,9 +4541,6 @@ void SmartScript::InitTimer(SmartScriptHolder& e)
case SMART_EVENT_NEAR_UNIT_NEGATION:
RecalcTimer(e, e.event.nearUnit.timer, e.event.nearUnit.timer);
break;
case SMART_EVENT_AREA_CASTING:
RecalcTimer(e, e.event.areaCasting.min, e.event.areaCasting.max);
break;
default:
e.active = true;
break;
@@ -4550,14 +4569,14 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
if (e.timer < diff)
{
// delay spell cast event if another spell is being casted
// delay spell cast for another AI tick if another spell is being cast
if (e.GetActionType() == SMART_ACTION_CAST)
{
if (!(e.action.cast.castFlags & SMARTCAST_INTERRUPT_PREVIOUS))
{
if (me && me->HasUnitState(UNIT_STATE_CASTING))
{
e.timer = 1;
e.timer = 1200;
return;
}
}
@@ -4566,9 +4585,9 @@ void SmartScript::UpdateTimer(SmartScriptHolder& e, uint32 const diff)
// Delay flee for assist event if casting
if (e.GetActionType() == SMART_ACTION_FLEE_FOR_ASSIST && me && me->HasUnitState(UNIT_STATE_CASTING))
{
e.timer = 1;
e.timer = 1200;
return;
}
} // @TODO: Can't these be handled by the action themselves instead? Less expensive
e.active = true;//activate events with cooldown
switch (e.GetEventType())//process ONLY timed events
@@ -4809,10 +4828,10 @@ void SmartScript::OnInitialize(WorldObject* obj, AreaTrigger const* at)
InitTimer((*i));//calculate timers for first time use
if (i->GetEventType() == SMART_EVENT_RANGE && i->GetActionType() == SMART_ACTION_ALLOW_COMBAT_MOVEMENT)
{
if (i->action.combatMove.move == 1 && i->event.rangeRepeat.minRange > minEnableDist)
minEnableDist = i->event.rangeRepeat.minRange;
else if (i->action.combatMove.move == 0 && (i->event.rangeRepeat.maxRange < maxDisableDist || maxDisableDist == 0))
maxDisableDist = i->event.rangeRepeat.maxRange;
if (i->action.combatMove.move == 1 && i->event.minMaxRepeat.rangeMin > minEnableDist)
minEnableDist = i->event.minMaxRepeat.rangeMin;
else if (i->action.combatMove.move == 0 && (i->event.minMaxRepeat.rangeMax < maxDisableDist || maxDisableDist == 0))
maxDisableDist = i->event.minMaxRepeat.rangeMax;
}
// Xinef: if smartcast combat move flag is present
@@ -264,29 +264,14 @@ void SmartAIMgr::LoadSmartAIFromDB()
case SMART_EVENT_FRIENDLY_MISSING_BUFF:
case SMART_EVENT_HAS_AURA:
case SMART_EVENT_TARGET_BUFFED:
case SMART_EVENT_RANGE:
case SMART_EVENT_AREA_RANGE:
case SMART_EVENT_AREA_CASTING:
case SMART_EVENT_IS_BEHIND_TARGET:
if (temp.event.minMaxRepeat.repeatMin == 0 && temp.event.minMaxRepeat.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
break;
case SMART_EVENT_RANGE:
if (temp.event.rangeRepeat.repeatMin == 0 && temp.event.rangeRepeat.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
// Will only work properly if value is 0 or 1
if (temp.event.rangeRepeat.onlyFireOnRepeat > 1)
temp.event.rangeRepeat.onlyFireOnRepeat = 1;
break;
case SMART_EVENT_AREA_RANGE:
if (temp.event.areaRange.repeatMin == 0 && temp.event.areaRange.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
break;
case SMART_EVENT_VICTIM_CASTING:
case SMART_EVENT_IS_BEHIND_TARGET:
if (temp.event.minMaxRepeat.min == 0 && temp.event.minMaxRepeat.max == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
break;
case SMART_EVENT_AREA_CASTING:
if (temp.event.areaCasting.repeatMin == 0 && temp.event.areaCasting.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
break;
case SMART_EVENT_FRIENDLY_IS_CC:
if (temp.event.friendlyCC.repeatMin == 0 && temp.event.friendlyCC.repeatMax == 0)
temp.event.event_flags |= SMART_EVENT_FLAG_NOT_REPEATABLE;
@@ -506,7 +491,7 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
case SMART_EVENT_DEATH: return NO_PARAMS;
case SMART_EVENT_EVADE: return NO_PARAMS;
case SMART_EVENT_SPELLHIT: return sizeof(SmartEvent::spellHit);
case SMART_EVENT_RANGE: return sizeof(SmartEvent::rangeRepeat);
case SMART_EVENT_RANGE: return sizeof(SmartEvent::minMaxRepeat);
case SMART_EVENT_OOC_LOS: return sizeof(SmartEvent::los);
case SMART_EVENT_RESPAWN: return sizeof(SmartEvent::respawn);
case SMART_EVENT_TARGET_HEALTH_PCT: return sizeof(SmartEvent::minMaxRepeat);
@@ -564,7 +549,7 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
case SMART_EVENT_GOSSIP_HELLO: return sizeof(SmartEvent::gossipHello);
case SMART_EVENT_FOLLOW_COMPLETED: return NO_PARAMS;
case SMART_EVENT_EVENT_PHASE_CHANGE: return sizeof(SmartEvent::eventPhaseChange);
case SMART_EVENT_IS_BEHIND_TARGET: return sizeof(SmartEvent::behindTarget);
case SMART_EVENT_IS_BEHIND_TARGET: return sizeof(SmartEvent::minMaxRepeat);
case SMART_EVENT_GAME_EVENT_START: return sizeof(SmartEvent::gameEvent);
case SMART_EVENT_GAME_EVENT_END: return sizeof(SmartEvent::gameEvent);
case SMART_EVENT_GO_STATE_CHANGED: return sizeof(SmartEvent::goStateChanged);
@@ -584,8 +569,8 @@ bool SmartAIMgr::CheckUnusedEventParams(SmartScriptHolder const& e)
case SMART_EVENT_NEAR_PLAYERS_NEGATION: return sizeof(SmartEvent::nearPlayerNegation);
case SMART_EVENT_NEAR_UNIT: return sizeof(SmartEvent::nearUnit);
case SMART_EVENT_NEAR_UNIT_NEGATION: return sizeof(SmartEvent::nearUnitNegation);
case SMART_EVENT_AREA_CASTING: return sizeof(SmartEvent::areaCasting);
case SMART_EVENT_AREA_RANGE: return sizeof(SmartEvent::areaRange);
case SMART_EVENT_AREA_CASTING: return sizeof(SmartEvent::minMaxRepeat);
case SMART_EVENT_AREA_RANGE: return sizeof(SmartEvent::minMaxRepeat);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an event {} with no unused params specified in SmartAIMgr::CheckUnusedEventParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType(), e.GetEventType());
@@ -781,6 +766,7 @@ bool SmartAIMgr::CheckUnusedActionParams(SmartScriptHolder const& e)
case SMART_ACTION_SUMMON_RADIAL: return sizeof(SmartAction::radialSummon);
case SMART_ACTION_PLAY_SPELL_VISUAL: return sizeof(SmartAction::spellVisual);
case SMART_ACTION_FOLLOW_GROUP: return sizeof(SmartAction::followGroup);
case SMART_ACTION_SET_ORIENTATION_TARGET: return sizeof(SmartAction::orientationTarget);
default:
LOG_WARN("sql.sql", "SmartAIMgr: entryorguid {} source_type {} id {} action_type {} is using an action with no unused params specified in SmartAIMgr::CheckUnusedActionParams(), please report this.",
e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
@@ -963,21 +949,17 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax))
return false;
break;
case SMART_EVENT_RANGE:
if (!IsMinMaxValid(e, e.event.rangeRepeat.minRange, e.event.rangeRepeat.maxRange))
return false;
if (!IsMinMaxValid(e, e.event.rangeRepeat.repeatMin, e.event.rangeRepeat.repeatMax))
return false;
break;
case SMART_EVENT_AREA_RANGE:
if (!IsMinMaxValid(e, e.event.areaRange.min, e.event.areaRange.max))
case SMART_EVENT_AREA_CASTING:
case SMART_EVENT_IS_BEHIND_TARGET:
case SMART_EVENT_RANGE:
if (!IsMinMaxValid(e, e.event.minMaxRepeat.min, e.event.minMaxRepeat.max))
return false;
if (!IsMinMaxValid(e, e.event.areaRange.repeatMin, e.event.areaRange.repeatMax))
if (!IsMinMaxValid(e, e.event.minMaxRepeat.repeatMin, e.event.minMaxRepeat.repeatMax))
return false;
if (!IsMinMaxValid(e, e.event.areaRange.rangeMin, e.event.areaRange.rangeMax))
if (!IsMinMaxValid(e, e.event.minMaxRepeat.rangeMin, e.event.minMaxRepeat.rangeMax))
return false;
break;
@@ -1070,16 +1052,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
if (!IsMinMaxValid(e, e.event.targetCasting.repeatMin, e.event.targetCasting.repeatMax))
return false;
break;
case SMART_EVENT_AREA_CASTING:
if (!IsMinMaxValid(e, e.event.areaCasting.min, e.event.areaCasting.max))
return false;
if (!IsMinMaxValid(e, e.event.areaCasting.repeatMin, e.event.areaCasting.repeatMax))
return false;
if (!IsMinMaxValid(e, e.event.areaCasting.rangeMin, e.event.areaCasting.rangeMax))
return false;
break;
case SMART_EVENT_PASSENGER_BOARDED:
case SMART_EVENT_PASSENGER_REMOVED:
if (!IsMinMaxValid(e, e.event.minMax.repeatMin, e.event.minMax.repeatMax))
@@ -1185,10 +1157,6 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
}
break;
}
case SMART_EVENT_IS_BEHIND_TARGET:
if (!IsMinMaxValid(e, e.event.behindTarget.cooldownMin, e.event.behindTarget.cooldownMax))
return false;
break;
case SMART_EVENT_GAME_EVENT_START:
case SMART_EVENT_GAME_EVENT_END:
{
@@ -1207,10 +1175,13 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
break;
}
case SMART_EVENT_FRIENDLY_HEALTH_PCT:
if (!IsMinMaxValid(e, e.event.friendlyHealthPct.min, e.event.friendlyHealthPct.max))
return false;
if (!IsMinMaxValid(e, e.event.friendlyHealthPct.repeatMin, e.event.friendlyHealthPct.repeatMax))
return false;
if (e.event.friendlyHealthPct.maxHpPct > 100 || e.event.friendlyHealthPct.minHpPct > 100)
if (e.event.friendlyHealthPct.hpPct > 100)
{
LOG_ERROR("sql.sql", "SmartAIMgr: Entry {} SourceType {} Event {} Action {} has pct value above 100, skipped.", e.entryOrGuid, e.GetScriptType(), e.event_id, e.GetActionType());
return false;
@@ -1971,6 +1942,7 @@ bool SmartAIMgr::IsEventValid(SmartScriptHolder& e)
case SMART_ACTION_SUMMON_RADIAL:
case SMART_ACTION_PLAY_SPELL_VISUAL:
case SMART_ACTION_FOLLOW_GROUP:
case SMART_ACTION_SET_ORIENTATION_TARGET:
break;
default:
LOG_ERROR("sql.sql", "SmartAIMgr: Not handled action_type({}), event_type({}), Entry {} SourceType {} Event {}, skipped.", e.GetActionType(), e.GetEventType(), e.entryOrGuid, e.GetScriptType(), e.event_id);
@@ -124,7 +124,7 @@ enum SMART_EVENT
SMART_EVENT_DEATH = 6, // NONE
SMART_EVENT_EVADE = 7, // NONE
SMART_EVENT_SPELLHIT = 8, // SpellID, School, CooldownMin, CooldownMax
SMART_EVENT_RANGE = 9, // minRange, maxRange, repeatMin, repeatMax, onlyFireOnRepeat
SMART_EVENT_RANGE = 9, // min, max, repeatMin, repeatMax, rangeMin, rangeMax
SMART_EVENT_OOC_LOS = 10, // HostilityMode, MaxRnage, CooldownMin, CooldownMax, PlayerOnly
SMART_EVENT_RESPAWN = 11, // type, MapId, ZoneId
SMART_EVENT_TARGET_HEALTH_PCT = 12, // HPMin%, HPMax%, RepeatMin, RepeatMax
@@ -182,14 +182,14 @@ enum SMART_EVENT
SMART_EVENT_GOSSIP_HELLO = 64, // event_para_1 (only) 0 = no filter set, always execute action, 1 = GossipHello only filter set, skip action if reportUse, 2 = reportUse only filter set, skip action if GossipHello
SMART_EVENT_FOLLOW_COMPLETED = 65, // none
SMART_EVENT_EVENT_PHASE_CHANGE = 66, // event phase mask (<= SMART_EVENT_PHASE_ALL)
SMART_EVENT_IS_BEHIND_TARGET = 67, // cooldownMin, CooldownMax
SMART_EVENT_IS_BEHIND_TARGET = 67, // min, max, repeatMin, repeatMax, rangeMin, rangeMax
SMART_EVENT_GAME_EVENT_START = 68, // game_event.Entry
SMART_EVENT_GAME_EVENT_END = 69, // game_event.Entry
SMART_EVENT_GO_STATE_CHANGED = 70, // go state
SMART_EVENT_GO_EVENT_INFORM = 71, // eventId
SMART_EVENT_ACTION_DONE = 72, // eventId (SharedDefines.EventId)
SMART_EVENT_ON_SPELLCLICK = 73, // clicker (unit)
SMART_EVENT_FRIENDLY_HEALTH_PCT = 74, // minHpPct, maxHpPct, repeatMin, repeatMax
SMART_EVENT_FRIENDLY_HEALTH_PCT = 74, // min, max, repeatMin, repeatMax, hpPct, range
SMART_EVENT_DISTANCE_CREATURE = 75, // guid, entry, distance, repeat
SMART_EVENT_DISTANCE_GAMEOBJECT = 76, // guid, entry, distance, repeat
SMART_EVENT_COUNTER_SET = 77, // id, value, cooldownMin, cooldownMax
@@ -210,8 +210,8 @@ enum SMART_EVENT
SMART_EVENT_NEAR_PLAYERS_NEGATION = 102, // max, radius, first timer, repeatMin, repeatMax
SMART_EVENT_NEAR_UNIT = 103, // type (0: creature 1: gob), entry, count, range, timer
SMART_EVENT_NEAR_UNIT_NEGATION = 104, // type (0: creature 1: gob), entry, count, range, timer
SMART_EVENT_AREA_CASTING = 105, // min, max, repeatMin, repeatMax, range
SMART_EVENT_AREA_RANGE = 106, // min, max, repeatMin, repeatMax, range
SMART_EVENT_AREA_CASTING = 105, // min, max, repeatMin, repeatMax, rangeMin, rangeMax
SMART_EVENT_AREA_RANGE = 106, // min, max, repeatMin, repeatMax, rangeMin, rangeMax
SMART_EVENT_AC_END = 107
};
@@ -230,17 +230,10 @@ struct SmartEvent
uint32 max;
uint32 repeatMin;
uint32 repeatMax;
uint32 rangeMin;
uint32 rangeMax;
} minMaxRepeat;
struct
{
uint32 minRange;
uint32 maxRange;
uint32 repeatMin;
uint32 repeatMax;
uint32 onlyFireOnRepeat;
} rangeRepeat;
struct
{
uint32 cooldownMin;
@@ -430,12 +423,6 @@ struct SmartEvent
uint32 phasemask;
} eventPhaseChange;
struct
{
uint32 cooldownMin;
uint32 cooldownMax;
} behindTarget;
struct
{
uint32 gameEventId;
@@ -458,10 +445,11 @@ struct SmartEvent
struct
{
uint32 minHpPct;
uint32 maxHpPct;
uint32 min;
uint32 max;
uint32 repeatMin;
uint32 repeatMax;
uint32 hpPct;
uint32 radius;
} friendlyHealthPct;
@@ -517,26 +505,6 @@ struct SmartEvent
uint32 timer;
} nearUnitNegation;
struct
{
uint32 min;
uint32 max;
uint32 repeatMin;
uint32 repeatMax;
uint32 rangeMin;
uint32 rangeMax;
} areaCasting;
struct
{
uint32 min;
uint32 max;
uint32 repeatMin;
uint32 repeatMax;
uint32 rangeMin;
uint32 rangeMax;
} areaRange;
struct
{
uint32 param1;
@@ -743,8 +711,9 @@ enum SMART_ACTION
SMART_ACTION_SUMMON_RADIAL = 228, // summonEntry, summonDuration, repetitions, startAngle, stepAngle, dist
SMART_ACTION_PLAY_SPELL_VISUAL = 229, // visualId, visualIdImpact
SMART_ACTION_FOLLOW_GROUP = 230, // followState, followType, dist
SMART_ACTION_SET_ORIENTATION_TARGET = 231, // type, target_type, target_param1, target_param2, target_param3, target_param4
SMART_ACTION_AC_END = 231, // placeholder
SMART_ACTION_AC_END = 232, // placeholder
};
enum class SmartActionSummonCreatureFlags
@@ -1453,6 +1422,16 @@ struct SmartAction
uint32 followType;
uint32 dist;
} followGroup;
struct
{
uint32 type;
uint32 targetType;
uint32 targetParam1;
uint32 targetParam2;
uint32 targetParam3;
uint32 targetParam4;
} orientationTarget;
//! Note for any new future actions
//! All parameters must have type uint32
@@ -1484,10 +1463,10 @@ enum SMARTAI_TARGETS
SMART_TARGET_NONE = 0, // NONE
SMART_TARGET_SELF = 1, // Self cast
SMART_TARGET_VICTIM = 2, // Our current target (ie: highest aggro)
SMART_TARGET_HOSTILE_SECOND_AGGRO = 3, // Second highest aggro, maxdist, playerOnly, powerType + 1
SMART_TARGET_HOSTILE_LAST_AGGRO = 4, // Dead last on aggro, maxdist, playerOnly, powerType + 1
SMART_TARGET_HOSTILE_RANDOM = 5, // Just any random target on our threat list, maxdist, playerOnly, powerType + 1
SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 6, // Any random target except top threat, maxdist, playerOnly, powerType + 1
SMART_TARGET_HOSTILE_SECOND_AGGRO = 3, // Second highest aggro, maxdist, playerOnly, powerType + 1, missing aura
SMART_TARGET_HOSTILE_LAST_AGGRO = 4, // Dead last on aggro, maxdist, playerOnly, powerType + 1, missing aura
SMART_TARGET_HOSTILE_RANDOM = 5, // Just any random target on our threat list, maxdist, playerOnly, powerType + 1, missing aura
SMART_TARGET_HOSTILE_RANDOM_NOT_TOP = 6, // Any random target except top threat, maxdist, playerOnly, powerType + 1, missing aura
SMART_TARGET_ACTION_INVOKER = 7, // Unit who caused this Event to occur
SMART_TARGET_POSITION = 8, // use xyz from event params
SMART_TARGET_CREATURE_RANGE = 9, // CreatureEntry(0any), minDist, maxDist, alive(0 - both, 1 - alive, 2 - dead)
@@ -1551,6 +1530,7 @@ struct SmartTarget
uint32 maxDist;
SAIBool playerOnly;
uint32 powerType;
uint32 aura;
} hostileRandom;
struct
@@ -1864,16 +1844,16 @@ const uint32 SmartAIEventMask[SMART_EVENT_AC_END][2] =
enum SmartEventFlags
{
SMART_EVENT_FLAG_NOT_REPEATABLE = 0x001, //Event can not repeat
SMART_EVENT_FLAG_DIFFICULTY_0 = 0x002, //Event only occurs in instance difficulty 0
SMART_EVENT_FLAG_DIFFICULTY_1 = 0x004, //Event only occurs in instance difficulty 1
SMART_EVENT_FLAG_DIFFICULTY_2 = 0x008, //Event only occurs in instance difficulty 2
SMART_EVENT_FLAG_DIFFICULTY_3 = 0x010, //Event only occurs in instance difficulty 3
SMART_EVENT_FLAG_NOT_REPEATABLE = 0x001, // Event can not repeat
SMART_EVENT_FLAG_DIFFICULTY_0 = 0x002, // Event only occurs in instance difficulty 0
SMART_EVENT_FLAG_DIFFICULTY_1 = 0x004, // Event only occurs in instance difficulty 1
SMART_EVENT_FLAG_DIFFICULTY_2 = 0x008, // Event only occurs in instance difficulty 2
SMART_EVENT_FLAG_DIFFICULTY_3 = 0x010, // Event only occurs in instance difficulty 3
SMART_EVENT_FLAG_RESERVED_5 = 0x020,
SMART_EVENT_FLAG_RESERVED_6 = 0x040,
SMART_EVENT_FLAG_DEBUG_ONLY = 0x080, //Event only occurs in debug build
SMART_EVENT_FLAG_DONT_RESET = 0x100, //Event will not reset in SmartScript::OnReset()
SMART_EVENT_FLAG_WHILE_CHARMED = 0x200, //Event occurs even if AI owner is charmed
SMART_EVENT_FLAG_DEBUG_ONLY = 0x080, // Event only occurs in debug build
SMART_EVENT_FLAG_DONT_RESET = 0x100, // Event will not reset in SmartScript::OnReset()
SMART_EVENT_FLAG_WHILE_CHARMED = 0x200, // Event occurs even if AI owner is charmed
SMART_EVENT_FLAG_DIFFICULTY_ALL = (SMART_EVENT_FLAG_DIFFICULTY_0 | SMART_EVENT_FLAG_DIFFICULTY_1 | SMART_EVENT_FLAG_DIFFICULTY_2 | SMART_EVENT_FLAG_DIFFICULTY_3),
SMART_EVENT_FLAGS_ALL = (SMART_EVENT_FLAG_NOT_REPEATABLE | SMART_EVENT_FLAG_DIFFICULTY_ALL | SMART_EVENT_FLAG_RESERVED_5 | SMART_EVENT_FLAG_RESERVED_6 | SMART_EVENT_FLAG_DEBUG_ONLY | SMART_EVENT_FLAG_DONT_RESET | SMART_EVENT_FLAG_WHILE_CHARMED)
@@ -1881,14 +1861,15 @@ enum SmartEventFlags
enum SmartCastFlags
{
SMARTCAST_INTERRUPT_PREVIOUS = 0x01, //Interrupt any spell casting
SMARTCAST_TRIGGERED = 0x02, //Triggered (this makes spell cost zero mana and have no cast time)
//CAST_FORCE_CAST = 0x04, //Forces cast even if creature is out of mana or out of range
//CAST_NO_MELEE_IF_OOM = 0x08, //Prevents creature from entering melee if out of mana or out of range
//CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself
SMARTCAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell
SMARTCAST_COMBAT_MOVE = 0x40, //Prevents combat movement if cast successful. Allows movement on range, OOM, LOS
SMARTCAST_THREATLIST_NOT_SINGLE = 0x80 //Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment)
SMARTCAST_INTERRUPT_PREVIOUS = 0x001, // Interrupt any spell casting
SMARTCAST_TRIGGERED = 0x002, // Triggered (this makes spell cost zero mana and have no cast time)
//CAST_FORCE_CAST = 0x004, // Forces cast even if creature is out of mana or out of range
//CAST_NO_MELEE_IF_OOM = 0x008, // Prevents creature from entering melee if out of mana or out of range
//CAST_FORCE_TARGET_SELF = 0x010, // Forces the target to cast this spell on itself
SMARTCAST_AURA_NOT_PRESENT = 0x020, // Only casts the spell if the target does not have an aura from the spell
SMARTCAST_COMBAT_MOVE = 0x040, // Prevents combat movement if cast successful. Allows movement on range, OOM, LOS
SMARTCAST_THREATLIST_NOT_SINGLE = 0x080, // Only cast if the source's threatlist is higher than one. This includes pets (see Skeram's True Fulfillment)
SMARTCAST_TARGET_POWER_MANA = 0x100 // Only cast if the target has power type mana (e.g. Mana Drain)
};
enum SmartFollowType
@@ -0,0 +1,70 @@
/*
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero 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 "CreatureAI.h"
#include "Define.h"
#include "SmartEnum.h"
#include <stdexcept>
namespace Acore::Impl::EnumUtilsImpl
{
/****************************************************************************\
|* data for enum 'CreatureAI::EvadeReason' in 'CreatureAI.h' auto-generated *|
\****************************************************************************/
template <>
AC_API_EXPORT EnumText EnumUtils<CreatureAI::EvadeReason>::ToString(CreatureAI::EvadeReason value)
{
switch (value)
{
case CreatureAI::EVADE_REASON_NO_HOSTILES: return { "EVADE_REASON_NO_HOSTILES", "EVADE_REASON_NO_HOSTILES", "the creature's threat list is empty" };
case CreatureAI::EVADE_REASON_BOUNDARY: return { "EVADE_REASON_BOUNDARY", "EVADE_REASON_BOUNDARY", "the creature has moved outside its evade boundary" };
case CreatureAI::EVADE_REASON_SEQUENCE_BREAK: return { "EVADE_REASON_SEQUENCE_BREAK", "EVADE_REASON_SEQUENCE_BREAK", "this is a boss and the pre-requisite encounters for engaging it are not defeated yet" };
case CreatureAI::EVADE_REASON_NO_PATH: return { "EVADE_REASON_NO_PATH", "EVADE_REASON_NO_PATH", "the creature was unable to reach its target for over 5 seconds" };
default: throw std::out_of_range("value");
}
}
template <>
AC_API_EXPORT size_t EnumUtils<CreatureAI::EvadeReason>::Count() { return 4; }
template <>
AC_API_EXPORT CreatureAI::EvadeReason EnumUtils<CreatureAI::EvadeReason>::FromIndex(size_t index)
{
switch (index)
{
case 0: return CreatureAI::EVADE_REASON_NO_HOSTILES;
case 1: return CreatureAI::EVADE_REASON_BOUNDARY;
case 2: return CreatureAI::EVADE_REASON_SEQUENCE_BREAK;
case 3: return CreatureAI::EVADE_REASON_NO_PATH;
default: throw std::out_of_range("index");
}
}
template <>
AC_API_EXPORT size_t EnumUtils<CreatureAI::EvadeReason>::ToIndex(CreatureAI::EvadeReason value)
{
switch (value)
{
case CreatureAI::EVADE_REASON_NO_HOSTILES: return 0;
case CreatureAI::EVADE_REASON_BOUNDARY: return 1;
case CreatureAI::EVADE_REASON_SEQUENCE_BREAK: return 2;
case CreatureAI::EVADE_REASON_NO_PATH: return 3;
default: throw std::out_of_range("value");
}
}
}
@@ -82,6 +82,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
case ACHIEVEMENT_CRITERIA_TYPE_ON_LOGIN:
case ACHIEVEMENT_CRITERIA_TYPE_LOOT_EPIC_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
break;
default:
if (dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_SCRIPT)
@@ -757,7 +758,12 @@ void AchievementMgr::SendCriteriaUpdate(AchievementCriteriaEntry const* entry, C
data << uint32(timedCompleted ? 0 : 1); // 1 is for keeping the counter at 0 in client
data.AppendPackedTime(progress->date);
data << uint32(timeElapsed); // time elapsed in seconds
data << uint32(0); // unk
if (sAchievementMgr->IsAverageCriteria(entry))
data << uint32(GameTime::GetGameTime().count() - GetPlayer()->GetCreationTime().count()); // for average achievements
else
data << uint32(timeElapsed); // time elapsed in seconds
GetPlayer()->SendDirectMessage(&data);
}
@@ -881,6 +887,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
case ACHIEVEMENT_CRITERIA_TYPE_WON_AUCTIONS: /* FIXME: for online player only currently */
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_DISENCHANT:
case ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED:
case ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN:
case ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS:
@@ -1147,6 +1154,21 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
{
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if (!miscValue1)
continue;
Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : sMapMgr->FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
if (!map || !map->IsDungeon())
continue;
if (map->ToInstanceMap()->GetMaxPlayers() != achievementCriteria->complete_raid.groupSize)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if (!miscValue1)
@@ -1732,6 +1754,16 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
{
// those requirements couldn't be found in the dbc
AchievementCriteriaDataSet const* data = sAchievementMgr->GetCriteriaDataSet(achievementCriteria);
if (!data || !data->Meets(GetPlayer(), nullptr))
continue;
SetCriteriaProgress(achievementCriteria, 1);
break;
}
// std case: not exist in DBC, not triggered in code as result
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
@@ -1741,8 +1773,6 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING:
break;
// FIXME: not triggered in code as result, need to implement
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
case ACHIEVEMENT_CRITERIA_TYPE_TOTAL:
break; // Not implemented yet :(
}
@@ -1907,11 +1937,14 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve
return true;
case ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA:
return achievementCriteria->win_arena.count && progress->counter >= achievementCriteria->win_arena.count;
case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
return true;
// handle all statistic-only criteria here
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND:
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP:
case ACHIEVEMENT_CRITERIA_TYPE_DEATH:
case ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON:
case ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID:
case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER:
case ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM:
@@ -1936,6 +1969,7 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve
case ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_DISENCHANT:
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALTH:
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER:
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_ARMOR:
@@ -2344,7 +2378,11 @@ void AchievementMgr::BuildAllDataPacket(WorldPacket* data) const
*data << uint32(0); /// @todo: This should be 1 if it is a failed timed criteria
data->AppendPackedTime(iter->second.date);
*data << uint32(now - iter->second.date);
*data << uint32(now - iter->second.date);
if (sAchievementMgr->IsAverageCriteria(sAchievementCriteriaStore.LookupEntry(iter->first)))
*data << uint32(now - GetPlayer()->GetCreationTime().count()); // for average achievements
else
*data << uint32(now - iter->second.date);
}
*data << int32(-1);
@@ -2433,6 +2471,19 @@ bool AchievementGlobalMgr::IsStatisticAchievement(AchievementEntry const* achiev
return false;
}
bool AchievementGlobalMgr::IsAverageCriteria(AchievementCriteriaEntry const* criteria) const
{
if ((sAchievementStore.LookupEntry(criteria->referredAchievement))->flags & ACHIEVEMENT_FLAG_AVERAGE)
return true;
if (AchievementEntryList const* achRefList = GetAchievementByReferencedId(criteria->referredAchievement))
for (AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
if ((*itr)->flags & ACHIEVEMENT_FLAG_AVERAGE)
return true;
return false;
}
bool AchievementGlobalMgr::IsRealmCompleted(AchievementEntry const* achievement) const
{
auto itr = _allCompletedAchievements.find(achievement->ID);
@@ -2724,6 +2775,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
case ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL:
case ACHIEVEMENT_CRITERIA_TYPE_BG_OBJECTIVE_CAPTURE:
case ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL:
case ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK:
case ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT:
case ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT:
@@ -330,6 +330,7 @@ public:
bool IsStatisticCriteria(AchievementCriteriaEntry const* achievementCriteria) const;
bool IsStatisticAchievement(AchievementEntry const* achievement) const;
bool IsAverageCriteria(AchievementCriteriaEntry const* criteria) const;
[[nodiscard]] AchievementCriteriaEntryList const* GetAchievementCriteriaByType(AchievementCriteriaTypes type) const
{
@@ -761,6 +761,9 @@ bool AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
}
AuctionEntry* Aentry = itr->second;
if (!Aentry)
return false;
// Skip expired auctions
if (Aentry->expire_time < curTime.count())
{
@@ -122,4 +122,4 @@ protected:
uint32 HealingDone = 0;
};
#endif // TRINITY_BATTLEGROUND_SCORE_H
#endif // _BATTLEGROUND_SCORE_H
@@ -42,7 +42,7 @@ AC_API_EXPORT EnumText EnumUtils<CreatureFlagsExtra>::ToString(CreatureFlagsExtr
case CREATURE_FLAG_EXTRA_NO_TAUNT: return { "CREATURE_FLAG_EXTRA_NO_TAUNT", "CREATURE_FLAG_EXTRA_NO_TAUNT", "creature is immune to taunt auras and 'attack me' effects" };
case CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE: return { "CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE", "CREATURE_FLAG_EXTRA_NO_MOVE_FLAGS_UPDATE", "creature won't update movement flags" };
case CREATURE_FLAG_EXTRA_GHOST_VISIBILITY: return { "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "CREATURE_FLAG_EXTRA_GHOST_VISIBILITY", "creature will only be visible to dead players" };
case CREATURE_FLAG_EXTRA_UNUSED_12: return { "CREATURE_FLAG_EXTRA_UNUSED_12", "CREATURE_FLAG_EXTRA_UNUSED_12", "TODO: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)" };
case CREATURE_FLAG_EXTRA_UNUSED_12: return { "CREATURE_FLAG_EXTRA_UNUSED_12", "CREATURE_FLAG_EXTRA_UNUSED_12", "/ @todo: Implement CREATURE_FLAG_EXTRA_USE_OFFHAND_ATTACK (creature will use offhand attacks)" };
case CREATURE_FLAG_EXTRA_NO_SELL_VENDOR: return { "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "CREATURE_FLAG_EXTRA_NO_SELL_VENDOR", "players can't sell items to this vendor" };
case CREATURE_FLAG_EXTRA_IGNORE_COMBAT: return { "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "CREATURE_FLAG_EXTRA_IGNORE_COMBAT", "" };
case CREATURE_FLAG_EXTRA_WORLDEVENT: return { "CREATURE_FLAG_EXTRA_WORLDEVENT", "CREATURE_FLAG_EXTRA_WORLDEVENT", "custom flag for world event creatures (left room for merging)" };
@@ -55,10 +55,10 @@ AC_API_EXPORT EnumText EnumUtils<CreatureFlagsExtra>::ToString(CreatureFlagsExtr
case CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ: return { "CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ", "CREATURE_FLAG_EXTRA_NO_PLAYER_DAMAGE_REQ", "creature does not need to take player damage for kill credit" };
case CREATURE_FLAG_EXTRA_AVOID_AOE: return { "CREATURE_FLAG_EXTRA_AVOID_AOE", "CREATURE_FLAG_EXTRA_AVOID_AOE", "pussywizard: ignored by aoe attacks (for icc blood prince council npc - Dark Nucleus)" };
case CREATURE_FLAG_EXTRA_NO_DODGE: return { "CREATURE_FLAG_EXTRA_NO_DODGE", "CREATURE_FLAG_EXTRA_NO_DODGE", "xinef: target cannot dodge" };
case CREATURE_FLAG_EXTRA_MODULE: return { "CREATURE_FLAG_EXTRA_MODULE", "CREATURE_FLAG_EXTRA_MODULE", "Used by module creatures to avoid blizzlike checks." };
case CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE: return { "CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE", "Creature does not call for assistance on initial aggro", "" };
case CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS: return { "CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS", "Prevents creature from responding to assistance calls", "" };
case CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI: return { "CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI", "Creature entry SAI won't be overriden by GUID SAI", "" };
case CREATURE_FLAG_EXTRA_MODULE: return { "CREATURE_FLAG_EXTRA_MODULE", "CREATURE_FLAG_EXTRA_MODULE", "" };
case CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE: return { "CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE", "CREATURE_FLAG_EXTRA_DONT_CALL_ASSISTANCE", "Prevent creatures from calling for assistance on initial aggro" };
case CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS: return { "CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS", "CREATURE_FLAG_EXTRA_IGNORE_ALL_ASSISTANCE_CALLS", "Prevents creature from responding to assistance calls" };
case CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI: return { "CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI", "CREATURE_FLAG_DONT_OVERRIDE_ENTRY_SAI", "Load both ENTRY and GUID specific SAI" };
case CREATURE_FLAG_EXTRA_DUNGEON_BOSS: return { "CREATURE_FLAG_EXTRA_DUNGEON_BOSS", "CREATURE_FLAG_EXTRA_DUNGEON_BOSS", "creature is a dungeon boss (SET DYNAMICALLY, DO NOT ADD IN DB)" };
case CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING: return { "CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING", "CREATURE_FLAG_EXTRA_IGNORE_PATHFINDING", "creature ignore pathfinding" };
case CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK: return { "CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK", "CREATURE_FLAG_EXTRA_IMMUNITY_KNOCKBACK", "creature is immune to knockback effects" };
@@ -1725,7 +1725,10 @@ void GameObject::Use(Unit* user)
player->SendCinematicStart(info->camera.cinematicId);
if (info->camera.eventID)
{
GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this);
EventInform(info->camera.eventID);
}
return;
}
+2 -2
View File
@@ -196,10 +196,10 @@ struct Position
float GetRelativeAngle(const Position* pos) const
{
return GetAngle(pos) - m_orientation;
return NormalizeOrientation(GetAngle(pos) - m_orientation);
}
[[nodiscard]] float GetRelativeAngle(float x, float y) const { return GetAngle(x, y) - m_orientation; }
[[nodiscard]] float GetRelativeAngle(float x, float y) const { return NormalizeOrientation(GetAngle(x, y) - m_orientation); }
[[nodiscard]] float ToAbsoluteAngle(float relAngle) const { return NormalizeOrientation(relAngle + m_orientation); }
void GetSinCos(float x, float y, float& vsin, float& vcos) const;
+16 -1
View File
@@ -384,6 +384,8 @@ Player::Player(WorldSession* session): Unit(true), m_mover(this)
_activeCheats = CHEAT_NONE;
m_creationTime = 0s;
_cinematicMgr = new CinematicMgr(this);
m_achievementMgr = new AchievementMgr(this);
@@ -8043,6 +8045,12 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
// need know merged fishing/corpse loot type for achievements
loot->loot_type = loot_type;
if (!sScriptMgr->OnAllowedToLootContainerCheck(this, guid))
{
SendLootError(guid, LOOT_ERROR_DIDNT_KILL);
return;
}
if (permission != NONE_PERMISSION)
{
SetLootGUID(guid);
@@ -13268,6 +13276,8 @@ void Player::SetTitle(CharTitlesEntry const* title, bool lost)
data << uint32(title->bit_index);
data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost
GetSession()->SendPacket(&data);
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK);
}
uint32 Player::GetRuneBaseCooldown(uint8 index, bool skipGrace)
@@ -13544,8 +13554,13 @@ uint32 Player::CalculateTalentsPoints() const
return uint32(talentPointsForLevel * sWorld->getRate(RATE_TALENT));
}
bool Player::canFlyInZone(uint32 mapid, uint32 zone, SpellInfo const* bySpell) const
bool Player::canFlyInZone(uint32 mapid, uint32 zone, SpellInfo const* bySpell)
{
if (!sScriptMgr->OnCanPlayerFlyInZone(this, mapid,zone,bySpell))
{
return false;
}
// continent checked in SpellInfo::CheckLocation at cast and area update
uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone);
if (v_map == 571 && !bySpell->HasAttribute(SPELL_ATTR7_IGNORES_COLD_WEATHER_FLYING_REQUIREMENT))
+6 -1
View File
@@ -2299,7 +2299,7 @@ public:
}
void HandleFall(MovementInfo const& movementInfo);
[[nodiscard]] bool canFlyInZone(uint32 mapid, uint32 zone, SpellInfo const* bySpell) const;
[[nodiscard]] bool canFlyInZone(uint32 mapid, uint32 zone, SpellInfo const* bySpell);
void SetClientControl(Unit* target, bool allowMove, bool packetOnly = false);
@@ -2495,6 +2495,9 @@ public:
void CompletedAchievement(AchievementEntry const* entry);
[[nodiscard]] AchievementMgr* GetAchievementMgr() const { return m_achievementMgr; }
void SetCreationTime(Seconds creationTime) { m_creationTime = creationTime; }
[[nodiscard]] Seconds GetCreationTime() const { return m_creationTime; }
[[nodiscard]] bool HasTitle(uint32 bitIndex) const;
bool HasTitle(CharTitlesEntry const* title) const { return HasTitle(title->bit_index); }
void SetTitle(CharTitlesEntry const* title, bool lost = false);
@@ -2951,6 +2954,8 @@ private:
bool _wasOutdoor;
PlayerSettingMap m_charSettingsMap;
Seconds m_creationTime;
};
void AddItemsSetItem(Player* player, Item* item);
@@ -4948,8 +4948,8 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
//"arenaPoints, totalHonorPoints, todayHonorPoints, yesterdayHonorPoints, totalKills, todayKills, yesterdayKills, chosenTitle, knownCurrencies, watchedFaction, drunk, "
// 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
//"health, power1, power2, power3, power4, power5, power6, power7, instance_id, talentGroupsCount, activeTalentGroup, exploredZones, equipmentCache, ammoId, knownTitles,
// 70 71 72 73
//"actionBars, grantableLevels, innTriggerId, extraBonusTalentCount FROM characters WHERE guid = '{}'", guid);
// 70 71 72 73 74
//"actionBars, grantableLevels, innTriggerId, extraBonusTalentCount, UNIX_TIMESTAMP(creation_date) FROM characters WHERE guid = '{}'", guid);
PreparedQueryResult result = holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_FROM);
if (!result)
@@ -5026,6 +5026,9 @@ bool Player::LoadFromDB(ObjectGuid playerGuid, CharacterDatabaseQueryHolder cons
SetObjectScale(1.0f);
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f);
// load character creation date, relevant for achievements of type average
SetCreationTime(fields[74].Get<Seconds>());
// load achievements before anything else to prevent multiple gains for the same achievement/criteria on every loading (as loading does call UpdateAchievementCriteria)
m_achievementMgr->LoadFromDB(holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_ACHIEVEMENTS), holder.GetPreparedResult(PLAYER_LOGIN_QUERY_LOAD_CRITERIA_PROGRESS));
+3
View File
@@ -13973,6 +13973,9 @@ bool Unit::_IsValidAttackTarget(Unit const* target, SpellInfo const* bySpell, Wo
// additional checks - only PvP case
if (playerAffectingAttacker && playerAffectingTarget)
{
if (!IsPvP() && bySpell && bySpell->IsAffectingArea() && !bySpell->HasAttribute(SPELL_ATTR5_IGNORE_AREA_EFFECT_PVP_CHECK))
return false;
if (target->IsPvP())
return true;
+72 -66
View File
@@ -31,6 +31,7 @@ AC_API_EXPORT EnumText EnumUtils<UnitFlags>::ToString(UnitFlags value)
{
switch (value)
{
case UNIT_FLAG_NONE: return { "UNIT_FLAG_NONE", "UNIT_FLAG_NONE", "" };
case UNIT_FLAG_SERVER_CONTROLLED: return { "UNIT_FLAG_SERVER_CONTROLLED", "UNIT_FLAG_SERVER_CONTROLLED", "set only when unit movement is controlled by server - by SPLINE/MONSTER_MOVE packets, together with UNIT_FLAG_STUNNED; only set to units controlled by client; client function CGUnit_C::IsClientControlled returns false when set for owner" };
case UNIT_FLAG_NON_ATTACKABLE: return { "UNIT_FLAG_NON_ATTACKABLE", "UNIT_FLAG_NON_ATTACKABLE", "not attackable" };
case UNIT_FLAG_DISABLE_MOVE: return { "UNIT_FLAG_DISABLE_MOVE", "UNIT_FLAG_DISABLE_MOVE", "" };
@@ -68,45 +69,46 @@ AC_API_EXPORT EnumText EnumUtils<UnitFlags>::ToString(UnitFlags value)
}
template <>
AC_API_EXPORT size_t EnumUtils<UnitFlags>::Count() { return 32; }
AC_API_EXPORT size_t EnumUtils<UnitFlags>::Count() { return 33; }
template <>
AC_API_EXPORT UnitFlags EnumUtils<UnitFlags>::FromIndex(size_t index)
{
switch (index)
{
case 0: return UNIT_FLAG_SERVER_CONTROLLED;
case 1: return UNIT_FLAG_NON_ATTACKABLE;
case 2: return UNIT_FLAG_DISABLE_MOVE;
case 3: return UNIT_FLAG_PLAYER_CONTROLLED;
case 4: return UNIT_FLAG_RENAME;
case 5: return UNIT_FLAG_PREPARATION;
case 6: return UNIT_FLAG_UNK_6;
case 7: return UNIT_FLAG_NOT_ATTACKABLE_1;
case 8: return UNIT_FLAG_IMMUNE_TO_PC;
case 9: return UNIT_FLAG_IMMUNE_TO_NPC;
case 10: return UNIT_FLAG_LOOTING;
case 11: return UNIT_FLAG_PET_IN_COMBAT;
case 12: return UNIT_FLAG_PVP;
case 13: return UNIT_FLAG_SILENCED;
case 14: return UNIT_FLAG_CANNOT_SWIM;
case 15: return UNIT_FLAG_SWIMMING;
case 16: return UNIT_FLAG_NON_ATTACKABLE_2;
case 17: return UNIT_FLAG_PACIFIED;
case 18: return UNIT_FLAG_STUNNED;
case 19: return UNIT_FLAG_IN_COMBAT;
case 20: return UNIT_FLAG_TAXI_FLIGHT;
case 21: return UNIT_FLAG_DISARMED;
case 22: return UNIT_FLAG_CONFUSED;
case 23: return UNIT_FLAG_FLEEING;
case 24: return UNIT_FLAG_POSSESSED;
case 25: return UNIT_FLAG_NOT_SELECTABLE;
case 26: return UNIT_FLAG_SKINNABLE;
case 27: return UNIT_FLAG_MOUNT;
case 28: return UNIT_FLAG_UNK_28;
case 29: return UNIT_FLAG_PREVENT_EMOTES_FROM_CHAT_TEXT;
case 30: return UNIT_FLAG_SHEATHE;
case 31: return UNIT_FLAG_IMMUNE;
case 0: return UNIT_FLAG_NONE;
case 1: return UNIT_FLAG_SERVER_CONTROLLED;
case 2: return UNIT_FLAG_NON_ATTACKABLE;
case 3: return UNIT_FLAG_DISABLE_MOVE;
case 4: return UNIT_FLAG_PLAYER_CONTROLLED;
case 5: return UNIT_FLAG_RENAME;
case 6: return UNIT_FLAG_PREPARATION;
case 7: return UNIT_FLAG_UNK_6;
case 8: return UNIT_FLAG_NOT_ATTACKABLE_1;
case 9: return UNIT_FLAG_IMMUNE_TO_PC;
case 10: return UNIT_FLAG_IMMUNE_TO_NPC;
case 11: return UNIT_FLAG_LOOTING;
case 12: return UNIT_FLAG_PET_IN_COMBAT;
case 13: return UNIT_FLAG_PVP;
case 14: return UNIT_FLAG_SILENCED;
case 15: return UNIT_FLAG_CANNOT_SWIM;
case 16: return UNIT_FLAG_SWIMMING;
case 17: return UNIT_FLAG_NON_ATTACKABLE_2;
case 18: return UNIT_FLAG_PACIFIED;
case 19: return UNIT_FLAG_STUNNED;
case 20: return UNIT_FLAG_IN_COMBAT;
case 21: return UNIT_FLAG_TAXI_FLIGHT;
case 22: return UNIT_FLAG_DISARMED;
case 23: return UNIT_FLAG_CONFUSED;
case 24: return UNIT_FLAG_FLEEING;
case 25: return UNIT_FLAG_POSSESSED;
case 26: return UNIT_FLAG_NOT_SELECTABLE;
case 27: return UNIT_FLAG_SKINNABLE;
case 28: return UNIT_FLAG_MOUNT;
case 29: return UNIT_FLAG_UNK_28;
case 30: return UNIT_FLAG_PREVENT_EMOTES_FROM_CHAT_TEXT;
case 31: return UNIT_FLAG_SHEATHE;
case 32: return UNIT_FLAG_IMMUNE;
default: throw std::out_of_range("index");
}
}
@@ -116,38 +118,39 @@ AC_API_EXPORT size_t EnumUtils<UnitFlags>::ToIndex(UnitFlags value)
{
switch (value)
{
case UNIT_FLAG_SERVER_CONTROLLED: return 0;
case UNIT_FLAG_NON_ATTACKABLE: return 1;
case UNIT_FLAG_DISABLE_MOVE: return 2;
case UNIT_FLAG_PLAYER_CONTROLLED: return 3;
case UNIT_FLAG_RENAME: return 4;
case UNIT_FLAG_PREPARATION: return 5;
case UNIT_FLAG_UNK_6: return 6;
case UNIT_FLAG_NOT_ATTACKABLE_1: return 7;
case UNIT_FLAG_IMMUNE_TO_PC: return 8;
case UNIT_FLAG_IMMUNE_TO_NPC: return 9;
case UNIT_FLAG_LOOTING: return 10;
case UNIT_FLAG_PET_IN_COMBAT: return 11;
case UNIT_FLAG_PVP: return 12;
case UNIT_FLAG_SILENCED: return 13;
case UNIT_FLAG_CANNOT_SWIM: return 14;
case UNIT_FLAG_SWIMMING: return 15;
case UNIT_FLAG_NON_ATTACKABLE_2: return 16;
case UNIT_FLAG_PACIFIED: return 17;
case UNIT_FLAG_STUNNED: return 18;
case UNIT_FLAG_IN_COMBAT: return 19;
case UNIT_FLAG_TAXI_FLIGHT: return 20;
case UNIT_FLAG_DISARMED: return 21;
case UNIT_FLAG_CONFUSED: return 22;
case UNIT_FLAG_FLEEING: return 23;
case UNIT_FLAG_POSSESSED: return 24;
case UNIT_FLAG_NOT_SELECTABLE: return 25;
case UNIT_FLAG_SKINNABLE: return 26;
case UNIT_FLAG_MOUNT: return 27;
case UNIT_FLAG_UNK_28: return 28;
case UNIT_FLAG_PREVENT_EMOTES_FROM_CHAT_TEXT: return 29;
case UNIT_FLAG_SHEATHE: return 30;
case UNIT_FLAG_IMMUNE: return 31;
case UNIT_FLAG_NONE: return 0;
case UNIT_FLAG_SERVER_CONTROLLED: return 1;
case UNIT_FLAG_NON_ATTACKABLE: return 2;
case UNIT_FLAG_DISABLE_MOVE: return 3;
case UNIT_FLAG_PLAYER_CONTROLLED: return 4;
case UNIT_FLAG_RENAME: return 5;
case UNIT_FLAG_PREPARATION: return 6;
case UNIT_FLAG_UNK_6: return 7;
case UNIT_FLAG_NOT_ATTACKABLE_1: return 8;
case UNIT_FLAG_IMMUNE_TO_PC: return 9;
case UNIT_FLAG_IMMUNE_TO_NPC: return 10;
case UNIT_FLAG_LOOTING: return 11;
case UNIT_FLAG_PET_IN_COMBAT: return 12;
case UNIT_FLAG_PVP: return 13;
case UNIT_FLAG_SILENCED: return 14;
case UNIT_FLAG_CANNOT_SWIM: return 15;
case UNIT_FLAG_SWIMMING: return 16;
case UNIT_FLAG_NON_ATTACKABLE_2: return 17;
case UNIT_FLAG_PACIFIED: return 18;
case UNIT_FLAG_STUNNED: return 19;
case UNIT_FLAG_IN_COMBAT: return 20;
case UNIT_FLAG_TAXI_FLIGHT: return 21;
case UNIT_FLAG_DISARMED: return 22;
case UNIT_FLAG_CONFUSED: return 23;
case UNIT_FLAG_FLEEING: return 24;
case UNIT_FLAG_POSSESSED: return 25;
case UNIT_FLAG_NOT_SELECTABLE: return 26;
case UNIT_FLAG_SKINNABLE: return 27;
case UNIT_FLAG_MOUNT: return 28;
case UNIT_FLAG_UNK_28: return 29;
case UNIT_FLAG_PREVENT_EMOTES_FROM_CHAT_TEXT: return 30;
case UNIT_FLAG_SHEATHE: return 31;
case UNIT_FLAG_IMMUNE: return 32;
default: throw std::out_of_range("value");
}
}
@@ -187,12 +190,13 @@ AC_API_EXPORT EnumText EnumUtils<NPCFlags>::ToString(NPCFlags value)
case UNIT_NPC_FLAG_SPELLCLICK: return { "UNIT_NPC_FLAG_SPELLCLICK", "has spell click enabled", "cause client to send 1015 opcode (spell click)" };
case UNIT_NPC_FLAG_PLAYER_VEHICLE: return { "UNIT_NPC_FLAG_PLAYER_VEHICLE", "is player vehicle", "players with mounts that have vehicle data should have it set" };
case UNIT_NPC_FLAG_MAILBOX: return { "UNIT_NPC_FLAG_MAILBOX", "is mailbox", "" };
case UNIT_NPC_FLAG_VENDOR_MASK: return { "UNIT_NPC_FLAG_VENDOR_MASK", "UNIT_NPC_FLAG_VENDOR_MASK", "" };
default: throw std::out_of_range("value");
}
}
template <>
AC_API_EXPORT size_t EnumUtils<NPCFlags>::Count() { return 27; }
AC_API_EXPORT size_t EnumUtils<NPCFlags>::Count() { return 28; }
template <>
AC_API_EXPORT NPCFlags EnumUtils<NPCFlags>::FromIndex(size_t index)
@@ -226,6 +230,7 @@ AC_API_EXPORT NPCFlags EnumUtils<NPCFlags>::FromIndex(size_t index)
case 24: return UNIT_NPC_FLAG_SPELLCLICK;
case 25: return UNIT_NPC_FLAG_PLAYER_VEHICLE;
case 26: return UNIT_NPC_FLAG_MAILBOX;
case 27: return UNIT_NPC_FLAG_VENDOR_MASK;
default: throw std::out_of_range("index");
}
}
@@ -262,6 +267,7 @@ AC_API_EXPORT size_t EnumUtils<NPCFlags>::ToIndex(NPCFlags value)
case UNIT_NPC_FLAG_SPELLCLICK: return 24;
case UNIT_NPC_FLAG_PLAYER_VEHICLE: return 25;
case UNIT_NPC_FLAG_MAILBOX: return 26;
case UNIT_NPC_FLAG_VENDOR_MASK: return 27;
default: throw std::out_of_range("value");
}
}
+65 -120
View File
@@ -2863,47 +2863,43 @@ void ObjectMgr::LoadItemTemplates()
if (dbcitem)
{
if (itemTemplate.Class != dbcitem->ClassID)
if (enforceDBCAttributes)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Class value ({}), must be ({}).", entry, itemTemplate.Class, dbcitem->ClassID);
if (enforceDBCAttributes)
if (itemTemplate.Class != dbcitem->ClassID)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Class value ({}), must be ({}).", entry, itemTemplate.Class, dbcitem->ClassID);
itemTemplate.Class = dbcitem->ClassID;
}
if (itemTemplate.SubClass != dbcitem->SubclassID)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Subclass value ({}) for class {}, must be ({}).", entry, itemTemplate.SubClass, itemTemplate.Class, dbcitem->SubclassID);
if (enforceDBCAttributes)
}
if (itemTemplate.SubClass != dbcitem->SubclassID)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Subclass value ({}) for class {}, must be ({}).", entry, itemTemplate.SubClass, itemTemplate.Class, dbcitem->SubclassID);
itemTemplate.SubClass = dbcitem->SubclassID;
}
if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclassID)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct SoundOverrideSubclass ({}), must be {}.", entry, itemTemplate.SoundOverrideSubclass);
if (enforceDBCAttributes)
}
if (itemTemplate.SoundOverrideSubclass != dbcitem->SoundOverrideSubclassID)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct SoundOverrideSubclass ({}), must be {}.", entry, itemTemplate.SoundOverrideSubclass, dbcitem->SoundOverrideSubclassID);
itemTemplate.SoundOverrideSubclass = dbcitem->SoundOverrideSubclassID;
}
if (itemTemplate.Material != dbcitem->Material)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct material ({}), must be {}.", entry, itemTemplate.Material, dbcitem->Material);
if (enforceDBCAttributes)
}
if (itemTemplate.Material != dbcitem->Material)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct material ({}), must be {}.", entry, itemTemplate.Material, dbcitem->Material);
itemTemplate.Material = dbcitem->Material;
}
if (itemTemplate.InventoryType != dbcitem->InventoryType)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong InventoryType value ({}), must be {}.", entry, itemTemplate.InventoryType, dbcitem->InventoryType);
if (enforceDBCAttributes)
}
if (itemTemplate.InventoryType != dbcitem->InventoryType)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong InventoryType value ({}), must be {}.", entry, itemTemplate.InventoryType, dbcitem->InventoryType);
itemTemplate.InventoryType = dbcitem->InventoryType;
}
if (itemTemplate.DisplayInfoID != dbcitem->DisplayInfoID)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct display id ({}), must be {}.", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayInfoID);
if (enforceDBCAttributes)
}
if (itemTemplate.DisplayInfoID != dbcitem->DisplayInfoID)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) does not have a correct display id ({}), must be {}.", entry, itemTemplate.DisplayInfoID, dbcitem->DisplayInfoID);
itemTemplate.DisplayInfoID = dbcitem->DisplayInfoID;
}
if (itemTemplate.Sheath != dbcitem->SheatheType)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Sheath ({}), must be {}.", entry, itemTemplate.Sheath, dbcitem->SheatheType);
if (enforceDBCAttributes)
}
if (itemTemplate.Sheath != dbcitem->SheatheType)
{
LOG_ERROR("sql.sql", "Item (Entry: {}) has wrong Sheath ({}), must be {}.", entry, itemTemplate.Sheath, dbcitem->SheatheType);
itemTemplate.Sheath = dbcitem->SheatheType;
}
}
}
else
@@ -4062,88 +4058,6 @@ void ObjectMgr::LoadPlayerInfo()
}
}
// Loading levels data (class only dependent)
LOG_INFO("server.loading", "Loading Player Create Level HP/Mana Data...");
{
uint32 oldMSTime = getMSTime();
// 0 1 2 3
QueryResult result = WorldDatabase.Query("SELECT class, level, basehp, basemana FROM player_classlevelstats");
if (!result)
{
LOG_FATAL("server.loading", ">> Loaded 0 level health/mana definitions. DB table `player_classlevelstats` is empty.");
exit(1);
}
uint32 count = 0;
do
{
Field* fields = result->Fetch();
uint32 current_class = fields[0].Get<uint8>();
if (current_class >= MAX_CLASSES)
{
LOG_ERROR("sql.sql", "Wrong class {} in `player_classlevelstats` table, ignoring.", current_class);
continue;
}
uint8 current_level = fields[1].Get<uint8>(); // Can't be > than STRONG_MAX_LEVEL (hardcoded level maximum) due to var type
if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
LOG_INFO("sql.sql", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_classlevelstats` table, ignoring.", current_level);
++count; // make result loading percent "expected" correct in case disabled detail mode for example.
continue;
}
PlayerClassInfo* info = _playerClassInfo[current_class];
if (!info)
{
info = new PlayerClassInfo();
info->levelInfo = new PlayerClassLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];
_playerClassInfo[current_class] = info;
}
PlayerClassLevelInfo& levelInfo = info->levelInfo[current_level - 1];
levelInfo.basehealth = fields[2].Get<uint32>();
levelInfo.basemana = fields[3].Get<uint32>();
++count;
} while (result->NextRow());
// Fill gaps and check integrity
for (int class_ = 0; class_ < MAX_CLASSES; ++class_)
{
// skip non existed classes
if (!sChrClassesStore.LookupEntry(class_))
continue;
PlayerClassInfo* pClassInfo = _playerClassInfo[class_];
// fatal error if no initial level data
if (!pClassInfo->levelInfo || (pClassInfo->levelInfo[sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) - 1].basehealth == 0 && class_ != CLASS_DEATH_KNIGHT) || (pClassInfo->levelInfo[sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL) - 1].basehealth == 0 && class_ == CLASS_DEATH_KNIGHT))
{
LOG_ERROR("sql.sql", "Class {} initial level does not have health/mana data!", class_);
exit(1);
}
// fill level gaps
for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
{
if ((pClassInfo->levelInfo[level].basehealth == 0 && class_ != CLASS_DEATH_KNIGHT) || (level >= sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL) && pClassInfo->levelInfo[level].basehealth == 0 && class_ == CLASS_DEATH_KNIGHT))
{
LOG_ERROR("sql.sql", "Class {} level {} does not have health/mana data. Using stats data of level {}.", class_, level + 1, level);
pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
}
}
}
LOG_INFO("server.loading", ">> Loaded {} Level Health/Mana Definitions in {} ms", count, GetMSTimeDiffToNow(oldMSTime));
LOG_INFO("server.loading", " ");
}
// Loading levels data (class/race dependent)
LOG_INFO("server.loading", "Loading Player Create Level Stats Data...");
{
@@ -4182,8 +4096,8 @@ void ObjectMgr::LoadPlayerInfo()
} while (raceStatsResult->NextRow());
// 0 1 2 3 4 5 6
QueryResult result = WorldDatabase.Query("SELECT Class, Level, Strength, Agility, Stamina, Intellect, Spirit FROM player_class_stats");
// 0 1 2 3 4 5 6 7 8
QueryResult result = WorldDatabase.Query("SELECT Class, Level, Strength, Agility, Stamina, Intellect, Spirit, BaseHP, BaseMana FROM player_class_stats");
if (!result)
{
@@ -4207,7 +4121,7 @@ void ObjectMgr::LoadPlayerInfo()
uint32 current_level = fields[1].Get<uint8>();
if (current_level > sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL))
{
if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
if (current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
LOG_ERROR("sql.sql", "Wrong (> {}) level {} in `player_class_stats` table, ignoring.", STRONG_MAX_LEVEL, current_level);
else
LOG_DEBUG("sql.sql", "Unused (> MaxPlayerLevel in worldserver.conf) level {} in `player_class_stats` table, ignoring.", current_level);
@@ -4228,6 +4142,19 @@ void ObjectMgr::LoadPlayerInfo()
}
}
PlayerClassInfo* info = _playerClassInfo[current_class];
if (!info)
{
info = new PlayerClassInfo();
info->levelInfo = new PlayerClassLevelInfo[sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL)];
_playerClassInfo[current_class] = info;
}
PlayerClassLevelInfo& levelInfo = info->levelInfo[current_level - 1];
levelInfo.basehealth = fields[7].Get<uint32>();
levelInfo.basemana = fields[8].Get<uint32>();
++count;
} while (result->NextRow());
@@ -4244,6 +4171,7 @@ void ObjectMgr::LoadPlayerInfo()
if (!sChrClassesStore.LookupEntry(class_))
continue;
PlayerClassInfo* pClassInfo = _playerClassInfo[class_];
PlayerInfo* info = _playerInfo[race][class_];
if (!info)
continue;
@@ -4256,14 +4184,21 @@ void ObjectMgr::LoadPlayerInfo()
if (sWorld->getIntConfig(CONFIG_EXPANSION) < EXPANSION_WRATH_OF_THE_LICH_KING && class_ == CLASS_DEATH_KNIGHT)
continue;
// fatal error if no initial level data
// fatal error if no initial stats data
if (!info->levelInfo || (info->levelInfo[sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) - 1].stats[0] == 0 && class_ != CLASS_DEATH_KNIGHT) || (info->levelInfo[sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL) - 1].stats[0] == 0 && class_ == CLASS_DEATH_KNIGHT))
{
LOG_ERROR("sql.sql", "Race {} class {} initial level does not have stats data!", race, class_);
exit(1);
}
// fill level gaps
// fatal error if no initial health/mana data
if (!pClassInfo->levelInfo || (pClassInfo->levelInfo[sWorld->getIntConfig(CONFIG_START_PLAYER_LEVEL) - 1].basehealth == 0 && class_ != CLASS_DEATH_KNIGHT) || (pClassInfo->levelInfo[sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL) - 1].basehealth == 0 && class_ == CLASS_DEATH_KNIGHT))
{
LOG_ERROR("sql.sql", "Class {} initial level does not have health/mana data!", class_);
exit(1);
}
// fill level gaps for stats
for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
{
if ((info->levelInfo[level].stats[0] == 0 && class_ != CLASS_DEATH_KNIGHT) || (level >= sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL) && info->levelInfo[level].stats[0] == 0 && class_ == CLASS_DEATH_KNIGHT))
@@ -4272,6 +4207,16 @@ void ObjectMgr::LoadPlayerInfo()
info->levelInfo[level] = info->levelInfo[level - 1];
}
}
// fill level gaps for health/mana
for (uint8 level = 1; level < sWorld->getIntConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
{
if ((pClassInfo->levelInfo[level].basehealth == 0 && class_ != CLASS_DEATH_KNIGHT) || (level >= sWorld->getIntConfig(CONFIG_START_HEROIC_PLAYER_LEVEL) && pClassInfo->levelInfo[level].basehealth == 0 && class_ == CLASS_DEATH_KNIGHT))
{
LOG_ERROR("sql.sql", "Class {} level {} does not have health/mana data. Using stats data of level {}.", class_, level + 1, level);
pClassInfo->levelInfo[level] = pClassInfo->levelInfo[level - 1];
}
}
}
}
@@ -536,6 +536,9 @@ void WorldSession::HandleLootRoll(WorldPacket& recvData)
case ROLL_GREED:
GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED, 1);
break;
case ROLL_DISENCHANT:
GetPlayer()->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_DISENCHANT, 1);
break;
}
}
@@ -675,6 +675,8 @@ InstancePlayerBind* InstanceSaveMgr::PlayerBindToInstance(ObjectGuid guid, Insta
stmt->SetData(1, save->GetInstanceId());
stmt->SetData(2, permanent);
CharacterDatabase.Execute(stmt);
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID, 1);
}
if (bind.save != save)
@@ -218,6 +218,11 @@ void InstanceScript::UpdateMinionState(Creature* minion, EncounterState state)
}
}
void InstanceScript::Update(uint32 diff)
{
scheduler.Update(diff);
}
void InstanceScript::UpdateDoorState(GameObject* door)
{
DoorInfoMapBounds range = doors.equal_range(door->GetEntry());
+4 -1
View File
@@ -20,6 +20,7 @@
#include "CreatureAI.h"
#include "ObjectMgr.h"
#include "TaskScheduler.h"
#include "World.h"
#include "ZoneScript.h"
#include <set>
@@ -159,7 +160,7 @@ public:
void SaveToDB();
virtual void Update(uint32 /*diff*/) {}
virtual void Update(uint32 /*diff*/);
//Used by the map's CanEnter function.
//This is to prevent players from entering during boss encounters.
@@ -263,6 +264,8 @@ public:
// Allows executing code using all creatures registered in the instance script as minions
void DoForAllMinions(uint32 id, std::function<void(Creature*)> exec);
TaskScheduler scheduler;
protected:
void SetHeaders(std::string const& dataHeaders);
void SetBossNumber(uint32 number) { bosses.resize(number); }
+1 -1
View File
@@ -1058,7 +1058,7 @@ enum AcoreStrings
LANG_COMMAND_TICKETLISTRESPONSE = 2029,
LANG_COMMAND_TICKETCOMPLETED = 2030,
// Trinity strings 5000-9999
// acore strings 5000-9999
LANG_COMMAND_FREEZE = 5000,
LANG_COMMAND_FREEZE_ERROR = 5001,
LANG_COMMAND_FREEZE_WRONG = 5002,
+3 -3
View File
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYSERVER_MOVEPLINE_H
#define TRINITYSERVER_MOVEPLINE_H
#ifndef AC_MOVEPLINE_H
#define AC_MOVEPLINE_H
#include "MoveSplineInitArgs.h"
#include "Spline.h"
@@ -132,4 +132,4 @@ namespace Movement
}
};
}
#endif // TRINITYSERVER_MOVEPLINE_H
#endif // AC_MOVEPLINE_H
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYSERVER_MOVESPLINEFLAG_H
#define TRINITYSERVER_MOVESPLINEFLAG_H
#ifndef AC_MOVESPLINEFLAG_H
#define AC_MOVESPLINEFLAG_H
#include "MovementTypedefs.h"
#include <string>
@@ -145,4 +145,4 @@ namespace Movement
#endif
}
#endif // TRINITYSERVER_MOVESPLINEFLAG_H
#endif // AC_MOVESPLINEFLAG_H
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYSERVER_MOVESPLINEINIT_H
#define TRINITYSERVER_MOVESPLINEINIT_H
#ifndef AC_MOVESPLINEINIT_H
#define AC_MOVESPLINEINIT_H
#include "MoveSplineInitArgs.h"
#include "PathGenerator.h"
@@ -209,4 +209,4 @@ namespace Movement
inline void MoveSplineInit::DisableTransportPathTransformations() { args.TransformForTransport = false; }
}
#endif // TRINITYSERVER_MOVESPLINEINIT_H
#endif // AC_MOVESPLINEINIT_H
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYSERVER_MOVESPLINEINIT_ARGS_H
#define TRINITYSERVER_MOVESPLINEINIT_ARGS_H
#ifndef AC_MOVESPLINEINIT_ARGS_H
#define AC_MOVESPLINEINIT_ARGS_H
#include "MoveSplineFlag.h"
#include <G3D/Vector3.h>
@@ -68,4 +68,4 @@ namespace Movement
};
}
#endif // TRINITYSERVER_MOVESPLINEINIT_ARGS_H
#endif // AC_MOVESPLINEINIT_ARGS_H
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYSERVER_PACKET_BUILDER_H
#define TRINITYSERVER_PACKET_BUILDER_H
#ifndef AC_PACKET_BUILDER_H
#define AC_PACKET_BUILDER_H
#include "Define.h"
@@ -40,4 +40,4 @@ namespace Movement
static void WriteCreate(const MoveSpline& mov, ByteBuffer& data);
};
}
#endif // TRINITYSERVER_PACKET_BUILDER_H
#endif // AC_PACKET_BUILDER_H
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYSERVER_TYPEDEFS_H
#define TRINITYSERVER_TYPEDEFS_H
#ifndef AC_TYPEDEFS_H
#define AC_TYPEDEFS_H
#include "Common.h"
@@ -74,4 +74,4 @@ namespace Movement
extern UInt32Counter splineIdGen;
}
#endif // TRINITYSERVER_TYPEDEFS_H
#endif // AC_TYPEDEFS_H
+3 -3
View File
@@ -15,8 +15,8 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TRINITYSERVER_SPLINE_H
#define TRINITYSERVER_SPLINE_H
#ifndef AC_SPLINE_H
#define AC_SPLINE_H
#include "Errors.h"
#include "MovementTypedefs.h"
@@ -210,4 +210,4 @@ namespace Movement
#include "SplineImpl.h"
#endif // TRINITYSERVER_SPLINE_H
#endif // AC_SPLINE_H
@@ -178,6 +178,21 @@ bool ScriptMgr::OnAllowedForPlayerLootCheck(Player const* player, ObjectGuid sou
return true;
}
bool ScriptMgr::OnAllowedToLootContainerCheck(Player const* player, ObjectGuid source)
{
auto ret = IsValidBoolScript<GlobalScript>([&](GlobalScript* script)
{
return script->OnAllowedToLootContainerCheck(player, source);
});
if (ret && *ret)
{
return false;
}
return true;
}
/**
* @brief Called when an instance Id is deleted, usually because it expired or no players are bound to it anymore.
*
@@ -74,6 +74,21 @@ void ScriptMgr::OnPlayerReleasedGhost(Player* player)
});
}
bool ScriptMgr::OnCanPlayerFlyInZone(Player* player, uint32 mapId, uint32 zoneId, SpellInfo const* bySpell)
{
auto ret = IsValidBoolScript<PlayerScript>([player, mapId, zoneId, bySpell](PlayerScript* script)
{
return !script->OnCanPlayerFlyInZone(player, mapId, zoneId, bySpell);
});
if (ret && *ret)
{
return false;
}
return true;
}
void ScriptMgr::OnPVPKill(Player* killer, Player* killed)
{
ExecuteScript<PlayerScript>([&](PlayerScript* script)
+16 -1
View File
@@ -1474,6 +1474,16 @@ public:
*/
virtual void OnQuestAbandon(Player* /*player*/, uint32 /*questId*/) { }
/**
* @brief This hook called before other CanFlyChecks are applied
*
* @param player Contains information about the Player
* @param mapId Contains information about the current map id
* @param zoneId Contains information about the current zone
* @param bySpell Contains information about the spell that invoked the check
*/
[[nodiscard]] virtual bool OnCanPlayerFlyInZone(Player* /*player*/, uint32 /*mapId*/, uint32 /*zoneId*/, SpellInfo const* /*bySpell*/) { return true; }
// Passive Anticheat System
virtual void AnticheatSetSkipOnePacketForASH(Player* /*player*/, bool /*apply*/) { }
virtual void AnticheatSetCanFlybyServer(Player* /*player*/, bool /*apply*/) { }
@@ -1648,9 +1658,12 @@ public:
// Called after loading spell dbc corrections
virtual void OnLoadSpellCustomAttr(SpellInfo* /*spell*/) { }
// Called when checking if a player can see the creature loot
// Called when checking if a player can see the creature loot item
virtual bool OnAllowedForPlayerLootCheck(Player const* /*player*/, ObjectGuid /*source*/) { return false; };
// Called when checking if a player can see the creature loot (if it can click the corpse f.e)
virtual bool OnAllowedToLootContainerCheck(Player const* /*player*/, ObjectGuid /*source*/) { return false; };
// Called when instance id is removed from database (e.g. instance reset)
virtual void OnInstanceIdRemoved(uint32 /*instanceId*/) { }
@@ -2474,6 +2487,7 @@ public: /* PlayerScript */
bool CanSendErrorAlreadyLooted(Player* player);
void OnAfterCreatureLoot(Player* player);
void OnAfterCreatureLootMoney(Player* player);
bool OnCanPlayerFlyInZone(Player* player, uint32 mapId, uint32 zoneId, SpellInfo const* bySpell);
// Anti cheat
void AnticheatSetSkipOnePacketForASH(Player* player, bool apply);
@@ -2535,6 +2549,7 @@ public: /* GlobalScript */
bool OnSpellHealingBonusTakenNegativeModifiers(Unit const* target, Unit const* caster, SpellInfo const* spellInfo, float& val);
void OnLoadSpellCustomAttr(SpellInfo* spell);
bool OnAllowedForPlayerLootCheck(Player const* player, ObjectGuid source);
bool OnAllowedToLootContainerCheck(Player const* player, ObjectGuid source);
void OnInstanceIdRemoved(uint32 instanceId);
void OnBeforeSetBossState(uint32 id, EncounterState newState, EncounterState oldState, Map* instance);
@@ -57,6 +57,7 @@ namespace WorldPackets
class AC_GAME_API PlayMusic final : public ServerPacket
{
public:
// cppcheck-suppress missingReturn
PlayMusic() : ServerPacket(SMSG_PLAY_MUSIC, 4) { }
PlayMusic(uint32 soundKitID) : ServerPacket(SMSG_PLAY_MUSIC, 4), SoundKitID(soundKitID) { }
@@ -68,6 +69,7 @@ namespace WorldPackets
class AC_GAME_API PlayObjectSound final : public ServerPacket
{
public:
// cppcheck-suppress missingReturn
PlayObjectSound() : ServerPacket(SMSG_PLAY_OBJECT_SOUND, 4 + 8) { }
PlayObjectSound(ObjectGuid const& sourceObjectGUID, uint32 soundKitID)
: ServerPacket(SMSG_PLAY_OBJECT_SOUND, 4 + 8), SourceObjectGUID(sourceObjectGUID), SoundKitID(soundKitID) { }
@@ -82,6 +84,7 @@ namespace WorldPackets
class AC_GAME_API Playsound final : public ServerPacket
{
public:
// cppcheck-suppress missingReturn
Playsound() : ServerPacket(SMSG_PLAY_SOUND, 4) { }
Playsound(uint32 soundKitID) : ServerPacket(SMSG_PLAY_SOUND, 4), SoundKitID(soundKitID) { }
+1 -1
View File
@@ -564,7 +564,7 @@ void WorldSocket::HandleAuthSessionCallback(std::shared_ptr<AuthSession> authSes
//! Negative mutetime indicates amount of minutes to be muted effective on next login - which is now.
if (account.MuteTime < 0)
{
account.MuteTime = GameTime::GetGameTime().count() + llabs(account.MuteTime);
account.MuteTime = GameTime::GetGameTime().count() + std::llabs(account.MuteTime);
auto* stmt = LoginDatabase.GetPreparedStatement(LOGIN_UPD_MUTE_TIME_LOGIN);
stmt->SetData(0, account.MuteTime);
-17
View File
@@ -6088,23 +6088,6 @@ SpellCastResult Spell::CheckCast(bool strict)
// for effects of spells that have only one target
switch (m_spellInfo->Effects[i].Effect)
{
case SPELL_EFFECT_DUMMY:
{
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT)
{
// Raise Ally
if( m_spellInfo->Id == 61999 )
{
Unit* target = m_targets.GetUnitTarget();
if (!target)
return SPELL_FAILED_BAD_TARGETS;
if (target->IsAlive()) // not discovered attributeEx5?
return SPELL_FAILED_TARGET_NOT_DEAD;
}
}
break;
}
case SPELL_EFFECT_LEARN_SPELL:
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
+1 -1
View File
@@ -1470,7 +1470,7 @@ SpellCastResult SpellInfo::CheckShapeshift(uint32 form) const
return SPELL_CAST_OK;
}
SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player /*= nullptr*/, bool strict /*= true*/) const
SpellCastResult SpellInfo::CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player* player /*= nullptr*/, bool strict /*= true*/) const
{
// normal case
if (AreaGroupId > 0)
+1 -1
View File
@@ -474,7 +474,7 @@ public:
bool IsAuraExclusiveBySpecificPerCasterWith(SpellInfo const* spellInfo) const;
SpellCastResult CheckShapeshift(uint32 form) const;
SpellCastResult CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = nullptr, bool strict = true) const;
SpellCastResult CheckLocation(uint32 map_id, uint32 zone_id, uint32 area_id, Player* player = nullptr, bool strict = true) const;
SpellCastResult CheckTarget(Unit const* caster, WorldObject const* target, bool implicit = true) const;
SpellCastResult CheckExplicitTarget(Unit const* caster, WorldObject const* target, Item const* itemTarget = nullptr) const;
bool CheckTargetCreatureType(Unit const* target) const;
@@ -1530,8 +1530,8 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->ChannelInterruptFlags |= AURA_INTERRUPT_FLAG_MOVE;
});
// Debris
ApplySpellFix({ 36449 }, [](SpellInfo* spellInfo)
// Debris - Debris Visual
ApplySpellFix({ 36449, 30632 }, [](SpellInfo* spellInfo)
{
spellInfo->Attributes |= SPELL_ATTR0_AURA_IS_DEBUFF;
});
@@ -1542,12 +1542,6 @@ void SpellMgr::LoadSpellInfoCorrections()
spellInfo->AttributesEx3 |= SPELL_ATTR3_DOT_STACKING_RULE;
});
// Debris Visual
ApplySpellFix({ 30632 }, [](SpellInfo* spellInfo)
{
spellInfo->Effects[EFFECT_0].TargetB = SpellImplicitTargetInfo(TARGET_DEST_DYNOBJ_ALLY);
});
// Activate Sunblade Protecto
ApplySpellFix({ 46475, 46476 }, [](SpellInfo* spellInfo)
{
@@ -290,7 +290,7 @@ public:
bool OnGossipHello(Player* player, GameObject* go) override
{
if (InstanceScript* instance = go->GetInstanceScript())
if (instance->GetData(DATA_EGG_EVENT) != DONE && !player->HasAura(SPELL_MIND_EXHAUSTION))
if (instance->GetData(DATA_EGG_EVENT) != DONE && !player->HasAura(SPELL_MIND_EXHAUSTION) && !player->GetPet())
if (Creature* razor = ObjectAccessor::GetCreature(*go, instance->GetGuidData(DATA_RAZORGORE_THE_UNTAMED)))
{
razor->AI()->SetGUID(player->GetGUID());
@@ -54,9 +54,11 @@ struct boss_curator : public BossAI
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_POWER_BURN, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_POWER_BURN, true);
ScheduleHealthCheckEvent(15, [&] {
DoCastSelf(SPELL_ARCANE_INFUSION, true);
Talk(SAY_ENRAGE);
me->InterruptNonMeleeSpells(true);
DoCastSelf(SPELL_ARCANE_INFUSION, true);
Talk(SAY_ENRAGE);
});
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA));
}
void KilledUnit(Unit* victim) override
@@ -85,7 +87,7 @@ struct boss_curator : public BossAI
DoCastSelf(SPELL_ASTRAL_DECONSTRUCTION, true);
}).Schedule(10s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 0, 45.0f, true, false))
if (Unit* target = SelectTarget(SelectTargetMethod::MaxThreat, 1, 45.0f, true, false))
{
DoCast(target, SPELL_HATEFUL_BOLT);
}
@@ -218,13 +218,17 @@ struct boss_netherspite : public BossAI
}
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
UpdatePortals();
context.Repeat(1s);
UpdatePortals();
context.Repeat(1s);
}).Schedule(10s, PORTAL_PHASE, [this](TaskContext context)
{
DoCastSelf(SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
context.Repeat(90s);
DoCastSelf(SPELL_EMPOWERMENT);
me->AddAura(SPELL_NETHERBURN_AURA, me);
context.Repeat(90s);
}).Schedule(15s, PORTAL_PHASE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_VOIDZONE, 1, 45.0f, true, true);
context.Repeat(15s);
});
Talk(EMOTE_PHASE_PORTAL);
}
@@ -237,13 +241,10 @@ struct boss_netherspite : public BossAI
DoCastSelf(SPELL_BANISH_VISUAL, true);
DoCastSelf(SPELL_BANISH_ROOT, true);
DestroyPortals();
scheduler.Schedule(30s, [this](TaskContext /*context*/)
scheduler.Schedule(30s, [this](TaskContext)
{
if (!me->IsNonMeleeSpellCast(false))
{
SwitchToPortalPhase();
return;
}
SwitchToPortalPhase();
return;
}).Schedule(10s, VANISH_PHASE, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_NETHERBREATH, 0, 40.0f, true);
@@ -270,11 +271,7 @@ struct boss_netherspite : public BossAI
HandleDoors(false);
SwitchToPortalPhase();
DoZoneInCombat();
scheduler.Schedule(15s, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_VOIDZONE, 1, 45.0f, true, true);
context.Repeat(15s);
}).Schedule(9min, [this](TaskContext /*context*/)
scheduler.Schedule(9min, [this](TaskContext /*context*/)
{
if (!berserk)
{
@@ -18,6 +18,7 @@
#include "ScriptMgr.h"
#include "ScriptedCreature.h"
#include "SpellInfo.h"
#include "SpellScript.h"
#include "karazhan.h"
enum PrinceSay
@@ -47,11 +48,13 @@ enum Spells
enum creatures
{
NPC_NETHERSPITE_INFERNAL = 17646,
NPC_MALCHEZARS_AXE = 17650,
INFERNAL_MODEL_INVISIBLE = 11686,
SPELL_INFERNAL_RELAY = 33814, // 30835,
EQUIP_ID_AXE = 33542
NPC_NETHERSPITE_INFERNAL = 17646,
NPC_MALCHEZAARS_AXE = 17650,
INFERNAL_MODEL_INVISIBLE = 11686,
SPELL_INFERNAL_RELAY = 33814, // 30835,
SPELL_INFERNAL_RELAY_ONE = 30834,
SPELL_INFERNAL_RELAY_TWO = 30835,
EQUIP_ID_AXE = 33542
};
enum EventGroups
@@ -68,44 +71,19 @@ enum Phases
PHASE_THREE = 3
};
struct InfernalPoint
{
float x, y;
};
#define INFERNAL_Z 275.5f
/*static InfernalPoint InfernalPoints[] =
{
{ -10922.8f, -1985.2f },
{ -10916.2f, -1996.2f },
{ -10932.2f, -2008.1f },
{ -10948.8f, -2022.1f },
{ -10958.7f, -1997.7f },
{ -10971.5f, -1997.5f },
{ -10990.8f, -1995.1f },
{ -10989.8f, -1976.5f },
{ -10971.6f, -1973.0f },
{ -10955.5f, -1974.0f },
{ -10939.6f, -1969.8f },
{ -10958.0f, -1952.2f },
{ -10941.7f, -1954.8f },
{ -10943.1f, -1988.5f },
{ -10948.8f, -2005.1f },
{ -10984.0f, -2019.3f },
{ -10932.8f, -1979.6f },
{ -10935.7f, -1996.0f }
};*/
struct boss_malchezaar : public BossAI
{
boss_malchezaar(Creature* creature) : BossAI(creature, DATA_MALCHEZZAR) { }
boss_malchezaar(Creature* creature) : BossAI(creature, DATA_MALCHEZAAR) { }
std::list<Creature*> relays;
std::list<Creature*> infernalTargets;
void Initialize()
{
_phase = 1;
clearweapons();
positions.clear();
relays.clear();
infernalTargets.clear();
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
}
@@ -145,7 +123,7 @@ struct boss_malchezaar : public BossAI
_phase = PHASE_THREE;
clearweapons();
me->SummonCreature(NPC_MALCHEZARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
me->SummonCreature(NPC_MALCHEZAARS_AXE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
scheduler.Schedule(20s, 30s, [this](TaskContext context)
{
@@ -174,15 +152,52 @@ struct boss_malchezaar : public BossAI
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), true);
}
void SpawnInfernal(Creature* relay, Creature* target)
{
if (Creature* infernal = relay->SummonCreature(NPC_NETHERSPITE_INFERNAL, target->GetPosition(), TEMPSUMMON_TIMED_DESPAWN, 180000))
{
infernal->SetDisplayId(INFERNAL_MODEL_INVISIBLE);
relay->CastSpell(infernal, SPELL_INFERNAL_RELAY);
infernal->SetFaction(me->GetFaction());
infernal->SetControlled(true, UNIT_STATE_ROOT);
relay->CastSpell(infernal, SPELL_INFERNAL_RELAY);
summons.Summon(infernal);
}
}
bool MaxSpawns(std::list<Creature*> spawns)
{
return spawns.size() == 0;
}
Creature* PickTarget(std::list<Creature*> pickList)
{
uint8 index = urand(0, pickList.size()-1);
uint8 counter = 0;
for (Creature* creature : pickList)
{
if (counter == index)
{
return creature;
}
counter++;
}
return nullptr;
}
void JustEngagedWith(Unit* /*who*/) override
{
Talk(SAY_AGGRO);
_JustEngagedWith();
me->GetCreaturesWithEntryInRange(relays, 250.0f, NPC_INFERNAL_RELAY);
me->GetCreaturesWithEntryInRange(infernalTargets, 100.0f, NPC_INFERNAL_TARGET);
instance->HandleGameObject(instance->GetGuidData(DATA_GO_NETHER_DOOR), false);
scheduler.Schedule(30s, [this](TaskContext context)
{
EnfeebleHealthEffect();
DoCastAOE(SPELL_ENFEEBLE);
scheduler.Schedule(9s, [this](TaskContext)
{
@@ -199,23 +214,30 @@ struct boss_malchezaar : public BossAI
context.Repeat();
}).Schedule(40s, [this](TaskContext context)
{
Position pos = me->GetRandomNearPosition(40.0);;
if (Creature* RELAY = me->FindNearestCreature(NPC_RELAY, 100.0f))
if (!MaxSpawns(infernalTargets)) // only spawn infernal when the area is not full
{
if (Creature* infernal = RELAY->SummonCreature(NPC_NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000))
Talk(SAY_SUMMON);
if (Creature* infernalRelayOne = relays.back())
{
infernal->SetDisplayId(INFERNAL_MODEL_INVISIBLE);
infernal->SetFaction(me->GetFaction());
infernal->SetControlled(true, UNIT_STATE_ROOT);
RELAY->AI()->DoCast(infernal, SPELL_INFERNAL_RELAY);
summons.Summon(infernal);
if (Creature* infernalRelayTwo = relays.front())
{
infernalRelayOne->CastSpell(infernalRelayTwo, SPELL_INFERNAL_RELAY_ONE, true);
if (Creature* infernalTarget = PickTarget(infernalTargets))
{
infernalTargets.remove(infernalTarget);
SpawnInfernal(infernalRelayTwo, infernalTarget);
scheduler.Schedule(3min, [this, infernalTarget](TaskContext)
{
infernalTargets.push_back(infernalTarget); //adds to list again
});
}
}
}
}
context.Repeat(_phase == PHASE_THREE ? 15s : 45s);
Talk(SAY_SUMMON);
}).Schedule(20s, [this](TaskContext context)
{
DoCastVictim(SPELL_SHADOW_WORD_PAIN);
@@ -224,23 +246,12 @@ struct boss_malchezaar : public BossAI
});
}
void EnfeebleHealthEffect()
void SpellHitTarget(Unit* target, SpellInfo const* spell) override
{
std::list<Unit*> targetList;
SelectTargetList(targetList, 5, SelectTargetMethod::Random, 1, [&](Unit* u) { return u->IsAlive() && u->IsPlayer(); });
if (targetList.empty())
return;
for (auto const& target : targetList)
if (spell->Id == SPELL_ENFEEBLE)
{
if (target)
{
_enfeebleTargets[target->GetGUID()] = target->GetHealth();
me->CastSpell(target, SPELL_ENFEEBLE, true);
target->SetHealth(1);
}
_enfeebleTargets[target->GetGUID()] = target->GetHealth();
target->SetHealth(1);
}
}
@@ -261,7 +272,6 @@ struct boss_malchezaar : public BossAI
private:
uint32 _phase;
std::map<ObjectGuid, uint32> _enfeebleTargets;
std::vector<InfernalPoint*> positions;
};
struct npc_netherspite_infernal : public ScriptedAI
@@ -357,9 +367,43 @@ struct npc_malchezaar_axe : public ScriptedAI
TaskScheduler _scheduler;
};
// 30843 - Enfeeble
class spell_malchezaar_enfeeble : public SpellScript
{
PrepareSpellScript(spell_malchezaar_enfeeble);
bool Load() override
{
return GetCaster()->ToCreature();
}
void FilterTargets(std::list<WorldObject*>& targets)
{
uint8 maxSize = 5;
Unit* caster = GetCaster();
targets.remove_if([caster](WorldObject const* target) -> bool
{
// Should not target current victim.
return caster->GetVictim() == target;
});
if (targets.size() > maxSize)
{
Acore::Containers::RandomResize(targets, maxSize);
}
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_malchezaar_enfeeble::FilterTargets, EFFECT_ALL, TARGET_UNIT_SRC_AREA_ENEMY);
}
};
void AddSC_boss_malchezaar()
{
RegisterKarazhanCreatureAI(boss_malchezaar);
RegisterKarazhanCreatureAI(npc_malchezaar_axe);
RegisterKarazhanCreatureAI(npc_netherspite_infernal);
RegisterSpellScript(spell_malchezaar_enfeeble);
}
@@ -55,7 +55,6 @@ enum ShadeOfAran
//Creature Spells
SPELL_CIRCULAR_BLIZZARD = 29951,
SPELL_WATERBOLT = 31012,
SPELL_SHADOW_PYRO = 29978,
//Creatures
@@ -77,6 +76,8 @@ enum Groups
GROUP_DRINKING = 1
};
Position const roomCenter = {-11158.f, -1920.f};
Position const elementalPos[4] =
{
{-11168.1f, -1939.29f, 232.092f, 1.46f},
@@ -103,12 +104,10 @@ struct boss_shade_of_aran : public BossAI
uint32 CurrentNormalSpell;
bool Drinking;
void Reset() override
{
BossAI::Reset();
drinkScheduler.CancelAll();
_drinkScheduler.CancelAll();
LastSuperSpell = rand() % 3;
for (uint8 i = 0; i < 3; ++i)
@@ -120,7 +119,7 @@ struct boss_shade_of_aran : public BossAI
_fireCooledDown = true;
_frostCooledDown = true;
Drinking = false;
_drinking = false;
// Not in progress
instance->SetData(DATA_ARAN, NOT_STARTED);
@@ -149,6 +148,23 @@ struct boss_shade_of_aran : public BossAI
});
}
bool CheckAranInRoom()
{
return me->GetDistance2d(roomCenter.GetPositionX(), roomCenter.GetPositionY()) < 45.0f;
}
void AttackStart(Unit* who) override
{
if (who && who->isTargetableForAttack() && me->GetReactState() != REACT_PASSIVE)
{
if (me->Attack(who, false))
{
me->GetMotionMaster()->MoveChase(who, 45.0f, 0);
me->AddThreat(who, 0.0f);
}
}
}
void KilledUnit(Unit* /*victim*/) override
{
Talk(SAY_KILL);
@@ -207,9 +223,9 @@ struct boss_shade_of_aran : public BossAI
libraryDoor->SetGoState(GO_STATE_READY);
libraryDoor->SetGameObjectFlag(GO_FLAG_NOT_SELECTABLE);
}
}).Schedule(1ms, [this](TaskContext context)
}).Schedule(1s, [this](TaskContext context)
{
if (!me->IsNonMeleeSpellCast(false))
if (!me->IsNonMeleeSpellCast(false) && !_drinking)
{
Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true);
if (!target)
@@ -242,93 +258,144 @@ struct boss_shade_of_aran : public BossAI
DoCast(target, CurrentNormalSpell);
}
}
context.Repeat(10s);
context.Repeat(2s);
}).Schedule(5s, [this](TaskContext context)
{
switch (urand(0, 1))
if (!_drinking)
{
case 0:
DoCastSelf(SPELL_AOE_CS);
break;
case 1:
DoCastRandomTarget(SPELL_CHAINSOFICE);
break;
switch (urand(0, 1))
{
case 0:
DoCastSelf(SPELL_AOE_CS);
break;
case 1:
DoCastRandomTarget(SPELL_CHAINSOFICE);
break;
}
}
context.Repeat(5s, 20s);
}).Schedule(35s, [this](TaskContext context)
{
uint8 Available[2];
switch (LastSuperSpell)
if (!_drinking)
{
case SUPER_AE:
Available[0] = SUPER_FLAME;
Available[1] = SUPER_BLIZZARD;
break;
case SUPER_FLAME:
Available[0] = SUPER_AE;
Available[1] = SUPER_BLIZZARD;
break;
case SUPER_BLIZZARD:
Available[0] = SUPER_FLAME;
Available[1] = SUPER_AE;
break;
}
uint8 Available[2];
LastSuperSpell = Available[urand(0, 1)];
switch (LastSuperSpell)
{
case SUPER_AE:
Available[0] = SUPER_FLAME;
Available[1] = SUPER_BLIZZARD;
break;
case SUPER_FLAME:
Available[0] = SUPER_AE;
Available[1] = SUPER_BLIZZARD;
break;
case SUPER_BLIZZARD:
Available[0] = SUPER_FLAME;
Available[1] = SUPER_AE;
break;
}
switch (LastSuperSpell)
{
case SUPER_AE:
Talk(SAY_EXPLOSION);
LastSuperSpell = Available[urand(0, 1)];
DoCastSelf(SPELL_BLINK_CENTER, true);
DoCastSelf(SPELL_PLAYERPULL, true);
DoCastSelf(SPELL_MASSSLOW, true);
DoCastSelf(SPELL_AEXPLOSION, false);
break;
switch (LastSuperSpell)
{
case SUPER_AE:
Talk(SAY_EXPLOSION);
case SUPER_FLAME:
Talk(SAY_FLAMEWREATH);
DoCastSelf(SPELL_BLINK_CENTER, true);
DoCastSelf(SPELL_PLAYERPULL, true);
DoCastSelf(SPELL_MASSSLOW, true);
DoCastSelf(SPELL_AEXPLOSION, false);
break;
scheduler.Schedule(20s, GROUP_FLAMEWREATH, [this](TaskContext)
{
scheduler.CancelGroup(GROUP_FLAMEWREATH);
}).Schedule(500ms, GROUP_FLAMEWREATH, [this](TaskContext context)
{
for (uint8 i = 0; i < 3; ++i)
case SUPER_FLAME:
Talk(SAY_FLAMEWREATH);
scheduler.Schedule(20s, GROUP_FLAMEWREATH, [this](TaskContext)
{
if (!FlameWreathTarget[i])
continue;
Unit* unit = ObjectAccessor::GetUnit(*me, FlameWreathTarget[i]);
if (unit && !unit->IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3))
scheduler.CancelGroup(GROUP_FLAMEWREATH);
}).Schedule(500ms, GROUP_FLAMEWREATH, [this](TaskContext context)
{
for (uint8 i = 0; i < 3; ++i)
{
unit->CastSpell(unit, 20476, true, 0, 0, me->GetGUID());
FlameWreathTarget[i].Clear();
if (!FlameWreathTarget[i])
continue;
Unit* unit = ObjectAccessor::GetUnit(*me, FlameWreathTarget[i]);
if (unit && !unit->IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3))
{
unit->CastSpell(unit, 20476, true, 0, 0, me->GetGUID());
FlameWreathTarget[i].Clear();
}
}
context.Repeat(500ms);
});
FlameWreathTarget[0].Clear();
FlameWreathTarget[1].Clear();
FlameWreathTarget[2].Clear();
FlameWreathEffect();
break;
case SUPER_BLIZZARD:
Talk(SAY_BLIZZARD);
if (Creature* pSpawn = me->SummonCreature(NPC_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000))
{
pSpawn->SetFaction(me->GetFaction());
pSpawn->CastSpell(me, SPELL_CIRCULAR_BLIZZARD, false);
}
context.Repeat(500ms);
});
FlameWreathTarget[0].Clear();
FlameWreathTarget[1].Clear();
FlameWreathTarget[2].Clear();
FlameWreathEffect();
break;
case SUPER_BLIZZARD:
Talk(SAY_BLIZZARD);
if (Creature* pSpawn = me->SummonCreature(NPC_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000))
{
pSpawn->SetFaction(me->GetFaction());
pSpawn->CastSpell(me, SPELL_CIRCULAR_BLIZZARD, false);
}
break;
break;
}
}
context.Repeat(35s, 40s);
}).Schedule(1s, [this](TaskContext context){
if (me->GetMaxPower(POWER_MANA) && (me->GetPower(POWER_MANA) * 100 / me->GetMaxPower(POWER_MANA)) < 20)
{
_drinking = true;
me->InterruptNonMeleeSpells(true);
Talk(SAY_DRINK);
DoCastSelf(SPELL_MASS_POLY, true);
DoCastSelf(SPELL_CONJURE, false);
me->SetReactState(REACT_PASSIVE);
me->SetStandState(UNIT_STAND_STATE_SIT);
DoCastSelf(SPELL_DRINK, true);
_currentHealth = me->GetHealth();
_drinkScheduler.Schedule(500ms, GROUP_DRINKING, [this](TaskContext context)
{
//check for damage to interrupt
if (me->GetHealth() < _currentHealth)
{
me->RemoveAurasDueToSpell(SPELL_DRINK);
me->SetStandState(UNIT_STAND_STATE_STAND);
me->SetReactState(REACT_AGGRESSIVE);
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000);
DoCastSelf(SPELL_POTION, false);
DoCastSelf(SPELL_AOE_PYROBLAST, false);
_drinkScheduler.CancelGroup(GROUP_DRINKING);
_drinking = false;
} else
{
context.Repeat(500ms);
}
}).Schedule(10s, GROUP_DRINKING, [this](TaskContext)
{
me->SetStandState(UNIT_STAND_STATE_STAND);
me->SetReactState(REACT_AGGRESSIVE);
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000);
DoCastSelf(SPELL_POTION, true);
DoCastSelf(SPELL_AOE_PYROBLAST, false);
_drinkScheduler.CancelGroup(GROUP_DRINKING);
_drinking = false;
});
context.Repeat(12s); //semi-arbitrary duration to envelop drinking duration
}
else
{
context.Repeat(1s);
}
}).Schedule(12min, [this](TaskContext context)
{
for (uint32 i = 0; i < 5; ++i)
@@ -384,70 +451,21 @@ struct boss_shade_of_aran : public BossAI
void UpdateAI(uint32 diff) override
{
scheduler.Update(diff);
drinkScheduler.Update(diff);
_drinkScheduler.Update(diff);
if (!UpdateVictim())
return;
if (!Drinking && me->GetMaxPower(POWER_MANA) && (me->GetPower(POWER_MANA) * 100 / me->GetMaxPower(POWER_MANA)) < 20)
if (!CheckAranInRoom())
{
Drinking = true;
me->InterruptNonMeleeSpells(false);
Talk(SAY_DRINK);
scheduler.DelayAll(10s);
DoCastSelf(SPELL_MASS_POLY, true);
DoCastSelf(SPELL_CONJURE, false);
me->SetReactState(REACT_PASSIVE);
me->SetStandState(UNIT_STAND_STATE_SIT);
DoCastSelf(SPELL_DRINK, true);
_currentHealth = me->GetHealth();
drinkScheduler.Schedule(500ms, GROUP_DRINKING, [this](TaskContext context)
{
//check for damage to interrupt
if(CheckDamageDuringDrinking(_currentHealth))
{
Drinking = false;
me->RemoveAurasDueToSpell(SPELL_DRINK);
me->SetStandState(UNIT_STAND_STATE_STAND);
me->SetReactState(REACT_AGGRESSIVE);
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000);
DoCastSelf(SPELL_POTION, false);
DoCastSelf(SPELL_AOE_PYROBLAST, false);
drinkScheduler.CancelGroup(GROUP_DRINKING);
} else {
context.Repeat(500ms);
}
}).Schedule(10s, GROUP_DRINKING, [this](TaskContext)
{
me->SetStandState(UNIT_STAND_STATE_STAND);
me->SetReactState(REACT_AGGRESSIVE);
me->SetPower(POWER_MANA, me->GetMaxPower(POWER_MANA) - 32000);
DoCastSelf(SPELL_POTION, true);
DoCastSelf(SPELL_AOE_PYROBLAST, false);
Drinking = false;
drinkScheduler.CancelGroup(GROUP_DRINKING);
});
EnterEvadeMode();
return;
}
if (_arcaneCooledDown && _fireCooledDown && _frostCooledDown && !Drinking)
if (_arcaneCooledDown && _fireCooledDown && _frostCooledDown && !_drinking)
DoMeleeAttackIfReady();
}
bool CheckDamageDuringDrinking(uint32 oldHealth)
{
if (Drinking)
{
if (me->GetHealth() < oldHealth)
{
return true;
}
}
return false;
}
void SpellHit(Unit* /*pAttacker*/, SpellInfo const* Spell) override
{
//We only care about interrupt effects and only if they are durring a spell currently being cast
@@ -476,52 +494,16 @@ struct boss_shade_of_aran : public BossAI
}
}
private:
TaskScheduler drinkScheduler;
TaskScheduler _drinkScheduler;
bool _arcaneCooledDown;
bool _fireCooledDown;
bool _frostCooledDown;
bool _drinking;
uint32 _currentHealth;
};
struct npc_aran_elemental : public ScriptedAI
{
npc_aran_elemental(Creature* creature) : ScriptedAI(creature)
{
SetCombatMovement(false);
_scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
void Reset() override
{
_scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(2s, [this](TaskContext context)
{
DoCastVictim(SPELL_WATERBOLT);
context.Repeat(2s);
});
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_scheduler.Update(diff);
}
private:
TaskScheduler _scheduler;
};
void AddSC_boss_shade_of_aran()
{
RegisterKarazhanCreatureAI(boss_shade_of_aran);
RegisterKarazhanCreatureAI(npc_aran_elemental);
}
@@ -52,7 +52,6 @@ enum Spells
enum Creatures
{
NPC_DEMONCHAINS = 17248,
NPC_FIENDISHIMP = 17267,
NPC_PORTAL = 17265
};
@@ -146,64 +145,6 @@ private:
ObjectGuid sacrificeGUID;
};
struct npc_fiendish_portal : public PassiveAI
{
npc_fiendish_portal(Creature* creature) : PassiveAI(creature), summons(me) {}
void Reset() override
{
DespawnAllImp();
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
DoZoneInCombat(summon);
}
void DespawnAllImp()
{
summons.DespawnAll();
}
private:
SummonList summons;
};
struct npc_fiendish_imp : public ScriptedAI
{
npc_fiendish_imp(Creature* creature) : ScriptedAI(creature) {}
void Reset() override
{
_scheduler.CancelAll();
}
void JustEngagedWith(Unit* /*who*/) override
{
_scheduler.Schedule(2s, [this](TaskContext context)
{
DoCastVictim(SPELL_FIREBOLT);
context.Repeat(2200ms);
});
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
_scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
private:
TaskScheduler _scheduler;
};
struct boss_terestian_illhoof : public BossAI
{
boss_terestian_illhoof(Creature* creature) : BossAI(creature, DATA_TERESTIAN)
@@ -218,20 +159,6 @@ struct boss_terestian_illhoof : public BossAI
{
_Reset();
SummonKilrek();
portalsCount = 0;
berserk = false;
for (uint8 i = 0; i < 2; ++i)
{
if (portalGUID[i])
{
if (Creature* pPortal = ObjectAccessor::GetCreature(*me, portalGUID[i]))
{
pPortal->AI()->Reset();
pPortal->DespawnOrUnsummon();
}
portalGUID[i].Clear();
}
}
}
void SummonKilrek()
@@ -256,10 +183,13 @@ struct boss_terestian_illhoof : public BossAI
DoZoneInCombat();
scheduler.Schedule(30s, [this](TaskContext context)
{
if (Unit * target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, false))
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100.0f, true, false))
{
DoCast(target, SPELL_SACRIFICE, true);
target->CastSpell(target, SPELL_SUMMON_DEMONCHAINS, true);
target->m_Events.AddEventAtOffset([target] {
target->CastSpell(target, SPELL_SUMMON_DEMONCHAINS, true);
}, 1s);
Talk(SAY_SACRIFICE);
context.Repeat(30s);
}
@@ -267,31 +197,15 @@ struct boss_terestian_illhoof : public BossAI
{
DoCastVictim(SPELL_SHADOW_BOLT);
context.Repeat(10s);
}).Schedule(10s, [this](TaskContext context)
}).Schedule(10s, [this](TaskContext)
{
if (!portalGUID[0])
{
DoCastVictim(SPELL_FIENDISH_PORTAL);
}
if (!portalGUID[1])
{
DoCastVictim(SPELL_FIENDISH_PORTAL_1);
}
if (portalGUID[0] && portalGUID[1])
{
if (Creature* pPortal = ObjectAccessor::GetCreature(*me, portalGUID[urand(0, 1)]))
{
pPortal->CastSpell(me->GetVictim(), SPELL_SUMMON_FIENDISIMP);
}
context.Repeat(5s);
}
DoCastAOE(SPELL_FIENDISH_PORTAL);
}).Schedule(11s, [this](TaskContext)
{
DoCastAOE(SPELL_FIENDISH_PORTAL_1);
}).Schedule(10min, [this](TaskContext /*context*/)
{
if (!berserk)
{
DoCastSelf(SPELL_BERSERK);
berserk = true;
}
DoCastSelf(SPELL_BERSERK);
});
}
@@ -299,13 +213,14 @@ struct boss_terestian_illhoof : public BossAI
{
if (summoned->GetEntry() == NPC_PORTAL)
{
portalGUID[portalsCount] = summoned->GetGUID();
++portalsCount;
summoned->SetReactState(REACT_PASSIVE);
if (summoned->GetUInt32Value(UNIT_CREATED_BY_SPELL) == SPELL_FIENDISH_PORTAL_1)
{
Talk(SAY_SUMMON);
}
}
summons.Summon(summoned);
}
void KilledUnit(Unit* victim) override
@@ -318,44 +233,14 @@ struct boss_terestian_illhoof : public BossAI
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
for (uint8 i = 0; i < 2; ++i)
{
if (portalGUID[i])
{
if (Creature* pPortal = ObjectAccessor::GetCreature((*me), portalGUID[i]))
{
pPortal->AI()->Reset();
pPortal->DespawnOrUnsummon();
}
portalGUID[i].Clear();
}
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
scheduler.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
DoMeleeAttackIfReady();
}
private:
bool berserk;
ObjectGuid portalGUID[2];
uint8 portalsCount;
};
void AddSC_boss_terestian_illhoof()
{
RegisterKarazhanCreatureAI(boss_terestian_illhoof);
RegisterKarazhanCreatureAI(npc_fiendish_imp);
RegisterKarazhanCreatureAI(npc_fiendish_portal);
RegisterKarazhanCreatureAI(npc_kilrek);
RegisterKarazhanCreatureAI(npc_demon_chain);
}
@@ -508,7 +508,7 @@ struct boss_strawman : public ScriptedAI
void SpellHit(Unit* /*caster*/, SpellInfo const* Spell) override
{
if ((Spell->SchoolMask == SPELL_SCHOOL_MASK_FIRE) && (!(rand() % 10)))
if ((Spell->SchoolMask == SPELL_SCHOOL_MASK_FIRE) && roll_chance_i(10))
{
/*
if (not direct damage(aoe, dot))
@@ -44,6 +44,7 @@ ObjectData const creatureData[] =
{ NPC_ROMULO, DATA_ROMULO },
{ NPC_JULIANNE, DATA_JULIANNE },
{ NPC_NIGHTBANE, DATA_NIGHTBANE },
{ NPC_TERESTIAN_ILLHOOF, DATA_TERESTIAN },
{ 0, 0 }
};
@@ -122,6 +123,16 @@ public:
case NPC_ECHO_OF_MEDIVH:
_echoOfMedivhGUID = creature->GetGUID();
break;
case NPC_FIENDISH_IMP:
if (Creature* terestrian = GetCreature(DATA_TERESTIAN))
{
if (terestrian->AI())
{
terestrian->AI()->JustSummoned(creature);
creature->SetInCombatWithZone();
}
}
break;
default:
break;
}
@@ -252,7 +263,6 @@ public:
piece->NearTeleportTo(x, y, z, o);
piece->AI()->DoAction(ACTION_CHESS_PIECE_RESET_ORIENTATION);
piece->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
piece->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP);
piece->AI()->Reset();
}
}
@@ -37,7 +37,7 @@ enum KZDataTypes
DATA_TERESTIAN = 7,
DATA_NETHERSPITE = 8,
DATA_CHESS_EVENT = 9,
DATA_MALCHEZZAR = 10,
DATA_MALCHEZAAR = 10,
DATA_NIGHTBANE = 11,
DATA_SERVANT_QUARTERS = 12,
DATA_OPERA_OZ_DEATHCOUNT = 13,
@@ -117,6 +117,7 @@ enum KZCreatures
NPC_ROAR = 17546,
NPC_STRAWMAN = 17543,
NPC_TINHEAD = 17547,
NPC_FIENDISH_IMP = 17267,
// Chess Event
NPC_ECHO_OF_MEDIVH = 16816,
@@ -132,7 +133,12 @@ enum KZCreatures
NPC_ROOK_A = 21160,
NPC_KING_H = 21752,
NPC_KING_A = 21684,
NPC_CHESS_EVENT_MEDIVH_CHEAT_FIRES = 22521
NPC_CHESS_EVENT_MEDIVH_CHEAT_FIRES = 22521,
// Malchezaar Helpers
NPC_INFERNAL_TARGET = 17644,
NPC_INFERNAL_RELAY = 17645
};
enum KZGameObjectIds
@@ -16,9 +16,18 @@
*/
#include "InstanceScript.h"
#include "ScriptedCreature.h"
#include "ScriptMgr.h"
#include "magisters_terrace.h"
ObjectData const creatureData[] =
{
{ NPC_KALECGOS, DATA_KALECGOS },
{ 0, 0 }
};
Position const KalecgosSpawnPos = { 164.3747f, -397.1197f, 2.151798f, 1.66219f };
class instance_magisters_terrace : public InstanceMapScript
{
public:
@@ -29,6 +38,7 @@ public:
instance_magisters_terrace_InstanceMapScript(Map* map) : InstanceScript(map)
{
SetHeaders(DataHeader);
LoadObjectData(creatureData, nullptr);
}
uint32 Encounter[MAX_ENCOUNTER];
@@ -69,6 +79,24 @@ public:
return 0;
}
void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override
{
if (eventId == EVENT_SPAWN_KALECGOS)
{
if (!GetCreature(DATA_KALECGOS) && !scheduler.IsGroupScheduled(DATA_KALECGOS))
{
scheduler.Schedule(1min, 1min, DATA_KALECGOS,[this](TaskContext)
{
if (Creature* kalecgos = instance->SummonCreature(NPC_KALECGOS, KalecgosSpawnPos))
{
kalecgos->GetMotionMaster()->MovePath(PATH_KALECGOS_FLIGHT, false);
kalecgos->AI()->Talk(SAY_KALECGOS_SPAWN);
}
});
}
}
}
void SetData(uint32 identifier, uint32 data) override
{
switch (identifier)
@@ -116,6 +144,8 @@ public:
kael->AI()->JustSummoned(creature);
break;
}
InstanceScript::OnCreatureCreate(creature);
}
void OnGameObjectCreate(GameObject* go) override
@@ -184,7 +214,53 @@ public:
}
};
enum Spells
{
SPELL_KALECGOS_TRANSFORM = 44670,
SPELL_TRANSFORM_VISUAL = 24085,
SPELL_CAMERA_SHAKE = 44762,
SPELL_ORB_KILL_CREDIT = 46307
};
enum MovementPoints
{
POINT_ID_PREPARE_LANDING = 6
};
struct npc_kalecgos : public ScriptedAI
{
npc_kalecgos(Creature* creature) : ScriptedAI(creature) { }
void MovementInform(uint32 type, uint32 pointId) override
{
if (type != WAYPOINT_MOTION_TYPE)
return;
if (pointId == POINT_ID_PREPARE_LANDING)
{
me->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
me->SetDisableGravity(false);
me->SetHover(false);
me->m_Events.AddEventAtOffset([this]()
{
DoCastAOE(SPELL_CAMERA_SHAKE);
me->SetObjectScale(0.6f);
me->m_Events.AddEventAtOffset([this]()
{
DoCastSelf(SPELL_ORB_KILL_CREDIT, true);
DoCastSelf(SPELL_TRANSFORM_VISUAL);
DoCastSelf(SPELL_KALECGOS_TRANSFORM);
me->UpdateEntry(NPC_HUMAN_KALECGOS);
}, 1s);
}, 2s);
}
}
};
void AddSC_instance_magisters_terrace()
{
new instance_magisters_terrace();
RegisterMagistersTerraceCreatureAI(npc_kalecgos);
}
@@ -32,7 +32,9 @@ enum MTData
DATA_VEXALLUS_EVENT = 1,
DATA_DELRISSA_EVENT = 2,
DATA_KAELTHAS_EVENT = 3,
MAX_ENCOUNTER = 4
MAX_ENCOUNTER = 4,
DATA_KALECGOS = 5
};
enum MTCreatures
@@ -41,7 +43,9 @@ enum MTCreatures
NPC_FEL_CRYSTAL = 24722,
NPC_KAEL_THAS = 24664,
NPC_PHOENIX = 21362,
NPC_PHOENIX_EGG = 21364
NPC_PHOENIX_EGG = 21364,
NPC_KALECGOS = 24844,
NPC_HUMAN_KALECGOS = 24848
};
enum MTGameObjects
@@ -54,10 +58,27 @@ enum MTGameObjects
GO_ESCAPE_ORB = 188173
};
enum InstanceEventIds
{
EVENT_SPAWN_KALECGOS = 16547
};
enum InstanceText
{
SAY_KALECGOS_SPAWN = 0
};
enum MovementData
{
PATH_KALECGOS_FLIGHT = 248440
};
template <class AI, class T>
inline AI* GetMagistersTerraceAI(T* obj)
{
return GetInstanceAI<AI>(obj, MTScriptName);
}
#define RegisterMagistersTerraceCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetMagistersTerraceAI)
#endif
@@ -695,106 +695,6 @@ class npc_fairbanks : public CreatureScript
public:
npc_fairbanks() : CreatureScript("npc_fairbanks") { }
bool OnGossipHello(Player* player, Creature* creature) override
{
AddGossipItemFor(player, 0, "Curse? What's going on here, Fairbanks?", GOSSIP_SENDER_MAIN, 1);
SendGossipMenuFor(player, 100100, creature->GetGUID());
return true;
}
bool OnGossipSelect(Player* player, Creature* creature, uint32 /*Sender*/, uint32 uiAction) override
{
ClearGossipMenuFor(player);
switch (uiAction)
{
case 1:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "Mograine?", GOSSIP_SENDER_MAIN, 2);
SendGossipMenuFor(player, 100101, creature->GetGUID());
return true;
case 2:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "What do you mean?", GOSSIP_SENDER_MAIN, 3);
SendGossipMenuFor(player, 100102, creature->GetGUID());
return true;
case 3:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "I still do not fully understand.", GOSSIP_SENDER_MAIN, 4);
SendGossipMenuFor(player, 100103, creature->GetGUID());
return true;
case 4:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "Incredible story. So how did he die?", GOSSIP_SENDER_MAIN, 5);
SendGossipMenuFor(player, 100104, creature->GetGUID());
return true;
case 5:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "You mean...", GOSSIP_SENDER_MAIN, 6);
SendGossipMenuFor(player, 100105, creature->GetGUID());
return true;
case 6:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "How do you know all of this?", GOSSIP_SENDER_MAIN, 7);
SendGossipMenuFor(player, 100106, creature->GetGUID());
return true;
case 7:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "A thousand? For one man?", GOSSIP_SENDER_MAIN, 8);
SendGossipMenuFor(player, 100107, creature->GetGUID());
return true;
case 8:
creature->HandleEmoteCommand(5);
AddGossipItemFor(player, 0, "Yet? Yet what?", GOSSIP_SENDER_MAIN, 9);
SendGossipMenuFor(player, 100108, creature->GetGUID());
return true;
case 9:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "And did he?", GOSSIP_SENDER_MAIN, 10);
SendGossipMenuFor(player, 100109, creature->GetGUID());
return true;
case 10:
creature->HandleEmoteCommand(274);
AddGossipItemFor(player, 0, "Continue please, Fairbanks.", GOSSIP_SENDER_MAIN, 11);
SendGossipMenuFor(player, 100110, creature->GetGUID());
return true;
case 11:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "You mean...", GOSSIP_SENDER_MAIN, 12);
SendGossipMenuFor(player, 100111, creature->GetGUID());
return true;
case 12:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "You were right, Fairbanks. That is tragic.", GOSSIP_SENDER_MAIN, 13);
SendGossipMenuFor(player, 100112, creature->GetGUID());
return true;
case 13:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "And you did...", GOSSIP_SENDER_MAIN, 14);
SendGossipMenuFor(player, 100113, creature->GetGUID());
return true;
case 14:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "You tell an incredible tale, Fairbanks. What of the blade? Is it beyond redemption?", GOSSIP_SENDER_MAIN, 15);
SendGossipMenuFor(player, 100114, creature->GetGUID());
return true;
case 15:
creature->HandleEmoteCommand(1);
AddGossipItemFor(player, 0, "But his son is dead.", GOSSIP_SENDER_MAIN, 16);
SendGossipMenuFor(player, 100115, creature->GetGUID());
return true;
case 16:
SendGossipMenuFor(player, 100116, creature->GetGUID());
/// @todo: we need to play these 3 emote in sequence, we play only the last one right now.
creature->HandleEmoteCommand(274);
creature->HandleEmoteCommand(1);
creature->HandleEmoteCommand(397);
return true;
}
return true;
}
struct npc_fairbanksAI : public SmartAI
{
npc_fairbanksAI(Creature* creature) : SmartAI(creature) { }
@@ -812,7 +712,6 @@ public:
{
me->SetFaction(FACTION_FRIENDLY);
me->SetNpcFlag(UNIT_NPC_FLAG_GOSSIP);
me->SetSheath(SHEATH_STATE_UNARMED);
me->CastSpell(me, 57767, true);
me->SetDisplayId(16179);
me->SetFacingToObject(player);
@@ -154,8 +154,8 @@ public:
switch (type)
{
case DATA_RAJAXX_WAVE_ENGAGED:
_scheduler.CancelGroup(GROUP_RAJAXX_WAVE_TIMER);
_scheduler.Schedule(2min, [this](TaskContext context)
scheduler.CancelGroup(GROUP_RAJAXX_WAVE_TIMER);
scheduler.Schedule(2min, [this](TaskContext context)
{
CallNextRajaxxLeader();
context.SetGroup(GROUP_RAJAXX_WAVE_TIMER);
@@ -195,8 +195,8 @@ public:
case NPC_YEGGETH:
case NPC_PAKKON:
case NPC_ZERRAN:
_scheduler.CancelAll();
_scheduler.Schedule(1s, [this, formation](TaskContext /*context*/)
scheduler.CancelAll();
scheduler.Schedule(1s, [this, formation](TaskContext /*context*/)
{
if (!formation->IsAnyMemberAlive())
{
@@ -212,11 +212,6 @@ public:
}
}
void Update(uint32 diff) override
{
_scheduler.Update(diff);
}
void SetGuidData(uint32 type, ObjectGuid data) override
{
if (type == DATA_PARALYZED)
@@ -285,7 +280,7 @@ public:
void ResetRajaxxWaves()
{
_rajaxWaveCounter = 0;
_scheduler.CancelAll();
scheduler.CancelAll();
for (auto const& data : RajaxxWavesData)
{
if (Creature* creature = GetCreature(data.at(0)))
@@ -308,7 +303,6 @@ public:
ObjectGuid _andorovGUID;
uint32 _rajaxWaveCounter;
uint8 _buruPhase;
TaskScheduler _scheduler;
};
InstanceScript* GetInstanceScript(InstanceMap* map) const override
@@ -210,15 +210,9 @@ public:
return true;
}
void Update(uint32 diff) override
{
scheduler.Update(diff);
}
private:
GuidVector CThunGraspGUIDs;
uint32 BugTrioDeathCount;
TaskScheduler scheduler;
};
};
@@ -1,7 +1,18 @@
/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version.
* Copyright (C) 2008-2016 TrinityCore <http://www.trinitycore.org/>
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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 Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
@@ -175,6 +175,7 @@ enum Kerlonian
SPELL_SLEEP_VISUAL = 25148,
SPELL_AWAKEN = 17536,
SPELL_BEAR_FORM = 18309,
QUEST_SLEEPER_AWAKENED = 5321,
NPC_LILADRIS = 11219 //attackers entries unknown
};
@@ -194,6 +195,8 @@ public:
void Reset() override
{
FallAsleepTimer = urand(10000, 45000);
DoCastSelf(SPELL_BEAR_FORM);
}
void MoveInLineOfSight(Unit* who) override
@@ -69,219 +69,185 @@ enum Misc
{
GROUP_ABILITIES = 1,
NPC_PURE_SPAWN_OF_HYDROSS = 22035,
EVENT_SPELL_MARK_OF_CORRUPTION1 = 1,
EVENT_SPELL_MARK_OF_CORRUPTION2 = 2,
EVENT_SPELL_MARK_OF_CORRUPTION3 = 3,
EVENT_SPELL_MARK_OF_CORRUPTION4 = 4,
EVENT_SPELL_MARK_OF_CORRUPTION5 = 5,
EVENT_SPELL_MARK_OF_CORRUPTION6 = 6,
EVENT_SPELL_MARK_OF_HYDROSS1 = 7,
EVENT_SPELL_MARK_OF_HYDROSS2 = 8,
EVENT_SPELL_MARK_OF_HYDROSS3 = 9,
EVENT_SPELL_MARK_OF_HYDROSS4 = 10,
EVENT_SPELL_MARK_OF_HYDROSS5 = 11,
EVENT_SPELL_MARK_OF_HYDROSS6 = 12,
EVENT_SPELL_WATER_TOMB = 13,
EVENT_SPELL_VILE_SLUDGE = 14,
EVENT_SPELL_ENRAGE = 15,
EVENT_CHECK_AURA = 16,
EVENT_KILL_TALK = 17
};
class boss_hydross_the_unstable : public CreatureScript
struct boss_hydross_the_unstable : public BossAI
{
public:
boss_hydross_the_unstable() : CreatureScript("boss_hydross_the_unstable") { }
CreatureAI* GetAI(Creature* creature) const override
boss_hydross_the_unstable(Creature* creature) : BossAI(creature, DATA_HYDROSS_THE_UNSTABLE)
{
return GetSerpentShrineAI<boss_hydross_the_unstableAI>(creature);
scheduler.SetValidator([this]
{
return !me->HasUnitState(UNIT_STATE_CASTING);
});
}
struct boss_hydross_the_unstableAI : public BossAI
void Reset() override
{
boss_hydross_the_unstableAI(Creature* creature) : BossAI(creature, DATA_HYDROSS_THE_UNSTABLE)
BossAI::Reset();
_recentlySpoken = false;
}
void JustReachedHome() override
{
BossAI::JustReachedHome();
if (!me->HasAura(SPELL_BLUE_BEAM))
{
me->RemoveAurasDueToSpell(SPELL_CLEANSING_FIELD_AURA);
}
}
void SetForm(bool corrupt, bool initial)
{
scheduler.CancelGroup(GROUP_ABILITIES);
DoResetThreatList();
if (corrupt)
{
me->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true);
DoCastSelf(SPELL_CORRUPTION, true);
scheduler.Schedule(0s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_CORRUPTION1);
}).Schedule(15s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_CORRUPTION2);
}).Schedule(30s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_CORRUPTION3);
}).Schedule(45s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_CORRUPTION4);
}).Schedule(60s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_CORRUPTION5);
}).Schedule(75s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_CORRUPTION6);
}).Schedule(12150ms, GROUP_ABILITIES, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_VILE_SLUDGE, true);
context.Repeat(9700ms, 32800ms);
});
}
else
{
me->SetMeleeDamageSchool(SPELL_SCHOOL_FROST);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false);
me->RemoveAurasDueToSpell(SPELL_CORRUPTION);
scheduler.Schedule(0s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_HYDROSS1);
}).Schedule(15s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_HYDROSS2);
}).Schedule(30s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_HYDROSS3);
}).Schedule(45s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_HYDROSS4);
}).Schedule(60s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_HYDROSS5);
}).Schedule(75s, GROUP_ABILITIES, [this](TaskContext)
{
DoCastSelf(SPELL_MARK_OF_HYDROSS6);
}).Schedule(12150ms, GROUP_ABILITIES, [this](TaskContext context)
{
DoCastRandomTarget(SPELL_WATER_TOMB, true);
context.Repeat(9700ms, 32800ms);
});
}
void Reset() override
if (initial)
{
BossAI::Reset();
return;
}
void JustReachedHome() override
if (corrupt)
{
BossAI::JustReachedHome();
if (!me->HasAura(SPELL_BLUE_BEAM))
me->RemoveAurasDueToSpell(SPELL_CLEANSING_FIELD_AURA);
}
void SetForm(bool corrupt, bool initial)
{
events.CancelEventGroup(GROUP_ABILITIES);
DoResetThreatList();
if (corrupt)
Talk(SAY_SWITCH_TO_CORRUPT);
for (uint32 spellId = SPELL_SUMMON_CORRUPTED1; spellId <= SPELL_SUMMON_CORRUPTED4; ++spellId)
{
me->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, false);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true);
me->CastSpell(me, SPELL_CORRUPTION, true);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_CORRUPTION1, 0, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_CORRUPTION2, 15000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_CORRUPTION3, 30000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_CORRUPTION4, 45000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_CORRUPTION5, 60000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_CORRUPTION6, 75000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_VILE_SLUDGE, 7000, GROUP_ABILITIES);
}
else
{
me->SetMeleeDamageSchool(SPELL_SCHOOL_FROST);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true);
me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, false);
me->RemoveAurasDueToSpell(SPELL_CORRUPTION);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_HYDROSS1, 0, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_HYDROSS2, 15000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_HYDROSS3, 30000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_HYDROSS4, 45000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_HYDROSS5, 60000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_HYDROSS6, 75000, GROUP_ABILITIES);
events.ScheduleEvent(EVENT_SPELL_WATER_TOMB, 7000, GROUP_ABILITIES);
}
if (initial)
return;
if (corrupt)
{
Talk(SAY_SWITCH_TO_CORRUPT);
for (uint32 i = SPELL_SUMMON_CORRUPTED1; i <= SPELL_SUMMON_CORRUPTED4; ++i)
me->CastSpell(me, i, true);
}
else
{
Talk(SAY_SWITCH_TO_CLEAN);
for (uint32 i = SPELL_SUMMON_PURIFIED1; i <= SPELL_SUMMON_PURIFIED4; ++i)
me->CastSpell(me, i, true);
DoCastSelf(spellId, true);
}
}
void JustEngagedWith(Unit* who) override
else
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
events.ScheduleEvent(EVENT_SPELL_ENRAGE, 600000);
events.ScheduleEvent(EVENT_CHECK_AURA, 1000);
SetForm(false, true);
}
void KilledUnit(Unit* /*victim*/) override
{
if (events.GetNextEventTime(EVENT_KILL_TALK) == 0)
Talk(SAY_SWITCH_TO_CLEAN);
for (uint32 spellId = SPELL_SUMMON_PURIFIED1; spellId <= SPELL_SUMMON_PURIFIED4; ++spellId)
{
Talk(me->HasAura(SPELL_CORRUPTION) ? SAY_CORRUPT_SLAY : SAY_CLEAN_SLAY);
events.ScheduleEvent(EVENT_KILL_TALK, 6000);
DoCastSelf(spellId, true);
}
}
}
void JustSummoned(Creature* summon) override
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
SetForm(false, true);
scheduler.Schedule(1s, [this](TaskContext context)
{
summons.Summon(summon);
summon->CastSpell(summon, SPELL_ELEMENTAL_SPAWNIN, true);
summon->SetInCombatWithZone();
if (summon->GetEntry() == NPC_PURE_SPAWN_OF_HYDROSS)
summon->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true);
else
summon->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true);
}
void SummonedCreatureDespawn(Creature* summon) override
{
summons.Despawn(summon);
}
void JustDied(Unit* killer) override
{
Talk(me->HasAura(SPELL_CORRUPTION) ? SAY_CORRUPT_DEATH : SAY_CLEAN_DEATH);
BossAI::JustDied(killer);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
if (me->HasAura(SPELL_BLUE_BEAM) == me->HasAura(SPELL_CORRUPTION))
{
case EVENT_CHECK_AURA:
if (me->HasAura(SPELL_BLUE_BEAM) == me->HasAura(SPELL_CORRUPTION))
SetForm(!me->HasAura(SPELL_BLUE_BEAM), false);
events.ScheduleEvent(EVENT_CHECK_AURA, 1000);
break;
case EVENT_SPELL_ENRAGE:
me->CastSpell(me, SPELL_ENRAGE, true);
break;
case EVENT_SPELL_MARK_OF_HYDROSS1:
me->CastSpell(me, SPELL_MARK_OF_HYDROSS1, false);
break;
case EVENT_SPELL_MARK_OF_HYDROSS2:
me->CastSpell(me, SPELL_MARK_OF_HYDROSS2, false);
break;
case EVENT_SPELL_MARK_OF_HYDROSS3:
me->CastSpell(me, SPELL_MARK_OF_HYDROSS3, false);
break;
case EVENT_SPELL_MARK_OF_HYDROSS4:
me->CastSpell(me, SPELL_MARK_OF_HYDROSS4, false);
break;
case EVENT_SPELL_MARK_OF_HYDROSS5:
me->CastSpell(me, SPELL_MARK_OF_HYDROSS5, false);
break;
case EVENT_SPELL_MARK_OF_HYDROSS6:
me->CastSpell(me, SPELL_MARK_OF_HYDROSS6, false);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_HYDROSS6, 15000, GROUP_ABILITIES);
break;
case EVENT_SPELL_MARK_OF_CORRUPTION1:
me->CastSpell(me, SPELL_MARK_OF_CORRUPTION1, false);
break;
case EVENT_SPELL_MARK_OF_CORRUPTION2:
me->CastSpell(me, SPELL_MARK_OF_CORRUPTION2, false);
break;
case EVENT_SPELL_MARK_OF_CORRUPTION3:
me->CastSpell(me, SPELL_MARK_OF_CORRUPTION3, false);
break;
case EVENT_SPELL_MARK_OF_CORRUPTION4:
me->CastSpell(me, SPELL_MARK_OF_CORRUPTION4, false);
break;
case EVENT_SPELL_MARK_OF_CORRUPTION5:
me->CastSpell(me, SPELL_MARK_OF_CORRUPTION5, false);
break;
case EVENT_SPELL_MARK_OF_CORRUPTION6:
me->CastSpell(me, SPELL_MARK_OF_CORRUPTION6, false);
events.ScheduleEvent(EVENT_SPELL_MARK_OF_CORRUPTION6, 15000, GROUP_ABILITIES);
break;
case EVENT_SPELL_WATER_TOMB:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true))
me->CastSpell(target, SPELL_WATER_TOMB, false);
events.ScheduleEvent(EVENT_SPELL_WATER_TOMB, 7000, GROUP_ABILITIES);
break;
case EVENT_SPELL_VILE_SLUDGE:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 60.0f, true))
me->CastSpell(target, SPELL_VILE_SLUDGE, false);
events.ScheduleEvent(EVENT_SPELL_VILE_SLUDGE, 15000, GROUP_ABILITIES);
break;
SetForm(!me->HasAura(SPELL_BLUE_BEAM), false);
}
context.Repeat(1s);
}).Schedule(10min, [this](TaskContext)
{
DoCastSelf(SPELL_ENRAGE, true);
});
}
DoMeleeAttackIfReady();
void KilledUnit(Unit* /*victim*/) override
{
if (!_recentlySpoken)
{
Talk(me->HasAura(SPELL_CORRUPTION) ? SAY_CORRUPT_SLAY : SAY_CLEAN_SLAY);
_recentlySpoken = true;
}
};
scheduler.Schedule(6s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
void JustSummoned(Creature* summon) override
{
summons.Summon(summon);
summon->CastSpell(summon, SPELL_ELEMENTAL_SPAWNIN, true);
summon->SetInCombatWithZone();
if (summon->GetEntry() == NPC_PURE_SPAWN_OF_HYDROSS)
{
summon->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true);
}
else
{
summon->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true);
}
}
void SummonedCreatureDespawn(Creature* summon) override
{
summons.Despawn(summon);
}
void JustDied(Unit* killer) override
{
Talk(me->HasAura(SPELL_CORRUPTION) ? SAY_CORRUPT_DEATH : SAY_CLEAN_DEATH);
BossAI::JustDied(killer);
}
private:
bool _recentlySpoken;
};
class spell_hydross_cleansing_field_aura : public SpellScriptLoader
@@ -377,7 +343,7 @@ public:
void AddSC_boss_hydross_the_unstable()
{
new boss_hydross_the_unstable();
RegisterSerpentShrineAI(boss_hydross_the_unstable);
new spell_hydross_cleansing_field_aura();
new spell_hydross_cleansing_field_command();
new spell_hydross_mark_of_hydross();
@@ -1,14 +1,14 @@
/*
* Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/>
* This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
*
* 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
* under the terms of the GNU Affero General Public License as published by the
* Free Software Foundation; either version 3 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
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
@@ -55,8 +55,10 @@ enum Spells
SPELL_QUAKE_KNOCKBACK = 30571,
SPELL_COLLAPSE_DAMAGE = 36449,
SPELL_CAMERA_SHAKE = 36455,
SPELL_DEBRIS_TARGET = 30629,
SPELL_DEBRIS_SPAWN = 30630,
SPELL_DEBRIS_DAMAGE = 30631,
SPELL_DEBRIS_VISUAL = 30632,
SPELL_DEBRIS_DAMAGE = 30631
};
enum Groups
@@ -70,26 +72,9 @@ enum Actions
ACTION_INCREASE_HELLFIRE_CHANNELER_DEATH_COUNT = 1
};
class DealDebrisDamage : public BasicEvent
{
public:
DealDebrisDamage(Creature& creature, ObjectGuid targetGUID) : _owner(creature), _targetGUID(targetGUID) { }
bool Execute(uint64 /*eventTime*/, uint32 /*updateTime*/) override
{
if (Unit* target = ObjectAccessor::GetUnit(_owner, _targetGUID))
target->CastSpell(target, SPELL_DEBRIS_DAMAGE, true, nullptr, nullptr, _owner.GetGUID());
return true;
}
private:
Creature& _owner;
ObjectGuid _targetGUID;
};
struct boss_magtheridon : public BossAI
{
boss_magtheridon(Creature* creature) : BossAI(creature, TYPE_MAGTHERIDON)
boss_magtheridon(Creature* creature) : BossAI(creature, DATA_MAGTHERIDON)
{
scheduler.SetValidator([this]
{
@@ -137,11 +122,7 @@ struct boss_magtheridon : public BossAI
_currentPhase = 0;
scheduler.Schedule(20s, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random))
{
target->CastSpell(target, SPELL_DEBRIS_VISUAL, true, nullptr, nullptr, me->GetGUID());
me->m_Events.AddEvent(new DealDebrisDamage(*me, target->GetGUID()), me->m_Events.CalculateTime(5000));
}
DoCastAOE(SPELL_DEBRIS_TARGET);
context.Repeat(20s);
});
});
@@ -170,7 +151,7 @@ struct boss_magtheridon : public BossAI
void ScheduleCombatEvents()
{
me->GetThreatMgr().ClearAllThreat();
DoResetThreatList();
me->RemoveUnitFlag(UNIT_FLAG_NOT_SELECTABLE);
me->SetImmuneToPC(false);
me->SetReactState(REACT_AGGRESSIVE);
@@ -248,7 +229,7 @@ struct boss_magtheridon : public BossAI
BossAI::JustEngagedWith(who);
Talk(SAY_EMOTE_BEGIN);
instance->DoForAllMinions(TYPE_MAGTHERIDON, [&](Creature* creature) {
instance->DoForAllMinions(DATA_MAGTHERIDON, [&](Creature* creature) {
creature->SetInCombatWithZone();
});
@@ -289,6 +270,37 @@ private:
TaskScheduler _interruptScheduler;
};
struct npc_target_trigger : public ScriptedAI
{
npc_target_trigger(Creature* creature) : ScriptedAI(creature), _cast(false)
{
me->SetReactState(REACT_PASSIVE);
}
void Reset() override
{
if (!_cast)
{
DoCastSelf(SPELL_DEBRIS_VISUAL);
_cast = true;
_scheduler.Schedule(5s, [this](TaskContext /*context*/)
{
DoCastSelf(SPELL_DEBRIS_DAMAGE);
me->DespawnOrUnsummon(6000);
});
}
}
void UpdateAI(uint32 diff) override
{
_scheduler.Update(diff);
}
protected:
TaskScheduler _scheduler;
bool _cast;
};
class spell_magtheridon_blaze : public SpellScript
{
PrepareSpellScript(spell_magtheridon_blaze);
@@ -348,6 +360,33 @@ class spell_magtheridon_quake : public SpellScript
}
};
class spell_magtheridon_debris_target_selector : public SpellScript
{
PrepareSpellScript(spell_magtheridon_debris_target_selector);
void FilterTargets(std::list<WorldObject*>& targets)
{
targets.remove_if([&](WorldObject* target) -> bool
{
return target->GetEntry() != NPC_TARGET_TRIGGER;
});
Acore::Containers::RandomResize(targets, 1);
}
void HandleHit()
{
if (Unit* target = GetHitUnit())
target->CastSpell(target, SPELL_DEBRIS_SPAWN);
}
void Register() override
{
OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_magtheridon_debris_target_selector::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
OnHit += SpellHitFn(spell_magtheridon_debris_target_selector::HandleHit);
}
};
class go_manticron_cube : public GameObjectScript
{
public:
@@ -369,8 +408,10 @@ public:
void AddSC_boss_magtheridon()
{
RegisterMagtheridonsLairCreatureAI(boss_magtheridon);
RegisterMagtheridonsLairCreatureAI(npc_target_trigger);
RegisterSpellScript(spell_magtheridon_blaze);
RegisterSpellScript(spell_magtheridon_shadow_grasp);
RegisterSpellScript(spell_magtheridon_quake);
RegisterSpellScript(spell_magtheridon_debris_target_selector);
new go_manticron_cube();
}
@@ -21,18 +21,18 @@
BossBoundaryData const boundaries =
{
{ TYPE_MAGTHERIDON, new CircleBoundary(Position(-18.70f, 2.24f), 52.30) }
{ DATA_MAGTHERIDON, new CircleBoundary(Position(-18.70f, 2.24f), 52.30) }
};
DoorData const doorData[] =
{
{ GO_MAGTHERIDON_DOORS, TYPE_MAGTHERIDON, DOOR_TYPE_ROOM },
{ GO_MAGTHERIDON_DOORS, DATA_MAGTHERIDON, DOOR_TYPE_ROOM },
{ 0, 0, DOOR_TYPE_ROOM } // END
};
MinionData const minionData[] =
{
{ NPC_HELLFIRE_CHANNELER, TYPE_MAGTHERIDON }
{ NPC_HELLFIRE_CHANNELER, DATA_MAGTHERIDON }
};
class instance_magtheridons_lair : public InstanceMapScript
@@ -133,7 +133,7 @@ public:
if (!InstanceScript::SetBossState(id, state))
return false;
if (id == TYPE_MAGTHERIDON)
if (id == DATA_MAGTHERIDON)
{
if (state == IN_PROGRESS)
{
@@ -163,7 +163,7 @@ public:
switch (type)
{
case DATA_CHANNELER_COMBAT:
if (GetBossState(TYPE_MAGTHERIDON) != IN_PROGRESS)
if (GetBossState(DATA_MAGTHERIDON) != IN_PROGRESS)
if (Creature* magtheridon = instance->GetCreature(_magtheridonGUID))
magtheridon->SetInCombatWithZone();
break;
@@ -27,7 +27,7 @@
enum DataTypes
{
TYPE_MAGTHERIDON = 0,
DATA_MAGTHERIDON = 0,
MAX_ENCOUNTER = 1,
DATA_CHANNELER_COMBAT = 10,
@@ -41,6 +41,7 @@ enum NpcIds
NPC_HELLFIRE_CHANNELER = 17256,
NPC_HELLFIRE_WARDER = 18829,
NPC_HELLFIRE_RAID_TRIGGER = 17376,
NPC_TARGET_TRIGGER = 17474
};
enum GoIds
@@ -29,107 +29,94 @@ enum voidReaver
SPELL_POUNDING = 34162,
SPELL_ARCANE_ORB = 34172,
SPELL_KNOCK_AWAY = 25778,
SPELL_BERSERK = 26662,
EVENT_SPELL_POUNDING = 1,
EVENT_SPELL_ARCANEORB = 2,
EVENT_SPELL_KNOCK_AWAY = 3,
EVENT_SPELL_BERSERK = 4
SPELL_BERSERK = 26662
};
class boss_void_reaver : public CreatureScript
enum Groups
{
public:
boss_void_reaver() : CreatureScript("boss_void_reaver") { }
GROUP_ARCANE_ORB = 1
};
struct boss_void_reaverAI : public BossAI
struct boss_void_reaver : public BossAI
{
boss_void_reaver(Creature* creature) : BossAI(creature, DATA_REAVER)
{
boss_void_reaverAI(Creature* creature) : BossAI(creature, DATA_REAVER)
scheduler.SetValidator([this]
{
me->ApplySpellImmune(0, IMMUNITY_DISPEL, DISPEL_POISON, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_HEALTH_LEECH, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_POWER_DRAIN, true);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_PERIODIC_LEECH, true);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_PERIODIC_MANA_LEECH, true);
}
return !me->HasUnitState(UNIT_STATE_CASTING);
});
void Reset() override
{
BossAI::Reset();
}
void KilledUnit(Unit* victim) override
{
if (victim->GetTypeId() == TYPEID_PLAYER && roll_chance_i(50))
Talk(SAY_SLAY);
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit* who) override
{
Talk(SAY_AGGRO);
BossAI::JustEngagedWith(who);
events.ScheduleEvent(EVENT_SPELL_POUNDING, 15000);
events.ScheduleEvent(EVENT_SPELL_ARCANEORB, 3000);
events.ScheduleEvent(EVENT_SPELL_KNOCK_AWAY, 30000);
events.ScheduleEvent(EVENT_SPELL_BERSERK, 600000);
me->CallForHelp(105.0f);
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
events.Update(diff);
if (me->HasUnitState(UNIT_STATE_CASTING))
return;
switch (events.ExecuteEvent())
{
case EVENT_SPELL_BERSERK:
me->CastSpell(me, SPELL_BERSERK, true);
break;
case EVENT_SPELL_POUNDING:
Talk(SAY_POUNDING);
me->CastSpell(me, SPELL_POUNDING, false);
events.ScheduleEvent(EVENT_SPELL_POUNDING, 15000);
break;
case EVENT_SPELL_ARCANEORB:
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, -18.0f, true))
me->CastSpell(target, SPELL_ARCANE_ORB, false);
else if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 20.0f, true))
me->CastSpell(target, SPELL_ARCANE_ORB, false);
events.ScheduleEvent(EVENT_SPELL_ARCANEORB, 4000);
break;
case EVENT_SPELL_KNOCK_AWAY:
me->CastSpell(me->GetVictim(), SPELL_KNOCK_AWAY, false);
events.ScheduleEvent(EVENT_SPELL_POUNDING, 25000);
break;
}
DoMeleeAttackIfReady();
}
bool CheckEvadeIfOutOfCombatArea() const override
{
return me->GetDistance2d(432.59f, 371.93f) > 105.0f;
}
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetTheEyeAI<boss_void_reaverAI>(creature);
me->ApplySpellImmune(0, IMMUNITY_DISPEL, DISPEL_POISON, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_HEALTH_LEECH, true);
me->ApplySpellImmune(0, IMMUNITY_EFFECT, SPELL_EFFECT_POWER_DRAIN, true);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_PERIODIC_LEECH, true);
me->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_PERIODIC_MANA_LEECH, true);
}
void Reset() override
{
BossAI::Reset();
_recentlySpoken = false;
}
void KilledUnit(Unit* /*victim*/) override
{
if (!_recentlySpoken)
{
Talk(SAY_SLAY);
_recentlySpoken = true;
scheduler.Schedule(5s, [this](TaskContext)
{
_recentlySpoken = false;
});
}
}
void JustDied(Unit* killer) override
{
Talk(SAY_DEATH);
BossAI::JustDied(killer);
}
void JustEngagedWith(Unit* who) override
{
BossAI::JustEngagedWith(who);
Talk(SAY_AGGRO);
me->CallForHelp(105.0f);
scheduler.Schedule(10min, [this](TaskContext)
{
DoCastSelf(SPELL_BERSERK);
}).Schedule(15s, [this](TaskContext context)
{
Talk(SAY_POUNDING);
DoCastSelf(SPELL_POUNDING);
scheduler.DelayGroup(GROUP_ARCANE_ORB, 3s);
context.Repeat(15s);
}).Schedule(3s, GROUP_ARCANE_ORB, [this](TaskContext context)
{
if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, -18.0f, true))
me->CastSpell(target, SPELL_ARCANE_ORB, false);
else if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 20.0f, true))
me->CastSpell(target, SPELL_ARCANE_ORB, false);
context.Repeat(3s);
}).Schedule(30s, [this](TaskContext context)
{
DoCastVictim(SPELL_KNOCK_AWAY);
context.Repeat(25s);
});
}
bool CheckEvadeIfOutOfCombatArea() const override
{
return me->GetDistance2d(432.59f, 371.93f) > 105.0f;
}
private:
bool _recentlySpoken;
};
void AddSC_boss_void_reaver()
{
new boss_void_reaver();
RegisterTheEyeAI(boss_void_reaver);
}
@@ -65,4 +65,6 @@ inline AI* GetTheEyeAI(T* obj)
return GetInstanceAI<AI>(obj, TheEyeScriptName);
}
#define RegisterTheEyeAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetTheEyeAI)
#endif
@@ -22,6 +22,8 @@
#include "ScriptedEscortAI.h"
#include "ScriptedGossip.h"
#include "SpellInfo.h"
#include "SpellAuras.h"
#include "SpellScript.h"
// Ours
enum saeed
@@ -697,9 +699,6 @@ public:
{
Drained = true;
int32 uHpPct = int32(me->GetHealthPct());
me->UpdateEntry(NPC_DRAINED_PHASE_HUNTER_ENTRY);
me->SetHealth(me->CountPctFromMaxHealth(uHpPct));
me->LowerPlayerDamageReq(me->GetMaxHealth() - me->GetHealth());
me->SetInCombatWith(player);
@@ -911,6 +910,45 @@ public:
}
};
class spell_q10190_battery_recharging_blaster : public SpellScript
{
PrepareSpellScript(spell_q10190_battery_recharging_blaster);
SpellCastResult CheckCast()
{
if (Unit* target = GetExplTargetUnit())
if (target->GetHealthPct() <= 25.0f)
return SPELL_CAST_OK;
return SPELL_FAILED_BAD_TARGETS;
}
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_q10190_battery_recharging_blaster::CheckCast);
}
};
class spell_q10190_battery_recharging_blaster_aura : public AuraScript
{
PrepareAuraScript(spell_q10190_battery_recharging_blaster_aura);
void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
{
if (GetTargetApplication()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE)
return;
if (Creature* phasehunter = GetTarget()->ToCreature())
if (phasehunter->GetEntry() == NPC_PHASE_HUNTER_ENTRY)
phasehunter->UpdateEntry(NPC_DRAINED_PHASE_HUNTER_ENTRY);
}
void Register() override
{
OnEffectRemove += AuraEffectRemoveFn(spell_q10190_battery_recharging_blaster_aura::HandleEffectRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
}
};
void AddSC_netherstorm()
{
// Ours
@@ -922,5 +960,5 @@ void AddSC_netherstorm()
new npc_phase_hunter();
new npc_bessy();
new npc_maxx_a_million_escort();
RegisterSpellAndAuraScriptPair(spell_q10190_battery_recharging_blaster, spell_q10190_battery_recharging_blaster_aura);
}
+13
View File
@@ -146,6 +146,18 @@ class spell_dk_raise_ally : public SpellScript
{
PrepareSpellScript(spell_dk_raise_ally);
SpellCastResult CheckCast()
{
Player* unitTarget = GetHitPlayer();
if (!unitTarget)
return SPELL_FAILED_BAD_TARGETS;
if (unitTarget->IsAlive()) // not discovered attributeEx5?
return SPELL_FAILED_TARGET_NOT_DEAD;
return SPELL_CAST_OK;
}
void HandleDummy(SpellEffIndex /*effIndex*/)
{
if (Player* unitTarget = GetHitPlayer())
@@ -237,6 +249,7 @@ class spell_dk_raise_ally : public SpellScript
void Register() override
{
OnCheckCast += SpellCheckCastFn(spell_dk_raise_ally::CheckCast);
OnEffectHitTarget += SpellEffectFn(spell_dk_raise_ally::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
}
};
+3 -3
View File
@@ -302,7 +302,8 @@ private:
enum Spell
{
PAL_SPELL_ARDENT_DEFENDER_HEAL = 66235
PAL_SPELL_ARDENT_DEFENDER_DEBUFF = 66233,
PAL_SPELL_ARDENT_DEFENDER_HEAL = 66235
};
bool Validate(SpellInfo const* /*spellInfo*/) override
@@ -329,7 +330,7 @@ private:
int32 remainingHealth = victim->GetHealth() - dmgInfo.GetDamage();
uint32 allowedHealth = victim->CountPctFromMaxHealth(35);
// If damage kills us
if (remainingHealth <= 0 && !victim->ToPlayer()->HasSpellCooldown(PAL_SPELL_ARDENT_DEFENDER_HEAL))
if (remainingHealth <= 0 && !victim->ToPlayer()->HasAura(PAL_SPELL_ARDENT_DEFENDER_DEBUFF))
{
// Cast healing spell, completely avoid damage
absorbAmount = dmgInfo.GetDamage();
@@ -344,7 +345,6 @@ private:
int32 healAmount = int32(victim->CountPctFromMaxHealth(uint32(healPct * pctFromDefense)));
victim->CastCustomSpell(PAL_SPELL_ARDENT_DEFENDER_HEAL, SPELLVALUE_BASE_POINT0, healAmount, victim, true, nullptr, aurEff);
victim->ToPlayer()->AddSpellCooldown(PAL_SPELL_ARDENT_DEFENDER_HEAL, 0, 120000);
}
else if (remainingHealth < int32(allowedHealth))
{
+22 -23
View File
@@ -19,12 +19,7 @@
#include "Group.h"
#include "Guild.h"
#include "ScriptMgr.h"
#define LOG_CHAT(TYPE, ...) \
if (lang != LANG_ADDON) \
LOG_DEBUG("chat.log." TYPE, __VA_ARGS__); \
else \
LOG_DEBUG("chat.log.addon." TYPE, __VA_ARGS__);
#include "Log.h"
class ChatLogScript : public PlayerScript
{
@@ -36,66 +31,67 @@ public:
switch (type)
{
case CHAT_MSG_SAY:
LOG_CHAT("say", "Player {} says (language {}): {}",
LOG_INFO("chat.say", "Player {} says (language {}): {}",
player->GetName(), lang, msg);
break;
case CHAT_MSG_EMOTE:
LOG_CHAT("emote", "Player {} emotes: {}",
LOG_INFO("chat.emote", "Player {} emotes: {}",
player->GetName(), msg);
break;
case CHAT_MSG_YELL:
LOG_CHAT("yell", "Player {} yells (language {}): {}",
LOG_INFO("chat.yell", "Player {} yells (language {}): {}",
player->GetName(), lang, msg);
break;
}
}
void OnChat(Player* player, uint32 /*type*/, uint32 lang, std::string& msg, Player* receiver) override
void OnChat(Player* player, uint32 /*type*/, uint32 /*lang*/, std::string& msg, Player* receiver) override
{
LOG_CHAT("whisper", "Player {} tells {}: {}",
LOG_INFO("chat.whisper", "Player {} tells {}: {}",
player->GetName(), receiver ? receiver->GetName() : "<unknown>", msg);
}
void OnChat(Player* player, uint32 type, uint32 lang, std::string& msg, Group* group) override
{
std::string str = lang != LANG_ADDON ? "chat." : "chat.addon.";
//! NOTE:
//! LANG_ADDON can only be sent by client in "PARTY", "RAID", "GUILD", "BATTLEGROUND", "WHISPER"
switch (type)
{
case CHAT_MSG_PARTY:
LOG_CHAT("party", "Player {} tells group with leader {}: {}",
LOG_INFO(str + "party", "Player {} tells group with leader {}: {}",
player->GetName(), group ? group->GetLeaderName() : "<unknown>", msg);
break;
case CHAT_MSG_PARTY_LEADER:
LOG_CHAT("party", "Leader {} tells group: {}",
LOG_INFO(str + "party", "Leader {} tells group: {}",
player->GetName(), msg);
break;
case CHAT_MSG_RAID:
LOG_CHAT("raid", "Player {} tells raid with leader {}: {}",
LOG_INFO(str + "raid", "Player {} tells raid with leader {}: {}",
player->GetName(), group ? group->GetLeaderName() : "<unknown>", msg);
break;
case CHAT_MSG_RAID_LEADER:
LOG_CHAT("raid", "Leader player {} tells raid: {}",
LOG_INFO(str + "raid", "Leader player {} tells raid: {}",
player->GetName(), msg);
break;
case CHAT_MSG_RAID_WARNING:
LOG_CHAT("raid", "Leader player {} warns raid with: {}",
LOG_INFO(str + "raid", "Leader player {} sends raid warning: {}",
player->GetName(), msg);
break;
case CHAT_MSG_BATTLEGROUND:
LOG_CHAT("bg", "Player {} tells battleground with leader {}: {}",
LOG_INFO(str + "bg", "Player {} tells battleground with leader {}: {}",
player->GetName(), group ? group->GetLeaderName() : "<unknown>", msg);
break;
case CHAT_MSG_BATTLEGROUND_LEADER:
LOG_CHAT("bg", "Leader player {} tells battleground: {}",
LOG_INFO(str + "bg", "Leader player {} tells battleground: {}",
player->GetName(), msg);
break;
}
@@ -103,21 +99,22 @@ public:
void OnChat(Player* player, uint32 type, uint32 lang, std::string& msg, Guild* guild) override
{
std::string str = lang != LANG_ADDON ? "chat." : "chat.addon.";
switch (type)
{
case CHAT_MSG_GUILD:
LOG_CHAT("guild", "Player {} tells guild {}: {}",
LOG_INFO(str + "guild", "Player {} tells guild {}: {}",
player->GetName(), guild ? guild->GetName() : "<unknown>", msg);
break;
case CHAT_MSG_OFFICER:
LOG_CHAT("guild.officer", "Player {} tells guild {} officers: {}",
LOG_INFO(str + "guild.officer", "Player {} tells guild {} officers: {}",
player->GetName(), guild ? guild->GetName() : "<unknown>", msg);
break;
}
}
void OnChat(Player* player, uint32 /*type*/, uint32 lang, std::string& msg, Channel* channel) override
void OnChat(Player* player, uint32 /*type*/, uint32 /*lang*/, std::string& msg, Channel* channel) override
{
bool isSystem = channel &&
(channel->HasFlag(CHANNEL_FLAG_TRADE) ||
@@ -127,13 +124,15 @@ public:
if (isSystem)
{
LOG_CHAT("system", "Player {} tells channel {}: {}",
LOG_INFO("chat.channel", "Player {} tells channel {}: {}",
player->GetName(), channel->GetName(), msg);
}
else
{
// Allow to log custom channels. i.e. world channel
// in that case set config: Logger.channel.world=6,Chat
std::string channelName = channel ? channel->GetName() : "<unknown>";
LOG_CHAT("channel." + channelName, "Player {} tells channel {}: {}",
LOG_INFO("chat.channel." + channelName, "Player {} tells channel {}: {}",
player->GetName(), channelName, msg);
}
}
+1
View File
@@ -225,6 +225,7 @@ enum AchievementCriteriaTypes
ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113,
ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS = 114,
ACHIEVEMENT_CRITERIA_TYPE_EARN_ACHIEVEMENT_POINTS = 115,
ACHIEVEMENT_CRITERIA_TYPE_ROLL_DISENCHANT = 117,
ACHIEVEMENT_CRITERIA_TYPE_USE_LFD_TO_GROUP_WITH_PLAYERS = 119,
ACHIEVEMENT_CRITERIA_TYPE_TOTAL = 124, // 0..123 => 124 criteria types total
};